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.

DCC.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /*
  2. * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.addons.dcc;
  23. import java.net.Socket;
  24. import java.net.ServerSocket;
  25. import java.io.IOException;
  26. import java.util.concurrent.Semaphore;
  27. /**
  28. * This class handles the main "grunt work" of DCC, subclasses process the data
  29. * received by this class.
  30. *
  31. * @author Shane 'Dataforce' McCormack
  32. */
  33. public abstract class DCC implements Runnable {
  34. /** Address. */
  35. protected long address = 0;
  36. /** Port. */
  37. protected int port = 0;
  38. /** Socket used to communicate with. */
  39. protected Socket socket;
  40. /** The Thread in use for this. */
  41. private volatile Thread myThread;
  42. /** Are we already running? */
  43. protected boolean running = false;
  44. /** Are we a listen socket? */
  45. protected boolean listen = false;
  46. /**
  47. * The current socket in use if this is a listen socket.
  48. * This reference may be changed if and only if exactly one permit from the
  49. * <code>serverSocketSem</code> and <code>serverListenignSem</code>
  50. * semaphores is held by the thread doing the modification.
  51. */
  52. private ServerSocket serverSocket;
  53. /**
  54. * Semaphore to control write access to ServerSocket.
  55. * If an object acquires a permit from the <code>serverSocketSem</code>, then
  56. * <code>serverSocket</code> is <em>guaranteed</em> not to be externally
  57. * modified until that permit is released, <em>unless</em> the object also
  58. * acquires a permit from the <code>serverListeningSem</code>.
  59. */
  60. private final Semaphore serverSocketSem = new Semaphore(1);
  61. /**
  62. * Semaphore used when we're blocking waiting for connections.
  63. * If an object acquires a permit from the <code>serverListeningSem</code>,
  64. * then it is <em>guaranteed</em> that the {@link #run()} method is blocking
  65. * waiting for incoming connections. In addition, it is <em>guaranteed</em>
  66. * that the {@link #run()} method is holding the <code>serverSocketSem</code>
  67. * permit, and it will continue holding that permit until it can reaquire
  68. * the <code>serverListeningSem</code> permit.
  69. */
  70. private final Semaphore serverListeningSem = new Semaphore(0);
  71. /**
  72. * Creates a new instance of DCC.
  73. */
  74. public DCC() {
  75. super();
  76. }
  77. /**
  78. * Connect this dcc.
  79. */
  80. public void connect() {
  81. try {
  82. if (listen) {
  83. address = 0;
  84. port = serverSocket.getLocalPort();
  85. } else {
  86. // socket = new Socket(longToIP(address), port, bindIP, 0);
  87. socket = new Socket(longToIP(address), port);
  88. socketOpened();
  89. }
  90. } catch (IOException ioe) {
  91. socketClosed();
  92. return;
  93. }
  94. myThread = new Thread(this);
  95. myThread.start();
  96. }
  97. /**
  98. * Start a listen socket rather than a connect socket.
  99. *
  100. * @throws IOException If the listen socket can't be created
  101. */
  102. public void listen() throws IOException {
  103. serverSocketSem.acquireUninterruptibly();
  104. serverSocket = new ServerSocket(0, 1);
  105. serverSocketSem.release();
  106. listen = true;
  107. connect();
  108. }
  109. /**
  110. * Start a listen socket rather than a connect socket, use a port from the
  111. * given range.
  112. *
  113. * @param startPort Port to try first
  114. * @param endPort Last port to try.
  115. * @throws IOException If no sockets were available in the given range
  116. */
  117. public void listen(final int startPort, final int endPort) throws IOException {
  118. listen = true;
  119. for (int i = startPort; i <= endPort; ++i) {
  120. try {
  121. serverSocketSem.acquireUninterruptibly();
  122. serverSocket = new ServerSocket(i, 1);
  123. serverSocketSem.release();
  124. // Found a socket we can use!
  125. break;
  126. } catch (IOException ioe) {
  127. // Try next socket.
  128. } catch (SecurityException se) {
  129. // Try next socket.
  130. }
  131. }
  132. if (serverSocket == null) {
  133. throw new IOException("No available sockets in range " + startPort + ":" + endPort);
  134. } else {
  135. connect();
  136. }
  137. }
  138. /**
  139. * This handles the socket to keep it out of the main thread
  140. */
  141. @Override
  142. public void run() {
  143. if (running) {
  144. return;
  145. }
  146. running = true;
  147. // handleSocket is implemented by sub classes, and should return false
  148. // when the socket is closed.
  149. Thread thisThread = Thread.currentThread();
  150. while (myThread == thisThread) {
  151. serverSocketSem.acquireUninterruptibly();
  152. if (serverSocket == null) {
  153. serverSocketSem.release();
  154. if (!handleSocket()) {
  155. close();
  156. break;
  157. }
  158. } else {
  159. try {
  160. serverListeningSem.release();
  161. socket = serverSocket.accept();
  162. serverSocket.close();
  163. socketOpened();
  164. } catch (IOException ioe) {
  165. socketClosed();
  166. break;
  167. } finally {
  168. serverListeningSem.acquireUninterruptibly();
  169. serverSocket = null;
  170. serverSocketSem.release();
  171. }
  172. }
  173. // Sleep for a short period of time to reduce CPU usage.
  174. try {
  175. Thread.sleep(100);
  176. } catch (InterruptedException ie) {
  177. }
  178. }
  179. // Socket closed
  180. thisThread = null;
  181. running = false;
  182. }
  183. /**
  184. * Called to close the socket
  185. */
  186. protected void close() {
  187. boolean haveSLS = false;
  188. while (!serverSocketSem.tryAcquire() && !(haveSLS = serverListeningSem.tryAcquire())) {
  189. try {
  190. Thread.sleep(100);
  191. } catch (InterruptedException ex) {
  192. // Do we care? I doubt we do! Should be unchecked damnit.
  193. }
  194. }
  195. if (serverSocket != null) {
  196. try {
  197. if (!serverSocket.isClosed()) {
  198. serverSocket.close();
  199. }
  200. } catch (IOException ioe) {
  201. }
  202. serverSocket = null;
  203. }
  204. if (haveSLS) {
  205. serverListeningSem.release();
  206. } else {
  207. serverSocketSem.release();
  208. }
  209. if (socket != null) {
  210. try {
  211. if (!socket.isClosed()) {
  212. socket.close();
  213. }
  214. } catch (IOException ioe) {
  215. }
  216. socketClosed();
  217. socket = null;
  218. }
  219. }
  220. /**
  221. * Called when the socket is first opened, before any data is handled.
  222. */
  223. protected void socketOpened() {
  224. }
  225. /**
  226. * Called when the socket is closed, before the thread terminates.
  227. */
  228. protected void socketClosed() {
  229. }
  230. /**
  231. * Check if this socket can be written to.
  232. *
  233. * @return True if the socket is writable, false otehrwise
  234. */
  235. public boolean isWriteable() {
  236. return false;
  237. }
  238. /**
  239. * Handle the socket.
  240. *
  241. * @return false when socket is closed, true will cause the method to be
  242. * called again.
  243. */
  244. protected abstract boolean handleSocket();
  245. /**
  246. * Set the address to connect to for this DCC
  247. *
  248. * @param address Address as an int (Network Byte Order, as specified in the DCC CTCP)
  249. * @param port Port to connect to
  250. */
  251. public void setAddress(final long address, final int port) {
  252. this.address = address;
  253. this.port = port;
  254. }
  255. /**
  256. * Is this a listening socket
  257. *
  258. * @return True if this is a listening socket
  259. */
  260. public boolean isListenSocket() {
  261. return listen;
  262. }
  263. /**
  264. * Get the host this socket is listening on/connecting to
  265. *
  266. * @return The IP that this socket is listening on/connecting to.
  267. */
  268. public String getHost() {
  269. return longToIP(address);
  270. }
  271. /**
  272. * Get the port this socket is listening on/connecting to
  273. *
  274. * @return The port that this socket is listening on/connecting to.
  275. */
  276. public int getPort() {
  277. return port;
  278. }
  279. /**
  280. * Convert the given IP Address to a long
  281. *
  282. * @param ip Input IP Address
  283. * @return ip as a long
  284. */
  285. public static long ipToLong(final String ip) {
  286. final String bits[] = ip.split("\\.");
  287. if (bits.length > 3) {
  288. return (Long.parseLong(bits[0]) << 24) + (Long.parseLong(bits[1]) << 16) + (Long.parseLong(bits[2]) << 8) + Long.parseLong(bits[3]);
  289. }
  290. return 0;
  291. }
  292. /**
  293. * Convert the given long to an IP Address
  294. *
  295. * @param in Input long
  296. * @return long as an IP
  297. */
  298. public static String longToIP(final long in) {
  299. return ((in & 0xff000000) >> 24) + "." + ((in & 0x00ff0000) >> 16) + "." + ((in & 0x0000ff00) >> 8) + "." + (in & 0x000000ff);
  300. }
  301. }