master
go 167 lines 5.58 KB
Raw
1 package libp2p
2
3 import (
4 "testing"
5
6 ma "github.com/multiformats/go-multiaddr"
7 "github.com/stretchr/testify/require"
8 )
9
10 // mustMultiaddrs parses a list of multiaddr strings or fails the test.
11 func mustMultiaddrs(t *testing.T, addrs ...string) []ma.Multiaddr {
12 t.Helper()
13 out := make([]ma.Multiaddr, 0, len(addrs))
14 for _, s := range addrs {
15 m, err := ma.NewMultiaddr(s)
16 require.NoError(t, err, "parse %q", s)
17 out = append(out, m)
18 }
19 return out
20 }
21
22 func TestFindDeadListeners(t *testing.T) {
23 cases := []struct {
24 name string
25 listenAddrs []ma.Multiaddr
26 addrFilters []string
27 noAnnounce []string
28 want []deadListenerFinding
29 }{
30 {
31 name: "empty config produces no findings",
32 listenAddrs: mustMultiaddrs(t, "/ip4/192.168.1.5/tcp/4001"),
33 },
34 {
35 name: "loopback listener with loopback AddrFilters: one finding",
36 listenAddrs: mustMultiaddrs(t, "/ip4/127.0.0.1/tcp/8081/ws"),
37 addrFilters: []string{"/ip4/127.0.0.0/ipcidr/8"},
38 want: []deadListenerFinding{
39 {Listener: "/ip4/127.0.0.1/tcp/8081/ws", Source: deadListenerSourceAddrFilters, Rule: "/ip4/127.0.0.0/ipcidr/8"},
40 },
41 },
42 {
43 name: "loopback NoAnnounce match alone is operator-intent: skipped",
44 listenAddrs: mustMultiaddrs(t, "/ip4/127.0.0.1/tcp/8081/ws"),
45 noAnnounce: []string{"/ip4/127.0.0.0/ipcidr/8"},
46 },
47 {
48 name: "loopback in both lists: AddrFilters reported, NoAnnounce skipped",
49 listenAddrs: mustMultiaddrs(t, "/ip4/127.0.0.1/tcp/8081/ws"),
50 addrFilters: []string{"/ip4/127.0.0.0/ipcidr/8"},
51 noAnnounce: []string{"/ip4/127.0.0.0/ipcidr/8"},
52 want: []deadListenerFinding{
53 {Listener: "/ip4/127.0.0.1/tcp/8081/ws", Source: deadListenerSourceAddrFilters, Rule: "/ip4/127.0.0.0/ipcidr/8"},
54 },
55 },
56 {
57 name: "non-loopback NoAnnounce match is reported",
58 listenAddrs: mustMultiaddrs(t, "/ip4/192.168.1.5/tcp/4001"),
59 noAnnounce: []string{"/ip4/192.168.0.0/ipcidr/16"},
60 want: []deadListenerFinding{
61 {Listener: "/ip4/192.168.1.5/tcp/4001", Source: deadListenerSourceNoAnnounce, Rule: "/ip4/192.168.0.0/ipcidr/16"},
62 },
63 },
64 {
65 name: "IPv6 loopback (resolved from `::`) with `::/3` AddrFilters: flagged",
66 listenAddrs: mustMultiaddrs(t, "/ip6/::1/tcp/4001"),
67 addrFilters: []string{"/ip6/::/ipcidr/3"},
68 want: []deadListenerFinding{
69 {Listener: "/ip6/::1/tcp/4001", Source: deadListenerSourceAddrFilters, Rule: "/ip6/::/ipcidr/3"},
70 },
71 },
72 {
73 name: "IPv6 loopback NoAnnounce-only is operator-intent: skipped",
74 listenAddrs: mustMultiaddrs(t, "/ip6/::1/tcp/4001"),
75 noAnnounce: []string{"/ip6/::/ipcidr/3"},
76 },
77 {
78 name: "globally-routable IPv6 (resolved from `::`) is not flagged by `::/3`",
79 listenAddrs: mustMultiaddrs(t, "/ip6/2604:2dc0:200:484::1/tcp/4001"),
80 addrFilters: []string{"/ip6/::/ipcidr/3"},
81 },
82 {
83 name: "private LAN listener with matching private CIDR: flagged on AddrFilters",
84 listenAddrs: mustMultiaddrs(t, "/ip4/192.168.1.5/tcp/4001"),
85 addrFilters: []string{"/ip4/192.168.0.0/ipcidr/16"},
86 want: []deadListenerFinding{
87 {Listener: "/ip4/192.168.1.5/tcp/4001", Source: deadListenerSourceAddrFilters, Rule: "/ip4/192.168.0.0/ipcidr/16"},
88 },
89 },
90 {
91 name: "DNS listener has no IP component: no finding",
92 listenAddrs: mustMultiaddrs(t, "/dns/example.com/tcp/443/wss"),
93 addrFilters: []string{"/ip4/127.0.0.0/ipcidr/8"},
94 },
95 {
96 name: "exact-match NoAnnounce entry is skipped (operator-explicit)",
97 listenAddrs: mustMultiaddrs(t, "/ip4/127.0.0.1/tcp/8081/ws"),
98 noAnnounce: []string{"/ip4/127.0.0.1/tcp/8081/ws"},
99 },
100 {
101 name: "malformed filter entry: skipped, valid filters still match",
102 listenAddrs: mustMultiaddrs(t, "/ip4/127.0.0.1/tcp/8081/ws"),
103 addrFilters: []string{"garbage", "/ip4/127.0.0.0/ipcidr/8"},
104 want: []deadListenerFinding{
105 {Listener: "/ip4/127.0.0.1/tcp/8081/ws", Source: deadListenerSourceAddrFilters, Rule: "/ip4/127.0.0.0/ipcidr/8"},
106 },
107 },
108 {
109 name: "bootstrapper-style mix: only AddrFilters loopback fires",
110 listenAddrs: mustMultiaddrs(t,
111 "/ip4/147.135.44.132/tcp/4001",
112 "/ip4/127.0.0.1/tcp/8081/ws",
113 "/ip6/2604:2dc0:200:484::1/tcp/4001",
114 "/ip6/::1/tcp/4001",
115 ),
116 addrFilters: []string{
117 "/ip4/127.0.0.0/ipcidr/8",
118 "/ip6/::/ipcidr/3",
119 },
120 noAnnounce: []string{
121 "/ip4/127.0.0.0/ipcidr/8",
122 "/ip6/::/ipcidr/3",
123 },
124 want: []deadListenerFinding{
125 {Listener: "/ip4/127.0.0.1/tcp/8081/ws", Source: deadListenerSourceAddrFilters, Rule: "/ip4/127.0.0.0/ipcidr/8"},
126 {Listener: "/ip6/::1/tcp/4001", Source: deadListenerSourceAddrFilters, Rule: "/ip6/::/ipcidr/3"},
127 },
128 },
129 }
130
131 for _, tc := range cases {
132 t.Run(tc.name, func(t *testing.T) {
133 got := findDeadListeners(tc.listenAddrs, tc.addrFilters, tc.noAnnounce)
134 require.ElementsMatch(t, tc.want, got)
135 })
136 }
137 }
138
139 // makeAddrsFactory must drop empty multiaddrs from the input list.
140 // A zero-component Multiaddr would otherwise reach the host's signed
141 // peer record and propagate to peers as "/" when they decode the wire
142 // bytes.
143 //
144 // See https://github.com/libp2p/js-libp2p/issues/3478#issuecomment-4322093929
145 func TestMakeAddrsFactoryDropsEmptyMultiaddrs(t *testing.T) {
146 factory, err := makeAddrsFactory(nil, nil, nil)
147 if err != nil {
148 t.Fatal(err)
149 }
150
151 good, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/4001")
152 if err != nil {
153 t.Fatal(err)
154 }
155
156 in := []ma.Multiaddr{nil, good, {}, good}
157 out := factory(in)
158
159 if len(out) != 2 {
160 t.Fatalf("expected 2 addrs after factory filter, got %d: %v", len(out), out)
161 }
162 for i, a := range out {
163 if len(a) == 0 {
164 t.Fatalf("factory returned an empty multiaddr at index %d", i)
165 }
166 }
167 }