diff --git a/.travis.yml b/.travis.yml index f2a4f17bf8..94b734d37e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,6 +58,9 @@ matrix: - name: "Linux GCC Code Coverage" os: linux compiler: gcc + before_install: + # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. + - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' before_script: - mkdir build && cd build && cmake .. -DCODE_COVERAGE=TRUE -DBUILD_TEST=TRUE after_success: @@ -71,6 +74,9 @@ matrix: env: - ASAN_OPTIONS=detect_odr_violation=0:detect_leaks=1 - LSAN_OPTIONS=suppressions=../tst/suppressions/LSAN.supp + before_install: + # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. + - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' before_script: mkdir build && cd build && cmake .. -DBUILD_TEST=TRUE -DADDRESS_SANITIZER=TRUE # UndefinedBehaviorSanitizer @@ -78,11 +84,17 @@ matrix: os: linux compiler: clang env: UBSAN_OPTIONS=halt_on_error=1 + before_install: + # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. + - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' before_script: mkdir build && cd build && cmake .. -DBUILD_TEST=TRUE -DUNDEFINED_BEHAVIOR_SANITIZER=TRUE # MemorySanitizer - name: "Linux Clang MemorySanitizer" before_install: + # TODO: Remove the following 2 lines. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. + - echo '{"ipv6":true,"fixed-cidr-v6":"2001:db8:1::/64"}' | sudo tee /etc/docker/daemon.json + - sudo service docker restart - mkdir build - docker run -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -w /src/build -dit --name msan-tester -v $(pwd):/src seaduboi/kvs-msan-tester - msan-tester() { docker exec -it msan-tester "$@"; } @@ -97,12 +109,17 @@ matrix: os: linux compiler: clang env: TSAN_OPTIONS=halt_on_error=1:suppressions=../tst/suppressions/TSAN.supp + before_install: + # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. + - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' before_script: mkdir build && cd build && cmake .. -DBUILD_TEST=TRUE -DTHREAD_SANITIZER=TRUE # Old Version GCC 4.4 - name: "Linux GCC 4.4 Build" os: linux before_install: + # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. + - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo apt-get -q update - sudo apt-get -y install gcc-4.4 @@ -113,6 +130,9 @@ matrix: # Static Build - name: "Static Build" before_install: + # TODO: Remove the following 2 lines. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. + - echo '{"ipv6":true,"fixed-cidr-v6":"2001:db8:1::/64"}' | sudo tee /etc/docker/daemon.json + - sudo service docker restart - mkdir build - docker run -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -w /src/build -dit --name alpine -v $(pwd):/src alpine - alpine() { docker exec -it alpine "$@"; } @@ -135,6 +155,9 @@ matrix: - g++-arm-linux-gnueabi - binutils-arm-linux-gnueabi compiler: gcc + before_install: + # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. + - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' before_script: - export CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ - mkdir build && cd build diff --git a/src/source/Ice/IceAgent.c b/src/source/Ice/IceAgent.c index 42f9551405..1811b62270 100644 --- a/src/source/Ice/IceAgent.c +++ b/src/source/Ice/IceAgent.c @@ -254,8 +254,10 @@ STATUS iceAgentAddRemoteCandidate(PIceAgent pIceAgent, PCHAR pIceCandidateString BOOL locked = FALSE; PIceCandidate pIceCandidate = NULL, pDuplicatedIceCandidate = NULL; PCHAR curr, tail, next; - UINT32 tokenLen, portValue, remoteCandidateCount, i; - BOOL foundIpAndPort = FALSE, freeIceCandidateIfFail = TRUE; + UINT32 tokenLen, portValue, remoteCandidateCount, i, len; + BOOL freeIceCandidateIfFail = TRUE; + BOOL foundIp = FALSE, foundPort = FALSE; + CHAR ipBuf[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; KvsIpAddress candidateIpAddr; CHK(pIceAgent != NULL && pIceCandidateString != NULL, STATUS_NULL_ARG); @@ -271,26 +273,32 @@ STATUS iceAgentAddRemoteCandidate(PIceAgent pIceAgent, PCHAR pIceCandidateString curr = pIceCandidateString; tail = pIceCandidateString + STRLEN(pIceCandidateString); - while ((next = STRNCHR(curr, tail - curr, ' ')) != NULL && !foundIpAndPort) { + while ((next = STRNCHR(curr, tail - curr, ' ')) != NULL && !(foundIp && foundPort)) { tokenLen = (UINT32) (next - curr); CHK(STRNCMPI("tcp", curr, tokenLen) != 0, STATUS_ICE_CANDIDATE_STRING_IS_TCP); - if (candidateIpAddr.address[0] != 0) { + if (foundIp) { CHK_STATUS(STRTOUI32(curr, curr + tokenLen, 10, &portValue)); candidateIpAddr.port = htons(portValue); - candidateIpAddr.family = KVS_IP_FAMILY_TYPE_IPV4; - } else if (STRNCHR(curr, tokenLen, '.') != NULL) { - CHK(tokenLen <= KVS_MAX_IPV4_ADDRESS_STRING_LEN, STATUS_ICE_CANDIDATE_STRING_INVALID_IP); // IPv4 is 15 characters at most - CHK_STATUS(populateIpFromString(&candidateIpAddr, curr)); + foundPort = TRUE; + } else { + len = MIN(next - curr, KVS_IP_ADDRESS_STRING_BUFFER_LEN - 1); + STRNCPY(ipBuf, curr, len); + ipBuf[len] = '\0'; + + if ((foundIp = inet_pton(AF_INET, ipBuf, candidateIpAddr.address) == 1 ? TRUE : FALSE)) { + candidateIpAddr.family = KVS_IP_FAMILY_TYPE_IPV4; + } else if ((foundIp = inet_pton(AF_INET6, ipBuf, candidateIpAddr.address) == 1 ? TRUE : FALSE)) { + candidateIpAddr.family = KVS_IP_FAMILY_TYPE_IPV6; + } } curr = next + 1; - foundIpAndPort = (candidateIpAddr.port != 0) && (candidateIpAddr.address[0] != 0); } - CHK(candidateIpAddr.port != 0, STATUS_ICE_CANDIDATE_STRING_MISSING_PORT); - CHK(candidateIpAddr.address[0] != 0, STATUS_ICE_CANDIDATE_STRING_MISSING_IP); + CHK(foundPort, STATUS_ICE_CANDIDATE_STRING_MISSING_PORT); + CHK(foundIp, STATUS_ICE_CANDIDATE_STRING_MISSING_IP); CHK_STATUS(findCandidateWithIp(&candidateIpAddr, pIceAgent->remoteCandidates, &pDuplicatedIceCandidate)); CHK(pDuplicatedIceCandidate == NULL, retStatus); @@ -341,8 +349,7 @@ STATUS iceAgentInitHostCandidate(PIceAgent pIceAgent) // make sure pIceAgent->localCandidates has no duplicates CHK_STATUS(findCandidateWithIp(pIpAddress, pIceAgent->localCandidates, &pDuplicatedIceCandidate)); - if (pIpAddress->family == KVS_IP_FAMILY_TYPE_IPV4 && // Disable ipv6 gathering for now - pDuplicatedIceCandidate == NULL && + if (pDuplicatedIceCandidate == NULL && STATUS_SUCCEEDED(createSocketConnection(pIpAddress, NULL, KVS_SOCKET_PROTOCOL_UDP, (UINT64) pIceAgent, incomingDataHandler, pIceAgent->kvsRtcConfiguration.sendBufSize, &pSocketConnection))) { pTmpIceCandidate = MEMCALLOC(1, SIZEOF(IceCandidate)); @@ -747,7 +754,10 @@ STATUS createIceCandidatePairs(PIceAgent pIceAgent, PIceCandidate pIceCandidate, pCurrentIceCandidate = (PIceCandidate) data; pCurNode = pCurNode->pNext; - if (pCurrentIceCandidate->state == ICE_CANDIDATE_STATE_VALID) { + // https://tools.ietf.org/html/rfc8445#section-6.1.2.2 + // pair local and remote candidates with the same family + if (pCurrentIceCandidate->state == ICE_CANDIDATE_STATE_VALID && + pCurrentIceCandidate->ipAddress.family == pIceCandidate->ipAddress.family) { pIceCandidatePair = (PIceCandidatePair) MEMCALLOC(1, SIZEOF(IceCandidatePair)); CHK(pIceCandidatePair != NULL, STATUS_NOT_ENOUGH_MEMORY); @@ -1047,20 +1057,21 @@ STATUS iceAgentSendSrflxCandidateRequest(PIceAgent pIceAgent) switch(pCandidate->iceCandidateType) { case ICE_CANDIDATE_TYPE_SERVER_REFLEXIVE: pIceServer = &(pIceAgent->iceServers[pCandidate->iceServerIndex]); - transactionIdStoreInsert(pIceAgent->pStunBindingRequestTransactionIdStore, - pBindingRequest->header.transactionId); - retStatus = iceUtilsSendStunPacket(pBindingRequest, - NULL, - 0, - &pIceServer->ipAddress, - pCandidate->pSocketConnection, - NULL, - FALSE); - if (STATUS_FAILED(retStatus)) { - DLOGD("iceUtilsSendStunPacket failed with 0x%08x", retStatus); - retStatus = STATUS_SUCCESS; + if (pIceServer->ipAddress.family == pCandidate->ipAddress.family) { + transactionIdStoreInsert(pIceAgent->pStunBindingRequestTransactionIdStore, + pBindingRequest->header.transactionId); + retStatus = iceUtilsSendStunPacket(pBindingRequest, + NULL, + 0, + &pIceServer->ipAddress, + pCandidate->pSocketConnection, + NULL, + FALSE); + if (STATUS_FAILED(retStatus)) { + DLOGD("iceUtilsSendStunPacket failed with 0x%08x", retStatus); + retStatus = STATUS_SUCCESS; + } } - break; default: @@ -1324,7 +1335,9 @@ STATUS iceAgentInitSrflxCandidate(PIceAgent pIceAgent) pCurNode = pCurNode->pNext; pCandidate = (PIceCandidate) data; - if (pCandidate->iceCandidateType == ICE_CANDIDATE_TYPE_HOST) { + // TODO: Stop skipping IPv6. Stun serialization and deserialization needs to be implemented properly first. + if (pCandidate->iceCandidateType == ICE_CANDIDATE_TYPE_HOST && + IS_IPV4_ADDR(&pCandidate->ipAddress)) { for (j = 0; j < pIceAgent->iceServersCount; j++) { if (!pIceAgent->iceServers[j].isTurn) { CHK((pNewCandidate = (PIceCandidate) MEMCALLOC(1, SIZEOF(IceCandidate))) != NULL, @@ -1402,8 +1415,15 @@ STATUS iceAgentInitRelayCandidate(PIceAgent pIceAgent) pSocketAddrForTurn = NULL; // if an VPN interface (isPointToPoint is TRUE) is found, use that instead for(i = 0; i < pIceAgent->localNetworkInterfaceCount; ++i) { - if(pIceAgent->localNetworkInterfaces[i].family == pIceAgent->iceServers[j].ipAddress.family && - (pSocketAddrForTurn == NULL || pIceAgent->localNetworkInterfaces[i].isPointToPoint)) { + // TODO: Stop skipping IPv6. Stun serialization and deserialization needs to be implemented properly first. + // Also, we need to handle the following kinds of relays: + // 1. IPv4-to-IPv6 + // 2. IPv6-to-IPv6 + // 3. IPv6-to-IPv4 + // RFC: https://tools.ietf.org/html/rfc6156 + if(pIceAgent->localNetworkInterfaces[i].family == KVS_IP_FAMILY_TYPE_IPV4 && + pIceAgent->localNetworkInterfaces[i].family == pIceAgent->iceServers[j].ipAddress.family && + (pSocketAddrForTurn == NULL || pIceAgent->localNetworkInterfaces[i].isPointToPoint)) { pSocketAddrForTurn = &pIceAgent->localNetworkInterfaces[i]; } } @@ -1460,8 +1480,12 @@ STATUS iceAgentInitRelayCandidate(PIceAgent pIceAgent) pCurNode = pCurNode->pNext; pCandidate = (PIceCandidate) data; - CHK_STATUS(turnConnectionAddPeer(pCurrentTurnConnectionTracker->pTurnConnection, - &pCandidate->ipAddress)); + // TODO: Stop skipping IPv6. Since we're allowing IPv6 remote candidates from iceAgentAddRemoteCandidate for host candidates, + // it's possible to have a situation where the turn server uses IPv4 and the remote candidate uses IPv6. + if (IS_IPV4_ADDR(&pCandidate->ipAddress)) { + CHK_STATUS(turnConnectionAddPeer(pCurrentTurnConnectionTracker->pTurnConnection, + &pCandidate->ipAddress)); + } } MUTEX_UNLOCK(pIceAgent->lock); @@ -1863,18 +1887,32 @@ STATUS iceCandidateSerialize(PIceCandidate pIceCandidate, PCHAR pOutputData, PUI pIceCandidate->ipAddress.address[3], (UINT16) getInt16(pIceCandidate->ipAddress.port), iceAgentGetCandidateTypeStr(pIceCandidate->iceCandidateType)); + } else { + amountWritten = SNPRINTF(pOutputData, + pOutputData == NULL ? 0 : *pOutputLength, + "%u 1 udp %u %02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X " + "%d typ %s raddr ::/0 rport 0 generation 0 network-cost 999", + pIceCandidate->foundation, + pIceCandidate->priority, + pIceCandidate->ipAddress.address[0], pIceCandidate->ipAddress.address[1], + pIceCandidate->ipAddress.address[2], pIceCandidate->ipAddress.address[3], + pIceCandidate->ipAddress.address[4], pIceCandidate->ipAddress.address[5], + pIceCandidate->ipAddress.address[6], pIceCandidate->ipAddress.address[7], + pIceCandidate->ipAddress.address[8], pIceCandidate->ipAddress.address[9], + pIceCandidate->ipAddress.address[10], pIceCandidate->ipAddress.address[11], + pIceCandidate->ipAddress.address[12], pIceCandidate->ipAddress.address[13], + pIceCandidate->ipAddress.address[14], pIceCandidate->ipAddress.address[15], + (UINT16) getInt16(pIceCandidate->ipAddress.port), + iceAgentGetCandidateTypeStr(pIceCandidate->iceCandidateType)); + } - CHK_WARN(amountWritten > 0, STATUS_INTERNAL_ERROR, "SNPRINTF failed"); - - if (pOutputData == NULL) { - *pOutputLength = ((UINT32) amountWritten) + 1; // +1 for null terminator - } else { - // amountWritten doesnt account for null char - CHK(amountWritten < (INT32) *pOutputLength, STATUS_BUFFER_TOO_SMALL); - } + CHK_WARN(amountWritten > 0, STATUS_INTERNAL_ERROR, "SNPRINTF failed"); + if (pOutputData == NULL) { + *pOutputLength = ((UINT32) amountWritten) + 1; // +1 for null terminator } else { - DLOGW("ipv6 not supported yet"); + // amountWritten doesnt account for null char + CHK(amountWritten < (INT32) *pOutputLength, STATUS_BUFFER_TOO_SMALL); } CleanUp: diff --git a/src/source/Ice/IceUtils.c b/src/source/Ice/IceUtils.c index 00866a8152..5c4c6c4400 100644 --- a/src/source/Ice/IceUtils.c +++ b/src/source/Ice/IceUtils.c @@ -185,45 +185,6 @@ STATUS iceUtilsSendData(PBYTE buffer, UINT32 size, return retStatus; } -// only work with ipv4 right now -STATUS populateIpFromString(PKvsIpAddress pKvsIpAddress, PCHAR pBuff) -{ - ENTERS(); - STATUS retStatus = STATUS_SUCCESS; - PCHAR curr, tail, next; - UINT8 octet = 0; - UINT32 ipValue; - - CHK(pKvsIpAddress != NULL && pBuff != NULL, STATUS_NULL_ARG); - CHK(STRNLEN(pBuff, KVS_MAX_IPV4_ADDRESS_STRING_LEN) > 0, STATUS_INVALID_ARG); - - curr = pBuff; - tail = pBuff + STRLEN(pBuff); - // first 3 octet should always end with a '.', the last octet may end with ' ' or '\0' - while ((next = STRNCHR(curr, tail - curr, '.')) != NULL && octet < 3) { - CHK_STATUS(STRTOUI32(curr, curr + (next - curr), 10, &ipValue)); - pKvsIpAddress->address[octet] = (UINT8) ipValue; - octet++; - - curr = next + 1; - } - - // work with string containing just ip address and string that has ip address as substring - if ((next = STRNCHR(curr, tail - curr, ' ')) != NULL || (next = STRNCHR(curr, tail - curr, '\0')) != NULL) { - CHK_STATUS(STRTOUI32(curr, curr + (next - curr), 10, &ipValue)); - pKvsIpAddress->address[octet] = (UINT8) ipValue; - octet++; - } - - CHK(octet == 4, STATUS_ICE_CANDIDATE_STRING_INVALID_IP); // IPv4 MUST have 4 octets -CleanUp: - - CHK_LOG_ERR(retStatus); - - LEAVES(); - return retStatus; -} - STATUS parseIceServer(PIceServer pIceServer, PCHAR url, PCHAR username, PCHAR credential) { ENTERS(); diff --git a/src/source/Ice/IceUtils.h b/src/source/Ice/IceUtils.h index e406c545d5..dc465692a8 100644 --- a/src/source/Ice/IceUtils.h +++ b/src/source/Ice/IceUtils.h @@ -37,8 +37,6 @@ STATUS iceUtilsPackageStunPacket(PStunPacket, PBYTE, UINT32, PBYTE, PUINT32); STATUS iceUtilsSendStunPacket(PStunPacket, PBYTE, UINT32, PKvsIpAddress, PSocketConnection, struct __TurnConnection*, BOOL); STATUS iceUtilsSendData(PBYTE, UINT32, PKvsIpAddress, PSocketConnection, struct __TurnConnection*, BOOL); -STATUS populateIpFromString(PKvsIpAddress, PCHAR); - typedef struct { BOOL isTurn; CHAR url[MAX_ICE_CONFIG_URI_LEN + 1]; diff --git a/src/source/Ice/Network.c b/src/source/Ice/Network.c index 1f918130fb..8ac7beaff7 100644 --- a/src/source/Ice/Network.c +++ b/src/source/Ice/Network.c @@ -51,6 +51,12 @@ STATUS getLocalhostIpAddresses(PKvsIpAddress destIpList, PUINT32 pDestIpListLen, destIpList[ipCount].family = KVS_IP_FAMILY_TYPE_IPV6; destIpList[ipCount].port = 0; pIpv6Addr = (struct sockaddr_in6 *) ifa->ifa_addr; + // Ignore link local: not very useful and will add work unnecessarily + // Ignore site local: https://tools.ietf.org/html/rfc8445#section-5.1.1.1 + if (IN6_IS_ADDR_LINKLOCAL(&pIpv6Addr->sin6_addr) || + IN6_IS_ADDR_SITELOCAL(&pIpv6Addr->sin6_addr)) { + continue; + } MEMCPY(destIpList[ipCount].address, &pIpv6Addr->sin6_addr, IPV6_ADDRESS_LENGTH); } diff --git a/tst/IceApiTest.cpp b/tst/IceApiTest.cpp index 46435b9fc3..c71af726e2 100644 --- a/tst/IceApiTest.cpp +++ b/tst/IceApiTest.cpp @@ -91,33 +91,6 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name EXPECT_EQ(STATUS_SUCCESS, freeStunPacket(&pStunPacket)); EXPECT_EQ(STATUS_SUCCESS, freeTransactionIdStore(&pTransactionIdStore)); } - - TEST_F(IceApiTest, IceUtilPopulateIpFromStringTest) - { - KvsIpAddress kvsIpAddress; - PCHAR ipString = (PCHAR) "16.213.65.123"; - BYTE expectIpAddr[IPV4_ADDRESS_LENGTH] = {0x10, 0xD5, 0x41, 0x7B}; - PCHAR ipString2 = (PCHAR) "255.255.255.255"; - PCHAR ipString2Longer = (PCHAR) "255.255.255.255 34388 222.222.222.222"; - BYTE expectIpAddr2[IPV4_ADDRESS_LENGTH] = {0xFF, 0xFF, 0xFF, 0xFF}; - - MEMSET(&kvsIpAddress, 0x0, SIZEOF(KvsIpAddress)); - - EXPECT_NE(STATUS_SUCCESS, populateIpFromString(NULL, NULL)); - EXPECT_NE(STATUS_SUCCESS, populateIpFromString(&kvsIpAddress, NULL)); - EXPECT_NE(STATUS_SUCCESS, populateIpFromString(&kvsIpAddress, (PCHAR) "")); - EXPECT_NE(STATUS_SUCCESS, populateIpFromString(NULL, ipString)); - - EXPECT_EQ(STATUS_SUCCESS, populateIpFromString(&kvsIpAddress, ipString)); - EXPECT_EQ(0, MEMCMP(expectIpAddr, kvsIpAddress.address, IPV4_ADDRESS_LENGTH)); - - EXPECT_EQ(STATUS_SUCCESS, populateIpFromString(&kvsIpAddress, ipString2)); - EXPECT_EQ(0, MEMCMP(expectIpAddr2, kvsIpAddress.address, IPV4_ADDRESS_LENGTH)); - - EXPECT_EQ(STATUS_SUCCESS, populateIpFromString(&kvsIpAddress, ipString2Longer)); - EXPECT_EQ(0, MEMCMP(expectIpAddr2, kvsIpAddress.address, IPV4_ADDRESS_LENGTH)); - } - } } } diff --git a/tst/IceFunctionalityTest.cpp b/tst/IceFunctionalityTest.cpp index 6127d78bd9..bd9da0ac12 100644 --- a/tst/IceFunctionalityTest.cpp +++ b/tst/IceFunctionalityTest.cpp @@ -102,6 +102,7 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name typedef struct { PConnectionListener pConnectionListener; UINT32 connectionToAdd; + KVS_IP_FAMILY_TYPE family; } ConnectionListenerTestCustomData,*PConnectionListenerTestCustomData; PVOID connectionListenAddConnectionRoutine(PVOID arg) @@ -113,15 +114,20 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name KvsIpAddress localhost; MEMSET(&localhost, 0x00, SIZEOF(KvsIpAddress)); - - localhost.family = KVS_IP_FAMILY_TYPE_IPV4; + localhost.isPointToPoint = FALSE; - // 127.0.0.1 - localhost.address[0] = 0x7f; - localhost.address[1] = 0x00; - localhost.address[2] = 0x00; - localhost.address[3] = 0x01; localhost.port = 0; + localhost.family = pCustomData->family; + if (pCustomData->family == KVS_IP_FAMILY_TYPE_IPV4) { + // 127.0.0.1 + localhost.address[0] = 0x7f; + localhost.address[1] = 0x00; + localhost.address[2] = 0x00; + localhost.address[3] = 0x01; + } else { + // ::1 + localhost.address[15] = 1; + } for(i = 0; i < pCustomData->connectionToAdd; ++i) { randomDelay = (UINT64) (RAND() % 300) * HUNDREDS_OF_NANOS_IN_A_MILLISECOND; @@ -162,6 +168,8 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name routine2CustomData.pConnectionListener = pConnectionListener; routine1CustomData.connectionToAdd = 3; routine2CustomData.connectionToAdd = 7; + routine1CustomData.family = KVS_IP_FAMILY_TYPE_IPV4; + routine2CustomData.family = KVS_IP_FAMILY_TYPE_IPV6; THREAD_CREATE(&routine1, connectionListenAddConnectionRoutine, (PVOID) &routine1CustomData); THREAD_CREATE(&routine2, connectionListenAddConnectionRoutine, (PVOID) &routine2CustomData); @@ -235,6 +243,13 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name EXPECT_EQ(localCandidate.ipAddress.port, newIpAddress.port); EXPECT_EQ(0, MEMCMP(localCandidate.ipAddress.address, newIpAddress.address, IPV4_ADDRESS_LENGTH)); + + newIpAddress.family = KVS_IP_FAMILY_TYPE_IPV6; + localCandidate.state = ICE_CANDIDATE_STATE_NEW; + EXPECT_EQ(STATUS_SUCCESS, updateCandidateAddress(&localCandidate, &newIpAddress)); + + EXPECT_EQ(localCandidate.ipAddress.port, newIpAddress.port); + EXPECT_EQ(0, MEMCMP(localCandidate.ipAddress.address, newIpAddress.address, IPV6_ADDRESS_LENGTH)); } TEST_F(IceFunctionalityTest, IceAgentIceAgentAddIceServerUnitTest) @@ -261,15 +276,20 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name { IceAgent iceAgent; UINT32 remoteCandidateCount = 0, iceCandidateCount = 0; - PCHAR hostCandidateStr = (PCHAR) "sdpMidate:543899094 1 udp 2122260223 12.131.158.132 64616 typ host generation 0 ufrag OFZ/ network-id 1 network-cost 10"; + PCHAR ip4HostCandidateStr = (PCHAR) "sdpMidate:543899094 1 udp 2122260223 12.131.158.132 64616 typ host generation 0 ufrag OFZ/ network-id 1 network-cost 10"; + PCHAR ip6HostCandidateStr = (PCHAR) "candidate:2526845803 1 udp 2122262783 2600:1700:cd70:2540:fd41:66ab:a9cd:f0aa 55216 typ host generation 0 ufrag qnXe network-id 2 network-cost 10"; PCHAR relayCandidateStr = (PCHAR) "sdpMidate:1501054171 1 udp 41885439 59.189.124.250 62834 typ relay raddr 205.251.233.176 rport 14669 generation 0 ufrag OFZ/ network-id 1 network-cost 10"; - IceCandidate testLocalCandidate; + IceCandidate ip4TestLocalCandidate, ip6TestLocalCandidate; PDoubleListNode pCurNode = NULL; PIceCandidatePair pIceCandidatePair = NULL; MEMSET(&iceAgent, 0x00, SIZEOF(IceAgent)); - MEMSET(&testLocalCandidate, 0x00, SIZEOF(IceCandidate)); - testLocalCandidate.state = ICE_CANDIDATE_STATE_VALID; + MEMSET(&ip4TestLocalCandidate, 0x00, SIZEOF(IceCandidate)); + MEMSET(&ip6TestLocalCandidate, 0x00, SIZEOF(IceCandidate)); + ip4TestLocalCandidate.state = ICE_CANDIDATE_STATE_VALID; + ip4TestLocalCandidate.ipAddress.family = KVS_IP_FAMILY_TYPE_IPV4; + ip6TestLocalCandidate.state = ICE_CANDIDATE_STATE_VALID; + ip6TestLocalCandidate.ipAddress.family = KVS_IP_FAMILY_TYPE_IPV6; // init needed members in iceAgent iceAgent.lock = MUTEX_CREATE(TRUE); @@ -282,14 +302,14 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name // invalid input EXPECT_NE(STATUS_SUCCESS, iceAgentAddRemoteCandidate(NULL, NULL)); EXPECT_NE(STATUS_SUCCESS, iceAgentAddRemoteCandidate(&iceAgent, NULL)); - EXPECT_NE(STATUS_SUCCESS, iceAgentAddRemoteCandidate(NULL, hostCandidateStr)); + EXPECT_NE(STATUS_SUCCESS, iceAgentAddRemoteCandidate(NULL, ip4HostCandidateStr)); EXPECT_NE(STATUS_SUCCESS, iceAgentAddRemoteCandidate(&iceAgent, (PCHAR) "")); EXPECT_NE(STATUS_SUCCESS, iceAgentAddRemoteCandidate(&iceAgent, (PCHAR) "randomStuff")); - // add a local candidate so that iceCandidate pair will be formed when add remote candidate succeeded - EXPECT_EQ(STATUS_SUCCESS, doubleListInsertItemTail(iceAgent.localCandidates, (UINT64) &testLocalCandidate)); - EXPECT_EQ(STATUS_SUCCESS, iceAgentAddRemoteCandidate(&iceAgent, hostCandidateStr)); - EXPECT_EQ(STATUS_SUCCESS, iceAgentAddRemoteCandidate(&iceAgent, hostCandidateStr)); + // add a ip4 local candidate so that iceCandidate pair will be formed when add remote candidate succeeded + EXPECT_EQ(STATUS_SUCCESS, doubleListInsertItemTail(iceAgent.localCandidates, (UINT64) &ip4TestLocalCandidate)); + EXPECT_EQ(STATUS_SUCCESS, iceAgentAddRemoteCandidate(&iceAgent, ip4HostCandidateStr)); + EXPECT_EQ(STATUS_SUCCESS, iceAgentAddRemoteCandidate(&iceAgent, ip4HostCandidateStr)); EXPECT_EQ(STATUS_SUCCESS, doubleListGetNodeCount(iceAgent.remoteCandidates, &remoteCandidateCount)); // duplicated candidates are not added EXPECT_EQ(1, remoteCandidateCount); @@ -297,14 +317,25 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name EXPECT_EQ(STATUS_SUCCESS, doubleListGetNodeCount(iceAgent.iceCandidatePairs, &iceCandidateCount)); EXPECT_EQ(1, iceCandidateCount); - iceAgent.iceAgentState = ICE_AGENT_STATE_CHECK_CONNECTION; - EXPECT_EQ(STATUS_SUCCESS, iceAgentAddRemoteCandidate(&iceAgent, relayCandidateStr)); + // add an ip6 local candidate so that iceCandidate pair will be formed when add remote candidate succeeded + EXPECT_EQ(STATUS_SUCCESS, doubleListInsertItemTail(iceAgent.localCandidates, (UINT64) &ip6TestLocalCandidate)); + EXPECT_EQ(STATUS_SUCCESS, iceAgentAddRemoteCandidate(&iceAgent, ip6HostCandidateStr)); + EXPECT_EQ(STATUS_SUCCESS, iceAgentAddRemoteCandidate(&iceAgent, ip6HostCandidateStr)); EXPECT_EQ(STATUS_SUCCESS, doubleListGetNodeCount(iceAgent.remoteCandidates, &remoteCandidateCount)); + // duplicated candidates are not added EXPECT_EQ(2, remoteCandidateCount); // candidate pair formed EXPECT_EQ(STATUS_SUCCESS, doubleListGetNodeCount(iceAgent.iceCandidatePairs, &iceCandidateCount)); EXPECT_EQ(2, iceCandidateCount); + iceAgent.iceAgentState = ICE_AGENT_STATE_CHECK_CONNECTION; + EXPECT_EQ(STATUS_SUCCESS, iceAgentAddRemoteCandidate(&iceAgent, relayCandidateStr)); + EXPECT_EQ(STATUS_SUCCESS, doubleListGetNodeCount(iceAgent.remoteCandidates, &remoteCandidateCount)); + EXPECT_EQ(3, remoteCandidateCount); + // candidate pair formed + EXPECT_EQ(STATUS_SUCCESS, doubleListGetNodeCount(iceAgent.iceCandidatePairs, &iceCandidateCount)); + EXPECT_EQ(3, iceCandidateCount); + MUTEX_FREE(iceAgent.lock); EXPECT_EQ(STATUS_SUCCESS, doubleListGetHeadNode(iceAgent.iceCandidatePairs, &pCurNode)); while (pCurNode != NULL) { @@ -335,7 +366,7 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name EXPECT_NE(STATUS_SUCCESS, findCandidateWithIp(&ipAddress, NULL, NULL)); EXPECT_NE(STATUS_SUCCESS, findCandidateWithIp(&ipAddress, &candidateList, NULL)); - EXPECT_EQ(STATUS_SUCCESS, populateIpFromString(&ipAddress, (PCHAR) "127.0.0.1")); + EXPECT_EQ(1, inet_pton(AF_INET, (PCHAR) "127.0.0.1", &ipAddress.address)); ipAddress.port = 123; ipAddress.family = KVS_IP_FAMILY_TYPE_IPV4; EXPECT_EQ(STATUS_SUCCESS, findCandidateWithIp(&ipAddress, &candidateList, &pIceCandidate)); @@ -351,12 +382,12 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name EXPECT_EQ(NULL, pIceCandidate); ipAddress.family = KVS_IP_FAMILY_TYPE_IPV4; - EXPECT_EQ(STATUS_SUCCESS, populateIpFromString(&ipAddress, (PCHAR) "127.0.0.2")); + EXPECT_EQ(1, inet_pton(AF_INET, (PCHAR) "127.0.0.2", &ipAddress.address)); EXPECT_EQ(STATUS_SUCCESS, findCandidateWithIp(&ipAddress, &candidateList, &pIceCandidate)); // address not match EXPECT_EQ(NULL, pIceCandidate); - EXPECT_EQ(STATUS_SUCCESS, populateIpFromString(&ipAddress, (PCHAR) "127.0.0.1")); + EXPECT_EQ(1, inet_pton(AF_INET, (PCHAR) "127.0.0.1", &ipAddress.address)); ipAddress.port = 124; EXPECT_EQ(STATUS_SUCCESS, findCandidateWithIp(&ipAddress, &candidateList, &pIceCandidate)); // port not match @@ -407,7 +438,7 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name { IceAgent iceAgent; IceCandidate localCandidate1, localCandidate2; - IceCandidate remoteCandidate1, remoteCandidate2; + IceCandidate remoteCandidate1, remoteCandidate2, remoteCandidate3; UINT32 iceCandidateCount = 0; PDoubleListNode pCurNode = NULL; PIceCandidatePair pIceCandidatePair = NULL; @@ -417,10 +448,17 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name MEMSET(&localCandidate2, 0x00, SIZEOF(IceCandidate)); localCandidate1.state = ICE_CANDIDATE_STATE_VALID; localCandidate2.state = ICE_CANDIDATE_STATE_VALID; + localCandidate1.ipAddress.family = KVS_IP_FAMILY_TYPE_IPV4; + localCandidate2.ipAddress.family = KVS_IP_FAMILY_TYPE_IPV6; MEMSET(&remoteCandidate1, 0x00, SIZEOF(IceCandidate)); MEMSET(&remoteCandidate2, 0x00, SIZEOF(IceCandidate)); + MEMSET(&remoteCandidate3, 0x00, SIZEOF(IceCandidate)); remoteCandidate1.state = ICE_CANDIDATE_STATE_VALID; remoteCandidate2.state = ICE_CANDIDATE_STATE_VALID; + remoteCandidate3.state = ICE_CANDIDATE_STATE_VALID; + remoteCandidate1.ipAddress.family = KVS_IP_FAMILY_TYPE_IPV6; + remoteCandidate2.ipAddress.family = KVS_IP_FAMILY_TYPE_IPV6; + remoteCandidate3.ipAddress.family = KVS_IP_FAMILY_TYPE_IPV6; EXPECT_EQ(STATUS_SUCCESS, doubleListCreate(&iceAgent.localCandidates)); EXPECT_EQ(STATUS_SUCCESS, doubleListCreate(&iceAgent.remoteCandidates)); EXPECT_EQ(STATUS_SUCCESS, doubleListCreate(&iceAgent.iceCandidatePairs)); @@ -453,6 +491,12 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name localCandidate1.state = ICE_CANDIDATE_STATE_VALID; EXPECT_EQ(STATUS_SUCCESS, createIceCandidatePairs(&iceAgent, &remoteCandidate1, TRUE)); EXPECT_EQ(STATUS_SUCCESS, doubleListGetNodeCount(iceAgent.iceCandidatePairs, &iceCandidateCount)); + // candidate has to be the same socket family type + EXPECT_EQ(0, iceCandidateCount); + + remoteCandidate1.ipAddress.family = KVS_IP_FAMILY_TYPE_IPV4; + EXPECT_EQ(STATUS_SUCCESS, createIceCandidatePairs(&iceAgent, &remoteCandidate1, TRUE)); + EXPECT_EQ(STATUS_SUCCESS, doubleListGetNodeCount(iceAgent.iceCandidatePairs, &iceCandidateCount)); // both candidate are valid now. Ice candidate pair should be created EXPECT_EQ(1, iceCandidateCount); EXPECT_EQ(STATUS_SUCCESS, doubleListGetHeadNode(iceAgent.iceCandidatePairs, &pCurNode)); @@ -462,14 +506,21 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { name EXPECT_EQ(STATUS_SUCCESS, doubleListInsertItemHead(iceAgent.localCandidates, (UINT64) &localCandidate2)); EXPECT_EQ(STATUS_SUCCESS, createIceCandidatePairs(&iceAgent, &localCandidate2, FALSE)); - // 2 local vs 1 remote, thus 2 pairs + // 1 local ip4 & 1 local ip6 vs 1 remote ip4, thus 1 pair EXPECT_EQ(STATUS_SUCCESS, doubleListGetNodeCount(iceAgent.iceCandidatePairs, &iceCandidateCount)); - EXPECT_EQ(2, iceCandidateCount); + EXPECT_EQ(1, iceCandidateCount); EXPECT_EQ(STATUS_SUCCESS, doubleListInsertItemHead(iceAgent.remoteCandidates, (UINT64) &remoteCandidate2)); EXPECT_EQ(STATUS_SUCCESS, createIceCandidatePairs(&iceAgent, &remoteCandidate2, TRUE)); + // 1 local ip4 & 1 local ip6 vs 1 remote ip4 & 1 remote ip6, thus 2 pairs + EXPECT_EQ(STATUS_SUCCESS, doubleListGetNodeCount(iceAgent.iceCandidatePairs, &iceCandidateCount)); + EXPECT_EQ(2, iceCandidateCount); + + EXPECT_EQ(STATUS_SUCCESS, doubleListInsertItemHead(iceAgent.remoteCandidates, (UINT64) &remoteCandidate3)); + EXPECT_EQ(STATUS_SUCCESS, createIceCandidatePairs(&iceAgent, &remoteCandidate3, TRUE)); + // 1 local ip4 & 1 local ip6 vs 1 remote ip4 & 2 remote ip6, thus 3 pairs EXPECT_EQ(STATUS_SUCCESS, doubleListGetNodeCount(iceAgent.iceCandidatePairs, &iceCandidateCount)); - EXPECT_EQ(4, iceCandidateCount); + EXPECT_EQ(3, iceCandidateCount); EXPECT_EQ(STATUS_SUCCESS, doubleListClear(iceAgent.localCandidates, FALSE)); EXPECT_EQ(STATUS_SUCCESS, doubleListClear(iceAgent.remoteCandidates, FALSE));