/*
* Copyright 2009 Thomas Bocek
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package net.tomp2p.peers;
import java.io.Serializable;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import io.netty.buffer.ByteBuf;
import lombok.Builder;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.experimental.Wither;
import net.tomp2p.peers.IP.IPv4;
import net.tomp2p.peers.PeerSocketAddress.PeerSocket4Address;
import net.tomp2p.peers.PeerSocketAddress.PeerSocket6Address;
import net.tomp2p.utils.Pair;
import net.tomp2p.utils.Utils;
/**
* A PeerAddress contains the node ID and how to contact this node using both TCP and UDP. This class is thread safe (or
* it does not matter if its not). The format looks as follows:
*
* <pre>
* 20 bytes - Number160
* 2 bytes - Header
* - 1 byte options: IPv6, firewalled UDP, firewalled TCP, is relayed
* - 1 byte relays:
* - first 3 bits: number of relays (max 5.)
* - second 5 bits: if the 5 relays are IPv6 (bit set) or not (no bit set)
* 2 bytes - TCP port
* 2 bytes - UDP port
* 4 or 16 bytes - Inet Address
* 0-5 relays:
* - 2 bytes - TCP port
* - 2 bytes - UDP port
* - 4 or 16 bytes - Inet Address
* </pre>
*
* @author Thomas Bocek
*/
@Builder
@Accessors(fluent = true, chain = true)
public final class PeerAddress implements Comparable<PeerAddress>, Serializable {
private static final long serialVersionUID = 8483270473601620720L;
public static final int HEADER_SIZE = 3;
public static final int MIN_SIZE = HEADER_SIZE + Number160.BYTE_ARRAY_SIZE; //23
public static final int MIN_SIZE_HEADER = MIN_SIZE + 6; //29
public static final int MAX_SIZE = MIN_SIZE + (2 * PeerSocket4Address.SIZE) + (8 * PeerSocket6Address.SIZE); //219
//1 Byte - 8 bits
private static final int IPV4 = 0x1; // xxxxxxxx xxxxxxxx xxxxxxx1
private static final int IPV6 = 0x2; // xxxxxxxx xxxxxxxx xxxxxx1x
private static final int REACHABLE4_UDP = 0x4; // xxxxxxxx xxxxxxxx xxxxx1xx
private static final int REACHABLE4_TCP = 0x8; // xxxxxxxx xxxxxxxx xxxx1xxx
private static final int REACHABLE4_UDT = 0x10; // xxxxxxxx xxxxxxxx xxx1xxxx
private static final int REACHABLE6_UDP = 0x20; // xxxxxxxx xxxxxxxx xx1xxxxx
private static final int REACHABLE6_TCP = 0x40; // xxxxxxxx xxxxxxxx x1xxxxxx
private static final int REACHABLE6_UDT = 0x80; // xxxxxxxx xxxxxxxx 1xxxxxxx
//next 1 Byte - 8 bits
private static final int RELAY_SIZE_MASK = 0x700; // xxxxxxxx xxxxx111 xxxxxxxx
// indicates that a relay is used. If its relayed it can be slow (buffered) or holepunching
// xxxxxxxx xxx00xxx xxxxxxxx - no flags
private static final int HOLE_PUNCHING = 0x1000; // xxxxxxxx xxx10xxx xxxxxxxx
// if relayed, indicates that the peer is slow
private static final int SLOW = 0x800; // xxxxxxxx xxx01xxx xxxxxxxx
// if relayed, indicate if HP is possible
private static final int RELAY_FLAGS_MASK = 0x1800; // xxxxxxxx xxx11xxx xxxxxxxx - unused
private static final int UNREACHABLE = 0x2000; // xxxxxxxx xx1xxxxx xxxxxxxx
// indicate IPv4 with the internal IP
private static final int NET4_INTERNAL = 0x4000; // xxxxxxxx x1xxxxxx xxxxxxxx //hopefully we'll never use this for IPv6
private static final int SKIP_IPV4 = 0x8000; // xxxxxxxx 1xxxxxxx xxxxxxxx
private static final int SKIP_IPV6 = 0x10000; // xxxxxxx1 xxxxxxxx xxxxxxxx
//next 1 Byte - 8 bits - we can have at most 7 relays
private static final int RELAY_TYPE_MASK = 0xfe0000; // 1111111x xxxxxxxx xxxxxxxx
@Getter @Wither private final PeerSocket4Address ipInternalSocket;
@Getter @Wither private final int ipInternalNetworkPrefix;
//
@Getter @Wither private final PeerSocket4Address ipv4Socket;
@Getter @Wither private final PeerSocket6Address ipv6Socket;
@Getter @Wither private final boolean ipv4Flag;
@Getter @Wither private final boolean ipv6Flag;
//@Getter @Wither private final boolean net6Flag; // unused
@Getter @Wither private final boolean reachable4UDP;
@Getter @Wither private final boolean reachable4TCP;
@Getter @Wither private final boolean reachable4UDT;
@Getter @Wither private final boolean reachable6UDP;
@Getter @Wither private final boolean reachable6TCP;
@Getter @Wither private final boolean reachable6UDT;
// network information
@Getter @Wither private final Number160 peerId;
// connection information
@Getter @Wither private final int relaySize;
@Getter @Wither private final boolean slow;
@Getter @Wither private final boolean holePunching;
//@Getter @Wither private final boolean relayFlag; // unused
@Getter @Wither private final boolean unreachable;
@Getter @Wither private final boolean net4Internal;
@Getter @Wither private final boolean skipIPv4;
@Getter @Wither private final boolean skipIPv6;
@Wither private final BitSet relayTypes;
// we can make the hash final as it never changes, and this class is used
// multiple times in maps. A new peer is always added to the peermap, so
// making this final saves CPU time. The maps used are removedPeerCache,
// that is always checked, peerMap that is either added or checked if
// already present. Also peers from the neighbor list sent over the wire are
// added to the peermap.
private final int hashCode;
//relays
private final Collection<PeerSocketAddress> relays;
public static final Collection<PeerSocketAddress> EMPTY_PEER_SOCKET_ADDRESSES = Collections.emptySet();
public static final byte EMPTY_RELAY_TYPES = 0;
public static final boolean preferIPv6Addresses;
static {
preferIPv6Addresses = "true".equalsIgnoreCase(System.getProperty("java.net.preferIPv6Addresses"));
}
//Change lomboks default behavior
public static class PeerAddressBuilder {
public PeerAddressBuilder relay(PeerSocketAddress relay) {
if(relaySize == 7) {
throw new IllegalArgumentException("cannot have more than 7 relays");
}
if (this.relays == null) {
this.relays = new ArrayList<PeerSocketAddress>(1);
}
if(this.relayTypes == null) {
this.relayTypes = new BitSet(8);
}
if(relay instanceof PeerSocket4Address) {
this.relayTypes.set(this.relaySize++, false);
}
else {
this.relayTypes.set(this.relaySize++, true);
}
this.relays.add(relay);
return this;
}
private PeerAddressBuilder relay(PeerSocketAddress relay, int index) {
if(relaySize > 7) {
throw new IllegalArgumentException("cannot have more than 7 relays");
}
if (this.relays == null) {
this.relays = new ArrayList<PeerSocketAddress>(relaySize);
}
if(this.relayTypes == null) {
this.relayTypes = new BitSet(8);
}
if(relay instanceof PeerSocket4Address) {
this.relayTypes.set(index, false);
}
else {
this.relayTypes.set(index, true);
}
this.relays.add(relay);
return this;
}
public PeerAddressBuilder relay(Collection<? extends PeerSocketAddress> relays) {
if (this.relays == null) {
this.relays = new ArrayList<PeerSocketAddress>(relays.size());
}
for (PeerSocketAddress relay : relays) {
relay(relay);
}
return this;
}
public PeerAddressBuilder ipv4Socket(PeerSocket4Address ipv4Socket) {
this.ipv4Flag = true;
this.ipv4Socket = ipv4Socket;
return this;
}
public PeerAddressBuilder ipv6Socket(PeerSocket6Address ipv6Socket) {
this.ipv6Flag = true;
this.ipv6Socket = ipv6Socket;
return this;
}
public PeerAddressBuilder ipInternalSocket(PeerSocket4Address ipInternalSocket) {
this.net4Internal = true;
this.ipInternalSocket = ipInternalSocket;
return this;
}
}
public PeerAddress withRelays(Collection<PeerSocketAddress> relays) {
if(this.relays == relays) {
return this;
}
final int relaySize;
final BitSet relayTypes = new BitSet(8);
if(relays != null) {
relaySize = relays.size();
int index=0;
for(PeerSocketAddress relay: relays) {
if(relay instanceof PeerSocket4Address) {
relayTypes.set(index++, false);
}
else {
relayTypes.set(index++, true);
}
}
} else {
relaySize = 0;
}
return this.relays == relays ? this : new PeerAddress(ipInternalSocket,
ipInternalNetworkPrefix,
ipv4Socket,
ipv6Socket,
ipv4Flag,
ipv6Flag,
reachable4UDP,
reachable4TCP,
reachable4UDT,
reachable6UDP,
reachable6TCP,
reachable6UDT,
peerId,
relaySize,
slow,
holePunching,
unreachable,
net4Internal,
skipIPv4,
skipIPv6,
relayTypes,
hashCode,
relays);
}
public Collection<PeerSocketAddress> relays() {
if(relays == null) {
return EMPTY_PEER_SOCKET_ADDRESSES;
}
return relays;
}
public byte relayTypes() {
if(relayTypes == null) {
return EMPTY_RELAY_TYPES;
}
byte[] tmp = relayTypes.toByteArray();
return tmp.length != 1 ? EMPTY_RELAY_TYPES : tmp[0];
}
/**
* Creates a peer address, where the byte array has to be in the rigth format and in the right size. The new
* offset can be accessed with offset().
*
*/
public static Pair<PeerAddress, Integer> decode(final byte[] array) {
return decode(array, 0);
}
public static Pair<PeerAddress, Integer> decode(final byte[] array, int offset) {
final int header = Utils.byteArrayToMedium(array, offset);
offset+=3;
return decode(header, array, offset);
}
public static Pair<PeerAddress, Integer> decode(final int header, final byte[] array, int offset) {
final PeerAddressBuilder builder = new PeerAddressBuilder();
decodeHeader(builder, header);
//relays
for(int i = 0; i<builder.relaySize; i++) {
final Pair<? extends PeerSocketAddress, Integer> pair;
if(builder.relayTypes.get(i)) {
builder.relay((pair = PeerSocket6Address.decode(array, offset)).element0(), i);
} else {
builder.relay((pair = PeerSocket4Address.decode(array, offset)).element0(), i);
}
offset = pair.element1();
}
if(builder.ipv4Flag) {
final Pair<PeerSocket4Address, Integer> pair;
builder.ipv4Socket((pair = PeerSocket4Address.decode(array, offset, builder.skipIPv4)).element0());
offset = pair.element1();
}
if(builder.net4Internal) {
final Pair<PeerSocket4Address, Integer> pair;
builder.ipInternalSocket((pair = PeerSocket4Address.decode(array, offset)).element0());
offset = pair.element1();
}
if(builder.ipv6Flag) {
final Pair<PeerSocket6Address, Integer> pair;
builder.ipv6Socket((pair = PeerSocket6Address.decode(array, offset, builder.skipIPv6)).element0());
offset = pair.element1();
}
final Pair<Number160, Integer> pair;
final PeerAddress peerAddress = builder.peerId((pair = Number160.decode(array, offset)).element0())
.hashCode(builder.peerId.hashCode())
.build();
return new Pair<PeerAddress, Integer>(peerAddress, pair.element1());
}
public static PeerAddress decode(final ByteBuf buf) {
final int header = buf.readUnsignedMedium();
return decode(header, buf);
}
public static PeerAddress decode(final int header, final ByteBuf buf) {
final PeerAddressBuilder builder = new PeerAddressBuilder();
decodeHeader(builder, header);
//relays
for(int i=0;i<builder.relaySize;i++) {
if(builder.relayTypes.get(i)) {
builder.relay(PeerSocket6Address.decode(buf), i);
} else {
builder.relay(PeerSocket4Address.decode(buf), i);
}
}
if(builder.ipv4Flag) {
builder.ipv4Socket(PeerSocket4Address.decode(buf, builder.skipIPv4));
}
if(builder.net4Internal) {
builder.ipInternalSocket(PeerSocket4Address.decode(buf));
}
if(builder.ipv6Flag) {
builder.ipv6Socket(PeerSocket6Address.decode(buf, builder.skipIPv6));
}
return builder
.peerId(Number160.decode(buf))
.hashCode(builder.peerId.hashCode())
.build();
}
//we have a 2 byte header
private static PeerAddressBuilder decodeHeader(final PeerAddressBuilder builder, final int header) {
final byte[] tmp = new byte[]{(byte) ((header & RELAY_TYPE_MASK) >>> 17)};
builder.ipv4Flag((header & IPV4) == IPV4)
.ipv6Flag((header & IPV6) == IPV6)
.reachable4UDP((header & REACHABLE4_UDP) == REACHABLE4_UDP)
.reachable4TCP((header & REACHABLE4_TCP) == REACHABLE4_TCP)
.reachable4UDT((header & REACHABLE4_UDT) == REACHABLE4_UDT)
.reachable6UDP((header & REACHABLE6_UDP) == REACHABLE6_UDP)
.reachable6TCP((header & REACHABLE6_TCP) == REACHABLE6_TCP)
.reachable6UDT((header & REACHABLE6_UDT) == REACHABLE6_UDT)
.relaySize((header & RELAY_SIZE_MASK) >>> 8)
.holePunching((header & RELAY_FLAGS_MASK) == HOLE_PUNCHING)
.slow((header & RELAY_FLAGS_MASK) == SLOW)
.unreachable((header & UNREACHABLE) == UNREACHABLE)
.net4Internal((header & NET4_INTERNAL) == NET4_INTERNAL)
.skipIPv4((header & SKIP_IPV4) == SKIP_IPV4)
.skipIPv6((header & SKIP_IPV6) == SKIP_IPV6)
.relayTypes(BitSet.valueOf(tmp));
return builder;
}
/**
* @return The size of the serialized peer address, calculated using header information only
*/
public int size() {
int size = HEADER_SIZE + Number160.BYTE_ARRAY_SIZE;
for(int i=0;i<relaySize;i++) {
size += relayTypes.get(i) ? PeerSocket6Address.SIZE : PeerSocket4Address.SIZE;
}
if(ipv4Flag) {
size += skipIPv4 ? PeerSocketAddress.PORT_SIZE : PeerSocket4Address.SIZE;
}
if(net4Internal) {
size += PeerSocket4Address.SIZE;
}
if(ipv6Flag) {
size += skipIPv6 ? PeerSocketAddress.PORT_SIZE : PeerSocket6Address.SIZE;
}
return size;
}
public static int size(final int header) {
final PeerAddressBuilder builder = new PeerAddressBuilder();
return decodeHeader(builder, header).build().size();
}
/**
* Serializes to a new array with the proper size.
*
* @return The serialized representation.
*/
public byte[] encode() {
final byte[] retVal = new byte[size()];
encode(retVal, 0);
return retVal;
}
/**
* Serializes to an existing array.
*
* @param offset
* The offset where to start to save the result in the byte array
* @return The new offset.
*/
public int encode(final byte[] array, int offset) {
offset = Utils.mediumToByteArray(encodeHeader(), array, offset);
for(final PeerSocketAddress relay:relays()) {
offset = relay.encode(array, offset);
}
if(ipv4Flag) {
offset = ipv4Socket.encode(array, offset, skipIPv4);
}
if(net4Internal) {
offset = ipInternalSocket.encode(array, offset);
}
if(ipv6Flag) {
offset = ipv6Socket.encode(array, offset, skipIPv6);
}
offset = peerId.encode(array, offset);
return offset;
}
public PeerAddress encode(final ByteBuf buf) {
final int header = encodeHeader() ;
buf.writeMedium(header);
for(final PeerSocketAddress relay:relays()) {
relay.encode(buf);
}
if(ipv4Flag) {
ipv4Socket.encode(buf, skipIPv4);
}
if(net4Internal) {
ipInternalSocket.encode(buf);
}
if(ipv6Flag) {
ipv6Socket.encode(buf, skipIPv6);
}
peerId.encode(buf);
return this;
}
private int encodeHeader() {
return (ipv4Flag ? IPV4 : 0)
| (ipv6Flag ? IPV6 : 0)
| (reachable4UDP ? REACHABLE4_UDP : 0)
| (reachable4TCP ? REACHABLE4_TCP : 0)
| (reachable4UDT ? REACHABLE4_UDT : 0)
| (reachable6UDP ? REACHABLE6_UDP : 0)
| (reachable6TCP ? REACHABLE6_TCP : 0)
| (reachable6UDT ? REACHABLE6_UDT : 0)
| ((relaySize << 8) & RELAY_SIZE_MASK)
| (holePunching ? HOLE_PUNCHING : 0)
| (slow ? SLOW : 0)
| (unreachable ? UNREACHABLE : 0)
| (net4Internal ? NET4_INTERNAL : 0)
| (skipIPv4 ? SKIP_IPV4 : 0)
| (skipIPv6 ? SKIP_IPV6 : 0)
| ((relayTypes() << 17) & RELAY_TYPE_MASK);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("paddr[");
if(ipv4Flag) {
sb.append(ipv4Socket);
if(!reachable4UDP) {
sb.append("u!");
}
if(!reachable4TCP) {
sb.append("t!");
}
if(!reachable4UDT) {
sb.append("d!");
}
}
if(net4Internal) {
sb.append(ipInternalSocket);
}
if(ipv6Flag) {
sb.append(ipv6Socket);
if(!reachable6UDP) {
sb.append("u!");
}
if(!reachable6TCP) {
sb.append("t!");
}
if(!reachable6UDT) {
sb.append("d!");
}
}
sb.append('{');
if(holePunching) {
sb.append('h');
}
if(slow) {
sb.append('s');
}
if(unreachable) {
sb.append('u');
}
if(skipIPv4) {
sb.append('4');
}
if(skipIPv6) {
sb.append('6');
}
sb.append('}');
sb.append("r:").append(relaySize).append("(");
for(final PeerSocketAddress relay:relays()) {
sb.append(relay);
}
sb.append(')');
sb.append(peerId);
return sb.append(']').toString();
}
@Override
public int compareTo(final PeerAddress nodeAddress) {
// the id determines if two peers are equal, the address does not matter
return peerId.compareTo(nodeAddress.peerId);
}
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof PeerAddress)) {
return false;
}
if (this == obj) {
return true;
}
PeerAddress pa = (PeerAddress) obj;
return peerId.equals(pa.peerId);
}
@Override
public int hashCode() {
// don't calculate all the time, only once.
return this.hashCode;
}
public IPv4 calcInternalInetAddress(IPv4 remote) {
if(ipInternalNetworkPrefix == -1) {
throw new IllegalArgumentException("network prefix must be set");
}
if(ipInternalSocket == null) {
throw new IllegalArgumentException("internal peer socket must be set");
}
IP.IPv4 mask = ipInternalSocket.ipv4().maskWithNetworkMask(ipInternalNetworkPrefix);
IP.IPv4 masked = remote.maskWithNetworkMaskInv(ipInternalNetworkPrefix);
return mask.set(masked);
}
public PeerAddress withIPSocket(PeerSocketAddress ps) {
if(ps instanceof PeerSocket4Address) {
return withIpv4Socket((PeerSocket4Address) ps);
} else {
return withIpv6Socket((PeerSocket6Address) ps);
}
}
public static PeerAddress create(final Number160 peerId) {
return builder().peerId(peerId).build();
}
public static PeerAddress create(final Number160 peerId, String host, int udpPort, int tcpPort, int udtPort) throws UnknownHostException {
return create(peerId, InetAddress.getByName(host), udpPort, tcpPort, udtPort);
}
public static PeerAddress create(final Number160 peerId, InetAddress inet, int port) {
return create(peerId, inet, port, port, port + 1);
}
public static PeerAddress create(final Number160 peerId, InetSocketAddress inetSocket) {
return create(peerId, inetSocket.getAddress(), inetSocket.getPort(), inetSocket.getPort(), inetSocket.getPort() + 1);
}
public static PeerAddress create(final Number160 peerId, InetAddress inet, int udpPort, int tcpPort, int udtPort) {
if(inet instanceof Inet4Address) {
final PeerSocket4Address ps4a = PeerSocket4Address.builder()
.ipv4(IP.IPv4.fromInet4Address(inet))
.udpPort(udpPort)
.tcpPort(tcpPort)
.udtPort(udtPort)
.build();
return builder()
.peerId(peerId)
.ipv4Socket(ps4a)
.build();
} else {
final PeerSocket6Address ps6a = PeerSocket6Address.builder()
.ipv6(IP.IPv6.fromInet6Address(inet))
.udpPort(udpPort)
.tcpPort(tcpPort)
.udtPort(udtPort)
.build();
return builder()
.peerId(peerId)
.ipv6Socket(ps6a)
.build();
}
}
public InetSocketAddress createUDPSocket(final PeerAddress other) {
final boolean canIPv6 = ipv6Flag && other.ipv6Flag;
final boolean canIPv4 = ipv4Flag && other.ipv4Flag;
if((preferIPv6Addresses && canIPv6) ||
(!canIPv4 && canIPv6)) {
if(ipv6Socket == null) {
throw new RuntimeException("Flag indicates that ipv6 is present, but its not");
}
return ipv6Socket.createUDPSocket();
} else if(canIPv4) {
if(ipv4Socket == null) {
throw new RuntimeException("Flag indicates that ipv4 is present, but its not");
}
return ipv4Socket.createUDPSocket();
}
else {
throw new RuntimeException("No matching protocal found");
}
}
public InetSocketAddress createTCPSocket(final PeerAddress other) {
final boolean canIPv6 = ipv6Flag && other.ipv6Flag;
final boolean canIPv4 = ipv4Flag && other.ipv4Flag;
if((preferIPv6Addresses && canIPv6) ||
(!canIPv4 && canIPv6)) {
if(ipv6Socket == null) {
throw new RuntimeException("Flag indicates that ipv6 is present, but its not");
}
return ipv6Socket.createTCPSocket();
} else if(canIPv4) {
if(ipv4Socket == null) {
throw new RuntimeException("Flag indicates that ipv4 is present, but its not");
}
return ipv4Socket.createTCPSocket();
}
else {
throw new RuntimeException("No matching protocal found");
}
}
public InetSocketAddress createSocket(final PeerAddress other, final int port) {
final boolean canIPv6 = ipv6Flag && other.ipv6Flag;
final boolean canIPv4 = ipv4Flag && other.ipv4Flag;
if((preferIPv6Addresses && canIPv6) ||
(!canIPv4 && canIPv6)) {
if(ipv6Socket == null) {
throw new RuntimeException("Flag indicates that ipv6 is present, but its not");
}
return ipv6Socket.createSocket(port);
} else if(canIPv4) {
if(ipv4Socket == null) {
throw new RuntimeException("Flag indicates that ipv4 is present, but its not");
}
return ipv4Socket.createSocket(port);
}
else {
throw new RuntimeException("No matching protocal found");
}
}
/*private int prefix4(InetAddress inetAddress) {
NetworkInterface networkInterface;
try {
networkInterface = NetworkInterface.getByInetAddress(internalPeerSocketAddress.inetAddress());
if(networkInterface == null) {
return -1;
}
if(networkInterface.getInterfaceAddresses().size() <= 0) {
return -1;
}
for(InterfaceAddress iface: networkInterface.getInterfaceAddresses()) {
if(iface == null) {
continue;
}
if(iface.getAddress() instanceof Inet4Address) {
return iface.getNetworkPrefixLength();
}
}
return -1;
} catch (SocketException e) {
e.printStackTrace();
return -1;
}
}*/
/*public PeerSocketAddress strip() {
if(internalPeerSocketAddress == null) {
throw new IllegalArgumentException("internal peer socket must be set");
}
if(internalNetworkPrefix == -1) {
return internalPeerSocketAddress;
}
IP.IPv4 masked = IP.fromInet4Address(internalPeerSocketAddress.inetAddress());
masked = masked.maskWithNetworkMaskInv(internalNetworkPrefix);
final InetAddress inet = masked.toInetAddress();
return new PeerSocketAddress(inet, internalPeerSocketAddress.tcpPort(), internalPeerSocketAddress.udpPort());
}*/
}