Skip to content

Commit 2d91524

Browse files
committed
🦛 ssnone: implement netio stream interfaces
1 parent 4d6d57f commit 2d91524

File tree

3 files changed

+121
-0
lines changed

3 files changed

+121
-0
lines changed

ssnone/ssnone.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Package ssnone provides an implementation of the Shadowsocks "none" protocol.
2+
//
3+
// The "none" protocol, also known as "plain", is quite simple: It just prepends
4+
// the destination address in SOCKS5 address format to the byte stream or packet.
5+
package ssnone

ssnone/stream.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package ssnone
2+
3+
import (
4+
"context"
5+
6+
"github.com/database64128/shadowsocks-go/conn"
7+
"github.com/database64128/shadowsocks-go/netio"
8+
"github.com/database64128/shadowsocks-go/socks5"
9+
"go.uber.org/zap"
10+
)
11+
12+
// StreamClientConfig is the configuration for a Shadowsocks "none" stream client.
13+
type StreamClientConfig struct {
14+
// Name is the name of the client.
15+
Name string
16+
17+
// InnerClient is the underlying stream client.
18+
InnerClient netio.StreamClient
19+
20+
// Addr is the address of the Shadowsocks "none" server.
21+
Addr conn.Addr
22+
}
23+
24+
// NewStreamClient returns a new Shadowsocks "none" stream client.
25+
func (c *StreamClientConfig) NewStreamClient() *StreamClient {
26+
return &StreamClient{
27+
name: c.Name,
28+
innerClient: c.InnerClient,
29+
serverAddr: c.Addr,
30+
}
31+
}
32+
33+
// StreamClient is a Shadowsocks "none" stream client.
34+
//
35+
// StreamClient implements [netio.StreamClient] and [netio.StreamDialer].
36+
type StreamClient struct {
37+
name string
38+
innerClient netio.StreamClient
39+
serverAddr conn.Addr
40+
}
41+
42+
var (
43+
_ netio.StreamClient = (*StreamClient)(nil)
44+
_ netio.StreamDialer = (*StreamClient)(nil)
45+
)
46+
47+
// NewStreamDialer implements [netio.StreamClient.NewStreamDialer].
48+
func (c *StreamClient) NewStreamDialer() (netio.StreamDialer, netio.StreamDialerInfo) {
49+
return c, netio.StreamDialerInfo{
50+
Name: c.name,
51+
NativeInitialPayload: true,
52+
}
53+
}
54+
55+
// DialStream implements [netio.StreamDialer.DialStream].
56+
func (c *StreamClient) DialStream(ctx context.Context, addr conn.Addr, payload []byte) (netio.Conn, error) {
57+
addrLen := socks5.LengthOfAddrFromConnAddr(addr)
58+
b := make([]byte, addrLen+len(payload))
59+
_ = socks5.WriteAddrFromConnAddr(b, addr)
60+
_ = copy(b[addrLen:], payload)
61+
return c.innerClient.DialStream(ctx, c.serverAddr, b)
62+
}
63+
64+
// StreamServer is a Shadowsocks "none" stream server.
65+
//
66+
// The zero value is ready for use.
67+
//
68+
// StreamServer implements [netio.StreamServer].
69+
type StreamServer struct{}
70+
71+
var _ netio.StreamServer = StreamServer{}
72+
73+
// StreamServerInfo implements [netio.StreamServer.StreamServerInfo].
74+
func (StreamServer) StreamServerInfo() netio.StreamServerInfo {
75+
return netio.StreamServerInfo{
76+
NativeInitialPayload: false,
77+
}
78+
}
79+
80+
// HandleStream implements [netio.StreamServer.HandleStream].
81+
func (StreamServer) HandleStream(c netio.Conn, _ *zap.Logger) (netio.ConnRequest, error) {
82+
addr, err := socks5.ConnAddrFromReader(c)
83+
if err != nil {
84+
return netio.ConnRequest{}, err
85+
}
86+
return netio.ConnRequest{
87+
PendingConn: netio.NopPendingConn(c),
88+
Addr: addr,
89+
}, nil
90+
}

ssnone/stream_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package ssnone_test
2+
3+
import (
4+
"net/netip"
5+
"testing"
6+
7+
"github.com/database64128/shadowsocks-go/conn"
8+
"github.com/database64128/shadowsocks-go/netio"
9+
"github.com/database64128/shadowsocks-go/netiotest"
10+
"github.com/database64128/shadowsocks-go/ssnone"
11+
)
12+
13+
func TestStreamClientServer(t *testing.T) {
14+
addr := conn.AddrFromIPPort(netip.AddrPortFrom(netip.IPv6Loopback(), 8388))
15+
16+
newClient := func(psc *netiotest.PipeStreamClient) netio.StreamClient {
17+
clientConfig := ssnone.StreamClientConfig{
18+
Name: "test",
19+
InnerClient: psc,
20+
Addr: addr,
21+
}
22+
return clientConfig.NewStreamClient()
23+
}
24+
25+
netiotest.TestPreambleStreamClientServerProceed(t, newClient, ssnone.StreamServer{}, addr, "")
26+
}

0 commit comments

Comments
 (0)