pidgin: bad09643: Implementation of IPv6 support for Bonjo...
datallah at pidgin.im
datallah at pidgin.im
Wed Feb 24 00:00:42 EST 2010
-----------------------------------------------------------------
Revision: bad096432d2d601b84837925471454773c42d1f7
Ancestor: 817daf1dfecbb61b26b4f0f365425e748d71ed0a
Author: datallah at pidgin.im
Date: 2010-02-24T05:00:09
Branch: im.pidgin.pidgin
URL: http://d.pidgin.im/viewmtn/revision/info/bad096432d2d601b84837925471454773c42d1f7
Modified files:
ChangeLog libpurple/protocols/bonjour/bonjour.c
libpurple/protocols/bonjour/bonjour_ft.c
libpurple/protocols/bonjour/jabber.c
libpurple/protocols/bonjour/jabber.h
libpurple/protocols/bonjour/mdns_avahi.c
ChangeLog:
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
-------------- next part --------------
============================================================
--- ChangeLog b1d3fd6edf04ed013a8b5b0e05759289584fb88a
+++ ChangeLog ca6031c32294138c0fc1fb812a7117d4d2618cb0
@@ -18,6 +18,9 @@ version 2.7.0 (??/??/????):
* Make the search dialog unobtrusive in the conversation window (by making
it look and behave like the search dialog in Firefox)
+ Bonjour:
+ * Added support for IPv6. (Thanks to T_X for testing)
+
version 2.6.6 (02/18/2010):
libpurple:
* Fix 'make check' on OS X. (David Fang)
============================================================
--- libpurple/protocols/bonjour/bonjour.c afa27371709ba018eee8b2ddc9e8d9a32827310d
+++ libpurple/protocols/bonjour/bonjour.c f4a3499329e3dae805b276959553ea90471c8d25
@@ -101,6 +101,8 @@ bonjour_login(PurpleAccount *account)
/* Start waiting for jabber connections (iChat style) */
bd->jabber_data = g_new0(BonjourJabber, 1);
+ bd->jabber_data->socket = -1;
+ bd->jabber_data->socket6 = -1;
bd->jabber_data->port = purple_account_get_int(account, "port", BONJOUR_DEFAULT_PORT);
bd->jabber_data->account = account;
============================================================
--- libpurple/protocols/bonjour/bonjour_ft.c 1f6c56a94ac9fc4262c9f4cf2c4f0d3301e18985
+++ libpurple/protocols/bonjour/bonjour_ft.c 277080b0283bd73a1b27658613486b6a0e9db626
@@ -747,8 +747,7 @@ bonjour_bytestreams_listen(int sock, gpo
XepIq *iq;
xmlnode *query, *streamhost;
gchar *port;
- const char *next_ip, *local_ip;
- const char token [] = ";";
+ GSList *local_ips;
BonjourData *bd;
purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock);
@@ -773,17 +772,16 @@ bonjour_bytestreams_listen(int sock, gpo
xfer->local_port = purple_network_get_port_from_fd(sock);
- local_ip = purple_network_get_my_ip_ext2(sock);
- /* cheat a little here - the intent of the "const" attribute is to make it clear that the string doesn't need to be freed */
- next_ip = strtok((char *)local_ip, token);
+ local_ips = bonjour_jabber_get_local_ips(sock);
port = g_strdup_printf("%hu", xfer->local_port);
- while(next_ip != NULL) {
+ while(local_ips) {
streamhost = xmlnode_new_child(query, "streamhost");
xmlnode_set_attrib(streamhost, "jid", xf->sid);
- xmlnode_set_attrib(streamhost, "host", next_ip);
+ xmlnode_set_attrib(streamhost, "host", local_ips->data);
xmlnode_set_attrib(streamhost, "port", port);
- next_ip = strtok(NULL, token);
+ g_free(local_ips->data);
+ local_ips = g_slist_delete_link(local_ips, local_ips);
}
g_free(port);
@@ -796,15 +794,17 @@ bonjour_bytestreams_init(PurpleXfer *xfe
XepXfer *xf;
if(xfer == NULL)
return;
+
purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
xf = xfer->data;
+
purple_network_listen_map_external(FALSE);
xf->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
bonjour_bytestreams_listen, xfer);
purple_network_listen_map_external(TRUE);
- if (xf->listen_data == NULL) {
+ if (xf->listen_data == NULL)
purple_xfer_cancel_local(xfer);
- }
+
return;
}
============================================================
--- libpurple/protocols/bonjour/jabber.c f3045119213f831561154d5a4da5be436bc56e10
+++ libpurple/protocols/bonjour/jabber.c 6f0c25d0cf3e373490257a95d6e7e3265b810eff
@@ -44,6 +44,11 @@
#endif
#include <fcntl.h>
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
+
+
#include "network.h"
#include "eventloop.h"
#include "connection.h"
@@ -623,15 +628,22 @@ void bonjour_jabber_stream_started(Bonjo
}
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
static void
_server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition)
{
BonjourJabber *jdata = data;
- struct sockaddr_in their_addr; /* connector's address information */
- socklen_t sin_size = sizeof(struct sockaddr);
+ struct sockaddr_storage their_addr; /* connector's address information */
+ socklen_t sin_size = sizeof(struct sockaddr_storage);
int client_socket;
int flags;
- char *address_text = NULL;
+#ifdef HAVE_INET_NTOP
+ char addrstr[INET6_ADDRSTRLEN];
+#endif
+ const char *address_text;
struct _match_buddies_by_address_t *mbba;
BonjourJabberConversation *bconv;
GSList *buddies;
@@ -640,7 +652,9 @@ _server_socket_handler(gpointer data, in
if (condition != PURPLE_INPUT_READ)
return;
- if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1)
+ memset(&their_addr, 0, sin_size);
+
+ if ((client_socket = accept(server_socket, (struct sockaddr*)&their_addr, &sin_size)) == -1)
return;
flags = fcntl(client_socket, F_GETFL);
@@ -650,7 +664,16 @@ _server_socket_handler(gpointer data, in
#endif
/* Look for the buddy that has opened the conversation and fill information */
- address_text = inet_ntoa(their_addr.sin_addr);
+#ifdef HAVE_INET_NTOP
+ if (their_addr.ss_family == AF_INET6)
+ address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in6 *)&their_addr)->sin6_addr,
+ addrstr, sizeof(addrstr));
+ else
+ address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in *)&their_addr)->sin_addr,
+ addrstr, sizeof(addrstr));
+#else
+ address_text = inet_ntoa(((struct sockaddr_in *)&their_addr)->sin_addr);
+#endif
purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
mbba = g_new0(struct _match_buddies_by_address_t, 1);
mbba->address = address_text;
@@ -680,52 +703,42 @@ _server_socket_handler(gpointer data, in
}
-gint
-bonjour_jabber_start(BonjourJabber *jdata)
+static int
+start_serversocket_listening(int port, int socket, struct sockaddr *addr, size_t addr_size, gboolean ip6, gboolean allow_port_fallback)
{
- struct sockaddr_in my_addr;
+ int ret_port = port;
- /* Open a listening socket for incoming conversations */
- jdata->socket = socket(PF_INET, SOCK_STREAM, 0);
- if (jdata->socket < 0) {
- gchar *buf = g_strdup_printf(_("Unable to create socket: %s"),
- g_strerror(errno));
- purple_connection_error_reason(jdata->account->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf);
- g_free(buf);
- return -1;
- }
+ purple_debug_info("bonjour", "Attempting to bind IPv%d socket to port %d.\n", ip6 ? 6 : 4, port);
- memset(&my_addr, 0, sizeof(struct sockaddr_in));
- my_addr.sin_family = AF_INET;
+ /* Try to use the specified port - if it isn't available, use a random port */
+ if (bind(socket, addr, addr_size) != 0) {
- /* Try to use the specified port - if it isn't available, use a random port */
- my_addr.sin_port = htons(jdata->port);
- if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0)
- {
purple_debug_info("bonjour", "Unable to bind to specified "
- "port %i: %s\n", jdata->port, g_strerror(errno));
- my_addr.sin_port = 0;
- if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0)
- {
- gchar *buf = g_strdup_printf(_("Unable to bind socket "
- "to port: %s"), g_strerror(errno));
- purple_connection_error_reason(jdata->account->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf);
- g_free(buf);
+ "port %i: %s\n", port, g_strerror(errno));
+
+ if (!allow_port_fallback) {
+ purple_debug_warning("bonjour", "Not attempting random port assignment.\n");
return -1;
}
- jdata->port = purple_network_get_port_from_fd(jdata->socket);
+#ifdef PF_INET6
+ if (ip6)
+ ((struct sockaddr_in6 *) addr)->sin6_port = 0;
+ else
+#endif
+ ((struct sockaddr_in *) addr)->sin_port = 0;
+
+ if (bind(socket, addr, addr_size) != 0) {
+ purple_debug_error("bonjour", "Unable to bind IPv%d socket to port: %s\n", ip6 ? 6 : 4, g_strerror(errno));
+ return -1;
+ }
+ ret_port = purple_network_get_port_from_fd(socket);
}
+ purple_debug_info("bonjour", "Bound IPv%d socket to port %d.\n", ip6 ? 6 : 4, ret_port);
+
/* Attempt to listen on the bound socket */
- if (listen(jdata->socket, 10) != 0)
- {
- gchar *buf = g_strdup_printf(_("Unable to listen on socket: %s"),
- g_strerror(errno));
- purple_connection_error_reason(jdata->account->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf);
- g_free(buf);
+ if (listen(socket, 10) != 0) {
+ purple_debug_error("bonjour", "Unable to listen on IPv%d socket: %s\n", ip6 ? 6 : 4, g_strerror(errno));
return -1;
}
@@ -739,9 +752,67 @@ bonjour_jabber_start(BonjourJabber *jdat
}
#endif
- /* Open a watcher in the socket we have just opened */
- jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata);
+ return ret_port;
+}
+gint
+bonjour_jabber_start(BonjourJabber *jdata)
+{
+ int ipv6_port = -1, ipv4_port = -1;
+
+ /* Open a listening socket for incoming conversations */
+#ifdef PF_INET6
+ jdata->socket6 = socket(PF_INET6, SOCK_STREAM, 0);
+#endif
+ jdata->socket = socket(PF_INET, SOCK_STREAM, 0);
+ if (jdata->socket == -1 && jdata->socket6 == -1) {
+ purple_debug_error("bonjour", "Unable to create socket: %s",
+ g_strerror(errno));
+ return -1;
+ }
+
+#ifdef PF_INET6
+ if (jdata->socket6 != -1) {
+ struct sockaddr_in6 addr6;
+ memset(&addr6, 0, sizeof(addr6));
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_port = htons(jdata->port);
+ addr6.sin6_addr = in6addr_any;
+ ipv6_port = start_serversocket_listening(jdata->port, jdata->socket6, (struct sockaddr *) &addr6, sizeof(addr6), TRUE, TRUE);
+ /* Open a watcher in the socket we have just opened */
+ if (ipv6_port > 0) {
+ jdata->watcher_id6 = purple_input_add(jdata->socket6, PURPLE_INPUT_READ, _server_socket_handler, jdata);
+ jdata->port = ipv6_port;
+ } else {
+ purple_debug_error("bonjour", "Failed to start listening on IPv6 socket.\n");
+ close(jdata->socket6);
+ jdata->socket6 = -1;
+ }
+ }
+#endif
+ if (jdata->socket != -1) {
+ struct sockaddr_in addr4;
+ memset(&addr4, 0, sizeof(addr4));
+ addr4.sin_family = AF_INET;
+ addr4.sin_port = htons(jdata->port);
+ ipv4_port = start_serversocket_listening(jdata->port, jdata->socket, (struct sockaddr *) &addr4, sizeof(addr4), FALSE, ipv6_port != -1);
+ /* Open a watcher in the socket we have just opened */
+ if (ipv4_port > 0) {
+ jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata);
+ jdata->port = ipv4_port;
+ } else {
+ purple_debug_error("bonjour", "Failed to start listening on IPv4 socket.\n");
+ close(jdata->socket);
+ jdata->socket = -1;
+ }
+ }
+
+ if (!(ipv6_port > 0 || ipv4_port > 0)) {
+ purple_debug_error("bonjour", "Unable to listen on socket: %s",
+ g_strerror(errno));
+ return -1;
+ }
+
return jdata->port;
}
@@ -1101,6 +1172,10 @@ bonjour_jabber_stop(BonjourJabber *jdata
close(jdata->socket);
if (jdata->watcher_id > 0)
purple_input_remove(jdata->watcher_id);
+ if (jdata->socket6 >= 0)
+ close(jdata->socket6);
+ if (jdata->watcher_id6 > 0)
+ purple_input_remove(jdata->watcher_id6);
/* Close all the conversation sockets and remove all the watchers after sending end streams */
if (jdata->account->gc != NULL) {
@@ -1234,58 +1309,97 @@ xep_iq_send_and_free(XepIq *iq)
return (ret >= 0) ? 0 : -1;
}
-/* This returns a ';' delimited string containing all non-localhost IPs */
-const char *
-purple_network_get_my_ip_ext2(int fd)
+/* This returns a list containing all non-localhost IPs */
+GSList *
+bonjour_jabber_get_local_ips(int fd)
{
- char buffer[1024];
- static char ip_ext[17 * 10];
+ GSList *ips = NULL;
+ const char *address_text;
+ int ret;
+
+#ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
+ {
+ struct ifaddrs *ifap, *ifa;
+ struct sockaddr *addr;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ ret = getifaddrs(&ifap);
+ if (ret != 0) {
+ const char *error = g_strerror(errno);
+ purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)");
+ return NULL;
+ }
+
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ if (!(ifa->ifa_flags & IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) || ifa->ifa_addr == NULL)
+ continue;
+
+ addr = ifa->ifa_addr;
+ address_text = NULL;
+ switch (addr->sa_family) {
+ case AF_INET:
+ address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr,
+ addrstr, sizeof(addrstr));
+ break;
+#ifdef PF_INET6
+ case AF_INET6:
+ address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in6 *)addr)->sin6_addr,
+ addrstr, sizeof(addrstr));
+ break;
+#endif
+ }
+
+ if (address_text != NULL) {
+ if (addr->sa_family == AF_INET)
+ ips = g_slist_append(ips, g_strdup(address_text));
+ else
+ ips = g_slist_prepend(ips, g_strdup(address_text));
+ }
+ }
+
+ freeifaddrs(ifap);
+
+ }
+#else
+ {
char *tmp;
- char *tip;
struct ifconf ifc;
struct ifreq *ifr;
+ char buffer[1024];
struct sockaddr_in *sinptr;
- guint32 lhost = htonl(127 * 256 * 256 * 256 + 1);
- long unsigned int add;
int source = fd;
- int len, count = 0;
if (fd < 0)
source = socket(PF_INET, SOCK_STREAM, 0);
ifc.ifc_len = sizeof(buffer);
ifc.ifc_req = (struct ifreq *)buffer;
- ioctl(source, SIOCGIFCONF, &ifc);
+ ret = ioctl(source, SIOCGIFCONF, &ifc);
if (fd < 0)
close(source);
- memset(ip_ext, 0, sizeof(ip_ext));
- memcpy(ip_ext, "0.0.0.0", 7);
+ if (ret < 0) {
+ const char *error = g_strerror(errno);
+ purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)");
+ return NULL;
+ }
+
tmp = buffer;
- tip = ip_ext;
- while (tmp < buffer + ifc.ifc_len && count < 10)
- {
+ while (tmp < buffer + ifc.ifc_len) {
ifr = (struct ifreq *)tmp;
tmp += HX_SIZE_OF_IFREQ(*ifr);
- if (ifr->ifr_addr.sa_family == AF_INET)
- {
+ if (ifr->ifr_addr.sa_family == AF_INET) {
sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
- if (sinptr->sin_addr.s_addr != lhost)
- {
- add = ntohl(sinptr->sin_addr.s_addr);
- len = g_snprintf(tip, 17, "%lu.%lu.%lu.%lu;",
- ((add >> 24) & 255),
- ((add >> 16) & 255),
- ((add >> 8) & 255),
- add & 255);
- tip = &tip[len];
- count++;
- continue;
+ if ((ntohl(sinptr->sin_addr.s_addr) >> 24) != 127) {
+ address_text = inet_ntoa(sinptr->sin_addr);
+ ips = g_slist_prepend(ips, g_strdup(address_text));
}
- }
+ }
}
+ }
+#endif
- return ip_ext;
+ return ips;
}
============================================================
--- libpurple/protocols/bonjour/jabber.h fd0d9608d8b1594febf8281baa27b4521ee2cedf
+++ libpurple/protocols/bonjour/jabber.h 74658715d14a243e6b806b5ed8bc55797ef0d902
@@ -37,7 +37,9 @@ typedef struct _BonjourJabber
{
gint port;
gint socket;
+ gint socket6;
gint watcher_id;
+ gint watcher_id6;
PurpleAccount *account;
GSList *pending_conversations;
} BonjourJabber;
@@ -105,6 +107,6 @@ int xep_iq_send_and_free(XepIq *iq);
XepIq *xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id);
int xep_iq_send_and_free(XepIq *iq);
-const char *purple_network_get_my_ip_ext2(int fd);
+GSList * bonjour_jabber_get_local_ips(int fd);
#endif /* _BONJOUR_JABBER_H_ */
============================================================
--- libpurple/protocols/bonjour/mdns_avahi.c aa1aa21e1b7953db07428479f93b2be6c21e4279
+++ libpurple/protocols/bonjour/mdns_avahi.c 00340f6b687dd942e968b3a514cb38a83bcfb04c
@@ -189,8 +189,12 @@ _resolver_callback(AvahiServiceResolver
bb->ips = g_slist_remove(bb->ips, rd->ip);
g_free((gchar *) rd->ip);
}
- bb->ips = g_slist_prepend(bb->ips, g_strdup(ip));
- rd->ip = bb->ips->data;
+ rd->ip = g_strdup(ip);
+ /* IPv6 goes at the front of the list and IPv4 at the end so that we "prefer" IPv6, if present */
+ if (protocol == AVAHI_PROTO_INET6)
+ bb->ips = g_slist_prepend(bb->ips, (gchar *) rd->ip);
+ else
+ bb->ips = g_slist_append(bb->ips, (gchar *) rd->ip);
}
bb->port_p2pj = port;
@@ -249,7 +253,7 @@ _browser_callback(AvahiServiceBrowser *b
/* Make sure it isn't us */
if (purple_utf8_strcasecmp(name, account->username) != 0) {
if (!avahi_service_resolver_new(avahi_service_browser_get_client(b),
- interface, protocol, name, type, domain, AVAHI_PROTO_INET,
+ interface, protocol, name, type, domain, protocol,
0, _resolver_callback, account)) {
purple_debug_warning("bonjour", "_browser_callback -- Error initiating resolver: %s\n",
avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
@@ -448,14 +452,14 @@ gboolean _mdns_publish(BonjourDnsSd *dat
case PUBLISH_START:
publish_result = avahi_entry_group_add_service_strlst(
idata->group, AVAHI_IF_UNSPEC,
- AVAHI_PROTO_INET, 0,
+ AVAHI_PROTO_UNSPEC, 0,
purple_account_get_username(data->account),
LINK_LOCAL_RECORD_NAME, NULL, NULL, data->port_p2pj, lst);
break;
case PUBLISH_UPDATE:
publish_result = avahi_entry_group_update_service_txt_strlst(
idata->group, AVAHI_IF_UNSPEC,
- AVAHI_PROTO_INET, 0,
+ AVAHI_PROTO_UNSPEC, 0,
purple_account_get_username(data->account),
LINK_LOCAL_RECORD_NAME, NULL, lst);
break;
@@ -487,7 +491,7 @@ gboolean _mdns_browse(BonjourDnsSd *data
g_return_val_if_fail(idata != NULL, FALSE);
- idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, LINK_LOCAL_RECORD_NAME, NULL, 0, _browser_callback, data->account);
+ idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, LINK_LOCAL_RECORD_NAME, NULL, 0, _browser_callback, data->account);
if (!idata->sb) {
purple_debug_error("bonjour",
@@ -533,7 +537,7 @@ gboolean _mdns_set_buddy_icon_data(Bonjo
purple_account_get_username(data->account));
ret = avahi_entry_group_add_record(idata->buddy_icon_group, AVAHI_IF_UNSPEC,
- AVAHI_PROTO_INET, flags, svc_name,
+ AVAHI_PROTO_UNSPEC, flags, svc_name,
AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 120, avatar_data, avatar_len);
g_free(svc_name);
@@ -622,7 +626,7 @@ void _mdns_retrieve_buddy_icon(BonjourBu
name = g_strdup_printf("%s." LINK_LOCAL_RECORD_NAME "local", buddy->name);
idata->buddy_icon_rec_browser = avahi_record_browser_new(session_idata->client, AVAHI_IF_UNSPEC,
- AVAHI_PROTO_INET, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0,
+ AVAHI_PROTO_UNSPEC, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0,
_buddy_icon_record_cb, buddy);
g_free(name);
More information about the Commits
mailing list