Browse Source

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

pull/2/head
Juan Batiz-Benet 10 years ago
parent
commit
63145110b6
  1. 33
      net/conn/dial.go
  2. 8
      net/conn/listen.go
  3. 193
      net/swarm/addr.go
  4. 258
      net/swarm/addr/addr.go
  5. 198
      net/swarm/addr/addr_test.go
  6. 11
      net/swarm/swarm.go
  7. 39
      net/swarm/swarm_addr.go
  8. 39
      net/swarm/swarm_addr_test.go
  9. 6
      net/swarm/swarm_dial.go
  10. 5
      net/swarm/swarm_listen.go

33
net/conn/dial.go

@ -22,7 +22,7 @@ func (d *Dialer) String() string {
// Example: d.DialAddr(ctx, peer.Addresses()[0], peer) // Example: d.DialAddr(ctx, peer.Addresses()[0], peer)
func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (Conn, error) { func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (Conn, error) {
network, _, err := manet.DialArgs(raddr) _, _, err := manet.DialArgs(raddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -31,17 +31,20 @@ func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (
return nil, debugerror.Errorf("Attempted to connect to zero address: %s", raddr) return nil, debugerror.Errorf("Attempted to connect to zero address: %s", raddr)
} }
var laddr ma.Multiaddr
if len(d.LocalAddrs) > 0 { if len(d.LocalAddrs) > 0 {
// laddr := MultiaddrNetMatch(raddr, d.LocalAddrs) laddrs := manet.AddrMatch(raddr, d.LocalAddrs)
laddr = NetAddress(network, d.LocalAddrs) if len(laddrs) < 1 {
if laddr == nil { return nil, debugerror.Errorf("No local address matches %s %s", raddr, d.LocalAddrs)
return nil, debugerror.Errorf("No local address for network %s", network)
} }
}
// TODO: try to get reusing addr/ports to work. // TODO pick with a good heuristic
// d.Dialer.LocalAddr = laddr // we use a random one for now to prevent bad addresses from making nodes unreachable
// with a random selection, multiple tries may work.
// laddr := laddrs[rand.Intn(len(laddrs))]
// TODO: try to get reusing addr/ports to work.
// d.Dialer.LocalAddr = laddr
}
log.Debugf("%s dialing %s %s", d.LocalPeer, remote, raddr) log.Debugf("%s dialing %s %s", d.LocalPeer, remote, raddr)
maconn, err := d.Dialer.Dial(raddr) maconn, err := d.Dialer.Dial(raddr)
@ -116,15 +119,3 @@ func MultiaddrNetMatch(tgt ma.Multiaddr, srcs []ma.Multiaddr) ma.Multiaddr {
} }
return nil return nil
} }
// NetAddress returns the first Multiaddr found for a given network.
func NetAddress(n string, addrs []ma.Multiaddr) ma.Multiaddr {
for _, a := range addrs {
for _, p := range a.Protocols() {
if p.Name == n {
return a
}
}
}
return nil
}

8
net/conn/listen.go

@ -102,11 +102,7 @@ func (l *listener) Addr() net.Addr {
// If there is an error converting from net.Addr to ma.Multiaddr, // If there is an error converting from net.Addr to ma.Multiaddr,
// the return value will be nil. // the return value will be nil.
func (l *listener) Multiaddr() ma.Multiaddr { func (l *listener) Multiaddr() ma.Multiaddr {
maddr, err := manet.FromNetAddr(l.Addr()) return l.Listener.Multiaddr()
if err != nil {
return nil // error
}
return maddr
} }
// LocalPeer is the identity of the local Peer. // 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
} }
l.cg.SetTeardown(l.teardown) l.cg.SetTeardown(l.teardown)
log.Infof("swarm listening on %s", l.Multiaddr()) log.Debugf("Conn Listener on %s", l.Multiaddr())
log.Event(ctx, "swarmListen", l) log.Event(ctx, "swarmListen", l)
return l, nil return l, nil
} }

193
net/swarm/addr.go

