mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2026-02-16 10:30:59 +00:00
Rewrite STUN code to avoid requiring TCP and use multiple servers in parallel
This commit is contained in:
@@ -38,9 +38,9 @@ typedef struct _STUN_MESSAGE {
|
||||
int LiFindExternalAddressIP4(const char* stunServer, unsigned short stunPort, unsigned int* wanAddr)
|
||||
{
|
||||
SOCKET sock;
|
||||
struct sockaddr_storage stunAddr;
|
||||
struct sockaddr_in* stunAddrIn;
|
||||
SOCKADDR_LEN stunAddrLen;
|
||||
struct addrinfo* stunAddrs;
|
||||
struct addrinfo hints;
|
||||
char stunPortStr[6];
|
||||
int err;
|
||||
STUN_MESSAGE reqMsg;
|
||||
int i;
|
||||
@@ -58,13 +58,21 @@ int LiFindExternalAddressIP4(const char* stunServer, unsigned short stunPort, un
|
||||
return err;
|
||||
}
|
||||
|
||||
err = resolveHostName(stunServer, AF_INET, 3478, &stunAddr, &stunAddrLen);
|
||||
if (err != 0) {
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
|
||||
sprintf(stunPortStr, "%u", stunPort);
|
||||
err = getaddrinfo(stunServer, stunPortStr, &hints, &stunAddrs);
|
||||
if (err != 0 || stunAddrs == NULL) {
|
||||
Limelog("Failed to resolve STUN server: %d\n", err);
|
||||
stunAddrs = NULL;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
sock = bindUdpSocket(AF_INET, 2048);
|
||||
sock = bindUdpSocket(hints.ai_family, 2048);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
err = LastSocketFail();
|
||||
Limelog("Failed to connect to STUN server: %d\n", err);
|
||||
@@ -79,29 +87,29 @@ int LiFindExternalAddressIP4(const char* stunServer, unsigned short stunPort, un
|
||||
}
|
||||
|
||||
bytesRead = SOCKET_ERROR;
|
||||
for (i = 0; i < STUN_RECV_TIMEOUT_SEC * 1000 / UDP_RECV_POLL_TIMEOUT_MS; i++) {
|
||||
// Retransmit the request every second until the timeout elapses
|
||||
for (i = 0; i < STUN_RECV_TIMEOUT_SEC * 1000 / UDP_RECV_POLL_TIMEOUT_MS && bytesRead <= 0; i++) {
|
||||
// Retransmit the request every second until the timeout elapses or we get a response
|
||||
if (i % (1000 / UDP_RECV_POLL_TIMEOUT_MS) == 0) {
|
||||
stunAddrIn = (struct sockaddr_in*)&stunAddr;
|
||||
stunAddrIn->sin_port = htons(stunPort);
|
||||
err = (int)sendto(sock, (char *)&reqMsg, sizeof(reqMsg), 0,
|
||||
(struct sockaddr*)stunAddrIn, stunAddrLen);
|
||||
if (err == SOCKET_ERROR) {
|
||||
err = LastSocketFail();
|
||||
Limelog("Failed to send STUN binding request: %d\n", err);
|
||||
closeSocket(sock);
|
||||
goto Exit;
|
||||
struct addrinfo *current;
|
||||
|
||||
// Send a request to each resolved address but stop if we get a response
|
||||
for (current = stunAddrs; current != NULL && bytesRead <= 0; current = current->ai_next) {
|
||||
err = (int)sendto(sock, (char *)&reqMsg, sizeof(reqMsg), 0, current->ai_addr, current->ai_addrlen);
|
||||
if (err == SOCKET_ERROR) {
|
||||
err = LastSocketFail();
|
||||
Limelog("Failed to send STUN binding request: %d\n", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Wait UDP_RECV_POLL_TIMEOUT_MS before moving on to the next server to
|
||||
// avoid having to spam the other STUN servers if we find a working one.
|
||||
bytesRead = recvUdpSocket(sock, resp.buf, sizeof(resp.buf), 1);
|
||||
}
|
||||
}
|
||||
|
||||
// This waits in UDP_RECV_POLL_TIMEOUT_MS increments
|
||||
bytesRead = recvUdpSocket(sock, resp.buf, sizeof(resp.buf), 1);
|
||||
if (bytesRead == 0) {
|
||||
// Timeout - continue looping
|
||||
continue;
|
||||
else {
|
||||
// This waits in UDP_RECV_POLL_TIMEOUT_MS increments
|
||||
bytesRead = recvUdpSocket(sock, resp.buf, sizeof(resp.buf), 1);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
closeSocket(sock);
|
||||
@@ -176,6 +184,10 @@ int LiFindExternalAddressIP4(const char* stunServer, unsigned short stunPort, un
|
||||
err = -6;
|
||||
|
||||
Exit:
|
||||
if (stunAddrs != NULL) {
|
||||
freeaddrinfo(stunAddrs);
|
||||
}
|
||||
|
||||
cleanupPlatformSockets();
|
||||
return err;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user