OSDN Git Service

Add listen port open check
authorYahtoo Ma <yahtoo.ma@gmail.com>
Fri, 4 May 2018 10:07:51 +0000 (18:07 +0800)
committerYahtoo Ma <yahtoo.ma@gmail.com>
Tue, 8 May 2018 07:32:53 +0000 (15:32 +0800)
netsync/handle.go
p2p/listener.go
p2p/public_ip.go [new file with mode: 0644]
p2p/switch.go

index e89cb86..0494a38 100644 (file)
@@ -14,6 +14,8 @@ import (
        core "github.com/bytom/protocol"
        "github.com/bytom/protocol/bc"
        "github.com/bytom/version"
+       "net"
+       "time"
 )
 
 //SyncManager Sync Manager is responsible for the business layer information synchronization
@@ -28,6 +30,7 @@ type SyncManager struct {
        fetcher     *Fetcher
        blockKeeper *blockKeeper
        peers       *peerSet
+       mapResult   bool
 
        newBlockCh    chan *bc.Hash
        newPeerCh     chan struct{}
@@ -64,14 +67,16 @@ func NewSyncManager(config *cfg.Config, chain *core.Chain, txPool *core.TxPool,
        manager.sw.AddReactor("PROTOCOL", protocolReactor)
 
        // Create & add listener
+       var mapResult bool
+       var l p2p.Listener
        if !config.VaultMode {
                p, address := protocolAndAddress(manager.config.P2P.ListenAddress)
-               l := p2p.NewDefaultListener(p, address, manager.config.P2P.SkipUPNP, nil)
+               l, mapResult = p2p.NewDefaultListener(p, address, manager.config.P2P.SkipUPNP, nil)
                manager.sw.AddListener(l)
        }
-       manager.sw.SetNodeInfo(manager.makeNodeInfo())
+       manager.sw.SetNodeInfo(manager.makeNodeInfo(mapResult))
        manager.sw.SetNodePrivKey(manager.privKey)
-
+       manager.mapResult = mapResult
        // Optionally, start the pex reactor
        //var addrBook *p2p.AddrBook
        if config.P2P.PexReactor {
@@ -93,7 +98,7 @@ func protocolAndAddress(listenAddr string) (string, string) {
        return p, address
 }
 
-func (sm *SyncManager) makeNodeInfo() *p2p.NodeInfo {
+func (sm *SyncManager) makeNodeInfo(listenOpen bool) *p2p.NodeInfo {
        nodeInfo := &p2p.NodeInfo{
                PubKey:  sm.privKey.PubKey().Unwrap().(crypto.PubKeyEd25519),
                Moniker: sm.config.Moniker,
@@ -110,13 +115,15 @@ func (sm *SyncManager) makeNodeInfo() *p2p.NodeInfo {
        }
 
        p2pListener := sm.sw.Listeners()[0]
-       p2pHost := p2pListener.ExternalAddress().IP.String()
-       p2pPort := p2pListener.ExternalAddress().Port
 
        // 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
-       nodeInfo.ListenAddr = cmn.Fmt("%v:%v", p2pHost, p2pPort)
+       if listenOpen {
+               nodeInfo.ListenAddr = cmn.Fmt("%v:%v", p2pListener.ExternalAddress().IP.String(), p2pListener.ExternalAddress().Port)
+       } else {
+               nodeInfo.ListenAddr = cmn.Fmt("%v:%v", p2pListener.InternalAddress().IP.String(), p2pListener.InternalAddress().Port)
+       }
        return nodeInfo
 }
 
@@ -127,6 +134,20 @@ func (sm *SyncManager) netStart() error {
                return err
        }
 
+       if !sm.mapResult {
+               conn, err := net.DialTimeout("tcp", sm.NodeInfo().ListenAddr, 3*time.Second)
+
+               if err != nil && conn == nil {
+                       log.Info("Could not open listen port")
+               }
+
+               if err == nil && conn != nil {
+                       log.Info("Success open listen port")
+                       conn.Close()
+                       sm.sw.SetNodeInfo(sm.makeNodeInfo(true))
+               }
+       }
+
        // If seeds exist, add them to the address book and dial out
        if sm.config.P2P.Seeds != "" {
                // dial out
index cf0b97a..5102522 100644 (file)
@@ -49,13 +49,14 @@ func splitHostPort(addr string) (host string, port int) {
 }
 
 // skipUPNP: If true, does not try getUPNPExternalAddress()
-func NewDefaultListener(protocol string, lAddr string, skipUPNP bool, logger tlog.Logger) Listener {
+func NewDefaultListener(protocol string, lAddr string, skipUPNP bool, logger tlog.Logger) (Listener, bool) {
        // Local listen IP & port
        lAddrIP, lAddrPort := splitHostPort(lAddr)
 
        // Create listener
        var listener net.Listener
        var err error
+       var mapResult bool
        for i := 0; i < tryListenSeconds; i++ {
                listener, err = net.Listen(protocol, lAddr)
                if err == nil {
@@ -87,6 +88,14 @@ func NewDefaultListener(protocol string, lAddr string, skipUPNP bool, logger tlo
                // If the lAddrIP is INADDR_ANY, try UPnP
                if lAddrIP == "" || lAddrIP == "0.0.0.0" {
                        extAddr = getUPNPExternalAddress(lAddrPort, listenerPort)
+                       if extAddr!=nil {
+                               mapResult = true
+                       }
+               }
+       }
+       if extAddr == nil {
+               if address := GetIP([]string{}, time.Duration(0)); address.Success == true {
+                       extAddr = NewNetAddressIPPort(net.ParseIP(address.Ip), uint16(lAddrPort))
                }
        }
        // Otherwise just use the local address...
@@ -105,7 +114,7 @@ func NewDefaultListener(protocol string, lAddr string, skipUPNP bool, logger tlo
        }
        dl.BaseService = *cmn.NewBaseService(logger, "DefaultListener", dl)
        dl.Start() // Started upon construction
-       return dl
+       return dl, mapResult
 }
 
 func (l *DefaultListener) OnStart() error {
diff --git a/p2p/public_ip.go b/p2p/public_ip.go
new file mode 100644 (file)
index 0000000..3db86d9
--- /dev/null
@@ -0,0 +1,104 @@
+package p2p
+
+import (
+       "errors"
+       "fmt"
+       "io/ioutil"
+       "net/http"
+       "strings"
+       "time"
+       "regexp"
+)
+
+var defaultServices  = []string{
+       "http://icanhazip.com/",
+       "http://ifconfig.io/ip",
+       "http://ident.me/",
+       "http://whatismyip.akamai.com/",
+       "http://myip.dnsomatic.com/",
+       "http://diagnostic.opendns.com/myip",
+}
+
+type IpResult struct {
+       Success bool
+       Ip string
+       Error error
+}
+
+var timeout time.Duration
+
+func GetIP(services []string, to time.Duration) *IpResult {
+
+       if services == nil || len(services) == 0 {
+               services = defaultServices
+       }
+       if to == 0 {
+               to = time.Duration(10)
+       }
+       timeout = to
+
+       count := len(services)
+       done := make(chan *IpResult)
+       for k := range services {
+               go ipAddress(services[k], done)
+       }
+       for ;; {
+               select{
+               case result := <-done:
+                       if result.Success {
+                               return result
+                       } else {
+                               count--
+                               if count == 0 {
+                                       result.Error = errors.New("All services doesn't available.")
+                                       return result
+                               }
+                       }
+                       continue
+               case <-time.After(time.Second * timeout):
+                       return &IpResult{false, "", errors.New("Timed out")}
+               }
+       }
+}
+
+func ipAddress(service string, done chan<- *IpResult) {
+
+       timeout := time.Duration(time.Second * timeout)
+       client := http.Client{Timeout: timeout}
+       resp, err := client.Get(service)
+
+       if err != nil {
+               sendResult(&IpResult{false, "", errors.New("Time out")}, done)
+               return
+       }
+
+       if err == nil {
+
+               defer resp.Body.Close()
+
+               address, err := ioutil.ReadAll(resp.Body)
+               ip := fmt.Sprintf("%s", strings.TrimSpace(string(address)))
+               if err== nil && checkIp(ip) {
+                       sendResult(&IpResult{true, ip, nil}, done)
+                       return
+               }
+       }
+       sendResult(&IpResult{false, "", errors.New("Unable to talk with a service")}, done)
+}
+
+func sendResult(result *IpResult, done chan<- *IpResult) {
+       select {
+       case done <- result:
+               return
+       default:
+               return
+       }
+}
+
+func checkIp(ip string) bool {
+       match, _ := regexp.MatchString(`^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`, ip)
+       if match {
+               return true
+       }
+       return false
+}
index 5d56bfd..861f8d0 100644 (file)
@@ -331,7 +331,7 @@ func (sw *Switch) DialSeeds(addrBook *AddrBook, seeds []string) error {
        }
        //permute the list, dial them in random order.
        perm := rand.Perm(len(netAddrs))
-       for i := 0; i < len(perm)/2; i++ {
+       for i := 0; i < len(perm); i++ {
                j := perm[i]
                sw.dialSeed(netAddrs[j])
        }