package com.limegroup.gnutella; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteOrder; import java.util.Set; import java.util.StringTokenizer; import org.limewire.collection.BitNumbers; import org.limewire.io.Connectable; import org.limewire.io.GUID; import org.limewire.io.InvalidDataException; import org.limewire.io.IpPort; import org.limewire.io.IpPortSet; import org.limewire.io.NetworkInstanceUtils; import org.limewire.io.NetworkUtils; import org.limewire.logging.Log; import org.limewire.logging.LogFactory; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; import com.google.inject.name.Named; import com.limegroup.gnutella.filters.IPFilter; import com.limegroup.gnutella.http.HTTPConstants; import com.limegroup.gnutella.http.HTTPUtils; import com.limegroup.gnutella.messages.BadPacketException; @Singleton public class PushEndpointFactoryImpl implements PushEndpointFactory { private static final Log LOG = LogFactory.getLog(PushEndpointFactoryImpl.class); private final Provider<PushEndpointCache> pushEndpointCache; private final Provider<SelfEndpoint> selfProvider; private final NetworkInstanceUtils networkInstanceUtils; private final Provider<IPFilter> hostileFilter; @Inject public PushEndpointFactoryImpl( Provider<PushEndpointCache> pushEndpointCache, Provider<SelfEndpoint> selfProvider, NetworkInstanceUtils networkInstanceUtils, @Named("hostileFilter") Provider<IPFilter> hostileFilter) { this.pushEndpointCache = pushEndpointCache; this.selfProvider = selfProvider; this.networkInstanceUtils = networkInstanceUtils; this.hostileFilter = hostileFilter; } public PushEndpoint createPushEndpoint(byte[] guid) { return createPushEndpoint(guid, IpPort.EMPTY_SET); } public PushEndpoint createPushEndpoint(byte[] guid, Set<? extends IpPort> proxies) { return createPushEndpoint(guid, proxies, PushEndpoint.PLAIN, 0); } public PushEndpoint createPushEndpoint(byte[] guid, Set<? extends IpPort> proxies, byte features, int version) { return createPushEndpoint(guid, proxies, features, version, null); } public PushEndpoint createPushEndpoint(byte[] guid, Set<? extends IpPort> proxies, byte features, int version, IpPort addr) { return new PushEndpointImpl(guid, proxies, features, version, addr, pushEndpointCache.get(), networkInstanceUtils); } public PushEndpoint createPushEndpoint(String httpString) throws IOException { byte[] guid; if (httpString.length() < 32 || httpString.indexOf(";") > 32) throw new IOException("http string does not contain valid guid"); //the first token is the guid String guidS=httpString.substring(0,32); httpString = httpString.substring(32); try { guid = GUID.fromHexString(guidS); } catch(IllegalArgumentException iae) { throw new IOException(iae.getMessage()); } StringTokenizer tok = new StringTokenizer(httpString,";"); Set<IpPort> proxies = new IpPortSet(); int fwtVersion =0; IpPort addr = null; BitNumbers tlsProxies = null; while(tok.hasMoreTokens()) { String current = tok.nextToken().trim(); // see if this token is the fwt header // if this token fails to parse we abort since we must know // if the PE supports fwt or not. if (current.startsWith(HTTPConstants.FW_TRANSFER)) { fwtVersion = (int) HTTPUtils.parseFeatureToken(current); continue; } // don't parse it in the middle of parsing proxies. if (proxies.size() == 0 && current.startsWith(PushEndpoint.PPTLS_HTTP)) { String value = HTTPUtils.parseValue(current); if(value != null) { try { tlsProxies = new BitNumbers(value); } catch(IllegalArgumentException invalid) { throw (IOException)new IOException().initCause(invalid); } } continue; } // Only look for more proxies if we didn't reach our limit if(proxies.size() < PushEndpoint.MAX_PROXIES) { boolean tlsCapable = tlsProxies != null && tlsProxies.isSet(proxies.size()); // if its not the header, try to parse it as a push proxy try { Connectable ipp = NetworkUtils.parseIpPort(current, tlsCapable); if (isGoodPushProxy(ipp)) { proxies.add(ipp); } } catch(IOException ohWell) { tlsProxies = null; // stop adding TLS, since our index may be off } } // if its not a push proxy, try to parse it as a port:ip // only the first occurence of port:ip is parsed if (addr==null) { try { IpPort ipp = NetworkUtils.parsePortIp(current); if(!networkInstanceUtils.isPrivateAddress(ipp.getInetAddress())) addr = ipp; }catch(IOException notBad) {} } } // if address isn't there or private, reset address and fwt if (addr == null || !networkInstanceUtils.isValidExternalIpPort(addr) || addr.equals(RemoteFileDesc.BOGUS_IP)) { fwtVersion = 0; addr = null; } return createPushEndpoint(guid, proxies, (byte)(proxies.size() | fwtVersion << 3), fwtVersion, addr); } public PushEndpoint createFromBytes(DataInputStream dais) throws BadPacketException, IOException { byte [] guid =new byte[16]; Set<IpPort> proxies = new IpPortSet(); IpPort addr = null; byte header = (byte)(dais.read() & 0xFF); // get the number of push proxies byte number = (byte)(header & PushEndpoint.SIZE_MASK); byte features = (byte)(header & PushEndpoint.FEATURES_MASK); byte version = (byte)((header & PushEndpoint.FWT_VERSION_MASK) >> 3); dais.readFully(guid); if (version > 0) { byte [] host = new byte[6]; dais.readFully(host); try { addr = NetworkUtils.getIpPort(host, ByteOrder.LITTLE_ENDIAN); } catch(InvalidDataException ide) { throw new BadPacketException(ide); } if (addr.getAddress().equals(RemoteFileDesc.BOGUS_IP)) { addr = null; version = 0; } } // If the features mentioned this has pptls bytes, read that. BitNumbers bn = null; if((features & PushEndpoint.PPTLS_BINARY) != 0) { byte[] tlsIndexes = new byte[1]; dais.readFully(tlsIndexes); bn = new BitNumbers(tlsIndexes); } byte [] tmp = new byte[6]; for (int i = 0; i < number; i++) { dais.readFully(tmp); try { boolean tlsCapable = bn != null && bn.isSet(i); Connectable ipp = NetworkUtils.getConnectable(tmp, ByteOrder.LITTLE_ENDIAN, tlsCapable); if (isGoodPushProxy(ipp)) { proxies.add(ipp); } } catch(InvalidDataException ide) { throw new BadPacketException(ide); } } /** this adds the read set to the existing proxies */ PushEndpoint pe = createPushEndpoint(guid, proxies, features, version, addr); return pe; } /* (non-Javadoc) * @see com.limegroup.gnutella.PushEndpointFactory#createForSelf() */ public PushEndpoint createForSelf() { return selfProvider.get(); } boolean isGoodPushProxy(Connectable connectable) { if (networkInstanceUtils.isPrivateAddress(connectable.getInetAddress())) { LOG.debugf("push proxy not public: {0}", connectable); return false; } if (!hostileFilter.get().allow(connectable)) { LOG.debugf("push proxy hostile: {0}", connectable); return false; } return true; } }