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

     1  package direct
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"net/netip"
     9  	"os"
    10  
    11  	"github.com/database64128/shadowsocks-go/conn"
    12  	"github.com/database64128/shadowsocks-go/socks5"
    13  	"github.com/database64128/shadowsocks-go/zerocopy"
    14  	"go.uber.org/zap"
    15  )
    16  
    17  // DirectUDPClient implements the zerocopy UDPClient interface.
    18  type DirectUDPClient struct {
    19  	info    zerocopy.UDPClientInfo
    20  	session zerocopy.UDPClientSession
    21  }
    22  
    23  // NewDirectUDPClient creates a new UDP client that sends packets directly.
    24  func NewDirectUDPClient(name, network string, mtu int, listenConfig conn.ListenConfig) *DirectUDPClient {
    25  	return &DirectUDPClient{
    26  		info: zerocopy.UDPClientInfo{
    27  			Name:         name,
    28  			MTU:          mtu,
    29  			ListenConfig: listenConfig,
    30  		},
    31  		session: zerocopy.UDPClientSession{
    32  			MaxPacketSize: zerocopy.MaxPacketSizeForAddr(mtu, netip.IPv4Unspecified()),
    33  			Packer:        NewDirectPacketClientPacker(network, mtu),
    34  			Unpacker:      DirectPacketClientUnpacker{},
    35  			Close:         zerocopy.NoopClose,
    36  		},
    37  	}
    38  }
    39  
    40  // Info implements the zerocopy.UDPClient Info method.
    41  func (c *DirectUDPClient) Info() zerocopy.UDPClientInfo {
    42  	return c.info
    43  }
    44  
    45  // NewSession implements the zerocopy.UDPClient NewSession method.
    46  func (c *DirectUDPClient) NewSession(ctx context.Context) (zerocopy.UDPClientInfo, zerocopy.UDPClientSession, error) {
    47  	return c.info, c.session, nil
    48  }
    49  
    50  // ShadowsocksNoneUDPClient implements the zerocopy UDPClient interface.
    51  type ShadowsocksNoneUDPClient struct {
    52  	network string
    53  	addr    conn.Addr
    54  	info    zerocopy.UDPClientInfo
    55  }
    56  
    57  // NewShadowsocksNoneUDPClient creates a new Shadowsocks none UDP client.
    58  func NewShadowsocksNoneUDPClient(name, network string, addr conn.Addr, mtu int, listenConfig conn.ListenConfig) *ShadowsocksNoneUDPClient {
    59  	return &ShadowsocksNoneUDPClient{
    60  		network: network,
    61  		addr:    addr,
    62  		info: zerocopy.UDPClientInfo{
    63  			Name:           name,
    64  			PackerHeadroom: ShadowsocksNonePacketClientMessageHeadroom,
    65  			MTU:            mtu,
    66  			ListenConfig:   listenConfig,
    67  		},
    68  	}
    69  }
    70  
    71  // Info implements the zerocopy.UDPClient Info method.
    72  func (c *ShadowsocksNoneUDPClient) Info() zerocopy.UDPClientInfo {
    73  	return c.info
    74  }
    75  
    76  // NewSession implements the zerocopy.UDPClient NewSession method.
    77  func (c *ShadowsocksNoneUDPClient) NewSession(ctx context.Context) (zerocopy.UDPClientInfo, zerocopy.UDPClientSession, error) {
    78  	addrPort, err := c.addr.ResolveIPPort(ctx, c.network)
    79  	if err != nil {
    80  		return c.info, zerocopy.UDPClientSession{}, fmt.Errorf("failed to resolve endpoint address: %w", err)
    81  	}
    82  	maxPacketSize := zerocopy.MaxPacketSizeForAddr(c.info.MTU, addrPort.Addr())
    83  
    84  	return c.info, zerocopy.UDPClientSession{
    85  		MaxPacketSize: maxPacketSize,
    86  		Packer:        NewShadowsocksNonePacketClientPacker(addrPort, maxPacketSize),
    87  		Unpacker:      NewShadowsocksNonePacketClientUnpacker(addrPort),
    88  		Close:         zerocopy.NoopClose,
    89  	}, nil
    90  }
    91  
    92  // Socks5UDPClient implements the zerocopy UDPClient interface.
    93  type Socks5UDPClient struct {
    94  	logger     *zap.Logger
    95  	networkTCP string
    96  	networkIP  string
    97  	address    string
    98  	dialer     conn.Dialer
    99  	info       zerocopy.UDPClientInfo
   100  }
   101  
   102  // NewSocks5UDPClient creates a new SOCKS5 UDP client.
   103  func NewSocks5UDPClient(logger *zap.Logger, name, networkTCP, networkIP, address string, dialer conn.Dialer, mtu int, listenConfig conn.ListenConfig) *Socks5UDPClient {
   104  	return &Socks5UDPClient{
   105  		logger:     logger,
   106  		networkTCP: networkTCP,
   107  		networkIP:  networkIP,
   108  		address:    address,
   109  		dialer:     dialer,
   110  		info: zerocopy.UDPClientInfo{
   111  			Name:           name,
   112  			PackerHeadroom: Socks5PacketClientMessageHeadroom,
   113  			MTU:            mtu,
   114  			ListenConfig:   listenConfig,
   115  		},
   116  	}
   117  }
   118  
   119  // Info implements the zerocopy.UDPClient Info method.
   120  func (c *Socks5UDPClient) Info() zerocopy.UDPClientInfo {
   121  	return c.info
   122  }
   123  
   124  // NewSession implements the zerocopy.UDPClient NewSession method.
   125  func (c *Socks5UDPClient) NewSession(ctx context.Context) (zerocopy.UDPClientInfo, zerocopy.UDPClientSession, error) {
   126  	tc, err := c.dialer.DialTCP(ctx, c.networkTCP, c.address, nil)
   127  	if err != nil {
   128  		return c.info, zerocopy.UDPClientSession{}, err
   129  	}
   130  
   131  	addr, err := socks5.ClientUDPAssociate(tc, conn.Addr{})
   132  	if err != nil {
   133  		tc.Close()
   134  		return c.info, zerocopy.UDPClientSession{}, fmt.Errorf("failed to request UDP association: %w", err)
   135  	}
   136  
   137  	addrPort, err := addr.ResolveIPPort(ctx, c.networkIP)
   138  	if err != nil {
   139  		tc.Close()
   140  		return c.info, zerocopy.UDPClientSession{}, fmt.Errorf("failed to resolve endpoint address: %w", err)
   141  	}
   142  	maxPacketSize := zerocopy.MaxPacketSizeForAddr(c.info.MTU, addrPort.Addr())
   143  
   144  	go func() {
   145  		b := make([]byte, 1)
   146  		_, err := tc.Read(b)
   147  		switch err {
   148  		case nil, io.EOF:
   149  		default:
   150  			if !errors.Is(err, os.ErrDeadlineExceeded) {
   151  				c.logger.Warn("Failed to keep TCP connection open for UDP association",
   152  					zap.String("client", c.info.Name),
   153  					zap.Error(err),
   154  				)
   155  			}
   156  		}
   157  		tc.Close()
   158  	}()
   159  
   160  	return c.info, zerocopy.UDPClientSession{
   161  		MaxPacketSize: maxPacketSize,
   162  		Packer:        NewSocks5PacketClientPacker(addrPort, maxPacketSize),
   163  		Unpacker:      NewSocks5PacketClientUnpacker(addrPort),
   164  		Close: func() error {
   165  			return tc.SetReadDeadline(conn.ALongTimeAgo)
   166  		},
   167  	}, nil
   168  }
   169  
   170  // DirectUDPNATServer implements the zerocopy UDPNATServer interface.
   171  type DirectUDPNATServer struct {
   172  	p *DirectPacketServerPackUnpacker
   173  }
   174  
   175  func NewDirectUDPNATServer(targetAddr conn.Addr, targetAddrOnly bool) *DirectUDPNATServer {
   176  	return &DirectUDPNATServer{
   177  		p: NewDirectPacketServerPackUnpacker(targetAddr, targetAddrOnly),
   178  	}
   179  }
   180  
   181  // Info implements the zerocopy.UDPNATServer Info method.
   182  func (s *DirectUDPNATServer) Info() zerocopy.UDPNATServerInfo {
   183  	return zerocopy.UDPNATServerInfo{}
   184  }
   185  
   186  // NewUnpacker implements the zerocopy.UDPNATServer NewUnpacker method.
   187  func (s *DirectUDPNATServer) NewUnpacker() (zerocopy.ServerUnpacker, error) {
   188  	return s.p, nil
   189  }
   190  
   191  // ShadowsocksNoneUDPNATServer implements the zerocopy UDPNATServer interface.
   192  type ShadowsocksNoneUDPNATServer struct{}
   193  
   194  // Info implements the zerocopy.UDPNATServer Info method.
   195  func (ShadowsocksNoneUDPNATServer) Info() zerocopy.UDPNATServerInfo {
   196  	return zerocopy.UDPNATServerInfo{
   197  		UnpackerHeadroom: ShadowsocksNonePacketClientMessageHeadroom,
   198  	}
   199  }
   200  
   201  // NewUnpacker implements the zerocopy.UDPNATServer NewUnpacker method.
   202  func (ShadowsocksNoneUDPNATServer) NewUnpacker() (zerocopy.ServerUnpacker, error) {
   203  	return &ShadowsocksNonePacketServerUnpacker{}, nil
   204  }
   205  
   206  // Socks5UDPNATServer implements the zerocopy UDPNATServer interface.
   207  type Socks5UDPNATServer struct{}
   208  
   209  // Info implements the zerocopy.UDPNATServer Info method.
   210  func (Socks5UDPNATServer) Info() zerocopy.UDPNATServerInfo {
   211  	return zerocopy.UDPNATServerInfo{
   212  		UnpackerHeadroom: Socks5PacketClientMessageHeadroom,
   213  	}
   214  }
   215  
   216  // NewUnpacker implements the zerocopy.UDPNATServer NewUnpacker method.
   217  func (Socks5UDPNATServer) NewUnpacker() (zerocopy.ServerUnpacker, error) {
   218  	return &Socks5PacketServerUnpacker{}, nil
   219  }