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