From c8311a1c35a14df38547d5184b81c18ed57d2d3b Mon Sep 17 00:00:00 2001 From: Max Lv Date: Wed, 14 Oct 2026 15:25:36 +0800 Subject: [PATCH] Run ss-local and libshadowsocks-c on the C++ TCP relay engine The SOCKS5 handshake, the ACL bypass decision (with its own resolver for host-name rules) and the short wait for a first payload are now engine code; local.cpp keeps only option parsing and startup. The library entry points start the same engine. --- scripts/no_heap_baseline.txt | 2 +- src/local.cpp | 1321 +++------------------------------- src/local.h | 111 --- 3 files changed, 106 insertions(+), 1328 deletions(-) delete mode 100644 src/local.h diff --git a/scripts/no_heap_baseline.txt b/scripts/no_heap_baseline.txt index 7a6db978..66b0002d 100644 --- a/scripts/no_heap_baseline.txt +++ b/scripts/no_heap_baseline.txt @@ -9,7 +9,7 @@ src/crypto.cpp 4 src/ipset.cpp 3 src/jconf.cpp 16 src/json.cpp 2 -src/local.cpp 16 +src/local.cpp 2 src/manager.cpp 14 src/plugin.cpp 3 src/process.cpp 26 diff --git a/src/local.cpp b/src/local.cpp index a20fa6b9..2daf3db8 100644 --- a/src/local.cpp +++ b/src/local.cpp @@ -58,9 +58,18 @@ #include "socks5.h" #include "acl.h" #include "plugin.h" -#include "local.h" +#include "ss_event.h" +#include "crypto.h" +#include "jconf.h" +#include "common.h" +#include "udprelay.h" #include "ss_windows.h" +#include "relay/tcp.hpp" + +#undef min +#undef max + #ifndef LIB_ONLY #ifdef __APPLE__ #include @@ -128,11 +137,6 @@ static int nofile = 0; #endif #endif -static void server_recv_cb(SS_P_ ss_io *w, int revents); -static void server_send_cb(SS_P_ ss_io *w, int revents); -static void remote_recv_cb(SS_P_ ss_io *w, int revents); -static void remote_send_cb(SS_P_ ss_io *w, int revents); -static void accept_cb(SS_P_ ss_io *w, int revents); static void signal_cb(SS_P_ ss_signal *w, int revents); #if defined(__MINGW32__) && !defined(LIB_ONLY) static void plugin_watcher_cb(SS_P_ ss_io *w, int revents); @@ -142,17 +146,6 @@ static int create_and_bind(const char *addr, const char *port); #ifdef HAVE_LAUNCHD static int launch_or_create(const char *addr, const char *port); #endif -static remote_t *create_remote(listen_ctx_t *listener, struct sockaddr *addr, int direct); -static void free_remote(remote_t *remote); -static void close_and_free_remote(SS_P_ remote_t *remote); -static void free_server(server_t *server); -static void close_and_free_server(SS_P_ server_t *server); - -static remote_t *new_remote(int fd, int timeout); -static server_t *new_server(int fd); - -static struct ss_list connections; - #ifndef __MINGW32__ int setnonblocking(int fd) @@ -264,727 +257,6 @@ launch_or_create(const char *addr, const char *port) #endif -static void -free_connections(struct ss_loop *loop) -{ - struct ss_list_item *curr, *next; - ss_list_foreach_void(&connections, curr, next) { - server_t *server = ss_container_of(curr, server_t, entries); - remote_t *remote = server->remote; - close_and_free_server(loop, server); - close_and_free_remote(loop, remote); - } -} - -static void -delayed_connect_cb(SS_P_ ss_timer *watcher, int revents) -{ - server_t *server = ss_container_of(watcher, server_t, - delayed_connect_watcher); - - server_recv_cb(SS_A_ & server->recv_ctx->io, revents); -} - -static int -server_handshake_reply(SS_P_ ss_io *w, int udp_assc, struct socks5_response *response) -{ - server_ctx_t *server_recv_ctx = (server_ctx_t *)w; - server_t *server = server_recv_ctx->server; - remote_t *remote = server->remote; - if (server->stage != STAGE_HANDSHAKE) - return 0; - - struct sockaddr_in sock_addr; - if (udp_assc) { - socklen_t addr_len = sizeof(sock_addr); - if (getsockname(server->fd, (struct sockaddr *)&sock_addr, &addr_len) < 0) { - LOGE("getsockname: %s", strerror(ss_socket_error())); - response->rep = SOCKS5_REP_CONN_REFUSED; - send(server->fd, (char *)response, sizeof(struct socks5_response), 0); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return -1; - } - } else - memset(&sock_addr, 0, sizeof(sock_addr)); - - buffer_t resp_to_send; - buffer_t *resp_buf = &resp_to_send; - balloc(resp_buf, SOCKET_BUF_SIZE); - - memcpy(resp_buf->data, response, sizeof(struct socks5_response)); - memcpy(resp_buf->data + sizeof(struct socks5_response), - &sock_addr.sin_addr, sizeof(sock_addr.sin_addr)); - memcpy(resp_buf->data + sizeof(struct socks5_response) + - sizeof(sock_addr.sin_addr), - &sock_addr.sin_port, sizeof(sock_addr.sin_port)); - - int reply_size = sizeof(struct socks5_response) + - sizeof(sock_addr.sin_addr) + sizeof(sock_addr.sin_port); - - int s = send(server->fd, resp_buf->data, reply_size, 0); - - bfree(resp_buf); - - if (s < reply_size) { - LOGE("failed to send fake reply"); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return -1; - } - if (udp_assc) { - // Wait until client closes the connection - return -1; - } - return 0; -} - -struct local_dns_query { - server_t *server; - struct ss_loop *loop; -}; -static void local_dns_free(void *data) { ss_free(data); } -static void local_dns_done(struct sockaddr *address, void *data) -{ - struct local_dns_query *query = (struct local_dns_query *)data; - server_t *server = query->server; - if (!server) return; - server->dns_query = NULL; - server->dns_done = 1; - server->dns_success = address != NULL; - if (address) - memcpy(&server->dns_address, address, get_sockaddr_len(address)); - ss_io_start(query->loop, &server->recv_ctx->io); - /* Resume the buffered handshake without reading additional socket data. */ - server_recv_cb(query->loop, &server->recv_ctx->io, SS_TIMER); -} -static int local_dns_address(server_t *server, struct sockaddr_storage *address) -{ - if (!server->dns_success) return -1; - *address = server->dns_address; - return 0; -} - -static int -server_handshake(SS_P_ ss_io *w, buffer_t *buf) -{ - server_ctx_t *server_recv_ctx = (server_ctx_t *)w; - server_t *server = server_recv_ctx->server; - remote_t *remote = server->remote; - - struct socks5_request *request = (struct socks5_request *)buf->data; - size_t request_len = sizeof(struct socks5_request); - - if (buf->len < request_len) { - return -1; - } - - struct socks5_response response; - response.ver = SVERSION; - response.rep = SOCKS5_REP_SUCCEEDED; - response.rsv = 0; - response.atyp = SOCKS5_ATYP_IPV4; - - if (request->cmd == SOCKS5_CMD_UDP_ASSOCIATE) { - if (verbose) { - LOGI("udp assc request accepted"); - } - return server_handshake_reply(SS_A_ w, 1, &response); - } else if (request->cmd != SOCKS5_CMD_CONNECT) { - LOGE("unsupported command: %d", request->cmd); - response.rep = SOCKS5_REP_CMD_NOT_SUPPORTED; - char *send_buf = (char *)&response; - send(server->fd, send_buf, 4, 0); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return -1; - } - - char host[MAX_HOSTNAME_LEN + 1], ip[INET6_ADDRSTRLEN], port[16]; - - buffer_t *abuf = server->abuf; - abuf->idx = 0; - abuf->len = 0; - - abuf->data[abuf->len++] = request->atyp; - int atyp = request->atyp; - - // get remote addr and port - if (atyp == SOCKS5_ATYP_IPV4) { - size_t in_addr_len = sizeof(struct in_addr); - if (buf->len < request_len + in_addr_len + 2) { - return -1; - } - memcpy(abuf->data + abuf->len, buf->data + request_len, in_addr_len + 2); - abuf->len += in_addr_len + 2; - - if (acl || verbose) { - uint16_t p = load16_be(buf->data + request_len + in_addr_len); - if (!ss_inet_ntop(AF_INET, (const void *)(buf->data + request_len), - ip, INET_ADDRSTRLEN)) { - LOGI("ss_inet_ntop(AF_INET): %s", strerror(ss_socket_error())); - ip[0] = '\0'; - } - snprintf(port, sizeof(port), "%d", p); - } - } else if (atyp == SOCKS5_ATYP_DOMAIN) { - if (buf->len < request_len + 1) { - return -1; - } - uint8_t name_len = *(uint8_t *)(buf->data + request_len); - if (buf->len < request_len + 1 + name_len + 2) { - return -1; - } - abuf->data[abuf->len++] = name_len; - memcpy(abuf->data + abuf->len, buf->data + request_len + 1, name_len + 2); - abuf->len += name_len + 2; - - if (acl || verbose) { - uint16_t p = load16_be(buf->data + request_len + 1 + name_len); - memcpy(host, buf->data + request_len + 1, name_len); - host[name_len] = '\0'; - snprintf(port, sizeof(port), "%d", p); - } - } else if (atyp == SOCKS5_ATYP_IPV6) { - size_t in6_addr_len = sizeof(struct in6_addr); - if (buf->len < request_len + in6_addr_len + 2) { - return -1; - } - memcpy(abuf->data + abuf->len, buf->data + request_len, in6_addr_len + 2); - abuf->len += in6_addr_len + 2; - - if (acl || verbose) { - uint16_t p = load16_be(buf->data + request_len + in6_addr_len); - if (!ss_inet_ntop(AF_INET6, (const void *)(buf->data + request_len), - ip, INET6_ADDRSTRLEN)) { - LOGI("ss_inet_ntop(AF_INET6): %s", strerror(ss_socket_error())); - ip[0] = '\0'; - } - snprintf(port, sizeof(port), "%d", p); - } - } else { - LOGE("unsupported addrtype: %d", request->atyp); - response.rep = SOCKS5_REP_ADDRTYPE_NOT_SUPPORTED; - char *send_buf = (char *)&response; - send(server->fd, send_buf, 4, 0); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return -1; - } - - if (acl && atyp == SOCKS5_ATYP_DOMAIN && !server->dns_done -#ifdef __ANDROID__ - && !vpn -#endif - && acl_match_host(host) >= 0) { - uint16_t numeric_port; - if (parse_numeric_port(port, &numeric_port) != 0) { - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return -1; - } - struct local_dns_query *query = (struct local_dns_query *)ss_malloc(sizeof(*query)); - query->server = server; - query->loop = loop; - server->dns_query = query; - ss_io_stop(loop, &server->recv_ctx->io); - resolv_start(host, htons(numeric_port), local_dns_done, local_dns_free, query); - return -1; - } - - if (server_handshake_reply(SS_A_ w, 0, &response) < 0) - return -1; - server->stage = STAGE_STREAM; - - buf->len -= (3 + abuf->len); - if (buf->len > 0) { - memmove(buf->data, buf->data + 3 + abuf->len, buf->len); - } - - if (verbose) { - if (atyp == SOCKS5_ATYP_DOMAIN) - LOGI("connect to %s:%s", host, port); - else if (atyp == SOCKS5_ATYP_IPV4) - LOGI("connect to %s:%s", ip, port); - else if (atyp == SOCKS5_ATYP_IPV6) - LOGI("connect to [%s]:%s", ip, port); - } - - if (acl -#ifdef __ANDROID__ - && !(vpn && strcmp(port, "53") == 0) -#endif - ) { - int bypass = 0; - int resolved = 0; - struct sockaddr_storage storage; - memset(&storage, 0, sizeof(struct sockaddr_storage)); - int err; - - int host_match = 0; - if (atyp == SOCKS5_ATYP_DOMAIN) - host_match = acl_match_host(host); - - if (host_match > 0) - bypass = 1; // bypass hostnames in black list - else if (host_match < 0) - bypass = 0; // proxy hostnames in white list - else { - if (atyp == SOCKS5_ATYP_DOMAIN -#ifdef __ANDROID__ - && !vpn -#endif - ) { // resolve domain so we can bypass domain with geoip - if (local_dns_address(server, &storage)) - goto not_bypass; - resolved = 1; - switch (((struct sockaddr *)&storage)->sa_family) { - case AF_INET: - { - struct sockaddr_in *addr_in = (struct sockaddr_in *)&storage; - if (!ss_inet_ntop(AF_INET, &(addr_in->sin_addr), ip, INET_ADDRSTRLEN)) - goto not_bypass; - break; - } - case AF_INET6: - { - struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)&storage; - if (!ss_inet_ntop(AF_INET6, &(addr_in6->sin6_addr), ip, INET6_ADDRSTRLEN)) - goto not_bypass; - break; - } - default: - goto not_bypass; - } - } - - int ip_match = (resolved || atyp == SOCKS5_ATYP_IPV4 - || atyp == SOCKS5_ATYP_IPV6) ? acl_match_host(ip) : 0; - - switch (get_acl_mode()) { - case BLACK_LIST: - if (ip_match > 0) - bypass = 1; // bypass IPs in black list - break; - case WHITE_LIST: - bypass = 1; - if (ip_match < 0) - bypass = 0; // proxy IPs in white list - break; - } - } - - if (bypass) { - if (verbose) { - if (atyp == SOCKS5_ATYP_DOMAIN) - LOGI("bypass %s:%s", host, port); - else if (atyp == 1) - LOGI("bypass %s:%s", ip, port); - else if (atyp == 4) - LOGI("bypass [%s]:%s", ip, port); - } - if (atyp == SOCKS5_ATYP_DOMAIN && !resolved) -#ifdef __ANDROID__ - if (vpn) - goto not_bypass; - else -#endif - err = local_dns_address(server, &storage); - else - err = get_sockaddr(ip, port, &storage, 0, ipv6first); - if (err != -1) { - remote = create_remote(server->listener, (struct sockaddr *)&storage, 1); - } - } - } - -not_bypass: - // Not bypass - if (remote == NULL) { - remote = create_remote(server->listener, NULL, 0); - } - - if (remote == NULL) { - LOGE("invalid remote addr"); - close_and_free_server(SS_A_ server); - return -1; - } - - if (!remote->direct) { - int err = crypto->encrypt(abuf, server->e_ctx, SOCKET_BUF_SIZE); - if (err) { - LOGE("invalid password or cipher"); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return -1; - } - } - - if (buf->len > 0) { - memcpy(remote->buf->data, buf->data, buf->len); - remote->buf->len = buf->len; - } - - server->remote = remote; - remote->server = server; - - if (buf->len > 0) { - return 0; - } else { - ss_timer_start(SS_A_ & server->delayed_connect_watcher); - } - - return -1; -} - -static void -server_stream(SS_P_ ss_io *w, buffer_t *buf) -{ - server_ctx_t *server_recv_ctx = (server_ctx_t *)w; - server_t *server = server_recv_ctx->server; - remote_t *remote = server->remote; - - if (remote == NULL) { - LOGE("invalid remote"); - close_and_free_server(SS_A_ server); - return; - } - - // insert shadowsocks header - if (!remote->direct) { -#ifdef __ANDROID__ - tx += remote->buf->len; -#endif - int err = crypto->encrypt(remote->buf, server->e_ctx, SOCKET_BUF_SIZE); - - if (err) { - LOGE("invalid password or cipher"); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } - - if (server->abuf) { - bprepend(remote->buf, server->abuf, SOCKET_BUF_SIZE); - bfree(server->abuf); - ss_free(server->abuf); - server->abuf = NULL; - } - } - - if (!remote->send_ctx->connected) { -#ifdef __ANDROID__ - if (vpn) { - int not_protect = 0; - if (remote->addr.ss_family == AF_INET) { - struct sockaddr_in *s = (struct sockaddr_in *)&remote->addr; - if (s->sin_addr.s_addr == inet_addr("127.0.0.1")) - not_protect = 1; - } - if (!not_protect) { - if (protect_socket(remote->fd) == -1) { - ERROR("protect_socket"); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } - } - } -#endif - - remote->buf->idx = 0; - - if (!fast_open || remote->direct) { - // connecting, wait until connected - int r = connect(remote->fd, (struct sockaddr *)&(remote->addr), remote->addr_len); - - if (r == -1 && ss_socket_error() != CONNECT_IN_PROGRESS) { - ERROR("connect"); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } - - // wait on remote connected event - ss_io_stop(SS_A_ & server_recv_ctx->io); - ss_io_start(SS_A_ & remote->send_ctx->io); - ss_timer_start(SS_A_ & remote->send_ctx->watcher); - } else { -#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) - int s = -1; - s = sendto(remote->fd, remote->buf->data, remote->buf->len, MSG_FASTOPEN, - (struct sockaddr *)&(remote->addr), remote->addr_len); -#elif defined(TCP_FASTOPEN_WINSOCK) - DWORD s = -1; - DWORD err = 0; - do { - int optval = 1; - // Set fast open option - if (ss_setsockopt(remote->fd, IPPROTO_TCP, TCP_FASTOPEN, - &optval, sizeof(optval)) != 0) { - ERROR("setsockopt"); - break; - } - // Load ConnectEx function - LPFN_CONNECTEX ConnectEx = winsock_getconnectex(); - if (ConnectEx == NULL) { - LOGE("Cannot load ConnectEx() function"); - err = WSAENOPROTOOPT; - break; - } - // ConnectEx requires a bound socket - if (winsock_dummybind(remote->fd, - (struct sockaddr *)&(remote->addr)) != 0) { - ERROR("bind"); - break; - } - // Call ConnectEx to send data - memset(&remote->olap, 0, sizeof(remote->olap)); - remote->connect_ex_done = 0; - if (ConnectEx(remote->fd, (const struct sockaddr *)&(remote->addr), - remote->addr_len, remote->buf->data, remote->buf->len, - &s, &remote->olap)) { - remote->connect_ex_done = 1; - break; - } - // XXX: ConnectEx pending, check later in remote_send - if (WSAGetLastError() == ERROR_IO_PENDING) { - err = CONNECT_IN_PROGRESS; - break; - } - ERROR("ConnectEx"); - } while (0); - // Set error number - if (err) { - SetLastError(err); - } -#else - int s = -1; -#if defined(CONNECT_DATA_IDEMPOTENT) - ((struct sockaddr_in *)&(remote->addr))->sin_len = sizeof(struct sockaddr_in); - sa_endpoints_t endpoints; - memset((char *)&endpoints, 0, sizeof(endpoints)); - endpoints.sae_dstaddr = (struct sockaddr *)&(remote->addr); - endpoints.sae_dstaddrlen = remote->addr_len; - - s = connectx(remote->fd, &endpoints, SAE_ASSOCID_ANY, - CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT, - NULL, 0, NULL, NULL); -#elif defined(TCP_FASTOPEN_CONNECT) - int optval = 1; - if (ss_setsockopt(remote->fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, - (void *)&optval, sizeof(optval)) < 0) - FATAL("failed to set TCP_FASTOPEN_CONNECT"); - s = connect(remote->fd, (struct sockaddr *)&(remote->addr), remote->addr_len); -#else - FATAL("fast open is not enabled in this build"); -#endif - if (s == 0) - s = send(remote->fd, remote->buf->data, remote->buf->len, 0); -#endif - if (s == -1) { - if (ss_socket_error() == CONNECT_IN_PROGRESS) { - // in progress, wait until connected - remote->buf->idx = 0; - ss_io_stop(SS_A_ & server_recv_ctx->io); - ss_io_start(SS_A_ & remote->send_ctx->io); - return; - } else { - if (ss_socket_error() == EOPNOTSUPP || ss_socket_error() == EPROTONOSUPPORT || - ss_socket_error() == ENOPROTOOPT) { - LOGE("fast open is not supported on this platform"); - // just turn it off - fast_open = 0; - } else { - ERROR("fast_open_connect"); - } - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } - } else { - remote->buf->len -= s; - remote->buf->idx = s; - - ss_io_stop(SS_A_ & server_recv_ctx->io); - ss_io_start(SS_A_ & remote->send_ctx->io); - ss_timer_start(SS_A_ & remote->send_ctx->watcher); - return; - } - } - } else { - int s = send(remote->fd, remote->buf->data, remote->buf->len, 0); - if (s == -1) { - if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { - // no data, wait for send - remote->buf->idx = 0; - ss_io_stop(SS_A_ & server_recv_ctx->io); - ss_io_start(SS_A_ & remote->send_ctx->io); - return; - } else { - ERROR("server_recv_cb_send"); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } - } else if (s < (int)(remote->buf->len)) { - remote->buf->len -= s; - remote->buf->idx = s; - ss_io_stop(SS_A_ & server_recv_ctx->io); - ss_io_start(SS_A_ & remote->send_ctx->io); - return; - } else { - remote->buf->idx = 0; - remote->buf->len = 0; - } - } -} - -static void -server_recv_cb(SS_P_ ss_io *w, int revents) -{ - server_ctx_t *server_recv_ctx = (server_ctx_t *)w; - server_t *server = server_recv_ctx->server; - remote_t *remote = server->remote; - buffer_t *buf; - ssize_t r; - - ss_timer_stop(SS_A_ & server->delayed_connect_watcher); - - if (remote == NULL) { - buf = server->buf; - } else { - buf = remote->buf; - } - - if (revents != SS_TIMER) { - r = recv(server->fd, buf->data + buf->len, SOCKET_BUF_SIZE - buf->len, 0); - - if (r == 0) { - // connection closed - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } else if (r == -1) { - if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { - // no data - // continue to wait for recv - return; - } else { - if (verbose) - ERROR("server_recv_cb_recv"); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } - } - buf->len += r; - } - - while (1) { - // local socks5 server - if (server->stage == STAGE_STREAM) { - server_stream(SS_A_ w, buf); - - // all processed - return; - } else if (server->stage == STAGE_INIT) { - if (verbose) { - struct sockaddr_in peer_addr; - socklen_t peer_addr_len = sizeof peer_addr; - if (getpeername(server->fd, (struct sockaddr *)&peer_addr, &peer_addr_len) == 0) { - // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage): filled by getpeername() on success - LOGI("connection from %s:%hu", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port)); - } - } - if (buf->len < 1) - return; - if (buf->data[0] != SVERSION) { - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } - if (buf->len < sizeof(struct method_select_request)) { - return; - } - struct method_select_request *method = (struct method_select_request *)buf->data; - int method_len = method->nmethods + sizeof(struct method_select_request); - if (buf->len < method_len) { - return; - } - - struct method_select_response response; - response.ver = SVERSION; - response.method = METHOD_UNACCEPTABLE; - for (int i = 0; i < method->nmethods; i++) - if (method->methods[i] == METHOD_NOAUTH) { - response.method = METHOD_NOAUTH; - break; - } - char *send_buf = (char *)&response; - send(server->fd, send_buf, sizeof(response), 0); - if (response.method == METHOD_UNACCEPTABLE) { - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } - - server->stage = STAGE_HANDSHAKE; - - if (method_len < (int)(buf->len)) { - memmove(buf->data, buf->data + method_len, buf->len - method_len); - buf->len -= method_len; - continue; - } - - buf->len = 0; - return; - } else if (server->stage == STAGE_HANDSHAKE) { - int ret = server_handshake(SS_A_ w, buf); - if (ret) - return; - } - } -} - -static void -server_send_cb(SS_P_ ss_io *w, int revents) -{ - server_ctx_t *server_send_ctx = (server_ctx_t *)w; - server_t *server = server_send_ctx->server; - remote_t *remote = server->remote; - if (server->buf->len == 0) { - // close and free - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } else { - // has data to send - ssize_t s = send(server->fd, server->buf->data + server->buf->idx, - server->buf->len, 0); - if (s == -1) { - if (ss_socket_error() != EAGAIN && ss_socket_error() != EWOULDBLOCK) { - ERROR("server_send_cb_send"); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - } - return; - } else if (s < (ssize_t)(server->buf->len)) { - // partly sent, move memory, wait for the next time to send - server->buf->len -= s; - server->buf->idx += s; - return; - } else { - // all sent out, wait for reading - server->buf->len = 0; - server->buf->idx = 0; - ss_io_stop(SS_A_ & server_send_ctx->io); - ss_io_start(SS_A_ & remote->recv_ctx->io); - return; - } - } -} - #ifdef __ANDROID__ void stat_update_cb() @@ -998,412 +270,87 @@ stat_update_cb() #endif -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); +// The TCP side of ss-local: the relay engine on the ss_event loop, plus a +// resolver for host-name ACL rules. +namespace { - remote_t *remote = remote_ctx->remote; - server_t *server = remote->server; +struct TcpSide { + ss::event::Loop loop; + ss::relay::Method method; + ss::relay::Resolver resolver; + ss::relay::TcpRelay relay; + bool resolving = false; + bool running = false; - if (verbose) { - LOGI("TCP connection timeout"); - } - - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); -} - -static void -remote_recv_cb(SS_P_ ss_io *w, int revents) -{ - remote_ctx_t *remote_recv_ctx = (remote_ctx_t *)w; - remote_t *remote = remote_recv_ctx->remote; - server_t *server = remote->server; - - ssize_t r = recv(remote->fd, server->buf->data, SOCKET_BUF_SIZE, 0); - - if (r == 0) { - // connection closed - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } else if (r == -1) { - if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { - // no data - // continue to wait for recv - return; - } else { - ERROR("remote_recv_cb_recv"); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; + bool start(struct ss_loop *ss_loop, const char *cipher, const char *pass, const char *key, + ss::relay::TcpConfig &config, int listenfd) + { + if (!loop.adopt(ss_native_loop(ss_loop))) { + return false; } + if (!method.init(cipher, pass, key, ss::crypto::Role::client)) { + LOGE("failed to initialize ciphers"); + return false; + } + if (config.acl) { + config.acl_hooks.match_host = &acl_match_host; + config.acl_hooks.mode = &get_acl_mode; + config.acl_hooks.outbound_blocked = &outbound_block_match_host; + if (!resolver.init(loop, NULL, ipv6first != 0, config.max_connections)) { + return false; + } + resolving = true; + } + if (!relay.init(loop, method, config, resolving ? &resolver : NULL) || !relay.listen(listenfd)) { + return false; + } + running = true; + return true; } - server->buf->len = r; + void stop() + { + if (running) { + relay.shutdown(); + } + static bool resolver_closed; + resolver_closed = !resolving; + if (resolving) { + resolver.shutdown(ss::CloseCompletion([](void *, uint32_t) { resolver_closed = true; }, NULL, 0)); + } + while ((running && !relay.closed()) || !resolver_closed) { + loop.run_nowait(); + } + running = resolving = false; + } +}; - if (!remote->direct) { +void +fill_tcp_config(ss::relay::TcpConfig &config, const struct sockaddr_storage *remotes, int remote_count, + int timeout, int mptcp, char *iface) +{ + config.kind = ss::relay::Kind::local; + config.remotes = { remotes, (size_t)remote_count }; + config.timeout_secs = timeout; + config.max_connections = DEFAULT_MAX_CONNECTIONS; + config.fast_open = fast_open != 0; + config.no_delay = no_delay != 0; + config.printable_salt = printable_salt != 0; + config.acl = acl != 0; + config.upstream.sndbuf = tcp_outgoing_sndbuf; + config.upstream.rcvbuf = tcp_outgoing_rcvbuf; + config.upstream.mptcp = mptcp; + config.upstream.iface = iface; #ifdef __ANDROID__ - rx += server->buf->len; - stat_update_cb(); + config.vpn = vpn != 0; + config.upstream.protect = protect_socket; + config.tx = &tx; + config.rx = ℞ + config.on_traffic = &stat_update_cb; #endif - int err = crypto->decrypt(server->buf, server->d_ctx, SOCKET_BUF_SIZE); - if (err == CRYPTO_ERROR) { - LOGE("invalid password or cipher"); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } else if (err == CRYPTO_NEED_MORE) { - return; // Wait for more - } - } - - int s = send(server->fd, server->buf->data, server->buf->len, 0); - - if (s == -1) { - if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { - // no data, wait for send - server->buf->idx = 0; - ss_io_stop(SS_A_ & remote_recv_ctx->io); - ss_io_start(SS_A_ & server->send_ctx->io); - } else { - ERROR("remote_recv_cb_send"); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } - } else if (s < (int)(server->buf->len)) { - server->buf->len -= s; - server->buf->idx = s; - ss_io_stop(SS_A_ & remote_recv_ctx->io); - ss_io_start(SS_A_ & server->send_ctx->io); - } - - // Disable TCP_NODELAY after the first response are sent - if (!remote->recv_ctx->connected && !no_delay) { - int opt = 0; - ss_setsockopt(server->fd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); - ss_setsockopt(remote->fd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); - } - remote->recv_ctx->connected = 1; } -static void -remote_send_cb(SS_P_ ss_io *w, int revents) -{ - remote_ctx_t *remote_send_ctx = (remote_ctx_t *)w; - remote_t *remote = remote_send_ctx->remote; - server_t *server = remote->server; - - if (!remote_send_ctx->connected) { -#ifdef TCP_FASTOPEN_WINSOCK - if (fast_open) { - // Check if ConnectEx is done - if (!remote->connect_ex_done) { - DWORD numBytes; - DWORD flags; - // Non-blocking way to fetch ConnectEx result - if (WSAGetOverlappedResult(remote->fd, &remote->olap, - &numBytes, FALSE, &flags)) { - remote->buf->len -= numBytes; - remote->buf->idx = numBytes; - remote->connect_ex_done = 1; - } else if (WSAGetLastError() == WSA_IO_INCOMPLETE) { - // XXX: ConnectEx still not connected, wait for next time - return; - } else { - ERROR("WSAGetOverlappedResult"); - // not connected - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } - } - - // Make getpeername work - if (ss_setsockopt(remote->fd, SOL_SOCKET, - SO_UPDATE_CONNECT_CONTEXT, NULL, 0) != 0) { - ERROR("setsockopt"); - } - } -#endif - struct sockaddr_storage addr; - socklen_t len = sizeof addr; - int r = getpeername(remote->fd, (struct sockaddr *)&addr, &len); - if (r == 0) { - remote_send_ctx->connected = 1; - ss_timer_stop(SS_A_ & remote_send_ctx->watcher); - ss_io_start(SS_A_ & remote->recv_ctx->io); - - // no need to send any data - if (remote->buf->len == 0) { - ss_io_stop(SS_A_ & remote_send_ctx->io); - ss_io_start(SS_A_ & server->recv_ctx->io); - return; - } - } else { - // not connected - ERROR("getpeername"); - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } - } - - if (remote->buf->len == 0) { - // close and free - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - return; - } else { - // has data to send - ssize_t s = send(remote->fd, remote->buf->data + remote->buf->idx, - remote->buf->len, 0); - if (s == -1) { - if (ss_socket_error() != EAGAIN && ss_socket_error() != EWOULDBLOCK) { - ERROR("remote_send_cb_send"); - // close and free - close_and_free_remote(SS_A_ remote); - close_and_free_server(SS_A_ server); - } - return; - } else if (s < (ssize_t)(remote->buf->len)) { - // partly sent, move memory, wait for the next time to send - remote->buf->len -= s; - remote->buf->idx += s; - return; - } else { - // all sent out, wait for reading - remote->buf->len = 0; - remote->buf->idx = 0; - ss_io_stop(SS_A_ & remote_send_ctx->io); - ss_io_start(SS_A_ & server->recv_ctx->io); - } - } -} - -static remote_t * -new_remote(int fd, int timeout) -{ - remote_t *remote; - remote = (remote_t *)ss_malloc(sizeof(remote_t)); - - memset(remote, 0, sizeof(remote_t)); - - remote->buf = (buffer_t *)ss_malloc(sizeof(buffer_t)); - remote->recv_ctx = (struct remote_ctx *)ss_malloc(sizeof(remote_ctx_t)); - remote->send_ctx = (struct remote_ctx *)ss_malloc(sizeof(remote_ctx_t)); - balloc(remote->buf, SOCKET_BUF_SIZE); - memset(remote->recv_ctx, 0, sizeof(remote_ctx_t)); - memset(remote->send_ctx, 0, sizeof(remote_ctx_t)); - remote->recv_ctx->connected = 0; - remote->send_ctx->connected = 0; - remote->fd = fd; - remote->recv_ctx->remote = remote; - remote->send_ctx->remote = remote; - - ss_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, SS_READ); - ss_io_init(&remote->send_ctx->io, remote_send_cb, fd, SS_WRITE); - ss_timer_init(&remote->send_ctx->watcher, remote_timeout_cb, - min(MAX_CONNECT_TIMEOUT, timeout), 0); - - return remote; -} - -static void -free_remote(remote_t *remote) -{ - if (remote->server != NULL) { - remote->server->remote = NULL; - } - if (remote->buf != NULL) { - bfree(remote->buf); - ss_free(remote->buf); - } - ss_free(remote->recv_ctx); - ss_free(remote->send_ctx); - ss_free(remote); -} - -static void -close_and_free_remote(SS_P_ remote_t *remote) -{ - if (remote != NULL) { - ss_timer_stop(SS_A_ & remote->send_ctx->watcher); - ss_io_stop(SS_A_ & remote->send_ctx->io); - ss_io_stop(SS_A_ & remote->recv_ctx->io); - ss_socket_close(remote->fd); - free_remote(remote); - } -} - -static server_t * -new_server(int fd) -{ - server_t *server; - server = (server_t *)ss_malloc(sizeof(server_t)); - - memset(server, 0, sizeof(server_t)); - - server->recv_ctx = (struct server_ctx *)ss_malloc(sizeof(server_ctx_t)); - server->send_ctx = (struct server_ctx *)ss_malloc(sizeof(server_ctx_t)); - server->buf = (buffer_t *)ss_malloc(sizeof(buffer_t)); - server->abuf = (buffer_t *)ss_malloc(sizeof(buffer_t)); - balloc(server->buf, SOCKET_BUF_SIZE); - balloc(server->abuf, SOCKET_BUF_SIZE); - memset(server->recv_ctx, 0, sizeof(server_ctx_t)); - memset(server->send_ctx, 0, sizeof(server_ctx_t)); - server->stage = STAGE_INIT; - server->recv_ctx->connected = 0; - server->send_ctx->connected = 0; - server->fd = fd; - server->recv_ctx->server = server; - server->send_ctx->server = server; - - server->e_ctx = (cipher_ctx_t *)ss_malloc(sizeof(cipher_ctx_t)); - server->d_ctx = (cipher_ctx_t *)ss_malloc(sizeof(cipher_ctx_t)); - crypto->ctx_init(crypto->cipher, server->e_ctx, 1); - crypto->ctx_init(crypto->cipher, server->d_ctx, 0); - server->e_ctx->printable_salt = printable_salt; - cipher_ctx_pair(server->e_ctx, server->d_ctx); - - ss_io_init(&server->recv_ctx->io, server_recv_cb, fd, SS_READ); - ss_io_init(&server->send_ctx->io, server_send_cb, fd, SS_WRITE); - - ss_timer_init(&server->delayed_connect_watcher, - delayed_connect_cb, 0.05, 0); - - ss_list_add(&connections, &server->entries); - - return server; -} - -static void -free_server(server_t *server) -{ - ss_list_remove(&server->entries); - - if (server->remote != NULL) { - server->remote->server = NULL; - } - if (server->e_ctx != NULL) { - crypto->ctx_release(server->e_ctx); - ss_free(server->e_ctx); - } - if (server->d_ctx != NULL) { - crypto->ctx_release(server->d_ctx); - ss_free(server->d_ctx); - } - if (server->buf != NULL) { - bfree(server->buf); - ss_free(server->buf); - } - if (server->abuf != NULL) { - bfree(server->abuf); - ss_free(server->abuf); - } - ss_free(server->recv_ctx); - ss_free(server->send_ctx); - ss_free(server); -} - -static void -close_and_free_server(SS_P_ server_t *server) -{ - if (server != NULL) { - if (server->dns_query) { - server->dns_query->server = NULL; - server->dns_query = NULL; - } - ss_io_stop(SS_A_ & server->send_ctx->io); - ss_io_stop(SS_A_ & server->recv_ctx->io); - ss_timer_stop(SS_A_ & server->delayed_connect_watcher); - ss_socket_close(server->fd); - free_server(server); - } -} - -static remote_t * -create_remote(listen_ctx_t *listener, - struct sockaddr *addr, - int direct) -{ - struct sockaddr *remote_addr; - - int index = (int)randombytes_uniform((uint32_t)listener->remote_num); - if (addr == NULL) { - remote_addr = listener->remote_addr[index]; - } else { - remote_addr = addr; - } - - int protocol = IPPROTO_TCP; - if (listener->mptcp < 0) { - protocol = IPPROTO_MPTCP; // Enable upstream MPTCP - } - int remotefd = ss_socket(remote_addr->sa_family, SOCK_STREAM, protocol); - if (remotefd == -1) { - ERROR("socket"); - return NULL; - } - - int opt = 1; - ss_setsockopt(remotefd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); -#ifdef SO_NOSIGPIPE - ss_setsockopt(remotefd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); -#endif - - // Enable out-of-tree MPTCP - if (listener->mptcp > 1) { - int err = ss_setsockopt(remotefd, SOL_TCP, listener->mptcp, &opt, sizeof(opt)); - if (err == -1) { - ERROR("failed to enable out-of-tree multipath TCP"); - } - } else if (listener->mptcp == 1) { - int i = 0; - while ((listener->mptcp = mptcp_enabled_values[i]) > 0) { - int err = ss_setsockopt(remotefd, SOL_TCP, listener->mptcp, &opt, sizeof(opt)); - if (err != -1) { - break; - } - i++; - } - if (listener->mptcp == 0) { - ERROR("failed to enable out-of-tree multipath TCP"); - } - } - - if (tcp_outgoing_sndbuf > 0) { - ss_setsockopt(remotefd, SOL_SOCKET, SO_SNDBUF, &tcp_outgoing_sndbuf, sizeof(int)); - } - - if (tcp_outgoing_rcvbuf > 0) { - ss_setsockopt(remotefd, SOL_SOCKET, SO_RCVBUF, &tcp_outgoing_rcvbuf, sizeof(int)); - } - - // Setup - setnonblocking(remotefd); -#ifdef SET_INTERFACE - if (listener->iface) { - if (setinterface(remotefd, listener->iface) == -1) - ERROR("setinterface"); - } -#endif - - remote_t *remote = new_remote(remotefd, direct ? MAX_CONNECT_TIMEOUT : listener->timeout); - remote->addr_len = get_sockaddr_len(remote_addr); - memcpy(&(remote->addr), remote_addr, remote->addr_len); - remote->direct = direct; - - if (verbose) { - struct sockaddr_in *sockaddr = (struct sockaddr_in *)&remote->addr; - LOGI("remote: %s:%hu", inet_ntoa(sockaddr->sin_addr), ntohs(sockaddr->sin_port)); - } - - return remote; -} +} // namespace static void signal_cb(SS_P_ ss_signal *w, int revents) @@ -1457,28 +404,6 @@ plugin_watcher_cb(SS_P_ ss_io *w, int revents) #endif -void -accept_cb(SS_P_ ss_io *w, int revents) -{ - listen_ctx_t *listener = (listen_ctx_t *)w; - int serverfd = ss_accept(listener->fd, NULL, NULL); - if (serverfd == -1) { - ERROR("accept"); - return; - } - setnonblocking(serverfd); - int opt = 1; - ss_setsockopt(serverfd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); -#ifdef SO_NOSIGPIPE - ss_setsockopt(serverfd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); -#endif - - server_t *server = new_server(serverfd); - server->listener = listener; - - ss_io_start(SS_A_ & server->recv_ctx->io); -} - #ifndef LIB_ONLY int main(int argc, char **argv) @@ -2219,10 +1144,9 @@ Same behavior as `-S`; see that option for details. Short alias: `-S`. FATAL("failed to initialize ciphers"); // Setup proxy context - listen_ctx_t listen_ctx; - listen_ctx.remote_num = 0; - listen_ctx.remote_addr = (struct sockaddr **)ss_malloc(sizeof(struct sockaddr *) * remote_num); - memset(listen_ctx.remote_addr, 0, sizeof(struct sockaddr *) * remote_num); + struct sockaddr_storage remote_storage[MAX_REMOTE_NUM]; + int remote_count = 0; + memset(remote_storage, 0, sizeof(remote_storage)); for (i = 0; i < remote_num; i++) { char *host = remote_addr[i].host; char *port = remote_addr[i].port == NULL ? remote_port : @@ -2231,20 +1155,17 @@ Same behavior as `-S`; see that option for details. Short alias: `-S`. host = plugin_host; port = plugin_port; } - struct sockaddr_storage *storage = (struct sockaddr_storage *)ss_malloc(sizeof(struct sockaddr_storage)); - memset(storage, 0, sizeof(struct sockaddr_storage)); - if (get_sockaddr(host, port, storage, 1, ipv6first) == -1) { + if (get_sockaddr(host, port, &remote_storage[i], 1, ipv6first) == -1) { FATAL("failed to resolve the provided hostname"); } - listen_ctx.remote_addr[i] = (struct sockaddr *)storage; - ++listen_ctx.remote_num; + ++remote_count; if (plugin != NULL) break; } - listen_ctx.timeout = timeout_secs; - listen_ctx.iface = iface; - listen_ctx.mptcp = mptcp; + ss::relay::TcpConfig tcp_config; + fill_tcp_config(tcp_config, remote_storage, remote_count, timeout_secs, mptcp, iface); + TcpSide tcp; // Setup signal handler ss_signal_init(&sigint_watcher, signal_cb, SIGINT); @@ -2262,7 +1183,6 @@ Same behavior as `-S`; see that option for details. Short alias: `-S`. LOGI("listening at %s:%s", local_addr, local_port); struct ss_loop *loop = SS_DEFAULT; - if (acl) resolv_init(loop, NULL, ipv6first); if (mode != UDP_ONLY) { // Setup socket @@ -2278,12 +1198,9 @@ Same behavior as `-S`; see that option for details. Short alias: `-S`. if (listen(listenfd, SOMAXCONN) == -1) { FATAL("listen() error"); } - setnonblocking(listenfd); - - listen_ctx.fd = listenfd; - - ss_io_init(&listen_ctx.io, accept_cb, listenfd, SS_READ); - ss_io_start(loop, &listen_ctx.io); + if (!tcp.start(loop, method, password, key, tcp_config, listenfd)) { + FATAL("failed to set up the TCP relay"); + } } // Setup UDP @@ -2298,7 +1215,7 @@ Same behavior as `-S`; see that option for details. Short alias: `-S`. } struct sockaddr *addr = (struct sockaddr *)storage; udp_fd = init_udprelay(local_addr, local_port, addr, - get_sockaddr_len(addr), mtu, crypto, listen_ctx.timeout, iface); + get_sockaddr_len(addr), mtu, crypto, timeout_secs, iface); if (udp_fd == -1) { FATAL("failed to initialize UDP relay"); } @@ -2321,9 +1238,6 @@ Same behavior as `-S`; see that option for details. Short alias: `-S`. } #endif - // Init connections - ss_list_init(&connections); - // Enter the loop ss_run(loop, 0); @@ -2336,19 +1250,11 @@ Same behavior as `-S`; see that option for details. Short alias: `-S`. stop_plugin(); } - if (mode != UDP_ONLY) { - ss_io_stop(loop, &listen_ctx.io); - free_connections(loop); - - for (i = 0; i < listen_ctx.remote_num; i++) - ss_free(listen_ctx.remote_addr[i]); - ss_free(listen_ctx.remote_addr); - } + tcp.stop(); if (mode != TCP_ONLY) { free_udprelay(); } - if (acl) resolv_shutdown(loop); ss_loop_destroy(loop); #ifdef __MINGW32__ @@ -2433,18 +1339,10 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat // Setup proxy context struct ss_loop *loop = SS_DEFAULT; - if (acl) resolv_init(loop, NULL, ipv6first); - - struct sockaddr *remote_addr_tmp[MAX_REMOTE_NUM]; - listen_ctx_t listen_ctx; - // fd stays -1 in UDP_ONLY mode but is still passed to the callback below - listen_ctx.fd = -1; - listen_ctx.remote_num = 1; - listen_ctx.remote_addr = remote_addr_tmp; - listen_ctx.remote_addr[0] = (struct sockaddr *)(&storage); - listen_ctx.timeout = timeout; - listen_ctx.iface = NULL; - listen_ctx.mptcp = mptcp; + ss::relay::TcpConfig tcp_config; + fill_tcp_config(tcp_config, &storage, 1, timeout, mptcp, NULL); + TcpSide tcp; + int listen_fd = -1; // stays -1 in UDP_ONLY mode but is still passed to the callback below if (ss_is_ipv6addr(local_addr)) LOGI("listening at [%s]:%s", local_addr, local_port_str); @@ -2463,12 +1361,11 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat ERROR("listen()"); return -1; } - setnonblocking(listenfd); - - listen_ctx.fd = listenfd; - - ss_io_init(&listen_ctx.io, accept_cb, listenfd, SS_READ); - ss_io_start(loop, &listen_ctx.io); + if (!tcp.start(loop, method, password, NULL, tcp_config, listenfd)) { + LOGE("failed to set up the TCP relay"); + return -1; + } + listen_fd = listenfd; } // Setup UDP @@ -2482,11 +1379,8 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat } } - // Init connections - ss_list_init(&connections); - if (callback) { - callback(listen_ctx.fd, udp_fd, udata); + callback(listen_fd, udp_fd, udata); } // Enter the loop @@ -2497,16 +1391,11 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat } // Clean up - if (mode != UDP_ONLY) { - ss_io_stop(loop, &listen_ctx.io); - free_connections(loop); - ss_socket_close(listen_ctx.fd); - } + tcp.stop(); if (mode != TCP_ONLY) { free_udprelay(); } - if (acl) resolv_shutdown(loop); ss_loop_destroy(loop); #ifdef __MINGW32__ diff --git a/src/local.h b/src/local.h deleted file mode 100644 index ae526ab9..00000000 --- a/src/local.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * local.h - Define the client's buffers and callbacks - * - * 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 - * . - */ - -#ifndef _LOCAL_H -#define _LOCAL_H - -#include "core.h" - -#include "ss_event.h" - -#ifdef __MINGW32__ -#include "ss_windows.h" -#endif - -#include "crypto.h" -#include "jconf.h" - -#include "common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct listen_ctx { - ss_io io; - char *iface; - int remote_num; - int timeout; - int fd; - int mptcp; - struct sockaddr **remote_addr; -} listen_ctx_t; - -typedef struct server_ctx { - ss_io io; - int connected; - struct server *server; -} server_ctx_t; - -typedef struct server { - int fd; - int stage; - struct local_dns_query *dns_query; - int dns_done, dns_success; - struct sockaddr_storage dns_address; - - cipher_ctx_t *e_ctx; - cipher_ctx_t *d_ctx; - struct server_ctx *recv_ctx; - struct server_ctx *send_ctx; - struct listen_ctx *listener; - struct remote *remote; - - buffer_t *buf; - buffer_t *abuf; - - ss_timer delayed_connect_watcher; - - struct ss_list_item entries; -} server_t; - -typedef struct remote_ctx { - ss_io io; - ss_timer watcher; - - int connected; - struct remote *remote; -} remote_ctx_t; - -typedef struct remote { - int fd; - int direct; - int addr_len; - uint32_t counter; -#ifdef TCP_FASTOPEN_WINSOCK - OVERLAPPED olap; - int connect_ex_done; -#endif - - buffer_t *buf; - - struct remote_ctx *recv_ctx; - struct remote_ctx *send_ctx; - struct server *server; - struct sockaddr_storage addr; -} remote_t; - -#ifdef __cplusplus -} -#endif - -#endif // _LOCAL_H