diff --git a/microsocks/Makefile b/microsocks/Makefile index 5d80729..096c9c4 100644 --- a/microsocks/Makefile +++ b/microsocks/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=microsocks PKG_VERSION:=1.0.5 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/rofl0r/microsocks/tar.gz/v$(PKG_VERSION)? @@ -25,7 +25,7 @@ define Package/microsocks SECTION:=net CATEGORY:=Network SUBMENU:=Web Servers/Proxies - TITLE:=Tiny, portable SOCKS5 server + TITLE:=Tiny, portable SOCKS5 server. Support forwarding rules URL:=https://github.com/rofl0r/microsocks DEPENDS:=+libpthread endef @@ -33,6 +33,7 @@ endef define Package/microsocks/description A SOCKS5 service that you can run on your remote boxes to tunnel connections through them, if for some reason SSH doesn't cut it for you. + This version supports forwarding rules. endef define Package/microsocks/install diff --git a/microsocks/patches/100-Add-SOCKS5-forwarding-rules-support.patch b/microsocks/patches/100-Add-SOCKS5-forwarding-rules-support.patch new file mode 100644 index 0000000..2b1309e --- /dev/null +++ b/microsocks/patches/100-Add-SOCKS5-forwarding-rules-support.patch @@ -0,0 +1,421 @@ +--- a/sockssrv.c ++++ b/sockssrv.c +@@ -33,8 +33,10 @@ + #include + #include + #include ++#include + #include "server.h" + #include "sblist.h" ++#define MICROSOCKS_VERSION "1.0.5-forward" + + /* timeout in microseconds on resource exhaustion to prevent excessive + cpu usage. */ +@@ -71,6 +73,7 @@ + static pthread_rwlock_t auth_ips_lock = PTHREAD_RWLOCK_INITIALIZER; + static const struct server* server; + static union sockaddr_union bind_addr = {.v4.sin_family = AF_UNSPEC}; ++static sblist *fwd_rules; + + enum socksstate { + SS_1_CONNECTED, +@@ -97,6 +100,17 @@ + EC_ADDRESSTYPE_NOT_SUPPORTED = 8, + }; + ++struct fwd_rule { ++ char *match_name; ++ short match_port; ++ char *auth_buf; /* Username/Password request buffer (RFC-1929) */ ++ size_t auth_len; ++ char *upstream_name; ++ short upstream_port; ++ char *req_buf; /* Client Connection Request buffer to send to upstream */ ++ size_t req_len; ++}; ++ + struct thread { + pthread_t pt; + struct client client; +@@ -116,6 +130,109 @@ + static void dolog(const char* fmt, ...) { } + #endif + ++static int upstream_handshake(const struct fwd_rule* rule, unsigned char *client_buf, size_t client_buf_len, ++ int client_fd, int upstream_fd, unsigned short client_port) { ++ unsigned char sbuf[512]; ++ ssize_t r; ++ ++ if(rule->auth_buf) { ++ unsigned char handshake[4] = {5, 2, 0, 2}; ++ if (write(upstream_fd, handshake, 4) != 4) { ++ close(upstream_fd); ++ return -1; ++ } ++ } else { ++ unsigned char handshake[3] = {5, 1, 0}; ++ if (write(upstream_fd, handshake, 3) != 3) { ++ close(upstream_fd); ++ return -1; ++ } ++ } ++ ++ if (read(upstream_fd, sbuf, 2) != 2 || sbuf[0] != 5) { ++ close(upstream_fd); ++ return -1; ++ } ++ ++ if (sbuf[1] == 2) { ++ if (!rule->auth_buf) { ++ close(upstream_fd); ++ return -1; ++ } ++ if (write(upstream_fd, rule->auth_buf, rule->auth_len) != (ssize_t)rule->auth_len) { ++ close(upstream_fd); ++ return -1; ++ } ++ if (read(upstream_fd, sbuf, 2) != 2 || sbuf[0] != 1 || sbuf[1] != 0) { ++ close(upstream_fd); ++ return -1; ++ } ++ } else if (sbuf[1] != 0) { ++ close(upstream_fd); ++ return -1; ++ } ++ ++ if (write(upstream_fd, client_buf, client_buf_len) != (ssize_t)client_buf_len) { ++ close(upstream_fd); ++ return -1; ++ } ++ ++ size_t total = 0; ++ size_t need = 4; ++ ++ while (total < need) { ++ r = read(upstream_fd, sbuf + total, need - total); ++ if (r <= 0) { ++ close(upstream_fd); ++ return -1; ++ } ++ total += r; ++ } ++ ++ if (sbuf[1] != 0) { ++ close(upstream_fd); ++ return -sbuf[1]; ++ } ++ ++ size_t need_more = 0; ++ switch (sbuf[3]) { ++ case 1: ++ need_more = 4 + 2; ++ break; ++ case 4: ++ need_more = 16 + 2; ++ break; ++ case 3: ++ r = read(upstream_fd, sbuf + total, 1); ++ if (r != 1) { ++ close(upstream_fd); ++ return -1; ++ } ++ total += r; ++ need_more = sbuf[4] + 2; ++ break; ++ default: ++ close(upstream_fd); ++ return -EC_ADDRESSTYPE_NOT_SUPPORTED; ++ } ++ ++ while (total < need + need_more) { ++ r = read(upstream_fd, sbuf + total, (need + need_more) - total); ++ if (r <= 0) { ++ close(upstream_fd); ++ return -1; ++ } ++ total += r; ++ } ++ ++ if (write(client_fd, sbuf, total) != (ssize_t)total) { ++ close(upstream_fd); ++ return -1; ++ } ++ ++ return upstream_fd; ++} ++ + static struct addrinfo* addr_choose(struct addrinfo* list, union sockaddr_union* bindaddr) { + int af = SOCKADDR_UNION_AF(bindaddr); + if(af == AF_UNSPEC) return list; +@@ -125,7 +242,9 @@ + return list; + } + +-static int connect_socks_target(unsigned char *buf, size_t n, struct client *client) { ++static int connect_socks_target(unsigned char *buf, size_t n, struct client *client, int *used_rule) { ++ *used_rule = 0; ++ + if(n < 5) return -EC_GENERAL_FAILURE; + if(buf[0] != 5) return -EC_GENERAL_FAILURE; + if(buf[1] != 1) return -EC_COMMAND_NOT_SUPPORTED; /* we support only CONNECT method */ +@@ -158,6 +277,29 @@ + } + unsigned short port; + port = (buf[minlen-2] << 8) | buf[minlen-1]; ++ ++ size_t i; ++ struct fwd_rule *rule = NULL; ++ char original_name[256]; ++ unsigned short original_port = port; ++ strncpy(original_name, namebuf, sizeof(original_name) - 1); ++ original_name[sizeof(original_name) - 1] = '\0'; ++ if(fwd_rules) { ++ for(i=0;imatch_name[0]=='\0' || strcmp(r->match_name, namebuf) == 0); ++ int port_match = (r->match_port == 0 || r->match_port == port); ++ if(name_match && port_match) { ++ rule = r; ++ *used_rule = 1; ++ strncpy(namebuf, r->upstream_name, sizeof(namebuf)-1); ++ namebuf[sizeof(namebuf)-1] = '\0'; ++ port = r->upstream_port; ++ break; ++ } ++ } ++ } ++ + /* there's no suitable errorcode in rfc1928 for dns lookup failure */ + if(resolve(namebuf, port, &remote)) return -EC_GENERAL_FAILURE; + struct addrinfo* raddr = addr_choose(remote, &bind_addr); +@@ -186,6 +328,11 @@ + return -EC_GENERAL_FAILURE; + } + } ++ ++ struct timeval tv = {5, 0}; ++ setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); ++ setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)); ++ + if(SOCKADDR_UNION_AF(&bind_addr) == raddr->ai_family && + bindtoip(fd, &bind_addr) == -1) + goto eval_errno; +@@ -198,9 +345,22 @@ + af = SOCKADDR_UNION_AF(&client->addr); + void *ipdata = SOCKADDR_UNION_ADDRESS(&client->addr); + inet_ntop(af, ipdata, clientname, sizeof clientname); +- dolog("client[%d] %s: connected to %s:%d\n", client->fd, clientname, namebuf, port); ++ if (rule) { ++ dolog("client[%d] %s: %s:%d -> via %s:%d\n", client->fd, clientname, original_name, original_port, rule->upstream_name, rule->upstream_port); ++ } else { ++ dolog("client[%d] %s: connected to %s:%d\n", client->fd, clientname, namebuf, port); ++ } + } +- return fd; ++ ++ if (rule) { ++ int result = upstream_handshake(rule, buf, n, client->fd, fd, original_port); ++ if (result < 0) { ++ close(fd); ++ return result; ++ } ++ return result; ++ } ++ return fd; + } + + static int is_authed(union sockaddr_union *client, union sockaddr_union *authedip) { +@@ -345,12 +505,15 @@ + } + break; + case SS_3_AUTHED: +- ret = connect_socks_target(buf, n, &t->client); ++ int used_rule = 0; ++ ret = connect_socks_target(buf, n, &t->client, &used_rule); + if(ret < 0) { + send_error(t->client.fd, ret*-1); + return -1; + } +- send_error(t->client.fd, EC_SUCCESS); ++ if (!used_rule) { ++ send_error(t->client.fd, EC_SUCCESS); ++ } + return ret; + } + } +@@ -382,11 +545,131 @@ + } + } + ++static short host_get_port(char *name) { ++ int p,n; ++ char *c; ++ if((c = strrchr(name, ':')) && sscanf(c+1,"%d%n",&p, &n)==1 && n == (int)(strlen(c + 1)) && p >= 0 && p < USHRT_MAX) ++ return (*c='\0'),(short)p; ++ else ++ return -1; ++} ++ ++static int fwd_rules_add(char *str) { ++ char *match = NULL, *upstream = NULL, *remote = NULL; ++ unsigned short match_port, upstream_port, remote_port; ++ int ncred; ++ ++ if(sscanf(str, "%m[^,],%n%m[^,],%ms\n", &match, &ncred, &upstream, &remote) != 3) ++ return 1; ++ ++ match_port = host_get_port(match); ++ upstream_port = host_get_port(upstream); ++ remote_port = host_get_port(remote); ++ ++ if(match_port < 0 || upstream_port <= 0 || remote_port < 0) { ++ free(match); ++ free(upstream); ++ free(remote); ++ return 1; ++ } ++ ++ char *match_copy = strdup(match); ++ char *upstream_copy = strdup(upstream); ++ char *remote_copy = strdup(remote); ++ ++ struct fwd_rule *rule = (struct fwd_rule*)malloc(sizeof(struct fwd_rule)); ++ if (!rule) { ++ free(match_copy); ++ free(upstream_copy); ++ free(remote_copy); ++ free(match); ++ free(upstream); ++ free(remote); ++ return 1; ++ } ++ ++ if(strcmp(match_copy, "0.0.0.0") == 0 || strcmp(match_copy, "*") == 0) { ++ free(match_copy); ++ rule->match_name = strdup(""); ++ } else { ++ rule->match_name = match_copy; ++ } ++ rule->match_port = match_port; ++ rule->auth_buf = NULL; ++ rule->auth_len = 0; ++ ++ char *at_sign = strchr(upstream_copy, '@'); ++ if (at_sign) { ++ *at_sign = '\0'; ++ char *auth_part = upstream_copy; ++ char *host_part = at_sign + 1; ++ char *colon = strchr(auth_part, ':'); ++ if (!colon) { ++ free(rule); ++ free(upstream_copy); ++ free(remote_copy); ++ free(match); ++ free(upstream); ++ free(remote); ++ return 1; ++ } ++ *colon++ = '\0'; ++ char *username = auth_part; ++ char *password = colon; ++ size_t ulen = strlen(username); ++ size_t plen = strlen(password); ++ if (ulen > 255 || plen > 255) { ++ free(rule); ++ free(upstream_copy); ++ free(remote_copy); ++ free(match); ++ free(upstream); ++ free(remote); ++ return 1; ++ } ++ rule->auth_len = 1 + 1 + ulen + 1 + plen; ++ rule->auth_buf = malloc(rule->auth_len); ++ rule->auth_buf[0] = 1; ++ rule->auth_buf[1] = ulen; ++ memcpy(&rule->auth_buf[2], username, ulen); ++ rule->auth_buf[2 + ulen] = plen; ++ memcpy(&rule->auth_buf[3 + ulen], password, plen); ++ rule->upstream_name = strdup(host_part); ++ rule->upstream_port = upstream_port; ++ /* hide from ps */ ++ memset(str+ncred, '*', ulen+1+plen); ++ } else { ++ rule->upstream_name = strdup(upstream_copy); ++ rule->upstream_port = upstream_port; ++ } ++ ++ free(upstream_copy); ++ short rlen = strlen(remote_copy); ++ rule->req_len = 3 + 1 + 1 + rlen + 2; ++ rule->req_buf = (char*)malloc(rule->req_len); ++ rule->req_buf[0] = 5; ++ rule->req_buf[1] = 1; ++ rule->req_buf[2] = 0; ++ rule->req_buf[3] = 3; ++ rule->req_buf[4] = rlen; ++ memcpy(&rule->req_buf[5], remote_copy, rlen); ++ unsigned short rport = remote_port ? remote_port : 0; ++ rule->req_buf[5 + rlen] = (rport >> 8) & 0xFF; ++ rule->req_buf[5 + rlen + 1] = (rport & 0xFF); ++ free(remote_copy); ++ sblist_add(fwd_rules, rule); ++ free(match); ++ free(upstream); ++ free(remote); ++ ++ return 0; ++} ++ + static int usage(void) { + dprintf(2, + "MicroSocks SOCKS5 Server\n" + "------------------------\n" +- "usage: microsocks -1 -q -i listenip -p port -u user -P pass -b bindaddr -w ips\n" ++ "usage: microsocks -1 -q -i listenip -p port -u user -P pass -b bindaddr -w ips -f fwdrule\n" + "all arguments are optional.\n" + "by default listenip is 0.0.0.0 and port 1080.\n\n" + "option -q disables logging.\n" +@@ -401,6 +684,12 @@ + " this is handy for programs like firefox that don't support\n" + " user/pass auth. for it to work you'd basically make one connection\n" + " with another program that supports it, and then you can use firefox too.\n" ++ "option -f specifies a forwarding rule of the form\n" ++ " match_name:match_port,[user:password@]upstream_name:upstream_port,remote_name:remote_port\n" ++ " this will cause requests that /match/ to be renamed to /remote/\n" ++ " and sent to the /upstream/ SOCKS5 proxy server.\n" ++ " this option may be specified multiple times.\n" ++ "option -V prints version information and exits.\n" + ); + return 1; + } +@@ -416,7 +705,7 @@ + const char *listenip = "0.0.0.0"; + char *p, *q; + unsigned port = 1080; +- while((ch = getopt(argc, argv, ":1qb:i:p:u:P:w:")) != -1) { ++ while((ch = getopt(argc, argv, ":1qb:i:p:u:P:w:f:V")) != -1) { + switch(ch) { + case 'w': /* fall-through */ + case '1': +@@ -456,11 +745,20 @@ + case 'p': + port = atoi(optarg); + break; ++ case 'f': ++ if(!fwd_rules) ++ fwd_rules = sblist_new(sizeof(struct fwd_rule), 16); ++ if(fwd_rules_add(optarg)) ++ return dprintf(2, "error: could not parse forwarding rule %s\n", optarg), 1; ++ break; + case ':': + dprintf(2, "error: option -%c requires an operand\n", optopt); + /* fall through */ + case '?': + return usage(); ++ case 'V': ++ dprintf(1, "MicroSocks %s\n", MICROSOCKS_VERSION); ++ return 0; + } + } + if((auth_user && !auth_pass) || (!auth_user && auth_pass)) {