github.com/database64128/shadowsocks-go@v1.10.2-0.20240315062903-143a773533f1/conn/conn.go (about)

     1  package conn
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"syscall"
     7  
     8  	"github.com/database64128/tfo-go/v2"
     9  )
    10  
    11  type setFunc = func(fd int, network string) error
    12  
    13  type setFuncSlice []setFunc
    14  
    15  func (fns setFuncSlice) controlContextFunc() func(ctx context.Context, network, address string, c syscall.RawConn) error {
    16  	if len(fns) == 0 {
    17  		return nil
    18  	}
    19  	return func(ctx context.Context, network, address string, c syscall.RawConn) (err error) {
    20  		if cerr := c.Control(func(fd uintptr) {
    21  			for _, fn := range fns {
    22  				if err = fn(int(fd), network); err != nil {
    23  					return
    24  				}
    25  			}
    26  		}); cerr != nil {
    27  			return cerr
    28  		}
    29  		return
    30  	}
    31  }
    32  
    33  func (fns setFuncSlice) controlFunc() func(network, address string, c syscall.RawConn) error {
    34  	if len(fns) == 0 {
    35  		return nil
    36  	}
    37  	return func(network, address string, c syscall.RawConn) (err error) {
    38  		if cerr := c.Control(func(fd uintptr) {
    39  			for _, fn := range fns {
    40  				if err = fn(int(fd), network); err != nil {
    41  					return
    42  				}
    43  			}
    44  		}); cerr != nil {
    45  			return cerr
    46  		}
    47  		return
    48  	}
    49  }
    50  
    51  // ListenConfig is [tfo.ListenConfig] but provides a subjectively nicer API.
    52  type ListenConfig tfo.ListenConfig
    53  
    54  // ListenTCP wraps [tfo.ListenConfig.Listen] and returns a [*net.TCPListener] directly.
    55  func (lc *ListenConfig) ListenTCP(ctx context.Context, network, address string) (*net.TCPListener, error) {
    56  	l, err := (*tfo.ListenConfig)(lc).Listen(ctx, network, address)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	return l.(*net.TCPListener), nil
    61  }
    62  
    63  // ListenUDP wraps [net.ListenConfig.ListenPacket] and returns a [*net.UDPConn] directly.
    64  func (lc *ListenConfig) ListenUDP(ctx context.Context, network, address string) (*net.UDPConn, error) {
    65  	pc, err := lc.ListenConfig.ListenPacket(ctx, network, address)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	return pc.(*net.UDPConn), nil
    70  }
    71  
    72  // ListenerSocketOptions contains listener-specific socket options.
    73  type ListenerSocketOptions struct {
    74  	// Fwmark sets the listener's fwmark on Linux, or user cookie on FreeBSD.
    75  	//
    76  	// Available on Linux and FreeBSD.
    77  	Fwmark int
    78  
    79  	// TrafficClass sets the traffic class of the listener.
    80  	//
    81  	// Available on most platforms except Windows.
    82  	TrafficClass int
    83  
    84  	// TCPFastOpenBacklog specifies the maximum number of pending TFO connections on Linux.
    85  	// If the value is 0, Go std's listen(2) backlog is used.
    86  	//
    87  	// On other platforms, a non-negative value is ignored, as they do not have the option to set the TFO backlog.
    88  	//
    89  	// On all platforms, a negative value disables TFO.
    90  	TCPFastOpenBacklog int
    91  
    92  	// ReusePort enables SO_REUSEPORT on the listener.
    93  	//
    94  	// Available on Linux and the BSDs.
    95  	ReusePort bool
    96  
    97  	// Transparent enables transparent proxy on the listener.
    98  	//
    99  	// Only available on Linux.
   100  	Transparent bool
   101  
   102  	// PathMTUDiscovery enables Path MTU Discovery on the listener.
   103  	//
   104  	// Available on Linux, macOS, FreeBSD, and Windows.
   105  	PathMTUDiscovery bool
   106  
   107  	// TCPFastOpen enables TCP Fast Open on the listener.
   108  	//
   109  	// Available on Linux, macOS, FreeBSD, and Windows.
   110  	TCPFastOpen bool
   111  
   112  	// TCPFastOpenFallback enables runtime detection of TCP Fast Open support on the listener.
   113  	//
   114  	// When enabled, the listener will start without TFO if TFO is not available on the system.
   115  	// When disabled, the listener will abort if TFO cannot be enabled on the socket.
   116  	//
   117  	// Available on all platforms.
   118  	TCPFastOpenFallback bool
   119  
   120  	// MultipathTCP enables multipath TCP on the listener.
   121  	//
   122  	// Unlike Go std, we make MPTCP strictly opt-in.
   123  	// That is, if this field is false, MPTCP will be explicitly disabled.
   124  	// This ensures that if Go std suddenly decides to enable MPTCP by default,
   125  	// existing configurations won't encounter issues due to missing features in the kernel MPTCP stack,
   126  	// such as TCP keepalive (as of Linux 6.5), and failed connect attempts won't always be retried once.
   127  	//
   128  	// Available on platforms supported by Go std's MPTCP implementation.
   129  	MultipathTCP bool
   130  
   131  	// ReceivePacketInfo enables the reception of packet information control messages on the listener.
   132  	//
   133  	// Available on Linux, macOS, and Windows.
   134  	ReceivePacketInfo bool
   135  
   136  	// ReceiveOriginalDestAddr enables the reception of original destination address control messages on the listener.
   137  	//
   138  	// Only available on Linux.
   139  	ReceiveOriginalDestAddr bool
   140  }
   141  
   142  // ListenConfig returns a [ListenConfig] with a control function that sets the socket options.
   143  func (lso ListenerSocketOptions) ListenConfig() ListenConfig {
   144  	lc := ListenConfig{
   145  		ListenConfig: net.ListenConfig{
   146  			Control: lso.buildSetFns().controlFunc(),
   147  		},
   148  		Backlog:    lso.TCPFastOpenBacklog,
   149  		DisableTFO: !lso.TCPFastOpen,
   150  		Fallback:   lso.TCPFastOpenFallback,
   151  	}
   152  	lc.SetMultipathTCP(lso.MultipathTCP)
   153  	return lc
   154  }
   155  
   156  var (
   157  	// DefaultTCPListenerSocketOptions is the default [ListenerSocketOptions] for TCP servers.
   158  	DefaultTCPListenerSocketOptions = ListenerSocketOptions{
   159  		TCPFastOpen: true,
   160  	}
   161  
   162  	// DefaultTCPListenConfig is the default [ListenConfig] for TCP listeners.
   163  	DefaultTCPListenConfig = DefaultTCPListenerSocketOptions.ListenConfig()
   164  
   165  	// DefaultUDPServerSocketOptions is the default [ListenerSocketOptions] for UDP servers.
   166  	DefaultUDPServerSocketOptions = ListenerSocketOptions{
   167  		PathMTUDiscovery:  true,
   168  		ReceivePacketInfo: true,
   169  	}
   170  
   171  	// DefaultUDPServerListenConfig is the default [ListenConfig] for UDP servers.
   172  	DefaultUDPServerListenConfig = DefaultUDPServerSocketOptions.ListenConfig()
   173  
   174  	// DefaultUDPClientSocketOptions is the default [ListenerSocketOptions] for UDP clients.
   175  	DefaultUDPClientSocketOptions = ListenerSocketOptions{
   176  		PathMTUDiscovery: true,
   177  	}
   178  
   179  	// DefaultUDPClientListenConfig is the default [ListenConfig] for UDP clients.
   180  	DefaultUDPClientListenConfig = DefaultUDPClientSocketOptions.ListenConfig()
   181  )
   182  
   183  // Dialer is [tfo.Dialer] but provides a subjectively nicer API.
   184  type Dialer tfo.Dialer
   185  
   186  // DialTCP wraps [tfo.Dialer.DialContext] and returns a [*net.TCPConn] directly.
   187  func (d *Dialer) DialTCP(ctx context.Context, network, address string, b []byte) (*net.TCPConn, error) {
   188  	c, err := (*tfo.Dialer)(d).DialContext(ctx, network, address, b)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  	return c.(*net.TCPConn), nil
   193  }
   194  
   195  // DialUDP wraps [net.Dialer.DialContext] and returns a [*net.UDPConn] directly.
   196  func (d *Dialer) DialUDP(ctx context.Context, network, address string) (*net.UDPConn, error) {
   197  	c, err := d.Dialer.DialContext(ctx, network, address)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  	return c.(*net.UDPConn), nil
   202  }
   203  
   204  // DialerSocketOptions contains dialer-specific socket options.
   205  type DialerSocketOptions struct {
   206  	// Fwmark sets the dialer's fwmark on Linux, or user cookie on FreeBSD.
   207  	//
   208  	// Available on Linux and FreeBSD.
   209  	Fwmark int
   210  
   211  	// TrafficClass sets the traffic class of the dialer.
   212  	//
   213  	// Available on most platforms except Windows.
   214  	TrafficClass int
   215  
   216  	// TCPFastOpen enables TCP Fast Open on the dialer.
   217  	//
   218  	// Available on Linux, macOS, FreeBSD, and Windows.
   219  	TCPFastOpen bool
   220  
   221  	// TCPFastOpenFallback enables runtime detection of TCP Fast Open support on the dialer.
   222  	//
   223  	// When enabled, the dialer will connect without TFO if TFO is not available on the system.
   224  	// When disabled, the dialer will abort if TFO cannot be enabled on the socket.
   225  	//
   226  	// Available on all platforms.
   227  	TCPFastOpenFallback bool
   228  
   229  	// MultipathTCP enables multipath TCP on the dialer.
   230  	//
   231  	// Unlike Go std, we make MPTCP strictly opt-in.
   232  	// That is, if this field is false, MPTCP will be explicitly disabled.
   233  	// This ensures that if Go std suddenly decides to enable MPTCP by default,
   234  	// existing configurations won't encounter issues due to missing features in the kernel MPTCP stack,
   235  	// such as TCP keepalive (as of Linux 6.5), and failed connect attempts won't always be retried once.
   236  	//
   237  	// Available on platforms supported by Go std's MPTCP implementation.
   238  	MultipathTCP bool
   239  }
   240  
   241  // Dialer returns a [Dialer] with a control function that sets the socket options.
   242  func (dso DialerSocketOptions) Dialer() Dialer {
   243  	d := Dialer{
   244  		Dialer: net.Dialer{
   245  			ControlContext: dso.buildSetFns().controlContextFunc(),
   246  		},
   247  		DisableTFO: !dso.TCPFastOpen,
   248  		Fallback:   dso.TCPFastOpenFallback,
   249  	}
   250  	d.SetMultipathTCP(dso.MultipathTCP)
   251  	return d
   252  }
   253  
   254  var (
   255  	// DefaultTCPDialerSocketOptions is the default [DialerSocketOptions] for TCP clients.
   256  	DefaultTCPDialerSocketOptions = DialerSocketOptions{
   257  		TCPFastOpen: true,
   258  	}
   259  
   260  	// DefaultTCPDialer is the default [Dialer] for TCP clients.
   261  	DefaultTCPDialer = DefaultTCPDialerSocketOptions.Dialer()
   262  )
   263  
   264  // ListenConfigCache is a map of [ListenerSocketOptions] to [ListenConfig].
   265  type ListenConfigCache map[ListenerSocketOptions]ListenConfig
   266  
   267  // NewListenConfigCache creates a new cache for [ListenConfig] with a few default entries.
   268  func NewListenConfigCache() ListenConfigCache {
   269  	return ListenConfigCache{
   270  		DefaultTCPListenerSocketOptions: DefaultTCPListenConfig,
   271  		DefaultUDPServerSocketOptions:   DefaultUDPServerListenConfig,
   272  		DefaultUDPClientSocketOptions:   DefaultUDPClientListenConfig,
   273  	}
   274  }
   275  
   276  // Get returns a [ListenConfig] for the given [ListenerSocketOptions].
   277  func (cache ListenConfigCache) Get(lso ListenerSocketOptions) (lc ListenConfig) {
   278  	lc, ok := cache[lso]
   279  	if ok {
   280  		return
   281  	}
   282  	lc = lso.ListenConfig()
   283  	cache[lso] = lc
   284  	return
   285  }
   286  
   287  // DialerCache is a map of [DialerSocketOptions] to [Dialer].
   288  type DialerCache map[DialerSocketOptions]Dialer
   289  
   290  // NewDialerCache creates a new cache for [Dialer] with a few default entries.
   291  func NewDialerCache() DialerCache {
   292  	return DialerCache{
   293  		DefaultTCPDialerSocketOptions: DefaultTCPDialer,
   294  	}
   295  }
   296  
   297  // Get returns a [Dialer] for the given [DialerSocketOptions].
   298  func (cache DialerCache) Get(dso DialerSocketOptions) (d Dialer) {
   299  	d, ok := cache[dso]
   300  	if ok {
   301  		return
   302  	}
   303  	d = dso.Dialer()
   304  	cache[dso] = d
   305  	return
   306  }