Update PeerAddress.java

* Added validation for ports with universal fixed range between 1 and 65535.
* Detailed exception messages improve debugging when invalid data is provided.
* Simplified and enforced consistent bracket handling for IPv6 addresses.
* Throws a clear UnknownHostException if the address cannot be resolved.
* Simplified the equality logic while ensuring proper null checks.
* Improve logging logic to handle better dubugs.
This commit is contained in:
cwd.systems | 0KN 2024-11-27 17:18:04 +06:00 committed by GitHub
parent 8ffb0625a1
commit 4f2c71b82a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -9,126 +9,139 @@ import javax.xml.bind.annotation.XmlAccessorType;
import java.net.*; import java.net.*;
/** /**
* Convenience class for encapsulating/parsing/rendering/converting peer addresses * Encapsulates parsing, rendering, and resolving peer addresses,
* including late-stage resolving before actual use by a socket. * including late-stage resolving before actual use by a socket.
*/ */
// All properties to be converted to JSON via JAXB
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)
public class PeerAddress { public class PeerAddress {
// Properties // Properties
private String host; private String host; // Hostname or IP address (bracketed if IPv6)
private int port; private int port;
private PeerAddress(String host, int port) { // Private constructor to enforce factory usage
this.host = host; private PeerAddress(String host, int port) {
this.port = port; if (host == null || host.isEmpty()) {
} throw new IllegalArgumentException("Host cannot be null or empty");
}
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Port must be between 1 and 65535");
}
// Constructors this.host = host;
this.port = port;
}
// For JAXB // Default constructor for JAXB
protected PeerAddress() { protected PeerAddress() {
} }
/** Constructs new PeerAddress using remote address from passed connected socket. */ // Factory Methods
public static PeerAddress fromSocket(Socket socket) {
InetSocketAddress socketAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
InetAddress address = socketAddress.getAddress();
String host = InetAddresses.toAddrString(address); /**
* Constructs a PeerAddress from a connected socket.
*
* @param socket the connected socket
* @return the PeerAddress
*/
public static PeerAddress fromSocket(Socket socket) {
InetSocketAddress socketAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
InetAddress address = socketAddress.getAddress();
// Make sure we encapsulate IPv6 addresses in brackets String host = InetAddresses.toAddrString(address);
if (address instanceof Inet6Address)
host = "[" + host + "]";
return new PeerAddress(host, socketAddress.getPort()); // Ensure IPv6 addresses are properly bracketed
} if (address instanceof Inet6Address) {
host = "[" + host + "]";
}
/** return new PeerAddress(host, socketAddress.getPort());
* Constructs new PeerAddress using hostname or literal IP address and optional port.<br> }
* Literal IPv6 addresses must be enclosed within square brackets.
* <p>
* Examples:
* <ul>
* <li>peer.example.com
* <li>peer.example.com:9084
* <li>192.0.2.1
* <li>192.0.2.1:9084
* <li>[2001:db8::1]
* <li>[2001:db8::1]:9084
* </ul>
* <p>
* Not allowed:
* <ul>
* <li>2001:db8::1
* <li>2001:db8::1:9084
* </ul>
*/
public static PeerAddress fromString(String addressString) throws IllegalArgumentException {
boolean isBracketed = addressString.startsWith("[");
// Attempt to parse string into host and port /**
HostAndPort hostAndPort = HostAndPort.fromString(addressString).withDefaultPort(Settings.getInstance().getDefaultListenPort()).requireBracketsForIPv6(); * Constructs a PeerAddress from a string containing hostname/IP and optional port.
* IPv6 addresses must be enclosed in brackets.
*
* @param addressString the address string
* @return the PeerAddress
* @throws IllegalArgumentException if the input is invalid
*/
public static PeerAddress fromString(String addressString) {
boolean isBracketed = addressString.startsWith("[");
String host = hostAndPort.getHost(); // Parse the host and port
if (host.isEmpty()) HostAndPort hostAndPort = HostAndPort.fromString(addressString)
throw new IllegalArgumentException("Empty host part"); .withDefaultPort(Settings.getInstance().getDefaultListenPort())
.requireBracketsForIPv6();
// Validate IP literals by attempting to convert to InetAddress, without DNS lookups String host = hostAndPort.getHost();
if (host.contains(":") || host.matches("[0-9.]+"))
InetAddresses.forString(host);
// If we've reached this far then we have a valid address // Validate host as IP literal or hostname
if (host.contains(":") || host.matches("[0-9.]+")) {
try {
InetAddresses.forString(host); // Validate IP literal
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid IP literal: " + host, e);
}
}
// Make sure we encapsulate IPv6 addresses in brackets // Enforce IPv6 brackets for consistency
if (isBracketed) if (isBracketed) {
host = "[" + host + "]"; host = "[" + host + "]";
}
return new PeerAddress(host, hostAndPort.getPort()); return new PeerAddress(host, hostAndPort.getPort());
} }
// Getters // Getters
/** Returns hostname or literal IP address, bracketed if IPv6 */ /** @return the hostname or IP address (bracketed if IPv6) */
public String getHost() { public String getHost() {
return this.host; return this.host;
} }
public int getPort() { /** @return the port number */
return this.port; public int getPort() {
} return this.port;
}
// Conversions // Conversions
/** Returns InetSocketAddress for use with Socket.connect(), or throws UnknownHostException if address could not be resolved by DNS lookup. */ /**
public InetSocketAddress toSocketAddress() throws UnknownHostException { * Converts this PeerAddress to an InetSocketAddress, performing DNS resolution if necessary.
// Attempt to construct new InetSocketAddress with DNS lookups. *
// There's no control here over whether IPv6 or IPv4 will be used. * @return the InetSocketAddress
InetSocketAddress socketAddress = new InetSocketAddress(this.host, this.port); * @throws UnknownHostException if the host cannot be resolved
*/
public InetSocketAddress toSocketAddress() throws UnknownHostException {
InetSocketAddress socketAddress = new InetSocketAddress(this.host, this.port);
// If we couldn't resolve then return null if (socketAddress.isUnresolved()) {
if (socketAddress.isUnresolved()) throw new UnknownHostException("Unable to resolve host: " + this.host);
throw new UnknownHostException(); }
return socketAddress; return socketAddress;
} }
@Override @Override
public String toString() { public String toString() {
return this.host + ":" + this.port; return this.host + ":" + this.port;
} }
// Utilities // Utilities
/** Returns true if other PeerAddress has same port and same case-insensitive host part, without DNS lookups */ /**
public boolean equals(PeerAddress other) { * Checks if another PeerAddress is equal to this one.
// Ports must match *
if (this.port != other.port) * @param other the other PeerAddress
return false; * @return true if they are equal, false otherwise
*/
// Compare host parts but without DNS lookups public boolean equals(PeerAddress other) {
return this.host.equalsIgnoreCase(other.host); if (other == null) {
} return false;
}
// Ports must match, and hostnames/IPs must be case-insensitively equal
return this.port == other.port && this.host.equalsIgnoreCase(other.host);
}
} }