# HG changeset patch # User Ethan Blanton # Date 1322615793 0 # Node ID 0734b9c8c3456f13166f656262f5d86f49ca9a7c # Parent ca4714f51bb1052b46acd8e76eaa43782f27ec8d# Parent 58d4a721f0c085851e9553f0760286e5966cbeef propagate from branch 'im.pidgin.pidgin.2.x.y' (head bc79b1bf09dcfa1d8edac86a06761fce7416e69c) to branch 'im.pidgin.pidgin' (head 1f625d66be9b91c38a5398c3a4c302c651a19698) diff -r ca4714f51bb1 -r 0734b9c8c345 ChangeLog --- a/ChangeLog Tue Nov 29 03:59:09 2011 +0000 +++ b/ChangeLog Wed Nov 30 01:16:33 2011 +0000 @@ -41,6 +41,9 @@ Finch: * Fix compilation on OpenBSD. + Bonjour: + * IPv6 fixes (Linus Lüssing) + Gadu-Gadu: * Fix problems linking against GnuTLS. (#14544) diff -r ca4714f51bb1 -r 0734b9c8c345 libpurple/protocols/bonjour/bonjour_ft.c --- a/libpurple/protocols/bonjour/bonjour_ft.c Tue Nov 29 03:59:09 2011 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.c Wed Nov 30 01:16:33 2011 +0000 @@ -427,77 +427,173 @@ type = xmlnode_get_attrib(packet, "type"); id = xmlnode_get_attrib(packet, "id"); - if(type) { - if(!strcmp(type, "set")) { - const char *profile; - xmlnode *si; - gboolean parsed_receive = FALSE; + if(!type) + return; - si = xmlnode_get_child(packet, "si"); + if(!strcmp(type, "set")) { + const char *profile; + xmlnode *si; + gboolean parsed_receive = FALSE; - purple_debug_info("bonjour", "si offer Message type - SET.\n"); - if (si && (profile = xmlnode_get_attrib(si, "profile")) - && !strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) { - const char *filename = NULL, *filesize_str = NULL; - goffset filesize = 0; - xmlnode *file; + si = xmlnode_get_child(packet, "si"); - const char *sid = xmlnode_get_attrib(si, "id"); - - if ((file = xmlnode_get_child(si, "file"))) { - filename = xmlnode_get_attrib(file, "name"); - if((filesize_str = xmlnode_get_attrib(file, "size"))) - filesize = g_ascii_strtoll(filesize_str, NULL, 10); - } + purple_debug_info("bonjour", "si offer Message type - SET.\n"); + if (si && (profile = xmlnode_get_attrib(si, "profile")) + && !strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) { + const char *filename = NULL, *filesize_str = NULL; + int filesize = 0; + xmlnode *file; - /* TODO: Make sure that it is advertising a bytestreams transfer */ + const char *sid = xmlnode_get_attrib(si, "id"); - if (filename) { - bonjour_xfer_receive(pc, id, sid, name, filesize, filename, XEP_BYTESTREAMS); - - parsed_receive = TRUE; - } + if ((file = xmlnode_get_child(si, "file"))) { + filename = xmlnode_get_attrib(file, "name"); + if((filesize_str = xmlnode_get_attrib(file, "size"))) + filesize = atoi(filesize_str); } - if (!parsed_receive) { - BonjourData *bd = purple_connection_get_protocol_data(pc); + /* TODO: Make sure that it is advertising a bytestreams transfer */ + + if (filename) { + bonjour_xfer_receive(pc, id, sid, name, filesize, filename, XEP_BYTESTREAMS); + + parsed_receive = TRUE; + } + } + + if (!parsed_receive) { + BonjourData *bd = purple_connection_get_protocol_data(pc); + + purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n"); + xep_ft_si_reject(bd, id, name, "403", "cancel"); + /*TODO: Send Cancel (501) */ + } + } else if(!strcmp(type, "result")) { + purple_debug_info("bonjour", "si offer Message type - RESULT.\n"); - purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n"); - xep_ft_si_reject(bd, id, name, "403", "cancel"); - /*TODO: Send Cancel (501) */ - } - } else if(!strcmp(type, "result")) { - purple_debug_info("bonjour", "si offer Message type - RESULT.\n"); + xfer = bonjour_si_xfer_find(bd, id, name); + + if(xfer == NULL) { + BonjourData *bd = purple_connection_get_protocol_data(pc); + purple_debug_info("bonjour", "xfer find fail.\n"); + xep_ft_si_reject(bd, id, name, "403", "cancel"); + } else + bonjour_bytestreams_init(xfer); - xfer = bonjour_si_xfer_find(bd, id, name); + } else if(!strcmp(type, "error")) { + purple_debug_info("bonjour", "si offer Message type - ERROR.\n"); + + xfer = bonjour_si_xfer_find(bd, id, name); + + if(xfer == NULL) + purple_debug_info("bonjour", "xfer find fail.\n"); + else + purple_xfer_cancel_remote(xfer); + } else + purple_debug_info("bonjour", "si offer Message type - Unknown-%s.\n", type); +} - if(xfer == NULL) { - BonjourData *bd = purple_connection_get_protocol_data(pc); - purple_debug_info("bonjour", "xfer find fail.\n"); - xep_ft_si_reject(bd, id, name, "403", "cancel"); - } else - bonjour_bytestreams_init(xfer); +/** + * Will compare a host with a buddy_ip. + * + * Additionally to a common '!strcmp(host, buddy_ip)', it will also return TRUE + * if 'host' is a link local IPv6 address without an appended interface + * identifier and 'buddy_ip' string is "host" + "%iface". + * + * Note: This may theoretically result in the attempt to connect to the wrong + * host, because we do not know for sure which interface the according link + * local IPv6 address might relate to and RFC4862 for instance only ensures the + * uniqueness of this address on a given link. So we could possibly have two + * distinct buddies with the same ipv6 link local address on two distinct + * interfaces. Unfortunately XEP-0065 does not seem to specify how to deal with + * link local ip addresses properly... + * However, in practice the possiblity for such a conflict is relatively low + * (2011 - might be different in the future though?). + * + * @param host ipv4 or ipv6 address string + * @param buddy_ip ipv4 or ipv6 address string + * @return TRUE if they match, FALSE otherwise + */ +static gboolean +xep_cmp_addr(const char *host, const char *buddy_ip) +{ +#if defined(AF_INET6) && defined(HAVE_GETADDRINFO) + struct addrinfo hint, *res = NULL; + int ret; - } else if(!strcmp(type, "error")) { - purple_debug_info("bonjour", "si offer Message type - ERROR.\n"); + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_UNSPEC; + hint.ai_flags = AI_NUMERICHOST; + + ret = getaddrinfo(host, NULL, &hint, &res); + if(ret) + goto out; + + if(res->ai_family != AF_INET6 || + !IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)) { + freeaddrinfo(res); + goto out; + } + freeaddrinfo(res); + + if(strlen(buddy_ip) <= strlen(host) || + buddy_ip[strlen(host)] != '%') + return FALSE; - xfer = bonjour_si_xfer_find(bd, id, name); + return !strncmp(host, buddy_ip, strlen(host)); + +out: +#endif + return !strcmp(host, buddy_ip); +} + +static gboolean +__xep_bytestreams_parse(PurpleBuddy *pb, PurpleXfer *xfer, xmlnode *query, + const char *iq_id) +{ + const char *jid, *host, *port; + int portnum; + xmlnode *streamhost; + XepXfer *xf = NULL; + + xf = (XepXfer*)xfer->data; + for(streamhost = xmlnode_get_child(query, "streamhost"); + streamhost; + streamhost = xmlnode_get_next_twin(streamhost)) { - if(xfer == NULL) - purple_debug_info("bonjour", "xfer find fail.\n"); - else - purple_xfer_cancel_remote(xfer); - } else - purple_debug_info("bonjour", "si offer Message type - Unknown-%s.\n", type); + if(!(jid = xmlnode_get_attrib(streamhost, "jid")) || + !(host = xmlnode_get_attrib(streamhost, "host")) || + !(port = xmlnode_get_attrib(streamhost, "port")) || + !(portnum = atoi(port))) { + purple_debug_info("bonjour", "bytestream offer Message parse error.\n"); + continue; + } + + if(!xep_cmp_addr(host, xf->buddy_ip)) + continue; + + g_free(xf->iq_id); + xf->iq_id = g_strdup(iq_id); + xf->jid = g_strdup(jid); + xf->proxy_host = g_strdup(xf->buddy_ip); + xf->proxy_port = portnum; + purple_debug_info("bonjour", "bytestream offer parse" + "jid=%s host=%s port=%d.\n", jid, host, portnum); + bonjour_bytestreams_connect(xfer, pb); + return TRUE; } + + return FALSE; } + void xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb) { - const char *type, *from; + const char *type, *from, *iq_id, *sid; xmlnode *query; BonjourData *bd; + PurpleXfer *xfer; g_return_if_fail(pc != NULL); g_return_if_fail(packet != NULL); @@ -512,66 +608,27 @@ type = xmlnode_get_attrib(packet, "type"); from = purple_buddy_get_name(pb); query = xmlnode_get_child(packet,"query"); - if(type) { - if(!strcmp(type, "set")) { - const char *iq_id, *sid; - gboolean found = FALSE; - PurpleXfer *xfer; - - purple_debug_info("bonjour", "bytestream offer Message type - SET.\n"); - - iq_id = xmlnode_get_attrib(packet, "id"); - - sid = xmlnode_get_attrib(query, "sid"); - xfer = bonjour_si_xfer_find(bd, sid, from); + if(!type) + return; - if(xfer) { - const char *jid, *host, *port; - xmlnode *streamhost; - int portnum; - XepXfer *xf = NULL; + if(strcmp(type, "set")) { + purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type); + return; + } - xf = purple_xfer_get_protocol_data(xfer); - for(streamhost = xmlnode_get_child(query, "streamhost"); - streamhost; - streamhost = xmlnode_get_next_twin(streamhost)) { - - if((jid = xmlnode_get_attrib(streamhost, "jid")) && - (host = xmlnode_get_attrib(streamhost, "host")) && - (port = xmlnode_get_attrib(streamhost, "port")) && - (portnum = atoi(port))) { + purple_debug_info("bonjour", "bytestream offer Message type - SET.\n"); - if(!strcmp(host, xf->buddy_ip)) { - g_free(xf->iq_id); - xf->iq_id = g_strdup(iq_id); - xf->jid = g_strdup(jid); - xf->proxy_host = g_strdup(host); - xf->proxy_port = portnum; - purple_debug_info("bonjour", "bytestream offer parse" - "jid=%s host=%s port=%d.\n", jid, host, portnum); - bonjour_bytestreams_connect(xfer, pb); - found = TRUE; - break; - } - } else { - purple_debug_info("bonjour", "bytestream offer Message parse error.\n"); - } - } - } else { + iq_id = xmlnode_get_attrib(packet, "id"); - } - - if (!found) { - purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n"); + sid = xmlnode_get_attrib(query, "sid"); + xfer = bonjour_si_xfer_find(bd, sid, from); + if(xfer && __xep_bytestreams_parse(pb, xfer, query, iq_id)) + return; /* success */ - if (iq_id && xfer != NULL) - xep_ft_si_reject(bd, iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel"); - } + purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n"); - } else { - purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type); - } - } + if (iq_id && xfer != NULL) + xep_ft_si_reject(bd, iq_id, xfer->who, "404", "cancel"); } static void diff -r ca4714f51bb1 -r 0734b9c8c345 libpurple/protocols/bonjour/jabber.c --- a/libpurple/protocols/bonjour/jabber.c Tue Nov 29 03:59:09 2011 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Wed Nov 30 01:16:33 2011 +0000 @@ -665,9 +665,13 @@ /* Look for the buddy that has opened the conversation and fill information */ #ifdef HAVE_INET_NTOP - if (their_addr.ss_family == AF_INET6) + 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)); + + append_iface_if_linklocal(addrstr, + ((struct sockaddr_in6 *)&their_addr)->sin6_scope_id); + } else address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in *)&their_addr)->sin_addr, addrstr, sizeof(addrstr)); @@ -1449,3 +1453,19 @@ return ips; } + +void +append_iface_if_linklocal(char *ip, uint32_t interface) { + struct in6_addr in6_addr; + int len_remain = INET6_ADDRSTRLEN - strlen(ip); + + if (len_remain <= 1) + return; + + if (inet_pton(AF_INET6, ip, &in6_addr) != 1 || + !IN6_IS_ADDR_LINKLOCAL(&in6_addr)) + return; + + snprintf(ip + strlen(ip), len_remain, "%%%d", + interface); +} diff -r ca4714f51bb1 -r 0734b9c8c345 libpurple/protocols/bonjour/jabber.h --- a/libpurple/protocols/bonjour/jabber.h Tue Nov 29 03:59:09 2011 +0000 +++ b/libpurple/protocols/bonjour/jabber.h Wed Nov 30 01:16:33 2011 +0000 @@ -111,4 +111,6 @@ int xep_iq_send_and_free(XepIq *iq); GSList * bonjour_jabber_get_local_ips(int fd); +void append_iface_if_linklocal(char *ip, uint32_t interface); + #endif /* _BONJOUR_JABBER_H_ */ diff -r ca4714f51bb1 -r 0734b9c8c345 libpurple/protocols/bonjour/mdns_avahi.c --- a/libpurple/protocols/bonjour/mdns_avahi.c Tue Nov 29 03:59:09 2011 +0000 +++ b/libpurple/protocols/bonjour/mdns_avahi.c Wed Nov 30 01:16:33 2011 +0000 @@ -179,6 +179,9 @@ ip[0] = '\0'; avahi_address_snprint(ip, AVAHI_ADDRESS_STR_MAX, a); + if (protocol == AVAHI_PROTO_INET6) + append_iface_if_linklocal(ip, interface); + purple_debug_info("bonjour", "_resolve_callback - name:%s ip:%s prev_ip:%s\n", name, ip, rd->ip); @@ -190,7 +193,7 @@ } /* 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) { - rd->ip = g_strdup_printf("%s%%%d", ip, interface); + rd->ip = g_strdup_printf("%s", ip); bb->ips = g_slist_prepend(bb->ips, (gchar *) rd->ip); } else { rd->ip = g_strdup(ip);