// iocp_s5.c -- single-file, WinSock IOCP + CNG(AES-256-CFB) + DNS-over-tunnel // Build: cl /nologo /O2 /DNDEBUG /MT /utf-8 iocp_s5.c /link /MACHINE:X64 /OPT:REF /OPT:ICF ws2_32.lib mswsock.lib bcrypt.lib #define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include #include #include #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "mswsock.lib") #pragma comment(lib, "bcrypt.lib") //===================== Config ===================== typedef struct { char remote_host[64]; int remote_port; char password[64]; char mx_head[128]; char listen_host[32]; int listen_port; int recv_buf; int connect_timeout_sec; int udp_timeout_sec; int verbose; int dns_on; // 1=enable DNS over tunnel for domain CONNECT } config_t; static void config_default(config_t* c){ memset(c,0,sizeof(*c)); strcpy(c->remote_host, "121.14.152.149"); c->remote_port = 10004; strcpy(c->password, "dwz1GtF7"); strcpy(c->mx_head, "com.win64.oppc.game.common:22021709,102024080020541279"); strcpy(c->listen_host, "0.0.0.0"); c->listen_port = 10807; c->recv_buf = 8192; c->connect_timeout_sec = 10; c->udp_timeout_sec = 180; c->verbose = 1; c->dns_on = 1; } static void parse_args(int argc, char** argv, config_t* c){ config_default(c); for (int i=1;iremote_host, argv[++i], sizeof(c->remote_host)-1); } else if (!strcmp(argv[i],"--remote-port") && i+1remote_port = atoi(argv[++i]); } else if (!strcmp(argv[i],"--password") && i+1password, argv[++i], sizeof(c->password)-1); } else if (!strcmp(argv[i],"--mx") && i+1mx_head, argv[++i], sizeof(c->mx_head)-1); } else if (!strcmp(argv[i],"--listen") && i+1listen_host, argv[++i], sizeof(c->listen_host)-1); } else if (!strcmp(argv[i],"--port") && i+1listen_port = atoi(argv[++i]); } else if (!strcmp(argv[i],"--recv-buf") && i+1recv_buf = atoi(argv[++i]); } else if (!strcmp(argv[i],"--connect-timeout") && i+1connect_timeout_sec = atoi(argv[++i]); } else if (!strcmp(argv[i],"--udp-timeout") && i+1udp_timeout_sec = atoi(argv[++i]); } else if (!strcmp(argv[i],"--verbose") && i+1verbose = atoi(argv[++i]); } else if (!strcmp(argv[i],"--dns-on") && i+1dns_on = atoi(argv[++i]); } } } //===================== Log ===================== static DWORD g_main_tid = 0; static int g_verbose = 1; static uint64_t now_ms(){ FILETIME ft; GetSystemTimeAsFileTime(&ft); ULARGE_INTEGER u; u.LowPart=ft.dwLowDateTime; u.HighPart=ft.dwHighDateTime; return (u.QuadPart/10000ULL); } static void logv(char lv, const char* fmt, ...){ if (lv=='D' && !g_verbose) return; va_list ap; va_start(ap, fmt); SYSTEMTIME st; GetLocalTime(&st); DWORD tid = GetCurrentThreadId(); char buf[1024]; int n = vsnprintf(buf,sizeof(buf),fmt,ap); va_end(ap); if (n<0) n=0; if (n> (int)sizeof(buf)-1) n=(int)sizeof(buf)-1; printf("[%02d:%02d:%02d.%03d][%c][T%u] %s\n", st.wHour,st.wMinute,st.wSecond,st.wMilliseconds, lv, (unsigned)tid, buf); fflush(stdout); } //===================== Helpers ===================== static bool resolve_host_port(const char* host, uint16_t port, struct sockaddr_storage* ss, int* slen){ struct addrinfo hints = {0}, *res=NULL; char pbuf[8]; _snprintf_s(pbuf, sizeof(pbuf), _TRUNCATE, "%u", (unsigned)port); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(host,pbuf,&hints,&res)!=0 || !res){ if (res) freeaddrinfo(res); return false; } memcpy(ss, res->ai_addr, res->ai_addrlen); *slen = (int)res->ai_addrlen; freeaddrinfo(res); return true; } static int host_literal_ip(const char* host, uint8_t* packed16, size_t* plen){ struct in_addr a4; struct in6_addr a6; if (InetPtonA(AF_INET, host, &a4)==1){ memcpy(packed16,&a4,4); *plen=4; return AF_INET; } if (InetPtonA(AF_INET6, host, &a6)==1){ memcpy(packed16,&a6,16); *plen=16; return AF_INET6; } return 0; } //===================== KDF: EVP_BytesToKey(md5) 32B ===================== static bool md5_once(const uint8_t* in, DWORD inlen, uint8_t out16[16]){ BCRYPT_ALG_HANDLE hAlg=NULL; BCRYPT_HASH_HANDLE hHash=NULL; NTSTATUS s=BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_MD5_ALGORITHM, NULL, 0); if (s) return false; s=BCryptCreateHash(hAlg, &hHash, NULL, 0, NULL, 0, 0); if (s){ BCryptCloseAlgorithmProvider(hAlg,0); return false; } s=BCryptHashData(hHash, (PUCHAR)in, inlen, 0); if (s){ BCryptDestroyHash(hHash); BCryptCloseAlgorithmProvider(hAlg,0); return false; } s=BCryptFinishHash(hHash, out16, 16, 0); BCryptDestroyHash(hHash); BCryptCloseAlgorithmProvider(hAlg,0); return s==0; } static bool kdf_key32_from_password(const char* pw, uint8_t out32[32]){ uint8_t h1[16], h2[16]; if (!md5_once((const uint8_t*)pw, (DWORD)strlen(pw), h1)) return false; uint8_t tmp[16+64]; memcpy(tmp,h1,16); memcpy(tmp+16,pw,strlen(pw)); if (!md5_once(tmp, (DWORD)(16+strlen(pw)), h2)) return false; memcpy(out32,h1,16); memcpy(out32+16,h2,16); return true; } //===================== AES-256-CFB via CNG ===================== typedef struct { BCRYPT_ALG_HANDLE hAlg; BCRYPT_KEY_HANDLE hKey; uint8_t iv[16]; // updated in place per call uint8_t key[32]; BOOL is_enc; // 1=encrypt, 0=decrypt BOOL init_ok; } cfb_ctx_t; static bool cfb_init(cfb_ctx_t* c, const uint8_t key[32], const uint8_t iv16[16], BOOL enc){ memset(c,0,sizeof(*c)); memcpy(c->key, key, 32); memcpy(c->iv, iv16, 16); c->is_enc = enc; NTSTATUS s=BCryptOpenAlgorithmProvider(&c->hAlg, BCRYPT_AES_ALGORITHM, NULL, 0); if (s){ logv('E',"BCryptOpenAlgorithmProvider AES failed: 0x%08X", s); return false; } s=BCryptSetProperty(c->hAlg, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_CFB, (ULONG)sizeof(BCRYPT_CHAIN_MODE_CFB), 0); if (s){ logv('E',"Set ChainingModeCFB failed: 0x%08X", s); BCryptCloseAlgorithmProvider(c->hAlg,0); return false; } s=BCryptGenerateSymmetricKey(c->hAlg, &c->hKey, NULL, 0, (PUCHAR)c->key, 32, 0); if (s){ logv('E',"GenerateSymmetricKey failed: 0x%08X", s); BCryptCloseAlgorithmProvider(c->hAlg,0); return false; } c->init_ok = TRUE; return true; } static void cfb_free(cfb_ctx_t* c){ if (c->hKey){ BCryptDestroyKey(c->hKey); c->hKey=NULL; } if (c->hAlg){ BCryptCloseAlgorithmProvider(c->hAlg,0); c->hAlg=NULL; } c->init_ok=FALSE; } // in==out allowed? 为安全起见用不同 buffer(上层已分配) static bool cfb_update(cfb_ctx_t* c, const uint8_t* in, DWORD inlen, uint8_t* out, DWORD* outlen){ if (!c->init_ok) return false; ULONG cb=0; NTSTATUS s; if (c->is_enc) s=BCryptEncrypt(c->hKey,(PUCHAR)in,inlen,NULL,(PUCHAR)c->iv,16,(PUCHAR)out,inlen,&cb,0); else s=BCryptDecrypt(c->hKey,(PUCHAR)in,inlen,NULL,(PUCHAR)c->iv,16,(PUCHAR)out,inlen,&cb,0); if (s){ logv('E',"CFB update failed 0x%08X", s); return false; } *outlen = cb; return true; } //===================== DNS minimal ===================== static int dns_build_query_a(const char* host, uint8_t* out, size_t cap, size_t* outlen){ if (!host || !out || cap<12) return -1; uint16_t id = (uint16_t)rand(); size_t off=0; if (cap<12) return -1; *(uint16_t*)(out+off) = htons(id); off+=2; *(uint16_t*)(out+off) = htons(0x0100); off+=2; // rd *(uint16_t*)(out+off) = htons(1); off+=2; // qdcount *(uint16_t*)(out+off) = 0; off+=2; // an *(uint16_t*)(out+off) = 0; off+=2; // ns *(uint16_t*)(out+off) = 0; off+=2; // ar // qname const char* p=host; while (*p){ const char* dot = strchr(p,'.'); size_t lab = dot ? (size_t)(dot-p) : strlen(p); if (lab==0 || lab>63) return -1; if (off+1+lab>=cap) return -1; out[off++] = (uint8_t)lab; memcpy(out+off, p, lab); off+=lab; if (!dot) break; p = dot+1; } if (off+1+4>cap) return -1; out[off++] = 0; *(uint16_t*)(out+off) = htons(1); off+=2; // QTYPE A *(uint16_t*)(out+off) = htons(1); off+=2; // QCLASS IN *outlen = off; return 0; } static int dns_skip_name(const uint8_t* b,size_t len,size_t* off){ size_t i=*off; while (i=len) return -1; i++; break; } else if (c==0){ break; } else { if (i+c>len) return -1; i+=c; } } *off=i; return 0; } static int dns_parse_a_ip(const uint8_t* b,size_t len,char ip[16]){ if (len<12) return -1; uint16_t qd = ntohs(*(uint16_t*)(b+4)); uint16_t an = ntohs(*(uint16_t*)(b+6)); size_t off=12; for (uint16_t i=0;ilen) return -1; off+=4; } for (uint16_t i=0;ilen) return -1; uint16_t type = ntohs(*(uint16_t*)(b+off)); off+=2; uint16_t cls = ntohs(*(uint16_t*)(b+off)); off+=2; off+=4; // ttl uint16_t rdlen= ntohs(*(uint16_t*)(b+off)); off+=2; if (off+rdlen>len) return -1; if (type==1 && cls==1 && rdlen==4){ const struct in_addr* a4=(const struct in_addr*)(b+off); inet_ntop(AF_INET, a4, ip, 16); return 0; } off+=rdlen; } return -1; } //===================== protocol: encode_addr ===================== // tcp=true : first byte 'a'(0x61)/'c'(0x63)/'d'(0x64) // tcp=false: first byte 0x01/0x03/0x04 static size_t encode_addr_block(bool tcp, uint8_t atyp, const char* host, uint16_t port, uint8_t* out, size_t cap){ uint8_t* p=out; size_t left=cap; uint8_t packed[16]; size_t plen=0; int afip = host_literal_ip(host,packed,&plen); if (tcp){ if (afip==AF_INET && atyp==0x01){ if (left<1+4+2) return 0; *p++=0x61; memcpy(p,packed,4); p+=4; } else if (afip==AF_INET6 && atyp==0x04){ if (left<1+16+2) return 0; *p++=0x64; memcpy(p,packed,16); p+=16; } else if (atyp==0x03){ size_t L=strlen(host); if (L>255) return 0; if (left<1+1+L+2) return 0; *p++=0x63; *p++=(uint8_t)L; memcpy(p,host,L); p+=L; } else return 0; }else{ if (afip==AF_INET && atyp==0x01){ if (left<1+4+2) return 0; *p++=0x01; memcpy(p,packed,4); p+=4; } else if (afip==AF_INET6 && atyp==0x04){ if (left<1+16+2) return 0; *p++=0x04; memcpy(p,packed,16); p+=16; } else if (atyp==0x03){ size_t L=strlen(host); if (L>255) return 0; if (left<1+1+L+2) return 0; *p++=0x03; *p++=(uint8_t)L; memcpy(p,host,L); p+=L; } else return 0; } uint16_t np = htons(port); memcpy(p,&np,2); p+=2; return (size_t)(p - out); } //===================== IOCP Types ===================== typedef enum { OP_NONE=0, OP_CLI_RECV, OP_CLI_SEND, OP_SRV_RECV, OP_SRV_SEND, OP_CONNECT } opkind_t; typedef struct iocp_op_s { OVERLAPPED ov; WSABUF buf; opkind_t kind; char storage[1]; // flexible for debugging } iocp_op_t; static iocp_op_t* op_alloc(opkind_t k, DWORD cap){ iocp_op_t* op = (iocp_op_t*)calloc(1, sizeof(iocp_op_t) + (cap?cap-1:0)); op->kind = k; op->buf.buf = cap? op->storage : NULL; op->buf.len = cap; return op; } static void op_free(iocp_op_t* op){ free(op); } //===================== Bridge ===================== typedef struct { SOCKET s_cli, s_srv; HANDLE iocp; config_t* cfg; // ConnectEx LPFN_CONNECTEX pConnectEx; // state enum { ST_GREETING=0, ST_REQUEST=1, ST_CONNECTING=2, ST_STREAM=3, ST_CLOSED=4 } st; // SOCKS5 request uint8_t req_cmd, req_atyp; char req_host[128]; uint16_t req_port; // crypto uint8_t key32[32]; uint8_t iv_up[16]; // client->server: sent once at beginning cfb_ctx_t c_up; // encryptor (iv_up evolves in place) BOOL up_inited; uint8_t iv_dn[16]; // server->client: received once from remote size_t ivdn_have; // bytes collected for dn iv cfb_ctx_t c_dn; // decryptor BOOL dn_inited; LONG pending_ops; // refcnt-like BOOL closing; // buffers DWORD recv_cap; } bridge_t; static void bridge_start_close(bridge_t* b); //============= small utils ============= static void rand_bytes(uint8_t* p, size_t n){ for (size_t i=0;i127?127:L; memcpy(host,in+off,cp); host[cp]=0; off+=L; *pport = ntohs(*(uint16_t*)(in+off)); off+=2; }else if (*patyp==0x04){ if (inlen255) L=255; *p++=0x03; *p++=(uint8_t)L; memcpy(p,bind_host,L); p+=L; } uint16_t np = htons(bind_port); memcpy(p,&np,2); p+=2; DWORD sent=0; WSASend(s, (WSABUF*)&(WSABUF){ .buf=(CHAR*)out, .len=(ULONG)(p-out) }, 1, &sent, 0, NULL, NULL); } //===================== GQCS loop ===================== typedef struct { HANDLE iocp; config_t* cfg; } worker_arg_t; static void post_recv(bridge_t* b, SOCKET s, opkind_t kind){ iocp_op_t* op=op_alloc(kind, b->recv_cap); InterlockedIncrement(&b->pending_ops); DWORD fl=0; DWORD recvd=0; int rc = WSARecv(s, &op->buf, 1, &recvd, &fl, &op->ov, NULL); if (rc==SOCKET_ERROR){ int e=WSAGetLastError(); if (e!=WSA_IO_PENDING){ logv('E',"WSARecv immediate fail e=%d", e); InterlockedDecrement(&b->pending_ops); op_free(op); bridge_start_close(b); }else{ if (g_verbose) logv('D',"post RECV kind=%d cap=%u (pending_ops=%ld)", kind, op->buf.len, b->pending_ops); } } } static void post_send(bridge_t* b, SOCKET s, const void* data, DWORD len, opkind_t kind){ iocp_op_t* op=op_alloc(kind, len); memcpy(op->buf.buf, data, len); op->buf.len=len; InterlockedIncrement(&b->pending_ops); DWORD sent=0; int rc=WSASend(s, &op->buf, 1, &sent, 0, &op->ov, NULL); if (rc==SOCKET_ERROR){ int e=WSAGetLastError(); if (e!=WSA_IO_PENDING){ logv('E',"WSASend immediate fail e=%d", e); InterlockedDecrement(&b->pending_ops); op_free(op); bridge_start_close(b); }else{ if (g_verbose) logv('D',"post SEND kind=%d len=%u (pending_ops=%ld)", kind, len, b->pending_ops); } } } static void after_remote_connected(bridge_t* b){ // 1) 生成上行IV并初始化加密流 rand_bytes(b->iv_up, 16); if (!cfb_init(&b->c_up, b->key32, b->iv_up, TRUE)){ bridge_start_close(b); return; } b->up_inited=TRUE; // 2) 组首包:encode_addr(tcp=true) + MX uint8_t addr[256]; size_t alen = encode_addr_block(true, b->req_atyp, b->req_host, b->req_port, addr, sizeof(addr)); if (!alen){ logv('E',"encode_addr(tcp) failed"); bridge_start_close(b); return; } size_t mxL = strlen(b->cfg->mx_head); if (mxL>255) mxL=255; uint8_t plain[256+1+255]; memcpy(plain, addr, alen); plain[alen]=(uint8_t)mxL; memcpy(plain+alen+1, b->cfg->mx_head, mxL); DWORD plain_len = (DWORD)(alen + 1 + mxL); // 3) 加密首包(不含 IV) uint8_t ct[512]; DWORD ctlen=0; if (!cfb_update(&b->c_up, plain, plain_len, ct, &ctlen)){ bridge_start_close(b); return; } // 4) 发送:IV + CT uint8_t out[16+512]; memcpy(out, b->iv_up, 16); memcpy(out+16, ct, ctlen); post_send(b, b->s_srv, out, 16+ctlen, OP_SRV_SEND); // 5) 给客户端回复 OK,并进入流转发 s5_reply(b->s_cli, 0x00, "0.0.0.0", 0); b->st = ST_STREAM; // 6) 分别贴上读取 post_recv(b, b->s_cli, OP_CLI_RECV); post_recv(b, b->s_srv, OP_SRV_RECV); logv('I',"enter STREAM (+posted cli/srv recv)"); } //===================== DNS over tunnel (UDP) ===================== static bool dns_over_tunnel_query(const config_t* cfg, const uint8_t key32[32], const char* host, char out_ip[16]){ // 明文: encode_addr(tcp=false) to 8.8.8.8:53 + DNS query uint8_t addr[64]; size_t alen = encode_addr_block(false, 0x01, "8.8.8.8", 53, addr, sizeof(addr)); if (!alen) return false; uint8_t q[512]; size_t qlen=0; if (dns_build_query_a(host, q, sizeof(q), &qlen)!=0) return false; uint8_t plain[800]; if (alen+qlen>sizeof(plain)) return false; memcpy(plain, addr, alen); memcpy(plain+alen, q, qlen); uint8_t iv[16]; rand_bytes(iv,16); cfb_ctx_t enc; if (!cfb_init(&enc, key32, iv, TRUE)) return false; uint8_t ct[800]; DWORD ctlen=0; bool ok = cfb_update(&enc, plain, (DWORD)(alen+qlen), ct, &ctlen); cfb_free(&enc); if (!ok) return false; uint8_t out[16+800]; memcpy(out, iv,16); memcpy(out+16, ct, ctlen); // UDP sendto SOCKET su = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,0,0); if (su==INVALID_SOCKET) return false; struct sockaddr_storage rs; int rslen=0; if (!resolve_host_port(cfg->remote_host, (uint16_t)cfg->remote_port, &rs, &rslen)){ closesocket(su); return false; } DWORD to=2000; setsockopt(su, SOL_SOCKET, SO_RCVTIMEO, (const char*)&to, sizeof(to)); int sn = sendto(su, (const char*)out, (int)(16+ctlen), 0, (struct sockaddr*)&rs, rslen); if (sn<=0){ closesocket(su); return false; } uint8_t buf[1200]; struct sockaddr_storage from; int fromlen=sizeof(from); int rn = recvfrom(su, (char*)buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen); closesocket(su); if (rn<16) return false; cfb_ctx_t dec; if (!cfb_init(&dec, key32, buf, FALSE)) return false; uint8_t plain2[1200]; DWORD p2=0; ok = cfb_update(&dec, buf+16, rn-16, plain2, &p2); cfb_free(&dec); if (!ok || p2==0) return false; // 跳过 addr 块 size_t off=0; uint8_t first = plain2[0]; if (first==0x01){ off = 1+4+2; } else if (first==0x03){ if (p2<2) return false; uint8_t L=plain2[1]; off = 1+1+L+2; if (off>p2) return false; } else if (first==0x04){ off = 1+16+2; } else return false; if (off>=p2) return false; return dns_parse_a_ip(plain2+off, p2-off, out_ip)==0; } //===================== ConnectEx helper ===================== static bool load_connectex(SOCKET s, LPFN_CONNECTEX* pOut){ GUID g = WSAID_CONNECTEX; DWORD bytes=0; int rc = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &g, sizeof(g), pOut, sizeof(*pOut), &bytes, NULL, NULL); return rc==0 && *pOut!=NULL; } static bool bridge_connect_remote(bridge_t* b){ // 建 srv socket & 绑定本地 b->s_srv = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); if (b->s_srv==INVALID_SOCKET){ logv('E',"WSASocket srv fail %d", WSAGetLastError()); return false; } CreateIoCompletionPort((HANDLE)b->s_srv, b->iocp, (ULONG_PTR)b, 0); SOCKADDR_STORAGE local; int llen=0; // 绑定任意本地 struct sockaddr_in sin={0}; sin.sin_family=AF_INET; sin.sin_addr.s_addr=0; sin.sin_port=0; if (bind(b->s_srv, (struct sockaddr*)&sin, sizeof(sin))!=0){ logv('E',"bind srv fail %d", WSAGetLastError()); return false; } if (!load_connectex(b->s_srv, &b->pConnectEx)){ logv('E',"load ConnectEx fail"); return false; } SOCKADDR_STORAGE rs; int rslen=0; if (!resolve_host_port(b->cfg->remote_host, (uint16_t)b->cfg->remote_port, &rs, &rslen)){ logv('E',"resolve remote fail"); return false; } iocp_op_t* op = op_alloc(OP_CONNECT, 0); InterlockedIncrement(&b->pending_ops); BOOL ok = b->pConnectEx(b->s_srv, (SOCKADDR*)&rs, rslen, NULL, 0, NULL, &op->ov); if (!ok){ int e=WSAGetLastError(); if (e!=ERROR_IO_PENDING){ logv('E',"ConnectEx immediate fail %d", e); InterlockedDecrement(&b->pending_ops); op_free(op); return false; } } logv('I',"ConnectEx to remote server %s:%d", b->cfg->remote_host, b->cfg->remote_port); return true; } //===================== Close ===================== static void bridge_free(bridge_t* b){ if (!b) return; if (b->s_cli!=INVALID_SOCKET){ closesocket(b->s_cli); b->s_cli=INVALID_SOCKET; } if (b->s_srv!=INVALID_SOCKET){ closesocket(b->s_srv); b->s_srv=INVALID_SOCKET; } if (b->up_inited){ cfb_free(&b->c_up); b->up_inited=FALSE; } if (b->dn_inited){ cfb_free(&b->c_dn); b->dn_inited=FALSE; } free(b); } static void bridge_start_close(bridge_t* b){ if (b->closing) return; b->closing=TRUE; logv('I',"bridge %p start_close (pending_ops=%ld)", b, b->pending_ops); shutdown(b->s_srv, SD_BOTH); shutdown(b->s_cli, SD_BOTH); } //===================== Worker (GQCS) ===================== static void handle_cli_data(bridge_t* b, const uint8_t* data, DWORD n){ if (b->st!=ST_STREAM) return; if (!b->up_inited){ logv('E',"up cipher not ready"); bridge_start_close(b); return; } // 加密并发给远端(不再发送IV) uint8_t* out = (uint8_t*)malloc(n); DWORD m=0; if (!cfb_update(&b->c_up, data, n, out, &m)){ free(out); bridge_start_close(b); return; } if (g_verbose) logv('D',"STREAM up: in=%u enc=%u", n, m); post_send(b, b->s_srv, out, m, OP_SRV_SEND); free(out); } static void handle_srv_data(bridge_t* b, uint8_t* data, DWORD n){ DWORD off=0; // 下行首次收到时,先收满 16B IV if (!b->dn_inited){ DWORD need = (DWORD)(16 - b->ivdn_have); if (n < need){ memcpy(b->iv_dn + b->ivdn_have, data, n); b->ivdn_have += n; return; }else{ // 完成 IV 收集 memcpy(b->iv_dn + b->ivdn_have, data, need); b->ivdn_have = 16; off = need; if (!cfb_init(&b->c_dn, b->key32, b->iv_dn, FALSE)){ bridge_start_close(b); return; } b->dn_inited=TRUE; logv('I',"downstream IV received"); } } if (off < n){ uint8_t* out=(uint8_t*)malloc(n - off); DWORD m=0; if (!cfb_update(&b->c_dn, data+off, n-off, out, &m)){ free(out); bridge_start_close(b); return; } if (g_verbose) logv('D',"STREAM down: in=%u dec=%u", n-off, m); post_send(b, b->s_cli, out, m, OP_CLI_SEND); free(out); } } static DWORD WINAPI worker_thread(LPVOID arg_){ worker_arg_t* arg=(worker_arg_t*)arg_; HANDLE iocp=arg->iocp; for (;;){ DWORD bytes=0; ULONG_PTR key=0; OVERLAPPED* pov=NULL; BOOL ok = GetQueuedCompletionStatus(iocp, &bytes, &key, &pov, INFINITE); bridge_t* b=(bridge_t*)key; iocp_op_t* op = (iocp_op_t*)pov; if (!pov){ // shutdown signal? break; } if (!ok){ DWORD gle=GetLastError(); // 某些取消/超时也会走这里 // 统一当作该 op 完成,交给状态机关闭 } if (op->kind==OP_CLI_RECV){ if (g_verbose) logv('D',"GQCS: CLI_RECV %u bytes", bytes); if (bytes==0){ // 客户端关闭 InterlockedDecrement(&b->pending_ops); op_free(op); bridge_start_close(b); continue; } // 状态机:GREETING 或 REQUEST 或 STREAM if (b->st==ST_GREETING){ DWORD need=0; if (!s5_greeting((uint8_t*)op->buf.buf, bytes, &need)){ // 不足,理论上应缓存;这里简单化,认为一次收齐(curl等会在一个包里) if (bytes<2){ // 再贴收 post_recv(b, b->s_cli, OP_CLI_RECV); InterlockedDecrement(&b->pending_ops); op_free(op); continue; } logv('E',"S5 greeting malformed"); InterlockedDecrement(&b->pending_ops); op_free(op); bridge_start_close(b); continue; } // 丢掉 greeting,并回 05 00 uint8_t ok2[2]={0x05,0x00}; post_send(b, b->s_cli, ok2, 2, OP_CLI_SEND); if (g_verbose) logv('I',"S5 greeting ok"); b->st = ST_REQUEST; // 再收 request post_recv(b, b->s_cli, OP_CLI_RECV); InterlockedDecrement(&b->pending_ops); op_free(op); continue; }else if (b->st==ST_REQUEST){ uint8_t cmd,atyp; char host[128]; uint16_t port=0; DWORD used=0; int r = s5_parse_request((uint8_t*)op->buf.buf, bytes, &cmd,&atyp,host,&port,&used); if (r<=0){ // 继续收 post_recv(b, b->s_cli, OP_CLI_RECV); InterlockedDecrement(&b->pending_ops); op_free(op); continue; } b->req_cmd=cmd; b->req_atyp=atyp; strncpy(b->req_host,host,127); b->req_port=port; if (g_verbose) logv('I',"S5 request cmd=0x%02X atyp=0x%02X host=%s port=%u", cmd,atyp,host,port); if (cmd==0x01){ // CONNECT // 先派生密钥 if (!kdf_key32_from_password(b->cfg->password, b->key32)){ logv('E',"KDF failed"); bridge_start_close(b); InterlockedDecrement(&b->pending_ops); op_free(op); continue; } // 如果是域名且启用 DNS over tunnel,先尝试查询 A if (b->cfg->dns_on && atyp==0x03){ char ip[16]={0}; if (dns_over_tunnel_query(b->cfg, b->key32, host, ip)){ strncpy(b->req_host, ip, 127); b->req_atyp = 0x01; logv('I',"DOT result %s -> %s", host, ip); }else{ logv('W',"DOT fail, fallback to remote resolve (keep domain)"); } } // 连接远端中继 if (!bridge_connect_remote(b)){ s5_reply(b->s_cli, 0x05, "0.0.0.0", 0); bridge_start_close(b); InterlockedDecrement(&b->pending_ops); op_free(op); continue; } b->st = ST_CONNECTING; }else if (cmd==0x03){ // UDP ASSOC(此版本先不实现 SOCKS UDP 转发,只回 OK,防吞包) s5_reply(b->s_cli, 0x00, "0.0.0.0", 0); }else{ s5_reply(b->s_cli, 0x07, "0.0.0.0", 0); bridge_start_close(b); } InterlockedDecrement(&b->pending_ops); op_free(op); continue; }else if (b->st==ST_STREAM){ handle_cli_data(b, (uint8_t*)op->buf.buf, bytes); // 继续收客户端 post_recv(b, b->s_cli, OP_CLI_RECV); InterlockedDecrement(&b->pending_ops); op_free(op); continue; } // 其它状态 InterlockedDecrement(&b->pending_ops); op_free(op); }else if (op->kind==OP_SRV_RECV){ if (g_verbose) logv('D',"GQCS: SRV_RECV %u bytes", bytes); if (bytes==0){ InterlockedDecrement(&b->pending_ops); op_free(op); bridge_start_close(b); continue; } handle_srv_data(b, (uint8_t*)op->buf.buf, bytes); // 继续收远端 post_recv(b, b->s_srv, OP_SRV_RECV); InterlockedDecrement(&b->pending_ops); op_free(op); }else if (op->kind==OP_CLI_SEND || op->kind==OP_SRV_SEND){ if (g_verbose) logv('D',"GQCS: %s %u bytes", op->kind==OP_CLI_SEND?"CLI_SEND":"SRV_SEND", bytes); InterlockedDecrement(&b->pending_ops); op_free(op); }else if (op->kind==OP_CONNECT){ DWORD gle = 0; BOOL ok2 = WSAGetOverlappedResult(b->s_srv, &op->ov, &bytes, FALSE, &gle); if (!ok2){ logv('E',"ConnectEx complete err gle=%u", gle); s5_reply(b->s_cli, 0x05, "0.0.0.0", 0); InterlockedDecrement(&b->pending_ops); op_free(op); bridge_start_close(b); continue; } // 必须调用 setsockopt/更新为连接态 setsockopt(b->s_srv, IPPROTO_TCP, TCP_NODELAY, (const char*)&(int){1}, sizeof(int)); // 将 socket 转换为连接态(微软文档:ConnectEx 完成后需调用 setsockopt 或 WSAIoctl SIO_KEEPALIVE_VALS/或者调用 getsockname 等) struct sockaddr_in name; int namelen=sizeof(name); getsockname(b->s_srv,(struct sockaddr*)&name,&namelen); if (g_verbose) logv('I',"ConnectEx completed ok=1 gle=0"); InterlockedDecrement(&b->pending_ops); op_free(op); after_remote_connected(b); } // 统一关闭判定 if (b->closing && InterlockedCompareExchange(&b->pending_ops,0,0)==0){ logv('I',"bridge %p free", b); bridge_free(b); } } return 0; } //===================== Accept loop ===================== static DWORD WINAPI accept_loop(LPVOID arg_){ worker_arg_t* wa=(worker_arg_t*)arg_; config_t* cfg = wa->cfg; SOCKET ls = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL,0, WSA_FLAG_OVERLAPPED); if (ls==INVALID_SOCKET){ logv('E',"listen socket fail %d", WSAGetLastError()); return 1; } struct sockaddr_in sin={0}; sin.sin_family=AF_INET; sin.sin_port=htons((u_short)cfg->listen_port); InetPtonA(AF_INET, cfg->listen_host, &sin.sin_addr); if (bind(ls,(struct sockaddr*)&sin,sizeof(sin))!=0){ logv('E',"bind fail %d", WSAGetLastError()); return 1; } if (listen(ls, SOMAXCONN)!=0){ logv('E',"listen fail %d", WSAGetLastError()); return 1; } logv('I',"ready."); for (;;){ struct sockaddr_storage cs; int clen=sizeof(cs); SOCKET s = accept(ls, (struct sockaddr*)&cs, &clen); if (s==INVALID_SOCKET){ int e=WSAGetLastError(); if (e==WSAEINTR) break; continue; } char ipbuf[64]; uint16_t cport=0; if (cs.ss_family==AF_INET){ struct sockaddr_in* a=(struct sockaddr_in*)&cs; inet_ntop(AF_INET, &a->sin_addr, ipbuf, sizeof(ipbuf)); cport=ntohs(a->sin_port); }else{ strcpy(ipbuf,"::1"); cport=0; } logv('I',"accepted socket=%llu from %s:%u", (unsigned long long)s, ipbuf, cport); // 建 bridge bridge_t* b=(bridge_t*)calloc(1,sizeof(bridge_t)); b->s_cli = s; b->s_srv=INVALID_SOCKET; b->cfg = cfg; b->recv_cap = (DWORD)cfg->recv_buf; b->st=ST_GREETING; b->iocp = wa->iocp; CreateIoCompletionPort((HANDLE)b->s_cli, wa->iocp, (ULONG_PTR)b, 0); // 贴收客户端 post_recv(b, b->s_cli, OP_CLI_RECV); } return 0; } //===================== main ===================== int main(int argc, char** argv){ config_t cfg; parse_args(argc,argv,&cfg); g_verbose = cfg.verbose; srand((unsigned)time(NULL)); g_main_tid = GetCurrentThreadId(); WSADATA w; if (WSAStartup(MAKEWORD(2,2), &w)!=0){ return 1; } HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); logv('I',"=== mx iocp socks5 ==="); logv('I',"listen %s:%d remote %s:%d verbose=%d", cfg.listen_host, cfg.listen_port, cfg.remote_host, cfg.remote_port, cfg.verbose); logv('I',"udp_timeout=%ds connect_timeout=%ds", cfg.udp_timeout_sec, cfg.connect_timeout_sec); worker_arg_t wa = { .iocp=iocp, .cfg=&cfg }; // workers const int N=3; HANDLE th[N]; for (int i=0;i