forked from riobard/go-shadowsocks2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tcp_linux.go
125 lines (110 loc) · 3.4 KB
/
tcp_linux.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
package main
import (
"errors"
"net"
"syscall"
"unsafe"
"github.com/riobard/go-shadowsocks2/socks"
)
const (
SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv4.h
IP6T_SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv6/ip6_tables.h
)
// Listen on addr for netfilter redirected TCP connections
func redirLocal(addr string, d Dialer) {
// logf("TCP redirect %s <-> %s", addr, server)
tcpLocal(addr, d, func(c net.Conn) (socks.Addr, error) { return getOrigDst(c, false) })
}
// Listen on addr for netfilter redirected TCP IPv6 connections.
func redir6Local(addr string, d Dialer) {
// logf("TCP6 redirect %s <-> %s", addr, server)
tcpLocal(addr, d, func(c net.Conn) (socks.Addr, error) { return getOrigDst(c, true) })
}
// Get the original destination of a TCP connection.
func getOrigDst(conn net.Conn, ipv6 bool) (socks.Addr, error) {
c, ok := conn.(*net.TCPConn)
if !ok {
return nil, errors.New("only work with TCP connection")
}
rc, err := c.SyscallConn()
if err != nil {
return nil, err
}
var addr socks.Addr
rc.Control(func(fd uintptr) {
if ipv6 {
addr, err = ipv6_getorigdst(fd)
} else {
addr, err = getorigdst(fd)
}
})
return addr, err
}
// Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
func getorigdst(fd uintptr) (socks.Addr, error) {
raw := syscall.RawSockaddrInet4{}
siz := unsafe.Sizeof(raw)
if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil {
return nil, err
}
addr := make([]byte, 1+net.IPv4len+2)
addr[0] = socks.AtypIPv4
copy(addr[1:1+net.IPv4len], raw.Addr[:])
port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian
addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1]
return addr, nil
}
// Call ipv6_getorigdst() from linux/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
// NOTE: I haven't tried yet but it should work since Linux 3.8.
func ipv6_getorigdst(fd uintptr) (socks.Addr, error) {
raw := syscall.RawSockaddrInet6{}
siz := unsafe.Sizeof(raw)
if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil {
return nil, err
}
addr := make([]byte, 1+net.IPv6len+2)
addr[0] = socks.AtypIPv6
copy(addr[1:1+net.IPv6len], raw.Addr[:])
port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian
addr[1+net.IPv6len], addr[1+net.IPv6len+1] = port[0], port[1]
return addr, nil
}
func tproxyTCP(addr string, d Dialer) error {
l, err := net.Listen("tcp", addr)
if err != nil {
return err
}
rc, err := l.(*net.TCPListener).SyscallConn()
if err != nil {
return err
}
rc.Control(func(fd uintptr) { err = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1) })
if err != nil {
return err
}
logf("TPROXY on tcp://%v", addr)
for {
c, err := l.Accept()
if err != nil {
return err
}
go func() {
defer c.Close()
tcpKeepAlive(c)
rc, err := d.Dial("tcp", c.LocalAddr().String())
if err != nil {
logf("failed to connect: %v", err)
return
}
defer rc.Close()
tcpKeepAlive(rc)
logf("TPROXY TCP %s <--[%s]--> %s", c.RemoteAddr(), rc.RemoteAddr(), c.LocalAddr())
if err = relay(rc, c); err != nil {
if err, ok := err.(net.Error); ok && err.Timeout() {
return // ignore i/o timeout
}
logf("relay error: %v", err)
}
}()
}
}