diff --git a/README.md b/README.md new file mode 100644 index 0000000..7138609 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +Srelay - the SOCKS proxy and Relay (with Randomized Balancing) +============================================================== +phoeagon + +Added *randomized* downstream balancing: when multiple rules apply, choose any +in a random way. +Added *prioritized random* downstreaming: when multiple downstreams available, +maintain a list of priorities of each downstream. Penalize a downstream if +a connection to it fails. Choose a random downstream in prioritized way. (Useful +for load-balancing multiple SOCKS5 proxy server). + +Eg: + + srelay -i 0.0.0.0:9999 \ # Listen on 9999 port + -P -R \ # Randomized, prioritized + -c config.conf \ # Choose a config file + -f # Stay forground. + +The original repo was imported from +[sourceforge](http://socks-relay.sourceforge.net/). + +What is it? +============ + +* Srelay is a socks 4/5 protocol proxy server. +* Supports socks connect/bind request in the protocol v4, v4a, and v5. +* Supports socks server chaining with both v4 and v5 servers. +* Supports Username/Password authentication in v5 (not recommended). +* Testing on FreeBSD 8.1R, Solaris 8, 10, Linux-i386, MacOS 10.5. +* Supports IPv6 as well as IPv4. +* Srelay is Free. + +Documents +========= + +Config Sample: [basic config](http://socks-relay.sourceforge.net/samples.html) + +References +========== +(These document links do not assure the compliancy of this software. Yeah, indeed.) + +* [SOCKS Protocol Version 4](http://socks-relay.sourceforge.net/socks4.protocol.txt) +* [SOCKS Protocol Version 4A socks 4a](http://socks-relay.sourceforge.net/socks4a.protocol.txt) +* SOCKS Protocol Version 5 [RFC 1928](http://www.ietf.org/rfc/rfc1928.txt) +* Username/Password Authentication for SOCKS V5 [RFC 1929](http://www.ietf.org/rfc/rfc1929.txt) diff --git a/configure b/configure index cf8c8e3..06610fb 100755 --- a/configure +++ b/configure @@ -4297,7 +4297,7 @@ cat >>conftest.$ac_ext <<_ACEOF void init_routine() { - return(0); + return; } main() { diff --git a/configure.in b/configure.in index 432d812..01ebe89 100644 --- a/configure.in +++ b/configure.in @@ -152,7 +152,7 @@ if test "$thread" != "no"; then void init_routine() { - return(0); + return; } main() { diff --git a/get-bind.c b/get-bind.c index ee1123c..8abc5f2 100644 --- a/get-bind.c +++ b/get-bind.c @@ -423,7 +423,7 @@ int get_bind_addr(bin_addr *dest, struct addrinfo *ba) close(s); /* - msg_out(norm,"nlmsg_pid: %d, nlmsg_seq: %d\n", + msg_out(norm,"nlmsg_pid: %d, nlmsg_seq: %d", h->nlmsg_pid, h->nlmsg_seq); */ len = h->nlmsg_len; @@ -437,11 +437,11 @@ int get_bind_addr(bin_addr *dest, struct addrinfo *ba) /* if (tb[RTA_DST]) { inet_ntop(AF_INET, RTA_DATA(tb[RTA_DST]), str, sizeof(str)); - msg_out(norm, "DST %s\n", str); + msg_out(norm, "DST %s", str); } if (tb[RTA_GATEWAY]) { inet_ntop(AF_INET, RTA_DATA(tb[RTA_GATEWAY]), str, sizeof(str)); - msg_out(norm, "GW %s\n", str); + msg_out(norm, "GW %s", str); } */ if (tb[RTA_OIF]) { @@ -501,7 +501,7 @@ int get_ifconf(int index, struct addrinfo *ba) close(s); /* - msg_out(norm,"nlmsg_pid: %d, nlmsg_seq: %d\n", + msg_out(norm,"nlmsg_pid: %d, nlmsg_seq: %d", h->nlmsg_pid, h->nlmsg_seq); */ while (NLMSG_OK(h, status)) { @@ -519,7 +519,7 @@ int get_ifconf(int index, struct addrinfo *ba) /* char str[128]; inet_ntop(AF_INET, RTA_DATA(tb[IFA_ADDRESS]), str, sizeof(str)); - msg_out(norm, "ADDRESS %s\n", str); + msg_out(norm, "ADDRESS %s", str); */ ba->ai_family = AF_INET; /* IPv4 */ ba->ai_socktype = SOCK_STREAM; @@ -533,7 +533,7 @@ int get_ifconf(int index, struct addrinfo *ba) /* if (tb[IFA_LOCAL]) { unsigned *d = RTA_DATA(tb[IFA_LOCAL]); - msg_out(norm, "LOCAL %08x\n", *d); + msg_out(norm, "LOCAL %08x", *d); } */ } diff --git a/main.c b/main.c index fd84591..4965dae 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,6 @@ /* main.c: - $Id$ + $Id: main.c,v 1.22 2010/12/20 14:12:00 bulkstream Exp $ Copyright (C) 2001-2010 Tomo.M (author). All rights reserved. @@ -66,6 +66,8 @@ int deny_severity = LOG_AUTH|LOG_NOTICE; extern int hosts_ctl __P((char *, char *, char *, char *)); #endif /* HAVE_LIBWRAP */ +int prioritize_downstreams = 0; /* If multiple apply, pick by priorities.*/ +int random_downstream = 0; /* If multiple rules apply, pick a random one. */ int max_child; int cur_child; @@ -99,6 +101,8 @@ void usage() "\t-a np\tauth methods n: no, p:pass\n" "\t-u file\tsrelay password file\n" "\t-f\trun into foreground\n" + "\t-P\tmaintain priority list of downstreams when multiple apply, implies -R\n" + "\t-R\tpick a random downstream if multiply fits\n" "\t-r\tresolve client name in log\n" "\t-s\tforce logging to syslog\n" "\t-t\tdisable threading\n" @@ -108,7 +112,6 @@ void usage() "\t-w\tuse tcp_wrapper access control\n" #endif /* HAVE_LIBWRAP */ "\t-I\tinetd mode\n" - "\t-q\twill be quiet\n" "\t-v\tshow version and exit\n" "\t-h\tshow this help and exit\n"); exit(1); @@ -431,7 +434,7 @@ int main(int ac, char **av) openlog(ident, LOG_PID | LOG_NDELAY, SYSLOGFAC); - while((ch = getopt(ac, av, "a:c:i:J:m:o:p:u:frstbwgIqvh?")) != -1) + while((ch = getopt(ac, av, "a:c:i:J:m:o:p:u:fRPrstbwgIvh?")) != -1) switch (ch) { case 'a': if (optarg != NULL) { @@ -465,6 +468,15 @@ int main(int ac, char **av) bind_restrict = 0; break; + case 'P': + prioritize_downstreams = 1; + random_downstream = 1; + break; + + case 'R': + random_downstream = 1; + break; + case 'c': if (optarg != NULL) { config = strdup(optarg); @@ -547,10 +559,6 @@ int main(int ac, char **av) inetd_mode = 1; break; - case 'q': - be_quiet = 1; - break; - case 'v': show_version(); exit(1); diff --git a/readconf.c b/readconf.c index be9ab00..8fedcd6 100644 --- a/readconf.c +++ b/readconf.c @@ -339,7 +339,7 @@ void add_entry(ROUTE_INFO *r, ROUTE_INFO *t, int ind) void parse_err(int sev, int line, char *msg) { - msg_out(sev, "%s: line %d: %s\n", CONFIG, line, msg); + msg_out(sev, "%s: line %d: %s", CONFIG, line, msg); } int str_to_addr(char *addr, bin_addr *dest) diff --git a/relay.c b/relay.c index 112b22b..454b9a8 100644 --- a/relay.c +++ b/relay.c @@ -1,6 +1,6 @@ /* relay.c: - $Id$ + $Id: relay.c,v 1.20 2010/11/05 02:13:12 bulkstream Exp $ Copyright (C) 2001-2010 Tomo.M (author). All rights reserved. @@ -61,6 +61,14 @@ int decode_socks_udp __P((UDP_ATTR *, u_char *)); void relay_tcp __P((SOCKS_STATE *)); void relay_udp __P((SOCKS_STATE *)); int log_transfer __P((SOCK_INFO *, LOGINFO *)); +void soft_penal(int index); +void encourage(int index, int delta); + +int mathlog( int x ){ + int r = 0; + while (x >0) { r++; x/=2; } + return r; +} void readn(rlyinfo *ri) { @@ -314,33 +322,38 @@ void relay_tcp(SOCKS_STATE *state) ri.from = state->r; ri.to = state->s; ri.flags = 0; if ((wc = forward(&ri)) <= 0) done++; - else + else { li.bc += wc; li.dnl += wc; - + } FD_CLR(state->r, &rfds); } if (FD_ISSET(state->r, &xfds)) { ri.from = state->r; ri.to = state->s; ri.flags = MSG_OOB; if ((wc = forward(&ri)) <= 0) done++; - else + else { li.bc += wc; li.dnl += wc; + } + if ( wc < 0 ) + soft_penal(state->tbl_ind); FD_CLR(state->r, &xfds); } if (FD_ISSET(state->s, &rfds)) { ri.from = state->s; ri.to = state->r; ri.flags = 0; if ((wc = forward(&ri)) <= 0) done++; - else + else { li.bc += wc; li.upl += wc; + } FD_CLR(state->s, &rfds); } if (FD_ISSET(state->s, &xfds)) { ri.from = state->s; ri.to = state->r; ri.flags = MSG_OOB; if ((wc = forward(&ri)) <= 0) done++; - else + else { li.bc += wc; li.upl += wc; + } FD_CLR(state->s, &xfds); } if (done > 0) @@ -356,6 +369,13 @@ void relay_tcp(SOCKS_STATE *state) } } } + fprintf(stderr, " proxy %d: up=%d, down=%d\n", state->tbl_ind, li.upl, li.dnl); + if (li.dnl < 10 && li.upl > 100) { + // Error: Timeout or something + soft_penal(state->tbl_ind); + } else if ( li.dnl > li.upl ) { + encourage(state->tbl_ind, (li.dnl/(li.upl+1)) * 64); + } gettimeofday(&li.end, &tz); log_transfer(state->si, &li); diff --git a/scripts/rc.srelay b/scripts/rc.srelay index 2983b9f..e3ef865 100755 --- a/scripts/rc.srelay +++ b/scripts/rc.srelay @@ -1,7 +1,7 @@ #!/bin/bash # startup script for srelay # Tomo.M -# $Id$ +# $Id: rc.srelay,v 1.1 2009/09/02 13:41:40 bulkstream Exp $ # chkconfig: 345 99 0 # description: Srelay is a SOCKS proxy. diff --git a/scripts/srelay.sh b/scripts/srelay.sh index 1f1bbac..7df377b 100755 --- a/scripts/srelay.sh +++ b/scripts/srelay.sh @@ -1,7 +1,7 @@ #!/bin/sh # startup script for srelay # srelay_enable="Yes" in /etc/rc.conf needed for enable. -# $Id$ +# $Id: srelay.sh,v 1.1 2009/09/02 13:41:40 bulkstream Exp $ # Tomo.M # PROVIDE: srelay diff --git a/scripts/svc-srelay b/scripts/svc-srelay index b693431..21d47b9 100755 --- a/scripts/svc-srelay +++ b/scripts/svc-srelay @@ -1,6 +1,6 @@ #!/bin/sh # startup script for srelay -# $Id$ +# $Id: svc-srelay,v 1.1 2009/09/02 13:41:40 bulkstream Exp $ # Tomo.M name="srelay" diff --git a/socks.c b/socks.c index 2af027e..7d7d375 100644 --- a/socks.c +++ b/socks.c @@ -1,6 +1,6 @@ /* socks.c: - $Id$ + $Id: socks.c,v 1.27 2010/11/05 02:13:12 bulkstream Exp $ Copyright (C) 2001-2010 Tomo.M (author). All rights reserved. @@ -34,6 +34,13 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "srelay.h" +extern int prioritize_downstreams; +extern int random_downstream; + +static int * tbl_priority = NULL; +#define DEFAULT_PRIORITY (512) +#define MAX_PRIORITY (1024) + #define TIMEOUTSEC 30 #define GEN_ERR_REP(s, v) \ @@ -118,6 +125,38 @@ int lookup_tbl __P((SOCKS_STATE *)); int resolv_host __P((bin_addr *, u_int16_t, struct host_info *)); int log_request __P((SOCKS_STATE *)); +void print_priority() { + int y; + for (y = 0; y < proxy_tbl_ind; ++y) + fprintf(stderr, "%d ", tbl_priority[y]); + fprintf(stderr, "\n"); +} +void soft_penal(int index) { + if (!prioritize_downstreams) return; + tbl_priority[index] *= 0.8; + if (tbl_priority[index] == 0) + tbl_priority[index] = 1; + fprintf(stderr, "Soft Penal #%d out of %d\n", index, proxy_tbl_ind); + print_priority(); +} +void hard_penal(int index) { + if (!prioritize_downstreams) return; + // half priority + tbl_priority[index] /= 2; + if (tbl_priority[index] == 0) + tbl_priority[index] = 1; + fprintf(stderr, "Hard Penal #%d out of %d\n", index, proxy_tbl_ind); + print_priority(); +} +void encourage(int index, int delta) { + if (!prioritize_downstreams) return; + // increase priority + tbl_priority[index] += delta; + if (tbl_priority[index] > MAX_PRIORITY) + tbl_priority[index] = MAX_PRIORITY; + fprintf(stderr, "Encourage #%d out of %d, delta=%d\n", index, proxy_tbl_ind, delta); + print_priority(); +} /* proto_socks: @@ -190,9 +229,11 @@ int proto_socks(SOCKS_STATE *state) state->si->prs.len = len; } + encourage(state->tbl_ind, 1); return(0); /* 0: OK */ } /* error */ + hard_penal(state->tbl_ind); if (state->r >= 0) { close(state->r); } @@ -783,6 +824,9 @@ int socks_proxy_reply(int v, SOCKS_STATE *state) switch (v) { /* server socks version */ case 4: /* server v:4 */ + // Test if success + if ( buf[1] != S4AGRANTED ) + soft_penal(state->tbl_ind); if ( r < 8 ) { /* from v4 spec, r should be 8 */ /* cannot read server reply */ r = -1; @@ -816,6 +860,9 @@ int socks_proxy_reply(int v, SOCKS_STATE *state) r = -1; break; } + // Test if success + if ( buf[1] != S5AGRANTED ) + soft_penal(state->tbl_ind); switch (state->sr.ver) { /* client ver */ case 4: /* translate reply v5->v4 */ @@ -1473,8 +1520,26 @@ int lookup_tbl(SOCKS_STATE *state) struct sockaddr_in *sa; struct sockaddr_in6 *sa6; + #define MAX_CANDIDATE (32) + int candidates[MAX_CANDIDATE]; + #define ADD(i) do { \ + if (match < MAX_CANDIDATE) \ + candidates[ match ++ ] = i;\ + }while(0) + if (prioritize_downstreams && !tbl_priority) { + // init priority list + tbl_priority = malloc((1 + proxy_tbl_ind) * sizeof(int)); + // Leave proxy_tbl_ind to be an accessible index so as to avoid boundary check + int y; + for (y = 0; y < proxy_tbl_ind; ++y) + tbl_priority[y] = DEFAULT_PRIORITY; + } + match = 0; for (i=0; i < proxy_tbl_ind; i++) { + /* check atype */ + if ( state->sr.dest.atype != proxy_tbl[i].dest.atype ) + continue; /* check IP PROTO */ if ( (state->sr.req == S5REQ_UDPA && proxy_tbl[i].proto == TCP) || (state->sr.req != S5REQ_UDPA && proxy_tbl[i].proto == UDP)) @@ -1486,8 +1551,9 @@ int lookup_tbl(SOCKS_STATE *state) if (addr_comp(&(state->sr.dest), &(proxy_tbl[i].dest), proxy_tbl[i].mask) == 0) { - match++; - break; + ADD(i); + if (!random_downstream) + break; } } @@ -1535,10 +1601,11 @@ int lookup_tbl(SOCKS_STATE *state) continue; if (addr_comp(&addr, &(proxy_tbl[i].dest), proxy_tbl[i].mask) == 0) - match++; - break; + ADD(i); + if (!random_downstream) + break; } - if ( match ) + if ( match && !random_downstream) break; } freeaddrinfo(res0); @@ -1548,6 +1615,33 @@ int lookup_tbl(SOCKS_STATE *state) memset(&(state->rtbl), 0, sizeof(ROUTE_INFO)); if (match) { + if ( random_downstream && match > 1 ) { + if (!prioritize_downstreams) { + i = candidates[rand() % match]; + } else { + int priority_vector[MAX_CANDIDATE]; + int y; + for (y = 0 ; y < match; ++y) + priority_vector[y] = tbl_priority[candidates[y]]; + for (y = 1 ; y < match; ++y) + priority_vector[y] += priority_vector[y - 1]; + int pivot = rand() % priority_vector[match - 1]; + // binary search the point + int l = 0, r = match-1; + i = 0; + while (l<=r) { + int m = (l + r) / 2; + if ( priority_vector[m] >= pivot ) { + r = m - 1; + i = m; + } else { + l = m + 1; + } + } + i = candidates[i]; + // Now i is the selected server + } + } memcpy(&(state->rtbl), &(proxy_tbl[i]), sizeof(ROUTE_INFO)); state->tbl_ind = i; } else diff --git a/srelay.h b/srelay.h index ae19b30..31111fa 100644 --- a/srelay.h +++ b/srelay.h @@ -1,6 +1,6 @@ /* srelay.h: - $Id$ + $Id: srelay.h,v 1.30 2010/12/20 14:12:00 bulkstream Exp $ common definitions. Copyright (C) 2001-2010 Tomo.M (author). @@ -103,7 +103,7 @@ typedef u_int32_t socklen_t; # endif #endif -#define version "srelay 0.4.8b6 2013/03/11 (Tomo.M)" +#define version "srelay 0.4.8b5 2010/12/20 (Tomo.M)" #ifndef SYSCONFDIR # define SYSCONFDIR "/usr/local/etc" @@ -416,7 +416,6 @@ extern u_long idle_timeout; /* from util.c */ extern int forcesyslog; -extern int be_quiet; /* from socks.c */ diff --git a/util.c b/util.c index cc86532..a76f2ec 100644 --- a/util.c +++ b/util.c @@ -66,6 +66,9 @@ void msg_out(int severity, const char *fmt, ...) va_start(ap, fmt); if (fg && !forcesyslog && isatty(fileno(stderr))) { vfprintf(stderr, fmt, ap); + /* syslog adds a newline if one is not present, do the same here */ + if (fmt[strlen(fmt) - 1] != '\n') + putc('\n', stderr); } else { vsyslog(priority, fmt, ap); }