Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

-b IPv4 not working #40

Open
olili opened this issue Aug 12, 2021 · 14 comments
Open

-b IPv4 not working #40

olili opened this issue Aug 12, 2021 · 14 comments

Comments

@olili
Copy link

olili commented Aug 12, 2021

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
Copy link
Owner

rofl0r commented Aug 12, 2021

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
Copy link
Author

olili commented Aug 12, 2021

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
Copy link
Owner

rofl0r commented Aug 12, 2021

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
Copy link
Author

olili commented Aug 12, 2021

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

dbg.txt

@rofl0r
Copy link
Owner

rofl0r commented Aug 12, 2021

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
Copy link
Author

olili commented Aug 13, 2021

thx a lot!!!!!!

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

dbg2.txt

@rofl0r
Copy link
Owner

rofl0r commented Aug 13, 2021

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
Copy link
Author

olili commented Aug 13, 2021

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
Copy link
Author

olili commented Aug 13, 2021

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
Copy link
Owner

rofl0r commented Aug 14, 2021

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
Copy link
Author

olili commented Aug 15, 2021

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

microsocks.zip

@rofl0r
Copy link
Owner

rofl0r commented Aug 15, 2021

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
Copy link
Author

olili commented Aug 15, 2021

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
Copy link
Owner

rofl0r commented Aug 15, 2021

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

parke added a commit to parke/microsocks that referenced this issue Dec 12, 2021
If `-b bindaddr` is used, then created sockets must be of the same address family as `bindaddr` (otherwise, calls to `bind()` will fail).

This patch harmonizes the address family across the calls to `socket()`, `bind()`, and `connect()`.

Prior to this patch, `microsocks` would use the first `addrinfo` record returned by `getaddrinfo()`.  On systems with both IPv4 and IPv6, sometimes the address family of the first record would be a mismatch with `bindaddr`, resulting in an error.

This patch may fix issue rofl0r#30.  (And issue rofl0r#40?)
parke added a commit to parke/microsocks that referenced this issue Dec 12, 2021
If `-b bindaddr` is used, then created sockets must be of the same address family as `bindaddr` (otherwise, calls to `bind()` will fail).

This patch harmonizes the address family across the calls to `socket()`, `bind()`, and `connect()`.

Prior to this patch, `microsocks` would use the first `addrinfo` record returned by `getaddrinfo()`.  On systems with both IPv4 and IPv6, sometimes the address family of the first record would be a mismatch with `bindaddr`, resulting in an error.

This patch may fix issue rofl0r#30.  (And issue rofl0r#40?)
rofl0r pushed a commit that referenced this issue Dec 13, 2021
If `-b bindaddr` is used, then created sockets must be of the same address
family as `bindaddr`, otherwise, calls to `bind()` will fail.

This patch harmonizes the address family across the calls to `socket()`,
`bind()`, and `connect()`.

Prior to this patch, `microsocks` would use the first `addrinfo` record
returned by `getaddrinfo()`.
On systems with both IPv4 and IPv6, sometimes the address family of the
first record would be a mismatch with `bindaddr`, resulting in an error.

Fixes #30.  (And possibly #40)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants