package com.limegroup.gnutella.dht.db; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.limewire.core.settings.DHTSettings; import org.limewire.io.Connectable; import org.limewire.io.ConnectableImpl; import org.limewire.io.GUID; import org.limewire.io.IpPort; import org.limewire.io.IpPortImpl; import org.limewire.mojito.EntityKey; import org.limewire.mojito.KUID; import org.limewire.mojito.concurrent.DHTFuture; import org.limewire.mojito.db.DHTValue; import org.limewire.mojito.db.DHTValueEntity; import org.limewire.mojito.result.FindValueResult; import org.limewire.mojito.routing.Contact; import org.limewire.nio.observer.Shutdownable; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.inject.name.Named; import com.limegroup.gnutella.PushEndpoint; import com.limegroup.gnutella.URN; import com.limegroup.gnutella.altlocs.AltLocManager; import com.limegroup.gnutella.altlocs.AlternateLocation; import com.limegroup.gnutella.altlocs.AlternateLocationFactory; import com.limegroup.gnutella.dht.DHTManager; import com.limegroup.gnutella.dht.util.KUIDUtils; /** * Default implementation of {@link AltLocFinder}, uses the DHT to find * alternate locations. */ @Singleton class AltLocFinderImpl implements AltLocFinder { private static final Log LOG = LogFactory.getLog(AltLocFinderImpl.class); private final DHTManager dhtManager; private final AlternateLocationFactory alternateLocationFactory; private final AltLocManager altLocManager; private final PushEndpointService pushEndpointManager; @Inject public AltLocFinderImpl(DHTManager dhtManager, AlternateLocationFactory alternateLocationFactory, AltLocManager altLocManager, @Named("pushEndpointManager") PushEndpointService pushEndpointManager) { this.dhtManager = dhtManager; this.alternateLocationFactory = alternateLocationFactory; this.altLocManager = altLocManager; this.pushEndpointManager = pushEndpointManager; } public Shutdownable findAltLocs(URN urn, SearchListener<AlternateLocation> listener) { listener = SearchListenerAdapter.nonNullListener(listener); KUID key = KUIDUtils.toKUID(urn); EntityKey lookupKey = EntityKey.createEntityKey(key, AbstractAltLocValue.ALT_LOC); final DHTFuture<FindValueResult> future = dhtManager.get(lookupKey); if(future == null) { return null; } else { future.addFutureListener(new AltLocsHandler(dhtManager, urn, key, listener)); return new Shutdownable() { public void shutdown() { future.cancel(true); } }; } } /** * Looks up the push endpoint for an alternate location based on the guid. */ private void findPushAltLocs(final GUID guid, final URN urn, final DHTValueEntity altLocEntity, final SearchListener<AlternateLocation> listener) { SearchListener<PushEndpoint> pushEndpointListener = new SearchListener<PushEndpoint>() { public void handleResult(PushEndpoint pushEndpoint) { // So we made a lookup for AltLocs, the found AltLoc was // firewalled and we made a lookup for its PushProxies. // In any case the creator of both values should be the // same Node! InetAddress creatorAddress = ((InetSocketAddress)altLocEntity.getCreator().getContactAddress()).getAddress(); InetAddress externalAddress = pushEndpoint.getInetAddress(); // external address can be null if not retrieved from DHT if (externalAddress != null && !externalAddress.equals(creatorAddress)) { if (LOG.isWarnEnabled()) { LOG.warn("Creator of " + altLocEntity + " and found " + pushEndpoint + " do not match!"); } listener.searchFailed(); } else { AlternateLocation alternateLocation = alternateLocationFactory.createPushAltLoc(pushEndpoint, urn); altLocManager.add(alternateLocation, this); listener.handleResult(alternateLocation); } } public void searchFailed() { listener.searchFailed(); } }; pushEndpointManager.findPushEndpoint(guid, pushEndpointListener); } /** * The AltLocsHandler listens for the FindValueResult, constructs * AlternateLocations from the results and passes them to AltLocManager * which in turn notifies every Downloader about the new locations. */ private class AltLocsHandler extends AbstractResultHandler { private final SearchListener<AlternateLocation> listener; private final URN urn; private AltLocsHandler(DHTManager dhtManager, URN urn, KUID key, SearchListener<AlternateLocation> listener) { super(dhtManager, key, listener, AbstractAltLocValue.ALT_LOC); this.urn = urn; this.listener = listener; } @Override protected Result handleDHTValueEntity(DHTValueEntity entity) { DHTValue value = entity.getValue(); if (!(value instanceof AltLocValue)) { return Result.NOT_FOUND; } AltLocValue altLoc = (AltLocValue)value; // If the AltLoc is firewalled then do a lookup for // its PushProxies if (altLoc.isFirewalled()) { if (DHTSettings.ENABLE_PUSH_PROXY_QUERIES.getValue()) { GUID guid = new GUID(altLoc.getGUID()); findPushAltLocs(guid, urn, entity, listener); return Result.NOT_YET_FOUND; } // If it's not then create just an AlternateLocation // from the info } else { Contact creator = entity.getCreator(); InetAddress addr = ((InetSocketAddress) creator.getContactAddress()).getAddress(); IpPort ipp = new IpPortImpl(addr, altLoc.getPort()); Connectable c = new ConnectableImpl(ipp, altLoc.supportsTLS()); long fileSize = altLoc.getFileSize(); byte[] ttroot = altLoc.getRootHash(); try { AlternateLocation location = alternateLocationFactory .createDirectDHTAltLoc(c, urn, fileSize, ttroot); altLocManager.add(location, this); listener.handleResult(location); return Result.FOUND; } catch (IOException e) { // Thrown if IpPort is an invalid address LOG.error("IOException", e); } } return Result.NOT_FOUND; } } }