/src/ibis/smartsockets/direct/DirectSocketAddress.java

https://github.com/interdroid/smartsockets · Java · 1544 lines · 891 code · 325 blank · 328 comment · 244 complexity · 77b3859d7c4aca2d9020c9f5a83ec3d0 MD5 · raw file

  1. package ibis.smartsockets.direct;
  2. import ibis.smartsockets.util.InetAddressCache;
  3. import ibis.smartsockets.util.MalformedAddressException;
  4. import ibis.smartsockets.util.NetworkUtils;
  5. import java.io.DataInput;
  6. import java.io.DataInputStream;
  7. import java.io.DataOutput;
  8. import java.io.IOException;
  9. import java.io.ObjectInputStream;
  10. import java.io.ObjectOutputStream;
  11. import java.net.Inet4Address;
  12. import java.net.InetAddress;
  13. import java.net.InetSocketAddress;
  14. import java.net.SocketAddress;
  15. import java.net.UnknownHostException;
  16. import java.util.ArrayList;
  17. import java.util.Arrays;
  18. import java.util.LinkedList;
  19. import java.util.StringTokenizer;
  20. /**
  21. * This class implements a multi SocketAddress (any number of IP addresses and
  22. * port numbers).
  23. *
  24. * It provides an immutable object used by SmartSockets for binding, connecting,
  25. * or as returned values.
  26. *
  27. * @author Jason Maassen
  28. * @version 1.0 Dec 19, 2005
  29. * @since 1.0
  30. */
  31. public class DirectSocketAddress extends SocketAddress
  32. implements Comparable<DirectSocketAddress> {
  33. private static final long serialVersionUID = -2662260670251814982L;
  34. private static final char IP_PORT_SEPERATOR = '-';
  35. private static final char ADDRESS_SEPERATOR = '/';
  36. private static final char USER_SEPERATOR = '~';
  37. private static final char UUID_SEPERATOR = '#';
  38. private static final char EXTERNAL_START = '{';
  39. private static final char EXTERNAL_END = '}';
  40. // Should contain all of the above!
  41. private static final String SEPARATORS = "{}-/~#";
  42. private transient InetSocketAddress[] externalAds;
  43. private transient InetSocketAddress[] publicAds;
  44. private transient InetSocketAddress[] privateAds;
  45. private transient byte[] UUID;
  46. // Unfortunately, this is the least that is required for SSH-tunneling...
  47. private transient String user;
  48. private transient int hashCode = 0;
  49. private transient byte[] codedForm = null;
  50. private transient String toStringCache = null;
  51. private transient IPAddressSet addressCache;
  52. private transient InetSocketAddress[] allAddressesCache;
  53. private DirectSocketAddress(InetSocketAddress[] externalAds,
  54. InetSocketAddress[] publicAds, InetSocketAddress[] privateAds,
  55. byte[] UUID, String user) {
  56. this.externalAds = (externalAds == null ? new InetSocketAddress[0]
  57. : externalAds);
  58. this.publicAds = (publicAds == null ? new InetSocketAddress[0]
  59. : publicAds);
  60. this.privateAds = (privateAds == null ? new InetSocketAddress[0]
  61. : privateAds);
  62. this.UUID = UUID;
  63. this.user = user;
  64. }
  65. /**
  66. * Construct a new IbisSocketAddress, starting from a byte encode version
  67. *
  68. * @param address
  69. * The InetSocketAddress.
  70. * @throws UnknownHostException
  71. */
  72. private DirectSocketAddress(byte[] coded, int off)
  73. throws UnknownHostException, MalformedAddressException {
  74. decode(coded, off);
  75. }
  76. private void decode(byte[] coded, int off) throws UnknownHostException,
  77. MalformedAddressException {
  78. try {
  79. int index = off;
  80. externalAds = new InetSocketAddress[coded[index++] & 0xFF];
  81. publicAds = new InetSocketAddress[coded[index++] & 0xFF];
  82. privateAds = new InetSocketAddress[coded[index++] & 0xFF];
  83. int uuidLen = coded[index++] & 0xFF;
  84. int userLen = coded[index++] & 0xFF;
  85. index = decode(externalAds, coded, index);
  86. index = decode(publicAds, coded, index);
  87. index = decode(privateAds, coded, index);
  88. if (uuidLen > 0) {
  89. UUID = new byte[uuidLen];
  90. System.arraycopy(coded, index, UUID, 0, uuidLen);
  91. index += uuidLen;
  92. }
  93. if (userLen > 0) {
  94. user = new String(coded, index, userLen);
  95. }
  96. } catch (UnknownHostException e) {
  97. // pass through
  98. throw e;
  99. } catch (MalformedAddressException e) {
  100. // pass through
  101. throw new MalformedAddressException("Failed to parse address "
  102. + "containing " + externalAds.length + " external, "
  103. + publicAds.length + " public, " + privateAds.length
  104. + " private addresses, coded array has length "
  105. + coded.length + " reading at offset " + off, e);
  106. } catch (Exception e) {
  107. throw new MalformedAddressException("Failed to decode address", e);
  108. }
  109. }
  110. private static int decode(InetSocketAddress[] target, byte[] src, int index)
  111. throws UnknownHostException, MalformedAddressException {
  112. try {
  113. byte[] tmp4 = null;
  114. byte[] tmp16 = null;
  115. for (int i = 0; i < target.length; i++) {
  116. int adlen = src[index++] & 0xFF;
  117. int port = 0;
  118. if (adlen == 4) {
  119. // IPv4
  120. if (tmp4 == null) {
  121. tmp4 = new byte[4];
  122. }
  123. System.arraycopy(src, index, tmp4, 0, 4);
  124. index += 4;
  125. port = (src[index++] & 0xFF);
  126. port |= (src[index++] & 0xFF) << 8;
  127. // target[i] = new InetSocketAddress(
  128. // InetAddress.getByAddress(tmp4), port);
  129. // -- roelof, the above method doesn't work on Android
  130. String ipAddress = "" + (tmp4[0] & 0xFF) + "."
  131. + (tmp4[1] & 0xFF) + "." + (tmp4[2] & 0xFF) + "."
  132. + (tmp4[3] & 0xFF);
  133. target[i] = new InetSocketAddress(InetAddressCache
  134. .getByName(ipAddress), port);
  135. } else if (adlen == 16) {
  136. // IPv6
  137. if (tmp16 == null) {
  138. tmp16 = new byte[16];
  139. }
  140. System.arraycopy(src, index, tmp16, 0, 16);
  141. index += 16;
  142. port = (src[index++] & 0xFF);
  143. port |= (src[index++] & 0xFF) << 8;
  144. target[i] = new InetSocketAddress(InetAddress
  145. .getByAddress(tmp16), port);
  146. } else {
  147. throw new MalformedAddressException("Failed to decode "
  148. + "address of length: " + adlen);
  149. }
  150. // address = IPAddressSet.getFromAddress(tmp);
  151. }
  152. } catch (UnknownHostException e) {
  153. // pass through
  154. throw e;
  155. } catch (MalformedAddressException e) {
  156. // pass through
  157. throw e;
  158. } catch (Exception e) {
  159. throw new MalformedAddressException("Failed to decode address", e);
  160. }
  161. return index;
  162. }
  163. private static int codedSize(InetSocketAddress[] a) {
  164. if (a == null || a.length == 0) {
  165. return 0;
  166. }
  167. int len = 0;
  168. for (InetSocketAddress sa : a) {
  169. if (sa.getAddress() instanceof Inet4Address) {
  170. len += 1 + 4 + 2;
  171. } else {
  172. len += 1 + 16 + 2;
  173. }
  174. }
  175. return len;
  176. }
  177. private static int encode(InetSocketAddress[] a, byte[] dest, int index) {
  178. if (a == null || a.length == 0) {
  179. return index;
  180. }
  181. for (InetSocketAddress sa : a) {
  182. byte[] tmp = sa.getAddress().getAddress();
  183. dest[index++] = (byte) (tmp.length & 0xFF);
  184. System.arraycopy(tmp, 0, dest, index, tmp.length);
  185. index += tmp.length;
  186. int port = sa.getPort();
  187. dest[index++] = (byte) (port & 0xFF);
  188. dest[index++] = (byte) ((port >> 8) & 0xFF);
  189. }
  190. return index;
  191. }
  192. /**
  193. * This method returns the byte coded form of the SocketAddressSet.
  194. *
  195. * This representation is either contains the 6 or 18 bytes of a single
  196. * InetAddress + port number, or it has the form (EGLMN (SAP)* (U)*) where:
  197. *
  198. * E is the number of external addresses that follow (1 byte) G is the
  199. * number of public addresses that follow (1 byte) L is the number of
  200. * private addresses that follow (1 byte) M is the length of the UUID (1
  201. * byte, normally 0 or 16) N is the length of the username (1 byte, 0 if
  202. * unused) S is the length of the next address (1 byte) A is an InetAddress
  203. * (4 or 16 bytes) P is the port number (2 bytes) U is a UUID (16 bytes)
  204. *
  205. * @return the bytes
  206. */
  207. public byte[] getAddress() {
  208. if (codedForm == null) {
  209. byte[] codedUser = null;
  210. // First calculate the size of the address n bytes....
  211. int len = 5;
  212. len += codedSize(externalAds);
  213. len += codedSize(publicAds);
  214. len += codedSize(privateAds);
  215. if (UUID != null) {
  216. len += UUID.length;
  217. }
  218. if (user != null && user.length() > 0) {
  219. codedUser = user.getBytes();
  220. len += codedUser.length;
  221. }
  222. // Now encode it...
  223. codedForm = new byte[len];
  224. int index = 0;
  225. codedForm[index++] = (byte) (externalAds.length & 0xFF);
  226. codedForm[index++] = (byte) (publicAds.length & 0xFF);
  227. codedForm[index++] = (byte) (privateAds.length & 0xFF);
  228. codedForm[index++] = (byte) ((UUID == null ? 0 : UUID.length) & 0xFF);
  229. codedForm[index++] = (byte) ((codedUser == null ? 0
  230. : codedUser.length) & 0xFF);
  231. index = encode(externalAds, codedForm, index);
  232. index = encode(publicAds, codedForm, index);
  233. index = encode(privateAds, codedForm, index);
  234. if (UUID != null) {
  235. System.arraycopy(UUID, 0, codedForm, index, UUID.length);
  236. index += UUID.length;
  237. }
  238. if (user != null && user.length() > 0) {
  239. System.arraycopy(codedUser, 0, codedForm, index,
  240. codedUser.length);
  241. }
  242. }
  243. return codedForm.clone();
  244. }
  245. /**
  246. * Gets the InetAddressSet.
  247. *
  248. * @return the InetAddressSet.
  249. */
  250. public IPAddressSet getAddressSet() {
  251. if (addressCache == null) {
  252. ArrayList<InetAddress> tmp = new ArrayList<InetAddress>();
  253. for (InetSocketAddress a : publicAds) {
  254. tmp.add(a.getAddress());
  255. }
  256. for (InetSocketAddress a : externalAds) {
  257. tmp.add(a.getAddress());
  258. }
  259. for (InetSocketAddress a : privateAds) {
  260. tmp.add(a.getAddress());
  261. }
  262. addressCache = IPAddressSet.getFromAddress(tmp
  263. .toArray(new InetAddress[0]));
  264. }
  265. return addressCache;
  266. }
  267. /**
  268. * Return the port numbers used in this DirectSocketAddress.
  269. *
  270. * @param includeExternal
  271. * should external (NAT) addresses be included ?
  272. * @return the port numbers used in this DirectSocketAddress.
  273. */
  274. public int[] getPorts(boolean includeExternal) {
  275. int len = publicAds.length + privateAds.length;
  276. if (includeExternal) {
  277. len += externalAds.length;
  278. }
  279. int[] result = new int[len];
  280. int index = 0;
  281. for (InetSocketAddress i : publicAds) {
  282. result[index++] = i.getPort();
  283. }
  284. for (InetSocketAddress i : privateAds) {
  285. result[index++] = i.getPort();
  286. }
  287. if (includeExternal) {
  288. for (InetSocketAddress i : externalAds) {
  289. result[index++] = i.getPort();
  290. }
  291. }
  292. return result;
  293. }
  294. /**
  295. * Gets the SocketAddresses.
  296. *
  297. * @return the addresses.
  298. */
  299. protected InetSocketAddress[] getSocketAddresses() {
  300. // TODO: this is a VERY BAD IDEA!!! ONLY USED IN CONNECTION SETUP!!
  301. // MUST REPLACE WITH SOMETHING SMARTER ASAP!!
  302. if (allAddressesCache == null) {
  303. int len = publicAds.length + externalAds.length + privateAds.length;
  304. allAddressesCache = new InetSocketAddress[len];
  305. int index = 0;
  306. System.arraycopy(publicAds, 0, allAddressesCache, index,
  307. publicAds.length);
  308. index += publicAds.length;
  309. System.arraycopy(externalAds, 0, allAddressesCache, index,
  310. externalAds.length);
  311. index += externalAds.length;
  312. System.arraycopy(privateAds, 0, allAddressesCache, index,
  313. privateAds.length);
  314. // index += privateAds.length;
  315. }
  316. return allAddressesCache;
  317. }
  318. /**
  319. * Return the external addresses of this DirectSocketAddress.
  320. *
  321. * @return an array containing the external addresses of this
  322. * DirectSocketAddress
  323. */
  324. public InetSocketAddress[] getExternalAddresses() {
  325. return externalAds.clone();
  326. }
  327. /**
  328. * Return the public addresses of this DirectSocketAddress.
  329. *
  330. * @return an array containing the public addresses of this
  331. * DirectSocketAddress
  332. */
  333. public InetSocketAddress[] getPublicAddresses() {
  334. return publicAds.clone();
  335. }
  336. /**
  337. * Return the private addresses of this DirectSocketAddress.
  338. *
  339. * @return an array containing the private addresses of this
  340. * DirectSocketAddress
  341. */
  342. public InetSocketAddress[] getPrivateAddresses() {
  343. return privateAds.clone();
  344. }
  345. /**
  346. * Returns is this DirectSocketAddress has any public addresses.
  347. *
  348. * @return <code>true</code> if this DirectSocketAddress has any public
  349. * addresses, <code>false</code> otherwise.
  350. */
  351. public boolean hasPublicAddress() {
  352. return publicAds != null && publicAds.length > 0;
  353. }
  354. /**
  355. * Returns if the given InetSocketAddress is one of the external addresses
  356. * of this DirectSocketAddress.
  357. *
  358. * @return <code>true</code> if the given InetSocketAddress is one of the
  359. * external addresses, <code>false</code> otherwise.
  360. */
  361. public boolean inExternalAddress(InetSocketAddress a) {
  362. return NetworkUtils.contains(externalAds, a);
  363. }
  364. /**
  365. * Returns if the given InetSocketAddress is one of the public addresses of
  366. * this DirectSocketAddress.
  367. *
  368. * @return <code>true</code> if the given InetSocketAddress is one of the
  369. * public addresses, <code>false</code> otherwise.
  370. */
  371. public boolean inPublicAddress(InetSocketAddress a) {
  372. return NetworkUtils.contains(publicAds, a);
  373. }
  374. /**
  375. * Returns if the given InetSocketAddress is one of the private addresses of
  376. * this DirectSocketAddress.
  377. *
  378. * @return <code>true</code> if the given InetSocketAddress is one of the
  379. * private addresses, <code>false</code> otherwise.
  380. */
  381. public boolean inPrivateAddress(InetSocketAddress a) {
  382. return NetworkUtils.contains(privateAds, a);
  383. }
  384. /**
  385. * Returns the username of the DirectSocketAddress.
  386. *
  387. * @return the username.
  388. */
  389. public String getUser() {
  390. return user;
  391. }
  392. /*
  393. * (non-Javadoc)
  394. *
  395. * @see java.lang.Object#hashCode()
  396. */
  397. public int hashCode() {
  398. if (hashCode == 0) {
  399. hashCode = (Arrays.hashCode(externalAds)
  400. ^ Arrays.hashCode(publicAds) ^ Arrays.hashCode(privateAds));
  401. // Small chance, but let's fix this case anyway...
  402. if (hashCode == 0) {
  403. hashCode = 1;
  404. }
  405. }
  406. return hashCode;
  407. }
  408. /*
  409. * (non-Javadoc)
  410. *
  411. * @see java.lang.Object#equals(java.lang.Object)
  412. */
  413. public boolean equals(Object other) {
  414. // NOTE: this checks if the addresses are exactly the same.
  415. // For partial comparisons please use 'isCompatible'.
  416. // Check pointers
  417. if (this == other) {
  418. return true;
  419. }
  420. // Check type
  421. if (!(other instanceof DirectSocketAddress)) {
  422. return false;
  423. }
  424. DirectSocketAddress tmp = (DirectSocketAddress) other;
  425. // First, compare UUIDs
  426. if (!Arrays.equals(UUID, tmp.UUID)) {
  427. return false;
  428. }
  429. // Next, compare ports and addresses..
  430. if (!compare(externalAds, tmp.externalAds)) {
  431. return false;
  432. }
  433. if (!compare(publicAds, tmp.publicAds)) {
  434. return false;
  435. }
  436. if (!compare(privateAds, tmp.privateAds)) {
  437. return false;
  438. }
  439. return true;
  440. }
  441. private boolean compare(InetSocketAddress[] a, InetSocketAddress[] b) {
  442. if (a.length != b.length) {
  443. return false;
  444. }
  445. for (int i = 0; i < a.length; i++) {
  446. InetSocketAddress ad1 = a[i];
  447. boolean gotIt = false;
  448. for (int j = 0; j < b.length; j++) {
  449. // Use an offset here, since this is more efficient when the
  450. // two addresses are exactly the same.
  451. InetSocketAddress ad2 = b[(j + i) % b.length];
  452. if (ad1.equals(ad2)) {
  453. gotIt = true;
  454. break;
  455. }
  456. }
  457. if (!gotIt) {
  458. return false;
  459. }
  460. }
  461. return true;
  462. }
  463. private void partialToString(StringBuilder b, InetSocketAddress[] a) {
  464. final int len = a.length;
  465. for (int i = 0; i < len; i++) {
  466. b.append(NetworkUtils.ipToString(a[i].getAddress()));
  467. if (i < len - 1) {
  468. if (a[i].getPort() != a[i + 1].getPort()) {
  469. b.append(IP_PORT_SEPERATOR);
  470. b.append(a[i].getPort());
  471. }
  472. b.append(ADDRESS_SEPERATOR);
  473. } else {
  474. b.append(IP_PORT_SEPERATOR);
  475. b.append(a[i].getPort());
  476. }
  477. }
  478. }
  479. /*
  480. * (non-Javadoc)
  481. *
  482. * @see java.lang.Object#toString()
  483. */
  484. public String toString() {
  485. if (toStringCache == null) {
  486. StringBuilder b = new StringBuilder();
  487. boolean needSlash = false;
  488. if (externalAds.length > 0) {
  489. b.append(EXTERNAL_START);
  490. partialToString(b, externalAds);
  491. b.append(EXTERNAL_END);
  492. needSlash = true;
  493. }
  494. if (publicAds.length > 0) {
  495. if (needSlash) {
  496. b.append(ADDRESS_SEPERATOR);
  497. }
  498. partialToString(b, publicAds);
  499. needSlash = true;
  500. }
  501. if (privateAds.length > 0) {
  502. if (needSlash) {
  503. b.append(ADDRESS_SEPERATOR);
  504. }
  505. partialToString(b, privateAds);
  506. needSlash = true;
  507. }
  508. if (UUID != null) {
  509. b.append(UUID_SEPERATOR);
  510. b.append(NetworkUtils.UUIDToString(UUID));
  511. }
  512. if (user != null && user.length() > 0) {
  513. b.append(USER_SEPERATOR);
  514. b.append(user);
  515. }
  516. toStringCache = b.toString();
  517. }
  518. return toStringCache;
  519. }
  520. public int compareTo(DirectSocketAddress other) {
  521. if (this == other) {
  522. return 0;
  523. }
  524. if (hashCode() < other.hashCode()) {
  525. return -1;
  526. } else {
  527. return 1;
  528. }
  529. }
  530. private static boolean compatible(InetSocketAddress[] a,
  531. InetSocketAddress[] b, boolean comparePorts) {
  532. // If either (or both) is empty we give up!
  533. if (a.length == 0 || b.length == 0) {
  534. return false;
  535. }
  536. // If there is at least on shared address, we assume that they are the
  537. // same.
  538. for (InetSocketAddress a1 : a) {
  539. for (InetSocketAddress a2 : b) {
  540. if (!comparePorts) {
  541. if (a1.getAddress().equals(a2.getAddress())) {
  542. return true;
  543. }
  544. } else {
  545. if (a1.equals(a2)) {
  546. return true;
  547. }
  548. }
  549. }
  550. }
  551. // Otherwise, wo do not match...
  552. return false;
  553. }
  554. private boolean isLoopBack(InetSocketAddress[] ads) {
  555. if (ads == null || ads.length == 0) {
  556. return false;
  557. }
  558. for (InetSocketAddress a : ads) {
  559. if (!a.getAddress().isLoopbackAddress()) {
  560. return false;
  561. }
  562. }
  563. return true;
  564. }
  565. /**
  566. * Check if this SocketAddressSet refers to the same machine as the 'other'
  567. * address. The following tests are performed:
  568. *
  569. * if either is loopback -> return true
  570. *
  571. * if both have public -> return (public overlap ?)
  572. *
  573. * if both have external && !(external overlap) return false
  574. *
  575. * if both have private -> return (private overlap)
  576. *
  577. * else they are different machines -> return false
  578. *
  579. * @param target
  580. * @return
  581. */
  582. private boolean isCompatible(DirectSocketAddress other, boolean comparePorts) {
  583. // Check pointers
  584. if (this == other) {
  585. return true;
  586. }
  587. // Check UUIDs. Three cases fail here, either both have a UUID and they
  588. // are different, or either has a UUID and the other has one or more
  589. // public addresses. All other combination pass on to the next tests...
  590. if (UUID != null) {
  591. if (other.UUID != null) {
  592. if (!Arrays.equals(UUID, other.UUID)) {
  593. return false;
  594. }
  595. } else {
  596. if (other.publicAds.length > 0) {
  597. // I have a UUID so I must only have private addresses,
  598. // while the other has public addresses as well...
  599. return false;
  600. }
  601. }
  602. } else {
  603. if (other.UUID != null && publicAds.length > 0) {
  604. // The other has a UUID so it must only have private addresses,
  605. // while I have public addresses as well...
  606. return false;
  607. }
  608. }
  609. // If either is loopback, we always match.
  610. // TODO: I don't understand this. What if one is a loopback and the
  611. // other represents a remote host? --Ceriel
  612. if (isLoopBack(privateAds) || isLoopBack(other.privateAds)) {
  613. // TODO: What if comparePorts is set? --Ceriel
  614. return true;
  615. }
  616. // If both have 'public' addresses, they -MUST- overlap.
  617. if (publicAds.length > 0 && other.publicAds.length > 0) {
  618. return compatible(publicAds, other.publicAds, comparePorts);
  619. }
  620. // If both have external (NAT) addresses, they -MUST- overlap, and the
  621. // private addresses -MUST- overlap also!
  622. // TODO: does this work in the multiple NAT case ? We should also have a
  623. // UUID there....
  624. if (externalAds.length > 0 && other.externalAds.length > 0) {
  625. return (compatible(externalAds, other.externalAds, comparePorts) && compatible(
  626. privateAds, other.privateAds, comparePorts));
  627. }
  628. // Else, just check the private addresses.
  629. return compatible(privateAds, other.privateAds, comparePorts);
  630. }
  631. private void writeObject(ObjectOutputStream out) throws IOException {
  632. write(this, out);
  633. }
  634. private void readObject(ObjectInputStream in) throws IOException {
  635. int len = in.readInt();
  636. byte[] tmp = new byte[len];
  637. in.readFully(tmp);
  638. decode(tmp, 0);
  639. }
  640. public static void write(DirectSocketAddress s, DataOutput out)
  641. throws IOException {
  642. if (s == null) {
  643. out.writeInt(0);
  644. } else {
  645. byte[] a = s.getAddress();
  646. out.writeInt(a.length);
  647. out.write(a);
  648. }
  649. }
  650. public static DirectSocketAddress read(DataInput in) throws IOException {
  651. int len = in.readInt();
  652. if (len == 0) {
  653. return null;
  654. }
  655. byte[] tmp = new byte[len];
  656. in.readFully(tmp);
  657. return new DirectSocketAddress(tmp, 0);
  658. }
  659. public static void skip(DataInputStream in) throws IOException {
  660. int len = in.readInt();
  661. if (len == 0) {
  662. return;
  663. }
  664. while (len > 0) {
  665. len -= in.skip(len);
  666. }
  667. }
  668. public static DirectSocketAddress fromBytes(byte[] coded)
  669. throws UnknownHostException, MalformedAddressException {
  670. return fromBytes(coded, 0);
  671. }
  672. public static DirectSocketAddress fromBytes(byte[] coded, int off)
  673. throws UnknownHostException, MalformedAddressException {
  674. return new DirectSocketAddress(coded, off);
  675. }
  676. public static DirectSocketAddress getByAddress(InetSocketAddress a)
  677. throws UnknownHostException {
  678. if (NetworkUtils.isLocalAddress(a.getAddress())) {
  679. return new DirectSocketAddress(null, null,
  680. new InetSocketAddress[] { a }, null, null);
  681. } else {
  682. return new DirectSocketAddress(null, new InetSocketAddress[] { a },
  683. null, null, null);
  684. }
  685. }
  686. /**
  687. * Construct a new SocketAddressSet, using IPAddressSet and a port number.
  688. *
  689. * A valid port value is between 0 and 65535. A port number of zero will let
  690. * the system pick up an ephemeral port in a bind operation.
  691. *
  692. * A null address will assign the wildcard address.
  693. *
  694. * @param a
  695. * The IPAddressSet.
  696. * @param port
  697. * The port number.
  698. */
  699. public static DirectSocketAddress getByAddress(IPAddressSet a, int port,
  700. String user) throws UnknownHostException {
  701. return getByAddress(null, null, a, new int[] { port }, user);
  702. }
  703. public static DirectSocketAddress getByAddress(IPAddressSet a, int port)
  704. throws UnknownHostException {
  705. return getByAddress(null, null, a, new int[] { port }, null);
  706. }
  707. public static DirectSocketAddress getByAddress(IPAddressSet external,
  708. int externalPort, IPAddressSet other, int otherPort, String user) {
  709. return getByAddress(external, new int[] { externalPort }, other,
  710. new int[] { otherPort }, user);
  711. }
  712. /**
  713. * Construct a new SocketAddressSet, using an IPAddressSet and an array of
  714. * ports.
  715. *
  716. * A valid port value is between 1 and 65535. A port number of zero is not
  717. * allowed.
  718. *
  719. * A null address will assign the wildcard address.
  720. *
  721. * @param external
  722. * The IPAddressSet containg the external addresses of the
  723. * NAT box that this machine is behind.
  724. * @param externalPorts
  725. * The ports of each of the external addresses. This should
  726. * have size 1 or the same length as external has addresses.
  727. * @param other
  728. * The IPAddressSet containg the addresses found on the
  729. * machine
  730. * @param otherPorts
  731. * The ports of each of the other addresses. This should have
  732. * size 1 or the same length as other has addresses.
  733. */
  734. public static DirectSocketAddress getByAddress(IPAddressSet external,
  735. int[] externalPorts, IPAddressSet other, int[] otherPorts,
  736. String user) {
  737. if (other == null) {
  738. other = IPAddressSet.getLocalHost();
  739. }
  740. if (otherPorts.length != 1
  741. && otherPorts.length != other.addresses.length) {
  742. throw new MalformedAddressException(
  743. "Number of ports does not match" + "number of addresses");
  744. }
  745. int numPublic = 0;
  746. int numPrivate = 0;
  747. for (InetAddress a : other.addresses) {
  748. if (NetworkUtils.isExternalAddress(a)) {
  749. numPublic++;
  750. } else {
  751. numPrivate++;
  752. }
  753. }
  754. InetSocketAddress[] publicAds = new InetSocketAddress[numPublic];
  755. InetSocketAddress[] privateAds = new InetSocketAddress[numPrivate];
  756. int publicIndex = 0;
  757. int privateIndex = 0;
  758. for (int i = 0; i < other.addresses.length; i++) {
  759. if (i < otherPorts.length
  760. && (otherPorts[i] <= 0 || otherPorts[i] > 65535)) {
  761. throw new MalformedAddressException("Port[" + i
  762. + "] out of range");
  763. }
  764. int port = 0;
  765. if (otherPorts.length == 1) {
  766. port = otherPorts[0];
  767. } else {
  768. port = otherPorts[i];
  769. }
  770. if (NetworkUtils.isExternalAddress(other.addresses[i])) {
  771. publicAds[publicIndex++] = new InetSocketAddress(
  772. other.addresses[i], port);
  773. } else {
  774. privateAds[privateIndex++] = new InetSocketAddress(
  775. other.addresses[i], port);
  776. }
  777. }
  778. InetSocketAddress[] extern = null;
  779. if (external == null || external.addresses.length == 0) {
  780. extern = new InetSocketAddress[0];
  781. } else {
  782. if (externalPorts.length != 1
  783. && externalPorts.length != external.addresses.length) {
  784. throw new MalformedAddressException("Number of external ports "
  785. + "does not match number of external addresses");
  786. }
  787. extern = new InetSocketAddress[external.addresses.length];
  788. for (int i = 0; i < external.addresses.length; i++) {
  789. int port = 0;
  790. if (externalPorts.length == 1) {
  791. port = externalPorts[0];
  792. } else {
  793. port = externalPorts[i];
  794. }
  795. extern[i] = new InetSocketAddress(external.addresses[i], port);
  796. }
  797. }
  798. return new DirectSocketAddress(extern, publicAds, privateAds,
  799. other.UUID, user);
  800. }
  801. private static InetSocketAddress[] resize(InetSocketAddress[] orig, int add) {
  802. InetSocketAddress[] result;
  803. if (orig == null) {
  804. result = new InetSocketAddress[add];
  805. } else {
  806. result = new InetSocketAddress[orig.length + add];
  807. System.arraycopy(orig, 0, result, 0, orig.length);
  808. }
  809. return result;
  810. }
  811. private static InetSocketAddress[] addToArray(InetSocketAddress[] array,
  812. LinkedList<InetAddress> ads, int port) {
  813. int index = (array == null ? 0 : array.length);
  814. array = resize(array, ads.size());
  815. for (InetAddress a : ads) {
  816. array[index++] = new InetSocketAddress(a, port);
  817. }
  818. ads.clear();
  819. return array;
  820. }
  821. /**
  822. * Construct a new SocketAddresssET from a String representation of a
  823. * SocketAddressSet.
  824. *
  825. * This representation contains any number of InetAddresses seperated by
  826. * ADDRESS_SEPARATOR characters (usually defined as '/'), followed by a
  827. * IP_PORT_SEPERATOR (usually '-') and a port number.
  828. *
  829. * This sequence may be repeated any number of times, separated by slashes.
  830. *
  831. * The following examples are valid IPv4 string representations:
  832. *
  833. * 192.168.1.35-1234 192.168.1.35/10.0.0.1-1234
  834. * 192.168.1.35/10.0.0.1-1234/192.31.231.65-5678
  835. * 192.168.1.35/10.0.0.1-1234/192.31.231.65/130.37.24.4-5678
  836. *
  837. * We can also handle IPv6:
  838. *
  839. * fe80:0:0:0:2e0:18ff:fe2c:a31%2-1234
  840. *
  841. * Or a mix of the two:
  842. *
  843. * fe80:0:0:0:2e0:18ff:fe2c:a31%2/169.254.207.84-1234
  844. *
  845. * External addresses (for machines behind a NAT box) are marked using curly
  846. * brackets '{ }' since they are special. For example, the following address
  847. * identifies a NAT-ed machine with external address '82.161.4.24-5678' and
  848. * internal address '192.168.1.35-1234'
  849. *
  850. * {82.161.4.24-5678}/192.168.1.35-1234
  851. *
  852. * @param addressPort
  853. * The String representation of a IbisSocketAddress.
  854. * @throws UnknownHostException
  855. */
  856. public static DirectSocketAddress getByAddress(String addressPort)
  857. throws UnknownHostException, MalformedAddressException {
  858. DirectSocketAddress result = parseOldStyleAddress(addressPort);
  859. if (result != null) {
  860. return result;
  861. }
  862. return parseNewStyleAddress(addressPort);
  863. }
  864. private static DirectSocketAddress parseOldStyleAddress(String addressPort) {
  865. int lastIndex = addressPort.lastIndexOf(':');
  866. if (lastIndex == -1) {
  867. return null;
  868. }
  869. int port = -1;
  870. try {
  871. port = Integer.parseInt(addressPort.substring(lastIndex + 1));
  872. } catch (Exception e) {
  873. // It's not a valid 'old' style address....
  874. return null;
  875. }
  876. try {
  877. return DirectSocketAddress.getByAddress(new InetSocketAddress(
  878. addressPort.substring(0, lastIndex), port));
  879. } catch (Exception e) {
  880. // It's not a valid 'old' style address....
  881. return null;
  882. }
  883. }
  884. private static DirectSocketAddress parseNewStyleAddress(String addressPort)
  885. throws MalformedAddressException {
  886. StringTokenizer st = new StringTokenizer(addressPort, SEPARATORS, true);
  887. boolean readingExternal = false;
  888. boolean readingPort = false;
  889. boolean readingUUID = false;
  890. boolean readingUser = false;
  891. boolean allowExternalStart = true;
  892. boolean allowExternalEnd = false;
  893. boolean allowAddress = true;
  894. boolean allowSlash = false;
  895. boolean allowDash = false;
  896. boolean allowDone = false;
  897. boolean allowUser = false;
  898. boolean allowUUID = false;
  899. InetSocketAddress[] externalAds = null;
  900. InetSocketAddress[] publicAds = null;
  901. InetSocketAddress[] privateAds = null;
  902. byte[] UUID = null;
  903. String user = null;
  904. LinkedList<InetAddress> currentGlobal = new LinkedList<InetAddress>();
  905. LinkedList<InetAddress> currentLocal = new LinkedList<InetAddress>();
  906. while (st.hasMoreTokens()) {
  907. String s = st.nextToken();
  908. // System.out.println("Read: " + s);
  909. // FIXED - We need the SEPERATORS comparision to ensure we
  910. // can parse single character user names!!!!
  911. // Jason @ ComplexHPC2011 tutorial
  912. if (s.length() == 1 && SEPARATORS.contains(s)) {
  913. char delim = s.charAt(0);
  914. switch (delim) {
  915. case EXTERNAL_START:
  916. if (!allowExternalStart) {
  917. throw new MalformedAddressException("Unexpected "
  918. + EXTERNAL_START + " in address(" + addressPort
  919. + ")");
  920. }
  921. allowExternalStart = false;
  922. allowAddress = true;
  923. allowDone = false;
  924. allowUUID = false;
  925. readingExternal = true;
  926. break;
  927. case EXTERNAL_END:
  928. if (!allowExternalEnd) {
  929. throw new MalformedAddressException("Unexpected "
  930. + EXTERNAL_END + " in address(" + addressPort
  931. + ")");
  932. }
  933. allowExternalEnd = false;
  934. allowExternalStart = false;
  935. allowAddress = true;
  936. readingExternal = false;
  937. if (privateAds != null && privateAds.length > 0) {
  938. allowDone = true;
  939. allowUUID = true;
  940. }
  941. break;
  942. case ADDRESS_SEPERATOR:
  943. if (!allowSlash) {
  944. throw new MalformedAddressException("Unexpected "
  945. + ADDRESS_SEPERATOR + " in address("
  946. + addressPort + ")");
  947. }
  948. allowSlash = false;
  949. allowAddress = true;
  950. allowUUID = false;
  951. break;
  952. case IP_PORT_SEPERATOR:
  953. if (!allowDash) {
  954. throw new MalformedAddressException("Unexpected "
  955. + IP_PORT_SEPERATOR + " in address("
  956. + addressPort + ")");
  957. }
  958. allowDash = false;
  959. readingPort = true;
  960. break;
  961. case UUID_SEPERATOR:
  962. if (!allowUUID) {
  963. throw new MalformedAddressException("Unexpected "
  964. + UUID_SEPERATOR + " in address(" + addressPort
  965. + ")");
  966. }
  967. allowUUID = false;
  968. allowDone = false;
  969. allowUser = false;
  970. readingUUID = true;
  971. break;
  972. case USER_SEPERATOR:
  973. if (!allowUser) {
  974. throw new MalformedAddressException("Unexpected "
  975. + USER_SEPERATOR + " in address(" + addressPort
  976. + ")");
  977. }
  978. allowDone = false;
  979. allowUser = false;
  980. allowUUID = false;
  981. readingUser = true;
  982. break;
  983. default:
  984. // should never happen ?
  985. throw new MalformedAddressException(
  986. "Unexpected delimiter: " + delim + " in address("
  987. + addressPort + ")");
  988. }
  989. } else if (readingUUID) {
  990. UUID = NetworkUtils.stringToUUID(s);
  991. readingUUID = false;
  992. allowSlash = false;
  993. allowDone = true;
  994. allowUser = true;
  995. } else if (readingUser) {
  996. user = s;
  997. readingUser = false;
  998. allowSlash = false;
  999. allowDone = true;
  1000. } else if (readingPort) {
  1001. // This should complete a group of addresses. More may folow
  1002. int port = Integer.parseInt(s);
  1003. // ... do a sanity check on the port value ...
  1004. if (port <= 0 || port > 65535) {
  1005. throw new MalformedAddressException("Port out of range: "
  1006. + port);
  1007. }
  1008. if (readingExternal) {
  1009. externalAds = addToArray(externalAds, currentGlobal, port);
  1010. } else {
  1011. publicAds = addToArray(publicAds, currentGlobal, port);
  1012. privateAds = addToArray(privateAds, currentLocal, port);
  1013. }
  1014. readingPort = false;
  1015. allowSlash = true;
  1016. if (readingExternal) {
  1017. allowExternalEnd = true;
  1018. } else {
  1019. allowDone = true;
  1020. allowUser = true;
  1021. allowUUID = true;
  1022. }
  1023. } else if (allowAddress) {
  1024. // reading address
  1025. InetAddress tmp = null;
  1026. try {
  1027. tmp = InetAddressCache.getByName(s);
  1028. } catch (UnknownHostException e) {
  1029. throw new MalformedAddressException("Broken inet address "
  1030. + s + " in address(" + addressPort + ")");
  1031. }
  1032. if (NetworkUtils.isLocalAddress(tmp)) {
  1033. currentLocal.add(tmp);
  1034. } else {
  1035. currentGlobal.add(tmp);
  1036. }
  1037. allowSlash = true;
  1038. allowDash = true;
  1039. allowAddress = false;
  1040. allowDone = false;
  1041. } else {
  1042. throw new MalformedAddressException("Unexpected data " + s
  1043. + " in address(" + addressPort + ")");
  1044. }
  1045. }
  1046. if (!allowDone) {
  1047. throw new MalformedAddressException("Address " + addressPort
  1048. + " is incomplete!");
  1049. }
  1050. return new DirectSocketAddress(externalAds, publicAds, privateAds,
  1051. UUID, user);
  1052. }
  1053. public static DirectSocketAddress getByAddress(String host, int port)
  1054. throws UnknownHostException {
  1055. return getByAddress(IPAddressSet.getFromString(host), port, null);
  1056. }
  1057. private static InetSocketAddress[] merge(InetSocketAddress[] a,
  1058. InetSocketAddress[] b) {
  1059. int alen = (a == null ? 0 : a.length);
  1060. int blen = (b == null ? 0 : b.length);
  1061. InetSocketAddress[] res = new InetSocketAddress[alen + blen];
  1062. if (alen > 0) {
  1063. System.arraycopy(a, 0, res, 0, alen);
  1064. }
  1065. if (blen > 0) {
  1066. System.arraycopy(b, 0, res, alen, blen);
  1067. }
  1068. return res;
  1069. }
  1070. /**
  1071. * Merges two IbisSocketAddresses.
  1072. *
  1073. * @param s1
  1074. * the first IbisSocketAddress
  1075. * @param s2
  1076. * the second IbisSocketAddress
  1077. * @return a new SmartSocketAddress
  1078. */
  1079. public static DirectSocketAddress merge(DirectSocketAddress s1,
  1080. DirectSocketAddress s2) {
  1081. byte[] UUID = s1.UUID;
  1082. if (s1.UUID == null) {
  1083. UUID = s2.UUID;
  1084. } else if (s2.UUID != null) {
  1085. if (!Arrays.equals(s1.UUID, s2.UUID)) {
  1086. throw new IllegalArgumentException("Cannot merge two "
  1087. + "addresses with different UUIDs!");
  1088. }
  1089. }
  1090. String user = s1.user;
  1091. if (s1.user == null || user.length() == 0) {
  1092. user = s2.user;
  1093. } else if (s2.user != null && s2.user.length() > 0) {
  1094. if (!s1.user.equals(s2.user)) {
  1095. throw new IllegalArgumentException("Cannot merge two "
  1096. + "addresses with different user names!");
  1097. }
  1098. // They are equal...
  1099. user = s2.user;
  1100. } else {
  1101. user = s2.user;
  1102. }
  1103. return new DirectSocketAddress(merge(s1.externalAds, s2.externalAds),
  1104. merge(s1.publicAds, s2.publicAds), merge(s1.privateAds,
  1105. s2.privateAds), UUID, user);
  1106. }
  1107. /**
  1108. * Converts an array of SocketAddressSets to a String array.
  1109. *
  1110. * @param s
  1111. * the array of {@link DirectSocketAddress}
  1112. * @return a new String array containing the {@link String} representations
  1113. * of the {@link DirectSocketAddress}s, or <code>null</code> if s
  1114. * was <code>null</code>
  1115. */
  1116. public static String[] convertToStrings(DirectSocketAddress[] s) {
  1117. if (s == null) {
  1118. return null;
  1119. }
  1120. String[] result = new String[s.length];
  1121. for (int i = 0; i < s.length; i++) {
  1122. if (s[i] != null) {
  1123. result[i] = s[i].toString();
  1124. }
  1125. }
  1126. return result;
  1127. }
  1128. /**
  1129. * Converts an array of Strings into an array of SocketAddressSets.
  1130. *
  1131. * @param s
  1132. * the array of {@link String} to convert
  1133. * @param ignoreProblems
  1134. * indicates if conversion problems should be silently
  1135. * ignored.
  1136. * @return a new array containing the {@link DirectSocketAddress}s or
  1137. * <code>null</code> if s was <code>null</code>
  1138. * @throws UnknownHostException
  1139. * when any of the Strings cannot be converted and
  1140. * ignoreProblems is false
  1141. */
  1142. public static DirectSocketAddress[] convertToSocketAddressSet(String[] s,
  1143. boolean ignoreProblems) throws UnknownHostException,
  1144. MalformedAddressException {
  1145. if (s == null) {
  1146. return null;
  1147. }
  1148. DirectSocketAddress[] result = new DirectSocketAddress[s.length];
  1149. for (int i = 0; i < s.length; i++) {
  1150. if (s[i] != null) {
  1151. if (ignoreProblems) {
  1152. try {
  1153. result[i] = DirectSocketAddress.getByAddress(s[i]);
  1154. } catch (UnknownHostException e) {
  1155. // ignore
  1156. }
  1157. } else {
  1158. result[i] = DirectSocketAddress.getByAddress(s[i]);
  1159. }
  1160. }
  1161. }
  1162. return result;
  1163. }
  1164. /**
  1165. * Converts an array of Strings into an array of SocketAddressSets.
  1166. *
  1167. * @param s
  1168. * the array of {@link String} to convert
  1169. * @return a new array containing the {@link DirectSocketAddress}s or
  1170. * <code>null</code> if s was <code>null</code>
  1171. * @throws UnknownHostException
  1172. * when any of the Strings cannot be converted
  1173. */
  1174. public static DirectSocketAddress[] convertToSocketAddressSet(String[] s)
  1175. throws UnknownHostException {
  1176. return convertToSocketAddressSet(s, false);
  1177. }
  1178. /**
  1179. * Returns if the other SocketAddressSet represents the same machine as this
  1180. * one.
  1181. *
  1182. * @param other
  1183. * the SocketAddressSet to compare to
  1184. * @return if both SocketAddressSets represent the same machine
  1185. */
  1186. public boolean sameMachine(DirectSocketAddress other) {
  1187. return isCompatible(other, false);
  1188. }
  1189. /**
  1190. * Returns if the other SocketAddressSet represents the same process as this
  1191. * one.
  1192. *
  1193. * @param other
  1194. * the SocketAddressSet to compare to
  1195. * @return if both SocketAddressSets represent the same process
  1196. */
  1197. public boolean sameProcess(DirectSocketAddress other) {
  1198. return isCompatible(other, true);
  1199. }
  1200. /**
  1201. * Returns if the two SocketAddressSets represent the same machine.
  1202. *
  1203. */
  1204. public static boolean sameMachine(DirectSocketAddress a,
  1205. DirectSocketAddress b) {
  1206. return a.sameMachine(b);
  1207. }
  1208. /**
  1209. * Returns if the two SocketAddressSets represent the same process.
  1210. *
  1211. */
  1212. public static boolean sameProcess(DirectSocketAddress a,
  1213. DirectSocketAddress b) {
  1214. return a.sameProcess(b);
  1215. }
  1216. public int numberOfAddresses() {
  1217. return externalAds.length + publicAds.length + privateAds.length;
  1218. }
  1219. }