/* * udprelay.cpp - Setup UDP relay for both client and server * * Copyright (C) 2013 - 2019, Max Lv * * This file is part of the shadowsocks-libev. * * shadowsocks-libev is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * shadowsocks-libev is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with shadowsocks-libev; see the file COPYING. If not, see * . */ #include #include #include #include #include #include #include #include #include #ifndef __MINGW32__ #include #include #include #include #include #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__) #include #include #define SET_INTERFACE #endif #include "core.h" #include "utils.h" #include "netutils.h" #include "cache.h" #include "udprelay.h" #include "ss_ref.h" #include "ss_windows.h" #ifdef MODULE_REMOTE #define MAX_UDP_CONN_NUM 512 #else #define MAX_UDP_CONN_NUM 256 #endif #ifdef MODULE_REMOTE #ifdef MODULE_ #error "MODULE_REMOTE and MODULE_LOCAL should not be both defined" #endif #endif #ifndef EAGAIN #define EAGAIN EWOULDBLOCK #endif #ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN #endif static void server_recv_cb(SS_P_ ss_io *w, int revents); static void remote_recv_cb(SS_P_ ss_io *w, int revents); static void remote_timeout_cb(SS_P_ ss_timer *watcher, int revents); static char *hash_key(const int af, const struct sockaddr_storage *addr); #ifdef MODULE_REMOTE static void resolv_free_cb(void *data); static void resolv_cb(struct sockaddr *addr, void *data); #endif static void close_and_free_remote(SS_P_ remote_ctx_t *ctx); static remote_ctx_t *new_remote(int fd, server_ctx_t *server_ctx); #ifdef __ANDROID__ extern uint64_t tx; extern uint64_t rx; extern int vpn; extern void stat_update_cb(); #endif extern int verbose; extern int reuse_port; #ifdef MODULE_REMOTE extern uint64_t tx; extern uint64_t rx; extern int is_bind_local_addr; extern struct sockaddr_storage local_addr_v4; extern struct sockaddr_storage local_addr_v6; #endif static int packet_size = DEFAULT_PACKET_SIZE; /* UDP preserves datagram boundaries. MTU is a transmission hint, not a * receive limit: a smaller buffer silently truncates valid fragmented packets * and makes authentication fail. Covers the full IPv4/IPv6 UDP datagram. */ static const int buf_size = 65536; static int server_num = 0; static server_ctx_t *server_ctx_list[MAX_REMOTE_NUM] = { NULL }; const char *s_port = NULL; #ifndef __MINGW32__ static int setnonblocking(int fd) { int flags; if (-1 == (flags = fcntl(fd, F_GETFL, 0))) { flags = 0; } return fcntl(fd, F_SETFL, flags | O_NONBLOCK); } #endif #if defined(MODULE_REMOTE) && defined(SO_BROADCAST) static int set_broadcast(int socket_fd) { int opt = 1; return ss_setsockopt(socket_fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)); } #endif #ifdef SO_NOSIGPIPE static int set_nosigpipe(int socket_fd) { int opt = 1; return ss_setsockopt(socket_fd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); } #endif #ifdef MODULE_REDIR #ifndef IP_TRANSPARENT #define IP_TRANSPARENT 19 #endif #ifndef IPV6_TRANSPARENT #define IPV6_TRANSPARENT 75 #endif #ifndef IP_RECVORIGDSTADDR #ifdef IP_ORIGDSTADDR #define IP_RECVORIGDSTADDR IP_ORIGDSTADDR #else #define IP_RECVORIGDSTADDR 20 #endif #endif #ifndef IPV6_RECVORIGDSTADDR #ifdef IPV6_ORIGDSTADDR #define IPV6_RECVORIGDSTADDR IPV6_ORIGDSTADDR #else #define IPV6_RECVORIGDSTADDR 74 #endif #endif static int get_dstaddr(struct msghdr *msg, struct sockaddr_storage *dstaddr) { struct cmsghdr *cmsg; for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVORIGDSTADDR) { memcpy(dstaddr, CMSG_DATA(cmsg), sizeof(struct sockaddr_in)); dstaddr->ss_family = AF_INET; return 0; } else if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVORIGDSTADDR) { memcpy(dstaddr, CMSG_DATA(cmsg), sizeof(struct sockaddr_in6)); dstaddr->ss_family = AF_INET6; return 0; } } return 1; } #endif #define HASH_KEY_LEN sizeof(struct sockaddr_storage) + sizeof(int) static char * hash_key(const int af, const struct sockaddr_storage *addr) { size_t addr_len = sizeof(struct sockaddr_storage); static char key[HASH_KEY_LEN]; memset(key, 0, HASH_KEY_LEN); memcpy(key, &af, sizeof(int)); memcpy(key + sizeof(int), (const uint8_t *)addr, addr_len); return key; } #if defined(MODULE_REDIR) || defined(MODULE_REMOTE) static int construct_udprelay_header(const struct sockaddr_storage *in_addr, char *addr_header) { int addr_header_len = 0; if (in_addr->ss_family == AF_INET) { struct sockaddr_in *addr = (struct sockaddr_in *)in_addr; size_t addr_len = sizeof(struct in_addr); addr_header[addr_header_len++] = 1; memcpy(addr_header + addr_header_len, &addr->sin_addr, addr_len); addr_header_len += addr_len; memcpy(addr_header + addr_header_len, &addr->sin_port, 2); addr_header_len += 2; } else if (in_addr->ss_family == AF_INET6) { struct sockaddr_in6 *addr = (struct sockaddr_in6 *)in_addr; size_t addr_len = sizeof(struct in6_addr); addr_header[addr_header_len++] = 4; memcpy(addr_header + addr_header_len, &addr->sin6_addr, addr_len); addr_header_len += addr_len; memcpy(addr_header + addr_header_len, &addr->sin6_port, 2); addr_header_len += 2; } else { return 0; } return addr_header_len; } #endif static int parse_udprelay_header(const char *buf, const size_t buf_len, char *host, char *port, struct sockaddr_storage *storage) { if (buf_len < 1) { return 0; } const uint8_t atyp = *(uint8_t *)buf; int offset = 1; // get remote addr and port if ((atyp & ADDRTYPE_MASK) == 1) { // IP V4 size_t in_addr_len = sizeof(struct in_addr); if (buf_len >= in_addr_len + 3) { if (storage != NULL) { struct sockaddr_in *addr = (struct sockaddr_in *)storage; addr->sin_family = AF_INET; memcpy(&addr->sin_addr, buf + offset, sizeof(struct in_addr)); memcpy(&addr->sin_port, buf + offset + in_addr_len, sizeof(uint16_t)); } if (host != NULL) { ss_inet_ntop(AF_INET, (const void *)(buf + offset), host, INET_ADDRSTRLEN); } offset += in_addr_len; } } else if ((atyp & ADDRTYPE_MASK) == 3) { // Domain name if (buf_len < offset + 1) { return 0; } uint8_t name_len = *(uint8_t *)(buf + offset); if (name_len + 4 <= buf_len) { if (storage != NULL) { char tmp[MAX_HOSTNAME_LEN] = { 0 }; struct ss_ip ip; memcpy(tmp, buf + offset + 1, name_len); tmp[name_len] = '\0'; if (ss_ip_init(&ip, tmp) != -1) { if (ip.version == 4) { struct sockaddr_in *addr = (struct sockaddr_in *)storage; inet_pton(AF_INET, tmp, &(addr->sin_addr)); memcpy(&addr->sin_port, buf + offset + 1 + name_len, sizeof(uint16_t)); addr->sin_family = AF_INET; } else if (ip.version == 6) { struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; inet_pton(AF_INET6, tmp, &(addr->sin6_addr)); memcpy(&addr->sin6_port, buf + offset + 1 + name_len, sizeof(uint16_t)); addr->sin6_family = AF_INET6; } } else if (!validate_hostname(tmp, name_len)) { LOGE("[udp] invalid host name"); return 0; } } if (host != NULL) { memcpy(host, buf + offset + 1, name_len); host[name_len] = '\0'; } offset += 1 + name_len; } } else if ((atyp & ADDRTYPE_MASK) == 4) { // IP V6 size_t in6_addr_len = sizeof(struct in6_addr); if (buf_len >= in6_addr_len + 3) { if (storage != NULL) { struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; addr->sin6_family = AF_INET6; memcpy(&addr->sin6_addr, buf + offset, sizeof(struct in6_addr)); memcpy(&addr->sin6_port, buf + offset + in6_addr_len, sizeof(uint16_t)); } if (host != NULL) { ss_inet_ntop(AF_INET6, (const void *)(buf + offset), host, INET6_ADDRSTRLEN); } offset += in6_addr_len; } } if (offset == 1) { LOGE("[udp] invalid header with addr type %d", atyp); return 0; } if (port != NULL) { snprintf(port, MAX_PORT_STR_LEN, "%d", load16_be(buf + offset)); } offset += 2; return offset; } static char * get_addr_str(const struct sockaddr *sa, bool has_port) { static char s[SS_ADDRSTRLEN]; memset(s, 0, SS_ADDRSTRLEN); char addr[INET6_ADDRSTRLEN] = { 0 }; char port[PORTSTRLEN] = { 0 }; uint16_t p; struct sockaddr_in sa_in; struct sockaddr_in6 sa_in6; switch (sa->sa_family) { case AF_INET: memcpy(&sa_in, sa, sizeof(struct sockaddr_in)); ss_inet_ntop(AF_INET, &sa_in.sin_addr, addr, INET_ADDRSTRLEN); p = ntohs(sa_in.sin_port); snprintf(port, sizeof(port), "%d", p); break; case AF_INET6: memcpy(&sa_in6, sa, sizeof(struct sockaddr_in6)); ss_inet_ntop(AF_INET6, &sa_in6.sin6_addr, addr, INET6_ADDRSTRLEN); p = ntohs(sa_in6.sin6_port); snprintf(port, sizeof(port), "%d", p); break; default: strncpy(s, "Unknown AF", SS_ADDRSTRLEN); } int addr_len = strlen(addr); int port_len = strlen(port); // s is a zeroed static buffer large enough for addr + ':' + port memcpy(s, addr, addr_len); // NOLINT(bugprone-not-null-terminated-result) if (has_port) { memcpy(s + addr_len + 1, port, port_len); // NOLINT(bugprone-not-null-terminated-result) s[addr_len] = ':'; } return s; } int create_remote_socket(int ipv6) { int remote_sock; if (ipv6) { // Try to bind IPv6 first struct sockaddr_in6 addr; memset(&addr, 0, sizeof(struct sockaddr_in6)); addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_port = 0; remote_sock = ss_socket(AF_INET6, SOCK_DGRAM, 0); if (remote_sock == -1) { ERROR("[udp] cannot create socket"); return -1; } #ifdef MODULE_REMOTE if (is_bind_local_addr) { if (local_addr_v6.ss_family == AF_INET6) { if (bind_to_addr(&local_addr_v6, remote_sock) == -1) { ERROR("bind_to_addr"); FATAL("[udp] cannot bind socket"); return -1; } } } else { #endif if (bind(remote_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { FATAL("[udp] cannot bind socket"); return -1; } #ifdef MODULE_REMOTE } #endif } else { // Or else bind to IPv4 struct sockaddr_in addr; memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = 0; remote_sock = ss_socket(AF_INET, SOCK_DGRAM, 0); if (remote_sock == -1) { ERROR("[udp] cannot create socket"); return -1; } #ifdef MODULE_REMOTE if (is_bind_local_addr) { if (local_addr_v4.ss_family == AF_INET) { if (bind_to_addr(&local_addr_v4, remote_sock) == -1) { ERROR("bind_to_addr"); FATAL("[udp] cannot bind socket"); return -1; } } } else { #endif if (bind(remote_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { FATAL("[udp] cannot bind remote"); return -1; } #ifdef MODULE_REMOTE } #endif } #if defined(__linux__) // Disable fragmentation int val = IP_PMTUDISC_DO; ss_setsockopt(remote_sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); #endif return remote_sock; } int create_server_socket(const char *host, const char *port) { struct addrinfo hints; struct addrinfo *result, *rp, *ipv4v6bindall; int s, server_sock = -1; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ hints.ai_socktype = SOCK_DGRAM; /* We want a UDP socket */ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* For wildcard IP address */ hints.ai_protocol = IPPROTO_UDP; s = getaddrinfo(host, port, &hints, &result); if (s != 0) { LOGE("[udp] getaddrinfo: %s", gai_strerror(s)); return -1; } if (result == NULL) { LOGE("[udp] cannot bind"); return -1; } rp = result; /* * On Linux, with net.ipv6.bindv6only = 0 (the default), getaddrinfo(NULL) with * AI_PASSIVE returns 0.0.0.0 and :: (in this order). AI_PASSIVE was meant to * return a list of addresses to listen on, but it is impossible to listen on * 0.0.0.0 and :: at the same time, if :: implies dualstack mode. */ if (!host) { ipv4v6bindall = result; /* Loop over all address infos found until a IPV6 address is found. */ while (ipv4v6bindall) { if (ipv4v6bindall->ai_family == AF_INET6) { rp = ipv4v6bindall; /* Take first IPV6 address available */ break; } ipv4v6bindall = ipv4v6bindall->ai_next; /* Get next address info, if any */ } } for (/*rp = result*/; rp != NULL; rp = rp->ai_next) { server_sock = ss_socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (server_sock == -1) { continue; } if (rp->ai_family == AF_INET6) { int ipv6only = host ? 1 : 0; ss_setsockopt(server_sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)); } int opt = 1; ss_setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); #ifdef SO_NOSIGPIPE set_nosigpipe(server_sock); #endif if (reuse_port) { int err = set_reuseport(server_sock); if (err == 0) { LOGI("udp port reuse enabled"); } } #ifdef IP_TOS // Set QoS flag int tos = 46 << 2; int rc = ss_setsockopt(server_sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); if (rc < 0 && ss_socket_error() != ENOPROTOOPT) { LOGE("setting ipv4 dscp failed: %d", ss_socket_error()); } #ifdef IPV6_TCLASS rc = ss_setsockopt(server_sock, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)); if (rc < 0 && ss_socket_error() != ENOPROTOOPT) { LOGE("setting ipv6 dscp failed: %d", ss_socket_error()); } #endif #endif #ifdef MODULE_REDIR int sol = rp->ai_family == AF_INET ? SOL_IP : SOL_IPV6; int flag_t = rp->ai_family == AF_INET ? IP_TRANSPARENT : IPV6_TRANSPARENT; int flag_r = rp->ai_family == AF_INET ? IP_RECVORIGDSTADDR : IPV6_RECVORIGDSTADDR; if (ss_setsockopt(server_sock, sol, flag_t, &opt, sizeof(opt))) { ERROR("[udp] setsockopt IP_TRANSPARENT"); exit(EXIT_FAILURE); } if (ss_setsockopt(server_sock, sol, flag_r, &opt, sizeof(opt))) { FATAL("[udp] setsockopt IP_RECVORIGDSTADDR"); } #endif s = bind(server_sock, rp->ai_addr, rp->ai_addrlen); if (s == 0) { /* We managed to bind successfully! */ break; } else { ERROR("[udp] bind"); } ss_socket_close(server_sock); server_sock = -1; } freeaddrinfo(result); #if defined(__linux__) // Disable fragmentation int val = IP_PMTUDISC_DO; ss_setsockopt(server_sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); #endif return server_sock; } remote_ctx_t * new_remote(int fd, server_ctx_t *server_ctx) { remote_ctx_t *ctx = (remote_ctx_t *)ss_malloc(sizeof(remote_ctx_t)); memset(ctx, 0, sizeof(remote_ctx_t)); ctx->fd = fd; ctx->server_ctx = server_ctx; ctx->af = AF_UNSPEC; ss_io_init(&ctx->io, remote_recv_cb, fd, SS_READ); ss_timer_init(&ctx->watcher, remote_timeout_cb, server_ctx->timeout, server_ctx->timeout); return ctx; } server_ctx_t * new_server_ctx(int fd) { server_ctx_t *ctx = (server_ctx_t *)ss_malloc(sizeof(server_ctx_t)); memset(ctx, 0, sizeof(server_ctx_t)); ctx->fd = fd; ss_io_init(&ctx->io, server_recv_cb, fd, SS_READ); return ctx; } #ifdef MODULE_REMOTE struct query_ctx * new_query_ctx(char *buf, size_t len) { struct query_ctx *ctx = (struct query_ctx *)ss_malloc(sizeof(struct query_ctx)); memset(ctx, 0, sizeof(struct query_ctx)); ctx->buf = (buffer_t *)ss_malloc(sizeof(buffer_t)); balloc(ctx->buf, len); memcpy(ctx->buf->data, buf, len); ctx->buf->len = len; return ctx; } void close_and_free_query(SS_P_ struct query_ctx *ctx) { if (ctx != NULL) { if (ctx->buf != NULL) { bfree(ctx->buf); ss_free(ctx->buf); } ss_ptr_reset(&ctx->udp_session, ctx->server_ctx->crypto->udp_session_release); ss_free(ctx); } } #endif void close_and_free_remote(SS_P_ remote_ctx_t *ctx) { if (ctx != NULL) { ss_timer_stop(SS_A_ & ctx->watcher); ss_io_stop(SS_A_ & ctx->io); ss_socket_close(ctx->fd); if (ctx->server_ctx != NULL) ss_ptr_reset(&ctx->udp_session, ctx->server_ctx->crypto->udp_session_release); ss_free(ctx); } } static void remote_timeout_cb(SS_P_ ss_timer *watcher, int revents) { remote_ctx_t *remote_ctx = ss_container_of(watcher, remote_ctx_t, watcher); if (verbose) { LOGI("[udp] connection timeout"); } char *key = hash_key(remote_ctx->af, &remote_ctx->src_addr); cache_remove(remote_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN); } #ifdef MODULE_REMOTE static void resolv_free_cb(void *data) { struct query_ctx *ctx = (struct query_ctx *)data; if (ctx->buf != NULL) { bfree(ctx->buf); ss_free(ctx->buf); } ss_free(ctx); } static void resolv_cb(struct sockaddr *addr, void *data) { struct query_ctx *query_ctx = (struct query_ctx *)data; struct ss_loop *loop = query_ctx->server_ctx->loop; if (addr == NULL) { LOGE("[udp] unable to resolve"); } else { remote_ctx_t *remote_ctx = query_ctx->remote_ctx; int cache_hit = 0; // Lookup in the conn cache if (remote_ctx == NULL) { char *key = hash_key(AF_UNSPEC, &query_ctx->src_addr); cache_lookup(query_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)&remote_ctx); } if (remote_ctx == NULL) { int remotefd = create_remote_socket(addr->sa_family == AF_INET6); if (remotefd != -1) { setnonblocking(remotefd); #ifdef SO_BROADCAST set_broadcast(remotefd); #endif #ifdef SO_NOSIGPIPE set_nosigpipe(remotefd); #endif #ifdef IP_TOS // Set QoS flag int tos = 46 << 2; int rc = ss_setsockopt(remotefd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); if (rc < 0 && ss_socket_error() != ENOPROTOOPT) { LOGE("setting ipv4 dscp failed: %d", ss_socket_error()); } #ifdef IPV6_TCLASS rc = ss_setsockopt(remotefd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)); if (rc < 0 && ss_socket_error() != ENOPROTOOPT) { LOGE("setting ipv6 dscp failed: %d", ss_socket_error()); } #endif #endif #ifdef SET_INTERFACE if (query_ctx->server_ctx->iface) { if (setinterface(remotefd, query_ctx->server_ctx->iface) == -1) ERROR("setinterface"); } #endif remote_ctx = new_remote(remotefd, query_ctx->server_ctx); remote_ctx->src_addr = query_ctx->src_addr; remote_ctx->server_ctx = query_ctx->server_ctx; } else { ERROR("[udp] bind() error"); } } else { cache_hit = 1; } if (remote_ctx != NULL) { ss_ptr_move(&remote_ctx->udp_session, &query_ctx->udp_session, query_ctx->server_ctx->crypto->udp_session_release); if (addr->sa_family == AF_INET) memcpy(&remote_ctx->dst_addr, addr, sizeof(struct sockaddr_in)); else memcpy(&remote_ctx->dst_addr, addr, sizeof(struct sockaddr_in6)); size_t addr_len = get_sockaddr_len(addr); int s = sendto(remote_ctx->fd, query_ctx->buf->data, query_ctx->buf->len, 0, addr, addr_len); if (s == -1) { ERROR("[udp] sendto_remote"); if (!cache_hit) { close_and_free_remote(SS_A_ remote_ctx); } } else { if (!cache_hit) { // Add to conn cache char *key = hash_key(AF_UNSPEC, &remote_ctx->src_addr); cache_insert(query_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); ss_io_start(SS_A_ & remote_ctx->io); ss_timer_start(SS_A_ & remote_ctx->watcher); } } } } } #endif void convert_ipv4_mapped_ipv6(struct sockaddr_storage* addr) { if (addr->ss_family == AF_INET) { struct sockaddr_in* ipv4_addr = (struct sockaddr_in*)addr; struct sockaddr_storage mapped_addr; memset(&mapped_addr, 0, sizeof(mapped_addr)); struct sockaddr_in6* mapped_ipv6_addr = (struct sockaddr_in6*)&mapped_addr; mapped_ipv6_addr->sin6_family = AF_INET6; mapped_ipv6_addr->sin6_port = ipv4_addr->sin_port; uint8_t* ipv6_raw_addr = mapped_ipv6_addr->sin6_addr.s6_addr; ipv6_raw_addr[10] = 0xff; ipv6_raw_addr[11] = 0xff; uint32_t ipv4_raw_addr = ntohl(ipv4_addr->sin_addr.s_addr); ipv6_raw_addr[12] = (ipv4_raw_addr >> 24) & 0xff; ipv6_raw_addr[13] = (ipv4_raw_addr >> 16) & 0xff; ipv6_raw_addr[14] = (ipv4_raw_addr >> 8) & 0xff; ipv6_raw_addr[15] = ipv4_raw_addr & 0xff; memcpy(addr, &mapped_addr, sizeof(mapped_addr)); } } static void remote_recv_cb(SS_P_ ss_io *w, int revents) { ssize_t r; remote_ctx_t *remote_ctx = (remote_ctx_t *)w; server_ctx_t *server_ctx = remote_ctx->server_ctx; // server has been closed if (server_ctx == NULL) { LOGE("[udp] invalid server"); close_and_free_remote(SS_A_ remote_ctx); return; } if (verbose) { LOGI("[udp] remote receive a packet"); } struct sockaddr_storage src_addr; socklen_t src_addr_len = sizeof(struct sockaddr_storage); memset(&src_addr, 0, src_addr_len); buffer_t *buf = (buffer_t *)ss_malloc(sizeof(buffer_t)); balloc(buf, buf_size); // recv r = recvfrom(remote_ctx->fd, buf->data, buf_size, 0, (struct sockaddr *)&src_addr, &src_addr_len); if (r == -1) { // error on recv // simply drop that packet ERROR("[udp] remote_recv_recvfrom"); goto CLEAN_UP; } else if (r > packet_size) { if (verbose) { LOGI("[udp] remote_recv_recvfrom fragmentation, MTU at least be: " SSIZE_FMT, r + PACKET_HEADER_SIZE); } } buf->len = r; #ifdef MODULE_LOCAL int err; err = server_ctx->crypto->decrypt_udp != NULL ? server_ctx->crypto->decrypt_udp(buf, server_ctx->crypto->cipher, buf_size, &remote_ctx->udp_session) : server_ctx->crypto->decrypt_all(buf, server_ctx->crypto->cipher, buf_size); if (err) { LOGE("failed to handshake with %s: %s", get_addr_str((struct sockaddr *)&src_addr, false), "suspicious UDP packet"); // drop the packet silently goto CLEAN_UP; } #ifdef MODULE_REDIR struct sockaddr_storage dst_addr; memset(&dst_addr, 0, sizeof(struct sockaddr_storage)); int len; len = parse_udprelay_header(buf->data, buf->len, NULL, NULL, &dst_addr); if (dst_addr.ss_family != AF_INET && dst_addr.ss_family != AF_INET6) { LOGI("[udp] ss-redir does not support domain name"); goto CLEAN_UP; } #else int len; len = parse_udprelay_header(buf->data, buf->len, NULL, NULL, NULL); #endif if (len == 0) { // error when parsing header LOGE("[udp] error in parse header"); goto CLEAN_UP; } #if defined(MODULE_TUNNEL) || defined(MODULE_REDIR) // Construct packet buf->len -= len; memmove(buf->data, buf->data + len, buf->len); #else #ifdef __ANDROID__ rx += buf->len; stat_update_cb(); #endif // Construct packet brealloc(buf, buf->len + 3, buf_size); memmove(buf->data + 3, buf->data, buf->len); memset(buf->data, 0, 3); buf->len += 3; #endif #endif #ifdef MODULE_REMOTE rx += buf->len; // Reconstruct UDP response header char addr_header[MAX_ADDR_HEADER_SIZE]; int addr_header_len; addr_header_len = construct_udprelay_header(&src_addr, addr_header); // Construct packet brealloc(buf, buf->len + addr_header_len, buf_size); memmove(buf->data + addr_header_len, buf->data, buf->len); memcpy(buf->data, addr_header, addr_header_len); buf->len += addr_header_len; int err; err = server_ctx->crypto->encrypt_udp != NULL ? server_ctx->crypto->encrypt_udp(buf, server_ctx->crypto->cipher, buf_size, &remote_ctx->udp_session) : server_ctx->crypto->encrypt_all(buf, server_ctx->crypto->cipher, buf_size); if (err) { // drop the packet silently goto CLEAN_UP; } #endif if (buf->len > packet_size) { if (verbose) { LOGI("[udp] remote_recv_sendto fragmentation, MTU at least be: " SIZE_FMT, buf->len + PACKET_HEADER_SIZE); } } #ifdef MODULE_REDIR convert_ipv4_mapped_ipv6(&dst_addr); size_t remote_dst_addr_len; remote_dst_addr_len = get_sockaddr_len((struct sockaddr *)&dst_addr); int src_fd; src_fd = ss_socket(AF_INET6, SOCK_DGRAM, 0); if (src_fd < 0) { ERROR("[udp] remote_recv_socket"); goto CLEAN_UP; } int opt; opt = 1; if (ss_setsockopt(src_fd, SOL_IPV6, IPV6_TRANSPARENT, &opt, sizeof(opt))) { ERROR("[udp] remote_recv_setsockopt"); ss_socket_close(src_fd); goto CLEAN_UP; } if (ss_setsockopt(src_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { ERROR("[udp] remote_recv_setsockopt"); ss_socket_close(src_fd); goto CLEAN_UP; } if (reuse_port) { if (set_reuseport(src_fd) != 0) { ERROR("[udp] remote_recv port_reuse"); } } #ifdef IP_TOS // Set QoS flag int tos; tos = 46 << 2; int rc; rc = ss_setsockopt(src_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); if (rc < 0 && ss_socket_error() != ENOPROTOOPT) { LOGE("setting ipv4 dscp failed: %d", ss_socket_error()); } #ifdef IPV6_TCLASS rc = ss_setsockopt(src_fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)); if (rc < 0 && ss_socket_error() != ENOPROTOOPT) { LOGE("setting ipv6 dscp failed: %d", ss_socket_error()); } #endif #endif if (bind(src_fd, (struct sockaddr *)&dst_addr, remote_dst_addr_len) != 0) { ERROR("[udp] remote_recv_bind"); ss_socket_close(src_fd); goto CLEAN_UP; } struct sockaddr_storage mapped_src_addr; memcpy(&mapped_src_addr, &remote_ctx->src_addr, sizeof(remote_ctx->src_addr)); convert_ipv4_mapped_ipv6(&mapped_src_addr); size_t remote_src_addr_len; remote_src_addr_len = get_sockaddr_len((struct sockaddr *)&mapped_src_addr); int s; s = sendto(src_fd, buf->data, buf->len, 0, (struct sockaddr *)&mapped_src_addr, remote_src_addr_len); if (s == -1 && !(ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK)) { ERROR("[udp] remote_recv_sendto"); ss_socket_close(src_fd); goto CLEAN_UP; } ss_socket_close(src_fd); #else size_t remote_src_addr_len; remote_src_addr_len = get_sockaddr_len((struct sockaddr *)&remote_ctx->src_addr); int s; s = sendto(server_ctx->fd, buf->data, buf->len, 0, (struct sockaddr *)&remote_ctx->src_addr, remote_src_addr_len); if (s == -1 && !(ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK)) { ERROR("[udp] remote_recv_sendto"); goto CLEAN_UP; } #endif // handle the UDP packet successfully, // triger the timer ss_timer_again(SS_A_ & remote_ctx->watcher); CLEAN_UP: bfree(buf); ss_free(buf); } static void server_recv_cb(SS_P_ ss_io *w, int revents) { server_ctx_t *server_ctx = (server_ctx_t *)w; struct sockaddr_storage src_addr; memset(&src_addr, 0, sizeof(struct sockaddr_storage)); buffer_t *buf = (buffer_t *)ss_malloc(sizeof(buffer_t)); balloc(buf, buf_size); socklen_t src_addr_len = sizeof(struct sockaddr_storage); unsigned int offset = 0; #ifdef MODULE_REMOTE void *udp_session = NULL; #endif #ifdef MODULE_REDIR char control_buffer[64] = { 0 }; struct msghdr msg; memset(&msg, 0, sizeof(struct msghdr)); struct iovec iov[1]; struct sockaddr_storage dst_addr; memset(&dst_addr, 0, sizeof(struct sockaddr_storage)); msg.msg_name = &src_addr; msg.msg_namelen = src_addr_len; msg.msg_control = control_buffer; msg.msg_controllen = sizeof(control_buffer); iov[0].iov_base = buf->data; iov[0].iov_len = buf_size; msg.msg_iov = iov; msg.msg_iovlen = 1; buf->len = recvmsg(server_ctx->fd, &msg, 0); if (buf->len == -1) { ERROR("[udp] server_recvmsg"); goto CLEAN_UP; } else if (buf->len > packet_size) { if (verbose) { LOGI("[udp] UDP server_recv_recvmsg fragmentation, MTU at least be: " SIZE_FMT, buf->len + PACKET_HEADER_SIZE); } } if (get_dstaddr(&msg, &dst_addr)) { LOGE("[udp] unable to get dest addr"); goto CLEAN_UP; } #else ssize_t r; r = recvfrom(server_ctx->fd, buf->data, buf_size, 0, (struct sockaddr *)&src_addr, &src_addr_len); if (r == -1) { // error on recv // simply drop that packet ERROR("[udp] server_recv_recvfrom"); goto CLEAN_UP; } else if (r > packet_size) { if (verbose) { LOGI("[udp] server_recv_recvfrom fragmentation, MTU at least be: " SSIZE_FMT, r + PACKET_HEADER_SIZE); } } buf->len = r; #endif if (verbose) { LOGI("[udp] server receive a packet"); } #ifdef MODULE_REMOTE tx += buf->len; /* * AEAD-2022 routes by client session ID, which is only known after * decryption, so the crypto layer resolves the session itself and hands * it back here to be attached to the relay context. */ int err; err = server_ctx->crypto->decrypt_udp != NULL ? server_ctx->crypto->decrypt_udp(buf, server_ctx->crypto->cipher, buf_size, &udp_session) : server_ctx->crypto->decrypt_all(buf, server_ctx->crypto->cipher, buf_size); if (err) { LOGE("failed to handshake with %s: %s", get_addr_str((struct sockaddr *)&src_addr, false), "suspicious UDP packet"); // drop the packet silently goto CLEAN_UP; } #endif #ifdef MODULE_LOCAL #if !defined(MODULE_TUNNEL) && !defined(MODULE_REDIR) #ifdef __ANDROID__ tx += buf->len; #endif if (buf->len < 3) { LOGE("[udp] invalid SOCKS5 UDP header"); goto CLEAN_UP; } uint8_t frag; frag = *(uint8_t *)(buf->data + 2); offset += 3; #endif #endif /* * * SOCKS5 UDP Request * +----+------+------+----------+----------+----------+ * |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | * +----+------+------+----------+----------+----------+ * | 2 | 1 | 1 | Variable | 2 | Variable | * +----+------+------+----------+----------+----------+ * * SOCKS5 UDP Response * +----+------+------+----------+----------+----------+ * |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | * +----+------+------+----------+----------+----------+ * | 2 | 1 | 1 | Variable | 2 | Variable | * +----+------+------+----------+----------+----------+ * * shadowsocks UDP Request (before encrypted) * +------+----------+----------+----------+ * | ATYP | DST.ADDR | DST.PORT | DATA | * +------+----------+----------+----------+ * | 1 | Variable | 2 | Variable | * +------+----------+----------+----------+ * * shadowsocks UDP Response (before encrypted) * +------+----------+----------+----------+ * | ATYP | DST.ADDR | DST.PORT | DATA | * +------+----------+----------+----------+ * | 1 | Variable | 2 | Variable | * +------+----------+----------+----------+ * * shadowsocks UDP Request and Response (after encrypted) * +-------+--------------+ * | IV | PAYLOAD | * +-------+--------------+ * | Fixed | Variable | * +-------+--------------+ * */ #ifdef MODULE_REDIR char addr_header[MAX_ADDR_HEADER_SIZE]; memset(addr_header, 0, sizeof(addr_header)); int addr_header_len; addr_header_len = construct_udprelay_header(&dst_addr, addr_header); if (addr_header_len == 0) { LOGE("[udp] failed to parse tproxy addr"); goto CLEAN_UP; } // reconstruct the buffer brealloc(buf, buf->len + addr_header_len, buf_size); memmove(buf->data + addr_header_len, buf->data, buf->len); memcpy(buf->data, addr_header, addr_header_len); buf->len += addr_header_len; #elif MODULE_TUNNEL char addr_header[MAX_ADDR_HEADER_SIZE]; memset(addr_header, 0, sizeof(addr_header)); char *host; host = server_ctx->tunnel_addr.host; char *port; port = server_ctx->tunnel_addr.port; uint16_t port_num; port_num = 0; if (ss_parse_uint16_port(port, &port_num) == -1) { LOGE("[udp] invalid tunnel port"); goto CLEAN_UP; } uint16_t port_net_num; port_net_num = htons(port_num); int addr_header_len; addr_header_len = 0; struct ss_ip ip; if (ss_ip_init(&ip, host) != -1) { if (ip.version == 4) { // send as IPv4 struct in_addr host_addr; memset(&host_addr, 0, sizeof(struct in_addr)); int host_len = sizeof(struct in_addr); if (inet_pton(AF_INET, host, &host_addr) == -1) { FATAL("IP parser error"); } addr_header[addr_header_len++] = 1; memcpy(addr_header + addr_header_len, &host_addr, host_len); addr_header_len += host_len; } else if (ip.version == 6) { // send as IPv6 struct in6_addr host_addr; memset(&host_addr, 0, sizeof(struct in6_addr)); int host_len = sizeof(struct in6_addr); if (inet_pton(AF_INET6, host, &host_addr) == -1) { FATAL("IP parser error"); } addr_header[addr_header_len++] = 4; memcpy(addr_header + addr_header_len, &host_addr, host_len); addr_header_len += host_len; } else { FATAL("IP parser error"); } } else { // send as domain int host_len = strlen(host); if (host_len > UINT8_MAX) { LOGE("[udp] tunnel host name is too long"); goto CLEAN_UP; } addr_header[addr_header_len++] = 3; addr_header[addr_header_len++] = host_len; // addr_header is a binary protocol buffer, not a C string memcpy(addr_header + addr_header_len, host, host_len); // NOLINT(bugprone-not-null-terminated-result) addr_header_len += host_len; } memcpy(addr_header + addr_header_len, &port_net_num, 2); addr_header_len += 2; // reconstruct the buffer brealloc(buf, buf->len + addr_header_len, buf_size); memmove(buf->data + addr_header_len, buf->data, buf->len); memcpy(buf->data, addr_header, addr_header_len); buf->len += addr_header_len; #else char host[MAX_HOSTNAME_LEN]; char port[MAX_PORT_STR_LEN]; memset(host, 0, sizeof(host)); memset(port, 0, sizeof(port)); struct sockaddr_storage dst_addr; memset(&dst_addr, 0, sizeof(struct sockaddr_storage)); int addr_header_len; addr_header_len = parse_udprelay_header(buf->data + offset, buf->len - offset, host, port, &dst_addr); if (addr_header_len == 0) { // error in parse header goto CLEAN_UP; } #endif char *key; #ifdef MODULE_LOCAL key = hash_key(server_ctx->remote_addr->sa_family, &src_addr); #else key = hash_key(dst_addr.ss_family, &src_addr); #endif struct cache *conn_cache; conn_cache = server_ctx->conn_cache; remote_ctx_t *remote_ctx; remote_ctx = NULL; cache_lookup(conn_cache, key, HASH_KEY_LEN, (void *)&remote_ctx); if (remote_ctx != NULL) { if (sockaddr_cmp(&src_addr, &remote_ctx->src_addr, sizeof(src_addr))) { remote_ctx = NULL; } } // reset the timer if (remote_ctx != NULL) { ss_timer_again(SS_A_ & remote_ctx->watcher); } if (remote_ctx == NULL) { if (verbose) { #ifdef MODULE_REDIR char src[SS_ADDRSTRLEN]; char dst[SS_ADDRSTRLEN]; strcpy(src, get_addr_str((struct sockaddr *)&src_addr, true)); strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr, true)); LOGI("[%s] [udp] cache miss: %s <-> %s", s_port, dst, src); #else LOGI("[%s] [udp] cache miss: %s:%s <-> %s", s_port, host, port, get_addr_str((struct sockaddr *)&src_addr, true)); #endif } } else { if (verbose) { #ifdef MODULE_REDIR char src[SS_ADDRSTRLEN]; char dst[SS_ADDRSTRLEN]; strcpy(src, get_addr_str((struct sockaddr *)&src_addr, true)); strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr, true)); LOGI("[%s] [udp] cache hit: %s <-> %s", s_port, dst, src); #else LOGI("[%s] [udp] cache hit: %s:%s <-> %s", s_port, host, port, get_addr_str((struct sockaddr *)&src_addr, true)); #endif } } #ifdef MODULE_LOCAL #if !defined(MODULE_TUNNEL) && !defined(MODULE_REDIR) if (frag) { LOGE("[udp] drop a message since frag is not 0, but %d", frag); goto CLEAN_UP; } #endif const struct sockaddr *remote_addr; int remote_addr_len; remote_addr = server_ctx->remote_addr; remote_addr_len = server_ctx->remote_addr_len; if (remote_ctx == NULL) { // Bind to any port int remotefd = create_remote_socket(remote_addr->sa_family == AF_INET6); if (remotefd < 0) { ERROR("[udp] udprelay bind() error"); goto CLEAN_UP; } setnonblocking(remotefd); #ifdef SO_NOSIGPIPE set_nosigpipe(remotefd); #endif #ifdef IP_TOS // Set QoS flag int tos = 46 << 2; int rc = ss_setsockopt(remotefd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); if (rc < 0 && ss_socket_error() != ENOPROTOOPT) { LOGE("setting ipv4 dscp failed: %d", ss_socket_error()); } #ifdef IPV6_TCLASS rc = ss_setsockopt(remotefd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)); if (rc < 0 && ss_socket_error() != ENOPROTOOPT) { LOGE("setting ipv6 dscp failed: %d", ss_socket_error()); } #endif #endif #ifdef SET_INTERFACE if (server_ctx->iface) { if (setinterface(remotefd, server_ctx->iface) == -1) ERROR("setinterface"); } #endif #ifdef __ANDROID__ if (vpn) { if (protect_socket(remotefd) == -1) { ERROR("protect_socket"); ss_socket_close(remotefd); goto CLEAN_UP; } } #endif // Init remote_ctx remote_ctx = new_remote(remotefd, server_ctx); remote_ctx->src_addr = src_addr; remote_ctx->af = remote_addr->sa_family; // Add to conn cache cache_insert(conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); // Start remote io ss_io_start(SS_A_ & remote_ctx->io); ss_timer_start(SS_A_ & remote_ctx->watcher); } if (offset > 0) { buf->len -= offset; memmove(buf->data, buf->data + offset, buf->len); } int err; err = server_ctx->crypto->encrypt_udp != NULL ? server_ctx->crypto->encrypt_udp(buf, server_ctx->crypto->cipher, buf_size, &remote_ctx->udp_session) : server_ctx->crypto->encrypt_all(buf, server_ctx->crypto->cipher, buf_size); if (err) { // drop the packet silently goto CLEAN_UP; } if (buf->len > packet_size) { if (verbose) { LOGI("[udp] server_recv_sendto fragmentation, MTU at least be: " SIZE_FMT, buf->len + PACKET_HEADER_SIZE); } } int s; s = sendto(remote_ctx->fd, buf->data, buf->len, 0, remote_addr, remote_addr_len); if (s == -1) { ERROR("[udp] server_recv_sendto"); } #else int cache_hit; cache_hit = 0; int need_query; need_query = 0; char *addr_header; addr_header = buf->data + offset; if (buf->len - addr_header_len > packet_size) { if (verbose) { LOGI("[udp] server_recv_sendto fragmentation, MTU at least be: " SIZE_FMT, buf->len - addr_header_len + PACKET_HEADER_SIZE); } } if (remote_ctx != NULL) { cache_hit = 1; if (dst_addr.ss_family != AF_INET && dst_addr.ss_family != AF_INET6) { need_query = 1; } } else { if (dst_addr.ss_family == AF_INET || dst_addr.ss_family == AF_INET6) { int remotefd = create_remote_socket(dst_addr.ss_family == AF_INET6); if (remotefd != -1) { setnonblocking(remotefd); #ifdef SO_BROADCAST set_broadcast(remotefd); #endif #ifdef SO_NOSIGPIPE set_nosigpipe(remotefd); #endif #ifdef IP_TOS // Set QoS flag int tos = 46 << 2; int rc = ss_setsockopt(remotefd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); if (rc < 0 && ss_socket_error() != ENOPROTOOPT) { LOGE("setting ipv4 dscp failed: %d", ss_socket_error()); } #ifdef IPV6_TCLASS rc = ss_setsockopt(remotefd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)); if (rc < 0 && ss_socket_error() != ENOPROTOOPT) { LOGE("setting ipv6 dscp failed: %d", ss_socket_error()); } #endif #endif #ifdef SET_INTERFACE if (server_ctx->iface) { if (setinterface(remotefd, server_ctx->iface) == -1) ERROR("setinterface"); } #endif remote_ctx = new_remote(remotefd, server_ctx); remote_ctx->src_addr = src_addr; remote_ctx->server_ctx = server_ctx; memcpy(&remote_ctx->dst_addr, &dst_addr, sizeof(struct sockaddr_storage)); } else { ERROR("[udp] bind() error"); goto CLEAN_UP; } } } if (remote_ctx != NULL && !need_query) { ss_ptr_move(&remote_ctx->udp_session, &udp_session, server_ctx->crypto->udp_session_release); } if (remote_ctx != NULL && !need_query) { size_t addr_len = get_sockaddr_len((struct sockaddr *)&dst_addr); int s = sendto(remote_ctx->fd, buf->data + addr_header_len, buf->len - addr_header_len, 0, (struct sockaddr *)&dst_addr, addr_len); if (s == -1) { ERROR("[udp] sendto_remote"); if (!cache_hit) { close_and_free_remote(SS_A_ remote_ctx); } } else { if (!cache_hit) { // Add to conn cache remote_ctx->af = dst_addr.ss_family; char *key = hash_key(remote_ctx->af, &remote_ctx->src_addr); cache_insert(server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); ss_io_start(SS_A_ & remote_ctx->io); ss_timer_start(SS_A_ & remote_ctx->watcher); } } } else { struct query_ctx *query_ctx = new_query_ctx(buf->data + addr_header_len, buf->len - addr_header_len); query_ctx->server_ctx = server_ctx; ss_ptr_move(&query_ctx->udp_session, &udp_session, server_ctx->crypto->udp_session_release); query_ctx->addr_header_len = addr_header_len; query_ctx->src_addr = src_addr; memcpy(query_ctx->addr_header, addr_header, addr_header_len); if (need_query) { query_ctx->remote_ctx = remote_ctx; } int port_num = 0; if (ss_parse_int(port, 0, UINT16_MAX, &port_num) == -1) { LOGE("[udp] invalid target port"); resolv_free_cb(query_ctx); goto CLEAN_UP; } resolv_start(host, htons((uint16_t)port_num), resolv_cb, resolv_free_cb, query_ctx); } #endif CLEAN_UP: #ifdef MODULE_REMOTE ss_ptr_reset(&udp_session, server_ctx->crypto->udp_session_release); #endif bfree(buf); ss_free(buf); } void free_cb(void *key, void *element) { remote_ctx_t *remote_ctx = (remote_ctx_t *)element; if (verbose) { LOGI("[udp] one connection freed"); } close_and_free_remote(SS_DEFAULT, remote_ctx); } int init_udprelay(const char *server_host, const char *server_port, #ifdef MODULE_LOCAL const struct sockaddr *remote_addr, const int remote_addr_len, #ifdef MODULE_TUNNEL const ss_addr_t tunnel_addr, #endif #endif int mtu, crypto_t *crypto, int timeout, const char *iface) { s_port = server_port; // Initialize ev loop struct ss_loop *loop = SS_DEFAULT; // Initialize MTU if (mtu > 0) { if (mtu <= PACKET_HEADER_SIZE) { LOGE("MTU must be greater than %d", PACKET_HEADER_SIZE); return -1; } packet_size = mtu - PACKET_HEADER_SIZE; } // //////////////////////////////////////////////// // Setup server context // Bind to port int serverfd = create_server_socket(server_host, server_port); if (serverfd < 0) { return -1; } setnonblocking(serverfd); // Initialize cache struct cache *conn_cache; cache_create(&conn_cache, MAX_UDP_CONN_NUM, free_cb); server_ctx_t *server_ctx = new_server_ctx(serverfd); #ifdef MODULE_REMOTE server_ctx->loop = loop; #endif server_ctx->timeout = max(timeout, MIN_UDP_TIMEOUT); server_ctx->crypto = crypto; server_ctx->iface = iface; server_ctx->conn_cache = conn_cache; #ifdef MODULE_LOCAL server_ctx->remote_addr = remote_addr; server_ctx->remote_addr_len = remote_addr_len; #ifdef MODULE_TUNNEL server_ctx->tunnel_addr = tunnel_addr; #endif #endif ss_io_start(loop, &server_ctx->io); server_ctx_list[server_num++] = server_ctx; return serverfd; } void free_udprelay() { struct ss_loop *loop = SS_DEFAULT; while (server_num > 0) { server_ctx_t *server_ctx = server_ctx_list[--server_num]; ss_io_stop(loop, &server_ctx->io); ss_socket_close(server_ctx->fd); cache_delete(server_ctx->conn_cache, 0); #ifdef MODULE_LOCAL free((char*) server_ctx->remote_addr); #endif ss_free(server_ctx); server_ctx_list[server_num] = NULL; } }