OSDN Git Service

docs(release note): update bytom version 1.1.0 release note
[bytom/bytom.git] / p2p / listener.go
index 8415bcc..14117d3 100644 (file)
@@ -4,13 +4,24 @@ import (
        "fmt"
        "net"
        "strconv"
+       "strings"
        "time"
 
-       "github.com/bytom/p2p/upnp"
+       log "github.com/sirupsen/logrus"
        cmn "github.com/tendermint/tmlibs/common"
-       "github.com/tendermint/tmlibs/log"
+
+       cfg "github.com/bytom/bytom/config"
+       "github.com/bytom/bytom/errors"
+       "github.com/bytom/bytom/p2p/upnp"
+)
+
+const (
+       numBufferedConnections = 10
+       defaultExternalPort    = 8770
+       tryListenTimes         = 5
 )
 
+//Listener subset of the methods of DefaultListener
 type Listener interface {
        Connections() <-chan net.Conn
        InternalAddress() *NetAddress
@@ -19,21 +30,56 @@ type Listener interface {
        Stop() bool
 }
 
-// Implements Listener
-type DefaultListener struct {
-       cmn.BaseService
+// Defaults to tcp
+func protocolAndAddress(listenAddr string) (string, string) {
+       p, address := "tcp", listenAddr
+       parts := strings.SplitN(address, "://", 2)
+       if len(parts) == 2 {
+               p, address = parts[0], parts[1]
+       }
+       return p, address
+}
 
-       listener    net.Listener
-       intAddr     *NetAddress
-       extAddr     *NetAddress
-       connections chan net.Conn
+// GetListener get listener and listen address.
+func GetListener(config *cfg.P2PConfig) (Listener, string) {
+       p, address := protocolAndAddress(config.ListenAddress)
+       l, listenerStatus := NewDefaultListener(p, address, config.SkipUPNP)
+
+       // We assume that the rpcListener has the same ExternalAddress.
+       // This is probably true because both P2P and RPC listeners use UPnP,
+       // except of course if the rpc is only bound to localhost
+       if listenerStatus {
+               return l, cmn.Fmt("%v:%v", l.ExternalAddress().IP.String(), l.ExternalAddress().Port)
+       }
+
+       return l, cmn.Fmt("%v:%v", l.InternalAddress().IP.String(), l.InternalAddress().Port)
 }
 
-const (
-       numBufferedConnections = 10
-       defaultExternalPort    = 8770
-       tryListenSeconds       = 5
-)
+//getUPNPExternalAddress UPNP external address discovery & port mapping
+func getUPNPExternalAddress(externalPort, internalPort int) (*NetAddress, error) {
+       nat, err := upnp.Discover()
+       if err != nil {
+               return nil, errors.Wrap(err, "could not perform UPNP discover")
+       }
+
+       ext, err := nat.GetExternalAddress()
+       if err != nil {
+               return nil, errors.Wrap(err, "could not perform UPNP external address")
+       }
+
+       if externalPort == 0 {
+               externalPort = defaultExternalPort
+       }
+       externalPort, err = nat.AddPortMapping("tcp", externalPort, internalPort, "bytomd tcp", 0)
+       if err != nil {
+               return nil, errors.Wrap(err, "could not add tcp UPNP port mapping")
+       }
+       externalPort, err = nat.AddPortMapping("udp", externalPort, internalPort, "bytomd udp", 0)
+       if err != nil {
+               return nil, errors.Wrap(err, "could not add udp UPNP port mapping")
+       }
+       return NewNetAddressIPPort(ext, uint16(externalPort)), nil
+}
 
 func splitHostPort(addr string) (host string, port int) {
        host, portStr, err := net.SplitHostPort(addr)
@@ -47,50 +93,57 @@ func splitHostPort(addr string) (host string, port int) {
        return host, port
 }
 
-// skipUPNP: If true, does not try getUPNPExternalAddress()
-func NewDefaultListener(protocol string, lAddr string, skipUPNP bool, logger log.Logger) Listener {
+//DefaultListener Implements bytomd server Listener
+type DefaultListener struct {
+       cmn.BaseService
+
+       listener    net.Listener
+       intAddr     *NetAddress
+       extAddr     *NetAddress
+       connections chan net.Conn
+}
+
+//NewDefaultListener create a default listener
+func NewDefaultListener(protocol string, lAddr string, skipUPNP bool) (Listener, bool) {
        // Local listen IP & port
        lAddrIP, lAddrPort := splitHostPort(lAddr)
 
-       // Create listener
-       var listener net.Listener
-       var err error
-       for i := 0; i < tryListenSeconds; i++ {
+       listener, err := net.Listen(protocol, lAddr)
+       for i := 0; i < tryListenTimes && err != nil; i++ {
+               time.Sleep(time.Second * 1)
                listener, err = net.Listen(protocol, lAddr)
-               if err == nil {
-                       break
-               } else if i < tryListenSeconds-1 {
-                       time.Sleep(time.Second * 1)
-               }
        }
        if err != nil {
-               cmn.PanicCrisis(err)
+               log.Panic(err)
        }
-       // Actual listener local IP & port
-       listenerIP, listenerPort := splitHostPort(listener.Addr().String())
-       logger.Info("Local listener", "ip", listenerIP, "port", listenerPort)
 
-       // Determine internal address...
-       var intAddr *NetAddress
-       intAddr, err = NewNetAddressString(lAddr)
+       intAddr, err := NewNetAddressString(lAddr)
        if err != nil {
-               cmn.PanicCrisis(err)
+               log.Panic(err)
        }
 
+       // Actual listener local IP & port
+       listenerIP, listenerPort := splitHostPort(listener.Addr().String())
+       log.Info("Local listener", " ip:", listenerIP, " port:", listenerPort)
+
        // Determine external address...
        var extAddr *NetAddress
-       if !skipUPNP {
-               // If the lAddrIP is INADDR_ANY, try UPnP
-               if lAddrIP == "" || lAddrIP == "0.0.0.0" {
-                       extAddr = getUPNPExternalAddress(lAddrPort, listenerPort, logger)
-               }
-       }
-       // Otherwise just use the local address...
-       if extAddr == nil {
-               extAddr = getNaiveExternalAddress(listenerPort)
+       var upnpMap bool
+       if !skipUPNP && (lAddrIP == "" || lAddrIP == "0.0.0.0") {
+               extAddr, err = getUPNPExternalAddress(lAddrPort, listenerPort)
+               upnpMap = err == nil
+               log.WithFields(log.Fields{"module": logModule, "err": err}).Info("get UPNP external address")
        }
+
+       // Get the IPv4 available
        if extAddr == nil {
-               cmn.PanicCrisis("Could not determine external address!")
+               if ip, err := ExternalIPv4(); err != nil {
+                       log.WithFields(log.Fields{"module": logModule, "err": err}).Warning("get ipv4 external address")
+                       log.Panic("get ipv4 external address fail!")
+               } else {
+                       extAddr = NewNetAddressIPPort(net.ParseIP(ip), uint16(lAddrPort))
+                       log.WithFields(log.Fields{"module": logModule, "addr": extAddr}).Info("get ipv4 external address success")
+               }
        }
 
        dl := &DefaultListener{
@@ -99,120 +152,72 @@ func NewDefaultListener(protocol string, lAddr string, skipUPNP bool, logger log
                extAddr:     extAddr,
                connections: make(chan net.Conn, numBufferedConnections),
        }
-       dl.BaseService = *cmn.NewBaseService(logger, "DefaultListener", dl)
+       dl.BaseService = *cmn.NewBaseService(nil, "DefaultListener", dl)
        dl.Start() // Started upon construction
-       return dl
+       if upnpMap {
+               return dl, true
+       }
+
+       conn, err := net.DialTimeout("tcp", extAddr.String(), 3*time.Second)
+       if err != nil {
+               return dl, false
+       }
+       conn.Close()
+       return dl, true
 }
 
+//OnStart start listener
 func (l *DefaultListener) OnStart() error {
        l.BaseService.OnStart()
        go l.listenRoutine()
        return nil
 }
 
+//OnStop stop listener
 func (l *DefaultListener) OnStop() {
        l.BaseService.OnStop()
        l.listener.Close()
 }
 
-// Accept connections and pass on the channel
+//listenRoutine Accept connections and pass on the channel
 func (l *DefaultListener) listenRoutine() {
        for {
                conn, err := l.listener.Accept()
-
                if !l.IsRunning() {
                        break // Go to cleanup
                }
-
                // listener wasn't stopped,
                // yet we encountered an error.
                if err != nil {
-                       cmn.PanicCrisis(err)
+                       log.Panic(err)
                }
-
                l.connections <- conn
        }
-
        // Cleanup
        close(l.connections)
-       for _ = range l.connections {
-               // Drain
-       }
 }
 
-// A channel of inbound connections.
-// It gets closed when the listener closes.
+//Connections a channel of inbound connections. It gets closed when the listener closes.
 func (l *DefaultListener) Connections() <-chan net.Conn {
        return l.connections
 }
 
+//InternalAddress listener internal address
 func (l *DefaultListener) InternalAddress() *NetAddress {
        return l.intAddr
 }
 
+//ExternalAddress listener external address for remote peer dial
 func (l *DefaultListener) ExternalAddress() *NetAddress {
        return l.extAddr
 }
 
-// NOTE: The returned listener is already Accept()'ing.
-// So it's not suitable to pass into http.Serve().
+// NetListener the returned listener is already Accept()'ing. So it's not suitable to pass into http.Serve().
 func (l *DefaultListener) NetListener() net.Listener {
        return l.listener
 }
 
+//String string of default listener
 func (l *DefaultListener) String() string {
        return fmt.Sprintf("Listener(@%v)", l.extAddr)
 }
-
-/* external address helpers */
-
-// UPNP external address discovery & port mapping
-func getUPNPExternalAddress(externalPort, internalPort int, logger log.Logger) *NetAddress {
-       logger.Info("Getting UPNP external address")
-       nat, err := upnp.Discover()
-       if err != nil {
-               logger.Info("Could not perform UPNP discover", "error", err)
-               return nil
-       }
-
-       ext, err := nat.GetExternalAddress()
-       if err != nil {
-               logger.Info("Could not get UPNP external address", "error", err)
-               return nil
-       }
-
-       // UPnP can't seem to get the external port, so let's just be explicit.
-       if externalPort == 0 {
-               externalPort = defaultExternalPort
-       }
-
-       externalPort, err = nat.AddPortMapping("tcp", externalPort, internalPort, "tendermint", 0)
-       if err != nil {
-               logger.Info("Could not add UPNP port mapping", "error", err)
-               return nil
-       }
-
-       logger.Info("Got UPNP external address", "address", ext)
-       return NewNetAddressIPPort(ext, uint16(externalPort))
-}
-
-// TODO: use syscalls: http://pastebin.com/9exZG4rh
-func getNaiveExternalAddress(port int) *NetAddress {
-       addrs, err := net.InterfaceAddrs()
-       if err != nil {
-               cmn.PanicCrisis(cmn.Fmt("Could not fetch interface addresses: %v", err))
-       }
-
-       for _, a := range addrs {
-               ipnet, ok := a.(*net.IPNet)
-               if !ok {
-                       continue
-               }
-               v4 := ipnet.IP.To4()
-               if v4 == nil || v4[0] == 127 {
-                       continue
-               } // loopback
-               return NewNetAddressIPPort(ipnet.IP, uint16(port))
-       }
-       return nil
-}