neoneo-go/pkg/network/tcp_transport.go
Roman Khimov 31948ee4a7 network: add a nil check in (*TCPTransport).Close, prevent panic
You have to try to trigger that, but in the event of server shutdown before it
actually started listening Close will easily panic like this:

2020-03-05T11:48:08.660+0300    INFO    node started    {"blockHeight": 6458, "headerHeight": 15605}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x28 pc=0x9d5f0a]

goroutine 1 [running]:
github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Close(0xc000fd7050)
        /home/rik/dev/neo-go/pkg/network/tcp_transport.go:78 +0x2a
github.com/nspcc-dev/neo-go/pkg/network.(*Server).Shutdown(0xc000182280)
        /home/rik/dev/neo-go/pkg/network/server.go:190 +0x189
github.com/nspcc-dev/neo-go/cli/server.startServer(0xc000200160, 0x0, 0x0)
        /home/rik/dev/neo-go/cli/server/server.go:367 +0x79e
github.com/urfave/cli.HandleAction(0xb84860, 0xccf240, 0xc000200160, 0xc0001c4500, 0x0)
        /home/rik/dev/neo-go/vendor/github.com/urfave/cli/app.go:490 +0xc8
github.com/urfave/cli.Command.Run(0xc9a160, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0xca407f, 0x10, 0x0, ...)
        /home/rik/dev/neo-go/vendor/github.com/urfave/cli/command.go:210 +0x996
github.com/urfave/cli.(*App).Run(0xc0001d64e0, 0xc0000ca000, 0x3, 0x3, 0x0, 0x0)
        /home/rik/dev/neo-go/vendor/github.com/urfave/cli/app.go:255 +0x6af
main.main()
        /home/rik/dev/neo-go/cli/main.go:25 +0x505
2020-03-05 11:53:26 +03:00

86 lines
1.7 KiB
Go

package network
import (
"net"
"regexp"
"time"
"go.uber.org/zap"
)
// TCPTransport allows network communication over TCP.
type TCPTransport struct {
log *zap.Logger
server *Server
listener net.Listener
bindAddr string
}
var reClosedNetwork = regexp.MustCompile(".* use of closed network connection")
// NewTCPTransport returns a new TCPTransport that will listen for
// new incoming peer connections.
func NewTCPTransport(s *Server, bindAddr string, log *zap.Logger) *TCPTransport {
return &TCPTransport{
log: log,
server: s,
bindAddr: bindAddr,
}
}
// Dial implements the Transporter interface.
func (t *TCPTransport) Dial(addr string, timeout time.Duration) error {
conn, err := net.DialTimeout("tcp", addr, timeout)
if err != nil {
return err
}
p := NewTCPPeer(conn, t.server)
go p.handleConn()
return nil
}
// Accept implements the Transporter interface.
func (t *TCPTransport) Accept() {
l, err := net.Listen("tcp", t.bindAddr)
if err != nil {
t.log.Panic("TCP listen error", zap.Error(err))
return
}
t.listener = l
for {
conn, err := l.Accept()
if err != nil {
t.log.Warn("TCP accept error", zap.Error(err))
if t.isCloseError(err) {
break
}
continue
}
p := NewTCPPeer(conn, t.server)
go p.handleConn()
}
}
func (t *TCPTransport) isCloseError(err error) bool {
if opErr, ok := err.(*net.OpError); ok {
if reClosedNetwork.Match([]byte(opErr.Error())) {
return true
}
}
return false
}
// Close implements the Transporter interface.
func (t *TCPTransport) Close() {
if t.listener != nil {
t.listener.Close()
}
}
// Proto implements the Transporter interface.
func (t *TCPTransport) Proto() string {
return "tcp"
}