/** * Returns the ip (given in BIG-endian) format of buf[offset]...buf[offset+3] as standard * dotted-decimal, e.g., 192.168.0.1 * * <p> * * @param ip the IP address to convert * @param offset the offset into the IP array to convert * @return the IP address as a dotted-quad string */ public static final String ip2string(byte[] ip, int offset) { // xxx.xxx.xxx.xxx => 15 chars StringBuffer sbuf = new StringBuffer(16); sbuf.append(ByteOrder.ubyte2int(ip[offset])); sbuf.append('.'); sbuf.append(ByteOrder.ubyte2int(ip[offset + 1])); sbuf.append('.'); sbuf.append(ByteOrder.ubyte2int(ip[offset + 2])); sbuf.append('.'); sbuf.append(ByteOrder.ubyte2int(ip[offset + 3])); return sbuf.toString(); }
/** * Make a new PatchTableMessage object by parsing data a remote computer sent us. * * <p>Creates a new PATCH variant with data read from the network. The first byte is guaranteed to * be PATCH_VARIANT. * * <p>Throws BadPacketException the remaining values in payload are not well-formed, e.g., because * it's the wrong length, the sequence size is less than the sequence number, etc. * * @param guid The message GUID we read from the header * @param ttl The TTL we read from the message header * @param hops The hops count we read from the message header * @param payload The payload of the message after that, like fNSCBpatchdatapatchdatapatchdata */ protected PatchTableMessage(byte[] guid, byte ttl, byte hops, byte[] payload) throws BadPacketException { // Save the GUID, TTL, hops and length in the Message and RouteTableMessage cores of this new // PatchTableMessage super( guid, ttl, hops, payload.length, RouteTableMessage.PATCH_VARIANT); // 0x01 identifies this as a PatchTableMessage /* * TODO: maybe we shouldn't enforce this * if (payload.length < 5) throw new BadPacketException("Extra arguments in reset message."); */ /* * The payload of a patch message looks like this: * * fNSCBpatchdatapatchdata... */ // Make sure the first payload byte, f, is 0x01, identifying this as a patch message Assert.that(payload[0] == PATCH_VARIANT); // Read the sequence number and sequence size, N and S, a distance of 1 and 2 bytes into the // payload data this.sequenceNumber = (short) ByteOrder.ubyte2int(payload[1]); this.sequenceSize = (short) ByteOrder.ubyte2int(payload[2]); if (sequenceNumber < 1 || sequenceSize < 1 || sequenceNumber > sequenceSize) throw new BadPacketException("Bad sequence/size: " + sequenceNumber + "/" + sequenceSize); // Find out if the patch data is compressed by reading C 3 bytes into the payload data this.compressor = payload[3]; if (!(compressor == COMPRESSOR_NONE || compressor == COMPRESSOR_DEFLATE)) throw new BadPacketException("Bad compressor: " + compressor); // Find out if the patch data was halved or not by reading B 4 bytes into the payload data this.entryBits = payload[4]; if (entryBits < 0) throw new BadPacketException("Negative entryBits: " + entryBits); // Clip out the chunk of probably compressed and halved patch data this patch message carries // after that this.data = new byte [payload.length - 5]; // It's the whole paylaod not including the first 4 fNSCB bytes we read System.arraycopy(payload, 5, data, 0, data.length); // Copy it from 5 bytes in to the end }
/** * Make a new PushRequest object for us to send with the given information. This is the packet * maker. * * @param guid For the header, the message GUID that will mark this packet unique * @param ttl For the header, the TTL * @param clientGUID For the payload, the client ID GUID of the firewalled computer that has the * file and will push open the connection * @param index For the payload, the ID number the firewalled computer has assigned the file the * downloading computer wants * @param ip For the payload, the downloading computer's IP address the firewalled computer will * push open a connection to * @param port For the payload, the downloading computer's port number the firewalled computer * will push open a connection to * @param network The Internet protocol this push packet will travel on, like 1 N_TCP or 2 N_UDP, * this is not a part of the packet that's sent * @return A new PushRequest object with that information */ public PushRequest( byte[] guid, byte ttl, byte[] clientGUID, long index, byte[] ip, int port, int network) { // Call the Message constructor to save packet header information super( guid, // The message GUID Message.F_PUSH, // 0x40, this is a push message ttl, // The number of times this push will be able to travel between ultrapeers (byte) 0, // This push message hasn't traveled any hops yet STANDARD_PAYLOAD_SIZE, // The payload length will be 26 bytes network); // The Internet protocol we'll use to send, the Message object keeps this // information but it is not part of the packet that's sent // Make sure the GUID is 16 bytes, the file id fits in 4 bytes, the IP is 4 bytes, the IP // address doesn't start 0 or 255, and the port number fits in 2 bytes if (clientGUID.length != 16) throw new IllegalArgumentException("invalid guid length: " + clientGUID.length); else if ((index & 0xFFFFFFFF00000000l) != 0) throw new IllegalArgumentException("invalid index: " + index); else if (ip.length != 4) throw new IllegalArgumentException("invalid ip length: " + ip.length); else if (!NetworkUtils.isValidAddress(ip)) throw new IllegalArgumentException("invalid ip " + NetworkUtils.ip2string(ip)); else if (!NetworkUtils.isValidPort(port)) throw new IllegalArgumentException("invalid port: " + port); /* * The 26 byte pong payload has this structure: * * [client ID GUID] At 0, 16 bytes The client ID GUID of the computer that will get this packet and push open a new connection * FILE At 16, 4 bytes The number the pushing computer has given the file the wanting computer wants * IPIPPP At 20, 6 bytes The IP address and port number of the wanting computer */ // Compose the 26 byte push payload payload = new byte[STANDARD_PAYLOAD_SIZE]; // Make a byte array that can hold 26 bytes System.arraycopy(clientGUID, 0, payload, 0, 16); // Copy the client ID GUID into the first 16 ByteOrder.int2leb((int) index, payload, 16); // 16 bytes into the array, copy the 4 byte file id payload[20] = ip[0]; // 20 bytes into the array, copy the 4 bytes of the IP address payload[21] = ip[1]; payload[22] = ip[2]; payload[23] = ip[3]; ByteOrder.short2leb( (short) port, payload, 24); // 24 bytes into the array, place the 2 byte port number }
/** Packs a Collection of IpPorts into a byte array. */ public static byte[] packIpPorts(Collection ipPorts) { byte[] data = new byte[ipPorts.size() * 6]; int offset = 0; for (Iterator i = ipPorts.iterator(); i.hasNext(); ) { IpPort next = (IpPort) i.next(); byte[] addr = next.getInetAddress().getAddress(); int port = next.getPort(); System.arraycopy(addr, 0, data, offset, 4); offset += 4; ByteOrder.short2leb((short) port, data, offset); offset += 2; } return data; }
/** * Get the port number this push packet tells the firewalled computer to push open a connection * to. This is the port number of the computer that wants the file and sent this packet. * * @return The downloading computer's port number */ public int getPort() { // Read the port number from the 2 bytes after the IP address return ByteOrder.ushort2int(ByteOrder.leb2short(payload, 24)); }
/** * Get the file ID index of the file the firewalled computer is sharing, and will push out. The * firewalled computer returned a query hit with this file ID. The downloading computer writes * this ID in this push packet to tell the firewalled computer which one to push out. * * @return The shared file ID */ public long getIndex() { // Return the 4 byte file id 16 bytes into the payload return ByteOrder.uint2long(ByteOrder.leb2int(payload, 16)); }