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 }