-
Notifications
You must be signed in to change notification settings - Fork 0
/
shadowsocks_impl.go
146 lines (125 loc) · 3.82 KB
/
shadowsocks_impl.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package chained
import (
"context"
crand "crypto/rand"
"encoding/binary"
goerrors "errors"
"fmt"
"io"
mrand "math/rand"
"net"
"strconv"
"sync"
"github.com/Jigsaw-Code/outline-ss-server/client"
"github.com/getlantern/common/config"
"github.com/getlantern/errors"
"github.com/getlantern/flashlight/v7/chained/prefixgen"
"github.com/getlantern/flashlight/v7/ops"
)
const (
defaultShadowsocksUpstreamSuffix = "test"
)
type shadowsocksImpl struct {
reportDialCore reportDialCoreFn
client client.Client
upstream string
rng *mrand.Rand
rngmx sync.Mutex
}
func newShadowsocksImpl(name, addr string, pc *config.ProxyConfig, reportDialCore reportDialCoreFn) (proxyImpl, error) {
secret := ptSetting(pc, "shadowsocks_secret")
cipher := ptSetting(pc, "shadowsocks_cipher")
upstream := ptSetting(pc, "shadowsocks_upstream")
prefixGen := ptSetting(pc, "shadowsocks_prefix_generator")
if upstream == "" {
upstream = defaultShadowsocksUpstreamSuffix
}
host, portStr, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
port, err := strconv.Atoi(portStr)
if err != nil {
return nil, errors.New("unable to parse port in address %v: %v", addr, err)
}
cl, err := client.NewClient(host, port, secret, cipher)
if err != nil {
return nil, errors.New("failed to create shadowsocks client: %v", err)
}
// Infrastructure python code seems to insert "None" as the prefix generator if there is none.
if prefixGen != "" && prefixGen != "None" {
gen, err := prefixgen.New(prefixGen)
if err != nil {
log.Errorf("failed to parse shadowsocks prefix generator from %v for proxy %v: %v", prefixGen, name, err)
} else {
prefixFunc := func() ([]byte, error) { return gen(), nil }
cl.SetTCPSaltGenerator(client.NewPrefixSaltGenerator(prefixFunc))
}
}
var seed int64
err = binary.Read(crand.Reader, binary.BigEndian, &seed)
if err != nil {
return nil, errors.New("unable to initialize rng: %v", err)
}
source := mrand.NewSource(seed)
rng := mrand.New(source)
return &shadowsocksImpl{
reportDialCore: reportDialCore,
client: cl,
upstream: upstream,
rng: rng,
}, nil
}
func (impl *shadowsocksImpl) close() {
}
func (impl *shadowsocksImpl) dialServer(op *ops.Op, ctx context.Context) (net.Conn, error) {
return impl.reportDialCore(op, func() (net.Conn, error) {
conn, err := impl.client.DialTCP(nil, impl.generateUpstream())
if err != nil {
return nil, err
}
return &ssWrapConn{conn}, nil
})
}
// generateUpstream() creates a marker upstream address. This isn't an
// acutal upstream that will be dialed, it signals that the upstream
// should be determined by other methods. It's just a bit random just to
// mix it up and not do anything especially consistent on every dial.
//
// To satisy shadowsocks expectations, a small random string is prefixed onto the
// configured suffix (along with a .) and a port is affixed to the end.
func (impl *shadowsocksImpl) generateUpstream() string {
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
impl.rngmx.Lock()
defer impl.rngmx.Unlock()
// [2 - 22]
sz := 2 + impl.rng.Intn(21)
b := make([]byte, sz)
for i := range b {
b[i] = letters[impl.rng.Intn(len(letters))]
}
return fmt.Sprintf("%s.%s:443", string(b), impl.upstream)
}
// this is a helper to smooth out error bumps
// that the rest of lantern doesn't really expect, but happen
// in the shadowsocks impl when closing.
type ssWrapConn struct {
net.Conn
}
func (c *ssWrapConn) Write(b []byte) (int, error) {
n, err := c.Conn.Write(b)
return n, ssTranslateError(err)
}
func (c *ssWrapConn) Read(b []byte) (int, error) {
n, err := c.Conn.Read(b)
return n, ssTranslateError(err)
}
func ssTranslateError(err error) error {
if err == nil {
return nil
}
if goerrors.Is(err, net.ErrClosed) {
return io.EOF
}
return err
}