forked from eycorsican/go-tun2socks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tcp.go
104 lines (85 loc) · 1.85 KB
/
tcp.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
package socks
import (
"io"
"net"
"sync"
"golang.org/x/net/proxy"
"github.com/eycorsican/go-tun2socks/common/log"
"github.com/eycorsican/go-tun2socks/core"
)
type tcpHandler struct {
sync.Mutex
proxyHost string
proxyPort uint16
}
func NewTCPHandler(proxyHost string, proxyPort uint16) core.TCPConnHandler {
return &tcpHandler{
proxyHost: proxyHost,
proxyPort: proxyPort,
}
}
type direction byte
const (
dirUplink direction = iota
dirDownlink
)
type duplexConn interface {
net.Conn
CloseRead() error
CloseWrite() error
}
func (h *tcpHandler) relay(lhs, rhs net.Conn) {
upCh := make(chan struct{})
cls := func(dir direction, interrupt bool) {
lhsDConn, lhsOk := lhs.(duplexConn)
rhsDConn, rhsOk := rhs.(duplexConn)
if !interrupt && lhsOk && rhsOk {
switch dir {
case dirUplink:
lhsDConn.CloseRead()
rhsDConn.CloseWrite()
case dirDownlink:
lhsDConn.CloseWrite()
rhsDConn.CloseRead()
default:
panic("unexpected direction")
}
} else {
lhs.Close()
rhs.Close()
}
}
// Uplink
go func() {
var err error
_, err = io.Copy(rhs, lhs)
if err != nil {
cls(dirUplink, true) // interrupt the conn if the error is not nil (not EOF)
} else {
cls(dirUplink, false) // half close uplink direction of the TCP conn if possible
}
upCh <- struct{}{}
}()
// Downlink
var err error
_, err = io.Copy(lhs, rhs)
if err != nil {
cls(dirDownlink, true)
} else {
cls(dirDownlink, false)
}
<-upCh // Wait for uplink done.
}
func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error {
dialer, err := proxy.SOCKS5("tcp", core.ParseTCPAddr(h.proxyHost, h.proxyPort).String(), nil, nil)
if err != nil {
return err
}
c, err := dialer.Dial(target.Network(), target.String())
if err != nil {
return err
}
go h.relay(conn, c)
log.Infof("new proxy connection to %v", target)
return nil
}