@ -1,193 +0,0 @@
package swarm
import (
conn "github.com/jbenet/go-ipfs/p2p/net/conn"
eventlog "github.com/jbenet/go-ipfs/util/eventlog"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
)
// SupportedTransportStrings is the list of supported transports for the swarm.
// These are strings of encapsulated multiaddr protocols. E.g.:
// /ip4/tcp
var SupportedTransportStrings = []string{
"/ip4/tcp",
"/ip6/tcp",
// "/ip4/udp/utp", disabled because the lib is broken
// "/ip6/udp/utp", disabled because the lib is broken
// "/ip4/udp/udt", disabled because the lib doesnt work on arm
// "/ip6/udp/udt", disabled because the lib doesnt work on arm
}
// SupportedTransportProtocols is the list of supported transports for the swarm.
// These are []ma.Protocol lists. Populated at runtime from SupportedTransportStrings
var SupportedTransportProtocols = [][]ma.Protocol{}
func init() {
// initialize SupportedTransportProtocols
transports := make([][]ma.Protocol, len(SupportedTransportStrings))
for _, s := range SupportedTransportStrings {
t, err := ma.ProtocolsWithString(s)
if err != nil {
panic(err) // important to fix this in the codebase
}
transports = append(transports, t)
}
SupportedTransportProtocols = transports
}
// FilterAddrs is a filter that removes certain addresses
// from a list. the addresses removed are those known NOT
// to work with swarm. Namely, addresses with UTP.
func FilterAddrs(a []ma.Multiaddr) []ma.Multiaddr {
b := make([]ma.Multiaddr, 0, len(a))
for _, addr := range a {
if AddrUsable(addr) {
b = append(b, addr)
}
}
return b
}
// AddrUsable returns whether the swarm can use this addr.
func AddrUsable(a ma.Multiaddr) bool {
// test the address protocol list is in SupportedTransportProtocols
matches := func(a, b []ma.Protocol) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i].Code != b[i].Code {
return false
}
}
return true
}
transport := a.Protocols()
for _, supported := range SupportedTransportProtocols {
if matches(supported, transport) {
return true
}
}
return false
}
// ListenAddresses returns a list of addresses at which this swarm listens.
func (s *Swarm) ListenAddresses() []ma.Multiaddr {
listeners := s.swarm.Listeners()
addrs := make([]ma.Multiaddr, 0, len(listeners))
for _, l := range listeners {
if l2, ok := l.NetListener().(conn.Listener); ok {
addrs = append(addrs, l2.Multiaddr())
}
}
return addrs
}
// InterfaceListenAddresses returns a list of addresses at which this swarm
// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces.
func InterfaceListenAddresses(s *Swarm) ([]ma.Multiaddr, error) {
return resolveUnspecifiedAddresses(s.ListenAddresses())
}
// resolveUnspecifiedAddresses expands unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces.
func resolveUnspecifiedAddresses(unspecifiedAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
var outputAddrs []ma.Multiaddr
// todo optimize: only fetch these if we have a "any" addr.
ifaceAddrs, err := interfaceAddresses()
if err != nil {
return nil, err
}
for _, a := range unspecifiedAddrs {
// split address into its components
split := ma.Split(a)
// if first component (ip) is not unspecified, use it as is.
if !manet.IsIPUnspecified(split[0]) {
outputAddrs = append(outputAddrs, a)
continue
}
// unspecified? add one address per interface.
for _, ia := range ifaceAddrs {
split[0] = ia
joined := ma.Join(split...)
outputAddrs = append(outputAddrs, joined)
}
}
log.Event(context.TODO(), "interfaceListenAddresses", func() eventlog.Loggable {
var addrs []string
for _, addr := range outputAddrs {
addrs = append(addrs, addr.String())
}
return eventlog.Metadata{"addresses": addrs}
}())
log.Debug("InterfaceListenAddresses:", outputAddrs)
return outputAddrs, nil
}
// interfaceAddresses returns a list of addresses associated with local machine
func interfaceAddresses() ([]ma.Multiaddr, error) {
maddrs, err := manet.InterfaceMultiaddrs()
if err != nil {
return nil, err
}
var nonLoopback []ma.Multiaddr
for _, a := range maddrs {
if !manet.IsIPLoopback(a) {
nonLoopback = append(nonLoopback, a)
}
}
return nonLoopback, nil
}
// addrInList returns whether or not an address is part of a list.
// this is useful to check if NAT is happening (or other bugs?)
func addrInList(addr ma.Multiaddr, list []ma.Multiaddr) bool {
for _, addr2 := range list {
if addr.Equal(addr2) {
return true
}
}
return false
}
// checkNATWarning checks if our observed addresses differ. if so,
// informs the user that certain things might not work yet
func checkNATWarning(s *Swarm, observed ma.Multiaddr, expected ma.Multiaddr) {
if observed.Equal(expected) {
return
}
listen, err := InterfaceListenAddresses(s)
if err != nil {
log.Errorf("Error retrieving swarm.InterfaceListenAddresses: %s", err)
return
}
if !addrInList(observed, listen) { // probably a nat
log.Warningf(natWarning, observed, listen)
}
}
const natWarning = `Remote peer observed our address to be: %s
The local addresses are: %s
Thus, connection is going through NAT, and other connections may fail.
IPFS NAT traversal is still under development. Please bug us on github or irc to fix this.
Baby steps: http://jbenet.static.s3.amazonaws.com/271dfcf/baby-steps.gif
`

