github.com/database64128/shadowsocks-go@v1.7.0/ss2022/udp.go (about)

     1  package ss2022
     2  
     3  import (
     4  	"crypto/cipher"
     5  	"crypto/rand"
     6  	"crypto/subtle"
     7  	"encoding/binary"
     8  	"fmt"
     9  	"net/netip"
    10  
    11  	"github.com/database64128/shadowsocks-go/zerocopy"
    12  	"github.com/database64128/tfo-go/v2"
    13  )
    14  
    15  // UDPClient implements the zerocopy UDPClient interface.
    16  type UDPClient struct {
    17  	addrPort         netip.AddrPort
    18  	info             zerocopy.UDPClientInfo
    19  	nonAEADHeaderLen int
    20  	cipherConfig     *ClientCipherConfig
    21  	shouldPad        PaddingPolicy
    22  }
    23  
    24  func NewUDPClient(addrPort netip.AddrPort, name string, mtu int, listenConfig tfo.ListenConfig, cipherConfig *ClientCipherConfig, shouldPad PaddingPolicy) *UDPClient {
    25  	identityHeadersLen := IdentityHeaderLength * len(cipherConfig.iPSKs)
    26  	return &UDPClient{
    27  		addrPort: addrPort,
    28  		info: zerocopy.UDPClientInfo{
    29  			Name:           name,
    30  			PackerHeadroom: ShadowPacketClientMessageHeadroom(identityHeadersLen),
    31  			MaxPacketSize:  zerocopy.MaxPacketSizeForAddr(mtu, addrPort.Addr()),
    32  			ListenConfig:   listenConfig,
    33  		},
    34  		nonAEADHeaderLen: UDPSeparateHeaderLength + identityHeadersLen,
    35  		cipherConfig:     cipherConfig,
    36  		shouldPad:        shouldPad,
    37  	}
    38  }
    39  
    40  // Info implements the zerocopy.UDPClient Info method.
    41  func (c *UDPClient) Info() zerocopy.UDPClientInfo {
    42  	return c.info
    43  }
    44  
    45  // NewSession implements the zerocopy.UDPClient NewSession method.
    46  func (c *UDPClient) NewSession() (zerocopy.UDPClientInfo, zerocopy.ClientPacker, zerocopy.ClientUnpacker, error) {
    47  	// Random client session ID.
    48  	salt := make([]byte, 8)
    49  	_, err := rand.Read(salt)
    50  	if err != nil {
    51  		return c.info, nil, nil, err
    52  	}
    53  	csid := binary.BigEndian.Uint64(salt)
    54  	aead, err := c.cipherConfig.AEAD(salt)
    55  	if err != nil {
    56  		return c.info, nil, nil, err
    57  	}
    58  
    59  	return c.info, &ShadowPacketClientPacker{
    60  			csid:             csid,
    61  			aead:             aead,
    62  			block:            c.cipherConfig.UDPSeparateHeaderPackerCipher(),
    63  			shouldPad:        c.shouldPad,
    64  			eihCiphers:       c.cipherConfig.UDPIdentityHeaderCiphers(),
    65  			eihPSKHashes:     c.cipherConfig.EIHPSKHashes(),
    66  			maxPacketSize:    c.info.MaxPacketSize,
    67  			nonAEADHeaderLen: c.nonAEADHeaderLen,
    68  			info: zerocopy.ClientPackerInfo{
    69  				Headroom: c.info.PackerHeadroom,
    70  			},
    71  			serverAddrPort: c.addrPort,
    72  		}, &ShadowPacketClientUnpacker{
    73  			csid:         csid,
    74  			cipherConfig: c.cipherConfig,
    75  		}, nil
    76  }
    77  
    78  // UDPServer implements the zerocopy UDPSessionServer interface.
    79  type UDPServer struct {
    80  	CredStore
    81  	info                 zerocopy.UDPSessionServerInfo
    82  	identityHeaderLen    int
    83  	block                cipher.Block
    84  	identityCipherConfig ServerIdentityCipherConfig
    85  	shouldPad            PaddingPolicy
    86  	userCipherConfig     UserCipherConfig
    87  }
    88  
    89  func NewUDPServer(userCipherConfig UserCipherConfig, identityCipherConfig ServerIdentityCipherConfig, shouldPad PaddingPolicy) *UDPServer {
    90  	var identityHeaderLen int
    91  	block := userCipherConfig.Block()
    92  	if block == nil {
    93  		identityHeaderLen = IdentityHeaderLength
    94  		block = identityCipherConfig.UDP()
    95  	}
    96  
    97  	return &UDPServer{
    98  		info: zerocopy.UDPSessionServerInfo{
    99  			UnpackerHeadroom: ShadowPacketClientMessageHeadroom(identityHeaderLen),
   100  		},
   101  		identityHeaderLen:    identityHeaderLen,
   102  		block:                block,
   103  		identityCipherConfig: identityCipherConfig,
   104  		shouldPad:            shouldPad,
   105  		userCipherConfig:     userCipherConfig,
   106  	}
   107  }
   108  
   109  // Info implements the zerocopy.UDPSessionServer Info method.
   110  func (s *UDPServer) Info() zerocopy.UDPSessionServerInfo {
   111  	return s.info
   112  }
   113  
   114  // SessionInfo implements the zerocopy.UDPSessionServer SessionInfo method.
   115  func (s *UDPServer) SessionInfo(b []byte) (csid uint64, err error) {
   116  	if len(b) < UDPSeparateHeaderLength {
   117  		err = fmt.Errorf("%w: %d", zerocopy.ErrPacketTooSmall, len(b))
   118  		return
   119  	}
   120  
   121  	s.block.Decrypt(b, b)
   122  
   123  	csid = binary.BigEndian.Uint64(b)
   124  	return
   125  }
   126  
   127  // NewUnpacker implements the zerocopy.UDPSessionServer NewUnpacker method.
   128  func (s *UDPServer) NewUnpacker(b []byte, csid uint64) (zerocopy.SessionServerUnpacker, string, error) {
   129  	nonAEADHeaderLen := UDPSeparateHeaderLength + s.identityHeaderLen
   130  
   131  	if len(b) < nonAEADHeaderLen {
   132  		return nil, "", fmt.Errorf("%w: %d", zerocopy.ErrPacketTooSmall, len(b))
   133  	}
   134  
   135  	userCipherConfig := s.userCipherConfig
   136  	var username string
   137  
   138  	// Process identity header.
   139  	if s.identityHeaderLen != 0 {
   140  		separateHeader := b[:UDPSeparateHeaderLength]
   141  		identityHeader := b[UDPSeparateHeaderLength:nonAEADHeaderLen]
   142  		s.block.Decrypt(identityHeader, identityHeader)
   143  		subtle.XORBytes(identityHeader, identityHeader, separateHeader)
   144  		uPSKHash := *(*[IdentityHeaderLength]byte)(identityHeader)
   145  		serverUserCipherConfig := s.ulm[uPSKHash]
   146  		if serverUserCipherConfig == nil {
   147  			return nil, "", ErrIdentityHeaderUserPSKNotFound
   148  		}
   149  		userCipherConfig = serverUserCipherConfig.UserCipherConfig
   150  		username = serverUserCipherConfig.Name
   151  	}
   152  
   153  	aead, err := userCipherConfig.AEAD(b[:8])
   154  	if err != nil {
   155  		return nil, "", err
   156  	}
   157  
   158  	return &ShadowPacketServerUnpacker{
   159  		csid:             csid,
   160  		aead:             aead,
   161  		nonAEADHeaderLen: nonAEADHeaderLen,
   162  		info: zerocopy.ServerUnpackerInfo{
   163  			Headroom: s.info.UnpackerHeadroom,
   164  		},
   165  		userCipherConfig: userCipherConfig,
   166  		packerShouldPad:  s.shouldPad,
   167  	}, username, nil
   168  }