123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- #include <arpa/inet.h>
- #include <netinet/in.h>
- #include <netinet/ip.h>
- #include <netinet/udp.h>
- #include <string.h>
- #include <errno.h>
- #include <libubox/usock.h>
- #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);
- }
- }
|