258
net/swarm/addr/addr.go

@ -0,0 +1,258 @@
package addrutil
import (
"fmt"
eventlog "github.com/jbenet/go-ipfs/util/eventlog"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
)
var log = eventlog.Logger("p2p/net/swarm/addr")
// SupportedTransportStrings is the list of supported transports for the swarm.
// These are strings of encapsulated multiaddr protocols. E.g.:
// /ip4/tcp
var SupportedTransportStrings = []string{
"/ip4/tcp",
"/ip6/tcp",
// "/ip4/udp/utp", disabled because the lib is broken
// "/ip6/udp/utp", disabled because the lib is broken
// "/ip4/udp/udt", disabled because the lib doesnt work on arm
// "/ip6/udp/udt", disabled because the lib doesnt work on arm
}
// SupportedTransportProtocols is the list of supported transports for the swarm.
// These are []ma.Protocol lists. Populated at runtime from SupportedTransportStrings
var SupportedTransportProtocols = [][]ma.Protocol{}
func init() {
// initialize SupportedTransportProtocols
transports := make([][]ma.Protocol, len(SupportedTransportStrings))
for _, s := range SupportedTransportStrings {
t, err := ma.ProtocolsWithString(s)
if err != nil {
panic(err) // important to fix this in the codebase
}
transports = append(transports, t)
}
SupportedTransportProtocols = transports
}
// FilterAddrs is a filter that removes certain addresses
// from a list. the addresses removed are those known NOT
// to work with our network. Namely, addresses with UTP.
func FilterAddrs(a []ma.Multiaddr) []ma.Multiaddr {
b := make([]ma.Multiaddr, 0, len(a))
for _, addr := range a {
if AddrUsable(addr, false) {
b = append(b, addr)
}
}
return b
}
// AddrOverNonLocalIP returns whether the addr uses a non-local ip link
func AddrOverNonLocalIP(a ma.Multiaddr) bool {
split := ma.Split(a)
if len(split) < 1 {
return false
}
if manet.IsIP6LinkLocal(split[0]) {
return false
}
return true
}
// AddrUsable returns whether our network can use this addr.
// We only use the transports in SupportedTransportStrings,
// and we do not link local addresses. Loopback is ok
// as we need to be able to connect to multiple ipfs nodes
// in the same machine.
func AddrUsable(a ma.Multiaddr, partial bool) bool {
if !AddrOverNonLocalIP(a) {
return false
}
// test the address protocol list is in SupportedTransportProtocols
matches := func(supported, test []ma.Protocol) bool {
if len(test) > len(supported) {
return false
}
// when partial, it's ok if test < supported.
if !partial && len(supported) != len(test) {
return false
}
for i := range test {
if supported[i].Code != test[i].Code {
return false
}
}
return true
}
transport := a.Protocols()
for _, supported := range SupportedTransportProtocols {
if matches(supported, transport) {
return true
}
}
return false
}
// ResolveUnspecifiedAddress expands an unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces. If ifaceAddr is nil, we request interface addresses
// from the network stack. (this is so you can provide a cached value if resolving many addrs)
func ResolveUnspecifiedAddress(resolve ma.Multiaddr, ifaceAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
// split address into its components
split := ma.Split(resolve)
// if first component (ip) is not unspecified, use it as is.
if !manet.IsIPUnspecified(split[0]) {
return []ma.Multiaddr{resolve}, nil
}
out := make([]ma.Multiaddr, 0, len(ifaceAddrs))
for _, ia := range ifaceAddrs {
// must match the first protocol to be resolve.
if ia.Protocols()[0].Code != resolve.Protocols()[0].Code {
continue
}
split[0] = ia
joined := ma.Join(split...)
out = append(out, joined)
log.Debug("adding resolved addr:", resolve, joined, out)
}
if len(out) < 1 {
return nil, fmt.Errorf("failed to resolve: %s", resolve)
}
return out, nil
}
// ResolveUnspecifiedAddresses expands unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces.
func ResolveUnspecifiedAddresses(unspecAddrs, ifaceAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
// todo optimize: only fetch these if we have a "any" addr.
if len(ifaceAddrs) < 1 {
var err error
ifaceAddrs, err = InterfaceAddresses()
if err != nil {
return nil, err
}
// log.Debug("InterfaceAddresses:", ifaceAddrs)
}
var outputAddrs []ma.Multiaddr
for _, a := range unspecAddrs {
// unspecified?
resolved, err := ResolveUnspecifiedAddress(a, ifaceAddrs)
if err != nil {
continue // optimistic. if we cant resolve anything, we'll know at the bottom.
}
// log.Debug("resolved:", a, resolved)
outputAddrs = append(outputAddrs, resolved...)
}
if len(outputAddrs) < 1 {
return nil, fmt.Errorf("failed to specify addrs: %s", unspecAddrs)
}
log.Event(context.TODO(), "interfaceListenAddresses", func() eventlog.Loggable {
var addrs []string
for _, addr := range outputAddrs {
addrs = append(addrs, addr.String())
}
return eventlog.Metadata{"addresses": addrs}
}())
log.Debug("ResolveUnspecifiedAddresses:", unspecAddrs, ifaceAddrs, outputAddrs)
return outputAddrs, nil
}
// InterfaceAddresses returns a list of addresses associated with local machine
// Note: we do not return link local addresses. IP loopback is ok, because we
// may be connecting to other nodes in the same machine.
func InterfaceAddresses() ([]ma.Multiaddr, error) {
maddrs, err := manet.InterfaceMultiaddrs()
if err != nil {
return nil, err
}
log.Debug("InterfaceAddresses: from manet:", maddrs)
var out []ma.Multiaddr
for _, a := range maddrs {
if !AddrUsable(a, true) { // partial
// log.Debug("InterfaceAddresses: skipping unusable:", a)
continue
}
out = append(out, a)
}
log.Debug("InterfaceAddresses: usable:", out)
return out, nil
}
// AddrInList returns whether or not an address is part of a list.
// this is useful to check if NAT is happening (or other bugs?)
func AddrInList(addr ma.Multiaddr, list []ma.Multiaddr) bool {
for _, addr2 := range list {
if addr.Equal(addr2) {
return true
}
}
return false
}
// AddrIsShareableOnWAN returns whether the given address should be shareable on the
// wide area network (wide internet).
func AddrIsShareableOnWAN(addr ma.Multiaddr) bool {
s := ma.Split(addr)
if len(s) < 1 {
return false
}
a := s[0]
if manet.IsIPLoopback(a) || manet.IsIP6LinkLocal(a) || manet.IsIPUnspecified(a) {
return false
}
return manet.IsThinWaist(a)
}
// WANShareableAddrs filters addresses based on whether they're shareable on WAN
func WANShareableAddrs(inp []ma.Multiaddr) []ma.Multiaddr {
out := make([]ma.Multiaddr, 0, len(inp))
for _, a := range inp {
if AddrIsShareableOnWAN(a) {
out = append(out, a)
}
}
return out
}
// CheckNATWarning checks if our observed addresses differ. if so,
// informs the user that certain things might not work yet
func CheckNATWarning(observed, expected ma.Multiaddr, listen []ma.Multiaddr) {
if observed.Equal(expected) {
return
}
if !AddrInList(observed, listen) { // probably a nat
log.Warningf(natWarning, observed, listen)
}
}
const natWarning = `Remote peer observed our address to be: %s
The local addresses are: %s
Thus, connection is going through NAT, and other connections may fail.
IPFS NAT traversal is still under development. Please bug us on github or irc to fix this.
Baby steps: http://jbenet.static.s3.amazonaws.com/271dfcf/baby-steps.gif
`

