1package tcp 2 3import ( 4 "context" 5 "net" 6 "time" 7 8 logging "github.com/ipfs/go-log" 9 "github.com/libp2p/go-libp2p-core/peer" 10 "github.com/libp2p/go-libp2p-core/transport" 11 tptu "github.com/libp2p/go-libp2p-transport-upgrader" 12 rtpt "github.com/libp2p/go-reuseport-transport" 13 14 ma "github.com/multiformats/go-multiaddr" 15 mafmt "github.com/multiformats/go-multiaddr-fmt" 16 manet "github.com/multiformats/go-multiaddr-net" 17) 18 19// DefaultConnectTimeout is the (default) maximum amount of time the TCP 20// transport will spend on the initial TCP connect before giving up. 21var DefaultConnectTimeout = 5 * time.Second 22 23var log = logging.Logger("tcp-tpt") 24 25// try to set linger on the connection, if possible. 26func tryLinger(conn net.Conn, sec int) { 27 type canLinger interface { 28 SetLinger(int) error 29 } 30 31 if lingerConn, ok := conn.(canLinger); ok { 32 _ = lingerConn.SetLinger(sec) 33 } 34} 35 36type lingerListener struct { 37 manet.Listener 38 sec int 39} 40 41func (ll *lingerListener) Accept() (manet.Conn, error) { 42 c, err := ll.Listener.Accept() 43 if err != nil { 44 return nil, err 45 } 46 tryLinger(c, ll.sec) 47 return c, nil 48} 49 50// TcpTransport is the TCP transport. 51type TcpTransport struct { 52 // Connection upgrader for upgrading insecure stream connections to 53 // secure multiplex connections. 54 Upgrader *tptu.Upgrader 55 56 // Explicitly disable reuseport. 57 DisableReuseport bool 58 59 // TCP connect timeout 60 ConnectTimeout time.Duration 61 62 reuse rtpt.Transport 63} 64 65var _ transport.Transport = &TcpTransport{} 66 67// NewTCPTransport creates a tcp transport object that tracks dialers and listeners 68// created. It represents an entire tcp stack (though it might not necessarily be) 69func NewTCPTransport(upgrader *tptu.Upgrader) *TcpTransport { 70 return &TcpTransport{Upgrader: upgrader, ConnectTimeout: DefaultConnectTimeout} 71} 72 73var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_TCP)) 74 75// CanDial returns true if this transport believes it can dial the given 76// multiaddr. 77func (t *TcpTransport) CanDial(addr ma.Multiaddr) bool { 78 return dialMatcher.Matches(addr) 79} 80 81func (t *TcpTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (manet.Conn, error) { 82 // Apply the deadline iff applicable 83 if t.ConnectTimeout > 0 { 84 deadline := time.Now().Add(t.ConnectTimeout) 85 if d, ok := ctx.Deadline(); !ok || deadline.Before(d) { 86 var cancel func() 87 ctx, cancel = context.WithDeadline(ctx, deadline) 88 defer cancel() 89 } 90 } 91 92 if t.UseReuseport() { 93 return t.reuse.DialContext(ctx, raddr) 94 } 95 var d manet.Dialer 96 return d.DialContext(ctx, raddr) 97} 98 99// Dial dials the peer at the remote address. 100func (t *TcpTransport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (transport.CapableConn, error) { 101 conn, err := t.maDial(ctx, raddr) 102 if err != nil { 103 return nil, err 104 } 105 // Set linger to 0 so we never get stuck in the TIME-WAIT state. When 106 // linger is 0, connections are _reset_ instead of closed with a FIN. 107 // This means we can immediately reuse the 5-tuple and reconnect. 108 tryLinger(conn, 0) 109 return t.Upgrader.UpgradeOutbound(ctx, t, conn, p) 110} 111 112// UseReuseport returns true if reuseport is enabled and available. 113func (t *TcpTransport) UseReuseport() bool { 114 return !t.DisableReuseport && ReuseportIsAvailable() 115} 116 117func (t *TcpTransport) maListen(laddr ma.Multiaddr) (manet.Listener, error) { 118 if t.UseReuseport() { 119 return t.reuse.Listen(laddr) 120 } 121 return manet.Listen(laddr) 122} 123 124// Listen listens on the given multiaddr. 125func (t *TcpTransport) Listen(laddr ma.Multiaddr) (transport.Listener, error) { 126 list, err := t.maListen(laddr) 127 if err != nil { 128 return nil, err 129 } 130 list = &lingerListener{list, 0} 131 return t.Upgrader.UpgradeListener(t, list), nil 132} 133 134// Protocols returns the list of terminal protocols this transport can dial. 135func (t *TcpTransport) Protocols() []int { 136 return []int{ma.P_TCP} 137} 138 139// Proxy always returns false for the TCP transport. 140func (t *TcpTransport) Proxy() bool { 141 return false 142} 143 144func (t *TcpTransport) String() string { 145 return "TCP" 146} 147