Skip to content

Commit 3764bac

Browse files
committed
fix(neutrino): parse discovered .onion peers for ban checks
1 parent 421c476 commit 3764bac

81 files changed

Lines changed: 24165 additions & 7 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- Fix `.onion` peer hostname handling in ban checks by patching the neutrino
13+
ban manager parser to support Tor v2/v3 addresses, so discovered onion peers
14+
no longer trigger `unsupported IP type` parse errors.
15+
1016
## [1.0.0] - 2026-04-07
1117

1218
### Added

neutrino_server/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ require (
1414
golang.org/x/net v0.48.0
1515
)
1616

17+
replace github.com/lightninglabs/neutrino => ./third_party/neutrino
18+
1719
require (
1820
github.com/aead/siphash v1.0.1 // indirect
1921
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect

neutrino_server/go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
7474
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
7575
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE=
7676
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
77-
github.com/lightninglabs/neutrino v0.16.0 h1:YNTQG32fPR/Zg0vvJVI65OBH8l3U18LSXXtX91hx0q0=
78-
github.com/lightninglabs/neutrino v0.16.0/go.mod h1:x3OmY2wsA18+Kc3TSV2QpSUewOCiscw2mKpXgZv2kZk=
7977
github.com/lightninglabs/neutrino/cache v1.1.2 h1:C9DY/DAPaPxbFC+xNNEI/z1SJY9GS3shmlu5hIQ798g=
8078
github.com/lightninglabs/neutrino/cache v1.1.2/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo=
8179
github.com/lightningnetwork/lnd/clock v1.0.1 h1:QQod8+m3KgqHdvVMV+2DRNNZS1GRFir8mHZYA+Z2hFo=
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package neutrino
2+
3+
import (
4+
"net"
5+
"testing"
6+
7+
"github.com/lightninglabs/neutrino/banman"
8+
)
9+
10+
func TestParseIPNetOnionV3(t *testing.T) {
11+
addr := "yov4edh4vgbgywplxuxv4esroksz2brb64fdtjbryc5wbo43wtlbsiad.onion:38333"
12+
13+
ipNet, err := banman.ParseIPNet(addr, nil)
14+
if err != nil {
15+
t.Fatalf("ParseIPNet() failed for onion v3: %v", err)
16+
}
17+
18+
if ipNet == nil || ipNet.IP == nil || ipNet.IP.To16() == nil {
19+
t.Fatalf("expected IPv6 ipnet for onion v3, got: %#v", ipNet)
20+
}
21+
22+
ones, bits := ipNet.Mask.Size()
23+
if ones != 128 || bits != 128 {
24+
t.Fatalf("expected /128 mask for onion v3, got /%d (%d bits)", ones, bits)
25+
}
26+
}
27+
28+
func TestParseIPNetOnionV2(t *testing.T) {
29+
addr := "777myonionurl777.onion:8333"
30+
31+
ipNet, err := banman.ParseIPNet(addr, nil)
32+
if err != nil {
33+
t.Fatalf("ParseIPNet() failed for onion v2: %v", err)
34+
}
35+
36+
if ipNet == nil || ipNet.IP == nil || ipNet.IP.To16() == nil {
37+
t.Fatalf("expected IPv6 ipnet for onion v2, got: %#v", ipNet)
38+
}
39+
}
40+
41+
func TestParseIPNetOnionStableAcrossPortVariants(t *testing.T) {
42+
host := "yov4edh4vgbgywplxuxv4esroksz2brb64fdtjbryc5wbo43wtlbsiad.onion"
43+
44+
withPort, err := banman.ParseIPNet(net.JoinHostPort(host, "38333"), nil)
45+
if err != nil {
46+
t.Fatalf("ParseIPNet() with port failed: %v", err)
47+
}
48+
49+
withoutPort, err := banman.ParseIPNet(host, nil)
50+
if err != nil {
51+
t.Fatalf("ParseIPNet() without port failed: %v", err)
52+
}
53+
54+
if withPort.String() != withoutPort.String() {
55+
t.Fatalf("expected stable onion mapping, withPort=%s withoutPort=%s", withPort.String(), withoutPort.String())
56+
}
57+
}
58+
59+
func TestParseIPNetRejectsInvalidOnion(t *testing.T) {
60+
_, err := banman.ParseIPNet("invalid.onion:8333", nil)
61+
if err == nil {
62+
t.Fatal("expected error for invalid onion hostname")
63+
}
64+
}

neutrino_server/internal/neutrino/node.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -284,17 +284,24 @@ func (n *Node) startChainService(useTor bool) error {
284284

285285
// Set up DNS resolution through Tor to prevent DNS leaks
286286
neutrinoConfig.NameResolver = func(host string) ([]net.IP, error) {
287-
if ip := net.ParseIP(host); ip != nil {
287+
lookupHost := host
288+
if splitHost, _, err := net.SplitHostPort(host); err == nil {
289+
lookupHost = splitHost
290+
}
291+
292+
if ip := net.ParseIP(lookupHost); ip != nil {
288293
return []net.IP{ip}, nil
289294
}
290295

291-
if strings.HasSuffix(host, ".onion") {
292-
return []net.IP{net.IP([]byte(host))}, nil
296+
if strings.HasSuffix(strings.ToLower(lookupHost), ".onion") {
297+
// Tor hostnames are not DNS-resolved. They are dialed directly
298+
// through SOCKS when selected as peers.
299+
return nil, nil
293300
}
294301

295-
ips, err := connmgr.TorLookupIP(host, n.config.TorProxy)
302+
ips, err := connmgr.TorLookupIP(lookupHost, n.config.TorProxy)
296303
if err != nil {
297-
n.logger.Warnf("Tor DNS lookup failed for %s: %v", host, err)
304+
n.logger.Warnf("Tor DNS lookup failed for %s: %v", lookupHost, err)
298305
return nil, err
299306
}
300307

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- "master"
7+
pull_request:
8+
branches:
9+
- "*"
10+
11+
defaults:
12+
run:
13+
shell: bash
14+
15+
env:
16+
GOCACHE: /home/runner/work/go/pkg/build
17+
GOPATH: /home/runner/work/go
18+
GO_VERSION: 1.19.x
19+
20+
jobs:
21+
########################
22+
# compilation check
23+
########################
24+
rpc-check:
25+
name: RPC and mobile compilation check
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: git checkout
29+
uses: actions/checkout@v2
30+
31+
- name: setup go ${{ env.GO_VERSION }}
32+
uses: actions/setup-go@v2
33+
with:
34+
go-version: '${{ env.GO_VERSION }}'
35+
36+
- name: run check
37+
run: make build
38+
39+
########################
40+
# lint code
41+
########################
42+
lint:
43+
name: lint code
44+
runs-on: ubuntu-latest
45+
steps:
46+
- name: git checkout
47+
uses: actions/checkout@v2
48+
with:
49+
# The same as "git fetch --unshallow" but also works when running the
50+
# action locally with "act".
51+
fetch-depth: 0
52+
53+
- name: setup go ${{ env.GO_VERSION }}
54+
uses: actions/setup-go@v2
55+
with:
56+
go-version: '${{ env.GO_VERSION }}'
57+
58+
- name: lint
59+
run: make lint
60+
61+
########################
62+
# run unit tests
63+
########################
64+
unit-test:
65+
name: run unit tests
66+
runs-on: ubuntu-latest
67+
strategy:
68+
# Allow other tests in the matrix to continue if one fails.
69+
fail-fast: false
70+
matrix:
71+
unit_type:
72+
- unit-cover
73+
- unit-race
74+
steps:
75+
- name: git checkout
76+
uses: actions/checkout@v2
77+
78+
- name: setup go ${{ env.GO_VERSION }}
79+
uses: actions/setup-go@v2
80+
with:
81+
go-version: '${{ env.GO_VERSION }}'
82+
83+
- name: run ${{ matrix.unit_type }}
84+
run: make ${{ matrix.unit_type }}
85+
86+
- name: Send coverage
87+
uses: shogo82148/actions-goveralls@v1
88+
if: matrix.unit_type == 'unit-cover'
89+
with:
90+
path-to-profile: coverage.txt
91+
parallel: true
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Binaries for programs and plugins
2+
*.exe
3+
*.dll
4+
*.so
5+
*.dylib
6+
7+
# Test binary, build with `go test -c`
8+
*.test
9+
10+
# Output of the go coverage tool, specifically when used with LiteIDE
11+
*.out
12+
13+
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
14+
.glide/
15+
16+
# Glide vendor subdirectory
17+
vendor/
18+
19+
#GoLand config files
20+
.idea
21+
*.DS_Store
22+
23+
# vim swap files
24+
*.swp
25+
26+
# delve breakpoints
27+
breakpoints.txt
28+
29+
# coverage output
30+
coverage.txt
31+
32+
# go workspace
33+
go.work
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
run:
2+
# timeout for analysis
3+
deadline: 10m
4+
5+
linters-settings:
6+
govet:
7+
# Don't report about shadowed variables
8+
check-shadowing: false
9+
gofmt:
10+
# simplify code: gofmt with `-s` option, true by default
11+
simplify: true
12+
whitespace:
13+
multi-func: true
14+
multi-if: true
15+
16+
linters:
17+
enable-all: true
18+
disable:
19+
# Global variables are used in many places throughout the code base.
20+
- gochecknoglobals
21+
22+
# Some lines are over 80 characters on purpose and we don't want to make them
23+
# even longer by marking them as 'nolint'.
24+
- lll
25+
26+
# We want to allow short variable names.
27+
- varnamelen
28+
29+
# We want to allow TODOs.
30+
- godox
31+
32+
# We have long functions, especially in tests. Moving or renaming those would
33+
# trigger funlen problems that we may not want to solve at that time.
34+
- funlen
35+
36+
# Disable for now as we haven't yet tuned the sensitivity to our codebase
37+
# yet. Enabling by default for example, would also force new contributors to
38+
# potentially extensively refactor code, when they want to smaller change to
39+
# land.
40+
- gocyclo
41+
- gocognit
42+
- cyclop
43+
44+
# Instances of table driven tests that don't pre-allocate shouldn't trigger
45+
# the linter.
46+
- prealloc
47+
48+
# Init functions are used by loggers throughout the codebase.
49+
- gochecknoinits
50+
51+
# Causes stack overflow, see https://github.com/polyfloyd/go-errorlint/issues/19.
52+
- errorlint
53+
54+
# Deprecated linters. See https://golangci-lint.run/usage/linters/.
55+
- interfacer
56+
- golint
57+
- maligned
58+
- scopelint
59+
60+
# New linters that need a code adjustment first.
61+
- wrapcheck
62+
- nolintlint
63+
- paralleltest
64+
- tparallel
65+
- testpackage
66+
- gofumpt
67+
- gomoddirectives
68+
- ireturn
69+
- maintidx
70+
- nlreturn
71+
- dogsled
72+
- gci
73+
- containedctx
74+
- contextcheck
75+
- errname
76+
- exhaustivestruct
77+
- goerr113
78+
- gomnd
79+
- ifshort
80+
- noctx
81+
- nestif
82+
- wsl
83+
- exhaustive
84+
- forcetypeassert
85+
- nilerr
86+
- nilnil
87+
- stylecheck
88+
- thelper
89+
- exhaustruct
90+
- nosnakecase
91+
92+
issues:
93+
exclude-rules:
94+
# Exclude gosec from running for tests so that tests with weak randomness
95+
# (math/rand) will pass the linter.
96+
- path: _test\.go
97+
linters:
98+
- gosec
99+
- errcheck
100+
- dupl
101+
102+
# Instances of table driven tests that don't pre-allocate shouldn't
103+
# trigger the linter.
104+
- prealloc
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017-2022 Lightning Labs
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)