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