/* * ss_socks5_client.c * 复刻 Python 版本的加密 SOCKS5 客户端(TCP/UDP),高性能 C 实现 * * 依赖: libevent, OpenSSL * 构建: gcc -O3 -march=native -DNDEBUG ss_socks5_client.c -o ss_socks5_client -levent -lcrypto * * 作者:Galaxy 的专属“码农工具人” * 风险提示:仅供学习与实验,请遵从当地法律法规。 */ /* ===== 平台头文件与兼容层 ===== */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0A00 /* Win10 */ #endif #define _WINSOCK_DEPRECATED_NO_WARNINGS #include #include #pragma comment(lib, "Ws2_32.lib") typedef int socklen_t; #ifndef ssize_t #define ssize_t SSIZE_T #endif #define CLOSESOCK(s) closesocket((s)) #define SOCK_ERR() WSAGetLastError() #define WOULD_BLOCK(e) ((e)==WSAEWOULDBLOCK) /* 没有 /,选项值由 winsock 提供 */ #else #include #include #include #include #include #include #include #define CLOSESOCK(s) close((s)) #define SOCK_ERR() errno #define WOULD_BLOCK(e) ((e)==EAGAIN || (e)==EWOULDBLOCK) #endif /*----------------------------- 配置与常量 ------------------------------*/ #define RECV_BUF_DEFAULT (64 * 1024) #define DNS_CACHE_SIZE 4096 #define UDP_MAP_CAP 2048 #define DOMAIN_MAX 255 #define LOG_BUFSZ 1024 /* Python 中的 DOMAIN_LISTS */ static const char *k_domain_lists[] = { "google.com", "youtube.com", "github.com", "githubassets.com", "ggpht.com", "googlevideo.com", "ytimg.com" }; static const size_t k_domain_lists_cnt = sizeof(k_domain_lists)/sizeof(k_domain_lists[0]); typedef enum { LOG_DEBUG = 0, LOG_INFO = 1, LOG_WARN = 2, LOG_ERROR = 3 } log_level_t; static log_level_t g_log_level = LOG_INFO; static void vlog_msg(log_level_t lv, const char *fmt, va_list ap) { if (lv < g_log_level) return; static const char *tag[] = {"DEBUG","INFO","WARN","ERROR"}; char buf[LOG_BUFSZ]; vsnprintf(buf, sizeof(buf), fmt, ap); fprintf((lv >= LOG_WARN) ? stderr : stdout, "%s: %s\n", tag[lv], buf); } static void log_msg(log_level_t lv, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlog_msg(lv, fmt, ap); va_end(ap); } /*----------------------------- 实用函数 ------------------------------*/ static bool ends_with(const char *s, const char *suffix) { size_t ls = strlen(s), lt = strlen(suffix); if (lt > ls) return false; return strcasecmp(s + (ls - lt), suffix) == 0; } static bool host_in_domain_list(const char *host) { for (size_t i = 0; i < k_domain_lists_cnt; ++i) { if (ends_with(host, k_domain_lists[i])) return true; } return false; } /* 把文本 host:port 解析成 sockaddr_storage(支持 IPv4/IPv6/域名解析) */ static bool resolve_host_port(const char *host, uint16_t port, struct sockaddr_storage *ss, socklen_t *sslen) { struct addrinfo hints, *res = NULL; char portstr[16]; snprintf(portstr, sizeof(portstr), "%u", (unsigned)port); memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; int rc = getaddrinfo(host, portstr, &hints, &res); if (rc != 0 || !res) { log_msg(LOG_ERROR, "getaddrinfo(%s:%s) failed: %s", host, portstr, (rc!=0 ? gai_strerror(rc) : "no result")); if (res) freeaddrinfo(res); return false; } memcpy(ss, res->ai_addr, res->ai_addrlen); *sslen = (socklen_t)res->ai_addrlen; freeaddrinfo(res); return true; } /* 尝试将文本 host 解析为字面 IP(成功返回 AF_INET/AF_INET6;失败返回 0) */ static int host_literal_ip(const char *host, uint8_t *packed16, size_t *packed_len) { struct in_addr a4; struct in6_addr a6; if (inet_pton(AF_INET, host, &a4) == 1) { memcpy(packed16, &a4, 4); *packed_len = 4; return AF_INET; } if (inet_pton(AF_INET6, host, &a6) == 1) { memcpy(packed16, &a6, 16); *packed_len = 16; return AF_INET6; } return 0; } /*----------------------------- KDF 与 AES ------------------------------*/ static void kdf_evp_bytes_to_key(const uint8_t *pw, size_t pwlen, uint8_t out32[32]) { /* out = MD5(pw) || MD5(MD5(pw) + pw) */ uint8_t h1[16]; uint8_t h2[16]; MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx, pw, pwlen); MD5_Final(h1, &ctx); MD5_Init(&ctx); MD5_Update(&ctx, h1, 16); MD5_Update(&ctx, pw, pwlen); MD5_Final(h2, &ctx); memcpy(out32, h1, 16); memcpy(out32 + 16, h2, 16); } typedef struct { EVP_CIPHER_CTX *ctx; } aes_cfb_stream_t; static void aes_cfb_init(aes_cfb_stream_t *s, const uint8_t key[32], const uint8_t iv[16], int enc) { s->ctx = EVP_CIPHER_CTX_new(); EVP_CipherInit_ex(s->ctx, EVP_aes_256_cfb128(), NULL, key, iv, enc); } static void aes_cfb_free(aes_cfb_stream_t *s) { if (s->ctx) EVP_CIPHER_CTX_free(s->ctx); } static void aes_cfb_update(aes_cfb_stream_t *s, const uint8_t *in, size_t inlen, uint8_t *out, int *outlen) { int n = 0; EVP_CipherUpdate(s->ctx, out, &n, in, (int)inlen); *outlen = n; } /* 单包 CFB:用于 UDP(每包新 IV) */ static int aes_cfb_one_shot(const uint8_t key[32], const uint8_t iv[16], int enc, const uint8_t *in, size_t inlen, uint8_t *out) { EVP_CIPHER_CTX *c = EVP_CIPHER_CTX_new(); if (!c) return -1; int n1 = 0, n2 = 0; EVP_CipherInit_ex(c, EVP_aes_256_cfb128(), NULL, key, iv, enc); if (!EVP_CipherUpdate(c, out, &n1, in, (int)inlen)) { EVP_CIPHER_CTX_free(c); return -1; } if (!EVP_CipherFinal_ex(c, out + n1, &n2)) { EVP_CIPHER_CTX_free(c); return -1; } EVP_CIPHER_CTX_free(c); return n1 + n2; } /*----------------------------- 地址编码 ------------------------------*/ /* out: TCP 自定义编码 [0x61/0x63/0x64 | host | port] */ static bool encode_addr_tcp(uint8_t atyp, const char *host, uint16_t port, struct evbuffer *out) { uint8_t packed[16]; size_t plen = 0; switch (atyp) { case 0x01: { /* IPv4 -> 0x61 */ if (host_literal_ip(host, packed, &plen) != AF_INET) return false; evbuffer_add_printf(out, "%c", (char)0x61); evbuffer_add(out, packed, 4); break; } case 0x03: { /* DOMAIN -> 0x63 */ size_t len = strlen(host); if (len > 255) return false; evbuffer_add_printf(out, "%c", (char)0x63); evbuffer_add_printf(out, "%c", (char)len); evbuffer_add(out, host, len); break; } case 0x04: { /* IPv6 -> 0x64 */ if (host_literal_ip(host, packed, &plen) != AF_INET6) return false; evbuffer_add_printf(out, "%c", (char)0x64); evbuffer_add(out, packed, 16); break; } default: return false; } uint16_t nport = htons(port); evbuffer_add(out, &nport, 2); return true; } /* out: UDP 标准编码 [0x01/0x03/0x04 | host | port] */ static bool encode_addr_udp(uint8_t atyp, const char *host, uint16_t port, struct evbuffer *out) { uint8_t packed[16]; size_t plen = 0; switch (atyp) { case 0x01: { if (host_literal_ip(host, packed, &plen) != AF_INET) return false; uint8_t t = 0x01; evbuffer_add(out, &t, 1); evbuffer_add(out, packed, 4); break; } case 0x03: { size_t len = strlen(host); if (len > 255) return false; uint8_t t = 0x03; evbuffer_add(out, &t, 1); uint8_t l = (uint8_t)len; evbuffer_add(out, &l, 1); evbuffer_add(out, host, len); break; } case 0x04: { if (host_literal_ip(host, packed, &plen) != AF_INET6) return false; uint8_t t = 0x04; evbuffer_add(out, &t, 1); evbuffer_add(out, packed, 16); break; } default: return false; } uint16_t nport = htons(port); evbuffer_add(out, &nport, 2); return true; } /*----------------------------- DNS 打包/解析 ------------------------------*/ /* 构造最小 A 记录查询(RD=1)。返回长度与 ID */ static int dns_build_query(const char *host, uint8_t *out, size_t cap, size_t *outlen, uint16_t *out_id) { if (!host || !out || cap < 12) return -1; uint16_t id; RAND_bytes((unsigned char*)&id, sizeof(id)); *out_id = id; size_t off = 0; // Header (12 bytes) uint16_t flags = htons(0x0100); // RD=1 uint16_t qd = htons(1), an = 0, ns = 0, ar = 0; if (cap < 12) return -1; *(uint16_t*)(out+off) = htons(id); off+=2; *(uint16_t*)(out+off) = flags; off+=2; *(uint16_t*)(out+off) = qd; off+=2; *(uint16_t*)(out+off) = an; off+=2; *(uint16_t*)(out+off) = ns; off+=2; *(uint16_t*)(out+off) = ar; off+=2; // Question: QNAME const char *p = host, *dot = NULL; while (*p) { dot = strchr(p, '.'); size_t lab_len = dot ? (size_t)(dot - p) : strlen(p); if (lab_len == 0 || lab_len > 63) return -1; if (off + 1 + lab_len >= cap) return -1; out[off++] = (uint8_t)lab_len; memcpy(out + off, p, lab_len); off += lab_len; if (!dot) break; p = dot + 1; } if (off + 1 + 4 > cap) return -1; out[off++] = 0; // end of name // QTYPE=A(1), QCLASS=IN(1) *(uint16_t*)(out+off) = htons(1); off+=2; *(uint16_t*)(out+off) = htons(1); off+=2; *outlen = off; return 0; } /* 读取(压缩)域名:仅前进 offset(不拷贝名字) */ static int dns_skip_name(const uint8_t *buf, size_t len, size_t *off) { size_t i = *off; while (i < len) { uint8_t c = buf[i++]; if ((c & 0xC0) == 0xC0) { // pointer if (i >= len) return -1; i++; break; } else if (c == 0) { break; } else { if (i + c > len) return -1; i += c; } } *off = i; return 0; } /* 解析 DNS 回复,提取第一条 A 记录;成功返回 0 并写入 ip_str */ static int dns_parse_a(const uint8_t *buf, size_t len, char *ip_str, size_t ip_cap) { if (len < 12) return -1; uint16_t qd = ntohs(*(const uint16_t *)(buf + 4)); uint16_t an = ntohs(*(const uint16_t *)(buf + 6)); size_t off = 12; for (uint16_t i = 0; i < qd; ++i) { if (dns_skip_name(buf, len, &off) != 0) return -1; if (off + 4 > len) return -1; off += 4; // QTYPE+QCLASS } for (uint16_t i = 0; i < an; ++i) { if (dns_skip_name(buf, len, &off) != 0) return -1; if (off + 10 > len) return -1; uint16_t type = ntohs(*(const uint16_t *)(buf + off)); off += 2; uint16_t class_= ntohs(*(const uint16_t *)(buf + off)); off += 2; off += 4; // TTL uint16_t rdlen = ntohs(*(const uint16_t *)(buf + off)); off += 2; if (off + rdlen > len) return -1; if (type == 1 && class_ == 1 && rdlen == 4) { struct in_addr a4; memcpy(&a4, buf + off, 4); const char *ret = inet_ntop(AF_INET, &a4, ip_str, (socklen_t)ip_cap); return ret ? 0 : -1; } off += rdlen; } return -1; } /*----------------------------- 轻量 TTL 缓存 ------------------------------*/ /* DNS 缓存:host -> ip (A 记录),TTL=300s,固定表 */ typedef struct { bool used; time_t expire; char host[DOMAIN_MAX+1]; char ip[INET_ADDRSTRLEN]; } dns_cache_entry_t; typedef struct { dns_cache_entry_t tab[DNS_CACHE_SIZE]; } dns_cache_t; static void dns_cache_init(dns_cache_t *c) { memset(c, 0, sizeof(*c)); } static const char* dns_cache_get(dns_cache_t *c, const char *host) { time_t now = time(NULL); for (size_t i = 0; i < DNS_CACHE_SIZE; ++i) { if (c->tab[i].used && strcmp(c->tab[i].host, host) == 0) { if (c->tab[i].expire > now) return c->tab[i].ip; c->tab[i].used = false; } } return NULL; } static void dns_cache_put(dns_cache_t *c, const char *host, const char *ip, int ttl_sec) { time_t now = time(NULL); size_t slot = DNS_CACHE_SIZE; time_t oldest = now; for (size_t i = 0; i < DNS_CACHE_SIZE; ++i) { if (!c->tab[i].used) { slot = i; break; } if (c->tab[i].expire < oldest) { oldest = c->tab[i].expire; slot = i; } } if (slot >= DNS_CACHE_SIZE) slot = 0; dns_cache_entry_t *e = &c->tab[slot]; e->used = true; e->expire = now + ttl_sec; strncpy(e->host, host, sizeof(e->host)-1); e->host[sizeof(e->host)-1] = 0; strncpy(e->ip, ip, sizeof(e->ip)-1); e->ip[sizeof(e->ip)-1] = 0; } /*----------------------------- 配置结构 ------------------------------*/ typedef struct { char remote_host[256]; int remote_port; char password[256]; char mx_head[256]; char listen_host[64]; int listen_port; int recv_buf; int connect_timeout; // seconds int udp_timeout; // seconds } config_t; static void config_default(config_t *cfg) { memset(cfg, 0, sizeof(*cfg)); strcpy(cfg->remote_host, "121.14.152.149"); cfg->remote_port = 10004; strcpy(cfg->password, "dwz1GtF7"); strcpy(cfg->mx_head, "com.win64.oppc.game.common:22021709,102024080020541279"); strcpy(cfg->listen_host, "0.0.0.0"); cfg->listen_port = 10807; cfg->recv_buf = RECV_BUF_DEFAULT; cfg->connect_timeout = 10; cfg->udp_timeout = 180; } /*----------------------------- 前置声明 ------------------------------*/ struct bridge_s; typedef struct bridge_s bridge_t; /*----------------------------- UDP 关联 ------------------------------*/ /* 将 [addr_block] -> client_addr 的映射保存在固定表 */ typedef struct { bool used; time_t ts; /* 以二进制 addr_block 做 key(避免重复编码差异) */ uint8_t key[300]; size_t key_len; struct sockaddr_storage client_ss; socklen_t client_len; } udp_map_entry_t; typedef struct { bridge_t *owner; int fd; struct event *ev_read; struct event *ev_timer; struct sockaddr_storage remote_ss; socklen_t remote_len; uint8_t key32[32]; udp_map_entry_t map[UDP_MAP_CAP]; struct sockaddr_storage last_client; socklen_t last_client_len; bool has_last; } udp_assoc_t; /* 遍历查找/设置映射 */ static int udp_map_find(udp_assoc_t *ua, const uint8_t *k, size_t klen) { for (int i = 0; i < UDP_MAP_CAP; ++i) { if (ua->map[i].used && ua->map[i].key_len == klen && memcmp(ua->map[i].key, k, klen) == 0) return i; } return -1; } static int udp_map_alloc_slot(udp_assoc_t *ua) { for (int i = 0; i < UDP_MAP_CAP; ++i) if (!ua->map[i].used) return i; /* 简单淘汰:最老 */ time_t oldest = time(NULL); int idx = 0; for (int i = 0; i < UDP_MAP_CAP; ++i) { if (ua->map[i].ts < oldest) { oldest = ua->map[i].ts; idx = i; } } return idx; } static void udp_assoc_close(udp_assoc_t *ua) { if (!ua) return; if (ua->ev_read) event_free(ua->ev_read); if (ua->ev_timer) event_free(ua->ev_timer); if (ua->fd >= 0) CLOSESOCK(ua->fd); free(ua); } /*----------------------------- 桥接(CONNECT/UDP) ------------------------------*/ typedef enum { ST_GREETING = 0, ST_REQUEST = 1, ST_CONNECTING_REMOTE = 2, ST_STREAM = 3, ST_UDP_ASSOC= 4, ST_CLOSED = 5 } bridge_state_t; typedef struct { uint8_t cmd; /* 0x01 CONNECT, 0x03 UDP ASSOC */ uint8_t atyp; /* 0x01/0x03/0x04 */ char host[256]; uint16_t port; } socks5_req_t; struct bridge_s { struct event_base *base; config_t *cfg; /* client <-> us */ struct bufferevent *cli_bev; evutil_socket_t cli_fd; /* server <-> us */ struct bufferevent *srv_bev; evutil_socket_t srv_fd; /* 状态机 */ bridge_state_t state; /* 密钥与流加解密 */ uint8_t key32[32]; aes_cfb_stream_t up_enc; bool up_ready; aes_cfb_stream_t down_dec; bool down_ready; uint8_t iv_server[16]; size_t iv_have; /* UDP 关联与 DNS 隧道 */ udp_assoc_t *udp_assoc; /* DNS 缓存(按 Python 行为在 bridge 级别重用) */ dns_cache_t dns_cache; /* 当前请求 */ socks5_req_t req; }; /*----------------------------- SOCKS5 回复 ------------------------------*/ static void socks5_send_reply(struct bufferevent *bev, uint8_t rep, const char *bind_host, uint16_t bind_port) { struct evbuffer *out = evbuffer_new(); uint8_t ver = 5, rsv = 0; evbuffer_add(out, &ver, 1); evbuffer_add(out, &rep, 1); evbuffer_add(out, &rsv, 1); uint8_t packed[16]; size_t plen = 0; int af = host_literal_ip(bind_host, packed, &plen); if (af == AF_INET) { uint8_t t = 0x01; evbuffer_add(out, &t, 1); evbuffer_add(out, packed, 4); } else if (af == AF_INET6) { uint8_t t = 0x04; evbuffer_add(out, &t, 1); evbuffer_add(out, packed, 16); } else { size_t len = strlen(bind_host); if (len > 255) len = 255; uint8_t t = 0x03; uint8_t l = (uint8_t)len; evbuffer_add(out, &t, 1); evbuffer_add(out, &l, 1); evbuffer_add(out, bind_host, len); } uint16_t p = htons(bind_port); evbuffer_add(out, &p, 2); bufferevent_write_buffer(bev, out); evbuffer_free(out); } /*----------------------------- 解析 SOCKS5 请求 ------------------------------*/ static int parse_socks5_request(struct evbuffer *in, socks5_req_t *req, size_t *consumed) { size_t len = evbuffer_get_length(in); if (len < 4) return 0; unsigned char *p = evbuffer_pullup(in, len); if (!p) return 0; if (p[0] != 5) return -2; req->cmd = p[1]; req->atyp = p[3]; size_t off = 4; if (req->atyp == 0x01) { /* IPv4 */ if (len < off + 4 + 2) return 0; char ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, p + off, ip, sizeof(ip)); strncpy(req->host, ip, sizeof(req->host)-1); req->host[255]=0; off += 4; req->port = ntohs(*(uint16_t*)(p + off)); off += 2; } else if (req->atyp == 0x03) { if (len < off + 1) return 0; uint8_t l = p[off++]; if (len < off + l + 2) return 0; size_t cplen = (l > 255) ? 255 : l; memcpy(req->host, p + off, cplen); req->host[cplen]=0; off += l; req->port = ntohs(*(uint16_t*)(p + off)); off += 2; } else if (req->atyp == 0x04) { if (len < off + 16 + 2) return 0; char ip6[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, p + off, ip6, sizeof(ip6)); strncpy(req->host, ip6, sizeof(req->host)-1); req->host[255]=0; off += 16; req->port = ntohs(*(uint16_t*)(p + off)); off += 2; } else { return -3; } *consumed = off; return 1; } /*----------------------------- UDP 关联实现 ------------------------------*/ static void udp_assoc_on_timer(evutil_socket_t fd, short what, void *arg); static void udp_assoc_on_read(evutil_socket_t fd, short what, void *arg); static udp_assoc_t* udp_assoc_create(bridge_t *b) { udp_assoc_t *ua = (udp_assoc_t*)calloc(1, sizeof(*ua)); ua->owner = b; ua->fd = socket(AF_INET, SOCK_DGRAM, 0); if (ua->fd < 0) { free(ua); return NULL; } int on = 1; setsockopt(ua->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); #ifdef SO_REUSEPORT setsockopt(ua->fd, SOL_SOCKET, SO_REUSEPORT, (const char*)&on, sizeof(on)); #endif int sz = 1<<20; setsockopt(ua->fd, SOL_SOCKET, SO_SNDBUF, (const char*)&sz, sizeof(sz)); setsockopt(ua->fd, SOL_SOCKET, SO_RCVBUF, (const char*)&sz, sizeof(sz)); /* 绑定到监听地址(端口 0 随机) */ struct sockaddr_in sin; memset(&sin,0,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(0); inet_pton(AF_INET, b->cfg->listen_host, &sin.sin_addr); if (bind(ua->fd, (struct sockaddr*)&sin, sizeof(sin)) != 0) { log_msg(LOG_ERROR, "UDP bind failed: %s", strerror(errno)); CLOSESOCK(ua->fd); free(ua); return NULL; } evutil_make_socket_nonblocking(ua->fd); /* 远端目标(与 TCP 相同的 remote_host:remote_port) */ if (!resolve_host_port(b->cfg->remote_host, (uint16_t)b->cfg->remote_port, &ua->remote_ss, &ua->remote_len)) { CLOSESOCK(ua->fd); free(ua); return NULL; } memcpy(ua->key32, b->key32, 32); ua->ev_read = event_new(b->base, ua->fd, EV_READ|EV_PERSIST, udp_assoc_on_read, ua); event_add(ua->ev_read, NULL); /* TTL 定期清理 */ struct timeval tv = { .tv_sec = 5, .tv_usec = 0 }; ua->ev_timer = event_new(b->base, -1, EV_PERSIST, udp_assoc_on_timer, ua); event_add(ua->ev_timer, &tv); return ua; } static void udp_assoc_on_timer(evutil_socket_t fd, short what, void *arg) { (void)fd; (void)what; udp_assoc_t *ua = (udp_assoc_t*)arg; time_t now = time(NULL); for (int i = 0; i < UDP_MAP_CAP; ++i) { if (ua->map[i].used && now - ua->map[i].ts > ua->owner->cfg->udp_timeout) { ua->map[i].used = false; } } } /* 解析 SOCKS5 UDP 请求头,返回 与 payload 偏移 */ static bool udp_parse_client_packet(const uint8_t *buf, size_t len, size_t *addr_off, size_t *payload_off) { if (len < 3) return false; if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0) return false; /* RSV,FRAG=0 */ size_t off = 3; if (len < off + 1) return false; uint8_t atyp = buf[off++]; if (atyp == 0x01) { /* IPv4 */ if (len < off + 4 + 2) return false; off += 4 + 2; } else if (atyp == 0x03) { if (len < off + 1) return false; uint8_t l = buf[off++]; if (len < off + l + 2) return false; off += l + 2; } else if (atyp == 0x04) { if (len < off + 16 + 2) return false; off += 16 + 2; } else return false; *addr_off = 3; *payload_off = off; return true; } /* 将 规范化编码回自己,便于作为 key 存储 */ static bool udp_clone_addr_block(const uint8_t *buf, size_t addr_off, size_t payload_off, uint8_t *out, size_t *outlen) { size_t n = payload_off - addr_off; if (n > 300) return false; memcpy(out, buf + addr_off, n); *outlen = n; return true; } static void udp_assoc_sendto(udp_assoc_t *ua, const void *data, size_t len) { sendto(ua->fd, (const char*)data, (int)len, 0, (struct sockaddr*)&ua->remote_ss, ua->remote_len); } /* UDP 读回调:可能来自 local app 或 remote server */ static void udp_assoc_on_read(evutil_socket_t fd, short what, void *arg) { (void)what; udp_assoc_t *ua = (udp_assoc_t*)arg; uint8_t buf[65536]; struct sockaddr_storage from; socklen_t fromlen = sizeof(from); for (;;) { int n = (int)recvfrom(fd, (char*)buf, (int)sizeof(buf), 0, (struct sockaddr*)&from, &fromlen); if (n < 0) { int e = SOCK_ERR(); if (WOULD_BLOCK(e)) return; log_msg(LOG_WARN, "UDP recv error: %d", e); return; } /* 判断是否来自 remote */ bool from_remote = false; if (from.ss_family == ua->remote_ss.ss_family) { if (from.ss_family == AF_INET) { struct sockaddr_in *a=(struct sockaddr_in*)&from, *b=(struct sockaddr_in*)&ua->remote_ss; from_remote = (a->sin_port==b->sin_port && a->sin_addr.s_addr==b->sin_addr.s_addr); } else if (from.ss_family == AF_INET6) { struct sockaddr_in6 *a=(struct sockaddr_in6*)&from, *b=(struct sockaddr_in6*)&ua->remote_ss; from_remote = (a->sin6_port==b->sin6_port && memcmp(&a->sin6_addr,&b->sin6_addr,sizeof(a->sin6_addr))==0); } } if (from_remote) { /* remote -> local : [IV][CFB(addr_block+payload)] */ if (n < 16) continue; const uint8_t *iv = buf; const uint8_t *ct = buf + 16; size_t ctlen = (size_t)n - 16; uint8_t plain[65536]; int m = aes_cfb_one_shot(ua->key32, iv, 0, ct, ctlen, plain); if (m <= 0) continue; /* 解析 addr_block 以便回送给正确的 client */ size_t off = 0; if (m < 1) continue; uint8_t atyp = plain[off++]; if (atyp == 0x01) off += 4 + 2; else if (atyp == 0x03) { if (off>= (size_t)m) continue; uint8_t l=plain[off++]; off += l + 2; } else if (atyp == 0x04) off += 16 + 2; else continue; if ((size_t)m < off) continue; size_t addrlen = off; size_t payload_len = (size_t)m - off; /* 尝试匹配映射表 */ int idx = udp_map_find(ua, plain, addrlen); struct sockaddr_storage dst; socklen_t dstlen = 0; if (idx >= 0) { dst = ua->map[idx].client_ss; dstlen = ua->map[idx].client_len; } else if (ua->has_last) { dst = ua->last_client; dstlen = ua->last_client_len; } else { continue; } /* 组装 SOCKS5 UDP 响应:RSV|FRAG(0)|addr|payload */ uint8_t out[65536]; size_t outlen = 3 + addrlen + payload_len; if (outlen > sizeof(out)) continue; out[0] = 0; out[1] = 0; out[2] = 0; memcpy(out + 3, plain, addrlen); memcpy(out + 3 + addrlen, plain + addrlen, payload_len); sendto(ua->fd, (const char*)out, (int)outlen, 0, (struct sockaddr*)&dst, dstlen); } else { /* local app -> remote : SOCKS5 UDP 请求 */ size_t addr_off=0, payload_off=0; if (!udp_parse_client_packet(buf, (size_t)n, &addr_off, &payload_off)) continue; /* 存映射:addr_block -> client */ uint8_t keybuf[300]; size_t klen=0; if (!udp_clone_addr_block(buf, addr_off, payload_off, keybuf, &klen)) continue; int idx = udp_map_find(ua, keybuf, klen); if (idx < 0) idx = udp_map_alloc_slot(ua); ua->map[idx].used = true; ua->map[idx].ts = time(NULL); memcpy(ua->map[idx].key, keybuf, klen); ua->map[idx].key_len = klen; ua->map[idx].client_ss = from; ua->map[idx].client_len = fromlen; ua->last_client = from; ua->last_client_len = fromlen; ua->has_last = true; /* 加密并发往 remote:IV + CFB(addr_block + payload) */ uint8_t iv[16]; RAND_bytes(iv, sizeof(iv)); size_t plain_len = (size_t)n - 3; /* 去掉 RSV|FRAG */ const uint8_t *plain = buf + 3; uint8_t ct[65536]; int m = aes_cfb_one_shot(ua->key32, iv, 1, plain, plain_len, ct); if (m <= 0) continue; uint8_t out[65536]; if ((size_t)m + 16 > sizeof(out)) continue; memcpy(out, iv, 16); memcpy(out + 16, ct, m); udp_assoc_sendto(ua, out, (size_t)m + 16); } } } /*----------------------------- Bridge 读写/事件 ------------------------------*/ static void bridge_free(bridge_t *b) { if (!b) return; if (b->cli_bev) bufferevent_free(b->cli_bev); if (b->srv_bev) bufferevent_free(b->srv_bev); if (b->udp_assoc) udp_assoc_close(b->udp_assoc); if (b->up_ready) aes_cfb_free(&b->up_enc); if (b->down_ready)aes_cfb_free(&b->down_dec); free(b); } /* 读取 client 的 GREETING 并应答无认证 */ static int handle_socks5_greeting(struct bufferevent *bev) { struct evbuffer *in = bufferevent_get_input(bev); size_t len = evbuffer_get_length(in); if (len < 2) return 0; unsigned char hdr[2]; evbuffer_copyout(in, hdr, 2); if (hdr[0] != 5) return -2; size_t need = 2 + hdr[1]; if (len < need) return 0; evbuffer_drain(in, need); uint8_t resp[2] = {0x05, 0x00}; /* NO AUTH */ bufferevent_write(bev, resp, 2); return 1; } static void on_client_read(struct bufferevent *bev, void *ctx); static void on_server_read(struct bufferevent *bev, void *ctx); static void on_client_event(struct bufferevent *bev, short events, void *ctx); static void on_server_event(struct bufferevent *bev, short events, void *ctx); static void bridge_start_stream(bridge_t *b) { b->state = ST_STREAM; bufferevent_setcb(b->cli_bev, on_client_read, NULL, on_client_event, b); bufferevent_setcb(b->srv_bev, on_server_read, NULL, on_server_event, b); bufferevent_enable(b->cli_bev, EV_READ|EV_WRITE); bufferevent_enable(b->srv_bev, EV_READ|EV_WRITE); } /* 远端 TCP 连接完成后,发送首个包:[client_iv][CFB(addr + mx)] */ static void after_remote_connected(bridge_t *b) { struct evbuffer *addr = evbuffer_new(); if (!encode_addr_tcp(b->req.atyp, b->req.host, b->req.port, addr)) { log_msg(LOG_ERROR, "encode_addr_tcp failed"); socks5_send_reply(b->cli_bev, 0x04, "0.0.0.0", 0); /* Host unreachable */ evbuffer_free(addr); bridge_free(b); return; } /* mx head: 1byte len + bytes */ size_t mx_len = strlen(b->cfg->mx_head); if (mx_len > 255) { evbuffer_free(addr); bridge_free(b); return; } uint8_t ml = (uint8_t)mx_len; evbuffer_add(addr, &ml, 1); evbuffer_add(addr, b->cfg->mx_head, mx_len); size_t plain_len = evbuffer_get_length(addr); uint8_t *plain = (uint8_t*)malloc(plain_len); evbuffer_copyout(addr, plain, plain_len); uint8_t iv[16]; RAND_bytes(iv, sizeof(iv)); aes_cfb_init(&b->up_enc, b->key32, iv, 1); b->up_ready = true; /* 加密 */ uint8_t *ct = (uint8_t*)malloc(plain_len); int m = 0; aes_cfb_update(&b->up_enc, plain, plain_len, ct, &m); /* 发给 server:IV + CFB(addr+mx) */ bufferevent_write(b->srv_bev, iv, 16); bufferevent_write(b->srv_bev, ct, (size_t)m); /* 回应本地应用:成功,绑定地址 0.0.0.0:0 */ socks5_send_reply(b->cli_bev, 0x00, "0.0.0.0", 0); free(plain); free(ct); evbuffer_free(addr); bridge_start_stream(b); } /* 发起远端 TCP 连接 */ static void connect_remote(bridge_t *b) { b->srv_bev = bufferevent_socket_new(b->base, -1, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); bufferevent_setcb(b->srv_bev, NULL, NULL, on_server_event, b); bufferevent_enable(b->srv_bev, EV_READ|EV_WRITE); /* 解析 remote_host:remote_port */ struct sockaddr_storage ss; socklen_t sslen; if (!resolve_host_port(b->cfg->remote_host, (uint16_t)b->cfg->remote_port, &ss, &sslen)) { socks5_send_reply(b->cli_bev, 0x04, "0.0.0.0", 0); bridge_free(b); return; } /* 设置一些 socket 选项用于性能 */ evutil_socket_t fd = bufferevent_getfd(b->srv_bev); (void)fd; /* 连接后再设置选项 */ /* 连接 */ if (bufferevent_socket_connect(b->srv_bev, (struct sockaddr*)&ss, sslen) != 0) { socks5_send_reply(b->cli_bev, 0x05, "0.0.0.0", 0); bridge_free(b); return; } /* 读写超时(连接阶段 & 后续) */ struct timeval tv = { .tv_sec = b->cfg->connect_timeout, .tv_usec = 0 }; bufferevent_set_timeouts(b->srv_bev, &tv, &tv); b->state = ST_CONNECTING_REMOTE; } /* 执行“远端 UDP DNS 解析”并在完成后继续连接 */ typedef struct { bridge_t *b; int fd; struct event *ev_read; struct event *ev_timer; uint16_t id; char host[256]; } dns_tunnel_t; static void dns_tunnel_free(dns_tunnel_t *dt) { if (!dt) return; if (dt->ev_read) event_free(dt->ev_read); if (dt->ev_timer) event_free(dt->ev_timer); if (dt->fd >= 0) CLOSESOCK(dt->fd); free(dt); } static void dns_tunnel_on_timeout(evutil_socket_t fd, short what, void *arg) { (void)fd; (void)what; dns_tunnel_t *dt = (dns_tunnel_t*)arg; log_msg(LOG_WARN, "DNS query for %s timed out", dt->host); socks5_send_reply(dt->b->cli_bev, 0x04, "0.0.0.0", 0); dns_tunnel_free(dt); bridge_free(dt->b); } static void dns_tunnel_on_read(evutil_socket_t fd, short what, void *arg) { (void)what; dns_tunnel_t *dt = (dns_tunnel_t*)arg; uint8_t buf[65536]; struct sockaddr_storage from; socklen_t fromlen = sizeof(from); int n = (int)recvfrom(fd, (char*)buf, (int)sizeof(buf), 0, (struct sockaddr*)&from, &fromlen); if (n < 0) return; if (n < 16) { dns_tunnel_free(dt); return; } /* 解密并解析 DNS */ uint8_t plain[65536]; int m = aes_cfb_one_shot(dt->b->key32, buf, 0, buf + 16, (size_t)n - 16, plain); if (m <= 0) { dns_tunnel_free(dt); return; } /* 跳过 addr_block(8.8.8.8:53 的 SOCKS5 UDP 头) */ size_t off = 0; if (plain[0] == 0x01) off = 1 + 4 + 2; else if (plain[0] == 0x04) off = 1 + 16 + 2; else { dns_tunnel_free(dt); return; } if ((size_t)m <= off) { dns_tunnel_free(dt); return; } char ip[INET_ADDRSTRLEN]; if (dns_parse_a(plain + off, (size_t)m - off, ip, sizeof(ip)) == 0) { log_msg(LOG_DEBUG, "Resolved %s -> %s", dt->host, ip); dns_cache_put(&dt->b->dns_cache, dt->host, ip, 300); /* 修改请求为 IPv4 */ strncpy(dt->b->req.host, ip, sizeof(dt->b->req.host)-1); dt->b->req.atyp = 0x01; /* 去连接远端 */ dns_tunnel_free(dt); connect_remote(dt->b); return; } log_msg(LOG_WARN, "Could not parse A record for %s", dt->host); socks5_send_reply(dt->b->cli_bev, 0x04, "0.0.0.0", 0); dns_tunnel_free(dt); bridge_free(dt->b); } static void start_dns_over_tunnel(bridge_t *b, const char *host) { /* DNS 缓存命中 */ const char *cached = dns_cache_get(&b->dns_cache, host); if (cached) { log_msg(LOG_DEBUG, "DNS cache hit: %s -> %s", host, cached); strncpy(b->req.host, cached, sizeof(b->req.host)-1); b->req.atyp = 0x01; connect_remote(b); return; } dns_tunnel_t *dt = (dns_tunnel_t*)calloc(1, sizeof(*dt)); dt->b = b; dt->fd = socket(AF_INET, SOCK_DGRAM, 0); if (dt->fd < 0) { free(dt); socks5_send_reply(b->cli_bev, 0x04, "0.0.0.0", 0); bridge_free(b); return; } evutil_make_socket_nonblocking(dt->fd); strncpy(dt->host, host, sizeof(dt->host)-1); /* 远端 UDP 地址 = remote_host:remote_port */ struct sockaddr_storage r; socklen_t rlen; if (!resolve_host_port(b->cfg->remote_host, (uint16_t)b->cfg->remote_port, &r, &rlen)) { CLOSESOCK(dt->fd); free(dt); socks5_send_reply(b->cli_bev, 0x04, "0.0.0.0", 0); bridge_free(b); return; } /* 构建 DNS 查询 */ uint8_t q[512]; size_t qlen=0; uint16_t qid=0; if (dns_build_query(host, q, sizeof(q), &qlen, &qid) != 0) { CLOSESOCK(dt->fd); free(dt); socks5_send_reply(b->cli_bev, 0x04, "0.0.0.0", 0); bridge_free(b); return; } dt->id = qid; /* 封装为 SS-UDP:IV + CFB( [UDP addr 8.8.8.8:53] + dns_payload ) */ struct evbuffer *ab = evbuffer_new(); encode_addr_udp(0x01, "8.8.8.8", 53, ab); size_t ablen = evbuffer_get_length(ab); uint8_t *plain = (uint8_t*)malloc(ablen + qlen); evbuffer_copyout(ab, plain, ablen); memcpy(plain + ablen, q, qlen); uint8_t iv[16]; RAND_bytes(iv, sizeof(iv)); uint8_t ct[2048]; int m = aes_cfb_one_shot(b->key32, iv, 1, plain, ablen + qlen, ct); struct iovec { const void *base; size_t len; } iov[2] = {{iv,16}, {ct,(size_t)m}}; uint8_t out[4096]; size_t outlen = 0; memcpy(out+outlen, iov[0].base, iov[0].len); outlen += iov[0].len; memcpy(out+outlen, iov[1].base, iov[1].len); outlen += iov[1].len; sendto(dt->fd, out, outlen, 0, (struct sockaddr*)&r, rlen); dt->ev_read = event_new(b->base, dt->fd, EV_READ|EV_PERSIST, dns_tunnel_on_read, dt); event_add(dt->ev_read, NULL); struct timeval tv = { .tv_sec = b->cfg->connect_timeout, .tv_usec = 0 }; dt->ev_timer = event_new(b->base, -1, 0, dns_tunnel_on_timeout, dt); evtimer_add(dt->ev_timer, &tv); evbuffer_free(ab); free(plain); } /* 处理 UDP ASSOCIATE */ static void handle_udp_associate(bridge_t *b) { b->udp_assoc = udp_assoc_create(b); if (!b->udp_assoc) { socks5_send_reply(b->cli_bev, 0x01, "0.0.0.0", 0); /* general failure */ bridge_free(b); return; } /* 告知客户端我们的 UDP 中继地址 */ struct sockaddr_in sin; socklen_t slen = sizeof(sin); getsockname(b->udp_assoc->fd, (struct sockaddr*)&sin, &slen); char bind_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &sin.sin_addr, bind_ip, sizeof(bind_ip)); uint16_t bind_port = ntohs(sin.sin_port); socks5_send_reply(b->cli_bev, 0x00, bind_ip, bind_port); /* 转入“保持控制连接直至 EOF”的状态 */ b->state = ST_UDP_ASSOC; } /* 客户端读:握手/请求/上行 */ static void on_client_read(struct bufferevent *bev, void *ctx) { bridge_t *b = (bridge_t*)ctx; if (b->state == ST_GREETING) { int r = handle_socks5_greeting(bev); if (r < 0) { bridge_free(b); return; } if (r == 1) b->state = ST_REQUEST; if (r == 0) return; } if (b->state == ST_REQUEST) { size_t consumed = 0; int r = parse_socks5_request(bufferevent_get_input(bev), &b->req, &consumed); if (r < 0) { bridge_free(b); return; } if (r == 0) return; evbuffer_drain(bufferevent_get_input(bev), consumed); if (b->req.cmd == 0x01) { /* CONNECT */ if (b->req.atyp == 0x03 && host_in_domain_list(b->req.host)) { log_msg(LOG_DEBUG, "Host %s in DOMAIN_LISTS, resolve via remote DNS", b->req.host); start_dns_over_tunnel(b, b->req.host); return; } connect_remote(b); } else if (b->req.cmd == 0x03) { /* UDP ASSOCIATE */ handle_udp_associate(b); } else { socks5_send_reply(b->cli_bev, 0x07, "0.0.0.0", 0); bridge_free(b); } return; } if (b->state == ST_STREAM) { /* 上行:明文 -> CFB -> server */ struct evbuffer *in = bufferevent_get_input(b->cli_bev); size_t len = evbuffer_get_length(in); if (!b->up_ready || len == 0) return; while (len) { size_t chunk = len > (size_t)b->cfg->recv_buf ? (size_t)b->cfg->recv_buf : len; unsigned char *p = evbuffer_pullup(in, chunk); if (!p) break; uint8_t *ct = (uint8_t*)malloc(chunk); int m = 0; aes_cfb_update(&b->up_enc, p, chunk, ct, &m); bufferevent_write(b->srv_bev, ct, (size_t)m); evbuffer_drain(in, chunk); free(ct); len -= chunk; } } if (b->state == ST_UDP_ASSOC) { /* 控制连接仅消费数据直到 EOF;这里直接丢弃 */ struct evbuffer *in = bufferevent_get_input(b->cli_bev); size_t len = evbuffer_get_length(in); if (len) evbuffer_drain(in, len); } } /* 服务端读:下行 CFB 解密 -> 客户端 */ static void on_server_read(struct bufferevent *bev, void *ctx) { bridge_t *b = (bridge_t*)ctx; struct evbuffer *in = bufferevent_get_input(b->srv_bev); /* 首次需要 16B IV */ if (!b->down_ready) { size_t len = evbuffer_get_length(in); if (len < 16) return; evbuffer_remove(in, b->iv_server, 16); aes_cfb_init(&b->down_dec, b->key32, b->iv_server, 0); b->down_ready = true; } size_t len = evbuffer_get_length(in); while (len) { size_t chunk = len > (size_t)b->cfg->recv_buf ? (size_t)b->cfg->recv_buf : len; unsigned char *p = evbuffer_pullup(in, chunk); if (!p) break; uint8_t *pt = (uint8_t*)malloc(chunk); int m = 0; aes_cfb_update(&b->down_dec, p, chunk, pt, &m); bufferevent_write(b->cli_bev, pt, (size_t)m); evbuffer_drain(in, chunk); free(pt); len -= chunk; } } /* 事件回调:client/server */ static void on_client_event(struct bufferevent *bev, short events, void *ctx) { bridge_t *b = (bridge_t*)ctx; if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { bridge_free(b); } } static void on_server_event(struct bufferevent *bev, short events, void *ctx) { bridge_t *b = (bridge_t*)ctx; if (events & BEV_EVENT_CONNECTED) { /* 设置 TCP 选项 */ evutil_socket_t fd = bufferevent_getfd(bev); int on = 1, sz = 1<<20; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on)); setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char*)&sz, sizeof(sz)); setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&sz, sizeof(sz)); #ifdef _WIN32 /* 尝试启用 Loopback Fast Path(仅 127.0.0.0/8 有效,失败忽略) */ DWORD bytes = 0, enabled = 1; WSAIoctl(fd, 0x98000010 /* SIO_LOOPBACK_FAST_PATH */, &enabled, sizeof(enabled), NULL, 0, &bytes, NULL, NULL); #endif after_remote_connected(b); return; } if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR|BEV_EVENT_TIMEOUT)) { socks5_send_reply(b->cli_bev, 0x05, "0.0.0.0", 0); bridge_free(b); } } /*----------------------------- 接入监听 ------------------------------*/ static void on_accept(struct evconnlistener *lev, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *ctx) { (void)addr; (void)socklen; config_t *cfg = (config_t*)ctx; bridge_t *b = (bridge_t*)calloc(1, sizeof(*b)); b->base = evconnlistener_get_base(lev); b->cfg = cfg; b->cli_fd = fd; b->cli_bev = bufferevent_socket_new(b->base, fd, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); #ifdef _WIN32 { DWORD bytes=0, enabled=1; WSAIoctl(fd, 0x98000010 /* SIO_LOOPBACK_FAST_PATH */, &enabled, sizeof(enabled), NULL, 0, &bytes, NULL, NULL); } #endif bufferevent_setcb(b->cli_bev, on_client_read, NULL, on_client_event, b); bufferevent_enable(b->cli_bev, EV_READ|EV_WRITE); /* derive AES key */ kdf_evp_bytes_to_key((const uint8_t*)cfg->password, strlen(cfg->password), b->key32); dns_cache_init(&b->dns_cache); b->state = ST_GREETING; } static void on_accept_error(struct evconnlistener *lev, void *ctx) { (void)ctx; int err = EVUTIL_SOCKET_ERROR(); log_msg(LOG_ERROR, "accept error %d(%s)", err, evutil_socket_error_to_string(err)); } /*----------------------------- 参数解析 ------------------------------*/ static void usage(const char *argv0) { fprintf(stderr, "Usage: %s [--remote-host H] [--remote-port P] [--password S]\n" " [--mx STR] [--listen H] [--port P] [--log LEVEL]\n", argv0); } static void parse_args(int argc, char **argv, config_t *cfg) { config_default(cfg); for (int i = 1; i < argc; ++i) { if (!strcmp(argv[i], "--remote-host") && i+1 < argc) { strncpy(cfg->remote_host, argv[++i], sizeof(cfg->remote_host)-1); } else if (!strcmp(argv[i], "--remote-port") && i+1 < argc) { cfg->remote_port = atoi(argv[++i]); } else if (!strcmp(argv[i], "--password") && i+1 < argc) { strncpy(cfg->password, argv[++i], sizeof(cfg->password)-1); } else if (!strcmp(argv[i], "--mx") && i+1 < argc) { strncpy(cfg->mx_head, argv[++i], sizeof(cfg->mx_head)-1); } else if (!strcmp(argv[i], "--listen") && i+1 < argc) { strncpy(cfg->listen_host, argv[++i], sizeof(cfg->listen_host)-1); } else if (!strcmp(argv[i], "--port") && i+1 < argc) { cfg->listen_port = atoi(argv[++i]); } else if (!strcmp(argv[i], "--log") && i+1 < argc) { ++i; if (!strcasecmp(argv[i], "DEBUG")) g_log_level = LOG_DEBUG; else if (!strcasecmp(argv[i], "INFO")) g_log_level = LOG_INFO; else if (!strcasecmp(argv[i], "WARNING") || !strcasecmp(argv[i],"WARN")) g_log_level = LOG_WARN; else if (!strcasecmp(argv[i], "ERROR")) g_log_level = LOG_ERROR; } else { usage(argv[0]); exit(2); } } } /*----------------------------- main ------------------------------*/ int main(int argc, char **argv) { config_t cfg; parse_args(argc, argv, &cfg); struct event_base *base = event_base_new(); if (!base) { fprintf(stderr, "event_base_new failed\n"); return 1; } /* 监听 TCP(SOCKS5) */ struct sockaddr_in sin; memset(&sin,0,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons((uint16_t)cfg.listen_port); inet_pton(AF_INET, cfg.listen_host, &sin.sin_addr); struct evconnlistener *lev = evconnlistener_new_bind(base, on_accept, &cfg, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE|LEV_OPT_REUSEABLE_PORT, -1, (struct sockaddr*)&sin, sizeof(sin)); if (!lev) { fprintf(stderr, "bind listener failed\n"); event_base_free(base); return 1; } evconnlistener_set_error_cb(lev, on_accept_error); log_msg(LOG_INFO, "Listening on %s:%d (SOCKS5)", cfg.listen_host, cfg.listen_port); int rc = event_base_dispatch(base); evconnlistener_free(lev); event_base_free(base); return rc ? 1 : 0; }