-
Notifications
You must be signed in to change notification settings - Fork 1
/
tcpproxy.go
143 lines (126 loc) · 2.94 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
package main
import (
"crypto/tls"
"io"
"net"
log "github.com/Sirupsen/logrus"
)
// ProxyCounter counts the proxy's traffic
type ProxyCounter struct {
to, from uint64
}
// To adds bytes to the to counter
func (p *ProxyCounter) To(b uint64) {
tot := p.to + b
p.to = tot
}
// From adds bytes to the from counter
func (p *ProxyCounter) From(b uint64) {
tot := p.from + b
p.from = tot
}
// Total returns the count
func (p *ProxyCounter) Total() (to, from uint64) {
to = p.to
from = p.from
return
}
// InterruptHandler writes info when an os signal is encountered.
func (p *ProxyCounter) InterruptHandler() {
to, from := p.Total()
log.Infof("TCP proxy sent %v bytes and received %v bytes", to, from)
}
// TCPProxy is the wrapper object for a proxy connection. It tracks the amount
// of data sent and received, local and remote server settings, TLS config,
// and any connection errors.
type TCPProxy struct {
Counter *ProxyCounter
ServerAddr, RemoteAddr string
ServerConn, RemoteConn net.Conn
RemoteTLSConf *tls.Config
ErrorState bool
ErrorSignal chan bool
prefix string
showContent bool
}
func (p *TCPProxy) err(s string, err error) {
if p.ErrorState {
return
}
if err != io.EOF {
log.Warningf(p.prefix+s, err)
}
p.ErrorSignal <- true
p.ErrorState = true
}
func (p *TCPProxy) start() {
defer p.ServerConn.Close()
//connect to remote
var (
rConn net.Conn
err error
isTLS bool
)
if p.RemoteTLSConf != nil {
isTLS = true
p.RemoteTLSConf.BuildNameToCertificate()
log.Debugf("Dialing %s", p.RemoteAddr)
rConn, err = tls.Dial("tcp", p.RemoteAddr, p.RemoteTLSConf)
} else {
isTLS = false
rConn, err = net.Dial("tcp", p.RemoteAddr)
}
if err != nil {
p.err("Remote connection failed: %s", err)
return
}
p.RemoteConn = rConn
defer p.RemoteConn.Close()
// Log info about both ends of the conn
log.Infof("%sOpened connection %s >>> %s TLS=%v", p.prefix,
p.ServerConn.RemoteAddr().String(),
p.RemoteConn.RemoteAddr().String(), isTLS)
//bidirectional copy in separate goroutines
go p.pipe(p.ServerConn, p.RemoteConn)
go p.pipe(p.RemoteConn, p.ServerConn)
//wait for close...
<-p.ErrorSignal
log.Infof("%s Closed", p.prefix)
}
func (p *TCPProxy) pipe(src, dst net.Conn) {
//data direction
var f string
islocal := src == p.ServerConn
if islocal {
f = p.prefix + " >>> %d bytes sent%s"
} else {
f = p.prefix + " <<< %d bytes received%s"
}
//directional copy (64k buffer)
buff := make([]byte, 0xffff)
for {
n, err := src.Read(buff)
if err != nil {
p.err("Read failed '%s'", err)
return
}
b := buff[:n]
//show output if necessary
if p.showContent {
log.Debugf(f, n, "\n"+string(b))
} else {
log.Debugf(f, n, "")
}
//write out result
n, err = dst.Write(b)
if err != nil {
p.err("Write failed '%s'", err)
return
}
if islocal {
p.Counter.To(uint64(n))
} else {
p.Counter.From(uint64(n))
}
}
}