diff --git a/core/core.go b/core/core.go index c1fcb458f..c06535a1f 100644 --- a/core/core.go +++ b/core/core.go @@ -18,7 +18,6 @@ import ( ic "github.com/jbenet/go-ipfs/p2p/crypto" p2phost "github.com/jbenet/go-ipfs/p2p/host" p2pbhost "github.com/jbenet/go-ipfs/p2p/host/basic" - inat "github.com/jbenet/go-ipfs/p2p/nat" swarm "github.com/jbenet/go-ipfs/p2p/net/swarm" addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr" peer "github.com/jbenet/go-ipfs/p2p/peer" @@ -422,7 +421,7 @@ func constructPeerHost(ctx context.Context, cfg *config.Config, id peer.ID, ps p return nil, debugerror.Wrap(err) } - peerhost := p2pbhost.New(network) + peerhost := p2pbhost.New(network, p2pbhost.NATPortMap) // explicitly set these as our listen addrs. // (why not do it inside inet.NewNetwork? because this way we can // listen on addresses without necessarily advertising those publicly.) @@ -432,16 +431,6 @@ func constructPeerHost(ctx context.Context, cfg *config.Config, id peer.ID, ps p } log.Infof("Swarm listening at: %s", addrs) - nat := inat.DiscoverGateway() - if nat != nil { - nat.PortMapAddrs(filteredAddrs) - mapAddrs := nat.ExternalAddrs() - if len(mapAddrs) > 0 { - log.Infof("NAT mapping addrs: %s", mapAddrs) - addrs = append(addrs, mapAddrs...) - } - } - ps.AddAddresses(id, addrs) return peerhost, nil } diff --git a/p2p/host/basic/basic_host.go b/p2p/host/basic/basic_host.go index 944e9d6e1..26912aeb8 100644 --- a/p2p/host/basic/basic_host.go +++ b/p2p/host/basic/basic_host.go @@ -1,10 +1,15 @@ package basichost import ( + "sync" + 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" + goprocess "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" + inat "github.com/jbenet/go-ipfs/p2p/nat" inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" protocol "github.com/jbenet/go-ipfs/p2p/protocol" @@ -14,20 +19,35 @@ import ( var log = eventlog.Logger("p2p/host/basic") +type Option int + +const ( + NATPortMap Option = iota +) + type BasicHost struct { network inet.Network mux *protocol.Mux ids *identify.IDService relay *relay.RelayService + + natmu sync.Mutex + nat *inat.NAT + + proc goprocess.Process } // New constructs and sets up a new *BasicHost with given Network -func New(net inet.Network) *BasicHost { +func New(net inet.Network, opts ...Option) *BasicHost { h := &BasicHost{ network: net, mux: protocol.NewMux(), } + h.proc = goprocess.WithTeardown(func() error { + return h.Network().Close() + }) + // setup host services h.ids = identify.NewIDService(h) h.relay = relay.NewRelayService(h, h.Mux().HandleSync) @@ -35,9 +55,48 @@ func New(net inet.Network) *BasicHost { net.SetConnHandler(h.newConnHandler) net.SetStreamHandler(h.newStreamHandler) + for _, o := range opts { + switch o { + case NATPortMap: + h.setupNATPortMap() + } + } + return h } +func (h *BasicHost) setupNATPortMap() { + // do this asynchronously to avoid blocking daemon startup + + h.proc.Go(func(worker goprocess.Process) { + nat := inat.DiscoverNAT() + if nat == nil { // no nat, or failed to get it. + return + } + + select { + case <-worker.Closing(): + nat.Close() + return + default: + } + + // wire up the nat to close when proc closes. + h.proc.AddChild(nat.Process()) + + h.natmu.Lock() + h.nat = nat + h.natmu.Unlock() + + addrs := h.Network().ListenAddresses() + nat.PortMapAddrs(addrs) + mapAddrs := nat.ExternalAddrs() + if len(mapAddrs) > 0 { + log.Infof("NAT mapping addrs: %s", mapAddrs) + } + }) +} + // newConnHandler is the remote-opened conn handler for inet.Network func (h *BasicHost) newConnHandler(c inet.Conn) { h.ids.IdentifyConn(c) @@ -143,7 +202,23 @@ func (h *BasicHost) dialPeer(ctx context.Context, p peer.ID) error { return nil } +func (h *BasicHost) Addrs() []ma.Multiaddr { + addrs, err := h.Network().InterfaceListenAddresses() + if err != nil { + log.Debug("error retrieving network interface addrs") + } + + h.natmu.Lock() + nat := h.nat + h.natmu.Unlock() + if nat != nil { + addrs = append(addrs, nat.ExternalAddrs()...) + } + + return addrs +} + // Close shuts down the Host's services (network, etc). func (h *BasicHost) Close() error { - return h.Network().Close() + return h.proc.Close() } diff --git a/p2p/host/host.go b/p2p/host/host.go index b98ada53c..6424c9c67 100644 --- a/p2p/host/host.go +++ b/p2p/host/host.go @@ -2,6 +2,7 @@ package host import ( 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" inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" @@ -23,6 +24,9 @@ type Host interface { // Peerstore returns the Host's repository of Peer Addresses and Keys. Peerstore() peer.Peerstore + // Returns the listen addresses of the Host + Addrs() []ma.Multiaddr + // Networks returns the Network interface of the Host Network() inet.Network diff --git a/p2p/nat/nat.go b/p2p/nat/nat.go index 3d7dad55c..1340d6ee2 100644 --- a/p2p/nat/nat.go +++ b/p2p/nat/nat.go @@ -29,9 +29,9 @@ var log = eventlog.Logger("nat") // Port mappings are renewed every (MappingDuration / 3) const MappingDuration = time.Second * 60 -// DiscoverGateway looks for a NAT device in the network and +// DiscoverNAT looks for a NAT device in the network and // returns an object that can manage port mappings. -func DiscoverGateway() *NAT { +func DiscoverNAT() *NAT { nat, err := nat.DiscoverGateway() if err != nil { log.Debug("DiscoverGateway error:", err) @@ -72,6 +72,12 @@ func (nat *NAT) Close() error { return nat.proc.Close() } +// Process returns the nat's life-cycle manager, for making it listen +// to close signals. +func (nat *NAT) Process() goprocess.Process { + return nat.proc +} + // Notifier is an object that assists NAT in notifying listeners. // It is implemented using github.com/jbenet/go-ipfs/thirdparty/notifier type Notifier struct {