Files
shadowsocks-libev/src/udprelay.cpp
T
Max Lv d6691b9e33 C++20 port 4/14: compile the event loop, UDP relay and programs as C++ (#3068)
* Make the event loop, UDP relay and programs compile as C++

Cast allocator results and void* callback data explicitly, cast string
literals stored in char * variables, replace compound literals in
ss_event.c with zeroing plus member assignment, and give ss-server's
listener array its MAX_REMOTE_NUM bound instead of a VLA.

Where a goto CLEAN_UP jumps over an initialised declaration (C++
rejects that), split the declaration from its initialisation in place.
The variables were already indeterminate at the label in C, and the
cleanup code does not read them. Checked for every MODULE_* variant of
udprelay.c, plus LIB_ONLY for local.c. Still valid C.

* Compile the event loop, UDP relay and programs as C++

Rename utils, ss_event, udprelay, winsock, local, server, tunnel, redir
and manager from .c to .cpp. Inside the moved files only the
\snippet{doc} file references and header comments change; snippet
references in doc/*.md, tests/test_cli_docs.py, the build files and
CONTRIBUTION.md follow the new names.

Also cast the Windows-only string literal returned by
get_default_conf(), found by the zig x86_64-windows-gnu build.

Every module of src/ except the unbuilt android.c and the vendored
blake3 is now compiled as C++20. The new .cpp files join the no-heap
ratchet baseline.

* Add the two casts ss-redir needs as C++

ss-redir only builds on Linux, so the macOS conversion checks never
compiled it: getsockname() wants a struct sockaddr *, and memcpy's
void * result needs a cast.
2026-10-15 09:53:31 +08:00

1620 lines
49 KiB
C++

/*
* udprelay.cpp - Setup UDP relay for both client and server
*
* Copyright (C) 2013 - 2019, Max Lv <max.c.lv@gmail.com>
*
* 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
* <http://www.gnu.org/licenses/>.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <locale.h>
#include <signal.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#ifndef __MINGW32__
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#endif
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__)
#include <net/if.h>
#include <sys/ioctl.h>
#define SET_INTERFACE
#endif
#include "core.h"
#include "utils.h"
#include "netutils.h"
#include "cache.h"
#include "udprelay.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_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->udp_session != NULL
&& ctx->server_ctx != NULL
&& ctx->server_ctx->crypto->udp_session_release != NULL) {
ctx->server_ctx->crypto->udp_session_release(ctx->udp_session);
}
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) {
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_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.
*/
void *udp_session;
udp_session = NULL;
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) {
// Attach the AEAD-2022 session so replies are encrypted with it
remote_ctx->udp_session = udp_session;
}
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;
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:
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;
}
}