forked from p4gefau1t/trojan-go
-
Notifications
You must be signed in to change notification settings - Fork 1
/
tcp.go
136 lines (120 loc) · 3.45 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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//go:build linux
// +build linux
package tproxy
import (
"fmt"
"net"
"os"
"syscall"
"unsafe"
)
// Listener describes a TCP Listener
// with the Linux IP_TRANSPARENT option defined
// on the listening socket
type Listener struct {
base net.Listener
}
// Accept waits for and returns
// the next connection to the listener.
//
// This command wraps the AcceptTProxy
// method of the Listener
func (listener *Listener) Accept() (net.Conn, error) {
tcpConn, err := listener.base.(*net.TCPListener).AcceptTCP()
if err != nil {
return nil, err
}
return tcpConn, nil
}
// Addr returns the network address
// the listener is accepting connections
// from
func (listener *Listener) Addr() net.Addr {
return listener.base.Addr()
}
// Close will close the listener from accepting
// any more connections. Any blocked connections
// will unblock and close
func (listener *Listener) Close() error {
return listener.base.Close()
}
// ListenTCP will construct a new TCP listener
// socket with the Linux IP_TRANSPARENT option
// set on the underlying socket
func ListenTCP(network string, laddr *net.TCPAddr) (net.Listener, error) {
listener, err := net.ListenTCP(network, laddr)
if err != nil {
return nil, err
}
fileDescriptorSource, err := listener.File()
if err != nil {
return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddr, Err: fmt.Errorf("get file descriptor: %s", err)}
}
defer fileDescriptorSource.Close()
if err = syscall.SetsockoptInt(int(fileDescriptorSource.Fd()), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddr, Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)}
}
return &Listener{listener}, nil
}
const (
IP6T_SO_ORIGINAL_DST = 80
SO_ORIGINAL_DST = 80
)
// getOriginalTCPDest retrieves the original destination address from
// NATed connection. Currently, only Linux iptables using DNAT/REDIRECT
// is supported. For other operating systems, this will just return
// conn.LocalAddr().
//
// Note that this function only works when nf_conntrack_ipv4 and/or
// nf_conntrack_ipv6 is loaded in the kernel.
func getOriginalTCPDest(conn *net.TCPConn) (*net.TCPAddr, error) {
f, err := conn.File()
if err != nil {
return nil, err
}
defer f.Close()
fd := int(f.Fd())
// revert to non-blocking mode.
// see http://stackoverflow.com/a/28968431/1493661
if err = syscall.SetNonblock(fd, true); err != nil {
return nil, os.NewSyscallError("setnonblock", err)
}
v6 := conn.LocalAddr().(*net.TCPAddr).IP.To4() == nil
if v6 {
var addr syscall.RawSockaddrInet6
var len uint32
len = uint32(unsafe.Sizeof(addr))
err = getsockopt(fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST,
unsafe.Pointer(&addr), &len)
if err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
ip := make([]byte, 16)
for i, b := range addr.Addr {
ip[i] = b
}
pb := *(*[2]byte)(unsafe.Pointer(&addr.Port))
return &net.TCPAddr{
IP: ip,
Port: int(pb[0])*256 + int(pb[1]),
}, nil
}
// IPv4
var addr syscall.RawSockaddrInet4
var len uint32
len = uint32(unsafe.Sizeof(addr))
err = getsockopt(fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST,
unsafe.Pointer(&addr), &len)
if err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
ip := make([]byte, 4)
for i, b := range addr.Addr {
ip[i] = b
}
pb := *(*[2]byte)(unsafe.Pointer(&addr.Port))
return &net.TCPAddr{
IP: ip,
Port: int(pb[0])*256 + int(pb[1]),
}, nil
}