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