@@ -2,6 +2,7 @@ package main
2
2
3
3
import (
4
4
"bytes"
5
+ "context"
5
6
"crypto/sha256"
6
7
"crypto/tls"
7
8
"crypto/x509"
@@ -14,7 +15,11 @@ import (
14
15
"github.com/sirupsen/logrus"
15
16
goproxy "golang.org/x/net/proxy"
16
17
"net"
18
+ "net/http"
19
+ "net/url"
20
+ "nhooyr.io/websocket"
17
21
"os"
22
+ "strings"
18
23
"time"
19
24
)
20
25
@@ -30,11 +35,12 @@ func main() {
30
35
var acceptFingerprint = flag .String ("accept-fingerprint" , "" , "accept certificates matching the following SHA256 fingerprint (hex format)" )
31
36
var verbose = flag .Bool ("v" , false , "enable verbose mode" )
32
37
var retry = flag .Bool ("retry" , false , "auto-retry on error" )
33
- var socksProxy = flag .String ("socks" , "" , "socks5 proxy address (ip:port)" )
34
- var socksUser = flag .String ("socks-user" , "" , "socks5 username" )
35
- var socksPass = flag .String ("socks-pass" , "" , "socks5 password" )
38
+ var socksProxy = flag .String ("proxy" , "" , "proxy URL address (http://admin:secret@127.0.0.1:8080)" +
39
+ " or socks://admin:secret@127.0.0.1:8080" )
36
40
var serverAddr = flag .String ("connect" , "" , "connect to proxy (domain:port)" )
37
41
var bindAddr = flag .String ("bind" , "" , "bind to ip:port" )
42
+ var userAgent = flag .String ("ua" , "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
43
+ "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36" , "HTTP User-Agent" )
38
44
var versionFlag = flag .Bool ("version" , false , "show the current version" )
39
45
40
46
flag .Usage = func () {
@@ -91,11 +97,20 @@ func main() {
91
97
if * serverAddr == "" {
92
98
logrus .Fatal ("please, specify the target host user -connect host:port" )
93
99
}
94
- host , _ , err := net .SplitHostPort (* serverAddr )
95
- if err != nil {
96
- logrus .Fatal ("invalid connect address, please use host:port" )
100
+
101
+ serverUrl , err := url .Parse (* serverAddr )
102
+ if serverUrl .Scheme == "https" && err == nil {
103
+ //websocket https connection
104
+ tlsConfig .ServerName = serverUrl .Hostname ()
105
+ } else {
106
+ //direct connection. try to parse as host:port
107
+ host , _ , err := net .SplitHostPort (* serverAddr )
108
+ if err != nil {
109
+ logrus .Fatal ("Invalid connect address, please use https://host:port for websocket or host:port for tcp" )
110
+ }
111
+ tlsConfig .ServerName = host
97
112
}
98
- tlsConfig . ServerName = host
113
+
99
114
if * ignoreCertificate {
100
115
logrus .Warn ("warning, certificate validation disabled" )
101
116
tlsConfig .InsecureSkipVerify = true
@@ -105,33 +120,51 @@ func main() {
105
120
106
121
for {
107
122
var err error
108
- if * socksProxy != "" {
109
- if _ , _ , err := net .SplitHostPort (* socksProxy ); err != nil {
110
- logrus .Fatal ("invalid socks5 address, please use host:port" )
111
- }
112
- conn , err = sockDial (* serverAddr , * socksProxy , * socksUser , * socksPass )
123
+ if serverUrl .Scheme == "https" {
124
+ * serverAddr = strings .Replace (* serverAddr , "https://" , "wss://" , 1 )
125
+ //websocket
126
+ err = wsconnect (& tlsConfig , * serverAddr , * socksProxy , * userAgent )
113
127
} else {
114
- conn , err = net .Dial ("tcp" , * serverAddr )
115
- }
116
- if err == nil {
117
- if * acceptFingerprint != "" {
118
- tlsConfig .InsecureSkipVerify = true
119
- tlsConfig .VerifyPeerCertificate = func (rawCerts [][]byte , verifiedChains [][]* x509.Certificate ) error {
120
- crtFingerprint := sha256 .Sum256 (rawCerts [0 ])
121
- crtMatch , err := hex .DecodeString (* acceptFingerprint )
122
- if err != nil {
123
- return fmt .Errorf ("invalid cert fingerprint: %v\n " , err )
124
- }
125
- if bytes .Compare (crtMatch , crtFingerprint [:]) != 0 {
126
- return fmt .Errorf ("certificate does not match fingerprint: %X != %X" , crtFingerprint , crtMatch )
127
- }
128
- return nil
128
+ if * socksProxy != "" {
129
+ //suppose that scheme is socks:// or socks5://
130
+ var proxyUrl * url.URL
131
+ proxyUrl , err = url .Parse (* socksProxy )
132
+ if err != nil {
133
+ logrus .Fatal ("invalid proxy address, please use socks5://host:port" )
134
+ }
135
+ if proxyUrl .Scheme == "http" {
136
+ logrus .Fatal ("Can't use http-proxy with direct (tcp) connection. Only with websocket" )
129
137
}
138
+ if proxyUrl .Scheme == "socks" || proxyUrl .Scheme == "socks5" {
139
+ pass , _ := proxyUrl .User .Password ()
140
+ conn , err = sockDial (* serverAddr , proxyUrl .Host , proxyUrl .User .Username (), pass )
141
+ } else {
142
+ logrus .Fatal ("invalid socks5 address, please use socks://host:port" )
143
+ }
144
+ } else {
145
+ conn , err = net .Dial ("tcp" , * serverAddr )
130
146
}
131
- tlsConn := tls .Client (conn , & tlsConfig )
147
+ if err == nil {
148
+ if * acceptFingerprint != "" {
149
+ tlsConfig .InsecureSkipVerify = true
150
+ tlsConfig .VerifyPeerCertificate = func (rawCerts [][]byte , verifiedChains [][]* x509.Certificate ) error {
151
+ crtFingerprint := sha256 .Sum256 (rawCerts [0 ])
152
+ crtMatch , err := hex .DecodeString (* acceptFingerprint )
153
+ if err != nil {
154
+ return fmt .Errorf ("invalid cert fingerprint: %v\n " , err )
155
+ }
156
+ if bytes .Compare (crtMatch , crtFingerprint [:]) != 0 {
157
+ return fmt .Errorf ("certificate does not match fingerprint: %X != %X" , crtFingerprint , crtMatch )
158
+ }
159
+ return nil
160
+ }
161
+ }
162
+ tlsConn := tls .Client (conn , & tlsConfig )
132
163
133
- err = connect (tlsConn )
164
+ err = connect (tlsConn )
165
+ }
134
166
}
167
+
135
168
logrus .Errorf ("Connection error: %v" , err )
136
169
if * retry {
137
170
logrus .Info ("Retrying in 5 seconds." )
@@ -169,3 +202,53 @@ func connect(conn net.Conn) error {
169
202
go agent .HandleConn (conn )
170
203
}
171
204
}
205
+
206
+ func wsconnect (config * tls.Config , wsaddr string , proxystr string , useragent string ) error {
207
+
208
+ //timeout for websocket library connection - 20 seconds
209
+ ctx , cancel := context .WithTimeout (context .Background (), time .Second * 20 )
210
+ defer cancel ()
211
+
212
+ //in case of websocket proxy can be http with login:pass
213
+ //Ex: proxystr = "http://admin:secret@127.0.0.1:8080"
214
+ proxyUrl , err := url .Parse (proxystr )
215
+ if err != nil || proxystr == "" {
216
+ proxyUrl = nil
217
+ }
218
+
219
+ httpTransport := & http.Transport {}
220
+ config .MinVersion = tls .VersionTLS10
221
+
222
+ httpTransport = & http.Transport {
223
+ MaxIdleConns : http .DefaultMaxIdleConnsPerHost ,
224
+ TLSClientConfig : config ,
225
+ Proxy : http .ProxyURL (proxyUrl ),
226
+ }
227
+
228
+ httpClient := & http.Client {Transport : httpTransport }
229
+ httpheader := & http.Header {}
230
+ httpheader .Add ("User-Agent" , useragent )
231
+
232
+ wsConn , _ , err := websocket .Dial (ctx , wsaddr , & websocket.DialOptions {HTTPClient : httpClient , HTTPHeader : * httpheader })
233
+ if err != nil {
234
+ return err
235
+ }
236
+
237
+ //timeout for netconn derived from websocket connection - it must be very big
238
+ netctx , cancel := context .WithTimeout (context .Background (), time .Hour * 999999 )
239
+ netConn := websocket .NetConn (netctx , wsConn , websocket .MessageBinary )
240
+ defer cancel ()
241
+ yamuxConn , err := yamux .Server (netConn , yamux .DefaultConfig ())
242
+ if err != nil {
243
+ return err
244
+ }
245
+
246
+ logrus .Info ("Websocket connection established" )
247
+ for {
248
+ conn , err := yamuxConn .Accept ()
249
+ if err != nil {
250
+ return err
251
+ }
252
+ go agent .HandleConn (conn )
253
+ }
254
+ }
0 commit comments