package com.limegroup.gnutella.messagehandlers;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.concurrent.Executor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.limewire.io.IP;
import org.limewire.io.NetworkInstanceUtils;
import org.limewire.security.SecureMessage;
import org.limewire.security.SecureMessageCallback;
import org.limewire.security.SecureMessageVerifier;
import org.limewire.setting.LongSetting;
import org.limewire.setting.StringArraySetting;
import com.limegroup.gnutella.NetworkManager;
import com.limegroup.gnutella.ReplyHandler;
import com.limegroup.gnutella.UDPReplyHandlerFactory;
import com.limegroup.gnutella.filters.IPList;
import com.limegroup.gnutella.messages.Message;
import com.limegroup.gnutella.messages.vendor.RoutableGGEPMessage;
import com.limegroup.gnutella.simpp.SimppListener;
import com.limegroup.gnutella.simpp.SimppManager;
/**
* A message handler that responds to messages only to hosts
* contained in a simppable whitelist.
*/
abstract class RestrictedResponder implements SimppListener, MessageHandler {
private static final Log LOG = LogFactory.getLog(RestrictedResponder.class);
/** list of hosts that we can send responses to */
private volatile IPList allowed;
/** setting to check for updates to the host list */
private final StringArraySetting setting;
/** an optional verifier to very secure messages */
private final SecureMessageVerifier verifier;
/** The last version of the routable message that was routed */
private final LongSetting lastRoutedVersion;
private final NetworkManager networkManager;
private final UDPReplyHandlerFactory udpReplyHandlerFactory;
private final Executor messageExecutorService;
private final NetworkInstanceUtils networkInstanceUtils;
public RestrictedResponder(StringArraySetting setting,
NetworkManager networkManager,
SimppManager simppManager,
UDPReplyHandlerFactory udpReplyHandlerFactory,
Executor messageExecutor,
NetworkInstanceUtils networkInstanceUtils) {
this(setting, null, null, networkManager, simppManager,
udpReplyHandlerFactory, messageExecutor, networkInstanceUtils);
}
/**
* @param setting the setting containing the list of allowed
* hosts to respond to.
* @param verifier the <tt>SignatureVerifier</tt> to use. Null if we
* want to process all messages.
*/
// TODO cleanup: SimmpManager registration should be done in extra initialize method
// and also cleaned up
public RestrictedResponder(StringArraySetting setting,
SecureMessageVerifier verifier,
LongSetting lastRoutedVersion, NetworkManager networkManager,
SimppManager simppManager,
UDPReplyHandlerFactory udpReplyHandlerFactory,
Executor messageExecutorService,
NetworkInstanceUtils networkInstanceUtils) {
this.setting = setting;
this.verifier = verifier;
this.lastRoutedVersion = lastRoutedVersion;
this.networkManager = networkManager;
this.udpReplyHandlerFactory = udpReplyHandlerFactory;
this.messageExecutorService = messageExecutorService;
this.networkInstanceUtils = networkInstanceUtils;
allowed = new IPList();
allowed.add("*.*.*.*");
simppManager.addListener(this);
updateAllowed();
}
private void updateAllowed() {
IPList newCrawlers = new IPList();
try {
for (String ip : setting.get())
newCrawlers.add(new IP(ip));
if (newCrawlers.isValidFilter(false, networkInstanceUtils))
allowed = newCrawlers;
} catch (IllegalArgumentException badSimpp) {}
}
public void simppUpdated(int newVersion) {
updateAllowed();
}
public final void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) {
if (msg instanceof RoutableGGEPMessage) {
// if we have a verifier, verify
if (verifier != null && msg instanceof SecureMessage)
verifier.verify((SecureMessage)msg, new SecureCallback(addr, handler));
else
processRoutableMessage((RoutableGGEPMessage)msg, addr, handler);
} else {
// just check the return address.
IP ip = new IP(handler.getAddress());
if (!allowed.contains(ip)) {
if (LOG.isDebugEnabled()) {
LOG.debug("restricted message not allowed from ip: " + ip);
}
return;
}
processAllowedMessage(msg, addr, handler);
}
}
/**
* Processes a routable message.
*
* If the message has a return address, it must have a routable version.
* If not, it must have either a routable version or a destination address.
*/
private void processRoutableMessage(RoutableGGEPMessage msg, InetSocketAddress addr, ReplyHandler handler) {
// if the message specifies a return address, use that
if (msg.getReturnAddress() != null) {
// messages with return address MUST have routable version
if (msg.getRoutableVersion() < 0)
return;
handler = udpReplyHandlerFactory.createUDPReplyHandler(
msg.getReturnAddress().getInetAddress(),
msg.getReturnAddress().getPort());
} else if (msg.getDestinationAddress() != null) {
// if there is a destination address, it must match our external address
if (!Arrays.equals(networkManager.getExternalAddress(),
msg.getDestinationAddress().getInetAddress().getAddress()))
return;
} else if (msg.getRoutableVersion() < 0) // no routable version either? drop.
return;
IP ip = new IP(handler.getAddress());
if (!allowed.contains(ip)) {
if (LOG.isDebugEnabled()) {
LOG.debug("restricted message not allowed from ip: " + ip);
}
return;
}
// check if its a newer version than the last we routed.
long routableVersion = msg.getRoutableVersion();
if (lastRoutedVersion != null && routableVersion > 0) {
synchronized(lastRoutedVersion) {
if (routableVersion <= lastRoutedVersion.getValue())
return;
lastRoutedVersion.setValue(routableVersion);
}
}
processAllowedMessage(msg, addr, handler);
}
/**
* small listener that will process a message if it verifies.
*/
private class SecureCallback implements SecureMessageCallback {
private final InetSocketAddress addr;
private final ReplyHandler handler;
SecureCallback(InetSocketAddress addr, ReplyHandler handler) {
this.addr = addr;
this.handler = handler;
}
public void handleSecureMessage(final SecureMessage sm, boolean passed) {
if (!passed) {
if (LOG.isDebugEnabled()) {
LOG.debug("Message: " + sm + "didn't verify");
}
return;
}
messageExecutorService.execute(new Runnable() {
public void run() {
processRoutableMessage((RoutableGGEPMessage)sm, addr, handler);
}
});
}
}
/**
* Process the specified message because it has been approved.
*/
protected abstract void processAllowedMessage(Message msg, InetSocketAddress addr, ReplyHandler handler);
}