Mercurial > pidgin.yaz
view libpurple/protocols/msn/directconn.c @ 31430:94b3092c904d
Apparently, UBM was updated with more parameters in MSNp16. One is
your own email address, but I'm not sure what the 4th parameter is.
Anyway, update payload length to use the correct parameter now.
Fixes #13022.
author | Elliott Sales de Andrade <qulogic@pidgin.im> |
---|---|
date | Thu, 02 Dec 2010 03:59:58 +0000 |
parents | 2de522fa534a |
children | 2f2c45abb3af |
line wrap: on
line source
/** * @file directconn.c MSN direct connection functions * * purple * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "internal.h" #include "cipher.h" #include "debug.h" #include "msn.h" #include "directconn.h" #include "slp.h" #include "slpmsg.h" #include "p2p.h" #define DC_MAX_BODY_SIZE 8*1024 #define DC_MAX_PACKET_SIZE (P2P_PACKET_HEADER_SIZE + DC_MAX_BODY_SIZE) static void msn_dc_calculate_nonce_hash(MsnDirectConnNonceType type, const guchar nonce[16], gchar nonce_hash[37]) { guchar digest[20]; if (type == DC_NONCE_SHA1) { PurpleCipher *cipher = purple_ciphers_find_cipher("sha1"); PurpleCipherContext *context = purple_cipher_context_new(cipher, NULL); purple_cipher_context_append(context, nonce, sizeof(nonce)); purple_cipher_context_digest(context, sizeof(digest), digest, NULL); purple_cipher_context_destroy(context); } else if (type == DC_NONCE_PLAIN) { memcpy(digest, nonce, 16); } g_sprintf(nonce_hash, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", digest[3], digest[2], digest[1], digest[0], digest[5], digest[4], digest[7], digest[6], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15] ); } static void msn_dc_generate_nonce(MsnDirectConn *dc) { guint32 *nonce; int i; nonce = (guint32 *)&dc->nonce; for (i = 0; i < 4; i++) nonce[i] = rand(); msn_dc_calculate_nonce_hash(dc->nonce_type, dc->nonce, dc->nonce_hash); if (purple_debug_is_verbose()) purple_debug_info("msn", "DC %p generated nonce %s\n", dc, dc->nonce_hash); } static MsnDirectConnPacket * msn_dc_new_packet(guint32 length) { MsnDirectConnPacket *p; p = g_new0(MsnDirectConnPacket, 1); p->length = length; p->data = g_malloc(length); return p; } static void msn_dc_destroy_packet(MsnDirectConnPacket *p) { g_free(p->data); #if 0 if (p->msg) msn_message_unref(p->msg); #endif if (p->part) msn_slpmsgpart_unref(p->part); g_free(p); } MsnDirectConn * msn_dc_new(MsnSlpCall *slpcall) { MsnDirectConn *dc; g_return_val_if_fail(slpcall != NULL, NULL); dc = g_new0(MsnDirectConn, 1); if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_new %p\n", dc); dc->slplink = slpcall->slplink; dc->slpcall = slpcall; if (dc->slplink->dc != NULL) purple_debug_warning("msn", "msn_dc_new: slplink already has an allocated DC!\n"); dc->slplink->dc = dc; dc->msg_body = NULL; dc->prev_ack = NULL; dc->listen_data = NULL; dc->connect_data = NULL; dc->listenfd = -1; dc->listenfd_handle = 0; dc->connect_timeout_handle = 0; dc->fd = -1; dc->recv_handle = 0; dc->send_handle = 0; dc->state = DC_STATE_CLOSED; dc->in_buffer = NULL; dc->out_queue = g_queue_new(); dc->msg_pos = -1; dc->send_connection_info_msg_cb = NULL; dc->ext_ip = NULL; dc->timeout_handle = 0; dc->progress = FALSE; /*dc->num_calls = 1;*/ /* TODO: Probably should set this based on buddy caps */ dc->nonce_type = DC_NONCE_PLAIN; msn_dc_generate_nonce(dc); return dc; } void msn_dc_destroy(MsnDirectConn *dc) { MsnSlpLink *slplink; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_destroy %p\n", dc); g_return_if_fail(dc != NULL); if (dc->slpcall != NULL) dc->slpcall->wait_for_socket = FALSE; slplink = dc->slplink; if (slplink) { slplink->dc = NULL; if (slplink->swboard == NULL) msn_slplink_unref(slplink); } g_free(dc->msg_body); if (dc->prev_ack) { msn_slpmsg_destroy(dc->prev_ack); } if (dc->listen_data != NULL) { purple_network_listen_cancel(dc->listen_data); } if (dc->connect_data != NULL) { purple_proxy_connect_cancel(dc->connect_data); } if (dc->listenfd != -1) { purple_network_remove_port_mapping(dc->listenfd); close(dc->listenfd); } if (dc->listenfd_handle != 0) { purple_input_remove(dc->listenfd_handle); } if (dc->connect_timeout_handle != 0) { purple_timeout_remove(dc->connect_timeout_handle); } if (dc->fd != -1) { close(dc->fd); } if (dc->send_handle != 0) { purple_input_remove(dc->send_handle); } if (dc->recv_handle != 0) { purple_input_remove(dc->recv_handle); } g_free(dc->in_buffer); if (dc->out_queue != NULL) { while (!g_queue_is_empty(dc->out_queue)) msn_dc_destroy_packet( g_queue_pop_head(dc->out_queue) ); g_queue_free(dc->out_queue); } g_free(dc->ext_ip); if (dc->timeout_handle != 0) { purple_timeout_remove(dc->timeout_handle); } g_free(dc); } /* void msn_dc_ref(MsnDirectConn *dc) { g_return_if_fail(dc != NULL); dc->num_calls++; } void msn_dc_unref(MsnDirectConn *dc) { g_return_if_fail(dc != NULL); if (dc->num_calls > 0) { dc->num_calls--; } } */ void msn_dc_send_invite(MsnDirectConn *dc) { MsnSlpCall *slpcall; MsnSlpMessage *msg; gchar *header; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_send_invite %p\n", dc); g_return_if_fail(dc != NULL); slpcall = dc->slpcall; g_return_if_fail(slpcall != NULL); header = g_strdup_printf( "INVITE MSNMSGR:%s MSNSLP/1.0", slpcall->slplink->remote_user ); msg = msn_slpmsg_sip_new( slpcall, 0, header, slpcall->branch, "application/x-msnmsgr-transrespbody", dc->msg_body ); msg->info = "DC INVITE"; msg->text_body = TRUE; g_free(header); g_free(dc->msg_body); dc->msg_body = NULL; msn_slplink_queue_slpmsg(slpcall->slplink, msg); } void msn_dc_send_ok(MsnDirectConn *dc) { if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_send_ok %p\n", dc); g_return_if_fail(dc != NULL); msn_slp_send_ok(dc->slpcall, dc->slpcall->branch, "application/x-msnmsgr-transrespbody", dc->msg_body); g_free(dc->msg_body); dc->msg_body = NULL; msn_slplink_send_slpmsg(dc->slpcall->slplink, dc->prev_ack); msn_slpmsg_destroy(dc->prev_ack); dc->prev_ack = NULL; msn_slplink_send_queued_slpmsgs(dc->slpcall->slplink); } void msn_dc_fallback_to_sb(MsnDirectConn *dc) { MsnSlpLink *slplink; MsnSlpCall *slpcall; GQueue *queue = NULL; purple_debug_info("msn", "msn_dc_fallback_to_sb %p\n", dc); g_return_if_fail(dc != NULL); slpcall = dc->slpcall; slplink = msn_slplink_ref(dc->slplink); if (slpcall && !g_queue_is_empty(dc->out_queue)) { queue = dc->out_queue; dc->out_queue = NULL; } msn_dc_destroy(dc); if (slpcall) { msn_slpcall_session_init(slpcall); if (queue) { while (!g_queue_is_empty(queue)) { MsnDirectConnPacket *p = g_queue_pop_head(queue); msn_slplink_send_msgpart(slplink, (MsnSlpMessage*)p->part->ack_data); msn_dc_destroy_packet(p); } g_queue_free(queue); } } msn_slplink_unref(slplink); } static void msn_dc_send_cb(gpointer data, gint fd, PurpleInputCondition cond) { MsnDirectConn *dc = data; MsnDirectConnPacket *p; int bytes_to_send; int bytes_sent; g_return_if_fail(dc != NULL); g_return_if_fail(fd != -1); if (g_queue_is_empty(dc->out_queue)) { if (dc->send_handle != 0) { purple_input_remove(dc->send_handle); dc->send_handle = 0; } return; } p = g_queue_peek_head(dc->out_queue); if (dc->msg_pos < 0) { /* First we send the length of the packet */ guint32 len = GUINT32_TO_LE(p->length); bytes_sent = send(fd, &len, 4, 0); if (bytes_sent < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) return; purple_debug_warning("msn", "msn_dc_send_cb: send error\n"); msn_dc_destroy(dc); return; } dc->msg_pos = 0; } bytes_to_send = p->length - dc->msg_pos; bytes_sent = send(fd, p->data + dc->msg_pos, bytes_to_send, 0); if (bytes_sent < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) return; purple_debug_warning("msn", "msn_dc_send_cb: send error\n"); msn_dc_destroy(dc); return; } dc->progress = TRUE; dc->msg_pos += bytes_sent; if (dc->msg_pos == p->length) { if (p->sent_cb != NULL) p->sent_cb(p); g_queue_pop_head(dc->out_queue); msn_dc_destroy_packet(p); dc->msg_pos = -1; } } static void msn_dc_enqueue_packet(MsnDirectConn *dc, MsnDirectConnPacket *p) { gboolean was_empty; was_empty = g_queue_is_empty(dc->out_queue); g_queue_push_tail(dc->out_queue, p); if (was_empty && dc->send_handle == 0) { dc->send_handle = purple_input_add(dc->fd, PURPLE_INPUT_WRITE, msn_dc_send_cb, dc); msn_dc_send_cb(dc, dc->fd, PURPLE_INPUT_WRITE); } } static void msn_dc_send_foo(MsnDirectConn *dc) { MsnDirectConnPacket *p; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_send_foo %p\n", dc); p = msn_dc_new_packet(4); memcpy(p->data, "foo\0", 4); msn_dc_enqueue_packet(dc, p); } static void msn_dc_send_handshake_with_nonce(MsnDirectConn *dc, MsnDirectConnPacket *p) { const gchar *h; h = (gchar*) msn_p2p_header_to_wire(&dc->header); memcpy(p->data, h, P2P_PACKET_HEADER_SIZE); memcpy(p->data + offsetof(MsnP2PHeader, ack_id), dc->nonce, 16); msn_dc_enqueue_packet(dc, p); } static void msn_dc_send_handshake(MsnDirectConn *dc) { MsnDirectConnPacket *p; p = msn_dc_new_packet(P2P_PACKET_HEADER_SIZE); dc->header.session_id = 0; dc->header.id = dc->slpcall->slplink->slp_seq_id++; dc->header.offset = 0; dc->header.total_size = 0; dc->header.length = 0; dc->header.flags = 0x100; msn_dc_send_handshake_with_nonce(dc, p); } static void msn_dc_send_handshake_reply(MsnDirectConn *dc) { MsnDirectConnPacket *p; p = msn_dc_new_packet(P2P_PACKET_HEADER_SIZE); dc->header.id = dc->slpcall->slplink->slp_seq_id++; dc->header.length = 0; msn_dc_send_handshake_with_nonce(dc, p); } static gboolean msn_dc_verify_handshake(MsnDirectConn *dc, guint32 packet_length) { guchar nonce[16]; gchar nonce_hash[37]; if (packet_length != P2P_PACKET_HEADER_SIZE) return FALSE; memcpy(nonce, dc->in_buffer + 4 + offsetof(MsnP2PHeader, ack_id), 16); if (dc->nonce_type == DC_NONCE_PLAIN) { if (memcmp(dc->nonce, nonce, 16) == 0) { purple_debug_info("msn", "Nonce from buddy request and nonce from DC attempt match, " "allowing direct connection\n"); return TRUE; } else { purple_debug_warning("msn", "Nonce from buddy request and nonce from DC attempt " "don't match, ignoring direct connection\n"); return FALSE; } } else if (dc->nonce_type == DC_NONCE_SHA1) { msn_dc_calculate_nonce_hash(dc->nonce_type, nonce, nonce_hash); if (g_str_equal(dc->remote_nonce, nonce_hash)) { purple_debug_info("msn", "Received nonce %s from buddy request " "and calculated nonce %s from DC attempt. " "Nonces match, allowing direct connection\n", dc->remote_nonce, nonce_hash); return TRUE; } else { purple_debug_warning("msn", "Received nonce %s from buddy request " "and calculated nonce %s from DC attempt. " "Nonces don't match, ignoring direct connection\n", dc->remote_nonce, nonce_hash); return FALSE; } } else return FALSE; } static void msn_dc_send_packet_cb(MsnDirectConnPacket *p) { if (p->part != NULL && p->part->ack_cb != NULL) p->part->ack_cb(p->part, p->part->ack_data); } #if 0 static void msn_dc_send_packet_cb(MsnDirectConnPacket *p) { if (p->msg != NULL && p->msg->ack_cb != NULL) p->msg->ack_cb(p->msg, p->msg->ack_data); } void msn_dc_enqueue_msg(MsnDirectConn *dc, MsnMessage *msg) { MsnDirectConnPacket *p; guint32 length; length = msg->body_len + P2P_PACKET_HEADER_SIZE; p = msn_dc_new_packet(length); memcpy(p->data, msg->slpmsg->header, P2P_PACKET_HEADER_SIZE); memcpy(p->data + P2P_PACKET_HEADER_SIZE, msg->body, msg->body_len); p->sent_cb = msn_dc_send_packet_cb; p->msg = msn_message_ref(msg); msn_dc_enqueue_packet(dc, p); } #endif void msn_dc_enqueue_part(MsnDirectConn *dc, MsnSlpMessagePart *part) { MsnDirectConnPacket *p; guint32 length; length = part->size + P2P_PACKET_HEADER_SIZE; p = msn_dc_new_packet(length); memcpy(p->data, part->header, P2P_PACKET_HEADER_SIZE); memcpy(p->data + P2P_PACKET_HEADER_SIZE, part->buffer, part->size); p->sent_cb = msn_dc_send_packet_cb; p->part = part; msn_dc_enqueue_packet(dc, p); } static int msn_dc_process_packet(MsnDirectConn *dc, guint32 packet_length) { MsnSlpMessagePart *part; g_return_val_if_fail(dc != NULL, DC_PROCESS_ERROR); switch (dc->state) { case DC_STATE_CLOSED: break; case DC_STATE_FOO: /* FOO message is always 4 bytes long */ if (packet_length != 4 || memcmp(dc->in_buffer, "\4\0\0\0foo", 8) != 0) return DC_PROCESS_FALLBACK; dc->state = DC_STATE_HANDSHAKE; break; case DC_STATE_HANDSHAKE: if (!msn_dc_verify_handshake(dc, packet_length)) return DC_PROCESS_FALLBACK; msn_dc_send_handshake_reply(dc); dc->state = DC_STATE_ESTABLISHED; msn_slpcall_session_init(dc->slpcall); dc->slpcall = NULL; break; case DC_STATE_HANDSHAKE_REPLY: if (!msn_dc_verify_handshake(dc, packet_length)) return DC_PROCESS_FALLBACK; dc->state = DC_STATE_ESTABLISHED; msn_slpcall_session_init(dc->slpcall); dc->slpcall = NULL; break; case DC_STATE_ESTABLISHED: if (dc->header.length) { part = msn_slpmsgpart_new_from_data(dc->in_buffer + 4, dc->header.length); msn_slplink_process_msg(dc->slplink, part); msn_slpmsgpart_destroy(part); } /* if (dc->num_calls == 0) { msn_dc_destroy(dc); return DC_PROCESS_CLOSE; } */ break; } return DC_PROCESS_OK; } static void msn_dc_recv_cb(gpointer data, gint fd, PurpleInputCondition cond) { MsnDirectConn *dc; int free_buf_space; int bytes_received; guint32 packet_length; g_return_if_fail(data != NULL); g_return_if_fail(fd != -1); dc = data; free_buf_space = dc->in_size - dc->in_pos; bytes_received = recv(fd, dc->in_buffer + dc->in_pos, free_buf_space, 0); if (bytes_received < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) return; purple_debug_warning("msn", "msn_dc_recv_cb: recv error\n"); if(dc->state != DC_STATE_ESTABLISHED) msn_dc_fallback_to_sb(dc); else msn_dc_destroy(dc); return; } else if (bytes_received == 0) { /* EOF. Remote side closed connection. */ purple_debug_info("msn", "msn_dc_recv_cb: recv EOF\n"); if(dc->state != DC_STATE_ESTABLISHED) msn_dc_fallback_to_sb(dc); else msn_dc_destroy(dc); return; } dc->progress = TRUE; dc->in_pos += bytes_received; /* Wait for packet length */ while (dc->in_pos >= 4) { packet_length = GUINT32_FROM_LE(*((guint32*)dc->in_buffer)); if (packet_length > DC_MAX_PACKET_SIZE) { /* Oversized packet */ purple_debug_warning("msn", "msn_dc_recv_cb: oversized packet received\n"); return; } /* Wait for the whole packet to arrive */ if (dc->in_pos < 4 + packet_length) return; if (dc->state != DC_STATE_FOO) { MsnP2PHeader *context; MsnP2PHeader *h; /* Skip packet size */ context = (MsnP2PHeader *)(dc->in_buffer + 4); h = msn_p2p_header_from_wire(context); memcpy(&dc->header, h, P2P_PACKET_HEADER_SIZE); g_free(h); } switch (msn_dc_process_packet(dc, packet_length)) { case DC_PROCESS_CLOSE: return; case DC_PROCESS_FALLBACK: purple_debug_warning("msn", "msn_dc_recv_cb: packet processing error, fall back to SB\n"); msn_dc_fallback_to_sb(dc); return; } if (dc->in_pos > packet_length + 4) { g_memmove(dc->in_buffer, dc->in_buffer + 4 + packet_length, dc->in_pos - packet_length - 4); } dc->in_pos -= packet_length + 4; } } static gboolean msn_dc_timeout(gpointer data) { MsnDirectConn *dc = data; g_return_val_if_fail(dc != NULL, FALSE); if (dc->progress) { dc->progress = FALSE; return TRUE; } else { dc->timeout_handle = 0; msn_dc_destroy(dc); return FALSE; } } static void msn_dc_init(MsnDirectConn *dc) { g_return_if_fail(dc != NULL); dc->in_size = DC_MAX_PACKET_SIZE + 4; dc->in_pos = 0; dc->in_buffer = g_malloc(dc->in_size); dc->recv_handle = purple_input_add(dc->fd, PURPLE_INPUT_READ, msn_dc_recv_cb, dc); dc->send_handle = purple_input_add(dc->fd, PURPLE_INPUT_WRITE, msn_dc_send_cb, dc); dc->timeout_handle = purple_timeout_add_seconds(DC_TIMEOUT, msn_dc_timeout, dc); } void msn_dc_connected_to_peer_cb(gpointer data, gint fd, const gchar *error_msg) { MsnDirectConn *dc = data; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_connected_to_peer_cb %p\n", dc); g_return_if_fail(dc != NULL); dc->connect_data = NULL; purple_timeout_remove(dc->connect_timeout_handle); dc->connect_timeout_handle = 0; dc->fd = fd; if (dc->fd != -1) { msn_dc_init(dc); msn_dc_send_foo(dc); msn_dc_send_handshake(dc); dc->state = DC_STATE_HANDSHAKE_REPLY; } } /* * This callback will be called when we're the server * and nobody has connected us in DC_INCOMING_TIMEOUT seconds */ static gboolean msn_dc_incoming_connection_timeout_cb(gpointer data) { MsnDirectConn *dc = data; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_incoming_connection_timeout_cb %p\n", dc); g_return_val_if_fail(dc != NULL, FALSE); if (dc->listen_data != NULL) { purple_network_listen_cancel(dc->listen_data); dc->listen_data = NULL; } if (dc->listenfd_handle != 0) { purple_input_remove(dc->listenfd_handle); dc->listenfd_handle = 0; } if (dc->listenfd != -1) { purple_network_remove_port_mapping(dc->listenfd); close(dc->listenfd); dc->listenfd = -1; } dc->connect_timeout_handle = 0; msn_dc_fallback_to_sb(dc); return FALSE; } /* * This callback will be called when we're unable to connect to * the remote host in DC_OUTGOING_TIMEOUT seconds. */ gboolean msn_dc_outgoing_connection_timeout_cb(gpointer data) { MsnDirectConn *dc = data; purple_debug_info("msn", "msn_dc_outgoing_connection_timeout_cb %p\n", dc); g_return_val_if_fail(dc != NULL, FALSE); dc->connect_timeout_handle = 0; if (dc->connect_data != NULL) { purple_proxy_connect_cancel(dc->connect_data); dc->connect_data = NULL; } if (dc->ext_ip && dc->ext_port) { /* Try external IP/port if available. */ dc->connect_data = purple_proxy_connect( NULL, dc->slpcall->slplink->session->account, dc->ext_ip, dc->ext_port, msn_dc_connected_to_peer_cb, dc ); g_free(dc->ext_ip); dc->ext_ip = NULL; if (dc->connect_data) { dc->connect_timeout_handle = purple_timeout_add_seconds( DC_OUTGOING_TIMEOUT, msn_dc_outgoing_connection_timeout_cb, dc ); } else { /* * Connection failed * Fall back to SB transfer */ msn_dc_outgoing_connection_timeout_cb(dc); } } else { /* * Both internal and external connection attempts failed. * Fall back to SB transfer. */ msn_dc_fallback_to_sb(dc); } return FALSE; } /* * This callback will be called when we're the server * and somebody has connected to us in DC_INCOMING_TIMEOUT seconds. */ static void msn_dc_incoming_connection_cb(gpointer data, gint listenfd, PurpleInputCondition cond) { MsnDirectConn *dc = data; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_incoming_connection_cb %p\n", dc); g_return_if_fail(dc != NULL); if (dc->connect_timeout_handle != 0) { purple_timeout_remove(dc->connect_timeout_handle); dc->connect_timeout_handle = 0; } if (dc->listenfd_handle != 0) { purple_input_remove(dc->listenfd_handle); dc->listenfd_handle = 0; } dc->fd = accept(listenfd, NULL, 0); purple_network_remove_port_mapping(dc->listenfd); close(dc->listenfd); dc->listenfd = -1; if (dc->fd != -1) { msn_dc_init(dc); dc->state = DC_STATE_FOO; } } void msn_dc_listen_socket_created_cb(int listenfd, gpointer data) { MsnDirectConn *dc = data; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_listen_socket_created_cb %p\n", dc); g_return_if_fail(dc != NULL); dc->listen_data = NULL; if (listenfd != -1) { const char *ext_ip; const char *int_ip; int port; ext_ip = purple_network_get_my_ip(listenfd); int_ip = purple_network_get_local_system_ip(listenfd); port = purple_network_get_port_from_fd(listenfd); dc->listenfd = listenfd; dc->listenfd_handle = purple_input_add( listenfd, PURPLE_INPUT_READ, msn_dc_incoming_connection_cb, dc ); dc->connect_timeout_handle = purple_timeout_add_seconds( DC_INCOMING_TIMEOUT, msn_dc_incoming_connection_timeout_cb, dc ); if (strcmp(int_ip, ext_ip) != 0) { dc->msg_body = g_strdup_printf( "Bridge: TCPv1\r\n" "Listening: true\r\n" "%sNonce: {%s}\r\n" "IPv4External-Addrs: %s\r\n" "IPv4External-Port: %d\r\n" "IPv4Internal-Addrs: %s\r\n" "IPv4Internal-Port: %d\r\n" "\r\n", dc->nonce_type != DC_NONCE_PLAIN ? "Hashed-" : "", dc->nonce_hash, ext_ip, port, int_ip, port ); } else { dc->msg_body = g_strdup_printf( "Bridge: TCPv1\r\n" "Listening: true\r\n" "%sNonce: {%s}\r\n" "IPv4External-Addrs: %s\r\n" "IPv4External-Port: %d\r\n" "\r\n", dc->nonce_type != DC_NONCE_PLAIN ? "Hashed-" : "", dc->nonce_hash, ext_ip, port ); } if (dc->slpcall->wait_for_socket) { if (dc->send_connection_info_msg_cb != NULL) dc->send_connection_info_msg_cb(dc); dc->slpcall->wait_for_socket = FALSE; } } }