#include #include #include #include #include #include #include #include "unetd.h" static inline int avl_stun_cmp(const void *k1, const void *k2, void *priv) { return memcmp(k1, k2, 12); } static bool has_connected_peer(struct network *net, bool pex) { struct network_peer *peer; vlist_for_each_element(&net->peers, peer, node) { if (pex && !peer->pex_port) continue; if (peer->state.connected || peer->indirect) return true; } return false; } void network_stun_server_add(struct network *net, const char *host) { struct network_stun *stun = &net->stun; struct network_stun_server *s; char *name_buf; s = calloc_a(sizeof(*s), &name_buf, strlen(host) + 1); s->pending_node.key = s->req.transaction; s->host = strcpy(name_buf, host); list_add_tail(&s->list, &stun->servers); } static void network_stun_close_socket(struct network *net) { struct network_host *local = net->net_config.local_host; struct network_stun *stun = &net->stun; if (!stun->wgport_disabled) return; D_NET(net, "close STUN socket"); uloop_fd_delete(&stun->socket); close(stun->socket.fd); wg_init_local(net, &local->peer); stun->wgport_disabled = false; } static void network_stun_socket_cb(struct uloop_fd *fd, unsigned int events) { struct network_stun *stun = container_of(fd, struct network_stun, socket); struct network *net = container_of(stun, struct network, stun); char buf[1024]; ssize_t len; while (1) { len = recv(fd->fd, buf, sizeof(buf), 0); if (len < 0) { if (errno == EAGAIN) break; if (errno == EINTR) continue; perror("recv"); network_stun_close_socket(net); return; } if (!stun_msg_is_valid(buf, len)) continue; network_stun_rx_packet(net, buf, len); } } static void network_stun_open_socket(struct network *net) { struct network_host *local = net->net_config.local_host; struct network_stun *stun = &net->stun; int fd; if (stun->wgport_disabled) return; D_NET(net, "open STUN socket"); wg_init_local(net, NULL); fd = usock(USOCK_SERVER | USOCK_UDP | USOCK_IPV4ONLY | USOCK_NONBLOCK, NULL, usock_port(stun->port_local)); if (fd < 0) { wg_init_local(net, &local->peer); return; } stun->socket.fd = fd; uloop_fd_add(&stun->socket, ULOOP_READ); stun->wgport_disabled = true; } static bool network_stun_query_next(struct network *net) { struct network_stun *stun = &net->stun; struct network_stun_server *s; char addrstr[INET6_ADDRSTRLEN]; union network_endpoint ep; uint16_t res_port = 0; const void *msg; ssize_t ret; size_t len; s = list_first_entry(&stun->servers, struct network_stun_server, list); if (s->pending) return false; /* send next query */ if (network_get_endpoint(&ep, AF_INET, s->host, 0, s->seq++) < 0) { D_NET(net, "lookup failed for STUN host %s", s->host); goto out; } if (ep.sa.sa_family != AF_INET || !ep.in.sin_port) goto out; if (!stun->wgport_disabled && stun->auth_port_ext) res_port = stun->auth_port_ext; D_NET(net, "Send STUN query to %s, res_port=%d, wg_disabled=%d", inet_ntop(ep.sa.sa_family, network_endpoint_addr(&ep, NULL), addrstr, sizeof(addrstr)), res_port, stun->wgport_disabled); msg = stun_msg_request_prepare(&s->req, &len, res_port); if (!msg) goto out; retry: s->req_auth_port = false; if (stun->wgport_disabled) { ret = sendto(stun->socket.fd, msg, len, 0, &ep.sa, sizeof(ep.in)); } else if (!stun->auth_port_ext) { s->req_auth_port = true; ret = sendto(pex_socket(), msg, len, 0, &ep.sa, sizeof(ep.in)); } else { struct { struct ip ip; struct udphdr udp; } packet_hdr = {}; union network_addr local_addr = {}; network_get_local_addr(&local_addr, &ep); packet_hdr.ip = (struct ip){ .ip_hl = 5, .ip_v = 4, .ip_ttl = 64, .ip_p = IPPROTO_UDP, .ip_src = local_addr.in, .ip_dst = ep.in.sin_addr, }; packet_hdr.udp = (struct udphdr){ .uh_sport = htons(stun->port_local), .uh_dport = ep.in.sin_port, }; ep.in.sin_port = 0; ret = sendto_rawudp(pex_raw_socket(AF_INET), &ep, &packet_hdr, sizeof(packet_hdr), msg, len); } if (ret < 0 && errno == EINTR) goto retry; out: avl_insert(&stun->pending, &s->pending_node); s->pending = true; if (!list_is_last(&s->list, &stun->servers)) list_move_tail(&s->list, &stun->servers); return true; } static void network_stun_query_clear_pending(struct network *net) { struct network_stun *stun = &net->stun; struct network_stun_server *s; list_for_each_entry(s, &stun->servers, list) { if (!s->pending) continue; avl_delete(&stun->pending, &s->pending_node); s->pending = false; } } void network_stun_rx_packet(struct network *net, const void *data, size_t len) { struct network_stun *stun = &net->stun; const struct stun_msg_hdr *hdr = data; struct network_stun_server *s; s = avl_find_element(&stun->pending, hdr->transaction, s, pending_node); if (!s) return; if (!stun_msg_request_complete(&s->req, data, len)) return; if (!s->req.port) return; network_stun_update_port(net, s->req_auth_port, s->req.port); if (s->req_auth_port) stun->state = STUN_STATE_STUN_QUERY_SEND; else stun->state = STUN_STATE_IDLE; network_stun_query_clear_pending(net); uloop_timeout_set(&stun->timer, 1); } static void network_stun_timer_cb(struct uloop_timeout *t) { struct network_stun *stun = container_of(t, struct network_stun, timer); struct network *net = container_of(stun, struct network, stun); unsigned int next = 0; restart: switch (stun->state) { case STUN_STATE_IDLE: network_stun_close_socket(net); next = 15 * 60 * 1000; stun->state = STUN_STATE_STUN_QUERY_SEND; D_NET(net, "STUN idle"); break; case STUN_STATE_PEX_QUERY_WAIT: stun->state = STUN_STATE_STUN_QUERY_SEND; fallthrough; case STUN_STATE_STUN_QUERY_SEND: if (network_stun_query_next(net)) { next = 50; break; } stun->state = STUN_STATE_STUN_QUERY_WAIT; D_NET(net, "wait for STUN server responses"); next = 1000; break; case STUN_STATE_STUN_QUERY_WAIT: D_NET(net, "timeout waiting for STUN server responses, retry=%d", stun->retry); network_stun_query_clear_pending(net); if (stun->retry > 0) { stun->retry--; stun->state = STUN_STATE_STUN_QUERY_SEND; goto restart; } if (!stun->port_ext && !stun->wgport_disabled) { network_stun_open_socket(net); stun->state = STUN_STATE_STUN_QUERY_SEND; stun->retry = 2; } else { stun->state = STUN_STATE_IDLE; } goto restart; } if (next) uloop_timeout_set(t, next); } void network_stun_update_port(struct network *net, bool auth, uint16_t val) { struct network_stun *stun = &net->stun; uint16_t *port = auth ? &stun->auth_port_ext : &stun->port_ext; D_NET(net, "Update external %s port: %d", auth ? "auth" : "data", val); *port = val; } void network_stun_start(struct network *net) { struct network_host *local = net->net_config.local_host; struct network_stun *stun = &net->stun; unsigned int next = 1; if (!local || list_empty(&stun->servers)) return; if (local->peer.port != stun->port_local) { stun->port_ext = 0; stun->port_local = local->peer.port; } if (!stun->port_ext && has_connected_peer(net, true)) { D_NET(net, "wait for port information from PEX"); stun->state = STUN_STATE_PEX_QUERY_WAIT; next = 60 * 1000; } else { if (!stun->port_ext && !has_connected_peer(net, false)) network_stun_open_socket(net); stun->state = STUN_STATE_STUN_QUERY_SEND; stun->retry = 2; } uloop_timeout_set(&stun->timer, next); } void network_stun_init(struct network *net) { struct network_stun *stun = &net->stun; stun->socket.cb = network_stun_socket_cb; stun->timer.cb = network_stun_timer_cb; INIT_LIST_HEAD(&stun->servers); avl_init(&stun->pending, avl_stun_cmp, true, NULL); } void network_stun_free(struct network *net) { struct network_stun *stun = &net->stun; struct network_stun_server *s, *tmp; uloop_timeout_cancel(&stun->timer); network_stun_close_socket(net); avl_remove_all_elements(&stun->pending, s, pending_node, tmp) s->pending = false; list_for_each_entry_safe(s, tmp, &stun->servers, list) { list_del(&s->list); free(s); } }