package com.limegroup.gnutella;
import java.io.IOException;
import java.net.InetAddress;
import com.limegroup.gnutella.filters.SpamFilter;
import com.limegroup.gnutella.util.DataUtils;
import com.limegroup.gnutella.messages.Message;
import com.limegroup.gnutella.messages.PingReply;
import com.limegroup.gnutella.messages.PushRequest;
import com.limegroup.gnutella.messages.QueryReply;
import com.limegroup.gnutella.messages.vendor.SimppVM;
import com.limegroup.gnutella.messages.vendor.StatisticVendorMessage;
import com.limegroup.gnutella.messages.vendor.UDPCrawlerPong;
import com.limegroup.gnutella.settings.ApplicationSettings;
import com.limegroup.gnutella.statistics.SentMessageStatHandler;
import com.limegroup.gnutella.util.NetworkUtils;
/**
* This class is an implementation of <tt>ReplyHandler</tt> that is
* specialized for handling UDP messages.
*/
public final class UDPReplyHandler implements ReplyHandler {
/**
* Constant for the <tt>InetAddress</tt> of the host to reply to.
*/
private final InetAddress IP;
/**
* Constant for the port of the host to reply to.
*/
private final int PORT;
/**
* Constant for the <tt>UDPService</tt>.
*/
private static final UDPService UDP_SERVICE = UDPService.instance();
/**
* Used to filter messages that are considered spam.
* With the introduction of OOB replies, it is important
* to check UDP replies for spam too.
*
* Uses one static instance instead of creating a new
* filter for every single UDP message.
*/
private static volatile SpamFilter _personalFilter =
SpamFilter.newPersonalFilter();
/**
* Constructor that sets the ip and port to reply to.
*
* @param ip the <tt>InetAddress</tt> to reply to
* @param port the port to reply to
*/
public UDPReplyHandler(InetAddress ip, int port) {
if(!NetworkUtils.isValidPort(port))
throw new IllegalArgumentException("invalid port: " + port);
if(!NetworkUtils.isValidAddress(ip))
throw new IllegalArgumentException("invalid ip: " + ip);
IP = ip;
PORT = port;
}
/**
* Sets the new personal spam filter to be used for all UDPReplyHandlers.
*/
public static void setPersonalFilter(SpamFilter filter) {
_personalFilter = filter;
}
/**
* Sends the <tt>PingReply</tt> via a UDP datagram to the IP and port
* for this handler.<p>
*
* Implements <tt>ReplyHandler</tt>.
*
* @param hit the <tt>PingReply</tt> to send
* @param handler the <tt>ReplyHandler</tt> to use for sending the reply
*/
public void handlePingReply(PingReply pong, ReplyHandler handler) {
UDP_SERVICE.send(pong, IP, PORT);
SentMessageStatHandler.UDP_PING_REPLIES.addMessage(pong);
}
/**
* Sends the <tt>QueryReply</tt> via a UDP datagram to the IP and port
* for this handler.<p>
*
* Implements <tt>ReplyHandler</tt>.
*
* @param hit the <tt>QueryReply</tt> to send
* @param handler the <tt>ReplyHandler</tt> to use for sending the reply
*/
public void handleQueryReply(QueryReply hit, ReplyHandler handler) {
UDP_SERVICE.send(hit, IP, PORT);
SentMessageStatHandler.UDP_QUERY_REPLIES.addMessage(hit);
}
/**
* Sends the <tt>QueryRequest</tt> via a UDP datagram to the IP and port
* for this handler.<p>
*
* Implements <tt>ReplyHandler</tt>.
*
* @param request the <tt>QueryRequest</tt> to send
* @param handler the <tt>ReplyHandler</tt> to use for sending the reply
*/
public void handlePushRequest(PushRequest request, ReplyHandler handler) {
UDP_SERVICE.send(request, IP, PORT);
SentMessageStatHandler.UDP_PUSH_REQUESTS.addMessage(request);
}
public void countDroppedMessage() {}
public boolean isPersonalSpam(Message m) {
return !_personalFilter.allow(m);
}
public boolean isOpen() {
return true;
}
public int getNumMessagesReceived() {
return 0;
}
public boolean isOutgoing() {
return false;
}
// inherit doc comment
public boolean isKillable() {
return false;
}
/**
* Implements <tt>ReplyHandler</tt>. This always returns <tt>false</tt>
* for UDP reply handlers, as leaves are always connected via TCP.
*
* @return <tt>false</tt>, as all leaves are connected via TCP, so
* directly connected leaves will not have <tt>UDPReplyHandler</tt>s
*/
public boolean isSupernodeClientConnection() {
return false;
}
/**
* Implements <tt>ReplyHandler</tt> interface. Always returns
* <tt>false</tt> because leaves are connected via TCP, not UDP.
*
* @return <tt>false</tt>, since leaves never maintain their connections
* via UDP, only TCP
*/
public boolean isLeafConnection() {
return false;
}
/**
* Returns whether or not this connection is a high-degree connection,
* meaning that it maintains a high number of intra-Ultrapeer connections.
* In the case of UDP reply handlers, this always returns <tt>false<tt>.
*
* @return <tt>false</tt> because, by definition, a UDP 'connection' is not
* a connection at all
*/
public boolean isHighDegreeConnection() {
return false;
}
/**
* Returns <tt>false</tt> since UDP reply handlers are not TCP
* connections in the first place.
*
* @return <tt>false</tt>, since UDP handlers are not connections in
* the first place, and therefore cannot use Ultrapeer query routing
*/
public boolean isUltrapeerQueryRoutingConnection() {
return false;
}
/**
* Returns <tt>false</tt>, as this node is not a "connection"
* in the first place, and so could never have sent the requisite
* headers.
*
* @return <tt>false</tt>, as this node is not a real connection
*/
public boolean isGoodUltrapeer() {
return false;
}
/**
* Returns <tt>false</tt>, as this node is not a "connection"
* in the first place, and so could never have sent the requisite
* headers.
*
* @return <tt>false</tt>, as this node is not a real connection
*/
public boolean isGoodLeaf() {
return false;
}
/**
* Returns <tt>false</tt>, since we don't know whether a host
* communicating via UDP supports pong caching or not.
*
* @return <tt>false</tt> since we don't know if this node supports
* pong caching or not
*/
public boolean supportsPongCaching() {
return false;
}
/**
* Returns whether or not to allow new pings from this <tt>ReplyHandler</tt>.
* Since this ping is over UDP, we'll always allow it.
*
* @return <tt>true</tt> since this ping is received over UDP
*/
public boolean allowNewPings() {
return true;
}
/**
* sends a Vendor Message to the host/port in this reply handler by UDP
* datagram.
*/
public void handleStatisticVM(StatisticVendorMessage m) throws IOException {
UDPService.instance().send(m, IP, PORT);
}
/**
* As of now there is no need to send SimppMessages via UDP,
*/
public void handleSimppVM(SimppVM simppVM) {
//This should never happen. But if it does, ignore it and move on
return;
}
// inherit doc comment
public InetAddress getInetAddress() {
return IP;
}
/**
* Retrieves the host address.
*/
public String getAddress() {
return IP.getHostAddress();
}
/**
* Returns <tt>false</tt> to indicate that <tt>UDPReplyHandler</tt>s
* should never be considered stable, due to data loss over UDP and lack
* of knowledge as to whether the host is still alive.
*
* @return <tt>false</tt> since UDP handler are never stable
*/
public boolean isStable() {
return false;
}
/**
* implementation of interface. this is not used.
*/
public String getLocalePref() {
return ApplicationSettings.DEFAULT_LOCALE.getValue();
}
/**
* Overrides toString to print out more detailed information about
* this <tt>UDPReplyHandler</tt>
*/
public String toString() {
return ("UDPReplyHandler:\r\n"+
IP.toString()+"\r\n"+
PORT+"\r\n");
}
/**
* sends the response through udp back to the requesting party
*/
public void handleUDPCrawlerPong(UDPCrawlerPong m) {
UDPService.instance().send(m, IP, PORT);
}
public void reply(Message m) {
UDPService.instance().send(m, IP,PORT);
}
public int getPort() {
return PORT;
}
public byte[] getClientGUID() {
return DataUtils.EMPTY_GUID;
}
}