-
Notifications
You must be signed in to change notification settings - Fork 14
/
main.go
350 lines (303 loc) · 8.37 KB
/
main.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
package main
import (
"context"
"encoding/binary"
"flag"
"fmt"
"io"
"net"
"net/http"
"strings"
"github.com/armon/go-socks5"
turner "github.com/staaldraad/turner/lib"
"gortc.io/stun"
"gortc.io/turn"
"gortc.io/turnc"
)
var (
server = flag.String("server",
"localhost:3478",
"turn server address",
)
username = flag.String("u", "user", "username")
password = flag.String("p", "secret", "password")
socksProx = flag.Bool("socks5", false, "Start a SOCKS5 server")
httpProx = flag.Bool("http", false, "Start HTTP Proxy")
socksPort = flag.Int("sp", 8000, "Port to use for SOCKS server")
httpPort = flag.Int("hp", 8080, "Port to use for HTTP Proxy")
socksHost = flag.String("sh", "127.0.0.1", "Host addr to listen on SOCKS5 (default 127.0.0.1)")
httpHost = flag.String("hh", "127.0.0.1", "Host addr to listen on HTTP (default 127.0.0.1)")
translateIPv = flag.Bool("translate", false, "Transalte IP family in target, for example IPv6. Allows connecting to turn server with one version (IPv4) and be relayed to a peer with other (IPv6)")
)
func copyHeader(dst, src http.Header) {
for k, vv := range src {
for _, v := range vv {
dst.Add(k, v)
}
}
}
func bufHeader(src http.Header) []byte {
buf := make([]byte, 0)
for k, vv := range src {
buf = append(buf, []byte(k)...)
buf = append(buf, []byte(":")...)
for _, v := range vv {
buf = append(buf, []byte(v)...)
}
buf = append(buf, []byte("\r\n")...)
}
return buf
}
// this function is such an ugly hack but I'm tired and it works
// look at replacing with real code that does io.Copy and
// better buffer handling
// this drains http headers, constructs manual method line
// and manual host line
// then sends everything to the server
func handleHTTP(w http.ResponseWriter, r *http.Request) {
target := r.URL.Host
if target == "" {
w.Write([]byte("This is a HTTP Proxy, use it as such"))
return
}
port := r.URL.Port()
if port == "" {
port = "80"
}
peer := target
if strings.Index(target, ":") == -1 {
peer = fmt.Sprintf("%s:%s", target, port)
}
fmt.Printf("[*] Proxy to peer: %s\n", peer)
stunConnector, err := connectTurn(peer)
if err != nil {
fmt.Printf("[x] error setting up STUN %s\n", err)
http.Error(w, "Proxy encountered error", http.StatusInternalServerError)
return
}
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
return
}
conn, bufwr, err := hj.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
//ugly hack to recreate same function that could be achieved with httputil.DumpRequest
// create method line
methodLine := fmt.Sprintf("%s %s %s\r\n", r.Method, r.URL.Path, r.Proto)
hostLine := fmt.Sprintf("Host: %s\r\n", target)
stunConnector.Write([]byte(methodLine))
stunConnector.Write([]byte(hostLine))
stunConnector.Write(bufHeader(r.Header))
stunConnector.Write([]byte("\r\n"))
//drain body
io.Copy(stunConnector, r.Body)
io.Copy(bufwr, stunConnector)
// close the connections
defer conn.Close()
defer stunConnector.Close()
}
func handleProxyTun(w http.ResponseWriter, r *http.Request) {
target := r.URL.Host
if target == "" {
w.Write([]byte("This is a HTTP Proxy, use it as such"))
return
}
port := r.URL.Port()
if port == "" {
port = "80"
}
peer := r.Host
stunConnector, err := connectTurn(peer)
if err != nil {
fmt.Printf("[x] error setting up STUN %s\n", err)
w.WriteHeader(http.StatusInternalServerError)
//clientConn.Write([]byte("Proxy encountered error"))
return
}
w.WriteHeader(http.StatusOK)
hijacker, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "Hijacking not supported", http.StatusInternalServerError)
return
}
clientConn, _, err := hijacker.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
go transfer(stunConnector, clientConn)
go transfer(clientConn, stunConnector)
}
func transfer(destination io.WriteCloser, source io.ReadCloser) {
defer destination.Close()
defer source.Close()
io.Copy(destination, source)
}
func connectTurn(target string) (*turner.StunConnection, error) {
stunConnector := &turner.StunConnection{}
// Resolving to TURN server.
raddr, err := net.ResolveTCPAddr("tcp", *server)
if err != nil {
fmt.Println(err)
return nil, err
}
c, err := net.DialTCP("tcp", nil, raddr)
if err != nil {
fmt.Println(err)
return nil, err
}
fmt.Printf("[*] Dial server %s -> %s\n", c.LocalAddr(), c.RemoteAddr())
client, clientErr := turnc.New(turnc.Options{
Conn: c,
Username: *username,
Password: *password,
})
if clientErr != nil {
fmt.Println(clientErr)
c.Close()
return nil, clientErr
}
var alloc *turnc.Allocation
var allocErr error
// do address family selection
// this allows:
// IPv4 <-> turn-server <-> IPv6
// IPv6 <-> turn-server <-> IPv4
// hide this behind a flag for now, normal behaviour is to
// use the same address family ex: IPv4 <-> turn-server <-> IPv4
if *translateIPv {
// strip port
t := target[:strings.LastIndex(target, ":")]
// silly check, IPv4 or hostname should be dotted decimal notation
if strings.Count(t, ":") == 0 {
alloc, allocErr = client.AllocateTCP4()
} else { // ipv6 will have :
alloc, allocErr = client.AllocateTCP6()
}
} else {
alloc, allocErr = client.AllocateTCP()
}
if allocErr != nil {
fmt.Println(allocErr)
client.Close()
return nil, allocErr
}
peerAddr, resolveErr := net.ResolveTCPAddr("tcp", target)
if resolveErr != nil {
client.Close()
return nil, resolveErr
}
fmt.Println("[*] Create peer permission")
permission, createErr := alloc.Create(peerAddr.IP)
if createErr != nil {
client.Close()
return nil, createErr
}
fmt.Println("[*] Create TCP Session Connection")
conn, err := permission.CreateTCP(peerAddr)
if err != nil {
client.Close()
return nil, err
}
fmt.Println("[*] Create connect request")
var connid stun.RawAttribute
if connid, err = conn.Connect(); err != nil {
client.Close()
return nil, err
}
// setup bind
fmt.Println("[*] Create bind TCP connection")
cb, err := net.DialTCP("tcp", nil, raddr)
if err != nil {
client.Close()
return nil, err
}
fmt.Println("[*] Auth and Create client ")
sideChanReader, sideChanWriter := io.Pipe()
r := io.MultiReader(sideChanReader, cb)
clientb, clientErr := turnc.NewData(turnc.Options{
Conn: cb,
Username: *username,
}, *sideChanWriter)
if clientErr != nil {
client.Close()
return nil, clientErr
}
connD, err := permission.CreateTCP(peerAddr)
if err != nil {
client.Close()
return nil, err
}
fmt.Println("[*] Bind client ")
_, err = clientb.ConnectionBind(turn.ConnectionID(binary.BigEndian.Uint32(connid.Value)), alloc, connD)
if err != nil {
client.Close()
clientb.Close()
return nil, err
}
/*
buf := make([]byte, 10)
conn.Read(buf)
fmt.Println(buf)
*/
fmt.Println("[*] Bound")
stunConnector.CntrClient = *client
stunConnector.DataClient = *clientb
stunConnector.Conn = cb
stunConnector.MultiRead = r
return stunConnector, nil
}
func turnDial(ctx context.Context, network, addr string) (net.Conn, error) {
cnn, err := connectTurn(addr)
if err != nil {
return nil, err
}
return cnn, nil
}
func main() {
flag.Parse()
if !*httpProx && !*socksProx {
fmt.Println("[x] No mode selected. Use either, or both, -http or -socks5")
return
}
errChan := make(chan error)
if *httpProx {
go func(errChan chan error) {
fmt.Printf("[*] Starting HTTP Server on %s:%d\n", *httpHost, *httpPort)
httpServer := &http.Server{
Addr: fmt.Sprintf("%s:%d", *httpHost, *httpPort),
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodConnect {
handleProxyTun(w, r)
} else {
handleHTTP(w, r)
}
}),
// Disable HTTP/2.
//TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
}
errChan <- httpServer.ListenAndServe()
}(errChan)
}
if *socksProx {
fmt.Printf("[*] Starting SOCKS5 Server on %s:%d\n", *socksHost, *socksPort)
go func(errChan chan error) {
conf := &socks5.Config{Dial: turnDial}
server, err := socks5.New(conf)
if err != nil {
errChan <- err
return
}
// Create SOCKS5 proxy on localhost port 8000
errChan <- server.ListenAndServe("tcp", fmt.Sprintf("%s:%d", *socksHost, *socksPort))
}(errChan)
}
select {
case <-errChan:
fmt.Println("Error setting up server.", errChan)
}
}