feat(android): support NAT64
Adds Nat64InfoModule which resolves IPv6 addresses for IPv4 addresses in IPv6 only network where jitsi-meet deployment does not provide any IPv6 addresses as ICE candidates.
This commit is contained in:
parent
0456df239f
commit
968b279b37
|
@ -33,6 +33,8 @@ dependencies {
|
||||||
compile project(':react-native-vector-icons')
|
compile project(':react-native-vector-icons')
|
||||||
compile project(':react-native-webrtc')
|
compile project(':react-native-webrtc')
|
||||||
compile project(':react-native-calendar-events')
|
compile project(':react-native-calendar-events')
|
||||||
|
|
||||||
|
testCompile 'junit:junit:4.12'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build process helpers
|
// Build process helpers
|
||||||
|
|
|
@ -21,7 +21,6 @@ import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
@ -78,7 +77,8 @@ public class JitsiMeetView extends FrameLayout {
|
||||||
new ExternalAPIModule(reactContext),
|
new ExternalAPIModule(reactContext),
|
||||||
new PictureInPictureModule(reactContext),
|
new PictureInPictureModule(reactContext),
|
||||||
new ProximityModule(reactContext),
|
new ProximityModule(reactContext),
|
||||||
new WiFiStatsModule(reactContext)
|
new WiFiStatsModule(reactContext),
|
||||||
|
new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
/*
|
||||||
|
* Copyright @ 2018-present Atlassian Pty Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jitsi.meet.sdk.net;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs IPv6 addresses for IPv4 addresses in the NAT64 environment.
|
||||||
|
*
|
||||||
|
* NAT64 translates IPv4 to IPv6 addresses by adding "well known" prefix and
|
||||||
|
* suffix configured by the administrator. Those are figured out by discovering
|
||||||
|
* both IPv6 and IPv4 addresses of a host and then trying to find a place where
|
||||||
|
* the IPv4 address fits into the format described here:
|
||||||
|
* https://tools.ietf.org/html/rfc6052#section-2.2
|
||||||
|
*/
|
||||||
|
public class NAT64AddrInfo {
|
||||||
|
/**
|
||||||
|
* Coverts bytes array to upper case HEX string.
|
||||||
|
*
|
||||||
|
* @param bytes an array of bytes to be converted
|
||||||
|
* @return ex. "010AFF" for an array of {1, 10, 255}.
|
||||||
|
*/
|
||||||
|
static String bytesToHexString(byte[] bytes) {
|
||||||
|
StringBuilder hexStr = new StringBuilder();
|
||||||
|
|
||||||
|
for (byte b : bytes) {
|
||||||
|
hexStr.append(String.format("%02X", b));
|
||||||
|
}
|
||||||
|
|
||||||
|
return hexStr.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to discover the NAT64 prefix/suffix based on the IPv4 and IPv6
|
||||||
|
* addresses resolved for given {@code host}.
|
||||||
|
*
|
||||||
|
* @param host the host for which the code will try to discover IPv4 and
|
||||||
|
* IPv6 addresses which then will be used to figure out the NAT64 prefix.
|
||||||
|
* @return {@link NAT64AddrInfo} instance if the NAT64 prefix/suffix was
|
||||||
|
* successfully discovered or {@code null} if it failed for any reason.
|
||||||
|
* @throws UnknownHostException thrown by {@link InetAddress#getAllByName}.
|
||||||
|
*/
|
||||||
|
public static NAT64AddrInfo discover(String host)
|
||||||
|
throws UnknownHostException {
|
||||||
|
InetAddress ipv4 = null;
|
||||||
|
InetAddress ipv6 = null;
|
||||||
|
|
||||||
|
for(InetAddress addr : InetAddress.getAllByName(host)) {
|
||||||
|
byte[] bytes = addr.getAddress();
|
||||||
|
|
||||||
|
if (bytes.length == 4) {
|
||||||
|
ipv4 = addr;
|
||||||
|
} else if (bytes.length == 16) {
|
||||||
|
ipv6 = addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ipv4 != null && ipv6 != null) {
|
||||||
|
return figureOutNAT64AddrInfo(ipv4.getAddress(), ipv6.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on IPv4 and IPv6 addresses of the same host, the method will make
|
||||||
|
* an attempt to figure out what are the NAT64 prefix and suffix.
|
||||||
|
*
|
||||||
|
* @param ipv4AddrBytes the IPv4 address of the same host in NAT64 network,
|
||||||
|
* as returned by {@link InetAddress#getAddress()}.
|
||||||
|
* @param ipv6AddrBytes the IPv6 address of the same host in NAT64 network,
|
||||||
|
* as returned by {@link InetAddress#getAddress()}.
|
||||||
|
* @return {@link NAT64AddrInfo} instance which contains the prefix/suffix
|
||||||
|
* of the current NAT64 network or {@code null} if the prefix could not be
|
||||||
|
* found.
|
||||||
|
*/
|
||||||
|
static NAT64AddrInfo figureOutNAT64AddrInfo(
|
||||||
|
byte[] ipv4AddrBytes,
|
||||||
|
byte[] ipv6AddrBytes) {
|
||||||
|
String ipv6Str = bytesToHexString(ipv6AddrBytes);
|
||||||
|
String ipv4Str = bytesToHexString(ipv4AddrBytes);
|
||||||
|
|
||||||
|
// NAT64 address format:
|
||||||
|
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
// |PL| 0-------------32--40--48--56--64--72--80--88--96--104---------|
|
||||||
|
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
// |32| prefix |v4(32) | u | suffix |
|
||||||
|
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
// |40| prefix |v4(24) | u |(8)| suffix |
|
||||||
|
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
// |48| prefix |v4(16) | u | (16) | suffix |
|
||||||
|
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
// |56| prefix |(8)| u | v4(24) | suffix |
|
||||||
|
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
// |64| prefix | u | v4(32) | suffix |
|
||||||
|
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
// |96| prefix | v4(32) |
|
||||||
|
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
int prefixLength = 96;
|
||||||
|
int suffixLength = 0;
|
||||||
|
String prefix = null;
|
||||||
|
String suffix = null;
|
||||||
|
|
||||||
|
if (ipv4Str.equalsIgnoreCase(ipv6Str.substring(prefixLength / 4))) {
|
||||||
|
prefix = ipv6Str.substring(0, prefixLength / 4);
|
||||||
|
} else {
|
||||||
|
// Cut out the 'u' octet
|
||||||
|
ipv6Str = ipv6Str.substring(0, 16) + ipv6Str.substring(18);
|
||||||
|
|
||||||
|
for (prefixLength = 64, suffixLength = 6; prefixLength >= 32; ) {
|
||||||
|
if (ipv4Str.equalsIgnoreCase(
|
||||||
|
ipv6Str.substring(
|
||||||
|
prefixLength / 4, prefixLength / 4 + 8))) {
|
||||||
|
prefix = ipv6Str.substring(0, prefixLength / 4);
|
||||||
|
suffix = ipv6Str.substring(ipv6Str.length() - suffixLength);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixLength -= 8;
|
||||||
|
suffixLength += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefix != null ? new NAT64AddrInfo(prefix, suffix) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An overload for {@link #hexStringToIPv6String(StringBuilder)}.
|
||||||
|
*
|
||||||
|
* @param hexStr a hex representation of IPv6 address bytes.
|
||||||
|
* @return an IPv6 address string.
|
||||||
|
*/
|
||||||
|
static String hexStringToIPv6String(String hexStr) {
|
||||||
|
return hexStringToIPv6String(new StringBuilder(hexStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts from HEX representation of IPv6 address bytes into IPv6 address
|
||||||
|
* string which includes the ':' signs.
|
||||||
|
*
|
||||||
|
* @param str a hex representation of IPv6 address bytes.
|
||||||
|
* @return eg. FE80:CD00:0000:0CDA:1357:0000:212F:749C
|
||||||
|
*/
|
||||||
|
static String hexStringToIPv6String(StringBuilder str) {
|
||||||
|
for (int i = 32 - 4; i > 0; i -= 4) {
|
||||||
|
str.insert(i, ":");
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.toString().toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an IPv4 address string and returns it's byte array representation.
|
||||||
|
*
|
||||||
|
* @param ipv4Address eg. '192.168.3.23'
|
||||||
|
* @return byte representation of given IPv4 address string.
|
||||||
|
* @throws IllegalArgumentException if the address is not in valid format.
|
||||||
|
*/
|
||||||
|
static byte[] ipv4AddressStringToBytes(String ipv4Address) {
|
||||||
|
InetAddress address;
|
||||||
|
|
||||||
|
try {
|
||||||
|
address = InetAddress.getByName(ipv4Address);
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid IP address: " + ipv4Address, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bytes = address.getAddress();
|
||||||
|
|
||||||
|
if (bytes.length != 4) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Not an IPv4 address: " + ipv4Address);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The NAT64 prefix added to construct IPv6 from an IPv4 address.
|
||||||
|
*/
|
||||||
|
private final String prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The NAT64 suffix (if any) used to construct IPv6 from an IPv4 address.
|
||||||
|
*/
|
||||||
|
private final String suffix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new instance of {@link NAT64AddrInfo}.
|
||||||
|
*
|
||||||
|
* @param prefix the NAT64 prefix.
|
||||||
|
* @param suffix the NAT64 suffix.
|
||||||
|
*/
|
||||||
|
private NAT64AddrInfo(String prefix, String suffix) {
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.suffix = suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on the NAT64 prefix and suffix will create an IPv6 representation
|
||||||
|
* of the given IPv4 address.
|
||||||
|
*
|
||||||
|
* @param ipv4Address eg. '192.34.2.3'
|
||||||
|
* @return IPv6 address string eg. FE80:CD00:0000:0CDA:1357:0000:212F:749C
|
||||||
|
* @throws IllegalArgumentException if given string is not a valid IPv4
|
||||||
|
* address.
|
||||||
|
*/
|
||||||
|
public String getIPv6Address(String ipv4Address) {
|
||||||
|
byte[] ipv4AddressBytes = ipv4AddressStringToBytes(ipv4Address);
|
||||||
|
StringBuilder newIPv6Str = new StringBuilder();
|
||||||
|
|
||||||
|
newIPv6Str.append(prefix);
|
||||||
|
newIPv6Str.append(bytesToHexString(ipv4AddressBytes));
|
||||||
|
|
||||||
|
if (suffix != null) {
|
||||||
|
// Insert the 'u' octet.
|
||||||
|
newIPv6Str.insert(16, "00");
|
||||||
|
newIPv6Str.append(suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hexStringToIPv6String(newIPv6Str);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Copyright @ 2018-present Atlassian Pty Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jitsi.meet.sdk.net;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.Promise;
|
||||||
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
|
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This module exposes the functionality of creating an IPv6 representation
|
||||||
|
* of IPv4 addresses in NAT64 environment.
|
||||||
|
*
|
||||||
|
* See[1] and [2] for more info on what NAT64 is.
|
||||||
|
* [1]: https://tools.ietf.org/html/rfc6146
|
||||||
|
* [2]: https://tools.ietf.org/html/rfc6052
|
||||||
|
*/
|
||||||
|
public class NAT64AddrInfoModule extends ReactContextBaseJavaModule {
|
||||||
|
/**
|
||||||
|
* The host for which the module wil try to resolve both IPv4 and IPv6
|
||||||
|
* addresses in order to figure out the NAT64 prefix.
|
||||||
|
*/
|
||||||
|
private final static String HOST = "nat64.jitsi.net";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long is the {@link NAT64AddrInfo} instance valid.
|
||||||
|
*/
|
||||||
|
private final static long INFO_LIFETIME = 60 * 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of this module.
|
||||||
|
*/
|
||||||
|
private final static String MODULE_NAME = "NAT64AddrInfo";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code Log} tag {@code NAT64AddrInfoModule} is to log messages with.
|
||||||
|
*/
|
||||||
|
private final static String TAG = MODULE_NAME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link NAT64AddrInfo} instance which holds NAT64 prefix/suffix.
|
||||||
|
*/
|
||||||
|
private NAT64AddrInfo info;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When {@link #info} was created.
|
||||||
|
*/
|
||||||
|
private long infoTimestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new {@link NAT64AddrInfoModule}.
|
||||||
|
*
|
||||||
|
* @param reactContext the react context to be used by the new module
|
||||||
|
* instance.
|
||||||
|
*/
|
||||||
|
public NAT64AddrInfoModule(ReactApplicationContext reactContext) {
|
||||||
|
super(reactContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to obtain IPv6 address for given IPv4 address in NAT64 environment.
|
||||||
|
*
|
||||||
|
* @param ipv4Address IPv4 address string.
|
||||||
|
* @param promise a {@link Promise} which will be resolved either with IPv6
|
||||||
|
* address for given IPv4 address or with {@code null} if no
|
||||||
|
* {@link NAT64AddrInfo} was resolved for the current network. Will be
|
||||||
|
* rejected if given {@code ipv4Address} is not a valid IPv4 address.
|
||||||
|
*/
|
||||||
|
@ReactMethod
|
||||||
|
public void getIPv6Address(String ipv4Address, final Promise promise) {
|
||||||
|
// Reset if cached for too long.
|
||||||
|
if (System.currentTimeMillis() - infoTimestamp > INFO_LIFETIME) {
|
||||||
|
info = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == null) {
|
||||||
|
String host = HOST;
|
||||||
|
|
||||||
|
try {
|
||||||
|
info = NAT64AddrInfo.discover(host);
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
Log.e(TAG, "NAT64AddrInfo.discover: " + host, e);
|
||||||
|
}
|
||||||
|
infoTimestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
String result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = info == null ? null : info.getIPv6Address(ipv4Address);
|
||||||
|
} catch (IllegalArgumentException exc) {
|
||||||
|
Log.e(TAG, "Failed to get IPv6 address for: " + ipv4Address, exc);
|
||||||
|
|
||||||
|
// We don't want to reject. It's not a big deal if there's no IPv6
|
||||||
|
// address resolved.
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
promise.resolve(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return MODULE_NAME;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jitsi.meet.sdk.net;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link NAT64AddrInfo} class.
|
||||||
|
*/
|
||||||
|
public class NAT64AddrInfoTest {
|
||||||
|
/**
|
||||||
|
* Test case for the 96 prefix length.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void test96Prefix() {
|
||||||
|
testPrefixSuffix(
|
||||||
|
"260777000000000400000000", "", "203.0.113.1", "23.17.23.3");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for the 64 prefix length.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void test64Prefix() {
|
||||||
|
String prefix = "1FF2A227B3AAF3D2";
|
||||||
|
String suffix = "BB87C8";
|
||||||
|
|
||||||
|
testPrefixSuffix(prefix, suffix, "48.46.87.34", "23.87.145.4");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for the 56 prefix length.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void test56Prefix() {
|
||||||
|
String prefix = "1FF2A227B3AAF3";
|
||||||
|
String suffix = "A2BB87C8";
|
||||||
|
|
||||||
|
testPrefixSuffix(prefix, suffix, "34.72.234.255", "1.235.3.65");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for the 48 prefix length.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void test48Prefix() {
|
||||||
|
String prefix = "1FF2A227B3AA";
|
||||||
|
String suffix = "72A2BB87C8";
|
||||||
|
|
||||||
|
testPrefixSuffix(prefix, suffix, "97.54.3.23", "77.49.0.33");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for the 40 prefix length.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void test40Prefix() {
|
||||||
|
String prefix = "1FF2A227B3";
|
||||||
|
String suffix = "D972A2BB87C8";
|
||||||
|
|
||||||
|
testPrefixSuffix(prefix, suffix, "10.23.56.121", "97.65.32.21");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for the 32 prefix length.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void test32Prefix()
|
||||||
|
throws UnknownHostException {
|
||||||
|
String prefix = "1FF2A227";
|
||||||
|
String suffix = "20D972A2BB87C8";
|
||||||
|
|
||||||
|
testPrefixSuffix(prefix, suffix, "162.63.65.189", "135.222.84.206");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildIPv6Addr(
|
||||||
|
String prefix, String suffix, String ipv4Hex) {
|
||||||
|
String ipv6Str = prefix + ipv4Hex + suffix;
|
||||||
|
|
||||||
|
if (suffix.length() > 0) {
|
||||||
|
ipv6Str = new StringBuilder(ipv6Str).insert(16, "00").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipv6Str;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testPrefixSuffix(
|
||||||
|
String prefix, String suffix, String ipv4, String otherIPv4) {
|
||||||
|
byte[] ipv4Bytes = NAT64AddrInfo.ipv4AddressStringToBytes(ipv4);
|
||||||
|
String ipv4String = NAT64AddrInfo.bytesToHexString(ipv4Bytes);
|
||||||
|
String ipv6Str = buildIPv6Addr(prefix, suffix, ipv4String);
|
||||||
|
|
||||||
|
BigInteger ipv6Address = new BigInteger(ipv6Str, 16);
|
||||||
|
|
||||||
|
NAT64AddrInfo nat64AddrInfo
|
||||||
|
= NAT64AddrInfo.figureOutNAT64AddrInfo(
|
||||||
|
ipv4Bytes, ipv6Address.toByteArray());
|
||||||
|
|
||||||
|
assertNotNull("Failed to figure out NAT64 info", nat64AddrInfo);
|
||||||
|
|
||||||
|
String newIPv6 = nat64AddrInfo.getIPv6Address(ipv4);
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
NAT64AddrInfo.hexStringToIPv6String(ipv6Address.toString(16)),
|
||||||
|
newIPv6);
|
||||||
|
|
||||||
|
byte[] ipv4Addr2 = NAT64AddrInfo.ipv4AddressStringToBytes(otherIPv4);
|
||||||
|
String ipv4Addr2Hex = NAT64AddrInfo.bytesToHexString(ipv4Addr2);
|
||||||
|
|
||||||
|
newIPv6 = nat64AddrInfo.getIPv6Address(otherIPv4);
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
NAT64AddrInfo.hexStringToIPv6String(
|
||||||
|
buildIPv6Addr(prefix, suffix, ipv4Addr2Hex)),
|
||||||
|
newIPv6);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidIPv4Format() {
|
||||||
|
testInvalidIPv4Format("256.1.2.3");
|
||||||
|
testInvalidIPv4Format("FE80:CD00:0000:0CDA:1357:0000:212F:749C");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testInvalidIPv4Format(String ipv4Str) {
|
||||||
|
try {
|
||||||
|
NAT64AddrInfo.ipv4AddressStringToBytes(ipv4Str);
|
||||||
|
fail("Did not throw IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException exc) {
|
||||||
|
/* OK */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -231,8 +231,54 @@ function _setRemoteDescription(sessionDescription) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX The function _synthesizeIPv6FromIPv4Address is not placed relative to the
|
||||||
|
// other functions in the file according to alphabetical sorting rule of the
|
||||||
|
// coding style. But eslint wants constants to be defined before they are used.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synthesize IPv6 addresses on iOS in order to support IPv6 NAT64 networks.
|
* Synthesizes an IPv6 address from a specific IPv4 address.
|
||||||
|
*
|
||||||
|
* @param {string} ipv4 - The IPv4 address from which an IPv6 address is to be
|
||||||
|
* synthesized.
|
||||||
|
* @returns {Promise<?string>} A {@code Promise} which gets resolved with the
|
||||||
|
* IPv6 address synthesized from the specified {@code ipv4} or a falsy value to
|
||||||
|
* be treated as inability to synthesize an IPv6 address from the specified
|
||||||
|
* {@code ipv4}.
|
||||||
|
*/
|
||||||
|
const _synthesizeIPv6FromIPv4Address: string => Promise<?string> = (function() {
|
||||||
|
// POSIX.getaddrinfo
|
||||||
|
const { POSIX } = NativeModules;
|
||||||
|
|
||||||
|
if (POSIX) {
|
||||||
|
const { getaddrinfo } = POSIX;
|
||||||
|
|
||||||
|
if (typeof getaddrinfo === 'function') {
|
||||||
|
return ipv4 =>
|
||||||
|
getaddrinfo(/* hostname */ ipv4, /* servname */ undefined)
|
||||||
|
.then(([ { ai_addr: ipv6 } ]) => ipv6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NAT64AddrInfo.getIPv6Address
|
||||||
|
const { NAT64AddrInfo } = NativeModules;
|
||||||
|
|
||||||
|
if (NAT64AddrInfo) {
|
||||||
|
const { getIPv6Address } = NAT64AddrInfo;
|
||||||
|
|
||||||
|
if (typeof getIPv6Address === 'function') {
|
||||||
|
return getIPv6Address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's no POSIX.getaddrinfo or NAT64AddrInfo.getIPv6Address.
|
||||||
|
return () =>
|
||||||
|
Promise.reject(
|
||||||
|
'The impossible just happened! No POSIX.getaddrinfo or'
|
||||||
|
+ ' NAT64AddrInfo.getIPv6Address!');
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synthesizes IPv6 addresses on iOS in order to support IPv6 NAT64 networks.
|
||||||
*
|
*
|
||||||
* @param {RTCSessionDescription} sdp - The RTCSessionDescription which
|
* @param {RTCSessionDescription} sdp - The RTCSessionDescription which
|
||||||
* specifies the configuration of the remote end of the connection.
|
* specifies the configuration of the remote end of the connection.
|
||||||
|
@ -240,12 +286,6 @@ function _setRemoteDescription(sessionDescription) {
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
function _synthesizeIPv6Addresses(sdp) {
|
function _synthesizeIPv6Addresses(sdp) {
|
||||||
// The synthesis of IPv6 addresses is implemented on iOS only at the time of
|
|
||||||
// this writing.
|
|
||||||
if (!NativeModules.POSIX) {
|
|
||||||
return Promise.resolve(sdp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
new Promise(resolve => resolve(_synthesizeIPv6Addresses0(sdp)))
|
new Promise(resolve => resolve(_synthesizeIPv6Addresses0(sdp)))
|
||||||
.then(({ ips, lines }) =>
|
.then(({ ips, lines }) =>
|
||||||
|
@ -272,7 +312,6 @@ function _synthesizeIPv6Addresses0(sessionDescription) {
|
||||||
let start = 0;
|
let start = 0;
|
||||||
const lines = [];
|
const lines = [];
|
||||||
const ips = new Map();
|
const ips = new Map();
|
||||||
const { getaddrinfo } = NativeModules.POSIX;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
const end = sdp.indexOf('\r\n', start);
|
const end = sdp.indexOf('\r\n', start);
|
||||||
|
@ -311,9 +350,10 @@ function _synthesizeIPv6Addresses0(sessionDescription) {
|
||||||
if (v && typeof v === 'string') {
|
if (v && typeof v === 'string') {
|
||||||
resolve(v);
|
resolve(v);
|
||||||
} else {
|
} else {
|
||||||
getaddrinfo(ip, undefined).then(
|
_synthesizeIPv6FromIPv4Address(ip).then(
|
||||||
([ { ai_addr: value } ]) => {
|
value => {
|
||||||
if (value.indexOf(':') === -1
|
if (!value
|
||||||
|
|| value.indexOf(':') === -1
|
||||||
|| value === ips.get(ip)) {
|
|| value === ips.get(ip)) {
|
||||||
ips.delete(ip);
|
ips.delete(ip);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue