123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- /*
- * This file is part of JSTUN.
- *
- * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
- * reserved.
- *
- * This software is licensed under either the GNU Public License (GPL),
- * or the Apache 2.0 license. Copies of both license agreements are
- * included in this distribution.
- */
-
- package de.javawi.jstun.test;
-
- import java.io.IOException;
- import java.net.DatagramPacket;
- import java.net.DatagramSocket;
- import java.net.InetAddress;
- import java.net.InetSocketAddress;
- import java.net.SocketException;
- import java.net.SocketTimeoutException;
- import java.net.UnknownHostException;
- import java.util.logging.Logger;
-
- import de.javawi.jstun.attribute.ChangeRequest;
- import de.javawi.jstun.attribute.ChangedAddress;
- import de.javawi.jstun.attribute.ErrorCode;
- import de.javawi.jstun.attribute.MappedAddress;
- import de.javawi.jstun.attribute.MessageAttribute;
- import de.javawi.jstun.attribute.MessageAttributeException;
- import de.javawi.jstun.attribute.MessageAttributeParsingException;
- import de.javawi.jstun.header.MessageHeader;
- import de.javawi.jstun.header.MessageHeaderParsingException;
- import de.javawi.jstun.util.UtilityException;
-
- public class DiscoveryTest {
- private static Logger logger = Logger.getLogger("de.javawi.stun.test.DiscoveryTest");
- InetAddress iaddress;
- String stunServer;
- int port;
- int timeoutInitValue = 300; //ms
- MappedAddress ma = null;
- ChangedAddress ca = null;
- boolean nodeNatted = true;
- DatagramSocket socketTest1 = null;
- DiscoveryInfo di = null;
-
- public DiscoveryTest(InetAddress iaddress , String stunServer, int port) {
- super();
- this.iaddress = iaddress;
- this.stunServer = stunServer;
- this.port = port;
- }
-
- public DiscoveryInfo test() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException{
- ma = null;
- ca = null;
- nodeNatted = true;
- socketTest1 = null;
- di = new DiscoveryInfo(iaddress);
-
- if (test1()) {
- if (test2()) {
- if (test1Redo()) {
- test3();
- }
- }
- }
-
- socketTest1.close();
-
- return di;
- }
-
- private boolean test1() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageHeaderParsingException {
- int timeSinceFirstTransmission = 0;
- int timeout = timeoutInitValue;
- while (true) {
- try {
- // Test 1 including response
- socketTest1 = new DatagramSocket(new InetSocketAddress(iaddress, 0));
- socketTest1.setReuseAddress(true);
- socketTest1.connect(InetAddress.getByName(stunServer), port);
- socketTest1.setSoTimeout(timeout);
-
- MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
- sendMH.generateTransactionID();
-
- ChangeRequest changeRequest = new ChangeRequest();
- sendMH.addMessageAttribute(changeRequest);
-
- byte[] data = sendMH.getBytes();
- DatagramPacket send = new DatagramPacket(data, data.length);
- socketTest1.send(send);
- logger.finer("Test 1: Binding Request sent.");
-
- MessageHeader receiveMH = new MessageHeader();
- while (!(receiveMH.equalTransactionID(sendMH))) {
- DatagramPacket receive = new DatagramPacket(new byte[200], 200);
- socketTest1.receive(receive);
- receiveMH = MessageHeader.parseHeader(receive.getData());
- receiveMH.parseAttributes(receive.getData());
- }
-
- ma = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
- ca = (ChangedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ChangedAddress);
- ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
- if (ec != null) {
- di.setError(ec.getResponseCode(), ec.getReason());
- logger.config("Message header contains an Errorcode message attribute.");
- return false;
- }
- if ((ma == null) || (ca == null)) {
- di.setError(700, "The server is sending an incomplete response (Mapped Address and Changed Address message attributes are missing). The client should not retry.");
- logger.config("Response does not contain a Mapped Address or Changed Address message attribute.");
- return false;
- } else {
- di.setPublicIP(ma.getAddress().getInetAddress());
- if ((ma.getPort() == socketTest1.getLocalPort()) && (ma.getAddress().getInetAddress().equals(socketTest1.getLocalAddress()))) {
- logger.fine("Node is not natted.");
- nodeNatted = false;
- } else {
- logger.fine("Node is natted.");
- }
- return true;
- }
- } catch (SocketTimeoutException ste) {
- if (timeSinceFirstTransmission < 7900) {
- logger.finer("Test 1: Socket timeout while receiving the response.");
- timeSinceFirstTransmission += timeout;
- int timeoutAddValue = (timeSinceFirstTransmission * 2);
- if (timeoutAddValue > 1600) timeoutAddValue = 1600;
- timeout = timeoutAddValue;
- } else {
- // node is not capable of udp communication
- logger.finer("Test 1: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
- di.setBlockedUDP();
- logger.fine("Node is not capable of UDP communication.");
- return false;
- }
- }
- }
- }
-
- private boolean test2() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException {
- int timeSinceFirstTransmission = 0;
- int timeout = timeoutInitValue;
- while (true) {
- try {
- // Test 2 including response
- DatagramSocket sendSocket = new DatagramSocket(new InetSocketAddress(iaddress, 0));
- sendSocket.connect(InetAddress.getByName(stunServer), port);
- sendSocket.setSoTimeout(timeout);
-
- MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
- sendMH.generateTransactionID();
-
- ChangeRequest changeRequest = new ChangeRequest();
- changeRequest.setChangeIP();
- changeRequest.setChangePort();
- sendMH.addMessageAttribute(changeRequest);
-
- byte[] data = sendMH.getBytes();
- DatagramPacket send = new DatagramPacket(data, data.length);
- sendSocket.send(send);
- logger.finer("Test 2: Binding Request sent.");
-
- int localPort = sendSocket.getLocalPort();
- InetAddress localAddress = sendSocket.getLocalAddress();
-
- sendSocket.close();
-
- DatagramSocket receiveSocket = new DatagramSocket(localPort, localAddress);
- receiveSocket.connect(ca.getAddress().getInetAddress(), ca.getPort());
- receiveSocket.setSoTimeout(timeout);
-
- MessageHeader receiveMH = new MessageHeader();
- while(!(receiveMH.equalTransactionID(sendMH))) {
- DatagramPacket receive = new DatagramPacket(new byte[200], 200);
- receiveSocket.receive(receive);
- receiveMH = MessageHeader.parseHeader(receive.getData());
- receiveMH.parseAttributes(receive.getData());
- }
- ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
- if (ec != null) {
- di.setError(ec.getResponseCode(), ec.getReason());
- logger.config("Message header contains an Errorcode message attribute.");
- return false;
- }
- if (!nodeNatted) {
- di.setOpenAccess();
- logger.fine("Node has open access to the Internet (or, at least the node is behind a full-cone NAT without translation).");
- } else {
- di.setFullCone();
- logger.fine("Node is behind a full-cone NAT.");
- }
- return false;
- } catch (SocketTimeoutException ste) {
- if (timeSinceFirstTransmission < 7900) {
- logger.finer("Test 2: Socket timeout while receiving the response.");
- timeSinceFirstTransmission += timeout;
- int timeoutAddValue = (timeSinceFirstTransmission * 2);
- if (timeoutAddValue > 1600) timeoutAddValue = 1600;
- timeout = timeoutAddValue;
- } else {
- logger.finer("Test 2: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
- if (!nodeNatted) {
- di.setSymmetricUDPFirewall();
- logger.fine("Node is behind a symmetric UDP firewall.");
- return false;
- } else {
- // not is natted
- // redo test 1 with address and port as offered in the changed-address message attribute
- return true;
- }
- }
- }
- }
- }
-
- private boolean test1Redo() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageHeaderParsingException{
- int timeSinceFirstTransmission = 0;
- int timeout = timeoutInitValue;
- while (true) {
- // redo test 1 with address and port as offered in the changed-address message attribute
- try {
- // Test 1 with changed port and address values
- socketTest1.connect(ca.getAddress().getInetAddress(), ca.getPort());
- socketTest1.setSoTimeout(timeout);
-
- MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
- sendMH.generateTransactionID();
-
- ChangeRequest changeRequest = new ChangeRequest();
- sendMH.addMessageAttribute(changeRequest);
-
- byte[] data = sendMH.getBytes();
- DatagramPacket send = new DatagramPacket(data, data.length);
- socketTest1.send(send);
- logger.finer("Test 1 redo with changed address: Binding Request sent.");
-
- MessageHeader receiveMH = new MessageHeader();
- while (!(receiveMH.equalTransactionID(sendMH))) {
- DatagramPacket receive = new DatagramPacket(new byte[200], 200);
- socketTest1.receive(receive);
- receiveMH = MessageHeader.parseHeader(receive.getData());
- receiveMH.parseAttributes(receive.getData());
- }
- MappedAddress ma2 = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
- ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
- if (ec != null) {
- di.setError(ec.getResponseCode(), ec.getReason());
- logger.config("Message header contains an Errorcode message attribute.");
- return false;
- }
- if (ma2 == null) {
- di.setError(700, "The server is sending an incomplete response (Mapped Address message attribute is missing). The client should not retry.");
- logger.config("Response does not contain a Mapped Address message attribute.");
- return false;
- } else {
- if ((ma.getPort() != ma2.getPort()) || (!(ma.getAddress().getInetAddress().equals(ma2.getAddress().getInetAddress())))) {
- di.setSymmetric();
- logger.fine("Node is behind a symmetric NAT.");
- return false;
- }
- }
- return true;
- } catch (SocketTimeoutException ste2) {
- if (timeSinceFirstTransmission < 7900) {
- logger.config("Test 1 redo with changed address: Socket timeout while receiving the response.");
- timeSinceFirstTransmission += timeout;
- int timeoutAddValue = (timeSinceFirstTransmission * 2);
- if (timeoutAddValue > 1600) timeoutAddValue = 1600;
- timeout = timeoutAddValue;
- } else {
- logger.config("Test 1 redo with changed address: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
- return false;
- }
- }
- }
- }
-
- private void test3() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException {
- int timeSinceFirstTransmission = 0;
- int timeout = timeoutInitValue;
- while (true) {
- try {
- // Test 3 including response
- DatagramSocket sendSocket = new DatagramSocket(new InetSocketAddress(iaddress, 0));
- sendSocket.connect(InetAddress.getByName(stunServer), port);
- sendSocket.setSoTimeout(timeout);
-
- MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
- sendMH.generateTransactionID();
-
- ChangeRequest changeRequest = new ChangeRequest();
- changeRequest.setChangePort();
- sendMH.addMessageAttribute(changeRequest);
-
- byte[] data = sendMH.getBytes();
- DatagramPacket send = new DatagramPacket(data, data.length);
- sendSocket.send(send);
- logger.finer("Test 3: Binding Request sent.");
-
- int localPort = sendSocket.getLocalPort();
- InetAddress localAddress = sendSocket.getLocalAddress();
-
- sendSocket.close();
-
- DatagramSocket receiveSocket = new DatagramSocket(localPort, localAddress);
- receiveSocket.connect(InetAddress.getByName(stunServer), ca.getPort());
- receiveSocket.setSoTimeout(timeout);
-
- MessageHeader receiveMH = new MessageHeader();
- while (!(receiveMH.equalTransactionID(sendMH))) {
- DatagramPacket receive = new DatagramPacket(new byte[200], 200);
- receiveSocket.receive(receive);
- receiveMH = MessageHeader.parseHeader(receive.getData());
- receiveMH.parseAttributes(receive.getData());
- }
- ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
- if (ec != null) {
- di.setError(ec.getResponseCode(), ec.getReason());
- logger.config("Message header contains an Errorcode message attribute.");
- return;
- }
- if (nodeNatted) {
- di.setRestrictedCone();
- logger.fine("Node is behind a restricted NAT.");
- return;
- }
- } catch (SocketTimeoutException ste) {
- if (timeSinceFirstTransmission < 7900) {
- logger.finer("Test 3: Socket timeout while receiving the response.");
- timeSinceFirstTransmission += timeout;
- int timeoutAddValue = (timeSinceFirstTransmission * 2);
- if (timeoutAddValue > 1600) timeoutAddValue = 1600;
- timeout = timeoutAddValue;
- } else {
- logger.finer("Test 3: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
- di.setPortRestrictedCone();
- logger.fine("Node is behind a port restricted NAT.");
- return;
- }
- }
- }
- }
- }
|