package edu.washington.cs.oneswarm.f2f; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.nio.ByteBuffer; import java.security.PublicKey; import java.util.Enumeration; import java.util.List; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; import org.bouncycastle.util.encoders.Base64; import org.gudy.azureus2.core3.download.DownloadManager; import org.gudy.azureus2.core3.global.GlobalManagerStats; 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.PluginInterface; import org.gudy.azureus2.plugins.PluginListener; import com.aelitis.azureus.core.impl.AzureusCoreImpl; import com.aelitis.azureus.core.networkmanager.NetworkConnection; import com.aelitis.azureus.core.networkmanager.NetworkManager; import com.aelitis.azureus.core.networkmanager.impl.TransportHelper; import com.aelitis.azureus.core.networkmanager.impl.osssl.OneSwarmSslKeyManager; import com.aelitis.azureus.core.networkmanager.impl.osssl.OneSwarmSslTransportHelperFilterStream; import com.aelitis.azureus.core.peermanager.messaging.MessageStreamDecoder; import com.aelitis.azureus.core.peermanager.messaging.MessageStreamEncoder; import com.aelitis.azureus.core.peermanager.messaging.MessageStreamFactory; import edu.washington.cs.oneswarm.f2f.dht.DHTConnector; import edu.washington.cs.oneswarm.f2f.friends.FriendManager; import edu.washington.cs.oneswarm.f2f.friends.LanFriendFinder; import edu.washington.cs.oneswarm.f2f.invitations.InvitationManager; import edu.washington.cs.oneswarm.f2f.messaging.OSF2FMessage; import edu.washington.cs.oneswarm.f2f.messaging.OSF2FMessageDecoder; import edu.washington.cs.oneswarm.f2f.messaging.OSF2FMessageEncoder; import edu.washington.cs.oneswarm.f2f.messaging.invitation.OSF2FAuthMessage; import edu.washington.cs.oneswarm.f2f.messaging.invitation.OSF2FAuthMessageDecoder; import edu.washington.cs.oneswarm.f2f.messaging.invitation.OSF2FAuthMessageEncoder; import edu.washington.cs.oneswarm.f2f.multisource.Sha1DownloadManager; import edu.washington.cs.oneswarm.f2f.multisource.Sha1HashManager.Sha1CalcListener; import edu.washington.cs.oneswarm.f2f.multisource.Sha1HashManager.Sha1HashJobListener; import edu.washington.cs.oneswarm.f2f.multisource.Sha1HashManager.Sha1Result; import edu.washington.cs.oneswarm.f2f.network.F2FDownloadManager; import edu.washington.cs.oneswarm.f2f.network.OverlayManager; import edu.washington.cs.oneswarm.f2f.permissions.GroupBean; import edu.washington.cs.oneswarm.f2f.permissions.PermissionsDAO; public class OSF2FMain { private static Logger logger = Logger.getLogger(OSF2FMain.class.getName()); private boolean initialized = false; private OverlayManager overlayManager; private FriendManager friendManager; private DHTConnector friendConnector; private FileListManager fileListManager; private F2FDownloadManager f2DownloadManager; private final static OSF2FMain instance = new OSF2FMain(); private ClassLoader classLoader; private LanFriendFinder lanFriendFinder; private PermissionsDAO permissionManager; private GlobalManagerStats stats; private OSF2FNatChecker natChecker; private OSF2FSpeedChecker speedChecker; private Sha1DownloadManager sha1DownloadManager; public static OSF2FMain getSingelton() { return instance; } protected OSF2FMain() { try { /* * check if the logging is initialized, if not load it */ System.err.println("OSF2FMain: loading log settings. cwd: " + (new File(".")).getAbsolutePath()); File logConfig = new File("./logging.properties"); if (logConfig.exists()) { System.err.println("OSF2FMain: log file found"); synchronized (LogManager.class) { final LogManager logManager = LogManager.getLogManager(); Enumeration<String> loggers = logManager.getLoggerNames(); // check if we have any non null loggers boolean logConfLoaded = false; while (loggers.hasMoreElements()) { final String l = loggers.nextElement(); if (l.length() > 0 && Logger.getLogger(l).getLevel() != null) { logConfLoaded = true; break; } } if (!logConfLoaded) { BufferedReader in = new BufferedReader(new FileReader(logConfig)); String line; int num = 0; while ((line = in.readLine()) != null) { if ("# libs".equals(line)) { break; } if (line.startsWith("#")) { continue; } try { String[] toks = line.split(".level"); if (toks.length == 2) { // force-load the classes so the logging is // initialized properly during testing Class.forName(toks[0]).getCanonicalName(); num++; } } catch (Exception e) { System.err.println("class not found: " + e.getMessage()); } } logManager.readConfiguration(new FileInputStream(logConfig)); System.err.println("OSF2FMain: read log configuration (" + num + " classes configured)"); Enumeration<String> loggerNames = logManager.getLoggerNames(); while (loggerNames.hasMoreElements()) { final String l = loggerNames.nextElement(); System.err.println("OSF2FMain: log-" + l + " " + Logger.getLogger(l).getLevel()); } } } System.err.println("OSF2FMain: logger created"); logger.fine("logger loaded"); System.err.println("OSF2FMain: logger loaded"); } } catch (IOException e) { Debug.out("error loading logger", e); } } private long startTime; private void logWithTime(String msg, Level level) { final String text = (System.currentTimeMillis() - startTime) + " ms:: " + msg; logger.log(level, text); System.err.println("OSF2FMain: " + text); } private InvitationManager invitationManager; public void init(final PluginInterface pIf) { logWithTime("init started", Level.FINE); synchronized (instance) { if (!initialized) { initialized = true; startTime = System.currentTimeMillis(); logWithTime("in synchronized block", Level.FINE); this.classLoader = pIf.getPluginClassLoader(); logWithTime("getting global manager stats", Level.FINE); this.stats = AzureusCoreImpl.getSingleton().getGlobalManager().getStats(); logWithTime("getting own public key", Level.FINE); PublicKey localPublicKey = OneSwarmSslKeyManager.getInstance().getOwnPublicKey(); logWithTime("creating friend manager", Level.FINE); this.friendManager = new FriendManager(classLoader, localPublicKey.getEncoded()); logWithTime("loading permissions manager", Level.FINE); this.permissionManager = PermissionsDAO.get(); this.permissionManager.init(pIf.getIPC()); logWithTime("creating invitation manager", Level.FINE); this.invitationManager = new InvitationManager(classLoader, friendManager, localPublicKey.getEncoded()); logWithTime("creating file list manager", Level.FINE); this.fileListManager = new FileListManager(permissionManager); logWithTime("creating overlaymanager", Level.FINE); this.overlayManager = new OverlayManager(friendManager, localPublicKey, fileListManager, stats); logWithTime("starting protocol matcher thread)", Level.FINE); Thread t = new Thread(new Runnable() { @Override public void run() { logWithTime("Protocol matcher thread started", Level.FINE); OsProtocolMatcher osMatcher = new OsProtocolMatcher(true); NetworkManager.getSingleton().requestIncomingConnectionRouting(osMatcher, new OsNetworkRouterListener(), new MessageStreamFactory() { @Override public MessageStreamDecoder createDecoder() { return new OSF2FMessageDecoder(); } @Override public MessageStreamEncoder createEncoder() { return new OSF2FMessageEncoder(); } }); logWithTime("OS Protocol matcher installed", Level.FINE); OsProtocolMatcher osAuthMatcher = new OsProtocolMatcher(false); NetworkManager.getSingleton().requestIncomingConnectionRouting( osAuthMatcher, new OsAuthRouterListener(), new MessageStreamFactory() { @Override public MessageStreamDecoder createDecoder() { return new OSF2FAuthMessageDecoder(); } @Override public MessageStreamEncoder createEncoder() { return new OSF2FAuthMessageEncoder(); } }); logWithTime("OS-Auth Protocol matcher installed", Level.FINE); } }); t.setDaemon(true); t.setName("OSF2F protocol matcher loader"); t.start(); pIf.addListener(new PluginListener() { @Override public void initializationComplete() { AEThread2 t = new AEThread2("OSF2F::dhtConnect", true) { @Override public void run() { logWithTime("creating friend connector", Level.FINE); friendConnector = new DHTConnector(friendManager, invitationManager, overlayManager); logWithTime("crating lan friend finder", Level.FINE); try { lanFriendFinder = new LanFriendFinder(friendConnector, friendManager, invitationManager, overlayManager .getOwnPublicKey().getEncoded()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } logWithTime("friendConnector dependent init completed", Level.FINE); } }; t.start(); } @Override public void closedownInitiated() { } @Override public void closedownComplete() { } }); logWithTime("creating f2f download manager", Level.FINE); this.f2DownloadManager = new F2FDownloadManager(overlayManager); this.natChecker = new OSF2FNatChecker(); this.speedChecker = new OSF2FSpeedChecker(stats); logWithTime("loading sha1 matcher", Level.INFO); sha1DownloadManager = new Sha1DownloadManager(); sha1DownloadManager.addHashJobListener(new Sha1HashJobListener() { @Override public Sha1CalcListener jobAdded(String name) { return new Sha1CalcListener() { @Override public void completed(Sha1Result result) { fileListManager.scheduleFileListRefresh(); } @Override public void errorOccured(Throwable cause) { } @Override public void progress(double fraction) { } }; } }); sanity_check_perms(); PermissionsDAO.get().f2fInitialized(); logWithTime("f2f init done", Level.INFO); } } } public OSF2FNatChecker getNatChecker() { return natChecker; } public OSF2FSpeedChecker getSpeedChecker() { return speedChecker; } @SuppressWarnings("unchecked") private void sanity_check_perms() { long start = System.currentTimeMillis(); for (DownloadManager dm : (List<DownloadManager>) AzureusCoreImpl.getSingleton() .getGlobalManager().getDownloadManagers()) { try { byte[] hashBytes = dm.getTorrent().getHash(); List<GroupBean> groups = permissionManager.getGroupsForHash(ByteFormatter .encodeString(hashBytes)); boolean any_friend = false, pub_net = false; for (GroupBean g : groups) { if (g.equals(GroupBean.PUBLIC) == false) { any_friend = true; } else { pub_net = true; } } boolean update = false; if (F2FDownloadManager.isSharedWithFriends(hashBytes) != any_friend) { logger.warning("f2f mismatch in network / perms. perm: " + any_friend + " / net: " + F2FDownloadManager.isSharedWithFriends(hashBytes)); update = true; } if (F2FDownloadManager.isSharedWithPublic(hashBytes) != pub_net) { logger.warning("public mismatch in network / perms. perm: " + pub_net + " / net: " + F2FDownloadManager.isSharedWithPublic(hashBytes)); update = true; } if (update) { f2DownloadManager.setTorrentPrivacy(hashBytes, pub_net, any_friend); } else { // Log.log("good perms/privacy for: " + dm.getDisplayName() // + " f2f: " + any_friend + " pub: " + pub_net); } } catch (Exception e) { StackTraceElement[] st = e.getStackTrace(); logWithTime( "error sanity checking perms for: " + dm.getDisplayName() + " / " + e.toString() + " " + st[0].getFileName() + ":" + st[0].getLineNumber(), Level.WARNING); } } logWithTime("sanity check perms took: " + (System.currentTimeMillis() - start), Level.FINE); } public LanFriendFinder getLanFriendFinder() { return lanFriendFinder; } public F2FDownloadManager getF2DownloadManager() { return f2DownloadManager; } public FriendManager getFriendManager() { return friendManager; } public DHTConnector getDHTConnector() { return friendConnector; } public OverlayManager getOverlayManager() { return overlayManager; } private class OsNetworkRouterListener implements NetworkManager.RoutingListener { @Override public boolean autoCryptoFallback() { return (false); } @Override public void connectionRouted(NetworkConnection connection, Object routing_data) { if (!connection.getTransport().getEncryption() .startsWith(OneSwarmSslTransportHelperFilterStream.SSL_NAME)) { logger.warning("OSF2F connection without SSL!: " + connection + " (" + connection.getTransport().getEncryption() + "), disconnecting"); connection.close(); return; } if (routing_data == null || !(routing_data instanceof byte[])) { logger.warning("got strange routing data: " + routing_data); connection.close(); return; } byte[] remotePub = (byte[]) routing_data; logger.finest("Incoming connection from [" + connection + "] successfully routed to OneSwarm F2F: encr: " + connection.getTransport().getEncryption() + "\n" + "remote public key: " + new String(Base64.encode(remotePub))); Friend f = friendManager.getFriend(remotePub); if (f == null) { logger.warning("unknown remote host (not friend), disconnecting. Remote public key: \n" + new String(Base64.encode(remotePub))); friendManager.gotConnectionFromNonFriend(connection.getEndpoint() .getNotionalAddress().getAddress().getHostAddress(), remotePub); connection.close(); } else if (f.isBlocked()) { logger.warning("connection from blocked user: " + f.getNick() + ", disconnecting"); connection.close(); } else { logger.finer("connection from: " + f.getNick()); overlayManager.createIncomingConnection(remotePub, connection); } } } private class OsAuthRouterListener implements NetworkManager.RoutingListener { @Override public boolean autoCryptoFallback() { return (false); } @Override public void connectionRouted(NetworkConnection connection, Object routing_data) { if (!connection.getTransport().getEncryption() .startsWith(OneSwarmSslTransportHelperFilterStream.SSL_NAME)) { logger.warning("OSAuth connection without SSL!: " + connection + " (" + connection.getTransport().getEncryption() + ")"); connection.close(); return; } if (routing_data == null || !(routing_data instanceof byte[])) { logger.warning("got strange routing data: " + routing_data); connection.close(); return; } byte[] remotePub = (byte[]) routing_data; if (remotePub == null) { logger.warning("got incoming auth connection with null public key!"); connection.close(); return; } logger.fine("Incoming connection from [" + connection + "] successfully routed to OneSwarm Auth: encr: " + connection.getTransport().getEncryption() + "\n" + "remote public key: " + new String(Base64.encode(remotePub))); if (!invitationManager.newIncomingConnection(remotePub, connection)) { connection.close(); } } } static class OsProtocolMatcher implements NetworkManager.ByteMatcher { private final ByteBuffer legacy_handshake_header; private int size; public OsProtocolMatcher(boolean defaultProtocol) { if (defaultProtocol) { legacy_handshake_header = ByteBuffer.allocate(20); legacy_handshake_header.put((byte) OSF2FMessage.ONESWARM_PROTOCOL.length()); legacy_handshake_header.put(OSF2FMessage.ONESWARM_PROTOCOL.getBytes()); legacy_handshake_header.flip(); this.size = legacy_handshake_header.remaining(); } else { legacy_handshake_header = ByteBuffer.allocate(20); legacy_handshake_header .put((byte) OSF2FAuthMessage.ONESWARM_AUTH_PROTOCOL.length()); legacy_handshake_header.put(OSF2FAuthMessage.ONESWARM_AUTH_PROTOCOL.getBytes()); legacy_handshake_header.flip(); this.size = legacy_handshake_header.remaining(); } } @Override public byte[][] getSharedSecrets() { return null; } @Override public int getSpecificPort() { return (-1); } @Override public Object matches(TransportHelper transport, ByteBuffer to_compare, int port) { // System.out.println("incoming connection to match: " // + transport.getAddress()); Object obj = transport .getUserData(OneSwarmSslTransportHelperFilterStream.REMOTE_SSL_PUBLIC_KEY); if (!(obj instanceof byte[])) { return null; } // System.out.println(new String(to_compare.array())); int old_limit = to_compare.limit(); to_compare.limit(to_compare.position() + maxSize()); boolean matches = to_compare.equals(legacy_handshake_header); to_compare.limit(old_limit); // restore buffer structure if (!matches) { return null; } return obj; } @Override public int matchThisSizeOrBigger() { return (maxSize()); } @Override public int maxSize() { return size; } @Override public Object minMatches(TransportHelper transport, ByteBuffer to_compare, int port) { return (matches(transport, to_compare, port)); } @Override public int minSize() { return maxSize(); } } public boolean isInitialized() { return initialized; } public ClassLoader getClassLoader() { return classLoader; } public void redeemInvitation(FriendInvitation invitation, boolean testOnly) throws Exception { invitationManager.redeemInvitation(invitation, testOnly); } public InvitationManager getAuthManager() { return invitationManager; } public List<FriendInvitation> getLocallyCreatedInvitations() { return invitationManager.getLocallyCreatedInvitations(); } }