package edu.uw.cse.netlab.reputation.storage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.Serializable; import java.net.InetAddress; import java.security.PublicKey; import java.security.Signature; import java.util.Date; import java.util.List; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.util.AEThread2; import org.gudy.azureus2.core3.util.ByteFormatter; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.plugins.ddb.DistributedDatabase; import org.gudy.azureus2.plugins.ddb.DistributedDatabaseEvent; import org.gudy.azureus2.plugins.ddb.DistributedDatabaseException; import org.gudy.azureus2.plugins.ddb.DistributedDatabaseKey; import org.gudy.azureus2.plugins.ddb.DistributedDatabaseListener; import org.gudy.azureus2.plugins.ddb.DistributedDatabaseValue; import org.gudy.azureus2.pluginsimpl.local.ddb.DDBaseImpl; import com.aelitis.azureus.core.AzureusCore; import com.aelitis.azureus.core.AzureusCoreComponent; import com.aelitis.azureus.core.AzureusCoreException; import com.aelitis.azureus.core.AzureusCoreLifecycleListener; import com.aelitis.azureus.core.impl.AzureusCoreImpl; import edu.uw.cse.netlab.reputation.LocalIdentity; import edu.uw.cse.netlab.utils.ByteManip; import edu.uw.cse.netlab.utils.CoreWaiter; import edu.uw.cse.netlab.utils.KeyManipulation; /** * * This class is responsible for keeping our local table of soft state key -> IP mappings * fresh. * */ class SoftState { private static final long serialVersionUID = 1L; public byte [] ip; public int tcp_port; public int udp_port; public byte [] sig; public long [] salt = new long[]{ (long)(Math.random()*Long.MAX_VALUE), (long)(Math.random()*Long.MAX_VALUE), (long)(Math.random()*Long.MAX_VALUE), (long)(Math.random()*Long.MAX_VALUE), (long)(Math.random()*Long.MAX_VALUE) }; public Date timestamp = new Date(); public String toString() { try { return "IP: " + ip == null ? "null" : InetAddress.getByAddress(ip) + " TCP: " + tcp_port + " UDP: " + udp_port + " Timestamp: " + timestamp; } catch( Exception e ) { return null; } } public byte [] getBytes() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(protectedBytes()); baos.write(sig); return baos.toByteArray(); } public byte [] protectedBytes() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); for( int i=0; i<salt.length; i++ ) dos.writeLong(salt[i]); dos.writeShort((short)ip.length); baos.write(ip); dos.writeShort((short)tcp_port); dos.writeShort((short)udp_port); dos.writeLong(timestamp.getTime()); return baos.toByteArray(); } public static SoftState fromBytes( byte [] inBytes ) throws IOException { SoftState s = new SoftState(); ByteArrayInputStream bais = new ByteArrayInputStream(inBytes); DataInputStream dis = new DataInputStream(bais); for( int i=0; i<s.salt.length; i++ ) s.salt[i] = dis.readLong(); short ip_size = (short)dis.readShort(); if( ip_size > 64 ) { throw new IOException("bogus IP size"); } s.ip = new byte[ip_size]; bais.read(s.ip); s.tcp_port = dis.readShort(); s.udp_port = dis.readShort(); if( s.tcp_port < 0 ) s.tcp_port += 65536; if( s.udp_port < 0 ) s.udp_port += 65536; s.timestamp = new Date(dis.readLong()); s.sig = new byte[64]; bais.read(s.sig); return s; } public void sign_local() { sig = null; try { byte [] dat = protectedBytes(); Signature s = Signature.getInstance("SHA1withRSA"); s.initSign(LocalIdentity.get().getKeys().getPrivate()); s.update(dat); sig = s.sign(); } catch( Exception e ) { System.err.println(e); e.printStackTrace(); } } }; public class SoftStateSync extends CoreWaiter { // For local state refresh and lookups public static final int TIMEOUT_MINUTES = 3; DistributedDatabase mDB; public SoftStateSync() { super(); } protected void init() { mDB = DDBaseImpl.getSingleton(AzureusCoreImpl.getSingleton()); (new AEThread2("Local IP refresher", true) { public void run() { //System.out.println("local IP refresher thread started"); long next = 0; while( true ) { // once per hour. try { next = 60 * 60 * 1000; if( mDB.isAvailable() ) { republishLocalID(); } else { // Give the DHT some time to start //System.out.println("dht not available, onehop waiting 10 seconds..."); next = 10 * 1000; } } catch( IOException e ) { System.err.println(e); e.printStackTrace(); } try { Thread.sleep(next); } catch( InterruptedException e ) { System.err.println(e); e.printStackTrace(); } } } }).start(); } public void refreshRemoteID( final PublicKey inID, final SoftStateListener inListener ) throws IOException { // Don't perform these repeatedly // TODO: magic constant -- min time between IP refreshes Date lastUpdate = ReputationDAO.get().get_last_soft_state_update(inID); if( lastUpdate != null && lastUpdate.after(new Date(System.currentTimeMillis()-(15*60*1000))) ) { //System.out.println("skipping refresh of id due to timing: " + ByteFormatter.encodeString(inID.getEncoded()) ); return; } //System.out.println("refreshing remote id: " + KeyManipulation.concise(inID.getEncoded()) ); DistributedDatabaseKey k = keyFromPub(inID); try { mDB.read(new DistributedDatabaseListener() { public void event( DistributedDatabaseEvent event ) { //System.out.println("DDB event: " + event.getType() + " " + event.toString()); try { if( event.getType() == DistributedDatabaseEvent.ET_VALUE_READ ) { byte [] rbytes = (byte[])event.getValue().getValue(byte[].class); SoftState s = SoftState.fromBytes(rbytes); // verify the signature for this entry, this is kind of kludgy byte [] real_sig = s.sig; byte [] bytes = s.protectedBytes(); try { Signature sigs = Signature.getInstance("SHA1withRSA"); sigs.initVerify(inID); sigs.update(bytes); if( sigs.verify(real_sig) == false ) { System.err.println("sig check failed for soft state update: " + s); return; } ReputationDAO rep = ReputationDAO.get(); rep.update_soft_state(inID, s.ip, s.tcp_port, s.udp_port, s.timestamp); if( inListener != null ) inListener.refresh_complete(inID); } catch( Exception e ) { System.err.println(e); e.printStackTrace(); } } // event occurred } catch( Exception e ) { System.err.println(e); e.printStackTrace(); } } }, k, TIMEOUT_MINUTES * 60 * 1000); } catch( DistributedDatabaseException e ) { System.err.println(e); e.printStackTrace(); } } private DistributedDatabaseKey keyFromPub( PublicKey inID ) { try { return mDB.createKey(inID.getEncoded()); } catch( DistributedDatabaseException e ) { System.err.println(e); e.printStackTrace(); return null; } } boolean debug = false; public void republishLocalID( ) throws IOException { if( debug ) return; //System.out.println("republishing local: " + KeyManipulation.concise(LocalIdentity.get().getKeys().getPublic().getEncoded())); try { DistributedDatabaseKey k = keyFromPub(LocalIdentity.get().getKeys().getPublic()); SoftState s = new SoftState(); s.ip = InetAddress.getLocalHost().getAddress(); s.tcp_port = (short)COConfigurationManager.getIntParameter("TCP.Listen.Port"); s.udp_port = (short)COConfigurationManager.getIntParameter("UDP.Listen.Port"); s.sign_local(); byte [] bytes = s.getBytes(); DistributedDatabaseValue v = mDB.createValue(bytes); //System.out.println("putting in: " + k + " " + v + " value size: " + bytes.length ); mDB.write(new DistributedDatabaseListener() { public void event( DistributedDatabaseEvent event ) { if( event.getType() == DistributedDatabaseEvent.ET_OPERATION_COMPLETE || event.getType() == DistributedDatabaseEvent.ET_OPERATION_TIMEOUT ) { // System.out.println("refresh local id event: " + event.getType() + " / " + event.toString()); } } }, k, v); } catch( DistributedDatabaseException e ) { throw new IOException(e.toString()); } } public static void main( String [] args ) throws Exception { // ByteArrayOutputStream baos = new ByteArrayOutputStream(); // DataOutputStream dos = new DataOutputStream(baos); // dos.writeInt(12); // byte [] random = baos.toByteArray(); // // Signature gen = Signature.getInstance("SHA1withRSA"); // gen.initSign(LocalIdentity.get().getKeys().getPrivate()); // gen.update(random); // byte [] sig = gen.sign(); // // gen = Signature.getInstance("SHA1withRSA"); // gen.initVerify(LocalIdentity.get().getKeys().getPublic()); // gen.update(random); // if( gen.verify(sig) == false ) // System.out.println("hash check fails"); // else // System.out.println("hash check pass"); // FileInputStream f = new FileInputStream("/tmp/read1"); // byte [] b = new byte[f.available()]; // f.read(b); // SoftState s = SoftState.fromBytes(b); // System.out.println(s); // // byte [] bytes = s.protectedBytes(); // try // { // Signature sigs = Signature.getInstance("SHA1withRSA"); // sigs.initVerify(LocalIdentity.get().getKeys().getPublic()); // sigs.update(bytes); // if( sigs.verify(s.sig) == false ) // { // System.err.println("sig check failed for soft state update: " + s); // return; // } // } // catch( Exception e ) // { // e.printStackTrace(); // } SoftState s = new SoftState(); s.ip = InetAddress.getLocalHost().getAddress(); s.tcp_port = (short)COConfigurationManager.getIntParameter("TCP.Listen.Port"); s.udp_port = (short)COConfigurationManager.getIntParameter("UDP.Listen.Port"); s.sign_local(); byte [] bytes = s.getBytes(); //System.out.println("it's " + bytes.length + " bytes"); // // SoftState s2 = SoftState.fromBytes(bytes); // System.out.println(s); // System.out.println(s2); // // bytes = s2.protectedBytes(); // try // { // Signature sigs = Signature.getInstance("SHA1withRSA"); // sigs.initVerify(LocalIdentity.get().getKeys().getPublic()); // sigs.update(bytes); // if( sigs.verify(s.sig) == false ) // { // System.err.println("sig check failed for soft state update: " + s); // } // } // catch( Exception e ) // { // e.printStackTrace(); // } // FileInputStream fis = new FileInputStream("dhtbytes4696307"); // byte [] b = new byte[fis.available()]; // fis.read(b); // // SoftState s = SoftState.fromBytes(b); // // System.out.println(s); // // System.out.println(s.sig.length); // // System.out.println(ByteFormatter.encodeString(s.sig)); // s.sign_local(); // // System.out.println(s.sig.length); // System.out.println(ByteFormatter.encodeString(s.sig)); // // DEBUG // FileOutputStream out = new FileOutputStream("/tmp/read1"); // out.write(bytes); // out.close(); // } }