Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
V4NSH4J
GitHub Repository: V4NSH4J/discord-mass-DM-GO
Path: blob/main/client/connect.go
310 views
1
package client
2
3
// borrowed from from https://github.com/caddyserver/forwardproxy/blob/master/httpclient/httpclient.go
4
import (
5
"bufio"
6
"context"
7
"crypto/tls"
8
"encoding/base64"
9
"errors"
10
"io"
11
"net"
12
"net/url"
13
"strconv"
14
"sync"
15
16
http "github.com/Danny-Dasilva/fhttp"
17
http2 "github.com/Danny-Dasilva/fhttp/http2"
18
"golang.org/x/net/proxy"
19
)
20
21
// connectDialer allows to configure one-time use HTTP CONNECT client
22
type connectDialer struct {
23
ProxyURL url.URL
24
DefaultHeader http.Header
25
26
Dialer net.Dialer // overridden dialer allow to control establishment of TCP connection
27
28
// overridden DialTLS allows user to control establishment of TLS connection
29
// MUST return connection with completed Handshake, and NegotiatedProtocol
30
DialTLS func(network string, address string) (net.Conn, string, error)
31
32
EnableH2ConnReuse bool
33
cacheH2Mu sync.Mutex
34
cachedH2ClientConn *http2.ClientConn
35
cachedH2RawConn net.Conn
36
}
37
38
// newConnectDialer creates a dialer to issue CONNECT requests and tunnel traffic via HTTP/S proxy.
39
// proxyUrlStr must provide Scheme and Host, may provide credentials and port.
40
// Example: https://username:[email protected]:443
41
func newConnectDialer(proxyURLStr string, UserAgent string) (proxy.ContextDialer, error) {
42
proxyURL, err := url.Parse(proxyURLStr)
43
if err != nil {
44
return nil, err
45
}
46
47
if proxyURL.Host == "" || proxyURL.Host == "undefined" {
48
return nil, errors.New("invalid url `" + proxyURLStr +
49
"`, make sure to specify full url like https://username:[email protected]:443/")
50
}
51
52
switch proxyURL.Scheme {
53
case "http":
54
if proxyURL.Port() == "" {
55
proxyURL.Host = net.JoinHostPort(proxyURL.Host, "80")
56
}
57
case "https":
58
if proxyURL.Port() == "" {
59
proxyURL.Host = net.JoinHostPort(proxyURL.Host, "443")
60
}
61
case "":
62
return nil, errors.New("specify scheme explicitly (https://)")
63
default:
64
return nil, errors.New("scheme " + proxyURL.Scheme + " is not supported")
65
}
66
67
client := &connectDialer{
68
ProxyURL: *proxyURL,
69
DefaultHeader: make(http.Header),
70
EnableH2ConnReuse: true,
71
}
72
73
if proxyURL.User != nil {
74
if proxyURL.User.Username() != "" {
75
// password, _ := proxyUrl.User.Password()
76
// client.DefaultHeader.Set("Proxy-Authorization", "Basic "+
77
// base64.StdEncoding.EncodeToString([]byte(proxyUrl.User.Username()+":"+password)))
78
79
username := proxyURL.User.Username()
80
password, _ := proxyURL.User.Password()
81
82
// client.DefaultHeader.SetBasicAuth(username, password)
83
auth := username + ":" + password
84
basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
85
client.DefaultHeader.Add("Proxy-Authorization", basicAuth)
86
}
87
}
88
client.DefaultHeader.Set("User-Agent", UserAgent)
89
return client, nil
90
}
91
92
func (c *connectDialer) Dial(network, address string) (net.Conn, error) {
93
return c.DialContext(context.Background(), network, address)
94
}
95
96
// ContextKeyHeader Users of context.WithValue should define their own types for keys
97
type ContextKeyHeader struct{}
98
99
// ctx.Value will be inspected for optional ContextKeyHeader{} key, with `http.Header` value,
100
// which will be added to outgoing request headers, overriding any colliding c.DefaultHeader
101
func (c *connectDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
102
req := (&http.Request{
103
Method: "CONNECT",
104
URL: &url.URL{Host: address},
105
Header: make(http.Header),
106
Host: address,
107
}).WithContext(ctx)
108
for k, v := range c.DefaultHeader {
109
req.Header[k] = v
110
}
111
if ctxHeader, ctxHasHeader := ctx.Value(ContextKeyHeader{}).(http.Header); ctxHasHeader {
112
for k, v := range ctxHeader {
113
req.Header[k] = v
114
}
115
}
116
connectHTTP2 := func(rawConn net.Conn, h2clientConn *http2.ClientConn) (net.Conn, error) {
117
req.Proto = "HTTP/2.0"
118
req.ProtoMajor = 2
119
req.ProtoMinor = 0
120
pr, pw := io.Pipe()
121
req.Body = pr
122
123
resp, err := h2clientConn.RoundTrip(req)
124
if err != nil {
125
_ = rawConn.Close()
126
return nil, err
127
}
128
129
if resp.StatusCode != http.StatusOK {
130
_ = rawConn.Close()
131
return nil, errors.New("Proxy responded with non 200 code: " + resp.Status + "StatusCode:" + strconv.Itoa(resp.StatusCode))
132
}
133
return newHTTP2Conn(rawConn, pw, resp.Body), nil
134
}
135
136
connectHTTP1 := func(rawConn net.Conn) (net.Conn, error) {
137
req.Proto = "HTTP/1.1"
138
req.ProtoMajor = 1
139
req.ProtoMinor = 1
140
141
err := req.Write(rawConn)
142
if err != nil {
143
_ = rawConn.Close()
144
return nil, err
145
}
146
147
resp, err := http.ReadResponse(bufio.NewReader(rawConn), req)
148
if err != nil {
149
_ = rawConn.Close()
150
return nil, err
151
}
152
153
if resp.StatusCode != http.StatusOK {
154
_ = rawConn.Close()
155
return nil, errors.New("Proxy responded with non 200 code: " + resp.Status + " StatusCode:" + strconv.Itoa(resp.StatusCode))
156
}
157
return rawConn, nil
158
}
159
160
if c.EnableH2ConnReuse {
161
c.cacheH2Mu.Lock()
162
unlocked := false
163
if c.cachedH2ClientConn != nil && c.cachedH2RawConn != nil {
164
if c.cachedH2ClientConn.CanTakeNewRequest() {
165
rc := c.cachedH2RawConn
166
cc := c.cachedH2ClientConn
167
c.cacheH2Mu.Unlock()
168
unlocked = true
169
proxyConn, err := connectHTTP2(rc, cc)
170
if err == nil {
171
return proxyConn, err
172
}
173
// else: carry on and try again
174
}
175
}
176
if !unlocked {
177
c.cacheH2Mu.Unlock()
178
}
179
}
180
181
var err error
182
var rawConn net.Conn
183
negotiatedProtocol := ""
184
switch c.ProxyURL.Scheme {
185
case "http":
186
rawConn, err = c.Dialer.DialContext(ctx, network, c.ProxyURL.Host)
187
if err != nil {
188
return nil, err
189
}
190
case "https":
191
if c.DialTLS != nil {
192
rawConn, negotiatedProtocol, err = c.DialTLS(network, c.ProxyURL.Host)
193
if err != nil {
194
return nil, err
195
}
196
} else {
197
tlsConf := tls.Config{
198
NextProtos: []string{"h2", "http/1.1"},
199
ServerName: c.ProxyURL.Hostname(),
200
InsecureSkipVerify: true,
201
}
202
tlsConn, err := tls.Dial(network, c.ProxyURL.Host, &tlsConf)
203
if err != nil {
204
return nil, err
205
}
206
err = tlsConn.Handshake()
207
if err != nil {
208
return nil, err
209
}
210
negotiatedProtocol = tlsConn.ConnectionState().NegotiatedProtocol
211
rawConn = tlsConn
212
}
213
default:
214
return nil, errors.New("scheme " + c.ProxyURL.Scheme + " is not supported")
215
}
216
217
switch negotiatedProtocol {
218
case "":
219
fallthrough
220
case "http/1.1":
221
return connectHTTP1(rawConn)
222
case "h2":
223
//TODO: update this with correct navigator
224
t := http2.Transport{Navigator: "chrome"}
225
h2clientConn, err := t.NewClientConn(rawConn)
226
if err != nil {
227
_ = rawConn.Close()
228
return nil, err
229
}
230
231
proxyConn, err := connectHTTP2(rawConn, h2clientConn)
232
if err != nil {
233
_ = rawConn.Close()
234
return nil, err
235
}
236
if c.EnableH2ConnReuse {
237
c.cacheH2Mu.Lock()
238
c.cachedH2ClientConn = h2clientConn
239
c.cachedH2RawConn = rawConn
240
c.cacheH2Mu.Unlock()
241
}
242
return proxyConn, err
243
default:
244
_ = rawConn.Close()
245
return nil, errors.New("negotiated unsupported application layer protocol: " +
246
negotiatedProtocol)
247
}
248
}
249
250
func newHTTP2Conn(c net.Conn, pipedReqBody *io.PipeWriter, respBody io.ReadCloser) net.Conn {
251
return &http2Conn{Conn: c, in: pipedReqBody, out: respBody}
252
}
253
254
type http2Conn struct {
255
net.Conn
256
in *io.PipeWriter
257
out io.ReadCloser
258
}
259
260
func (h *http2Conn) Read(p []byte) (n int, err error) {
261
return h.out.Read(p)
262
}
263
264
func (h *http2Conn) Write(p []byte) (n int, err error) {
265
return h.in.Write(p)
266
}
267
268
func (h *http2Conn) Close() error {
269
var retErr error = nil
270
if err := h.in.Close(); err != nil {
271
retErr = err
272
}
273
if err := h.out.Close(); err != nil {
274
retErr = err
275
}
276
return retErr
277
}
278
279
func (h *http2Conn) CloseConn() error {
280
return h.Conn.Close()
281
}
282
283
func (h *http2Conn) CloseWrite() error {
284
return h.in.Close()
285
}
286
287
func (h *http2Conn) CloseRead() error {
288
return h.out.Close()
289
}
290
291