2 * This file is part of JSTUN.
4 * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
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.
12 package org.linphone.p2pproxy.test;
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;
23 import junit.framework.Assert;
24 import junit.framework.TestCase;
26 import org.apache.log4j.BasicConfigurator;
27 import org.apache.log4j.Logger;
28 import org.linphone.p2pproxy.core.GenericUdpSession;
29 import org.linphone.p2pproxy.core.stun.StunServer;
33 import de.javawi.jstun.attribute.ChangeRequest;
34 import de.javawi.jstun.attribute.ChangedAddress;
35 import de.javawi.jstun.attribute.ErrorCode;
36 import de.javawi.jstun.attribute.MappedAddress;
37 import de.javawi.jstun.attribute.MessageAttribute;
38 import de.javawi.jstun.attribute.MessageAttributeException;
39 import de.javawi.jstun.attribute.MessageAttributeParsingException;
40 import de.javawi.jstun.header.MessageHeader;
41 import de.javawi.jstun.header.MessageHeaderParsingException;
42 import de.javawi.jstun.test.DiscoveryInfo;
43 import de.javawi.jstun.util.UtilityException;
45 public class StunServerTester extends TestCase {
46 private static Logger logger = Logger.getLogger(StunServerTester.class);
47 InetAddress iaddress ;
48 String stunServer = "localhost";
50 int timeoutInitValue = 300; //ms
51 MappedAddress ma = null;
52 ChangedAddress ca = null;
53 boolean nodeNatted = true;
54 DatagramSocket socketTest1 = null;
55 DiscoveryInfo di = null;
56 static StunServer mSturServer = null;
57 static GenericUdpSession mGenericUdpSession = null;
60 public void setUp() throws Exception {
62 if (mSturServer == null) {
63 BasicConfigurator.configure();
64 mGenericUdpSession = new GenericUdpSession(new InetSocketAddress(port));
65 mSturServer = new StunServer(mGenericUdpSession.getSocket());
66 mGenericUdpSession.addMessageHandler(mSturServer);
67 iaddress = InetAddress.getLocalHost();
69 di = new DiscoveryInfo(iaddress);
72 public DiscoveryInfo xxxtest() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException{
77 di = new DiscoveryInfo(iaddress);
92 public void testBindRequest() {
95 } catch (Exception e) {
96 logger.error("testBindRequest ko",e);
97 Assert.fail(e.getMessage());
101 private boolean bindRequest() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageHeaderParsingException {
102 int timeSinceFirstTransmission = 0;
103 int timeout = timeoutInitValue;
106 // Test 1 including response
107 socketTest1 = new DatagramSocket(new InetSocketAddress(iaddress, 0));
108 socketTest1.setReuseAddress(true);
109 socketTest1.connect(mGenericUdpSession.getSocket().getLocalSocketAddress());
110 socketTest1.setSoTimeout(timeout);
112 MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
113 sendMH.generateTransactionID();
115 ChangeRequest changeRequest = new ChangeRequest();
116 sendMH.addMessageAttribute(changeRequest);
118 byte[] data = sendMH.getBytes();
119 DatagramPacket send = new DatagramPacket(data, data.length);
120 socketTest1.send(send);
121 logger.info("Test 1: Binding Request sent.");
123 MessageHeader receiveMH = new MessageHeader();
124 while (!(receiveMH.equalTransactionID(sendMH))) {
125 DatagramPacket receive = new DatagramPacket(new byte[200], 200);
126 socketTest1.receive(receive);
127 receiveMH = MessageHeader.parseHeader(receive.getData());
128 receiveMH.parseAttributes(receive.getData());
131 ma = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
132 ca = (ChangedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ChangedAddress);
133 ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
135 di.setError(ec.getResponseCode(), ec.getReason());
136 logger.info("Message header contains an Errorcode message attribute.");
140 di.setError(700, "The server is sending an incomplete response (Mapped Address and Changed Address message attributes are missing). The client should not retry.");
141 logger.info("Response does not contain a Mapped Address or Changed Address message attribute.");
144 di.setPublicIP(ma.getAddress().getInetAddress());
145 if ((ma.getPort() == socketTest1.getLocalPort()) && (ma.getAddress().getInetAddress().equals(socketTest1.getLocalAddress()))) {
146 logger.info("Node is not natted.");
149 logger.info("Node is natted.");
153 } catch (SocketTimeoutException ste) {
154 if (timeSinceFirstTransmission < 7900) {
155 logger.info("Test 1: Socket timeout while receiving the response.");
156 timeSinceFirstTransmission += timeout;
157 int timeoutAddValue = (timeSinceFirstTransmission * 2);
158 if (timeoutAddValue > 1600) timeoutAddValue = 1600;
159 timeout = timeoutAddValue;
161 // node is not capable of udp communication
162 logger.info("Test 1: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
164 logger.info("Node is not capable of UDP communication.");
171 * Test allocate request
175 public void xxxAllocateRequest() {
177 int timeSinceFirstTransmission = 0;
178 int timeout = timeoutInitValue;
180 // Test 1 including response
181 socketTest1 = new DatagramSocket(new InetSocketAddress(iaddress, 0));
182 socketTest1.setReuseAddress(true);
183 socketTest1.connect(InetAddress.getByName(stunServer), port);
184 socketTest1.setSoTimeout(timeout);
186 MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.AllocateRequest);
187 sendMH.generateTransactionID();
189 ChangeRequest changeRequest = new ChangeRequest();
190 sendMH.addMessageAttribute(changeRequest);
192 byte[] data = sendMH.getBytes();
193 DatagramPacket send = new DatagramPacket(data, data.length);
194 socketTest1.send(send);
195 logger.info("Allocate Request sent.");
197 MessageHeader receiveMH = new MessageHeader();
198 while (!(receiveMH.equalTransactionID(sendMH))) {
199 DatagramPacket receive = new DatagramPacket(new byte[200], 200);
200 socketTest1.receive(receive);
201 receiveMH = MessageHeader.parseHeader(receive.getData());
202 receiveMH.parseAttributes(receive.getData());
205 } catch (Exception ste) {
206 if (timeSinceFirstTransmission < 7900) {
207 logger.info("Allocate Rquest: Socket timeout while receiving the response.");
208 timeSinceFirstTransmission += timeout;
209 int timeoutAddValue = (timeSinceFirstTransmission * 2);
210 if (timeoutAddValue > 1600) timeoutAddValue = 1600;
211 timeout = timeoutAddValue;
213 // node is not capable of udp communication
214 logger.info("Allocate Rquest: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
216 logger.info("Node is not capable of UDP communication.");
222 private boolean xxxtest2() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException {
223 int timeSinceFirstTransmission = 0;
224 int timeout = timeoutInitValue;
227 // Test 2 including response
228 DatagramSocket sendSocket = new DatagramSocket(new InetSocketAddress(iaddress, 0));
229 sendSocket.connect(InetAddress.getByName(stunServer), port);
230 sendSocket.setSoTimeout(timeout);
232 MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
233 sendMH.generateTransactionID();
235 ChangeRequest changeRequest = new ChangeRequest();
236 changeRequest.setChangeIP();
237 changeRequest.setChangePort();
238 sendMH.addMessageAttribute(changeRequest);
240 byte[] data = sendMH.getBytes();
241 DatagramPacket send = new DatagramPacket(data, data.length);
242 sendSocket.send(send);
243 logger.info("Test 2: Binding Request sent.");
245 int localPort = sendSocket.getLocalPort();
246 InetAddress localAddress = sendSocket.getLocalAddress();
250 DatagramSocket receiveSocket = new DatagramSocket(localPort, localAddress);
251 receiveSocket.connect(ca.getAddress().getInetAddress(), ca.getPort());
252 receiveSocket.setSoTimeout(timeout);
254 MessageHeader receiveMH = new MessageHeader();
255 while(!(receiveMH.equalTransactionID(sendMH))) {
256 DatagramPacket receive = new DatagramPacket(new byte[200], 200);
257 receiveSocket.receive(receive);
258 receiveMH = MessageHeader.parseHeader(receive.getData());
259 receiveMH.parseAttributes(receive.getData());
261 ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
263 di.setError(ec.getResponseCode(), ec.getReason());
264 logger.info("Message header contains an Errorcode message attribute.");
269 logger.info("Node has open access to the Internet (or, at least the node is behind a full-cone NAT without translation).");
272 logger.info("Node is behind a full-cone NAT.");
275 } catch (SocketTimeoutException ste) {
276 if (timeSinceFirstTransmission < 7900) {
277 logger.info("Test 2: Socket timeout while receiving the response.");
278 timeSinceFirstTransmission += timeout;
279 int timeoutAddValue = (timeSinceFirstTransmission * 2);
280 if (timeoutAddValue > 1600) timeoutAddValue = 1600;
281 timeout = timeoutAddValue;
283 logger.info("Test 2: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
285 di.setSymmetricUDPFirewall();
286 logger.info("Node is behind a symmetric UDP firewall.");
290 // redo test 1 with address and port as offered in the changed-address message attribute
298 private boolean xxxtest1Redo() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageHeaderParsingException{
299 int timeSinceFirstTransmission = 0;
300 int timeout = timeoutInitValue;
302 // redo test 1 with address and port as offered in the changed-address message attribute
304 // Test 1 with changed port and address values
305 socketTest1.connect(ca.getAddress().getInetAddress(), ca.getPort());
306 socketTest1.setSoTimeout(timeout);
308 MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
309 sendMH.generateTransactionID();
311 ChangeRequest changeRequest = new ChangeRequest();
312 sendMH.addMessageAttribute(changeRequest);
314 byte[] data = sendMH.getBytes();
315 DatagramPacket send = new DatagramPacket(data, data.length);
316 socketTest1.send(send);
317 logger.info("Test 1 redo with changed address: Binding Request sent.");
319 MessageHeader receiveMH = new MessageHeader();
320 while (!(receiveMH.equalTransactionID(sendMH))) {
321 DatagramPacket receive = new DatagramPacket(new byte[200], 200);
322 socketTest1.receive(receive);
323 receiveMH = MessageHeader.parseHeader(receive.getData());
324 receiveMH.parseAttributes(receive.getData());
326 MappedAddress ma2 = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
327 ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
329 di.setError(ec.getResponseCode(), ec.getReason());
330 logger.info("Message header contains an Errorcode message attribute.");
334 di.setError(700, "The server is sending an incomplete response (Mapped Address message attribute is missing). The client should not retry.");
335 logger.info("Response does not contain a Mapped Address message attribute.");
338 if ((ma.getPort() != ma2.getPort()) || (!(ma.getAddress().getInetAddress().equals(ma2.getAddress().getInetAddress())))) {
340 logger.info("Node is behind a symmetric NAT.");
345 } catch (SocketTimeoutException ste2) {
346 if (timeSinceFirstTransmission < 7900) {
347 logger.info("Test 1 redo with changed address: Socket timeout while receiving the response.");
348 timeSinceFirstTransmission += timeout;
349 int timeoutAddValue = (timeSinceFirstTransmission * 2);
350 if (timeoutAddValue > 1600) timeoutAddValue = 1600;
351 timeout = timeoutAddValue;
353 logger.info("Test 1 redo with changed address: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
360 private void xxxtest3() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException {
361 int timeSinceFirstTransmission = 0;
362 int timeout = timeoutInitValue;
365 // Test 3 including response
366 DatagramSocket sendSocket = new DatagramSocket(new InetSocketAddress(iaddress, 0));
367 sendSocket.connect(InetAddress.getByName(stunServer), port);
368 sendSocket.setSoTimeout(timeout);
370 MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
371 sendMH.generateTransactionID();
373 ChangeRequest changeRequest = new ChangeRequest();
374 changeRequest.setChangePort();
375 sendMH.addMessageAttribute(changeRequest);
377 byte[] data = sendMH.getBytes();
378 DatagramPacket send = new DatagramPacket(data, data.length);
379 sendSocket.send(send);
380 logger.info("Test 3: Binding Request sent.");
382 int localPort = sendSocket.getLocalPort();
383 InetAddress localAddress = sendSocket.getLocalAddress();
387 DatagramSocket receiveSocket = new DatagramSocket(localPort, localAddress);
388 receiveSocket.connect(InetAddress.getByName(stunServer), ca.getPort());
389 receiveSocket.setSoTimeout(timeout);
391 MessageHeader receiveMH = new MessageHeader();
392 while (!(receiveMH.equalTransactionID(sendMH))) {
393 DatagramPacket receive = new DatagramPacket(new byte[200], 200);
394 receiveSocket.receive(receive);
395 receiveMH = MessageHeader.parseHeader(receive.getData());
396 receiveMH.parseAttributes(receive.getData());
398 ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
400 di.setError(ec.getResponseCode(), ec.getReason());
401 logger.info("Message header contains an Errorcode message attribute.");
405 di.setRestrictedCone();
406 logger.info("Node is behind a restricted NAT.");
409 } catch (SocketTimeoutException ste) {
410 if (timeSinceFirstTransmission < 7900) {
411 logger.info("Test 3: Socket timeout while receiving the response.");
412 timeSinceFirstTransmission += timeout;
413 int timeoutAddValue = (timeSinceFirstTransmission * 2);
414 if (timeoutAddValue > 1600) timeoutAddValue = 1600;
415 timeout = timeoutAddValue;
417 logger.info("Test 3: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
418 di.setPortRestrictedCone();
419 logger.info("Node is behind a port restricted NAT.");