Mercurial > pidgin
comparison libpurple/protocols/msn/slp.c @ 30049: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
30048:b491612862dc | 30049: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 } |