1268 lines
42 KiB
C
1268 lines
42 KiB
C
/*
|
||
* 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 <event2/buffer.h>
|
||
#include <event2/bufferevent.h>
|
||
#include <event2/event.h>
|
||
#include <event2/listener.h>
|
||
#include <event2/util.h>
|
||
#include <inttypes.h>
|
||
#include <openssl/evp.h>
|
||
#include <openssl/md5.h>
|
||
#include <openssl/rand.h>
|
||
#include <stdbool.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <stdarg.h>
|
||
#include <string.h>
|
||
#include <time.h>
|
||
|
||
#ifdef _WIN32
|
||
#ifndef _WIN32_WINNT
|
||
#define _WIN32_WINNT 0x0A00 /* Win10 */
|
||
#endif
|
||
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||
#include <winsock2.h>
|
||
#include <ws2tcpip.h>
|
||
#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)
|
||
/* 没有 <arpa/inet.h>/<netinet/tcp.h>,选项值由 winsock 提供 */
|
||
#else
|
||
#include <arpa/inet.h>
|
||
#include <errno.h>
|
||
#include <netinet/in.h>
|
||
#include <netinet/tcp.h>
|
||
#include <sys/socket.h>
|
||
#include <sys/types.h>
|
||
#include <unistd.h>
|
||
#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 请求头,返回 <addr_block> 与 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;
|
||
}
|
||
|
||
/* 将 <ATYP|ADDR|PORT> 规范化编码回自己,便于作为 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;
|
||
}
|