From 43f3923e2507a95d17adb556d2d64d2aae2f62f5 Mon Sep 17 00:00:00 2001 From: kitty Date: Sat, 12 Jan 2013 17:00:58 +0800 Subject: [PATCH 001/192] Committer: Zhuofei Wang Changes: 1. Modified registration of SIGUSR1 to prevent segment fault on MIPS openwrt. 2. Introduced mechanism of self monitor to recover from hang process. 3. Introduced a new type of method 'direct' which relays incoming traffic out directly by default route and default interface. This method is useful to users whose ISP limits network connections from different computers. 4. Introduced a new type of method 'autosocks5' which by default relays incoming traffic out directly by default route and default interface. In case relay connection is closed/reset immediately by remote/GFW, redsocks then relays the traffic to SOCKS5 proxy. By doing this, most of traffic can be relayed without going through proxy. But, for those connections blocked by GFW can go through proxy automatically. --- autosocks.c | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100644 autosocks.c diff --git a/autosocks.c b/autosocks.c new file mode 100644 index 00000000..98bfbb46 --- /dev/null +++ b/autosocks.c @@ -0,0 +1,463 @@ +/* redsocks - transparent TCP-to-proxy redirector + * Copyright (C) 2007-2011 Leonid Evdokimov + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include "utils.h" +#include "log.h" +#include "redsocks.h" +#include "socks5.h" + +typedef enum socks5_state_t { + socks5_pre_detect, + socks5_direct, + socks5_new, + socks5_method_sent, + socks5_auth_sent, + socks5_request_sent, + socks5_skip_domain, + socks5_skip_address, + socks5_MAX, +} socks5_state; + +typedef struct socks5_client_t { + int do_password; // 1 - password authentication is possible + int to_skip; // valid while reading last reply (after main request) + time_t time_connect_relay; // timestamp when start to connect relay +} socks5_client; + +static const char *socks5_strstatus[] = { + "ok", + "server failure", + "connection not allowed by ruleset", + "network unreachable", + "host unreachable", + "connection refused", + "TTL expired", + "command not supported", + "address type not supported", +}; +static const size_t socks5_strstatus_len = SIZEOF_ARRAY(socks5_strstatus); + +const char* socks5_status_to_str(int socks5_status); + +int socks5_is_valid_cred(const char *login, const char *password); + +static int auto_retry_or_drop(redsocks_client * client); + +static void auto_connect_relay(redsocks_client *client); + +#define ADDR_CACHE_SIZE 64 +static struct sockaddr_in addr_cache[ADDR_CACHE_SIZE]; +static int addr_count = 0; +static int first_addr = 0; +static int cache_init = 0; + +static void init_addr_cache() +{ + if (!cache_init) + { + memset((void *)addr_cache, 0, sizeof(struct sockaddr_in)*ADDR_CACHE_SIZE); + addr_count = 0; + first_addr = 0; + cache_init = 1; + } +} + +static int is_addr_in_cache(const struct sockaddr_in * addr) +{ + int i = 0; + /* do reverse search for efficency */ + for ( i = addr_count - 1; i >= 0; i -- ) + if (0 == memcmp((void *)addr, (void *)&addr_cache[(first_addr+i)%ADDR_CACHE_SIZE], sizeof(struct sockaddr_in))) + return 1; + + return 0; +} + +static void add_addr_to_cache(const struct sockaddr_in * addr) +{ + if (addr_count < ADDR_CACHE_SIZE) + { + memcpy((void *)&addr_cache[addr_count], (void *) addr, sizeof(struct sockaddr_in)); + addr_count ++; + } + else + { + memcpy((void *)&addr_cache[first_addr], (void *) addr, sizeof(struct sockaddr_in)); + first_addr ++; + first_addr %= ADDR_CACHE_SIZE; + } +} + + +void auto_socks5_client_init(redsocks_client *client) +{ + socks5_client *socks5 = (void*)(client + 1); + const redsocks_config *config = &client->instance->config; + + client->state = socks5_pre_detect; + socks5->do_password = socks5_is_valid_cred(config->login, config->password); + init_addr_cache(); +} + +static struct evbuffer *socks5_mkmethods(redsocks_client *client) +{ + socks5_client *socks5 = (void*)(client + 1); + return socks5_mkmethods_plain(socks5->do_password); +} + +struct evbuffer *socks5_mkmethods_plain(int do_password); + +static struct evbuffer *socks5_mkpassword(redsocks_client *client) +{ + return socks5_mkpassword_plain(client->instance->config.login, client->instance->config.password); +} + +struct evbuffer *socks5_mkpassword_plain(const char *login, const char *password); +struct evbuffer *socks5_mkcommand_plain(int socks5_cmd, const struct sockaddr_in *destaddr); + +static struct evbuffer *socks5_mkconnect(redsocks_client *client) +{ + return socks5_mkcommand_plain(socks5_cmd_connect, &client->destaddr); +} + +static void socks5_write_cb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + + redsocks_touch_client(client); + + if (client->state == socks5_pre_detect) { + client->state = socks5_direct; + +/* + if (EVBUFFER_LENGTH(buffev->input) == 0 && client->relay_evshut & EV_READ) + { + if (auto_retry_or_drop(client)) + redsocks_drop_client(client); + } + else +*/ + redsocks_start_relay(client); +/* + if (bufferevent_enable(client->relay, EV_READ | EV_WRITE)) + redsocks_drop_client(client); +*/ + } +/* + else if (client->state == socks5_direct) + redsocks_start_relay(client); +*/ + else if (client->state == socks5_new) { + redsocks_write_helper( + buffev, client, + socks5_mkmethods, socks5_method_sent, sizeof(socks5_method_reply) + ); + } +} + +const char* socks5_is_known_auth_method(socks5_method_reply *reply, int do_password); + + +static void socks5_read_auth_methods(struct bufferevent *buffev, redsocks_client *client, socks5_client *socks5) +{ + socks5_method_reply reply; + const char *error = NULL; + + if (redsocks_read_expected(client, buffev->input, &reply, sizes_equal, sizeof(reply)) < 0) + return; + + error = socks5_is_known_auth_method(&reply, socks5->do_password); + if (error) { + redsocks_log_error(client, LOG_NOTICE, "socks5_is_known_auth_method: %s", error); + redsocks_drop_client(client); + } + else if (reply.method == socks5_auth_none) { + redsocks_write_helper( + buffev, client, + socks5_mkconnect, socks5_request_sent, sizeof(socks5_reply) + ); + } + else if (reply.method == socks5_auth_password) { + redsocks_write_helper( + buffev, client, + socks5_mkpassword, socks5_auth_sent, sizeof(socks5_auth_reply) + ); + } +} + +static void socks5_read_auth_reply(struct bufferevent *buffev, redsocks_client *client, socks5_client *socks5) +{ + socks5_auth_reply reply; + + if (redsocks_read_expected(client, buffev->input, &reply, sizes_equal, sizeof(reply)) < 0) + return; + + if (reply.ver != socks5_password_ver) { + redsocks_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected auth reply version..."); + redsocks_drop_client(client); + } + else if (reply.status == socks5_password_passed) + redsocks_write_helper( + buffev, client, + socks5_mkconnect, socks5_request_sent, sizeof(socks5_reply) + ); + else + redsocks_drop_client(client); +} + +static void socks5_read_reply(struct bufferevent *buffev, redsocks_client *client, socks5_client *socks5) +{ + socks5_reply reply; + + if (redsocks_read_expected(client, buffev->input, &reply, sizes_greater_equal, sizeof(reply)) < 0) + return; + + if (reply.ver != socks5_ver) { + redsocks_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected reply version..."); + redsocks_drop_client(client); + } + else if (reply.status == socks5_status_succeeded) { + socks5_state nextstate; + size_t len; + + if (reply.addrtype == socks5_addrtype_ipv4) { + len = socks5->to_skip = sizeof(socks5_addr_ipv4); + nextstate = socks5_skip_address; + } + else if (reply.addrtype == socks5_addrtype_ipv6) { + len = socks5->to_skip = sizeof(socks5_addr_ipv6); + nextstate = socks5_skip_address; + } + else if (reply.addrtype == socks5_addrtype_domain) { + socks5_addr_domain domain; + len = sizeof(domain.size); + nextstate = socks5_skip_domain; + } + else { + redsocks_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected address type..."); + redsocks_drop_client(client); + return; + } + + redsocks_write_helper( + buffev, client, + NULL, nextstate, len + ); + } + else { + redsocks_log_error(client, LOG_NOTICE, "Socks5 server status: %s (%i)", + /* 0 <= reply.status && */ reply.status < SIZEOF_ARRAY(socks5_strstatus) + ? socks5_strstatus[reply.status] : "?", reply.status); + redsocks_drop_client(client); + } +} + +static void socks5_read_cb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + socks5_client *socks5 = (void*)(client + 1); + + redsocks_touch_client(client); + + if (client->state == socks5_pre_detect) { + /* Should never be here */ +/* + client->state = socks5_direct; + redsocks_start_relay(client); +*/ + } + else if (client->state == socks5_direct) { + /* if (EVBUFFER_LENGTH(buffev->input) == 0 && client->relay_evshut & EV_READ) */ + } + else if (client->state == socks5_method_sent) { + socks5_read_auth_methods(buffev, client, socks5); + } + else if (client->state == socks5_auth_sent) { + socks5_read_auth_reply(buffev, client, socks5); + } + else if (client->state == socks5_request_sent) { + socks5_read_reply(buffev, client, socks5); + } + else if (client->state == socks5_skip_domain) { + socks5_addr_ipv4 ipv4; // all socks5_addr*.port are equal + uint8_t size; + if (redsocks_read_expected(client, buffev->input, &size, sizes_greater_equal, sizeof(size)) < 0) + return; + socks5->to_skip = size + sizeof(ipv4.port); + redsocks_write_helper( + buffev, client, + NULL, socks5_skip_address, socks5->to_skip + ); + } + else if (client->state == socks5_skip_address) { + uint8_t data[socks5->to_skip]; + if (redsocks_read_expected(client, buffev->input, data, sizes_greater_equal, socks5->to_skip) < 0) + return; + redsocks_start_relay(client); + } + else { + redsocks_drop_client(client); + } +} + +static void auto_drop_relay(redsocks_client *client) +{ + redsocks_log_error(client, LOG_INFO, "dropping relay only"); + + if (client->relay) { + redsocks_close(EVENT_FD(&client->relay->ev_write)); + bufferevent_free(client->relay); + client->relay = NULL; + } +} + +void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); + + +static void auto_relay_connected(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + + assert(buffev == client->relay); + + redsocks_touch_client(client); + + if (!red_is_socket_connected_ok(buffev)) { + if (client->state == socks5_pre_detect && !auto_retry_or_drop(client)) + return; + + redsocks_log_error(client, LOG_DEBUG, "failed to connect to proxy"); + goto fail; + } + + client->relay->readcb = client->instance->relay_ss->readcb; + client->relay->writecb = client->instance->relay_ss->writecb; + client->relay->writecb(buffev, _arg); + return; + +fail: + redsocks_drop_client(client); +} + +static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) +{ + redsocks_client *client = _arg; + assert(buffev == client->relay || buffev == client->client); + + redsocks_touch_client(client); + + redsocks_log_errno(client, LOG_DEBUG, "EOF %d", client->state); + if (client->state == socks5_pre_detect || client->state == socks5_direct ) + { + if (!auto_retry_or_drop(client)) + return; + } + if (what == (EVBUFFER_READ|EVBUFFER_EOF)) { + struct bufferevent *antiev; + if (buffev == client->relay) + antiev = client->client; + else + antiev = client->relay; + + redsocks_shutdown(client, buffev, SHUT_RD); + + if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0) + redsocks_shutdown(client, antiev, SHUT_WR); + } + else { + /* + errno = redsocks_socket_geterrno(client, buffev); + redsocks_log_errno(client, LOG_NOTICE, "%s error, code " event_fmt_str, + buffev == client->relay ? "relay" : "client", + event_fmt(what)); + */ + redsocks_drop_client(client); + } +} + + +/* return 1 for drop, 0 for retry. */ +static int auto_retry_or_drop(redsocks_client * client) +{ + time_t now = redsocks_time(NULL); + socks5_client *socks5 = (void*)(client + 1); + + if (client->state == socks5_pre_detect || client->state == socks5_direct) + { + if (now - socks5->time_connect_relay <= 3) + { + if (client->state == socks5_direct) + bufferevent_disable(client->client, EV_READ| EV_WRITE); + /* drop relay and update state, then retry with socks5 relay */ + auto_drop_relay(client); + client->state = socks5_new; + add_addr_to_cache(&client->destaddr); + auto_connect_relay(client); /* Retry SOCKS5 proxy relay */ + return 0; + } + } + /* drop */ + return 1; +} + +static void auto_connect_relay(redsocks_client *client) +{ + socks5_client *socks5 = (void*)(client + 1); + + if (client->state == socks5_pre_detect) + { + if (is_addr_in_cache(&client->destaddr)) + { + client->state = socks5_new; /* Connect SOCKS5 */ + redsocks_log_error(client, LOG_DEBUG, "Found in cache"); + } + } + client->relay = red_connect_relay( client->state == socks5_pre_detect + ? &client->destaddr : &client->instance->config.relayaddr, + auto_relay_connected, auto_event_error, client); + + socks5->time_connect_relay = redsocks_time(NULL); + + if (!client->relay) { + redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); + redsocks_drop_client(client); + } +} + + + + + + +relay_subsys autosocks5_subsys = +{ + .name = "autosocks5", + .payload_len = sizeof(socks5_client), + .instance_payload_len = 0, + .readcb = socks5_read_cb, + .writecb = socks5_write_cb, + .init = auto_socks5_client_init, + .connect_relay = auto_connect_relay, +}; + + +/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ +/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ From 374d45ec708e3317b8edd26ddfc1b87e7bfff364 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 12 Jan 2013 17:45:22 +0800 Subject: [PATCH 002/192] Many changes. --- Makefile | 6 ++-- README | 4 +++ base.c | 89 +++++++++++++++++++++++++++++++++++++++++++-- redsocks.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++----- redudp.c | 24 +++++++++++++ utils.c | 4 +++ 6 files changed, 218 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 4b83c132..4e0358b1 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ -OBJS := parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o autosocks.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks -VERSION := 0.4 +VERSION := 0.41 LIBS := -levent -CFLAGS += -g -O2 +CFLAGS +=-static -fPIC -O2 -I /home/kitty/openwrt/backfire/build_dir/target-mips_uClibc-0.9.32/libevent-2.0.21-stable/ipkg-install/usr/include/ -L /home/kitty/openwrt/backfire/build_dir/target-mips_uClibc-0.9.32/libevent-2.0.21-stable/ipkg-install/usr/lib/ override CFLAGS += -std=gnu99 -Wall all: $(OUT) diff --git a/README b/README index 17ffee7c..9b3ac790 100644 --- a/README +++ b/README @@ -1,3 +1,7 @@ +This is an modified version of original redsocks. +This variant is useful for anti-GFW (Great Fire Wall). + +--------------------------------------------------------------------- This tool allows you to redirect any TCP connection to SOCKS or HTTPS proxy using your firewall, so redirection is system-wide. diff --git a/base.c b/base.c index b04959f3..04532de6 100644 --- a/base.c +++ b/base.c @@ -14,6 +14,7 @@ * under the License. */ +#include #include #include #include @@ -34,8 +35,8 @@ # include # include # include -# include #endif +# include #include "log.h" #include "main.h" #include "parser.h" @@ -320,6 +321,89 @@ static parser_section base_conf_section = /*********************************************************************** * `base` initialization */ +static void myproc() +{ + time_t now; + time_t last; + FILE * tmp = NULL; + pid_t pid = -1; + int stop = 0; + char * buf = NULL; + size_t len = 0; + ssize_t dsize ; + struct sigaction sa ; + + /* Set SIGCHLD handler to ignore death of child. */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_RESTART; + + if (sigaction(SIGCHLD, &sa, NULL) == -1) { + log_errno(LOG_ERR, "sigaction SIGCHLD"); + return ; + } + for(;;) + { + pid = fork(); + switch (pid) { + case -1: // error + log_errno(LOG_ERR, "fork()"); + return ; + case 0: // child + return; + default: // parent, pid is returned + /* let's monitor child process */ + sleep(5);/* give child process 5 seconds for initialization */ + stop = 0; + for(;stop==0;) + { + if (kill(pid, SIGUSR2) == -1) + { + if (errno == ESRCH) + { + /* process is dead ? */ + stop = 1; + } + log_error(LOG_NOTICE, "Failed to send SIGUSR2 to pid %d", pid); + sleep(1); + continue; + } + sleep(1); + + tmp = fopen("/tmp/redtime", "r"); + if (tmp) + { + len = 0; + buf = NULL; + dsize =getline( &buf, &len, tmp); + if (dsize != -1) + { + last = atol(buf); + now = time(NULL); + if (now-last>4) + { + kill(pid, SIGKILL); + sleep(1); + stop = 1; + } + } + free(buf); + fclose(tmp); + } + else + { +/* + kill(pid, SIGKILL); + sleep(1); + stop = 1; +*/ + } + + } + } + } +} + + static int base_fini(); static int base_init() @@ -409,7 +493,7 @@ static int base_init() exit(EXIT_SUCCESS); } } - + log_open(); // child has nothing to do with TTY if (instance.daemon) { @@ -428,6 +512,7 @@ static int base_init() close(devnull); } + myproc(); return 0; fail: if (devnull != -1) diff --git a/redsocks.c b/redsocks.c index ba5eab22..0ac66641 100644 --- a/redsocks.c +++ b/redsocks.c @@ -35,18 +35,61 @@ #include "utils.h" -#define REDSOCKS_RELAY_HALFBUFF 4096 +#define REDSOCKS_RELAY_HALFBUFF 1024*32 +void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); +static void redsocks_relay_relayreadcb(struct bufferevent *from, void *_client); +static void redsocks_relay_relaywritecb(struct bufferevent *from, void *_client); +void redsocks_direct_connect_relay(redsocks_client *client); +static void direct_relay_init(redsocks_client *client) +{ + client->state = 0; +} -static void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); - +static void direct_instance_fini(redsocks_instance *instance) +{ +} +static void direct_read_cb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + redsocks_touch_client(client); + if (client->state == 0) + { + client->state = 1; + redsocks_start_relay(client); + } +} +static void direct_write_cb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + redsocks_touch_client(client); + if (client->state == 0) + { + client->state = 1; + redsocks_start_relay(client); + } +} +relay_subsys direct_connect_subsys = +{ + .name = "direct", + .payload_len = 0, + .instance_payload_len = 0, + .readcb = direct_read_cb, + .writecb = direct_write_cb, + .init = direct_relay_init, + .instance_fini = direct_instance_fini, + .connect_relay = redsocks_direct_connect_relay, +}; extern relay_subsys http_connect_subsys; extern relay_subsys http_relay_subsys; extern relay_subsys socks4_subsys; extern relay_subsys socks5_subsys; +extern relay_subsys autosocks5_subsys; static relay_subsys *relay_subsystems[] = { + &autosocks5_subsys, + &direct_connect_subsys, &http_connect_subsys, &http_relay_subsys, &socks4_subsys, @@ -348,7 +391,7 @@ void redsocks_drop_client(redsocks_client *client) free(client); } -static void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how) +void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how) { short evhow = 0; char *strev, *strhow = NULL, *strevhow = NULL; @@ -395,6 +438,11 @@ static void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffe redsocks_log_error(client, LOG_DEBUG, "both client and server disconnected"); redsocks_drop_client(client); } + else + { + if (how == SHUT_WR && buffev == client->relay && client->relay->enabled == 0) + redsocks_drop_client(client); + } } // I assume that -1 is invalid errno value @@ -573,6 +621,16 @@ void redsocks_connect_relay(redsocks_client *client) redsocks_drop_client(client); } } +void redsocks_direct_connect_relay(redsocks_client *client) +{ + client->relay = red_connect_relay(&client->destaddr, + redsocks_relay_connected, redsocks_event_error, client); + if (!client->relay) { + redsocks_log_errno(client, LOG_ERR, "red_connect_relay"); + redsocks_drop_client(client); + } +} + static void redsocks_accept_backoff(int fd, short what, void *_arg) { @@ -767,6 +825,19 @@ static void redsocks_debug_dump(int sig, short what, void *_arg) redsocks_debug_dump_instance(instance, now); } +static void redsocks_heartbeat(int sig, short what, void *_arg) +{ + time_t now = redsocks_time(NULL); + FILE * tmp = NULL; + + tmp = fopen("/tmp/redtime", "w"); + if (tmp) + { + fprintf(tmp, "%d", now); + fclose(tmp); + } +} + static void redsocks_fini_instance(redsocks_instance *instance); static int redsocks_init_instance(redsocks_instance *instance) @@ -876,14 +947,25 @@ static void redsocks_fini_instance(redsocks_instance *instance) { static int redsocks_fini(); static struct event debug_dumper; +static struct event heartbeat_writer; + +//static void ignore_sig(int t) {} static int redsocks_init() { - struct sigaction sa = { }, sa_old = { }; + struct sigaction sa , sa_old; redsocks_instance *tmp, *instance = NULL; - + void (* old_hdl)(int)= NULL; +/* + old_hdl = signal(SIGPIPE, ignore_sig); + if (old_hdl == -1) { + log_errno(LOG_ERR, "sigaction"); + return -1; + } +*/ sa.sa_handler = SIG_IGN; sa.sa_flags = SA_RESTART; - if (sigaction(SIGPIPE, &sa, &sa_old) == -1) { + + if (sigaction(SIGPIPE, &sa, NULL) == -1) { log_errno(LOG_ERR, "sigaction"); return -1; } @@ -893,6 +975,11 @@ static int redsocks_init() { log_errno(LOG_ERR, "signal_add"); goto fail; } + signal_set(&heartbeat_writer, SIGUSR2, redsocks_heartbeat, NULL); + if (signal_add(&heartbeat_writer, NULL) != 0) { + log_errno(LOG_ERR, "signal_add SIGUSR2"); + goto fail; + } list_for_each_entry_safe(instance, tmp, &instances, list) { if (redsocks_init_instance(instance) != 0) @@ -903,7 +990,8 @@ static int redsocks_init() { fail: // that was the first resource allocation, it return's on failure, not goto-fail's - sigaction(SIGPIPE, &sa_old, NULL); +/* sigaction(SIGPIPE, &sa_old, NULL); */ +// signal(SIGPIPE, old_hdl); redsocks_fini(); diff --git a/redudp.c b/redudp.c index 262af3ee..ca3afe5f 100644 --- a/redudp.c +++ b/redudp.c @@ -33,6 +33,30 @@ #include "redsocks.h" #include "redudp.h" +/* Just in case the IP_TRANSPARENT define isn't included somehow */ + +#if !defined(IP_TRANSPARENT) +#define IP_TRANSPARENT 19 +#define IP_ORIGDSTADDR 20 +#define IP_RECVORIGDSTADDR IP_ORIGDSTADDR +#endif + +/* Just in case the IP_TRANSPARENT define isn't included somehow */ + +#if !defined(IP_TRANSPARENT) +#define IP_TRANSPARENT 19 +#define IP_ORIGDSTADDR 20 +#define IP_RECVORIGDSTADDR IP_ORIGDSTADDR +#endif + +/* Just in case the IP_TRANSPARENT define isn't included somehow */ + +#if !defined(IP_TRANSPARENT) +#define IP_TRANSPARENT 19 +#define IP_ORIGDSTADDR 20 +#define IP_RECVORIGDSTADDR IP_ORIGDSTADDR +#endif + #define redudp_log_error(client, prio, msg...) \ redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &(client)->clientaddr, get_destaddr(client), prio, ## msg) #define redudp_log_errno(client, prio, msg...) \ diff --git a/utils.c b/utils.c index 31c68940..35bb2f46 100644 --- a/utils.c +++ b/utils.c @@ -26,6 +26,10 @@ #include "utils.h" #include "redsocks.h" // for redsocks_close +#ifndef IP_ORIGDSTADDR +#define IP_ORIGDSTADDR 20 +#endif + int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr, struct sockaddr_in *toaddr) { socklen_t addrlen = sizeof(*inaddr); From 068ba5fc3cb534efdb0f5a73fc8c97b18cea8c93 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 13 Jan 2013 15:37:52 +0800 Subject: [PATCH 003/192] 1. Remove redundant code in autosocks.c. 2. Enhance address cache in autosocks.c. The new cache stores IP addresses in blocks. --- autosocks.c | 225 ++++++++++------------------------------------------ socks5.c | 4 +- 2 files changed, 44 insertions(+), 185 deletions(-) diff --git a/autosocks.c b/autosocks.c index 98bfbb46..f630c3de 100644 --- a/autosocks.c +++ b/autosocks.c @@ -25,8 +25,6 @@ #include "socks5.h" typedef enum socks5_state_t { - socks5_pre_detect, - socks5_direct, socks5_new, socks5_method_sent, socks5_auth_sent, @@ -34,6 +32,8 @@ typedef enum socks5_state_t { socks5_skip_domain, socks5_skip_address, socks5_MAX, + socks5_pre_detect=100, /* Introduce additional states to socks5 subsystem */ + socks5_direct, } socks5_state; typedef struct socks5_client_t { @@ -42,20 +42,6 @@ typedef struct socks5_client_t { time_t time_connect_relay; // timestamp when start to connect relay } socks5_client; -static const char *socks5_strstatus[] = { - "ok", - "server failure", - "connection not allowed by ruleset", - "network unreachable", - "host unreachable", - "connection refused", - "TTL expired", - "command not supported", - "address type not supported", -}; -static const size_t socks5_strstatus_len = SIZEOF_ARRAY(socks5_strstatus); - -const char* socks5_status_to_str(int socks5_status); int socks5_is_valid_cred(const char *login, const char *password); @@ -63,29 +49,41 @@ static int auto_retry_or_drop(redsocks_client * client); static void auto_connect_relay(redsocks_client *client); -#define ADDR_CACHE_SIZE 64 -static struct sockaddr_in addr_cache[ADDR_CACHE_SIZE]; -static int addr_count = 0; -static int first_addr = 0; +#define CIRCUIT_RESET_SECONDS 3 +#define ADDR_CACHE_BLOCKS 64 +#define ADDR_CACHE_BLOCK_SIZE 32 +#define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) +static struct sockaddr_in addr_cache[ADDR_CACHE_BLOCKS][ADDR_CACHE_BLOCK_SIZE]; +static int addr_cache_counters[ADDR_CACHE_BLOCKS]; +static int addr_cache_pointers[ADDR_CACHE_BLOCKS]; static int cache_init = 0; static void init_addr_cache() { if (!cache_init) { - memset((void *)addr_cache, 0, sizeof(struct sockaddr_in)*ADDR_CACHE_SIZE); - addr_count = 0; - first_addr = 0; + memset((void *)addr_cache, 0, sizeof(addr_cache)); + memset((void *)addr_cache_counters, 0, sizeof(addr_cache_counters)); + memset((void *)addr_cache_pointers, 0, sizeof(addr_cache_pointers)); cache_init = 1; } } static int is_addr_in_cache(const struct sockaddr_in * addr) { + /* get block index */ + int block = block_from_sockaddr_in(addr); + int count = addr_cache_counters[block]; + int first = addr_cache_pointers[block]; int i = 0; /* do reverse search for efficency */ - for ( i = addr_count - 1; i >= 0; i -- ) - if (0 == memcmp((void *)addr, (void *)&addr_cache[(first_addr+i)%ADDR_CACHE_SIZE], sizeof(struct sockaddr_in))) + for ( i = count - 1; i >= 0; i -- ) + /* + if (0 == memcmp((void *)addr, (void *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE], sizeof(struct sockaddr_in))) +*/ + if (addr->sin_addr.s_addr == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].sin_addr.s_addr + && addr->sin_family == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].sin_family + ) return 1; return 0; @@ -93,16 +91,20 @@ static int is_addr_in_cache(const struct sockaddr_in * addr) static void add_addr_to_cache(const struct sockaddr_in * addr) { - if (addr_count < ADDR_CACHE_SIZE) + int block = block_from_sockaddr_in(addr); + int count = addr_cache_counters[block]; + int first = addr_cache_pointers[block]; + + if (count < ADDR_CACHE_BLOCK_SIZE) { - memcpy((void *)&addr_cache[addr_count], (void *) addr, sizeof(struct sockaddr_in)); - addr_count ++; + memcpy((void *)&addr_cache[block][count], (void *) addr, sizeof(struct sockaddr_in)); + addr_cache_counters[block]++; } else { - memcpy((void *)&addr_cache[first_addr], (void *) addr, sizeof(struct sockaddr_in)); - first_addr ++; - first_addr %= ADDR_CACHE_SIZE; + memcpy((void *)&addr_cache[block][first], (void *) addr, sizeof(struct sockaddr_in)); + addr_cache_pointers[block]++; + addr_cache_pointers[block]%=ADDR_CACHE_BLOCK_SIZE; } } @@ -117,28 +119,9 @@ void auto_socks5_client_init(redsocks_client *client) init_addr_cache(); } -static struct evbuffer *socks5_mkmethods(redsocks_client *client) -{ - socks5_client *socks5 = (void*)(client + 1); - return socks5_mkmethods_plain(socks5->do_password); -} - -struct evbuffer *socks5_mkmethods_plain(int do_password); - -static struct evbuffer *socks5_mkpassword(redsocks_client *client) -{ - return socks5_mkpassword_plain(client->instance->config.login, client->instance->config.password); -} - -struct evbuffer *socks5_mkpassword_plain(const char *login, const char *password); -struct evbuffer *socks5_mkcommand_plain(int socks5_cmd, const struct sockaddr_in *destaddr); - -static struct evbuffer *socks5_mkconnect(redsocks_client *client) -{ - return socks5_mkcommand_plain(socks5_cmd_connect, &client->destaddr); -} -static void socks5_write_cb(struct bufferevent *buffev, void *_arg) +void socks5_write_cb(struct bufferevent *buffev, void *_arg); +static void auto_write_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; @@ -165,112 +148,14 @@ static void socks5_write_cb(struct bufferevent *buffev, void *_arg) else if (client->state == socks5_direct) redsocks_start_relay(client); */ - else if (client->state == socks5_new) { - redsocks_write_helper( - buffev, client, - socks5_mkmethods, socks5_method_sent, sizeof(socks5_method_reply) - ); - } -} - -const char* socks5_is_known_auth_method(socks5_method_reply *reply, int do_password); - - -static void socks5_read_auth_methods(struct bufferevent *buffev, redsocks_client *client, socks5_client *socks5) -{ - socks5_method_reply reply; - const char *error = NULL; - - if (redsocks_read_expected(client, buffev->input, &reply, sizes_equal, sizeof(reply)) < 0) - return; - - error = socks5_is_known_auth_method(&reply, socks5->do_password); - if (error) { - redsocks_log_error(client, LOG_NOTICE, "socks5_is_known_auth_method: %s", error); - redsocks_drop_client(client); - } - else if (reply.method == socks5_auth_none) { - redsocks_write_helper( - buffev, client, - socks5_mkconnect, socks5_request_sent, sizeof(socks5_reply) - ); - } - else if (reply.method == socks5_auth_password) { - redsocks_write_helper( - buffev, client, - socks5_mkpassword, socks5_auth_sent, sizeof(socks5_auth_reply) - ); - } -} - -static void socks5_read_auth_reply(struct bufferevent *buffev, redsocks_client *client, socks5_client *socks5) -{ - socks5_auth_reply reply; - - if (redsocks_read_expected(client, buffev->input, &reply, sizes_equal, sizeof(reply)) < 0) - return; - - if (reply.ver != socks5_password_ver) { - redsocks_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected auth reply version..."); - redsocks_drop_client(client); - } - else if (reply.status == socks5_password_passed) - redsocks_write_helper( - buffev, client, - socks5_mkconnect, socks5_request_sent, sizeof(socks5_reply) - ); else - redsocks_drop_client(client); + socks5_write_cb(buffev, _arg); } -static void socks5_read_reply(struct bufferevent *buffev, redsocks_client *client, socks5_client *socks5) -{ - socks5_reply reply; - - if (redsocks_read_expected(client, buffev->input, &reply, sizes_greater_equal, sizeof(reply)) < 0) - return; - - if (reply.ver != socks5_ver) { - redsocks_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected reply version..."); - redsocks_drop_client(client); - } - else if (reply.status == socks5_status_succeeded) { - socks5_state nextstate; - size_t len; - if (reply.addrtype == socks5_addrtype_ipv4) { - len = socks5->to_skip = sizeof(socks5_addr_ipv4); - nextstate = socks5_skip_address; - } - else if (reply.addrtype == socks5_addrtype_ipv6) { - len = socks5->to_skip = sizeof(socks5_addr_ipv6); - nextstate = socks5_skip_address; - } - else if (reply.addrtype == socks5_addrtype_domain) { - socks5_addr_domain domain; - len = sizeof(domain.size); - nextstate = socks5_skip_domain; - } - else { - redsocks_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected address type..."); - redsocks_drop_client(client); - return; - } - redsocks_write_helper( - buffev, client, - NULL, nextstate, len - ); - } - else { - redsocks_log_error(client, LOG_NOTICE, "Socks5 server status: %s (%i)", - /* 0 <= reply.status && */ reply.status < SIZEOF_ARRAY(socks5_strstatus) - ? socks5_strstatus[reply.status] : "?", reply.status); - redsocks_drop_client(client); - } -} - -static void socks5_read_cb(struct bufferevent *buffev, void *_arg) +void socks5_read_cb(struct bufferevent *buffev, void *_arg); +static void auto_read_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; socks5_client *socks5 = (void*)(client + 1); @@ -287,34 +172,8 @@ static void socks5_read_cb(struct bufferevent *buffev, void *_arg) else if (client->state == socks5_direct) { /* if (EVBUFFER_LENGTH(buffev->input) == 0 && client->relay_evshut & EV_READ) */ } - else if (client->state == socks5_method_sent) { - socks5_read_auth_methods(buffev, client, socks5); - } - else if (client->state == socks5_auth_sent) { - socks5_read_auth_reply(buffev, client, socks5); - } - else if (client->state == socks5_request_sent) { - socks5_read_reply(buffev, client, socks5); - } - else if (client->state == socks5_skip_domain) { - socks5_addr_ipv4 ipv4; // all socks5_addr*.port are equal - uint8_t size; - if (redsocks_read_expected(client, buffev->input, &size, sizes_greater_equal, sizeof(size)) < 0) - return; - socks5->to_skip = size + sizeof(ipv4.port); - redsocks_write_helper( - buffev, client, - NULL, socks5_skip_address, socks5->to_skip - ); - } - else if (client->state == socks5_skip_address) { - uint8_t data[socks5->to_skip]; - if (redsocks_read_expected(client, buffev->input, data, sizes_greater_equal, socks5->to_skip) < 0) - return; - redsocks_start_relay(client); - } else { - redsocks_drop_client(client); + socks5_read_cb(buffev, _arg); } } @@ -402,7 +261,7 @@ static int auto_retry_or_drop(redsocks_client * client) if (client->state == socks5_pre_detect || client->state == socks5_direct) { - if (now - socks5->time_connect_relay <= 3) + if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) { if (client->state == socks5_direct) bufferevent_disable(client->client, EV_READ| EV_WRITE); @@ -452,8 +311,8 @@ relay_subsys autosocks5_subsys = .name = "autosocks5", .payload_len = sizeof(socks5_client), .instance_payload_len = 0, - .readcb = socks5_read_cb, - .writecb = socks5_write_cb, + .readcb = auto_read_cb, + .writecb = auto_write_cb, .init = auto_socks5_client_init, .connect_relay = auto_connect_relay, }; diff --git a/socks5.c b/socks5.c index 4b5456d4..124e7838 100644 --- a/socks5.c +++ b/socks5.c @@ -151,7 +151,7 @@ static struct evbuffer *socks5_mkconnect(redsocks_client *client) return socks5_mkcommand_plain(socks5_cmd_connect, &client->destaddr); } -static void socks5_write_cb(struct bufferevent *buffev, void *_arg) +void socks5_write_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; @@ -271,7 +271,7 @@ static void socks5_read_reply(struct bufferevent *buffev, redsocks_client *clien } } -static void socks5_read_cb(struct bufferevent *buffev, void *_arg) +void socks5_read_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; socks5_client *socks5 = (void*)(client + 1); From 08d6a6fe92ecf2df69ed4ab0570b2e5cdb3db99e Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 13 Jan 2013 19:18:33 +0800 Subject: [PATCH 004/192] Enhanced 'autosocks5' to allow connect to target via proxy when timeout occurs while connecting to target. The timeout value is set to 13 seconds. Whenever a connection to target timeouts, the connection will be retried via proxy. --- autosocks.c | 44 ++++++++++++++++++++++++++++++++--------- utils.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 9 deletions(-) diff --git a/autosocks.c b/autosocks.c index f630c3de..08738159 100644 --- a/autosocks.c +++ b/autosocks.c @@ -50,6 +50,7 @@ static int auto_retry_or_drop(redsocks_client * client); static void auto_connect_relay(redsocks_client *client); #define CIRCUIT_RESET_SECONDS 3 +#define CONNECT_TIMEOUT_SECONDS 12 #define ADDR_CACHE_BLOCKS 64 #define ADDR_CACHE_BLOCK_SIZE 32 #define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) @@ -130,6 +131,9 @@ static void auto_write_cb(struct bufferevent *buffev, void *_arg) if (client->state == socks5_pre_detect) { client->state = socks5_direct; + /* We do not need to detect timeouts any more. + The two ppers will handle it. */ + bufferevent_set_timeouts(buffev, NULL, NULL); /* if (EVBUFFER_LENGTH(buffev->input) == 0 && client->relay_evshut & EV_READ) { @@ -188,6 +192,18 @@ static void auto_drop_relay(redsocks_client *client) } } +static int auto_retry(redsocks_client * client) +{ + if (client->state == socks5_direct) + bufferevent_disable(client->client, EV_READ| EV_WRITE); + /* drop relay and update state, then retry with socks5 relay */ + auto_drop_relay(client); + client->state = socks5_new; + add_addr_to_cache(&client->destaddr); + auto_connect_relay(client); /* Retry SOCKS5 proxy relay */ +} + + void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); @@ -223,12 +239,24 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) redsocks_touch_client(client); + if (what & EVBUFFER_TIMEOUT) + { + redsocks_log_errno(client, LOG_DEBUG, "Timeout %d", client->state); + /* In case timeout occurs for connecting relay, we try to connect + to target with SOCKS5 proxy. It is possible that the connection to + target can be set up a bit longer than the timeout value we set. + However, it is still better to make connection via proxy. */ + auto_retry(client); + return; + } + redsocks_log_errno(client, LOG_DEBUG, "EOF %d", client->state); if (client->state == socks5_pre_detect || client->state == socks5_direct ) { if (!auto_retry_or_drop(client)) return; } + if (what == (EVBUFFER_READ|EVBUFFER_EOF)) { struct bufferevent *antiev; if (buffev == client->relay) @@ -263,13 +291,7 @@ static int auto_retry_or_drop(redsocks_client * client) { if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) { - if (client->state == socks5_direct) - bufferevent_disable(client->client, EV_READ| EV_WRITE); - /* drop relay and update state, then retry with socks5 relay */ - auto_drop_relay(client); - client->state = socks5_new; - add_addr_to_cache(&client->destaddr); - auto_connect_relay(client); /* Retry SOCKS5 proxy relay */ + auto_retry(client); return 0; } } @@ -280,6 +302,9 @@ static int auto_retry_or_drop(redsocks_client * client) static void auto_connect_relay(redsocks_client *client) { socks5_client *socks5 = (void*)(client + 1); + struct timeval tv; + tv.tv_sec = CONNECT_TIMEOUT_SECONDS; + tv.tv_usec = 0; if (client->state == socks5_pre_detect) { @@ -289,9 +314,10 @@ static void auto_connect_relay(redsocks_client *client) redsocks_log_error(client, LOG_DEBUG, "Found in cache"); } } - client->relay = red_connect_relay( client->state == socks5_pre_detect + client->relay = red_connect_relay2( client->state == socks5_pre_detect ? &client->destaddr : &client->instance->config.relayaddr, - auto_relay_connected, auto_event_error, client); + auto_relay_connected, auto_event_error, client, + client->state == socks5_pre_detect ? &tv: NULL); socks5->time_connect_relay = redsocks_time(NULL); diff --git a/utils.c b/utils.c index 35bb2f46..0ac2eea4 100644 --- a/utils.c +++ b/utils.c @@ -164,6 +164,62 @@ struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb write return NULL; } + +struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg, const struct timeval *timeout_write) +{ + struct bufferevent *retval = NULL; + int on = 1; + int relay_fd = -1; + int error; + + relay_fd = socket(AF_INET, SOCK_STREAM, 0); + if (relay_fd == -1) { + log_errno(LOG_ERR, "socket"); + goto fail; + } + + error = fcntl_nonblock(relay_fd); + if (error) { + log_errno(LOG_ERR, "fcntl"); + goto fail; + } + + error = setsockopt(relay_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); + if (error) { + log_errno(LOG_WARNING, "setsockopt"); + goto fail; + } + + error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); + if (error && errno != EINPROGRESS) { + log_errno(LOG_NOTICE, "connect"); + goto fail; + } + + retval = bufferevent_new(relay_fd, NULL, writecb, errorcb, cbarg); + if (!retval) { + log_errno(LOG_ERR, "bufferevent_new"); + goto fail; + } + + bufferevent_set_timeouts(retval, NULL, timeout_write); + + error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... + if (error) { + log_errno(LOG_ERR, "bufferevent_enable"); + goto fail; + } + + return retval; + +fail: + if (relay_fd != -1) + redsocks_close(relay_fd); + if (retval) + bufferevent_free(retval); + return NULL; +} + int red_socket_geterrno(struct bufferevent *buffev) { int error; From 9490f0858adfa4bf28b5eec966b24980ef9c0489 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 14 Jan 2013 07:10:51 +0800 Subject: [PATCH 005/192] Commit missing changes in utils.h made during last update. --- utils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/utils.h b/utils.h index c2277e91..362ae7c3 100644 --- a/utils.h +++ b/utils.h @@ -42,6 +42,7 @@ struct sockaddr_in; time_t redsocks_time(time_t *t); char *redsocks_evbuffer_readline(struct evbuffer *buf); struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg); +struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg, const struct timeval *timeout_write); int red_socket_geterrno(struct bufferevent *buffev); int red_is_socket_connected_ok(struct bufferevent *buffev); int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *fromaddr, struct sockaddr_in *toaddr); From ab1df26d58cf1dbacebfeac93fd934c7ceaf3bd7 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 14 Jan 2013 07:45:14 +0800 Subject: [PATCH 006/192] Update README --- README | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/README b/README index 9b3ac790..976c3c1f 100644 --- a/README +++ b/README @@ -1,5 +1,31 @@ -This is an modified version of original redsocks. +This is a modified version of original redsocks. This variant is useful for anti-GFW (Great Fire Wall). +It provides the following advanced features: +1. new 'direct' mehtod. + This method is intent to direct any TCP connections directly without +going through a proxy. Why this mehtod? Some ISP, like China Mobile (CMCC), +detects numbers of computers in your local network sharing same account by +montioring HTTP/HTTPS traffic. Only one computer has full access to all +websites. Other computers in local network are limited to some websites. +By depolying transparent proxy with this method, all your computers can +access websites without restrictions. +2. new 'autosocks5' method. + This mehtod is specially customized for fighting against GFW. By +default, all TCP connections are redirected to connect to target directly +without going through socks5 proxy. But, in case the connection to target +is RESET/CLOSED immediately for some reason (e.g. by GFW), the connection +is redirected via socks5 proxy. The same logic is also applied when the +connection to target fails to establish in certain seconds (13 seconds +currently). For such slow connection, no matter it is really a slow +connection or the handshake packets are dropped by GFW, let such +connections go through proxy is not a bad idea. + REDSOCKS also maintances a cache for addresses that need to be relayed +via proxy. So, whenever an address is identified need to be relayed via +proxy, the susequent connections to those addresses will be relayed via +proxy immediately unless the addresses are removed from cache by code logic. + The advantage of this method is that you do not need to set thousands +entries in iptables for blocked sites manually. It is especially useful +for proxy Youtube traffic. --------------------------------------------------------------------- This tool allows you to redirect any TCP connection to SOCKS or HTTPS From 2efe35cb95872174c2da2285a17e2871e007b5e0 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 14 Jan 2013 08:56:46 +0800 Subject: [PATCH 007/192] Updated README and config examples. --- README | 11 ++++++++--- redsocks.conf.example | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README b/README index 976c3c1f..ca354876 100644 --- a/README +++ b/README @@ -17,15 +17,20 @@ is RESET/CLOSED immediately for some reason (e.g. by GFW), the connection is redirected via socks5 proxy. The same logic is also applied when the connection to target fails to establish in certain seconds (13 seconds currently). For such slow connection, no matter it is really a slow -connection or the handshake packets are dropped by GFW, let such +connection or the handshake packets are dropped by GFW, making such connections go through proxy is not a bad idea. REDSOCKS also maintances a cache for addresses that need to be relayed via proxy. So, whenever an address is identified need to be relayed via -proxy, the susequent connections to those addresses will be relayed via +proxy, the subsequent connections to those addresses will be relayed via proxy immediately unless the addresses are removed from cache by code logic. The advantage of this method is that you do not need to set thousands entries in iptables for blocked sites manually. It is especially useful -for proxy Youtube traffic. +for proxying Youtube which has so many sub-domains for video content cache. + +HOW TO BUILD: +Since this variant of redsocks is customized for running with Openwrt, please +read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how +to compile. --------------------------------------------------------------------- This tool allows you to redirect any TCP connection to SOCKS or HTTPS diff --git a/redsocks.conf.example b/redsocks.conf.example index a71eb5c3..aa6f1e03 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -58,11 +58,13 @@ redsocks { // `ip' and `port' are IP and tcp-port of proxy-server // You can also use hostname instead of IP, only one (random) // address of multihomed host will be used. + // The two fields are meaningless when proxy type is 'direct'. ip = example.org; port = 1080; // known types: socks4, socks5, http-connect, http-relay + // New types: direct, autosocks5 type = socks5; // login = "foobar"; From 8b49c38887a57ab232cf57ed647e30e1b4675002 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 15 Jan 2013 14:23:52 +0800 Subject: [PATCH 008/192] 1. Update circuit reset timer to be 1 second 2. Detect connection reset with more reliable method. --- autosocks.c | 171 +++++++++++++++++++++++++++++++++------------------- redsocks.c | 5 +- redsocks.h | 2 +- 3 files changed, 112 insertions(+), 66 deletions(-) diff --git a/autosocks.c b/autosocks.c index 08738159..a28e3a34 100644 --- a/autosocks.c +++ b/autosocks.c @@ -1,4 +1,7 @@ -/* redsocks - transparent TCP-to-proxy redirector +/* Copyright (C) 2013 Zhuofei Wang + * + * + * redsocks - transparent TCP-to-proxy redirector * Copyright (C) 2007-2011 Leonid Evdokimov * * Licensed under the Apache License, Version 2.0 (the "License"); you may not @@ -17,6 +20,7 @@ #include #include #include +#include #include #include #include "utils.h" @@ -40,17 +44,20 @@ typedef struct socks5_client_t { int do_password; // 1 - password authentication is possible int to_skip; // valid while reading last reply (after main request) time_t time_connect_relay; // timestamp when start to connect relay + int got_data; } socks5_client; int socks5_is_valid_cred(const char *login, const char *password); +void socks5_write_cb(struct bufferevent *buffev, void *_arg); +void socks5_read_cb(struct bufferevent *buffev, void *_arg); +void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); static int auto_retry_or_drop(redsocks_client * client); - static void auto_connect_relay(redsocks_client *client); -#define CIRCUIT_RESET_SECONDS 3 -#define CONNECT_TIMEOUT_SECONDS 12 +#define CIRCUIT_RESET_SECONDS 1 +#define CONNECT_TIMEOUT_SECONDS 13 #define ADDR_CACHE_BLOCKS 64 #define ADDR_CACHE_BLOCK_SIZE 32 #define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) @@ -112,19 +119,42 @@ static void add_addr_to_cache(const struct sockaddr_in * addr) void auto_socks5_client_init(redsocks_client *client) { - socks5_client *socks5 = (void*)(client + 1); + socks5_client * socks5= (void*)(client + 1); const redsocks_config *config = &client->instance->config; client->state = socks5_pre_detect; + socks5->got_data = 0; socks5->do_password = socks5_is_valid_cred(config->login, config->password); init_addr_cache(); } +static void auto_relay_readcb(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) +{ + if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { + if (bufferevent_write_buffer(to, from->input) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + } + else { + if (bufferevent_disable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); + } +} + +static void auto_relay_relayreadcb(struct bufferevent *from, void *_client) +{ + redsocks_client *client = _client; + socks5_client *socks5 = (void*)(client + 1); + + redsocks_touch_client(client); + socks5->got_data = 1; + auto_relay_readcb(client, client->relay, client->client); +} + -void socks5_write_cb(struct bufferevent *buffev, void *_arg); static void auto_write_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; + struct timeval tv; redsocks_touch_client(client); @@ -133,32 +163,22 @@ static void auto_write_cb(struct bufferevent *buffev, void *_arg) /* We do not need to detect timeouts any more. The two ppers will handle it. */ - bufferevent_set_timeouts(buffev, NULL, NULL); -/* - if (EVBUFFER_LENGTH(buffev->input) == 0 && client->relay_evshut & EV_READ) + + if (!redsocks_start_relay(client)) { - if (auto_retry_or_drop(client)) - redsocks_drop_client(client); + /* overwrite theread callback to my function */ + client->relay->readcb = auto_relay_relayreadcb; } - else -*/ - redsocks_start_relay(client); -/* - if (bufferevent_enable(client->relay, EV_READ | EV_WRITE)) - redsocks_drop_client(client); -*/ } -/* + else if (client->state == socks5_direct) - redsocks_start_relay(client); -*/ + redsocks_log_error(client, LOG_DEBUG, "Should not be here!"); else socks5_write_cb(buffev, _arg); } -void socks5_read_cb(struct bufferevent *buffev, void *_arg); static void auto_read_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; @@ -192,20 +212,53 @@ static void auto_drop_relay(redsocks_client *client) } } -static int auto_retry(redsocks_client * client) +static void auto_retry(redsocks_client * client, int updcache) { if (client->state == socks5_direct) - bufferevent_disable(client->client, EV_READ| EV_WRITE); + bufferevent_disable(client->client, EV_READ| EV_WRITE); /* drop relay and update state, then retry with socks5 relay */ + if (updcache) + { + add_addr_to_cache(&client->destaddr); + redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %x", client->destaddr.sin_addr.s_addr); + } auto_drop_relay(client); client->state = socks5_new; - add_addr_to_cache(&client->destaddr); auto_connect_relay(client); /* Retry SOCKS5 proxy relay */ } +/* return 1 for drop, 0 for retry. */ +static int auto_retry_or_drop(redsocks_client * client) +{ + time_t now = redsocks_time(NULL); + socks5_client *socks5 = (void*)(client + 1); + + if (client->state == socks5_pre_detect) + { + if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) + { + auto_retry(client, 1); + return 0; + } + } + if ( client->state == socks5_direct && socks5->got_data == 0) + { + if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) + { + redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %x", client->destaddr.sin_addr.s_addr); + add_addr_to_cache(&client->destaddr); + /* Do not retry. The client may already sent data to relay, + and we have lost the data sent. + Connection can not be retried. + */ + return 1; + } + } -void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); - + + /* drop */ + return 1; +} static void auto_relay_connected(struct bufferevent *buffev, void *_arg) { @@ -235,28 +288,37 @@ static void auto_relay_connected(struct bufferevent *buffev, void *_arg) static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) { redsocks_client *client = _arg; + int saved_errno = errno; assert(buffev == client->relay || buffev == client->client); redsocks_touch_client(client); - if (what & EVBUFFER_TIMEOUT) - { - redsocks_log_errno(client, LOG_DEBUG, "Timeout %d", client->state); - /* In case timeout occurs for connecting relay, we try to connect - to target with SOCKS5 proxy. It is possible that the connection to - target can be set up a bit longer than the timeout value we set. - However, it is still better to make connection via proxy. */ - auto_retry(client); - return; - } - - redsocks_log_errno(client, LOG_DEBUG, "EOF %d", client->state); - if (client->state == socks5_pre_detect || client->state == socks5_direct ) + redsocks_log_errno(client, LOG_DEBUG, "Errno: %d, State: %d, what: %x", saved_errno, client->state, what); + if (buffev == client->relay) { - if (!auto_retry_or_drop(client)) + if ( client->state == socks5_pre_detect + && what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT)) + { + /* In case timeout occurs for connecting relay, we try to connect + to target with SOCKS5 proxy. It is possible that the connection to + target can be set up a bit longer than the timeout value we set. + However, it is still better to make connection via proxy. */ + auto_retry(client, 1); return; - } - + } + + if (client->state == socks5_pre_detect && saved_errno == ECONNRESET) + if (!auto_retry_or_drop(client)) + return; + + if (client->state == socks5_direct && what == (EVBUFFER_READ|EVBUFFER_ERROR) && saved_errno == ECONNRESET) + //&& saved_errno == ECONNRESET ) + { + if (!auto_retry_or_drop(client)) + return; + } + } + if (what == (EVBUFFER_READ|EVBUFFER_EOF)) { struct bufferevent *antiev; if (buffev == client->relay) @@ -270,8 +332,9 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) redsocks_shutdown(client, antiev, SHUT_WR); } else { + /* - errno = redsocks_socket_geterrno(client, buffev); + myerrno = red_socket_geterrno(buffev); redsocks_log_errno(client, LOG_NOTICE, "%s error, code " event_fmt_str, buffev == client->relay ? "relay" : "client", event_fmt(what)); @@ -281,24 +344,6 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) } -/* return 1 for drop, 0 for retry. */ -static int auto_retry_or_drop(redsocks_client * client) -{ - time_t now = redsocks_time(NULL); - socks5_client *socks5 = (void*)(client + 1); - - if (client->state == socks5_pre_detect || client->state == socks5_direct) - { - if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) - { - auto_retry(client); - return 0; - } - } - /* drop */ - return 1; -} - static void auto_connect_relay(redsocks_client *client) { socks5_client *socks5 = (void*)(client + 1); @@ -316,7 +361,7 @@ static void auto_connect_relay(redsocks_client *client) } client->relay = red_connect_relay2( client->state == socks5_pre_detect ? &client->destaddr : &client->instance->config.relayaddr, - auto_relay_connected, auto_event_error, client, + auto_relay_connected, auto_event_error, client, client->state == socks5_pre_detect ? &tv: NULL); socks5->time_connect_relay = redsocks_time(NULL); diff --git a/redsocks.c b/redsocks.c index 0ac66641..ac851052 100644 --- a/redsocks.c +++ b/redsocks.c @@ -35,7 +35,7 @@ #include "utils.h" -#define REDSOCKS_RELAY_HALFBUFF 1024*32 +#define REDSOCKS_RELAY_HALFBUFF 1024*8 void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); static void redsocks_relay_relayreadcb(struct bufferevent *from, void *_client); @@ -336,7 +336,7 @@ static void redsocks_relay_clientwritecb(struct bufferevent *to, void *_client) redsocks_relay_writecb(client, client->relay, client->client); } -void redsocks_start_relay(redsocks_client *client) +int redsocks_start_relay(redsocks_client *client) { int error; @@ -368,6 +368,7 @@ void redsocks_start_relay(redsocks_client *client) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); redsocks_drop_client(client); } + return error; } void redsocks_drop_client(redsocks_client *client) diff --git a/redsocks.h b/redsocks.h index 4f072d56..c4feb278 100644 --- a/redsocks.h +++ b/redsocks.h @@ -67,7 +67,7 @@ typedef struct redsocks_client_t { void redsocks_drop_client(redsocks_client *client); void redsocks_touch_client(redsocks_client *client); void redsocks_connect_relay(redsocks_client *client); -void redsocks_start_relay(redsocks_client *client); +int redsocks_start_relay(redsocks_client *client); typedef int (*size_comparator)(size_t a, size_t b); int sizes_equal(size_t a, size_t b); From 022f654210b7da91274880fb9a17cbcfcdc4cdc2 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 16 Jan 2013 07:38:41 +0800 Subject: [PATCH 009/192] Move implementation of 'direct' method from redsocks.c into direct.c. --- direct.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ redsocks.c | 55 +++---------------------------------- 2 files changed, 82 insertions(+), 52 deletions(-) create mode 100644 direct.c diff --git a/direct.c b/direct.c new file mode 100644 index 00000000..e21b4ee1 --- /dev/null +++ b/direct.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 Zhuofei Wang + * + */ + +#include +#include +#include +#include +#include +#include "parser.h" +#include "log.h" +#include "main.h" +#include "base.h" +#include "redsocks.h" +#include "utils.h" + + +int redsocks_start_relay(redsocks_client *client); +void redsocks_touch_client(redsocks_client *client); +void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg); +void redsocks_relay_connected(struct bufferevent *buffev, void *_arg); + +static void direct_relay_init(redsocks_client *client) +{ + client->state = 0; +} + +static void direct_instance_fini(redsocks_instance *instance) +{ +} + +static void direct_read_cb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + redsocks_touch_client(client); + if (client->state == 0) + { + client->state = 1; + redsocks_start_relay(client); + } +} + +static void direct_write_cb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + redsocks_touch_client(client); + if (client->state == 0) + { + client->state = 1; + redsocks_start_relay(client); + } +} + +void redsocks_direct_connect_relay(redsocks_client *client) +{ + client->relay = red_connect_relay(&client->destaddr, + redsocks_relay_connected, redsocks_event_error, client); + if (!client->relay) { + redsocks_log_errno(client, LOG_ERR, "red_connect_relay"); + redsocks_drop_client(client); + } +} + +relay_subsys direct_connect_subsys = +{ + .name = "direct", + .payload_len = 0, + .instance_payload_len = 0, + .readcb = direct_read_cb, + .writecb = direct_write_cb, + .init = direct_relay_init, + .instance_fini = direct_instance_fini, + .connect_relay = redsocks_direct_connect_relay, +}; + + +/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ +/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/redsocks.c b/redsocks.c index ac851052..7fa06842 100644 --- a/redsocks.c +++ b/redsocks.c @@ -40,47 +40,8 @@ void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int static void redsocks_relay_relayreadcb(struct bufferevent *from, void *_client); static void redsocks_relay_relaywritecb(struct bufferevent *from, void *_client); -void redsocks_direct_connect_relay(redsocks_client *client); -static void direct_relay_init(redsocks_client *client) -{ - client->state = 0; -} - -static void direct_instance_fini(redsocks_instance *instance) -{ -} -static void direct_read_cb(struct bufferevent *buffev, void *_arg) -{ - redsocks_client *client = _arg; - redsocks_touch_client(client); - if (client->state == 0) - { - client->state = 1; - redsocks_start_relay(client); - } -} -static void direct_write_cb(struct bufferevent *buffev, void *_arg) -{ - redsocks_client *client = _arg; - redsocks_touch_client(client); - if (client->state == 0) - { - client->state = 1; - redsocks_start_relay(client); - } -} -relay_subsys direct_connect_subsys = -{ - .name = "direct", - .payload_len = 0, - .instance_payload_len = 0, - .readcb = direct_read_cb, - .writecb = direct_write_cb, - .init = direct_relay_init, - .instance_fini = direct_instance_fini, - .connect_relay = redsocks_direct_connect_relay, -}; +extern relay_subsys direct_connect_subsys; extern relay_subsys http_connect_subsys; extern relay_subsys http_relay_subsys; extern relay_subsys socks4_subsys; @@ -457,7 +418,7 @@ static int redsocks_socket_geterrno(redsocks_client *client, struct bufferevent return pseudo_errno; } -static void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg) +void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg) { redsocks_client *client = _arg; assert(buffev == client->relay || buffev == client->client); @@ -591,7 +552,7 @@ int redsocks_write_helper( return redsocks_write_helper_ex(buffev, client, mkmessage, state, wm_only, wm_only); } -static void redsocks_relay_connected(struct bufferevent *buffev, void *_arg) +void redsocks_relay_connected(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; @@ -622,16 +583,6 @@ void redsocks_connect_relay(redsocks_client *client) redsocks_drop_client(client); } } -void redsocks_direct_connect_relay(redsocks_client *client) -{ - client->relay = red_connect_relay(&client->destaddr, - redsocks_relay_connected, redsocks_event_error, client); - if (!client->relay) { - redsocks_log_errno(client, LOG_ERR, "red_connect_relay"); - redsocks_drop_client(client); - } -} - static void redsocks_accept_backoff(int fd, short what, void *_arg) { From ccff1175212aed7c43bb35620cddba2478ca8e43 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 16 Jan 2013 10:10:03 +0800 Subject: [PATCH 010/192] Fix build error due to introduce of direct.c. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4e0358b1..8c552a35 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -OBJS := parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o autosocks.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o direct.o http-connect.o socks4.o socks5.o autosocks.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend From f1d7c81af90628105e3b82ea90184f780a1e6d1d Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 4 Mar 2013 16:48:44 +0800 Subject: [PATCH 011/192] New feature: connect to target via socks5 proxy automatically when we failed to connect to target directly. Connection will be retried via proxy when connection timeouts in about 10 seconds or connection is reset before data is read & writen successfully. --- Makefile | 9 ++- autosocks.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++------ main.c | 2 + redsocks.c | 6 +- 4 files changed, 204 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index 8c552a35..7b8b7ea8 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,16 @@ -OBJS := parser.o main.o redsocks.o log.o direct.o http-connect.o socks4.o socks5.o autosocks.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o direct.o socks5.o autosocks.o base.o base64.o utils.o gen/version.o SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks VERSION := 0.41 -LIBS := -levent -CFLAGS +=-static -fPIC -O2 -I /home/kitty/openwrt/backfire/build_dir/target-mips_uClibc-0.9.32/libevent-2.0.21-stable/ipkg-install/usr/include/ -L /home/kitty/openwrt/backfire/build_dir/target-mips_uClibc-0.9.32/libevent-2.0.21-stable/ipkg-install/usr/lib/ +LIBS := -levent_core +CFLAGS +=-static -flto -fPIC -Os -mips32r2 -mtune=mips32r2 -fno-caller-saves -mno-branch-likely \ + -I /home/kitty/openwrt/trunk//build_dir/target-mipsel_r2_uClibc-0.9.33.2/libevent-2.0.19-stable/ipkg-install/usr/include/ \ + -L /home/kitty/openwrt/trunk//build_dir/target-mipsel_r2_uClibc-0.9.33.2/libevent-2.0.19-stable/.libs/ override CFLAGS += -std=gnu99 -Wall +#LDFLAGS += -fwhole-program all: $(OUT) diff --git a/autosocks.c b/autosocks.c index a28e3a34..2664b853 100644 --- a/autosocks.c +++ b/autosocks.c @@ -38,6 +38,7 @@ typedef enum socks5_state_t { socks5_MAX, socks5_pre_detect=100, /* Introduce additional states to socks5 subsystem */ socks5_direct, + socks5_direct_confirmed, } socks5_state; typedef struct socks5_client_t { @@ -45,6 +46,7 @@ typedef struct socks5_client_t { int to_skip; // valid while reading last reply (after main request) time_t time_connect_relay; // timestamp when start to connect relay int got_data; + size_t data_sent; } socks5_client; @@ -55,11 +57,12 @@ void socks5_read_cb(struct bufferevent *buffev, void *_arg); void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); static int auto_retry_or_drop(redsocks_client * client); static void auto_connect_relay(redsocks_client *client); +static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); #define CIRCUIT_RESET_SECONDS 1 #define CONNECT_TIMEOUT_SECONDS 13 #define ADDR_CACHE_BLOCKS 64 -#define ADDR_CACHE_BLOCK_SIZE 32 +#define ADDR_CACHE_BLOCK_SIZE 16 #define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) static struct sockaddr_in addr_cache[ADDR_CACHE_BLOCKS][ADDR_CACHE_BLOCK_SIZE]; static int addr_cache_counters[ADDR_CACHE_BLOCKS]; @@ -124,11 +127,12 @@ void auto_socks5_client_init(redsocks_client *client) client->state = socks5_pre_detect; socks5->got_data = 0; + socks5->data_sent = 0; socks5->do_password = socks5_is_valid_cred(config->login, config->password); init_addr_cache(); } -static void auto_relay_readcb(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) +static void direct_relay_readcb_helper(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) { if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) @@ -140,34 +144,200 @@ static void auto_relay_readcb(redsocks_client *client, struct bufferevent *from, } } -static void auto_relay_relayreadcb(struct bufferevent *from, void *_client) + +static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) +{ + redsocks_client *client = _client; + socks5_client *socks5 = (void*)(client + 1); + + redsocks_touch_client(client); + + if (client->state == socks5_direct) + { + if (socks5->data_sent && socks5->got_data) + { + /* No CONNECTION RESET error occur after sending data, good. */ + client->state = socks5_direct_confirmed; + if (evbuffer_get_length(from->input)) + { + evbuffer_drain(from->input, socks5->data_sent); + socks5->data_sent = 0; + } + } + } + direct_relay_readcb_helper(client, client->client, client->relay); +} + + +static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) +{ + redsocks_client *client = _client; + socks5_client *socks5 = (void*)(client + 1); + + redsocks_touch_client(client); + if (!socks5->got_data) + socks5->got_data = EVBUFFER_LENGTH(from->input); + direct_relay_readcb_helper(client, client->relay, client->client); +} + +static size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src) +{ + int n, i; + size_t written = 0; + struct evbuffer_iovec *v; + struct evbuffer_iovec quick_v[5];/* a vector with 5 elements is usually enough */ + + size_t maxlen = dst->wm_write.high - EVBUFFER_LENGTH(dst->output); + maxlen = EVBUFFER_LENGTH(src->input)> maxlen?maxlen: EVBUFFER_LENGTH(src->input); + + n = evbuffer_peek(src->input, maxlen, NULL, NULL, 0); + if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) + v = malloc(sizeof(struct evbuffer_iovec)*n); + else + v = quick_v; + n = evbuffer_peek(src->input, maxlen, NULL, v, n); + for (i=0; i maxlen) + len = maxlen - written; + if (bufferevent_write(dst, v[i].iov_base, len)) + break; + /* We keep track of the bytes written separately; if we don't, + * we may write more than we need if the last chunk puts + * us over the limit. */ + written += len; + } + if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) + free(v); + return written; +} + +static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) +{ + redsocks_client *client = _client; + socks5_client *socks5 = (void*)(client + 1); + struct bufferevent * from = client->relay; + + redsocks_touch_client(client); + + if (EVBUFFER_LENGTH(from->input) == 0 && (client->relay_evshut & EV_READ)) { + redsocks_shutdown(client, to, SHUT_WR); + return; + } + if (client->state == socks5_direct) + { + if (!socks5->got_data) + socks5->got_data = EVBUFFER_LENGTH(from->input); + } + if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { + if (bufferevent_write_buffer(to, from->input) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } +} + + + +static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; socks5_client *socks5 = (void*)(client + 1); + struct bufferevent * from = client->client; redsocks_touch_client(client); - socks5->got_data = 1; - auto_relay_readcb(client, client->relay, client->client); + + if (EVBUFFER_LENGTH(from->input) == 0 && (client->client_evshut & EV_READ)) { + redsocks_shutdown(client, to, SHUT_WR); + return; + } + else if (client->state == socks5_direct ) + { + /* Not send or receive data. */ + if (!socks5->data_sent && !socks5->got_data) + { + /* Ensure we have data to send */ + if (EVBUFFER_LENGTH(from->input)) + { + /* copy data from input to output of relay */ + socks5->data_sent = copy_evbuffer (to, from); + redsocks_log_error(client, LOG_DEBUG, "not sent, not got %d", EVBUFFER_LENGTH(from->input)); + } + } + /* + * Relay reaceived data before writing to relay. + */ + else if (!socks5->data_sent && socks5->got_data) + { + redsocks_log_error(client, LOG_DEBUG, "not sent, got"); + socks5->data_sent = copy_evbuffer(to, from); + } + /* client->state = socks5_direct_confirmed; */ + /* We have writen data to relay, but got nothing until we are requested to + * write to it again. + */ + else if (socks5->data_sent && !socks5->got_data) + { + /* No response from relay and no CONNECTION RESET, + Send more data. + */ + redsocks_log_error(client, LOG_DEBUG, "sent, not got in:%d out:%d high:%d sent:%d", + evbuffer_get_length(from->input), + evbuffer_get_length(to->output), + to->wm_write.high, socks5->data_sent ); + /* TODO: Write more data? */ + if (EVBUFFER_LENGTH(to->output) < socks5->data_sent /* data is sent out, more or less */ + && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* read buffer is full */ + && EVBUFFER_LENGTH(from->input) == socks5->data_sent /* all data in read buffer is sent */ + ) + { + evbuffer_drain(from->input, socks5->data_sent); + socks5->data_sent = 0; + client->state = socks5_direct_confirmed; + } + } + /* We sent data to and got data from relay. */ + else if (socks5->data_sent && socks5->got_data) + { + /* No CONNECTION RESET error occur after sending data, good. */ + client->state = socks5_direct_confirmed; + redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", socks5->got_data); + if (evbuffer_get_length(from->input)) + { + evbuffer_drain(from->input, socks5->data_sent); + socks5->data_sent = 0; + } + } + } + + if (client->state == socks5_direct_confirmed) + { + if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { + if (bufferevent_write_buffer(to, from->input) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } + } } static void auto_write_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; - struct timeval tv; redsocks_touch_client(client); if (client->state == socks5_pre_detect) { client->state = socks5_direct; - /* We do not need to detect timeouts any more. - The two ppers will handle it. */ - if (!redsocks_start_relay(client)) { /* overwrite theread callback to my function */ - client->relay->readcb = auto_relay_relayreadcb; + client->client->readcb = direct_relay_clientreadcb; + client->client->writecb = direct_relay_clientwritecb; + client->relay->readcb = direct_relay_relayreadcb; + client->relay->writecb = direct_relay_relaywritecb; } } @@ -182,7 +352,6 @@ static void auto_write_cb(struct bufferevent *buffev, void *_arg) static void auto_read_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; - socks5_client *socks5 = (void*)(client + 1); redsocks_touch_client(client); @@ -203,7 +372,7 @@ static void auto_read_cb(struct bufferevent *buffev, void *_arg) static void auto_drop_relay(redsocks_client *client) { - redsocks_log_error(client, LOG_INFO, "dropping relay only"); + redsocks_log_error(client, LOG_DEBUG, "dropping relay only"); if (client->relay) { redsocks_close(EVENT_FD(&client->relay->ev_write)); @@ -241,20 +410,14 @@ static int auto_retry_or_drop(redsocks_client * client) return 0; } } - if ( client->state == socks5_direct && socks5->got_data == 0) + if ( client->state == socks5_direct) { - if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) +// if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) { - redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %x", client->destaddr.sin_addr.s_addr); - add_addr_to_cache(&client->destaddr); - /* Do not retry. The client may already sent data to relay, - and we have lost the data sent. - Connection can not be retried. - */ - return 1; + auto_retry(client, 0); + return 0; } } - /* drop */ return 1; @@ -276,6 +439,10 @@ static void auto_relay_connected(struct bufferevent *buffev, void *_arg) goto fail; } + /* We do not need to detect timeouts any more. + The two peers will handle it. */ + bufferevent_set_timeouts(client->relay, NULL, NULL); + client->relay->readcb = client->instance->relay_ss->readcb; client->relay->writecb = client->instance->relay_ss->writecb; client->relay->writecb(buffev, _arg); @@ -311,8 +478,8 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) if (!auto_retry_or_drop(client)) return; - if (client->state == socks5_direct && what == (EVBUFFER_READ|EVBUFFER_ERROR) && saved_errno == ECONNRESET) - //&& saved_errno == ECONNRESET ) + if (client->state == socks5_direct && what == (EVBUFFER_READ|EVBUFFER_ERROR) + && saved_errno == ECONNRESET ) { if (!auto_retry_or_drop(client)) return; diff --git a/main.c b/main.c index e6f6ec0d..626e61e9 100644 --- a/main.c +++ b/main.c @@ -35,8 +35,10 @@ extern app_subsys dnstc_subsys; app_subsys *subsystems[] = { &redsocks_subsys, &base_subsys, +/* &redudp_subsys, &dnstc_subsys, +*/ }; static const char *confname = "redsocks.conf"; diff --git a/redsocks.c b/redsocks.c index 7fa06842..1d82b23a 100644 --- a/redsocks.c +++ b/redsocks.c @@ -35,25 +35,29 @@ #include "utils.h" -#define REDSOCKS_RELAY_HALFBUFF 1024*8 +#define REDSOCKS_RELAY_HALFBUFF 1024*4 void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); static void redsocks_relay_relayreadcb(struct bufferevent *from, void *_client); static void redsocks_relay_relaywritecb(struct bufferevent *from, void *_client); +/* extern relay_subsys direct_connect_subsys; extern relay_subsys http_connect_subsys; extern relay_subsys http_relay_subsys; extern relay_subsys socks4_subsys; +*/ extern relay_subsys socks5_subsys; extern relay_subsys autosocks5_subsys; static relay_subsys *relay_subsystems[] = { &autosocks5_subsys, +/* &direct_connect_subsys, &http_connect_subsys, &http_relay_subsys, &socks4_subsys, +*/ &socks5_subsys, }; From 0d8eb7afaba0f6fb7d3072316a92983e150f0fde Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 7 Mar 2013 09:13:12 +0800 Subject: [PATCH 012/192] Restore subsystems removed by mistake. --- Makefile | 6 +++--- redsocks.c | 4 ---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 7b8b7ea8..c59981d5 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ -OBJS := parser.o main.o redsocks.o log.o direct.o socks5.o autosocks.o base.o base64.o utils.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o direct.o http-connect.o socks4.o socks5.o autosocks.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks VERSION := 0.41 -LIBS := -levent_core -CFLAGS +=-static -flto -fPIC -Os -mips32r2 -mtune=mips32r2 -fno-caller-saves -mno-branch-likely \ +LIBS := -levent +CFLAGS +=-fPIC -Os -mips32r2 -mtune=mips32r2 -fno-caller-saves -mno-branch-likely \ -I /home/kitty/openwrt/trunk//build_dir/target-mipsel_r2_uClibc-0.9.33.2/libevent-2.0.19-stable/ipkg-install/usr/include/ \ -L /home/kitty/openwrt/trunk//build_dir/target-mipsel_r2_uClibc-0.9.33.2/libevent-2.0.19-stable/.libs/ override CFLAGS += -std=gnu99 -Wall diff --git a/redsocks.c b/redsocks.c index 1d82b23a..201dcb67 100644 --- a/redsocks.c +++ b/redsocks.c @@ -41,23 +41,19 @@ void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int static void redsocks_relay_relayreadcb(struct bufferevent *from, void *_client); static void redsocks_relay_relaywritecb(struct bufferevent *from, void *_client); -/* extern relay_subsys direct_connect_subsys; extern relay_subsys http_connect_subsys; extern relay_subsys http_relay_subsys; extern relay_subsys socks4_subsys; -*/ extern relay_subsys socks5_subsys; extern relay_subsys autosocks5_subsys; static relay_subsys *relay_subsystems[] = { &autosocks5_subsys, -/* &direct_connect_subsys, &http_connect_subsys, &http_relay_subsys, &socks4_subsys, -*/ &socks5_subsys, }; From 1e591b565559b675d0ef2e5c333bb09fb6fb3d67 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 7 Mar 2013 09:38:31 +0800 Subject: [PATCH 013/192] Fix missing subsystems. --- main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/main.c b/main.c index 626e61e9..e6f6ec0d 100644 --- a/main.c +++ b/main.c @@ -35,10 +35,8 @@ extern app_subsys dnstc_subsys; app_subsys *subsystems[] = { &redsocks_subsys, &base_subsys, -/* &redudp_subsys, &dnstc_subsys, -*/ }; static const char *confname = "redsocks.conf"; From a818ff49d2eda468c3cde7a2d0e036eac9f98fc5 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 20 Mar 2013 20:30:08 +0800 Subject: [PATCH 014/192] Introduce new method 'autohttp-connect'. This method is behaves just like 'autosocks5' and is intent to support Goagent. --- Makefile | 2 +- autohttp-connect.c | 571 +++++++++++++++++++++++++++++++++++++++++++++ http-connect.c | 4 +- redsocks.c | 2 + 4 files changed, 576 insertions(+), 3 deletions(-) create mode 100644 autohttp-connect.c diff --git a/Makefile b/Makefile index c59981d5..aea9b030 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -OBJS := parser.o main.o redsocks.o log.o direct.o http-connect.o socks4.o socks5.o autosocks.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o direct.o autohttp-connect.o http-connect.o socks4.o socks5.o autosocks.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend diff --git a/autohttp-connect.c b/autohttp-connect.c new file mode 100644 index 00000000..f7bf39f2 --- /dev/null +++ b/autohttp-connect.c @@ -0,0 +1,571 @@ +/* redsocks - transparent TCP-to-proxy redirector + * Copyright (C) 2007-2011 Leonid Evdokimov + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * + * http-connect upstream module for redsocks + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "redsocks.h" +#include "http-auth.h" + +typedef enum httpc_state_t { + httpc_new, + httpc_request_sent, + httpc_reply_came, + httpc_headers_skipped, + httpc_MAX, + httpc_pre_detect=100, /* Introduce additional states to httpc subsystem */ + httpc_direct, + httpc_direct_confirmed, +} httpc_state; + +typedef struct httpc_client_t { + time_t time_connect_relay; // timestamp when start to connect relay + size_t data_recv; + size_t data_sent; +} httpc_client; + + +void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); +static int auto_retry_or_drop(redsocks_client * client); +static void auto_connect_relay(redsocks_client *client); +static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); + +#define HTTP_HEAD_WM_HIGH 4096 // that should be enough for one HTTP line. +extern void httpc_read_cb(struct bufferevent *buffev, void *_arg); +extern void httpc_write_cb(struct bufferevent *buffev, void *_arg); + +#define CIRCUIT_RESET_SECONDS 1 +#define CONNECT_TIMEOUT_SECONDS 13 +#define ADDR_CACHE_BLOCKS 64 +#define ADDR_CACHE_BLOCK_SIZE 16 +#define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) +static struct sockaddr_in addr_cache[ADDR_CACHE_BLOCKS][ADDR_CACHE_BLOCK_SIZE]; +static int addr_cache_counters[ADDR_CACHE_BLOCKS]; +static int addr_cache_pointers[ADDR_CACHE_BLOCKS]; +static int cache_init = 0; + +static void init_addr_cache() +{ + if (!cache_init) + { + memset((void *)addr_cache, 0, sizeof(addr_cache)); + memset((void *)addr_cache_counters, 0, sizeof(addr_cache_counters)); + memset((void *)addr_cache_pointers, 0, sizeof(addr_cache_pointers)); + cache_init = 1; + } +} + +static int is_addr_in_cache(const struct sockaddr_in * addr) +{ + /* get block index */ + int block = block_from_sockaddr_in(addr); + int count = addr_cache_counters[block]; + int first = addr_cache_pointers[block]; + int i = 0; + /* do reverse search for efficency */ + for ( i = count - 1; i >= 0; i -- ) + /* + if (0 == memcmp((void *)addr, (void *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE], sizeof(struct sockaddr_in))) +*/ + if (addr->sin_addr.s_addr == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].sin_addr.s_addr + && addr->sin_family == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].sin_family + ) + return 1; + + return 0; +} + +static void add_addr_to_cache(const struct sockaddr_in * addr) +{ + int block = block_from_sockaddr_in(addr); + int count = addr_cache_counters[block]; + int first = addr_cache_pointers[block]; + + if (count < ADDR_CACHE_BLOCK_SIZE) + { + memcpy((void *)&addr_cache[block][count], (void *) addr, sizeof(struct sockaddr_in)); + addr_cache_counters[block]++; + } + else + { + memcpy((void *)&addr_cache[block][first], (void *) addr, sizeof(struct sockaddr_in)); + addr_cache_pointers[block]++; + addr_cache_pointers[block]%=ADDR_CACHE_BLOCK_SIZE; + } +} + + + +static void auto_httpc_client_init(redsocks_client *client) +{ + httpc_client * httpc= (void*)(client + 1); + + client->state = httpc_pre_detect; + httpc->data_recv = 0; + httpc->data_sent = 0; + init_addr_cache(); + +} + +static void httpc_instance_fini(redsocks_instance *instance) +{ + http_auth *auth = (void*)(instance + 1); + free(auth->last_auth_query); + auth->last_auth_query = NULL; +} + + + + + +static void direct_relay_readcb_helper(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) +{ + if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { + if (bufferevent_write_buffer(to, from->input) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + } + else { + if (bufferevent_disable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); + } +} + + +static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) +{ + redsocks_client *client = _client; + httpc_client *httpc = (void*)(client + 1); + + redsocks_touch_client(client); + + if (client->state == httpc_direct) + { + if (httpc->data_sent && httpc->data_recv) + { + /* No CONNECTION RESET error occur after sending data, good. */ + client->state = httpc_direct_confirmed; + if (evbuffer_get_length(from->input)) + { + evbuffer_drain(from->input, httpc->data_sent); + httpc->data_sent = 0; + } + } + } + direct_relay_readcb_helper(client, client->client, client->relay); +} + + +static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) +{ + redsocks_client *client = _client; + httpc_client *httpc = (void*)(client + 1); + + redsocks_touch_client(client); + if (!httpc->data_recv) + httpc->data_recv = EVBUFFER_LENGTH(from->input); + direct_relay_readcb_helper(client, client->relay, client->client); +} + +static size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src) +{ + int n, i; + size_t written = 0; + struct evbuffer_iovec *v; + struct evbuffer_iovec quick_v[5];/* a vector with 5 elements is usually enough */ + + size_t maxlen = dst->wm_write.high - EVBUFFER_LENGTH(dst->output); + maxlen = EVBUFFER_LENGTH(src->input)> maxlen?maxlen: EVBUFFER_LENGTH(src->input); + + n = evbuffer_peek(src->input, maxlen, NULL, NULL, 0); + if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) + v = malloc(sizeof(struct evbuffer_iovec)*n); + else + v = quick_v; + n = evbuffer_peek(src->input, maxlen, NULL, v, n); + for (i=0; i maxlen) + len = maxlen - written; + if (bufferevent_write(dst, v[i].iov_base, len)) + break; + /* We keep track of the bytes written separately; if we don't, + * we may write more than we need if the last chunk puts + * us over the limit. */ + written += len; + } + if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) + free(v); + return written; +} + +static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) +{ + redsocks_client *client = _client; + httpc_client *httpc = (void*)(client + 1); + struct bufferevent * from = client->relay; + + redsocks_touch_client(client); + + if (EVBUFFER_LENGTH(from->input) == 0 && (client->relay_evshut & EV_READ)) { + redsocks_shutdown(client, to, SHUT_WR); + return; + } + if (client->state == httpc_direct) + { + if (!httpc->data_recv) + httpc->data_recv = EVBUFFER_LENGTH(from->input); + } + if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { + if (bufferevent_write_buffer(to, from->input) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } +} + + + +static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) +{ + redsocks_client *client = _client; + httpc_client *httpc = (void*)(client + 1); + struct bufferevent * from = client->client; + + redsocks_touch_client(client); + + if (EVBUFFER_LENGTH(from->input) == 0 && (client->client_evshut & EV_READ)) { + redsocks_shutdown(client, to, SHUT_WR); + return; + } + else if (client->state == httpc_direct ) + { + /* Not send or receive data. */ + if (!httpc->data_sent && !httpc->data_recv) + { + /* Ensure we have data to send */ + if (EVBUFFER_LENGTH(from->input)) + { + /* copy data from input to output of relay */ + httpc->data_sent = copy_evbuffer (to, from); + redsocks_log_error(client, LOG_DEBUG, "not sent, not got %d", EVBUFFER_LENGTH(from->input)); + } + } + /* + * Relay reaceived data before writing to relay. + */ + else if (!httpc->data_sent && httpc->data_recv) + { + redsocks_log_error(client, LOG_DEBUG, "not sent, got"); + httpc->data_sent = copy_evbuffer(to, from); + } + /* client->state = httpc_direct_confirmed; */ + /* We have writen data to relay, but got nothing until we are requested to + * write to it again. + */ + else if (httpc->data_sent && !httpc->data_recv) + { + /* No response from relay and no CONNECTION RESET, + Send more data. + */ + redsocks_log_error(client, LOG_DEBUG, "sent, not got in:%d out:%d high:%d sent:%d", + evbuffer_get_length(from->input), + evbuffer_get_length(to->output), + to->wm_write.high, httpc->data_sent ); + /* TODO: Write more data? */ + if (EVBUFFER_LENGTH(to->output) < httpc->data_sent /* data is sent out, more or less */ + && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* read buffer is full */ + && EVBUFFER_LENGTH(from->input) == httpc->data_sent /* all data in read buffer is sent */ + ) + { + evbuffer_drain(from->input, httpc->data_sent); + httpc->data_sent = 0; + client->state = httpc_direct_confirmed; + } + } + /* We sent data to and got data from relay. */ + else if (httpc->data_sent && httpc->data_recv) + { + /* No CONNECTION RESET error occur after sending data, good. */ + client->state = httpc_direct_confirmed; + redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", httpc->data_recv); + if (evbuffer_get_length(from->input)) + { + evbuffer_drain(from->input, httpc->data_sent); + httpc->data_sent = 0; + } + } + } + + if (client->state == httpc_direct_confirmed) + { + if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { + if (bufferevent_write_buffer(to, from->input) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } + } +} + + +static void auto_write_cb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + + redsocks_touch_client(client); + + if (client->state == httpc_pre_detect) { + client->state = httpc_direct; + + if (!redsocks_start_relay(client)) + { + /* overwrite theread callback to my function */ + client->client->readcb = direct_relay_clientreadcb; + client->client->writecb = direct_relay_clientwritecb; + client->relay->readcb = direct_relay_relayreadcb; + client->relay->writecb = direct_relay_relaywritecb; + } + } + + else if (client->state == httpc_direct) + redsocks_log_error(client, LOG_DEBUG, "Should not be here!"); + else + httpc_write_cb(buffev, _arg); +} + + + +static void auto_read_cb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + + redsocks_touch_client(client); + + if (client->state == httpc_pre_detect) { + /* Should never be here */ +/* + client->state = httpc_direct; + redsocks_start_relay(client); +*/ + } + else if (client->state == httpc_direct) { + /* if (EVBUFFER_LENGTH(buffev->input) == 0 && client->relay_evshut & EV_READ) */ + } + else { + httpc_read_cb(buffev, _arg); + } +} + +static void auto_drop_relay(redsocks_client *client) +{ + redsocks_log_error(client, LOG_DEBUG, "dropping relay only"); + + if (client->relay) { + redsocks_close(EVENT_FD(&client->relay->ev_write)); + bufferevent_free(client->relay); + client->relay = NULL; + } +} + +static void auto_retry(redsocks_client * client, int updcache) +{ + if (client->state == httpc_direct) + bufferevent_disable(client->client, EV_READ| EV_WRITE); + /* drop relay and update state, then retry with httpc relay */ + if (updcache) + { + add_addr_to_cache(&client->destaddr); + redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %x", client->destaddr.sin_addr.s_addr); + } + auto_drop_relay(client); + client->state = httpc_new; + auto_connect_relay(client); /* Retry SOCKS5 proxy relay */ +} + +/* return 1 for drop, 0 for retry. */ +static int auto_retry_or_drop(redsocks_client * client) +{ + time_t now = redsocks_time(NULL); + httpc_client *httpc = (void*)(client + 1); + + if (client->state == httpc_pre_detect) + { + if (now - httpc->time_connect_relay <= CIRCUIT_RESET_SECONDS) + { + auto_retry(client, 1); + return 0; + } + } + if ( client->state == httpc_direct) + { +// if (now - httpc->time_connect_relay <= CIRCUIT_RESET_SECONDS) + { + auto_retry(client, 0); + return 0; + } + } + + /* drop */ + return 1; +} + +static void auto_relay_connected(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + + assert(buffev == client->relay); + + redsocks_touch_client(client); + + if (!red_is_socket_connected_ok(buffev)) { + if (client->state == httpc_pre_detect && !auto_retry_or_drop(client)) + return; + + redsocks_log_error(client, LOG_DEBUG, "failed to connect to proxy"); + goto fail; + } + + /* We do not need to detect timeouts any more. + The two peers will handle it. */ + bufferevent_set_timeouts(client->relay, NULL, NULL); + + client->relay->readcb = client->instance->relay_ss->readcb; + client->relay->writecb = client->instance->relay_ss->writecb; + client->relay->writecb(buffev, _arg); + return; + +fail: + redsocks_drop_client(client); +} + +static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) +{ + redsocks_client *client = _arg; + int saved_errno = errno; + assert(buffev == client->relay || buffev == client->client); + + redsocks_touch_client(client); + + redsocks_log_errno(client, LOG_DEBUG, "Errno: %d, State: %d, what: %x", saved_errno, client->state, what); + if (buffev == client->relay) + { + if ( client->state == httpc_pre_detect + && what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT)) + { + /* In case timeout occurs for connecting relay, we try to connect + to target with SOCKS5 proxy. It is possible that the connection to + target can be set up a bit longer than the timeout value we set. + However, it is still better to make connection via proxy. */ + auto_retry(client, 1); + return; + } + + if (client->state == httpc_pre_detect && saved_errno == ECONNRESET) + if (!auto_retry_or_drop(client)) + return; + + if (client->state == httpc_direct && what == (EVBUFFER_READ|EVBUFFER_ERROR) + && saved_errno == ECONNRESET ) + { + if (!auto_retry_or_drop(client)) + return; + } + } + + if (what == (EVBUFFER_READ|EVBUFFER_EOF)) { + struct bufferevent *antiev; + if (buffev == client->relay) + antiev = client->client; + else + antiev = client->relay; + + redsocks_shutdown(client, buffev, SHUT_RD); + + if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0) + redsocks_shutdown(client, antiev, SHUT_WR); + } + else { + + /* + myerrno = red_socket_geterrno(buffev); + redsocks_log_errno(client, LOG_NOTICE, "%s error, code " event_fmt_str, + buffev == client->relay ? "relay" : "client", + event_fmt(what)); + */ + redsocks_drop_client(client); + } +} + + +static void auto_connect_relay(redsocks_client *client) +{ + httpc_client *httpc = (void*)(client + 1); + struct timeval tv; + tv.tv_sec = CONNECT_TIMEOUT_SECONDS; + tv.tv_usec = 0; + + if (client->state == httpc_pre_detect) + { + if (is_addr_in_cache(&client->destaddr)) + { + client->state = httpc_new; /* Connect SOCKS5 */ + redsocks_log_error(client, LOG_DEBUG, "Found in cache"); + } + } + client->relay = red_connect_relay2( client->state == httpc_pre_detect + ? &client->destaddr : &client->instance->config.relayaddr, + auto_relay_connected, auto_event_error, client, + client->state == httpc_pre_detect ? &tv: NULL); + + httpc->time_connect_relay = redsocks_time(NULL); + + if (!client->relay) { + redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); + redsocks_drop_client(client); + } +} + + + + + +relay_subsys autohttp_connect_subsys = +{ + .name = "autohttp-connect", + .payload_len = sizeof(httpc_client), + .instance_payload_len = sizeof(http_auth), + .instance_fini = httpc_instance_fini, + .readcb = auto_read_cb, + .writecb = auto_write_cb, + .init = auto_httpc_client_init, + .connect_relay = auto_connect_relay, +}; + + +/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ +/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/http-connect.c b/http-connect.c index e2435282..9fe7f50e 100644 --- a/http-connect.c +++ b/http-connect.c @@ -75,7 +75,7 @@ static char *get_auth_request_header(struct evbuffer *buf) } } -static void httpc_read_cb(struct bufferevent *buffev, void *_arg) +void httpc_read_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; int dropped = 0; @@ -254,7 +254,7 @@ static struct evbuffer *httpc_mkconnect(redsocks_client *client) } -static void httpc_write_cb(struct bufferevent *buffev, void *_arg) +void httpc_write_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; diff --git a/redsocks.c b/redsocks.c index 201dcb67..27b1badf 100644 --- a/redsocks.c +++ b/redsocks.c @@ -47,9 +47,11 @@ extern relay_subsys http_relay_subsys; extern relay_subsys socks4_subsys; extern relay_subsys socks5_subsys; extern relay_subsys autosocks5_subsys; +extern relay_subsys autohttp_connect_subsys; static relay_subsys *relay_subsystems[] = { &autosocks5_subsys, + &autohttp_connect_subsys, &direct_connect_subsys, &http_connect_subsys, &http_relay_subsys, From e792db7b0528b7aeb89f76396e116663102d331b Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 7 May 2013 10:38:43 +0800 Subject: [PATCH 015/192] Update Makefile and README. --- README | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README b/README index ca354876..a77717ad 100644 --- a/README +++ b/README @@ -26,6 +26,10 @@ proxy immediately unless the addresses are removed from cache by code logic. The advantage of this method is that you do not need to set thousands entries in iptables for blocked sites manually. It is especially useful for proxying Youtube which has so many sub-domains for video content cache. +3. new 'autohttp-connect' method. + This method behaves similar as 'autoscoks5' method except the proxy +to be used must support HTTP CONNECT method. This method is useful for +using redsocks together with GoProxy. HOW TO BUILD: Since this variant of redsocks is customized for running with Openwrt, please From aad8c81f2617113b548798f1f6c02f35bbbd1aa3 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 7 May 2013 10:41:45 +0800 Subject: [PATCH 016/192] Update Makefile to remove optimization for MIPS. --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index aea9b030..e64be01e 100644 --- a/Makefile +++ b/Makefile @@ -6,9 +6,9 @@ OUT := redsocks VERSION := 0.41 LIBS := -levent -CFLAGS +=-fPIC -Os -mips32r2 -mtune=mips32r2 -fno-caller-saves -mno-branch-likely \ - -I /home/kitty/openwrt/trunk//build_dir/target-mipsel_r2_uClibc-0.9.33.2/libevent-2.0.19-stable/ipkg-install/usr/include/ \ - -L /home/kitty/openwrt/trunk//build_dir/target-mipsel_r2_uClibc-0.9.33.2/libevent-2.0.19-stable/.libs/ +CFLAGS +=-fPIC -Os \ + -I ~/openwrt/trunk/build_dir/target-mipsel_r2_uClibc-0.9.33.2/libevent-2.0.19-stable/ipkg-install/usr/include/ \ + -L ~/openwrt/trunk/build_dir/target-mipsel_r2_uClibc-0.9.33.2/libevent-2.0.19-stable/.libs/ override CFLAGS += -std=gnu99 -Wall #LDFLAGS += -fwhole-program From 396e1ea03c15160ac41de690106713778260affb Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 22 Aug 2013 11:33:56 +0800 Subject: [PATCH 017/192] Fix: hung half-closed connections never get closed. --- autohttp-connect.c | 75 ++++++++++++++++--------------------------- autosocks.c | 79 ++++++++++++++++++---------------------------- redsocks.c | 6 +++- utils.c | 46 +++++++++++++++++++++++++++ utils.h | 2 ++ 5 files changed, 111 insertions(+), 97 deletions(-) diff --git a/autohttp-connect.c b/autohttp-connect.c index f7bf39f2..e6e13a4a 100644 --- a/autohttp-connect.c +++ b/autohttp-connect.c @@ -60,7 +60,8 @@ extern void httpc_read_cb(struct bufferevent *buffev, void *_arg); extern void httpc_write_cb(struct bufferevent *buffev, void *_arg); #define CIRCUIT_RESET_SECONDS 1 -#define CONNECT_TIMEOUT_SECONDS 13 +#define CONNECT_TIMEOUT_SECONDS 10 +#define SHUTDOWN_TIMEOUT_SECONDS 5 #define ADDR_CACHE_BLOCKS 64 #define ADDR_CACHE_BLOCK_SIZE 16 #define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) @@ -191,38 +192,6 @@ static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) direct_relay_readcb_helper(client, client->relay, client->client); } -static size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src) -{ - int n, i; - size_t written = 0; - struct evbuffer_iovec *v; - struct evbuffer_iovec quick_v[5];/* a vector with 5 elements is usually enough */ - - size_t maxlen = dst->wm_write.high - EVBUFFER_LENGTH(dst->output); - maxlen = EVBUFFER_LENGTH(src->input)> maxlen?maxlen: EVBUFFER_LENGTH(src->input); - - n = evbuffer_peek(src->input, maxlen, NULL, NULL, 0); - if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) - v = malloc(sizeof(struct evbuffer_iovec)*n); - else - v = quick_v; - n = evbuffer_peek(src->input, maxlen, NULL, v, n); - for (i=0; i maxlen) - len = maxlen - written; - if (bufferevent_write(dst, v[i].iov_base, len)) - break; - /* We keep track of the bytes written separately; if we don't, - * we may write more than we need if the last chunk puts - * us over the limit. */ - written += len; - } - if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) - free(v); - return written; -} - static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; @@ -271,7 +240,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) if (EVBUFFER_LENGTH(from->input)) { /* copy data from input to output of relay */ - httpc->data_sent = copy_evbuffer (to, from); + httpc->data_sent = copy_evbuffer (to, from, 0); redsocks_log_error(client, LOG_DEBUG, "not sent, not got %d", EVBUFFER_LENGTH(from->input)); } } @@ -281,11 +250,12 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) else if (!httpc->data_sent && httpc->data_recv) { redsocks_log_error(client, LOG_DEBUG, "not sent, got"); - httpc->data_sent = copy_evbuffer(to, from); + httpc->data_sent = copy_evbuffer(to, from, 0); } /* client->state = httpc_direct_confirmed; */ /* We have writen data to relay, but got nothing until we are requested to * write to it again. + * r */ else if (httpc->data_sent && !httpc->data_recv) { @@ -296,9 +266,13 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) evbuffer_get_length(from->input), evbuffer_get_length(to->output), to->wm_write.high, httpc->data_sent ); - /* TODO: Write more data? */ - if (EVBUFFER_LENGTH(to->output) < httpc->data_sent /* data is sent out, more or less */ - && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* read buffer is full */ + /* Write more data util input buffer is full */ + if (EVBUFFER_LENGTH(from->input)- httpc->data_sent > 0) /* we have more data waiting to be sent */ + { + httpc->data_sent += copy_evbuffer(to, from, httpc->data_sent); + } + else if (EVBUFFER_LENGTH(to->output) < httpc->data_sent /* data is sent out, more or less */ + /* && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* read buffer is full */ && EVBUFFER_LENGTH(from->input) == httpc->data_sent /* all data in read buffer is sent */ ) { @@ -400,7 +374,8 @@ static void auto_retry(redsocks_client * client, int updcache) if (updcache) { add_addr_to_cache(&client->destaddr); - redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %x", client->destaddr.sin_addr.s_addr); + redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %s", + inet_ntoa(client->destaddr.sin_addr)); } auto_drop_relay(client); client->state = httpc_new; @@ -471,7 +446,7 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) redsocks_touch_client(client); - redsocks_log_errno(client, LOG_DEBUG, "Errno: %d, State: %d, what: %x", saved_errno, client->state, what); + redsocks_log_errno(client, LOG_DEBUG, "Errno: %d, State: %d, what: " event_fmt_str, saved_errno, client->state, event_fmt(what)); if (buffev == client->relay) { if ( client->state == httpc_pre_detect @@ -508,15 +483,21 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0) redsocks_shutdown(client, antiev, SHUT_WR); + if (antiev != NULL && bufferevent_get_enabled(antiev) & EV_READ) + { + /* Set up a timer on read so that we can ensure the connection + can be taken down when client socket is closed without + notification. This is required to prevent from hung half-closed + connections. + */ + redsocks_log_error(client, LOG_DEBUG, "Setup force closing timer" ); + struct timeval tv; + tv.tv_sec = SHUTDOWN_TIMEOUT_SECONDS; + tv.tv_usec = 0; + bufferevent_set_timeouts(antiev, &tv, NULL); + } } else { - - /* - myerrno = red_socket_geterrno(buffev); - redsocks_log_errno(client, LOG_NOTICE, "%s error, code " event_fmt_str, - buffev == client->relay ? "relay" : "client", - event_fmt(what)); - */ redsocks_drop_client(client); } } diff --git a/autosocks.c b/autosocks.c index 2664b853..8ec6f57a 100644 --- a/autosocks.c +++ b/autosocks.c @@ -60,7 +60,8 @@ static void auto_connect_relay(redsocks_client *client); static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); #define CIRCUIT_RESET_SECONDS 1 -#define CONNECT_TIMEOUT_SECONDS 13 +#define CONNECT_TIMEOUT_SECONDS 10 +#define SHUTDOWN_TIMEOUT_SECONDS 5 #define ADDR_CACHE_BLOCKS 64 #define ADDR_CACHE_BLOCK_SIZE 16 #define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) @@ -180,38 +181,6 @@ static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) direct_relay_readcb_helper(client, client->relay, client->client); } -static size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src) -{ - int n, i; - size_t written = 0; - struct evbuffer_iovec *v; - struct evbuffer_iovec quick_v[5];/* a vector with 5 elements is usually enough */ - - size_t maxlen = dst->wm_write.high - EVBUFFER_LENGTH(dst->output); - maxlen = EVBUFFER_LENGTH(src->input)> maxlen?maxlen: EVBUFFER_LENGTH(src->input); - - n = evbuffer_peek(src->input, maxlen, NULL, NULL, 0); - if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) - v = malloc(sizeof(struct evbuffer_iovec)*n); - else - v = quick_v; - n = evbuffer_peek(src->input, maxlen, NULL, v, n); - for (i=0; i maxlen) - len = maxlen - written; - if (bufferevent_write(dst, v[i].iov_base, len)) - break; - /* We keep track of the bytes written separately; if we don't, - * we may write more than we need if the last chunk puts - * us over the limit. */ - written += len; - } - if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) - free(v); - return written; -} - static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; @@ -260,7 +229,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) if (EVBUFFER_LENGTH(from->input)) { /* copy data from input to output of relay */ - socks5->data_sent = copy_evbuffer (to, from); + socks5->data_sent = copy_evbuffer (to, from, 0); redsocks_log_error(client, LOG_DEBUG, "not sent, not got %d", EVBUFFER_LENGTH(from->input)); } } @@ -270,7 +239,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) else if (!socks5->data_sent && socks5->got_data) { redsocks_log_error(client, LOG_DEBUG, "not sent, got"); - socks5->data_sent = copy_evbuffer(to, from); + socks5->data_sent = copy_evbuffer(to, from, 0); } /* client->state = socks5_direct_confirmed; */ /* We have writen data to relay, but got nothing until we are requested to @@ -285,9 +254,13 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) evbuffer_get_length(from->input), evbuffer_get_length(to->output), to->wm_write.high, socks5->data_sent ); - /* TODO: Write more data? */ - if (EVBUFFER_LENGTH(to->output) < socks5->data_sent /* data is sent out, more or less */ - && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* read buffer is full */ + /* Write more data util input buffer is full */ + if (EVBUFFER_LENGTH(from->input)- socks5->data_sent > 0) /* we have more data waiting to be sent */ + { + socks5->data_sent += copy_evbuffer(to, from, socks5->data_sent); + } + else if (EVBUFFER_LENGTH(to->output) < socks5->data_sent /* data is sent out, more or less */ + /* && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* read buffer is full */ && EVBUFFER_LENGTH(from->input) == socks5->data_sent /* all data in read buffer is sent */ ) { @@ -389,7 +362,8 @@ static void auto_retry(redsocks_client * client, int updcache) if (updcache) { add_addr_to_cache(&client->destaddr); - redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %x", client->destaddr.sin_addr.s_addr); + redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %s", + inet_ntoa(client->destaddr.sin_addr)); } auto_drop_relay(client); client->state = socks5_new; @@ -460,14 +434,15 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) redsocks_touch_client(client); - redsocks_log_errno(client, LOG_DEBUG, "Errno: %d, State: %d, what: %x", saved_errno, client->state, what); + redsocks_log_errno(client, LOG_DEBUG, "Errno: %d, State: %d, what: " event_fmt_str, saved_errno, client->state, event_fmt(what)); if (buffev == client->relay) { + if ( client->state == socks5_pre_detect && what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT)) { - /* In case timeout occurs for connecting relay, we try to connect - to target with SOCKS5 proxy. It is possible that the connection to + /* In case timeout occurs while connecting relay, we try to connect + to target via SOCKS5 proxy. It is possible that the connection to target can be set up a bit longer than the timeout value we set. However, it is still better to make connection via proxy. */ auto_retry(client, 1); @@ -497,15 +472,21 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0) redsocks_shutdown(client, antiev, SHUT_WR); + if (antiev != NULL && bufferevent_get_enabled(antiev) & EV_READ) + { + /* Set up a timer on read so that we can ensure the connection + can be taken down when client socket is closed without + notification. This is required to prevent from hung half-closed + connections. + */ + redsocks_log_error(client, LOG_DEBUG, "Setup force closing timer" ); + struct timeval tv; + tv.tv_sec = SHUTDOWN_TIMEOUT_SECONDS; + tv.tv_usec = 0; + bufferevent_set_timeouts(antiev, &tv, NULL); + } } else { - - /* - myerrno = red_socket_geterrno(buffev); - redsocks_log_errno(client, LOG_NOTICE, "%s error, code " event_fmt_str, - buffev == client->relay ? "relay" : "client", - event_fmt(what)); - */ redsocks_drop_client(client); } } diff --git a/redsocks.c b/redsocks.c index 27b1badf..151aa019 100644 --- a/redsocks.c +++ b/redsocks.c @@ -757,13 +757,17 @@ static void redsocks_debug_dump_instance(redsocks_instance *instance, time_t now const char *s_client_evshut = redsocks_evshut_str(client->client_evshut); const char *s_relay_evshut = redsocks_evshut_str(client->relay_evshut); - redsocks_log_error(client, LOG_DEBUG, "client: %i (%s)%s%s, relay: %i (%s)%s%s, age: %li sec, idle: %li sec.", + redsocks_log_error(client, LOG_DEBUG, "client: %i (%s)%s%s input %d output %d, relay: %i (%s)%s%s input %d output %d, age: %li sec, idle: %li sec.", EVENT_FD(&client->client->ev_write), redsocks_event_str(client->client->enabled), s_client_evshut[0] ? " " : "", s_client_evshut, + EVBUFFER_LENGTH(client->client->input), + EVBUFFER_LENGTH(client->client->output), EVENT_FD(&client->relay->ev_write), redsocks_event_str(client->relay->enabled), s_relay_evshut[0] ? " " : "", s_relay_evshut, + EVBUFFER_LENGTH(client->relay->input), + EVBUFFER_LENGTH(client->relay->output), now - client->first_event, now - client->last_event); } diff --git a/utils.c b/utils.c index 0ac2eea4..eb82b425 100644 --- a/utils.c +++ b/utils.c @@ -302,4 +302,50 @@ char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_si return buffer; } +/* copy event buffer from source to destination as much as possible. + * If parameter skip is not zero, copy will start from the number of skip bytes. + */ +size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, size_t skip) +{ + int n, i; + size_t written = 0; + struct evbuffer_iovec *v; + struct evbuffer_iovec quick_v[5];/* a vector with 5 elements is usually enough */ + + size_t maxlen = dst->wm_write.high - EVBUFFER_LENGTH(dst->output); + maxlen = EVBUFFER_LENGTH(src->input) - skip> maxlen?maxlen: EVBUFFER_LENGTH(src->input)-skip; + + n = evbuffer_peek(src->input, maxlen, NULL, NULL, 0); + if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) + v = malloc(sizeof(struct evbuffer_iovec)*n); + else + v = quick_v; + n = evbuffer_peek(src->input, maxlen, NULL, v, n); + for (i=0; i= len) + { + skip -= len; + continue; + } + else + { + len -= skip; + } + if (written + len > maxlen) + len = maxlen - written; + if (bufferevent_write(dst, v[i].iov_base+skip, len)) + break; + skip = 0; + /* We keep track of the bytes written separately; if we don't, + * we may write more than we need if the last chunk puts + * us over the limit. */ + written += len; + } + if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) + free(v); + return written; +} + + /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ diff --git a/utils.h b/utils.h index 362ae7c3..aefaea91 100644 --- a/utils.h +++ b/utils.h @@ -49,6 +49,8 @@ int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *froma int fcntl_nonblock(int fd); +size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, size_t skip); + #define event_fmt_str "%s|%s|%s|%s|%s|0x%x" #define event_fmt(what) \ (what) & EVBUFFER_READ ? "EVBUFFER_READ" : "0", \ From e3afb4a1908f012e947f9f4aa7376cb8730b9a64 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 22 Aug 2013 13:47:32 +0800 Subject: [PATCH 018/192] Fix: Copying buffer starting with non-zero offset fails --- utils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils.c b/utils.c index eb82b425..0d54126d 100644 --- a/utils.c +++ b/utils.c @@ -315,12 +315,12 @@ size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, s size_t maxlen = dst->wm_write.high - EVBUFFER_LENGTH(dst->output); maxlen = EVBUFFER_LENGTH(src->input) - skip> maxlen?maxlen: EVBUFFER_LENGTH(src->input)-skip; - n = evbuffer_peek(src->input, maxlen, NULL, NULL, 0); + n = evbuffer_peek(src->input, maxlen+skip, NULL, NULL, 0); if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) v = malloc(sizeof(struct evbuffer_iovec)*n); else v = quick_v; - n = evbuffer_peek(src->input, maxlen, NULL, v, n); + n = evbuffer_peek(src->input, maxlen+skip, NULL, v, n); for (i=0; i= len) From cdbb1bbbb96ecc8bd1ec99f721421f0091b30f8b Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 31 Aug 2013 21:56:23 +0800 Subject: [PATCH 019/192] Fix: confirm before circuit reset is received. --- autosocks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autosocks.c b/autosocks.c index 8ec6f57a..8222a038 100644 --- a/autosocks.c +++ b/autosocks.c @@ -260,7 +260,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) socks5->data_sent += copy_evbuffer(to, from, socks5->data_sent); } else if (EVBUFFER_LENGTH(to->output) < socks5->data_sent /* data is sent out, more or less */ - /* && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* read buffer is full */ + && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* do not confirm unless read buffer is full */ && EVBUFFER_LENGTH(from->input) == socks5->data_sent /* all data in read buffer is sent */ ) { @@ -384,7 +384,7 @@ static int auto_retry_or_drop(redsocks_client * client) return 0; } } - if ( client->state == socks5_direct) + else if ( client->state == socks5_direct) { // if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) { From 99772ae937b04d7678e7cc8ab8332063e0610027 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 18 Oct 2013 12:07:08 +0800 Subject: [PATCH 020/192] Introduce audit to clean up hung connections. --- autohttp-connect.c | 16 ++------- autosocks.c | 16 ++------- redsocks.c | 87 +++++++++++++++++++++++++++++++++++++++++++--- utils.c | 28 +++++++-------- 4 files changed, 100 insertions(+), 47 deletions(-) diff --git a/autohttp-connect.c b/autohttp-connect.c index e6e13a4a..e28540a3 100644 --- a/autohttp-connect.c +++ b/autohttp-connect.c @@ -61,7 +61,6 @@ extern void httpc_write_cb(struct bufferevent *buffev, void *_arg); #define CIRCUIT_RESET_SECONDS 1 #define CONNECT_TIMEOUT_SECONDS 10 -#define SHUTDOWN_TIMEOUT_SECONDS 5 #define ADDR_CACHE_BLOCKS 64 #define ADDR_CACHE_BLOCK_SIZE 16 #define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) @@ -149,6 +148,8 @@ static void direct_relay_readcb_helper(redsocks_client *client, struct buffereve if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } else { if (bufferevent_disable(from, EV_READ) == -1) @@ -483,19 +484,6 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0) redsocks_shutdown(client, antiev, SHUT_WR); - if (antiev != NULL && bufferevent_get_enabled(antiev) & EV_READ) - { - /* Set up a timer on read so that we can ensure the connection - can be taken down when client socket is closed without - notification. This is required to prevent from hung half-closed - connections. - */ - redsocks_log_error(client, LOG_DEBUG, "Setup force closing timer" ); - struct timeval tv; - tv.tv_sec = SHUTDOWN_TIMEOUT_SECONDS; - tv.tv_usec = 0; - bufferevent_set_timeouts(antiev, &tv, NULL); - } } else { redsocks_drop_client(client); diff --git a/autosocks.c b/autosocks.c index 8222a038..4814b145 100644 --- a/autosocks.c +++ b/autosocks.c @@ -61,7 +61,6 @@ static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); #define CIRCUIT_RESET_SECONDS 1 #define CONNECT_TIMEOUT_SECONDS 10 -#define SHUTDOWN_TIMEOUT_SECONDS 5 #define ADDR_CACHE_BLOCKS 64 #define ADDR_CACHE_BLOCK_SIZE 16 #define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) @@ -138,6 +137,8 @@ static void direct_relay_readcb_helper(redsocks_client *client, struct buffereve if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } else { if (bufferevent_disable(from, EV_READ) == -1) @@ -472,19 +473,6 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0) redsocks_shutdown(client, antiev, SHUT_WR); - if (antiev != NULL && bufferevent_get_enabled(antiev) & EV_READ) - { - /* Set up a timer on read so that we can ensure the connection - can be taken down when client socket is closed without - notification. This is required to prevent from hung half-closed - connections. - */ - redsocks_log_error(client, LOG_DEBUG, "Setup force closing timer" ); - struct timeval tv; - tv.tv_sec = SHUTDOWN_TIMEOUT_SECONDS; - tv.tv_usec = 0; - bufferevent_set_timeouts(antiev, &tv, NULL); - } } else { redsocks_drop_client(client); diff --git a/redsocks.c b/redsocks.c index 151aa019..56ccdc07 100644 --- a/redsocks.c +++ b/redsocks.c @@ -36,6 +36,7 @@ #define REDSOCKS_RELAY_HALFBUFF 1024*4 +#define REDSOCKS_AUDIT_INTERVAL 60 void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); static void redsocks_relay_relayreadcb(struct bufferevent *from, void *_client); @@ -247,6 +248,8 @@ static void redsocks_relay_readcb(redsocks_client *client, struct bufferevent *f if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } else { if (bufferevent_disable(from, EV_READ) == -1) @@ -402,11 +405,6 @@ void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int redsocks_log_error(client, LOG_DEBUG, "both client and server disconnected"); redsocks_drop_client(client); } - else - { - if (how == SHUT_WR && buffev == client->relay && client->relay->enabled == 0) - redsocks_drop_client(client); - } } // I assume that -1 is invalid errno value @@ -783,6 +781,64 @@ static void redsocks_debug_dump(int sig, short what, void *_arg) redsocks_debug_dump_instance(instance, now); } +/* Audit is required to clean up hung connections. + * Not all connections are closed gracefully by both ends. In any case that + * either far end of client or far end of relay does not close connection + * gracefully, we got hung connections. + */ +static void redsocks_audit_instance(redsocks_instance *instance) +{ + redsocks_client *tmp, *client = NULL; + time_t now = redsocks_time(NULL); + int drop_it = 0; + + log_error(LOG_DEBUG, "Audit client list for instance %p:", instance); + list_for_each_entry_safe(client, tmp, &instance->clients, list) { + drop_it = 0; + + if (now - client->last_event >= REDSOCKS_AUDIT_INTERVAL){ + /* Only take actions if no touch of the client for at least an audit cycle.*/ + /* drop this client if either end disconnected */ + if ((client->client_evshut == EV_WRITE && client->relay_evshut == EV_READ) + || (client->client_evshut == EV_READ && client->relay_evshut == EV_WRITE) + || (client->client_evshut == (EV_READ|EV_WRITE) && client->relay_evshut == EV_WRITE)) + drop_it = 1; + if (client->client->enabled == 0 && client->relay->enabled== EV_WRITE + && client->client_evshut == 0 && client->relay_evshut == 0 ) + { + /* for debug purpose only */ + redsocks_log_error(client, LOG_DEBUG, "%i connect: %i",EVENT_FD(&client->client->ev_write), red_is_socket_connected_ok(client->relay)); + } + } + /* close long connections without activities */ + if (now - client->last_event >= 3600 * 2) + drop_it = 1; + + if (drop_it){ + const char *s_client_evshut = redsocks_evshut_str(client->client_evshut); + const char *s_relay_evshut = redsocks_evshut_str(client->relay_evshut); + + redsocks_log_error(client, LOG_DEBUG, "Audit client: %i (%s)%s%s input %d output %d, relay: %i (%s)%s%s input %d output %d, age: %li sec, idle: %li sec.", + EVENT_FD(&client->client->ev_write), + redsocks_event_str(client->client->enabled), + s_client_evshut[0] ? " " : "", s_client_evshut, + EVBUFFER_LENGTH(client->client->input), + EVBUFFER_LENGTH(client->client->output), + EVENT_FD(&client->relay->ev_write), + redsocks_event_str(client->relay->enabled), + s_relay_evshut[0] ? " " : "", s_relay_evshut, + EVBUFFER_LENGTH(client->relay->input), + EVBUFFER_LENGTH(client->relay->output), + now - client->first_event, + now - client->last_event); + + redsocks_drop_client(client); + } + } + log_error(LOG_DEBUG, "End of auditing client list."); +} + + static void redsocks_heartbeat(int sig, short what, void *_arg) { time_t now = redsocks_time(NULL); @@ -796,6 +852,14 @@ static void redsocks_heartbeat(int sig, short what, void *_arg) } } +static void redsocks_audit(int sig, short what, void *_arg) +{ + redsocks_instance * tmp, *instance = NULL; + + list_for_each_entry_safe(instance, tmp, &instances, list) + redsocks_audit_instance(instance); +} + static void redsocks_fini_instance(redsocks_instance *instance); static int redsocks_init_instance(redsocks_instance *instance) @@ -906,6 +970,7 @@ static int redsocks_fini(); static struct event debug_dumper; static struct event heartbeat_writer; +static struct event audit_event; //static void ignore_sig(int t) {} @@ -913,6 +978,9 @@ static int redsocks_init() { struct sigaction sa , sa_old; redsocks_instance *tmp, *instance = NULL; void (* old_hdl)(int)= NULL; + struct timeval audit_time; + + memset(&audit_event, 0, sizeof(audit_event)); /* old_hdl = signal(SIGPIPE, ignore_sig); if (old_hdl == -1) { @@ -938,6 +1006,11 @@ static int redsocks_init() { log_errno(LOG_ERR, "signal_add SIGUSR2"); goto fail; } + /* Start audit */ + audit_time.tv_sec = REDSOCKS_AUDIT_INTERVAL; + audit_time.tv_usec = 0; + event_set(&audit_event, 0, EV_TIMEOUT|EV_PERSIST, redsocks_audit, NULL); + evtimer_add(&audit_event, &audit_time); list_for_each_entry_safe(instance, tmp, &instances, list) { if (redsocks_init_instance(instance) != 0) @@ -963,6 +1036,10 @@ static int redsocks_fini() list_for_each_entry_safe(instance, tmp, &instances, list) redsocks_fini_instance(instance); + /* stop audit */ + if (event_initialized(&audit_event)) + evtimer_del(&audit_event); + if (signal_initialized(&debug_dumper)) { if (signal_del(&debug_dumper) != 0) log_errno(LOG_WARNING, "signal_del"); diff --git a/utils.c b/utils.c index 0d54126d..fef22eb4 100644 --- a/utils.c +++ b/utils.c @@ -136,12 +136,6 @@ struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb write goto fail; } - error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); - if (error && errno != EINPROGRESS) { - log_errno(LOG_NOTICE, "connect"); - goto fail; - } - retval = bufferevent_new(relay_fd, NULL, writecb, errorcb, cbarg); if (!retval) { log_errno(LOG_ERR, "bufferevent_new"); @@ -154,6 +148,12 @@ struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb write goto fail; } + error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); + if (error && errno != EINPROGRESS) { + log_errno(LOG_NOTICE, "connect"); + goto fail; + } + return retval; fail: @@ -190,26 +190,26 @@ struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, evbuffercb writ goto fail; } - error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); - if (error && errno != EINPROGRESS) { - log_errno(LOG_NOTICE, "connect"); - goto fail; - } - retval = bufferevent_new(relay_fd, NULL, writecb, errorcb, cbarg); if (!retval) { log_errno(LOG_ERR, "bufferevent_new"); goto fail; } - bufferevent_set_timeouts(retval, NULL, timeout_write); - error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... if (error) { log_errno(LOG_ERR, "bufferevent_enable"); goto fail; } + bufferevent_set_timeouts(retval, NULL, timeout_write); + + error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); + if (error && errno != EINPROGRESS) { + log_errno(LOG_NOTICE, "connect"); + goto fail; + } + return retval; fail: From 308d12f9fc33ca7d9d348abf107203627279539c Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 22 Oct 2013 19:59:57 +0800 Subject: [PATCH 021/192] Make autoproxy feature independ on proxy implementation. --- Makefile | 8 +- README | 26 +- README.md | 27 ++ autohttp-connect.c | 540 ------------------------------------- autosocks.c => autoproxy.c | 267 ++++++++---------- redsocks.c | 24 +- redsocks.conf.example | 9 +- redsocks.h | 1 + socks5.c | 4 +- 9 files changed, 204 insertions(+), 702 deletions(-) create mode 100644 README.md delete mode 100644 autohttp-connect.c rename autosocks.c => autoproxy.c (63%) diff --git a/Makefile b/Makefile index e64be01e..c70cde83 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ -OBJS := parser.o main.o redsocks.o log.o direct.o autohttp-connect.o http-connect.o socks4.o socks5.o autosocks.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o direct.o autoproxy.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks -VERSION := 0.41 +VERSION := 0.5 LIBS := -levent CFLAGS +=-fPIC -Os \ - -I ~/openwrt/trunk/build_dir/target-mipsel_r2_uClibc-0.9.33.2/libevent-2.0.19-stable/ipkg-install/usr/include/ \ - -L ~/openwrt/trunk/build_dir/target-mipsel_r2_uClibc-0.9.33.2/libevent-2.0.19-stable/.libs/ + -I ~/openwrt/openwrt/staging_dir/target-mipsel_dsp_uClibc-0.9.33.2/usr/include/ \ + -L ~/openwrt/openwrt/staging_dir/target-mipsel_dsp_uClibc-0.9.33.2/usr/lib/ override CFLAGS += -std=gnu99 -Wall #LDFLAGS += -fwhole-program diff --git a/README b/README index a77717ad..63f52f3b 100644 --- a/README +++ b/README @@ -1,5 +1,27 @@ This is a modified version of original redsocks. This variant is useful for anti-GFW (Great Fire Wall). + +Note: +Method 'autosocks5' and 'autohttp-connect' are removed. +To use the autoproxy feature, please change the redsocks section in +configuration file like this: + +redsocks { + local_ip = 192.168.1.1; + local_port = 1081; + ip = 192.168.1.1; + port = 9050; + type = socks5; // I use socks5 proxy for GFW'ed IP + autoproxy = 1; // I want autoproxy feature enabled on this section. + // The two lines above have same effect as + // type = autosocks5; + // in previous release. + //type = http-connect; + //login = username; + //password = passwd; +} + +-------------------------------------------------------------------- It provides the following advanced features: 1. new 'direct' mehtod. This method is intent to direct any TCP connections directly without @@ -9,7 +31,7 @@ montioring HTTP/HTTPS traffic. Only one computer has full access to all websites. Other computers in local network are limited to some websites. By depolying transparent proxy with this method, all your computers can access websites without restrictions. -2. new 'autosocks5' method. +2. new 'autosocks5' method. (REPLACED BY NEW METHOD!!!!) This mehtod is specially customized for fighting against GFW. By default, all TCP connections are redirected to connect to target directly without going through socks5 proxy. But, in case the connection to target @@ -26,7 +48,7 @@ proxy immediately unless the addresses are removed from cache by code logic. The advantage of this method is that you do not need to set thousands entries in iptables for blocked sites manually. It is especially useful for proxying Youtube which has so many sub-domains for video content cache. -3. new 'autohttp-connect' method. +3. new 'autohttp-connect' method. (REPLACED BY NEW METHOD!!!!) This method behaves similar as 'autoscoks5' method except the proxy to be used must support HTTP CONNECT method. This method is useful for using redsocks together with GoProxy. diff --git a/README.md b/README.md new file mode 100644 index 00000000..f9a55ce9 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +REDSOCKS2 +========= +This is a modified version of original redsocks. +The name is changed to be REDSOCKS2 since this release to distinguish +with original redsocks. +This variant is useful for anti-GFW (Great Fire Wall). + +##Note: +Method 'autosocks5' and 'autohttp-connect' are removed. +To use the autoproxy feature, please change the redsocks section in +configuration file like this: + + redsocks { + local_ip = 192.168.1.1; + local_port = 1081; + ip = 192.168.1.1; + port = 9050; + type = socks5; // I use socks5 proxy for GFW'ed IP + autoproxy = 1; // I want autoproxy feature enabled on this section. + // The two lines above have same effect as + // type = autosocks5; + // in previous release. + //type = http-connect; + //login = username; + //password = passwd; + } + diff --git a/autohttp-connect.c b/autohttp-connect.c deleted file mode 100644 index e28540a3..00000000 --- a/autohttp-connect.c +++ /dev/null @@ -1,540 +0,0 @@ -/* redsocks - transparent TCP-to-proxy redirector - * Copyright (C) 2007-2011 Leonid Evdokimov - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * - * http-connect upstream module for redsocks - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "log.h" -#include "redsocks.h" -#include "http-auth.h" - -typedef enum httpc_state_t { - httpc_new, - httpc_request_sent, - httpc_reply_came, - httpc_headers_skipped, - httpc_MAX, - httpc_pre_detect=100, /* Introduce additional states to httpc subsystem */ - httpc_direct, - httpc_direct_confirmed, -} httpc_state; - -typedef struct httpc_client_t { - time_t time_connect_relay; // timestamp when start to connect relay - size_t data_recv; - size_t data_sent; -} httpc_client; - - -void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); -static int auto_retry_or_drop(redsocks_client * client); -static void auto_connect_relay(redsocks_client *client); -static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); - -#define HTTP_HEAD_WM_HIGH 4096 // that should be enough for one HTTP line. -extern void httpc_read_cb(struct bufferevent *buffev, void *_arg); -extern void httpc_write_cb(struct bufferevent *buffev, void *_arg); - -#define CIRCUIT_RESET_SECONDS 1 -#define CONNECT_TIMEOUT_SECONDS 10 -#define ADDR_CACHE_BLOCKS 64 -#define ADDR_CACHE_BLOCK_SIZE 16 -#define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) -static struct sockaddr_in addr_cache[ADDR_CACHE_BLOCKS][ADDR_CACHE_BLOCK_SIZE]; -static int addr_cache_counters[ADDR_CACHE_BLOCKS]; -static int addr_cache_pointers[ADDR_CACHE_BLOCKS]; -static int cache_init = 0; - -static void init_addr_cache() -{ - if (!cache_init) - { - memset((void *)addr_cache, 0, sizeof(addr_cache)); - memset((void *)addr_cache_counters, 0, sizeof(addr_cache_counters)); - memset((void *)addr_cache_pointers, 0, sizeof(addr_cache_pointers)); - cache_init = 1; - } -} - -static int is_addr_in_cache(const struct sockaddr_in * addr) -{ - /* get block index */ - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - int first = addr_cache_pointers[block]; - int i = 0; - /* do reverse search for efficency */ - for ( i = count - 1; i >= 0; i -- ) - /* - if (0 == memcmp((void *)addr, (void *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE], sizeof(struct sockaddr_in))) -*/ - if (addr->sin_addr.s_addr == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].sin_addr.s_addr - && addr->sin_family == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].sin_family - ) - return 1; - - return 0; -} - -static void add_addr_to_cache(const struct sockaddr_in * addr) -{ - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - int first = addr_cache_pointers[block]; - - if (count < ADDR_CACHE_BLOCK_SIZE) - { - memcpy((void *)&addr_cache[block][count], (void *) addr, sizeof(struct sockaddr_in)); - addr_cache_counters[block]++; - } - else - { - memcpy((void *)&addr_cache[block][first], (void *) addr, sizeof(struct sockaddr_in)); - addr_cache_pointers[block]++; - addr_cache_pointers[block]%=ADDR_CACHE_BLOCK_SIZE; - } -} - - - -static void auto_httpc_client_init(redsocks_client *client) -{ - httpc_client * httpc= (void*)(client + 1); - - client->state = httpc_pre_detect; - httpc->data_recv = 0; - httpc->data_sent = 0; - init_addr_cache(); - -} - -static void httpc_instance_fini(redsocks_instance *instance) -{ - http_auth *auth = (void*)(instance + 1); - free(auth->last_auth_query); - auth->last_auth_query = NULL; -} - - - - - -static void direct_relay_readcb_helper(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) -{ - if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { - if (bufferevent_write_buffer(to, from->input) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - } - else { - if (bufferevent_disable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); - } -} - - -static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) -{ - redsocks_client *client = _client; - httpc_client *httpc = (void*)(client + 1); - - redsocks_touch_client(client); - - if (client->state == httpc_direct) - { - if (httpc->data_sent && httpc->data_recv) - { - /* No CONNECTION RESET error occur after sending data, good. */ - client->state = httpc_direct_confirmed; - if (evbuffer_get_length(from->input)) - { - evbuffer_drain(from->input, httpc->data_sent); - httpc->data_sent = 0; - } - } - } - direct_relay_readcb_helper(client, client->client, client->relay); -} - - -static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) -{ - redsocks_client *client = _client; - httpc_client *httpc = (void*)(client + 1); - - redsocks_touch_client(client); - if (!httpc->data_recv) - httpc->data_recv = EVBUFFER_LENGTH(from->input); - direct_relay_readcb_helper(client, client->relay, client->client); -} - -static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) -{ - redsocks_client *client = _client; - httpc_client *httpc = (void*)(client + 1); - struct bufferevent * from = client->relay; - - redsocks_touch_client(client); - - if (EVBUFFER_LENGTH(from->input) == 0 && (client->relay_evshut & EV_READ)) { - redsocks_shutdown(client, to, SHUT_WR); - return; - } - if (client->state == httpc_direct) - { - if (!httpc->data_recv) - httpc->data_recv = EVBUFFER_LENGTH(from->input); - } - if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { - if (bufferevent_write_buffer(to, from->input) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - } -} - - - -static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) -{ - redsocks_client *client = _client; - httpc_client *httpc = (void*)(client + 1); - struct bufferevent * from = client->client; - - redsocks_touch_client(client); - - if (EVBUFFER_LENGTH(from->input) == 0 && (client->client_evshut & EV_READ)) { - redsocks_shutdown(client, to, SHUT_WR); - return; - } - else if (client->state == httpc_direct ) - { - /* Not send or receive data. */ - if (!httpc->data_sent && !httpc->data_recv) - { - /* Ensure we have data to send */ - if (EVBUFFER_LENGTH(from->input)) - { - /* copy data from input to output of relay */ - httpc->data_sent = copy_evbuffer (to, from, 0); - redsocks_log_error(client, LOG_DEBUG, "not sent, not got %d", EVBUFFER_LENGTH(from->input)); - } - } - /* - * Relay reaceived data before writing to relay. - */ - else if (!httpc->data_sent && httpc->data_recv) - { - redsocks_log_error(client, LOG_DEBUG, "not sent, got"); - httpc->data_sent = copy_evbuffer(to, from, 0); - } - /* client->state = httpc_direct_confirmed; */ - /* We have writen data to relay, but got nothing until we are requested to - * write to it again. - * r - */ - else if (httpc->data_sent && !httpc->data_recv) - { - /* No response from relay and no CONNECTION RESET, - Send more data. - */ - redsocks_log_error(client, LOG_DEBUG, "sent, not got in:%d out:%d high:%d sent:%d", - evbuffer_get_length(from->input), - evbuffer_get_length(to->output), - to->wm_write.high, httpc->data_sent ); - /* Write more data util input buffer is full */ - if (EVBUFFER_LENGTH(from->input)- httpc->data_sent > 0) /* we have more data waiting to be sent */ - { - httpc->data_sent += copy_evbuffer(to, from, httpc->data_sent); - } - else if (EVBUFFER_LENGTH(to->output) < httpc->data_sent /* data is sent out, more or less */ - /* && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* read buffer is full */ - && EVBUFFER_LENGTH(from->input) == httpc->data_sent /* all data in read buffer is sent */ - ) - { - evbuffer_drain(from->input, httpc->data_sent); - httpc->data_sent = 0; - client->state = httpc_direct_confirmed; - } - } - /* We sent data to and got data from relay. */ - else if (httpc->data_sent && httpc->data_recv) - { - /* No CONNECTION RESET error occur after sending data, good. */ - client->state = httpc_direct_confirmed; - redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", httpc->data_recv); - if (evbuffer_get_length(from->input)) - { - evbuffer_drain(from->input, httpc->data_sent); - httpc->data_sent = 0; - } - } - } - - if (client->state == httpc_direct_confirmed) - { - if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { - if (bufferevent_write_buffer(to, from->input) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - } - } -} - - -static void auto_write_cb(struct bufferevent *buffev, void *_arg) -{ - redsocks_client *client = _arg; - - redsocks_touch_client(client); - - if (client->state == httpc_pre_detect) { - client->state = httpc_direct; - - if (!redsocks_start_relay(client)) - { - /* overwrite theread callback to my function */ - client->client->readcb = direct_relay_clientreadcb; - client->client->writecb = direct_relay_clientwritecb; - client->relay->readcb = direct_relay_relayreadcb; - client->relay->writecb = direct_relay_relaywritecb; - } - } - - else if (client->state == httpc_direct) - redsocks_log_error(client, LOG_DEBUG, "Should not be here!"); - else - httpc_write_cb(buffev, _arg); -} - - - -static void auto_read_cb(struct bufferevent *buffev, void *_arg) -{ - redsocks_client *client = _arg; - - redsocks_touch_client(client); - - if (client->state == httpc_pre_detect) { - /* Should never be here */ -/* - client->state = httpc_direct; - redsocks_start_relay(client); -*/ - } - else if (client->state == httpc_direct) { - /* if (EVBUFFER_LENGTH(buffev->input) == 0 && client->relay_evshut & EV_READ) */ - } - else { - httpc_read_cb(buffev, _arg); - } -} - -static void auto_drop_relay(redsocks_client *client) -{ - redsocks_log_error(client, LOG_DEBUG, "dropping relay only"); - - if (client->relay) { - redsocks_close(EVENT_FD(&client->relay->ev_write)); - bufferevent_free(client->relay); - client->relay = NULL; - } -} - -static void auto_retry(redsocks_client * client, int updcache) -{ - if (client->state == httpc_direct) - bufferevent_disable(client->client, EV_READ| EV_WRITE); - /* drop relay and update state, then retry with httpc relay */ - if (updcache) - { - add_addr_to_cache(&client->destaddr); - redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %s", - inet_ntoa(client->destaddr.sin_addr)); - } - auto_drop_relay(client); - client->state = httpc_new; - auto_connect_relay(client); /* Retry SOCKS5 proxy relay */ -} - -/* return 1 for drop, 0 for retry. */ -static int auto_retry_or_drop(redsocks_client * client) -{ - time_t now = redsocks_time(NULL); - httpc_client *httpc = (void*)(client + 1); - - if (client->state == httpc_pre_detect) - { - if (now - httpc->time_connect_relay <= CIRCUIT_RESET_SECONDS) - { - auto_retry(client, 1); - return 0; - } - } - if ( client->state == httpc_direct) - { -// if (now - httpc->time_connect_relay <= CIRCUIT_RESET_SECONDS) - { - auto_retry(client, 0); - return 0; - } - } - - /* drop */ - return 1; -} - -static void auto_relay_connected(struct bufferevent *buffev, void *_arg) -{ - redsocks_client *client = _arg; - - assert(buffev == client->relay); - - redsocks_touch_client(client); - - if (!red_is_socket_connected_ok(buffev)) { - if (client->state == httpc_pre_detect && !auto_retry_or_drop(client)) - return; - - redsocks_log_error(client, LOG_DEBUG, "failed to connect to proxy"); - goto fail; - } - - /* We do not need to detect timeouts any more. - The two peers will handle it. */ - bufferevent_set_timeouts(client->relay, NULL, NULL); - - client->relay->readcb = client->instance->relay_ss->readcb; - client->relay->writecb = client->instance->relay_ss->writecb; - client->relay->writecb(buffev, _arg); - return; - -fail: - redsocks_drop_client(client); -} - -static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) -{ - redsocks_client *client = _arg; - int saved_errno = errno; - assert(buffev == client->relay || buffev == client->client); - - redsocks_touch_client(client); - - redsocks_log_errno(client, LOG_DEBUG, "Errno: %d, State: %d, what: " event_fmt_str, saved_errno, client->state, event_fmt(what)); - if (buffev == client->relay) - { - if ( client->state == httpc_pre_detect - && what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT)) - { - /* In case timeout occurs for connecting relay, we try to connect - to target with SOCKS5 proxy. It is possible that the connection to - target can be set up a bit longer than the timeout value we set. - However, it is still better to make connection via proxy. */ - auto_retry(client, 1); - return; - } - - if (client->state == httpc_pre_detect && saved_errno == ECONNRESET) - if (!auto_retry_or_drop(client)) - return; - - if (client->state == httpc_direct && what == (EVBUFFER_READ|EVBUFFER_ERROR) - && saved_errno == ECONNRESET ) - { - if (!auto_retry_or_drop(client)) - return; - } - } - - if (what == (EVBUFFER_READ|EVBUFFER_EOF)) { - struct bufferevent *antiev; - if (buffev == client->relay) - antiev = client->client; - else - antiev = client->relay; - - redsocks_shutdown(client, buffev, SHUT_RD); - - if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0) - redsocks_shutdown(client, antiev, SHUT_WR); - } - else { - redsocks_drop_client(client); - } -} - - -static void auto_connect_relay(redsocks_client *client) -{ - httpc_client *httpc = (void*)(client + 1); - struct timeval tv; - tv.tv_sec = CONNECT_TIMEOUT_SECONDS; - tv.tv_usec = 0; - - if (client->state == httpc_pre_detect) - { - if (is_addr_in_cache(&client->destaddr)) - { - client->state = httpc_new; /* Connect SOCKS5 */ - redsocks_log_error(client, LOG_DEBUG, "Found in cache"); - } - } - client->relay = red_connect_relay2( client->state == httpc_pre_detect - ? &client->destaddr : &client->instance->config.relayaddr, - auto_relay_connected, auto_event_error, client, - client->state == httpc_pre_detect ? &tv: NULL); - - httpc->time_connect_relay = redsocks_time(NULL); - - if (!client->relay) { - redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); - redsocks_drop_client(client); - } -} - - - - - -relay_subsys autohttp_connect_subsys = -{ - .name = "autohttp-connect", - .payload_len = sizeof(httpc_client), - .instance_payload_len = sizeof(http_auth), - .instance_fini = httpc_instance_fini, - .readcb = auto_read_cb, - .writecb = auto_write_cb, - .init = auto_httpc_client_init, - .connect_relay = auto_connect_relay, -}; - - -/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ -/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/autosocks.c b/autoproxy.c similarity index 63% rename from autosocks.c rename to autoproxy.c index 4814b145..b86b56d8 100644 --- a/autosocks.c +++ b/autoproxy.c @@ -26,34 +26,21 @@ #include "utils.h" #include "log.h" #include "redsocks.h" -#include "socks5.h" - -typedef enum socks5_state_t { - socks5_new, - socks5_method_sent, - socks5_auth_sent, - socks5_request_sent, - socks5_skip_domain, - socks5_skip_address, - socks5_MAX, - socks5_pre_detect=100, /* Introduce additional states to socks5 subsystem */ - socks5_direct, - socks5_direct_confirmed, -} socks5_state; - -typedef struct socks5_client_t { - int do_password; // 1 - password authentication is possible - int to_skip; // valid while reading last reply (after main request) + +typedef enum autoproxy_state_t { + /* Introduce subsystem */ + AUTOPROXY_NEW=10000, + AUTOPROXY_CONNECTED, + AUTOPROXY_CONFIRMED, +} autoproxy_state; + +typedef struct autoproxy_client_t { time_t time_connect_relay; // timestamp when start to connect relay - int got_data; + size_t data_recv; size_t data_sent; -} socks5_client; +} autoproxy_client; -int socks5_is_valid_cred(const char *login, const char *password); -void socks5_write_cb(struct bufferevent *buffev, void *_arg); -void socks5_read_cb(struct bufferevent *buffev, void *_arg); - void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); static int auto_retry_or_drop(redsocks_client * client); static void auto_connect_relay(redsocks_client *client); @@ -120,15 +107,13 @@ static void add_addr_to_cache(const struct sockaddr_in * addr) } -void auto_socks5_client_init(redsocks_client *client) +void auto_client_init(redsocks_client *client) { - socks5_client * socks5= (void*)(client + 1); - const redsocks_config *config = &client->instance->config; + autoproxy_client * aclient = (void*)(client + 1); - client->state = socks5_pre_detect; - socks5->got_data = 0; - socks5->data_sent = 0; - socks5->do_password = socks5_is_valid_cred(config->login, config->password); + client->state = AUTOPROXY_NEW; + aclient->data_recv = 0; + aclient->data_sent = 0; init_addr_cache(); } @@ -150,20 +135,20 @@ static void direct_relay_readcb_helper(redsocks_client *client, struct buffereve static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) { redsocks_client *client = _client; - socks5_client *socks5 = (void*)(client + 1); + autoproxy_client *aclient = (void*)(client + 1); redsocks_touch_client(client); - if (client->state == socks5_direct) + if (client->state == AUTOPROXY_CONNECTED) { - if (socks5->data_sent && socks5->got_data) + if (aclient->data_sent && aclient->data_recv) { /* No CONNECTION RESET error occur after sending data, good. */ - client->state = socks5_direct_confirmed; + client->state = AUTOPROXY_CONFIRMED; if (evbuffer_get_length(from->input)) { - evbuffer_drain(from->input, socks5->data_sent); - socks5->data_sent = 0; + evbuffer_drain(from->input, aclient->data_sent); + aclient->data_sent = 0; } } } @@ -174,18 +159,18 @@ static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) { redsocks_client *client = _client; - socks5_client *socks5 = (void*)(client + 1); + autoproxy_client *aclient = (void*)(client + 1); redsocks_touch_client(client); - if (!socks5->got_data) - socks5->got_data = EVBUFFER_LENGTH(from->input); + if (!aclient->data_recv) + aclient->data_recv = EVBUFFER_LENGTH(from->input); direct_relay_readcb_helper(client, client->relay, client->client); } static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; - socks5_client *socks5 = (void*)(client + 1); + autoproxy_client *aclient = (void*)(client + 1); struct bufferevent * from = client->relay; redsocks_touch_client(client); @@ -194,10 +179,10 @@ static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) redsocks_shutdown(client, to, SHUT_WR); return; } - if (client->state == socks5_direct) + if (client->state == AUTOPROXY_CONNECTED) { - if (!socks5->got_data) - socks5->got_data = EVBUFFER_LENGTH(from->input); + if (!aclient->data_recv) + aclient->data_recv = EVBUFFER_LENGTH(from->input); } if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) @@ -212,7 +197,7 @@ static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; - socks5_client *socks5 = (void*)(client + 1); + autoproxy_client *aclient = (void*)(client + 1); struct bufferevent * from = client->client; redsocks_touch_client(client); @@ -221,32 +206,32 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) redsocks_shutdown(client, to, SHUT_WR); return; } - else if (client->state == socks5_direct ) + else if (client->state == AUTOPROXY_CONNECTED ) { /* Not send or receive data. */ - if (!socks5->data_sent && !socks5->got_data) + if (!aclient->data_sent && !aclient->data_recv) { /* Ensure we have data to send */ if (EVBUFFER_LENGTH(from->input)) { /* copy data from input to output of relay */ - socks5->data_sent = copy_evbuffer (to, from, 0); + aclient->data_sent = copy_evbuffer (to, from, 0); redsocks_log_error(client, LOG_DEBUG, "not sent, not got %d", EVBUFFER_LENGTH(from->input)); } } /* * Relay reaceived data before writing to relay. */ - else if (!socks5->data_sent && socks5->got_data) + else if (!aclient->data_sent && aclient->data_recv) { redsocks_log_error(client, LOG_DEBUG, "not sent, got"); - socks5->data_sent = copy_evbuffer(to, from, 0); + aclient->data_sent = copy_evbuffer(to, from, 0); } - /* client->state = socks5_direct_confirmed; */ + /* client->state = AUTOPROXY_CONFIRMED; */ /* We have writen data to relay, but got nothing until we are requested to * write to it again. */ - else if (socks5->data_sent && !socks5->got_data) + else if (aclient->data_sent && !aclient->data_recv) { /* No response from relay and no CONNECTION RESET, Send more data. @@ -254,37 +239,37 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) redsocks_log_error(client, LOG_DEBUG, "sent, not got in:%d out:%d high:%d sent:%d", evbuffer_get_length(from->input), evbuffer_get_length(to->output), - to->wm_write.high, socks5->data_sent ); + to->wm_write.high, aclient->data_sent ); /* Write more data util input buffer is full */ - if (EVBUFFER_LENGTH(from->input)- socks5->data_sent > 0) /* we have more data waiting to be sent */ + if (EVBUFFER_LENGTH(from->input)- aclient->data_sent > 0) /* we have more data waiting to be sent */ { - socks5->data_sent += copy_evbuffer(to, from, socks5->data_sent); + aclient->data_sent += copy_evbuffer(to, from, aclient->data_sent); } - else if (EVBUFFER_LENGTH(to->output) < socks5->data_sent /* data is sent out, more or less */ + else if (EVBUFFER_LENGTH(to->output) < aclient->data_sent /* data is sent out, more or less */ && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* do not confirm unless read buffer is full */ - && EVBUFFER_LENGTH(from->input) == socks5->data_sent /* all data in read buffer is sent */ + && EVBUFFER_LENGTH(from->input) == aclient->data_sent /* all data in read buffer is sent */ ) { - evbuffer_drain(from->input, socks5->data_sent); - socks5->data_sent = 0; - client->state = socks5_direct_confirmed; + evbuffer_drain(from->input, aclient->data_sent); + aclient->data_sent = 0; + client->state = AUTOPROXY_CONFIRMED; } } /* We sent data to and got data from relay. */ - else if (socks5->data_sent && socks5->got_data) + else if (aclient->data_sent && aclient->data_recv) { /* No CONNECTION RESET error occur after sending data, good. */ - client->state = socks5_direct_confirmed; - redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", socks5->got_data); + client->state = AUTOPROXY_CONFIRMED; + redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", aclient->data_recv); if (evbuffer_get_length(from->input)) { - evbuffer_drain(from->input, socks5->data_sent); - socks5->data_sent = 0; + evbuffer_drain(from->input, aclient->data_sent); + aclient->data_sent = 0; } } } - if (client->state == socks5_direct_confirmed) + if (client->state == AUTOPROXY_CONFIRMED) { if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) @@ -295,55 +280,6 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) } } - -static void auto_write_cb(struct bufferevent *buffev, void *_arg) -{ - redsocks_client *client = _arg; - - redsocks_touch_client(client); - - if (client->state == socks5_pre_detect) { - client->state = socks5_direct; - - if (!redsocks_start_relay(client)) - { - /* overwrite theread callback to my function */ - client->client->readcb = direct_relay_clientreadcb; - client->client->writecb = direct_relay_clientwritecb; - client->relay->readcb = direct_relay_relayreadcb; - client->relay->writecb = direct_relay_relaywritecb; - } - } - - else if (client->state == socks5_direct) - redsocks_log_error(client, LOG_DEBUG, "Should not be here!"); - else - socks5_write_cb(buffev, _arg); -} - - - -static void auto_read_cb(struct bufferevent *buffev, void *_arg) -{ - redsocks_client *client = _arg; - - redsocks_touch_client(client); - - if (client->state == socks5_pre_detect) { - /* Should never be here */ -/* - client->state = socks5_direct; - redsocks_start_relay(client); -*/ - } - else if (client->state == socks5_direct) { - /* if (EVBUFFER_LENGTH(buffev->input) == 0 && client->relay_evshut & EV_READ) */ - } - else { - socks5_read_cb(buffev, _arg); - } -} - static void auto_drop_relay(redsocks_client *client) { redsocks_log_error(client, LOG_DEBUG, "dropping relay only"); @@ -357,9 +293,9 @@ static void auto_drop_relay(redsocks_client *client) static void auto_retry(redsocks_client * client, int updcache) { - if (client->state == socks5_direct) + if (client->state == AUTOPROXY_CONNECTED) bufferevent_disable(client->client, EV_READ| EV_WRITE); - /* drop relay and update state, then retry with socks5 relay */ + /* drop relay and update state, then retry with specified relay */ if (updcache) { add_addr_to_cache(&client->destaddr); @@ -367,27 +303,35 @@ static void auto_retry(redsocks_client * client, int updcache) inet_ntoa(client->destaddr.sin_addr)); } auto_drop_relay(client); - client->state = socks5_new; - auto_connect_relay(client); /* Retry SOCKS5 proxy relay */ + + /* init subsytem as ordinary subsystem */ + client->instance->relay_ss->init(client); + // enable reading to handle EOF from client + bufferevent_enable(client->client, EV_READ); + /* connect to relay */ + if (client->instance->relay_ss->connect_relay) + client->instance->relay_ss->connect_relay(client); + else + redsocks_connect_relay(client); } /* return 1 for drop, 0 for retry. */ static int auto_retry_or_drop(redsocks_client * client) { time_t now = redsocks_time(NULL); - socks5_client *socks5 = (void*)(client + 1); + autoproxy_client *aclient = (void*)(client + 1); - if (client->state == socks5_pre_detect) + if (client->state == AUTOPROXY_NEW) { - if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) + if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) { - auto_retry(client, 1); + auto_retry(client, 0); return 0; } } - else if ( client->state == socks5_direct) + else if ( client->state == AUTOPROXY_CONNECTED) { -// if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) +// if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) { auto_retry(client, 0); return 0; @@ -407,19 +351,33 @@ static void auto_relay_connected(struct bufferevent *buffev, void *_arg) redsocks_touch_client(client); if (!red_is_socket_connected_ok(buffev)) { - if (client->state == socks5_pre_detect && !auto_retry_or_drop(client)) + if (client->state == AUTOPROXY_NEW && !auto_retry_or_drop(client)) return; redsocks_log_error(client, LOG_DEBUG, "failed to connect to proxy"); goto fail; } - + + /* update client state */ + client->state = AUTOPROXY_CONNECTED; + /* We do not need to detect timeouts any more. The two peers will handle it. */ bufferevent_set_timeouts(client->relay, NULL, NULL); - client->relay->readcb = client->instance->relay_ss->readcb; - client->relay->writecb = client->instance->relay_ss->writecb; + if (!redsocks_start_relay(client)) + { + /* overwrite theread callback to my function */ + client->client->readcb = direct_relay_clientreadcb; + client->client->writecb = direct_relay_clientwritecb; + client->relay->readcb = direct_relay_relayreadcb; + client->relay->writecb = direct_relay_relaywritecb; + } + else + { + redsocks_log_error(client, LOG_DEBUG, "failed to start relay"); + goto fail; + } client->relay->writecb(buffev, _arg); return; @@ -435,11 +393,13 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) redsocks_touch_client(client); - redsocks_log_errno(client, LOG_DEBUG, "Errno: %d, State: %d, what: " event_fmt_str, saved_errno, client->state, event_fmt(what)); + redsocks_log_errno(client, LOG_DEBUG, "%s errno(%d), State: %d, what: " event_fmt_str, + buffev == client->client?"client":"relay", + saved_errno, client->state, event_fmt(what)); if (buffev == client->relay) { - if ( client->state == socks5_pre_detect + if ( client->state == AUTOPROXY_NEW && what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT)) { /* In case timeout occurs while connecting relay, we try to connect @@ -450,11 +410,11 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) return; } - if (client->state == socks5_pre_detect && saved_errno == ECONNRESET) + if (client->state == AUTOPROXY_NEW && saved_errno == ECONNRESET) if (!auto_retry_or_drop(client)) return; - if (client->state == socks5_direct && what == (EVBUFFER_READ|EVBUFFER_ERROR) + if (client->state == AUTOPROXY_CONNECTED && what == (EVBUFFER_READ|EVBUFFER_ERROR) && saved_errno == ECONNRESET ) { if (!auto_retry_or_drop(client)) @@ -482,29 +442,34 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) static void auto_connect_relay(redsocks_client *client) { - socks5_client *socks5 = (void*)(client + 1); + autoproxy_client * aclient = (void*)(client + 1); struct timeval tv; tv.tv_sec = CONNECT_TIMEOUT_SECONDS; tv.tv_usec = 0; - if (client->state == socks5_pre_detect) + if (client->state == AUTOPROXY_NEW) { if (is_addr_in_cache(&client->destaddr)) { - client->state = socks5_new; /* Connect SOCKS5 */ redsocks_log_error(client, LOG_DEBUG, "Found in cache"); + auto_retry(client, 0); + return ; + } + /* connect to target directly without going through proxy */ + client->relay = red_connect_relay2(&client->destaddr, + auto_relay_connected, auto_event_error, client, + &tv); + + aclient->time_connect_relay = redsocks_time(NULL); + + if (!client->relay) { + redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); + redsocks_drop_client(client); } } - client->relay = red_connect_relay2( client->state == socks5_pre_detect - ? &client->destaddr : &client->instance->config.relayaddr, - auto_relay_connected, auto_event_error, client, - client->state == socks5_pre_detect ? &tv: NULL); - - socks5->time_connect_relay = redsocks_time(NULL); - - if (!client->relay) { - redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); - redsocks_drop_client(client); + else + { + redsocks_log_errno(client, LOG_ERR, "invalid state: %d", client->state); } } @@ -513,14 +478,16 @@ static void auto_connect_relay(redsocks_client *client) -relay_subsys autosocks5_subsys = +relay_subsys autoproxy_subsys = { - .name = "autosocks5", - .payload_len = sizeof(socks5_client), + .name = "autoproxy", + .payload_len = sizeof(autoproxy_client), .instance_payload_len = 0, +/* .readcb = auto_read_cb, .writecb = auto_write_cb, - .init = auto_socks5_client_init, +*/ + .init = auto_client_init, .connect_relay = auto_connect_relay, }; diff --git a/redsocks.c b/redsocks.c index 56ccdc07..c789c91b 100644 --- a/redsocks.c +++ b/redsocks.c @@ -59,6 +59,7 @@ static relay_subsys *relay_subsystems[] = &socks4_subsys, &socks5_subsys, }; +extern relay_subsys autoproxy_subsys; static list_head instances = LIST_HEAD_INIT(instances); @@ -74,6 +75,7 @@ static parser_entry redsocks_entries[] = { .key = "listenq", .type = pt_uint16 }, { .key = "min_accept_backoff", .type = pt_uint16 }, { .key = "max_accept_backoff", .type = pt_uint16 }, + { .key = "autoproxy", .type = pt_uint16 }, { } }; @@ -130,6 +132,7 @@ static int redsocks_onenter(parser_section *section) instance->config.listenq = SOMAXCONN; instance->config.min_backoff_ms = 100; instance->config.max_backoff_ms = 60000; + instance->config.autoproxy = 0; for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = @@ -143,6 +146,7 @@ static int redsocks_onenter(parser_section *section) (strcmp(entry->key, "listenq") == 0) ? (void*)&instance->config.listenq : (strcmp(entry->key, "min_accept_backoff") == 0) ? (void*)&instance->config.min_backoff_ms : (strcmp(entry->key, "max_accept_backoff") == 0) ? (void*)&instance->config.max_backoff_ms : + (strcmp(entry->key, "autoproxy") == 0) ? (void*)&instance->config.autoproxy : NULL; section->data = instance; return 0; @@ -678,16 +682,26 @@ static void redsocks_accept_client(int fd, short what, void *_arg) } // everything seems to be ok, let's allocate some memory - client = calloc(1, sizeof(redsocks_client) + self->relay_ss->payload_len); + if (self->config.autoproxy) + client = calloc(1, sizeof(redsocks_client) + + (self->relay_ss->payload_len>= autoproxy_subsys.payload_len? + self->relay_ss->payload_len: autoproxy_subsys.payload_len) + ); + else + client = calloc(1, sizeof(redsocks_client) + self->relay_ss->payload_len); if (!client) { log_errno(LOG_ERR, "calloc"); goto fail; } + client->instance = self; memcpy(&client->clientaddr, &clientaddr, sizeof(clientaddr)); memcpy(&client->destaddr, &destaddr, sizeof(destaddr)); INIT_LIST_HEAD(&client->list); - self->relay_ss->init(client); + if (self->config.autoproxy) + autoproxy_subsys.init(client); + else + self->relay_ss->init(client); if (redsocks_time(&client->first_event) == ((time_t)-1)) goto fail; @@ -711,7 +725,11 @@ static void redsocks_accept_client(int fd, short what, void *_arg) redsocks_log_error(client, LOG_INFO, "accepted"); - if (self->relay_ss->connect_relay) + if (self->config.autoproxy && autoproxy_subsys.connect_relay) + { + autoproxy_subsys.connect_relay(client); + } + else if (self->relay_ss->connect_relay) self->relay_ss->connect_relay(client); else redsocks_connect_relay(client); diff --git a/redsocks.conf.example b/redsocks.conf.example index aa6f1e03..9ba5b8d2 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -64,9 +64,16 @@ redsocks { // known types: socks4, socks5, http-connect, http-relay - // New types: direct, autosocks5 + // New types: direct type = socks5; + // Change this parameter to 1 if you want auto proxy feature. + // When autoproxy is set to non-zero, the connection to target + // will be made directly first. If direct connection to target + // fails for timeout/connection refuse, redsocks will try to + // connect to target via the proxy. + autoproxy = 0; + // login = "foobar"; // password = "baz"; } diff --git a/redsocks.h b/redsocks.h index c4feb278..a6f8deef 100644 --- a/redsocks.h +++ b/redsocks.h @@ -32,6 +32,7 @@ typedef struct redsocks_config_t { uint16_t min_backoff_ms; uint16_t max_backoff_ms; // backoff capped by 65 seconds is enough :) uint16_t listenq; + uint16_t autoproxy; } redsocks_config; struct tracked_event { diff --git a/socks5.c b/socks5.c index 124e7838..4b5456d4 100644 --- a/socks5.c +++ b/socks5.c @@ -151,7 +151,7 @@ static struct evbuffer *socks5_mkconnect(redsocks_client *client) return socks5_mkcommand_plain(socks5_cmd_connect, &client->destaddr); } -void socks5_write_cb(struct bufferevent *buffev, void *_arg) +static void socks5_write_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; @@ -271,7 +271,7 @@ static void socks5_read_reply(struct bufferevent *buffev, redsocks_client *clien } } -void socks5_read_cb(struct bufferevent *buffev, void *_arg) +static void socks5_read_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; socks5_client *socks5 = (void*)(client + 1); From 73724767ce406653194a8aca678fd6859f067062 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 22 Oct 2013 20:04:32 +0800 Subject: [PATCH 022/192] Make autoproxy feature independ on proxy implementation. --- Makefile | 8 +- README | 26 +- README.md | 27 ++ autohttp-connect.c | 540 ------------------------------------- autosocks.c => autoproxy.c | 267 ++++++++---------- redsocks.c | 28 +- redsocks.conf.example | 9 +- redsocks.h | 1 + socks5.c | 4 +- 9 files changed, 204 insertions(+), 706 deletions(-) create mode 100644 README.md delete mode 100644 autohttp-connect.c rename autosocks.c => autoproxy.c (63%) diff --git a/Makefile b/Makefile index e64be01e..c70cde83 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ -OBJS := parser.o main.o redsocks.o log.o direct.o autohttp-connect.o http-connect.o socks4.o socks5.o autosocks.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o direct.o autoproxy.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks -VERSION := 0.41 +VERSION := 0.5 LIBS := -levent CFLAGS +=-fPIC -Os \ - -I ~/openwrt/trunk/build_dir/target-mipsel_r2_uClibc-0.9.33.2/libevent-2.0.19-stable/ipkg-install/usr/include/ \ - -L ~/openwrt/trunk/build_dir/target-mipsel_r2_uClibc-0.9.33.2/libevent-2.0.19-stable/.libs/ + -I ~/openwrt/openwrt/staging_dir/target-mipsel_dsp_uClibc-0.9.33.2/usr/include/ \ + -L ~/openwrt/openwrt/staging_dir/target-mipsel_dsp_uClibc-0.9.33.2/usr/lib/ override CFLAGS += -std=gnu99 -Wall #LDFLAGS += -fwhole-program diff --git a/README b/README index a77717ad..63f52f3b 100644 --- a/README +++ b/README @@ -1,5 +1,27 @@ This is a modified version of original redsocks. This variant is useful for anti-GFW (Great Fire Wall). + +Note: +Method 'autosocks5' and 'autohttp-connect' are removed. +To use the autoproxy feature, please change the redsocks section in +configuration file like this: + +redsocks { + local_ip = 192.168.1.1; + local_port = 1081; + ip = 192.168.1.1; + port = 9050; + type = socks5; // I use socks5 proxy for GFW'ed IP + autoproxy = 1; // I want autoproxy feature enabled on this section. + // The two lines above have same effect as + // type = autosocks5; + // in previous release. + //type = http-connect; + //login = username; + //password = passwd; +} + +-------------------------------------------------------------------- It provides the following advanced features: 1. new 'direct' mehtod. This method is intent to direct any TCP connections directly without @@ -9,7 +31,7 @@ montioring HTTP/HTTPS traffic. Only one computer has full access to all websites. Other computers in local network are limited to some websites. By depolying transparent proxy with this method, all your computers can access websites without restrictions. -2. new 'autosocks5' method. +2. new 'autosocks5' method. (REPLACED BY NEW METHOD!!!!) This mehtod is specially customized for fighting against GFW. By default, all TCP connections are redirected to connect to target directly without going through socks5 proxy. But, in case the connection to target @@ -26,7 +48,7 @@ proxy immediately unless the addresses are removed from cache by code logic. The advantage of this method is that you do not need to set thousands entries in iptables for blocked sites manually. It is especially useful for proxying Youtube which has so many sub-domains for video content cache. -3. new 'autohttp-connect' method. +3. new 'autohttp-connect' method. (REPLACED BY NEW METHOD!!!!) This method behaves similar as 'autoscoks5' method except the proxy to be used must support HTTP CONNECT method. This method is useful for using redsocks together with GoProxy. diff --git a/README.md b/README.md new file mode 100644 index 00000000..f9a55ce9 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +REDSOCKS2 +========= +This is a modified version of original redsocks. +The name is changed to be REDSOCKS2 since this release to distinguish +with original redsocks. +This variant is useful for anti-GFW (Great Fire Wall). + +##Note: +Method 'autosocks5' and 'autohttp-connect' are removed. +To use the autoproxy feature, please change the redsocks section in +configuration file like this: + + redsocks { + local_ip = 192.168.1.1; + local_port = 1081; + ip = 192.168.1.1; + port = 9050; + type = socks5; // I use socks5 proxy for GFW'ed IP + autoproxy = 1; // I want autoproxy feature enabled on this section. + // The two lines above have same effect as + // type = autosocks5; + // in previous release. + //type = http-connect; + //login = username; + //password = passwd; + } + diff --git a/autohttp-connect.c b/autohttp-connect.c deleted file mode 100644 index e28540a3..00000000 --- a/autohttp-connect.c +++ /dev/null @@ -1,540 +0,0 @@ -/* redsocks - transparent TCP-to-proxy redirector - * Copyright (C) 2007-2011 Leonid Evdokimov - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * - * http-connect upstream module for redsocks - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "log.h" -#include "redsocks.h" -#include "http-auth.h" - -typedef enum httpc_state_t { - httpc_new, - httpc_request_sent, - httpc_reply_came, - httpc_headers_skipped, - httpc_MAX, - httpc_pre_detect=100, /* Introduce additional states to httpc subsystem */ - httpc_direct, - httpc_direct_confirmed, -} httpc_state; - -typedef struct httpc_client_t { - time_t time_connect_relay; // timestamp when start to connect relay - size_t data_recv; - size_t data_sent; -} httpc_client; - - -void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); -static int auto_retry_or_drop(redsocks_client * client); -static void auto_connect_relay(redsocks_client *client); -static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); - -#define HTTP_HEAD_WM_HIGH 4096 // that should be enough for one HTTP line. -extern void httpc_read_cb(struct bufferevent *buffev, void *_arg); -extern void httpc_write_cb(struct bufferevent *buffev, void *_arg); - -#define CIRCUIT_RESET_SECONDS 1 -#define CONNECT_TIMEOUT_SECONDS 10 -#define ADDR_CACHE_BLOCKS 64 -#define ADDR_CACHE_BLOCK_SIZE 16 -#define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) -static struct sockaddr_in addr_cache[ADDR_CACHE_BLOCKS][ADDR_CACHE_BLOCK_SIZE]; -static int addr_cache_counters[ADDR_CACHE_BLOCKS]; -static int addr_cache_pointers[ADDR_CACHE_BLOCKS]; -static int cache_init = 0; - -static void init_addr_cache() -{ - if (!cache_init) - { - memset((void *)addr_cache, 0, sizeof(addr_cache)); - memset((void *)addr_cache_counters, 0, sizeof(addr_cache_counters)); - memset((void *)addr_cache_pointers, 0, sizeof(addr_cache_pointers)); - cache_init = 1; - } -} - -static int is_addr_in_cache(const struct sockaddr_in * addr) -{ - /* get block index */ - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - int first = addr_cache_pointers[block]; - int i = 0; - /* do reverse search for efficency */ - for ( i = count - 1; i >= 0; i -- ) - /* - if (0 == memcmp((void *)addr, (void *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE], sizeof(struct sockaddr_in))) -*/ - if (addr->sin_addr.s_addr == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].sin_addr.s_addr - && addr->sin_family == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].sin_family - ) - return 1; - - return 0; -} - -static void add_addr_to_cache(const struct sockaddr_in * addr) -{ - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - int first = addr_cache_pointers[block]; - - if (count < ADDR_CACHE_BLOCK_SIZE) - { - memcpy((void *)&addr_cache[block][count], (void *) addr, sizeof(struct sockaddr_in)); - addr_cache_counters[block]++; - } - else - { - memcpy((void *)&addr_cache[block][first], (void *) addr, sizeof(struct sockaddr_in)); - addr_cache_pointers[block]++; - addr_cache_pointers[block]%=ADDR_CACHE_BLOCK_SIZE; - } -} - - - -static void auto_httpc_client_init(redsocks_client *client) -{ - httpc_client * httpc= (void*)(client + 1); - - client->state = httpc_pre_detect; - httpc->data_recv = 0; - httpc->data_sent = 0; - init_addr_cache(); - -} - -static void httpc_instance_fini(redsocks_instance *instance) -{ - http_auth *auth = (void*)(instance + 1); - free(auth->last_auth_query); - auth->last_auth_query = NULL; -} - - - - - -static void direct_relay_readcb_helper(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) -{ - if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { - if (bufferevent_write_buffer(to, from->input) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - } - else { - if (bufferevent_disable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); - } -} - - -static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) -{ - redsocks_client *client = _client; - httpc_client *httpc = (void*)(client + 1); - - redsocks_touch_client(client); - - if (client->state == httpc_direct) - { - if (httpc->data_sent && httpc->data_recv) - { - /* No CONNECTION RESET error occur after sending data, good. */ - client->state = httpc_direct_confirmed; - if (evbuffer_get_length(from->input)) - { - evbuffer_drain(from->input, httpc->data_sent); - httpc->data_sent = 0; - } - } - } - direct_relay_readcb_helper(client, client->client, client->relay); -} - - -static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) -{ - redsocks_client *client = _client; - httpc_client *httpc = (void*)(client + 1); - - redsocks_touch_client(client); - if (!httpc->data_recv) - httpc->data_recv = EVBUFFER_LENGTH(from->input); - direct_relay_readcb_helper(client, client->relay, client->client); -} - -static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) -{ - redsocks_client *client = _client; - httpc_client *httpc = (void*)(client + 1); - struct bufferevent * from = client->relay; - - redsocks_touch_client(client); - - if (EVBUFFER_LENGTH(from->input) == 0 && (client->relay_evshut & EV_READ)) { - redsocks_shutdown(client, to, SHUT_WR); - return; - } - if (client->state == httpc_direct) - { - if (!httpc->data_recv) - httpc->data_recv = EVBUFFER_LENGTH(from->input); - } - if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { - if (bufferevent_write_buffer(to, from->input) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - } -} - - - -static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) -{ - redsocks_client *client = _client; - httpc_client *httpc = (void*)(client + 1); - struct bufferevent * from = client->client; - - redsocks_touch_client(client); - - if (EVBUFFER_LENGTH(from->input) == 0 && (client->client_evshut & EV_READ)) { - redsocks_shutdown(client, to, SHUT_WR); - return; - } - else if (client->state == httpc_direct ) - { - /* Not send or receive data. */ - if (!httpc->data_sent && !httpc->data_recv) - { - /* Ensure we have data to send */ - if (EVBUFFER_LENGTH(from->input)) - { - /* copy data from input to output of relay */ - httpc->data_sent = copy_evbuffer (to, from, 0); - redsocks_log_error(client, LOG_DEBUG, "not sent, not got %d", EVBUFFER_LENGTH(from->input)); - } - } - /* - * Relay reaceived data before writing to relay. - */ - else if (!httpc->data_sent && httpc->data_recv) - { - redsocks_log_error(client, LOG_DEBUG, "not sent, got"); - httpc->data_sent = copy_evbuffer(to, from, 0); - } - /* client->state = httpc_direct_confirmed; */ - /* We have writen data to relay, but got nothing until we are requested to - * write to it again. - * r - */ - else if (httpc->data_sent && !httpc->data_recv) - { - /* No response from relay and no CONNECTION RESET, - Send more data. - */ - redsocks_log_error(client, LOG_DEBUG, "sent, not got in:%d out:%d high:%d sent:%d", - evbuffer_get_length(from->input), - evbuffer_get_length(to->output), - to->wm_write.high, httpc->data_sent ); - /* Write more data util input buffer is full */ - if (EVBUFFER_LENGTH(from->input)- httpc->data_sent > 0) /* we have more data waiting to be sent */ - { - httpc->data_sent += copy_evbuffer(to, from, httpc->data_sent); - } - else if (EVBUFFER_LENGTH(to->output) < httpc->data_sent /* data is sent out, more or less */ - /* && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* read buffer is full */ - && EVBUFFER_LENGTH(from->input) == httpc->data_sent /* all data in read buffer is sent */ - ) - { - evbuffer_drain(from->input, httpc->data_sent); - httpc->data_sent = 0; - client->state = httpc_direct_confirmed; - } - } - /* We sent data to and got data from relay. */ - else if (httpc->data_sent && httpc->data_recv) - { - /* No CONNECTION RESET error occur after sending data, good. */ - client->state = httpc_direct_confirmed; - redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", httpc->data_recv); - if (evbuffer_get_length(from->input)) - { - evbuffer_drain(from->input, httpc->data_sent); - httpc->data_sent = 0; - } - } - } - - if (client->state == httpc_direct_confirmed) - { - if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { - if (bufferevent_write_buffer(to, from->input) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - } - } -} - - -static void auto_write_cb(struct bufferevent *buffev, void *_arg) -{ - redsocks_client *client = _arg; - - redsocks_touch_client(client); - - if (client->state == httpc_pre_detect) { - client->state = httpc_direct; - - if (!redsocks_start_relay(client)) - { - /* overwrite theread callback to my function */ - client->client->readcb = direct_relay_clientreadcb; - client->client->writecb = direct_relay_clientwritecb; - client->relay->readcb = direct_relay_relayreadcb; - client->relay->writecb = direct_relay_relaywritecb; - } - } - - else if (client->state == httpc_direct) - redsocks_log_error(client, LOG_DEBUG, "Should not be here!"); - else - httpc_write_cb(buffev, _arg); -} - - - -static void auto_read_cb(struct bufferevent *buffev, void *_arg) -{ - redsocks_client *client = _arg; - - redsocks_touch_client(client); - - if (client->state == httpc_pre_detect) { - /* Should never be here */ -/* - client->state = httpc_direct; - redsocks_start_relay(client); -*/ - } - else if (client->state == httpc_direct) { - /* if (EVBUFFER_LENGTH(buffev->input) == 0 && client->relay_evshut & EV_READ) */ - } - else { - httpc_read_cb(buffev, _arg); - } -} - -static void auto_drop_relay(redsocks_client *client) -{ - redsocks_log_error(client, LOG_DEBUG, "dropping relay only"); - - if (client->relay) { - redsocks_close(EVENT_FD(&client->relay->ev_write)); - bufferevent_free(client->relay); - client->relay = NULL; - } -} - -static void auto_retry(redsocks_client * client, int updcache) -{ - if (client->state == httpc_direct) - bufferevent_disable(client->client, EV_READ| EV_WRITE); - /* drop relay and update state, then retry with httpc relay */ - if (updcache) - { - add_addr_to_cache(&client->destaddr); - redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %s", - inet_ntoa(client->destaddr.sin_addr)); - } - auto_drop_relay(client); - client->state = httpc_new; - auto_connect_relay(client); /* Retry SOCKS5 proxy relay */ -} - -/* return 1 for drop, 0 for retry. */ -static int auto_retry_or_drop(redsocks_client * client) -{ - time_t now = redsocks_time(NULL); - httpc_client *httpc = (void*)(client + 1); - - if (client->state == httpc_pre_detect) - { - if (now - httpc->time_connect_relay <= CIRCUIT_RESET_SECONDS) - { - auto_retry(client, 1); - return 0; - } - } - if ( client->state == httpc_direct) - { -// if (now - httpc->time_connect_relay <= CIRCUIT_RESET_SECONDS) - { - auto_retry(client, 0); - return 0; - } - } - - /* drop */ - return 1; -} - -static void auto_relay_connected(struct bufferevent *buffev, void *_arg) -{ - redsocks_client *client = _arg; - - assert(buffev == client->relay); - - redsocks_touch_client(client); - - if (!red_is_socket_connected_ok(buffev)) { - if (client->state == httpc_pre_detect && !auto_retry_or_drop(client)) - return; - - redsocks_log_error(client, LOG_DEBUG, "failed to connect to proxy"); - goto fail; - } - - /* We do not need to detect timeouts any more. - The two peers will handle it. */ - bufferevent_set_timeouts(client->relay, NULL, NULL); - - client->relay->readcb = client->instance->relay_ss->readcb; - client->relay->writecb = client->instance->relay_ss->writecb; - client->relay->writecb(buffev, _arg); - return; - -fail: - redsocks_drop_client(client); -} - -static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) -{ - redsocks_client *client = _arg; - int saved_errno = errno; - assert(buffev == client->relay || buffev == client->client); - - redsocks_touch_client(client); - - redsocks_log_errno(client, LOG_DEBUG, "Errno: %d, State: %d, what: " event_fmt_str, saved_errno, client->state, event_fmt(what)); - if (buffev == client->relay) - { - if ( client->state == httpc_pre_detect - && what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT)) - { - /* In case timeout occurs for connecting relay, we try to connect - to target with SOCKS5 proxy. It is possible that the connection to - target can be set up a bit longer than the timeout value we set. - However, it is still better to make connection via proxy. */ - auto_retry(client, 1); - return; - } - - if (client->state == httpc_pre_detect && saved_errno == ECONNRESET) - if (!auto_retry_or_drop(client)) - return; - - if (client->state == httpc_direct && what == (EVBUFFER_READ|EVBUFFER_ERROR) - && saved_errno == ECONNRESET ) - { - if (!auto_retry_or_drop(client)) - return; - } - } - - if (what == (EVBUFFER_READ|EVBUFFER_EOF)) { - struct bufferevent *antiev; - if (buffev == client->relay) - antiev = client->client; - else - antiev = client->relay; - - redsocks_shutdown(client, buffev, SHUT_RD); - - if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0) - redsocks_shutdown(client, antiev, SHUT_WR); - } - else { - redsocks_drop_client(client); - } -} - - -static void auto_connect_relay(redsocks_client *client) -{ - httpc_client *httpc = (void*)(client + 1); - struct timeval tv; - tv.tv_sec = CONNECT_TIMEOUT_SECONDS; - tv.tv_usec = 0; - - if (client->state == httpc_pre_detect) - { - if (is_addr_in_cache(&client->destaddr)) - { - client->state = httpc_new; /* Connect SOCKS5 */ - redsocks_log_error(client, LOG_DEBUG, "Found in cache"); - } - } - client->relay = red_connect_relay2( client->state == httpc_pre_detect - ? &client->destaddr : &client->instance->config.relayaddr, - auto_relay_connected, auto_event_error, client, - client->state == httpc_pre_detect ? &tv: NULL); - - httpc->time_connect_relay = redsocks_time(NULL); - - if (!client->relay) { - redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); - redsocks_drop_client(client); - } -} - - - - - -relay_subsys autohttp_connect_subsys = -{ - .name = "autohttp-connect", - .payload_len = sizeof(httpc_client), - .instance_payload_len = sizeof(http_auth), - .instance_fini = httpc_instance_fini, - .readcb = auto_read_cb, - .writecb = auto_write_cb, - .init = auto_httpc_client_init, - .connect_relay = auto_connect_relay, -}; - - -/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ -/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/autosocks.c b/autoproxy.c similarity index 63% rename from autosocks.c rename to autoproxy.c index 4814b145..b86b56d8 100644 --- a/autosocks.c +++ b/autoproxy.c @@ -26,34 +26,21 @@ #include "utils.h" #include "log.h" #include "redsocks.h" -#include "socks5.h" - -typedef enum socks5_state_t { - socks5_new, - socks5_method_sent, - socks5_auth_sent, - socks5_request_sent, - socks5_skip_domain, - socks5_skip_address, - socks5_MAX, - socks5_pre_detect=100, /* Introduce additional states to socks5 subsystem */ - socks5_direct, - socks5_direct_confirmed, -} socks5_state; - -typedef struct socks5_client_t { - int do_password; // 1 - password authentication is possible - int to_skip; // valid while reading last reply (after main request) + +typedef enum autoproxy_state_t { + /* Introduce subsystem */ + AUTOPROXY_NEW=10000, + AUTOPROXY_CONNECTED, + AUTOPROXY_CONFIRMED, +} autoproxy_state; + +typedef struct autoproxy_client_t { time_t time_connect_relay; // timestamp when start to connect relay - int got_data; + size_t data_recv; size_t data_sent; -} socks5_client; +} autoproxy_client; -int socks5_is_valid_cred(const char *login, const char *password); -void socks5_write_cb(struct bufferevent *buffev, void *_arg); -void socks5_read_cb(struct bufferevent *buffev, void *_arg); - void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); static int auto_retry_or_drop(redsocks_client * client); static void auto_connect_relay(redsocks_client *client); @@ -120,15 +107,13 @@ static void add_addr_to_cache(const struct sockaddr_in * addr) } -void auto_socks5_client_init(redsocks_client *client) +void auto_client_init(redsocks_client *client) { - socks5_client * socks5= (void*)(client + 1); - const redsocks_config *config = &client->instance->config; + autoproxy_client * aclient = (void*)(client + 1); - client->state = socks5_pre_detect; - socks5->got_data = 0; - socks5->data_sent = 0; - socks5->do_password = socks5_is_valid_cred(config->login, config->password); + client->state = AUTOPROXY_NEW; + aclient->data_recv = 0; + aclient->data_sent = 0; init_addr_cache(); } @@ -150,20 +135,20 @@ static void direct_relay_readcb_helper(redsocks_client *client, struct buffereve static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) { redsocks_client *client = _client; - socks5_client *socks5 = (void*)(client + 1); + autoproxy_client *aclient = (void*)(client + 1); redsocks_touch_client(client); - if (client->state == socks5_direct) + if (client->state == AUTOPROXY_CONNECTED) { - if (socks5->data_sent && socks5->got_data) + if (aclient->data_sent && aclient->data_recv) { /* No CONNECTION RESET error occur after sending data, good. */ - client->state = socks5_direct_confirmed; + client->state = AUTOPROXY_CONFIRMED; if (evbuffer_get_length(from->input)) { - evbuffer_drain(from->input, socks5->data_sent); - socks5->data_sent = 0; + evbuffer_drain(from->input, aclient->data_sent); + aclient->data_sent = 0; } } } @@ -174,18 +159,18 @@ static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) { redsocks_client *client = _client; - socks5_client *socks5 = (void*)(client + 1); + autoproxy_client *aclient = (void*)(client + 1); redsocks_touch_client(client); - if (!socks5->got_data) - socks5->got_data = EVBUFFER_LENGTH(from->input); + if (!aclient->data_recv) + aclient->data_recv = EVBUFFER_LENGTH(from->input); direct_relay_readcb_helper(client, client->relay, client->client); } static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; - socks5_client *socks5 = (void*)(client + 1); + autoproxy_client *aclient = (void*)(client + 1); struct bufferevent * from = client->relay; redsocks_touch_client(client); @@ -194,10 +179,10 @@ static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) redsocks_shutdown(client, to, SHUT_WR); return; } - if (client->state == socks5_direct) + if (client->state == AUTOPROXY_CONNECTED) { - if (!socks5->got_data) - socks5->got_data = EVBUFFER_LENGTH(from->input); + if (!aclient->data_recv) + aclient->data_recv = EVBUFFER_LENGTH(from->input); } if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) @@ -212,7 +197,7 @@ static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; - socks5_client *socks5 = (void*)(client + 1); + autoproxy_client *aclient = (void*)(client + 1); struct bufferevent * from = client->client; redsocks_touch_client(client); @@ -221,32 +206,32 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) redsocks_shutdown(client, to, SHUT_WR); return; } - else if (client->state == socks5_direct ) + else if (client->state == AUTOPROXY_CONNECTED ) { /* Not send or receive data. */ - if (!socks5->data_sent && !socks5->got_data) + if (!aclient->data_sent && !aclient->data_recv) { /* Ensure we have data to send */ if (EVBUFFER_LENGTH(from->input)) { /* copy data from input to output of relay */ - socks5->data_sent = copy_evbuffer (to, from, 0); + aclient->data_sent = copy_evbuffer (to, from, 0); redsocks_log_error(client, LOG_DEBUG, "not sent, not got %d", EVBUFFER_LENGTH(from->input)); } } /* * Relay reaceived data before writing to relay. */ - else if (!socks5->data_sent && socks5->got_data) + else if (!aclient->data_sent && aclient->data_recv) { redsocks_log_error(client, LOG_DEBUG, "not sent, got"); - socks5->data_sent = copy_evbuffer(to, from, 0); + aclient->data_sent = copy_evbuffer(to, from, 0); } - /* client->state = socks5_direct_confirmed; */ + /* client->state = AUTOPROXY_CONFIRMED; */ /* We have writen data to relay, but got nothing until we are requested to * write to it again. */ - else if (socks5->data_sent && !socks5->got_data) + else if (aclient->data_sent && !aclient->data_recv) { /* No response from relay and no CONNECTION RESET, Send more data. @@ -254,37 +239,37 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) redsocks_log_error(client, LOG_DEBUG, "sent, not got in:%d out:%d high:%d sent:%d", evbuffer_get_length(from->input), evbuffer_get_length(to->output), - to->wm_write.high, socks5->data_sent ); + to->wm_write.high, aclient->data_sent ); /* Write more data util input buffer is full */ - if (EVBUFFER_LENGTH(from->input)- socks5->data_sent > 0) /* we have more data waiting to be sent */ + if (EVBUFFER_LENGTH(from->input)- aclient->data_sent > 0) /* we have more data waiting to be sent */ { - socks5->data_sent += copy_evbuffer(to, from, socks5->data_sent); + aclient->data_sent += copy_evbuffer(to, from, aclient->data_sent); } - else if (EVBUFFER_LENGTH(to->output) < socks5->data_sent /* data is sent out, more or less */ + else if (EVBUFFER_LENGTH(to->output) < aclient->data_sent /* data is sent out, more or less */ && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* do not confirm unless read buffer is full */ - && EVBUFFER_LENGTH(from->input) == socks5->data_sent /* all data in read buffer is sent */ + && EVBUFFER_LENGTH(from->input) == aclient->data_sent /* all data in read buffer is sent */ ) { - evbuffer_drain(from->input, socks5->data_sent); - socks5->data_sent = 0; - client->state = socks5_direct_confirmed; + evbuffer_drain(from->input, aclient->data_sent); + aclient->data_sent = 0; + client->state = AUTOPROXY_CONFIRMED; } } /* We sent data to and got data from relay. */ - else if (socks5->data_sent && socks5->got_data) + else if (aclient->data_sent && aclient->data_recv) { /* No CONNECTION RESET error occur after sending data, good. */ - client->state = socks5_direct_confirmed; - redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", socks5->got_data); + client->state = AUTOPROXY_CONFIRMED; + redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", aclient->data_recv); if (evbuffer_get_length(from->input)) { - evbuffer_drain(from->input, socks5->data_sent); - socks5->data_sent = 0; + evbuffer_drain(from->input, aclient->data_sent); + aclient->data_sent = 0; } } } - if (client->state == socks5_direct_confirmed) + if (client->state == AUTOPROXY_CONFIRMED) { if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) @@ -295,55 +280,6 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) } } - -static void auto_write_cb(struct bufferevent *buffev, void *_arg) -{ - redsocks_client *client = _arg; - - redsocks_touch_client(client); - - if (client->state == socks5_pre_detect) { - client->state = socks5_direct; - - if (!redsocks_start_relay(client)) - { - /* overwrite theread callback to my function */ - client->client->readcb = direct_relay_clientreadcb; - client->client->writecb = direct_relay_clientwritecb; - client->relay->readcb = direct_relay_relayreadcb; - client->relay->writecb = direct_relay_relaywritecb; - } - } - - else if (client->state == socks5_direct) - redsocks_log_error(client, LOG_DEBUG, "Should not be here!"); - else - socks5_write_cb(buffev, _arg); -} - - - -static void auto_read_cb(struct bufferevent *buffev, void *_arg) -{ - redsocks_client *client = _arg; - - redsocks_touch_client(client); - - if (client->state == socks5_pre_detect) { - /* Should never be here */ -/* - client->state = socks5_direct; - redsocks_start_relay(client); -*/ - } - else if (client->state == socks5_direct) { - /* if (EVBUFFER_LENGTH(buffev->input) == 0 && client->relay_evshut & EV_READ) */ - } - else { - socks5_read_cb(buffev, _arg); - } -} - static void auto_drop_relay(redsocks_client *client) { redsocks_log_error(client, LOG_DEBUG, "dropping relay only"); @@ -357,9 +293,9 @@ static void auto_drop_relay(redsocks_client *client) static void auto_retry(redsocks_client * client, int updcache) { - if (client->state == socks5_direct) + if (client->state == AUTOPROXY_CONNECTED) bufferevent_disable(client->client, EV_READ| EV_WRITE); - /* drop relay and update state, then retry with socks5 relay */ + /* drop relay and update state, then retry with specified relay */ if (updcache) { add_addr_to_cache(&client->destaddr); @@ -367,27 +303,35 @@ static void auto_retry(redsocks_client * client, int updcache) inet_ntoa(client->destaddr.sin_addr)); } auto_drop_relay(client); - client->state = socks5_new; - auto_connect_relay(client); /* Retry SOCKS5 proxy relay */ + + /* init subsytem as ordinary subsystem */ + client->instance->relay_ss->init(client); + // enable reading to handle EOF from client + bufferevent_enable(client->client, EV_READ); + /* connect to relay */ + if (client->instance->relay_ss->connect_relay) + client->instance->relay_ss->connect_relay(client); + else + redsocks_connect_relay(client); } /* return 1 for drop, 0 for retry. */ static int auto_retry_or_drop(redsocks_client * client) { time_t now = redsocks_time(NULL); - socks5_client *socks5 = (void*)(client + 1); + autoproxy_client *aclient = (void*)(client + 1); - if (client->state == socks5_pre_detect) + if (client->state == AUTOPROXY_NEW) { - if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) + if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) { - auto_retry(client, 1); + auto_retry(client, 0); return 0; } } - else if ( client->state == socks5_direct) + else if ( client->state == AUTOPROXY_CONNECTED) { -// if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) +// if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) { auto_retry(client, 0); return 0; @@ -407,19 +351,33 @@ static void auto_relay_connected(struct bufferevent *buffev, void *_arg) redsocks_touch_client(client); if (!red_is_socket_connected_ok(buffev)) { - if (client->state == socks5_pre_detect && !auto_retry_or_drop(client)) + if (client->state == AUTOPROXY_NEW && !auto_retry_or_drop(client)) return; redsocks_log_error(client, LOG_DEBUG, "failed to connect to proxy"); goto fail; } - + + /* update client state */ + client->state = AUTOPROXY_CONNECTED; + /* We do not need to detect timeouts any more. The two peers will handle it. */ bufferevent_set_timeouts(client->relay, NULL, NULL); - client->relay->readcb = client->instance->relay_ss->readcb; - client->relay->writecb = client->instance->relay_ss->writecb; + if (!redsocks_start_relay(client)) + { + /* overwrite theread callback to my function */ + client->client->readcb = direct_relay_clientreadcb; + client->client->writecb = direct_relay_clientwritecb; + client->relay->readcb = direct_relay_relayreadcb; + client->relay->writecb = direct_relay_relaywritecb; + } + else + { + redsocks_log_error(client, LOG_DEBUG, "failed to start relay"); + goto fail; + } client->relay->writecb(buffev, _arg); return; @@ -435,11 +393,13 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) redsocks_touch_client(client); - redsocks_log_errno(client, LOG_DEBUG, "Errno: %d, State: %d, what: " event_fmt_str, saved_errno, client->state, event_fmt(what)); + redsocks_log_errno(client, LOG_DEBUG, "%s errno(%d), State: %d, what: " event_fmt_str, + buffev == client->client?"client":"relay", + saved_errno, client->state, event_fmt(what)); if (buffev == client->relay) { - if ( client->state == socks5_pre_detect + if ( client->state == AUTOPROXY_NEW && what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT)) { /* In case timeout occurs while connecting relay, we try to connect @@ -450,11 +410,11 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) return; } - if (client->state == socks5_pre_detect && saved_errno == ECONNRESET) + if (client->state == AUTOPROXY_NEW && saved_errno == ECONNRESET) if (!auto_retry_or_drop(client)) return; - if (client->state == socks5_direct && what == (EVBUFFER_READ|EVBUFFER_ERROR) + if (client->state == AUTOPROXY_CONNECTED && what == (EVBUFFER_READ|EVBUFFER_ERROR) && saved_errno == ECONNRESET ) { if (!auto_retry_or_drop(client)) @@ -482,29 +442,34 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) static void auto_connect_relay(redsocks_client *client) { - socks5_client *socks5 = (void*)(client + 1); + autoproxy_client * aclient = (void*)(client + 1); struct timeval tv; tv.tv_sec = CONNECT_TIMEOUT_SECONDS; tv.tv_usec = 0; - if (client->state == socks5_pre_detect) + if (client->state == AUTOPROXY_NEW) { if (is_addr_in_cache(&client->destaddr)) { - client->state = socks5_new; /* Connect SOCKS5 */ redsocks_log_error(client, LOG_DEBUG, "Found in cache"); + auto_retry(client, 0); + return ; + } + /* connect to target directly without going through proxy */ + client->relay = red_connect_relay2(&client->destaddr, + auto_relay_connected, auto_event_error, client, + &tv); + + aclient->time_connect_relay = redsocks_time(NULL); + + if (!client->relay) { + redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); + redsocks_drop_client(client); } } - client->relay = red_connect_relay2( client->state == socks5_pre_detect - ? &client->destaddr : &client->instance->config.relayaddr, - auto_relay_connected, auto_event_error, client, - client->state == socks5_pre_detect ? &tv: NULL); - - socks5->time_connect_relay = redsocks_time(NULL); - - if (!client->relay) { - redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); - redsocks_drop_client(client); + else + { + redsocks_log_errno(client, LOG_ERR, "invalid state: %d", client->state); } } @@ -513,14 +478,16 @@ static void auto_connect_relay(redsocks_client *client) -relay_subsys autosocks5_subsys = +relay_subsys autoproxy_subsys = { - .name = "autosocks5", - .payload_len = sizeof(socks5_client), + .name = "autoproxy", + .payload_len = sizeof(autoproxy_client), .instance_payload_len = 0, +/* .readcb = auto_read_cb, .writecb = auto_write_cb, - .init = auto_socks5_client_init, +*/ + .init = auto_client_init, .connect_relay = auto_connect_relay, }; diff --git a/redsocks.c b/redsocks.c index 56ccdc07..799bfafb 100644 --- a/redsocks.c +++ b/redsocks.c @@ -47,18 +47,15 @@ extern relay_subsys http_connect_subsys; extern relay_subsys http_relay_subsys; extern relay_subsys socks4_subsys; extern relay_subsys socks5_subsys; -extern relay_subsys autosocks5_subsys; -extern relay_subsys autohttp_connect_subsys; static relay_subsys *relay_subsystems[] = { - &autosocks5_subsys, - &autohttp_connect_subsys, &direct_connect_subsys, &http_connect_subsys, &http_relay_subsys, &socks4_subsys, &socks5_subsys, }; +extern relay_subsys autoproxy_subsys; static list_head instances = LIST_HEAD_INIT(instances); @@ -74,6 +71,7 @@ static parser_entry redsocks_entries[] = { .key = "listenq", .type = pt_uint16 }, { .key = "min_accept_backoff", .type = pt_uint16 }, { .key = "max_accept_backoff", .type = pt_uint16 }, + { .key = "autoproxy", .type = pt_uint16 }, { } }; @@ -130,6 +128,7 @@ static int redsocks_onenter(parser_section *section) instance->config.listenq = SOMAXCONN; instance->config.min_backoff_ms = 100; instance->config.max_backoff_ms = 60000; + instance->config.autoproxy = 0; for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = @@ -143,6 +142,7 @@ static int redsocks_onenter(parser_section *section) (strcmp(entry->key, "listenq") == 0) ? (void*)&instance->config.listenq : (strcmp(entry->key, "min_accept_backoff") == 0) ? (void*)&instance->config.min_backoff_ms : (strcmp(entry->key, "max_accept_backoff") == 0) ? (void*)&instance->config.max_backoff_ms : + (strcmp(entry->key, "autoproxy") == 0) ? (void*)&instance->config.autoproxy : NULL; section->data = instance; return 0; @@ -678,16 +678,26 @@ static void redsocks_accept_client(int fd, short what, void *_arg) } // everything seems to be ok, let's allocate some memory - client = calloc(1, sizeof(redsocks_client) + self->relay_ss->payload_len); + if (self->config.autoproxy) + client = calloc(1, sizeof(redsocks_client) + + (self->relay_ss->payload_len>= autoproxy_subsys.payload_len? + self->relay_ss->payload_len: autoproxy_subsys.payload_len) + ); + else + client = calloc(1, sizeof(redsocks_client) + self->relay_ss->payload_len); if (!client) { log_errno(LOG_ERR, "calloc"); goto fail; } + client->instance = self; memcpy(&client->clientaddr, &clientaddr, sizeof(clientaddr)); memcpy(&client->destaddr, &destaddr, sizeof(destaddr)); INIT_LIST_HEAD(&client->list); - self->relay_ss->init(client); + if (self->config.autoproxy) + autoproxy_subsys.init(client); + else + self->relay_ss->init(client); if (redsocks_time(&client->first_event) == ((time_t)-1)) goto fail; @@ -711,7 +721,11 @@ static void redsocks_accept_client(int fd, short what, void *_arg) redsocks_log_error(client, LOG_INFO, "accepted"); - if (self->relay_ss->connect_relay) + if (self->config.autoproxy && autoproxy_subsys.connect_relay) + { + autoproxy_subsys.connect_relay(client); + } + else if (self->relay_ss->connect_relay) self->relay_ss->connect_relay(client); else redsocks_connect_relay(client); diff --git a/redsocks.conf.example b/redsocks.conf.example index aa6f1e03..9ba5b8d2 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -64,9 +64,16 @@ redsocks { // known types: socks4, socks5, http-connect, http-relay - // New types: direct, autosocks5 + // New types: direct type = socks5; + // Change this parameter to 1 if you want auto proxy feature. + // When autoproxy is set to non-zero, the connection to target + // will be made directly first. If direct connection to target + // fails for timeout/connection refuse, redsocks will try to + // connect to target via the proxy. + autoproxy = 0; + // login = "foobar"; // password = "baz"; } diff --git a/redsocks.h b/redsocks.h index c4feb278..a6f8deef 100644 --- a/redsocks.h +++ b/redsocks.h @@ -32,6 +32,7 @@ typedef struct redsocks_config_t { uint16_t min_backoff_ms; uint16_t max_backoff_ms; // backoff capped by 65 seconds is enough :) uint16_t listenq; + uint16_t autoproxy; } redsocks_config; struct tracked_event { diff --git a/socks5.c b/socks5.c index 124e7838..4b5456d4 100644 --- a/socks5.c +++ b/socks5.c @@ -151,7 +151,7 @@ static struct evbuffer *socks5_mkconnect(redsocks_client *client) return socks5_mkcommand_plain(socks5_cmd_connect, &client->destaddr); } -void socks5_write_cb(struct bufferevent *buffev, void *_arg) +static void socks5_write_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; @@ -271,7 +271,7 @@ static void socks5_read_reply(struct bufferevent *buffev, redsocks_client *clien } } -void socks5_read_cb(struct bufferevent *buffev, void *_arg) +static void socks5_read_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; socks5_client *socks5 = (void*)(client + 1); From 238a64be0fce05346f181d447dc4476735a7ec13 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 4 Nov 2013 13:02:22 +0800 Subject: [PATCH 023/192] Allow config timeout and introduce cache stale. --- Makefile | 4 +- README.md | 44 ++++++++++++++++++++ autoproxy.c | 93 +++++++++++++++++++++++++++++++++++-------- redsocks.c | 3 ++ redsocks.conf.example | 5 +++ redsocks.h | 1 + 6 files changed, 131 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index c70cde83..c8ecd2f6 100644 --- a/Makefile +++ b/Makefile @@ -2,11 +2,11 @@ OBJS := parser.o main.o redsocks.o log.o direct.o autoproxy.o http-connect.o soc SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend -OUT := redsocks +OUT := redsocks2 VERSION := 0.5 LIBS := -levent -CFLAGS +=-fPIC -Os \ +CFLAGS +=-fPIC -O2 \ -I ~/openwrt/openwrt/staging_dir/target-mipsel_dsp_uClibc-0.9.33.2/usr/include/ \ -L ~/openwrt/openwrt/staging_dir/target-mipsel_dsp_uClibc-0.9.33.2/usr/lib/ override CFLAGS += -std=gnu99 -Wall diff --git a/README.md b/README.md index f9a55ce9..61023a2a 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,11 @@ The name is changed to be REDSOCKS2 since this release to distinguish with original redsocks. This variant is useful for anti-GFW (Great Fire Wall). +HOW it works +------------ +Who can help me to complete this part? -_- + + ##Note: Method 'autosocks5' and 'autohttp-connect' are removed. To use the autoproxy feature, please change the redsocks section in @@ -20,8 +25,47 @@ configuration file like this: // The two lines above have same effect as // type = autosocks5; // in previous release. + // timeout is meaningful when 'autoproxy' is non-zero. + // It specified timeout value when trying to connect to destination + // directly. Default is 10 seconds. When it is set to 0, default + // timeout value will be used. + timeout = 10; //type = http-connect; //login = username; //password = passwd; } +##Work with GoAgent +To make redsocks2 works with GoAgent proxy, you need to set proxy type as +'http-relay' for HTTP protocol and 'http-connect' for HTTPS protocol +respectively. +Suppose your goagent local proxy is running at the same server as redsocks2, +The configuration for forwarding connections to GoAgent is like below: + + redsocks { + local_ip = 192.168.1.1; + local_port = 1081; //HTTP should be redirect to this port. + ip = 192.168.1.1; + port = 8080; + type = http-relay; // Must be 'htt-relay' for HTTP traffic. + autoproxy = 1; // I want autoproxy feature enabled on this section. + // timeout is meaningful when 'autoproxy' is non-zero. + // It specified timeout value when trying to connect to destination + // directly. Default is 10 seconds. When it is set to 0, default + // timeout value will be used. + timeout = 10; + } + redsocks { + local_ip = 192.168.1.1; + local_port = 1082; // HTTPS should be redirect to this port. + ip = 192.168.1.1; + port = 8080; + type = http-connect; // Must be 'htt-connect' for HTTPS traffic. + autoproxy = 1; // I want autoproxy feature enabled on this section. + // timeout is meaningful when 'autoproxy' is non-zero. + // It specified timeout value when trying to connect to destination + // directly. Default is 10 seconds. When it is set to 0, default + // timeout value will be used. + timeout = 10; + } + diff --git a/autoproxy.c b/autoproxy.c index b86b56d8..38774d8b 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -47,11 +47,18 @@ static void auto_connect_relay(redsocks_client *client); static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); #define CIRCUIT_RESET_SECONDS 1 -#define CONNECT_TIMEOUT_SECONDS 10 +#define DEFAULT_CONNECT_TIMEOUT_SECONDS 10 +#define CACHE_ITEM_STALE_SECONDS 60*15 #define ADDR_CACHE_BLOCKS 64 #define ADDR_CACHE_BLOCK_SIZE 16 #define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) -static struct sockaddr_in addr_cache[ADDR_CACHE_BLOCKS][ADDR_CACHE_BLOCK_SIZE]; + +typedef struct cache_data_t { + struct sockaddr_in addr; + time_t access_time; +} cache_data; + +static cache_data addr_cache[ADDR_CACHE_BLOCKS][ADDR_CACHE_BLOCK_SIZE]; static int addr_cache_counters[ADDR_CACHE_BLOCKS]; static int addr_cache_pointers[ADDR_CACHE_BLOCKS]; static int cache_init = 0; @@ -76,36 +83,71 @@ static int is_addr_in_cache(const struct sockaddr_in * addr) int i = 0; /* do reverse search for efficency */ for ( i = count - 1; i >= 0; i -- ) - /* - if (0 == memcmp((void *)addr, (void *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE], sizeof(struct sockaddr_in))) -*/ - if (addr->sin_addr.s_addr == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].sin_addr.s_addr - && addr->sin_family == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].sin_family + if (addr->sin_addr.s_addr == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr.sin_addr.s_addr + && addr->sin_family == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr.sin_family ) return 1; return 0; } -static void add_addr_to_cache(const struct sockaddr_in * addr) +static time_t * get_addr_time_in_cache(const struct sockaddr_in * addr) { + /* get block index */ int block = block_from_sockaddr_in(addr); int count = addr_cache_counters[block]; int first = addr_cache_pointers[block]; + int i = 0; + /* do reverse search for efficency */ + for ( i = count - 1; i >= 0; i -- ) + if (addr->sin_addr.s_addr == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr.sin_addr.s_addr + && addr->sin_family == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr.sin_family + ) + return &addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].access_time; + + return NULL; +} +static void add_addr_to_cache(const struct sockaddr_in * addr) +{ + int block = block_from_sockaddr_in(addr); + int count = addr_cache_counters[block]; + /* use 'first' to index item in cache block when count is equal or greater than block size */ + int first = addr_cache_pointers[block]; if (count < ADDR_CACHE_BLOCK_SIZE) { - memcpy((void *)&addr_cache[block][count], (void *) addr, sizeof(struct sockaddr_in)); + memcpy((void *)&addr_cache[block][count].addr, (void *) addr, sizeof(struct sockaddr_in)); + addr_cache[block][count].access_time = redsocks_time(NULL); addr_cache_counters[block]++; } else { - memcpy((void *)&addr_cache[block][first], (void *) addr, sizeof(struct sockaddr_in)); + memcpy((void *)&addr_cache[block][first].addr, (void *) addr, sizeof(struct sockaddr_in)); + addr_cache[block][first].access_time = redsocks_time(NULL); addr_cache_pointers[block]++; addr_cache_pointers[block]%=ADDR_CACHE_BLOCK_SIZE; } } +static void del_addr_from_cache(const struct sockaddr_in * addr) +{ /* get block index */ + int block = block_from_sockaddr_in(addr); + int count = addr_cache_counters[block]; + int first = addr_cache_pointers[block]; + int i = 0; + /* do reverse search for efficency */ + for ( i = count - 1; i >= 0; i -- ) + if (addr->sin_addr.s_addr == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr.sin_addr.s_addr + && addr->sin_family == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr.sin_family + ) + /* found. zero this item */ + { + memset((void *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE], 0, sizeof(cache_data)); + break; + } +} + + void auto_client_init(redsocks_client *client) { @@ -282,9 +324,9 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) static void auto_drop_relay(redsocks_client *client) { - redsocks_log_error(client, LOG_DEBUG, "dropping relay only"); - if (client->relay) { + redsocks_log_error(client, LOG_DEBUG, "dropping relay only"); + redsocks_close(EVENT_FD(&client->relay->ev_write)); bufferevent_free(client->relay); client->relay = NULL; @@ -293,6 +335,8 @@ static void auto_drop_relay(redsocks_client *client) static void auto_retry(redsocks_client * client, int updcache) { + autoproxy_client *aclient = (void*)(client + 1); + if (client->state == AUTOPROXY_CONNECTED) bufferevent_disable(client->client, EV_READ| EV_WRITE); /* drop relay and update state, then retry with specified relay */ @@ -305,6 +349,7 @@ static void auto_retry(redsocks_client * client, int updcache) auto_drop_relay(client); /* init subsytem as ordinary subsystem */ + memset((void *)aclient, 0, sizeof(aclient)); client->instance->relay_ss->init(client); // enable reading to handle EOF from client bufferevent_enable(client->client, EV_READ); @@ -444,16 +489,30 @@ static void auto_connect_relay(redsocks_client *client) { autoproxy_client * aclient = (void*)(client + 1); struct timeval tv; - tv.tv_sec = CONNECT_TIMEOUT_SECONDS; + tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; + time_t * acc_time = NULL; + time_t now = redsocks_time(NULL); + + /* use default timeout if timeout is not configured */ + if (tv.tv_sec == 0) + tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS; if (client->state == AUTOPROXY_NEW) { - if (is_addr_in_cache(&client->destaddr)) + acc_time = get_addr_time_in_cache(&client->destaddr); + if (acc_time) { - redsocks_log_error(client, LOG_DEBUG, "Found in cache"); - auto_retry(client, 0); - return ; + if (now - *acc_time < CACHE_ITEM_STALE_SECONDS ) + { + redsocks_log_error(client, LOG_DEBUG, "Found dest IP in cache"); + + auto_retry(client, 0); + return ; + } + else + /* stale this address in cache */ + del_addr_from_cache(&client->destaddr); } /* connect to target directly without going through proxy */ client->relay = red_connect_relay2(&client->destaddr, diff --git a/redsocks.c b/redsocks.c index 799bfafb..9dc77fbb 100644 --- a/redsocks.c +++ b/redsocks.c @@ -72,6 +72,7 @@ static parser_entry redsocks_entries[] = { .key = "min_accept_backoff", .type = pt_uint16 }, { .key = "max_accept_backoff", .type = pt_uint16 }, { .key = "autoproxy", .type = pt_uint16 }, + { .key = "timeout", .type = pt_uint16 }, { } }; @@ -129,6 +130,7 @@ static int redsocks_onenter(parser_section *section) instance->config.min_backoff_ms = 100; instance->config.max_backoff_ms = 60000; instance->config.autoproxy = 0; + instance->config.timeout = 0; for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = @@ -143,6 +145,7 @@ static int redsocks_onenter(parser_section *section) (strcmp(entry->key, "min_accept_backoff") == 0) ? (void*)&instance->config.min_backoff_ms : (strcmp(entry->key, "max_accept_backoff") == 0) ? (void*)&instance->config.max_backoff_ms : (strcmp(entry->key, "autoproxy") == 0) ? (void*)&instance->config.autoproxy : + (strcmp(entry->key, "timeout") == 0) ? (void*)&instance->config.timeout: NULL; section->data = instance; return 0; diff --git a/redsocks.conf.example b/redsocks.conf.example index 9ba5b8d2..b4447263 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -73,6 +73,11 @@ redsocks { // fails for timeout/connection refuse, redsocks will try to // connect to target via the proxy. autoproxy = 0; + // timeout is meaningful when 'autoproxy' is non-zero. + // It specified timeout value when trying to connect to destination + // directly. Default is 10 seconds. When it is set to 0, default + // timeout value will be used. + timeout = 10; // login = "foobar"; // password = "baz"; diff --git a/redsocks.h b/redsocks.h index a6f8deef..2908dd9a 100644 --- a/redsocks.h +++ b/redsocks.h @@ -33,6 +33,7 @@ typedef struct redsocks_config_t { uint16_t max_backoff_ms; // backoff capped by 65 seconds is enough :) uint16_t listenq; uint16_t autoproxy; + uint16_t timeout; } redsocks_config; struct tracked_event { From 992737893ee29994aa968ef78c2c0caef7d4c442 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 14 Nov 2013 11:09:05 +0800 Subject: [PATCH 024/192] Fix adding redundant IP to cache. Add quick detection to stale IP in cache. --- autoproxy.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index 38774d8b..2fc53012 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -48,6 +48,7 @@ static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); #define CIRCUIT_RESET_SECONDS 1 #define DEFAULT_CONNECT_TIMEOUT_SECONDS 10 +#define QUICK_CONNECT_TIMEOUT_SECONDS 2 #define CACHE_ITEM_STALE_SECONDS 60*15 #define ADDR_CACHE_BLOCKS 64 #define ADDR_CACHE_BLOCK_SIZE 16 @@ -159,6 +160,21 @@ void auto_client_init(redsocks_client *client) init_addr_cache(); } +static void on_connection_confirmed(redsocks_client *client) +{ + autoproxy_client * aclient = (void*)(client + 1); + + redsocks_log_error(client, LOG_DEBUG, "IP Confirmed"); +} + +static void on_connection_blocked(redsocks_client *client) +{ + autoproxy_client * aclient = (void*)(client + 1); + + redsocks_log_error(client, LOG_DEBUG, "IP Blocked"); + +} + static void direct_relay_readcb_helper(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) { if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { @@ -187,6 +203,7 @@ static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) { /* No CONNECTION RESET error occur after sending data, good. */ client->state = AUTOPROXY_CONFIRMED; + on_connection_confirmed(client); if (evbuffer_get_length(from->input)) { evbuffer_drain(from->input, aclient->data_sent); @@ -295,6 +312,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) evbuffer_drain(from->input, aclient->data_sent); aclient->data_sent = 0; client->state = AUTOPROXY_CONFIRMED; + on_connection_confirmed(client); } } /* We sent data to and got data from relay. */ @@ -302,6 +320,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) { /* No CONNECTION RESET error occur after sending data, good. */ client->state = AUTOPROXY_CONFIRMED; + on_connection_confirmed(client); redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", aclient->data_recv); if (evbuffer_get_length(from->input)) { @@ -342,9 +361,13 @@ static void auto_retry(redsocks_client * client, int updcache) /* drop relay and update state, then retry with specified relay */ if (updcache) { - add_addr_to_cache(&client->destaddr); - redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %s", + /* only add IP to cache when the IP is not in cache */ + if (get_addr_time_in_cache(&client->destaddr) == NULL) + { + add_addr_to_cache(&client->destaddr); + redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %s", inet_ntoa(client->destaddr.sin_addr)); + } } auto_drop_relay(client); @@ -370,6 +393,7 @@ static int auto_retry_or_drop(redsocks_client * client) { if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) { + on_connection_blocked(client); auto_retry(client, 0); return 0; } @@ -378,6 +402,7 @@ static int auto_retry_or_drop(redsocks_client * client) { // if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) { + on_connection_blocked(client); auto_retry(client, 0); return 0; } @@ -447,6 +472,7 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) if ( client->state == AUTOPROXY_NEW && what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT)) { + on_connection_blocked(client); /* In case timeout occurs while connecting relay, we try to connect to target via SOCKS5 proxy. It is possible that the connection to target can be set up a bit longer than the timeout value we set. @@ -511,8 +537,12 @@ static void auto_connect_relay(redsocks_client *client) return ; } else + { /* stale this address in cache */ del_addr_from_cache(&client->destaddr); + /* update timeout value for quick detection */ + tv.tv_sec = QUICK_CONNECT_TIMEOUT_SECONDS; + } } /* connect to target directly without going through proxy */ client->relay = red_connect_relay2(&client->destaddr, From f3662ab4628a1db97fa1c7ff384b215f6164a214 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 16 Nov 2013 11:14:04 +0800 Subject: [PATCH 025/192] Fix crash when working with GoAgent while autoproxy is ON. --- autoproxy.c | 74 ++++++++++++++++++++++++++--------------------------- redsocks.c | 6 ++--- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index 2fc53012..b6c22144 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -35,6 +35,7 @@ typedef enum autoproxy_state_t { } autoproxy_state; typedef struct autoproxy_client_t { + autoproxy_state state; time_t time_connect_relay; // timestamp when start to connect relay size_t data_recv; size_t data_sent; @@ -50,7 +51,7 @@ static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); #define DEFAULT_CONNECT_TIMEOUT_SECONDS 10 #define QUICK_CONNECT_TIMEOUT_SECONDS 2 #define CACHE_ITEM_STALE_SECONDS 60*15 -#define ADDR_CACHE_BLOCKS 64 +#define ADDR_CACHE_BLOCKS 128 #define ADDR_CACHE_BLOCK_SIZE 16 #define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) @@ -152,9 +153,9 @@ static void del_addr_from_cache(const struct sockaddr_in * addr) void auto_client_init(redsocks_client *client) { - autoproxy_client * aclient = (void*)(client + 1); + autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; - client->state = AUTOPROXY_NEW; + aclient->state = AUTOPROXY_NEW; aclient->data_recv = 0; aclient->data_sent = 0; init_addr_cache(); @@ -162,17 +163,12 @@ void auto_client_init(redsocks_client *client) static void on_connection_confirmed(redsocks_client *client) { - autoproxy_client * aclient = (void*)(client + 1); - redsocks_log_error(client, LOG_DEBUG, "IP Confirmed"); } static void on_connection_blocked(redsocks_client *client) { - autoproxy_client * aclient = (void*)(client + 1); - redsocks_log_error(client, LOG_DEBUG, "IP Blocked"); - } static void direct_relay_readcb_helper(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) @@ -193,16 +189,16 @@ static void direct_relay_readcb_helper(redsocks_client *client, struct buffereve static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) { redsocks_client *client = _client; - autoproxy_client *aclient = (void*)(client + 1); + autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; redsocks_touch_client(client); - if (client->state == AUTOPROXY_CONNECTED) + if (aclient->state == AUTOPROXY_CONNECTED) { if (aclient->data_sent && aclient->data_recv) { /* No CONNECTION RESET error occur after sending data, good. */ - client->state = AUTOPROXY_CONFIRMED; + aclient->state = AUTOPROXY_CONFIRMED; on_connection_confirmed(client); if (evbuffer_get_length(from->input)) { @@ -218,7 +214,7 @@ static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) { redsocks_client *client = _client; - autoproxy_client *aclient = (void*)(client + 1); + autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; redsocks_touch_client(client); if (!aclient->data_recv) @@ -229,7 +225,7 @@ static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; - autoproxy_client *aclient = (void*)(client + 1); + autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; struct bufferevent * from = client->relay; redsocks_touch_client(client); @@ -238,7 +234,7 @@ static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) redsocks_shutdown(client, to, SHUT_WR); return; } - if (client->state == AUTOPROXY_CONNECTED) + if (aclient->state == AUTOPROXY_CONNECTED) { if (!aclient->data_recv) aclient->data_recv = EVBUFFER_LENGTH(from->input); @@ -256,7 +252,7 @@ static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; - autoproxy_client *aclient = (void*)(client + 1); + autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; struct bufferevent * from = client->client; redsocks_touch_client(client); @@ -265,7 +261,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) redsocks_shutdown(client, to, SHUT_WR); return; } - else if (client->state == AUTOPROXY_CONNECTED ) + else if (aclient->state == AUTOPROXY_CONNECTED ) { /* Not send or receive data. */ if (!aclient->data_sent && !aclient->data_recv) @@ -286,7 +282,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) redsocks_log_error(client, LOG_DEBUG, "not sent, got"); aclient->data_sent = copy_evbuffer(to, from, 0); } - /* client->state = AUTOPROXY_CONFIRMED; */ + /* aclient->state = AUTOPROXY_CONFIRMED; */ /* We have writen data to relay, but got nothing until we are requested to * write to it again. */ @@ -311,7 +307,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) { evbuffer_drain(from->input, aclient->data_sent); aclient->data_sent = 0; - client->state = AUTOPROXY_CONFIRMED; + aclient->state = AUTOPROXY_CONFIRMED; on_connection_confirmed(client); } } @@ -319,7 +315,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) else if (aclient->data_sent && aclient->data_recv) { /* No CONNECTION RESET error occur after sending data, good. */ - client->state = AUTOPROXY_CONFIRMED; + aclient->state = AUTOPROXY_CONFIRMED; on_connection_confirmed(client); redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", aclient->data_recv); if (evbuffer_get_length(from->input)) @@ -330,7 +326,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) } } - if (client->state == AUTOPROXY_CONFIRMED) + if (aclient->state == AUTOPROXY_CONFIRMED) { if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) @@ -354,9 +350,9 @@ static void auto_drop_relay(redsocks_client *client) static void auto_retry(redsocks_client * client, int updcache) { - autoproxy_client *aclient = (void*)(client + 1); + autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; - if (client->state == AUTOPROXY_CONNECTED) + if (aclient->state == AUTOPROXY_CONNECTED) bufferevent_disable(client->client, EV_READ| EV_WRITE); /* drop relay and update state, then retry with specified relay */ if (updcache) @@ -371,9 +367,8 @@ static void auto_retry(redsocks_client * client, int updcache) } auto_drop_relay(client); - /* init subsytem as ordinary subsystem */ - memset((void *)aclient, 0, sizeof(aclient)); - client->instance->relay_ss->init(client); + // restore callbacks for ordinary client. + bufferevent_setcb(client->client, NULL, NULL, client->client->errorcb, client); // enable reading to handle EOF from client bufferevent_enable(client->client, EV_READ); /* connect to relay */ @@ -381,15 +376,18 @@ static void auto_retry(redsocks_client * client, int updcache) client->instance->relay_ss->connect_relay(client); else redsocks_connect_relay(client); + // + if (EVBUFFER_LENGTH(client->client->input) && client->client->readcb) + client->client->readcb(client->client, client); } /* return 1 for drop, 0 for retry. */ static int auto_retry_or_drop(redsocks_client * client) { time_t now = redsocks_time(NULL); - autoproxy_client *aclient = (void*)(client + 1); + autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; - if (client->state == AUTOPROXY_NEW) + if (aclient->state == AUTOPROXY_NEW) { if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) { @@ -398,7 +396,7 @@ static int auto_retry_or_drop(redsocks_client * client) return 0; } } - else if ( client->state == AUTOPROXY_CONNECTED) + else if ( aclient->state == AUTOPROXY_CONNECTED) { // if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) { @@ -415,13 +413,14 @@ static int auto_retry_or_drop(redsocks_client * client) static void auto_relay_connected(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; + autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; assert(buffev == client->relay); redsocks_touch_client(client); if (!red_is_socket_connected_ok(buffev)) { - if (client->state == AUTOPROXY_NEW && !auto_retry_or_drop(client)) + if (aclient->state == AUTOPROXY_NEW && !auto_retry_or_drop(client)) return; redsocks_log_error(client, LOG_DEBUG, "failed to connect to proxy"); @@ -429,7 +428,7 @@ static void auto_relay_connected(struct bufferevent *buffev, void *_arg) } /* update client state */ - client->state = AUTOPROXY_CONNECTED; + aclient->state = AUTOPROXY_CONNECTED; /* We do not need to detect timeouts any more. The two peers will handle it. */ @@ -458,6 +457,7 @@ static void auto_relay_connected(struct bufferevent *buffev, void *_arg) static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) { redsocks_client *client = _arg; + autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; int saved_errno = errno; assert(buffev == client->relay || buffev == client->client); @@ -465,11 +465,11 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) redsocks_log_errno(client, LOG_DEBUG, "%s errno(%d), State: %d, what: " event_fmt_str, buffev == client->client?"client":"relay", - saved_errno, client->state, event_fmt(what)); + saved_errno, aclient->state, event_fmt(what)); if (buffev == client->relay) { - if ( client->state == AUTOPROXY_NEW + if ( aclient->state == AUTOPROXY_NEW && what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT)) { on_connection_blocked(client); @@ -481,11 +481,11 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) return; } - if (client->state == AUTOPROXY_NEW && saved_errno == ECONNRESET) + if (aclient->state == AUTOPROXY_NEW && saved_errno == ECONNRESET) if (!auto_retry_or_drop(client)) return; - if (client->state == AUTOPROXY_CONNECTED && what == (EVBUFFER_READ|EVBUFFER_ERROR) + if (aclient->state == AUTOPROXY_CONNECTED && what == (EVBUFFER_READ|EVBUFFER_ERROR) && saved_errno == ECONNRESET ) { if (!auto_retry_or_drop(client)) @@ -513,7 +513,7 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) static void auto_connect_relay(redsocks_client *client) { - autoproxy_client * aclient = (void*)(client + 1); + autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; struct timeval tv; tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; @@ -524,7 +524,7 @@ static void auto_connect_relay(redsocks_client *client) if (tv.tv_sec == 0) tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS; - if (client->state == AUTOPROXY_NEW) + if (aclient->state == AUTOPROXY_NEW) { acc_time = get_addr_time_in_cache(&client->destaddr); if (acc_time) @@ -558,7 +558,7 @@ static void auto_connect_relay(redsocks_client *client) } else { - redsocks_log_errno(client, LOG_ERR, "invalid state: %d", client->state); + redsocks_log_errno(client, LOG_ERR, "invalid state: %d", aclient->state); } } diff --git a/redsocks.c b/redsocks.c index 9dc77fbb..a258b4d8 100644 --- a/redsocks.c +++ b/redsocks.c @@ -683,8 +683,7 @@ static void redsocks_accept_client(int fd, short what, void *_arg) // everything seems to be ok, let's allocate some memory if (self->config.autoproxy) client = calloc(1, sizeof(redsocks_client) + - (self->relay_ss->payload_len>= autoproxy_subsys.payload_len? - self->relay_ss->payload_len: autoproxy_subsys.payload_len) + self->relay_ss->payload_len + autoproxy_subsys.payload_len ); else client = calloc(1, sizeof(redsocks_client) + self->relay_ss->payload_len); @@ -697,10 +696,9 @@ static void redsocks_accept_client(int fd, short what, void *_arg) memcpy(&client->clientaddr, &clientaddr, sizeof(clientaddr)); memcpy(&client->destaddr, &destaddr, sizeof(destaddr)); INIT_LIST_HEAD(&client->list); + self->relay_ss->init(client); if (self->config.autoproxy) autoproxy_subsys.init(client); - else - self->relay_ss->init(client); if (redsocks_time(&client->first_event) == ((time_t)-1)) goto fail; From 3f2b9265a00d2fac810366f2cacdd1c90316b27c Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 22 Mar 2014 18:24:25 +0800 Subject: [PATCH 026/192] Fix: only monitor child process in daemon mode. --- base.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/base.c b/base.c index 04532de6..1d2e0933 100644 --- a/base.c +++ b/base.c @@ -379,7 +379,7 @@ static void myproc() { last = atol(buf); now = time(NULL); - if (now-last>4) + if (now-last>10) { kill(pid, SIGKILL); sleep(1); @@ -511,8 +511,9 @@ static int base_init() } close(devnull); + /* only fork and monitor child process when running as daemon */ + myproc(); } - myproc(); return 0; fail: if (devnull != -1) From 64dfe9e5994b7831fc0b76e8dba5d240b7bc58d1 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 27 Mar 2014 18:11:01 +0800 Subject: [PATCH 027/192] Always try to connect target directly with short timeout for cached IP before go through proxy --- autoproxy.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index b6c22144..1ad9168c 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -163,6 +163,7 @@ void auto_client_init(redsocks_client *client) static void on_connection_confirmed(redsocks_client *client) { + del_addr_from_cache(&client->destaddr); redsocks_log_error(client, LOG_DEBUG, "IP Confirmed"); } @@ -529,19 +530,22 @@ static void auto_connect_relay(redsocks_client *client) acc_time = get_addr_time_in_cache(&client->destaddr); if (acc_time) { - if (now - *acc_time < CACHE_ITEM_STALE_SECONDS ) - { - redsocks_log_error(client, LOG_DEBUG, "Found dest IP in cache"); - - auto_retry(client, 0); - return ; - } - else + redsocks_log_error(client, LOG_DEBUG, "Found dest IP in cache"); + /* update timeout value for quick detection. + * Sometimes, good sites are added into cache due to occasionally + * connection timeout. It is annoying. So, decision is made to + * always try to connect to destination first when the destination + * is found in cache. + * For most destinations, the connection could be set up correctly + * in short time. And, for most blocked sites, we get connection + * reset almost immediately when connection is set up or when HTTP + * request is sent. + */ + tv.tv_sec = QUICK_CONNECT_TIMEOUT_SECONDS; + if (now - *acc_time >= CACHE_ITEM_STALE_SECONDS ) { /* stale this address in cache */ del_addr_from_cache(&client->destaddr); - /* update timeout value for quick detection */ - tv.tv_sec = QUICK_CONNECT_TIMEOUT_SECONDS; } } /* connect to target directly without going through proxy */ From 7ded6496d1cb09eb0b747769cabe21b306c744c6 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 27 Mar 2014 18:16:23 +0800 Subject: [PATCH 028/192] Enable timeout for outgoing relay connection --- redsocks.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/redsocks.c b/redsocks.c index a258b4d8..8c830535 100644 --- a/redsocks.c +++ b/redsocks.c @@ -579,8 +579,14 @@ void redsocks_relay_connected(struct bufferevent *buffev, void *_arg) void redsocks_connect_relay(redsocks_client *client) { - client->relay = red_connect_relay(&client->instance->config.relayaddr, - redsocks_relay_connected, redsocks_event_error, client); + struct timeval tv; + tv.tv_sec = client->instance->config.timeout; + tv.tv_usec = 0; + if (tv.tv_sec == 0) + tv.tv_sec = 10; + + client->relay = red_connect_relay2(&client->instance->config.relayaddr, + redsocks_relay_connected, redsocks_event_error, client, &tv); if (!client->relay) { redsocks_log_errno(client, LOG_ERR, "red_connect_relay"); redsocks_drop_client(client); From 50234c0c60e412875997277600ad27c4f9d1b6a1 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 4 Apr 2014 16:03:06 +0800 Subject: [PATCH 029/192] Change version number to 0.51 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c8ecd2f6..e84404bf 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks2 -VERSION := 0.5 +VERSION := 0.51 LIBS := -levent CFLAGS +=-fPIC -O2 \ From 4b8ce8d1e5bb4364115159ebd143fd514bc5b54e Mon Sep 17 00:00:00 2001 From: "V.E.O" Date: Thu, 11 Dec 2014 19:24:00 +0800 Subject: [PATCH 030/192] fix proxy request --- http-relay.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/http-relay.c b/http-relay.c index 4a394518..fb1671de 100644 --- a/http-relay.c +++ b/http-relay.c @@ -403,7 +403,8 @@ static int httpr_toss_http_firstline(redsocks_client *client) uri = strchr(httpr->firstline, ' '); if (uri) - uri += 1; // one char further + while (*uri == ' ') + uri += 1; // one char further else { redsocks_log_error(client, LOG_NOTICE, "malformed request came"); goto fail; @@ -417,10 +418,12 @@ static int httpr_toss_http_firstline(redsocks_client *client) if (httpr_buffer_append(&nbuff, httpr->firstline, uri - httpr->firstline) != 0) goto addition_fail; - if (httpr_buffer_append(&nbuff, "http://", 7) != 0) - goto addition_fail; - if (httpr_buffer_append(&nbuff, host, strlen(host)) != 0) - goto addition_fail; + if (*uri == '/') { + if (httpr_buffer_append(&nbuff, "http://", 7) != 0) + goto addition_fail; + if (httpr_buffer_append(&nbuff, host, strlen(host)) != 0) + goto addition_fail; + } if (httpr_buffer_append(&nbuff, uri, strlen(uri)) != 0) goto addition_fail; if (httpr_buffer_append(&nbuff, "\x0d\x0a", 2) != 0) From d95007974280be710a046718bfc52faa3ebe9c0d Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 16 Dec 2014 14:12:59 +0800 Subject: [PATCH 031/192] Rewrite the program and drop support to libevent 1.x. --- Makefile | 6 +- README | 63 +- README.html | 215 ------ README.md | 38 +- autoproxy.c | 1065 +++++++++++++++------------ base.c | 4 +- direct.c | 75 +- main.c | 23 +- main.h | 1 + redsocks.c | 1583 +++++++++++++++++++++-------------------- redsocks.conf.example | 6 +- redsocks.h | 3 + redudp.c | 6 +- utils.c | 545 +++++++------- utils.h | 25 +- 15 files changed, 1835 insertions(+), 1823 deletions(-) delete mode 100644 README.html diff --git a/Makefile b/Makefile index e84404bf..e17bc3c4 100644 --- a/Makefile +++ b/Makefile @@ -3,12 +3,10 @@ SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks2 -VERSION := 0.51 +VERSION := 0.60 LIBS := -levent -CFLAGS +=-fPIC -O2 \ - -I ~/openwrt/openwrt/staging_dir/target-mipsel_dsp_uClibc-0.9.33.2/usr/include/ \ - -L ~/openwrt/openwrt/staging_dir/target-mipsel_dsp_uClibc-0.9.33.2/usr/lib/ +CFLAGS +=-fPIC -O3 override CFLAGS += -std=gnu99 -Wall #LDFLAGS += -fwhole-program diff --git a/README b/README index 63f52f3b..8c8c5cb0 100644 --- a/README +++ b/README @@ -1,63 +1,6 @@ -This is a modified version of original redsocks. -This variant is useful for anti-GFW (Great Fire Wall). - -Note: -Method 'autosocks5' and 'autohttp-connect' are removed. -To use the autoproxy feature, please change the redsocks section in -configuration file like this: - -redsocks { - local_ip = 192.168.1.1; - local_port = 1081; - ip = 192.168.1.1; - port = 9050; - type = socks5; // I use socks5 proxy for GFW'ed IP - autoproxy = 1; // I want autoproxy feature enabled on this section. - // The two lines above have same effect as - // type = autosocks5; - // in previous release. - //type = http-connect; - //login = username; - //password = passwd; -} - --------------------------------------------------------------------- -It provides the following advanced features: -1. new 'direct' mehtod. - This method is intent to direct any TCP connections directly without -going through a proxy. Why this mehtod? Some ISP, like China Mobile (CMCC), -detects numbers of computers in your local network sharing same account by -montioring HTTP/HTTPS traffic. Only one computer has full access to all -websites. Other computers in local network are limited to some websites. -By depolying transparent proxy with this method, all your computers can -access websites without restrictions. -2. new 'autosocks5' method. (REPLACED BY NEW METHOD!!!!) - This mehtod is specially customized for fighting against GFW. By -default, all TCP connections are redirected to connect to target directly -without going through socks5 proxy. But, in case the connection to target -is RESET/CLOSED immediately for some reason (e.g. by GFW), the connection -is redirected via socks5 proxy. The same logic is also applied when the -connection to target fails to establish in certain seconds (13 seconds -currently). For such slow connection, no matter it is really a slow -connection or the handshake packets are dropped by GFW, making such -connections go through proxy is not a bad idea. - REDSOCKS also maintances a cache for addresses that need to be relayed -via proxy. So, whenever an address is identified need to be relayed via -proxy, the subsequent connections to those addresses will be relayed via -proxy immediately unless the addresses are removed from cache by code logic. - The advantage of this method is that you do not need to set thousands -entries in iptables for blocked sites manually. It is especially useful -for proxying Youtube which has so many sub-domains for video content cache. -3. new 'autohttp-connect' method. (REPLACED BY NEW METHOD!!!!) - This method behaves similar as 'autoscoks5' method except the proxy -to be used must support HTTP CONNECT method. This method is useful for -using redsocks together with GoProxy. - -HOW TO BUILD: -Since this variant of redsocks is customized for running with Openwrt, please -read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how -to compile. - +This is a modified version of original redsocks and is useful for +anti-GFW (Great Fire Wall). +The content below is from original redsocks project. --------------------------------------------------------------------- This tool allows you to redirect any TCP connection to SOCKS or HTTPS proxy using your firewall, so redirection is system-wide. diff --git a/README.html b/README.html deleted file mode 100644 index ba669df9..00000000 --- a/README.html +++ /dev/null @@ -1,215 +0,0 @@ - - - - - -redsocks - transparent socks redirector - - - -

redsocks - transparent socks redirector

- - -
- -

This tool allows you to redirect any TCP connection to SOCKS or HTTPS -proxy using your firewall, so redirection is system-wide.

- -

Why is that useful? I can suggest following reasons:

-
    -
  • you use tor and don't want any TCP connection to leak
  • -
  • you use DVB ISP and this ISP provides internet connectivity with some - special daemon that may be also called "Internet accelerator" and this - accelerator acts as proxy. Globax is example of such an accelerator
  • -
- -

Linux/iptables, OpenBSD/pf and FreeBSD/ipfw are supported. -Linux/iptables is well-tested, other implementations may have bugs, -your bugreports are welcome.

- -

Transocks is alike project but it has noticable performance penality.

- -

Transsocks_ev is alike project too, but it has no HTTPS-proxy support -and does not support authentication.

- -

Several Andoird apps also use redsocks under-the-hood: ProxyDroid (@AndroidMarket) and -sshtunnel (@AndroidMarket). And that's over 100'000 downloads! Wow!

- -

Another related issue is DNS over TCP. Redsocks includes `dnstc' that is fake -and really dumb DNS server that returns "truncated answer" to every query via -UDP. RFC-compliant resolver should repeat same query via TCP in this case - so -the request can be redirected using usual redsocks facilities.

- -

Known compliant resolvers are:

-
    -
  • bind9 (server)
  • -
  • dig, nslookup (tools based on bind9 code)
  • -
-

Known non-compliant resolvers are:

-
    -
  • eglibc resolver fails without any attempt to send request via TCP
  • -
  • powerdns-recursor can't properly startup without UDP connectivity as it - can't load root hints
  • -
- -

On the other hand, DNS via TCP using bind9 may be painfully slow. If your bind9 -setup is really slow, you have at least two options: pdnsd caching server -can run in TCP-only mode, ttdnsd (git repo) has no cache but can be useful for same -purpose.

- -

Features

- -

Redirect any TCP connection to SOCKS4, SOCKS5 or HTTPS (HTTP/CONNECT) -proxy server.

- -

Login/password authentication is supported for SOCKS5/HTTPS connections. -SOCKS4 supports only username, password is ignored. for HTTPS, currently -only Basic and Digest scheme is supported.

- -

Redirect UDP packets via SOCKS5 proxy server. NB: UDP still goes via UDP, so -you can't relay UDP via OpenSSH.

- -

Sends "truncated reply" as an answer to UDP DNS queries.

- -

Redirect any HTTP connection to proxy that does not support transparent -proxying (e.g. old SQUID had broken `acl myport' for such connections).

- - -

License

- -

All source code is licensed under Apache 2.0 license.

-

You can get a copy at http://www.apache.org/licenses/LICENSE-2.0.html

- - -

Compilation

- -

libevent is required.

- -

gcc is only supported compiler right now, other compilers can be used -but may require some code changes.

- -

Compilation is as easy as running `make', there is no `./configure' magic.

- -

GNU Make works, other implementations of make were not tested.

- - -

Running

- -

Program has following command-line options:
--c sets proper path to config file ("./redsocks.conf" is default one)
--t tests config file syntax
--p set a file to write the getpid() into

- -

Following signals are understood:
-SIGUSR1 dumps list of connected clients to log
-SIGTERM and SIGINT terminates daemon, all active connections are closed

- -

You can see configuration file example in redsocks.conf.example

- - -

iptables example

- -

You have to build iptables with connection tracking and REDIRECT target.

- -
-# Create new chain
-root# iptables -t nat -N REDSOCKS
-
-# Ignore LANs and some other reserved addresses.
-# See Wikipedia and RFC5735 for full list of reserved networks.
-root# iptables -t nat -A REDSOCKS -d 0.0.0.0/8 -j RETURN
-root# iptables -t nat -A REDSOCKS -d 10.0.0.0/8 -j RETURN
-root# iptables -t nat -A REDSOCKS -d 127.0.0.0/8 -j RETURN
-root# iptables -t nat -A REDSOCKS -d 169.254.0.0/16 -j RETURN
-root# iptables -t nat -A REDSOCKS -d 172.16.0.0/12 -j RETURN
-root# iptables -t nat -A REDSOCKS -d 192.168.0.0/16 -j RETURN
-root# iptables -t nat -A REDSOCKS -d 224.0.0.0/4 -j RETURN
-root# iptables -t nat -A REDSOCKS -d 240.0.0.0/4 -j RETURN
-
-# Anything else should be redirected to port 12345
-root# iptables -t nat -A REDSOCKS -p tcp -j REDIRECT --to-ports 12345
-
-# Any tcp connection made by `luser' should be redirected.
-root# iptables -t nat -A OUTPUT -p tcp -m owner --uid-owner luser -j REDSOCKS
-
-# You can also control that in more precise way using `gid-owner` from
-# iptables.
-root# groupadd socksified
-root# usermod --append --groups socksified luser
-root# iptables -t nat -A OUTPUT -p tcp -m owner --gid-owner socksified -j REDSOCKS
-
-# Now you can launch your specific application with GID `socksified` and it
-# will be... socksified. See following commands (numbers may vary).
-# Note: you may have to relogin to apply `usermod` changes.
-luser$ id
-uid=1000(luser) gid=1000(luser) groups=1000(luser),1001(socksified)
-luser$ sg socksified -c id
-uid=1000(luser) gid=1001(socksified) groups=1000(luser),1001(socksified)
-luser$ sg socksified -c "firefox"
-
-# If you want to configure socksifying router, you should look at
-# doc/iptables-packet-flow.png and doc/iptables-packet-flow-ng.png
-# Note, you should have proper `local_ip' value to get external packets with
-# redsocks, default 127.0.0.1 will not go. See iptables(8) manpage regarding
-# REDIRECT target for details.
-# Depending on your network configuration iptables conf. may be as easy as:
-root# iptables -t nat -A PREROUTING --in-interface eth_int -p tcp -j REDSOCKS
-
- -

Note about GID-based redirection

-

-Keep in mind, that changed GID affects filesystem permissions, so if your -application creates some files, the files will be created with luser:socksified -owner/group. So, if you're not the only user in the group `socksified` and your -umask allows to create group-readable files and your directory permissions, and -so on, blah-blah, etc. THEN you may expose your files to another user. -

-

-Ok, you have been warned. -

- -

Homepage

- -

Homepage: http://darkk.net.ru/redsocks/

- -

Mailing list: redsocks@librelist.com

- -

Mailing list also has archives.

- - -

TODO

- -
    -
  • Test OpenBSD (pf) and FreeBSD (ipfw) and write setup examples for those - firewall types.
  • -
- - -

Author

-This program was written by Leonid Evdokimov. - - - - -
- (~~~~~~~~~~\   __
- | jabber:  )  /  \
- | mailto: (  /|oo \
-  \_________\(_|  /_)
-              _ @/_ \
-             |     | \   \\
-      leon   | (*) |  \   ))  darkk.net.ru
-             |__U__| /  \//
-              _//|| _\   /
-             (_/(_|(____/
-                         Valid CSS!
-                         Valid XHTML 1.0 Transitional
-
- - - - diff --git a/README.md b/README.md index 61023a2a..6f3d0a57 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,17 @@ This variant is useful for anti-GFW (Great Fire Wall). HOW it works ------------ -Who can help me to complete this part? -_- +Anyone can help me to complete this part? -_- +HOW TO BUILD +------------ +On general linux, simply run command below to build. + + make + +Since this variant of redsocks is customized for running with Openwrt, please +read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how +to cross compile. ##Note: Method 'autosocks5' and 'autohttp-connect' are removed. @@ -29,12 +38,30 @@ configuration file like this: // It specified timeout value when trying to connect to destination // directly. Default is 10 seconds. When it is set to 0, default // timeout value will be used. - timeout = 10; + // NOTE: decrease the timeout value may lead increase of chance for + // normal IP to be misjudged. + timeout = 13; //type = http-connect; //login = username; //password = passwd; } + +##Redirect Blocked Traffic via VPN Automatically +Suppose you have VPN connection setup with interface tun0. You want all +all blocked traffic pass through via VPN connection while normal traffic +pass through via default internet connection. + + redsocks { + local_ip = 192.168.1.1; + local_port = 1080; + interface = tun0; // Outgoing interface for blocked traffic + type = direct; + timeout = 13; + autoproxy = 1; + } + + ##Work with GoAgent To make redsocks2 works with GoAgent proxy, you need to set proxy type as 'http-relay' for HTTP protocol and 'http-connect' for HTTPS protocol @@ -53,7 +80,7 @@ The configuration for forwarding connections to GoAgent is like below: // It specified timeout value when trying to connect to destination // directly. Default is 10 seconds. When it is set to 0, default // timeout value will be used. - timeout = 10; + timeout = 13; } redsocks { local_ip = 192.168.1.1; @@ -66,6 +93,9 @@ The configuration for forwarding connections to GoAgent is like below: // It specified timeout value when trying to connect to destination // directly. Default is 10 seconds. When it is set to 0, default // timeout value will be used. - timeout = 10; + timeout = 13; } +AUTHOR +------ +Zhuofei Wang diff --git a/autoproxy.c b/autoproxy.c index 1ad9168c..250cc47d 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -1,9 +1,7 @@ -/* Copyright (C) 2013 Zhuofei Wang - * - * - * redsocks - transparent TCP-to-proxy redirector - * Copyright (C) 2007-2011 Leonid Evdokimov +/* redsocks2 - transparent TCP-to-proxy redirector + * Copyright (C) 2013-2014 Zhuofei Wang * + * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at @@ -17,6 +15,8 @@ * under the License. */ + + #include #include #include @@ -28,36 +28,44 @@ #include "redsocks.h" typedef enum autoproxy_state_t { - /* Introduce subsystem */ - AUTOPROXY_NEW=10000, - AUTOPROXY_CONNECTED, - AUTOPROXY_CONFIRMED, + /* Introduce subsystem */ + AUTOPROXY_NEW=10000, + AUTOPROXY_CONNECTED, + AUTOPROXY_CONFIRMED, + AUTOPROXY_SHUTDOWN, // Not used } autoproxy_state; typedef struct autoproxy_client_t { - autoproxy_state state; - time_t time_connect_relay; // timestamp when start to connect relay - size_t data_recv; - size_t data_sent; + autoproxy_state state; + time_t time_connect_relay; // timestamp when start to connect relay + size_t data_recv; + size_t data_sent; + struct event * recv_timer_event; + int quick_check; // flag indicating quick check initiated. } autoproxy_client; void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); +void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg); static int auto_retry_or_drop(redsocks_client * client); static void auto_connect_relay(redsocks_client *client); static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); +static void auto_event_error(struct bufferevent *buffev, short what, void *_arg); #define CIRCUIT_RESET_SECONDS 1 #define DEFAULT_CONNECT_TIMEOUT_SECONDS 10 -#define QUICK_CONNECT_TIMEOUT_SECONDS 2 -#define CACHE_ITEM_STALE_SECONDS 60*15 -#define ADDR_CACHE_BLOCKS 128 +#define QUICK_CONNECT_TIMEOUT_SECONDS 3 +#define NO_CHECK_SECONDS 60 +#define CACHE_ITEM_STALE_SECONDS 60*30 +#define ADDR_CACHE_BLOCKS 256 #define ADDR_CACHE_BLOCK_SIZE 16 +#define ADDR_PORT_CHECK 1 #define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) +#define get_autoproxy_client(client) (void*)(client + 1) + client->instance->relay_ss->payload_len; typedef struct cache_data_t { - struct sockaddr_in addr; - time_t access_time; + struct sockaddr_in addr; + time_t access_time; } cache_data; static cache_data addr_cache[ADDR_CACHE_BLOCKS][ADDR_CACHE_BLOCK_SIZE]; @@ -66,522 +74,665 @@ static int addr_cache_pointers[ADDR_CACHE_BLOCKS]; static int cache_init = 0; static void init_addr_cache() -{ - if (!cache_init) - { - memset((void *)addr_cache, 0, sizeof(addr_cache)); - memset((void *)addr_cache_counters, 0, sizeof(addr_cache_counters)); - memset((void *)addr_cache_pointers, 0, sizeof(addr_cache_pointers)); - cache_init = 1; - } +{ + if (!cache_init) + { + memset((void *)addr_cache, 0, sizeof(addr_cache)); + memset((void *)addr_cache_counters, 0, sizeof(addr_cache_counters)); + memset((void *)addr_cache_pointers, 0, sizeof(addr_cache_pointers)); + cache_init = 1; + } } static int is_addr_in_cache(const struct sockaddr_in * addr) { - /* get block index */ - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - int first = addr_cache_pointers[block]; - int i = 0; - /* do reverse search for efficency */ - for ( i = count - 1; i >= 0; i -- ) - if (addr->sin_addr.s_addr == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr.sin_addr.s_addr - && addr->sin_family == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr.sin_family - ) - return 1; - - return 0; + /* get block index */ + int block = block_from_sockaddr_in(addr); + int count = addr_cache_counters[block]; + int first = addr_cache_pointers[block]; + int i = 0; + /* do reverse search for efficency */ + for ( i = count - 1; i >= 0; i -- ) + { + if (0 == evutil_sockaddr_cmp((const struct sockaddr *)addr, + (const struct sockaddr *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr, + ADDR_PORT_CHECK)) + return 1; + } + return 0; } static time_t * get_addr_time_in_cache(const struct sockaddr_in * addr) { - /* get block index */ - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - int first = addr_cache_pointers[block]; - int i = 0; - /* do reverse search for efficency */ - for ( i = count - 1; i >= 0; i -- ) - if (addr->sin_addr.s_addr == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr.sin_addr.s_addr - && addr->sin_family == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr.sin_family - ) - return &addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].access_time; - - return NULL; + /* get block index */ + int block = block_from_sockaddr_in(addr); + int count = addr_cache_counters[block]; + int first = addr_cache_pointers[block]; + int i = 0; + /* do reverse search for efficency */ + for ( i = count - 1; i >= 0; i -- ) + { + if (0 == evutil_sockaddr_cmp((const struct sockaddr *)addr, + (const struct sockaddr *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr, + ADDR_PORT_CHECK)) + return &addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].access_time; + } + return NULL; } + +void set_addr_time_in_cache(const struct sockaddr_in * addr, time_t time) +{ + /* get block index */ + int block = block_from_sockaddr_in(addr); + int count = addr_cache_counters[block]; + int first = addr_cache_pointers[block]; + int i = 0; + /* do reverse search for efficency */ + for ( i = count - 1; i >= 0; i -- ) + { + if (0 == evutil_sockaddr_cmp((const struct sockaddr *)addr, + (const struct sockaddr *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr, + ADDR_PORT_CHECK)) + { + addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].access_time = time; + return; + } + } +} + static void add_addr_to_cache(const struct sockaddr_in * addr) { - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - /* use 'first' to index item in cache block when count is equal or greater than block size */ - int first = addr_cache_pointers[block]; - - if (count < ADDR_CACHE_BLOCK_SIZE) - { - memcpy((void *)&addr_cache[block][count].addr, (void *) addr, sizeof(struct sockaddr_in)); - addr_cache[block][count].access_time = redsocks_time(NULL); - addr_cache_counters[block]++; - } - else - { - memcpy((void *)&addr_cache[block][first].addr, (void *) addr, sizeof(struct sockaddr_in)); - addr_cache[block][first].access_time = redsocks_time(NULL); - addr_cache_pointers[block]++; - addr_cache_pointers[block]%=ADDR_CACHE_BLOCK_SIZE; - } + int block = block_from_sockaddr_in(addr); + int count = addr_cache_counters[block]; + /* use 'first' to index item in cache block when count is equal or greater than block size */ + int first = addr_cache_pointers[block]; + + if (count < ADDR_CACHE_BLOCK_SIZE) + { + memcpy((void *)&addr_cache[block][count].addr, (void *) addr, sizeof(struct sockaddr_in)); + addr_cache[block][count].access_time = redsocks_time(NULL); + addr_cache_counters[block]++; + } + else + { + memcpy((void *)&addr_cache[block][first].addr, (void *) addr, sizeof(struct sockaddr_in)); + addr_cache[block][first].access_time = redsocks_time(NULL); + addr_cache_pointers[block]++; + addr_cache_pointers[block]%=ADDR_CACHE_BLOCK_SIZE; + } } static void del_addr_from_cache(const struct sockaddr_in * addr) -{ /* get block index */ - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - int first = addr_cache_pointers[block]; - int i = 0; - /* do reverse search for efficency */ - for ( i = count - 1; i >= 0; i -- ) - if (addr->sin_addr.s_addr == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr.sin_addr.s_addr - && addr->sin_family == addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr.sin_family - ) - /* found. zero this item */ - { - memset((void *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE], 0, sizeof(cache_data)); - break; - } +{ /* get block index */ + int block = block_from_sockaddr_in(addr); + int count = addr_cache_counters[block]; + int first = addr_cache_pointers[block]; + int i = 0; + /* do reverse search for efficency */ + for ( i = count - 1; i >= 0; i -- ) + { + if (0 == evutil_sockaddr_cmp((const struct sockaddr *)addr, + (const struct sockaddr *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr, + ADDR_PORT_CHECK)) + /* found. zero this item */ + { + memset((void *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE], 0, sizeof(cache_data)); + break; + } + } } - - void auto_client_init(redsocks_client *client) { - autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; + autoproxy_client * aclient = get_autoproxy_client(client); - aclient->state = AUTOPROXY_NEW; - aclient->data_recv = 0; - aclient->data_sent = 0; - init_addr_cache(); + memset((void *) aclient, 0, sizeof(autoproxy_client)); + aclient->state = AUTOPROXY_NEW; + init_addr_cache(); +} + +void auto_client_fini(redsocks_client *client) +{ + autoproxy_client * aclient = get_autoproxy_client(client); + + if (aclient->recv_timer_event) + { + event_del(aclient->recv_timer_event); + event_free(aclient->recv_timer_event); + aclient->recv_timer_event = NULL; + } } static void on_connection_confirmed(redsocks_client *client) { - del_addr_from_cache(&client->destaddr); - redsocks_log_error(client, LOG_DEBUG, "IP Confirmed"); + redsocks_log_error(client, LOG_DEBUG, "IP Confirmed"); + + del_addr_from_cache(&client->destaddr); } static void on_connection_blocked(redsocks_client *client) { - redsocks_log_error(client, LOG_DEBUG, "IP Blocked"); + redsocks_log_error(client, LOG_DEBUG, "IP Blocked"); } +static void auto_confirm_connection(redsocks_client * client) +{ + autoproxy_client * aclient = get_autoproxy_client(client); + + assert(aclient->state == AUTOPROXY_CONNECTED); + aclient->state = AUTOPROXY_CONFIRMED; + if (aclient->data_sent) + { + evbuffer_drain(bufferevent_get_input(client->client), aclient->data_sent); + aclient->data_sent = 0; + } + // Cancel timer and release event object for timer + if (aclient->recv_timer_event) + { + event_del(aclient->recv_timer_event); + event_free(aclient->recv_timer_event); + aclient->recv_timer_event = NULL; + } + on_connection_confirmed(client); +} + +static void auto_recv_timeout_cb(evutil_socket_t fd, short events, void * arg) +{ + redsocks_client *client = arg; + autoproxy_client * aclient = get_autoproxy_client(client); + + redsocks_log_error(client, LOG_DEBUG, "RECV Timeout, state: %d, data_sent: %d", aclient->state, aclient->data_sent); + + assert(events & EV_TIMEOUT); + // Let's make connection confirmed + if (aclient->state == AUTOPROXY_CONNECTED) + auto_confirm_connection(client); + else + return; + + // TODO: need or not? + if (!(client->relay_evshut & EV_READ) && !(client->client_evshut & EV_WRITE)) + { + if (bufferevent_write_buffer(client->client, bufferevent_get_input(client->relay)) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(client->client, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } + +} + + static void direct_relay_readcb_helper(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) { - if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { - if (bufferevent_write_buffer(to, from->input) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - } - else { - if (bufferevent_disable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); - } + if (evbuffer_get_length(bufferevent_get_output(to)) < to->wm_write.high) + { + if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } + else { + if (bufferevent_disable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); + } } static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) { - redsocks_client *client = _client; - autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; - - redsocks_touch_client(client); - - if (aclient->state == AUTOPROXY_CONNECTED) - { - if (aclient->data_sent && aclient->data_recv) - { - /* No CONNECTION RESET error occur after sending data, good. */ - aclient->state = AUTOPROXY_CONFIRMED; - on_connection_confirmed(client); - if (evbuffer_get_length(from->input)) - { - evbuffer_drain(from->input, aclient->data_sent); - aclient->data_sent = 0; - } - } - } - direct_relay_readcb_helper(client, client->client, client->relay); + redsocks_client *client = _client; + autoproxy_client * aclient = get_autoproxy_client(client); + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + + redsocks_log_error(client, LOG_DEBUG, "client readcb: client in: %d", input_size); + redsocks_touch_client(client); + + if (aclient->state == AUTOPROXY_CONNECTED) + { + if (aclient->data_sent && aclient->data_recv) + { + /* No CONNECTION RESET error occur after sending data, good. */ + auto_confirm_connection(client); + } + } + direct_relay_readcb_helper(client, client->client, client->relay); } static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) { - redsocks_client *client = _client; - autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; - - redsocks_touch_client(client); - if (!aclient->data_recv) - aclient->data_recv = EVBUFFER_LENGTH(from->input); - direct_relay_readcb_helper(client, client->relay, client->client); + redsocks_client *client = _client; + autoproxy_client * aclient = get_autoproxy_client(client); + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + + redsocks_touch_client(client); + if (!aclient->data_recv) + { + aclient->data_recv = input_size; + if (input_size && aclient->state == AUTOPROXY_CONNECTED) + { + auto_confirm_connection(client); + } + } + direct_relay_readcb_helper(client, client->relay, client->client); } static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) { - redsocks_client *client = _client; - autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; - struct bufferevent * from = client->relay; - - redsocks_touch_client(client); - - if (EVBUFFER_LENGTH(from->input) == 0 && (client->relay_evshut & EV_READ)) { - redsocks_shutdown(client, to, SHUT_WR); - return; - } - if (aclient->state == AUTOPROXY_CONNECTED) - { - if (!aclient->data_recv) - aclient->data_recv = EVBUFFER_LENGTH(from->input); - } - if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { - if (bufferevent_write_buffer(to, from->input) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - } + redsocks_client *client = _client; + autoproxy_client * aclient = get_autoproxy_client(client); + struct bufferevent * from = client->relay; + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); + + redsocks_touch_client(client); + + if (input_size == 0 && (client->relay_evshut & EV_READ)) + { + redsocks_shutdown(client, to, SHUT_WR); + return; + } + if (aclient->state == AUTOPROXY_CONNECTED) + { + if (!aclient->data_recv) + { + aclient->data_recv = input_size; + if (input_size) + auto_confirm_connection(client); + } + } + if (output_size < to->wm_write.high) + { + if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } } - - static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) { - redsocks_client *client = _client; - autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; - struct bufferevent * from = client->client; - - redsocks_touch_client(client); - - if (EVBUFFER_LENGTH(from->input) == 0 && (client->client_evshut & EV_READ)) { - redsocks_shutdown(client, to, SHUT_WR); - return; - } - else if (aclient->state == AUTOPROXY_CONNECTED ) - { - /* Not send or receive data. */ - if (!aclient->data_sent && !aclient->data_recv) - { - /* Ensure we have data to send */ - if (EVBUFFER_LENGTH(from->input)) - { - /* copy data from input to output of relay */ - aclient->data_sent = copy_evbuffer (to, from, 0); - redsocks_log_error(client, LOG_DEBUG, "not sent, not got %d", EVBUFFER_LENGTH(from->input)); - } - } - /* - * Relay reaceived data before writing to relay. - */ - else if (!aclient->data_sent && aclient->data_recv) - { - redsocks_log_error(client, LOG_DEBUG, "not sent, got"); - aclient->data_sent = copy_evbuffer(to, from, 0); - } - /* aclient->state = AUTOPROXY_CONFIRMED; */ - /* We have writen data to relay, but got nothing until we are requested to - * write to it again. - */ - else if (aclient->data_sent && !aclient->data_recv) - { - /* No response from relay and no CONNECTION RESET, - Send more data. - */ - redsocks_log_error(client, LOG_DEBUG, "sent, not got in:%d out:%d high:%d sent:%d", - evbuffer_get_length(from->input), - evbuffer_get_length(to->output), - to->wm_write.high, aclient->data_sent ); - /* Write more data util input buffer is full */ - if (EVBUFFER_LENGTH(from->input)- aclient->data_sent > 0) /* we have more data waiting to be sent */ - { - aclient->data_sent += copy_evbuffer(to, from, aclient->data_sent); - } - else if (EVBUFFER_LENGTH(to->output) < aclient->data_sent /* data is sent out, more or less */ - && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* do not confirm unless read buffer is full */ - && EVBUFFER_LENGTH(from->input) == aclient->data_sent /* all data in read buffer is sent */ - ) - { - evbuffer_drain(from->input, aclient->data_sent); - aclient->data_sent = 0; - aclient->state = AUTOPROXY_CONFIRMED; - on_connection_confirmed(client); - } - } - /* We sent data to and got data from relay. */ - else if (aclient->data_sent && aclient->data_recv) - { - /* No CONNECTION RESET error occur after sending data, good. */ - aclient->state = AUTOPROXY_CONFIRMED; - on_connection_confirmed(client); - redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", aclient->data_recv); - if (evbuffer_get_length(from->input)) - { - evbuffer_drain(from->input, aclient->data_sent); - aclient->data_sent = 0; - } - } - } - - if (aclient->state == AUTOPROXY_CONFIRMED) - { - if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { - if (bufferevent_write_buffer(to, from->input) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - } - } + redsocks_client *client = _client; + autoproxy_client * aclient = get_autoproxy_client(client); + struct bufferevent * from = client->client; + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); + + redsocks_touch_client(client); + + if (input_size == 0 && (client->client_evshut & EV_READ)) { + redsocks_shutdown(client, to, SHUT_WR); + return; + } + else if (aclient->state == AUTOPROXY_CONNECTED ) + { + redsocks_log_error(client, LOG_DEBUG, "sent: %d, recv: %d, in:%d, out:%d", + aclient->data_sent, + aclient->data_recv, + input_size, + output_size + ); + /* Not send or receive data. */ + if (!aclient->data_sent && !aclient->data_recv) + { + /* Ensure we have data to send */ + if (input_size) + { + /* copy data from input to output of relay */ + aclient->data_sent = copy_evbuffer (to, from, 0); + } + } + /* + * Relay reaceived data before writing to relay. + */ + else if (!aclient->data_sent && aclient->data_recv) + { + // TODO: + // In case we receive data before sending any data, + // should we confirm connection immediately or after + // sending data? + //aclient->data_sent = copy_evbuffer(to, from, 0); + auto_confirm_connection(client); + + } + /* aclient->state = AUTOPROXY_CONFIRMED; */ + /* We have writen data to relay, but got nothing until we are requested to + * write to it again. + */ + else if (aclient->data_sent && !aclient->data_recv) + { + /* No response from relay and no CONNECTION RESET, + Send more data. + */ + /* Write more data util input buffer of relay is full */ + if (input_size - aclient->data_sent > 0) /* we have more data waiting to be sent */ + { + aclient->data_sent += copy_evbuffer(to, from, aclient->data_sent); + } + + else if (output_size < aclient->data_sent /* data is sent out, more or less */ + && input_size == aclient->data_sent /* all data in read buffer is sent */ + ) + { + aclient->recv_timer_event = evtimer_new(bufferevent_get_base(to), auto_recv_timeout_cb , _client); + if (aclient->recv_timer_event) + { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 600000; + if (-1 == evtimer_add(aclient->recv_timer_event, &tv)) + { + redsocks_log_error(client, LOG_DEBUG, "Failed to add timer!"); + // In case we failed to add timer, it is abnormal. + // Let's confirm the connection directly so that normal service is not + // impacted. + auto_confirm_connection(client); + } + } + } + } + /* We sent data to and got data from relay. */ + else if (aclient->data_sent && aclient->data_recv) + { + /* No CONNECTION RESET error occur after sending data, good. */ + auto_confirm_connection(client); + } + } + + if (aclient->state == AUTOPROXY_CONFIRMED) + { + if (output_size < to->wm_write.high) + { + if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } + } } static void auto_drop_relay(redsocks_client *client) { - if (client->relay) { - redsocks_log_error(client, LOG_DEBUG, "dropping relay only"); - - redsocks_close(EVENT_FD(&client->relay->ev_write)); - bufferevent_free(client->relay); - client->relay = NULL; - } + if (client->relay) + { + redsocks_log_error(client, LOG_DEBUG, "dropping relay only"); + + redsocks_close(bufferevent_getfd(client->relay)); + bufferevent_free(client->relay); + client->relay = NULL; + } } static void auto_retry(redsocks_client * client, int updcache) { - autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; - - if (aclient->state == AUTOPROXY_CONNECTED) - bufferevent_disable(client->client, EV_READ| EV_WRITE); - /* drop relay and update state, then retry with specified relay */ - if (updcache) - { - /* only add IP to cache when the IP is not in cache */ - if (get_addr_time_in_cache(&client->destaddr) == NULL) - { - add_addr_to_cache(&client->destaddr); - redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %s", - inet_ntoa(client->destaddr.sin_addr)); - } - } - auto_drop_relay(client); - - // restore callbacks for ordinary client. - bufferevent_setcb(client->client, NULL, NULL, client->client->errorcb, client); - // enable reading to handle EOF from client - bufferevent_enable(client->client, EV_READ); - /* connect to relay */ - if (client->instance->relay_ss->connect_relay) - client->instance->relay_ss->connect_relay(client); - else - redsocks_connect_relay(client); - // - if (EVBUFFER_LENGTH(client->client->input) && client->client->readcb) - client->client->readcb(client->client, client); + autoproxy_client * aclient = get_autoproxy_client(client); + + if (aclient->state == AUTOPROXY_CONNECTED) + bufferevent_disable(client->client, EV_READ| EV_WRITE); + /* drop relay and update state, then retry with specified relay */ + if (updcache) + { + /* only add IP to cache when the IP is not in cache */ + if (get_addr_time_in_cache(&client->destaddr) == NULL) + { + add_addr_to_cache(&client->destaddr); + redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %s", + inet_ntoa(client->destaddr.sin_addr)); + } + } + // Release timer + if (aclient->recv_timer_event) + { + event_del(aclient->recv_timer_event); + event_free(aclient->recv_timer_event); + aclient->recv_timer_event = NULL; + } + + auto_drop_relay(client); + + // restore callbacks for ordinary client. + bufferevent_setcb(client->client, NULL, NULL, redsocks_event_error, client); + // enable reading to handle EOF from client + bufferevent_enable(client->client, EV_READ); + + /* connect to relay */ + if (client->instance->relay_ss->connect_relay) + client->instance->relay_ss->connect_relay(client); + else + redsocks_connect_relay(client); } /* return 1 for drop, 0 for retry. */ static int auto_retry_or_drop(redsocks_client * client) { - time_t now = redsocks_time(NULL); - autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; - - if (aclient->state == AUTOPROXY_NEW) - { - if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) - { - on_connection_blocked(client); - auto_retry(client, 0); - return 0; - } - } - else if ( aclient->state == AUTOPROXY_CONNECTED) - { -// if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) - { - on_connection_blocked(client); - auto_retry(client, 0); - return 0; - } - } - - /* drop */ - return 1; + autoproxy_client * aclient = get_autoproxy_client(client); + time_t now = redsocks_time(NULL); + + if (aclient->state == AUTOPROXY_NEW) + { + if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) + { + on_connection_blocked(client); + auto_retry(client, 0); + return 0; + } + } + else if ( aclient->state == AUTOPROXY_CONNECTED) + { +// if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) + { + on_connection_blocked(client); + auto_retry(client, 0); + return 0; + } + } + + /* drop */ + return 1; } static void auto_relay_connected(struct bufferevent *buffev, void *_arg) { - redsocks_client *client = _arg; - autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; - - assert(buffev == client->relay); - - redsocks_touch_client(client); - - if (!red_is_socket_connected_ok(buffev)) { - if (aclient->state == AUTOPROXY_NEW && !auto_retry_or_drop(client)) - return; - - redsocks_log_error(client, LOG_DEBUG, "failed to connect to proxy"); - goto fail; - } - - /* update client state */ - aclient->state = AUTOPROXY_CONNECTED; - - /* We do not need to detect timeouts any more. - The two peers will handle it. */ - bufferevent_set_timeouts(client->relay, NULL, NULL); - - if (!redsocks_start_relay(client)) - { - /* overwrite theread callback to my function */ - client->client->readcb = direct_relay_clientreadcb; - client->client->writecb = direct_relay_clientwritecb; - client->relay->readcb = direct_relay_relayreadcb; - client->relay->writecb = direct_relay_relaywritecb; - } - else - { - redsocks_log_error(client, LOG_DEBUG, "failed to start relay"); - goto fail; - } - client->relay->writecb(buffev, _arg); - return; - -fail: - redsocks_drop_client(client); + redsocks_client *client = _arg; + autoproxy_client * aclient = get_autoproxy_client(client); + + assert(buffev == client->relay); + + redsocks_touch_client(client); + + + if (!red_is_socket_connected_ok(buffev)) { + if (aclient->state == AUTOPROXY_NEW && !auto_retry_or_drop(client)) + return; + + redsocks_log_error(client, LOG_DEBUG, "failed to connect to destination"); + redsocks_drop_client(client); + return; + } + + /* update client state */ + aclient->state = AUTOPROXY_CONNECTED; + + /* We do not need to detect timeouts any more. + The two peers will handle it. */ + bufferevent_set_timeouts(client->relay, NULL, NULL); + + if (!redsocks_start_relay(client)) + { + /* overwrite theread callback to my function */ + bufferevent_setcb(client->client, direct_relay_clientreadcb, + direct_relay_clientwritecb, + auto_event_error, + client); + bufferevent_setcb(client->relay, direct_relay_relayreadcb, + direct_relay_relaywritecb, + auto_event_error, + client); + } + else + { + redsocks_log_error(client, LOG_DEBUG, "failed to start relay"); + redsocks_drop_client(client); + return; + } + // Write any data received from client side to relay. + if (evbuffer_get_length(bufferevent_get_input(client->client))) + direct_relay_relaywritecb(client->relay, client); + return; } - + static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) { - redsocks_client *client = _arg; - autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; - int saved_errno = errno; - assert(buffev == client->relay || buffev == client->client); - - redsocks_touch_client(client); - - redsocks_log_errno(client, LOG_DEBUG, "%s errno(%d), State: %d, what: " event_fmt_str, - buffev == client->client?"client":"relay", - saved_errno, aclient->state, event_fmt(what)); - if (buffev == client->relay) - { - - if ( aclient->state == AUTOPROXY_NEW - && what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT)) - { - on_connection_blocked(client); - /* In case timeout occurs while connecting relay, we try to connect - to target via SOCKS5 proxy. It is possible that the connection to - target can be set up a bit longer than the timeout value we set. - However, it is still better to make connection via proxy. */ - auto_retry(client, 1); - return; - } - - if (aclient->state == AUTOPROXY_NEW && saved_errno == ECONNRESET) - if (!auto_retry_or_drop(client)) - return; - - if (aclient->state == AUTOPROXY_CONNECTED && what == (EVBUFFER_READ|EVBUFFER_ERROR) - && saved_errno == ECONNRESET ) - { - if (!auto_retry_or_drop(client)) - return; - } - } - - if (what == (EVBUFFER_READ|EVBUFFER_EOF)) { - struct bufferevent *antiev; - if (buffev == client->relay) - antiev = client->client; - else - antiev = client->relay; - - redsocks_shutdown(client, buffev, SHUT_RD); - - if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0) - redsocks_shutdown(client, antiev, SHUT_WR); - } - else { - redsocks_drop_client(client); - } -} + redsocks_client *client = _arg; + autoproxy_client * aclient = get_autoproxy_client(client); + int saved_errno = errno; + assert(buffev == client->relay || buffev == client->client); + + redsocks_touch_client(client); + + redsocks_log_errno(client, LOG_DEBUG, "%s, errno(%d), State: %d, what: " event_fmt_str, + buffev == client->client?"client":"relay", + saved_errno, aclient->state, event_fmt(what)); + if (buffev == client->relay) + { +/* + if (what & BEV_EVENT_CONNECTED) + { + auto_relay_connected(buffev, _arg); + return; + } +*/ + if ( aclient->state == AUTOPROXY_NEW + && what == (BEV_EVENT_WRITING | BEV_EVENT_TIMEOUT)) + { + // Update access time for IP fails again. + if (aclient->quick_check) + set_addr_time_in_cache(&client->destaddr, redsocks_time(NULL)); + + on_connection_blocked(client); + /* In case timeout occurs while connecting relay, we try to connect + to target via configured proxy. It is possible that the connection to + target can be set up a bit longer than the timeout value we set. + However, it is still better to make connection via proxy. */ + auto_retry(client, 1); + return; + } + + if (aclient->state == AUTOPROXY_NEW && saved_errno == ECONNRESET) + if (!auto_retry_or_drop(client)) + return; + + if (aclient->state == AUTOPROXY_CONNECTED + && what == (BEV_EVENT_READING|BEV_EVENT_ERROR) + && saved_errno == ECONNRESET ) + { + if (!auto_retry_or_drop(client)) + return; + } + } + + if (what == (BEV_EVENT_READING|BEV_EVENT_EOF)) + { + struct bufferevent *antiev; + if (buffev == client->relay) + antiev = client->client; + else + antiev = client->relay; + + // Release timer + if (aclient->recv_timer_event) + { + event_del(aclient->recv_timer_event); + event_free(aclient->recv_timer_event); + aclient->recv_timer_event = NULL; + } + + redsocks_shutdown(client, buffev, SHUT_RD); + + if (antiev != NULL && evbuffer_get_length(bufferevent_get_output(antiev)) == 0) + redsocks_shutdown(client, antiev, SHUT_WR); + } + else + { + redsocks_drop_client(client); + } +} static void auto_connect_relay(redsocks_client *client) { - autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; - struct timeval tv; - tv.tv_sec = client->instance->config.timeout; - tv.tv_usec = 0; - time_t * acc_time = NULL; - time_t now = redsocks_time(NULL); - - /* use default timeout if timeout is not configured */ - if (tv.tv_sec == 0) - tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS; - - if (aclient->state == AUTOPROXY_NEW) - { - acc_time = get_addr_time_in_cache(&client->destaddr); - if (acc_time) - { - redsocks_log_error(client, LOG_DEBUG, "Found dest IP in cache"); - /* update timeout value for quick detection. - * Sometimes, good sites are added into cache due to occasionally - * connection timeout. It is annoying. So, decision is made to - * always try to connect to destination first when the destination - * is found in cache. - * For most destinations, the connection could be set up correctly - * in short time. And, for most blocked sites, we get connection - * reset almost immediately when connection is set up or when HTTP - * request is sent. - */ - tv.tv_sec = QUICK_CONNECT_TIMEOUT_SECONDS; - if (now - *acc_time >= CACHE_ITEM_STALE_SECONDS ) - { - /* stale this address in cache */ - del_addr_from_cache(&client->destaddr); - } - } - /* connect to target directly without going through proxy */ - client->relay = red_connect_relay2(&client->destaddr, - auto_relay_connected, auto_event_error, client, - &tv); - - aclient->time_connect_relay = redsocks_time(NULL); - - if (!client->relay) { - redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); - redsocks_drop_client(client); - } - } - else - { - redsocks_log_errno(client, LOG_ERR, "invalid state: %d", aclient->state); - } + autoproxy_client * aclient = get_autoproxy_client(client); + struct timeval tv; + tv.tv_sec = client->instance->config.timeout; + tv.tv_usec = 0; + time_t * acc_time = NULL; + time_t now = redsocks_time(NULL); + + /* use default timeout if timeout is not configured */ + if (tv.tv_sec == 0) + tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS; + + if (aclient->state == AUTOPROXY_NEW) + { + acc_time = get_addr_time_in_cache(&client->destaddr); + if (acc_time) + { + redsocks_log_error(client, LOG_DEBUG, "Found dest IP in cache"); + // No quick check when the time passed since IP is added to cache is + // less than NO_CHECK_SECONDS. Just let it go via proxy. + if (now - *acc_time < NO_CHECK_SECONDS) + { + auto_retry(client, 0); + return; + } + + /* update timeout value for quick detection. + * Sometimes, good sites are added into cache due to occasionally + * connection timeout. It is annoying. So, decision is made to + * always try to connect to destination first when the destination + * is found in cache. + * For most destinations, the connection could be set up correctly + * in short time. And, for most blocked sites, we get connection + * reset almost immediately when connection is set up or when HTTP + * request is sent. + */ + tv.tv_sec = QUICK_CONNECT_TIMEOUT_SECONDS; + aclient->quick_check = 1; + if (now - *acc_time >= CACHE_ITEM_STALE_SECONDS ) + { + /* stale this address in cache */ + del_addr_from_cache(&client->destaddr); + } + } + /* connect to target directly without going through proxy */ + client->relay = red_connect_relay2(&client->destaddr, + NULL, auto_relay_connected, auto_event_error, client, + &tv); + + aclient->time_connect_relay = redsocks_time(NULL); + + if (!client->relay) { + redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); + redsocks_drop_client(client); + } + } + else + { + redsocks_log_errno(client, LOG_ERR, "invalid state: %d", aclient->state); + } } - - - - - + relay_subsys autoproxy_subsys = { - .name = "autoproxy", - .payload_len = sizeof(autoproxy_client), - .instance_payload_len = 0, -/* - .readcb = auto_read_cb, - .writecb = auto_write_cb, -*/ - .init = auto_client_init, - .connect_relay = auto_connect_relay, + .name = "autoproxy", + .payload_len = sizeof(autoproxy_client), + .instance_payload_len = 0, + .readcb = direct_relay_relayreadcb, + .writecb = direct_relay_relaywritecb, + .init = auto_client_init, + .fini = auto_client_fini, + .connect_relay = auto_connect_relay, }; diff --git a/base.c b/base.c index 1d2e0933..ad682e92 100644 --- a/base.c +++ b/base.c @@ -332,6 +332,8 @@ static void myproc() size_t len = 0; ssize_t dsize ; struct sigaction sa ; + char tmp_fname[128]; + snprintf(tmp_fname, sizeof(tmp_fname), "/tmp/redtime-%d", getpid()); /* Set SIGCHLD handler to ignore death of child. */ sa.sa_handler = SIG_IGN; @@ -369,7 +371,7 @@ static void myproc() } sleep(1); - tmp = fopen("/tmp/redtime", "r"); + tmp = fopen(tmp_fname, "r"); if (tmp) { len = 0; diff --git a/direct.c b/direct.c index e21b4ee1..6fa38b8d 100644 --- a/direct.c +++ b/direct.c @@ -1,8 +1,21 @@ -/* - * Copyright (C) 2013 Zhuofei Wang +/* redsocks2 - transparent TCP-to-proxy redirector + * Copyright (C) 2013-2014 Zhuofei Wang * + * This code is based on redsocks project developed by Leonid Evdokimov. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. */ + #include #include #include @@ -23,43 +36,50 @@ void redsocks_relay_connected(struct bufferevent *buffev, void *_arg); static void direct_relay_init(redsocks_client *client) { - client->state = 0; + client->state = 0; } -static void direct_instance_fini(redsocks_instance *instance) +static void direct_relay_fini(redsocks_client *client) { } -static void direct_read_cb(struct bufferevent *buffev, void *_arg) +static void direct_write_cb(struct bufferevent *buffev, void *_arg) { - redsocks_client *client = _arg; + redsocks_client *client = _arg; redsocks_touch_client(client); if (client->state == 0) { client->state = 1; - redsocks_start_relay(client); + if (redsocks_start_relay(client)) + { + // Failed to start relay. Drop connection. + redsocks_drop_client(client); + return; + } + // Write any data received from client to relay + if (evbuffer_get_length(bufferevent_get_input(client->client))) + client->instance->relay_ss->writecb(buffev, client); } } -static void direct_write_cb(struct bufferevent *buffev, void *_arg) +void direct_connect_relay(redsocks_client *client) { - redsocks_client *client = _arg; - redsocks_touch_client(client); - if (client->state == 0) + char * interface = client->instance->config.interface; + // Allowing binding relay socket to specified IP for outgoing connections + if (interface && strlen(interface)) { - client->state = 1; - redsocks_start_relay(client); + client->relay = red_connect_relay_if(interface, + &client->destaddr, NULL, + redsocks_relay_connected, redsocks_event_error, client); + } + else + client->relay = red_connect_relay(&client->destaddr, NULL, + redsocks_relay_connected, redsocks_event_error, client); + if (!client->relay) + { + redsocks_log_errno(client, LOG_ERR, "red_connect_relay"); + redsocks_drop_client(client); } -} - -void redsocks_direct_connect_relay(redsocks_client *client) -{ - client->relay = red_connect_relay(&client->destaddr, - redsocks_relay_connected, redsocks_event_error, client); - if (!client->relay) { - redsocks_log_errno(client, LOG_ERR, "red_connect_relay"); - redsocks_drop_client(client); - } } relay_subsys direct_connect_subsys = @@ -67,11 +87,10 @@ relay_subsys direct_connect_subsys = .name = "direct", .payload_len = 0, .instance_payload_len = 0, - .readcb = direct_read_cb, - .writecb = direct_write_cb, - .init = direct_relay_init, - .instance_fini = direct_instance_fini, - .connect_relay = redsocks_direct_connect_relay, + .writecb = direct_write_cb, + .init = direct_relay_init, + .fini = direct_relay_fini, + .connect_relay = direct_connect_relay, }; diff --git a/main.c b/main.c index e6f6ec0d..168a2ee6 100644 --- a/main.c +++ b/main.c @@ -41,10 +41,11 @@ app_subsys *subsystems[] = { static const char *confname = "redsocks.conf"; static const char *pidfile = NULL; +static struct event_base * g_event_base = NULL; static void terminate(int sig, short what, void *_arg) { - if (event_loopbreak() != 0) + if (g_event_base && event_base_loopbreak(g_event_base) != 0) log_error(LOG_WARNING, "event_loopbreak"); } @@ -56,6 +57,11 @@ static void red_srand() srand(tv.tv_sec*1000000+tv.tv_usec); } +struct event_base * get_event_base() +{ + return g_event_base; +} + int main(int argc, char **argv) { int error; @@ -119,7 +125,11 @@ int main(int argc, char **argv) if (conftest) return EXIT_SUCCESS; - event_init(); + // Initialize global event base + g_event_base = event_base_new(); + if (!g_event_base) + return EXIT_FAILURE; + memset(terminators, 0, sizeof(terminators)); FOREACH(ss, subsystems) { @@ -142,7 +152,7 @@ int main(int argc, char **argv) assert(SIZEOF_ARRAY(exit_signals) == SIZEOF_ARRAY(terminators)); for (i = 0; i < SIZEOF_ARRAY(exit_signals); i++) { - signal_set(&terminators[i], exit_signals[i], terminate, NULL); + evsignal_assign(&terminators[i], get_event_base(), exit_signals[i], terminate, NULL); if (signal_add(&terminators[i], NULL) != 0) { log_errno(LOG_ERR, "signal_add"); goto shutdown; @@ -151,7 +161,7 @@ int main(int argc, char **argv) log_error(LOG_NOTICE, "redsocks started"); - event_dispatch(); + event_base_dispatch(g_event_base); log_error(LOG_NOTICE, "redsocks goes down"); @@ -168,8 +178,9 @@ int main(int argc, char **argv) if ((*ss)->fini) (*ss)->fini(); - event_base_free(NULL); - + if (g_event_base) + event_base_free(g_event_base); + return !error ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/main.h b/main.h index ca49c702..3930e438 100644 --- a/main.h +++ b/main.h @@ -13,6 +13,7 @@ typedef struct app_subsys_t { #define FOREACH(ptr, array) for (ptr = array; ptr < array + SIZEOF_ARRAY(array); ptr++) #define FOREACH_REV(ptr, array) for (ptr = array + SIZEOF_ARRAY(array) - 1; ptr >= array; ptr--) +struct event_base * get_event_base(); /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/redsocks.c b/redsocks.c index 8c830535..a3b16fa8 100644 --- a/redsocks.c +++ b/redsocks.c @@ -1,4 +1,10 @@ -/* redsocks - transparent TCP-to-proxy redirector +/* redsocks2 - transparent TCP-to-proxy redirector + * Copyright (C) 2013-2014 Zhuofei Wang + * + * This code is based on redsocks project developed by Leonid Evdokimov. + * Licensed under the Apache License, Version 2.0 (the "License"). + * + * * Copyright (C) 2007-2011 Leonid Evdokimov * * Licensed under the Apache License, Version 2.0 (the "License"); you may not @@ -35,12 +41,13 @@ #include "utils.h" -#define REDSOCKS_RELAY_HALFBUFF 1024*4 +#define REDSOCKS_RELAY_HALFBUFF 1024*32 #define REDSOCKS_AUDIT_INTERVAL 60 void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); static void redsocks_relay_relayreadcb(struct bufferevent *from, void *_client); static void redsocks_relay_relaywritecb(struct bufferevent *from, void *_client); +void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg); extern relay_subsys direct_connect_subsys; extern relay_subsys http_connect_subsys; @@ -50,10 +57,10 @@ extern relay_subsys socks5_subsys; static relay_subsys *relay_subsystems[] = { &direct_connect_subsys, - &http_connect_subsys, - &http_relay_subsys, - &socks4_subsys, - &socks5_subsys, + &http_connect_subsys, + &http_relay_subsys, + &socks4_subsys, + &socks5_subsys, }; extern relay_subsys autoproxy_subsys; @@ -61,745 +68,774 @@ static list_head instances = LIST_HEAD_INIT(instances); static parser_entry redsocks_entries[] = { - { .key = "local_ip", .type = pt_in_addr }, - { .key = "local_port", .type = pt_uint16 }, - { .key = "ip", .type = pt_in_addr }, - { .key = "port", .type = pt_uint16 }, - { .key = "type", .type = pt_pchar }, - { .key = "login", .type = pt_pchar }, - { .key = "password", .type = pt_pchar }, - { .key = "listenq", .type = pt_uint16 }, - { .key = "min_accept_backoff", .type = pt_uint16 }, - { .key = "max_accept_backoff", .type = pt_uint16 }, - { .key = "autoproxy", .type = pt_uint16 }, - { .key = "timeout", .type = pt_uint16 }, - { } + { .key = "local_ip", .type = pt_in_addr }, + { .key = "local_port", .type = pt_uint16 }, + { .key = "interface", .type = pt_pchar }, + { .key = "ip", .type = pt_in_addr }, + { .key = "port", .type = pt_uint16 }, + { .key = "type", .type = pt_pchar }, + { .key = "login", .type = pt_pchar }, + { .key = "password", .type = pt_pchar }, + { .key = "listenq", .type = pt_uint16 }, + { .key = "min_accept_backoff", .type = pt_uint16 }, + { .key = "max_accept_backoff", .type = pt_uint16 }, + { .key = "autoproxy", .type = pt_uint16 }, + { .key = "timeout", .type = pt_uint16 }, + { } }; /* There is no way to get `EVLIST_INSERTED` event flag outside of libevent, so * here are tracking functions. */ static void tracked_event_set( - struct tracked_event *tev, evutil_socket_t fd, short events, - void (*callback)(evutil_socket_t, short, void *), void *arg) + struct tracked_event *tev, evutil_socket_t fd, short events, + void (*callback)(evutil_socket_t, short, void *), void *arg) { - event_set(&tev->ev, fd, events, callback, arg); - timerclear(&tev->inserted); + event_assign(&tev->ev, get_event_base(), fd, events, callback, arg); + timerclear(&tev->inserted); } static int tracked_event_add(struct tracked_event *tev, const struct timeval *tv) { - int ret = event_add(&tev->ev, tv); - if (ret == 0) - gettimeofday(&tev->inserted, NULL); - return ret; + int ret = event_add(&tev->ev, tv); + if (ret == 0) + gettimeofday(&tev->inserted, NULL); + return ret; } static int tracked_event_del(struct tracked_event *tev) { - int ret = event_del(&tev->ev); - if (ret == 0) - timerclear(&tev->inserted); - return ret; + int ret = event_del(&tev->ev); + if (ret == 0) + timerclear(&tev->inserted); + return ret; } static int redsocks_onenter(parser_section *section) { - // FIXME: find proper way to calulate instance_payload_len - int instance_payload_len = 0; - relay_subsys **ss; - FOREACH(ss, relay_subsystems) - if (instance_payload_len < (*ss)->instance_payload_len) - instance_payload_len = (*ss)->instance_payload_len; - - redsocks_instance *instance = calloc(1, sizeof(*instance) + instance_payload_len); - if (!instance) { - parser_error(section->context, "Not enough memory"); - return -1; - } - - INIT_LIST_HEAD(&instance->list); - INIT_LIST_HEAD(&instance->clients); - instance->config.bindaddr.sin_family = AF_INET; - instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - instance->config.relayaddr.sin_family = AF_INET; - instance->config.relayaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - /* Default value can be checked in run-time, but I doubt anyone needs that. - * Linux: sysctl net.core.somaxconn - * FreeBSD: sysctl kern.ipc.somaxconn */ - instance->config.listenq = SOMAXCONN; - instance->config.min_backoff_ms = 100; - instance->config.max_backoff_ms = 60000; - instance->config.autoproxy = 0; - instance->config.timeout = 0; - - for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) - entry->addr = - (strcmp(entry->key, "local_ip") == 0) ? (void*)&instance->config.bindaddr.sin_addr : - (strcmp(entry->key, "local_port") == 0) ? (void*)&instance->config.bindaddr.sin_port : - (strcmp(entry->key, "ip") == 0) ? (void*)&instance->config.relayaddr.sin_addr : - (strcmp(entry->key, "port") == 0) ? (void*)&instance->config.relayaddr.sin_port : - (strcmp(entry->key, "type") == 0) ? (void*)&instance->config.type : - (strcmp(entry->key, "login") == 0) ? (void*)&instance->config.login : - (strcmp(entry->key, "password") == 0) ? (void*)&instance->config.password : - (strcmp(entry->key, "listenq") == 0) ? (void*)&instance->config.listenq : - (strcmp(entry->key, "min_accept_backoff") == 0) ? (void*)&instance->config.min_backoff_ms : - (strcmp(entry->key, "max_accept_backoff") == 0) ? (void*)&instance->config.max_backoff_ms : - (strcmp(entry->key, "autoproxy") == 0) ? (void*)&instance->config.autoproxy : - (strcmp(entry->key, "timeout") == 0) ? (void*)&instance->config.timeout: - NULL; - section->data = instance; - return 0; + // FIXME: find proper way to calulate instance_payload_len + int instance_payload_len = 0; + relay_subsys **ss; + FOREACH(ss, relay_subsystems) + if (instance_payload_len < (*ss)->instance_payload_len) + instance_payload_len = (*ss)->instance_payload_len; + + redsocks_instance *instance = calloc(1, sizeof(*instance) + instance_payload_len); + if (!instance) { + parser_error(section->context, "Not enough memory"); + return -1; + } + + INIT_LIST_HEAD(&instance->list); + INIT_LIST_HEAD(&instance->clients); + instance->config.bindaddr.sin_family = AF_INET; + instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + instance->config.relayaddr.sin_family = AF_INET; + instance->config.relayaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + /* Default value can be checked in run-time, but I doubt anyone needs that. + * Linux: sysctl net.core.somaxconn + * FreeBSD: sysctl kern.ipc.somaxconn */ + instance->config.listenq = SOMAXCONN; + instance->config.min_backoff_ms = 100; + instance->config.max_backoff_ms = 60000; + instance->config.autoproxy = 0; + instance->config.timeout = 0; + + for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) + entry->addr = + (strcmp(entry->key, "local_ip") == 0) ? (void*)&instance->config.bindaddr.sin_addr : + (strcmp(entry->key, "local_port") == 0) ? (void*)&instance->config.bindaddr.sin_port : + (strcmp(entry->key, "interface") == 0) ? (void*)&instance->config.interface : + (strcmp(entry->key, "ip") == 0) ? (void*)&instance->config.relayaddr.sin_addr : + (strcmp(entry->key, "port") == 0) ? (void*)&instance->config.relayaddr.sin_port : + (strcmp(entry->key, "type") == 0) ? (void*)&instance->config.type : + (strcmp(entry->key, "login") == 0) ? (void*)&instance->config.login : + (strcmp(entry->key, "password") == 0) ? (void*)&instance->config.password : + (strcmp(entry->key, "listenq") == 0) ? (void*)&instance->config.listenq : + (strcmp(entry->key, "min_accept_backoff") == 0) ? (void*)&instance->config.min_backoff_ms : + (strcmp(entry->key, "max_accept_backoff") == 0) ? (void*)&instance->config.max_backoff_ms : + (strcmp(entry->key, "autoproxy") == 0) ? (void*)&instance->config.autoproxy : + (strcmp(entry->key, "timeout") == 0) ? (void*)&instance->config.timeout: + NULL; + section->data = instance; + return 0; } static int redsocks_onexit(parser_section *section) { - /* FIXME: Rewrite in bullet-proof style. There are memory leaks if config - * file is not correct, so correct on-the-fly config reloading is - * currently impossible. - */ - const char *err = NULL; - redsocks_instance *instance = section->data; - - section->data = NULL; - for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) - entry->addr = NULL; - - instance->config.bindaddr.sin_port = htons(instance->config.bindaddr.sin_port); - instance->config.relayaddr.sin_port = htons(instance->config.relayaddr.sin_port); - - if (instance->config.type) { - relay_subsys **ss; - FOREACH(ss, relay_subsystems) { - if (!strcmp((*ss)->name, instance->config.type)) { - instance->relay_ss = *ss; - list_add(&instance->list, &instances); - break; - } - } - if (!instance->relay_ss) - err = "invalid `type` for redsocks"; - } - else { - err = "no `type` for redsocks"; - } - - if (!err && !instance->config.min_backoff_ms) { - err = "`min_accept_backoff` must be positive, 0 ms is too low"; - } - - if (!err && !instance->config.max_backoff_ms) { - err = "`max_accept_backoff` must be positive, 0 ms is too low"; - } - - if (!err && !(instance->config.min_backoff_ms < instance->config.max_backoff_ms)) { - err = "`min_accept_backoff` must be less than `max_accept_backoff`"; - } - - if (err) - parser_error(section->context, err); - - return err ? -1 : 0; + /* FIXME: Rewrite in bullet-proof style. There are memory leaks if config + * file is not correct, so correct on-the-fly config reloading is + * currently impossible. + */ + const char *err = NULL; + redsocks_instance *instance = section->data; + + section->data = NULL; + for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) + entry->addr = NULL; + + instance->config.bindaddr.sin_port = htons(instance->config.bindaddr.sin_port); + instance->config.relayaddr.sin_port = htons(instance->config.relayaddr.sin_port); + + if (instance->config.type) { + relay_subsys **ss; + FOREACH(ss, relay_subsystems) { + if (!strcmp((*ss)->name, instance->config.type)) { + instance->relay_ss = *ss; + list_add(&instance->list, &instances); + break; + } + } + if (!instance->relay_ss) + err = "invalid `type` for redsocks"; + } + else { + err = "no `type` for redsocks"; + } + + if (!err && !instance->config.min_backoff_ms) { + err = "`min_accept_backoff` must be positive, 0 ms is too low"; + } + + if (!err && !instance->config.max_backoff_ms) { + err = "`max_accept_backoff` must be positive, 0 ms is too low"; + } + + if (!err && !(instance->config.min_backoff_ms < instance->config.max_backoff_ms)) { + err = "`min_accept_backoff` must be less than `max_accept_backoff`"; + } + + if (err) + parser_error(section->context, err); + + return err ? -1 : 0; } static parser_section redsocks_conf_section = { - .name = "redsocks", - .entries = redsocks_entries, - .onenter = redsocks_onenter, - .onexit = redsocks_onexit + .name = "redsocks", + .entries = redsocks_entries, + .onenter = redsocks_onenter, + .onexit = redsocks_onexit }; void redsocks_log_write_plain( - const char *file, int line, const char *func, int do_errno, - const struct sockaddr_in *clientaddr, const struct sockaddr_in *destaddr, - int priority, const char *orig_fmt, ... + const char *file, int line, const char *func, int do_errno, + const struct sockaddr_in *clientaddr, const struct sockaddr_in *destaddr, + int priority, const char *orig_fmt, ... ) { - int saved_errno = errno; - struct evbuffer *fmt = evbuffer_new(); - va_list ap; - char clientaddr_str[RED_INET_ADDRSTRLEN], destaddr_str[RED_INET_ADDRSTRLEN]; - - if (!fmt) { - log_errno(LOG_ERR, "evbuffer_new()"); - // no return, as I have to call va_start/va_end - } - - if (fmt) { - evbuffer_add_printf(fmt, "[%s->%s]: %s", - red_inet_ntop(clientaddr, clientaddr_str, sizeof(clientaddr_str)), - red_inet_ntop(destaddr, destaddr_str, sizeof(destaddr_str)), - orig_fmt); - } - - va_start(ap, orig_fmt); - if (fmt) { - errno = saved_errno; - _log_vwrite(file, line, func, do_errno, priority, (const char*)EVBUFFER_DATA(fmt), ap); - evbuffer_free(fmt); - } - va_end(ap); + int saved_errno = errno; + struct evbuffer *fmt = evbuffer_new(); + va_list ap; + char clientaddr_str[RED_INET_ADDRSTRLEN], destaddr_str[RED_INET_ADDRSTRLEN]; + + if (!fmt) { + log_errno(LOG_ERR, "evbuffer_new()"); + // no return, as I have to call va_start/va_end + } + + if (fmt) { + evbuffer_add_printf(fmt, "[%s->%s]: %s", + red_inet_ntop(clientaddr, clientaddr_str, sizeof(clientaddr_str)), + red_inet_ntop(destaddr, destaddr_str, sizeof(destaddr_str)), + orig_fmt); + } + + va_start(ap, orig_fmt); + if (fmt) { + errno = saved_errno; + _log_vwrite(file, line, func, do_errno, priority, (const char*)EVBUFFER_DATA(fmt), ap); + evbuffer_free(fmt); + } + va_end(ap); } void redsocks_touch_client(redsocks_client *client) { - redsocks_time(&client->last_event); + redsocks_time(&client->last_event); } static void redsocks_relay_readcb(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) { - if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { - if (bufferevent_write_buffer(to, from->input) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - } - else { - if (bufferevent_disable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); - } + if (evbuffer_get_length(to->output) < to->wm_write.high) { + if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } + else { + if (bufferevent_disable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); + } } static void redsocks_relay_writecb(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) { - assert(from == client->client || from == client->relay); - char from_eof = (from == client->client ? client->client_evshut : client->relay_evshut) & EV_READ; - - if (EVBUFFER_LENGTH(from->input) == 0 && from_eof) { - redsocks_shutdown(client, to, SHUT_WR); - } - else if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { - if (bufferevent_write_buffer(to, from->input) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - } + assert(from == client->client || from == client->relay); + char from_eof = (from == client->client ? client->client_evshut : client->relay_evshut) & EV_READ; + + if (evbuffer_get_length(bufferevent_get_input(from)) == 0 && from_eof) { + redsocks_shutdown(client, to, SHUT_WR); + } + else if (evbuffer_get_length(to->output) < to->wm_write.high) { + if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } } static void redsocks_relay_relayreadcb(struct bufferevent *from, void *_client) { - redsocks_client *client = _client; - redsocks_touch_client(client); - redsocks_relay_readcb(client, client->relay, client->client); + redsocks_client *client = _client; + redsocks_touch_client(client); + redsocks_relay_readcb(client, client->relay, client->client); } static void redsocks_relay_relaywritecb(struct bufferevent *to, void *_client) { - redsocks_client *client = _client; - redsocks_touch_client(client); - redsocks_relay_writecb(client, client->client, client->relay); + redsocks_client *client = _client; + redsocks_touch_client(client); + redsocks_relay_writecb(client, client->client, client->relay); } static void redsocks_relay_clientreadcb(struct bufferevent *from, void *_client) { - redsocks_client *client = _client; - redsocks_touch_client(client); - redsocks_relay_readcb(client, client->client, client->relay); + redsocks_client *client = _client; + redsocks_touch_client(client); + redsocks_relay_readcb(client, client->client, client->relay); } static void redsocks_relay_clientwritecb(struct bufferevent *to, void *_client) { - redsocks_client *client = _client; - redsocks_touch_client(client); - redsocks_relay_writecb(client, client->relay, client->client); + redsocks_client *client = _client; + redsocks_touch_client(client); + redsocks_relay_writecb(client, client->relay, client->client); } int redsocks_start_relay(redsocks_client *client) { - int error; - - if (client->instance->relay_ss->fini) - client->instance->relay_ss->fini(client); - - client->relay->wm_read.low = 0; - client->relay->wm_write.low = 0; - client->client->wm_read.low = 0; - client->client->wm_write.low = 0; - client->relay->wm_read.high = REDSOCKS_RELAY_HALFBUFF; - client->relay->wm_write.high = REDSOCKS_RELAY_HALFBUFF; - client->client->wm_read.high = REDSOCKS_RELAY_HALFBUFF; - client->client->wm_write.high = REDSOCKS_RELAY_HALFBUFF; - - client->client->readcb = redsocks_relay_clientreadcb; - client->client->writecb = redsocks_relay_clientwritecb; - client->relay->readcb = redsocks_relay_relayreadcb; - client->relay->writecb = redsocks_relay_relaywritecb; - - error = bufferevent_enable(client->client, EV_READ | EV_WRITE); - if (!error) - error = bufferevent_enable(client->relay, EV_READ | EV_WRITE); - - if (!error) { - redsocks_log_error(client, LOG_DEBUG, "data relaying started"); - } - else { - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - redsocks_drop_client(client); - } - return error; + int error; + bufferevent_event_cb event_cb; + + if (client->instance->relay_ss->fini) + client->instance->relay_ss->fini(client); + + bufferevent_setwatermark(client->client, EV_READ|EV_WRITE, 0, REDSOCKS_RELAY_HALFBUFF); + bufferevent_setwatermark(client->relay, EV_READ|EV_WRITE, 0, REDSOCKS_RELAY_HALFBUFF); +#ifdef bufferevent_getcb + bufferevent_getcb(client->client, NULL, NULL, &event_cb, NULL); +#else + event_cb = client->client->errorcb; +#endif + bufferevent_setcb(client->client, redsocks_relay_clientreadcb, + redsocks_relay_clientwritecb, + event_cb, + client); +#ifdef bufferevent_getcb + bufferevent_getcb(client->relay, NULL, NULL, &event_cb, NULL); +#else + event_cb = client->relay->errorcb; +#endif + bufferevent_setcb(client->relay, redsocks_relay_relayreadcb, + redsocks_relay_relaywritecb, + event_cb, + client); + + error = bufferevent_enable(client->client, EV_READ | EV_WRITE); + if (!error) + error = bufferevent_enable(client->relay, EV_READ | EV_WRITE); + + if (!error) { + redsocks_log_error(client, LOG_DEBUG, "data relaying started"); + } + else { + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + redsocks_drop_client(client); + } + return error; } void redsocks_drop_client(redsocks_client *client) { - redsocks_log_error(client, LOG_INFO, "dropping client"); + redsocks_log_error(client, LOG_INFO, "dropping client"); - if (client->instance->relay_ss->fini) - client->instance->relay_ss->fini(client); + if (client->instance->config.autoproxy && autoproxy_subsys.fini) + autoproxy_subsys.fini(client); + if (client->instance->relay_ss->fini) + client->instance->relay_ss->fini(client); - if (client->client) { - redsocks_close(EVENT_FD(&client->client->ev_write)); - bufferevent_free(client->client); - } + if (client->client) { + redsocks_close(bufferevent_getfd(client->client)); + bufferevent_free(client->client); + } - if (client->relay) { - redsocks_close(EVENT_FD(&client->relay->ev_write)); - bufferevent_free(client->relay); - } + if (client->relay) { + redsocks_close(bufferevent_getfd(client->relay)); + bufferevent_free(client->relay); + } - list_del(&client->list); - free(client); + list_del(&client->list); + free(client); } void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how) { - short evhow = 0; - char *strev, *strhow = NULL, *strevhow = NULL; - unsigned short *pevshut; - - assert(how == SHUT_RD || how == SHUT_WR || how == SHUT_RDWR); - assert(buffev == client->client || buffev == client->relay); - assert(EVENT_FD(&buffev->ev_read) == EVENT_FD(&buffev->ev_write)); - - if (how == SHUT_RD) { - strhow = "SHUT_RD"; - evhow = EV_READ; - strevhow = "EV_READ"; - } - else if (how == SHUT_WR) { - strhow = "SHUT_WR"; - evhow = EV_WRITE; - strevhow = "EV_WRITE"; - } - else if (how == SHUT_RDWR) { - strhow = "SHUT_RDWR"; - evhow = EV_READ|EV_WRITE; - strevhow = "EV_READ|EV_WRITE"; - } - - assert(strhow && strevhow); - - strev = buffev == client->client ? "client" : "relay"; - pevshut = buffev == client->client ? &client->client_evshut : &client->relay_evshut; - - // if EV_WRITE is already shut and we're going to shutdown read then - // we're either going to abort data flow (bad behaviour) or confirm EOF - // and in this case socket is already SHUT_RD'ed - if ( !(how == SHUT_RD && (*pevshut & EV_WRITE)) ) - if (shutdown(EVENT_FD(&buffev->ev_read), how) != 0) - redsocks_log_errno(client, LOG_ERR, "shutdown(%s, %s)", strev, strhow); - - if (bufferevent_disable(buffev, evhow) != 0) - redsocks_log_errno(client, LOG_ERR, "bufferevent_disable(%s, %s)", strev, strevhow); - - *pevshut |= evhow; - - if (client->relay_evshut == (EV_READ|EV_WRITE) && client->client_evshut == (EV_READ|EV_WRITE)) { - redsocks_log_error(client, LOG_DEBUG, "both client and server disconnected"); - redsocks_drop_client(client); - } + short evhow = 0; + char *strev, *strhow = NULL, *strevhow = NULL; + unsigned short *pevshut; + + assert(how == SHUT_RD || how == SHUT_WR || how == SHUT_RDWR); + assert(buffev == client->client || buffev == client->relay); + + if (how == SHUT_RD) { + strhow = "SHUT_RD"; + evhow = EV_READ; + strevhow = "EV_READ"; + } + else if (how == SHUT_WR) { + strhow = "SHUT_WR"; + evhow = EV_WRITE; + strevhow = "EV_WRITE"; + } + else if (how == SHUT_RDWR) { + strhow = "SHUT_RDWR"; + evhow = EV_READ|EV_WRITE; + strevhow = "EV_READ|EV_WRITE"; + } + + assert(strhow && strevhow); + + strev = buffev == client->client ? "client" : "relay"; + pevshut = buffev == client->client ? &client->client_evshut : &client->relay_evshut; + + // if EV_WRITE is already shut and we're going to shutdown read then + // we're either going to abort data flow (bad behaviour) or confirm EOF + // and in this case socket is already SHUT_RD'ed + if ( !(how == SHUT_RD && (*pevshut & EV_WRITE)) ) + if (shutdown(bufferevent_getfd(buffev), how) != 0) + redsocks_log_errno(client, LOG_ERR, "shutdown(%s, %s)", strev, strhow); + + if (bufferevent_disable(buffev, evhow) != 0) + redsocks_log_errno(client, LOG_ERR, "bufferevent_disable(%s, %s)", strev, strevhow); + + *pevshut |= evhow; + + if (client->relay_evshut == (EV_READ|EV_WRITE) && client->client_evshut == (EV_READ|EV_WRITE)) { + redsocks_log_error(client, LOG_DEBUG, "both client and server disconnected"); + redsocks_drop_client(client); + } } // I assume that -1 is invalid errno value static int redsocks_socket_geterrno(redsocks_client *client, struct bufferevent *buffev) { - int pseudo_errno = red_socket_geterrno(buffev); - if (pseudo_errno == -1) { - redsocks_log_errno(client, LOG_ERR, "red_socket_geterrno"); - return -1; - } - return pseudo_errno; + int pseudo_errno = red_socket_geterrno(buffev); + if (pseudo_errno == -1) { + redsocks_log_errno(client, LOG_ERR, "red_socket_geterrno"); + return -1; + } + return pseudo_errno; } void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg) { - redsocks_client *client = _arg; - assert(buffev == client->relay || buffev == client->client); - - redsocks_touch_client(client); - - if (what == (EVBUFFER_READ|EVBUFFER_EOF)) { - struct bufferevent *antiev; - if (buffev == client->relay) - antiev = client->client; - else - antiev = client->relay; - - redsocks_shutdown(client, buffev, SHUT_RD); - - if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0) - redsocks_shutdown(client, antiev, SHUT_WR); - } - else { - errno = redsocks_socket_geterrno(client, buffev); - redsocks_log_errno(client, LOG_NOTICE, "%s error, code " event_fmt_str, - buffev == client->relay ? "relay" : "client", - event_fmt(what)); - redsocks_drop_client(client); - } + redsocks_client *client = _arg; + assert(buffev == client->relay || buffev == client->client); + + redsocks_touch_client(client); + + redsocks_log_errno(client, LOG_DEBUG, "%s, what: " event_fmt_str, + buffev == client->client?"client":"relay", + event_fmt(what)); + + if (what == (BEV_EVENT_READING|BEV_EVENT_EOF)) { + struct bufferevent *antiev; + if (buffev == client->relay) + antiev = client->client; + else + antiev = client->relay; + + redsocks_shutdown(client, buffev, SHUT_RD); + + if (antiev != NULL && evbuffer_get_length(bufferevent_get_output(antiev)) == 0) + redsocks_shutdown(client, antiev, SHUT_WR); + } + else { + errno = redsocks_socket_geterrno(client, buffev); + redsocks_log_errno(client, LOG_NOTICE, "%s error, code " event_fmt_str, + buffev == client->relay ? "relay" : "client", + event_fmt(what)); + redsocks_drop_client(client); + } } int sizes_equal(size_t a, size_t b) { - return a == b; + return a == b; } int sizes_greater_equal(size_t a, size_t b) { - return a >= b; + return a >= b; } int redsocks_read_expected(redsocks_client *client, struct evbuffer *input, void *data, size_comparator comparator, size_t expected) { - size_t len = EVBUFFER_LENGTH(input); - if (comparator(len, expected)) { - int read = evbuffer_remove(input, data, expected); - UNUSED(read); - assert(read == expected); - return 0; - } - else { - redsocks_log_error(client, LOG_NOTICE, "Can't get expected amount of data"); - redsocks_drop_client(client); - return -1; - } + size_t len = evbuffer_get_length(input); + if (comparator(len, expected)) { + int read = evbuffer_remove(input, data, expected); + UNUSED(read); + assert(read == expected); + return 0; + } + else { + redsocks_log_error(client, LOG_NOTICE, "Can't get expected amount of data"); + redsocks_drop_client(client); + return -1; + } } struct evbuffer *mkevbuffer(void *data, size_t len) { - struct evbuffer *buff = NULL, *retval = NULL; + struct evbuffer *buff = NULL, *retval = NULL; - buff = evbuffer_new(); - if (!buff) { - log_errno(LOG_ERR, "evbuffer_new"); - goto fail; - } + buff = evbuffer_new(); + if (!buff) { + log_errno(LOG_ERR, "evbuffer_new"); + goto fail; + } - if (evbuffer_add(buff, data, len) < 0) { - log_errno(LOG_ERR, "evbuffer_add"); - goto fail; - } + if (evbuffer_add(buff, data, len) < 0) { + log_errno(LOG_ERR, "evbuffer_add"); + goto fail; + } - retval = buff; - buff = NULL; + retval = buff; + buff = NULL; fail: - if (buff) - evbuffer_free(buff); - return retval; + if (buff) + evbuffer_free(buff); + return retval; } int redsocks_write_helper_ex( - struct bufferevent *buffev, redsocks_client *client, - redsocks_message_maker mkmessage, int state, size_t wm_low, size_t wm_high) + struct bufferevent *buffev, redsocks_client *client, + redsocks_message_maker mkmessage, int state, size_t wm_low, size_t wm_high) { - assert(client); - return redsocks_write_helper_ex_plain(buffev, client, (redsocks_message_maker_plain)mkmessage, - client, state, wm_low, wm_high); + assert(client); + return redsocks_write_helper_ex_plain(buffev, client, (redsocks_message_maker_plain)mkmessage, + client, state, wm_low, wm_high); } int redsocks_write_helper_ex_plain( - struct bufferevent *buffev, redsocks_client *client, - redsocks_message_maker_plain mkmessage, void *p, int state, size_t wm_low, size_t wm_high) + struct bufferevent *buffev, redsocks_client *client, + redsocks_message_maker_plain mkmessage, void *p, int state, size_t wm_low, size_t wm_high) { - int len; - struct evbuffer *buff = NULL; - int drop = 1; - - if (mkmessage) { - buff = mkmessage(p); - if (!buff) - goto fail; - - assert(!client || buffev == client->relay); - len = bufferevent_write_buffer(buffev, buff); - if (len < 0) { - if (client) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - else - log_errno(LOG_ERR, "bufferevent_write_buffer"); - goto fail; - } - } - - if (client) - client->state = state; - buffev->wm_read.low = wm_low; - buffev->wm_read.high = wm_high; - bufferevent_enable(buffev, EV_READ); - drop = 0; + int len; + struct evbuffer *buff = NULL; + int drop = 1; + + if (mkmessage) { + buff = mkmessage(p); + if (!buff) + goto fail; + + assert(!client || buffev == client->relay); + len = bufferevent_write_buffer(buffev, buff); + if (len < 0) { + if (client) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + else + log_errno(LOG_ERR, "bufferevent_write_buffer"); + goto fail; + } + } + + if (client) + client->state = state; + buffev->wm_read.low = wm_low; + buffev->wm_read.high = wm_high; + bufferevent_enable(buffev, EV_READ); + drop = 0; fail: - if (buff) - evbuffer_free(buff); - if (drop && client) - redsocks_drop_client(client); - return drop ? -1 : 0; + if (buff) + evbuffer_free(buff); + if (drop && client) + redsocks_drop_client(client); + return drop ? -1 : 0; } int redsocks_write_helper( - struct bufferevent *buffev, redsocks_client *client, - redsocks_message_maker mkmessage, int state, size_t wm_only) + struct bufferevent *buffev, redsocks_client *client, + redsocks_message_maker mkmessage, int state, size_t wm_only) { - assert(client); - return redsocks_write_helper_ex(buffev, client, mkmessage, state, wm_only, wm_only); + assert(client); + return redsocks_write_helper_ex(buffev, client, mkmessage, state, wm_only, wm_only); } void redsocks_relay_connected(struct bufferevent *buffev, void *_arg) { - redsocks_client *client = _arg; + redsocks_client *client = _arg; - assert(buffev == client->relay); + assert(buffev == client->relay); - redsocks_touch_client(client); + redsocks_touch_client(client); - if (!red_is_socket_connected_ok(buffev)) { - redsocks_log_errno(client, LOG_NOTICE, "red_is_socket_connected_ok"); - goto fail; - } + if (!red_is_socket_connected_ok(buffev)) { + redsocks_log_errno(client, LOG_NOTICE, "red_is_socket_connected_ok"); + goto fail; + } - client->relay->readcb = client->instance->relay_ss->readcb; - client->relay->writecb = client->instance->relay_ss->writecb; - client->relay->writecb(buffev, _arg); - return; + bufferevent_setcb(client->relay, client->instance->relay_ss->readcb, + client->instance->relay_ss->writecb, + redsocks_event_error, + client); + if (evbuffer_get_length(bufferevent_get_input(client->client))) + client->instance->relay_ss->writecb(client->relay, client); +// else +// bufferevent_enable(client->relay, EV_READ); + return; fail: - redsocks_drop_client(client); + redsocks_drop_client(client); } void redsocks_connect_relay(redsocks_client *client) { - struct timeval tv; - tv.tv_sec = client->instance->config.timeout; - tv.tv_usec = 0; - if (tv.tv_sec == 0) - tv.tv_sec = 10; - - client->relay = red_connect_relay2(&client->instance->config.relayaddr, - redsocks_relay_connected, redsocks_event_error, client, &tv); - if (!client->relay) { - redsocks_log_errno(client, LOG_ERR, "red_connect_relay"); - redsocks_drop_client(client); - } + struct timeval tv; + tv.tv_sec = client->instance->config.timeout; + tv.tv_usec = 0; + if (tv.tv_sec == 0) + tv.tv_sec = 10; + + + client->relay = red_connect_relay2(&client->instance->config.relayaddr, + NULL, + redsocks_relay_connected, + redsocks_event_error, client, &tv); + if (!client->relay) { + redsocks_log_errno(client, LOG_ERR, "red_connect_relay failed!!!"); + redsocks_drop_client(client); + } } static void redsocks_accept_backoff(int fd, short what, void *_arg) { - redsocks_instance *self = _arg; + redsocks_instance *self = _arg; - /* Isn't it already deleted? EV_PERSIST has nothing common with timeouts in - * old libevent... On the other hand libevent does not return any error. */ - if (tracked_event_del(&self->accept_backoff) != 0) - log_errno(LOG_ERR, "event_del"); + /* Isn't it already deleted? EV_PERSIST has nothing common with timeouts in + * old libevent... On the other hand libevent does not return any error. */ + if (tracked_event_del(&self->accept_backoff) != 0) + log_errno(LOG_ERR, "event_del"); - if (tracked_event_add(&self->listener, NULL) != 0) - log_errno(LOG_ERR, "event_add"); + if (tracked_event_add(&self->listener, NULL) != 0) + log_errno(LOG_ERR, "event_add"); } void redsocks_close_internal(int fd, const char* file, int line, const char *func) { - if (close(fd) == 0) { - redsocks_instance *instance = NULL; - struct timeval now; - gettimeofday(&now, NULL); - list_for_each_entry(instance, &instances, list) { - if (timerisset(&instance->accept_backoff.inserted)) { - struct timeval min_accept_backoff = { - instance->config.min_backoff_ms / 1000, - (instance->config.min_backoff_ms % 1000) * 1000}; - struct timeval time_passed; - timersub(&now, &instance->accept_backoff.inserted, &time_passed); - if (timercmp(&min_accept_backoff, &time_passed, <)) { - redsocks_accept_backoff(-1, 0, instance); - break; - } - } - } - } - else { - const int do_errno = 1; - _log_write(file, line, func, do_errno, LOG_WARNING, "close"); - } + if (close(fd) == 0) { + redsocks_instance *instance = NULL; + struct timeval now; + gettimeofday(&now, NULL); + list_for_each_entry(instance, &instances, list) { + if (timerisset(&instance->accept_backoff.inserted)) { + struct timeval min_accept_backoff = { + instance->config.min_backoff_ms / 1000, + (instance->config.min_backoff_ms % 1000) * 1000}; + struct timeval time_passed; + timersub(&now, &instance->accept_backoff.inserted, &time_passed); + if (timercmp(&min_accept_backoff, &time_passed, <)) { + redsocks_accept_backoff(-1, 0, instance); + break; + } + } + } + } + else { + const int do_errno = 1; + _log_write(file, line, func, do_errno, LOG_WARNING, "close"); + } } static void redsocks_accept_client(int fd, short what, void *_arg) { - redsocks_instance *self = _arg; - redsocks_client *client = NULL; - struct sockaddr_in clientaddr; - struct sockaddr_in myaddr; - struct sockaddr_in destaddr; - socklen_t addrlen = sizeof(clientaddr); - int on = 1; - int client_fd = -1; - int error; - - // working with client_fd - client_fd = accept(fd, (struct sockaddr*)&clientaddr, &addrlen); - if (client_fd == -1) { - /* Different systems use different `errno` value to signal different - * `lack of file descriptors` conditions. Here are most of them. */ - if (errno == ENFILE || errno == EMFILE || errno == ENOBUFS || errno == ENOMEM) { - self->accept_backoff_ms = (self->accept_backoff_ms << 1) + 1; - clamp_value(self->accept_backoff_ms, self->config.min_backoff_ms, self->config.max_backoff_ms); - int delay = (random() % self->accept_backoff_ms) + 1; - log_errno(LOG_WARNING, "accept: out of file descriptors, backing off for %u ms", delay); - struct timeval tvdelay = { delay / 1000, (delay % 1000) * 1000 }; - if (tracked_event_del(&self->listener) != 0) - log_errno(LOG_ERR, "event_del"); - if (tracked_event_add(&self->accept_backoff, &tvdelay) != 0) - log_errno(LOG_ERR, "event_add"); - } - else { - log_errno(LOG_WARNING, "accept"); - } - goto fail; - } - self->accept_backoff_ms = 0; - - // socket is really bound now (it could be bound to 0.0.0.0) - addrlen = sizeof(myaddr); - error = getsockname(client_fd, (struct sockaddr*)&myaddr, &addrlen); - if (error) { - log_errno(LOG_WARNING, "getsockname"); - goto fail; - } - - error = getdestaddr(client_fd, &clientaddr, &myaddr, &destaddr); - if (error) { - goto fail; - } - - error = setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); - if (error) { - log_errno(LOG_WARNING, "setsockopt"); - goto fail; - } - - // everything seems to be ok, let's allocate some memory - if (self->config.autoproxy) - client = calloc(1, sizeof(redsocks_client) + - self->relay_ss->payload_len + autoproxy_subsys.payload_len - ); - else - client = calloc(1, sizeof(redsocks_client) + self->relay_ss->payload_len); - if (!client) { - log_errno(LOG_ERR, "calloc"); - goto fail; - } - - client->instance = self; - memcpy(&client->clientaddr, &clientaddr, sizeof(clientaddr)); - memcpy(&client->destaddr, &destaddr, sizeof(destaddr)); - INIT_LIST_HEAD(&client->list); - self->relay_ss->init(client); - if (self->config.autoproxy) - autoproxy_subsys.init(client); - - if (redsocks_time(&client->first_event) == ((time_t)-1)) - goto fail; - - redsocks_touch_client(client); - - client->client = bufferevent_new(client_fd, NULL, NULL, redsocks_event_error, client); - if (!client->client) { - log_errno(LOG_ERR, "bufferevent_new"); - goto fail; - } - client_fd = -1; - - list_add(&client->list, &self->clients); - - // enable reading to handle EOF from client - if (bufferevent_enable(client->client, EV_READ) != 0) { - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - goto fail; - } - - redsocks_log_error(client, LOG_INFO, "accepted"); - - if (self->config.autoproxy && autoproxy_subsys.connect_relay) - { - autoproxy_subsys.connect_relay(client); - } - else if (self->relay_ss->connect_relay) - self->relay_ss->connect_relay(client); - else - redsocks_connect_relay(client); - - return; + redsocks_instance *self = _arg; + redsocks_client *client = NULL; + struct sockaddr_in clientaddr; + struct sockaddr_in myaddr; + struct sockaddr_in destaddr; + socklen_t addrlen = sizeof(clientaddr); + int on = 1; + int client_fd = -1; + int error; + + // working with client_fd + client_fd = accept(fd, (struct sockaddr*)&clientaddr, &addrlen); + if (client_fd == -1) { + /* Different systems use different `errno` value to signal different + * `lack of file descriptors` conditions. Here are most of them. */ + if (errno == ENFILE || errno == EMFILE || errno == ENOBUFS || errno == ENOMEM) { + self->accept_backoff_ms = (self->accept_backoff_ms << 1) + 1; + clamp_value(self->accept_backoff_ms, self->config.min_backoff_ms, self->config.max_backoff_ms); + int delay = (random() % self->accept_backoff_ms) + 1; + log_errno(LOG_WARNING, "accept: out of file descriptors, backing off for %u ms", delay); + struct timeval tvdelay = { delay / 1000, (delay % 1000) * 1000 }; + if (tracked_event_del(&self->listener) != 0) + log_errno(LOG_ERR, "event_del"); + if (tracked_event_add(&self->accept_backoff, &tvdelay) != 0) + log_errno(LOG_ERR, "event_add"); + } + else { + log_errno(LOG_WARNING, "accept"); + } + goto fail; + } + self->accept_backoff_ms = 0; + + // socket is really bound now (it could be bound to 0.0.0.0) + addrlen = sizeof(myaddr); + error = getsockname(client_fd, (struct sockaddr*)&myaddr, &addrlen); + if (error) { + log_errno(LOG_WARNING, "getsockname"); + goto fail; + } + + error = getdestaddr(client_fd, &clientaddr, &myaddr, &destaddr); + if (error) { + goto fail; + } + + error = setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); + if (error) { + log_errno(LOG_WARNING, "setsockopt"); + goto fail; + } + + // everything seems to be ok, let's allocate some memory + if (self->config.autoproxy) + client = calloc(1, sizeof(redsocks_client) + + self->relay_ss->payload_len + autoproxy_subsys.payload_len + ); + else + client = calloc(1, sizeof(redsocks_client) + self->relay_ss->payload_len); + if (!client) { + log_errno(LOG_ERR, "calloc"); + goto fail; + } + + client->instance = self; + memcpy(&client->clientaddr, &clientaddr, sizeof(clientaddr)); + memcpy(&client->destaddr, &destaddr, sizeof(destaddr)); + INIT_LIST_HEAD(&client->list); + self->relay_ss->init(client); + if (self->config.autoproxy) + autoproxy_subsys.init(client); + + if (redsocks_time(&client->first_event) == ((time_t)-1)) + goto fail; + + redsocks_touch_client(client); + + client->client = bufferevent_socket_new(get_event_base(), client_fd, 0); + if (!client->client) { + log_errno(LOG_ERR, "bufferevent_socket_new"); + goto fail; + } + bufferevent_setcb(client->client, NULL, NULL, redsocks_event_error, client); + + client_fd = -1; + + list_add(&client->list, &self->clients); + + // enable reading to handle EOF from client + if (bufferevent_enable(client->client, EV_READ) != 0) { + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + goto fail; + } + + redsocks_log_error(client, LOG_DEBUG, "accepted"); + + if (self->config.autoproxy && autoproxy_subsys.connect_relay) + autoproxy_subsys.connect_relay(client); + else if (self->relay_ss->connect_relay) + self->relay_ss->connect_relay(client); + else + redsocks_connect_relay(client); + + return; fail: - if (client) { - redsocks_drop_client(client); - } - if (client_fd != -1) - redsocks_close(client_fd); + if (client) { + redsocks_drop_client(client); + } + if (client_fd != -1) + redsocks_close(client_fd); } static const char *redsocks_evshut_str(unsigned short evshut) { - return - evshut == EV_READ ? "SHUT_RD" : - evshut == EV_WRITE ? "SHUT_WR" : - evshut == (EV_READ|EV_WRITE) ? "SHUT_RDWR" : - evshut == 0 ? "" : - "???"; + return + evshut == EV_READ ? "SHUT_RD" : + evshut == EV_WRITE ? "SHUT_WR" : + evshut == (EV_READ|EV_WRITE) ? "SHUT_RDWR" : + evshut == 0 ? "" : + "???"; } static const char *redsocks_event_str(unsigned short what) { - return - what == EV_READ ? "R/-" : - what == EV_WRITE ? "-/W" : - what == (EV_READ|EV_WRITE) ? "R/W" : - what == 0 ? "-/-" : - "???"; + return + what == EV_READ ? "R/-" : + what == EV_WRITE ? "-/W" : + what == (EV_READ|EV_WRITE) ? "R/W" : + what == 0 ? "-/-" : + "???"; } -static void redsocks_debug_dump_instance(redsocks_instance *instance, time_t now) +void redsocks_dump_client(redsocks_client * client) { - redsocks_client *client = NULL; - - log_error(LOG_DEBUG, "Dumping client list for instance %p:", instance); - list_for_each_entry(client, &instance->clients, list) { - const char *s_client_evshut = redsocks_evshut_str(client->client_evshut); - const char *s_relay_evshut = redsocks_evshut_str(client->relay_evshut); - - redsocks_log_error(client, LOG_DEBUG, "client: %i (%s)%s%s input %d output %d, relay: %i (%s)%s%s input %d output %d, age: %li sec, idle: %li sec.", - EVENT_FD(&client->client->ev_write), - redsocks_event_str(client->client->enabled), - s_client_evshut[0] ? " " : "", s_client_evshut, - EVBUFFER_LENGTH(client->client->input), - EVBUFFER_LENGTH(client->client->output), - EVENT_FD(&client->relay->ev_write), - redsocks_event_str(client->relay->enabled), - s_relay_evshut[0] ? " " : "", s_relay_evshut, - EVBUFFER_LENGTH(client->relay->input), - EVBUFFER_LENGTH(client->relay->output), - now - client->first_event, - now - client->last_event); - } - log_error(LOG_DEBUG, "End of client list."); + time_t now = redsocks_time(NULL); + + const char *s_client_evshut = redsocks_evshut_str(client->client_evshut); + const char *s_relay_evshut = redsocks_evshut_str(client->relay_evshut); + + redsocks_log_error(client, LOG_DEBUG, "client: %i (%s)%s%s input %d output %d, relay: %i (%s)%s%s input %d output %d, age: %li sec, idle: %li sec.", + bufferevent_getfd(client->client), + redsocks_event_str(bufferevent_get_enabled(client->client)), + s_client_evshut[0] ? " " : "", s_client_evshut, + evbuffer_get_length(bufferevent_get_input(client->client)), + evbuffer_get_length(bufferevent_get_output(client->client)), + bufferevent_getfd(client->relay), + redsocks_event_str(bufferevent_get_enabled(client->relay)), + s_relay_evshut[0] ? " " : "", s_relay_evshut, + evbuffer_get_length(bufferevent_get_input(client->relay)), + evbuffer_get_length(bufferevent_get_output(client->relay)), + now - client->first_event, + now - client->last_event); +} + +static void redsocks_debug_dump_instance(redsocks_instance *instance) +{ + redsocks_client *client = NULL; + + log_error(LOG_DEBUG, "Dumping client list for instance %p:", instance); + list_for_each_entry(client, &instance->clients, list) + redsocks_dump_client(client); + + log_error(LOG_DEBUG, "End of client list."); } static void redsocks_debug_dump(int sig, short what, void *_arg) { - redsocks_instance *instance = NULL; - time_t now = redsocks_time(NULL); + redsocks_instance *instance = NULL; - list_for_each_entry(instance, &instances, list) - redsocks_debug_dump_instance(instance, now); + list_for_each_entry(instance, &instances, list) + redsocks_debug_dump_instance(instance); } /* Audit is required to clean up hung connections. @@ -809,63 +845,49 @@ static void redsocks_debug_dump(int sig, short what, void *_arg) */ static void redsocks_audit_instance(redsocks_instance *instance) { - redsocks_client *tmp, *client = NULL; - time_t now = redsocks_time(NULL); - int drop_it = 0; - - log_error(LOG_DEBUG, "Audit client list for instance %p:", instance); - list_for_each_entry_safe(client, tmp, &instance->clients, list) { - drop_it = 0; - - if (now - client->last_event >= REDSOCKS_AUDIT_INTERVAL){ - /* Only take actions if no touch of the client for at least an audit cycle.*/ - /* drop this client if either end disconnected */ - if ((client->client_evshut == EV_WRITE && client->relay_evshut == EV_READ) - || (client->client_evshut == EV_READ && client->relay_evshut == EV_WRITE) - || (client->client_evshut == (EV_READ|EV_WRITE) && client->relay_evshut == EV_WRITE)) - drop_it = 1; - if (client->client->enabled == 0 && client->relay->enabled== EV_WRITE - && client->client_evshut == 0 && client->relay_evshut == 0 ) - { - /* for debug purpose only */ - redsocks_log_error(client, LOG_DEBUG, "%i connect: %i",EVENT_FD(&client->client->ev_write), red_is_socket_connected_ok(client->relay)); - } - } - /* close long connections without activities */ - if (now - client->last_event >= 3600 * 2) - drop_it = 1; - - if (drop_it){ - const char *s_client_evshut = redsocks_evshut_str(client->client_evshut); - const char *s_relay_evshut = redsocks_evshut_str(client->relay_evshut); - - redsocks_log_error(client, LOG_DEBUG, "Audit client: %i (%s)%s%s input %d output %d, relay: %i (%s)%s%s input %d output %d, age: %li sec, idle: %li sec.", - EVENT_FD(&client->client->ev_write), - redsocks_event_str(client->client->enabled), - s_client_evshut[0] ? " " : "", s_client_evshut, - EVBUFFER_LENGTH(client->client->input), - EVBUFFER_LENGTH(client->client->output), - EVENT_FD(&client->relay->ev_write), - redsocks_event_str(client->relay->enabled), - s_relay_evshut[0] ? " " : "", s_relay_evshut, - EVBUFFER_LENGTH(client->relay->input), - EVBUFFER_LENGTH(client->relay->output), - now - client->first_event, - now - client->last_event); - - redsocks_drop_client(client); - } - } - log_error(LOG_DEBUG, "End of auditing client list."); + redsocks_client *tmp, *client = NULL; + time_t now = redsocks_time(NULL); + int drop_it = 0; + + log_error(LOG_DEBUG, "Audit client list for instance %p:", instance); + list_for_each_entry_safe(client, tmp, &instance->clients, list) { + drop_it = 0; + + if (now - client->last_event >= REDSOCKS_AUDIT_INTERVAL){ + /* Only take actions if no touch of the client for at least an audit cycle.*/ + /* drop this client if either end disconnected */ + if ((client->client_evshut == EV_WRITE && client->relay_evshut == EV_READ) + || (client->client_evshut == EV_READ && client->relay_evshut == EV_WRITE) + || (client->client_evshut == (EV_READ|EV_WRITE) && client->relay_evshut == EV_WRITE)) + drop_it = 1; + if (client->client->enabled == 0 && client->relay->enabled== EV_WRITE + && client->client_evshut == 0 && client->relay_evshut == 0 ) + { + /* for debug purpose only */ + redsocks_log_error(client, LOG_DEBUG, "%i connect: %i",bufferevent_getfd(client->client), red_is_socket_connected_ok(client->relay)); + } + } + /* close long connections without activities */ + if (now - client->last_event >= 3600 * 4) + drop_it = 1; + + if (drop_it){ + redsocks_dump_client(client); + redsocks_drop_client(client); + } + } + log_error(LOG_DEBUG, "End of auditing client list."); } static void redsocks_heartbeat(int sig, short what, void *_arg) { - time_t now = redsocks_time(NULL); + time_t now = redsocks_time(NULL); FILE * tmp = NULL; + char tmp_fname[128]; - tmp = fopen("/tmp/redtime", "w"); + snprintf(tmp_fname, sizeof(tmp_fname), "/tmp/redtime-%d", getppid()); + tmp = fopen(tmp_fname, "w"); if (tmp) { fprintf(tmp, "%d", now); @@ -875,116 +897,115 @@ static void redsocks_heartbeat(int sig, short what, void *_arg) static void redsocks_audit(int sig, short what, void *_arg) { - redsocks_instance * tmp, *instance = NULL; + redsocks_instance * tmp, *instance = NULL; - list_for_each_entry_safe(instance, tmp, &instances, list) - redsocks_audit_instance(instance); + list_for_each_entry_safe(instance, tmp, &instances, list) + redsocks_audit_instance(instance); } static void redsocks_fini_instance(redsocks_instance *instance); static int redsocks_init_instance(redsocks_instance *instance) { - /* FIXME: redsocks_fini_instance is called in case of failure, this - * function will remove instance from instances list - result - * looks ugly. - */ - int error; - int on = 1; - int fd = -1; - - fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd == -1) { - log_errno(LOG_ERR, "socket"); - goto fail; - } - - error = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (error) { - log_errno(LOG_ERR, "setsockopt"); - goto fail; - } - - error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); - if (error) { - log_errno(LOG_ERR, "bind"); - goto fail; - } - - error = fcntl_nonblock(fd); - if (error) { - log_errno(LOG_ERR, "fcntl"); - goto fail; - } - - error = listen(fd, instance->config.listenq); - if (error) { - log_errno(LOG_ERR, "listen"); - goto fail; - } - - tracked_event_set(&instance->listener, fd, EV_READ | EV_PERSIST, redsocks_accept_client, instance); - fd = -1; - - tracked_event_set(&instance->accept_backoff, -1, 0, redsocks_accept_backoff, instance); - - error = tracked_event_add(&instance->listener, NULL); - if (error) { - log_errno(LOG_ERR, "event_add"); - goto fail; - } - - return 0; + /* FIXME: redsocks_fini_instance is called in case of failure, this + * function will remove instance from instances list - result + * looks ugly. + */ + int error; + evutil_socket_t fd = -1; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + log_errno(LOG_ERR, "socket"); + goto fail; + } + + error = evutil_make_listen_socket_reuseable(fd); + if (error) { + log_errno(LOG_ERR, "evutil_make_listen_socket_reuseable"); + goto fail; + } + + error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); + if (error) { + log_errno(LOG_ERR, "bind"); + goto fail; + } + + error = evutil_make_socket_nonblocking(fd); + if (error) { + log_errno(LOG_ERR, "evutil_make_socket_nonblocking"); + goto fail; + } + + error = listen(fd, instance->config.listenq); + if (error) { + log_errno(LOG_ERR, "listen"); + goto fail; + } + + tracked_event_set(&instance->listener, fd, EV_READ | EV_PERSIST, redsocks_accept_client, instance); + fd = -1; + + tracked_event_set(&instance->accept_backoff, -1, 0, redsocks_accept_backoff, instance); + + error = tracked_event_add(&instance->listener, NULL); + if (error) { + log_errno(LOG_ERR, "event_add"); + goto fail; + } + + return 0; fail: - redsocks_fini_instance(instance); + redsocks_fini_instance(instance); - if (fd != -1) { - redsocks_close(fd); - } + if (fd != -1) { + redsocks_close(fd); + } - return -1; + return -1; } /* Drops instance completely, freeing its memory and removing from * instances list. */ static void redsocks_fini_instance(redsocks_instance *instance) { - if (!list_empty(&instance->clients)) { - redsocks_client *tmp, *client = NULL; - - log_error(LOG_WARNING, "There are connected clients during shutdown! Disconnecting them."); - list_for_each_entry_safe(client, tmp, &instance->clients, list) { - redsocks_drop_client(client); - } - } - - if (instance->relay_ss->instance_fini) - instance->relay_ss->instance_fini(instance); - - if (event_initialized(&instance->listener.ev)) { - if (timerisset(&instance->listener.inserted)) - if (tracked_event_del(&instance->listener) != 0) - log_errno(LOG_WARNING, "event_del"); - redsocks_close(EVENT_FD(&instance->listener.ev)); - memset(&instance->listener, 0, sizeof(instance->listener)); - } - - if (event_initialized(&instance->accept_backoff.ev)) { - if (timerisset(&instance->accept_backoff.inserted)) - if (tracked_event_del(&instance->accept_backoff) != 0) - log_errno(LOG_WARNING, "event_del"); - memset(&instance->accept_backoff, 0, sizeof(instance->accept_backoff)); - } - - list_del(&instance->list); - - free(instance->config.type); - free(instance->config.login); - free(instance->config.password); - - memset(instance, 0, sizeof(*instance)); - free(instance); + if (!list_empty(&instance->clients)) { + redsocks_client *tmp, *client = NULL; + + log_error(LOG_WARNING, "There are connected clients during shutdown! Disconnecting them."); + list_for_each_entry_safe(client, tmp, &instance->clients, list) { + redsocks_drop_client(client); + } + } + + if (instance->relay_ss->instance_fini) + instance->relay_ss->instance_fini(instance); + + if (event_initialized(&instance->listener.ev)) { + if (timerisset(&instance->listener.inserted)) + if (tracked_event_del(&instance->listener) != 0) + log_errno(LOG_WARNING, "event_del"); + redsocks_close(EVENT_FD(&instance->listener.ev)); + memset(&instance->listener, 0, sizeof(instance->listener)); + } + + if (event_initialized(&instance->accept_backoff.ev)) { + if (timerisset(&instance->accept_backoff.inserted)) + if (tracked_event_del(&instance->accept_backoff) != 0) + log_errno(LOG_WARNING, "event_del"); + memset(&instance->accept_backoff, 0, sizeof(instance->accept_backoff)); + } + + list_del(&instance->list); + + free(instance->config.type); + free(instance->config.login); + free(instance->config.password); + + memset(instance, 0, sizeof(*instance)); + free(instance); } static int redsocks_fini(); @@ -996,85 +1017,87 @@ static struct event audit_event; //static void ignore_sig(int t) {} static int redsocks_init() { - struct sigaction sa , sa_old; - redsocks_instance *tmp, *instance = NULL; - void (* old_hdl)(int)= NULL; - struct timeval audit_time; + struct sigaction sa/* , sa_old*/; + redsocks_instance *tmp, *instance = NULL; +// void (* old_hdl)(int)= NULL; + struct timeval audit_time; + struct event_base * base = get_event_base(); - memset(&audit_event, 0, sizeof(audit_event)); + memset(&audit_event, 0, sizeof(audit_event)); /* old_hdl = signal(SIGPIPE, ignore_sig); - if (old_hdl == -1) { - log_errno(LOG_ERR, "sigaction"); - return -1; - } + if (old_hdl == -1) { + log_errno(LOG_ERR, "sigaction"); + return -1; + } */ - sa.sa_handler = SIG_IGN; - sa.sa_flags = SA_RESTART; - - if (sigaction(SIGPIPE, &sa, NULL) == -1) { - log_errno(LOG_ERR, "sigaction"); - return -1; - } - - signal_set(&debug_dumper, SIGUSR1, redsocks_debug_dump, NULL); - if (signal_add(&debug_dumper, NULL) != 0) { - log_errno(LOG_ERR, "signal_add"); - goto fail; - } - signal_set(&heartbeat_writer, SIGUSR2, redsocks_heartbeat, NULL); - if (signal_add(&heartbeat_writer, NULL) != 0) { - log_errno(LOG_ERR, "signal_add SIGUSR2"); - goto fail; - } - /* Start audit */ - audit_time.tv_sec = REDSOCKS_AUDIT_INTERVAL; - audit_time.tv_usec = 0; - event_set(&audit_event, 0, EV_TIMEOUT|EV_PERSIST, redsocks_audit, NULL); - evtimer_add(&audit_event, &audit_time); - - list_for_each_entry_safe(instance, tmp, &instances, list) { - if (redsocks_init_instance(instance) != 0) - goto fail; - } - - return 0; + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = SIG_IGN; + //sa.sa_flags = SA_RESTART; + + if (sigaction(SIGPIPE, &sa, NULL) == -1) { + log_errno(LOG_ERR, "sigaction"); + return -1; + } + + evsignal_assign(&debug_dumper, base, SIGUSR1, redsocks_debug_dump, NULL); + if (evsignal_add(&debug_dumper, NULL) != 0) { + log_errno(LOG_ERR, "evsignal_add"); + goto fail; + } + evsignal_assign(&heartbeat_writer, base, SIGUSR2, redsocks_heartbeat, NULL); + if (evsignal_add(&heartbeat_writer, NULL) != 0) { + log_errno(LOG_ERR, "evsignal_add SIGUSR2"); + goto fail; + } + /* Start audit */ + audit_time.tv_sec = REDSOCKS_AUDIT_INTERVAL; + audit_time.tv_usec = 0; + event_assign(&audit_event, base, 0, EV_TIMEOUT|EV_PERSIST, redsocks_audit, NULL); + evtimer_add(&audit_event, &audit_time); + + list_for_each_entry_safe(instance, tmp, &instances, list) { + if (redsocks_init_instance(instance) != 0) + goto fail; + } + + return 0; fail: - // that was the first resource allocation, it return's on failure, not goto-fail's -/* sigaction(SIGPIPE, &sa_old, NULL); */ + // that was the first resource allocation, it return's on failure, not goto-fail's +/* sigaction(SIGPIPE, &sa_old, NULL); */ // signal(SIGPIPE, old_hdl); - redsocks_fini(); + redsocks_fini(); - return -1; + return -1; } static int redsocks_fini() { - redsocks_instance *tmp, *instance = NULL; + redsocks_instance *tmp, *instance = NULL; - list_for_each_entry_safe(instance, tmp, &instances, list) - redsocks_fini_instance(instance); + list_for_each_entry_safe(instance, tmp, &instances, list) + redsocks_fini_instance(instance); - /* stop audit */ - if (event_initialized(&audit_event)) - evtimer_del(&audit_event); + /* stop audit */ + if (event_initialized(&audit_event)) + evtimer_del(&audit_event); - if (signal_initialized(&debug_dumper)) { - if (signal_del(&debug_dumper) != 0) - log_errno(LOG_WARNING, "signal_del"); - memset(&debug_dumper, 0, sizeof(debug_dumper)); - } + if (evsignal_initialized(&debug_dumper)) { + if (evsignal_del(&debug_dumper) != 0) + log_errno(LOG_WARNING, "evsignal_del"); + memset(&debug_dumper, 0, sizeof(debug_dumper)); + } - return 0; + return 0; } app_subsys redsocks_subsys = { - .init = redsocks_init, - .fini = redsocks_fini, - .conf_section = &redsocks_conf_section, + .init = redsocks_init, + .fini = redsocks_fini, + .conf_section = &redsocks_conf_section, }; diff --git a/redsocks.conf.example b/redsocks.conf.example index b4447263..0aa28926 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -62,11 +62,15 @@ redsocks { ip = example.org; port = 1080; - // known types: socks4, socks5, http-connect, http-relay // New types: direct type = socks5; + // Specify interface for outgoing connections when 'direct' type + // is used. This is useful when you have multiple connections to + // internet or you have VPN connections. + // interface = tun0; + // Change this parameter to 1 if you want auto proxy feature. // When autoproxy is set to non-zero, the connection to target // will be made directly first. If direct connection to target diff --git a/redsocks.h b/redsocks.h index 2908dd9a..dd082e5e 100644 --- a/redsocks.h +++ b/redsocks.h @@ -21,6 +21,7 @@ typedef struct relay_subsys_t { void (*instance_fini)(struct redsocks_instance_t *instance); // connect_relay (if any) is called instead of redsocks_connect_relay after client connection acceptance void (*connect_relay)(struct redsocks_client_t *client); + //void (*relay_connected)(struct redsocks_client_t *client); } relay_subsys; typedef struct redsocks_config_t { @@ -34,6 +35,7 @@ typedef struct redsocks_config_t { uint16_t listenq; uint16_t autoproxy; uint16_t timeout; + char *interface;// interface of relay } redsocks_config; struct tracked_event { @@ -70,6 +72,7 @@ void redsocks_drop_client(redsocks_client *client); void redsocks_touch_client(redsocks_client *client); void redsocks_connect_relay(redsocks_client *client); int redsocks_start_relay(redsocks_client *client); +void redsocks_dump_client(redsocks_client * client); typedef int (*size_comparator)(size_t a, size_t b); int sizes_equal(size_t a, size_t b); diff --git a/redudp.c b/redudp.c index ca3afe5f..9378edfa 100644 --- a/redudp.c +++ b/redudp.c @@ -395,7 +395,7 @@ static void redudp_read_assoc_reply(struct bufferevent *buffev, void *_arg) goto fail; } - event_set(&client->udprelay, fd, EV_READ | EV_PERSIST, redudp_pkt_from_socks, client); + event_assign(&client->udprelay, get_event_base(), fd, EV_READ | EV_PERSIST, redudp_pkt_from_socks, client); error = event_add(&client->udprelay, NULL); if (error) { redudp_log_errno(client, LOG_ERR, "event_add"); @@ -557,7 +557,7 @@ static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_ client->sender_fd = -1; // it's postponed until socks-server replies to avoid trivial DoS - client->relay = red_connect_relay(&client->instance->config.relayaddr, + client->relay = red_connect_relay(&client->instance->config.relayaddr, NULL, redudp_relay_connected, redudp_relay_error, client); if (!client->relay) goto fail; @@ -819,7 +819,7 @@ static int redudp_init_instance(redudp_instance *instance) goto fail; } - event_set(&instance->listener, fd, EV_READ | EV_PERSIST, redudp_pkt_from_client, instance); + event_assign(&instance->listener, get_event_base(), fd, EV_READ | EV_PERSIST, redudp_pkt_from_client, instance); error = event_add(&instance->listener, NULL); if (error) { log_errno(LOG_ERR, "event_add"); diff --git a/utils.c b/utils.c index fef22eb4..d19f9ed9 100644 --- a/utils.c +++ b/utils.c @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ - +#include #include #include #include @@ -22,6 +22,7 @@ #include #include #include +#include "main.h" #include "log.h" #include "utils.h" #include "redsocks.h" // for redsocks_close @@ -32,208 +33,242 @@ int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr, struct sockaddr_in *toaddr) { - socklen_t addrlen = sizeof(*inaddr); - ssize_t pktlen; - struct msghdr msg; - struct iovec io; - char control[1024]; - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = inaddr; - msg.msg_namelen = sizeof(*inaddr); - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - io.iov_base = buf; - io.iov_len = buflen; - - pktlen = recvmsg(fd, &msg, 0); - if (pktlen == -1) { - log_errno(LOG_WARNING, "recvfrom"); - return -1; - } - - if (toaddr) { - memset(toaddr, 0, sizeof(*toaddr)); - for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if ( - cmsg->cmsg_level == SOL_IP && - cmsg->cmsg_type == IP_ORIGDSTADDR && - cmsg->cmsg_len >= CMSG_LEN(sizeof(*toaddr)) - ) { - struct sockaddr_in* cmsgaddr = (struct sockaddr_in*)CMSG_DATA(cmsg); - char buf[RED_INET_ADDRSTRLEN]; - log_error(LOG_DEBUG, "IP_ORIGDSTADDR: %s", red_inet_ntop(cmsgaddr, buf, sizeof(buf))); - memcpy(toaddr, cmsgaddr, sizeof(*toaddr)); - } - else { - log_error(LOG_WARNING, "unexepcted cmsg (level,type) = (%d,%d)", - cmsg->cmsg_level, cmsg->cmsg_type); - } - } - if (toaddr->sin_family != AF_INET) { - log_error(LOG_WARNING, "(SOL_IP, IP_ORIGDSTADDR) not found"); - return -1; - } - } - - if (addrlen != sizeof(*inaddr)) { - log_error(LOG_WARNING, "unexpected address length %u instead of %zu", addrlen, sizeof(*inaddr)); - return -1; - } - - if (pktlen >= buflen) { - char buf[RED_INET_ADDRSTRLEN]; - log_error(LOG_WARNING, "wow! Truncated udp packet of size %zd from %s! impossible! dropping it...", - pktlen, red_inet_ntop(inaddr, buf, sizeof(buf))); - return -1; - } - - return pktlen; + socklen_t addrlen = sizeof(*inaddr); + ssize_t pktlen; + struct msghdr msg; + struct iovec io; + char control[1024]; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = inaddr; + msg.msg_namelen = sizeof(*inaddr); + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + io.iov_base = buf; + io.iov_len = buflen; + + pktlen = recvmsg(fd, &msg, 0); + if (pktlen == -1) { + log_errno(LOG_WARNING, "recvfrom"); + return -1; + } + + if (toaddr) { + memset(toaddr, 0, sizeof(*toaddr)); + for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if ( + cmsg->cmsg_level == SOL_IP && + cmsg->cmsg_type == IP_ORIGDSTADDR && + cmsg->cmsg_len >= CMSG_LEN(sizeof(*toaddr)) + ) { + struct sockaddr_in* cmsgaddr = (struct sockaddr_in*)CMSG_DATA(cmsg); + char buf[RED_INET_ADDRSTRLEN]; + log_error(LOG_DEBUG, "IP_ORIGDSTADDR: %s", red_inet_ntop(cmsgaddr, buf, sizeof(buf))); + memcpy(toaddr, cmsgaddr, sizeof(*toaddr)); + } + else { + log_error(LOG_WARNING, "unexepcted cmsg (level,type) = (%d,%d)", + cmsg->cmsg_level, cmsg->cmsg_type); + } + } + if (toaddr->sin_family != AF_INET) { + log_error(LOG_WARNING, "(SOL_IP, IP_ORIGDSTADDR) not found"); + return -1; + } + } + + if (addrlen != sizeof(*inaddr)) { + log_error(LOG_WARNING, "unexpected address length %u instead of %zu", addrlen, sizeof(*inaddr)); + return -1; + } + + if (pktlen >= buflen) { + char buf[RED_INET_ADDRSTRLEN]; + log_error(LOG_WARNING, "wow! Truncated udp packet of size %zd from %s! impossible! dropping it...", + pktlen, red_inet_ntop(inaddr, buf, sizeof(buf))); + return -1; + } + + return pktlen; } time_t redsocks_time(time_t *t) { - time_t retval; - retval = time(t); - if (retval == ((time_t) -1)) - log_errno(LOG_WARNING, "time"); - return retval; + time_t retval; + retval = time(t); + if (retval == ((time_t) -1)) + log_errno(LOG_WARNING, "time"); + return retval; } char *redsocks_evbuffer_readline(struct evbuffer *buf) { #if _EVENT_NUMERIC_VERSION >= 0x02000000 - return evbuffer_readln(buf, NULL, EVBUFFER_EOL_CRLF); + return evbuffer_readln(buf, NULL, EVBUFFER_EOL_CRLF); #else - return evbuffer_readline(buf); + return evbuffer_readline(buf); #endif } -struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg) +struct bufferevent* red_connect_relay_if(const char *ifname, + struct sockaddr_in *addr, + evbuffercb readcb, + evbuffercb writecb, + everrorcb errorcb, + void *cbarg) { - struct bufferevent *retval = NULL; - int on = 1; - int relay_fd = -1; - int error; - - relay_fd = socket(AF_INET, SOCK_STREAM, 0); - if (relay_fd == -1) { - log_errno(LOG_ERR, "socket"); - goto fail; - } - - error = fcntl_nonblock(relay_fd); - if (error) { - log_errno(LOG_ERR, "fcntl"); - goto fail; - } - - error = setsockopt(relay_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); - if (error) { - log_errno(LOG_WARNING, "setsockopt"); - goto fail; - } - - retval = bufferevent_new(relay_fd, NULL, writecb, errorcb, cbarg); - if (!retval) { - log_errno(LOG_ERR, "bufferevent_new"); - goto fail; - } - - error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... - if (error) { - log_errno(LOG_ERR, "bufferevent_enable"); - goto fail; - } - - error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); - if (error && errno != EINPROGRESS) { - log_errno(LOG_NOTICE, "connect"); - goto fail; - } - - return retval; + struct bufferevent *retval = NULL; + int on = 1; + int relay_fd = -1; + int error; + + relay_fd = socket(AF_INET, SOCK_STREAM, 0); + if (relay_fd == -1) { + log_errno(LOG_ERR, "socket"); + goto fail; + } + + if (ifname) { + error = setsockopt(relay_fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); + if (error) { + log_errno(LOG_ERR, "bind"); + goto fail; + } + } + + error = evutil_make_socket_nonblocking(relay_fd); + if (error) { + log_errno(LOG_ERR, "fcntl"); + goto fail; + } + + error = setsockopt(relay_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); + if (error) { + log_errno(LOG_WARNING, "setsockopt"); + goto fail; + } + + retval = bufferevent_socket_new(get_event_base(), relay_fd, 0); + if (!retval) { + log_errno(LOG_ERR, "bufferevent_socket_new"); + goto fail; + } + + bufferevent_setcb(retval, readcb, writecb, errorcb, cbarg); + error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... + if (error) { + log_errno(LOG_ERR, "bufferevent_enable"); + goto fail; + } + +// error = bufferevent_socket_connect(retval, (struct sockaddr*)addr, sizeof(*addr)); +// if (error) { + error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); + if (error && errno != EINPROGRESS) { + log_errno(LOG_NOTICE, "bufferevent_socket_connect"); + goto fail; + } + + return retval; fail: - if (relay_fd != -1) - redsocks_close(relay_fd); - if (retval) - bufferevent_free(retval); - return NULL; + if (relay_fd != -1) + redsocks_close(relay_fd); + if (retval) + bufferevent_free(retval); + return NULL; +} + + +struct bufferevent* red_connect_relay(struct sockaddr_in *addr, + evbuffercb readcb, + evbuffercb writecb, + everrorcb errorcb, + void *cbarg) +{ + return red_connect_relay_if(NULL, addr, readcb, writecb, errorcb, cbarg); } -struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg, const struct timeval *timeout_write) +struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, + evbuffercb readcb, + evbuffercb writecb, + everrorcb errorcb, + void *cbarg, + const struct timeval *timeout_write) { - struct bufferevent *retval = NULL; - int on = 1; - int relay_fd = -1; - int error; - - relay_fd = socket(AF_INET, SOCK_STREAM, 0); - if (relay_fd == -1) { - log_errno(LOG_ERR, "socket"); - goto fail; - } - - error = fcntl_nonblock(relay_fd); - if (error) { - log_errno(LOG_ERR, "fcntl"); - goto fail; - } - - error = setsockopt(relay_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); - if (error) { - log_errno(LOG_WARNING, "setsockopt"); - goto fail; - } - - retval = bufferevent_new(relay_fd, NULL, writecb, errorcb, cbarg); - if (!retval) { - log_errno(LOG_ERR, "bufferevent_new"); - goto fail; - } - - error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... - if (error) { - log_errno(LOG_ERR, "bufferevent_enable"); - goto fail; - } - - bufferevent_set_timeouts(retval, NULL, timeout_write); - - error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); - if (error && errno != EINPROGRESS) { - log_errno(LOG_NOTICE, "connect"); - goto fail; - } - - return retval; + struct bufferevent *retval = NULL; + int on = 1; + int relay_fd = -1; + int error; + + relay_fd = socket(AF_INET, SOCK_STREAM, 0); + if (relay_fd == -1) { + log_errno(LOG_ERR, "socket"); + goto fail; + } + + error = evutil_make_socket_nonblocking(relay_fd); + if (error) { + log_errno(LOG_ERR, "fcntl"); + goto fail; + } + + error = setsockopt(relay_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); + if (error) { + log_errno(LOG_WARNING, "setsockopt"); + goto fail; + } + + retval = bufferevent_socket_new(get_event_base(), relay_fd, 0); + if (!retval) { + log_errno(LOG_ERR, "bufferevent_socket_new"); + goto fail; + } + + bufferevent_setcb(retval, readcb, writecb, errorcb, cbarg); + + error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... + if (error) { + log_errno(LOG_ERR, "bufferevent_enable"); + goto fail; + } + bufferevent_set_timeouts(retval, NULL, timeout_write); + +// error = bufferevent_socket_connect(retval, (struct sockaddr*)addr, sizeof(*addr)); +// if (error) { + error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); + if (error && errno != EINPROGRESS) { + log_errno(LOG_NOTICE, "bufferevent_socket_connect"); + goto fail; + } + + return retval; fail: - if (relay_fd != -1) - redsocks_close(relay_fd); - if (retval) - bufferevent_free(retval); - return NULL; + if (relay_fd != -1) + redsocks_close(relay_fd); + if (retval) + bufferevent_free(retval); + return NULL; } int red_socket_geterrno(struct bufferevent *buffev) { - int error; - int pseudo_errno; - socklen_t optlen = sizeof(pseudo_errno); - - assert(EVENT_FD(&buffev->ev_read) == EVENT_FD(&buffev->ev_write)); - - error = getsockopt(EVENT_FD(&buffev->ev_read), SOL_SOCKET, SO_ERROR, &pseudo_errno, &optlen); - if (error) { - log_errno(LOG_ERR, "getsockopt"); - return -1; - } - return pseudo_errno; + int error; + int pseudo_errno; + socklen_t optlen = sizeof(pseudo_errno); + + assert(EVENT_FD(&buffev->ev_read) == EVENT_FD(&buffev->ev_write)); + + error = getsockopt(EVENT_FD(&buffev->ev_read), SOL_SOCKET, SO_ERROR, &pseudo_errno, &optlen); + if (error) { + log_errno(LOG_ERR, "getsockopt"); + return -1; + } + return pseudo_errno; } /** simple fcntl(2) wrapper, provides errno and all logging to caller @@ -242,64 +277,64 @@ int red_socket_geterrno(struct bufferevent *buffev) */ int fcntl_nonblock(int fd) { - int error; - int flags; + int error; + int flags; - flags = fcntl(fd, F_GETFL); - if (flags == -1) - return -1; + flags = fcntl(fd, F_GETFL); + if (flags == -1) + return -1; - error = fcntl(fd, F_SETFL, flags | O_NONBLOCK); - if (error) - return -1; + error = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (error) + return -1; - return 0; + return 0; } int red_is_socket_connected_ok(struct bufferevent *buffev) { - int pseudo_errno = red_socket_geterrno(buffev); - - if (pseudo_errno == -1) { - return 0; - } - else if (pseudo_errno) { - errno = pseudo_errno; - log_errno(LOG_NOTICE, "connect"); - return 0; - } - else { - return 1; - } + int pseudo_errno = red_socket_geterrno(buffev); + + if (pseudo_errno == -1) { + return 0; + } + else if (pseudo_errno) { + errno = pseudo_errno; + log_errno(LOG_NOTICE, "connect"); + return 0; + } + else { + return 1; + } } char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_size) { - const char *retval = 0; - size_t len = 0; - uint16_t port; - const char placeholder[] = "???:???"; - - assert(buffer_size >= sizeof(placeholder)); - - memset(buffer, 0, buffer_size); - if (sa->sin_family == AF_INET) { - retval = inet_ntop(AF_INET, &sa->sin_addr, buffer, buffer_size); - port = ((struct sockaddr_in*)sa)->sin_port; - } - else if (sa->sin_family == AF_INET6) { - retval = inet_ntop(AF_INET6, &((const struct sockaddr_in6*)sa)->sin6_addr, buffer, buffer_size); - port = ((struct sockaddr_in6*)sa)->sin6_port; - } - if (retval) { - assert(retval == buffer); - len = strlen(retval); - snprintf(buffer + len, buffer_size - len, ":%d", ntohs(port)); - } - else { - strcpy(buffer, placeholder); - } - return buffer; + const char *retval = 0; + size_t len = 0; + uint16_t port; + const char placeholder[] = "???:???"; + + assert(buffer_size >= sizeof(placeholder)); + + memset(buffer, 0, buffer_size); + if (sa->sin_family == AF_INET) { + retval = inet_ntop(AF_INET, &sa->sin_addr, buffer, buffer_size); + port = ((struct sockaddr_in*)sa)->sin_port; + } + else if (sa->sin_family == AF_INET6) { + retval = inet_ntop(AF_INET6, &((const struct sockaddr_in6*)sa)->sin6_addr, buffer, buffer_size); + port = ((struct sockaddr_in6*)sa)->sin6_port; + } + if (retval) { + assert(retval == buffer); + len = strlen(retval); + snprintf(buffer + len, buffer_size - len, ":%d", ntohs(port)); + } + else { + strcpy(buffer, placeholder); + } + return buffer; } /* copy event buffer from source to destination as much as possible. @@ -307,44 +342,44 @@ char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_si */ size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, size_t skip) { - int n, i; - size_t written = 0; - struct evbuffer_iovec *v; - struct evbuffer_iovec quick_v[5];/* a vector with 5 elements is usually enough */ - - size_t maxlen = dst->wm_write.high - EVBUFFER_LENGTH(dst->output); - maxlen = EVBUFFER_LENGTH(src->input) - skip> maxlen?maxlen: EVBUFFER_LENGTH(src->input)-skip; - - n = evbuffer_peek(src->input, maxlen+skip, NULL, NULL, 0); - if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) - v = malloc(sizeof(struct evbuffer_iovec)*n); - else - v = quick_v; - n = evbuffer_peek(src->input, maxlen+skip, NULL, v, n); - for (i=0; iwm_write.high - EVBUFFER_LENGTH(dst->output); + maxlen = EVBUFFER_LENGTH(src->input) - skip> maxlen?maxlen: EVBUFFER_LENGTH(src->input)-skip; + + n = evbuffer_peek(src->input, maxlen+skip, NULL, NULL, 0); + if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) + v = malloc(sizeof(struct evbuffer_iovec)*n); + else + v = quick_v; + n = evbuffer_peek(src->input, maxlen+skip, NULL, v, n); + for (i=0; i= len) - { - skip -= len; - continue; - } - else - { - len -= skip; - } + if (skip >= len) + { + skip -= len; + continue; + } + else + { + len -= skip; + } if (written + len > maxlen) len = maxlen - written; - if (bufferevent_write(dst, v[i].iov_base+skip, len)) + if (bufferevent_write(dst, v[i].iov_base+skip, len)) break; - skip = 0; + skip = 0; /* We keep track of the bytes written separately; if we don't, - * we may write more than we need if the last chunk puts - * us over the limit. */ + * we may write more than we need if the last chunk puts + * us over the limit. */ written += len; } - if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) - free(v); - return written; + if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) + free(v); + return written; } diff --git a/utils.h b/utils.h index aefaea91..1188b59d 100644 --- a/utils.h +++ b/utils.h @@ -41,8 +41,14 @@ struct sockaddr_in; time_t redsocks_time(time_t *t); char *redsocks_evbuffer_readline(struct evbuffer *buf); -struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg); -struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg, const struct timeval *timeout_write); +struct bufferevent* red_connect_relay_if(const char *ifname, + struct sockaddr_in *addr, + evbuffercb readcb, + evbuffercb writecb, + everrorcb errorcb, + void *cbarg); +struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg); +struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg, const struct timeval *timeout_write); int red_socket_geterrno(struct bufferevent *buffev); int red_is_socket_connected_ok(struct bufferevent *buffev); int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *fromaddr, struct sockaddr_in *toaddr); @@ -51,14 +57,15 @@ int fcntl_nonblock(int fd); size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, size_t skip); -#define event_fmt_str "%s|%s|%s|%s|%s|0x%x" +#define event_fmt_str "%s|%s|%s|%s|%s|%s|0x%x" #define event_fmt(what) \ - (what) & EVBUFFER_READ ? "EVBUFFER_READ" : "0", \ - (what) & EVBUFFER_WRITE ? "EVBUFFER_WRITE" : "0", \ - (what) & EVBUFFER_EOF ? "EVBUFFER_EOF" : "0", \ - (what) & EVBUFFER_ERROR ? "EVBUFFER_ERROR" : "0", \ - (what) & EVBUFFER_TIMEOUT ? "EVBUFFER_TIMEOUT" : "0", \ - (what) & ~(EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF|EVBUFFER_ERROR|EVBUFFER_TIMEOUT) + (what) & BEV_EVENT_READING ? "READING" : "0", \ + (what) & BEV_EVENT_WRITING ? "WRITING" : "0", \ + (what) & BEV_EVENT_EOF ? "EOF" : "0", \ + (what) & BEV_EVENT_ERROR ? "ERROR" : "0", \ + (what) & BEV_EVENT_TIMEOUT ? "TIMEOUT" : "0", \ + (what) & BEV_EVENT_CONNECTED ? "CONNECTED" : "0", \ + (what) & ~(BEV_EVENT_READING|BEV_EVENT_WRITING|BEV_EVENT_EOF|BEV_EVENT_ERROR|BEV_EVENT_TIMEOUT|BEV_EVENT_CONNECTED) #if INET6_ADDRSTRLEN < INET_ADDRSTRLEN # error Impossible happens: INET6_ADDRSTRLEN < INET_ADDRSTRLEN From 40178d345faab8ebff0fc38415faa8e48d453a8b Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 31 Dec 2014 12:48:53 +0800 Subject: [PATCH 032/192] Minor fixes. --- README.md | 6 ++---- autoproxy.c | 2 ++ base.c | 41 ++++++++++++----------------------------- redsocks.c | 2 +- 4 files changed, 17 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 6f3d0a57..cabdef62 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ REDSOCKS2 ========= This is a modified version of original redsocks. -The name is changed to be REDSOCKS2 since this release to distinguish -with original redsocks. +The name is changed to REDSOCKS2 to distinguish with original redsocks. This variant is useful for anti-GFW (Great Fire Wall). HOW it works @@ -20,7 +19,6 @@ read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how to cross compile. ##Note: -Method 'autosocks5' and 'autohttp-connect' are removed. To use the autoproxy feature, please change the redsocks section in configuration file like this: @@ -38,7 +36,7 @@ configuration file like this: // It specified timeout value when trying to connect to destination // directly. Default is 10 seconds. When it is set to 0, default // timeout value will be used. - // NOTE: decrease the timeout value may lead increase of chance for + // NOTE: decreasing the timeout value may lead increase of chance for // normal IP to be misjudged. timeout = 13; //type = http-connect; diff --git a/autoproxy.c b/autoproxy.c index 250cc47d..2dc13497 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -294,6 +294,8 @@ static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) /* No CONNECTION RESET error occur after sending data, good. */ auto_confirm_connection(client); } + if (0 == aclient->data_sent) + aclient->data_sent = input_size; } direct_relay_readcb_helper(client, client->client, client->relay); } diff --git a/base.c b/base.c index ad682e92..de7b841b 100644 --- a/base.c +++ b/base.c @@ -328,9 +328,6 @@ static void myproc() FILE * tmp = NULL; pid_t pid = -1; int stop = 0; - char * buf = NULL; - size_t len = 0; - ssize_t dsize ; struct sigaction sa ; char tmp_fname[128]; snprintf(tmp_fname, sizeof(tmp_fname), "/tmp/redtime-%d", getpid()); @@ -369,37 +366,23 @@ static void myproc() sleep(1); continue; } - sleep(1); + sleep(2); - tmp = fopen(tmp_fname, "r"); - if (tmp) - { - len = 0; - buf = NULL; - dsize =getline( &buf, &len, tmp); - if (dsize != -1) + tmp = fopen(tmp_fname, "r"); + if (tmp) { - last = atol(buf); - now = time(NULL); - if (now-last>10) + if (fscanf(tmp, "%ld", &last) > 0) { - kill(pid, SIGKILL); - sleep(1); - stop = 1; + now = time(NULL); + if (now-last>10) + { + kill(pid, SIGKILL); + sleep(1); + stop = 1; + } } + fclose(tmp); } - free(buf); - fclose(tmp); - } - else - { -/* - kill(pid, SIGKILL); - sleep(1); - stop = 1; -*/ - } - } } } diff --git a/redsocks.c b/redsocks.c index a3b16fa8..58211d26 100644 --- a/redsocks.c +++ b/redsocks.c @@ -890,7 +890,7 @@ static void redsocks_heartbeat(int sig, short what, void *_arg) tmp = fopen(tmp_fname, "w"); if (tmp) { - fprintf(tmp, "%d", now); + fprintf(tmp, "%ld ", (long int)now); fclose(tmp); } } From de412cca5026421e803ea551c9aa4509027ea60e Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 13 Feb 2015 13:56:46 +0800 Subject: [PATCH 033/192] Introduce transparent UDP to TCP DNS request redirector. --- Makefile | 2 +- README.md | 22 +++- dnstc.c | 259 ------------------------------------------ dnstc.h | 16 --- main.c | 4 +- reddns.c | 96 ---------------- redsocks.conf.example | 18 ++- 7 files changed, 36 insertions(+), 381 deletions(-) delete mode 100644 dnstc.c delete mode 100644 dnstc.h delete mode 100644 reddns.c diff --git a/Makefile b/Makefile index e17bc3c4..8ea32ee5 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -OBJS := parser.o main.o redsocks.o log.o direct.o autoproxy.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o direct.o autoproxy.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o tcpdns.o gen/version.o SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend diff --git a/README.md b/README.md index cabdef62..00f13726 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Since this variant of redsocks is customized for running with Openwrt, please read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how to cross compile. -##Note: +###Note: To use the autoproxy feature, please change the redsocks section in configuration file like this: @@ -94,6 +94,26 @@ The configuration for forwarding connections to GoAgent is like below: timeout = 13; } +##Redirect UPD based DNS Request via TCP connection +Sending DNS request via TCP connection is one way to prevent from DNS +poisoning. You can redirect all UDP based DNS requests via TCP connection +with the following config section. + + tcpdns { + // Transform UDP DNS requests into TCP DNS requests. + // You can also redirect connections to external TCP DNS server to + // REDSOCKS transparent proxy via iptables. + local_ip = 192.168.1.1; // Local server to act as DNS server + local_port = 1053; // UDP port to receive UDP DNS requests + tcpdns1 = 8.8.4.4; // DNS server that supports TCP DNS requests + tcpdns2 = 8.8.8.8; // DNS server that supports TCP DNS requests + timeout = 4; // Timeout value for TCP DNS requests + } + +Then, you can either redirect all your DNS requests to the local IP:port +configured above by iptables, or just change your system default DNS upstream +server as the local IP:port configured above. + AUTHOR ------ Zhuofei Wang diff --git a/dnstc.c b/dnstc.c deleted file mode 100644 index 5f9feddf..00000000 --- a/dnstc.c +++ /dev/null @@ -1,259 +0,0 @@ -/* redsocks - transparent TCP-to-proxy redirector - * Copyright (C) 2007-2011 Leonid Evdokimov - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "list.h" -#include "log.h" -#include "parser.h" -#include "main.h" -#include "redsocks.h" -#include "dnstc.h" -#include "utils.h" - -#define dnstc_log_error(prio, msg...) \ - redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &clientaddr, &self->config.bindaddr, prio, ## msg) -#define dnstc_log_errno(prio, msg...) \ - redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &clientaddr, &self->config.bindaddr, prio, ## msg) - -static void dnstc_fini_instance(dnstc_instance *instance); -static int dnstc_fini(); - -typedef struct dns_header_t { - uint16_t id; - uint8_t qr_opcode_aa_tc_rd; - uint8_t ra_z_rcode; - uint16_t qdcount; - uint16_t ancount; - uint16_t nscount; - uint16_t arcount; -} PACKED dns_header; - -#define DNS_QR 0x80 -#define DNS_TC 0x02 -#define DNS_Z 0x70 - -/*********************************************************************** - * Logic - */ -static void dnstc_pkt_from_client(int fd, short what, void *_arg) -{ - dnstc_instance *self = _arg; - struct sockaddr_in clientaddr; - union { - char raw[0xFFFF]; // UDP packet can't be larger then that - dns_header h; - } buf; - ssize_t pktlen, outgoing; - - assert(fd == EVENT_FD(&self->listener)); - pktlen = red_recv_udp_pkt(fd, buf.raw, sizeof(buf), &clientaddr, NULL); - if (pktlen == -1) - return; - - if (pktlen <= sizeof(dns_header)) { - dnstc_log_error(LOG_INFO, "incomplete DNS request"); - return; - } - - if (1 - && (buf.h.qr_opcode_aa_tc_rd & DNS_QR) == 0 /* query */ - && (buf.h.ra_z_rcode & DNS_Z) == 0 /* Z is Zero */ - && buf.h.qdcount /* some questions */ - && !buf.h.ancount && !buf.h.nscount && !buf.h.arcount /* no answers */ - ) { - buf.h.qr_opcode_aa_tc_rd |= DNS_QR; - buf.h.qr_opcode_aa_tc_rd |= DNS_TC; - outgoing = sendto(fd, buf.raw, pktlen, 0, - (struct sockaddr*)&clientaddr, sizeof(clientaddr)); - if (outgoing != pktlen) - dnstc_log_errno(LOG_WARNING, "sendto: I was sending %zd bytes, but only %zd were sent.", - pktlen, outgoing); - else - dnstc_log_error(LOG_INFO, "sent truncated DNS reply"); - } - else { - dnstc_log_error(LOG_INFO, "malformed DNS request"); - } -} - -/*********************************************************************** - * Init / shutdown - */ -static parser_entry dnstc_entries[] = -{ - { .key = "local_ip", .type = pt_in_addr }, - { .key = "local_port", .type = pt_uint16 }, - { } -}; - -static list_head instances = LIST_HEAD_INIT(instances); - -static int dnstc_onenter(parser_section *section) -{ - dnstc_instance *instance = calloc(1, sizeof(*instance)); - if (!instance) { - parser_error(section->context, "Not enough memory"); - return -1; - } - - INIT_LIST_HEAD(&instance->list); - instance->config.bindaddr.sin_family = AF_INET; - instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) - entry->addr = - (strcmp(entry->key, "local_ip") == 0) ? (void*)&instance->config.bindaddr.sin_addr : - (strcmp(entry->key, "local_port") == 0) ? (void*)&instance->config.bindaddr.sin_port : - NULL; - section->data = instance; - return 0; -} - -static int dnstc_onexit(parser_section *section) -{ - dnstc_instance *instance = section->data; - - section->data = NULL; - for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) - entry->addr = NULL; - - instance->config.bindaddr.sin_port = htons(instance->config.bindaddr.sin_port); - - list_add(&instance->list, &instances); - - return 0; -} - -static int dnstc_init_instance(dnstc_instance *instance) -{ - /* FIXME: dnstc_fini_instance is called in case of failure, this - * function will remove instance from instances list - result - * looks ugly. - */ - int error; - int fd = -1; - - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd == -1) { - log_errno(LOG_ERR, "socket"); - goto fail; - } - - error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); - if (error) { - log_errno(LOG_ERR, "bind"); - goto fail; - } - - error = fcntl_nonblock(fd); - if (error) { - log_errno(LOG_ERR, "fcntl"); - goto fail; - } - - event_set(&instance->listener, fd, EV_READ | EV_PERSIST, dnstc_pkt_from_client, instance); - error = event_add(&instance->listener, NULL); - if (error) { - log_errno(LOG_ERR, "event_add"); - goto fail; - } - - return 0; - -fail: - dnstc_fini_instance(instance); - - if (fd != -1) { - if (close(fd) != 0) - log_errno(LOG_WARNING, "close"); - } - - return -1; -} - -/* Drops instance completely, freeing its memory and removing from - * instances list. - */ -static void dnstc_fini_instance(dnstc_instance *instance) -{ - if (event_initialized(&instance->listener)) { - if (event_del(&instance->listener) != 0) - log_errno(LOG_WARNING, "event_del"); - if (close(EVENT_FD(&instance->listener)) != 0) - log_errno(LOG_WARNING, "close"); - memset(&instance->listener, 0, sizeof(instance->listener)); - } - - list_del(&instance->list); - - memset(instance, 0, sizeof(*instance)); - free(instance); -} - -static int dnstc_init() -{ - dnstc_instance *tmp, *instance = NULL; - - // TODO: init debug_dumper - - list_for_each_entry_safe(instance, tmp, &instances, list) { - if (dnstc_init_instance(instance) != 0) - goto fail; - } - - return 0; - -fail: - dnstc_fini(); - return -1; -} - -static int dnstc_fini() -{ - dnstc_instance *tmp, *instance = NULL; - - list_for_each_entry_safe(instance, tmp, &instances, list) - dnstc_fini_instance(instance); - - return 0; -} - -static parser_section dnstc_conf_section = -{ - .name = "dnstc", - .entries = dnstc_entries, - .onenter = dnstc_onenter, - .onexit = dnstc_onexit -}; - -app_subsys dnstc_subsys = -{ - .init = dnstc_init, - .fini = dnstc_fini, - .conf_section = &dnstc_conf_section, -}; - -/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ -/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/dnstc.h b/dnstc.h deleted file mode 100644 index 8aaf28be..00000000 --- a/dnstc.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef DNSTC_H -#define DNSTC_H - -typedef struct dnstc_config_t { - struct sockaddr_in bindaddr; -} dnstc_config; - -typedef struct dnstc_instance_t { - list_head list; - dnstc_config config; - struct event listener; -} dnstc_instance; - -/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ -/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ -#endif /* REDUDP_H */ diff --git a/main.c b/main.c index 168a2ee6..023feb7f 100644 --- a/main.c +++ b/main.c @@ -30,13 +30,13 @@ extern app_subsys redsocks_subsys; extern app_subsys base_subsys; extern app_subsys redudp_subsys; -extern app_subsys dnstc_subsys; +extern app_subsys tcpdns_subsys; app_subsys *subsystems[] = { &redsocks_subsys, &base_subsys, &redudp_subsys, - &dnstc_subsys, + &tcpdns_subsys, }; static const char *confname = "redsocks.conf"; diff --git a/reddns.c b/reddns.c deleted file mode 100644 index 1ca12ab1..00000000 --- a/reddns.c +++ /dev/null @@ -1,96 +0,0 @@ -/* redsocks - transparent TCP-to-proxy redirector - * Copyright (C) 2007-2011 Leonid Evdokimov - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include -#include "parser.h" -#include "main.h" - -typedef struct reddns_config_t reddns_config; -struct reddns_config_t { - struct sockaddr_in bindaddr; - struct sockaddr_in relayaddr; - struct in_addr fakenet; - struct in_addr fakenetmask; -}; - -int reddns_onenter(parser_section *section) -{ - reddns_config *conf = calloc(1, sizeof(reddns_config)); - section->data = conf; - section->entries[0].addr = &conf->bindaddr.sin_port; - section->entries[1].addr = &conf->fakenet; - section->entries[2].addr = &conf->fakenetmask; - section->entries[3].addr = &conf->relayaddr.sin_addr; - section->entries[4].addr = &conf->relayaddr.sin_port; - fprintf(stderr, "%s\n", __FUNCTION__); - return 0; -} - -int reddns_onexit(parser_section *section) -{ - reddns_config *conf = section->data; - fprintf(stderr, - "%s {\n" - "local_port = %u;\n" - "fakeip_net = %s/%s;\n" - "ip = %s;\n" - "port = %u;\n" - "}\n", - __FUNCTION__, - conf->bindaddr.sin_port, - strdup(inet_ntoa(conf->fakenet)), - strdup(inet_ntoa(conf->fakenetmask)), - strdup(inet_ntoa(conf->relayaddr.sin_addr)), - conf->relayaddr.sin_port - ); - return 0; -} - -parser_entry reddns_entries[] = { - { .key = "local_port", .type = pt_uint16 }, - { .key = "fakeip_net", .type = pt_in_addr2 }, - { .key = "fakeip_netmask", .type = pt_in_addr }, - { .key = "ip", .type = pt_in_addr }, - { .key = "port", .type = pt_uint16 }, - { } -}; - -parser_section reddns_conf_section = { - .name = "reddns", - .entries = reddns_entries, - .onenter = reddns_onenter, - .onexit = reddns_onexit -}; - -int reddns_init() { -#error It's non-working stub at the moment. - return 0; -} - -int reddns_fini() { - return 0; -} - -app_subsys reddns_subsys = { - .init = reddns_init, - .fini = reddns_fini, - .conf_section = &reddns_conf_section, -}; - -/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ -/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/redsocks.conf.example b/redsocks.conf.example index 0aa28926..9b0644e8 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -112,12 +112,18 @@ redudp { udp_timeout_stream = 180; } -dnstc { - // fake and really dumb DNS server that returns "truncated answer" to - // every query via UDP, RFC-compliant resolver should repeat same query - // via TCP in this case. - local_ip = 127.0.0.1; - local_port = 5300; +tcpdns { + // Transform UDP DNS requests into TCP DNS requests. + // You can also redirect connections to external TCP DNS server to + // REDSOCKS transparent proxy via iptables. + local_ip = 192.168.1.1; // Local server to act as DNS server + local_port = 1053; // UDP port to receive UDP DNS requests + tcpdns1 = 8.8.4.4; // DNS server that supports TCP DNS requests + tcpdns2 = 8.8.8.8; // DNS server that supports TCP DNS requests + timeout = 4; // Timeout value for TCP DNS requests } + // you can add more `redsocks' and `redudp' sections if you need. + + From df76a1754971800640539f6b3ed9567b838991f5 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 13 Feb 2015 16:46:36 +0800 Subject: [PATCH 034/192] Add missing files and bug fix. --- redsocks.c | 4 + tcpdns.c | 505 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tcpdns.h | 53 ++++++ 3 files changed, 562 insertions(+) create mode 100644 tcpdns.c create mode 100644 tcpdns.h diff --git a/redsocks.c b/redsocks.c index 58211d26..57cb39fe 100644 --- a/redsocks.c +++ b/redsocks.c @@ -590,6 +590,10 @@ void redsocks_relay_connected(struct bufferevent *buffev, void *_arg) goto fail; } + /* We do not need to detect timeouts any more. + The two peers will handle it. */ + bufferevent_set_timeouts(client->relay, NULL, NULL); + bufferevent_setcb(client->relay, client->instance->relay_ss->readcb, client->instance->relay_ss->writecb, redsocks_event_error, diff --git a/tcpdns.c b/tcpdns.c new file mode 100644 index 00000000..2a3a25a5 --- /dev/null +++ b/tcpdns.c @@ -0,0 +1,505 @@ +/* redsocks2 - transparent TCP-to-proxy redirector + * Copyright (C) 2013-2014 Zhuofei Wang + * + * This code is based on redsocks project developed by Leonid Evdokimov. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "log.h" +#include "parser.h" +#include "main.h" +#include "redsocks.h" +#include "tcpdns.h" +#include "utils.h" + +#define tcpdns_log_error(prio, msg...) \ + redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &req->client_addr, &req->instance->config.bindaddr, prio, ## msg) +#define tcpdns_log_errno(prio, msg...) \ + redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &req->client_addr, &req->instance->config.bindaddr, prio, ## msg) + +static void tcpdns_fini_instance(tcpdns_instance *instance); +static int tcpdns_fini(); + +#define DNS_QR 0x80 +#define DNS_TC 0x02 +#define DNS_Z 0x70 +#define DEFAULT_TIMEOUT_SECONDS 4 + +#define FLAG_TCP_TEST 0x01 +#define FLAG_UDP_TEST 0x02 + +typedef enum tcpdns_state_t { + STATE_NEW, + STATE_REQUEST_SENT, + STATE_RECV_RESPONSE, + STATE_RESPONSE_SENT, + STATE_CONNECTION_TIMEOUT, +} tcpdns_state; + + +/*********************************************************************** + * Logic + */ +static void tcpdns_drop_request(dns_request * req) +{ + tcpdns_log_error(LOG_DEBUG, "dropping request"); + if (req->resolver) + { + close(bufferevent_getfd(req->resolver)); + bufferevent_free(req->resolver); + } + + if (req->delay && req->state != STATE_RESPONSE_SENT) + { + * req->delay = req->state == STATE_CONNECTION_TIMEOUT ? -1 : 0; + } + + list_del(&req->list); + free(req); +} + +static void tcpdns_readcb(struct bufferevent *from, void *_arg) +{ + dns_request * req = _arg; + union { + short len; + char raw[4096]; + } buff; + struct timeval tv; + assert(from == req->resolver); + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + size_t read_size; + + tcpdns_log_error(LOG_DEBUG, "response size: %d", input_size); + + if (input_size == 0 || input_size > sizeof(buff)) + // EOF or response is too large. Drop it. + goto finish; + + if (req->state == STATE_REQUEST_SENT + && input_size > 2 // At least length indicator is received + ) + { + // FIXME: + // suppose we got all data in one read + read_size = bufferevent_read(from, &buff, sizeof(buff)); + if (read_size > 2) + { + int fd = EVENT_FD(&req->instance->listener); + sendto(fd, &buff.raw[2], ntohs(buff.len), 0, + (struct sockaddr*)&req->client_addr, sizeof(req->client_addr)); + req->state = STATE_RESPONSE_SENT; + // calculate and update DNS resolver's delay + if (req->delay) + { + gettimeofday(&tv, 0); + timersub(&tv, &req->req_time, &tv); + * req->delay = tv.tv_sec*1000+tv.tv_usec/1000; + } + } + } +finish: + tcpdns_drop_request(req); +} + + +static void tcpdns_connected(struct bufferevent *buffev, void *_arg) +{ + dns_request * req = _arg; + assert(buffev == req->resolver); + struct timeval tv; + + if (!red_is_socket_connected_ok(buffev)) + { + tcpdns_log_error(LOG_DEBUG, "failed to connect to destination"); + tcpdns_drop_request(req); + return; + } + + if (req->state != STATE_NEW) + return; + + // Write dns request to DNS resolver and shutdown connection + uint16_t len = htons((uint16_t)req->data_len); + if (bufferevent_write(buffev, &len, sizeof(uint16_t)) == -1 + || bufferevent_write(buffev, &req->data.raw, req->data_len) == -1) + { + tcpdns_log_errno(LOG_ERR, "bufferevent_write_buffer"); + tcpdns_drop_request(req); + return; + } + + // Set timeout for read with time left since connection setup. + gettimeofday(&tv, 0); + timersub(&tv, &req->req_time, &tv); + bufferevent_set_timeouts(buffev, &tv, NULL); + bufferevent_enable(buffev, EV_READ); + req->state = STATE_REQUEST_SENT; +} + + +static void tcpdns_event_error(struct bufferevent *buffev, short what, void *_arg) +{ + dns_request * req = _arg; + int saved_errno = errno; + assert(buffev == req->resolver); + + tcpdns_log_errno(LOG_DEBUG, "errno(%d), what: " event_fmt_str, + saved_errno, event_fmt(what)); + + if (req->state == STATE_NEW + && what == (BEV_EVENT_WRITING | BEV_EVENT_TIMEOUT)) + { + req->state = STATE_CONNECTION_TIMEOUT; + } + tcpdns_drop_request(req); +} + +static struct sockaddr_in * choose_tcpdns(tcpdns_instance * instance, int **delay) +{ + static int n = 0; + log_error(LOG_DEBUG, "Dealy of TCP DNS resolvers: %d, %d", instance->tcp1_delay_ms, instance->tcp2_delay_ms); + if (instance->config.tcpdns1_addr.sin_addr.s_addr != htonl(INADDR_ANY) + && (instance->config.tcpdns2_addr.sin_addr.s_addr != htonl(INADDR_ANY)) + ) + { + if (instance->tcp1_delay_ms <= 0 + && instance->tcp2_delay_ms <= 0) + { + // choose one + n += 1; + if (n%2) + goto return_tcp1; + else + goto return_tcp2; + } + if (instance->tcp1_delay_ms > instance->tcp2_delay_ms) + { + if (instance->tcp2_delay_ms < 0) + goto return_tcp1; + else + goto return_tcp2; + } + else + { + if (instance->tcp1_delay_ms < 0) + goto return_tcp2; + else + goto return_tcp1; + } + } + if (instance->config.tcpdns1_addr.sin_addr.s_addr != htonl(INADDR_ANY)) + goto return_tcp1; + if (instance->config.tcpdns2_addr.sin_addr.s_addr != htonl(INADDR_ANY)) + goto return_tcp2; + + * delay = NULL; + return NULL; + +return_tcp1: + * delay = &instance->tcp1_delay_ms; + return &instance->config.tcpdns1_addr; + +return_tcp2: + * delay = &instance->tcp2_delay_ms; + return &instance->config.tcpdns2_addr; + +} + +static void tcpdns_pkt_from_client(int fd, short what, void *_arg) +{ + tcpdns_instance *self = _arg; + dns_request * req = NULL; + struct timeval tv; + struct sockaddr_in * destaddr; + ssize_t pktlen; + + assert(fd == EVENT_FD(&self->listener)); + /* allocate and initialize request structure */ + req = (dns_request *)calloc(sizeof(dns_request), 1); + if (!req) + { + log_error(LOG_INFO, "Out of memeory."); + return; + } + req->instance = self; + req->state = STATE_NEW; + gettimeofday(&req->req_time, 0); + pktlen = red_recv_udp_pkt(fd, req->data.raw, sizeof(req->data.raw), &req->client_addr, NULL); + if (pktlen == -1) + return; + if (pktlen <= sizeof(dns_header)) + { + tcpdns_log_error(LOG_INFO, "incomplete DNS request"); + return; + } + req->data_len = pktlen; + + if ( (req->data.header.qr_opcode_aa_tc_rd & DNS_QR) == 0 /* query */ + && (req->data.header.ra_z_rcode & DNS_Z) == 0 /* Z is Zero */ + && req->data.header.qdcount /* some questions */ + && !req->data.header.ancount && !req->data.header.nscount && !req->data.header.arcount /* no answers */ + ) + { + tv.tv_sec = DEFAULT_TIMEOUT_SECONDS; + tv.tv_usec = 0; + if (self->config.timeout>0) + tv.tv_sec = self->config.timeout; + + destaddr = choose_tcpdns(self, &req->delay); + if (!destaddr) + { + free(req); + tcpdns_log_error(LOG_INFO, "No valid DNS resolver configured"); + return; + } + /* connect to target directly without going through proxy */ + req->resolver = red_connect_relay2(destaddr, + tcpdns_readcb, tcpdns_connected, tcpdns_event_error, req, + &tv); + if (req->resolver) + list_add(&req->list, &self->requests); + else + { + free(req); + tcpdns_log_error(LOG_INFO, "Failed to setup connection to DNS resolver"); + } + } + else + { + free(req); + tcpdns_log_error(LOG_INFO, "malformed DNS request"); + } +} + +/*********************************************************************** + * DNS Resolver Delay Checking + */ +static void check_udpdns_delay() +{ +} + +static void check_tcpdns_delay() +{ +} + +static void check_dns_delay() +{ + check_udpdns_delay(); + check_tcpdns_delay(); +} + + +/*********************************************************************** + * Init / shutdown + */ +static parser_entry tcpdns_entries[] = +{ + { .key = "local_ip", .type = pt_in_addr }, + { .key = "local_port", .type = pt_uint16 }, + { .key = "tcpdns1", .type = pt_in_addr }, + { .key = "tcpdns2", .type = pt_in_addr }, + { .key = "timeout", .type = pt_uint16 }, + { } +}; + +static list_head instances = LIST_HEAD_INIT(instances); + +static int tcpdns_onenter(parser_section *section) +{ + tcpdns_instance *instance = calloc(1, sizeof(*instance)); + if (!instance) { + parser_error(section->context, "Not enough memory"); + return -1; + } + + INIT_LIST_HEAD(&instance->list); + INIT_LIST_HEAD(&instance->requests); + instance->config.bindaddr.sin_family = AF_INET; + instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + instance->config.udpdns1_addr.sin_family = AF_INET; + instance->config.udpdns1_addr.sin_addr.s_addr = htonl(INADDR_ANY); + instance->config.udpdns1_addr.sin_port = htons(53); + instance->config.udpdns2_addr.sin_family = AF_INET; + instance->config.udpdns2_addr.sin_addr.s_addr = htonl(INADDR_ANY); + instance->config.udpdns2_addr.sin_port = htons(53); + instance->config.tcpdns1_addr.sin_family = AF_INET; + instance->config.tcpdns1_addr.sin_addr.s_addr = htonl(INADDR_ANY); + instance->config.tcpdns1_addr.sin_port = htons(53); + instance->config.tcpdns2_addr.sin_family = AF_INET; + instance->config.tcpdns2_addr.sin_addr.s_addr = htonl(INADDR_ANY); + instance->config.tcpdns2_addr.sin_port = htons(53); + + for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) + entry->addr = + (strcmp(entry->key, "local_ip") == 0) ? (void*)&instance->config.bindaddr.sin_addr : + (strcmp(entry->key, "local_port") == 0) ? (void*)&instance->config.bindaddr.sin_port : + (strcmp(entry->key, "udpdns1") == 0) ? (void*)&instance->config.udpdns1_addr.sin_addr : + (strcmp(entry->key, "udpdns2") == 0) ? (void*)&instance->config.udpdns2_addr.sin_addr : + (strcmp(entry->key, "tcpdns1") == 0) ? (void*)&instance->config.tcpdns1_addr.sin_addr : + (strcmp(entry->key, "tcpdns2") == 0) ? (void*)&instance->config.tcpdns2_addr.sin_addr : + (strcmp(entry->key, "timeout") == 0) ? (void*)&instance->config.timeout : + NULL; + section->data = instance; + return 0; +} + +static int tcpdns_onexit(parser_section *section) +{ + const char *err = NULL; + tcpdns_instance *instance = section->data; + + section->data = NULL; + for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) + entry->addr = NULL; + + if (instance->config.bindaddr.sin_port == 0) + err = "Local port must be configured"; + else + instance->config.bindaddr.sin_port = htons(instance->config.bindaddr.sin_port); + + if (instance->config.tcpdns1_addr.sin_addr.s_addr == htonl(INADDR_ANY) + && instance->config.tcpdns2_addr.sin_addr.s_addr == htonl(INADDR_ANY)) + err = "At least one TCP DNS resolver must be configured."; + + if (err) + parser_error(section->context, err); + else + list_add(&instance->list, &instances); + + return err ? -1 : 0; +} + +static int tcpdns_init_instance(tcpdns_instance *instance) +{ + /* FIXME: tcpdns_fini_instance is called in case of failure, this + * function will remove instance from instances list - result + * looks ugly. + */ + int error; + int fd = -1; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + log_errno(LOG_ERR, "socket"); + goto fail; + } + + error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); + if (error) { + log_errno(LOG_ERR, "bind"); + goto fail; + } + + error = evutil_make_socket_nonblocking(fd); + if (error) { + log_errno(LOG_ERR, "evutil_make_socket_nonblocking"); + goto fail; + } + + event_assign(&instance->listener, get_event_base(), fd, EV_READ | EV_PERSIST, tcpdns_pkt_from_client, instance); + error = event_add(&instance->listener, NULL); + if (error) + { + log_errno(LOG_ERR, "event_add"); + goto fail; + } + + return 0; + +fail: + tcpdns_fini_instance(instance); + + if (fd != -1) { + if (close(fd) != 0) + log_errno(LOG_WARNING, "close"); + } + + return -1; +} + +/* Drops instance completely, freeing its memory and removing from + * instances list. + */ +static void tcpdns_fini_instance(tcpdns_instance *instance) +{ + if (event_initialized(&instance->listener)) { + if (event_del(&instance->listener) != 0) + log_errno(LOG_WARNING, "event_del"); + if (close(EVENT_FD(&instance->listener)) != 0) + log_errno(LOG_WARNING, "close"); + memset(&instance->listener, 0, sizeof(instance->listener)); + } + + list_del(&instance->list); + + memset(instance, 0, sizeof(*instance)); + free(instance); +} + +static int tcpdns_init() +{ + tcpdns_instance *tmp, *instance = NULL; + + // TODO: init debug_dumper + + list_for_each_entry_safe(instance, tmp, &instances, list) { + if (tcpdns_init_instance(instance) != 0) + goto fail; + } + + return 0; + +fail: + tcpdns_fini(); + return -1; +} + +static int tcpdns_fini() +{ + tcpdns_instance *tmp, *instance = NULL; + + list_for_each_entry_safe(instance, tmp, &instances, list) + tcpdns_fini_instance(instance); + + return 0; +} + +static parser_section tcpdns_conf_section = +{ + .name = "tcpdns", + .entries = tcpdns_entries, + .onenter = tcpdns_onenter, + .onexit = tcpdns_onexit +}; + +app_subsys tcpdns_subsys = +{ + .init = tcpdns_init, + .fini = tcpdns_fini, + .conf_section = &tcpdns_conf_section, +}; + +/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ +/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/tcpdns.h b/tcpdns.h new file mode 100644 index 00000000..eb00a962 --- /dev/null +++ b/tcpdns.h @@ -0,0 +1,53 @@ +#ifndef TCPDNS_H +#define TCPDNS_H + +typedef struct tcpdns_config_t { + struct sockaddr_in bindaddr; + struct sockaddr_in udpdns1_addr; + struct sockaddr_in udpdns2_addr; + struct sockaddr_in tcpdns1_addr; + struct sockaddr_in tcpdns2_addr; + int timeout; /* timeout value for DNS response*/ +} tcpdns_config; + +typedef struct tcpdns_instance_t { + list_head list; + tcpdns_config config; + struct event listener; + list_head requests; + // Data for DNS resolver status tracking/checking + int udp1_delay_ms; + int udp2_delay_ms; + int tcp1_delay_ms; + int tcp2_delay_ms; +} tcpdns_instance; + + +typedef struct dns_header_t { + uint16_t id; + uint8_t qr_opcode_aa_tc_rd; + uint8_t ra_z_rcode; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +} PACKED dns_header; + + +typedef struct dns_request_t { + list_head list; + tcpdns_instance * instance; + short state; + int flags; + struct bufferevent* resolver; + struct sockaddr_in client_addr; + struct timeval req_time; + int * delay; + size_t data_len; + union { + char raw[513]; // DNS request longer than 512 should go over TCP. + dns_header header; + } data; +} dns_request; + +#endif /* TCPDNS_H */ From f43d320917f90c69c2a2e5613a9672d73975897a Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 17 Feb 2015 11:34:38 +0800 Subject: [PATCH 035/192] Refine code and make autoproxy much configurable --- Makefile | 2 +- README.md | 9 +- autoproxy.c | 244 +++++++++++++------------------ ipcache.c | 326 ++++++++++++++++++++++++++++++++++++++++++ ipcache.h | 10 ++ main.c | 34 ++++- main.h | 1 + redsocks.c | 50 +++---- redsocks.conf.example | 17 +++ redsocks.h | 6 +- 10 files changed, 523 insertions(+), 176 deletions(-) create mode 100644 ipcache.c create mode 100644 ipcache.h diff --git a/Makefile b/Makefile index 8ea32ee5..4d09709e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -OBJS := parser.o main.o redsocks.o log.o direct.o autoproxy.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o tcpdns.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o tcpdns.o gen/version.o SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend diff --git a/README.md b/README.md index 00f13726..aa12f66b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,12 @@ Since this variant of redsocks is customized for running with Openwrt, please read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how to cross compile. -###Note: +Configurations +-------------- +Please see 'redsocks.conf.example' for whole picture of configuration file. +Below are additional sample configuration sections for different usage. + +##Redirect Blocked Traffic via Proxy Automatically To use the autoproxy feature, please change the redsocks section in configuration file like this: @@ -116,4 +121,4 @@ server as the local IP:port configured above. AUTHOR ------ -Zhuofei Wang +Zhuofei Wang **Accept donations by AliPay with this email** diff --git a/autoproxy.c b/autoproxy.c index 2dc13497..874a1cc2 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -1,5 +1,5 @@ /* redsocks2 - transparent TCP-to-proxy redirector - * Copyright (C) 2013-2014 Zhuofei Wang + * Copyright (C) 2013-2015 Zhuofei Wang * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"); you may not @@ -26,6 +26,10 @@ #include "utils.h" #include "log.h" #include "redsocks.h" +#include "parser.h" +#include "list.h" +#include "main.h" +#include "ipcache.h" typedef enum autoproxy_state_t { /* Introduce subsystem */ @@ -52,143 +56,119 @@ static void auto_connect_relay(redsocks_client *client); static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); static void auto_event_error(struct bufferevent *buffev, short what, void *_arg); -#define CIRCUIT_RESET_SECONDS 1 -#define DEFAULT_CONNECT_TIMEOUT_SECONDS 10 #define QUICK_CONNECT_TIMEOUT_SECONDS 3 #define NO_CHECK_SECONDS 60 -#define CACHE_ITEM_STALE_SECONDS 60*30 -#define ADDR_CACHE_BLOCKS 256 -#define ADDR_CACHE_BLOCK_SIZE 16 -#define ADDR_PORT_CHECK 1 -#define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & 0xFF) / (256/ADDR_CACHE_BLOCKS) -#define get_autoproxy_client(client) (void*)(client + 1) + client->instance->relay_ss->payload_len; -typedef struct cache_data_t { - struct sockaddr_in addr; - time_t access_time; -} cache_data; -static cache_data addr_cache[ADDR_CACHE_BLOCKS][ADDR_CACHE_BLOCK_SIZE]; -static int addr_cache_counters[ADDR_CACHE_BLOCKS]; -static int addr_cache_pointers[ADDR_CACHE_BLOCKS]; -static int cache_init = 0; +typedef struct autoproxy_config_t { + list_head list; // Make it a list to support multiple configurations + int quick_connect_timeout; + int no_quick_check_seconds; +} autoproxy_config; -static void init_addr_cache() -{ - if (!cache_init) - { - memset((void *)addr_cache, 0, sizeof(addr_cache)); - memset((void *)addr_cache_counters, 0, sizeof(addr_cache_counters)); - memset((void *)addr_cache_pointers, 0, sizeof(addr_cache_pointers)); - cache_init = 1; - } -} +static autoproxy_config default_config = { + .quick_connect_timeout = QUICK_CONNECT_TIMEOUT_SECONDS, + .no_quick_check_seconds = NO_CHECK_SECONDS, +}; + +static list_head configs = LIST_HEAD_INIT(configs); -static int is_addr_in_cache(const struct sockaddr_in * addr) +static parser_entry autoproxy_entries[] = { - /* get block index */ - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - int first = addr_cache_pointers[block]; - int i = 0; - /* do reverse search for efficency */ - for ( i = count - 1; i >= 0; i -- ) - { - if (0 == evutil_sockaddr_cmp((const struct sockaddr *)addr, - (const struct sockaddr *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr, - ADDR_PORT_CHECK)) - return 1; - } + { .key = "quick_connect_timeout", .type = pt_uint16 }, + { .key = "no_quick_check_seconds", .type = pt_uint16 }, + { } +}; + +static int autoproxy_onenter(parser_section *section) +{ + autoproxy_config * config = malloc(sizeof(*config)); + if (!config) { + parser_error(section->context, "Not enough memory"); + return -1; + } + + INIT_LIST_HEAD(&config->list); + /* initialize with default config */ + memcpy(config, &default_config, sizeof(*config)); + + for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) + entry->addr = + (strcmp(entry->key, "quick_connect_timeout") == 0) ? (void*)&config->quick_connect_timeout: + (strcmp(entry->key, "no_quick_check_seconds") == 0) ? (void*)&config->no_quick_check_seconds: + NULL; + section->data = config; return 0; } -static time_t * get_addr_time_in_cache(const struct sockaddr_in * addr) +static int autoproxy_onexit(parser_section *section) { - /* get block index */ - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - int first = addr_cache_pointers[block]; - int i = 0; - /* do reverse search for efficency */ - for ( i = count - 1; i >= 0; i -- ) - { - if (0 == evutil_sockaddr_cmp((const struct sockaddr *)addr, - (const struct sockaddr *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr, - ADDR_PORT_CHECK)) - return &addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].access_time; - } - return NULL; + /* FIXME: Rewrite in bullet-proof style. There are memory leaks if config + * file is not correct, so correct on-the-fly config reloading is + * currently impossible. + */ + const char *err = NULL; + autoproxy_config * config = section->data; + + section->data = NULL; + for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) + entry->addr = NULL; + + /* Check and update values here */ + if (!config->quick_connect_timeout) + err = "Timeout value for quick check can not be 0. Default: 3"; + + if (err) + parser_error(section->context, err); + else + // Add config to config list + list_add(&config->list, &configs); + + return err ? -1 : 0; } -void set_addr_time_in_cache(const struct sockaddr_in * addr, time_t time) +static parser_section autoproxy_conf_section = { - /* get block index */ - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - int first = addr_cache_pointers[block]; - int i = 0; - /* do reverse search for efficency */ - for ( i = count - 1; i >= 0; i -- ) - { - if (0 == evutil_sockaddr_cmp((const struct sockaddr *)addr, - (const struct sockaddr *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr, - ADDR_PORT_CHECK)) - { - addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].access_time = time; - return; - } - } -} + .name = "autoproxy", + .entries = autoproxy_entries, + .onenter = autoproxy_onenter, + .onexit = autoproxy_onexit +}; -static void add_addr_to_cache(const struct sockaddr_in * addr) +app_subsys autoproxy_app_subsys = { - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - /* use 'first' to index item in cache block when count is equal or greater than block size */ - int first = addr_cache_pointers[block]; +// .init = autoproxy_init, +// .fini = autoproxy_fini, + .conf_section = &autoproxy_conf_section, +}; - if (count < ADDR_CACHE_BLOCK_SIZE) + +static autoproxy_config * get_config(redsocks_client * client) +{ + // TODO: By far, only the first configuration section takes effect. + // We need to find a proper way to let user specify which config section + // to associate with. + autoproxy_config * config = NULL; + if (!list_empty(&configs)) { - memcpy((void *)&addr_cache[block][count].addr, (void *) addr, sizeof(struct sockaddr_in)); - addr_cache[block][count].access_time = redsocks_time(NULL); - addr_cache_counters[block]++; + list_for_each_entry(config, &configs, list) + break; + return config; } else { - memcpy((void *)&addr_cache[block][first].addr, (void *) addr, sizeof(struct sockaddr_in)); - addr_cache[block][first].access_time = redsocks_time(NULL); - addr_cache_pointers[block]++; - addr_cache_pointers[block]%=ADDR_CACHE_BLOCK_SIZE; - } -} - -static void del_addr_from_cache(const struct sockaddr_in * addr) -{ /* get block index */ - int block = block_from_sockaddr_in(addr); - int count = addr_cache_counters[block]; - int first = addr_cache_pointers[block]; - int i = 0; - /* do reverse search for efficency */ - for ( i = count - 1; i >= 0; i -- ) - { - if (0 == evutil_sockaddr_cmp((const struct sockaddr *)addr, - (const struct sockaddr *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE].addr, - ADDR_PORT_CHECK)) - /* found. zero this item */ - { - memset((void *)&addr_cache[block][(first+i)%ADDR_CACHE_BLOCK_SIZE], 0, sizeof(cache_data)); - break; - } + return &default_config; } } +#define get_autoproxy_client(client) (void*)(client + 1) + client->instance->relay_ss->payload_len; + void auto_client_init(redsocks_client *client) { autoproxy_client * aclient = get_autoproxy_client(client); memset((void *) aclient, 0, sizeof(autoproxy_client)); aclient->state = AUTOPROXY_NEW; - init_addr_cache(); } void auto_client_fini(redsocks_client *client) @@ -207,7 +187,7 @@ static void on_connection_confirmed(redsocks_client *client) { redsocks_log_error(client, LOG_DEBUG, "IP Confirmed"); - del_addr_from_cache(&client->destaddr); + cache_del_addr(&client->destaddr); } static void on_connection_blocked(redsocks_client *client) @@ -475,9 +455,9 @@ static void auto_retry(redsocks_client * client, int updcache) if (updcache) { /* only add IP to cache when the IP is not in cache */ - if (get_addr_time_in_cache(&client->destaddr) == NULL) + if (cache_get_addr_time(&client->destaddr) == NULL) { - add_addr_to_cache(&client->destaddr); + cache_add_addr(&client->destaddr); redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %s", inet_ntoa(client->destaddr.sin_addr)); } @@ -508,27 +488,14 @@ static void auto_retry(redsocks_client * client, int updcache) static int auto_retry_or_drop(redsocks_client * client) { autoproxy_client * aclient = get_autoproxy_client(client); - time_t now = redsocks_time(NULL); - if (aclient->state == AUTOPROXY_NEW) - { - if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) - { - on_connection_blocked(client); - auto_retry(client, 0); - return 0; - } - } - else if ( aclient->state == AUTOPROXY_CONNECTED) + if (aclient->state == AUTOPROXY_NEW || aclient->state == AUTOPROXY_CONNECTED) { -// if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) - { - on_connection_blocked(client); - auto_retry(client, 0); - return 0; - } + on_connection_blocked(client); + auto_retry(client, 0); + return 0; } - + /* drop */ return 1; } @@ -541,7 +508,6 @@ static void auto_relay_connected(struct bufferevent *buffev, void *_arg) assert(buffev == client->relay); redsocks_touch_client(client); - if (!red_is_socket_connected_ok(buffev)) { if (aclient->state == AUTOPROXY_NEW && !auto_retry_or_drop(client)) @@ -609,7 +575,7 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) { // Update access time for IP fails again. if (aclient->quick_check) - set_addr_time_in_cache(&client->destaddr, redsocks_time(NULL)); + cache_touch_addr(&client->destaddr); on_connection_blocked(client); /* In case timeout occurs while connecting relay, we try to connect @@ -664,6 +630,7 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) static void auto_connect_relay(redsocks_client *client) { autoproxy_client * aclient = get_autoproxy_client(client); + autoproxy_config * config = NULL; struct timeval tv; tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; @@ -672,22 +639,22 @@ static void auto_connect_relay(redsocks_client *client) /* use default timeout if timeout is not configured */ if (tv.tv_sec == 0) - tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS; + tv.tv_sec = DEFAULT_CONNECT_TIMEOUT; if (aclient->state == AUTOPROXY_NEW) { - acc_time = get_addr_time_in_cache(&client->destaddr); + acc_time = cache_get_addr_time(&client->destaddr); if (acc_time) { redsocks_log_error(client, LOG_DEBUG, "Found dest IP in cache"); + config = get_config(client); // No quick check when the time passed since IP is added to cache is // less than NO_CHECK_SECONDS. Just let it go via proxy. - if (now - *acc_time < NO_CHECK_SECONDS) + if (now - *acc_time < config->no_quick_check_seconds) { auto_retry(client, 0); return; } - /* update timeout value for quick detection. * Sometimes, good sites are added into cache due to occasionally * connection timeout. It is annoying. So, decision is made to @@ -698,13 +665,8 @@ static void auto_connect_relay(redsocks_client *client) * reset almost immediately when connection is set up or when HTTP * request is sent. */ - tv.tv_sec = QUICK_CONNECT_TIMEOUT_SECONDS; + tv.tv_sec = config->quick_connect_timeout; aclient->quick_check = 1; - if (now - *acc_time >= CACHE_ITEM_STALE_SECONDS ) - { - /* stale this address in cache */ - del_addr_from_cache(&client->destaddr); - } } /* connect to target directly without going through proxy */ client->relay = red_connect_relay2(&client->destaddr, diff --git a/ipcache.c b/ipcache.c new file mode 100644 index 00000000..de1c7629 --- /dev/null +++ b/ipcache.c @@ -0,0 +1,326 @@ +/* redsocks2 - transparent TCP-to-proxy redirector + * Copyright (C) 2013-2015 Zhuofei Wang + * + * This code is based on redsocks project developed by Leonid Evdokimov. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + + + +#include +#include +#include +#include +#include +#include +#include "redsocks.h" +#include "log.h" +#include "parser.h" +#include "main.h" +#include "ipcache.h" + +#define ADDR_CACHE_BLOCKS 256 +#define ADDR_CACHE_BLOCK_SIZE 16 +#define ADDR_PORT_CHECK 1 +#define CACHE_ITEM_STALE_SECONDS 60*30 +#define MAX_BLOCK_SIZE 32 + + +//---------------------------------------------------------------------------------------- +typedef struct cache_config_t { + // Values to be read from config file + unsigned int cache_size; + unsigned int port_check; + unsigned int stale_time; + // Dynamically calculated values. + unsigned int block_size; + unsigned int block_count; +} cache_config; + +static cache_config default_config = { + .cache_size = ADDR_CACHE_BLOCKS * ADDR_CACHE_BLOCK_SIZE / 1024, + .port_check = ADDR_PORT_CHECK, + .stale_time = CACHE_ITEM_STALE_SECONDS, + .block_size = ADDR_CACHE_BLOCK_SIZE, + .block_count = ADDR_CACHE_BLOCKS, +}; + +static parser_entry cache_entries[] = +{ + { .key = "cache_size", .type = pt_uint16 }, + { .key = "port_check", .type = pt_uint16 }, + { .key = "stale_time", .type = pt_uint16 }, + { } +}; + +static int cache_onenter(parser_section *section) +{ + cache_config * config = &default_config; + + config->cache_size = 0; + for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) + entry->addr = + (strcmp(entry->key, "cache_size") == 0) ? (void*)&config->cache_size: + (strcmp(entry->key, "port_check") == 0) ? (void*)&config->port_check: + (strcmp(entry->key, "stale_time") == 0) ? (void*)&config->stale_time: + NULL; + section->data = config; + return 0; +} + +static int cache_onexit(parser_section *section) +{ + const char *err = NULL; + cache_config * config = section->data; + + section->data = NULL; + for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) + entry->addr = NULL; + + /* Check and update values here */ + /* + Let uer specify cache size by number of 1K items. + Make it easier for user to config cache size. + */ + if (config->cache_size > MAX_BLOCK_SIZE) + err = "Cache size must be in range [0-32]. 0 means default. Default: 4"; + else if (config->cache_size) + { + config->block_count = ADDR_CACHE_BLOCKS; + config->block_size = config->cache_size * 1024 / config->block_count; + while(config->block_size > MAX_BLOCK_SIZE) + { + config->block_count <<= 1; + config->block_size = config->cache_size * 1024 / config->block_count; + } + } + + if (!err && config->stale_time < 5) + err = "Time to stale cache item must be equal or greater than 5 seconds."; + + if (err) + parser_error(section->context, err); + + return err ? -1 : 0; +} + +static parser_section cache_conf_section = +{ + .name = "ipcache", + .entries = cache_entries, + .onenter = cache_onenter, + .onexit = cache_onexit +}; + +#define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & (config->block_count -1)) + +typedef struct cache_data_t { + char present; + struct sockaddr_in addr; + time_t access_time; +} cache_data; + +static int * addr_cache_counters = NULL; +static int * addr_cache_pointers = NULL; +static cache_data * addr_cache = NULL; + +static inline cache_config * get_config() +{ + return &default_config; +} + +static int cache_init() +{ + cache_config * config = get_config(); + size_t size; + + size = sizeof(cache_data) * config->block_size * config->block_count; + if (!addr_cache) + { + addr_cache = malloc(size); + } + memset((void *)addr_cache, 0, size); + + size = sizeof(* addr_cache_counters) * config->block_count; + if (!addr_cache_counters) + { + addr_cache_counters = malloc(size); + } + memset((void *)addr_cache_counters, 0, size); + + size = sizeof(* addr_cache_pointers) * config->block_count; + if (!addr_cache_pointers) + { + addr_cache_pointers = malloc(size); + } + memset((void *)addr_cache_pointers, 0, size); + return 0; +} + +static int cache_fini() +{ + if (addr_cache) + { + free(addr_cache); + addr_cache = NULL; + } + if (addr_cache_counters) + { + free(addr_cache_counters); + addr_cache_counters = NULL; + } + if (addr_cache_pointers) + { + free(addr_cache_pointers); + addr_cache_pointers = NULL; + } + return 0; +} + +static inline cache_data * get_cache_data(unsigned int block, unsigned int index) +{ + cache_config * config = get_config(); + + unsigned int i = block * config->block_size + index % config->block_size; + return &addr_cache[i]; +} + +static cache_data * get_cache_item(const struct sockaddr_in * addr) +{ + cache_config * config = get_config(); + time_t now = redsocks_time(NULL); + cache_data * item; + /* get block index */ + unsigned int block = block_from_sockaddr_in(addr); + unsigned int count = addr_cache_counters[block]; + unsigned int first = addr_cache_pointers[block]; + unsigned int i = 0; + /* do reverse search for efficency */ + for (i = count; i > 0; i--) + { + item = get_cache_data(block, first+i-1); + if (item + && item->present + && 0 == evutil_sockaddr_cmp((const struct sockaddr *)addr, + (const struct sockaddr *)&item->addr, + config->port_check)) + { + // Remove stale item + if (item->access_time + config->stale_time < now) + { + item->present = 0; + return NULL; + } + return item; + } + } + return NULL; +} + +time_t * cache_get_addr_time(const struct sockaddr_in * addr) +{ + cache_data * item = get_cache_item(addr); + if (item) + return &item->access_time; + return NULL; +} + +void cache_touch_addr(const struct sockaddr_in * addr) +{ + cache_data * item = get_cache_item(addr); + if (item) + item->access_time = redsocks_time(NULL); +} + +void cache_add_addr(const struct sockaddr_in * addr) +{ + cache_config * config = get_config(); + cache_data * item; + unsigned int block = block_from_sockaddr_in(addr); + unsigned int count = addr_cache_counters[block]; + /* use 'first' to index item in cache block when count is equal or greater than block size */ + unsigned int first = addr_cache_pointers[block]; + + if (count < config->block_size) + item = get_cache_data(block, count); + else + item = get_cache_data(block, first); + + memcpy((void *)&item->addr, (void *)addr, sizeof(struct sockaddr_in)); + item->present = 1; + item->access_time = redsocks_time(NULL); + addr_cache_pointers[block]++; + addr_cache_pointers[block] %= config->block_size; +} + +void cache_del_addr(const struct sockaddr_in * addr) +{ + cache_data * item = get_cache_item(addr); + if (item) + item->present = 0; +} + +#define ADDR_COUNT_PER_LINE 4 +static void cache_dumper() +{ + unsigned int count = 0; + unsigned int blk = 0; + unsigned int idx = 0; + unsigned int p = 0, j; + char addr_str[ADDR_COUNT_PER_LINE][RED_INET_ADDRSTRLEN]; + cache_data * item; + cache_config * config = get_config(); + + log_error(LOG_INFO, "Start dumping IP cache:"); + for (; blk < config->block_count; blk++) + { + for (idx=0; idx < config->block_size; idx++) + { + item = get_cache_data(blk, idx); + if (item && item->present) + { + count++; + red_inet_ntop(&item->addr, addr_str[p], sizeof(addr_str[0])); + p++; + if (p == ADDR_COUNT_PER_LINE) + { + p = 0; + // TODO: Replace this implementation with better one + log_error(LOG_INFO, "%s %s %s %s", addr_str[0], + addr_str[1], addr_str[2], addr_str[3]); + } + } + } + } + if (p) + { + // TODO: Replace this implementation with better one + for (j = p; j < ADDR_COUNT_PER_LINE; j++) + addr_str[j][0] = 0; + log_error(LOG_INFO, "%s %s %s %s", addr_str[0], + addr_str[1], addr_str[2], addr_str[3]); + } + + log_error(LOG_INFO, "End of dumping IP cache. Totally %u entries.", count); +} + +app_subsys cache_app_subsys = +{ + .init = cache_init, + .fini = cache_fini, + .dump = cache_dumper, + .conf_section = &cache_conf_section, +}; + +/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ +/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/ipcache.h b/ipcache.h new file mode 100644 index 00000000..01810189 --- /dev/null +++ b/ipcache.h @@ -0,0 +1,10 @@ +#ifndef REDSOCKS_CACHE_H +#define REDSOCKS_CACHE_H + +void cache_add_addr(const struct sockaddr_in * addr); +void cache_del_addr(const struct sockaddr_in * addr); +void cache_touch_addr(const struct sockaddr_in * addr); +time_t * cache_get_addr_time(const struct sockaddr_in * addr); + +#endif + diff --git a/main.c b/main.c index 023feb7f..91459b92 100644 --- a/main.c +++ b/main.c @@ -31,10 +31,14 @@ extern app_subsys redsocks_subsys; extern app_subsys base_subsys; extern app_subsys redudp_subsys; extern app_subsys tcpdns_subsys; +extern app_subsys autoproxy_app_subsys; +extern app_subsys cache_app_subsys; app_subsys *subsystems[] = { &redsocks_subsys, &base_subsys, + &autoproxy_app_subsys, + &cache_app_subsys, &redudp_subsys, &tcpdns_subsys, }; @@ -49,6 +53,16 @@ static void terminate(int sig, short what, void *_arg) log_error(LOG_WARNING, "event_loopbreak"); } +static void dump_handler(int sig, short what, void *_arg) +{ + app_subsys **ss; + FOREACH(ss, subsystems) { + if ((*ss)->dump) { + (*ss)->dump(); + } + } +} + static void red_srand() { struct timeval tv; @@ -68,6 +82,7 @@ int main(int argc, char **argv) app_subsys **ss; int exit_signals[2] = {SIGTERM, SIGINT}; struct event terminators[2]; + struct event dumper; bool conftest = false; int opt; int i; @@ -153,12 +168,19 @@ int main(int argc, char **argv) assert(SIZEOF_ARRAY(exit_signals) == SIZEOF_ARRAY(terminators)); for (i = 0; i < SIZEOF_ARRAY(exit_signals); i++) { evsignal_assign(&terminators[i], get_event_base(), exit_signals[i], terminate, NULL); - if (signal_add(&terminators[i], NULL) != 0) { + if (evsignal_add(&terminators[i], NULL) != 0) { log_errno(LOG_ERR, "signal_add"); goto shutdown; } } + memset(&dumper, 0, sizeof(dumper)); + evsignal_assign(&dumper, get_event_base(), SIGUSR1, dump_handler, NULL); + if (evsignal_add(&dumper, NULL) != 0) { + log_errno(LOG_ERR, "evsignal_add"); + goto shutdown; + } + log_error(LOG_NOTICE, "redsocks started"); event_base_dispatch(g_event_base); @@ -166,9 +188,15 @@ int main(int argc, char **argv) log_error(LOG_NOTICE, "redsocks goes down"); shutdown: + if (evsignal_initialized(&dumper)) { + if (evsignal_del(&dumper) != 0) + log_errno(LOG_WARNING, "signal_del"); + memset(&dumper, 0, sizeof(dumper)); + } + for (i = 0; i < SIZEOF_ARRAY(exit_signals); i++) { - if (signal_initialized(&terminators[i])) { - if (signal_del(&terminators[i]) != 0) + if (evsignal_initialized(&terminators[i])) { + if (evsignal_del(&terminators[i]) != 0) log_errno(LOG_WARNING, "signal_del"); memset(&terminators[i], 0, sizeof(terminators[i])); } diff --git a/main.h b/main.h index 3930e438..6473cee8 100644 --- a/main.h +++ b/main.h @@ -6,6 +6,7 @@ typedef struct app_subsys_t { int (*init)(); int (*fini)(); + void (*dump)();// Allow subsystem to dump information parser_section* conf_section; } app_subsys; diff --git a/redsocks.c b/redsocks.c index 57cb39fe..b1379d4e 100644 --- a/redsocks.c +++ b/redsocks.c @@ -1,5 +1,5 @@ /* redsocks2 - transparent TCP-to-proxy redirector - * Copyright (C) 2013-2014 Zhuofei Wang + * Copyright (C) 2013-2015 Zhuofei Wang * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"). @@ -319,9 +319,6 @@ int redsocks_start_relay(redsocks_client *client) int error; bufferevent_event_cb event_cb; - if (client->instance->relay_ss->fini) - client->instance->relay_ss->fini(client); - bufferevent_setwatermark(client->client, EV_READ|EV_WRITE, 0, REDSOCKS_RELAY_HALFBUFF); bufferevent_setwatermark(client->relay, EV_READ|EV_WRITE, 0, REDSOCKS_RELAY_HALFBUFF); #ifdef bufferevent_getcb @@ -359,7 +356,7 @@ int redsocks_start_relay(redsocks_client *client) void redsocks_drop_client(redsocks_client *client) { - redsocks_log_error(client, LOG_INFO, "dropping client"); + redsocks_log_error(client, LOG_DEBUG, "dropping client"); if (client->instance->config.autoproxy && autoproxy_subsys.fini) autoproxy_subsys.fini(client); @@ -614,7 +611,7 @@ void redsocks_connect_relay(redsocks_client *client) tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; if (tv.tv_sec == 0) - tv.tv_sec = 10; + tv.tv_sec = DEFAULT_CONNECT_TIMEOUT; client->relay = red_connect_relay2(&client->instance->config.relayaddr, @@ -801,14 +798,14 @@ static const char *redsocks_event_str(unsigned short what) "???"; } -void redsocks_dump_client(redsocks_client * client) +void redsocks_dump_client(redsocks_client * client, int loglevel) { time_t now = redsocks_time(NULL); const char *s_client_evshut = redsocks_evshut_str(client->client_evshut); const char *s_relay_evshut = redsocks_evshut_str(client->relay_evshut); - redsocks_log_error(client, LOG_DEBUG, "client: %i (%s)%s%s input %d output %d, relay: %i (%s)%s%s input %d output %d, age: %li sec, idle: %li sec.", + redsocks_log_error(client, loglevel, "client: %i (%s)%s%s input %d output %d, relay: %i (%s)%s%s input %d output %d, age: %li sec, idle: %li sec.", bufferevent_getfd(client->client), redsocks_event_str(bufferevent_get_enabled(client->client)), s_client_evshut[0] ? " " : "", s_client_evshut, @@ -823,23 +820,24 @@ void redsocks_dump_client(redsocks_client * client) now - client->last_event); } -static void redsocks_debug_dump_instance(redsocks_instance *instance) +static void redsocks_dump_instance(redsocks_instance *instance) { redsocks_client *client = NULL; - log_error(LOG_DEBUG, "Dumping client list for instance %p:", instance); + log_error(LOG_INFO, "Dumping client list for instance %p(%s):", instance, + instance->relay_ss->name); list_for_each_entry(client, &instance->clients, list) - redsocks_dump_client(client); + redsocks_dump_client(client, LOG_INFO); - log_error(LOG_DEBUG, "End of client list."); + log_error(LOG_INFO, "End of client list."); } -static void redsocks_debug_dump(int sig, short what, void *_arg) +static void redsocks_debug_dump() { redsocks_instance *instance = NULL; list_for_each_entry(instance, &instances, list) - redsocks_debug_dump_instance(instance); + redsocks_dump_instance(instance); } /* Audit is required to clean up hung connections. @@ -853,7 +851,8 @@ static void redsocks_audit_instance(redsocks_instance *instance) time_t now = redsocks_time(NULL); int drop_it = 0; - log_error(LOG_DEBUG, "Audit client list for instance %p:", instance); + log_error(LOG_DEBUG, "Audit client list for instance %p(%s):", instance, + instance->relay_ss->name); list_for_each_entry_safe(client, tmp, &instance->clients, list) { drop_it = 0; @@ -876,7 +875,7 @@ static void redsocks_audit_instance(redsocks_instance *instance) drop_it = 1; if (drop_it){ - redsocks_dump_client(client); + redsocks_dump_client(client, LOG_DEBUG); redsocks_drop_client(client); } } @@ -918,6 +917,12 @@ static int redsocks_init_instance(redsocks_instance *instance) int error; evutil_socket_t fd = -1; + if (instance->relay_ss->instance_init + && instance->relay_ss->instance_init(instance)) { + log_errno(LOG_ERR, "Failed to init relay subsystem."); + goto fail; + } + fd = socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) { log_errno(LOG_ERR, "socket"); @@ -1014,7 +1019,6 @@ static void redsocks_fini_instance(redsocks_instance *instance) { static int redsocks_fini(); -static struct event debug_dumper; static struct event heartbeat_writer; static struct event audit_event; @@ -1044,11 +1048,6 @@ static int redsocks_init() { return -1; } - evsignal_assign(&debug_dumper, base, SIGUSR1, redsocks_debug_dump, NULL); - if (evsignal_add(&debug_dumper, NULL) != 0) { - log_errno(LOG_ERR, "evsignal_add"); - goto fail; - } evsignal_assign(&heartbeat_writer, base, SIGUSR2, redsocks_heartbeat, NULL); if (evsignal_add(&heartbeat_writer, NULL) != 0) { log_errno(LOG_ERR, "evsignal_add SIGUSR2"); @@ -1088,12 +1087,6 @@ static int redsocks_fini() if (event_initialized(&audit_event)) evtimer_del(&audit_event); - if (evsignal_initialized(&debug_dumper)) { - if (evsignal_del(&debug_dumper) != 0) - log_errno(LOG_WARNING, "evsignal_del"); - memset(&debug_dumper, 0, sizeof(debug_dumper)); - } - return 0; } @@ -1101,6 +1094,7 @@ app_subsys redsocks_subsys = { .init = redsocks_init, .fini = redsocks_fini, + .dump = redsocks_debug_dump, .conf_section = &redsocks_conf_section, }; diff --git a/redsocks.conf.example b/redsocks.conf.example index 9b0644e8..f946c2b8 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -123,6 +123,23 @@ tcpdns { timeout = 4; // Timeout value for TCP DNS requests } +autoproxy { + no_quick_check_seconds = 60; // Directly relay traffic to proxy if an IP + // is found blocked in cache and it has been + // added into cache no earlier than this + // specified number of seconds. + quick_connect_timeout = 3; // Timeout value when performing quick + // connection check if an IP is found blocked + // in cache. +} + +ipcache { + // Configure IP cache + cache_size = 4; // Maximum number of IP's in 1K. + stale_time = 900; // Seconds to stale an IP in cache since it is added + // into cahce + port_check = 1; // Whether to distinguish port number in address +} // you can add more `redsocks' and `redudp' sections if you need. diff --git a/redsocks.h b/redsocks.h index dd082e5e..b1ce38c6 100644 --- a/redsocks.h +++ b/redsocks.h @@ -7,6 +7,8 @@ #include "list.h" +#define DEFAULT_CONNECT_TIMEOUT 10 + struct redsocks_client_t; struct redsocks_instance_t; @@ -18,6 +20,7 @@ typedef struct relay_subsys_t { evbuffercb writecb; void (*init)(struct redsocks_client_t *client); void (*fini)(struct redsocks_client_t *client); + int (*instance_init)(struct redsocks_instance_t *instance); void (*instance_fini)(struct redsocks_instance_t *instance); // connect_relay (if any) is called instead of redsocks_connect_relay after client connection acceptance void (*connect_relay)(struct redsocks_client_t *client); @@ -72,7 +75,8 @@ void redsocks_drop_client(redsocks_client *client); void redsocks_touch_client(redsocks_client *client); void redsocks_connect_relay(redsocks_client *client); int redsocks_start_relay(redsocks_client *client); -void redsocks_dump_client(redsocks_client * client); +void redsocks_dump_client(redsocks_client * client, int loglevel); +void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); typedef int (*size_comparator)(size_t a, size_t b); int sizes_equal(size_t a, size_t b); From 14d27661fc8a5922dfb24f48a60c9ceb8449fb18 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 17 Feb 2015 12:00:27 +0800 Subject: [PATCH 036/192] Add support to shadowsocks proxy(IPv4 TCP only) --- Makefile | 13 +- README.md | 89 +++- encrypt.c | 1003 +++++++++++++++++++++++++++++++++++++++++ encrypt.h | 155 +++++++ redsocks.c | 2 + redsocks.conf.example | 5 +- shadowsocks.c | 422 +++++++++++++++++ 7 files changed, 1673 insertions(+), 16 deletions(-) create mode 100644 encrypt.c create mode 100644 encrypt.h create mode 100644 shadowsocks.c diff --git a/Makefile b/Makefile index 4d09709e..08705eba 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o tcpdns.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o encrypt.o shadowsocks.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o tcpdns.o gen/version.o SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend @@ -6,9 +6,18 @@ OUT := redsocks2 VERSION := 0.60 LIBS := -levent -CFLAGS +=-fPIC -O3 +CFLAGS +=-fPIC -O3 override CFLAGS += -std=gnu99 -Wall #LDFLAGS += -fwhole-program +ifdef USE_CRYPTO_POLARSSL +override LIBS += -lpolarssl +override CFLAGS += -DUSE_CRYPTO_POLARSSL +$(info Compile with PolarSSL.) +else +override LIBS += -lssl -lcrypto +override CFLAGS += -DUSE_CRYPTO_OPENSSL +$(info Compile with OpenSSL by default. To compile with PolarSSL, run 'make USE_CRYPTO_POLARSSL=true' instead.) +endif all: $(OUT) diff --git a/README.md b/README.md index aa12f66b..84c49d69 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,39 @@ REDSOCKS2 ========= This is a modified version of original redsocks. The name is changed to REDSOCKS2 to distinguish with original redsocks. -This variant is useful for anti-GFW (Great Fire Wall). +This variant is useful for anti-GFW (Great Fire Wall). REDSOCKS2 contains +several new features. + +1. Redirect TCP connections which are blocked via proxy automatically without +need of blacklist. +2. Redirect UDP based DNS requests via TCP connection. +3. Integrated [shadowsocks](http://shadowsocks.org/) proxy support(IPv4 Only). +4. Redirect TCP connections without proxy. +5. Redirect TCP connections via specified network interface. + +If you feel my work done is helpful, please consider donation. Thanks. +**Accept donations by AliPay with account ** HOW it works ------------ -Anyone can help me to complete this part? -_- +Anyone can help me to complete this part? HOW TO BUILD ------------ -On general linux, simply run command below to build. +###Prerequisites +The following libraries are required. + +* OpenSSL or PolarSSL + +###Steps +On general linux, simply run command below to build with OpenSSL. make +To compile with PolarSSL + + make USE_CRYPTO_POLARSSL=true + Since this variant of redsocks is customized for running with Openwrt, please read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how to cross compile. @@ -23,7 +44,7 @@ Configurations Please see 'redsocks.conf.example' for whole picture of configuration file. Below are additional sample configuration sections for different usage. -##Redirect Blocked Traffic via Proxy Automatically +###Redirect Blocked Traffic via Proxy Automatically To use the autoproxy feature, please change the redsocks section in configuration file like this: @@ -34,9 +55,6 @@ configuration file like this: port = 9050; type = socks5; // I use socks5 proxy for GFW'ed IP autoproxy = 1; // I want autoproxy feature enabled on this section. - // The two lines above have same effect as - // type = autosocks5; - // in previous release. // timeout is meaningful when 'autoproxy' is non-zero. // It specified timeout value when trying to connect to destination // directly. Default is 10 seconds. When it is set to 0, default @@ -49,8 +67,7 @@ configuration file like this: //password = passwd; } - -##Redirect Blocked Traffic via VPN Automatically +###Redirect Blocked Traffic via VPN Automatically Suppose you have VPN connection setup with interface tun0. You want all all blocked traffic pass through via VPN connection while normal traffic pass through via default internet connection. @@ -64,8 +81,55 @@ pass through via default internet connection. autoproxy = 1; } +###Redirect Blocked Traffic via shadowsocks proxy +Similar like other redsocks section. The encryption method is specified +by field 'login'. -##Work with GoAgent + redsocks { + local_ip = 192.168.1.1; + local_port = 1080; + type = shadowsocks; + ip = 192.168.1.1; + port = 8388; + timeout = 13; + autoproxy = 1; + login = 'aes-128-cfb'; // field 'login' is reused as encryption + // method of shadowsocks + password = 'your password'; // Your shadowsocks password + } + +List of supported encryption methods(Compiled with OpenSSL): + + table + rc4 + rc4-md5 + aes-128-cfb + aes-192-cfb + aes-256-cfb + bf-cfb + camellia-128-cfb + camellia-192-cfb + camellia-256-cfb + cast5-cfb + des-cfb + idea-cfb + rc2-cfb + seed-cfb + +List of supported encryption methods(Compiled with PolarSSL): + + table + ARC4-128 + ARC4-128 + AES-128-CFB128 + AES-192-CFB128 + AES-256-CFB128 + BLOWFISH-CFB64 + CAMELLIA-128-CFB128 + CAMELLIA-192-CFB128 + CAMELLIA-256-CFB128 + +###Work with GoAgent To make redsocks2 works with GoAgent proxy, you need to set proxy type as 'http-relay' for HTTP protocol and 'http-connect' for HTTPS protocol respectively. @@ -99,7 +163,7 @@ The configuration for forwarding connections to GoAgent is like below: timeout = 13; } -##Redirect UPD based DNS Request via TCP connection +###Redirect UPD based DNS Request via TCP connection Sending DNS request via TCP connection is one way to prevent from DNS poisoning. You can redirect all UDP based DNS requests via TCP connection with the following config section. @@ -121,4 +185,5 @@ server as the local IP:port configured above. AUTHOR ------ -Zhuofei Wang **Accept donations by AliPay with this email** +[Zhuofei Wang](mailto:semigodking.com) semigodking@gmail.com + diff --git a/encrypt.c b/encrypt.c new file mode 100644 index 00000000..e31a30c6 --- /dev/null +++ b/encrypt.c @@ -0,0 +1,1003 @@ +/* + * encrypt.c - Manage the global encryptor + * + * Copyright (C) 2013 - 2015, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#include + +#if defined(USE_CRYPTO_OPENSSL) + +#include +#include + +#elif defined(USE_CRYPTO_POLARSSL) + +#include +#include +#include +#include +#define CIPHER_UNSUPPORTED "unsupported" + +#include +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +#endif + +// #include + +#include "encrypt.h" + +#define OFFSET_ROL(p, o) ((uint64_t)(*(p + o)) << (8 * o)) + +#ifdef DEBUG +static void dump(char *tag, char *text, int len) +{ + int i; + printf("%s: ", tag); + for (i = 0; i < len; i++) { + printf("0x%02x ", (uint8_t)text[i]); + } + printf("\n"); +} +#endif + +static const char * supported_ciphers[] = +{ + "table", + "rc4", + "rc4-md5", + "aes-128-cfb", + "aes-192-cfb", + "aes-256-cfb", + "bf-cfb", + "camellia-128-cfb", + "camellia-192-cfb", + "camellia-256-cfb", + "cast5-cfb", + "des-cfb", + "idea-cfb", + "rc2-cfb", + "seed-cfb", +// "salsa20", +// "chacha20" +}; + +#ifdef USE_CRYPTO_POLARSSL +static const char * supported_ciphers_polarssl[] = +{ + "table", + "ARC4-128", + "ARC4-128", + "AES-128-CFB128", + "AES-192-CFB128", + "AES-256-CFB128", + "BLOWFISH-CFB64", + "CAMELLIA-128-CFB128", + "CAMELLIA-192-CFB128", + "CAMELLIA-256-CFB128", + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, +// "salsa20", +// "chacha20" +}; +#endif + +#ifdef USE_CRYPTO_APPLECC +static const CCAlgorithm supported_ciphers_applecc[] = +{ + kCCAlgorithmInvalid, + kCCAlgorithmRC4, + kCCAlgorithmRC4, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmBlowfish, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmCAST, + kCCAlgorithmDES, + kCCAlgorithmInvalid, + kCCAlgorithmRC2, + kCCAlgorithmInvalid, +// kCCAlgorithmInvalid, +// kCCAlgorithmInvalid +}; + +#endif + +static const int supported_ciphers_iv_size[] = +{ + 0, 0, 16, 16, 16, 16, 8, 16, 16, 16, 8, 8, 8, 8, 16//, 8, 8 +}; + +static const int supported_ciphers_key_size[] = +{ + 0, 16, 16, 16, 24, 32, 16, 16, 24, 32, 16, 8, 16, 16, 16//, 32, 32 +}; + +#define CIPHER_NUM (sizeof(supported_ciphers)/sizeof(supported_ciphers[0])) + +static int crypto_stream_xor_ic(uint8_t *c, const uint8_t *m, uint64_t mlen, + const uint8_t *n, uint64_t ic, const uint8_t *k, + int method) +{ +/* + switch (method) { + case SALSA20: + return crypto_stream_salsa20_xor_ic(c, m, mlen, n, ic, k); + case CHACHA20: + return crypto_stream_chacha20_xor_ic(c, m, mlen, n, ic, k); + } +*/ + // always return 0 + return 0; +} + +static int random_compare(const void *_x, const void *_y, uint32_t i, + uint64_t a) +{ + uint8_t x = *((uint8_t *)_x); + uint8_t y = *((uint8_t *)_y); + return a % (x + i) - a % (y + i); +} + +static void merge(uint8_t *left, int llength, uint8_t *right, + int rlength, uint32_t salt, uint64_t key) +{ + uint8_t *ltmp = (uint8_t *)malloc(llength * sizeof(uint8_t)); + uint8_t *rtmp = (uint8_t *)malloc(rlength * sizeof(uint8_t)); + + uint8_t *ll = ltmp; + uint8_t *rr = rtmp; + + uint8_t *result = left; + + memcpy(ltmp, left, llength * sizeof(uint8_t)); + memcpy(rtmp, right, rlength * sizeof(uint8_t)); + + while (llength > 0 && rlength > 0) { + if (random_compare(ll, rr, salt, key) <= 0) { + *result = *ll; + ++ll; + --llength; + } else { + *result = *rr; + ++rr; + --rlength; + } + ++result; + } + + if (llength > 0) { + while (llength > 0) { + *result = *ll; + ++result; + ++ll; + --llength; + } + } else { + while (rlength > 0) { + *result = *rr; + ++result; + ++rr; + --rlength; + } + } + + free(ltmp); + free(rtmp); +} + +static void merge_sort(uint8_t array[], int length, + uint32_t salt, uint64_t key) +{ + uint8_t middle; + uint8_t *left, *right; + int llength; + + if (length <= 1) { + return; + } + + middle = length / 2; + + llength = length - middle; + + left = array; + right = array + llength; + + merge_sort(left, llength, salt, key); + merge_sort(right, middle, salt, key); + merge(left, llength, right, middle, salt, key); +} + +static unsigned char *enc_md5(const unsigned char *d, size_t n, unsigned char *md) +{ +#if defined(USE_CRYPTO_OPENSSL) + return MD5(d, n, md); +#elif defined(USE_CRYPTO_POLARSSL) + static unsigned char m[16]; + if (md == NULL) { + md = m; + } + md5(d, n, md); + return md; +#endif +} + +static void enc_table_init(enc_info * info, const char *pass) +{ + uint32_t i; + uint64_t key = 0; + uint8_t *digest; + + info->enc_table = malloc(256); + info->dec_table = malloc(256); + + digest = enc_md5((const uint8_t *)pass, strlen(pass), NULL); + + for (i = 0; i < 8; i++) { + key += OFFSET_ROL(digest, i); + } + + for (i = 0; i < 256; ++i) { + info->enc_table[i] = i; + } + for (i = 1; i < 1024; ++i) { + merge_sort(info->enc_table, 256, i, key); + } + for (i = 0; i < 256; ++i) { + // gen decrypt table from encrypt table + info->dec_table[info->enc_table[i]] = i; + } +} + +int cipher_iv_size(const cipher_kt_t *cipher) +{ +#if defined(USE_CRYPTO_OPENSSL) + return EVP_CIPHER_iv_length(cipher); +#elif defined(USE_CRYPTO_POLARSSL) + if (cipher == NULL) { + return 0; + } + return cipher->iv_size; +#endif +} + +int cipher_key_size(const cipher_kt_t *cipher) +{ +#if defined(USE_CRYPTO_OPENSSL) + return EVP_CIPHER_key_length(cipher); +#elif defined(USE_CRYPTO_POLARSSL) + if (cipher == NULL) { + return 0; + } + /* Override PolarSSL 32 bit default key size with sane 128 bit default */ + if (cipher->base != NULL && POLARSSL_CIPHER_ID_BLOWFISH == + cipher->base->cipher) { + return 128 / 8; + } + return cipher->key_length / 8; +#endif +} + +int bytes_to_key(const cipher_kt_t *cipher, const digest_type_t *md, + const uint8_t *pass, uint8_t *key, uint8_t *iv) +{ + size_t datal; + datal = strlen((const char *)pass); +#if defined(USE_CRYPTO_OPENSSL) + return EVP_BytesToKey(cipher, md, NULL, pass, datal, 1, key, iv); +#elif defined(USE_CRYPTO_POLARSSL) + md_context_t c; + unsigned char md_buf[MAX_MD_SIZE]; + int niv; + int nkey; + int addmd; + unsigned int mds; + unsigned int i; + int rv; + + nkey = cipher_key_size(cipher); + niv = cipher_iv_size(cipher); + rv = nkey; + if (pass == NULL) { + return nkey; + } + + memset(&c, 0, sizeof(md_context_t)); + if (md_init_ctx(&c, md)) { + return 0; + } + addmd = 0; + mds = md_get_size(md); + for (;; ) { + int error; + do { + error = 1; + if (md_starts(&c)) { + break; + } + if (addmd) { + if (md_update(&c, &(md_buf[0]), mds)) { + break; + } + } else { + addmd = 1; + } + if (md_update(&c, pass, datal)) { + break; + } + if (md_finish(&c, &(md_buf[0]))) { + break; + } + error = 0; + } while (0); + if (error) { + md_free_ctx(&c); + memset(md_buf, 0, MAX_MD_SIZE); + return 0; + } + + i = 0; + if (nkey) { + for (;; ) { + if (nkey == 0) { + break; + } + if (i == mds) { + break; + } + if (key != NULL) { + *(key++) = md_buf[i]; + } + nkey--; + i++; + } + } + if (niv && (i != mds)) { + for (;; ) { + if (niv == 0) { + break; + } + if (i == mds) { + break; + } + if (iv != NULL) { + *(iv++) = md_buf[i]; + } + niv--; + i++; + } + } + if ((nkey == 0) && (niv == 0)) { + break; + } + } + md_free_ctx(&c); + memset(md_buf, 0, MAX_MD_SIZE); + return rv; +#endif +} + +int rand_bytes(uint8_t *output, int len) +{ +#if defined(USE_CRYPTO_OPENSSL) + return RAND_bytes(output, len); +#elif defined(USE_CRYPTO_POLARSSL) + static entropy_context ec = {}; + static ctr_drbg_context cd_ctx = {}; + static unsigned char rand_initialised = 0; + const size_t blen = min(len, CTR_DRBG_MAX_REQUEST); + + if (!rand_initialised) { +#ifdef _WIN32 + HCRYPTPROV hProvider; + union { + unsigned __int64 seed; + BYTE buffer[8]; + } rand_buffer; + + hProvider = 0; + if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, \ + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + CryptGenRandom(hProvider, 8, rand_buffer.buffer); + CryptReleaseContext(hProvider, 0); + } else { + rand_buffer.seed = (unsigned __int64)clock(); + } +#else + FILE *urand; + union { + uint64_t seed; + uint8_t buffer[8]; + } rand_buffer; + + urand = fopen("/dev/urandom", "r"); + if (urand) { + int read = fread(&rand_buffer.seed, sizeof(rand_buffer.seed), 1, + urand); + fclose(urand); + if (read <= 0) { + rand_buffer.seed = (uint64_t)clock(); + } + } else { + rand_buffer.seed = (uint64_t)clock(); + } +#endif + entropy_init(&ec); + if (ctr_drbg_init(&cd_ctx, entropy_func, &ec, + (const unsigned char *)rand_buffer.buffer, 8) != 0) { +#if POLARSSL_VERSION_NUMBER >= 0x01030000 + entropy_free(&ec); +#endif + //FATAL("Failed to initialize random generator"); + } + rand_initialised = 1; + } + while (len > 0) { + if (ctr_drbg_random(&cd_ctx, output, blen) != 0) { + return 0; + } + output += blen; + len -= blen; + } + return 1; +#endif +} + +const cipher_kt_t *get_cipher_type(int method) +{ + if (method <= TABLE || method >= CIPHER_NUM) { + //LOGE("get_cipher_type(): Illegal method"); + return NULL; + } + + if (method == RC4_MD5) { + method = RC4; + } +/* + if (method >= SALSA20) { + return NULL; + } +*/ + const char *ciphername = supported_ciphers[method]; +#if defined(USE_CRYPTO_OPENSSL) + return EVP_get_cipherbyname(ciphername); +#elif defined(USE_CRYPTO_POLARSSL) + const char *polarname = supported_ciphers_polarssl[method]; + if (strcmp(polarname, CIPHER_UNSUPPORTED) == 0) { + //LOGE("Cipher %s currently is not supported by PolarSSL library", + // ciphername); + return NULL; + } + return cipher_info_from_string(polarname); +#endif +} + +const digest_type_t *get_digest_type(const char *digest) +{ + if (digest == NULL) { + //LOGE("get_digest_type(): Digest name is null"); + return NULL; + } + +#if defined(USE_CRYPTO_OPENSSL) + return EVP_get_digestbyname(digest); +#elif defined(USE_CRYPTO_POLARSSL) + return md_info_from_string(digest); +#endif +} + +static int cipher_context_init(const enc_info * info, cipher_ctx_t *ctx, int enc) +{ + int method = info->method; + if (method <= TABLE || method >= CIPHER_NUM) { + // Illegal method + return -1; + } +/* + if (method >= SALSA20) { + enc_iv_len = supported_ciphers_iv_size[method]; + return; + } +*/ + const char *ciphername = supported_ciphers[method]; +#if defined(USE_CRYPTO_APPLECC) + cipher_cc_t *cc = &ctx->cc; + cc->cryptor = NULL; + cc->cipher = supported_ciphers_applecc[method]; + if (cc->cipher == kCCAlgorithmInvalid) { + cc->valid = kCCContextInvalid; + } else { + cc->valid = kCCContextValid; + if (cc->cipher == kCCAlgorithmRC4) { + cc->mode = kCCModeRC4; + cc->padding = ccNoPadding; + } else { + cc->mode = kCCModeCFB; + cc->padding = ccPKCS7Padding; + } + return 0; + } +#endif + + cipher_evp_t *evp = &ctx->evp; + const cipher_kt_t *cipher = get_cipher_type(method); +#if defined(USE_CRYPTO_OPENSSL) + if (cipher == NULL) { + // Cipher is not found in OpenSSL library + return -1; + } + EVP_CIPHER_CTX_init(evp); + if (!EVP_CipherInit_ex(evp, cipher, NULL, NULL, NULL, enc)) { + // annot initialize cipher + return -1; + } + if (!EVP_CIPHER_CTX_set_key_length(evp, info->key_len)) { + EVP_CIPHER_CTX_cleanup(evp); + // Invalid key length + return -1; + } + if (method > RC4_MD5) { + EVP_CIPHER_CTX_set_padding(evp, 1); + } +#elif defined(USE_CRYPTO_POLARSSL) + if (cipher == NULL) { + // Cipher is not found in PolarSSL library + return -1; + } + if (cipher_init_ctx(evp, cipher) != 0) { + // Cannot initialize PolarSSL cipher context + return -1; + } +#endif + return 0; +} + +static void cipher_context_set_iv(const enc_info * info, cipher_ctx_t *ctx, uint8_t *iv, size_t iv_len, + int enc) +{ + const unsigned char *true_key; + + if (iv == NULL) { + //LOGE("cipher_context_set_iv(): IV is null"); + return; + } + + if (enc) { + rand_bytes(iv, iv_len); + } +/* + if (enc_method >= SALSA20) { + memcpy(ctx->iv, iv, iv_len); + return; + } +*/ + if (info->method == RC4_MD5) { + unsigned char key_iv[32]; + memcpy(key_iv, info->key, 16); + memcpy(key_iv + 16, iv, 16); + true_key = enc_md5(key_iv, 32, NULL); + iv_len = 0; + } else { + true_key = info->key; + } + +#ifdef USE_CRYPTO_APPLECC + cipher_cc_t *cc = &ctx->cc; + if (cc->valid == kCCContextValid) { + memcpy(cc->iv, iv, iv_len); + memcpy(cc->key, true_key, info->key_len); + cc->iv_len = iv_len; + cc->key_len = info->key_len; + cc->encrypt = enc ? kCCEncrypt : kCCDecrypt; + if (cc->cryptor != NULL) { + CCCryptorRelease(cc->cryptor); + cc->cryptor = NULL; + } + + CCCryptorStatus ret; + ret = CCCryptorCreateWithMode( + cc->encrypt, + cc->mode, + cc->cipher, + cc->padding, + cc->iv, cc->key, cc->key_len, + NULL, 0, 0, 0, + &cc->cryptor); + if (ret != kCCSuccess) { + if (cc->cryptor != NULL) { + CCCryptorRelease(cc->cryptor); + cc->cryptor = NULL; + } + //FATAL("Cannot set CommonCrypto key and IV"); + } + return; + } +#endif + + cipher_evp_t *evp = &ctx->evp; + if (evp == NULL) { + //LOGE("cipher_context_set_iv(): Cipher context is null"); + return; + } +#if defined(USE_CRYPTO_OPENSSL) + if (!EVP_CipherInit_ex(evp, NULL, NULL, true_key, iv, enc)) { + EVP_CIPHER_CTX_cleanup(evp); + //FATAL("Cannot set key and IV"); + } +#elif defined(USE_CRYPTO_POLARSSL) + if (cipher_setkey(evp, true_key, info->key_len * 8, enc) != 0) { + cipher_free_ctx(evp); + //FATAL("Cannot set PolarSSL cipher key"); + } +#if POLARSSL_VERSION_NUMBER >= 0x01030000 + if (cipher_set_iv(evp, iv, iv_len) != 0) { + cipher_free_ctx(evp); + //FATAL("Cannot set PolarSSL cipher IV"); + } + if (cipher_reset(evp) != 0) { + cipher_free_ctx(evp); + //FATAL("Cannot finalize PolarSSL cipher context"); + } +#else + if (cipher_reset(evp, iv) != 0) { + cipher_free_ctx(evp); + //FATAL("Cannot set PolarSSL cipher IV"); + } +#endif +#endif + +#ifdef DEBUG + dump("IV", (char *)iv, iv_len); +#endif +} + +static void cipher_context_release(enc_info * info, cipher_ctx_t *ctx) +{ + if (info->method >= SALSA20) { + return; + } + +#ifdef USE_CRYPTO_APPLECC + cipher_cc_t *cc = &ctx->cc; + if (cc->cryptor != NULL) { + CCCryptorRelease(cc->cryptor); + cc->cryptor = NULL; + } + if (cc->valid == kCCContextValid) { + return; + } +#endif + + cipher_evp_t *evp = &ctx->evp; +#if defined(USE_CRYPTO_OPENSSL) + EVP_CIPHER_CTX_cleanup(evp); +#elif defined(USE_CRYPTO_POLARSSL) + cipher_free_ctx(evp); +#endif +} + +static int cipher_context_update(cipher_ctx_t *ctx, uint8_t *output, int *olen, + const uint8_t *input, int ilen) +{ +#ifdef USE_CRYPTO_APPLECC + cipher_cc_t *cc = &ctx->cc; + if (cc->valid == kCCContextValid) { + CCCryptorStatus ret; + ret = CCCryptorUpdate(cc->cryptor, input, ilen, output, + ilen, (size_t *)olen); + return (ret == kCCSuccess) ? 1 : 0; + } +#endif + cipher_evp_t *evp = &ctx->evp; +#if defined(USE_CRYPTO_OPENSSL) + return EVP_CipherUpdate(evp, (uint8_t *)output, olen, + (const uint8_t *)input, (size_t)ilen); +#elif defined(USE_CRYPTO_POLARSSL) + return !cipher_update(evp, (const uint8_t *)input, (size_t)ilen, + (uint8_t *)output, (size_t *)olen); +#endif +} + +/* Calculate buffer size required for encrypt/decrypt data */ +size_t ss_calc_buffer_size(int method, size_t ilen) +{ +#if defined(USE_CRYPTO_OPENSSL) + const cipher_kt_t *cipher = get_cipher_type(method); + return EVP_CIPHER_iv_length(cipher) + ilen + EVP_CIPHER_block_size(cipher); +#elif defined(USE_CRYPTO_POLARSSL) + // TODO: implement +#endif +} + +int ss_encrypt(struct enc_ctx *ctx, char *plaintext, size_t plen, + char * ciphertext, size_t * clen) +{ + if (ctx != NULL) { + int err = 1; + int iv_len = 0; + int p_len = plen, c_len = plen; + if (!ctx->init) { + iv_len = ctx->info->iv_len; + } + + if (!ctx->init) { + uint8_t iv[MAX_IV_LENGTH]; + cipher_context_set_iv(ctx->info, &ctx->evp, iv, iv_len, 1); + memcpy(ciphertext, iv, iv_len); + ctx->counter = 0; + ctx->init = 1; + } + + if (ctx->info->method >= SALSA20) { +/* + int padding = ctx->counter % SODIUM_BLOCK_SIZE; + if (buf_len < iv_len + padding + c_len) { + buf_len = max(iv_len + (padding + c_len) * 2, buf_size); + ciphertext = realloc(ciphertext, buf_len); + tmp_len = buf_len; + tmp_buf = ciphertext; + } + if (padding) { + plaintext = realloc(plaintext, max(p_len + padding, buf_size)); + memmove(plaintext + padding, plaintext, p_len); + memset(plaintext, 0, padding); + } + crypto_stream_xor_ic((uint8_t *)(ciphertext + iv_len), + (const uint8_t *)plaintext, + (uint64_t)(p_len + padding), + (const uint8_t *)ctx->evp.iv, + ctx->counter / SODIUM_BLOCK_SIZE, enc_key, + enc_method); + ctx->counter += p_len; + if (padding) { + memmove(ciphertext + iv_len, ciphertext + iv_len + padding, + c_len); + } +*/ + } else { + err = + cipher_context_update(&ctx->evp, + (uint8_t *)(ciphertext + iv_len), + &c_len, (const uint8_t *)plaintext, + p_len); + if (!err) { + return 0; + } + } + +#ifdef DEBUG + dump("PLAIN", plaintext, p_len); + dump("CIPHER", ciphertext + iv_len, c_len); +#endif + + *clen = iv_len + c_len; + return 1; + } else { + char *begin = plaintext; + while (plaintext < begin + plen) { + *ciphertext = (char)ctx->info->enc_table[(uint8_t)*plaintext]; + plaintext++; + ciphertext++; + } + *clen = plen; + return 1; + } +} + +/* You need to ensure you have enough output buffer allocated */ +int ss_decrypt(struct enc_ctx *ctx, char *ciphertext, size_t clen, + char *plaintext, size_t *olen) +{ + if (ctx != NULL) { + int p_len = clen; + int iv_len = 0; + int err = 1; + + if (!ctx->init) { + iv_len = ctx->info->iv_len; + p_len -= iv_len; + cipher_context_set_iv(ctx->info, &ctx->evp, (uint8_t *)ciphertext, iv_len, 0); + ctx->counter = 0; + ctx->init = 1; + } + + if (ctx->info->method >= SALSA20) { +/* + int padding = ctx->counter % SODIUM_BLOCK_SIZE; + if (buf_len < (p_len + padding) * 2) { + buf_len = max((p_len + padding) * 2, buf_size); + plaintext = realloc(plaintext, buf_len); + tmp_len = buf_len; + tmp_buf = plaintext; + } + if (padding) { + ciphertext = + realloc(ciphertext, max(c_len + padding, buf_size)); + memmove(ciphertext + iv_len + padding, ciphertext + iv_len, + c_len - iv_len); + memset(ciphertext + iv_len, 0, padding); + } + crypto_stream_xor_ic((uint8_t *)plaintext, + (const uint8_t *)(ciphertext + iv_len), + (uint64_t)(c_len - iv_len + padding), + (const uint8_t *)ctx->evp.iv, + ctx->counter / SODIUM_BLOCK_SIZE, enc_key, + enc_method); + ctx->counter += c_len - iv_len; + if (padding) { + memmove(plaintext, plaintext + padding, p_len); + } +*/ + } else { + err = cipher_context_update(&ctx->evp, (uint8_t *)plaintext, &p_len, + (const uint8_t *)(ciphertext + iv_len), + clen - iv_len); + } + + if (!err) { +// free(ciphertext); + return 0; + } + + *olen = p_len; + + return 1; + } else { + char *begin = ciphertext; + while (ciphertext < begin + clen) { + *plaintext = (char)ctx->info->dec_table[(uint8_t)*ciphertext]; + ciphertext++; + plaintext++; + } + *olen = clen; + return 1; + } +} + + +void enc_ctx_init(enc_info * info, struct enc_ctx *ctx, int enc) +{ + memset(ctx, 0, sizeof(struct enc_ctx)); + ctx->info = info; + cipher_context_init(info, &ctx->evp, enc); +} + +void enc_ctx_free(struct enc_ctx *ctx) +{ + cipher_context_release(ctx->info, &ctx->evp); +} + +static int enc_key_init(enc_info * info, int method, const char *pass) +{ + if (method <= TABLE || method >= CIPHER_NUM) + return -1; + +#if defined(USE_CRYPTO_OPENSSL) + OpenSSL_add_all_algorithms(); +#endif + + uint8_t iv[MAX_IV_LENGTH]; + + cipher_kt_t *cipher = NULL; + cipher_kt_t cipher_info; + + + if (method == SALSA20 || method == CHACHA20) { +/* + if (sodium_init() == -1) { + //FATAL("Failed to initialize sodium"); + } + // Fake cipher + cipher = (cipher_kt_t *)&cipher_info; +#if defined(USE_CRYPTO_OPENSSL) + cipher->key_len = supported_ciphers_key_size[method]; + cipher->iv_len = supported_ciphers_iv_size[method]; +#endif +#if defined(USE_CRYPTO_POLARSSL) + cipher->base = NULL; + cipher->key_length = supported_ciphers_key_size[method] * 8; + cipher->iv_size = supported_ciphers_iv_size[method]; +#endif +*/ + } else { + cipher = (cipher_kt_t *)get_cipher_type(method); + } + + if (cipher == NULL) { + do { +#if defined(USE_CRYPTO_POLARSSL) && defined(USE_CRYPTO_APPLECC) + if (supported_ciphers_applecc[method] != kCCAlgorithmInvalid) { + cipher_info.base = NULL; + cipher_info.key_length = supported_ciphers_key_size[method] * 8; + cipher_info.iv_size = supported_ciphers_iv_size[method]; + cipher = (cipher_kt_t *)&cipher_info; + break; + } +#endif + return -1; + } while (0); + } + + const digest_type_t *md = get_digest_type("MD5"); + if (md == NULL) + return -1; + + info->key_len = bytes_to_key(cipher, md, (const uint8_t *)pass, info->key, iv); + if (info->key_len == 0) { + //FATAL("Cannot generate key and IV"); + } + if (method == RC4_MD5) { + info->iv_len = 16; + } else { + info->iv_len = cipher_iv_size(cipher); + } + info->method = method; + return method; +} + +int enc_init(enc_info * info, const char *pass, const char *method) +{ + memset((void *)info, 0, sizeof(enc_info)); + int m = TABLE; + if (method != NULL) { + for (m = TABLE; m < CIPHER_NUM; m++) { + if (strcmp(method, supported_ciphers[m]) == 0) { + break; + } + } + if (m >= CIPHER_NUM) { + //LOGE("Invalid cipher name: %s, use table instead", method); + m = TABLE; + } + } + if (m == TABLE) { + enc_table_init(info, pass); + } else { + m = enc_key_init(info, m, pass); + } + return m; +} + +void enc_free(enc_info * info) +{ + if (info->enc_table) + { + free(info->enc_table); + info->enc_table = NULL; + } + if (info->dec_table) + { + free(info->dec_table); + info->dec_table = NULL; + } + +} diff --git a/encrypt.h b/encrypt.h new file mode 100644 index 00000000..982293a0 --- /dev/null +++ b/encrypt.h @@ -0,0 +1,155 @@ +/* + * encrypt.h - Define the enryptor's interface + * + * Copyright (C) 2013 - 2015, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _ENCRYPT_H +#define _ENCRYPT_H + +#ifndef __MINGW32__ +#include +#else + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#endif + +#include +#include +#include +#include + +#if defined(USE_CRYPTO_OPENSSL) + +#include +typedef EVP_CIPHER cipher_kt_t; +typedef EVP_CIPHER_CTX cipher_evp_t; +typedef EVP_MD digest_type_t; +#define MAX_KEY_LENGTH EVP_MAX_KEY_LENGTH +#define MAX_IV_LENGTH EVP_MAX_IV_LENGTH +#define MAX_MD_SIZE EVP_MAX_MD_SIZE + +#elif defined(USE_CRYPTO_POLARSSL) + +#include +#include +typedef cipher_info_t cipher_kt_t; +typedef cipher_context_t cipher_evp_t; +typedef md_info_t digest_type_t; +#define MAX_KEY_LENGTH 64 +#define MAX_IV_LENGTH POLARSSL_MAX_IV_LENGTH +#define MAX_MD_SIZE POLARSSL_MD_MAX_SIZE + +#endif + +#ifdef USE_CRYPTO_APPLECC + +#include + +#define kCCAlgorithmInvalid UINT32_MAX +#define kCCContextValid 0 +#define kCCContextInvalid -1 + +typedef struct { + CCCryptorRef cryptor; + int valid; + CCOperation encrypt; + CCAlgorithm cipher; + CCMode mode; + CCPadding padding; + uint8_t iv[MAX_IV_LENGTH]; + uint8_t key[MAX_KEY_LENGTH]; + size_t iv_len; + size_t key_len; +} cipher_cc_t; + +#endif + +typedef struct { + cipher_evp_t evp; +#ifdef USE_CRYPTO_APPLECC + cipher_cc_t cc; +#endif + uint8_t iv[MAX_IV_LENGTH]; +} cipher_ctx_t; + +#ifdef HAVE_STDINT_H +#include +#elif HAVE_INTTYPES_H +#include +#endif + +//#define SODIUM_BLOCK_SIZE 64 + +#define NONE -1 +#define TABLE 0 +#define RC4 1 +#define RC4_MD5 2 +#define AES_128_CFB 3 +#define AES_192_CFB 4 +#define AES_256_CFB 5 +#define BF_CFB 6 +#define CAMELLIA_128_CFB 7 +#define CAMELLIA_192_CFB 8 +#define CAMELLIA_256_CFB 9 +#define CAST5_CFB 10 +#define DES_CFB 11 +#define IDEA_CFB 12 +#define RC2_CFB 13 +#define SEED_CFB 14 +#define SALSA20 15 +#define CHACHA20 16 + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +typedef struct enc_info_t { + int method; + uint8_t key[MAX_KEY_LENGTH]; + int key_len; + int iv_len; + uint8_t *enc_table; + uint8_t *dec_table; +} enc_info; + +struct enc_ctx { + uint8_t init; + uint64_t counter; + cipher_ctx_t evp; + enc_info * info; +}; + +int enc_init(enc_info * info, const char *pass, const char *method); +void enc_free(enc_info * info); +void enc_ctx_init(enc_info * info, struct enc_ctx *ctx, int enc); +void enc_ctx_free(struct enc_ctx *ctx); +int ss_encrypt(struct enc_ctx *ctx, char *plaintext, size_t plen, + char * ciphertext, size_t * clen); +int ss_decrypt(struct enc_ctx *ctx, char *ciphertext, size_t clen, + char *plaintext, size_t *olen); +size_t ss_calc_buffer_size(int method, size_t ilen); + +#endif // _ENCRYPT_H diff --git a/redsocks.c b/redsocks.c index b1379d4e..5b53423a 100644 --- a/redsocks.c +++ b/redsocks.c @@ -54,6 +54,7 @@ extern relay_subsys http_connect_subsys; extern relay_subsys http_relay_subsys; extern relay_subsys socks4_subsys; extern relay_subsys socks5_subsys; +extern relay_subsys shadowsocks_subsys; static relay_subsys *relay_subsystems[] = { &direct_connect_subsys, @@ -61,6 +62,7 @@ static relay_subsys *relay_subsystems[] = &http_relay_subsys, &socks4_subsys, &socks5_subsys, + &shadowsocks_subsys, }; extern relay_subsys autoproxy_subsys; diff --git a/redsocks.conf.example b/redsocks.conf.example index f946c2b8..002b4aec 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -63,7 +63,7 @@ redsocks { port = 1080; // known types: socks4, socks5, http-connect, http-relay - // New types: direct + // New types: direct, shadowsocks type = socks5; // Specify interface for outgoing connections when 'direct' type @@ -83,7 +83,8 @@ redsocks { // timeout value will be used. timeout = 10; - // login = "foobar"; + // login = "foobar";// field 'login' is reused as encryption + // method of shadowsocks // password = "baz"; } diff --git a/shadowsocks.c b/shadowsocks.c new file mode 100644 index 00000000..686d356c --- /dev/null +++ b/shadowsocks.c @@ -0,0 +1,422 @@ +/* redsocks2 - transparent TCP-to-proxy redirector + * Copyright (C) 2013-2015 Zhuofei Wang + * + * This code is based on redsocks project developed by Leonid Evdokimov. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include "utils.h" +#include "log.h" +#include "redsocks.h" +#include "encrypt.h" + +#define INITIAL_BUFFER_SIZE 8192 + +static const int ss_addrtype_ipv4 = 1; +static const int ss_addrtype_domain = 3; +static const int ss_addrtype_ipv6 = 4; + +typedef enum ss_state_t { + ss_new, + ss_connected, + ss_MAX, +} ss_state; + +typedef struct ss_client_t { + struct enc_ctx e_ctx; + struct enc_ctx d_ctx; +} ss_client; + +typedef struct ss_instance_t { + int init; + int method; + // Clients of each instance share a same buffer for encryption/decryption + void * buff; + size_t buff_size; + enc_info info; +} ss_instance; + +void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg); + +int ss_is_valid_cred(const char *method, const char *password) +{ + if (!method || !password) + return 0; + if (strlen(method) > 255) { + log_error(LOG_WARNING, "Shadowsocks encryption method can't be more than 255 chars."); + return 0; + } + if (strlen(password) > 255) { + log_error(LOG_WARNING, "Shadowsocks encryption password can't be more than 255 chars."); + return 0; + } + return 1; +} + +void ss_client_init(redsocks_client *client) +{ + ss_client *sclient = (void*)(client + 1); + ss_instance * ss = (ss_instance *)(client->instance+1); + + client->state = ss_new; + enc_ctx_init(&ss->info, &sclient->e_ctx, 1); + enc_ctx_init(&ss->info, &sclient->d_ctx, 0); +} + +void ss_client_fini(redsocks_client *client) +{ + ss_client *sclient = (void*)(client + 1); + enc_ctx_free(&sclient->e_ctx); + enc_ctx_free(&sclient->d_ctx); +} + +static void get_shared_buffer(redsocks_client *client, size_t in_size, void **buff, size_t *buff_size) +{ + ss_instance * ss = (ss_instance *)(client->instance+1); + + size_t required = max(ss_calc_buffer_size(ss->method, in_size), + ss_calc_buffer_size(ss->method, in_size)); + if (ss->buff_size < required) + { + ss->buff = realloc(buff, required); + ss->buff_size = required; + } + *buff = ss->buff; + *buff_size = ss->buff_size; +} + +static void encrypt_mem(redsocks_client * client, + char * data, size_t len, + struct bufferevent * to) +{ + ss_client *sclient = (void*)(client + 1); + size_t buff_len; + char * buff; + int rc; + + get_shared_buffer(client, len, (void **)&buff, &buff_len); + if (! data || !len) + return; + rc = ss_encrypt(&sclient->e_ctx, data, len, buff, &buff_len); + if (rc) + { + bufferevent_write(to, buff, buff_len); + } +} + + +static void encrypt_buffer(redsocks_client *client, + struct bufferevent * from, + struct bufferevent * to) +{ + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + char * input; + + if (!input_size) + return; + + input = (char *)evbuffer_pullup(bufferevent_get_input(from), input_size); + encrypt_mem(client, input, input_size, to); + evbuffer_drain(bufferevent_get_input(from), input_size); +} + +static void decrypt_buffer(redsocks_client * client, + struct bufferevent * from, + struct bufferevent * to) +{ + ss_client *sclient = (void*)(client + 1); + struct enc_ctx * ctx = &sclient->d_ctx; + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + size_t buff_len; + char * buff; + char * input; + int rc; + + get_shared_buffer(client, input_size, (void **)&buff, &buff_len); + if (!buff || !input_size) + return; + + input = (char *)evbuffer_pullup(bufferevent_get_input(from), input_size); + rc = ss_decrypt(ctx, input, input_size, buff, &buff_len); + if (rc) + { + bufferevent_write(to, buff, buff_len); + evbuffer_drain(bufferevent_get_input(from), input_size); + } +} + + +static void ss_client_writecb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + struct bufferevent * from = client->relay; + struct bufferevent * to = buffev; + char from_eof = client->relay_evshut & EV_READ; + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); + + assert(buffev == client->client); + redsocks_touch_client(client); + + if (input_size == 0 && from_eof) + { + redsocks_shutdown(client, to, SHUT_WR); + return; + } + + if (client->state == ss_connected) + { + /* encrypt and forward data received from client side */ + if (output_size < to->wm_write.high) + { + if (input_size) + decrypt_buffer(client, from, to); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } + } + else + { + redsocks_drop_client(client); + } +} + +static void ss_client_readcb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + struct bufferevent * from = buffev; + struct bufferevent * to = client->relay; + size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); + + assert(buffev == client->client); + redsocks_touch_client(client); + + if (client->state == ss_connected) + { + /* encrypt and forward data to the other side */ + if (output_size < to->wm_write.high) + { + encrypt_buffer(client, from, to); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } + else + { + if (bufferevent_disable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); + } + } + else + { + redsocks_drop_client(client); + } +} + + +static void ss_relay_writecb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + struct bufferevent * from = client->client; + struct bufferevent * to = buffev; + char from_eof = client->client_evshut & EV_READ; + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); + + assert(buffev == client->relay); + redsocks_touch_client(client); + + if (input_size == 0 && from_eof) + { + redsocks_shutdown(client, to, SHUT_WR); + return; + } + + if (client->state == ss_connected) + { + /* encrypt and forward data received from client side */ + if (output_size < to->wm_write.high) + { + if (input_size) + encrypt_buffer(client, from, to); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } + } + else + { + redsocks_drop_client(client); + } +} + +static void ss_relay_readcb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + struct bufferevent * from = buffev; + struct bufferevent * to = client->client; + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); + + assert(buffev == client->relay); + redsocks_touch_client(client); + + if (client->state == ss_connected) + { + /* decrypt and forward data to client side */ + if (output_size < to->wm_write.high) + { + if (input_size) + decrypt_buffer(client, from, to); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } + else + { + if (bufferevent_disable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); + } + } + else + { + redsocks_drop_client(client); + } +} + +static void ss_relay_connected(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + char buff[512] ; + size_t len = 0; + + assert(buffev == client->relay); + assert(client->state == ss_new); + redsocks_touch_client(client); + + if (!red_is_socket_connected_ok(buffev)) { + redsocks_log_error(client, LOG_DEBUG, "failed to connect to destination"); + redsocks_drop_client(client); + return; + } + + /* We do not need to detect timeouts any more. + The two peers will handle it. */ + bufferevent_set_timeouts(client->relay, NULL, NULL); + + if (!redsocks_start_relay(client)) + { + /* overwrite theread callback to my function */ + bufferevent_setcb(client->client, ss_client_readcb, + ss_client_writecb, + redsocks_event_error, + client); + bufferevent_setcb(client->relay, ss_relay_readcb, + ss_relay_writecb, + redsocks_event_error, + client); + } + else + { + redsocks_log_error(client, LOG_DEBUG, "failed to start relay"); + redsocks_drop_client(client); + return; + } + + /* build and send header */ + // TODO: + buff[len] = ss_addrtype_ipv4; + len += 1; + memcpy(&buff[len], &client->destaddr.sin_addr, sizeof(client->destaddr.sin_addr)); + len += sizeof(client->destaddr.sin_addr); + memcpy(&buff[len], &client->destaddr.sin_port, sizeof(client->destaddr.sin_port)); + len += sizeof(client->destaddr.sin_port); + encrypt_mem(client, &buff[0], len, client->relay); + + client->state = ss_connected; + + // Write any data received from client side to relay. + if (evbuffer_get_length(bufferevent_get_input(client->client))) + ss_relay_writecb(client->relay, client); + return; + +} + + +static void ss_connect_relay(redsocks_client *client) +{ + struct timeval tv; + + tv.tv_sec = client->instance->config.timeout; + tv.tv_usec = 0; + /* use default timeout if timeout is not configured */ + if (tv.tv_sec == 0) + tv.tv_sec = DEFAULT_CONNECT_TIMEOUT; + + client->relay = red_connect_relay2(&client->instance->config.relayaddr, + NULL, ss_relay_connected, redsocks_event_error, client, + &tv); + + if (!client->relay) { + redsocks_log_errno(client, LOG_ERR, "ss_connect_relay"); + redsocks_drop_client(client); + } +} + +static int ss_instance_init(struct redsocks_instance_t *instance) +{ + ss_instance * ss = (ss_instance *)(instance+1); + const redsocks_config *config = &instance->config; + + int valid_cred = ss_is_valid_cred(config->login, config->password); + if (!valid_cred + || (ss->method = enc_init(&ss->info, config->password, config->login), ss->method == -1)) + { + log_error(LOG_ERR, "Invalided encrytion method or password."); + return -1; + } + else + { + log_error(LOG_INFO, "using encryption method: %d", ss->method); + } + /* Setting up shared buffer */ + ss->buff = malloc(INITIAL_BUFFER_SIZE); + ss->buff_size = INITIAL_BUFFER_SIZE; + if (!ss->buff) + return -1; + return 0; +} + +static void ss_instance_fini(struct redsocks_instance_t *instance) +{ + ss_instance * ss = (ss_instance *)(instance + 1); + if (ss->buff) + free (ss->buff); +} + +relay_subsys shadowsocks_subsys = +{ + .name = "shadowsocks", + .payload_len = sizeof(ss_client), + .instance_payload_len = sizeof(ss_instance), + .init = ss_client_init, + .fini = ss_client_fini, + .connect_relay = ss_connect_relay, + .instance_init = ss_instance_init, + .instance_fini = ss_instance_fini, +}; + + +/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ +/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ From 0971e57e3928521839652cd396a63a913e5b0eda Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 20 Feb 2015 01:29:11 +0800 Subject: [PATCH 037/192] Fix: http-relay not work --- autoproxy.c | 11 +++++++++++ redsocks.c | 5 +---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index 874a1cc2..2f021fd0 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -479,7 +479,18 @@ static void auto_retry(redsocks_client * client, int updcache) /* connect to relay */ if (client->instance->relay_ss->connect_relay) + { client->instance->relay_ss->connect_relay(client); + // In case the underline relay system does not connect relay, + // it maybe is waiting for client read event. + // Take 'http-relay' for example. + if (!client->relay && evbuffer_get_length(bufferevent_get_input(client->client))) +#ifdef bufferevent_trigger_event + bufferevent_trigger_event(client->client, EV_READ, 0); +#else + client->client->readcb(client->client, client); +#endif + } else redsocks_connect_relay(client); } diff --git a/redsocks.c b/redsocks.c index 5b53423a..89eff1f0 100644 --- a/redsocks.c +++ b/redsocks.c @@ -597,10 +597,7 @@ void redsocks_relay_connected(struct bufferevent *buffev, void *_arg) client->instance->relay_ss->writecb, redsocks_event_error, client); - if (evbuffer_get_length(bufferevent_get_input(client->client))) - client->instance->relay_ss->writecb(client->relay, client); -// else -// bufferevent_enable(client->relay, EV_READ); + client->instance->relay_ss->writecb(client->relay, client); return; fail: From e1f866b7402e75b05e5a6e40b06483e1d6df98d7 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 24 Feb 2015 23:40:47 +0800 Subject: [PATCH 038/192] Fix: segments fault caused by 'redudp'. --- redudp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redudp.c b/redudp.c index 9378edfa..682498e5 100644 --- a/redudp.c +++ b/redudp.c @@ -552,7 +552,7 @@ static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_ memcpy(&client->clientaddr, clientaddr, sizeof(*clientaddr)); if (destaddr) memcpy(&client->destaddr, destaddr, sizeof(client->destaddr)); - evtimer_set(&client->timeout, redudp_timeout, client); + evtimer_assign(&client->timeout, get_event_base(), redudp_timeout, client); // XXX: self->relay_ss->init(client); client->sender_fd = -1; // it's postponed until socks-server replies to avoid trivial DoS From bf96693cbcade823ae2c5b4f7140f87c84112b45 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 28 Feb 2015 15:23:23 +0800 Subject: [PATCH 039/192] Enhance IP cache to support persistence --- autoproxy.c | 3 +- ipcache.c | 144 +++++++++++++++++++++++++++++++++++++++++- redsocks.conf.example | 12 +++- utils.c | 8 ++- utils.h | 2 +- 5 files changed, 163 insertions(+), 6 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index 2f021fd0..6e84fcb4 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -661,7 +661,8 @@ static void auto_connect_relay(redsocks_client *client) config = get_config(client); // No quick check when the time passed since IP is added to cache is // less than NO_CHECK_SECONDS. Just let it go via proxy. - if (now - *acc_time < config->no_quick_check_seconds) + if (config->no_quick_check_seconds == 0 + || now - *acc_time < config->no_quick_check_seconds) { auto_retry(client, 0); return; diff --git a/ipcache.c b/ipcache.c index de1c7629..981dbfc9 100644 --- a/ipcache.c +++ b/ipcache.c @@ -34,6 +34,7 @@ #define ADDR_PORT_CHECK 1 #define CACHE_ITEM_STALE_SECONDS 60*30 #define MAX_BLOCK_SIZE 32 +#define CACHE_FILE_UPDATE_INTERVAL 3600 * 2 //---------------------------------------------------------------------------------------- @@ -42,6 +43,8 @@ typedef struct cache_config_t { unsigned int cache_size; unsigned int port_check; unsigned int stale_time; + char * cache_file; + unsigned int autosave_interval; // Dynamically calculated values. unsigned int block_size; unsigned int block_count; @@ -51,6 +54,7 @@ static cache_config default_config = { .cache_size = ADDR_CACHE_BLOCKS * ADDR_CACHE_BLOCK_SIZE / 1024, .port_check = ADDR_PORT_CHECK, .stale_time = CACHE_ITEM_STALE_SECONDS, + .autosave_interval = CACHE_FILE_UPDATE_INTERVAL, .block_size = ADDR_CACHE_BLOCK_SIZE, .block_count = ADDR_CACHE_BLOCKS, }; @@ -60,6 +64,8 @@ static parser_entry cache_entries[] = { .key = "cache_size", .type = pt_uint16 }, { .key = "port_check", .type = pt_uint16 }, { .key = "stale_time", .type = pt_uint16 }, + { .key = "cache_file", .type = pt_pchar }, + { .key = "autosave_interval", .type = pt_uint16 }, { } }; @@ -73,6 +79,8 @@ static int cache_onenter(parser_section *section) (strcmp(entry->key, "cache_size") == 0) ? (void*)&config->cache_size: (strcmp(entry->key, "port_check") == 0) ? (void*)&config->port_check: (strcmp(entry->key, "stale_time") == 0) ? (void*)&config->stale_time: + (strcmp(entry->key, "cache_file") == 0) ? (void*)&config->cache_file: + (strcmp(entry->key, "autosave_interval") == 0) ? (void*)&config->autosave_interval: NULL; section->data = config; return 0; @@ -123,6 +131,8 @@ static parser_section cache_conf_section = }; #define block_from_sockaddr_in(addr) (addr->sin_addr.s_addr & (config->block_count -1)) +#define set_cache_changed(changed) (cache_changed = changed) +#define is_cache_changed(changed) (cache_changed != 0) typedef struct cache_data_t { char present; @@ -133,16 +143,111 @@ typedef struct cache_data_t { static int * addr_cache_counters = NULL; static int * addr_cache_pointers = NULL; static cache_data * addr_cache = NULL; +static char cache_changed = 0; +static struct event timer_event; + +static inline cache_data * get_cache_data(unsigned int block, unsigned int index); static inline cache_config * get_config() { return &default_config; } +static int load_cache(const char * path) +{ + FILE * f; + char line[256]; + char * pline = 0; + struct sockaddr_in addr; + unsigned long int port; + char * end; + + if (!path) + return -1; + + f = fopen(path, "r"); + if (!f) + return -1; + while(1) + { + pline = fgets(line, sizeof(line), f); + if (!pline) + break; + memset(&addr, 0, sizeof(addr)); + if (pline[0] == '[') + addr.sin_family = AF_INET6; + else + addr.sin_family = AF_INET; + pline = strrchr(line, ':'); + if (pline) + { + * pline = 0; + pline ++; + port = strtoul(pline, &end, 0); + if (port <= 0xFFFF) + addr.sin_port = htons(port); + else + log_error(LOG_INFO, "Invalid port number in cache file: %s", pline); + } + // TODO: IPv6 Support + if (inet_aton(line, &addr.sin_addr) != 0) + cache_add_addr(&addr); + else + // Invalid address + log_error(LOG_INFO, "Invalid IP address in cache file: %s", line); + } + fclose(f); + return 0; +} + +static int save_cache(const char * path) +{ + FILE * f; + unsigned int blk = 0; + unsigned int idx = 0; + char addr_str[RED_INET_ADDRSTRLEN]; + cache_data * item; + cache_config * config = get_config(); + + + if (!path) + return -1; + + f = fopen(path, "w"); + if (!f) + return -1; + + for (; blk < config->block_count; blk++) + { + for (idx=0; idx < config->block_size; idx++) + { + item = get_cache_data(blk, idx); + if (item && item->present) + { + red_inet_ntop(&item->addr, addr_str, sizeof(addr_str)); + fprintf(f, "%s\n", addr_str); + } + } + } + fclose(f); + return 0; +} + +static void cache_auto_saver(int sig, short what, void *_arg) +{ + cache_config * config = get_config(); + if (is_cache_changed() && config->cache_file) + { + save_cache(config->cache_file); + set_cache_changed(0); + } +} + static int cache_init() { cache_config * config = get_config(); size_t size; + struct timeval tv; size = sizeof(cache_data) * config->block_size * config->block_count; if (!addr_cache) @@ -164,11 +269,41 @@ static int cache_init() addr_cache_pointers = malloc(size); } memset((void *)addr_cache_pointers, 0, size); + + memset(&timer_event, 0, sizeof(timer_event)); + if (config->cache_file) + { + if (load_cache(config->cache_file)) + log_error(LOG_INFO, "Failed to load IP addresses from cache file: %s", config->cache_file); + + // start timer to save cache into file periodically. + if (config->autosave_interval) + { + tv.tv_sec = config->autosave_interval; + tv.tv_usec = 0; + event_assign(&timer_event, get_event_base(), 0, EV_TIMEOUT|EV_PERSIST, cache_auto_saver, NULL); + evtimer_add(&timer_event, &tv); + } + } + set_cache_changed(0); + return 0; } static int cache_fini() { + cache_config * config = get_config(); + // Update cache file before exit + if (config->autosave_interval && is_cache_changed() && config->cache_file) + { + save_cache(config->cache_file); + set_cache_changed(0); + } + if (event_initialized(&timer_event)) + { + evtimer_del(&timer_event); + } + // Free buffers allocated for cache if (addr_cache) { free(addr_cache); @@ -216,9 +351,11 @@ static cache_data * get_cache_item(const struct sockaddr_in * addr) config->port_check)) { // Remove stale item - if (item->access_time + config->stale_time < now) + if (config->stale_time > 0 + && item->access_time + config->stale_time < now) { item->present = 0; + set_cache_changed(1); return NULL; } return item; @@ -261,13 +398,18 @@ void cache_add_addr(const struct sockaddr_in * addr) item->access_time = redsocks_time(NULL); addr_cache_pointers[block]++; addr_cache_pointers[block] %= config->block_size; + + set_cache_changed(1); } void cache_del_addr(const struct sockaddr_in * addr) { cache_data * item = get_cache_item(addr); if (item) + { item->present = 0; + set_cache_changed(1); + } } #define ADDR_COUNT_PER_LINE 4 diff --git a/redsocks.conf.example b/redsocks.conf.example index 002b4aec..3833172b 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -129,6 +129,10 @@ autoproxy { // is found blocked in cache and it has been // added into cache no earlier than this // specified number of seconds. + // Set it to 0 if you do not want to perform + // quick check when an IP is found in blocked + // IP cache, thus the connection will be + // redirected to proxy immediately. quick_connect_timeout = 3; // Timeout value when performing quick // connection check if an IP is found blocked // in cache. @@ -138,8 +142,14 @@ ipcache { // Configure IP cache cache_size = 4; // Maximum number of IP's in 1K. stale_time = 900; // Seconds to stale an IP in cache since it is added - // into cahce + // into cahce. + // Set it to 0 to disable cache stale. port_check = 1; // Whether to distinguish port number in address + cache_file = "/tmp/ipcache.txt"; // File used to store blocked IP's in cache. + autosave_interval = 3600; // Interval for saving ip cache into file. + // Set it to 0 to disable autosave. + // When autosave_interval and stale_time are both 0, IP cache behaves like + // a static blacklist. } // you can add more `redsocks' and `redudp' sections if you need. diff --git a/utils.c b/utils.c index d19f9ed9..61b62f47 100644 --- a/utils.c +++ b/utils.c @@ -323,13 +323,17 @@ char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_si port = ((struct sockaddr_in*)sa)->sin_port; } else if (sa->sin_family == AF_INET6) { - retval = inet_ntop(AF_INET6, &((const struct sockaddr_in6*)sa)->sin6_addr, buffer, buffer_size); + buffer[0] = '['; + retval = inet_ntop(AF_INET6, &((const struct sockaddr_in6*)sa)->sin6_addr, buffer+1, buffer_size-1); port = ((struct sockaddr_in6*)sa)->sin6_port; } if (retval) { assert(retval == buffer); len = strlen(retval); - snprintf(buffer + len, buffer_size - len, ":%d", ntohs(port)); + if (sa->sin_family == AF_INET6) + snprintf(buffer + len, buffer_size - len, "]:%d", ntohs(port)); + else + snprintf(buffer + len, buffer_size - len, ":%d", ntohs(port)); } else { strcpy(buffer, placeholder); diff --git a/utils.h b/utils.h index 1188b59d..d7139f09 100644 --- a/utils.h +++ b/utils.h @@ -70,7 +70,7 @@ size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, s #if INET6_ADDRSTRLEN < INET_ADDRSTRLEN # error Impossible happens: INET6_ADDRSTRLEN < INET_ADDRSTRLEN #else -# define RED_INET_ADDRSTRLEN (INET6_ADDRSTRLEN + 1 + 5 + 1) // addr + : + port + \0 +# define RED_INET_ADDRSTRLEN (1 + INET6_ADDRSTRLEN + 1 + 1 + 5 + 1) // [ + addr + ] + : + port + \0 #endif char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_size); From 60fe38b9f7769f1e44fa07f78f198cee960b0db4 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 28 Feb 2015 22:34:07 +0800 Subject: [PATCH 040/192] Fix bugs and increase performance of shadowsocks. --- encrypt.c | 12 +++++++----- shadowsocks.c | 36 +++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/encrypt.c b/encrypt.c index e31a30c6..02882fb5 100644 --- a/encrypt.c +++ b/encrypt.c @@ -734,7 +734,10 @@ size_t ss_calc_buffer_size(int method, size_t ilen) const cipher_kt_t *cipher = get_cipher_type(method); return EVP_CIPHER_iv_length(cipher) + ilen + EVP_CIPHER_block_size(cipher); #elif defined(USE_CRYPTO_POLARSSL) - // TODO: implement + if (cipher == NULL) { + return 0; + } + return cipher->iv_size + ilen + cipher_get_block_size(cipher); #endif } @@ -974,10 +977,9 @@ int enc_init(enc_info * info, const char *pass, const char *method) break; } } - if (m >= CIPHER_NUM) { - //LOGE("Invalid cipher name: %s, use table instead", method); - m = TABLE; - } + if (m >= CIPHER_NUM) + // Invalid encryption method + return -1; } if (m == TABLE) { enc_table_init(info, pass); diff --git a/shadowsocks.c b/shadowsocks.c index 686d356c..b547707c 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -84,19 +84,23 @@ void ss_client_fini(redsocks_client *client) enc_ctx_free(&sclient->d_ctx); } -static void get_shared_buffer(redsocks_client *client, size_t in_size, void **buff, size_t *buff_size) +static int get_shared_buffer(redsocks_client *client, size_t in_size, void **buff, size_t *buff_size) { ss_instance * ss = (ss_instance *)(client->instance+1); + void * tmp; - size_t required = max(ss_calc_buffer_size(ss->method, in_size), - ss_calc_buffer_size(ss->method, in_size)); + size_t required = ss_calc_buffer_size(ss->method, in_size); if (ss->buff_size < required) { - ss->buff = realloc(buff, required); + tmp = realloc(buff, required); + if (!tmp) + return -1; + ss->buff = tmp; ss->buff_size = required; } *buff = ss->buff; *buff_size = ss->buff_size; + return 0; } static void encrypt_mem(redsocks_client * client, @@ -108,8 +112,8 @@ static void encrypt_mem(redsocks_client * client, char * buff; int rc; - get_shared_buffer(client, len, (void **)&buff, &buff_len); - if (! data || !len) + rc = get_shared_buffer(client, len, (void **)&buff, &buff_len); + if (rc || !data || !len) return; rc = ss_encrypt(&sclient->e_ctx, data, len, buff, &buff_len); if (rc) @@ -123,7 +127,8 @@ static void encrypt_buffer(redsocks_client *client, struct bufferevent * from, struct bufferevent * to) { - size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + // To reduce memory copy, just encrypt one block a time + size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from)); char * input; if (!input_size) @@ -140,14 +145,15 @@ static void decrypt_buffer(redsocks_client * client, { ss_client *sclient = (void*)(client + 1); struct enc_ctx * ctx = &sclient->d_ctx; - size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + // To reduce memory copy, just decrypt one block a time + size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from)); size_t buff_len; char * buff; char * input; int rc; - get_shared_buffer(client, input_size, (void **)&buff, &buff_len); - if (!buff || !input_size) + rc = get_shared_buffer(client, input_size, (void **)&buff, &buff_len); + if (rc || !buff || !input_size) return; input = (char *)evbuffer_pullup(bufferevent_get_input(from), input_size); @@ -166,7 +172,7 @@ static void ss_client_writecb(struct bufferevent *buffev, void *_arg) struct bufferevent * from = client->relay; struct bufferevent * to = buffev; char from_eof = client->relay_evshut & EV_READ; - size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from)); size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); assert(buffev == client->client); @@ -233,7 +239,7 @@ static void ss_relay_writecb(struct bufferevent *buffev, void *_arg) struct bufferevent * from = client->client; struct bufferevent * to = buffev; char from_eof = client->client_evshut & EV_READ; - size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from)); size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); assert(buffev == client->relay); @@ -267,7 +273,7 @@ static void ss_relay_readcb(struct bufferevent *buffev, void *_arg) redsocks_client *client = _arg; struct bufferevent * from = buffev; struct bufferevent * to = client->client; - size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from)); size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); assert(buffev == client->relay); @@ -335,7 +341,7 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) } /* build and send header */ - // TODO: + // TODO: Better implementation and IPv6 Support buff[len] = ss_addrtype_ipv4; len += 1; memcpy(&buff[len], &client->destaddr.sin_addr, sizeof(client->destaddr.sin_addr)); @@ -388,7 +394,7 @@ static int ss_instance_init(struct redsocks_instance_t *instance) } else { - log_error(LOG_INFO, "using encryption method: %d", ss->method); + log_error(LOG_INFO, "using encryption method: %s", config->login); } /* Setting up shared buffer */ ss->buff = malloc(INITIAL_BUFFER_SIZE); From a1f86a2ee60bde84a5b8202219e8fa4f7e7263d9 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 1 Mar 2015 10:44:32 +0800 Subject: [PATCH 041/192] Improve performance of shadowsocks --- shadowsocks.c | 86 +++++++++++++++++---------------------------------- 1 file changed, 28 insertions(+), 58 deletions(-) diff --git a/shadowsocks.c b/shadowsocks.c index b547707c..9e63b16e 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -44,9 +44,6 @@ typedef struct ss_client_t { typedef struct ss_instance_t { int init; int method; - // Clients of each instance share a same buffer for encryption/decryption - void * buff; - size_t buff_size; enc_info info; } ss_instance; @@ -84,41 +81,30 @@ void ss_client_fini(redsocks_client *client) enc_ctx_free(&sclient->d_ctx); } -static int get_shared_buffer(redsocks_client *client, size_t in_size, void **buff, size_t *buff_size) -{ - ss_instance * ss = (ss_instance *)(client->instance+1); - void * tmp; - - size_t required = ss_calc_buffer_size(ss->method, in_size); - if (ss->buff_size < required) - { - tmp = realloc(buff, required); - if (!tmp) - return -1; - ss->buff = tmp; - ss->buff_size = required; - } - *buff = ss->buff; - *buff_size = ss->buff_size; - return 0; -} - static void encrypt_mem(redsocks_client * client, char * data, size_t len, - struct bufferevent * to) + struct bufferevent * to, int decrypt) { ss_client *sclient = (void*)(client + 1); - size_t buff_len; - char * buff; + ss_instance * ss = (ss_instance *)(client->instance+1); + struct evbuffer_iovec vec; + struct evbuffer * buf_out = bufferevent_get_output(to); + size_t required; int rc; - rc = get_shared_buffer(client, len, (void **)&buff, &buff_len); - if (rc || !data || !len) + if (!len || !data) return; - rc = ss_encrypt(&sclient->e_ctx, data, len, buff, &buff_len); - if (rc) + + required = ss_calc_buffer_size(ss->method, len); + if (required && evbuffer_reserve_space(buf_out, required, &vec, 1) == 1) { - bufferevent_write(to, buff, buff_len); + if (decrypt) + rc = ss_decrypt(&sclient->e_ctx, data, len, vec.iov_base, &vec.iov_len); + else + rc = ss_encrypt(&sclient->e_ctx, data, len, vec.iov_base, &vec.iov_len); + if (!rc) + vec.iov_len = 0; + evbuffer_commit_space(buf_out, &vec, 1); } } @@ -128,41 +114,33 @@ static void encrypt_buffer(redsocks_client *client, struct bufferevent * to) { // To reduce memory copy, just encrypt one block a time - size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from)); + struct evbuffer * buf_in = bufferevent_get_input(from); + size_t input_size = evbuffer_get_contiguous_space(buf_in); char * input; if (!input_size) return; - input = (char *)evbuffer_pullup(bufferevent_get_input(from), input_size); - encrypt_mem(client, input, input_size, to); - evbuffer_drain(bufferevent_get_input(from), input_size); + input = (char *)evbuffer_pullup(buf_in, input_size); + encrypt_mem(client, input, input_size, to, 0); + evbuffer_drain(buf_in, input_size); } static void decrypt_buffer(redsocks_client * client, struct bufferevent * from, struct bufferevent * to) { - ss_client *sclient = (void*)(client + 1); - struct enc_ctx * ctx = &sclient->d_ctx; // To reduce memory copy, just decrypt one block a time - size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from)); - size_t buff_len; - char * buff; + struct evbuffer * buf_in = bufferevent_get_input(from); + size_t input_size = evbuffer_get_contiguous_space(buf_in); char * input; - int rc; - rc = get_shared_buffer(client, input_size, (void **)&buff, &buff_len); - if (rc || !buff || !input_size) + if (!input_size) return; - input = (char *)evbuffer_pullup(bufferevent_get_input(from), input_size); - rc = ss_decrypt(ctx, input, input_size, buff, &buff_len); - if (rc) - { - bufferevent_write(to, buff, buff_len); - evbuffer_drain(bufferevent_get_input(from), input_size); - } + input = (char *)evbuffer_pullup(buf_in, input_size); + encrypt_mem(client, input, input_size, to, 1); + evbuffer_drain(buf_in, input_size); } @@ -348,7 +326,7 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) len += sizeof(client->destaddr.sin_addr); memcpy(&buff[len], &client->destaddr.sin_port, sizeof(client->destaddr.sin_port)); len += sizeof(client->destaddr.sin_port); - encrypt_mem(client, &buff[0], len, client->relay); + encrypt_mem(client, &buff[0], len, client->relay, 0); client->state = ss_connected; @@ -396,19 +374,11 @@ static int ss_instance_init(struct redsocks_instance_t *instance) { log_error(LOG_INFO, "using encryption method: %s", config->login); } - /* Setting up shared buffer */ - ss->buff = malloc(INITIAL_BUFFER_SIZE); - ss->buff_size = INITIAL_BUFFER_SIZE; - if (!ss->buff) - return -1; return 0; } static void ss_instance_fini(struct redsocks_instance_t *instance) { - ss_instance * ss = (ss_instance *)(instance + 1); - if (ss->buff) - free (ss->buff); } relay_subsys shadowsocks_subsys = From 9277d6ba1f64bd806270ed1aca2b992c1c889f09 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 1 Mar 2015 21:53:49 +0800 Subject: [PATCH 042/192] Fix decryption failure in shadowsocks --- shadowsocks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks.c b/shadowsocks.c index 9e63b16e..81d9dc5b 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -99,7 +99,7 @@ static void encrypt_mem(redsocks_client * client, if (required && evbuffer_reserve_space(buf_out, required, &vec, 1) == 1) { if (decrypt) - rc = ss_decrypt(&sclient->e_ctx, data, len, vec.iov_base, &vec.iov_len); + rc = ss_decrypt(&sclient->d_ctx, data, len, vec.iov_base, &vec.iov_len); else rc = ss_encrypt(&sclient->e_ctx, data, len, vec.iov_base, &vec.iov_len); if (!rc) From 79f52d97e3c0d3ce269a3a6d408260bd9a44a405 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 7 Mar 2015 08:55:24 +0000 Subject: [PATCH 043/192] Change README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 84c49d69..782a608f 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,11 @@ need of blacklist. 4. Redirect TCP connections without proxy. 5. Redirect TCP connections via specified network interface. -If you feel my work done is helpful, please consider donation. Thanks. +If you feel my work done is helpful, please consider donation. Thanks. **Accept donations by AliPay with account ** +[Chinese Reference](https://github.com/semigodking/redsocks/wiki) + HOW it works ------------ Anyone can help me to complete this part? From eb6fe2fe5de7ecbe6be7bae975a23fc6ba1a5e90 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 18 Mar 2015 09:52:46 +0800 Subject: [PATCH 044/192] Fix: segment fault when subsystem initialization fail --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index 91459b92..cb673a2e 100644 --- a/main.c +++ b/main.c @@ -145,6 +145,7 @@ int main(int argc, char **argv) if (!g_event_base) return EXIT_FAILURE; + memset(&dumper, 0, sizeof(dumper)); memset(terminators, 0, sizeof(terminators)); FOREACH(ss, subsystems) { @@ -174,7 +175,6 @@ int main(int argc, char **argv) } } - memset(&dumper, 0, sizeof(dumper)); evsignal_assign(&dumper, get_event_base(), SIGUSR1, dump_handler, NULL); if (evsignal_add(&dumper, NULL) != 0) { log_errno(LOG_ERR, "evsignal_add"); From ddfc411972cbf2d7c7487560fd19688a50662374 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 19 Mar 2015 17:41:32 +0800 Subject: [PATCH 045/192] Refine code for shadowsocks TCP. --- encrypt.c | 19 +++++++++++++------ encrypt.h | 2 +- shadowsocks.c | 25 +++++++++++-------------- shadowsocks.h | 16 ++++++++++++++++ 4 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 shadowsocks.h diff --git a/encrypt.c b/encrypt.c index 02882fb5..e1f9ad9a 100644 --- a/encrypt.c +++ b/encrypt.c @@ -728,16 +728,23 @@ static int cipher_context_update(cipher_ctx_t *ctx, uint8_t *output, int *olen, } /* Calculate buffer size required for encrypt/decrypt data */ -size_t ss_calc_buffer_size(int method, size_t ilen) +size_t ss_calc_buffer_size(struct enc_ctx * ctx, size_t ilen) { -#if defined(USE_CRYPTO_OPENSSL) + int method = ctx->info->method; const cipher_kt_t *cipher = get_cipher_type(method); - return EVP_CIPHER_iv_length(cipher) + ilen + EVP_CIPHER_block_size(cipher); +#if defined(USE_CRYPTO_OPENSSL) + if (ctx->init) + return ilen + EVP_CIPHER_block_size(cipher); + else + return EVP_CIPHER_iv_length(cipher) + ilen + EVP_CIPHER_block_size(cipher); #elif defined(USE_CRYPTO_POLARSSL) if (cipher == NULL) { - return 0; + return ilen; } - return cipher->iv_size + ilen + cipher_get_block_size(cipher); + if (ctx->init) + return ilen + cipher_get_block_size(cipher); + else + return cipher->iv_size + ilen + cipher_get_block_size(cipher); #endif } @@ -886,7 +893,6 @@ int ss_decrypt(struct enc_ctx *ctx, char *ciphertext, size_t clen, } } - void enc_ctx_init(enc_info * info, struct enc_ctx *ctx, int enc) { memset(ctx, 0, sizeof(struct enc_ctx)); @@ -957,6 +963,7 @@ static int enc_key_init(enc_info * info, int method, const char *pass) info->key_len = bytes_to_key(cipher, md, (const uint8_t *)pass, info->key, iv); if (info->key_len == 0) { //FATAL("Cannot generate key and IV"); + return -1; } if (method == RC4_MD5) { info->iv_len = 16; diff --git a/encrypt.h b/encrypt.h index 982293a0..e587892b 100644 --- a/encrypt.h +++ b/encrypt.h @@ -150,6 +150,6 @@ int ss_encrypt(struct enc_ctx *ctx, char *plaintext, size_t plen, char * ciphertext, size_t * clen); int ss_decrypt(struct enc_ctx *ctx, char *ciphertext, size_t clen, char *plaintext, size_t *olen); -size_t ss_calc_buffer_size(int method, size_t ilen); +size_t ss_calc_buffer_size(struct enc_ctx *ctx, size_t ilen); #endif // _ENCRYPT_H diff --git a/shadowsocks.c b/shadowsocks.c index 81d9dc5b..f7dead48 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -23,13 +23,10 @@ #include "log.h" #include "redsocks.h" #include "encrypt.h" +#include "shadowsocks.h" #define INITIAL_BUFFER_SIZE 8192 -static const int ss_addrtype_ipv4 = 1; -static const int ss_addrtype_domain = 3; -static const int ss_addrtype_ipv6 = 4; - typedef enum ss_state_t { ss_new, ss_connected, @@ -86,7 +83,6 @@ static void encrypt_mem(redsocks_client * client, struct bufferevent * to, int decrypt) { ss_client *sclient = (void*)(client + 1); - ss_instance * ss = (ss_instance *)(client->instance+1); struct evbuffer_iovec vec; struct evbuffer * buf_out = bufferevent_get_output(to); size_t required; @@ -95,7 +91,10 @@ static void encrypt_mem(redsocks_client * client, if (!len || !data) return; - required = ss_calc_buffer_size(ss->method, len); + if (decrypt) + required = ss_calc_buffer_size(&sclient->d_ctx, len); + else + required = ss_calc_buffer_size(&sclient->e_ctx, len); if (required && evbuffer_reserve_space(buf_out, required, &vec, 1) == 1) { if (decrypt) @@ -282,7 +281,7 @@ static void ss_relay_readcb(struct bufferevent *buffev, void *_arg) static void ss_relay_connected(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; - char buff[512] ; + ss_header_ipv4 header; size_t len = 0; assert(buffev == client->relay); @@ -320,13 +319,11 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) /* build and send header */ // TODO: Better implementation and IPv6 Support - buff[len] = ss_addrtype_ipv4; - len += 1; - memcpy(&buff[len], &client->destaddr.sin_addr, sizeof(client->destaddr.sin_addr)); - len += sizeof(client->destaddr.sin_addr); - memcpy(&buff[len], &client->destaddr.sin_port, sizeof(client->destaddr.sin_port)); - len += sizeof(client->destaddr.sin_port); - encrypt_mem(client, &buff[0], len, client->relay, 0); + header.addr_type = ss_addrtype_ipv4; + header.addr = client->destaddr.sin_addr.s_addr; + header.port = client->destaddr.sin_port; + len += sizeof(header); + encrypt_mem(client, (char *)&header, len, client->relay, 0); client->state = ss_connected; diff --git a/shadowsocks.h b/shadowsocks.h new file mode 100644 index 00000000..3265e3af --- /dev/null +++ b/shadowsocks.h @@ -0,0 +1,16 @@ +#ifndef SHADOWSOCKS_H +#define SHADOWSOCKS_H + +enum { + ss_addrtype_ipv4 = 1, + ss_addrtype_domain = 3, + ss_addrtype_ipv6 = 4, +}; +typedef struct ss_header_ipv4_t { + unsigned char addr_type; + uint32_t addr; + uint16_t port; +} PACKED ss_header_ipv4; + + +#endif From df555c96dde11c67b18581526458f6bee4bb1a37 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 23 Mar 2015 21:34:40 +0800 Subject: [PATCH 046/192] Simplify IP parsing and correct subsystem init sequence --- ipcache.c | 27 +++++---------------------- main.c | 6 +++--- 2 files changed, 8 insertions(+), 25 deletions(-) diff --git a/ipcache.c b/ipcache.c index 981dbfc9..0723a338 100644 --- a/ipcache.c +++ b/ipcache.c @@ -158,9 +158,9 @@ static int load_cache(const char * path) FILE * f; char line[256]; char * pline = 0; + // TODO: IPv6 Support struct sockaddr_in addr; - unsigned long int port; - char * end; + int addr_size; if (!path) return -1; @@ -173,28 +173,11 @@ static int load_cache(const char * path) pline = fgets(line, sizeof(line), f); if (!pline) break; - memset(&addr, 0, sizeof(addr)); - if (pline[0] == '[') - addr.sin_family = AF_INET6; + addr_size = sizeof(addr); + if (evutil_parse_sockaddr_port(pline, (struct sockaddr *)&addr, &addr_size)) + log_error(LOG_INFO, "Invalid IP address: %s", line); else - addr.sin_family = AF_INET; - pline = strrchr(line, ':'); - if (pline) - { - * pline = 0; - pline ++; - port = strtoul(pline, &end, 0); - if (port <= 0xFFFF) - addr.sin_port = htons(port); - else - log_error(LOG_INFO, "Invalid port number in cache file: %s", pline); - } - // TODO: IPv6 Support - if (inet_aton(line, &addr.sin_addr) != 0) cache_add_addr(&addr); - else - // Invalid address - log_error(LOG_INFO, "Invalid IP address in cache file: %s", line); } fclose(f); return 0; diff --git a/main.c b/main.c index cb673a2e..d73ca4ff 100644 --- a/main.c +++ b/main.c @@ -35,10 +35,10 @@ extern app_subsys autoproxy_app_subsys; extern app_subsys cache_app_subsys; app_subsys *subsystems[] = { - &redsocks_subsys, &base_subsys, + &redsocks_subsys, &autoproxy_app_subsys, - &cache_app_subsys, + &cache_app_subsys, &redudp_subsys, &tcpdns_subsys, }; @@ -145,7 +145,7 @@ int main(int argc, char **argv) if (!g_event_base) return EXIT_FAILURE; - memset(&dumper, 0, sizeof(dumper)); + memset(&dumper, 0, sizeof(dumper)); memset(terminators, 0, sizeof(terminators)); FOREACH(ss, subsystems) { From c3e3d60cec44724c3ca57a5d85f9da43e4be0958 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 1 Apr 2015 14:06:53 +0800 Subject: [PATCH 047/192] Decouple proxy impl. for UDP from redudp like redsocks --- Makefile | 4 +- redsocks.conf.example | 8 +- redudp.c | 1155 ++++++++++++++++------------------------- redudp.h | 38 +- socks5-udp.c | 419 +++++++++++++++ utils.c | 20 - utils.h | 2 - 7 files changed, 899 insertions(+), 747 deletions(-) create mode 100644 socks5-udp.c diff --git a/Makefile b/Makefile index 08705eba..f3ff5e2d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ -OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o encrypt.o shadowsocks.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o tcpdns.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o encrypt.o shadowsocks.o http-connect.o \ + socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o socks5-udp.o \ + tcpdns.o gen/version.o SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend diff --git a/redsocks.conf.example b/redsocks.conf.example index 3833172b..d6fdd4ae 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -101,6 +101,9 @@ redudp { login = username; password = pazzw0rd; + // know types: socks5 + type = socks5; + // kernel does not give us this information, so we have to duplicate it // in both iptables rules and configuration file. By the way, you can // set `local_ip' to 127.45.67.89 if you need more than 65535 ports to @@ -109,8 +112,11 @@ redudp { dest_ip = 8.8.8.8; dest_port = 53; + // Do not set it large if this section is for DNS requests. Otherwise, + // you may encounter out of file descriptor problem. For DNS requests, + // 10s is adequate. udp_timeout = 30; - udp_timeout_stream = 180; + // udp_timeout_stream = 180; } tcpdns { diff --git a/redudp.c b/redudp.c index 682498e5..fbec5b74 100644 --- a/redudp.c +++ b/redudp.c @@ -34,56 +34,35 @@ #include "redudp.h" /* Just in case the IP_TRANSPARENT define isn't included somehow */ - -#if !defined(IP_TRANSPARENT) -#define IP_TRANSPARENT 19 -#define IP_ORIGDSTADDR 20 -#define IP_RECVORIGDSTADDR IP_ORIGDSTADDR -#endif - -/* Just in case the IP_TRANSPARENT define isn't included somehow */ - -#if !defined(IP_TRANSPARENT) -#define IP_TRANSPARENT 19 -#define IP_ORIGDSTADDR 20 -#define IP_RECVORIGDSTADDR IP_ORIGDSTADDR -#endif - -/* Just in case the IP_TRANSPARENT define isn't included somehow */ - #if !defined(IP_TRANSPARENT) #define IP_TRANSPARENT 19 #define IP_ORIGDSTADDR 20 #define IP_RECVORIGDSTADDR IP_ORIGDSTADDR #endif -#define redudp_log_error(client, prio, msg...) \ - redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &(client)->clientaddr, get_destaddr(client), prio, ## msg) -#define redudp_log_errno(client, prio, msg...) \ - redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &(client)->clientaddr, get_destaddr(client), prio, ## msg) +// Multiple instances share the same buffer for message receiving +static char recv_buff[64*1024];// max size of UDP packet is less than 64K -static void redudp_pkt_from_socks(int fd, short what, void *_arg); -static void redudp_drop_client(redudp_client *client); static void redudp_fini_instance(redudp_instance *instance); static int redudp_fini(); static int redudp_transparent(int fd); -typedef struct redudp_expected_assoc_reply_t { - socks5_reply h; - socks5_addr_ipv4 ip; -} PACKED redudp_expected_assoc_reply; - struct bound_udp4_key { - struct in_addr sin_addr; - uint16_t sin_port; + struct in_addr sin_addr; + uint16_t sin_port; }; struct bound_udp4 { - struct bound_udp4_key key; - int ref; - int fd; + struct bound_udp4_key key; + int ref; + int fd; }; +extern udprelay_subsys socks5_udp_subsys; +static udprelay_subsys *relay_subsystems[] = +{ + &socks5_udp_subsys, +}; /*********************************************************************** * Helpers */ @@ -92,603 +71,306 @@ static void* root_bound_udp4 = NULL; // to avoid two binds to same IP:port static int bound_udp4_cmp(const void *a, const void *b) { - return memcmp(a, b, sizeof(struct bound_udp4_key)); + return memcmp(a, b, sizeof(struct bound_udp4_key)); } static void bound_udp4_mkkey(struct bound_udp4_key *key, const struct sockaddr_in *addr) { - memset(key, 0, sizeof(*key)); - key->sin_addr = addr->sin_addr; - key->sin_port = addr->sin_port; + memset(key, 0, sizeof(*key)); + key->sin_addr = addr->sin_addr; + key->sin_port = addr->sin_port; } static int bound_udp4_get(const struct sockaddr_in *addr) { - struct bound_udp4_key key; - struct bound_udp4 *node, **pnode; - - bound_udp4_mkkey(&key, addr); - // I assume, that memory allocation for lookup is awful, so I use - // tfind/tsearch pair instead of tsearch/check-result. - pnode = tfind(&key, &root_bound_udp4, bound_udp4_cmp); - if (pnode) { - assert((*pnode)->ref > 0); - (*pnode)->ref++; - return (*pnode)->fd; - } - - node = calloc(1, sizeof(*node)); - if (!node) { - log_errno(LOG_ERR, "calloc"); - goto fail; - } - - node->key = key; - node->ref = 1; - node->fd = socket(AF_INET, SOCK_DGRAM, 0); - if (node->fd == -1) { - log_errno(LOG_ERR, "socket"); - goto fail; - } - - if (0 != redudp_transparent(node->fd)) - goto fail; - - if (0 != bind(node->fd, (struct sockaddr*)addr, sizeof(*addr))) { - log_errno(LOG_ERR, "bind"); - goto fail; - } - - pnode = tsearch(node, &root_bound_udp4, bound_udp4_cmp); - if (!pnode) { - log_errno(LOG_ERR, "tsearch(%p) == %p", node, pnode); - goto fail; - } - assert(node == *pnode); - - return node->fd; + struct bound_udp4_key key; + struct bound_udp4 *node, **pnode; + + bound_udp4_mkkey(&key, addr); + // I assume, that memory allocation for lookup is awful, so I use + // tfind/tsearch pair instead of tsearch/check-result. + pnode = tfind(&key, &root_bound_udp4, bound_udp4_cmp); + if (pnode) { + assert((*pnode)->ref > 0); + (*pnode)->ref++; + return (*pnode)->fd; + } + + node = calloc(1, sizeof(*node)); + if (!node) { + log_errno(LOG_ERR, "calloc"); + goto fail; + } + + node->key = key; + node->ref = 1; + node->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (node->fd == -1) { + log_errno(LOG_ERR, "socket"); + goto fail; + } + + if (0 != redudp_transparent(node->fd)) + goto fail; + + if (0 != bind(node->fd, (struct sockaddr*)addr, sizeof(*addr))) { + log_errno(LOG_ERR, "bind"); + goto fail; + } + + pnode = tsearch(node, &root_bound_udp4, bound_udp4_cmp); + if (!pnode) { + log_errno(LOG_ERR, "tsearch(%p) == %p", node, pnode); + goto fail; + } + assert(node == *pnode); + + return node->fd; fail: - if (node) { - if (node->fd != -1) - redsocks_close(node->fd); - free(node); - } - return -1; + if (node) { + if (node->fd != -1) + redsocks_close(node->fd); + free(node); + } + return -1; } static void bound_udp4_put(const struct sockaddr_in *addr) { - struct bound_udp4_key key; - struct bound_udp4 **pnode, *node; - void *parent; + struct bound_udp4_key key; + struct bound_udp4 **pnode, *node; + void *parent; - bound_udp4_mkkey(&key, addr); - pnode = tfind(&key, &root_bound_udp4, bound_udp4_cmp); - assert(pnode && (*pnode)->ref > 0); + bound_udp4_mkkey(&key, addr); + pnode = tfind(&key, &root_bound_udp4, bound_udp4_cmp); + assert(pnode && (*pnode)->ref > 0); - node = *pnode; + node = *pnode; - node->ref--; - if (node->ref) - return; + node->ref--; + if (node->ref) + return; - parent = tdelete(node, &root_bound_udp4, bound_udp4_cmp); - assert(parent); + parent = tdelete(node, &root_bound_udp4, bound_udp4_cmp); + assert(parent); - redsocks_close(node->fd); // expanding `pnode` to avoid use after free - free(node); + redsocks_close(node->fd); // expanding `pnode` to avoid use after free + free(node); } static int redudp_transparent(int fd) { - int on = 1; - int error = setsockopt(fd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); - if (error) - log_errno(LOG_ERR, "setsockopt(..., SOL_IP, IP_TRANSPARENT)"); - return error; + int on = 1; + int error = setsockopt(fd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); + if (error) + log_errno(LOG_ERR, "setsockopt(..., SOL_IP, IP_TRANSPARENT)"); + return error; } static int do_tproxy(redudp_instance* instance) { - return instance->config.destaddr.sin_addr.s_addr == 0; -} - -static struct sockaddr_in* get_destaddr(redudp_client *client) -{ - if (do_tproxy(client->instance)) - return &client->destaddr; - else - return &client->instance->config.destaddr; -} - -static void redudp_fill_preamble(socks5_udp_preabmle *preamble, redudp_client *client) -{ - preamble->reserved = 0; - preamble->frag_no = 0; /* fragmentation is not supported */ - preamble->addrtype = socks5_addrtype_ipv4; - preamble->ip.addr = get_destaddr(client)->sin_addr.s_addr; - preamble->ip.port = get_destaddr(client)->sin_port; -} - -static struct evbuffer* socks5_mkmethods_plain_wrapper(void *p) -{ - int *do_password = p; - return socks5_mkmethods_plain(*do_password); + return instance->config.destaddr.sin_addr.s_addr == 0; } -static struct evbuffer* socks5_mkpassword_plain_wrapper(void *p) +struct sockaddr_in* get_destaddr(redudp_client *client) { - redudp_instance *self = p; - return socks5_mkpassword_plain(self->config.login, self->config.password); -} - -static struct evbuffer* socks5_mkassociate(void *p) -{ - struct sockaddr_in sa; - p = p; /* Make compiler happy */ - memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - return socks5_mkcommand_plain(socks5_cmd_udp_associate, &sa); + if (do_tproxy(client->instance)) + return &client->destaddr; + else + return &client->instance->config.destaddr; } /*********************************************************************** * Logic */ -static void redudp_drop_client(redudp_client *client) -{ - int fd; - redudp_log_error(client, LOG_INFO, "Dropping..."); - enqueued_packet *q, *tmp; - if (event_initialized(&client->timeout)) { - if (event_del(&client->timeout) == -1) - redudp_log_errno(client, LOG_ERR, "event_del"); - } - if (client->relay) { - fd = EVENT_FD(&client->relay->ev_read); - bufferevent_free(client->relay); - shutdown(fd, SHUT_RDWR); - redsocks_close(fd); - } - if (event_initialized(&client->udprelay)) { - fd = EVENT_FD(&client->udprelay); - if (event_del(&client->udprelay) == -1) - redudp_log_errno(client, LOG_ERR, "event_del"); - redsocks_close(fd); - } - if (client->sender_fd != -1) - bound_udp4_put(&client->destaddr); - list_for_each_entry_safe(q, tmp, &client->queue, list) { - list_del(&q->list); - free(q); - } - list_del(&client->list); - free(client); +void redudp_drop_client(redudp_client *client) +{ + redudp_log_error(client, LOG_DEBUG, "Dropping..."); + enqueued_packet *q, *tmp; + + if (client->instance->relay_ss->fini) + client->instance->relay_ss->fini(client); + + if (event_initialized(&client->timeout)) { + if (event_del(&client->timeout) == -1) + redudp_log_errno(client, LOG_ERR, "event_del"); + } + redudp_log_error(client, LOG_DEBUG, "Dropping...2"); + if (client->sender_fd != -1) + bound_udp4_put(&client->destaddr); + list_for_each_entry_safe(q, tmp, &client->queue, list) { + list_del(&q->list); + free(q); + } + redudp_log_error(client, LOG_DEBUG, "Dropping...3"); + list_del(&client->list); + free(client); + redudp_log_error(client, LOG_DEBUG, "Dropping...4"); } -static void redudp_bump_timeout(redudp_client *client) +void redudp_bump_timeout(redudp_client *client) { - struct timeval tv; - tv.tv_sec = client->instance->config.udp_timeout; - tv.tv_usec = 0; - // TODO: implement udp_timeout_stream - if (event_add(&client->timeout, &tv) != 0) { - redudp_log_error(client, LOG_WARNING, "event_add(&client->timeout, ...)"); - redudp_drop_client(client); - } + struct timeval tv; + tv.tv_sec = client->instance->config.udp_timeout; + tv.tv_usec = 0; + // TODO: implement udp_timeout_stream + if (event_add(&client->timeout, &tv) != 0) { + redudp_log_error(client, LOG_WARNING, "event_add(&client->timeout, ...)"); + redudp_drop_client(client); + } } -static void redudp_forward_pkt(redudp_client *client, char *buf, size_t pktlen) -{ - socks5_udp_preabmle req; - struct msghdr msg; - struct iovec io[2]; - ssize_t outgoing, fwdlen = pktlen + sizeof(req); - - redudp_fill_preamble(&req, client); - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &client->udprelayaddr; - msg.msg_namelen = sizeof(client->udprelayaddr); - msg.msg_iov = io; - msg.msg_iovlen = SIZEOF_ARRAY(io); - - io[0].iov_base = &req; - io[0].iov_len = sizeof(req); - io[1].iov_base = buf; - io[1].iov_len = pktlen; - - outgoing = sendmsg(EVENT_FD(&client->udprelay), &msg, 0); - if (outgoing == -1) { - redudp_log_errno(client, LOG_WARNING, "sendmsg: Can't forward packet, dropping it"); - return; - } - else if (outgoing != fwdlen) { - redudp_log_error(client, LOG_WARNING, "sendmsg: I was sending %zd bytes, but only %zd were sent.", fwdlen, outgoing); - return; - } -} - -static int redudp_enqeue_pkt(redudp_client *client, char *buf, size_t pktlen) -{ - enqueued_packet *q = NULL; - - redudp_log_error(client, LOG_DEBUG, ""); - - if (client->queue_len >= client->instance->config.max_pktqueue) { - redudp_log_error(client, LOG_WARNING, "There are already %u packets in queue. Dropping.", - client->queue_len); - return -1; - } - - q = calloc(1, sizeof(enqueued_packet) + pktlen); - if (!q) { - redudp_log_errno(client, LOG_ERR, "Can't enqueue packet: calloc"); - return -1; - } - - q->len = pktlen; - memcpy(q->data, buf, pktlen); - client->queue_len += 1; - list_add_tail(&q->list, &client->queue); - return 0; -} - -static void redudp_flush_queue(redudp_client *client) -{ - enqueued_packet *q, *tmp; - redudp_log_error(client, LOG_INFO, "Starting UDP relay"); - list_for_each_entry_safe(q, tmp, &client->queue, list) { - redudp_forward_pkt(client, q->data, q->len); - list_del(&q->list); - free(q); - } - client->queue_len = 0; - assert(list_empty(&client->queue)); -} +void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len) +{ + size_t sent; + redsocks_time(&client->last_relay_event); + redudp_bump_timeout(client); + + if (do_tproxy(client->instance) && client->sender_fd == -1) { + client->sender_fd = bound_udp4_get(&client->destaddr); + if (client->sender_fd == -1) { + redudp_log_error(client, LOG_WARNING, "bound_udp4_get failure"); + return; + } + } + + sent = sendto(do_tproxy(client->instance) + ? client->sender_fd + : EVENT_FD(&client->instance->listener), + buf, len, 0, + (struct sockaddr*)&client->clientaddr, sizeof(client->clientaddr)); + if (sent != len) { + redudp_log_error(client, LOG_WARNING, "sendto: I was sending %zd bytes, but only %zd were sent.", + len, sent); + return; + } -static void redudp_read_assoc_reply(struct bufferevent *buffev, void *_arg) -{ - redudp_client *client = _arg; - redudp_expected_assoc_reply reply; - int read = evbuffer_remove(buffev->input, &reply, sizeof(reply)); - int fd = -1; - int error; - redudp_log_error(client, LOG_DEBUG, ""); - - if (read != sizeof(reply)) { - redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", - read, sizeof(reply)); - goto fail; - } - - if (reply.h.ver != socks5_ver) { - redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected reply version: %u", reply.h.ver); - goto fail; - } - - if (reply.h.status != socks5_status_succeeded) { - redudp_log_error(client, LOG_NOTICE, "Socks5 server status: \"%s\" (%i)", - socks5_status_to_str(reply.h.status), reply.h.status); - goto fail; - } - - if (reply.h.addrtype != socks5_addrtype_ipv4) { - redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected address type for UDP dgram destination: %u", - reply.h.addrtype); - goto fail; - } - - client->udprelayaddr.sin_family = AF_INET; - client->udprelayaddr.sin_port = reply.ip.port; - client->udprelayaddr.sin_addr.s_addr = reply.ip.addr; - - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd == -1) { - redudp_log_errno(client, LOG_ERR, "socket"); - goto fail; - } - - error = connect(fd, (struct sockaddr*)&client->udprelayaddr, sizeof(client->udprelayaddr)); - if (error) { - redudp_log_errno(client, LOG_NOTICE, "connect"); - goto fail; - } - - event_assign(&client->udprelay, get_event_base(), fd, EV_READ | EV_PERSIST, redudp_pkt_from_socks, client); - error = event_add(&client->udprelay, NULL); - if (error) { - redudp_log_errno(client, LOG_ERR, "event_add"); - goto fail; - } - - redudp_flush_queue(client); - // TODO: bufferevent_disable ? - - return; - -fail: - if (fd != -1) - redsocks_close(fd); - redudp_drop_client(client); -} - -static void redudp_read_auth_reply(struct bufferevent *buffev, void *_arg) -{ - redudp_client *client = _arg; - socks5_auth_reply reply; - int read = evbuffer_remove(buffev->input, &reply, sizeof(reply)); - int error; - redudp_log_error(client, LOG_DEBUG, ""); - - if (read != sizeof(reply)) { - redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", - read, sizeof(reply)); - goto fail; - } - - if (reply.ver != socks5_password_ver || reply.status != socks5_password_passed) { - redudp_log_error(client, LOG_NOTICE, "Socks5 authentication error. Version: %u, error code: %u", - reply.ver, reply.status); - goto fail; - } - - error = redsocks_write_helper_ex_plain( - client->relay, NULL, socks5_mkassociate, NULL, 0, /* last two are ignored */ - sizeof(redudp_expected_assoc_reply), sizeof(redudp_expected_assoc_reply)); - if (error) - goto fail; - - client->relay->readcb = redudp_read_assoc_reply; - - return; - -fail: - redudp_drop_client(client); } -static void redudp_read_auth_methods(struct bufferevent *buffev, void *_arg) +static int redudp_enqeue_pkt(redudp_client *client, char *buf, size_t pktlen) { - redudp_client *client = _arg; - int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password); - socks5_method_reply reply; - int read = evbuffer_remove(buffev->input, &reply, sizeof(reply)); - const char *error = NULL; - int ierror = 0; - redudp_log_error(client, LOG_DEBUG, ""); - - if (read != sizeof(reply)) { - redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", - read, sizeof(reply)); - goto fail; - } - - error = socks5_is_known_auth_method(&reply, do_password); - if (error) { - redudp_log_error(client, LOG_NOTICE, "socks5_is_known_auth_method: %s", error); - goto fail; - } - else if (reply.method == socks5_auth_none) { - ierror = redsocks_write_helper_ex_plain( - client->relay, NULL, socks5_mkassociate, NULL, 0, /* last two are ignored */ - sizeof(redudp_expected_assoc_reply), sizeof(redudp_expected_assoc_reply)); - if (ierror) - goto fail; - - client->relay->readcb = redudp_read_assoc_reply; - } - else if (reply.method == socks5_auth_password) { - ierror = redsocks_write_helper_ex_plain( - client->relay, NULL, socks5_mkpassword_plain_wrapper, client->instance, 0, /* last one is ignored */ - sizeof(socks5_auth_reply), sizeof(socks5_auth_reply)); - if (ierror) - goto fail; - - client->relay->readcb = redudp_read_auth_reply; - } - - return; - -fail: - redudp_drop_client(client); + enqueued_packet *q = NULL; + + if (client->queue_len >= client->instance->config.max_pktqueue) { + redudp_log_error(client, LOG_WARNING, "There are already %u packets in queue. Dropping.", + client->queue_len); + return -1; + } + + q = malloc(sizeof(enqueued_packet) + pktlen); + if (!q) { + redudp_log_errno(client, LOG_ERR, "Can't enqueue packet: malloc"); + return -1; + } + + INIT_LIST_HEAD(&q->list); + q->len = pktlen; + memcpy(q->data, buf, pktlen); + client->queue_len += 1; + list_add_tail(&q->list, &client->queue); + return 0; } -static void redudp_relay_connected(struct bufferevent *buffev, void *_arg) +void redudp_flush_queue(redudp_client *client) { - redudp_client *client = _arg; - int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password); - int error; - char relayaddr_str[RED_INET_ADDRSTRLEN]; - redudp_log_error(client, LOG_DEBUG, "via %s", red_inet_ntop(&client->instance->config.relayaddr, relayaddr_str, sizeof(relayaddr_str))); - - if (!red_is_socket_connected_ok(buffev)) { - redudp_log_errno(client, LOG_NOTICE, "red_is_socket_connected_ok"); - goto fail; - } - - error = redsocks_write_helper_ex_plain( - client->relay, NULL, socks5_mkmethods_plain_wrapper, &do_password, 0 /* does not matter */, - sizeof(socks5_method_reply), sizeof(socks5_method_reply)); - if (error) - goto fail; - - client->relay->readcb = redudp_read_auth_methods; - client->relay->writecb = 0; - //bufferevent_disable(buffev, EV_WRITE); // I don't want to check for writeability. - return; + enqueued_packet *q, *tmp; + assert(client->instance->relay_ss->ready_to_fwd(client)); -fail: - redudp_drop_client(client); + redudp_log_error(client, LOG_DEBUG, "Starting UDP relay"); + list_for_each_entry_safe(q, tmp, &client->queue, list) { + client->instance->relay_ss->forward_pkt(client, q->data, q->len); + list_del(&q->list); + free(q); + } + client->queue_len = 0; + assert(list_empty(&client->queue)); } -static void redudp_relay_error(struct bufferevent *buffev, short what, void *_arg) -{ - redudp_client *client = _arg; - // TODO: FIXME: Implement me - redudp_log_error(client, LOG_NOTICE, "redudp_relay_error"); - redudp_drop_client(client); -} static void redudp_timeout(int fd, short what, void *_arg) { - redudp_client *client = _arg; - redudp_log_error(client, LOG_INFO, "Client timeout. First: %li, last_client: %li, last_relay: %li.", - client->first_event, client->last_client_event, client->last_relay_event); - redudp_drop_client(client); + redudp_client *client = _arg; + redudp_log_error(client, LOG_DEBUG, "Client timeout. First: %li, last_client: %li, last_relay: %li.", + client->first_event, client->last_client_event, client->last_relay_event); + redudp_drop_client(client); } static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_in *clientaddr, struct sockaddr_in *destaddr, char *buf, size_t pktlen) { - redudp_client *client = calloc(1, sizeof(*client)); + redudp_client *client = calloc(1, sizeof(*client)+self->relay_ss->payload_len); - if (!client) { - log_errno(LOG_WARNING, "calloc"); - return; - } + if (!client) { + log_errno(LOG_WARNING, "calloc"); + return; + } - INIT_LIST_HEAD(&client->list); - INIT_LIST_HEAD(&client->queue); - client->instance = self; - memcpy(&client->clientaddr, clientaddr, sizeof(*clientaddr)); - if (destaddr) - memcpy(&client->destaddr, destaddr, sizeof(client->destaddr)); - evtimer_assign(&client->timeout, get_event_base(), redudp_timeout, client); - // XXX: self->relay_ss->init(client); + INIT_LIST_HEAD(&client->list); + INIT_LIST_HEAD(&client->queue); + client->instance = self; + memcpy(&client->clientaddr, clientaddr, sizeof(*clientaddr)); + if (destaddr) + memcpy(&client->destaddr, destaddr, sizeof(client->destaddr)); + evtimer_assign(&client->timeout, get_event_base(), redudp_timeout, client); + self->relay_ss->init(client); - client->sender_fd = -1; // it's postponed until socks-server replies to avoid trivial DoS + client->sender_fd = -1; // it's postponed until proxy replies to avoid trivial DoS - client->relay = red_connect_relay(&client->instance->config.relayaddr, NULL, - redudp_relay_connected, redudp_relay_error, client); - if (!client->relay) - goto fail; + redsocks_time(&client->first_event); + client->last_client_event = client->first_event; + redudp_bump_timeout(client); - if (redsocks_time(&client->first_event) == (time_t)-1) - goto fail; - client->last_client_event = client->first_event; - redudp_bump_timeout(client); + if (redudp_enqeue_pkt(client, buf, pktlen) == -1) + goto fail; - if (redudp_enqeue_pkt(client, buf, pktlen) == -1) - goto fail; + list_add(&client->list, &self->clients); - list_add(&client->list, &self->clients); - redudp_log_error(client, LOG_INFO, "got 1st packet from client"); - return; + if (self->relay_ss->connect_relay) + self->relay_ss->connect_relay(client); -fail: - redudp_drop_client(client); -} + redudp_log_error(client, LOG_DEBUG, "got 1st packet from client"); + return; -static void redudp_pkt_from_socks(int fd, short what, void *_arg) -{ - redudp_client *client = _arg; - union { - char buf[0xFFFF]; - socks5_udp_preabmle header; - } pkt; - ssize_t pktlen, fwdlen, outgoing; - struct sockaddr_in udprelayaddr; - - assert(fd == EVENT_FD(&client->udprelay)); - - pktlen = red_recv_udp_pkt(fd, pkt.buf, sizeof(pkt.buf), &udprelayaddr, NULL); - if (pktlen == -1) - return; - - if (memcmp(&udprelayaddr, &client->udprelayaddr, sizeof(udprelayaddr)) != 0) { - char buf[RED_INET_ADDRSTRLEN]; - redudp_log_error(client, LOG_NOTICE, "Got packet from unexpected address %s.", - red_inet_ntop(&udprelayaddr, buf, sizeof(buf))); - return; - } - - if (pkt.header.frag_no != 0) { - // FIXME: does anybody need it? - redudp_log_error(client, LOG_WARNING, "Got fragment #%u. Packet fragmentation is not supported!", - pkt.header.frag_no); - return; - } - - if (pkt.header.addrtype != socks5_addrtype_ipv4) { - redudp_log_error(client, LOG_NOTICE, "Got address type #%u instead of expected #%u (IPv4).", - pkt.header.addrtype, socks5_addrtype_ipv4); - return; - } - - if (pkt.header.ip.port != get_destaddr(client)->sin_port || - pkt.header.ip.addr != get_destaddr(client)->sin_addr.s_addr) - { - char buf[RED_INET_ADDRSTRLEN]; - struct sockaddr_in pktaddr = { - .sin_family = AF_INET, - .sin_addr = { pkt.header.ip.addr }, - .sin_port = pkt.header.ip.port, - }; - redudp_log_error(client, LOG_NOTICE, "Socks5 server relayed packet from unexpected address %s.", - red_inet_ntop(&pktaddr, buf, sizeof(buf))); - return; - } - - redsocks_time(&client->last_relay_event); - redudp_bump_timeout(client); - - if (do_tproxy(client->instance) && client->sender_fd == -1) { - client->sender_fd = bound_udp4_get(&client->destaddr); - if (client->sender_fd == -1) { - redudp_log_error(client, LOG_WARNING, "bound_udp4_get failure"); - return; - } - } - - fwdlen = pktlen - sizeof(pkt.header); - outgoing = sendto(do_tproxy(client->instance) - ? client->sender_fd - : EVENT_FD(&client->instance->listener), - pkt.buf + sizeof(pkt.header), fwdlen, 0, - (struct sockaddr*)&client->clientaddr, sizeof(client->clientaddr)); - if (outgoing != fwdlen) { - redudp_log_error(client, LOG_WARNING, "sendto: I was sending %zd bytes, but only %zd were sent.", - fwdlen, outgoing); - return; - } +fail: + redudp_drop_client(client); } static void redudp_pkt_from_client(int fd, short what, void *_arg) { - redudp_instance *self = _arg; - struct sockaddr_in clientaddr, destaddr, *pdestaddr; - char buf[0xFFFF]; // UDP packet can't be larger then that - ssize_t pktlen; - redudp_client *tmp, *client = NULL; - - pdestaddr = do_tproxy(self) ? &destaddr : NULL; - - assert(fd == EVENT_FD(&self->listener)); - pktlen = red_recv_udp_pkt(fd, buf, sizeof(buf), &clientaddr, pdestaddr); - if (pktlen == -1) - return; - - // TODO: this lookup may be SLOOOOOW. - list_for_each_entry(tmp, &self->clients, list) { - // TODO: check destaddr - if (0 == memcmp(&clientaddr, &tmp->clientaddr, sizeof(clientaddr))) { - client = tmp; - break; - } - } - - if (client) { - redsocks_time(&client->last_client_event); - redudp_bump_timeout(client); - if (event_initialized(&client->udprelay)) { - redudp_forward_pkt(client, buf, pktlen); - } - else { - redudp_enqeue_pkt(client, buf, pktlen); - } - } - else { - redudp_first_pkt_from_client(self, &clientaddr, pdestaddr, buf, pktlen); - } + redudp_instance *self = _arg; + struct sockaddr_in clientaddr, destaddr, *pdestaddr; + ssize_t pktlen; + redudp_client *tmp, *client = NULL; + + pdestaddr = do_tproxy(self) ? &destaddr : NULL; + + assert(fd == EVENT_FD(&self->listener)); + pktlen = red_recv_udp_pkt(fd, recv_buff, sizeof(recv_buff), &clientaddr, pdestaddr); + if (pktlen == -1) + return; + + // TODO: this lookup may be SLOOOOOW. + list_for_each_entry(tmp, &self->clients, list) { + // TODO: check destaddr + if (0 == memcmp(&clientaddr, &tmp->clientaddr, sizeof(clientaddr))) { + client = tmp; + break; + } + } + + if (client) { + redsocks_time(&client->last_client_event); + redudp_bump_timeout(client); + if (self->relay_ss->ready_to_fwd(client)) { + self->relay_ss->forward_pkt(client, recv_buff, pktlen); + } + else { + redudp_enqeue_pkt(client, recv_buff, pktlen); + } + } + else { + redudp_first_pkt_from_client(self, &clientaddr, pdestaddr, recv_buff, pktlen); + } } /*********************************************************************** @@ -696,146 +378,176 @@ static void redudp_pkt_from_client(int fd, short what, void *_arg) */ static parser_entry redudp_entries[] = { - { .key = "local_ip", .type = pt_in_addr }, - { .key = "local_port", .type = pt_uint16 }, - { .key = "ip", .type = pt_in_addr }, - { .key = "port", .type = pt_uint16 }, - { .key = "login", .type = pt_pchar }, - { .key = "password", .type = pt_pchar }, - { .key = "dest_ip", .type = pt_in_addr }, - { .key = "dest_port", .type = pt_uint16 }, - { .key = "udp_timeout", .type = pt_uint16 }, - { .key = "udp_timeout_stream", .type = pt_uint16 }, - { } + { .key = "local_ip", .type = pt_in_addr }, + { .key = "local_port", .type = pt_uint16 }, + { .key = "ip", .type = pt_in_addr }, + { .key = "port", .type = pt_uint16 }, + { .key = "type", .type = pt_pchar }, + { .key = "login", .type = pt_pchar }, + { .key = "password", .type = pt_pchar }, + { .key = "dest_ip", .type = pt_in_addr }, + { .key = "dest_port", .type = pt_uint16 }, + { .key = "udp_timeout", .type = pt_uint16 }, + { .key = "udp_timeout_stream", .type = pt_uint16 }, + { } }; static list_head instances = LIST_HEAD_INIT(instances); static int redudp_onenter(parser_section *section) { - redudp_instance *instance = calloc(1, sizeof(*instance)); - if (!instance) { - parser_error(section->context, "Not enough memory"); - return -1; - } - - INIT_LIST_HEAD(&instance->list); - INIT_LIST_HEAD(&instance->clients); - instance->config.bindaddr.sin_family = AF_INET; - instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - instance->config.relayaddr.sin_family = AF_INET; - instance->config.relayaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - instance->config.destaddr.sin_family = AF_INET; - instance->config.max_pktqueue = 5; - instance->config.udp_timeout = 30; - instance->config.udp_timeout_stream = 180; - - for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) - entry->addr = - (strcmp(entry->key, "local_ip") == 0) ? (void*)&instance->config.bindaddr.sin_addr : - (strcmp(entry->key, "local_port") == 0) ? (void*)&instance->config.bindaddr.sin_port : - (strcmp(entry->key, "ip") == 0) ? (void*)&instance->config.relayaddr.sin_addr : - (strcmp(entry->key, "port") == 0) ? (void*)&instance->config.relayaddr.sin_port : - (strcmp(entry->key, "login") == 0) ? (void*)&instance->config.login : - (strcmp(entry->key, "password") == 0) ? (void*)&instance->config.password : - (strcmp(entry->key, "dest_ip") == 0) ? (void*)&instance->config.destaddr.sin_addr : - (strcmp(entry->key, "dest_port") == 0) ? (void*)&instance->config.destaddr.sin_port : - (strcmp(entry->key, "max_pktqueue") == 0) ? (void*)&instance->config.max_pktqueue : - (strcmp(entry->key, "udp_timeout") == 0) ? (void*)&instance->config.udp_timeout: - (strcmp(entry->key, "udp_timeout_stream") == 0) ? (void*)&instance->config.udp_timeout_stream : - NULL; - section->data = instance; - return 0; + // FIXME: find proper way to calulate instance_payload_len + int instance_payload_len = 0; + udprelay_subsys **ss; + FOREACH(ss, relay_subsystems) + if (instance_payload_len < (*ss)->instance_payload_len) + instance_payload_len = (*ss)->instance_payload_len; + + redudp_instance *instance = calloc(1, sizeof(*instance) + instance_payload_len); + if (!instance) { + parser_error(section->context, "Not enough memory"); + return -1; + } + + INIT_LIST_HEAD(&instance->list); + INIT_LIST_HEAD(&instance->clients); + instance->config.bindaddr.sin_family = AF_INET; + instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + instance->config.relayaddr.sin_family = AF_INET; + instance->config.relayaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + instance->config.destaddr.sin_family = AF_INET; + instance->config.max_pktqueue = 5; + instance->config.udp_timeout = 30; + instance->config.udp_timeout_stream = 180; + + for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) + entry->addr = + (strcmp(entry->key, "local_ip") == 0) ? (void*)&instance->config.bindaddr.sin_addr : + (strcmp(entry->key, "local_port") == 0) ? (void*)&instance->config.bindaddr.sin_port : + (strcmp(entry->key, "ip") == 0) ? (void*)&instance->config.relayaddr.sin_addr : + (strcmp(entry->key, "port") == 0) ? (void*)&instance->config.relayaddr.sin_port : + (strcmp(entry->key, "type") == 0) ? (void*)&instance->config.type : + (strcmp(entry->key, "login") == 0) ? (void*)&instance->config.login : + (strcmp(entry->key, "password") == 0) ? (void*)&instance->config.password : + (strcmp(entry->key, "dest_ip") == 0) ? (void*)&instance->config.destaddr.sin_addr : + (strcmp(entry->key, "dest_port") == 0) ? (void*)&instance->config.destaddr.sin_port : + (strcmp(entry->key, "max_pktqueue") == 0) ? (void*)&instance->config.max_pktqueue : + (strcmp(entry->key, "udp_timeout") == 0) ? (void*)&instance->config.udp_timeout: + (strcmp(entry->key, "udp_timeout_stream") == 0) ? (void*)&instance->config.udp_timeout_stream : + NULL; + section->data = instance; + return 0; } static int redudp_onexit(parser_section *section) { - redudp_instance *instance = section->data; - - section->data = NULL; - for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) - entry->addr = NULL; - - instance->config.bindaddr.sin_port = htons(instance->config.bindaddr.sin_port); - instance->config.relayaddr.sin_port = htons(instance->config.relayaddr.sin_port); - instance->config.destaddr.sin_port = htons(instance->config.destaddr.sin_port); - - if (instance->config.udp_timeout_stream < instance->config.udp_timeout) { - parser_error(section->context, "udp_timeout_stream should be not less then udp_timeout"); - return -1; - } - - list_add(&instance->list, &instances); - - return 0; + redudp_instance *instance = section->data; + char * err = NULL; + + section->data = NULL; + for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) + entry->addr = NULL; + + instance->config.bindaddr.sin_port = htons(instance->config.bindaddr.sin_port); + instance->config.relayaddr.sin_port = htons(instance->config.relayaddr.sin_port); + instance->config.destaddr.sin_port = htons(instance->config.destaddr.sin_port); + + if (instance->config.type) { + udprelay_subsys **ss; + FOREACH(ss, relay_subsystems) { + if (!strcmp((*ss)->name, instance->config.type)) { + instance->relay_ss = *ss; + list_add(&instance->list, &instances); + break; + } + } + if (!instance->relay_ss) + err = "invalid `type` for redudp"; + } + else { + err = "no `type` for redudp"; + } + + if (instance->config.udp_timeout_stream < instance->config.udp_timeout) { + parser_error(section->context, "udp_timeout_stream should be not less then udp_timeout"); + return -1; + } + + return err?-1:0; } static int redudp_init_instance(redudp_instance *instance) { - /* FIXME: redudp_fini_instance is called in case of failure, this - * function will remove instance from instances list - result - * looks ugly. - */ - int error; - int fd = -1; - - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd == -1) { - log_errno(LOG_ERR, "socket"); - goto fail; - } - - if (do_tproxy(instance)) { - int on = 1; - char buf[RED_INET_ADDRSTRLEN]; - // iptables TPROXY target does not send packets to non-transparent sockets - if (0 != redudp_transparent(fd)) - goto fail; - - error = setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &on, sizeof(on)); - if (error) { - log_errno(LOG_ERR, "setsockopt(listener, SOL_IP, IP_RECVORIGDSTADDR)"); - goto fail; - } - - log_error(LOG_DEBUG, "redudp @ %s: TPROXY", red_inet_ntop(&instance->config.bindaddr, buf, sizeof(buf))); - } - else { - char buf1[RED_INET_ADDRSTRLEN], buf2[RED_INET_ADDRSTRLEN]; - log_error(LOG_DEBUG, "redudp @ %s: destaddr=%s", - red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1)), - red_inet_ntop(&instance->config.destaddr, buf2, sizeof(buf2))); - } - - error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); - if (error) { - log_errno(LOG_ERR, "bind"); - goto fail; - } - - error = fcntl_nonblock(fd); - if (error) { - log_errno(LOG_ERR, "fcntl"); - goto fail; - } - - event_assign(&instance->listener, get_event_base(), fd, EV_READ | EV_PERSIST, redudp_pkt_from_client, instance); - error = event_add(&instance->listener, NULL); - if (error) { - log_errno(LOG_ERR, "event_add"); - goto fail; - } - - return 0; + /* FIXME: redudp_fini_instance is called in case of failure, this + * function will remove instance from instances list - result + * looks ugly. + */ + int error; + int fd = -1; + + if (instance->relay_ss->instance_init + && instance->relay_ss->instance_init(instance)) { + log_errno(LOG_ERR, "Failed to init UDP relay subsystem."); + goto fail; + } + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + log_errno(LOG_ERR, "socket"); + goto fail; + } + + if (do_tproxy(instance)) { + int on = 1; + char buf[RED_INET_ADDRSTRLEN]; + // iptables TPROXY target does not send packets to non-transparent sockets + if (0 != redudp_transparent(fd)) + goto fail; + + error = setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &on, sizeof(on)); + if (error) { + log_errno(LOG_ERR, "setsockopt(listener, SOL_IP, IP_RECVORIGDSTADDR)"); + goto fail; + } + + log_error(LOG_DEBUG, "redudp @ %s: TPROXY", red_inet_ntop(&instance->config.bindaddr, buf, sizeof(buf))); + } + else { + char buf1[RED_INET_ADDRSTRLEN], buf2[RED_INET_ADDRSTRLEN]; + log_error(LOG_DEBUG, "redudp @ %s: destaddr=%s", + red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1)), + red_inet_ntop(&instance->config.destaddr, buf2, sizeof(buf2))); + } + + error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); + if (error) { + log_errno(LOG_ERR, "bind"); + goto fail; + } + + error = evutil_make_socket_nonblocking(fd); + if (error) { + log_errno(LOG_ERR, "set nonblocking"); + goto fail; + } + + event_assign(&instance->listener, get_event_base(), fd, EV_READ | EV_PERSIST, redudp_pkt_from_client, instance); + error = event_add(&instance->listener, NULL); + if (error) { + log_errno(LOG_ERR, "event_add"); + goto fail; + } + + return 0; fail: - redudp_fini_instance(instance); + redudp_fini_instance(instance); - if (fd != -1) { - redsocks_close(fd); - } + if (fd != -1) { + redsocks_close(fd); + } - return -1; + return -1; } /* Drops instance completely, freeing its memory and removing from @@ -843,72 +555,75 @@ static int redudp_init_instance(redudp_instance *instance) */ static void redudp_fini_instance(redudp_instance *instance) { - if (!list_empty(&instance->clients)) { - redudp_client *tmp, *client = NULL; + if (!list_empty(&instance->clients)) { + redudp_client *tmp, *client = NULL; + + log_error(LOG_WARNING, "There are connected clients during shutdown! Disconnecting them."); + list_for_each_entry_safe(client, tmp, &instance->clients, list) { + redudp_drop_client(client); + } + } - log_error(LOG_WARNING, "There are connected clients during shutdown! Disconnecting them."); - list_for_each_entry_safe(client, tmp, &instance->clients, list) { - redudp_drop_client(client); - } - } + if (event_initialized(&instance->listener)) { + if (event_del(&instance->listener) != 0) + log_errno(LOG_WARNING, "event_del"); + redsocks_close(EVENT_FD(&instance->listener)); + memset(&instance->listener, 0, sizeof(instance->listener)); + } - if (event_initialized(&instance->listener)) { - if (event_del(&instance->listener) != 0) - log_errno(LOG_WARNING, "event_del"); - redsocks_close(EVENT_FD(&instance->listener)); - memset(&instance->listener, 0, sizeof(instance->listener)); - } + if (instance->relay_ss->instance_fini) + instance->relay_ss->instance_fini(instance); - list_del(&instance->list); + list_del(&instance->list); - free(instance->config.login); - free(instance->config.password); + free(instance->config.login); + free(instance->config.password); - memset(instance, 0, sizeof(*instance)); - free(instance); + memset(instance, 0, sizeof(*instance)); + free(instance); } static int redudp_init() { - redudp_instance *tmp, *instance = NULL; + redudp_instance *tmp, *instance = NULL; - // TODO: init debug_dumper + // TODO: init debug_dumper - list_for_each_entry_safe(instance, tmp, &instances, list) { - if (redudp_init_instance(instance) != 0) - goto fail; - } + list_for_each_entry_safe(instance, tmp, &instances, list) { + if (redudp_init_instance(instance) != 0) + goto fail; + } - return 0; + return 0; fail: - redudp_fini(); - return -1; + redudp_fini(); + return -1; } static int redudp_fini() { - redudp_instance *tmp, *instance = NULL; + redudp_instance *tmp, *instance = NULL; - list_for_each_entry_safe(instance, tmp, &instances, list) - redudp_fini_instance(instance); + list_for_each_entry_safe(instance, tmp, &instances, list) + redudp_fini_instance(instance); - return 0; + return 0; } static parser_section redudp_conf_section = { - .name = "redudp", - .entries = redudp_entries, - .onenter = redudp_onenter, - .onexit = redudp_onexit + .name = "redudp", + .entries = redudp_entries, + .onenter = redudp_onenter, + .onexit = redudp_onexit }; app_subsys redudp_subsys = { - .init = redudp_init, - .fini = redudp_fini, - .conf_section = &redudp_conf_section, + .init = redudp_init, + .fini = redudp_fini, + .conf_section = &redudp_conf_section, }; /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ diff --git a/redudp.h b/redudp.h index 3f1d9d10..0df56099 100644 --- a/redudp.h +++ b/redudp.h @@ -1,11 +1,34 @@ #ifndef REDUDP_H #define REDUDP_H +#include +#include "list.h" + +struct redudp_client_t; +struct redudp_instance_t; + +typedef struct udprelay_subsys_t { + char *name; + size_t payload_len; // size of relay-specific data in client section + size_t instance_payload_len; // size of relay-specify data in instance section + void (*init)(struct redudp_client_t *client); + void (*fini)(struct redudp_client_t *client); + int (*instance_init)(struct redudp_instance_t *instance); + void (*instance_fini)(struct redudp_instance_t *instance); + // connect_relay (if any) is called instead of redudp_connect_relay after client connection acceptance + void (*connect_relay)(struct redudp_client_t *client); + //void (*relay_connected)(struct redudp_client_t *client); + void (*forward_pkt)(struct redudp_client_t *client, void * data, size_t len); + int (*ready_to_fwd)(struct redudp_client_t *client); +} udprelay_subsys; + + typedef struct redudp_config_t { struct sockaddr_in bindaddr; struct sockaddr_in relayaddr; // TODO: outgoingaddr; struct sockaddr_in destaddr; + char *type; char *login; char *password; uint16_t max_pktqueue; @@ -18,6 +41,7 @@ typedef struct redudp_instance_t { redudp_config config; struct event listener; list_head clients; + udprelay_subsys *relay_ss; } redudp_instance; typedef struct redudp_client_t { @@ -27,9 +51,6 @@ typedef struct redudp_client_t { struct sockaddr_in destaddr; int sender_fd; // shared between several clients socket (bound to `destaddr`) struct event timeout; - struct bufferevent *relay; - struct event udprelay; - struct sockaddr_in udprelayaddr; int state; // it's used by bottom layer time_t first_event; time_t last_client_event; @@ -44,6 +65,17 @@ typedef struct enqueued_packet_t { char data[1]; } enqueued_packet; +struct sockaddr_in* get_destaddr(redudp_client *client); +void redudp_drop_client(redudp_client *client); +void redudp_flush_queue(redudp_client *client); +void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len); +void redudp_bump_timeout(redudp_client *client); + +#define redudp_log_error(client, prio, msg...) \ + redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &(client)->clientaddr, get_destaddr(client), prio, ## msg) +#define redudp_log_errno(client, prio, msg...) \ + redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &(client)->clientaddr, get_destaddr(client), prio, ## msg) + /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ #endif /* REDUDP_H */ diff --git a/socks5-udp.c b/socks5-udp.c new file mode 100644 index 00000000..335a9c94 --- /dev/null +++ b/socks5-udp.c @@ -0,0 +1,419 @@ +/* redsocks2 - transparent TCP-to-proxy redirector + * Copyright (C) 2013-2015 Zhuofei Wang + * + * This code is based on redsocks project developed by Leonid Evdokimov. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include "main.h" +#include "utils.h" +#include "log.h" +#include "redudp.h" +#include "redsocks.h" +#include "socks5.h" + +typedef struct socks5_expected_assoc_reply_t { + socks5_reply h; + socks5_addr_ipv4 ip; +} PACKED socks5_expected_assoc_reply; + +static struct evbuffer* socks5_mkmethods_plain_wrapper(void *p) +{ + int *do_password = p; + return socks5_mkmethods_plain(*do_password); +} + +static struct evbuffer* socks5_mkpassword_plain_wrapper(void *p) +{ + redudp_instance *self = p; + return socks5_mkpassword_plain(self->config.login, self->config.password); +} + +static struct evbuffer* socks5_mkassociate(void *p) +{ + struct sockaddr_in sa; + //p = p; /* Make compiler happy */ + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + return socks5_mkcommand_plain(socks5_cmd_udp_associate, &sa); +} + +static void socks5_fill_preamble(socks5_udp_preabmle *preamble, redudp_client *client) +{ + preamble->reserved = 0; + preamble->frag_no = 0; /* fragmentation is not supported */ + preamble->addrtype = socks5_addrtype_ipv4; + preamble->ip.addr = get_destaddr(client)->sin_addr.s_addr; + preamble->ip.port = get_destaddr(client)->sin_port; +} + + + +/************************************************************************** + * Logic + * */ + +typedef struct socks5_client_t { + struct event udprelay; + struct sockaddr_in udprelayaddr; + struct bufferevent *relay; + int ready_fwd; +} socks5_client; + +static void socks5_client_init(redudp_client *client) +{ + socks5_client *socks5client = (void*)(client + 1); + memset(socks5client, 0, sizeof(socks5_client)); +} + +static void socks5_client_fini(redudp_client *client) +{ + socks5_client *socks5client = (void*)(client + 1); + int fd; + + if (event_initialized(&socks5client->udprelay)) { + fd = EVENT_FD(&socks5client->udprelay); + if (event_del(&socks5client->udprelay) == -1) + redudp_log_errno(client, LOG_ERR, "event_del"); + redsocks_close(fd); + } + if (socks5client->relay) { + fd = EVENT_FD(&socks5client->relay->ev_read); + bufferevent_free(socks5client->relay); + shutdown(fd, SHUT_RDWR); + redsocks_close(fd); + } +} + +static int socks5_ready_to_fwd(struct redudp_client_t *client) +{ + socks5_client *socks5client = (void*)(client + 1); + return socks5client->ready_fwd; +} + +static void socks5_forward_pkt(redudp_client *client, void *buf, size_t pktlen) +{ + socks5_client *socks5client = (void*)(client + 1); + socks5_udp_preabmle req; + struct msghdr msg; + struct iovec io[2]; + ssize_t outgoing, fwdlen = pktlen + sizeof(req); + + socks5_fill_preamble(&req, client); + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &socks5client->udprelayaddr; + msg.msg_namelen = sizeof(socks5client->udprelayaddr); + msg.msg_iov = io; + msg.msg_iovlen = SIZEOF_ARRAY(io); + + io[0].iov_base = &req; + io[0].iov_len = sizeof(req); + io[1].iov_base = buf; + io[1].iov_len = pktlen; + + outgoing = sendmsg(EVENT_FD(&socks5client->udprelay), &msg, 0); + if (outgoing == -1) { + redudp_log_errno(client, LOG_WARNING, "sendmsg: Can't forward packet, dropping it"); + return; + } + else if (outgoing != fwdlen) { + redudp_log_error(client, LOG_WARNING, "sendmsg: I was sending %zd bytes, but only %zd were sent.", fwdlen, outgoing); + return; + } +} + +static void socks5_pkt_from_socks(int fd, short what, void *_arg) +{ + redudp_client *client = _arg; + socks5_client *socks5client = (void*)(client + 1); + union { + char buf[0xFFFF]; + socks5_udp_preabmle header; + } pkt; + ssize_t pktlen, fwdlen; + struct sockaddr_in udprelayaddr; + + assert(fd == EVENT_FD(&socks5client->udprelay)); + + pktlen = red_recv_udp_pkt(fd, pkt.buf, sizeof(pkt.buf), &udprelayaddr, NULL); + if (pktlen == -1) + return; + + if (memcmp(&udprelayaddr, &socks5client->udprelayaddr, sizeof(udprelayaddr)) != 0) { + char buf[RED_INET_ADDRSTRLEN]; + redudp_log_error(client, LOG_NOTICE, "Got packet from unexpected address %s.", + red_inet_ntop(&udprelayaddr, buf, sizeof(buf))); + return; + } + + if (pkt.header.frag_no != 0) { + // FIXME: does anybody need it? + redudp_log_error(client, LOG_WARNING, "Got fragment #%u. Packet fragmentation is not supported!", + pkt.header.frag_no); + return; + } + + if (pkt.header.addrtype != socks5_addrtype_ipv4) { + redudp_log_error(client, LOG_NOTICE, "Got address type #%u instead of expected #%u (IPv4).", + pkt.header.addrtype, socks5_addrtype_ipv4); + return; + } + + if (pkt.header.ip.port != get_destaddr(client)->sin_port || + pkt.header.ip.addr != get_destaddr(client)->sin_addr.s_addr) + { + char buf[RED_INET_ADDRSTRLEN]; + struct sockaddr_in pktaddr = { + .sin_family = AF_INET, + .sin_addr = { pkt.header.ip.addr }, + .sin_port = pkt.header.ip.port, + }; + redudp_log_error(client, LOG_NOTICE, "Socks5 server relayed packet from unexpected address %s.", + red_inet_ntop(&pktaddr, buf, sizeof(buf))); + return; + } + + fwdlen = pktlen - sizeof(pkt.header); + redudp_fwd_pkt_to_sender(client, pkt.buf + sizeof(pkt.header), fwdlen); +} + + +static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg) +{ + redudp_client *client = _arg; + socks5_client *socks5client = (void*)(client + 1); + socks5_expected_assoc_reply reply; + int read = evbuffer_remove(buffev->input, &reply, sizeof(reply)); + int fd = -1; + int error; + redudp_log_error(client, LOG_DEBUG, ""); + + if (read != sizeof(reply)) { + redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", + read, sizeof(reply)); + goto fail; + } + + if (reply.h.ver != socks5_ver) { + redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected reply version: %u", reply.h.ver); + goto fail; + } + + if (reply.h.status != socks5_status_succeeded) { + redudp_log_error(client, LOG_NOTICE, "Socks5 server status: \"%s\" (%i)", + socks5_status_to_str(reply.h.status), reply.h.status); + goto fail; + } + + if (reply.h.addrtype != socks5_addrtype_ipv4) { + redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected address type for UDP dgram destination: %u", + reply.h.addrtype); + goto fail; + } + + socks5client->udprelayaddr.sin_family = AF_INET; + socks5client->udprelayaddr.sin_port = reply.ip.port; + socks5client->udprelayaddr.sin_addr.s_addr = reply.ip.addr; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + redudp_log_errno(client, LOG_ERR, "socket"); + goto fail; + } + + error = connect(fd, (struct sockaddr*)&socks5client->udprelayaddr, sizeof(socks5client->udprelayaddr)); + if (error) { + redudp_log_errno(client, LOG_NOTICE, "connect"); + goto fail; + } + + event_assign(&socks5client->udprelay, get_event_base(), fd, EV_READ | EV_PERSIST, socks5_pkt_from_socks, client); + error = event_add(&socks5client->udprelay, NULL); + if (error) { + redudp_log_errno(client, LOG_ERR, "event_add"); + goto fail; + } + + socks5client->ready_fwd = 1; + redudp_flush_queue(client); + // TODO: bufferevent_disable ? + + return; + +fail: + if (fd != -1) + redsocks_close(fd); + redudp_drop_client(client); +} + +static void socks5_read_auth_reply(struct bufferevent *buffev, void *_arg) +{ + redudp_client *client = _arg; + socks5_client *socks5client = (void*)(client + 1); + socks5_auth_reply reply; + int read = evbuffer_remove(buffev->input, &reply, sizeof(reply)); + int error; + redudp_log_error(client, LOG_DEBUG, ""); + + if (read != sizeof(reply)) { + redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", + read, sizeof(reply)); + goto fail; + } + + if (reply.ver != socks5_password_ver || reply.status != socks5_password_passed) { + redudp_log_error(client, LOG_NOTICE, "Socks5 authentication error. Version: %u, error code: %u", + reply.ver, reply.status); + goto fail; + } + + error = redsocks_write_helper_ex_plain( + socks5client->relay, NULL, socks5_mkassociate, NULL, 0, /* last two are ignored */ + sizeof(socks5_expected_assoc_reply), sizeof(socks5_expected_assoc_reply)); + if (error) + goto fail; + + socks5client->relay->readcb = socks5_read_assoc_reply; + + return; + +fail: + redudp_drop_client(client); +} + + +static void socks5_read_auth_methods(struct bufferevent *buffev, void *_arg) +{ + redudp_client *client = _arg; + socks5_client *socks5client = (void*)(client + 1); + int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password); + socks5_method_reply reply; + int read = evbuffer_remove(buffev->input, &reply, sizeof(reply)); + const char *error = NULL; + int ierror = 0; + redudp_log_error(client, LOG_DEBUG, ""); + + if (read != sizeof(reply)) { + redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", + read, sizeof(reply)); + goto fail; + } + + error = socks5_is_known_auth_method(&reply, do_password); + if (error) { + redudp_log_error(client, LOG_NOTICE, "socks5_is_known_auth_method: %s", error); + goto fail; + } + else if (reply.method == socks5_auth_none) { + ierror = redsocks_write_helper_ex_plain( + socks5client->relay, NULL, socks5_mkassociate, NULL, 0, /* last two are ignored */ + sizeof(socks5_expected_assoc_reply), sizeof(socks5_expected_assoc_reply)); + if (ierror) + goto fail; + + socks5client->relay->readcb = socks5_read_assoc_reply; + } + else if (reply.method == socks5_auth_password) { + ierror = redsocks_write_helper_ex_plain( + socks5client->relay, NULL, socks5_mkpassword_plain_wrapper, client->instance, 0, /* last one is ignored */ + sizeof(socks5_auth_reply), sizeof(socks5_auth_reply)); + if (ierror) + goto fail; + + socks5client->relay->readcb = socks5_read_auth_reply; + } + + return; + +fail: + redudp_drop_client(client); +} + +static void socks5_relay_connected(struct bufferevent *buffev, void *_arg) +{ + redudp_client *client = _arg; + socks5_client *socks5client = (void*)(client + 1); + int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password); + int error; + char relayaddr_str[RED_INET_ADDRSTRLEN]; + redudp_log_error(client, LOG_DEBUG, "via %s", red_inet_ntop(&client->instance->config.relayaddr, relayaddr_str, sizeof(relayaddr_str))); + + if (!red_is_socket_connected_ok(buffev)) { + redudp_log_errno(client, LOG_NOTICE, "red_is_socket_connected_ok"); + goto fail; + } + + error = redsocks_write_helper_ex_plain( + socks5client->relay, NULL, socks5_mkmethods_plain_wrapper, &do_password, 0 /* does not matter */, + sizeof(socks5_method_reply), sizeof(socks5_method_reply)); + if (error) + goto fail; + + socks5client->relay->readcb = socks5_read_auth_methods; + socks5client->relay->writecb = 0; + //bufferevent_disable(buffev, EV_WRITE); // I don't want to check for writeability. + return; + +fail: + redudp_drop_client(client); +} + +static void socks5_relay_error(struct bufferevent *buffev, short what, void *_arg) +{ + redudp_client *client = _arg; + // TODO: FIXME: Implement me + redudp_log_error(client, LOG_NOTICE, "socks5_relay_error"); + redudp_drop_client(client); +} + + +static void socks5_connect_relay(redudp_client *client) +{ + socks5_client *socks5client = (void*)(client + 1); + socks5client->relay = red_connect_relay(&client->instance->config.relayaddr, NULL, + socks5_relay_connected, socks5_relay_error, client); + if (!socks5client->relay) + redudp_drop_client(client); +} + +static int socks5_instance_init(struct redudp_instance_t *instance) +{ + return 0; +} + +static void socks5_instance_fini(struct redudp_instance_t *instance) +{ +} + +udprelay_subsys socks5_udp_subsys = +{ + .name = "socks5", + .payload_len = sizeof(socks5_client), + .instance_payload_len = 0, + .init = socks5_client_init, + .fini = socks5_client_fini, + .instance_init = socks5_instance_init, + .instance_fini = socks5_instance_fini, + .connect_relay = socks5_connect_relay, + .forward_pkt = socks5_forward_pkt, + .ready_to_fwd = socks5_ready_to_fwd, +}; + + +/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ +/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/utils.c b/utils.c index 61b62f47..54049f61 100644 --- a/utils.c +++ b/utils.c @@ -271,26 +271,6 @@ int red_socket_geterrno(struct bufferevent *buffev) return pseudo_errno; } -/** simple fcntl(2) wrapper, provides errno and all logging to caller - * I have to use it in event-driven code because of accept(2) (see NOTES) - * and connect(2) (see ERRORS about EINPROGRESS) - */ -int fcntl_nonblock(int fd) -{ - int error; - int flags; - - flags = fcntl(fd, F_GETFL); - if (flags == -1) - return -1; - - error = fcntl(fd, F_SETFL, flags | O_NONBLOCK); - if (error) - return -1; - - return 0; -} - int red_is_socket_connected_ok(struct bufferevent *buffev) { int pseudo_errno = red_socket_geterrno(buffev); diff --git a/utils.h b/utils.h index d7139f09..d364466d 100644 --- a/utils.h +++ b/utils.h @@ -53,8 +53,6 @@ int red_socket_geterrno(struct bufferevent *buffev); int red_is_socket_connected_ok(struct bufferevent *buffev); int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *fromaddr, struct sockaddr_in *toaddr); -int fcntl_nonblock(int fd); - size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, size_t skip); #define event_fmt_str "%s|%s|%s|%s|%s|%s|0x%x" From b713402132a2cb11d50a402338f3dfd5d8552a63 Mon Sep 17 00:00:00 2001 From: She Jinxin Date: Wed, 1 Apr 2015 15:33:12 +0800 Subject: [PATCH 048/192] typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 782a608f..ee3e626c 100644 --- a/README.md +++ b/README.md @@ -95,9 +95,9 @@ by field 'login'. port = 8388; timeout = 13; autoproxy = 1; - login = 'aes-128-cfb'; // field 'login' is reused as encryption + login = "aes-128-cfb"; // field 'login' is reused as encryption // method of shadowsocks - password = 'your password'; // Your shadowsocks password + password = "your password"; // Your shadowsocks password } List of supported encryption methods(Compiled with OpenSSL): From 6dd77782b7ee60ae783f8fa5d9a5b3d54be6d125 Mon Sep 17 00:00:00 2001 From: Emong Date: Sun, 12 Apr 2015 20:17:39 +0800 Subject: [PATCH 049/192] ipcache use 'uint16_t' use uint16_t for different platform --- ipcache.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ipcache.c b/ipcache.c index 0723a338..cc3be0b6 100644 --- a/ipcache.c +++ b/ipcache.c @@ -40,11 +40,11 @@ //---------------------------------------------------------------------------------------- typedef struct cache_config_t { // Values to be read from config file - unsigned int cache_size; - unsigned int port_check; - unsigned int stale_time; + uint16_t cache_size; + uint16_t port_check; + uint16_t stale_time; char * cache_file; - unsigned int autosave_interval; + uint16_t autosave_interval; // Dynamically calculated values. unsigned int block_size; unsigned int block_count; From 318bca618d41293870439144c8968549c500b349 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 13 Apr 2015 15:19:04 +0800 Subject: [PATCH 050/192] Add support to Shadowsocks UDP. Bug fix to redudp. --- Makefile | 2 +- README.md | 16 ++- encrypt.c | 4 +- encrypt.h | 2 +- redsocks.conf.example | 5 +- redudp.c | 14 +- shadowsocks-udp.c | 295 ++++++++++++++++++++++++++++++++++++++++++ shadowsocks.c | 10 +- socks5-udp.c | 7 +- 9 files changed, 335 insertions(+), 20 deletions(-) create mode 100644 shadowsocks-udp.c diff --git a/Makefile b/Makefile index f3ff5e2d..6d79cc24 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o encrypt.o shadowsocks.o http-connect.o \ - socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o socks5-udp.o \ + socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o socks5-udp.o shadowsocks-udp.o \ tcpdns.o gen/version.o SRCS := $(OBJS:.o=.c) CONF := config.h diff --git a/README.md b/README.md index ee3e626c..b1166d51 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Configurations -------------- Please see 'redsocks.conf.example' for whole picture of configuration file. Below are additional sample configuration sections for different usage. +Operations required to iptables are not listed here. ###Redirect Blocked Traffic via Proxy Automatically To use the autoproxy feature, please change the redsocks section in @@ -99,6 +100,20 @@ by field 'login'. // method of shadowsocks password = "your password"; // Your shadowsocks password } + + redudp { + local_ip = 127.0.0.1; + local_port = 1053; + ip = your.ss-server.com; + port = 443; + type = shadowsocks; + login = rc4-md5; + password = "ss server password"; + dest_ip = 8.8.8.8; + dest_port = 53; + udp_timeout = 3; + } + List of supported encryption methods(Compiled with OpenSSL): @@ -122,7 +137,6 @@ List of supported encryption methods(Compiled with PolarSSL): table ARC4-128 - ARC4-128 AES-128-CFB128 AES-192-CFB128 AES-256-CFB128 diff --git a/encrypt.c b/encrypt.c index e1f9ad9a..82260d19 100644 --- a/encrypt.c +++ b/encrypt.c @@ -893,11 +893,11 @@ int ss_decrypt(struct enc_ctx *ctx, char *ciphertext, size_t clen, } } -void enc_ctx_init(enc_info * info, struct enc_ctx *ctx, int enc) +int enc_ctx_init(enc_info * info, struct enc_ctx *ctx, int enc) { memset(ctx, 0, sizeof(struct enc_ctx)); ctx->info = info; - cipher_context_init(info, &ctx->evp, enc); + return cipher_context_init(info, &ctx->evp, enc); } void enc_ctx_free(struct enc_ctx *ctx) diff --git a/encrypt.h b/encrypt.h index e587892b..61b3fc72 100644 --- a/encrypt.h +++ b/encrypt.h @@ -144,7 +144,7 @@ struct enc_ctx { int enc_init(enc_info * info, const char *pass, const char *method); void enc_free(enc_info * info); -void enc_ctx_init(enc_info * info, struct enc_ctx *ctx, int enc); +int enc_ctx_init(enc_info * info, struct enc_ctx *ctx, int enc); void enc_ctx_free(struct enc_ctx *ctx); int ss_encrypt(struct enc_ctx *ctx, char *plaintext, size_t plen, char * ciphertext, size_t * clen); diff --git a/redsocks.conf.example b/redsocks.conf.example index d6fdd4ae..793a06f5 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -98,10 +98,11 @@ redudp { // `ip' and `port' of socks5 proxy server. ip = 10.0.0.1; port = 1080; - login = username; + login = username;// field 'login' is reused as encryption + // method of shadowsocks password = pazzw0rd; - // know types: socks5 + // know types: socks5, shadowsocks type = socks5; // kernel does not give us this information, so we have to duplicate it diff --git a/redudp.c b/redudp.c index fbec5b74..f04938b9 100644 --- a/redudp.c +++ b/redudp.c @@ -59,9 +59,11 @@ struct bound_udp4 { }; extern udprelay_subsys socks5_udp_subsys; +extern udprelay_subsys shadowsocks_udp_subsys; static udprelay_subsys *relay_subsystems[] = { &socks5_udp_subsys, + &shadowsocks_udp_subsys, }; /*********************************************************************** * Helpers @@ -130,7 +132,7 @@ static int bound_udp4_get(const struct sockaddr_in *addr) fail: if (node) { if (node->fd != -1) - redsocks_close(node->fd); + close(node->fd); free(node); } return -1; @@ -155,7 +157,7 @@ static void bound_udp4_put(const struct sockaddr_in *addr) parent = tdelete(node, &root_bound_udp4, bound_udp4_cmp); assert(parent); - redsocks_close(node->fd); // expanding `pnode` to avoid use after free + close(node->fd); // expanding `pnode` to avoid use after free free(node); } @@ -320,11 +322,11 @@ static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_ client->last_client_event = client->first_event; redudp_bump_timeout(client); + list_add(&client->list, &self->clients); + if (redudp_enqeue_pkt(client, buf, pktlen) == -1) goto fail; - list_add(&client->list, &self->clients); - if (self->relay_ss->connect_relay) self->relay_ss->connect_relay(client); @@ -544,7 +546,7 @@ static int redudp_init_instance(redudp_instance *instance) redudp_fini_instance(instance); if (fd != -1) { - redsocks_close(fd); + close(fd); } return -1; @@ -567,7 +569,7 @@ static void redudp_fini_instance(redudp_instance *instance) if (event_initialized(&instance->listener)) { if (event_del(&instance->listener) != 0) log_errno(LOG_WARNING, "event_del"); - redsocks_close(EVENT_FD(&instance->listener)); + close(EVENT_FD(&instance->listener)); memset(&instance->listener, 0, sizeof(instance->listener)); } diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c new file mode 100644 index 00000000..fc4f9c13 --- /dev/null +++ b/shadowsocks-udp.c @@ -0,0 +1,295 @@ +/* redsocks2 - transparent TCP-to-proxy redirector + * Copyright (C) 2013-2015 Zhuofei Wang + * + * This code is based on redsocks project developed by Leonid Evdokimov. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include "utils.h" +#include "log.h" +#include "redsocks.h" +#include "main.h" +#include "redudp.h" +#include "encrypt.h" +#include "shadowsocks.h" + +#define SHARED_BUFF_SIZE 0x10000 //64K + +typedef struct ss_client_t { + struct event udprelay; +} ss_client; + +typedef struct ss_instance_t { + int init; + int method; + enc_info info; + struct enc_ctx e_ctx; + struct enc_ctx d_ctx; + void * buff; + void * buff2; +} ss_instance; + + +static int ss_is_valid_cred(const char *method, const char *password) +{ + if (!method || !password) + return 0; + if (strlen(method) > 255) { + log_error(LOG_WARNING, "Shadowsocks encryption method can't be more than 255 chars."); + return 0; + } + if (strlen(password) > 255) { + log_error(LOG_WARNING, "Shadowsocks encryption password can't be more than 255 chars."); + return 0; + } + return 1; +} + +static void ss_client_init(redudp_client *client) +{ +} + +static void ss_client_fini(redudp_client *client) +{ + ss_client *ssclient = (void*)(client + 1); + if (event_initialized(&ssclient->udprelay)) { + close(EVENT_FD(&ssclient->udprelay)); + if (event_del(&ssclient->udprelay) == -1) + redudp_log_errno(client, LOG_ERR, "event_del"); + } +} + +static void ss_forward_pkt(redudp_client *client, void *data, size_t pktlen) +{ + ss_client *ssclient = (void*)(client + 1); + ss_instance * ss = (ss_instance *)(client->instance+1); + struct sockaddr_in * relayaddr = &client->instance->config.relayaddr; + struct sockaddr_in * destaddr = get_destaddr(client); + struct msghdr msg; + struct iovec io[1]; + ssize_t outgoing; + int rc; + ss_header_ipv4 header; + size_t len = 0; + size_t fwdlen = 0; + + /* build and send header */ + // TODO: Better implementation and IPv6 Support + header.addr_type = ss_addrtype_ipv4; + header.addr = destaddr->sin_addr.s_addr; + header.port = destaddr->sin_port; + + if (enc_ctx_init(&ss->info, &ss->e_ctx, 1)) { + redudp_log_error(client, LOG_ERR, "Shadowsocks UDP failed to initialize encryption context."); + return; + } + + rc = ss_encrypt(&ss->e_ctx, (char *)&header, sizeof(header), ss->buff, &len); + if (rc) + { + if (len + pktlen < SHARED_BUFF_SIZE) + rc = ss_encrypt(&ss->e_ctx, (char *)data, pktlen, ss->buff+len, &fwdlen); + else + rc = 0; + } + enc_ctx_free(&ss->e_ctx); + if (!rc) + { + redudp_log_error(client, LOG_DEBUG, "Can't encrypt packet, dropping it"); + return; + } + fwdlen += len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = relayaddr; + msg.msg_namelen = sizeof(*relayaddr); + msg.msg_iov = io; + msg.msg_iovlen = SIZEOF_ARRAY(io); + + io[0].iov_base = ss->buff; + io[0].iov_len = fwdlen; + + outgoing = sendmsg(EVENT_FD(&ssclient->udprelay), &msg, 0); + if (outgoing == -1) { + redudp_log_errno(client, LOG_DEBUG, "sendmsg: Can't forward packet, dropping it"); + return; + } + else if (outgoing != fwdlen) { + redudp_log_error(client, LOG_DEBUG, "sendmsg: I was sending %zd bytes, but only %zd were sent.", fwdlen, outgoing); + return; + } +} + +static void ss_pkt_from_server(int fd, short what, void *_arg) +{ + redudp_client *client = _arg; + ss_client *ssclient = (void*)(client + 1); + ss_instance * ss = (ss_instance *)(client->instance+1); + ss_header_ipv4 * header; + ssize_t pktlen; + size_t fwdlen; + struct sockaddr_in udprelayaddr; + int rc; + + assert(fd == EVENT_FD(&ssclient->udprelay)); + + pktlen = red_recv_udp_pkt(fd, ss->buff, SHARED_BUFF_SIZE, &udprelayaddr, NULL); + if (pktlen == -1) + return; + + if (enc_ctx_init(&ss->info, &ss->d_ctx, 0)) { + redudp_log_error(client, LOG_ERR, "Shadowsocks UDP failed to initialize decryption context."); + return; + } + rc = ss_decrypt(&ss->d_ctx, ss->buff, pktlen, ss->buff2, &fwdlen); + enc_ctx_free(&ss->d_ctx); + if (!rc) + { + redudp_log_error(client, LOG_DEBUG, "Can't decrypt packet, dropping it"); + return; + } + header = (ss_header_ipv4 *)ss->buff2; + if (header->addr_type != ss_addrtype_ipv4) { + redudp_log_error(client, LOG_DEBUG, "Got address type #%u instead of expected #%u (IPv4).", + header->addr_type, ss_addrtype_ipv4); + return; + } + + if (header->port != get_destaddr(client)->sin_port || + header->addr != get_destaddr(client)->sin_addr.s_addr) + { + char addrbuf[RED_INET_ADDRSTRLEN]; + struct sockaddr_in pktaddr = { + .sin_family = AF_INET, + .sin_addr = { header->addr }, + .sin_port = header->port, + }; + redudp_log_error(client, LOG_NOTICE, "Shadowsocks server relayed packet from unexpected address %s.", + red_inet_ntop(&pktaddr, addrbuf, sizeof(addrbuf))); + return; + } + + if (fwdlen < sizeof(*header)) + { + redudp_log_error(client, LOG_DEBUG, "Packet too short."); + return; + } + + fwdlen -= sizeof(*header); + redudp_fwd_pkt_to_sender(client, ss->buff2 + sizeof(*header), fwdlen); +} + +static int ss_ready_to_fwd(struct redudp_client_t *client) +{ + return 1; +} + +static void ss_connect_relay(redudp_client *client) +{ + ss_client *ssclient = (void*)(client + 1); + struct sockaddr_in * addr = &client->instance->config.relayaddr; + int fd = -1; + int error; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + redudp_log_errno(client, LOG_ERR, "socket"); + goto fail; + } + + error = connect(fd, (struct sockaddr*)addr, sizeof(*addr)); + if (error) { + redudp_log_errno(client, LOG_NOTICE, "connect"); + goto fail; + } + + event_assign(&ssclient->udprelay, get_event_base(), fd, EV_READ | EV_PERSIST, ss_pkt_from_server, client); + error = event_add(&ssclient->udprelay, NULL); + if (error) { + redudp_log_errno(client, LOG_ERR, "event_add"); + goto fail; + } + + redudp_flush_queue(client); + return; + +fail: + if (fd != -1) + close(fd); + redudp_drop_client(client); +} + +static int ss_instance_init(struct redudp_instance_t *instance) +{ + ss_instance * ss = (ss_instance *)(instance+1); + const redudp_config *config = &instance->config; + + int valid_cred = ss_is_valid_cred(config->login, config->password); + if (!valid_cred + || (ss->method = enc_init(&ss->info, config->password, config->login), ss->method == -1)) + { + log_error(LOG_ERR, "Invalided encrytion method or password."); + return -1; + } + else + { + log_error(LOG_INFO, "using encryption method: %s", config->login); + } + ss->buff = malloc(SHARED_BUFF_SIZE); + ss->buff2 = malloc(SHARED_BUFF_SIZE); + if (!ss->buff || !ss->buff2) + { + log_error(LOG_ERR, "Out of memory."); + return -1; + } + + return 0; +} + +static void ss_instance_fini(struct redudp_instance_t *instance) +{ + ss_instance * ss = (ss_instance *)(instance+1); + if (ss->buff) + { + free(ss->buff); + ss->buff = NULL; + } + if (ss->buff2) + { + free(ss->buff2); + ss->buff2 = NULL; + } +} + +udprelay_subsys shadowsocks_udp_subsys = +{ + .name = "shadowsocks", + .payload_len = sizeof(ss_client), + .instance_payload_len = sizeof(ss_instance), + .init = ss_client_init, + .fini = ss_client_fini, + .instance_init = ss_instance_init, + .instance_fini = ss_instance_fini, + .connect_relay = ss_connect_relay, + .forward_pkt = ss_forward_pkt, + .ready_to_fwd = ss_ready_to_fwd, +}; + + +/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ +/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/shadowsocks.c b/shadowsocks.c index f7dead48..cff4c960 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -61,17 +61,19 @@ int ss_is_valid_cred(const char *method, const char *password) return 1; } -void ss_client_init(redsocks_client *client) +static void ss_client_init(redsocks_client *client) { ss_client *sclient = (void*)(client + 1); ss_instance * ss = (ss_instance *)(client->instance+1); client->state = ss_new; - enc_ctx_init(&ss->info, &sclient->e_ctx, 1); - enc_ctx_init(&ss->info, &sclient->d_ctx, 0); + if (enc_ctx_init(&ss->info, &sclient->e_ctx, 1)) + log_error(LOG_ERR, "Shadowsocks failed to initialize encryption context."); + if (enc_ctx_init(&ss->info, &sclient->d_ctx, 0)) + log_error(LOG_ERR, "Shadowsocks failed to initialize decryption context."); } -void ss_client_fini(redsocks_client *client) +static void ss_client_fini(redsocks_client *client) { ss_client *sclient = (void*)(client + 1); enc_ctx_free(&sclient->e_ctx); diff --git a/socks5-udp.c b/socks5-udp.c index 335a9c94..cb29df1f 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -89,13 +90,13 @@ static void socks5_client_fini(redudp_client *client) fd = EVENT_FD(&socks5client->udprelay); if (event_del(&socks5client->udprelay) == -1) redudp_log_errno(client, LOG_ERR, "event_del"); - redsocks_close(fd); + close(fd); } if (socks5client->relay) { fd = EVENT_FD(&socks5client->relay->ev_read); bufferevent_free(socks5client->relay); shutdown(fd, SHUT_RDWR); - redsocks_close(fd); + close(fd); } } @@ -257,7 +258,7 @@ static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg) fail: if (fd != -1) - redsocks_close(fd); + close(fd); redudp_drop_client(client); } From 15da7ff18ec582b20f8c13027a2b60c22ecc67c3 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 12 Apr 2015 21:43:22 +0800 Subject: [PATCH 051/192] Critical update, see full commit message for details. Fix many long standing problems: Hang connections due to improper socket operations. Program stop responding due to improper socket operations. Connection drops due to EOF from client before relay is connected. Bugs in autoproxy. --- README.md | 7 +- autoproxy.c | 283 +++++++++++++++++++++++++++------------------- redsocks.c | 105 ++++++++++------- redsocks.h | 4 + redudp.c | 3 - shadowsocks-udp.c | 1 - shadowsocks.c | 13 +-- 7 files changed, 240 insertions(+), 176 deletions(-) diff --git a/README.md b/README.md index b1166d51..ed2060fe 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ REDSOCKS2 This is a modified version of original redsocks. The name is changed to REDSOCKS2 to distinguish with original redsocks. This variant is useful for anti-GFW (Great Fire Wall). REDSOCKS2 contains -several new features. +several new features besides many bug fixes to original redsocks. 1. Redirect TCP connections which are blocked via proxy automatically without need of blacklist. @@ -11,16 +11,13 @@ need of blacklist. 3. Integrated [shadowsocks](http://shadowsocks.org/) proxy support(IPv4 Only). 4. Redirect TCP connections without proxy. 5. Redirect TCP connections via specified network interface. +6. UDP transparent proxy via shadowsocks proxy. If you feel my work done is helpful, please consider donation. Thanks. **Accept donations by AliPay with account ** [Chinese Reference](https://github.com/semigodking/redsocks/wiki) -HOW it works ------------- -Anyone can help me to complete this part? - HOW TO BUILD ------------ ###Prerequisites diff --git a/autoproxy.c b/autoproxy.c index 6e84fcb4..c00f4777 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -163,7 +163,7 @@ static autoproxy_config * get_config(redsocks_client * client) #define get_autoproxy_client(client) (void*)(client + 1) + client->instance->relay_ss->payload_len; -void auto_client_init(redsocks_client *client) +static void auto_client_init(redsocks_client *client) { autoproxy_client * aclient = get_autoproxy_client(client); @@ -171,7 +171,7 @@ void auto_client_init(redsocks_client *client) aclient->state = AUTOPROXY_NEW; } -void auto_client_fini(redsocks_client *client) +static void auto_client_fini(redsocks_client *client) { autoproxy_client * aclient = get_autoproxy_client(client); @@ -221,24 +221,24 @@ static void auto_recv_timeout_cb(evutil_socket_t fd, short events, void * arg) redsocks_client *client = arg; autoproxy_client * aclient = get_autoproxy_client(client); - redsocks_log_error(client, LOG_DEBUG, "RECV Timeout, state: %d, data_sent: %d", aclient->state, aclient->data_sent); - + redsocks_log_error(client, LOG_DEBUG, "RECV Timeout, state: %d, data_sent: %u", aclient->state, aclient->data_sent); assert(events & EV_TIMEOUT); + + redsocks_touch_client(client); // Let's make connection confirmed if (aclient->state == AUTOPROXY_CONNECTED) auto_confirm_connection(client); else return; - // TODO: need or not? - if (!(client->relay_evshut & EV_READ) && !(client->client_evshut & EV_WRITE)) + // No ERROR/EOF/data received before timeout, continue sending data + if (!(client->relay_evshut & EV_WRITE)) { - if (bufferevent_write_buffer(client->client, bufferevent_get_input(client->relay)) == -1) + if (bufferevent_write_buffer(client->relay, bufferevent_get_input(client->client)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); if (bufferevent_enable(client->client, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } - } @@ -257,98 +257,19 @@ static void direct_relay_readcb_helper(redsocks_client *client, struct buffereve } } - -static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) -{ - redsocks_client *client = _client; - autoproxy_client * aclient = get_autoproxy_client(client); - size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); - - redsocks_log_error(client, LOG_DEBUG, "client readcb: client in: %d", input_size); - redsocks_touch_client(client); - - if (aclient->state == AUTOPROXY_CONNECTED) - { - if (aclient->data_sent && aclient->data_recv) - { - /* No CONNECTION RESET error occur after sending data, good. */ - auto_confirm_connection(client); - } - if (0 == aclient->data_sent) - aclient->data_sent = input_size; - } - direct_relay_readcb_helper(client, client->client, client->relay); -} - - -static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) -{ - redsocks_client *client = _client; - autoproxy_client * aclient = get_autoproxy_client(client); - size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); - - redsocks_touch_client(client); - if (!aclient->data_recv) - { - aclient->data_recv = input_size; - if (input_size && aclient->state == AUTOPROXY_CONNECTED) - { - auto_confirm_connection(client); - } - } - direct_relay_readcb_helper(client, client->relay, client->client); -} - -static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) -{ - redsocks_client *client = _client; - autoproxy_client * aclient = get_autoproxy_client(client); - struct bufferevent * from = client->relay; - size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); - size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); - - redsocks_touch_client(client); - - if (input_size == 0 && (client->relay_evshut & EV_READ)) - { - redsocks_shutdown(client, to, SHUT_WR); - return; - } - if (aclient->state == AUTOPROXY_CONNECTED) - { - if (!aclient->data_recv) - { - aclient->data_recv = input_size; - if (input_size) - auto_confirm_connection(client); - } - } - if (output_size < to->wm_write.high) - { - if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); - } -} - -static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) +// Caller should continue writing if this function returns 0. +// Otherwise, stop writing. +static int handle_write_to_relay(redsocks_client *client) { - redsocks_client *client = _client; autoproxy_client * aclient = get_autoproxy_client(client); struct bufferevent * from = client->client; + struct bufferevent * to = client->relay; size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); - redsocks_touch_client(client); - - if (input_size == 0 && (client->client_evshut & EV_READ)) { - redsocks_shutdown(client, to, SHUT_WR); - return; - } - else if (aclient->state == AUTOPROXY_CONNECTED ) + if (aclient->state == AUTOPROXY_CONNECTED ) { - redsocks_log_error(client, LOG_DEBUG, "sent: %d, recv: %d, in:%d, out:%d", + redsocks_log_error(client, LOG_DEBUG, "sent: %u, recv: %u, in:%u, out:%u", aclient->data_sent, aclient->data_recv, input_size, @@ -362,6 +283,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) { /* copy data from input to output of relay */ aclient->data_sent = copy_evbuffer (to, from, 0); + return 1; } } /* @@ -394,9 +316,10 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) else if (output_size < aclient->data_sent /* data is sent out, more or less */ && input_size == aclient->data_sent /* all data in read buffer is sent */ + && !aclient->recv_timer_event /* timer is not activated yet */ ) { - aclient->recv_timer_event = evtimer_new(bufferevent_get_base(to), auto_recv_timeout_cb , _client); + aclient->recv_timer_event = evtimer_new(bufferevent_get_base(to), auto_recv_timeout_cb, client); if (aclient->recv_timer_event) { struct timeval tv; @@ -409,9 +332,11 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) // Let's confirm the connection directly so that normal service is not // impacted. auto_confirm_connection(client); + return 0; } } } + return 1; } /* We sent data to and got data from relay. */ else if (aclient->data_sent && aclient->data_recv) @@ -420,7 +345,115 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) auto_confirm_connection(client); } } + return 0; +} + +// Caller should continue writing if this function returns 0. +// Otherwise, stop writing. +static int handle_write_to_client(redsocks_client *client) +{ + autoproxy_client * aclient = get_autoproxy_client(client); + struct bufferevent * from = client->relay; + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + + if (aclient->state == AUTOPROXY_CONNECTED) + { + if (!aclient->data_recv) + { + aclient->data_recv = input_size; + if (input_size) + auto_confirm_connection(client); + } + } + return 0; +} + +static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) +{ + redsocks_client *client = _client; + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + + redsocks_log_error(client, LOG_DEBUG, "client in: %u", input_size); + redsocks_touch_client(client); + if (handle_write_to_relay(client)) + return; + direct_relay_readcb_helper(client, client->client, client->relay); +} + + +static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) +{ + redsocks_client *client = _client; + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + + redsocks_log_error(client, LOG_DEBUG, "relay in: %u", input_size); + redsocks_touch_client(client); + if (handle_write_to_client(client)) + return; + direct_relay_readcb_helper(client, client->relay, client->client); +} + +static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) +{ + redsocks_client *client = _client; + struct bufferevent * from = client->relay; + size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); + + redsocks_touch_client(client); + if (process_shutdown_on_write_(client, from, to)) + return; + if (handle_write_to_client(client)) + return; + if (output_size < to->wm_write.high) + { + if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); + if (bufferevent_enable(from, EV_READ) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + } +} + +static int process_shutdown_on_write_2(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) +{ + autoproxy_client * aclient = get_autoproxy_client(client); + size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); + unsigned short from_evshut = from == client->client ? client->client_evshut : client->relay_evshut; + unsigned short to_evshut = to == client->client ? client->client_evshut : client->relay_evshut; + + redsocks_log_error(client, LOG_DEBUG, "WCB %s, fs: %u, ts: %u, fin: %u, fout: %u, tin: %u", + to == client->client?"client":"relay", + from_evshut, + to_evshut, + evbuffer_get_length(bufferevent_get_input(from)), + evbuffer_get_length(bufferevent_get_output(from)), + evbuffer_get_length(bufferevent_get_input(to))); + + if ((from_evshut & EV_READ) && !(to_evshut & EV_WRITE)) + { + if (input_size == 0 + || (input_size == aclient->data_sent && aclient->state == AUTOPROXY_CONNECTED)) + { + redsocks_shutdown(client, to, SHUT_WR); + return 1; + } + } + return 0; +} + +static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) +{ + redsocks_client *client = _client; + autoproxy_client * aclient = get_autoproxy_client(client); + struct bufferevent * from = client->client; + size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); + + redsocks_touch_client(client); + + if (process_shutdown_on_write_2(client, from, to)) + return; + if (handle_write_to_relay(client)) + return; if (aclient->state == AUTOPROXY_CONFIRMED) { if (output_size < to->wm_write.high) @@ -435,14 +468,16 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) static void auto_drop_relay(redsocks_client *client) { + int fd; if (client->relay) { - redsocks_log_error(client, LOG_DEBUG, "dropping relay only"); - - redsocks_close(bufferevent_getfd(client->relay)); + redsocks_log_error(client, LOG_DEBUG, "dropping relay only "); + fd = bufferevent_getfd(client->relay); bufferevent_free(client->relay); + redsocks_close(fd); client->relay = NULL; } + client->relay_connected = 0; } static void auto_retry(redsocks_client * client, int updcache) @@ -475,7 +510,8 @@ static void auto_retry(redsocks_client * client, int updcache) // restore callbacks for ordinary client. bufferevent_setcb(client->client, NULL, NULL, redsocks_event_error, client); // enable reading to handle EOF from client - bufferevent_enable(client->client, EV_READ); + if (!(client->client_evshut & EV_READ)) + bufferevent_enable(client->client, EV_READ); /* connect to relay */ if (client->instance->relay_ss->connect_relay) @@ -506,7 +542,6 @@ static int auto_retry_or_drop(redsocks_client * client) auto_retry(client, 0); return 0; } - /* drop */ return 1; } @@ -531,6 +566,7 @@ static void auto_relay_connected(struct bufferevent *buffev, void *_arg) /* update client state */ aclient->state = AUTOPROXY_CONNECTED; + client->relay_connected = 1; /* We do not need to detect timeouts any more. The two peers will handle it. */ @@ -560,18 +596,22 @@ static void auto_relay_connected(struct bufferevent *buffev, void *_arg) return; } +// Note: before relay is connected, READING EOF/ERROR reported from client +// is handled by redsocks default ERROR handler. static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) { redsocks_client *client = _arg; autoproxy_client * aclient = get_autoproxy_client(client); int saved_errno = errno; + assert(buffev == client->relay || buffev == client->client); - redsocks_touch_client(client); - + + if (!(what & BEV_EVENT_ERROR)) + errno = red_socket_geterrno(buffev); redsocks_log_errno(client, LOG_DEBUG, "%s, errno(%d), State: %d, what: " event_fmt_str, buffev == client->client?"client":"relay", - saved_errno, aclient->state, event_fmt(what)); + errno, aclient->state, event_fmt(what)); if (buffev == client->relay) { /* @@ -603,7 +643,11 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) if (aclient->state == AUTOPROXY_CONNECTED && what == (BEV_EVENT_READING|BEV_EVENT_ERROR) - && saved_errno == ECONNRESET ) + /* No matter it is disconnected due to Connection Reset or any + other reason, we still have a chance to forward connection via + proxy. I prefer retry only if we got connection reset. + */ + && saved_errno == ECONNRESET) { if (!auto_retry_or_drop(client)) return; @@ -612,24 +656,26 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) if (what == (BEV_EVENT_READING|BEV_EVENT_EOF)) { - struct bufferevent *antiev; - if (buffev == client->relay) - antiev = client->client; - else - antiev = client->relay; + // Timer cases: + // 1. READ EOF from relay (normal case, need to releae timer) + // 2. READ EOF from client (normal case, no need to release timer) + // 3. READ ERROR from client (abnormal, not recoverable) + // 4. READ ERROR from erlay (abnormal, not recoverable) + if (aclient->recv_timer_event && buffev == client->relay) + auto_confirm_connection(client); - // Release timer - if (aclient->recv_timer_event) + redsocks_shutdown(client, buffev, SHUT_RD); + // Ensure the other party could send remaining data and SHUT_WR also + if (buffev == client->client) { - event_del(aclient->recv_timer_event); - event_free(aclient->recv_timer_event); - aclient->recv_timer_event = NULL; + if (!(client->relay_evshut & EV_WRITE)) + bufferevent_enable(client->relay, EV_WRITE); + } + else + { + if (!(client->client_evshut & EV_WRITE)) + bufferevent_enable(client->client, EV_WRITE); } - - redsocks_shutdown(client, buffev, SHUT_RD); - - if (antiev != NULL && evbuffer_get_length(bufferevent_get_output(antiev)) == 0) - redsocks_shutdown(client, antiev, SHUT_WR); } else { @@ -695,6 +741,7 @@ static void auto_connect_relay(redsocks_client *client) else { redsocks_log_errno(client, LOG_ERR, "invalid state: %d", aclient->state); + redsocks_drop_client(client); } } diff --git a/redsocks.c b/redsocks.c index 89eff1f0..085f721f 100644 --- a/redsocks.c +++ b/redsocks.c @@ -42,7 +42,7 @@ #define REDSOCKS_RELAY_HALFBUFF 1024*32 -#define REDSOCKS_AUDIT_INTERVAL 60 +#define REDSOCKS_AUDIT_INTERVAL 60*2 void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); static void redsocks_relay_relayreadcb(struct bufferevent *from, void *_client); @@ -259,6 +259,9 @@ void redsocks_touch_client(redsocks_client *client) static void redsocks_relay_readcb(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) { + redsocks_log_error(client, LOG_DEBUG, "RCB %s, in: %u", from == client->client?"client":"relay", + evbuffer_get_length(bufferevent_get_input(from))); + if (evbuffer_get_length(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); @@ -271,15 +274,36 @@ static void redsocks_relay_readcb(redsocks_client *client, struct bufferevent *f } } -static void redsocks_relay_writecb(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) +int process_shutdown_on_write_(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) { assert(from == client->client || from == client->relay); - char from_eof = (from == client->client ? client->client_evshut : client->relay_evshut) & EV_READ; - - if (evbuffer_get_length(bufferevent_get_input(from)) == 0 && from_eof) { + unsigned short from_evshut = from == client->client ? client->client_evshut : client->relay_evshut; + unsigned short to_evshut = to == client->client ? client->client_evshut : client->relay_evshut; + + redsocks_log_error(client, LOG_DEBUG, "WCB %s, fs: %u, ts: %u, fin: %u, fout: %u, tin: %u", + to == client->client?"client":"relay", + from_evshut, + to_evshut, + evbuffer_get_length(bufferevent_get_input(from)), + evbuffer_get_length(bufferevent_get_output(from)), + evbuffer_get_length(bufferevent_get_input(to))); + + if (evbuffer_get_length(bufferevent_get_input(from)) == 0 + && (from_evshut & EV_READ) + && !(to_evshut & EV_WRITE)) { redsocks_shutdown(client, to, SHUT_WR); + return 1; } - else if (evbuffer_get_length(to->output) < to->wm_write.high) { + return 0; +} + +static void redsocks_relay_writecb(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) +{ + assert(from == client->client || from == client->relay); + + if (process_shutdown_on_write_(client, from, to)) + return; + if (evbuffer_get_length(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); if (bufferevent_enable(from, EV_READ) == -1) @@ -342,7 +366,10 @@ int redsocks_start_relay(redsocks_client *client) event_cb, client); - error = bufferevent_enable(client->client, EV_READ | EV_WRITE); + error = bufferevent_enable(client->client, + client->client_evshut == EV_READ ? EV_WRITE : + client->client_evshut == EV_WRITE ? EV_READ : + client->client_evshut == (EV_READ|EV_WRITE) ? 0 : EV_READ | EV_WRITE); if (!error) error = bufferevent_enable(client->relay, EV_READ | EV_WRITE); @@ -358,21 +385,29 @@ int redsocks_start_relay(redsocks_client *client) void redsocks_drop_client(redsocks_client *client) { + int fd; redsocks_log_error(client, LOG_DEBUG, "dropping client"); if (client->instance->config.autoproxy && autoproxy_subsys.fini) autoproxy_subsys.fini(client); + if (client->instance->relay_ss->fini) client->instance->relay_ss->fini(client); if (client->client) { - redsocks_close(bufferevent_getfd(client->client)); + fd = bufferevent_getfd(client->client); bufferevent_free(client->client); + if (!(client->client_evshut & EV_WRITE)) + shutdown(fd, SHUT_WR); + redsocks_close(fd); } if (client->relay) { - redsocks_close(bufferevent_getfd(client->relay)); + fd = bufferevent_getfd(client->relay); bufferevent_free(client->relay); + if (!(client->relay_evshut & EV_WRITE)) + shutdown(fd, SHUT_WR); + redsocks_close(fd); } list_del(&client->list); @@ -409,6 +444,9 @@ void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int strev = buffev == client->client ? "client" : "relay"; pevshut = buffev == client->client ? &client->client_evshut : &client->relay_evshut; + if (bufferevent_disable(buffev, evhow) != 0) + redsocks_log_errno(client, LOG_ERR, "bufferevent_disable(%s, %s)", strev, strevhow); + // if EV_WRITE is already shut and we're going to shutdown read then // we're either going to abort data flow (bad behaviour) or confirm EOF // and in this case socket is already SHUT_RD'ed @@ -416,9 +454,6 @@ void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int if (shutdown(bufferevent_getfd(buffev), how) != 0) redsocks_log_errno(client, LOG_ERR, "shutdown(%s, %s)", strev, strhow); - if (bufferevent_disable(buffev, evhow) != 0) - redsocks_log_errno(client, LOG_ERR, "bufferevent_disable(%s, %s)", strev, strevhow); - *pevshut |= evhow; if (client->relay_evshut == (EV_READ|EV_WRITE) && client->client_evshut == (EV_READ|EV_WRITE)) { @@ -445,27 +480,27 @@ void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg) redsocks_touch_client(client); - redsocks_log_errno(client, LOG_DEBUG, "%s, what: " event_fmt_str, + if (!(what & BEV_EVENT_ERROR)) + errno = redsocks_socket_geterrno(client, buffev); + redsocks_log_errno(client, LOG_DEBUG, "%s, what: " event_fmt_str, buffev == client->client?"client":"relay", event_fmt(what)); if (what == (BEV_EVENT_READING|BEV_EVENT_EOF)) { - struct bufferevent *antiev; - if (buffev == client->relay) - antiev = client->client; - else - antiev = client->relay; - redsocks_shutdown(client, buffev, SHUT_RD); - - if (antiev != NULL && evbuffer_get_length(bufferevent_get_output(antiev)) == 0) - redsocks_shutdown(client, antiev, SHUT_WR); + // Ensure the other party could send remaining data and SHUT_WR also + if (buffev == client->client) + { + if (!(client->relay_evshut & EV_WRITE) && client->relay_connected) + bufferevent_enable(client->relay, EV_WRITE); + } + else + { + if (!(client->client_evshut & EV_WRITE)) + bufferevent_enable(client->client, EV_WRITE); + } } else { - errno = redsocks_socket_geterrno(client, buffev); - redsocks_log_errno(client, LOG_NOTICE, "%s error, code " event_fmt_str, - buffev == client->relay ? "relay" : "client", - event_fmt(what)); redsocks_drop_client(client); } } @@ -579,7 +614,6 @@ int redsocks_write_helper( void redsocks_relay_connected(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; - assert(buffev == client->relay); redsocks_touch_client(client); @@ -588,11 +622,10 @@ void redsocks_relay_connected(struct bufferevent *buffev, void *_arg) redsocks_log_errno(client, LOG_NOTICE, "red_is_socket_connected_ok"); goto fail; } - + client->relay_connected = 1; /* We do not need to detect timeouts any more. The two peers will handle it. */ bufferevent_set_timeouts(client->relay, NULL, NULL); - bufferevent_setcb(client->relay, client->instance->relay_ss->readcb, client->instance->relay_ss->writecb, redsocks_event_error, @@ -750,14 +783,14 @@ static void redsocks_accept_client(int fd, short what, void *_arg) client_fd = -1; - list_add(&client->list, &self->clients); - // enable reading to handle EOF from client if (bufferevent_enable(client->client, EV_READ) != 0) { redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); goto fail; } + list_add(&client->list, &self->clients); + redsocks_log_error(client, LOG_DEBUG, "accepted"); if (self->config.autoproxy && autoproxy_subsys.connect_relay) @@ -804,7 +837,7 @@ void redsocks_dump_client(redsocks_client * client, int loglevel) const char *s_client_evshut = redsocks_evshut_str(client->client_evshut); const char *s_relay_evshut = redsocks_evshut_str(client->relay_evshut); - redsocks_log_error(client, loglevel, "client: %i (%s)%s%s input %d output %d, relay: %i (%s)%s%s input %d output %d, age: %li sec, idle: %li sec.", + redsocks_log_error(client, loglevel, "client(%i): (%s)%s%s input %u output %u, relay(%i): (%s)%s%s input %u output %u, age: %li sec, idle: %li sec.", bufferevent_getfd(client->client), redsocks_event_str(bufferevent_get_enabled(client->client)), s_client_evshut[0] ? " " : "", s_client_evshut, @@ -862,15 +895,9 @@ static void redsocks_audit_instance(redsocks_instance *instance) || (client->client_evshut == EV_READ && client->relay_evshut == EV_WRITE) || (client->client_evshut == (EV_READ|EV_WRITE) && client->relay_evshut == EV_WRITE)) drop_it = 1; - if (client->client->enabled == 0 && client->relay->enabled== EV_WRITE - && client->client_evshut == 0 && client->relay_evshut == 0 ) - { - /* for debug purpose only */ - redsocks_log_error(client, LOG_DEBUG, "%i connect: %i",bufferevent_getfd(client->client), red_is_socket_connected_ok(client->relay)); - } } /* close long connections without activities */ - if (now - client->last_event >= 3600 * 4) + if (now - client->last_event >= 3600 * 2) drop_it = 1; if (drop_it){ diff --git a/redsocks.h b/redsocks.h index b1ce38c6..8e394693 100644 --- a/redsocks.h +++ b/redsocks.h @@ -64,6 +64,7 @@ typedef struct redsocks_client_t { struct sockaddr_in clientaddr; struct sockaddr_in destaddr; int state; // it's used by bottom layer + int relay_connected; unsigned short client_evshut; unsigned short relay_evshut; time_t first_event; @@ -115,6 +116,9 @@ void redsocks_log_write_plain( __attribute__ (( format (printf, 8, 9) )) #endif ; +/* unsafe internal functions. Only use them when you know exactly what +you are doing with */ +int process_shutdown_on_write_(redsocks_client *client, struct bufferevent *from, struct bufferevent *to); /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/redudp.c b/redudp.c index f04938b9..dc420f90 100644 --- a/redudp.c +++ b/redudp.c @@ -198,17 +198,14 @@ void redudp_drop_client(redudp_client *client) if (event_del(&client->timeout) == -1) redudp_log_errno(client, LOG_ERR, "event_del"); } - redudp_log_error(client, LOG_DEBUG, "Dropping...2"); if (client->sender_fd != -1) bound_udp4_put(&client->destaddr); list_for_each_entry_safe(q, tmp, &client->queue, list) { list_del(&q->list); free(q); } - redudp_log_error(client, LOG_DEBUG, "Dropping...3"); list_del(&client->list); free(client); - redudp_log_error(client, LOG_DEBUG, "Dropping...4"); } void redudp_bump_timeout(redudp_client *client) diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index fc4f9c13..0d70c25d 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -98,7 +98,6 @@ static void ss_forward_pkt(redudp_client *client, void *data, size_t pktlen) redudp_log_error(client, LOG_ERR, "Shadowsocks UDP failed to initialize encryption context."); return; } - rc = ss_encrypt(&ss->e_ctx, (char *)&header, sizeof(header), ss->buff, &len); if (rc) { diff --git a/shadowsocks.c b/shadowsocks.c index cff4c960..2da42a6a 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -150,18 +150,14 @@ static void ss_client_writecb(struct bufferevent *buffev, void *_arg) redsocks_client *client = _arg; struct bufferevent * from = client->relay; struct bufferevent * to = buffev; - char from_eof = client->relay_evshut & EV_READ; size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from)); size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); assert(buffev == client->client); redsocks_touch_client(client); - if (input_size == 0 && from_eof) - { - redsocks_shutdown(client, to, SHUT_WR); + if (process_shutdown_on_write_(client, from, to)) return; - } if (client->state == ss_connected) { @@ -217,18 +213,14 @@ static void ss_relay_writecb(struct bufferevent *buffev, void *_arg) redsocks_client *client = _arg; struct bufferevent * from = client->client; struct bufferevent * to = buffev; - char from_eof = client->client_evshut & EV_READ; size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from)); size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); assert(buffev == client->relay); redsocks_touch_client(client); - if (input_size == 0 && from_eof) - { - redsocks_shutdown(client, to, SHUT_WR); + if (process_shutdown_on_write_(client, from, to)) return; - } if (client->state == ss_connected) { @@ -296,6 +288,7 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) return; } + client->relay_connected = 1; /* We do not need to detect timeouts any more. The two peers will handle it. */ bufferevent_set_timeouts(client->relay, NULL, NULL); From 03123cc63c1c5c17459c9fe557fbb8ebacd14a70 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 20 Apr 2015 10:41:16 +0800 Subject: [PATCH 052/192] Fix: Segv when redsocks failed to setup socket for relay This issue happens when interface down while handling redirections. --- autoproxy.c | 21 +++++++++++---------- direct.c | 4 +++- http-relay.c | 5 +++-- redsocks.c | 6 +++--- redsocks.h | 5 +++-- shadowsocks.c | 6 ++++-- 6 files changed, 27 insertions(+), 20 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index c00f4777..293cf751 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -52,7 +52,6 @@ typedef struct autoproxy_client_t { void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg); static int auto_retry_or_drop(redsocks_client * client); -static void auto_connect_relay(redsocks_client *client); static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); static void auto_event_error(struct bufferevent *buffev, short what, void *_arg); @@ -480,9 +479,10 @@ static void auto_drop_relay(redsocks_client *client) client->relay_connected = 0; } -static void auto_retry(redsocks_client * client, int updcache) +static int auto_retry(redsocks_client * client, int updcache) { autoproxy_client * aclient = get_autoproxy_client(client); + int rc; if (aclient->state == AUTOPROXY_CONNECTED) bufferevent_disable(client->client, EV_READ| EV_WRITE); @@ -516,11 +516,11 @@ static void auto_retry(redsocks_client * client, int updcache) /* connect to relay */ if (client->instance->relay_ss->connect_relay) { - client->instance->relay_ss->connect_relay(client); + rc = client->instance->relay_ss->connect_relay(client); // In case the underline relay system does not connect relay, // it maybe is waiting for client read event. // Take 'http-relay' for example. - if (!client->relay && evbuffer_get_length(bufferevent_get_input(client->client))) + if (!rc && !client->relay && evbuffer_get_length(bufferevent_get_input(client->client))) #ifdef bufferevent_trigger_event bufferevent_trigger_event(client->client, EV_READ, 0); #else @@ -528,7 +528,8 @@ static void auto_retry(redsocks_client * client, int updcache) #endif } else - redsocks_connect_relay(client); + rc = redsocks_connect_relay(client); + return rc; } /* return 1 for drop, 0 for retry. */ @@ -684,7 +685,7 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) } -static void auto_connect_relay(redsocks_client *client) +static int auto_connect_relay(redsocks_client *client) { autoproxy_client * aclient = get_autoproxy_client(client); autoproxy_config * config = NULL; @@ -709,10 +710,7 @@ static void auto_connect_relay(redsocks_client *client) // less than NO_CHECK_SECONDS. Just let it go via proxy. if (config->no_quick_check_seconds == 0 || now - *acc_time < config->no_quick_check_seconds) - { - auto_retry(client, 0); - return; - } + return auto_retry(client, 0); /* update timeout value for quick detection. * Sometimes, good sites are added into cache due to occasionally * connection timeout. It is annoying. So, decision is made to @@ -736,13 +734,16 @@ static void auto_connect_relay(redsocks_client *client) if (!client->relay) { redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); redsocks_drop_client(client); + return -1; } } else { redsocks_log_errno(client, LOG_ERR, "invalid state: %d", aclient->state); redsocks_drop_client(client); + return -1; } + return 0; } diff --git a/direct.c b/direct.c index 6fa38b8d..300584d5 100644 --- a/direct.c +++ b/direct.c @@ -62,7 +62,7 @@ static void direct_write_cb(struct bufferevent *buffev, void *_arg) } } -void direct_connect_relay(redsocks_client *client) +static int direct_connect_relay(redsocks_client *client) { char * interface = client->instance->config.interface; // Allowing binding relay socket to specified IP for outgoing connections @@ -79,7 +79,9 @@ void direct_connect_relay(redsocks_client *client) { redsocks_log_errno(client, LOG_ERR, "red_connect_relay"); redsocks_drop_client(client); + return -1; } + return 0; } relay_subsys direct_connect_subsys = diff --git a/http-relay.c b/http-relay.c index fb1671de..e0adc948 100644 --- a/http-relay.c +++ b/http-relay.c @@ -60,7 +60,7 @@ typedef struct httpr_client_t { extern const char *auth_request_header; extern const char *auth_response_header; -static void httpr_connect_relay(redsocks_client *client); +static int httpr_connect_relay(redsocks_client *client); static int httpr_buffer_init(httpr_buffer *buff) { @@ -558,7 +558,7 @@ static void httpr_client_read_cb(struct bufferevent *buffev, void *_arg) } } -static void httpr_connect_relay(redsocks_client *client) +static int httpr_connect_relay(redsocks_client *client) { int error; @@ -568,6 +568,7 @@ static void httpr_connect_relay(redsocks_client *client) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); redsocks_drop_client(client); } + return error; } relay_subsys http_relay_subsys = diff --git a/redsocks.c b/redsocks.c index 085f721f..0e855680 100644 --- a/redsocks.c +++ b/redsocks.c @@ -43,8 +43,6 @@ #define REDSOCKS_RELAY_HALFBUFF 1024*32 #define REDSOCKS_AUDIT_INTERVAL 60*2 -void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); - static void redsocks_relay_relayreadcb(struct bufferevent *from, void *_client); static void redsocks_relay_relaywritecb(struct bufferevent *from, void *_client); void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg); @@ -637,7 +635,7 @@ void redsocks_relay_connected(struct bufferevent *buffev, void *_arg) redsocks_drop_client(client); } -void redsocks_connect_relay(redsocks_client *client) +int redsocks_connect_relay(redsocks_client *client) { struct timeval tv; tv.tv_sec = client->instance->config.timeout; @@ -653,7 +651,9 @@ void redsocks_connect_relay(redsocks_client *client) if (!client->relay) { redsocks_log_errno(client, LOG_ERR, "red_connect_relay failed!!!"); redsocks_drop_client(client); + return -1; } + return 0; } static void redsocks_accept_backoff(int fd, short what, void *_arg) diff --git a/redsocks.h b/redsocks.h index 8e394693..4dbb8ca9 100644 --- a/redsocks.h +++ b/redsocks.h @@ -23,7 +23,8 @@ typedef struct relay_subsys_t { int (*instance_init)(struct redsocks_instance_t *instance); void (*instance_fini)(struct redsocks_instance_t *instance); // connect_relay (if any) is called instead of redsocks_connect_relay after client connection acceptance - void (*connect_relay)(struct redsocks_client_t *client); + // It must returns 0 on success, returns -1 or error code on failures. + int (*connect_relay)(struct redsocks_client_t *client); //void (*relay_connected)(struct redsocks_client_t *client); } relay_subsys; @@ -74,7 +75,7 @@ typedef struct redsocks_client_t { void redsocks_drop_client(redsocks_client *client); void redsocks_touch_client(redsocks_client *client); -void redsocks_connect_relay(redsocks_client *client); +int redsocks_connect_relay(redsocks_client *client); int redsocks_start_relay(redsocks_client *client); void redsocks_dump_client(redsocks_client * client, int loglevel); void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); diff --git a/shadowsocks.c b/shadowsocks.c index 2da42a6a..4812b13c 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -230,7 +230,7 @@ static void ss_relay_writecb(struct bufferevent *buffev, void *_arg) if (input_size) encrypt_buffer(client, from, to); if (bufferevent_enable(from, EV_READ) == -1) - redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); + redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } } else @@ -330,7 +330,7 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) } -static void ss_connect_relay(redsocks_client *client) +static int ss_connect_relay(redsocks_client *client) { struct timeval tv; @@ -347,7 +347,9 @@ static void ss_connect_relay(redsocks_client *client) if (!client->relay) { redsocks_log_errno(client, LOG_ERR, "ss_connect_relay"); redsocks_drop_client(client); + return -1; } + return 0; } static int ss_instance_init(struct redsocks_instance_t *instance) From 917a374c55d7b16a964620e47eba39447acb4932 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 21 Apr 2015 10:33:18 +0800 Subject: [PATCH 053/192] Do not disable read if SHUT_RD is set --- autoproxy.c | 6 +++--- redsocks.c | 3 ++- shadowsocks.c | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index 293cf751..6bfa98d1 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -235,7 +235,7 @@ static void auto_recv_timeout_cb(evutil_socket_t fd, short events, void * arg) { if (bufferevent_write_buffer(client->relay, bufferevent_get_input(client->client)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(client->client, EV_READ) == -1) + if (!(client->client_evshut & EV_READ) && bufferevent_enable(client->client, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } } @@ -407,7 +407,7 @@ static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) { if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) + if (!(client->relay_evshut & EV_READ) && bufferevent_enable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } } @@ -459,7 +459,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) { if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) + if (!(client->client_evshut & EV_READ) && bufferevent_enable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } } diff --git a/redsocks.c b/redsocks.c index 0e855680..9af63566 100644 --- a/redsocks.c +++ b/redsocks.c @@ -297,6 +297,7 @@ int process_shutdown_on_write_(redsocks_client *client, struct bufferevent *from static void redsocks_relay_writecb(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) { + unsigned short from_evshut = from == client->client ? client->client_evshut : client->relay_evshut; assert(from == client->client || from == client->relay); if (process_shutdown_on_write_(client, from, to)) @@ -304,7 +305,7 @@ static void redsocks_relay_writecb(redsocks_client *client, struct bufferevent * if (evbuffer_get_length(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); - if (bufferevent_enable(from, EV_READ) == -1) + if (!(from_evshut & EV_READ) && bufferevent_enable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } } diff --git a/shadowsocks.c b/shadowsocks.c index 4812b13c..b7b27426 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -166,7 +166,7 @@ static void ss_client_writecb(struct bufferevent *buffev, void *_arg) { if (input_size) decrypt_buffer(client, from, to); - if (bufferevent_enable(from, EV_READ) == -1) + if (!(client->relay_evshut & EV_READ) && bufferevent_enable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } } @@ -229,7 +229,7 @@ static void ss_relay_writecb(struct bufferevent *buffev, void *_arg) { if (input_size) encrypt_buffer(client, from, to); - if (bufferevent_enable(from, EV_READ) == -1) + if (!(client->client_evshut & EV_READ) && bufferevent_enable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } } From d01c2a34e1d42a2d705f2674d76f00013847250d Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 8 May 2015 13:04:21 +0800 Subject: [PATCH 054/192] Fix: UDP transparent proxy with TPROXY not working --- redudp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/redudp.c b/redudp.c index dc420f90..1218bc55 100644 --- a/redudp.c +++ b/redudp.c @@ -115,6 +115,11 @@ static int bound_udp4_get(const struct sockaddr_in *addr) if (0 != redudp_transparent(node->fd)) goto fail; + if (evutil_make_listen_socket_reuseable(node->fd)) { + log_errno(LOG_ERR, "evutil_make_listen_socket_reuseable"); + goto fail; + } + if (0 != bind(node->fd, (struct sockaddr*)addr, sizeof(*addr))) { log_errno(LOG_ERR, "bind"); goto fail; From ffad0b506e1d4fb5d5232a529d76000febfe307a Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 17 Jul 2015 12:45:12 +0800 Subject: [PATCH 055/192] Make git ignore generated files --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5930ef53..92269858 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.o config.h tags -redsocks +redsocks2 +gen/ .depend From 68a479e0720b84e8429b1037c5efa9b953c5bb6c Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 7 Aug 2015 14:35:49 +0800 Subject: [PATCH 056/192] Handle log level correctly and efficiently --- log.c | 35 ++++++++++++++--------------------- log.h | 2 ++ 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/log.c b/log.c index e25073be..b3311e79 100644 --- a/log.c +++ b/log.c @@ -23,8 +23,6 @@ #include "utils.h" #include "log.h" -static const char *lowmem = ""; - typedef void (*log_func)(const char *file, int line, const char *func, int priority, const char *message, const char *appendix); static void fprint_timestamp( @@ -65,19 +63,22 @@ static void syslog_msg(const char *file, int line, const char *func, int priorit static log_func log_msg = stderr_msg; static log_func log_msg_next = NULL; - +static int log_mask = LOG_MASK(LOG_NOTICE)|LOG_MASK(LOG_WARNING)|LOG_MASK(LOG_ERR); int log_preopen(const char *dst, bool log_debug, bool log_info) { const char *syslog_prefix = "syslog:"; const char *file_prefix = "file:"; + if (log_debug) + log_mask |= LOG_MASK(LOG_DEBUG); + if (log_info) + log_mask |= LOG_MASK(LOG_INFO); if (strcmp(dst, "stderr") == 0) { log_msg_next = stderr_msg; } else if (strncmp(dst, syslog_prefix, strlen(syslog_prefix)) == 0) { const char *facility_name = dst + strlen(syslog_prefix); int facility = -1; - int logmask; struct { char *name; int value; } *ptpl, tpl[] = { @@ -104,12 +105,12 @@ int log_preopen(const char *dst, bool log_debug, bool log_info) openlog("redsocks", LOG_NDELAY | LOG_PID, facility); - logmask = setlogmask(0); + log_mask = setlogmask(0); if (!log_debug) - logmask &= ~(LOG_MASK(LOG_DEBUG)); + log_mask &= ~(LOG_MASK(LOG_DEBUG)); if (!log_info) - logmask &= ~(LOG_MASK(LOG_INFO)); - setlogmask(logmask); + log_mask &= ~(LOG_MASK(LOG_INFO)); + setlogmask(log_mask); log_msg_next = syslog_msg; } @@ -138,20 +139,12 @@ void log_open() void _log_vwrite(const char *file, int line, const char *func, int do_errno, int priority, const char *fmt, va_list ap) { int saved_errno = errno; - struct evbuffer *buff = evbuffer_new(); - const char *message; - - if (buff) { - evbuffer_add_vprintf(buff, fmt, ap); - message = (const char*)EVBUFFER_DATA(buff); - } - else - message = lowmem; - - log_msg(file, line, func, priority, message, do_errno ? strerror(saved_errno) : NULL); + char message[MAX_LOG_LENGTH+1]; - if (buff) - evbuffer_free(buff); + if (!(log_mask & LOG_MASK(priority))) + return; + vsnprintf(&message[0], sizeof(message), fmt, ap); + log_msg(file, line, func, priority, &message[0], do_errno ? strerror(saved_errno) : NULL); } void _log_write(const char *file, int line, const char *func, int do_errno, int priority, const char *fmt, ...) diff --git a/log.h b/log.h index 38987ca7..92f51489 100644 --- a/log.h +++ b/log.h @@ -5,6 +5,8 @@ #include #include +#define MAX_LOG_LENGTH 512 + #define log_errno(prio, msg...) _log_write(__FILE__, __LINE__, __func__, 1, prio, ## msg) #define log_error(prio, msg...) _log_write(__FILE__, __LINE__, __func__, 0, prio, ## msg) From 81058bebad6caea9f241f46aa5e4d68594837c4d Mon Sep 17 00:00:00 2001 From: Soff Date: Sat, 22 Aug 2015 16:54:25 +0800 Subject: [PATCH 057/192] Update README.md fix a typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed2060fe..4d98c601 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ The configuration for forwarding connections to GoAgent is like below: timeout = 13; } -###Redirect UPD based DNS Request via TCP connection +###Redirect UDP based DNS Request via TCP connection Sending DNS request via TCP connection is one way to prevent from DNS poisoning. You can redirect all UDP based DNS requests via TCP connection with the following config section. From fef52e0661f7beeaceb5fadf910e393fcdfbd8d9 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 16 Oct 2015 10:04:14 +0800 Subject: [PATCH 058/192] Version bump - 0.65 and update docs. This version is stable version. --- Makefile | 2 +- README | 4 +- README.md | 8 ++-- debian/redsocks.conf | 93 ++++++++++++++++++++++++++++++++++++++------ 4 files changed, 87 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 6d79cc24..db290933 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks2 -VERSION := 0.60 +VERSION := 0.65 LIBS := -levent CFLAGS +=-fPIC -O3 diff --git a/README b/README index 8c8c5cb0..77e818df 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ -This is a modified version of original redsocks and is useful for -anti-GFW (Great Fire Wall). +This is a enhanced version of original redsocks. + The content below is from original redsocks project. --------------------------------------------------------------------- This tool allows you to redirect any TCP connection to SOCKS or HTTPS diff --git a/README.md b/README.md index ed2060fe..2c0f7774 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ REDSOCKS2 ========= This is a modified version of original redsocks. The name is changed to REDSOCKS2 to distinguish with original redsocks. -This variant is useful for anti-GFW (Great Fire Wall). REDSOCKS2 contains -several new features besides many bug fixes to original redsocks. +REDSOCKS2 contains several new features besides many bug fixes to original +redsocks. 1. Redirect TCP connections which are blocked via proxy automatically without need of blacklist. @@ -13,9 +13,6 @@ need of blacklist. 5. Redirect TCP connections via specified network interface. 6. UDP transparent proxy via shadowsocks proxy. -If you feel my work done is helpful, please consider donation. Thanks. -**Accept donations by AliPay with account ** - [Chinese Reference](https://github.com/semigodking/redsocks/wiki) HOW TO BUILD @@ -23,6 +20,7 @@ HOW TO BUILD ###Prerequisites The following libraries are required. +* libevent2 * OpenSSL or PolarSSL ###Steps diff --git a/debian/redsocks.conf b/debian/redsocks.conf index 011aecad..8c074466 100644 --- a/debian/redsocks.conf +++ b/debian/redsocks.conf @@ -33,7 +33,6 @@ base { redirector = iptables; } -redsocks { /* `local_ip' defaults to 127.0.0.1 for security reasons, * use 0.0.0.0 if you want to listen on every interface. * `local_*' are used as port to redirect to. @@ -41,17 +40,48 @@ redsocks { local_ip = 127.0.0.1; local_port = 12345; + // listen() queue length. Default value is SOMAXCONN and it should be + // good enough for most of us. + // listenq = 128; // SOMAXCONN equals 128 on my Linux box. + + // `max_accept_backoff` is a delay to retry `accept()` after accept + // failure (e.g. due to lack of file descriptors). It's measured in + // milliseconds and maximal value is 65535. `min_accept_backoff` is + // used as initial backoff value and as a damper for `accept() after + // close()` logic. + // min_accept_backoff = 100; + // max_accept_backoff = 60000; + // `ip' and `port' are IP and tcp-port of proxy-server // You can also use hostname instead of IP, only one (random) // address of multihomed host will be used. - ip = 127.0.0.1; + // The two fields are meaningless when proxy type is 'direct'. + ip = example.org; port = 1080; - // known types: socks4, socks5, http-connect, http-relay + // New types: direct, shadowsocks type = socks5; - // login = "foobar"; + // Specify interface for outgoing connections when 'direct' type + // is used. This is useful when you have multiple connections to + // internet or you have VPN connections. + // interface = tun0; + + // Change this parameter to 1 if you want auto proxy feature. + // When autoproxy is set to non-zero, the connection to target + // will be made directly first. If direct connection to target + // fails for timeout/connection refuse, redsocks will try to + // connect to target via the proxy. + autoproxy = 0; + // timeout is meaningful when 'autoproxy' is non-zero. + // It specified timeout value when trying to connect to destination + // directly. Default is 10 seconds. When it is set to 0, default + // timeout value will be used. + timeout = 10; + + // login = "foobar";// field 'login' is reused as encryption + // method of shadowsocks // password = "baz"; } @@ -65,9 +95,13 @@ redudp { // `ip' and `port' of socks5 proxy server. ip = 10.0.0.1; port = 1080; - login = username; + login = username;// field 'login' is reused as encryption + // method of shadowsocks password = pazzw0rd; + // know types: socks5, shadowsocks + type = socks5; + // kernel does not give us this information, so we have to duplicate it // in both iptables rules and configuration file. By the way, you can // set `local_ip' to 127.45.67.89 if you need more than 65535 ports to @@ -76,16 +110,51 @@ redudp { dest_ip = 8.8.8.8; dest_port = 53; + // Do not set it large if this section is for DNS requests. Otherwise, + // you may encounter out of file descriptor problem. For DNS requests, + // 10s is adequate. udp_timeout = 30; - udp_timeout_stream = 180; + // udp_timeout_stream = 180; } -dnstc { - // fake and really dumb DNS server that returns "truncated answer" to - // every query via UDP, RFC-compliant resolver should repeat same query - // via TCP in this case. - local_ip = 127.0.0.1; - local_port = 5300; +tcpdns { + // Transform UDP DNS requests into TCP DNS requests. + // You can also redirect connections to external TCP DNS server to + // REDSOCKS transparent proxy via iptables. + local_ip = 192.168.1.1; // Local server to act as DNS server + local_port = 1053; // UDP port to receive UDP DNS requests + tcpdns1 = 8.8.4.4; // DNS server that supports TCP DNS requests + tcpdns2 = 8.8.8.8; // DNS server that supports TCP DNS requests + timeout = 4; // Timeout value for TCP DNS requests } +autoproxy { + no_quick_check_seconds = 60; // Directly relay traffic to proxy if an IP + // is found blocked in cache and it has been + // added into cache no earlier than this + // specified number of seconds. + // Set it to 0 if you do not want to perform + // quick check when an IP is found in blocked + // IP cache, thus the connection will be + // redirected to proxy immediately. + quick_connect_timeout = 3; // Timeout value when performing quick + // connection check if an IP is found blocked + // in cache. +} + +ipcache { + // Configure IP cache + cache_size = 4; // Maximum number of IP's in 1K. + stale_time = 900; // Seconds to stale an IP in cache since it is added + // into cahce. + // Set it to 0 to disable cache stale. + port_check = 1; // Whether to distinguish port number in address + cache_file = "/tmp/ipcache.txt"; // File used to store blocked IP's in cache. + autosave_interval = 3600; // Interval for saving ip cache into file. + // Set it to 0 to disable autosave. + // When autosave_interval and stale_time are both 0, IP cache behaves like + // a static blacklist. +} + + // you can add more `redsocks' and `redudp' sections if you need. From 02a53ce90413c73895fd3d126319478540c46571 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 16 Oct 2015 13:34:47 -0600 Subject: [PATCH 059/192] Introduce support of NAT Traversal Full-cone NAT works with shadowsocks proxy if your shadowsocks server supports NAT traversal also. --- README.md | 1 + redsocks.c | 2 +- redsocks.conf.example | 7 +- redudp.c | 152 +++++++++++++++++++++++++++++++----------- redudp.h | 6 +- shadowsocks-udp.c | 46 +++++-------- socks5-udp.c | 30 +++------ utils.c | 4 +- 8 files changed, 153 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index 035edbab..bd3af680 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ need of blacklist. 4. Redirect TCP connections without proxy. 5. Redirect TCP connections via specified network interface. 6. UDP transparent proxy via shadowsocks proxy. +7. Support Ful-cone NAT Traversal when working with shadowsocks proxy. [Chinese Reference](https://github.com/semigodking/redsocks/wiki) diff --git a/redsocks.c b/redsocks.c index 9af63566..9f2944fb 100644 --- a/redsocks.c +++ b/redsocks.c @@ -1,4 +1,4 @@ -/* redsocks2 - transparent TCP-to-proxy redirector +/* redsocks2 - transparent TCP/UDP-to-proxy redirector * Copyright (C) 2013-2015 Zhuofei Wang * * This code is based on redsocks project developed by Leonid Evdokimov. diff --git a/redsocks.conf.example b/redsocks.conf.example index 793a06f5..984316c4 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -110,12 +110,13 @@ redudp { // set `local_ip' to 127.45.67.89 if you need more than 65535 ports to // forward ;-) // This limitation may be relaxed in future versions using contrack-tools. + // Comment out the two lines below if you use TPROXY for UDP redirection! dest_ip = 8.8.8.8; dest_port = 53; - // Do not set it large if this section is for DNS requests. Otherwise, - // you may encounter out of file descriptor problem. For DNS requests, - // 10s is adequate. + // Do not set it large if this section is for DNS requests. Otherwise, + // you may encounter out of file descriptor problem. For DNS requests, + // 10s is adequate. udp_timeout = 30; // udp_timeout_stream = 180; } diff --git a/redudp.c b/redudp.c index 1218bc55..4bb81773 100644 --- a/redudp.c +++ b/redudp.c @@ -1,4 +1,11 @@ -/* redsocks - transparent TCP-to-proxy redirector +/* redsocks2 - transparent TCP/UDP-to-proxy redirector + * Copyright (C) 2013-2015 Zhuofei Wang + * + * This code is based on redsocks project developed by Leonid Evdokimov. + * Licensed under the Apache License, Version 2.0 (the "License"). + * + * + * redsocks - transparent TCP-to-proxy redirector * Copyright (C) 2007-2011 Leonid Evdokimov * * Licensed under the Apache License, Version 2.0 (the "License"); you may not @@ -40,6 +47,10 @@ #define IP_RECVORIGDSTADDR IP_ORIGDSTADDR #endif +#define DEFAULT_MAX_PKTQUEUE 5 +#define DEFAULT_UDP_TIMEOUT 30 +#define REDUDP_AUDIT_INTERVAL 10 + // Multiple instances share the same buffer for message receiving static char recv_buff[64*1024];// max size of UDP packet is less than 64K @@ -56,6 +67,7 @@ struct bound_udp4 { struct bound_udp4_key key; int ref; int fd; + time_t t_last_rx; }; extern udprelay_subsys socks5_udp_subsys; @@ -95,6 +107,7 @@ static int bound_udp4_get(const struct sockaddr_in *addr) if (pnode) { assert((*pnode)->ref > 0); (*pnode)->ref++; + (*pnode)->t_last_rx = redsocks_time(NULL); return (*pnode)->fd; } @@ -107,6 +120,7 @@ static int bound_udp4_get(const struct sockaddr_in *addr) node->key = key; node->ref = 1; node->fd = socket(AF_INET, SOCK_DGRAM, 0); + node->t_last_rx = redsocks_time(NULL); if (node->fd == -1) { log_errno(LOG_ERR, "socket"); goto fail; @@ -166,6 +180,41 @@ static void bound_udp4_put(const struct sockaddr_in *addr) free(node); } +/* + * This procedure is ued to audit tree items for destination addresses. + * For each destination address, if no packet received from it for a certain period, + * it is removed and the corresponding FD is closed. + */ +static void bound_udp4_action(const void *nodep, const VISIT which, const int depth) +{ + time_t now; + struct bound_udp4 *datap; + void *parent; + char buf[RED_INET_ADDRSTRLEN]; + + switch (which) { + case preorder: + case postorder: + break; + case endorder: + case leaf: + now = redsocks_time(NULL); + datap = *(struct bound_udp4 **) nodep; + // TODO: find a proper way to make timeout configurable for each instance. + if (datap->t_last_rx + 20 < now) { + parent = tdelete(datap, &root_bound_udp4, bound_udp4_cmp); + assert(parent); + + inet_ntop(AF_INET, &datap->key.sin_addr, &buf[0], sizeof(buf)); + log_error(LOG_DEBUG, "Close UDP socket %d to %s:%u", datap->fd, + &buf[0], datap->key.sin_port); + close(datap->fd); + free(datap); + } + break; + } +} + static int redudp_transparent(int fd) { int on = 1; @@ -193,7 +242,7 @@ struct sockaddr_in* get_destaddr(redudp_client *client) */ void redudp_drop_client(redudp_client *client) { - redudp_log_error(client, LOG_DEBUG, "Dropping..."); + redudp_log_error(client, LOG_DEBUG, "Dropping UDP client"); enqueued_packet *q, *tmp; if (client->instance->relay_ss->fini) @@ -203,8 +252,6 @@ void redudp_drop_client(redudp_client *client) if (event_del(&client->timeout) == -1) redudp_log_errno(client, LOG_ERR, "event_del"); } - if (client->sender_fd != -1) - bound_udp4_put(&client->destaddr); list_for_each_entry_safe(q, tmp, &client->queue, list) { list_del(&q->list); free(q); @@ -225,34 +272,34 @@ void redudp_bump_timeout(redudp_client *client) } } -void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len) +void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, + struct sockaddr_in * srcaddr) { size_t sent; + int fd; redsocks_time(&client->last_relay_event); redudp_bump_timeout(client); - if (do_tproxy(client->instance) && client->sender_fd == -1) { - client->sender_fd = bound_udp4_get(&client->destaddr); - if (client->sender_fd == -1) { - redudp_log_error(client, LOG_WARNING, "bound_udp4_get failure"); - return; - } + // When working with TPROXY, we have to get sender FD from tree on + // receipt of each packet from relay. + fd = do_tproxy(client->instance) ? bound_udp4_get(srcaddr) + : EVENT_FD(&client->instance->listener); + if (fd == -1) { + redudp_log_error(client, LOG_WARNING, "bound_udp4_get failure"); + return; } + // TODO: record remote address in client - sent = sendto(do_tproxy(client->instance) - ? client->sender_fd - : EVENT_FD(&client->instance->listener), - buf, len, 0, - (struct sockaddr*)&client->clientaddr, sizeof(client->clientaddr)); + sent = sendto(fd, buf, len, 0, + (struct sockaddr*)&client->clientaddr, sizeof(client->clientaddr)); if (sent != len) { redudp_log_error(client, LOG_WARNING, "sendto: I was sending %zd bytes, but only %zd were sent.", len, sent); return; } - } -static int redudp_enqeue_pkt(redudp_client *client, char *buf, size_t pktlen) +static int redudp_enqeue_pkt(redudp_client *client, struct sockaddr_in * destaddr, char *buf, size_t pktlen) { enqueued_packet *q = NULL; @@ -269,6 +316,7 @@ static int redudp_enqeue_pkt(redudp_client *client, char *buf, size_t pktlen) } INIT_LIST_HEAD(&q->list); + memcpy(&q->destaddr, destaddr, sizeof(*destaddr)); q->len = pktlen; memcpy(q->data, buf, pktlen); client->queue_len += 1; @@ -283,7 +331,7 @@ void redudp_flush_queue(redudp_client *client) redudp_log_error(client, LOG_DEBUG, "Starting UDP relay"); list_for_each_entry_safe(q, tmp, &client->queue, list) { - client->instance->relay_ss->forward_pkt(client, q->data, q->len); + client->instance->relay_ss->forward_pkt(client, (struct sockaddr *)&q->destaddr, q->data, q->len); list_del(&q->list); free(q); } @@ -291,7 +339,6 @@ void redudp_flush_queue(redudp_client *client) assert(list_empty(&client->queue)); } - static void redudp_timeout(int fd, short what, void *_arg) { redudp_client *client = _arg; @@ -303,7 +350,6 @@ static void redudp_timeout(int fd, short what, void *_arg) static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_in *clientaddr, struct sockaddr_in *destaddr, char *buf, size_t pktlen) { redudp_client *client = calloc(1, sizeof(*client)+self->relay_ss->payload_len); - if (!client) { log_errno(LOG_WARNING, "calloc"); return; @@ -313,26 +359,25 @@ static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_ INIT_LIST_HEAD(&client->queue); client->instance = self; memcpy(&client->clientaddr, clientaddr, sizeof(*clientaddr)); + // TODO: remove client->destaddr if (destaddr) memcpy(&client->destaddr, destaddr, sizeof(client->destaddr)); evtimer_assign(&client->timeout, get_event_base(), redudp_timeout, client); self->relay_ss->init(client); - client->sender_fd = -1; // it's postponed until proxy replies to avoid trivial DoS - redsocks_time(&client->first_event); client->last_client_event = client->first_event; redudp_bump_timeout(client); list_add(&client->list, &self->clients); - if (redudp_enqeue_pkt(client, buf, pktlen) == -1) + redudp_log_error(client, LOG_DEBUG, "got 1st packet from client"); + + if (redudp_enqeue_pkt(client, destaddr, buf, pktlen) == -1) goto fail; if (self->relay_ss->connect_relay) self->relay_ss->connect_relay(client); - - redudp_log_error(client, LOG_DEBUG, "got 1st packet from client"); return; fail: @@ -349,13 +394,16 @@ static void redudp_pkt_from_client(int fd, short what, void *_arg) pdestaddr = do_tproxy(self) ? &destaddr : NULL; assert(fd == EVENT_FD(&self->listener)); + // destaddr will be filled with true destination if it is available pktlen = red_recv_udp_pkt(fd, recv_buff, sizeof(recv_buff), &clientaddr, pdestaddr); if (pktlen == -1) return; + if (!pdestaddr) + // In case tproxy is not used, use configured destination address instead. + pdestaddr = &self->config.destaddr; // TODO: this lookup may be SLOOOOOW. list_for_each_entry(tmp, &self->clients, list) { - // TODO: check destaddr if (0 == memcmp(&clientaddr, &tmp->clientaddr, sizeof(clientaddr))) { client = tmp; break; @@ -365,11 +413,12 @@ static void redudp_pkt_from_client(int fd, short what, void *_arg) if (client) { redsocks_time(&client->last_client_event); redudp_bump_timeout(client); + if (self->relay_ss->ready_to_fwd(client)) { - self->relay_ss->forward_pkt(client, recv_buff, pktlen); + self->relay_ss->forward_pkt(client, (struct sockaddr *)pdestaddr, recv_buff, pktlen); } else { - redudp_enqeue_pkt(client, recv_buff, pktlen); + redudp_enqeue_pkt(client, pdestaddr, recv_buff, pktlen); } } else { @@ -393,6 +442,7 @@ static parser_entry redudp_entries[] = { .key = "dest_port", .type = pt_uint16 }, { .key = "udp_timeout", .type = pt_uint16 }, { .key = "udp_timeout_stream", .type = pt_uint16 }, + { .key = "max_pktqueue", .type = pt_uint16 }, { } }; @@ -420,8 +470,8 @@ static int redudp_onenter(parser_section *section) instance->config.relayaddr.sin_family = AF_INET; instance->config.relayaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); instance->config.destaddr.sin_family = AF_INET; - instance->config.max_pktqueue = 5; - instance->config.udp_timeout = 30; + instance->config.max_pktqueue = DEFAULT_MAX_PKTQUEUE; + instance->config.udp_timeout = DEFAULT_UDP_TIMEOUT; instance->config.udp_timeout_stream = 180; for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) @@ -472,8 +522,17 @@ static int redudp_onexit(parser_section *section) err = "no `type` for redudp"; } + + if (instance->config.max_pktqueue == 0) { + parser_error(section->context, "max_pktqueue must be greater than 0."); + return -1; + } + if (instance->config.udp_timeout == 0) { + parser_error(section->context, "udp_timeout must be greater than 0."); + return -1; + } if (instance->config.udp_timeout_stream < instance->config.udp_timeout) { - parser_error(section->context, "udp_timeout_stream should be not less then udp_timeout"); + parser_error(section->context, "udp_timeout_stream should be not less than udp_timeout"); return -1; } @@ -488,6 +547,7 @@ static int redudp_init_instance(redudp_instance *instance) */ int error; int fd = -1; + char buf1[RED_INET_ADDRSTRLEN], buf2[RED_INET_ADDRSTRLEN]; if (instance->relay_ss->instance_init && instance->relay_ss->instance_init(instance)) { @@ -503,7 +563,6 @@ static int redudp_init_instance(redudp_instance *instance) if (do_tproxy(instance)) { int on = 1; - char buf[RED_INET_ADDRSTRLEN]; // iptables TPROXY target does not send packets to non-transparent sockets if (0 != redudp_transparent(fd)) goto fail; @@ -514,11 +573,10 @@ static int redudp_init_instance(redudp_instance *instance) goto fail; } - log_error(LOG_DEBUG, "redudp @ %s: TPROXY", red_inet_ntop(&instance->config.bindaddr, buf, sizeof(buf))); + log_error(LOG_INFO, "redudp @ %s: TPROXY", red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1))); } else { - char buf1[RED_INET_ADDRSTRLEN], buf2[RED_INET_ADDRSTRLEN]; - log_error(LOG_DEBUG, "redudp @ %s: destaddr=%s", + log_error(LOG_INFO, "redudp @ %s: destaddr=%s", red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1)), red_inet_ntop(&instance->config.destaddr, buf2, sizeof(buf2))); } @@ -587,17 +645,31 @@ static void redudp_fini_instance(redudp_instance *instance) free(instance); } +static struct event audit_event; + +static void redudp_audit(int sig, short what, void *_arg) +{ + twalk(root_bound_udp4, bound_udp4_action); +} + static int redudp_init() { redudp_instance *tmp, *instance = NULL; - - // TODO: init debug_dumper + struct timeval audit_time; + struct event_base * base = get_event_base(); list_for_each_entry_safe(instance, tmp, &instances, list) { if (redudp_init_instance(instance) != 0) goto fail; } + memset(&audit_event, 0, sizeof(audit_event)); + /* Start audit */ + audit_time.tv_sec = REDUDP_AUDIT_INTERVAL; + audit_time.tv_usec = 0; + event_assign(&audit_event, base, 0, EV_TIMEOUT|EV_PERSIST, redudp_audit, NULL); + evtimer_add(&audit_event, &audit_time); + return 0; fail: @@ -609,6 +681,10 @@ static int redudp_fini() { redudp_instance *tmp, *instance = NULL; + /* stop audit */ + if (event_initialized(&audit_event)) + evtimer_del(&audit_event); + list_for_each_entry_safe(instance, tmp, &instances, list) redudp_fini_instance(instance); diff --git a/redudp.h b/redudp.h index 0df56099..27fbabc6 100644 --- a/redudp.h +++ b/redudp.h @@ -18,7 +18,7 @@ typedef struct udprelay_subsys_t { // connect_relay (if any) is called instead of redudp_connect_relay after client connection acceptance void (*connect_relay)(struct redudp_client_t *client); //void (*relay_connected)(struct redudp_client_t *client); - void (*forward_pkt)(struct redudp_client_t *client, void * data, size_t len); + void (*forward_pkt)(struct redudp_client_t *client, struct sockaddr * destaddr, void * data, size_t len); int (*ready_to_fwd)(struct redudp_client_t *client); } udprelay_subsys; @@ -49,7 +49,6 @@ typedef struct redudp_client_t { redudp_instance *instance; struct sockaddr_in clientaddr; struct sockaddr_in destaddr; - int sender_fd; // shared between several clients socket (bound to `destaddr`) struct event timeout; int state; // it's used by bottom layer time_t first_event; @@ -61,6 +60,7 @@ typedef struct redudp_client_t { typedef struct enqueued_packet_t { list_head list; + struct sockaddr_in destaddr; size_t len; char data[1]; } enqueued_packet; @@ -68,7 +68,7 @@ typedef struct enqueued_packet_t { struct sockaddr_in* get_destaddr(redudp_client *client); void redudp_drop_client(redudp_client *client); void redudp_flush_queue(redudp_client *client); -void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len); +void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, struct sockaddr_in * srcaddr); void redudp_bump_timeout(redudp_client *client); #define redudp_log_error(client, prio, msg...) \ diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index 0d70c25d..f8e3d25a 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -74,12 +74,11 @@ static void ss_client_fini(redudp_client *client) } } -static void ss_forward_pkt(redudp_client *client, void *data, size_t pktlen) +static void ss_forward_pkt(redudp_client *client, struct sockaddr * destaddr, void *data, size_t pktlen) { ss_client *ssclient = (void*)(client + 1); ss_instance * ss = (ss_instance *)(client->instance+1); struct sockaddr_in * relayaddr = &client->instance->config.relayaddr; - struct sockaddr_in * destaddr = get_destaddr(client); struct msghdr msg; struct iovec io[1]; ssize_t outgoing; @@ -91,8 +90,8 @@ static void ss_forward_pkt(redudp_client *client, void *data, size_t pktlen) /* build and send header */ // TODO: Better implementation and IPv6 Support header.addr_type = ss_addrtype_ipv4; - header.addr = destaddr->sin_addr.s_addr; - header.port = destaddr->sin_port; + header.addr = ((struct sockaddr_in *)destaddr)->sin_addr.s_addr; + header.port = ((struct sockaddr_in *)destaddr)->sin_port; if (enc_ctx_init(&ss->info, &ss->e_ctx, 1)) { redudp_log_error(client, LOG_ERR, "Shadowsocks UDP failed to initialize encryption context."); @@ -157,40 +156,30 @@ static void ss_pkt_from_server(int fd, short what, void *_arg) } rc = ss_decrypt(&ss->d_ctx, ss->buff, pktlen, ss->buff2, &fwdlen); enc_ctx_free(&ss->d_ctx); - if (!rc) - { + if (!rc) { redudp_log_error(client, LOG_DEBUG, "Can't decrypt packet, dropping it"); return; } header = (ss_header_ipv4 *)ss->buff2; + // We do not verify src address, but at least, we need to ensure address type is correct. if (header->addr_type != ss_addrtype_ipv4) { redudp_log_error(client, LOG_DEBUG, "Got address type #%u instead of expected #%u (IPv4).", header->addr_type, ss_addrtype_ipv4); return; } - if (header->port != get_destaddr(client)->sin_port || - header->addr != get_destaddr(client)->sin_addr.s_addr) - { - char addrbuf[RED_INET_ADDRSTRLEN]; - struct sockaddr_in pktaddr = { - .sin_family = AF_INET, - .sin_addr = { header->addr }, - .sin_port = header->port, - }; - redudp_log_error(client, LOG_NOTICE, "Shadowsocks server relayed packet from unexpected address %s.", - red_inet_ntop(&pktaddr, addrbuf, sizeof(addrbuf))); - return; - } + struct sockaddr_in pktaddr = { + .sin_family = AF_INET, + .sin_addr = { header->addr }, + .sin_port = header->port, + }; - if (fwdlen < sizeof(*header)) - { + if (fwdlen < sizeof(*header)) { redudp_log_error(client, LOG_DEBUG, "Packet too short."); return; } - fwdlen -= sizeof(*header); - redudp_fwd_pkt_to_sender(client, ss->buff2 + sizeof(*header), fwdlen); + redudp_fwd_pkt_to_sender(client, ss->buff2 + sizeof(*header), fwdlen, &pktaddr); } static int ss_ready_to_fwd(struct redudp_client_t *client) @@ -249,10 +238,11 @@ static int ss_instance_init(struct redudp_instance_t *instance) { log_error(LOG_INFO, "using encryption method: %s", config->login); } + // Two buffers are allocated for each instance. One is for receiving plain + // data, one is for encryption/decrption. ss->buff = malloc(SHARED_BUFF_SIZE); ss->buff2 = malloc(SHARED_BUFF_SIZE); - if (!ss->buff || !ss->buff2) - { + if (!ss->buff || !ss->buff2) { log_error(LOG_ERR, "Out of memory."); return -1; } @@ -263,13 +253,11 @@ static int ss_instance_init(struct redudp_instance_t *instance) static void ss_instance_fini(struct redudp_instance_t *instance) { ss_instance * ss = (ss_instance *)(instance+1); - if (ss->buff) - { + if (ss->buff) { free(ss->buff); ss->buff = NULL; } - if (ss->buff2) - { + if (ss->buff2) { free(ss->buff2); ss->buff2 = NULL; } diff --git a/socks5-udp.c b/socks5-udp.c index cb29df1f..847da8fa 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -53,13 +53,13 @@ static struct evbuffer* socks5_mkassociate(void *p) return socks5_mkcommand_plain(socks5_cmd_udp_associate, &sa); } -static void socks5_fill_preamble(socks5_udp_preabmle *preamble, redudp_client *client) +static void socks5_fill_preamble(socks5_udp_preabmle *preamble, struct sockaddr_in * addr) { preamble->reserved = 0; preamble->frag_no = 0; /* fragmentation is not supported */ preamble->addrtype = socks5_addrtype_ipv4; - preamble->ip.addr = get_destaddr(client)->sin_addr.s_addr; - preamble->ip.port = get_destaddr(client)->sin_port; + preamble->ip.addr = addr->sin_addr.s_addr; + preamble->ip.port = addr->sin_port; } @@ -106,7 +106,7 @@ static int socks5_ready_to_fwd(struct redudp_client_t *client) return socks5client->ready_fwd; } -static void socks5_forward_pkt(redudp_client *client, void *buf, size_t pktlen) +static void socks5_forward_pkt(redudp_client *client, struct sockaddr *destaddr, void *buf, size_t pktlen) { socks5_client *socks5client = (void*)(client + 1); socks5_udp_preabmle req; @@ -114,7 +114,7 @@ static void socks5_forward_pkt(redudp_client *client, void *buf, size_t pktlen) struct iovec io[2]; ssize_t outgoing, fwdlen = pktlen + sizeof(req); - socks5_fill_preamble(&req, client); + socks5_fill_preamble(&req, (struct sockaddr_in *)destaddr); memset(&msg, 0, sizeof(msg)); msg.msg_name = &socks5client->udprelayaddr; @@ -175,22 +175,14 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) return; } - if (pkt.header.ip.port != get_destaddr(client)->sin_port || - pkt.header.ip.addr != get_destaddr(client)->sin_addr.s_addr) - { - char buf[RED_INET_ADDRSTRLEN]; - struct sockaddr_in pktaddr = { - .sin_family = AF_INET, - .sin_addr = { pkt.header.ip.addr }, - .sin_port = pkt.header.ip.port, - }; - redudp_log_error(client, LOG_NOTICE, "Socks5 server relayed packet from unexpected address %s.", - red_inet_ntop(&pktaddr, buf, sizeof(buf))); - return; - } + struct sockaddr_in pktaddr = { + .sin_family = AF_INET, + .sin_addr = { pkt.header.ip.addr }, + .sin_port = pkt.header.ip.port, + }; fwdlen = pktlen - sizeof(pkt.header); - redudp_fwd_pkt_to_sender(client, pkt.buf + sizeof(pkt.header), fwdlen); + redudp_fwd_pkt_to_sender(client, pkt.buf + sizeof(pkt.header), fwdlen, &pktaddr); } diff --git a/utils.c b/utils.c index 54049f61..8dfdff91 100644 --- a/utils.c +++ b/utils.c @@ -64,8 +64,8 @@ int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inadd cmsg->cmsg_len >= CMSG_LEN(sizeof(*toaddr)) ) { struct sockaddr_in* cmsgaddr = (struct sockaddr_in*)CMSG_DATA(cmsg); - char buf[RED_INET_ADDRSTRLEN]; - log_error(LOG_DEBUG, "IP_ORIGDSTADDR: %s", red_inet_ntop(cmsgaddr, buf, sizeof(buf))); + //char buf[RED_INET_ADDRSTRLEN]; + //log_error(LOG_DEBUG, "IP_ORIGDSTADDR: %s", red_inet_ntop(cmsgaddr, buf, sizeof(buf))); memcpy(toaddr, cmsgaddr, sizeof(*toaddr)); } else { From f7fee97db71c1806ef214cf26ae978eae664954e Mon Sep 17 00:00:00 2001 From: rampageX Date: Wed, 30 Dec 2015 21:13:39 +0800 Subject: [PATCH 060/192] add static compile support easy static compile on tomatoware --- Makefile | 4 ++++ README.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 6d79cc24..4befbcb7 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,10 @@ override LIBS += -lssl -lcrypto override CFLAGS += -DUSE_CRYPTO_OPENSSL $(info Compile with OpenSSL by default. To compile with PolarSSL, run 'make USE_CRYPTO_POLARSSL=true' instead.) endif +ifdef ENABLE_STATIC +override LIBS += -ldl -lz +override LDFLAGS += -Wl,-static -static -static-libgcc +endif all: $(OUT) diff --git a/README.md b/README.md index ed2060fe..b1a06912 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,10 @@ To compile with PolarSSL make USE_CRYPTO_POLARSSL=true +To compile static binaries (with Tomatoware) + + make ENABLE_STATIC=true + Since this variant of redsocks is customized for running with Openwrt, please read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how to cross compile. From 3d766d7df608ea1e4d006e551a0ec25fb9a241d4 Mon Sep 17 00:00:00 2001 From: rampageX Date: Wed, 30 Dec 2015 21:57:11 +0800 Subject: [PATCH 061/192] multiple definition of `md5_finish' When static compile with PolarSSL, an error will happen: libpolarssl.a(md5.c.o): In function `md5_finish': md5.c:(.text+0x1d80): multiple definition of `md5_finish' md5.o:md5.c:(.text+0xee8): first defined here so rude replace the name of md5_finish in file http-auth.c, md5.c and md5.h --- .gitignore | 1 + http-auth.c | 6 +++--- md5.c | 2 +- md5.h | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 5930ef53..cb71a20d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ config.h tags redsocks .depend +README.md.html diff --git a/http-auth.c b/http-auth.c index d37d6559..ec9d38d9 100644 --- a/http-auth.c +++ b/http-auth.c @@ -197,7 +197,7 @@ char* digest_authentication_encode(const char *line, const char *user, const cha md5_append(&ctx, (md5_byte_t*)realm, strlen(realm)); md5_append(&ctx, (md5_byte_t*)":", 1); md5_append(&ctx, (md5_byte_t*)passwd, strlen(passwd)); - md5_finish(&ctx, hash); + md5_finish_rs(&ctx, hash); dump_hash(a1buf, hash); /* A2 = Method ":" digest-uri-value */ @@ -205,7 +205,7 @@ char* digest_authentication_encode(const char *line, const char *user, const cha md5_append(&ctx, (md5_byte_t*)method, strlen(method)); md5_append(&ctx, (md5_byte_t*)":", 1); md5_append(&ctx, (md5_byte_t*)path, strlen(path)); - md5_finish(&ctx, hash); + md5_finish_rs(&ctx, hash); dump_hash(a2buf, hash); /* qop set: request-digest = H(A1) ":" nonce-value ":" nc-value ":" cnonce-value ":" qop-value ":" H(A2) */ @@ -224,7 +224,7 @@ char* digest_authentication_encode(const char *line, const char *user, const cha md5_append(&ctx, (md5_byte_t*)":", 1); } md5_append(&ctx, (md5_byte_t*)a2buf, strlen(a2buf)); - md5_finish(&ctx, hash); + md5_finish_rs(&ctx, hash); dump_hash(response, hash); /* prepare the final string */ diff --git a/md5.c b/md5.c index 8e0e2a5b..4ea35af2 100644 --- a/md5.c +++ b/md5.c @@ -354,7 +354,7 @@ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) memcpy(pms->buf, p, left); } -void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +void md5_finish_rs(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/md5.h b/md5.h index 698c995d..cc5558e2 100644 --- a/md5.h +++ b/md5.h @@ -82,7 +82,7 @@ void md5_init(md5_state_t *pms); void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ -void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); +void md5_finish_rs(md5_state_t *pms, md5_byte_t digest[16]); #ifdef __cplusplus } /* end extern "C" */ From 4f61713db65ae5b966cf28df7231d2509fd73601 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 6 Jan 2016 13:39:30 +0800 Subject: [PATCH 062/192] Remove code for self-protection Self-protection was introduced when REDSOCKS2 was not stable and may stop responding randomly. Now, REDSOCKS2 is proved stable enough to remove self-protection code. If you still want REDSOCKS2 restarted whenever it is dead, you can consider crontab or systemd. --- base.c | 70 ------------------------------------------------------ redsocks.c | 22 ----------------- 2 files changed, 92 deletions(-) diff --git a/base.c b/base.c index de7b841b..bdd789f8 100644 --- a/base.c +++ b/base.c @@ -321,74 +321,6 @@ static parser_section base_conf_section = /*********************************************************************** * `base` initialization */ -static void myproc() -{ - time_t now; - time_t last; - FILE * tmp = NULL; - pid_t pid = -1; - int stop = 0; - struct sigaction sa ; - char tmp_fname[128]; - snprintf(tmp_fname, sizeof(tmp_fname), "/tmp/redtime-%d", getpid()); - - /* Set SIGCHLD handler to ignore death of child. */ - sa.sa_handler = SIG_IGN; - sa.sa_flags = SA_RESTART; - - if (sigaction(SIGCHLD, &sa, NULL) == -1) { - log_errno(LOG_ERR, "sigaction SIGCHLD"); - return ; - } - for(;;) - { - pid = fork(); - switch (pid) { - case -1: // error - log_errno(LOG_ERR, "fork()"); - return ; - case 0: // child - return; - default: // parent, pid is returned - /* let's monitor child process */ - sleep(5);/* give child process 5 seconds for initialization */ - stop = 0; - for(;stop==0;) - { - if (kill(pid, SIGUSR2) == -1) - { - if (errno == ESRCH) - { - /* process is dead ? */ - stop = 1; - } - log_error(LOG_NOTICE, "Failed to send SIGUSR2 to pid %d", pid); - sleep(1); - continue; - } - sleep(2); - - tmp = fopen(tmp_fname, "r"); - if (tmp) - { - if (fscanf(tmp, "%ld", &last) > 0) - { - now = time(NULL); - if (now-last>10) - { - kill(pid, SIGKILL); - sleep(1); - stop = 1; - } - } - fclose(tmp); - } - } - } - } -} - - static int base_fini(); static int base_init() @@ -496,8 +428,6 @@ static int base_init() } close(devnull); - /* only fork and monitor child process when running as daemon */ - myproc(); } return 0; fail: diff --git a/redsocks.c b/redsocks.c index 9f2944fb..8f96da9f 100644 --- a/redsocks.c +++ b/redsocks.c @@ -909,22 +909,6 @@ static void redsocks_audit_instance(redsocks_instance *instance) log_error(LOG_DEBUG, "End of auditing client list."); } - -static void redsocks_heartbeat(int sig, short what, void *_arg) -{ - time_t now = redsocks_time(NULL); - FILE * tmp = NULL; - char tmp_fname[128]; - - snprintf(tmp_fname, sizeof(tmp_fname), "/tmp/redtime-%d", getppid()); - tmp = fopen(tmp_fname, "w"); - if (tmp) - { - fprintf(tmp, "%ld ", (long int)now); - fclose(tmp); - } -} - static void redsocks_audit(int sig, short what, void *_arg) { redsocks_instance * tmp, *instance = NULL; @@ -1046,7 +1030,6 @@ static void redsocks_fini_instance(redsocks_instance *instance) { static int redsocks_fini(); -static struct event heartbeat_writer; static struct event audit_event; //static void ignore_sig(int t) {} @@ -1075,11 +1058,6 @@ static int redsocks_init() { return -1; } - evsignal_assign(&heartbeat_writer, base, SIGUSR2, redsocks_heartbeat, NULL); - if (evsignal_add(&heartbeat_writer, NULL) != 0) { - log_errno(LOG_ERR, "evsignal_add SIGUSR2"); - goto fail; - } /* Start audit */ audit_time.tv_sec = REDSOCKS_AUDIT_INTERVAL; audit_time.tv_usec = 0; From 5ef3fa05b2ada0fc68e553a7f3b61220bba4738e Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 6 Jan 2016 17:51:25 +0800 Subject: [PATCH 063/192] Ignore SIGPIPE for whole application In case TCP connection is used by services while 'redsocks' service is not configured, SIGPIPE may be raised. With this update, SIGPIPE is ignored for whole application. --- main.c | 17 +++++++++++++++++ redsocks.c | 24 ------------------------ 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/main.c b/main.c index d73ca4ff..94e0ffae 100644 --- a/main.c +++ b/main.c @@ -71,6 +71,20 @@ static void red_srand() srand(tv.tv_sec*1000000+tv.tv_usec); } +/* Setup signals not to be handled with libevent */ +static int setup_signals() +{ + struct sigaction sa/* , sa_old*/; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &sa, NULL) == -1) { + log_errno(LOG_ERR, "sigaction"); + return -1; + } + return 0; +} + struct event_base * get_event_base() { return g_event_base; @@ -140,6 +154,9 @@ int main(int argc, char **argv) if (conftest) return EXIT_SUCCESS; + if (setup_signals()) + return EXIT_SUCCESS; + // Initialize global event base g_event_base = event_base_new(); if (!g_event_base) diff --git a/redsocks.c b/redsocks.c index 8f96da9f..e418a765 100644 --- a/redsocks.c +++ b/redsocks.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -1032,32 +1031,12 @@ static int redsocks_fini(); static struct event audit_event; -//static void ignore_sig(int t) {} - static int redsocks_init() { - struct sigaction sa/* , sa_old*/; redsocks_instance *tmp, *instance = NULL; -// void (* old_hdl)(int)= NULL; struct timeval audit_time; struct event_base * base = get_event_base(); memset(&audit_event, 0, sizeof(audit_event)); -/* - old_hdl = signal(SIGPIPE, ignore_sig); - if (old_hdl == -1) { - log_errno(LOG_ERR, "sigaction"); - return -1; - } -*/ - memset(&sa, 0, sizeof(struct sigaction)); - sa.sa_handler = SIG_IGN; - //sa.sa_flags = SA_RESTART; - - if (sigaction(SIGPIPE, &sa, NULL) == -1) { - log_errno(LOG_ERR, "sigaction"); - return -1; - } - /* Start audit */ audit_time.tv_sec = REDSOCKS_AUDIT_INTERVAL; audit_time.tv_usec = 0; @@ -1073,9 +1052,6 @@ static int redsocks_init() { fail: // that was the first resource allocation, it return's on failure, not goto-fail's -/* sigaction(SIGPIPE, &sa_old, NULL); */ -// signal(SIGPIPE, old_hdl); - redsocks_fini(); return -1; From c41ad2b7563ab2774d971702d55d73a331ec1565 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 15 Jan 2016 16:25:24 +0800 Subject: [PATCH 064/192] Performance Optimization By optimize log writing method, performance is improved by ~25%. --- log.c | 7 ++++++- log.h | 1 + redsocks.c | 26 +++++++++----------------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/log.c b/log.c index b3311e79..9a456ae2 100644 --- a/log.c +++ b/log.c @@ -136,12 +136,17 @@ void log_open() log_msg_next = NULL; } +int log_level_enabled(int priority) +{ + return (log_mask & LOG_MASK(priority)); +} + void _log_vwrite(const char *file, int line, const char *func, int do_errno, int priority, const char *fmt, va_list ap) { int saved_errno = errno; char message[MAX_LOG_LENGTH+1]; - if (!(log_mask & LOG_MASK(priority))) + if (!log_level_enabled(priority)) return; vsnprintf(&message[0], sizeof(message), fmt, ap); log_msg(file, line, func, priority, &message[0], do_errno ? strerror(saved_errno) : NULL); diff --git a/log.h b/log.h index 92f51489..a433b0b0 100644 --- a/log.h +++ b/log.h @@ -12,6 +12,7 @@ int log_preopen(const char *dst, bool log_debug, bool log_info); void log_open(); +int log_level_enabled(int priority); void _log_vwrite(const char *file, int line, const char *func, int do_errno, int priority, const char *fmt, va_list ap); diff --git a/redsocks.c b/redsocks.c index e418a765..07766f86 100644 --- a/redsocks.c +++ b/redsocks.c @@ -223,28 +223,21 @@ void redsocks_log_write_plain( int priority, const char *orig_fmt, ... ) { int saved_errno = errno; - struct evbuffer *fmt = evbuffer_new(); va_list ap; char clientaddr_str[RED_INET_ADDRSTRLEN], destaddr_str[RED_INET_ADDRSTRLEN]; + char fmt[MAX_LOG_LENGTH+1]; - if (!fmt) { - log_errno(LOG_ERR, "evbuffer_new()"); - // no return, as I have to call va_start/va_end - } + if (!log_level_enabled(priority)) + return; - if (fmt) { - evbuffer_add_printf(fmt, "[%s->%s]: %s", + snprintf(fmt, sizeof(fmt), "[%s->%s]: %s", red_inet_ntop(clientaddr, clientaddr_str, sizeof(clientaddr_str)), red_inet_ntop(destaddr, destaddr_str, sizeof(destaddr_str)), orig_fmt); - } va_start(ap, orig_fmt); - if (fmt) { - errno = saved_errno; - _log_vwrite(file, line, func, do_errno, priority, (const char*)EVBUFFER_DATA(fmt), ap); - evbuffer_free(fmt); - } + errno = saved_errno; + _log_vwrite(file, line, func, do_errno, priority, &fmt[0], ap); va_end(ap); } @@ -285,9 +278,8 @@ int process_shutdown_on_write_(redsocks_client *client, struct bufferevent *from evbuffer_get_length(bufferevent_get_output(from)), evbuffer_get_length(bufferevent_get_input(to))); - if (evbuffer_get_length(bufferevent_get_input(from)) == 0 - && (from_evshut & EV_READ) - && !(to_evshut & EV_WRITE)) { + if ((from_evshut & EV_READ) && !(to_evshut & EV_WRITE) + && evbuffer_get_length(bufferevent_get_input(from)) == 0) { redsocks_shutdown(client, to, SHUT_WR); return 1; } @@ -296,8 +288,8 @@ int process_shutdown_on_write_(redsocks_client *client, struct bufferevent *from static void redsocks_relay_writecb(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) { - unsigned short from_evshut = from == client->client ? client->client_evshut : client->relay_evshut; assert(from == client->client || from == client->relay); + unsigned short from_evshut = from == client->client ? client->client_evshut : client->relay_evshut; if (process_shutdown_on_write_(client, from, to)) return; From 02ffec4f967d5a1858f43cb891c6a75f366b2865 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 20 Jan 2016 23:12:15 +0800 Subject: [PATCH 065/192] Fix: UDP redirector uses blocking sockets --- redudp.c | 5 +++++ shadowsocks-udp.c | 6 ++++++ socks5-udp.c | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/redudp.c b/redudp.c index 4bb81773..bebcb76e 100644 --- a/redudp.c +++ b/redudp.c @@ -139,6 +139,11 @@ static int bound_udp4_get(const struct sockaddr_in *addr) goto fail; } + if (0 != evutil_make_socket_nonblocking(node->fd)) { + log_errno(LOG_ERR, "evutil_make_socket_nonblocking"); + goto fail; + } + pnode = tsearch(node, &root_bound_udp4, bound_udp4_cmp); if (!pnode) { log_errno(LOG_ERR, "tsearch(%p) == %p", node, pnode); diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index f8e3d25a..1bf0d07a 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -200,6 +200,12 @@ static void ss_connect_relay(redudp_client *client) goto fail; } + error = evutil_make_socket_nonblocking(fd); + if (error) { + redudp_log_errno(client, LOG_ERR, "evutil_make_socket_nonblocking"); + goto fail; + } + error = connect(fd, (struct sockaddr*)addr, sizeof(*addr)); if (error) { redudp_log_errno(client, LOG_NOTICE, "connect"); diff --git a/socks5-udp.c b/socks5-udp.c index 847da8fa..5025ae02 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -229,6 +229,13 @@ static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg) goto fail; } + error = evutil_make_socket_nonblocking(fd); + if (error) { + redudp_log_errno(client, LOG_ERR, "evutil_make_socket_nonblocking"); + goto fail; + } + + error = connect(fd, (struct sockaddr*)&socks5client->udprelayaddr, sizeof(socks5client->udprelayaddr)); if (error) { redudp_log_errno(client, LOG_NOTICE, "connect"); From 084c05fd07da133560d50d2f66c02e4a51ff6bd0 Mon Sep 17 00:00:00 2001 From: rampageX Date: Sat, 20 Feb 2016 00:48:50 +0800 Subject: [PATCH 066/192] multiple definition of `md5_init' fix multiple definition of `md5_init' error when static compile PolarSSL version --- http-auth.c | 6 +++--- md5.c | 2 +- md5.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/http-auth.c b/http-auth.c index ec9d38d9..8302ea84 100644 --- a/http-auth.c +++ b/http-auth.c @@ -191,7 +191,7 @@ char* digest_authentication_encode(const char *line, const char *user, const cha char response[MD5_HASHLEN * 2 + 1]; /* A1 = username-value ":" realm-value ":" passwd */ - md5_init(&ctx); + md5_init_rs(&ctx); md5_append(&ctx, (md5_byte_t*)user, strlen(user)); md5_append(&ctx, (md5_byte_t*)":", 1); md5_append(&ctx, (md5_byte_t*)realm, strlen(realm)); @@ -201,7 +201,7 @@ char* digest_authentication_encode(const char *line, const char *user, const cha dump_hash(a1buf, hash); /* A2 = Method ":" digest-uri-value */ - md5_init(&ctx); + md5_init_rs(&ctx); md5_append(&ctx, (md5_byte_t*)method, strlen(method)); md5_append(&ctx, (md5_byte_t*)":", 1); md5_append(&ctx, (md5_byte_t*)path, strlen(path)); @@ -210,7 +210,7 @@ char* digest_authentication_encode(const char *line, const char *user, const cha /* qop set: request-digest = H(A1) ":" nonce-value ":" nc-value ":" cnonce-value ":" qop-value ":" H(A2) */ /* not set: request-digest = H(A1) ":" nonce-value ":" H(A2) */ - md5_init(&ctx); + md5_init_rs(&ctx); md5_append(&ctx, (md5_byte_t*)a1buf, strlen(a1buf)); md5_append(&ctx, (md5_byte_t*)":", 1); md5_append(&ctx, (md5_byte_t*)nonce, strlen(nonce)); diff --git a/md5.c b/md5.c index 4ea35af2..af37531a 100644 --- a/md5.c +++ b/md5.c @@ -308,7 +308,7 @@ static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) pms->abcd[3] += d; } -void md5_init(md5_state_t *pms) +void md5_init_rs(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; diff --git a/md5.h b/md5.h index cc5558e2..fa90736c 100644 --- a/md5.h +++ b/md5.h @@ -76,7 +76,7 @@ extern "C" #endif /* Initialize the algorithm. */ -void md5_init(md5_state_t *pms); +void md5_init_rs(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); From d01ceb6a1ba3e4c2d50641a57d1acff7388864b3 Mon Sep 17 00:00:00 2001 From: rampageX Date: Sat, 20 Feb 2016 01:15:25 +0800 Subject: [PATCH 067/192] show crypt lib used when -v set show redsocks2 compile with OpenSSL or PolarSSL info when -v set --- Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 15e420b2..20649d6c 100644 --- a/Makefile +++ b/Makefile @@ -13,16 +13,18 @@ override CFLAGS += -std=gnu99 -Wall #LDFLAGS += -fwhole-program ifdef USE_CRYPTO_POLARSSL override LIBS += -lpolarssl -override CFLAGS += -DUSE_CRYPTO_POLARSSL +override CFLAGS += -DUSE_CRYPTO_POLARSSL $(info Compile with PolarSSL.) +CRYPTO := PolarSSL else override LIBS += -lssl -lcrypto override CFLAGS += -DUSE_CRYPTO_OPENSSL $(info Compile with OpenSSL by default. To compile with PolarSSL, run 'make USE_CRYPTO_POLARSSL=true' instead.) +CRYPTO := OpenSSL endif ifdef ENABLE_STATIC override LIBS += -ldl -lz -override LDFLAGS += -Wl,-static -static -static-libgcc +override LDFLAGS += -Wl,-static -static -static-libgcc -s endif all: $(OUT) @@ -53,12 +55,12 @@ gen/version.c: *.c *.h gen/.build echo '#include "../version.h"' >> $@.tmp echo 'const char* redsocks_version = ' >> $@.tmp if [ -d .git ]; then \ - echo '"redsocks.git/'`git describe --tags`'"'; \ + echo '"redsocks.git/'`git describe --tags`' $(CRYPTO)"'; \ if [ `git status --porcelain | grep -v -c '^??'` != 0 ]; then \ echo '"-unclean"'; \ fi \ else \ - echo '"redsocks/$(VERSION)"'; \ + echo '"redsocks/$(VERSION) $(CRYPTO)"'; \ fi >> $@.tmp echo ';' >> $@.tmp mv -f $@.tmp $@ From 92801f4c0b70f0c5469c7f9eeab48fea54f1d32d Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 23 Feb 2016 17:28:53 +0800 Subject: [PATCH 068/192] Fix: memory leaks in 'tcpdns' --- tcpdns.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tcpdns.c b/tcpdns.c index 2a3a25a5..73ae1b5d 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -248,9 +248,13 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) gettimeofday(&req->req_time, 0); pktlen = red_recv_udp_pkt(fd, req->data.raw, sizeof(req->data.raw), &req->client_addr, NULL); if (pktlen == -1) + { + free(req); return; + } if (pktlen <= sizeof(dns_header)) { + free(req); tcpdns_log_error(LOG_INFO, "incomplete DNS request"); return; } @@ -271,7 +275,7 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) if (!destaddr) { free(req); - tcpdns_log_error(LOG_INFO, "No valid DNS resolver configured"); + tcpdns_log_error(LOG_WARNING, "No valid DNS resolver configured"); return; } /* connect to target directly without going through proxy */ From 58ab445f21e439e11f8eec044e6d33cbc5de8754 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 29 Feb 2016 10:54:39 +0800 Subject: [PATCH 069/192] Fix: use after free in 'tcpdns'. --- tcpdns.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tcpdns.c b/tcpdns.c index 73ae1b5d..2b13444e 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -254,8 +254,8 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) } if (pktlen <= sizeof(dns_header)) { - free(req); tcpdns_log_error(LOG_INFO, "incomplete DNS request"); + free(req); return; } req->data_len = pktlen; @@ -274,8 +274,8 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) destaddr = choose_tcpdns(self, &req->delay); if (!destaddr) { - free(req); tcpdns_log_error(LOG_WARNING, "No valid DNS resolver configured"); + free(req); return; } /* connect to target directly without going through proxy */ @@ -286,14 +286,14 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) list_add(&req->list, &self->requests); else { - free(req); tcpdns_log_error(LOG_INFO, "Failed to setup connection to DNS resolver"); + free(req); } } else { - free(req); tcpdns_log_error(LOG_INFO, "malformed DNS request"); + free(req); } } From 5b970edfa988b40f55a391a71b7427bcb8f84a80 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 8 Mar 2016 17:26:56 +0800 Subject: [PATCH 070/192] Fix: bugs in 'http-relay' 1. Memory leaks in certain cases 2. Double 'free' in certain case 3. Improve efficiency --- http-relay.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/http-relay.c b/http-relay.c index e0adc948..05fc7198 100644 --- a/http-relay.c +++ b/http-relay.c @@ -33,6 +33,7 @@ #include "utils.h" #define HTTP_HEAD_WM_HIGH (4096) +#define MAX_HTTP_REQUEST_LINE_LENGTH 4095 typedef enum httpr_state_t { httpr_new, @@ -176,7 +177,6 @@ static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg) dropped = 1; } else { - free(line); char *auth_request = get_auth_request_header(buffev->input); if (!auth_request) { @@ -184,6 +184,7 @@ static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg) redsocks_drop_client(client); dropped = 1; } else { + free(line); free(auth->last_auth_query); char *ptr = auth_request; @@ -341,6 +342,8 @@ static void httpr_relay_write_cb(struct bufferevent *buffev, void *_arg) len |= bufferevent_write(client->relay, " ", 1); len |= bufferevent_write(client->relay, auth_string, strlen(auth_string)); len |= bufferevent_write(client->relay, "\r\n", 2); + free(auth_string); + auth_string = NULL; if (len) { redsocks_log_errno(client, LOG_ERR, "bufferevent_write"); redsocks_drop_client(client); @@ -348,7 +351,6 @@ static void httpr_relay_write_cb(struct bufferevent *buffev, void *_arg) } } - free(auth_string); len = bufferevent_write(client->relay, httpr->client_buffer.buff, httpr->client_buffer.len); if (len < 0) { @@ -372,7 +374,7 @@ static int httpr_append_header(redsocks_client *client, char *line) if (httpr_buffer_append(&httpr->client_buffer, line, strlen(line)) != 0) return -1; - if (httpr_buffer_append(&httpr->client_buffer, "\x0d\x0a", 2) != 0) + if (httpr_buffer_append(&httpr->client_buffer, "\r\n", 2) != 0) return -1; return 0; } @@ -398,9 +400,16 @@ static int httpr_toss_http_firstline(redsocks_client *client) httpr_client *httpr = (void*)(client + 1); char *uri = NULL; char *host = httpr->has_host ? httpr->host : fmt_http_host(client->destaddr); + static char nbuff[MAX_HTTP_REQUEST_LINE_LENGTH + 1]; + size_t len = 0; assert(httpr->firstline); + if (strlen(httpr->firstline) + strlen(host) + 7 + 2 > MAX_HTTP_REQUEST_LINE_LENGTH) { + redsocks_log_error(client, LOG_NOTICE, "HTTP request line too line!"); + goto fail; + } + uri = strchr(httpr->firstline, ' '); if (uri) while (*uri == ' ') @@ -410,34 +419,25 @@ static int httpr_toss_http_firstline(redsocks_client *client) goto fail; } - httpr_buffer nbuff; - if (httpr_buffer_init(&nbuff) != 0) { - redsocks_log_error(client, LOG_ERR, "httpr_buffer_init"); - goto fail; - } - - if (httpr_buffer_append(&nbuff, httpr->firstline, uri - httpr->firstline) != 0) - goto addition_fail; + memcpy(&nbuff[0], httpr->firstline, uri - httpr->firstline); + len = uri - httpr->firstline; if (*uri == '/') { - if (httpr_buffer_append(&nbuff, "http://", 7) != 0) - goto addition_fail; - if (httpr_buffer_append(&nbuff, host, strlen(host)) != 0) - goto addition_fail; + memcpy(&nbuff[len], "http://", 7); + len += 7; + memcpy(&nbuff[len], host, strlen(host)); + len += strlen(host); } - if (httpr_buffer_append(&nbuff, uri, strlen(uri)) != 0) - goto addition_fail; - if (httpr_buffer_append(&nbuff, "\x0d\x0a", 2) != 0) - goto addition_fail; + memcpy(&nbuff[len], uri, strlen(uri)); + len += strlen(uri); + memcpy(&nbuff[len], "\r\n", 3); // Including NULL terminator + len += 3; free(httpr->firstline); - httpr->firstline = calloc(nbuff.len + 1, 1); - strcpy(httpr->firstline, nbuff.buff); - httpr_buffer_fini(&nbuff); + httpr->firstline = calloc(len, 1); + strcpy(httpr->firstline, &nbuff[0]); return 0; -addition_fail: - httpr_buffer_fini(&nbuff); fail: redsocks_log_error(client, LOG_ERR, "httpr_toss_http_firstline"); return -1; From f2b132d5c53af6b5b2e72b1a926ede10455798ef Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 9 Mar 2016 17:49:15 +0800 Subject: [PATCH 071/192] Fix: socket close order --- autoproxy.c | 1 + http-connect.c | 3 ++- http-relay.c | 3 ++- socks5-udp.c | 2 +- tcpdns.c | 4 +++- utils.c | 8 ++++---- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index 6bfa98d1..58096ccf 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -476,6 +476,7 @@ static void auto_drop_relay(redsocks_client *client) redsocks_close(fd); client->relay = NULL; } + client->relay_evshut = 0; client->relay_connected = 0; } diff --git a/http-connect.c b/http-connect.c index 3ce3b008..8a2fbcee 100644 --- a/http-connect.c +++ b/http-connect.c @@ -132,8 +132,9 @@ void httpc_read_cb(struct bufferevent *buffev, void *_arg) } /* close relay tunnel */ - redsocks_close(EVENT_FD(&client->relay->ev_write)); + int fd = bufferevent_getfd(client->relay); bufferevent_free(client->relay); + redsocks_close(fd); /* set to initial state*/ client->state = httpc_new; diff --git a/http-relay.c b/http-relay.c index 946026c5..8c2e8e27 100644 --- a/http-relay.c +++ b/http-relay.c @@ -206,8 +206,9 @@ static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg) } /* close relay tunnel */ - redsocks_close(EVENT_FD(&client->relay->ev_write)); + int fd = bufferevent_getfd(client->relay); bufferevent_free(client->relay); + redsocks_close(fd); /* set to initial state*/ client->state = httpr_recv_request_headers; diff --git a/socks5-udp.c b/socks5-udp.c index 5025ae02..6ebb92e9 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -93,7 +93,7 @@ static void socks5_client_fini(redudp_client *client) close(fd); } if (socks5client->relay) { - fd = EVENT_FD(&socks5client->relay->ev_read); + fd = bufferevent_getfd(socks5client->relay); bufferevent_free(socks5client->relay); shutdown(fd, SHUT_RDWR); close(fd); diff --git a/tcpdns.c b/tcpdns.c index 2b13444e..13e597c1 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -63,11 +63,13 @@ typedef enum tcpdns_state_t { */ static void tcpdns_drop_request(dns_request * req) { + int fd; tcpdns_log_error(LOG_DEBUG, "dropping request"); if (req->resolver) { - close(bufferevent_getfd(req->resolver)); + fd = bufferevent_getfd(req->resolver); bufferevent_free(req->resolver); + close(fd); } if (req->delay && req->state != STATE_RESPONSE_SENT) diff --git a/utils.c b/utils.c index c761829a..2d8262bb 100644 --- a/utils.c +++ b/utils.c @@ -186,10 +186,10 @@ struct bufferevent* red_connect_relay_if(const char *ifname, return retval; fail: - if (relay_fd != -1) - redsocks_close(relay_fd); if (retval) bufferevent_free(retval); + if (relay_fd != -1) + redsocks_close(relay_fd); return NULL; } @@ -263,10 +263,10 @@ struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, return retval; fail: - if (relay_fd != -1) - redsocks_close(relay_fd); if (retval) bufferevent_free(retval); + if (relay_fd != -1) + redsocks_close(relay_fd); return NULL; } From fa3deac94c794ddead0814c20f6d56e1bd579ed3 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 15 Mar 2016 11:26:09 +0800 Subject: [PATCH 072/192] Fix: incorrect timeout value used by 'tcpdns' --- tcpdns.c | 19 +++++++++++++++---- tcpdns.h | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/tcpdns.c b/tcpdns.c index 13e597c1..636278bd 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -130,7 +130,7 @@ static void tcpdns_connected(struct bufferevent *buffev, void *_arg) { dns_request * req = _arg; assert(buffev == req->resolver); - struct timeval tv; + struct timeval tv, tv2; if (!red_is_socket_connected_ok(buffev)) { @@ -140,6 +140,7 @@ static void tcpdns_connected(struct bufferevent *buffev, void *_arg) } if (req->state != STATE_NEW) + // Nothing to write return; // Write dns request to DNS resolver and shutdown connection @@ -155,9 +156,19 @@ static void tcpdns_connected(struct bufferevent *buffev, void *_arg) // Set timeout for read with time left since connection setup. gettimeofday(&tv, 0); timersub(&tv, &req->req_time, &tv); - bufferevent_set_timeouts(buffev, &tv, NULL); - bufferevent_enable(buffev, EV_READ); - req->state = STATE_REQUEST_SENT; + tv2.tv_sec = DEFAULT_TIMEOUT_SECONDS; + tv2.tv_usec = 0; + if (req->instance->config.timeout > 0) + tv2.tv_sec = req->instance->config.timeout; + timersub(&tv2, &tv, &tv); + if (tv.tv_sec >=0) { + bufferevent_set_timeouts(buffev, &tv, NULL); + bufferevent_enable(buffev, EV_READ); + req->state = STATE_REQUEST_SENT; + } + else { + tcpdns_drop_request(req); + } } diff --git a/tcpdns.h b/tcpdns.h index eb00a962..5568ea26 100644 --- a/tcpdns.h +++ b/tcpdns.h @@ -7,7 +7,7 @@ typedef struct tcpdns_config_t { struct sockaddr_in udpdns2_addr; struct sockaddr_in tcpdns1_addr; struct sockaddr_in tcpdns2_addr; - int timeout; /* timeout value for DNS response*/ + uint16_t timeout; /* timeout value for DNS response*/ } tcpdns_config; typedef struct tcpdns_instance_t { From dceddf23088ceaa8df4609ea3db847389f480934 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 15 Mar 2016 12:23:19 +0800 Subject: [PATCH 073/192] Fix: errors when compile with MUSL C lib --- autoproxy.c | 2 +- ipcache.c | 2 +- redsocks.c | 2 +- tcpdns.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index 58096ccf..68c36fe9 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -118,7 +118,7 @@ static int autoproxy_onexit(parser_section *section) err = "Timeout value for quick check can not be 0. Default: 3"; if (err) - parser_error(section->context, err); + parser_error(section->context, "%s", err); else // Add config to config list list_add(&config->list, &configs); diff --git a/ipcache.c b/ipcache.c index cc3be0b6..cbf5b40e 100644 --- a/ipcache.c +++ b/ipcache.c @@ -117,7 +117,7 @@ static int cache_onexit(parser_section *section) err = "Time to stale cache item must be equal or greater than 5 seconds."; if (err) - parser_error(section->context, err); + parser_error(section->context, "%s", err); return err ? -1 : 0; } diff --git a/redsocks.c b/redsocks.c index a5240fd6..a68ccc88 100644 --- a/redsocks.c +++ b/redsocks.c @@ -205,7 +205,7 @@ static int redsocks_onexit(parser_section *section) } if (err) - parser_error(section->context, err); + parser_error(section->context, "%s", err); return err ? -1 : 0; } diff --git a/tcpdns.c b/tcpdns.c index 636278bd..f066029f 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -401,7 +401,7 @@ static int tcpdns_onexit(parser_section *section) err = "At least one TCP DNS resolver must be configured."; if (err) - parser_error(section->context, err); + parser_error(section->context, "%s", err); else list_add(&instance->list, &instances); From 09ffb10165bf447cb55775852e9df74f722417ab Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 19 Mar 2016 21:34:28 +0800 Subject: [PATCH 074/192] Fix: SEGV when dumping/auditing in partly-initialized case --- redsocks.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/redsocks.c b/redsocks.c index a68ccc88..98d5669a 100644 --- a/redsocks.c +++ b/redsocks.c @@ -836,16 +836,16 @@ void redsocks_dump_client(redsocks_client * client, int loglevel) const char *s_relay_evshut = redsocks_evshut_str(client->relay_evshut); redsocks_log_error(client, loglevel, "client(%i): (%s)%s%s input %u output %u, relay(%i): (%s)%s%s input %u output %u, age: %li sec, idle: %li sec.", - bufferevent_getfd(client->client), - redsocks_event_str(bufferevent_get_enabled(client->client)), + client->client ? bufferevent_getfd(client->client) : -1, + redsocks_event_str(client->client ? bufferevent_get_enabled(client->client) : 0), s_client_evshut[0] ? " " : "", s_client_evshut, - evbuffer_get_length(bufferevent_get_input(client->client)), - evbuffer_get_length(bufferevent_get_output(client->client)), - bufferevent_getfd(client->relay), - redsocks_event_str(bufferevent_get_enabled(client->relay)), + client->client ? evbuffer_get_length(bufferevent_get_input(client->client)) : 0, + client->client ? evbuffer_get_length(bufferevent_get_output(client->client)) : 0, + client->relay ? bufferevent_getfd(client->relay) : -1, + redsocks_event_str(client->relay ? bufferevent_get_enabled(client->relay) : 0), s_relay_evshut[0] ? " " : "", s_relay_evshut, - evbuffer_get_length(bufferevent_get_input(client->relay)), - evbuffer_get_length(bufferevent_get_output(client->relay)), + client->relay ? evbuffer_get_length(bufferevent_get_input(client->relay)) : 0, + client->relay ? evbuffer_get_length(bufferevent_get_output(client->relay)) : 0, now - client->first_event, now - client->last_event); } From 7291654818b885116d2deed6979115e312dd9c02 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 20 Mar 2016 20:32:06 +0800 Subject: [PATCH 075/192] Fix: audit signle end connection --- redsocks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/redsocks.c b/redsocks.c index 98d5669a..5a31c0cc 100644 --- a/redsocks.c +++ b/redsocks.c @@ -891,7 +891,8 @@ static void redsocks_audit_instance(redsocks_instance *instance) /* drop this client if either end disconnected */ if ((client->client_evshut == EV_WRITE && client->relay_evshut == EV_READ) || (client->client_evshut == EV_READ && client->relay_evshut == EV_WRITE) - || (client->client_evshut == (EV_READ|EV_WRITE) && client->relay_evshut == EV_WRITE)) + || (client->client_evshut == (EV_READ|EV_WRITE) && client->relay_evshut == EV_WRITE) + || (client->client_evshut == EV_READ && client->relay == NULL)) drop_it = 1; } /* close long connections without activities */ From 79df541378bed0e07e7b025696040dcf0242bf23 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 21 Mar 2016 11:14:40 +0800 Subject: [PATCH 076/192] Misc. updates --- autoproxy.c | 11 ++++------- redsocks.c | 4 ++-- redudp.c | 4 ++-- redudp.h | 2 +- shadowsocks-udp.c | 3 +-- shadowsocks.c | 1 - socks5-udp.c | 2 +- tcpdns.c | 29 ++++++++++++++++++++++++++--- utils.c | 10 ++-------- 9 files changed, 39 insertions(+), 27 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index 68c36fe9..9bc04be6 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -41,11 +41,10 @@ typedef enum autoproxy_state_t { typedef struct autoproxy_client_t { autoproxy_state state; - time_t time_connect_relay; // timestamp when start to connect relay size_t data_recv; size_t data_sent; struct event * recv_timer_event; - int quick_check; // flag indicating quick check initiated. + short quick_check; // flag indicating quick check initiated. } autoproxy_client; @@ -61,8 +60,8 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) typedef struct autoproxy_config_t { list_head list; // Make it a list to support multiple configurations - int quick_connect_timeout; - int no_quick_check_seconds; + uint16_t quick_connect_timeout; + uint16_t no_quick_check_seconds; } autoproxy_config; static autoproxy_config default_config = { @@ -323,7 +322,7 @@ static int handle_write_to_relay(redsocks_client *client) { struct timeval tv; tv.tv_sec = 0; - tv.tv_usec = 600000; + tv.tv_usec = 400000; if (-1 == evtimer_add(aclient->recv_timer_event, &tv)) { redsocks_log_error(client, LOG_DEBUG, "Failed to add timer!"); @@ -730,8 +729,6 @@ static int auto_connect_relay(redsocks_client *client) NULL, auto_relay_connected, auto_event_error, client, &tv); - aclient->time_connect_relay = redsocks_time(NULL); - if (!client->relay) { redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); redsocks_drop_client(client); diff --git a/redsocks.c b/redsocks.c index 5a31c0cc..0d57746d 100644 --- a/redsocks.c +++ b/redsocks.c @@ -41,7 +41,7 @@ #include "libevent-compat.h" -#define REDSOCKS_RELAY_HALFBUFF 1024*32 +#define REDSOCKS_RELAY_HALFBUFF 1024*16 #define REDSOCKS_AUDIT_INTERVAL 60*2 static void redsocks_relay_relayreadcb(struct bufferevent *from, void *_client); static void redsocks_relay_relaywritecb(struct bufferevent *from, void *_client); @@ -932,7 +932,7 @@ static int redsocks_init_instance(redsocks_instance *instance) goto fail; } - fd = socket(AF_INET, SOCK_STREAM, 0); + fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (fd == -1) { log_errno(LOG_ERR, "socket"); goto fail; diff --git a/redudp.c b/redudp.c index fd4beb6b..1e6e2046 100644 --- a/redudp.c +++ b/redudp.c @@ -113,7 +113,7 @@ static int bound_udp4_get(const struct sockaddr_in *addr) node->key = key; node->ref = 1; - node->fd = socket(AF_INET, SOCK_DGRAM, 0); + node->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); node->t_last_rx = redsocks_time(NULL); if (node->fd == -1) { log_errno(LOG_ERR, "socket"); @@ -554,7 +554,7 @@ static int redudp_init_instance(redudp_instance *instance) goto fail; } - fd = socket(AF_INET, SOCK_DGRAM, 0); + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd == -1) { log_errno(LOG_ERR, "socket"); goto fail; diff --git a/redudp.h b/redudp.h index 27fbabc6..6b1dae3f 100644 --- a/redudp.h +++ b/redudp.h @@ -54,7 +54,7 @@ typedef struct redudp_client_t { time_t first_event; time_t last_client_event; time_t last_relay_event; - unsigned int queue_len; + uint16_t queue_len; list_head queue; } redudp_client; diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index 1bf0d07a..16294e3e 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -35,7 +35,6 @@ typedef struct ss_client_t { } ss_client; typedef struct ss_instance_t { - int init; int method; enc_info info; struct enc_ctx e_ctx; @@ -194,7 +193,7 @@ static void ss_connect_relay(redudp_client *client) int fd = -1; int error; - fd = socket(AF_INET, SOCK_DGRAM, 0); + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd == -1) { redudp_log_errno(client, LOG_ERR, "socket"); goto fail; diff --git a/shadowsocks.c b/shadowsocks.c index b7b27426..7625c3a7 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -39,7 +39,6 @@ typedef struct ss_client_t { } ss_client; typedef struct ss_instance_t { - int init; int method; enc_info info; } ss_instance; diff --git a/socks5-udp.c b/socks5-udp.c index 6ebb92e9..4056e06b 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -223,7 +223,7 @@ static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg) socks5client->udprelayaddr.sin_port = reply.ip.port; socks5client->udprelayaddr.sin_addr.s_addr = reply.ip.addr; - fd = socket(AF_INET, SOCK_DGRAM, 0); + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd == -1) { redudp_log_errno(client, LOG_ERR, "socket"); goto fail; diff --git a/tcpdns.c b/tcpdns.c index f066029f..17a873c5 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -417,7 +417,7 @@ static int tcpdns_init_instance(tcpdns_instance *instance) int error; int fd = -1; - fd = socket(AF_INET, SOCK_DGRAM, 0); + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd == -1) { log_errno(LOG_ERR, "socket"); goto fail; @@ -479,8 +479,6 @@ static int tcpdns_init() { tcpdns_instance *tmp, *instance = NULL; - // TODO: init debug_dumper - list_for_each_entry_safe(instance, tmp, &instances, list) { if (tcpdns_init_instance(instance) != 0) goto fail; @@ -503,6 +501,30 @@ static int tcpdns_fini() return 0; } +static void tcpdns_dump_instance(tcpdns_instance *instance) +{ + char buf1[RED_INET_ADDRSTRLEN]; + + log_error(LOG_INFO, "Dumping data for instance (tcpdns @ %s):", + red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1))); + log_error(LOG_INFO, "Delay of TCP DNS [%s]: %dms", + red_inet_ntop(&instance->config.tcpdns1_addr, buf1, sizeof(buf1)), + instance->tcp1_delay_ms); + log_error(LOG_INFO, "Delay of TCP DNS [%s]: %dms", + red_inet_ntop(&instance->config.tcpdns2_addr, buf1, sizeof(buf1)), + instance->tcp2_delay_ms); + log_error(LOG_INFO, "End of data dumping."); +} + + +static void tcpdns_debug_dump() +{ + tcpdns_instance *instance = NULL; + + list_for_each_entry(instance, &instances, list) + tcpdns_dump_instance(instance); +} + static parser_section tcpdns_conf_section = { .name = "tcpdns", @@ -515,6 +537,7 @@ app_subsys tcpdns_subsys = { .init = tcpdns_init, .fini = tcpdns_fini, + .dump = tcpdns_debug_dump, .conf_section = &tcpdns_conf_section, }; diff --git a/utils.c b/utils.c index 2d8262bb..ad4f9fed 100644 --- a/utils.c +++ b/utils.c @@ -29,10 +29,6 @@ #include "redsocks.h" // for redsocks_close #include "libc-compat.h" -#ifndef IP_ORIGDSTADDR -#define IP_ORIGDSTADDR 20 -#endif - int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr, struct sockaddr_in *toaddr) { socklen_t addrlen = sizeof(*inaddr); @@ -66,8 +62,6 @@ int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inadd cmsg->cmsg_len >= CMSG_LEN(sizeof(*toaddr)) ) { struct sockaddr_in* cmsgaddr = (struct sockaddr_in*)CMSG_DATA(cmsg); - //char buf[RED_INET_ADDRSTRLEN]; - //log_error(LOG_DEBUG, "IP_ORIGDSTADDR: %s", red_inet_ntop(cmsgaddr, buf, sizeof(buf))); memcpy(toaddr, cmsgaddr, sizeof(*toaddr)); } else { @@ -133,7 +127,7 @@ struct bufferevent* red_connect_relay_if(const char *ifname, int relay_fd = -1; int error; - relay_fd = socket(AF_INET, SOCK_STREAM, 0); + relay_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (relay_fd == -1) { log_errno(LOG_ERR, "socket"); goto fail; @@ -216,7 +210,7 @@ struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, int relay_fd = -1; int error; - relay_fd = socket(AF_INET, SOCK_STREAM, 0); + relay_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (relay_fd == -1) { log_errno(LOG_ERR, "socket"); goto fail; From d9a5799f7fdf0d2dba71ffd6ace2e12fb35f3ba0 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 8 Apr 2016 08:28:22 +0800 Subject: [PATCH 077/192] Fix: mark client socket in nonblocking --- redsocks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/redsocks.c b/redsocks.c index 0d57746d..ae7b4dbc 100644 --- a/redsocks.c +++ b/redsocks.c @@ -741,6 +741,12 @@ static void redsocks_accept_client(int fd, short what, void *_arg) goto fail; } + error = evutil_make_socket_nonblocking(client_fd); + if (error) { + log_errno(LOG_ERR, "evutil_make_socket_nonblocking"); + goto fail; + } + error = setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); if (error) { log_errno(LOG_WARNING, "setsockopt"); From eaf51e4ea119d6d53265072ae3e1d7dc8127eb03 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 8 Apr 2016 14:28:30 +0800 Subject: [PATCH 078/192] Enhance 'tcpdns' In case TCP DNS server responses with failure code like SERVFAIL or REFUSED, the response is dropped silently and the server is penalized by setting its delay to large value. By doing this, server with low latency but not serving is not used. --- tcpdns.c | 93 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/tcpdns.c b/tcpdns.c index 17a873c5..2e43e8c1 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -44,6 +44,19 @@ static int tcpdns_fini(); #define DNS_QR 0x80 #define DNS_TC 0x02 #define DNS_Z 0x70 +#define DNS_RC_MASK 0x0F + +#define DNS_RC_NOERROR 0 +#define DNS_RC_FORMERR 1 +#define DNS_RC_SERVFAIL 2 +#define DNS_RC_NXDOMAIN 3 +#define DNS_RC_NOTIMP 4 +#define DNS_RC_REFUSED 5 +#define DNS_RC_YXDOMAIN 6 +#define DNS_RC_XRRSET 7 +#define DNS_RC_NOTAUTH 8 +#define DNS_RC_NOTZONE 9 + #define DEFAULT_TIMEOUT_SECONDS 4 #define FLAG_TCP_TEST 0x01 @@ -52,9 +65,7 @@ static int tcpdns_fini(); typedef enum tcpdns_state_t { STATE_NEW, STATE_REQUEST_SENT, - STATE_RECV_RESPONSE, STATE_RESPONSE_SENT, - STATE_CONNECTION_TIMEOUT, } tcpdns_state; @@ -72,15 +83,16 @@ static void tcpdns_drop_request(dns_request * req) close(fd); } - if (req->delay && req->state != STATE_RESPONSE_SENT) - { - * req->delay = req->state == STATE_CONNECTION_TIMEOUT ? -1 : 0; - } - list_del(&req->list); free(req); } +static inline void tcpdns_update_delay(dns_request * req, int delay) +{ + if (req->delay) + * req->delay = delay; +} + static void tcpdns_readcb(struct bufferevent *from, void *_arg) { dns_request * req = _arg; @@ -106,18 +118,30 @@ static void tcpdns_readcb(struct bufferevent *from, void *_arg) // FIXME: // suppose we got all data in one read read_size = bufferevent_read(from, &buff, sizeof(buff)); - if (read_size > 2) + if (read_size > (2 + sizeof(dns_header))) { - int fd = EVENT_FD(&req->instance->listener); - sendto(fd, &buff.raw[2], ntohs(buff.len), 0, - (struct sockaddr*)&req->client_addr, sizeof(req->client_addr)); - req->state = STATE_RESPONSE_SENT; - // calculate and update DNS resolver's delay - if (req->delay) - { - gettimeofday(&tv, 0); - timersub(&tv, &req->req_time, &tv); - * req->delay = tv.tv_sec*1000+tv.tv_usec/1000; + dns_header * dh = (dns_header *)&buff.raw[2]; + switch (dh->ra_z_rcode & DNS_RC_MASK) { + case DNS_RC_NOERROR: + case DNS_RC_FORMERR: + case DNS_RC_NXDOMAIN: + { + int fd = EVENT_FD(&req->instance->listener); + if (sendto(fd, &buff.raw[2], read_size - 2, 0, + (struct sockaddr*)&req->client_addr, + sizeof(req->client_addr)) != read_size - 2) { + tcpdns_log_errno(LOG_ERR, "sendto"); + } + req->state = STATE_RESPONSE_SENT; + // calculate and update DNS resolver's delay + gettimeofday(&tv, 0); + timersub(&tv, &req->req_time, &tv); + tcpdns_update_delay(req, tv.tv_sec*1000+tv.tv_usec/1000); + } + break; + default: + // panalize server + tcpdns_update_delay(req, (req->instance->config.timeout + 1) * 1000); } } } @@ -148,7 +172,7 @@ static void tcpdns_connected(struct bufferevent *buffev, void *_arg) if (bufferevent_write(buffev, &len, sizeof(uint16_t)) == -1 || bufferevent_write(buffev, &req->data.raw, req->data_len) == -1) { - tcpdns_log_errno(LOG_ERR, "bufferevent_write_buffer"); + tcpdns_log_errno(LOG_ERR, "bufferevent_write"); tcpdns_drop_request(req); return; } @@ -156,17 +180,17 @@ static void tcpdns_connected(struct bufferevent *buffev, void *_arg) // Set timeout for read with time left since connection setup. gettimeofday(&tv, 0); timersub(&tv, &req->req_time, &tv); - tv2.tv_sec = DEFAULT_TIMEOUT_SECONDS; + tv2.tv_sec = req->instance->config.timeout; tv2.tv_usec = 0; - if (req->instance->config.timeout > 0) - tv2.tv_sec = req->instance->config.timeout; timersub(&tv2, &tv, &tv); - if (tv.tv_sec >=0) { + if (tv.tv_sec > 0 || tv.tv_usec > 0) { bufferevent_set_timeouts(buffev, &tv, NULL); + // Allow reading response bufferevent_enable(buffev, EV_READ); req->state = STATE_REQUEST_SENT; } else { + tcpdns_update_delay(req, tv2.tv_sec * 1000); tcpdns_drop_request(req); } } @@ -184,7 +208,11 @@ static void tcpdns_event_error(struct bufferevent *buffev, short what, void *_ar if (req->state == STATE_NEW && what == (BEV_EVENT_WRITING | BEV_EVENT_TIMEOUT)) { - req->state = STATE_CONNECTION_TIMEOUT; + tcpdns_update_delay(req, -1); + } + else if (saved_errno == ECONNRESET) { + // If connect is reset, try to not use this DNS server next time. + tcpdns_update_delay(req, (req->instance->config.timeout + 1) * 1000); } tcpdns_drop_request(req); } @@ -253,7 +281,7 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) req = (dns_request *)calloc(sizeof(dns_request), 1); if (!req) { - log_error(LOG_INFO, "Out of memeory."); + log_error(LOG_ERR, "Out of memeory."); return; } req->instance = self; @@ -279,10 +307,8 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) && !req->data.header.ancount && !req->data.header.nscount && !req->data.header.arcount /* no answers */ ) { - tv.tv_sec = DEFAULT_TIMEOUT_SECONDS; + tv.tv_sec = self->config.timeout; tv.tv_usec = 0; - if (self->config.timeout>0) - tv.tv_sec = self->config.timeout; destaddr = choose_tcpdns(self, &req->delay); if (!destaddr) @@ -404,7 +430,9 @@ static int tcpdns_onexit(parser_section *section) parser_error(section->context, "%s", err); else list_add(&instance->list, &instances); - + // If timeout is not configured or is configured as zero, use default timeout. + if (instance->config.timeout == 0) + instance->config.timeout = DEFAULT_TIMEOUT_SECONDS; return err ? -1 : 0; } @@ -448,10 +476,8 @@ static int tcpdns_init_instance(tcpdns_instance *instance) fail: tcpdns_fini_instance(instance); - if (fd != -1) { - if (close(fd) != 0) - log_errno(LOG_WARNING, "close"); - } + if (fd != -1 && close(fd) != 0) + log_errno(LOG_WARNING, "close"); return -1; } @@ -466,7 +492,6 @@ static void tcpdns_fini_instance(tcpdns_instance *instance) log_errno(LOG_WARNING, "event_del"); if (close(EVENT_FD(&instance->listener)) != 0) log_errno(LOG_WARNING, "close"); - memset(&instance->listener, 0, sizeof(instance->listener)); } list_del(&instance->list); From 2a93e7e763c7abda7a5e349f3bc403199ec4f2e5 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 8 Apr 2016 14:37:28 -0600 Subject: [PATCH 079/192] Make logs more informative --- ipcache.c | 2 +- main.c | 2 +- redsocks.c | 14 +++++++++----- redudp.c | 2 +- shadowsocks-udp.c | 6 +++++- shadowsocks.c | 6 +++++- tcpdns.c | 5 ++++- 7 files changed, 26 insertions(+), 11 deletions(-) diff --git a/ipcache.c b/ipcache.c index cbf5b40e..5cfe4772 100644 --- a/ipcache.c +++ b/ipcache.c @@ -406,7 +406,7 @@ static void cache_dumper() cache_data * item; cache_config * config = get_config(); - log_error(LOG_INFO, "Start dumping IP cache:"); + log_error(LOG_INFO, "Dumping IP cache:"); for (; blk < config->block_count; blk++) { for (idx=0; idx < config->block_size; idx++) diff --git a/main.c b/main.c index e1ccee1b..e3eab1f2 100644 --- a/main.c +++ b/main.c @@ -190,7 +190,7 @@ int main(int argc, char **argv) goto shutdown; } - log_error(LOG_NOTICE, "redsocks started"); + log_error(LOG_NOTICE, "redsocks started with: %s", event_base_get_method(g_event_base)); event_base_dispatch(g_event_base); diff --git a/redsocks.c b/redsocks.c index ae7b4dbc..f6000b0b 100644 --- a/redsocks.c +++ b/redsocks.c @@ -382,7 +382,7 @@ int redsocks_start_relay(redsocks_client *client) void redsocks_drop_client(redsocks_client *client) { int fd; - redsocks_log_error(client, LOG_DEBUG, "dropping client"); + redsocks_log_error(client, LOG_DEBUG, "dropping client @ state: %d", client->state); if (client->instance->config.autoproxy && autoproxy_subsys.fini) autoproxy_subsys.fini(client); @@ -859,9 +859,11 @@ void redsocks_dump_client(redsocks_client * client, int loglevel) static void redsocks_dump_instance(redsocks_instance *instance) { redsocks_client *client = NULL; + char addr_str[RED_INET_ADDRSTRLEN]; - log_error(LOG_INFO, "Dumping client list for instance %p(%s):", instance, - instance->relay_ss->name); + log_error(LOG_INFO, "Dumping client list for instance (%s @ %s):", + instance->relay_ss->name, + red_inet_ntop(&instance->config.bindaddr, addr_str, sizeof(addr_str))); list_for_each_entry(client, &instance->clients, list) redsocks_dump_client(client, LOG_INFO); @@ -886,9 +888,11 @@ static void redsocks_audit_instance(redsocks_instance *instance) redsocks_client *tmp, *client = NULL; time_t now = redsocks_time(NULL); int drop_it = 0; + char addr_str[RED_INET_ADDRSTRLEN]; - log_error(LOG_DEBUG, "Audit client list for instance %p(%s):", instance, - instance->relay_ss->name); + log_error(LOG_DEBUG, "Audit client list for instance (%s @ %s):", + instance->relay_ss->name, + red_inet_ntop(&instance->config.bindaddr, addr_str, sizeof(addr_str))); list_for_each_entry_safe(client, tmp, &instance->clients, list) { drop_it = 0; diff --git a/redudp.c b/redudp.c index 1e6e2046..a139d13a 100644 --- a/redudp.c +++ b/redudp.c @@ -241,7 +241,7 @@ struct sockaddr_in* get_destaddr(redudp_client *client) */ void redudp_drop_client(redudp_client *client) { - redudp_log_error(client, LOG_DEBUG, "Dropping UDP client"); + redudp_log_error(client, LOG_DEBUG, "Dropping client @ state: %d", client->state); enqueued_packet *q, *tmp; if (client->instance->relay_ss->fini) diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index 16294e3e..b803a380 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -231,6 +231,7 @@ static int ss_instance_init(struct redudp_instance_t *instance) { ss_instance * ss = (ss_instance *)(instance+1); const redudp_config *config = &instance->config; + char buf1[RED_INET_ADDRSTRLEN]; int valid_cred = ss_is_valid_cred(config->login, config->password); if (!valid_cred @@ -241,7 +242,10 @@ static int ss_instance_init(struct redudp_instance_t *instance) } else { - log_error(LOG_INFO, "using encryption method: %s", config->login); + log_error(LOG_INFO, "%s @ %s: encryption method: %s", + instance->relay_ss->name, + red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1)), + config->login); } // Two buffers are allocated for each instance. One is for receiving plain // data, one is for encryption/decrption. diff --git a/shadowsocks.c b/shadowsocks.c index 7625c3a7..56823d74 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -355,6 +355,7 @@ static int ss_instance_init(struct redsocks_instance_t *instance) { ss_instance * ss = (ss_instance *)(instance+1); const redsocks_config *config = &instance->config; + char buf1[RED_INET_ADDRSTRLEN]; int valid_cred = ss_is_valid_cred(config->login, config->password); if (!valid_cred @@ -365,7 +366,10 @@ static int ss_instance_init(struct redsocks_instance_t *instance) } else { - log_error(LOG_INFO, "using encryption method: %s", config->login); + log_error(LOG_INFO, "%s @ %s: encryption method: %s", + instance->relay_ss->name, + red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1)), + config->login); } return 0; } diff --git a/tcpdns.c b/tcpdns.c index 2e43e8c1..27311d5a 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -75,7 +75,7 @@ typedef enum tcpdns_state_t { static void tcpdns_drop_request(dns_request * req) { int fd; - tcpdns_log_error(LOG_DEBUG, "dropping request"); + tcpdns_log_error(LOG_DEBUG, "dropping request @ state: %d", req->state); if (req->resolver) { fd = bufferevent_getfd(req->resolver); @@ -444,6 +444,7 @@ static int tcpdns_init_instance(tcpdns_instance *instance) */ int error; int fd = -1; + char buf1[RED_INET_ADDRSTRLEN]; fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd == -1) { @@ -471,6 +472,8 @@ static int tcpdns_init_instance(tcpdns_instance *instance) goto fail; } + log_error(LOG_INFO, "tcpdns @ %s", + red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1))); return 0; fail: From 225428013a13c0ac0cf86dad9677b8715cafc234 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 13 Apr 2016 13:59:12 +0800 Subject: [PATCH 080/192] Fix memory leak in Basic http-auth Back propped from darkk/redsocks/master --- http-auth.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/http-auth.c b/http-auth.c index 8302ea84..c4a67942 100644 --- a/http-auth.c +++ b/http-auth.c @@ -32,15 +32,15 @@ char* basic_authentication_encode(const char *user, const char *passwd) { /* prepare the user:pass key pair */ int pair_len = strlen(user) + 1 + strlen(passwd); - char *pair_ptr = calloc(pair_len + 1, 1); + char pair[pair_len + 1]; - sprintf(pair_ptr, "%s:%s", user, passwd); + sprintf(pair, "%s:%s", user, passwd); /* calculate the final string length */ int basic_len = BASE64_SIZE(pair_len); char *basic_ptr = calloc(basic_len + 1, 1); - if (!base64_encode(basic_ptr, basic_len, (const uint8_t*)pair_ptr, pair_len)) + if (!base64_encode(basic_ptr, basic_len, (const uint8_t*)pair, pair_len)) return NULL; return basic_ptr; From 181b6d893aefb222c55b9e2f006bd03268422a12 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 15 Apr 2016 11:06:55 +0800 Subject: [PATCH 081/192] Increase range of IP cache stale time to 32-bit value --- ipcache.c | 10 +++++----- parser.c | 17 +++++++++++++++++ parser.h | 1 + 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/ipcache.c b/ipcache.c index 5cfe4772..1f926e7a 100644 --- a/ipcache.c +++ b/ipcache.c @@ -42,9 +42,9 @@ typedef struct cache_config_t { // Values to be read from config file uint16_t cache_size; uint16_t port_check; - uint16_t stale_time; - char * cache_file; - uint16_t autosave_interval; + uint32_t stale_time; + char * cache_file; + uint32_t autosave_interval; // Dynamically calculated values. unsigned int block_size; unsigned int block_count; @@ -63,9 +63,9 @@ static parser_entry cache_entries[] = { { .key = "cache_size", .type = pt_uint16 }, { .key = "port_check", .type = pt_uint16 }, - { .key = "stale_time", .type = pt_uint16 }, + { .key = "stale_time", .type = pt_uint32 }, { .key = "cache_file", .type = pt_pchar }, - { .key = "autosave_interval", .type = pt_uint16 }, + { .key = "autosave_interval", .type = pt_uint32 }, { } }; diff --git a/parser.c b/parser.c index d12f63e2..7e0ab0b3 100644 --- a/parser.c +++ b/parser.c @@ -297,6 +297,22 @@ static int vp_uint16(parser_context *context, void *addr, const char *token) return 0; } +static int vp_uint32(parser_context *context, void *addr, const char *token) +{ + char *end; + unsigned long int uli = strtoul(token, &end, 0); + if (uli > 0xFFFFFFFF) { + parser_error(context, "integer out of 32bit range"); + return -1; + } + if (*end != '\0') { + parser_error(context, "integer is not parsed"); + return -1; + } + *(uint32_t*)addr = (uint32_t)uli; + return 0; +} + static int vp_in_addr(parser_context *context, void *addr, const char *token) { struct in_addr ia; @@ -397,6 +413,7 @@ static value_parser value_parser_by_type[] = [pt_bool] = vp_pbool, [pt_pchar] = vp_pchar, [pt_uint16] = vp_uint16, + [pt_uint32] = vp_uint32, [pt_in_addr] = vp_in_addr, [pt_in_addr2] = vp_in_addr2, }; diff --git a/parser.h b/parser.h index 085681c1..7b101f2e 100644 --- a/parser.h +++ b/parser.h @@ -8,6 +8,7 @@ typedef enum { pt_bool, // "bool" from stdbool.h, not "_Bool" or anything else pt_pchar, pt_uint16, + pt_uint32, pt_in_addr, pt_in_addr2, // inaddr[0] = net, inaddr[1] = netmask } parser_type; From 4f9ee0e7196d70cdd729262b58c6edaf2112b853 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 15 Apr 2016 13:58:27 +0800 Subject: [PATCH 082/192] Fix: potential crash due to dropping client twice Other trivial changes are included. --- autoproxy.c | 73 ++++++++++++++++++----------------------------- direct.c | 5 +--- redsocks.c | 9 +++--- redudp.c | 6 ++-- shadowsocks-udp.c | 6 ++-- shadowsocks.c | 34 +++++++--------------- socks5-udp.c | 6 ++-- tcpdns.c | 6 ++-- utils.c | 18 ++++++------ 9 files changed, 64 insertions(+), 99 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index 9bc04be6..b58d5d78 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -159,7 +159,18 @@ static autoproxy_config * get_config(redsocks_client * client) } } -#define get_autoproxy_client(client) (void*)(client + 1) + client->instance->relay_ss->payload_len; +#define get_autoproxy_client(client) ((void*)(client + 1) + client->instance->relay_ss->payload_len) + +static void auto_release_recv_timer(autoproxy_client * aclient) +{ + // Cancel timer and release event object for timer + if (aclient->recv_timer_event) + { + event_del(aclient->recv_timer_event); + event_free(aclient->recv_timer_event); + aclient->recv_timer_event = NULL; + } +} static void auto_client_init(redsocks_client *client) { @@ -172,13 +183,7 @@ static void auto_client_init(redsocks_client *client) static void auto_client_fini(redsocks_client *client) { autoproxy_client * aclient = get_autoproxy_client(client); - - if (aclient->recv_timer_event) - { - event_del(aclient->recv_timer_event); - event_free(aclient->recv_timer_event); - aclient->recv_timer_event = NULL; - } + auto_release_recv_timer(aclient); } static void on_connection_confirmed(redsocks_client *client) @@ -204,13 +209,7 @@ static void auto_confirm_connection(redsocks_client * client) evbuffer_drain(bufferevent_get_input(client->client), aclient->data_sent); aclient->data_sent = 0; } - // Cancel timer and release event object for timer - if (aclient->recv_timer_event) - { - event_del(aclient->recv_timer_event); - event_free(aclient->recv_timer_event); - aclient->recv_timer_event = NULL; - } + auto_release_recv_timer(aclient); on_connection_confirmed(client); } @@ -497,16 +496,9 @@ static int auto_retry(redsocks_client * client, int updcache) inet_ntoa(client->destaddr.sin_addr)); } } - // Release timer - if (aclient->recv_timer_event) - { - event_del(aclient->recv_timer_event); - event_free(aclient->recv_timer_event); - aclient->recv_timer_event = NULL; - } + auto_release_recv_timer(aclient); auto_drop_relay(client); - // restore callbacks for ordinary client. bufferevent_setcb(client->client, NULL, NULL, redsocks_event_error, client); // enable reading to handle EOF from client @@ -540,8 +532,7 @@ static int auto_retry_or_drop(redsocks_client * client) if (aclient->state == AUTOPROXY_NEW || aclient->state == AUTOPROXY_CONNECTED) { on_connection_blocked(client); - auto_retry(client, 0); - return 0; + return auto_retry(client, 0); } /* drop */ return 1; @@ -573,24 +564,18 @@ static void auto_relay_connected(struct bufferevent *buffev, void *_arg) The two peers will handle it. */ bufferevent_set_timeouts(client->relay, NULL, NULL); - if (!redsocks_start_relay(client)) - { - /* overwrite theread callback to my function */ - bufferevent_setcb(client->client, direct_relay_clientreadcb, - direct_relay_clientwritecb, - auto_event_error, - client); - bufferevent_setcb(client->relay, direct_relay_relayreadcb, - direct_relay_relaywritecb, - auto_event_error, - client); - } - else - { - redsocks_log_error(client, LOG_DEBUG, "failed to start relay"); - redsocks_drop_client(client); + if (redsocks_start_relay(client)) + // redsocks_start_relay() drops client on failure return; - } + /* overwrite theread callback to my function */ + bufferevent_setcb(client->client, direct_relay_clientreadcb, + direct_relay_clientwritecb, + auto_event_error, + client); + bufferevent_setcb(client->relay, direct_relay_relayreadcb, + direct_relay_relaywritecb, + auto_event_error, + client); // Write any data received from client side to relay. if (evbuffer_get_length(bufferevent_get_input(client->client))) direct_relay_relaywritecb(client->relay, client); @@ -695,10 +680,6 @@ static int auto_connect_relay(redsocks_client *client) time_t * acc_time = NULL; time_t now = redsocks_time(NULL); - /* use default timeout if timeout is not configured */ - if (tv.tv_sec == 0) - tv.tv_sec = DEFAULT_CONNECT_TIMEOUT; - if (aclient->state == AUTOPROXY_NEW) { acc_time = cache_get_addr_time(&client->destaddr); diff --git a/direct.c b/direct.c index 300584d5..0f83dbdd 100644 --- a/direct.c +++ b/direct.c @@ -51,11 +51,8 @@ static void direct_write_cb(struct bufferevent *buffev, void *_arg) { client->state = 1; if (redsocks_start_relay(client)) - { - // Failed to start relay. Drop connection. - redsocks_drop_client(client); + // Failed to start relay. Connection is dropped. return; - } // Write any data received from client to relay if (evbuffer_get_length(bufferevent_get_input(client->client))) client->instance->relay_ss->writecb(buffev, client); diff --git a/redsocks.c b/redsocks.c index f6000b0b..8cbde270 100644 --- a/redsocks.c +++ b/redsocks.c @@ -207,6 +207,8 @@ static int redsocks_onexit(parser_section *section) if (err) parser_error(section->context, "%s", err); + if (instance->config.timeout == 0) + instance->config.timeout = DEFAULT_CONNECT_TIMEOUT; return err ? -1 : 0; } @@ -638,9 +640,6 @@ int redsocks_connect_relay(redsocks_client *client) struct timeval tv; tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; - if (tv.tv_sec == 0) - tv.tv_sec = DEFAULT_CONNECT_TIMEOUT; - client->relay = red_connect_relay2(&client->instance->config.relayaddr, NULL, @@ -713,7 +712,7 @@ static void redsocks_accept_client(int fd, short what, void *_arg) if (errno == ENFILE || errno == EMFILE || errno == ENOBUFS || errno == ENOMEM) { self->accept_backoff_ms = (self->accept_backoff_ms << 1) + 1; clamp_value(self->accept_backoff_ms, self->config.min_backoff_ms, self->config.max_backoff_ms); - int delay = (random() % self->accept_backoff_ms) + 1; + int delay = (red_randui32() % self->accept_backoff_ms) + 1; log_errno(LOG_WARNING, "accept: out of file descriptors, backing off for %u ms", delay); struct timeval tvdelay = { delay / 1000, (delay % 1000) * 1000 }; if (tracked_event_del(&self->listener) != 0) @@ -1015,7 +1014,7 @@ static void redsocks_fini_instance(redsocks_instance *instance) { if (timerisset(&instance->listener.inserted)) if (tracked_event_del(&instance->listener) != 0) log_errno(LOG_WARNING, "event_del"); - redsocks_close(EVENT_FD(&instance->listener.ev)); + redsocks_close(event_get_fd(&instance->listener.ev)); memset(&instance->listener, 0, sizeof(instance->listener)); } diff --git a/redudp.c b/redudp.c index a139d13a..74529dc6 100644 --- a/redudp.c +++ b/redudp.c @@ -282,7 +282,7 @@ void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, // When working with TPROXY, we have to get sender FD from tree on // receipt of each packet from relay. fd = do_tproxy(client->instance) ? bound_udp4_get(srcaddr) - : EVENT_FD(&client->instance->listener); + : event_get_fd(&client->instance->listener); if (fd == -1) { redudp_log_error(client, LOG_WARNING, "bound_udp4_get failure"); return; @@ -392,7 +392,7 @@ static void redudp_pkt_from_client(int fd, short what, void *_arg) pdestaddr = do_tproxy(self) ? &destaddr : NULL; - assert(fd == EVENT_FD(&self->listener)); + assert(fd == event_get_fd(&self->listener)); // destaddr will be filled with true destination if it is available pktlen = red_recv_udp_pkt(fd, recv_buff, sizeof(recv_buff), &clientaddr, pdestaddr); if (pktlen == -1) @@ -628,7 +628,7 @@ static void redudp_fini_instance(redudp_instance *instance) if (event_initialized(&instance->listener)) { if (event_del(&instance->listener) != 0) log_errno(LOG_WARNING, "event_del"); - close(EVENT_FD(&instance->listener)); + close(event_get_fd(&instance->listener)); memset(&instance->listener, 0, sizeof(instance->listener)); } diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index b803a380..07472b72 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -67,7 +67,7 @@ static void ss_client_fini(redudp_client *client) { ss_client *ssclient = (void*)(client + 1); if (event_initialized(&ssclient->udprelay)) { - close(EVENT_FD(&ssclient->udprelay)); + close(event_get_fd(&ssclient->udprelay)); if (event_del(&ssclient->udprelay) == -1) redudp_log_errno(client, LOG_ERR, "event_del"); } @@ -121,7 +121,7 @@ static void ss_forward_pkt(redudp_client *client, struct sockaddr * destaddr, vo io[0].iov_base = ss->buff; io[0].iov_len = fwdlen; - outgoing = sendmsg(EVENT_FD(&ssclient->udprelay), &msg, 0); + outgoing = sendmsg(event_get_fd(&ssclient->udprelay), &msg, 0); if (outgoing == -1) { redudp_log_errno(client, LOG_DEBUG, "sendmsg: Can't forward packet, dropping it"); return; @@ -143,7 +143,7 @@ static void ss_pkt_from_server(int fd, short what, void *_arg) struct sockaddr_in udprelayaddr; int rc; - assert(fd == EVENT_FD(&ssclient->udprelay)); + assert(fd == event_get_fd(&ssclient->udprelay)); pktlen = red_recv_udp_pkt(fd, ss->buff, SHARED_BUFF_SIZE, &udprelayaddr, NULL); if (pktlen == -1) diff --git a/shadowsocks.c b/shadowsocks.c index 56823d74..a03ac09d 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -25,8 +25,6 @@ #include "encrypt.h" #include "shadowsocks.h" -#define INITIAL_BUFFER_SIZE 8192 - typedef enum ss_state_t { ss_new, ss_connected, @@ -292,24 +290,18 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) The two peers will handle it. */ bufferevent_set_timeouts(client->relay, NULL, NULL); - if (!redsocks_start_relay(client)) - { - /* overwrite theread callback to my function */ - bufferevent_setcb(client->client, ss_client_readcb, - ss_client_writecb, - redsocks_event_error, - client); - bufferevent_setcb(client->relay, ss_relay_readcb, - ss_relay_writecb, - redsocks_event_error, - client); - } - else - { - redsocks_log_error(client, LOG_DEBUG, "failed to start relay"); - redsocks_drop_client(client); + if (redsocks_start_relay(client)) + // redsocks_start_relay() drops client on failure return; - } + /* overwrite theread callback to my function */ + bufferevent_setcb(client->client, ss_client_readcb, + ss_client_writecb, + redsocks_event_error, + client); + bufferevent_setcb(client->relay, ss_relay_readcb, + ss_relay_writecb, + redsocks_event_error, + client); /* build and send header */ // TODO: Better implementation and IPv6 Support @@ -335,10 +327,6 @@ static int ss_connect_relay(redsocks_client *client) tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; - /* use default timeout if timeout is not configured */ - if (tv.tv_sec == 0) - tv.tv_sec = DEFAULT_CONNECT_TIMEOUT; - client->relay = red_connect_relay2(&client->instance->config.relayaddr, NULL, ss_relay_connected, redsocks_event_error, client, &tv); diff --git a/socks5-udp.c b/socks5-udp.c index 4056e06b..d7c9c723 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -87,7 +87,7 @@ static void socks5_client_fini(redudp_client *client) int fd; if (event_initialized(&socks5client->udprelay)) { - fd = EVENT_FD(&socks5client->udprelay); + fd = event_get_fd(&socks5client->udprelay); if (event_del(&socks5client->udprelay) == -1) redudp_log_errno(client, LOG_ERR, "event_del"); close(fd); @@ -127,7 +127,7 @@ static void socks5_forward_pkt(redudp_client *client, struct sockaddr *destaddr, io[1].iov_base = buf; io[1].iov_len = pktlen; - outgoing = sendmsg(EVENT_FD(&socks5client->udprelay), &msg, 0); + outgoing = sendmsg(event_get_fd(&socks5client->udprelay), &msg, 0); if (outgoing == -1) { redudp_log_errno(client, LOG_WARNING, "sendmsg: Can't forward packet, dropping it"); return; @@ -149,7 +149,7 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) ssize_t pktlen, fwdlen; struct sockaddr_in udprelayaddr; - assert(fd == EVENT_FD(&socks5client->udprelay)); + assert(fd == event_get_fd(&socks5client->udprelay)); pktlen = red_recv_udp_pkt(fd, pkt.buf, sizeof(pkt.buf), &udprelayaddr, NULL); if (pktlen == -1) diff --git a/tcpdns.c b/tcpdns.c index 27311d5a..776fce32 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -126,7 +126,7 @@ static void tcpdns_readcb(struct bufferevent *from, void *_arg) case DNS_RC_FORMERR: case DNS_RC_NXDOMAIN: { - int fd = EVENT_FD(&req->instance->listener); + int fd = event_get_fd(&req->instance->listener); if (sendto(fd, &buff.raw[2], read_size - 2, 0, (struct sockaddr*)&req->client_addr, sizeof(req->client_addr)) != read_size - 2) { @@ -276,7 +276,7 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) struct sockaddr_in * destaddr; ssize_t pktlen; - assert(fd == EVENT_FD(&self->listener)); + assert(fd == event_get_fd(&self->listener)); /* allocate and initialize request structure */ req = (dns_request *)calloc(sizeof(dns_request), 1); if (!req) @@ -493,7 +493,7 @@ static void tcpdns_fini_instance(tcpdns_instance *instance) if (event_initialized(&instance->listener)) { if (event_del(&instance->listener) != 0) log_errno(LOG_WARNING, "event_del"); - if (close(EVENT_FD(&instance->listener)) != 0) + if (close(event_get_fd(&instance->listener)) != 0) log_errno(LOG_WARNING, "close"); } diff --git a/utils.c b/utils.c index ad4f9fed..f8b1c341 100644 --- a/utils.c +++ b/utils.c @@ -143,7 +143,7 @@ struct bufferevent* red_connect_relay_if(const char *ifname, error = evutil_make_socket_nonblocking(relay_fd); if (error) { - log_errno(LOG_ERR, "fcntl"); + log_errno(LOG_ERR, "evutil_make_socket_nonblocking"); goto fail; } @@ -173,7 +173,7 @@ struct bufferevent* red_connect_relay_if(const char *ifname, // if (error) { error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); if (error && errno != EINPROGRESS) { - log_errno(LOG_NOTICE, "bufferevent_socket_connect"); + log_errno(LOG_NOTICE, "connect"); goto fail; } @@ -218,7 +218,7 @@ struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, error = evutil_make_socket_nonblocking(relay_fd); if (error) { - log_errno(LOG_ERR, "fcntl"); + log_errno(LOG_ERR, "evutil_make_socket_nonblocking"); goto fail; } @@ -250,7 +250,7 @@ struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, // if (error) { error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); if (error && errno != EINPROGRESS) { - log_errno(LOG_NOTICE, "bufferevent_socket_connect"); + log_errno(LOG_NOTICE, "connect"); goto fail; } @@ -270,9 +270,9 @@ int red_socket_geterrno(struct bufferevent *buffev) int pseudo_errno; socklen_t optlen = sizeof(pseudo_errno); - assert(EVENT_FD(&buffev->ev_read) == EVENT_FD(&buffev->ev_write)); + assert(event_get_fd(&buffev->ev_read) == event_get_fd(&buffev->ev_write)); - error = getsockopt(EVENT_FD(&buffev->ev_read), SOL_SOCKET, SO_ERROR, &pseudo_errno, &optlen); + error = getsockopt(event_get_fd(&buffev->ev_read), SOL_SOCKET, SO_ERROR, &pseudo_errno, &optlen); if (error) { log_errno(LOG_ERR, "getsockopt"); return -1; @@ -344,10 +344,10 @@ size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, s maxlen = EVBUFFER_LENGTH(src->input) - skip> maxlen?maxlen: EVBUFFER_LENGTH(src->input)-skip; n = evbuffer_peek(src->input, maxlen+skip, NULL, NULL, 0); - if (n>sizeof(quick_v)/sizeof(struct evbuffer_iovec)) + if (n > sizeof(quick_v)/sizeof(struct evbuffer_iovec)) v = malloc(sizeof(struct evbuffer_iovec)*n); else - v = quick_v; + v = &quick_v[0]; n = evbuffer_peek(src->input, maxlen+skip, NULL, v, n); for (i=0; isizeof(quick_v)/sizeof(struct evbuffer_iovec)) + if (v != &quick_v[0]) free(v); return written; } From 76e99d716129b2efc1a9070b2e4f7eae7d354851 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 12 Apr 2016 12:42:04 +0800 Subject: [PATCH 083/192] Reduce memory consumption --- redsocks.h | 2 +- redudp.c | 11 ++++++----- redudp.h | 3 +++ shadowsocks-udp.c | 35 ++++++++++++++--------------------- socks5-udp.c | 22 +++++++++++----------- 5 files changed, 35 insertions(+), 38 deletions(-) diff --git a/redsocks.h b/redsocks.h index 4dbb8ca9..ca439aa5 100644 --- a/redsocks.h +++ b/redsocks.h @@ -65,7 +65,7 @@ typedef struct redsocks_client_t { struct sockaddr_in clientaddr; struct sockaddr_in destaddr; int state; // it's used by bottom layer - int relay_connected; + short relay_connected; unsigned short client_evshut; unsigned short relay_evshut; time_t first_event; diff --git a/redudp.c b/redudp.c index 74529dc6..85e3f6bb 100644 --- a/redudp.c +++ b/redudp.c @@ -46,7 +46,7 @@ #define REDUDP_AUDIT_INTERVAL 10 // Multiple instances share the same buffer for message receiving -static char recv_buff[64*1024];// max size of UDP packet is less than 64K +static char shared_buff[MAX_UDP_PACKET_SIZE];// max size of UDP packet is less than 64K static void redudp_fini_instance(redudp_instance *instance); static int redudp_fini(); @@ -394,7 +394,7 @@ static void redudp_pkt_from_client(int fd, short what, void *_arg) assert(fd == event_get_fd(&self->listener)); // destaddr will be filled with true destination if it is available - pktlen = red_recv_udp_pkt(fd, recv_buff, sizeof(recv_buff), &clientaddr, pdestaddr); + pktlen = red_recv_udp_pkt(fd, self->shared_buff, MAX_UDP_PACKET_SIZE, &clientaddr, pdestaddr); if (pktlen == -1) return; if (!pdestaddr) @@ -414,14 +414,14 @@ static void redudp_pkt_from_client(int fd, short what, void *_arg) redudp_bump_timeout(client); if (self->relay_ss->ready_to_fwd(client)) { - self->relay_ss->forward_pkt(client, (struct sockaddr *)pdestaddr, recv_buff, pktlen); + self->relay_ss->forward_pkt(client, (struct sockaddr *)pdestaddr, self->shared_buff, pktlen); } else { - redudp_enqeue_pkt(client, pdestaddr, recv_buff, pktlen); + redudp_enqeue_pkt(client, pdestaddr, self->shared_buff, pktlen); } } else { - redudp_first_pkt_from_client(self, &clientaddr, pdestaddr, recv_buff, pktlen); + redudp_first_pkt_from_client(self, &clientaddr, pdestaddr, self->shared_buff, pktlen); } } @@ -548,6 +548,7 @@ static int redudp_init_instance(redudp_instance *instance) int fd = -1; char buf1[RED_INET_ADDRSTRLEN], buf2[RED_INET_ADDRSTRLEN]; + instance->shared_buff = &shared_buff[0]; if (instance->relay_ss->instance_init && instance->relay_ss->instance_init(instance)) { log_errno(LOG_ERR, "Failed to init UDP relay subsystem."); diff --git a/redudp.h b/redudp.h index 6b1dae3f..4adf4a4f 100644 --- a/redudp.h +++ b/redudp.h @@ -4,6 +4,8 @@ #include #include "list.h" +#define MAX_UDP_PACKET_SIZE 0xFFFF + struct redudp_client_t; struct redudp_instance_t; @@ -42,6 +44,7 @@ typedef struct redudp_instance_t { struct event listener; list_head clients; udprelay_subsys *relay_ss; + void * shared_buff; // pointer to 64K buffer shared by clients for receiving/processing udp packets } redudp_instance; typedef struct redudp_client_t { diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index 07472b72..9db7de7f 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -28,8 +28,6 @@ #include "encrypt.h" #include "shadowsocks.h" -#define SHARED_BUFF_SIZE 0x10000 //64K - typedef struct ss_client_t { struct event udprelay; } ss_client; @@ -40,7 +38,6 @@ typedef struct ss_instance_t { struct enc_ctx e_ctx; struct enc_ctx d_ctx; void * buff; - void * buff2; } ss_instance; @@ -85,6 +82,7 @@ static void ss_forward_pkt(redudp_client *client, struct sockaddr * destaddr, vo ss_header_ipv4 header; size_t len = 0; size_t fwdlen = 0; + void * buff = client->instance->shared_buff; /* build and send header */ // TODO: Better implementation and IPv6 Support @@ -96,11 +94,11 @@ static void ss_forward_pkt(redudp_client *client, struct sockaddr * destaddr, vo redudp_log_error(client, LOG_ERR, "Shadowsocks UDP failed to initialize encryption context."); return; } - rc = ss_encrypt(&ss->e_ctx, (char *)&header, sizeof(header), ss->buff, &len); + rc = ss_encrypt(&ss->e_ctx, (char *)&header, sizeof(header), buff, &len); if (rc) { - if (len + pktlen < SHARED_BUFF_SIZE) - rc = ss_encrypt(&ss->e_ctx, (char *)data, pktlen, ss->buff+len, &fwdlen); + if (len + pktlen < MAX_UDP_PACKET_SIZE) + rc = ss_encrypt(&ss->e_ctx, (char *)data, pktlen, buff+len, &fwdlen); else rc = 0; } @@ -118,7 +116,7 @@ static void ss_forward_pkt(redudp_client *client, struct sockaddr * destaddr, vo msg.msg_iov = io; msg.msg_iovlen = SIZEOF_ARRAY(io); - io[0].iov_base = ss->buff; + io[0].iov_base = buff; io[0].iov_len = fwdlen; outgoing = sendmsg(event_get_fd(&ssclient->udprelay), &msg, 0); @@ -142,10 +140,12 @@ static void ss_pkt_from_server(int fd, short what, void *_arg) size_t fwdlen; struct sockaddr_in udprelayaddr; int rc; + void * buff = client->instance->shared_buff; + void * buff2 = ss->buff; assert(fd == event_get_fd(&ssclient->udprelay)); - pktlen = red_recv_udp_pkt(fd, ss->buff, SHARED_BUFF_SIZE, &udprelayaddr, NULL); + pktlen = red_recv_udp_pkt(fd, buff, MAX_UDP_PACKET_SIZE, &udprelayaddr, NULL); if (pktlen == -1) return; @@ -153,13 +153,13 @@ static void ss_pkt_from_server(int fd, short what, void *_arg) redudp_log_error(client, LOG_ERR, "Shadowsocks UDP failed to initialize decryption context."); return; } - rc = ss_decrypt(&ss->d_ctx, ss->buff, pktlen, ss->buff2, &fwdlen); + rc = ss_decrypt(&ss->d_ctx, buff, pktlen, buff2, &fwdlen); enc_ctx_free(&ss->d_ctx); if (!rc) { redudp_log_error(client, LOG_DEBUG, "Can't decrypt packet, dropping it"); return; } - header = (ss_header_ipv4 *)ss->buff2; + header = (ss_header_ipv4 *)buff2; // We do not verify src address, but at least, we need to ensure address type is correct. if (header->addr_type != ss_addrtype_ipv4) { redudp_log_error(client, LOG_DEBUG, "Got address type #%u instead of expected #%u (IPv4).", @@ -178,7 +178,7 @@ static void ss_pkt_from_server(int fd, short what, void *_arg) return; } fwdlen -= sizeof(*header); - redudp_fwd_pkt_to_sender(client, ss->buff2 + sizeof(*header), fwdlen, &pktaddr); + redudp_fwd_pkt_to_sender(client, buff2 + sizeof(*header), fwdlen, &pktaddr); } static int ss_ready_to_fwd(struct redudp_client_t *client) @@ -247,15 +247,12 @@ static int ss_instance_init(struct redudp_instance_t *instance) red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1)), config->login); } - // Two buffers are allocated for each instance. One is for receiving plain - // data, one is for encryption/decrption. - ss->buff = malloc(SHARED_BUFF_SIZE); - ss->buff2 = malloc(SHARED_BUFF_SIZE); - if (!ss->buff || !ss->buff2) { + // An additional buffer is allocated for each instance for encryption/decrption. + ss->buff = malloc(MAX_UDP_PACKET_SIZE); + if (!ss->buff) { log_error(LOG_ERR, "Out of memory."); return -1; } - return 0; } @@ -266,10 +263,6 @@ static void ss_instance_fini(struct redudp_instance_t *instance) free(ss->buff); ss->buff = NULL; } - if (ss->buff2) { - free(ss->buff2); - ss->buff2 = NULL; - } } udprelay_subsys shadowsocks_udp_subsys = diff --git a/socks5-udp.c b/socks5-udp.c index d7c9c723..53ef38d8 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -143,15 +143,15 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) redudp_client *client = _arg; socks5_client *socks5client = (void*)(client + 1); union { - char buf[0xFFFF]; + char buf[MAX_UDP_PACKET_SIZE]; socks5_udp_preabmle header; - } pkt; + } * pkt = client->instance->shared_buff; ssize_t pktlen, fwdlen; struct sockaddr_in udprelayaddr; assert(fd == event_get_fd(&socks5client->udprelay)); - pktlen = red_recv_udp_pkt(fd, pkt.buf, sizeof(pkt.buf), &udprelayaddr, NULL); + pktlen = red_recv_udp_pkt(fd, pkt->buf, MAX_UDP_PACKET_SIZE, &udprelayaddr, NULL); if (pktlen == -1) return; @@ -162,27 +162,27 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) return; } - if (pkt.header.frag_no != 0) { + if (pkt->header.frag_no != 0) { // FIXME: does anybody need it? redudp_log_error(client, LOG_WARNING, "Got fragment #%u. Packet fragmentation is not supported!", - pkt.header.frag_no); + pkt->header.frag_no); return; } - if (pkt.header.addrtype != socks5_addrtype_ipv4) { + if (pkt->header.addrtype != socks5_addrtype_ipv4) { redudp_log_error(client, LOG_NOTICE, "Got address type #%u instead of expected #%u (IPv4).", - pkt.header.addrtype, socks5_addrtype_ipv4); + pkt->header.addrtype, socks5_addrtype_ipv4); return; } struct sockaddr_in pktaddr = { .sin_family = AF_INET, - .sin_addr = { pkt.header.ip.addr }, - .sin_port = pkt.header.ip.port, + .sin_addr = { pkt->header.ip.addr }, + .sin_port = pkt->header.ip.port, }; - fwdlen = pktlen - sizeof(pkt.header); - redudp_fwd_pkt_to_sender(client, pkt.buf + sizeof(pkt.header), fwdlen, &pktaddr); + fwdlen = pktlen - sizeof(pkt->header); + redudp_fwd_pkt_to_sender(client, pkt->buf + sizeof(pkt->header), fwdlen, &pktaddr); } From 65e518f298c82b4590941f75a6d2ffa48f9912ba Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 22 Apr 2016 21:42:25 +0800 Subject: [PATCH 084/192] Fix: crash if compiled with PolarSSL Verified with PolarSSL 1.3.14 --- encrypt.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/encrypt.c b/encrypt.c index 82260d19..ce04c6f4 100644 --- a/encrypt.c +++ b/encrypt.c @@ -487,8 +487,8 @@ const cipher_kt_t *get_cipher_type(int method) return NULL; } */ - const char *ciphername = supported_ciphers[method]; #if defined(USE_CRYPTO_OPENSSL) + const char *ciphername = supported_ciphers[method]; return EVP_get_cipherbyname(ciphername); #elif defined(USE_CRYPTO_POLARSSL) const char *polarname = supported_ciphers_polarssl[method]; @@ -528,7 +528,6 @@ static int cipher_context_init(const enc_info * info, cipher_ctx_t *ctx, int enc return; } */ - const char *ciphername = supported_ciphers[method]; #if defined(USE_CRYPTO_APPLECC) cipher_cc_t *cc = &ctx->cc; cc->cryptor = NULL; @@ -742,9 +741,9 @@ size_t ss_calc_buffer_size(struct enc_ctx * ctx, size_t ilen) return ilen; } if (ctx->init) - return ilen + cipher_get_block_size(cipher); + return ilen + cipher_get_block_size(&ctx->evp.evp); else - return cipher->iv_size + ilen + cipher_get_block_size(cipher); + return cipher->iv_size + ilen + cipher_get_block_size(&ctx->evp.evp); #endif } From 5ef53fe5f87808ce905c4a633c9c5bdedf7652ef Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 22 Apr 2016 22:08:47 +0800 Subject: [PATCH 085/192] Defer init of crypto contexts in shadowsocks until relay connected --- shadowsocks.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/shadowsocks.c b/shadowsocks.c index a03ac09d..f9222f8b 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -34,6 +34,8 @@ typedef enum ss_state_t { typedef struct ss_client_t { struct enc_ctx e_ctx; struct enc_ctx d_ctx; + short e_ctx_init; + short d_ctx_init; } ss_client; typedef struct ss_instance_t { @@ -60,21 +62,21 @@ int ss_is_valid_cred(const char *method, const char *password) static void ss_client_init(redsocks_client *client) { - ss_client *sclient = (void*)(client + 1); - ss_instance * ss = (ss_instance *)(client->instance+1); - client->state = ss_new; - if (enc_ctx_init(&ss->info, &sclient->e_ctx, 1)) - log_error(LOG_ERR, "Shadowsocks failed to initialize encryption context."); - if (enc_ctx_init(&ss->info, &sclient->d_ctx, 0)) - log_error(LOG_ERR, "Shadowsocks failed to initialize decryption context."); } static void ss_client_fini(redsocks_client *client) { ss_client *sclient = (void*)(client + 1); - enc_ctx_free(&sclient->e_ctx); - enc_ctx_free(&sclient->d_ctx); + + if (sclient->e_ctx_init) { + enc_ctx_free(&sclient->e_ctx); + sclient->e_ctx_init = 0; + } + if (sclient->d_ctx_init) { + enc_ctx_free(&sclient->d_ctx); + sclient->d_ctx_init = 0; + } } static void encrypt_mem(redsocks_client * client, @@ -272,6 +274,8 @@ static void ss_relay_readcb(struct bufferevent *buffev, void *_arg) static void ss_relay_connected(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; + ss_client *sclient = (void*)(client + 1); + ss_instance * ss = (ss_instance *)(client->instance+1); ss_header_ipv4 header; size_t len = 0; @@ -286,6 +290,21 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) } client->relay_connected = 1; + client->state = ss_connected; + + if (enc_ctx_init(&ss->info, &sclient->e_ctx, 1)) { + log_error(LOG_ERR, "Shadowsocks failed to initialize encryption context."); + redsocks_drop_client(client); + return; + } + sclient->e_ctx_init = 1; + if (enc_ctx_init(&ss->info, &sclient->d_ctx, 0)) { + log_error(LOG_ERR, "Shadowsocks failed to initialize decryption context."); + redsocks_drop_client(client); + return; + } + sclient->e_ctx_init = 1; + /* We do not need to detect timeouts any more. The two peers will handle it. */ bufferevent_set_timeouts(client->relay, NULL, NULL); @@ -311,8 +330,6 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) len += sizeof(header); encrypt_mem(client, (char *)&header, len, client->relay, 0); - client->state = ss_connected; - // Write any data received from client side to relay. if (evbuffer_get_length(bufferevent_get_input(client->client))) ss_relay_writecb(client->relay, client); @@ -332,7 +349,6 @@ static int ss_connect_relay(redsocks_client *client) &tv); if (!client->relay) { - redsocks_log_errno(client, LOG_ERR, "ss_connect_relay"); redsocks_drop_client(client); return -1; } From 104a76f94e81404204159f36e098b663ebfc5775 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 27 Apr 2016 11:00:57 +0800 Subject: [PATCH 086/192] Handle shutdown properly --- autoproxy.c | 6 +++--- redsocks.c | 23 +++++++++++++---------- redsocks.h | 2 +- utils.c | 8 ++++++-- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index b58d5d78..128654ed 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -48,7 +48,6 @@ typedef struct autoproxy_client_t { } autoproxy_client; -void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg); static int auto_retry_or_drop(redsocks_client * client); static void direct_relay_clientreadcb(struct bufferevent *from, void *_client); @@ -430,7 +429,7 @@ static int process_shutdown_on_write_2(redsocks_client *client, struct buffereve if (input_size == 0 || (input_size == aclient->data_sent && aclient->state == AUTOPROXY_CONNECTED)) { - redsocks_shutdown(client, to, SHUT_WR); + redsocks_shutdown(client, to, SHUT_WR, 0); return 1; } } @@ -470,6 +469,7 @@ static void auto_drop_relay(redsocks_client *client) { redsocks_log_error(client, LOG_DEBUG, "dropping relay only "); fd = bufferevent_getfd(client->relay); + bufferevent_disable(client->relay, EV_READ|EV_WRITE); bufferevent_free(client->relay); redsocks_close(fd); client->relay = NULL; @@ -650,7 +650,7 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) if (aclient->recv_timer_event && buffev == client->relay) auto_confirm_connection(client); - redsocks_shutdown(client, buffev, SHUT_RD); + redsocks_shutdown(client, buffev, SHUT_RD, 1); // Ensure the other party could send remaining data and SHUT_WR also if (buffev == client->client) { diff --git a/redsocks.c b/redsocks.c index 8cbde270..ba1845e1 100644 --- a/redsocks.c +++ b/redsocks.c @@ -288,7 +288,7 @@ int process_shutdown_on_write_(redsocks_client *client, struct bufferevent *from if ((from_evshut & EV_READ) && !(to_evshut & EV_WRITE) && evbuffer_get_length(bufferevent_get_input(from)) == 0) { - redsocks_shutdown(client, to, SHUT_WR); + redsocks_shutdown(client, to, SHUT_WR, 0); return 1; } return 0; @@ -394,17 +394,15 @@ void redsocks_drop_client(redsocks_client *client) if (client->client) { fd = bufferevent_getfd(client->client); + bufferevent_disable(client->client, EV_READ|EV_WRITE); bufferevent_free(client->client); - if (!(client->client_evshut & EV_WRITE)) - shutdown(fd, SHUT_WR); redsocks_close(fd); } if (client->relay) { fd = bufferevent_getfd(client->relay); + bufferevent_disable(client->relay, EV_READ|EV_WRITE); bufferevent_free(client->relay); - if (!(client->relay_evshut & EV_WRITE)) - shutdown(fd, SHUT_WR); redsocks_close(fd); } @@ -412,7 +410,7 @@ void redsocks_drop_client(redsocks_client *client) free(client); } -void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how) +void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how, int pseudo) { short evhow = 0; char *strev, *strhow = NULL, *strevhow = NULL; @@ -448,9 +446,14 @@ void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int // if EV_WRITE is already shut and we're going to shutdown read then // we're either going to abort data flow (bad behaviour) or confirm EOF // and in this case socket is already SHUT_RD'ed - if ( !(how == SHUT_RD && (*pevshut & EV_WRITE)) ) - if (shutdown(bufferevent_getfd(buffev), how) != 0) - redsocks_log_errno(client, LOG_ERR, "shutdown(%s, %s)", strev, strhow); + if (!pseudo) + if ( !(how == SHUT_RD && (*pevshut & EV_WRITE)) ) + if (shutdown(bufferevent_getfd(buffev), how) != 0) { + redsocks_log_errno(client, LOG_ERR, "shutdown(%s, %s)", strev, strhow); + // In case of 'Transport endpoint is not connected', shutdown as SHUT_RDWR. + if (errno == ENOTCONN) + evhow = EV_READ|EV_WRITE; + } *pevshut |= evhow; @@ -485,7 +488,7 @@ void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg) event_fmt(what)); if (what == (BEV_EVENT_READING|BEV_EVENT_EOF)) { - redsocks_shutdown(client, buffev, SHUT_RD); + redsocks_shutdown(client, buffev, SHUT_RD, 1); // Ensure the other party could send remaining data and SHUT_WR also if (buffev == client->client) { diff --git a/redsocks.h b/redsocks.h index ca439aa5..d26b7574 100644 --- a/redsocks.h +++ b/redsocks.h @@ -78,7 +78,7 @@ void redsocks_touch_client(redsocks_client *client); int redsocks_connect_relay(redsocks_client *client); int redsocks_start_relay(redsocks_client *client); void redsocks_dump_client(redsocks_client * client, int loglevel); -void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how); +void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how, int pseudo); typedef int (*size_comparator)(size_t a, size_t b); int sizes_equal(size_t a, size_t b); diff --git a/utils.c b/utils.c index f8b1c341..93454fb2 100644 --- a/utils.c +++ b/utils.c @@ -180,8 +180,10 @@ struct bufferevent* red_connect_relay_if(const char *ifname, return retval; fail: - if (retval) + if (retval){ + bufferevent_disable(retval, EV_READ|EV_WRITE); bufferevent_free(retval); + } if (relay_fd != -1) redsocks_close(relay_fd); return NULL; @@ -257,8 +259,10 @@ struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, return retval; fail: - if (retval) + if (retval) { + bufferevent_disable(retval, EV_READ|EV_WRITE); bufferevent_free(retval); + } if (relay_fd != -1) redsocks_close(relay_fd); return NULL; From fa332981ce9a74c217d670cb91964c2cd0137710 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 5 May 2016 17:47:49 +0800 Subject: [PATCH 087/192] Apply KEEPALIVE to all TCP connections --- base.c | 2 ++ redsocks.c | 6 +----- utils.c | 14 -------------- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/base.c b/base.c index 1b815586..0edc8db1 100644 --- a/base.c +++ b/base.c @@ -251,9 +251,11 @@ int apply_tcp_keepalive(int fd) { struct { int level, option, value; } opt[] = { { SOL_SOCKET, SO_KEEPALIVE, 1 }, +#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPCNT) && defined(TCP_KEEPINTVL) { IPPROTO_TCP, TCP_KEEPIDLE, instance.tcp_keepalive_time }, { IPPROTO_TCP, TCP_KEEPCNT, instance.tcp_keepalive_probes }, { IPPROTO_TCP, TCP_KEEPINTVL, instance.tcp_keepalive_intvl }, +#endif }; for (int i = 0; i < SIZEOF_ARRAY(opt); ++i) { if (opt[i].value) { diff --git a/redsocks.c b/redsocks.c index ba1845e1..3af0ae59 100644 --- a/redsocks.c +++ b/redsocks.c @@ -703,7 +703,6 @@ static void redsocks_accept_client(int fd, short what, void *_arg) struct sockaddr_in myaddr; struct sockaddr_in destaddr; socklen_t addrlen = sizeof(clientaddr); - int on = 1; int client_fd = -1; int error; @@ -749,11 +748,8 @@ static void redsocks_accept_client(int fd, short what, void *_arg) goto fail; } - error = setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); - if (error) { - log_errno(LOG_WARNING, "setsockopt"); + if (apply_tcp_keepalive(client_fd)) goto fail; - } // everything seems to be ok, let's allocate some memory if (self->config.autoproxy) diff --git a/utils.c b/utils.c index 93454fb2..6ecc6852 100644 --- a/utils.c +++ b/utils.c @@ -123,7 +123,6 @@ struct bufferevent* red_connect_relay_if(const char *ifname, void *cbarg) { struct bufferevent *retval = NULL; - int on = 1; int relay_fd = -1; int error; @@ -147,12 +146,6 @@ struct bufferevent* red_connect_relay_if(const char *ifname, goto fail; } - error = setsockopt(relay_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); - if (error) { - log_errno(LOG_WARNING, "setsockopt"); - goto fail; - } - retval = bufferevent_socket_new(get_event_base(), relay_fd, 0); if (!retval) { log_errno(LOG_ERR, "bufferevent_socket_new"); @@ -208,7 +201,6 @@ struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, const struct timeval *timeout_write) { struct bufferevent *retval = NULL; - int on = 1; int relay_fd = -1; int error; @@ -224,12 +216,6 @@ struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, goto fail; } - error = setsockopt(relay_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); - if (error) { - log_errno(LOG_WARNING, "setsockopt"); - goto fail; - } - retval = bufferevent_socket_new(get_event_base(), relay_fd, 0); if (!retval) { log_errno(LOG_ERR, "bufferevent_socket_new"); From 8b9228e12f34bf55c5b5f91d15753419a0fe1288 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 28 Jul 2015 12:28:55 +0800 Subject: [PATCH 088/192] Wait for network ready before further initializations --- main.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index e3eab1f2..10c9cc98 100644 --- a/main.c +++ b/main.c @@ -82,6 +82,33 @@ struct event_base * get_event_base() return g_event_base; } +static void wait_for_network() +{ + struct evutil_addrinfo hints; + struct evutil_addrinfo *answer = NULL; + int err; + + /* Build the hints to tell getaddrinfo how to act. */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* v4 or v6 is fine. */ + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; /* We want a TCP socket */ + /* Only return addresses we can use. */ + hints.ai_flags = EVUTIL_AI_ADDRCONFIG; + + /* Look up the hostname. */ + do { + err = evutil_getaddrinfo("www.google.com", NULL, &hints, &answer); + if (err) + sleep(2); + /* If there was no error, we should have at least one answer. */ + if (answer) { + evutil_freeaddrinfo(answer); + answer = NULL; + } + } while (err != 0); +} + int main(int argc, char **argv) { int error; @@ -92,13 +119,17 @@ int main(int argc, char **argv) bool conftest = false; int opt; int i; + bool wait = false; evutil_secure_rng_init(); - while ((opt = getopt(argc, argv, "h?vtc:p:")) != -1) { + while ((opt = getopt(argc, argv, "h?wvtc:p:")) != -1) { switch (opt) { case 't': conftest = true; break; + case 'w': + wait = true; + break; case 'c': confname = optarg; break; @@ -110,8 +141,9 @@ int main(int argc, char **argv) return EXIT_SUCCESS; default: printf( - "Usage: %s [-?hvt] [-c config] [-p pidfile]\n" + "Usage: %s [-?hwvt] [-c config] [-p pidfile]\n" " -h, -? this message\n" + " -w wait util network ready\n" " -v print version\n" " -t test config syntax\n" " -p write pid to pidfile\n", @@ -120,6 +152,10 @@ int main(int argc, char **argv) } } + // Wait for network ready before further initializations so that + // parser can resolve domain names. + if (wait) + wait_for_network(); FILE *f = fopen(confname, "r"); if (!f) { From 151f09cf5ad9289c871749db41f737ac16fe40c4 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 13 Sep 2016 13:37:18 +0800 Subject: [PATCH 089/192] Add TPROXY support to redsocks --- redsocks.c | 4 ++++ redudp.c | 14 ++------------ utils.c | 8 ++++++++ utils.h | 1 + 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/redsocks.c b/redsocks.c index 3af0ae59..d856c043 100644 --- a/redsocks.c +++ b/redsocks.c @@ -952,6 +952,10 @@ static int redsocks_init_instance(redsocks_instance *instance) goto fail; } + // iptables TPROXY target does not send packets to non-transparent sockets + if (make_socket_transparent(fd)) + log_error(LOG_WARNING, "Continue without TPROXY support"); + error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); if (error) { log_errno(LOG_ERR, "bind"); diff --git a/redudp.c b/redudp.c index 85e3f6bb..3764861e 100644 --- a/redudp.c +++ b/redudp.c @@ -50,7 +50,6 @@ static char shared_buff[MAX_UDP_PACKET_SIZE];// max size of UDP packet is less t static void redudp_fini_instance(redudp_instance *instance); static int redudp_fini(); -static int redudp_transparent(int fd); struct bound_udp4_key { struct in_addr sin_addr; @@ -120,7 +119,7 @@ static int bound_udp4_get(const struct sockaddr_in *addr) goto fail; } - if (0 != redudp_transparent(node->fd)) + if (0 != make_socket_transparent(node->fd)) goto fail; if (evutil_make_listen_socket_reuseable(node->fd)) { @@ -214,15 +213,6 @@ static void bound_udp4_action(const void *nodep, const VISIT which, const int de } } -static int redudp_transparent(int fd) -{ - int on = 1; - int error = setsockopt(fd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); - if (error) - log_errno(LOG_ERR, "setsockopt(..., SOL_IP, IP_TRANSPARENT)"); - return error; -} - static int do_tproxy(redudp_instance* instance) { return instance->config.destaddr.sin_addr.s_addr == 0; @@ -564,7 +554,7 @@ static int redudp_init_instance(redudp_instance *instance) if (do_tproxy(instance)) { int on = 1; // iptables TPROXY target does not send packets to non-transparent sockets - if (0 != redudp_transparent(fd)) + if (0 != make_socket_transparent(fd)) goto fail; error = setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &on, sizeof(on)); diff --git a/utils.c b/utils.c index 6ecc6852..64770cf5 100644 --- a/utils.c +++ b/utils.c @@ -365,5 +365,13 @@ size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, s return written; } +int make_socket_transparent(int fd) +{ + int on = 1; + int error = setsockopt(fd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); + if (error) + log_errno(LOG_ERR, "setsockopt(..., SOL_IP, IP_TRANSPARENT)"); + return error; +} /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ diff --git a/utils.h b/utils.h index a77bff9e..28b12aec 100644 --- a/utils.h +++ b/utils.h @@ -63,6 +63,7 @@ int red_is_socket_connected_ok(struct bufferevent *buffev); int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *fromaddr, struct sockaddr_in *toaddr); size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, size_t skip); +int make_socket_transparent(int fd); #define event_fmt_str "%s|%s|%s|%s|%s|%s|0x%x" #define event_fmt(what) \ From 6acef13e03c87da64d00b0e3efa67cef64436a30 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 13 Sep 2016 15:08:06 +0800 Subject: [PATCH 090/192] Add FreeBSD Compatibility --- Makefile | 12 ++++++++++-- base.c | 8 +++++++- libc-compat.h | 11 +++++++++++ utils.c | 7 +++++-- utils.h | 1 + 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 532bb51d..4d13ff4f 100644 --- a/Makefile +++ b/Makefile @@ -6,10 +6,15 @@ CONF := config.h DEPS := .depend OUT := redsocks2 VERSION := 0.65 +OS := $(shell uname) LIBS := -levent CFLAGS +=-fPIC -O3 -override CFLAGS += -std=c99 -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_DEFAULT_SOURCE -Wall +override CFLAGS += -D_BSD_SOURCE -D_DEFAULT_SOURCE -Wall +ifeq ($(OS), Linux) +override CFLAGS += -std=c99 -D_XOPEN_SOURCE=600 +endif + #LDFLAGS += -fwhole-program ifdef USE_CRYPTO_POLARSSL override LIBS += -lpolarssl @@ -35,10 +40,13 @@ tags: *.c *.h ctags -R $(CONF): - @case `uname` in \ + @case $(OS) in \ Linux*) \ echo "#define USE_IPTABLES" >$(CONF) \ ;; \ + FreeBSD) \ + echo "#define USE_PF" >$(CONF) \ + ;; \ OpenBSD) \ echo "#define USE_PF" >$(CONF) \ ;; \ diff --git a/base.c b/base.c index 0edc8db1..65963081 100644 --- a/base.c +++ b/base.c @@ -32,12 +32,18 @@ # include # include #endif + #if defined USE_PF # include # include # include #endif -# include +#ifdef __FreeBSD__ +# include +# include +#endif + +#include #include "log.h" #include "main.h" #include "parser.h" diff --git a/libc-compat.h b/libc-compat.h index adcf63bc..7e1eb878 100644 --- a/libc-compat.h +++ b/libc-compat.h @@ -22,4 +22,15 @@ # define IP_TRANSPARENT 19 #endif +#ifndef SOL_IP +# warning Using hardcoded value for SOL_IP as libc headers do not define it. +# define SOL_IP IPPROTO_IP +#endif + +#ifdef __FreeBSD__ +#ifndef INADDR_LOOPBACK +# warning Using hardcoded value for INADDR_LOOPBACK for FreeBSD. +# define INADDR_LOOPBACK 0x7F000001 +#endif +#endif #endif // 67C91670_FCCB_4855_BDF7_609F1EECB8B4 diff --git a/utils.c b/utils.c index 64770cf5..36064380 100644 --- a/utils.c +++ b/utils.c @@ -22,6 +22,7 @@ #include #include #include +#include "config.h" #include "main.h" #include "log.h" #include "base.h" @@ -131,15 +132,17 @@ struct bufferevent* red_connect_relay_if(const char *ifname, log_errno(LOG_ERR, "socket"); goto fail; } - if (ifname) { +#ifdef USE_PF // BSD + error = setsockopt(relay_fd, SOL_SOCKET, IP_RECVIF, ifname, strlen(ifname)); +#else // Linux error = setsockopt(relay_fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); +#endif if (error) { log_errno(LOG_ERR, "bind"); goto fail; } } - error = evutil_make_socket_nonblocking(relay_fd); if (error) { log_errno(LOG_ERR, "evutil_make_socket_nonblocking"); diff --git a/utils.h b/utils.h index 28b12aec..44fc9f2f 100644 --- a/utils.h +++ b/utils.h @@ -4,6 +4,7 @@ #include #include #include +#include struct sockaddr_in; From c9ccdfaf112e2957fa143bfb3669f30c548d16d4 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 3 Oct 2016 13:56:23 +0800 Subject: [PATCH 091/192] Fix: 'tcpdns' does not work sometimes See RFC2065 6.1 The AD and CD Header Bits for details. --- tcpdns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcpdns.c b/tcpdns.c index 776fce32..72b6d2ac 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -43,7 +43,7 @@ static int tcpdns_fini(); #define DNS_QR 0x80 #define DNS_TC 0x02 -#define DNS_Z 0x70 +#define DNS_Z 0x40 #define DNS_RC_MASK 0x0F #define DNS_RC_NOERROR 0 From 79e7b99843d5266313b3cd6007e7ec87a668fbf8 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 14 Oct 2015 10:04:20 +0800 Subject: [PATCH 092/192] Allow specify outgoing interface for autoproxy --- autoproxy.c | 18 ++++---- direct.c | 13 ++---- redsocks.c | 5 ++- redsocks.conf.example | 11 +++-- shadowsocks-udp.c | 3 +- shadowsocks.c | 3 +- socks5-udp.c | 4 +- tcpdns.c | 2 +- utils.c | 96 ++++++++++++++++--------------------------- utils.h | 13 ++++-- 10 files changed, 75 insertions(+), 93 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index 128654ed..8c00ea61 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -59,6 +59,7 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) typedef struct autoproxy_config_t { list_head list; // Make it a list to support multiple configurations + char * interface; // interface to be used for relay uint16_t quick_connect_timeout; uint16_t no_quick_check_seconds; } autoproxy_config; @@ -72,6 +73,7 @@ static list_head configs = LIST_HEAD_INIT(configs); static parser_entry autoproxy_entries[] = { + { .key = "interface", .type = pt_pchar }, { .key = "quick_connect_timeout", .type = pt_uint16 }, { .key = "no_quick_check_seconds", .type = pt_uint16 }, { } @@ -91,6 +93,7 @@ static int autoproxy_onenter(parser_section *section) for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = + (strcmp(entry->key, "interface") == 0) ? (void*)&config->interface : (strcmp(entry->key, "quick_connect_timeout") == 0) ? (void*)&config->quick_connect_timeout: (strcmp(entry->key, "no_quick_check_seconds") == 0) ? (void*)&config->no_quick_check_seconds: NULL; @@ -673,10 +676,8 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) static int auto_connect_relay(redsocks_client *client) { autoproxy_client * aclient = get_autoproxy_client(client); - autoproxy_config * config = NULL; - struct timeval tv; - tv.tv_sec = client->instance->config.timeout; - tv.tv_usec = 0; + autoproxy_config * config = get_config(client); + struct timeval tv = {client->instance->config.timeout, 0}; time_t * acc_time = NULL; time_t now = redsocks_time(NULL); @@ -686,7 +687,6 @@ static int auto_connect_relay(redsocks_client *client) if (acc_time) { redsocks_log_error(client, LOG_DEBUG, "Found dest IP in cache"); - config = get_config(client); // No quick check when the time passed since IP is added to cache is // less than NO_CHECK_SECONDS. Just let it go via proxy. if (config->no_quick_check_seconds == 0 @@ -706,14 +706,12 @@ static int auto_connect_relay(redsocks_client *client) aclient->quick_check = 1; } /* connect to target directly without going through proxy */ - client->relay = red_connect_relay2(&client->destaddr, + client->relay = red_connect_relay(config->interface, &client->destaddr, NULL, auto_relay_connected, auto_event_error, client, &tv); - if (!client->relay) { - redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); - redsocks_drop_client(client); - return -1; + // Failed to connect to destination directly, try again via proxy. + return auto_retry(client, 0); } } else diff --git a/direct.c b/direct.c index 0f83dbdd..4e6b95b5 100644 --- a/direct.c +++ b/direct.c @@ -62,16 +62,11 @@ static void direct_write_cb(struct bufferevent *buffev, void *_arg) static int direct_connect_relay(redsocks_client *client) { char * interface = client->instance->config.interface; + struct timeval tv = {client->instance->config.timeout, 0}; + // Allowing binding relay socket to specified IP for outgoing connections - if (interface && strlen(interface)) - { - client->relay = red_connect_relay_if(interface, - &client->destaddr, NULL, - redsocks_relay_connected, redsocks_event_error, client); - } - else - client->relay = red_connect_relay(&client->destaddr, NULL, - redsocks_relay_connected, redsocks_event_error, client); + client->relay = red_connect_relay(interface, &client->destaddr, NULL, + redsocks_relay_connected, redsocks_event_error, client, &tv); if (!client->relay) { redsocks_log_errno(client, LOG_ERR, "red_connect_relay"); diff --git a/redsocks.c b/redsocks.c index d856c043..dd1ca294 100644 --- a/redsocks.c +++ b/redsocks.c @@ -640,11 +640,13 @@ void redsocks_relay_connected(struct bufferevent *buffev, void *_arg) int redsocks_connect_relay(redsocks_client *client) { + char * interface = client->instance->config.interface; struct timeval tv; tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; - client->relay = red_connect_relay2(&client->instance->config.relayaddr, + // Allowing binding relay socket to specified IP for outgoing connections + client->relay = red_connect_relay(interface, &client->instance->config.relayaddr, NULL, redsocks_relay_connected, redsocks_event_error, client, &tv); @@ -968,6 +970,7 @@ static int redsocks_init_instance(redsocks_instance *instance) goto fail; } + apply_tcp_fastopen(fd); error = listen(fd, instance->config.listenq); if (error) { log_errno(LOG_ERR, "listen"); diff --git a/redsocks.conf.example b/redsocks.conf.example index 142dc100..48a40e71 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -73,9 +73,9 @@ redsocks { // New types: direct, shadowsocks type = socks5; - // Specify interface for outgoing connections when 'direct' type - // is used. This is useful when you have multiple connections to - // internet or you have VPN connections. + // Specify interface for outgoing connections. + // This is useful when you have multiple connections to + // internet or when you have VPN connections. // interface = tun0; // Change this parameter to 1 if you want auto proxy feature. @@ -142,6 +142,11 @@ tcpdns { } autoproxy { + // Specify interface for outgoing connections. + // This is useful when you have multiple connections to + // internet or when you have VPN connections. + // interface = wlan0; + no_quick_check_seconds = 60; // Directly relay traffic to proxy if an IP // is found blocked in cache and it has been // added into cache no earlier than this diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index 9db7de7f..478a238b 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -64,9 +64,10 @@ static void ss_client_fini(redudp_client *client) { ss_client *ssclient = (void*)(client + 1); if (event_initialized(&ssclient->udprelay)) { - close(event_get_fd(&ssclient->udprelay)); + int fd = event_get_fd(&ssclient->udprelay); if (event_del(&ssclient->udprelay) == -1) redudp_log_errno(client, LOG_ERR, "event_del"); + close(fd); } } diff --git a/shadowsocks.c b/shadowsocks.c index f9222f8b..b81e90c1 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -340,11 +340,12 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) static int ss_connect_relay(redsocks_client *client) { + char * interface = client->instance->config.interface; struct timeval tv; tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; - client->relay = red_connect_relay2(&client->instance->config.relayaddr, + client->relay = red_connect_relay(interface, &client->instance->config.relayaddr, NULL, ss_relay_connected, redsocks_event_error, client, &tv); diff --git a/socks5-udp.c b/socks5-udp.c index 53ef38d8..272265c3 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -385,8 +385,8 @@ static void socks5_relay_error(struct bufferevent *buffev, short what, void *_ar static void socks5_connect_relay(redudp_client *client) { socks5_client *socks5client = (void*)(client + 1); - socks5client->relay = red_connect_relay(&client->instance->config.relayaddr, NULL, - socks5_relay_connected, socks5_relay_error, client); + socks5client->relay = red_connect_relay(NULL, &client->instance->config.relayaddr, NULL, + socks5_relay_connected, socks5_relay_error, client, NULL); if (!socks5client->relay) redudp_drop_client(client); } diff --git a/tcpdns.c b/tcpdns.c index 72b6d2ac..1d85c0c5 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -318,7 +318,7 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) return; } /* connect to target directly without going through proxy */ - req->resolver = red_connect_relay2(destaddr, + req->resolver = red_connect_relay(NULL, destaddr, tcpdns_readcb, tcpdns_connected, tcpdns_event_error, req, &tv); if (req->resolver) diff --git a/utils.c b/utils.c index 36064380..5c3323db 100644 --- a/utils.c +++ b/utils.c @@ -116,8 +116,7 @@ char *redsocks_evbuffer_readline(struct evbuffer *buf) #endif } -struct bufferevent* red_connect_relay_if(const char *ifname, - struct sockaddr_in *addr, +struct bufferevent* red_prepare_relay(const char *ifname, evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, @@ -132,14 +131,14 @@ struct bufferevent* red_connect_relay_if(const char *ifname, log_errno(LOG_ERR, "socket"); goto fail; } - if (ifname) { + if (ifname && strlen(ifname)) { #ifdef USE_PF // BSD error = setsockopt(relay_fd, SOL_SOCKET, IP_RECVIF, ifname, strlen(ifname)); #else // Linux error = setsockopt(relay_fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); #endif if (error) { - log_errno(LOG_ERR, "bind"); + log_errno(LOG_ERR, "setsockopt"); goto fail; } } @@ -165,14 +164,6 @@ struct bufferevent* red_connect_relay_if(const char *ifname, if (apply_tcp_keepalive(relay_fd)) goto fail; -// error = bufferevent_socket_connect(retval, (struct sockaddr*)addr, sizeof(*addr)); -// if (error) { - error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); - if (error && errno != EINPROGRESS) { - log_errno(LOG_NOTICE, "connect"); - goto fail; - } - return retval; fail: @@ -185,18 +176,8 @@ struct bufferevent* red_connect_relay_if(const char *ifname, return NULL; } - -struct bufferevent* red_connect_relay(struct sockaddr_in *addr, - evbuffercb readcb, - evbuffercb writecb, - everrorcb errorcb, - void *cbarg) -{ - return red_connect_relay_if(NULL, addr, readcb, writecb, errorcb, cbarg); -} - - -struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, +struct bufferevent* red_connect_relay(const char *ifname, + struct sockaddr_in *addr, evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, @@ -207,44 +188,20 @@ struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, int relay_fd = -1; int error; - relay_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (relay_fd == -1) { - log_errno(LOG_ERR, "socket"); - goto fail; - } - - error = evutil_make_socket_nonblocking(relay_fd); - if (error) { - log_errno(LOG_ERR, "evutil_make_socket_nonblocking"); - goto fail; - } - - retval = bufferevent_socket_new(get_event_base(), relay_fd, 0); - if (!retval) { - log_errno(LOG_ERR, "bufferevent_socket_new"); - goto fail; - } - - bufferevent_setcb(retval, readcb, writecb, errorcb, cbarg); - - error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... - if (error) { - log_errno(LOG_ERR, "bufferevent_enable"); - goto fail; - } - bufferevent_set_timeouts(retval, NULL, timeout_write); - - if (apply_tcp_keepalive(relay_fd)) - goto fail; - -// error = bufferevent_socket_connect(retval, (struct sockaddr*)addr, sizeof(*addr)); -// if (error) { - error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); - if (error && errno != EINPROGRESS) { - log_errno(LOG_NOTICE, "connect"); - goto fail; + retval = red_prepare_relay(ifname, readcb, writecb, errorcb, cbarg); + if (retval) { + relay_fd = bufferevent_getfd(retval); + if (timeout_write) + bufferevent_set_timeouts(retval, NULL, timeout_write); + + // error = bufferevent_socket_connect(retval, (struct sockaddr*)addr, sizeof(*addr)); + // if (error) { + error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); + if (error && errno != EINPROGRESS) { + log_errno(LOG_NOTICE, "connect"); + goto fail; + } } - return retval; fail: @@ -377,4 +334,21 @@ int make_socket_transparent(int fd) return error; } +int apply_tcp_fastopen(int fd) +{ +#ifdef TCP_FASTOPEN +#ifdef __APPLE__ + int opt = 1; +#else + int opt = 5; +#endif + int rc = setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &opt, sizeof(opt)); + if (rc == -1) + log_errno(LOG_ERR, "setsockopt"); + return rc; +#else + return -1; +#endif +} + /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ diff --git a/utils.h b/utils.h index 44fc9f2f..1917c573 100644 --- a/utils.h +++ b/utils.h @@ -51,20 +51,25 @@ struct sockaddr_in; uint32_t red_randui32(); time_t redsocks_time(time_t *t); char *redsocks_evbuffer_readline(struct evbuffer *buf); -struct bufferevent* red_connect_relay_if(const char *ifname, - struct sockaddr_in *addr, +struct bufferevent* red_prepare_relay(const char *ifname, evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg); -struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg); -struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg, const struct timeval *timeout_write); +struct bufferevent* red_connect_relay(const char *ifname, + struct sockaddr_in *addr, + evbuffercb readcb, + evbuffercb writecb, + everrorcb errorcb, + void *cbarg, + const struct timeval *timeout_write); int red_socket_geterrno(struct bufferevent *buffev); int red_is_socket_connected_ok(struct bufferevent *buffev); int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *fromaddr, struct sockaddr_in *toaddr); size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, size_t skip); int make_socket_transparent(int fd); +int apply_tcp_fastopen(int fd); #define event_fmt_str "%s|%s|%s|%s|%s|%s|0x%x" #define event_fmt(what) \ From 94c8a8ce25960b26dd5fc8763301b883a70e85c4 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 13 Jun 2016 17:50:50 +0800 Subject: [PATCH 093/192] Fix: SEGV if redsocks2 is built with libevent 2.1+ --- http-connect.c | 9 +++++---- http-relay.c | 13 +++++++------ redsocks.c | 4 ++-- socks4.c | 2 +- socks5-udp.c | 6 +++--- socks5.c | 10 +++++----- utils.c | 19 +++++++++---------- 7 files changed, 32 insertions(+), 31 deletions(-) diff --git a/http-connect.c b/http-connect.c index 8a2fbcee..ec559c9a 100644 --- a/http-connect.c +++ b/http-connect.c @@ -79,14 +79,15 @@ void httpc_read_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; int dropped = 0; + struct evbuffer * evbinput = bufferevent_get_input(buffev); assert(client->state >= httpc_request_sent); redsocks_touch_client(client); if (client->state == httpc_request_sent) { - size_t len = EVBUFFER_LENGTH(buffev->input); - char *line = redsocks_evbuffer_readline(buffev->input); + size_t len = evbuffer_get_length(evbinput); + char *line = redsocks_evbuffer_readline(evbinput); if (line) { unsigned int code; if (sscanf(line, "HTTP/%*u.%*u %u", &code) == 1) { // 1 == one _assigned_ match @@ -104,7 +105,7 @@ void httpc_read_cb(struct bufferevent *buffev, void *_arg) dropped = 1; } else { - char *auth_request = get_auth_request_header(buffev->input); + char *auth_request = get_auth_request_header(evbinput); if (!auth_request) { redsocks_log_error(client, LOG_NOTICE, "403 found, but no proxy auth challenge"); @@ -164,7 +165,7 @@ void httpc_read_cb(struct bufferevent *buffev, void *_arg) return; while (client->state == httpc_reply_came) { - char *line = redsocks_evbuffer_readline(buffev->input); + char *line = redsocks_evbuffer_readline(evbinput); if (line) { if (strlen(line) == 0) { client->state = httpc_headers_skipped; diff --git a/http-relay.c b/http-relay.c index 8c2e8e27..cab7208b 100644 --- a/http-relay.c +++ b/http-relay.c @@ -147,6 +147,7 @@ static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg) redsocks_client *client = _arg; httpr_client *httpr = (void*)(client + 1); int dropped = 0; + struct evbuffer * evbinput = bufferevent_get_input(buffev); assert(client->state >= httpr_request_sent); @@ -156,8 +157,8 @@ static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg) httpr_buffer_init(&httpr->relay_buffer); if (client->state == httpr_request_sent) { - size_t len = EVBUFFER_LENGTH(buffev->input); - char *line = redsocks_evbuffer_readline(buffev->input); + size_t len = evbuffer_get_length(evbinput); + char *line = redsocks_evbuffer_readline(evbinput); if (line) { httpr_buffer_append(&httpr->relay_buffer, line, strlen(line)); httpr_buffer_append(&httpr->relay_buffer, "\r\n", 2); @@ -177,7 +178,7 @@ static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg) dropped = 1; } else { - char *auth_request = get_auth_request_header(buffev->input); + char *auth_request = get_auth_request_header(evbinput); if (!auth_request) { redsocks_log_error(client, LOG_NOTICE, "403 found, but no proxy auth challenge"); @@ -238,7 +239,7 @@ static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg) return; while (client->state == httpr_reply_came) { - char *line = redsocks_evbuffer_readline(buffev->input); + char *line = redsocks_evbuffer_readline(evbinput); if (line) { httpr_buffer_append(&httpr->relay_buffer, line, strlen(line)); httpr_buffer_append(&httpr->relay_buffer, "\r\n", 2); @@ -456,7 +457,7 @@ static void httpr_client_read_content(struct bufferevent *buffev, redsocks_clien } int error; while (true) { - error = evbuffer_remove(buffev->input, post_buffer, post_buffer_len); + error = evbuffer_remove(bufferevent_get_input(buffev), post_buffer, post_buffer_len); if (error < 0) { free(post_buffer); redsocks_log_error(client, LOG_ERR, "evbuffer_remove"); @@ -494,7 +495,7 @@ static void httpr_client_read_cb(struct bufferevent *buffev, void *_arg) char *line = NULL; int connect_relay = 0; - while (!connect_relay && (line = redsocks_evbuffer_readline(buffev->input))) { + while (!connect_relay && (line = redsocks_evbuffer_readline(bufferevent_get_input(buffev)))) { int skip_line = 0; int do_drop = 0; diff --git a/redsocks.c b/redsocks.c index dd1ca294..218a0ddc 100644 --- a/redsocks.c +++ b/redsocks.c @@ -260,7 +260,7 @@ static void redsocks_relay_readcb(redsocks_client *client, struct bufferevent *f redsocks_log_error(client, LOG_DEBUG, "RCB %s, in: %u", from == client->client?"client":"relay", evbuffer_get_length(bufferevent_get_input(from))); - if (evbuffer_get_length(to->output) < to->wm_write.high) { + if (evbuffer_get_length(bufferevent_get_output(to)) < to->wm_write.high) { if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); if (bufferevent_enable(from, EV_READ) == -1) @@ -301,7 +301,7 @@ static void redsocks_relay_writecb(redsocks_client *client, struct bufferevent * if (process_shutdown_on_write_(client, from, to)) return; - if (evbuffer_get_length(to->output) < to->wm_write.high) { + if (evbuffer_get_length(bufferevent_get_output(to)) < to->wm_write.high) { if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); if (!(from_evshut & EV_READ) && bufferevent_enable(from, EV_READ) == -1) diff --git a/socks4.c b/socks4.c index 865cc77e..ce294d7b 100644 --- a/socks4.c +++ b/socks4.c @@ -72,7 +72,7 @@ static void socks4_read_cb(struct bufferevent *buffev, void *_arg) if (client->state == socks4_request_sent) { socks4_reply reply; - if (redsocks_read_expected(client, buffev->input, &reply, sizes_greater_equal, sizeof(reply)) < 0) + if (redsocks_read_expected(client, bufferevent_get_input(buffev), &reply, sizes_greater_equal, sizeof(reply)) < 0) return; client->state = socks4_reply_came; diff --git a/socks5-udp.c b/socks5-udp.c index 272265c3..2c0d13d6 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -191,7 +191,7 @@ static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg) redudp_client *client = _arg; socks5_client *socks5client = (void*)(client + 1); socks5_expected_assoc_reply reply; - int read = evbuffer_remove(buffev->input, &reply, sizeof(reply)); + int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply)); int fd = -1; int error; redudp_log_error(client, LOG_DEBUG, ""); @@ -266,7 +266,7 @@ static void socks5_read_auth_reply(struct bufferevent *buffev, void *_arg) redudp_client *client = _arg; socks5_client *socks5client = (void*)(client + 1); socks5_auth_reply reply; - int read = evbuffer_remove(buffev->input, &reply, sizeof(reply)); + int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply)); int error; redudp_log_error(client, LOG_DEBUG, ""); @@ -303,7 +303,7 @@ static void socks5_read_auth_methods(struct bufferevent *buffev, void *_arg) socks5_client *socks5client = (void*)(client + 1); int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password); socks5_method_reply reply; - int read = evbuffer_remove(buffev->input, &reply, sizeof(reply)); + int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply)); const char *error = NULL; int ierror = 0; redudp_log_error(client, LOG_DEBUG, ""); diff --git a/socks5.c b/socks5.c index 4b5456d4..69638dab 100644 --- a/socks5.c +++ b/socks5.c @@ -182,7 +182,7 @@ static void socks5_read_auth_methods(struct bufferevent *buffev, redsocks_client socks5_method_reply reply; const char *error = NULL; - if (redsocks_read_expected(client, buffev->input, &reply, sizes_equal, sizeof(reply)) < 0) + if (redsocks_read_expected(client, bufferevent_get_input(buffev), &reply, sizes_equal, sizeof(reply)) < 0) return; error = socks5_is_known_auth_method(&reply, socks5->do_password); @@ -208,7 +208,7 @@ static void socks5_read_auth_reply(struct bufferevent *buffev, redsocks_client * { socks5_auth_reply reply; - if (redsocks_read_expected(client, buffev->input, &reply, sizes_equal, sizeof(reply)) < 0) + if (redsocks_read_expected(client, bufferevent_get_input(buffev), &reply, sizes_equal, sizeof(reply)) < 0) return; if (reply.ver != socks5_password_ver) { @@ -228,7 +228,7 @@ static void socks5_read_reply(struct bufferevent *buffev, redsocks_client *clien { socks5_reply reply; - if (redsocks_read_expected(client, buffev->input, &reply, sizes_greater_equal, sizeof(reply)) < 0) + if (redsocks_read_expected(client, bufferevent_get_input(buffev), &reply, sizes_greater_equal, sizeof(reply)) < 0) return; if (reply.ver != socks5_ver) { @@ -290,7 +290,7 @@ static void socks5_read_cb(struct bufferevent *buffev, void *_arg) else if (client->state == socks5_skip_domain) { socks5_addr_ipv4 ipv4; // all socks5_addr*.port are equal uint8_t size; - if (redsocks_read_expected(client, buffev->input, &size, sizes_greater_equal, sizeof(size)) < 0) + if (redsocks_read_expected(client, bufferevent_get_input(buffev), &size, sizes_greater_equal, sizeof(size)) < 0) return; socks5->to_skip = size + sizeof(ipv4.port); redsocks_write_helper( @@ -300,7 +300,7 @@ static void socks5_read_cb(struct bufferevent *buffev, void *_arg) } else if (client->state == socks5_skip_address) { uint8_t data[socks5->to_skip]; - if (redsocks_read_expected(client, buffev->input, data, sizes_greater_equal, socks5->to_skip) < 0) + if (redsocks_read_expected(client, bufferevent_get_input(buffev), data, sizes_greater_equal, socks5->to_skip) < 0) return; redsocks_start_relay(client); } diff --git a/utils.c b/utils.c index 5c3323db..1a6ed864 100644 --- a/utils.c +++ b/utils.c @@ -219,12 +219,11 @@ int red_socket_geterrno(struct bufferevent *buffev) int error; int pseudo_errno; socklen_t optlen = sizeof(pseudo_errno); + int fd = bufferevent_getfd(buffev); - assert(event_get_fd(&buffev->ev_read) == event_get_fd(&buffev->ev_write)); - - error = getsockopt(event_get_fd(&buffev->ev_read), SOL_SOCKET, SO_ERROR, &pseudo_errno, &optlen); + error = getsockopt(fd, SOL_SOCKET, SO_ERROR, &pseudo_errno, &optlen); if (error) { - log_errno(LOG_ERR, "getsockopt"); + log_errno(LOG_ERR, "getsockopt(fd=%d)", fd); return -1; } return pseudo_errno; @@ -283,22 +282,22 @@ char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_si /* copy event buffer from source to destination as much as possible. * If parameter skip is not zero, copy will start from the number of skip bytes. */ -size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, size_t skip) +size_t copy_evbuffer(struct bufferevent * dst, struct bufferevent * src, size_t skip) { int n, i; size_t written = 0; struct evbuffer_iovec *v; struct evbuffer_iovec quick_v[5];/* a vector with 5 elements is usually enough */ + struct evbuffer * evbinput = bufferevent_get_input(src); + size_t maxlen = dst->wm_write.high - evbuffer_get_length(bufferevent_get_output(dst)); + maxlen = evbuffer_get_length(evbinput) - skip > maxlen ? maxlen: evbuffer_get_length(evbinput)-skip; - size_t maxlen = dst->wm_write.high - EVBUFFER_LENGTH(dst->output); - maxlen = EVBUFFER_LENGTH(src->input) - skip> maxlen?maxlen: EVBUFFER_LENGTH(src->input)-skip; - - n = evbuffer_peek(src->input, maxlen+skip, NULL, NULL, 0); + n = evbuffer_peek(evbinput, maxlen+skip, NULL, NULL, 0); if (n > sizeof(quick_v)/sizeof(struct evbuffer_iovec)) v = malloc(sizeof(struct evbuffer_iovec)*n); else v = &quick_v[0]; - n = evbuffer_peek(src->input, maxlen+skip, NULL, v, n); + n = evbuffer_peek(evbinput, maxlen+skip, NULL, v, n); for (i=0; i= len) From 01c58b1c11b0112ba70d8a41261633d53004b3d9 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 24 Jun 2016 14:09:23 +0800 Subject: [PATCH 094/192] Switch to libevent2 API --- autoproxy.c | 8 +- direct.c | 2 - http-relay.c | 5 +- ipcache.c | 14 ++- libevent-compat.h | 11 -- log.c | 2 +- main.c | 296 ++++++++++++++++++++++++---------------------- parser.c | 2 +- redsocks.c | 96 ++++++++++----- redsocks.h | 8 +- redudp.c | 46 +++---- redudp.h | 10 +- shadowsocks-udp.c | 21 ++-- shadowsocks.c | 10 +- socks5-udp.c | 2 + tcpdns.c | 19 +-- tcpdns.h | 2 +- utils.c | 27 +++-- utils.h | 19 +-- 19 files changed, 336 insertions(+), 264 deletions(-) delete mode 100644 libevent-compat.h diff --git a/autoproxy.c b/autoproxy.c index 8c00ea61..d4977d38 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "utils.h" #include "log.h" #include "redsocks.h" @@ -243,7 +245,7 @@ static void auto_recv_timeout_cb(evutil_socket_t fd, short events, void * arg) static void direct_relay_readcb_helper(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) { - if (evbuffer_get_length(bufferevent_get_output(to)) < to->wm_write.high) + if (evbuffer_get_length(bufferevent_get_output(to)) < get_write_hwm(to)) { if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); @@ -403,7 +405,7 @@ static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) return; if (handle_write_to_client(client)) return; - if (output_size < to->wm_write.high) + if (output_size < get_write_hwm(to)) { if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); @@ -455,7 +457,7 @@ static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) return; if (aclient->state == AUTOPROXY_CONFIRMED) { - if (output_size < to->wm_write.high) + if (output_size < get_write_hwm(to)) { if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); diff --git a/direct.c b/direct.c index 4e6b95b5..8a96780e 100644 --- a/direct.c +++ b/direct.c @@ -20,11 +20,9 @@ #include #include #include -#include #include "parser.h" #include "log.h" #include "main.h" -#include "base.h" #include "redsocks.h" #include "utils.h" diff --git a/http-relay.c b/http-relay.c index cab7208b..9e6ce93e 100644 --- a/http-relay.c +++ b/http-relay.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "log.h" #include "redsocks.h" #include "http-auth.h" @@ -362,8 +364,7 @@ static void httpr_relay_write_cb(struct bufferevent *buffev, void *_arg) client->state = httpr_request_sent; - buffev->wm_read.low = 1; - buffev->wm_read.high = HTTP_HEAD_WM_HIGH; + bufferevent_setwatermark(buffev, EV_READ, 1, HTTP_HEAD_WM_HIGH); bufferevent_enable(buffev, EV_READ); } } diff --git a/ipcache.c b/ipcache.c index 1f926e7a..7c99aa2f 100644 --- a/ipcache.c +++ b/ipcache.c @@ -144,7 +144,7 @@ static int * addr_cache_counters = NULL; static int * addr_cache_pointers = NULL; static cache_data * addr_cache = NULL; static char cache_changed = 0; -static struct event timer_event; +static struct event * timer_event = NULL; static inline cache_data * get_cache_data(unsigned int block, unsigned int index); @@ -253,7 +253,6 @@ static int cache_init() } memset((void *)addr_cache_pointers, 0, size); - memset(&timer_event, 0, sizeof(timer_event)); if (config->cache_file) { if (load_cache(config->cache_file)) @@ -264,8 +263,9 @@ static int cache_init() { tv.tv_sec = config->autosave_interval; tv.tv_usec = 0; - event_assign(&timer_event, get_event_base(), 0, EV_TIMEOUT|EV_PERSIST, cache_auto_saver, NULL); - evtimer_add(&timer_event, &tv); + timer_event = event_new(get_event_base(), -1, EV_TIMEOUT|EV_PERSIST, cache_auto_saver, NULL); + if (timer_event) + evtimer_add(timer_event, &tv); } } set_cache_changed(0); @@ -282,9 +282,11 @@ static int cache_fini() save_cache(config->cache_file); set_cache_changed(0); } - if (event_initialized(&timer_event)) + if (timer_event) { - evtimer_del(&timer_event); + evtimer_del(timer_event); + event_free(timer_event); + timer_event = NULL; } // Free buffers allocated for cache if (addr_cache) diff --git a/libevent-compat.h b/libevent-compat.h deleted file mode 100644 index a7f1ca1b..00000000 --- a/libevent-compat.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef UUID_FC148CFA_5ECC_488E_8A62_CD39406C9F1E -#define UUID_FC148CFA_5ECC_488E_8A62_CD39406C9F1E - -/* evutil_socket_t is macros in libevent-2.0, not typedef, libevent-1.4 is - * still supported because of Ubuntu 10.04 LTS */ -#ifndef evutil_socket_t -# warning Using hardcoded value for evutil_socket_t as libevent headers do not define it. -# define evutil_socket_t int -#endif - -#endif // FC148CFA_5ECC_488E_8A62_CD39406C9F1E diff --git a/log.c b/log.c index 9a456ae2..2e2e1642 100644 --- a/log.c +++ b/log.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include "utils.h" #include "log.h" diff --git a/main.c b/main.c index 10c9cc98..0c6db743 100644 --- a/main.c +++ b/main.c @@ -14,6 +14,7 @@ * under the License. */ +#include #include #include #include @@ -21,7 +22,7 @@ #include #include #include -#include +#include #include "log.h" #include "main.h" #include "utils.h" @@ -35,12 +36,12 @@ extern app_subsys autoproxy_app_subsys; extern app_subsys cache_app_subsys; app_subsys *subsystems[] = { - &base_subsys, - &redsocks_subsys, - &autoproxy_app_subsys, - &cache_app_subsys, - &redudp_subsys, - &tcpdns_subsys, + &base_subsys, + &redsocks_subsys, + &autoproxy_app_subsys, + &cache_app_subsys, + &redudp_subsys, + &tcpdns_subsys, }; static const char *confname = "redsocks.conf"; @@ -49,15 +50,15 @@ static struct event_base * g_event_base = NULL; static void terminate(int sig, short what, void *_arg) { - if (g_event_base && event_base_loopbreak(g_event_base) != 0) - log_error(LOG_WARNING, "event_loopbreak"); + if (g_event_base && event_base_loopbreak(g_event_base) != 0) + log_error(LOG_WARNING, "event_loopbreak"); } static void dump_handler(int sig, short what, void *_arg) { - app_subsys **ss; - FOREACH(ss, subsystems) { - if ((*ss)->dump) { + app_subsys **ss; + FOREACH(ss, subsystems) { + if ((*ss)->dump) { (*ss)->dump(); } } @@ -79,7 +80,7 @@ static int setup_signals() struct event_base * get_event_base() { - return g_event_base; + return g_event_base; } static void wait_for_network() @@ -111,150 +112,157 @@ static void wait_for_network() int main(int argc, char **argv) { - int error; - app_subsys **ss; - int exit_signals[2] = {SIGTERM, SIGINT}; - struct event terminators[2]; - struct event dumper; - bool conftest = false; - int opt; - int i; - bool wait = false; - - evutil_secure_rng_init(); - while ((opt = getopt(argc, argv, "h?wvtc:p:")) != -1) { - switch (opt) { - case 't': - conftest = true; - break; - case 'w': - wait = true; - break; - case 'c': - confname = optarg; - break; - case 'p': - pidfile = optarg; - break; - case 'v': - puts(redsocks_version); - return EXIT_SUCCESS; - default: - printf( - "Usage: %s [-?hwvt] [-c config] [-p pidfile]\n" - " -h, -? this message\n" - " -w wait util network ready\n" - " -v print version\n" - " -t test config syntax\n" - " -p write pid to pidfile\n", - argv[0]); - return (opt == '?' || opt == 'h') ? EXIT_SUCCESS : EXIT_FAILURE; - } - } - - // Wait for network ready before further initializations so that - // parser can resolve domain names. - if (wait) - wait_for_network(); - - FILE *f = fopen(confname, "r"); - if (!f) { - perror("Unable to open config file"); - return EXIT_FAILURE; - } - - parser_context* parser = parser_start(f); - if (!parser) { - perror("Not enough memory for parser"); - return EXIT_FAILURE; - } - - FOREACH(ss, subsystems) - if ((*ss)->conf_section) - parser_add_section(parser, (*ss)->conf_section); - error = parser_run(parser); - parser_stop(parser); - fclose(f); - - if (error) - return EXIT_FAILURE; - - if (conftest) - return EXIT_SUCCESS; + int error; + app_subsys **ss; + int exit_signals[2] = {SIGTERM, SIGINT}; + struct event * terminators[2]; + struct event * dumper = NULL; + bool conftest = false; + int opt; + int i; + bool wait = false; + + evutil_secure_rng_init(); + while ((opt = getopt(argc, argv, "h?wvtc:p:")) != -1) { + switch (opt) { + case 't': + conftest = true; + break; + case 'w': + wait = true; + break; + case 'c': + confname = optarg; + break; + case 'p': + pidfile = optarg; + break; + case 'v': + puts(redsocks_version); + return EXIT_SUCCESS; + default: + printf( + "Usage: %s [-?hwvt] [-c config] [-p pidfile]\n" + " -h, -? this message\n" + " -w wait util network ready\n" + " -v print version\n" + " -t test config syntax\n" + " -p write pid to pidfile\n", + argv[0]); + return (opt == '?' || opt == 'h') ? EXIT_SUCCESS : EXIT_FAILURE; + } + } - if (setup_signals()) + // Wait for network ready before further initializations so that + // parser can resolve domain names. + if (wait) + wait_for_network(); + + FILE *f = fopen(confname, "r"); + if (!f) { + perror("Unable to open config file"); + return EXIT_FAILURE; + } + + parser_context* parser = parser_start(f); + if (!parser) { + perror("Not enough memory for parser"); + return EXIT_FAILURE; + } + + FOREACH(ss, subsystems) + if ((*ss)->conf_section) + parser_add_section(parser, (*ss)->conf_section); + error = parser_run(parser); + parser_stop(parser); + fclose(f); + + if (error) + return EXIT_FAILURE; + + if (conftest) return EXIT_SUCCESS; - // Initialize global event base - g_event_base = event_base_new(); - if (!g_event_base) - return EXIT_FAILURE; - - memset(&dumper, 0, sizeof(dumper)); - memset(terminators, 0, sizeof(terminators)); - - FOREACH(ss, subsystems) { - if ((*ss)->init) { - error = (*ss)->init(); - if (error) - goto shutdown; - } - } - - if (pidfile) { - f = fopen(pidfile, "w"); - if (!f) { - perror("Unable to open pidfile for write"); - return EXIT_FAILURE; - } - fprintf(f, "%d\n", getpid()); - fclose(f); - } - - assert(SIZEOF_ARRAY(exit_signals) == SIZEOF_ARRAY(terminators)); - for (i = 0; i < SIZEOF_ARRAY(exit_signals); i++) { - evsignal_assign(&terminators[i], get_event_base(), exit_signals[i], terminate, NULL); - if (evsignal_add(&terminators[i], NULL) != 0) { - log_errno(LOG_ERR, "signal_add"); - goto shutdown; - } - } - - evsignal_assign(&dumper, get_event_base(), SIGUSR1, dump_handler, NULL); - if (evsignal_add(&dumper, NULL) != 0) { + if (setup_signals()) + return EXIT_FAILURE; + + // Initialize global event base + g_event_base = event_base_new(); + if (!g_event_base) + return EXIT_FAILURE; + + memset(terminators, 0, sizeof(terminators)); + + FOREACH(ss, subsystems) { + if ((*ss)->init) { + error = (*ss)->init(); + if (error) + goto shutdown; + } + } + + if (pidfile) { + f = fopen(pidfile, "w"); + if (!f) { + perror("Unable to open pidfile for write"); + return EXIT_FAILURE; + } + fprintf(f, "%d\n", getpid()); + fclose(f); + } + + assert(SIZEOF_ARRAY(exit_signals) == SIZEOF_ARRAY(terminators)); + for (i = 0; i < SIZEOF_ARRAY(exit_signals); i++) { + terminators[i] = evsignal_new(get_event_base(), exit_signals[i], terminate, NULL); + if (!terminators[i]) { + log_errno(LOG_ERR, "evsignal_new"); + goto shutdown; + } + if (evsignal_add(terminators[i], NULL) != 0) { + log_errno(LOG_ERR, "evsignal_add"); + goto shutdown; + } + } + + dumper = evsignal_new(get_event_base(), SIGUSR1, dump_handler, NULL); + if (!dumper) { + log_errno(LOG_ERR, "evsignal_new"); + goto shutdown; + } + if (evsignal_add(dumper, NULL) != 0) { log_errno(LOG_ERR, "evsignal_add"); goto shutdown; } - log_error(LOG_NOTICE, "redsocks started with: %s", event_base_get_method(g_event_base)); + log_error(LOG_NOTICE, "redsocks started with: %s", event_base_get_method(g_event_base)); - event_base_dispatch(g_event_base); + event_base_dispatch(g_event_base); - log_error(LOG_NOTICE, "redsocks goes down"); + log_error(LOG_NOTICE, "redsocks goes down"); shutdown: - if (evsignal_initialized(&dumper)) { - if (evsignal_del(&dumper) != 0) - log_errno(LOG_WARNING, "signal_del"); - memset(&dumper, 0, sizeof(dumper)); + if (dumper) { + if (evsignal_del(dumper) != 0) + log_errno(LOG_WARNING, "evsignal_del"); + event_free(dumper); + } + + for (i = 0; i < SIZEOF_ARRAY(exit_signals); i++) { + if (terminators[i]) { + if (evsignal_del(terminators[i]) != 0) + log_errno(LOG_WARNING, "evsignal_del"); + event_free(terminators[i]); + } } - for (i = 0; i < SIZEOF_ARRAY(exit_signals); i++) { - if (evsignal_initialized(&terminators[i])) { - if (evsignal_del(&terminators[i]) != 0) - log_errno(LOG_WARNING, "signal_del"); - memset(&terminators[i], 0, sizeof(terminators[i])); - } - } - - for (--ss; ss >= subsystems; ss--) - if ((*ss)->fini) - (*ss)->fini(); - - if (g_event_base) - event_base_free(g_event_base); - - return !error ? EXIT_SUCCESS : EXIT_FAILURE; + for (--ss; ss >= subsystems; ss--) + if ((*ss)->fini) + (*ss)->fini(); + + if (g_event_base) + event_base_free(g_event_base); + + return !error ? EXIT_SUCCESS : EXIT_FAILURE; } /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ diff --git a/parser.c b/parser.c index 7e0ab0b3..8929f4cf 100644 --- a/parser.c +++ b/parser.c @@ -55,7 +55,7 @@ void parser_error(parser_context *context, const char *fmt, ...) va_start(ap, fmt); if (buff) { evbuffer_add_vprintf(buff, fmt, ap); - msg = (const char*)EVBUFFER_DATA(buff); + msg = (const char*)evbuffer_pullup(buff, -1); } else msg = ""; diff --git a/redsocks.c b/redsocks.c index 218a0ddc..0f2f505b 100644 --- a/redsocks.c +++ b/redsocks.c @@ -20,6 +20,7 @@ * under the License. */ +#include #include #include #include @@ -30,7 +31,9 @@ #include #include #include -#include +#include +#include +#include #include "list.h" #include "parser.h" #include "log.h" @@ -38,7 +41,6 @@ #include "base.h" #include "redsocks.h" #include "utils.h" -#include "libevent-compat.h" #define REDSOCKS_RELAY_HALFBUFF 1024*16 @@ -90,13 +92,13 @@ static void tracked_event_set( struct tracked_event *tev, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg) { - event_assign(&tev->ev, get_event_base(), fd, events, callback, arg); + tev->ev = event_new(get_event_base(), fd, events, callback, arg); timerclear(&tev->inserted); } static int tracked_event_add(struct tracked_event *tev, const struct timeval *tv) { - int ret = event_add(&tev->ev, tv); + int ret = event_add(tev->ev, tv); if (ret == 0) gettimeofday(&tev->inserted, NULL); return ret; @@ -104,12 +106,28 @@ static int tracked_event_add(struct tracked_event *tev, const struct timeval *tv static int tracked_event_del(struct tracked_event *tev) { - int ret = event_del(&tev->ev); - if (ret == 0) - timerclear(&tev->inserted); + int ret = -1; + if (tev->ev) { + ret = event_del(tev->ev); + if (ret == 0) { + timerclear(&tev->inserted); + } + } return ret; } +static void tracked_event_free(struct tracked_event *tev) +{ + if (tev->ev) { + if (timerisset(&tev->inserted)) { + event_del(tev->ev); + timerclear(&tev->inserted); + } + event_free(tev->ev); + tev->ev = NULL; + } +} + static int redsocks_onenter(parser_section *section) { // FIXME: find proper way to calulate instance_payload_len @@ -260,7 +278,7 @@ static void redsocks_relay_readcb(redsocks_client *client, struct bufferevent *f redsocks_log_error(client, LOG_DEBUG, "RCB %s, in: %u", from == client->client?"client":"relay", evbuffer_get_length(bufferevent_get_input(from))); - if (evbuffer_get_length(bufferevent_get_output(to)) < to->wm_write.high) { + if (evbuffer_get_length(bufferevent_get_output(to)) < get_write_hwm(to)) { if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); if (bufferevent_enable(from, EV_READ) == -1) @@ -301,7 +319,7 @@ static void redsocks_relay_writecb(redsocks_client *client, struct bufferevent * if (process_shutdown_on_write_(client, from, to)) return; - if (evbuffer_get_length(bufferevent_get_output(to)) < to->wm_write.high) { + if (evbuffer_get_length(bufferevent_get_output(to)) < get_write_hwm(to)) { if (bufferevent_write_buffer(to, bufferevent_get_input(from)) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); if (!(from_evshut & EV_READ) && bufferevent_enable(from, EV_READ) == -1) @@ -591,8 +609,7 @@ int redsocks_write_helper_ex_plain( if (client) client->state = state; - buffev->wm_read.low = wm_low; - buffev->wm_read.high = wm_high; + bufferevent_setwatermark(buffev, EV_READ, wm_low, wm_high); bufferevent_enable(buffev, EV_READ); drop = 0; @@ -870,12 +887,32 @@ static void redsocks_dump_instance(redsocks_instance *instance) log_error(LOG_INFO, "End of client list."); } +static void +display_mallinfo(void) +{ + struct mallinfo mi; + + mi = mallinfo(); + + log_error(LOG_INFO, "Total non-mmapped bytes (arena): %d\n", mi.arena); + log_error(LOG_INFO, "# of free chunks (ordblks): %d\n", mi.ordblks); + log_error(LOG_INFO, "# of free fastbin blocks (smblks): %d\n", mi.smblks); + log_error(LOG_INFO, "# of mapped regions (hblks): %d\n", mi.hblks); + log_error(LOG_INFO, "Bytes in mapped regions (hblkhd): %d\n", mi.hblkhd); + log_error(LOG_INFO, "Max. total allocated space (usmblks): %d\n", mi.usmblks); + log_error(LOG_INFO, "Free bytes held in fastbins (fsmblks): %d\n", mi.fsmblks); + log_error(LOG_INFO, "Total allocated space (uordblks): %d\n", mi.uordblks); + log_error(LOG_INFO, "Total free space (fordblks): %d\n", mi.fordblks); + log_error(LOG_INFO, "Topmost releasable block (keepcost): %d\n", mi.keepcost); +} + static void redsocks_debug_dump() { redsocks_instance *instance = NULL; list_for_each_entry(instance, &instances, list) redsocks_dump_instance(instance); + display_mallinfo(); } /* Audit is required to clean up hung connections. @@ -1016,26 +1053,21 @@ static void redsocks_fini_instance(redsocks_instance *instance) { if (instance->relay_ss->instance_fini) instance->relay_ss->instance_fini(instance); - if (event_initialized(&instance->listener.ev)) { - if (timerisset(&instance->listener.inserted)) - if (tracked_event_del(&instance->listener) != 0) - log_errno(LOG_WARNING, "event_del"); - redsocks_close(event_get_fd(&instance->listener.ev)); + if (instance->listener.ev) { + int fd = event_get_fd(instance->listener.ev); + tracked_event_free(&instance->listener); + redsocks_close(fd); memset(&instance->listener, 0, sizeof(instance->listener)); } - - if (event_initialized(&instance->accept_backoff.ev)) { - if (timerisset(&instance->accept_backoff.inserted)) - if (tracked_event_del(&instance->accept_backoff) != 0) - log_errno(LOG_WARNING, "event_del"); - memset(&instance->accept_backoff, 0, sizeof(instance->accept_backoff)); - } + tracked_event_free(&instance->accept_backoff); + memset(&instance->accept_backoff, 0, sizeof(instance->accept_backoff)); list_del(&instance->list); free(instance->config.type); free(instance->config.login); free(instance->config.password); + free(instance->config.interface); memset(instance, 0, sizeof(*instance)); free(instance); @@ -1043,19 +1075,21 @@ static void redsocks_fini_instance(redsocks_instance *instance) { static int redsocks_fini(); -static struct event audit_event; +static struct event * audit_event = NULL; static int redsocks_init() { redsocks_instance *tmp, *instance = NULL; struct timeval audit_time; struct event_base * base = get_event_base(); - memset(&audit_event, 0, sizeof(audit_event)); /* Start audit */ audit_time.tv_sec = REDSOCKS_AUDIT_INTERVAL; audit_time.tv_usec = 0; - event_assign(&audit_event, base, 0, EV_TIMEOUT|EV_PERSIST, redsocks_audit, NULL); - evtimer_add(&audit_event, &audit_time); + audit_event = event_new(base, -1, EV_TIMEOUT|EV_PERSIST, redsocks_audit, NULL); + if (!audit_event) + goto fail; + if (evtimer_add(audit_event, &audit_time)) + goto fail; list_for_each_entry_safe(instance, tmp, &instances, list) { if (redsocks_init_instance(instance) != 0) @@ -1079,9 +1113,11 @@ static int redsocks_fini() redsocks_fini_instance(instance); /* stop audit */ - if (event_initialized(&audit_event)) - evtimer_del(&audit_event); - + if (audit_event) { + evtimer_del(audit_event); + event_free(audit_event); + audit_event = NULL; + } return 0; } diff --git a/redsocks.h b/redsocks.h index d26b7574..6a49495f 100644 --- a/redsocks.h +++ b/redsocks.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include "list.h" @@ -16,8 +16,8 @@ typedef struct relay_subsys_t { char *name; size_t payload_len; // size of relay-specific data in client section size_t instance_payload_len; // size of relay-specify data in instance section - evbuffercb readcb; - evbuffercb writecb; + bufferevent_data_cb readcb; + bufferevent_data_cb writecb; void (*init)(struct redsocks_client_t *client); void (*fini)(struct redsocks_client_t *client); int (*instance_init)(struct redsocks_instance_t *instance); @@ -43,7 +43,7 @@ typedef struct redsocks_config_t { } redsocks_config; struct tracked_event { - struct event ev; + struct event * ev; struct timeval inserted; }; diff --git a/redudp.c b/redudp.c index 3764861e..c78ca3c9 100644 --- a/redudp.c +++ b/redudp.c @@ -237,9 +237,10 @@ void redudp_drop_client(redudp_client *client) if (client->instance->relay_ss->fini) client->instance->relay_ss->fini(client); - if (event_initialized(&client->timeout)) { - if (event_del(&client->timeout) == -1) + if (client->timeoutev) { + if (evtimer_del(client->timeoutev) == -1) redudp_log_errno(client, LOG_ERR, "event_del"); + event_free(client->timeoutev); } list_for_each_entry_safe(q, tmp, &client->queue, list) { list_del(&q->list); @@ -255,8 +256,8 @@ void redudp_bump_timeout(redudp_client *client) tv.tv_sec = client->instance->config.udp_timeout; tv.tv_usec = 0; // TODO: implement udp_timeout_stream - if (event_add(&client->timeout, &tv) != 0) { - redudp_log_error(client, LOG_WARNING, "event_add(&client->timeout, ...)"); + if (event_add(client->timeoutev, &tv) != 0) { + redudp_log_error(client, LOG_WARNING, "event_add(&client->timeoutev, ...)"); redudp_drop_client(client); } } @@ -272,7 +273,7 @@ void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, // When working with TPROXY, we have to get sender FD from tree on // receipt of each packet from relay. fd = do_tproxy(client->instance) ? bound_udp4_get(srcaddr) - : event_get_fd(&client->instance->listener); + : event_get_fd(client->instance->listener); if (fd == -1) { redudp_log_error(client, LOG_WARNING, "bound_udp4_get failure"); return; @@ -351,7 +352,7 @@ static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_ // TODO: remove client->destaddr if (destaddr) memcpy(&client->destaddr, destaddr, sizeof(client->destaddr)); - evtimer_assign(&client->timeout, get_event_base(), redudp_timeout, client); + client->timeoutev = evtimer_new(get_event_base(), redudp_timeout, client); self->relay_ss->init(client); redsocks_time(&client->first_event); @@ -382,7 +383,7 @@ static void redudp_pkt_from_client(int fd, short what, void *_arg) pdestaddr = do_tproxy(self) ? &destaddr : NULL; - assert(fd == event_get_fd(&self->listener)); + assert(fd == event_get_fd(self->listener)); // destaddr will be filled with true destination if it is available pktlen = red_recv_udp_pkt(fd, self->shared_buff, MAX_UDP_PACKET_SIZE, &clientaddr, pdestaddr); if (pktlen == -1) @@ -583,8 +584,12 @@ static int redudp_init_instance(redudp_instance *instance) goto fail; } - event_assign(&instance->listener, get_event_base(), fd, EV_READ | EV_PERSIST, redudp_pkt_from_client, instance); - error = event_add(&instance->listener, NULL); + instance->listener = event_new(get_event_base(), fd, EV_READ | EV_PERSIST, redudp_pkt_from_client, instance); + if (!instance->listener) { + log_errno(LOG_ERR, "event_new"); + goto fail; + } + error = event_add(instance->listener, NULL); if (error) { log_errno(LOG_ERR, "event_add"); goto fail; @@ -616,10 +621,10 @@ static void redudp_fini_instance(redudp_instance *instance) } } - if (event_initialized(&instance->listener)) { - if (event_del(&instance->listener) != 0) + if (instance->listener) { + if (event_del(instance->listener) != 0) log_errno(LOG_WARNING, "event_del"); - close(event_get_fd(&instance->listener)); + close(event_get_fd(instance->listener)); memset(&instance->listener, 0, sizeof(instance->listener)); } @@ -627,7 +632,7 @@ static void redudp_fini_instance(redudp_instance *instance) instance->relay_ss->instance_fini(instance); list_del(&instance->list); - + free(instance->config.type); free(instance->config.login); free(instance->config.password); @@ -635,7 +640,7 @@ static void redudp_fini_instance(redudp_instance *instance) free(instance); } -static struct event audit_event; +static struct event * audit_event = NULL; static void redudp_audit(int sig, short what, void *_arg) { @@ -653,12 +658,11 @@ static int redudp_init() goto fail; } - memset(&audit_event, 0, sizeof(audit_event)); /* Start audit */ audit_time.tv_sec = REDUDP_AUDIT_INTERVAL; audit_time.tv_usec = 0; - event_assign(&audit_event, base, 0, EV_TIMEOUT|EV_PERSIST, redudp_audit, NULL); - evtimer_add(&audit_event, &audit_time); + audit_event = event_new(base, -1, EV_TIMEOUT|EV_PERSIST, redudp_audit, NULL); + evtimer_add(audit_event, &audit_time); return 0; @@ -672,9 +676,11 @@ static int redudp_fini() redudp_instance *tmp, *instance = NULL; /* stop audit */ - if (event_initialized(&audit_event)) - evtimer_del(&audit_event); - + if (audit_event) { + evtimer_del(audit_event); + event_free(audit_event); + audit_event = NULL; + } list_for_each_entry_safe(instance, tmp, &instances, list) redudp_fini_instance(instance); diff --git a/redudp.h b/redudp.h index 4adf4a4f..29341e0e 100644 --- a/redudp.h +++ b/redudp.h @@ -1,7 +1,7 @@ #ifndef REDUDP_H #define REDUDP_H -#include +#include #include "list.h" #define MAX_UDP_PACKET_SIZE 0xFFFF @@ -41,18 +41,18 @@ typedef struct redudp_config_t { typedef struct redudp_instance_t { list_head list; redudp_config config; - struct event listener; + struct event * listener; list_head clients; - udprelay_subsys *relay_ss; + udprelay_subsys*relay_ss; void * shared_buff; // pointer to 64K buffer shared by clients for receiving/processing udp packets } redudp_instance; typedef struct redudp_client_t { list_head list; - redudp_instance *instance; + redudp_instance * instance; struct sockaddr_in clientaddr; struct sockaddr_in destaddr; - struct event timeout; + struct event * timeoutev; int state; // it's used by bottom layer time_t first_event; time_t last_client_event; diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index 478a238b..a6a0f8d9 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -29,7 +29,7 @@ #include "shadowsocks.h" typedef struct ss_client_t { - struct event udprelay; + struct event * udprelay; } ss_client; typedef struct ss_instance_t { @@ -63,10 +63,11 @@ static void ss_client_init(redudp_client *client) static void ss_client_fini(redudp_client *client) { ss_client *ssclient = (void*)(client + 1); - if (event_initialized(&ssclient->udprelay)) { - int fd = event_get_fd(&ssclient->udprelay); - if (event_del(&ssclient->udprelay) == -1) + if (ssclient->udprelay) { + int fd = event_get_fd(ssclient->udprelay); + if (event_del(ssclient->udprelay) == -1) redudp_log_errno(client, LOG_ERR, "event_del"); + event_free(ssclient->udprelay); close(fd); } } @@ -120,7 +121,7 @@ static void ss_forward_pkt(redudp_client *client, struct sockaddr * destaddr, vo io[0].iov_base = buff; io[0].iov_len = fwdlen; - outgoing = sendmsg(event_get_fd(&ssclient->udprelay), &msg, 0); + outgoing = sendmsg(event_get_fd(ssclient->udprelay), &msg, 0); if (outgoing == -1) { redudp_log_errno(client, LOG_DEBUG, "sendmsg: Can't forward packet, dropping it"); return; @@ -144,7 +145,7 @@ static void ss_pkt_from_server(int fd, short what, void *_arg) void * buff = client->instance->shared_buff; void * buff2 = ss->buff; - assert(fd == event_get_fd(&ssclient->udprelay)); + assert(fd == event_get_fd(ssclient->udprelay)); pktlen = red_recv_udp_pkt(fd, buff, MAX_UDP_PACKET_SIZE, &udprelayaddr, NULL); if (pktlen == -1) @@ -212,8 +213,12 @@ static void ss_connect_relay(redudp_client *client) goto fail; } - event_assign(&ssclient->udprelay, get_event_base(), fd, EV_READ | EV_PERSIST, ss_pkt_from_server, client); - error = event_add(&ssclient->udprelay, NULL); + ssclient->udprelay = event_new(get_event_base(), fd, EV_READ | EV_PERSIST, ss_pkt_from_server, client); + if (!ssclient->udprelay) { + redudp_log_errno(client, LOG_ERR, "event_new"); + goto fail; + } + error = event_add(ssclient->udprelay, NULL); if (error) { redudp_log_errno(client, LOG_ERR, "event_add"); goto fail; diff --git a/shadowsocks.c b/shadowsocks.c index b81e90c1..7d8d12a4 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "utils.h" #include "log.h" #include "redsocks.h" @@ -161,7 +163,7 @@ static void ss_client_writecb(struct bufferevent *buffev, void *_arg) if (client->state == ss_connected) { /* encrypt and forward data received from client side */ - if (output_size < to->wm_write.high) + if (output_size < get_write_hwm(to)) { if (input_size) decrypt_buffer(client, from, to); @@ -188,7 +190,7 @@ static void ss_client_readcb(struct bufferevent *buffev, void *_arg) if (client->state == ss_connected) { /* encrypt and forward data to the other side */ - if (output_size < to->wm_write.high) + if (output_size < get_write_hwm(to)) { encrypt_buffer(client, from, to); if (bufferevent_enable(from, EV_READ) == -1) @@ -224,7 +226,7 @@ static void ss_relay_writecb(struct bufferevent *buffev, void *_arg) if (client->state == ss_connected) { /* encrypt and forward data received from client side */ - if (output_size < to->wm_write.high) + if (output_size < get_write_hwm(to)) { if (input_size) encrypt_buffer(client, from, to); @@ -252,7 +254,7 @@ static void ss_relay_readcb(struct bufferevent *buffev, void *_arg) if (client->state == ss_connected) { /* decrypt and forward data to client side */ - if (output_size < to->wm_write.high) + if (output_size < get_write_hwm(to)) { if (input_size) decrypt_buffer(client, from, to); diff --git a/socks5-udp.c b/socks5-udp.c index 2c0d13d6..e8ef910a 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "main.h" #include "utils.h" #include "log.h" diff --git a/tcpdns.c b/tcpdns.c index 1d85c0c5..04248e7d 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -126,7 +126,7 @@ static void tcpdns_readcb(struct bufferevent *from, void *_arg) case DNS_RC_FORMERR: case DNS_RC_NXDOMAIN: { - int fd = event_get_fd(&req->instance->listener); + int fd = event_get_fd(req->instance->listener); if (sendto(fd, &buff.raw[2], read_size - 2, 0, (struct sockaddr*)&req->client_addr, sizeof(req->client_addr)) != read_size - 2) { @@ -276,7 +276,7 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) struct sockaddr_in * destaddr; ssize_t pktlen; - assert(fd == event_get_fd(&self->listener)); + assert(fd == event_get_fd(self->listener)); /* allocate and initialize request structure */ req = (dns_request *)calloc(sizeof(dns_request), 1); if (!req) @@ -464,8 +464,12 @@ static int tcpdns_init_instance(tcpdns_instance *instance) goto fail; } - event_assign(&instance->listener, get_event_base(), fd, EV_READ | EV_PERSIST, tcpdns_pkt_from_client, instance); - error = event_add(&instance->listener, NULL); + instance->listener = event_new(get_event_base(), fd, EV_READ | EV_PERSIST, tcpdns_pkt_from_client, instance); + if (!instance->listener) { + log_errno(LOG_ERR, "event_new"); + goto fail; + } + error = event_add(instance->listener, NULL); if (error) { log_errno(LOG_ERR, "event_add"); @@ -490,11 +494,12 @@ static int tcpdns_init_instance(tcpdns_instance *instance) */ static void tcpdns_fini_instance(tcpdns_instance *instance) { - if (event_initialized(&instance->listener)) { - if (event_del(&instance->listener) != 0) + if (instance->listener) { + if (event_del(instance->listener) != 0) log_errno(LOG_WARNING, "event_del"); - if (close(event_get_fd(&instance->listener)) != 0) + if (close(event_get_fd(instance->listener)) != 0) log_errno(LOG_WARNING, "close"); + event_free(instance->listener); } list_del(&instance->list); diff --git a/tcpdns.h b/tcpdns.h index 5568ea26..ee8d9f0a 100644 --- a/tcpdns.h +++ b/tcpdns.h @@ -13,7 +13,7 @@ typedef struct tcpdns_config_t { typedef struct tcpdns_instance_t { list_head list; tcpdns_config config; - struct event listener; + struct event * listener; list_head requests; // Data for DNS resolver status tracking/checking int udp1_delay_ms; diff --git a/utils.c b/utils.c index 1a6ed864..bd0f83a8 100644 --- a/utils.c +++ b/utils.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "config.h" #include "main.h" #include "log.h" @@ -117,9 +119,9 @@ char *redsocks_evbuffer_readline(struct evbuffer *buf) } struct bufferevent* red_prepare_relay(const char *ifname, - evbuffercb readcb, - evbuffercb writecb, - everrorcb errorcb, + bufferevent_data_cb readcb, + bufferevent_data_cb writecb, + bufferevent_event_cb errorcb, void *cbarg) { struct bufferevent *retval = NULL; @@ -178,9 +180,9 @@ struct bufferevent* red_prepare_relay(const char *ifname, struct bufferevent* red_connect_relay(const char *ifname, struct sockaddr_in *addr, - evbuffercb readcb, - evbuffercb writecb, - everrorcb errorcb, + bufferevent_data_cb readcb, + bufferevent_data_cb writecb, + bufferevent_event_cb errorcb, void *cbarg, const struct timeval *timeout_write) { @@ -289,7 +291,7 @@ size_t copy_evbuffer(struct bufferevent * dst, struct bufferevent * src, size_t struct evbuffer_iovec *v; struct evbuffer_iovec quick_v[5];/* a vector with 5 elements is usually enough */ struct evbuffer * evbinput = bufferevent_get_input(src); - size_t maxlen = dst->wm_write.high - evbuffer_get_length(bufferevent_get_output(dst)); + size_t maxlen = get_write_hwm(dst) - evbuffer_get_length(bufferevent_get_output(dst)); maxlen = evbuffer_get_length(evbinput) - skip > maxlen ? maxlen: evbuffer_get_length(evbinput)-skip; n = evbuffer_peek(evbinput, maxlen+skip, NULL, NULL, 0); @@ -324,6 +326,17 @@ size_t copy_evbuffer(struct bufferevent * dst, struct bufferevent * src, size_t return written; } +size_t get_write_hwm(struct bufferevent *bufev) +{ +#ifdef bufferevent_getwatermark + size_t high; + bufferevent_getwatermark(bufev, EV_WRITE, NULL, &high); + return high; +#else + return bufev->wm_write.high; +#endif +} + int make_socket_transparent(int fd) { int on = 1; diff --git a/utils.h b/utils.h index 1917c573..8fb195b2 100644 --- a/utils.h +++ b/utils.h @@ -3,8 +3,10 @@ #include #include -#include #include +#include +#include +#include struct sockaddr_in; @@ -52,22 +54,23 @@ uint32_t red_randui32(); time_t redsocks_time(time_t *t); char *redsocks_evbuffer_readline(struct evbuffer *buf); struct bufferevent* red_prepare_relay(const char *ifname, - evbuffercb readcb, - evbuffercb writecb, - everrorcb errorcb, + bufferevent_data_cb readcb, + bufferevent_data_cb writecb, + bufferevent_event_cb errorcb, void *cbarg); struct bufferevent* red_connect_relay(const char *ifname, struct sockaddr_in *addr, - evbuffercb readcb, - evbuffercb writecb, - everrorcb errorcb, + bufferevent_data_cb readcb, + bufferevent_data_cb writecb, + bufferevent_event_cb errorcb, void *cbarg, const struct timeval *timeout_write); int red_socket_geterrno(struct bufferevent *buffev); int red_is_socket_connected_ok(struct bufferevent *buffev); int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *fromaddr, struct sockaddr_in *toaddr); -size_t copy_evbuffer(struct bufferevent * dst, const struct bufferevent * src, size_t skip); +size_t copy_evbuffer(struct bufferevent * dst, struct bufferevent * src, size_t skip); +size_t get_write_hwm(struct bufferevent *bufev); int make_socket_transparent(int fd); int apply_tcp_fastopen(int fd); From 03b482510a6a412df702df9a2987447de910abad Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 15 Oct 2016 20:43:43 +0800 Subject: [PATCH 095/192] Remove debugging code --- redsocks.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/redsocks.c b/redsocks.c index 0f2f505b..78e408ee 100644 --- a/redsocks.c +++ b/redsocks.c @@ -20,7 +20,6 @@ * under the License. */ -#include #include #include #include @@ -887,32 +886,12 @@ static void redsocks_dump_instance(redsocks_instance *instance) log_error(LOG_INFO, "End of client list."); } -static void -display_mallinfo(void) -{ - struct mallinfo mi; - - mi = mallinfo(); - - log_error(LOG_INFO, "Total non-mmapped bytes (arena): %d\n", mi.arena); - log_error(LOG_INFO, "# of free chunks (ordblks): %d\n", mi.ordblks); - log_error(LOG_INFO, "# of free fastbin blocks (smblks): %d\n", mi.smblks); - log_error(LOG_INFO, "# of mapped regions (hblks): %d\n", mi.hblks); - log_error(LOG_INFO, "Bytes in mapped regions (hblkhd): %d\n", mi.hblkhd); - log_error(LOG_INFO, "Max. total allocated space (usmblks): %d\n", mi.usmblks); - log_error(LOG_INFO, "Free bytes held in fastbins (fsmblks): %d\n", mi.fsmblks); - log_error(LOG_INFO, "Total allocated space (uordblks): %d\n", mi.uordblks); - log_error(LOG_INFO, "Total free space (fordblks): %d\n", mi.fordblks); - log_error(LOG_INFO, "Topmost releasable block (keepcost): %d\n", mi.keepcost); -} - static void redsocks_debug_dump() { redsocks_instance *instance = NULL; list_for_each_entry(instance, &instances, list) redsocks_dump_instance(instance); - display_mallinfo(); } /* Audit is required to clean up hung connections. From d3f53ec45cea7a6e05bf4c26e7da714c4249579b Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 3 Dec 2016 10:44:09 +0800 Subject: [PATCH 096/192] Fix: TCPDNS does not accept request with additional records --- main.c | 1 - tcpdns.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/main.c b/main.c index 0c6db743..b075b7f6 100644 --- a/main.c +++ b/main.c @@ -14,7 +14,6 @@ * under the License. */ -#include #include #include #include diff --git a/tcpdns.c b/tcpdns.c index 04248e7d..3ea1ac65 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -304,7 +304,7 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) if ( (req->data.header.qr_opcode_aa_tc_rd & DNS_QR) == 0 /* query */ && (req->data.header.ra_z_rcode & DNS_Z) == 0 /* Z is Zero */ && req->data.header.qdcount /* some questions */ - && !req->data.header.ancount && !req->data.header.nscount && !req->data.header.arcount /* no answers */ + && !req->data.header.ancount && !req->data.header.nscount ) { tv.tv_sec = self->config.timeout; From bc2706a331c04a76df428748da97a7d4b5fa1754 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 3 Dec 2016 10:49:33 +0800 Subject: [PATCH 097/192] Bump version to 0.66 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4d13ff4f..647a8721 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks2 -VERSION := 0.65 +VERSION := 0.66 OS := $(shell uname) LIBS := -levent From 82a4fb30ab71da160cdae28d8ac40381eebfad28 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 27 Feb 2017 15:44:13 +0800 Subject: [PATCH 098/192] Clean compiler warnings on x64 platform --- autoproxy.c | 10 +++++----- redsocks.c | 6 +++--- tcpdns.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index d4977d38..3ee40520 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -222,7 +222,7 @@ static void auto_recv_timeout_cb(evutil_socket_t fd, short events, void * arg) redsocks_client *client = arg; autoproxy_client * aclient = get_autoproxy_client(client); - redsocks_log_error(client, LOG_DEBUG, "RECV Timeout, state: %d, data_sent: %u", aclient->state, aclient->data_sent); + redsocks_log_error(client, LOG_DEBUG, "RECV Timeout, state: %d, data_sent: %zu", aclient->state, aclient->data_sent); assert(events & EV_TIMEOUT); redsocks_touch_client(client); @@ -270,7 +270,7 @@ static int handle_write_to_relay(redsocks_client *client) if (aclient->state == AUTOPROXY_CONNECTED ) { - redsocks_log_error(client, LOG_DEBUG, "sent: %u, recv: %u, in:%u, out:%u", + redsocks_log_error(client, LOG_DEBUG, "sent: %zu, recv: %zu, in:%zu, out:%zu", aclient->data_sent, aclient->data_recv, input_size, @@ -374,7 +374,7 @@ static void direct_relay_clientreadcb(struct bufferevent *from, void *_client) redsocks_client *client = _client; size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); - redsocks_log_error(client, LOG_DEBUG, "client in: %u", input_size); + redsocks_log_error(client, LOG_DEBUG, "client in: %zu", input_size); redsocks_touch_client(client); if (handle_write_to_relay(client)) return; @@ -387,7 +387,7 @@ static void direct_relay_relayreadcb(struct bufferevent *from, void *_client) redsocks_client *client = _client; size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); - redsocks_log_error(client, LOG_DEBUG, "relay in: %u", input_size); + redsocks_log_error(client, LOG_DEBUG, "relay in: %zu", input_size); redsocks_touch_client(client); if (handle_write_to_client(client)) return; @@ -421,7 +421,7 @@ static int process_shutdown_on_write_2(redsocks_client *client, struct buffereve unsigned short from_evshut = from == client->client ? client->client_evshut : client->relay_evshut; unsigned short to_evshut = to == client->client ? client->client_evshut : client->relay_evshut; - redsocks_log_error(client, LOG_DEBUG, "WCB %s, fs: %u, ts: %u, fin: %u, fout: %u, tin: %u", + redsocks_log_error(client, LOG_DEBUG, "WCB %s, fs: %u, ts: %u, fin: %zu, fout: %zu, tin: %zu", to == client->client?"client":"relay", from_evshut, to_evshut, diff --git a/redsocks.c b/redsocks.c index 78e408ee..a77a7ffa 100644 --- a/redsocks.c +++ b/redsocks.c @@ -274,7 +274,7 @@ static inline const char* bufname(redsocks_client *client, struct bufferevent *b static void redsocks_relay_readcb(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) { - redsocks_log_error(client, LOG_DEBUG, "RCB %s, in: %u", from == client->client?"client":"relay", + redsocks_log_error(client, LOG_DEBUG, "RCB %s, in: %zu", from == client->client?"client":"relay", evbuffer_get_length(bufferevent_get_input(from))); if (evbuffer_get_length(bufferevent_get_output(to)) < get_write_hwm(to)) { @@ -295,7 +295,7 @@ int process_shutdown_on_write_(redsocks_client *client, struct bufferevent *from unsigned short from_evshut = from == client->client ? client->client_evshut : client->relay_evshut; unsigned short to_evshut = to == client->client ? client->client_evshut : client->relay_evshut; - redsocks_log_error(client, LOG_DEBUG, "WCB %s, fs: %u, ts: %u, fin: %u, fout: %u, tin: %u", + redsocks_log_error(client, LOG_DEBUG, "WCB %s, fs: %u, ts: %u, fin: %zu, fout: %zu, tin: %zu", to == client->client?"client":"relay", from_evshut, to_evshut, @@ -857,7 +857,7 @@ void redsocks_dump_client(redsocks_client * client, int loglevel) const char *s_client_evshut = redsocks_evshut_str(client->client_evshut); const char *s_relay_evshut = redsocks_evshut_str(client->relay_evshut); - redsocks_log_error(client, loglevel, "client(%i): (%s)%s%s input %u output %u, relay(%i): (%s)%s%s input %u output %u, age: %li sec, idle: %li sec.", + redsocks_log_error(client, loglevel, "client(%i): (%s)%s%s input %zu output %zu, relay(%i): (%s)%s%s input %zu output %zu, age: %li sec, idle: %li sec.", client->client ? bufferevent_getfd(client->client) : -1, redsocks_event_str(client->client ? bufferevent_get_enabled(client->client) : 0), s_client_evshut[0] ? " " : "", s_client_evshut, diff --git a/tcpdns.c b/tcpdns.c index 3ea1ac65..39b23de4 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -105,7 +105,7 @@ static void tcpdns_readcb(struct bufferevent *from, void *_arg) size_t input_size = evbuffer_get_length(bufferevent_get_input(from)); size_t read_size; - tcpdns_log_error(LOG_DEBUG, "response size: %d", input_size); + tcpdns_log_error(LOG_DEBUG, "response size: %zu", input_size); if (input_size == 0 || input_size > sizeof(buff)) // EOF or response is too large. Drop it. From 4b6b488c8b63be4b2a9c6d39385ce4f1125b0b7b Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 17 Feb 2017 17:20:01 +0800 Subject: [PATCH 099/192] Add support of TCP Fast Open to shadowsocks --- shadowsocks.c | 84 +++++++++++++++++++++++++++++++++------------------ utils.c | 70 ++++++++++++++++++++++++++++++++++++++++++ utils.h | 10 ++++++ 3 files changed, 135 insertions(+), 29 deletions(-) diff --git a/shadowsocks.c b/shadowsocks.c index 7d8d12a4..661fb03a 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -38,6 +38,7 @@ typedef struct ss_client_t { struct enc_ctx d_ctx; short e_ctx_init; short d_ctx_init; + bool tfo; } ss_client; typedef struct ss_instance_t { @@ -83,11 +84,10 @@ static void ss_client_fini(redsocks_client *client) static void encrypt_mem(redsocks_client * client, char * data, size_t len, - struct bufferevent * to, int decrypt) + struct evbuffer * buf_out, int decrypt) { ss_client *sclient = (void*)(client + 1); struct evbuffer_iovec vec; - struct evbuffer * buf_out = bufferevent_get_output(to); size_t required; int rc; @@ -124,7 +124,7 @@ static void encrypt_buffer(redsocks_client *client, return; input = (char *)evbuffer_pullup(buf_in, input_size); - encrypt_mem(client, input, input_size, to, 0); + encrypt_mem(client, input, input_size, bufferevent_get_output(to), 0); evbuffer_drain(buf_in, input_size); } @@ -141,7 +141,7 @@ static void decrypt_buffer(redsocks_client * client, return; input = (char *)evbuffer_pullup(buf_in, input_size); - encrypt_mem(client, input, input_size, to, 1); + encrypt_mem(client, input, input_size, bufferevent_get_output(to), 1); evbuffer_drain(buf_in, input_size); } @@ -277,7 +277,6 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; ss_client *sclient = (void*)(client + 1); - ss_instance * ss = (ss_instance *)(client->instance+1); ss_header_ipv4 header; size_t len = 0; @@ -294,19 +293,6 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) client->relay_connected = 1; client->state = ss_connected; - if (enc_ctx_init(&ss->info, &sclient->e_ctx, 1)) { - log_error(LOG_ERR, "Shadowsocks failed to initialize encryption context."); - redsocks_drop_client(client); - return; - } - sclient->e_ctx_init = 1; - if (enc_ctx_init(&ss->info, &sclient->d_ctx, 0)) { - log_error(LOG_ERR, "Shadowsocks failed to initialize decryption context."); - redsocks_drop_client(client); - return; - } - sclient->e_ctx_init = 1; - /* We do not need to detect timeouts any more. The two peers will handle it. */ bufferevent_set_timeouts(client->relay, NULL, NULL); @@ -323,15 +309,15 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) ss_relay_writecb, redsocks_event_error, client); - - /* build and send header */ - // TODO: Better implementation and IPv6 Support - header.addr_type = ss_addrtype_ipv4; - header.addr = client->destaddr.sin_addr.s_addr; - header.port = client->destaddr.sin_port; - len += sizeof(header); - encrypt_mem(client, (char *)&header, len, client->relay, 0); - + if(!sclient->tfo) { + /* build and send header */ + // TODO: Better implementation and IPv6 Support + header.addr_type = ss_addrtype_ipv4; + header.addr = client->destaddr.sin_addr.s_addr; + header.port = client->destaddr.sin_port; + len += sizeof(header); + encrypt_mem(client, (char *)&header, len, bufferevent_get_output(client->relay), 0); + } // Write any data received from client side to relay. if (evbuffer_get_length(bufferevent_get_input(client->client))) ss_relay_writecb(client->relay, client); @@ -343,18 +329,58 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) static int ss_connect_relay(redsocks_client *client) { char * interface = client->instance->config.interface; + ss_client *sclient = (void*)(client + 1); + ss_instance * ss = (ss_instance *)(client->instance+1); + ss_header_ipv4 header; struct timeval tv; + size_t len = 0; + + if (enc_ctx_init(&ss->info, &sclient->e_ctx, 1)) { + log_error(LOG_ERR, "Shadowsocks failed to initialize encryption context."); + redsocks_drop_client(client); + return -1; + } + sclient->e_ctx_init = 1; + if (enc_ctx_init(&ss->info, &sclient->d_ctx, 0)) { + log_error(LOG_ERR, "Shadowsocks failed to initialize decryption context."); + redsocks_drop_client(client); + return -1; + } + sclient->e_ctx_init = 1; + + /* build and send header */ + // TODO: Better implementation and IPv6 Support + header.addr_type = ss_addrtype_ipv4; + header.addr = client->destaddr.sin_addr.s_addr; + header.port = client->destaddr.sin_port; + len += sizeof(header); + char buff[1024]; + size_t sz = sizeof(buff); + if (!ss_encrypt(&sclient->e_ctx, (char *)&header, len, buff, &sz)) { + log_error(LOG_ERR, "Encryption error."); + redsocks_drop_client(client); + return -1; + } + len = sz; tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; - client->relay = red_connect_relay(interface, &client->instance->config.relayaddr, + client->relay = red_connect_relay_tfo(interface, &client->instance->config.relayaddr, NULL, ss_relay_connected, redsocks_event_error, client, - &tv); + &tv, buff, &sz); if (!client->relay) { redsocks_drop_client(client); return -1; } + if (sz && sz == len) { + sclient->tfo = true; + } + else if (sz) { + log_error(LOG_ERR, "Unexpected length of data sent."); + redsocks_drop_client(client); + return -1; + } return 0; } diff --git a/utils.c b/utils.c index bd0f83a8..204dfbaa 100644 --- a/utils.c +++ b/utils.c @@ -216,6 +216,76 @@ struct bufferevent* red_connect_relay(const char *ifname, return NULL; } +struct bufferevent* red_connect_relay_tfo(const char *ifname, + struct sockaddr_in *addr, + bufferevent_data_cb readcb, + bufferevent_data_cb writecb, + bufferevent_event_cb errorcb, + void *cbarg, + const struct timeval *timeout_write, + void *data, + size_t *len) +{ + struct bufferevent *retval = NULL; + int relay_fd = -1; + int error; + + retval = red_prepare_relay(ifname, readcb, writecb, errorcb, cbarg); + if (retval) { + relay_fd = bufferevent_getfd(retval); + if (timeout_write) + bufferevent_set_timeouts(retval, NULL, timeout_write); + +#ifdef MSG_FASTOPEN + size_t s = sendto(relay_fd, data, * len, MSG_FASTOPEN, + (struct sockaddr *)addr, sizeof(*addr) + ); + *len = 0; // Assume nothing sent, caller needs to write data again when connection is setup. + if (s == -1) { + if (errno == EINPROGRESS || errno == EAGAIN + || errno == EWOULDBLOCK) { + // Remote server doesn't support tfo or it's the first connection to the server. + // Connection will automatically fall back to conventional TCP. + log_error(LOG_DEBUG, "TFO: no cookie"); + return retval; + } else if (errno == EOPNOTSUPP || errno == EPROTONOSUPPORT || + errno == ENOPROTOOPT) { + // Disable fast open as it's not supported + log_error(LOG_DEBUG, "TFO: not support"); + goto fallback; + } else { + log_errno(LOG_NOTICE, "sendto"); + goto fail; + } + } + else { + log_error(LOG_DEBUG, "TFO: cookie found"); + *len = s; // data is put into socket buffer + return retval; + } +fallback: +#endif + + *len = 0; // Nothing sent, caller needs to write data again when connection is setup. + error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); + if (error && errno != EINPROGRESS) { + log_errno(LOG_NOTICE, "connect"); + goto fail; + } + } + return retval; + +fail: + if (retval) { + bufferevent_disable(retval, EV_READ|EV_WRITE); + bufferevent_free(retval); + } + if (relay_fd != -1) + redsocks_close(relay_fd); + return NULL; +} + + int red_socket_geterrno(struct bufferevent *buffev) { int error; diff --git a/utils.h b/utils.h index 8fb195b2..ef5ea429 100644 --- a/utils.h +++ b/utils.h @@ -65,6 +65,16 @@ struct bufferevent* red_connect_relay(const char *ifname, bufferevent_event_cb errorcb, void *cbarg, const struct timeval *timeout_write); +struct bufferevent* red_connect_relay_tfo(const char *ifname, + struct sockaddr_in *addr, + bufferevent_data_cb readcb, + bufferevent_data_cb writecb, + bufferevent_event_cb errorcb, + void *cbarg, + const struct timeval *timeout_write, + void *data, + size_t *len); + int red_socket_geterrno(struct bufferevent *buffev); int red_is_socket_connected_ok(struct bufferevent *buffev); int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *fromaddr, struct sockaddr_in *toaddr); From 0b6d35d01c34eab01607bd5fc53d01c93bb6ea74 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 6 Mar 2017 17:34:48 +0800 Subject: [PATCH 100/192] Add source files required for building with Android NDK --- Makefile | 2 +- extra/search.h | 62 ++++++++++++++ extra/tsearch_avl.c | 204 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 extra/search.h create mode 100644 extra/tsearch_avl.c diff --git a/Makefile b/Makefile index 647a8721..d955eb74 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,7 @@ gen/.build: base.c: $(CONF) $(DEPS): $(SRCS) - gcc -MM $(SRCS) 2>/dev/null >$(DEPS) || \ + $(CC) -MM $(SRCS) 2>/dev/null >$(DEPS) || \ ( \ for I in $(wildcard *.h); do \ export $${I//[-.]/_}_DEPS="`sed '/^\#[ \t]*include \?"\(.*\)".*/!d;s//\1/' $$I`"; \ diff --git a/extra/search.h b/extra/search.h new file mode 100644 index 00000000..9059e6f1 --- /dev/null +++ b/extra/search.h @@ -0,0 +1,62 @@ +#ifndef _SEARCH_H +#define _SEARCH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define __NEED_size_t + +typedef enum { FIND, ENTER } ACTION; +typedef enum { preorder, postorder, endorder, leaf } VISIT; + +typedef struct entry { + char *key; + void *data; +} ENTRY; + +int hcreate(size_t); +void hdestroy(void); +ENTRY *hsearch(ENTRY, ACTION); + +#ifdef _GNU_SOURCE +struct hsearch_data { + struct __tab *__tab; + unsigned int __unused1; + unsigned int __unused2; +}; + +int hcreate_r(size_t, struct hsearch_data *); +void hdestroy_r(struct hsearch_data *); +int hsearch_r(ENTRY, ACTION, ENTRY **, struct hsearch_data *); +#endif + +void insque(void *, void *); +void remque(void *); + +void *lsearch(const void *, void *, size_t *, size_t, + int (*)(const void *, const void *)); +void *lfind(const void *, const void *, size_t *, size_t, + int (*)(const void *, const void *)); + +void *tdelete(const void *__restrict, void **__restrict, int(*)(const void *, const void *)); +void *tfind(const void *, void *const *, int(*)(const void *, const void *)); +void *tsearch(const void *, void **, int (*)(const void *, const void *)); +void twalk(const void *, void (*)(const void *, VISIT, int)); + +#ifdef _GNU_SOURCE +struct qelem { + struct qelem *q_forw, *q_back; + char q_data[1]; +}; + +void tdestroy(void *, void (*)(void *)); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extra/tsearch_avl.c b/extra/tsearch_avl.c new file mode 100644 index 00000000..104a65b3 --- /dev/null +++ b/extra/tsearch_avl.c @@ -0,0 +1,204 @@ +#include +#include "search.h" + +/* +avl tree implementation using recursive functions +the height of an n node tree is less than 1.44*log2(n+2)-1 +(so the max recursion depth in case of a tree with 2^32 nodes is 45) +*/ + +struct node { + const void *key; + struct node *left; + struct node *right; + int height; +}; + +static int delta(struct node *n) { + return (n->left ? n->left->height:0) - (n->right ? n->right->height:0); +} + +static void updateheight(struct node *n) { + n->height = 0; + if (n->left && n->left->height > n->height) + n->height = n->left->height; + if (n->right && n->right->height > n->height) + n->height = n->right->height; + n->height++; +} + +static struct node *rotl(struct node *n) { + struct node *r = n->right; + n->right = r->left; + r->left = n; + updateheight(n); + updateheight(r); + return r; +} + +static struct node *rotr(struct node *n) { + struct node *l = n->left; + n->left = l->right; + l->right = n; + updateheight(n); + updateheight(l); + return l; +} + +static struct node *balance(struct node *n) { + int d = delta(n); + + if (d < -1) { + if (delta(n->right) > 0) + n->right = rotr(n->right); + return rotl(n); + } else if (d > 1) { + if (delta(n->left) < 0) + n->left = rotl(n->left); + return rotr(n); + } + updateheight(n); + return n; +} + +static struct node *find(struct node *n, const void *k, + int (*cmp)(const void *, const void *)) +{ + int c; + + if (!n) + return 0; + c = cmp(k, n->key); + if (c == 0) + return n; + if (c < 0) + return find(n->left, k, cmp); + else + return find(n->right, k, cmp); +} + +static struct node *insert(struct node *n, const void *k, + int (*cmp)(const void *, const void *), struct node **found) +{ + struct node *r; + int c; + + if (!n) { + n = malloc(sizeof *n); + if (n) { + n->key = k; + n->left = n->right = 0; + n->height = 1; + } + *found = n; + return n; + } + c = cmp(k, n->key); + if (c == 0) { + *found = n; + return 0; + } + r = insert(c < 0 ? n->left : n->right, k, cmp, found); + if (r) { + if (c < 0) + n->left = r; + else + n->right = r; + r = balance(n); + } + return r; +} + +static struct node *remove_rightmost(struct node *n, struct node **rightmost) +{ + if (!n->right) { + *rightmost = n; + return n->left; + } + n->right = remove_rightmost(n->right, rightmost); + return balance(n); +} + +static struct node *remove(struct node **n, const void *k, + int (*cmp)(const void *, const void *), struct node *parent) +{ + int c; + + if (!*n) + return 0; + c = cmp(k, (*n)->key); + if (c == 0) { + struct node *r = *n; + if (r->left) { + r->left = remove_rightmost(r->left, n); + (*n)->left = r->left; + (*n)->right = r->right; + *n = balance(*n); + } else + *n = r->right; + free(r); + return parent; + } + if (c < 0) + parent = remove(&(*n)->left, k, cmp, *n); + else + parent = remove(&(*n)->right, k, cmp, *n); + if (parent) + *n = balance(*n); + return parent; +} + +void *tdelete(const void *restrict key, void **restrict rootp, + int(*compar)(const void *, const void *)) +{ + if (!rootp) + return 0; + struct node *n = *rootp; + struct node *ret; + /* last argument is arbitrary non-null pointer + which is returned when the root node is deleted */ + ret = remove(&n, key, compar, n); + *rootp = n; + return ret; +} + +void *tfind(const void *key, void *const *rootp, + int(*compar)(const void *, const void *)) +{ + if (!rootp) + return 0; + return find(*rootp, key, compar); +} + +void *tsearch(const void *key, void **rootp, + int (*compar)(const void *, const void *)) +{ + struct node *update; + struct node *ret; + if (!rootp) + return 0; + update = insert(*rootp, key, compar, &ret); + if (update) + *rootp = update; + return ret; +} + +static void walk(const struct node *r, void (*action)(const void *, VISIT, int), int d) +{ + if (r == 0) + return; + if (r->left == 0 && r->right == 0) + action(r, leaf, d); + else { + action(r, preorder, d); + walk(r->left, action, d+1); + action(r, postorder, d); + walk(r->right, action, d+1); + action(r, endorder, d); + } +} + +void twalk(const void *root, void (*action)(const void *, VISIT, int)) +{ + walk(root, action, 0); +} From 2e7d09372c35bd52904d4494fb4b59992b8f5907 Mon Sep 17 00:00:00 2001 From: Leonid Evdokimov Date: Thu, 17 Mar 2016 18:48:08 +0300 Subject: [PATCH 101/192] Emit better errors from config file parser --- parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.c b/parser.c index 8929f4cf..fc2b58bd 100644 --- a/parser.c +++ b/parser.c @@ -516,7 +516,7 @@ int parser_run(parser_context *context) parser_error(context, "section->onenter failed"); } else { - parser_error(context, "unknown section"); + parser_error(context, "unknown section <%s>", section_token); } FREE(section_token); } From 94a942108d83d7cfb0794a32389ded837acf74e5 Mon Sep 17 00:00:00 2001 From: Leonid Evdokimov Date: Mon, 21 Mar 2016 11:10:11 +0300 Subject: [PATCH 102/192] Mention `nogroup' name difference between RedHat-like and Debian-like distributions. See also #60 --- redsocks.conf.example | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redsocks.conf.example b/redsocks.conf.example index 48a40e71..a0ce0a31 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -21,6 +21,9 @@ base { * privilegies on startup. * Note, your chroot may requre /etc/localtime if you write log to syslog. * Log is opened before chroot & uid changing. + * Debian, Ubuntu and some other distributions use `nogroup` instead of + * `nobody`, so change it according to your system if you want redsocks + * to drop root privileges. */ // user = nobody; // group = nobody; From b12b102853aa0a3a5edb33c535e3a70cdb23269a Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 15 Mar 2017 15:47:29 +0800 Subject: [PATCH 103/192] Fix: shadowsocks not work with non-TFO enabled servers --- shadowsocks.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/shadowsocks.c b/shadowsocks.c index 661fb03a..d02f9fa9 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -39,6 +39,8 @@ typedef struct ss_client_t { short e_ctx_init; short d_ctx_init; bool tfo; + size_t tfo_size; + char tfo_buff[512]; } ss_client; typedef struct ss_instance_t { @@ -277,8 +279,6 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; ss_client *sclient = (void*)(client + 1); - ss_header_ipv4 header; - size_t len = 0; assert(buffev == client->relay); assert(client->state == ss_new); @@ -310,13 +310,7 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) redsocks_event_error, client); if(!sclient->tfo) { - /* build and send header */ - // TODO: Better implementation and IPv6 Support - header.addr_type = ss_addrtype_ipv4; - header.addr = client->destaddr.sin_addr.s_addr; - header.port = client->destaddr.sin_port; - len += sizeof(header); - encrypt_mem(client, (char *)&header, len, bufferevent_get_output(client->relay), 0); + bufferevent_write(client->relay, &sclient->tfo_buff[0], sclient->tfo_size); } // Write any data received from client side to relay. if (evbuffer_get_length(bufferevent_get_input(client->client))) @@ -354,20 +348,19 @@ static int ss_connect_relay(redsocks_client *client) header.addr = client->destaddr.sin_addr.s_addr; header.port = client->destaddr.sin_port; len += sizeof(header); - char buff[1024]; - size_t sz = sizeof(buff); - if (!ss_encrypt(&sclient->e_ctx, (char *)&header, len, buff, &sz)) { + size_t sz = sizeof(sclient->tfo_buff); + if (!ss_encrypt(&sclient->e_ctx, (char *)&header, len, &sclient->tfo_buff[0], &sz)) { log_error(LOG_ERR, "Encryption error."); redsocks_drop_client(client); return -1; } - len = sz; + sclient->tfo_size = len = sz; tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; client->relay = red_connect_relay_tfo(interface, &client->instance->config.relayaddr, NULL, ss_relay_connected, redsocks_event_error, client, - &tv, buff, &sz); + &tv, &sclient->tfo_buff[0], &sz); if (!client->relay) { redsocks_drop_client(client); From e1824c8e6bf9b136cbadbced7f8f1eb7f36cbc33 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 8 Jun 2017 17:53:16 +0800 Subject: [PATCH 104/192] Introduce HTTPS proxy support and fix some bugs --- Makefile | 9 +- README.md | 8 +- autoproxy.c | 4 +- direct.c | 2 +- http-connect.c | 25 +---- http-relay.c | 10 +- https-connect.c | 247 ++++++++++++++++++++++++++++++++++++++++++ ipcache.c | 2 +- main.c | 14 +++ redsocks.c | 12 +- redsocks.conf.example | 10 +- redudp.c | 2 +- shadowsocks-udp.c | 2 +- shadowsocks.c | 2 +- socks5-udp.c | 2 +- tcpdns.c | 2 +- utils.c | 93 +++++++++++++--- utils.h | 14 +++ 18 files changed, 397 insertions(+), 63 deletions(-) create mode 100644 https-connect.c diff --git a/Makefile b/Makefile index d955eb74..601bf1cc 100644 --- a/Makefile +++ b/Makefile @@ -22,10 +22,15 @@ override CFLAGS += -DUSE_CRYPTO_POLARSSL $(info Compile with PolarSSL.) CRYPTO := PolarSSL else -override LIBS += -lssl -lcrypto -override CFLAGS += -DUSE_CRYPTO_OPENSSL $(info Compile with OpenSSL by default. To compile with PolarSSL, run 'make USE_CRYPTO_POLARSSL=true' instead.) CRYPTO := OpenSSL +ifdef ENABLE_HTTPS_PROXY +override OBJS += https-connect.o +override CFLAGS += -DENABLE_HTTPS_PROXY +$(info Compile with HTTPS proxy enabled.) +endif +override LIBS += -levent -lssl -lcrypto +override CFLAGS += -DUSE_CRYPTO_OPENSSL endif ifdef ENABLE_STATIC override LIBS += -ldl -lz diff --git a/README.md b/README.md index 93efa6d1..006f4588 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ need of blacklist. 4. Redirect TCP connections without proxy. 5. Redirect TCP connections via specified network interface. 6. UDP transparent proxy via shadowsocks proxy. -7. Support Ful-cone NAT Traversal when working with shadowsocks proxy. +7. Support Ful-cone NAT Traversal when working with shadowsocks or socks5 proxy. +8. Integrated HTTPS proxy support(HTTP CONNECT over SSL). [Chinese Reference](https://github.com/semigodking/redsocks/wiki) @@ -37,6 +38,11 @@ To compile static binaries (with Tomatoware) make ENABLE_STATIC=true +By default, HTTPS proxy support is disabled. To enable this feature, you need to +compile like (Require libevent2 compiled with OpenSSL support): + + make ENABLE_HTTPS_PROXY=true + Since this variant of redsocks is customized for running with Openwrt, please read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how to cross compile. diff --git a/autoproxy.c b/autoproxy.c index 3ee40520..bbc60cc3 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -1,5 +1,5 @@ /* redsocks2 - transparent TCP-to-proxy redirector - * Copyright (C) 2013-2015 Zhuofei Wang + * Copyright (C) 2013-2017 Zhuofei Wang * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"); you may not @@ -518,7 +518,7 @@ static int auto_retry(redsocks_client * client, int updcache) // it maybe is waiting for client read event. // Take 'http-relay' for example. if (!rc && !client->relay && evbuffer_get_length(bufferevent_get_input(client->client))) -#ifdef bufferevent_trigger_event +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 bufferevent_trigger_event(client->client, EV_READ, 0); #else client->client->readcb(client->client, client); diff --git a/direct.c b/direct.c index 8a96780e..b2260dbd 100644 --- a/direct.c +++ b/direct.c @@ -1,5 +1,5 @@ /* redsocks2 - transparent TCP-to-proxy redirector - * Copyright (C) 2013-2014 Zhuofei Wang + * Copyright (C) 2013-2017 Zhuofei Wang * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"); you may not diff --git a/http-connect.c b/http-connect.c index ec559c9a..8785c1d5 100644 --- a/http-connect.c +++ b/http-connect.c @@ -40,7 +40,7 @@ typedef enum httpc_state_t { } httpc_state; -#define HTTP_HEAD_WM_HIGH 4096 // that should be enough for one HTTP line. +#define HTTP_HEAD_WM_HIGH 8192 // that should be enough for one HTTP line. static void httpc_client_init(redsocks_client *client) @@ -55,25 +55,10 @@ static void httpc_instance_fini(redsocks_instance *instance) auth->last_auth_query = NULL; } -static struct evbuffer *httpc_mkconnect(redsocks_client *client); - extern const char *auth_request_header; extern const char *auth_response_header; -static char *get_auth_request_header(struct evbuffer *buf) -{ - char *line; - for (;;) { - line = redsocks_evbuffer_readline(buf); - if (line == NULL || *line == '\0' || strchr(line, ':') == NULL) { - free(line); - return NULL; - } - if (strncasecmp(line, auth_request_header, strlen(auth_request_header)) == 0) - return line; - free(line); - } -} +extern char *get_auth_request_header(struct evbuffer *buf); void httpc_read_cb(struct bufferevent *buffev, void *_arg) { @@ -87,7 +72,7 @@ void httpc_read_cb(struct bufferevent *buffev, void *_arg) if (client->state == httpc_request_sent) { size_t len = evbuffer_get_length(evbinput); - char *line = redsocks_evbuffer_readline(evbinput); + char *line = evbuffer_readln(evbinput, NULL, EVBUFFER_EOL_CRLF_STRICT); if (line) { unsigned int code; if (sscanf(line, "HTTP/%*u.%*u %u", &code) == 1) { // 1 == one _assigned_ match @@ -165,7 +150,7 @@ void httpc_read_cb(struct bufferevent *buffev, void *_arg) return; while (client->state == httpc_reply_came) { - char *line = redsocks_evbuffer_readline(evbinput); + char *line = evbuffer_readln(evbinput, NULL, EVBUFFER_EOL_CRLF_STRICT); if (line) { if (strlen(line) == 0) { client->state = httpc_headers_skipped; @@ -182,7 +167,7 @@ void httpc_read_cb(struct bufferevent *buffev, void *_arg) } } -static struct evbuffer *httpc_mkconnect(redsocks_client *client) +struct evbuffer *httpc_mkconnect(redsocks_client *client) { struct evbuffer *buff = NULL, *retval = NULL; int len; diff --git a/http-relay.c b/http-relay.c index 9e6ce93e..a64b6ca1 100644 --- a/http-relay.c +++ b/http-relay.c @@ -129,11 +129,11 @@ static void httpr_instance_fini(redsocks_instance *instance) auth->last_auth_query = NULL; } -static char *get_auth_request_header(struct evbuffer *buf) +char *get_auth_request_header(struct evbuffer *buf) { char *line; for (;;) { - line = redsocks_evbuffer_readline(buf); + line = evbuffer_readln(buf, NULL, EVBUFFER_EOL_CRLF); if (line == NULL || *line == '\0' || strchr(line, ':') == NULL) { free(line); return NULL; @@ -160,7 +160,7 @@ static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg) if (client->state == httpr_request_sent) { size_t len = evbuffer_get_length(evbinput); - char *line = redsocks_evbuffer_readline(evbinput); + char *line = evbuffer_readln(evbinput, NULL, EVBUFFER_EOL_CRLF_STRICT); if (line) { httpr_buffer_append(&httpr->relay_buffer, line, strlen(line)); httpr_buffer_append(&httpr->relay_buffer, "\r\n", 2); @@ -241,7 +241,7 @@ static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg) return; while (client->state == httpr_reply_came) { - char *line = redsocks_evbuffer_readline(evbinput); + char *line = evbuffer_readln(evbinput, NULL, EVBUFFER_EOL_CRLF_STRICT); if (line) { httpr_buffer_append(&httpr->relay_buffer, line, strlen(line)); httpr_buffer_append(&httpr->relay_buffer, "\r\n", 2); @@ -496,7 +496,7 @@ static void httpr_client_read_cb(struct bufferevent *buffev, void *_arg) char *line = NULL; int connect_relay = 0; - while (!connect_relay && (line = redsocks_evbuffer_readline(bufferevent_get_input(buffev)))) { + while (!connect_relay && (line = evbuffer_readln(bufferevent_get_input(buffev), NULL, EVBUFFER_EOL_CRLF_STRICT))) { int skip_line = 0; int do_drop = 0; diff --git a/https-connect.c b/https-connect.c new file mode 100644 index 00000000..a7067ff1 --- /dev/null +++ b/https-connect.c @@ -0,0 +1,247 @@ +/* redsocks2 - transparent TCP-to-proxy redirector + * Copyright (C) 2013-2017 Zhuofei Wang + * + * This code is based on redsocks project developed by Leonid Evdokimov. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "redsocks.h" +#include "http-auth.h" + +#ifndef EVENT__HAVE_OPENSSL +#error The libevent2 you are compiling with does not have OpenSSL enabled! +#endif + +typedef enum httpsc_state_t { + httpc_new, + httpc_request_sent, + httpc_reply_came, + httpc_headers_skipped, + httpc_MAX, +} httpc_state; + +typedef struct httpsc_client_t { + SSL * ssl; +} httpsc_client; + +typedef struct httpsc_instance_t { + http_auth auth; + SSL_CTX * ctx; +} httpsc_instance; + +#define HTTP_HEAD_WM_HIGH 8192 // that should be enough for one HTTP line. + + +static void log_ssl_error(redsocks_client *client, struct bufferevent * buffev) +{ + unsigned long err; + while ((err = (bufferevent_get_openssl_error(buffev)))) { + const char *msg = (const char*) + ERR_reason_error_string(err); + const char *lib = (const char*) + ERR_lib_error_string(err); + const char *func = (const char*) + ERR_func_error_string(err); + redsocks_log_errno(client, LOG_DEBUG, "SSL Error: %s %s: %s", lib, func, msg); + } +} + +static void httpsc_client_init(redsocks_client *client) +{ + client->state = httpc_new; +} + +static void httpsc_client_fini(redsocks_client *client) +{ + httpsc_client *sclient = (void*)(client + 1); + struct bufferevent * underlying = NULL; + + if (client->relay) { + underlying = bufferevent_get_underlying(client->relay); + if (underlying) { + bufferevent_free(client->relay); + client->relay = underlying; + } + } + if (sclient->ssl) { + SSL_free(sclient->ssl); + sclient->ssl = NULL; + } +} + +static int httpsc_instance_init(struct redsocks_instance_t *instance) +{ + httpsc_instance * httpsc = (httpsc_instance *)(instance + 1); + SSL_CTX * ctx = NULL; + + ctx = SSL_CTX_new(SSLv3_client_method()); + if (!ctx) + { + unsigned long err = ERR_get_error(); + log_error(LOG_ERR, "Failed to allocate SSL context. SSL Error: %s", ERR_lib_error_string(err)); + return -1; + } + httpsc->ctx = ctx; + return 0; +} + +static void httpsc_instance_fini(redsocks_instance *instance) +{ + httpsc_instance * httpsc = (httpsc_instance *)(instance + 1); + + free(httpsc->auth.last_auth_query); + httpsc->auth.last_auth_query = NULL; + if (httpsc->ctx) { + SSL_CTX_free (httpsc->ctx); + httpsc->ctx = NULL; + } +} + +extern struct evbuffer *httpc_mkconnect(redsocks_client *client); +extern void httpc_read_cb(struct bufferevent *buffev, void *_arg); + +static void httpsc_event_cb(struct bufferevent *buffev, short what, void *_arg) +{ + redsocks_client *client = _arg; + assert(buffev == client->relay || buffev == client->client); + + redsocks_touch_client(client); + + if (!(what & BEV_EVENT_ERROR)) + errno = red_socket_geterrno(buffev); + else if (!bufferevent_openssl_get_allow_dirty_shutdown(client->relay)) + log_ssl_error(client, client->relay); + redsocks_log_errno(client, LOG_DEBUG, "%s, what: " event_fmt_str, + buffev == client->client?"client":"relay", + event_fmt(what)); + + if (what == (BEV_EVENT_READING|BEV_EVENT_EOF)) { + redsocks_shutdown(client, buffev, SHUT_RD, 1); + // Ensure the other party could send remaining data and SHUT_WR also + if (buffev == client->client) + { + if (!(client->relay_evshut & EV_WRITE) && client->relay_connected) + // when we got EOF from client, we need to shutdown relay's write + process_shutdown_on_write_(client, client->client, client->relay); + } + else + { + if (bufferevent_openssl_get_allow_dirty_shutdown(client->relay)) + log_ssl_error(client, client->relay); + if (!(client->client_evshut & EV_WRITE)) + bufferevent_enable(client->client, EV_WRITE); + } + } + else if (what == BEV_EVENT_CONNECTED) { + // usually this event is not generated as 'connect' is used to + // setup connection. For openssl socket, this event is generated. + client->relay_connected = 1; + if (!process_shutdown_on_write_(client, client->client, client->relay)) { + /* We do not need to detect timeouts any more. + The two peers will handle it. */ + bufferevent_set_timeouts(client->relay, NULL, NULL); + redsocks_write_helper_ex( + buffev, client, + httpc_mkconnect, httpc_request_sent, 1, HTTP_HEAD_WM_HIGH + ); + } + } + else { + redsocks_drop_client(client); + } +} + +static void httpsc_read_cb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + + httpc_read_cb(buffev, _arg); + + if (client->state == httpc_headers_skipped) { + bufferevent_data_cb read_cb, write_cb; + +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 + bufferevent_getcb(client->client, &read_cb, &write_cb, NULL, NULL); +#else + read_cb = client->client->readcb; + write_cb = client->client->writecb; +#endif + bufferevent_setcb(client->client, read_cb, write_cb, httpsc_event_cb, client); + if (client->client) { + struct evbuffer * input = bufferevent_get_input(client->client); + if (evbuffer_get_length(input)) +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 + bufferevent_trigger(client->relay, EV_WRITE, 0); +#else + client->relay->writecb(client->relay, client); +#endif + } + } +} + +static int httpsc_connect_relay(redsocks_client *client) +{ + httpsc_client *sclient = (void*)(client + 1); + httpsc_instance *httpsc = (httpsc_instance *)(client->instance + 1); + char * interface = client->instance->config.interface; + struct timeval tv = {client->instance->config.timeout, 0}; + + sclient->ssl = SSL_new(httpsc->ctx); + + // Allowing binding relay socket to specified IP for outgoing connections + client->relay = red_connect_relay_ssl(interface, &client->instance->config.relayaddr, + sclient->ssl, + httpsc_read_cb, + NULL, + httpsc_event_cb, client, &tv); + if (!client->relay) { + redsocks_log_errno(client, LOG_ERR, "red_connect_relay_ssl"); + redsocks_drop_client(client); + return -1; + } + + return 0; +} + +relay_subsys https_connect_subsys = +{ + .name = "https-connect", + .payload_len = sizeof(httpsc_client), + .instance_payload_len = sizeof(httpsc_instance), + .readcb = httpsc_read_cb, + .init = httpsc_client_init, + .fini = httpsc_client_fini, + .connect_relay = httpsc_connect_relay, + .instance_init = httpsc_instance_init, + .instance_fini = httpsc_instance_fini, +}; + +/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ +/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/ipcache.c b/ipcache.c index 7c99aa2f..e4d444c6 100644 --- a/ipcache.c +++ b/ipcache.c @@ -1,5 +1,5 @@ /* redsocks2 - transparent TCP-to-proxy redirector - * Copyright (C) 2013-2015 Zhuofei Wang + * Copyright (C) 2013-2017 Zhuofei Wang * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"); you may not diff --git a/main.c b/main.c index 30106831..2f523134 100644 --- a/main.c +++ b/main.c @@ -22,6 +22,10 @@ #include #include #include +#if defined(ENABLE_HTTPS_PROXY) +#include +#include +#endif #include "log.h" #include "main.h" #include "utils.h" @@ -159,6 +163,12 @@ int main(int argc, char **argv) } } +#if defined(ENABLE_HTTPS_PROXY) + SSL_library_init (); + ERR_load_crypto_strings(); + SSL_load_error_strings (); + OpenSSL_add_all_algorithms (); +#endif // Wait for network ready before further initializations so that // parser can resolve domain names. if (wait) @@ -268,6 +278,10 @@ int main(int argc, char **argv) if (g_event_base) event_base_free(g_event_base); +#if defined(ENABLE_HTTPS_PROXY) + EVP_cleanup(); + ERR_free_strings(); +#endif return !error ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/redsocks.c b/redsocks.c index a77a7ffa..f284e233 100644 --- a/redsocks.c +++ b/redsocks.c @@ -1,5 +1,5 @@ /* redsocks2 - transparent TCP/UDP-to-proxy redirector - * Copyright (C) 2013-2015 Zhuofei Wang + * Copyright (C) 2013-2017 Zhuofei Wang * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"). @@ -50,6 +50,9 @@ void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg); extern relay_subsys direct_connect_subsys; extern relay_subsys http_connect_subsys; +#if defined(ENABLE_HTTPS_PROXY) +extern relay_subsys https_connect_subsys; +#endif extern relay_subsys http_relay_subsys; extern relay_subsys socks4_subsys; extern relay_subsys socks5_subsys; @@ -62,6 +65,9 @@ static relay_subsys *relay_subsystems[] = &socks4_subsys, &socks5_subsys, &shadowsocks_subsys, +#if defined(ENABLE_HTTPS_PROXY) + &https_connect_subsys, +#endif }; extern relay_subsys autoproxy_subsys; @@ -362,7 +368,7 @@ int redsocks_start_relay(redsocks_client *client) bufferevent_setwatermark(client->client, EV_READ|EV_WRITE, 0, REDSOCKS_RELAY_HALFBUFF); bufferevent_setwatermark(client->relay, EV_READ|EV_WRITE, 0, REDSOCKS_RELAY_HALFBUFF); -#ifdef bufferevent_getcb +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 bufferevent_getcb(client->client, NULL, NULL, &event_cb, NULL); #else event_cb = client->client->errorcb; @@ -371,7 +377,7 @@ int redsocks_start_relay(redsocks_client *client) redsocks_relay_clientwritecb, event_cb, client); -#ifdef bufferevent_getcb +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 bufferevent_getcb(client->relay, NULL, NULL, &event_cb, NULL); #else event_cb = client->relay->errorcb; diff --git a/redsocks.conf.example b/redsocks.conf.example index a0ce0a31..0d4f588a 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -73,13 +73,13 @@ redsocks { port = 1080; // known types: socks4, socks5, http-connect, http-relay - // New types: direct, shadowsocks + // New types: direct, shadowsocks, https-connect type = socks5; - // Specify interface for outgoing connections. - // This is useful when you have multiple connections to - // internet or when you have VPN connections. - // interface = tun0; + // Specify interface for outgoing connections. + // This is useful when you have multiple connections to + // internet or when you have VPN connections. + // interface = tun0; // Change this parameter to 1 if you want auto proxy feature. // When autoproxy is set to non-zero, the connection to target diff --git a/redudp.c b/redudp.c index c78ca3c9..cf014c3f 100644 --- a/redudp.c +++ b/redudp.c @@ -1,5 +1,5 @@ /* redsocks2 - transparent TCP/UDP-to-proxy redirector - * Copyright (C) 2013-2015 Zhuofei Wang + * Copyright (C) 2013-2017 Zhuofei Wang * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"). diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index a6a0f8d9..131503b9 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -1,5 +1,5 @@ /* redsocks2 - transparent TCP-to-proxy redirector - * Copyright (C) 2013-2015 Zhuofei Wang + * Copyright (C) 2013-2017 Zhuofei Wang * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"); you may not diff --git a/shadowsocks.c b/shadowsocks.c index d02f9fa9..d42f6595 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -1,5 +1,5 @@ /* redsocks2 - transparent TCP-to-proxy redirector - * Copyright (C) 2013-2015 Zhuofei Wang + * Copyright (C) 2013-2017 Zhuofei Wang * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"); you may not diff --git a/socks5-udp.c b/socks5-udp.c index e8ef910a..0ee459f5 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -1,5 +1,5 @@ /* redsocks2 - transparent TCP-to-proxy redirector - * Copyright (C) 2013-2015 Zhuofei Wang + * Copyright (C) 2013-2017 Zhuofei Wang * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"); you may not diff --git a/tcpdns.c b/tcpdns.c index 39b23de4..be1fff7d 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -1,5 +1,5 @@ /* redsocks2 - transparent TCP-to-proxy redirector - * Copyright (C) 2013-2014 Zhuofei Wang + * Copyright (C) 2013-2017 Zhuofei Wang * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"); you may not diff --git a/utils.c b/utils.c index 204dfbaa..ca573089 100644 --- a/utils.c +++ b/utils.c @@ -95,9 +95,9 @@ int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inadd uint32_t red_randui32() { - uint32_t ret; - evutil_secure_rng_get_bytes(&ret, sizeof(ret)); - return ret; + uint32_t ret; + evutil_secure_rng_get_bytes(&ret, sizeof(ret)); + return ret; } time_t redsocks_time(time_t *t) @@ -109,15 +109,6 @@ time_t redsocks_time(time_t *t) return retval; } -char *redsocks_evbuffer_readline(struct evbuffer *buf) -{ -#if _EVENT_NUMERIC_VERSION >= 0x02000000 - return evbuffer_readln(buf, NULL, EVBUFFER_EOL_CRLF); -#else - return evbuffer_readline(buf); -#endif -} - struct bufferevent* red_prepare_relay(const char *ifname, bufferevent_data_cb readcb, bufferevent_data_cb writecb, @@ -157,10 +148,12 @@ struct bufferevent* red_prepare_relay(const char *ifname, } bufferevent_setcb(retval, readcb, writecb, errorcb, cbarg); - error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... - if (error) { - log_errno(LOG_ERR, "bufferevent_enable"); - goto fail; + if (writecb) { + error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... + if (error) { + log_errno(LOG_ERR, "bufferevent_enable"); + goto fail; + } } if (apply_tcp_keepalive(relay_fd)) @@ -216,6 +209,70 @@ struct bufferevent* red_connect_relay(const char *ifname, return NULL; } +#if defined(ENABLE_HTTPS_PROXY) +struct bufferevent* red_connect_relay_ssl(const char *ifname, + struct sockaddr_in *addr, + SSL * ssl, + bufferevent_data_cb readcb, + bufferevent_data_cb writecb, + bufferevent_event_cb errorcb, + void *cbarg, + const struct timeval *timeout_write) +{ + struct bufferevent *retval = NULL; + struct bufferevent *underlying = NULL; + int relay_fd = -1; + int error; + + underlying = red_prepare_relay(ifname, NULL, NULL, NULL, NULL); + if (!underlying) + goto fail; + relay_fd = bufferevent_getfd(underlying); + if (timeout_write) + bufferevent_set_timeouts(underlying, NULL, timeout_write); + + error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); + if (error && errno != EINPROGRESS) { + log_errno(LOG_NOTICE, "connect"); + goto fail; + } + retval = bufferevent_openssl_filter_new(bufferevent_get_base(underlying), + underlying, + ssl, + BUFFEREVENT_SSL_CONNECTING, + BEV_OPT_DEFER_CALLBACKS); + if (!retval) { + log_errno(LOG_NOTICE, "bufferevent_openssl_filter_new"); + goto fail; + } + if (timeout_write) + bufferevent_set_timeouts(retval, NULL, timeout_write); + + bufferevent_setcb(retval, readcb, writecb, errorcb, cbarg); + if (writecb) { + error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... + if (error) { + log_errno(LOG_ERR, "bufferevent_enable"); + goto fail; + } + } + return retval; + +fail: + if (retval) { + bufferevent_disable(retval, EV_READ|EV_WRITE); + bufferevent_free(retval); + } + if (underlying) { + bufferevent_disable(underlying, EV_READ|EV_WRITE); + bufferevent_free(underlying); + } + if (relay_fd != -1) + redsocks_close(relay_fd); + return NULL; +} +#endif + struct bufferevent* red_connect_relay_tfo(const char *ifname, struct sockaddr_in *addr, bufferevent_data_cb readcb, @@ -325,7 +382,7 @@ char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_si uint16_t port; const char placeholder[] = "???:???"; - assert(buffer_size >= sizeof(placeholder)); + assert(buffer_size >= RED_INET_ADDRSTRLEN); memset(buffer, 0, buffer_size); if (sa->sin_family == AF_INET) { @@ -398,7 +455,7 @@ size_t copy_evbuffer(struct bufferevent * dst, struct bufferevent * src, size_t size_t get_write_hwm(struct bufferevent *bufev) { -#ifdef bufferevent_getwatermark +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 size_t high; bufferevent_getwatermark(bufev, EV_WRITE, NULL, &high); return high; diff --git a/utils.h b/utils.h index ef5ea429..dab4f7c8 100644 --- a/utils.h +++ b/utils.h @@ -7,6 +7,10 @@ #include #include #include +#if defined(ENABLE_HTTPS_PROXY) +#include +#include +#endif struct sockaddr_in; @@ -65,6 +69,16 @@ struct bufferevent* red_connect_relay(const char *ifname, bufferevent_event_cb errorcb, void *cbarg, const struct timeval *timeout_write); +#if defined(ENABLE_HTTPS_PROXY) +struct bufferevent* red_connect_relay_ssl(const char *ifname, + struct sockaddr_in *addr, + SSL * ssl, + bufferevent_data_cb readcb, + bufferevent_data_cb writecb, + bufferevent_event_cb errorcb, + void *cbarg, + const struct timeval *timeout_write); +#endif struct bufferevent* red_connect_relay_tfo(const char *ifname, struct sockaddr_in *addr, bufferevent_data_cb readcb, From 0b352a2ab0b4a12b7cdf28b68ade43ea47048e57 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 9 Jun 2017 16:32:08 +0800 Subject: [PATCH 105/192] Failed to compile with libevent 2.0 if HTTPS proxy enabled --- https-connect.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/https-connect.c b/https-connect.c index a7067ff1..a2558617 100644 --- a/https-connect.c +++ b/https-connect.c @@ -136,7 +136,11 @@ static void httpsc_event_cb(struct bufferevent *buffev, short what, void *_arg) if (!(what & BEV_EVENT_ERROR)) errno = red_socket_geterrno(buffev); +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 else if (!bufferevent_openssl_get_allow_dirty_shutdown(client->relay)) +#else + else +#endif log_ssl_error(client, client->relay); redsocks_log_errno(client, LOG_DEBUG, "%s, what: " event_fmt_str, buffev == client->client?"client":"relay", @@ -153,7 +157,9 @@ static void httpsc_event_cb(struct bufferevent *buffev, short what, void *_arg) } else { +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 if (bufferevent_openssl_get_allow_dirty_shutdown(client->relay)) +#endif log_ssl_error(client, client->relay); if (!(client->client_evshut & EV_WRITE)) bufferevent_enable(client->client, EV_WRITE); From 499ea4f82a3f0d4d52da767eed319bf7e6a758ad Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 9 Jun 2017 23:22:33 +0800 Subject: [PATCH 106/192] Fix: missing link library Official libevent is usually built with automake. To link with offical libevent when HTTPS proxy is enabled, libevent_openssl.so is required. --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 601bf1cc..8ed90649 100644 --- a/Makefile +++ b/Makefile @@ -26,10 +26,11 @@ $(info Compile with OpenSSL by default. To compile with PolarSSL, run 'make USE_ CRYPTO := OpenSSL ifdef ENABLE_HTTPS_PROXY override OBJS += https-connect.o +override LIBS += -levent_openssl override CFLAGS += -DENABLE_HTTPS_PROXY $(info Compile with HTTPS proxy enabled.) endif -override LIBS += -levent -lssl -lcrypto +override LIBS += -lssl -lcrypto override CFLAGS += -DUSE_CRYPTO_OPENSSL endif ifdef ENABLE_STATIC From 1d0ddc08fc9846a45f1300ad5dca761545ad0cfd Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 10 Jun 2017 10:45:33 +0800 Subject: [PATCH 107/192] Allow SSLv2 --- https-connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/https-connect.c b/https-connect.c index a2558617..6a1f4cac 100644 --- a/https-connect.c +++ b/https-connect.c @@ -101,7 +101,7 @@ static int httpsc_instance_init(struct redsocks_instance_t *instance) httpsc_instance * httpsc = (httpsc_instance *)(instance + 1); SSL_CTX * ctx = NULL; - ctx = SSL_CTX_new(SSLv3_client_method()); + ctx = SSL_CTX_new(SSLv23_client_method()); if (!ctx) { unsigned long err = ERR_get_error(); From 097fbf650a4622ae2294473f1edb091f6d267c4b Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 10 Jun 2017 19:41:01 +0800 Subject: [PATCH 108/192] Fix bugs in HTTPS proxy --- http-connect.c | 5 ++++- https-connect.c | 29 +++++++++++++++++++---------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/http-connect.c b/http-connect.c index 8785c1d5..cf98fdbf 100644 --- a/http-connect.c +++ b/http-connect.c @@ -126,7 +126,10 @@ void httpc_read_cb(struct bufferevent *buffev, void *_arg) client->state = httpc_new; /* and reconnect */ - redsocks_connect_relay(client); + if (client->instance->relay_ss->connect_relay) + client->instance->relay_ss->connect_relay(client); + else + redsocks_connect_relay(client); return; } } diff --git a/https-connect.c b/https-connect.c index 6a1f4cac..e122a436 100644 --- a/https-connect.c +++ b/https-connect.c @@ -169,15 +169,13 @@ static void httpsc_event_cb(struct bufferevent *buffev, short what, void *_arg) // usually this event is not generated as 'connect' is used to // setup connection. For openssl socket, this event is generated. client->relay_connected = 1; - if (!process_shutdown_on_write_(client, client->client, client->relay)) { - /* We do not need to detect timeouts any more. - The two peers will handle it. */ - bufferevent_set_timeouts(client->relay, NULL, NULL); - redsocks_write_helper_ex( - buffev, client, - httpc_mkconnect, httpc_request_sent, 1, HTTP_HEAD_WM_HIGH - ); - } + /* We do not need to detect timeouts any more. + The two peers will handle it. */ + bufferevent_set_timeouts(client->relay, NULL, NULL); + redsocks_write_helper_ex( + buffev, client, + httpc_mkconnect, httpc_request_sent, 0, HTTP_HEAD_WM_HIGH + ); } else { redsocks_drop_client(client); @@ -206,12 +204,22 @@ static void httpsc_read_cb(struct bufferevent *buffev, void *_arg) #if LIBEVENT_VERSION_NUMBER >= 0x02010100 bufferevent_trigger(client->relay, EV_WRITE, 0); #else - client->relay->writecb(client->relay, client); + if (client->relay->writecb) + client->relay->writecb(client->relay, client); #endif } } } +static void httpsc_write_cb(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + struct bufferevent * from = client->client; + struct bufferevent * to = client->relay; + + process_shutdown_on_write_(client, from, to); +} + static int httpsc_connect_relay(redsocks_client *client) { httpsc_client *sclient = (void*)(client + 1); @@ -242,6 +250,7 @@ relay_subsys https_connect_subsys = .payload_len = sizeof(httpsc_client), .instance_payload_len = sizeof(httpsc_instance), .readcb = httpsc_read_cb, + .writecb = httpsc_write_cb, .init = httpsc_client_init, .fini = httpsc_client_fini, .connect_relay = httpsc_connect_relay, From 2ac71a5e39bf698a2e118691cf473d9ec902b2bf Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 16 Jun 2017 15:13:00 +0800 Subject: [PATCH 109/192] Do not access bufferevent internals when compiling with Libevent 2.1+ --- direct.c | 6 ++++-- http-relay.c | 2 +- https-connect.c | 23 ++++++++--------------- redsocks.c | 7 ++++++- socks5-udp.c | 11 +++++------ utils.c | 40 ++++++++++++++++++++++++++++++++++++++++ utils.h | 3 +++ 7 files changed, 67 insertions(+), 25 deletions(-) diff --git a/direct.c b/direct.c index b2260dbd..0d5688d8 100644 --- a/direct.c +++ b/direct.c @@ -52,8 +52,10 @@ static void direct_write_cb(struct bufferevent *buffev, void *_arg) // Failed to start relay. Connection is dropped. return; // Write any data received from client to relay - if (evbuffer_get_length(bufferevent_get_input(client->client))) - client->instance->relay_ss->writecb(buffev, client); + struct evbuffer * input = bufferevent_get_input(client->client); + if (evbuffer_get_length(input)) + if (bufferevent_write_buffer(client->relay, input) == -1) + redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); } } diff --git a/http-relay.c b/http-relay.c index a64b6ca1..a13e6295 100644 --- a/http-relay.c +++ b/http-relay.c @@ -564,7 +564,7 @@ static int httpr_connect_relay(redsocks_client *client) { int error; - client->client->readcb = httpr_client_read_cb; + replace_readcb(client->client, httpr_client_read_cb); error = bufferevent_enable(client->client, EV_READ); if (error) { redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); diff --git a/https-connect.c b/https-connect.c index e122a436..a3e703e8 100644 --- a/https-connect.c +++ b/https-connect.c @@ -191,23 +191,15 @@ static void httpsc_read_cb(struct bufferevent *buffev, void *_arg) if (client->state == httpc_headers_skipped) { bufferevent_data_cb read_cb, write_cb; + replace_eventcb(client->client, httpsc_event_cb); + struct evbuffer * input = bufferevent_get_input(client->client); + if (evbuffer_get_length(input)) #if LIBEVENT_VERSION_NUMBER >= 0x02010100 - bufferevent_getcb(client->client, &read_cb, &write_cb, NULL, NULL); + bufferevent_trigger(client->relay, EV_WRITE, 0); #else - read_cb = client->client->readcb; - write_cb = client->client->writecb; + if (client->relay->writecb) + client->relay->writecb(client->relay, client); #endif - bufferevent_setcb(client->client, read_cb, write_cb, httpsc_event_cb, client); - if (client->client) { - struct evbuffer * input = bufferevent_get_input(client->client); - if (evbuffer_get_length(input)) -#if LIBEVENT_VERSION_NUMBER >= 0x02010100 - bufferevent_trigger(client->relay, EV_WRITE, 0); -#else - if (client->relay->writecb) - client->relay->writecb(client->relay, client); -#endif - } } } @@ -227,7 +219,8 @@ static int httpsc_connect_relay(redsocks_client *client) char * interface = client->instance->config.interface; struct timeval tv = {client->instance->config.timeout, 0}; - sclient->ssl = SSL_new(httpsc->ctx); + if (!sclient->ssl) + sclient->ssl = SSL_new(httpsc->ctx); // Allowing binding relay socket to specified IP for outgoing connections client->relay = red_connect_relay_ssl(interface, &client->instance->config.relayaddr, diff --git a/redsocks.c b/redsocks.c index f284e233..69022b41 100644 --- a/redsocks.c +++ b/redsocks.c @@ -653,7 +653,12 @@ void redsocks_relay_connected(struct bufferevent *buffev, void *_arg) client->instance->relay_ss->writecb, redsocks_event_error, client); - client->instance->relay_ss->writecb(client->relay, client); +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 + bufferevent_trigger(client->relay, EV_WRITE, 0); +#else + if (client->instance->relay_ss->writecb) + client->instance->relay_ss->writecb(client->relay, client); +#endif return; fail: diff --git a/socks5-udp.c b/socks5-udp.c index 0ee459f5..e80fdb1c 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -290,8 +290,7 @@ static void socks5_read_auth_reply(struct bufferevent *buffev, void *_arg) if (error) goto fail; - socks5client->relay->readcb = socks5_read_assoc_reply; - + replace_readcb(socks5client->relay, socks5_read_assoc_reply); return; fail: @@ -328,7 +327,7 @@ static void socks5_read_auth_methods(struct bufferevent *buffev, void *_arg) if (ierror) goto fail; - socks5client->relay->readcb = socks5_read_assoc_reply; + replace_readcb(socks5client->relay, socks5_read_assoc_reply); } else if (reply.method == socks5_auth_password) { ierror = redsocks_write_helper_ex_plain( @@ -337,7 +336,7 @@ static void socks5_read_auth_methods(struct bufferevent *buffev, void *_arg) if (ierror) goto fail; - socks5client->relay->readcb = socks5_read_auth_reply; + replace_readcb(socks5client->relay, socks5_read_auth_reply); } return; @@ -366,8 +365,8 @@ static void socks5_relay_connected(struct bufferevent *buffev, void *_arg) if (error) goto fail; - socks5client->relay->readcb = socks5_read_auth_methods; - socks5client->relay->writecb = 0; + replace_readcb(socks5client->relay, socks5_read_auth_methods); + replace_writecb(socks5client->relay, NULL); //bufferevent_disable(buffev, EV_WRITE); // I don't want to check for writeability. return; diff --git a/utils.c b/utils.c index ca573089..9aebbcf4 100644 --- a/utils.c +++ b/utils.c @@ -490,4 +490,44 @@ int apply_tcp_fastopen(int fd) #endif } + +void replace_readcb(struct bufferevent * buffev, bufferevent_data_cb readcb) +{ +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 + bufferevent_event_cb eventcb; + bufferevent_data_cb writecb; + void * arg; + bufferevent_getcb(buffev, NULL, &writecb, &eventcb, &arg); + bufferevent_setcb(buffev, readcb, writecb, eventcb, arg); +#else + buffev->readcb = readcb; +#endif +} + +void replace_writecb(struct bufferevent * buffev, bufferevent_data_cb writecb) +{ +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 + bufferevent_event_cb eventcb; + bufferevent_data_cb readcb; + void * arg; + bufferevent_getcb(buffev, &readcb, NULL, &eventcb, &arg); + bufferevent_setcb(buffev, readcb, writecb, eventcb, arg); +#else + buffev->writecb = writecb; +#endif +} + +void replace_eventcb(struct bufferevent * buffev, bufferevent_event_cb eventcb) +{ +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 + bufferevent_data_cb readcb, writecb; + void * arg; + bufferevent_getcb(buffev, &readcb, &writecb, NULL, &arg); + bufferevent_setcb(buffev, readcb, writecb, eventcb, arg); +#else + buffev->errorcb = eventcb; +#endif +} + + /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ diff --git a/utils.h b/utils.h index dab4f7c8..3e8c2220 100644 --- a/utils.h +++ b/utils.h @@ -97,6 +97,9 @@ size_t copy_evbuffer(struct bufferevent * dst, struct bufferevent * src, size_t size_t get_write_hwm(struct bufferevent *bufev); int make_socket_transparent(int fd); int apply_tcp_fastopen(int fd); +void replace_readcb(struct bufferevent * buffev, bufferevent_data_cb readcb); +void replace_writecb(struct bufferevent * buffev, bufferevent_data_cb writecb); +void replace_eventcb(struct bufferevent * buffev, bufferevent_event_cb eventcb); #define event_fmt_str "%s|%s|%s|%s|%s|%s|0x%x" #define event_fmt(what) \ From 07b4faeaf941985d19c20bb149240e02de7399c4 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 23 Jul 2017 12:39:43 +0800 Subject: [PATCH 110/192] ALlow specify remote DNS server port for TCPDNS. --- redsocks.conf.example | 2 ++ tcpdns.c | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/redsocks.conf.example b/redsocks.conf.example index 0d4f588a..639f64af 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -140,7 +140,9 @@ tcpdns { local_ip = 192.168.1.1; // Local server to act as DNS server local_port = 1053; // UDP port to receive UDP DNS requests tcpdns1 = 8.8.4.4; // DNS server that supports TCP DNS requests + tcpdns1_port = 53; // DNS server port, default 53 tcpdns2 = 8.8.8.8; // DNS server that supports TCP DNS requests + tcpdns2_port = 53; // DNS server port, default 53 timeout = 4; // Timeout value for TCP DNS requests } diff --git a/tcpdns.c b/tcpdns.c index be1fff7d..0ad51678 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -362,7 +362,9 @@ static parser_entry tcpdns_entries[] = { .key = "local_ip", .type = pt_in_addr }, { .key = "local_port", .type = pt_uint16 }, { .key = "tcpdns1", .type = pt_in_addr }, + { .key = "tcpdns1_port", .type = pt_uint16 }, { .key = "tcpdns2", .type = pt_in_addr }, + { .key = "tcpdns2_port", .type = pt_uint16 }, { .key = "timeout", .type = pt_uint16 }, { } }; @@ -389,10 +391,10 @@ static int tcpdns_onenter(parser_section *section) instance->config.udpdns2_addr.sin_port = htons(53); instance->config.tcpdns1_addr.sin_family = AF_INET; instance->config.tcpdns1_addr.sin_addr.s_addr = htonl(INADDR_ANY); - instance->config.tcpdns1_addr.sin_port = htons(53); + instance->config.tcpdns1_addr.sin_port = 53; instance->config.tcpdns2_addr.sin_family = AF_INET; instance->config.tcpdns2_addr.sin_addr.s_addr = htonl(INADDR_ANY); - instance->config.tcpdns2_addr.sin_port = htons(53); + instance->config.tcpdns2_addr.sin_port = 53; for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = @@ -401,7 +403,9 @@ static int tcpdns_onenter(parser_section *section) (strcmp(entry->key, "udpdns1") == 0) ? (void*)&instance->config.udpdns1_addr.sin_addr : (strcmp(entry->key, "udpdns2") == 0) ? (void*)&instance->config.udpdns2_addr.sin_addr : (strcmp(entry->key, "tcpdns1") == 0) ? (void*)&instance->config.tcpdns1_addr.sin_addr : + (strcmp(entry->key, "tcpdns1_port") == 0) ? (void*)&instance->config.tcpdns1_addr.sin_port : (strcmp(entry->key, "tcpdns2") == 0) ? (void*)&instance->config.tcpdns2_addr.sin_addr : + (strcmp(entry->key, "tcpdns2_port") == 0) ? (void*)&instance->config.tcpdns2_addr.sin_port : (strcmp(entry->key, "timeout") == 0) ? (void*)&instance->config.timeout : NULL; section->data = instance; @@ -426,6 +430,15 @@ static int tcpdns_onexit(parser_section *section) && instance->config.tcpdns2_addr.sin_addr.s_addr == htonl(INADDR_ANY)) err = "At least one TCP DNS resolver must be configured."; + if (instance->config.tcpdns1_addr.sin_port == 0) + err = "Incorrect port number for TCP DNS1"; + else + instance->config.tcpdns1_addr.sin_port = htons(instance->config.tcpdns1_addr.sin_port); + if (instance->config.tcpdns2_addr.sin_port == 0) + err = "Incorrect port number for TCP DNS2"; + else + instance->config.tcpdns2_addr.sin_port = htons(instance->config.tcpdns2_addr.sin_port); + if (err) parser_error(section->context, "%s", err); else From 98331d586ea5dc75b8bb97159f6b4490e1b95666 Mon Sep 17 00:00:00 2001 From: Kevin LARQUEMIN Date: Sat, 29 Jul 2017 16:18:01 +0200 Subject: [PATCH 111/192] fixing MacOS Sierra 10.12 compilation --- Makefile | 30 +- base.c | 11 +- xnu/10.12/libkern/tree.h | 802 +++++++++++++ xnu/10.12/net/pfvar.h | 2446 ++++++++++++++++++++++++++++++++++++++ xnu/10.12/net/radix.h | 214 ++++ 5 files changed, 3481 insertions(+), 22 deletions(-) create mode 100644 xnu/10.12/libkern/tree.h create mode 100644 xnu/10.12/net/pfvar.h create mode 100644 xnu/10.12/net/radix.h diff --git a/Makefile b/Makefile index 8ed90649..02c028e9 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,11 @@ override CFLAGS += -D_BSD_SOURCE -D_DEFAULT_SOURCE -Wall ifeq ($(OS), Linux) override CFLAGS += -std=c99 -D_XOPEN_SOURCE=600 endif +ifeq ($(OS), Darwin) +override CFLAGS +=-I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib +override CFLAGS +=-Ixnu/10.12/ +endif + #LDFLAGS += -fwhole-program ifdef USE_CRYPTO_POLARSSL @@ -56,6 +61,9 @@ $(CONF): OpenBSD) \ echo "#define USE_PF" >$(CONF) \ ;; \ + Darwin) \ + echo "#define USE_PF\n#define _APPLE_" >$(CONF) \ + ;; \ *) \ echo "Unknown system, only generic firewall code is compiled" 1>&2; \ echo "/* Unknown system, only generic firewall code is compiled */" >$(CONF) \ @@ -86,27 +94,7 @@ gen/.build: base.c: $(CONF) $(DEPS): $(SRCS) - $(CC) -MM $(SRCS) 2>/dev/null >$(DEPS) || \ - ( \ - for I in $(wildcard *.h); do \ - export $${I//[-.]/_}_DEPS="`sed '/^\#[ \t]*include \?"\(.*\)".*/!d;s//\1/' $$I`"; \ - done; \ - echo -n >$(DEPS); \ - for SRC in $(SRCS); do \ - echo -n "$${SRC%.c}.o: " >>$(DEPS); \ - export SRC_DEPS="`sed '/\#[ \t]*include \?"\(.*\)".*/!d;s//\1/' $$SRC | sort`"; \ - while true; do \ - export SRC_DEPS_OLD="$$SRC_DEPS"; \ - export SRC_DEEP_DEPS=""; \ - for HDR in $$SRC_DEPS; do \ - eval export SRC_DEEP_DEPS="\"$$SRC_DEEP_DEPS \$$$${HDR//[-.]/_}_DEPS\""; \ - done; \ - export SRC_DEPS="`echo $$SRC_DEPS $$SRC_DEEP_DEPS | sed 's/ */\n/g' | sort -u`"; \ - test "$$SRC_DEPS" = "$$SRC_DEPS_OLD" && break; \ - done; \ - echo $$SRC $$SRC_DEPS >>$(DEPS); \ - done; \ - ) + $(CC) -MM $(CFLAGS) $(SRCS) 2>/dev/null >$(DEPS) -include $(DEPS) diff --git a/base.c b/base.c index 65963081..dcfe434e 100644 --- a/base.c +++ b/base.c @@ -35,7 +35,16 @@ #if defined USE_PF # include +#if defined _APPLE_ +#define PRIVATE +#endif # include +#if defined _APPLE_ +#define sport sxport.port +#define dport dxport.port +#define rdport rdxport.port +#undef PRIVATE +#endif # include #endif #ifdef __FreeBSD__ @@ -449,7 +458,7 @@ static int base_init() exit(EXIT_SUCCESS); } } - + log_open(); // child has nothing to do with TTY if (instance.daemon) { diff --git a/xnu/10.12/libkern/tree.h b/xnu/10.12/libkern/tree.h new file mode 100644 index 00000000..3a26162b --- /dev/null +++ b/xnu/10.12/libkern/tree.h @@ -0,0 +1,802 @@ +/* + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $NetBSD: tree.h,v 1.13 2006/08/27 22:32:38 christos Exp $ */ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LIBKERN_TREE_H_ +#define _LIBKERN_TREE_H_ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_PLACEHOLDER NULL +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ +} + +#define RB_COLOR_MASK (uintptr_t)0x1 +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define _RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(name, elm, parent, field) do { \ + name##_RB_SETPARENT(elm, parent); \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + name##_RB_SETCOLOR(elm, RB_RED); \ +} while (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(name, black, red, field) do { \ + name##_RB_SETCOLOR(black, RB_BLACK); \ + name##_RB_SETCOLOR(red, RB_RED); \ +} while (/*CONSTCOND*/ 0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) (void)(x) +#endif + +#define RB_ROTATE_LEFT(name, head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ + name##_RB_SETPARENT(RB_LEFT(tmp, field),(elm)); \ + } \ + RB_AUGMENT(elm); \ + if (name##_RB_SETPARENT(tmp, name##_RB_GETPARENT(elm)) != NULL) { \ + if ((elm) == RB_LEFT(name##_RB_GETPARENT(elm), field)) \ + RB_LEFT(name##_RB_GETPARENT(elm), field) = (tmp); \ + else \ + RB_RIGHT(name##_RB_GETPARENT(elm), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + name##_RB_SETPARENT(elm, (tmp)); \ + RB_AUGMENT(tmp); \ + if ((name##_RB_GETPARENT(tmp))) \ + RB_AUGMENT(name##_RB_GETPARENT(tmp)); \ +} while (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(name, head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ + name##_RB_SETPARENT(RB_RIGHT(tmp, field), (elm)); \ + } \ + RB_AUGMENT(elm); \ + if (name##_RB_SETPARENT(tmp, name##_RB_GETPARENT(elm)) != NULL) { \ + if ((elm) == RB_LEFT(name##_RB_GETPARENT(elm), field)) \ + RB_LEFT(name##_RB_GETPARENT(elm), field) = (tmp); \ + else \ + RB_RIGHT(name##_RB_GETPARENT(elm), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + name##_RB_SETPARENT(elm, tmp); \ + RB_AUGMENT(tmp); \ + if ((name##_RB_GETPARENT(tmp))) \ + RB_AUGMENT(name##_RB_GETPARENT(tmp)); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ +void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +struct type *name##_RB_REMOVE(struct name *, struct type *); \ +struct type *name##_RB_INSERT(struct name *, struct type *); \ +struct type *name##_RB_FIND(struct name *, struct type *); \ +struct type *name##_RB_NEXT(struct type *); \ +struct type *name##_RB_MINMAX(struct name *, int); \ +struct type *name##_RB_GETPARENT(struct type*); \ +struct type *name##_RB_SETPARENT(struct type*, struct type*); \ +int name##_RB_GETCOLOR(struct type*); \ +void name##_RB_SETCOLOR(struct type*,int); + +/* Generates prototypes (with storage class) and inline functions */ +#define RB_PROTOTYPE_SC(_sc_, name, type, field, cmp) \ +_sc_ void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +_sc_ void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *); \ +_sc_ struct type *name##_RB_REMOVE(struct name *, struct type *); \ +_sc_ struct type *name##_RB_INSERT(struct name *, struct type *); \ +_sc_ struct type *name##_RB_FIND(struct name *, struct type *); \ +_sc_ struct type *name##_RB_NEXT(struct type *); \ +_sc_ struct type *name##_RB_MINMAX(struct name *, int); \ +_sc_ struct type *name##_RB_GETPARENT(struct type*); \ +_sc_ struct type *name##_RB_SETPARENT(struct type*, struct type*); \ +_sc_ int name##_RB_GETCOLOR(struct type*); \ +_sc_ void name##_RB_SETCOLOR(struct type*,int); + + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ +struct type *name##_RB_GETPARENT(struct type *elm) { \ + struct type *parent = _RB_PARENT(elm, field); \ + if( parent != NULL) { \ + parent = (struct type*)((uintptr_t)parent & ~RB_COLOR_MASK);\ + return( (struct type*) ( (parent == (struct type*) RB_PLACEHOLDER) ? NULL: parent));\ + } \ + return((struct type*)NULL); \ +} \ +int name##_RB_GETCOLOR(struct type *elm) { \ + int color = 0; \ + color = (int)((uintptr_t)_RB_PARENT(elm,field) & RB_COLOR_MASK);\ + return(color); \ +} \ +void name##_RB_SETCOLOR(struct type *elm,int color) { \ + struct type *parent = name##_RB_GETPARENT(elm); \ + if(parent == (struct type*)NULL) \ + parent = (struct type*) RB_PLACEHOLDER; \ + _RB_PARENT(elm, field) = (struct type*)((uintptr_t)parent | (unsigned int)color);\ +} \ +struct type *name##_RB_SETPARENT(struct type *elm, struct type *parent) { \ + int color = name##_RB_GETCOLOR(elm); \ + _RB_PARENT(elm, field) = parent; \ + if(color) name##_RB_SETCOLOR(elm, color); \ + return(name##_RB_GETPARENT(elm)); \ +} \ + \ +void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = name##_RB_GETPARENT(elm)) != NULL && \ + name##_RB_GETCOLOR(parent) == RB_RED) { \ + gparent = name##_RB_GETPARENT(parent); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && name##_RB_GETCOLOR(tmp) == RB_RED) { \ + name##_RB_SETCOLOR(tmp, RB_BLACK); \ + RB_SET_BLACKRED(name, parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(name, head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(name, parent, gparent, field); \ + RB_ROTATE_RIGHT(name,head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && name##_RB_GETCOLOR(tmp) == RB_RED) { \ + name##_RB_SETCOLOR(tmp, RB_BLACK); \ + RB_SET_BLACKRED(name, parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(name, head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(name, parent, gparent, field); \ + RB_ROTATE_LEFT(name, head, gparent, tmp, field); \ + } \ + } \ + name##_RB_SETCOLOR(head->rbh_root, RB_BLACK); \ +} \ + \ +void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || name##_RB_GETCOLOR(elm) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (name##_RB_GETCOLOR(tmp) == RB_RED) { \ + RB_SET_BLACKRED(name, tmp, parent, field); \ + RB_ROTATE_LEFT(name, head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK)) {\ + name##_RB_SETCOLOR(tmp, RB_RED); \ + elm = parent; \ + parent = name##_RB_GETPARENT(elm); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)) \ + != NULL) \ + name##_RB_SETCOLOR(oleft, RB_BLACK);\ + name##_RB_SETCOLOR(tmp, RB_RED); \ + RB_ROTATE_RIGHT(name, head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + name##_RB_SETCOLOR(tmp, (name##_RB_GETCOLOR(parent)));\ + name##_RB_SETCOLOR(parent, RB_BLACK); \ + if (RB_RIGHT(tmp, field)) \ + name##_RB_SETCOLOR(RB_RIGHT(tmp, field),RB_BLACK);\ + RB_ROTATE_LEFT(name, head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (name##_RB_GETCOLOR(tmp) == RB_RED) { \ + RB_SET_BLACKRED(name, tmp, parent, field); \ + RB_ROTATE_RIGHT(name, head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK)) {\ + name##_RB_SETCOLOR(tmp, RB_RED); \ + elm = parent; \ + parent = name##_RB_GETPARENT(elm); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)) \ + != NULL) \ + name##_RB_SETCOLOR(oright, RB_BLACK);\ + name##_RB_SETCOLOR(tmp, RB_RED); \ + RB_ROTATE_LEFT(name, head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + name##_RB_SETCOLOR(tmp,(name##_RB_GETCOLOR(parent)));\ + name##_RB_SETCOLOR(parent, RB_BLACK); \ + if (RB_LEFT(tmp, field)) \ + name##_RB_SETCOLOR(RB_LEFT(tmp, field), RB_BLACK);\ + RB_ROTATE_RIGHT(name, head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + name##_RB_SETCOLOR(elm, RB_BLACK); \ +} \ + \ +struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field)) != NULL) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = name##_RB_GETPARENT(elm); \ + color = name##_RB_GETCOLOR(elm); \ + if (child) \ + name##_RB_SETPARENT(child, parent); \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (name##_RB_GETPARENT(elm) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (name##_RB_GETPARENT(old)) { \ + if (RB_LEFT(name##_RB_GETPARENT(old), field) == old)\ + RB_LEFT(name##_RB_GETPARENT(old), field) = elm;\ + else \ + RB_RIGHT(name##_RB_GETPARENT(old), field) = elm;\ + RB_AUGMENT(name##_RB_GETPARENT(old)); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_SETPARENT(RB_LEFT(old, field), elm); \ + if (RB_RIGHT(old, field)) \ + name##_RB_SETPARENT(RB_RIGHT(old, field), elm); \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = name##_RB_GETPARENT(left)) != NULL); \ + } \ + goto color; \ + } \ + parent = name##_RB_GETPARENT(elm); \ + color = name##_RB_GETCOLOR(elm); \ + if (child) \ + name##_RB_SETPARENT(child, parent); \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(name, elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* ARGSUSED */ \ +struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (name##_RB_GETPARENT(elm) && \ + (elm == RB_LEFT(name##_RB_GETPARENT(elm), field))) \ + elm = name##_RB_GETPARENT(elm); \ + else { \ + while (name##_RB_GETPARENT(elm) && \ + (elm == RB_RIGHT(name##_RB_GETPARENT(elm), field)))\ + elm = name##_RB_GETPARENT(elm); \ + elm = name##_RB_GETPARENT(elm); \ + } \ + } \ + return (elm); \ +} \ + \ +struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + + +#define RB_PROTOTYPE_PREV(name, type, field, cmp) \ + RB_PROTOTYPE(name, type, field, cmp) \ +struct type *name##_RB_PREV(struct type *); + + +#define RB_PROTOTYPE_SC_PREV(_sc_, name, type, field, cmp) \ + RB_PROTOTYPE_SC(_sc_, name, type, field, cmp) \ +_sc_ struct type *name##_RB_PREV(struct type *); + +#define RB_GENERATE_PREV(name, type, field, cmp) \ + RB_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (name##_RB_GETPARENT(elm) && \ + (elm == RB_RIGHT(name##_RB_GETPARENT(elm), field))) \ + elm = name##_RB_GETPARENT(elm); \ + else { \ + while (name##_RB_GETPARENT(elm) && \ + (elm == RB_LEFT(name##_RB_GETPARENT(elm), field)))\ + elm = name##_RB_GETPARENT(elm); \ + elm = name##_RB_GETPARENT(elm); \ + } \ + } \ + return (elm); \ +} \ + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#endif /* _LIBKERN_TREE_H_ */ diff --git a/xnu/10.12/net/pfvar.h b/xnu/10.12/net/pfvar.h new file mode 100644 index 00000000..11e771bf --- /dev/null +++ b/xnu/10.12/net/pfvar.h @@ -0,0 +1,2446 @@ +/* + * Copyright (c) 2007-2015 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $apfw: git commit b6bf13f8321283cd7ee82b1795e86506084b1b95 $ */ +/* $OpenBSD: pfvar.h,v 1.259 2007/12/02 12:08:04 pascoe Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * NAT64 - Copyright (c) 2010 Viagenie Inc. (http://www.viagenie.ca) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _NET_PFVAR_H_ +#define _NET_PFVAR_H_ + +#ifdef PRIVATE +/* + * XXX + * XXX Private interfaces. Do not include this file; use pfctl(8) instead. + * XXX + */ +#if PF || !defined(KERNEL) + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef KERNEL +#include +#include +#include + +#include +#include + +#if BYTE_ORDER == BIG_ENDIAN +#define htobe64(x) (x) +#else /* LITTLE ENDIAN */ +#define htobe64(x) __DARWIN_OSSwapInt64(x) +#endif /* LITTLE_ENDIAN */ + +#define be64toh(x) htobe64(x) + +extern lck_rw_t *pf_perim_lock; +extern lck_mtx_t *pf_lock; + +struct pool { + struct zone *pool_zone; /* pointer to backend zone */ + const char *pool_name; /* name of pool */ + unsigned int pool_count; /* # of outstanding elements */ + unsigned int pool_hiwat; /* high watermark */ + unsigned int pool_limit; /* hard limit */ + unsigned int pool_fails; /* # of failed allocs due to limit */ +}; + +#define PR_NOWAIT FALSE +#define PR_WAITOK TRUE + +__private_extern__ void pool_init(struct pool *, size_t, unsigned int, + unsigned int, int, const char *, void *); +__private_extern__ void pool_destroy(struct pool *); +__private_extern__ void pool_sethiwat(struct pool *, int); +__private_extern__ void pool_sethardlimit(struct pool *, int, + const char *, int); +__private_extern__ void *pool_get(struct pool *, int); +__private_extern__ void pool_put(struct pool *, void *); +__private_extern__ u_int64_t pf_time_second(void); +__private_extern__ u_int64_t pf_calendar_time_second(void); +#endif /* KERNEL */ + +union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; + +#define PF_TCPS_PROXY_SRC ((TCP_NSTATES)+0) +#define PF_TCPS_PROXY_DST ((TCP_NSTATES)+1) + +#define PF_MD5_DIGEST_LENGTH 16 +#ifdef MD5_DIGEST_LENGTH +#if PF_MD5_DIGEST_LENGTH != MD5_DIGEST_LENGTH +#error +#endif /* PF_MD5_DIGEST_LENGTH != MD5_DIGEST_LENGTH */ +#endif /* MD5_DIGEST_LENGTH */ + +#ifdef KERNEL +struct ip; +struct ip6_hdr; +struct tcphdr; +struct pf_grev1_hdr; +struct pf_esp_hdr; +#endif /* KERNEL */ + +#define PF_GRE_PPTP_VARIANT 0x01 + +enum { PF_INOUT, PF_IN, PF_OUT }; +enum { PF_PASS, PF_DROP, PF_SCRUB, PF_NOSCRUB, PF_NAT, PF_NONAT, + PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP, + PF_DUMMYNET, PF_NODUMMYNET, PF_NAT64, PF_NONAT64 }; +enum { PF_RULESET_SCRUB, PF_RULESET_FILTER, PF_RULESET_NAT, + PF_RULESET_BINAT, PF_RULESET_RDR, PF_RULESET_DUMMYNET, + PF_RULESET_MAX }; +enum { PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT, + PF_OP_LE, PF_OP_GT, PF_OP_GE, PF_OP_XRG, PF_OP_RRG }; +enum { PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC, PF_DEBUG_NOISY }; +enum { PF_CHANGE_NONE, PF_CHANGE_ADD_HEAD, PF_CHANGE_ADD_TAIL, + PF_CHANGE_ADD_BEFORE, PF_CHANGE_ADD_AFTER, + PF_CHANGE_REMOVE, PF_CHANGE_GET_TICKET }; +enum { PF_GET_NONE, PF_GET_CLR_CNTR }; + +/* + * Note about PFTM_*: real indices into pf_rule.timeout[] come before + * PFTM_MAX, special cases afterwards. See pf_state_expires(). + */ +enum { PFTM_TCP_FIRST_PACKET, PFTM_TCP_OPENING, PFTM_TCP_ESTABLISHED, + PFTM_TCP_CLOSING, PFTM_TCP_FIN_WAIT, PFTM_TCP_CLOSED, + PFTM_UDP_FIRST_PACKET, PFTM_UDP_SINGLE, PFTM_UDP_MULTIPLE, + PFTM_ICMP_FIRST_PACKET, PFTM_ICMP_ERROR_REPLY, + PFTM_GREv1_FIRST_PACKET, PFTM_GREv1_INITIATING, + PFTM_GREv1_ESTABLISHED, PFTM_ESP_FIRST_PACKET, PFTM_ESP_INITIATING, + PFTM_ESP_ESTABLISHED, PFTM_OTHER_FIRST_PACKET, PFTM_OTHER_SINGLE, + PFTM_OTHER_MULTIPLE, PFTM_FRAG, PFTM_INTERVAL, + PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_SRC_NODE, + PFTM_TS_DIFF, PFTM_MAX, PFTM_PURGE, PFTM_UNLINKED }; + +/* PFTM default values */ +#define PFTM_TCP_FIRST_PACKET_VAL 120 /* First TCP packet */ +#define PFTM_TCP_OPENING_VAL 30 /* No response yet */ +#define PFTM_TCP_ESTABLISHED_VAL (24 * 60 * 60) /* Established */ +#define PFTM_TCP_CLOSING_VAL (15 * 60) /* Half closed */ +#define PFTM_TCP_FIN_WAIT_VAL 45 /* Got both FINs */ +#define PFTM_TCP_CLOSED_VAL 90 /* Got a RST */ +#define PFTM_UDP_FIRST_PACKET_VAL 60 /* First UDP packet */ +#define PFTM_UDP_SINGLE_VAL 30 /* Unidirectional */ +#define PFTM_UDP_MULTIPLE_VAL 60 /* Bidirectional */ +#define PFTM_ICMP_FIRST_PACKET_VAL 20 /* First ICMP packet */ +#define PFTM_ICMP_ERROR_REPLY_VAL 10 /* Got error response */ +#define PFTM_GREv1_FIRST_PACKET_VAL 120 +#define PFTM_GREv1_INITIATING_VAL 30 +#define PFTM_GREv1_ESTABLISHED_VAL 1800 +#define PFTM_ESP_FIRST_PACKET_VAL 120 +#define PFTM_ESP_INITIATING_VAL 30 +#define PFTM_ESP_ESTABLISHED_VAL 900 +#define PFTM_OTHER_FIRST_PACKET_VAL 60 /* First packet */ +#define PFTM_OTHER_SINGLE_VAL 30 /* Unidirectional */ +#define PFTM_OTHER_MULTIPLE_VAL 60 /* Bidirectional */ +#define PFTM_FRAG_VAL 30 /* Fragment expire */ +#define PFTM_INTERVAL_VAL 10 /* Expire interval */ +#define PFTM_SRC_NODE_VAL 0 /* Source tracking */ +#define PFTM_TS_DIFF_VAL 30 /* Allowed TS diff */ + +enum { PF_NOPFROUTE, PF_FASTROUTE, PF_ROUTETO, PF_DUPTO, PF_REPLYTO }; +enum { PF_LIMIT_STATES, + PF_LIMIT_APP_STATES, + PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, + PF_LIMIT_TABLES, PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; +#define PF_POOL_IDMASK 0x0f +enum { PF_POOL_NONE, PF_POOL_BITMASK, PF_POOL_RANDOM, + PF_POOL_SRCHASH, PF_POOL_ROUNDROBIN }; +enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL, + PF_ADDR_TABLE, PF_ADDR_RTLABEL, PF_ADDR_URPFFAILED, + PF_ADDR_RANGE }; +#define PF_POOL_TYPEMASK 0x0f +#define PF_POOL_STICKYADDR 0x20 +#define PF_WSCALE_FLAG 0x80 +#define PF_WSCALE_MASK 0x0f + +#define PF_LOG 0x01 +#define PF_LOG_ALL 0x02 +#define PF_LOG_SOCKET_LOOKUP 0x04 + +struct pf_addr { + union { + struct in_addr v4; + struct in6_addr v6; + u_int8_t addr8[16]; + u_int16_t addr16[8]; + u_int32_t addr32[4]; + } pfa; /* 128-bit address */ +#define v4 pfa.v4 +#define v6 pfa.v6 +#define addr8 pfa.addr8 +#define addr16 pfa.addr16 +#define addr32 pfa.addr32 +}; + +#define PF_TABLE_NAME_SIZE 32 + +#define PFI_AFLAG_NETWORK 0x01 +#define PFI_AFLAG_BROADCAST 0x02 +#define PFI_AFLAG_PEER 0x04 +#define PFI_AFLAG_MODEMASK 0x07 +#define PFI_AFLAG_NOALIAS 0x08 + +#ifndef RTLABEL_LEN +#define RTLABEL_LEN 32 +#endif + +struct pf_addr_wrap { + union { + struct { + struct pf_addr addr; + struct pf_addr mask; + } a; + char ifname[IFNAMSIZ]; + char tblname[PF_TABLE_NAME_SIZE]; + char rtlabelname[RTLABEL_LEN]; + u_int32_t rtlabel; + } v; + union { +#ifdef KERNEL + struct pfi_dynaddr *dyn __attribute__((aligned(8))); + struct pfr_ktable *tbl __attribute__((aligned(8))); +#else /* !KERNEL */ + void *dyn __attribute__((aligned(8))); + void *tbl __attribute__((aligned(8))); +#endif /* !KERNEL */ + int dyncnt __attribute__((aligned(8))); + int tblcnt __attribute__((aligned(8))); + } p __attribute__((aligned(8))); + u_int8_t type; /* PF_ADDR_* */ + u_int8_t iflags; /* PFI_AFLAG_* */ +}; + +struct pf_port_range { + u_int16_t port[2]; + u_int8_t op; +}; + +union pf_rule_xport { + struct pf_port_range range; + u_int16_t call_id; + u_int32_t spi; +}; + +#ifdef KERNEL +struct pfi_dynaddr { + TAILQ_ENTRY(pfi_dynaddr) entry; + struct pf_addr pfid_addr4; + struct pf_addr pfid_mask4; + struct pf_addr pfid_addr6; + struct pf_addr pfid_mask6; + struct pfr_ktable *pfid_kt; + struct pfi_kif *pfid_kif; + void *pfid_hook_cookie; + int pfid_net; /* mask or 128 */ + int pfid_acnt4; /* address count IPv4 */ + int pfid_acnt6; /* address count IPv6 */ + sa_family_t pfid_af; /* rule af */ + u_int8_t pfid_iflags; /* PFI_AFLAG_* */ +}; + +/* + * Address manipulation macros + */ + +#if INET +#if !INET6 +#define PF_INET_ONLY +#endif /* ! INET6 */ +#endif /* INET */ + +#if INET6 +#if !INET +#define PF_INET6_ONLY +#endif /* ! INET */ +#endif /* INET6 */ + +#if INET +#if INET6 +#define PF_INET_INET6 +#endif /* INET6 */ +#endif /* INET */ + +#else /* !KERNEL */ + +#define PF_INET_INET6 + +#endif /* !KERNEL */ + +/* Both IPv4 and IPv6 */ +#ifdef PF_INET_INET6 + +#define PF_AEQ(a, b, c) \ + ((c == AF_INET && (a)->addr32[0] == (b)->addr32[0]) || \ + ((a)->addr32[3] == (b)->addr32[3] && \ + (a)->addr32[2] == (b)->addr32[2] && \ + (a)->addr32[1] == (b)->addr32[1] && \ + (a)->addr32[0] == (b)->addr32[0])) \ + +#define PF_ANEQ(a, b, c) \ + ((c == AF_INET && (a)->addr32[0] != (b)->addr32[0]) || \ + ((a)->addr32[3] != (b)->addr32[3] || \ + (a)->addr32[2] != (b)->addr32[2] || \ + (a)->addr32[1] != (b)->addr32[1] || \ + (a)->addr32[0] != (b)->addr32[0])) \ + +#define PF_ALEQ(a, b, c) \ + ((c == AF_INET && (a)->addr32[0] <= (b)->addr32[0]) || \ + ((a)->addr32[3] <= (b)->addr32[3] && \ + (a)->addr32[2] <= (b)->addr32[2] && \ + (a)->addr32[1] <= (b)->addr32[1] && \ + (a)->addr32[0] <= (b)->addr32[0])) \ + +#define PF_AZERO(a, c) \ + ((c == AF_INET && !(a)->addr32[0]) || \ + (!(a)->addr32[0] && !(a)->addr32[1] && \ + !(a)->addr32[2] && !(a)->addr32[3])) \ + +#define PF_MATCHA(n, a, m, b, f) \ + pf_match_addr(n, a, m, b, f) + +#define PF_ACPY(a, b, f) \ + pf_addrcpy(a, b, f) + +#define PF_AINC(a, f) \ + pf_addr_inc(a, f) + +#define PF_POOLMASK(a, b, c, d, f) \ + pf_poolmask(a, b, c, d, f) + +#else + +/* Just IPv6 */ + +#ifdef PF_INET6_ONLY + +#define PF_AEQ(a, b, c) \ + ((a)->addr32[3] == (b)->addr32[3] && \ + (a)->addr32[2] == (b)->addr32[2] && \ + (a)->addr32[1] == (b)->addr32[1] && \ + (a)->addr32[0] == (b)->addr32[0]) \ + +#define PF_ANEQ(a, b, c) \ + ((a)->addr32[3] != (b)->addr32[3] || \ + (a)->addr32[2] != (b)->addr32[2] || \ + (a)->addr32[1] != (b)->addr32[1] || \ + (a)->addr32[0] != (b)->addr32[0]) \ + +#define PF_ALEQ(a, b, c) \ + ((a)->addr32[3] <= (b)->addr32[3] && \ + (a)->addr32[2] <= (b)->addr32[2] && \ + (a)->addr32[1] <= (b)->addr32[1] && \ + (a)->addr32[0] <= (b)->addr32[0]) \ + +#define PF_AZERO(a, c) \ + (!(a)->addr32[0] && \ + !(a)->addr32[1] && \ + !(a)->addr32[2] && \ + !(a)->addr32[3]) \ + +#define PF_MATCHA(n, a, m, b, f) \ + pf_match_addr(n, a, m, b, f) + +#define PF_ACPY(a, b, f) \ + pf_addrcpy(a, b, f) + +#define PF_AINC(a, f) \ + pf_addr_inc(a, f) + +#define PF_POOLMASK(a, b, c, d, f) \ + pf_poolmask(a, b, c, d, f) + +#else + +/* Just IPv4 */ +#ifdef PF_INET_ONLY + +#define PF_AEQ(a, b, c) \ + ((a)->addr32[0] == (b)->addr32[0]) + +#define PF_ANEQ(a, b, c) \ + ((a)->addr32[0] != (b)->addr32[0]) + +#define PF_ALEQ(a, b, c) \ + ((a)->addr32[0] <= (b)->addr32[0]) + +#define PF_AZERO(a, c) \ + (!(a)->addr32[0]) + +#define PF_MATCHA(n, a, m, b, f) \ + pf_match_addr(n, a, m, b, f) + +#define PF_ACPY(a, b, f) \ + (a)->v4.s_addr = (b)->v4.s_addr + +#define PF_AINC(a, f) \ + do { \ + (a)->addr32[0] = htonl(ntohl((a)->addr32[0]) + 1); \ + } while (0) + +#define PF_POOLMASK(a, b, c, d, f) \ + do { \ + (a)->addr32[0] = ((b)->addr32[0] & (c)->addr32[0]) | \ + (((c)->addr32[0] ^ 0xffffffff) & (d)->addr32[0]); \ + } while (0) + +#endif /* PF_INET_ONLY */ +#endif /* PF_INET6_ONLY */ +#endif /* PF_INET_INET6 */ + +#ifdef KERNEL +#define PF_MISMATCHAW(aw, x, af, neg, ifp) \ + ( \ + (((aw)->type == PF_ADDR_NOROUTE && \ + pf_routable((x), (af), NULL)) || \ + (((aw)->type == PF_ADDR_URPFFAILED && (ifp) != NULL && \ + pf_routable((x), (af), (ifp))) || \ + ((aw)->type == PF_ADDR_RTLABEL && \ + !pf_rtlabel_match((x), (af), (aw))) || \ + ((aw)->type == PF_ADDR_TABLE && \ + !pfr_match_addr((aw)->p.tbl, (x), (af))) || \ + ((aw)->type == PF_ADDR_DYNIFTL && \ + !pfi_match_addr((aw)->p.dyn, (x), (af))) || \ + ((aw)->type == PF_ADDR_RANGE && \ + !pf_match_addr_range(&(aw)->v.a.addr, \ + &(aw)->v.a.mask, (x), (af))) || \ + ((aw)->type == PF_ADDR_ADDRMASK && \ + !PF_AZERO(&(aw)->v.a.mask, (af)) && \ + !PF_MATCHA(0, &(aw)->v.a.addr, \ + &(aw)->v.a.mask, (x), (af))))) != \ + (neg) \ + ) +#endif /* KERNEL */ + +struct pf_rule_uid { + uid_t uid[2]; + u_int8_t op; + u_int8_t _pad[3]; +}; + +struct pf_rule_gid { + uid_t gid[2]; + u_int8_t op; + u_int8_t _pad[3]; +}; + +struct pf_rule_addr { + struct pf_addr_wrap addr; + union pf_rule_xport xport; + u_int8_t neg; +}; + +struct pf_pooladdr { + struct pf_addr_wrap addr; + TAILQ_ENTRY(pf_pooladdr) entries; +#if !defined(__LP64__) + u_int32_t _pad[2]; +#endif /* !__LP64__ */ + char ifname[IFNAMSIZ]; +#ifdef KERNEL + struct pfi_kif *kif __attribute__((aligned(8))); +#else /* !KERNEL */ + void *kif __attribute__((aligned(8))); +#endif /* !KERNEL */ +}; + +TAILQ_HEAD(pf_palist, pf_pooladdr); + +struct pf_poolhashkey { + union { + u_int8_t key8[16]; + u_int16_t key16[8]; + u_int32_t key32[4]; + } pfk; /* 128-bit hash key */ +#define key8 pfk.key8 +#define key16 pfk.key16 +#define key32 pfk.key32 +}; + +struct pf_pool { + struct pf_palist list; +#if !defined(__LP64__) + u_int32_t _pad[2]; +#endif /* !__LP64__ */ +#ifdef KERNEL + struct pf_pooladdr *cur __attribute__((aligned(8))); +#else /* !KERNEL */ + void *cur __attribute__((aligned(8))); +#endif /* !KERNEL */ + struct pf_poolhashkey key __attribute__((aligned(8))); + struct pf_addr counter; + int tblidx; + u_int16_t proxy_port[2]; + u_int8_t port_op; + u_int8_t opts; + sa_family_t af; +}; + + +/* A packed Operating System description for fingerprinting */ +typedef u_int32_t pf_osfp_t; +#define PF_OSFP_ANY ((pf_osfp_t)0) +#define PF_OSFP_UNKNOWN ((pf_osfp_t)-1) +#define PF_OSFP_NOMATCH ((pf_osfp_t)-2) + +struct pf_osfp_entry { + SLIST_ENTRY(pf_osfp_entry) fp_entry; +#if !defined(__LP64__) + u_int32_t _pad; +#endif /* !__LP64__ */ + pf_osfp_t fp_os; + int fp_enflags; +#define PF_OSFP_EXPANDED 0x001 /* expanded entry */ +#define PF_OSFP_GENERIC 0x002 /* generic signature */ +#define PF_OSFP_NODETAIL 0x004 /* no p0f details */ +#define PF_OSFP_LEN 32 + char fp_class_nm[PF_OSFP_LEN]; + char fp_version_nm[PF_OSFP_LEN]; + char fp_subtype_nm[PF_OSFP_LEN]; +}; +#define PF_OSFP_ENTRY_EQ(a, b) \ + ((a)->fp_os == (b)->fp_os && \ + memcmp((a)->fp_class_nm, (b)->fp_class_nm, PF_OSFP_LEN) == 0 && \ + memcmp((a)->fp_version_nm, (b)->fp_version_nm, PF_OSFP_LEN) == 0 && \ + memcmp((a)->fp_subtype_nm, (b)->fp_subtype_nm, PF_OSFP_LEN) == 0) + +/* handle pf_osfp_t packing */ +#define _FP_RESERVED_BIT 1 /* For the special negative #defines */ +#define _FP_UNUSED_BITS 1 +#define _FP_CLASS_BITS 10 /* OS Class (Windows, Linux) */ +#define _FP_VERSION_BITS 10 /* OS version (95, 98, NT, 2.4.54, 3.2) */ +#define _FP_SUBTYPE_BITS 10 /* patch level (NT SP4, SP3, ECN patch) */ +#define PF_OSFP_UNPACK(osfp, class, version, subtype) do { \ + (class) = ((osfp) >> (_FP_VERSION_BITS+_FP_SUBTYPE_BITS)) & \ + ((1 << _FP_CLASS_BITS) - 1); \ + (version) = ((osfp) >> _FP_SUBTYPE_BITS) & \ + ((1 << _FP_VERSION_BITS) - 1);\ + (subtype) = (osfp) & ((1 << _FP_SUBTYPE_BITS) - 1); \ +} while (0) +#define PF_OSFP_PACK(osfp, class, version, subtype) do { \ + (osfp) = ((class) & ((1 << _FP_CLASS_BITS) - 1)) << (_FP_VERSION_BITS \ + + _FP_SUBTYPE_BITS); \ + (osfp) |= ((version) & ((1 << _FP_VERSION_BITS) - 1)) << \ + _FP_SUBTYPE_BITS; \ + (osfp) |= (subtype) & ((1 << _FP_SUBTYPE_BITS) - 1); \ +} while (0) + +/* the fingerprint of an OSes TCP SYN packet */ +typedef u_int64_t pf_tcpopts_t; +struct pf_os_fingerprint { + SLIST_HEAD(pf_osfp_enlist, pf_osfp_entry) fp_oses; /* list of matches */ + pf_tcpopts_t fp_tcpopts; /* packed TCP options */ + u_int16_t fp_wsize; /* TCP window size */ + u_int16_t fp_psize; /* ip->ip_len */ + u_int16_t fp_mss; /* TCP MSS */ + u_int16_t fp_flags; +#define PF_OSFP_WSIZE_MOD 0x0001 /* Window modulus */ +#define PF_OSFP_WSIZE_DC 0x0002 /* Window don't care */ +#define PF_OSFP_WSIZE_MSS 0x0004 /* Window multiple of MSS */ +#define PF_OSFP_WSIZE_MTU 0x0008 /* Window multiple of MTU */ +#define PF_OSFP_PSIZE_MOD 0x0010 /* packet size modulus */ +#define PF_OSFP_PSIZE_DC 0x0020 /* packet size don't care */ +#define PF_OSFP_WSCALE 0x0040 /* TCP window scaling */ +#define PF_OSFP_WSCALE_MOD 0x0080 /* TCP window scale modulus */ +#define PF_OSFP_WSCALE_DC 0x0100 /* TCP window scale dont-care */ +#define PF_OSFP_MSS 0x0200 /* TCP MSS */ +#define PF_OSFP_MSS_MOD 0x0400 /* TCP MSS modulus */ +#define PF_OSFP_MSS_DC 0x0800 /* TCP MSS dont-care */ +#define PF_OSFP_DF 0x1000 /* IPv4 don't fragment bit */ +#define PF_OSFP_TS0 0x2000 /* Zero timestamp */ +#define PF_OSFP_INET6 0x4000 /* IPv6 */ + u_int8_t fp_optcnt; /* TCP option count */ + u_int8_t fp_wscale; /* TCP window scaling */ + u_int8_t fp_ttl; /* IPv4 TTL */ +#define PF_OSFP_MAXTTL_OFFSET 40 +/* TCP options packing */ +#define PF_OSFP_TCPOPT_NOP 0x0 /* TCP NOP option */ +#define PF_OSFP_TCPOPT_WSCALE 0x1 /* TCP window scaling option */ +#define PF_OSFP_TCPOPT_MSS 0x2 /* TCP max segment size opt */ +#define PF_OSFP_TCPOPT_SACK 0x3 /* TCP SACK OK option */ +#define PF_OSFP_TCPOPT_TS 0x4 /* TCP timestamp option */ +#define PF_OSFP_TCPOPT_BITS 3 /* bits used by each option */ +#define PF_OSFP_MAX_OPTS \ + (sizeof(((struct pf_os_fingerprint *)0)->fp_tcpopts) * 8) \ + / PF_OSFP_TCPOPT_BITS + + SLIST_ENTRY(pf_os_fingerprint) fp_next; +}; + +struct pf_osfp_ioctl { + struct pf_osfp_entry fp_os; + pf_tcpopts_t fp_tcpopts; /* packed TCP options */ + u_int16_t fp_wsize; /* TCP window size */ + u_int16_t fp_psize; /* ip->ip_len */ + u_int16_t fp_mss; /* TCP MSS */ + u_int16_t fp_flags; + u_int8_t fp_optcnt; /* TCP option count */ + u_int8_t fp_wscale; /* TCP window scaling */ + u_int8_t fp_ttl; /* IPv4 TTL */ + + int fp_getnum; /* DIOCOSFPGET number */ +}; + + +union pf_rule_ptr { + struct pf_rule *ptr __attribute__((aligned(8))); + u_int32_t nr __attribute__((aligned(8))); +} __attribute__((aligned(8))); + +#define PF_ANCHOR_NAME_SIZE 64 + +struct pf_rule { + struct pf_rule_addr src; + struct pf_rule_addr dst; +#define PF_SKIP_IFP 0 +#define PF_SKIP_DIR 1 +#define PF_SKIP_AF 2 +#define PF_SKIP_PROTO 3 +#define PF_SKIP_SRC_ADDR 4 +#define PF_SKIP_SRC_PORT 5 +#define PF_SKIP_DST_ADDR 6 +#define PF_SKIP_DST_PORT 7 +#define PF_SKIP_COUNT 8 + union pf_rule_ptr skip[PF_SKIP_COUNT]; +#define PF_RULE_LABEL_SIZE 64 + char label[PF_RULE_LABEL_SIZE]; +#define PF_QNAME_SIZE 64 + char ifname[IFNAMSIZ]; + char qname[PF_QNAME_SIZE]; + char pqname[PF_QNAME_SIZE]; +#define PF_TAG_NAME_SIZE 64 + char tagname[PF_TAG_NAME_SIZE]; + char match_tagname[PF_TAG_NAME_SIZE]; + + char overload_tblname[PF_TABLE_NAME_SIZE]; + + TAILQ_ENTRY(pf_rule) entries; +#if !defined(__LP64__) + u_int32_t _pad[2]; +#endif /* !__LP64__ */ + struct pf_pool rpool; + + u_int64_t evaluations; + u_int64_t packets[2]; + u_int64_t bytes[2]; + + u_int64_t ticket; +#define PF_OWNER_NAME_SIZE 64 + char owner[PF_OWNER_NAME_SIZE]; + u_int32_t priority; + +#ifdef KERNEL + struct pfi_kif *kif __attribute__((aligned(8))); +#else /* !KERNEL */ + void *kif __attribute__((aligned(8))); +#endif /* !KERNEL */ + struct pf_anchor *anchor __attribute__((aligned(8))); +#ifdef KERNEL + struct pfr_ktable *overload_tbl __attribute__((aligned(8))); +#else /* !KERNEL */ + void *overload_tbl __attribute__((aligned(8))); +#endif /* !KERNEL */ + + pf_osfp_t os_fingerprint __attribute__((aligned(8))); + + unsigned int rtableid; + u_int32_t timeout[PFTM_MAX]; + u_int32_t states; + u_int32_t max_states; + u_int32_t src_nodes; + u_int32_t max_src_nodes; + u_int32_t max_src_states; + u_int32_t max_src_conn; + struct { + u_int32_t limit; + u_int32_t seconds; + } max_src_conn_rate; + u_int32_t qid; + u_int32_t pqid; + u_int32_t rt_listid; + u_int32_t nr; + u_int32_t prob; + uid_t cuid; + pid_t cpid; + + u_int16_t return_icmp; + u_int16_t return_icmp6; + u_int16_t max_mss; + u_int16_t tag; + u_int16_t match_tag; + + struct pf_rule_uid uid; + struct pf_rule_gid gid; + + u_int32_t rule_flag; + u_int8_t action; + u_int8_t direction; + u_int8_t log; + u_int8_t logif; + u_int8_t quick; + u_int8_t ifnot; + u_int8_t match_tag_not; + u_int8_t natpass; + +#define PF_STATE_NORMAL 0x1 +#define PF_STATE_MODULATE 0x2 +#define PF_STATE_SYNPROXY 0x3 + u_int8_t keep_state; + sa_family_t af; + u_int8_t proto; + u_int8_t type; + u_int8_t code; + u_int8_t flags; + u_int8_t flagset; + u_int8_t min_ttl; + u_int8_t allow_opts; + u_int8_t rt; + u_int8_t return_ttl; + +/* service class categories */ +#define SCIDX_MASK 0x0f +#define SC_BE 0x10 +#define SC_BK_SYS 0x11 +#define SC_BK 0x12 +#define SC_RD 0x13 +#define SC_OAM 0x14 +#define SC_AV 0x15 +#define SC_RV 0x16 +#define SC_VI 0x17 +#define SC_VO 0x18 +#define SC_CTL 0x19 + +/* diffserve code points */ +#define DSCP_MASK 0xfc +#define DSCP_CUMASK 0x03 +#define DSCP_EF 0xb8 +#define DSCP_AF11 0x28 +#define DSCP_AF12 0x30 +#define DSCP_AF13 0x38 +#define DSCP_AF21 0x48 +#define DSCP_AF22 0x50 +#define DSCP_AF23 0x58 +#define DSCP_AF31 0x68 +#define DSCP_AF32 0x70 +#define DSCP_AF33 0x78 +#define DSCP_AF41 0x88 +#define DSCP_AF42 0x90 +#define DSCP_AF43 0x98 +#define AF_CLASSMASK 0xe0 +#define AF_DROPPRECMASK 0x18 + u_int8_t tos; + u_int8_t anchor_relative; + u_int8_t anchor_wildcard; + +#define PF_FLUSH 0x01 +#define PF_FLUSH_GLOBAL 0x02 + u_int8_t flush; + + u_int8_t proto_variant; + u_int8_t extfilter; /* Filter mode [PF_EXTFILTER_xxx] */ + u_int8_t extmap; /* Mapping mode [PF_EXTMAP_xxx] */ + u_int32_t dnpipe; + u_int32_t dntype; +}; + +/* pf device identifiers */ +#define PFDEV_PF 0 +#define PFDEV_PFM 1 +#define PFDEV_MAX 2 + +/* rule flags */ +#define PFRULE_DROP 0x0000 +#define PFRULE_RETURNRST 0x0001 +#define PFRULE_FRAGMENT 0x0002 +#define PFRULE_RETURNICMP 0x0004 +#define PFRULE_RETURN 0x0008 +#define PFRULE_NOSYNC 0x0010 +#define PFRULE_SRCTRACK 0x0020 /* track source states */ +#define PFRULE_RULESRCTRACK 0x0040 /* per rule */ + +/* scrub flags */ +#define PFRULE_NODF 0x0100 +#define PFRULE_FRAGCROP 0x0200 /* non-buffering frag cache */ +#define PFRULE_FRAGDROP 0x0400 /* drop funny fragments */ +#define PFRULE_RANDOMID 0x0800 +#define PFRULE_REASSEMBLE_TCP 0x1000 + +/* rule flags for TOS/DSCP/service class differentiation */ +#define PFRULE_TOS 0x2000 +#define PFRULE_DSCP 0x4000 +#define PFRULE_SC 0x8000 + +/* rule flags again */ +#define PFRULE_IFBOUND 0x00010000 /* if-bound */ +#define PFRULE_PFM 0x00020000 /* created by pfm device */ + +#define PFSTATE_HIWAT 10000 /* default state table size */ +#define PFSTATE_ADAPT_START 6000 /* default adaptive timeout start */ +#define PFSTATE_ADAPT_END 12000 /* default adaptive timeout end */ + +#define PFAPPSTATE_HIWAT 10000 /* default same as state table */ + +enum pf_extmap { + PF_EXTMAP_APD = 1, /* Address-port-dependent mapping */ + PF_EXTMAP_AD, /* Address-dependent mapping */ + PF_EXTMAP_EI /* Endpoint-independent mapping */ +}; + +enum pf_extfilter { + PF_EXTFILTER_APD = 1, /* Address-port-dependent filtering */ + PF_EXTFILTER_AD, /* Address-dependent filtering */ + PF_EXTFILTER_EI /* Endpoint-independent filtering */ +}; + +struct pf_threshold { + u_int32_t limit; +#define PF_THRESHOLD_MULT 1000 +#define PF_THRESHOLD_MAX 0xffffffff / PF_THRESHOLD_MULT + u_int32_t seconds; + u_int32_t count; + u_int32_t last; +}; + +struct pf_src_node { + RB_ENTRY(pf_src_node) entry; + struct pf_addr addr; + struct pf_addr raddr; + union pf_rule_ptr rule; +#ifdef KERNEL + struct pfi_kif *kif; +#else /* !KERNEL */ + void *kif; +#endif /* !KERNEL */ + u_int64_t bytes[2]; + u_int64_t packets[2]; + u_int32_t states; + u_int32_t conn; + struct pf_threshold conn_rate; + u_int64_t creation; + u_int64_t expire; + sa_family_t af; + u_int8_t ruletype; +}; + +#define PFSNODE_HIWAT 10000 /* default source node table size */ + +#ifdef KERNEL +struct pf_state_scrub { + struct timeval pfss_last; /* time received last packet */ + u_int32_t pfss_tsecr; /* last echoed timestamp */ + u_int32_t pfss_tsval; /* largest timestamp */ + u_int32_t pfss_tsval0; /* original timestamp */ + u_int16_t pfss_flags; +#define PFSS_TIMESTAMP 0x0001 /* modulate timestamp */ +#define PFSS_PAWS 0x0010 /* stricter PAWS checks */ +#define PFSS_PAWS_IDLED 0x0020 /* was idle too long. no PAWS */ +#define PFSS_DATA_TS 0x0040 /* timestamp on data packets */ +#define PFSS_DATA_NOTS 0x0080 /* no timestamp on data packets */ + u_int8_t pfss_ttl; /* stashed TTL */ + u_int8_t pad; + u_int32_t pfss_ts_mod; /* timestamp modulation */ +}; +#endif /* KERNEL */ + +union pf_state_xport { + u_int16_t port; + u_int16_t call_id; + u_int32_t spi; +}; + +struct pf_state_host { + struct pf_addr addr; + union pf_state_xport xport; +}; + +#ifdef KERNEL +struct pf_state_peer { + u_int32_t seqlo; /* Max sequence number sent */ + u_int32_t seqhi; /* Max the other end ACKd + win */ + u_int32_t seqdiff; /* Sequence number modulator */ + u_int16_t max_win; /* largest window (pre scaling) */ + u_int8_t state; /* active state level */ + u_int8_t wscale; /* window scaling factor */ + u_int16_t mss; /* Maximum segment size option */ + u_int8_t tcp_est; /* Did we reach TCPS_ESTABLISHED */ + struct pf_state_scrub *scrub; /* state is scrubbed */ + u_int8_t pad[3]; +}; + +TAILQ_HEAD(pf_state_queue, pf_state); + +struct pf_state; +struct pf_pdesc; +struct pf_app_state; + +typedef void (*pf_app_handler)(struct pf_state *, int, int, struct pf_pdesc *, + struct pfi_kif *); + +typedef int (*pf_app_compare)(struct pf_app_state *, struct pf_app_state *); + +struct pf_pptp_state { + struct pf_state *grev1_state; +}; + +struct pf_grev1_state { + struct pf_state *pptp_state; +}; + +struct pf_ike_state { + u_int64_t cookie; +}; + +struct pf_app_state { + pf_app_handler handler; + pf_app_compare compare_lan_ext; + pf_app_compare compare_ext_gwy; + union { + struct pf_pptp_state pptp; + struct pf_grev1_state grev1; + struct pf_ike_state ike; + } u; +}; + +/* keep synced with struct pf_state, used in RB_FIND */ +struct pf_state_key_cmp { + struct pf_state_host lan; + struct pf_state_host gwy; + struct pf_state_host ext_lan; + struct pf_state_host ext_gwy; + sa_family_t af_lan; + sa_family_t af_gwy; + u_int8_t proto; + u_int8_t direction; + u_int8_t proto_variant; + struct pf_app_state *app_state; +}; + +TAILQ_HEAD(pf_statelist, pf_state); + +struct pf_state_key { + struct pf_state_host lan; + struct pf_state_host gwy; + struct pf_state_host ext_lan; + struct pf_state_host ext_gwy; + sa_family_t af_lan; + sa_family_t af_gwy; + u_int8_t proto; + u_int8_t direction; + u_int8_t proto_variant; + struct pf_app_state *app_state; + u_int32_t flowsrc; + u_int32_t flowhash; + + RB_ENTRY(pf_state_key) entry_lan_ext; + RB_ENTRY(pf_state_key) entry_ext_gwy; + struct pf_statelist states; + u_int32_t refcnt; +}; + + +/* keep synced with struct pf_state, used in RB_FIND */ +struct pf_state_cmp { + u_int64_t id; + u_int32_t creatorid; + u_int32_t pad; +}; + +/* flowhash key (12-bytes multiple for performance) */ +struct pf_flowhash_key { + struct pf_state_host ap1; /* address+port blob 1 */ + struct pf_state_host ap2; /* address+port blob 2 */ + u_int32_t af; + u_int32_t proto; +}; +#endif /* KERNEL */ + +struct hook_desc; +TAILQ_HEAD(hook_desc_head, hook_desc); + +#ifdef KERNEL +struct pf_state { + u_int64_t id; + u_int32_t creatorid; + u_int32_t pad; + + TAILQ_ENTRY(pf_state) entry_list; + TAILQ_ENTRY(pf_state) next; + RB_ENTRY(pf_state) entry_id; + struct pf_state_peer src; + struct pf_state_peer dst; + union pf_rule_ptr rule; + union pf_rule_ptr anchor; + union pf_rule_ptr nat_rule; + struct pf_addr rt_addr; + struct hook_desc_head unlink_hooks; + struct pf_state_key *state_key; + struct pfi_kif *kif; + struct pfi_kif *rt_kif; + struct pf_src_node *src_node; + struct pf_src_node *nat_src_node; + u_int64_t packets[2]; + u_int64_t bytes[2]; + u_int64_t creation; + u_int64_t expire; + u_int64_t pfsync_time; + u_int16_t tag; + u_int8_t log; + u_int8_t allow_opts; + u_int8_t timeout; + u_int8_t sync_flags; +}; +#endif /* KERNEL */ + +#define PFSTATE_NOSYNC 0x01 +#define PFSTATE_FROMSYNC 0x02 +#define PFSTATE_STALE 0x04 + +#define __packed __attribute__((__packed__)) + +/* + * Unified state structures for pulling states out of the kernel + * used by pfsync(4) and the pf(4) ioctl. + */ +struct pfsync_state_scrub { + u_int16_t pfss_flags; + u_int8_t pfss_ttl; /* stashed TTL */ +#define PFSYNC_SCRUB_FLAG_VALID 0x01 + u_int8_t scrub_flag; + u_int32_t pfss_ts_mod; /* timestamp modulation */ +} __packed; + +struct pfsync_state_host { + struct pf_addr addr; + union pf_state_xport xport; + u_int16_t pad[2]; +} __packed; + +struct pfsync_state_peer { + struct pfsync_state_scrub scrub; /* state is scrubbed */ + u_int32_t seqlo; /* Max sequence number sent */ + u_int32_t seqhi; /* Max the other end ACKd + win */ + u_int32_t seqdiff; /* Sequence number modulator */ + u_int16_t max_win; /* largest window (pre scaling) */ + u_int16_t mss; /* Maximum segment size option */ + u_int8_t state; /* active state level */ + u_int8_t wscale; /* window scaling factor */ + u_int8_t pad[6]; +} __packed; + +struct pfsync_state { + u_int32_t id[2]; + char ifname[IFNAMSIZ]; + struct pfsync_state_host lan; + struct pfsync_state_host gwy; + struct pfsync_state_host ext_lan; + struct pfsync_state_host ext_gwy; + struct pfsync_state_peer src; + struct pfsync_state_peer dst; + struct pf_addr rt_addr; + struct hook_desc_head unlink_hooks; +#if !defined(__LP64__) + u_int32_t _pad[2]; +#endif /* !__LP64__ */ + u_int32_t rule; + u_int32_t anchor; + u_int32_t nat_rule; + u_int64_t creation; + u_int64_t expire; + u_int32_t packets[2][2]; + u_int32_t bytes[2][2]; + u_int32_t creatorid; + u_int16_t tag; + sa_family_t af_lan; + sa_family_t af_gwy; + u_int8_t proto; + u_int8_t direction; + u_int8_t log; + u_int8_t allow_opts; + u_int8_t timeout; + u_int8_t sync_flags; + u_int8_t updates; + u_int8_t proto_variant; + u_int8_t __pad; + u_int32_t flowhash; +} __packed; + +#define PFSYNC_FLAG_COMPRESS 0x01 +#define PFSYNC_FLAG_STALE 0x02 +#define PFSYNC_FLAG_SRCNODE 0x04 +#define PFSYNC_FLAG_NATSRCNODE 0x08 + +#ifdef KERNEL +/* for copies to/from userland via pf_ioctl() */ +#define pf_state_peer_to_pfsync(s, d) do { \ + (d)->seqlo = (s)->seqlo; \ + (d)->seqhi = (s)->seqhi; \ + (d)->seqdiff = (s)->seqdiff; \ + (d)->max_win = (s)->max_win; \ + (d)->mss = (s)->mss; \ + (d)->state = (s)->state; \ + (d)->wscale = (s)->wscale; \ + if ((s)->scrub) { \ + (d)->scrub.pfss_flags = \ + (s)->scrub->pfss_flags & PFSS_TIMESTAMP; \ + (d)->scrub.pfss_ttl = (s)->scrub->pfss_ttl; \ + (d)->scrub.pfss_ts_mod = (s)->scrub->pfss_ts_mod; \ + (d)->scrub.scrub_flag = PFSYNC_SCRUB_FLAG_VALID; \ + } \ +} while (0) + +#define pf_state_peer_from_pfsync(s, d) do { \ + (d)->seqlo = (s)->seqlo; \ + (d)->seqhi = (s)->seqhi; \ + (d)->seqdiff = (s)->seqdiff; \ + (d)->max_win = (s)->max_win; \ + (d)->mss = ntohs((s)->mss); \ + (d)->state = (s)->state; \ + (d)->wscale = (s)->wscale; \ + if ((s)->scrub.scrub_flag == PFSYNC_SCRUB_FLAG_VALID && \ + (d)->scrub != NULL) { \ + (d)->scrub->pfss_flags = \ + ntohs((s)->scrub.pfss_flags) & PFSS_TIMESTAMP; \ + (d)->scrub->pfss_ttl = (s)->scrub.pfss_ttl; \ + (d)->scrub->pfss_ts_mod = (s)->scrub.pfss_ts_mod; \ + } \ +} while (0) +#endif /* KERNEL */ + +#define pf_state_counter_to_pfsync(s, d) do { \ + d[0] = (s>>32)&0xffffffff; \ + d[1] = s&0xffffffff; \ +} while (0) + +#define pf_state_counter_from_pfsync(s) \ + (((u_int64_t)(s[0])<<32) | (u_int64_t)(s[1])) + + + +TAILQ_HEAD(pf_rulequeue, pf_rule); + +struct pf_anchor; + +struct pf_ruleset { + struct { + struct pf_rulequeue queues[2]; + struct { + struct pf_rulequeue *ptr; + struct pf_rule **ptr_array; + u_int32_t rcount; + u_int32_t ticket; + int open; + } active, inactive; + } rules[PF_RULESET_MAX]; + struct pf_anchor *anchor; + u_int32_t tticket; + int tables; + int topen; +}; + +RB_HEAD(pf_anchor_global, pf_anchor); +RB_HEAD(pf_anchor_node, pf_anchor); +struct pf_anchor { + RB_ENTRY(pf_anchor) entry_global; + RB_ENTRY(pf_anchor) entry_node; + struct pf_anchor *parent; + struct pf_anchor_node children; + char name[PF_ANCHOR_NAME_SIZE]; + char path[MAXPATHLEN]; + struct pf_ruleset ruleset; + int refcnt; /* anchor rules */ + int match; + char owner[PF_OWNER_NAME_SIZE]; +}; +#ifdef KERNEL +RB_PROTOTYPE_SC(__private_extern__, pf_anchor_global, pf_anchor, entry_global, + pf_anchor_compare); +RB_PROTOTYPE_SC(__private_extern__, pf_anchor_node, pf_anchor, entry_node, + pf_anchor_compare); +#else /* !KERNEL */ +RB_PROTOTYPE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); +RB_PROTOTYPE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); +#endif /* !KERNEL */ + +#define PF_RESERVED_ANCHOR "_pf" + +#define PFR_TFLAG_PERSIST 0x00000001 +#define PFR_TFLAG_CONST 0x00000002 +#define PFR_TFLAG_ACTIVE 0x00000004 +#define PFR_TFLAG_INACTIVE 0x00000008 +#define PFR_TFLAG_REFERENCED 0x00000010 +#define PFR_TFLAG_REFDANCHOR 0x00000020 +#define PFR_TFLAG_USRMASK 0x00000003 +#define PFR_TFLAG_SETMASK 0x0000003C +#define PFR_TFLAG_ALLMASK 0x0000003F + +struct pfr_table { + char pfrt_anchor[MAXPATHLEN]; + char pfrt_name[PF_TABLE_NAME_SIZE]; + u_int32_t pfrt_flags; + u_int8_t pfrt_fback; +}; + +enum { PFR_FB_NONE, PFR_FB_MATCH, PFR_FB_ADDED, PFR_FB_DELETED, + PFR_FB_CHANGED, PFR_FB_CLEARED, PFR_FB_DUPLICATE, + PFR_FB_NOTMATCH, PFR_FB_CONFLICT, PFR_FB_MAX }; + +struct pfr_addr { + union { + struct in_addr _pfra_ip4addr; + struct in6_addr _pfra_ip6addr; + } pfra_u; + u_int8_t pfra_af; + u_int8_t pfra_net; + u_int8_t pfra_not; + u_int8_t pfra_fback; +}; +#define pfra_ip4addr pfra_u._pfra_ip4addr +#define pfra_ip6addr pfra_u._pfra_ip6addr + +enum { PFR_DIR_IN, PFR_DIR_OUT, PFR_DIR_MAX }; +enum { PFR_OP_BLOCK, PFR_OP_PASS, PFR_OP_ADDR_MAX, PFR_OP_TABLE_MAX }; +#define PFR_OP_XPASS PFR_OP_ADDR_MAX + +struct pfr_astats { + struct pfr_addr pfras_a; +#if !defined(__LP64__) + u_int32_t _pad; +#endif /* !__LP64__ */ + u_int64_t pfras_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + u_int64_t pfras_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + u_int64_t pfras_tzero; +}; + +enum { PFR_REFCNT_RULE, PFR_REFCNT_ANCHOR, PFR_REFCNT_MAX }; + +struct pfr_tstats { + struct pfr_table pfrts_t; + u_int64_t pfrts_packets[PFR_DIR_MAX][PFR_OP_TABLE_MAX]; + u_int64_t pfrts_bytes[PFR_DIR_MAX][PFR_OP_TABLE_MAX]; + u_int64_t pfrts_match; + u_int64_t pfrts_nomatch; + u_int64_t pfrts_tzero; + int pfrts_cnt; + int pfrts_refcnt[PFR_REFCNT_MAX]; +#if !defined(__LP64__) + u_int32_t _pad; +#endif /* !__LP64__ */ +}; +#define pfrts_name pfrts_t.pfrt_name +#define pfrts_flags pfrts_t.pfrt_flags + +#ifdef KERNEL +SLIST_HEAD(pfr_kentryworkq, pfr_kentry); +struct pfr_kentry { + struct radix_node pfrke_node[2]; + union sockaddr_union pfrke_sa; + u_int64_t pfrke_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + u_int64_t pfrke_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + SLIST_ENTRY(pfr_kentry) pfrke_workq; + u_int64_t pfrke_tzero; + u_int8_t pfrke_af; + u_int8_t pfrke_net; + u_int8_t pfrke_not; + u_int8_t pfrke_mark; + u_int8_t pfrke_intrpool; +}; + +SLIST_HEAD(pfr_ktableworkq, pfr_ktable); +RB_HEAD(pfr_ktablehead, pfr_ktable); +struct pfr_ktable { + struct pfr_tstats pfrkt_ts; + RB_ENTRY(pfr_ktable) pfrkt_tree; + SLIST_ENTRY(pfr_ktable) pfrkt_workq; + struct radix_node_head *pfrkt_ip4; + struct radix_node_head *pfrkt_ip6; + struct pfr_ktable *pfrkt_shadow; + struct pfr_ktable *pfrkt_root; + struct pf_ruleset *pfrkt_rs; + u_int64_t pfrkt_larg; + u_int32_t pfrkt_nflags; +}; +#define pfrkt_t pfrkt_ts.pfrts_t +#define pfrkt_name pfrkt_t.pfrt_name +#define pfrkt_anchor pfrkt_t.pfrt_anchor +#define pfrkt_ruleset pfrkt_t.pfrt_ruleset +#define pfrkt_flags pfrkt_t.pfrt_flags +#define pfrkt_cnt pfrkt_ts.pfrts_cnt +#define pfrkt_refcnt pfrkt_ts.pfrts_refcnt +#define pfrkt_packets pfrkt_ts.pfrts_packets +#define pfrkt_bytes pfrkt_ts.pfrts_bytes +#define pfrkt_match pfrkt_ts.pfrts_match +#define pfrkt_nomatch pfrkt_ts.pfrts_nomatch +#define pfrkt_tzero pfrkt_ts.pfrts_tzero + +RB_HEAD(pf_state_tree_lan_ext, pf_state_key); +RB_PROTOTYPE_SC(__private_extern__, pf_state_tree_lan_ext, pf_state_key, + entry_lan_ext, pf_state_compare_lan_ext); + +RB_HEAD(pf_state_tree_ext_gwy, pf_state_key); +RB_PROTOTYPE_SC(__private_extern__, pf_state_tree_ext_gwy, pf_state_key, + entry_ext_gwy, pf_state_compare_ext_gwy); + +RB_HEAD(pfi_ifhead, pfi_kif); + +/* state tables */ +extern struct pf_state_tree_lan_ext pf_statetbl_lan_ext; +extern struct pf_state_tree_ext_gwy pf_statetbl_ext_gwy; + +/* keep synced with pfi_kif, used in RB_FIND */ +struct pfi_kif_cmp { + char pfik_name[IFNAMSIZ]; +}; + +struct pfi_kif { + char pfik_name[IFNAMSIZ]; + RB_ENTRY(pfi_kif) pfik_tree; + u_int64_t pfik_packets[2][2][2]; + u_int64_t pfik_bytes[2][2][2]; + u_int64_t pfik_tzero; + int pfik_flags; + void *pfik_ah_cookie; + struct ifnet *pfik_ifp; + int pfik_states; + int pfik_rules; + TAILQ_HEAD(, pfi_dynaddr) pfik_dynaddrs; +}; + +enum pfi_kif_refs { + PFI_KIF_REF_NONE, + PFI_KIF_REF_STATE, + PFI_KIF_REF_RULE +}; + +struct pfi_uif { +#else /* !KERNEL */ +struct pfi_kif { +#endif /* !KERNEL */ + char pfik_name[IFNAMSIZ]; + u_int64_t pfik_packets[2][2][2]; + u_int64_t pfik_bytes[2][2][2]; + u_int64_t pfik_tzero; + int pfik_flags; + int pfik_states; + int pfik_rules; +#if !defined(__LP64__) + u_int32_t _pad; +#endif /* !__LP64__ */ +}; + +#define PFI_IFLAG_SKIP 0x0100 /* skip filtering on interface */ + +#ifdef KERNEL +struct pf_pdesc { + struct { + int done; + uid_t uid; + gid_t gid; + pid_t pid; + } lookup; + u_int64_t tot_len; /* Make Mickey money */ + union { + struct tcphdr *tcp; + struct udphdr *udp; + struct icmp *icmp; +#if INET6 + struct icmp6_hdr *icmp6; +#endif /* INET6 */ + struct pf_grev1_hdr *grev1; + struct pf_esp_hdr *esp; + void *any; + } hdr; + + /* XXX TODO: Change baddr and naddr to *saddr */ + struct pf_addr baddr; /* src address before translation */ + struct pf_addr bdaddr; /* dst address before translation */ + struct pf_addr naddr; /* src address after translation */ + struct pf_addr ndaddr; /* dst address after translation */ + struct pf_rule *nat_rule; /* nat/rdr rule applied to packet */ + struct pf_addr *src; + struct pf_addr *dst; + struct ether_header + *eh; + struct mbuf *mp; + int lmw; /* lazy writable offset */ + struct pf_mtag *pf_mtag; + u_int16_t *ip_sum; + u_int32_t off; /* protocol header offset */ + u_int32_t hdrlen; /* protocol header length */ + u_int32_t p_len; /* total length of payload */ + u_int16_t flags; /* Let SCRUB trigger behavior in */ + /* state code. Easier than tags */ +#define PFDESC_TCP_NORM 0x0001 /* TCP shall be statefully scrubbed */ +#define PFDESC_IP_REAS 0x0002 /* IP frags would've been reassembled */ +#define PFDESC_IP_FRAG 0x0004 /* This is a fragment */ + sa_family_t af; + sa_family_t naf; /* address family after translation */ + u_int8_t proto; + u_int8_t tos; + u_int8_t ttl; + u_int8_t proto_variant; + mbuf_svc_class_t sc; /* mbuf service class (MBUF_SVC) */ + u_int32_t pktflags; /* mbuf packet flags (PKTF) */ + u_int32_t flowsrc; /* flow source (FLOWSRC) */ + u_int32_t flowhash; /* flow hash to identify the sender */ +}; +#endif /* KERNEL */ + +/* flags for RDR options */ +#define PF_DPORT_RANGE 0x01 /* Dest port uses range */ +#define PF_RPORT_RANGE 0x02 /* RDR'ed port uses range */ + +/* Reasons code for passing/dropping a packet */ +#define PFRES_MATCH 0 /* Explicit match of a rule */ +#define PFRES_BADOFF 1 /* Bad offset for pull_hdr */ +#define PFRES_FRAG 2 /* Dropping following fragment */ +#define PFRES_SHORT 3 /* Dropping short packet */ +#define PFRES_NORM 4 /* Dropping by normalizer */ +#define PFRES_MEMORY 5 /* Dropped due to lacking mem */ +#define PFRES_TS 6 /* Bad TCP Timestamp (RFC1323) */ +#define PFRES_CONGEST 7 /* Congestion (of ipintrq) */ +#define PFRES_IPOPTIONS 8 /* IP option */ +#define PFRES_PROTCKSUM 9 /* Protocol checksum invalid */ +#define PFRES_BADSTATE 10 /* State mismatch */ +#define PFRES_STATEINS 11 /* State insertion failure */ +#define PFRES_MAXSTATES 12 /* State limit */ +#define PFRES_SRCLIMIT 13 /* Source node/conn limit */ +#define PFRES_SYNPROXY 14 /* SYN proxy */ +#define PFRES_DUMMYNET 15 /* Dummynet */ +#define PFRES_MAX 16 /* total+1 */ + +#define PFRES_NAMES { \ + "match", \ + "bad-offset", \ + "fragment", \ + "short", \ + "normalize", \ + "memory", \ + "bad-timestamp", \ + "congestion", \ + "ip-option", \ + "proto-cksum", \ + "state-mismatch", \ + "state-insert", \ + "state-limit", \ + "src-limit", \ + "synproxy", \ + "dummynet", \ + NULL \ +} + +/* Counters for other things we want to keep track of */ +#define LCNT_STATES 0 /* states */ +#define LCNT_SRCSTATES 1 /* max-src-states */ +#define LCNT_SRCNODES 2 /* max-src-nodes */ +#define LCNT_SRCCONN 3 /* max-src-conn */ +#define LCNT_SRCCONNRATE 4 /* max-src-conn-rate */ +#define LCNT_OVERLOAD_TABLE 5 /* entry added to overload table */ +#define LCNT_OVERLOAD_FLUSH 6 /* state entries flushed */ +#define LCNT_MAX 7 /* total+1 */ + +#define LCNT_NAMES { \ + "max states per rule", \ + "max-src-states", \ + "max-src-nodes", \ + "max-src-conn", \ + "max-src-conn-rate", \ + "overload table insertion", \ + "overload flush states", \ + NULL \ +} + +/* UDP state enumeration */ +#define PFUDPS_NO_TRAFFIC 0 +#define PFUDPS_SINGLE 1 +#define PFUDPS_MULTIPLE 2 + +#define PFUDPS_NSTATES 3 /* number of state levels */ + +#define PFUDPS_NAMES { \ + "NO_TRAFFIC", \ + "SINGLE", \ + "MULTIPLE", \ + NULL \ +} + +/* GREv1 protocol state enumeration */ +#define PFGRE1S_NO_TRAFFIC 0 +#define PFGRE1S_INITIATING 1 +#define PFGRE1S_ESTABLISHED 2 + +#define PFGRE1S_NSTATES 3 /* number of state levels */ + +#define PFGRE1S_NAMES { \ + "NO_TRAFFIC", \ + "INITIATING", \ + "ESTABLISHED", \ + NULL \ +} + +#define PFESPS_NO_TRAFFIC 0 +#define PFESPS_INITIATING 1 +#define PFESPS_ESTABLISHED 2 + +#define PFESPS_NSTATES 3 /* number of state levels */ + +#define PFESPS_NAMES { "NO_TRAFFIC", "INITIATING", "ESTABLISHED", NULL } + +/* Other protocol state enumeration */ +#define PFOTHERS_NO_TRAFFIC 0 +#define PFOTHERS_SINGLE 1 +#define PFOTHERS_MULTIPLE 2 + +#define PFOTHERS_NSTATES 3 /* number of state levels */ + +#define PFOTHERS_NAMES { \ + "NO_TRAFFIC", \ + "SINGLE", \ + "MULTIPLE", \ + NULL \ +} + +#define FCNT_STATE_SEARCH 0 +#define FCNT_STATE_INSERT 1 +#define FCNT_STATE_REMOVALS 2 +#define FCNT_MAX 3 + +#define SCNT_SRC_NODE_SEARCH 0 +#define SCNT_SRC_NODE_INSERT 1 +#define SCNT_SRC_NODE_REMOVALS 2 +#define SCNT_MAX 3 + +#ifdef KERNEL +#define ACTION_SET(a, x) \ + do { \ + if ((a) != NULL) \ + *(a) = (x); \ + } while (0) + +#define REASON_SET(a, x) \ + do { \ + if ((a) != NULL) \ + *(a) = (x); \ + if (x < PFRES_MAX) \ + pf_status.counters[x]++; \ + } while (0) +#endif /* KERNEL */ + +struct pf_status { + u_int64_t counters[PFRES_MAX]; + u_int64_t lcounters[LCNT_MAX]; /* limit counters */ + u_int64_t fcounters[FCNT_MAX]; + u_int64_t scounters[SCNT_MAX]; + u_int64_t pcounters[2][2][3]; + u_int64_t bcounters[2][2]; + u_int64_t stateid; + u_int32_t running; + u_int32_t states; + u_int32_t src_nodes; + u_int64_t since __attribute__((aligned(8))); + u_int32_t debug; + u_int32_t hostid; + char ifname[IFNAMSIZ]; + u_int8_t pf_chksum[PF_MD5_DIGEST_LENGTH]; +}; + +struct cbq_opts { + u_int32_t minburst; + u_int32_t maxburst; + u_int32_t pktsize; + u_int32_t maxpktsize; + u_int32_t ns_per_byte; + u_int32_t maxidle; + int32_t minidle; + u_int32_t offtime; + u_int32_t flags; +}; + +struct priq_opts { + u_int32_t flags; +}; + +struct qfq_opts { + u_int32_t flags; + u_int32_t lmax; +}; + +struct hfsc_opts { + /* real-time service curve */ + u_int64_t rtsc_m1; /* slope of the 1st segment in bps */ + u_int64_t rtsc_d; /* the x-projection of m1 in msec */ + u_int64_t rtsc_m2; /* slope of the 2nd segment in bps */ + u_int32_t rtsc_fl; /* service curve flags */ +#if !defined(__LP64__) + u_int32_t _pad; +#endif /* !__LP64__ */ + /* link-sharing service curve */ + u_int64_t lssc_m1; + u_int64_t lssc_d; + u_int64_t lssc_m2; + u_int32_t lssc_fl; +#if !defined(__LP64__) + u_int32_t __pad; +#endif /* !__LP64__ */ + /* upper-limit service curve */ + u_int64_t ulsc_m1; + u_int64_t ulsc_d; + u_int64_t ulsc_m2; + u_int32_t ulsc_fl; + u_int32_t flags; /* scheduler flags */ +}; + +struct fairq_opts { + u_int32_t nbuckets; /* hash buckets */ + u_int32_t flags; + u_int64_t hogs_m1; /* hog detection bandwidth */ + + /* link-sharing service curve */ + u_int64_t lssc_m1; + u_int64_t lssc_d; + u_int64_t lssc_m2; +}; + +/* bandwidth types */ +#define PF_ALTQ_BW_ABSOLUTE 1 /* bw in absolute value (bps) */ +#define PF_ALTQ_BW_PERCENT 2 /* bandwidth in percentage */ + +/* ALTQ rule flags */ +#define PF_ALTQF_TBR 0x1 /* enable Token Bucket Regulator */ + +/* queue rule flags */ +#define PF_ALTQ_QRF_WEIGHT 0x1 /* weight instead of priority */ + +struct pf_altq { + char ifname[IFNAMSIZ]; + + /* discipline-specific state */ + void *altq_disc __attribute__((aligned(8))); + TAILQ_ENTRY(pf_altq) entries __attribute__((aligned(8))); +#if !defined(__LP64__) + u_int32_t _pad[2]; +#endif /* !__LP64__ */ + + u_int32_t aflags; /* ALTQ rule flags */ + u_int32_t bwtype; /* bandwidth type */ + + /* scheduler spec */ + u_int32_t scheduler; /* scheduler type */ + u_int32_t tbrsize; /* tokenbucket regulator size */ + u_int64_t ifbandwidth; /* interface bandwidth */ + + /* queue spec */ + char qname[PF_QNAME_SIZE]; /* queue name */ + char parent[PF_QNAME_SIZE]; /* parent name */ + u_int32_t parent_qid; /* parent queue id */ + u_int32_t qrflags; /* queue rule flags */ + union { + u_int32_t priority; /* priority */ + u_int32_t weight; /* weight */ + }; + u_int32_t qlimit; /* queue size limit */ + u_int32_t flags; /* misc flags */ +#if !defined(__LP64__) + u_int32_t __pad; +#endif /* !__LP64__ */ + u_int64_t bandwidth; /* queue bandwidth */ + union { + struct cbq_opts cbq_opts; + struct priq_opts priq_opts; + struct hfsc_opts hfsc_opts; + struct fairq_opts fairq_opts; + struct qfq_opts qfq_opts; + } pq_u; + + u_int32_t qid; /* return value */ +}; + +struct pf_tagname { + TAILQ_ENTRY(pf_tagname) entries; + char name[PF_TAG_NAME_SIZE]; + u_int16_t tag; + int ref; +}; + +#define PFFRAG_FRENT_HIWAT 5000 /* Number of fragment entries */ +#define PFFRAG_FRAG_HIWAT 1000 /* Number of fragmented packets */ +#define PFFRAG_FRCENT_HIWAT 50000 /* Number of fragment cache entries */ +#define PFFRAG_FRCACHE_HIWAT 10000 /* Number of fragment descriptors */ + +#define PFR_KTABLE_HIWAT 1000 /* Number of tables */ +#define PFR_KENTRY_HIWAT 200000 /* Number of table entries */ +#define PFR_KENTRY_HIWAT_SMALL 100000 /* Number of table entries (tiny hosts) */ + +/* + * ioctl parameter structures + */ + +struct pfioc_pooladdr { + u_int32_t action; + u_int32_t ticket; + u_int32_t nr; + u_int32_t r_num; + u_int8_t r_action; + u_int8_t r_last; + u_int8_t af; + char anchor[MAXPATHLEN]; + struct pf_pooladdr addr; +}; + +struct pfioc_rule { + u_int32_t action; + u_int32_t ticket; + u_int32_t pool_ticket; + u_int32_t nr; + char anchor[MAXPATHLEN]; + char anchor_call[MAXPATHLEN]; + struct pf_rule rule; +}; + +struct pfioc_natlook { + struct pf_addr saddr; + struct pf_addr daddr; + struct pf_addr rsaddr; + struct pf_addr rdaddr; + union pf_state_xport sxport; + union pf_state_xport dxport; + union pf_state_xport rsxport; + union pf_state_xport rdxport; + sa_family_t af; + u_int8_t proto; + u_int8_t proto_variant; + u_int8_t direction; +}; + +struct pfioc_state { + struct pfsync_state state; +}; + +struct pfioc_src_node_kill { + /* XXX returns the number of src nodes killed in psnk_af */ + sa_family_t psnk_af; + struct pf_rule_addr psnk_src; + struct pf_rule_addr psnk_dst; +}; + +struct pfioc_state_addr_kill { + struct pf_addr_wrap addr; + u_int8_t reserved_[3]; + u_int8_t neg; + union pf_rule_xport xport; +}; + +struct pfioc_state_kill { + /* XXX returns the number of states killed in psk_af */ + sa_family_t psk_af; + u_int8_t psk_proto; + u_int8_t psk_proto_variant; + u_int8_t _pad; + struct pfioc_state_addr_kill psk_src; + struct pfioc_state_addr_kill psk_dst; + char psk_ifname[IFNAMSIZ]; + char psk_ownername[PF_OWNER_NAME_SIZE]; +}; + +struct pfioc_states { + int ps_len; + union { + caddr_t psu_buf; + struct pfsync_state *psu_states; + } ps_u __attribute__((aligned(8))); +#define ps_buf ps_u.psu_buf +#define ps_states ps_u.psu_states +}; + +#ifdef KERNEL +struct pfioc_states_32 { + int ps_len; + union { + user32_addr_t psu_buf; + user32_addr_t psu_states; + } ps_u __attribute__((aligned(8))); +}; + +struct pfioc_states_64 { + int ps_len; + union { + user64_addr_t psu_buf; + user64_addr_t psu_states; + } ps_u __attribute__((aligned(8))); +}; +#endif /* KERNEL */ + +#define PFTOK_PROCNAME_LEN 64 +#pragma pack(1) +struct pfioc_token { + u_int64_t token_value; + u_int64_t timestamp; + pid_t pid; + char proc_name[PFTOK_PROCNAME_LEN]; +}; +#pragma pack() + +struct pfioc_kernel_token { + SLIST_ENTRY(pfioc_kernel_token) next; + struct pfioc_token token; +}; + +struct pfioc_remove_token { + u_int64_t token_value; + u_int64_t refcount; +}; + +struct pfioc_tokens { + int size; + union { + caddr_t pgtu_buf; + struct pfioc_token *pgtu_tokens; + } pgt_u __attribute__((aligned(8))); +#define pgt_buf pgt_u.pgtu_buf +#define pgt_tokens pgt_u.pgtu_tokens +}; + +#ifdef KERNEL +struct pfioc_tokens_32 { + int size; + union { + user32_addr_t pgtu_buf; + user32_addr_t pgtu_tokens; + } pgt_u __attribute__((aligned(8))); +}; + +struct pfioc_tokens_64 { + int size; + union { + user64_addr_t pgtu_buf; + user64_addr_t pgtu_tokens; + } pgt_u __attribute__((aligned(8))); +}; +#endif /* KERNEL */ + + +struct pfioc_src_nodes { + int psn_len; + union { + caddr_t psu_buf; + struct pf_src_node *psu_src_nodes; + } psn_u __attribute__((aligned(8))); +#define psn_buf psn_u.psu_buf +#define psn_src_nodes psn_u.psu_src_nodes +}; + +#ifdef KERNEL +struct pfioc_src_nodes_32 { + int psn_len; + union { + user32_addr_t psu_buf; + user32_addr_t psu_src_nodes; + } psn_u __attribute__((aligned(8))); +}; + +struct pfioc_src_nodes_64 { + int psn_len; + union { + user64_addr_t psu_buf; + user64_addr_t psu_src_nodes; + } psn_u __attribute__((aligned(8))); +}; +#endif /* KERNEL */ + +struct pfioc_if { + char ifname[IFNAMSIZ]; +}; + +struct pfioc_tm { + int timeout; + int seconds; +}; + +struct pfioc_limit { + int index; + unsigned limit; +}; + +struct pfioc_altq { + u_int32_t action; + u_int32_t ticket; + u_int32_t nr; + struct pf_altq altq __attribute__((aligned(8))); +}; + +struct pfioc_qstats { + u_int32_t ticket; + u_int32_t nr; + void *buf __attribute__((aligned(8))); + int nbytes __attribute__((aligned(8))); + u_int8_t scheduler; +}; + +struct pfioc_ruleset { + u_int32_t nr; + char path[MAXPATHLEN]; + char name[PF_ANCHOR_NAME_SIZE]; +}; + +#define PF_RULESET_ALTQ (PF_RULESET_MAX) +#define PF_RULESET_TABLE (PF_RULESET_MAX+1) +struct pfioc_trans { + int size; /* number of elements */ + int esize; /* size of each element in bytes */ + struct pfioc_trans_e { + int rs_num; + char anchor[MAXPATHLEN]; + u_int32_t ticket; + } *array __attribute__((aligned(8))); +}; + +#ifdef KERNEL +struct pfioc_trans_32 { + int size; /* number of elements */ + int esize; /* size of each element in bytes */ + user32_addr_t array __attribute__((aligned(8))); +}; + +struct pfioc_trans_64 { + int size; /* number of elements */ + int esize; /* size of each element in bytes */ + user64_addr_t array __attribute__((aligned(8))); +}; +#endif /* KERNEL */ + + +#define PFR_FLAG_ATOMIC 0x00000001 +#define PFR_FLAG_DUMMY 0x00000002 +#define PFR_FLAG_FEEDBACK 0x00000004 +#define PFR_FLAG_CLSTATS 0x00000008 +#define PFR_FLAG_ADDRSTOO 0x00000010 +#define PFR_FLAG_REPLACE 0x00000020 +#define PFR_FLAG_ALLRSETS 0x00000040 +#define PFR_FLAG_ALLMASK 0x0000007F +#ifdef KERNEL +#define PFR_FLAG_USERIOCTL 0x10000000 +#endif /* KERNEL */ + +struct pfioc_table { + struct pfr_table pfrio_table; + void *pfrio_buffer __attribute__((aligned(8))); + int pfrio_esize __attribute__((aligned(8))); + int pfrio_size; + int pfrio_size2; + int pfrio_nadd; + int pfrio_ndel; + int pfrio_nchange; + int pfrio_flags; + u_int32_t pfrio_ticket; +}; +#define pfrio_exists pfrio_nadd +#define pfrio_nzero pfrio_nadd +#define pfrio_nmatch pfrio_nadd +#define pfrio_naddr pfrio_size2 +#define pfrio_setflag pfrio_size2 +#define pfrio_clrflag pfrio_nadd + +#ifdef KERNEL +struct pfioc_table_32 { + struct pfr_table pfrio_table; + user32_addr_t pfrio_buffer __attribute__((aligned(8))); + int pfrio_esize __attribute__((aligned(8))); + int pfrio_size; + int pfrio_size2; + int pfrio_nadd; + int pfrio_ndel; + int pfrio_nchange; + int pfrio_flags; + u_int32_t pfrio_ticket; +}; + +struct pfioc_table_64 { + struct pfr_table pfrio_table; + user64_addr_t pfrio_buffer __attribute__((aligned(8))); + int pfrio_esize __attribute__((aligned(8))); + int pfrio_size; + int pfrio_size2; + int pfrio_nadd; + int pfrio_ndel; + int pfrio_nchange; + int pfrio_flags; + u_int32_t pfrio_ticket; +}; +#endif /* KERNEL */ + +struct pfioc_iface { + char pfiio_name[IFNAMSIZ]; + void *pfiio_buffer __attribute__((aligned(8))); + int pfiio_esize __attribute__((aligned(8))); + int pfiio_size; + int pfiio_nzero; + int pfiio_flags; +}; + +#ifdef KERNEL +struct pfioc_iface_32 { + char pfiio_name[IFNAMSIZ]; + user32_addr_t pfiio_buffer __attribute__((aligned(8))); + int pfiio_esize __attribute__((aligned(8))); + int pfiio_size; + int pfiio_nzero; + int pfiio_flags; +}; + +struct pfioc_iface_64 { + char pfiio_name[IFNAMSIZ]; + user64_addr_t pfiio_buffer __attribute__((aligned(8))); + int pfiio_esize __attribute__((aligned(8))); + int pfiio_size; + int pfiio_nzero; + int pfiio_flags; +}; +#endif /* KERNEL */ + +struct pf_ifspeed { + char ifname[IFNAMSIZ]; + u_int64_t baudrate; +}; + +/* + * ioctl operations + */ + +#define DIOCSTART _IO ('D', 1) +#define DIOCSTOP _IO ('D', 2) +#define DIOCADDRULE _IOWR('D', 4, struct pfioc_rule) +#define DIOCGETSTARTERS _IOWR('D', 5, struct pfioc_tokens) +#define DIOCGETRULES _IOWR('D', 6, struct pfioc_rule) +#define DIOCGETRULE _IOWR('D', 7, struct pfioc_rule) +#define DIOCSTARTREF _IOR ('D', 8, u_int64_t) +#define DIOCSTOPREF _IOWR('D', 9, struct pfioc_remove_token) +/* XXX cut 10 - 17 */ +#define DIOCCLRSTATES _IOWR('D', 18, struct pfioc_state_kill) +#define DIOCGETSTATE _IOWR('D', 19, struct pfioc_state) +#define DIOCSETSTATUSIF _IOWR('D', 20, struct pfioc_if) +#define DIOCGETSTATUS _IOWR('D', 21, struct pf_status) +#define DIOCCLRSTATUS _IO ('D', 22) +#define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook) +#define DIOCSETDEBUG _IOWR('D', 24, u_int32_t) +#define DIOCGETSTATES _IOWR('D', 25, struct pfioc_states) +#define DIOCCHANGERULE _IOWR('D', 26, struct pfioc_rule) +#define DIOCINSERTRULE _IOWR('D', 27, struct pfioc_rule) +#define DIOCDELETERULE _IOWR('D', 28, struct pfioc_rule) +#define DIOCSETTIMEOUT _IOWR('D', 29, struct pfioc_tm) +#define DIOCGETTIMEOUT _IOWR('D', 30, struct pfioc_tm) +#define DIOCADDSTATE _IOWR('D', 37, struct pfioc_state) +#define DIOCCLRRULECTRS _IO ('D', 38) +#define DIOCGETLIMIT _IOWR('D', 39, struct pfioc_limit) +#define DIOCSETLIMIT _IOWR('D', 40, struct pfioc_limit) +#define DIOCKILLSTATES _IOWR('D', 41, struct pfioc_state_kill) +#define DIOCSTARTALTQ _IO ('D', 42) +#define DIOCSTOPALTQ _IO ('D', 43) +#define DIOCADDALTQ _IOWR('D', 45, struct pfioc_altq) +#define DIOCGETALTQS _IOWR('D', 47, struct pfioc_altq) +#define DIOCGETALTQ _IOWR('D', 48, struct pfioc_altq) +#define DIOCCHANGEALTQ _IOWR('D', 49, struct pfioc_altq) +#define DIOCGETQSTATS _IOWR('D', 50, struct pfioc_qstats) +#define DIOCBEGINADDRS _IOWR('D', 51, struct pfioc_pooladdr) +#define DIOCADDADDR _IOWR('D', 52, struct pfioc_pooladdr) +#define DIOCGETADDRS _IOWR('D', 53, struct pfioc_pooladdr) +#define DIOCGETADDR _IOWR('D', 54, struct pfioc_pooladdr) +#define DIOCCHANGEADDR _IOWR('D', 55, struct pfioc_pooladdr) +/* XXX cut 55 - 57 */ +#define DIOCGETRULESETS _IOWR('D', 58, struct pfioc_ruleset) +#define DIOCGETRULESET _IOWR('D', 59, struct pfioc_ruleset) +#define DIOCRCLRTABLES _IOWR('D', 60, struct pfioc_table) +#define DIOCRADDTABLES _IOWR('D', 61, struct pfioc_table) +#define DIOCRDELTABLES _IOWR('D', 62, struct pfioc_table) +#define DIOCRGETTABLES _IOWR('D', 63, struct pfioc_table) +#define DIOCRGETTSTATS _IOWR('D', 64, struct pfioc_table) +#define DIOCRCLRTSTATS _IOWR('D', 65, struct pfioc_table) +#define DIOCRCLRADDRS _IOWR('D', 66, struct pfioc_table) +#define DIOCRADDADDRS _IOWR('D', 67, struct pfioc_table) +#define DIOCRDELADDRS _IOWR('D', 68, struct pfioc_table) +#define DIOCRSETADDRS _IOWR('D', 69, struct pfioc_table) +#define DIOCRGETADDRS _IOWR('D', 70, struct pfioc_table) +#define DIOCRGETASTATS _IOWR('D', 71, struct pfioc_table) +#define DIOCRCLRASTATS _IOWR('D', 72, struct pfioc_table) +#define DIOCRTSTADDRS _IOWR('D', 73, struct pfioc_table) +#define DIOCRSETTFLAGS _IOWR('D', 74, struct pfioc_table) +#define DIOCRINADEFINE _IOWR('D', 77, struct pfioc_table) +#define DIOCOSFPFLUSH _IO('D', 78) +#define DIOCOSFPADD _IOWR('D', 79, struct pf_osfp_ioctl) +#define DIOCOSFPGET _IOWR('D', 80, struct pf_osfp_ioctl) +#define DIOCXBEGIN _IOWR('D', 81, struct pfioc_trans) +#define DIOCXCOMMIT _IOWR('D', 82, struct pfioc_trans) +#define DIOCXROLLBACK _IOWR('D', 83, struct pfioc_trans) +#define DIOCGETSRCNODES _IOWR('D', 84, struct pfioc_src_nodes) +#define DIOCCLRSRCNODES _IO('D', 85) +#define DIOCSETHOSTID _IOWR('D', 86, u_int32_t) +#define DIOCIGETIFACES _IOWR('D', 87, struct pfioc_iface) +#define DIOCSETIFFLAG _IOWR('D', 89, struct pfioc_iface) +#define DIOCCLRIFFLAG _IOWR('D', 90, struct pfioc_iface) +#define DIOCKILLSRCNODES _IOWR('D', 91, struct pfioc_src_node_kill) +#define DIOCGIFSPEED _IOWR('D', 92, struct pf_ifspeed) + +#ifdef KERNEL +RB_HEAD(pf_src_tree, pf_src_node); +RB_PROTOTYPE_SC(__private_extern__, pf_src_tree, pf_src_node, entry, + pf_src_compare); +extern struct pf_src_tree tree_src_tracking; + +RB_HEAD(pf_state_tree_id, pf_state); +RB_PROTOTYPE_SC(__private_extern__, pf_state_tree_id, pf_state, + entry_id, pf_state_compare_id); +extern struct pf_state_tree_id tree_id; +extern struct pf_state_queue state_list; + +TAILQ_HEAD(pf_poolqueue, pf_pool); +extern struct pf_poolqueue pf_pools[2]; +extern struct pf_palist pf_pabuf; +extern u_int32_t ticket_pabuf; +#if PF_ALTQ +TAILQ_HEAD(pf_altqqueue, pf_altq); +extern struct pf_altqqueue pf_altqs[2]; +extern u_int32_t ticket_altqs_active; +extern u_int32_t ticket_altqs_inactive; +extern int altqs_inactive_open; +extern struct pf_altqqueue *pf_altqs_active; +extern struct pf_altqqueue *pf_altqs_inactive; +#endif /* PF_ALTQ */ +extern struct pf_poolqueue *pf_pools_active; +extern struct pf_poolqueue *pf_pools_inactive; + +__private_extern__ int pf_tbladdr_setup(struct pf_ruleset *, + struct pf_addr_wrap *); +__private_extern__ void pf_tbladdr_remove(struct pf_addr_wrap *); +__private_extern__ void pf_tbladdr_copyout(struct pf_addr_wrap *); +__private_extern__ void pf_calc_skip_steps(struct pf_rulequeue *); +__private_extern__ u_int32_t pf_calc_state_key_flowhash(struct pf_state_key *); + +extern struct pool pf_src_tree_pl, pf_rule_pl; +extern struct pool pf_state_pl, pf_state_key_pl, pf_pooladdr_pl; +extern struct pool pf_state_scrub_pl; +#if PF_ALTQ +extern struct pool pf_altq_pl; +#endif /* PF_ALTQ */ +extern struct pool pf_app_state_pl; + +extern struct thread *pf_purge_thread; + +__private_extern__ void pfinit(void); +__private_extern__ void pf_purge_thread_fn(void *, wait_result_t); +__private_extern__ void pf_purge_expired_src_nodes(void); +__private_extern__ void pf_purge_expired_states(u_int32_t); +__private_extern__ void pf_unlink_state(struct pf_state *); +__private_extern__ void pf_free_state(struct pf_state *); +__private_extern__ int pf_insert_state(struct pfi_kif *, struct pf_state *); +__private_extern__ int pf_insert_src_node(struct pf_src_node **, + struct pf_rule *, struct pf_addr *, sa_family_t); +__private_extern__ void pf_src_tree_remove_state(struct pf_state *); +__private_extern__ struct pf_state *pf_find_state_byid(struct pf_state_cmp *); +__private_extern__ struct pf_state *pf_find_state_all(struct pf_state_key_cmp *, + u_int, int *); +__private_extern__ void pf_print_state(struct pf_state *); +__private_extern__ void pf_print_flags(u_int8_t); +__private_extern__ u_int16_t pf_cksum_fixup(u_int16_t, u_int16_t, u_int16_t, + u_int8_t); + +extern struct ifnet *sync_ifp; +extern struct pf_rule pf_default_rule; +__private_extern__ void pf_addrcpy(struct pf_addr *, struct pf_addr *, + u_int8_t); +__private_extern__ void pf_rm_rule(struct pf_rulequeue *, struct pf_rule *); + +struct ip_fw_args; +#if INET +__private_extern__ int pf_test(int, struct ifnet *, struct mbuf **, + struct ether_header *, struct ip_fw_args *); +#endif /* INET */ + +#if INET6 +__private_extern__ int pf_test6(int, struct ifnet *, struct mbuf **, + struct ether_header *, struct ip_fw_args *); +__private_extern__ void pf_poolmask(struct pf_addr *, struct pf_addr *, + struct pf_addr *, struct pf_addr *, u_int8_t); +__private_extern__ void pf_addr_inc(struct pf_addr *, sa_family_t); +#endif /* INET6 */ + +__private_extern__ struct mbuf *pf_lazy_makewritable(struct pf_pdesc *, + struct mbuf *, int); +__private_extern__ void *pf_pull_hdr(struct mbuf *, int, void *, int, + u_short *, u_short *, sa_family_t); +__private_extern__ void pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t); +__private_extern__ int pflog_packet(struct pfi_kif *, struct mbuf *, + sa_family_t, u_int8_t, u_int8_t, struct pf_rule *, struct pf_rule *, + struct pf_ruleset *, struct pf_pdesc *); +__private_extern__ int pf_match_addr(u_int8_t, struct pf_addr *, + struct pf_addr *, struct pf_addr *, sa_family_t); +__private_extern__ int pf_match_addr_range(struct pf_addr *, struct pf_addr *, + struct pf_addr *, sa_family_t); +__private_extern__ int pf_match(u_int8_t, u_int32_t, u_int32_t, u_int32_t); +__private_extern__ int pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t); +__private_extern__ int pf_match_xport(u_int8_t, u_int8_t, union pf_rule_xport *, + union pf_state_xport *); +__private_extern__ int pf_match_uid(u_int8_t, uid_t, uid_t, uid_t); +__private_extern__ int pf_match_gid(u_int8_t, gid_t, gid_t, gid_t); + +__private_extern__ void pf_normalize_init(void); +__private_extern__ int pf_normalize_isempty(void); +__private_extern__ int pf_normalize_ip(struct mbuf **, int, struct pfi_kif *, + u_short *, struct pf_pdesc *); +__private_extern__ int pf_normalize_ip6(struct mbuf **, int, struct pfi_kif *, + u_short *, struct pf_pdesc *); +__private_extern__ int pf_normalize_tcp(int, struct pfi_kif *, struct mbuf *, + int, int, void *, struct pf_pdesc *); +__private_extern__ void pf_normalize_tcp_cleanup(struct pf_state *); +__private_extern__ int pf_normalize_tcp_init(struct mbuf *, int, + struct pf_pdesc *, struct tcphdr *, struct pf_state_peer *, + struct pf_state_peer *); +__private_extern__ int pf_normalize_tcp_stateful(struct mbuf *, int, + struct pf_pdesc *, u_short *, struct tcphdr *, struct pf_state *, + struct pf_state_peer *, struct pf_state_peer *, int *); +__private_extern__ u_int64_t pf_state_expires(const struct pf_state *); +__private_extern__ void pf_purge_expired_fragments(void); +__private_extern__ int pf_routable(struct pf_addr *addr, sa_family_t af, + struct pfi_kif *); +__private_extern__ int pf_rtlabel_match(struct pf_addr *, sa_family_t, + struct pf_addr_wrap *); +__private_extern__ int pf_socket_lookup(int, struct pf_pdesc *); +__private_extern__ struct pf_state_key *pf_alloc_state_key(struct pf_state *, + struct pf_state_key *); +__private_extern__ void pfr_initialize(void); +__private_extern__ int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, + sa_family_t); +__private_extern__ void pfr_update_stats(struct pfr_ktable *, struct pf_addr *, + sa_family_t, u_int64_t, int, int, int); +__private_extern__ int pfr_pool_get(struct pfr_ktable *, int *, + struct pf_addr *, struct pf_addr **, struct pf_addr **, sa_family_t); +__private_extern__ void pfr_dynaddr_update(struct pfr_ktable *, + struct pfi_dynaddr *); +__private_extern__ void pfr_table_copyin_cleanup(struct pfr_table *); +__private_extern__ struct pfr_ktable *pfr_attach_table(struct pf_ruleset *, + char *); +__private_extern__ void pfr_detach_table(struct pfr_ktable *); +__private_extern__ int pfr_clr_tables(struct pfr_table *, int *, int); +__private_extern__ int pfr_add_tables(user_addr_t, int, int *, int); +__private_extern__ int pfr_del_tables(user_addr_t, int, int *, int); +__private_extern__ int pfr_get_tables(struct pfr_table *, user_addr_t, + int *, int); +__private_extern__ int pfr_get_tstats(struct pfr_table *, user_addr_t, + int *, int); +__private_extern__ int pfr_clr_tstats(user_addr_t, int, int *, int); +__private_extern__ int pfr_set_tflags(user_addr_t, int, int, int, int *, + int *, int); +__private_extern__ int pfr_clr_addrs(struct pfr_table *, int *, int); +__private_extern__ int pfr_insert_kentry(struct pfr_ktable *, struct pfr_addr *, + u_int64_t); +__private_extern__ int pfr_add_addrs(struct pfr_table *, user_addr_t, + int, int *, int); +__private_extern__ int pfr_del_addrs(struct pfr_table *, user_addr_t, + int, int *, int); +__private_extern__ int pfr_set_addrs(struct pfr_table *, user_addr_t, + int, int *, int *, int *, int *, int, u_int32_t); +__private_extern__ int pfr_get_addrs(struct pfr_table *, user_addr_t, + int *, int); +__private_extern__ int pfr_get_astats(struct pfr_table *, user_addr_t, + int *, int); +__private_extern__ int pfr_clr_astats(struct pfr_table *, user_addr_t, + int, int *, int); +__private_extern__ int pfr_tst_addrs(struct pfr_table *, user_addr_t, + int, int *, int); +__private_extern__ int pfr_ina_begin(struct pfr_table *, u_int32_t *, int *, + int); +__private_extern__ int pfr_ina_rollback(struct pfr_table *, u_int32_t, int *, + int); +__private_extern__ int pfr_ina_commit(struct pfr_table *, u_int32_t, int *, + int *, int); +__private_extern__ int pfr_ina_define(struct pfr_table *, user_addr_t, + int, int *, int *, u_int32_t, int); + +extern struct pfi_kif *pfi_all; + +__private_extern__ void pfi_initialize(void); +__private_extern__ struct pfi_kif *pfi_kif_get(const char *); +__private_extern__ void pfi_kif_ref(struct pfi_kif *, enum pfi_kif_refs); +__private_extern__ void pfi_kif_unref(struct pfi_kif *, enum pfi_kif_refs); +__private_extern__ int pfi_kif_match(struct pfi_kif *, struct pfi_kif *); +__private_extern__ void pfi_attach_ifnet(struct ifnet *); +__private_extern__ void pfi_detach_ifnet(struct ifnet *); +__private_extern__ int pfi_match_addr(struct pfi_dynaddr *, struct pf_addr *, + sa_family_t); +__private_extern__ int pfi_dynaddr_setup(struct pf_addr_wrap *, sa_family_t); +__private_extern__ void pfi_dynaddr_remove(struct pf_addr_wrap *); +__private_extern__ void pfi_dynaddr_copyout(struct pf_addr_wrap *); +__private_extern__ void pfi_update_status(const char *, struct pf_status *); +__private_extern__ int pfi_get_ifaces(const char *, user_addr_t, int *); +__private_extern__ int pfi_set_flags(const char *, int); +__private_extern__ int pfi_clear_flags(const char *, int); + +__private_extern__ u_int16_t pf_tagname2tag(char *); +__private_extern__ void pf_tag2tagname(u_int16_t, char *); +__private_extern__ void pf_tag_ref(u_int16_t); +__private_extern__ void pf_tag_unref(u_int16_t); +__private_extern__ int pf_tag_packet(struct mbuf *, struct pf_mtag *, + int, unsigned int, struct pf_pdesc *); +__private_extern__ void pf_step_into_anchor(int *, struct pf_ruleset **, int, + struct pf_rule **, struct pf_rule **, int *); +__private_extern__ int pf_step_out_of_anchor(int *, struct pf_ruleset **, int, + struct pf_rule **, struct pf_rule **, int *); +__private_extern__ u_int32_t pf_qname2qid(char *); +__private_extern__ void pf_qid2qname(u_int32_t, char *); +__private_extern__ void pf_qid_unref(u_int32_t); + +extern struct pf_status pf_status; +extern struct pool pf_frent_pl, pf_frag_pl; + +struct pf_pool_limit { + void *pp; + unsigned limit; +}; +extern struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX]; + +__private_extern__ int pf_af_hook(struct ifnet *, struct mbuf **, + struct mbuf **, unsigned int, int, struct ip_fw_args *); +__private_extern__ int pf_ifaddr_hook(struct ifnet *); +__private_extern__ void pf_ifnet_hook(struct ifnet *, int); + +/* + * The following are defined with "private extern" storage class for + * kernel, and "extern" for user-space. + */ +extern struct pf_anchor_global pf_anchors; +extern struct pf_anchor pf_main_anchor; +#define pf_main_ruleset pf_main_anchor.ruleset + +extern int pf_is_enabled; +extern int16_t pf_nat64_configured; +#define PF_IS_ENABLED (pf_is_enabled != 0) +extern u_int32_t pf_hash_seed; + +#if PF_ALTQ +extern u_int32_t altq_allowed; +#endif /* PF_ALTQ */ + +/* these ruleset functions can be linked into userland programs (pfctl) */ +__private_extern__ int pf_get_ruleset_number(u_int8_t); +__private_extern__ void pf_init_ruleset(struct pf_ruleset *); +__private_extern__ int pf_anchor_setup(struct pf_rule *, + const struct pf_ruleset *, const char *); +__private_extern__ int pf_anchor_copyout(const struct pf_ruleset *, + const struct pf_rule *, struct pfioc_rule *); +__private_extern__ void pf_anchor_remove(struct pf_rule *); +__private_extern__ void pf_remove_if_empty_ruleset(struct pf_ruleset *); +__private_extern__ struct pf_anchor *pf_find_anchor(const char *); +__private_extern__ struct pf_ruleset *pf_find_ruleset(const char *); +__private_extern__ struct pf_ruleset *pf_find_ruleset_with_owner(const char *, + const char *, int, int *); +__private_extern__ struct pf_ruleset *pf_find_or_create_ruleset(const char *); +__private_extern__ void pf_rs_initialize(void); + +__private_extern__ int pf_osfp_add(struct pf_osfp_ioctl *); +__private_extern__ struct pf_osfp_enlist *pf_osfp_fingerprint(struct pf_pdesc *, + struct mbuf *, int, const struct tcphdr *); +__private_extern__ struct pf_osfp_enlist *pf_osfp_fingerprint_hdr( + const struct ip *, const struct ip6_hdr *, const struct tcphdr *); +__private_extern__ void pf_osfp_flush(void); +__private_extern__ int pf_osfp_get(struct pf_osfp_ioctl *); +__private_extern__ void pf_osfp_initialize(void); +__private_extern__ int pf_osfp_match(struct pf_osfp_enlist *, pf_osfp_t); +__private_extern__ struct pf_os_fingerprint *pf_osfp_validate(void); +__private_extern__ struct pf_mtag *pf_find_mtag(struct mbuf *); +__private_extern__ struct pf_mtag *pf_get_mtag(struct mbuf *); +#else /* !KERNEL */ +extern struct pf_anchor_global pf_anchors; +extern struct pf_anchor pf_main_anchor; +#define pf_main_ruleset pf_main_anchor.ruleset + +/* these ruleset functions can be linked into userland programs (pfctl) */ +extern int pf_get_ruleset_number(u_int8_t); +extern void pf_init_ruleset(struct pf_ruleset *); +extern int pf_anchor_setup(struct pf_rule *, const struct pf_ruleset *, + const char *); +extern int pf_anchor_copyout(const struct pf_ruleset *, const struct pf_rule *, + struct pfioc_rule *); +extern void pf_anchor_remove(struct pf_rule *); +extern void pf_remove_if_empty_ruleset(struct pf_ruleset *); +extern struct pf_anchor *pf_find_anchor(const char *); +extern struct pf_ruleset *pf_find_ruleset(const char *); +extern struct pf_ruleset *pf_find_ruleset_with_owner(const char *, + const char *, int, int *); +extern struct pf_ruleset *pf_find_or_create_ruleset(const char *); +extern void pf_rs_initialize(void); +#endif /* !KERNEL */ + +#ifdef __cplusplus +} +#endif +#endif /* PF || !KERNEL */ +#endif /* PRIVATE */ +#endif /* _NET_PFVAR_H_ */ diff --git a/xnu/10.12/net/radix.h b/xnu/10.12/net/radix.h new file mode 100644 index 00000000..d48399aa --- /dev/null +++ b/xnu/10.12/net/radix.h @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2000-2008 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1988, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)radix.h 8.2 (Berkeley) 10/31/94 + * $FreeBSD: src/sys/net/radix.h,v 1.16.2.1 2000/05/03 19:17:11 wollman Exp $ + */ + +#ifndef _RADIX_H_ +#define _RADIX_H_ +#include + +#ifdef PRIVATE + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_RTABLE); +#endif + +/* + * Radix search tree node layout. + */ + +struct radix_node { + struct radix_mask *rn_mklist; /* list of masks contained in subtree */ + struct radix_node *rn_parent; /* parent */ + short rn_bit; /* bit offset; -1-index(netmask) */ + char rn_bmask; /* node: mask for bit test*/ + u_char rn_flags; /* enumerated next */ +#define RNF_NORMAL 1 /* leaf contains normal route */ +#define RNF_ROOT 2 /* leaf is root leaf for tree */ +#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */ + union { + struct { /* leaf only data: */ + caddr_t rn_Key; /* object of search */ + caddr_t rn_Mask; /* netmask, if present */ + struct radix_node *rn_Dupedkey; + } rn_leaf; + struct { /* node only data: */ + int rn_Off; /* where to start compare */ + struct radix_node *rn_L;/* progeny */ + struct radix_node *rn_R;/* progeny */ + } rn_node; + } rn_u; +#ifdef RN_DEBUG + int rn_info; + struct radix_node *rn_twin; + struct radix_node *rn_ybro; +#endif + +}; + +#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey +#define rn_key rn_u.rn_leaf.rn_Key +#define rn_mask rn_u.rn_leaf.rn_Mask +#define rn_offset rn_u.rn_node.rn_Off +#define rn_left rn_u.rn_node.rn_L +#define rn_right rn_u.rn_node.rn_R + +/* + * Annotations to tree concerning potential routes applying to subtrees. + */ + +struct radix_mask { + short rm_bit; /* bit offset; -1-index(netmask) */ + char rm_unused; /* cf. rn_bmask */ + u_char rm_flags; /* cf. rn_flags */ + struct radix_mask *rm_mklist; /* more masks to try */ + union { + caddr_t rmu_mask; /* the mask */ + struct radix_node *rmu_leaf; /* for normal routes */ + } rm_rmu; + int rm_refs; /* # of references to this struct */ +}; + +#define rm_mask rm_rmu.rmu_mask +#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */ + + +#define MKGet(m) {\ + if (rn_mkfreelist) {\ + m = rn_mkfreelist; \ + rn_mkfreelist = (m)->rm_mklist; \ + } else \ + R_Malloc(m, struct radix_mask *, sizeof (*(m))); }\ + +#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);} + +typedef int walktree_f_t(struct radix_node *, void *); +typedef int rn_matchf_t(struct radix_node *, void *); + +struct radix_node_head { + struct radix_node *rnh_treetop; + int rnh_addrsize; /* permit, but not require fixed keys */ + int rnh_pktsize; /* permit, but not require fixed keys */ + struct radix_node *(*rnh_addaddr) /* add based on sockaddr */ + (void *v, void *mask, + struct radix_node_head *head, struct radix_node nodes[]); + struct radix_node *(*rnh_addpkt) /* add based on packet hdr */ + (void *v, void *mask, + struct radix_node_head *head, struct radix_node nodes[]); + struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */ + (void *v, void *mask, struct radix_node_head *head); + struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */ + (void *v, void *mask, struct radix_node_head *head); + struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */ + (void *v, struct radix_node_head *head); + /* locate based on sockaddr and rn_matchf_t() */ + struct radix_node *(*rnh_matchaddr_args) + (void *v, struct radix_node_head *head, + rn_matchf_t *f, void *w); + struct radix_node *(*rnh_lookup) /* locate based on sockaddr */ + (void *v, void *mask, struct radix_node_head *head); + /* locate based on sockaddr, mask and rn_matchf_t() */ + struct radix_node *(*rnh_lookup_args) + (void *v, void *mask, struct radix_node_head *head, + rn_matchf_t *f, void *); + struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */ + (void *v, struct radix_node_head *head); + int (*rnh_walktree) /* traverse tree */ + (struct radix_node_head *head, walktree_f_t *f, void *w); + int (*rnh_walktree_from) /* traverse tree below a */ + (struct radix_node_head *head, void *a, void *m, + walktree_f_t *f, void *w); + void (*rnh_close) /* do something when the last ref drops */ + (struct radix_node *rn, struct radix_node_head *head); + struct radix_node rnh_nodes[3]; /* empty tree for common case */ + int rnh_cnt; /* tree dimension */ +}; + +#ifndef KERNEL +#define Bcmp(a, b, n) bcmp(((char *)(a)), ((char *)(b)), (n)) +#define Bcopy(a, b, n) bcopy(((char *)(a)), ((char *)(b)), (unsigned)(n)) +#define Bzero(p, n) bzero((char *)(p), (int)(n)); +#define R_Malloc(p, t, n) (p = (t) malloc((unsigned int)(n))) +#define R_Free(p) free((char *)p); +#else +#define Bcmp(a, b, n) bcmp(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n)) +#define Bcopy(a, b, n) bcopy(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n)) +#define Bzero(p, n) bzero((caddr_t)(p), (unsigned)(n)); +#define R_Malloc(p, t, n) (p = (t) _MALLOC((uint32_t)(n), M_RTABLE, M_WAITOK)) +#define R_Free(p) FREE((caddr_t)p, M_RTABLE); +#endif /*KERNEL*/ + +void rn_init(void); +int rn_inithead(void **, int); +int rn_refines(void *, void *); +struct radix_node + *rn_addmask(void *, int, int), + *rn_addroute(void *, void *, struct radix_node_head *, + struct radix_node [2]), + *rn_delete(void *, void *, struct radix_node_head *), + *rn_lookup(void *v_arg, void *m_arg, struct radix_node_head *head), + *rn_lookup_args(void *v_arg, void *m_arg, struct radix_node_head *head, + rn_matchf_t *, void *), + *rn_match(void *, struct radix_node_head *), + *rn_match_args(void *, struct radix_node_head *, rn_matchf_t *, void *); + +#endif /* PRIVATE */ +#endif /* _RADIX_H_ */ From d8267a98079cf258b62fa46ea58cc448097ffd70 Mon Sep 17 00:00:00 2001 From: Kevin LARQUEMIN Date: Sat, 29 Jul 2017 16:18:01 +0200 Subject: [PATCH 112/192] fixing MacOS Sierra 10.12 compilation --- Makefile | 50 +- base.c | 11 +- xnu/10.12/libkern/tree.h | 802 +++++++++++++ xnu/10.12/net/pfvar.h | 2446 ++++++++++++++++++++++++++++++++++++++ xnu/10.12/net/radix.h | 214 ++++ 5 files changed, 3498 insertions(+), 25 deletions(-) create mode 100644 xnu/10.12/libkern/tree.h create mode 100644 xnu/10.12/net/pfvar.h create mode 100644 xnu/10.12/net/radix.h diff --git a/Makefile b/Makefile index 8ed90649..86eb72d4 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,15 @@ override CFLAGS += -D_BSD_SOURCE -D_DEFAULT_SOURCE -Wall ifeq ($(OS), Linux) override CFLAGS += -std=c99 -D_XOPEN_SOURCE=600 endif +ifeq ($(OS), Darwin) +override CFLAGS +=-I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib +OSX_VERSION := $(shell sw_vers -productVersion | cut -d '.' -f 1,2) +OSX_ROOT_PATH := xnu +OSX_HEADERS_PATH := $(OSX_ROOT_PATH)/$(OSX_VERSION) +OSX_HEADERS := $(OSX_HEADERS_PATH)/net/pfvar.h $(OSX_HEADERS_PATH)/net/radix.h $(OSX_HEADERS_PATH)/libkern/tree.h +override CFLAGS +=-I$(OSX_HEADERS_PATH) +endif + #LDFLAGS += -fwhole-program ifdef USE_CRYPTO_POLARSSL @@ -56,6 +65,9 @@ $(CONF): OpenBSD) \ echo "#define USE_PF" >$(CONF) \ ;; \ + Darwin) \ + echo "#define USE_PF\n#define _APPLE_" >$(CONF) \ + ;; \ *) \ echo "Unknown system, only generic firewall code is compiled" 1>&2; \ echo "/* Unknown system, only generic firewall code is compiled */" >$(CONF) \ @@ -64,8 +76,8 @@ $(CONF): # Dependency on .git is useful to rebuild `version.c' after commit, but it breaks non-git builds. gen/version.c: *.c *.h gen/.build - rm -f $@.tmp - echo '/* this file is auto-generated during build */' > $@.tmp + $(RM) -f $@.tmp + echo '/* this file is -generated during build */' > $@.tmp echo '#include "../version.h"' >> $@.tmp echo 'const char* redsocks_version = ' >> $@.tmp if [ -d .git ]; then \ @@ -85,28 +97,17 @@ gen/.build: base.c: $(CONF) -$(DEPS): $(SRCS) - $(CC) -MM $(SRCS) 2>/dev/null >$(DEPS) || \ - ( \ - for I in $(wildcard *.h); do \ - export $${I//[-.]/_}_DEPS="`sed '/^\#[ \t]*include \?"\(.*\)".*/!d;s//\1/' $$I`"; \ - done; \ - echo -n >$(DEPS); \ - for SRC in $(SRCS); do \ - echo -n "$${SRC%.c}.o: " >>$(DEPS); \ - export SRC_DEPS="`sed '/\#[ \t]*include \?"\(.*\)".*/!d;s//\1/' $$SRC | sort`"; \ - while true; do \ - export SRC_DEPS_OLD="$$SRC_DEPS"; \ - export SRC_DEEP_DEPS=""; \ - for HDR in $$SRC_DEPS; do \ - eval export SRC_DEEP_DEPS="\"$$SRC_DEEP_DEPS \$$$${HDR//[-.]/_}_DEPS\""; \ - done; \ - export SRC_DEPS="`echo $$SRC_DEPS $$SRC_DEEP_DEPS | sed 's/ */\n/g' | sort -u`"; \ - test "$$SRC_DEPS" = "$$SRC_DEPS_OLD" && break; \ - done; \ - echo $$SRC $$SRC_DEPS >>$(DEPS); \ - done; \ - ) +ifeq ($(OS), Darwin) +$(OSX_HEADERS_PATH)/net/pfvar.h: + mkdir -p $(OSX_HEADERS_PATH)/net && curl -o $(OSX_HEADERS_PATH)/net/pfvar.h https://raw.githubusercontent.com/opensource-apple/xnu/$(OSX_VERSION)/bsd/net/pfvar.h +$(OSX_HEADERS_PATH)/net/radix.h: + mkdir -p $(OSX_HEADERS_PATH)/net && curl -o $(OSX_HEADERS_PATH)/net/radix.h https://raw.githubusercontent.com/opensource-apple/xnu/$(OSX_VERSION)/bsd/net/radix.h +$(OSX_HEADERS_PATH)/libkern/tree.h: + mkdir -p $(OSX_HEADERS_PATH)/libkern && curl -o $(OSX_HEADERS_PATH)/libkern/tree.h https://raw.githubusercontent.com/opensource-apple/xnu/$(OSX_VERSION)/libkern/libkern/tree.h +endif + +$(DEPS): $(OSX_HEADERS) $(SRCS) + $(CC) -MM $(CFLAGS) $(SRCS) 2>/dev/null >$(DEPS) -include $(DEPS) @@ -119,3 +120,4 @@ clean: distclean: clean $(RM) tags $(DEPS) $(RM) -r gen + $(RM) -r $(OSX_ROOT_PATH) diff --git a/base.c b/base.c index 65963081..dcfe434e 100644 --- a/base.c +++ b/base.c @@ -35,7 +35,16 @@ #if defined USE_PF # include +#if defined _APPLE_ +#define PRIVATE +#endif # include +#if defined _APPLE_ +#define sport sxport.port +#define dport dxport.port +#define rdport rdxport.port +#undef PRIVATE +#endif # include #endif #ifdef __FreeBSD__ @@ -449,7 +458,7 @@ static int base_init() exit(EXIT_SUCCESS); } } - + log_open(); // child has nothing to do with TTY if (instance.daemon) { diff --git a/xnu/10.12/libkern/tree.h b/xnu/10.12/libkern/tree.h new file mode 100644 index 00000000..3a26162b --- /dev/null +++ b/xnu/10.12/libkern/tree.h @@ -0,0 +1,802 @@ +/* + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $NetBSD: tree.h,v 1.13 2006/08/27 22:32:38 christos Exp $ */ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LIBKERN_TREE_H_ +#define _LIBKERN_TREE_H_ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_PLACEHOLDER NULL +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ +} + +#define RB_COLOR_MASK (uintptr_t)0x1 +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define _RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(name, elm, parent, field) do { \ + name##_RB_SETPARENT(elm, parent); \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + name##_RB_SETCOLOR(elm, RB_RED); \ +} while (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(name, black, red, field) do { \ + name##_RB_SETCOLOR(black, RB_BLACK); \ + name##_RB_SETCOLOR(red, RB_RED); \ +} while (/*CONSTCOND*/ 0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) (void)(x) +#endif + +#define RB_ROTATE_LEFT(name, head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ + name##_RB_SETPARENT(RB_LEFT(tmp, field),(elm)); \ + } \ + RB_AUGMENT(elm); \ + if (name##_RB_SETPARENT(tmp, name##_RB_GETPARENT(elm)) != NULL) { \ + if ((elm) == RB_LEFT(name##_RB_GETPARENT(elm), field)) \ + RB_LEFT(name##_RB_GETPARENT(elm), field) = (tmp); \ + else \ + RB_RIGHT(name##_RB_GETPARENT(elm), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + name##_RB_SETPARENT(elm, (tmp)); \ + RB_AUGMENT(tmp); \ + if ((name##_RB_GETPARENT(tmp))) \ + RB_AUGMENT(name##_RB_GETPARENT(tmp)); \ +} while (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(name, head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ + name##_RB_SETPARENT(RB_RIGHT(tmp, field), (elm)); \ + } \ + RB_AUGMENT(elm); \ + if (name##_RB_SETPARENT(tmp, name##_RB_GETPARENT(elm)) != NULL) { \ + if ((elm) == RB_LEFT(name##_RB_GETPARENT(elm), field)) \ + RB_LEFT(name##_RB_GETPARENT(elm), field) = (tmp); \ + else \ + RB_RIGHT(name##_RB_GETPARENT(elm), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + name##_RB_SETPARENT(elm, tmp); \ + RB_AUGMENT(tmp); \ + if ((name##_RB_GETPARENT(tmp))) \ + RB_AUGMENT(name##_RB_GETPARENT(tmp)); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ +void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +struct type *name##_RB_REMOVE(struct name *, struct type *); \ +struct type *name##_RB_INSERT(struct name *, struct type *); \ +struct type *name##_RB_FIND(struct name *, struct type *); \ +struct type *name##_RB_NEXT(struct type *); \ +struct type *name##_RB_MINMAX(struct name *, int); \ +struct type *name##_RB_GETPARENT(struct type*); \ +struct type *name##_RB_SETPARENT(struct type*, struct type*); \ +int name##_RB_GETCOLOR(struct type*); \ +void name##_RB_SETCOLOR(struct type*,int); + +/* Generates prototypes (with storage class) and inline functions */ +#define RB_PROTOTYPE_SC(_sc_, name, type, field, cmp) \ +_sc_ void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +_sc_ void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *); \ +_sc_ struct type *name##_RB_REMOVE(struct name *, struct type *); \ +_sc_ struct type *name##_RB_INSERT(struct name *, struct type *); \ +_sc_ struct type *name##_RB_FIND(struct name *, struct type *); \ +_sc_ struct type *name##_RB_NEXT(struct type *); \ +_sc_ struct type *name##_RB_MINMAX(struct name *, int); \ +_sc_ struct type *name##_RB_GETPARENT(struct type*); \ +_sc_ struct type *name##_RB_SETPARENT(struct type*, struct type*); \ +_sc_ int name##_RB_GETCOLOR(struct type*); \ +_sc_ void name##_RB_SETCOLOR(struct type*,int); + + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ +struct type *name##_RB_GETPARENT(struct type *elm) { \ + struct type *parent = _RB_PARENT(elm, field); \ + if( parent != NULL) { \ + parent = (struct type*)((uintptr_t)parent & ~RB_COLOR_MASK);\ + return( (struct type*) ( (parent == (struct type*) RB_PLACEHOLDER) ? NULL: parent));\ + } \ + return((struct type*)NULL); \ +} \ +int name##_RB_GETCOLOR(struct type *elm) { \ + int color = 0; \ + color = (int)((uintptr_t)_RB_PARENT(elm,field) & RB_COLOR_MASK);\ + return(color); \ +} \ +void name##_RB_SETCOLOR(struct type *elm,int color) { \ + struct type *parent = name##_RB_GETPARENT(elm); \ + if(parent == (struct type*)NULL) \ + parent = (struct type*) RB_PLACEHOLDER; \ + _RB_PARENT(elm, field) = (struct type*)((uintptr_t)parent | (unsigned int)color);\ +} \ +struct type *name##_RB_SETPARENT(struct type *elm, struct type *parent) { \ + int color = name##_RB_GETCOLOR(elm); \ + _RB_PARENT(elm, field) = parent; \ + if(color) name##_RB_SETCOLOR(elm, color); \ + return(name##_RB_GETPARENT(elm)); \ +} \ + \ +void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = name##_RB_GETPARENT(elm)) != NULL && \ + name##_RB_GETCOLOR(parent) == RB_RED) { \ + gparent = name##_RB_GETPARENT(parent); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && name##_RB_GETCOLOR(tmp) == RB_RED) { \ + name##_RB_SETCOLOR(tmp, RB_BLACK); \ + RB_SET_BLACKRED(name, parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(name, head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(name, parent, gparent, field); \ + RB_ROTATE_RIGHT(name,head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && name##_RB_GETCOLOR(tmp) == RB_RED) { \ + name##_RB_SETCOLOR(tmp, RB_BLACK); \ + RB_SET_BLACKRED(name, parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(name, head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(name, parent, gparent, field); \ + RB_ROTATE_LEFT(name, head, gparent, tmp, field); \ + } \ + } \ + name##_RB_SETCOLOR(head->rbh_root, RB_BLACK); \ +} \ + \ +void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || name##_RB_GETCOLOR(elm) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (name##_RB_GETCOLOR(tmp) == RB_RED) { \ + RB_SET_BLACKRED(name, tmp, parent, field); \ + RB_ROTATE_LEFT(name, head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK)) {\ + name##_RB_SETCOLOR(tmp, RB_RED); \ + elm = parent; \ + parent = name##_RB_GETPARENT(elm); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)) \ + != NULL) \ + name##_RB_SETCOLOR(oleft, RB_BLACK);\ + name##_RB_SETCOLOR(tmp, RB_RED); \ + RB_ROTATE_RIGHT(name, head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + name##_RB_SETCOLOR(tmp, (name##_RB_GETCOLOR(parent)));\ + name##_RB_SETCOLOR(parent, RB_BLACK); \ + if (RB_RIGHT(tmp, field)) \ + name##_RB_SETCOLOR(RB_RIGHT(tmp, field),RB_BLACK);\ + RB_ROTATE_LEFT(name, head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (name##_RB_GETCOLOR(tmp) == RB_RED) { \ + RB_SET_BLACKRED(name, tmp, parent, field); \ + RB_ROTATE_RIGHT(name, head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK)) {\ + name##_RB_SETCOLOR(tmp, RB_RED); \ + elm = parent; \ + parent = name##_RB_GETPARENT(elm); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)) \ + != NULL) \ + name##_RB_SETCOLOR(oright, RB_BLACK);\ + name##_RB_SETCOLOR(tmp, RB_RED); \ + RB_ROTATE_LEFT(name, head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + name##_RB_SETCOLOR(tmp,(name##_RB_GETCOLOR(parent)));\ + name##_RB_SETCOLOR(parent, RB_BLACK); \ + if (RB_LEFT(tmp, field)) \ + name##_RB_SETCOLOR(RB_LEFT(tmp, field), RB_BLACK);\ + RB_ROTATE_RIGHT(name, head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + name##_RB_SETCOLOR(elm, RB_BLACK); \ +} \ + \ +struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field)) != NULL) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = name##_RB_GETPARENT(elm); \ + color = name##_RB_GETCOLOR(elm); \ + if (child) \ + name##_RB_SETPARENT(child, parent); \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (name##_RB_GETPARENT(elm) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (name##_RB_GETPARENT(old)) { \ + if (RB_LEFT(name##_RB_GETPARENT(old), field) == old)\ + RB_LEFT(name##_RB_GETPARENT(old), field) = elm;\ + else \ + RB_RIGHT(name##_RB_GETPARENT(old), field) = elm;\ + RB_AUGMENT(name##_RB_GETPARENT(old)); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_SETPARENT(RB_LEFT(old, field), elm); \ + if (RB_RIGHT(old, field)) \ + name##_RB_SETPARENT(RB_RIGHT(old, field), elm); \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = name##_RB_GETPARENT(left)) != NULL); \ + } \ + goto color; \ + } \ + parent = name##_RB_GETPARENT(elm); \ + color = name##_RB_GETCOLOR(elm); \ + if (child) \ + name##_RB_SETPARENT(child, parent); \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(name, elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* ARGSUSED */ \ +struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (name##_RB_GETPARENT(elm) && \ + (elm == RB_LEFT(name##_RB_GETPARENT(elm), field))) \ + elm = name##_RB_GETPARENT(elm); \ + else { \ + while (name##_RB_GETPARENT(elm) && \ + (elm == RB_RIGHT(name##_RB_GETPARENT(elm), field)))\ + elm = name##_RB_GETPARENT(elm); \ + elm = name##_RB_GETPARENT(elm); \ + } \ + } \ + return (elm); \ +} \ + \ +struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + + +#define RB_PROTOTYPE_PREV(name, type, field, cmp) \ + RB_PROTOTYPE(name, type, field, cmp) \ +struct type *name##_RB_PREV(struct type *); + + +#define RB_PROTOTYPE_SC_PREV(_sc_, name, type, field, cmp) \ + RB_PROTOTYPE_SC(_sc_, name, type, field, cmp) \ +_sc_ struct type *name##_RB_PREV(struct type *); + +#define RB_GENERATE_PREV(name, type, field, cmp) \ + RB_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (name##_RB_GETPARENT(elm) && \ + (elm == RB_RIGHT(name##_RB_GETPARENT(elm), field))) \ + elm = name##_RB_GETPARENT(elm); \ + else { \ + while (name##_RB_GETPARENT(elm) && \ + (elm == RB_LEFT(name##_RB_GETPARENT(elm), field)))\ + elm = name##_RB_GETPARENT(elm); \ + elm = name##_RB_GETPARENT(elm); \ + } \ + } \ + return (elm); \ +} \ + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#endif /* _LIBKERN_TREE_H_ */ diff --git a/xnu/10.12/net/pfvar.h b/xnu/10.12/net/pfvar.h new file mode 100644 index 00000000..11e771bf --- /dev/null +++ b/xnu/10.12/net/pfvar.h @@ -0,0 +1,2446 @@ +/* + * Copyright (c) 2007-2015 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $apfw: git commit b6bf13f8321283cd7ee82b1795e86506084b1b95 $ */ +/* $OpenBSD: pfvar.h,v 1.259 2007/12/02 12:08:04 pascoe Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * NAT64 - Copyright (c) 2010 Viagenie Inc. (http://www.viagenie.ca) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _NET_PFVAR_H_ +#define _NET_PFVAR_H_ + +#ifdef PRIVATE +/* + * XXX + * XXX Private interfaces. Do not include this file; use pfctl(8) instead. + * XXX + */ +#if PF || !defined(KERNEL) + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef KERNEL +#include +#include +#include + +#include +#include + +#if BYTE_ORDER == BIG_ENDIAN +#define htobe64(x) (x) +#else /* LITTLE ENDIAN */ +#define htobe64(x) __DARWIN_OSSwapInt64(x) +#endif /* LITTLE_ENDIAN */ + +#define be64toh(x) htobe64(x) + +extern lck_rw_t *pf_perim_lock; +extern lck_mtx_t *pf_lock; + +struct pool { + struct zone *pool_zone; /* pointer to backend zone */ + const char *pool_name; /* name of pool */ + unsigned int pool_count; /* # of outstanding elements */ + unsigned int pool_hiwat; /* high watermark */ + unsigned int pool_limit; /* hard limit */ + unsigned int pool_fails; /* # of failed allocs due to limit */ +}; + +#define PR_NOWAIT FALSE +#define PR_WAITOK TRUE + +__private_extern__ void pool_init(struct pool *, size_t, unsigned int, + unsigned int, int, const char *, void *); +__private_extern__ void pool_destroy(struct pool *); +__private_extern__ void pool_sethiwat(struct pool *, int); +__private_extern__ void pool_sethardlimit(struct pool *, int, + const char *, int); +__private_extern__ void *pool_get(struct pool *, int); +__private_extern__ void pool_put(struct pool *, void *); +__private_extern__ u_int64_t pf_time_second(void); +__private_extern__ u_int64_t pf_calendar_time_second(void); +#endif /* KERNEL */ + +union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; + +#define PF_TCPS_PROXY_SRC ((TCP_NSTATES)+0) +#define PF_TCPS_PROXY_DST ((TCP_NSTATES)+1) + +#define PF_MD5_DIGEST_LENGTH 16 +#ifdef MD5_DIGEST_LENGTH +#if PF_MD5_DIGEST_LENGTH != MD5_DIGEST_LENGTH +#error +#endif /* PF_MD5_DIGEST_LENGTH != MD5_DIGEST_LENGTH */ +#endif /* MD5_DIGEST_LENGTH */ + +#ifdef KERNEL +struct ip; +struct ip6_hdr; +struct tcphdr; +struct pf_grev1_hdr; +struct pf_esp_hdr; +#endif /* KERNEL */ + +#define PF_GRE_PPTP_VARIANT 0x01 + +enum { PF_INOUT, PF_IN, PF_OUT }; +enum { PF_PASS, PF_DROP, PF_SCRUB, PF_NOSCRUB, PF_NAT, PF_NONAT, + PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP, + PF_DUMMYNET, PF_NODUMMYNET, PF_NAT64, PF_NONAT64 }; +enum { PF_RULESET_SCRUB, PF_RULESET_FILTER, PF_RULESET_NAT, + PF_RULESET_BINAT, PF_RULESET_RDR, PF_RULESET_DUMMYNET, + PF_RULESET_MAX }; +enum { PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT, + PF_OP_LE, PF_OP_GT, PF_OP_GE, PF_OP_XRG, PF_OP_RRG }; +enum { PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC, PF_DEBUG_NOISY }; +enum { PF_CHANGE_NONE, PF_CHANGE_ADD_HEAD, PF_CHANGE_ADD_TAIL, + PF_CHANGE_ADD_BEFORE, PF_CHANGE_ADD_AFTER, + PF_CHANGE_REMOVE, PF_CHANGE_GET_TICKET }; +enum { PF_GET_NONE, PF_GET_CLR_CNTR }; + +/* + * Note about PFTM_*: real indices into pf_rule.timeout[] come before + * PFTM_MAX, special cases afterwards. See pf_state_expires(). + */ +enum { PFTM_TCP_FIRST_PACKET, PFTM_TCP_OPENING, PFTM_TCP_ESTABLISHED, + PFTM_TCP_CLOSING, PFTM_TCP_FIN_WAIT, PFTM_TCP_CLOSED, + PFTM_UDP_FIRST_PACKET, PFTM_UDP_SINGLE, PFTM_UDP_MULTIPLE, + PFTM_ICMP_FIRST_PACKET, PFTM_ICMP_ERROR_REPLY, + PFTM_GREv1_FIRST_PACKET, PFTM_GREv1_INITIATING, + PFTM_GREv1_ESTABLISHED, PFTM_ESP_FIRST_PACKET, PFTM_ESP_INITIATING, + PFTM_ESP_ESTABLISHED, PFTM_OTHER_FIRST_PACKET, PFTM_OTHER_SINGLE, + PFTM_OTHER_MULTIPLE, PFTM_FRAG, PFTM_INTERVAL, + PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_SRC_NODE, + PFTM_TS_DIFF, PFTM_MAX, PFTM_PURGE, PFTM_UNLINKED }; + +/* PFTM default values */ +#define PFTM_TCP_FIRST_PACKET_VAL 120 /* First TCP packet */ +#define PFTM_TCP_OPENING_VAL 30 /* No response yet */ +#define PFTM_TCP_ESTABLISHED_VAL (24 * 60 * 60) /* Established */ +#define PFTM_TCP_CLOSING_VAL (15 * 60) /* Half closed */ +#define PFTM_TCP_FIN_WAIT_VAL 45 /* Got both FINs */ +#define PFTM_TCP_CLOSED_VAL 90 /* Got a RST */ +#define PFTM_UDP_FIRST_PACKET_VAL 60 /* First UDP packet */ +#define PFTM_UDP_SINGLE_VAL 30 /* Unidirectional */ +#define PFTM_UDP_MULTIPLE_VAL 60 /* Bidirectional */ +#define PFTM_ICMP_FIRST_PACKET_VAL 20 /* First ICMP packet */ +#define PFTM_ICMP_ERROR_REPLY_VAL 10 /* Got error response */ +#define PFTM_GREv1_FIRST_PACKET_VAL 120 +#define PFTM_GREv1_INITIATING_VAL 30 +#define PFTM_GREv1_ESTABLISHED_VAL 1800 +#define PFTM_ESP_FIRST_PACKET_VAL 120 +#define PFTM_ESP_INITIATING_VAL 30 +#define PFTM_ESP_ESTABLISHED_VAL 900 +#define PFTM_OTHER_FIRST_PACKET_VAL 60 /* First packet */ +#define PFTM_OTHER_SINGLE_VAL 30 /* Unidirectional */ +#define PFTM_OTHER_MULTIPLE_VAL 60 /* Bidirectional */ +#define PFTM_FRAG_VAL 30 /* Fragment expire */ +#define PFTM_INTERVAL_VAL 10 /* Expire interval */ +#define PFTM_SRC_NODE_VAL 0 /* Source tracking */ +#define PFTM_TS_DIFF_VAL 30 /* Allowed TS diff */ + +enum { PF_NOPFROUTE, PF_FASTROUTE, PF_ROUTETO, PF_DUPTO, PF_REPLYTO }; +enum { PF_LIMIT_STATES, + PF_LIMIT_APP_STATES, + PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, + PF_LIMIT_TABLES, PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; +#define PF_POOL_IDMASK 0x0f +enum { PF_POOL_NONE, PF_POOL_BITMASK, PF_POOL_RANDOM, + PF_POOL_SRCHASH, PF_POOL_ROUNDROBIN }; +enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL, + PF_ADDR_TABLE, PF_ADDR_RTLABEL, PF_ADDR_URPFFAILED, + PF_ADDR_RANGE }; +#define PF_POOL_TYPEMASK 0x0f +#define PF_POOL_STICKYADDR 0x20 +#define PF_WSCALE_FLAG 0x80 +#define PF_WSCALE_MASK 0x0f + +#define PF_LOG 0x01 +#define PF_LOG_ALL 0x02 +#define PF_LOG_SOCKET_LOOKUP 0x04 + +struct pf_addr { + union { + struct in_addr v4; + struct in6_addr v6; + u_int8_t addr8[16]; + u_int16_t addr16[8]; + u_int32_t addr32[4]; + } pfa; /* 128-bit address */ +#define v4 pfa.v4 +#define v6 pfa.v6 +#define addr8 pfa.addr8 +#define addr16 pfa.addr16 +#define addr32 pfa.addr32 +}; + +#define PF_TABLE_NAME_SIZE 32 + +#define PFI_AFLAG_NETWORK 0x01 +#define PFI_AFLAG_BROADCAST 0x02 +#define PFI_AFLAG_PEER 0x04 +#define PFI_AFLAG_MODEMASK 0x07 +#define PFI_AFLAG_NOALIAS 0x08 + +#ifndef RTLABEL_LEN +#define RTLABEL_LEN 32 +#endif + +struct pf_addr_wrap { + union { + struct { + struct pf_addr addr; + struct pf_addr mask; + } a; + char ifname[IFNAMSIZ]; + char tblname[PF_TABLE_NAME_SIZE]; + char rtlabelname[RTLABEL_LEN]; + u_int32_t rtlabel; + } v; + union { +#ifdef KERNEL + struct pfi_dynaddr *dyn __attribute__((aligned(8))); + struct pfr_ktable *tbl __attribute__((aligned(8))); +#else /* !KERNEL */ + void *dyn __attribute__((aligned(8))); + void *tbl __attribute__((aligned(8))); +#endif /* !KERNEL */ + int dyncnt __attribute__((aligned(8))); + int tblcnt __attribute__((aligned(8))); + } p __attribute__((aligned(8))); + u_int8_t type; /* PF_ADDR_* */ + u_int8_t iflags; /* PFI_AFLAG_* */ +}; + +struct pf_port_range { + u_int16_t port[2]; + u_int8_t op; +}; + +union pf_rule_xport { + struct pf_port_range range; + u_int16_t call_id; + u_int32_t spi; +}; + +#ifdef KERNEL +struct pfi_dynaddr { + TAILQ_ENTRY(pfi_dynaddr) entry; + struct pf_addr pfid_addr4; + struct pf_addr pfid_mask4; + struct pf_addr pfid_addr6; + struct pf_addr pfid_mask6; + struct pfr_ktable *pfid_kt; + struct pfi_kif *pfid_kif; + void *pfid_hook_cookie; + int pfid_net; /* mask or 128 */ + int pfid_acnt4; /* address count IPv4 */ + int pfid_acnt6; /* address count IPv6 */ + sa_family_t pfid_af; /* rule af */ + u_int8_t pfid_iflags; /* PFI_AFLAG_* */ +}; + +/* + * Address manipulation macros + */ + +#if INET +#if !INET6 +#define PF_INET_ONLY +#endif /* ! INET6 */ +#endif /* INET */ + +#if INET6 +#if !INET +#define PF_INET6_ONLY +#endif /* ! INET */ +#endif /* INET6 */ + +#if INET +#if INET6 +#define PF_INET_INET6 +#endif /* INET6 */ +#endif /* INET */ + +#else /* !KERNEL */ + +#define PF_INET_INET6 + +#endif /* !KERNEL */ + +/* Both IPv4 and IPv6 */ +#ifdef PF_INET_INET6 + +#define PF_AEQ(a, b, c) \ + ((c == AF_INET && (a)->addr32[0] == (b)->addr32[0]) || \ + ((a)->addr32[3] == (b)->addr32[3] && \ + (a)->addr32[2] == (b)->addr32[2] && \ + (a)->addr32[1] == (b)->addr32[1] && \ + (a)->addr32[0] == (b)->addr32[0])) \ + +#define PF_ANEQ(a, b, c) \ + ((c == AF_INET && (a)->addr32[0] != (b)->addr32[0]) || \ + ((a)->addr32[3] != (b)->addr32[3] || \ + (a)->addr32[2] != (b)->addr32[2] || \ + (a)->addr32[1] != (b)->addr32[1] || \ + (a)->addr32[0] != (b)->addr32[0])) \ + +#define PF_ALEQ(a, b, c) \ + ((c == AF_INET && (a)->addr32[0] <= (b)->addr32[0]) || \ + ((a)->addr32[3] <= (b)->addr32[3] && \ + (a)->addr32[2] <= (b)->addr32[2] && \ + (a)->addr32[1] <= (b)->addr32[1] && \ + (a)->addr32[0] <= (b)->addr32[0])) \ + +#define PF_AZERO(a, c) \ + ((c == AF_INET && !(a)->addr32[0]) || \ + (!(a)->addr32[0] && !(a)->addr32[1] && \ + !(a)->addr32[2] && !(a)->addr32[3])) \ + +#define PF_MATCHA(n, a, m, b, f) \ + pf_match_addr(n, a, m, b, f) + +#define PF_ACPY(a, b, f) \ + pf_addrcpy(a, b, f) + +#define PF_AINC(a, f) \ + pf_addr_inc(a, f) + +#define PF_POOLMASK(a, b, c, d, f) \ + pf_poolmask(a, b, c, d, f) + +#else + +/* Just IPv6 */ + +#ifdef PF_INET6_ONLY + +#define PF_AEQ(a, b, c) \ + ((a)->addr32[3] == (b)->addr32[3] && \ + (a)->addr32[2] == (b)->addr32[2] && \ + (a)->addr32[1] == (b)->addr32[1] && \ + (a)->addr32[0] == (b)->addr32[0]) \ + +#define PF_ANEQ(a, b, c) \ + ((a)->addr32[3] != (b)->addr32[3] || \ + (a)->addr32[2] != (b)->addr32[2] || \ + (a)->addr32[1] != (b)->addr32[1] || \ + (a)->addr32[0] != (b)->addr32[0]) \ + +#define PF_ALEQ(a, b, c) \ + ((a)->addr32[3] <= (b)->addr32[3] && \ + (a)->addr32[2] <= (b)->addr32[2] && \ + (a)->addr32[1] <= (b)->addr32[1] && \ + (a)->addr32[0] <= (b)->addr32[0]) \ + +#define PF_AZERO(a, c) \ + (!(a)->addr32[0] && \ + !(a)->addr32[1] && \ + !(a)->addr32[2] && \ + !(a)->addr32[3]) \ + +#define PF_MATCHA(n, a, m, b, f) \ + pf_match_addr(n, a, m, b, f) + +#define PF_ACPY(a, b, f) \ + pf_addrcpy(a, b, f) + +#define PF_AINC(a, f) \ + pf_addr_inc(a, f) + +#define PF_POOLMASK(a, b, c, d, f) \ + pf_poolmask(a, b, c, d, f) + +#else + +/* Just IPv4 */ +#ifdef PF_INET_ONLY + +#define PF_AEQ(a, b, c) \ + ((a)->addr32[0] == (b)->addr32[0]) + +#define PF_ANEQ(a, b, c) \ + ((a)->addr32[0] != (b)->addr32[0]) + +#define PF_ALEQ(a, b, c) \ + ((a)->addr32[0] <= (b)->addr32[0]) + +#define PF_AZERO(a, c) \ + (!(a)->addr32[0]) + +#define PF_MATCHA(n, a, m, b, f) \ + pf_match_addr(n, a, m, b, f) + +#define PF_ACPY(a, b, f) \ + (a)->v4.s_addr = (b)->v4.s_addr + +#define PF_AINC(a, f) \ + do { \ + (a)->addr32[0] = htonl(ntohl((a)->addr32[0]) + 1); \ + } while (0) + +#define PF_POOLMASK(a, b, c, d, f) \ + do { \ + (a)->addr32[0] = ((b)->addr32[0] & (c)->addr32[0]) | \ + (((c)->addr32[0] ^ 0xffffffff) & (d)->addr32[0]); \ + } while (0) + +#endif /* PF_INET_ONLY */ +#endif /* PF_INET6_ONLY */ +#endif /* PF_INET_INET6 */ + +#ifdef KERNEL +#define PF_MISMATCHAW(aw, x, af, neg, ifp) \ + ( \ + (((aw)->type == PF_ADDR_NOROUTE && \ + pf_routable((x), (af), NULL)) || \ + (((aw)->type == PF_ADDR_URPFFAILED && (ifp) != NULL && \ + pf_routable((x), (af), (ifp))) || \ + ((aw)->type == PF_ADDR_RTLABEL && \ + !pf_rtlabel_match((x), (af), (aw))) || \ + ((aw)->type == PF_ADDR_TABLE && \ + !pfr_match_addr((aw)->p.tbl, (x), (af))) || \ + ((aw)->type == PF_ADDR_DYNIFTL && \ + !pfi_match_addr((aw)->p.dyn, (x), (af))) || \ + ((aw)->type == PF_ADDR_RANGE && \ + !pf_match_addr_range(&(aw)->v.a.addr, \ + &(aw)->v.a.mask, (x), (af))) || \ + ((aw)->type == PF_ADDR_ADDRMASK && \ + !PF_AZERO(&(aw)->v.a.mask, (af)) && \ + !PF_MATCHA(0, &(aw)->v.a.addr, \ + &(aw)->v.a.mask, (x), (af))))) != \ + (neg) \ + ) +#endif /* KERNEL */ + +struct pf_rule_uid { + uid_t uid[2]; + u_int8_t op; + u_int8_t _pad[3]; +}; + +struct pf_rule_gid { + uid_t gid[2]; + u_int8_t op; + u_int8_t _pad[3]; +}; + +struct pf_rule_addr { + struct pf_addr_wrap addr; + union pf_rule_xport xport; + u_int8_t neg; +}; + +struct pf_pooladdr { + struct pf_addr_wrap addr; + TAILQ_ENTRY(pf_pooladdr) entries; +#if !defined(__LP64__) + u_int32_t _pad[2]; +#endif /* !__LP64__ */ + char ifname[IFNAMSIZ]; +#ifdef KERNEL + struct pfi_kif *kif __attribute__((aligned(8))); +#else /* !KERNEL */ + void *kif __attribute__((aligned(8))); +#endif /* !KERNEL */ +}; + +TAILQ_HEAD(pf_palist, pf_pooladdr); + +struct pf_poolhashkey { + union { + u_int8_t key8[16]; + u_int16_t key16[8]; + u_int32_t key32[4]; + } pfk; /* 128-bit hash key */ +#define key8 pfk.key8 +#define key16 pfk.key16 +#define key32 pfk.key32 +}; + +struct pf_pool { + struct pf_palist list; +#if !defined(__LP64__) + u_int32_t _pad[2]; +#endif /* !__LP64__ */ +#ifdef KERNEL + struct pf_pooladdr *cur __attribute__((aligned(8))); +#else /* !KERNEL */ + void *cur __attribute__((aligned(8))); +#endif /* !KERNEL */ + struct pf_poolhashkey key __attribute__((aligned(8))); + struct pf_addr counter; + int tblidx; + u_int16_t proxy_port[2]; + u_int8_t port_op; + u_int8_t opts; + sa_family_t af; +}; + + +/* A packed Operating System description for fingerprinting */ +typedef u_int32_t pf_osfp_t; +#define PF_OSFP_ANY ((pf_osfp_t)0) +#define PF_OSFP_UNKNOWN ((pf_osfp_t)-1) +#define PF_OSFP_NOMATCH ((pf_osfp_t)-2) + +struct pf_osfp_entry { + SLIST_ENTRY(pf_osfp_entry) fp_entry; +#if !defined(__LP64__) + u_int32_t _pad; +#endif /* !__LP64__ */ + pf_osfp_t fp_os; + int fp_enflags; +#define PF_OSFP_EXPANDED 0x001 /* expanded entry */ +#define PF_OSFP_GENERIC 0x002 /* generic signature */ +#define PF_OSFP_NODETAIL 0x004 /* no p0f details */ +#define PF_OSFP_LEN 32 + char fp_class_nm[PF_OSFP_LEN]; + char fp_version_nm[PF_OSFP_LEN]; + char fp_subtype_nm[PF_OSFP_LEN]; +}; +#define PF_OSFP_ENTRY_EQ(a, b) \ + ((a)->fp_os == (b)->fp_os && \ + memcmp((a)->fp_class_nm, (b)->fp_class_nm, PF_OSFP_LEN) == 0 && \ + memcmp((a)->fp_version_nm, (b)->fp_version_nm, PF_OSFP_LEN) == 0 && \ + memcmp((a)->fp_subtype_nm, (b)->fp_subtype_nm, PF_OSFP_LEN) == 0) + +/* handle pf_osfp_t packing */ +#define _FP_RESERVED_BIT 1 /* For the special negative #defines */ +#define _FP_UNUSED_BITS 1 +#define _FP_CLASS_BITS 10 /* OS Class (Windows, Linux) */ +#define _FP_VERSION_BITS 10 /* OS version (95, 98, NT, 2.4.54, 3.2) */ +#define _FP_SUBTYPE_BITS 10 /* patch level (NT SP4, SP3, ECN patch) */ +#define PF_OSFP_UNPACK(osfp, class, version, subtype) do { \ + (class) = ((osfp) >> (_FP_VERSION_BITS+_FP_SUBTYPE_BITS)) & \ + ((1 << _FP_CLASS_BITS) - 1); \ + (version) = ((osfp) >> _FP_SUBTYPE_BITS) & \ + ((1 << _FP_VERSION_BITS) - 1);\ + (subtype) = (osfp) & ((1 << _FP_SUBTYPE_BITS) - 1); \ +} while (0) +#define PF_OSFP_PACK(osfp, class, version, subtype) do { \ + (osfp) = ((class) & ((1 << _FP_CLASS_BITS) - 1)) << (_FP_VERSION_BITS \ + + _FP_SUBTYPE_BITS); \ + (osfp) |= ((version) & ((1 << _FP_VERSION_BITS) - 1)) << \ + _FP_SUBTYPE_BITS; \ + (osfp) |= (subtype) & ((1 << _FP_SUBTYPE_BITS) - 1); \ +} while (0) + +/* the fingerprint of an OSes TCP SYN packet */ +typedef u_int64_t pf_tcpopts_t; +struct pf_os_fingerprint { + SLIST_HEAD(pf_osfp_enlist, pf_osfp_entry) fp_oses; /* list of matches */ + pf_tcpopts_t fp_tcpopts; /* packed TCP options */ + u_int16_t fp_wsize; /* TCP window size */ + u_int16_t fp_psize; /* ip->ip_len */ + u_int16_t fp_mss; /* TCP MSS */ + u_int16_t fp_flags; +#define PF_OSFP_WSIZE_MOD 0x0001 /* Window modulus */ +#define PF_OSFP_WSIZE_DC 0x0002 /* Window don't care */ +#define PF_OSFP_WSIZE_MSS 0x0004 /* Window multiple of MSS */ +#define PF_OSFP_WSIZE_MTU 0x0008 /* Window multiple of MTU */ +#define PF_OSFP_PSIZE_MOD 0x0010 /* packet size modulus */ +#define PF_OSFP_PSIZE_DC 0x0020 /* packet size don't care */ +#define PF_OSFP_WSCALE 0x0040 /* TCP window scaling */ +#define PF_OSFP_WSCALE_MOD 0x0080 /* TCP window scale modulus */ +#define PF_OSFP_WSCALE_DC 0x0100 /* TCP window scale dont-care */ +#define PF_OSFP_MSS 0x0200 /* TCP MSS */ +#define PF_OSFP_MSS_MOD 0x0400 /* TCP MSS modulus */ +#define PF_OSFP_MSS_DC 0x0800 /* TCP MSS dont-care */ +#define PF_OSFP_DF 0x1000 /* IPv4 don't fragment bit */ +#define PF_OSFP_TS0 0x2000 /* Zero timestamp */ +#define PF_OSFP_INET6 0x4000 /* IPv6 */ + u_int8_t fp_optcnt; /* TCP option count */ + u_int8_t fp_wscale; /* TCP window scaling */ + u_int8_t fp_ttl; /* IPv4 TTL */ +#define PF_OSFP_MAXTTL_OFFSET 40 +/* TCP options packing */ +#define PF_OSFP_TCPOPT_NOP 0x0 /* TCP NOP option */ +#define PF_OSFP_TCPOPT_WSCALE 0x1 /* TCP window scaling option */ +#define PF_OSFP_TCPOPT_MSS 0x2 /* TCP max segment size opt */ +#define PF_OSFP_TCPOPT_SACK 0x3 /* TCP SACK OK option */ +#define PF_OSFP_TCPOPT_TS 0x4 /* TCP timestamp option */ +#define PF_OSFP_TCPOPT_BITS 3 /* bits used by each option */ +#define PF_OSFP_MAX_OPTS \ + (sizeof(((struct pf_os_fingerprint *)0)->fp_tcpopts) * 8) \ + / PF_OSFP_TCPOPT_BITS + + SLIST_ENTRY(pf_os_fingerprint) fp_next; +}; + +struct pf_osfp_ioctl { + struct pf_osfp_entry fp_os; + pf_tcpopts_t fp_tcpopts; /* packed TCP options */ + u_int16_t fp_wsize; /* TCP window size */ + u_int16_t fp_psize; /* ip->ip_len */ + u_int16_t fp_mss; /* TCP MSS */ + u_int16_t fp_flags; + u_int8_t fp_optcnt; /* TCP option count */ + u_int8_t fp_wscale; /* TCP window scaling */ + u_int8_t fp_ttl; /* IPv4 TTL */ + + int fp_getnum; /* DIOCOSFPGET number */ +}; + + +union pf_rule_ptr { + struct pf_rule *ptr __attribute__((aligned(8))); + u_int32_t nr __attribute__((aligned(8))); +} __attribute__((aligned(8))); + +#define PF_ANCHOR_NAME_SIZE 64 + +struct pf_rule { + struct pf_rule_addr src; + struct pf_rule_addr dst; +#define PF_SKIP_IFP 0 +#define PF_SKIP_DIR 1 +#define PF_SKIP_AF 2 +#define PF_SKIP_PROTO 3 +#define PF_SKIP_SRC_ADDR 4 +#define PF_SKIP_SRC_PORT 5 +#define PF_SKIP_DST_ADDR 6 +#define PF_SKIP_DST_PORT 7 +#define PF_SKIP_COUNT 8 + union pf_rule_ptr skip[PF_SKIP_COUNT]; +#define PF_RULE_LABEL_SIZE 64 + char label[PF_RULE_LABEL_SIZE]; +#define PF_QNAME_SIZE 64 + char ifname[IFNAMSIZ]; + char qname[PF_QNAME_SIZE]; + char pqname[PF_QNAME_SIZE]; +#define PF_TAG_NAME_SIZE 64 + char tagname[PF_TAG_NAME_SIZE]; + char match_tagname[PF_TAG_NAME_SIZE]; + + char overload_tblname[PF_TABLE_NAME_SIZE]; + + TAILQ_ENTRY(pf_rule) entries; +#if !defined(__LP64__) + u_int32_t _pad[2]; +#endif /* !__LP64__ */ + struct pf_pool rpool; + + u_int64_t evaluations; + u_int64_t packets[2]; + u_int64_t bytes[2]; + + u_int64_t ticket; +#define PF_OWNER_NAME_SIZE 64 + char owner[PF_OWNER_NAME_SIZE]; + u_int32_t priority; + +#ifdef KERNEL + struct pfi_kif *kif __attribute__((aligned(8))); +#else /* !KERNEL */ + void *kif __attribute__((aligned(8))); +#endif /* !KERNEL */ + struct pf_anchor *anchor __attribute__((aligned(8))); +#ifdef KERNEL + struct pfr_ktable *overload_tbl __attribute__((aligned(8))); +#else /* !KERNEL */ + void *overload_tbl __attribute__((aligned(8))); +#endif /* !KERNEL */ + + pf_osfp_t os_fingerprint __attribute__((aligned(8))); + + unsigned int rtableid; + u_int32_t timeout[PFTM_MAX]; + u_int32_t states; + u_int32_t max_states; + u_int32_t src_nodes; + u_int32_t max_src_nodes; + u_int32_t max_src_states; + u_int32_t max_src_conn; + struct { + u_int32_t limit; + u_int32_t seconds; + } max_src_conn_rate; + u_int32_t qid; + u_int32_t pqid; + u_int32_t rt_listid; + u_int32_t nr; + u_int32_t prob; + uid_t cuid; + pid_t cpid; + + u_int16_t return_icmp; + u_int16_t return_icmp6; + u_int16_t max_mss; + u_int16_t tag; + u_int16_t match_tag; + + struct pf_rule_uid uid; + struct pf_rule_gid gid; + + u_int32_t rule_flag; + u_int8_t action; + u_int8_t direction; + u_int8_t log; + u_int8_t logif; + u_int8_t quick; + u_int8_t ifnot; + u_int8_t match_tag_not; + u_int8_t natpass; + +#define PF_STATE_NORMAL 0x1 +#define PF_STATE_MODULATE 0x2 +#define PF_STATE_SYNPROXY 0x3 + u_int8_t keep_state; + sa_family_t af; + u_int8_t proto; + u_int8_t type; + u_int8_t code; + u_int8_t flags; + u_int8_t flagset; + u_int8_t min_ttl; + u_int8_t allow_opts; + u_int8_t rt; + u_int8_t return_ttl; + +/* service class categories */ +#define SCIDX_MASK 0x0f +#define SC_BE 0x10 +#define SC_BK_SYS 0x11 +#define SC_BK 0x12 +#define SC_RD 0x13 +#define SC_OAM 0x14 +#define SC_AV 0x15 +#define SC_RV 0x16 +#define SC_VI 0x17 +#define SC_VO 0x18 +#define SC_CTL 0x19 + +/* diffserve code points */ +#define DSCP_MASK 0xfc +#define DSCP_CUMASK 0x03 +#define DSCP_EF 0xb8 +#define DSCP_AF11 0x28 +#define DSCP_AF12 0x30 +#define DSCP_AF13 0x38 +#define DSCP_AF21 0x48 +#define DSCP_AF22 0x50 +#define DSCP_AF23 0x58 +#define DSCP_AF31 0x68 +#define DSCP_AF32 0x70 +#define DSCP_AF33 0x78 +#define DSCP_AF41 0x88 +#define DSCP_AF42 0x90 +#define DSCP_AF43 0x98 +#define AF_CLASSMASK 0xe0 +#define AF_DROPPRECMASK 0x18 + u_int8_t tos; + u_int8_t anchor_relative; + u_int8_t anchor_wildcard; + +#define PF_FLUSH 0x01 +#define PF_FLUSH_GLOBAL 0x02 + u_int8_t flush; + + u_int8_t proto_variant; + u_int8_t extfilter; /* Filter mode [PF_EXTFILTER_xxx] */ + u_int8_t extmap; /* Mapping mode [PF_EXTMAP_xxx] */ + u_int32_t dnpipe; + u_int32_t dntype; +}; + +/* pf device identifiers */ +#define PFDEV_PF 0 +#define PFDEV_PFM 1 +#define PFDEV_MAX 2 + +/* rule flags */ +#define PFRULE_DROP 0x0000 +#define PFRULE_RETURNRST 0x0001 +#define PFRULE_FRAGMENT 0x0002 +#define PFRULE_RETURNICMP 0x0004 +#define PFRULE_RETURN 0x0008 +#define PFRULE_NOSYNC 0x0010 +#define PFRULE_SRCTRACK 0x0020 /* track source states */ +#define PFRULE_RULESRCTRACK 0x0040 /* per rule */ + +/* scrub flags */ +#define PFRULE_NODF 0x0100 +#define PFRULE_FRAGCROP 0x0200 /* non-buffering frag cache */ +#define PFRULE_FRAGDROP 0x0400 /* drop funny fragments */ +#define PFRULE_RANDOMID 0x0800 +#define PFRULE_REASSEMBLE_TCP 0x1000 + +/* rule flags for TOS/DSCP/service class differentiation */ +#define PFRULE_TOS 0x2000 +#define PFRULE_DSCP 0x4000 +#define PFRULE_SC 0x8000 + +/* rule flags again */ +#define PFRULE_IFBOUND 0x00010000 /* if-bound */ +#define PFRULE_PFM 0x00020000 /* created by pfm device */ + +#define PFSTATE_HIWAT 10000 /* default state table size */ +#define PFSTATE_ADAPT_START 6000 /* default adaptive timeout start */ +#define PFSTATE_ADAPT_END 12000 /* default adaptive timeout end */ + +#define PFAPPSTATE_HIWAT 10000 /* default same as state table */ + +enum pf_extmap { + PF_EXTMAP_APD = 1, /* Address-port-dependent mapping */ + PF_EXTMAP_AD, /* Address-dependent mapping */ + PF_EXTMAP_EI /* Endpoint-independent mapping */ +}; + +enum pf_extfilter { + PF_EXTFILTER_APD = 1, /* Address-port-dependent filtering */ + PF_EXTFILTER_AD, /* Address-dependent filtering */ + PF_EXTFILTER_EI /* Endpoint-independent filtering */ +}; + +struct pf_threshold { + u_int32_t limit; +#define PF_THRESHOLD_MULT 1000 +#define PF_THRESHOLD_MAX 0xffffffff / PF_THRESHOLD_MULT + u_int32_t seconds; + u_int32_t count; + u_int32_t last; +}; + +struct pf_src_node { + RB_ENTRY(pf_src_node) entry; + struct pf_addr addr; + struct pf_addr raddr; + union pf_rule_ptr rule; +#ifdef KERNEL + struct pfi_kif *kif; +#else /* !KERNEL */ + void *kif; +#endif /* !KERNEL */ + u_int64_t bytes[2]; + u_int64_t packets[2]; + u_int32_t states; + u_int32_t conn; + struct pf_threshold conn_rate; + u_int64_t creation; + u_int64_t expire; + sa_family_t af; + u_int8_t ruletype; +}; + +#define PFSNODE_HIWAT 10000 /* default source node table size */ + +#ifdef KERNEL +struct pf_state_scrub { + struct timeval pfss_last; /* time received last packet */ + u_int32_t pfss_tsecr; /* last echoed timestamp */ + u_int32_t pfss_tsval; /* largest timestamp */ + u_int32_t pfss_tsval0; /* original timestamp */ + u_int16_t pfss_flags; +#define PFSS_TIMESTAMP 0x0001 /* modulate timestamp */ +#define PFSS_PAWS 0x0010 /* stricter PAWS checks */ +#define PFSS_PAWS_IDLED 0x0020 /* was idle too long. no PAWS */ +#define PFSS_DATA_TS 0x0040 /* timestamp on data packets */ +#define PFSS_DATA_NOTS 0x0080 /* no timestamp on data packets */ + u_int8_t pfss_ttl; /* stashed TTL */ + u_int8_t pad; + u_int32_t pfss_ts_mod; /* timestamp modulation */ +}; +#endif /* KERNEL */ + +union pf_state_xport { + u_int16_t port; + u_int16_t call_id; + u_int32_t spi; +}; + +struct pf_state_host { + struct pf_addr addr; + union pf_state_xport xport; +}; + +#ifdef KERNEL +struct pf_state_peer { + u_int32_t seqlo; /* Max sequence number sent */ + u_int32_t seqhi; /* Max the other end ACKd + win */ + u_int32_t seqdiff; /* Sequence number modulator */ + u_int16_t max_win; /* largest window (pre scaling) */ + u_int8_t state; /* active state level */ + u_int8_t wscale; /* window scaling factor */ + u_int16_t mss; /* Maximum segment size option */ + u_int8_t tcp_est; /* Did we reach TCPS_ESTABLISHED */ + struct pf_state_scrub *scrub; /* state is scrubbed */ + u_int8_t pad[3]; +}; + +TAILQ_HEAD(pf_state_queue, pf_state); + +struct pf_state; +struct pf_pdesc; +struct pf_app_state; + +typedef void (*pf_app_handler)(struct pf_state *, int, int, struct pf_pdesc *, + struct pfi_kif *); + +typedef int (*pf_app_compare)(struct pf_app_state *, struct pf_app_state *); + +struct pf_pptp_state { + struct pf_state *grev1_state; +}; + +struct pf_grev1_state { + struct pf_state *pptp_state; +}; + +struct pf_ike_state { + u_int64_t cookie; +}; + +struct pf_app_state { + pf_app_handler handler; + pf_app_compare compare_lan_ext; + pf_app_compare compare_ext_gwy; + union { + struct pf_pptp_state pptp; + struct pf_grev1_state grev1; + struct pf_ike_state ike; + } u; +}; + +/* keep synced with struct pf_state, used in RB_FIND */ +struct pf_state_key_cmp { + struct pf_state_host lan; + struct pf_state_host gwy; + struct pf_state_host ext_lan; + struct pf_state_host ext_gwy; + sa_family_t af_lan; + sa_family_t af_gwy; + u_int8_t proto; + u_int8_t direction; + u_int8_t proto_variant; + struct pf_app_state *app_state; +}; + +TAILQ_HEAD(pf_statelist, pf_state); + +struct pf_state_key { + struct pf_state_host lan; + struct pf_state_host gwy; + struct pf_state_host ext_lan; + struct pf_state_host ext_gwy; + sa_family_t af_lan; + sa_family_t af_gwy; + u_int8_t proto; + u_int8_t direction; + u_int8_t proto_variant; + struct pf_app_state *app_state; + u_int32_t flowsrc; + u_int32_t flowhash; + + RB_ENTRY(pf_state_key) entry_lan_ext; + RB_ENTRY(pf_state_key) entry_ext_gwy; + struct pf_statelist states; + u_int32_t refcnt; +}; + + +/* keep synced with struct pf_state, used in RB_FIND */ +struct pf_state_cmp { + u_int64_t id; + u_int32_t creatorid; + u_int32_t pad; +}; + +/* flowhash key (12-bytes multiple for performance) */ +struct pf_flowhash_key { + struct pf_state_host ap1; /* address+port blob 1 */ + struct pf_state_host ap2; /* address+port blob 2 */ + u_int32_t af; + u_int32_t proto; +}; +#endif /* KERNEL */ + +struct hook_desc; +TAILQ_HEAD(hook_desc_head, hook_desc); + +#ifdef KERNEL +struct pf_state { + u_int64_t id; + u_int32_t creatorid; + u_int32_t pad; + + TAILQ_ENTRY(pf_state) entry_list; + TAILQ_ENTRY(pf_state) next; + RB_ENTRY(pf_state) entry_id; + struct pf_state_peer src; + struct pf_state_peer dst; + union pf_rule_ptr rule; + union pf_rule_ptr anchor; + union pf_rule_ptr nat_rule; + struct pf_addr rt_addr; + struct hook_desc_head unlink_hooks; + struct pf_state_key *state_key; + struct pfi_kif *kif; + struct pfi_kif *rt_kif; + struct pf_src_node *src_node; + struct pf_src_node *nat_src_node; + u_int64_t packets[2]; + u_int64_t bytes[2]; + u_int64_t creation; + u_int64_t expire; + u_int64_t pfsync_time; + u_int16_t tag; + u_int8_t log; + u_int8_t allow_opts; + u_int8_t timeout; + u_int8_t sync_flags; +}; +#endif /* KERNEL */ + +#define PFSTATE_NOSYNC 0x01 +#define PFSTATE_FROMSYNC 0x02 +#define PFSTATE_STALE 0x04 + +#define __packed __attribute__((__packed__)) + +/* + * Unified state structures for pulling states out of the kernel + * used by pfsync(4) and the pf(4) ioctl. + */ +struct pfsync_state_scrub { + u_int16_t pfss_flags; + u_int8_t pfss_ttl; /* stashed TTL */ +#define PFSYNC_SCRUB_FLAG_VALID 0x01 + u_int8_t scrub_flag; + u_int32_t pfss_ts_mod; /* timestamp modulation */ +} __packed; + +struct pfsync_state_host { + struct pf_addr addr; + union pf_state_xport xport; + u_int16_t pad[2]; +} __packed; + +struct pfsync_state_peer { + struct pfsync_state_scrub scrub; /* state is scrubbed */ + u_int32_t seqlo; /* Max sequence number sent */ + u_int32_t seqhi; /* Max the other end ACKd + win */ + u_int32_t seqdiff; /* Sequence number modulator */ + u_int16_t max_win; /* largest window (pre scaling) */ + u_int16_t mss; /* Maximum segment size option */ + u_int8_t state; /* active state level */ + u_int8_t wscale; /* window scaling factor */ + u_int8_t pad[6]; +} __packed; + +struct pfsync_state { + u_int32_t id[2]; + char ifname[IFNAMSIZ]; + struct pfsync_state_host lan; + struct pfsync_state_host gwy; + struct pfsync_state_host ext_lan; + struct pfsync_state_host ext_gwy; + struct pfsync_state_peer src; + struct pfsync_state_peer dst; + struct pf_addr rt_addr; + struct hook_desc_head unlink_hooks; +#if !defined(__LP64__) + u_int32_t _pad[2]; +#endif /* !__LP64__ */ + u_int32_t rule; + u_int32_t anchor; + u_int32_t nat_rule; + u_int64_t creation; + u_int64_t expire; + u_int32_t packets[2][2]; + u_int32_t bytes[2][2]; + u_int32_t creatorid; + u_int16_t tag; + sa_family_t af_lan; + sa_family_t af_gwy; + u_int8_t proto; + u_int8_t direction; + u_int8_t log; + u_int8_t allow_opts; + u_int8_t timeout; + u_int8_t sync_flags; + u_int8_t updates; + u_int8_t proto_variant; + u_int8_t __pad; + u_int32_t flowhash; +} __packed; + +#define PFSYNC_FLAG_COMPRESS 0x01 +#define PFSYNC_FLAG_STALE 0x02 +#define PFSYNC_FLAG_SRCNODE 0x04 +#define PFSYNC_FLAG_NATSRCNODE 0x08 + +#ifdef KERNEL +/* for copies to/from userland via pf_ioctl() */ +#define pf_state_peer_to_pfsync(s, d) do { \ + (d)->seqlo = (s)->seqlo; \ + (d)->seqhi = (s)->seqhi; \ + (d)->seqdiff = (s)->seqdiff; \ + (d)->max_win = (s)->max_win; \ + (d)->mss = (s)->mss; \ + (d)->state = (s)->state; \ + (d)->wscale = (s)->wscale; \ + if ((s)->scrub) { \ + (d)->scrub.pfss_flags = \ + (s)->scrub->pfss_flags & PFSS_TIMESTAMP; \ + (d)->scrub.pfss_ttl = (s)->scrub->pfss_ttl; \ + (d)->scrub.pfss_ts_mod = (s)->scrub->pfss_ts_mod; \ + (d)->scrub.scrub_flag = PFSYNC_SCRUB_FLAG_VALID; \ + } \ +} while (0) + +#define pf_state_peer_from_pfsync(s, d) do { \ + (d)->seqlo = (s)->seqlo; \ + (d)->seqhi = (s)->seqhi; \ + (d)->seqdiff = (s)->seqdiff; \ + (d)->max_win = (s)->max_win; \ + (d)->mss = ntohs((s)->mss); \ + (d)->state = (s)->state; \ + (d)->wscale = (s)->wscale; \ + if ((s)->scrub.scrub_flag == PFSYNC_SCRUB_FLAG_VALID && \ + (d)->scrub != NULL) { \ + (d)->scrub->pfss_flags = \ + ntohs((s)->scrub.pfss_flags) & PFSS_TIMESTAMP; \ + (d)->scrub->pfss_ttl = (s)->scrub.pfss_ttl; \ + (d)->scrub->pfss_ts_mod = (s)->scrub.pfss_ts_mod; \ + } \ +} while (0) +#endif /* KERNEL */ + +#define pf_state_counter_to_pfsync(s, d) do { \ + d[0] = (s>>32)&0xffffffff; \ + d[1] = s&0xffffffff; \ +} while (0) + +#define pf_state_counter_from_pfsync(s) \ + (((u_int64_t)(s[0])<<32) | (u_int64_t)(s[1])) + + + +TAILQ_HEAD(pf_rulequeue, pf_rule); + +struct pf_anchor; + +struct pf_ruleset { + struct { + struct pf_rulequeue queues[2]; + struct { + struct pf_rulequeue *ptr; + struct pf_rule **ptr_array; + u_int32_t rcount; + u_int32_t ticket; + int open; + } active, inactive; + } rules[PF_RULESET_MAX]; + struct pf_anchor *anchor; + u_int32_t tticket; + int tables; + int topen; +}; + +RB_HEAD(pf_anchor_global, pf_anchor); +RB_HEAD(pf_anchor_node, pf_anchor); +struct pf_anchor { + RB_ENTRY(pf_anchor) entry_global; + RB_ENTRY(pf_anchor) entry_node; + struct pf_anchor *parent; + struct pf_anchor_node children; + char name[PF_ANCHOR_NAME_SIZE]; + char path[MAXPATHLEN]; + struct pf_ruleset ruleset; + int refcnt; /* anchor rules */ + int match; + char owner[PF_OWNER_NAME_SIZE]; +}; +#ifdef KERNEL +RB_PROTOTYPE_SC(__private_extern__, pf_anchor_global, pf_anchor, entry_global, + pf_anchor_compare); +RB_PROTOTYPE_SC(__private_extern__, pf_anchor_node, pf_anchor, entry_node, + pf_anchor_compare); +#else /* !KERNEL */ +RB_PROTOTYPE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); +RB_PROTOTYPE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); +#endif /* !KERNEL */ + +#define PF_RESERVED_ANCHOR "_pf" + +#define PFR_TFLAG_PERSIST 0x00000001 +#define PFR_TFLAG_CONST 0x00000002 +#define PFR_TFLAG_ACTIVE 0x00000004 +#define PFR_TFLAG_INACTIVE 0x00000008 +#define PFR_TFLAG_REFERENCED 0x00000010 +#define PFR_TFLAG_REFDANCHOR 0x00000020 +#define PFR_TFLAG_USRMASK 0x00000003 +#define PFR_TFLAG_SETMASK 0x0000003C +#define PFR_TFLAG_ALLMASK 0x0000003F + +struct pfr_table { + char pfrt_anchor[MAXPATHLEN]; + char pfrt_name[PF_TABLE_NAME_SIZE]; + u_int32_t pfrt_flags; + u_int8_t pfrt_fback; +}; + +enum { PFR_FB_NONE, PFR_FB_MATCH, PFR_FB_ADDED, PFR_FB_DELETED, + PFR_FB_CHANGED, PFR_FB_CLEARED, PFR_FB_DUPLICATE, + PFR_FB_NOTMATCH, PFR_FB_CONFLICT, PFR_FB_MAX }; + +struct pfr_addr { + union { + struct in_addr _pfra_ip4addr; + struct in6_addr _pfra_ip6addr; + } pfra_u; + u_int8_t pfra_af; + u_int8_t pfra_net; + u_int8_t pfra_not; + u_int8_t pfra_fback; +}; +#define pfra_ip4addr pfra_u._pfra_ip4addr +#define pfra_ip6addr pfra_u._pfra_ip6addr + +enum { PFR_DIR_IN, PFR_DIR_OUT, PFR_DIR_MAX }; +enum { PFR_OP_BLOCK, PFR_OP_PASS, PFR_OP_ADDR_MAX, PFR_OP_TABLE_MAX }; +#define PFR_OP_XPASS PFR_OP_ADDR_MAX + +struct pfr_astats { + struct pfr_addr pfras_a; +#if !defined(__LP64__) + u_int32_t _pad; +#endif /* !__LP64__ */ + u_int64_t pfras_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + u_int64_t pfras_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + u_int64_t pfras_tzero; +}; + +enum { PFR_REFCNT_RULE, PFR_REFCNT_ANCHOR, PFR_REFCNT_MAX }; + +struct pfr_tstats { + struct pfr_table pfrts_t; + u_int64_t pfrts_packets[PFR_DIR_MAX][PFR_OP_TABLE_MAX]; + u_int64_t pfrts_bytes[PFR_DIR_MAX][PFR_OP_TABLE_MAX]; + u_int64_t pfrts_match; + u_int64_t pfrts_nomatch; + u_int64_t pfrts_tzero; + int pfrts_cnt; + int pfrts_refcnt[PFR_REFCNT_MAX]; +#if !defined(__LP64__) + u_int32_t _pad; +#endif /* !__LP64__ */ +}; +#define pfrts_name pfrts_t.pfrt_name +#define pfrts_flags pfrts_t.pfrt_flags + +#ifdef KERNEL +SLIST_HEAD(pfr_kentryworkq, pfr_kentry); +struct pfr_kentry { + struct radix_node pfrke_node[2]; + union sockaddr_union pfrke_sa; + u_int64_t pfrke_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + u_int64_t pfrke_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + SLIST_ENTRY(pfr_kentry) pfrke_workq; + u_int64_t pfrke_tzero; + u_int8_t pfrke_af; + u_int8_t pfrke_net; + u_int8_t pfrke_not; + u_int8_t pfrke_mark; + u_int8_t pfrke_intrpool; +}; + +SLIST_HEAD(pfr_ktableworkq, pfr_ktable); +RB_HEAD(pfr_ktablehead, pfr_ktable); +struct pfr_ktable { + struct pfr_tstats pfrkt_ts; + RB_ENTRY(pfr_ktable) pfrkt_tree; + SLIST_ENTRY(pfr_ktable) pfrkt_workq; + struct radix_node_head *pfrkt_ip4; + struct radix_node_head *pfrkt_ip6; + struct pfr_ktable *pfrkt_shadow; + struct pfr_ktable *pfrkt_root; + struct pf_ruleset *pfrkt_rs; + u_int64_t pfrkt_larg; + u_int32_t pfrkt_nflags; +}; +#define pfrkt_t pfrkt_ts.pfrts_t +#define pfrkt_name pfrkt_t.pfrt_name +#define pfrkt_anchor pfrkt_t.pfrt_anchor +#define pfrkt_ruleset pfrkt_t.pfrt_ruleset +#define pfrkt_flags pfrkt_t.pfrt_flags +#define pfrkt_cnt pfrkt_ts.pfrts_cnt +#define pfrkt_refcnt pfrkt_ts.pfrts_refcnt +#define pfrkt_packets pfrkt_ts.pfrts_packets +#define pfrkt_bytes pfrkt_ts.pfrts_bytes +#define pfrkt_match pfrkt_ts.pfrts_match +#define pfrkt_nomatch pfrkt_ts.pfrts_nomatch +#define pfrkt_tzero pfrkt_ts.pfrts_tzero + +RB_HEAD(pf_state_tree_lan_ext, pf_state_key); +RB_PROTOTYPE_SC(__private_extern__, pf_state_tree_lan_ext, pf_state_key, + entry_lan_ext, pf_state_compare_lan_ext); + +RB_HEAD(pf_state_tree_ext_gwy, pf_state_key); +RB_PROTOTYPE_SC(__private_extern__, pf_state_tree_ext_gwy, pf_state_key, + entry_ext_gwy, pf_state_compare_ext_gwy); + +RB_HEAD(pfi_ifhead, pfi_kif); + +/* state tables */ +extern struct pf_state_tree_lan_ext pf_statetbl_lan_ext; +extern struct pf_state_tree_ext_gwy pf_statetbl_ext_gwy; + +/* keep synced with pfi_kif, used in RB_FIND */ +struct pfi_kif_cmp { + char pfik_name[IFNAMSIZ]; +}; + +struct pfi_kif { + char pfik_name[IFNAMSIZ]; + RB_ENTRY(pfi_kif) pfik_tree; + u_int64_t pfik_packets[2][2][2]; + u_int64_t pfik_bytes[2][2][2]; + u_int64_t pfik_tzero; + int pfik_flags; + void *pfik_ah_cookie; + struct ifnet *pfik_ifp; + int pfik_states; + int pfik_rules; + TAILQ_HEAD(, pfi_dynaddr) pfik_dynaddrs; +}; + +enum pfi_kif_refs { + PFI_KIF_REF_NONE, + PFI_KIF_REF_STATE, + PFI_KIF_REF_RULE +}; + +struct pfi_uif { +#else /* !KERNEL */ +struct pfi_kif { +#endif /* !KERNEL */ + char pfik_name[IFNAMSIZ]; + u_int64_t pfik_packets[2][2][2]; + u_int64_t pfik_bytes[2][2][2]; + u_int64_t pfik_tzero; + int pfik_flags; + int pfik_states; + int pfik_rules; +#if !defined(__LP64__) + u_int32_t _pad; +#endif /* !__LP64__ */ +}; + +#define PFI_IFLAG_SKIP 0x0100 /* skip filtering on interface */ + +#ifdef KERNEL +struct pf_pdesc { + struct { + int done; + uid_t uid; + gid_t gid; + pid_t pid; + } lookup; + u_int64_t tot_len; /* Make Mickey money */ + union { + struct tcphdr *tcp; + struct udphdr *udp; + struct icmp *icmp; +#if INET6 + struct icmp6_hdr *icmp6; +#endif /* INET6 */ + struct pf_grev1_hdr *grev1; + struct pf_esp_hdr *esp; + void *any; + } hdr; + + /* XXX TODO: Change baddr and naddr to *saddr */ + struct pf_addr baddr; /* src address before translation */ + struct pf_addr bdaddr; /* dst address before translation */ + struct pf_addr naddr; /* src address after translation */ + struct pf_addr ndaddr; /* dst address after translation */ + struct pf_rule *nat_rule; /* nat/rdr rule applied to packet */ + struct pf_addr *src; + struct pf_addr *dst; + struct ether_header + *eh; + struct mbuf *mp; + int lmw; /* lazy writable offset */ + struct pf_mtag *pf_mtag; + u_int16_t *ip_sum; + u_int32_t off; /* protocol header offset */ + u_int32_t hdrlen; /* protocol header length */ + u_int32_t p_len; /* total length of payload */ + u_int16_t flags; /* Let SCRUB trigger behavior in */ + /* state code. Easier than tags */ +#define PFDESC_TCP_NORM 0x0001 /* TCP shall be statefully scrubbed */ +#define PFDESC_IP_REAS 0x0002 /* IP frags would've been reassembled */ +#define PFDESC_IP_FRAG 0x0004 /* This is a fragment */ + sa_family_t af; + sa_family_t naf; /* address family after translation */ + u_int8_t proto; + u_int8_t tos; + u_int8_t ttl; + u_int8_t proto_variant; + mbuf_svc_class_t sc; /* mbuf service class (MBUF_SVC) */ + u_int32_t pktflags; /* mbuf packet flags (PKTF) */ + u_int32_t flowsrc; /* flow source (FLOWSRC) */ + u_int32_t flowhash; /* flow hash to identify the sender */ +}; +#endif /* KERNEL */ + +/* flags for RDR options */ +#define PF_DPORT_RANGE 0x01 /* Dest port uses range */ +#define PF_RPORT_RANGE 0x02 /* RDR'ed port uses range */ + +/* Reasons code for passing/dropping a packet */ +#define PFRES_MATCH 0 /* Explicit match of a rule */ +#define PFRES_BADOFF 1 /* Bad offset for pull_hdr */ +#define PFRES_FRAG 2 /* Dropping following fragment */ +#define PFRES_SHORT 3 /* Dropping short packet */ +#define PFRES_NORM 4 /* Dropping by normalizer */ +#define PFRES_MEMORY 5 /* Dropped due to lacking mem */ +#define PFRES_TS 6 /* Bad TCP Timestamp (RFC1323) */ +#define PFRES_CONGEST 7 /* Congestion (of ipintrq) */ +#define PFRES_IPOPTIONS 8 /* IP option */ +#define PFRES_PROTCKSUM 9 /* Protocol checksum invalid */ +#define PFRES_BADSTATE 10 /* State mismatch */ +#define PFRES_STATEINS 11 /* State insertion failure */ +#define PFRES_MAXSTATES 12 /* State limit */ +#define PFRES_SRCLIMIT 13 /* Source node/conn limit */ +#define PFRES_SYNPROXY 14 /* SYN proxy */ +#define PFRES_DUMMYNET 15 /* Dummynet */ +#define PFRES_MAX 16 /* total+1 */ + +#define PFRES_NAMES { \ + "match", \ + "bad-offset", \ + "fragment", \ + "short", \ + "normalize", \ + "memory", \ + "bad-timestamp", \ + "congestion", \ + "ip-option", \ + "proto-cksum", \ + "state-mismatch", \ + "state-insert", \ + "state-limit", \ + "src-limit", \ + "synproxy", \ + "dummynet", \ + NULL \ +} + +/* Counters for other things we want to keep track of */ +#define LCNT_STATES 0 /* states */ +#define LCNT_SRCSTATES 1 /* max-src-states */ +#define LCNT_SRCNODES 2 /* max-src-nodes */ +#define LCNT_SRCCONN 3 /* max-src-conn */ +#define LCNT_SRCCONNRATE 4 /* max-src-conn-rate */ +#define LCNT_OVERLOAD_TABLE 5 /* entry added to overload table */ +#define LCNT_OVERLOAD_FLUSH 6 /* state entries flushed */ +#define LCNT_MAX 7 /* total+1 */ + +#define LCNT_NAMES { \ + "max states per rule", \ + "max-src-states", \ + "max-src-nodes", \ + "max-src-conn", \ + "max-src-conn-rate", \ + "overload table insertion", \ + "overload flush states", \ + NULL \ +} + +/* UDP state enumeration */ +#define PFUDPS_NO_TRAFFIC 0 +#define PFUDPS_SINGLE 1 +#define PFUDPS_MULTIPLE 2 + +#define PFUDPS_NSTATES 3 /* number of state levels */ + +#define PFUDPS_NAMES { \ + "NO_TRAFFIC", \ + "SINGLE", \ + "MULTIPLE", \ + NULL \ +} + +/* GREv1 protocol state enumeration */ +#define PFGRE1S_NO_TRAFFIC 0 +#define PFGRE1S_INITIATING 1 +#define PFGRE1S_ESTABLISHED 2 + +#define PFGRE1S_NSTATES 3 /* number of state levels */ + +#define PFGRE1S_NAMES { \ + "NO_TRAFFIC", \ + "INITIATING", \ + "ESTABLISHED", \ + NULL \ +} + +#define PFESPS_NO_TRAFFIC 0 +#define PFESPS_INITIATING 1 +#define PFESPS_ESTABLISHED 2 + +#define PFESPS_NSTATES 3 /* number of state levels */ + +#define PFESPS_NAMES { "NO_TRAFFIC", "INITIATING", "ESTABLISHED", NULL } + +/* Other protocol state enumeration */ +#define PFOTHERS_NO_TRAFFIC 0 +#define PFOTHERS_SINGLE 1 +#define PFOTHERS_MULTIPLE 2 + +#define PFOTHERS_NSTATES 3 /* number of state levels */ + +#define PFOTHERS_NAMES { \ + "NO_TRAFFIC", \ + "SINGLE", \ + "MULTIPLE", \ + NULL \ +} + +#define FCNT_STATE_SEARCH 0 +#define FCNT_STATE_INSERT 1 +#define FCNT_STATE_REMOVALS 2 +#define FCNT_MAX 3 + +#define SCNT_SRC_NODE_SEARCH 0 +#define SCNT_SRC_NODE_INSERT 1 +#define SCNT_SRC_NODE_REMOVALS 2 +#define SCNT_MAX 3 + +#ifdef KERNEL +#define ACTION_SET(a, x) \ + do { \ + if ((a) != NULL) \ + *(a) = (x); \ + } while (0) + +#define REASON_SET(a, x) \ + do { \ + if ((a) != NULL) \ + *(a) = (x); \ + if (x < PFRES_MAX) \ + pf_status.counters[x]++; \ + } while (0) +#endif /* KERNEL */ + +struct pf_status { + u_int64_t counters[PFRES_MAX]; + u_int64_t lcounters[LCNT_MAX]; /* limit counters */ + u_int64_t fcounters[FCNT_MAX]; + u_int64_t scounters[SCNT_MAX]; + u_int64_t pcounters[2][2][3]; + u_int64_t bcounters[2][2]; + u_int64_t stateid; + u_int32_t running; + u_int32_t states; + u_int32_t src_nodes; + u_int64_t since __attribute__((aligned(8))); + u_int32_t debug; + u_int32_t hostid; + char ifname[IFNAMSIZ]; + u_int8_t pf_chksum[PF_MD5_DIGEST_LENGTH]; +}; + +struct cbq_opts { + u_int32_t minburst; + u_int32_t maxburst; + u_int32_t pktsize; + u_int32_t maxpktsize; + u_int32_t ns_per_byte; + u_int32_t maxidle; + int32_t minidle; + u_int32_t offtime; + u_int32_t flags; +}; + +struct priq_opts { + u_int32_t flags; +}; + +struct qfq_opts { + u_int32_t flags; + u_int32_t lmax; +}; + +struct hfsc_opts { + /* real-time service curve */ + u_int64_t rtsc_m1; /* slope of the 1st segment in bps */ + u_int64_t rtsc_d; /* the x-projection of m1 in msec */ + u_int64_t rtsc_m2; /* slope of the 2nd segment in bps */ + u_int32_t rtsc_fl; /* service curve flags */ +#if !defined(__LP64__) + u_int32_t _pad; +#endif /* !__LP64__ */ + /* link-sharing service curve */ + u_int64_t lssc_m1; + u_int64_t lssc_d; + u_int64_t lssc_m2; + u_int32_t lssc_fl; +#if !defined(__LP64__) + u_int32_t __pad; +#endif /* !__LP64__ */ + /* upper-limit service curve */ + u_int64_t ulsc_m1; + u_int64_t ulsc_d; + u_int64_t ulsc_m2; + u_int32_t ulsc_fl; + u_int32_t flags; /* scheduler flags */ +}; + +struct fairq_opts { + u_int32_t nbuckets; /* hash buckets */ + u_int32_t flags; + u_int64_t hogs_m1; /* hog detection bandwidth */ + + /* link-sharing service curve */ + u_int64_t lssc_m1; + u_int64_t lssc_d; + u_int64_t lssc_m2; +}; + +/* bandwidth types */ +#define PF_ALTQ_BW_ABSOLUTE 1 /* bw in absolute value (bps) */ +#define PF_ALTQ_BW_PERCENT 2 /* bandwidth in percentage */ + +/* ALTQ rule flags */ +#define PF_ALTQF_TBR 0x1 /* enable Token Bucket Regulator */ + +/* queue rule flags */ +#define PF_ALTQ_QRF_WEIGHT 0x1 /* weight instead of priority */ + +struct pf_altq { + char ifname[IFNAMSIZ]; + + /* discipline-specific state */ + void *altq_disc __attribute__((aligned(8))); + TAILQ_ENTRY(pf_altq) entries __attribute__((aligned(8))); +#if !defined(__LP64__) + u_int32_t _pad[2]; +#endif /* !__LP64__ */ + + u_int32_t aflags; /* ALTQ rule flags */ + u_int32_t bwtype; /* bandwidth type */ + + /* scheduler spec */ + u_int32_t scheduler; /* scheduler type */ + u_int32_t tbrsize; /* tokenbucket regulator size */ + u_int64_t ifbandwidth; /* interface bandwidth */ + + /* queue spec */ + char qname[PF_QNAME_SIZE]; /* queue name */ + char parent[PF_QNAME_SIZE]; /* parent name */ + u_int32_t parent_qid; /* parent queue id */ + u_int32_t qrflags; /* queue rule flags */ + union { + u_int32_t priority; /* priority */ + u_int32_t weight; /* weight */ + }; + u_int32_t qlimit; /* queue size limit */ + u_int32_t flags; /* misc flags */ +#if !defined(__LP64__) + u_int32_t __pad; +#endif /* !__LP64__ */ + u_int64_t bandwidth; /* queue bandwidth */ + union { + struct cbq_opts cbq_opts; + struct priq_opts priq_opts; + struct hfsc_opts hfsc_opts; + struct fairq_opts fairq_opts; + struct qfq_opts qfq_opts; + } pq_u; + + u_int32_t qid; /* return value */ +}; + +struct pf_tagname { + TAILQ_ENTRY(pf_tagname) entries; + char name[PF_TAG_NAME_SIZE]; + u_int16_t tag; + int ref; +}; + +#define PFFRAG_FRENT_HIWAT 5000 /* Number of fragment entries */ +#define PFFRAG_FRAG_HIWAT 1000 /* Number of fragmented packets */ +#define PFFRAG_FRCENT_HIWAT 50000 /* Number of fragment cache entries */ +#define PFFRAG_FRCACHE_HIWAT 10000 /* Number of fragment descriptors */ + +#define PFR_KTABLE_HIWAT 1000 /* Number of tables */ +#define PFR_KENTRY_HIWAT 200000 /* Number of table entries */ +#define PFR_KENTRY_HIWAT_SMALL 100000 /* Number of table entries (tiny hosts) */ + +/* + * ioctl parameter structures + */ + +struct pfioc_pooladdr { + u_int32_t action; + u_int32_t ticket; + u_int32_t nr; + u_int32_t r_num; + u_int8_t r_action; + u_int8_t r_last; + u_int8_t af; + char anchor[MAXPATHLEN]; + struct pf_pooladdr addr; +}; + +struct pfioc_rule { + u_int32_t action; + u_int32_t ticket; + u_int32_t pool_ticket; + u_int32_t nr; + char anchor[MAXPATHLEN]; + char anchor_call[MAXPATHLEN]; + struct pf_rule rule; +}; + +struct pfioc_natlook { + struct pf_addr saddr; + struct pf_addr daddr; + struct pf_addr rsaddr; + struct pf_addr rdaddr; + union pf_state_xport sxport; + union pf_state_xport dxport; + union pf_state_xport rsxport; + union pf_state_xport rdxport; + sa_family_t af; + u_int8_t proto; + u_int8_t proto_variant; + u_int8_t direction; +}; + +struct pfioc_state { + struct pfsync_state state; +}; + +struct pfioc_src_node_kill { + /* XXX returns the number of src nodes killed in psnk_af */ + sa_family_t psnk_af; + struct pf_rule_addr psnk_src; + struct pf_rule_addr psnk_dst; +}; + +struct pfioc_state_addr_kill { + struct pf_addr_wrap addr; + u_int8_t reserved_[3]; + u_int8_t neg; + union pf_rule_xport xport; +}; + +struct pfioc_state_kill { + /* XXX returns the number of states killed in psk_af */ + sa_family_t psk_af; + u_int8_t psk_proto; + u_int8_t psk_proto_variant; + u_int8_t _pad; + struct pfioc_state_addr_kill psk_src; + struct pfioc_state_addr_kill psk_dst; + char psk_ifname[IFNAMSIZ]; + char psk_ownername[PF_OWNER_NAME_SIZE]; +}; + +struct pfioc_states { + int ps_len; + union { + caddr_t psu_buf; + struct pfsync_state *psu_states; + } ps_u __attribute__((aligned(8))); +#define ps_buf ps_u.psu_buf +#define ps_states ps_u.psu_states +}; + +#ifdef KERNEL +struct pfioc_states_32 { + int ps_len; + union { + user32_addr_t psu_buf; + user32_addr_t psu_states; + } ps_u __attribute__((aligned(8))); +}; + +struct pfioc_states_64 { + int ps_len; + union { + user64_addr_t psu_buf; + user64_addr_t psu_states; + } ps_u __attribute__((aligned(8))); +}; +#endif /* KERNEL */ + +#define PFTOK_PROCNAME_LEN 64 +#pragma pack(1) +struct pfioc_token { + u_int64_t token_value; + u_int64_t timestamp; + pid_t pid; + char proc_name[PFTOK_PROCNAME_LEN]; +}; +#pragma pack() + +struct pfioc_kernel_token { + SLIST_ENTRY(pfioc_kernel_token) next; + struct pfioc_token token; +}; + +struct pfioc_remove_token { + u_int64_t token_value; + u_int64_t refcount; +}; + +struct pfioc_tokens { + int size; + union { + caddr_t pgtu_buf; + struct pfioc_token *pgtu_tokens; + } pgt_u __attribute__((aligned(8))); +#define pgt_buf pgt_u.pgtu_buf +#define pgt_tokens pgt_u.pgtu_tokens +}; + +#ifdef KERNEL +struct pfioc_tokens_32 { + int size; + union { + user32_addr_t pgtu_buf; + user32_addr_t pgtu_tokens; + } pgt_u __attribute__((aligned(8))); +}; + +struct pfioc_tokens_64 { + int size; + union { + user64_addr_t pgtu_buf; + user64_addr_t pgtu_tokens; + } pgt_u __attribute__((aligned(8))); +}; +#endif /* KERNEL */ + + +struct pfioc_src_nodes { + int psn_len; + union { + caddr_t psu_buf; + struct pf_src_node *psu_src_nodes; + } psn_u __attribute__((aligned(8))); +#define psn_buf psn_u.psu_buf +#define psn_src_nodes psn_u.psu_src_nodes +}; + +#ifdef KERNEL +struct pfioc_src_nodes_32 { + int psn_len; + union { + user32_addr_t psu_buf; + user32_addr_t psu_src_nodes; + } psn_u __attribute__((aligned(8))); +}; + +struct pfioc_src_nodes_64 { + int psn_len; + union { + user64_addr_t psu_buf; + user64_addr_t psu_src_nodes; + } psn_u __attribute__((aligned(8))); +}; +#endif /* KERNEL */ + +struct pfioc_if { + char ifname[IFNAMSIZ]; +}; + +struct pfioc_tm { + int timeout; + int seconds; +}; + +struct pfioc_limit { + int index; + unsigned limit; +}; + +struct pfioc_altq { + u_int32_t action; + u_int32_t ticket; + u_int32_t nr; + struct pf_altq altq __attribute__((aligned(8))); +}; + +struct pfioc_qstats { + u_int32_t ticket; + u_int32_t nr; + void *buf __attribute__((aligned(8))); + int nbytes __attribute__((aligned(8))); + u_int8_t scheduler; +}; + +struct pfioc_ruleset { + u_int32_t nr; + char path[MAXPATHLEN]; + char name[PF_ANCHOR_NAME_SIZE]; +}; + +#define PF_RULESET_ALTQ (PF_RULESET_MAX) +#define PF_RULESET_TABLE (PF_RULESET_MAX+1) +struct pfioc_trans { + int size; /* number of elements */ + int esize; /* size of each element in bytes */ + struct pfioc_trans_e { + int rs_num; + char anchor[MAXPATHLEN]; + u_int32_t ticket; + } *array __attribute__((aligned(8))); +}; + +#ifdef KERNEL +struct pfioc_trans_32 { + int size; /* number of elements */ + int esize; /* size of each element in bytes */ + user32_addr_t array __attribute__((aligned(8))); +}; + +struct pfioc_trans_64 { + int size; /* number of elements */ + int esize; /* size of each element in bytes */ + user64_addr_t array __attribute__((aligned(8))); +}; +#endif /* KERNEL */ + + +#define PFR_FLAG_ATOMIC 0x00000001 +#define PFR_FLAG_DUMMY 0x00000002 +#define PFR_FLAG_FEEDBACK 0x00000004 +#define PFR_FLAG_CLSTATS 0x00000008 +#define PFR_FLAG_ADDRSTOO 0x00000010 +#define PFR_FLAG_REPLACE 0x00000020 +#define PFR_FLAG_ALLRSETS 0x00000040 +#define PFR_FLAG_ALLMASK 0x0000007F +#ifdef KERNEL +#define PFR_FLAG_USERIOCTL 0x10000000 +#endif /* KERNEL */ + +struct pfioc_table { + struct pfr_table pfrio_table; + void *pfrio_buffer __attribute__((aligned(8))); + int pfrio_esize __attribute__((aligned(8))); + int pfrio_size; + int pfrio_size2; + int pfrio_nadd; + int pfrio_ndel; + int pfrio_nchange; + int pfrio_flags; + u_int32_t pfrio_ticket; +}; +#define pfrio_exists pfrio_nadd +#define pfrio_nzero pfrio_nadd +#define pfrio_nmatch pfrio_nadd +#define pfrio_naddr pfrio_size2 +#define pfrio_setflag pfrio_size2 +#define pfrio_clrflag pfrio_nadd + +#ifdef KERNEL +struct pfioc_table_32 { + struct pfr_table pfrio_table; + user32_addr_t pfrio_buffer __attribute__((aligned(8))); + int pfrio_esize __attribute__((aligned(8))); + int pfrio_size; + int pfrio_size2; + int pfrio_nadd; + int pfrio_ndel; + int pfrio_nchange; + int pfrio_flags; + u_int32_t pfrio_ticket; +}; + +struct pfioc_table_64 { + struct pfr_table pfrio_table; + user64_addr_t pfrio_buffer __attribute__((aligned(8))); + int pfrio_esize __attribute__((aligned(8))); + int pfrio_size; + int pfrio_size2; + int pfrio_nadd; + int pfrio_ndel; + int pfrio_nchange; + int pfrio_flags; + u_int32_t pfrio_ticket; +}; +#endif /* KERNEL */ + +struct pfioc_iface { + char pfiio_name[IFNAMSIZ]; + void *pfiio_buffer __attribute__((aligned(8))); + int pfiio_esize __attribute__((aligned(8))); + int pfiio_size; + int pfiio_nzero; + int pfiio_flags; +}; + +#ifdef KERNEL +struct pfioc_iface_32 { + char pfiio_name[IFNAMSIZ]; + user32_addr_t pfiio_buffer __attribute__((aligned(8))); + int pfiio_esize __attribute__((aligned(8))); + int pfiio_size; + int pfiio_nzero; + int pfiio_flags; +}; + +struct pfioc_iface_64 { + char pfiio_name[IFNAMSIZ]; + user64_addr_t pfiio_buffer __attribute__((aligned(8))); + int pfiio_esize __attribute__((aligned(8))); + int pfiio_size; + int pfiio_nzero; + int pfiio_flags; +}; +#endif /* KERNEL */ + +struct pf_ifspeed { + char ifname[IFNAMSIZ]; + u_int64_t baudrate; +}; + +/* + * ioctl operations + */ + +#define DIOCSTART _IO ('D', 1) +#define DIOCSTOP _IO ('D', 2) +#define DIOCADDRULE _IOWR('D', 4, struct pfioc_rule) +#define DIOCGETSTARTERS _IOWR('D', 5, struct pfioc_tokens) +#define DIOCGETRULES _IOWR('D', 6, struct pfioc_rule) +#define DIOCGETRULE _IOWR('D', 7, struct pfioc_rule) +#define DIOCSTARTREF _IOR ('D', 8, u_int64_t) +#define DIOCSTOPREF _IOWR('D', 9, struct pfioc_remove_token) +/* XXX cut 10 - 17 */ +#define DIOCCLRSTATES _IOWR('D', 18, struct pfioc_state_kill) +#define DIOCGETSTATE _IOWR('D', 19, struct pfioc_state) +#define DIOCSETSTATUSIF _IOWR('D', 20, struct pfioc_if) +#define DIOCGETSTATUS _IOWR('D', 21, struct pf_status) +#define DIOCCLRSTATUS _IO ('D', 22) +#define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook) +#define DIOCSETDEBUG _IOWR('D', 24, u_int32_t) +#define DIOCGETSTATES _IOWR('D', 25, struct pfioc_states) +#define DIOCCHANGERULE _IOWR('D', 26, struct pfioc_rule) +#define DIOCINSERTRULE _IOWR('D', 27, struct pfioc_rule) +#define DIOCDELETERULE _IOWR('D', 28, struct pfioc_rule) +#define DIOCSETTIMEOUT _IOWR('D', 29, struct pfioc_tm) +#define DIOCGETTIMEOUT _IOWR('D', 30, struct pfioc_tm) +#define DIOCADDSTATE _IOWR('D', 37, struct pfioc_state) +#define DIOCCLRRULECTRS _IO ('D', 38) +#define DIOCGETLIMIT _IOWR('D', 39, struct pfioc_limit) +#define DIOCSETLIMIT _IOWR('D', 40, struct pfioc_limit) +#define DIOCKILLSTATES _IOWR('D', 41, struct pfioc_state_kill) +#define DIOCSTARTALTQ _IO ('D', 42) +#define DIOCSTOPALTQ _IO ('D', 43) +#define DIOCADDALTQ _IOWR('D', 45, struct pfioc_altq) +#define DIOCGETALTQS _IOWR('D', 47, struct pfioc_altq) +#define DIOCGETALTQ _IOWR('D', 48, struct pfioc_altq) +#define DIOCCHANGEALTQ _IOWR('D', 49, struct pfioc_altq) +#define DIOCGETQSTATS _IOWR('D', 50, struct pfioc_qstats) +#define DIOCBEGINADDRS _IOWR('D', 51, struct pfioc_pooladdr) +#define DIOCADDADDR _IOWR('D', 52, struct pfioc_pooladdr) +#define DIOCGETADDRS _IOWR('D', 53, struct pfioc_pooladdr) +#define DIOCGETADDR _IOWR('D', 54, struct pfioc_pooladdr) +#define DIOCCHANGEADDR _IOWR('D', 55, struct pfioc_pooladdr) +/* XXX cut 55 - 57 */ +#define DIOCGETRULESETS _IOWR('D', 58, struct pfioc_ruleset) +#define DIOCGETRULESET _IOWR('D', 59, struct pfioc_ruleset) +#define DIOCRCLRTABLES _IOWR('D', 60, struct pfioc_table) +#define DIOCRADDTABLES _IOWR('D', 61, struct pfioc_table) +#define DIOCRDELTABLES _IOWR('D', 62, struct pfioc_table) +#define DIOCRGETTABLES _IOWR('D', 63, struct pfioc_table) +#define DIOCRGETTSTATS _IOWR('D', 64, struct pfioc_table) +#define DIOCRCLRTSTATS _IOWR('D', 65, struct pfioc_table) +#define DIOCRCLRADDRS _IOWR('D', 66, struct pfioc_table) +#define DIOCRADDADDRS _IOWR('D', 67, struct pfioc_table) +#define DIOCRDELADDRS _IOWR('D', 68, struct pfioc_table) +#define DIOCRSETADDRS _IOWR('D', 69, struct pfioc_table) +#define DIOCRGETADDRS _IOWR('D', 70, struct pfioc_table) +#define DIOCRGETASTATS _IOWR('D', 71, struct pfioc_table) +#define DIOCRCLRASTATS _IOWR('D', 72, struct pfioc_table) +#define DIOCRTSTADDRS _IOWR('D', 73, struct pfioc_table) +#define DIOCRSETTFLAGS _IOWR('D', 74, struct pfioc_table) +#define DIOCRINADEFINE _IOWR('D', 77, struct pfioc_table) +#define DIOCOSFPFLUSH _IO('D', 78) +#define DIOCOSFPADD _IOWR('D', 79, struct pf_osfp_ioctl) +#define DIOCOSFPGET _IOWR('D', 80, struct pf_osfp_ioctl) +#define DIOCXBEGIN _IOWR('D', 81, struct pfioc_trans) +#define DIOCXCOMMIT _IOWR('D', 82, struct pfioc_trans) +#define DIOCXROLLBACK _IOWR('D', 83, struct pfioc_trans) +#define DIOCGETSRCNODES _IOWR('D', 84, struct pfioc_src_nodes) +#define DIOCCLRSRCNODES _IO('D', 85) +#define DIOCSETHOSTID _IOWR('D', 86, u_int32_t) +#define DIOCIGETIFACES _IOWR('D', 87, struct pfioc_iface) +#define DIOCSETIFFLAG _IOWR('D', 89, struct pfioc_iface) +#define DIOCCLRIFFLAG _IOWR('D', 90, struct pfioc_iface) +#define DIOCKILLSRCNODES _IOWR('D', 91, struct pfioc_src_node_kill) +#define DIOCGIFSPEED _IOWR('D', 92, struct pf_ifspeed) + +#ifdef KERNEL +RB_HEAD(pf_src_tree, pf_src_node); +RB_PROTOTYPE_SC(__private_extern__, pf_src_tree, pf_src_node, entry, + pf_src_compare); +extern struct pf_src_tree tree_src_tracking; + +RB_HEAD(pf_state_tree_id, pf_state); +RB_PROTOTYPE_SC(__private_extern__, pf_state_tree_id, pf_state, + entry_id, pf_state_compare_id); +extern struct pf_state_tree_id tree_id; +extern struct pf_state_queue state_list; + +TAILQ_HEAD(pf_poolqueue, pf_pool); +extern struct pf_poolqueue pf_pools[2]; +extern struct pf_palist pf_pabuf; +extern u_int32_t ticket_pabuf; +#if PF_ALTQ +TAILQ_HEAD(pf_altqqueue, pf_altq); +extern struct pf_altqqueue pf_altqs[2]; +extern u_int32_t ticket_altqs_active; +extern u_int32_t ticket_altqs_inactive; +extern int altqs_inactive_open; +extern struct pf_altqqueue *pf_altqs_active; +extern struct pf_altqqueue *pf_altqs_inactive; +#endif /* PF_ALTQ */ +extern struct pf_poolqueue *pf_pools_active; +extern struct pf_poolqueue *pf_pools_inactive; + +__private_extern__ int pf_tbladdr_setup(struct pf_ruleset *, + struct pf_addr_wrap *); +__private_extern__ void pf_tbladdr_remove(struct pf_addr_wrap *); +__private_extern__ void pf_tbladdr_copyout(struct pf_addr_wrap *); +__private_extern__ void pf_calc_skip_steps(struct pf_rulequeue *); +__private_extern__ u_int32_t pf_calc_state_key_flowhash(struct pf_state_key *); + +extern struct pool pf_src_tree_pl, pf_rule_pl; +extern struct pool pf_state_pl, pf_state_key_pl, pf_pooladdr_pl; +extern struct pool pf_state_scrub_pl; +#if PF_ALTQ +extern struct pool pf_altq_pl; +#endif /* PF_ALTQ */ +extern struct pool pf_app_state_pl; + +extern struct thread *pf_purge_thread; + +__private_extern__ void pfinit(void); +__private_extern__ void pf_purge_thread_fn(void *, wait_result_t); +__private_extern__ void pf_purge_expired_src_nodes(void); +__private_extern__ void pf_purge_expired_states(u_int32_t); +__private_extern__ void pf_unlink_state(struct pf_state *); +__private_extern__ void pf_free_state(struct pf_state *); +__private_extern__ int pf_insert_state(struct pfi_kif *, struct pf_state *); +__private_extern__ int pf_insert_src_node(struct pf_src_node **, + struct pf_rule *, struct pf_addr *, sa_family_t); +__private_extern__ void pf_src_tree_remove_state(struct pf_state *); +__private_extern__ struct pf_state *pf_find_state_byid(struct pf_state_cmp *); +__private_extern__ struct pf_state *pf_find_state_all(struct pf_state_key_cmp *, + u_int, int *); +__private_extern__ void pf_print_state(struct pf_state *); +__private_extern__ void pf_print_flags(u_int8_t); +__private_extern__ u_int16_t pf_cksum_fixup(u_int16_t, u_int16_t, u_int16_t, + u_int8_t); + +extern struct ifnet *sync_ifp; +extern struct pf_rule pf_default_rule; +__private_extern__ void pf_addrcpy(struct pf_addr *, struct pf_addr *, + u_int8_t); +__private_extern__ void pf_rm_rule(struct pf_rulequeue *, struct pf_rule *); + +struct ip_fw_args; +#if INET +__private_extern__ int pf_test(int, struct ifnet *, struct mbuf **, + struct ether_header *, struct ip_fw_args *); +#endif /* INET */ + +#if INET6 +__private_extern__ int pf_test6(int, struct ifnet *, struct mbuf **, + struct ether_header *, struct ip_fw_args *); +__private_extern__ void pf_poolmask(struct pf_addr *, struct pf_addr *, + struct pf_addr *, struct pf_addr *, u_int8_t); +__private_extern__ void pf_addr_inc(struct pf_addr *, sa_family_t); +#endif /* INET6 */ + +__private_extern__ struct mbuf *pf_lazy_makewritable(struct pf_pdesc *, + struct mbuf *, int); +__private_extern__ void *pf_pull_hdr(struct mbuf *, int, void *, int, + u_short *, u_short *, sa_family_t); +__private_extern__ void pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t); +__private_extern__ int pflog_packet(struct pfi_kif *, struct mbuf *, + sa_family_t, u_int8_t, u_int8_t, struct pf_rule *, struct pf_rule *, + struct pf_ruleset *, struct pf_pdesc *); +__private_extern__ int pf_match_addr(u_int8_t, struct pf_addr *, + struct pf_addr *, struct pf_addr *, sa_family_t); +__private_extern__ int pf_match_addr_range(struct pf_addr *, struct pf_addr *, + struct pf_addr *, sa_family_t); +__private_extern__ int pf_match(u_int8_t, u_int32_t, u_int32_t, u_int32_t); +__private_extern__ int pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t); +__private_extern__ int pf_match_xport(u_int8_t, u_int8_t, union pf_rule_xport *, + union pf_state_xport *); +__private_extern__ int pf_match_uid(u_int8_t, uid_t, uid_t, uid_t); +__private_extern__ int pf_match_gid(u_int8_t, gid_t, gid_t, gid_t); + +__private_extern__ void pf_normalize_init(void); +__private_extern__ int pf_normalize_isempty(void); +__private_extern__ int pf_normalize_ip(struct mbuf **, int, struct pfi_kif *, + u_short *, struct pf_pdesc *); +__private_extern__ int pf_normalize_ip6(struct mbuf **, int, struct pfi_kif *, + u_short *, struct pf_pdesc *); +__private_extern__ int pf_normalize_tcp(int, struct pfi_kif *, struct mbuf *, + int, int, void *, struct pf_pdesc *); +__private_extern__ void pf_normalize_tcp_cleanup(struct pf_state *); +__private_extern__ int pf_normalize_tcp_init(struct mbuf *, int, + struct pf_pdesc *, struct tcphdr *, struct pf_state_peer *, + struct pf_state_peer *); +__private_extern__ int pf_normalize_tcp_stateful(struct mbuf *, int, + struct pf_pdesc *, u_short *, struct tcphdr *, struct pf_state *, + struct pf_state_peer *, struct pf_state_peer *, int *); +__private_extern__ u_int64_t pf_state_expires(const struct pf_state *); +__private_extern__ void pf_purge_expired_fragments(void); +__private_extern__ int pf_routable(struct pf_addr *addr, sa_family_t af, + struct pfi_kif *); +__private_extern__ int pf_rtlabel_match(struct pf_addr *, sa_family_t, + struct pf_addr_wrap *); +__private_extern__ int pf_socket_lookup(int, struct pf_pdesc *); +__private_extern__ struct pf_state_key *pf_alloc_state_key(struct pf_state *, + struct pf_state_key *); +__private_extern__ void pfr_initialize(void); +__private_extern__ int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, + sa_family_t); +__private_extern__ void pfr_update_stats(struct pfr_ktable *, struct pf_addr *, + sa_family_t, u_int64_t, int, int, int); +__private_extern__ int pfr_pool_get(struct pfr_ktable *, int *, + struct pf_addr *, struct pf_addr **, struct pf_addr **, sa_family_t); +__private_extern__ void pfr_dynaddr_update(struct pfr_ktable *, + struct pfi_dynaddr *); +__private_extern__ void pfr_table_copyin_cleanup(struct pfr_table *); +__private_extern__ struct pfr_ktable *pfr_attach_table(struct pf_ruleset *, + char *); +__private_extern__ void pfr_detach_table(struct pfr_ktable *); +__private_extern__ int pfr_clr_tables(struct pfr_table *, int *, int); +__private_extern__ int pfr_add_tables(user_addr_t, int, int *, int); +__private_extern__ int pfr_del_tables(user_addr_t, int, int *, int); +__private_extern__ int pfr_get_tables(struct pfr_table *, user_addr_t, + int *, int); +__private_extern__ int pfr_get_tstats(struct pfr_table *, user_addr_t, + int *, int); +__private_extern__ int pfr_clr_tstats(user_addr_t, int, int *, int); +__private_extern__ int pfr_set_tflags(user_addr_t, int, int, int, int *, + int *, int); +__private_extern__ int pfr_clr_addrs(struct pfr_table *, int *, int); +__private_extern__ int pfr_insert_kentry(struct pfr_ktable *, struct pfr_addr *, + u_int64_t); +__private_extern__ int pfr_add_addrs(struct pfr_table *, user_addr_t, + int, int *, int); +__private_extern__ int pfr_del_addrs(struct pfr_table *, user_addr_t, + int, int *, int); +__private_extern__ int pfr_set_addrs(struct pfr_table *, user_addr_t, + int, int *, int *, int *, int *, int, u_int32_t); +__private_extern__ int pfr_get_addrs(struct pfr_table *, user_addr_t, + int *, int); +__private_extern__ int pfr_get_astats(struct pfr_table *, user_addr_t, + int *, int); +__private_extern__ int pfr_clr_astats(struct pfr_table *, user_addr_t, + int, int *, int); +__private_extern__ int pfr_tst_addrs(struct pfr_table *, user_addr_t, + int, int *, int); +__private_extern__ int pfr_ina_begin(struct pfr_table *, u_int32_t *, int *, + int); +__private_extern__ int pfr_ina_rollback(struct pfr_table *, u_int32_t, int *, + int); +__private_extern__ int pfr_ina_commit(struct pfr_table *, u_int32_t, int *, + int *, int); +__private_extern__ int pfr_ina_define(struct pfr_table *, user_addr_t, + int, int *, int *, u_int32_t, int); + +extern struct pfi_kif *pfi_all; + +__private_extern__ void pfi_initialize(void); +__private_extern__ struct pfi_kif *pfi_kif_get(const char *); +__private_extern__ void pfi_kif_ref(struct pfi_kif *, enum pfi_kif_refs); +__private_extern__ void pfi_kif_unref(struct pfi_kif *, enum pfi_kif_refs); +__private_extern__ int pfi_kif_match(struct pfi_kif *, struct pfi_kif *); +__private_extern__ void pfi_attach_ifnet(struct ifnet *); +__private_extern__ void pfi_detach_ifnet(struct ifnet *); +__private_extern__ int pfi_match_addr(struct pfi_dynaddr *, struct pf_addr *, + sa_family_t); +__private_extern__ int pfi_dynaddr_setup(struct pf_addr_wrap *, sa_family_t); +__private_extern__ void pfi_dynaddr_remove(struct pf_addr_wrap *); +__private_extern__ void pfi_dynaddr_copyout(struct pf_addr_wrap *); +__private_extern__ void pfi_update_status(const char *, struct pf_status *); +__private_extern__ int pfi_get_ifaces(const char *, user_addr_t, int *); +__private_extern__ int pfi_set_flags(const char *, int); +__private_extern__ int pfi_clear_flags(const char *, int); + +__private_extern__ u_int16_t pf_tagname2tag(char *); +__private_extern__ void pf_tag2tagname(u_int16_t, char *); +__private_extern__ void pf_tag_ref(u_int16_t); +__private_extern__ void pf_tag_unref(u_int16_t); +__private_extern__ int pf_tag_packet(struct mbuf *, struct pf_mtag *, + int, unsigned int, struct pf_pdesc *); +__private_extern__ void pf_step_into_anchor(int *, struct pf_ruleset **, int, + struct pf_rule **, struct pf_rule **, int *); +__private_extern__ int pf_step_out_of_anchor(int *, struct pf_ruleset **, int, + struct pf_rule **, struct pf_rule **, int *); +__private_extern__ u_int32_t pf_qname2qid(char *); +__private_extern__ void pf_qid2qname(u_int32_t, char *); +__private_extern__ void pf_qid_unref(u_int32_t); + +extern struct pf_status pf_status; +extern struct pool pf_frent_pl, pf_frag_pl; + +struct pf_pool_limit { + void *pp; + unsigned limit; +}; +extern struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX]; + +__private_extern__ int pf_af_hook(struct ifnet *, struct mbuf **, + struct mbuf **, unsigned int, int, struct ip_fw_args *); +__private_extern__ int pf_ifaddr_hook(struct ifnet *); +__private_extern__ void pf_ifnet_hook(struct ifnet *, int); + +/* + * The following are defined with "private extern" storage class for + * kernel, and "extern" for user-space. + */ +extern struct pf_anchor_global pf_anchors; +extern struct pf_anchor pf_main_anchor; +#define pf_main_ruleset pf_main_anchor.ruleset + +extern int pf_is_enabled; +extern int16_t pf_nat64_configured; +#define PF_IS_ENABLED (pf_is_enabled != 0) +extern u_int32_t pf_hash_seed; + +#if PF_ALTQ +extern u_int32_t altq_allowed; +#endif /* PF_ALTQ */ + +/* these ruleset functions can be linked into userland programs (pfctl) */ +__private_extern__ int pf_get_ruleset_number(u_int8_t); +__private_extern__ void pf_init_ruleset(struct pf_ruleset *); +__private_extern__ int pf_anchor_setup(struct pf_rule *, + const struct pf_ruleset *, const char *); +__private_extern__ int pf_anchor_copyout(const struct pf_ruleset *, + const struct pf_rule *, struct pfioc_rule *); +__private_extern__ void pf_anchor_remove(struct pf_rule *); +__private_extern__ void pf_remove_if_empty_ruleset(struct pf_ruleset *); +__private_extern__ struct pf_anchor *pf_find_anchor(const char *); +__private_extern__ struct pf_ruleset *pf_find_ruleset(const char *); +__private_extern__ struct pf_ruleset *pf_find_ruleset_with_owner(const char *, + const char *, int, int *); +__private_extern__ struct pf_ruleset *pf_find_or_create_ruleset(const char *); +__private_extern__ void pf_rs_initialize(void); + +__private_extern__ int pf_osfp_add(struct pf_osfp_ioctl *); +__private_extern__ struct pf_osfp_enlist *pf_osfp_fingerprint(struct pf_pdesc *, + struct mbuf *, int, const struct tcphdr *); +__private_extern__ struct pf_osfp_enlist *pf_osfp_fingerprint_hdr( + const struct ip *, const struct ip6_hdr *, const struct tcphdr *); +__private_extern__ void pf_osfp_flush(void); +__private_extern__ int pf_osfp_get(struct pf_osfp_ioctl *); +__private_extern__ void pf_osfp_initialize(void); +__private_extern__ int pf_osfp_match(struct pf_osfp_enlist *, pf_osfp_t); +__private_extern__ struct pf_os_fingerprint *pf_osfp_validate(void); +__private_extern__ struct pf_mtag *pf_find_mtag(struct mbuf *); +__private_extern__ struct pf_mtag *pf_get_mtag(struct mbuf *); +#else /* !KERNEL */ +extern struct pf_anchor_global pf_anchors; +extern struct pf_anchor pf_main_anchor; +#define pf_main_ruleset pf_main_anchor.ruleset + +/* these ruleset functions can be linked into userland programs (pfctl) */ +extern int pf_get_ruleset_number(u_int8_t); +extern void pf_init_ruleset(struct pf_ruleset *); +extern int pf_anchor_setup(struct pf_rule *, const struct pf_ruleset *, + const char *); +extern int pf_anchor_copyout(const struct pf_ruleset *, const struct pf_rule *, + struct pfioc_rule *); +extern void pf_anchor_remove(struct pf_rule *); +extern void pf_remove_if_empty_ruleset(struct pf_ruleset *); +extern struct pf_anchor *pf_find_anchor(const char *); +extern struct pf_ruleset *pf_find_ruleset(const char *); +extern struct pf_ruleset *pf_find_ruleset_with_owner(const char *, + const char *, int, int *); +extern struct pf_ruleset *pf_find_or_create_ruleset(const char *); +extern void pf_rs_initialize(void); +#endif /* !KERNEL */ + +#ifdef __cplusplus +} +#endif +#endif /* PF || !KERNEL */ +#endif /* PRIVATE */ +#endif /* _NET_PFVAR_H_ */ diff --git a/xnu/10.12/net/radix.h b/xnu/10.12/net/radix.h new file mode 100644 index 00000000..d48399aa --- /dev/null +++ b/xnu/10.12/net/radix.h @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2000-2008 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1988, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)radix.h 8.2 (Berkeley) 10/31/94 + * $FreeBSD: src/sys/net/radix.h,v 1.16.2.1 2000/05/03 19:17:11 wollman Exp $ + */ + +#ifndef _RADIX_H_ +#define _RADIX_H_ +#include + +#ifdef PRIVATE + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_RTABLE); +#endif + +/* + * Radix search tree node layout. + */ + +struct radix_node { + struct radix_mask *rn_mklist; /* list of masks contained in subtree */ + struct radix_node *rn_parent; /* parent */ + short rn_bit; /* bit offset; -1-index(netmask) */ + char rn_bmask; /* node: mask for bit test*/ + u_char rn_flags; /* enumerated next */ +#define RNF_NORMAL 1 /* leaf contains normal route */ +#define RNF_ROOT 2 /* leaf is root leaf for tree */ +#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */ + union { + struct { /* leaf only data: */ + caddr_t rn_Key; /* object of search */ + caddr_t rn_Mask; /* netmask, if present */ + struct radix_node *rn_Dupedkey; + } rn_leaf; + struct { /* node only data: */ + int rn_Off; /* where to start compare */ + struct radix_node *rn_L;/* progeny */ + struct radix_node *rn_R;/* progeny */ + } rn_node; + } rn_u; +#ifdef RN_DEBUG + int rn_info; + struct radix_node *rn_twin; + struct radix_node *rn_ybro; +#endif + +}; + +#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey +#define rn_key rn_u.rn_leaf.rn_Key +#define rn_mask rn_u.rn_leaf.rn_Mask +#define rn_offset rn_u.rn_node.rn_Off +#define rn_left rn_u.rn_node.rn_L +#define rn_right rn_u.rn_node.rn_R + +/* + * Annotations to tree concerning potential routes applying to subtrees. + */ + +struct radix_mask { + short rm_bit; /* bit offset; -1-index(netmask) */ + char rm_unused; /* cf. rn_bmask */ + u_char rm_flags; /* cf. rn_flags */ + struct radix_mask *rm_mklist; /* more masks to try */ + union { + caddr_t rmu_mask; /* the mask */ + struct radix_node *rmu_leaf; /* for normal routes */ + } rm_rmu; + int rm_refs; /* # of references to this struct */ +}; + +#define rm_mask rm_rmu.rmu_mask +#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */ + + +#define MKGet(m) {\ + if (rn_mkfreelist) {\ + m = rn_mkfreelist; \ + rn_mkfreelist = (m)->rm_mklist; \ + } else \ + R_Malloc(m, struct radix_mask *, sizeof (*(m))); }\ + +#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);} + +typedef int walktree_f_t(struct radix_node *, void *); +typedef int rn_matchf_t(struct radix_node *, void *); + +struct radix_node_head { + struct radix_node *rnh_treetop; + int rnh_addrsize; /* permit, but not require fixed keys */ + int rnh_pktsize; /* permit, but not require fixed keys */ + struct radix_node *(*rnh_addaddr) /* add based on sockaddr */ + (void *v, void *mask, + struct radix_node_head *head, struct radix_node nodes[]); + struct radix_node *(*rnh_addpkt) /* add based on packet hdr */ + (void *v, void *mask, + struct radix_node_head *head, struct radix_node nodes[]); + struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */ + (void *v, void *mask, struct radix_node_head *head); + struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */ + (void *v, void *mask, struct radix_node_head *head); + struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */ + (void *v, struct radix_node_head *head); + /* locate based on sockaddr and rn_matchf_t() */ + struct radix_node *(*rnh_matchaddr_args) + (void *v, struct radix_node_head *head, + rn_matchf_t *f, void *w); + struct radix_node *(*rnh_lookup) /* locate based on sockaddr */ + (void *v, void *mask, struct radix_node_head *head); + /* locate based on sockaddr, mask and rn_matchf_t() */ + struct radix_node *(*rnh_lookup_args) + (void *v, void *mask, struct radix_node_head *head, + rn_matchf_t *f, void *); + struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */ + (void *v, struct radix_node_head *head); + int (*rnh_walktree) /* traverse tree */ + (struct radix_node_head *head, walktree_f_t *f, void *w); + int (*rnh_walktree_from) /* traverse tree below a */ + (struct radix_node_head *head, void *a, void *m, + walktree_f_t *f, void *w); + void (*rnh_close) /* do something when the last ref drops */ + (struct radix_node *rn, struct radix_node_head *head); + struct radix_node rnh_nodes[3]; /* empty tree for common case */ + int rnh_cnt; /* tree dimension */ +}; + +#ifndef KERNEL +#define Bcmp(a, b, n) bcmp(((char *)(a)), ((char *)(b)), (n)) +#define Bcopy(a, b, n) bcopy(((char *)(a)), ((char *)(b)), (unsigned)(n)) +#define Bzero(p, n) bzero((char *)(p), (int)(n)); +#define R_Malloc(p, t, n) (p = (t) malloc((unsigned int)(n))) +#define R_Free(p) free((char *)p); +#else +#define Bcmp(a, b, n) bcmp(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n)) +#define Bcopy(a, b, n) bcopy(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n)) +#define Bzero(p, n) bzero((caddr_t)(p), (unsigned)(n)); +#define R_Malloc(p, t, n) (p = (t) _MALLOC((uint32_t)(n), M_RTABLE, M_WAITOK)) +#define R_Free(p) FREE((caddr_t)p, M_RTABLE); +#endif /*KERNEL*/ + +void rn_init(void); +int rn_inithead(void **, int); +int rn_refines(void *, void *); +struct radix_node + *rn_addmask(void *, int, int), + *rn_addroute(void *, void *, struct radix_node_head *, + struct radix_node [2]), + *rn_delete(void *, void *, struct radix_node_head *), + *rn_lookup(void *v_arg, void *m_arg, struct radix_node_head *head), + *rn_lookup_args(void *v_arg, void *m_arg, struct radix_node_head *head, + rn_matchf_t *, void *), + *rn_match(void *, struct radix_node_head *), + *rn_match_args(void *, struct radix_node_head *, rn_matchf_t *, void *); + +#endif /* PRIVATE */ +#endif /* _RADIX_H_ */ From 9ddeb03b607cd9bfbcbd77f51e7deae544a4c902 Mon Sep 17 00:00:00 2001 From: Kevin LARQUEMIN Date: Sat, 29 Jul 2017 17:48:59 +0200 Subject: [PATCH 113/192] update README.md with MacOS --- README.md | 56 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 006f4588..c65a1e5c 100644 --- a/README.md +++ b/README.md @@ -19,29 +19,38 @@ need of blacklist. HOW TO BUILD ------------ -###Prerequisites +### Prerequisites The following libraries are required. * libevent2 * OpenSSL or PolarSSL -###Steps +## Build + +### Steps On general linux, simply run command below to build with OpenSSL. - make +``` +$ make +``` To compile with PolarSSL - make USE_CRYPTO_POLARSSL=true +``` +$ make USE_CRYPTO_POLARSSL=true +``` To compile static binaries (with Tomatoware) - make ENABLE_STATIC=true +``` +$ make ENABLE_STATIC=true +``` By default, HTTPS proxy support is disabled. To enable this feature, you need to compile like (Require libevent2 compiled with OpenSSL support): - - make ENABLE_HTTPS_PROXY=true +``` +$ make ENABLE_HTTPS_PROXY=true +``` Since this variant of redsocks is customized for running with Openwrt, please read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how @@ -53,7 +62,21 @@ Please see 'redsocks.conf.example' for whole picture of configuration file. Below are additional sample configuration sections for different usage. Operations required to iptables are not listed here. -###Redirect Blocked Traffic via Proxy Automatically +### MacOS +To build on a MacOS system, you will have to install OpenSSL headers and libevent2 +For this, brew is your best friends +``` +$ brew install openssl libevent +``` +Makefile include the folder of openssl headers and lib installed by brew. + +To build with PF and run on MacOS, you will need some pf headers that are not included with a standard MacOS installation. +You can find them on this repository : https://github.com/opensource-apple/xnu +And the Makefile will going find this file for you + +## Configuration + +### Redirect Blocked Traffic via Proxy Automatically To use the autoproxy feature, please change the redsocks section in configuration file like this: @@ -76,8 +99,8 @@ configuration file like this: //password = passwd; } -###Redirect Blocked Traffic via VPN Automatically -Suppose you have VPN connection setup with interface tun0. You want all +### Redirect Blocked Traffic via VPN Automatically +Suppose you have VPN connection setup with interface tun0. You want all all blocked traffic pass through via VPN connection while normal traffic pass through via default internet connection. @@ -90,7 +113,7 @@ pass through via default internet connection. autoproxy = 1; } -###Redirect Blocked Traffic via shadowsocks proxy +### Redirect Blocked Traffic via shadowsocks proxy Similar like other redsocks section. The encryption method is specified by field 'login'. @@ -106,7 +129,7 @@ by field 'login'. // method of shadowsocks password = "your password"; // Your shadowsocks password } - + redudp { local_ip = 127.0.0.1; local_port = 1053; @@ -151,7 +174,7 @@ List of supported encryption methods(Compiled with PolarSSL): CAMELLIA-192-CFB128 CAMELLIA-256-CFB128 -###Work with GoAgent +### Work with GoAgent To make redsocks2 works with GoAgent proxy, you need to set proxy type as 'http-relay' for HTTP protocol and 'http-connect' for HTTPS protocol respectively. @@ -163,7 +186,7 @@ The configuration for forwarding connections to GoAgent is like below: local_port = 1081; //HTTP should be redirect to this port. ip = 192.168.1.1; port = 8080; - type = http-relay; // Must be 'htt-relay' for HTTP traffic. + type = http-relay; // Must be 'htt-relay' for HTTP traffic. autoproxy = 1; // I want autoproxy feature enabled on this section. // timeout is meaningful when 'autoproxy' is non-zero. // It specified timeout value when trying to connect to destination @@ -176,7 +199,7 @@ The configuration for forwarding connections to GoAgent is like below: local_port = 1082; // HTTPS should be redirect to this port. ip = 192.168.1.1; port = 8080; - type = http-connect; // Must be 'htt-connect' for HTTPS traffic. + type = http-connect; // Must be 'htt-connect' for HTTPS traffic. autoproxy = 1; // I want autoproxy feature enabled on this section. // timeout is meaningful when 'autoproxy' is non-zero. // It specified timeout value when trying to connect to destination @@ -185,7 +208,7 @@ The configuration for forwarding connections to GoAgent is like below: timeout = 13; } -###Redirect UDP based DNS Request via TCP connection +### Redirect UDP based DNS Request via TCP connection Sending DNS request via TCP connection is one way to prevent from DNS poisoning. You can redirect all UDP based DNS requests via TCP connection with the following config section. @@ -208,4 +231,3 @@ server as the local IP:port configured above. AUTHOR ------ [Zhuofei Wang](mailto:semigodking.com) semigodking@gmail.com - From 6d0c588d17e24ce4a11c489e151dd4a989f47231 Mon Sep 17 00:00:00 2001 From: Kevin LARQUEMIN Date: Sat, 29 Jul 2017 17:53:10 +0200 Subject: [PATCH 114/192] make clean, clean object and let binary --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 86eb72d4..a383976f 100644 --- a/Makefile +++ b/Makefile @@ -115,9 +115,10 @@ $(OUT): $(OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) clean: - $(RM) $(OUT) $(CONF) $(OBJS) + $(RM) $(CONF) $(OBJS) distclean: clean + $(RM) $(OUT) $(RM) tags $(DEPS) $(RM) -r gen $(RM) -r $(OSX_ROOT_PATH) From d30099b7c3121db579165240f8c74ff7700ec1f2 Mon Sep 17 00:00:00 2001 From: Kevin LARQUEMIN Date: Sat, 29 Jul 2017 17:54:27 +0200 Subject: [PATCH 115/192] deleting xnu. Makefile will download them --- xnu/10.12/libkern/tree.h | 802 ------------- xnu/10.12/net/pfvar.h | 2446 -------------------------------------- xnu/10.12/net/radix.h | 214 ---- 3 files changed, 3462 deletions(-) delete mode 100644 xnu/10.12/libkern/tree.h delete mode 100644 xnu/10.12/net/pfvar.h delete mode 100644 xnu/10.12/net/radix.h diff --git a/xnu/10.12/libkern/tree.h b/xnu/10.12/libkern/tree.h deleted file mode 100644 index 3a26162b..00000000 --- a/xnu/10.12/libkern/tree.h +++ /dev/null @@ -1,802 +0,0 @@ -/* - * Copyright (c) 2009-2010 Apple Inc. All rights reserved. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - -/* $NetBSD: tree.h,v 1.13 2006/08/27 22:32:38 christos Exp $ */ -/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ -/* - * Copyright 2002 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _LIBKERN_TREE_H_ -#define _LIBKERN_TREE_H_ - -/* - * This file defines data structures for different types of trees: - * splay trees and red-black trees. - * - * A splay tree is a self-organizing data structure. Every operation - * on the tree causes a splay to happen. The splay moves the requested - * node to the root of the tree and partly rebalances it. - * - * This has the benefit that request locality causes faster lookups as - * the requested nodes move to the top of the tree. On the other hand, - * every lookup causes memory writes. - * - * The Balance Theorem bounds the total access time for m operations - * and n inserts on an initially empty tree as O((m + n)lg n). The - * amortized cost for a sequence of m accesses to a splay tree is O(lg n); - * - * A red-black tree is a binary search tree with the node color as an - * extra attribute. It fulfills a set of conditions: - * - every search path from the root to a leaf consists of the - * same number of black nodes, - * - each red node (except for the root) has a black parent, - * - each leaf node is black. - * - * Every operation on a red-black tree is bounded as O(lg n). - * The maximum height of a red-black tree is 2lg (n+1). - */ - -#define SPLAY_HEAD(name, type) \ -struct name { \ - struct type *sph_root; /* root of the tree */ \ -} - -#define SPLAY_INITIALIZER(root) \ - { NULL } - -#define SPLAY_INIT(root) do { \ - (root)->sph_root = NULL; \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_ENTRY(type) \ -struct { \ - struct type *spe_left; /* left element */ \ - struct type *spe_right; /* right element */ \ -} - -#define SPLAY_LEFT(elm, field) (elm)->field.spe_left -#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right -#define SPLAY_ROOT(head) (head)->sph_root -#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) - -/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ -#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_LINKLEFT(head, tmp, field) do { \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_LINKRIGHT(head, tmp, field) do { \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ - SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ - SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ -} while (/*CONSTCOND*/ 0) - -/* Generates prototypes and inline functions */ - -#define SPLAY_PROTOTYPE(name, type, field, cmp) \ -void name##_SPLAY(struct name *, struct type *); \ -void name##_SPLAY_MINMAX(struct name *, int); \ -struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ -struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ - \ -/* Finds the node with the same key as elm */ \ -static __inline struct type * \ -name##_SPLAY_FIND(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) \ - return(NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) \ - return (head->sph_root); \ - return (NULL); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_NEXT(struct name *head, struct type *elm) \ -{ \ - name##_SPLAY(head, elm); \ - if (SPLAY_RIGHT(elm, field) != NULL) { \ - elm = SPLAY_RIGHT(elm, field); \ - while (SPLAY_LEFT(elm, field) != NULL) { \ - elm = SPLAY_LEFT(elm, field); \ - } \ - } else \ - elm = NULL; \ - return (elm); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_MIN_MAX(struct name *head, int val) \ -{ \ - name##_SPLAY_MINMAX(head, val); \ - return (SPLAY_ROOT(head)); \ -} - -/* Main splay operation. - * Moves node close to the key of elm to top - */ -#define SPLAY_GENERATE(name, type, field, cmp) \ -struct type * \ -name##_SPLAY_INSERT(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) { \ - SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ - } else { \ - int __comp; \ - name##_SPLAY(head, elm); \ - __comp = (cmp)(elm, (head)->sph_root); \ - if(__comp < 0) { \ - SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ - SPLAY_RIGHT(elm, field) = (head)->sph_root; \ - SPLAY_LEFT((head)->sph_root, field) = NULL; \ - } else if (__comp > 0) { \ - SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ - SPLAY_LEFT(elm, field) = (head)->sph_root; \ - SPLAY_RIGHT((head)->sph_root, field) = NULL; \ - } else \ - return ((head)->sph_root); \ - } \ - (head)->sph_root = (elm); \ - return (NULL); \ -} \ - \ -struct type * \ -name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *__tmp; \ - if (SPLAY_EMPTY(head)) \ - return (NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) { \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ - } else { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ - name##_SPLAY(head, elm); \ - SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ - } \ - return (elm); \ - } \ - return (NULL); \ -} \ - \ -void \ -name##_SPLAY(struct name *head, struct type *elm) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ - int __comp; \ -\ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ - __left = __right = &__node; \ -\ - while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) > 0){ \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} \ - \ -/* Splay with either the minimum or the maximum element \ - * Used to find minimum or maximum element in tree. \ - */ \ -void name##_SPLAY_MINMAX(struct name *head, int __comp) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ -\ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ - __left = __right = &__node; \ -\ - while (1) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp > 0) { \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} - -#define SPLAY_NEGINF -1 -#define SPLAY_INF 1 - -#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) -#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) -#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) -#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) -#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) -#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) - -#define SPLAY_FOREACH(x, name, head) \ - for ((x) = SPLAY_MIN(name, head); \ - (x) != NULL; \ - (x) = SPLAY_NEXT(name, head, x)) - -/* Macros that define a red-black tree */ -#define RB_HEAD(name, type) \ -struct name { \ - struct type *rbh_root; /* root of the tree */ \ -} - -#define RB_INITIALIZER(root) \ - { NULL } - -#define RB_INIT(root) do { \ - (root)->rbh_root = NULL; \ -} while (/*CONSTCOND*/ 0) - -#define RB_BLACK 0 -#define RB_RED 1 -#define RB_PLACEHOLDER NULL -#define RB_ENTRY(type) \ -struct { \ - struct type *rbe_left; /* left element */ \ - struct type *rbe_right; /* right element */ \ - struct type *rbe_parent; /* parent element */ \ -} - -#define RB_COLOR_MASK (uintptr_t)0x1 -#define RB_LEFT(elm, field) (elm)->field.rbe_left -#define RB_RIGHT(elm, field) (elm)->field.rbe_right -#define _RB_PARENT(elm, field) (elm)->field.rbe_parent -#define RB_ROOT(head) (head)->rbh_root -#define RB_EMPTY(head) (RB_ROOT(head) == NULL) - -#define RB_SET(name, elm, parent, field) do { \ - name##_RB_SETPARENT(elm, parent); \ - RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ - name##_RB_SETCOLOR(elm, RB_RED); \ -} while (/*CONSTCOND*/ 0) - -#define RB_SET_BLACKRED(name, black, red, field) do { \ - name##_RB_SETCOLOR(black, RB_BLACK); \ - name##_RB_SETCOLOR(red, RB_RED); \ -} while (/*CONSTCOND*/ 0) - -#ifndef RB_AUGMENT -#define RB_AUGMENT(x) (void)(x) -#endif - -#define RB_ROTATE_LEFT(name, head, elm, tmp, field) do { \ - (tmp) = RB_RIGHT(elm, field); \ - if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ - name##_RB_SETPARENT(RB_LEFT(tmp, field),(elm)); \ - } \ - RB_AUGMENT(elm); \ - if (name##_RB_SETPARENT(tmp, name##_RB_GETPARENT(elm)) != NULL) { \ - if ((elm) == RB_LEFT(name##_RB_GETPARENT(elm), field)) \ - RB_LEFT(name##_RB_GETPARENT(elm), field) = (tmp); \ - else \ - RB_RIGHT(name##_RB_GETPARENT(elm), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_LEFT(tmp, field) = (elm); \ - name##_RB_SETPARENT(elm, (tmp)); \ - RB_AUGMENT(tmp); \ - if ((name##_RB_GETPARENT(tmp))) \ - RB_AUGMENT(name##_RB_GETPARENT(tmp)); \ -} while (/*CONSTCOND*/ 0) - -#define RB_ROTATE_RIGHT(name, head, elm, tmp, field) do { \ - (tmp) = RB_LEFT(elm, field); \ - if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ - name##_RB_SETPARENT(RB_RIGHT(tmp, field), (elm)); \ - } \ - RB_AUGMENT(elm); \ - if (name##_RB_SETPARENT(tmp, name##_RB_GETPARENT(elm)) != NULL) { \ - if ((elm) == RB_LEFT(name##_RB_GETPARENT(elm), field)) \ - RB_LEFT(name##_RB_GETPARENT(elm), field) = (tmp); \ - else \ - RB_RIGHT(name##_RB_GETPARENT(elm), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_RIGHT(tmp, field) = (elm); \ - name##_RB_SETPARENT(elm, tmp); \ - RB_AUGMENT(tmp); \ - if ((name##_RB_GETPARENT(tmp))) \ - RB_AUGMENT(name##_RB_GETPARENT(tmp)); \ -} while (/*CONSTCOND*/ 0) - -/* Generates prototypes and inline functions */ -#define RB_PROTOTYPE(name, type, field, cmp) \ -void name##_RB_INSERT_COLOR(struct name *, struct type *); \ -void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ -struct type *name##_RB_REMOVE(struct name *, struct type *); \ -struct type *name##_RB_INSERT(struct name *, struct type *); \ -struct type *name##_RB_FIND(struct name *, struct type *); \ -struct type *name##_RB_NEXT(struct type *); \ -struct type *name##_RB_MINMAX(struct name *, int); \ -struct type *name##_RB_GETPARENT(struct type*); \ -struct type *name##_RB_SETPARENT(struct type*, struct type*); \ -int name##_RB_GETCOLOR(struct type*); \ -void name##_RB_SETCOLOR(struct type*,int); - -/* Generates prototypes (with storage class) and inline functions */ -#define RB_PROTOTYPE_SC(_sc_, name, type, field, cmp) \ -_sc_ void name##_RB_INSERT_COLOR(struct name *, struct type *); \ -_sc_ void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *); \ -_sc_ struct type *name##_RB_REMOVE(struct name *, struct type *); \ -_sc_ struct type *name##_RB_INSERT(struct name *, struct type *); \ -_sc_ struct type *name##_RB_FIND(struct name *, struct type *); \ -_sc_ struct type *name##_RB_NEXT(struct type *); \ -_sc_ struct type *name##_RB_MINMAX(struct name *, int); \ -_sc_ struct type *name##_RB_GETPARENT(struct type*); \ -_sc_ struct type *name##_RB_SETPARENT(struct type*, struct type*); \ -_sc_ int name##_RB_GETCOLOR(struct type*); \ -_sc_ void name##_RB_SETCOLOR(struct type*,int); - - -/* Main rb operation. - * Moves node close to the key of elm to top - */ -#define RB_GENERATE(name, type, field, cmp) \ -struct type *name##_RB_GETPARENT(struct type *elm) { \ - struct type *parent = _RB_PARENT(elm, field); \ - if( parent != NULL) { \ - parent = (struct type*)((uintptr_t)parent & ~RB_COLOR_MASK);\ - return( (struct type*) ( (parent == (struct type*) RB_PLACEHOLDER) ? NULL: parent));\ - } \ - return((struct type*)NULL); \ -} \ -int name##_RB_GETCOLOR(struct type *elm) { \ - int color = 0; \ - color = (int)((uintptr_t)_RB_PARENT(elm,field) & RB_COLOR_MASK);\ - return(color); \ -} \ -void name##_RB_SETCOLOR(struct type *elm,int color) { \ - struct type *parent = name##_RB_GETPARENT(elm); \ - if(parent == (struct type*)NULL) \ - parent = (struct type*) RB_PLACEHOLDER; \ - _RB_PARENT(elm, field) = (struct type*)((uintptr_t)parent | (unsigned int)color);\ -} \ -struct type *name##_RB_SETPARENT(struct type *elm, struct type *parent) { \ - int color = name##_RB_GETCOLOR(elm); \ - _RB_PARENT(elm, field) = parent; \ - if(color) name##_RB_SETCOLOR(elm, color); \ - return(name##_RB_GETPARENT(elm)); \ -} \ - \ -void \ -name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ -{ \ - struct type *parent, *gparent, *tmp; \ - while ((parent = name##_RB_GETPARENT(elm)) != NULL && \ - name##_RB_GETCOLOR(parent) == RB_RED) { \ - gparent = name##_RB_GETPARENT(parent); \ - if (parent == RB_LEFT(gparent, field)) { \ - tmp = RB_RIGHT(gparent, field); \ - if (tmp && name##_RB_GETCOLOR(tmp) == RB_RED) { \ - name##_RB_SETCOLOR(tmp, RB_BLACK); \ - RB_SET_BLACKRED(name, parent, gparent, field);\ - elm = gparent; \ - continue; \ - } \ - if (RB_RIGHT(parent, field) == elm) { \ - RB_ROTATE_LEFT(name, head, parent, tmp, field);\ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(name, parent, gparent, field); \ - RB_ROTATE_RIGHT(name,head, gparent, tmp, field); \ - } else { \ - tmp = RB_LEFT(gparent, field); \ - if (tmp && name##_RB_GETCOLOR(tmp) == RB_RED) { \ - name##_RB_SETCOLOR(tmp, RB_BLACK); \ - RB_SET_BLACKRED(name, parent, gparent, field);\ - elm = gparent; \ - continue; \ - } \ - if (RB_LEFT(parent, field) == elm) { \ - RB_ROTATE_RIGHT(name, head, parent, tmp, field);\ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(name, parent, gparent, field); \ - RB_ROTATE_LEFT(name, head, gparent, tmp, field); \ - } \ - } \ - name##_RB_SETCOLOR(head->rbh_root, RB_BLACK); \ -} \ - \ -void \ -name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ -{ \ - struct type *tmp; \ - while ((elm == NULL || name##_RB_GETCOLOR(elm) == RB_BLACK) && \ - elm != RB_ROOT(head)) { \ - if (RB_LEFT(parent, field) == elm) { \ - tmp = RB_RIGHT(parent, field); \ - if (name##_RB_GETCOLOR(tmp) == RB_RED) { \ - RB_SET_BLACKRED(name, tmp, parent, field); \ - RB_ROTATE_LEFT(name, head, parent, tmp, field);\ - tmp = RB_RIGHT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) &&\ - (RB_RIGHT(tmp, field) == NULL || \ - name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK)) {\ - name##_RB_SETCOLOR(tmp, RB_RED); \ - elm = parent; \ - parent = name##_RB_GETPARENT(elm); \ - } else { \ - if (RB_RIGHT(tmp, field) == NULL || \ - name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK) {\ - struct type *oleft; \ - if ((oleft = RB_LEFT(tmp, field)) \ - != NULL) \ - name##_RB_SETCOLOR(oleft, RB_BLACK);\ - name##_RB_SETCOLOR(tmp, RB_RED); \ - RB_ROTATE_RIGHT(name, head, tmp, oleft, field);\ - tmp = RB_RIGHT(parent, field); \ - } \ - name##_RB_SETCOLOR(tmp, (name##_RB_GETCOLOR(parent)));\ - name##_RB_SETCOLOR(parent, RB_BLACK); \ - if (RB_RIGHT(tmp, field)) \ - name##_RB_SETCOLOR(RB_RIGHT(tmp, field),RB_BLACK);\ - RB_ROTATE_LEFT(name, head, parent, tmp, field);\ - elm = RB_ROOT(head); \ - break; \ - } \ - } else { \ - tmp = RB_LEFT(parent, field); \ - if (name##_RB_GETCOLOR(tmp) == RB_RED) { \ - RB_SET_BLACKRED(name, tmp, parent, field); \ - RB_ROTATE_RIGHT(name, head, parent, tmp, field);\ - tmp = RB_LEFT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) &&\ - (RB_RIGHT(tmp, field) == NULL || \ - name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK)) {\ - name##_RB_SETCOLOR(tmp, RB_RED); \ - elm = parent; \ - parent = name##_RB_GETPARENT(elm); \ - } else { \ - if (RB_LEFT(tmp, field) == NULL || \ - name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) {\ - struct type *oright; \ - if ((oright = RB_RIGHT(tmp, field)) \ - != NULL) \ - name##_RB_SETCOLOR(oright, RB_BLACK);\ - name##_RB_SETCOLOR(tmp, RB_RED); \ - RB_ROTATE_LEFT(name, head, tmp, oright, field);\ - tmp = RB_LEFT(parent, field); \ - } \ - name##_RB_SETCOLOR(tmp,(name##_RB_GETCOLOR(parent)));\ - name##_RB_SETCOLOR(parent, RB_BLACK); \ - if (RB_LEFT(tmp, field)) \ - name##_RB_SETCOLOR(RB_LEFT(tmp, field), RB_BLACK);\ - RB_ROTATE_RIGHT(name, head, parent, tmp, field);\ - elm = RB_ROOT(head); \ - break; \ - } \ - } \ - } \ - if (elm) \ - name##_RB_SETCOLOR(elm, RB_BLACK); \ -} \ - \ -struct type * \ -name##_RB_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *child, *parent, *old = elm; \ - int color; \ - if (RB_LEFT(elm, field) == NULL) \ - child = RB_RIGHT(elm, field); \ - else if (RB_RIGHT(elm, field) == NULL) \ - child = RB_LEFT(elm, field); \ - else { \ - struct type *left; \ - elm = RB_RIGHT(elm, field); \ - while ((left = RB_LEFT(elm, field)) != NULL) \ - elm = left; \ - child = RB_RIGHT(elm, field); \ - parent = name##_RB_GETPARENT(elm); \ - color = name##_RB_GETCOLOR(elm); \ - if (child) \ - name##_RB_SETPARENT(child, parent); \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ - if (name##_RB_GETPARENT(elm) == old) \ - parent = elm; \ - (elm)->field = (old)->field; \ - if (name##_RB_GETPARENT(old)) { \ - if (RB_LEFT(name##_RB_GETPARENT(old), field) == old)\ - RB_LEFT(name##_RB_GETPARENT(old), field) = elm;\ - else \ - RB_RIGHT(name##_RB_GETPARENT(old), field) = elm;\ - RB_AUGMENT(name##_RB_GETPARENT(old)); \ - } else \ - RB_ROOT(head) = elm; \ - name##_RB_SETPARENT(RB_LEFT(old, field), elm); \ - if (RB_RIGHT(old, field)) \ - name##_RB_SETPARENT(RB_RIGHT(old, field), elm); \ - if (parent) { \ - left = parent; \ - do { \ - RB_AUGMENT(left); \ - } while ((left = name##_RB_GETPARENT(left)) != NULL); \ - } \ - goto color; \ - } \ - parent = name##_RB_GETPARENT(elm); \ - color = name##_RB_GETCOLOR(elm); \ - if (child) \ - name##_RB_SETPARENT(child, parent); \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ -color: \ - if (color == RB_BLACK) \ - name##_RB_REMOVE_COLOR(head, parent, child); \ - return (old); \ -} \ - \ -/* Inserts a node into the RB tree */ \ -struct type * \ -name##_RB_INSERT(struct name *head, struct type *elm) \ -{ \ - struct type *tmp; \ - struct type *parent = NULL; \ - int comp = 0; \ - tmp = RB_ROOT(head); \ - while (tmp) { \ - parent = tmp; \ - comp = (cmp)(elm, parent); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - RB_SET(name, elm, parent, field); \ - if (parent != NULL) { \ - if (comp < 0) \ - RB_LEFT(parent, field) = elm; \ - else \ - RB_RIGHT(parent, field) = elm; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = elm; \ - name##_RB_INSERT_COLOR(head, elm); \ - return (NULL); \ -} \ - \ -/* Finds the node with the same key as elm */ \ -struct type * \ -name##_RB_FIND(struct name *head, struct type *elm) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - int comp; \ - while (tmp) { \ - comp = cmp(elm, tmp); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - return (NULL); \ -} \ - \ -/* ARGSUSED */ \ -struct type * \ -name##_RB_NEXT(struct type *elm) \ -{ \ - if (RB_RIGHT(elm, field)) { \ - elm = RB_RIGHT(elm, field); \ - while (RB_LEFT(elm, field)) \ - elm = RB_LEFT(elm, field); \ - } else { \ - if (name##_RB_GETPARENT(elm) && \ - (elm == RB_LEFT(name##_RB_GETPARENT(elm), field))) \ - elm = name##_RB_GETPARENT(elm); \ - else { \ - while (name##_RB_GETPARENT(elm) && \ - (elm == RB_RIGHT(name##_RB_GETPARENT(elm), field)))\ - elm = name##_RB_GETPARENT(elm); \ - elm = name##_RB_GETPARENT(elm); \ - } \ - } \ - return (elm); \ -} \ - \ -struct type * \ -name##_RB_MINMAX(struct name *head, int val) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - struct type *parent = NULL; \ - while (tmp) { \ - parent = tmp; \ - if (val < 0) \ - tmp = RB_LEFT(tmp, field); \ - else \ - tmp = RB_RIGHT(tmp, field); \ - } \ - return (parent); \ -} - - -#define RB_PROTOTYPE_PREV(name, type, field, cmp) \ - RB_PROTOTYPE(name, type, field, cmp) \ -struct type *name##_RB_PREV(struct type *); - - -#define RB_PROTOTYPE_SC_PREV(_sc_, name, type, field, cmp) \ - RB_PROTOTYPE_SC(_sc_, name, type, field, cmp) \ -_sc_ struct type *name##_RB_PREV(struct type *); - -#define RB_GENERATE_PREV(name, type, field, cmp) \ - RB_GENERATE(name, type, field, cmp) \ -struct type * \ -name##_RB_PREV(struct type *elm) \ -{ \ - if (RB_LEFT(elm, field)) { \ - elm = RB_LEFT(elm, field); \ - while (RB_RIGHT(elm, field)) \ - elm = RB_RIGHT(elm, field); \ - } else { \ - if (name##_RB_GETPARENT(elm) && \ - (elm == RB_RIGHT(name##_RB_GETPARENT(elm), field))) \ - elm = name##_RB_GETPARENT(elm); \ - else { \ - while (name##_RB_GETPARENT(elm) && \ - (elm == RB_LEFT(name##_RB_GETPARENT(elm), field)))\ - elm = name##_RB_GETPARENT(elm); \ - elm = name##_RB_GETPARENT(elm); \ - } \ - } \ - return (elm); \ -} \ - -#define RB_NEGINF -1 -#define RB_INF 1 - -#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) -#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) -#define RB_FIND(name, x, y) name##_RB_FIND(x, y) -#define RB_NEXT(name, x, y) name##_RB_NEXT(y) -#define RB_PREV(name, x, y) name##_RB_PREV(y) -#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) -#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) - -#define RB_FOREACH(x, name, head) \ - for ((x) = RB_MIN(name, head); \ - (x) != NULL; \ - (x) = name##_RB_NEXT(x)) - -#define RB_FOREACH_FROM(x, name, y) \ - for ((x) = (y); \ - ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ - (x) = (y)) - -#define RB_FOREACH_REVERSE_FROM(x, name, y) \ - for ((x) = (y); \ - ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ - (x) = (y)) - -#define RB_FOREACH_SAFE(x, name, head, y) \ - for ((x) = RB_MIN(name, head); \ - ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ - (x) = (y)) - -#endif /* _LIBKERN_TREE_H_ */ diff --git a/xnu/10.12/net/pfvar.h b/xnu/10.12/net/pfvar.h deleted file mode 100644 index 11e771bf..00000000 --- a/xnu/10.12/net/pfvar.h +++ /dev/null @@ -1,2446 +0,0 @@ -/* - * Copyright (c) 2007-2015 Apple Inc. All rights reserved. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - -/* $apfw: git commit b6bf13f8321283cd7ee82b1795e86506084b1b95 $ */ -/* $OpenBSD: pfvar.h,v 1.259 2007/12/02 12:08:04 pascoe Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * NAT64 - Copyright (c) 2010 Viagenie Inc. (http://www.viagenie.ca) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef _NET_PFVAR_H_ -#define _NET_PFVAR_H_ - -#ifdef PRIVATE -/* - * XXX - * XXX Private interfaces. Do not include this file; use pfctl(8) instead. - * XXX - */ -#if PF || !defined(KERNEL) - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#include - -#include -#include -#include -#ifdef KERNEL -#include -#include -#include - -#include -#include - -#if BYTE_ORDER == BIG_ENDIAN -#define htobe64(x) (x) -#else /* LITTLE ENDIAN */ -#define htobe64(x) __DARWIN_OSSwapInt64(x) -#endif /* LITTLE_ENDIAN */ - -#define be64toh(x) htobe64(x) - -extern lck_rw_t *pf_perim_lock; -extern lck_mtx_t *pf_lock; - -struct pool { - struct zone *pool_zone; /* pointer to backend zone */ - const char *pool_name; /* name of pool */ - unsigned int pool_count; /* # of outstanding elements */ - unsigned int pool_hiwat; /* high watermark */ - unsigned int pool_limit; /* hard limit */ - unsigned int pool_fails; /* # of failed allocs due to limit */ -}; - -#define PR_NOWAIT FALSE -#define PR_WAITOK TRUE - -__private_extern__ void pool_init(struct pool *, size_t, unsigned int, - unsigned int, int, const char *, void *); -__private_extern__ void pool_destroy(struct pool *); -__private_extern__ void pool_sethiwat(struct pool *, int); -__private_extern__ void pool_sethardlimit(struct pool *, int, - const char *, int); -__private_extern__ void *pool_get(struct pool *, int); -__private_extern__ void pool_put(struct pool *, void *); -__private_extern__ u_int64_t pf_time_second(void); -__private_extern__ u_int64_t pf_calendar_time_second(void); -#endif /* KERNEL */ - -union sockaddr_union { - struct sockaddr sa; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; -}; - -#define PF_TCPS_PROXY_SRC ((TCP_NSTATES)+0) -#define PF_TCPS_PROXY_DST ((TCP_NSTATES)+1) - -#define PF_MD5_DIGEST_LENGTH 16 -#ifdef MD5_DIGEST_LENGTH -#if PF_MD5_DIGEST_LENGTH != MD5_DIGEST_LENGTH -#error -#endif /* PF_MD5_DIGEST_LENGTH != MD5_DIGEST_LENGTH */ -#endif /* MD5_DIGEST_LENGTH */ - -#ifdef KERNEL -struct ip; -struct ip6_hdr; -struct tcphdr; -struct pf_grev1_hdr; -struct pf_esp_hdr; -#endif /* KERNEL */ - -#define PF_GRE_PPTP_VARIANT 0x01 - -enum { PF_INOUT, PF_IN, PF_OUT }; -enum { PF_PASS, PF_DROP, PF_SCRUB, PF_NOSCRUB, PF_NAT, PF_NONAT, - PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP, - PF_DUMMYNET, PF_NODUMMYNET, PF_NAT64, PF_NONAT64 }; -enum { PF_RULESET_SCRUB, PF_RULESET_FILTER, PF_RULESET_NAT, - PF_RULESET_BINAT, PF_RULESET_RDR, PF_RULESET_DUMMYNET, - PF_RULESET_MAX }; -enum { PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT, - PF_OP_LE, PF_OP_GT, PF_OP_GE, PF_OP_XRG, PF_OP_RRG }; -enum { PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC, PF_DEBUG_NOISY }; -enum { PF_CHANGE_NONE, PF_CHANGE_ADD_HEAD, PF_CHANGE_ADD_TAIL, - PF_CHANGE_ADD_BEFORE, PF_CHANGE_ADD_AFTER, - PF_CHANGE_REMOVE, PF_CHANGE_GET_TICKET }; -enum { PF_GET_NONE, PF_GET_CLR_CNTR }; - -/* - * Note about PFTM_*: real indices into pf_rule.timeout[] come before - * PFTM_MAX, special cases afterwards. See pf_state_expires(). - */ -enum { PFTM_TCP_FIRST_PACKET, PFTM_TCP_OPENING, PFTM_TCP_ESTABLISHED, - PFTM_TCP_CLOSING, PFTM_TCP_FIN_WAIT, PFTM_TCP_CLOSED, - PFTM_UDP_FIRST_PACKET, PFTM_UDP_SINGLE, PFTM_UDP_MULTIPLE, - PFTM_ICMP_FIRST_PACKET, PFTM_ICMP_ERROR_REPLY, - PFTM_GREv1_FIRST_PACKET, PFTM_GREv1_INITIATING, - PFTM_GREv1_ESTABLISHED, PFTM_ESP_FIRST_PACKET, PFTM_ESP_INITIATING, - PFTM_ESP_ESTABLISHED, PFTM_OTHER_FIRST_PACKET, PFTM_OTHER_SINGLE, - PFTM_OTHER_MULTIPLE, PFTM_FRAG, PFTM_INTERVAL, - PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_SRC_NODE, - PFTM_TS_DIFF, PFTM_MAX, PFTM_PURGE, PFTM_UNLINKED }; - -/* PFTM default values */ -#define PFTM_TCP_FIRST_PACKET_VAL 120 /* First TCP packet */ -#define PFTM_TCP_OPENING_VAL 30 /* No response yet */ -#define PFTM_TCP_ESTABLISHED_VAL (24 * 60 * 60) /* Established */ -#define PFTM_TCP_CLOSING_VAL (15 * 60) /* Half closed */ -#define PFTM_TCP_FIN_WAIT_VAL 45 /* Got both FINs */ -#define PFTM_TCP_CLOSED_VAL 90 /* Got a RST */ -#define PFTM_UDP_FIRST_PACKET_VAL 60 /* First UDP packet */ -#define PFTM_UDP_SINGLE_VAL 30 /* Unidirectional */ -#define PFTM_UDP_MULTIPLE_VAL 60 /* Bidirectional */ -#define PFTM_ICMP_FIRST_PACKET_VAL 20 /* First ICMP packet */ -#define PFTM_ICMP_ERROR_REPLY_VAL 10 /* Got error response */ -#define PFTM_GREv1_FIRST_PACKET_VAL 120 -#define PFTM_GREv1_INITIATING_VAL 30 -#define PFTM_GREv1_ESTABLISHED_VAL 1800 -#define PFTM_ESP_FIRST_PACKET_VAL 120 -#define PFTM_ESP_INITIATING_VAL 30 -#define PFTM_ESP_ESTABLISHED_VAL 900 -#define PFTM_OTHER_FIRST_PACKET_VAL 60 /* First packet */ -#define PFTM_OTHER_SINGLE_VAL 30 /* Unidirectional */ -#define PFTM_OTHER_MULTIPLE_VAL 60 /* Bidirectional */ -#define PFTM_FRAG_VAL 30 /* Fragment expire */ -#define PFTM_INTERVAL_VAL 10 /* Expire interval */ -#define PFTM_SRC_NODE_VAL 0 /* Source tracking */ -#define PFTM_TS_DIFF_VAL 30 /* Allowed TS diff */ - -enum { PF_NOPFROUTE, PF_FASTROUTE, PF_ROUTETO, PF_DUPTO, PF_REPLYTO }; -enum { PF_LIMIT_STATES, - PF_LIMIT_APP_STATES, - PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, - PF_LIMIT_TABLES, PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; -#define PF_POOL_IDMASK 0x0f -enum { PF_POOL_NONE, PF_POOL_BITMASK, PF_POOL_RANDOM, - PF_POOL_SRCHASH, PF_POOL_ROUNDROBIN }; -enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL, - PF_ADDR_TABLE, PF_ADDR_RTLABEL, PF_ADDR_URPFFAILED, - PF_ADDR_RANGE }; -#define PF_POOL_TYPEMASK 0x0f -#define PF_POOL_STICKYADDR 0x20 -#define PF_WSCALE_FLAG 0x80 -#define PF_WSCALE_MASK 0x0f - -#define PF_LOG 0x01 -#define PF_LOG_ALL 0x02 -#define PF_LOG_SOCKET_LOOKUP 0x04 - -struct pf_addr { - union { - struct in_addr v4; - struct in6_addr v6; - u_int8_t addr8[16]; - u_int16_t addr16[8]; - u_int32_t addr32[4]; - } pfa; /* 128-bit address */ -#define v4 pfa.v4 -#define v6 pfa.v6 -#define addr8 pfa.addr8 -#define addr16 pfa.addr16 -#define addr32 pfa.addr32 -}; - -#define PF_TABLE_NAME_SIZE 32 - -#define PFI_AFLAG_NETWORK 0x01 -#define PFI_AFLAG_BROADCAST 0x02 -#define PFI_AFLAG_PEER 0x04 -#define PFI_AFLAG_MODEMASK 0x07 -#define PFI_AFLAG_NOALIAS 0x08 - -#ifndef RTLABEL_LEN -#define RTLABEL_LEN 32 -#endif - -struct pf_addr_wrap { - union { - struct { - struct pf_addr addr; - struct pf_addr mask; - } a; - char ifname[IFNAMSIZ]; - char tblname[PF_TABLE_NAME_SIZE]; - char rtlabelname[RTLABEL_LEN]; - u_int32_t rtlabel; - } v; - union { -#ifdef KERNEL - struct pfi_dynaddr *dyn __attribute__((aligned(8))); - struct pfr_ktable *tbl __attribute__((aligned(8))); -#else /* !KERNEL */ - void *dyn __attribute__((aligned(8))); - void *tbl __attribute__((aligned(8))); -#endif /* !KERNEL */ - int dyncnt __attribute__((aligned(8))); - int tblcnt __attribute__((aligned(8))); - } p __attribute__((aligned(8))); - u_int8_t type; /* PF_ADDR_* */ - u_int8_t iflags; /* PFI_AFLAG_* */ -}; - -struct pf_port_range { - u_int16_t port[2]; - u_int8_t op; -}; - -union pf_rule_xport { - struct pf_port_range range; - u_int16_t call_id; - u_int32_t spi; -}; - -#ifdef KERNEL -struct pfi_dynaddr { - TAILQ_ENTRY(pfi_dynaddr) entry; - struct pf_addr pfid_addr4; - struct pf_addr pfid_mask4; - struct pf_addr pfid_addr6; - struct pf_addr pfid_mask6; - struct pfr_ktable *pfid_kt; - struct pfi_kif *pfid_kif; - void *pfid_hook_cookie; - int pfid_net; /* mask or 128 */ - int pfid_acnt4; /* address count IPv4 */ - int pfid_acnt6; /* address count IPv6 */ - sa_family_t pfid_af; /* rule af */ - u_int8_t pfid_iflags; /* PFI_AFLAG_* */ -}; - -/* - * Address manipulation macros - */ - -#if INET -#if !INET6 -#define PF_INET_ONLY -#endif /* ! INET6 */ -#endif /* INET */ - -#if INET6 -#if !INET -#define PF_INET6_ONLY -#endif /* ! INET */ -#endif /* INET6 */ - -#if INET -#if INET6 -#define PF_INET_INET6 -#endif /* INET6 */ -#endif /* INET */ - -#else /* !KERNEL */ - -#define PF_INET_INET6 - -#endif /* !KERNEL */ - -/* Both IPv4 and IPv6 */ -#ifdef PF_INET_INET6 - -#define PF_AEQ(a, b, c) \ - ((c == AF_INET && (a)->addr32[0] == (b)->addr32[0]) || \ - ((a)->addr32[3] == (b)->addr32[3] && \ - (a)->addr32[2] == (b)->addr32[2] && \ - (a)->addr32[1] == (b)->addr32[1] && \ - (a)->addr32[0] == (b)->addr32[0])) \ - -#define PF_ANEQ(a, b, c) \ - ((c == AF_INET && (a)->addr32[0] != (b)->addr32[0]) || \ - ((a)->addr32[3] != (b)->addr32[3] || \ - (a)->addr32[2] != (b)->addr32[2] || \ - (a)->addr32[1] != (b)->addr32[1] || \ - (a)->addr32[0] != (b)->addr32[0])) \ - -#define PF_ALEQ(a, b, c) \ - ((c == AF_INET && (a)->addr32[0] <= (b)->addr32[0]) || \ - ((a)->addr32[3] <= (b)->addr32[3] && \ - (a)->addr32[2] <= (b)->addr32[2] && \ - (a)->addr32[1] <= (b)->addr32[1] && \ - (a)->addr32[0] <= (b)->addr32[0])) \ - -#define PF_AZERO(a, c) \ - ((c == AF_INET && !(a)->addr32[0]) || \ - (!(a)->addr32[0] && !(a)->addr32[1] && \ - !(a)->addr32[2] && !(a)->addr32[3])) \ - -#define PF_MATCHA(n, a, m, b, f) \ - pf_match_addr(n, a, m, b, f) - -#define PF_ACPY(a, b, f) \ - pf_addrcpy(a, b, f) - -#define PF_AINC(a, f) \ - pf_addr_inc(a, f) - -#define PF_POOLMASK(a, b, c, d, f) \ - pf_poolmask(a, b, c, d, f) - -#else - -/* Just IPv6 */ - -#ifdef PF_INET6_ONLY - -#define PF_AEQ(a, b, c) \ - ((a)->addr32[3] == (b)->addr32[3] && \ - (a)->addr32[2] == (b)->addr32[2] && \ - (a)->addr32[1] == (b)->addr32[1] && \ - (a)->addr32[0] == (b)->addr32[0]) \ - -#define PF_ANEQ(a, b, c) \ - ((a)->addr32[3] != (b)->addr32[3] || \ - (a)->addr32[2] != (b)->addr32[2] || \ - (a)->addr32[1] != (b)->addr32[1] || \ - (a)->addr32[0] != (b)->addr32[0]) \ - -#define PF_ALEQ(a, b, c) \ - ((a)->addr32[3] <= (b)->addr32[3] && \ - (a)->addr32[2] <= (b)->addr32[2] && \ - (a)->addr32[1] <= (b)->addr32[1] && \ - (a)->addr32[0] <= (b)->addr32[0]) \ - -#define PF_AZERO(a, c) \ - (!(a)->addr32[0] && \ - !(a)->addr32[1] && \ - !(a)->addr32[2] && \ - !(a)->addr32[3]) \ - -#define PF_MATCHA(n, a, m, b, f) \ - pf_match_addr(n, a, m, b, f) - -#define PF_ACPY(a, b, f) \ - pf_addrcpy(a, b, f) - -#define PF_AINC(a, f) \ - pf_addr_inc(a, f) - -#define PF_POOLMASK(a, b, c, d, f) \ - pf_poolmask(a, b, c, d, f) - -#else - -/* Just IPv4 */ -#ifdef PF_INET_ONLY - -#define PF_AEQ(a, b, c) \ - ((a)->addr32[0] == (b)->addr32[0]) - -#define PF_ANEQ(a, b, c) \ - ((a)->addr32[0] != (b)->addr32[0]) - -#define PF_ALEQ(a, b, c) \ - ((a)->addr32[0] <= (b)->addr32[0]) - -#define PF_AZERO(a, c) \ - (!(a)->addr32[0]) - -#define PF_MATCHA(n, a, m, b, f) \ - pf_match_addr(n, a, m, b, f) - -#define PF_ACPY(a, b, f) \ - (a)->v4.s_addr = (b)->v4.s_addr - -#define PF_AINC(a, f) \ - do { \ - (a)->addr32[0] = htonl(ntohl((a)->addr32[0]) + 1); \ - } while (0) - -#define PF_POOLMASK(a, b, c, d, f) \ - do { \ - (a)->addr32[0] = ((b)->addr32[0] & (c)->addr32[0]) | \ - (((c)->addr32[0] ^ 0xffffffff) & (d)->addr32[0]); \ - } while (0) - -#endif /* PF_INET_ONLY */ -#endif /* PF_INET6_ONLY */ -#endif /* PF_INET_INET6 */ - -#ifdef KERNEL -#define PF_MISMATCHAW(aw, x, af, neg, ifp) \ - ( \ - (((aw)->type == PF_ADDR_NOROUTE && \ - pf_routable((x), (af), NULL)) || \ - (((aw)->type == PF_ADDR_URPFFAILED && (ifp) != NULL && \ - pf_routable((x), (af), (ifp))) || \ - ((aw)->type == PF_ADDR_RTLABEL && \ - !pf_rtlabel_match((x), (af), (aw))) || \ - ((aw)->type == PF_ADDR_TABLE && \ - !pfr_match_addr((aw)->p.tbl, (x), (af))) || \ - ((aw)->type == PF_ADDR_DYNIFTL && \ - !pfi_match_addr((aw)->p.dyn, (x), (af))) || \ - ((aw)->type == PF_ADDR_RANGE && \ - !pf_match_addr_range(&(aw)->v.a.addr, \ - &(aw)->v.a.mask, (x), (af))) || \ - ((aw)->type == PF_ADDR_ADDRMASK && \ - !PF_AZERO(&(aw)->v.a.mask, (af)) && \ - !PF_MATCHA(0, &(aw)->v.a.addr, \ - &(aw)->v.a.mask, (x), (af))))) != \ - (neg) \ - ) -#endif /* KERNEL */ - -struct pf_rule_uid { - uid_t uid[2]; - u_int8_t op; - u_int8_t _pad[3]; -}; - -struct pf_rule_gid { - uid_t gid[2]; - u_int8_t op; - u_int8_t _pad[3]; -}; - -struct pf_rule_addr { - struct pf_addr_wrap addr; - union pf_rule_xport xport; - u_int8_t neg; -}; - -struct pf_pooladdr { - struct pf_addr_wrap addr; - TAILQ_ENTRY(pf_pooladdr) entries; -#if !defined(__LP64__) - u_int32_t _pad[2]; -#endif /* !__LP64__ */ - char ifname[IFNAMSIZ]; -#ifdef KERNEL - struct pfi_kif *kif __attribute__((aligned(8))); -#else /* !KERNEL */ - void *kif __attribute__((aligned(8))); -#endif /* !KERNEL */ -}; - -TAILQ_HEAD(pf_palist, pf_pooladdr); - -struct pf_poolhashkey { - union { - u_int8_t key8[16]; - u_int16_t key16[8]; - u_int32_t key32[4]; - } pfk; /* 128-bit hash key */ -#define key8 pfk.key8 -#define key16 pfk.key16 -#define key32 pfk.key32 -}; - -struct pf_pool { - struct pf_palist list; -#if !defined(__LP64__) - u_int32_t _pad[2]; -#endif /* !__LP64__ */ -#ifdef KERNEL - struct pf_pooladdr *cur __attribute__((aligned(8))); -#else /* !KERNEL */ - void *cur __attribute__((aligned(8))); -#endif /* !KERNEL */ - struct pf_poolhashkey key __attribute__((aligned(8))); - struct pf_addr counter; - int tblidx; - u_int16_t proxy_port[2]; - u_int8_t port_op; - u_int8_t opts; - sa_family_t af; -}; - - -/* A packed Operating System description for fingerprinting */ -typedef u_int32_t pf_osfp_t; -#define PF_OSFP_ANY ((pf_osfp_t)0) -#define PF_OSFP_UNKNOWN ((pf_osfp_t)-1) -#define PF_OSFP_NOMATCH ((pf_osfp_t)-2) - -struct pf_osfp_entry { - SLIST_ENTRY(pf_osfp_entry) fp_entry; -#if !defined(__LP64__) - u_int32_t _pad; -#endif /* !__LP64__ */ - pf_osfp_t fp_os; - int fp_enflags; -#define PF_OSFP_EXPANDED 0x001 /* expanded entry */ -#define PF_OSFP_GENERIC 0x002 /* generic signature */ -#define PF_OSFP_NODETAIL 0x004 /* no p0f details */ -#define PF_OSFP_LEN 32 - char fp_class_nm[PF_OSFP_LEN]; - char fp_version_nm[PF_OSFP_LEN]; - char fp_subtype_nm[PF_OSFP_LEN]; -}; -#define PF_OSFP_ENTRY_EQ(a, b) \ - ((a)->fp_os == (b)->fp_os && \ - memcmp((a)->fp_class_nm, (b)->fp_class_nm, PF_OSFP_LEN) == 0 && \ - memcmp((a)->fp_version_nm, (b)->fp_version_nm, PF_OSFP_LEN) == 0 && \ - memcmp((a)->fp_subtype_nm, (b)->fp_subtype_nm, PF_OSFP_LEN) == 0) - -/* handle pf_osfp_t packing */ -#define _FP_RESERVED_BIT 1 /* For the special negative #defines */ -#define _FP_UNUSED_BITS 1 -#define _FP_CLASS_BITS 10 /* OS Class (Windows, Linux) */ -#define _FP_VERSION_BITS 10 /* OS version (95, 98, NT, 2.4.54, 3.2) */ -#define _FP_SUBTYPE_BITS 10 /* patch level (NT SP4, SP3, ECN patch) */ -#define PF_OSFP_UNPACK(osfp, class, version, subtype) do { \ - (class) = ((osfp) >> (_FP_VERSION_BITS+_FP_SUBTYPE_BITS)) & \ - ((1 << _FP_CLASS_BITS) - 1); \ - (version) = ((osfp) >> _FP_SUBTYPE_BITS) & \ - ((1 << _FP_VERSION_BITS) - 1);\ - (subtype) = (osfp) & ((1 << _FP_SUBTYPE_BITS) - 1); \ -} while (0) -#define PF_OSFP_PACK(osfp, class, version, subtype) do { \ - (osfp) = ((class) & ((1 << _FP_CLASS_BITS) - 1)) << (_FP_VERSION_BITS \ - + _FP_SUBTYPE_BITS); \ - (osfp) |= ((version) & ((1 << _FP_VERSION_BITS) - 1)) << \ - _FP_SUBTYPE_BITS; \ - (osfp) |= (subtype) & ((1 << _FP_SUBTYPE_BITS) - 1); \ -} while (0) - -/* the fingerprint of an OSes TCP SYN packet */ -typedef u_int64_t pf_tcpopts_t; -struct pf_os_fingerprint { - SLIST_HEAD(pf_osfp_enlist, pf_osfp_entry) fp_oses; /* list of matches */ - pf_tcpopts_t fp_tcpopts; /* packed TCP options */ - u_int16_t fp_wsize; /* TCP window size */ - u_int16_t fp_psize; /* ip->ip_len */ - u_int16_t fp_mss; /* TCP MSS */ - u_int16_t fp_flags; -#define PF_OSFP_WSIZE_MOD 0x0001 /* Window modulus */ -#define PF_OSFP_WSIZE_DC 0x0002 /* Window don't care */ -#define PF_OSFP_WSIZE_MSS 0x0004 /* Window multiple of MSS */ -#define PF_OSFP_WSIZE_MTU 0x0008 /* Window multiple of MTU */ -#define PF_OSFP_PSIZE_MOD 0x0010 /* packet size modulus */ -#define PF_OSFP_PSIZE_DC 0x0020 /* packet size don't care */ -#define PF_OSFP_WSCALE 0x0040 /* TCP window scaling */ -#define PF_OSFP_WSCALE_MOD 0x0080 /* TCP window scale modulus */ -#define PF_OSFP_WSCALE_DC 0x0100 /* TCP window scale dont-care */ -#define PF_OSFP_MSS 0x0200 /* TCP MSS */ -#define PF_OSFP_MSS_MOD 0x0400 /* TCP MSS modulus */ -#define PF_OSFP_MSS_DC 0x0800 /* TCP MSS dont-care */ -#define PF_OSFP_DF 0x1000 /* IPv4 don't fragment bit */ -#define PF_OSFP_TS0 0x2000 /* Zero timestamp */ -#define PF_OSFP_INET6 0x4000 /* IPv6 */ - u_int8_t fp_optcnt; /* TCP option count */ - u_int8_t fp_wscale; /* TCP window scaling */ - u_int8_t fp_ttl; /* IPv4 TTL */ -#define PF_OSFP_MAXTTL_OFFSET 40 -/* TCP options packing */ -#define PF_OSFP_TCPOPT_NOP 0x0 /* TCP NOP option */ -#define PF_OSFP_TCPOPT_WSCALE 0x1 /* TCP window scaling option */ -#define PF_OSFP_TCPOPT_MSS 0x2 /* TCP max segment size opt */ -#define PF_OSFP_TCPOPT_SACK 0x3 /* TCP SACK OK option */ -#define PF_OSFP_TCPOPT_TS 0x4 /* TCP timestamp option */ -#define PF_OSFP_TCPOPT_BITS 3 /* bits used by each option */ -#define PF_OSFP_MAX_OPTS \ - (sizeof(((struct pf_os_fingerprint *)0)->fp_tcpopts) * 8) \ - / PF_OSFP_TCPOPT_BITS - - SLIST_ENTRY(pf_os_fingerprint) fp_next; -}; - -struct pf_osfp_ioctl { - struct pf_osfp_entry fp_os; - pf_tcpopts_t fp_tcpopts; /* packed TCP options */ - u_int16_t fp_wsize; /* TCP window size */ - u_int16_t fp_psize; /* ip->ip_len */ - u_int16_t fp_mss; /* TCP MSS */ - u_int16_t fp_flags; - u_int8_t fp_optcnt; /* TCP option count */ - u_int8_t fp_wscale; /* TCP window scaling */ - u_int8_t fp_ttl; /* IPv4 TTL */ - - int fp_getnum; /* DIOCOSFPGET number */ -}; - - -union pf_rule_ptr { - struct pf_rule *ptr __attribute__((aligned(8))); - u_int32_t nr __attribute__((aligned(8))); -} __attribute__((aligned(8))); - -#define PF_ANCHOR_NAME_SIZE 64 - -struct pf_rule { - struct pf_rule_addr src; - struct pf_rule_addr dst; -#define PF_SKIP_IFP 0 -#define PF_SKIP_DIR 1 -#define PF_SKIP_AF 2 -#define PF_SKIP_PROTO 3 -#define PF_SKIP_SRC_ADDR 4 -#define PF_SKIP_SRC_PORT 5 -#define PF_SKIP_DST_ADDR 6 -#define PF_SKIP_DST_PORT 7 -#define PF_SKIP_COUNT 8 - union pf_rule_ptr skip[PF_SKIP_COUNT]; -#define PF_RULE_LABEL_SIZE 64 - char label[PF_RULE_LABEL_SIZE]; -#define PF_QNAME_SIZE 64 - char ifname[IFNAMSIZ]; - char qname[PF_QNAME_SIZE]; - char pqname[PF_QNAME_SIZE]; -#define PF_TAG_NAME_SIZE 64 - char tagname[PF_TAG_NAME_SIZE]; - char match_tagname[PF_TAG_NAME_SIZE]; - - char overload_tblname[PF_TABLE_NAME_SIZE]; - - TAILQ_ENTRY(pf_rule) entries; -#if !defined(__LP64__) - u_int32_t _pad[2]; -#endif /* !__LP64__ */ - struct pf_pool rpool; - - u_int64_t evaluations; - u_int64_t packets[2]; - u_int64_t bytes[2]; - - u_int64_t ticket; -#define PF_OWNER_NAME_SIZE 64 - char owner[PF_OWNER_NAME_SIZE]; - u_int32_t priority; - -#ifdef KERNEL - struct pfi_kif *kif __attribute__((aligned(8))); -#else /* !KERNEL */ - void *kif __attribute__((aligned(8))); -#endif /* !KERNEL */ - struct pf_anchor *anchor __attribute__((aligned(8))); -#ifdef KERNEL - struct pfr_ktable *overload_tbl __attribute__((aligned(8))); -#else /* !KERNEL */ - void *overload_tbl __attribute__((aligned(8))); -#endif /* !KERNEL */ - - pf_osfp_t os_fingerprint __attribute__((aligned(8))); - - unsigned int rtableid; - u_int32_t timeout[PFTM_MAX]; - u_int32_t states; - u_int32_t max_states; - u_int32_t src_nodes; - u_int32_t max_src_nodes; - u_int32_t max_src_states; - u_int32_t max_src_conn; - struct { - u_int32_t limit; - u_int32_t seconds; - } max_src_conn_rate; - u_int32_t qid; - u_int32_t pqid; - u_int32_t rt_listid; - u_int32_t nr; - u_int32_t prob; - uid_t cuid; - pid_t cpid; - - u_int16_t return_icmp; - u_int16_t return_icmp6; - u_int16_t max_mss; - u_int16_t tag; - u_int16_t match_tag; - - struct pf_rule_uid uid; - struct pf_rule_gid gid; - - u_int32_t rule_flag; - u_int8_t action; - u_int8_t direction; - u_int8_t log; - u_int8_t logif; - u_int8_t quick; - u_int8_t ifnot; - u_int8_t match_tag_not; - u_int8_t natpass; - -#define PF_STATE_NORMAL 0x1 -#define PF_STATE_MODULATE 0x2 -#define PF_STATE_SYNPROXY 0x3 - u_int8_t keep_state; - sa_family_t af; - u_int8_t proto; - u_int8_t type; - u_int8_t code; - u_int8_t flags; - u_int8_t flagset; - u_int8_t min_ttl; - u_int8_t allow_opts; - u_int8_t rt; - u_int8_t return_ttl; - -/* service class categories */ -#define SCIDX_MASK 0x0f -#define SC_BE 0x10 -#define SC_BK_SYS 0x11 -#define SC_BK 0x12 -#define SC_RD 0x13 -#define SC_OAM 0x14 -#define SC_AV 0x15 -#define SC_RV 0x16 -#define SC_VI 0x17 -#define SC_VO 0x18 -#define SC_CTL 0x19 - -/* diffserve code points */ -#define DSCP_MASK 0xfc -#define DSCP_CUMASK 0x03 -#define DSCP_EF 0xb8 -#define DSCP_AF11 0x28 -#define DSCP_AF12 0x30 -#define DSCP_AF13 0x38 -#define DSCP_AF21 0x48 -#define DSCP_AF22 0x50 -#define DSCP_AF23 0x58 -#define DSCP_AF31 0x68 -#define DSCP_AF32 0x70 -#define DSCP_AF33 0x78 -#define DSCP_AF41 0x88 -#define DSCP_AF42 0x90 -#define DSCP_AF43 0x98 -#define AF_CLASSMASK 0xe0 -#define AF_DROPPRECMASK 0x18 - u_int8_t tos; - u_int8_t anchor_relative; - u_int8_t anchor_wildcard; - -#define PF_FLUSH 0x01 -#define PF_FLUSH_GLOBAL 0x02 - u_int8_t flush; - - u_int8_t proto_variant; - u_int8_t extfilter; /* Filter mode [PF_EXTFILTER_xxx] */ - u_int8_t extmap; /* Mapping mode [PF_EXTMAP_xxx] */ - u_int32_t dnpipe; - u_int32_t dntype; -}; - -/* pf device identifiers */ -#define PFDEV_PF 0 -#define PFDEV_PFM 1 -#define PFDEV_MAX 2 - -/* rule flags */ -#define PFRULE_DROP 0x0000 -#define PFRULE_RETURNRST 0x0001 -#define PFRULE_FRAGMENT 0x0002 -#define PFRULE_RETURNICMP 0x0004 -#define PFRULE_RETURN 0x0008 -#define PFRULE_NOSYNC 0x0010 -#define PFRULE_SRCTRACK 0x0020 /* track source states */ -#define PFRULE_RULESRCTRACK 0x0040 /* per rule */ - -/* scrub flags */ -#define PFRULE_NODF 0x0100 -#define PFRULE_FRAGCROP 0x0200 /* non-buffering frag cache */ -#define PFRULE_FRAGDROP 0x0400 /* drop funny fragments */ -#define PFRULE_RANDOMID 0x0800 -#define PFRULE_REASSEMBLE_TCP 0x1000 - -/* rule flags for TOS/DSCP/service class differentiation */ -#define PFRULE_TOS 0x2000 -#define PFRULE_DSCP 0x4000 -#define PFRULE_SC 0x8000 - -/* rule flags again */ -#define PFRULE_IFBOUND 0x00010000 /* if-bound */ -#define PFRULE_PFM 0x00020000 /* created by pfm device */ - -#define PFSTATE_HIWAT 10000 /* default state table size */ -#define PFSTATE_ADAPT_START 6000 /* default adaptive timeout start */ -#define PFSTATE_ADAPT_END 12000 /* default adaptive timeout end */ - -#define PFAPPSTATE_HIWAT 10000 /* default same as state table */ - -enum pf_extmap { - PF_EXTMAP_APD = 1, /* Address-port-dependent mapping */ - PF_EXTMAP_AD, /* Address-dependent mapping */ - PF_EXTMAP_EI /* Endpoint-independent mapping */ -}; - -enum pf_extfilter { - PF_EXTFILTER_APD = 1, /* Address-port-dependent filtering */ - PF_EXTFILTER_AD, /* Address-dependent filtering */ - PF_EXTFILTER_EI /* Endpoint-independent filtering */ -}; - -struct pf_threshold { - u_int32_t limit; -#define PF_THRESHOLD_MULT 1000 -#define PF_THRESHOLD_MAX 0xffffffff / PF_THRESHOLD_MULT - u_int32_t seconds; - u_int32_t count; - u_int32_t last; -}; - -struct pf_src_node { - RB_ENTRY(pf_src_node) entry; - struct pf_addr addr; - struct pf_addr raddr; - union pf_rule_ptr rule; -#ifdef KERNEL - struct pfi_kif *kif; -#else /* !KERNEL */ - void *kif; -#endif /* !KERNEL */ - u_int64_t bytes[2]; - u_int64_t packets[2]; - u_int32_t states; - u_int32_t conn; - struct pf_threshold conn_rate; - u_int64_t creation; - u_int64_t expire; - sa_family_t af; - u_int8_t ruletype; -}; - -#define PFSNODE_HIWAT 10000 /* default source node table size */ - -#ifdef KERNEL -struct pf_state_scrub { - struct timeval pfss_last; /* time received last packet */ - u_int32_t pfss_tsecr; /* last echoed timestamp */ - u_int32_t pfss_tsval; /* largest timestamp */ - u_int32_t pfss_tsval0; /* original timestamp */ - u_int16_t pfss_flags; -#define PFSS_TIMESTAMP 0x0001 /* modulate timestamp */ -#define PFSS_PAWS 0x0010 /* stricter PAWS checks */ -#define PFSS_PAWS_IDLED 0x0020 /* was idle too long. no PAWS */ -#define PFSS_DATA_TS 0x0040 /* timestamp on data packets */ -#define PFSS_DATA_NOTS 0x0080 /* no timestamp on data packets */ - u_int8_t pfss_ttl; /* stashed TTL */ - u_int8_t pad; - u_int32_t pfss_ts_mod; /* timestamp modulation */ -}; -#endif /* KERNEL */ - -union pf_state_xport { - u_int16_t port; - u_int16_t call_id; - u_int32_t spi; -}; - -struct pf_state_host { - struct pf_addr addr; - union pf_state_xport xport; -}; - -#ifdef KERNEL -struct pf_state_peer { - u_int32_t seqlo; /* Max sequence number sent */ - u_int32_t seqhi; /* Max the other end ACKd + win */ - u_int32_t seqdiff; /* Sequence number modulator */ - u_int16_t max_win; /* largest window (pre scaling) */ - u_int8_t state; /* active state level */ - u_int8_t wscale; /* window scaling factor */ - u_int16_t mss; /* Maximum segment size option */ - u_int8_t tcp_est; /* Did we reach TCPS_ESTABLISHED */ - struct pf_state_scrub *scrub; /* state is scrubbed */ - u_int8_t pad[3]; -}; - -TAILQ_HEAD(pf_state_queue, pf_state); - -struct pf_state; -struct pf_pdesc; -struct pf_app_state; - -typedef void (*pf_app_handler)(struct pf_state *, int, int, struct pf_pdesc *, - struct pfi_kif *); - -typedef int (*pf_app_compare)(struct pf_app_state *, struct pf_app_state *); - -struct pf_pptp_state { - struct pf_state *grev1_state; -}; - -struct pf_grev1_state { - struct pf_state *pptp_state; -}; - -struct pf_ike_state { - u_int64_t cookie; -}; - -struct pf_app_state { - pf_app_handler handler; - pf_app_compare compare_lan_ext; - pf_app_compare compare_ext_gwy; - union { - struct pf_pptp_state pptp; - struct pf_grev1_state grev1; - struct pf_ike_state ike; - } u; -}; - -/* keep synced with struct pf_state, used in RB_FIND */ -struct pf_state_key_cmp { - struct pf_state_host lan; - struct pf_state_host gwy; - struct pf_state_host ext_lan; - struct pf_state_host ext_gwy; - sa_family_t af_lan; - sa_family_t af_gwy; - u_int8_t proto; - u_int8_t direction; - u_int8_t proto_variant; - struct pf_app_state *app_state; -}; - -TAILQ_HEAD(pf_statelist, pf_state); - -struct pf_state_key { - struct pf_state_host lan; - struct pf_state_host gwy; - struct pf_state_host ext_lan; - struct pf_state_host ext_gwy; - sa_family_t af_lan; - sa_family_t af_gwy; - u_int8_t proto; - u_int8_t direction; - u_int8_t proto_variant; - struct pf_app_state *app_state; - u_int32_t flowsrc; - u_int32_t flowhash; - - RB_ENTRY(pf_state_key) entry_lan_ext; - RB_ENTRY(pf_state_key) entry_ext_gwy; - struct pf_statelist states; - u_int32_t refcnt; -}; - - -/* keep synced with struct pf_state, used in RB_FIND */ -struct pf_state_cmp { - u_int64_t id; - u_int32_t creatorid; - u_int32_t pad; -}; - -/* flowhash key (12-bytes multiple for performance) */ -struct pf_flowhash_key { - struct pf_state_host ap1; /* address+port blob 1 */ - struct pf_state_host ap2; /* address+port blob 2 */ - u_int32_t af; - u_int32_t proto; -}; -#endif /* KERNEL */ - -struct hook_desc; -TAILQ_HEAD(hook_desc_head, hook_desc); - -#ifdef KERNEL -struct pf_state { - u_int64_t id; - u_int32_t creatorid; - u_int32_t pad; - - TAILQ_ENTRY(pf_state) entry_list; - TAILQ_ENTRY(pf_state) next; - RB_ENTRY(pf_state) entry_id; - struct pf_state_peer src; - struct pf_state_peer dst; - union pf_rule_ptr rule; - union pf_rule_ptr anchor; - union pf_rule_ptr nat_rule; - struct pf_addr rt_addr; - struct hook_desc_head unlink_hooks; - struct pf_state_key *state_key; - struct pfi_kif *kif; - struct pfi_kif *rt_kif; - struct pf_src_node *src_node; - struct pf_src_node *nat_src_node; - u_int64_t packets[2]; - u_int64_t bytes[2]; - u_int64_t creation; - u_int64_t expire; - u_int64_t pfsync_time; - u_int16_t tag; - u_int8_t log; - u_int8_t allow_opts; - u_int8_t timeout; - u_int8_t sync_flags; -}; -#endif /* KERNEL */ - -#define PFSTATE_NOSYNC 0x01 -#define PFSTATE_FROMSYNC 0x02 -#define PFSTATE_STALE 0x04 - -#define __packed __attribute__((__packed__)) - -/* - * Unified state structures for pulling states out of the kernel - * used by pfsync(4) and the pf(4) ioctl. - */ -struct pfsync_state_scrub { - u_int16_t pfss_flags; - u_int8_t pfss_ttl; /* stashed TTL */ -#define PFSYNC_SCRUB_FLAG_VALID 0x01 - u_int8_t scrub_flag; - u_int32_t pfss_ts_mod; /* timestamp modulation */ -} __packed; - -struct pfsync_state_host { - struct pf_addr addr; - union pf_state_xport xport; - u_int16_t pad[2]; -} __packed; - -struct pfsync_state_peer { - struct pfsync_state_scrub scrub; /* state is scrubbed */ - u_int32_t seqlo; /* Max sequence number sent */ - u_int32_t seqhi; /* Max the other end ACKd + win */ - u_int32_t seqdiff; /* Sequence number modulator */ - u_int16_t max_win; /* largest window (pre scaling) */ - u_int16_t mss; /* Maximum segment size option */ - u_int8_t state; /* active state level */ - u_int8_t wscale; /* window scaling factor */ - u_int8_t pad[6]; -} __packed; - -struct pfsync_state { - u_int32_t id[2]; - char ifname[IFNAMSIZ]; - struct pfsync_state_host lan; - struct pfsync_state_host gwy; - struct pfsync_state_host ext_lan; - struct pfsync_state_host ext_gwy; - struct pfsync_state_peer src; - struct pfsync_state_peer dst; - struct pf_addr rt_addr; - struct hook_desc_head unlink_hooks; -#if !defined(__LP64__) - u_int32_t _pad[2]; -#endif /* !__LP64__ */ - u_int32_t rule; - u_int32_t anchor; - u_int32_t nat_rule; - u_int64_t creation; - u_int64_t expire; - u_int32_t packets[2][2]; - u_int32_t bytes[2][2]; - u_int32_t creatorid; - u_int16_t tag; - sa_family_t af_lan; - sa_family_t af_gwy; - u_int8_t proto; - u_int8_t direction; - u_int8_t log; - u_int8_t allow_opts; - u_int8_t timeout; - u_int8_t sync_flags; - u_int8_t updates; - u_int8_t proto_variant; - u_int8_t __pad; - u_int32_t flowhash; -} __packed; - -#define PFSYNC_FLAG_COMPRESS 0x01 -#define PFSYNC_FLAG_STALE 0x02 -#define PFSYNC_FLAG_SRCNODE 0x04 -#define PFSYNC_FLAG_NATSRCNODE 0x08 - -#ifdef KERNEL -/* for copies to/from userland via pf_ioctl() */ -#define pf_state_peer_to_pfsync(s, d) do { \ - (d)->seqlo = (s)->seqlo; \ - (d)->seqhi = (s)->seqhi; \ - (d)->seqdiff = (s)->seqdiff; \ - (d)->max_win = (s)->max_win; \ - (d)->mss = (s)->mss; \ - (d)->state = (s)->state; \ - (d)->wscale = (s)->wscale; \ - if ((s)->scrub) { \ - (d)->scrub.pfss_flags = \ - (s)->scrub->pfss_flags & PFSS_TIMESTAMP; \ - (d)->scrub.pfss_ttl = (s)->scrub->pfss_ttl; \ - (d)->scrub.pfss_ts_mod = (s)->scrub->pfss_ts_mod; \ - (d)->scrub.scrub_flag = PFSYNC_SCRUB_FLAG_VALID; \ - } \ -} while (0) - -#define pf_state_peer_from_pfsync(s, d) do { \ - (d)->seqlo = (s)->seqlo; \ - (d)->seqhi = (s)->seqhi; \ - (d)->seqdiff = (s)->seqdiff; \ - (d)->max_win = (s)->max_win; \ - (d)->mss = ntohs((s)->mss); \ - (d)->state = (s)->state; \ - (d)->wscale = (s)->wscale; \ - if ((s)->scrub.scrub_flag == PFSYNC_SCRUB_FLAG_VALID && \ - (d)->scrub != NULL) { \ - (d)->scrub->pfss_flags = \ - ntohs((s)->scrub.pfss_flags) & PFSS_TIMESTAMP; \ - (d)->scrub->pfss_ttl = (s)->scrub.pfss_ttl; \ - (d)->scrub->pfss_ts_mod = (s)->scrub.pfss_ts_mod; \ - } \ -} while (0) -#endif /* KERNEL */ - -#define pf_state_counter_to_pfsync(s, d) do { \ - d[0] = (s>>32)&0xffffffff; \ - d[1] = s&0xffffffff; \ -} while (0) - -#define pf_state_counter_from_pfsync(s) \ - (((u_int64_t)(s[0])<<32) | (u_int64_t)(s[1])) - - - -TAILQ_HEAD(pf_rulequeue, pf_rule); - -struct pf_anchor; - -struct pf_ruleset { - struct { - struct pf_rulequeue queues[2]; - struct { - struct pf_rulequeue *ptr; - struct pf_rule **ptr_array; - u_int32_t rcount; - u_int32_t ticket; - int open; - } active, inactive; - } rules[PF_RULESET_MAX]; - struct pf_anchor *anchor; - u_int32_t tticket; - int tables; - int topen; -}; - -RB_HEAD(pf_anchor_global, pf_anchor); -RB_HEAD(pf_anchor_node, pf_anchor); -struct pf_anchor { - RB_ENTRY(pf_anchor) entry_global; - RB_ENTRY(pf_anchor) entry_node; - struct pf_anchor *parent; - struct pf_anchor_node children; - char name[PF_ANCHOR_NAME_SIZE]; - char path[MAXPATHLEN]; - struct pf_ruleset ruleset; - int refcnt; /* anchor rules */ - int match; - char owner[PF_OWNER_NAME_SIZE]; -}; -#ifdef KERNEL -RB_PROTOTYPE_SC(__private_extern__, pf_anchor_global, pf_anchor, entry_global, - pf_anchor_compare); -RB_PROTOTYPE_SC(__private_extern__, pf_anchor_node, pf_anchor, entry_node, - pf_anchor_compare); -#else /* !KERNEL */ -RB_PROTOTYPE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); -RB_PROTOTYPE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); -#endif /* !KERNEL */ - -#define PF_RESERVED_ANCHOR "_pf" - -#define PFR_TFLAG_PERSIST 0x00000001 -#define PFR_TFLAG_CONST 0x00000002 -#define PFR_TFLAG_ACTIVE 0x00000004 -#define PFR_TFLAG_INACTIVE 0x00000008 -#define PFR_TFLAG_REFERENCED 0x00000010 -#define PFR_TFLAG_REFDANCHOR 0x00000020 -#define PFR_TFLAG_USRMASK 0x00000003 -#define PFR_TFLAG_SETMASK 0x0000003C -#define PFR_TFLAG_ALLMASK 0x0000003F - -struct pfr_table { - char pfrt_anchor[MAXPATHLEN]; - char pfrt_name[PF_TABLE_NAME_SIZE]; - u_int32_t pfrt_flags; - u_int8_t pfrt_fback; -}; - -enum { PFR_FB_NONE, PFR_FB_MATCH, PFR_FB_ADDED, PFR_FB_DELETED, - PFR_FB_CHANGED, PFR_FB_CLEARED, PFR_FB_DUPLICATE, - PFR_FB_NOTMATCH, PFR_FB_CONFLICT, PFR_FB_MAX }; - -struct pfr_addr { - union { - struct in_addr _pfra_ip4addr; - struct in6_addr _pfra_ip6addr; - } pfra_u; - u_int8_t pfra_af; - u_int8_t pfra_net; - u_int8_t pfra_not; - u_int8_t pfra_fback; -}; -#define pfra_ip4addr pfra_u._pfra_ip4addr -#define pfra_ip6addr pfra_u._pfra_ip6addr - -enum { PFR_DIR_IN, PFR_DIR_OUT, PFR_DIR_MAX }; -enum { PFR_OP_BLOCK, PFR_OP_PASS, PFR_OP_ADDR_MAX, PFR_OP_TABLE_MAX }; -#define PFR_OP_XPASS PFR_OP_ADDR_MAX - -struct pfr_astats { - struct pfr_addr pfras_a; -#if !defined(__LP64__) - u_int32_t _pad; -#endif /* !__LP64__ */ - u_int64_t pfras_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; - u_int64_t pfras_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; - u_int64_t pfras_tzero; -}; - -enum { PFR_REFCNT_RULE, PFR_REFCNT_ANCHOR, PFR_REFCNT_MAX }; - -struct pfr_tstats { - struct pfr_table pfrts_t; - u_int64_t pfrts_packets[PFR_DIR_MAX][PFR_OP_TABLE_MAX]; - u_int64_t pfrts_bytes[PFR_DIR_MAX][PFR_OP_TABLE_MAX]; - u_int64_t pfrts_match; - u_int64_t pfrts_nomatch; - u_int64_t pfrts_tzero; - int pfrts_cnt; - int pfrts_refcnt[PFR_REFCNT_MAX]; -#if !defined(__LP64__) - u_int32_t _pad; -#endif /* !__LP64__ */ -}; -#define pfrts_name pfrts_t.pfrt_name -#define pfrts_flags pfrts_t.pfrt_flags - -#ifdef KERNEL -SLIST_HEAD(pfr_kentryworkq, pfr_kentry); -struct pfr_kentry { - struct radix_node pfrke_node[2]; - union sockaddr_union pfrke_sa; - u_int64_t pfrke_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; - u_int64_t pfrke_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; - SLIST_ENTRY(pfr_kentry) pfrke_workq; - u_int64_t pfrke_tzero; - u_int8_t pfrke_af; - u_int8_t pfrke_net; - u_int8_t pfrke_not; - u_int8_t pfrke_mark; - u_int8_t pfrke_intrpool; -}; - -SLIST_HEAD(pfr_ktableworkq, pfr_ktable); -RB_HEAD(pfr_ktablehead, pfr_ktable); -struct pfr_ktable { - struct pfr_tstats pfrkt_ts; - RB_ENTRY(pfr_ktable) pfrkt_tree; - SLIST_ENTRY(pfr_ktable) pfrkt_workq; - struct radix_node_head *pfrkt_ip4; - struct radix_node_head *pfrkt_ip6; - struct pfr_ktable *pfrkt_shadow; - struct pfr_ktable *pfrkt_root; - struct pf_ruleset *pfrkt_rs; - u_int64_t pfrkt_larg; - u_int32_t pfrkt_nflags; -}; -#define pfrkt_t pfrkt_ts.pfrts_t -#define pfrkt_name pfrkt_t.pfrt_name -#define pfrkt_anchor pfrkt_t.pfrt_anchor -#define pfrkt_ruleset pfrkt_t.pfrt_ruleset -#define pfrkt_flags pfrkt_t.pfrt_flags -#define pfrkt_cnt pfrkt_ts.pfrts_cnt -#define pfrkt_refcnt pfrkt_ts.pfrts_refcnt -#define pfrkt_packets pfrkt_ts.pfrts_packets -#define pfrkt_bytes pfrkt_ts.pfrts_bytes -#define pfrkt_match pfrkt_ts.pfrts_match -#define pfrkt_nomatch pfrkt_ts.pfrts_nomatch -#define pfrkt_tzero pfrkt_ts.pfrts_tzero - -RB_HEAD(pf_state_tree_lan_ext, pf_state_key); -RB_PROTOTYPE_SC(__private_extern__, pf_state_tree_lan_ext, pf_state_key, - entry_lan_ext, pf_state_compare_lan_ext); - -RB_HEAD(pf_state_tree_ext_gwy, pf_state_key); -RB_PROTOTYPE_SC(__private_extern__, pf_state_tree_ext_gwy, pf_state_key, - entry_ext_gwy, pf_state_compare_ext_gwy); - -RB_HEAD(pfi_ifhead, pfi_kif); - -/* state tables */ -extern struct pf_state_tree_lan_ext pf_statetbl_lan_ext; -extern struct pf_state_tree_ext_gwy pf_statetbl_ext_gwy; - -/* keep synced with pfi_kif, used in RB_FIND */ -struct pfi_kif_cmp { - char pfik_name[IFNAMSIZ]; -}; - -struct pfi_kif { - char pfik_name[IFNAMSIZ]; - RB_ENTRY(pfi_kif) pfik_tree; - u_int64_t pfik_packets[2][2][2]; - u_int64_t pfik_bytes[2][2][2]; - u_int64_t pfik_tzero; - int pfik_flags; - void *pfik_ah_cookie; - struct ifnet *pfik_ifp; - int pfik_states; - int pfik_rules; - TAILQ_HEAD(, pfi_dynaddr) pfik_dynaddrs; -}; - -enum pfi_kif_refs { - PFI_KIF_REF_NONE, - PFI_KIF_REF_STATE, - PFI_KIF_REF_RULE -}; - -struct pfi_uif { -#else /* !KERNEL */ -struct pfi_kif { -#endif /* !KERNEL */ - char pfik_name[IFNAMSIZ]; - u_int64_t pfik_packets[2][2][2]; - u_int64_t pfik_bytes[2][2][2]; - u_int64_t pfik_tzero; - int pfik_flags; - int pfik_states; - int pfik_rules; -#if !defined(__LP64__) - u_int32_t _pad; -#endif /* !__LP64__ */ -}; - -#define PFI_IFLAG_SKIP 0x0100 /* skip filtering on interface */ - -#ifdef KERNEL -struct pf_pdesc { - struct { - int done; - uid_t uid; - gid_t gid; - pid_t pid; - } lookup; - u_int64_t tot_len; /* Make Mickey money */ - union { - struct tcphdr *tcp; - struct udphdr *udp; - struct icmp *icmp; -#if INET6 - struct icmp6_hdr *icmp6; -#endif /* INET6 */ - struct pf_grev1_hdr *grev1; - struct pf_esp_hdr *esp; - void *any; - } hdr; - - /* XXX TODO: Change baddr and naddr to *saddr */ - struct pf_addr baddr; /* src address before translation */ - struct pf_addr bdaddr; /* dst address before translation */ - struct pf_addr naddr; /* src address after translation */ - struct pf_addr ndaddr; /* dst address after translation */ - struct pf_rule *nat_rule; /* nat/rdr rule applied to packet */ - struct pf_addr *src; - struct pf_addr *dst; - struct ether_header - *eh; - struct mbuf *mp; - int lmw; /* lazy writable offset */ - struct pf_mtag *pf_mtag; - u_int16_t *ip_sum; - u_int32_t off; /* protocol header offset */ - u_int32_t hdrlen; /* protocol header length */ - u_int32_t p_len; /* total length of payload */ - u_int16_t flags; /* Let SCRUB trigger behavior in */ - /* state code. Easier than tags */ -#define PFDESC_TCP_NORM 0x0001 /* TCP shall be statefully scrubbed */ -#define PFDESC_IP_REAS 0x0002 /* IP frags would've been reassembled */ -#define PFDESC_IP_FRAG 0x0004 /* This is a fragment */ - sa_family_t af; - sa_family_t naf; /* address family after translation */ - u_int8_t proto; - u_int8_t tos; - u_int8_t ttl; - u_int8_t proto_variant; - mbuf_svc_class_t sc; /* mbuf service class (MBUF_SVC) */ - u_int32_t pktflags; /* mbuf packet flags (PKTF) */ - u_int32_t flowsrc; /* flow source (FLOWSRC) */ - u_int32_t flowhash; /* flow hash to identify the sender */ -}; -#endif /* KERNEL */ - -/* flags for RDR options */ -#define PF_DPORT_RANGE 0x01 /* Dest port uses range */ -#define PF_RPORT_RANGE 0x02 /* RDR'ed port uses range */ - -/* Reasons code for passing/dropping a packet */ -#define PFRES_MATCH 0 /* Explicit match of a rule */ -#define PFRES_BADOFF 1 /* Bad offset for pull_hdr */ -#define PFRES_FRAG 2 /* Dropping following fragment */ -#define PFRES_SHORT 3 /* Dropping short packet */ -#define PFRES_NORM 4 /* Dropping by normalizer */ -#define PFRES_MEMORY 5 /* Dropped due to lacking mem */ -#define PFRES_TS 6 /* Bad TCP Timestamp (RFC1323) */ -#define PFRES_CONGEST 7 /* Congestion (of ipintrq) */ -#define PFRES_IPOPTIONS 8 /* IP option */ -#define PFRES_PROTCKSUM 9 /* Protocol checksum invalid */ -#define PFRES_BADSTATE 10 /* State mismatch */ -#define PFRES_STATEINS 11 /* State insertion failure */ -#define PFRES_MAXSTATES 12 /* State limit */ -#define PFRES_SRCLIMIT 13 /* Source node/conn limit */ -#define PFRES_SYNPROXY 14 /* SYN proxy */ -#define PFRES_DUMMYNET 15 /* Dummynet */ -#define PFRES_MAX 16 /* total+1 */ - -#define PFRES_NAMES { \ - "match", \ - "bad-offset", \ - "fragment", \ - "short", \ - "normalize", \ - "memory", \ - "bad-timestamp", \ - "congestion", \ - "ip-option", \ - "proto-cksum", \ - "state-mismatch", \ - "state-insert", \ - "state-limit", \ - "src-limit", \ - "synproxy", \ - "dummynet", \ - NULL \ -} - -/* Counters for other things we want to keep track of */ -#define LCNT_STATES 0 /* states */ -#define LCNT_SRCSTATES 1 /* max-src-states */ -#define LCNT_SRCNODES 2 /* max-src-nodes */ -#define LCNT_SRCCONN 3 /* max-src-conn */ -#define LCNT_SRCCONNRATE 4 /* max-src-conn-rate */ -#define LCNT_OVERLOAD_TABLE 5 /* entry added to overload table */ -#define LCNT_OVERLOAD_FLUSH 6 /* state entries flushed */ -#define LCNT_MAX 7 /* total+1 */ - -#define LCNT_NAMES { \ - "max states per rule", \ - "max-src-states", \ - "max-src-nodes", \ - "max-src-conn", \ - "max-src-conn-rate", \ - "overload table insertion", \ - "overload flush states", \ - NULL \ -} - -/* UDP state enumeration */ -#define PFUDPS_NO_TRAFFIC 0 -#define PFUDPS_SINGLE 1 -#define PFUDPS_MULTIPLE 2 - -#define PFUDPS_NSTATES 3 /* number of state levels */ - -#define PFUDPS_NAMES { \ - "NO_TRAFFIC", \ - "SINGLE", \ - "MULTIPLE", \ - NULL \ -} - -/* GREv1 protocol state enumeration */ -#define PFGRE1S_NO_TRAFFIC 0 -#define PFGRE1S_INITIATING 1 -#define PFGRE1S_ESTABLISHED 2 - -#define PFGRE1S_NSTATES 3 /* number of state levels */ - -#define PFGRE1S_NAMES { \ - "NO_TRAFFIC", \ - "INITIATING", \ - "ESTABLISHED", \ - NULL \ -} - -#define PFESPS_NO_TRAFFIC 0 -#define PFESPS_INITIATING 1 -#define PFESPS_ESTABLISHED 2 - -#define PFESPS_NSTATES 3 /* number of state levels */ - -#define PFESPS_NAMES { "NO_TRAFFIC", "INITIATING", "ESTABLISHED", NULL } - -/* Other protocol state enumeration */ -#define PFOTHERS_NO_TRAFFIC 0 -#define PFOTHERS_SINGLE 1 -#define PFOTHERS_MULTIPLE 2 - -#define PFOTHERS_NSTATES 3 /* number of state levels */ - -#define PFOTHERS_NAMES { \ - "NO_TRAFFIC", \ - "SINGLE", \ - "MULTIPLE", \ - NULL \ -} - -#define FCNT_STATE_SEARCH 0 -#define FCNT_STATE_INSERT 1 -#define FCNT_STATE_REMOVALS 2 -#define FCNT_MAX 3 - -#define SCNT_SRC_NODE_SEARCH 0 -#define SCNT_SRC_NODE_INSERT 1 -#define SCNT_SRC_NODE_REMOVALS 2 -#define SCNT_MAX 3 - -#ifdef KERNEL -#define ACTION_SET(a, x) \ - do { \ - if ((a) != NULL) \ - *(a) = (x); \ - } while (0) - -#define REASON_SET(a, x) \ - do { \ - if ((a) != NULL) \ - *(a) = (x); \ - if (x < PFRES_MAX) \ - pf_status.counters[x]++; \ - } while (0) -#endif /* KERNEL */ - -struct pf_status { - u_int64_t counters[PFRES_MAX]; - u_int64_t lcounters[LCNT_MAX]; /* limit counters */ - u_int64_t fcounters[FCNT_MAX]; - u_int64_t scounters[SCNT_MAX]; - u_int64_t pcounters[2][2][3]; - u_int64_t bcounters[2][2]; - u_int64_t stateid; - u_int32_t running; - u_int32_t states; - u_int32_t src_nodes; - u_int64_t since __attribute__((aligned(8))); - u_int32_t debug; - u_int32_t hostid; - char ifname[IFNAMSIZ]; - u_int8_t pf_chksum[PF_MD5_DIGEST_LENGTH]; -}; - -struct cbq_opts { - u_int32_t minburst; - u_int32_t maxburst; - u_int32_t pktsize; - u_int32_t maxpktsize; - u_int32_t ns_per_byte; - u_int32_t maxidle; - int32_t minidle; - u_int32_t offtime; - u_int32_t flags; -}; - -struct priq_opts { - u_int32_t flags; -}; - -struct qfq_opts { - u_int32_t flags; - u_int32_t lmax; -}; - -struct hfsc_opts { - /* real-time service curve */ - u_int64_t rtsc_m1; /* slope of the 1st segment in bps */ - u_int64_t rtsc_d; /* the x-projection of m1 in msec */ - u_int64_t rtsc_m2; /* slope of the 2nd segment in bps */ - u_int32_t rtsc_fl; /* service curve flags */ -#if !defined(__LP64__) - u_int32_t _pad; -#endif /* !__LP64__ */ - /* link-sharing service curve */ - u_int64_t lssc_m1; - u_int64_t lssc_d; - u_int64_t lssc_m2; - u_int32_t lssc_fl; -#if !defined(__LP64__) - u_int32_t __pad; -#endif /* !__LP64__ */ - /* upper-limit service curve */ - u_int64_t ulsc_m1; - u_int64_t ulsc_d; - u_int64_t ulsc_m2; - u_int32_t ulsc_fl; - u_int32_t flags; /* scheduler flags */ -}; - -struct fairq_opts { - u_int32_t nbuckets; /* hash buckets */ - u_int32_t flags; - u_int64_t hogs_m1; /* hog detection bandwidth */ - - /* link-sharing service curve */ - u_int64_t lssc_m1; - u_int64_t lssc_d; - u_int64_t lssc_m2; -}; - -/* bandwidth types */ -#define PF_ALTQ_BW_ABSOLUTE 1 /* bw in absolute value (bps) */ -#define PF_ALTQ_BW_PERCENT 2 /* bandwidth in percentage */ - -/* ALTQ rule flags */ -#define PF_ALTQF_TBR 0x1 /* enable Token Bucket Regulator */ - -/* queue rule flags */ -#define PF_ALTQ_QRF_WEIGHT 0x1 /* weight instead of priority */ - -struct pf_altq { - char ifname[IFNAMSIZ]; - - /* discipline-specific state */ - void *altq_disc __attribute__((aligned(8))); - TAILQ_ENTRY(pf_altq) entries __attribute__((aligned(8))); -#if !defined(__LP64__) - u_int32_t _pad[2]; -#endif /* !__LP64__ */ - - u_int32_t aflags; /* ALTQ rule flags */ - u_int32_t bwtype; /* bandwidth type */ - - /* scheduler spec */ - u_int32_t scheduler; /* scheduler type */ - u_int32_t tbrsize; /* tokenbucket regulator size */ - u_int64_t ifbandwidth; /* interface bandwidth */ - - /* queue spec */ - char qname[PF_QNAME_SIZE]; /* queue name */ - char parent[PF_QNAME_SIZE]; /* parent name */ - u_int32_t parent_qid; /* parent queue id */ - u_int32_t qrflags; /* queue rule flags */ - union { - u_int32_t priority; /* priority */ - u_int32_t weight; /* weight */ - }; - u_int32_t qlimit; /* queue size limit */ - u_int32_t flags; /* misc flags */ -#if !defined(__LP64__) - u_int32_t __pad; -#endif /* !__LP64__ */ - u_int64_t bandwidth; /* queue bandwidth */ - union { - struct cbq_opts cbq_opts; - struct priq_opts priq_opts; - struct hfsc_opts hfsc_opts; - struct fairq_opts fairq_opts; - struct qfq_opts qfq_opts; - } pq_u; - - u_int32_t qid; /* return value */ -}; - -struct pf_tagname { - TAILQ_ENTRY(pf_tagname) entries; - char name[PF_TAG_NAME_SIZE]; - u_int16_t tag; - int ref; -}; - -#define PFFRAG_FRENT_HIWAT 5000 /* Number of fragment entries */ -#define PFFRAG_FRAG_HIWAT 1000 /* Number of fragmented packets */ -#define PFFRAG_FRCENT_HIWAT 50000 /* Number of fragment cache entries */ -#define PFFRAG_FRCACHE_HIWAT 10000 /* Number of fragment descriptors */ - -#define PFR_KTABLE_HIWAT 1000 /* Number of tables */ -#define PFR_KENTRY_HIWAT 200000 /* Number of table entries */ -#define PFR_KENTRY_HIWAT_SMALL 100000 /* Number of table entries (tiny hosts) */ - -/* - * ioctl parameter structures - */ - -struct pfioc_pooladdr { - u_int32_t action; - u_int32_t ticket; - u_int32_t nr; - u_int32_t r_num; - u_int8_t r_action; - u_int8_t r_last; - u_int8_t af; - char anchor[MAXPATHLEN]; - struct pf_pooladdr addr; -}; - -struct pfioc_rule { - u_int32_t action; - u_int32_t ticket; - u_int32_t pool_ticket; - u_int32_t nr; - char anchor[MAXPATHLEN]; - char anchor_call[MAXPATHLEN]; - struct pf_rule rule; -}; - -struct pfioc_natlook { - struct pf_addr saddr; - struct pf_addr daddr; - struct pf_addr rsaddr; - struct pf_addr rdaddr; - union pf_state_xport sxport; - union pf_state_xport dxport; - union pf_state_xport rsxport; - union pf_state_xport rdxport; - sa_family_t af; - u_int8_t proto; - u_int8_t proto_variant; - u_int8_t direction; -}; - -struct pfioc_state { - struct pfsync_state state; -}; - -struct pfioc_src_node_kill { - /* XXX returns the number of src nodes killed in psnk_af */ - sa_family_t psnk_af; - struct pf_rule_addr psnk_src; - struct pf_rule_addr psnk_dst; -}; - -struct pfioc_state_addr_kill { - struct pf_addr_wrap addr; - u_int8_t reserved_[3]; - u_int8_t neg; - union pf_rule_xport xport; -}; - -struct pfioc_state_kill { - /* XXX returns the number of states killed in psk_af */ - sa_family_t psk_af; - u_int8_t psk_proto; - u_int8_t psk_proto_variant; - u_int8_t _pad; - struct pfioc_state_addr_kill psk_src; - struct pfioc_state_addr_kill psk_dst; - char psk_ifname[IFNAMSIZ]; - char psk_ownername[PF_OWNER_NAME_SIZE]; -}; - -struct pfioc_states { - int ps_len; - union { - caddr_t psu_buf; - struct pfsync_state *psu_states; - } ps_u __attribute__((aligned(8))); -#define ps_buf ps_u.psu_buf -#define ps_states ps_u.psu_states -}; - -#ifdef KERNEL -struct pfioc_states_32 { - int ps_len; - union { - user32_addr_t psu_buf; - user32_addr_t psu_states; - } ps_u __attribute__((aligned(8))); -}; - -struct pfioc_states_64 { - int ps_len; - union { - user64_addr_t psu_buf; - user64_addr_t psu_states; - } ps_u __attribute__((aligned(8))); -}; -#endif /* KERNEL */ - -#define PFTOK_PROCNAME_LEN 64 -#pragma pack(1) -struct pfioc_token { - u_int64_t token_value; - u_int64_t timestamp; - pid_t pid; - char proc_name[PFTOK_PROCNAME_LEN]; -}; -#pragma pack() - -struct pfioc_kernel_token { - SLIST_ENTRY(pfioc_kernel_token) next; - struct pfioc_token token; -}; - -struct pfioc_remove_token { - u_int64_t token_value; - u_int64_t refcount; -}; - -struct pfioc_tokens { - int size; - union { - caddr_t pgtu_buf; - struct pfioc_token *pgtu_tokens; - } pgt_u __attribute__((aligned(8))); -#define pgt_buf pgt_u.pgtu_buf -#define pgt_tokens pgt_u.pgtu_tokens -}; - -#ifdef KERNEL -struct pfioc_tokens_32 { - int size; - union { - user32_addr_t pgtu_buf; - user32_addr_t pgtu_tokens; - } pgt_u __attribute__((aligned(8))); -}; - -struct pfioc_tokens_64 { - int size; - union { - user64_addr_t pgtu_buf; - user64_addr_t pgtu_tokens; - } pgt_u __attribute__((aligned(8))); -}; -#endif /* KERNEL */ - - -struct pfioc_src_nodes { - int psn_len; - union { - caddr_t psu_buf; - struct pf_src_node *psu_src_nodes; - } psn_u __attribute__((aligned(8))); -#define psn_buf psn_u.psu_buf -#define psn_src_nodes psn_u.psu_src_nodes -}; - -#ifdef KERNEL -struct pfioc_src_nodes_32 { - int psn_len; - union { - user32_addr_t psu_buf; - user32_addr_t psu_src_nodes; - } psn_u __attribute__((aligned(8))); -}; - -struct pfioc_src_nodes_64 { - int psn_len; - union { - user64_addr_t psu_buf; - user64_addr_t psu_src_nodes; - } psn_u __attribute__((aligned(8))); -}; -#endif /* KERNEL */ - -struct pfioc_if { - char ifname[IFNAMSIZ]; -}; - -struct pfioc_tm { - int timeout; - int seconds; -}; - -struct pfioc_limit { - int index; - unsigned limit; -}; - -struct pfioc_altq { - u_int32_t action; - u_int32_t ticket; - u_int32_t nr; - struct pf_altq altq __attribute__((aligned(8))); -}; - -struct pfioc_qstats { - u_int32_t ticket; - u_int32_t nr; - void *buf __attribute__((aligned(8))); - int nbytes __attribute__((aligned(8))); - u_int8_t scheduler; -}; - -struct pfioc_ruleset { - u_int32_t nr; - char path[MAXPATHLEN]; - char name[PF_ANCHOR_NAME_SIZE]; -}; - -#define PF_RULESET_ALTQ (PF_RULESET_MAX) -#define PF_RULESET_TABLE (PF_RULESET_MAX+1) -struct pfioc_trans { - int size; /* number of elements */ - int esize; /* size of each element in bytes */ - struct pfioc_trans_e { - int rs_num; - char anchor[MAXPATHLEN]; - u_int32_t ticket; - } *array __attribute__((aligned(8))); -}; - -#ifdef KERNEL -struct pfioc_trans_32 { - int size; /* number of elements */ - int esize; /* size of each element in bytes */ - user32_addr_t array __attribute__((aligned(8))); -}; - -struct pfioc_trans_64 { - int size; /* number of elements */ - int esize; /* size of each element in bytes */ - user64_addr_t array __attribute__((aligned(8))); -}; -#endif /* KERNEL */ - - -#define PFR_FLAG_ATOMIC 0x00000001 -#define PFR_FLAG_DUMMY 0x00000002 -#define PFR_FLAG_FEEDBACK 0x00000004 -#define PFR_FLAG_CLSTATS 0x00000008 -#define PFR_FLAG_ADDRSTOO 0x00000010 -#define PFR_FLAG_REPLACE 0x00000020 -#define PFR_FLAG_ALLRSETS 0x00000040 -#define PFR_FLAG_ALLMASK 0x0000007F -#ifdef KERNEL -#define PFR_FLAG_USERIOCTL 0x10000000 -#endif /* KERNEL */ - -struct pfioc_table { - struct pfr_table pfrio_table; - void *pfrio_buffer __attribute__((aligned(8))); - int pfrio_esize __attribute__((aligned(8))); - int pfrio_size; - int pfrio_size2; - int pfrio_nadd; - int pfrio_ndel; - int pfrio_nchange; - int pfrio_flags; - u_int32_t pfrio_ticket; -}; -#define pfrio_exists pfrio_nadd -#define pfrio_nzero pfrio_nadd -#define pfrio_nmatch pfrio_nadd -#define pfrio_naddr pfrio_size2 -#define pfrio_setflag pfrio_size2 -#define pfrio_clrflag pfrio_nadd - -#ifdef KERNEL -struct pfioc_table_32 { - struct pfr_table pfrio_table; - user32_addr_t pfrio_buffer __attribute__((aligned(8))); - int pfrio_esize __attribute__((aligned(8))); - int pfrio_size; - int pfrio_size2; - int pfrio_nadd; - int pfrio_ndel; - int pfrio_nchange; - int pfrio_flags; - u_int32_t pfrio_ticket; -}; - -struct pfioc_table_64 { - struct pfr_table pfrio_table; - user64_addr_t pfrio_buffer __attribute__((aligned(8))); - int pfrio_esize __attribute__((aligned(8))); - int pfrio_size; - int pfrio_size2; - int pfrio_nadd; - int pfrio_ndel; - int pfrio_nchange; - int pfrio_flags; - u_int32_t pfrio_ticket; -}; -#endif /* KERNEL */ - -struct pfioc_iface { - char pfiio_name[IFNAMSIZ]; - void *pfiio_buffer __attribute__((aligned(8))); - int pfiio_esize __attribute__((aligned(8))); - int pfiio_size; - int pfiio_nzero; - int pfiio_flags; -}; - -#ifdef KERNEL -struct pfioc_iface_32 { - char pfiio_name[IFNAMSIZ]; - user32_addr_t pfiio_buffer __attribute__((aligned(8))); - int pfiio_esize __attribute__((aligned(8))); - int pfiio_size; - int pfiio_nzero; - int pfiio_flags; -}; - -struct pfioc_iface_64 { - char pfiio_name[IFNAMSIZ]; - user64_addr_t pfiio_buffer __attribute__((aligned(8))); - int pfiio_esize __attribute__((aligned(8))); - int pfiio_size; - int pfiio_nzero; - int pfiio_flags; -}; -#endif /* KERNEL */ - -struct pf_ifspeed { - char ifname[IFNAMSIZ]; - u_int64_t baudrate; -}; - -/* - * ioctl operations - */ - -#define DIOCSTART _IO ('D', 1) -#define DIOCSTOP _IO ('D', 2) -#define DIOCADDRULE _IOWR('D', 4, struct pfioc_rule) -#define DIOCGETSTARTERS _IOWR('D', 5, struct pfioc_tokens) -#define DIOCGETRULES _IOWR('D', 6, struct pfioc_rule) -#define DIOCGETRULE _IOWR('D', 7, struct pfioc_rule) -#define DIOCSTARTREF _IOR ('D', 8, u_int64_t) -#define DIOCSTOPREF _IOWR('D', 9, struct pfioc_remove_token) -/* XXX cut 10 - 17 */ -#define DIOCCLRSTATES _IOWR('D', 18, struct pfioc_state_kill) -#define DIOCGETSTATE _IOWR('D', 19, struct pfioc_state) -#define DIOCSETSTATUSIF _IOWR('D', 20, struct pfioc_if) -#define DIOCGETSTATUS _IOWR('D', 21, struct pf_status) -#define DIOCCLRSTATUS _IO ('D', 22) -#define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook) -#define DIOCSETDEBUG _IOWR('D', 24, u_int32_t) -#define DIOCGETSTATES _IOWR('D', 25, struct pfioc_states) -#define DIOCCHANGERULE _IOWR('D', 26, struct pfioc_rule) -#define DIOCINSERTRULE _IOWR('D', 27, struct pfioc_rule) -#define DIOCDELETERULE _IOWR('D', 28, struct pfioc_rule) -#define DIOCSETTIMEOUT _IOWR('D', 29, struct pfioc_tm) -#define DIOCGETTIMEOUT _IOWR('D', 30, struct pfioc_tm) -#define DIOCADDSTATE _IOWR('D', 37, struct pfioc_state) -#define DIOCCLRRULECTRS _IO ('D', 38) -#define DIOCGETLIMIT _IOWR('D', 39, struct pfioc_limit) -#define DIOCSETLIMIT _IOWR('D', 40, struct pfioc_limit) -#define DIOCKILLSTATES _IOWR('D', 41, struct pfioc_state_kill) -#define DIOCSTARTALTQ _IO ('D', 42) -#define DIOCSTOPALTQ _IO ('D', 43) -#define DIOCADDALTQ _IOWR('D', 45, struct pfioc_altq) -#define DIOCGETALTQS _IOWR('D', 47, struct pfioc_altq) -#define DIOCGETALTQ _IOWR('D', 48, struct pfioc_altq) -#define DIOCCHANGEALTQ _IOWR('D', 49, struct pfioc_altq) -#define DIOCGETQSTATS _IOWR('D', 50, struct pfioc_qstats) -#define DIOCBEGINADDRS _IOWR('D', 51, struct pfioc_pooladdr) -#define DIOCADDADDR _IOWR('D', 52, struct pfioc_pooladdr) -#define DIOCGETADDRS _IOWR('D', 53, struct pfioc_pooladdr) -#define DIOCGETADDR _IOWR('D', 54, struct pfioc_pooladdr) -#define DIOCCHANGEADDR _IOWR('D', 55, struct pfioc_pooladdr) -/* XXX cut 55 - 57 */ -#define DIOCGETRULESETS _IOWR('D', 58, struct pfioc_ruleset) -#define DIOCGETRULESET _IOWR('D', 59, struct pfioc_ruleset) -#define DIOCRCLRTABLES _IOWR('D', 60, struct pfioc_table) -#define DIOCRADDTABLES _IOWR('D', 61, struct pfioc_table) -#define DIOCRDELTABLES _IOWR('D', 62, struct pfioc_table) -#define DIOCRGETTABLES _IOWR('D', 63, struct pfioc_table) -#define DIOCRGETTSTATS _IOWR('D', 64, struct pfioc_table) -#define DIOCRCLRTSTATS _IOWR('D', 65, struct pfioc_table) -#define DIOCRCLRADDRS _IOWR('D', 66, struct pfioc_table) -#define DIOCRADDADDRS _IOWR('D', 67, struct pfioc_table) -#define DIOCRDELADDRS _IOWR('D', 68, struct pfioc_table) -#define DIOCRSETADDRS _IOWR('D', 69, struct pfioc_table) -#define DIOCRGETADDRS _IOWR('D', 70, struct pfioc_table) -#define DIOCRGETASTATS _IOWR('D', 71, struct pfioc_table) -#define DIOCRCLRASTATS _IOWR('D', 72, struct pfioc_table) -#define DIOCRTSTADDRS _IOWR('D', 73, struct pfioc_table) -#define DIOCRSETTFLAGS _IOWR('D', 74, struct pfioc_table) -#define DIOCRINADEFINE _IOWR('D', 77, struct pfioc_table) -#define DIOCOSFPFLUSH _IO('D', 78) -#define DIOCOSFPADD _IOWR('D', 79, struct pf_osfp_ioctl) -#define DIOCOSFPGET _IOWR('D', 80, struct pf_osfp_ioctl) -#define DIOCXBEGIN _IOWR('D', 81, struct pfioc_trans) -#define DIOCXCOMMIT _IOWR('D', 82, struct pfioc_trans) -#define DIOCXROLLBACK _IOWR('D', 83, struct pfioc_trans) -#define DIOCGETSRCNODES _IOWR('D', 84, struct pfioc_src_nodes) -#define DIOCCLRSRCNODES _IO('D', 85) -#define DIOCSETHOSTID _IOWR('D', 86, u_int32_t) -#define DIOCIGETIFACES _IOWR('D', 87, struct pfioc_iface) -#define DIOCSETIFFLAG _IOWR('D', 89, struct pfioc_iface) -#define DIOCCLRIFFLAG _IOWR('D', 90, struct pfioc_iface) -#define DIOCKILLSRCNODES _IOWR('D', 91, struct pfioc_src_node_kill) -#define DIOCGIFSPEED _IOWR('D', 92, struct pf_ifspeed) - -#ifdef KERNEL -RB_HEAD(pf_src_tree, pf_src_node); -RB_PROTOTYPE_SC(__private_extern__, pf_src_tree, pf_src_node, entry, - pf_src_compare); -extern struct pf_src_tree tree_src_tracking; - -RB_HEAD(pf_state_tree_id, pf_state); -RB_PROTOTYPE_SC(__private_extern__, pf_state_tree_id, pf_state, - entry_id, pf_state_compare_id); -extern struct pf_state_tree_id tree_id; -extern struct pf_state_queue state_list; - -TAILQ_HEAD(pf_poolqueue, pf_pool); -extern struct pf_poolqueue pf_pools[2]; -extern struct pf_palist pf_pabuf; -extern u_int32_t ticket_pabuf; -#if PF_ALTQ -TAILQ_HEAD(pf_altqqueue, pf_altq); -extern struct pf_altqqueue pf_altqs[2]; -extern u_int32_t ticket_altqs_active; -extern u_int32_t ticket_altqs_inactive; -extern int altqs_inactive_open; -extern struct pf_altqqueue *pf_altqs_active; -extern struct pf_altqqueue *pf_altqs_inactive; -#endif /* PF_ALTQ */ -extern struct pf_poolqueue *pf_pools_active; -extern struct pf_poolqueue *pf_pools_inactive; - -__private_extern__ int pf_tbladdr_setup(struct pf_ruleset *, - struct pf_addr_wrap *); -__private_extern__ void pf_tbladdr_remove(struct pf_addr_wrap *); -__private_extern__ void pf_tbladdr_copyout(struct pf_addr_wrap *); -__private_extern__ void pf_calc_skip_steps(struct pf_rulequeue *); -__private_extern__ u_int32_t pf_calc_state_key_flowhash(struct pf_state_key *); - -extern struct pool pf_src_tree_pl, pf_rule_pl; -extern struct pool pf_state_pl, pf_state_key_pl, pf_pooladdr_pl; -extern struct pool pf_state_scrub_pl; -#if PF_ALTQ -extern struct pool pf_altq_pl; -#endif /* PF_ALTQ */ -extern struct pool pf_app_state_pl; - -extern struct thread *pf_purge_thread; - -__private_extern__ void pfinit(void); -__private_extern__ void pf_purge_thread_fn(void *, wait_result_t); -__private_extern__ void pf_purge_expired_src_nodes(void); -__private_extern__ void pf_purge_expired_states(u_int32_t); -__private_extern__ void pf_unlink_state(struct pf_state *); -__private_extern__ void pf_free_state(struct pf_state *); -__private_extern__ int pf_insert_state(struct pfi_kif *, struct pf_state *); -__private_extern__ int pf_insert_src_node(struct pf_src_node **, - struct pf_rule *, struct pf_addr *, sa_family_t); -__private_extern__ void pf_src_tree_remove_state(struct pf_state *); -__private_extern__ struct pf_state *pf_find_state_byid(struct pf_state_cmp *); -__private_extern__ struct pf_state *pf_find_state_all(struct pf_state_key_cmp *, - u_int, int *); -__private_extern__ void pf_print_state(struct pf_state *); -__private_extern__ void pf_print_flags(u_int8_t); -__private_extern__ u_int16_t pf_cksum_fixup(u_int16_t, u_int16_t, u_int16_t, - u_int8_t); - -extern struct ifnet *sync_ifp; -extern struct pf_rule pf_default_rule; -__private_extern__ void pf_addrcpy(struct pf_addr *, struct pf_addr *, - u_int8_t); -__private_extern__ void pf_rm_rule(struct pf_rulequeue *, struct pf_rule *); - -struct ip_fw_args; -#if INET -__private_extern__ int pf_test(int, struct ifnet *, struct mbuf **, - struct ether_header *, struct ip_fw_args *); -#endif /* INET */ - -#if INET6 -__private_extern__ int pf_test6(int, struct ifnet *, struct mbuf **, - struct ether_header *, struct ip_fw_args *); -__private_extern__ void pf_poolmask(struct pf_addr *, struct pf_addr *, - struct pf_addr *, struct pf_addr *, u_int8_t); -__private_extern__ void pf_addr_inc(struct pf_addr *, sa_family_t); -#endif /* INET6 */ - -__private_extern__ struct mbuf *pf_lazy_makewritable(struct pf_pdesc *, - struct mbuf *, int); -__private_extern__ void *pf_pull_hdr(struct mbuf *, int, void *, int, - u_short *, u_short *, sa_family_t); -__private_extern__ void pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t); -__private_extern__ int pflog_packet(struct pfi_kif *, struct mbuf *, - sa_family_t, u_int8_t, u_int8_t, struct pf_rule *, struct pf_rule *, - struct pf_ruleset *, struct pf_pdesc *); -__private_extern__ int pf_match_addr(u_int8_t, struct pf_addr *, - struct pf_addr *, struct pf_addr *, sa_family_t); -__private_extern__ int pf_match_addr_range(struct pf_addr *, struct pf_addr *, - struct pf_addr *, sa_family_t); -__private_extern__ int pf_match(u_int8_t, u_int32_t, u_int32_t, u_int32_t); -__private_extern__ int pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t); -__private_extern__ int pf_match_xport(u_int8_t, u_int8_t, union pf_rule_xport *, - union pf_state_xport *); -__private_extern__ int pf_match_uid(u_int8_t, uid_t, uid_t, uid_t); -__private_extern__ int pf_match_gid(u_int8_t, gid_t, gid_t, gid_t); - -__private_extern__ void pf_normalize_init(void); -__private_extern__ int pf_normalize_isempty(void); -__private_extern__ int pf_normalize_ip(struct mbuf **, int, struct pfi_kif *, - u_short *, struct pf_pdesc *); -__private_extern__ int pf_normalize_ip6(struct mbuf **, int, struct pfi_kif *, - u_short *, struct pf_pdesc *); -__private_extern__ int pf_normalize_tcp(int, struct pfi_kif *, struct mbuf *, - int, int, void *, struct pf_pdesc *); -__private_extern__ void pf_normalize_tcp_cleanup(struct pf_state *); -__private_extern__ int pf_normalize_tcp_init(struct mbuf *, int, - struct pf_pdesc *, struct tcphdr *, struct pf_state_peer *, - struct pf_state_peer *); -__private_extern__ int pf_normalize_tcp_stateful(struct mbuf *, int, - struct pf_pdesc *, u_short *, struct tcphdr *, struct pf_state *, - struct pf_state_peer *, struct pf_state_peer *, int *); -__private_extern__ u_int64_t pf_state_expires(const struct pf_state *); -__private_extern__ void pf_purge_expired_fragments(void); -__private_extern__ int pf_routable(struct pf_addr *addr, sa_family_t af, - struct pfi_kif *); -__private_extern__ int pf_rtlabel_match(struct pf_addr *, sa_family_t, - struct pf_addr_wrap *); -__private_extern__ int pf_socket_lookup(int, struct pf_pdesc *); -__private_extern__ struct pf_state_key *pf_alloc_state_key(struct pf_state *, - struct pf_state_key *); -__private_extern__ void pfr_initialize(void); -__private_extern__ int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, - sa_family_t); -__private_extern__ void pfr_update_stats(struct pfr_ktable *, struct pf_addr *, - sa_family_t, u_int64_t, int, int, int); -__private_extern__ int pfr_pool_get(struct pfr_ktable *, int *, - struct pf_addr *, struct pf_addr **, struct pf_addr **, sa_family_t); -__private_extern__ void pfr_dynaddr_update(struct pfr_ktable *, - struct pfi_dynaddr *); -__private_extern__ void pfr_table_copyin_cleanup(struct pfr_table *); -__private_extern__ struct pfr_ktable *pfr_attach_table(struct pf_ruleset *, - char *); -__private_extern__ void pfr_detach_table(struct pfr_ktable *); -__private_extern__ int pfr_clr_tables(struct pfr_table *, int *, int); -__private_extern__ int pfr_add_tables(user_addr_t, int, int *, int); -__private_extern__ int pfr_del_tables(user_addr_t, int, int *, int); -__private_extern__ int pfr_get_tables(struct pfr_table *, user_addr_t, - int *, int); -__private_extern__ int pfr_get_tstats(struct pfr_table *, user_addr_t, - int *, int); -__private_extern__ int pfr_clr_tstats(user_addr_t, int, int *, int); -__private_extern__ int pfr_set_tflags(user_addr_t, int, int, int, int *, - int *, int); -__private_extern__ int pfr_clr_addrs(struct pfr_table *, int *, int); -__private_extern__ int pfr_insert_kentry(struct pfr_ktable *, struct pfr_addr *, - u_int64_t); -__private_extern__ int pfr_add_addrs(struct pfr_table *, user_addr_t, - int, int *, int); -__private_extern__ int pfr_del_addrs(struct pfr_table *, user_addr_t, - int, int *, int); -__private_extern__ int pfr_set_addrs(struct pfr_table *, user_addr_t, - int, int *, int *, int *, int *, int, u_int32_t); -__private_extern__ int pfr_get_addrs(struct pfr_table *, user_addr_t, - int *, int); -__private_extern__ int pfr_get_astats(struct pfr_table *, user_addr_t, - int *, int); -__private_extern__ int pfr_clr_astats(struct pfr_table *, user_addr_t, - int, int *, int); -__private_extern__ int pfr_tst_addrs(struct pfr_table *, user_addr_t, - int, int *, int); -__private_extern__ int pfr_ina_begin(struct pfr_table *, u_int32_t *, int *, - int); -__private_extern__ int pfr_ina_rollback(struct pfr_table *, u_int32_t, int *, - int); -__private_extern__ int pfr_ina_commit(struct pfr_table *, u_int32_t, int *, - int *, int); -__private_extern__ int pfr_ina_define(struct pfr_table *, user_addr_t, - int, int *, int *, u_int32_t, int); - -extern struct pfi_kif *pfi_all; - -__private_extern__ void pfi_initialize(void); -__private_extern__ struct pfi_kif *pfi_kif_get(const char *); -__private_extern__ void pfi_kif_ref(struct pfi_kif *, enum pfi_kif_refs); -__private_extern__ void pfi_kif_unref(struct pfi_kif *, enum pfi_kif_refs); -__private_extern__ int pfi_kif_match(struct pfi_kif *, struct pfi_kif *); -__private_extern__ void pfi_attach_ifnet(struct ifnet *); -__private_extern__ void pfi_detach_ifnet(struct ifnet *); -__private_extern__ int pfi_match_addr(struct pfi_dynaddr *, struct pf_addr *, - sa_family_t); -__private_extern__ int pfi_dynaddr_setup(struct pf_addr_wrap *, sa_family_t); -__private_extern__ void pfi_dynaddr_remove(struct pf_addr_wrap *); -__private_extern__ void pfi_dynaddr_copyout(struct pf_addr_wrap *); -__private_extern__ void pfi_update_status(const char *, struct pf_status *); -__private_extern__ int pfi_get_ifaces(const char *, user_addr_t, int *); -__private_extern__ int pfi_set_flags(const char *, int); -__private_extern__ int pfi_clear_flags(const char *, int); - -__private_extern__ u_int16_t pf_tagname2tag(char *); -__private_extern__ void pf_tag2tagname(u_int16_t, char *); -__private_extern__ void pf_tag_ref(u_int16_t); -__private_extern__ void pf_tag_unref(u_int16_t); -__private_extern__ int pf_tag_packet(struct mbuf *, struct pf_mtag *, - int, unsigned int, struct pf_pdesc *); -__private_extern__ void pf_step_into_anchor(int *, struct pf_ruleset **, int, - struct pf_rule **, struct pf_rule **, int *); -__private_extern__ int pf_step_out_of_anchor(int *, struct pf_ruleset **, int, - struct pf_rule **, struct pf_rule **, int *); -__private_extern__ u_int32_t pf_qname2qid(char *); -__private_extern__ void pf_qid2qname(u_int32_t, char *); -__private_extern__ void pf_qid_unref(u_int32_t); - -extern struct pf_status pf_status; -extern struct pool pf_frent_pl, pf_frag_pl; - -struct pf_pool_limit { - void *pp; - unsigned limit; -}; -extern struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX]; - -__private_extern__ int pf_af_hook(struct ifnet *, struct mbuf **, - struct mbuf **, unsigned int, int, struct ip_fw_args *); -__private_extern__ int pf_ifaddr_hook(struct ifnet *); -__private_extern__ void pf_ifnet_hook(struct ifnet *, int); - -/* - * The following are defined with "private extern" storage class for - * kernel, and "extern" for user-space. - */ -extern struct pf_anchor_global pf_anchors; -extern struct pf_anchor pf_main_anchor; -#define pf_main_ruleset pf_main_anchor.ruleset - -extern int pf_is_enabled; -extern int16_t pf_nat64_configured; -#define PF_IS_ENABLED (pf_is_enabled != 0) -extern u_int32_t pf_hash_seed; - -#if PF_ALTQ -extern u_int32_t altq_allowed; -#endif /* PF_ALTQ */ - -/* these ruleset functions can be linked into userland programs (pfctl) */ -__private_extern__ int pf_get_ruleset_number(u_int8_t); -__private_extern__ void pf_init_ruleset(struct pf_ruleset *); -__private_extern__ int pf_anchor_setup(struct pf_rule *, - const struct pf_ruleset *, const char *); -__private_extern__ int pf_anchor_copyout(const struct pf_ruleset *, - const struct pf_rule *, struct pfioc_rule *); -__private_extern__ void pf_anchor_remove(struct pf_rule *); -__private_extern__ void pf_remove_if_empty_ruleset(struct pf_ruleset *); -__private_extern__ struct pf_anchor *pf_find_anchor(const char *); -__private_extern__ struct pf_ruleset *pf_find_ruleset(const char *); -__private_extern__ struct pf_ruleset *pf_find_ruleset_with_owner(const char *, - const char *, int, int *); -__private_extern__ struct pf_ruleset *pf_find_or_create_ruleset(const char *); -__private_extern__ void pf_rs_initialize(void); - -__private_extern__ int pf_osfp_add(struct pf_osfp_ioctl *); -__private_extern__ struct pf_osfp_enlist *pf_osfp_fingerprint(struct pf_pdesc *, - struct mbuf *, int, const struct tcphdr *); -__private_extern__ struct pf_osfp_enlist *pf_osfp_fingerprint_hdr( - const struct ip *, const struct ip6_hdr *, const struct tcphdr *); -__private_extern__ void pf_osfp_flush(void); -__private_extern__ int pf_osfp_get(struct pf_osfp_ioctl *); -__private_extern__ void pf_osfp_initialize(void); -__private_extern__ int pf_osfp_match(struct pf_osfp_enlist *, pf_osfp_t); -__private_extern__ struct pf_os_fingerprint *pf_osfp_validate(void); -__private_extern__ struct pf_mtag *pf_find_mtag(struct mbuf *); -__private_extern__ struct pf_mtag *pf_get_mtag(struct mbuf *); -#else /* !KERNEL */ -extern struct pf_anchor_global pf_anchors; -extern struct pf_anchor pf_main_anchor; -#define pf_main_ruleset pf_main_anchor.ruleset - -/* these ruleset functions can be linked into userland programs (pfctl) */ -extern int pf_get_ruleset_number(u_int8_t); -extern void pf_init_ruleset(struct pf_ruleset *); -extern int pf_anchor_setup(struct pf_rule *, const struct pf_ruleset *, - const char *); -extern int pf_anchor_copyout(const struct pf_ruleset *, const struct pf_rule *, - struct pfioc_rule *); -extern void pf_anchor_remove(struct pf_rule *); -extern void pf_remove_if_empty_ruleset(struct pf_ruleset *); -extern struct pf_anchor *pf_find_anchor(const char *); -extern struct pf_ruleset *pf_find_ruleset(const char *); -extern struct pf_ruleset *pf_find_ruleset_with_owner(const char *, - const char *, int, int *); -extern struct pf_ruleset *pf_find_or_create_ruleset(const char *); -extern void pf_rs_initialize(void); -#endif /* !KERNEL */ - -#ifdef __cplusplus -} -#endif -#endif /* PF || !KERNEL */ -#endif /* PRIVATE */ -#endif /* _NET_PFVAR_H_ */ diff --git a/xnu/10.12/net/radix.h b/xnu/10.12/net/radix.h deleted file mode 100644 index d48399aa..00000000 --- a/xnu/10.12/net/radix.h +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2000-2008 Apple Inc. All rights reserved. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ -/* - * Copyright (c) 1988, 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)radix.h 8.2 (Berkeley) 10/31/94 - * $FreeBSD: src/sys/net/radix.h,v 1.16.2.1 2000/05/03 19:17:11 wollman Exp $ - */ - -#ifndef _RADIX_H_ -#define _RADIX_H_ -#include - -#ifdef PRIVATE - -#ifdef MALLOC_DECLARE -MALLOC_DECLARE(M_RTABLE); -#endif - -/* - * Radix search tree node layout. - */ - -struct radix_node { - struct radix_mask *rn_mklist; /* list of masks contained in subtree */ - struct radix_node *rn_parent; /* parent */ - short rn_bit; /* bit offset; -1-index(netmask) */ - char rn_bmask; /* node: mask for bit test*/ - u_char rn_flags; /* enumerated next */ -#define RNF_NORMAL 1 /* leaf contains normal route */ -#define RNF_ROOT 2 /* leaf is root leaf for tree */ -#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */ - union { - struct { /* leaf only data: */ - caddr_t rn_Key; /* object of search */ - caddr_t rn_Mask; /* netmask, if present */ - struct radix_node *rn_Dupedkey; - } rn_leaf; - struct { /* node only data: */ - int rn_Off; /* where to start compare */ - struct radix_node *rn_L;/* progeny */ - struct radix_node *rn_R;/* progeny */ - } rn_node; - } rn_u; -#ifdef RN_DEBUG - int rn_info; - struct radix_node *rn_twin; - struct radix_node *rn_ybro; -#endif - -}; - -#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey -#define rn_key rn_u.rn_leaf.rn_Key -#define rn_mask rn_u.rn_leaf.rn_Mask -#define rn_offset rn_u.rn_node.rn_Off -#define rn_left rn_u.rn_node.rn_L -#define rn_right rn_u.rn_node.rn_R - -/* - * Annotations to tree concerning potential routes applying to subtrees. - */ - -struct radix_mask { - short rm_bit; /* bit offset; -1-index(netmask) */ - char rm_unused; /* cf. rn_bmask */ - u_char rm_flags; /* cf. rn_flags */ - struct radix_mask *rm_mklist; /* more masks to try */ - union { - caddr_t rmu_mask; /* the mask */ - struct radix_node *rmu_leaf; /* for normal routes */ - } rm_rmu; - int rm_refs; /* # of references to this struct */ -}; - -#define rm_mask rm_rmu.rmu_mask -#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */ - - -#define MKGet(m) {\ - if (rn_mkfreelist) {\ - m = rn_mkfreelist; \ - rn_mkfreelist = (m)->rm_mklist; \ - } else \ - R_Malloc(m, struct radix_mask *, sizeof (*(m))); }\ - -#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);} - -typedef int walktree_f_t(struct radix_node *, void *); -typedef int rn_matchf_t(struct radix_node *, void *); - -struct radix_node_head { - struct radix_node *rnh_treetop; - int rnh_addrsize; /* permit, but not require fixed keys */ - int rnh_pktsize; /* permit, but not require fixed keys */ - struct radix_node *(*rnh_addaddr) /* add based on sockaddr */ - (void *v, void *mask, - struct radix_node_head *head, struct radix_node nodes[]); - struct radix_node *(*rnh_addpkt) /* add based on packet hdr */ - (void *v, void *mask, - struct radix_node_head *head, struct radix_node nodes[]); - struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */ - (void *v, void *mask, struct radix_node_head *head); - struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */ - (void *v, void *mask, struct radix_node_head *head); - struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */ - (void *v, struct radix_node_head *head); - /* locate based on sockaddr and rn_matchf_t() */ - struct radix_node *(*rnh_matchaddr_args) - (void *v, struct radix_node_head *head, - rn_matchf_t *f, void *w); - struct radix_node *(*rnh_lookup) /* locate based on sockaddr */ - (void *v, void *mask, struct radix_node_head *head); - /* locate based on sockaddr, mask and rn_matchf_t() */ - struct radix_node *(*rnh_lookup_args) - (void *v, void *mask, struct radix_node_head *head, - rn_matchf_t *f, void *); - struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */ - (void *v, struct radix_node_head *head); - int (*rnh_walktree) /* traverse tree */ - (struct radix_node_head *head, walktree_f_t *f, void *w); - int (*rnh_walktree_from) /* traverse tree below a */ - (struct radix_node_head *head, void *a, void *m, - walktree_f_t *f, void *w); - void (*rnh_close) /* do something when the last ref drops */ - (struct radix_node *rn, struct radix_node_head *head); - struct radix_node rnh_nodes[3]; /* empty tree for common case */ - int rnh_cnt; /* tree dimension */ -}; - -#ifndef KERNEL -#define Bcmp(a, b, n) bcmp(((char *)(a)), ((char *)(b)), (n)) -#define Bcopy(a, b, n) bcopy(((char *)(a)), ((char *)(b)), (unsigned)(n)) -#define Bzero(p, n) bzero((char *)(p), (int)(n)); -#define R_Malloc(p, t, n) (p = (t) malloc((unsigned int)(n))) -#define R_Free(p) free((char *)p); -#else -#define Bcmp(a, b, n) bcmp(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n)) -#define Bcopy(a, b, n) bcopy(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n)) -#define Bzero(p, n) bzero((caddr_t)(p), (unsigned)(n)); -#define R_Malloc(p, t, n) (p = (t) _MALLOC((uint32_t)(n), M_RTABLE, M_WAITOK)) -#define R_Free(p) FREE((caddr_t)p, M_RTABLE); -#endif /*KERNEL*/ - -void rn_init(void); -int rn_inithead(void **, int); -int rn_refines(void *, void *); -struct radix_node - *rn_addmask(void *, int, int), - *rn_addroute(void *, void *, struct radix_node_head *, - struct radix_node [2]), - *rn_delete(void *, void *, struct radix_node_head *), - *rn_lookup(void *v_arg, void *m_arg, struct radix_node_head *head), - *rn_lookup_args(void *v_arg, void *m_arg, struct radix_node_head *head, - rn_matchf_t *, void *), - *rn_match(void *, struct radix_node_head *), - *rn_match_args(void *, struct radix_node_head *, rn_matchf_t *, void *); - -#endif /* PRIVATE */ -#endif /* _RADIX_H_ */ From e4a71a7d8c0a7c79d01cb4d71acf3f03313a3a8c Mon Sep 17 00:00:00 2001 From: Kevin LARQUEMIN Date: Sat, 29 Jul 2017 18:00:13 +0200 Subject: [PATCH 116/192] fix my mistitling in README.md --- README.md | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c65a1e5c..cc5eb70e 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,6 @@ The following libraries are required. * libevent2 * OpenSSL or PolarSSL -## Build - ### Steps On general linux, simply run command below to build with OpenSSL. @@ -56,12 +54,6 @@ Since this variant of redsocks is customized for running with Openwrt, please read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how to cross compile. -Configurations --------------- -Please see 'redsocks.conf.example' for whole picture of configuration file. -Below are additional sample configuration sections for different usage. -Operations required to iptables are not listed here. - ### MacOS To build on a MacOS system, you will have to install OpenSSL headers and libevent2 For this, brew is your best friends @@ -74,7 +66,11 @@ To build with PF and run on MacOS, you will need some pf headers that are not in You can find them on this repository : https://github.com/opensource-apple/xnu And the Makefile will going find this file for you -## Configuration +Configurations +-------------- +Please see 'redsocks.conf.example' for whole picture of configuration file. +Below are additional sample configuration sections for different usage. +Operations required to iptables are not listed here. ### Redirect Blocked Traffic via Proxy Automatically To use the autoproxy feature, please change the redsocks section in From 5bcc55e49544f99c96aaf95b661a363e785cbe05 Mon Sep 17 00:00:00 2001 From: Kevin LARQUEMIN Date: Sun, 30 Jul 2017 09:57:13 +0200 Subject: [PATCH 117/192] =?UTF-8?q?fix=20=C2=AB=C2=A0auto=C2=A0=C2=BB=20co?= =?UTF-8?q?mmentary=20deleted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a383976f..7af965ea 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ $(CONF): # Dependency on .git is useful to rebuild `version.c' after commit, but it breaks non-git builds. gen/version.c: *.c *.h gen/.build $(RM) -f $@.tmp - echo '/* this file is -generated during build */' > $@.tmp + echo '/* this file is auto-generated during build */' > $@.tmp echo '#include "../version.h"' >> $@.tmp echo 'const char* redsocks_version = ' >> $@.tmp if [ -d .git ]; then \ From c88f593edba1918ab93dcf72d721bc71e6169a95 Mon Sep 17 00:00:00 2001 From: Kevin LARQUEMIN Date: Sun, 30 Jul 2017 11:02:28 +0200 Subject: [PATCH 118/192] revert deletion of portable dependency maker --- Makefile | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7af965ea..9a15d3a5 100644 --- a/Makefile +++ b/Makefile @@ -107,7 +107,27 @@ $(OSX_HEADERS_PATH)/libkern/tree.h: endif $(DEPS): $(OSX_HEADERS) $(SRCS) - $(CC) -MM $(CFLAGS) $(SRCS) 2>/dev/null >$(DEPS) + $(CC) -MM $(CFLAGS) $(SRCS) 2>/dev/null >$(DEPS) || \ + ( \ + for I in $(wildcard *.h); do \ + export $${I//[-.]/_}_DEPS="`sed '/^\#[ \t]*include \?"\(.*\)".*/!d;s//\1/' $$I`"; \ + done; \ + echo -n >$(DEPS); \ + for SRC in $(SRCS); do \ + echo -n "$${SRC%.c}.o: " >>$(DEPS); \ + export SRC_DEPS="`sed '/\#[ \t]*include \?"\(.*\)".*/!d;s//\1/' $$SRC | sort`"; \ + while true; do \ + export SRC_DEPS_OLD="$$SRC_DEPS"; \ + export SRC_DEEP_DEPS=""; \ + for HDR in $$SRC_DEPS; do \ + eval export SRC_DEEP_DEPS="\"$$SRC_DEEP_DEPS \$$$${HDR//[-.]/_}_DEPS\""; \ + done; \ + export SRC_DEPS="`echo $$SRC_DEPS $$SRC_DEEP_DEPS | sed 's/ */\n/g' | sort -u`"; \ + test "$$SRC_DEPS" = "$$SRC_DEPS_OLD" && break; \ + done; \ + echo $$SRC $$SRC_DEPS >>$(DEPS); \ + done; \ + ) -include $(DEPS) From d2917aac1a6d3ebdfa9cc7d30fca0fb7fdce109c Mon Sep 17 00:00:00 2001 From: Kevin LARQUEMIN Date: Sun, 30 Jul 2017 11:05:47 +0200 Subject: [PATCH 119/192] force /bin/bash shell on MacOS --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 9a15d3a5..9b1b4de6 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ override CFLAGS += -std=c99 -D_XOPEN_SOURCE=600 endif ifeq ($(OS), Darwin) override CFLAGS +=-I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib +SHELL := /bin/bash OSX_VERSION := $(shell sw_vers -productVersion | cut -d '.' -f 1,2) OSX_ROOT_PATH := xnu OSX_HEADERS_PATH := $(OSX_ROOT_PATH)/$(OSX_VERSION) From 9f565d5ba0e625ec00ba54fc3587856e633ca7e9 Mon Sep 17 00:00:00 2001 From: Kevin LARQUEMIN Date: Sun, 30 Jul 2017 11:06:56 +0200 Subject: [PATCH 120/192] add xnu folder to ignore file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 92269858..b78af580 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ tags redsocks2 gen/ .depend +xnu/ From 9896e829392fac0622cb446d8a6ff8a669d22a58 Mon Sep 17 00:00:00 2001 From: Kevin LARQUEMIN Date: Sun, 30 Jul 2017 11:21:10 +0200 Subject: [PATCH 121/192] fix echo interpretation of backslash with /bin/bash --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9b1b4de6..c1e064a6 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ $(CONF): echo "#define USE_PF" >$(CONF) \ ;; \ Darwin) \ - echo "#define USE_PF\n#define _APPLE_" >$(CONF) \ + echo -e "#define USE_PF\n#define _APPLE_" >$(CONF) \ ;; \ *) \ echo "Unknown system, only generic firewall code is compiled" 1>&2; \ From 2c00e5922ddb72f1c4b7e7a910d6b493528d9679 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 3 Jan 2018 20:05:59 +0800 Subject: [PATCH 122/192] Fix: build failure on Ubuntu 17.04 with ENABLE_HTTPS_PROXY=true --- https-connect.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/https-connect.c b/https-connect.c index a3e703e8..8b319c42 100644 --- a/https-connect.c +++ b/https-connect.c @@ -35,8 +35,14 @@ #include "redsocks.h" #include "http-auth.h" -#ifndef EVENT__HAVE_OPENSSL -#error The libevent2 you are compiling with does not have OpenSSL enabled! +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 +# ifndef EVENT__HAVE_OPENSSL +# error The libevent2 you are compiling with does not have OpenSSL enabled! +# endif +#else +# ifndef _EVENT_HAVE_OPENSSL +# error The libevent2 you are compiling with does not have OpenSSL enabled! +# endif #endif typedef enum httpsc_state_t { From 5f4c05196282164269824fe08ab32075c18f24ef Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 11 Jan 2018 21:42:43 +0800 Subject: [PATCH 123/192] Fix: program exits abnormally in daemon mode on FreeBSD 11. --- main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/main.c b/main.c index 2f523134..ad439391 100644 --- a/main.c +++ b/main.c @@ -202,11 +202,6 @@ int main(int argc, char **argv) if (setup_signals()) return EXIT_FAILURE; - // Initialize global event base - g_event_base = event_base_new(); - if (!g_event_base) - return EXIT_FAILURE; - memset(terminators, 0, sizeof(terminators)); FOREACH(ss, subsystems) { @@ -215,6 +210,13 @@ int main(int argc, char **argv) if (error) goto shutdown; } + // init global event base only after base subsystem init is done. + if (!g_event_base) { + // Initialize global event base + g_event_base = event_base_new(); + if (!g_event_base) + goto shutdown; + } } if (pidfile) { From 70f6c689bd6773d68c0bb396ddf592a896f5b594 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 21 Jan 2018 15:55:44 +0800 Subject: [PATCH 124/192] Fix: IP in ipcache not identified as blocked --- ipcache.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ipcache.c b/ipcache.c index e4d444c6..59c45ce7 100644 --- a/ipcache.c +++ b/ipcache.c @@ -373,16 +373,18 @@ void cache_add_addr(const struct sockaddr_in * addr) /* use 'first' to index item in cache block when count is equal or greater than block size */ unsigned int first = addr_cache_pointers[block]; - if (count < config->block_size) + if (count < config->block_size) { item = get_cache_data(block, count); - else + addr_cache_counters[block]++; + } + else { item = get_cache_data(block, first); - + addr_cache_pointers[block]++; + addr_cache_pointers[block] %= config->block_size; + } memcpy((void *)&item->addr, (void *)addr, sizeof(struct sockaddr_in)); item->present = 1; item->access_time = redsocks_time(NULL); - addr_cache_pointers[block]++; - addr_cache_pointers[block] %= config->block_size; set_cache_changed(1); } From 10a4678d4bca4025a1085cdfddf359c45b77ead9 Mon Sep 17 00:00:00 2001 From: Alexey Kamenskiy Date: Wed, 7 Mar 2018 18:52:07 +0800 Subject: [PATCH 125/192] Add patch to compile with newer OpenSSL (#85) * Create disable-ss.patch * Add information how to compile with newer OpenSSL --- README.md | 6 ++++ patches/disable-ss.patch | 61 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 patches/disable-ss.patch diff --git a/README.md b/README.md index cc5eb70e..731771e6 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,12 @@ compile like (Require libevent2 compiled with OpenSSL support): $ make ENABLE_HTTPS_PROXY=true ``` +To compile on newer systems with OpenSSL 1.1.0 and newer (disables shadowsocks support): +``` +$ git apply patches/disable-ss.patch +$ make +``` + Since this variant of redsocks is customized for running with Openwrt, please read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how to cross compile. diff --git a/patches/disable-ss.patch b/patches/disable-ss.patch new file mode 100644 index 00000000..ccd93466 --- /dev/null +++ b/patches/disable-ss.patch @@ -0,0 +1,61 @@ +diff --git a/Makefile b/Makefile +index c1e064a..ee6a07a 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,5 +1,5 @@ +-OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o encrypt.o shadowsocks.o http-connect.o \ +- socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o socks5-udp.o shadowsocks-udp.o \ ++OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o http-connect.o \ ++ socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o socks5-udp.o \ + tcpdns.o gen/version.o + SRCS := $(OBJS:.o=.c) + CONF := config.h +@@ -40,7 +40,7 @@ override LIBS += -levent_openssl + override CFLAGS += -DENABLE_HTTPS_PROXY + $(info Compile with HTTPS proxy enabled.) + endif +-override LIBS += -lssl -lcrypto ++#override LIBS += -lssl -lcrypto + override CFLAGS += -DUSE_CRYPTO_OPENSSL + endif + ifdef ENABLE_STATIC +diff --git a/redsocks.c b/redsocks.c +index 69022b4..66313a0 100644 +--- a/redsocks.c ++++ b/redsocks.c +@@ -56,7 +56,7 @@ extern relay_subsys https_connect_subsys; + extern relay_subsys http_relay_subsys; + extern relay_subsys socks4_subsys; + extern relay_subsys socks5_subsys; +-extern relay_subsys shadowsocks_subsys; ++//extern relay_subsys shadowsocks_subsys; + static relay_subsys *relay_subsystems[] = + { + &direct_connect_subsys, +@@ -64,7 +64,7 @@ static relay_subsys *relay_subsystems[] = + &http_relay_subsys, + &socks4_subsys, + &socks5_subsys, +- &shadowsocks_subsys, ++// &shadowsocks_subsys, + #if defined(ENABLE_HTTPS_PROXY) + &https_connect_subsys, + #endif +diff --git a/redudp.c b/redudp.c +index cf014c3..ab7d55b 100644 +--- a/redudp.c ++++ b/redudp.c +@@ -64,11 +64,11 @@ struct bound_udp4 { + }; + + extern udprelay_subsys socks5_udp_subsys; +-extern udprelay_subsys shadowsocks_udp_subsys; ++//extern udprelay_subsys shadowsocks_udp_subsys; + static udprelay_subsys *relay_subsystems[] = + { + &socks5_udp_subsys, +- &shadowsocks_udp_subsys, ++// &shadowsocks_udp_subsys, + }; + /*********************************************************************** + * Helpers From d608c456a2116a64502e3cd4782648bd6937acfb Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 13 Jul 2018 10:08:39 +0800 Subject: [PATCH 126/192] Fix: memory leaks caused by wrong flag set in shadowsocks --- shadowsocks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks.c b/shadowsocks.c index d42f6595..48406119 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -340,7 +340,7 @@ static int ss_connect_relay(redsocks_client *client) redsocks_drop_client(client); return -1; } - sclient->e_ctx_init = 1; + sclient->d_ctx_init = 1; /* build and send header */ // TODO: Better implementation and IPv6 Support From e7ff18b55131bcb850363f85ecdc73693c0202b5 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 13 Jul 2018 10:32:30 +0800 Subject: [PATCH 127/192] Simplify TFO support --- shadowsocks.c | 21 ++++++--------------- utils.c | 7 ++++++- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/shadowsocks.c b/shadowsocks.c index 48406119..3eb2e946 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -38,9 +38,6 @@ typedef struct ss_client_t { struct enc_ctx d_ctx; short e_ctx_init; short d_ctx_init; - bool tfo; - size_t tfo_size; - char tfo_buff[512]; } ss_client; typedef struct ss_instance_t { @@ -278,7 +275,6 @@ static void ss_relay_readcb(struct bufferevent *buffev, void *_arg) static void ss_relay_connected(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; - ss_client *sclient = (void*)(client + 1); assert(buffev == client->relay); assert(client->state == ss_new); @@ -309,9 +305,6 @@ static void ss_relay_connected(struct bufferevent *buffev, void *_arg) ss_relay_writecb, redsocks_event_error, client); - if(!sclient->tfo) { - bufferevent_write(client->relay, &sclient->tfo_buff[0], sclient->tfo_size); - } // Write any data received from client side to relay. if (evbuffer_get_length(bufferevent_get_input(client->client))) ss_relay_writecb(client->relay, client); @@ -328,6 +321,7 @@ static int ss_connect_relay(redsocks_client *client) ss_header_ipv4 header; struct timeval tv; size_t len = 0; + char buff[64+sizeof(header)]; if (enc_ctx_init(&ss->info, &sclient->e_ctx, 1)) { log_error(LOG_ERR, "Shadowsocks failed to initialize encryption context."); @@ -348,28 +342,25 @@ static int ss_connect_relay(redsocks_client *client) header.addr = client->destaddr.sin_addr.s_addr; header.port = client->destaddr.sin_port; len += sizeof(header); - size_t sz = sizeof(sclient->tfo_buff); - if (!ss_encrypt(&sclient->e_ctx, (char *)&header, len, &sclient->tfo_buff[0], &sz)) { + size_t sz = sizeof(buff); + if (!ss_encrypt(&sclient->e_ctx, (char *)&header, len, &buff[0], &sz)) { log_error(LOG_ERR, "Encryption error."); redsocks_drop_client(client); return -1; } - sclient->tfo_size = len = sz; + len = sz; tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; client->relay = red_connect_relay_tfo(interface, &client->instance->config.relayaddr, NULL, ss_relay_connected, redsocks_event_error, client, - &tv, &sclient->tfo_buff[0], &sz); + &tv, &buff[0], &sz); if (!client->relay) { redsocks_drop_client(client); return -1; } - if (sz && sz == len) { - sclient->tfo = true; - } - else if (sz) { + else if (sz && sz != len) { log_error(LOG_ERR, "Unexpected length of data sent."); redsocks_drop_client(client); return -1; diff --git a/utils.c b/utils.c index 9aebbcf4..9fe148e5 100644 --- a/utils.c +++ b/utils.c @@ -323,12 +323,17 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, fallback: #endif - *len = 0; // Nothing sent, caller needs to write data again when connection is setup. error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "connect"); goto fail; } + // write data to evbuffer so that data can be sent when connection is set up + if (bufferevent_write(retval, data, *len) != 0) { + log_errno(LOG_NOTICE, "bufferevent_write"); + *len = 0; // Nothing sent, caller needs to write data again when connection is setup. + goto fail; + } } return retval; From 4133bcf5f9ee7dd7687ab55bd36c051d149eb3ea Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 28 Sep 2018 13:36:34 +0800 Subject: [PATCH 128/192] Add SO_REUSEPORT support --- README.md | 4 +++- base.c | 22 ++++++++++++++++++++++ base.h | 1 + redsocks.c | 3 +++ redsocks.conf.example | 7 +++++++ redudp.c | 4 ++++ tcpdns.c | 3 +++ 7 files changed, 43 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 731771e6..423b737f 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ need of blacklist. 6. UDP transparent proxy via shadowsocks proxy. 7. Support Ful-cone NAT Traversal when working with shadowsocks or socks5 proxy. 8. Integrated HTTPS proxy support(HTTP CONNECT over SSL). +9. Support TCP Fast Open on local server side and shadowsocks client side +10.Support port reuse ([SO_REUSEPORT](https://lwn.net/Articles/542629/)) [Chinese Reference](https://github.com/semigodking/redsocks/wiki) @@ -26,7 +28,7 @@ The following libraries are required. * OpenSSL or PolarSSL ### Steps -On general linux, simply run command below to build with OpenSSL. +On general Linux, simply run command below to build with OpenSSL. ``` $ make diff --git a/base.c b/base.c index dcfe434e..98aeea25 100644 --- a/base.c +++ b/base.c @@ -78,6 +78,9 @@ typedef struct base_instance_t { bool log_debug; bool log_info; bool daemon; +#ifdef SO_REUSEPORT + bool reuseport; +#endif #if defined(TCP_KEEPIDLE) && defined(TCP_KEEPCNT) && defined(TCP_KEEPINTVL) uint16_t tcp_keepalive_time; uint16_t tcp_keepalive_probes; @@ -284,6 +287,22 @@ int apply_tcp_keepalive(int fd) return 0; } +int apply_reuseport(int fd) +{ + if (!instance.reuseport) + return 0; + +#ifdef SO_REUSEPORT + int opt = 1; + int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); + if (rc == -1) + log_errno(LOG_ERR, "setsockopt"); + return rc; +#else + return -1; +#endif +} + static redirector_subsys redirector_subsystems[] = { #ifdef __FreeBSD__ @@ -315,6 +334,9 @@ static parser_entry base_entries[] = { .key = "tcp_keepalive_time", .type = pt_uint16, .addr = &instance.tcp_keepalive_time }, { .key = "tcp_keepalive_probes", .type = pt_uint16, .addr = &instance.tcp_keepalive_probes }, { .key = "tcp_keepalive_intvl", .type = pt_uint16, .addr = &instance.tcp_keepalive_intvl }, +#endif +#ifdef SO_REUSEPORT + { .key = "reuseport", .type = pt_bool, .addr = &instance.reuseport}, #endif { } }; diff --git a/base.h b/base.h index 2f56f2d5..4c889f6e 100644 --- a/base.h +++ b/base.h @@ -3,6 +3,7 @@ int getdestaddr(int fd, const struct sockaddr_in *client, const struct sockaddr_in *bindaddr, struct sockaddr_in *destaddr); int apply_tcp_keepalive(int fd); +int apply_reuseport(int fd); /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ diff --git a/redsocks.c b/redsocks.c index 69022b41..c943c485 100644 --- a/redsocks.c +++ b/redsocks.c @@ -985,6 +985,9 @@ static int redsocks_init_instance(redsocks_instance *instance) if (make_socket_transparent(fd)) log_error(LOG_WARNING, "Continue without TPROXY support"); + if (apply_reuseport(fd)) + log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); + error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); if (error) { log_errno(LOG_ERR, "bind"); diff --git a/redsocks.conf.example b/redsocks.conf.example index 639f64af..fa40a2f9 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -43,6 +43,13 @@ base { //tcp_keepalive_time = 0; //tcp_keepalive_probes = 0; //tcp_keepalive_intvl = 0; + + /* Enable or disable Linux 3.9+ specific socket option SO_REUSEPORT. + * Some older versions of Linux like CentOS 6.5 (Kernel 2.6.32) also + # support this option. + * Default to off. + */ + reuseport = off; } redsocks { diff --git a/redudp.c b/redudp.c index cf014c3f..15223eec 100644 --- a/redudp.c +++ b/redudp.c @@ -32,6 +32,7 @@ #include #include +#include "base.h" #include "list.h" #include "log.h" #include "socks5.h" @@ -572,6 +573,9 @@ static int redudp_init_instance(redudp_instance *instance) red_inet_ntop(&instance->config.destaddr, buf2, sizeof(buf2))); } + if (apply_reuseport(fd)) + log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); + error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); if (error) { log_errno(LOG_ERR, "bind"); diff --git a/tcpdns.c b/tcpdns.c index 0ad51678..cba24bee 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -25,6 +25,7 @@ #include #include +#include "base.h" #include "list.h" #include "log.h" #include "parser.h" @@ -464,6 +465,8 @@ static int tcpdns_init_instance(tcpdns_instance *instance) log_errno(LOG_ERR, "socket"); goto fail; } + if (apply_reuseport(fd)) + log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); if (error) { From f72fc2ab34bab495bfad8e982b94c64acaef591c Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 31 Dec 2018 20:24:46 +0800 Subject: [PATCH 129/192] Fix: compiling error when no SO_REUSEPORT support --- base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base.c b/base.c index 98aeea25..6454f4f5 100644 --- a/base.c +++ b/base.c @@ -289,10 +289,10 @@ int apply_tcp_keepalive(int fd) int apply_reuseport(int fd) { +#ifdef SO_REUSEPORT if (!instance.reuseport) return 0; -#ifdef SO_REUSEPORT int opt = 1; int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); if (rc == -1) From 53cad23a14afdd5f61a0542615beb7f4d98be728 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 19 Jan 2019 22:58:44 +0800 Subject: [PATCH 130/192] Bump version to 0.67 Seems it is stable enough for a release. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c1e064a6..c834c5fa 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks2 -VERSION := 0.66 +VERSION := 0.67 OS := $(shell uname) LIBS := -levent From 6f58724478297c6a4b063ca55e7b06d89b5d9fe5 Mon Sep 17 00:00:00 2001 From: rampageX Date: Wed, 13 Mar 2019 20:43:49 +0800 Subject: [PATCH 131/192] refine build with openssl 1.1.0+ (#114) no patch needed, just use DISABLE_SHADOWSOCKS, and backward compatibility with openssl 1.0.x when ENABLE_HTTPS_PROXY. --- Makefile | 7 +++++++ README.md | 5 +++++ https-connect.c | 16 ++++++++++------ redsocks.c | 38 +++++++++++++++++++++----------------- redudp.c | 14 +++++++++----- 5 files changed, 52 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index c834c5fa..076fe3d4 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,13 @@ +ifdef DISABLE_SHADOWSOCKS +OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o http-connect.o \ + socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o socks5-udp.o \ + tcpdns.o gen/version.o +override CFLAGS += -DDISABLE_SHADOWSOCKS +else OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o encrypt.o shadowsocks.o http-connect.o \ socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o socks5-udp.o shadowsocks-udp.o \ tcpdns.o gen/version.o +endif SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend diff --git a/README.md b/README.md index 423b737f..e5f35c59 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,11 @@ $ git apply patches/disable-ss.patch $ make ``` +To compile on newer systems with OpenSSL 1.1.1+ (just disable shadowsocks support, no patch need and worked with ENABLE_HTTPS_PROXY. DO NOT APPLY THE PATCH!): +``` +$ make DISABLE_SHADOWSOCKS=true +``` + Since this variant of redsocks is customized for running with Openwrt, please read documents here (http://wiki.openwrt.org/doc/devel/crosscompile) for how to cross compile. diff --git a/https-connect.c b/https-connect.c index 8b319c42..dbbd73cf 100644 --- a/https-connect.c +++ b/https-connect.c @@ -106,8 +106,12 @@ static int httpsc_instance_init(struct redsocks_instance_t *instance) { httpsc_instance * httpsc = (httpsc_instance *)(instance + 1); SSL_CTX * ctx = NULL; - - ctx = SSL_CTX_new(SSLv23_client_method()); + + #if (OPENSSL_VERSION_NUMBER < 0x10100000L) + ctx = SSL_CTX_new(SSLv23_client_method()); + #else + ctx = SSL_CTX_new(TLS_client_method()); + #endif if (!ctx) { unsigned long err = ERR_get_error(); @@ -148,7 +152,7 @@ static void httpsc_event_cb(struct bufferevent *buffev, short what, void *_arg) else #endif log_ssl_error(client, client->relay); - redsocks_log_errno(client, LOG_DEBUG, "%s, what: " event_fmt_str, + redsocks_log_errno(client, LOG_DEBUG, "%s, what: " event_fmt_str, buffev == client->client?"client":"relay", event_fmt(what)); @@ -159,7 +163,7 @@ static void httpsc_event_cb(struct bufferevent *buffev, short what, void *_arg) { if (!(client->relay_evshut & EV_WRITE) && client->relay_connected) // when we got EOF from client, we need to shutdown relay's write - process_shutdown_on_write_(client, client->client, client->relay); + process_shutdown_on_write_(client, client->client, client->relay); } else { @@ -226,12 +230,12 @@ static int httpsc_connect_relay(redsocks_client *client) struct timeval tv = {client->instance->config.timeout, 0}; if (!sclient->ssl) - sclient->ssl = SSL_new(httpsc->ctx); + sclient->ssl = SSL_new(httpsc->ctx); // Allowing binding relay socket to specified IP for outgoing connections client->relay = red_connect_relay_ssl(interface, &client->instance->config.relayaddr, sclient->ssl, - httpsc_read_cb, + httpsc_read_cb, NULL, httpsc_event_cb, client, &tv); if (!client->relay) { diff --git a/redsocks.c b/redsocks.c index c943c485..e919e400 100644 --- a/redsocks.c +++ b/redsocks.c @@ -3,8 +3,8 @@ * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"). - * - * + * + * * Copyright (C) 2007-2011 Leonid Evdokimov * * Licensed under the Apache License, Version 2.0 (the "License"); you may not @@ -56,7 +56,9 @@ extern relay_subsys https_connect_subsys; extern relay_subsys http_relay_subsys; extern relay_subsys socks4_subsys; extern relay_subsys socks5_subsys; +#if !defined(DISABLE_SHADOWSOCKS) extern relay_subsys shadowsocks_subsys; +#endif static relay_subsys *relay_subsystems[] = { &direct_connect_subsys, @@ -64,7 +66,9 @@ static relay_subsys *relay_subsystems[] = &http_relay_subsys, &socks4_subsys, &socks5_subsys, +#if !defined(DISABLE_SHADOWSOCKS) &shadowsocks_subsys, +#endif #if defined(ENABLE_HTTPS_PROXY) &https_connect_subsys, #endif @@ -179,7 +183,7 @@ static int redsocks_onenter(parser_section *section) (strcmp(entry->key, "autoproxy") == 0) ? (void*)&instance->config.autoproxy : (strcmp(entry->key, "timeout") == 0) ? (void*)&instance->config.timeout: NULL; - section->data = instance; + section->data = instance; return 0; } @@ -387,9 +391,9 @@ int redsocks_start_relay(redsocks_client *client) event_cb, client); - error = bufferevent_enable(client->client, - client->client_evshut == EV_READ ? EV_WRITE : - client->client_evshut == EV_WRITE ? EV_READ : + error = bufferevent_enable(client->client, + client->client_evshut == EV_READ ? EV_WRITE : + client->client_evshut == EV_WRITE ? EV_READ : client->client_evshut == (EV_READ|EV_WRITE) ? 0 : EV_READ | EV_WRITE); if (!error) error = bufferevent_enable(client->relay, EV_READ | EV_WRITE); @@ -506,7 +510,7 @@ void redsocks_event_error(struct bufferevent *buffev, short what, void *_arg) if (!(what & BEV_EVENT_ERROR)) errno = redsocks_socket_geterrno(client, buffev); - redsocks_log_errno(client, LOG_DEBUG, "%s, what: " event_fmt_str, + redsocks_log_errno(client, LOG_DEBUG, "%s, what: " event_fmt_str, buffev == client->client?"client":"relay", event_fmt(what)); @@ -674,7 +678,7 @@ int redsocks_connect_relay(redsocks_client *client) // Allowing binding relay socket to specified IP for outgoing connections client->relay = red_connect_relay(interface, &client->instance->config.relayaddr, - NULL, + NULL, redsocks_relay_connected, redsocks_event_error, client, &tv); if (!client->relay) { @@ -782,7 +786,7 @@ static void redsocks_accept_client(int fd, short what, void *_arg) // everything seems to be ok, let's allocate some memory if (self->config.autoproxy) - client = calloc(1, sizeof(redsocks_client) + + client = calloc(1, sizeof(redsocks_client) + self->relay_ss->payload_len + autoproxy_subsys.payload_len ); else @@ -809,9 +813,9 @@ static void redsocks_accept_client(int fd, short what, void *_arg) if (!client->client) { log_errno(LOG_ERR, "bufferevent_socket_new"); goto fail; - } + } bufferevent_setcb(client->client, NULL, NULL, redsocks_event_error, client); - + client_fd = -1; // enable reading to handle EOF from client @@ -893,7 +897,7 @@ static void redsocks_dump_instance(redsocks_instance *instance) red_inet_ntop(&instance->config.bindaddr, addr_str, sizeof(addr_str))); list_for_each_entry(client, &instance->clients, list) redsocks_dump_client(client, LOG_INFO); - + log_error(LOG_INFO, "End of client list."); } @@ -905,9 +909,9 @@ static void redsocks_debug_dump() redsocks_dump_instance(instance); } -/* Audit is required to clean up hung connections. +/* Audit is required to clean up hung connections. * Not all connections are closed gracefully by both ends. In any case that - * either far end of client or far end of relay does not close connection + * either far end of client or far end of relay does not close connection * gracefully, we got hung connections. */ static void redsocks_audit_instance(redsocks_instance *instance) @@ -925,7 +929,7 @@ static void redsocks_audit_instance(redsocks_instance *instance) if (now - client->last_event >= REDSOCKS_AUDIT_INTERVAL){ /* Only take actions if no touch of the client for at least an audit cycle.*/ - /* drop this client if either end disconnected */ + /* drop this client if either end disconnected */ if ((client->client_evshut == EV_WRITE && client->relay_evshut == EV_READ) || (client->client_evshut == EV_READ && client->relay_evshut == EV_WRITE) || (client->client_evshut == (EV_READ|EV_WRITE) && client->relay_evshut == EV_WRITE) @@ -963,11 +967,11 @@ static int redsocks_init_instance(redsocks_instance *instance) int error; evutil_socket_t fd = -1; - if (instance->relay_ss->instance_init + if (instance->relay_ss->instance_init && instance->relay_ss->instance_init(instance)) { log_errno(LOG_ERR, "Failed to init relay subsystem."); goto fail; - } + } fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (fd == -1) { diff --git a/redudp.c b/redudp.c index 15223eec..78071a26 100644 --- a/redudp.c +++ b/redudp.c @@ -3,8 +3,8 @@ * * This code is based on redsocks project developed by Leonid Evdokimov. * Licensed under the Apache License, Version 2.0 (the "License"). - * - * + * + * * redsocks - transparent TCP-to-proxy redirector * Copyright (C) 2007-2011 Leonid Evdokimov * @@ -65,11 +65,15 @@ struct bound_udp4 { }; extern udprelay_subsys socks5_udp_subsys; +#if !defined(DISABLE_SHADOWSOCKS) extern udprelay_subsys shadowsocks_udp_subsys; +#endif static udprelay_subsys *relay_subsystems[] = { &socks5_udp_subsys, + #if !defined(DISABLE_SHADOWSOCKS) &shadowsocks_udp_subsys, + #endif }; /*********************************************************************** * Helpers @@ -263,7 +267,7 @@ void redudp_bump_timeout(redudp_client *client) } } -void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, +void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, struct sockaddr_in * srcaddr) { size_t sent; @@ -541,11 +545,11 @@ static int redudp_init_instance(redudp_instance *instance) char buf1[RED_INET_ADDRSTRLEN], buf2[RED_INET_ADDRSTRLEN]; instance->shared_buff = &shared_buff[0]; - if (instance->relay_ss->instance_init + if (instance->relay_ss->instance_init && instance->relay_ss->instance_init(instance)) { log_errno(LOG_ERR, "Failed to init UDP relay subsystem."); goto fail; - } + } fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd == -1) { From 3ac15043c8f89b90b0a43dec68f4c9a7977f74cb Mon Sep 17 00:00:00 2001 From: rampageX Date: Wed, 13 Mar 2019 22:45:34 +0800 Subject: [PATCH 132/192] revise Makefile (#115) Restore the original CFLAGS parameter. add `Features: ` when run `redsocks2 -v` --- Makefile | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 076fe3d4..6f16b7fc 100644 --- a/Makefile +++ b/Makefile @@ -2,11 +2,13 @@ ifdef DISABLE_SHADOWSOCKS OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o http-connect.o \ socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o socks5-udp.o \ tcpdns.o gen/version.o -override CFLAGS += -DDISABLE_SHADOWSOCKS +CFLAGS +=-fPIC -O3 -DDISABLE_SHADOWSOCKS +FEATURES += DISABLE_SHADOWSOCKS else OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o encrypt.o shadowsocks.o http-connect.o \ socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o socks5-udp.o shadowsocks-udp.o \ tcpdns.o gen/version.o +CFLAGS +=-fPIC -O3 endif SRCS := $(OBJS:.o=.c) CONF := config.h @@ -16,7 +18,6 @@ VERSION := 0.67 OS := $(shell uname) LIBS := -levent -CFLAGS +=-fPIC -O3 override CFLAGS += -D_BSD_SOURCE -D_DEFAULT_SOURCE -Wall ifeq ($(OS), Linux) override CFLAGS += -std=c99 -D_XOPEN_SOURCE=600 @@ -45,6 +46,7 @@ ifdef ENABLE_HTTPS_PROXY override OBJS += https-connect.o override LIBS += -levent_openssl override CFLAGS += -DENABLE_HTTPS_PROXY +override FEATURES += ENABLE_HTTPS_PROXY $(info Compile with HTTPS proxy enabled.) endif override LIBS += -lssl -lcrypto @@ -53,6 +55,7 @@ endif ifdef ENABLE_STATIC override LIBS += -ldl -lz override LDFLAGS += -Wl,-static -static -static-libgcc -s +override FEATURES += STATIC_COMPILE endif all: $(OUT) @@ -92,9 +95,13 @@ gen/version.c: *.c *.h gen/.build echo '"redsocks.git/'`git describe --tags`' $(CRYPTO)"'; \ if [ `git status --porcelain | grep -v -c '^??'` != 0 ]; then \ echo '"-unclean"'; \ - fi \ + fi; \ + echo '"\n"'; \ + echo '"Features: $(FEATURES)"'; \ else \ echo '"redsocks/$(VERSION) $(CRYPTO)"'; \ + echo '"\n"'; \ + echo '"Features: $(FEATURES)"'; \ fi >> $@.tmp echo ';' >> $@.tmp mv -f $@.tmp $@ From a40e60b48438e2ca1bcf6112a64431da6951c97a Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 23 Mar 2019 19:00:23 +0800 Subject: [PATCH 133/192] Fix: compiler error: missing terminating " character --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 6f16b7fc..798c7fd6 100644 --- a/Makefile +++ b/Makefile @@ -96,12 +96,12 @@ gen/version.c: *.c *.h gen/.build if [ `git status --porcelain | grep -v -c '^??'` != 0 ]; then \ echo '"-unclean"'; \ fi; \ - echo '"\n"'; \ - echo '"Features: $(FEATURES)"'; \ + echo '"\\n"'; \ + echo '"Features: $(FEATURES)"' \ else \ echo '"redsocks/$(VERSION) $(CRYPTO)"'; \ - echo '"\n"'; \ - echo '"Features: $(FEATURES)"'; \ + echo '"\\n"'; \ + echo '"Features: $(FEATURES)"' \ fi >> $@.tmp echo ';' >> $@.tmp mv -f $@.tmp $@ From b9cafa94664551362fb6a17b2933c1fefc14bf8f Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 20 Apr 2019 21:56:22 +0800 Subject: [PATCH 134/192] Fix: No rule to make target 'gen/version.c' It seems this issue occurs on Federa. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 798c7fd6..8e1e495d 100644 --- a/Makefile +++ b/Makefile @@ -97,11 +97,11 @@ gen/version.c: *.c *.h gen/.build echo '"-unclean"'; \ fi; \ echo '"\\n"'; \ - echo '"Features: $(FEATURES)"' \ + echo '"Features: $(FEATURES)"'; \ else \ echo '"redsocks/$(VERSION) $(CRYPTO)"'; \ echo '"\\n"'; \ - echo '"Features: $(FEATURES)"' \ + echo '"Features: $(FEATURES)"'; \ fi >> $@.tmp echo ';' >> $@.tmp mv -f $@.tmp $@ From 3f41aa2805f74aac8cbb7b9033eaf1a4302d9507 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 25 Sep 2019 22:16:25 +0800 Subject: [PATCH 135/192] One step to support IPv6 Since this commit, the config file will not be compatible with earlier versions. With this update, "redsocks" should support proxy servers at IPv6 address. --- autoproxy.c | 11 +- direct.c | 10 +- redsocks.c | 26 +- redsocks.conf.example | 17 +- redsocks.h | 3 +- shadowsocks.c | 13 +- socks5-udp.c | 552 +++++++++++++++++++++--------------------- tcpdns.c | 2 +- utils.c | 25 +- utils.h | 7 +- 10 files changed, 352 insertions(+), 314 deletions(-) diff --git a/autoproxy.c b/autoproxy.c index bbc60cc3..3a8eda1a 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -708,9 +708,14 @@ static int auto_connect_relay(redsocks_client *client) aclient->quick_check = 1; } /* connect to target directly without going through proxy */ - client->relay = red_connect_relay(config->interface, &client->destaddr, - NULL, auto_relay_connected, auto_event_error, client, - &tv); + client->relay = red_connect_relay( + config->interface, + (struct sockaddr *)&client->destaddr, + NULL, + auto_relay_connected, + auto_event_error, + client, + &tv); if (!client->relay) { // Failed to connect to destination directly, try again via proxy. return auto_retry(client, 0); diff --git a/direct.c b/direct.c index 0d5688d8..018abc39 100644 --- a/direct.c +++ b/direct.c @@ -65,8 +65,14 @@ static int direct_connect_relay(redsocks_client *client) struct timeval tv = {client->instance->config.timeout, 0}; // Allowing binding relay socket to specified IP for outgoing connections - client->relay = red_connect_relay(interface, &client->destaddr, NULL, - redsocks_relay_connected, redsocks_event_error, client, &tv); + client->relay = red_connect_relay( + interface, + (struct sockaddr *)&client->destaddr, + NULL, + redsocks_relay_connected, + redsocks_event_error, + client, + &tv); if (!client->relay) { redsocks_log_errno(client, LOG_ERR, "red_connect_relay"); diff --git a/redsocks.c b/redsocks.c index e919e400..ead3ca31 100644 --- a/redsocks.c +++ b/redsocks.c @@ -82,8 +82,7 @@ static parser_entry redsocks_entries[] = { .key = "local_ip", .type = pt_in_addr }, { .key = "local_port", .type = pt_uint16 }, { .key = "interface", .type = pt_pchar }, - { .key = "ip", .type = pt_in_addr }, - { .key = "port", .type = pt_uint16 }, + { .key = "relay", .type = pt_pchar }, { .key = "type", .type = pt_pchar }, { .key = "login", .type = pt_pchar }, { .key = "password", .type = pt_pchar }, @@ -156,8 +155,6 @@ static int redsocks_onenter(parser_section *section) INIT_LIST_HEAD(&instance->clients); instance->config.bindaddr.sin_family = AF_INET; instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - instance->config.relayaddr.sin_family = AF_INET; - instance->config.relayaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* Default value can be checked in run-time, but I doubt anyone needs that. * Linux: sysctl net.core.somaxconn * FreeBSD: sysctl kern.ipc.somaxconn */ @@ -172,8 +169,7 @@ static int redsocks_onenter(parser_section *section) (strcmp(entry->key, "local_ip") == 0) ? (void*)&instance->config.bindaddr.sin_addr : (strcmp(entry->key, "local_port") == 0) ? (void*)&instance->config.bindaddr.sin_port : (strcmp(entry->key, "interface") == 0) ? (void*)&instance->config.interface : - (strcmp(entry->key, "ip") == 0) ? (void*)&instance->config.relayaddr.sin_addr : - (strcmp(entry->key, "port") == 0) ? (void*)&instance->config.relayaddr.sin_port : + (strcmp(entry->key, "relay") == 0) ? (void*)&instance->config.relay : (strcmp(entry->key, "type") == 0) ? (void*)&instance->config.type : (strcmp(entry->key, "login") == 0) ? (void*)&instance->config.login : (strcmp(entry->key, "password") == 0) ? (void*)&instance->config.password : @@ -200,10 +196,17 @@ static int redsocks_onexit(parser_section *section) for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = NULL; + // Parse and update relay address + struct sockaddr * addr = (struct sockaddr *)&instance->config.relayaddr; + int addr_size = sizeof(instance->config.relayaddr); + if (instance->config.relay) { + if (evutil_parse_sockaddr_port(instance->config.relay, addr, &addr_size)) + err = "invalid relay address"; + } + instance->config.bindaddr.sin_port = htons(instance->config.bindaddr.sin_port); - instance->config.relayaddr.sin_port = htons(instance->config.relayaddr.sin_port); - if (instance->config.type) { + if (!err && instance->config.type) { relay_subsys **ss; FOREACH(ss, relay_subsystems) { if (!strcmp((*ss)->name, instance->config.type)) { @@ -278,8 +281,8 @@ void redsocks_touch_client(redsocks_client *client) static inline const char* bufname(redsocks_client *client, struct bufferevent *buf) { - assert(buf == client->client || buf == client->relay); - return buf == client->client ? "client" : "relay"; + assert(buf == client->client || buf == client->relay); + return buf == client->client ? "client" : "relay"; } static void redsocks_relay_readcb(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) @@ -677,7 +680,8 @@ int redsocks_connect_relay(redsocks_client *client) tv.tv_usec = 0; // Allowing binding relay socket to specified IP for outgoing connections - client->relay = red_connect_relay(interface, &client->instance->config.relayaddr, + client->relay = red_connect_relay(interface, + (struct sockaddr *)&client->instance->config.relayaddr, NULL, redsocks_relay_connected, redsocks_event_error, client, &tv); diff --git a/redsocks.conf.example b/redsocks.conf.example index fa40a2f9..6b617663 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -73,11 +73,18 @@ redsocks { // max_accept_backoff = 60000; // `ip' and `port' are IP and tcp-port of proxy-server - // You can also use hostname instead of IP, only one (random) - // address of multihomed host will be used. - // The two fields are meaningless when proxy type is 'direct'. - ip = example.org; - port = 1080; + // These two parameters are removed to support IPv6. + + // `relay' is IP address and port of proxy-server. Domain name is not + // supported yet. + // Can be: + // [IPv6Address]:port + // [IPv6Address] + // IPv6Address + // IPv4Address:port + // IPv4Address + // If no port is given, 0 is used. Usually, a valid port is required. + relay = "127.0.0.1:1080"; // known types: socks4, socks5, http-connect, http-relay // New types: direct, shadowsocks, https-connect diff --git a/redsocks.h b/redsocks.h index 6a49495f..15d281b5 100644 --- a/redsocks.h +++ b/redsocks.h @@ -30,7 +30,8 @@ typedef struct relay_subsys_t { typedef struct redsocks_config_t { struct sockaddr_in bindaddr; - struct sockaddr_in relayaddr; + struct sockaddr_storage relayaddr; + char *relay; char *type; char *login; char *password; diff --git a/shadowsocks.c b/shadowsocks.c index 3eb2e946..e483d5a4 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -352,9 +352,16 @@ static int ss_connect_relay(redsocks_client *client) tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; - client->relay = red_connect_relay_tfo(interface, &client->instance->config.relayaddr, - NULL, ss_relay_connected, redsocks_event_error, client, - &tv, &buff[0], &sz); + client->relay = red_connect_relay_tfo( + interface, + (struct sockaddr *)&client->instance->config.relayaddr, + NULL, + ss_relay_connected, + redsocks_event_error, + client, + &tv, + &buff[0], + &sz); if (!client->relay) { redsocks_drop_client(client); diff --git a/socks5-udp.c b/socks5-udp.c index e80fdb1c..7a7412c8 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -30,38 +30,38 @@ #include "socks5.h" typedef struct socks5_expected_assoc_reply_t { - socks5_reply h; - socks5_addr_ipv4 ip; + socks5_reply h; + socks5_addr_ipv4 ip; } PACKED socks5_expected_assoc_reply; static struct evbuffer* socks5_mkmethods_plain_wrapper(void *p) { - int *do_password = p; - return socks5_mkmethods_plain(*do_password); + int *do_password = p; + return socks5_mkmethods_plain(*do_password); } static struct evbuffer* socks5_mkpassword_plain_wrapper(void *p) { - redudp_instance *self = p; - return socks5_mkpassword_plain(self->config.login, self->config.password); + redudp_instance *self = p; + return socks5_mkpassword_plain(self->config.login, self->config.password); } static struct evbuffer* socks5_mkassociate(void *p) { - struct sockaddr_in sa; - //p = p; /* Make compiler happy */ - memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - return socks5_mkcommand_plain(socks5_cmd_udp_associate, &sa); + struct sockaddr_in sa; + //p = p; /* Make compiler happy */ + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + return socks5_mkcommand_plain(socks5_cmd_udp_associate, &sa); } static void socks5_fill_preamble(socks5_udp_preabmle *preamble, struct sockaddr_in * addr) { - preamble->reserved = 0; - preamble->frag_no = 0; /* fragmentation is not supported */ - preamble->addrtype = socks5_addrtype_ipv4; - preamble->ip.addr = addr->sin_addr.s_addr; - preamble->ip.port = addr->sin_port; + preamble->reserved = 0; + preamble->frag_no = 0; /* fragmentation is not supported */ + preamble->addrtype = socks5_addrtype_ipv4; + preamble->ip.addr = addr->sin_addr.s_addr; + preamble->ip.port = addr->sin_port; } @@ -71,29 +71,29 @@ static void socks5_fill_preamble(socks5_udp_preabmle *preamble, struct sockaddr_ * */ typedef struct socks5_client_t { - struct event udprelay; - struct sockaddr_in udprelayaddr; - struct bufferevent *relay; - int ready_fwd; + struct event udprelay; + struct sockaddr_in udprelayaddr; + struct bufferevent *relay; + int ready_fwd; } socks5_client; static void socks5_client_init(redudp_client *client) { - socks5_client *socks5client = (void*)(client + 1); - memset(socks5client, 0, sizeof(socks5_client)); + socks5_client *socks5client = (void*)(client + 1); + memset(socks5client, 0, sizeof(socks5_client)); } static void socks5_client_fini(redudp_client *client) { - socks5_client *socks5client = (void*)(client + 1); - int fd; - - if (event_initialized(&socks5client->udprelay)) { - fd = event_get_fd(&socks5client->udprelay); - if (event_del(&socks5client->udprelay) == -1) - redudp_log_errno(client, LOG_ERR, "event_del"); - close(fd); - } + socks5_client *socks5client = (void*)(client + 1); + int fd; + + if (event_initialized(&socks5client->udprelay)) { + fd = event_get_fd(&socks5client->udprelay); + if (event_del(&socks5client->udprelay) == -1) + redudp_log_errno(client, LOG_ERR, "event_del"); + close(fd); + } if (socks5client->relay) { fd = bufferevent_getfd(socks5client->relay); bufferevent_free(socks5client->relay); @@ -104,132 +104,132 @@ static void socks5_client_fini(redudp_client *client) static int socks5_ready_to_fwd(struct redudp_client_t *client) { - socks5_client *socks5client = (void*)(client + 1); - return socks5client->ready_fwd; + socks5_client *socks5client = (void*)(client + 1); + return socks5client->ready_fwd; } static void socks5_forward_pkt(redudp_client *client, struct sockaddr *destaddr, void *buf, size_t pktlen) { - socks5_client *socks5client = (void*)(client + 1); - socks5_udp_preabmle req; - struct msghdr msg; - struct iovec io[2]; - ssize_t outgoing, fwdlen = pktlen + sizeof(req); - - socks5_fill_preamble(&req, (struct sockaddr_in *)destaddr); - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &socks5client->udprelayaddr; - msg.msg_namelen = sizeof(socks5client->udprelayaddr); - msg.msg_iov = io; - msg.msg_iovlen = SIZEOF_ARRAY(io); - - io[0].iov_base = &req; - io[0].iov_len = sizeof(req); - io[1].iov_base = buf; - io[1].iov_len = pktlen; - - outgoing = sendmsg(event_get_fd(&socks5client->udprelay), &msg, 0); - if (outgoing == -1) { - redudp_log_errno(client, LOG_WARNING, "sendmsg: Can't forward packet, dropping it"); - return; - } - else if (outgoing != fwdlen) { - redudp_log_error(client, LOG_WARNING, "sendmsg: I was sending %zd bytes, but only %zd were sent.", fwdlen, outgoing); - return; - } + socks5_client *socks5client = (void*)(client + 1); + socks5_udp_preabmle req; + struct msghdr msg; + struct iovec io[2]; + ssize_t outgoing, fwdlen = pktlen + sizeof(req); + + socks5_fill_preamble(&req, (struct sockaddr_in *)destaddr); + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &socks5client->udprelayaddr; + msg.msg_namelen = sizeof(socks5client->udprelayaddr); + msg.msg_iov = io; + msg.msg_iovlen = SIZEOF_ARRAY(io); + + io[0].iov_base = &req; + io[0].iov_len = sizeof(req); + io[1].iov_base = buf; + io[1].iov_len = pktlen; + + outgoing = sendmsg(event_get_fd(&socks5client->udprelay), &msg, 0); + if (outgoing == -1) { + redudp_log_errno(client, LOG_WARNING, "sendmsg: Can't forward packet, dropping it"); + return; + } + else if (outgoing != fwdlen) { + redudp_log_error(client, LOG_WARNING, "sendmsg: I was sending %zd bytes, but only %zd were sent.", fwdlen, outgoing); + return; + } } static void socks5_pkt_from_socks(int fd, short what, void *_arg) { - redudp_client *client = _arg; - socks5_client *socks5client = (void*)(client + 1); - union { - char buf[MAX_UDP_PACKET_SIZE]; - socks5_udp_preabmle header; - } * pkt = client->instance->shared_buff; - ssize_t pktlen, fwdlen; - struct sockaddr_in udprelayaddr; - - assert(fd == event_get_fd(&socks5client->udprelay)); - - pktlen = red_recv_udp_pkt(fd, pkt->buf, MAX_UDP_PACKET_SIZE, &udprelayaddr, NULL); - if (pktlen == -1) - return; - - if (memcmp(&udprelayaddr, &socks5client->udprelayaddr, sizeof(udprelayaddr)) != 0) { - char buf[RED_INET_ADDRSTRLEN]; - redudp_log_error(client, LOG_NOTICE, "Got packet from unexpected address %s.", - red_inet_ntop(&udprelayaddr, buf, sizeof(buf))); - return; - } - - if (pkt->header.frag_no != 0) { - // FIXME: does anybody need it? - redudp_log_error(client, LOG_WARNING, "Got fragment #%u. Packet fragmentation is not supported!", - pkt->header.frag_no); - return; - } - - if (pkt->header.addrtype != socks5_addrtype_ipv4) { - redudp_log_error(client, LOG_NOTICE, "Got address type #%u instead of expected #%u (IPv4).", - pkt->header.addrtype, socks5_addrtype_ipv4); - return; - } - - struct sockaddr_in pktaddr = { - .sin_family = AF_INET, - .sin_addr = { pkt->header.ip.addr }, - .sin_port = pkt->header.ip.port, - }; - - fwdlen = pktlen - sizeof(pkt->header); - redudp_fwd_pkt_to_sender(client, pkt->buf + sizeof(pkt->header), fwdlen, &pktaddr); + redudp_client *client = _arg; + socks5_client *socks5client = (void*)(client + 1); + union { + char buf[MAX_UDP_PACKET_SIZE]; + socks5_udp_preabmle header; + } * pkt = client->instance->shared_buff; + ssize_t pktlen, fwdlen; + struct sockaddr_in udprelayaddr; + + assert(fd == event_get_fd(&socks5client->udprelay)); + + pktlen = red_recv_udp_pkt(fd, pkt->buf, MAX_UDP_PACKET_SIZE, &udprelayaddr, NULL); + if (pktlen == -1) + return; + + if (memcmp(&udprelayaddr, &socks5client->udprelayaddr, sizeof(udprelayaddr)) != 0) { + char buf[RED_INET_ADDRSTRLEN]; + redudp_log_error(client, LOG_NOTICE, "Got packet from unexpected address %s.", + red_inet_ntop(&udprelayaddr, buf, sizeof(buf))); + return; + } + + if (pkt->header.frag_no != 0) { + // FIXME: does anybody need it? + redudp_log_error(client, LOG_WARNING, "Got fragment #%u. Packet fragmentation is not supported!", + pkt->header.frag_no); + return; + } + + if (pkt->header.addrtype != socks5_addrtype_ipv4) { + redudp_log_error(client, LOG_NOTICE, "Got address type #%u instead of expected #%u (IPv4).", + pkt->header.addrtype, socks5_addrtype_ipv4); + return; + } + + struct sockaddr_in pktaddr = { + .sin_family = AF_INET, + .sin_addr = { pkt->header.ip.addr }, + .sin_port = pkt->header.ip.port, + }; + + fwdlen = pktlen - sizeof(pkt->header); + redudp_fwd_pkt_to_sender(client, pkt->buf + sizeof(pkt->header), fwdlen, &pktaddr); } static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg) { - redudp_client *client = _arg; - socks5_client *socks5client = (void*)(client + 1); - socks5_expected_assoc_reply reply; - int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply)); - int fd = -1; - int error; - redudp_log_error(client, LOG_DEBUG, ""); - - if (read != sizeof(reply)) { - redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", - read, sizeof(reply)); - goto fail; - } - - if (reply.h.ver != socks5_ver) { - redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected reply version: %u", reply.h.ver); - goto fail; - } - - if (reply.h.status != socks5_status_succeeded) { - redudp_log_error(client, LOG_NOTICE, "Socks5 server status: \"%s\" (%i)", - socks5_status_to_str(reply.h.status), reply.h.status); - goto fail; - } - - if (reply.h.addrtype != socks5_addrtype_ipv4) { - redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected address type for UDP dgram destination: %u", - reply.h.addrtype); - goto fail; - } - - socks5client->udprelayaddr.sin_family = AF_INET; - socks5client->udprelayaddr.sin_port = reply.ip.port; - socks5client->udprelayaddr.sin_addr.s_addr = reply.ip.addr; - - fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (fd == -1) { - redudp_log_errno(client, LOG_ERR, "socket"); - goto fail; - } + redudp_client *client = _arg; + socks5_client *socks5client = (void*)(client + 1); + socks5_expected_assoc_reply reply; + int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply)); + int fd = -1; + int error; + redudp_log_error(client, LOG_DEBUG, ""); + + if (read != sizeof(reply)) { + redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", + read, sizeof(reply)); + goto fail; + } + + if (reply.h.ver != socks5_ver) { + redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected reply version: %u", reply.h.ver); + goto fail; + } + + if (reply.h.status != socks5_status_succeeded) { + redudp_log_error(client, LOG_NOTICE, "Socks5 server status: \"%s\" (%i)", + socks5_status_to_str(reply.h.status), reply.h.status); + goto fail; + } + + if (reply.h.addrtype != socks5_addrtype_ipv4) { + redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected address type for UDP dgram destination: %u", + reply.h.addrtype); + goto fail; + } + + socks5client->udprelayaddr.sin_family = AF_INET; + socks5client->udprelayaddr.sin_port = reply.ip.port; + socks5client->udprelayaddr.sin_addr.s_addr = reply.ip.addr; + + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd == -1) { + redudp_log_errno(client, LOG_ERR, "socket"); + goto fail; + } error = evutil_make_socket_nonblocking(fd); if (error) { @@ -238,158 +238,164 @@ static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg) } - error = connect(fd, (struct sockaddr*)&socks5client->udprelayaddr, sizeof(socks5client->udprelayaddr)); - if (error) { - redudp_log_errno(client, LOG_NOTICE, "connect"); - goto fail; - } + error = connect(fd, (struct sockaddr*)&socks5client->udprelayaddr, sizeof(socks5client->udprelayaddr)); + if (error) { + redudp_log_errno(client, LOG_NOTICE, "connect"); + goto fail; + } - event_assign(&socks5client->udprelay, get_event_base(), fd, EV_READ | EV_PERSIST, socks5_pkt_from_socks, client); - error = event_add(&socks5client->udprelay, NULL); - if (error) { - redudp_log_errno(client, LOG_ERR, "event_add"); - goto fail; - } + event_assign(&socks5client->udprelay, get_event_base(), fd, EV_READ | EV_PERSIST, socks5_pkt_from_socks, client); + error = event_add(&socks5client->udprelay, NULL); + if (error) { + redudp_log_errno(client, LOG_ERR, "event_add"); + goto fail; + } - socks5client->ready_fwd = 1; - redudp_flush_queue(client); - // TODO: bufferevent_disable ? + socks5client->ready_fwd = 1; + redudp_flush_queue(client); + // TODO: bufferevent_disable ? - return; + return; fail: - if (fd != -1) - close(fd); - redudp_drop_client(client); + if (fd != -1) + close(fd); + redudp_drop_client(client); } static void socks5_read_auth_reply(struct bufferevent *buffev, void *_arg) { - redudp_client *client = _arg; - socks5_client *socks5client = (void*)(client + 1); - socks5_auth_reply reply; - int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply)); - int error; - redudp_log_error(client, LOG_DEBUG, ""); - - if (read != sizeof(reply)) { - redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", - read, sizeof(reply)); - goto fail; - } - - if (reply.ver != socks5_password_ver || reply.status != socks5_password_passed) { - redudp_log_error(client, LOG_NOTICE, "Socks5 authentication error. Version: %u, error code: %u", - reply.ver, reply.status); - goto fail; - } - - error = redsocks_write_helper_ex_plain( - socks5client->relay, NULL, socks5_mkassociate, NULL, 0, /* last two are ignored */ - sizeof(socks5_expected_assoc_reply), sizeof(socks5_expected_assoc_reply)); - if (error) - goto fail; - - replace_readcb(socks5client->relay, socks5_read_assoc_reply); - return; + redudp_client *client = _arg; + socks5_client *socks5client = (void*)(client + 1); + socks5_auth_reply reply; + int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply)); + int error; + redudp_log_error(client, LOG_DEBUG, ""); + + if (read != sizeof(reply)) { + redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", + read, sizeof(reply)); + goto fail; + } + + if (reply.ver != socks5_password_ver || reply.status != socks5_password_passed) { + redudp_log_error(client, LOG_NOTICE, "Socks5 authentication error. Version: %u, error code: %u", + reply.ver, reply.status); + goto fail; + } + + error = redsocks_write_helper_ex_plain( + socks5client->relay, NULL, socks5_mkassociate, NULL, 0, /* last two are ignored */ + sizeof(socks5_expected_assoc_reply), sizeof(socks5_expected_assoc_reply)); + if (error) + goto fail; + + replace_readcb(socks5client->relay, socks5_read_assoc_reply); + return; fail: - redudp_drop_client(client); + redudp_drop_client(client); } static void socks5_read_auth_methods(struct bufferevent *buffev, void *_arg) { - redudp_client *client = _arg; - socks5_client *socks5client = (void*)(client + 1); - int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password); - socks5_method_reply reply; - int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply)); - const char *error = NULL; - int ierror = 0; - redudp_log_error(client, LOG_DEBUG, ""); - - if (read != sizeof(reply)) { - redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", - read, sizeof(reply)); - goto fail; - } - - error = socks5_is_known_auth_method(&reply, do_password); - if (error) { - redudp_log_error(client, LOG_NOTICE, "socks5_is_known_auth_method: %s", error); - goto fail; - } - else if (reply.method == socks5_auth_none) { - ierror = redsocks_write_helper_ex_plain( - socks5client->relay, NULL, socks5_mkassociate, NULL, 0, /* last two are ignored */ - sizeof(socks5_expected_assoc_reply), sizeof(socks5_expected_assoc_reply)); - if (ierror) - goto fail; - - replace_readcb(socks5client->relay, socks5_read_assoc_reply); - } - else if (reply.method == socks5_auth_password) { - ierror = redsocks_write_helper_ex_plain( - socks5client->relay, NULL, socks5_mkpassword_plain_wrapper, client->instance, 0, /* last one is ignored */ - sizeof(socks5_auth_reply), sizeof(socks5_auth_reply)); - if (ierror) - goto fail; - - replace_readcb(socks5client->relay, socks5_read_auth_reply); - } - - return; + redudp_client *client = _arg; + socks5_client *socks5client = (void*)(client + 1); + int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password); + socks5_method_reply reply; + int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply)); + const char *error = NULL; + int ierror = 0; + redudp_log_error(client, LOG_DEBUG, ""); + + if (read != sizeof(reply)) { + redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", + read, sizeof(reply)); + goto fail; + } + + error = socks5_is_known_auth_method(&reply, do_password); + if (error) { + redudp_log_error(client, LOG_NOTICE, "socks5_is_known_auth_method: %s", error); + goto fail; + } + else if (reply.method == socks5_auth_none) { + ierror = redsocks_write_helper_ex_plain( + socks5client->relay, NULL, socks5_mkassociate, NULL, 0, /* last two are ignored */ + sizeof(socks5_expected_assoc_reply), sizeof(socks5_expected_assoc_reply)); + if (ierror) + goto fail; + + replace_readcb(socks5client->relay, socks5_read_assoc_reply); + } + else if (reply.method == socks5_auth_password) { + ierror = redsocks_write_helper_ex_plain( + socks5client->relay, NULL, socks5_mkpassword_plain_wrapper, client->instance, 0, /* last one is ignored */ + sizeof(socks5_auth_reply), sizeof(socks5_auth_reply)); + if (ierror) + goto fail; + + replace_readcb(socks5client->relay, socks5_read_auth_reply); + } + + return; fail: - redudp_drop_client(client); + redudp_drop_client(client); } static void socks5_relay_connected(struct bufferevent *buffev, void *_arg) { - redudp_client *client = _arg; - socks5_client *socks5client = (void*)(client + 1); - int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password); - int error; - char relayaddr_str[RED_INET_ADDRSTRLEN]; - redudp_log_error(client, LOG_DEBUG, "via %s", red_inet_ntop(&client->instance->config.relayaddr, relayaddr_str, sizeof(relayaddr_str))); - - if (!red_is_socket_connected_ok(buffev)) { - redudp_log_errno(client, LOG_NOTICE, "red_is_socket_connected_ok"); - goto fail; - } - - error = redsocks_write_helper_ex_plain( - socks5client->relay, NULL, socks5_mkmethods_plain_wrapper, &do_password, 0 /* does not matter */, - sizeof(socks5_method_reply), sizeof(socks5_method_reply)); - if (error) - goto fail; - - replace_readcb(socks5client->relay, socks5_read_auth_methods); - replace_writecb(socks5client->relay, NULL); - //bufferevent_disable(buffev, EV_WRITE); // I don't want to check for writeability. - return; + redudp_client *client = _arg; + socks5_client *socks5client = (void*)(client + 1); + int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password); + int error; + char relayaddr_str[RED_INET_ADDRSTRLEN]; + redudp_log_error(client, LOG_DEBUG, "via %s", red_inet_ntop(&client->instance->config.relayaddr, relayaddr_str, sizeof(relayaddr_str))); + + if (!red_is_socket_connected_ok(buffev)) { + redudp_log_errno(client, LOG_NOTICE, "red_is_socket_connected_ok"); + goto fail; + } + + error = redsocks_write_helper_ex_plain( + socks5client->relay, NULL, socks5_mkmethods_plain_wrapper, &do_password, 0 /* does not matter */, + sizeof(socks5_method_reply), sizeof(socks5_method_reply)); + if (error) + goto fail; + + replace_readcb(socks5client->relay, socks5_read_auth_methods); + replace_writecb(socks5client->relay, NULL); + //bufferevent_disable(buffev, EV_WRITE); // I don't want to check for writeability. + return; fail: - redudp_drop_client(client); + redudp_drop_client(client); } static void socks5_relay_error(struct bufferevent *buffev, short what, void *_arg) { - redudp_client *client = _arg; - // TODO: FIXME: Implement me - redudp_log_error(client, LOG_NOTICE, "socks5_relay_error"); - redudp_drop_client(client); + redudp_client *client = _arg; + // TODO: FIXME: Implement me + redudp_log_error(client, LOG_NOTICE, "socks5_relay_error"); + redudp_drop_client(client); } static void socks5_connect_relay(redudp_client *client) { - socks5_client *socks5client = (void*)(client + 1); - socks5client->relay = red_connect_relay(NULL, &client->instance->config.relayaddr, NULL, - socks5_relay_connected, socks5_relay_error, client, NULL); - if (!socks5client->relay) - redudp_drop_client(client); + socks5_client *socks5client = (void*)(client + 1); + socks5client->relay = red_connect_relay( + NULL, + (struct sockaddr *)&client->instance->config.relayaddr, + NULL, + socks5_relay_connected, + socks5_relay_error, + client, + NULL); + if (!socks5client->relay) + redudp_drop_client(client); } static int socks5_instance_init(struct redudp_instance_t *instance) @@ -403,16 +409,16 @@ static void socks5_instance_fini(struct redudp_instance_t *instance) udprelay_subsys socks5_udp_subsys = { - .name = "socks5", - .payload_len = sizeof(socks5_client), - .instance_payload_len = 0, - .init = socks5_client_init, - .fini = socks5_client_fini, - .instance_init = socks5_instance_init, - .instance_fini = socks5_instance_fini, - .connect_relay = socks5_connect_relay, - .forward_pkt = socks5_forward_pkt, - .ready_to_fwd = socks5_ready_to_fwd, + .name = "socks5", + .payload_len = sizeof(socks5_client), + .instance_payload_len = 0, + .init = socks5_client_init, + .fini = socks5_client_fini, + .instance_init = socks5_instance_init, + .instance_fini = socks5_instance_fini, + .connect_relay = socks5_connect_relay, + .forward_pkt = socks5_forward_pkt, + .ready_to_fwd = socks5_ready_to_fwd, }; diff --git a/tcpdns.c b/tcpdns.c index cba24bee..a60a0d09 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -319,7 +319,7 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) return; } /* connect to target directly without going through proxy */ - req->resolver = red_connect_relay(NULL, destaddr, + req->resolver = red_connect_relay(NULL, (struct sockaddr *)destaddr, tcpdns_readcb, tcpdns_connected, tcpdns_event_error, req, &tv); if (req->resolver) diff --git a/utils.c b/utils.c index 9fe148e5..ae84b2ca 100644 --- a/utils.c +++ b/utils.c @@ -110,6 +110,7 @@ time_t redsocks_time(time_t *t) } struct bufferevent* red_prepare_relay(const char *ifname, + int sa_family, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, @@ -119,7 +120,7 @@ struct bufferevent* red_prepare_relay(const char *ifname, int relay_fd = -1; int error; - relay_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + relay_fd = socket(sa_family, SOCK_STREAM, IPPROTO_TCP); if (relay_fd == -1) { log_errno(LOG_ERR, "socket"); goto fail; @@ -172,7 +173,7 @@ struct bufferevent* red_prepare_relay(const char *ifname, } struct bufferevent* red_connect_relay(const char *ifname, - struct sockaddr_in *addr, + struct sockaddr *addr, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, @@ -183,15 +184,15 @@ struct bufferevent* red_connect_relay(const char *ifname, int relay_fd = -1; int error; - retval = red_prepare_relay(ifname, readcb, writecb, errorcb, cbarg); + retval = red_prepare_relay(ifname, addr->sa_family, readcb, writecb, errorcb, cbarg); if (retval) { relay_fd = bufferevent_getfd(retval); if (timeout_write) bufferevent_set_timeouts(retval, NULL, timeout_write); - // error = bufferevent_socket_connect(retval, (struct sockaddr*)addr, sizeof(*addr)); + // error = bufferevent_socket_connect(retval, addr, sizeof(*addr)); // if (error) { - error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); + error = connect(relay_fd, addr, sizeof(struct sockaddr_storage)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "connect"); goto fail; @@ -211,7 +212,7 @@ struct bufferevent* red_connect_relay(const char *ifname, #if defined(ENABLE_HTTPS_PROXY) struct bufferevent* red_connect_relay_ssl(const char *ifname, - struct sockaddr_in *addr, + struct sockaddr *addr, SSL * ssl, bufferevent_data_cb readcb, bufferevent_data_cb writecb, @@ -224,14 +225,14 @@ struct bufferevent* red_connect_relay_ssl(const char *ifname, int relay_fd = -1; int error; - underlying = red_prepare_relay(ifname, NULL, NULL, NULL, NULL); + underlying = red_prepare_relay(ifname, addr->sa_family, NULL, NULL, NULL, NULL); if (!underlying) goto fail; relay_fd = bufferevent_getfd(underlying); if (timeout_write) bufferevent_set_timeouts(underlying, NULL, timeout_write); - error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); + error = connect(relay_fd, addr, sizeof(struct sockaddr_storage)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "connect"); goto fail; @@ -274,7 +275,7 @@ struct bufferevent* red_connect_relay_ssl(const char *ifname, #endif struct bufferevent* red_connect_relay_tfo(const char *ifname, - struct sockaddr_in *addr, + struct sockaddr *addr, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, @@ -287,7 +288,7 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, int relay_fd = -1; int error; - retval = red_prepare_relay(ifname, readcb, writecb, errorcb, cbarg); + retval = red_prepare_relay(ifname, addr->sa_family, readcb, writecb, errorcb, cbarg); if (retval) { relay_fd = bufferevent_getfd(retval); if (timeout_write) @@ -295,7 +296,7 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, #ifdef MSG_FASTOPEN size_t s = sendto(relay_fd, data, * len, MSG_FASTOPEN, - (struct sockaddr *)addr, sizeof(*addr) + addr, sizeof(struct sockaddr_storage) ); *len = 0; // Assume nothing sent, caller needs to write data again when connection is setup. if (s == -1) { @@ -323,7 +324,7 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, fallback: #endif - error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); + error = connect(relay_fd, addr, sizeof(struct sockaddr_storage)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "connect"); goto fail; diff --git a/utils.h b/utils.h index 3e8c2220..ed8b5764 100644 --- a/utils.h +++ b/utils.h @@ -58,12 +58,13 @@ uint32_t red_randui32(); time_t redsocks_time(time_t *t); char *redsocks_evbuffer_readline(struct evbuffer *buf); struct bufferevent* red_prepare_relay(const char *ifname, + int sa_family, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, void *cbarg); struct bufferevent* red_connect_relay(const char *ifname, - struct sockaddr_in *addr, + struct sockaddr *addr, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, @@ -71,7 +72,7 @@ struct bufferevent* red_connect_relay(const char *ifname, const struct timeval *timeout_write); #if defined(ENABLE_HTTPS_PROXY) struct bufferevent* red_connect_relay_ssl(const char *ifname, - struct sockaddr_in *addr, + struct sockaddr *addr, SSL * ssl, bufferevent_data_cb readcb, bufferevent_data_cb writecb, @@ -80,7 +81,7 @@ struct bufferevent* red_connect_relay_ssl(const char *ifname, const struct timeval *timeout_write); #endif struct bufferevent* red_connect_relay_tfo(const char *ifname, - struct sockaddr_in *addr, + struct sockaddr *addr, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, From ea2b56a7036f86c2afe876bf010d2f0f90c20782 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 27 Sep 2019 00:37:05 +0800 Subject: [PATCH 136/192] One more step to support IPv6 NOTE: not verified --- README.md | 41 +++++++------------ autoproxy.c | 20 ++++++---- base.c | 26 +++++++++--- base.h | 2 +- direct.c | 2 +- http-connect.c | 19 ++++----- http-relay.c | 22 +++++------ ipcache.c | 4 +- redsocks.c | 39 ++++++++++-------- redsocks.conf.example | 39 +++++++----------- redsocks.h | 13 +++--- redudp.c | 84 +++++++++++++++++++++++++-------------- redudp.h | 19 +++++---- shadowsocks-udp.c | 92 ++++++++++++++++++++++++++++++------------- shadowsocks.c | 30 ++++++++++---- shadowsocks.h | 12 ++++++ socks4.c | 5 ++- socks5-udp.c | 8 ++-- socks5.c | 1 + tcpdns.c | 87 ++++++++++++++++++++-------------------- tcpdns.h | 13 +++--- utils.c | 39 ++++++++++-------- utils.h | 10 ++--- 23 files changed, 362 insertions(+), 265 deletions(-) diff --git a/README.md b/README.md index e5f35c59..ce6767a4 100644 --- a/README.md +++ b/README.md @@ -90,10 +90,8 @@ To use the autoproxy feature, please change the redsocks section in configuration file like this: redsocks { - local_ip = 192.168.1.1; - local_port = 1081; - ip = 192.168.1.1; - port = 9050; + bind = "192.168.1.1:1081"; + relay = "192.168.1.1:9050"; type = socks5; // I use socks5 proxy for GFW'ed IP autoproxy = 1; // I want autoproxy feature enabled on this section. // timeout is meaningful when 'autoproxy' is non-zero. @@ -114,8 +112,7 @@ all blocked traffic pass through via VPN connection while normal traffic pass through via default internet connection. redsocks { - local_ip = 192.168.1.1; - local_port = 1080; + bind = "192.168.1.1:1081"; interface = tun0; // Outgoing interface for blocked traffic type = direct; timeout = 13; @@ -127,11 +124,9 @@ Similar like other redsocks section. The encryption method is specified by field 'login'. redsocks { - local_ip = 192.168.1.1; - local_port = 1080; + bind = "192.168.1.1:1080"; type = shadowsocks; - ip = 192.168.1.1; - port = 8388; + relay = "192.168.1.1:8388"; timeout = 13; autoproxy = 1; login = "aes-128-cfb"; // field 'login' is reused as encryption @@ -140,15 +135,12 @@ by field 'login'. } redudp { - local_ip = 127.0.0.1; - local_port = 1053; - ip = your.ss-server.com; - port = 443; + bind = "127.0.0.1:1053"; + relay = "123.123.123.123:1082"; type = shadowsocks; login = rc4-md5; password = "ss server password"; - dest_ip = 8.8.8.8; - dest_port = 53; + dest = "8.8.8.8:53"; udp_timeout = 3; } @@ -191,10 +183,8 @@ Suppose your goagent local proxy is running at the same server as redsocks2, The configuration for forwarding connections to GoAgent is like below: redsocks { - local_ip = 192.168.1.1; - local_port = 1081; //HTTP should be redirect to this port. - ip = 192.168.1.1; - port = 8080; + bind = "192.168.1.1:1081"; //HTTP should be redirect to this port. + relay = "192.168.1.1:8080"; type = http-relay; // Must be 'htt-relay' for HTTP traffic. autoproxy = 1; // I want autoproxy feature enabled on this section. // timeout is meaningful when 'autoproxy' is non-zero. @@ -204,10 +194,8 @@ The configuration for forwarding connections to GoAgent is like below: timeout = 13; } redsocks { - local_ip = 192.168.1.1; - local_port = 1082; // HTTPS should be redirect to this port. - ip = 192.168.1.1; - port = 8080; + bind = "192.168.1.1:1082"; //HTTPS should be redirect to this port. + relay = "192.168.1.1:8080"; type = http-connect; // Must be 'htt-connect' for HTTPS traffic. autoproxy = 1; // I want autoproxy feature enabled on this section. // timeout is meaningful when 'autoproxy' is non-zero. @@ -226,9 +214,8 @@ with the following config section. // Transform UDP DNS requests into TCP DNS requests. // You can also redirect connections to external TCP DNS server to // REDSOCKS transparent proxy via iptables. - local_ip = 192.168.1.1; // Local server to act as DNS server - local_port = 1053; // UDP port to receive UDP DNS requests - tcpdns1 = 8.8.4.4; // DNS server that supports TCP DNS requests + bind = "192.168.1.1:1053"; // Local server to act as DNS server + tcpdns1 = "8.8.4.4:53"; // DNS server that supports TCP DNS requests tcpdns2 = 8.8.8.8; // DNS server that supports TCP DNS requests timeout = 4; // Timeout value for TCP DNS requests } diff --git a/autoproxy.c b/autoproxy.c index 3a8eda1a..dd0b7d09 100644 --- a/autoproxy.c +++ b/autoproxy.c @@ -194,7 +194,8 @@ static void on_connection_confirmed(redsocks_client *client) { redsocks_log_error(client, LOG_DEBUG, "IP Confirmed"); - cache_del_addr(&client->destaddr); + if (client->destaddr.ss_family == AF_INET) + cache_del_addr((struct sockaddr_in *)&client->destaddr); } static void on_connection_blocked(redsocks_client *client) @@ -494,11 +495,13 @@ static int auto_retry(redsocks_client * client, int updcache) if (updcache) { /* only add IP to cache when the IP is not in cache */ - if (cache_get_addr_time(&client->destaddr) == NULL) + if (client->destaddr.ss_family == AF_INET + && cache_get_addr_time((struct sockaddr_in *)&client->destaddr) == NULL) { - cache_add_addr(&client->destaddr); + char destaddr_str[RED_INET_ADDRSTRLEN]; + cache_add_addr((struct sockaddr_in *)&client->destaddr); redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %s", - inet_ntoa(client->destaddr.sin_addr)); + red_inet_ntop(&client->destaddr, destaddr_str, sizeof(destaddr_str))); } } @@ -616,8 +619,8 @@ static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) && what == (BEV_EVENT_WRITING | BEV_EVENT_TIMEOUT)) { // Update access time for IP fails again. - if (aclient->quick_check) - cache_touch_addr(&client->destaddr); + if (aclient->quick_check && client->destaddr.ss_family == AF_INET) + cache_touch_addr((struct sockaddr_in*)&client->destaddr); on_connection_blocked(client); /* In case timeout occurs while connecting relay, we try to connect @@ -685,7 +688,8 @@ static int auto_connect_relay(redsocks_client *client) if (aclient->state == AUTOPROXY_NEW) { - acc_time = cache_get_addr_time(&client->destaddr); + if (client->destaddr.ss_family == AF_INET) + acc_time = cache_get_addr_time((struct sockaddr_in *)&client->destaddr); if (acc_time) { redsocks_log_error(client, LOG_DEBUG, "Found dest IP in cache"); @@ -710,7 +714,7 @@ static int auto_connect_relay(redsocks_client *client) /* connect to target directly without going through proxy */ client->relay = red_connect_relay( config->interface, - (struct sockaddr *)&client->destaddr, + &client->destaddr, NULL, auto_relay_connected, auto_event_error, diff --git a/base.c b/base.c index 6454f4f5..db812499 100644 --- a/base.c +++ b/base.c @@ -61,7 +61,7 @@ typedef struct redirector_subsys_t { int (*init)(); void (*fini)(); - int (*getdestaddr)(int fd, const struct sockaddr_in *client, const struct sockaddr_in *bindaddr, struct sockaddr_in *destaddr); + int (*getdestaddr)(int fd, const struct sockaddr_storage *client, const struct sockaddr_storage *bindaddr, struct sockaddr_storage *destaddr); const char *name; // some subsystems may store data here: int private; @@ -124,7 +124,10 @@ static int redir_init_ipf() return redir_open_private(fname, O_RDONLY); } -static int getdestaddr_ipf(int fd, const struct sockaddr_in *client, const struct sockaddr_in *bindaddr, struct sockaddr_in *destaddr) +static int getdestaddr_ipf(int fd, + const struct sockaddr_storage *client, + const struct sockaddr_storage *bindaddr, + struct sockaddr_storage *destaddr) { int natfd = instance.redirector->private; struct natlookup natLookup; @@ -184,6 +187,7 @@ static int redir_init_pf() return redir_open_private("/dev/pf", O_RDWR); } +// FIXME: Support IPv6 static int getdestaddr_pf( int fd, const struct sockaddr_in *client, const struct sockaddr_in *bindaddr, struct sockaddr_in *destaddr) @@ -233,7 +237,11 @@ static int getdestaddr_pf( #endif #ifdef USE_IPTABLES -static int getdestaddr_iptables(int fd, const struct sockaddr_in *client, const struct sockaddr_in *bindaddr, struct sockaddr_in *destaddr) +static int getdestaddr_iptables( + int fd, + const struct sockaddr_storage *client, + const struct sockaddr_storage *bindaddr, + struct sockaddr_storage *destaddr) { socklen_t socklen = sizeof(*destaddr); int error; @@ -247,7 +255,11 @@ static int getdestaddr_iptables(int fd, const struct sockaddr_in *client, const } #endif -static int getdestaddr_generic(int fd, const struct sockaddr_in *client, const struct sockaddr_in *bindaddr, struct sockaddr_in *destaddr) +static int getdestaddr_generic( + int fd, + const struct sockaddr_storage *client, + const struct sockaddr_storage *bindaddr, + struct sockaddr_storage *destaddr) { socklen_t socklen = sizeof(*destaddr); int error; @@ -260,7 +272,11 @@ static int getdestaddr_generic(int fd, const struct sockaddr_in *client, const s return 0; } -int getdestaddr(int fd, const struct sockaddr_in *client, const struct sockaddr_in *bindaddr, struct sockaddr_in *destaddr) +int getdestaddr( + int fd, + const struct sockaddr_storage *client, + const struct sockaddr_storage *bindaddr, + struct sockaddr_storage *destaddr) { return instance.redirector->getdestaddr(fd, client, bindaddr, destaddr); } diff --git a/base.h b/base.h index 4c889f6e..5519ee2f 100644 --- a/base.h +++ b/base.h @@ -1,7 +1,7 @@ #ifndef BASE_H_SUN_JUN__3_20_15_57_2007 #define BASE_H_SUN_JUN__3_20_15_57_2007 -int getdestaddr(int fd, const struct sockaddr_in *client, const struct sockaddr_in *bindaddr, struct sockaddr_in *destaddr); +int getdestaddr(int fd, const struct sockaddr_storage *client, const struct sockaddr_storage *bindaddr, struct sockaddr_storage *destaddr); int apply_tcp_keepalive(int fd); int apply_reuseport(int fd); diff --git a/direct.c b/direct.c index 018abc39..9521fadb 100644 --- a/direct.c +++ b/direct.c @@ -67,7 +67,7 @@ static int direct_connect_relay(redsocks_client *client) // Allowing binding relay socket to specified IP for outgoing connections client->relay = red_connect_relay( interface, - (struct sockaddr *)&client->destaddr, + &client->destaddr, NULL, redsocks_relay_connected, redsocks_event_error, diff --git a/http-connect.c b/http-connect.c index cf98fdbf..245b3e69 100644 --- a/http-connect.c +++ b/http-connect.c @@ -187,6 +187,10 @@ struct evbuffer *httpc_mkconnect(redsocks_client *client) const char *auth_scheme = NULL; char *auth_string = NULL; + /* calculate uri */ + char uri[RED_INET_ADDRSTRLEN]; + red_inet_ntop(&client->destaddr, uri, sizeof(uri)); + if (auth->last_auth_query != NULL) { /* find previous auth challange */ @@ -194,10 +198,6 @@ struct evbuffer *httpc_mkconnect(redsocks_client *client) auth_string = basic_authentication_encode(client->instance->config.login, client->instance->config.password); auth_scheme = "Basic"; } else if (strncasecmp(auth->last_auth_query, "Digest", 6) == 0) { - /* calculate uri */ - char uri[128]; - snprintf(uri, 128, "%s:%u", inet_ntoa(client->destaddr.sin_addr), ntohs(client->destaddr.sin_port)); - /* prepare an random string for cnounce */ char cnounce[17]; snprintf(cnounce, sizeof(cnounce), "%08x%08x", red_randui32(), red_randui32()); @@ -210,16 +210,11 @@ struct evbuffer *httpc_mkconnect(redsocks_client *client) } if (auth_string == NULL) { - len = evbuffer_add_printf(buff, - "CONNECT %s:%u HTTP/1.0\r\n\r\n", - inet_ntoa(client->destaddr.sin_addr), - ntohs(client->destaddr.sin_port) - ); + len = evbuffer_add_printf(buff, "CONNECT %s HTTP/1.0\r\n\r\n", uri); } else { len = evbuffer_add_printf(buff, - "CONNECT %s:%u HTTP/1.0\r\n%s %s %s\r\n\r\n", - inet_ntoa(client->destaddr.sin_addr), - ntohs(client->destaddr.sin_port), + "CONNECT %s HTTP/1.0\r\n%s %s %s\r\n\r\n", + uri, auth_response_header, auth_scheme, auth_string diff --git a/http-relay.c b/http-relay.c index a13e6295..80515cba 100644 --- a/http-relay.c +++ b/http-relay.c @@ -382,17 +382,17 @@ static int httpr_append_header(redsocks_client *client, char *line) } // This function is not reenterable -static char *fmt_http_host(struct sockaddr_in addr) +static const char *fmt_http_host(struct sockaddr_storage * addr) { - static char host[] = "123.123.123.123:12345"; - if (ntohs(addr.sin_port) == 80) - return inet_ntoa(addr.sin_addr); + static char host[RED_INET_ADDRSTRLEN]; + + if ( + (addr->ss_family == AF_INET && ntohs(((struct sockaddr_in *)addr)->sin_port) == 80) + || (addr->ss_family == AF_INET6 && ntohs(((struct sockaddr_in6 *)addr)->sin6_port) == 80) + ) + return inet_ntop(addr->ss_family, addr, &host[0], sizeof(host)); else { - snprintf(host, sizeof(host), - "%s:%u", - inet_ntoa(addr.sin_addr), - ntohs(addr.sin_port) - ); + red_inet_ntop(addr, host, sizeof(host)); return host; } } @@ -401,7 +401,7 @@ static int httpr_toss_http_firstline(redsocks_client *client) { httpr_client *httpr = (void*)(client + 1); char *uri = NULL; - char *host = httpr->has_host ? httpr->host : fmt_http_host(client->destaddr); + const char *host = httpr->has_host ? httpr->host : fmt_http_host(&client->destaddr); static char nbuff[MAX_HTTP_REQUEST_LINE_LENGTH + 1]; size_t len = 0; @@ -525,7 +525,7 @@ static void httpr_client_read_cb(struct bufferevent *buffev, void *_arg) if (!httpr->has_host) { char host[32]; // "Host: 123.456.789.012:34567" int written_wo_null = snprintf(host, sizeof(host), "Host: %s", - fmt_http_host(client->destaddr)); + fmt_http_host(&client->destaddr)); UNUSED(written_wo_null); assert(0 < written_wo_null && written_wo_null < sizeof(host)); if (httpr_append_header(client, host) < 0) diff --git a/ipcache.c b/ipcache.c index 59c45ce7..48066be2 100644 --- a/ipcache.c +++ b/ipcache.c @@ -207,7 +207,7 @@ static int save_cache(const char * path) item = get_cache_data(blk, idx); if (item && item->present) { - red_inet_ntop(&item->addr, addr_str, sizeof(addr_str)); + red_inet_ntop((struct sockaddr_storage *)&item->addr, addr_str, sizeof(addr_str)); fprintf(f, "%s\n", addr_str); } } @@ -419,7 +419,7 @@ static void cache_dumper() if (item && item->present) { count++; - red_inet_ntop(&item->addr, addr_str[p], sizeof(addr_str[0])); + red_inet_ntop((struct sockaddr_storage *)&item->addr, addr_str[p], sizeof(addr_str[0])); p++; if (p == ADDR_COUNT_PER_LINE) { diff --git a/redsocks.c b/redsocks.c index ead3ca31..abb23880 100644 --- a/redsocks.c +++ b/redsocks.c @@ -153,8 +153,9 @@ static int redsocks_onenter(parser_section *section) INIT_LIST_HEAD(&instance->list); INIT_LIST_HEAD(&instance->clients); - instance->config.bindaddr.sin_family = AF_INET; - instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + struct sockaddr_in * addr = (struct sockaddr_in *)&instance->config.bindaddr; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* Default value can be checked in run-time, but I doubt anyone needs that. * Linux: sysctl net.core.somaxconn * FreeBSD: sysctl kern.ipc.somaxconn */ @@ -166,9 +167,8 @@ static int redsocks_onenter(parser_section *section) for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = - (strcmp(entry->key, "local_ip") == 0) ? (void*)&instance->config.bindaddr.sin_addr : - (strcmp(entry->key, "local_port") == 0) ? (void*)&instance->config.bindaddr.sin_port : (strcmp(entry->key, "interface") == 0) ? (void*)&instance->config.interface : + (strcmp(entry->key, "bind") == 0) ? (void*)&instance->config.bind : (strcmp(entry->key, "relay") == 0) ? (void*)&instance->config.relay : (strcmp(entry->key, "type") == 0) ? (void*)&instance->config.type : (strcmp(entry->key, "login") == 0) ? (void*)&instance->config.login : @@ -196,16 +196,20 @@ static int redsocks_onexit(parser_section *section) for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = NULL; - // Parse and update relay address - struct sockaddr * addr = (struct sockaddr *)&instance->config.relayaddr; - int addr_size = sizeof(instance->config.relayaddr); - if (instance->config.relay) { + // Parse and update bind address and relay address + if (instance->config.bind) { + struct sockaddr * addr = (struct sockaddr *)&instance->config.bindaddr; + int addr_size = sizeof(instance->config.bindaddr); + if (evutil_parse_sockaddr_port(instance->config.bind, addr, &addr_size)) + err = "invalid bind address"; + } + if (!err && instance->config.relay) { + struct sockaddr * addr = (struct sockaddr *)&instance->config.relayaddr; + int addr_size = sizeof(instance->config.relayaddr); if (evutil_parse_sockaddr_port(instance->config.relay, addr, &addr_size)) - err = "invalid relay address"; + err = "invalid relay address"; } - instance->config.bindaddr.sin_port = htons(instance->config.bindaddr.sin_port); - if (!err && instance->config.type) { relay_subsys **ss; FOREACH(ss, relay_subsystems) { @@ -252,7 +256,8 @@ static parser_section redsocks_conf_section = void redsocks_log_write_plain( const char *file, int line, const char *func, int do_errno, - const struct sockaddr_in *clientaddr, const struct sockaddr_in *destaddr, + const struct sockaddr_storage *clientaddr, + const struct sockaddr_storage *destaddr, int priority, const char *orig_fmt, ... ) { int saved_errno = errno; @@ -681,7 +686,7 @@ int redsocks_connect_relay(redsocks_client *client) // Allowing binding relay socket to specified IP for outgoing connections client->relay = red_connect_relay(interface, - (struct sockaddr *)&client->instance->config.relayaddr, + &client->instance->config.relayaddr, NULL, redsocks_relay_connected, redsocks_event_error, client, &tv); @@ -736,10 +741,10 @@ static void redsocks_accept_client(int fd, short what, void *_arg) { redsocks_instance *self = _arg; redsocks_client *client = NULL; - struct sockaddr_in clientaddr; - struct sockaddr_in myaddr; - struct sockaddr_in destaddr; - socklen_t addrlen = sizeof(clientaddr); + struct sockaddr_storage clientaddr; + struct sockaddr_storage myaddr; + struct sockaddr_storage destaddr; + socklen_t addrlen = sizeof(clientaddr); int client_fd = -1; int error; diff --git a/redsocks.conf.example b/redsocks.conf.example index 6b617663..3a641536 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -53,12 +53,11 @@ base { } redsocks { - /* `local_ip' defaults to 127.0.0.1 for security reasons, + /* `bind' defaults to 127.0.0.1:0 for security reasons, * use 0.0.0.0 if you want to listen on every interface. - * `local_*' are used as port to redirect to. + * `bind' are used as ip:port to redirect to. */ - local_ip = 127.0.0.1; - local_port = 12345; + bind = "127.0.0.1:12345"; // listen() queue length. Default value is SOMAXCONN and it should be // good enough for most of us. @@ -72,9 +71,6 @@ redsocks { // min_accept_backoff = 100; // max_accept_backoff = 60000; - // `ip' and `port' are IP and tcp-port of proxy-server - // These two parameters are removed to support IPv6. - // `relay' is IP address and port of proxy-server. Domain name is not // supported yet. // Can be: @@ -113,15 +109,13 @@ redsocks { } redudp { - // `local_ip' should not be 0.0.0.0 as it's also used for outgoing + // `bind' should not be 0.0.0.0:0 as it's also used for outgoing // packets that are sent as replies - and it should be fixed // if we want NAT to work properly. - local_ip = 127.0.0.1; - local_port = 10053; + bind = "127.0.0.1:10053"; - // `ip' and `port' of socks5 proxy server. - ip = 10.0.0.1; - port = 1080; + // `relay' is ip and port of socks5 proxy server. + relay = "10.0.0.1:1080"; login = username;// field 'login' is reused as encryption // method of shadowsocks password = pazzw0rd; @@ -133,12 +127,10 @@ redudp { // linux: TPROXY and REDIRECT. TPROXY requires more complex routing // configuration and fresh kernel (>= 2.6.37 according to squid // developers[1]) but has hack-free way to get original destination - // address, REDIRECT is easier to configure, but requires `dest_ip` and - // `dest_port` to be set, limiting packet redirection to single - // destination. + // address, REDIRECT is easier to configure, but requires `dest` + // to be set, limiting packet redirection to single destination. // [1] http://wiki.squid-cache.org/Features/Tproxy4 - dest_ip = 8.8.8.8; - dest_port = 53; + dest = "8.8.8.8:53"; // Do not set it large if this section is for DNS requests. Otherwise, // you may encounter out of file descriptor problem. For DNS requests, @@ -151,13 +143,10 @@ tcpdns { // Transform UDP DNS requests into TCP DNS requests. // You can also redirect connections to external TCP DNS server to // REDSOCKS transparent proxy via iptables. - local_ip = 192.168.1.1; // Local server to act as DNS server - local_port = 1053; // UDP port to receive UDP DNS requests - tcpdns1 = 8.8.4.4; // DNS server that supports TCP DNS requests - tcpdns1_port = 53; // DNS server port, default 53 - tcpdns2 = 8.8.8.8; // DNS server that supports TCP DNS requests - tcpdns2_port = 53; // DNS server port, default 53 - timeout = 4; // Timeout value for TCP DNS requests + bind = "192.168.1.1:1053"; // Local server to act as DNS server + tcpdns1 = "8.8.4.4:53"; // DNS server that supports TCP DNS requests + tcpdns2 = "8.8.8.8" ; // DNS server that supports TCP DNS requests + timeout = 4; // Timeout value for TCP DNS requests } autoproxy { diff --git a/redsocks.h b/redsocks.h index 15d281b5..1437dfb1 100644 --- a/redsocks.h +++ b/redsocks.h @@ -29,8 +29,9 @@ typedef struct relay_subsys_t { } relay_subsys; typedef struct redsocks_config_t { - struct sockaddr_in bindaddr; + struct sockaddr_storage bindaddr; struct sockaddr_storage relayaddr; + char *bind; char *relay; char *type; char *login; @@ -63,8 +64,8 @@ typedef struct redsocks_client_t { redsocks_instance *instance; struct bufferevent *client; struct bufferevent *relay; - struct sockaddr_in clientaddr; - struct sockaddr_in destaddr; + struct sockaddr_storage clientaddr; + struct sockaddr_storage destaddr; int state; // it's used by bottom layer short relay_connected; unsigned short client_evshut; @@ -107,12 +108,12 @@ int redsocks_write_helper( void redsocks_close_internal(int fd, const char* file, int line, const char *func); #define redsocks_log_error(client, prio, msg...) \ - redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &(client)->clientaddr, &(client)->destaddr, prio, ## msg) + redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, (struct sockaddr_storage *)&(client)->clientaddr, (struct sockaddr_storage *)&(client)->destaddr, prio, ## msg) #define redsocks_log_errno(client, prio, msg...) \ - redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &(client)->clientaddr, &(client)->destaddr, prio, ## msg) + redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, (struct sockaddr_storage *)&(client)->clientaddr, (struct sockaddr_storage *)&(client)->destaddr, prio, ## msg) void redsocks_log_write_plain( const char *file, int line, const char *func, int do_errno, - const struct sockaddr_in *clientaddr, const struct sockaddr_in *destaddr, + const struct sockaddr_storage *clientaddr, const struct sockaddr_storage *destaddr, int priority, const char *fmt, ...) #if defined(__GNUC__) __attribute__ (( format (printf, 8, 9) )) diff --git a/redudp.c b/redudp.c index 78071a26..0ebf14f3 100644 --- a/redudp.c +++ b/redudp.c @@ -220,10 +220,10 @@ static void bound_udp4_action(const void *nodep, const VISIT which, const int de static int do_tproxy(redudp_instance* instance) { - return instance->config.destaddr.sin_addr.s_addr == 0; + return instance->config.dest == NULL; } -struct sockaddr_in* get_destaddr(redudp_client *client) +struct sockaddr_storage* get_destaddr(redudp_client *client) { if (do_tproxy(client->instance)) return &client->destaddr; @@ -268,7 +268,7 @@ void redudp_bump_timeout(redudp_client *client) } void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, - struct sockaddr_in * srcaddr) + struct sockaddr_storage * srcaddr) { size_t sent; int fd; @@ -277,8 +277,9 @@ void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, // When working with TPROXY, we have to get sender FD from tree on // receipt of each packet from relay. - fd = do_tproxy(client->instance) ? bound_udp4_get(srcaddr) - : event_get_fd(client->instance->listener); + // FIXME: Support IPv6 + fd = (do_tproxy(client->instance) && srcaddr->ss_family == AF_INET) + ? bound_udp4_get((struct sockaddr_in*)srcaddr) : event_get_fd(client->instance->listener); if (fd == -1) { redudp_log_error(client, LOG_WARNING, "bound_udp4_get failure"); return; @@ -288,13 +289,21 @@ void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, sent = sendto(fd, buf, len, 0, (struct sockaddr*)&client->clientaddr, sizeof(client->clientaddr)); if (sent != len) { - redudp_log_error(client, LOG_WARNING, "sendto: I was sending %zd bytes, but only %zd were sent.", - len, sent); + redudp_log_error( + client, + LOG_WARNING, + "sendto: I was sending %zd bytes, but only %zd were sent.", + len, + sent); return; } } -static int redudp_enqeue_pkt(redudp_client *client, struct sockaddr_in * destaddr, char *buf, size_t pktlen) +static int redudp_enqeue_pkt( + redudp_client *client, + struct sockaddr_storage * destaddr, + char *buf, + size_t pktlen) { enqueued_packet *q = NULL; @@ -342,7 +351,12 @@ static void redudp_timeout(int fd, short what, void *_arg) redudp_drop_client(client); } -static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_in *clientaddr, struct sockaddr_in *destaddr, char *buf, size_t pktlen) +static void redudp_first_pkt_from_client( + redudp_instance *self, + struct sockaddr_storage *clientaddr, + struct sockaddr_storage *destaddr, + char *buf, + size_t pktlen) { redudp_client *client = calloc(1, sizeof(*client)+self->relay_ss->payload_len); if (!client) { @@ -382,7 +396,7 @@ static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_ static void redudp_pkt_from_client(int fd, short what, void *_arg) { redudp_instance *self = _arg; - struct sockaddr_in clientaddr, destaddr, *pdestaddr; + struct sockaddr_storage clientaddr, destaddr, *pdestaddr; ssize_t pktlen; redudp_client *tmp, *client = NULL; @@ -426,15 +440,12 @@ static void redudp_pkt_from_client(int fd, short what, void *_arg) */ static parser_entry redudp_entries[] = { - { .key = "local_ip", .type = pt_in_addr }, - { .key = "local_port", .type = pt_uint16 }, - { .key = "ip", .type = pt_in_addr }, - { .key = "port", .type = pt_uint16 }, + { .key = "bind", .type = pt_pchar }, + { .key = "relay", .type = pt_pchar }, + { .key = "dest", .type = pt_pchar }, { .key = "type", .type = pt_pchar }, { .key = "login", .type = pt_pchar }, { .key = "password", .type = pt_pchar }, - { .key = "dest_ip", .type = pt_in_addr }, - { .key = "dest_port", .type = pt_uint16 }, { .key = "udp_timeout", .type = pt_uint16 }, { .key = "udp_timeout_stream", .type = pt_uint16 }, { .key = "max_pktqueue", .type = pt_uint16 }, @@ -460,26 +471,21 @@ static int redudp_onenter(parser_section *section) INIT_LIST_HEAD(&instance->list); INIT_LIST_HEAD(&instance->clients); - instance->config.bindaddr.sin_family = AF_INET; - instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - instance->config.relayaddr.sin_family = AF_INET; - instance->config.relayaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - instance->config.destaddr.sin_family = AF_INET; + struct sockaddr_in * addr = (struct sockaddr_in *)&instance->config.bindaddr; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK); instance->config.max_pktqueue = DEFAULT_MAX_PKTQUEUE; instance->config.udp_timeout = DEFAULT_UDP_TIMEOUT; instance->config.udp_timeout_stream = 180; for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = - (strcmp(entry->key, "local_ip") == 0) ? (void*)&instance->config.bindaddr.sin_addr : - (strcmp(entry->key, "local_port") == 0) ? (void*)&instance->config.bindaddr.sin_port : - (strcmp(entry->key, "ip") == 0) ? (void*)&instance->config.relayaddr.sin_addr : - (strcmp(entry->key, "port") == 0) ? (void*)&instance->config.relayaddr.sin_port : + (strcmp(entry->key, "bind") == 0) ? (void*)&instance->config.bind : + (strcmp(entry->key, "relay") == 0) ? (void*)&instance->config.relay : + (strcmp(entry->key, "dest") == 0) ? (void*)&instance->config.dest : (strcmp(entry->key, "type") == 0) ? (void*)&instance->config.type : (strcmp(entry->key, "login") == 0) ? (void*)&instance->config.login : (strcmp(entry->key, "password") == 0) ? (void*)&instance->config.password : - (strcmp(entry->key, "dest_ip") == 0) ? (void*)&instance->config.destaddr.sin_addr : - (strcmp(entry->key, "dest_port") == 0) ? (void*)&instance->config.destaddr.sin_port : (strcmp(entry->key, "max_pktqueue") == 0) ? (void*)&instance->config.max_pktqueue : (strcmp(entry->key, "udp_timeout") == 0) ? (void*)&instance->config.udp_timeout: (strcmp(entry->key, "udp_timeout_stream") == 0) ? (void*)&instance->config.udp_timeout_stream : @@ -497,9 +503,27 @@ static int redudp_onexit(parser_section *section) for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = NULL; - instance->config.bindaddr.sin_port = htons(instance->config.bindaddr.sin_port); - instance->config.relayaddr.sin_port = htons(instance->config.relayaddr.sin_port); - instance->config.destaddr.sin_port = htons(instance->config.destaddr.sin_port); + // Parse and update bind address and relay address + if (instance->config.bind) { + struct sockaddr * addr = (struct sockaddr *)&instance->config.bindaddr; + int addr_size = sizeof(instance->config.bindaddr); + if (evutil_parse_sockaddr_port(instance->config.bind, addr, &addr_size)) + err = "invalid bind address"; + } + if (!err && instance->config.relay) { + struct sockaddr * addr = (struct sockaddr *)&instance->config.relayaddr; + int addr_size = sizeof(instance->config.relayaddr); + if (evutil_parse_sockaddr_port(instance->config.relay, addr, &addr_size)) + err = "invalid relay address"; + } + else if (!instance->config.relay) + err = "missing relay address"; + if (!err && instance->config.dest) { + struct sockaddr * addr = (struct sockaddr *)&instance->config.destaddr; + int addr_size = sizeof(instance->config.destaddr); + if (evutil_parse_sockaddr_port(instance->config.dest, addr, &addr_size)) + err = "invalid dest address"; + } if (instance->config.type) { udprelay_subsys **ss; diff --git a/redudp.h b/redudp.h index 29341e0e..70f55e60 100644 --- a/redudp.h +++ b/redudp.h @@ -26,10 +26,13 @@ typedef struct udprelay_subsys_t { typedef struct redudp_config_t { - struct sockaddr_in bindaddr; - struct sockaddr_in relayaddr; + struct sockaddr_storage bindaddr; + struct sockaddr_storage relayaddr; // TODO: outgoingaddr; - struct sockaddr_in destaddr; + struct sockaddr_storage destaddr; + char *bind; + char *relay; + char *dest; char *type; char *login; char *password; @@ -50,8 +53,8 @@ typedef struct redudp_instance_t { typedef struct redudp_client_t { list_head list; redudp_instance * instance; - struct sockaddr_in clientaddr; - struct sockaddr_in destaddr; + struct sockaddr_storage clientaddr; + struct sockaddr_storage destaddr; struct event * timeoutev; int state; // it's used by bottom layer time_t first_event; @@ -63,15 +66,15 @@ typedef struct redudp_client_t { typedef struct enqueued_packet_t { list_head list; - struct sockaddr_in destaddr; + struct sockaddr_storage destaddr; size_t len; char data[1]; } enqueued_packet; -struct sockaddr_in* get_destaddr(redudp_client *client); +struct sockaddr_storage* get_destaddr(redudp_client *client); void redudp_drop_client(redudp_client *client); void redudp_flush_queue(redudp_client *client); -void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, struct sockaddr_in * srcaddr); +void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, struct sockaddr_storage * srcaddr); void redudp_bump_timeout(redudp_client *client); #define redudp_log_error(client, prio, msg...) \ diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index 131503b9..561b2c84 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -72,31 +72,46 @@ static void ss_client_fini(redudp_client *client) } } -static void ss_forward_pkt(redudp_client *client, struct sockaddr * destaddr, void *data, size_t pktlen) +static void ss_forward_pkt(redudp_client *client, struct sockaddr* destaddr, void *data, size_t pktlen) { ss_client *ssclient = (void*)(client + 1); ss_instance * ss = (ss_instance *)(client->instance+1); - struct sockaddr_in * relayaddr = &client->instance->config.relayaddr; + struct sockaddr_storage * relayaddr = &client->instance->config.relayaddr; struct msghdr msg; struct iovec io[1]; ssize_t outgoing; int rc; - ss_header_ipv4 header; + ss_header header; size_t len = 0; + size_t header_len = 0; size_t fwdlen = 0; void * buff = client->instance->shared_buff; /* build and send header */ - // TODO: Better implementation and IPv6 Support - header.addr_type = ss_addrtype_ipv4; - header.addr = ((struct sockaddr_in *)destaddr)->sin_addr.s_addr; - header.port = ((struct sockaddr_in *)destaddr)->sin_port; + if (client->destaddr.ss_family == AF_INET) { + struct sockaddr_in * addr = (struct sockaddr_in *)&client->destaddr; + header.v4.addr_type = ss_addrtype_ipv4; + header.v4.addr = addr->sin_addr.s_addr; + header.v4.port = addr->sin_port; + header_len = sizeof(ss_header_ipv4); + } + else if (client->destaddr.ss_family == AF_INET6) { + struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&client->destaddr; + header.v6.addr_type = ss_addrtype_ipv6; + header.v6.addr = addr->sin6_addr; + header.v6.port = addr->sin6_port; + header_len = sizeof(ss_header_ipv6); + } + else { + redudp_log_error(client, LOG_ERR, "Unsupported address family: %d", client->destaddr.ss_family); + return ; + } if (enc_ctx_init(&ss->info, &ss->e_ctx, 1)) { redudp_log_error(client, LOG_ERR, "Shadowsocks UDP failed to initialize encryption context."); return; } - rc = ss_encrypt(&ss->e_ctx, (char *)&header, sizeof(header), buff, &len); + rc = ss_encrypt(&ss->e_ctx, (char *)&header, header_len, buff, &len); if (rc) { if (len + pktlen < MAX_UDP_PACKET_SIZE) @@ -137,10 +152,10 @@ static void ss_pkt_from_server(int fd, short what, void *_arg) redudp_client *client = _arg; ss_client *ssclient = (void*)(client + 1); ss_instance * ss = (ss_instance *)(client->instance+1); - ss_header_ipv4 * header; + ss_header * header; ssize_t pktlen; size_t fwdlen; - struct sockaddr_in udprelayaddr; + struct sockaddr_storage udprelayaddr; int rc; void * buff = client->instance->shared_buff; void * buff2 = ss->buff; @@ -161,26 +176,49 @@ static void ss_pkt_from_server(int fd, short what, void *_arg) redudp_log_error(client, LOG_DEBUG, "Can't decrypt packet, dropping it"); return; } - header = (ss_header_ipv4 *)buff2; + header = (ss_header*)buff2; // We do not verify src address, but at least, we need to ensure address type is correct. - if (header->addr_type != ss_addrtype_ipv4) { - redudp_log_error(client, LOG_DEBUG, "Got address type #%u instead of expected #%u (IPv4).", - header->addr_type, ss_addrtype_ipv4); - return; + if (header->addr_type == ss_addrtype_ipv4) { + struct sockaddr_in pktaddr = { + .sin_family = AF_INET, + .sin_addr = { header->v4.addr }, + .sin_port = header->v4.port, + }; + + if (fwdlen < sizeof(header->v4)) { + redudp_log_error(client, LOG_DEBUG, "Packet too short."); + return; + } + fwdlen -= sizeof(*header); + redudp_fwd_pkt_to_sender( + client, + buff2 + sizeof(*header), + fwdlen, + (struct sockaddr_storage*)&pktaddr); } - - struct sockaddr_in pktaddr = { - .sin_family = AF_INET, - .sin_addr = { header->addr }, - .sin_port = header->port, - }; - - if (fwdlen < sizeof(*header)) { - redudp_log_error(client, LOG_DEBUG, "Packet too short."); + else if (header->addr_type == ss_addrtype_ipv6) { + struct sockaddr_in6 pktaddr = { + .sin6_family = AF_INET, + .sin6_port = header->v6.port, + }; + memcpy(&pktaddr.sin6_addr, &header->v6.addr, sizeof(header->v6.addr)); + + if (fwdlen < sizeof(header->v6)) { + redudp_log_error(client, LOG_DEBUG, "Packet too short."); + return; + } + fwdlen -= sizeof(*header); + redudp_fwd_pkt_to_sender( + client, + buff2 + sizeof(*header), + fwdlen, + (struct sockaddr_storage*)&pktaddr); + } + else { + redudp_log_error(client, LOG_DEBUG, "Got address type #%u instead of expected #%u (IPv4/IPv6).", + header->addr_type, ss_addrtype_ipv4); return; } - fwdlen -= sizeof(*header); - redudp_fwd_pkt_to_sender(client, buff2 + sizeof(*header), fwdlen, &pktaddr); } static int ss_ready_to_fwd(struct redudp_client_t *client) @@ -191,7 +229,7 @@ static int ss_ready_to_fwd(struct redudp_client_t *client) static void ss_connect_relay(redudp_client *client) { ss_client *ssclient = (void*)(client + 1); - struct sockaddr_in * addr = &client->instance->config.relayaddr; + struct sockaddr_storage * addr = &client->instance->config.relayaddr; int fd = -1; int error; diff --git a/shadowsocks.c b/shadowsocks.c index e483d5a4..67aa4e77 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -318,9 +318,10 @@ static int ss_connect_relay(redsocks_client *client) char * interface = client->instance->config.interface; ss_client *sclient = (void*)(client + 1); ss_instance * ss = (ss_instance *)(client->instance+1); - ss_header_ipv4 header; + ss_header header; struct timeval tv; size_t len = 0; + size_t header_len = 0; char buff[64+sizeof(header)]; if (enc_ctx_init(&ss->info, &sclient->e_ctx, 1)) { @@ -337,11 +338,26 @@ static int ss_connect_relay(redsocks_client *client) sclient->d_ctx_init = 1; /* build and send header */ - // TODO: Better implementation and IPv6 Support - header.addr_type = ss_addrtype_ipv4; - header.addr = client->destaddr.sin_addr.s_addr; - header.port = client->destaddr.sin_port; - len += sizeof(header); + if (client->destaddr.ss_family == AF_INET) { + struct sockaddr_in * addr = (struct sockaddr_in *)&client->destaddr; + header.v4.addr_type = ss_addrtype_ipv4; + header.v4.addr = addr->sin_addr.s_addr; + header.v4.port = addr->sin_port; + header_len = sizeof(ss_header_ipv4); + } + else if (client->destaddr.ss_family == AF_INET6) { + struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&client->destaddr; + header.v6.addr_type = ss_addrtype_ipv6; + header.v6.addr = addr->sin6_addr; + header.v6.port = addr->sin6_port; + header_len = sizeof(ss_header_ipv6); + } + else { + log_error(LOG_ERR, "Unsupported address family: %d", client->destaddr.ss_family); + redsocks_drop_client(client); + return -1; + } + len += header_len; size_t sz = sizeof(buff); if (!ss_encrypt(&sclient->e_ctx, (char *)&header, len, &buff[0], &sz)) { log_error(LOG_ERR, "Encryption error."); @@ -354,7 +370,7 @@ static int ss_connect_relay(redsocks_client *client) tv.tv_usec = 0; client->relay = red_connect_relay_tfo( interface, - (struct sockaddr *)&client->instance->config.relayaddr, + &client->instance->config.relayaddr, NULL, ss_relay_connected, redsocks_event_error, diff --git a/shadowsocks.h b/shadowsocks.h index 3265e3af..99599940 100644 --- a/shadowsocks.h +++ b/shadowsocks.h @@ -6,11 +6,23 @@ enum { ss_addrtype_domain = 3, ss_addrtype_ipv6 = 4, }; + typedef struct ss_header_ipv4_t { unsigned char addr_type; uint32_t addr; uint16_t port; } PACKED ss_header_ipv4; +typedef struct ss_header_ipv6_t { + unsigned char addr_type; + struct in6_addr addr; + uint16_t port; +} PACKED ss_header_ipv6; + +typedef union { + unsigned char addr_type; + ss_header_ipv4 v4; + ss_header_ipv6 v6; +} ss_header; #endif diff --git a/socks4.c b/socks4.c index ce294d7b..faea63eb 100644 --- a/socks4.c +++ b/socks4.c @@ -100,12 +100,13 @@ static struct evbuffer *socks4_mkconnect(redsocks_client *client) // space for \0 comes from socks4_req->login size_t username_len = strlen(username); size_t len = sizeof(socks4_req) + username_len; + struct sockaddr_in * addr = (struct sockaddr_in *)&client->destaddr; socks4_req *req = calloc(1, len); req->ver = socks4_ver; req->cmd = socks4_cmd_connect; - req->port = client->destaddr.sin_port; - req->addr = client->destaddr.sin_addr.s_addr; + req->port = addr->sin_port; + req->addr = addr->sin_addr.s_addr; memcpy(req->login, username, username_len + 1); struct evbuffer *ret = mkevbuffer(req, len); diff --git a/socks5-udp.c b/socks5-udp.c index 7a7412c8..fc9217b0 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -149,7 +149,7 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) socks5_udp_preabmle header; } * pkt = client->instance->shared_buff; ssize_t pktlen, fwdlen; - struct sockaddr_in udprelayaddr; + struct sockaddr_storage udprelayaddr; assert(fd == event_get_fd(&socks5client->udprelay)); @@ -177,6 +177,7 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) return; } + // FIXME: Support IPv6 struct sockaddr_in pktaddr = { .sin_family = AF_INET, .sin_addr = { pkt->header.ip.addr }, @@ -184,7 +185,8 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) }; fwdlen = pktlen - sizeof(pkt->header); - redudp_fwd_pkt_to_sender(client, pkt->buf + sizeof(pkt->header), fwdlen, &pktaddr); + redudp_fwd_pkt_to_sender(client, pkt->buf + sizeof(pkt->header), fwdlen, + (struct sockaddr_storage *)&pktaddr); } @@ -388,7 +390,7 @@ static void socks5_connect_relay(redudp_client *client) socks5_client *socks5client = (void*)(client + 1); socks5client->relay = red_connect_relay( NULL, - (struct sockaddr *)&client->instance->config.relayaddr, + &client->instance->config.relayaddr, NULL, socks5_relay_connected, socks5_relay_error, diff --git a/socks5.c b/socks5.c index 69638dab..43da666a 100644 --- a/socks5.c +++ b/socks5.c @@ -137,6 +137,7 @@ struct evbuffer *socks5_mkcommand_plain(int socks5_cmd, const struct sockaddr_in assert(destaddr->sin_family == AF_INET); + // FIXME: Support IPv6 req.head.ver = socks5_ver; req.head.cmd = socks5_cmd; req.head.reserved = 0; diff --git a/tcpdns.c b/tcpdns.c index a60a0d09..67f9371d 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -218,13 +218,11 @@ static void tcpdns_event_error(struct bufferevent *buffev, short what, void *_ar tcpdns_drop_request(req); } -static struct sockaddr_in * choose_tcpdns(tcpdns_instance * instance, int **delay) +static struct sockaddr_storage * choose_tcpdns(tcpdns_instance * instance, int **delay) { static int n = 0; log_error(LOG_DEBUG, "Dealy of TCP DNS resolvers: %d, %d", instance->tcp1_delay_ms, instance->tcp2_delay_ms); - if (instance->config.tcpdns1_addr.sin_addr.s_addr != htonl(INADDR_ANY) - && (instance->config.tcpdns2_addr.sin_addr.s_addr != htonl(INADDR_ANY)) - ) + if (instance->config.tcpdns1 && instance->config.tcpdns2) { if (instance->tcp1_delay_ms <= 0 && instance->tcp2_delay_ms <= 0) @@ -251,9 +249,9 @@ static struct sockaddr_in * choose_tcpdns(tcpdns_instance * instance, int **dela goto return_tcp1; } } - if (instance->config.tcpdns1_addr.sin_addr.s_addr != htonl(INADDR_ANY)) + if (instance->config.tcpdns1) goto return_tcp1; - if (instance->config.tcpdns2_addr.sin_addr.s_addr != htonl(INADDR_ANY)) + if (instance->config.tcpdns2) goto return_tcp2; * delay = NULL; @@ -274,7 +272,7 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) tcpdns_instance *self = _arg; dns_request * req = NULL; struct timeval tv; - struct sockaddr_in * destaddr; + struct sockaddr_storage * destaddr; ssize_t pktlen; assert(fd == event_get_fd(self->listener)); @@ -319,7 +317,7 @@ static void tcpdns_pkt_from_client(int fd, short what, void *_arg) return; } /* connect to target directly without going through proxy */ - req->resolver = red_connect_relay(NULL, (struct sockaddr *)destaddr, + req->resolver = red_connect_relay(NULL, destaddr, tcpdns_readcb, tcpdns_connected, tcpdns_event_error, req, &tv); if (req->resolver) @@ -382,31 +380,16 @@ static int tcpdns_onenter(parser_section *section) INIT_LIST_HEAD(&instance->list); INIT_LIST_HEAD(&instance->requests); - instance->config.bindaddr.sin_family = AF_INET; - instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - instance->config.udpdns1_addr.sin_family = AF_INET; - instance->config.udpdns1_addr.sin_addr.s_addr = htonl(INADDR_ANY); - instance->config.udpdns1_addr.sin_port = htons(53); - instance->config.udpdns2_addr.sin_family = AF_INET; - instance->config.udpdns2_addr.sin_addr.s_addr = htonl(INADDR_ANY); - instance->config.udpdns2_addr.sin_port = htons(53); - instance->config.tcpdns1_addr.sin_family = AF_INET; - instance->config.tcpdns1_addr.sin_addr.s_addr = htonl(INADDR_ANY); - instance->config.tcpdns1_addr.sin_port = 53; - instance->config.tcpdns2_addr.sin_family = AF_INET; - instance->config.tcpdns2_addr.sin_addr.s_addr = htonl(INADDR_ANY); - instance->config.tcpdns2_addr.sin_port = 53; + struct sockaddr_in * addr = (struct sockaddr_in *)&instance->config.bindaddr; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr->sin_port = htons(53); for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = - (strcmp(entry->key, "local_ip") == 0) ? (void*)&instance->config.bindaddr.sin_addr : - (strcmp(entry->key, "local_port") == 0) ? (void*)&instance->config.bindaddr.sin_port : - (strcmp(entry->key, "udpdns1") == 0) ? (void*)&instance->config.udpdns1_addr.sin_addr : - (strcmp(entry->key, "udpdns2") == 0) ? (void*)&instance->config.udpdns2_addr.sin_addr : - (strcmp(entry->key, "tcpdns1") == 0) ? (void*)&instance->config.tcpdns1_addr.sin_addr : - (strcmp(entry->key, "tcpdns1_port") == 0) ? (void*)&instance->config.tcpdns1_addr.sin_port : - (strcmp(entry->key, "tcpdns2") == 0) ? (void*)&instance->config.tcpdns2_addr.sin_addr : - (strcmp(entry->key, "tcpdns2_port") == 0) ? (void*)&instance->config.tcpdns2_addr.sin_port : + (strcmp(entry->key, "bin") == 0) ? (void*)&instance->config.bind: + (strcmp(entry->key, "tcpdns1") == 0) ? (void*)&instance->config.tcpdns1 : + (strcmp(entry->key, "tcpdns2") == 0) ? (void*)&instance->config.tcpdns2 : (strcmp(entry->key, "timeout") == 0) ? (void*)&instance->config.timeout : NULL; section->data = instance; @@ -422,23 +405,37 @@ static int tcpdns_onexit(parser_section *section) for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = NULL; - if (instance->config.bindaddr.sin_port == 0) - err = "Local port must be configured"; - else - instance->config.bindaddr.sin_port = htons(instance->config.bindaddr.sin_port); + // Parse and update bind address and relay address + if (instance->config.bind) { + struct sockaddr * addr = (struct sockaddr *)&instance->config.bindaddr; + int addr_size = sizeof(instance->config.bindaddr); + if (evutil_parse_sockaddr_port(instance->config.bind, addr, &addr_size)) + err = "invalid bind address"; + } + if (!err && instance->config.tcpdns1) { + struct sockaddr * addr = (struct sockaddr *)&instance->config.tcpdns1_addr; + int addr_size = sizeof(instance->config.tcpdns1_addr); + if (evutil_parse_sockaddr_port(instance->config.tcpdns1, addr, &addr_size)) + err = "invalid tcpdns1 address"; + else if (addr->sa_family == AF_INET && ((struct sockaddr_in *)addr)->sin_port == 0) + ((struct sockaddr_in *)addr)->sin_port = htons(53); + else if (addr->sa_family == AF_INET6 && ((struct sockaddr_in6 *)addr)->sin6_port == 0) + ((struct sockaddr_in6 *)addr)->sin6_port = htons(53); + } + if (!err && instance->config.tcpdns2) { + struct sockaddr * addr = (struct sockaddr *)&instance->config.tcpdns2_addr; + int addr_size = sizeof(instance->config.tcpdns2_addr); + if (evutil_parse_sockaddr_port(instance->config.tcpdns2, addr, &addr_size)) + err = "invalid tcpdns2 address"; + else if (addr->sa_family == AF_INET && ((struct sockaddr_in *)addr)->sin_port == 0) + ((struct sockaddr_in *)addr)->sin_port = htons(53); + else if (addr->sa_family == AF_INET6 && ((struct sockaddr_in6 *)addr)->sin6_port == 0) + ((struct sockaddr_in6 *)addr)->sin6_port = htons(53); + } - if (instance->config.tcpdns1_addr.sin_addr.s_addr == htonl(INADDR_ANY) - && instance->config.tcpdns2_addr.sin_addr.s_addr == htonl(INADDR_ANY)) - err = "At least one TCP DNS resolver must be configured."; - if (instance->config.tcpdns1_addr.sin_port == 0) - err = "Incorrect port number for TCP DNS1"; - else - instance->config.tcpdns1_addr.sin_port = htons(instance->config.tcpdns1_addr.sin_port); - if (instance->config.tcpdns2_addr.sin_port == 0) - err = "Incorrect port number for TCP DNS2"; - else - instance->config.tcpdns2_addr.sin_port = htons(instance->config.tcpdns2_addr.sin_port); + if (instance->config.tcpdns1 == NULL && instance->config.tcpdns2 == NULL) + err = "At least one TCP DNS resolver must be configured."; if (err) parser_error(section->context, "%s", err); diff --git a/tcpdns.h b/tcpdns.h index ee8d9f0a..bbb8b1c5 100644 --- a/tcpdns.h +++ b/tcpdns.h @@ -2,11 +2,12 @@ #define TCPDNS_H typedef struct tcpdns_config_t { - struct sockaddr_in bindaddr; - struct sockaddr_in udpdns1_addr; - struct sockaddr_in udpdns2_addr; - struct sockaddr_in tcpdns1_addr; - struct sockaddr_in tcpdns2_addr; + struct sockaddr_storage bindaddr; + struct sockaddr_storage tcpdns1_addr; + struct sockaddr_storage tcpdns2_addr; + char *bind; + char *tcpdns1; + char *tcpdns2; uint16_t timeout; /* timeout value for DNS response*/ } tcpdns_config; @@ -40,7 +41,7 @@ typedef struct dns_request_t { short state; int flags; struct bufferevent* resolver; - struct sockaddr_in client_addr; + struct sockaddr_storage client_addr; struct timeval req_time; int * delay; size_t data_len; diff --git a/utils.c b/utils.c index ae84b2ca..084888a8 100644 --- a/utils.c +++ b/utils.c @@ -32,7 +32,12 @@ #include "redsocks.h" // for redsocks_close #include "libc-compat.h" -int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr, struct sockaddr_in *toaddr) +int red_recv_udp_pkt( + int fd, + char *buf, + size_t buflen, + struct sockaddr_storage *inaddr, + struct sockaddr_storage *toaddr) { socklen_t addrlen = sizeof(*inaddr); ssize_t pktlen; @@ -64,7 +69,7 @@ int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inadd cmsg->cmsg_type == IP_ORIGDSTADDR && cmsg->cmsg_len >= CMSG_LEN(sizeof(*toaddr)) ) { - struct sockaddr_in* cmsgaddr = (struct sockaddr_in*)CMSG_DATA(cmsg); + struct sockaddr_storage* cmsgaddr = (struct sockaddr_storage*)CMSG_DATA(cmsg); memcpy(toaddr, cmsgaddr, sizeof(*toaddr)); } else { @@ -72,7 +77,7 @@ int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inadd cmsg->cmsg_level, cmsg->cmsg_type); } } - if (toaddr->sin_family != AF_INET) { + if (toaddr->ss_family != AF_INET && toaddr->ss_family != AF_INET6) { log_error(LOG_WARNING, "(SOL_IP, IP_ORIGDSTADDR) not found"); return -1; } @@ -173,7 +178,7 @@ struct bufferevent* red_prepare_relay(const char *ifname, } struct bufferevent* red_connect_relay(const char *ifname, - struct sockaddr *addr, + struct sockaddr_storage *addr, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, @@ -184,7 +189,7 @@ struct bufferevent* red_connect_relay(const char *ifname, int relay_fd = -1; int error; - retval = red_prepare_relay(ifname, addr->sa_family, readcb, writecb, errorcb, cbarg); + retval = red_prepare_relay(ifname, addr->ss_family, readcb, writecb, errorcb, cbarg); if (retval) { relay_fd = bufferevent_getfd(retval); if (timeout_write) @@ -192,7 +197,7 @@ struct bufferevent* red_connect_relay(const char *ifname, // error = bufferevent_socket_connect(retval, addr, sizeof(*addr)); // if (error) { - error = connect(relay_fd, addr, sizeof(struct sockaddr_storage)); + error = connect(relay_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_storage)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "connect"); goto fail; @@ -212,7 +217,7 @@ struct bufferevent* red_connect_relay(const char *ifname, #if defined(ENABLE_HTTPS_PROXY) struct bufferevent* red_connect_relay_ssl(const char *ifname, - struct sockaddr *addr, + struct sockaddr_storage *addr, SSL * ssl, bufferevent_data_cb readcb, bufferevent_data_cb writecb, @@ -225,7 +230,7 @@ struct bufferevent* red_connect_relay_ssl(const char *ifname, int relay_fd = -1; int error; - underlying = red_prepare_relay(ifname, addr->sa_family, NULL, NULL, NULL, NULL); + underlying = red_prepare_relay(ifname, addr->ss_family, NULL, NULL, NULL, NULL); if (!underlying) goto fail; relay_fd = bufferevent_getfd(underlying); @@ -275,7 +280,7 @@ struct bufferevent* red_connect_relay_ssl(const char *ifname, #endif struct bufferevent* red_connect_relay_tfo(const char *ifname, - struct sockaddr *addr, + struct sockaddr_storage *addr, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, @@ -288,7 +293,7 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, int relay_fd = -1; int error; - retval = red_prepare_relay(ifname, addr->sa_family, readcb, writecb, errorcb, cbarg); + retval = red_prepare_relay(ifname, addr->ss_family, readcb, writecb, errorcb, cbarg); if (retval) { relay_fd = bufferevent_getfd(retval); if (timeout_write) @@ -296,7 +301,7 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, #ifdef MSG_FASTOPEN size_t s = sendto(relay_fd, data, * len, MSG_FASTOPEN, - addr, sizeof(struct sockaddr_storage) + (struct sockaddr *)addr, sizeof(struct sockaddr_storage) ); *len = 0; // Assume nothing sent, caller needs to write data again when connection is setup. if (s == -1) { @@ -324,7 +329,7 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, fallback: #endif - error = connect(relay_fd, addr, sizeof(struct sockaddr_storage)); + error = connect(relay_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_storage)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "connect"); goto fail; @@ -381,7 +386,7 @@ int red_is_socket_connected_ok(struct bufferevent *buffev) } } -char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_size) +char *red_inet_ntop(const struct sockaddr_storage* sa, char* buffer, size_t buffer_size) { const char *retval = 0; size_t len = 0; @@ -391,11 +396,11 @@ char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_si assert(buffer_size >= RED_INET_ADDRSTRLEN); memset(buffer, 0, buffer_size); - if (sa->sin_family == AF_INET) { - retval = inet_ntop(AF_INET, &sa->sin_addr, buffer, buffer_size); + if (sa->ss_family == AF_INET) { + retval = inet_ntop(AF_INET, &((const struct sockaddr_in *)sa)->sin_addr, buffer, buffer_size); port = ((struct sockaddr_in*)sa)->sin_port; } - else if (sa->sin_family == AF_INET6) { + else if (sa->ss_family == AF_INET6) { buffer[0] = '['; retval = inet_ntop(AF_INET6, &((const struct sockaddr_in6*)sa)->sin6_addr, buffer+1, buffer_size-1); port = ((struct sockaddr_in6*)sa)->sin6_port; @@ -403,7 +408,7 @@ char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_si if (retval) { assert(retval == buffer); len = strlen(retval); - if (sa->sin_family == AF_INET6) + if (sa->ss_family == AF_INET6) snprintf(buffer + len, buffer_size - len, "]:%d", ntohs(port)); else snprintf(buffer + len, buffer_size - len, ":%d", ntohs(port)); diff --git a/utils.h b/utils.h index ed8b5764..746b5a86 100644 --- a/utils.h +++ b/utils.h @@ -64,7 +64,7 @@ struct bufferevent* red_prepare_relay(const char *ifname, bufferevent_event_cb errorcb, void *cbarg); struct bufferevent* red_connect_relay(const char *ifname, - struct sockaddr *addr, + struct sockaddr_storage *addr, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, @@ -72,7 +72,7 @@ struct bufferevent* red_connect_relay(const char *ifname, const struct timeval *timeout_write); #if defined(ENABLE_HTTPS_PROXY) struct bufferevent* red_connect_relay_ssl(const char *ifname, - struct sockaddr *addr, + struct sockaddr_storage *addr, SSL * ssl, bufferevent_data_cb readcb, bufferevent_data_cb writecb, @@ -81,7 +81,7 @@ struct bufferevent* red_connect_relay_ssl(const char *ifname, const struct timeval *timeout_write); #endif struct bufferevent* red_connect_relay_tfo(const char *ifname, - struct sockaddr *addr, + struct sockaddr_storage *addr, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, @@ -92,7 +92,7 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, int red_socket_geterrno(struct bufferevent *buffev); int red_is_socket_connected_ok(struct bufferevent *buffev); -int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *fromaddr, struct sockaddr_in *toaddr); +int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_storage *fromaddr, struct sockaddr_storage *toaddr); size_t copy_evbuffer(struct bufferevent * dst, struct bufferevent * src, size_t skip); size_t get_write_hwm(struct bufferevent *bufev); @@ -117,7 +117,7 @@ void replace_eventcb(struct bufferevent * buffev, bufferevent_event_cb eventcb); #else # define RED_INET_ADDRSTRLEN (1 + INET6_ADDRSTRLEN + 1 + 1 + 5 + 1) // [ + addr + ] + : + port + \0 #endif -char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_size); +char *red_inet_ntop(const struct sockaddr_storage * sa, char* buffer, size_t buffer_size); /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ From 513576a47a03f9318d663bc8c137aa1f7ae67124 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 2 Oct 2019 15:46:06 +0800 Subject: [PATCH 137/192] Fix bugs introduced during moving to IPv6 --- http-relay.c | 2 +- redsocks.c | 4 +++- shadowsocks-udp.c | 8 +++---- shadowsocks.h | 7 ++++++ socks5-udp.c | 56 ++++++++++++++++++++++++++++++++++------------- socks5.h | 10 ++++++++- utils.c | 51 ++++++++++++++++++++++++------------------ 7 files changed, 95 insertions(+), 43 deletions(-) diff --git a/http-relay.c b/http-relay.c index 80515cba..3e3e311b 100644 --- a/http-relay.c +++ b/http-relay.c @@ -523,7 +523,7 @@ static void httpr_client_read_cb(struct bufferevent *buffev, void *_arg) do_drop = 1; if (!httpr->has_host) { - char host[32]; // "Host: 123.456.789.012:34567" + char host[266]; // "Host: 123.456.789.012:34567" int written_wo_null = snprintf(host, sizeof(host), "Host: %s", fmt_http_host(&client->destaddr)); UNUSED(written_wo_null); diff --git a/redsocks.c b/redsocks.c index abb23880..f708661d 100644 --- a/redsocks.c +++ b/redsocks.c @@ -1001,7 +1001,9 @@ static int redsocks_init_instance(redsocks_instance *instance) if (apply_reuseport(fd)) log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); - error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); + error = bind(fd, + (struct sockaddr*)&instance->config.bindaddr, + sizeof(instance->config.bindaddr)); if (error) { log_errno(LOG_ERR, "bind"); goto fail; diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index 561b2c84..63fb7d82 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -189,10 +189,10 @@ static void ss_pkt_from_server(int fd, short what, void *_arg) redudp_log_error(client, LOG_DEBUG, "Packet too short."); return; } - fwdlen -= sizeof(*header); + fwdlen -= sizeof(header->v4); redudp_fwd_pkt_to_sender( client, - buff2 + sizeof(*header), + buff2 + sizeof(header->v4), fwdlen, (struct sockaddr_storage*)&pktaddr); } @@ -207,10 +207,10 @@ static void ss_pkt_from_server(int fd, short what, void *_arg) redudp_log_error(client, LOG_DEBUG, "Packet too short."); return; } - fwdlen -= sizeof(*header); + fwdlen -= sizeof(header->v6); redudp_fwd_pkt_to_sender( client, - buff2 + sizeof(*header), + buff2 + sizeof(header->v6), fwdlen, (struct sockaddr_storage*)&pktaddr); } diff --git a/shadowsocks.h b/shadowsocks.h index 99599940..61b056b1 100644 --- a/shadowsocks.h +++ b/shadowsocks.h @@ -19,6 +19,13 @@ typedef struct ss_header_ipv6_t { uint16_t port; } PACKED ss_header_ipv6; +typedef struct ss_header_domain_t { + unsigned char addr_type; + uint8_t length; + char domain[255]; + uint16_t port; +} PACKED ss_header_domain; + typedef union { unsigned char addr_type; ss_header_ipv4 v4; diff --git a/socks5-udp.c b/socks5-udp.c index fc9217b0..f4e9a8c6 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -55,13 +55,27 @@ static struct evbuffer* socks5_mkassociate(void *p) return socks5_mkcommand_plain(socks5_cmd_udp_associate, &sa); } -static void socks5_fill_preamble(socks5_udp_preabmle *preamble, struct sockaddr_in * addr) +static void socks5_fill_preamble( + socks5_udp_preabmle *preamble, + struct sockaddr * addr, + size_t *preamble_len) { preamble->reserved = 0; preamble->frag_no = 0; /* fragmentation is not supported */ - preamble->addrtype = socks5_addrtype_ipv4; - preamble->ip.addr = addr->sin_addr.s_addr; - preamble->ip.port = addr->sin_port; + if (addr->sa_family == AF_INET) { + struct sockaddr_in * in_addr = (struct sockaddr_in *) addr; + preamble->addrtype = socks5_addrtype_ipv4; + preamble->addr.v4.addr = in_addr->sin_addr.s_addr; + preamble->addr.v4.port = in_addr->sin_port; + *preamble_len = 4 + sizeof(preamble->addr.v4); + } + else if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 * in6_addr = (struct sockaddr_in6 *) addr; + preamble->addrtype = socks5_addrtype_ipv6; + memcpy(&preamble->addr.v6, &in6_addr->sin6_addr, sizeof(in6_addr->sin6_addr)); + preamble->addr.v6.port = in6_addr->sin6_port; + *preamble_len = 4 + sizeof(preamble->addr.v6); + } } @@ -114,10 +128,11 @@ static void socks5_forward_pkt(redudp_client *client, struct sockaddr *destaddr, socks5_udp_preabmle req; struct msghdr msg; struct iovec io[2]; - ssize_t outgoing, fwdlen = pktlen + sizeof(req); + size_t preamble_len; - socks5_fill_preamble(&req, (struct sockaddr_in *)destaddr); + socks5_fill_preamble(&req, destaddr, &preamble_len); + ssize_t outgoing, fwdlen = pktlen + preamble_len; memset(&msg, 0, sizeof(msg)); msg.msg_name = &socks5client->udprelayaddr; msg.msg_namelen = sizeof(socks5client->udprelayaddr); @@ -125,7 +140,7 @@ static void socks5_forward_pkt(redudp_client *client, struct sockaddr *destaddr, msg.msg_iovlen = SIZEOF_ARRAY(io); io[0].iov_base = &req; - io[0].iov_len = sizeof(req); + io[0].iov_len = preamble_len; io[1].iov_base = buf; io[1].iov_len = pktlen; @@ -177,16 +192,27 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) return; } - // FIXME: Support IPv6 - struct sockaddr_in pktaddr = { - .sin_family = AF_INET, - .sin_addr = { pkt->header.ip.addr }, - .sin_port = pkt->header.ip.port, - }; + // Support IPv6 + struct sockaddr_storage src_addr; + size_t header_size = 4; + if (pkt->header.addrtype == socks5_addrtype_ipv4) { + struct sockaddr_in * src = (struct sockaddr_in *)&src_addr; + src->sin_family = AF_INET; + src->sin_addr.s_addr = pkt->header.addr.v4.addr; + src->sin_port = pkt->header.addr.v4.port; + header_size += sizeof(socks5_addr_ipv4); + } + else if (pkt->header.addrtype == socks5_addrtype_ipv6) { + struct sockaddr_in6 * src = (struct sockaddr_in6 *)&src_addr; + src->sin6_family = AF_INET6; + memcpy(&src->sin6_addr, &pkt->header.addr.v6.addr, sizeof(src->sin6_addr)); + src->sin6_port = pkt->header.addr.v6.port; + header_size += sizeof(socks5_addr_ipv6); + } + // TODO: Support domain addr fwdlen = pktlen - sizeof(pkt->header); - redudp_fwd_pkt_to_sender(client, pkt->buf + sizeof(pkt->header), fwdlen, - (struct sockaddr_storage *)&pktaddr); + redudp_fwd_pkt_to_sender(client, pkt->buf + header_size, fwdlen, &src_addr); } diff --git a/socks5.h b/socks5.h index 5155225a..22cab9d5 100644 --- a/socks5.h +++ b/socks5.h @@ -36,6 +36,11 @@ typedef struct socks5_addr_ipv4_t { uint16_t port; } PACKED socks5_addr_ipv4; +typedef struct socks5_addr_ipv6_t { + struct in6_addr addr; + uint16_t port; +} PACKED socks5_addr_ipv6; + typedef struct socks5_addr_domain_t { uint8_t size; uint8_t more[1]; @@ -68,7 +73,10 @@ typedef struct socks5_udp_preabmle_t { uint8_t frag_no; uint8_t addrtype; /* 0x01 for IPv4 */ /* socks5_addr_* */ - socks5_addr_ipv4 ip; /* I support only IPv4 at the moment */ + union { + socks5_addr_ipv4 v4; + socks5_addr_ipv6 v6; + } addr; } PACKED socks5_udp_preabmle; static const int socks5_reply_maxlen = 512; // as domain name can't be longer than 256 bytes diff --git a/utils.c b/utils.c index 084888a8..bd7bd853 100644 --- a/utils.c +++ b/utils.c @@ -32,6 +32,10 @@ #include "redsocks.h" // for redsocks_close #include "libc-compat.h" + +#define addr_size(addr) (((struct sockaddr *)addr)->sa_family == AF_INET ? sizeof(struct sockaddr_in): \ + ((struct sockaddr *)addr)->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6): sizeof(struct sockaddr_storage)) + int red_recv_udp_pkt( int fd, char *buf, @@ -39,7 +43,6 @@ int red_recv_udp_pkt( struct sockaddr_storage *inaddr, struct sockaddr_storage *toaddr) { - socklen_t addrlen = sizeof(*inaddr); ssize_t pktlen; struct msghdr msg; struct iovec io; @@ -61,6 +64,13 @@ int red_recv_udp_pkt( return -1; } + if (pktlen >= buflen) { + char buf[RED_INET_ADDRSTRLEN]; + log_error(LOG_WARNING, "wow! Truncated udp packet of size %zd from %s! impossible! dropping it...", + pktlen, red_inet_ntop(inaddr, buf, sizeof(buf))); + return -1; + } + if (toaddr) { memset(toaddr, 0, sizeof(*toaddr)); for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { @@ -69,32 +79,31 @@ int red_recv_udp_pkt( cmsg->cmsg_type == IP_ORIGDSTADDR && cmsg->cmsg_len >= CMSG_LEN(sizeof(*toaddr)) ) { - struct sockaddr_storage* cmsgaddr = (struct sockaddr_storage*)CMSG_DATA(cmsg); - memcpy(toaddr, cmsgaddr, sizeof(*toaddr)); + struct sockaddr* cmsgaddr = (struct sockaddr*)CMSG_DATA(cmsg); + if (cmsgaddr->sa_family == AF_INET) { + memcpy(toaddr, cmsgaddr, sizeof(struct sockaddr_in)); + } + else if (cmsgaddr->sa_family == AF_INET6) { + memcpy(toaddr, cmsgaddr, sizeof(struct sockaddr_in6)); + } + else { + log_error(LOG_WARNING, + "unexepcted socket address type: %d", + cmsgaddr->sa_family); + } + break; } else { log_error(LOG_WARNING, "unexepcted cmsg (level,type) = (%d,%d)", cmsg->cmsg_level, cmsg->cmsg_type); } } - if (toaddr->ss_family != AF_INET && toaddr->ss_family != AF_INET6) { + if (toaddr->ss_family == 0) { log_error(LOG_WARNING, "(SOL_IP, IP_ORIGDSTADDR) not found"); return -1; } } - if (addrlen != sizeof(*inaddr)) { - log_error(LOG_WARNING, "unexpected address length %u instead of %zu", addrlen, sizeof(*inaddr)); - return -1; - } - - if (pktlen >= buflen) { - char buf[RED_INET_ADDRSTRLEN]; - log_error(LOG_WARNING, "wow! Truncated udp packet of size %zd from %s! impossible! dropping it...", - pktlen, red_inet_ntop(inaddr, buf, sizeof(buf))); - return -1; - } - return pktlen; } @@ -115,7 +124,7 @@ time_t redsocks_time(time_t *t) } struct bufferevent* red_prepare_relay(const char *ifname, - int sa_family, + int sa_family, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, @@ -197,7 +206,7 @@ struct bufferevent* red_connect_relay(const char *ifname, // error = bufferevent_socket_connect(retval, addr, sizeof(*addr)); // if (error) { - error = connect(relay_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_storage)); + error = connect(relay_fd, (struct sockaddr *)addr, addr_size(addr)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "connect"); goto fail; @@ -237,7 +246,7 @@ struct bufferevent* red_connect_relay_ssl(const char *ifname, if (timeout_write) bufferevent_set_timeouts(underlying, NULL, timeout_write); - error = connect(relay_fd, addr, sizeof(struct sockaddr_storage)); + error = connect(relay_fd, addr, addr_size(addr)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "connect"); goto fail; @@ -301,7 +310,7 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, #ifdef MSG_FASTOPEN size_t s = sendto(relay_fd, data, * len, MSG_FASTOPEN, - (struct sockaddr *)addr, sizeof(struct sockaddr_storage) + (struct sockaddr *)addr, addr_size(addr) ); *len = 0; // Assume nothing sent, caller needs to write data again when connection is setup. if (s == -1) { @@ -329,7 +338,7 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, fallback: #endif - error = connect(relay_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_storage)); + error = connect(relay_fd, (struct sockaddr *)addr, addr_size(addr)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "connect"); goto fail; From 8fcc880b78ae1ba262efa22ab5f49bbadbcad953 Mon Sep 17 00:00:00 2001 From: HQ Ong Date: Sat, 12 Oct 2019 21:25:23 +0800 Subject: [PATCH 138/192] Fix compile failure with gcc 8.3 (#128) Openwrt switched to gcc 8.3 and here we have a redefinition issue. --- socks5.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/socks5.h b/socks5.h index 22cab9d5..06245d5f 100644 --- a/socks5.h +++ b/socks5.h @@ -47,11 +47,6 @@ typedef struct socks5_addr_domain_t { /* uint16_t port; */ } PACKED socks5_addr_domain; -typedef struct socks5_addr_ipv6_t { - uint8_t addr[16]; - uint16_t port; -} PACKED socks5_addr_ipv6; - typedef struct socks5_req_t { uint8_t ver; uint8_t cmd; From 7c8715db47a6218ae3db738b03cb88c2334f99d4 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 24 Oct 2019 00:04:57 +0800 Subject: [PATCH 139/192] Fix: unknown key --- redsocks.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/redsocks.c b/redsocks.c index f708661d..77f29623 100644 --- a/redsocks.c +++ b/redsocks.c @@ -79,8 +79,7 @@ static list_head instances = LIST_HEAD_INIT(instances); static parser_entry redsocks_entries[] = { - { .key = "local_ip", .type = pt_in_addr }, - { .key = "local_port", .type = pt_uint16 }, + { .key = "bind", .type = pt_pchar }, { .key = "interface", .type = pt_pchar }, { .key = "relay", .type = pt_pchar }, { .key = "type", .type = pt_pchar }, From 7a2f8ad287aa1f3ed117a312571d5331671129a6 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 14 Nov 2019 13:54:39 +0000 Subject: [PATCH 140/192] Fix TCPDNS config error --- tcpdns.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tcpdns.c b/tcpdns.c index 67f9371d..3c06fb78 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -358,12 +358,9 @@ static void check_dns_delay() */ static parser_entry tcpdns_entries[] = { - { .key = "local_ip", .type = pt_in_addr }, - { .key = "local_port", .type = pt_uint16 }, - { .key = "tcpdns1", .type = pt_in_addr }, - { .key = "tcpdns1_port", .type = pt_uint16 }, - { .key = "tcpdns2", .type = pt_in_addr }, - { .key = "tcpdns2_port", .type = pt_uint16 }, + { .key = "bind", .type = pt_pchar }, + { .key = "tcpdns1", .type = pt_pchar }, + { .key = "tcpdns2", .type = pt_pchar }, { .key = "timeout", .type = pt_uint16 }, { } }; @@ -387,7 +384,7 @@ static int tcpdns_onenter(parser_section *section) for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = - (strcmp(entry->key, "bin") == 0) ? (void*)&instance->config.bind: + (strcmp(entry->key, "bind") == 0) ? (void*)&instance->config.bind: (strcmp(entry->key, "tcpdns1") == 0) ? (void*)&instance->config.tcpdns1 : (strcmp(entry->key, "tcpdns2") == 0) ? (void*)&instance->config.tcpdns2 : (strcmp(entry->key, "timeout") == 0) ? (void*)&instance->config.timeout : From 3a46f2fd6094e4811eb6054567a0aabdc35605d7 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 22 Nov 2019 23:48:15 +0800 Subject: [PATCH 141/192] Fix: redudp failing to extract original destination address --- utils.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/utils.c b/utils.c index bd7bd853..af79b4e6 100644 --- a/utils.c +++ b/utils.c @@ -34,7 +34,7 @@ #define addr_size(addr) (((struct sockaddr *)addr)->sa_family == AF_INET ? sizeof(struct sockaddr_in): \ - ((struct sockaddr *)addr)->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6): sizeof(struct sockaddr_storage)) + ((struct sockaddr *)addr)->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6): sizeof(struct sockaddr_storage)) int red_recv_udp_pkt( int fd, @@ -77,7 +77,9 @@ int red_recv_udp_pkt( if ( cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_ORIGDSTADDR && - cmsg->cmsg_len >= CMSG_LEN(sizeof(*toaddr)) + (cmsg->cmsg_len == CMSG_LEN(sizeof(struct sockaddr_in)) + || cmsg->cmsg_len == CMSG_LEN(sizeof(struct sockaddr_in6))) && + cmsg->cmsg_len <= CMSG_LEN(sizeof(*toaddr)) ) { struct sockaddr* cmsgaddr = (struct sockaddr*)CMSG_DATA(cmsg); if (cmsgaddr->sa_family == AF_INET) { From 4cc0fefadb20ebac7f3da24a7c3a161167a9b3ae Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 23 Nov 2019 09:57:27 +0800 Subject: [PATCH 142/192] Fix: wrong size and address type in socks5-udp --- socks5-udp.c | 26 +++++++++++++++++++++----- socks5.h | 7 +++++-- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/socks5-udp.c b/socks5-udp.c index f4e9a8c6..3bcf941b 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -56,7 +56,7 @@ static struct evbuffer* socks5_mkassociate(void *p) } static void socks5_fill_preamble( - socks5_udp_preabmle *preamble, + socks5_udp_preamble *preamble, struct sockaddr * addr, size_t *preamble_len) { @@ -125,11 +125,25 @@ static int socks5_ready_to_fwd(struct redudp_client_t *client) static void socks5_forward_pkt(redudp_client *client, struct sockaddr *destaddr, void *buf, size_t pktlen) { socks5_client *socks5client = (void*)(client + 1); - socks5_udp_preabmle req; + socks5_udp_preamble req; struct msghdr msg; struct iovec io[2]; size_t preamble_len; + if (socks5client->udprelayaddr.sin_family == AF_INET) { + preamble_len = SOCKS5_UDP_PREAMBLE_SIZE_V4; + } + /* + else if (socks5client->udprelayaddr.ss_family == AF_INET6) { + preamble_len = SOCKS5_UDP_PREAMBLE_SIZE_V6; + } + */ + else { + redudp_log_errno(client, LOG_WARNING, "Unknown address type %d", + socks5client->udprelayaddr.sin_family); + return; + } + socks5_fill_preamble(&req, destaddr, &preamble_len); ssize_t outgoing, fwdlen = pktlen + preamble_len; @@ -161,7 +175,7 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) socks5_client *socks5client = (void*)(client + 1); union { char buf[MAX_UDP_PACKET_SIZE]; - socks5_udp_preabmle header; + socks5_udp_preamble header; } * pkt = client->instance->shared_buff; ssize_t pktlen, fwdlen; struct sockaddr_storage udprelayaddr; @@ -172,7 +186,9 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) if (pktlen == -1) return; - if (memcmp(&udprelayaddr, &socks5client->udprelayaddr, sizeof(udprelayaddr)) != 0) { + if (evutil_sockaddr_cmp((struct sockaddr *)&udprelayaddr, + (struct sockaddr *)&socks5client->udprelayaddr, + 1) != 0) { char buf[RED_INET_ADDRSTRLEN]; redudp_log_error(client, LOG_NOTICE, "Got packet from unexpected address %s.", red_inet_ntop(&udprelayaddr, buf, sizeof(buf))); @@ -211,7 +227,7 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) } // TODO: Support domain addr - fwdlen = pktlen - sizeof(pkt->header); + fwdlen = pktlen - header_size; redudp_fwd_pkt_to_sender(client, pkt->buf + header_size, fwdlen, &src_addr); } diff --git a/socks5.h b/socks5.h index 06245d5f..aae857fa 100644 --- a/socks5.h +++ b/socks5.h @@ -63,7 +63,7 @@ typedef struct socks5_reply_t { /* socks5_addr_* */ } PACKED socks5_reply; -typedef struct socks5_udp_preabmle_t { +typedef struct socks5_udp_preamble_t { uint16_t reserved; uint8_t frag_no; uint8_t addrtype; /* 0x01 for IPv4 */ @@ -72,7 +72,10 @@ typedef struct socks5_udp_preabmle_t { socks5_addr_ipv4 v4; socks5_addr_ipv6 v6; } addr; -} PACKED socks5_udp_preabmle; +} PACKED socks5_udp_preamble; + +#define SOCKS5_UDP_PREAMBLE_SIZE_V4 (4 + sizeof(socks5_addr_ipv4)) +#define SOCKS5_UDP_PREAMBLE_SIZE_V6 (4 + sizeof(socks5_addr_ipv6)) static const int socks5_reply_maxlen = 512; // as domain name can't be longer than 256 bytes static const int socks5_addrtype_ipv4 = 1; From cba154cceff8c5c8952b80cc360c225b4952c8cb Mon Sep 17 00:00:00 2001 From: semigodking Date: Sat, 23 Nov 2019 10:06:42 +0800 Subject: [PATCH 143/192] Create ccpp.yml --- .github/workflows/ccpp.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/ccpp.yml diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml new file mode 100644 index 00000000..aa919197 --- /dev/null +++ b/.github/workflows/ccpp.yml @@ -0,0 +1,13 @@ +name: C/C++ CI + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: make + run: make DISABLE_SHADOWSOCKS=1 From d32011d396ed8491d5719f7411b61f8cb0e79345 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 23 Nov 2019 17:27:51 +0800 Subject: [PATCH 144/192] Fix: shadowsocks not working --- shadowsocks.h | 2 +- utils.c | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/shadowsocks.h b/shadowsocks.h index 61b056b1..8dc956d2 100644 --- a/shadowsocks.h +++ b/shadowsocks.h @@ -30,6 +30,6 @@ typedef union { unsigned char addr_type; ss_header_ipv4 v4; ss_header_ipv6 v6; -} ss_header; +} PACKED ss_header; #endif diff --git a/utils.c b/utils.c index af79b4e6..71162f53 100644 --- a/utils.c +++ b/utils.c @@ -306,6 +306,13 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, retval = red_prepare_relay(ifname, addr->ss_family, readcb, writecb, errorcb, cbarg); if (retval) { + // write data to evbuffer so that data can be sent when connection is set up + if (bufferevent_write(retval, data, *len) != 0) { + log_errno(LOG_NOTICE, "bufferevent_write"); + *len = 0; // Nothing sent, caller needs to write data again when connection is setup. + goto fail; + } + relay_fd = bufferevent_getfd(retval); if (timeout_write) bufferevent_set_timeouts(retval, NULL, timeout_write); @@ -345,12 +352,6 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, log_errno(LOG_NOTICE, "connect"); goto fail; } - // write data to evbuffer so that data can be sent when connection is set up - if (bufferevent_write(retval, data, *len) != 0) { - log_errno(LOG_NOTICE, "bufferevent_write"); - *len = 0; // Nothing sent, caller needs to write data again when connection is setup. - goto fail; - } } return retval; From 9b915ac340a58487a4a26b45c620bf546f53e3e2 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 23 Nov 2019 17:31:54 +0800 Subject: [PATCH 145/192] Fix: Link failure on Ubuntu --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8e1e495d..6bf34826 100644 --- a/Makefile +++ b/Makefile @@ -49,11 +49,11 @@ override CFLAGS += -DENABLE_HTTPS_PROXY override FEATURES += ENABLE_HTTPS_PROXY $(info Compile with HTTPS proxy enabled.) endif -override LIBS += -lssl -lcrypto +override LIBS += -lssl -lcrypto -ldl override CFLAGS += -DUSE_CRYPTO_OPENSSL endif ifdef ENABLE_STATIC -override LIBS += -ldl -lz +override LIBS += -lz override LDFLAGS += -Wl,-static -static -static-libgcc -s override FEATURES += STATIC_COMPILE endif From ebbd1d0c04e47bc52706aeac3febcab0b6b35424 Mon Sep 17 00:00:00 2001 From: semigodking Date: Sat, 23 Nov 2019 17:37:44 +0800 Subject: [PATCH 146/192] Update ccpp.yml --- .github/workflows/ccpp.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index aa919197..a0f3c7c7 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -9,5 +9,7 @@ jobs: steps: - uses: actions/checkout@v1 + - name: install depends + run: apt install -y libevent-dev - name: make run: make DISABLE_SHADOWSOCKS=1 From 46b23142a0d806c62762e8d9abfe8e7ac241da50 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 25 Nov 2019 13:28:49 +0800 Subject: [PATCH 147/192] Fix: TFO not working --- utils.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/utils.c b/utils.c index 71162f53..634e56f9 100644 --- a/utils.c +++ b/utils.c @@ -306,13 +306,6 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, retval = red_prepare_relay(ifname, addr->ss_family, readcb, writecb, errorcb, cbarg); if (retval) { - // write data to evbuffer so that data can be sent when connection is set up - if (bufferevent_write(retval, data, *len) != 0) { - log_errno(LOG_NOTICE, "bufferevent_write"); - *len = 0; // Nothing sent, caller needs to write data again when connection is setup. - goto fail; - } - relay_fd = bufferevent_getfd(retval); if (timeout_write) bufferevent_set_timeouts(retval, NULL, timeout_write); @@ -321,13 +314,17 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, size_t s = sendto(relay_fd, data, * len, MSG_FASTOPEN, (struct sockaddr *)addr, addr_size(addr) ); - *len = 0; // Assume nothing sent, caller needs to write data again when connection is setup. if (s == -1) { if (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK) { // Remote server doesn't support tfo or it's the first connection to the server. // Connection will automatically fall back to conventional TCP. log_error(LOG_DEBUG, "TFO: no cookie"); + // write data to evbuffer so that data can be sent when connection is set up + if (bufferevent_write(retval, data, *len) != 0) { + log_errno(LOG_NOTICE, "bufferevent_write"); + goto fail; + } return retval; } else if (errno == EOPNOTSUPP || errno == EPROTONOSUPPORT || errno == ENOPROTOOPT) { @@ -352,6 +349,11 @@ struct bufferevent* red_connect_relay_tfo(const char *ifname, log_errno(LOG_NOTICE, "connect"); goto fail; } + // write data to evbuffer so that data can be sent when connection is set up + if (bufferevent_write(retval, data, *len) != 0) { + log_errno(LOG_NOTICE, "bufferevent_write"); + goto fail; + } } return retval; From 8d1f95dc588112afa7ca7cf711725e8e36f7b97e Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 28 Nov 2019 21:56:53 +0800 Subject: [PATCH 148/192] update CI action --- .github/workflows/ccpp.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index a0f3c7c7..5e4723f3 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -10,6 +10,8 @@ jobs: steps: - uses: actions/checkout@v1 - name: install depends - run: apt install -y libevent-dev - - name: make - run: make DISABLE_SHADOWSOCKS=1 + run: | + sudo apt update + sudo apt install -y libssl1.0-dev libevent-dev + - name: Build + run: make From fdd4fe262f5f8c89004fab9cce80e011d9ef9c73 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 23 Feb 2020 12:07:25 +0000 Subject: [PATCH 149/192] Allow redsocks bind to IPv6 address Not sure if it works. --- redsocks.c | 2 +- tcpdns.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/redsocks.c b/redsocks.c index 77f29623..d98a4236 100644 --- a/redsocks.c +++ b/redsocks.c @@ -981,7 +981,7 @@ static int redsocks_init_instance(redsocks_instance *instance) goto fail; } - fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + fd = socket(instance->config.bindaddr.ss_family, SOCK_STREAM, IPPROTO_TCP); if (fd == -1) { log_errno(LOG_ERR, "socket"); goto fail; diff --git a/tcpdns.c b/tcpdns.c index 3c06fb78..3cd5fc5d 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -454,7 +454,7 @@ static int tcpdns_init_instance(tcpdns_instance *instance) int fd = -1; char buf1[RED_INET_ADDRSTRLEN]; - fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fd = socket(instance->config.bindaddr.ss_family, SOCK_DGRAM, IPPROTO_UDP); if (fd == -1) { log_errno(LOG_ERR, "socket"); goto fail; From dad894da9c360829b9a566b25deac0cfb575ac03 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 25 Feb 2020 07:17:10 +0000 Subject: [PATCH 150/192] Fix: IPv6 support in shadowsocks-udp --- shadowsocks-udp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index 63fb7d82..52b941a0 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -198,7 +198,7 @@ static void ss_pkt_from_server(int fd, short what, void *_arg) } else if (header->addr_type == ss_addrtype_ipv6) { struct sockaddr_in6 pktaddr = { - .sin6_family = AF_INET, + .sin6_family = AF_INET6, .sin6_port = header->v6.port, }; memcpy(&pktaddr.sin6_addr, &header->v6.addr, sizeof(header->v6.addr)); From c23af86cc4e4f1e4a8a8a3430058ed906328a10a Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 27 Feb 2020 11:51:43 +0000 Subject: [PATCH 151/192] Fix: core dump caused by IPv6 address formatting --- utils.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils.c b/utils.c index 634e56f9..0f390fc5 100644 --- a/utils.c +++ b/utils.c @@ -418,6 +418,8 @@ char *red_inet_ntop(const struct sockaddr_storage* sa, char* buffer, size_t buff buffer[0] = '['; retval = inet_ntop(AF_INET6, &((const struct sockaddr_in6*)sa)->sin6_addr, buffer+1, buffer_size-1); port = ((struct sockaddr_in6*)sa)->sin6_port; + if (retval) + retval = buffer; } if (retval) { assert(retval == buffer); From 986229ab2c5c2872adacdf287f1a3465cd4f37ca Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 27 Feb 2020 14:23:57 +0000 Subject: [PATCH 152/192] Fix: IPv6 can not get dest address Not verified --- base.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/base.c b/base.c index db812499..01312dec 100644 --- a/base.c +++ b/base.c @@ -246,7 +246,12 @@ static int getdestaddr_iptables( socklen_t socklen = sizeof(*destaddr); int error; - error = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen); +#ifdef SOL_IPV6 + int sol = (client->ss_family == AF_INET6) ? SOL_IPV6 : SOL_IP; +#else + int sol = SOL_IP; +#endif + error = getsockopt(fd, sol, SO_ORIGINAL_DST, destaddr, &socklen); if (error) { log_errno(LOG_WARNING, "getsockopt"); return -1; From abeae8f91765b05fdb3130359b8ab91ac34eca7b Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 29 Feb 2020 03:05:46 +0000 Subject: [PATCH 153/192] Add IPv6 support to socks5 subsystem --- socks5-udp.c | 24 +++++++----------------- socks5.c | 47 +++++++++++++++++++++++++++++++---------------- socks5.h | 4 ++-- 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/socks5-udp.c b/socks5-udp.c index 3bcf941b..1f0e2d27 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -48,10 +48,9 @@ static struct evbuffer* socks5_mkpassword_plain_wrapper(void *p) static struct evbuffer* socks5_mkassociate(void *p) { - struct sockaddr_in sa; - //p = p; /* Make compiler happy */ + struct sockaddr_storage sa; memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; + sa.ss_family = ((const struct sockaddr_storage *)p)->ss_family; return socks5_mkcommand_plain(socks5_cmd_udp_associate, &sa); } @@ -128,24 +127,15 @@ static void socks5_forward_pkt(redudp_client *client, struct sockaddr *destaddr, socks5_udp_preamble req; struct msghdr msg; struct iovec io[2]; - size_t preamble_len; + size_t preamble_len = 0; - if (socks5client->udprelayaddr.sin_family == AF_INET) { - preamble_len = SOCKS5_UDP_PREAMBLE_SIZE_V4; - } - /* - else if (socks5client->udprelayaddr.ss_family == AF_INET6) { - preamble_len = SOCKS5_UDP_PREAMBLE_SIZE_V6; - } - */ - else { + if (socks5client->udprelayaddr.sin_family != AF_INET && socks5client->udprelayaddr.sin_family != AF_INET6) { redudp_log_errno(client, LOG_WARNING, "Unknown address type %d", socks5client->udprelayaddr.sin_family); return; } socks5_fill_preamble(&req, destaddr, &preamble_len); - ssize_t outgoing, fwdlen = pktlen + preamble_len; memset(&msg, 0, sizeof(msg)); msg.msg_name = &socks5client->udprelayaddr; @@ -221,7 +211,7 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) else if (pkt->header.addrtype == socks5_addrtype_ipv6) { struct sockaddr_in6 * src = (struct sockaddr_in6 *)&src_addr; src->sin6_family = AF_INET6; - memcpy(&src->sin6_addr, &pkt->header.addr.v6.addr, sizeof(src->sin6_addr)); + src->sin6_addr = pkt->header.addr.v6.addr; src->sin6_port = pkt->header.addr.v6.port; header_size += sizeof(socks5_addr_ipv6); } @@ -329,7 +319,7 @@ static void socks5_read_auth_reply(struct bufferevent *buffev, void *_arg) } error = redsocks_write_helper_ex_plain( - socks5client->relay, NULL, socks5_mkassociate, NULL, 0, /* last two are ignored */ + socks5client->relay, NULL, socks5_mkassociate, &client->destaddr, 0, sizeof(socks5_expected_assoc_reply), sizeof(socks5_expected_assoc_reply)); if (error) goto fail; @@ -366,7 +356,7 @@ static void socks5_read_auth_methods(struct bufferevent *buffev, void *_arg) } else if (reply.method == socks5_auth_none) { ierror = redsocks_write_helper_ex_plain( - socks5client->relay, NULL, socks5_mkassociate, NULL, 0, /* last two are ignored */ + socks5client->relay, NULL, socks5_mkassociate, &client->destaddr, 0, sizeof(socks5_expected_assoc_reply), sizeof(socks5_expected_assoc_reply)); if (ierror) goto fail; diff --git a/socks5.c b/socks5.c index 43da666a..66d1fe5b 100644 --- a/socks5.c +++ b/socks5.c @@ -128,23 +128,38 @@ struct evbuffer *socks5_mkpassword_plain(const char *login, const char *password return mkevbuffer(req, length); } -struct evbuffer *socks5_mkcommand_plain(int socks5_cmd, const struct sockaddr_in *destaddr) +struct evbuffer *socks5_mkcommand_plain(int socks5_cmd, const struct sockaddr_storage *destaddr) { - struct { - socks5_req head; - socks5_addr_ipv4 ip; - } PACKED req; - - assert(destaddr->sin_family == AF_INET); - - // FIXME: Support IPv6 - req.head.ver = socks5_ver; - req.head.cmd = socks5_cmd; - req.head.reserved = 0; - req.head.addrtype = socks5_addrtype_ipv4; - req.ip.addr = destaddr->sin_addr.s_addr; - req.ip.port = destaddr->sin_port; - return mkevbuffer(&req, sizeof(req)); + if (destaddr->ss_family == AF_INET) { + struct { + socks5_req head; + socks5_addr_ipv4 ip; + } PACKED req; + const struct sockaddr_in * addr = (const struct sockaddr_in *)destaddr; + + req.head.ver = socks5_ver; + req.head.cmd = socks5_cmd; + req.head.reserved = 0; + req.head.addrtype = socks5_addrtype_ipv4; + req.ip.addr = addr->sin_addr.s_addr; + req.ip.port = addr->sin_port; + return mkevbuffer(&req, sizeof(req)); + } + else { + struct { + socks5_req head; + socks5_addr_ipv6 ip; + } PACKED req; + const struct sockaddr_in6 * addr = (const struct sockaddr_in6 *)destaddr; + + req.head.ver = socks5_ver; + req.head.cmd = socks5_cmd; + req.head.reserved = 0; + req.head.addrtype = socks5_addrtype_ipv6; + req.ip.addr = addr->sin6_addr; + req.ip.port = addr->sin6_port; + return mkevbuffer(&req, sizeof(req)); + } } static struct evbuffer *socks5_mkconnect(redsocks_client *client) diff --git a/socks5.h b/socks5.h index aae857fa..2949317e 100644 --- a/socks5.h +++ b/socks5.h @@ -37,7 +37,7 @@ typedef struct socks5_addr_ipv4_t { } PACKED socks5_addr_ipv4; typedef struct socks5_addr_ipv6_t { - struct in6_addr addr; + struct in6_addr addr; uint16_t port; } PACKED socks5_addr_ipv6; @@ -102,7 +102,7 @@ const char* socks5_is_known_auth_method(socks5_method_reply *reply, int do_passw static const int socks5_cmd_connect = 1; static const int socks5_cmd_bind = 2; static const int socks5_cmd_udp_associate = 3; -struct evbuffer *socks5_mkcommand_plain(int socks5_cmd, const struct sockaddr_in *destaddr); +struct evbuffer *socks5_mkcommand_plain(int socks5_cmd, const struct sockaddr_storage *destaddr); /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ From ad2732e0a5a14c1049c65acc6cd3cd3e2f345ed3 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 1 Mar 2020 06:42:53 +0000 Subject: [PATCH 154/192] Add IPv6 support to redudp Not verified --- libc-compat.h | 10 ++++++++++ redudp.c | 29 +++++++++++++++++++++-------- utils.c | 7 +++++-- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/libc-compat.h b/libc-compat.h index 7e1eb878..95e38c47 100644 --- a/libc-compat.h +++ b/libc-compat.h @@ -17,6 +17,16 @@ # define IP_RECVORIGDSTADDR IP_ORIGDSTADDR #endif +#ifndef IPV6_ORIGDSTADDR +# warning Using hardcoded value for IPV6_ORIGDSTADDR as libc headers do not define it. +# define IPV6_ORIGDSTADDR 74 +#endif + +#ifndef IPV6_RECVORIGDSTADDR +# warning Using hardcoded value for IPV6_RECVORIGDSTADDR as libc headers do not define it. +# define IPV6_RECVORIGDSTADDR IPV6_ORIGDSTADDR +#endif + #ifndef IP_TRANSPARENT # warning Using hardcoded value for IP_TRANSPARENT as libc headers do not define it. # define IP_TRANSPARENT 19 diff --git a/redudp.c b/redudp.c index 0ebf14f3..fe224a86 100644 --- a/redudp.c +++ b/redudp.c @@ -220,7 +220,8 @@ static void bound_udp4_action(const void *nodep, const VISIT which, const int de static int do_tproxy(redudp_instance* instance) { - return instance->config.dest == NULL; + // When listner is bound to IPv6 address, TPORXY must be used. + return instance->config.bindaddr.ss_family == AF_INET6 || instance->config.dest == NULL; } struct sockaddr_storage* get_destaddr(redudp_client *client) @@ -575,7 +576,7 @@ static int redudp_init_instance(redudp_instance *instance) goto fail; } - fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fd = socket(instance->config.bindaddr.ss_family, SOCK_DGRAM, IPPROTO_UDP); if (fd == -1) { log_errno(LOG_ERR, "socket"); goto fail; @@ -586,13 +587,25 @@ static int redudp_init_instance(redudp_instance *instance) // iptables TPROXY target does not send packets to non-transparent sockets if (0 != make_socket_transparent(fd)) goto fail; - - error = setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &on, sizeof(on)); - if (error) { - log_errno(LOG_ERR, "setsockopt(listener, SOL_IP, IP_RECVORIGDSTADDR)"); - goto fail; + +#ifdef SOL_IPV6 + if (instance->config.bindaddr.ss_family == AF_INET) { +#endif + error = setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &on, sizeof(on)); + if (error) { + log_errno(LOG_ERR, "setsockopt(listener, SOL_IP, IP_RECVORIGDSTADDR)"); + goto fail; + } +#ifdef SOL_IPV6 } - + else { + error = setsockopt(fd, SOL_IPV6, IPV6_RECVORIGDSTADDR, &on, sizeof(on)); + if (error) { + log_errno(LOG_ERR, "setsockopt(listener, SOL_IPV6, IPV6_RECVORIGDSTADDR)"); + goto fail; + } + } +#endif log_error(LOG_INFO, "redudp @ %s: TPROXY", red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1))); } else { diff --git a/utils.c b/utils.c index 0f390fc5..1100d640 100644 --- a/utils.c +++ b/utils.c @@ -75,8 +75,11 @@ int red_recv_udp_pkt( memset(toaddr, 0, sizeof(*toaddr)); for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if ( - cmsg->cmsg_level == SOL_IP && - cmsg->cmsg_type == IP_ORIGDSTADDR && + ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_ORIGDSTADDR) +#ifdef SOL_IPV6 + || (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_ORIGDSTADDR) +#endif + ) && (cmsg->cmsg_len == CMSG_LEN(sizeof(struct sockaddr_in)) || cmsg->cmsg_len == CMSG_LEN(sizeof(struct sockaddr_in6))) && cmsg->cmsg_len <= CMSG_LEN(sizeof(*toaddr)) From fd8aad1aba73cde08e7ecacd2c038b1d15297d68 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 1 Mar 2020 11:00:14 +0000 Subject: [PATCH 155/192] Fix: Failed to get dest address --- base.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/base.c b/base.c index 01312dec..eaa14031 100644 --- a/base.c +++ b/base.c @@ -252,6 +252,12 @@ static int getdestaddr_iptables( int sol = SOL_IP; #endif error = getsockopt(fd, sol, SO_ORIGINAL_DST, destaddr, &socklen); +#ifdef SOL_IPV6 + if (error && sol == SOL_IPV6) { + sol = SOL_IP; + error = getsockopt(fd, sol, SO_ORIGINAL_DST, destaddr, &socklen); + } +#endif if (error) { log_errno(LOG_WARNING, "getsockopt"); return -1; From 6a793f549a661895f6d1f47bfb3a6abd790e5ae8 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 1 Mar 2020 13:42:34 +0000 Subject: [PATCH 156/192] Fix: broken redudp --- libc-compat.h | 5 +++++ redudp.c | 3 +-- utils.c | 13 ++++++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/libc-compat.h b/libc-compat.h index 95e38c47..eb4b340c 100644 --- a/libc-compat.h +++ b/libc-compat.h @@ -32,6 +32,11 @@ # define IP_TRANSPARENT 19 #endif +#ifndef IPV6_TRANSPARENT +# warning Using hardcoded value for IPV6_TRANSPARENT as libc headers do not define it. +# define IPV6_TRANSPARENT 75 +#endif + #ifndef SOL_IP # warning Using hardcoded value for SOL_IP as libc headers do not define it. # define SOL_IP IPPROTO_IP diff --git a/redudp.c b/redudp.c index fe224a86..bfe94248 100644 --- a/redudp.c +++ b/redudp.c @@ -220,8 +220,7 @@ static void bound_udp4_action(const void *nodep, const VISIT which, const int de static int do_tproxy(redudp_instance* instance) { - // When listner is bound to IPv6 address, TPORXY must be used. - return instance->config.bindaddr.ss_family == AF_INET6 || instance->config.dest == NULL; + return instance->config.dest == NULL; } struct sockaddr_storage* get_destaddr(redudp_client *client) diff --git a/utils.c b/utils.c index 1100d640..735a8003 100644 --- a/utils.c +++ b/utils.c @@ -77,9 +77,9 @@ int red_recv_udp_pkt( if ( ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_ORIGDSTADDR) #ifdef SOL_IPV6 - || (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_ORIGDSTADDR) + || (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_ORIGDSTADDR) #endif - ) && + ) && (cmsg->cmsg_len == CMSG_LEN(sizeof(struct sockaddr_in)) || cmsg->cmsg_len == CMSG_LEN(sizeof(struct sockaddr_in6))) && cmsg->cmsg_len <= CMSG_LEN(sizeof(*toaddr)) @@ -497,7 +497,14 @@ size_t get_write_hwm(struct bufferevent *bufev) int make_socket_transparent(int fd) { int on = 1; - int error = setsockopt(fd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); + int error = 0; +#ifdef SOL_IPV6 + // Try IPv6 first + error = setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, &on, sizeof(on)); + if (!error) + return error; +#endif + error = setsockopt(fd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); if (error) log_errno(LOG_ERR, "setsockopt(..., SOL_IP, IP_TRANSPARENT)"); return error; From 2b8fe69e4faba9b256808bc664d4c9daedd76f70 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 1 Mar 2020 14:31:45 +0000 Subject: [PATCH 157/192] Fix: redudp generates get origin address warning with IPv4 --- utils.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/utils.c b/utils.c index 735a8003..0dab1a56 100644 --- a/utils.c +++ b/utils.c @@ -499,15 +499,20 @@ int make_socket_transparent(int fd) int on = 1; int error = 0; #ifdef SOL_IPV6 + int error_6 = 0; // Try IPv6 first - error = setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, &on, sizeof(on)); - if (!error) - return error; + error_6 = setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, &on, sizeof(on)); + if (error_6) + log_errno(LOG_DEBUG, "setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT)"); #endif error = setsockopt(fd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); if (error) - log_errno(LOG_ERR, "setsockopt(..., SOL_IP, IP_TRANSPARENT)"); - return error; + log_errno(LOG_DEBUG, "setsockopt(fd, SOL_IP, IP_TRANSPARENT)"); + if (error && error_6) { + log_error(LOG_ERR, "Can not make socket transparent. See debug log for details."); + return error; + } + return 0; } int apply_tcp_fastopen(int fd) From a11c058d9fc9d757e27bf9d683dba1e9919311af Mon Sep 17 00:00:00 2001 From: livelazily Date: Tue, 10 Mar 2020 22:29:35 +0800 Subject: [PATCH 158/192] Fix build for macos (#143) * opensource-apple/xnu removed the version tags * Use the up-to-date xnu repo: apple/darwin-xnu * `v4` in pfvar.h is changed to `v4addr` https://github.com/darkk/redsocks/issues/75#issuecomment-392059972 * update macos document url --- Makefile | 6 +++--- README.md | 2 +- base.c | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 6bf34826..e32a4ff5 100644 --- a/Makefile +++ b/Makefile @@ -114,11 +114,11 @@ base.c: $(CONF) ifeq ($(OS), Darwin) $(OSX_HEADERS_PATH)/net/pfvar.h: - mkdir -p $(OSX_HEADERS_PATH)/net && curl -o $(OSX_HEADERS_PATH)/net/pfvar.h https://raw.githubusercontent.com/opensource-apple/xnu/$(OSX_VERSION)/bsd/net/pfvar.h + mkdir -p $(OSX_HEADERS_PATH)/net && curl -o $(OSX_HEADERS_PATH)/net/pfvar.h https://raw.githubusercontent.com/apple/darwin-xnu/master/bsd/net/pfvar.h $(OSX_HEADERS_PATH)/net/radix.h: - mkdir -p $(OSX_HEADERS_PATH)/net && curl -o $(OSX_HEADERS_PATH)/net/radix.h https://raw.githubusercontent.com/opensource-apple/xnu/$(OSX_VERSION)/bsd/net/radix.h + mkdir -p $(OSX_HEADERS_PATH)/net && curl -o $(OSX_HEADERS_PATH)/net/radix.h https://raw.githubusercontent.com/apple/darwin-xnu/master/bsd/net/radix.h $(OSX_HEADERS_PATH)/libkern/tree.h: - mkdir -p $(OSX_HEADERS_PATH)/libkern && curl -o $(OSX_HEADERS_PATH)/libkern/tree.h https://raw.githubusercontent.com/opensource-apple/xnu/$(OSX_VERSION)/libkern/libkern/tree.h + mkdir -p $(OSX_HEADERS_PATH)/libkern && curl -o $(OSX_HEADERS_PATH)/libkern/tree.h https://raw.githubusercontent.com/apple/darwin-xnu/master/libkern/libkern/tree.h endif $(DEPS): $(OSX_HEADERS) $(SRCS) diff --git a/README.md b/README.md index ce6767a4..d9f696b4 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ $ brew install openssl libevent Makefile include the folder of openssl headers and lib installed by brew. To build with PF and run on MacOS, you will need some pf headers that are not included with a standard MacOS installation. -You can find them on this repository : https://github.com/opensource-apple/xnu +You can find them on this repository : https://github.com/apple/darwin-xnu And the Makefile will going find this file for you Configurations diff --git a/base.c b/base.c index eaa14031..d4a99b14 100644 --- a/base.c +++ b/base.c @@ -198,9 +198,9 @@ static int getdestaddr_pf( char clientaddr_str[INET6_ADDRSTRLEN], bindaddr_str[INET6_ADDRSTRLEN]; memset(&nl, 0, sizeof(struct pfioc_natlook)); - nl.saddr.v4.s_addr = client->sin_addr.s_addr; + nl.saddr.v4addr.s_addr = client->sin_addr.s_addr; nl.sport = client->sin_port; - nl.daddr.v4.s_addr = bindaddr->sin_addr.s_addr; + nl.daddr.v4addr.s_addr = bindaddr->sin_addr.s_addr; nl.dport = bindaddr->sin_port; nl.af = AF_INET; nl.proto = IPPROTO_TCP; @@ -219,7 +219,7 @@ static int getdestaddr_pf( } destaddr->sin_family = AF_INET; destaddr->sin_port = nl.rdport; - destaddr->sin_addr = nl.rdaddr.v4; + destaddr->sin_addr = nl.rdaddr.v4addr; return 0; fail: From d94c245ea47859cda5b4b7373308589206b97bdc Mon Sep 17 00:00:00 2001 From: Hao Hu Date: Sun, 10 May 2020 14:05:15 +0800 Subject: [PATCH 159/192] two commits for fix bugs on macos 10.15 & openssl 1.1: (#146) * fix Invalid bind address format bug on macos * change version number * add __APPLE__ macro when bind to be compatible with linux and openbsd sockets definition This reverts commit 8ce821efca870ffe33ddf3f24582a5bfe071a8bd. --- Makefile | 2 +- redsocks.c | 10 +++++++--- redudp.c | 8 +++++++- tcpdns.c | 8 +++++++- utils.c | 18 ++++++++++-------- 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index e32a4ff5..82274944 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks2 -VERSION := 0.67 +VERSION := 0.68 OS := $(shell uname) LIBS := -levent diff --git a/redsocks.c b/redsocks.c index d98a4236..db0453ef 100644 --- a/redsocks.c +++ b/redsocks.c @@ -973,6 +973,7 @@ static int redsocks_init_instance(redsocks_instance *instance) * looks ugly. */ int error; + int bindaddr_len = 0; evutil_socket_t fd = -1; if (instance->relay_ss->instance_init @@ -1000,9 +1001,12 @@ static int redsocks_init_instance(redsocks_instance *instance) if (apply_reuseport(fd)) log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); - error = bind(fd, - (struct sockaddr*)&instance->config.bindaddr, - sizeof(instance->config.bindaddr)); +#ifdef __APPLE__ + bindaddr_len = instance->config.bindaddr.ss_len > 0 ? instance->config.bindaddr.ss_len : sizeof(instance->config.bindaddr); +#else + bindaddr_len = sizeof(instance->config.bindaddr); +#endif + error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, bindaddr_len); if (error) { log_errno(LOG_ERR, "bind"); goto fail; diff --git a/redudp.c b/redudp.c index bfe94248..dd56cbab 100644 --- a/redudp.c +++ b/redudp.c @@ -566,6 +566,7 @@ static int redudp_init_instance(redudp_instance *instance) */ int error; int fd = -1; + int bindaddr_len = 0; char buf1[RED_INET_ADDRSTRLEN], buf2[RED_INET_ADDRSTRLEN]; instance->shared_buff = &shared_buff[0]; @@ -616,7 +617,12 @@ static int redudp_init_instance(redudp_instance *instance) if (apply_reuseport(fd)) log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); - error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); +#ifdef __APPLE__ + bindaddr_len = instance->config.bindaddr.ss_len > 0 ? instance->config.bindaddr.ss_len : sizeof(instance->config.bindaddr); +#else + bindaddr_len = sizeof(instance->config.bindaddr); +#endif + error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, bindaddr_len); if (error) { log_errno(LOG_ERR, "bind"); goto fail; diff --git a/tcpdns.c b/tcpdns.c index 3cd5fc5d..5232b7e7 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -452,6 +452,7 @@ static int tcpdns_init_instance(tcpdns_instance *instance) */ int error; int fd = -1; + int bindaddr_len = 0; char buf1[RED_INET_ADDRSTRLEN]; fd = socket(instance->config.bindaddr.ss_family, SOCK_DGRAM, IPPROTO_UDP); @@ -462,7 +463,12 @@ static int tcpdns_init_instance(tcpdns_instance *instance) if (apply_reuseport(fd)) log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); - error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); +#ifdef __APPLE__ + bindaddr_len = instance->config.bindaddr.ss_len > 0 ? instance->config.bindaddr.ss_len : sizeof(instance->config.bindaddr); +#else + bindaddr_len = sizeof(instance->config.bindaddr); +#endif + error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, bindaddr_len); if (error) { log_errno(LOG_ERR, "bind"); goto fail; diff --git a/utils.c b/utils.c index 0dab1a56..5baf1f6f 100644 --- a/utils.c +++ b/utils.c @@ -500,19 +500,21 @@ int make_socket_transparent(int fd) int error = 0; #ifdef SOL_IPV6 int error_6 = 0; - // Try IPv6 first - error_6 = setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, &on, sizeof(on)); - if (error_6) - log_errno(LOG_DEBUG, "setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT)"); #endif + error = setsockopt(fd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); if (error) log_errno(LOG_DEBUG, "setsockopt(fd, SOL_IP, IP_TRANSPARENT)"); - if (error && error_6) { + +#ifdef SOL_IPV6 + error_6 = setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, &on, sizeof(on)); + if (error_6) + log_errno(LOG_DEBUG, "setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT)"); + + if (error && error_6) log_error(LOG_ERR, "Can not make socket transparent. See debug log for details."); - return error; - } - return 0; +#endif + return error; } int apply_tcp_fastopen(int fd) From c35c9422f5a6d04c63f1276a4e01c16b8229b09c Mon Sep 17 00:00:00 2001 From: chenrui Date: Sun, 5 Jul 2020 01:17:57 -0400 Subject: [PATCH 160/192] add license, Apache-2.0 --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 57296470b1c864c0c58e6170f2609f34c77d6323 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Fri, 13 Nov 2020 12:55:04 +0000 Subject: [PATCH 161/192] Try to make redsocks2 working with FreeBSD 12 --- base.c | 86 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/base.c b/base.c index d4a99b14..edacfa94 100644 --- a/base.c +++ b/base.c @@ -57,6 +57,7 @@ #include "main.h" #include "parser.h" #include "redsocks.h" +#include "utils.h" typedef struct redirector_subsys_t { int (*init)(); @@ -130,7 +131,7 @@ static int getdestaddr_ipf(int fd, struct sockaddr_storage *destaddr) { int natfd = instance.redirector->private; - struct natlookup natLookup; + struct natlookup nl; int x; #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027) struct ipfobj obj; @@ -140,17 +141,17 @@ static int getdestaddr_ipf(int fd, #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027) obj.ipfo_rev = IPFILTER_VERSION; - obj.ipfo_size = sizeof(natLookup); - obj.ipfo_ptr = &natLookup; + obj.ipfo_size = sizeof(nl); + obj.ipfo_ptr = &nl; obj.ipfo_type = IPFOBJ_NATLOOKUP; obj.ipfo_offset = 0; #endif - natLookup.nl_inport = bindaddr->sin_port; - natLookup.nl_outport = client->sin_port; - natLookup.nl_inip = bindaddr->sin_addr; - natLookup.nl_outip = client->sin_addr; - natLookup.nl_flags = IPN_TCP; + nl.nl_inport = ((struct sockaddr_in *)bindaddr)->sin_port; + nl.nl_outport = ((struct sockaddr_in *)client)->sin_port; + nl.nl_inip = ((struct sockaddr_in *)bindaddr)->sin_addr; + nl.nl_outip = ((struct sockaddr_in *)client)->sin_addr; + nl.nl_flags = IPN_TCP; #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027) x = ioctl(natfd, SIOCGNATL, &obj); #else @@ -162,10 +163,10 @@ static int getdestaddr_ipf(int fd, * this seems simpler. */ if (63 == siocgnatl_cmd) { - struct natlookup *nlp = &natLookup; + struct natlookup *nlp = &nl; x = ioctl(natfd, SIOCGNATL, &nlp); } else { - x = ioctl(natfd, SIOCGNATL, &natLookup); + x = ioctl(natfd, SIOCGNATL, &nl); } #endif if (x < 0) { @@ -173,9 +174,9 @@ static int getdestaddr_ipf(int fd, log_errno(LOG_WARNING, "ioctl(SIOCGNATL)\n"); return -1; } else { - destaddr->sin_family = AF_INET; - destaddr->sin_port = natLookup.nl_realport; - destaddr->sin_addr = natLookup.nl_realip; + destaddr->ss_family = AF_INET; + ((struct sockaddr_in *)destaddr)->sin_port = nl.nl_realport; + ((struct sockaddr_in *)destaddr)->sin_addr = nl.nl_realip; return 0; } } @@ -189,20 +190,40 @@ static int redir_init_pf() // FIXME: Support IPv6 static int getdestaddr_pf( - int fd, const struct sockaddr_in *client, const struct sockaddr_in *bindaddr, - struct sockaddr_in *destaddr) + int fd, + const struct sockaddr_storage *client, + const struct sockaddr_storage *bindaddr, + struct sockaddr_storage *destaddr) { int pffd = instance.redirector->private; struct pfioc_natlook nl; int saved_errno; - char clientaddr_str[INET6_ADDRSTRLEN], bindaddr_str[INET6_ADDRSTRLEN]; + char clientaddr_str[RED_INET_ADDRSTRLEN], bindaddr_str[RED_INET_ADDRSTRLEN]; memset(&nl, 0, sizeof(struct pfioc_natlook)); - nl.saddr.v4addr.s_addr = client->sin_addr.s_addr; - nl.sport = client->sin_port; - nl.daddr.v4addr.s_addr = bindaddr->sin_addr.s_addr; - nl.dport = bindaddr->sin_port; - nl.af = AF_INET; + if (client->ss_family == AF_INET) { + nl.saddr.v4 = ((const struct sockaddr_in *)client)->sin_addr; + nl.sport = ((struct sockaddr_in *)client)->sin_port; + } + else if (client->ss_family == AF_INET6) { + memcpy(&nl.saddr.v6, &((const struct sockaddr_in6 *)client)->sin6_addr, sizeof(struct in6_addr)); + nl.sport = ((struct sockaddr_in6 *)client)->sin6_port; + } + else { + goto fail; + } + if (bindaddr->ss_family == AF_INET) { + nl.daddr.v4 = ((const struct sockaddr_in *)bindaddr)->sin_addr; + nl.dport = ((struct sockaddr_in *)bindaddr)->sin_port; + } + else if (bindaddr->ss_family == AF_INET6) { + memcpy(&nl.daddr.v6, &((const struct sockaddr_in6 *)bindaddr)->sin6_addr, sizeof(struct in6_addr)); + nl.dport = ((struct sockaddr_in6 *)bindaddr)->sin6_port; + } + else { + goto fail; + } + nl.af = client->ss_family; // Use same address family ass client nl.proto = IPPROTO_TCP; nl.direction = PF_OUT; @@ -217,21 +238,24 @@ static int getdestaddr_pf( goto fail; } } - destaddr->sin_family = AF_INET; - destaddr->sin_port = nl.rdport; - destaddr->sin_addr = nl.rdaddr.v4addr; + destaddr->ss_family = nl.af; + if (nl.af == AF_INET) { + ((struct sockaddr_in *)destaddr)->sin_port = nl.rdport; + ((struct sockaddr_in *)destaddr)->sin_addr = nl.rdaddr.v4; + } + else { + ((struct sockaddr_in6 *)destaddr)->sin6_port = nl.rdport; + memcpy(&(((struct sockaddr_in6 *)destaddr)->sin6_addr), &nl.rdaddr.v6, sizeof(struct in6_addr)); + } return 0; fail: saved_errno = errno; - if (!inet_ntop(client->sin_family, &client->sin_addr, clientaddr_str, sizeof(clientaddr_str))) - strncpy(clientaddr_str, "???", sizeof(clientaddr_str)); - if (!inet_ntop(bindaddr->sin_family, &bindaddr->sin_addr, bindaddr_str, sizeof(bindaddr_str))) - strncpy(bindaddr_str, "???", sizeof(bindaddr_str)); - + red_inet_ntop(client, clientaddr_str, sizeof(clientaddr_str)); + red_inet_ntop(bindaddr, bindaddr_str, sizeof(bindaddr_str)); errno = saved_errno; - log_errno(LOG_WARNING, "ioctl(DIOCNATLOOK {src=%s:%d, dst=%s:%d})", - clientaddr_str, ntohs(nl.sport), bindaddr_str, ntohs(nl.dport)); + log_errno(LOG_WARNING, "ioctl(DIOCNATLOOK {src=%s, dst=%s})", + clientaddr_str, bindaddr_str); return -1; } #endif From 088914be5fa6ea77a89caccddea8764beab285dc Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 24 Nov 2020 22:01:45 +0000 Subject: [PATCH 162/192] Fix: error in bind on FreeBSD --- Makefile | 3 +++ redsocks.c | 6 +++--- redudp.c | 2 +- tcpdns.c | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 82274944..70237a24 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,9 @@ override CFLAGS += -D_BSD_SOURCE -D_DEFAULT_SOURCE -Wall ifeq ($(OS), Linux) override CFLAGS += -std=c99 -D_XOPEN_SOURCE=600 endif +ifeq ($(OS), FreeBSD) +override CFLAGS +=-I/usr/local/include -L/usr/local//lib +endif ifeq ($(OS), Darwin) override CFLAGS +=-I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib SHELL := /bin/bash diff --git a/redsocks.c b/redsocks.c index db0453ef..4c1b58d6 100644 --- a/redsocks.c +++ b/redsocks.c @@ -998,10 +998,10 @@ static int redsocks_init_instance(redsocks_instance *instance) if (make_socket_transparent(fd)) log_error(LOG_WARNING, "Continue without TPROXY support"); - if (apply_reuseport(fd)) - log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); +// if (apply_reuseport(fd)) +// log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__FreeBSD__) bindaddr_len = instance->config.bindaddr.ss_len > 0 ? instance->config.bindaddr.ss_len : sizeof(instance->config.bindaddr); #else bindaddr_len = sizeof(instance->config.bindaddr); diff --git a/redudp.c b/redudp.c index dd56cbab..d58f9b24 100644 --- a/redudp.c +++ b/redudp.c @@ -617,7 +617,7 @@ static int redudp_init_instance(redudp_instance *instance) if (apply_reuseport(fd)) log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__FreeBSD__) bindaddr_len = instance->config.bindaddr.ss_len > 0 ? instance->config.bindaddr.ss_len : sizeof(instance->config.bindaddr); #else bindaddr_len = sizeof(instance->config.bindaddr); diff --git a/tcpdns.c b/tcpdns.c index 5232b7e7..7d15f8d9 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -463,7 +463,7 @@ static int tcpdns_init_instance(tcpdns_instance *instance) if (apply_reuseport(fd)) log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__FreeBSD__) bindaddr_len = instance->config.bindaddr.ss_len > 0 ? instance->config.bindaddr.ss_len : sizeof(instance->config.bindaddr); #else bindaddr_len = sizeof(instance->config.bindaddr); From 71529143f538f6580e4401c5a5e2bf9c5ab27aa2 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 25 Nov 2020 01:06:29 +0000 Subject: [PATCH 163/192] Github actions for build on FreeBSD --- .github/workflows/ccpp.yml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 5e4723f3..5552d635 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -4,9 +4,7 @@ on: [push] jobs: build: - runs-on: ubuntu-latest - steps: - uses: actions/checkout@v1 - name: install depends @@ -15,3 +13,21 @@ jobs: sudo apt install -y libssl1.0-dev libevent-dev - name: Build run: make + + build_freebsd: + runs-on: macos-latest + name: A job to run test FreeBSD + # env: + steps: + - uses: actions/checkout@v2 + - name: Build in FreeBSD + id: test + uses: vmactions/freebsd-vm@v0.0.9 + with: + # envs: 'MYTOKEN MYTOKEN2' + usesh: true + prepare: pkg install -y curl libevent gmake + run: | + pwd + freebsd-version + gmake DISABLE_SHADOWSOCKS=1 From 45c881e89d3102e1b5bbb95d4c42e14258b0a832 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 25 Nov 2020 01:48:16 +0000 Subject: [PATCH 164/192] Add build status to README --- .github/workflows/{ccpp.yml => freebsd_build.yml} | 13 +------------ .github/workflows/linux_build.yml | 15 +++++++++++++++ README.md | 5 ++++- 3 files changed, 20 insertions(+), 13 deletions(-) rename .github/workflows/{ccpp.yml => freebsd_build.yml} (63%) create mode 100644 .github/workflows/linux_build.yml diff --git a/.github/workflows/ccpp.yml b/.github/workflows/freebsd_build.yml similarity index 63% rename from .github/workflows/ccpp.yml rename to .github/workflows/freebsd_build.yml index 5552d635..7a1e0878 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/freebsd_build.yml @@ -1,20 +1,9 @@ -name: C/C++ CI +name: freebsd_build on: [push] jobs: build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: install depends - run: | - sudo apt update - sudo apt install -y libssl1.0-dev libevent-dev - - name: Build - run: make - - build_freebsd: runs-on: macos-latest name: A job to run test FreeBSD # env: diff --git a/.github/workflows/linux_build.yml b/.github/workflows/linux_build.yml new file mode 100644 index 00000000..a598db41 --- /dev/null +++ b/.github/workflows/linux_build.yml @@ -0,0 +1,15 @@ +name: linux_build + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: install depends + run: | + sudo apt update + sudo apt install -y libssl1.0-dev libevent-dev + - name: Build + run: make diff --git a/README.md b/README.md index d9f696b4..69767dc1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ REDSOCKS2 ========= +[![Linux Build Status](https://github.com/semigodking/redsocks/workflows/linux_build/badge.svg)](https://github.com/semigodking/redsocks/actions) +[![FreeBSD Build Status](https://github.com/semigodking/redsocks/workflows/freebsd_build/badge.svg)](https://github.com/semigodking/redsocks/actions) + This is a modified version of original redsocks. The name is changed to REDSOCKS2 to distinguish with original redsocks. REDSOCKS2 contains several new features besides many bug fixes to original @@ -15,7 +18,7 @@ need of blacklist. 7. Support Ful-cone NAT Traversal when working with shadowsocks or socks5 proxy. 8. Integrated HTTPS proxy support(HTTP CONNECT over SSL). 9. Support TCP Fast Open on local server side and shadowsocks client side -10.Support port reuse ([SO_REUSEPORT](https://lwn.net/Articles/542629/)) +10. Support port reuse ([SO_REUSEPORT](https://lwn.net/Articles/542629/)) [Chinese Reference](https://github.com/semigodking/redsocks/wiki) From 1951b49b774b0aab702348d0370e26bae1c3a7df Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 29 Dec 2020 10:49:29 +0800 Subject: [PATCH 165/192] Allow 'direct' to forward connections to specific destination --- direct.c | 11 ++++++++--- redsocks.conf.example | 9 ++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/direct.c b/direct.c index 9521fadb..d7179efc 100644 --- a/direct.c +++ b/direct.c @@ -61,13 +61,18 @@ static void direct_write_cb(struct bufferevent *buffev, void *_arg) static int direct_connect_relay(redsocks_client *client) { - char * interface = client->instance->config.interface; - struct timeval tv = {client->instance->config.timeout, 0}; + redsocks_instance * instance = client->instance; + char * interface = instance->config.interface; + struct timeval tv = {instance->config.timeout, 0}; + struct sockaddr_storage * destaddr = &client->destaddr; + if (instance->config.relay) { + destaddr = &instance->config.relayaddr; + } // Allowing binding relay socket to specified IP for outgoing connections client->relay = red_connect_relay( interface, - &client->destaddr, + destaddr, NULL, redsocks_relay_connected, redsocks_event_error, diff --git a/redsocks.conf.example b/redsocks.conf.example index 3a641536..82d5c373 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -71,7 +71,7 @@ redsocks { // min_accept_backoff = 100; // max_accept_backoff = 60000; - // `relay' is IP address and port of proxy-server. Domain name is not + // `relay` is IP address and port of proxy-server. Domain name is not // supported yet. // Can be: // [IPv6Address]:port @@ -84,6 +84,13 @@ redsocks { // known types: socks4, socks5, http-connect, http-relay // New types: direct, shadowsocks, https-connect + // For type direct: + // if `relay` is not specified, connections will be forwarded to + // original destinations. + // if `relay` is filled with valid IP address, connections will be + // forwarded to IP address defined in `relay`. It is useful when you + // just want to forward connections to a specific IP address without + // transparent proxy. E.g. forward IPv4:port to IPv6:port. type = socks5; // Specify interface for outgoing connections. From c1676bcfc7b6f02e81fcf9eb8abb36831771feec Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Mon, 29 May 2023 11:45:07 +0800 Subject: [PATCH 166/192] Fix UDP source address compare --- redudp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/redudp.c b/redudp.c index d58f9b24..0a851ff5 100644 --- a/redudp.c +++ b/redudp.c @@ -413,7 +413,10 @@ static void redudp_pkt_from_client(int fd, short what, void *_arg) // TODO: this lookup may be SLOOOOOW. list_for_each_entry(tmp, &self->clients, list) { - if (0 == memcmp(&clientaddr, &tmp->clientaddr, sizeof(clientaddr))) { + if (0 == evutil_sockaddr_cmp((struct sockaddr *)&clientaddr, + (struct sockaddr *)&tmp->clientaddr, + 1)) { + client = tmp; break; } From 5a1b5334bbb248f02c660d7850e3444551c446f5 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 3 Jun 2023 18:19:32 +0800 Subject: [PATCH 167/192] Fix linux build action --- .github/workflows/linux_build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/linux_build.yml b/.github/workflows/linux_build.yml index a598db41..c62365b5 100644 --- a/.github/workflows/linux_build.yml +++ b/.github/workflows/linux_build.yml @@ -9,6 +9,7 @@ jobs: - uses: actions/checkout@v1 - name: install depends run: | + sudo echo "deb http://security.ubuntu.com/ubuntu bionic-security main" >> /etc/apt/sources.list sudo apt update sudo apt install -y libssl1.0-dev libevent-dev - name: Build From 4ac3b770cb39ec1e6dbf123b2118be6d864d9fc0 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 3 Jun 2023 18:25:29 +0800 Subject: [PATCH 168/192] Fix linux build action --- .github/workflows/linux_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux_build.yml b/.github/workflows/linux_build.yml index c62365b5..496d4849 100644 --- a/.github/workflows/linux_build.yml +++ b/.github/workflows/linux_build.yml @@ -9,7 +9,7 @@ jobs: - uses: actions/checkout@v1 - name: install depends run: | - sudo echo "deb http://security.ubuntu.com/ubuntu bionic-security main" >> /etc/apt/sources.list + echo "deb http://security.ubuntu.com/ubuntu bionic-security main" | sudo tee -a /etc/apt/sources.list sudo apt update sudo apt install -y libssl1.0-dev libevent-dev - name: Build From 2db0ba82871085520f050a2010d039d006dde4f1 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 3 Jun 2023 19:22:58 +0800 Subject: [PATCH 169/192] Fix Linux build action again --- .github/workflows/linux_build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/linux_build.yml b/.github/workflows/linux_build.yml index 496d4849..f70a50ec 100644 --- a/.github/workflows/linux_build.yml +++ b/.github/workflows/linux_build.yml @@ -9,7 +9,10 @@ jobs: - uses: actions/checkout@v1 - name: install depends run: | + sudo apt update + sudo apt install -y gnupg2 echo "deb http://security.ubuntu.com/ubuntu bionic-security main" | sudo tee -a /etc/apt/sources.list + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32 sudo apt update sudo apt install -y libssl1.0-dev libevent-dev - name: Build From 7fe2741cf436f025eb44ff9d4f4b2ebe9f13c0fb Mon Sep 17 00:00:00 2001 From: pato-pan <131620424+pato-pan@users.noreply.github.com> Date: Wed, 2 Aug 2023 06:20:33 -0400 Subject: [PATCH 170/192] Update redsocks.conf.example (#188) minor misspelling of the word cache (cahce) --- redsocks.conf.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redsocks.conf.example b/redsocks.conf.example index 82d5c373..0e5821b3 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -179,7 +179,7 @@ ipcache { // Configure IP cache cache_size = 4; // Maximum number of IP's in 1K. stale_time = 900; // Seconds to stale an IP in cache since it is added - // into cahce. + // into cache. // Set it to 0 to disable cache stale. port_check = 1; // Whether to distinguish port number in address cache_file = "/tmp/ipcache.txt"; // File used to store blocked IP's in cache. From 99b11228192003af5e4d60bc2b74eaffb688caf2 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 20 Sep 2023 18:29:24 +0800 Subject: [PATCH 171/192] Accept hostname in 'relay' --- README.md | 13 +++++++------ redsocks.c | 25 ++++++++++++++++++++++-- redsocks.conf.example | 9 +++++---- redudp.c | 25 ++++++++++++++++++++++-- utils.c | 44 +++++++++++++++++++++++++++++++++++++++++++ utils.h | 1 + 6 files changed, 103 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 69767dc1..d4debd77 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,15 @@ redsocks. 1. Redirect TCP connections which are blocked via proxy automatically without need of blacklist. 2. Redirect UDP based DNS requests via TCP connection. -3. Integrated [shadowsocks](http://shadowsocks.org/) proxy support(IPv4 Only). +3. Integrated [shadowsocks](http://shadowsocks.org/) proxy support. 4. Redirect TCP connections without proxy. 5. Redirect TCP connections via specified network interface. 6. UDP transparent proxy via shadowsocks proxy. 7. Support Ful-cone NAT Traversal when working with shadowsocks or socks5 proxy. 8. Integrated HTTPS proxy support(HTTP CONNECT over SSL). -9. Support TCP Fast Open on local server side and shadowsocks client side -10. Support port reuse ([SO_REUSEPORT](https://lwn.net/Articles/542629/)) +9. Support TCP Fast Open on local server side and shadowsocks client side. +10. Support port reuse ([SO_REUSEPORT](https://lwn.net/Articles/542629/)). +11. Support IPv6. [Chinese Reference](https://github.com/semigodking/redsocks/wiki) @@ -133,7 +134,7 @@ by field 'login'. timeout = 13; autoproxy = 1; login = "aes-128-cfb"; // field 'login' is reused as encryption - // method of shadowsocks + // method of shadowsocks password = "your password"; // Your shadowsocks password } @@ -217,8 +218,8 @@ with the following config section. // Transform UDP DNS requests into TCP DNS requests. // You can also redirect connections to external TCP DNS server to // REDSOCKS transparent proxy via iptables. - bind = "192.168.1.1:1053"; // Local server to act as DNS server - tcpdns1 = "8.8.4.4:53"; // DNS server that supports TCP DNS requests + bind = "192.168.1.1:1053"; // Local server to act as DNS server + tcpdns1 = "8.8.4.4:53"; // DNS server that supports TCP DNS requests tcpdns2 = 8.8.8.8; // DNS server that supports TCP DNS requests timeout = 4; // Timeout value for TCP DNS requests } diff --git a/redsocks.c b/redsocks.c index 4c1b58d6..204f9601 100644 --- a/redsocks.c +++ b/redsocks.c @@ -205,8 +205,29 @@ static int redsocks_onexit(parser_section *section) if (!err && instance->config.relay) { struct sockaddr * addr = (struct sockaddr *)&instance->config.relayaddr; int addr_size = sizeof(instance->config.relayaddr); - if (evutil_parse_sockaddr_port(instance->config.relay, addr, &addr_size)) - err = "invalid relay address"; + if (evutil_parse_sockaddr_port(instance->config.relay, addr, &addr_size)) { + char * pos = strchr(instance->config.relay, ':'); + char * host = NULL; + if (pos != NULL) + host = strndup(instance->config.relay, pos - instance->config.relay); + else + host = instance->config.relay; + int result = resolve_hostname(host, AF_INET, addr); + if (result != 0) { + result = resolve_hostname(host, AF_INET6, addr); + } + if (result != 0) { + err = "invalid relay address"; + } + if (!err && pos != NULL) { + if (addr->sa_family == AF_INET) + ((struct sockaddr_in*)addr)->sin_port = htons(atoi(pos+1)); + else + ((struct sockaddr_in6*)addr)->sin6_port = htons(atoi(pos+1)); + } + if (host != instance->config.relay) + free(host); + } } if (!err && instance->config.type) { diff --git a/redsocks.conf.example b/redsocks.conf.example index 0e5821b3..d93532ac 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -71,15 +71,16 @@ redsocks { // min_accept_backoff = 100; // max_accept_backoff = 60000; - // `relay` is IP address and port of proxy-server. Domain name is not - // supported yet. + // `relay` is IP address and port of proxy-server. // Can be: // [IPv6Address]:port // [IPv6Address] // IPv6Address // IPv4Address:port // IPv4Address + // domain.name:port // If no port is given, 0 is used. Usually, a valid port is required. + // Random valid resolved IP address will be used. relay = "127.0.0.1:1080"; // known types: socks4, socks5, http-connect, http-relay @@ -111,7 +112,7 @@ redsocks { timeout = 10; // login = "foobar";// field 'login' is reused as encryption - // method of shadowsocks + // method of shadowsocks // password = "baz"; } @@ -124,7 +125,7 @@ redudp { // `relay' is ip and port of socks5 proxy server. relay = "10.0.0.1:1080"; login = username;// field 'login' is reused as encryption - // method of shadowsocks + // method of shadowsocks password = pazzw0rd; // know types: socks5, shadowsocks diff --git a/redudp.c b/redudp.c index 0a851ff5..3ad0f24b 100644 --- a/redudp.c +++ b/redudp.c @@ -516,8 +516,29 @@ static int redudp_onexit(parser_section *section) if (!err && instance->config.relay) { struct sockaddr * addr = (struct sockaddr *)&instance->config.relayaddr; int addr_size = sizeof(instance->config.relayaddr); - if (evutil_parse_sockaddr_port(instance->config.relay, addr, &addr_size)) - err = "invalid relay address"; + if (evutil_parse_sockaddr_port(instance->config.relay, addr, &addr_size)) { + char * pos = strchr(instance->config.relay, ':'); + char * host = NULL; + if (pos != NULL) + host = strndup(instance->config.relay, pos - instance->config.relay); + else + host = instance->config.relay; + int result = resolve_hostname(host, AF_INET, addr); + if (result != 0) { + result = resolve_hostname(host, AF_INET6, addr); + } + if (result != 0) { + err = "invalid relay address"; + } + if (!err && pos != NULL) { + if (addr->sa_family == AF_INET) + ((struct sockaddr_in*)addr)->sin_port = htons(atoi(pos+1)); + else + ((struct sockaddr_in6*)addr)->sin6_port = htons(atoi(pos+1)); + } + if (host != instance->config.relay) + free(host); + } } else if (!instance->config.relay) err = "missing relay address"; diff --git a/utils.c b/utils.c index 5baf1f6f..7c78d8a3 100644 --- a/utils.c +++ b/utils.c @@ -573,5 +573,49 @@ void replace_eventcb(struct bufferevent * buffev, bufferevent_event_cb eventcb) #endif } +int resolve_hostname(const char *hostname, int sa_family, struct sockaddr *addr) { + char addr_str[RED_INET_ADDRSTRLEN]; + struct addrinfo *ainfo, hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = sa_family; /* IPv4-only */ + hints.ai_socktype = SOCK_STREAM; /* I want to have one address once and ONLY once, that's why I specify socktype and protocol */ + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_ADDRCONFIG; /* I don't need IPv4 addrs without IPv4 connectivity */ + int addr_err = getaddrinfo(hostname, NULL, &hints, &ainfo); + if (addr_err == 0) { + int count, taken; + struct addrinfo *iter; + struct sockaddr *resolved_addr; + for (iter = ainfo, count = 0; iter; iter = iter->ai_next, ++count) + ; + taken = red_randui32() % count; + for (iter = ainfo; taken > 0; iter = iter->ai_next, --taken) + ; + resolved_addr = iter->ai_addr; + assert(resolved_addr->sa_family == iter->ai_family && iter->ai_family == sa_family); + if (count != 1) + log_error(LOG_WARNING, "%s resolves to %d addresses, using %s", + hostname, + count, + red_inet_ntop((const struct sockaddr_storage*)resolved_addr, addr_str, sizeof(addr_str))); + if (resolved_addr->sa_family == AF_INET) { + memcpy(&(((struct sockaddr_in*)addr)->sin_addr), + &(((struct sockaddr_in*)resolved_addr)->sin_addr), + sizeof(struct in_addr)); + } + else if (resolved_addr->sa_family == AF_INET6) { + memcpy(&(((struct sockaddr_in6*)addr)->sin6_addr), + &(((struct sockaddr_in6*)resolved_addr)->sin6_addr), + sizeof(struct in6_addr)); + } + freeaddrinfo(ainfo); + addr->sa_family = sa_family; + return 0; + } + else { + log_errno(LOG_ERR, "Unable to resolve hostname: %s", hostname); + return -1; + } +} /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ diff --git a/utils.h b/utils.h index 746b5a86..a0d144da 100644 --- a/utils.h +++ b/utils.h @@ -118,6 +118,7 @@ void replace_eventcb(struct bufferevent * buffev, bufferevent_event_cb eventcb); # define RED_INET_ADDRSTRLEN (1 + INET6_ADDRSTRLEN + 1 + 1 + 5 + 1) // [ + addr + ] + : + port + \0 #endif char *red_inet_ntop(const struct sockaddr_storage * sa, char* buffer, size_t buffer_size); +int resolve_hostname(const char *hostname, int sa_family, struct sockaddr *addr); /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ From 1b532b3e78b0f31f3308ec19cbfb4cd435d7660f Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 21 Sep 2023 22:41:13 +0800 Subject: [PATCH 172/192] Compatible with non-standard HTTP proxies --- http-connect.c | 4 ++-- http-relay.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/http-connect.c b/http-connect.c index 245b3e69..170e3e85 100644 --- a/http-connect.c +++ b/http-connect.c @@ -72,7 +72,7 @@ void httpc_read_cb(struct bufferevent *buffev, void *_arg) if (client->state == httpc_request_sent) { size_t len = evbuffer_get_length(evbinput); - char *line = evbuffer_readln(evbinput, NULL, EVBUFFER_EOL_CRLF_STRICT); + char *line = evbuffer_readln(evbinput, NULL, EVBUFFER_EOL_CRLF); if (line) { unsigned int code; if (sscanf(line, "HTTP/%*u.%*u %u", &code) == 1) { // 1 == one _assigned_ match @@ -153,7 +153,7 @@ void httpc_read_cb(struct bufferevent *buffev, void *_arg) return; while (client->state == httpc_reply_came) { - char *line = evbuffer_readln(evbinput, NULL, EVBUFFER_EOL_CRLF_STRICT); + char *line = evbuffer_readln(evbinput, NULL, EVBUFFER_EOL_CRLF); if (line) { if (strlen(line) == 0) { client->state = httpc_headers_skipped; diff --git a/http-relay.c b/http-relay.c index 3e3e311b..a1a203c7 100644 --- a/http-relay.c +++ b/http-relay.c @@ -160,7 +160,7 @@ static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg) if (client->state == httpr_request_sent) { size_t len = evbuffer_get_length(evbinput); - char *line = evbuffer_readln(evbinput, NULL, EVBUFFER_EOL_CRLF_STRICT); + char *line = evbuffer_readln(evbinput, NULL, EVBUFFER_EOL_CRLF); if (line) { httpr_buffer_append(&httpr->relay_buffer, line, strlen(line)); httpr_buffer_append(&httpr->relay_buffer, "\r\n", 2); @@ -241,7 +241,7 @@ static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg) return; while (client->state == httpr_reply_came) { - char *line = evbuffer_readln(evbinput, NULL, EVBUFFER_EOL_CRLF_STRICT); + char *line = evbuffer_readln(evbinput, NULL, EVBUFFER_EOL_CRLF); if (line) { httpr_buffer_append(&httpr->relay_buffer, line, strlen(line)); httpr_buffer_append(&httpr->relay_buffer, "\r\n", 2); @@ -496,7 +496,7 @@ static void httpr_client_read_cb(struct bufferevent *buffev, void *_arg) char *line = NULL; int connect_relay = 0; - while (!connect_relay && (line = evbuffer_readln(bufferevent_get_input(buffev), NULL, EVBUFFER_EOL_CRLF_STRICT))) { + while (!connect_relay && (line = evbuffer_readln(bufferevent_get_input(buffev), NULL, EVBUFFER_EOL_CRLF))) { int skip_line = 0; int do_drop = 0; From 198cca29f752570f9cc53731c94685fc467753c0 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 28 Sep 2023 15:09:13 +0800 Subject: [PATCH 173/192] Remove staled patch --- README.md | 8 +----- patches/disable-ss.patch | 61 ---------------------------------------- 2 files changed, 1 insertion(+), 68 deletions(-) delete mode 100644 patches/disable-ss.patch diff --git a/README.md b/README.md index d4debd77..1ed32bd3 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,7 @@ compile like (Require libevent2 compiled with OpenSSL support): $ make ENABLE_HTTPS_PROXY=true ``` -To compile on newer systems with OpenSSL 1.1.0 and newer (disables shadowsocks support): -``` -$ git apply patches/disable-ss.patch -$ make -``` - -To compile on newer systems with OpenSSL 1.1.1+ (just disable shadowsocks support, no patch need and worked with ENABLE_HTTPS_PROXY. DO NOT APPLY THE PATCH!): +To compile on newer systems with OpenSSL 1.1.1+ (just disable shadowsocks support, no patch need and worked with `ENABLE_HTTPS_PROXY`.): ``` $ make DISABLE_SHADOWSOCKS=true ``` diff --git a/patches/disable-ss.patch b/patches/disable-ss.patch deleted file mode 100644 index ccd93466..00000000 --- a/patches/disable-ss.patch +++ /dev/null @@ -1,61 +0,0 @@ -diff --git a/Makefile b/Makefile -index c1e064a..ee6a07a 100644 ---- a/Makefile -+++ b/Makefile -@@ -1,5 +1,5 @@ --OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o encrypt.o shadowsocks.o http-connect.o \ -- socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o socks5-udp.o shadowsocks-udp.o \ -+OBJS := parser.o main.o redsocks.o log.o direct.o ipcache.o autoproxy.o http-connect.o \ -+ socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o socks5-udp.o \ - tcpdns.o gen/version.o - SRCS := $(OBJS:.o=.c) - CONF := config.h -@@ -40,7 +40,7 @@ override LIBS += -levent_openssl - override CFLAGS += -DENABLE_HTTPS_PROXY - $(info Compile with HTTPS proxy enabled.) - endif --override LIBS += -lssl -lcrypto -+#override LIBS += -lssl -lcrypto - override CFLAGS += -DUSE_CRYPTO_OPENSSL - endif - ifdef ENABLE_STATIC -diff --git a/redsocks.c b/redsocks.c -index 69022b4..66313a0 100644 ---- a/redsocks.c -+++ b/redsocks.c -@@ -56,7 +56,7 @@ extern relay_subsys https_connect_subsys; - extern relay_subsys http_relay_subsys; - extern relay_subsys socks4_subsys; - extern relay_subsys socks5_subsys; --extern relay_subsys shadowsocks_subsys; -+//extern relay_subsys shadowsocks_subsys; - static relay_subsys *relay_subsystems[] = - { - &direct_connect_subsys, -@@ -64,7 +64,7 @@ static relay_subsys *relay_subsystems[] = - &http_relay_subsys, - &socks4_subsys, - &socks5_subsys, -- &shadowsocks_subsys, -+// &shadowsocks_subsys, - #if defined(ENABLE_HTTPS_PROXY) - &https_connect_subsys, - #endif -diff --git a/redudp.c b/redudp.c -index cf014c3..ab7d55b 100644 ---- a/redudp.c -+++ b/redudp.c -@@ -64,11 +64,11 @@ struct bound_udp4 { - }; - - extern udprelay_subsys socks5_udp_subsys; --extern udprelay_subsys shadowsocks_udp_subsys; -+//extern udprelay_subsys shadowsocks_udp_subsys; - static udprelay_subsys *relay_subsystems[] = - { - &socks5_udp_subsys, -- &shadowsocks_udp_subsys, -+// &shadowsocks_udp_subsys, - }; - /*********************************************************************** - * Helpers From 285e77f9edaa48fa2471ce4710d028eed4653bf7 Mon Sep 17 00:00:00 2001 From: semigodking Date: Fri, 13 Oct 2023 21:58:32 +0800 Subject: [PATCH 174/192] Create release build with CI (#190) --- .github/workflows/release.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..fc8f507a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,29 @@ +name: tagged-release + +on: + push: + tags: + - "release-*" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: install depends + run: | + sudo apt update + sudo apt install -y gnupg2 + echo "deb http://security.ubuntu.com/ubuntu bionic-security main" | sudo tee -a /etc/apt/sources.list + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32 + sudo apt update + sudo apt install -y libssl1.0-dev libevent-dev zlib1g-dev + - name: Build + run: | + mkdir release + make ENABLE_STATIC=1 + mv redsocks2 release/redsocks2-`arch`-${{github.ref_name}} + - name: Release + uses: softprops/action-gh-release@v1 + with: + files: release/* From b9af495539a03f61625acfba24cc2734761a7907 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 26 Nov 2023 16:31:38 +0800 Subject: [PATCH 175/192] Ensure SO_REUSEPORT is applied if enabled --- redsocks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redsocks.c b/redsocks.c index 204f9601..78eb1b03 100644 --- a/redsocks.c +++ b/redsocks.c @@ -1019,8 +1019,8 @@ static int redsocks_init_instance(redsocks_instance *instance) if (make_socket_transparent(fd)) log_error(LOG_WARNING, "Continue without TPROXY support"); -// if (apply_reuseport(fd)) -// log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); + if (apply_reuseport(fd)) + log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); #if defined(__APPLE__) || defined(__FreeBSD__) bindaddr_len = instance->config.bindaddr.ss_len > 0 ? instance->config.bindaddr.ss_len : sizeof(instance->config.bindaddr); From 6bd8a9c0f473d03b4edc669e1d67a112ca58aae5 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 9 Nov 2023 10:57:19 +0800 Subject: [PATCH 176/192] Use latest FreeBSD CI --- .github/workflows/freebsd_build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/freebsd_build.yml b/.github/workflows/freebsd_build.yml index 7a1e0878..26e11d1a 100644 --- a/.github/workflows/freebsd_build.yml +++ b/.github/workflows/freebsd_build.yml @@ -4,14 +4,14 @@ on: [push] jobs: build: - runs-on: macos-latest + runs-on: ubuntu-22.04 name: A job to run test FreeBSD # env: steps: - uses: actions/checkout@v2 - name: Build in FreeBSD id: test - uses: vmactions/freebsd-vm@v0.0.9 + uses: vmactions/freebsd-vm@v1 with: # envs: 'MYTOKEN MYTOKEN2' usesh: true From 11f730439803017732b8a7b11d64fe0608944181 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 26 Nov 2023 22:20:29 +0800 Subject: [PATCH 177/192] Fix: wrong error message emitted while parsing config --- redsocks.c | 2 +- redudp.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/redsocks.c b/redsocks.c index 78eb1b03..14c91a4a 100644 --- a/redsocks.c +++ b/redsocks.c @@ -242,7 +242,7 @@ static int redsocks_onexit(parser_section *section) if (!instance->relay_ss) err = "invalid `type` for redsocks"; } - else { + else if (!err) { err = "no `type` for redsocks"; } diff --git a/redudp.c b/redudp.c index 3ad0f24b..bd60c497 100644 --- a/redudp.c +++ b/redudp.c @@ -549,7 +549,7 @@ static int redudp_onexit(parser_section *section) err = "invalid dest address"; } - if (instance->config.type) { + if (!err && instance->config.type) { udprelay_subsys **ss; FOREACH(ss, relay_subsystems) { if (!strcmp((*ss)->name, instance->config.type)) { @@ -561,7 +561,7 @@ static int redudp_onexit(parser_section *section) if (!instance->relay_ss) err = "invalid `type` for redudp"; } - else { + else if (!err) { err = "no `type` for redudp"; } From 72d17efdfd2d6a22fbcbb1c4a836e18805d2e6ee Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 26 Nov 2023 22:29:01 +0800 Subject: [PATCH 178/192] Remove GoAgent part from README --- README.md | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/README.md b/README.md index 1ed32bd3..143927fb 100644 --- a/README.md +++ b/README.md @@ -173,36 +173,6 @@ List of supported encryption methods(Compiled with PolarSSL): CAMELLIA-192-CFB128 CAMELLIA-256-CFB128 -### Work with GoAgent -To make redsocks2 works with GoAgent proxy, you need to set proxy type as -'http-relay' for HTTP protocol and 'http-connect' for HTTPS protocol -respectively. -Suppose your goagent local proxy is running at the same server as redsocks2, -The configuration for forwarding connections to GoAgent is like below: - - redsocks { - bind = "192.168.1.1:1081"; //HTTP should be redirect to this port. - relay = "192.168.1.1:8080"; - type = http-relay; // Must be 'htt-relay' for HTTP traffic. - autoproxy = 1; // I want autoproxy feature enabled on this section. - // timeout is meaningful when 'autoproxy' is non-zero. - // It specified timeout value when trying to connect to destination - // directly. Default is 10 seconds. When it is set to 0, default - // timeout value will be used. - timeout = 13; - } - redsocks { - bind = "192.168.1.1:1082"; //HTTPS should be redirect to this port. - relay = "192.168.1.1:8080"; - type = http-connect; // Must be 'htt-connect' for HTTPS traffic. - autoproxy = 1; // I want autoproxy feature enabled on this section. - // timeout is meaningful when 'autoproxy' is non-zero. - // It specified timeout value when trying to connect to destination - // directly. Default is 10 seconds. When it is set to 0, default - // timeout value will be used. - timeout = 13; - } - ### Redirect UDP based DNS Request via TCP connection Sending DNS request via TCP connection is one way to prevent from DNS poisoning. You can redirect all UDP based DNS requests via TCP connection From 699babb2cc795a69be4ef91cc0c76064b3a5d2f0 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 26 Nov 2023 23:00:40 +0800 Subject: [PATCH 179/192] Emit better message for not supported type in redudp --- redudp.c | 4 +++- utils.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/redudp.c b/redudp.c index bd60c497..346126b2 100644 --- a/redudp.c +++ b/redudp.c @@ -565,7 +565,6 @@ static int redudp_onexit(parser_section *section) err = "no `type` for redudp"; } - if (instance->config.max_pktqueue == 0) { parser_error(section->context, "max_pktqueue must be greater than 0."); return -1; @@ -579,6 +578,9 @@ static int redudp_onexit(parser_section *section) return -1; } + if (err) + parser_error(section->context, "%s", err); + return err?-1:0; } diff --git a/utils.c b/utils.c index 7c78d8a3..e5475b7f 100644 --- a/utils.c +++ b/utils.c @@ -613,7 +613,9 @@ int resolve_hostname(const char *hostname, int sa_family, struct sockaddr *addr) return 0; } else { - log_errno(LOG_ERR, "Unable to resolve hostname: %s", hostname); + log_errno(LOG_INFO, "Unable to resolve hostname (%s): %s", + sa_family == AF_INET6 ? "IPv6": "IPv4", + hostname); return -1; } } From a8d9efb9d1bc0618484d958abd5352eb65126afa Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 26 Nov 2023 22:04:29 +0800 Subject: [PATCH 180/192] Redudp supports IPv6 --- redudp.c | 91 ++++++++++++++++++++++++++++------------------- shadowsocks-udp.c | 2 +- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/redudp.c b/redudp.c index 346126b2..bee36e7f 100644 --- a/redudp.c +++ b/redudp.c @@ -52,13 +52,21 @@ static char shared_buff[MAX_UDP_PACKET_SIZE];// max size of UDP packet is less t static void redudp_fini_instance(redudp_instance *instance); static int redudp_fini(); -struct bound_udp4_key { - struct in_addr sin_addr; - uint16_t sin_port; +struct bound_udp_key { + int sa_family; + union { + uint16_t sin_port; + uint16_t sin6_port; + }; + // Do not change position + union { + struct in_addr sin_addr; + struct in6_addr sin6_addr; + }; }; -struct bound_udp4 { - struct bound_udp4_key key; +struct bound_udp { + struct bound_udp_key key; int ref; int fd; time_t t_last_rx; @@ -79,29 +87,36 @@ static udprelay_subsys *relay_subsystems[] = * Helpers */ // TODO: separate binding to privileged process (this operation requires uid-0) -static void* root_bound_udp4 = NULL; // to avoid two binds to same IP:port +static void* root_bound_udp = NULL; // to avoid two binds to same IP:port -static int bound_udp4_cmp(const void *a, const void *b) +static int bound_udp_cmp(const void *a, const void *b) { - return memcmp(a, b, sizeof(struct bound_udp4_key)); + return memcmp(a, b, sizeof(struct bound_udp_key)); } -static void bound_udp4_mkkey(struct bound_udp4_key *key, const struct sockaddr_in *addr) +static void bound_udp_mkkey(struct bound_udp_key *key, const struct sockaddr *addr) { memset(key, 0, sizeof(*key)); - key->sin_addr = addr->sin_addr; - key->sin_port = addr->sin_port; + key->sa_family = addr->sa_family; + if (addr->sa_family == AF_INET) { + key->sin_addr = ((const struct sockaddr_in*)addr)->sin_addr; + key->sin_port = ((const struct sockaddr_in*)addr)->sin_port; + } + else if (addr->sa_family == AF_INET6) { + key->sin6_addr = ((const struct sockaddr_in6*)addr)->sin6_addr; + key->sin6_port = ((const struct sockaddr_in6*)addr)->sin6_port; + } } -static int bound_udp4_get(const struct sockaddr_in *addr) +static int bound_udp_get(const struct sockaddr *addr) { - struct bound_udp4_key key; - struct bound_udp4 *node, **pnode; + struct bound_udp_key key; + struct bound_udp *node, **pnode; - bound_udp4_mkkey(&key, addr); + bound_udp_mkkey(&key, addr); // I assume, that memory allocation for lookup is awful, so I use // tfind/tsearch pair instead of tsearch/check-result. - pnode = tfind(&key, &root_bound_udp4, bound_udp4_cmp); + pnode = tfind(&key, &root_bound_udp, bound_udp_cmp); if (pnode) { assert((*pnode)->ref > 0); (*pnode)->ref++; @@ -117,7 +132,7 @@ static int bound_udp4_get(const struct sockaddr_in *addr) node->key = key; node->ref = 1; - node->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + node->fd = socket(addr->sa_family, SOCK_DGRAM, IPPROTO_UDP); node->t_last_rx = redsocks_time(NULL); if (node->fd == -1) { log_errno(LOG_ERR, "socket"); @@ -142,7 +157,7 @@ static int bound_udp4_get(const struct sockaddr_in *addr) goto fail; } - pnode = tsearch(node, &root_bound_udp4, bound_udp4_cmp); + pnode = tsearch(node, &root_bound_udp, bound_udp_cmp); if (!pnode) { log_errno(LOG_ERR, "tsearch(%p) == %p", node, pnode); goto fail; @@ -160,14 +175,14 @@ static int bound_udp4_get(const struct sockaddr_in *addr) return -1; } -static void bound_udp4_put(const struct sockaddr_in *addr) +static void bound_udp_put(const struct sockaddr *addr) { - struct bound_udp4_key key; - struct bound_udp4 **pnode, *node; + struct bound_udp_key key; + struct bound_udp **pnode, *node; void *parent; - bound_udp4_mkkey(&key, addr); - pnode = tfind(&key, &root_bound_udp4, bound_udp4_cmp); + bound_udp_mkkey(&key, addr); + pnode = tfind(&key, &root_bound_udp, bound_udp_cmp); assert(pnode && (*pnode)->ref > 0); node = *pnode; @@ -176,7 +191,7 @@ static void bound_udp4_put(const struct sockaddr_in *addr) if (node->ref) return; - parent = tdelete(node, &root_bound_udp4, bound_udp4_cmp); + parent = tdelete(node, &root_bound_udp, bound_udp_cmp); assert(parent); close(node->fd); // expanding `pnode` to avoid use after free @@ -188,10 +203,10 @@ static void bound_udp4_put(const struct sockaddr_in *addr) * For each destination address, if no packet received from it for a certain period, * it is removed and the corresponding FD is closed. */ -static void bound_udp4_action(const void *nodep, const VISIT which, const int depth) +static void bound_udp_action(const void *nodep, const VISIT which, const int depth) { time_t now; - struct bound_udp4 *datap; + struct bound_udp *datap; void *parent; char buf[RED_INET_ADDRSTRLEN]; @@ -202,13 +217,13 @@ static void bound_udp4_action(const void *nodep, const VISIT which, const int de case endorder: case leaf: now = redsocks_time(NULL); - datap = *(struct bound_udp4 **) nodep; + datap = *(struct bound_udp **) nodep; // TODO: find a proper way to make timeout configurable for each instance. if (datap->t_last_rx + 20 < now) { - parent = tdelete(datap, &root_bound_udp4, bound_udp4_cmp); + parent = tdelete(datap, &root_bound_udp, bound_udp_cmp); assert(parent); - inet_ntop(AF_INET, &datap->key.sin_addr, &buf[0], sizeof(buf)); + inet_ntop(datap->key.sa_family, &datap->key.sin_addr, &buf[0], sizeof(buf)); log_error(LOG_DEBUG, "Close UDP socket %d to %s:%u", datap->fd, &buf[0], datap->key.sin_port); close(datap->fd); @@ -277,11 +292,10 @@ void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, // When working with TPROXY, we have to get sender FD from tree on // receipt of each packet from relay. - // FIXME: Support IPv6 - fd = (do_tproxy(client->instance) && srcaddr->ss_family == AF_INET) - ? bound_udp4_get((struct sockaddr_in*)srcaddr) : event_get_fd(client->instance->listener); + fd = do_tproxy(client->instance) + ? bound_udp_get((struct sockaddr*)srcaddr) : event_get_fd(client->instance->listener); if (fd == -1) { - redudp_log_error(client, LOG_WARNING, "bound_udp4_get failure"); + redudp_log_error(client, LOG_WARNING, "bound_udp_get failure"); return; } // TODO: record remote address in client @@ -593,7 +607,9 @@ static int redudp_init_instance(redudp_instance *instance) int error; int fd = -1; int bindaddr_len = 0; - char buf1[RED_INET_ADDRSTRLEN], buf2[RED_INET_ADDRSTRLEN]; + char buf1[RED_INET_ADDRSTRLEN], + buf2[RED_INET_ADDRSTRLEN], + buf3[RED_INET_ADDRSTRLEN]; instance->shared_buff = &shared_buff[0]; if (instance->relay_ss->instance_init @@ -635,9 +651,10 @@ static int redudp_init_instance(redudp_instance *instance) log_error(LOG_INFO, "redudp @ %s: TPROXY", red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1))); } else { - log_error(LOG_INFO, "redudp @ %s: destaddr=%s", + log_error(LOG_INFO, "redudp @ %s: relay=%s destaddr=%s", red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1)), - red_inet_ntop(&instance->config.destaddr, buf2, sizeof(buf2))); + red_inet_ntop(&instance->config.relayaddr, buf2, sizeof(buf2)), + red_inet_ntop(&instance->config.destaddr, buf3, sizeof(buf3))); } if (apply_reuseport(fd)) @@ -720,7 +737,7 @@ static struct event * audit_event = NULL; static void redudp_audit(int sig, short what, void *_arg) { - twalk(root_bound_udp4, bound_udp4_action); + twalk(root_bound_udp, bound_udp_action); } static int redudp_init() diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index 52b941a0..82bb5885 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -233,7 +233,7 @@ static void ss_connect_relay(redudp_client *client) int fd = -1; int error; - fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fd = socket(addr->ss_family, SOCK_DGRAM, IPPROTO_UDP); if (fd == -1) { redudp_log_errno(client, LOG_ERR, "socket"); goto fail; From 92dbff008a54540159bbb4c0ff19ccf224155d76 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sat, 27 Jan 2024 22:02:10 +0800 Subject: [PATCH 181/192] Fix: wrong port number in redudp log while closing socket --- redudp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redudp.c b/redudp.c index bee36e7f..1434c776 100644 --- a/redudp.c +++ b/redudp.c @@ -225,7 +225,7 @@ static void bound_udp_action(const void *nodep, const VISIT which, const int dep inet_ntop(datap->key.sa_family, &datap->key.sin_addr, &buf[0], sizeof(buf)); log_error(LOG_DEBUG, "Close UDP socket %d to %s:%u", datap->fd, - &buf[0], datap->key.sin_port); + &buf[0], ntohs(datap->key.sin_port)); close(datap->fd); free(datap); } From c8e1e6c4c1d623b2e540528ac9efd06dde952006 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 26 Nov 2023 22:04:29 +0800 Subject: [PATCH 182/192] Many improvements to socks5 support --- socks5-udp.c | 135 +++++++++++++++++++++++++++++++++------------------ utils.c | 11 +++++ utils.h | 1 + 3 files changed, 99 insertions(+), 48 deletions(-) diff --git a/socks5-udp.c b/socks5-udp.c index 1f0e2d27..5666e024 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -31,7 +31,10 @@ typedef struct socks5_expected_assoc_reply_t { socks5_reply h; - socks5_addr_ipv4 ip; + union { + socks5_addr_ipv4 v4; + socks5_addr_ipv6 v6; + }; } PACKED socks5_expected_assoc_reply; static struct evbuffer* socks5_mkmethods_plain_wrapper(void *p) @@ -48,10 +51,7 @@ static struct evbuffer* socks5_mkpassword_plain_wrapper(void *p) static struct evbuffer* socks5_mkassociate(void *p) { - struct sockaddr_storage sa; - memset(&sa, 0, sizeof(sa)); - sa.ss_family = ((const struct sockaddr_storage *)p)->ss_family; - return socks5_mkcommand_plain(socks5_cmd_udp_associate, &sa); + return socks5_mkcommand_plain(socks5_cmd_udp_associate, p); } static void socks5_fill_preamble( @@ -85,7 +85,7 @@ static void socks5_fill_preamble( typedef struct socks5_client_t { struct event udprelay; - struct sockaddr_in udprelayaddr; + struct sockaddr_storage udprelayaddr; struct bufferevent *relay; int ready_fwd; } socks5_client; @@ -129,9 +129,9 @@ static void socks5_forward_pkt(redudp_client *client, struct sockaddr *destaddr, struct iovec io[2]; size_t preamble_len = 0; - if (socks5client->udprelayaddr.sin_family != AF_INET && socks5client->udprelayaddr.sin_family != AF_INET6) { + if (socks5client->udprelayaddr.ss_family != AF_INET && socks5client->udprelayaddr.ss_family != AF_INET6) { redudp_log_errno(client, LOG_WARNING, "Unknown address type %d", - socks5client->udprelayaddr.sin_family); + socks5client->udprelayaddr.ss_family); return; } @@ -192,9 +192,8 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) return; } - if (pkt->header.addrtype != socks5_addrtype_ipv4) { - redudp_log_error(client, LOG_NOTICE, "Got address type #%u instead of expected #%u (IPv4).", - pkt->header.addrtype, socks5_addrtype_ipv4); + if (pkt->header.addrtype != socks5_addrtype_ipv4 && pkt->header.addrtype != socks5_addrtype_ipv6) { + redudp_log_error(client, LOG_NOTICE, "Got address type #%u.", pkt->header.addrtype); return; } @@ -222,44 +221,86 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg) } +static size_t calc_assoc_reply_size(int ss_family) { + size_t reply_size = sizeof(socks5_reply); + if (ss_family == AF_INET) + reply_size += sizeof(socks5_addr_ipv4); + else if (ss_family == AF_INET6) + reply_size += sizeof(socks5_addr_ipv6); + return reply_size; +} + static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg) { redudp_client *client = _arg; socks5_client *socks5client = (void*)(client + 1); - socks5_expected_assoc_reply reply; - int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply)); + struct evbuffer * input = bufferevent_get_input(buffev); int fd = -1; int error; - redudp_log_error(client, LOG_DEBUG, ""); + size_t max_reply_size = calc_assoc_reply_size(AF_INET6); + + // Inspect reply code + { + socks5_reply * reply = (socks5_reply *)evbuffer_pullup(input, -1); + size_t data_size = evbuffer_get_length(input); + + if (data_size < sizeof(socks5_reply)) { + // Wait for more data + bufferevent_setwatermark(buffev, EV_READ, sizeof(socks5_reply), max_reply_size); + return; + } + if (reply->ver != socks5_ver) { + redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected reply version: %u", reply->ver); + goto fail; + } + if (reply->status != socks5_status_succeeded) { + redudp_log_error(client, LOG_NOTICE, "Socks5 server status: \"%s\" (%i)", + socks5_status_to_str(reply->status), reply->status); + goto fail; + } + if (reply->addrtype == socks5_addrtype_ipv4) + max_reply_size = calc_assoc_reply_size(AF_INET); + else if (reply->addrtype == socks5_addrtype_ipv6) + max_reply_size = calc_assoc_reply_size(AF_INET6); + else { + redudp_log_error(client, LOG_NOTICE, "Socks5 server replies bad address type: %d", reply->addrtype); + goto fail; + } - if (read != sizeof(reply)) { - redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", - read, sizeof(reply)); - goto fail; + if (data_size < max_reply_size) { + // Wait for more data + bufferevent_setwatermark(buffev, EV_READ, max_reply_size, max_reply_size); + return; + } } + // Enough data received + socks5_expected_assoc_reply reply; + int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, max_reply_size); + redudp_log_error(client, LOG_DEBUG, ""); - if (reply.h.ver != socks5_ver) { - redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected reply version: %u", reply.h.ver); + if (read != max_reply_size) { + // Should never occur + redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", + read, max_reply_size); goto fail; } - if (reply.h.status != socks5_status_succeeded) { - redudp_log_error(client, LOG_NOTICE, "Socks5 server status: \"%s\" (%i)", - socks5_status_to_str(reply.h.status), reply.h.status); - goto fail; + // Use relay address instead of address in reply. + // Unless server allocates different IP for UDP association, + // this should work. + // Use port number from UDP association reply as destination + // port. + memcpy(&socks5client->udprelayaddr, + &client->instance->config.relayaddr, + sizeof(struct sockaddr_storage)); + if (reply.h.addrtype == socks5_addrtype_ipv4) { + set_sockaddr_port(&socks5client->udprelayaddr, reply.v4.port); } - - if (reply.h.addrtype != socks5_addrtype_ipv4) { - redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected address type for UDP dgram destination: %u", - reply.h.addrtype); - goto fail; + else if (reply.h.addrtype == socks5_addrtype_ipv6) { + set_sockaddr_port(&socks5client->udprelayaddr, reply.v6.port); } - socks5client->udprelayaddr.sin_family = AF_INET; - socks5client->udprelayaddr.sin_port = reply.ip.port; - socks5client->udprelayaddr.sin_addr.s_addr = reply.ip.addr; - - fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fd = socket(socks5client->udprelayaddr.ss_family, SOCK_DGRAM, IPPROTO_UDP); if (fd == -1) { redudp_log_errno(client, LOG_ERR, "socket"); goto fail; @@ -271,7 +312,6 @@ static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg) goto fail; } - error = connect(fd, (struct sockaddr*)&socks5client->udprelayaddr, sizeof(socks5client->udprelayaddr)); if (error) { redudp_log_errno(client, LOG_NOTICE, "connect"); @@ -288,7 +328,6 @@ static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg) socks5client->ready_fwd = 1; redudp_flush_queue(client); // TODO: bufferevent_disable ? - return; fail: @@ -318,9 +357,10 @@ static void socks5_read_auth_reply(struct bufferevent *buffev, void *_arg) goto fail; } + size_t reply_size = calc_assoc_reply_size(client->instance->config.relayaddr.ss_family); error = redsocks_write_helper_ex_plain( socks5client->relay, NULL, socks5_mkassociate, &client->destaddr, 0, - sizeof(socks5_expected_assoc_reply), sizeof(socks5_expected_assoc_reply)); + sizeof(socks5_reply), reply_size); if (error) goto fail; @@ -341,7 +381,7 @@ static void socks5_read_auth_methods(struct bufferevent *buffev, void *_arg) int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply)); const char *error = NULL; int ierror = 0; - redudp_log_error(client, LOG_DEBUG, ""); + redudp_log_error(client, LOG_DEBUG, "do_password: %d", do_password); if (read != sizeof(reply)) { redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu", @@ -355,12 +395,12 @@ static void socks5_read_auth_methods(struct bufferevent *buffev, void *_arg) goto fail; } else if (reply.method == socks5_auth_none) { + size_t reply_size = calc_assoc_reply_size(client->instance->config.relayaddr.ss_family); ierror = redsocks_write_helper_ex_plain( socks5client->relay, NULL, socks5_mkassociate, &client->destaddr, 0, - sizeof(socks5_expected_assoc_reply), sizeof(socks5_expected_assoc_reply)); + sizeof(socks5_reply), reply_size); if (ierror) goto fail; - replace_readcb(socks5client->relay, socks5_read_assoc_reply); } else if (reply.method == socks5_auth_password) { @@ -369,7 +409,6 @@ static void socks5_read_auth_methods(struct bufferevent *buffev, void *_arg) sizeof(socks5_auth_reply), sizeof(socks5_auth_reply)); if (ierror) goto fail; - replace_readcb(socks5client->relay, socks5_read_auth_reply); } @@ -421,13 +460,13 @@ static void socks5_connect_relay(redudp_client *client) { socks5_client *socks5client = (void*)(client + 1); socks5client->relay = red_connect_relay( - NULL, - &client->instance->config.relayaddr, - NULL, - socks5_relay_connected, - socks5_relay_error, - client, - NULL); + NULL, + &client->instance->config.relayaddr, + NULL, + socks5_relay_connected, + socks5_relay_error, + client, + NULL); if (!socks5client->relay) redudp_drop_client(client); } diff --git a/utils.c b/utils.c index e5475b7f..5ef8386b 100644 --- a/utils.c +++ b/utils.c @@ -620,4 +620,15 @@ int resolve_hostname(const char *hostname, int sa_family, struct sockaddr *addr) } } +void set_sockaddr_port(struct sockaddr_storage * addr, uint16_t port) { + if (addr->ss_family == AF_INET) { + ((struct sockaddr_in *)addr)->sin_port = port; + } + else if (addr->ss_family == AF_INET6) { + ((struct sockaddr_in6 *)addr)->sin6_port = port; + } + else { + log_error(LOG_ERR, "Unknown address type: %d", addr->ss_family); + } +} /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ diff --git a/utils.h b/utils.h index a0d144da..5362b43e 100644 --- a/utils.h +++ b/utils.h @@ -119,6 +119,7 @@ void replace_eventcb(struct bufferevent * buffev, bufferevent_event_cb eventcb); #endif char *red_inet_ntop(const struct sockaddr_storage * sa, char* buffer, size_t buffer_size); int resolve_hostname(const char *hostname, int sa_family, struct sockaddr *addr); +void set_sockaddr_port(struct sockaddr_storage * addr, uint16_t port); /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ From 82fa9502894057c0df629df419763469b3c193ab Mon Sep 17 00:00:00 2001 From: ge9 <37699433+ge9@users.noreply.github.com> Date: Sat, 29 Jun 2024 23:46:19 +0900 Subject: [PATCH 183/192] added support for FreeBSD/OpenBSD (#201) --- Makefile | 11 ++++++++++- base.c | 2 +- libc-compat.h | 2 +- redsocks.c | 2 +- redudp.c | 32 +++++++++++++++++++++++++++++++- socks5-udp.c | 4 ++++ tcpdns.c | 2 +- utils.c | 15 +++++++++++++++ 8 files changed, 64 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 70237a24..b8c1f8aa 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,9 @@ endif ifeq ($(OS), FreeBSD) override CFLAGS +=-I/usr/local/include -L/usr/local//lib endif +ifeq ($(OS), OpenBSD) +override CFLAGS +=-I/usr/local/include -L/usr/local//lib #same as FreeBSD +endif ifeq ($(OS), Darwin) override CFLAGS +=-I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib SHELL := /bin/bash @@ -52,7 +55,10 @@ override CFLAGS += -DENABLE_HTTPS_PROXY override FEATURES += ENABLE_HTTPS_PROXY $(info Compile with HTTPS proxy enabled.) endif -override LIBS += -lssl -lcrypto -ldl +override LIBS += -lssl -lcrypto +ifneq ($(OS), OpenBSD) +override LIBS += -ldl +endif override CFLAGS += -DUSE_CRYPTO_OPENSSL endif ifdef ENABLE_STATIC @@ -79,6 +85,9 @@ $(CONF): OpenBSD) \ echo "#define USE_PF" >$(CONF) \ ;; \ + NetBSD) \ + echo "#define USE_PF" >$(CONF) \ + ;; \ Darwin) \ echo -e "#define USE_PF\n#define _APPLE_" >$(CONF) \ ;; \ diff --git a/base.c b/base.c index edacfa94..ed6af4b5 100644 --- a/base.c +++ b/base.c @@ -95,7 +95,7 @@ static base_instance instance = { .log_info = false, }; -#if defined __FreeBSD__ || defined USE_PF +#if defined __FreeBSD__ || defined USE_PF || defined __OpenBSD__ || defined __NetBSD__ static int redir_open_private(const char *fname, int flags) { int fd = open(fname, flags); diff --git a/libc-compat.h b/libc-compat.h index eb4b340c..02b28390 100644 --- a/libc-compat.h +++ b/libc-compat.h @@ -42,7 +42,7 @@ # define SOL_IP IPPROTO_IP #endif -#ifdef __FreeBSD__ +#if defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ #ifndef INADDR_LOOPBACK # warning Using hardcoded value for INADDR_LOOPBACK for FreeBSD. # define INADDR_LOOPBACK 0x7F000001 diff --git a/redsocks.c b/redsocks.c index 14c91a4a..2a089d37 100644 --- a/redsocks.c +++ b/redsocks.c @@ -1022,7 +1022,7 @@ static int redsocks_init_instance(redsocks_instance *instance) if (apply_reuseport(fd)) log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); -#if defined(__APPLE__) || defined(__FreeBSD__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) bindaddr_len = instance->config.bindaddr.ss_len > 0 ? instance->config.bindaddr.ss_len : sizeof(instance->config.bindaddr); #else bindaddr_len = sizeof(instance->config.bindaddr); diff --git a/redudp.c b/redudp.c index 1434c776..6f088b31 100644 --- a/redudp.c +++ b/redudp.c @@ -1,3 +1,4 @@ + /* redsocks2 - transparent TCP/UDP-to-proxy redirector * Copyright (C) 2013-2017 Zhuofei Wang * @@ -133,6 +134,22 @@ static int bound_udp_get(const struct sockaddr *addr) node->key = key; node->ref = 1; node->fd = socket(addr->sa_family, SOCK_DGRAM, IPPROTO_UDP); +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + int on = 1; +#if defined(__FreeBSD__) +#ifdef SOL_IPV6 + if (addr->sa_family == AF_INET) { +#endif + setsockopt(node->fd, IPPROTO_IP, IP_BINDANY, &on, sizeof(on)); +#ifdef SOL_IPV6 + } else { + setsockopt(node->fd, IPPROTO_IPV6, IPV6_BINDANY, &on, sizeof(on)); + } +#endif +#else + setsockopt(node->fd, SOL_SOCKET, SO_BINDANY, &on, sizeof(on)); +#endif +#endif node->t_last_rx = redsocks_time(NULL); if (node->fd == -1) { log_errno(LOG_ERR, "socket"); @@ -300,8 +317,13 @@ void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len, } // TODO: record remote address in client + sent = sendto(fd, buf, len, 0, +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + (struct sockaddr*)&client->clientaddr, sizeof(struct sockaddr_in)); +#else (struct sockaddr*)&client->clientaddr, sizeof(client->clientaddr)); +#endif if (sent != len) { redudp_log_error( client, @@ -638,6 +660,10 @@ static int redudp_init_instance(redudp_instance *instance) log_errno(LOG_ERR, "setsockopt(listener, SOL_IP, IP_RECVORIGDSTADDR)"); goto fail; } +#if defined(__OpenBSD__) || defined(__NetBSD__) + setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); + setsockopt(fd, IPPROTO_IP, IP_RECVDSTPORT, &on, sizeof(on)); +#endif #ifdef SOL_IPV6 } else { @@ -646,6 +672,10 @@ static int redudp_init_instance(redudp_instance *instance) log_errno(LOG_ERR, "setsockopt(listener, SOL_IPV6, IPV6_RECVORIGDSTADDR)"); goto fail; } +#if defined(__OpenBSD__) || defined(__NetBSD__) + setsockopt(fd, IPPROTO_IP, IPV6_RECVPKTINFO, &on, sizeof(on)); + setsockopt(fd, IPPROTO_IP, IPV6_RECVDSTPORT, &on, sizeof(on)); +#endif } #endif log_error(LOG_INFO, "redudp @ %s: TPROXY", red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1))); @@ -660,7 +690,7 @@ static int redudp_init_instance(redudp_instance *instance) if (apply_reuseport(fd)) log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); -#if defined(__APPLE__) || defined(__FreeBSD__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) bindaddr_len = instance->config.bindaddr.ss_len > 0 ? instance->config.bindaddr.ss_len : sizeof(instance->config.bindaddr); #else bindaddr_len = sizeof(instance->config.bindaddr); diff --git a/socks5-udp.c b/socks5-udp.c index 5666e024..66544ec1 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -312,7 +312,11 @@ static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg) goto fail; } +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + error = connect(fd, (struct sockaddr*)&socks5client->udprelayaddr, sizeof(struct sockaddr_in)); +#else error = connect(fd, (struct sockaddr*)&socks5client->udprelayaddr, sizeof(socks5client->udprelayaddr)); +#endif if (error) { redudp_log_errno(client, LOG_NOTICE, "connect"); goto fail; diff --git a/tcpdns.c b/tcpdns.c index 7d15f8d9..1b2d744a 100644 --- a/tcpdns.c +++ b/tcpdns.c @@ -463,7 +463,7 @@ static int tcpdns_init_instance(tcpdns_instance *instance) if (apply_reuseport(fd)) log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled"); -#if defined(__APPLE__) || defined(__FreeBSD__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) bindaddr_len = instance->config.bindaddr.ss_len > 0 ? instance->config.bindaddr.ss_len : sizeof(instance->config.bindaddr); #else bindaddr_len = sizeof(instance->config.bindaddr); diff --git a/utils.c b/utils.c index 5ef8386b..7600b2ed 100644 --- a/utils.c +++ b/utils.c @@ -98,14 +98,29 @@ int red_recv_udp_pkt( } break; } +#if defined(__OpenBSD__) || defined(__NetBSD__) + //TODO: support IPv6 + else if (cmsg->cmsg_type == IP_RECVDSTADDR || cmsg->cmsg_type == IP_RECVDSTPORT){ + struct sockaddr* cmsgaddr = (struct sockaddr*)CMSG_DATA(cmsg); + toaddr->ss_family=AF_INET; + struct sockaddr_in *toaddr_in = (struct sockaddr_in *)toaddr; + if (cmsg->cmsg_type == IP_RECVDSTADDR) { + memcpy(&toaddr_in->sin_addr, cmsgaddr, sizeof(struct in_addr)); + } else if (cmsg->cmsg_type == IP_RECVDSTPORT) { + memcpy(&toaddr_in->sin_port, cmsgaddr, sizeof(in_port_t)); + } + } +#endif else { log_error(LOG_WARNING, "unexepcted cmsg (level,type) = (%d,%d)", cmsg->cmsg_level, cmsg->cmsg_type); } } if (toaddr->ss_family == 0) { +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) log_error(LOG_WARNING, "(SOL_IP, IP_ORIGDSTADDR) not found"); return -1; +#endif } } From 2ca40529962f78ad228a2d81f0645e9b804b7e8e Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 8 Sep 2024 01:03:54 +0800 Subject: [PATCH 184/192] Bump version to 0.70 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b8c1f8aa..e405cde4 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks2 -VERSION := 0.68 +VERSION := 0.70 OS := $(shell uname) LIBS := -levent From 3c1f0a9bec2c2f5ebf9a4d0852395e85a35d2900 Mon Sep 17 00:00:00 2001 From: CesarRoaldes <43323641+CesarRoaldes@users.noreply.github.com> Date: Mon, 4 Nov 2024 15:01:59 +0100 Subject: [PATCH 185/192] Fix: gcc-14 point type checking error (#207) --- utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.c b/utils.c index 7600b2ed..4dd3bb16 100644 --- a/utils.c +++ b/utils.c @@ -266,7 +266,7 @@ struct bufferevent* red_connect_relay_ssl(const char *ifname, if (timeout_write) bufferevent_set_timeouts(underlying, NULL, timeout_write); - error = connect(relay_fd, addr, addr_size(addr)); + error = connect(relay_fd, (struct sockaddr *)addr, addr_size(addr)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "connect"); goto fail; From f1837c869fc34b7da21142a0a34191d5c8eea88c Mon Sep 17 00:00:00 2001 From: ge9 <37699433+ge9@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:49:49 +0900 Subject: [PATCH 186/192] revert a change in DST.ADDR and DST.PORT of UDP Associate request (#210) (#211) --- socks5-udp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/socks5-udp.c b/socks5-udp.c index 66544ec1..19f1f8ef 100644 --- a/socks5-udp.c +++ b/socks5-udp.c @@ -51,7 +51,10 @@ static struct evbuffer* socks5_mkpassword_plain_wrapper(void *p) static struct evbuffer* socks5_mkassociate(void *p) { - return socks5_mkcommand_plain(socks5_cmd_udp_associate, p); + struct sockaddr_storage sa; + memset(&sa, 0, sizeof(sa)); + sa.ss_family = ((const struct sockaddr_storage *)p)->ss_family; + return socks5_mkcommand_plain(socks5_cmd_udp_associate, &sa); } static void socks5_fill_preamble( From 5d2db3e15543854da30e88af40748dfe63af2c28 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Thu, 5 Dec 2024 12:05:20 +0800 Subject: [PATCH 187/192] Bump version to 0.71 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e405cde4..ff2e5fe6 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ SRCS := $(OBJS:.o=.c) CONF := config.h DEPS := .depend OUT := redsocks2 -VERSION := 0.70 +VERSION := 0.71 OS := $(shell uname) LIBS := -levent From fca772289edc56fa54b2eb413209ca38d55a57f3 Mon Sep 17 00:00:00 2001 From: ge9 <37699433+ge9@users.noreply.github.com> Date: Fri, 12 Dec 2025 18:02:41 +0900 Subject: [PATCH 188/192] Fix IPv6 bind() error in bound_udp_get() (#213) Co-authored-by: ge9 --- redudp.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/redudp.c b/redudp.c index 6f088b31..50947416 100644 --- a/redudp.c +++ b/redudp.c @@ -164,7 +164,17 @@ static int bound_udp_get(const struct sockaddr *addr) goto fail; } - if (0 != bind(node->fd, (struct sockaddr*)addr, sizeof(*addr))) { + socklen_t bindaddr_len; +#ifdef SOL_IPV6 + if (addr->sa_family == AF_INET) { +#endif + bindaddr_len = sizeof(struct sockaddr_in); +#ifdef SOL_IPV6 + } else { + bindaddr_len = sizeof(struct sockaddr_in6); + } +#endif + if (0 != bind(node->fd, (struct sockaddr*)addr, bindaddr_len)) { log_errno(LOG_ERR, "bind"); goto fail; } From 7d1a8f3d6857d6f90472100ab4f8a88fcf353af9 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Tue, 3 Mar 2026 17:11:59 -0800 Subject: [PATCH 189/192] Support compile with latest OpenSSL and MbedTLS --- Makefile | 18 ++- encrypt.c | 354 +++++++++++++++++++++++++++++++++++++----------------- encrypt.h | 43 ++++--- 3 files changed, 288 insertions(+), 127 deletions(-) diff --git a/Makefile b/Makefile index ff2e5fe6..aa0b2ee2 100644 --- a/Makefile +++ b/Makefile @@ -40,13 +40,20 @@ endif #LDFLAGS += -fwhole-program +# Backward compatibility: support USE_CRYPTO_POLARSSL variable ifdef USE_CRYPTO_POLARSSL -override LIBS += -lpolarssl -override CFLAGS += -DUSE_CRYPTO_POLARSSL -$(info Compile with PolarSSL.) -CRYPTO := PolarSSL +override LIBS += -lmbedtls -lmbedx509 -lmbedcrypto +override CFLAGS += -DUSE_CRYPTO_MBEDTLS +$(info Compile with mbedTLS (via USE_CRYPTO_POLARSSL).) +CRYPTO := mbedTLS else -$(info Compile with OpenSSL by default. To compile with PolarSSL, run 'make USE_CRYPTO_POLARSSL=true' instead.) +ifdef USE_CRYPTO_MBEDTLS +override LIBS += -lmbedtls -lmbedx509 -lmbedcrypto +override CFLAGS += -DUSE_CRYPTO_MBEDTLS +$(info Compile with mbedTLS.) +CRYPTO := mbedTLS +else +$(info Compile with OpenSSL by default. To compile with mbedTLS, run 'make USE_CRYPTO_MBEDTLS=true' instead.) CRYPTO := OpenSSL ifdef ENABLE_HTTPS_PROXY override OBJS += https-connect.o @@ -61,6 +68,7 @@ override LIBS += -ldl endif override CFLAGS += -DUSE_CRYPTO_OPENSSL endif +endif ifdef ENABLE_STATIC override LIBS += -lz override LDFLAGS += -Wl,-static -static -static-libgcc -s diff --git a/encrypt.c b/encrypt.c index ce04c6f4..825cdd4b 100644 --- a/encrypt.c +++ b/encrypt.c @@ -24,15 +24,28 @@ #if defined(USE_CRYPTO_OPENSSL) -#include -#include +/* OpenSSL 3.0+ compatibility */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#include +#define EVP_CIPHER_CTX_new_compat() EVP_CIPHER_CTX_new() +#define EVP_CIPHER_CTX_free_compat(ctx) EVP_CIPHER_CTX_free(ctx) +#define EVP_CIPHER_CTX_init_compat(ctx) +#define EVP_CIPHER_CTX_cleanup_compat(ctx) +#else +/* OpenSSL 1.1.x and earlier */ +#define EVP_CIPHER_CTX_new_compat() EVP_CIPHER_CTX_new() +#define EVP_CIPHER_CTX_free_compat(ctx) EVP_CIPHER_CTX_free(ctx) +#define EVP_CIPHER_CTX_init_compat(ctx) EVP_CIPHER_CTX_init(ctx) +#define EVP_CIPHER_CTX_cleanup_compat(ctx) EVP_CIPHER_CTX_cleanup(ctx) +#endif -#elif defined(USE_CRYPTO_POLARSSL) +#elif defined(USE_CRYPTO_MBEDTLS) -#include -#include -#include -#include +#include +#include +#include +#include #define CIPHER_UNSUPPORTED "unsupported" #include @@ -80,12 +93,15 @@ static const char * supported_ciphers[] = "idea-cfb", "rc2-cfb", "seed-cfb", + "aes-128-gcm", + "aes-192-gcm", + "aes-256-gcm", // "salsa20", // "chacha20" }; -#ifdef USE_CRYPTO_POLARSSL -static const char * supported_ciphers_polarssl[] = +#ifdef USE_CRYPTO_MBEDTLS +static const char * supported_ciphers_mbedtls[] = { "table", "ARC4-128", @@ -102,6 +118,9 @@ static const char * supported_ciphers_polarssl[] = CIPHER_UNSUPPORTED, CIPHER_UNSUPPORTED, CIPHER_UNSUPPORTED, + "AES-128-GCM", + "AES-192-GCM", + "AES-256-GCM", // "salsa20", // "chacha20" }; @@ -125,6 +144,9 @@ static const CCAlgorithm supported_ciphers_applecc[] = kCCAlgorithmInvalid, kCCAlgorithmRC2, kCCAlgorithmInvalid, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmAES, // kCCAlgorithmInvalid, // kCCAlgorithmInvalid }; @@ -133,17 +155,23 @@ static const CCAlgorithm supported_ciphers_applecc[] = static const int supported_ciphers_iv_size[] = { - 0, 0, 16, 16, 16, 16, 8, 16, 16, 16, 8, 8, 8, 8, 16//, 8, 8 + 0, 0, 16, 16, 16, 16, 8, 16, 16, 16, 8, 8, 8, 8, 16, 12, 12, 12 }; static const int supported_ciphers_key_size[] = { - 0, 16, 16, 16, 24, 32, 16, 16, 24, 32, 16, 8, 16, 16, 16//, 32, 32 + 0, 16, 16, 16, 24, 32, 16, 16, 24, 32, 16, 8, 16, 16, 16, 16, 24, 32 }; #define CIPHER_NUM (sizeof(supported_ciphers)/sizeof(supported_ciphers[0])) -static int crypto_stream_xor_ic(uint8_t *c, const uint8_t *m, uint64_t mlen, +/* Check if the method uses GCM mode (AEAD cipher) */ +static int is_gcm_mode(int method) +{ + return (method == AES_128_GCM || method == AES_192_GCM || method == AES_256_GCM); +} + +static int __attribute__((unused)) crypto_stream_xor_ic(uint8_t *c, const uint8_t *m, uint64_t mlen, const uint8_t *n, uint64_t ic, const uint8_t *k, int method) { @@ -240,13 +268,18 @@ static void merge_sort(uint8_t array[], int length, static unsigned char *enc_md5(const unsigned char *d, size_t n, unsigned char *md) { #if defined(USE_CRYPTO_OPENSSL) - return MD5(d, n, md); -#elif defined(USE_CRYPTO_POLARSSL) static unsigned char m[16]; if (md == NULL) { md = m; } - md5(d, n, md); + MD5(d, n, md); + return md; +#elif defined(USE_CRYPTO_MBEDTLS) + static unsigned char m[16]; + if (md == NULL) { + md = m; + } + mbedtls_md5(d, n, md); return md; #endif } @@ -282,11 +315,11 @@ int cipher_iv_size(const cipher_kt_t *cipher) { #if defined(USE_CRYPTO_OPENSSL) return EVP_CIPHER_iv_length(cipher); -#elif defined(USE_CRYPTO_POLARSSL) +#elif defined(USE_CRYPTO_MBEDTLS) if (cipher == NULL) { return 0; } - return cipher->iv_size; + return mbedtls_cipher_info_get_iv_size(cipher); #endif } @@ -294,16 +327,11 @@ int cipher_key_size(const cipher_kt_t *cipher) { #if defined(USE_CRYPTO_OPENSSL) return EVP_CIPHER_key_length(cipher); -#elif defined(USE_CRYPTO_POLARSSL) +#elif defined(USE_CRYPTO_MBEDTLS) if (cipher == NULL) { return 0; } - /* Override PolarSSL 32 bit default key size with sane 128 bit default */ - if (cipher->base != NULL && POLARSSL_CIPHER_ID_BLOWFISH == - cipher->base->cipher) { - return 128 / 8; - } - return cipher->key_length / 8; + return mbedtls_cipher_info_get_key_size(cipher); #endif } @@ -314,8 +342,8 @@ int bytes_to_key(const cipher_kt_t *cipher, const digest_type_t *md, datal = strlen((const char *)pass); #if defined(USE_CRYPTO_OPENSSL) return EVP_BytesToKey(cipher, md, NULL, pass, datal, 1, key, iv); -#elif defined(USE_CRYPTO_POLARSSL) - md_context_t c; +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_context_t c; unsigned char md_buf[MAX_MD_SIZE]; int niv; int nkey; @@ -331,36 +359,37 @@ int bytes_to_key(const cipher_kt_t *cipher, const digest_type_t *md, return nkey; } - memset(&c, 0, sizeof(md_context_t)); - if (md_init_ctx(&c, md)) { + mbedtls_md_init(&c); + if (mbedtls_md_setup(&c, md, 0) != 0) { + mbedtls_md_free(&c); return 0; } addmd = 0; - mds = md_get_size(md); + mds = mbedtls_md_get_size(md); for (;; ) { int error; do { error = 1; - if (md_starts(&c)) { + if (mbedtls_md_starts(&c) != 0) { break; } if (addmd) { - if (md_update(&c, &(md_buf[0]), mds)) { + if (mbedtls_md_update(&c, &(md_buf[0]), mds) != 0) { break; } } else { addmd = 1; } - if (md_update(&c, pass, datal)) { + if (mbedtls_md_update(&c, pass, datal) != 0) { break; } - if (md_finish(&c, &(md_buf[0]))) { + if (mbedtls_md_finish(&c, &(md_buf[0])) != 0) { break; } error = 0; } while (0); if (error) { - md_free_ctx(&c); + mbedtls_md_free(&c); memset(md_buf, 0, MAX_MD_SIZE); return 0; } @@ -400,7 +429,7 @@ int bytes_to_key(const cipher_kt_t *cipher, const digest_type_t *md, break; } } - md_free_ctx(&c); + mbedtls_md_free(&c); memset(md_buf, 0, MAX_MD_SIZE); return rv; #endif @@ -410,11 +439,11 @@ int rand_bytes(uint8_t *output, int len) { #if defined(USE_CRYPTO_OPENSSL) return RAND_bytes(output, len); -#elif defined(USE_CRYPTO_POLARSSL) - static entropy_context ec = {}; - static ctr_drbg_context cd_ctx = {}; +#elif defined(USE_CRYPTO_MBEDTLS) + static mbedtls_entropy_context ec = {}; + static mbedtls_ctr_drbg_context cd_ctx = {}; static unsigned char rand_initialised = 0; - const size_t blen = min(len, CTR_DRBG_MAX_REQUEST); + const size_t blen = min(len, MBEDTLS_CTR_DRBG_MAX_REQUEST); if (!rand_initialised) { #ifdef _WIN32 @@ -451,18 +480,16 @@ int rand_bytes(uint8_t *output, int len) rand_buffer.seed = (uint64_t)clock(); } #endif - entropy_init(&ec); - if (ctr_drbg_init(&cd_ctx, entropy_func, &ec, + mbedtls_entropy_init(&ec); + if (mbedtls_ctr_drbg_seed(&cd_ctx, mbedtls_entropy_func, &ec, (const unsigned char *)rand_buffer.buffer, 8) != 0) { -#if POLARSSL_VERSION_NUMBER >= 0x01030000 - entropy_free(&ec); -#endif + mbedtls_entropy_free(&ec); //FATAL("Failed to initialize random generator"); } rand_initialised = 1; } while (len > 0) { - if (ctr_drbg_random(&cd_ctx, output, blen) != 0) { + if (mbedtls_ctr_drbg_random(&cd_ctx, output, blen) != 0) { return 0; } output += blen; @@ -490,14 +517,14 @@ const cipher_kt_t *get_cipher_type(int method) #if defined(USE_CRYPTO_OPENSSL) const char *ciphername = supported_ciphers[method]; return EVP_get_cipherbyname(ciphername); -#elif defined(USE_CRYPTO_POLARSSL) - const char *polarname = supported_ciphers_polarssl[method]; - if (strcmp(polarname, CIPHER_UNSUPPORTED) == 0) { - //LOGE("Cipher %s currently is not supported by PolarSSL library", +#elif defined(USE_CRYPTO_MBEDTLS) + const char *mbedtls_name = supported_ciphers_mbedtls[method]; + if (strcmp(mbedtls_name, CIPHER_UNSUPPORTED) == 0) { + //LOGE("Cipher %s currently is not supported by mbedTLS library", // ciphername); return NULL; } - return cipher_info_from_string(polarname); + return mbedtls_cipher_info_from_string(mbedtls_name); #endif } @@ -510,8 +537,8 @@ const digest_type_t *get_digest_type(const char *digest) #if defined(USE_CRYPTO_OPENSSL) return EVP_get_digestbyname(digest); -#elif defined(USE_CRYPTO_POLARSSL) - return md_info_from_string(digest); +#elif defined(USE_CRYPTO_MBEDTLS) + return mbedtls_md_info_from_string(digest); #endif } @@ -547,35 +574,58 @@ static int cipher_context_init(const enc_info * info, cipher_ctx_t *ctx, int enc } #endif - cipher_evp_t *evp = &ctx->evp; - const cipher_kt_t *cipher = get_cipher_type(method); #if defined(USE_CRYPTO_OPENSSL) + cipher_evp_t evp = EVP_CIPHER_CTX_new_compat(); + if (evp == NULL) { + // Cannot allocate cipher context + return -1; + } + ctx->evp = evp; + + const cipher_kt_t *cipher = get_cipher_type(method); if (cipher == NULL) { // Cipher is not found in OpenSSL library + EVP_CIPHER_CTX_free_compat(evp); + ctx->evp = NULL; return -1; } - EVP_CIPHER_CTX_init(evp); + EVP_CIPHER_CTX_init_compat(evp); if (!EVP_CipherInit_ex(evp, cipher, NULL, NULL, NULL, enc)) { - // annot initialize cipher - return -1; + // Cannot initialize cipher + EVP_CIPHER_CTX_free_compat(evp); + ctx->evp = NULL; + return -1; } if (!EVP_CIPHER_CTX_set_key_length(evp, info->key_len)) { - EVP_CIPHER_CTX_cleanup(evp); + EVP_CIPHER_CTX_cleanup_compat(evp); + EVP_CIPHER_CTX_free_compat(evp); + ctx->evp = NULL; // Invalid key length return -1; } - if (method > RC4_MD5) { + if (is_gcm_mode(method)) { + /* GCM mode - disable padding and set tag length */ + EVP_CIPHER_CTX_set_padding(evp, 0); + /* Tag length will be set after initialization */ + } else if (method > RC4_MD5) { EVP_CIPHER_CTX_set_padding(evp, 1); } -#elif defined(USE_CRYPTO_POLARSSL) +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_cipher_context_t *evp = &ctx->evp; + const cipher_kt_t *cipher = get_cipher_type(method); if (cipher == NULL) { - // Cipher is not found in PolarSSL library + // Cipher is not found in mbedTLS library return -1; } - if (cipher_init_ctx(evp, cipher) != 0) { - // Cannot initialize PolarSSL cipher context + mbedtls_cipher_init(evp); + if (mbedtls_cipher_setup(evp, cipher) != 0) { + // Cannot initialize mbedTLS cipher context return -1; } + if (is_gcm_mode(method)) { + /* GCM mode in mbedTLS - set operation mode */ + /* The tag length is handled automatically by mbedTLS */ + } #endif return 0; } @@ -642,36 +692,31 @@ static void cipher_context_set_iv(const enc_info * info, cipher_ctx_t *ctx, uint } #endif - cipher_evp_t *evp = &ctx->evp; +#if defined(USE_CRYPTO_OPENSSL) + cipher_evp_t evp = ctx->evp; if (evp == NULL) { //LOGE("cipher_context_set_iv(): Cipher context is null"); return; } -#if defined(USE_CRYPTO_OPENSSL) if (!EVP_CipherInit_ex(evp, NULL, NULL, true_key, iv, enc)) { - EVP_CIPHER_CTX_cleanup(evp); + EVP_CIPHER_CTX_cleanup_compat(evp); //FATAL("Cannot set key and IV"); } -#elif defined(USE_CRYPTO_POLARSSL) - if (cipher_setkey(evp, true_key, info->key_len * 8, enc) != 0) { - cipher_free_ctx(evp); - //FATAL("Cannot set PolarSSL cipher key"); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_cipher_context_t *evp = &ctx->evp; + if (mbedtls_cipher_setkey(evp, true_key, info->key_len * 8, + enc ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) { + mbedtls_cipher_free(evp); + //FATAL("Cannot set mbedTLS cipher key"); } -#if POLARSSL_VERSION_NUMBER >= 0x01030000 - if (cipher_set_iv(evp, iv, iv_len) != 0) { - cipher_free_ctx(evp); - //FATAL("Cannot set PolarSSL cipher IV"); + if (mbedtls_cipher_set_iv(evp, iv, iv_len) != 0) { + mbedtls_cipher_free(evp); + //FATAL("Cannot set mbedTLS cipher IV"); } - if (cipher_reset(evp) != 0) { - cipher_free_ctx(evp); - //FATAL("Cannot finalize PolarSSL cipher context"); + if (mbedtls_cipher_reset(evp) != 0) { + mbedtls_cipher_free(evp); + //FATAL("Cannot reset mbedTLS cipher context"); } -#else - if (cipher_reset(evp, iv) != 0) { - cipher_free_ctx(evp); - //FATAL("Cannot set PolarSSL cipher IV"); - } -#endif #endif #ifdef DEBUG @@ -696,11 +741,16 @@ static void cipher_context_release(enc_info * info, cipher_ctx_t *ctx) } #endif - cipher_evp_t *evp = &ctx->evp; #if defined(USE_CRYPTO_OPENSSL) - EVP_CIPHER_CTX_cleanup(evp); -#elif defined(USE_CRYPTO_POLARSSL) - cipher_free_ctx(evp); + cipher_evp_t evp = ctx->evp; + if (evp != NULL) { + EVP_CIPHER_CTX_cleanup_compat(evp); + EVP_CIPHER_CTX_free_compat(evp); + ctx->evp = NULL; + } +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_cipher_context_t *evp = &ctx->evp; + mbedtls_cipher_free(evp); #endif } @@ -716,13 +766,16 @@ static int cipher_context_update(cipher_ctx_t *ctx, uint8_t *output, int *olen, return (ret == kCCSuccess) ? 1 : 0; } #endif - cipher_evp_t *evp = &ctx->evp; #if defined(USE_CRYPTO_OPENSSL) + EVP_CIPHER_CTX *evp = ctx->evp; return EVP_CipherUpdate(evp, (uint8_t *)output, olen, (const uint8_t *)input, (size_t)ilen); -#elif defined(USE_CRYPTO_POLARSSL) - return !cipher_update(evp, (const uint8_t *)input, (size_t)ilen, - (uint8_t *)output, (size_t *)olen); +#elif defined(USE_CRYPTO_MBEDTLS) + size_t outlen = *olen; + int ret = mbedtls_cipher_update(&ctx->evp, (const uint8_t *)input, (size_t)ilen, + (uint8_t *)output, &outlen); + *olen = (int)outlen; + return (ret == 0) ? 1 : 0; #endif } @@ -732,18 +785,34 @@ size_t ss_calc_buffer_size(struct enc_ctx * ctx, size_t ilen) int method = ctx->info->method; const cipher_kt_t *cipher = get_cipher_type(method); #if defined(USE_CRYPTO_OPENSSL) - if (ctx->init) - return ilen + EVP_CIPHER_block_size(cipher); - else - return EVP_CIPHER_iv_length(cipher) + ilen + EVP_CIPHER_block_size(cipher); -#elif defined(USE_CRYPTO_POLARSSL) + if (is_gcm_mode(method)) { + /* GCM mode needs extra 16 bytes for the authentication tag */ + if (ctx->init) + return ilen + 16; + else + return EVP_CIPHER_iv_length(cipher) + ilen + 16; + } else { + if (ctx->init) + return ilen + EVP_CIPHER_block_size(cipher); + else + return EVP_CIPHER_iv_length(cipher) + ilen + EVP_CIPHER_block_size(cipher); + } +#elif defined(USE_CRYPTO_MBEDTLS) if (cipher == NULL) { return ilen; } - if (ctx->init) - return ilen + cipher_get_block_size(&ctx->evp.evp); - else - return cipher->iv_size + ilen + cipher_get_block_size(&ctx->evp.evp); + if (is_gcm_mode(method)) { + /* GCM mode needs extra 16 bytes for the authentication tag */ + if (ctx->init) + return ilen + 16; + else + return mbedtls_cipher_info_get_iv_size(cipher) + ilen + 16; + } else { + if (ctx->init) + return ilen + mbedtls_cipher_get_block_size(&ctx->evp); + else + return mbedtls_cipher_info_get_iv_size(cipher) + ilen + mbedtls_cipher_get_block_size(&ctx->evp); + } #endif } @@ -754,6 +823,7 @@ int ss_encrypt(struct enc_ctx *ctx, char *plaintext, size_t plen, int err = 1; int iv_len = 0; int p_len = plen, c_len = plen; + int tag_len = 0; if (!ctx->init) { iv_len = ctx->info->iv_len; } @@ -766,6 +836,12 @@ int ss_encrypt(struct enc_ctx *ctx, char *plaintext, size_t plen, ctx->init = 1; } + /* For GCM mode, we need to handle the authentication tag */ + if (is_gcm_mode(ctx->info->method)) { + tag_len = 16; /* GCM standard tag length */ + c_len = p_len + tag_len; /* Output includes tag */ + } + if (ctx->info->method >= SALSA20) { /* int padding = ctx->counter % SODIUM_BLOCK_SIZE; @@ -801,6 +877,27 @@ int ss_encrypt(struct enc_ctx *ctx, char *plaintext, size_t plen, if (!err) { return 0; } + + /* For GCM mode, get and append the authentication tag */ + if (is_gcm_mode(ctx->info->method)) { +#if defined(USE_CRYPTO_OPENSSL) + /* Get the tag from GCM mode */ + if (!EVP_CIPHER_CTX_ctrl(*(EVP_CIPHER_CTX **)&ctx->evp, EVP_CTRL_GCM_GET_TAG, 16, + ciphertext + iv_len + p_len)) { + // Failed to get tag + return 0; + } + c_len = p_len + 16; /* Total output = ciphertext + tag */ +#elif defined(USE_CRYPTO_MBEDTLS) + /* Get the tag from GCM mode */ + if (mbedtls_cipher_write_tag(&ctx->evp, + (unsigned char *)(ciphertext + iv_len + p_len), 16) != 0) { + // Failed to get tag + return 0; + } + c_len = p_len + 16; /* Total output = ciphertext + tag */ +#endif + } } #ifdef DEBUG @@ -830,13 +927,23 @@ int ss_decrypt(struct enc_ctx *ctx, char *ciphertext, size_t clen, int p_len = clen; int iv_len = 0; int err = 1; + int tag_len = 0; if (!ctx->init) { iv_len = ctx->info->iv_len; - p_len -= iv_len; + /* For GCM mode, we need to account for the tag in ciphertext */ + if (is_gcm_mode(ctx->info->method)) { + tag_len = 16; /* GCM standard tag length */ + } + p_len -= iv_len + tag_len; cipher_context_set_iv(ctx->info, &ctx->evp, (uint8_t *)ciphertext, iv_len, 0); ctx->counter = 0; ctx->init = 1; + } else { + /* Already initialized - need to account for tag if GCM mode */ + if (is_gcm_mode(ctx->info->method)) { + tag_len = 16; + } } if (ctx->info->method >= SALSA20) { @@ -865,11 +972,39 @@ int ss_decrypt(struct enc_ctx *ctx, char *ciphertext, size_t clen, if (padding) { memmove(plaintext, plaintext + padding, p_len); } -*/ +*/ } else { err = cipher_context_update(&ctx->evp, (uint8_t *)plaintext, &p_len, (const uint8_t *)(ciphertext + iv_len), - clen - iv_len); + clen - iv_len - tag_len); + if (!err) { + return 0; + } + + /* For GCM mode, verify the authentication tag */ + if (is_gcm_mode(ctx->info->method)) { +#if defined(USE_CRYPTO_OPENSSL) + /* Set the expected tag from ciphertext */ + if (!EVP_CIPHER_CTX_ctrl(*(EVP_CIPHER_CTX **)&ctx->evp, EVP_CTRL_GCM_SET_TAG, 16, + ciphertext + clen - tag_len)) { + // Failed to set tag for verification + return 0; + } + /* Note: In GCM mode, EVP_CipherFinal will verify the tag */ + int final_len = 0; + if (!EVP_CipherFinal_ex(*(EVP_CIPHER_CTX **)&ctx->evp, (unsigned char *)(plaintext + p_len), &final_len)) { + // Tag verification failed + return 0; + } +#elif defined(USE_CRYPTO_MBEDTLS) + /* Set the expected tag from ciphertext for verification */ + if (mbedtls_cipher_check_tag(&ctx->evp, + (const unsigned char *)(ciphertext + clen - tag_len), 16) != 0) { + // Tag verification failed + return 0; + } +#endif + } } if (!err) { @@ -910,14 +1045,15 @@ static int enc_key_init(enc_info * info, int method, const char *pass) return -1; #if defined(USE_CRYPTO_OPENSSL) + /* OpenSSL 3.0+ - algorithms are loaded automatically */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L OpenSSL_add_all_algorithms(); +#endif #endif uint8_t iv[MAX_IV_LENGTH]; - cipher_kt_t *cipher = NULL; - cipher_kt_t cipher_info; - + const cipher_kt_t *cipher = NULL; if (method == SALSA20 || method == CHACHA20) { /* @@ -942,12 +1078,14 @@ static int enc_key_init(enc_info * info, int method, const char *pass) if (cipher == NULL) { do { -#if defined(USE_CRYPTO_POLARSSL) && defined(USE_CRYPTO_APPLECC) +#if !defined(USE_CRYPTO_MBEDTLS) && defined(USE_CRYPTO_APPLECC) + /* Only apply AppleCC fallback for OpenSSL, not mbedTLS */ if (supported_ciphers_applecc[method] != kCCAlgorithmInvalid) { + mbedtls_cipher_info_t cipher_info; cipher_info.base = NULL; cipher_info.key_length = supported_ciphers_key_size[method] * 8; cipher_info.iv_size = supported_ciphers_iv_size[method]; - cipher = (cipher_kt_t *)&cipher_info; + cipher = (const cipher_kt_t *)&cipher_info; break; } #endif diff --git a/encrypt.h b/encrypt.h index 61b3fc72..634e0019 100644 --- a/encrypt.h +++ b/encrypt.h @@ -45,23 +45,31 @@ #if defined(USE_CRYPTO_OPENSSL) #include -typedef EVP_CIPHER cipher_kt_t; -typedef EVP_CIPHER_CTX cipher_evp_t; -typedef EVP_MD digest_type_t; +#include +#include +#include + +typedef const EVP_CIPHER cipher_kt_t; +typedef EVP_CIPHER_CTX *cipher_evp_t; +typedef const EVP_MD digest_type_t; #define MAX_KEY_LENGTH EVP_MAX_KEY_LENGTH #define MAX_IV_LENGTH EVP_MAX_IV_LENGTH #define MAX_MD_SIZE EVP_MAX_MD_SIZE -#elif defined(USE_CRYPTO_POLARSSL) +#elif defined(USE_CRYPTO_MBEDTLS) + +#include +#include +#include +#include +#include -#include -#include -typedef cipher_info_t cipher_kt_t; -typedef cipher_context_t cipher_evp_t; -typedef md_info_t digest_type_t; +typedef mbedtls_cipher_info_t cipher_kt_t; +typedef mbedtls_cipher_context_t cipher_evp_t; +typedef mbedtls_md_info_t digest_type_t; #define MAX_KEY_LENGTH 64 -#define MAX_IV_LENGTH POLARSSL_MAX_IV_LENGTH -#define MAX_MD_SIZE POLARSSL_MD_MAX_SIZE +#define MAX_IV_LENGTH MBEDTLS_MAX_IV_LENGTH +#define MAX_MD_SIZE MBEDTLS_MD_MAX_SIZE #endif @@ -89,7 +97,11 @@ typedef struct { #endif typedef struct { - cipher_evp_t evp; +#if defined(USE_CRYPTO_OPENSSL) + EVP_CIPHER_CTX *evp; +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_cipher_context_t evp; +#endif #ifdef USE_CRYPTO_APPLECC cipher_cc_t cc; #endif @@ -120,8 +132,11 @@ typedef struct { #define IDEA_CFB 12 #define RC2_CFB 13 #define SEED_CFB 14 -#define SALSA20 15 -#define CHACHA20 16 +#define AES_128_GCM 15 +#define AES_192_GCM 16 +#define AES_256_GCM 17 +#define SALSA20 18 +#define CHACHA20 19 #define min(a, b) (((a) < (b)) ? (a) : (b)) #define max(a, b) (((a) > (b)) ? (a) : (b)) From 25eb6902c5feac7ad57c4cabe4a7b20a055f6cbc Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 4 Mar 2026 18:10:57 +0800 Subject: [PATCH 190/192] Add new encryption methods and fix bugs --- Makefile | 10 +- README.md | 25 +- encrypt.c | 741 +++++++++++++++++++++++++------------------------- encrypt.h | 35 +-- shadowsocks.c | 2 +- 5 files changed, 396 insertions(+), 417 deletions(-) diff --git a/Makefile b/Makefile index aa0b2ee2..9116b170 100644 --- a/Makefile +++ b/Makefile @@ -40,13 +40,6 @@ endif #LDFLAGS += -fwhole-program -# Backward compatibility: support USE_CRYPTO_POLARSSL variable -ifdef USE_CRYPTO_POLARSSL -override LIBS += -lmbedtls -lmbedx509 -lmbedcrypto -override CFLAGS += -DUSE_CRYPTO_MBEDTLS -$(info Compile with mbedTLS (via USE_CRYPTO_POLARSSL).) -CRYPTO := mbedTLS -else ifdef USE_CRYPTO_MBEDTLS override LIBS += -lmbedtls -lmbedx509 -lmbedcrypto override CFLAGS += -DUSE_CRYPTO_MBEDTLS @@ -68,9 +61,8 @@ override LIBS += -ldl endif override CFLAGS += -DUSE_CRYPTO_OPENSSL endif -endif ifdef ENABLE_STATIC -override LIBS += -lz +override LIBS += $(shell nm -u $(shell $(CC) -print-file-name=libcrypto.a) 2>/dev/null | grep -q 'deflate\|inflate\|zlibVersion' && echo -lz) override LDFLAGS += -Wl,-static -static -static-libgcc -s override FEATURES += STATIC_COMPILE endif diff --git a/README.md b/README.md index 143927fb..5a8235c0 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,9 @@ HOW TO BUILD The following libraries are required. * libevent2 -* OpenSSL or PolarSSL +* OpenSSL >= 1.1.1 or mbedTLS >= 2.16 + +Note: PolarSSL is no longer supported. Use mbedTLS instead. ### Steps On general Linux, simply run command below to build with OpenSSL. @@ -38,10 +40,10 @@ On general Linux, simply run command below to build with OpenSSL. $ make ``` -To compile with PolarSSL +To compile with mbedTLS ``` -$ make USE_CRYPTO_POLARSSL=true +$ make USE_CRYPTO_MBEDTLS=true ``` To compile static binaries (with Tomatoware) @@ -56,7 +58,7 @@ compile like (Require libevent2 compiled with OpenSSL support): $ make ENABLE_HTTPS_PROXY=true ``` -To compile on newer systems with OpenSSL 1.1.1+ (just disable shadowsocks support, no patch need and worked with `ENABLE_HTTPS_PROXY`.): +To compile without shadowsocks support: ``` $ make DISABLE_SHADOWSOCKS=true ``` @@ -143,7 +145,7 @@ by field 'login'. } -List of supported encryption methods(Compiled with OpenSSL): +List of supported encryption methods(Compiled with OpenSSL >= 1.1.1): table rc4 @@ -160,8 +162,12 @@ List of supported encryption methods(Compiled with OpenSSL): idea-cfb rc2-cfb seed-cfb + aes-128-gcm + aes-192-gcm + aes-256-gcm + chacha20-ietf-poly1305 -List of supported encryption methods(Compiled with PolarSSL): +List of supported encryption methods(Compiled with mbedTLS >= 2.16): table ARC4-128 @@ -172,6 +178,13 @@ List of supported encryption methods(Compiled with PolarSSL): CAMELLIA-128-CFB128 CAMELLIA-192-CFB128 CAMELLIA-256-CFB128 + AES-128-GCM + AES-192-GCM + AES-256-GCM + CHACHA20-POLY1305 + +> Note: +> chacha20-ietf-poly1305 requires OpenSSL >= 1.1.0 or mbedTLS >= 2.16 with ChachaPoly support. ### Redirect UDP based DNS Request via TCP connection Sending DNS request via TCP connection is one way to prevent from DNS diff --git a/encrypt.c b/encrypt.c index 825cdd4b..86ff1875 100644 --- a/encrypt.c +++ b/encrypt.c @@ -58,10 +58,174 @@ #endif -// #include - #include "encrypt.h" +#define AEAD_TAG_LEN 16 +#define AEAD_NONCE_LEN 12 +#define AEAD_CHUNK_MAX 0x3FFF /* SIP004: payload length capped at 16383 */ + +/* HKDF-SHA1: RFC 5869. info = "ss-subkey" per SIP004 */ +static int hkdf_sha1(const uint8_t *key, int key_len, + const uint8_t *salt, int salt_len, + uint8_t *out, int out_len) +{ + const char *info = "ss-subkey"; + int info_len = 9; +#if defined(USE_CRYPTO_OPENSSL) + uint8_t prk[20]; + unsigned int prk_len = sizeof(prk); + if (!HMAC(EVP_sha1(), salt, salt_len, key, key_len, prk, &prk_len)) + return -1; + /* HKDF-Expand: T(i) = HMAC-SHA1(PRK, T(i-1) || info || i) */ + uint8_t t[20], buf[20 + 9 + 1]; + unsigned int t_len = 0; + int done = 0; + for (uint8_t i = 1; done < out_len; i++) { + int blen = t_len + info_len + 1; + memcpy(buf, t, t_len); + memcpy(buf + t_len, info, info_len); + buf[t_len + info_len] = i; + t_len = sizeof(t); + if (!HMAC(EVP_sha1(), prk, prk_len, buf, blen, t, &t_len)) + return -1; + int copy = min((int)t_len, out_len - done); + memcpy(out + done, t, copy); + done += copy; + } + return 0; +#elif defined(USE_CRYPTO_MBEDTLS) + uint8_t prk[20]; + const mbedtls_md_info_t *sha1 = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); + if (mbedtls_md_hmac(sha1, salt, salt_len, key, key_len, prk) != 0) + return -1; + uint8_t t[20], buf[20 + 9 + 1]; + size_t t_len = 0; + int done = 0; + for (uint8_t i = 1; done < out_len; i++) { + int blen = (int)t_len + info_len + 1; + memcpy(buf, t, t_len); + memcpy(buf + t_len, info, info_len); + buf[t_len + info_len] = i; + if (mbedtls_md_hmac(sha1, prk, sizeof(prk), buf, blen, t) != 0) + return -1; + t_len = 20; + int copy = min((int)t_len, out_len - done); + memcpy(out + done, t, copy); + done += copy; + } + return 0; +#endif +} + +/* Build 12-byte little-endian nonce from counter */ +static void make_nonce(uint8_t nonce[AEAD_NONCE_LEN], uint64_t counter) +{ + memset(nonce, 0, AEAD_NONCE_LEN); + /* little-endian */ + for (int i = 0; i < 8; i++) + nonce[i] = (counter >> (8 * i)) & 0xff; +} + +/* Single AEAD encrypt: plaintext -> ciphertext + tag. Returns 1 on success. */ +static int aead_encrypt(const uint8_t *subkey, int key_len, int is_chacha, + const uint8_t nonce[AEAD_NONCE_LEN], + const uint8_t *plain, int plen, + uint8_t *out) /* out must hold plen + AEAD_TAG_LEN */ +{ +#if defined(USE_CRYPTO_OPENSSL) + const EVP_CIPHER *cipher = is_chacha ? EVP_chacha20_poly1305() : + (key_len == 16) ? EVP_aes_128_gcm() : + (key_len == 24) ? EVP_aes_192_gcm() : + EVP_aes_256_gcm(); + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + int ok = 0, clen = 0, flen = 0; + if (!ctx) return 0; + if (!EVP_EncryptInit_ex(ctx, cipher, NULL, subkey, nonce)) goto done; + if (!EVP_EncryptUpdate(ctx, out, &clen, plain, plen)) goto done; + if (!EVP_EncryptFinal_ex(ctx, out + clen, &flen)) goto done; + int ctrl = is_chacha ? EVP_CTRL_AEAD_GET_TAG : EVP_CTRL_GCM_GET_TAG; + if (!EVP_CIPHER_CTX_ctrl(ctx, ctrl, AEAD_TAG_LEN, out + clen + flen)) goto done; + ok = 1; +done: + EVP_CIPHER_CTX_free(ctx); + return ok; +#elif defined(USE_CRYPTO_MBEDTLS) + if (is_chacha) { + mbedtls_chachapoly_context ctx; + mbedtls_chachapoly_init(&ctx); + int ok = 0; + if (mbedtls_chachapoly_setkey(&ctx, subkey) != 0) goto done_cp_enc; + if (mbedtls_chachapoly_encrypt_and_tag(&ctx, plen, nonce, + NULL, 0, plain, out, out + plen) != 0) goto done_cp_enc; + ok = 1; +done_cp_enc: + mbedtls_chachapoly_free(&ctx); + return ok; + } + mbedtls_gcm_context gcm; + mbedtls_gcm_init(&gcm); + int ok = 0; + if (mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, subkey, key_len * 8) != 0) goto done_enc; + if (mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, plen, + nonce, AEAD_NONCE_LEN, NULL, 0, + plain, out, AEAD_TAG_LEN, out + plen) != 0) goto done_enc; + ok = 1; +done_enc: + mbedtls_gcm_free(&gcm); + return ok; +#endif +} + +/* Single AEAD decrypt: ciphertext + tag -> plaintext. Returns 1 on success. */ +static int aead_decrypt(const uint8_t *subkey, int key_len, int is_chacha, + const uint8_t nonce[AEAD_NONCE_LEN], + const uint8_t *in, int clen, /* clen excludes tag */ + const uint8_t *tag, + uint8_t *out) +{ +#if defined(USE_CRYPTO_OPENSSL) + const EVP_CIPHER *cipher = is_chacha ? EVP_chacha20_poly1305() : + (key_len == 16) ? EVP_aes_128_gcm() : + (key_len == 24) ? EVP_aes_192_gcm() : + EVP_aes_256_gcm(); + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + int ok = 0, plen = 0, flen = 0; + if (!ctx) return 0; + if (!EVP_DecryptInit_ex(ctx, cipher, NULL, subkey, nonce)) goto done; + int ctrl = is_chacha ? EVP_CTRL_AEAD_SET_TAG : EVP_CTRL_GCM_SET_TAG; + if (!EVP_CIPHER_CTX_ctrl(ctx, ctrl, AEAD_TAG_LEN, (void *)tag)) goto done; + if (!EVP_DecryptUpdate(ctx, out, &plen, in, clen)) goto done; + if (!EVP_DecryptFinal_ex(ctx, out + plen, &flen)) goto done; + ok = 1; +done: + EVP_CIPHER_CTX_free(ctx); + return ok; +#elif defined(USE_CRYPTO_MBEDTLS) + if (is_chacha) { + mbedtls_chachapoly_context ctx; + mbedtls_chachapoly_init(&ctx); + int ok = 0; + if (mbedtls_chachapoly_setkey(&ctx, subkey) != 0) goto done_cp_dec; + if (mbedtls_chachapoly_auth_decrypt(&ctx, clen, nonce, + NULL, 0, tag, in, out) != 0) goto done_cp_dec; + ok = 1; +done_cp_dec: + mbedtls_chachapoly_free(&ctx); + return ok; + } + mbedtls_gcm_context gcm; + mbedtls_gcm_init(&gcm); + int ok = 0; + if (mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, subkey, key_len * 8) != 0) goto done_dec; + if (mbedtls_gcm_auth_decrypt(&gcm, clen, nonce, AEAD_NONCE_LEN, + NULL, 0, tag, AEAD_TAG_LEN, in, out) != 0) goto done_dec; + ok = 1; +done_dec: + mbedtls_gcm_free(&gcm); + return ok; +#endif +} + #define OFFSET_ROL(p, o) ((uint64_t)(*(p + o)) << (8 * o)) #ifdef DEBUG @@ -96,8 +260,7 @@ static const char * supported_ciphers[] = "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", -// "salsa20", -// "chacha20" + "chacha20-ietf-poly1305", }; #ifdef USE_CRYPTO_MBEDTLS @@ -121,70 +284,17 @@ static const char * supported_ciphers_mbedtls[] = "AES-128-GCM", "AES-192-GCM", "AES-256-GCM", -// "salsa20", -// "chacha20" -}; -#endif - -#ifdef USE_CRYPTO_APPLECC -static const CCAlgorithm supported_ciphers_applecc[] = -{ - kCCAlgorithmInvalid, - kCCAlgorithmRC4, - kCCAlgorithmRC4, - kCCAlgorithmAES, - kCCAlgorithmAES, - kCCAlgorithmAES, - kCCAlgorithmBlowfish, - kCCAlgorithmInvalid, - kCCAlgorithmInvalid, - kCCAlgorithmInvalid, - kCCAlgorithmCAST, - kCCAlgorithmDES, - kCCAlgorithmInvalid, - kCCAlgorithmRC2, - kCCAlgorithmInvalid, - kCCAlgorithmAES, - kCCAlgorithmAES, - kCCAlgorithmAES, -// kCCAlgorithmInvalid, -// kCCAlgorithmInvalid + "CHACHA20-POLY1305", }; - #endif -static const int supported_ciphers_iv_size[] = -{ - 0, 0, 16, 16, 16, 16, 8, 16, 16, 16, 8, 8, 8, 8, 16, 12, 12, 12 -}; - -static const int supported_ciphers_key_size[] = -{ - 0, 16, 16, 16, 24, 32, 16, 16, 24, 32, 16, 8, 16, 16, 16, 16, 24, 32 -}; - #define CIPHER_NUM (sizeof(supported_ciphers)/sizeof(supported_ciphers[0])) /* Check if the method uses GCM mode (AEAD cipher) */ -static int is_gcm_mode(int method) -{ - return (method == AES_128_GCM || method == AES_192_GCM || method == AES_256_GCM); -} - -static int __attribute__((unused)) crypto_stream_xor_ic(uint8_t *c, const uint8_t *m, uint64_t mlen, - const uint8_t *n, uint64_t ic, const uint8_t *k, - int method) +static int is_aead_mode(int method) { -/* - switch (method) { - case SALSA20: - return crypto_stream_salsa20_xor_ic(c, m, mlen, n, ic, k); - case CHACHA20: - return crypto_stream_chacha20_xor_ic(c, m, mlen, n, ic, k); - } -*/ - // always return 0 - return 0; + return (method == AES_128_GCM || method == AES_192_GCM || method == AES_256_GCM + || method == CHACHA20_IETF_POLY1305); } static int random_compare(const void *_x, const void *_y, uint32_t i, @@ -201,6 +311,12 @@ static void merge(uint8_t *left, int llength, uint8_t *right, uint8_t *ltmp = (uint8_t *)malloc(llength * sizeof(uint8_t)); uint8_t *rtmp = (uint8_t *)malloc(rlength * sizeof(uint8_t)); + if (!ltmp || !rtmp) { + free(ltmp); + free(rtmp); + return; + } + uint8_t *ll = ltmp; uint8_t *rr = rtmp; @@ -316,10 +432,17 @@ int cipher_iv_size(const cipher_kt_t *cipher) #if defined(USE_CRYPTO_OPENSSL) return EVP_CIPHER_iv_length(cipher); #elif defined(USE_CRYPTO_MBEDTLS) - if (cipher == NULL) { + if (cipher == NULL) + return 0; + mbedtls_cipher_context_t ctx; + mbedtls_cipher_init(&ctx); + if (mbedtls_cipher_setup(&ctx, cipher) != 0) { + mbedtls_cipher_free(&ctx); return 0; } - return mbedtls_cipher_info_get_iv_size(cipher); + int iv_size = mbedtls_cipher_get_iv_size(&ctx); + mbedtls_cipher_free(&ctx); + return iv_size; #endif } @@ -328,10 +451,17 @@ int cipher_key_size(const cipher_kt_t *cipher) #if defined(USE_CRYPTO_OPENSSL) return EVP_CIPHER_key_length(cipher); #elif defined(USE_CRYPTO_MBEDTLS) - if (cipher == NULL) { + if (cipher == NULL) + return 0; + mbedtls_cipher_context_t ctx; + mbedtls_cipher_init(&ctx); + if (mbedtls_cipher_setup(&ctx, cipher) != 0) { + mbedtls_cipher_free(&ctx); return 0; } - return mbedtls_cipher_info_get_key_size(cipher); + int key_bits = mbedtls_cipher_get_key_bitlen(&ctx); + mbedtls_cipher_free(&ctx); + return key_bits / 8; #endif } @@ -483,8 +613,9 @@ int rand_bytes(uint8_t *output, int len) mbedtls_entropy_init(&ec); if (mbedtls_ctr_drbg_seed(&cd_ctx, mbedtls_entropy_func, &ec, (const unsigned char *)rand_buffer.buffer, 8) != 0) { + mbedtls_ctr_drbg_free(&cd_ctx); mbedtls_entropy_free(&ec); - //FATAL("Failed to initialize random generator"); + return 0; } rand_initialised = 1; } @@ -509,12 +640,9 @@ const cipher_kt_t *get_cipher_type(int method) if (method == RC4_MD5) { method = RC4; } -/* - if (method >= SALSA20) { - return NULL; - } -*/ #if defined(USE_CRYPTO_OPENSSL) + if (method == CHACHA20_IETF_POLY1305) + return EVP_get_cipherbyname("chacha20-poly1305"); const char *ciphername = supported_ciphers[method]; return EVP_get_cipherbyname(ciphername); #elif defined(USE_CRYPTO_MBEDTLS) @@ -549,31 +677,6 @@ static int cipher_context_init(const enc_info * info, cipher_ctx_t *ctx, int enc // Illegal method return -1; } -/* - if (method >= SALSA20) { - enc_iv_len = supported_ciphers_iv_size[method]; - return; - } -*/ -#if defined(USE_CRYPTO_APPLECC) - cipher_cc_t *cc = &ctx->cc; - cc->cryptor = NULL; - cc->cipher = supported_ciphers_applecc[method]; - if (cc->cipher == kCCAlgorithmInvalid) { - cc->valid = kCCContextInvalid; - } else { - cc->valid = kCCContextValid; - if (cc->cipher == kCCAlgorithmRC4) { - cc->mode = kCCModeRC4; - cc->padding = ccNoPadding; - } else { - cc->mode = kCCModeCFB; - cc->padding = ccPKCS7Padding; - } - return 0; - } -#endif - #if defined(USE_CRYPTO_OPENSSL) cipher_evp_t evp = EVP_CIPHER_CTX_new_compat(); if (evp == NULL) { @@ -603,7 +706,7 @@ static int cipher_context_init(const enc_info * info, cipher_ctx_t *ctx, int enc // Invalid key length return -1; } - if (is_gcm_mode(method)) { + if (is_aead_mode(method)) { /* GCM mode - disable padding and set tag length */ EVP_CIPHER_CTX_set_padding(evp, 0); /* Tag length will be set after initialization */ @@ -619,13 +722,9 @@ static int cipher_context_init(const enc_info * info, cipher_ctx_t *ctx, int enc } mbedtls_cipher_init(evp); if (mbedtls_cipher_setup(evp, cipher) != 0) { - // Cannot initialize mbedTLS cipher context + mbedtls_cipher_free(evp); return -1; } - if (is_gcm_mode(method)) { - /* GCM mode in mbedTLS - set operation mode */ - /* The tag length is handled automatically by mbedTLS */ - } #endif return 0; } @@ -643,12 +742,6 @@ static void cipher_context_set_iv(const enc_info * info, cipher_ctx_t *ctx, uint if (enc) { rand_bytes(iv, iv_len); } -/* - if (enc_method >= SALSA20) { - memcpy(ctx->iv, iv, iv_len); - return; - } -*/ if (info->method == RC4_MD5) { unsigned char key_iv[32]; memcpy(key_iv, info->key, 16); @@ -659,39 +752,6 @@ static void cipher_context_set_iv(const enc_info * info, cipher_ctx_t *ctx, uint true_key = info->key; } -#ifdef USE_CRYPTO_APPLECC - cipher_cc_t *cc = &ctx->cc; - if (cc->valid == kCCContextValid) { - memcpy(cc->iv, iv, iv_len); - memcpy(cc->key, true_key, info->key_len); - cc->iv_len = iv_len; - cc->key_len = info->key_len; - cc->encrypt = enc ? kCCEncrypt : kCCDecrypt; - if (cc->cryptor != NULL) { - CCCryptorRelease(cc->cryptor); - cc->cryptor = NULL; - } - - CCCryptorStatus ret; - ret = CCCryptorCreateWithMode( - cc->encrypt, - cc->mode, - cc->cipher, - cc->padding, - cc->iv, cc->key, cc->key_len, - NULL, 0, 0, 0, - &cc->cryptor); - if (ret != kCCSuccess) { - if (cc->cryptor != NULL) { - CCCryptorRelease(cc->cryptor); - cc->cryptor = NULL; - } - //FATAL("Cannot set CommonCrypto key and IV"); - } - return; - } -#endif - #if defined(USE_CRYPTO_OPENSSL) cipher_evp_t evp = ctx->evp; if (evp == NULL) { @@ -726,21 +786,6 @@ static void cipher_context_set_iv(const enc_info * info, cipher_ctx_t *ctx, uint static void cipher_context_release(enc_info * info, cipher_ctx_t *ctx) { - if (info->method >= SALSA20) { - return; - } - -#ifdef USE_CRYPTO_APPLECC - cipher_cc_t *cc = &ctx->cc; - if (cc->cryptor != NULL) { - CCCryptorRelease(cc->cryptor); - cc->cryptor = NULL; - } - if (cc->valid == kCCContextValid) { - return; - } -#endif - #if defined(USE_CRYPTO_OPENSSL) cipher_evp_t evp = ctx->evp; if (evp != NULL) { @@ -757,15 +802,6 @@ static void cipher_context_release(enc_info * info, cipher_ctx_t *ctx) static int cipher_context_update(cipher_ctx_t *ctx, uint8_t *output, int *olen, const uint8_t *input, int ilen) { -#ifdef USE_CRYPTO_APPLECC - cipher_cc_t *cc = &ctx->cc; - if (cc->valid == kCCContextValid) { - CCCryptorStatus ret; - ret = CCCryptorUpdate(cc->cryptor, input, ilen, output, - ilen, (size_t *)olen); - return (ret == kCCSuccess) ? 1 : 0; - } -#endif #if defined(USE_CRYPTO_OPENSSL) EVP_CIPHER_CTX *evp = ctx->evp; return EVP_CipherUpdate(evp, (uint8_t *)output, olen, @@ -783,47 +819,101 @@ static int cipher_context_update(cipher_ctx_t *ctx, uint8_t *output, int *olen, size_t ss_calc_buffer_size(struct enc_ctx * ctx, size_t ilen) { int method = ctx->info->method; - const cipher_kt_t *cipher = get_cipher_type(method); -#if defined(USE_CRYPTO_OPENSSL) - if (is_gcm_mode(method)) { - /* GCM mode needs extra 16 bytes for the authentication tag */ - if (ctx->init) - return ilen + 16; - else - return EVP_CIPHER_iv_length(cipher) + ilen + 16; - } else { - if (ctx->init) - return ilen + EVP_CIPHER_block_size(cipher); - else - return EVP_CIPHER_iv_length(cipher) + ilen + EVP_CIPHER_block_size(cipher); + if (is_aead_mode(method)) { + /* + * SIP004 TCP format: + * first call: [salt] + N * ([2+TAG] + [payload+TAG]) + * subsequent: N * ([2+TAG] + [payload+TAG]) + * Worst case: each byte could be its own chunk. + * In practice ilen <= AEAD_CHUNK_MAX, so one chunk: + * (2 + AEAD_TAG_LEN) + (ilen + AEAD_TAG_LEN) + */ + size_t salt_len = ctx->init ? 0 : ctx->info->key_len; + size_t chunks = (ilen + AEAD_CHUNK_MAX - 1) / AEAD_CHUNK_MAX; + if (chunks == 0) chunks = 1; + return salt_len + chunks * (2 + AEAD_TAG_LEN + AEAD_CHUNK_MAX + AEAD_TAG_LEN); } +#if defined(USE_CRYPTO_OPENSSL) + const cipher_kt_t *cipher = get_cipher_type(method); + if (cipher == NULL) + return ilen; + if (ctx->init) + return ilen + EVP_CIPHER_block_size(cipher); + else + return EVP_CIPHER_iv_length(cipher) + ilen + EVP_CIPHER_block_size(cipher); #elif defined(USE_CRYPTO_MBEDTLS) - if (cipher == NULL) { + const cipher_kt_t *cipher = get_cipher_type(method); + if (cipher == NULL) return ilen; - } - if (is_gcm_mode(method)) { - /* GCM mode needs extra 16 bytes for the authentication tag */ - if (ctx->init) - return ilen + 16; - else - return mbedtls_cipher_info_get_iv_size(cipher) + ilen + 16; - } else { - if (ctx->init) - return ilen + mbedtls_cipher_get_block_size(&ctx->evp); - else - return mbedtls_cipher_info_get_iv_size(cipher) + ilen + mbedtls_cipher_get_block_size(&ctx->evp); - } + if (ctx->init) + return ilen + mbedtls_cipher_get_block_size(&ctx->evp.evp); + else + return mbedtls_cipher_get_iv_size(&ctx->evp.evp) + ilen + mbedtls_cipher_get_block_size(&ctx->evp.evp); #endif } int ss_encrypt(struct enc_ctx *ctx, char *plaintext, size_t plen, char * ciphertext, size_t * clen) { - if (ctx != NULL) { + if (ctx != NULL && ctx->info->method != TABLE) { + if (is_aead_mode(ctx->info->method)) { + /* + * SIP004 TCP AEAD encrypt: + * First call: prepend salt, derive subkey via HKDF-SHA1 + * Each chunk: [enc(2-byte-len) + TAG] [enc(payload) + TAG] + * Nonce: 12-byte little-endian counter, incremented per AE op + */ + uint8_t *out = (uint8_t *)ciphertext; + size_t out_len = 0; + int key_len = ctx->info->key_len; + int is_chacha = (ctx->info->method == CHACHA20_IETF_POLY1305); + + if (!ctx->init) { + /* Generate salt and derive subkey */ + rand_bytes(out, key_len); + if (hkdf_sha1(ctx->info->key, key_len, out, key_len, + ctx->subkey, key_len) != 0) + return 0; + out += key_len; + out_len += key_len; + ctx->counter = 0; + ctx->init = 1; + } + + const uint8_t *src = (const uint8_t *)plaintext; + size_t remaining = plen; + uint8_t nonce[AEAD_NONCE_LEN]; + + while (remaining > 0) { + uint16_t chunk = (uint16_t)min(remaining, (size_t)AEAD_CHUNK_MAX); + + /* Encrypt 2-byte length */ + uint8_t len_buf[2] = { (chunk >> 8) & 0xff, chunk & 0xff }; + make_nonce(nonce, ctx->counter++); + if (!aead_encrypt(ctx->subkey, key_len, is_chacha, nonce, + len_buf, 2, out)) + return 0; + out += 2 + AEAD_TAG_LEN; + out_len += 2 + AEAD_TAG_LEN; + + /* Encrypt payload */ + make_nonce(nonce, ctx->counter++); + if (!aead_encrypt(ctx->subkey, key_len, is_chacha, nonce, + src, chunk, out)) + return 0; + out += chunk + AEAD_TAG_LEN; + out_len += chunk + AEAD_TAG_LEN; + + src += chunk; + remaining -= chunk; + } + *clen = out_len; + return 1; + } + int err = 1; int iv_len = 0; int p_len = plen, c_len = plen; - int tag_len = 0; if (!ctx->init) { iv_len = ctx->info->iv_len; } @@ -836,75 +926,17 @@ int ss_encrypt(struct enc_ctx *ctx, char *plaintext, size_t plen, ctx->init = 1; } - /* For GCM mode, we need to handle the authentication tag */ - if (is_gcm_mode(ctx->info->method)) { - tag_len = 16; /* GCM standard tag length */ - c_len = p_len + tag_len; /* Output includes tag */ - } - - if (ctx->info->method >= SALSA20) { -/* - int padding = ctx->counter % SODIUM_BLOCK_SIZE; - if (buf_len < iv_len + padding + c_len) { - buf_len = max(iv_len + (padding + c_len) * 2, buf_size); - ciphertext = realloc(ciphertext, buf_len); - tmp_len = buf_len; - tmp_buf = ciphertext; - } - if (padding) { - plaintext = realloc(plaintext, max(p_len + padding, buf_size)); - memmove(plaintext + padding, plaintext, p_len); - memset(plaintext, 0, padding); - } - crypto_stream_xor_ic((uint8_t *)(ciphertext + iv_len), - (const uint8_t *)plaintext, - (uint64_t)(p_len + padding), - (const uint8_t *)ctx->evp.iv, - ctx->counter / SODIUM_BLOCK_SIZE, enc_key, - enc_method); - ctx->counter += p_len; - if (padding) { - memmove(ciphertext + iv_len, ciphertext + iv_len + padding, - c_len); - } -*/ - } else { - err = - cipher_context_update(&ctx->evp, - (uint8_t *)(ciphertext + iv_len), - &c_len, (const uint8_t *)plaintext, - p_len); - if (!err) { - return 0; - } - - /* For GCM mode, get and append the authentication tag */ - if (is_gcm_mode(ctx->info->method)) { -#if defined(USE_CRYPTO_OPENSSL) - /* Get the tag from GCM mode */ - if (!EVP_CIPHER_CTX_ctrl(*(EVP_CIPHER_CTX **)&ctx->evp, EVP_CTRL_GCM_GET_TAG, 16, - ciphertext + iv_len + p_len)) { - // Failed to get tag - return 0; - } - c_len = p_len + 16; /* Total output = ciphertext + tag */ -#elif defined(USE_CRYPTO_MBEDTLS) - /* Get the tag from GCM mode */ - if (mbedtls_cipher_write_tag(&ctx->evp, - (unsigned char *)(ciphertext + iv_len + p_len), 16) != 0) { - // Failed to get tag - return 0; - } - c_len = p_len + 16; /* Total output = ciphertext + tag */ -#endif - } - } + err = cipher_context_update(&ctx->evp, + (uint8_t *)(ciphertext + iv_len), + &c_len, (const uint8_t *)plaintext, + p_len); + if (!err) + return 0; #ifdef DEBUG dump("PLAIN", plaintext, p_len); dump("CIPHER", ciphertext + iv_len, c_len); #endif - *clen = iv_len + c_len; return 1; } else { @@ -923,97 +955,92 @@ int ss_encrypt(struct enc_ctx *ctx, char *plaintext, size_t plen, int ss_decrypt(struct enc_ctx *ctx, char *ciphertext, size_t clen, char *plaintext, size_t *olen) { - if (ctx != NULL) { + if (ctx != NULL && ctx->info->method != TABLE) { + if (is_aead_mode(ctx->info->method)) { + /* + * SIP004 TCP AEAD decrypt: + * First call: read salt, derive subkey via HKDF-SHA1 + * Each chunk: decrypt [enc(2-byte-len)+TAG] then [enc(payload)+TAG] + */ + const uint8_t *in = (const uint8_t *)ciphertext; + size_t in_remaining = clen; + uint8_t *out = (uint8_t *)plaintext; + size_t out_len = 0; + int key_len = ctx->info->key_len; + int is_chacha = (ctx->info->method == CHACHA20_IETF_POLY1305); + uint8_t nonce[AEAD_NONCE_LEN]; + + if (!ctx->init) { + if (in_remaining < (size_t)key_len) + return 0; + if (hkdf_sha1(ctx->info->key, key_len, in, key_len, + ctx->subkey, key_len) != 0) + return 0; + in += key_len; + in_remaining -= key_len; + ctx->counter = 0; + ctx->init = 1; + } + + while (in_remaining > 0) { + /* Need at least encrypted length field */ + if (in_remaining < 2 + AEAD_TAG_LEN) + break; + + /* Decrypt length */ + uint8_t len_plain[2]; + make_nonce(nonce, ctx->counter); + if (!aead_decrypt(ctx->subkey, key_len, is_chacha, nonce, + in, 2, in + 2, len_plain)) + return 0; + ctx->counter++; + + uint16_t chunk = ((uint16_t)len_plain[0] << 8) | len_plain[1]; + if (chunk > AEAD_CHUNK_MAX) + return 0; + + in += 2 + AEAD_TAG_LEN; + in_remaining -= 2 + AEAD_TAG_LEN; + + /* Need full encrypted payload */ + if (in_remaining < (size_t)(chunk + AEAD_TAG_LEN)) + break; + + /* Decrypt payload */ + make_nonce(nonce, ctx->counter); + if (!aead_decrypt(ctx->subkey, key_len, is_chacha, nonce, + in, chunk, in + chunk, out)) + return 0; + ctx->counter++; + + out += chunk; + out_len += chunk; + in += chunk + AEAD_TAG_LEN; + in_remaining -= chunk + AEAD_TAG_LEN; + } + *olen = out_len; + return 1; + } + int p_len = clen; int iv_len = 0; int err = 1; - int tag_len = 0; if (!ctx->init) { iv_len = ctx->info->iv_len; - /* For GCM mode, we need to account for the tag in ciphertext */ - if (is_gcm_mode(ctx->info->method)) { - tag_len = 16; /* GCM standard tag length */ - } - p_len -= iv_len + tag_len; + p_len -= iv_len; cipher_context_set_iv(ctx->info, &ctx->evp, (uint8_t *)ciphertext, iv_len, 0); ctx->counter = 0; ctx->init = 1; - } else { - /* Already initialized - need to account for tag if GCM mode */ - if (is_gcm_mode(ctx->info->method)) { - tag_len = 16; - } - } - - if (ctx->info->method >= SALSA20) { -/* - int padding = ctx->counter % SODIUM_BLOCK_SIZE; - if (buf_len < (p_len + padding) * 2) { - buf_len = max((p_len + padding) * 2, buf_size); - plaintext = realloc(plaintext, buf_len); - tmp_len = buf_len; - tmp_buf = plaintext; - } - if (padding) { - ciphertext = - realloc(ciphertext, max(c_len + padding, buf_size)); - memmove(ciphertext + iv_len + padding, ciphertext + iv_len, - c_len - iv_len); - memset(ciphertext + iv_len, 0, padding); - } - crypto_stream_xor_ic((uint8_t *)plaintext, - (const uint8_t *)(ciphertext + iv_len), - (uint64_t)(c_len - iv_len + padding), - (const uint8_t *)ctx->evp.iv, - ctx->counter / SODIUM_BLOCK_SIZE, enc_key, - enc_method); - ctx->counter += c_len - iv_len; - if (padding) { - memmove(plaintext, plaintext + padding, p_len); - } -*/ - } else { - err = cipher_context_update(&ctx->evp, (uint8_t *)plaintext, &p_len, - (const uint8_t *)(ciphertext + iv_len), - clen - iv_len - tag_len); - if (!err) { - return 0; - } - - /* For GCM mode, verify the authentication tag */ - if (is_gcm_mode(ctx->info->method)) { -#if defined(USE_CRYPTO_OPENSSL) - /* Set the expected tag from ciphertext */ - if (!EVP_CIPHER_CTX_ctrl(*(EVP_CIPHER_CTX **)&ctx->evp, EVP_CTRL_GCM_SET_TAG, 16, - ciphertext + clen - tag_len)) { - // Failed to set tag for verification - return 0; - } - /* Note: In GCM mode, EVP_CipherFinal will verify the tag */ - int final_len = 0; - if (!EVP_CipherFinal_ex(*(EVP_CIPHER_CTX **)&ctx->evp, (unsigned char *)(plaintext + p_len), &final_len)) { - // Tag verification failed - return 0; - } -#elif defined(USE_CRYPTO_MBEDTLS) - /* Set the expected tag from ciphertext for verification */ - if (mbedtls_cipher_check_tag(&ctx->evp, - (const unsigned char *)(ciphertext + clen - tag_len), 16) != 0) { - // Tag verification failed - return 0; - } -#endif - } } - if (!err) { -// free(ciphertext); + err = cipher_context_update(&ctx->evp, (uint8_t *)plaintext, &p_len, + (const uint8_t *)(ciphertext + iv_len), + clen - iv_len); + if (!err) return 0; - } *olen = p_len; - return 1; } else { char *begin = ciphertext; @@ -1031,12 +1058,15 @@ int enc_ctx_init(enc_info * info, struct enc_ctx *ctx, int enc) { memset(ctx, 0, sizeof(struct enc_ctx)); ctx->info = info; + if (is_aead_mode(info->method)) + return 0; /* GCM uses per-operation contexts, no persistent ctx needed */ return cipher_context_init(info, &ctx->evp, enc); } void enc_ctx_free(struct enc_ctx *ctx) { - cipher_context_release(ctx->info, &ctx->evp); + if (!is_aead_mode(ctx->info->method)) + cipher_context_release(ctx->info, &ctx->evp); } static int enc_key_init(enc_info * info, int method, const char *pass) @@ -1055,43 +1085,10 @@ static int enc_key_init(enc_info * info, int method, const char *pass) const cipher_kt_t *cipher = NULL; - if (method == SALSA20 || method == CHACHA20) { -/* - if (sodium_init() == -1) { - //FATAL("Failed to initialize sodium"); - } - // Fake cipher - cipher = (cipher_kt_t *)&cipher_info; -#if defined(USE_CRYPTO_OPENSSL) - cipher->key_len = supported_ciphers_key_size[method]; - cipher->iv_len = supported_ciphers_iv_size[method]; -#endif -#if defined(USE_CRYPTO_POLARSSL) - cipher->base = NULL; - cipher->key_length = supported_ciphers_key_size[method] * 8; - cipher->iv_size = supported_ciphers_iv_size[method]; -#endif -*/ - } else { - cipher = (cipher_kt_t *)get_cipher_type(method); - } + cipher = (cipher_kt_t *)get_cipher_type(method); - if (cipher == NULL) { - do { -#if !defined(USE_CRYPTO_MBEDTLS) && defined(USE_CRYPTO_APPLECC) - /* Only apply AppleCC fallback for OpenSSL, not mbedTLS */ - if (supported_ciphers_applecc[method] != kCCAlgorithmInvalid) { - mbedtls_cipher_info_t cipher_info; - cipher_info.base = NULL; - cipher_info.key_length = supported_ciphers_key_size[method] * 8; - cipher_info.iv_size = supported_ciphers_iv_size[method]; - cipher = (const cipher_kt_t *)&cipher_info; - break; - } -#endif - return -1; - } while (0); - } + if (cipher == NULL) + return -1; const digest_type_t *md = get_digest_type("MD5"); if (md == NULL) diff --git a/encrypt.h b/encrypt.h index 634e0019..4b618dd4 100644 --- a/encrypt.h +++ b/encrypt.h @@ -45,6 +45,7 @@ #if defined(USE_CRYPTO_OPENSSL) #include +#include #include #include #include @@ -59,6 +60,8 @@ typedef const EVP_MD digest_type_t; #elif defined(USE_CRYPTO_MBEDTLS) #include +#include +#include #include #include #include @@ -73,37 +76,11 @@ typedef mbedtls_md_info_t digest_type_t; #endif -#ifdef USE_CRYPTO_APPLECC - -#include - -#define kCCAlgorithmInvalid UINT32_MAX -#define kCCContextValid 0 -#define kCCContextInvalid -1 - -typedef struct { - CCCryptorRef cryptor; - int valid; - CCOperation encrypt; - CCAlgorithm cipher; - CCMode mode; - CCPadding padding; - uint8_t iv[MAX_IV_LENGTH]; - uint8_t key[MAX_KEY_LENGTH]; - size_t iv_len; - size_t key_len; -} cipher_cc_t; - -#endif - typedef struct { #if defined(USE_CRYPTO_OPENSSL) EVP_CIPHER_CTX *evp; #elif defined(USE_CRYPTO_MBEDTLS) mbedtls_cipher_context_t evp; -#endif -#ifdef USE_CRYPTO_APPLECC - cipher_cc_t cc; #endif uint8_t iv[MAX_IV_LENGTH]; } cipher_ctx_t; @@ -135,8 +112,7 @@ typedef struct { #define AES_128_GCM 15 #define AES_192_GCM 16 #define AES_256_GCM 17 -#define SALSA20 18 -#define CHACHA20 19 +#define CHACHA20_IETF_POLY1305 18 #define min(a, b) (((a) < (b)) ? (a) : (b)) #define max(a, b) (((a) > (b)) ? (a) : (b)) @@ -152,9 +128,10 @@ typedef struct enc_info_t { struct enc_ctx { uint8_t init; - uint64_t counter; + uint64_t counter; /* nonce counter for AEAD, incremented per AE op */ cipher_ctx_t evp; enc_info * info; + uint8_t subkey[MAX_KEY_LENGTH]; /* AEAD per-session subkey derived from salt */ }; int enc_init(enc_info * info, const char *pass, const char *method); diff --git a/shadowsocks.c b/shadowsocks.c index 67aa4e77..5cd97ae8 100644 --- a/shadowsocks.c +++ b/shadowsocks.c @@ -322,7 +322,7 @@ static int ss_connect_relay(redsocks_client *client) struct timeval tv; size_t len = 0; size_t header_len = 0; - char buff[64+sizeof(header)]; + char buff[128+sizeof(header)]; if (enc_ctx_init(&ss->info, &sclient->e_ctx, 1)) { log_error(LOG_ERR, "Shadowsocks failed to initialize encryption context."); From 8dc6d0078c05db68b0310a7a6dba334b09b9397e Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 4 Mar 2026 18:11:34 +0800 Subject: [PATCH 191/192] Update CI pipeline --- .github/workflows/freebsd_build.yml | 4 ++-- .github/workflows/linux_build.yml | 6 +----- .github/workflows/release.yml | 8 ++------ 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/.github/workflows/freebsd_build.yml b/.github/workflows/freebsd_build.yml index 26e11d1a..1ebae808 100644 --- a/.github/workflows/freebsd_build.yml +++ b/.github/workflows/freebsd_build.yml @@ -15,8 +15,8 @@ jobs: with: # envs: 'MYTOKEN MYTOKEN2' usesh: true - prepare: pkg install -y curl libevent gmake + prepare: pkg install -y curl libevent gmake openssl mbedtls run: | pwd freebsd-version - gmake DISABLE_SHADOWSOCKS=1 + gmake diff --git a/.github/workflows/linux_build.yml b/.github/workflows/linux_build.yml index f70a50ec..929b6cb0 100644 --- a/.github/workflows/linux_build.yml +++ b/.github/workflows/linux_build.yml @@ -10,10 +10,6 @@ jobs: - name: install depends run: | sudo apt update - sudo apt install -y gnupg2 - echo "deb http://security.ubuntu.com/ubuntu bionic-security main" | sudo tee -a /etc/apt/sources.list - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32 - sudo apt update - sudo apt install -y libssl1.0-dev libevent-dev + sudo apt install -y libevent-dev libssl-dev - name: Build run: make diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fc8f507a..c89c98da 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,15 +13,11 @@ jobs: - name: install depends run: | sudo apt update - sudo apt install -y gnupg2 - echo "deb http://security.ubuntu.com/ubuntu bionic-security main" | sudo tee -a /etc/apt/sources.list - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32 - sudo apt update - sudo apt install -y libssl1.0-dev libevent-dev zlib1g-dev + sudo apt install -y libevent-dev libmbedtls-dev zlib1g-dev - name: Build run: | mkdir release - make ENABLE_STATIC=1 + make USE_CRYPTO_MBEDTLS=1 ENABLE_STATIC=1 mv redsocks2 release/redsocks2-`arch`-${{github.ref_name}} - name: Release uses: softprops/action-gh-release@v1 From 33c37b37c0524ef01e3f2653a6c47a10f783cc39 Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Wed, 4 Mar 2026 04:17:45 -0800 Subject: [PATCH 192/192] Fix CI pipeline for FreeBSD --- .github/workflows/freebsd_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/freebsd_build.yml b/.github/workflows/freebsd_build.yml index 1ebae808..fe7c4c6a 100644 --- a/.github/workflows/freebsd_build.yml +++ b/.github/workflows/freebsd_build.yml @@ -15,7 +15,7 @@ jobs: with: # envs: 'MYTOKEN MYTOKEN2' usesh: true - prepare: pkg install -y curl libevent gmake openssl mbedtls + prepare: pkg install -y curl libevent gmake openssl run: | pwd freebsd-version