-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathproxy.go
135 lines (121 loc) · 3.36 KB
/
proxy.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
// Copyright 2020 The benchmark. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package main
import (
"bufio"
"encoding/base64"
"errors"
"golang.org/x/net/proxy"
"net"
"net/http"
"net/url"
"time"
)
// Return based on proxy url
func NewProxyConn(proxyUrl string, protocol clientProtocol) (ProxyConn, error) {
u, err := url.Parse(proxyUrl)
if err != nil {
return nil, err
}
switch u.Scheme {
case "socks5":
return NewSocks5Client(u, protocol), nil
case "http":
return NewHttpClient(u, protocol), nil
default:
return &DefaultClient{}, nil
}
}
// ProxyConn is used to define the proxy
type ProxyConn interface {
Dial(network string, address string, timeout time.Duration) (net.Conn, error)
}
// DefaultClient is used to implement a proxy in default
type DefaultClient struct {
rAddr *net.TCPAddr
}
// Socks5 implementation of ProxyConn
// Set KeepAlive=-1 to reduce the call of syscall
func (dc *DefaultClient) Dial(network string, address string, timeout time.Duration) (conn net.Conn, err error) {
if dc.rAddr == nil {
dc.rAddr, err = net.ResolveTCPAddr("tcp", address)
if err != nil {
return nil, err
}
}
return net.DialTCP(network, nil, dc.rAddr)
}
type clientProtocol struct {
transport string
quicProtocol string
}
// Socks5Client is used to implement a proxy in socks5
type Socks5Client struct {
proxyUrl *url.URL
clientProtocol
forward proxy.Dialer
}
func NewSocks5Client(proxyUrl *url.URL, protocol clientProtocol) *Socks5Client {
c := &Socks5Client{proxyUrl, protocol, nil}
if c.transport == "quic" {
c.forward = NewQuicDialer([]string{c.quicProtocol})
}
return c
}
// Socks5 implementation of ProxyConn
func (s5 *Socks5Client) Dial(network string, address string, timeout time.Duration) (net.Conn, error) {
d, err := proxy.FromURL(s5.proxyUrl, s5.forward)
if err != nil {
return nil, err
}
return d.Dial(network, address)
}
// Socks5Client is used to implement a proxy in http
type HttpClient struct {
proxyUrl *url.URL
clientProtocol
qd *QuicDialer
}
func NewHttpClient(proxyUrl *url.URL, protocol clientProtocol) *HttpClient {
c := &HttpClient{proxyUrl, protocol, nil}
if c.transport == "quic" {
c.qd = NewQuicDialer([]string{c.quicProtocol})
}
return c
}
func SetHTTPProxyBasicAuth(req *http.Request, username, password string) {
auth := username + ":" + password
authEncoded := base64.StdEncoding.EncodeToString([]byte(auth))
req.Header.Set("Proxy-Authorization", "Basic "+authEncoded)
}
// Http implementation of ProxyConn
func (hc *HttpClient) Dial(network string, address string, timeout time.Duration) (net.Conn, error) {
req, err := http.NewRequest("CONNECT", "http://"+address, nil)
if err != nil {
return nil, err
}
password, _ := hc.proxyUrl.User.Password()
SetHTTPProxyBasicAuth(req, hc.proxyUrl.User.Username(), password)
var proxyConn net.Conn
if hc.transport == "quic" {
proxyConn, err = hc.qd.Dial(network, hc.proxyUrl.Host)
} else {
proxyConn, err = net.DialTimeout("tcp", hc.proxyUrl.Host, timeout)
}
if err != nil {
return nil, err
}
if err = req.Write(proxyConn); err != nil {
return nil, err
}
res, err := http.ReadResponse(bufio.NewReader(proxyConn), req)
if err != nil {
return nil, err
}
_ = res.Body.Close()
if res.StatusCode != 200 {
return nil, errors.New("Proxy error " + res.Status)
}
return proxyConn, nil
}