@@ -2,9 +2,11 @@ package main
2
2
3
3
import (
4
4
"github.com/456vv/vproxy"
5
+ "github.com/456vv/vsocks5"
6
+ "github.com/456vv/vconn"
7
+ "golang.org/x/crypto/ssh"
5
8
"net"
6
9
"net/http"
7
- //"crypto/tls"
8
10
"net/url"
9
11
"time"
10
12
"flag"
@@ -13,6 +15,9 @@ import (
13
15
"io"
14
16
"os"
15
17
"context"
18
+ "bytes"
19
+ "errors"
20
+ "encoding/base64"
16
21
)
17
22
18
23
var (
22
27
flogLevel = flag .Int ("logLevel" , 0 , "日志级别,0)不记录 1)客户端IP 2)认证 3)访问的Host地址 4)路径 5)请求 6)响应 7)错误 (default 0)" )
23
28
faddr = flag .String ("addr" , "" , "代理服务器地 (format \" 0.0.0.0:8080\" )" )
24
29
fproxy = flag .
String (
"proxy" ,
"" ,
"代理服务器的上级代理IP地址 (format \" http://11.22.33.44:8888\" or \" socks5://admin:[email protected] :1080\" )" )
25
- fmaxIdleConns = flag .Int ("maxIdleConns" , 500 , "保持空闲连接(TCP)数量" )
26
- fmaxIdleConnsPerHost = flag .Int ("maxIdleConnsPerHost" , 500 , "保持空闲连接(Host)数量" )
27
- fdisableKeepAlives = flag .Bool ("disableKeepAlives" , false , "禁止长连接 (default false)" )
28
- fdisableCompression = flag .Bool ("disableCompression" , false , "禁止传送数据时候进行压缩 (default false)" )
29
- ftlsHandshakeTimeout = flag .Int64 ("tlsHandshakeTimeout" , 10000 , "SSL握手超时,单位毫秒" )
30
30
fidleConnTimeout = flag .Int64 ("idleConnTimeout" , 0 , "空闲连接超时时,单位毫秒 (default 0)" )
31
- fexpectContinueTimeout = flag .Int64 ("expectContinueTimeout" , 1000 , "http1.1过度到http2的等待超时,单位毫秒" )
32
- fresponseHeaderTimeout = flag .Int64 ("responseHeaderTimeout" , 0 , "读取服务器发来的文件标头超时,单位毫秒 (default 0)" )
33
- fmaxResponseHeaderBytes = flag .Int64 ("maxResponseHeaderBytes" , 0 , "读取服务器发来的文件标头大小限制 (default 0)" )
34
31
fdataBufioSize = flag .Int ("dataBufioSize" , 1024 * 10 , "代理数据交换缓冲区大小,单位字节" )
35
32
ftimeout = flag .Int64 ("timeout" , 300000 , "转发连接请求超时,单位毫秒" )
36
- fkeepAlive = flag .Int64 ("keepAlive" , 30000 , "保持连接心跳检测超时,单位毫秒" )
37
33
flinkPosterior = flag .Bool ("linkPosterior" , false , "支持连接式代理,如:http://111.222.333.444:8080/https://www.baidu.com/abc/file.zip" )
38
34
)
39
35
@@ -53,50 +49,144 @@ func main(){
53
49
}
54
50
out = file
55
51
}
52
+
56
53
p := & vproxy.Proxy {
57
- Config : & vproxy.Config {
58
- LinkPosterior : * flinkPosterior ,
59
- DataBufioSize : * fdataBufioSize ,
60
- Timeout : time .Duration (* ftimeout ) * time .Millisecond ,
61
- KeepAlive : time .Duration (* fkeepAlive ) * time .Millisecond ,
62
- },
54
+ LinkPosterior : * flinkPosterior ,
55
+ DataBufioSize : * fdataBufioSize ,
63
56
Addr : * faddr ,
64
- Transport : & http.Transport {
65
- DisableKeepAlives : * fdisableKeepAlives ,
66
- DisableCompression : * fdisableCompression ,
67
- MaxIdleConns : * fmaxIdleConns ,
68
- MaxIdleConnsPerHost : * fmaxIdleConnsPerHost ,
69
- IdleConnTimeout : time .Duration (* fidleConnTimeout ) * time .Millisecond ,
70
- ResponseHeaderTimeout : time .Duration (* fresponseHeaderTimeout ) * time .Millisecond ,
71
- MaxResponseHeaderBytes : * fmaxResponseHeaderBytes ,
72
- TLSHandshakeTimeout : time .Duration (* ftlsHandshakeTimeout ) * time .Millisecond ,
73
- ExpectContinueTimeout : time .Duration (* fexpectContinueTimeout ) * time .Millisecond ,
74
- },
75
57
ErrorLogLevel : vproxy .LogLevel (* flogLevel ),
76
58
}
77
59
p .ErrorLog = log .New (out , "" , log .Lshortfile | log .LstdFlags )
78
60
if * fuser != "" {
79
- p .Config . Auth = func (username , password string ) bool {
61
+ p .Auth = func (username , password string ) bool {
80
62
return username == * fuser && password == * fpwd
81
63
}
82
64
}
83
-
84
- if tr , ok := p .Transport .(* http.Transport ); ok && * fproxy != "" {
85
- dialer := & net.Dialer {
86
- Timeout : p .Config .Timeout ,
87
- KeepAlive : p .Config .KeepAlive ,
88
- DualStack : true ,
65
+
66
+ tr := http .DefaultTransport .(* http.Transport )
67
+ tr .IdleConnTimeout = time .Duration (* fidleConnTimeout ) * time .Millisecond
68
+ dialer := & net.Dialer {
69
+ Timeout : time .Duration (* ftimeout ) * time .Millisecond ,
70
+ DualStack : true ,
71
+ }
72
+ p .DialContext = dialer .DialContext
73
+
74
+ if * fproxy != "" {
75
+ purl , err := url .Parse (* fproxy )
76
+ if err != nil {
77
+ fmt .Println ("上级代理格式错误:" , err )
78
+ return
89
79
}
90
- tr .Proxy = func (r * http.Request ) (* url.URL , error ){
91
- return url .Parse (* fproxy )
92
- }
93
- tr .DialContext = func (ctx context.Context , network , addr string ) (net.Conn , error ){
94
- ctx , cancel := context .WithTimeout (ctx , p .Config .Timeout )
95
- defer cancel ()
96
- return dialer .DialContext (ctx , network , addr )
97
- }
98
- }
99
-
80
+ var puser , ppwd string
81
+ if purl .User != nil {
82
+ puser = purl .User .Username ()
83
+ ppwd , _ = purl .User .Password ()
84
+ }
85
+
86
+ switch purl .Scheme {
87
+ case "ssh" :
88
+ config := & ssh.ClientConfig {
89
+ User : puser ,
90
+ Auth : []ssh.AuthMethod {
91
+ ssh .Password (ppwd ),
92
+ },
93
+ HostKeyCallback : func (hostname string , remote net.Addr , key ssh.PublicKey ) error {
94
+ log .Println (hostname , remote , key )
95
+ return nil
96
+ },
97
+ HostKeyAlgorithms : []string {
98
+ ssh .KeyAlgoRSA ,
99
+ ssh .KeyAlgoDSA ,
100
+ ssh .KeyAlgoECDSA256 ,
101
+ ssh .KeyAlgoECDSA384 ,
102
+ ssh .KeyAlgoECDSA521 ,
103
+ ssh .KeyAlgoED25519 ,
104
+ },
105
+ Timeout : 5 * time .Second ,
106
+ }
107
+
108
+ sshConn , client , err := sshDial ("tcp" , purl .Host , config )
109
+ if err != nil {
110
+ fmt .Println ("代理拨号错误: " , err )
111
+ return
112
+ }
113
+ defer func (){
114
+ client .Close ()
115
+ }()
116
+
117
+ //connectProxy
118
+ p .DialContext = func (ctx context.Context , network , address string ) (net.Conn , error ){
119
+ if cn , ok := sshConn .(vconn.CloseNotifier ); ok {
120
+ select {
121
+ case <- cn .CloseNotify ():
122
+ sshConn , client , err = sshDial ("tcp" , purl .Host , config )
123
+ if err != nil {
124
+ return nil , err
125
+ }
126
+ default :
127
+ }
128
+ }
129
+
130
+ return client .Dial (network , address )
131
+ }
132
+
133
+ //httpProxy
134
+ tr .DialContext = p .DialContext
135
+ case "https" ,"http" :
136
+ //httpProxy
137
+ tr .Proxy = func (r * http.Request ) (* url.URL , error ){
138
+ return purl , nil
139
+ }
140
+
141
+ //connectProxy
142
+ p .DialContext = func (ctx context.Context , network , address string ) (net.Conn , error ){
143
+ pconn , err := dialer .Dial (network , purl .Host )
144
+ if err != nil {
145
+ return nil , err
146
+ }
147
+
148
+ if purl .Scheme == "http" {
149
+ return pconn , err
150
+ }
151
+
152
+ var pauth string
153
+ if puser != "" {
154
+ pauth = "\n Proxy-Authorization: Basic " + basicAuth (puser , ppwd )
155
+ }
156
+ pconn .Write ([]byte (fmt .Sprintf ("CONNECT %[1]s HTTP/1.1\r \n Host: %[1]s%s\r \n \r \n " , address , pauth )))
157
+
158
+ p := make ([]byte , 1024 )
159
+ n , err := pconn .Read (p )
160
+ if err != nil {
161
+ return nil , err
162
+ }
163
+ if bytes .Compare ([]byte ("HTTP/1.1 200 Connection established" ), p [:n ]) != 0 {
164
+ pconn .Close ()
165
+ return nil , errors .New ("https proxy not support" )
166
+ }
167
+ return pconn , err
168
+ }
169
+ case "socks5" :
170
+ //httpProxy
171
+ tr .Proxy = func (r * http.Request ) (* url.URL , error ){
172
+ return purl , nil
173
+ }
174
+
175
+ //connectProxy
176
+ s5Client := & vsocks5.Client {
177
+ Username : puser ,
178
+ Password : ppwd ,
179
+ Server : purl .Host ,
180
+ }
181
+ p .DialContext = func (ctx context.Context , network , address string ) (net.Conn , error ){
182
+ return s5Client .Dial (network , address )
183
+ }
184
+ default :
185
+ fmt .Printf ("暂时不支持 %s 协议代理!\n " , purl .Scheme )
186
+ return
187
+ }
188
+ }
189
+
100
190
defer p .Close ()
101
191
err := p .ListenAndServe ()
102
192
if err != nil {
@@ -105,3 +195,22 @@ func main(){
105
195
106
196
107
197
}
198
+ func sshDial (network , addr string , config * ssh.ClientConfig ) (net.Conn , * ssh.Client , error ){
199
+ conn , err := net .DialTimeout (network , addr , config .Timeout )
200
+ if err != nil {
201
+ return nil , nil , err
202
+ }
203
+
204
+ conn = vconn .NewConn (conn )
205
+ c , chans , reqs , err := ssh .NewClientConn (conn , addr , config )
206
+ if err != nil {
207
+ return nil , nil , err
208
+ }
209
+
210
+ return conn , ssh .NewClient (c , chans , reqs ), nil
211
+ }
212
+
213
+ func basicAuth (username , password string ) string {
214
+ auth := username + ":" + password
215
+ return base64 .StdEncoding .EncodeToString ([]byte (auth ))
216
+ }
0 commit comments