package com.limegroup.gnutella.messages.vendor; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.UnknownHostException; import com.limegroup.gnutella.ByteOrder; import com.limegroup.gnutella.ErrorService; import com.limegroup.gnutella.GUID; import com.limegroup.gnutella.messages.BadPacketException; import com.limegroup.gnutella.util.NetworkUtils; /** In Vendor Message parlance, the "message type" of this VMP is "LIME/8". * Used to ask a host that you are connected to to try and connect back to a * 3rd party via UDP. */ public final class UDPConnectBackRedirect extends VendorMessage { public static final int VERSION = 1; /** The payload has a 16-bit unsigned value - the port - at which one should * connect back. */ private final int _port; /** The payload has a 32-bit value - the host address - at which one should * connect back. */ private final InetAddress _addr; /** * Constructs a new UDPConnectBackRedirect with data from the network. */ UDPConnectBackRedirect(byte[] guid, byte ttl, byte hops, int version, byte[] payload) throws BadPacketException { super(guid, ttl, hops, F_LIME_VENDOR_ID, F_UDP_CONNECT_BACK_REDIR, version, payload); if ((getVersion() == 1) && (getPayload().length != 6)) throw new BadPacketException("UNSUPPORTED PAYLOAD LENGTH: " + payload.length); // get the ip from the payload byte[] ip = new byte[4]; System.arraycopy(getPayload(), 0, ip, 0, ip.length); if (!NetworkUtils.isValidAddress(ip)) throw new BadPacketException("Bad Host!!"); try { _addr = InetAddress.getByName(NetworkUtils.ip2string(ip)); } catch (UnknownHostException uhe) { throw new BadPacketException("Bad InetAddress!!"); } // get the port from the payload.... _port = ByteOrder.ushort2int(ByteOrder.leb2short(getPayload(), ip.length)); if (!NetworkUtils.isValidPort(_port)) throw new BadPacketException("invalid port"); } /** * Constructs a new UDPConnectBackRedirect to be sent out. * @param port The port you want people to connect back to. If you give a * bad port I don't check so check yourself! */ public UDPConnectBackRedirect(GUID guid, InetAddress addr, int port) { super(F_LIME_VENDOR_ID, F_UDP_CONNECT_BACK_REDIR, VERSION, derivePayload(addr, port)); setGUID(guid); _addr = addr; _port = port; } /** You need this to connect back with a Pong with this guid. */ public GUID getConnectBackGUID() { return new GUID(getGUID()); } public InetAddress getConnectBackAddress() { return _addr; } public int getConnectBackPort() { return _port; } private static byte[] derivePayload(InetAddress addr, int port) { try { // i do it during construction.... ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] ip = addr.getAddress(); if(!NetworkUtils.isValidAddress(ip)) throw new IllegalArgumentException("invalid addr: " + addr); baos.write(ip); // write _addr ByteOrder.short2leb((short)port,baos); // write _port return baos.toByteArray(); } catch (IOException ioe) { ErrorService.error(ioe); // impossible. return null; } } /** Overridden purely for stats handling. */ protected void writePayload(OutputStream out) throws IOException { super.writePayload(out); } /** Overridden purely for stats handling. */ public void recordDrop() { super.recordDrop(); } }