forked from aimin-git/go-tun2socks
/
tcp.go
118 lines (99 loc) · 2.1 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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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
Uname string
Pwd string
}
func NewTCPHandler(proxyHost string, proxyPort uint16, auths ...string) core.TCPConnHandler {
th := &tcpHandler{
proxyHost: proxyHost,
proxyPort: proxyPort,
}
if len(auths) > 0 {
th.Uname = auths[0]
th.Pwd = auths[1]
}
return th
}
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 {
var auth *proxy.Auth
if h.Uname != "" && h.Pwd != "" {
auth = &proxy.Auth{
User: h.Uname,
Password: h.Pwd,
}
}
dialer, err := proxy.SOCKS5("tcp", core.ParseTCPAddr(h.proxyHost, h.proxyPort).String(), auth, 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
}