Skip to content

Support Dual-Stack STUN Servers and IPv6 srflx Candidates #2115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: release-v1.12.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMake/Dependencies/libwebsockets-CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ ExternalProject_Add(project_libwebsockets
-DLWS_EXT_PTHREAD_LIBRARIES=${LWS_EXT_PTHREAD_LIBRARIES}
-DLWS_OPENSSL_INCLUDE_DIRS=${LWS_OPENSSL_INCLUDE_DIRS}
-DLWS_OPENSSL_LIBRARIES=${LWS_OPENSSL_LIBRARIES}
-DLWS_IPV6=1
BUILD_ALWAYS TRUE
TEST_COMMAND ""
)
54 changes: 38 additions & 16 deletions src/source/Ice/IceAgent.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ STATUS createIceAgent(PCHAR username, PCHAR password, PIceAgentCallbacks pIceAge
if (doStatCalcs) {
CHK(NULL != (pIceAgent->pRtcIceServerDiagnostics[i] = (PRtcIceServerDiagnostics) MEMCALLOC(1, SIZEOF(RtcIceServerDiagnostics))),
STATUS_NOT_ENOUGH_MEMORY);
pIceAgent->pRtcIceServerDiagnostics[i]->port = (INT32) getInt16(pIceAgent->iceServers[i].ipAddress.port);
pIceAgent->pRtcIceServerDiagnostics[i]->port = (INT32) getInt16(pIceAgent->iceServers[i].ipAddresses.ipv4Address.port);
switch (pIceAgent->iceServers[pIceAgent->iceServersCount].transport) {
case KVS_SOCKET_PROTOCOL_UDP:
STRCPY(pIceAgent->pRtcIceServerDiagnostics[i]->protocol, ICE_TRANSPORT_TYPE_UDP);
Expand Down Expand Up @@ -1458,14 +1458,34 @@ STATUS iceAgentSendSrflxCandidateRequest(PIceAgent pIceAgent)
switch (pCandidate->iceCandidateType) {
case ICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
pIceServer = &(pIceAgent->iceServers[pCandidate->iceServerIndex]);
if (pIceServer->ipAddress.family == pCandidate->ipAddress.family) {
if (pIceServer->ipAddresses.ipv4Address.family == pCandidate->ipAddress.family) {
// update transactionId
CHK_STATUS(
iceUtilsGenerateTransactionId(pBindingRequest->header.transactionId, ARRAY_SIZE(pBindingRequest->header.transactionId)));

transactionIdStoreInsert(pIceAgent->pStunBindingRequestTransactionIdStore, pBindingRequest->header.transactionId);
checkSum = COMPUTE_CRC32(pBindingRequest->header.transactionId, ARRAY_SIZE(pBindingRequest->header.transactionId));
CHK_STATUS(iceAgentSendStunPacket(pBindingRequest, NULL, 0, pIceAgent, pCandidate, &pIceServer->ipAddress));

DLOGD("Sending STUN binding request to IPv4 STUN server: %u:%u", pIceServer->ipAddresses.ipv4Address.address,
pIceServer->ipAddresses.ipv4Address.port);

CHK_STATUS(iceAgentSendStunPacket(pBindingRequest, NULL, 0, pIceAgent, pCandidate, &pIceServer->ipAddresses.ipv4Address));
if (pIceAgent->pRtcIceServerDiagnostics[pCandidate->iceServerIndex] != NULL) {
pIceAgent->pRtcIceServerDiagnostics[pCandidate->iceServerIndex]->totalRequestsSent++;
CHK_STATUS(hashTableUpsert(pIceAgent->requestTimestampDiagnostics, checkSum, GETTIME()));
}
} else if (pIceServer->ipAddresses.ipv6Address.family == pCandidate->ipAddress.family) {
// update transactionId
CHK_STATUS(
iceUtilsGenerateTransactionId(pBindingRequest->header.transactionId, ARRAY_SIZE(pBindingRequest->header.transactionId)));

transactionIdStoreInsert(pIceAgent->pStunBindingRequestTransactionIdStore, pBindingRequest->header.transactionId);
checkSum = COMPUTE_CRC32(pBindingRequest->header.transactionId, ARRAY_SIZE(pBindingRequest->header.transactionId));

DLOGD("Sending STUN binding request to IPv6 STUN server: %u:%u", pIceServer->ipAddresses.ipv6Address.address,
pIceServer->ipAddresses.ipv6Address.port);

CHK_STATUS(iceAgentSendStunPacket(pBindingRequest, NULL, 0, pIceAgent, pCandidate, &pIceServer->ipAddresses.ipv6Address));
if (pIceAgent->pRtcIceServerDiagnostics[pCandidate->iceServerIndex] != NULL) {
pIceAgent->pRtcIceServerDiagnostics[pCandidate->iceServerIndex]->totalRequestsSent++;
CHK_STATUS(hashTableUpsert(pIceAgent->requestTimestampDiagnostics, checkSum, GETTIME()));
Expand Down Expand Up @@ -1762,7 +1782,9 @@ STATUS iceAgentInitSrflxCandidate(PIceAgent pIceAgent)
if (pCandidate->iceCandidateType == ICE_CANDIDATE_TYPE_HOST) {
for (j = 0; j < pIceAgent->iceServersCount; j++) {
pIceServer = &pIceAgent->iceServers[j];
if (!pIceServer->isTurn && pIceServer->ipAddress.family == pCandidate->ipAddress.family) {
if (!pIceServer->isTurn &&
(pIceServer->ipAddresses.ipv4Address.family == pCandidate->ipAddress.family ||
pIceServer->ipAddresses.ipv6Address.family == pCandidate->ipAddress.family)) {
CHK((pNewCandidate = (PIceCandidate) MEMCALLOC(1, SIZEOF(IceCandidate))) != NULL, STATUS_NOT_ENOUGH_MEMORY);
generateJSONSafeString(pNewCandidate->id, ARRAY_SIZE(pNewCandidate->id));
pNewCandidate->isRemote = FALSE;
Expand Down Expand Up @@ -1792,20 +1814,20 @@ STATUS iceAgentInitSrflxCandidate(PIceAgent pIceAgent)
// Create and start the connection listener outside of the locks
for (j = 0; j < srflxCount; j++) {
pCandidate = srflxCandidates[j];
// TODO: IPv6 STUN is not supported at the moment. Remove this check if the support is added in the future
if (IS_IPV4_ADDR(&(pCandidate->ipAddress))) {
// open up a new socket at host candidate's ip address for server reflex candidate.
// the new port will be stored in pNewCandidate->ipAddress.port. And the Ip address will later be updated
// with the correct ip address once the STUN response is received.
CHK_STATUS(createSocketConnection(pCandidate->ipAddress.family, KVS_SOCKET_PROTOCOL_UDP, &pCandidate->ipAddress, NULL, (UINT64) pIceAgent,
incomingDataHandler, pIceAgent->kvsRtcConfiguration.sendBufSize, &pCandidate->pSocketConnection));
ATOMIC_STORE_BOOL(&pCandidate->pSocketConnection->receiveData, TRUE);
// connectionListener will free the pSocketConnection at the end.
CHK_STATUS(connectionListenerAddConnection(pIceAgent->pConnectionListener, pCandidate->pSocketConnection));

DLOGI("Initializing an IPv4 STUN candidate...");
} else {
DLOGW("IPv6 candidate detected, ignoring....");
DLOGI("Initializing an IPv6 STUN candidate...");
}

// open up a new socket at host candidate's ip address for server reflex candidate.
// the new port will be stored in pNewCandidate->ipAddress.port. And the Ip address will later be updated
// with the correct ip address once the STUN response is received.
CHK_STATUS(createSocketConnection(pCandidate->ipAddress.family, KVS_SOCKET_PROTOCOL_UDP, &pCandidate->ipAddress, NULL, (UINT64) pIceAgent,
incomingDataHandler, pIceAgent->kvsRtcConfiguration.sendBufSize, &pCandidate->pSocketConnection));
ATOMIC_STORE_BOOL(&pCandidate->pSocketConnection->receiveData, TRUE);
// connectionListener will free the pSocketConnection at the end.
CHK_STATUS(connectionListenerAddConnection(pIceAgent->pConnectionListener, pCandidate->pSocketConnection));
}

CleanUp:
Expand Down Expand Up @@ -1898,7 +1920,7 @@ STATUS iceAgentInitRelayCandidate(PIceAgent pIceAgent, UINT32 iceServerIndex, KV
// open up a new socket without binding to any host address. The candidate Ip address will later be updated
// with the correct relay ip address once the Allocation success response is received. Relay candidate's socket is managed
// by TurnConnection struct.
CHK_STATUS(createSocketConnection(KVS_IP_FAMILY_TYPE_IPV4, protocol, NULL, &pIceAgent->iceServers[iceServerIndex].ipAddress,
CHK_STATUS(createSocketConnection(KVS_IP_FAMILY_TYPE_IPV4, protocol, NULL, &pIceAgent->iceServers[iceServerIndex].ipAddresses.ipv4Address,
(UINT64) pNewCandidate, incomingRelayedDataHandler, pIceAgent->kvsRtcConfiguration.sendBufSize,
&pNewCandidate->pSocketConnection));
// connectionListener will free the pSocketConnection at the end.
Expand Down
33 changes: 23 additions & 10 deletions src/source/Ice/IceUtils.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,16 +168,18 @@ STATUS iceUtilsSendStunPacket(PStunPacket pStunPacket, PBYTE password, UINT32 pa
UINT32 stunPacketSize = STUN_PACKET_ALLOCATION_SIZE;
BYTE stunPacketBuffer[STUN_PACKET_ALLOCATION_SIZE];

CHAR ipAddrStr[KVS_IP_ADDRESS_STRING_BUFFER_LEN];

CHK_STATUS(getIpAddrStr(pDest, ipAddrStr, ARRAY_SIZE(ipAddrStr)));

CHK_STATUS(iceUtilsPackageStunPacket(pStunPacket, password, passwordLen, stunPacketBuffer, &stunPacketSize));
CHK(pDest != NULL, STATUS_NULL_ARG);
switch (pStunPacket->header.stunMessageType) {
case STUN_PACKET_TYPE_BINDING_REQUEST:
DLOGD("Sending BINDING_REQUEST to ip:%u.%u.%u.%u, port:%u", pDest->address[0], pDest->address[1], pDest->address[2], pDest->address[3],
(UINT16) getInt16(pDest->port));
DLOGD("Sending BINDING_REQUEST to ip:%s, port:%u", ipAddrStr, (UINT16) getInt16(pDest->port));
break;
case STUN_PACKET_TYPE_BINDING_RESPONSE_SUCCESS:
DLOGD("Sending BINDING_RESPONSE_SUCCESS to ip:%u.%u.%u.%u, port:%u", pDest->address[0], pDest->address[1], pDest->address[2],
pDest->address[3], (UINT16) getInt16(pDest->port));
DLOGD("Sending BINDING_RESPONSE_SUCCESS to ip:%s, port:%u", ipAddrStr, (UINT16) getInt16(pDest->port));
break;
default:
break;
Expand Down Expand Up @@ -221,7 +223,8 @@ STATUS parseIceServer(PIceServer pIceServer, PCHAR url, PCHAR username, PCHAR cr
STATUS retStatus = STATUS_SUCCESS;
PCHAR separator = NULL, urlNoPrefix = NULL, paramStart = NULL;
UINT32 port = ICE_STUN_DEFAULT_PORT;
CHAR addressResolved[KVS_IP_ADDRESS_STRING_BUFFER_LEN + 1] = {'\0'};
CHAR addressResolvedIPv4[KVS_IP_ADDRESS_STRING_BUFFER_LEN + 1] = {'\0'};
CHAR addressResolvedIPv6[KVS_IP_ADDRESS_STRING_BUFFER_LEN + 1] = {'\0'};

// username and credential is only mandatory for turn server
CHK(url != NULL && pIceServer != NULL, STATUS_NULL_ARG);
Expand Down Expand Up @@ -264,7 +267,7 @@ STATUS parseIceServer(PIceServer pIceServer, PCHAR url, PCHAR username, PCHAR cr
}

if (pIceServer->setIpFn != NULL) {
retStatus = pIceServer->setIpFn(0, pIceServer->url, &pIceServer->ipAddress);
retStatus = pIceServer->setIpFn(0, pIceServer->url, &pIceServer->ipAddresses);
}

// Adding a NULL_ARG check specifically to cover for the case where early STUN
Expand All @@ -274,12 +277,22 @@ STATUS parseIceServer(PIceServer pIceServer, PCHAR url, PCHAR username, PCHAR cr
// Reset the retStatus to ensure the appropriate status code is returned from
// getIpWithHostName
retStatus = STATUS_SUCCESS;
CHK_STATUS(getIpWithHostName(pIceServer->url, &pIceServer->ipAddress));
CHK_STATUS(getIpWithHostName(pIceServer->url, &pIceServer->ipAddresses));
}

pIceServer->ipAddress.port = (UINT16) getInt16((INT16) port);
getIpAddrStr(&pIceServer->ipAddress, addressResolved, ARRAY_SIZE(addressResolved));
DLOGP("ICE Server address for %s: %s", pIceServer->url, addressResolved);
if (pIceServer->ipAddresses.ipv4Address.family != KVS_IP_FAMILY_TYPE_NOT_SET) {
pIceServer->ipAddresses.ipv4Address.port = (UINT16) getInt16((INT16) port);
CHK_STATUS(getIpAddrStr(&pIceServer->ipAddresses.ipv4Address, addressResolvedIPv4, ARRAY_SIZE(addressResolvedIPv4)));
DLOGP("Resolved ICE Server IPv4 address for %s: %s", pIceServer->url, addressResolvedIPv4);
DLOGP("...with port: %u", pIceServer->ipAddresses.ipv4Address.port);
}

if (pIceServer->ipAddresses.ipv6Address.family != KVS_IP_FAMILY_TYPE_NOT_SET) {
pIceServer->ipAddresses.ipv6Address.port = (UINT16) getInt16((INT16) port);
CHK_STATUS(getIpAddrStr(&pIceServer->ipAddresses.ipv6Address, addressResolvedIPv6, ARRAY_SIZE(addressResolvedIPv6)));
DLOGP("Resolved ICE Server IPv6 address for %s: %s", pIceServer->url, addressResolvedIPv6);
DLOGP("...with port: %u", pIceServer->ipAddresses.ipv6Address.port);
}

CleanUp:

Expand Down
2 changes: 1 addition & 1 deletion src/source/Ice/IceUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ typedef struct {
BOOL isTurn;
BOOL isSecure;
CHAR url[MAX_ICE_CONFIG_URI_LEN + 1];
KvsIpAddress ipAddress;
DualKvsIpAddresses ipAddresses;
CHAR username[MAX_ICE_CONFIG_USER_NAME_LEN + 1];
CHAR credential[MAX_ICE_CONFIG_CREDENTIAL_LEN + 1];
KVS_SOCKET_PROTOCOL transport;
Expand Down
20 changes: 10 additions & 10 deletions src/source/Ice/NatBehaviorDiscovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ STATUS discoverNatMappingBehavior(PIceServer pStunServer, PNatTestData data, PSo

/* execute test I */
DLOGD("Running mapping behavior test I. Send binding request");
CHK_STATUS(executeNatTest(bindingRequest, &pStunServer->ipAddress, pSocketConnection, testIndex++, data, &bindingResponse));
CHK_STATUS(executeNatTest(bindingRequest, &pStunServer->ipAddresses.ipv4Address, pSocketConnection, testIndex++, data, &bindingResponse));

if (bindingResponse == NULL) {
natMappingBehavior = NAT_BEHAVIOR_NO_UDP_CONNECTIVITY;
Expand All @@ -144,7 +144,7 @@ STATUS discoverNatMappingBehavior(PIceServer pStunServer, PNatTestData data, PSo
/* execute test II */
DLOGD("Running mapping behavior test II. Send binding request to alternate address but primary port");
testDestAddress = otherAddress;
testDestAddress.port = pStunServer->ipAddress.port;
testDestAddress.port = pStunServer->ipAddresses.ipv4Address.port;
CHK_STATUS(executeNatTest(bindingRequest, &testDestAddress, pSocketConnection, testIndex++, data, &bindingResponse));
CHK_ERR(bindingResponse != NULL, retStatus, "Expect to receive binding response");

Expand Down Expand Up @@ -208,7 +208,7 @@ STATUS discoverNatFilteringBehavior(PIceServer pStunServer, PNatTestData data, P

/* execute test I */
DLOGD("Running filtering behavior test I. Send binding request");
CHK_STATUS(executeNatTest(bindingRequest, &pStunServer->ipAddress, pSocketConnection, testIndex++, data, &bindingResponse));
CHK_STATUS(executeNatTest(bindingRequest, &pStunServer->ipAddresses.ipv4Address, pSocketConnection, testIndex++, data, &bindingResponse));
if (bindingResponse == NULL) {
natFilteringBehavior = NAT_BEHAVIOR_NO_UDP_CONNECTIVITY;
CHK(FALSE, retStatus);
Expand All @@ -219,7 +219,7 @@ STATUS discoverNatFilteringBehavior(PIceServer pStunServer, PNatTestData data, P
CHK_STATUS(appendStunChangeRequestAttribute(bindingRequest,
STUN_ATTRIBUTE_CHANGE_REQUEST_FLAG_CHANGE_IP | STUN_ATTRIBUTE_CHANGE_REQUEST_FLAG_CHANGE_PORT));

CHK_STATUS(executeNatTest(bindingRequest, &pStunServer->ipAddress, pSocketConnection, testIndex++, data, &bindingResponse));
CHK_STATUS(executeNatTest(bindingRequest, &pStunServer->ipAddresses.ipv4Address, pSocketConnection, testIndex++, data, &bindingResponse));
if (bindingResponse != NULL) {
natFilteringBehavior = NAT_BEHAVIOR_ENDPOINT_INDEPENDENT;
CHK(FALSE, retStatus);
Expand All @@ -230,7 +230,7 @@ STATUS discoverNatFilteringBehavior(PIceServer pStunServer, PNatTestData data, P
CHK_STATUS(getStunAttribute(bindingRequest, STUN_ATTRIBUTE_TYPE_CHANGE_REQUEST, (PStunAttributeHeader*) &pStunAttributeChangeRequest));
pStunAttributeChangeRequest->changeFlag = STUN_ATTRIBUTE_CHANGE_REQUEST_FLAG_CHANGE_PORT;

CHK_STATUS(executeNatTest(bindingRequest, &pStunServer->ipAddress, pSocketConnection, testIndex++, data, &bindingResponse));
CHK_STATUS(executeNatTest(bindingRequest, &pStunServer->ipAddresses.ipv4Address, pSocketConnection, testIndex++, data, &bindingResponse));

if (bindingResponse != NULL) {
natFilteringBehavior = NAT_BEHAVIOR_ADDRESS_DEPENDENT;
Expand Down Expand Up @@ -291,15 +291,15 @@ STATUS discoverNatBehavior(PCHAR stunServer, NAT_BEHAVIOR* pNatMappingBehavior,

/* use the first usable local interface to create socket */
for (i = 0; i < localNetworkInterfaceCount; ++i) {
if (localNetworkInterfaces[i].family == iceServerStun.ipAddress.family) {
if (localNetworkInterfaces[i].family == iceServerStun.ipAddresses.ipv4Address.family) {
pSelectedLocalInterface = &localNetworkInterfaces[i];
break;
}
}
CHK_WARN(pSelectedLocalInterface != NULL, retStatus, "No usable local interface");

CHK_STATUS(createSocketConnection(iceServerStun.ipAddress.family, KVS_SOCKET_PROTOCOL_UDP, pSelectedLocalInterface, NULL, (UINT64) &customData,
natTestIncomingDataHandler, 0, &pSocketConnection));
CHK_STATUS(createSocketConnection(iceServerStun.ipAddresses.ipv4Address.family, KVS_SOCKET_PROTOCOL_UDP, pSelectedLocalInterface, NULL,
(UINT64) &customData, natTestIncomingDataHandler, 0, &pSocketConnection));
ATOMIC_STORE_BOOL(&pSocketConnection->receiveData, TRUE);

CHK_STATUS(createConnectionListener(&pConnectionListener));
Expand All @@ -322,8 +322,8 @@ STATUS discoverNatBehavior(PCHAR stunServer, NAT_BEHAVIOR* pNatMappingBehavior,

CHK_STATUS(connectionListenerRemoveAllConnection(pConnectionListener));
freeSocketConnection(&pSocketConnection);
CHK_STATUS(createSocketConnection(iceServerStun.ipAddress.family, KVS_SOCKET_PROTOCOL_UDP, pSelectedLocalInterface, NULL, (UINT64) &customData,
natTestIncomingDataHandler, 0, &pSocketConnection));
CHK_STATUS(createSocketConnection(iceServerStun.ipAddresses.ipv4Address.family, KVS_SOCKET_PROTOCOL_UDP, pSelectedLocalInterface, NULL,
(UINT64) &customData, natTestIncomingDataHandler, 0, &pSocketConnection));
ATOMIC_STORE_BOOL(&pSocketConnection->receiveData, TRUE);
CHK_STATUS(connectionListenerAddConnection(pConnectionListener, pSocketConnection));

Expand Down
Loading
Loading