Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

Handle case where peers advertise a listening port of 0 #1391

Merged
merged 2 commits into from
May 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,17 @@ protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final L
}

private Peer createPeer(final PeerInfo peerInfo, final ChannelHandlerContext ctx) {
InetSocketAddress remoteAddress = ((InetSocketAddress) ctx.channel().remoteAddress());
final InetSocketAddress remoteAddress = ((InetSocketAddress) ctx.channel().remoteAddress());
int port = peerInfo.getPort();
if (port == 0) {
// Most peers advertise a port of "0", just set a default best guess in this case
port = EnodeURL.DEFAULT_LISTENING_PORT;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of the default port perhaps we could use the remote port of the current communication? We are already doing this with the remoteAddress.

Also, can these two values ever differ? Such as if we have an eth loadbalancer or proxy in front of us (in case those ever exist for devp2p).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I experimented with trying to use the remote port, but it doesn't match the listening port. When the other nodes are initiating outbound connections, they're free to use whatever port they want for their side of the connection.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Un-resolving this comment because I think its a good question and will be helpful to have it visible here as a kind of documentation. Actually, I think I might add a comment in the codebase as well :D

}
return DefaultPeer.fromEnodeURL(
EnodeURL.builder()
.nodeId(peerInfo.getNodeId())
.ipAddress(remoteAddress.getAddress())
.listeningPort(peerInfo.getPort())
.listeningPort(port)
.build());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public interface Peer extends PeerId {
* @return The generated peer ID.
*/
static BytesValue randomId() {
final byte[] id = new byte[64];
final byte[] id = new byte[EnodeURL.NODE_ID_SIZE];
SecureRandomProvider.publicSecureRandom().nextBytes(id);
return BytesValue.wrap(id);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ public List<Capability> getCapabilities() {
return capabilities;
}

/**
* This value is meant to represent the port at which a peer is listening for connections.
* However, most peers actually advertise a port of "0" so this value is not reliable.
*
* @return (Unreliable) The tcp port on which the peer is listening for connections
*/
public int getPort() {
return port;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,59 @@ public void decode_handlesHello() throws ExecutionException, InterruptedExceptio
assertThat(out.size()).isEqualTo(1);
}

@Test
public void decode_handlesHelloFromPeerWithAdvertisedPortOf0()
throws ExecutionException, InterruptedException {
ChannelFuture future = NettyMocks.channelFuture(false);
when(channel.closeFuture()).thenReturn(future);

final Peer peer = createRemotePeer();
final PeerInfo remotePeerInfo =
new PeerInfo(
peerInfo.getVersion(),
peerInfo.getClientId(),
peerInfo.getCapabilities(),
0,
peer.getId());
final DeFramer deFramer = createDeFramer(null);

HelloMessage helloMessage = HelloMessage.create(remotePeerInfo);
ByteBuf data = Unpooled.wrappedBuffer(helloMessage.getData().extractArray());
when(framer.deframe(eq(data)))
.thenReturn(new RawMessage(helloMessage.getCode(), helloMessage.getData()))
.thenReturn(null);
List<Object> out = new ArrayList<>();
deFramer.decode(ctx, data, out);

assertThat(connectFuture).isDone();
assertThat(connectFuture).isNotCompletedExceptionally();
PeerConnection peerConnection = connectFuture.get();
assertThat(peerConnection.getPeerInfo()).isEqualTo(remotePeerInfo);
assertThat(out).isEmpty();

final EnodeURL expectedEnode =
EnodeURL.builder()
.ipAddress(remoteAddress.getAddress())
.nodeId(peer.getId())
// Listening port should be replaced with default port
.listeningPort(EnodeURL.DEFAULT_LISTENING_PORT)
.build();
assertThat(peerConnection.getPeer().getEnodeURL()).isEqualTo(expectedEnode);

// Next phase of pipeline should be setup
verify(pipeline, times(1)).addLast(any());

// Next message should be pushed out
PingMessage nextMessage = PingMessage.get();
ByteBuf nextData = Unpooled.wrappedBuffer(nextMessage.getData().extractArray());
when(framer.deframe(eq(nextData)))
.thenReturn(new RawMessage(nextMessage.getCode(), nextMessage.getData()))
.thenReturn(null);
verify(pipeline, times(1)).addLast(any());
deFramer.decode(ctx, nextData, out);
assertThat(out.size()).isEqualTo(1);
}

@Test
public void decode_handlesUnexpectedPeerId() {
ChannelFuture future = NettyMocks.channelFuture(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ public interface DefaultCommandValues {
SyncMode DEFAULT_SYNC_MODE = SyncMode.FULL;
int FAST_SYNC_MAX_WAIT_TIME = 0;
int FAST_SYNC_MIN_PEER_COUNT = 5;
int P2P_PORT = 30303;
int DEFAULT_MAX_PEERS = 25;

static Path getDefaultPantheonDataPath(final Object command) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ void setBootnodes(final List<String> values) {
paramLabel = MANDATORY_PORT_FORMAT_HELP,
description = "Port on which to listen for p2p communication (default: ${DEFAULT-VALUE})",
arity = "1")
private final Integer p2pPort = P2P_PORT;
private final Integer p2pPort = EnodeURL.DEFAULT_LISTENING_PORT;

@Option(
names = {"--network-id"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@

public class EnodeURL {

private static final int NODE_ID_SIZE = 64;
public static final int DEFAULT_LISTENING_PORT = 30303;
public static final int NODE_ID_SIZE = 64;
private static final Pattern DISCPORT_QUERY_STRING_REGEX =
Pattern.compile("^discport=([0-9]{1,5})$");
private static final Pattern NODE_ID_PATTERN = Pattern.compile("^[0-9a-fA-F]{128}$");
Expand Down