+++ /dev/null
-package p2p
-
-import (
- "fmt"
- "net"
- "strconv"
- "time"
-
- log "github.com/sirupsen/logrus"
- cmn "github.com/tendermint/tmlibs/common"
-
- "github.com/vapor/errors"
- "github.com/vapor/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
- ExternalAddress() *NetAddress
- String() string
- Stop() bool
-}
-
-//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 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
- }
- if v4 := ipnet.IP.To4(); v4 == nil || (!settleForLocal && v4[0] == 127) {
- continue
- }
- return NewNetAddressIPPort(ipnet.IP, uint16(port))
- }
-
- log.Info("Node may not be connected to internet. Settling for local address")
- return getNaiveExternalAddress(port, true)
-}
-
-func splitHostPort(addr string) (host string, port int) {
- host, portStr, err := net.SplitHostPort(addr)
- if err != nil {
- cmn.PanicSanity(err)
- }
- port, err = strconv.Atoi(portStr)
- if err != nil {
- cmn.PanicSanity(err)
- }
- return host, port
-}
-
-//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)
-
- 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 {
- cmn.PanicCrisis(err)
- }
-
- intAddr, err := NewNetAddressString(lAddr)
- if err != nil {
- cmn.PanicCrisis(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
- var upnpMap bool
- if !skipUPNP && (lAddrIP == "" || lAddrIP == "0.0.0.0") {
- extAddr, err = getUPNPExternalAddress(lAddrPort, listenerPort)
- upnpMap = err == nil
- log.WithField("err", err).Info("get UPNP external address")
- }
-
- if extAddr == nil {
- if address := GetIP(); address.Success == true {
- extAddr = NewNetAddressIPPort(net.ParseIP(address.IP), uint16(lAddrPort))
- }
- }
- if extAddr == nil {
- extAddr = getNaiveExternalAddress(listenerPort, false)
- }
- if extAddr == nil {
- cmn.PanicCrisis("could not determine external address!")
- }
-
- dl := &DefaultListener{
- listener: listener,
- intAddr: intAddr,
- extAddr: extAddr,
- connections: make(chan net.Conn, numBufferedConnections),
- }
- dl.BaseService = *cmn.NewBaseService(nil, "DefaultListener", dl)
- dl.Start() // Started upon construction
- 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()
-}
-
-//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)
- }
- l.connections <- conn
- }
- // Cleanup
- close(l.connections)
-}
-
-//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
-}
-
-// 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)
-}