-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathtcpproxy.go
156 lines (134 loc) · 3.53 KB
/
tcpproxy.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
package tcpproxy
import (
"crypto/tls"
"fmt"
"net"
"os"
"sync/atomic"
"time"
)
// Proxy is a TCP server that takes an incoming request and sends it to another
// server, proxying the response back to the client.
type Proxy struct {
// Target address
Target *net.TCPAddr
// Local address
Addr *net.TCPAddr
// Director must be a function which modifies the request into a new request
// to be sent. Its response is then copied back to the client unmodified.
Director func(b *[]byte)
// If config is not nil, the proxy connects to the target address and then
// initiates a TLS handshake.
Config *tls.Config
// Timeout is the duration the proxy is staying alive without activity from
// both client and target. Also, if a pipe is closed, the proxy waits 'timeout'
// seconds before closing the other one. By default timeout is 60 seconds.
Timeout time.Duration
}
// NewProxy created a new proxy which sends all packet to target. The function dir
// intercept and can change the packet before sending it to the target.
func NewProxy(target *net.TCPAddr, dir func(*[]byte), config *tls.Config) *Proxy {
p := &Proxy{
Target: target,
Director: dir,
Timeout: time.Minute,
Config: config,
}
return p
}
// ListenAndServe listens on the TCP network address laddr and then handle packets
// on incoming connections.
func (p *Proxy) ListenAndServe(laddr *net.TCPAddr) {
p.Addr = laddr
var listener net.Listener
listener, err := net.ListenTCP("tcp", laddr)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
p.serve(listener)
}
// ListenAndServeTLS acts identically to ListenAndServe, except that it uses TLS
// protocol. Additionally, files containing a certificate and matching private key
// for the server must be provided.
func (p *Proxy) ListenAndServeTLS(laddr *net.TCPAddr, certFile, keyFile string) {
p.Addr = laddr
var listener net.Listener
cer, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
fmt.Println(err)
return
}
config := &tls.Config{Certificates: []tls.Certificate{cer}}
listener, err = tls.Listen("tcp", laddr.String(), config)
if err != nil {
fmt.Println(err)
return
}
p.serve(listener)
}
func (p *Proxy) serve(ln net.Listener) {
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println(err)
continue
}
go p.handleConn(conn)
}
}
// handleConn handles connection.
func (p *Proxy) handleConn(conn net.Conn) {
// connects to target server
var rconn net.Conn
var err error
if p.Config == nil {
rconn, err = net.Dial("tcp", p.Target.String())
} else {
rconn, err = tls.Dial("tcp", p.Target.String(), p.Config)
}
if err != nil {
fmt.Println(err)
return
}
// pipeDone counts closed pipe
var pipeDone int32
var timer *time.Timer
// write to dst what it reads from src
var pipe = func(src, dst net.Conn, filter func(b *[]byte)) {
defer func() {
// if it is the first pipe to end...
if v := atomic.AddInt32(&pipeDone, 1); v == 1 {
// ...wait 'timeout' seconds before closing connections
timer = time.AfterFunc(p.Timeout, func() {
// test if the other pipe is still alive before closing conn
if atomic.AddInt32(&pipeDone, 1) == 2 {
conn.Close()
rconn.Close()
}
})
} else if v == 2 {
conn.Close()
rconn.Close()
timer.Stop()
}
}()
buff := make([]byte, 65535)
for {
n, err := src.Read(buff)
if err != nil {
return
}
b := buff[:n]
if filter != nil {
filter(&b)
}
n, err = dst.Write(b)
if err != nil {
return
}
}
}
go pipe(conn, rconn, p.Director)
go pipe(rconn, conn, nil)
}