static gboolean timeoutfunc(gpointer data) { struct stun_conn *sc = data; if(sc->retry >= 2) { purple_debug_warning("stun", "request timed out, giving up.\n"); if(sc->test == 2) nattype.type = PURPLE_STUN_NAT_TYPE_SYMMETRIC; /* set unknown */ nattype.status = PURPLE_STUN_STATUS_UNKNOWN; nattype.lookup_time = time(NULL); /* callbacks */ do_callbacks(); /* we don't need to remove the timeout (returning FALSE) */ sc->timeout = 0; close_stun_conn(sc); return FALSE; } purple_debug_info("stun", "request timed out, retrying.\n"); sc->retry++; if (sendto(sc->fd, sc->packet, sc->packetsize, 0, (struct sockaddr *)&(sc->addr), sizeof(struct sockaddr_in)) != (gssize)sc->packetsize) { purple_debug_warning("stun", "sendto failed\n"); return FALSE; } return TRUE; }
static void hbn_listen_cb(int fd, gpointer data) { StunHBNListenData *ld = (StunHBNListenData *)data; GInetAddress *address = NULL; GSocketAddress *socket_address = NULL; struct stun_conn *sc; static struct stun_header hdr_data; if(fd < 0) { nattype.status = PURPLE_STUN_STATUS_UNKNOWN; nattype.lookup_time = time(NULL); do_callbacks(); return; } sc = g_new0(struct stun_conn, 1); sc->fd = fd; sc->addr.sin_family = AF_INET; sc->addr.sin_port = htons(purple_network_get_port_from_fd(fd)); sc->addr.sin_addr.s_addr = INADDR_ANY; sc->incb = purple_input_add(fd, PURPLE_INPUT_READ, reply_cb, sc); address = G_INET_ADDRESS(ld->addresses->data); socket_address = g_inet_socket_address_new(address, ld->port); g_socket_address_to_native(socket_address, &(sc->addr), g_socket_address_get_native_size(socket_address), NULL); g_object_unref(G_OBJECT(address)); g_object_unref(G_OBJECT(socket_address)); g_resolver_free_addresses(ld->addresses); g_free(ld); hdr_data.type = htons(MSGTYPE_BINDINGREQUEST); hdr_data.len = 0; hdr_data.transid[0] = rand(); hdr_data.transid[1] = ntohl(((int)'g' << 24) + ((int)'a' << 16) + ((int)'i' << 8) + (int)'m'); hdr_data.transid[2] = rand(); hdr_data.transid[3] = rand(); if(sendto(sc->fd, &hdr_data, sizeof(struct stun_header), 0, (struct sockaddr *)&(sc->addr), sizeof(struct sockaddr_in)) < (gssize)sizeof(struct stun_header)) { nattype.status = PURPLE_STUN_STATUS_UNKNOWN; nattype.lookup_time = time(NULL); do_callbacks(); close_stun_conn(sc); return; } sc->test = 1; sc->packet = &hdr_data; sc->packetsize = sizeof(struct stun_header); sc->timeout = purple_timeout_add(500, (GSourceFunc) timeoutfunc, sc); }
static void hbn_listen_cb(int fd, gpointer data) { GSList *hosts = data; struct stun_conn *sc; static struct stun_header hdr_data; if(fd < 0) { nattype.status = PURPLE_STUN_STATUS_UNKNOWN; nattype.lookup_time = time(NULL); do_callbacks(); return; } sc = g_new0(struct stun_conn, 1); sc->fd = fd; sc->addr.sin_family = AF_INET; sc->addr.sin_port = htons(purple_network_get_port_from_fd(fd)); sc->addr.sin_addr.s_addr = INADDR_ANY; sc->incb = purple_input_add(fd, PURPLE_INPUT_READ, reply_cb, sc); hosts = g_slist_delete_link(hosts, hosts); memcpy(&(sc->addr), hosts->data, sizeof(struct sockaddr_in)); g_free(hosts->data); hosts = g_slist_delete_link(hosts, hosts); while (hosts) { hosts = g_slist_delete_link(hosts, hosts); g_free(hosts->data); hosts = g_slist_delete_link(hosts, hosts); } hdr_data.type = htons(MSGTYPE_BINDINGREQUEST); hdr_data.len = 0; hdr_data.transid[0] = rand(); hdr_data.transid[1] = ntohl(((int)'g' << 24) + ((int)'a' << 16) + ((int)'i' << 8) + (int)'m'); hdr_data.transid[2] = rand(); hdr_data.transid[3] = rand(); if(sendto(sc->fd, &hdr_data, sizeof(struct stun_header), 0, (struct sockaddr *)&(sc->addr), sizeof(struct sockaddr_in)) < (gssize)sizeof(struct stun_header)) { nattype.status = PURPLE_STUN_STATUS_UNKNOWN; nattype.lookup_time = time(NULL); do_callbacks(); close_stun_conn(sc); return; } sc->test = 1; sc->packet = &hdr_data; sc->packetsize = sizeof(struct stun_header); sc->timeout = purple_timeout_add(500, (GSourceFunc) timeoutfunc, sc); }
static void reply_cb(gpointer data, gint source, PurpleInputCondition cond) { struct stun_conn *sc = data; char buffer[65536]; char *tmp; gssize len; struct in_addr in; struct stun_attrib *attrib; struct stun_header *hdr; struct ifconf ifc; struct ifreq *ifr; struct sockaddr_in *sinptr; memset(&in, 0, sizeof(in)); len = recv(source, buffer, sizeof(buffer) - 1, 0); if (len < 0) { purple_debug_warning("stun", "unable to read stun response\n"); return; } buffer[len] = '\0'; if ((gsize)len < sizeof(struct stun_header)) { purple_debug_warning("stun", "got invalid response\n"); return; } hdr = (struct stun_header*) buffer; if ((gsize)len != (ntohs(hdr->len) + sizeof(struct stun_header))) { purple_debug_warning("stun", "got incomplete response\n"); return; } /* wrong transaction */ if(hdr->transid[0] != sc->packet->transid[0] || hdr->transid[1] != sc->packet->transid[1] || hdr->transid[2] != sc->packet->transid[2] || hdr->transid[3] != sc->packet->transid[3]) { purple_debug_warning("stun", "got wrong transid\n"); return; } if(sc->test==1) { if (hdr->type != MSGTYPE_BINDINGRESPONSE) { purple_debug_warning("stun", "Expected Binding Response, got %d\n", hdr->type); return; } tmp = buffer + sizeof(struct stun_header); while((buffer + len) > (tmp + sizeof(struct stun_attrib))) { attrib = (struct stun_attrib*) tmp; tmp += sizeof(struct stun_attrib); if (!((buffer + len) > (tmp + ntohs(attrib->len)))) break; if(attrib->type == htons(ATTRIB_MAPPEDADDRESS) && ntohs(attrib->len) == 8) { char *ip; /* Skip the first unused byte, * the family(1 byte), and the port(2 bytes); * then read the 4 byte IPv4 address */ memcpy(&in.s_addr, tmp + 4, 4); ip = inet_ntoa(in); if(ip) g_strlcpy(nattype.publicip, ip, sizeof(nattype.publicip)); } tmp += ntohs(attrib->len); } purple_debug_info("stun", "got public ip %s\n", nattype.publicip); nattype.status = PURPLE_STUN_STATUS_DISCOVERED; nattype.type = PURPLE_STUN_NAT_TYPE_UNKNOWN_NAT; nattype.lookup_time = time(NULL); /* is it a NAT? */ ifc.ifc_len = sizeof(buffer); ifc.ifc_req = (struct ifreq *) buffer; ioctl(source, SIOCGIFCONF, &ifc); tmp = buffer; while(tmp < buffer + ifc.ifc_len) { ifr = (struct ifreq *) tmp; tmp += sizeof(struct ifreq); if(ifr->ifr_addr.sa_family == AF_INET) { /* we only care about ipv4 interfaces */ sinptr = (struct sockaddr_in *) &ifr->ifr_addr; if(sinptr->sin_addr.s_addr == in.s_addr) { /* no NAT */ purple_debug_info("stun", "no nat\n"); nattype.type = PURPLE_STUN_NAT_TYPE_PUBLIC_IP; } } } #ifndef NOTYET close_stun_conn(sc); do_callbacks(); #else purple_timeout_remove(sc->timeout); sc->timeout = 0; do_test2(sc); } else if(sc->test == 2) { close_stun_conn(sc); nattype.type = PURPLE_STUN_NAT_TYPE_FULL_CONE; do_callbacks(); #endif } }