"fmt"
"net"
"strconv"
+ "strings"
"time"
- "github.com/bytom/p2p/upnp"
log "github.com/sirupsen/logrus"
cmn "github.com/tendermint/tmlibs/common"
+
+ 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
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)
return host, port
}
-// skipUPNP: If true, does not try getUPNPExternalAddress()
+//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
- var getExtIP = false
- var listenerStatus = false
-
- 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())
- log.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)
- if extAddr != nil {
- getExtIP = true
- listenerStatus = true
- }
- }
+ 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 {
- if address := GetIP(); address.Success == true {
- extAddr = NewNetAddressIPPort(net.ParseIP(address.Ip), uint16(lAddrPort))
- getExtIP = true
+ 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")
}
}
- // Otherwise just use the local address...
- if extAddr == nil {
- extAddr = getNaiveExternalAddress(listenerPort, false)
- }
- if extAddr == nil {
- cmn.PanicCrisis("Could not determine external address!")
- }
dl := &DefaultListener{
listener: listener,
}
dl.BaseService = *cmn.NewBaseService(nil, "DefaultListener", dl)
dl.Start() // Started upon construction
-
- if !listenerStatus && getExtIP {
- conn, err := net.DialTimeout("tcp", extAddr.String(), 3*time.Second)
-
- if err != nil && conn == nil {
- log.Error("Could not open listen port")
- }
-
- if err == nil && conn != nil {
- log.Info("Success open listen port")
- listenerStatus = true
- conn.Close()
- }
+ if upnpMap {
+ return dl, true
}
- return dl, listenerStatus
+ 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) *NetAddress {
- log.Info("Getting UPNP external address")
- nat, err := upnp.Discover()
- if err != nil {
- log.Info("Could not perform UPNP discover. error:", err)
- return nil
- }
-
- ext, err := nat.GetExternalAddress()
- if err != nil {
- log.Info("Could not perform 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, "bytomd", 0)
- if err != nil {
- log.Info("Could not add UPNP port mapping. error:", err)
- return nil
- }
-
- log.Info("Got UPNP external address ", ext)
- return NewNetAddressIPPort(ext, uint16(externalPort))
-}
-
-func getNaiveExternalAddress(port int, settleForLocal bool) *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 || (!settleForLocal && v4[0] == 127) {
- continue
- } // loopback
- return NewNetAddressIPPort(ipnet.IP, uint16(port))
- }
-
- // try again, but settle for local
- log.Info("Node may not be connected to internet. Settling for local address")
- return getNaiveExternalAddress(port, true)
-}