package com.limegroup.gnutella.messages.vendor; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteOrder; import java.security.Signature; import java.security.SignatureException; import org.limewire.io.BadGGEPPropertyException; import org.limewire.io.GGEP; import org.limewire.io.InvalidDataException; import org.limewire.io.IpPort; import org.limewire.io.NetworkUtils; import org.limewire.security.SecureMessage; import org.limewire.service.ErrorService; import com.limegroup.gnutella.messages.BadPacketException; import com.limegroup.gnutella.messages.GGEPKeys; import com.limegroup.gnutella.messages.GGEPParser; import com.limegroup.gnutella.messages.SecureGGEPData; /** * A ggep-based message that may have a specific return address. It requires * a routing version number and must be secure. */ public class RoutableGGEPMessage extends AbstractVendorMessage implements SecureMessage, VendorMessage.ControlMessage { static final String RETURN_ADDRESS_KEY = "RA"; static final String VERSION_KEY = "V"; static final String TO_ADDRESS_KEY = "TO"; /** Whether or not this message has been verified as secure. */ private Status _secureStatus = Status.INSECURE; /** * The ggep field that this message is. */ protected final GGEP ggep; /** * Secure ggep data. */ private final SecureGGEPData secureData; /** * The return address of this message. */ private final IpPort returnAddress; /** * The destination address of this message */ private final IpPort destAddress; /** * The routing version of this message */ private final long routableVersion; protected RoutableGGEPMessage(byte[] guid, byte ttl, byte hops, byte [] vendor, int selector, int version, byte[] payload, Network network) throws BadPacketException { super(guid, ttl, hops, vendor, selector, version, payload, network); // parse ggep GGEPParser parser = new GGEPParser(); parser.scanForGGEPs(payload, 0); GGEP ggep = parser.getSecureGGEP(); if (ggep == null) throw new BadPacketException("no secure ggep"); this.secureData = new SecureGGEPData(parser); ggep = parser.getNormalGGEP(); if (ggep == null) // no ggep at all? throw new BadPacketException("no normal ggep"); this.ggep = ggep; // get routable version long routableVersion; try { routableVersion = ggep.getLong(VERSION_KEY); } catch (BadGGEPPropertyException bad){ routableVersion = -1; } this.routableVersion = routableVersion; // get return address if any IpPort retAddr = null; try { byte [] returnAddress = ggep.get(RETURN_ADDRESS_KEY); if (returnAddress != null) retAddr = NetworkUtils.getIpPort(returnAddress, ByteOrder.LITTLE_ENDIAN); } catch (InvalidDataException bleh) {} this.returnAddress = retAddr; // get destination address if any IpPort destAddr = null; try { byte [] destAddress = ggep.get(TO_ADDRESS_KEY); if (destAddress != null) destAddr = NetworkUtils.getIpPort(destAddress, ByteOrder.LITTLE_ENDIAN); } catch (InvalidDataException bleh) {} this.destAddress = destAddr; } protected RoutableGGEPMessage(byte [] vendor, int selector, int version, GGEPSigner signer, GGEP ggep) { super(vendor, selector, version, derivePayload(signer, ggep)); this.ggep = ggep; // nodes cannot create messages with custom return address or version. this.returnAddress = null; this.destAddress = null; this.routableVersion = -1; this.secureData = null; } private static byte [] derivePayload(GGEPSigner signer, GGEP ggep) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { ggep.write(baos); signer.getSecureGGEP(ggep).write(baos); } catch (IOException impossible) { ErrorService.error(impossible); } return baos.toByteArray(); } /** * @return the address responses to this message should be sent to. * null if none was present. */ public IpPort getReturnAddress() { return returnAddress; } /** * @return the address this message was intended to go to. * null if none was present. */ public IpPort getDestinationAddress() { return destAddress; } /** * @return the routable version of this message * -1 if none was present */ public long getRoutableVersion() { return routableVersion; } public byte[] getSecureSignature() { SecureGGEPData sg = secureData; if(sg != null) { try { return sg.getGGEP().getBytes(GGEPKeys.GGEP_HEADER_SIGNATURE); } catch(BadGGEPPropertyException bgpe) { return null; } } else { return null; } } public Status getSecureStatus() { return _secureStatus; } public void setSecureStatus(Status secureStatus) { _secureStatus = secureStatus; } public void updateSignatureWithSecuredBytes(Signature signature) throws SignatureException { SecureGGEPData sg = secureData; if(sg != null) { signature.update(getPayload(), 0, sg.getStartIndex()); int end = sg.getEndIndex(); int length = getPayload().length - end; signature.update(getPayload(), end, length); } } public static interface GGEPSigner { /** * @param original the ggep to be signed * @return a secure ggep containing signature of the signed ggep. */ GGEP getSecureGGEP(GGEP original); } }