rofl0r / microsocks

tiny, portable SOCKS5 server with very moderate resource usage
Other
1.59k stars 277 forks source link

-b IPv4 not working #40

Open olili opened 3 years ago

olili commented 3 years ago

Hello,

with some big support from outside, microsocks is finally listening on 0.0.0.0. Thx.

Nevertheless I still have the problem to bind target output to an IPv4 address. Option "-b IPv4" is ignored on my Deb9+ systems and IPv6 addresses are bound/used.

Is there any hint or trick in order to motivate microsocks to use the IPv4 addresses on these systems for target adresses?

I can provide traces if needed. No problem.

Thx in advance Oliver

rofl0r commented 3 years ago

I can provide traces if needed

yeah, let's start with a strace including the startup of microsocks and an outgoing connection that tries bind to v4 addr but fails. also info about network adapters and netstat.

olili commented 3 years ago

here you are:

root@fr0:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: venet0: <BROADCAST,POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/void
    inet 127.0.0.1/32 scope host venet0
       valid_lft forever preferred_lft forever
    inet 145.239.124.222/32 brd 145.239.124.222 scope global venet0:0
       valid_lft forever preferred_lft forever
    inet6 2001:41d0:203:9ad1::6758:cde7/128 scope global
       valid_lft forever preferred_lft forever
