mirror of https://gerrit.osmocom.org/libosmocore
osmo_sock_init2: improve support for AF_UNSPEC
osmo_sock_init2 abstract two calls of getaddrinfo into one. While there aren't problems with AF_INET or AF_INET6. When using AF_UNSPEC there are corner cases when this fails. E.g. calling local_host with "" and remote_host with an IPv6 only address results in setting up a local socket with AF_INET while trying to connect from there towards AF_INET6 will most likely fail. To prevent such cases with AF_UNSPEC, search prio calling any syscalls if local and remote site supports AF_INET or AF_INET6. In case both supported, prefer AF_INET6 Change-Id: I397c633931fd00d4f083955a3c49a40fb002d766
This commit is contained in:
parent
6300aff3b2
commit
2c962f5de1
92
src/socket.c
92
src/socket.c
|
@ -260,22 +260,85 @@ int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
|
|||
const char *local_host, uint16_t local_port,
|
||||
const char *remote_host, uint16_t remote_port, unsigned int flags)
|
||||
{
|
||||
struct addrinfo *result, *rp;
|
||||
struct addrinfo *local = NULL, *remote = NULL, *rp;
|
||||
int sfd = -1, rc, on = 1;
|
||||
|
||||
bool local_ipv4 = false, local_ipv6 = false;
|
||||
bool remote_ipv4 = false, remote_ipv6 = false;
|
||||
|
||||
if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
|
||||
"BIND or CONNECT flags\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* figure out local address infos */
|
||||
if (flags & OSMO_SOCK_F_BIND) {
|
||||
local = addrinfo_helper(family, type, proto, local_host, local_port, true);
|
||||
if (!local)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* figure out remote address infos */
|
||||
if (flags & OSMO_SOCK_F_CONNECT) {
|
||||
remote = addrinfo_helper(family, type, proto, remote_host, remote_port, false);
|
||||
if (!remote) {
|
||||
if (local)
|
||||
freeaddrinfo(local);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* It must do a full run to ensure AF_UNSPEC does not fail.
|
||||
* In case first local valid entry is IPv4 and only remote valid entry
|
||||
* is IPv6 or vice versa */
|
||||
if (family == AF_UNSPEC) {
|
||||
for (rp = local; rp != NULL; rp = rp->ai_next) {
|
||||
switch (rp->ai_family) {
|
||||
case AF_INET:
|
||||
local_ipv4 = true;
|
||||
break;
|
||||
case AF_INET6:
|
||||
local_ipv6 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (rp = remote; rp != NULL; rp = rp->ai_next) {
|
||||
switch (rp->ai_family) {
|
||||
case AF_INET:
|
||||
remote_ipv4 = true;
|
||||
break;
|
||||
case AF_INET6:
|
||||
remote_ipv6 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* priotize ipv6 as per RFC */
|
||||
if (local_ipv6 && remote_ipv6)
|
||||
family = AF_INET6;
|
||||
else if (local_ipv4 && remote_ipv4)
|
||||
family = AF_INET;
|
||||
else {
|
||||
if (local)
|
||||
freeaddrinfo(local);
|
||||
if (remote)
|
||||
freeaddrinfo(remote);
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "Unable to find a common protocol (IPv4 or IPv6) for local host: %s and remote host: %s.\n",
|
||||
local_host, remote_host);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
/* figure out local side of socket */
|
||||
if (flags & OSMO_SOCK_F_BIND) {
|
||||
result = addrinfo_helper(family, type, proto, local_host, local_port, true);
|
||||
if (!result)
|
||||
return -EINVAL;
|
||||
for (rp = local; rp != NULL; rp = rp->ai_next) {
|
||||
/* When called with AF_UNSPEC, family will set to IPv4 or IPv6 */
|
||||
if (rp->ai_family != family)
|
||||
continue;
|
||||
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
sfd = socket_helper(rp, flags);
|
||||
if (sfd < 0)
|
||||
continue;
|
||||
|
@ -302,8 +365,11 @@ int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
|
|||
}
|
||||
break;
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
|
||||
freeaddrinfo(local);
|
||||
if (rp == NULL) {
|
||||
if (remote)
|
||||
freeaddrinfo(remote);
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: %s:%u\n",
|
||||
local_host, local_port);
|
||||
return -ENODEV;
|
||||
|
@ -316,14 +382,11 @@ int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
|
|||
|
||||
/* figure out remote side of socket */
|
||||
if (flags & OSMO_SOCK_F_CONNECT) {
|
||||
result = addrinfo_helper(family, type, proto, remote_host, remote_port, false);
|
||||
if (!result) {
|
||||
if (sfd >= 0)
|
||||
close(sfd);
|
||||
return -EINVAL;
|
||||
}
|
||||
for (rp = remote; rp != NULL; rp = rp->ai_next) {
|
||||
/* When called with AF_UNSPEC, family will set to IPv4 or IPv6 */
|
||||
if (rp->ai_family != family)
|
||||
continue;
|
||||
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
if (sfd < 0) {
|
||||
sfd = socket_helper(rp, flags);
|
||||
if (sfd < 0)
|
||||
|
@ -343,7 +406,8 @@ int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
|
|||
}
|
||||
break;
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
|
||||
freeaddrinfo(remote);
|
||||
if (rp == NULL) {
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "no suitable remote addr found for: %s:%u\n",
|
||||
remote_host, remote_port);
|
||||
|
|
|
@ -120,6 +120,27 @@ static int test_sockinit2(void)
|
|||
* FreeBSD 10 or 11 VM at home */
|
||||
OSMO_ASSERT(!strncmp(name, "(r=127.0.0.1:53<->l=127.0.0.1", 29));
|
||||
#endif
|
||||
|
||||
printf("Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv4 & IPv6\n");
|
||||
fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, "::1", 53,
|
||||
OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
|
||||
OSMO_ASSERT(fd < 0);
|
||||
|
||||
printf("Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv6 & IPv4\n");
|
||||
fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "::1", 0, "127.0.0.1", 53,
|
||||
OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
|
||||
OSMO_ASSERT(fd < 0);
|
||||
|
||||
printf("Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv4\n");
|
||||
fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, "127.0.0.1", 53,
|
||||
OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
|
||||
OSMO_ASSERT(fd >= 0);
|
||||
|
||||
printf("Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv6\n");
|
||||
fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "::1", 0, "::1", 53,
|
||||
OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
|
||||
OSMO_ASSERT(fd >= 0);
|
||||
|
||||
talloc_free(name);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
invalid: both bind and connect flags set: 0.0.0.0:0
|
||||
invalid: you have to specify either BIND or CONNECT flags
|
||||
Unable to find a common protocol (IPv4 or IPv6) for local host: 127.0.0.1 and remote host: ::1.
|
||||
Unable to find a common protocol (IPv4 or IPv6) for local host: ::1 and remote host: 127.0.0.1.
|
||||
|
|
|
@ -5,3 +5,7 @@ Checking osmo_sock_init2() with bind to a random local UDP port
|
|||
Checking osmo_sock_init2() for OSMO_SOCK_F_NONBLOCK
|
||||
Checking osmo_sock_init2() for invalid flags
|
||||
Checking osmo_sock_init2() for combined BIND + CONNECT
|
||||
Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv4 & IPv6
|
||||
Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv6 & IPv4
|
||||
Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv4
|
||||
Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv6
|
||||
|
|
Loading…
Reference in New Issue