Fix to handle OS disabled IPv6, issue #714.

- Changes made only in the os_calls.c file.
- Exported functions changed: g_tcp_bind g_tcp_bind_address g_tcp_connect
- Support three network configurations:
  1) Normal network, with IPv6
  2) Partly disabled IPv6 via sysctl.conf
  3) Total disabled IPv6 via grub
master
MichaelSweden 8 years ago committed by metalefty
parent 10fe699466
commit 106ae2cd43

@ -727,8 +727,69 @@ g_sck_close(int sck)
#endif #endif
} }
#if defined(XRDP_ENABLE_IPV6)
/*****************************************************************************/
/* Helper function for g_tcp_connect. */
static int
connect_loopback(int sck, const char *port)
{
struct sockaddr_in6 sa;
struct sockaddr_in s;
int res;
// First IPv6
g_memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6;
sa.sin6_addr = in6addr_loopback; // IPv6 ::1
sa.sin6_port = htons((tui16)atoi(port));
res = connect(sck, (struct sockaddr*)&sa, sizeof(sa));
if (res == -1 && errno == EINPROGRESS)
{
return -1;
}
if (res == 0 || (res == -1 && errno == EISCONN))
{
return 0;
}
// else IPv4
g_memset(&sa, 0, sizeof(s));
s.sin_family = AF_INET;
s.sin_addr.s_addr = htonl(INADDR_LOOPBACK); // IPv4 127.0.0.1
s.sin_port = htons((tui16)atoi(port));
res = connect(sck, (struct sockaddr*)&s, sizeof(s));
if (res == -1 && errno == EINPROGRESS)
{
return -1;
}
if (res == 0 || (res == -1 && errno == EISCONN))
{
return 0;
}
// else IPv6 with IPv4 address
g_memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6;
inet_pton(AF_INET6, "::FFFF:127.0.0.1", &sa.sin6_addr);
sa.sin6_port = htons((tui16)atoi(port));
res = connect(sck, (struct sockaddr*)&sa, sizeof(sa));
if (res == -1 && errno == EINPROGRESS)
{
return -1;
}
if (res == 0 || (res == -1 && errno == EISCONN))
{
return 0;
}
return -1;
}
#endif
/*****************************************************************************/ /*****************************************************************************/
/* returns error, zero is good */ /* returns error, zero is good */
/* The connection might get to be in progress, if so -1 is returned. */
/* The caller needs to call again to check if succeed. */
#if defined(XRDP_ENABLE_IPV6) #if defined(XRDP_ENABLE_IPV6)
int int
g_tcp_connect(int sck, const char *address, const char *port) g_tcp_connect(int sck, const char *address, const char *port)
@ -738,22 +799,15 @@ g_tcp_connect(int sck, const char *address, const char *port)
struct addrinfo *h = (struct addrinfo *)NULL; struct addrinfo *h = (struct addrinfo *)NULL;
struct addrinfo *rp = (struct addrinfo *)NULL; struct addrinfo *rp = (struct addrinfo *)NULL;
/* initialize (zero out) local variables: */
g_memset(&p, 0, sizeof(struct addrinfo)); g_memset(&p, 0, sizeof(struct addrinfo));
/* in IPv6-enabled environments, set the AI_V4MAPPED
* flag in ai_flags and specify ai_family=AF_INET6 in
* order to ensure that getaddrinfo() returns any
* available IPv4-mapped addresses in case the target
* host does not have a true IPv6 address:
*/
p.ai_socktype = SOCK_STREAM; p.ai_socktype = SOCK_STREAM;
p.ai_protocol = IPPROTO_TCP; p.ai_protocol = IPPROTO_TCP;
p.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED; p.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED;
p.ai_family = AF_INET6; p.ai_family = AF_INET6;
if (g_strcmp(address, "127.0.0.1") == 0) if (g_strcmp(address, "127.0.0.1") == 0)
{ {
res = getaddrinfo("::1", port, &p, &h); return connect_loopback(sck, port);
} }
else else
{ {
@ -761,8 +815,8 @@ g_tcp_connect(int sck, const char *address, const char *port)
} }
if (res != 0) if (res != 0)
{ {
log_message(LOG_LEVEL_ERROR, "g_tcp_connect: getaddrinfo() failed: %s", log_message(LOG_LEVEL_ERROR, "g_tcp_connect(%d, %s, %s): getaddrinfo() failed: %s",
gai_strerror(res)); sck, address, port, gai_strerror(res));
} }
if (res > -1) if (res > -1)
{ {
@ -770,23 +824,22 @@ g_tcp_connect(int sck, const char *address, const char *port)
{ {
for (rp = h; rp != NULL; rp = rp->ai_next) for (rp = h; rp != NULL; rp = rp->ai_next)
{ {
rp = h;
res = connect(sck, (struct sockaddr *)(rp->ai_addr), res = connect(sck, (struct sockaddr *)(rp->ai_addr),
rp->ai_addrlen); rp->ai_addrlen);
if (res != -1) if (res == -1 && errno == EINPROGRESS)
{ {
break; /* Return -1 */
}
if (res == 0 || (res == -1 && errno == EISCONN))
{
res = 0;
break; /* Success */ break; /* Success */
} }
} }
freeaddrinfo(h);
} }
} }
/* Mac OSX connect() returns -1 for already established connections */
if (res == -1 && errno == EISCONN)
{
res = 0;
}
return res; return res;
} }
#else #else
@ -871,156 +924,207 @@ g_sck_set_non_blocking(int sck)
#if defined(XRDP_ENABLE_IPV6) #if defined(XRDP_ENABLE_IPV6)
/*****************************************************************************/ /*****************************************************************************/
/* return boolean */ /* returns error, zero is good */
static int int
address_match(const char *address, struct addrinfo *j) g_tcp_bind(int sck, const char *port)
{ {
struct sockaddr_in *ipv4_in; struct sockaddr_in6 sa;
struct sockaddr_in6 *ipv6_in; struct sockaddr_in s;
int errno6;
if (address == 0) // First IPv6
g_memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6;
sa.sin6_addr = in6addr_any; // IPv6 ::
sa.sin6_port = htons((tui16)atoi(port));
if (bind(sck, (struct sockaddr*)&sa, sizeof(sa)) == 0)
{ {
return 1; return 0;
} }
if (address[0] == 0) errno6 = errno;
// else IPv4
g_memset(&sa, 0, sizeof(s));
s.sin_family = AF_INET;
s.sin_addr.s_addr = htonl(INADDR_ANY); // IPv4 0.0.0.0
s.sin_port = htons((tui16)atoi(port));
if (bind(sck, (struct sockaddr*)&s, sizeof(s)) == 0)
{ {
return 1; return 0;
} }
if (g_strcmp(address, "0.0.0.0") == 0)
{ log_message(LOG_LEVEL_ERROR, "g_tcp_bind(%d, %s) failed "
return 1; "bind IPv6 (errno=%d) and IPv4 (errno=%d).",
sck, port, errno6, errno);
return -1;
} }
if ((g_strcmp(address, "127.0.0.1") == 0) || #else
(g_strcmp(address, "::1") == 0) || int
(g_strcmp(address, "localhost") == 0)) g_tcp_bind(int sck, const char* port)
{
if (j->ai_addr != 0)
{
if (j->ai_addr->sa_family == AF_INET)
{
ipv4_in = (struct sockaddr_in *) (j->ai_addr);
if (inet_pton(AF_INET, "127.0.0.1", &(ipv4_in->sin_addr)))
{ {
return 1; struct sockaddr_in s;
}
memset(&s, 0, sizeof(struct sockaddr_in));
s.sin_family = AF_INET;
s.sin_port = htons((tui16)atoi(port));
s.sin_addr.s_addr = INADDR_ANY;
return bind(sck, (struct sockaddr*)&s, sizeof(struct sockaddr_in));
} }
if (j->ai_addr->sa_family == AF_INET6) #endif
{
ipv6_in = (struct sockaddr_in6 *) (j->ai_addr); /*****************************************************************************/
if (inet_pton(AF_INET6, "::1", &(ipv6_in->sin6_addr))) int
g_sck_local_bind(int sck, const char *port)
{ {
return 1; #if defined(_WIN32)
} return -1;
} #else
} struct sockaddr_un s;
memset(&s, 0, sizeof(struct sockaddr_un));
s.sun_family = AF_UNIX;
strncpy(s.sun_path, port, sizeof(s.sun_path));
s.sun_path[sizeof(s.sun_path) - 1] = 0;
return bind(sck, (struct sockaddr *)&s, sizeof(struct sockaddr_un));
#endif
} }
if (j->ai_addr != 0)
{ #if defined(XRDP_ENABLE_IPV6)
if (j->ai_addr->sa_family == AF_INET) /*****************************************************************************/
/* Helper function for g_tcp_bind_address. */
static int
bind_loopback(int sck, const char *port)
{ {
ipv4_in = (struct sockaddr_in *) (j->ai_addr); struct sockaddr_in6 sa;
if (inet_pton(AF_INET, address, &(ipv4_in->sin_addr))) struct sockaddr_in s;
int errno6;
int errno4;
// First IPv6
g_memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6;
sa.sin6_addr = in6addr_loopback; // IPv6 ::1
sa.sin6_port = htons((tui16)atoi(port));
if (bind(sck, (struct sockaddr*)&sa, sizeof(sa)) == 0)
{ {
return 1; return 0;
}
} }
if (j->ai_addr->sa_family == AF_INET6) errno6 = errno;
{
ipv6_in = (struct sockaddr_in6 *) (j->ai_addr); // else IPv4
if (inet_pton(AF_INET6, address, &(ipv6_in->sin6_addr))) g_memset(&sa, 0, sizeof(s));
s.sin_family = AF_INET;
s.sin_addr.s_addr = htonl(INADDR_LOOPBACK); // IPv4 127.0.0.1
s.sin_port = htons((tui16)atoi(port));
if (bind(sck, (struct sockaddr*)&s, sizeof(s)) == 0)
{ {
return 1; return 0;
}
}
} }
errno4 = errno;
// else IPv6 with IPv4 address
g_memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6;
inet_pton(AF_INET6, "::FFFF:127.0.0.1", &sa.sin6_addr);
sa.sin6_port = htons((tui16)atoi(port));
if (bind(sck, (struct sockaddr*)&sa, sizeof(sa)) == 0)
{
return 0; return 0;
} }
#endif
#if defined(XRDP_ENABLE_IPV6) log_message(LOG_LEVEL_ERROR, "bind_loopback(%d, %s) failed; "
"IPv6 ::1 (errno=%d), IPv4 127.0.0.1 (errno=%d) and IPv6 ::FFFF:127.0.0.1 (errno=%d).",
sck, port, errno6, errno4, errno);
return -1;
}
/*****************************************************************************/ /*****************************************************************************/
/* returns error, zero is good */ /* Helper function for g_tcp_bind_address. */
/* Returns error, zero is good. */
static int static int
g_tcp_bind_flags(int sck, const char *port, const char *address, int flags) getaddrinfo_bind(int sck, const char *port, const char *address)
{ {
int res; int res;
int error; int error;
struct addrinfo hints; struct addrinfo hints;
struct addrinfo *list;
struct addrinfo *i; struct addrinfo *i;
res = -1; res = -1;
g_memset(&hints, 0, sizeof(hints)); g_memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_flags = flags; hints.ai_flags = 0;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP; hints.ai_protocol = IPPROTO_TCP;
error = getaddrinfo(NULL, port, &hints, &i); error = getaddrinfo(address, port, &hints, &list);
if (error == 0) if (error == 0)
{ {
i = list;
while ((i != 0) && (res < 0)) while ((i != 0) && (res < 0))
{
if (address_match(address, i))
{ {
res = bind(sck, i->ai_addr, i->ai_addrlen); res = bind(sck, i->ai_addr, i->ai_addrlen);
}
i = i->ai_next; i = i->ai_next;
} }
freeaddrinfo(list);
}
else
{
log_message(LOG_LEVEL_ERROR, "getaddrinfo error: %s", gai_strerror(error));
return -1;
} }
return res; return res;
} }
/*****************************************************************************/ /*****************************************************************************/
/* returns error, zero is good */ /* Binds a socket to a port. If no specified address the port will be bind */
/* to 'any', i.e. available on all network. */
/* For bind to local host, see valid address strings below. */
/* Returns error, zero is good. */
int int
g_tcp_bind(int sck, const char *port) g_tcp_bind_address(int sck, const char *port, const char *address)
{ {
int flags; int res;
flags = AI_ADDRCONFIG | AI_PASSIVE; if ((address == 0) ||
return g_tcp_bind_flags(sck, port, 0, flags); (address[0] == 0) ||
} (g_strcmp(address, "0.0.0.0") == 0) ||
#else (g_strcmp(address, "::") == 0))
int
g_tcp_bind(int sck, const char* port)
{ {
struct sockaddr_in s; return g_tcp_bind(sck, port);
memset(&s, 0, sizeof(struct sockaddr_in));
s.sin_family = AF_INET;
s.sin_port = htons((tui16)atoi(port));
s.sin_addr.s_addr = INADDR_ANY;
return bind(sck, (struct sockaddr*)&s, sizeof(struct sockaddr_in));
} }
#endif
/*****************************************************************************/ if ((g_strcmp(address, "127.0.0.1") == 0) ||
int (g_strcmp(address, "::1") == 0) ||
g_sck_local_bind(int sck, const char *port) (g_strcmp(address, "localhost") == 0))
{ {
#if defined(_WIN32) return bind_loopback(sck, port);
return -1;
#else
struct sockaddr_un s;
memset(&s, 0, sizeof(struct sockaddr_un));
s.sun_family = AF_UNIX;
strncpy(s.sun_path, port, sizeof(s.sun_path));
s.sun_path[sizeof(s.sun_path) - 1] = 0;
return bind(sck, (struct sockaddr *)&s, sizeof(struct sockaddr_un));
#endif
} }
#if defined(XRDP_ENABLE_IPV6) // Let getaddrinfo translate the address string...
/*****************************************************************************/ // IPv4: ddd.ddd.ddd.ddd
/* returns error, zero is good */ // IPv6: x:x:x:x:x:x:x:x%<interface>, or x::x:x:x:x%<interface>
int res = getaddrinfo_bind(sck, port, address);
g_tcp_bind_address(int sck, const char *port, const char *address) if (res != 0)
{ {
int flags; // If fail and it is an IPv4 address, try with the mapped address
struct in_addr a;
if ((inet_aton(address, &a) == 1) && (strlen(address) <= 15))
{
char sz[7+15+1];
sprintf(sz, "::FFFF:%s", address);
res = getaddrinfo_bind(sck, port, sz);
if (res == 0)
{
return 0;
}
}
flags = AI_ADDRCONFIG | AI_PASSIVE; log_message(LOG_LEVEL_ERROR, "g_tcp_bind_address(%d, %s, %s) Failed!",
return g_tcp_bind_flags(sck, port, address, flags); sck, port, address);
return -1;
}
return 0;
} }
#else #else
int int

Loading…
Cancel
Save