OSDN Git Service

Add listen port open check
[bytom/bytom.git] / p2p / listener.go
1 package p2p
2
3 import (
4         "fmt"
5         "net"
6         "strconv"
7         "time"
8
9         "github.com/bytom/p2p/upnp"
10         log "github.com/sirupsen/logrus"
11         cmn "github.com/tendermint/tmlibs/common"
12         tlog "github.com/tendermint/tmlibs/log"
13 )
14
15 type Listener interface {
16         Connections() <-chan net.Conn
17         InternalAddress() *NetAddress
18         ExternalAddress() *NetAddress
19         String() string
20         Stop() bool
21 }
22
23 // Implements Listener
24 type DefaultListener struct {
25         cmn.BaseService
26
27         listener    net.Listener
28         intAddr     *NetAddress
29         extAddr     *NetAddress
30         connections chan net.Conn
31 }
32
33 const (
34         numBufferedConnections = 10
35         defaultExternalPort    = 8770
36         tryListenSeconds       = 5
37 )
38
39 func splitHostPort(addr string) (host string, port int) {
40         host, portStr, err := net.SplitHostPort(addr)
41         if err != nil {
42                 cmn.PanicSanity(err)
43         }
44         port, err = strconv.Atoi(portStr)
45         if err != nil {
46                 cmn.PanicSanity(err)
47         }
48         return host, port
49 }
50
51 // skipUPNP: If true, does not try getUPNPExternalAddress()
52 func NewDefaultListener(protocol string, lAddr string, skipUPNP bool, logger tlog.Logger) (Listener, bool) {
53         // Local listen IP & port
54         lAddrIP, lAddrPort := splitHostPort(lAddr)
55
56         // Create listener
57         var listener net.Listener
58         var err error
59         var mapResult bool
60         for i := 0; i < tryListenSeconds; i++ {
61                 listener, err = net.Listen(protocol, lAddr)
62                 if err == nil {
63                         break
64                 } else if i < tryListenSeconds-1 {
65                         time.Sleep(time.Second * 1)
66                 }
67         }
68         if err != nil {
69                 cmn.PanicCrisis(err)
70         }
71         // Actual listener local IP & port
72         listenerIP, listenerPort := splitHostPort(listener.Addr().String())
73         log.WithFields(log.Fields{
74                 "ip":   listenerIP,
75                 "port": listenerPort,
76         }).Info("Local listener")
77
78         // Determine internal address...
79         var intAddr *NetAddress
80         intAddr, err = NewNetAddressString(lAddr)
81         if err != nil {
82                 cmn.PanicCrisis(err)
83         }
84
85         // Determine external address...
86         var extAddr *NetAddress
87         if !skipUPNP {
88                 // If the lAddrIP is INADDR_ANY, try UPnP
89                 if lAddrIP == "" || lAddrIP == "0.0.0.0" {
90                         extAddr = getUPNPExternalAddress(lAddrPort, listenerPort)
91                         if extAddr!=nil {
92                                 mapResult = true
93                         }
94                 }
95         }
96         if extAddr == nil {
97                 if address := GetIP([]string{}, time.Duration(0)); address.Success == true {
98                         extAddr = NewNetAddressIPPort(net.ParseIP(address.Ip), uint16(lAddrPort))
99                 }
100         }
101         // Otherwise just use the local address...
102         if extAddr == nil {
103                 extAddr = getNaiveExternalAddress(listenerPort)
104         }
105         if extAddr == nil {
106                 cmn.PanicCrisis("Could not determine external address!")
107         }
108
109         dl := &DefaultListener{
110                 listener:    listener,
111                 intAddr:     intAddr,
112                 extAddr:     extAddr,
113                 connections: make(chan net.Conn, numBufferedConnections),
114         }
115         dl.BaseService = *cmn.NewBaseService(logger, "DefaultListener", dl)
116         dl.Start() // Started upon construction
117         return dl, mapResult
118 }
119
120 func (l *DefaultListener) OnStart() error {
121         l.BaseService.OnStart()
122         go l.listenRoutine()
123         return nil
124 }
125
126 func (l *DefaultListener) OnStop() {
127         l.BaseService.OnStop()
128         l.listener.Close()
129 }
130
131 // Accept connections and pass on the channel
132 func (l *DefaultListener) listenRoutine() {
133         for {
134                 conn, err := l.listener.Accept()
135
136                 if !l.IsRunning() {
137                         break // Go to cleanup
138                 }
139
140                 // listener wasn't stopped,
141                 // yet we encountered an error.
142                 if err != nil {
143                         cmn.PanicCrisis(err)
144                 }
145
146                 l.connections <- conn
147         }
148
149         // Cleanup
150         close(l.connections)
151         for _ = range l.connections {
152                 // Drain
153         }
154 }
155
156 // A channel of inbound connections.
157 // It gets closed when the listener closes.
158 func (l *DefaultListener) Connections() <-chan net.Conn {
159         return l.connections
160 }
161
162 func (l *DefaultListener) InternalAddress() *NetAddress {
163         return l.intAddr
164 }
165
166 func (l *DefaultListener) ExternalAddress() *NetAddress {
167         return l.extAddr
168 }
169
170 // NOTE: The returned listener is already Accept()'ing.
171 // So it's not suitable to pass into http.Serve().
172 func (l *DefaultListener) NetListener() net.Listener {
173         return l.listener
174 }
175
176 func (l *DefaultListener) String() string {
177         return fmt.Sprintf("Listener(@%v)", l.extAddr)
178 }
179
180 /* external address helpers */
181
182 // UPNP external address discovery & port mapping
183 func getUPNPExternalAddress(externalPort, internalPort int) *NetAddress {
184         log.Info("Getting UPNP external address")
185         nat, err := upnp.Discover()
186         if err != nil {
187                 log.WithField("error", err).Error("Could not perform UPNP discover")
188                 return nil
189         }
190
191         ext, err := nat.GetExternalAddress()
192         if err != nil {
193                 log.WithField("error", err).Error("Could not perform UPNP external address")
194                 return nil
195         }
196
197         // UPnP can't seem to get the external port, so let's just be explicit.
198         if externalPort == 0 {
199                 externalPort = defaultExternalPort
200         }
201
202         externalPort, err = nat.AddPortMapping("tcp", externalPort, internalPort, "bytomd", 0)
203         if err != nil {
204                 log.WithField("error", err).Error("Could not add UPNP port mapping")
205                 return nil
206         }
207
208         log.WithField("address", ext).Info("Got UPNP external address")
209         return NewNetAddressIPPort(ext, uint16(externalPort))
210 }
211
212 // TODO: use syscalls: http://pastebin.com/9exZG4rh
213 func getNaiveExternalAddress(port int) *NetAddress {
214         addrs, err := net.InterfaceAddrs()
215         if err != nil {
216                 cmn.PanicCrisis(cmn.Fmt("Could not fetch interface addresses: %v", err))
217         }
218
219         for _, a := range addrs {
220                 ipnet, ok := a.(*net.IPNet)
221                 if !ok {
222                         continue
223                 }
224                 v4 := ipnet.IP.To4()
225                 if v4 == nil || v4[0] == 127 {
226                         continue
227                 } // loopback
228                 return NewNetAddressIPPort(ipnet.IP, uint16(port))
229         }
230         return nil
231 }