198
net/swarm/addr/addr_test.go

@ -0,0 +1,198 @@
package addrutil
import (
"testing"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
)
func newMultiaddr(t *testing.T, s string) ma.Multiaddr {
maddr, err := ma.NewMultiaddr(s)
if err != nil {
t.Fatal(err)
}
return maddr
}
func TestFilterAddrs(t *testing.T) {
bad := []ma.Multiaddr{
newMultiaddr(t, "/ip4/1.2.3.4/udp/1234"), // unreliable
newMultiaddr(t, "/ip4/1.2.3.4/udp/1234/sctp/1234"), // not in manet
newMultiaddr(t, "/ip4/1.2.3.4/udp/1234/utp"), // utp is broken
newMultiaddr(t, "/ip4/1.2.3.4/udp/1234/udt"), // udt is broken on arm
newMultiaddr(t, "/ip6/fe80::1/tcp/1234"), // link local
newMultiaddr(t, "/ip6/fe80::100/tcp/1234"), // link local
}
good := []ma.Multiaddr{
newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234"),
newMultiaddr(t, "/ip6/::1/tcp/1234"),
}
goodAndBad := append(good, bad...)
// test filters
for _, a := range bad {
if AddrUsable(a, false) {
t.Errorf("addr %s should be unusable", a)
}
if AddrUsable(a, true) {
t.Errorf("addr %s should be unusable", a)
}
}
for _, a := range good {
if !AddrUsable(a, false) {
t.Errorf("addr %s should be usable", a)
}
if !AddrUsable(a, true) {
t.Errorf("addr %s should be usable", a)
}
}
subtestAddrsEqual(t, FilterAddrs(bad), []ma.Multiaddr{})
subtestAddrsEqual(t, FilterAddrs(good), good)
subtestAddrsEqual(t, FilterAddrs(goodAndBad), good)
}
func subtestAddrsEqual(t *testing.T, a, b []ma.Multiaddr) {
if len(a) != len(b) {
t.Error(t)
}
in := func(addr ma.Multiaddr, l []ma.Multiaddr) bool {
for _, addr2 := range l {
if addr.Equal(addr2) {
return true
}
}
return false
}
for _, aa := range a {
if !in(aa, b) {
t.Errorf("%s not in %s", aa, b)
}
}
}
func TestInterfaceAddrs(t *testing.T) {
addrs, err := InterfaceAddresses()
if err != nil {
t.Fatal(err)
}
if len(addrs) < 1 {
t.Error("no addresses")
}
for _, a := range addrs {
if manet.IsIP6LinkLocal(a) {
t.Error("should not return ip link local addresses", a)
}
}
if len(addrs) < 1 {
t.Error("no good interface addrs")
}
}
func TestResolvingAddrs(t *testing.T) {
unspec := []ma.Multiaddr{
newMultiaddr(t, "/ip4/0.0.0.0/tcp/1234"),
newMultiaddr(t, "/ip4/1.2.3.4/tcp/1234"),
newMultiaddr(t, "/ip6/::/tcp/1234"),
newMultiaddr(t, "/ip6/::100/tcp/1234"),
}
iface := []ma.Multiaddr{
newMultiaddr(t, "/ip4/127.0.0.1"),
newMultiaddr(t, "/ip4/10.20.30.40"),
newMultiaddr(t, "/ip6/::1"),
newMultiaddr(t, "/ip6/::f"),
}
spec := []ma.Multiaddr{
newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234"),
newMultiaddr(t, "/ip4/10.20.30.40/tcp/1234"),
newMultiaddr(t, "/ip4/1.2.3.4/tcp/1234"),
newMultiaddr(t, "/ip6/::1/tcp/1234"),
newMultiaddr(t, "/ip6/::f/tcp/1234"),
newMultiaddr(t, "/ip6/::100/tcp/1234"),
}
actual, err := ResolveUnspecifiedAddresses(unspec, iface)
if err != nil {
t.Fatal(err)
}
for i, a := range actual {
if !a.Equal(spec[i]) {
t.Error(a, " != ", spec[i])
}
}
ip4u := []ma.Multiaddr{newMultiaddr(t, "/ip4/0.0.0.0")}
ip4i := []ma.Multiaddr{newMultiaddr(t, "/ip4/1.2.3.4")}
ip6u := []ma.Multiaddr{newMultiaddr(t, "/ip6/::")}
ip6i := []ma.Multiaddr{newMultiaddr(t, "/ip6/::1")}
if _, err := ResolveUnspecifiedAddress(ip4u[0], ip6i); err == nil {
t.Fatal("should have failed")
}
if _, err := ResolveUnspecifiedAddress(ip6u[0], ip4i); err == nil {
t.Fatal("should have failed")
}
if _, err := ResolveUnspecifiedAddresses(ip6u, ip4i); err == nil {
t.Fatal("should have failed")
}
if _, err := ResolveUnspecifiedAddresses(ip4u, ip6i); err == nil {
t.Fatal("should have failed")
}
}
func TestWANShareable(t *testing.T) {
wanok := []ma.Multiaddr{
newMultiaddr(t, "/ip4/1.2.3.4/tcp/1234"),
newMultiaddr(t, "/ip6/abcd::1/tcp/1234"),
}
wanbad := []ma.Multiaddr{
newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234"),
newMultiaddr(t, "/ip4/0.0.0.0/tcp/1234"),
newMultiaddr(t, "/ip6/::1/tcp/1234"),
newMultiaddr(t, "/ip6/::/tcp/1234"),
newMultiaddr(t, "/ip6/fe80::1/tcp/1234"),
newMultiaddr(t, "/ip6/fe80::/tcp/1234"),
}
for _, a := range wanok {
if !AddrIsShareableOnWAN(a) {
t.Error("should be true", a)
}
}
for _, a := range wanbad {
if AddrIsShareableOnWAN(a) {
t.Error("should be false", a)
}
}
wanok2 := WANShareableAddrs(wanok)
if len(wanok) != len(wanok2) {
t.Error("should be the same")
}
wanbad2 := WANShareableAddrs(wanbad)
if len(wanbad2) != 0 {
t.Error("should be zero")
}
}

