Skip to content

Commit

Permalink
🫓 zerocopy: introduce TCPDialer and UDPClientSessionInfo
Browse files Browse the repository at this point in the history
In preparation for client groups, make more properties per-dialer/session.
  • Loading branch information
database64128 committed Feb 15, 2025
1 parent 8392d25 commit 565bbb5
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 106 deletions.
70 changes: 35 additions & 35 deletions direct/tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ import (
"github.com/database64128/shadowsocks-go/zerocopy"
)

// TCPClient implements the zerocopy TCPClient interface.
// TCPClient opens TCP connections and uses them directly.
//
// TCPClient implements [zerocopy.TCPClient] and [zerocopy.TCPDialer].
type TCPClient struct {
name string
network string
dialer conn.Dialer
}

// NewTCPClient creates a new TCP client.
func NewTCPClient(name, network string, dialer conn.Dialer) *TCPClient {
return &TCPClient{
name: name,
Expand All @@ -24,15 +27,15 @@ func NewTCPClient(name, network string, dialer conn.Dialer) *TCPClient {
}
}

// Info implements the zerocopy.TCPClient Info method.
func (c *TCPClient) Info() zerocopy.TCPClientInfo {
return zerocopy.TCPClientInfo{
// NewDialer implements [zerocopy.TCPClient.NewDialer].
func (c *TCPClient) NewDialer() (zerocopy.TCPDialer, zerocopy.TCPClientInfo) {
return c, zerocopy.TCPClientInfo{
Name: c.name,
NativeInitialPayload: !c.dialer.DisableTFO,
}
}

// Dial implements the zerocopy.TCPClient Dial method.
// Dial implements [zerocopy.TCPDialer.Dial].
func (c *TCPClient) Dial(ctx context.Context, targetAddr conn.Addr, payload []byte) (rawRW zerocopy.DirectReadWriteCloser, rw zerocopy.ReadWriter, err error) {
rawRW, err = c.dialer.DialTCP(ctx, c.network, targetAddr.String(), payload)
if err != nil {
Expand All @@ -44,73 +47,76 @@ func (c *TCPClient) Dial(ctx context.Context, targetAddr conn.Addr, payload []by

// TCPServer is the client-side tunnel server.
//
// TCPServer implements the zerocopy TCPServer interface.
// TCPServer implements [zerocopy.TCPServer].
type TCPServer struct {
targetAddr conn.Addr
}

// NewTCPServer creates a new TCP server.
func NewTCPServer(targetAddr conn.Addr) *TCPServer {
return &TCPServer{
targetAddr: targetAddr,
}
}

// Info implements the zerocopy.TCPServer Info method.
// Info implements [zerocopy.TCPServer.Info].
func (s *TCPServer) Info() zerocopy.TCPServerInfo {
return zerocopy.TCPServerInfo{
NativeInitialPayload: false,
DefaultTCPConnCloser: zerocopy.JustClose,
}
}

// Accept implements the zerocopy.TCPServer Accept method.
// Accept implements [zerocopy.TCPServer.Accept].
func (s *TCPServer) Accept(rawRW zerocopy.DirectReadWriteCloser) (rw zerocopy.ReadWriter, targetAddr conn.Addr, payload []byte, username string, err error) {
return &DirectStreamReadWriter{rw: rawRW}, s.targetAddr, nil, "", nil
}

// ShadowsocksNoneTCPClient implements the zerocopy TCPClient interface.
// ShadowsocksNoneTCPClient implements [zerocopy.TCPClient] and [zerocopy.TCPDialer].
type ShadowsocksNoneTCPClient struct {
name string
tco *zerocopy.TCPConnOpener
}

// NewShadowsocksNoneTCPClient creates a new Shadowsocks "none" TCP client.
func NewShadowsocksNoneTCPClient(name, network, address string, dialer conn.Dialer) *ShadowsocksNoneTCPClient {
return &ShadowsocksNoneTCPClient{
name: name,
tco: zerocopy.NewTCPConnOpener(dialer, network, address),
}
}

// Info implements the zerocopy.TCPClient Info method.
func (c *ShadowsocksNoneTCPClient) Info() zerocopy.TCPClientInfo {
return zerocopy.TCPClientInfo{
// NewDialer implements [zerocopy.TCPClient.NewDialer].
func (c *ShadowsocksNoneTCPClient) NewDialer() (zerocopy.TCPDialer, zerocopy.TCPClientInfo) {
return c, zerocopy.TCPClientInfo{
Name: c.name,
NativeInitialPayload: true,
}
}

// Dial implements the zerocopy.TCPClient Dial method.
// Dial implements [zerocopy.TCPDialer.Dial].
func (c *ShadowsocksNoneTCPClient) Dial(ctx context.Context, targetAddr conn.Addr, payload []byte) (rawRW zerocopy.DirectReadWriteCloser, rw zerocopy.ReadWriter, err error) {
rw, rawRW, err = NewShadowsocksNoneStreamClientReadWriter(ctx, c.tco, targetAddr, payload)
return
}

// ShadowsocksNoneTCPServer implements the zerocopy TCPServer interface.
// ShadowsocksNoneTCPServer implements [zerocopy.TCPServer].
type ShadowsocksNoneTCPServer struct{}

// NewShadowsocksNoneTCPServer creates a new Shadowsocks "none" TCP server.
func NewShadowsocksNoneTCPServer() ShadowsocksNoneTCPServer {
return ShadowsocksNoneTCPServer{}
}

// Info implements the zerocopy.TCPServer Info method.
// Info implements [zerocopy.TCPServer.Info].
func (ShadowsocksNoneTCPServer) Info() zerocopy.TCPServerInfo {
return zerocopy.TCPServerInfo{
NativeInitialPayload: false,
DefaultTCPConnCloser: zerocopy.JustClose,
}
}

// Accept implements the zerocopy.TCPServer Accept method.
// Accept implements [zerocopy.TCPServer.Accept].
func (ShadowsocksNoneTCPServer) Accept(rawRW zerocopy.DirectReadWriteCloser) (rw zerocopy.ReadWriter, targetAddr conn.Addr, payload []byte, username string, err error) {
rw, targetAddr, err = NewShadowsocksNoneStreamServerReadWriter(rawRW)
return
Expand Down Expand Up @@ -159,32 +165,23 @@ func (c *Socks5TCPClientConfig) NewClient() zerocopy.TCPClient {

// Socks5TCPClient is an unauthenticated SOCKS5 TCP client.
//
// Socks5TCPClient implements [zerocopy.TCPClient].
// Socks5TCPClient implements [zerocopy.TCPClient] and [zerocopy.TCPDialer].
type Socks5TCPClient struct {
name string
network string
address string
dialer conn.Dialer
}

func NewSocks5TCPClient(name, network, address string, dialer conn.Dialer) *Socks5TCPClient {
return &Socks5TCPClient{
name: name,
network: network,
address: address,
dialer: dialer,
}
}

// Info implements [zerocopy.TCPClient.Info].
func (c *Socks5TCPClient) Info() zerocopy.TCPClientInfo {
return zerocopy.TCPClientInfo{
// NewDialer implements [zerocopy.TCPClient.NewDialer].
func (c *Socks5TCPClient) NewDialer() (zerocopy.TCPDialer, zerocopy.TCPClientInfo) {
return c, zerocopy.TCPClientInfo{
Name: c.name,
NativeInitialPayload: false,
}
}

// Dial implements [zerocopy.TCPClient.Dial].
// Dial implements [zerocopy.TCPDialer.Dial].
func (c *Socks5TCPClient) Dial(ctx context.Context, targetAddr conn.Addr, payload []byte) (rawRW zerocopy.DirectReadWriteCloser, rw zerocopy.ReadWriter, err error) {
rawRW, err = c.dialer.DialTCP(ctx, c.network, c.address, nil)
if err != nil {
Expand All @@ -209,18 +206,21 @@ func (c *Socks5TCPClient) Dial(ctx context.Context, targetAddr conn.Addr, payloa

// Socks5AuthTCPClient is like [Socks5TCPClient], but uses username/password authentication.
//
// Socks5AuthTCPClient implements [zerocopy.TCPClient].
// Socks5AuthTCPClient implements [zerocopy.TCPClient] and [zerocopy.TCPDialer].
type Socks5AuthTCPClient struct {
plainClient Socks5TCPClient
authMsg []byte
}

// Info implements [zerocopy.TCPClient.Info].
func (c *Socks5AuthTCPClient) Info() zerocopy.TCPClientInfo {
return c.plainClient.Info()
// NewDialer implements [zerocopy.TCPClient.NewDialer].
func (c *Socks5AuthTCPClient) NewDialer() (zerocopy.TCPDialer, zerocopy.TCPClientInfo) {
return c, zerocopy.TCPClientInfo{
Name: c.plainClient.name,
NativeInitialPayload: false,
}
}

// Dial implements [zerocopy.TCPClient.Dial].
// Dial implements [zerocopy.TCPDialer.Dial].
func (c *Socks5AuthTCPClient) Dial(ctx context.Context, targetAddr conn.Addr, payload []byte) (rawRW zerocopy.DirectReadWriteCloser, rw zerocopy.ReadWriter, err error) {
rawRW, err = c.plainClient.dialer.DialTCP(ctx, c.plainClient.network, c.plainClient.address, nil)
if err != nil {
Expand Down
73 changes: 44 additions & 29 deletions direct/udp.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@ import (
"go.uber.org/zap"
)

// DirectUDPClient implements the zerocopy UDPClient interface.
// DirectUDPClient is a UDP client that makes no changes to the packets.
//
// DirectUDPClient implements [zerocopy.UDPClient].
type DirectUDPClient struct {
info zerocopy.UDPClientInfo
info zerocopy.UDPClientSessionInfo
session zerocopy.UDPClientSession
}

// NewDirectUDPClient creates a new UDP client that sends packets directly.
// NewDirectUDPClient creates a new UDP client that makes no changes to the packets.
func NewDirectUDPClient(name, network string, mtu int, listenConfig conn.ListenConfig) *DirectUDPClient {
return &DirectUDPClient{
info: zerocopy.UDPClientInfo{
info: zerocopy.UDPClientSessionInfo{
Name: name,
MTU: mtu,
ListenConfig: listenConfig,
Expand All @@ -37,29 +39,31 @@ func NewDirectUDPClient(name, network string, mtu int, listenConfig conn.ListenC
}
}

// Info implements the zerocopy.UDPClient Info method.
// Info implements [zerocopy.UDPClient.Info].
func (c *DirectUDPClient) Info() zerocopy.UDPClientInfo {
return c.info
return zerocopy.UDPClientInfo{}
}

// NewSession implements the zerocopy.UDPClient NewSession method.
func (c *DirectUDPClient) NewSession(ctx context.Context) (zerocopy.UDPClientInfo, zerocopy.UDPClientSession, error) {
// NewSession implements [zerocopy.UDPClient.NewSession].
func (c *DirectUDPClient) NewSession(ctx context.Context) (zerocopy.UDPClientSessionInfo, zerocopy.UDPClientSession, error) {
return c.info, c.session, nil
}

// ShadowsocksNoneUDPClient implements the zerocopy UDPClient interface.
// ShadowsocksNoneUDPClient is a Shadowsocks none UDP client.
//
// ShadowsocksNoneUDPClient implements [zerocopy.UDPClient].
type ShadowsocksNoneUDPClient struct {
network string
addr conn.Addr
info zerocopy.UDPClientInfo
info zerocopy.UDPClientSessionInfo
}

// NewShadowsocksNoneUDPClient creates a new Shadowsocks none UDP client.
func NewShadowsocksNoneUDPClient(name, network string, addr conn.Addr, mtu int, listenConfig conn.ListenConfig) *ShadowsocksNoneUDPClient {
return &ShadowsocksNoneUDPClient{
network: network,
addr: addr,
info: zerocopy.UDPClientInfo{
info: zerocopy.UDPClientSessionInfo{
Name: name,
PackerHeadroom: ShadowsocksNonePacketClientMessageHeadroom,
MTU: mtu,
Expand All @@ -68,13 +72,15 @@ func NewShadowsocksNoneUDPClient(name, network string, addr conn.Addr, mtu int,
}
}

// Info implements the zerocopy.UDPClient Info method.
// Info implements [zerocopy.UDPClient.Info].
func (c *ShadowsocksNoneUDPClient) Info() zerocopy.UDPClientInfo {
return c.info
return zerocopy.UDPClientInfo{
PackerHeadroom: ShadowsocksNonePacketClientMessageHeadroom,
}
}

// NewSession implements the zerocopy.UDPClient NewSession method.
func (c *ShadowsocksNoneUDPClient) NewSession(ctx context.Context) (zerocopy.UDPClientInfo, zerocopy.UDPClientSession, error) {
// NewSession implements [zerocopy.UDPClient.NewSession].
func (c *ShadowsocksNoneUDPClient) NewSession(ctx context.Context) (zerocopy.UDPClientSessionInfo, zerocopy.UDPClientSession, error) {
addrPort, err := c.addr.ResolveIPPort(ctx, c.network)
if err != nil {
return c.info, zerocopy.UDPClientSession{}, fmt.Errorf("failed to resolve endpoint address: %w", err)
Expand Down Expand Up @@ -135,7 +141,7 @@ func (c *Socks5UDPClientConfig) NewClient() zerocopy.UDPClient {
networkIP: c.NetworkIP,
address: c.Address,
dialer: c.Dialer,
info: zerocopy.UDPClientInfo{
info: zerocopy.UDPClientSessionInfo{
Name: c.Name,
PackerHeadroom: Socks5PacketClientMessageHeadroom,
MTU: c.MTU,
Expand All @@ -162,16 +168,18 @@ type Socks5UDPClient struct {
networkIP string
address string
dialer conn.Dialer
info zerocopy.UDPClientInfo
info zerocopy.UDPClientSessionInfo
}

// Info implements [zerocopy.UDPClient.Info].
func (c *Socks5UDPClient) Info() zerocopy.UDPClientInfo {
return c.info
return zerocopy.UDPClientInfo{
PackerHeadroom: Socks5PacketClientMessageHeadroom,
}
}

// NewSession implements [zerocopy.UDPClient.NewSession].
func (c *Socks5UDPClient) NewSession(ctx context.Context) (zerocopy.UDPClientInfo, zerocopy.UDPClientSession, error) {
func (c *Socks5UDPClient) NewSession(ctx context.Context) (zerocopy.UDPClientSessionInfo, zerocopy.UDPClientSession, error) {
tc, err := c.dialer.DialTCP(ctx, c.networkTCP, c.address, nil)
if err != nil {
return c.info, zerocopy.UDPClientSession{}, fmt.Errorf("failed to dial SOCKS5 server: %w", err)
Expand Down Expand Up @@ -231,7 +239,7 @@ func (c *Socks5AuthUDPClient) Info() zerocopy.UDPClientInfo {
}

// NewSession implements [zerocopy.UDPClient.NewSession].
func (c *Socks5AuthUDPClient) NewSession(ctx context.Context) (zerocopy.UDPClientInfo, zerocopy.UDPClientSession, error) {
func (c *Socks5AuthUDPClient) NewSession(ctx context.Context) (zerocopy.UDPClientSessionInfo, zerocopy.UDPClientSession, error) {
tc, err := c.plainClient.dialer.DialTCP(ctx, c.plainClient.networkTCP, c.plainClient.address, nil)
if err != nil {
return c.plainClient.info, zerocopy.UDPClientSession{}, fmt.Errorf("failed to dial SOCKS5 server: %w", err)
Expand All @@ -247,53 +255,60 @@ func (c *Socks5AuthUDPClient) NewSession(ctx context.Context) (zerocopy.UDPClien
return c.plainClient.info, session, err
}

// DirectUDPNATServer implements the zerocopy UDPNATServer interface.
// DirectUDPNATServer is a UDP NAT server that makes no changes to the packets.
//
// DirectUDPNATServer implements [zerocopy.UDPNATServer].
type DirectUDPNATServer struct {
p *DirectPacketServerPackUnpacker
}

// NewDirectUDPNATServer creates a new UDP NAT server that makes no changes to the packets.
func NewDirectUDPNATServer(targetAddr conn.Addr, targetAddrOnly bool) *DirectUDPNATServer {
return &DirectUDPNATServer{
p: NewDirectPacketServerPackUnpacker(targetAddr, targetAddrOnly),
}
}

// Info implements the zerocopy.UDPNATServer Info method.
// Info implements [zerocopy.UDPNATServer.Info].
func (s *DirectUDPNATServer) Info() zerocopy.UDPNATServerInfo {
return zerocopy.UDPNATServerInfo{}
}

// NewUnpacker implements the zerocopy.UDPNATServer NewUnpacker method.
// NewUnpacker implements [zerocopy.UDPNATServer.NewUnpacker].
func (s *DirectUDPNATServer) NewUnpacker() (zerocopy.ServerUnpacker, error) {
return s.p, nil
}

// ShadowsocksNoneUDPNATServer implements the zerocopy UDPNATServer interface.
// ShadowsocksNoneUDPNATServer is a Shadowsocks none UDP NAT server.
//
// ShadowsocksNoneUDPNATServer implements [zerocopy.UDPNATServer].
type ShadowsocksNoneUDPNATServer struct{}

// Info implements the zerocopy.UDPNATServer Info method.
// Info implements [zerocopy.UDPNATServer.Info].
func (ShadowsocksNoneUDPNATServer) Info() zerocopy.UDPNATServerInfo {
return zerocopy.UDPNATServerInfo{
UnpackerHeadroom: ShadowsocksNonePacketClientMessageHeadroom,
}
}

// NewUnpacker implements the zerocopy.UDPNATServer NewUnpacker method.
// NewUnpacker implements [zerocopy.UDPNATServer.NewUnpacker].
func (ShadowsocksNoneUDPNATServer) NewUnpacker() (zerocopy.ServerUnpacker, error) {
return &ShadowsocksNonePacketServerUnpacker{}, nil
}

// Socks5UDPNATServer implements the zerocopy UDPNATServer interface.
// Socks5UDPNATServer is a SOCKS5 UDP NAT server.
//
// Socks5UDPNATServer implements [zerocopy.UDPNATServer].
type Socks5UDPNATServer struct{}

// Info implements the zerocopy.UDPNATServer Info method.
// Info implements [zerocopy.UDPNATServer.Info].
func (Socks5UDPNATServer) Info() zerocopy.UDPNATServerInfo {
return zerocopy.UDPNATServerInfo{
UnpackerHeadroom: Socks5PacketClientMessageHeadroom,
}
}

// NewUnpacker implements the zerocopy.UDPNATServer NewUnpacker method.
// NewUnpacker implements [zerocopy.UDPNATServer.NewUnpacker].
func (Socks5UDPNATServer) NewUnpacker() (zerocopy.ServerUnpacker, error) {
return &Socks5PacketServerUnpacker{}, nil
}
Loading

0 comments on commit 565bbb5

Please sign in to comment.