<p>lynxis lazus has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/19140">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">osmo_sock_init2: improve support for AF_UNSPEC<br><br>osmo_sock_init2 abstract two calls of getaddrinfo into one.<br>While there aren't problems with AF_INET or AF_INET6. When using<br>AF_UNSPEC there are corner cases when this fails. E.g. calling<br>local_host with "" and remote_host with an IPv6 only address results in<br>setting up a local socket with AF_INET while trying to connect from there towards<br>AF_INET6 will most likely fail.<br>To prevent such cases with AF_UNSPEC, search prio calling any syscalls if local and remote site<br>supports AF_INET or AF_INET6. In case both supported, prefer AF_INET6<br><br>Change-Id: I397c633931fd00d4f083955a3c49a40fb002d766<br>---<br>M src/socket.c<br>M tests/socket/socket_test.c<br>M tests/socket/socket_test.ok<br>3 files changed, 99 insertions(+), 14 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/40/19140/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/socket.c b/src/socket.c</span><br><span>index 503ceaf..359e24d 100644</span><br><span>--- a/src/socket.c</span><br><span>+++ b/src/socket.c</span><br><span>@@ -260,22 +260,81 @@</span><br><span> const char *local_host, uint16_t local_port,</span><br><span> const char *remote_host, uint16_t remote_port, unsigned int flags)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- struct addrinfo *result, *rp;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct addrinfo *local = NULL, *remote = NULL, *rp;</span><br><span> int sfd = -1, rc, on = 1;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ bool local_ipv4 = false, local_ipv6 = false;</span><br><span style="color: hsl(120, 100%, 40%);">+ bool remote_ipv4 = false, remote_ipv6 = false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {</span><br><span> LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "</span><br><span> "BIND or CONNECT flags\n");</span><br><span> return -EINVAL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* figure out local address infos */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (flags & OSMO_SOCK_F_BIND) {</span><br><span style="color: hsl(120, 100%, 40%);">+ local = addrinfo_helper(family, type, proto, local_host, local_port, true);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!local)</span><br><span style="color: hsl(120, 100%, 40%);">+ return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* figure out remote address infos */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (flags & OSMO_SOCK_F_CONNECT) {</span><br><span style="color: hsl(120, 100%, 40%);">+ remote = addrinfo_helper(family, type, proto, remote_host, remote_port, false);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!remote) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (local)</span><br><span style="color: hsl(120, 100%, 40%);">+ freeaddrinfo(local);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* It must do a full run to ensure AF_UNSPEC does not fail.</span><br><span style="color: hsl(120, 100%, 40%);">+ * In case first local valid entry is IPv4 and only remote valid entry</span><br><span style="color: hsl(120, 100%, 40%);">+ * is IPv6 or vice versa */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (family == AF_UNSPEC) {</span><br><span style="color: hsl(120, 100%, 40%);">+ for (rp = local; rp != NULL; rp = rp->ai_next) {</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (rp->ai_family) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case AF_INET:</span><br><span style="color: hsl(120, 100%, 40%);">+ local_ipv4 = true;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case AF_INET6:</span><br><span style="color: hsl(120, 100%, 40%);">+ local_ipv6 = true;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (rp = remote; rp != NULL; rp = rp->ai_next) {</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (rp->ai_family) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case AF_INET:</span><br><span style="color: hsl(120, 100%, 40%);">+ remote_ipv4 = true;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case AF_INET6:</span><br><span style="color: hsl(120, 100%, 40%);">+ remote_ipv6 = true;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* priotize ipv6 as per RFC */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (local_ipv6 && remote_ipv6)</span><br><span style="color: hsl(120, 100%, 40%);">+ family = AF_INET6;</span><br><span style="color: hsl(120, 100%, 40%);">+ else if (local_ipv4 && remote_ipv4)</span><br><span style="color: hsl(120, 100%, 40%);">+ family = AF_INET;</span><br><span style="color: hsl(120, 100%, 40%);">+ else {</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGP(DLGLOBAL, LOGL_ERROR, "Unable to find a common protocol (IPv4 or IPv6) for local host: %s and remote host: %s.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ local_host, remote_host);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -ENODEV;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* figure out local side of socket */</span><br><span> if (flags & OSMO_SOCK_F_BIND) {</span><br><span style="color: hsl(0, 100%, 40%);">- result = addrinfo_helper(family, type, proto, local_host, local_port, true);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!result)</span><br><span style="color: hsl(0, 100%, 40%);">- return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+ for (rp = local; rp != NULL; rp = rp->ai_next) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* When called with AF_UNSPEC, family will set to IPv4 or IPv6 */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rp->ai_family != family)</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- for (rp = result; rp != NULL; rp = rp->ai_next) {</span><br><span> sfd = socket_helper(rp, flags);</span><br><span> if (sfd < 0)</span><br><span> continue;</span><br><span>@@ -302,8 +361,11 @@</span><br><span> }</span><br><span> break;</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- freeaddrinfo(result);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ freeaddrinfo(local);</span><br><span> if (rp == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (remote)</span><br><span style="color: hsl(120, 100%, 40%);">+ freeaddrinfo(remote);</span><br><span> LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: %s:%u\n",</span><br><span> local_host, local_port);</span><br><span> return -ENODEV;</span><br><span>@@ -316,14 +378,11 @@</span><br><span> </span><br><span> /* figure out remote side of socket */</span><br><span> if (flags & OSMO_SOCK_F_CONNECT) {</span><br><span style="color: hsl(0, 100%, 40%);">- result = addrinfo_helper(family, type, proto, remote_host, remote_port, false);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!result) {</span><br><span style="color: hsl(0, 100%, 40%);">- if (sfd >= 0)</span><br><span style="color: hsl(0, 100%, 40%);">- close(sfd);</span><br><span style="color: hsl(0, 100%, 40%);">- return -EINVAL;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(120, 100%, 40%);">+ for (rp = remote; rp != NULL; rp = rp->ai_next) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* When called with AF_UNSPEC, family will set to IPv4 or IPv6 */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rp->ai_family != family)</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- for (rp = result; rp != NULL; rp = rp->ai_next) {</span><br><span> if (sfd < 0) {</span><br><span> sfd = socket_helper(rp, flags);</span><br><span> if (sfd < 0)</span><br><span>@@ -343,7 +402,8 @@</span><br><span> }</span><br><span> break;</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- freeaddrinfo(result);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ freeaddrinfo(remote);</span><br><span> if (rp == NULL) {</span><br><span> LOGP(DLGLOBAL, LOGL_ERROR, "no suitable remote addr found for: %s:%u\n",</span><br><span> remote_host, remote_port);</span><br><span>diff --git a/tests/socket/socket_test.c b/tests/socket/socket_test.c</span><br><span>index 37e0281..e68243c 100644</span><br><span>--- a/tests/socket/socket_test.c</span><br><span>+++ b/tests/socket/socket_test.c</span><br><span>@@ -120,6 +120,27 @@</span><br><span> * FreeBSD 10 or 11 VM at home */</span><br><span> OSMO_ASSERT(!strncmp(name, "(r=127.0.0.1:53<->l=127.0.0.1", 29));</span><br><span> #endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ printf("Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv4 & IPv6\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, "::1", 53,</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(fd < 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ printf("Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv6 & IPv4\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "::1", 0, "127.0.0.1", 53,</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(fd < 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ printf("Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv4\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, "127.0.0.1", 53,</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(fd >= 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ printf("Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv6\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "::1", 0, "::1", 53,</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(fd >= 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> talloc_free(name);</span><br><span> </span><br><span> return 0;</span><br><span>diff --git a/tests/socket/socket_test.ok b/tests/socket/socket_test.ok</span><br><span>index 4b24fbc..4265be8 100644</span><br><span>--- a/tests/socket/socket_test.ok</span><br><span>+++ b/tests/socket/socket_test.ok</span><br><span>@@ -5,3 +5,7 @@</span><br><span> Checking osmo_sock_init2() for OSMO_SOCK_F_NONBLOCK</span><br><span> Checking osmo_sock_init2() for invalid flags</span><br><span> Checking osmo_sock_init2() for combined BIND + CONNECT</span><br><span style="color: hsl(120, 100%, 40%);">+Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv4 & IPv6</span><br><span style="color: hsl(120, 100%, 40%);">+Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv6 & IPv4</span><br><span style="color: hsl(120, 100%, 40%);">+Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv4</span><br><span style="color: hsl(120, 100%, 40%);">+Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv6</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/19140">change 19140</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/libosmocore/+/19140"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I397c633931fd00d4f083955a3c49a40fb002d766 </div>
<div style="display:none"> Gerrit-Change-Number: 19140 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: lynxis lazus <lynxis@fe80.eu> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>