From a111af1dd255eebae0d8d01f5b4474e601aacfed Mon Sep 17 00:00:00 2001 From: mk0walsk Date: Sun, 17 May 2026 20:24:21 +0800 Subject: [PATCH] cmd/devp2p,p2p/enode: validate fallback UDP port range --- cmd/devp2p/discv4cmd.go | 2 +- cmd/devp2p/discv4cmd_test.go | 43 ++++++++++++++++++++++++++++++++++++ p2p/enode/localnode.go | 3 +++ p2p/enode/localnode_test.go | 19 ++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 cmd/devp2p/discv4cmd_test.go diff --git a/cmd/devp2p/discv4cmd.go b/cmd/devp2p/discv4cmd.go index 84c7ef0c44..4896e52902 100644 --- a/cmd/devp2p/discv4cmd.go +++ b/cmd/devp2p/discv4cmd.go @@ -336,7 +336,7 @@ func parseExtAddr(spec string) (ip net.IP, port int, ok bool) { return nil, 0, false } port, err = strconv.Atoi(portstr) - if err != nil { + if err != nil || port < 0 || port > 65535 { return nil, 0, false } return ip, port, true diff --git a/cmd/devp2p/discv4cmd_test.go b/cmd/devp2p/discv4cmd_test.go new file mode 100644 index 0000000000..88b6bfe7d9 --- /dev/null +++ b/cmd/devp2p/discv4cmd_test.go @@ -0,0 +1,43 @@ +// Copyright 2026 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import "testing" + +func TestParseExtAddrPortRange(t *testing.T) { + tests := []struct { + input string + ok bool + port int + }{ + {input: "127.0.0.1", ok: true, port: 0}, + {input: "127.0.0.1:30303", ok: true, port: 30303}, + {input: "127.0.0.1:65535", ok: true, port: 65535}, + {input: "127.0.0.1:65536", ok: false}, + {input: "127.0.0.1:-1", ok: false}, + {input: "[2001:db8::1]:30303", ok: true, port: 30303}, + } + for _, tc := range tests { + _, port, ok := parseExtAddr(tc.input) + if ok != tc.ok { + t.Fatalf("parseExtAddr(%q) ok=%v, want %v", tc.input, ok, tc.ok) + } + if ok && port != tc.port { + t.Fatalf("parseExtAddr(%q) port=%d, want %d", tc.input, port, tc.port) + } + } +} diff --git a/p2p/enode/localnode.go b/p2p/enode/localnode.go index 6425560b02..f15b96f2d5 100644 --- a/p2p/enode/localnode.go +++ b/p2p/enode/localnode.go @@ -205,6 +205,9 @@ func (ln *LocalNode) SetFallbackIP(ip net.IP) { // SetFallbackUDP sets the last-resort UDP-on-IPv4 port. This port is used // if no endpoint prediction can be made. func (ln *LocalNode) SetFallbackUDP(port int) { + if port < 0 || port > 65535 { + return + } ln.mu.Lock() defer ln.mu.Unlock() diff --git a/p2p/enode/localnode_test.go b/p2p/enode/localnode_test.go index 5ddc302d65..c8eff68478 100644 --- a/p2p/enode/localnode_test.go +++ b/p2p/enode/localnode_test.go @@ -136,3 +136,22 @@ func TestLocalNodeEndpoint(t *testing.T) { assert.Equal(t, fallback.Port, ln.Node().UDP()) assert.Equal(t, initialSeq+3, ln.Node().Seq()) } + +func TestSetFallbackUDPRejectsOutOfRange(t *testing.T) { + ln, db := newLocalNodeForTesting() + defer db.Close() + + initialSeq := ln.Node().Seq() + ln.SetFallbackUDP(30303) + assert.Equal(t, 30303, ln.Node().UDP()) + assert.Equal(t, initialSeq+1, ln.Node().Seq()) + + seqAfterValid := ln.Node().Seq() + ln.SetFallbackUDP(65536) + assert.Equal(t, 30303, ln.Node().UDP()) + assert.Equal(t, seqAfterValid, ln.Node().Seq()) + + ln.SetFallbackUDP(-1) + assert.Equal(t, 30303, ln.Node().UDP()) + assert.Equal(t, seqAfterValid, ln.Node().Seq()) +}