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