Mercurial > pidgin.yaz
comparison libpurple/protocols/bonjour/jabber.c @ 29877:755219afed9f
Implementation of IPv6 support for Bonjour
* IPv6 buddy presences are preferred to IPv4 presences
* File Transfers currently don't support IPv6 (due to limitations in network.c)
Fixes #11290
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Wed, 24 Feb 2010 05:00:09 +0000 |
parents | 96cafa3d271c |
children | 99c9595e14a2 |
comparison
equal
deleted
inserted
replaced
29876:59099ccd141e | 29877:755219afed9f |
---|---|
42 #ifdef HAVE_UNISTD_H | 42 #ifdef HAVE_UNISTD_H |
43 #include <unistd.h> | 43 #include <unistd.h> |
44 #endif | 44 #endif |
45 #include <fcntl.h> | 45 #include <fcntl.h> |
46 | 46 |
47 #ifdef HAVE_GETIFADDRS | |
48 #include <ifaddrs.h> | |
49 #endif | |
50 | |
51 | |
47 #include "network.h" | 52 #include "network.h" |
48 #include "eventloop.h" | 53 #include "eventloop.h" |
49 #include "connection.h" | 54 #include "connection.h" |
50 #include "blist.h" | 55 #include "blist.h" |
51 #include "xmlnode.h" | 56 #include "xmlnode.h" |
621 _send_data_write_cb(bconv->pb, bconv->socket, PURPLE_INPUT_WRITE); | 626 _send_data_write_cb(bconv->pb, bconv->socket, PURPLE_INPUT_WRITE); |
622 } | 627 } |
623 | 628 |
624 } | 629 } |
625 | 630 |
631 #ifndef INET6_ADDRSTRLEN | |
632 #define INET6_ADDRSTRLEN 46 | |
633 #endif | |
634 | |
626 static void | 635 static void |
627 _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition) | 636 _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition) |
628 { | 637 { |
629 BonjourJabber *jdata = data; | 638 BonjourJabber *jdata = data; |
630 struct sockaddr_in their_addr; /* connector's address information */ | 639 struct sockaddr_storage their_addr; /* connector's address information */ |
631 socklen_t sin_size = sizeof(struct sockaddr); | 640 socklen_t sin_size = sizeof(struct sockaddr_storage); |
632 int client_socket; | 641 int client_socket; |
633 int flags; | 642 int flags; |
634 char *address_text = NULL; | 643 #ifdef HAVE_INET_NTOP |
644 char addrstr[INET6_ADDRSTRLEN]; | |
645 #endif | |
646 const char *address_text; | |
635 struct _match_buddies_by_address_t *mbba; | 647 struct _match_buddies_by_address_t *mbba; |
636 BonjourJabberConversation *bconv; | 648 BonjourJabberConversation *bconv; |
637 GSList *buddies; | 649 GSList *buddies; |
638 | 650 |
639 /* Check that it is a read condition */ | 651 /* Check that it is a read condition */ |
640 if (condition != PURPLE_INPUT_READ) | 652 if (condition != PURPLE_INPUT_READ) |
641 return; | 653 return; |
642 | 654 |
643 if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1) | 655 memset(&their_addr, 0, sin_size); |
656 | |
657 if ((client_socket = accept(server_socket, (struct sockaddr*)&their_addr, &sin_size)) == -1) | |
644 return; | 658 return; |
645 | 659 |
646 flags = fcntl(client_socket, F_GETFL); | 660 flags = fcntl(client_socket, F_GETFL); |
647 fcntl(client_socket, F_SETFL, flags | O_NONBLOCK); | 661 fcntl(client_socket, F_SETFL, flags | O_NONBLOCK); |
648 #ifndef _WIN32 | 662 #ifndef _WIN32 |
649 fcntl(client_socket, F_SETFD, FD_CLOEXEC); | 663 fcntl(client_socket, F_SETFD, FD_CLOEXEC); |
650 #endif | 664 #endif |
651 | 665 |
652 /* Look for the buddy that has opened the conversation and fill information */ | 666 /* Look for the buddy that has opened the conversation and fill information */ |
653 address_text = inet_ntoa(their_addr.sin_addr); | 667 #ifdef HAVE_INET_NTOP |
668 if (their_addr.ss_family == AF_INET6) | |
669 address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in6 *)&their_addr)->sin6_addr, | |
670 addrstr, sizeof(addrstr)); | |
671 else | |
672 address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in *)&their_addr)->sin_addr, | |
673 addrstr, sizeof(addrstr)); | |
674 #else | |
675 address_text = inet_ntoa(((struct sockaddr_in *)&their_addr)->sin_addr); | |
676 #endif | |
654 purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text); | 677 purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text); |
655 mbba = g_new0(struct _match_buddies_by_address_t, 1); | 678 mbba = g_new0(struct _match_buddies_by_address_t, 1); |
656 mbba->address = address_text; | 679 mbba->address = address_text; |
657 | 680 |
658 buddies = purple_find_buddies(jdata->account, NULL); | 681 buddies = purple_find_buddies(jdata->account, NULL); |
678 bconv->socket = client_socket; | 701 bconv->socket = client_socket; |
679 bconv->rx_handler = purple_input_add(client_socket, PURPLE_INPUT_READ, _client_socket_handler, bconv); | 702 bconv->rx_handler = purple_input_add(client_socket, PURPLE_INPUT_READ, _client_socket_handler, bconv); |
680 | 703 |
681 } | 704 } |
682 | 705 |
683 gint | 706 static int |
684 bonjour_jabber_start(BonjourJabber *jdata) | 707 start_serversocket_listening(int port, int socket, struct sockaddr *addr, size_t addr_size, gboolean ip6, gboolean allow_port_fallback) |
685 { | 708 { |
686 struct sockaddr_in my_addr; | 709 int ret_port = port; |
687 | 710 |
688 /* Open a listening socket for incoming conversations */ | 711 purple_debug_info("bonjour", "Attempting to bind IPv%d socket to port %d.\n", ip6 ? 6 : 4, port); |
689 jdata->socket = socket(PF_INET, SOCK_STREAM, 0); | |
690 if (jdata->socket < 0) { | |
691 gchar *buf = g_strdup_printf(_("Unable to create socket: %s"), | |
692 g_strerror(errno)); | |
693 purple_connection_error_reason(jdata->account->gc, | |
694 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf); | |
695 g_free(buf); | |
696 return -1; | |
697 } | |
698 | |
699 memset(&my_addr, 0, sizeof(struct sockaddr_in)); | |
700 my_addr.sin_family = AF_INET; | |
701 | 712 |
702 /* Try to use the specified port - if it isn't available, use a random port */ | 713 /* Try to use the specified port - if it isn't available, use a random port */ |
703 my_addr.sin_port = htons(jdata->port); | 714 if (bind(socket, addr, addr_size) != 0) { |
704 if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0) | 715 |
705 { | |
706 purple_debug_info("bonjour", "Unable to bind to specified " | 716 purple_debug_info("bonjour", "Unable to bind to specified " |
707 "port %i: %s\n", jdata->port, g_strerror(errno)); | 717 "port %i: %s\n", port, g_strerror(errno)); |
708 my_addr.sin_port = 0; | 718 |
709 if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0) | 719 if (!allow_port_fallback) { |
710 { | 720 purple_debug_warning("bonjour", "Not attempting random port assignment.\n"); |
711 gchar *buf = g_strdup_printf(_("Unable to bind socket " | |
712 "to port: %s"), g_strerror(errno)); | |
713 purple_connection_error_reason(jdata->account->gc, | |
714 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf); | |
715 g_free(buf); | |
716 return -1; | 721 return -1; |
717 } | 722 } |
718 jdata->port = purple_network_get_port_from_fd(jdata->socket); | 723 #ifdef PF_INET6 |
719 } | 724 if (ip6) |
725 ((struct sockaddr_in6 *) addr)->sin6_port = 0; | |
726 else | |
727 #endif | |
728 ((struct sockaddr_in *) addr)->sin_port = 0; | |
729 | |
730 if (bind(socket, addr, addr_size) != 0) { | |
731 purple_debug_error("bonjour", "Unable to bind IPv%d socket to port: %s\n", ip6 ? 6 : 4, g_strerror(errno)); | |
732 return -1; | |
733 } | |
734 ret_port = purple_network_get_port_from_fd(socket); | |
735 } | |
736 | |
737 purple_debug_info("bonjour", "Bound IPv%d socket to port %d.\n", ip6 ? 6 : 4, ret_port); | |
720 | 738 |
721 /* Attempt to listen on the bound socket */ | 739 /* Attempt to listen on the bound socket */ |
722 if (listen(jdata->socket, 10) != 0) | 740 if (listen(socket, 10) != 0) { |
723 { | 741 purple_debug_error("bonjour", "Unable to listen on IPv%d socket: %s\n", ip6 ? 6 : 4, g_strerror(errno)); |
724 gchar *buf = g_strdup_printf(_("Unable to listen on socket: %s"), | |
725 g_strerror(errno)); | |
726 purple_connection_error_reason(jdata->account->gc, | |
727 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf); | |
728 g_free(buf); | |
729 return -1; | 742 return -1; |
730 } | 743 } |
731 | 744 |
732 #if 0 | 745 #if 0 |
733 /* TODO: Why isn't this being used? */ | 746 /* TODO: Why isn't this being used? */ |
737 { | 750 { |
738 purple_debug_error("bonjour", "No se ha podido crear el socket\n"); | 751 purple_debug_error("bonjour", "No se ha podido crear el socket\n"); |
739 } | 752 } |
740 #endif | 753 #endif |
741 | 754 |
742 /* Open a watcher in the socket we have just opened */ | 755 return ret_port; |
743 jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata); | 756 } |
757 | |
758 gint | |
759 bonjour_jabber_start(BonjourJabber *jdata) | |
760 { | |
761 int ipv6_port = -1, ipv4_port = -1; | |
762 | |
763 /* Open a listening socket for incoming conversations */ | |
764 #ifdef PF_INET6 | |
765 jdata->socket6 = socket(PF_INET6, SOCK_STREAM, 0); | |
766 #endif | |
767 jdata->socket = socket(PF_INET, SOCK_STREAM, 0); | |
768 if (jdata->socket == -1 && jdata->socket6 == -1) { | |
769 purple_debug_error("bonjour", "Unable to create socket: %s", | |
770 g_strerror(errno)); | |
771 return -1; | |
772 } | |
773 | |
774 #ifdef PF_INET6 | |
775 if (jdata->socket6 != -1) { | |
776 struct sockaddr_in6 addr6; | |
777 memset(&addr6, 0, sizeof(addr6)); | |
778 addr6.sin6_family = AF_INET6; | |
779 addr6.sin6_port = htons(jdata->port); | |
780 addr6.sin6_addr = in6addr_any; | |
781 ipv6_port = start_serversocket_listening(jdata->port, jdata->socket6, (struct sockaddr *) &addr6, sizeof(addr6), TRUE, TRUE); | |
782 /* Open a watcher in the socket we have just opened */ | |
783 if (ipv6_port > 0) { | |
784 jdata->watcher_id6 = purple_input_add(jdata->socket6, PURPLE_INPUT_READ, _server_socket_handler, jdata); | |
785 jdata->port = ipv6_port; | |
786 } else { | |
787 purple_debug_error("bonjour", "Failed to start listening on IPv6 socket.\n"); | |
788 close(jdata->socket6); | |
789 jdata->socket6 = -1; | |
790 } | |
791 } | |
792 #endif | |
793 if (jdata->socket != -1) { | |
794 struct sockaddr_in addr4; | |
795 memset(&addr4, 0, sizeof(addr4)); | |
796 addr4.sin_family = AF_INET; | |
797 addr4.sin_port = htons(jdata->port); | |
798 ipv4_port = start_serversocket_listening(jdata->port, jdata->socket, (struct sockaddr *) &addr4, sizeof(addr4), FALSE, ipv6_port != -1); | |
799 /* Open a watcher in the socket we have just opened */ | |
800 if (ipv4_port > 0) { | |
801 jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata); | |
802 jdata->port = ipv4_port; | |
803 } else { | |
804 purple_debug_error("bonjour", "Failed to start listening on IPv4 socket.\n"); | |
805 close(jdata->socket); | |
806 jdata->socket = -1; | |
807 } | |
808 } | |
809 | |
810 if (!(ipv6_port > 0 || ipv4_port > 0)) { | |
811 purple_debug_error("bonjour", "Unable to listen on socket: %s", | |
812 g_strerror(errno)); | |
813 return -1; | |
814 } | |
744 | 815 |
745 return jdata->port; | 816 return jdata->port; |
746 } | 817 } |
747 | 818 |
748 static void | 819 static void |
1099 /* Close the server socket and remove the watcher */ | 1170 /* Close the server socket and remove the watcher */ |
1100 if (jdata->socket >= 0) | 1171 if (jdata->socket >= 0) |
1101 close(jdata->socket); | 1172 close(jdata->socket); |
1102 if (jdata->watcher_id > 0) | 1173 if (jdata->watcher_id > 0) |
1103 purple_input_remove(jdata->watcher_id); | 1174 purple_input_remove(jdata->watcher_id); |
1175 if (jdata->socket6 >= 0) | |
1176 close(jdata->socket6); | |
1177 if (jdata->watcher_id6 > 0) | |
1178 purple_input_remove(jdata->watcher_id6); | |
1104 | 1179 |
1105 /* Close all the conversation sockets and remove all the watchers after sending end streams */ | 1180 /* Close all the conversation sockets and remove all the watchers after sending end streams */ |
1106 if (jdata->account->gc != NULL) { | 1181 if (jdata->account->gc != NULL) { |
1107 GSList *buddies, *l; | 1182 GSList *buddies, *l; |
1108 | 1183 |
1232 g_free(iq); | 1307 g_free(iq); |
1233 | 1308 |
1234 return (ret >= 0) ? 0 : -1; | 1309 return (ret >= 0) ? 0 : -1; |
1235 } | 1310 } |
1236 | 1311 |
1237 /* This returns a ';' delimited string containing all non-localhost IPs */ | 1312 /* This returns a list containing all non-localhost IPs */ |
1238 const char * | 1313 GSList * |
1239 purple_network_get_my_ip_ext2(int fd) | 1314 bonjour_jabber_get_local_ips(int fd) |
1240 { | 1315 { |
1241 char buffer[1024]; | 1316 GSList *ips = NULL; |
1242 static char ip_ext[17 * 10]; | 1317 const char *address_text; |
1318 int ret; | |
1319 | |
1320 #ifdef HAVE_GETIFADDRS /* This is required for IPv6 */ | |
1321 { | |
1322 struct ifaddrs *ifap, *ifa; | |
1323 struct sockaddr *addr; | |
1324 char addrstr[INET6_ADDRSTRLEN]; | |
1325 | |
1326 ret = getifaddrs(&ifap); | |
1327 if (ret != 0) { | |
1328 const char *error = g_strerror(errno); | |
1329 purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)"); | |
1330 return NULL; | |
1331 } | |
1332 | |
1333 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { | |
1334 if (!(ifa->ifa_flags & IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) || ifa->ifa_addr == NULL) | |
1335 continue; | |
1336 | |
1337 addr = ifa->ifa_addr; | |
1338 address_text = NULL; | |
1339 switch (addr->sa_family) { | |
1340 case AF_INET: | |
1341 address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr, | |
1342 addrstr, sizeof(addrstr)); | |
1343 break; | |
1344 #ifdef PF_INET6 | |
1345 case AF_INET6: | |
1346 address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in6 *)addr)->sin6_addr, | |
1347 addrstr, sizeof(addrstr)); | |
1348 break; | |
1349 #endif | |
1350 } | |
1351 | |
1352 if (address_text != NULL) { | |
1353 if (addr->sa_family == AF_INET) | |
1354 ips = g_slist_append(ips, g_strdup(address_text)); | |
1355 else | |
1356 ips = g_slist_prepend(ips, g_strdup(address_text)); | |
1357 } | |
1358 } | |
1359 | |
1360 freeifaddrs(ifap); | |
1361 | |
1362 } | |
1363 #else | |
1364 { | |
1243 char *tmp; | 1365 char *tmp; |
1244 char *tip; | |
1245 struct ifconf ifc; | 1366 struct ifconf ifc; |
1246 struct ifreq *ifr; | 1367 struct ifreq *ifr; |
1368 char buffer[1024]; | |
1247 struct sockaddr_in *sinptr; | 1369 struct sockaddr_in *sinptr; |
1248 guint32 lhost = htonl(127 * 256 * 256 * 256 + 1); | |
1249 long unsigned int add; | |
1250 int source = fd; | 1370 int source = fd; |
1251 int len, count = 0; | |
1252 | 1371 |
1253 if (fd < 0) | 1372 if (fd < 0) |
1254 source = socket(PF_INET, SOCK_STREAM, 0); | 1373 source = socket(PF_INET, SOCK_STREAM, 0); |
1255 | 1374 |
1256 ifc.ifc_len = sizeof(buffer); | 1375 ifc.ifc_len = sizeof(buffer); |
1257 ifc.ifc_req = (struct ifreq *)buffer; | 1376 ifc.ifc_req = (struct ifreq *)buffer; |
1258 ioctl(source, SIOCGIFCONF, &ifc); | 1377 ret = ioctl(source, SIOCGIFCONF, &ifc); |
1259 | 1378 |
1260 if (fd < 0) | 1379 if (fd < 0) |
1261 close(source); | 1380 close(source); |
1262 | 1381 |
1263 memset(ip_ext, 0, sizeof(ip_ext)); | 1382 if (ret < 0) { |
1264 memcpy(ip_ext, "0.0.0.0", 7); | 1383 const char *error = g_strerror(errno); |
1384 purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)"); | |
1385 return NULL; | |
1386 } | |
1387 | |
1265 tmp = buffer; | 1388 tmp = buffer; |
1266 tip = ip_ext; | 1389 while (tmp < buffer + ifc.ifc_len) { |
1267 while (tmp < buffer + ifc.ifc_len && count < 10) | |
1268 { | |
1269 ifr = (struct ifreq *)tmp; | 1390 ifr = (struct ifreq *)tmp; |
1270 tmp += HX_SIZE_OF_IFREQ(*ifr); | 1391 tmp += HX_SIZE_OF_IFREQ(*ifr); |
1271 | 1392 |
1272 if (ifr->ifr_addr.sa_family == AF_INET) | 1393 if (ifr->ifr_addr.sa_family == AF_INET) { |
1273 { | |
1274 sinptr = (struct sockaddr_in *)&ifr->ifr_addr; | 1394 sinptr = (struct sockaddr_in *)&ifr->ifr_addr; |
1275 if (sinptr->sin_addr.s_addr != lhost) | 1395 if ((ntohl(sinptr->sin_addr.s_addr) >> 24) != 127) { |
1276 { | 1396 address_text = inet_ntoa(sinptr->sin_addr); |
1277 add = ntohl(sinptr->sin_addr.s_addr); | 1397 ips = g_slist_prepend(ips, g_strdup(address_text)); |
1278 len = g_snprintf(tip, 17, "%lu.%lu.%lu.%lu;", | |
1279 ((add >> 24) & 255), | |
1280 ((add >> 16) & 255), | |
1281 ((add >> 8) & 255), | |
1282 add & 255); | |
1283 tip = &tip[len]; | |
1284 count++; | |
1285 continue; | |
1286 } | 1398 } |
1287 } | 1399 } |
1288 } | 1400 } |
1289 | 1401 } |
1290 return ip_ext; | 1402 #endif |
1291 } | 1403 |
1404 return ips; | |
1405 } |