root@fr0:~# strace /usr/bin/microsocks -i :: -b 145.239.124.222
execve("/usr/bin/microsocks", ["/usr/bin/microsocks", "-i", "::", "-b", "145.239.124.222"], [/* 13 vars */]) = 0
brk(NULL)                               = 0x55ec3fe36000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=48358, ...}) = 0
mmap(NULL, 48358, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f4f06f57000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0Pa\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=135440, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4f06f55000
mmap(NULL, 2212936, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4f06b23000
mprotect(0x7f4f06b3b000, 2093056, PROT_NONE) = 0
mmap(0x7f4f06d3a000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17000) = 0x7f4f06d3a000
mmap(0x7f4f06d3c000, 13384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f4f06d3c000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\4\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1689360, ...}) = 0
mmap(NULL, 3795296, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4f06784000
mprotect(0x7f4f06919000, 2097152, PROT_NONE) = 0
mmap(0x7f4f06b19000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x195000) = 0x7f4f06b19000
mmap(0x7f4f06b1f000, 14688, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f4f06b1f000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4f06f53000
arch_prctl(ARCH_SET_FS, 0x7f4f06f53700) = 0
mprotect(0x7f4f06b19000, 16384, PROT_READ) = 0
mprotect(0x7f4f06d3a000, 4096, PROT_READ) = 0
mprotect(0x55ec3f1ae000, 4096, PROT_READ) = 0
mprotect(0x7f4f06f63000, 4096, PROT_READ) = 0
munmap(0x7f4f06f57000, 48358)           = 0
set_tid_address(0x7f4f06f539d0)         = 3341
set_robust_list(0x7f4f06f539e0, 24)     = 0
rt_sigaction(SIGRTMIN, {sa_handler=0x7f4f06b28bd0, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f4f06b340e0}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {sa_handler=0x7f4f06b28c60, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f4f06b340e0}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f4f067b7060}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
brk(NULL)                               = 0x55ec3fe36000
brk(0x55ec3fe57000)                     = 0x55ec3fe57000
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET6, sin6_port=htons(1080), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, 28) = 0
listen(3, 4096)                         = 0
accept(3, {sa_family=AF_INET6, sin6_port=htons(58419), inet_pton(AF_INET6, "::ffff:37.209.62.191", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [28]) = 4
mmap(NULL, 36864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f4f06f5a000
mprotect(0x7f4f06f5a000, 4096, PROT_NONE) = 0
clone(child_stack=0x7f4f06f61ff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f4f06f629d0, tls=0x7f4f06f62700, child_tidptr=0x7f4f06f629d0) = 3344
accept(3, client[4] ::ffff:37.209.62.191: connected to ident.me:80
{sa_family=AF_INET6, sin6_port=htons(58422), inet_pton(AF_INET6, "2a02:8070:21c3:4c00:b191:4c35:7a2f:5b74", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [28]) = 5
mmap(NULL, 36864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f4f06f4a000
mprotect(0x7f4f06f4a000, 4096, PROT_NONE) = 0
clone(child_stack=0x7f4f06f51ff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f4f06f529d0, tls=0x7f4f06f52700, child_tidptr=0x7f4f06f529d0) = 3347
accept(3, client[5] 2a02:8070:21c3:4c00:b191:4c35:7a2f:5b74: connected to ident.me:80

to accesses are logged:

C:\Users\Oli\Desktop\curl>curl -4 -x socks5h://fr0.box0.eu ident.me
2001:41d0:203:9ad1::6758:cde7
C:\Users\Oli\Desktop\curl>curl -6 -x socks5h://fr0.box0.eu ident.me
2001:41d0:203:9ad1::6758:cde7

Can you help?

rofl0r commented 3 years ago

strace /usr/bin/microsocks

this should have been strace -f as the interesting bit of information (what happens in client thread) is missing.

do you also have netstat -lnat available ?

olili commented 3 years ago

sorry, "-f" I forgot... Attached the traces. Do you need something elses?

dbg.txt

rofl0r commented 3 years ago

you seem to be using an old version of microsocks that still uses select(). the strace log is incredibly cluttered thanks to glibc's retarded getaddrinfo() implementation and it took me like 5 mins to find the actual chunk where the outgoing connection happens.

and what happens is odd: bind() call seems to happen with ip passed to -i parameter, not the one of -b parameter.

i've uploaded a static linked microsocks binary to the latest release (direct link: https://github.com/rofl0r/microsocks/releases/download/v1.0.2/microsocks-1.0.2-x86_64-static.xz ) which uses musl libc; it'd interesting if you could test this one instead. (unxz, chmod +x, move to /usr/bin )

olili commented 3 years ago

thx a lot!!!!!!

I immediatelly tested it. But it is continously delivering 'invalid argument' Traces and logs attached. Hopefully this helps.

dbg2.txt

rofl0r commented 3 years ago

interesting, this is more conclusive. the dns result returned for ident.me seems to prefer the ipv6 address (microsocks always uses first result) and so the outgoing socket is bound to AF_INET6; and naturally binding ipv4 address to ivp6 socket fails with EINVAL.

[pid 15601] socket(AF_INET6, SOCK_STREAM, IPPROTO_IP) = 6
[pid 15601] bind(6, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("145.239.124.222")}, 16) = -1 EINVAL (Invalid argument)

you can probably fix your exact issue by editing /etc/gai.conf to prefer ipv4 over ipv6: https://askubuntu.com/questions/32298/prefer-a-ipv4-dns-lookups-before-aaaaipv6-lookups (but you then need to use your original glibc-linked binary). as far as microsocks is concerned, i'm not yet sure whether there's a good "automatic" solution. just out of interest, could you post the results of host ident.me executed from that vps ?

olili commented 3 years ago

changed precedence ::ffff:0:0/96 100 in gai.conf.

Now the listen "0.0.0.0" problem seems to be back. "-i ::" is not listen to 0.0.0.0. See dbg3.txt

If I start microsocks now with "-i 0.0.0.0" it is at least listening to to IPv4 and outputing corretcly via IPv4. But It is not reachable by IPv6. See dbg4.txt

Pls don't hesitiate if you need more traces or tests.

Reverted gai.conf to the old state/original priorities and host ident.me delievers ident.me has address 176.58.123.25 ident.me has IPv6 address 2a01:7e00::f03c:91ff:fe70:2b9d ident.me mail is handled by 20 eforward5.registrar-servers.com. ident.me mail is handled by 15 eforward4.registrar-servers.com. ident.me mail is handled by 10 eforward1.registrar-servers.com. ident.me mail is handled by 10 eforward2.registrar-servers.com. ident.me mail is handled by 10 eforward3.registrar-servers.com.

olili commented 3 years ago

I#m not a very expert on that topic. But wrt getaddrinfo I read in the api: ai_family This field specifies the desired address family for the returned addresses. Valid values for this field include AF_INET and AF_INET6. The value AF_UNSPEC indicates that getaddrinfo() should return socket addresses for any address family (either IPv4 or IPv6, for example) that can be used with node and service.

Your resolver is AF_UNSPEC (if I understand the code correctly). So it's somehow undetermined. Maybe you have to distinguish this dependend on the address type requested by -i and -b.

rofl0r commented 3 years ago

i won't have time to look into this today to find a good solution. i guess one thing one could do is to iterate over gai results and prefer family matching the bind address. anyway, still unsure. what you could try in the meantime is to simply run two instances of microsocks, one listening on "0.0.0.0" and second one on "::". the starting order could matter. it should be possible to use the same port.

olili commented 3 years ago

resolved the issue by modifying of the resolver part. Thx for your support and this very smart tool O.

microsocks.zip

rofl0r commented 3 years ago

can you paste a diff your work ? so i (and everyone else reading this) dont have to download, extract, diff themselves. (just run git diff in your clone and paste output)

olili commented 3 years ago

sure. in server.c:

int resolve(const char *host, unsigned short port, struct addrinfo** addr, int aimode) {
    struct addrinfo hints = {
        .ai_family = aimode,   //UNSPEC,
        .ai_socktype = SOCK_STREAM,
        .ai_flags = AI_PASSIVE,
    };
    char port_buf[8];
    snprintf(port_buf, sizeof port_buf, "%u", port);
    return getaddrinfo(host, port_buf, &hints, addr);
}

int resolve_sa(const char *host, unsigned short port, union sockaddr_union *res, int *aimode ) {
    struct addrinfo hints, *ainfo = 0;
    int ret;

  memset (&hints, 0, sizeof (hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags |= AI_PASSIVE;

    char port_buf[8];
    snprintf(port_buf, sizeof port_buf, "%u", port);

  if ((ret = getaddrinfo (host, port_buf, &hints, &ainfo))) return ret;

  *aimode =  ainfo->ai_family; 

  memcpy(res, ainfo->ai_addr, ainfo->ai_addrlen);
  freeaddrinfo(ainfo);

    return 0;
}

Also update of the respective h: int resolve(const char *host, unsigned short port, struct addrinfo* addr, int aimode); int resolve_sa(const char host, unsigned short port, union sockaddr_union res, int aimode );

In socketsrv.c: some small changes, see atachment diff_socketsrv.pdf

`

rofl0r commented 3 years ago
diff --git a/server.c b/server.c
index 9f0ab9a..1730db9 100644
--- a/server.c
+++ b/server.c
@@ -3,9 +3,9 @@
 #include <string.h>
 #include <unistd.h>

-int resolve(const char *host, unsigned short port, struct addrinfo** addr) {
+int resolve(const char *host, unsigned short port, struct addrinfo** addr, int aimode) {
    struct addrinfo hints = {
-       .ai_family = AF_UNSPEC,
+       .ai_family = aimode,   //UNSPEC,
        .ai_socktype = SOCK_STREAM,
        .ai_flags = AI_PASSIVE,
    };
@@ -14,18 +14,30 @@ int resolve(const char *host, unsigned short port, struct addrinfo** addr) {
    return getaddrinfo(host, port_buf, &hints, addr);
 }

-int resolve_sa(const char *host, unsigned short port, union sockaddr_union *res) {
-   struct addrinfo *ainfo = 0;
+int resolve_sa(const char *host, unsigned short port, union sockaddr_union *res, int *aimode ) {
+   struct addrinfo hints, *ainfo = 0;
    int ret;
-   SOCKADDR_UNION_AF(res) = AF_UNSPEC;
-   if((ret = resolve(host, port, &ainfo))) return ret;
-   memcpy(res, ainfo->ai_addr, ainfo->ai_addrlen);
-   freeaddrinfo(ainfo);
-   return 0;
+
+  memset (&hints, 0, sizeof (hints));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags |= AI_PASSIVE;
+
+   char port_buf[8];
+   snprintf(port_buf, sizeof port_buf, "%u", port);
+
+  if ((ret = getaddrinfo (host, port_buf, &hints, &ainfo))) return ret;
+
+  *aimode =  ainfo->ai_family; 
+
+  memcpy(res, ainfo->ai_addr, ainfo->ai_addrlen);
+  freeaddrinfo(ainfo);
+  
+   return 0;
 }

 int bindtoip(int fd, union sockaddr_union *bindaddr) {
-   socklen_t sz = SOCKADDR_UNION_LENGTH(bindaddr);
+       socklen_t sz = SOCKADDR_UNION_LENGTH(bindaddr);
    if(sz)
        return bind(fd, (struct sockaddr*) bindaddr, sz);
    return 0;
@@ -38,7 +50,7 @@ int server_waitclient(struct server *server, struct client* client) {

 int server_setup(struct server *server, const char* listenip, unsigned short port) {
    struct addrinfo *ainfo = 0;
-   if(resolve(listenip, port, &ainfo)) return -1;
+   if(resolve(listenip, port, &ainfo, AF_UNSPEC)) return -1;
    struct addrinfo* p;
    int listenfd = -1;
    for(p = ainfo; p; p = p->ai_next) {
diff --git a/server.h b/server.h
index 5acf664..827ddc6 100644
--- a/server.h
+++ b/server.h
@@ -38,8 +38,8 @@ struct server {
    int fd;
 };

-int resolve(const char *host, unsigned short port, struct addrinfo** addr);
-int resolve_sa(const char *host, unsigned short port, union sockaddr_union *res);
+int resolve(const char *host, unsigned short port, struct addrinfo** addr, int aimode);
+int resolve_sa(const char *host, unsigned short port, union sockaddr_union *res, int *aimode );
 int bindtoip(int fd, union sockaddr_union *bindaddr);

 int server_waitclient(struct server *server, struct client* client);
diff --git a/sockssrv.c b/sockssrv.c
index 98e4622..2cc3959 100644
--- a/sockssrv.c
+++ b/sockssrv.c
@@ -60,6 +60,7 @@ static sblist* auth_ips;
 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 int aimode;

 enum socksstate {
    SS_1_CONNECTED,
@@ -139,7 +140,7 @@ static int connect_socks_target(unsigned char *buf, size_t n, struct client *cli
    unsigned short port;
    port = (buf[minlen-2] << 8) | buf[minlen-1];
    /* there's no suitable errorcode in rfc1928 for dns lookup failure */
-   if(resolve(namebuf, port, &remote)) return -EC_GENERAL_FAILURE;
+   if(resolve(namebuf, port, &remote, aimode)) return -EC_GENERAL_FAILURE;
    int fd = socket(remote->ai_addr->sa_family, SOCK_STREAM, 0);
    if(fd == -1) {
        eval_errno:
@@ -385,15 +386,16 @@ static void zero_arg(char *s) {

 int main(int argc, char** argv) {
    int ch;
-   const char *listenip = "0.0.0.0";
+   const char *listenip = "::";
+   aimode = AF_UNSPEC;
    unsigned port = 1080;
-   while((ch = getopt(argc, argv, ":1b:i:p:u:P:")) != -1) {
+   while((ch = getopt(argc, argv, ":m:1b:i:p:u:P:")) != -1) {
        switch(ch) {
            case '1':
                auth_ips = sblist_new(sizeof(union sockaddr_union), 8);
                break;
            case 'b':
-               resolve_sa(optarg, 0, &bind_addr);
+               resolve_sa(optarg, 0, &bind_addr, &aimode);
                break;
            case 'u':
                auth_user = strdup(optarg);
@@ -409,6 +411,11 @@ int main(int argc, char** argv) {
            case 'p':
                port = atoi(optarg);
                break;
+//         case 'm':
+//             if ( atoi(optarg) == 4) aimode = PF_INET; 
+//             if ( atoi(optarg) == 6) aimode = AF_INET6;
+//             printf("GetAddressInfo Mode: IPv%d \n",  atoi(optarg) );
+//             break;
            case ':':
                dprintf(2, "error: option -%c requires an operand\n", optopt);
                /* fall through */
@@ -424,6 +431,11 @@ int main(int argc, char** argv) {
        dprintf(2, "error: auth-once option must be used together with user/pass\n");
        return 1;
    }
+   
+   if ( aimode == AF_INET ) printf("ipV4 only for GetAddressInfo\n");
+   if ( aimode == AF_INET6 ) printf("ipV6 only for GetAddressInfo\n");
+   if ( aimode == AF_UNSPEC ) printf("ipv4 + ipv6 for GetAddressInfo\n");
+   
    signal(SIGPIPE, SIG_IGN);
    struct server s;
    sblist *threads = sblist_new(sizeof (struct thread*), 8);

http://ix.io/3w7M