1 /*
2  * Created on 18-Jan-2006
3  * Created by Paul Gardner
4  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
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  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  */
19 
20 package com.aelitis.azureus.core.networkmanager.impl;
21 
22 import java.io.IOException;
23 import java.net.InetSocketAddress;
24 import java.nio.ByteBuffer;
25 
26 import org.gudy.azureus2.core3.logging.LogEvent;
27 import org.gudy.azureus2.core3.logging.LogIDs;
28 import org.gudy.azureus2.core3.logging.Logger;
29 import org.gudy.azureus2.core3.util.AENetworkClassifier;
30 import org.gudy.azureus2.core3.util.AddressUtils;
31 import org.gudy.azureus2.core3.util.SystemTime;
32 
33 import com.aelitis.azureus.core.networkmanager.NetworkManager;
34 
35 public class
36 ProtocolDecoderInitial
37 	extends ProtocolDecoder
38 {
39 	private static final LogIDs LOGID = LogIDs.NWMAN;
40 
41 	private ProtocolDecoderAdapter	adapter;
42 
43 	private TransportHelperFilter	filter;
44 
45 	private TransportHelper	transport;
46 
47 	private byte[][]	shared_secrets;
48 	private ByteBuffer	initial_data;
49 	private ByteBuffer	decode_buffer;
50 	private int			decode_read;
51 
52 	private long	start_time	= SystemTime.getCurrentTime();
53 
54 	private ProtocolDecoderPHE	phe_decoder;
55 
56 	private long	last_read_time		= 0;
57 
58 	private boolean processing_complete;
59 
60 	public
ProtocolDecoderInitial( TransportHelper _transport, byte[][] _shared_secrets, boolean _outgoing, ByteBuffer _initial_data, ProtocolDecoderAdapter _adapter )61 	ProtocolDecoderInitial(
62 		TransportHelper				_transport,
63 		byte[][]					_shared_secrets,
64 		boolean						_outgoing,
65 		ByteBuffer					_initial_data,
66 		ProtocolDecoderAdapter		_adapter )
67 
68 		throws IOException
69 	{
70 		super( true );
71 
72 		transport		= _transport;
73 		shared_secrets	= _shared_secrets;
74 		initial_data	= _initial_data;
75 		adapter			= _adapter;
76 
77 		final TransportHelperFilterTransparent transparent_filter = new TransportHelperFilterTransparent( transport, false );
78 
79 		filter	= transparent_filter;
80 
81 		if ( _outgoing ){  //we assume that for outgoing connections, if we are here, we want to use crypto
82 
83 			if ( ProtocolDecoderPHE.isCryptoOK()){
84 
85 				decodePHE( null );
86 
87 			}else{
88 
89 				throw( new IOException( "Crypto required but unavailable" ));
90 			}
91 
92 		}else{
93 
94 			decode_buffer = ByteBuffer.allocate( adapter.getMaximumPlainHeaderLength());
95 
96 			transport.registerForReadSelects(
97 				new TransportHelper.selectListener()
98 				{
99 				   	public boolean
100 			    	selectSuccess(
101 			    		TransportHelper	helper,
102 			    		Object 			attachment )
103 				   	{
104 						try{
105 							int	len = helper.read( decode_buffer );
106 
107 							if ( len < 0 ){
108 
109 								failed( new IOException( "end of stream on socket read: in=" + decode_buffer.position()));
110 
111 							}else if ( len == 0 ){
112 
113 								return( false );
114 							}
115 
116 							last_read_time = SystemTime.getCurrentTime();
117 
118 							decode_read += len;
119 
120 							int	match =  adapter.matchPlainHeader( decode_buffer );
121 
122 							if ( match != ProtocolDecoderAdapter.MATCH_NONE ){
123 
124 								helper.cancelReadSelects();
125 
126 								if ( NetworkManager.REQUIRE_CRYPTO_HANDSHAKE && match == ProtocolDecoderAdapter.MATCH_CRYPTO_NO_AUTO_FALLBACK ){
127 
128 									InetSocketAddress isa = transport.getAddress();
129 
130 									if ( NetworkManager.INCOMING_HANDSHAKE_FALLBACK_ALLOWED ){
131 										if (Logger.isEnabled()){
132 											Logger.log(new LogEvent(LOGID, "Incoming connection ["+ isa + "] is not encrypted but has been accepted as fallback is enabled" ));
133 										}
134 									}else if( AddressUtils.isLANLocalAddress( AddressUtils.getHostAddress( isa )) == AddressUtils.LAN_LOCAL_YES ) {
135 										if (Logger.isEnabled()){
136 											Logger.log(new LogEvent(LOGID, "Incoming connection ["+ isa + "] is not encrypted but has been accepted as lan-local" ));
137 										}
138 									}else if ( AENetworkClassifier.categoriseAddress( isa ) != AENetworkClassifier.AT_PUBLIC ){
139 										if (Logger.isEnabled()){
140 											Logger.log(new LogEvent(LOGID, "Incoming connection ["+ isa + "] is not encrypted but has been accepted as not a public network" ));
141 										}
142 									}else{
143 										throw( new IOException( "Crypto required but incoming connection has none" ));
144 									}
145 								}
146 
147 								decode_buffer.flip();
148 
149 								transparent_filter.insertRead( decode_buffer );
150 
151 								complete( initial_data );
152 
153 							}else{
154 
155 								if ( !decode_buffer.hasRemaining()){
156 
157 									helper.cancelReadSelects();
158 
159 									if ( NetworkManager.INCOMING_CRYPTO_ALLOWED ){
160 
161 										decode_buffer.flip();
162 
163 										decodePHE( decode_buffer );
164 
165 									}else{
166 
167 										if (Logger.isEnabled())
168 											Logger.log(new LogEvent(LOGID, "Incoming connection ["+ transport.getAddress() + "] encrypted but rejected as not permitted" ));
169 
170 										throw( new IOException( "Incoming crypto connection not permitted" ));
171 									}
172 								}
173 							}
174 
175 							return( true );
176 
177 						}catch( Throwable e ){
178 
179 							selectFailure( helper, attachment, e );
180 
181 							return( false );
182 						}
183 				   	}
184 
185 			        public void
186 			        selectFailure(
187 			        	TransportHelper	helper,
188 			        	Object 			attachment,
189 			        	Throwable 		msg)
190 			        {
191 						helper.cancelReadSelects();
192 
193 						failed( msg );
194 			        }
195 				},
196 				this );
197 		}
198 	}
199 
200 	protected void
decodePHE( ByteBuffer buffer )201 	decodePHE(
202 		ByteBuffer	buffer )
203 
204 		throws IOException
205 	{
206 		ProtocolDecoderAdapter	phe_adapter =
207 			new ProtocolDecoderAdapter()
208 			{
209 				public void
210 				decodeComplete(
211 					ProtocolDecoder	decoder,
212 					ByteBuffer		remaining_initial_data )
213 				{
214 					filter = decoder.getFilter();
215 
216 					complete( remaining_initial_data );
217 				}
218 
219 				public void
220 				decodeFailed(
221 					ProtocolDecoder	decoder,
222 					Throwable			cause )
223 				{
224 					failed( cause );
225 				}
226 
227 				public void
228 				gotSecret(
229 					byte[]				session_secret )
230 				{
231 					adapter.gotSecret( session_secret );
232 				}
233 
234 				public int
235 				getMaximumPlainHeaderLength()
236 				{
237 					throw( new RuntimeException());
238 				}
239 
240 				public int
241 				matchPlainHeader(
242 					ByteBuffer			buffer )
243 				{
244 					throw( new RuntimeException());
245 				}
246 			};
247 
248 		phe_decoder = new ProtocolDecoderPHE( transport, shared_secrets, buffer, initial_data, phe_adapter );
249 	}
250 
251 	public boolean
isComplete( long now )252 	isComplete(
253 		long	now )
254 	{
255 		if ( transport == null ){
256 
257 				// can happen during initialisation
258 
259 			return( false );
260 		}
261 
262 		if ( !processing_complete ){
263 
264 			if ( start_time > now ){
265 
266 				start_time	= now;
267 			}
268 
269 			if ( last_read_time > now ){
270 
271 				last_read_time	= now;
272 			}
273 
274 			if ( phe_decoder != null ){
275 
276 				last_read_time	= phe_decoder.getLastReadTime();
277 			}
278 
279 			long	timeout;
280 			long	time;
281 
282 			if ( last_read_time == 0 ){
283 
284 				timeout = transport.getConnectTimeout();
285 				time	= start_time;
286 
287 			}else{
288 
289 				timeout = transport.getReadTimeout();
290 				time	= last_read_time;
291 			}
292 
293 			if ( now - time > timeout ){
294 
295 				try{
296 					transport.cancelReadSelects();
297 
298 					transport.cancelWriteSelects();
299 
300 				}catch( Throwable e ){
301 
302 				}
303 
304 				String	phe_str = "";
305 
306 				if ( phe_decoder != null ){
307 
308 					phe_str = ", crypto: " + phe_decoder.getString();
309 				}
310 
311 		       	if ( Logger.isEnabled()){
312 
313 					Logger.log(new LogEvent(LOGID, "Connection ["
314 							+ transport.getAddress() + "] forcibly timed out after "
315 							+ timeout/1000 + "sec due to socket inactivity"));
316 		       	}
317 
318 				failed( new Throwable( "Protocol decode aborted: timed out after " + timeout/1000+ "sec: " + decode_read + " bytes read" + phe_str ));
319 			}
320 		}
321 
322 		return( processing_complete );
323 	}
324 
325 	public TransportHelperFilter
getFilter()326 	getFilter()
327 	{
328 		return( filter );
329 	}
330 
331 	protected void
complete( ByteBuffer remaining_initial_data )332 	complete(
333 		ByteBuffer	remaining_initial_data )
334 	{
335 		if ( !processing_complete ){
336 
337 			processing_complete	= true;
338 
339 			adapter.decodeComplete( this, remaining_initial_data );
340 		}
341 	}
342 
343 	protected void
failed( Throwable reason )344 	failed(
345 		Throwable	reason )
346 	{
347 		if ( !processing_complete ){
348 
349 			processing_complete	= true;
350 
351 			adapter.decodeFailed( this, reason );
352 		}
353 	}
354 }
355