/** * Make a new PushRequest object to represent a push packet we read from the network. This is the * packet parser. * * @param guid The GUID that uniquely identifies this Gnutella message * @param ttl The message TTL, the number of times it will travel across the Internet * @param hops The number of times this Gnutella message has traveled across the Internet * @param payload The 26 byte payload * @param network The Internet protocol that brought us this message, like 1 N_TCP or 2 N_UDP * @return A new PushRequest object with that information */ public PushRequest(byte[] guid, byte ttl, byte hops, byte[] payload, int network) throws BadPacketException { // Call the Message constructor to save information from the packet header in the Message // members this PushRequest inherits super(guid, Message.F_PUSH, ttl, hops, payload.length, network); // Make sure the payload is at least 26 bytes if (payload.length < STANDARD_PAYLOAD_SIZE) { ReceivedErrorStat.PUSH_INVALID_PAYLOAD.incrementStat(); throw new BadPacketException("Payload too small: " + payload.length); } // Save the payload data this.payload = payload; // Make sure the port number isn't 0 if (!NetworkUtils.isValidPort(getPort())) { ReceivedErrorStat.PUSH_INVALID_PORT.incrementStat(); throw new BadPacketException("invalid port"); } // Make sure the IP address doesn't start 0 or 255 String ip = NetworkUtils.ip2string(payload, 20); if (!NetworkUtils.isValidAddress(ip)) { ReceivedErrorStat.PUSH_INVALID_ADDRESS.incrementStat(); throw new BadPacketException("invalid address: " + ip); } }
/** * @return whether this RFD supports firewall-to-firewall transfer. For this to be true we need to * have some push proxies, indication that the host supports FWT and we need to know that * hosts' external address. */ public final boolean supportsFWTransfer() { if (_host.equals(BOGUS_IP) || !NetworkUtils.isValidAddress(_host) || NetworkUtils.isPrivateAddress(_host)) return false; return _pushAddr == null ? false : _pushAddr.supportsFWTVersion() > 0; }
/** * Creates a new Endpoint instance * * @param hostBytes IP address of the host (MSB first) * @param port The port number for the host */ public Endpoint(byte[] hostBytes, int port) { if (!NetworkUtils.isValidPort(port)) throw new IllegalArgumentException("invalid port: " + port); if (!NetworkUtils.isValidAddress(hostBytes)) throw new IllegalArgumentException("invalid address"); this.port = port; this.hostname = NetworkUtils.ip2string(hostBytes); }
/** * Constructs a new endpoint using the specific hostname & port. If strict is true, this does a * DNS lookup against the name, failing if the lookup couldn't complete. */ public Endpoint(String hostname, int port, boolean strict) { if (!NetworkUtils.isValidPort(port)) throw new IllegalArgumentException("invalid port: " + port); if (strict && !NetworkUtils.isValidAddress(hostname)) throw new IllegalArgumentException("invalid address: " + hostname); this.hostname = hostname; this.port = port; }
public void writeMessageHeaders(OutputStream ostream) throws IOException { LOG.debug("writing headers"); byte[] clientGUID = GUID.fromHexString(UPLOADER.getFileName()); InetAddress hostAddress = UPLOADER.getNodeAddress(); int hostPort = UPLOADER.getNodePort(); if (clientGUID.length != 16 || hostAddress == null || !NetworkUtils.isValidPort(hostPort) || !NetworkUtils.isValidAddress(hostAddress)) { // send back a 400 String str = "HTTP/1.1 400 Push Proxy: Bad Request\r\n\r\n"; ostream.write(str.getBytes()); ostream.flush(); debug("PPUS.doUpload(): unknown host."); UploadStat.PUSH_PROXY_REQ_BAD.incrementStat(); return; } Map params = UPLOADER.getParameters(); int fileIndex = 0; // default to 0. Object index = params.get(P_FILE); // set the file index if we know it... if (index != null) fileIndex = ((Integer) index).intValue(); PushRequest push = new PushRequest( GUID.makeGuid(), (byte) 0, clientGUID, fileIndex, hostAddress.getAddress(), hostPort); try { RouterService.getMessageRouter().sendPushRequest(push); } catch (IOException ioe) { // send back a 410 String str = "HTTP/1.1 410 Push Proxy: Servent not connected\r\n\r\n"; ostream.write(str.getBytes()); ostream.flush(); debug("PPUS.doUpload(): push failed."); debug(ioe); UploadStat.PUSH_PROXY_REQ_FAILED.incrementStat(); return; } UploadStat.PUSH_PROXY_REQ_SUCCESS.incrementStat(); String str; str = "HTTP/1.1 202 Push Proxy: Message Sent\r\n"; ostream.write(str.getBytes()); str = "Server: " + CommonUtils.getHttpServer() + "\r\n"; ostream.write(str.getBytes()); str = "Content-Type: " + Constants.QUERYREPLY_MIME_TYPE + "\r\n"; ostream.write(str.getBytes()); str = "Content-Length: " + BAOS.size() + "\r\n"; ostream.write(str.getBytes()); str = "\r\n"; ostream.write(str.getBytes()); }
/** * @return true if I am not a multicast host and have a hash. also, if I am firewalled I must have * at least one push proxy, otherwise my port and address need to be valid. */ public final boolean isAltLocCapable() { boolean ret = getSHA1Urn() != null && !_replyToMulticast; if (_firewalled) ret = ret && _pushAddr != null && _pushAddr.getProxies().size() > 0; else ret = ret && NetworkUtils.isValidPort(_port) && !NetworkUtils.isPrivateAddress(_host) && NetworkUtils.isValidAddress(_host); return ret; }
/** * 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 }
/** * Constructs a new endpoint. If requireNumeric is true, or strict is false, no DNS lookups are * ever involved. If requireNumeric is false or strict is true, a DNS lookup MAY be performed if * the hostname is not numeric. * * <p>To never block, make sure strict is false. */ public Endpoint(String hostAndPort, boolean requireNumeric, boolean strict) { final int DEFAULT = 6346; int j = hostAndPort.indexOf(":"); if (j < 0) { this.hostname = hostAndPort; this.port = DEFAULT; } else if (j == 0) { throw new IllegalArgumentException(); } else if (j == (hostAndPort.length() - 1)) { this.hostname = hostAndPort.substring(0, j); this.port = DEFAULT; } else { this.hostname = hostAndPort.substring(0, j); try { this.port = Integer.parseInt(hostAndPort.substring(j + 1)); } catch (NumberFormatException e) { throw new IllegalArgumentException(); } if (!NetworkUtils.isValidPort(getPort())) throw new IllegalArgumentException("invalid port"); } if (requireNumeric) { // TODO3: implement with fewer allocations String[] numbers = StringUtils.split(hostname, '.'); if (numbers.length != 4) throw new IllegalArgumentException(); for (int i = 0; i < numbers.length; i++) { try { int x = Integer.parseInt(numbers[i]); if (x < 0 || x > 255) throw new IllegalArgumentException(); } catch (NumberFormatException fail) { throw new IllegalArgumentException(); } } } if (strict && !NetworkUtils.isValidAddress(hostname)) throw new IllegalArgumentException("invalid address: " + hostname); }
private static Set parseLocations(byte[] locBytes) { Set locations = null; IPFilter ipFilter = IPFilter.instance(); if (locBytes.length % 6 == 0) { for (int j = 0; j < locBytes.length; j += 6) { int port = ByteOrder.ushort2int(ByteOrder.leb2short(locBytes, j + 4)); if (!NetworkUtils.isValidPort(port)) continue; byte[] ip = new byte[4]; ip[0] = locBytes[j]; ip[1] = locBytes[j + 1]; ip[2] = locBytes[j + 2]; ip[3] = locBytes[j + 3]; if (!NetworkUtils.isValidAddress(ip) || !ipFilter.allow(ip) || NetworkUtils.isMe(ip, port)) continue; if (locations == null) locations = new HashSet(); locations.add(new Endpoint(ip, port)); } } return locations; }
/** * Parses a new ExtendedEndpoint. Strictly validates all data. For example, addresses MUST be in * dotted quad format. * * @param line a single line read from the stream * @return the endpoint constructed from the line * @exception IOException problem reading from in, e.g., EOF reached prematurely * @exception ParseException data not in proper format. Does NOT necessarily set the offset of the * exception properly. * @see write */ public static ExtendedEndpoint read(String line) throws ParseException { // Break the line into fields. Skip if badly formatted. Note that // subsequent delimiters are NOT coalesced. String[] linea = StringUtils.splitNoCoalesce(line, FIELD_SEPARATOR); if (linea.length == 0) throw new ParseException("Empty line", 0); // 1. Host and port. As a dirty trick, we use existing code in Endpoint. // Note that we strictly validate the address to work around corrupted // gnutella.net files from an earlier version boolean pureNumeric; String host; int port; try { Endpoint tmp = new Endpoint(linea[0], true); // require numeric. host = tmp.getAddress(); port = tmp.getPort(); pureNumeric = true; } catch (IllegalArgumentException e) { // Alright, pure numeric failed -- let's try constructing without // numeric & without requiring a DNS lookup. try { Endpoint tmp = new Endpoint(linea[0], false, false); host = tmp.getAddress(); port = tmp.getPort(); pureNumeric = false; } catch (IllegalArgumentException e2) { ParseException e3 = new ParseException("Couldn't extract address and port from: " + linea[0], 0); e3.initCause(e2); throw e3; } } // Build endpoint without any optional data. (We'll set it if possible // later.) ExtendedEndpoint ret = new ExtendedEndpoint(host, port, false); // 2. Average uptime (optional) if (linea.length >= 2) { try { ret.dailyUptime = Integer.parseInt(linea[1].trim()); } catch (NumberFormatException e) { } } // 3. Time of pong (optional). Do NOT use current system tome // if not set. ret.timeRecorded = DEFAULT_TIME_RECORDED; if (linea.length >= 3) { try { ret.timeRecorded = Long.parseLong(linea[2].trim()); } catch (NumberFormatException e) { } } // 4. Time of successful connects (optional) if (linea.length >= 4) { try { String times[] = StringUtils.split(linea[3], LIST_SEPARATOR); for (int i = times.length - 1; i >= 0; i--) ret.recordConnectionAttempt(ret.connectSuccesses, Long.parseLong(times[i].trim())); } catch (NumberFormatException e) { } } // 5. Time of failed connects (optional) if (linea.length >= 5) { try { String times[] = StringUtils.split(linea[4], LIST_SEPARATOR); for (int i = times.length - 1; i >= 0; i--) ret.recordConnectionAttempt(ret.connectFailures, Long.parseLong(times[i].trim())); } catch (NumberFormatException e) { } } // 6. locale of the connection (optional) if (linea.length >= 6) { ret.setClientLocale(linea[5]); } // 7. udp-host if (linea.length >= 7) { try { int i = Integer.parseInt(linea[6]); if (i >= 0) ret.udpHostCacheFailures = i; } catch (NumberFormatException nfe) { } } // validate address if numeric. if (pureNumeric && !NetworkUtils.isValidAddress(host)) throw new ParseException("invalid dotted addr: " + ret, 0); // validate that non UHC addresses were numeric. if (!ret.isUDPHostCache() && !pureNumeric) throw new ParseException("illegal non-UHC endpoint: " + ret, 0); return ret; }