github.com/DFWallet/tendermint-cosmos@v0.0.2/p2p/transport.go (about) 1 package p2p 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "time" 8 9 "golang.org/x/net/netutil" 10 11 "github.com/DFWallet/tendermint-cosmos/crypto" 12 "github.com/DFWallet/tendermint-cosmos/libs/protoio" 13 "github.com/DFWallet/tendermint-cosmos/p2p/conn" 14 tmp2p "github.com/DFWallet/tendermint-cosmos/proto/tendermint/p2p" 15 ) 16 17 const ( 18 defaultDialTimeout = time.Second 19 defaultFilterTimeout = 5 * time.Second 20 defaultHandshakeTimeout = 3 * time.Second 21 ) 22 23 // IPResolver is a behaviour subset of net.Resolver. 24 type IPResolver interface { 25 LookupIPAddr(context.Context, string) ([]net.IPAddr, error) 26 } 27 28 // accept is the container to carry the upgraded connection and NodeInfo from an 29 // asynchronously running routine to the Accept method. 30 type accept struct { 31 netAddr *NetAddress 32 conn net.Conn 33 nodeInfo NodeInfo 34 err error 35 } 36 37 // peerConfig is used to bundle data we need to fully setup a Peer with an 38 // MConn, provided by the caller of Accept and Dial (currently the Switch). This 39 // a temporary measure until reactor setup is less dynamic and we introduce the 40 // concept of PeerBehaviour to communicate about significant Peer lifecycle 41 // events. 42 // TODO(xla): Refactor out with more static Reactor setup and PeerBehaviour. 43 type peerConfig struct { 44 chDescs []*conn.ChannelDescriptor 45 onPeerError func(Peer, interface{}) 46 outbound bool 47 // isPersistent allows you to set a function, which, given socket address 48 // (for outbound peers) OR self-reported address (for inbound peers), tells 49 // if the peer is persistent or not. 50 isPersistent func(*NetAddress) bool 51 reactorsByCh map[byte]Reactor 52 metrics *Metrics 53 } 54 55 // Transport emits and connects to Peers. The implementation of Peer is left to 56 // the transport. Each transport is also responsible to filter establishing 57 // peers specific to its domain. 58 type Transport interface { 59 // Listening address. 60 NetAddress() NetAddress 61 62 // Accept returns a newly connected Peer. 63 Accept(peerConfig) (Peer, error) 64 65 // Dial connects to the Peer for the address. 66 Dial(NetAddress, peerConfig) (Peer, error) 67 68 // Cleanup any resources associated with Peer. 69 Cleanup(Peer) 70 } 71 72 // transportLifecycle bundles the methods for callers to control start and stop 73 // behaviour. 74 type transportLifecycle interface { 75 Close() error 76 Listen(NetAddress) error 77 } 78 79 // ConnFilterFunc to be implemented by filter hooks after a new connection has 80 // been established. The set of exisiting connections is passed along together 81 // with all resolved IPs for the new connection. 82 type ConnFilterFunc func(ConnSet, net.Conn, []net.IP) error 83 84 // ConnDuplicateIPFilter resolves and keeps all ips for an incoming connection 85 // and refuses new ones if they come from a known ip. 86 func ConnDuplicateIPFilter() ConnFilterFunc { 87 return func(cs ConnSet, c net.Conn, ips []net.IP) error { 88 for _, ip := range ips { 89 if cs.HasIP(ip) { 90 return ErrRejected{ 91 conn: c, 92 err: fmt.Errorf("ip<%v> already connected", ip), 93 isDuplicate: true, 94 } 95 } 96 } 97 98 return nil 99 } 100 } 101 102 // MultiplexTransportOption sets an optional parameter on the 103 // MultiplexTransport. 104 type MultiplexTransportOption func(*MultiplexTransport) 105 106 // MultiplexTransportConnFilters sets the filters for rejection new connections. 107 func MultiplexTransportConnFilters( 108 filters ...ConnFilterFunc, 109 ) MultiplexTransportOption { 110 return func(mt *MultiplexTransport) { mt.connFilters = filters } 111 } 112 113 // MultiplexTransportFilterTimeout sets the timeout waited for filter calls to 114 // return. 115 func MultiplexTransportFilterTimeout( 116 timeout time.Duration, 117 ) MultiplexTransportOption { 118 return func(mt *MultiplexTransport) { mt.filterTimeout = timeout } 119 } 120 121 // MultiplexTransportResolver sets the Resolver used for ip lokkups, defaults to 122 // net.DefaultResolver. 123 func MultiplexTransportResolver(resolver IPResolver) MultiplexTransportOption { 124 return func(mt *MultiplexTransport) { mt.resolver = resolver } 125 } 126 127 // MultiplexTransportMaxIncomingConnections sets the maximum number of 128 // simultaneous connections (incoming). Default: 0 (unlimited) 129 func MultiplexTransportMaxIncomingConnections(n int) MultiplexTransportOption { 130 return func(mt *MultiplexTransport) { mt.maxIncomingConnections = n } 131 } 132 133 // MultiplexTransport accepts and dials tcp connections and upgrades them to 134 // multiplexed peers. 135 type MultiplexTransport struct { 136 netAddr NetAddress 137 listener net.Listener 138 maxIncomingConnections int // see MaxIncomingConnections 139 140 acceptc chan accept 141 closec chan struct{} 142 143 // Lookup table for duplicate ip and id checks. 144 conns ConnSet 145 connFilters []ConnFilterFunc 146 147 dialTimeout time.Duration 148 filterTimeout time.Duration 149 handshakeTimeout time.Duration 150 nodeInfo NodeInfo 151 nodeKey NodeKey 152 resolver IPResolver 153 154 // TODO(xla): This config is still needed as we parameterise peerConn and 155 // peer currently. All relevant configuration should be refactored into options 156 // with sane defaults. 157 mConfig conn.MConnConfig 158 } 159 160 // Test multiplexTransport for interface completeness. 161 var _ Transport = (*MultiplexTransport)(nil) 162 var _ transportLifecycle = (*MultiplexTransport)(nil) 163 164 // NewMultiplexTransport returns a tcp connected multiplexed peer. 165 func NewMultiplexTransport( 166 nodeInfo NodeInfo, 167 nodeKey NodeKey, 168 mConfig conn.MConnConfig, 169 ) *MultiplexTransport { 170 return &MultiplexTransport{ 171 acceptc: make(chan accept), 172 closec: make(chan struct{}), 173 dialTimeout: defaultDialTimeout, 174 filterTimeout: defaultFilterTimeout, 175 handshakeTimeout: defaultHandshakeTimeout, 176 mConfig: mConfig, 177 nodeInfo: nodeInfo, 178 nodeKey: nodeKey, 179 conns: NewConnSet(), 180 resolver: net.DefaultResolver, 181 } 182 } 183 184 // NetAddress implements Transport. 185 func (mt *MultiplexTransport) NetAddress() NetAddress { 186 return mt.netAddr 187 } 188 189 // Accept implements Transport. 190 func (mt *MultiplexTransport) Accept(cfg peerConfig) (Peer, error) { 191 select { 192 // This case should never have any side-effectful/blocking operations to 193 // ensure that quality peers are ready to be used. 194 case a := <-mt.acceptc: 195 if a.err != nil { 196 return nil, a.err 197 } 198 199 cfg.outbound = false 200 201 return mt.wrapPeer(a.conn, a.nodeInfo, cfg, a.netAddr), nil 202 case <-mt.closec: 203 return nil, ErrTransportClosed{} 204 } 205 } 206 207 // Dial implements Transport. 208 func (mt *MultiplexTransport) Dial( 209 addr NetAddress, 210 cfg peerConfig, 211 ) (Peer, error) { 212 c, err := addr.DialTimeout(mt.dialTimeout) 213 if err != nil { 214 return nil, err 215 } 216 217 // TODO(xla): Evaluate if we should apply filters if we explicitly dial. 218 if err := mt.filterConn(c); err != nil { 219 return nil, err 220 } 221 222 secretConn, nodeInfo, err := mt.upgrade(c, &addr) 223 if err != nil { 224 return nil, err 225 } 226 227 cfg.outbound = true 228 229 p := mt.wrapPeer(secretConn, nodeInfo, cfg, &addr) 230 231 return p, nil 232 } 233 234 // Close implements transportLifecycle. 235 func (mt *MultiplexTransport) Close() error { 236 close(mt.closec) 237 238 if mt.listener != nil { 239 return mt.listener.Close() 240 } 241 242 return nil 243 } 244 245 // Listen implements transportLifecycle. 246 func (mt *MultiplexTransport) Listen(addr NetAddress) error { 247 ln, err := net.Listen("tcp", addr.DialString()) 248 if err != nil { 249 return err 250 } 251 252 if mt.maxIncomingConnections > 0 { 253 ln = netutil.LimitListener(ln, mt.maxIncomingConnections) 254 } 255 256 mt.netAddr = addr 257 mt.listener = ln 258 259 go mt.acceptPeers() 260 261 return nil 262 } 263 264 // AddChannel registers a channel to nodeInfo. 265 // NOTE: NodeInfo must be of type DefaultNodeInfo else channels won't be updated 266 // This is a bit messy at the moment but is cleaned up in the following version 267 // when NodeInfo changes from an interface to a concrete type 268 func (mt *MultiplexTransport) AddChannel(chID byte) { 269 if ni, ok := mt.nodeInfo.(DefaultNodeInfo); ok { 270 if !ni.HasChannel(chID) { 271 ni.Channels = append(ni.Channels, chID) 272 } 273 mt.nodeInfo = ni 274 } 275 } 276 277 func (mt *MultiplexTransport) acceptPeers() { 278 for { 279 c, err := mt.listener.Accept() 280 if err != nil { 281 // If Close() has been called, silently exit. 282 select { 283 case _, ok := <-mt.closec: 284 if !ok { 285 return 286 } 287 default: 288 // Transport is not closed 289 } 290 291 mt.acceptc <- accept{err: err} 292 return 293 } 294 295 // Connection upgrade and filtering should be asynchronous to avoid 296 // Head-of-line blocking[0]. 297 // Reference: https://github.com/DFWallet/tendermint-cosmos/issues/2047 298 // 299 // [0] https://en.wikipedia.org/wiki/Head-of-line_blocking 300 go func(c net.Conn) { 301 defer func() { 302 if r := recover(); r != nil { 303 err := ErrRejected{ 304 conn: c, 305 err: fmt.Errorf("recovered from panic: %v", r), 306 isAuthFailure: true, 307 } 308 select { 309 case mt.acceptc <- accept{err: err}: 310 case <-mt.closec: 311 // Give up if the transport was closed. 312 _ = c.Close() 313 return 314 } 315 } 316 }() 317 318 var ( 319 nodeInfo NodeInfo 320 secretConn *conn.SecretConnection 321 netAddr *NetAddress 322 ) 323 324 err := mt.filterConn(c) 325 if err == nil { 326 secretConn, nodeInfo, err = mt.upgrade(c, nil) 327 if err == nil { 328 addr := c.RemoteAddr() 329 id := PubKeyToID(secretConn.RemotePubKey()) 330 netAddr = NewNetAddress(id, addr) 331 } 332 } 333 334 select { 335 case mt.acceptc <- accept{netAddr, secretConn, nodeInfo, err}: 336 // Make the upgraded peer available. 337 case <-mt.closec: 338 // Give up if the transport was closed. 339 _ = c.Close() 340 return 341 } 342 }(c) 343 } 344 } 345 346 // Cleanup removes the given address from the connections set and 347 // closes the connection. 348 func (mt *MultiplexTransport) Cleanup(p Peer) { 349 mt.conns.RemoveAddr(p.RemoteAddr()) 350 _ = p.CloseConn() 351 } 352 353 func (mt *MultiplexTransport) cleanup(c net.Conn) error { 354 mt.conns.Remove(c) 355 356 return c.Close() 357 } 358 359 func (mt *MultiplexTransport) filterConn(c net.Conn) (err error) { 360 defer func() { 361 if err != nil { 362 _ = c.Close() 363 } 364 }() 365 366 // Reject if connection is already present. 367 if mt.conns.Has(c) { 368 return ErrRejected{conn: c, isDuplicate: true} 369 } 370 371 // Resolve ips for incoming conn. 372 ips, err := resolveIPs(mt.resolver, c) 373 if err != nil { 374 return err 375 } 376 377 errc := make(chan error, len(mt.connFilters)) 378 379 for _, f := range mt.connFilters { 380 go func(f ConnFilterFunc, c net.Conn, ips []net.IP, errc chan<- error) { 381 errc <- f(mt.conns, c, ips) 382 }(f, c, ips, errc) 383 } 384 385 for i := 0; i < cap(errc); i++ { 386 select { 387 case err := <-errc: 388 if err != nil { 389 return ErrRejected{conn: c, err: err, isFiltered: true} 390 } 391 case <-time.After(mt.filterTimeout): 392 return ErrFilterTimeout{} 393 } 394 395 } 396 397 mt.conns.Set(c, ips) 398 399 return nil 400 } 401 402 func (mt *MultiplexTransport) upgrade( 403 c net.Conn, 404 dialedAddr *NetAddress, 405 ) (secretConn *conn.SecretConnection, nodeInfo NodeInfo, err error) { 406 defer func() { 407 if err != nil { 408 _ = mt.cleanup(c) 409 } 410 }() 411 412 secretConn, err = upgradeSecretConn(c, mt.handshakeTimeout, mt.nodeKey.PrivKey) 413 if err != nil { 414 return nil, nil, ErrRejected{ 415 conn: c, 416 err: fmt.Errorf("secret conn failed: %v", err), 417 isAuthFailure: true, 418 } 419 } 420 421 // For outgoing conns, ensure connection key matches dialed key. 422 connID := PubKeyToID(secretConn.RemotePubKey()) 423 if dialedAddr != nil { 424 if dialedID := dialedAddr.ID; connID != dialedID { 425 return nil, nil, ErrRejected{ 426 conn: c, 427 id: connID, 428 err: fmt.Errorf( 429 "conn.ID (%v) dialed ID (%v) mismatch", 430 connID, 431 dialedID, 432 ), 433 isAuthFailure: true, 434 } 435 } 436 } 437 438 nodeInfo, err = handshake(secretConn, mt.handshakeTimeout, mt.nodeInfo) 439 if err != nil { 440 return nil, nil, ErrRejected{ 441 conn: c, 442 err: fmt.Errorf("handshake failed: %v", err), 443 isAuthFailure: true, 444 } 445 } 446 447 if err := nodeInfo.Validate(); err != nil { 448 return nil, nil, ErrRejected{ 449 conn: c, 450 err: err, 451 isNodeInfoInvalid: true, 452 } 453 } 454 455 // Ensure connection key matches self reported key. 456 if connID != nodeInfo.ID() { 457 return nil, nil, ErrRejected{ 458 conn: c, 459 id: connID, 460 err: fmt.Errorf( 461 "conn.ID (%v) NodeInfo.ID (%v) mismatch", 462 connID, 463 nodeInfo.ID(), 464 ), 465 isAuthFailure: true, 466 } 467 } 468 469 // Reject self. 470 if mt.nodeInfo.ID() == nodeInfo.ID() { 471 return nil, nil, ErrRejected{ 472 addr: *NewNetAddress(nodeInfo.ID(), c.RemoteAddr()), 473 conn: c, 474 id: nodeInfo.ID(), 475 isSelf: true, 476 } 477 } 478 479 if err := mt.nodeInfo.CompatibleWith(nodeInfo); err != nil { 480 return nil, nil, ErrRejected{ 481 conn: c, 482 err: err, 483 id: nodeInfo.ID(), 484 isIncompatible: true, 485 } 486 } 487 488 return secretConn, nodeInfo, nil 489 } 490 491 func (mt *MultiplexTransport) wrapPeer( 492 c net.Conn, 493 ni NodeInfo, 494 cfg peerConfig, 495 socketAddr *NetAddress, 496 ) Peer { 497 498 persistent := false 499 if cfg.isPersistent != nil { 500 if cfg.outbound { 501 persistent = cfg.isPersistent(socketAddr) 502 } else { 503 selfReportedAddr, err := ni.NetAddress() 504 if err == nil { 505 persistent = cfg.isPersistent(selfReportedAddr) 506 } 507 } 508 } 509 510 peerConn := newPeerConn( 511 cfg.outbound, 512 persistent, 513 c, 514 socketAddr, 515 ) 516 517 p := newPeer( 518 peerConn, 519 mt.mConfig, 520 ni, 521 cfg.reactorsByCh, 522 cfg.chDescs, 523 cfg.onPeerError, 524 PeerMetrics(cfg.metrics), 525 ) 526 527 return p 528 } 529 530 func handshake( 531 c net.Conn, 532 timeout time.Duration, 533 nodeInfo NodeInfo, 534 ) (NodeInfo, error) { 535 if err := c.SetDeadline(time.Now().Add(timeout)); err != nil { 536 return nil, err 537 } 538 539 var ( 540 errc = make(chan error, 2) 541 542 pbpeerNodeInfo tmp2p.DefaultNodeInfo 543 peerNodeInfo DefaultNodeInfo 544 ourNodeInfo = nodeInfo.(DefaultNodeInfo) 545 ) 546 547 go func(errc chan<- error, c net.Conn) { 548 _, err := protoio.NewDelimitedWriter(c).WriteMsg(ourNodeInfo.ToProto()) 549 errc <- err 550 }(errc, c) 551 go func(errc chan<- error, c net.Conn) { 552 protoReader := protoio.NewDelimitedReader(c, MaxNodeInfoSize()) 553 _, err := protoReader.ReadMsg(&pbpeerNodeInfo) 554 errc <- err 555 }(errc, c) 556 557 for i := 0; i < cap(errc); i++ { 558 err := <-errc 559 if err != nil { 560 return nil, err 561 } 562 } 563 564 peerNodeInfo, err := DefaultNodeInfoFromToProto(&pbpeerNodeInfo) 565 if err != nil { 566 return nil, err 567 } 568 569 return peerNodeInfo, c.SetDeadline(time.Time{}) 570 } 571 572 func upgradeSecretConn( 573 c net.Conn, 574 timeout time.Duration, 575 privKey crypto.PrivKey, 576 ) (*conn.SecretConnection, error) { 577 if err := c.SetDeadline(time.Now().Add(timeout)); err != nil { 578 return nil, err 579 } 580 581 sc, err := conn.MakeSecretConnection(c, privKey) 582 if err != nil { 583 return nil, err 584 } 585 586 return sc, sc.SetDeadline(time.Time{}) 587 } 588 589 func resolveIPs(resolver IPResolver, c net.Conn) ([]net.IP, error) { 590 host, _, err := net.SplitHostPort(c.RemoteAddr().String()) 591 if err != nil { 592 return nil, err 593 } 594 595 addrs, err := resolver.LookupIPAddr(context.Background(), host) 596 if err != nil { 597 return nil, err 598 } 599 600 ips := []net.IP{} 601 602 for _, addr := range addrs { 603 ips = append(ips, addr.IP) 604 } 605 606 return ips, nil 607 }