Transparent proxy mode (#114)

* Transparent proxy

* Typo errors

* long long -> intmax_t for printing time_t

---------

Co-authored-by: vel21ripn <>
Co-authored-by: Konstantin Saliy <ksaliy@s-terra.ru>
This commit is contained in:
Lurker00 2024-09-10 21:10:11 +03:00 committed by GitHub
parent 1d6c0a132e
commit 02a9e046fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 67 additions and 6 deletions

View File

@ -85,7 +85,7 @@ int mode_add_get(struct sockaddr_ina *dst, int m)
} }
time(&t); time(&t);
if (t > val->time + params.cache_ttl) { 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 0;
} }
return val->m; return val->m;

12
main.c
View File

@ -61,6 +61,9 @@ struct params params = {
const char help_text[] = { const char help_text[] = {
" -i, --ip, <ip> Listening IP, default 0.0.0.0\n" " -i, --ip, <ip> Listening IP, default 0.0.0.0\n"
" -p, --port <num> Listening port, default 1080\n" " -p, --port <num> Listening port, default 1080\n"
#ifdef __linux__
" -E, --transparent Transparent proxy mode\n"
#endif
" -c, --max-conn <count> Connection count limit, default 512\n" " -c, --max-conn <count> Connection count limit, default 512\n"
" -N, --no-domain Deny domain resolving\n" " -N, --no-domain Deny domain resolving\n"
" -U, --no-udp Deny UDP association\n" " -U, --no-udp Deny UDP association\n"
@ -116,6 +119,9 @@ const struct option options[] = {
{"version", 0, 0, 'v'}, {"version", 0, 0, 'v'},
{"ip", 1, 0, 'i'}, {"ip", 1, 0, 'i'},
{"port", 1, 0, 'p'}, {"port", 1, 0, 'p'},
#ifdef __linux__
{"transparent", 0, 0, 'E'},
#endif
{"conn-ip", 1, 0, 'I'}, {"conn-ip", 1, 0, 'I'},
{"buf-size", 1, 0, 'b'}, {"buf-size", 1, 0, 'b'},
{"max-conn", 1, 0, 'c'}, {"max-conn", 1, 0, 'c'},
@ -492,6 +498,11 @@ int main(int argc, char **argv)
case 'U': case 'U':
params.udp = 0; params.udp = 0;
break; break;
#ifdef __linux__
case 'E':
params.transparent = 1;
break;
#endif
case 'h': case 'h':
printf(help_text); printf(help_text);
@ -868,6 +879,7 @@ int main(int argc, char **argv)
clear_params(); clear_params();
return -1; return -1;
} }
int status = run((struct sockaddr_ina *)&params.laddr); int status = run((struct sockaddr_ina *)&params.laddr);
clear_params(); clear_params();
return status; return status;

View File

@ -105,6 +105,7 @@ struct params {
size_t bfsize; size_t bfsize;
struct sockaddr_in6 baddr; struct sockaddr_in6 baddr;
struct sockaddr_in6 laddr; struct sockaddr_in6 laddr;
char transparent;
struct mphdr *mempool; struct mphdr *mempool;
char *protect_path; char *protect_path;

54
proxy.c
View File

@ -32,6 +32,10 @@
#if defined(__linux__) && defined(__GLIBC__) #if defined(__linux__) && defined(__GLIBC__)
extern int accept4(int, struct sockaddr *__restrict, socklen_t *__restrict, int); extern int accept4(int, struct sockaddr *__restrict, socklen_t *__restrict, int);
#endif #endif
#ifdef __linux__
/* For SO_ORIGINAL_DST only (which is 0x50) */
#include "linux/netfilter_ipv4.h"
#endif
#endif #endif
@ -197,6 +201,17 @@ int resp_error(int fd, int e, int flag)
} }
return resp_s5_error(fd, e); 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; return 0;
} }
@ -242,7 +257,7 @@ int s5_get_addr(char *buffer, size_t n,
struct sockaddr_ina *addr, int type) struct sockaddr_ina *addr, int type)
{ {
if (n < S_SIZE_MIN) { 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; return -S_ER_GEN;
} }
struct s5_req *r = (struct s5_req *)buffer; struct s5_req *r = (struct s5_req *)buffer;
@ -519,6 +534,34 @@ int udp_associate(struct poolhd *pool,
return 0; 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) 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; continue;
} }
rval->in6 = client.in6; rval->in6 = client.in6;
#ifdef __linux__
if (params.transparent && transp_conn(pool, rval) < 0) {
close(c);
continue;
}
#endif
} }
return 0; return 0;
} }
@ -753,7 +802,7 @@ static inline int on_request(struct poolhd *pool, struct eval *val,
return 0; return 0;
} }
if (n < S_SIZE_MIN) { 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; return -1;
} }
struct s5_req *r = (struct s5_req *)buffer; struct s5_req *r = (struct s5_req *)buffer;
@ -982,4 +1031,3 @@ int run(struct sockaddr_ina *srv)
} }
return event_loop(fd); return event_loop(fd);
} }