/* * Created on 19-Jan-2006 * Created by Paul Gardner * Copyright (C) 2006 Aelitis, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * AELITIS, SAS au capital de 46,603.30 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. * */ package com.aelitis.azureus.core.networkmanager.impl; import java.io.IOException; import java.nio.ByteBuffer; import org.gudy.azureus2.core3.util.DirectByteBuffer; import org.gudy.azureus2.core3.util.DirectByteBufferPool; public abstract class TransportHelperFilterStream implements TransportHelperFilter { private TransportHelper transport; private DirectByteBuffer write_buffer_pending_db; private ByteBuffer write_buffer_pending_byte; protected TransportHelperFilterStream( TransportHelper _transport ) { transport = _transport; } public boolean hasBufferedWrite() { return( write_buffer_pending_db != null || write_buffer_pending_byte != null || transport.hasDelayedWrite()); } public boolean hasBufferedRead() { return false; } public TransportHelper getHelper() { return( transport ); } public long write( ByteBuffer[] buffers, int array_offset, int length ) throws IOException { // deal with any outstanding cached crypted data first if ( write_buffer_pending_byte != null ){ if ( transport.write( write_buffer_pending_byte, false ) == 0 ){ return( 0 ); } write_buffer_pending_byte = null; } long total_written = 0; if ( write_buffer_pending_db != null ){ ByteBuffer write_buffer_pending = write_buffer_pending_db.getBuffer( DirectByteBuffer.SS_NET ); int max_writable = 0; for (int i=array_offset;i<array_offset+length;i++){ ByteBuffer source_buffer = buffers[i]; int position = source_buffer.position(); int limit = source_buffer.limit(); int size = limit - position; max_writable += size; } int pending_position = write_buffer_pending.position(); int pending_limit = write_buffer_pending.limit(); int pending_writable = pending_limit - pending_position; if ( pending_writable > max_writable ){ pending_writable = max_writable; write_buffer_pending.limit( pending_position + pending_writable ); } int written = transport.write( write_buffer_pending, false ); write_buffer_pending.limit( pending_limit ); if ( written > 0 ){ total_written = written; if ( write_buffer_pending.remaining() == 0 ){ write_buffer_pending_db.returnToPool(); write_buffer_pending_db = null; } // skip "written" bytes in the source int skip = written; for (int i=array_offset;i<array_offset+length;i++){ ByteBuffer source_buffer = buffers[i]; int position = source_buffer.position(); int limit = source_buffer.limit(); int size = limit - position; if ( size <= skip ){ source_buffer.position( limit ); skip -= size; }else{ source_buffer.position( position + skip ); skip = 0; break; } } if ( skip != 0 ){ throw( new IOException( "skip inconsistent - " + skip )); } } // if write came up short or we've filled the source buffer then we can't do // any more if ( total_written < pending_writable || total_written == max_writable ){ return( total_written ); } } // problem - we must only crypt stuff once and when crypted it *has* // to be sent (else the stream will get out of sync). // so we have to turn this into single buffer operations for (int i=array_offset;i<array_offset+length;i++){ ByteBuffer source_buffer = buffers[i]; int position = source_buffer.position(); int limit = source_buffer.limit(); int size = limit - position; if ( size == 0 ){ continue; } DirectByteBuffer target_buffer_db = DirectByteBufferPool.getBuffer( DirectByteBuffer.AL_NET_CRYPT, size ); try{ ByteBuffer target_buffer = target_buffer_db.getBuffer( DirectByteBuffer.SS_NET ); cryptoOut( source_buffer, target_buffer ); target_buffer.position( 0 ); boolean partial_write = false; for (int j=i+1;j<array_offset+length;j++){ if ( buffers[j].hasRemaining()){ partial_write = true; } } int written = transport.write( target_buffer, partial_write ); total_written += written; source_buffer.position( position + written ); if ( written < size ){ write_buffer_pending_db = target_buffer_db; target_buffer_db = null; if ( written == 0 ){ // we gotta pretend at least 1 byte was written to // guarantee that the caller writes the rest write_buffer_pending_byte = ByteBuffer.wrap(new byte[]{target_buffer.get()}); source_buffer.get(); total_written++; } break; } }finally{ if ( target_buffer_db != null ){ target_buffer_db.returnToPool(); } } } // System.out.println( "...write " + total_written ); return( total_written ); } public long read( ByteBuffer[] buffers, int array_offset, int length ) throws IOException { int total_read = 0; DirectByteBuffer[] copy_db = new DirectByteBuffer[buffers.length]; ByteBuffer[] copy = new ByteBuffer[buffers.length]; try{ for (int i=array_offset;i<array_offset+length;i++){ ByteBuffer buffer = buffers[i]; int size = buffer.remaining(); if ( size > 0 ){ copy_db[i] = DirectByteBufferPool.getBuffer( DirectByteBuffer.AL_NET_CRYPT, size ); copy[i] = copy_db[i].getBuffer( DirectByteBuffer.SS_NET ); }else{ copy[i] = ByteBuffer.allocate(0); } } total_read += transport.read( copy, array_offset, length ); for (int i=array_offset;i<array_offset+length;i++){ ByteBuffer source_buffer = copy[i]; if ( source_buffer != null ){ ByteBuffer target_buffer = buffers[i]; int source_position = source_buffer.position(); if ( source_position > 0 ){ source_buffer.flip(); cryptoIn( source_buffer, target_buffer ); } } } // System.out.println( "...read " + total_read ); return( total_read ); }finally{ for (int i=0;i<copy_db.length;i++){ if ( copy_db[i] != null ){ copy_db[i].returnToPool(); } } } } public void setTrace( boolean on ) { transport.setTrace( on ); } protected abstract void cryptoOut( ByteBuffer source_buffer, ByteBuffer target_buffer ) throws IOException; protected abstract void cryptoIn( ByteBuffer source_buffer, ByteBuffer target_buffer ) throws IOException; }