diff --git a/extend.c b/extend.c index ee07b2c..a9395bb 100644 --- a/extend.c +++ b/extend.c @@ -85,7 +85,7 @@ int mode_add_get(struct sockaddr_ina *dst, int m) } time(&t); if (t > val->time + params.cache_ttl) { - LOG(LOG_S, "time=%ld, now=%ld, ignore\n", val->time, t); + LOG(LOG_S, "time=%jd, now=%jd, ignore\n", (intmax_t)val->time, (intmax_t)t); return 0; } return val->m; diff --git a/main.c b/main.c index 7e95145..a299b73 100644 --- a/main.c +++ b/main.c @@ -61,6 +61,9 @@ struct params params = { const char help_text[] = { " -i, --ip, Listening IP, default 0.0.0.0\n" " -p, --port Listening port, default 1080\n" + #ifdef __linux__ + " -E, --transparent Transparent proxy mode\n" + #endif " -c, --max-conn Connection count limit, default 512\n" " -N, --no-domain Deny domain resolving\n" " -U, --no-udp Deny UDP association\n" @@ -116,6 +119,9 @@ const struct option options[] = { {"version", 0, 0, 'v'}, {"ip", 1, 0, 'i'}, {"port", 1, 0, 'p'}, + #ifdef __linux__ + {"transparent", 0, 0, 'E'}, + #endif {"conn-ip", 1, 0, 'I'}, {"buf-size", 1, 0, 'b'}, {"max-conn", 1, 0, 'c'}, @@ -492,6 +498,11 @@ int main(int argc, char **argv) case 'U': params.udp = 0; break; + #ifdef __linux__ + case 'E': + params.transparent = 1; + break; + #endif case 'h': printf(help_text); @@ -868,6 +879,7 @@ int main(int argc, char **argv) clear_params(); return -1; } + int status = run((struct sockaddr_ina *)¶ms.laddr); clear_params(); return status; diff --git a/params.h b/params.h index d7b03e1..13a27fc 100644 --- a/params.h +++ b/params.h @@ -105,6 +105,7 @@ struct params { size_t bfsize; struct sockaddr_in6 baddr; struct sockaddr_in6 laddr; + char transparent; struct mphdr *mempool; char *protect_path; diff --git a/proxy.c b/proxy.c index d01e924..42a876d 100644 --- a/proxy.c +++ b/proxy.c @@ -32,9 +32,13 @@ #if defined(__linux__) && defined(__GLIBC__) extern int accept4(int, struct sockaddr *__restrict, socklen_t *__restrict, int); #endif + #ifdef __linux__ + /* For SO_ORIGINAL_DST only (which is 0x50) */ + #include "linux/netfilter_ipv4.h" + #endif #endif - + int NOT_EXIT = 1; static void on_cancel(int sig) { @@ -197,6 +201,17 @@ int resp_error(int fd, int e, int flag) } return resp_s5_error(fd, e); } + #ifdef __linux__ + if (params.transparent && + (e == ECONNREFUSED || e == ETIMEDOUT)) { + struct linger l = { .l_onoff = 1 }; + if (setsockopt(fd, + SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) { + uniperror("setsockopt SO_LINGER"); + return -1; + } + } + #endif return 0; } @@ -242,7 +257,7 @@ int s5_get_addr(char *buffer, size_t n, struct sockaddr_ina *addr, int type) { if (n < S_SIZE_MIN) { - LOG(LOG_E, "ss: request to small\n"); + LOG(LOG_E, "ss: request too small\n"); return -S_ER_GEN; } struct s5_req *r = (struct s5_req *)buffer; @@ -519,6 +534,34 @@ int udp_associate(struct poolhd *pool, return 0; } +#ifdef __linux__ +static inline int transp_conn(struct poolhd *pool, struct eval *val) +{ + struct sockaddr_ina remote, self; + socklen_t rlen = sizeof(remote), slen = sizeof(self); + if (getsockopt(val->fd, + IPPROTO_IP, SO_ORIGINAL_DST, &remote, &rlen) != 0) { + uniperror("getsockopt SO_ORIGINAL_DST"); + return -1; + } + if (getsockname(val->fd, &self.sa, &slen) < 0) { + uniperror("getsockname"); + return -1; + } + if (self.sa.sa_family == remote.sa.sa_family && + self.in.sin_port == remote.in.sin_port && + addr_equ(&self, &remote)) { + LOG(LOG_E, "connect to self, ignore\n"); + return -1; + } + int error = connect_hook(pool, val, &remote, EV_CONNECT); + if (error) { + uniperror("connect_hook"); + return -1; + } + return 0; +} +#endif static inline int on_accept(struct poolhd *pool, struct eval *val) { @@ -565,6 +608,12 @@ static inline int on_accept(struct poolhd *pool, struct eval *val) continue; } rval->in6 = client.in6; + #ifdef __linux__ + if (params.transparent && transp_conn(pool, rval) < 0) { + close(c); + continue; + } + #endif } return 0; } @@ -753,7 +802,7 @@ static inline int on_request(struct poolhd *pool, struct eval *val, return 0; } if (n < S_SIZE_MIN) { - LOG(LOG_E, "ss: request to small (%zd)\n", n); + LOG(LOG_E, "ss: request too small (%zd)\n", n); return -1; } struct s5_req *r = (struct s5_req *)buffer; @@ -844,7 +893,7 @@ void close_conn(struct poolhd *pool, struct eval *val) } -int event_loop(int srvfd) +int event_loop(int srvfd) { size_t bfsize = params.bfsize; @@ -982,4 +1031,3 @@ int run(struct sockaddr_ina *srv) } return event_loop(fd); } -