View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  
20  package org.xnap.plugin.opennap.net;
21  
22  import java.io.BufferedInputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.InterruptedIOException;
26  import java.io.OutputStream;
27  import java.net.ServerSocket;
28  import java.net.Socket;
29  import java.net.SocketException;
30  import java.util.List;
31  import java.util.Vector;
32  
33  import org.apache.log4j.Logger;
34  import org.xnap.plugin.opennap.net.msg.MessageHandler;
35  import org.xnap.plugin.opennap.net.msg.client.ChangeDataPortMessage;
36  import org.xnap.util.PortRange;
37  
38  // FIX: a DOS attack is easily possible by connecting to the listener
39  // and not sending data...
40  public class OpenNapListener  {
41  
42      //--- Constant(s) ---
43  
44      //--- Data field(s) ---
45  
46      protected static Logger logger = Logger.getLogger(ListenerThread.class);
47  
48      private PortRange range = null;
49      private ListenerThread runner = null;
50  
51      private List listeners = new Vector();
52      
53      //--- Constructor(s) ---
54      
55      public OpenNapListener(PortRange range) 
56      {
57  		setPortRange(range);
58      }
59  
60      public OpenNapListener()
61      {
62      }
63      
64      //--- Method(s) ---
65      
66      public void addSocketListener(SocketListener s)
67      {
68  		listeners.add(s);
69      }
70  
71      public void removeSocketListener(SocketListener s)
72      {
73  		listeners.remove(s);
74      }
75  
76      public int getPort()
77      {
78  		return (runner != null) ? runner.getPort() : 0;
79      }
80  
81      public void die() 
82      {
83  		if (runner != null) {
84  			runner.die();
85  			runner = null;
86  		}
87      }
88  
89      private void start(int port)
90      {
91  		if (range != null) {
92  			ServerSocket socket = null;
93  
94  			if (port != 0) {
95  				try {
96  					// try to open the socket on port first
97  					socket = new ServerSocket(port);
98  				} 
99  				catch (IOException e) {
100 				}
101 			}
102 
103 			if (socket == null) {
104 				// bind to next free port
105 				for (PortRange.IntIterator i = range.random(); i.hasNext();) {
106 					try {
107 						socket = new ServerSocket(i.next());
108 						break;
109 					} 
110 					catch (IOException e) {
111 					}
112 				}
113 			}
114 
115 			if (socket != null) {
116 				runner = new ListenerThread(socket);
117 				runner.start();
118 			}
119 			else {
120 				logger.debug("could not start listener on "
121 							 + range.toString());
122 			}
123 		}
124     }
125 
126     private void restart(int port)
127     {
128 		int oldPort = getPort();
129 
130 		die();
131 		start(port);
132 
133 		if (oldPort != getPort()) {
134 			// notify servers of changed port
135 			MessageHandler.send(new ChangeDataPortMessage(getPort()));
136 		}
137 
138     }
139 
140     private void restart()
141     {
142 		restart(0);
143     }
144     
145     /***
146      * Sets a new port range and restarts socket thread if needed.
147      */
148     public void setPortRange(PortRange newValue)
149     {
150 		PortRange oldValue = range;
151 		range = newValue;
152 	
153 		if (newValue != null && newValue.contains(getPort())) {
154 			// no need to restart the listener
155 			return;
156 		}
157 
158 		restart();
159     }
160 
161     private void fireSocketReceived(IncomingSocket s)
162     {
163         Object[] l = listeners.toArray();
164 
165 		if (l != null) {
166 			for (int i = l.length - 1; i >= 0; i--) {
167 				if (((SocketListener)l[i]).socketReceived(s)) {
168 					return;
169 				}
170 			}
171 		}
172 
173 		s.close();
174     }
175 
176     //--- Inner Class(es) ---
177 
178     private class ListenerThread extends Thread {
179 	
180 		private ServerSocket listenerSocket = null;
181 		private boolean die = false;
182 	
183 		public ListenerThread(ServerSocket listenerSocket)
184 		{
185 			super("OpenNapListener :" + listenerSocket.getLocalPort());
186 	    
187 			this.listenerSocket = listenerSocket;
188 		}
189 
190 		public void die()
191 		{
192 			die = true;
193 		}
194 
195 		public int getPort()
196 		{
197 			return listenerSocket.getLocalPort();
198 		}
199 
200 		private void handleSocket(Socket s)
201 		{
202 			InputStream in = null;
203 			OutputStream out = null;
204 	    
205 			try {
206 				in = new BufferedInputStream(s.getInputStream());
207 				out = s.getOutputStream();
208 		
209 				out.write('1');
210 				out.flush();
211 		
212 				byte data[] = new byte[2048];
213 				in.mark(8);
214 				int i = in.read(data, 0, 8);
215 		
216 				if (i > 0) {
217 					String response = new String(data, 0, i);
218 		    
219 					logger.debug("received: " + response);
220 					in.reset();
221 		    
222 					if (response.startsWith("GETLIST")) {
223 						in.skip(7);
224 						fireSocketReceived(new BrowseUploadSocket(s, in));
225 						logger.debug("creating BrowseUploadSocket");
226 					}
227 					else if (response.startsWith("GET")) {
228 						in.skip(3);
229 						fireSocketReceived(new UploadSocket(s, in));
230 						logger.debug("creating UploadSocket");
231 					}
232 					else if (response.startsWith("SENDLIST")) {
233 						// add to queue for reverse direct browse threads
234 						in.skip(8);
235 						fireSocketReceived(new BrowseDownloadSocket(s, in));
236 						logger.debug("creating BrowseDownloadSocket");
237 					}
238 					else if (response.startsWith("SEND")) {
239 						in.skip(4);
240 						// add to queue for reverse download threads
241 						fireSocketReceived(new DownloadSocket(s, in));
242 						logger.debug("creating DownloadSocket");
243 					}
244 					else {
245 						throw new IOException("invalid request: " + response);
246 					}
247 
248 				}
249 				else {
250 					throw new IOException("empty request");
251 				}
252 			} 
253 			catch (IOException e) {
254 				logger.warn("invalid listener request", e);
255 		
256 				try {
257 					if (s != null) {
258 						s.close();
259 					}
260 					if (in != null) {
261 						in.close();
262 					}
263 					if (out != null) {
264 						out.close();
265 					}
266 				} 
267 				catch (IOException ioe) {
268 				}
269 			}
270 		}
271 	
272 
273 		public void run() 
274 		{
275 			boolean restart = false;
276 			logger.debug("started listener on port " + getPort());
277 	    
278 			try {
279 				listenerSocket.setSoTimeout(10 * 1000);
280 			} 
281 			catch(SocketException e) {
282 				restart = true;
283 				die = true;
284 			}
285 	    
286 			while(!die) {
287 				Socket socket = null;
288 				try {
289 					socket = listenerSocket.accept();
290 					// only wait 10 seconds for a request, otherwise
291 					// the listener might be blocked forever.
292 					socket.setSoTimeout(30 * 1000);
293 				}
294 				catch(InterruptedIOException e) {
295 					continue;		
296 				}
297 				catch(IOException e) {
298 					restart = true;
299 					break;
300 				}
301 		
302 				if (socket != null) {
303 					handleSocket(socket);
304 				}
305 			}
306 	
307 			try {
308 				listenerSocket.close();
309 			} 
310 			catch(IOException e) {
311 			}
312 
313 			logger.debug("stopped listener on port " + getPort());
314 	    
315 			if (restart) {
316 				// the thread has died unexpectively, restart it
317 				logger.debug("listener died unexpectively, restarting");
318 				restart(getPort());
319 			}
320 		}
321 	
322     }
323 
324 }