Unsupported library that attempts to punch holes through NAT
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

StunServer.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  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.demo;
  12. import java.io.IOException;
  13. import java.net.DatagramPacket;
  14. import java.net.DatagramSocket;
  15. import java.net.InetAddress;
  16. import java.net.SocketException;
  17. import java.net.UnknownHostException;
  18. import java.util.Vector;
  19. import java.util.logging.FileHandler;
  20. import java.util.logging.Handler;
  21. import java.util.logging.Level;
  22. import java.util.logging.Logger;
  23. import java.util.logging.SimpleFormatter;
  24. import de.javawi.jstun.attribute.ChangeRequest;
  25. import de.javawi.jstun.attribute.ChangedAddress;
  26. import de.javawi.jstun.attribute.MappedAddress;
  27. import de.javawi.jstun.attribute.MessageAttributeException;
  28. import de.javawi.jstun.attribute.MessageAttributeParsingException;
  29. import de.javawi.jstun.attribute.ResponseAddress;
  30. import de.javawi.jstun.attribute.SourceAddress;
  31. import de.javawi.jstun.attribute.UnknownAttribute;
  32. import de.javawi.jstun.attribute.UnknownMessageAttributeException;
  33. import de.javawi.jstun.attribute.MessageAttributeInterface.MessageAttributeType;
  34. import de.javawi.jstun.header.MessageHeader;
  35. import de.javawi.jstun.header.MessageHeaderParsingException;
  36. import de.javawi.jstun.header.MessageHeaderInterface.MessageHeaderType;
  37. import de.javawi.jstun.util.Address;
  38. import de.javawi.jstun.util.UtilityException;
  39. /*
  40. * This class implements a STUN server as described in RFC 3489.
  41. * The server requires a machine that is dual-homed to be functional.
  42. */
  43. public class StunServer {
  44. private static Logger logger = Logger.getLogger("de.javawi.stun.test.StunServer");
  45. Vector<DatagramSocket> sockets;
  46. public StunServer(int primaryPort, InetAddress primary, int secondaryPort, InetAddress secondary) throws SocketException {
  47. sockets = new Vector<DatagramSocket>();
  48. sockets.add(new DatagramSocket(primaryPort, primary));
  49. sockets.add(new DatagramSocket(secondaryPort, primary));
  50. sockets.add(new DatagramSocket(primaryPort, secondary));
  51. sockets.add(new DatagramSocket(secondaryPort, secondary));
  52. }
  53. public void start() throws SocketException {
  54. for (DatagramSocket socket : sockets) {
  55. socket.setReceiveBufferSize(2000);
  56. StunServerReceiverThread ssrt = new StunServerReceiverThread(socket);
  57. ssrt.start();
  58. }
  59. }
  60. /*
  61. * Inner class to handle incoming packets and react accordingly.
  62. * I decided not to start a thread for every received Binding Request, because the time
  63. * required to receive a Binding Request, parse it, generate a Binding Response and send
  64. * it varies only between 2 and 4 milliseconds. This amount of time is small enough so
  65. * that no extra thread is needed for incoming Binding Request.
  66. */
  67. class StunServerReceiverThread extends Thread {
  68. private DatagramSocket receiverSocket;
  69. private DatagramSocket changedPort;
  70. private DatagramSocket changedIP;
  71. private DatagramSocket changedPortIP;
  72. StunServerReceiverThread(DatagramSocket datagramSocket) {
  73. this.receiverSocket = datagramSocket;
  74. for (DatagramSocket socket : sockets) {
  75. if ((socket.getLocalPort() != receiverSocket.getLocalPort()) &&
  76. (socket.getLocalAddress().equals(receiverSocket.getLocalAddress())))
  77. changedPort = socket;
  78. if ((socket.getLocalPort() == receiverSocket.getLocalPort()) &&
  79. (!socket.getLocalAddress().equals(receiverSocket.getLocalAddress())))
  80. changedIP = socket;
  81. if ((socket.getLocalPort() != receiverSocket.getLocalPort()) &&
  82. (!socket.getLocalAddress().equals(receiverSocket.getLocalAddress())))
  83. changedPortIP = socket;
  84. }
  85. }
  86. public void run() {
  87. while (true) {
  88. try {
  89. DatagramPacket receive = new DatagramPacket(new byte[200], 200);
  90. receiverSocket.receive(receive);
  91. logger.finest(receiverSocket.getLocalAddress().getHostAddress() + ":" + receiverSocket.getLocalPort() + " datagram received from " + receive.getAddress().getHostAddress() + ":" + receive.getPort());
  92. MessageHeader receiveMH = MessageHeader.parseHeader(receive.getData());
  93. try {
  94. receiveMH.parseAttributes(receive.getData());
  95. if (receiveMH.getType() == MessageHeaderType.BindingRequest) {
  96. logger.config(receiverSocket.getLocalAddress().getHostAddress() + ":" + receiverSocket.getLocalPort() + " Binding Request received from " + receive.getAddress().getHostAddress() + ":" + receive.getPort());
  97. ChangeRequest cr = (ChangeRequest) receiveMH.getMessageAttribute(MessageAttributeType.ChangeRequest);
  98. if (cr == null) throw new MessageAttributeException("Message attribute change request is not set.");
  99. ResponseAddress ra = (ResponseAddress) receiveMH.getMessageAttribute(MessageAttributeType.ResponseAddress);
  100. MessageHeader sendMH = new MessageHeader(MessageHeaderType.BindingResponse);
  101. sendMH.setTransactionID(receiveMH.getTransactionID());
  102. // Mapped address attribute
  103. MappedAddress ma = new MappedAddress();
  104. ma.setAddress(new Address(receive.getAddress().getAddress()));
  105. ma.setPort(receive.getPort());
  106. sendMH.addMessageAttribute(ma);
  107. // Changed address attribute
  108. ChangedAddress ca = new ChangedAddress();
  109. ca.setAddress(new Address(changedPortIP.getLocalAddress().getAddress()));
  110. ca.setPort(changedPortIP.getLocalPort());
  111. sendMH.addMessageAttribute(ca);
  112. if (cr.isChangePort() && (!cr.isChangeIP())) {
  113. logger.finer("Change port received in Change Request attribute");
  114. // Source address attribute
  115. SourceAddress sa = new SourceAddress();
  116. sa.setAddress(new Address(changedPort.getLocalAddress().getAddress()));
  117. sa.setPort(changedPort.getLocalPort());
  118. sendMH.addMessageAttribute(sa);
  119. byte[] data = sendMH.getBytes();
  120. DatagramPacket send = new DatagramPacket(data, data.length);
  121. if (ra != null) {
  122. send.setPort(ra.getPort());
  123. send.setAddress(ra.getAddress().getInetAddress());
  124. } else {
  125. send.setPort(receive.getPort());
  126. send.setAddress(receive.getAddress());
  127. }
  128. changedPort.send(send);
  129. logger.config(changedPort.getLocalAddress().getHostAddress() + ":" + changedPort.getLocalPort() + " send Binding Response to " + send.getAddress().getHostAddress() + ":" + send.getPort());
  130. } else if ((!cr.isChangePort()) && cr.isChangeIP()) {
  131. logger.finer("Change ip received in Change Request attribute");
  132. // Source address attribute
  133. SourceAddress sa = new SourceAddress();
  134. sa.setAddress(new Address(changedIP.getLocalAddress().getAddress()));
  135. sa.setPort(changedIP.getLocalPort());
  136. sendMH.addMessageAttribute(sa);
  137. byte[] data = sendMH.getBytes();
  138. DatagramPacket send = new DatagramPacket(data, data.length);
  139. if (ra != null) {
  140. send.setPort(ra.getPort());
  141. send.setAddress(ra.getAddress().getInetAddress());
  142. } else {
  143. send.setPort(receive.getPort());
  144. send.setAddress(receive.getAddress());
  145. }
  146. changedIP.send(send);
  147. logger.config(changedIP.getLocalAddress().getHostAddress() + ":" + changedIP.getLocalPort() + " send Binding Response to " + send.getAddress().getHostAddress() + ":" + send.getPort());
  148. } else if ((!cr.isChangePort()) && (!cr.isChangeIP())) {
  149. logger.finer("Nothing received in Change Request attribute");
  150. // Source address attribute
  151. SourceAddress sa = new SourceAddress();
  152. sa.setAddress(new Address(receiverSocket.getLocalAddress().getAddress()));
  153. sa.setPort(receiverSocket.getLocalPort());
  154. sendMH.addMessageAttribute(sa);
  155. byte[] data = sendMH.getBytes();
  156. DatagramPacket send = new DatagramPacket(data, data.length);
  157. if (ra != null) {
  158. send.setPort(ra.getPort());
  159. send.setAddress(ra.getAddress().getInetAddress());
  160. } else {
  161. send.setPort(receive.getPort());
  162. send.setAddress(receive.getAddress());
  163. }
  164. receiverSocket.send(send);
  165. logger.config(receiverSocket.getLocalAddress().getHostAddress() + ":" + receiverSocket.getLocalPort() + " send Binding Response to " + send.getAddress().getHostAddress() + ":" + send.getPort());
  166. } else if (cr.isChangePort() && cr.isChangeIP()) {
  167. logger.finer("Change port and ip received in Change Request attribute");
  168. // Source address attribute
  169. SourceAddress sa = new SourceAddress();
  170. sa.setAddress(new Address(changedPortIP.getLocalAddress().getAddress()));
  171. sa.setPort(changedPortIP.getLocalPort());
  172. sendMH.addMessageAttribute(sa);
  173. byte[] data = sendMH.getBytes();
  174. DatagramPacket send = new DatagramPacket(data, data.length);
  175. if (ra != null) {
  176. send.setPort(ra.getPort());
  177. send.setAddress(ra.getAddress().getInetAddress());
  178. } else {
  179. send.setPort(receive.getPort());
  180. send.setAddress(receive.getAddress());
  181. }
  182. changedPortIP.send(send);
  183. logger.config(changedPortIP.getLocalAddress().getHostAddress() + ":" + changedPortIP.getLocalPort() + " send Binding Response to " + send.getAddress().getHostAddress() + ":" + send.getPort());
  184. }
  185. }
  186. } catch (UnknownMessageAttributeException umae) {
  187. umae.printStackTrace();
  188. // Generate Binding error response
  189. MessageHeader sendMH = new MessageHeader(MessageHeaderType.BindingErrorResponse);
  190. sendMH.setTransactionID(receiveMH.getTransactionID());
  191. // Unknown attributes
  192. UnknownAttribute ua = new UnknownAttribute();
  193. ua.addAttribute(umae.getType());
  194. sendMH.addMessageAttribute(ua);
  195. byte[] data = sendMH.getBytes();
  196. DatagramPacket send = new DatagramPacket(data, data.length);
  197. send.setPort(receive.getPort());
  198. send.setAddress(receive.getAddress());
  199. receiverSocket.send(send);
  200. logger.config(changedPortIP.getLocalAddress().getHostAddress() + ":" + changedPortIP.getLocalPort() + " send Binding Error Response to " + send.getAddress().getHostAddress() + ":" + send.getPort());
  201. }
  202. } catch (IOException ioe) {
  203. ioe.printStackTrace();
  204. } catch (MessageAttributeParsingException mape) {
  205. mape.printStackTrace();
  206. } catch (MessageAttributeException mae) {
  207. mae.printStackTrace();
  208. } catch (MessageHeaderParsingException mhpe) {
  209. mhpe.printStackTrace();
  210. } catch (UtilityException ue) {
  211. ue.printStackTrace();
  212. } catch (ArrayIndexOutOfBoundsException aioobe) {
  213. aioobe.printStackTrace();
  214. }
  215. }
  216. }
  217. }
  218. /*
  219. * To invoke the STUN server two IP addresses and two ports are required.
  220. */
  221. public static void main(String args[]) {
  222. try {
  223. if (args.length != 4) {
  224. System.out.println("usage: java de.javawi.jstun.test.demo.StunServer PORT1 IP1 PORT2 IP2");
  225. System.out.println();
  226. System.out.println(" PORT1 - the first port that should be used by the server");
  227. System.out.println(" IP1 - the first ip address that should be used by the server");
  228. System.out.println(" PORT2 - the second port that should be used by the server");
  229. System.out.println(" IP2 - the second ip address that should be used by the server");
  230. System.exit(0);
  231. }
  232. Handler fh = new FileHandler("logging_server.txt");
  233. fh.setFormatter(new SimpleFormatter());
  234. Logger.getLogger("de.javawi.stun").addHandler(fh);
  235. Logger.getLogger("de.javawi.stun").setLevel(Level.ALL);
  236. StunServer ss = new StunServer(Integer.parseInt(args[0]),
  237. InetAddress.getByName(args[1]),
  238. Integer.parseInt(args[2]),
  239. InetAddress.getByName(args[3]));
  240. ss.start();
  241. } catch (SocketException se) {
  242. se.printStackTrace();
  243. } catch (UnknownHostException uhe) {
  244. uhe.printStackTrace();
  245. } catch (IOException ioe) {
  246. ioe.printStackTrace();
  247. }
  248. }
  249. }