comparison libpurple/protocols/msn/slp.c @ 30469:b0a7b31dcc5d

I think this finally works with both aMSN and the official client, for receiving and sending, and with a couple possibilities for which one is the listening client. Refs #247.
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Wed, 05 May 2010 07:32:45 +0000
parents 0cc4f8651462
children 71c24c85e4af
comparison
equal deleted inserted replaced
30468:b491612862dc 30469:b0a7b31dcc5d
274 274
275 purple_debug_error("msn", "Received illegal request for file %s\n", path); 275 purple_debug_error("msn", "Received illegal request for file %s\n", path);
276 return NULL; 276 return NULL;
277 } 277 }
278 278
279 static char *
280 parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype)
281 {
282 char *nonce;
283
284 *ntype = DC_NONCE_UNKNOWN;
285
286 nonce = get_token(content, "Hashed-Nonce: {", "}\r\n");
287 if (nonce) {
288 *ntype = DC_NONCE_SHA1;
289 } else {
290 guint32 n1, n5;
291 guint16 n2, n3, n4, n6;
292 nonce = get_token(content, "Nonce: {", "}\r\n");
293 *ntype = DC_NONCE_PLAIN;
294 if (sscanf(nonce, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
295 &n1, &n2, &n3, &n4, &n5, &n6) == 6) {
296 g_free(nonce);
297 nonce = g_malloc(16);
298 *(guint32 *)(nonce + 0) = GUINT32_TO_LE(n1);
299 *(guint16 *)(nonce + 4) = GUINT16_TO_LE(n2);
300 *(guint16 *)(nonce + 6) = GUINT16_TO_LE(n3);
301 *(guint16 *)(nonce + 8) = GUINT16_TO_BE(n4);
302 *(guint32 *)(nonce + 10) = GUINT32_TO_BE(n5);
303 *(guint16 *)(nonce + 14) = GUINT16_TO_BE(n6);
304 } else {
305 /* Invalid nonce, so ignore request */
306 g_free(nonce);
307 nonce = NULL;
308 }
309 }
310
311 return nonce;
312 }
313
279 static gboolean 314 static gboolean
280 msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content) 315 msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content)
281 { 316 {
282 /* A direct connection negotiation response */ 317 /* A direct connection negotiation response */
283 char *bridge; 318 char *bridge;
284 char *nonce; 319 char *nonce;
320 char *listening;
285 MsnDirectConn *dc = slpcall->slplink->dc; 321 MsnDirectConn *dc = slpcall->slplink->dc;
322 MsnDirectConnNonceType ntype;
286 gboolean result = FALSE; 323 gboolean result = FALSE;
287 324
288 purple_debug_info("msn", "process_transresp\n"); 325 purple_debug_info("msn", "process_transresp\n");
289 326
290 g_return_val_if_fail(dc != NULL, FALSE); 327 g_return_val_if_fail(dc != NULL, FALSE);
291 g_return_val_if_fail(dc->state == DC_STATE_CLOSED, FALSE); 328 g_return_val_if_fail(dc->state == DC_STATE_CLOSED, FALSE);
292 329
293 bridge = get_token(content, "Bridge: ", "\r\n"); 330 bridge = get_token(content, "Bridge: ", "\r\n");
294 nonce = get_token(content, "Hashed-Nonce: {", "}\r\n"); 331 nonce = parse_dc_nonce(content, &ntype);
295 if (nonce && bridge && !strcmp(bridge, "TCPv1")) { 332 listening = get_token(content, "Listening: ", "\r\n");
333 if (listening && bridge && !strcmp(bridge, "TCPv1")) {
296 /* Ok, the client supports direct TCP connection */ 334 /* Ok, the client supports direct TCP connection */
297 335
298 strncpy(dc->remote_nonce, nonce, 36); 336 /* We always need this. */
299 dc->remote_nonce[36] = '\0'; 337 if (ntype == DC_NONCE_SHA1) {
300 338 strncpy(dc->remote_nonce, nonce, 36);
301 if (dc->listen_data != NULL || dc->listenfd != -1) { 339 dc->remote_nonce[36] = '\0';
340 }
341
342 if (!strcasecmp(listening, "false")) {
302 if (dc->listen_data != NULL) { 343 if (dc->listen_data != NULL) {
303 /* 344 /*
304 * We'll listen for incoming connections but 345 * We'll listen for incoming connections but
305 * the listening socket isn't ready yet so we cannot 346 * the listening socket isn't ready yet so we cannot
306 * send the INVITE packet now. Put the slpcall into waiting mode 347 * send the INVITE packet now. Put the slpcall into waiting mode
307 * and let the callback send the invite. 348 * and let the callback send the invite.
308 */ 349 */
309 slpcall->wait_for_socket = TRUE; 350 slpcall->wait_for_socket = TRUE;
310 351
311 } else { 352 } else if (dc->listenfd != -1) {
312 /* The listening socket is ready. Send the INVITE here. */ 353 /* The listening socket is ready. Send the INVITE here. */
313 msn_dc_send_invite(dc); 354 msn_dc_send_invite(dc);
355
356 } else {
357 /* We weren't able to create a listener either. Use SB. */
358 msn_dc_fallback_to_p2p(dc);
314 } 359 }
315 360
316 } else { 361 } else {
317 /* 362 /*
318 * We should connect to the client so parse 363 * We should connect to the client so parse
319 * IP/port from response. 364 * IP/port from response.
320 */ 365 */
321 char *ip, *port_str; 366 char *ip, *port_str;
322 int port = 0; 367 int port = 0;
368
369 if (ntype == DC_NONCE_PLAIN) {
370 /* Only needed for listening side. */
371 memcpy(dc->nonce, nonce, 16);
372 }
373
374 /* Cancel any listen attempts because we don't need them. */
375 if (dc->listenfd_handle != 0) {
376 purple_input_remove(dc->listenfd_handle);
377 dc->listenfd_handle = 0;
378 }
379 if (dc->connect_timeout_handle != 0) {
380 purple_timeout_remove(dc->connect_timeout_handle);
381 dc->connect_timeout_handle = 0;
382 }
383 if (dc->listenfd != -1) {
384 purple_network_remove_port_mapping(dc->listenfd);
385 close(dc->listenfd);
386 dc->listenfd = -1;
387 }
388 if (dc->listen_data != NULL) {
389 purple_network_listen_cancel(dc->listen_data);
390 dc->listen_data = NULL;
391 }
323 392
324 /* Save external IP/port for later use. We'll try local connection first. */ 393 /* Save external IP/port for later use. We'll try local connection first. */
325 dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n"); 394 dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n");
326 port_str = get_token(content, "IPv4External-Port: ", "\r\n"); 395 port_str = get_token(content, "IPv4External-Port: ", "\r\n");
327 if (port_str) { 396 if (port_str) {
380 * Invalid direct connect invitation or 449 * Invalid direct connect invitation or
381 * TCP connection is not supported 450 * TCP connection is not supported
382 */ 451 */
383 } 452 }
384 453
454 g_free(listening);
385 g_free(nonce); 455 g_free(nonce);
386 g_free(bridge); 456 g_free(bridge);
387 457
388 return result; 458 return result;
389 } 459 }
638 /* Don't do anything if we already have a direct connection */ 708 /* Don't do anything if we already have a direct connection */
639 if (slpcall->slplink->dc != NULL) 709 if (slpcall->slplink->dc != NULL)
640 return; 710 return;
641 711
642 bridges = get_token(content, "Bridges: ", "\r\n"); 712 bridges = get_token(content, "Bridges: ", "\r\n");
643 nonce = get_token(content, "Hashed-Nonce: {", "}\r\n"); 713 nonce = parse_dc_nonce(content, &ntype);
644 if (nonce) {
645 ntype = DC_NONCE_SHA1;
646 } else {
647 nonce = get_token(content, "Nonce: {", "}\r\n");
648 ntype = DC_NONCE_PLAIN;
649 }
650 if (nonce && bridges && strstr(bridges, "TCPv1") != NULL) { 714 if (nonce && bridges && strstr(bridges, "TCPv1") != NULL) {
651 /* 715 /*
652 * Ok, the client supports direct TCP connection 716 * Ok, the client supports direct TCP connection
653 * Try to create a listening port 717 * Try to create a listening port
654 */ 718 */
655 MsnDirectConn *dc; 719 MsnDirectConn *dc;
656 720
657 dc = msn_dc_new(slpcall); 721 dc = msn_dc_new(slpcall);
658 dc->nonce_type = ntype; 722 if (ntype == DC_NONCE_PLAIN) {
659 strncpy(dc->remote_nonce, nonce, 36); 723 /* There is only one nonce for plain auth. */
660 dc->remote_nonce[36] = '\0'; 724 dc->nonce_type = ntype;
725 memcpy(dc->nonce, nonce, 16);
726 } else if (ntype == DC_NONCE_SHA1) {
727 /* Each side has a nonce in SHA1 auth. */
728 dc->nonce_type = ntype;
729 strncpy(dc->remote_nonce, nonce, 36);
730 dc->remote_nonce[36] = '\0';
731 }
661 732
662 dc->listen_data = purple_network_listen_range( 733 dc->listen_data = purple_network_listen_range(
663 0, 0, 734 0, 0,
664 SOCK_STREAM, 735 SOCK_STREAM,
665 msn_dc_listen_socket_created_cb, 736 msn_dc_listen_socket_created_cb,
725 796
726 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) 797 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
727 { 798 {
728 char *content; 799 char *content;
729 char *header; 800 char *header;
801 char *nonce = NULL;
730 MsnSlpMessage *msg; 802 MsnSlpMessage *msg;
731 MsnDirectConn *dc; 803 MsnDirectConn *dc;
732 MsnUser *user; 804 MsnUser *user;
733 805
734 if (slpcall->slplink->dc != NULL) { 806 if (slpcall->slplink->dc != NULL) {
761 header = g_strdup_printf( 833 header = g_strdup_printf(
762 "INVITE MSNMSGR:%s MSNSLP/1.0", 834 "INVITE MSNMSGR:%s MSNSLP/1.0",
763 slpcall->slplink->remote_user 835 slpcall->slplink->remote_user
764 ); 836 );
765 837
838 if (dc->nonce_type == DC_NONCE_SHA1)
839 nonce = g_strdup_printf("Hashed-Nonce: {%s}\r\n", dc->nonce_hash);
840
766 if (dc->listen_data == NULL) { 841 if (dc->listen_data == NULL) {
767 /* Listen socket creation failed */ 842 /* Listen socket creation failed */
768 purple_debug_info("msn", "got_ok: listening failed\n"); 843 purple_debug_info("msn", "got_ok: listening failed\n");
769 844
770 content = g_strdup_printf( 845 content = g_strdup_printf(
771 "Bridges: TCPv1\r\n" 846 "Bridges: TCPv1\r\n"
772 "NetID: %u\r\n" 847 "NetID: %u\r\n"
773 "Conn-Type: IP-Restrict-NAT\r\n" 848 "Conn-Type: IP-Restrict-NAT\r\n"
774 "UPnPNat: false\r\n" 849 "UPnPNat: false\r\n"
775 "ICF: false\r\n" 850 "ICF: false\r\n"
776 "%sNonce: {%s}\r\n" 851 "%s"
777 "\r\n", 852 "\r\n",
778 853
779 rand() % G_MAXUINT32, 854 rand() % G_MAXUINT32,
780 dc->nonce_type != DC_NONCE_PLAIN ? "Hashed-" : "", 855 nonce ? nonce : ""
781 dc->nonce_hash
782 ); 856 );
783 857
784 } else { 858 } else {
785 /* Listen socket created successfully. */ 859 /* Listen socket created successfully. */
786
787 purple_debug_info("msn", "got_ok: listening socket created\n"); 860 purple_debug_info("msn", "got_ok: listening socket created\n");
788 861
789 content = g_strdup_printf( 862 content = g_strdup_printf(
790 "Bridges: TCPv1\r\n" 863 "Bridges: TCPv1\r\n"
791 "NetID: 0\r\n" 864 "NetID: 0\r\n"
792 "Conn-Type: Direct-Connect\r\n" 865 "Conn-Type: Direct-Connect\r\n"
793 "UPnPNat: false\r\n" 866 "UPnPNat: false\r\n"
794 "ICF: false\r\n" 867 "ICF: false\r\n"
795 "%sNonce: {%s}\r\n" 868 "%s"
796 "\r\n", 869 "\r\n",
797 870
798 dc->nonce_type != DC_NONCE_PLAIN ? "Hashed-" : "", 871 nonce ? nonce : ""
799 dc->nonce_hash
800 ); 872 );
801 } 873 }
802 874
803 msg = msn_slpmsg_sip_new( 875 msg = msn_slpmsg_sip_new(
804 slpcall, 876 slpcall,
808 "application/x-msnmsgr-transreqbody", 880 "application/x-msnmsgr-transreqbody",
809 content 881 content
810 ); 882 );
811 msg->info = "DC INVITE"; 883 msg->info = "DC INVITE";
812 msg->text_body = TRUE; 884 msg->text_body = TRUE;
885 g_free(nonce);
813 g_free(header); 886 g_free(header);
814 g_free(content); 887 g_free(content);
815 888
816 msn_slplink_queue_slpmsg(slpcall->slplink, msg); 889 msn_slplink_queue_slpmsg(slpcall->slplink, msg);
817 } 890 }