/src/ibis/smartsockets/direct/IPAddressSet.java

https://github.com/interdroid/smartsockets · Java · 571 lines · 249 code · 102 blank · 220 comment · 77 complexity · f058b3fc2cdb76da587b75dfb2bee53d MD5 · raw file

  1. package ibis.smartsockets.direct;
  2. import ibis.smartsockets.util.AddressSorter;
  3. import ibis.smartsockets.util.InetAddressCache;
  4. import ibis.smartsockets.util.NetworkUtils;
  5. import java.io.Serializable;
  6. import java.net.InetAddress;
  7. import java.net.UnknownHostException;
  8. import java.util.Arrays;
  9. import java.util.StringTokenizer;
  10. /**
  11. * This class represents an set of IP addresses.
  12. * <p>
  13. * Many machines found in Grid systems contain multiple network interfaces and
  14. * can thus be reached using several different IP addresses. Often, each of
  15. * these IP addresses can only be reached from a certain range of source IPs.
  16. * Therefore, using only a single IP address to identify a machine may be
  17. * insufficient to reach it in all cases. This may result in problems,
  18. * especially when IP addresses are forwarded from one machine to another.
  19. * <p>
  20. * Example:
  21. * <p>
  22. * A cluster that consists of a frontend machine and a number of compute nodes.
  23. * The frontend is connected to two networks; the internet using a 'public' IP
  24. * address and the local network using a 'site local' address. The compute nodes
  25. * are only connected to the local network and only have a 'site local' address.
  26. * Therefore, to reach the frontend machine, either the 'public' or 'site local'
  27. * address must be used, depending on the location of the machine that is trying
  28. * to connect to the server.
  29. * <p>
  30. * This class encapsulates a set of IP addresses and is used to represent
  31. * all potential contact addresses of a single machine.
  32. *
  33. * @author Jason Maassen
  34. * @version 1.0 Dec 19, 2005
  35. * @since 1.0
  36. *
  37. */
  38. public final class IPAddressSet implements Serializable {
  39. private static final long serialVersionUID = 8548119455369383377L;
  40. // Size of the byte representations of 'standard' InetAddresses.
  41. private static final int LENGTH_IPv4 = 4;
  42. private static final int LENGTH_IPv6 = 16;
  43. // A object capable of sorting 'standard' InetAddresses.
  44. protected static final AddressSorter SORTER = new AddressSorter();
  45. // Cache for the InetXAddress representing this machine.
  46. private static IPAddressSet localHost;
  47. // The byte representation of this InetAddressSet.
  48. private transient byte[] codedForm;
  49. // The actual InetAddresses
  50. protected final InetAddress[] addresses;
  51. // The UUID (only used if the addresses are all private).
  52. protected final byte[] UUID;
  53. private IPAddressSet(InetAddress[] addresses, byte[] UUID, byte[] codedForm) {
  54. this.addresses = addresses;
  55. this.UUID = UUID;
  56. this.codedForm = codedForm;
  57. }
  58. private IPAddressSet(InetAddress[] addresses) {
  59. this(addresses, null, null);
  60. // Note: codeForm will be created on demand.
  61. }
  62. private IPAddressSet(InetAddress[] addresses, byte[] UUID) {
  63. this(addresses, UUID, null);
  64. }
  65. /**
  66. * Returns a byte representation of this InetAddressSet.
  67. *
  68. * This representation is either contains the 4 or 16 bytes of a single
  69. * InetAddress, or it has the form (NM (SA)* (U)*) where:
  70. *
  71. * N is the number of addresses that follow (1 byte) M is a flag that
  72. * indicates if the U field is used (0 or 1) S is the length of the next
  73. * address (1 byte) A is an InetAddress (4 or 16 bytes) U is the UUID of the
  74. * machine (16 bytes)
  75. *
  76. * @return the bytes
  77. */
  78. public byte[] getAddress() {
  79. if (codedForm == null) {
  80. if (addresses.length == 1 && UUID == null) {
  81. // It there is just a single address and no UUID, so we directly
  82. // return the byte representation of this address.
  83. codedForm = addresses[0].getAddress();
  84. } else {
  85. // There are more addresses and/or a UUID, so we first calucate
  86. // the length of the coded form. Note that this assumes that an
  87. // InetAddressSet always contains less than 256 addresses, and
  88. // each address is shorter that 256 bytes.
  89. int len = 2;
  90. for (int i = 0; i < addresses.length; i++) {
  91. len += 1 + addresses[i].getAddress().length;
  92. }
  93. if (UUID != null) {
  94. len += 16;
  95. }
  96. // When we get a combination which has the length of a
  97. // IPV6 address, we add a padding byte...
  98. if (len == LENGTH_IPv6) {
  99. len += 1;
  100. }
  101. // We now know the size, so create the array and fill it.
  102. codedForm = new byte[len];
  103. int index = 0;
  104. codedForm[index++] = (byte) addresses.length;
  105. codedForm[index++] = (byte) (UUID == null ? 0 : 1);
  106. for (int i = 0; i < addresses.length; i++) {
  107. byte[] tmp = addresses[i].getAddress();
  108. codedForm[index++] = (byte) tmp.length;
  109. System.arraycopy(tmp, 0, codedForm, index, tmp.length);
  110. index += tmp.length;
  111. }
  112. if (UUID != null) {
  113. System.arraycopy(UUID, 0, codedForm, index, UUID.length);
  114. }
  115. }
  116. }
  117. return codedForm.clone();
  118. }
  119. /**
  120. * Returns an array of all InetAddresses encapsulated by this object.
  121. *
  122. * @return array of InetAddresses.
  123. */
  124. public InetAddress[] getAddresses() {
  125. return addresses.clone();
  126. }
  127. /**
  128. * Checks if this IPAddressSet contains at least one public InetAddress.
  129. *
  130. * @return true if this IPAddressSet contains at least one public address,
  131. * false otherwise.
  132. */
  133. public boolean containsPublicAddress() {
  134. return NetworkUtils.containsGlobalAddress(addresses);
  135. }
  136. /**
  137. * Checks if this IPAddressSet contains a UUID.
  138. *
  139. * @return true if this IPAddressSet contains a UUID, false otherwise.
  140. */
  141. public boolean containsUUID() {
  142. return (UUID != null);
  143. }
  144. /*
  145. * (non-Javadoc)
  146. *
  147. * @see java.net.InetAddress#hashCode()
  148. */
  149. public int hashCode() {
  150. int code = 0;
  151. for (int i = 0; i < addresses.length; i++) {
  152. code ^= addresses[i].hashCode();
  153. }
  154. return code;
  155. }
  156. /*
  157. * (non-Javadoc)
  158. *
  159. * @see java.net.InetAddress#equals(java.lang.Object)
  160. */
  161. public boolean equals(Object other) {
  162. // Check pointers
  163. if (this == other) {
  164. return true;
  165. }
  166. // Check type.
  167. if (!(other instanceof IPAddressSet)) {
  168. return false;
  169. }
  170. IPAddressSet tmp = (IPAddressSet) other;
  171. // Compare addresses. Note that the length/order should be the same.
  172. if (!Arrays.equals(addresses, tmp.addresses)) {
  173. return false;
  174. }
  175. // Finally compare the UUID
  176. if (UUID == null && tmp.UUID == null) {
  177. return true;
  178. } else {
  179. return Arrays.equals(UUID, tmp.UUID);
  180. }
  181. }
  182. /*
  183. * (non-Javadoc)
  184. *
  185. * @see java.net.InetAddress#toString()
  186. */
  187. public String toString() {
  188. StringBuilder tmp = new StringBuilder("");
  189. for (int i = 0; i < addresses.length; i++) {
  190. tmp.append(NetworkUtils.ipToString(addresses[i]));
  191. if (i != addresses.length - 1) {
  192. tmp.append("/");
  193. }
  194. }
  195. if (UUID != null) {
  196. tmp.append("#");
  197. tmp.append(NetworkUtils.UUIDToString(UUID));
  198. }
  199. return tmp.toString();
  200. }
  201. /**
  202. * Create a new InetAddressSet by adding an InetAddress to an existing one.
  203. *
  204. * @param address
  205. * source InetAddressSet
  206. * @param add
  207. * InetAddress to add
  208. * @return new InetAddressSet
  209. */
  210. public static IPAddressSet add(IPAddressSet address, InetAddress add) {
  211. int len = address.addresses.length;
  212. InetAddress[] tmp = new InetAddress[len + 1];
  213. tmp[0] = add;
  214. System.arraycopy(address.addresses, 0, tmp, 1, len);
  215. return getFromAddress(tmp);
  216. }
  217. /**
  218. * Create a new InetAddressSet by combing two existing ones.
  219. *
  220. * @param a1
  221. * source InetAddressSet
  222. * @param a2
  223. * source InetAddressSet
  224. * @return new InetAddressSet containing the combination of the two.
  225. */
  226. public static IPAddressSet merge(IPAddressSet a1, IPAddressSet a2) {
  227. if (a1 == null) {
  228. return a2;
  229. }
  230. if (a2 == null) {
  231. return a1;
  232. }
  233. InetAddress[] tmp = new InetAddress[a1.addresses.length
  234. + a2.addresses.length];
  235. System.arraycopy(a1.addresses, 0, tmp, 0, a1.addresses.length);
  236. System.arraycopy(a2.addresses, 0, tmp, a1.addresses.length,
  237. a2.addresses.length);
  238. IPAddressSet result = getFromAddress(tmp);
  239. if (a1.UUID != null) {
  240. result = merge(result, a1.UUID);
  241. } else if (a2.UUID != null) {
  242. result = merge(result, a2.UUID);
  243. }
  244. return result;
  245. }
  246. /**
  247. * Create a new InetAddressSet by combing an existing one and an
  248. * InetAddress.
  249. *
  250. * @param a1
  251. * source InetAddressSet
  252. * @param a2
  253. * source InetAddress
  254. * @return new InetAddressSet containing the combination of the two.
  255. */
  256. public static IPAddressSet merge(IPAddressSet a1, InetAddress a2) {
  257. if (a1 == null && a2 != null) {
  258. return new IPAddressSet(new InetAddress[] { a2 });
  259. }
  260. if (a2 == null) {
  261. return a1;
  262. }
  263. InetAddress[] tmp = new InetAddress[a1.addresses.length + 1];
  264. System.arraycopy(a1.addresses, 0, tmp, 0, a1.addresses.length);
  265. tmp[tmp.length - 1] = a2;
  266. IPAddressSet result = getFromAddress(tmp);
  267. if (a1.UUID != null) {
  268. result = merge(result, a1.UUID);
  269. }
  270. return result;
  271. }
  272. /**
  273. * Create a new IPAddressSet by combing an existing one and a UUID
  274. *
  275. * @param a
  276. * source IPAddressSet
  277. * @param uuid
  278. * source UUID
  279. * @return new IPAddressSet containing the combination of the two.
  280. */
  281. public static IPAddressSet merge(IPAddressSet a, byte[] uuid) {
  282. return new IPAddressSet(a.addresses, uuid, null);
  283. }
  284. /**
  285. * Create a new InetAddressSet from a byte array.
  286. *
  287. * The byte array may either contain the byte representation of an
  288. * InetAddress (IPv4 or IPv6) or the byte representation of an
  289. * InetAddressSet which has the form (NM (SA)* (U)*) where:
  290. *
  291. * N is the number of addresses that follow (1 byte) M is a flag that
  292. * indicates if the U field is used (0 or 1) S is the length of the next
  293. * address (1 byte) A is an InetAddress (4 or 16 bytes) U is the UUID of the
  294. * machine (16 bytes)
  295. *
  296. * @param bytes
  297. * input byte array
  298. * @return new InetAddressSet
  299. * @throws UnknownHostException
  300. */
  301. public static IPAddressSet getByAddress(byte[] bytes)
  302. throws UnknownHostException {
  303. return getByAddress(bytes, 0, bytes.length);
  304. }
  305. public static IPAddressSet getByAddress(byte[] bytes, int off, int len)
  306. throws UnknownHostException {
  307. InetAddress[] addresses;
  308. byte[] uuid = null;
  309. if (len == LENGTH_IPv4 || len == LENGTH_IPv6) {
  310. // the byte [] contains a 'normal' InetAddress
  311. addresses = new InetAddress[1];
  312. byte[] tmp = new byte[len];
  313. System.arraycopy(bytes, off, tmp, 0, len);
  314. // addresses[0] = InetAddress.getByAddress(tmp);
  315. // -- roelof, the above method doesn't work on Android
  316. String ipAddress = "" + (tmp[0] & 0xFF) + "." + (tmp[1] & 0xFF)
  317. + "." + (tmp[2] & 0xFF) + "." + (tmp[3] & 0xFF);
  318. addresses[0] = InetAddressCache.getByName(ipAddress);
  319. } else {
  320. // the byte [] contains a 'extended' InetAddress
  321. int index = 0;
  322. int length = bytes[index++];
  323. boolean hasUUID = (bytes[index++] != 0);
  324. addresses = new InetAddress[length];
  325. byte[] tmp = null;
  326. for (int i = 0; i < addresses.length; i++) {
  327. int size = bytes[index++];
  328. if (tmp == null || tmp.length != size) {
  329. tmp = new byte[size];
  330. }
  331. System.arraycopy(bytes, index, tmp, 0, size);
  332. // addresses[i] = InetAddress.getByAddress(tmp);
  333. // -- roelof, the above method doesn't work on Android
  334. String ipAddress = "" + (tmp[0] & 0xFF) + "." + (tmp[1] & 0xFF)
  335. + "." + (tmp[2] & 0xFF) + "." + (tmp[3] & 0xFF);
  336. addresses[i] = InetAddress.getByName(ipAddress);
  337. index += size;
  338. }
  339. if (hasUUID) {
  340. uuid = new byte[16];
  341. System.arraycopy(bytes, index, uuid, 0, 16);
  342. }
  343. }
  344. return new IPAddressSet(addresses, uuid, bytes);
  345. }
  346. /**
  347. * Create a new InetAddressSet from a String.
  348. *
  349. * The String must have the form
  350. *
  351. * A ('/'A)* ['#'U]
  352. *
  353. * where: A is a String representation of a InetAddress, and U a UUID
  354. *
  355. * @param address
  356. * the InetAddressSet as a String
  357. * @return new InetAddressSet
  358. * @throws UnknownHostException
  359. */
  360. public static IPAddressSet getFromString(String address)
  361. throws UnknownHostException {
  362. StringTokenizer st = new StringTokenizer(address, "/#", false);
  363. int len = st.countTokens();
  364. boolean hasUUID = (address.indexOf('#') != -1);
  365. if (hasUUID) {
  366. len--;
  367. }
  368. InetAddress[] addresses = new InetAddress[len];
  369. for (int i = 0; i < len; i++) {
  370. addresses[i] = InetAddressCache.getByName(st.nextToken());
  371. }
  372. byte[] uuid = null;
  373. if (hasUUID) {
  374. uuid = NetworkUtils.stringToUUID(st.nextToken());
  375. }
  376. return new IPAddressSet(sort(addresses), uuid);
  377. }
  378. /**
  379. * Create a new InetAddressSet from an array of InetAddress objects.
  380. *
  381. * @param addresses
  382. * the InetAddresses
  383. * @return new InetAddressSet
  384. */
  385. public static IPAddressSet getFromAddress(InetAddress[] addresses) {
  386. return new IPAddressSet(sort(addresses));
  387. }
  388. /**
  389. * Create a new InetAddressSet from a single InetAddress object.
  390. *
  391. * @param address
  392. * the InetAddress
  393. * @return new InetAddressSet
  394. */
  395. public static IPAddressSet getFromAddress(InetAddress address) {
  396. return new IPAddressSet(new InetAddress[] { address });
  397. }
  398. /**
  399. * Create a new InetAddressSet that represents this host.
  400. *
  401. * All addresses that can be found locally will be included in the
  402. * InetAddressSet. Note that this does not necessarilly include the
  403. * 'external' address of the network when NAT is used.
  404. *
  405. * @return new InetAddressSet representing this host.
  406. */
  407. public static IPAddressSet getLocalHost() {
  408. return getLocalHost(true);
  409. }
  410. /**
  411. * Create a new InetAddressSet that represents this host.
  412. *
  413. * All addresses that can be found locally will be included in the
  414. * InetAddressSet. Note that this does not necessarilly include the
  415. * 'external' address of the network when NAT is used.
  416. *
  417. * @param cacheIPaddress
  418. * if set to false, the IPAddressSet will be refreshed upon every
  419. * call, otherwise the result will be cached.
  420. *
  421. * @return new InetAddressSet representing this host.
  422. */
  423. public static IPAddressSet getLocalHost(boolean cacheIPaddress) {
  424. if (localHost == null || !cacheIPaddress) {
  425. // Get all the local addresses, including IPv6 ones, but excluding
  426. // loopback addresses, and sort them.
  427. // TODO: removed ipv6 for now!!
  428. InetAddress[] addresses = NetworkUtils.getAllHostAddresses(true,
  429. true);
  430. if (addresses == null || addresses.length == 0) {
  431. // Oh dear, we don't have a network... Let's see if there is
  432. // a loopback available...
  433. // TODO: removed ipv6 for now!!
  434. addresses = NetworkUtils.getAllHostAddresses(false, true);
  435. }
  436. addresses = sort(addresses);
  437. DirectSocketFactory.logger.info("Result after sorting: ");
  438. DirectSocketFactory.logger.info(" "
  439. + NetworkUtils.ipToString(addresses));
  440. if (!NetworkUtils.containsGlobalAddress(addresses)) {
  441. DirectSocketFactory.logger.info(" Result does NOT contain "
  442. + "public address!");
  443. // TODO: Try to find the external address here ?
  444. } else {
  445. DirectSocketFactory.logger.info(" Result contains public "
  446. + "address!");
  447. }
  448. localHost = new IPAddressSet(addresses);
  449. }
  450. return localHost;
  451. }
  452. /**
  453. * Sorts an array of InetAddress object according to the order defined by
  454. * the InetAddressSorter.
  455. *
  456. * Note that this method changes the content of the parameter array and, for
  457. * convenience, also returns a reference to this array.
  458. *
  459. * @param in
  460. * the array that must be sorted.
  461. * @return reference to the sorted array parameter.
  462. */
  463. private static InetAddress[] sort(InetAddress[] in) {
  464. if (in != null && in.length > 1) {
  465. Arrays.sort(in, SORTER);
  466. }
  467. return in;
  468. }
  469. }