Test each address returned in the getaddrinfo() call and stop blindly preferring IPv4

Fixes NAT64 on iOS 12 and probably other obscure configurations
This commit is contained in:
Cameron Gutman
2018-11-16 00:37:38 -08:00
parent d9210a6a9a
commit 396b02a94d
4 changed files with 41 additions and 35 deletions
+1 -1
View File
@@ -213,7 +213,7 @@ int LiStartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION stre
Limelog("Resolving host name..."); Limelog("Resolving host name...");
ListenerCallbacks.stageStarting(STAGE_NAME_RESOLUTION); ListenerCallbacks.stageStarting(STAGE_NAME_RESOLUTION);
err = resolveHostName(serverInfo->address, &RemoteAddr, &RemoteAddrLen); err = resolveHostName(serverInfo->address, AF_UNSPEC, 47984, &RemoteAddr, &RemoteAddrLen);
if (err != 0) { if (err != 0) {
Limelog("failed: %d\n", err); Limelog("failed: %d\n", err);
ListenerCallbacks.stageFailed(STAGE_NAME_RESOLUTION, err); ListenerCallbacks.stageFailed(STAGE_NAME_RESOLUTION, err);
+38 -26
View File
@@ -1,6 +1,8 @@
#include "PlatformSockets.h" #include "PlatformSockets.h"
#include "Limelight-internal.h" #include "Limelight-internal.h"
#define TEST_PORT_TIMEOUT_SEC 3
#define RCV_BUFFER_SIZE_MIN 32767 #define RCV_BUFFER_SIZE_MIN 32767
#define RCV_BUFFER_SIZE_STEP 16384 #define RCV_BUFFER_SIZE_STEP 16384
@@ -282,43 +284,53 @@ int enableNoDelay(SOCKET s) {
return 0; return 0;
} }
int resolveHostName(const char* host, struct sockaddr_storage* addr, SOCKADDR_LEN* addrLen) int resolveHostName(const char* host, int family, int tcpTestPort, struct sockaddr_storage* addr, SOCKADDR_LEN* addrLen)
{ {
#ifndef __vita__ #ifndef __vita__
struct addrinfo hints, *res; struct addrinfo hints, *res, *currentAddr;
int err; int err;
// We must first try IPv4-only because GFE doesn't listen on IPv6,
// so we'll only want to use an IPv6 address if it's the only address we have.
// For NAT64 networks, the IPv4 address resolution will fail but the IPv6 address
// will give us working connectivity to the host. All other networks will use IPv4
// addresses.
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; hints.ai_family = family;
hints.ai_flags = AI_ADDRCONFIG; hints.ai_flags = AI_ADDRCONFIG;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
err = getaddrinfo(host, NULL, &hints, &res); err = getaddrinfo(host, NULL, &hints, &res);
if (err != 0 || res == NULL) { if (err != 0) {
memset(&hints, 0, sizeof(hints)); Limelog("getaddrinfo(%s) failed: %d\n", host, err);
hints.ai_family = AF_UNSPEC; return err;
hints.ai_flags = AI_ADDRCONFIG; }
err = getaddrinfo(host, NULL, &hints, &res); else if (res == NULL) {
if (err != 0) { Limelog("getaddrinfo(%s) returned success without addresses\n", host);
Limelog("getaddrinfo() failed: %d\n", err); return -1;
return err; }
}
for (currentAddr = res; currentAddr != NULL; currentAddr = currentAddr->ai_next) {
if (res == NULL) { // Use the test port to ensure this address is working
Limelog("getaddrinfo() returned success without addresses\n"); if (tcpTestPort != 0) {
return -1; SOCKET testSocket = connectTcpSocket((struct sockaddr_storage*)currentAddr->ai_addr,
currentAddr->ai_addrlen,
tcpTestPort,
TEST_PORT_TIMEOUT_SEC);
if (testSocket == INVALID_SOCKET) {
// Try the next address
continue;
}
else {
closeSocket(testSocket);
}
} }
memcpy(addr, currentAddr->ai_addr, currentAddr->ai_addrlen);
*addrLen = currentAddr->ai_addrlen;
freeaddrinfo(res);
return 0;
} }
// Use the first address in the list Limelog("No working addresses found for host: %s\n", host);
memcpy(addr, res->ai_addr, res->ai_addrlen);
*addrLen = res->ai_addrlen;
freeaddrinfo(res); freeaddrinfo(res);
return 0; return -1;
#else #else
struct hostent *phost = gethostbyname(host); struct hostent *phost = gethostbyname(host);
if (!phost) { if (!phost) {
+1 -1
View File
@@ -72,7 +72,7 @@ typedef socklen_t SOCKADDR_LEN;
#define URLSAFESTRING_LEN (INET6_ADDRSTRLEN+2) #define URLSAFESTRING_LEN (INET6_ADDRSTRLEN+2)
void addrToUrlSafeString(struct sockaddr_storage* addr, char* string); void addrToUrlSafeString(struct sockaddr_storage* addr, char* string);
int resolveHostName(const char* host, struct sockaddr_storage* addr, SOCKADDR_LEN* addrLen); int resolveHostName(const char* host, int family, int tcpTestPort, struct sockaddr_storage* addr, SOCKADDR_LEN* addrLen);
SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, unsigned short port, int timeoutSec); SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, unsigned short port, int timeoutSec);
SOCKET bindUdpSocket(int addrfamily, int bufferSize); SOCKET bindUdpSocket(int addrfamily, int bufferSize);
int enableNoDelay(SOCKET s); int enableNoDelay(SOCKET s);
+1 -7
View File
@@ -52,18 +52,12 @@ int LiFindExternalAddressIP4(const char* stunServer, unsigned short stunPort, un
char buf[1024]; char buf[1024];
} resp; } resp;
err = resolveHostName(stunServer, &stunAddr, &stunAddrLen); err = resolveHostName(stunServer, AF_INET, 0, &stunAddr, &stunAddrLen);
if (err != 0) { if (err != 0) {
Limelog("Failed to resolve STUN server: %d\n", err); Limelog("Failed to resolve STUN server: %d\n", err);
return err; return err;
} }
// We must use IPv4 to talk to the STUN server to get our IPv4 address back
if (stunAddr.ss_family != AF_INET) {
Limelog("STUN server was not reachable over IPv4\n");
return -1;
}
sock = bindUdpSocket(AF_INET, 2048); sock = bindUdpSocket(AF_INET, 2048);
if (sock == INVALID_SOCKET) { if (sock == INVALID_SOCKET) {
err = LastSocketFail(); err = LastSocketFail();