<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>