github.com/database64128/shadowsocks-go@v1.7.0/direct/packet.go (about)

     1  package direct
     2  
     3  import (
     4  	"fmt"
     5  	"net/netip"
     6  
     7  	"github.com/database64128/shadowsocks-go/conn"
     8  	"github.com/database64128/shadowsocks-go/socks5"
     9  	"github.com/database64128/shadowsocks-go/zerocopy"
    10  )
    11  
    12  // DirectPacketClientPackUnpacker packs and unpacks packets for direct connection.
    13  //
    14  // DirectPacketClientPackUnpacker implements the zerocopy ClientPacker and Unpacker interfaces.
    15  type DirectPacketClientPackUnpacker struct {
    16  	// cachedDomain caches the last used domain target to avoid excessive DNS lookups.
    17  	cachedDomain string
    18  
    19  	// cachedDomainIP is the last used domain target's resolved IP address.
    20  	cachedDomainIP netip.Addr
    21  
    22  	// mtu is used in the PackInPlace method to determine whether the payload is too big.
    23  	mtu int
    24  }
    25  
    26  // NewDirectPacketClientPackUnpacker creates a zerocopy.ClientPackUnpacker for direct connection.
    27  func NewDirectPacketClientPackUnpacker(mtu int) *DirectPacketClientPackUnpacker {
    28  	return &DirectPacketClientPackUnpacker{
    29  		mtu: mtu,
    30  	}
    31  }
    32  
    33  // ClientPackerInfo implements the zerocopy.ClientPacker ClientPackerInfo method.
    34  func (p *DirectPacketClientPackUnpacker) ClientPackerInfo() zerocopy.ClientPackerInfo {
    35  	return zerocopy.ClientPackerInfo{}
    36  }
    37  
    38  func (p *DirectPacketClientPackUnpacker) updateDomainIPCache(targetAddr conn.Addr) error {
    39  	if p.cachedDomain != targetAddr.Domain() {
    40  		ip, err := targetAddr.ResolveIP()
    41  		if err != nil {
    42  			return err
    43  		}
    44  		p.cachedDomain = targetAddr.Domain()
    45  		p.cachedDomainIP = ip
    46  	}
    47  	return nil
    48  }
    49  
    50  // PackInPlace implements the zerocopy.ClientPacker PackInPlace method.
    51  func (p *DirectPacketClientPackUnpacker) PackInPlace(b []byte, targetAddr conn.Addr, payloadStart, payloadLen int) (destAddrPort netip.AddrPort, packetStart, packetLen int, err error) {
    52  	if targetAddr.IsIP() {
    53  		destAddrPort = targetAddr.IPPort()
    54  	} else {
    55  		err = p.updateDomainIPCache(targetAddr)
    56  		if err != nil {
    57  			return
    58  		}
    59  		destAddrPort = netip.AddrPortFrom(p.cachedDomainIP, targetAddr.Port())
    60  	}
    61  	packetStart = payloadStart
    62  	packetLen = payloadLen
    63  	maxPacketLen := zerocopy.MaxPacketSizeForAddr(p.mtu, destAddrPort.Addr())
    64  	if packetLen > maxPacketLen {
    65  		err = zerocopy.ErrPayloadTooBig
    66  	}
    67  	return
    68  }
    69  
    70  // ClientUnpackerInfo implements the zerocopy.ClientUnpacker ClientUnpackerInfo method.
    71  func (p *DirectPacketClientPackUnpacker) ClientUnpackerInfo() zerocopy.ClientUnpackerInfo {
    72  	return zerocopy.ClientUnpackerInfo{}
    73  }
    74  
    75  // UnpackInPlace implements the zerocopy.ClientUnpacker UnpackInPlace method.
    76  func (p *DirectPacketClientPackUnpacker) UnpackInPlace(b []byte, packetSourceAddrPort netip.AddrPort, packetStart, packetLen int) (payloadSourceAddr netip.AddrPort, payloadStart, payloadLen int, err error) {
    77  	payloadSourceAddr = packetSourceAddrPort
    78  	payloadStart = packetStart
    79  	payloadLen = packetLen
    80  	return
    81  }
    82  
    83  type DirectPacketServerPackUnpacker struct {
    84  	// targetAddr is the address to which packets are forwarded by the direct server.
    85  	targetAddr conn.Addr
    86  
    87  	// targetAddrOnly controls whether to discard packets from non-target sources.
    88  	targetAddrOnly bool
    89  }
    90  
    91  // NewDirectPacketServerPackUnpacker creates a zerocopy.ServerPackUnpacker that forwards packets to the specified target address.
    92  func NewDirectPacketServerPackUnpacker(targetAddr conn.Addr, targetAddrOnly bool) *DirectPacketServerPackUnpacker {
    93  	return &DirectPacketServerPackUnpacker{
    94  		targetAddr:     targetAddr,
    95  		targetAddrOnly: targetAddrOnly,
    96  	}
    97  }
    98  
    99  // ServerPackerInfo implements the zerocopy.ServerPacker ServerPackerInfo method.
   100  func (p *DirectPacketServerPackUnpacker) ServerPackerInfo() zerocopy.ServerPackerInfo {
   101  	return zerocopy.ServerPackerInfo{}
   102  }
   103  
   104  // PackInPlace implements the zerocopy.ServerPacker PackInPlace method.
   105  func (p *DirectPacketServerPackUnpacker) PackInPlace(b []byte, sourceAddrPort netip.AddrPort, payloadStart, payloadLen, maxPacketLen int) (packetStart, packetLen int, err error) {
   106  	packetStart = payloadStart
   107  	packetLen = payloadLen
   108  	if packetLen > maxPacketLen {
   109  		err = zerocopy.ErrPayloadTooBig
   110  	}
   111  	if p.targetAddrOnly && !conn.AddrPortMappedEqual(sourceAddrPort, p.targetAddr.IPPort()) {
   112  		err = fmt.Errorf("dropped packet from non-target source %s", sourceAddrPort)
   113  	}
   114  	return
   115  }
   116  
   117  // ServerUnpackerInfo implements the zerocopy.ServerUnpacker ServerUnpackerInfo method.
   118  func (p *DirectPacketServerPackUnpacker) ServerUnpackerInfo() zerocopy.ServerUnpackerInfo {
   119  	return zerocopy.ServerUnpackerInfo{}
   120  }
   121  
   122  // UnpackInPlace implements the zerocopy.ServerUnpacker UnpackInPlace method.
   123  func (p *DirectPacketServerPackUnpacker) UnpackInPlace(b []byte, sourceAddrPort netip.AddrPort, packetStart, packetLen int) (targetAddr conn.Addr, payloadStart, payloadLen int, err error) {
   124  	targetAddr = p.targetAddr
   125  	payloadStart = packetStart
   126  	payloadLen = packetLen
   127  	return
   128  }
   129  
   130  // ShadowsocksNonePacketClientMessageHeadroom is the headroom required by a Shadowsocks none client message.
   131  var ShadowsocksNonePacketClientMessageHeadroom = zerocopy.Headroom{
   132  	Front: socks5.MaxAddrLen,
   133  	Rear:  0,
   134  }
   135  
   136  // ShadowsocksNonePacketServerMessageHeadroom is the headroom required by a Shadowsocks none server message.
   137  var ShadowsocksNonePacketServerMessageHeadroom = zerocopy.Headroom{
   138  	Front: socks5.IPv6AddrLen,
   139  	Rear:  0,
   140  }
   141  
   142  // ShadowsocksNonePacketClientPacker implements the zerocopy ClientPacker interface.
   143  type ShadowsocksNonePacketClientPacker struct {
   144  	// serverAddrPort is the Shadowsocks none server's IP and port.
   145  	serverAddrPort netip.AddrPort
   146  
   147  	// maxPacketSize is the maximum allowed size of a packed packet.
   148  	// The value is calculated from MTU and server address family.
   149  	maxPacketSize int
   150  }
   151  
   152  // NewShadowsocksNonePacketClientPacker creates a Shadowsocks none packet client packer.
   153  func NewShadowsocksNonePacketClientPacker(serverAddrPort netip.AddrPort, maxPacketSize int) *ShadowsocksNonePacketClientPacker {
   154  	return &ShadowsocksNonePacketClientPacker{
   155  		serverAddrPort: serverAddrPort,
   156  		maxPacketSize:  maxPacketSize,
   157  	}
   158  }
   159  
   160  // ClientPackerInfo implements the zerocopy.ClientPacker ClientPackerInfo method.
   161  func (p *ShadowsocksNonePacketClientPacker) ClientPackerInfo() zerocopy.ClientPackerInfo {
   162  	return zerocopy.ClientPackerInfo{
   163  		Headroom: ShadowsocksNonePacketClientMessageHeadroom,
   164  	}
   165  }
   166  
   167  // PackInPlace implements the zerocopy.ClientPacker PackInPlace method.
   168  func (p *ShadowsocksNonePacketClientPacker) PackInPlace(b []byte, targetAddr conn.Addr, payloadStart, payloadLen int) (destAddrPort netip.AddrPort, packetStart, packetLen int, err error) {
   169  	targetAddrLen := socks5.LengthOfAddrFromConnAddr(targetAddr)
   170  	destAddrPort = p.serverAddrPort
   171  	packetStart = payloadStart - targetAddrLen
   172  	packetLen = payloadLen + targetAddrLen
   173  	if packetLen > p.maxPacketSize {
   174  		err = zerocopy.ErrPayloadTooBig
   175  	}
   176  	socks5.WriteAddrFromConnAddr(b[packetStart:], targetAddr)
   177  	return
   178  }
   179  
   180  // ShadowsocksNonePacketClientUnpacker implements the zerocopy ClientUnpacker interface.
   181  type ShadowsocksNonePacketClientUnpacker struct {
   182  	// serverAddrPort is the Shadowsocks none server's IP and port.
   183  	serverAddrPort netip.AddrPort
   184  }
   185  
   186  // NewShadowsocksNonePacketClientUnpacker creates a Shadowsocks none packet client unpacker.
   187  func NewShadowsocksNonePacketClientUnpacker(serverAddrPort netip.AddrPort) *ShadowsocksNonePacketClientUnpacker {
   188  	return &ShadowsocksNonePacketClientUnpacker{
   189  		serverAddrPort: serverAddrPort,
   190  	}
   191  }
   192  
   193  // ClientUnpackerInfo implements the zerocopy.ClientUnpacker ClientUnpackerInfo method.
   194  func (p *ShadowsocksNonePacketClientUnpacker) ClientUnpackerInfo() zerocopy.ClientUnpackerInfo {
   195  	return zerocopy.ClientUnpackerInfo{
   196  		Headroom: ShadowsocksNonePacketServerMessageHeadroom,
   197  	}
   198  }
   199  
   200  // UnpackInPlace implements the zerocopy.ClientUnpacker UnpackInPlace method.
   201  func (p *ShadowsocksNonePacketClientUnpacker) UnpackInPlace(b []byte, packetSourceAddrPort netip.AddrPort, packetStart, packetLen int) (payloadSourceAddrPort netip.AddrPort, payloadStart, payloadLen int, err error) {
   202  	if !conn.AddrPortMappedEqual(packetSourceAddrPort, p.serverAddrPort) {
   203  		err = fmt.Errorf("dropped packet from non-server source %s", packetSourceAddrPort)
   204  		return
   205  	}
   206  	var payloadSourceAddrLen int
   207  	payloadSourceAddrPort, payloadSourceAddrLen, err = socks5.AddrPortFromSlice(b[packetStart : packetStart+packetLen])
   208  	payloadStart = packetStart + payloadSourceAddrLen
   209  	payloadLen = packetLen - payloadSourceAddrLen
   210  	return
   211  }
   212  
   213  // ShadowsocksNonePacketServerPacker implements the zerocopy ServerPacker interface.
   214  type ShadowsocksNonePacketServerPacker struct{}
   215  
   216  // ServerPackerInfo implements the zerocopy.ServerPacker ServerPackerInfo method.
   217  func (ShadowsocksNonePacketServerPacker) ServerPackerInfo() zerocopy.ServerPackerInfo {
   218  	return zerocopy.ServerPackerInfo{
   219  		Headroom: ShadowsocksNonePacketServerMessageHeadroom,
   220  	}
   221  }
   222  
   223  // PackInPlace implements the zerocopy.ServerPacker PackInPlace method.
   224  func (ShadowsocksNonePacketServerPacker) PackInPlace(b []byte, sourceAddrPort netip.AddrPort, payloadStart, payloadLen, maxPacketLen int) (packetStart, packetLen int, err error) {
   225  	targetAddrLen := socks5.LengthOfAddrFromAddrPort(sourceAddrPort)
   226  	packetStart = payloadStart - targetAddrLen
   227  	packetLen = payloadLen + targetAddrLen
   228  	if packetLen > maxPacketLen {
   229  		err = zerocopy.ErrPayloadTooBig
   230  	}
   231  	socks5.WriteAddrFromAddrPort(b[packetStart:], sourceAddrPort)
   232  	return
   233  }
   234  
   235  // ShadowsocksNonePacketServerUnpacker implements the zerocopy Unpacker interface.
   236  type ShadowsocksNonePacketServerUnpacker struct {
   237  	// cachedDomain caches the last used domain target to avoid allocating new strings.
   238  	cachedDomain string
   239  }
   240  
   241  // ServerUnpackerInfo implements the zerocopy.ServerUnpacker ServerUnpackerInfo method.
   242  func (ShadowsocksNonePacketServerUnpacker) ServerUnpackerInfo() zerocopy.ServerUnpackerInfo {
   243  	return zerocopy.ServerUnpackerInfo{
   244  		Headroom: ShadowsocksNonePacketClientMessageHeadroom,
   245  	}
   246  }
   247  
   248  // UnpackInPlace implements the zerocopy.ServerUnpacker UnpackInPlace method.
   249  func (p *ShadowsocksNonePacketServerUnpacker) UnpackInPlace(b []byte, sourceAddrPort netip.AddrPort, packetStart, packetLen int) (targetAddr conn.Addr, payloadStart, payloadLen int, err error) {
   250  	var targetAddrLen int
   251  	targetAddr, targetAddrLen, p.cachedDomain, err = socks5.ConnAddrFromSliceWithDomainCache(b[packetStart:packetStart+packetLen], p.cachedDomain)
   252  	payloadStart = packetStart + targetAddrLen
   253  	payloadLen = packetLen - targetAddrLen
   254  	return
   255  }
   256  
   257  // Socks5PacketClientMessageHeadroom is the headroom required by a SOCKS5 client message.
   258  var Socks5PacketClientMessageHeadroom = zerocopy.Headroom{
   259  	Front: 3 + socks5.MaxAddrLen,
   260  	Rear:  0,
   261  }
   262  
   263  // Socks5PacketServerMessageHeadroom is the headroom required by a SOCKS5 server message.
   264  var Socks5PacketServerMessageHeadroom = zerocopy.Headroom{
   265  	Front: 3 + socks5.IPv6AddrLen,
   266  	Rear:  0,
   267  }
   268  
   269  // Socks5PacketClientPacker implements the zerocopy ClientPacker interface.
   270  type Socks5PacketClientPacker struct {
   271  	// serverAddrPort is the SOCKS5 server's IP and port.
   272  	serverAddrPort netip.AddrPort
   273  
   274  	// maxPacketSize is the maximum allowed size of a packed packet.
   275  	// The value is calculated from MTU and server address family.
   276  	maxPacketSize int
   277  }
   278  
   279  // NewSocks5PacketClientPacker creates a SOCKS5 packet client packer.
   280  func NewSocks5PacketClientPacker(serverAddrPort netip.AddrPort, maxPacketSize int) *Socks5PacketClientPacker {
   281  	return &Socks5PacketClientPacker{
   282  		serverAddrPort: serverAddrPort,
   283  		maxPacketSize:  maxPacketSize,
   284  	}
   285  }
   286  
   287  // ClientPackerInfo implements the zerocopy.ClientPacker ClientPackerInfo method.
   288  func (p *Socks5PacketClientPacker) ClientPackerInfo() zerocopy.ClientPackerInfo {
   289  	return zerocopy.ClientPackerInfo{
   290  		Headroom: Socks5PacketClientMessageHeadroom,
   291  	}
   292  }
   293  
   294  // PackInPlace implements the zerocopy.ClientPacker PackInPlace method.
   295  func (p *Socks5PacketClientPacker) PackInPlace(b []byte, targetAddr conn.Addr, payloadStart, payloadLen int) (destAddrPort netip.AddrPort, packetStart, packetLen int, err error) {
   296  	targetAddrLen := socks5.LengthOfAddrFromConnAddr(targetAddr)
   297  	destAddrPort = p.serverAddrPort
   298  	packetStart = payloadStart - targetAddrLen - 3
   299  	packetLen = payloadLen + targetAddrLen + 3
   300  	if packetLen > p.maxPacketSize {
   301  		err = zerocopy.ErrPayloadTooBig
   302  	}
   303  	socks5.WritePacketHeader(b[packetStart:])
   304  	socks5.WriteAddrFromConnAddr(b[packetStart+3:], targetAddr)
   305  	return
   306  }
   307  
   308  // Socks5PacketClientUnpacker implements the zerocopy Unpacker interface.
   309  type Socks5PacketClientUnpacker struct {
   310  	// serverAddrPort is the SOCKS5 server's IP and port.
   311  	serverAddrPort netip.AddrPort
   312  }
   313  
   314  // NewSocks5PacketClientUnpacker creates a SOCKS5 packet client unpacker.
   315  func NewSocks5PacketClientUnpacker(serverAddrPort netip.AddrPort) *Socks5PacketClientUnpacker {
   316  	return &Socks5PacketClientUnpacker{
   317  		serverAddrPort: serverAddrPort,
   318  	}
   319  }
   320  
   321  // ClientUnpackerInfo implements the zerocopy.ClientUnpacker ClientUnpackerInfo method.
   322  func (p *Socks5PacketClientUnpacker) ClientUnpackerInfo() zerocopy.ClientUnpackerInfo {
   323  	return zerocopy.ClientUnpackerInfo{
   324  		Headroom: Socks5PacketServerMessageHeadroom,
   325  	}
   326  }
   327  
   328  // UnpackInPlace implements the zerocopy.ClientUnpacker UnpackInPlace method.
   329  func (p *Socks5PacketClientUnpacker) UnpackInPlace(b []byte, packetSourceAddrPort netip.AddrPort, packetStart, packetLen int) (payloadSourceAddrPort netip.AddrPort, payloadStart, payloadLen int, err error) {
   330  	if !conn.AddrPortMappedEqual(packetSourceAddrPort, p.serverAddrPort) {
   331  		err = fmt.Errorf("dropped packet from non-server source %s", packetSourceAddrPort)
   332  		return
   333  	}
   334  
   335  	if packetLen < 3 {
   336  		err = fmt.Errorf("%w: %d", zerocopy.ErrPacketTooSmall, packetLen)
   337  		return
   338  	}
   339  
   340  	pkt := b[packetStart : packetStart+packetLen]
   341  	err = socks5.ValidatePacketHeader(pkt)
   342  	if err != nil {
   343  		return
   344  	}
   345  
   346  	var payloadSourceAddrLen int
   347  	payloadSourceAddrPort, payloadSourceAddrLen, err = socks5.AddrPortFromSlice(pkt[3:])
   348  	payloadStart = packetStart + payloadSourceAddrLen + 3
   349  	payloadLen = packetLen - payloadSourceAddrLen - 3
   350  	return
   351  }
   352  
   353  // Socks5PacketServerPacker implements the zerocopy ServerPacker interface.
   354  type Socks5PacketServerPacker struct{}
   355  
   356  // ServerPackerInfo implements the zerocopy.ServerPacker ServerPackerInfo method.
   357  func (Socks5PacketServerPacker) ServerPackerInfo() zerocopy.ServerPackerInfo {
   358  	return zerocopy.ServerPackerInfo{
   359  		Headroom: Socks5PacketServerMessageHeadroom,
   360  	}
   361  }
   362  
   363  // PackInPlace implements the zerocopy.ServerPacker PackInPlace method.
   364  func (Socks5PacketServerPacker) PackInPlace(b []byte, sourceAddrPort netip.AddrPort, payloadStart, payloadLen, maxPacketLen int) (packetStart, packetLen int, err error) {
   365  	targetAddrLen := socks5.LengthOfAddrFromAddrPort(sourceAddrPort)
   366  	packetStart = payloadStart - targetAddrLen - 3
   367  	packetLen = payloadLen + targetAddrLen + 3
   368  	if packetLen > maxPacketLen {
   369  		err = zerocopy.ErrPayloadTooBig
   370  	}
   371  	socks5.WritePacketHeader(b[packetStart:])
   372  	socks5.WriteAddrFromAddrPort(b[packetStart+3:], sourceAddrPort)
   373  	return
   374  }
   375  
   376  // Socks5PacketServerUnpacker implements the zerocopy Unpacker interface.
   377  type Socks5PacketServerUnpacker struct {
   378  	// cachedDomain caches the last used domain target to avoid allocating new strings.
   379  	cachedDomain string
   380  }
   381  
   382  // ServerUnpackerInfo implements the zerocopy.ServerUnpacker ServerUnpackerInfo method.
   383  func (Socks5PacketServerUnpacker) ServerUnpackerInfo() zerocopy.ServerUnpackerInfo {
   384  	return zerocopy.ServerUnpackerInfo{
   385  		Headroom: Socks5PacketClientMessageHeadroom,
   386  	}
   387  }
   388  
   389  // UnpackInPlace implements the zerocopy.ServerUnpacker UnpackInPlace method.
   390  func (p *Socks5PacketServerUnpacker) UnpackInPlace(b []byte, sourceAddrPort netip.AddrPort, packetStart, packetLen int) (targetAddr conn.Addr, payloadStart, payloadLen int, err error) {
   391  	if packetLen < 3 {
   392  		err = fmt.Errorf("%w: %d", zerocopy.ErrPacketTooSmall, packetLen)
   393  		return
   394  	}
   395  
   396  	pkt := b[packetStart : packetStart+packetLen]
   397  	err = socks5.ValidatePacketHeader(pkt)
   398  	if err != nil {
   399  		return
   400  	}
   401  
   402  	var targetAddrLen int
   403  	targetAddr, targetAddrLen, p.cachedDomain, err = socks5.ConnAddrFromSliceWithDomainCache(pkt[3:], p.cachedDomain)
   404  	payloadStart = packetStart + targetAddrLen + 3
   405  	payloadLen = packetLen - targetAddrLen - 3
   406  	return
   407  }