11
net/swarm/swarm.go

@ -3,7 +3,10 @@
package swarm package swarm
import ( import (
"fmt"
inet "github.com/jbenet/go-ipfs/p2p/net" inet "github.com/jbenet/go-ipfs/p2p/net"
addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
peer "github.com/jbenet/go-ipfs/p2p/peer" peer "github.com/jbenet/go-ipfs/p2p/peer"
eventlog "github.com/jbenet/go-ipfs/util/eventlog" eventlog "github.com/jbenet/go-ipfs/util/eventlog"
@ -37,6 +40,14 @@ type Swarm struct {
func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr, func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr,
local peer.ID, peers peer.Peerstore) (*Swarm, error) { local peer.ID, peers peer.Peerstore) (*Swarm, error) {
if len(listenAddrs) > 0 {
filtered := addrutil.FilterAddrs(listenAddrs)
if len(filtered) < 1 {
return nil, fmt.Errorf("swarm cannot use any addr in: %s", listenAddrs)
}
listenAddrs = filtered
}
s := &Swarm{ s := &Swarm{
swarm: ps.NewSwarm(PSTransport), swarm: ps.NewSwarm(PSTransport),
local: local, local: local,

39
net/swarm/swarm_addr.go

@ -0,0 +1,39 @@
package swarm
import (
conn "github.com/jbenet/go-ipfs/p2p/net/conn"
addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
)
// ListenAddresses returns a list of addresses at which this swarm listens.
func (s *Swarm) ListenAddresses() []ma.Multiaddr {
listeners := s.swarm.Listeners()
addrs := make([]ma.Multiaddr, 0, len(listeners))
for _, l := range listeners {
if l2, ok := l.NetListener().(conn.Listener); ok {
addrs = append(addrs, l2.Multiaddr())
}
}
return addrs
}
// InterfaceListenAddresses returns a list of addresses at which this swarm
// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces.
func InterfaceListenAddresses(s *Swarm) ([]ma.Multiaddr, error) {
return addrutil.ResolveUnspecifiedAddresses(s.ListenAddresses(), nil)
}
// checkNATWarning checks if our observed addresses differ. if so,
// informs the user that certain things might not work yet
func checkNATWarning(s *Swarm, observed ma.Multiaddr, expected ma.Multiaddr) {
listen, err := InterfaceListenAddresses(s)
if err != nil {
log.Errorf("Error retrieving swarm.InterfaceListenAddresses: %s", err)
return
}
addrutil.CheckNATWarning(observed, expected, listen)
}

39
net/swarm/addr_test.go → net/swarm/swarm_addr_test.go

@ -3,6 +3,7 @@ package swarm
import ( import (
"testing" "testing"
addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
peer "github.com/jbenet/go-ipfs/p2p/peer" peer "github.com/jbenet/go-ipfs/p2p/peer"
testutil "github.com/jbenet/go-ipfs/util/testutil" testutil "github.com/jbenet/go-ipfs/util/testutil"
@ -25,6 +26,8 @@ func TestFilterAddrs(t *testing.T) {
m("/ip4/1.2.3.4/udp/1234/sctp/1234"), // not in manet m("/ip4/1.2.3.4/udp/1234/sctp/1234"), // not in manet
m("/ip4/1.2.3.4/udp/1234/utp"), // utp is broken m("/ip4/1.2.3.4/udp/1234/utp"), // utp is broken
m("/ip4/1.2.3.4/udp/1234/udt"), // udt is broken on arm m("/ip4/1.2.3.4/udp/1234/udt"), // udt is broken on arm
m("/ip6/fe80::1/tcp/1234"), // link local
m("/ip6/fe80::100/tcp/1234"), // link local
} }
good := []ma.Multiaddr{ good := []ma.Multiaddr{
@ -37,20 +40,20 @@ func TestFilterAddrs(t *testing.T) {
// test filters // test filters
for _, a := range bad { for _, a := range bad {
if AddrUsable(a) { if addrutil.AddrUsable(a, true) {
t.Errorf("addr %s should be unusable", a) t.Errorf("addr %s should be unusable", a)
} }
} }
for _, a := range good { for _, a := range good {
if !AddrUsable(a) { if !addrutil.AddrUsable(a, true) {
t.Errorf("addr %s should be usable", a) t.Errorf("addr %s should be usable", a)
} }
} }
subtestAddrsEqual(t, FilterAddrs(bad), []ma.Multiaddr{}) subtestAddrsEqual(t, addrutil.FilterAddrs(bad), []ma.Multiaddr{})
subtestAddrsEqual(t, FilterAddrs(good), good) subtestAddrsEqual(t, addrutil.FilterAddrs(good), good)
subtestAddrsEqual(t, FilterAddrs(goodAndBad), good) subtestAddrsEqual(t, addrutil.FilterAddrs(goodAndBad), good)
// now test it with swarm // now test it with swarm
@ -95,3 +98,29 @@ func subtestAddrsEqual(t *testing.T, a, b []ma.Multiaddr) {
} }
} }
} }
func TestDialBadAddrs(t *testing.T) {
m := func(s string) ma.Multiaddr {
maddr, err := ma.NewMultiaddr(s)
if err != nil {
t.Fatal(err)
}
return maddr
}
ctx := context.Background()
s := makeSwarms(ctx, t, 1)[0]
test := func(a ma.Multiaddr) {
p := testutil.RandPeerIDFatal(t)
s.peers.AddAddress(p, a)
if _, err := s.Dial(ctx, p); err == nil {
t.Error("swarm should not dial: %s", m)
}
}
test(m("/ip6/fe80::1")) // link local
test(m("/ip6/fe80::100")) // link local
test(m("/ip4/127.0.0.1/udp/1234/utp")) // utp
}

6
net/swarm/swarm_dial.go

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
conn "github.com/jbenet/go-ipfs/p2p/net/conn" conn "github.com/jbenet/go-ipfs/p2p/net/conn"
addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
peer "github.com/jbenet/go-ipfs/p2p/peer" peer "github.com/jbenet/go-ipfs/p2p/peer"
lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables" lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables"
@ -38,6 +39,8 @@ func (s *Swarm) Dial(ctx context.Context, p peer.ID) (*Conn, error) {
} }
remoteAddrs := s.peers.Addresses(p) remoteAddrs := s.peers.Addresses(p)
// make sure we can use the addresses.
remoteAddrs = addrutil.FilterAddrs(remoteAddrs)
if len(remoteAddrs) == 0 { if len(remoteAddrs) == 0 {
return nil, errors.New("peer has no addresses") return nil, errors.New("peer has no addresses")
} }
@ -67,6 +70,9 @@ func (s *Swarm) Dial(ctx context.Context, p peer.ID) (*Conn, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if connC == nil {
err = fmt.Errorf("failed to dial %s", p)
}
// ok try to setup the new connection. // ok try to setup the new connection.
swarmC, err := dialConnSetup(ctx, s, connC) swarmC, err := dialConnSetup(ctx, s, connC)

5
net/swarm/swarm_listen.go

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
conn "github.com/jbenet/go-ipfs/p2p/net/conn" conn "github.com/jbenet/go-ipfs/p2p/net/conn"
addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables" lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
@ -16,7 +17,7 @@ import (
func (s *Swarm) listen(addrs []ma.Multiaddr) error { func (s *Swarm) listen(addrs []ma.Multiaddr) error {
for _, addr := range addrs { for _, addr := range addrs {
if !AddrUsable(addr) { if !addrutil.AddrUsable(addr, true) {
return fmt.Errorf("cannot use addr: %s", addr) return fmt.Errorf("cannot use addr: %s", addr)
} }
} }
@ -59,10 +60,12 @@ func (s *Swarm) setupListener(maddr ma.Multiaddr) error {
// may be fine for sk to be nil, just log a warning. // may be fine for sk to be nil, just log a warning.
log.Warning("Listener not given PrivateKey, so WILL NOT SECURE conns.") log.Warning("Listener not given PrivateKey, so WILL NOT SECURE conns.")
} }
log.Infof("Swarm Listening at %s", maddr)
list, err := conn.Listen(s.cg.Context(), maddr, s.local, sk) list, err := conn.Listen(s.cg.Context(), maddr, s.local, sk)
if err != nil { if err != nil {
return err return err
} }
log.Infof("Swarm Listening at %s", s.ListenAddresses())
// AddListener to the peerstream Listener. this will begin accepting connections // AddListener to the peerstream Listener. this will begin accepting connections
// and streams! // and streams!

Loading…
Cancel
Save