1037 lines
33 KiB
C
1037 lines
33 KiB
C
#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 <string.h>
|
|
#include <time.h>
|
|
|
|
#ifdef _WIN32
|
|
#ifndef _WIN32_WINNT
|
|
#define _WIN32_WINNT 0x0A00
|
|
#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)
|
|
#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 8192
|
|
#define DNS_CACHE_SIZE 256
|
|
#define UDP_MAP_CAP 128
|
|
#define DOMAIN_MAX 255
|
|
|
|
static const char *k_domains[] = {"google.com", "youtube.com", "github.com"};
|
|
static const size_t k_domains_cnt = 3;
|
|
|
|
static bool ends_with(const char *s, const char *suffix) {
|
|
size_t ls = strlen(s), lt = strlen(suffix);
|
|
return lt <= ls && strcasecmp(s + (ls - lt), suffix) == 0;
|
|
}
|
|
|
|
static bool host_in_domain_list(const char *host) {
|
|
for (size_t i = 0; i < k_domains_cnt; ++i) {
|
|
if (ends_with(host, k_domains[i])) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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[8];
|
|
snprintf(portstr, sizeof(portstr), "%u", (unsigned)port);
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_family = AF_UNSPEC;
|
|
if (getaddrinfo(host, portstr, &hints, &res) != 0 || !res) {
|
|
if (res) freeaddrinfo(res);
|
|
return false;
|
|
}
|
|
memcpy(ss, res->ai_addr, res->ai_addrlen);
|
|
*sslen = (socklen_t)res->ai_addrlen;
|
|
freeaddrinfo(res);
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void kdf_evp_bytes_to_key(const uint8_t *pw, size_t pwlen, uint8_t out32[32]) {
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static bool encode_addr(uint8_t atyp, const char *host, uint16_t port,
|
|
struct evbuffer *out, bool tcp) {
|
|
uint8_t packed[16]; size_t plen = 0;
|
|
switch (atyp) {
|
|
case 0x01: {
|
|
if (host_literal_ip(host, packed, &plen) != AF_INET) return false;
|
|
evbuffer_add_printf(out, "%c", tcp ? (char)0x61 : (char)0x01);
|
|
evbuffer_add(out, packed, 4);
|
|
break;
|
|
}
|
|
case 0x03: {
|
|
size_t len = strlen(host);
|
|
if (len > 255) return false;
|
|
evbuffer_add_printf(out, "%c", tcp ? (char)0x63 : (char)0x03);
|
|
if (!tcp) evbuffer_add_printf(out, "%c", (char)len);
|
|
else evbuffer_add_printf(out, "%c", (char)len);
|
|
evbuffer_add(out, host, len);
|
|
break;
|
|
}
|
|
case 0x04: {
|
|
if (host_literal_ip(host, packed, &plen) != AF_INET6) return false;
|
|
evbuffer_add_printf(out, "%c", tcp ? (char)0x64 : (char)0x04);
|
|
evbuffer_add(out, packed, 16);
|
|
break;
|
|
}
|
|
default: return false;
|
|
}
|
|
uint16_t nport = htons(port);
|
|
evbuffer_add(out, &nport, 2);
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
uint16_t flags = htons(0x0100);
|
|
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;
|
|
|
|
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;
|
|
*(uint16_t*)(out+off) = htons(1); off+=2;
|
|
*(uint16_t*)(out+off) = htons(1); off+=2;
|
|
|
|
*outlen = off;
|
|
return 0;
|
|
}
|
|
|
|
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) {
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
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;
|
|
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;
|
|
}
|
|
|
|
typedef struct {
|
|
bool used;
|
|
time_t expire;
|
|
char host[64];
|
|
char ip[16];
|
|
} 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[64];
|
|
int remote_port;
|
|
char password[64];
|
|
char mx_head[128];
|
|
char listen_host[16];
|
|
int listen_port;
|
|
int recv_buf;
|
|
int connect_timeout;
|
|
int udp_timeout;
|
|
} 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;
|
|
cfg->connect_timeout = 10;
|
|
cfg->udp_timeout = 180;
|
|
}
|
|
|
|
struct bridge_s;
|
|
typedef struct bridge_s bridge_t;
|
|
|
|
typedef struct {
|
|
bool used;
|
|
time_t ts;
|
|
uint8_t key[128]; 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);
|
|
}
|
|
|
|
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, atyp;
|
|
char host[128];
|
|
uint16_t port;
|
|
} socks5_req_t;
|
|
|
|
struct bridge_s {
|
|
struct event_base *base;
|
|
config_t *cfg;
|
|
struct bufferevent *cli_bev;
|
|
evutil_socket_t cli_fd;
|
|
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_assoc_t *udp_assoc;
|
|
dns_cache_t dns_cache;
|
|
socks5_req_t req;
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
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) {
|
|
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[127]=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 > 127) ? 127 : 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[127]=0;
|
|
off += 16;
|
|
req->port = ntohs(*(uint16_t*)(p + off)); off += 2;
|
|
} else {
|
|
return -3;
|
|
}
|
|
*consumed = off;
|
|
return 1;
|
|
}
|
|
|
|
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<<18;
|
|
setsockopt(ua->fd, SOL_SOCKET, SO_SNDBUF, (const char*)&sz, sizeof(sz));
|
|
setsockopt(ua->fd, SOL_SOCKET, SO_RCVBUF, (const char*)&sz, sizeof(sz));
|
|
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) {
|
|
CLOSESOCK(ua->fd); free(ua); return NULL;
|
|
}
|
|
evutil_make_socket_nonblocking(ua->fd);
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
size_t off = 3;
|
|
if (len < off + 1) return false;
|
|
uint8_t atyp = buf[off++];
|
|
if (atyp == 0x01) {
|
|
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;
|
|
}
|
|
|
|
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 > 128) 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);
|
|
}
|
|
|
|
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[8192];
|
|
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;
|
|
return;
|
|
}
|
|
|
|
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) {
|
|
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[8192];
|
|
int m = aes_cfb_one_shot(ua->key32, iv, 0, ct, ctlen, plain);
|
|
if (m <= 0) continue;
|
|
|
|
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;
|
|
}
|
|
|
|
uint8_t out[8192];
|
|
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 {
|
|
size_t addr_off=0, payload_off=0;
|
|
if (!udp_parse_client_packet(buf, (size_t)n, &addr_off, &payload_off)) continue;
|
|
|
|
uint8_t keybuf[128]; 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;
|
|
|
|
uint8_t iv[16]; RAND_bytes(iv, sizeof(iv));
|
|
size_t plain_len = (size_t)n - 3;
|
|
const uint8_t *plain = buf + 3;
|
|
uint8_t ct[8192];
|
|
int m = aes_cfb_one_shot(ua->key32, iv, 1, plain, plain_len, ct);
|
|
if (m <= 0) continue;
|
|
|
|
uint8_t out[8192];
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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};
|
|
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);
|
|
}
|
|
|
|
static void after_remote_connected(bridge_t *b) {
|
|
struct evbuffer *addr = evbuffer_new();
|
|
if (!encode_addr(b->req.atyp, b->req.host, b->req.port, addr, true)) {
|
|
socks5_send_reply(b->cli_bev, 0x04, "0.0.0.0", 0);
|
|
evbuffer_free(addr); bridge_free(b); return;
|
|
}
|
|
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);
|
|
|
|
bufferevent_write(b->srv_bev, iv, 16);
|
|
bufferevent_write(b->srv_bev, ct, (size_t)m);
|
|
|
|
socks5_send_reply(b->cli_bev, 0x00, "0.0.0.0", 0);
|
|
|
|
free(plain); free(ct); evbuffer_free(addr);
|
|
bridge_start_stream(b);
|
|
}
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
typedef struct {
|
|
bridge_t *b;
|
|
int fd;
|
|
struct event *ev_read;
|
|
struct event *ev_timer;
|
|
uint16_t id;
|
|
char host[64];
|
|
} 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;
|
|
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[2048];
|
|
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; }
|
|
|
|
uint8_t plain[2048];
|
|
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; }
|
|
|
|
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) {
|
|
dns_cache_put(&dt->b->dns_cache, dt->host, ip, 300);
|
|
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;
|
|
}
|
|
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) {
|
|
const char *cached = dns_cache_get(&b->dns_cache, host);
|
|
if (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);
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
struct evbuffer *ab = evbuffer_new();
|
|
encode_addr(0x01, "8.8.8.8", 53, ab, false);
|
|
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[1024]; int m = aes_cfb_one_shot(b->key32, iv, 1, plain, ablen + qlen, ct);
|
|
uint8_t out[1024];
|
|
memcpy(out, iv, 16);
|
|
memcpy(out + 16, ct, m);
|
|
|
|
sendto(dt->fd, out, 16 + m, 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);
|
|
}
|
|
|
|
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);
|
|
bridge_free(b); return;
|
|
}
|
|
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);
|
|
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) {
|
|
if (b->req.atyp == 0x03 && host_in_domain_list(b->req.host)) {
|
|
start_dns_over_tunnel(b, b->req.host);
|
|
return;
|
|
}
|
|
connect_remote(b);
|
|
} else if (b->req.cmd == 0x03) {
|
|
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) {
|
|
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) {
|
|
struct evbuffer *in = bufferevent_get_input(b->cli_bev);
|
|
size_t len = evbuffer_get_length(in);
|
|
if (len) evbuffer_drain(in, len);
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
evutil_socket_t fd = bufferevent_getfd(bev);
|
|
int on = 1, sz = 1<<18;
|
|
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
|
|
DWORD bytes = 0, enabled = 1;
|
|
WSAIoctl(fd, 0x98000010, &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, &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);
|
|
|
|
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; (void)lev;
|
|
}
|
|
|
|
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]);
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
config_t cfg; parse_args(argc, argv, &cfg);
|
|
|
|
struct event_base *base = event_base_new();
|
|
if (!base) return 1;
|
|
|
|
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) { event_base_free(base); return 1; }
|
|
|
|
evconnlistener_set_error_cb(lev, on_accept_error);
|
|
|
|
int rc = event_base_dispatch(base);
|
|
|
|
evconnlistener_free(lev);
|
|
event_base_free(base);
|
|
return rc ? 1 : 0;
|
|
} |