diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index ce56dbf78..8960a4c94 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,6 @@ { "ImportPath": "github.com/jbenet/go-ipfs", - "GoVersion": "go1.3.3", + "GoVersion": "go1.3", "Packages": [ "./..." ], @@ -102,8 +102,8 @@ }, { "ImportPath": "github.com/jbenet/go-multiaddr", - "Comment": "0.1.2-17-g68a2067", - "Rev": "68a20675cb0829da219def0d90afe17a7219e8c7" + "Comment": "0.1.2-27-g62a88e0", + "Rev": "62a88e015e1bf5d6aaca29aec1aba0722f21c8d3" }, { "ImportPath": "github.com/jbenet/go-multihash", diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/README.md b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/README.md index 7ff9854f1..f2545485d 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/README.md +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/README.md @@ -34,16 +34,6 @@ addr.Protocols() // } ``` -### Other formats - -```go -// handles the stupid url version too -m = ma.NewUrl("udp4://127.0.0.1:1234") -// -m.Url(buf) -// udp4://127.0.0.1:1234 -``` - ### En/decapsulate ```go diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go index a6cf9911c..d08544b46 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go @@ -67,6 +67,30 @@ func bytesToString(b []byte) (ret string, err error) { return s, nil } +func bytesSplit(b []byte) (ret [][]byte, err error) { + // panic handler, in case we try accessing bytes incorrectly. + defer func() { + if e := recover(); e != nil { + ret = [][]byte{} + err = e.(error) + } + }() + + ret = [][]byte{} + for len(b) > 0 { + p := ProtocolWithCode(int(b[0])) + if p == nil { + return [][]byte{}, fmt.Errorf("no protocol with code %d", b[0]) + } + + length := 1 + (p.Size / 8) + ret = append(ret, b[:length]) + b = b[length:] + } + + return ret, nil +} + func addressStringToBytes(p *Protocol, s string) []byte { switch p.Code { diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go index 4ee63ca43..a2bea09ad 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go @@ -84,10 +84,10 @@ func (m *multiaddr) Encapsulate(o Multiaddr) Multiaddr { mb := m.bytes ob := o.Bytes() - var b bytes.Buffer - b.Write(mb) - b.Write(ob) - return &multiaddr{bytes: b.Bytes()} + b := make([]byte, len(mb)+len(ob)) + copy(b, mb) + copy(b[len(mb):], ob) + return &multiaddr{bytes: b} } // Decapsulate unwraps Multiaddr up until the given Multiaddr is found. diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr/multiaddr b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr/multiaddr new file mode 100644 index 000000000..c54b3bf08 Binary files /dev/null and b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr/multiaddr differ diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr/multiaddr.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr/multiaddr.go new file mode 100644 index 000000000..441f607a6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr/multiaddr.go @@ -0,0 +1,96 @@ +package main + +import ( + "encoding/hex" + "flag" + "fmt" + "os" + + 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" +) + +// flags +var formats = []string{"string", "bytes", "hex", "slice"} +var format string +var hideLoopback bool + +func init() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "usage: %s []\n\nFlags:\n", os.Args[0]) + flag.PrintDefaults() + } + + usage := fmt.Sprintf("output format, one of: %v", formats) + flag.StringVar(&format, "format", "string", usage) + flag.StringVar(&format, "f", "string", usage+" (shorthand)") + flag.BoolVar(&hideLoopback, "hide-loopback", false, "do not display loopback addresses") +} + +func main() { + flag.Parse() + args := flag.Args() + if len(args) == 0 { + output(localAddresses()...) + } else { + output(address(args[0])) + } +} + +func localAddresses() []ma.Multiaddr { + maddrs, err := manet.InterfaceMultiaddrs() + if err != nil { + die(err) + } + + if !hideLoopback { + return maddrs + } + + var maddrs2 []ma.Multiaddr + for _, a := range maddrs { + if !manet.IsIPLoopback(a) { + maddrs2 = append(maddrs2, a) + } + } + + return maddrs2 +} + +func address(addr string) ma.Multiaddr { + m, err := ma.NewMultiaddr(addr) + if err != nil { + die(err) + } + + return m +} + +func output(ms ...ma.Multiaddr) { + for _, m := range ms { + fmt.Println(outfmt(m)) + } +} + +func outfmt(m ma.Multiaddr) string { + switch format { + case "string": + return m.String() + case "slice": + return fmt.Sprintf("%v", m.Bytes()) + case "bytes": + return string(m.Bytes()) + case "hex": + return "0x" + hex.EncodeToString(m.Bytes()) + } + + die("error: invalid format", format) + return "" +} + +func die(v ...interface{}) { + fmt.Fprint(os.Stderr, v...) + fmt.Fprint(os.Stderr, "\n") + flag.Usage() + os.Exit(-1) +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go index 3a7820400..a5cb666a0 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go @@ -91,6 +91,49 @@ func TestBytesToString(t *testing.T) { testString("/ip4/127.0.0.1/udp/1234", "047f0000011104d2") } +func TestBytesSplitAndJoin(t *testing.T) { + + testString := func(s string, res []string) { + m, err := NewMultiaddr(s) + if err != nil { + t.Error("failed to convert", s) + } + + split := Split(m) + if len(split) != len(res) { + t.Error("not enough split components", split) + return + } + + for i, a := range split { + if a.String() != res[i] { + t.Errorf("split component failed: %s != %s", a, res[i]) + } + } + + joined := Join(split...) + if !m.Equal(joined) { + t.Errorf("joined components failed: %s != %s", m, joined) + } + + // modifying underlying bytes is fine. + m2 := m.(*multiaddr) + for i := range m2.bytes { + m2.bytes[i] = 0 + } + + for i, a := range split { + if a.String() != res[i] { + t.Errorf("split component failed: %s != %s", a, res[i]) + } + } + } + + testString("/ip4/1.2.3.4/udp/1234", []string{"/ip4/1.2.3.4", "/udp/1234"}) + testString("/ip4/1.2.3.4/tcp/1/ip4/2.3.4.5/udp/2", + []string{"/ip4/1.2.3.4", "/tcp/1", "/ip4/2.3.4.5", "/udp/2"}) +} + func TestProtocols(t *testing.T) { m, err := NewMultiaddr("/ip4/127.0.0.1/udp/1234") if err != nil { diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert.go index d7749d89d..c28159f84 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert.go @@ -1,4 +1,4 @@ -package net +package manet import ( "fmt" @@ -62,6 +62,13 @@ func FromNetAddr(a net.Addr) (ma.Multiaddr, error) { } return FromIP(ac.IP) + case "ip+net": + ac, ok := a.(*net.IPNet) + if !ok { + return nil, errIncorrectNetAddr + } + return FromIP(ac.IP) + default: return nil, fmt.Errorf("unknown network %v", a.Network()) } @@ -123,30 +130,3 @@ func DialArgs(m ma.Multiaddr) (string, string, error) { } return network, host, nil } - -// IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols. -// This means: /{IP4, IP6}[/{TCP, UDP}] -func IsThinWaist(m ma.Multiaddr) bool { - p := m.Protocols() - - // nothing? not even a waist. - if len(p) == 0 { - return false - } - - if p[0].Code != ma.P_IP4 && p[0].Code != ma.P_IP6 { - return false - } - - // only IP? still counts. - if len(p) == 1 { - return true - } - - switch p[1].Code { - case ma.P_TCP, ma.P_UDP, ma.P_IP4, ma.P_IP6: - return true - default: - return false - } -} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert_test.go index 7dc8e50a3..47b25e7b9 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert_test.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert_test.go @@ -1,4 +1,4 @@ -package net +package manet import ( "net" diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/doc.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/doc.go index d73b8652f..040ad3f02 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/doc.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/doc.go @@ -1,5 +1,5 @@ -// Package net provides Multiaddr specific versions of common +// Package manet provides Multiaddr specific versions of common // functions in stdlib's net package. This means wrappers of // standard net symbols like net.Dial and net.Listen, as well // as conversion to/from net.Addr. -package net +package manet diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/ip.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/ip.go new file mode 100644 index 000000000..57d2e38d6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/ip.go @@ -0,0 +1,76 @@ +package manet + +import ( + "bytes" + + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" +) + +// Loopback Addresses +var ( + // IP4Loopback is the ip4 loopback multiaddr + IP4Loopback = ma.StringCast("/ip4/127.0.0.1") + + // IP6Loopback is the ip6 loopback multiaddr + IP6Loopback = ma.StringCast("/ip6/::1") + + // IP6LinkLocalLoopback is the ip6 link-local loopback multiaddr + IP6LinkLocalLoopback = ma.StringCast("/ip6/fe80::1") +) + +// Unspecified Addresses (used for ) +var ( + IP4Unspecified = ma.StringCast("/ip4/0.0.0.0") + IP6Unspecified = ma.StringCast("/ip6/::") +) + +// IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols. +// This means: /{IP4, IP6}[/{TCP, UDP}] +func IsThinWaist(m ma.Multiaddr) bool { + p := m.Protocols() + + // nothing? not even a waist. + if len(p) == 0 { + return false + } + + if p[0].Code != ma.P_IP4 && p[0].Code != ma.P_IP6 { + return false + } + + // only IP? still counts. + if len(p) == 1 { + return true + } + + switch p[1].Code { + case ma.P_TCP, ma.P_UDP, ma.P_IP4, ma.P_IP6: + return true + default: + return false + } +} + +// IsIPLoopback returns whether a Multiaddr is a "Loopback" IP address +// This means either /ip4/127.0.0.1 or /ip6/::1 +func IsIPLoopback(m ma.Multiaddr) bool { + b := m.Bytes() + + // /ip4/127 prefix (_entire_ /8 is loopback...) + if bytes.HasPrefix(b, []byte{4, 127}) { + return true + } + + // /ip6/::1 + if IP6Loopback.Equal(m) || IP6LinkLocalLoopback.Equal(m) { + return true + } + + return false +} + +// IsIPUnspecified returns whether a Multiaddr is am Unspecified IP address +// This means either /ip4/0.0.0.0 or /ip6/:: +func IsIPUnspecified(m ma.Multiaddr) bool { + return IP4Unspecified.Equal(m) || IP6Unspecified.Equal(m) +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net.go index 3cb41622f..a4eb08938 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net.go @@ -1,4 +1,4 @@ -package net +package manet import ( "fmt" @@ -216,3 +216,20 @@ func Listen(laddr ma.Multiaddr) (Listener, error) { laddr: laddr, }, nil } + +// InterfaceMultiaddrs will return the addresses matching net.InterfaceAddrs +func InterfaceMultiaddrs() ([]ma.Multiaddr, error) { + addrs, err := net.InterfaceAddrs() + if err != nil { + return nil, err + } + + maddrs := make([]ma.Multiaddr, len(addrs)) + for i, a := range addrs { + maddrs[i], err = FromNetAddr(a) + if err != nil { + return nil, err + } + } + return maddrs, nil +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net_test.go index 316a57afa..46fd7aed4 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net_test.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net_test.go @@ -1,4 +1,4 @@ -package net +package manet import ( "bytes" @@ -198,3 +198,47 @@ func TestListenAndDial(t *testing.T) { cA.Close() wg.Wait() } + +func TestIPLoopback(t *testing.T) { + if IP4Loopback.String() != "/ip4/127.0.0.1" { + t.Error("IP4Loopback incorrect:", IP4Loopback) + } + + if IP6Loopback.String() != "/ip6/::1" { + t.Error("IP6Loopback incorrect:", IP6Loopback) + } + + if IP6LinkLocalLoopback.String() != "/ip6/fe80::1" { + t.Error("IP6LinkLocalLoopback incorrect:", IP6Loopback) + } + + if !IsIPLoopback(IP4Loopback) { + t.Error("IsIPLoopback failed (IP4Loopback)") + } + + if !IsIPLoopback(IP6Loopback) { + t.Error("IsIPLoopback failed (IP6Loopback)") + } + + if !IsIPLoopback(IP6LinkLocalLoopback) { + t.Error("IsIPLoopback failed (IP6LinkLocalLoopback)") + } +} + +func TestIPUnspecified(t *testing.T) { + if IP4Unspecified.String() != "/ip4/0.0.0.0" { + t.Error("IP4Unspecified incorrect:", IP4Unspecified) + } + + if IP6Unspecified.String() != "/ip6/::" { + t.Error("IP6Unspecified incorrect:", IP6Unspecified) + } + + if !IsIPUnspecified(IP4Unspecified) { + t.Error("IsIPUnspecified failed (IP4Unspecified)") + } + + if !IsIPUnspecified(IP6Unspecified) { + t.Error("IsIPUnspecified failed (IP6Unspecified)") + } +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/util.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/util.go new file mode 100644 index 000000000..d1b54afbb --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/util.go @@ -0,0 +1,56 @@ +package multiaddr + +import "fmt" + +// Split returns the sub-address portions of a multiaddr. +func Split(m Multiaddr) []Multiaddr { + split, err := bytesSplit(m.Bytes()) + if err != nil { + panic(fmt.Errorf("invalid multiaddr %s", m.String())) + } + + addrs := make([]Multiaddr, len(split)) + for i, addr := range split { + addrs[i] = &multiaddr{bytes: addr} + } + return addrs +} + +// Join returns a combination of addresses. +func Join(ms ...Multiaddr) Multiaddr { + + length := 0 + bs := make([][]byte, len(ms)) + for i, m := range ms { + bs[i] = m.Bytes() + length += len(bs[i]) + } + + bidx := 0 + b := make([]byte, length) + for _, mb := range bs { + for i := range mb { + b[bidx] = mb[i] + bidx++ + } + } + return &multiaddr{bytes: b} +} + +// Cast re-casts a byte slice as a multiaddr. will panic if it fails to parse. +func Cast(b []byte) Multiaddr { + _, err := bytesToString(b) + if err != nil { + panic(fmt.Errorf("multiaddr failed to parse: %s", err)) + } + return &multiaddr{bytes: b} +} + +// StringCast like Cast, but parses a string. Will also panic if it fails to parse. +func StringCast(s string) Multiaddr { + m, err := NewMultiaddr(s) + if err != nil { + panic(fmt.Errorf("multiaddr failed to parse: %s", err)) + } + return m +} diff --git a/core/core.go b/core/core.go index 543c71db0..5ceacab92 100644 --- a/core/core.go +++ b/core/core.go @@ -123,7 +123,12 @@ func NewIpfsNode(cfg *config.Config, online bool) (n *IpfsNode, err error) { } // setup the network - n.Network, err = inet.NewIpfsNetwork(ctx, n.Identity, n.Peerstore, muxMap) + listenAddrs, err := listenAddresses(cfg) + if err != nil { + return nil, err + } + + n.Network, err = inet.NewIpfsNetwork(ctx, listenAddrs, n.Identity, n.Peerstore, muxMap) if err != nil { return nil, err } @@ -183,16 +188,6 @@ func initIdentity(cfg *config.Config, peers peer.Peerstore, online bool) (peer.P return nil, err } - // address is optional - if len(cfg.Addresses.Swarm) > 0 { - maddr, err := ma.NewMultiaddr(cfg.Addresses.Swarm) - if err != nil { - return nil, err - } - - peer.AddAddress(maddr) - } - // when not online, don't need to parse private keys (yet) if online { skb, err := base64.StdEncoding.DecodeString(cfg.Identity.PrivKey) @@ -233,3 +228,18 @@ func initConnections(ctx context.Context, cfg *config.Config, pstore peer.Peerst } } } + +func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) { + var listen []ma.Multiaddr + + if len(cfg.Addresses.Swarm) > 0 { + maddr, err := ma.NewMultiaddr(cfg.Addresses.Swarm) + if err != nil { + return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm) + } + + listen = append(listen, maddr) + } + + return listen, nil +} diff --git a/net/conn/handshake.go b/net/conn/handshake.go index caabf9dce..496c23c40 100644 --- a/net/conn/handshake.go +++ b/net/conn/handshake.go @@ -3,15 +3,12 @@ package conn import ( "errors" "fmt" - "strings" handshake "github.com/jbenet/go-ipfs/net/handshake" hspb "github.com/jbenet/go-ipfs/net/handshake/pb" - u "github.com/jbenet/go-ipfs/util" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ) // Handshake1 exchanges local and remote versions and compares them @@ -62,87 +59,48 @@ func Handshake1(ctx context.Context, c Conn) error { } // Handshake3 exchanges local and remote service information -func Handshake3(ctx context.Context, c Conn) error { +func Handshake3(ctx context.Context, c Conn) (*handshake.Handshake3Result, error) { rpeer := c.RemotePeer() lpeer := c.LocalPeer() + // setup + send the message to remote var remoteH, localH *hspb.Handshake3 - localH = handshake.Handshake3Msg(lpeer) - - rma := c.RemoteMultiaddr() - localH.ObservedAddr = proto.String(rma.String()) - + localH = handshake.Handshake3Msg(lpeer, c.RemoteMultiaddr()) localB, err := proto.Marshal(localH) if err != nil { - return err + return nil, err } c.Out() <- localB log.Debugf("Handshake1: sent to %s", rpeer) + // wait + listen for response select { case <-ctx.Done(): - return ctx.Err() + return nil, ctx.Err() case <-c.Closing(): - return errors.New("Handshake3: error remote connection closed") + return nil, errors.New("Handshake3: error remote connection closed") case remoteB, ok := <-c.In(): if !ok { - return fmt.Errorf("Handshake3 error receiving from conn: %v", rpeer) + return nil, fmt.Errorf("Handshake3 error receiving from conn: %v", rpeer) } remoteH = new(hspb.Handshake3) err = proto.Unmarshal(remoteB, remoteH) if err != nil { - return fmt.Errorf("Handshake3 could not decode remote msg: %q", err) + return nil, fmt.Errorf("Handshake3 could not decode remote msg: %q", err) } log.Debugf("Handshake3 received from %s", rpeer) } - if err := handshake.Handshake3UpdatePeer(rpeer, remoteH); err != nil { + // actually update our state based on the new knowledge + res, err := handshake.Handshake3Update(lpeer, rpeer, remoteH) + if err != nil { log.Errorf("Handshake3 failed to update %s", rpeer) - return err } - - // If we are behind a NAT, inform the user that certain things might not work yet - nat, err := checkNAT(remoteH.GetObservedAddr()) - if err != nil { - log.Errorf("Error in NAT detection: %s", err) - } - if nat { - msg := `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 - ` - addrs, _ := u.GetLocalAddresses() - log.Warning(fmt.Sprintf(msg, remoteH.GetObservedAddr(), addrs)) - } - - return nil -} - -// checkNAT returns whether or not we might be behind a NAT -func checkNAT(observedaddr string) (bool, error) { - observedma, err := ma.NewMultiaddr(observedaddr) - if err != nil { - return false, err - } - addrs, err := u.GetLocalAddresses() - if err != nil { - return false, err - } - - omastr := observedma.String() - for _, addr := range addrs { - if strings.HasPrefix(omastr, addr.String()) { - return false, nil - } - } - - return true, nil + res.RemoteObservedAddress = c.RemoteMultiaddr() + return res, nil } diff --git a/net/handshake/handshake3.go b/net/handshake/handshake3.go index fa7126754..406728dd2 100644 --- a/net/handshake/handshake3.go +++ b/net/handshake/handshake3.go @@ -13,18 +13,21 @@ import ( var log = u.Logger("handshake") // Handshake3Msg constructs a Handshake3 msg. -func Handshake3Msg(localPeer peer.Peer) *pb.Handshake3 { +func Handshake3Msg(localPeer peer.Peer, remoteAddr ma.Multiaddr) *pb.Handshake3 { var msg pb.Handshake3 // don't need publicKey after secure channel. // msg.PublicKey = localPeer.PubKey().Bytes() - // addresses + // local listen addresses addrs := localPeer.Addresses() msg.ListenAddrs = make([][]byte, len(addrs)) for i, a := range addrs { msg.ListenAddrs[i] = a.Bytes() } + // observed remote address + msg.ObservedAddr = remoteAddr.Bytes() + // services // srv := localPeer.Services() // msg.Services = make([]mux.ProtocolID, len(srv)) @@ -35,20 +38,45 @@ func Handshake3Msg(localPeer peer.Peer) *pb.Handshake3 { return &msg } -// Handshake3UpdatePeer updates a remote peer with the information in the -// handshake3 msg we received from them. -func Handshake3UpdatePeer(remotePeer peer.Peer, msg *pb.Handshake3) error { +// Handshake3Update updates local knowledge with the information in the +// handshake3 msg we received from remote client. +func Handshake3Update(lpeer, rpeer peer.Peer, msg *pb.Handshake3) (*Handshake3Result, error) { + res := &Handshake3Result{} - // addresses + // our observed address + observedAddr, err := ma.NewMultiaddrBytes(msg.GetObservedAddr()) + if err != nil { + return res, err + } + if lpeer.AddAddress(observedAddr) { + log.Infof("(nat) added new local, remote-observed address: %s", observedAddr) + } + res.LocalObservedAddress = observedAddr + + // remote's reported addresses for _, a := range msg.GetListenAddrs() { addr, err := ma.NewMultiaddrBytes(a) if err != nil { err = fmt.Errorf("remote peer address not a multiaddr: %s", err) - log.Errorf("Handshake3: error %s", err) - return err + log.Errorf("Handshake3 error %s", err) + return res, err } - remotePeer.AddAddress(addr) + rpeer.AddAddress(addr) + res.RemoteListenAddresses = append(res.RemoteListenAddresses, addr) } - return nil + return res, nil +} + +// Handshake3Result collects the knowledge gained in Handshake3. +type Handshake3Result struct { + + // The addresses reported by the remote client + RemoteListenAddresses []ma.Multiaddr + + // The address of the remote client we observed in this connection + RemoteObservedAddress ma.Multiaddr + + // The address the remote client observed from this connection + LocalObservedAddress ma.Multiaddr } diff --git a/net/handshake/pb/handshake.pb.go b/net/handshake/pb/handshake.pb.go index 8a0a31efe..77ed5a41e 100644 --- a/net/handshake/pb/handshake.pb.go +++ b/net/handshake/pb/handshake.pb.go @@ -15,10 +15,12 @@ It has these top-level messages: package handshake_pb import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import json "encoding/json" import math "math" -// Reference imports to suppress errors if they are not otherwise used. +// Reference proto, json, and math imports to suppress error if they are not otherwise used. var _ = proto.Marshal +var _ = &json.SyntaxError{} var _ = math.Inf // Handshake1 is delivered _before_ the secure channel is initialized @@ -51,11 +53,13 @@ func (m *Handshake1) GetAgentVersion() string { // Handshake3 is delivered _after_ the secure channel is initialized type Handshake3 struct { - // listenAddrs are the multiaddrs this node listens for open connections on + // listenAddrs are the multiaddrs the sender node listens for open connections on ListenAddrs [][]byte `protobuf:"bytes,2,rep,name=listenAddrs" json:"listenAddrs,omitempty"` - // we'll have more fields here later. - ObservedAddr *string `protobuf:"bytes,4,opt,name=observedAddr" json:"observedAddr,omitempty"` - XXX_unrecognized []byte `json:"-"` + // oservedAddr is the multiaddr of the remote endpoint that the sender node perceives + // this is useful information to convey to the other side, as it helps the remote endpoint + // determine whether its connection to the local peer goes through NAT. + ObservedAddr []byte `protobuf:"bytes,4,opt,name=observedAddr" json:"observedAddr,omitempty"` + XXX_unrecognized []byte `json:"-"` } func (m *Handshake3) Reset() { *m = Handshake3{} } @@ -69,11 +73,11 @@ func (m *Handshake3) GetListenAddrs() [][]byte { return nil } -func (m *Handshake3) GetObservedAddr() string { - if m != nil && m.ObservedAddr != nil { - return *m.ObservedAddr +func (m *Handshake3) GetObservedAddr() []byte { + if m != nil { + return m.ObservedAddr } - return "" + return nil } func init() { diff --git a/net/handshake/pb/handshake.proto b/net/handshake/pb/handshake.proto index 789c9dbbb..1dc7cad93 100644 --- a/net/handshake/pb/handshake.proto +++ b/net/handshake/pb/handshake.proto @@ -22,7 +22,7 @@ message Handshake3 { // - then again, if we change / disable secure channel, may still want it. // optional bytes publicKey = 1; - // listenAddrs are the multiaddrs this node listens for open connections on + // listenAddrs are the multiaddrs the sender node listens for open connections on repeated bytes listenAddrs = 2; // TODO @@ -31,8 +31,8 @@ message Handshake3 { // we'll have more fields here later. - // oservedAddr is the multiaddr of the remote endpoint that the local node perceives + // oservedAddr is the multiaddr of the remote endpoint that the sender node perceives // this is useful information to convey to the other side, as it helps the remote endpoint // determine whether its connection to the local peer goes through NAT. - optional string observedAddr = 4; + optional bytes observedAddr = 4; } diff --git a/net/interface.go b/net/interface.go index 11918231d..84afd7258 100644 --- a/net/interface.go +++ b/net/interface.go @@ -6,6 +6,8 @@ import ( srv "github.com/jbenet/go-ipfs/net/service" peer "github.com/jbenet/go-ipfs/peer" ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" + + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ) // Network is the interface IPFS uses for connecting to the world. @@ -37,6 +39,14 @@ type Network interface { // SendMessage sends given Message out SendMessage(msg.NetMessage) error + + // ListenAddresses returns a list of addresses at which this network listens. + ListenAddresses() []ma.Multiaddr + + // InterfaceListenAddresses returns a list of addresses at which this network + // listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to + // use the known local interfaces. + InterfaceListenAddresses() ([]ma.Multiaddr, error) } // Sender interface for network services. diff --git a/net/net.go b/net/net.go index a6154a780..0bff06f6a 100644 --- a/net/net.go +++ b/net/net.go @@ -8,6 +8,7 @@ import ( ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" 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" ) // IpfsNetwork implements the Network interface, @@ -27,7 +28,7 @@ type IpfsNetwork struct { } // NewIpfsNetwork is the structure that implements the network interface -func NewIpfsNetwork(ctx context.Context, local peer.Peer, +func NewIpfsNetwork(ctx context.Context, listen []ma.Multiaddr, local peer.Peer, peers peer.Peerstore, pmap *mux.ProtocolMap) (*IpfsNetwork, error) { in := &IpfsNetwork{ @@ -37,7 +38,7 @@ func NewIpfsNetwork(ctx context.Context, local peer.Peer, } var err error - in.swarm, err = swarm.NewSwarm(ctx, local, peers) + in.swarm, err = swarm.NewSwarm(ctx, listen, local, peers) if err != nil { in.Close() return nil, err @@ -96,3 +97,15 @@ func (n *IpfsNetwork) GetPeerList() []peer.Peer { func (n *IpfsNetwork) GetBandwidthTotals() (in uint64, out uint64) { return n.muxer.GetBandwidthTotals() } + +// ListenAddresses returns a list of addresses at which this network listens. +func (n *IpfsNetwork) ListenAddresses() []ma.Multiaddr { + return n.swarm.ListenAddresses() +} + +// InterfaceListenAddresses returns a list of addresses at which this network +// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to +// use the known local interfaces. +func (n *IpfsNetwork) InterfaceListenAddresses() ([]ma.Multiaddr, error) { + return n.swarm.InterfaceListenAddresses() +} diff --git a/net/swarm/addrs.go b/net/swarm/addrs.go new file mode 100644 index 000000000..531ec62ae --- /dev/null +++ b/net/swarm/addrs.go @@ -0,0 +1,107 @@ +package swarm + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" + + 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 { + addrs := make([]ma.Multiaddr, len(s.listeners)) + for i, l := range s.listeners { + addrs[i] = l.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 (s *Swarm) InterfaceListenAddresses() ([]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) + continue + } + + // unspecified? add one address per interface. + for _, ia := range ifaceAddrs { + split[0] = ia + joined := ma.Join(split...) + outputAddrs = append(outputAddrs, joined) + } + } + + log.Info("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 (s *Swarm) checkNATWarning(observed ma.Multiaddr) { + listen, err := s.InterfaceListenAddresses() + 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 +` diff --git a/net/swarm/conn.go b/net/swarm/conn.go index 1b0aa933b..63f6910a4 100644 --- a/net/swarm/conn.go +++ b/net/swarm/conn.go @@ -12,14 +12,14 @@ import ( ) // Open listeners for each network the swarm should listen on -func (s *Swarm) listen() error { +func (s *Swarm) listen(addrs []ma.Multiaddr) error { hasErr := false retErr := &ListenErr{ - Errors: make([]error, len(s.local.Addresses())), + Errors: make([]error, len(addrs)), } // listen on every address - for i, addr := range s.local.Addresses() { + for i, addr := range addrs { err := s.connListen(addr) if err != nil { hasErr = true @@ -37,11 +37,21 @@ func (s *Swarm) listen() error { // Listen for new connections on the given multiaddr func (s *Swarm) connListen(maddr ma.Multiaddr) error { + resolved, err := resolveUnspecifiedAddresses([]ma.Multiaddr{maddr}) + if err != nil { + return err + } + list, err := conn.Listen(s.Context(), maddr, s.local, s.peers) if err != nil { return err } + // add resolved local addresses to peer + for _, addr := range resolved { + s.local.AddAddress(addr) + } + // make sure port can be reused. TOOD this doesn't work... // if err := setSocketReuse(list); err != nil { // return err @@ -102,11 +112,15 @@ func (s *Swarm) connSetup(c conn.Conn) (conn.Conn, error) { // handshake3 ctxT, _ := context.WithTimeout(c.Context(), conn.HandshakeTimeout) - if err := conn.Handshake3(ctxT, c); err != nil { + h3result, err := conn.Handshake3(ctxT, c) + if err != nil { c.Close() return nil, fmt.Errorf("Handshake3 failed: %s", err) } + // check for nats. you know, just in case. + s.checkNATWarning(h3result.LocalObservedAddress) + // add to conns s.connsLock.Lock() diff --git a/net/swarm/swarm.go b/net/swarm/swarm.go index e80758979..59279ed32 100644 --- a/net/swarm/swarm.go +++ b/net/swarm/swarm.go @@ -13,6 +13,7 @@ import ( ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" 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" ) var log = u.Logger("swarm") @@ -70,7 +71,7 @@ type Swarm struct { } // NewSwarm constructs a Swarm, with a Chan. -func NewSwarm(ctx context.Context, local peer.Peer, ps peer.Peerstore) (*Swarm, error) { +func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr, local peer.Peer, ps peer.Peerstore) (*Swarm, error) { s := &Swarm{ Pipe: msg.NewPipe(10), conns: conn.MultiConnMap{}, @@ -83,7 +84,7 @@ func NewSwarm(ctx context.Context, local peer.Peer, ps peer.Peerstore) (*Swarm, s.ContextCloser = ctxc.NewContextCloser(ctx, s.close) go s.fanOut() - return s, s.listen() + return s, s.listen(listenAddrs) } // close stops a swarm. It's the underlying function called by ContextCloser @@ -210,6 +211,3 @@ func (s *Swarm) GetPeerList() []peer.Peer { s.connsLock.RUnlock() return out } - -// Temporary to ensure that the Swarm always matches the Network interface as we are changing it -// var _ Network = &Swarm{} diff --git a/net/swarm/swarm_test.go b/net/swarm/swarm_test.go index 9fc9ab1f9..4dd2a1448 100644 --- a/net/swarm/swarm_test.go +++ b/net/swarm/swarm_test.go @@ -57,7 +57,7 @@ func makeSwarms(ctx context.Context, t *testing.T, addrs []string) ([]*Swarm, [] for _, addr := range addrs { local := setupPeer(t, addr) peerstore := peer.NewPeerstore() - swarm, err := NewSwarm(ctx, local, peerstore) + swarm, err := NewSwarm(ctx, local.Addresses(), local, peerstore) if err != nil { t.Fatal(err) } diff --git a/peer/peer.go b/peer/peer.go index fbea65fd1..7cc8a4ccb 100644 --- a/peer/peer.go +++ b/peer/peer.go @@ -66,7 +66,8 @@ type Peer interface { Addresses() []ma.Multiaddr // AddAddress adds the given Multiaddr address to Peer's addresses. - AddAddress(a ma.Multiaddr) + // returns whether this was a newly added address. + AddAddress(a ma.Multiaddr) bool // NetAddress returns the first Multiaddr found for a given network. NetAddress(n string) ma.Multiaddr @@ -141,16 +142,18 @@ func (p *peer) Addresses() []ma.Multiaddr { } // AddAddress adds the given Multiaddr address to Peer's addresses. -func (p *peer) AddAddress(a ma.Multiaddr) { +// Returns whether this address was a newly added address +func (p *peer) AddAddress(a ma.Multiaddr) bool { p.Lock() defer p.Unlock() for _, addr := range p.addresses { if addr.Equal(a) { - return + return false } } p.addresses = append(p.addresses, a) + return true } // NetAddress returns the first Multiaddr found for a given network. diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 2b8732338..3748e6519 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -24,7 +24,7 @@ func setupDHT(ctx context.Context, t *testing.T, p peer.Peer) *IpfsDHT { peerstore := peer.NewPeerstore() dhts := netservice.NewService(ctx, nil) // nil handler for now, need to patch it - net, err := inet.NewIpfsNetwork(ctx, p, peerstore, &mux.ProtocolMap{ + net, err := inet.NewIpfsNetwork(ctx, p.Addresses(), p, peerstore, &mux.ProtocolMap{ mux.ProtocolID_Routing: dhts, }) if err != nil { diff --git a/util/util.go b/util/util.go index b738bbfcb..da07dab4c 100644 --- a/util/util.go +++ b/util/util.go @@ -4,16 +4,13 @@ import ( "errors" "io" "math/rand" - "net" "os" "path/filepath" - "reflect" "strings" "time" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - 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" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/mitchellh/go-homedir" ) @@ -111,57 +108,3 @@ func GetenvBool(name string) bool { v := strings.ToLower(os.Getenv(name)) return v == "true" || v == "t" || v == "1" } - -// IsLoopbackAddr returns whether or not the ip portion of the passed in multiaddr -// string is a loopback address -func IsLoopbackAddr(addr string) bool { - loops := []string{"/ip4/127.0.0.1", "/ip6/::1"} - for _, loop := range loops { - if strings.HasPrefix(addr, loop) { - return true - } - } - return false -} - -// GetLocalAddresses returns a list of ip addresses associated with -// the local machine -func GetLocalAddresses() ([]ma.Multiaddr, error) { - // Enumerate interfaces on this machine - ifaces, err := net.Interfaces() - if err != nil { - return nil, err - } - - var maddrs []ma.Multiaddr - for _, i := range ifaces { - addrs, err := i.Addrs() - if err != nil { - log.Warningf("Skipping addr: %s", err) - continue - } - // Check each address and convert to a multiaddr - for _, addr := range addrs { - switch v := addr.(type) { - case *net.IPNet: - - // Build multiaddr - maddr, err := manet.FromIP(v.IP) - if err != nil { - log.Errorf("maddr parsing error: %s", err) - continue - } - - // Dont list loopback addresses - if IsLoopbackAddr(maddr.String()) { - continue - } - maddrs = append(maddrs, maddr) - default: - // Not sure if any other types will show up here - log.Errorf("Got '%s' type = '%s'", v, reflect.TypeOf(v)) - } - } - } - return maddrs, nil -}