mirror of
https://github.com/outbackdingo/openwrt-passwall-packages.git
synced 2026-01-27 10:19:59 +00:00
microsocks: add socks5 forwarding rules support (#553)
A self modified enhanced version prepared for optimizing passwall functionality. Version from: https://github.com/lwb1978/microsocks/tree/forward
This commit is contained in:
@@ -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
|
||||
|
||||
421
microsocks/patches/100-Add-SOCKS5-forwarding-rules-support.patch
Normal file
421
microsocks/patches/100-Add-SOCKS5-forwarding-rules-support.patch
Normal file
@@ -0,0 +1,421 @@
|
||||
--- a/sockssrv.c
|
||||
+++ b/sockssrv.c
|
||||
@@ -33,8 +33,10 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
+#include <sys/time.h>
|
||||
#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;i<sblist_getsize(fwd_rules);++i) {
|
||||
+ struct fwd_rule* r = (struct fwd_rule*)sblist_get(fwd_rules, i);
|
||||
+ int name_match = (r->match_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)) {
|
||||
Reference in New Issue
Block a user