@having5548/dsh-downloader 0.0.0-stage → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +376 -0
- package/LICENSE +29 -0
- package/README.en.md +378 -0
- package/README.md +382 -2
- package/cordis.patch.yml +9 -0
- package/lib/client.js +862 -0
- package/lib/core/ai-domains.js +130 -0
- package/lib/core/data/cn-cidrs.json.gz +0 -0
- package/lib/core/data/cn-cidrs6.json.gz +0 -0
- package/lib/core/data/cn-domains.json.gz +0 -0
- package/lib/core/profiles.js +372 -0
- package/lib/core/proxy-server.js +341 -0
- package/lib/core/rules.js +242 -0
- package/lib/core/session-guard.js +208 -0
- package/lib/core/share-links.js +371 -0
- package/lib/core/shell-proxy.js +143 -0
- package/lib/core/subscription.js +126 -0
- package/lib/core/transports.js +664 -0
- package/lib/core/upstream.js +1044 -0
- package/lib/core/vmess.js +298 -0
- package/lib/core/ws.js +128 -0
- package/lib/download/auth.js +110 -0
- package/lib/download/fetch-file.js +363 -0
- package/lib/download/http.js +299 -0
- package/lib/download/net-probe.js +126 -0
- package/lib/download/route.js +103 -0
- package/lib/download/segmented.js +279 -0
- package/lib/download/util.js +103 -0
- package/lib/index.js +1199 -0
- package/lib/native/connector.exe +0 -0
- package/native/README.md +59 -0
- package/native/dial.go +17 -0
- package/native/frame.go +112 -0
- package/native/go.mod +19 -0
- package/native/go.sum +32 -0
- package/native/hysteria2.go +178 -0
- package/native/main.go +79 -0
- package/native/reality.go +188 -0
- package/native/salamander.go +84 -0
- package/native/vless.go +455 -0
- package/package.json +86 -4
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"crypto/rand"
|
|
5
|
+
"net"
|
|
6
|
+
|
|
7
|
+
"golang.org/x/crypto/blake2b"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// salamanderSaltLen is the random salt prefixed to every datagram.
|
|
11
|
+
const salamanderSaltLen = 8
|
|
12
|
+
|
|
13
|
+
// salamanderPacketConn applies hysteria2's "salamander" obfuscation to every
|
|
14
|
+
// datagram: an 8-byte random salt, followed by the payload XORed with a
|
|
15
|
+
// BLAKE2b-256 keystream keyed by password||salt. Without it the server simply
|
|
16
|
+
// drops our packets — the QUIC handshake never completes.
|
|
17
|
+
//
|
|
18
|
+
// Mirrors github.com/sagernet/sing-quic/hysteria2 (GPL-3.0) and the hysteria2
|
|
19
|
+
// reference; this is the wire format, not an implementation detail.
|
|
20
|
+
type salamanderPacketConn struct {
|
|
21
|
+
net.PacketConn
|
|
22
|
+
password []byte
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// newSalamanderPacketConn wraps conn, or returns it untouched when no password
|
|
26
|
+
// is configured (plain hysteria2 servers exist).
|
|
27
|
+
func newSalamanderPacketConn(conn net.PacketConn, password string) net.PacketConn {
|
|
28
|
+
if password == "" {
|
|
29
|
+
return conn
|
|
30
|
+
}
|
|
31
|
+
return &salamanderPacketConn{PacketConn: conn, password: []byte(password)}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// keystream returns the per-packet key for a salt, without aliasing password.
|
|
35
|
+
func (s *salamanderPacketConn) keystream(salt []byte) [blake2b.Size256]byte {
|
|
36
|
+
seed := make([]byte, 0, len(s.password)+len(salt))
|
|
37
|
+
seed = append(seed, s.password...)
|
|
38
|
+
seed = append(seed, salt...)
|
|
39
|
+
return blake2b.Sum256(seed)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// SetReadBuffer / SetWriteBuffer keep quic-go's socket tuning working: without
|
|
43
|
+
// them the transport logs "connection doesn't allow setting of receive buffer
|
|
44
|
+
// size" and falls back to smaller buffers, which costs throughput.
|
|
45
|
+
func (s *salamanderPacketConn) SetReadBuffer(bytes int) error {
|
|
46
|
+
if setter, ok := s.PacketConn.(interface{ SetReadBuffer(int) error }); ok {
|
|
47
|
+
return setter.SetReadBuffer(bytes)
|
|
48
|
+
}
|
|
49
|
+
return nil
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
func (s *salamanderPacketConn) SetWriteBuffer(bytes int) error {
|
|
53
|
+
if setter, ok := s.PacketConn.(interface{ SetWriteBuffer(int) error }); ok {
|
|
54
|
+
return setter.SetWriteBuffer(bytes)
|
|
55
|
+
}
|
|
56
|
+
return nil
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func (s *salamanderPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
|
60
|
+
n, addr, err := s.PacketConn.ReadFrom(p)
|
|
61
|
+
if err != nil || n <= salamanderSaltLen {
|
|
62
|
+
return n, addr, err
|
|
63
|
+
}
|
|
64
|
+
key := s.keystream(p[:salamanderSaltLen])
|
|
65
|
+
for index, c := range p[salamanderSaltLen:n] {
|
|
66
|
+
p[index] = c ^ key[index%blake2b.Size256]
|
|
67
|
+
}
|
|
68
|
+
return n - salamanderSaltLen, addr, nil
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
func (s *salamanderPacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {
|
|
72
|
+
buffer := make([]byte, salamanderSaltLen+len(p))
|
|
73
|
+
if _, err := rand.Read(buffer[:salamanderSaltLen]); err != nil {
|
|
74
|
+
return 0, err
|
|
75
|
+
}
|
|
76
|
+
key := s.keystream(buffer[:salamanderSaltLen])
|
|
77
|
+
for index, c := range p {
|
|
78
|
+
buffer[salamanderSaltLen+index] = c ^ key[index%blake2b.Size256]
|
|
79
|
+
}
|
|
80
|
+
if _, err := s.PacketConn.WriteTo(buffer, addr); err != nil {
|
|
81
|
+
return 0, err
|
|
82
|
+
}
|
|
83
|
+
return len(p), nil
|
|
84
|
+
}
|
package/native/vless.go
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"crypto/rand"
|
|
6
|
+
"encoding/hex"
|
|
7
|
+
"fmt"
|
|
8
|
+
"io"
|
|
9
|
+
"math/big"
|
|
10
|
+
"net"
|
|
11
|
+
"reflect"
|
|
12
|
+
"time"
|
|
13
|
+
"unsafe"
|
|
14
|
+
|
|
15
|
+
utls "github.com/metacubex/utls"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
// ---- vless request ---------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
func buildVlessRequest(node *NodeConfig, host string, port uint16) []byte {
|
|
21
|
+
uuid, err := hex.DecodeString(stringsReplace(node.UUID, "-", ""))
|
|
22
|
+
if err != nil {
|
|
23
|
+
panic(fmt.Sprintf("bad uuid: %v", err))
|
|
24
|
+
}
|
|
25
|
+
var addons []byte
|
|
26
|
+
if node.Flow == "xtls-rprx-vision" {
|
|
27
|
+
flowBytes := []byte("xtls-rprx-vision")
|
|
28
|
+
addons = append(addons, 0x0a)
|
|
29
|
+
addons = appendUvarint(addons, uint64(len(flowBytes)))
|
|
30
|
+
addons = append(addons, flowBytes...)
|
|
31
|
+
}
|
|
32
|
+
var buf []byte
|
|
33
|
+
buf = append(buf, 0x00)
|
|
34
|
+
buf = append(buf, uuid...)
|
|
35
|
+
buf = append(buf, byte(len(addons)))
|
|
36
|
+
buf = append(buf, addons...)
|
|
37
|
+
buf = append(buf, 0x01) // cmd TCP
|
|
38
|
+
buf = append(buf, byte(port>>8), byte(port&0xff))
|
|
39
|
+
buf = append(buf, encodeVlessAddr(host)...)
|
|
40
|
+
return buf
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
func encodeVlessAddr(host string) []byte {
|
|
44
|
+
if ip := net.ParseIP(host); ip != nil {
|
|
45
|
+
if ip4 := ip.To4(); ip4 != nil {
|
|
46
|
+
return append([]byte{0x01}, ip4...)
|
|
47
|
+
}
|
|
48
|
+
return append([]byte{0x03}, ip.To16()...)
|
|
49
|
+
}
|
|
50
|
+
b := []byte(host)
|
|
51
|
+
return append([]byte{0x02, byte(len(b))}, b...)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
func appendUvarint(b []byte, v uint64) []byte {
|
|
55
|
+
for v >= 0x80 {
|
|
56
|
+
b = append(b, byte(v)|0x80)
|
|
57
|
+
v >>= 7
|
|
58
|
+
}
|
|
59
|
+
return append(b, byte(v))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
func stringsReplace(s, old, new string) string {
|
|
63
|
+
out := ""
|
|
64
|
+
for i := 0; i < len(s); i++ {
|
|
65
|
+
if i+len(old) <= len(s) && s[i:i+len(old)] == old {
|
|
66
|
+
out += new
|
|
67
|
+
i += len(old) - 1
|
|
68
|
+
} else {
|
|
69
|
+
out += s[i : i+1]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return out
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ---- vless logical conn (request on first write, response on first read) ---
|
|
76
|
+
|
|
77
|
+
type vlessConn struct {
|
|
78
|
+
conn net.Conn
|
|
79
|
+
request []byte
|
|
80
|
+
requestWritten bool
|
|
81
|
+
responseRead bool
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
func newVlessConn(conn net.Conn, node *NodeConfig, dest string) (*vlessConn, error) {
|
|
85
|
+
host, portStr, err := net.SplitHostPort(dest)
|
|
86
|
+
if err != nil {
|
|
87
|
+
return nil, err
|
|
88
|
+
}
|
|
89
|
+
var portNum int
|
|
90
|
+
fmt.Sscanf(portStr, "%d", &portNum)
|
|
91
|
+
return &vlessConn{conn: conn, request: buildVlessRequest(node, host, uint16(portNum))}, nil
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
func (c *vlessConn) Write(b []byte) (int, error) {
|
|
95
|
+
if !c.requestWritten {
|
|
96
|
+
payload := append(append([]byte{}, c.request...), b...)
|
|
97
|
+
if _, err := c.conn.Write(payload); err != nil {
|
|
98
|
+
return 0, err
|
|
99
|
+
}
|
|
100
|
+
c.requestWritten = true
|
|
101
|
+
return len(b), nil
|
|
102
|
+
}
|
|
103
|
+
return c.conn.Write(b)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func (c *vlessConn) Read(b []byte) (int, error) {
|
|
107
|
+
if !c.responseRead {
|
|
108
|
+
var head [2]byte
|
|
109
|
+
if _, err := io.ReadFull(c.conn, head[:]); err != nil {
|
|
110
|
+
return 0, err
|
|
111
|
+
}
|
|
112
|
+
if head[0] != 0x00 {
|
|
113
|
+
return 0, fmt.Errorf("vless bad response version %d", head[0])
|
|
114
|
+
}
|
|
115
|
+
if head[1] > 0 {
|
|
116
|
+
if _, err := io.ReadFull(c.conn, make([]byte, head[1])); err != nil {
|
|
117
|
+
return 0, err
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
c.responseRead = true
|
|
121
|
+
}
|
|
122
|
+
return c.conn.Read(b)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
func (c *vlessConn) Close() error { return c.conn.Close() }
|
|
126
|
+
func (c *vlessConn) LocalAddr() net.Addr { return c.conn.LocalAddr() }
|
|
127
|
+
func (c *vlessConn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() }
|
|
128
|
+
func (c *vlessConn) SetDeadline(t time.Time) error { return c.conn.SetDeadline(t) }
|
|
129
|
+
func (c *vlessConn) SetReadDeadline(t time.Time) error { return c.conn.SetReadDeadline(t) }
|
|
130
|
+
func (c *vlessConn) SetWriteDeadline(t time.Time) error { return c.conn.SetWriteDeadline(t) }
|
|
131
|
+
|
|
132
|
+
// ---- xtls-rprx-vision client (padding + XTLS direct) ----------------------
|
|
133
|
+
|
|
134
|
+
const xrayChunkSize = 8192
|
|
135
|
+
|
|
136
|
+
var (
|
|
137
|
+
tls13SupportedVersions = []byte{0x00, 0x2b, 0x00, 0x02, 0x03, 0x04}
|
|
138
|
+
tlsClientHandShakeStart = []byte{0x16, 0x03}
|
|
139
|
+
tlsServerHandShakeStart = []byte{0x16, 0x03, 0x03}
|
|
140
|
+
tlsApplicationDataStart = []byte{0x17, 0x03, 0x03}
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
const (
|
|
144
|
+
commandPaddingContinue byte = iota
|
|
145
|
+
commandPaddingEnd
|
|
146
|
+
commandPaddingDirect
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
var tls13CipherSuiteDic = map[uint16]string{
|
|
150
|
+
0x1301: "TLS_AES_128_GCM_SHA256",
|
|
151
|
+
0x1302: "TLS_AES_256_GCM_SHA384",
|
|
152
|
+
0x1303: "TLS_CHACHA20_POLY1305_SHA256",
|
|
153
|
+
0x1304: "TLS_AES_128_CCM_SHA256",
|
|
154
|
+
0x1305: "TLS_AES_128_CCM_8_SHA256",
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
type visionConn struct {
|
|
158
|
+
net.Conn // the vless logical conn (reads/writes raw TLS stream)
|
|
159
|
+
|
|
160
|
+
rawConn net.Conn // underlying TCP conn of the uTLS conn (for XTLS direct)
|
|
161
|
+
input *bytes.Reader
|
|
162
|
+
rawIn *bytes.Buffer
|
|
163
|
+
|
|
164
|
+
userUUID [16]byte
|
|
165
|
+
isTLS bool
|
|
166
|
+
numberOfPacketToFilter int
|
|
167
|
+
isTLS12orAbove bool
|
|
168
|
+
remainingServerHello int32
|
|
169
|
+
cipher uint16
|
|
170
|
+
enableXTLS bool
|
|
171
|
+
isPadding bool
|
|
172
|
+
directWrite bool
|
|
173
|
+
writeUUID bool
|
|
174
|
+
withinPaddingBuffers bool
|
|
175
|
+
remainingContent int
|
|
176
|
+
remainingPadding int
|
|
177
|
+
currentCommand byte
|
|
178
|
+
directRead bool
|
|
179
|
+
remainingReader io.Reader
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
func newVisionConn(vconn *vlessConn, uconn *utls.UConn, uuid [16]byte) (*visionConn, error) {
|
|
183
|
+
// Extract the uTLS conn's internal input/rawInput for XTLS splice.
|
|
184
|
+
connType := reflect.TypeOf(uconn.Conn).Elem()
|
|
185
|
+
inputField, ok1 := connType.FieldByName("input")
|
|
186
|
+
rawInputField, ok2 := connType.FieldByName("rawInput")
|
|
187
|
+
if !ok1 || !ok2 {
|
|
188
|
+
return nil, fmt.Errorf("vision: utls.Conn has no input/rawInput fields")
|
|
189
|
+
}
|
|
190
|
+
connPtr := uintptr(unsafe.Pointer(uconn.Conn))
|
|
191
|
+
return &visionConn{
|
|
192
|
+
Conn: vconn,
|
|
193
|
+
rawConn: uconn.NetConn(),
|
|
194
|
+
input: (*bytes.Reader)(unsafe.Pointer(connPtr + inputField.Offset)),
|
|
195
|
+
rawIn: (*bytes.Buffer)(unsafe.Pointer(connPtr + rawInputField.Offset)),
|
|
196
|
+
userUUID: uuid,
|
|
197
|
+
numberOfPacketToFilter: 8,
|
|
198
|
+
remainingServerHello: -1,
|
|
199
|
+
isPadding: true,
|
|
200
|
+
writeUUID: true,
|
|
201
|
+
withinPaddingBuffers: true,
|
|
202
|
+
remainingContent: -1,
|
|
203
|
+
remainingPadding: -1,
|
|
204
|
+
}, nil
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
func (c *visionConn) Read(p []byte) (n int, err error) {
|
|
208
|
+
if c.remainingReader != nil {
|
|
209
|
+
n, err = c.remainingReader.Read(p)
|
|
210
|
+
if err == io.EOF {
|
|
211
|
+
err = nil
|
|
212
|
+
c.remainingReader = nil
|
|
213
|
+
}
|
|
214
|
+
if n > 0 {
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if c.directRead {
|
|
219
|
+
return c.rawConn.Read(p)
|
|
220
|
+
}
|
|
221
|
+
var buffer []byte
|
|
222
|
+
if len(p) > xrayChunkSize {
|
|
223
|
+
n, err = c.Conn.Read(p)
|
|
224
|
+
if err != nil {
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
buffer = p[:n]
|
|
228
|
+
} else {
|
|
229
|
+
chunk := make([]byte, xrayChunkSize)
|
|
230
|
+
n, err = c.Conn.Read(chunk)
|
|
231
|
+
if err != nil && n == 0 {
|
|
232
|
+
return 0, err
|
|
233
|
+
}
|
|
234
|
+
buffer = chunk[:n]
|
|
235
|
+
}
|
|
236
|
+
if c.withinPaddingBuffers || c.numberOfPacketToFilter > 0 {
|
|
237
|
+
buffers := c.unPadding(buffer)
|
|
238
|
+
if c.remainingContent == 0 && c.remainingPadding == 0 {
|
|
239
|
+
if c.currentCommand == commandPaddingEnd {
|
|
240
|
+
c.withinPaddingBuffers = false
|
|
241
|
+
c.remainingContent = -1
|
|
242
|
+
c.remainingPadding = -1
|
|
243
|
+
} else if c.currentCommand == commandPaddingDirect {
|
|
244
|
+
c.withinPaddingBuffers = false
|
|
245
|
+
c.directRead = true
|
|
246
|
+
if ib, e := io.ReadAll(c.input); e == nil && len(ib) > 0 {
|
|
247
|
+
buffers = append(buffers, ib)
|
|
248
|
+
}
|
|
249
|
+
if rb, e := io.ReadAll(c.rawIn); e == nil && len(rb) > 0 {
|
|
250
|
+
buffers = append(buffers, rb)
|
|
251
|
+
}
|
|
252
|
+
} else if c.currentCommand == commandPaddingContinue {
|
|
253
|
+
c.withinPaddingBuffers = true
|
|
254
|
+
} else {
|
|
255
|
+
return 0, fmt.Errorf("vision: unknown command %d", c.currentCommand)
|
|
256
|
+
}
|
|
257
|
+
} else if c.remainingContent > 0 || c.remainingPadding > 0 {
|
|
258
|
+
c.withinPaddingBuffers = true
|
|
259
|
+
} else {
|
|
260
|
+
c.withinPaddingBuffers = false
|
|
261
|
+
}
|
|
262
|
+
if c.numberOfPacketToFilter > 0 {
|
|
263
|
+
for _, buf := range buffers {
|
|
264
|
+
c.filterTLS(buf)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
readers := make([]io.Reader, len(buffers))
|
|
268
|
+
for i, b := range buffers {
|
|
269
|
+
readers[i] = bytes.NewReader(b)
|
|
270
|
+
}
|
|
271
|
+
c.remainingReader = io.MultiReader(readers...)
|
|
272
|
+
return c.Read(p)
|
|
273
|
+
}
|
|
274
|
+
if c.numberOfPacketToFilter > 0 {
|
|
275
|
+
c.filterTLS(buffer)
|
|
276
|
+
}
|
|
277
|
+
return n, err
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
func (c *visionConn) Write(p []byte) (n int, err error) {
|
|
281
|
+
if c.numberOfPacketToFilter > 0 {
|
|
282
|
+
c.filterTLS(p)
|
|
283
|
+
}
|
|
284
|
+
if c.isPadding {
|
|
285
|
+
inputLen := len(p)
|
|
286
|
+
buffers := reshapeBuffer(p)
|
|
287
|
+
var specIndex int
|
|
288
|
+
for i, buffer := range buffers {
|
|
289
|
+
if c.isTLS && len(buffer) > 6 && bytes.Equal(tlsApplicationDataStart, buffer[:3]) {
|
|
290
|
+
command := byte(commandPaddingEnd)
|
|
291
|
+
if c.enableXTLS {
|
|
292
|
+
c.directWrite = true
|
|
293
|
+
specIndex = i
|
|
294
|
+
command = commandPaddingDirect
|
|
295
|
+
}
|
|
296
|
+
c.isPadding = false
|
|
297
|
+
buffers[i] = c.padding(buffer, command)
|
|
298
|
+
break
|
|
299
|
+
} else if !c.isTLS12orAbove && c.numberOfPacketToFilter <= 1 {
|
|
300
|
+
c.isPadding = false
|
|
301
|
+
buffers[i] = c.padding(buffer, commandPaddingEnd)
|
|
302
|
+
break
|
|
303
|
+
}
|
|
304
|
+
buffers[i] = c.padding(buffer, commandPaddingContinue)
|
|
305
|
+
}
|
|
306
|
+
if c.directWrite {
|
|
307
|
+
// Write the padded prefix through the vision layer, then switch the
|
|
308
|
+
// rest of the stream to raw TLS-bypass (XTLS splice).
|
|
309
|
+
for i := 0; i <= specIndex; i++ {
|
|
310
|
+
if _, err = c.Conn.Write(buffers[i]); err != nil {
|
|
311
|
+
return
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
buffers = buffers[specIndex+1:]
|
|
315
|
+
c.Conn = c.rawConn
|
|
316
|
+
time.Sleep(5 * time.Millisecond)
|
|
317
|
+
}
|
|
318
|
+
for _, buffer := range buffers {
|
|
319
|
+
if _, err = c.Conn.Write(buffer); err != nil {
|
|
320
|
+
return
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
n = inputLen
|
|
324
|
+
return
|
|
325
|
+
}
|
|
326
|
+
if c.directWrite {
|
|
327
|
+
return c.rawConn.Write(p)
|
|
328
|
+
}
|
|
329
|
+
return c.Conn.Write(p)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
func (c *visionConn) filterTLS(buffer []byte) {
|
|
333
|
+
c.numberOfPacketToFilter--
|
|
334
|
+
if len(buffer) > 6 {
|
|
335
|
+
if buffer[0] == 22 && buffer[1] == 3 && buffer[2] == 3 {
|
|
336
|
+
c.isTLS = true
|
|
337
|
+
if buffer[5] == 2 {
|
|
338
|
+
c.isTLS12orAbove = true
|
|
339
|
+
c.remainingServerHello = (int32(buffer[3])<<8 | int32(buffer[4])) + 5
|
|
340
|
+
if len(buffer) >= 79 && c.remainingServerHello >= 79 {
|
|
341
|
+
sessionIDLen := int32(buffer[43])
|
|
342
|
+
cipherSuite := buffer[43+sessionIDLen+1 : 43+sessionIDLen+3]
|
|
343
|
+
c.cipher = uint16(cipherSuite[0])<<8 | uint16(cipherSuite[1])
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
} else if bytes.Equal(tlsClientHandShakeStart, buffer[:2]) && buffer[5] == 1 {
|
|
347
|
+
c.isTLS = true
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if c.remainingServerHello > 0 {
|
|
351
|
+
end := int(c.remainingServerHello)
|
|
352
|
+
if end > len(buffer) {
|
|
353
|
+
end = len(buffer)
|
|
354
|
+
}
|
|
355
|
+
c.remainingServerHello -= int32(end)
|
|
356
|
+
if bytes.Contains(buffer[:end], tls13SupportedVersions) {
|
|
357
|
+
cipher, ok := tls13CipherSuiteDic[c.cipher]
|
|
358
|
+
if ok && cipher != "TLS_AES_128_CCM_8_SHA256" {
|
|
359
|
+
c.enableXTLS = true
|
|
360
|
+
}
|
|
361
|
+
c.numberOfPacketToFilter = 0
|
|
362
|
+
} else if c.remainingServerHello == 0 {
|
|
363
|
+
c.numberOfPacketToFilter = 0
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
func (c *visionConn) padding(buffer []byte, command byte) []byte {
|
|
369
|
+
contentLen := len(buffer)
|
|
370
|
+
paddingLen := 0
|
|
371
|
+
if contentLen < 900 && c.isTLS {
|
|
372
|
+
l, _ := rand.Int(rand.Reader, big.NewInt(500))
|
|
373
|
+
paddingLen = int(l.Int64()) + 900 - contentLen
|
|
374
|
+
} else {
|
|
375
|
+
l, _ := rand.Int(rand.Reader, big.NewInt(256))
|
|
376
|
+
paddingLen = int(l.Int64())
|
|
377
|
+
}
|
|
378
|
+
bufferLen := paddingLen + contentLen + 5
|
|
379
|
+
if c.writeUUID {
|
|
380
|
+
bufferLen += 16
|
|
381
|
+
}
|
|
382
|
+
out := make([]byte, 0, bufferLen)
|
|
383
|
+
if c.writeUUID {
|
|
384
|
+
out = append(out, c.userUUID[:]...)
|
|
385
|
+
c.writeUUID = false
|
|
386
|
+
}
|
|
387
|
+
out = append(out, command, byte(contentLen>>8), byte(contentLen), byte(paddingLen>>8), byte(paddingLen))
|
|
388
|
+
out = append(out, buffer...)
|
|
389
|
+
out = append(out, make([]byte, paddingLen)...)
|
|
390
|
+
return out
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
func (c *visionConn) unPadding(buffer []byte) [][]byte {
|
|
394
|
+
var bufferIndex int
|
|
395
|
+
if c.remainingContent == -1 && c.remainingPadding == -1 {
|
|
396
|
+
if len(buffer) >= 21 && bytes.Equal(c.userUUID[:], buffer[:16]) {
|
|
397
|
+
bufferIndex = 16
|
|
398
|
+
c.remainingContent = 0
|
|
399
|
+
c.remainingPadding = 0
|
|
400
|
+
c.currentCommand = 0
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if c.remainingContent == -1 && c.remainingPadding == -1 {
|
|
404
|
+
return [][]byte{buffer}
|
|
405
|
+
}
|
|
406
|
+
var buffers [][]byte
|
|
407
|
+
for bufferIndex < len(buffer) {
|
|
408
|
+
if c.remainingContent <= 0 && c.remainingPadding <= 0 {
|
|
409
|
+
if c.currentCommand == commandPaddingEnd {
|
|
410
|
+
buffers = append(buffers, buffer[bufferIndex:])
|
|
411
|
+
break
|
|
412
|
+
} else {
|
|
413
|
+
if bufferIndex+5 > len(buffer) {
|
|
414
|
+
break
|
|
415
|
+
}
|
|
416
|
+
paddingInfo := buffer[bufferIndex : bufferIndex+5]
|
|
417
|
+
c.currentCommand = paddingInfo[0]
|
|
418
|
+
c.remainingContent = int(paddingInfo[1])<<8 | int(paddingInfo[2])
|
|
419
|
+
c.remainingPadding = int(paddingInfo[3])<<8 | int(paddingInfo[4])
|
|
420
|
+
bufferIndex += 5
|
|
421
|
+
}
|
|
422
|
+
} else if c.remainingContent > 0 {
|
|
423
|
+
end := c.remainingContent
|
|
424
|
+
if end > len(buffer)-bufferIndex {
|
|
425
|
+
end = len(buffer) - bufferIndex
|
|
426
|
+
}
|
|
427
|
+
buffers = append(buffers, buffer[bufferIndex:bufferIndex+end])
|
|
428
|
+
c.remainingContent -= end
|
|
429
|
+
bufferIndex += end
|
|
430
|
+
} else {
|
|
431
|
+
end := c.remainingPadding
|
|
432
|
+
if end > len(buffer)-bufferIndex {
|
|
433
|
+
end = len(buffer) - bufferIndex
|
|
434
|
+
}
|
|
435
|
+
c.remainingPadding -= end
|
|
436
|
+
bufferIndex += end
|
|
437
|
+
}
|
|
438
|
+
if bufferIndex == len(buffer) {
|
|
439
|
+
break
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return buffers
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
func reshapeBuffer(b []byte) [][]byte {
|
|
446
|
+
const bufferLimit = 8192 - 21
|
|
447
|
+
if len(b) < bufferLimit {
|
|
448
|
+
return [][]byte{b}
|
|
449
|
+
}
|
|
450
|
+
index := int(bytes.LastIndex(b, tlsApplicationDataStart))
|
|
451
|
+
if index <= 0 {
|
|
452
|
+
index = 8192 / 2
|
|
453
|
+
}
|
|
454
|
+
return [][]byte{b[:index], b[index:]}
|
|
455
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,88 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@having5548/dsh-downloader",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.5.2",
|
|
4
|
+
"description": "DeepSeek Harness 下载代理插件:注册 dsh_download 等工具,让模型下载境外文件时自动走插件自带的代理内核(订阅自包含,或显式 proxyUrl),境内直连;带 sha256、大小上限、停滞检测与直连失败自动回退,另附「设置 → 下载代理」管理面板。",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"dsh",
|
|
7
|
+
"dsh-plugin",
|
|
8
|
+
"deepseek-harness",
|
|
9
|
+
"download",
|
|
10
|
+
"proxy",
|
|
11
|
+
"clash",
|
|
12
|
+
"subscription",
|
|
13
|
+
"smart-routing"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/having5548/dsh-downloader.git"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/having5548/dsh-downloader#readme",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/having5548/dsh-downloader/issues"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "lib/index.js",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"default": "./lib/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./client": {
|
|
30
|
+
"default": "./lib/client.js"
|
|
31
|
+
},
|
|
32
|
+
"./package.json": "./package.json"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"lib",
|
|
36
|
+
"native",
|
|
37
|
+
"cordis.patch.yml",
|
|
38
|
+
"README.md",
|
|
39
|
+
"README.en.md",
|
|
40
|
+
"CHANGELOG.md",
|
|
41
|
+
"LICENSE"
|
|
42
|
+
],
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=20"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"test": "node test/smoke.mjs",
|
|
48
|
+
"test:e2e": "node test/e2e.mjs"
|
|
49
|
+
},
|
|
50
|
+
"dsh": {
|
|
51
|
+
"bundle": {
|
|
52
|
+
"patch": "./cordis.patch.yml"
|
|
53
|
+
},
|
|
54
|
+
"client": {
|
|
55
|
+
"platform": "web",
|
|
56
|
+
"inject": [
|
|
57
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
58
|
+
"@deepseek-ai/dsh-client-locale",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-settings"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@deepseek-ai/cosmokit": "^1.8.5",
|
|
65
|
+
"@deepseek-ai/schemastery": "^3.18.4",
|
|
66
|
+
"js-yaml": "^4.1.0"
|
|
67
|
+
},
|
|
68
|
+
"peerDependencies": {
|
|
69
|
+
"@deepseek-ai/dsh-client-runtime": "*",
|
|
70
|
+
"react": "^18.2.0",
|
|
71
|
+
"react-dom": "^18.2.0"
|
|
72
|
+
},
|
|
73
|
+
"peerDependenciesMeta": {
|
|
74
|
+
"@deepseek-ai/dsh-client-runtime": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"react": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"react-dom": {
|
|
81
|
+
"optional": true
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"license": "MIT",
|
|
85
|
+
"publishConfig": {
|
|
86
|
+
"access": "public"
|
|
87
|
+
}
|
|
88
|
+
}
|