/* shared between STUN and TURN */ static void tcp_estab_handler(void *arg) { struct candidate *cand = arg; int err; re_printf("TCP established to STUN/TURN-server\n"); switch (cand->type) { case TYPE_STUN: err = stun_request(NULL, cand->ag->stun, IPPROTO_TCP, cand->tc, NULL, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_resp_handler, cand, 0); if (err) { re_printf("tcp: stun_request failed (%m)\n", err); } break; case TYPE_TURN: err = turnc_alloc(&cand->turnc, NULL, IPPROTO_TCP, cand->tc, LAYER_TURN, &cand->turn_srv, cand->ag->cli->param.username, cand->ag->cli->param.password, TURN_DEFAULT_LIFETIME, turnc_handler, cand); if (err) { re_printf("tcp: turn client: %m\n", err); } break; } }
static int hairpin_send(struct nat_hairpinning *nh, const struct sa *srv) { return stun_request(NULL, nh->stun, nh->proto, NULL, srv, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler2, nh, 1, STUN_ATTR_SOFTWARE, stun_software); }
static int mapped_send(struct nat_hairpinning *nh) { return stun_request(NULL, nh->stun, nh->proto, nh->us ? (void *)nh->us : (void *)nh->tc, &nh->srv, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, nh, 1, STUN_ATTR_SOFTWARE, stun_software); }
static int allocate_request(struct turnc *t) { const int proto = IPPROTO_UDP; return stun_request(&t->ct, t->stun, t->proto, t->sock, &t->srv, 0, STUN_METHOD_ALLOCATE, t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash), false, allocate_resp_handler, t, 6, STUN_ATTR_LIFETIME, &t->lifetime, STUN_ATTR_REQ_TRANSPORT, &proto, STUN_ATTR_USERNAME, t->realm ? t->username : NULL, STUN_ATTR_REALM, t->realm, STUN_ATTR_NONCE, t->nonce, STUN_ATTR_SOFTWARE, stun_software); }
static void tcp_estab_handler(void *arg) { struct nat_mapping *nm = arg; int err; err = stun_request(NULL, nm->stun, IPPROTO_TCP, nm->tc, NULL, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, nm, 1, STUN_ATTR_SOFTWARE, stun_software); if (err) { DEBUG_WARNING("TCP established: mapping_send (%m)\n", err); nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg); } }
static int createperm_request(struct perm *perm, bool reset_ls) { struct turnc *t = perm->turnc; if (reset_ls) turnc_loopstate_reset(&perm->ls); return stun_request(&perm->ct, t->stun, t->proto, t->sock, &t->srv, 0, STUN_METHOD_CREATEPERM, t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash), false, createperm_resp_handler, perm, 5, STUN_ATTR_XOR_PEER_ADDR, &perm->peer, STUN_ATTR_USERNAME, t->realm ? t->username : NULL, STUN_ATTR_REALM, t->realm, STUN_ATTR_NONCE, t->nonce, STUN_ATTR_SOFTWARE, stun_software); }
static int mapping_send(struct nat_mapping *nm) { switch (nm->proto) { case IPPROTO_UDP: return stun_request(NULL, nm->stun, IPPROTO_UDP, nm->us, &nm->srv, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, nm, 1, STUN_ATTR_SOFTWARE, stun_software); case IPPROTO_TCP: nm->tc = mem_deref(nm->tc); nm->tc = mem_ref(nm->tcv[nm->test_phase-1]); return tcp_conn_connect(nm->tc, &nm->srv); default: return EPROTONOSUPPORT; } }
/** Gather Server Reflexive address */ static int send_binding_request(struct icem *icem, struct icem_comp *comp) { int err; if (comp->ct_gath) return EALREADY; err = stun_request(&comp->ct_gath, icem->stun, icem->proto, comp->sock, &icem->stun_srv, 0, STUN_METHOD_BINDING, NULL, false, 0, stun_resp_handler, comp, 1, STUN_ATTR_SOFTWARE, stun_software); if (err) return err; ++icem->nstun; return 0; }
static void udpconn_keepalive_handler(void *arg) { struct sip_udpconn *uc = arg; int err; if (!uc->kal.head) { /* no need for us anymore */ udpconn_close(uc, 0); mem_deref(uc); return; } err = stun_request(&uc->ct, uc->stun, IPPROTO_UDP, uc->us, &uc->paddr, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, uc, 1, STUN_ATTR_SOFTWARE, stun_software); if (err) { udpconn_close(uc, err); mem_deref(uc); } }
static int refresh_request(struct turnc *t, uint32_t lifetime, bool reset_ls, stun_resp_h *resph, void *arg) { if (!t) return EINVAL; if (reset_ls) turnc_loopstate_reset(&t->ls); if (t->ct) t->ct = mem_deref(t->ct); return stun_request(&t->ct, t->stun, t->proto, t->sock, &t->srv, 0, STUN_METHOD_REFRESH, t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash), false, resph, arg, 5, STUN_ATTR_LIFETIME, &lifetime, STUN_ATTR_USERNAME, t->realm ? t->username : NULL, STUN_ATTR_REALM, t->realm, STUN_ATTR_NONCE, t->nonce, STUN_ATTR_SOFTWARE, stun_software); }
int icem_conncheck_send(struct ice_candpair *cp, bool use_cand, bool trigged) { struct ice_cand *lcand = cp->lcand; struct icem *icem = cp->icem; char username_buf[64]; size_t presz = 0; uint32_t prio_prflx; uint16_t ctrl_attr; int err = 0; icem_candpair_set_state(cp, ICE_CANDPAIR_INPROGRESS); (void)re_snprintf(username_buf, sizeof(username_buf), "%s:%s", icem->rufrag, icem->lufrag); /* PRIORITY and USE-CANDIDATE */ prio_prflx = ice_cand_calc_prio(ICE_CAND_TYPE_PRFLX, 0, lcand->compid); switch (icem->lrole) { case ICE_ROLE_CONTROLLING: ctrl_attr = STUN_ATTR_CONTROLLING; if (icem->conf.nom == ICE_NOMINATION_AGGRESSIVE) use_cand = true; break; case ICE_ROLE_CONTROLLED: ctrl_attr = STUN_ATTR_CONTROLLED; break; default: return EINVAL; } #if ICE_TRACE icecomp_printf(cp->comp, "Tx %H ---> %H (%s) %s %s\n", icem_cand_print, cp->lcand, icem_cand_print, cp->rcand, ice_candpair_state2name(cp->state), use_cand ? "[USE]" : "", trigged ? "[Trigged]" : ""); #else (void)trigged; #endif /* A connectivity check MUST utilize the STUN short term credential mechanism. */ /* The password is equal to the password provided by the peer */ if (!icem->rpwd) { DEBUG_WARNING("no remote password!\n"); } if (cp->ct_conn) { DEBUG_WARNING("send_req: CONNCHECK already Pending!\n"); return EBUSY; } switch (lcand->type) { case ICE_CAND_TYPE_RELAY: /* Creating Permissions for Relayed Candidates */ err = turnc_add_chan(cp->comp->turnc, &cp->rcand->addr, NULL, NULL); if (err) { DEBUG_WARNING("add channel: %m\n", err); break; } presz = 4; /*@fallthrough@*/ case ICE_CAND_TYPE_HOST: case ICE_CAND_TYPE_SRFLX: case ICE_CAND_TYPE_PRFLX: cp->ct_conn = mem_deref(cp->ct_conn); err = stun_request(&cp->ct_conn, icem->stun, icem->proto, cp->comp->sock, &cp->rcand->addr, presz, STUN_METHOD_BINDING, (uint8_t *)icem->rpwd, str_len(icem->rpwd), true, stunc_resp_handler, cp, 4, STUN_ATTR_USERNAME, username_buf, STUN_ATTR_PRIORITY, &prio_prflx, ctrl_attr, &icem->tiebrk, STUN_ATTR_USE_CAND, use_cand ? &use_cand : 0); break; default: DEBUG_WARNING("unknown candidate type %d\n", lcand->type); err = EINVAL; break; } return err; }
int main(int argc, char **argv) { int i, rc; char buffer[BUFLEN]; uint8_t arp[sizeof(struct ethhdr) + sizeof(struct arp_hdr)]; if(argc != 5) { fprintf(stderr, "Usage: %s <interface> <listen-port> <remote-host> <remote-port>\n\n", argv[0]); return(1); } memset(fd_udp_port, 0, sizeof(fd_udp_port)); int port = atoi(argv[2]); char *iface_out = argv[1]; char *rhost = argv[3]; int rport = atoi(argv[4]); int sock_udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); int sock_raw_recv = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); int sock_raw_send = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); setsockopt(sock_raw_recv, SOL_SOCKET, SO_BINDTODEVICE, iface_out, 4); setsockopt(sock_raw_send, SOL_SOCKET, SO_BINDTODEVICE, iface_out, 4); int on = 1; setsockopt(sock_raw_send, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)); ssize_t recsize; struct sockaddr_in udp_listen_addr, udp_client_addr, other_addr; socklen_t slen = sizeof(struct sockaddr_in); socklen_t udp_client_addr_size = slen; udp_listen_addr.sin_family = AF_INET; udp_listen_addr.sin_addr.s_addr = INADDR_ANY; if(port == 0) { srand(time(NULL)); do { port = 6001 + rand() % 22000; // random port 6001-28000 udp_listen_addr.sin_port = htons(port); rc = bind_public(sock_udp,(struct sockaddr *)&udp_listen_addr, sizeof(udp_listen_addr)); } while(rc == -1); } else { udp_listen_addr.sin_port = htons(port); rc = bind_public(sock_udp,(struct sockaddr *)&udp_listen_addr, sizeof(udp_listen_addr)); } if(rc == -1) { perror("error bind failed"); close(sock_udp); fprintf(stderr, "(is another tunnel or socat process already running?)\n"); exit(EXIT_FAILURE); } DEBUG_PRINT("Listening on port %i...\n", port); // Find interface index from interface name and store index in // struct sockaddr_ll device, which will be used as an argument of sendto(). struct sockaddr_ll device; memset(&device, 0, sizeof(struct sockaddr_ll)); device.sll_family = AF_PACKET; device.sll_halen = 6; if((device.sll_ifindex = if_nametoindex(iface_out)) == 0) { perror("if_nametoindex() failed to obtain interface index "); exit(EXIT_FAILURE); } DEBUG_PRINT("Index for interface %s is %i\n", iface_out, device.sll_ifindex); struct ifreq ifr; ifr.ifr_addr.sa_family = AF_INET; strncpy(ifr.ifr_name, iface_out, IFNAMSIZ-1); // get MAC address of iface_out if(ioctl(sock_raw_send, SIOCGIFHWADDR, &ifr) < 0) { perror("ioctl() failed to get source MAC address "); exit(EXIT_FAILURE); } uint8_t if_raw_mac[6]; memcpy(if_raw_mac, ifr.ifr_hwaddr.sa_data, 6 * sizeof(uint8_t)); memcpy(device.sll_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof (uint8_t)); DEBUG_PRINT("MAC address for interface %s is ", iface_out); for(i=0; i<5; i++) { DEBUG_PRINT("%02x:", if_raw_mac[i]); } DEBUG_PRINT("%02x\n", if_raw_mac[5]); // get IP address address of iface_out if(ioctl(sock_raw_send, SIOCGIFADDR, &ifr) < 0) { perror("ioctl() failed to get interface IP address"); exit(EXIT_FAILURE); } struct in_addr if_raw_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; printf("TUNIP=%s \n",inet_ntoa(if_raw_addr)); DEBUG_PRINT("Rewriting IP source address to %s\n", inet_ntoa(if_raw_addr)); // send gratuitous ARP DEBUG_PRINT("Sending gratuitous ARP to map "); for(i=0; i<5; i++) { DEBUG_PRINT("%02x:", if_raw_mac[i]); } DEBUG_PRINT("%02x to %s\n", if_raw_mac[5], inet_ntoa(if_raw_addr)); memset(arp, 0, sizeof(arp)); struct ethhdr *arp_ethhdr = (void *)arp; struct arp_hdr *arp_arphdr = (void *)arp + sizeof(struct ethhdr); arp_arphdr->htype = htons(1); arp_arphdr->ptype = htons(ETH_P_IP); arp_arphdr->hlen = 6; arp_arphdr->plen = 4; arp_arphdr->opcode = htons(2); // 1 = ARP request, 2 = ARP reply memcpy(arp_arphdr->sender_mac, if_raw_mac, 6 * sizeof(uint8_t)); memcpy(arp_arphdr->sender_ip, &if_raw_addr.s_addr, 4 * sizeof(uint8_t)); memcpy(arp_arphdr->target_mac, if_raw_mac, 6 * sizeof(uint8_t)); memcpy(arp_arphdr->target_ip, &if_raw_addr.s_addr, 4 * sizeof(uint8_t)); memcpy(arp_ethhdr->h_source, if_raw_mac, 6 * sizeof (uint8_t)); memset(arp_ethhdr->h_dest, 0xff, 6 * sizeof (uint8_t)); arp_ethhdr->h_proto = htons(ETH_P_ARP); int bytes_sent = sendto(sock_raw_recv, arp, sizeof(arp), 0,(struct sockaddr*)&device, sizeof(device)); if(bytes_sent != sizeof(arp)) { perror("sendto(arp) failed"); } struct sockaddr_in server,mapped; struct hostent *hostinfo; hostinfo = gethostbyname(stunserver); if (!hostinfo) { fprintf(stderr, "Error resolving host %s\n", stunserver); return -1; } memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_addr = *(struct in_addr*) hostinfo->h_addr; server.sin_port = htons(stunport); int res = stun_request(sock_udp, &server, NULL, &mapped); if (!res && (mapped.sin_addr.s_addr != htonl(INADDR_ANY))) { printf("CONNECT=%s:",inet_ntoa(mapped.sin_addr)); printf("%d\n",htons(mapped.sin_port)); } DEBUG_PRINT("Sending \"OPEN!\" packet to remote host (for UDP hole punching)\n"); hostinfo = gethostbyname(rhost); if(!hostinfo) { fprintf(stderr, "Error resolving remote-host\n"); return -1; } memset(&udp_client_addr, 0, sizeof(udp_client_addr)); udp_client_addr.sin_family = AF_INET; udp_client_addr.sin_addr = *(struct in_addr*) hostinfo->h_addr; udp_client_addr.sin_port = htons(rport); strcpy(buffer, "OPEN!"); sendto(sock_udp, buffer, 5, 0,(struct sockaddr*)&udp_client_addr, udp_client_addr_size); DEBUG_PRINT("Waiting for connection from remote host...\n"); recsize = recvfrom(sock_udp, (void *)buffer, BUFLEN, 0, (struct sockaddr *)&udp_client_addr, &udp_client_addr_size); if(recsize == -1) { perror("recv error"); close(sock_udp); close(sock_raw_recv); close(sock_raw_send); exit(EXIT_FAILURE); } DEBUG_PRINT("Received packet from %s:%d\n", inet_ntoa(udp_client_addr.sin_addr), ntohs(udp_client_addr.sin_port)); fd_set rset; FD_ZERO(&rset); maxfd = sock_raw_send; int eof_socket = 0; while(!eof_socket) { int res = -1; FD_SET(sock_udp, &rset); FD_SET(sock_raw_recv, &rset); FD_SET(sock_raw_send, &rset); int sock_fake; for(sock_fake = maxfd + 1; sock_fake <= maxfd; sock_fake++) { FD_SET(sock_fake, &rset); } res = select(maxfd + 1, &rset, 0, 0, 0); if(res < 0) { fprintf(stderr, "select failed!\n"); exit(EXIT_FAILURE); } if(FD_ISSET(sock_raw_recv, &rset)) { slen = sizeof(struct sockaddr_in); recsize = recvfrom(sock_raw_recv, (void *)buffer, BUFLEN, 0, (struct sockaddr *)&other_addr, &slen); if(recsize > 0) { struct ethhdr *eth = (struct ethhdr *)buffer; if(ntohs(eth->h_proto) == ETH_P_ARP) { struct arp_hdr *packet = (void *)buffer + sizeof(struct ethhdr); if(ntohs(packet->opcode) == 1) // ARP request { /* printf("Received ARP request from RAW socket\n"); */ /* printf("target_ip: %s\n", inet_ntoa(*(struct in_addr *)&packet->target_ip)); */ if(0 == memcmp(&packet->target_ip, &if_raw_addr.s_addr, 4 * sizeof(uint8_t))) // our IP { DEBUG_PRINT("Received ARP request for our IP from RAW socket, replying...\n"); // send ARP reply back to the sender of the ARP request memcpy(arp_ethhdr->h_dest, packet->sender_mac, 6 * sizeof (uint8_t)); int bytes_sent = sendto(sock_raw_recv, arp, sizeof(arp), 0,(struct sockaddr*)&device, sizeof(device)); if(bytes_sent != sizeof(arp)) { perror("sendto(arp) failed"); } } } } else if(ntohs(eth->h_proto) == ETH_P_IP) { struct iphdr *packet = (void *)buffer + sizeof(struct ethhdr); if(packet->daddr == if_raw_addr.s_addr) { DEBUG_PRINT("Received IP packet from RAW socket: "); DEBUG_PRINT("%s -> ", inet_ntoa(*(struct in_addr *)&packet->saddr)); DEBUG_PRINT("%s\n", inet_ntoa(*(struct in_addr *)&packet->daddr)); int skip = 0; if(packet->protocol == IPPROTO_ICMP) { struct icmphdr *data = (void *)buffer + sizeof(struct ethhdr) + sizeof(struct iphdr); if(data->type == ICMP_ECHOREPLY) { DEBUG_PRINT("Protocol: ICMP, echo reply, id: %x\n", data->un.echo.id); } else { DEBUG_PRINT("Protocol: ICMP, type: %d, code: %d\n", data->type, data->code); } } else if(packet->protocol == IPPROTO_TCP) { struct tcphdr *data = (void *)packet + sizeof(struct iphdr); DEBUG_PRINT("Protocol: TCP, sport: %d, dport: %d\n", htons(data->source), htons(data->dest)); DEBUG_PRINT("If we had a TCP stack we'd have to feed this packet into it, but now we're skipping it.\n"); skip = 1; } else if(packet->protocol == IPPROTO_UDP) { struct udphdr *data = (void *)packet + sizeof(struct iphdr); DEBUG_PRINT("Protocol: UDP, sport: %d, dport: %d\n", htons(data->source), htons(data->dest)); if(packet->saddr == udp_client_addr.sin_addr.s_addr) { if(htons(data->source) == rport && htons(data->dest) == port) { DEBUG_PRINT("Skipping packet, this is our own UDP tunnel!\n"); skip = 1; } } } else { DEBUG_PRINT("Unknown protocol: %d\n", packet->protocol); } if(skip == 0) { int bytes_sent = sendto(sock_udp, packet, recsize-sizeof(struct ethhdr), 0,(struct sockaddr*)&udp_client_addr, udp_client_addr_size); if(bytes_sent != recsize-sizeof(struct ethhdr)) { perror("sendto() failed"); DEBUG_PRINT("tried sending to %s:%d\n", inet_ntoa(udp_client_addr.sin_addr), ntohs(udp_client_addr.sin_port)); hexDump("packet", packet, recsize-sizeof(struct ethhdr)); hexDump("udp_client_addr", &udp_client_addr, udp_client_addr_size); } } } } } } if(FD_ISSET(sock_udp, &rset)) { slen = sizeof(struct sockaddr_in); recsize = recvfrom(sock_udp, (void *)buffer, BUFLEN, 0, (struct sockaddr *)&other_addr, &slen); if(recsize == 4 && strcmp(buffer, "EXIT") == 0) { eof_socket = 1; } else if(recsize > 0) { DEBUG_PRINT("Received packet from UDP socket\n"); struct iphdr *packet = (struct iphdr *)buffer; struct in_addr in_addr_source, in_addr_dest; in_addr_source.s_addr = packet->saddr; in_addr_dest.s_addr = packet->daddr; struct sockaddr_in dest_addr; dest_addr.sin_family = AF_INET; dest_addr.sin_addr = in_addr_dest; DEBUG_PRINT("saddr: %s ", inet_ntoa(in_addr_source)); DEBUG_PRINT("daddr: %s\n", inet_ntoa(in_addr_dest)); /* hexDump("buffer", &buffer, recsize); */ if(packet->protocol == IPPROTO_ICMP) { struct icmphdr *data = (void *)buffer + sizeof(struct iphdr); DEBUG_PRINT("Protocol: ICMP, id: %x\n", data->un.echo.id); if(-1 == sendto(sock_raw_send, buffer, recsize, 0,(struct sockaddr*)&dest_addr, sizeof(dest_addr))) { perror("sendto() failed"); } } else if(packet->protocol == IPPROTO_TCP) { // TCP should be handled by the other tunnel endpoint via tun2socks and not actually be sent through the UDP tunnel... struct tcphdr *data = (void *)buffer + sizeof(struct iphdr); DEBUG_PRINT("Protocol: TCP, sport: %d, dport: %d - what is this packet doing here?\n", htons(data->source), htons(data->dest)); } else if(packet->protocol == IPPROTO_UDP) { struct udphdr *data = (void *)buffer + sizeof(struct iphdr); DEBUG_PRINT("Protocol: UDP, sport: %d, dport: %d\n", htons(data->source), htons(data->dest)); // open and bind a UDP socket on the source port so the kernel doesn't send ICMP unreachable packets int sock_fake_udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); struct sockaddr_in fake_addr; fake_addr.sin_family = AF_INET; fake_addr.sin_addr.s_addr = INADDR_ANY; fake_addr.sin_port = data->source; if(-1 == bind_public(sock_fake_udp,(struct sockaddr *)&fake_addr, sizeof(struct sockaddr_in))) { perror("bind() failed"); } fd_udp_port[sock_fake_udp] = htons(data->dest); maxfd = sock_fake_udp; if(-1 == sendto(sock_raw_send, buffer, recsize, 0,(struct sockaddr*)&dest_addr, sizeof(dest_addr))) { perror("sendto() failed"); } } else { if(-1 == sendto(sock_raw_send, buffer, recsize, 0,(struct sockaddr*)&dest_addr, sizeof(dest_addr))) { perror("sendto() failed"); } } } else eof_socket = 1; } for(sock_fake = sock_raw_send + 1; sock_fake <= maxfd; sock_fake++) { if(FD_ISSET(sock_fake, &rset)) { slen = sizeof(struct sockaddr_in); // just empty the socket's recv buffer and discard the data, // it is read and forwarded by the RAW socket sock_raw_recv recsize = recvfrom(sock_fake, (void *)buffer, BUFLEN, 0, (struct sockaddr *)&other_addr, &slen); } } } return(0); }
static int test_stun_request(int proto, bool natted) { struct stunserver *srv = NULL; struct stun_ctrans *ct = NULL; struct nat *nat = NULL; struct test test; struct sa laddr, public_addr; int err; memset(&test, 0, sizeof(test)); err = stunserver_alloc(&srv); if (err) goto out; err = stun_alloc(&test.stun, NULL, NULL, NULL); if (err) goto out; if (proto == IPPROTO_UDP) { err = sa_set_str(&laddr, "127.0.0.1", 0); TEST_ERR(err); err = udp_listen(&test.us, &laddr, udp_recv_handler, &test); if (err) goto out; err = udp_local_get(test.us, &laddr); TEST_ERR(err); } if (natted) { err = sa_set_str(&public_addr, "4.5.6.7", 0); TEST_ERR(err); err = nat_alloc(&nat, srv->us, &public_addr); if (err) goto out; sa_set_port(&public_addr, sa_port(&laddr)); } else { public_addr = laddr; } err = stun_request(&ct, test.stun, proto, test.us, stunserver_addr(srv, proto), 0, STUN_METHOD_BINDING, NULL, 0, true, stun_resp_handler, &test, 0); if (err) goto out; TEST_ASSERT(ct != NULL); err = re_main_timeout(100); if (err) goto out; if (srv->err) { err = srv->err; goto out; } if (test.err) { err = test.err; goto out; } /* verify results */ TEST_ASSERT(srv->nrecv >= 1); TEST_EQUALS(1, test.n_resp); if (proto == IPPROTO_UDP) { TEST_SACMP(&public_addr, &test.mapped_addr, SA_ALL); } out: mem_deref(test.stun); mem_deref(test.us); mem_deref(nat); mem_deref(srv); return err; }