@cryptotaxi247 / kubo / commits / 7ec1a674e

p2p/net/swarm: do not usre link local addrs

Juan Batiz-Benet committed Jan 12, 2015 at 12:21 UTC 7ec1a674e34c5d30868c2d936d689ffe7b011ecb
12 files changed +586 -233
core/core.go
+8 -2
@@ -24,6 +24,7 @@ import (
24 p2phost "github.com/jbenet/go-ipfs/p2p/host"
25 p2pbhost "github.com/jbenet/go-ipfs/p2p/host/basic"
26 swarm "github.com/jbenet/go-ipfs/p2p/net/swarm"
27 + addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
28 peer "github.com/jbenet/go-ipfs/p2p/peer"
29 path "github.com/jbenet/go-ipfs/path"
30 pin "github.com/jbenet/go-ipfs/pin"
@@ -352,11 +353,16 @@ func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
353 // isolates the complex initialization steps
354 func constructPeerHost(ctx context.Context, ctxg ctxgroup.ContextGroup, cfg *config.Config, id peer.ID, ps peer.Peerstore) (p2phost.Host, error) {
355 listenAddrs, err := listenAddresses(cfg)
355 - // make sure we dont error out if our config includes some addresses we cant use.
356 - filteredAddrs := swarm.FilterAddrs(listenAddrs)
356 if err != nil {
357 return nil, debugerror.Wrap(err)
358 }
359 +
360 + // make sure we error out if our config does not have addresses we can use
361 + filteredAddrs := addrutil.FilterAddrs(listenAddrs)
362 + if len(filteredAddrs) < 1 {
363 + return nil, debugerror.Errorf("addresses in config not usable: %s", listenAddrs)
364 + }
365 +
366 network, err := swarm.NewNetwork(ctx, filteredAddrs, id, ps)
367 if err != nil {
368 return nil, debugerror.Wrap(err)
p2p/net/conn/dial.go
+12 -21
@@ -22,7 +22,7 @@ func (d *Dialer) String() string {
22 // Example: d.DialAddr(ctx, peer.Addresses()[0], peer)
23 func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (Conn, error) {
24
25 - network, _, err := manet.DialArgs(raddr)
25 + _, _, err := manet.DialArgs(raddr)
26 if err != nil {
27 return nil, err
28 }
@@ -31,17 +31,20 @@ func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (
31 return nil, debugerror.Errorf("Attempted to connect to zero address: %s", raddr)
32 }
33
34 - var laddr ma.Multiaddr
34 if len(d.LocalAddrs) > 0 {
36 - // laddr := MultiaddrNetMatch(raddr, d.LocalAddrs)
37 - laddr = NetAddress(network, d.LocalAddrs)
38 - if laddr == nil {
39 - return nil, debugerror.Errorf("No local address for network %s", network)
35 + laddrs := manet.AddrMatch(raddr, d.LocalAddrs)
36 + if len(laddrs) < 1 {
37 + return nil, debugerror.Errorf("No local address matches %s %s", raddr, d.LocalAddrs)
38 }
41 - }
39
43 - // TODO: try to get reusing addr/ports to work.
44 - // d.Dialer.LocalAddr = laddr
40 + // TODO pick with a good heuristic
41 + // we use a random one for now to prevent bad addresses from making nodes unreachable
42 + // with a random selection, multiple tries may work.
43 + // laddr := laddrs[rand.Intn(len(laddrs))]
44 +
45 + // TODO: try to get reusing addr/ports to work.
46 + // d.Dialer.LocalAddr = laddr
47 + }
48
49 log.Debugf("%s dialing %s %s", d.LocalPeer, remote, raddr)
50 maconn, err := d.Dialer.Dial(raddr)
@@ -116,15 +119,3 @@ func MultiaddrNetMatch(tgt ma.Multiaddr, srcs []ma.Multiaddr) ma.Multiaddr {
119 }
120 return nil
121 }
119 -
120 -// NetAddress returns the first Multiaddr found for a given network.
121 -func NetAddress(n string, addrs []ma.Multiaddr) ma.Multiaddr {
122 - for _, a := range addrs {
123 - for _, p := range a.Protocols() {
124 - if p.Name == n {
125 - return a
126 - }
127 - }
128 - }
129 - return nil
130 -}
p2p/net/conn/listen.go
+2 -6
@@ -102,11 +102,7 @@ func (l *listener) Addr() net.Addr {
102 // If there is an error converting from net.Addr to ma.Multiaddr,
103 // the return value will be nil.
104 func (l *listener) Multiaddr() ma.Multiaddr {
105 - maddr, err := manet.FromNetAddr(l.Addr())
106 - if err != nil {
107 - return nil // error
108 - }
109 - return maddr
105 + return l.Listener.Multiaddr()
106 }
107
108 // LocalPeer is the identity of the local Peer.
@@ -140,7 +136,7 @@ func Listen(ctx context.Context, addr ma.Multiaddr, local peer.ID, sk ic.PrivKey
136 }
137 l.cg.SetTeardown(l.teardown)
138
143 - log.Infof("swarm listening on %s", l.Multiaddr())
139 + log.Debugf("Conn Listener on %s", l.Multiaddr())
140 log.Event(ctx, "swarmListen", l)
141 return l, nil
142 }
p2p/net/swarm/addr.go deleted
-193
@@ -1,193 +0,0 @@
1 -package swarm
2 -
3 -import (
4 - conn "github.com/jbenet/go-ipfs/p2p/net/conn"
5 - eventlog "github.com/jbenet/go-ipfs/util/eventlog"
6 -
7 - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
8 - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
9 - manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
10 -)
11 -
12 -// SupportedTransportStrings is the list of supported transports for the swarm.
13 -// These are strings of encapsulated multiaddr protocols. E.g.:
14 -// /ip4/tcp
15 -var SupportedTransportStrings = []string{
16 - "/ip4/tcp",
17 - "/ip6/tcp",
18 - // "/ip4/udp/utp", disabled because the lib is broken
19 - // "/ip6/udp/utp", disabled because the lib is broken
20 - // "/ip4/udp/udt", disabled because the lib doesnt work on arm
21 - // "/ip6/udp/udt", disabled because the lib doesnt work on arm
22 -}
23 -
24 -// SupportedTransportProtocols is the list of supported transports for the swarm.
25 -// These are []ma.Protocol lists. Populated at runtime from SupportedTransportStrings
26 -var SupportedTransportProtocols = [][]ma.Protocol{}
27 -
28 -func init() {
29 - // initialize SupportedTransportProtocols
30 - transports := make([][]ma.Protocol, len(SupportedTransportStrings))
31 - for _, s := range SupportedTransportStrings {
32 - t, err := ma.ProtocolsWithString(s)
33 - if err != nil {
34 - panic(err) // important to fix this in the codebase
35 - }
36 - transports = append(transports, t)
37 - }
38 - SupportedTransportProtocols = transports
39 -}
40 -
41 -// FilterAddrs is a filter that removes certain addresses
42 -// from a list. the addresses removed are those known NOT
43 -// to work with swarm. Namely, addresses with UTP.
44 -func FilterAddrs(a []ma.Multiaddr) []ma.Multiaddr {
45 - b := make([]ma.Multiaddr, 0, len(a))
46 - for _, addr := range a {
47 - if AddrUsable(addr) {
48 - b = append(b, addr)
49 - }
50 - }
51 - return b
52 -}
53 -
54 -// AddrUsable returns whether the swarm can use this addr.
55 -func AddrUsable(a ma.Multiaddr) bool {
56 - // test the address protocol list is in SupportedTransportProtocols
57 -
58 - matches := func(a, b []ma.Protocol) bool {
59 - if len(a) != len(b) {
60 - return false
61 - }
62 -
63 - for i := range a {
64 - if a[i].Code != b[i].Code {
65 - return false
66 - }
67 - }
68 - return true
69 - }
70 -
71 - transport := a.Protocols()
72 - for _, supported := range SupportedTransportProtocols {
73 - if matches(supported, transport) {
74 - return true
75 - }
76 - }
77 -
78 - return false
79 -}
80 -
81 -// ListenAddresses returns a list of addresses at which this swarm listens.
82 -func (s *Swarm) ListenAddresses() []ma.Multiaddr {
83 - listeners := s.swarm.Listeners()
84 - addrs := make([]ma.Multiaddr, 0, len(listeners))
85 - for _, l := range listeners {
86 - if l2, ok := l.NetListener().(conn.Listener); ok {
87 - addrs = append(addrs, l2.Multiaddr())
88 - }
89 - }
90 - return addrs
91 -}
92 -
93 -// InterfaceListenAddresses returns a list of addresses at which this swarm
94 -// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
95 -// use the known local interfaces.
96 -func InterfaceListenAddresses(s *Swarm) ([]ma.Multiaddr, error) {
97 - return resolveUnspecifiedAddresses(s.ListenAddresses())
98 -}
99 -
100 -// resolveUnspecifiedAddresses expands unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
101 -// use the known local interfaces.
102 -func resolveUnspecifiedAddresses(unspecifiedAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
103 - var outputAddrs []ma.Multiaddr
104 -
105 - // todo optimize: only fetch these if we have a "any" addr.
106 - ifaceAddrs, err := interfaceAddresses()
107 - if err != nil {
108 - return nil, err
109 - }
110 -
111 - for _, a := range unspecifiedAddrs {
112 -
113 - // split address into its components
114 - split := ma.Split(a)
115 -
116 - // if first component (ip) is not unspecified, use it as is.
117 - if !manet.IsIPUnspecified(split[0]) {
118 - outputAddrs = append(outputAddrs, a)
119 - continue
120 - }
121 -
122 - // unspecified? add one address per interface.
123 - for _, ia := range ifaceAddrs {
124 - split[0] = ia
125 - joined := ma.Join(split...)
126 - outputAddrs = append(outputAddrs, joined)
127 - }
128 - }
129 -
130 - log.Event(context.TODO(), "interfaceListenAddresses", func() eventlog.Loggable {
131 - var addrs []string
132 - for _, addr := range outputAddrs {
133 - addrs = append(addrs, addr.String())
134 - }
135 - return eventlog.Metadata{"addresses": addrs}
136 - }())
137 - log.Debug("InterfaceListenAddresses:", outputAddrs)
138 - return outputAddrs, nil
139 -}
140 -
141 -// interfaceAddresses returns a list of addresses associated with local machine
142 -func interfaceAddresses() ([]ma.Multiaddr, error) {
143 - maddrs, err := manet.InterfaceMultiaddrs()
144 - if err != nil {
145 - return nil, err
146 - }
147 -
148 - var nonLoopback []ma.Multiaddr
149 - for _, a := range maddrs {
150 - if !manet.IsIPLoopback(a) {
151 - nonLoopback = append(nonLoopback, a)
152 - }
153 - }
154 -
155 - return nonLoopback, nil
156 -}
157 -
158 -// addrInList returns whether or not an address is part of a list.
159 -// this is useful to check if NAT is happening (or other bugs?)
160 -func addrInList(addr ma.Multiaddr, list []ma.Multiaddr) bool {
161 - for _, addr2 := range list {
162 - if addr.Equal(addr2) {
163 - return true
164 - }
165 - }
166 - return false
167 -}
168 -
169 -// checkNATWarning checks if our observed addresses differ. if so,
170 -// informs the user that certain things might not work yet
171 -func checkNATWarning(s *Swarm, observed ma.Multiaddr, expected ma.Multiaddr) {
172 - if observed.Equal(expected) {
173 - return
174 - }
175 -
176 - listen, err := InterfaceListenAddresses(s)
177 - if err != nil {
178 - log.Errorf("Error retrieving swarm.InterfaceListenAddresses: %s", err)
179 - return
180 - }
181 -
182 - if !addrInList(observed, listen) { // probably a nat
183 - log.Warningf(natWarning, observed, listen)
184 - }
185 -}
186 -
187 -const natWarning = `Remote peer observed our address to be: %s
188 -The local addresses are: %s
189 -Thus, connection is going through NAT, and other connections may fail.
190 -
191 -IPFS NAT traversal is still under development. Please bug us on github or irc to fix this.
192 -Baby steps: http://jbenet.static.s3.amazonaws.com/271dfcf/baby-steps.gif
193 -`
p2p/net/swarm/addr/addr.go new
+258
@@ -0,0 +1,258 @@
1 +package addrutil
2 +
3 +import (
4 + "fmt"
5 +
6 + eventlog "github.com/jbenet/go-ipfs/util/eventlog"
7 +
8 + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
9 + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
10 + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
11 +)
12 +
13 +var log = eventlog.Logger("p2p/net/swarm/addr")
14 +
15 +// SupportedTransportStrings is the list of supported transports for the swarm.
16 +// These are strings of encapsulated multiaddr protocols. E.g.:
17 +// /ip4/tcp
18 +var SupportedTransportStrings = []string{
19 + "/ip4/tcp",
20 + "/ip6/tcp",
21 + // "/ip4/udp/utp", disabled because the lib is broken
22 + // "/ip6/udp/utp", disabled because the lib is broken
23 + // "/ip4/udp/udt", disabled because the lib doesnt work on arm
24 + // "/ip6/udp/udt", disabled because the lib doesnt work on arm
25 +}
26 +
27 +// SupportedTransportProtocols is the list of supported transports for the swarm.
28 +// These are []ma.Protocol lists. Populated at runtime from SupportedTransportStrings
29 +var SupportedTransportProtocols = [][]ma.Protocol{}
30 +
31 +func init() {
32 + // initialize SupportedTransportProtocols
33 + transports := make([][]ma.Protocol, len(SupportedTransportStrings))
34 + for _, s := range SupportedTransportStrings {
35 + t, err := ma.ProtocolsWithString(s)
36 + if err != nil {
37 + panic(err) // important to fix this in the codebase
38 + }
39 + transports = append(transports, t)
40 + }
41 + SupportedTransportProtocols = transports
42 +}
43 +
44 +// FilterAddrs is a filter that removes certain addresses
45 +// from a list. the addresses removed are those known NOT
46 +// to work with our network. Namely, addresses with UTP.
47 +func FilterAddrs(a []ma.Multiaddr) []ma.Multiaddr {
48 + b := make([]ma.Multiaddr, 0, len(a))
49 + for _, addr := range a {
50 + if AddrUsable(addr, false) {
51 + b = append(b, addr)
52 + }
53 + }
54 + return b
55 +}
56 +
57 +// AddrOverNonLocalIP returns whether the addr uses a non-local ip link
58 +func AddrOverNonLocalIP(a ma.Multiaddr) bool {
59 + split := ma.Split(a)
60 + if len(split) < 1 {
61 + return false
62 + }
63 + if manet.IsIP6LinkLocal(split[0]) {
64 + return false
65 + }
66 + return true
67 +}
68 +
69 +// AddrUsable returns whether our network can use this addr.
70 +// We only use the transports in SupportedTransportStrings,
71 +// and we do not link local addresses. Loopback is ok
72 +// as we need to be able to connect to multiple ipfs nodes
73 +// in the same machine.
74 +func AddrUsable(a ma.Multiaddr, partial bool) bool {
75 +
76 + if !AddrOverNonLocalIP(a) {
77 + return false
78 + }
79 +
80 + // test the address protocol list is in SupportedTransportProtocols
81 + matches := func(supported, test []ma.Protocol) bool {
82 + if len(test) > len(supported) {
83 + return false
84 + }
85 +
86 + // when partial, it's ok if test < supported.
87 + if !partial && len(supported) != len(test) {
88 + return false
89 + }
90 +
91 + for i := range test {
92 + if supported[i].Code != test[i].Code {
93 + return false
94 + }
95 + }
96 + return true
97 + }
98 +
99 + transport := a.Protocols()
100 + for _, supported := range SupportedTransportProtocols {
101 + if matches(supported, transport) {
102 + return true
103 + }
104 + }
105 +
106 + return false
107 +}
108 +
109 +// ResolveUnspecifiedAddress expands an unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
110 +// use the known local interfaces. If ifaceAddr is nil, we request interface addresses
111 +// from the network stack. (this is so you can provide a cached value if resolving many addrs)
112 +func ResolveUnspecifiedAddress(resolve ma.Multiaddr, ifaceAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
113 + // split address into its components
114 + split := ma.Split(resolve)
115 +
116 + // if first component (ip) is not unspecified, use it as is.
117 + if !manet.IsIPUnspecified(split[0]) {
118 + return []ma.Multiaddr{resolve}, nil
119 + }
120 +
121 + out := make([]ma.Multiaddr, 0, len(ifaceAddrs))
122 + for _, ia := range ifaceAddrs {
123 + // must match the first protocol to be resolve.
124 + if ia.Protocols()[0].Code != resolve.Protocols()[0].Code {
125 + continue
126 + }
127 +
128 + split[0] = ia
129 + joined := ma.Join(split...)
130 + out = append(out, joined)
131 + log.Debug("adding resolved addr:", resolve, joined, out)
132 + }
133 + if len(out) < 1 {
134 + return nil, fmt.Errorf("failed to resolve: %s", resolve)
135 + }
136 + return out, nil
137 +}
138 +
139 +// ResolveUnspecifiedAddresses expands unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
140 +// use the known local interfaces.
141 +func ResolveUnspecifiedAddresses(unspecAddrs, ifaceAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
142 +
143 + // todo optimize: only fetch these if we have a "any" addr.
144 + if len(ifaceAddrs) < 1 {
145 + var err error
146 + ifaceAddrs, err = InterfaceAddresses()
147 + if err != nil {
148 + return nil, err
149 + }
150 + // log.Debug("InterfaceAddresses:", ifaceAddrs)
151 + }
152 +
153 + var outputAddrs []ma.Multiaddr
154 + for _, a := range unspecAddrs {
155 + // unspecified?
156 + resolved, err := ResolveUnspecifiedAddress(a, ifaceAddrs)
157 + if err != nil {
158 + continue // optimistic. if we cant resolve anything, we'll know at the bottom.
159 + }
160 + // log.Debug("resolved:", a, resolved)
161 + outputAddrs = append(outputAddrs, resolved...)
162 + }
163 +
164 + if len(outputAddrs) < 1 {
165 + return nil, fmt.Errorf("failed to specify addrs: %s", unspecAddrs)
166 + }
167 +
168 + log.Event(context.TODO(), "interfaceListenAddresses", func() eventlog.Loggable {
169 + var addrs []string
170 + for _, addr := range outputAddrs {
171 + addrs = append(addrs, addr.String())
172 + }
173 + return eventlog.Metadata{"addresses": addrs}
174 + }())
175 +
176 + log.Debug("ResolveUnspecifiedAddresses:", unspecAddrs, ifaceAddrs, outputAddrs)
177 + return outputAddrs, nil
178 +}
179 +
180 +// InterfaceAddresses returns a list of addresses associated with local machine
181 +// Note: we do not return link local addresses. IP loopback is ok, because we
182 +// may be connecting to other nodes in the same machine.
183 +func InterfaceAddresses() ([]ma.Multiaddr, error) {
184 + maddrs, err := manet.InterfaceMultiaddrs()
185 + if err != nil {
186 + return nil, err
187 + }
188 + log.Debug("InterfaceAddresses: from manet:", maddrs)
189 +
190 + var out []ma.Multiaddr
191 + for _, a := range maddrs {
192 + if !AddrUsable(a, true) { // partial
193 + // log.Debug("InterfaceAddresses: skipping unusable:", a)
194 + continue
195 + }
196 +
197 + out = append(out, a)
198 + }
199 +
200 + log.Debug("InterfaceAddresses: usable:", out)
201 + return out, nil
202 +}
203 +
204 +// AddrInList returns whether or not an address is part of a list.
205 +// this is useful to check if NAT is happening (or other bugs?)
206 +func AddrInList(addr ma.Multiaddr, list []ma.Multiaddr) bool {
207 + for _, addr2 := range list {
208 + if addr.Equal(addr2) {
209 + return true
210 + }
211 + }
212 + return false
213 +}
214 +
215 +// AddrIsShareableOnWAN returns whether the given address should be shareable on the
216 +// wide area network (wide internet).
217 +func AddrIsShareableOnWAN(addr ma.Multiaddr) bool {
218 + s := ma.Split(addr)
219 + if len(s) < 1 {
220 + return false
221 + }
222 + a := s[0]
223 + if manet.IsIPLoopback(a) || manet.IsIP6LinkLocal(a) || manet.IsIPUnspecified(a) {
224 + return false
225 + }
226 + return manet.IsThinWaist(a)
227 +}
228 +
229 +// WANShareableAddrs filters addresses based on whether they're shareable on WAN
230 +func WANShareableAddrs(inp []ma.Multiaddr) []ma.Multiaddr {
231 + out := make([]ma.Multiaddr, 0, len(inp))
232 + for _, a := range inp {
233 + if AddrIsShareableOnWAN(a) {
234 + out = append(out, a)
235 + }
236 + }
237 + return out
238 +}
239 +
240 +// CheckNATWarning checks if our observed addresses differ. if so,
241 +// informs the user that certain things might not work yet
242 +func CheckNATWarning(observed, expected ma.Multiaddr, listen []ma.Multiaddr) {
243 + if observed.Equal(expected) {
244 + return
245 + }
246 +
247 + if !AddrInList(observed, listen) { // probably a nat
248 + log.Warningf(natWarning, observed, listen)
249 + }
250 +}
251 +
252 +const natWarning = `Remote peer observed our address to be: %s
253 +The local addresses are: %s
254 +Thus, connection is going through NAT, and other connections may fail.
255 +
256 +IPFS NAT traversal is still under development. Please bug us on github or irc to fix this.
257 +Baby steps: http://jbenet.static.s3.amazonaws.com/271dfcf/baby-steps.gif
258 +`
p2p/net/swarm/addr/addr_test.go new
+198
@@ -0,0 +1,198 @@
1 +package addrutil
2 +
3 +import (
4 + "testing"
5 +
6 + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
7 + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
8 +)
9 +
10 +func newMultiaddr(t *testing.T, s string) ma.Multiaddr {
11 + maddr, err := ma.NewMultiaddr(s)
12 + if err != nil {
13 + t.Fatal(err)
14 + }
15 + return maddr
16 +}
17 +
18 +func TestFilterAddrs(t *testing.T) {
19 +
20 + bad := []ma.Multiaddr{
21 + newMultiaddr(t, "/ip4/1.2.3.4/udp/1234"), // unreliable
22 + newMultiaddr(t, "/ip4/1.2.3.4/udp/1234/sctp/1234"), // not in manet
23 + newMultiaddr(t, "/ip4/1.2.3.4/udp/1234/utp"), // utp is broken
24 + newMultiaddr(t, "/ip4/1.2.3.4/udp/1234/udt"), // udt is broken on arm
25 + newMultiaddr(t, "/ip6/fe80::1/tcp/1234"), // link local
26 + newMultiaddr(t, "/ip6/fe80::100/tcp/1234"), // link local
27 + }
28 +
29 + good := []ma.Multiaddr{
30 + newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234"),
31 + newMultiaddr(t, "/ip6/::1/tcp/1234"),
32 + }
33 +
34 + goodAndBad := append(good, bad...)
35 +
36 + // test filters
37 +
38 + for _, a := range bad {
39 + if AddrUsable(a, false) {
40 + t.Errorf("addr %s should be unusable", a)
41 + }
42 + if AddrUsable(a, true) {
43 + t.Errorf("addr %s should be unusable", a)
44 + }
45 + }
46 +
47 + for _, a := range good {
48 + if !AddrUsable(a, false) {
49 + t.Errorf("addr %s should be usable", a)
50 + }
51 + if !AddrUsable(a, true) {
52 + t.Errorf("addr %s should be usable", a)
53 + }
54 + }
55 +
56 + subtestAddrsEqual(t, FilterAddrs(bad), []ma.Multiaddr{})
57 + subtestAddrsEqual(t, FilterAddrs(good), good)
58 + subtestAddrsEqual(t, FilterAddrs(goodAndBad), good)
59 +}
60 +
61 +func subtestAddrsEqual(t *testing.T, a, b []ma.Multiaddr) {
62 + if len(a) != len(b) {
63 + t.Error(t)
64 + }
65 +
66 + in := func(addr ma.Multiaddr, l []ma.Multiaddr) bool {
67 + for _, addr2 := range l {
68 + if addr.Equal(addr2) {
69 + return true
70 + }
71 + }
72 + return false
73 + }
74 +
75 + for _, aa := range a {
76 + if !in(aa, b) {
77 + t.Errorf("%s not in %s", aa, b)
78 + }
79 + }
80 +}
81 +
82 +func TestInterfaceAddrs(t *testing.T) {
83 + addrs, err := InterfaceAddresses()
84 + if err != nil {
85 + t.Fatal(err)
86 + }
87 +
88 + if len(addrs) < 1 {
89 + t.Error("no addresses")
90 + }
91 +
92 + for _, a := range addrs {
93 + if manet.IsIP6LinkLocal(a) {
94 + t.Error("should not return ip link local addresses", a)
95 + }
96 + }
97 +
98 + if len(addrs) < 1 {
99 + t.Error("no good interface addrs")
100 + }
101 +}
102 +
103 +func TestResolvingAddrs(t *testing.T) {
104 +
105 + unspec := []ma.Multiaddr{
106 + newMultiaddr(t, "/ip4/0.0.0.0/tcp/1234"),
107 + newMultiaddr(t, "/ip4/1.2.3.4/tcp/1234"),
108 + newMultiaddr(t, "/ip6/::/tcp/1234"),
109 + newMultiaddr(t, "/ip6/::100/tcp/1234"),
110 + }
111 +
112 + iface := []ma.Multiaddr{
113 + newMultiaddr(t, "/ip4/127.0.0.1"),
114 + newMultiaddr(t, "/ip4/10.20.30.40"),
115 + newMultiaddr(t, "/ip6/::1"),
116 + newMultiaddr(t, "/ip6/::f"),
117 + }
118 +
119 + spec := []ma.Multiaddr{
120 + newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234"),
121 + newMultiaddr(t, "/ip4/10.20.30.40/tcp/1234"),
122 + newMultiaddr(t, "/ip4/1.2.3.4/tcp/1234"),
123 + newMultiaddr(t, "/ip6/::1/tcp/1234"),
124 + newMultiaddr(t, "/ip6/::f/tcp/1234"),
125 + newMultiaddr(t, "/ip6/::100/tcp/1234"),
126 + }
127 +
128 + actual, err := ResolveUnspecifiedAddresses(unspec, iface)
129 + if err != nil {
130 + t.Fatal(err)
131 + }
132 +
133 + for i, a := range actual {
134 + if !a.Equal(spec[i]) {
135 + t.Error(a, " != ", spec[i])
136 + }
137 + }
138 +
139 + ip4u := []ma.Multiaddr{newMultiaddr(t, "/ip4/0.0.0.0")}
140 + ip4i := []ma.Multiaddr{newMultiaddr(t, "/ip4/1.2.3.4")}
141 +
142 + ip6u := []ma.Multiaddr{newMultiaddr(t, "/ip6/::")}
143 + ip6i := []ma.Multiaddr{newMultiaddr(t, "/ip6/::1")}
144 +
145 + if _, err := ResolveUnspecifiedAddress(ip4u[0], ip6i); err == nil {
146 + t.Fatal("should have failed")
147 + }
148 + if _, err := ResolveUnspecifiedAddress(ip6u[0], ip4i); err == nil {
149 + t.Fatal("should have failed")
150 + }
151 +
152 + if _, err := ResolveUnspecifiedAddresses(ip6u, ip4i); err == nil {
153 + t.Fatal("should have failed")
154 + }
155 + if _, err := ResolveUnspecifiedAddresses(ip4u, ip6i); err == nil {
156 + t.Fatal("should have failed")
157 + }
158 +
159 +}
160 +
161 +func TestWANShareable(t *testing.T) {
162 +
163 + wanok := []ma.Multiaddr{
164 + newMultiaddr(t, "/ip4/1.2.3.4/tcp/1234"),
165 + newMultiaddr(t, "/ip6/abcd::1/tcp/1234"),
166 + }
167 +
168 + wanbad := []ma.Multiaddr{
169 + newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234"),
170 + newMultiaddr(t, "/ip4/0.0.0.0/tcp/1234"),
171 + newMultiaddr(t, "/ip6/::1/tcp/1234"),
172 + newMultiaddr(t, "/ip6/::/tcp/1234"),
173 + newMultiaddr(t, "/ip6/fe80::1/tcp/1234"),
174 + newMultiaddr(t, "/ip6/fe80::/tcp/1234"),
175 + }
176 +
177 + for _, a := range wanok {
178 + if !AddrIsShareableOnWAN(a) {
179 + t.Error("should be true", a)
180 + }
181 + }
182 +
183 + for _, a := range wanbad {
184 + if AddrIsShareableOnWAN(a) {
185 + t.Error("should be false", a)
186 + }
187 + }
188 +
189 + wanok2 := WANShareableAddrs(wanok)
190 + if len(wanok) != len(wanok2) {
191 + t.Error("should be the same")
192 + }
193 +
194 + wanbad2 := WANShareableAddrs(wanbad)
195 + if len(wanbad2) != 0 {
196 + t.Error("should be zero")
197 + }
198 +}
p2p/net/swarm/swarm.go
+11
@@ -3,7 +3,10 @@
3 package swarm
4
5 import (
6 + "fmt"
7 +
8 inet "github.com/jbenet/go-ipfs/p2p/net"
9 + addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
10 peer "github.com/jbenet/go-ipfs/p2p/peer"
11 eventlog "github.com/jbenet/go-ipfs/util/eventlog"
12
@@ -37,6 +40,14 @@ type Swarm struct {
40 func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr,
41 local peer.ID, peers peer.Peerstore) (*Swarm, error) {
42
43 + if len(listenAddrs) > 0 {
44 + filtered := addrutil.FilterAddrs(listenAddrs)
45 + if len(filtered) < 1 {
46 + return nil, fmt.Errorf("swarm cannot use any addr in: %s", listenAddrs)
47 + }
48 + listenAddrs = filtered
49 + }
50 +
51 s := &Swarm{
52 swarm: ps.NewSwarm(PSTransport),
53 local: local,
p2p/net/swarm/swarm_addr.go new
+39
@@ -0,0 +1,39 @@
1 +package swarm
2 +
3 +import (
4 + conn "github.com/jbenet/go-ipfs/p2p/net/conn"
5 + addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
6 +
7 + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
8 +)
9 +
10 +// ListenAddresses returns a list of addresses at which this swarm listens.
11 +func (s *Swarm) ListenAddresses() []ma.Multiaddr {
12 + listeners := s.swarm.Listeners()
13 + addrs := make([]ma.Multiaddr, 0, len(listeners))
14 + for _, l := range listeners {
15 + if l2, ok := l.NetListener().(conn.Listener); ok {
16 + addrs = append(addrs, l2.Multiaddr())
17 + }
18 + }
19 + return addrs
20 +}
21 +
22 +// InterfaceListenAddresses returns a list of addresses at which this swarm
23 +// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
24 +// use the known local interfaces.
25 +func InterfaceListenAddresses(s *Swarm) ([]ma.Multiaddr, error) {
26 + return addrutil.ResolveUnspecifiedAddresses(s.ListenAddresses(), nil)
27 +}
28 +
29 +// checkNATWarning checks if our observed addresses differ. if so,
30 +// informs the user that certain things might not work yet
31 +func checkNATWarning(s *Swarm, observed ma.Multiaddr, expected ma.Multiaddr) {
32 + listen, err := InterfaceListenAddresses(s)
33 + if err != nil {
34 + log.Errorf("Error retrieving swarm.InterfaceListenAddresses: %s", err)
35 + return
36 + }
37 +
38 + addrutil.CheckNATWarning(observed, expected, listen)
39 +}
p2p/net/swarm/swarm_addr_test.go renamed
+34 -5
@@ -3,6 +3,7 @@ package swarm
3 import (
4 "testing"
5
6 + addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
7 peer "github.com/jbenet/go-ipfs/p2p/peer"
8 testutil "github.com/jbenet/go-ipfs/util/testutil"
9
@@ -25,6 +26,8 @@ func TestFilterAddrs(t *testing.T) {
26 m("/ip4/1.2.3.4/udp/1234/sctp/1234"), // not in manet
27 m("/ip4/1.2.3.4/udp/1234/utp"), // utp is broken
28 m("/ip4/1.2.3.4/udp/1234/udt"), // udt is broken on arm
29 + m("/ip6/fe80::1/tcp/1234"), // link local
30 + m("/ip6/fe80::100/tcp/1234"), // link local
31 }
32
33 good := []ma.Multiaddr{
@@ -37,20 +40,20 @@ func TestFilterAddrs(t *testing.T) {
40 // test filters
41
42 for _, a := range bad {
40 - if AddrUsable(a) {
43 + if addrutil.AddrUsable(a, true) {
44 t.Errorf("addr %s should be unusable", a)
45 }
46 }
47
48 for _, a := range good {
46 - if !AddrUsable(a) {
49 + if !addrutil.AddrUsable(a, true) {
50 t.Errorf("addr %s should be usable", a)
51 }
52 }
53
51 - subtestAddrsEqual(t, FilterAddrs(bad), []ma.Multiaddr{})
52 - subtestAddrsEqual(t, FilterAddrs(good), good)
53 - subtestAddrsEqual(t, FilterAddrs(goodAndBad), good)
54 + subtestAddrsEqual(t, addrutil.FilterAddrs(bad), []ma.Multiaddr{})
55 + subtestAddrsEqual(t, addrutil.FilterAddrs(good), good)
56 + subtestAddrsEqual(t, addrutil.FilterAddrs(goodAndBad), good)
57
58 // now test it with swarm
59
@@ -95,3 +98,29 @@ func subtestAddrsEqual(t *testing.T, a, b []ma.Multiaddr) {
98 }
99 }
100 }
101 +
102 +func TestDialBadAddrs(t *testing.T) {
103 +
104 + m := func(s string) ma.Multiaddr {
105 + maddr, err := ma.NewMultiaddr(s)
106 + if err != nil {
107 + t.Fatal(err)
108 + }
109 + return maddr
110 + }
111 +
112 + ctx := context.Background()
113 + s := makeSwarms(ctx, t, 1)[0]
114 +
115 + test := func(a ma.Multiaddr) {
116 + p := testutil.RandPeerIDFatal(t)
117 + s.peers.AddAddress(p, a)
118 + if _, err := s.Dial(ctx, p); err == nil {
119 + t.Error("swarm should not dial: %s", m)
120 + }
121 + }
122 +
123 + test(m("/ip6/fe80::1")) // link local
124 + test(m("/ip6/fe80::100")) // link local
125 + test(m("/ip4/127.0.0.1/udp/1234/utp")) // utp
126 +}
p2p/net/swarm/swarm_dial.go
+6
@@ -5,6 +5,7 @@ import (
5 "fmt"
6
7 conn "github.com/jbenet/go-ipfs/p2p/net/conn"
8 + addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
9 peer "github.com/jbenet/go-ipfs/p2p/peer"
10 lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables"
11
@@ -38,6 +39,8 @@ func (s *Swarm) Dial(ctx context.Context, p peer.ID) (*Conn, error) {
39 }
40
41 remoteAddrs := s.peers.Addresses(p)
42 + // make sure we can use the addresses.
43 + remoteAddrs = addrutil.FilterAddrs(remoteAddrs)
44 if len(remoteAddrs) == 0 {
45 return nil, errors.New("peer has no addresses")
46 }
@@ -67,6 +70,9 @@ func (s *Swarm) Dial(ctx context.Context, p peer.ID) (*Conn, error) {
70 if err != nil {
71 return nil, err
72 }
73 + if connC == nil {
74 + err = fmt.Errorf("failed to dial %s", p)
75 + }
76
77 // ok try to setup the new connection.
78 swarmC, err := dialConnSetup(ctx, s, connC)
p2p/net/swarm/swarm_listen.go
+4 -1
@@ -4,6 +4,7 @@ import (
4 "fmt"
5
6 conn "github.com/jbenet/go-ipfs/p2p/net/conn"
7 + addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
8 lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables"
9
10 context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
@@ -16,7 +17,7 @@ import (
17 func (s *Swarm) listen(addrs []ma.Multiaddr) error {
18
19 for _, addr := range addrs {
19 - if !AddrUsable(addr) {
20 + if !addrutil.AddrUsable(addr, true) {
21 return fmt.Errorf("cannot use addr: %s", addr)
22 }
23 }
@@ -59,10 +60,12 @@ func (s *Swarm) setupListener(maddr ma.Multiaddr) error {
60 // may be fine for sk to be nil, just log a warning.
61 log.Warning("Listener not given PrivateKey, so WILL NOT SECURE conns.")
62 }
63 + log.Infof("Swarm Listening at %s", maddr)
64 list, err := conn.Listen(s.cg.Context(), maddr, s.local, sk)
65 if err != nil {
66 return err
67 }
68 + log.Infof("Swarm Listening at %s", s.ListenAddresses())
69
70 // AddListener to the peerstream Listener. this will begin accepting connections
71 // and streams!
routing/dht/dht.go
+14 -5
@@ -66,8 +66,13 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip
66 dht.peerstore = h.Peerstore()
67 dht.ContextGroup = ctxgroup.WithContext(ctx)
68 dht.host = h
69 - h.SetStreamHandler(ProtocolDHT, dht.handleNewStream)
69
70 + // sanity check. this should **never** happen
71 + if len(dht.peerstore.Addresses(dht.self)) < 1 {
72 + panic("attempt to initialize dht without addresses for self")
73 + }
74 +
75 + h.SetStreamHandler(ProtocolDHT, dht.handleNewStream)
76 dht.providers = NewProviderManager(dht.Context(), dht.self)
77 dht.AddChildGroup(dht.providers)
78
@@ -132,19 +137,23 @@ func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID,
137 // can provide the value of 'key'
138 func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, key string) error {
139
135 - pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(key), 0)
136 -
140 // add self as the provider
141 pi := dht.peerstore.PeerInfo(dht.self)
139 - pmes.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), []peer.PeerInfo{pi})
142 + // // only share WAN-friendly addresses ??
143 + // pi.Addrs = addrutil.WANShareableAddrs(pi.Addrs)
144 + if len(pi.Addrs) < 1 {
145 + log.Errorf("%s putProvider: %s for %s error: no wan-friendly addresses", dht.self, p, u.Key(key), pi.Addrs)
146 + return fmt.Errorf("no known addresses for self. cannot put provider.")
147 + }
148
149 + pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(key), 0)
150 + pmes.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), []peer.PeerInfo{pi})
151 err := dht.sendMessage(ctx, p, pmes)
152 if err != nil {
153 return err
154 }
155
156 log.Debugf("%s putProvider: %s for %s (%s)", dht.self, p, u.Key(key), pi.Addrs)
147 -
157 return nil
158 }
159