|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+/*
|
|
|
2
|
+ * Copyright @ 2018-present Atlassian Pty Ltd
|
|
|
3
|
+ *
|
|
|
4
|
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
5
|
+ * you may not use this file except in compliance with the License.
|
|
|
6
|
+ * You may obtain a copy of the License at
|
|
|
7
|
+ *
|
|
|
8
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
9
|
+ *
|
|
|
10
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
11
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
12
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
13
|
+ * See the License for the specific language governing permissions and
|
|
|
14
|
+ * limitations under the License.
|
|
|
15
|
+ */
|
|
|
16
|
+package org.jitsi.meet.sdk.net;
|
|
|
17
|
+
|
|
|
18
|
+import java.net.InetAddress;
|
|
|
19
|
+import java.net.UnknownHostException;
|
|
|
20
|
+
|
|
|
21
|
+/**
|
|
|
22
|
+ * Constructs IPv6 addresses for IPv4 addresses in the NAT64 environment.
|
|
|
23
|
+ *
|
|
|
24
|
+ * NAT64 translates IPv4 to IPv6 addresses by adding "well known" prefix and
|
|
|
25
|
+ * suffix configured by the administrator. Those are figured out by discovering
|
|
|
26
|
+ * both IPv6 and IPv4 addresses of a host and then trying to find a place where
|
|
|
27
|
+ * the IPv4 address fits into the format described here:
|
|
|
28
|
+ * https://tools.ietf.org/html/rfc6052#section-2.2
|
|
|
29
|
+ */
|
|
|
30
|
+public class NAT64AddrInfo {
|
|
|
31
|
+ /**
|
|
|
32
|
+ * Coverts bytes array to upper case HEX string.
|
|
|
33
|
+ *
|
|
|
34
|
+ * @param bytes an array of bytes to be converted
|
|
|
35
|
+ * @return ex. "010AFF" for an array of {1, 10, 255}.
|
|
|
36
|
+ */
|
|
|
37
|
+ static String bytesToHexString(byte[] bytes) {
|
|
|
38
|
+ StringBuilder hexStr = new StringBuilder();
|
|
|
39
|
+
|
|
|
40
|
+ for (byte b : bytes) {
|
|
|
41
|
+ hexStr.append(String.format("%02X", b));
|
|
|
42
|
+ }
|
|
|
43
|
+
|
|
|
44
|
+ return hexStr.toString();
|
|
|
45
|
+ }
|
|
|
46
|
+
|
|
|
47
|
+ /**
|
|
|
48
|
+ * Tries to discover the NAT64 prefix/suffix based on the IPv4 and IPv6
|
|
|
49
|
+ * addresses resolved for given {@code host}.
|
|
|
50
|
+ *
|
|
|
51
|
+ * @param host the host for which the code will try to discover IPv4 and
|
|
|
52
|
+ * IPv6 addresses which then will be used to figure out the NAT64 prefix.
|
|
|
53
|
+ * @return {@link NAT64AddrInfo} instance if the NAT64 prefix/suffix was
|
|
|
54
|
+ * successfully discovered or {@code null} if it failed for any reason.
|
|
|
55
|
+ * @throws UnknownHostException thrown by {@link InetAddress#getAllByName}.
|
|
|
56
|
+ */
|
|
|
57
|
+ public static NAT64AddrInfo discover(String host)
|
|
|
58
|
+ throws UnknownHostException {
|
|
|
59
|
+ InetAddress ipv4 = null;
|
|
|
60
|
+ InetAddress ipv6 = null;
|
|
|
61
|
+
|
|
|
62
|
+ for(InetAddress addr : InetAddress.getAllByName(host)) {
|
|
|
63
|
+ byte[] bytes = addr.getAddress();
|
|
|
64
|
+
|
|
|
65
|
+ if (bytes.length == 4) {
|
|
|
66
|
+ ipv4 = addr;
|
|
|
67
|
+ } else if (bytes.length == 16) {
|
|
|
68
|
+ ipv6 = addr;
|
|
|
69
|
+ }
|
|
|
70
|
+ }
|
|
|
71
|
+
|
|
|
72
|
+ if (ipv4 != null && ipv6 != null) {
|
|
|
73
|
+ return figureOutNAT64AddrInfo(ipv4.getAddress(), ipv6.getAddress());
|
|
|
74
|
+ }
|
|
|
75
|
+
|
|
|
76
|
+ return null;
|
|
|
77
|
+ }
|
|
|
78
|
+
|
|
|
79
|
+ /**
|
|
|
80
|
+ * Based on IPv4 and IPv6 addresses of the same host, the method will make
|
|
|
81
|
+ * an attempt to figure out what are the NAT64 prefix and suffix.
|
|
|
82
|
+ *
|
|
|
83
|
+ * @param ipv4AddrBytes the IPv4 address of the same host in NAT64 network,
|
|
|
84
|
+ * as returned by {@link InetAddress#getAddress()}.
|
|
|
85
|
+ * @param ipv6AddrBytes the IPv6 address of the same host in NAT64 network,
|
|
|
86
|
+ * as returned by {@link InetAddress#getAddress()}.
|
|
|
87
|
+ * @return {@link NAT64AddrInfo} instance which contains the prefix/suffix
|
|
|
88
|
+ * of the current NAT64 network or {@code null} if the prefix could not be
|
|
|
89
|
+ * found.
|
|
|
90
|
+ */
|
|
|
91
|
+ static NAT64AddrInfo figureOutNAT64AddrInfo(
|
|
|
92
|
+ byte[] ipv4AddrBytes,
|
|
|
93
|
+ byte[] ipv6AddrBytes) {
|
|
|
94
|
+ String ipv6Str = bytesToHexString(ipv6AddrBytes);
|
|
|
95
|
+ String ipv4Str = bytesToHexString(ipv4AddrBytes);
|
|
|
96
|
+
|
|
|
97
|
+ // NAT64 address format:
|
|
|
98
|
+ // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
99
|
+ // |PL| 0-------------32--40--48--56--64--72--80--88--96--104---------|
|
|
|
100
|
+ // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
101
|
+ // |32| prefix |v4(32) | u | suffix |
|
|
|
102
|
+ // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
103
|
+ // |40| prefix |v4(24) | u |(8)| suffix |
|
|
|
104
|
+ // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
105
|
+ // |48| prefix |v4(16) | u | (16) | suffix |
|
|
|
106
|
+ // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
107
|
+ // |56| prefix |(8)| u | v4(24) | suffix |
|
|
|
108
|
+ // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
109
|
+ // |64| prefix | u | v4(32) | suffix |
|
|
|
110
|
+ // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
111
|
+ // |96| prefix | v4(32) |
|
|
|
112
|
+ // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
113
|
+ int prefixLength = 96;
|
|
|
114
|
+ int suffixLength = 0;
|
|
|
115
|
+ String prefix = null;
|
|
|
116
|
+ String suffix = null;
|
|
|
117
|
+
|
|
|
118
|
+ if (ipv4Str.equalsIgnoreCase(ipv6Str.substring(prefixLength / 4))) {
|
|
|
119
|
+ prefix = ipv6Str.substring(0, prefixLength / 4);
|
|
|
120
|
+ } else {
|
|
|
121
|
+ // Cut out the 'u' octet
|
|
|
122
|
+ ipv6Str = ipv6Str.substring(0, 16) + ipv6Str.substring(18);
|
|
|
123
|
+
|
|
|
124
|
+ for (prefixLength = 64, suffixLength = 6; prefixLength >= 32; ) {
|
|
|
125
|
+ if (ipv4Str.equalsIgnoreCase(
|
|
|
126
|
+ ipv6Str.substring(
|
|
|
127
|
+ prefixLength / 4, prefixLength / 4 + 8))) {
|
|
|
128
|
+ prefix = ipv6Str.substring(0, prefixLength / 4);
|
|
|
129
|
+ suffix = ipv6Str.substring(ipv6Str.length() - suffixLength);
|
|
|
130
|
+ break;
|
|
|
131
|
+ }
|
|
|
132
|
+
|
|
|
133
|
+ prefixLength -= 8;
|
|
|
134
|
+ suffixLength += 2;
|
|
|
135
|
+ }
|
|
|
136
|
+ }
|
|
|
137
|
+
|
|
|
138
|
+ return prefix != null ? new NAT64AddrInfo(prefix, suffix) : null;
|
|
|
139
|
+ }
|
|
|
140
|
+
|
|
|
141
|
+ /**
|
|
|
142
|
+ * An overload for {@link #hexStringToIPv6String(StringBuilder)}.
|
|
|
143
|
+ *
|
|
|
144
|
+ * @param hexStr a hex representation of IPv6 address bytes.
|
|
|
145
|
+ * @return an IPv6 address string.
|
|
|
146
|
+ */
|
|
|
147
|
+ static String hexStringToIPv6String(String hexStr) {
|
|
|
148
|
+ return hexStringToIPv6String(new StringBuilder(hexStr));
|
|
|
149
|
+ }
|
|
|
150
|
+
|
|
|
151
|
+ /**
|
|
|
152
|
+ * Converts from HEX representation of IPv6 address bytes into IPv6 address
|
|
|
153
|
+ * string which includes the ':' signs.
|
|
|
154
|
+ *
|
|
|
155
|
+ * @param str a hex representation of IPv6 address bytes.
|
|
|
156
|
+ * @return eg. FE80:CD00:0000:0CDA:1357:0000:212F:749C
|
|
|
157
|
+ */
|
|
|
158
|
+ static String hexStringToIPv6String(StringBuilder str) {
|
|
|
159
|
+ for (int i = 32 - 4; i > 0; i -= 4) {
|
|
|
160
|
+ str.insert(i, ":");
|
|
|
161
|
+ }
|
|
|
162
|
+
|
|
|
163
|
+ return str.toString().toUpperCase();
|
|
|
164
|
+ }
|
|
|
165
|
+
|
|
|
166
|
+ /**
|
|
|
167
|
+ * Parses an IPv4 address string and returns it's byte array representation.
|
|
|
168
|
+ *
|
|
|
169
|
+ * @param ipv4Address eg. '192.168.3.23'
|
|
|
170
|
+ * @return byte representation of given IPv4 address string.
|
|
|
171
|
+ * @throws IllegalArgumentException if the address is not in valid format.
|
|
|
172
|
+ */
|
|
|
173
|
+ static byte[] ipv4AddressStringToBytes(String ipv4Address) {
|
|
|
174
|
+ InetAddress address;
|
|
|
175
|
+
|
|
|
176
|
+ try {
|
|
|
177
|
+ address = InetAddress.getByName(ipv4Address);
|
|
|
178
|
+ } catch (UnknownHostException e) {
|
|
|
179
|
+ throw new IllegalArgumentException(
|
|
|
180
|
+ "Invalid IP address: " + ipv4Address, e);
|
|
|
181
|
+ }
|
|
|
182
|
+
|
|
|
183
|
+ byte[] bytes = address.getAddress();
|
|
|
184
|
+
|
|
|
185
|
+ if (bytes.length != 4) {
|
|
|
186
|
+ throw new IllegalArgumentException(
|
|
|
187
|
+ "Not an IPv4 address: " + ipv4Address);
|
|
|
188
|
+ }
|
|
|
189
|
+
|
|
|
190
|
+ return bytes;
|
|
|
191
|
+ }
|
|
|
192
|
+
|
|
|
193
|
+ /**
|
|
|
194
|
+ * The NAT64 prefix added to construct IPv6 from an IPv4 address.
|
|
|
195
|
+ */
|
|
|
196
|
+ private final String prefix;
|
|
|
197
|
+
|
|
|
198
|
+ /**
|
|
|
199
|
+ * The NAT64 suffix (if any) used to construct IPv6 from an IPv4 address.
|
|
|
200
|
+ */
|
|
|
201
|
+ private final String suffix;
|
|
|
202
|
+
|
|
|
203
|
+ /**
|
|
|
204
|
+ * Creates new instance of {@link NAT64AddrInfo}.
|
|
|
205
|
+ *
|
|
|
206
|
+ * @param prefix the NAT64 prefix.
|
|
|
207
|
+ * @param suffix the NAT64 suffix.
|
|
|
208
|
+ */
|
|
|
209
|
+ private NAT64AddrInfo(String prefix, String suffix) {
|
|
|
210
|
+ this.prefix = prefix;
|
|
|
211
|
+ this.suffix = suffix;
|
|
|
212
|
+ }
|
|
|
213
|
+
|
|
|
214
|
+ /**
|
|
|
215
|
+ * Based on the NAT64 prefix and suffix will create an IPv6 representation
|
|
|
216
|
+ * of the given IPv4 address.
|
|
|
217
|
+ *
|
|
|
218
|
+ * @param ipv4Address eg. '192.34.2.3'
|
|
|
219
|
+ * @return IPv6 address string eg. FE80:CD00:0000:0CDA:1357:0000:212F:749C
|
|
|
220
|
+ * @throws IllegalArgumentException if given string is not a valid IPv4
|
|
|
221
|
+ * address.
|
|
|
222
|
+ */
|
|
|
223
|
+ public String getIPv6Address(String ipv4Address) {
|
|
|
224
|
+ byte[] ipv4AddressBytes = ipv4AddressStringToBytes(ipv4Address);
|
|
|
225
|
+ StringBuilder newIPv6Str = new StringBuilder();
|
|
|
226
|
+
|
|
|
227
|
+ newIPv6Str.append(prefix);
|
|
|
228
|
+ newIPv6Str.append(bytesToHexString(ipv4AddressBytes));
|
|
|
229
|
+
|
|
|
230
|
+ if (suffix != null) {
|
|
|
231
|
+ // Insert the 'u' octet.
|
|
|
232
|
+ newIPv6Str.insert(16, "00");
|
|
|
233
|
+ newIPv6Str.append(suffix);
|
|
|
234
|
+ }
|
|
|
235
|
+
|
|
|
236
|
+ return hexStringToIPv6String(newIPv6Str);
|
|
|
237
|
+ }
|
|
|
238
|
+}
|