Unsupported library that attempts to punch holes through NAT
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

DiscoveryTest.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*
  2. * This file is part of JSTUN.
  3. *
  4. * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
  5. * reserved.
  6. *
  7. * This software is licensed under either the GNU Public License (GPL),
  8. * or the Apache 2.0 license. Copies of both license agreements are
  9. * included in this distribution.
  10. */
  11. package de.javawi.jstun.test;
  12. import java.io.IOException;
  13. import java.net.DatagramPacket;
  14. import java.net.DatagramSocket;
  15. import java.net.InetAddress;
  16. import java.net.InetSocketAddress;
  17. import java.net.SocketException;
  18. import java.net.SocketTimeoutException;
  19. import java.net.UnknownHostException;
  20. import java.util.logging.Logger;
  21. import de.javawi.jstun.attribute.ChangeRequest;
  22. import de.javawi.jstun.attribute.ChangedAddress;
  23. import de.javawi.jstun.attribute.ErrorCode;
  24. import de.javawi.jstun.attribute.MappedAddress;
  25. import de.javawi.jstun.attribute.MessageAttribute;
  26. import de.javawi.jstun.attribute.MessageAttributeException;
  27. import de.javawi.jstun.attribute.MessageAttributeParsingException;
  28. import de.javawi.jstun.header.MessageHeader;
  29. import de.javawi.jstun.header.MessageHeaderParsingException;
  30. import de.javawi.jstun.util.UtilityException;
  31. public class DiscoveryTest {
  32. private static Logger logger = Logger.getLogger("de.javawi.stun.test.DiscoveryTest");
  33. InetAddress iaddress;
  34. String stunServer;
  35. int port;
  36. int timeoutInitValue = 300; //ms
  37. MappedAddress ma = null;
  38. ChangedAddress ca = null;
  39. boolean nodeNatted = true;
  40. DatagramSocket socketTest1 = null;
  41. DiscoveryInfo di = null;
  42. public DiscoveryTest(InetAddress iaddress , String stunServer, int port) {
  43. super();
  44. this.iaddress = iaddress;
  45. this.stunServer = stunServer;
  46. this.port = port;
  47. }
  48. public DiscoveryInfo test() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException{
  49. ma = null;
  50. ca = null;
  51. nodeNatted = true;
  52. socketTest1 = null;
  53. di = new DiscoveryInfo(iaddress);
  54. if (test1()) {
  55. if (test2()) {
  56. if (test1Redo()) {
  57. test3();
  58. }
  59. }
  60. }
  61. socketTest1.close();
  62. return di;
  63. }
  64. private boolean test1() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageHeaderParsingException {
  65. int timeSinceFirstTransmission = 0;
  66. int timeout = timeoutInitValue;
  67. while (true) {
  68. try {
  69. // Test 1 including response
  70. socketTest1 = new DatagramSocket(new InetSocketAddress(iaddress, 0));
  71. socketTest1.setReuseAddress(true);
  72. socketTest1.connect(InetAddress.getByName(stunServer), port);
  73. socketTest1.setSoTimeout(timeout);
  74. MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
  75. sendMH.generateTransactionID();
  76. ChangeRequest changeRequest = new ChangeRequest();
  77. sendMH.addMessageAttribute(changeRequest);
  78. byte[] data = sendMH.getBytes();
  79. DatagramPacket send = new DatagramPacket(data, data.length);
  80. socketTest1.send(send);
  81. logger.finer("Test 1: Binding Request sent.");
  82. MessageHeader receiveMH = new MessageHeader();
  83. while (!(receiveMH.equalTransactionID(sendMH))) {
  84. DatagramPacket receive = new DatagramPacket(new byte[200], 200);
  85. socketTest1.receive(receive);
  86. receiveMH = MessageHeader.parseHeader(receive.getData());
  87. receiveMH.parseAttributes(receive.getData());
  88. }
  89. ma = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
  90. ca = (ChangedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ChangedAddress);
  91. ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
  92. if (ec != null) {
  93. di.setError(ec.getResponseCode(), ec.getReason());
  94. logger.config("Message header contains an Errorcode message attribute.");
  95. return false;
  96. }
  97. if ((ma == null) || (ca == null)) {
  98. di.setError(700, "The server is sending an incomplete response (Mapped Address and Changed Address message attributes are missing). The client should not retry.");
  99. logger.config("Response does not contain a Mapped Address or Changed Address message attribute.");
  100. return false;
  101. } else {
  102. di.setPublicIP(ma.getAddress().getInetAddress());
  103. if ((ma.getPort() == socketTest1.getLocalPort()) && (ma.getAddress().getInetAddress().equals(socketTest1.getLocalAddress()))) {
  104. logger.fine("Node is not natted.");
  105. nodeNatted = false;
  106. } else {
  107. logger.fine("Node is natted.");
  108. }
  109. return true;
  110. }
  111. } catch (SocketTimeoutException ste) {
  112. if (timeSinceFirstTransmission < 7900) {
  113. logger.finer("Test 1: Socket timeout while receiving the response.");
  114. timeSinceFirstTransmission += timeout;
  115. int timeoutAddValue = (timeSinceFirstTransmission * 2);
  116. if (timeoutAddValue > 1600) timeoutAddValue = 1600;
  117. timeout = timeoutAddValue;
  118. } else {
  119. // node is not capable of udp communication
  120. logger.finer("Test 1: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
  121. di.setBlockedUDP();
  122. logger.fine("Node is not capable of UDP communication.");
  123. return false;
  124. }
  125. }
  126. }
  127. }
  128. private boolean test2() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException {
  129. int timeSinceFirstTransmission = 0;
  130. int timeout = timeoutInitValue;
  131. while (true) {
  132. try {
  133. // Test 2 including response
  134. DatagramSocket sendSocket = new DatagramSocket(new InetSocketAddress(iaddress, 0));
  135. sendSocket.connect(InetAddress.getByName(stunServer), port);
  136. sendSocket.setSoTimeout(timeout);
  137. MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
  138. sendMH.generateTransactionID();
  139. ChangeRequest changeRequest = new ChangeRequest();
  140. changeRequest.setChangeIP();
  141. changeRequest.setChangePort();
  142. sendMH.addMessageAttribute(changeRequest);
  143. byte[] data = sendMH.getBytes();
  144. DatagramPacket send = new DatagramPacket(data, data.length);
  145. sendSocket.send(send);
  146. logger.finer("Test 2: Binding Request sent.");
  147. int localPort = sendSocket.getLocalPort();
  148. InetAddress localAddress = sendSocket.getLocalAddress();
  149. sendSocket.close();
  150. DatagramSocket receiveSocket = new DatagramSocket(localPort, localAddress);
  151. receiveSocket.connect(ca.getAddress().getInetAddress(), ca.getPort());
  152. receiveSocket.setSoTimeout(timeout);
  153. MessageHeader receiveMH = new MessageHeader();
  154. while(!(receiveMH.equalTransactionID(sendMH))) {
  155. DatagramPacket receive = new DatagramPacket(new byte[200], 200);
  156. receiveSocket.receive(receive);
  157. receiveMH = MessageHeader.parseHeader(receive.getData());
  158. receiveMH.parseAttributes(receive.getData());
  159. }
  160. ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
  161. if (ec != null) {
  162. di.setError(ec.getResponseCode(), ec.getReason());
  163. logger.config("Message header contains an Errorcode message attribute.");
  164. return false;
  165. }
  166. if (!nodeNatted) {
  167. di.setOpenAccess();
  168. logger.fine("Node has open access to the Internet (or, at least the node is behind a full-cone NAT without translation).");
  169. } else {
  170. di.setFullCone();
  171. logger.fine("Node is behind a full-cone NAT.");
  172. }
  173. return false;
  174. } catch (SocketTimeoutException ste) {
  175. if (timeSinceFirstTransmission < 7900) {
  176. logger.finer("Test 2: Socket timeout while receiving the response.");
  177. timeSinceFirstTransmission += timeout;
  178. int timeoutAddValue = (timeSinceFirstTransmission * 2);
  179. if (timeoutAddValue > 1600) timeoutAddValue = 1600;
  180. timeout = timeoutAddValue;
  181. } else {
  182. logger.finer("Test 2: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
  183. if (!nodeNatted) {
  184. di.setSymmetricUDPFirewall();
  185. logger.fine("Node is behind a symmetric UDP firewall.");
  186. return false;
  187. } else {
  188. // not is natted
  189. // redo test 1 with address and port as offered in the changed-address message attribute
  190. return true;
  191. }
  192. }
  193. }
  194. }
  195. }
  196. private boolean test1Redo() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageHeaderParsingException{
  197. int timeSinceFirstTransmission = 0;
  198. int timeout = timeoutInitValue;
  199. while (true) {
  200. // redo test 1 with address and port as offered in the changed-address message attribute
  201. try {
  202. // Test 1 with changed port and address values
  203. socketTest1.connect(ca.getAddress().getInetAddress(), ca.getPort());
  204. socketTest1.setSoTimeout(timeout);
  205. MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
  206. sendMH.generateTransactionID();
  207. ChangeRequest changeRequest = new ChangeRequest();
  208. sendMH.addMessageAttribute(changeRequest);
  209. byte[] data = sendMH.getBytes();
  210. DatagramPacket send = new DatagramPacket(data, data.length);
  211. socketTest1.send(send);
  212. logger.finer("Test 1 redo with changed address: Binding Request sent.");
  213. MessageHeader receiveMH = new MessageHeader();
  214. while (!(receiveMH.equalTransactionID(sendMH))) {
  215. DatagramPacket receive = new DatagramPacket(new byte[200], 200);
  216. socketTest1.receive(receive);
  217. receiveMH = MessageHeader.parseHeader(receive.getData());
  218. receiveMH.parseAttributes(receive.getData());
  219. }
  220. MappedAddress ma2 = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
  221. ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
  222. if (ec != null) {
  223. di.setError(ec.getResponseCode(), ec.getReason());
  224. logger.config("Message header contains an Errorcode message attribute.");
  225. return false;
  226. }
  227. if (ma2 == null) {
  228. di.setError(700, "The server is sending an incomplete response (Mapped Address message attribute is missing). The client should not retry.");
  229. logger.config("Response does not contain a Mapped Address message attribute.");
  230. return false;
  231. } else {
  232. if ((ma.getPort() != ma2.getPort()) || (!(ma.getAddress().getInetAddress().equals(ma2.getAddress().getInetAddress())))) {
  233. di.setSymmetric();
  234. logger.fine("Node is behind a symmetric NAT.");
  235. return false;
  236. }
  237. }
  238. return true;
  239. } catch (SocketTimeoutException ste2) {
  240. if (timeSinceFirstTransmission < 7900) {
  241. logger.config("Test 1 redo with changed address: Socket timeout while receiving the response.");
  242. timeSinceFirstTransmission += timeout;
  243. int timeoutAddValue = (timeSinceFirstTransmission * 2);
  244. if (timeoutAddValue > 1600) timeoutAddValue = 1600;
  245. timeout = timeoutAddValue;
  246. } else {
  247. logger.config("Test 1 redo with changed address: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
  248. return false;
  249. }
  250. }
  251. }
  252. }
  253. private void test3() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException {
  254. int timeSinceFirstTransmission = 0;
  255. int timeout = timeoutInitValue;
  256. while (true) {
  257. try {
  258. // Test 3 including response
  259. DatagramSocket sendSocket = new DatagramSocket(new InetSocketAddress(iaddress, 0));
  260. sendSocket.connect(InetAddress.getByName(stunServer), port);
  261. sendSocket.setSoTimeout(timeout);
  262. MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
  263. sendMH.generateTransactionID();
  264. ChangeRequest changeRequest = new ChangeRequest();
  265. changeRequest.setChangePort();
  266. sendMH.addMessageAttribute(changeRequest);
  267. byte[] data = sendMH.getBytes();
  268. DatagramPacket send = new DatagramPacket(data, data.length);
  269. sendSocket.send(send);
  270. logger.finer("Test 3: Binding Request sent.");
  271. int localPort = sendSocket.getLocalPort();
  272. InetAddress localAddress = sendSocket.getLocalAddress();
  273. sendSocket.close();
  274. DatagramSocket receiveSocket = new DatagramSocket(localPort, localAddress);
  275. receiveSocket.connect(InetAddress.getByName(stunServer), ca.getPort());
  276. receiveSocket.setSoTimeout(timeout);
  277. MessageHeader receiveMH = new MessageHeader();
  278. while (!(receiveMH.equalTransactionID(sendMH))) {
  279. DatagramPacket receive = new DatagramPacket(new byte[200], 200);
  280. receiveSocket.receive(receive);
  281. receiveMH = MessageHeader.parseHeader(receive.getData());
  282. receiveMH.parseAttributes(receive.getData());
  283. }
  284. ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
  285. if (ec != null) {
  286. di.setError(ec.getResponseCode(), ec.getReason());
  287. logger.config("Message header contains an Errorcode message attribute.");
  288. return;
  289. }
  290. if (nodeNatted) {
  291. di.setRestrictedCone();
  292. logger.fine("Node is behind a restricted NAT.");
  293. return;
  294. }
  295. } catch (SocketTimeoutException ste) {
  296. if (timeSinceFirstTransmission < 7900) {
  297. logger.finer("Test 3: Socket timeout while receiving the response.");
  298. timeSinceFirstTransmission += timeout;
  299. int timeoutAddValue = (timeSinceFirstTransmission * 2);
  300. if (timeoutAddValue > 1600) timeoutAddValue = 1600;
  301. timeout = timeoutAddValue;
  302. } else {
  303. logger.finer("Test 3: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
  304. di.setPortRestrictedCone();
  305. logger.fine("Node is behind a port restricted NAT.");
  306. return;
  307. }
  308. }
  309. }
  310. }
  311. }