OpenShot Library | OpenShotAudio  0.2.2
juce_IPAddress.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 /** Union used to split a 16-bit unsigned integer into 2 8-bit unsigned integers or vice-versa */
28 {
29  uint16 combined;
30  uint8 split[2];
31 };
32 
33 static void zeroUnusedBytes (uint8* address) noexcept
34 {
35  for (int i = 4; i < 16; ++i)
36  address[i] = 0;
37 }
38 
40 {
41  for (int i = 0; i < 16; ++i)
42  address[i] = 0;
43 }
44 
45 IPAddress::IPAddress (const uint8 bytes[], bool IPv6) noexcept : isIPv6 (IPv6)
46 {
47  for (int i = 0; i < (isIPv6 ? 16 : 4); ++i)
48  address[i] = bytes[i];
49 
50  if (! isIPv6)
51  zeroUnusedBytes (address);
52 }
53 
54 IPAddress::IPAddress (const uint16 bytes[8]) noexcept : isIPv6 (true)
55 {
56  IPAddressByteUnion temp;
57 
58  for (int i = 0; i < 8; ++i)
59  {
60  temp.combined = bytes[i];
61 
62  address[i * 2] = temp.split[0];
63  address[i * 2 + 1] = temp.split[1];
64  }
65 }
66 
67 IPAddress::IPAddress (uint8 a0, uint8 a1, uint8 a2, uint8 a3) noexcept : isIPv6 (false)
68 {
69  address[0] = a0; address[1] = a1;
70  address[2] = a2; address[3] = a3;
71 
72  zeroUnusedBytes (address);
73 }
74 
75 IPAddress::IPAddress (uint16 a1, uint16 a2, uint16 a3, uint16 a4,
76  uint16 a5, uint16 a6, uint16 a7, uint16 a8) noexcept : isIPv6 (true)
77 
78 {
79  uint16 array[8] = { a1, a2, a3, a4, a5, a6, a7, a8 };
80 
81  IPAddressByteUnion temp;
82 
83  for (int i = 0; i < 8; ++i)
84  {
85  temp.combined = array[i];
86  address[i * 2] = temp.split[0];
87  address[i * 2 + 1] = temp.split[1];
88  }
89 }
90 
91 IPAddress::IPAddress (uint32 n) noexcept : isIPv6 (false)
92 {
93  address[0] = (n >> 24);
94  address[1] = (n >> 16) & 255;
95  address[2] = (n >> 8) & 255;
96  address[3] = (n & 255);
97 
98  zeroUnusedBytes (address);
99 }
100 
101 bool IPAddress::isNull() const
102 {
103  for (int i = 0; i < 16; ++i)
104  if (address[i] != 0)
105  return false;
106 
107  return true;
108 }
109 
110 static String removePort (const String& adr)
111 {
112  if (adr.containsAnyOf ("[]"))
113  return adr.fromFirstOccurrenceOf ("[", false, true).upToLastOccurrenceOf ("]", false, true);
114 
115  if (adr.indexOf (":") == adr.lastIndexOf (":"))
116  return adr.upToLastOccurrenceOf (":", false, true);
117 
118  return adr;
119 }
120 
122 {
123  auto ipAddress = removePort (adr);
124 
125  isIPv6 = ipAddress.contains (":");
126 
127  if (! isIPv6)
128  {
129  auto tokens = StringArray::fromTokens (ipAddress, ".", {});
130 
131  for (int i = 0; i < 4; ++i)
132  address[i] = (uint8) tokens[i].getIntValue();
133 
134  zeroUnusedBytes (address);
135  }
136  else
137  {
138  auto tokens = StringArray::fromTokens (ipAddress, ":", {});
139 
140  if (tokens.contains ({})) // if :: shorthand has been used
141  {
142  auto idx = tokens.indexOf ({});
143  tokens.set (idx, "0");
144  tokens.removeEmptyStrings();
145 
146  // mapped IPv4 address will be treated as a single token, so pad the end of the StringArray
147  if (tokens[tokens.size() - 1].containsChar ('.'))
148  tokens.add ({});
149 
150  while (tokens.size() < 8)
151  tokens.insert (idx, "0");
152  }
153 
154  for (int i = 0; i < 8; ++i)
155  {
156  if (i == 6 && isIPv4MappedAddress (IPAddress (address, true)))
157  {
158  IPAddress v4Address (tokens[i]);
159 
160  address[12] = v4Address.address[0];
161  address[13] = v4Address.address[1];
162  address[14] = v4Address.address[2];
163  address[15] = v4Address.address[3];
164 
165  break;
166  }
167 
168  IPAddressByteUnion temp;
169  temp.combined = (uint16) CharacterFunctions::HexParser<int>::parse (tokens[i].getCharPointer());
170 
171  address[i * 2] = temp.split[0];
172  address[i * 2 + 1] = temp.split[1];
173  }
174  }
175 }
176 
178 {
179  if (! isIPv6)
180  {
181  String s ((int) address[0]);
182 
183  for (int i = 1; i < 4; ++i)
184  s << '.' << (int) address[i];
185 
186  return s;
187  }
188 
189  IPAddressByteUnion temp;
190 
191  temp.split[0] = address[0];
192  temp.split[1] = address[1];
193 
194  auto addressString = String::toHexString (temp.combined);
195 
196  for (int i = 1; i < 8; ++i)
197  {
198  temp.split[0] = address[i * 2];
199  temp.split[1] = address[i * 2 + 1];
200 
201  addressString << ':' << String::toHexString (temp.combined);
202  }
203 
204  return getFormattedAddress (addressString);
205 }
206 
207 bool IPAddress::operator== (const IPAddress& other) const noexcept { return compare (other) == 0; }
208 bool IPAddress::operator!= (const IPAddress& other) const noexcept { return compare (other) != 0; }
209 bool IPAddress::operator< (const IPAddress& other) const noexcept { return compare (other) < 0; }
210 bool IPAddress::operator<= (const IPAddress& other) const noexcept { return compare (other) <= 0; }
211 bool IPAddress::operator> (const IPAddress& other) const noexcept { return compare (other) > 0; }
212 bool IPAddress::operator>= (const IPAddress& other) const noexcept { return compare (other) >= 0; }
213 
214 int IPAddress::compare (const IPAddress& other) const noexcept
215 {
216  if (isIPv6 != other.isIPv6)
217  {
218  if (isIPv6)
219  {
220  if (isIPv4MappedAddress (*this))
221  return convertIPv4MappedAddressToIPv4 (*this).compare (other);
222 
223  return 1;
224  }
225 
226  if (isIPv4MappedAddress (other))
227  return compare (convertIPv4MappedAddressToIPv4 (other));
228 
229  return -1;
230  }
231 
232  for (int i = 0; i < (isIPv6 ? 16 : 4); ++i)
233  {
234  if (address[i] > other.address[i]) return 1;
235  if (address[i] < other.address[i]) return -1;
236  }
237 
238  return 0;
239 }
240 
241 IPAddress IPAddress::any() noexcept { return IPAddress(); }
242 IPAddress IPAddress::broadcast() noexcept { return IPAddress (255, 255, 255, 255); }
243 IPAddress IPAddress::local (bool IPv6) noexcept { return IPv6 ? IPAddress (0, 0, 0, 0, 0, 0, 0, 1)
244  : IPAddress (127, 0, 0, 1); }
245 
246 String IPAddress::getFormattedAddress (const String& unformattedAddress)
247 {
248  jassert (unformattedAddress.contains (":") && ! unformattedAddress.contains ("::")); // needs to be an unformatted IPv6 address!
249 
250  auto portString = unformattedAddress.fromFirstOccurrenceOf ("]", false, true);
251  auto addressString = unformattedAddress.dropLastCharacters (portString.length()).removeCharacters ("[]");
252 
253  auto tokens = StringArray::fromTokens (addressString, ":", {});
254 
255  int numZeros = 0;
256  int numZerosTemp = 0;
257  bool isFirst = false;
258  bool isLast = false;
259 
260  for (int i = 0; i < tokens.size(); ++i)
261  {
262  auto& t = tokens.getReference (i);
263 
264  if (t.getHexValue32() == 0x0000)
265  {
266  ++numZeros;
267 
268  if (i == 0)
269  isFirst = true;
270  else if (i == tokens.size() - 1 && numZeros > numZerosTemp)
271  isLast = true;
272 
273  if (t.length() > 1)
274  addressString = addressString.replace (String::repeatedString ("0", t.length()), "0");
275 
276  if (isFirst && numZerosTemp != 0 && numZeros > numZerosTemp)
277  isFirst = false;
278  }
279  else
280  {
281  addressString = addressString.replace (t, t.trimCharactersAtStart ("0").toLowerCase());
282 
283  if (numZeros > 0)
284  {
285  if (numZeros > numZerosTemp)
286  numZerosTemp = numZeros;
287 
288  numZeros = 0;
289  }
290  }
291  }
292 
293  if (numZerosTemp > numZeros)
294  numZeros = numZerosTemp;
295 
296  if (numZeros > 1)
297  {
298  if (numZeros == tokens.size())
299  {
300  addressString = "::,";
301  }
302  else
303  {
304  auto zeroString = isFirst ? "0" + String::repeatedString (":0", numZeros - 1)
305  : String::repeatedString (":0", numZeros);
306 
307  addressString = addressString.replaceFirstOccurrenceOf (zeroString, ":");
308 
309  if (isLast)
310  addressString << ':';
311  }
312  }
313 
314  if (portString.isNotEmpty())
315  addressString = "[" + addressString + "]" + portString;
316 
317  return addressString;
318 }
319 
320 bool IPAddress::isIPv4MappedAddress (const IPAddress& mappedAddress)
321 {
322  if (! mappedAddress.isIPv6)
323  return false;
324 
325  for (int i = 0; i < 10; ++i)
326  if (mappedAddress.address[i] != 0)
327  return false;
328 
329  if (mappedAddress.address[10] != 255 || mappedAddress.address[11] != 255)
330  return false;
331 
332  return true;
333 }
334 
336 {
337  // The address that you're converting needs to be IPv6!
338  jassert (mappedAddress.isIPv6);
339 
340  if (isIPv4MappedAddress (mappedAddress))
341  return { mappedAddress.address[12], mappedAddress.address[13],
342  mappedAddress.address[14], mappedAddress.address[15] };
343 
344  return {};
345 }
346 
348 {
349  // The address that you're converting needs to be IPv4!
350  jassert (! addressToMap.isIPv6);
351 
352  return { 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff,
353  static_cast<uint16> ((addressToMap.address[0] << 8) | addressToMap.address[1]),
354  static_cast<uint16> ((addressToMap.address[2] << 8) | addressToMap.address[3]) };
355 }
356 
358 {
359  auto addresses = getAllAddresses (includeIPv6);
360 
361  for (auto& a : addresses)
362  if (a != local())
363  return a;
364 
365  return local();
366 }
367 
369 {
370  Array<IPAddress> addresses;
371  findAllAddresses (addresses, includeIPv6);
372  return addresses;
373 }
374 
375 
376 //==============================================================================
377 //==============================================================================
378 #if JUCE_UNIT_TESTS
379 
380 struct IPAddressTests : public UnitTest
381 {
382  IPAddressTests()
383  : UnitTest ("IPAddress", UnitTestCategories::networking)
384  {}
385 
386  void runTest() override
387  {
388  testConstructors();
389  testFindAllAddresses();
390  testFindBroadcastAddress();
391  }
392 
393  void testConstructors()
394  {
395  beginTest ("constructors");
396 
397  // Default IPAdress should be null
398  IPAddress defaultConstructed;
399  expect (defaultConstructed.isNull());
400 
401  auto local = IPAddress::local();
402  expect (! local.isNull());
403 
404  IPAddress ipv4{1, 2, 3, 4};
405  expect (! ipv4.isNull());
406  expect (! ipv4.isIPv6);
407  expect (ipv4.toString() == "1.2.3.4");
408  }
409 
410  void testFindAllAddresses()
411  {
412  beginTest ("find all addresses");
413 
414  Array<IPAddress> ipv4Addresses;
415  Array<IPAddress> allAddresses;
416 
417  IPAddress::findAllAddresses (ipv4Addresses, false);
418  IPAddress::findAllAddresses (allAddresses, true);
419 
420  expect (allAddresses.size() >= ipv4Addresses.size());
421 
422  for (auto& a : ipv4Addresses)
423  {
424  expect (! a.isNull());
425  expect (! a.isIPv6);
426  }
427 
428  for (auto& a : allAddresses)
429  {
430  expect (! a.isNull());
431  }
432  }
433 
434  void testFindBroadcastAddress()
435  {
436  beginTest ("broadcast addresses");
437 
438  Array<IPAddress> addresses;
439 
440  // Only IPv4 interfaces have broadcast
441  IPAddress::findAllAddresses (addresses, false);
442 
443  for (auto& a : addresses)
444  {
445  expect (! a.isNull());
446 
447  auto broadcastAddress = IPAddress::getInterfaceBroadcastAddress (a);
448 
449  // If we retrieve an address, it should be an IPv4 address
450  if (! broadcastAddress.isNull())
451  {
452  expect (! a.isIPv6);
453  }
454  }
455 
456  // Expect to fail to find a broadcast for this address
457  IPAddress address{1, 2, 3, 4};
458  expect (IPAddress::getInterfaceBroadcastAddress (address).isNull());
459  }
460 };
461 
462 static IPAddressTests iPAddressTests;
463 
464 #endif
465 
466 } // namespace juce
String toString() const
Returns a dot- or colon-separated string in the form "1.2.3.4" (IPv4) or "1:2:3:4:5:6:7:8" (IPv6)...
Union used to split a 16-bit unsigned integer into 2 8-bit unsigned integers or vice-versa.
static IPAddress convertIPv4AddressToIPv4Mapped(const IPAddress &addressToMap)
Converts an IPv4 address to an IPv4-mapped IPv6 address.
static String getFormattedAddress(const String &unformattedAddress)
Returns a formatted version of the provided IPv6 address conforming to RFC 5952 with leading zeros su...
String fromFirstOccurrenceOf(StringRef substringToStartFrom, bool includeSubStringInResult, bool ignoreCase) const
Returns a section of the string starting from a given substring.
Represents an IP address.
static IPAddress broadcast() noexcept
Returns an IPv4 address meaning "broadcast" (255.255.255.255)
int lastIndexOf(StringRef textToLookFor) const noexcept
Searches for a substring inside this string (working backwards from the end of the string)...
String dropLastCharacters(int numberToDrop) const
Returns a version of this string with a number of characters removed from the end.
IPAddress() noexcept
Creates a null address - 0.0.0.0 (IPv4) or ::, (IPv6)
The JUCE String class!
Definition: juce_String.h:42
static bool isIPv4MappedAddress(const IPAddress &mappedAddress)
Returns true if the given IP address is an IPv4-mapped IPv6 address.
This is a base class for classes that perform a unit test.
Definition: juce_UnitTest.h:73
String & getReference(int index) noexcept
Returns a reference to one of the strings in the array.
int indexOf(StringRef stringToLookFor, bool ignoreCase=false, int startIndex=0) const
Searches for a string in the array.
uint8 address[16]
The elements of the IP address.
static String repeatedString(StringRef stringToRepeat, int numberOfTimesToRepeat)
Creates a string which is a version of a string repeated and joined together.
bool containsAnyOf(StringRef charactersItMightContain) const noexcept
Looks for any of a set of characters in the string.
static IPAddress convertIPv4MappedAddressToIPv4(const IPAddress &mappedAddress)
Converts an IPv4-mapped IPv6 address to an IPv4 address.
bool contains(StringRef text) const noexcept
Tests whether the string contains another substring.
bool isNull() const
Returns whether the address contains the null address (e.g.
static void findAllAddresses(Array< IPAddress > &results, bool includeIPv6=false)
Populates a list of all the IP addresses that this machine is using.
static String toHexString(IntegerType number)
Returns a string representing this numeric value in hexadecimal.
Definition: juce_String.h:1057
String upToLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
Returns the start of this string, up to the last occurrence of a substring.
static StringArray fromTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
Returns an array containing the tokens in a given string.
static Array< IPAddress > getAllAddresses(bool includeIPv6=false)
Populates a list of all the IP addresses that this machine is using.
int indexOf(StringRef textToLookFor) const noexcept
Searches for a substring within this string.
Holds a resizable array of primitive or copy-by-value objects.
Definition: juce_Array.h:59
int size() const noexcept
Returns the current number of elements in the array.
Definition: juce_Array.h:219
static IPAddress getLocalAddress(bool includeIPv6=false)
Returns the first &#39;real&#39; address for the local machine.
String replace(StringRef stringToReplace, StringRef stringToInsertInstead, bool ignoreCase=false) const
Replaces all occurrences of a substring with another string.
static IPAddress any() noexcept
Returns an IP address meaning "any", equivalent to 0.0.0.0 (IPv4) or ::, (IPv6)
static IPAddress local(bool IPv6=false) noexcept
Returns an IPv4 or IPv6 address meaning "localhost", equivalent to 127.0.0.1 (IPv4) or ::1 (IPv6) ...
int compare(const IPAddress &) const noexcept
Compares this IPAddress with another.
static IPAddress getInterfaceBroadcastAddress(const IPAddress &interfaceAddress)
If the IPAdress is the address of an interface on the machine, returns the associated broadcast addre...
Parses a character string, to read a hexadecimal value.