-
Notifications
You must be signed in to change notification settings - Fork 656
/
Copy pathssvpn_interop.go
139 lines (106 loc) · 2.67 KB
/
ssvpn_interop.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
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package goflyway
import (
"encoding/binary"
"errors"
"net"
"strconv"
"syscall"
"unsafe"
"github.com/coyove/goflyway/fd"
)
func vpnDial(address string) (net.Conn, error) {
var family int
family = syscall.AF_INET
if address[0] == '[' {
// naive match
family = syscall.AF_INET6
}
sock, err := fd.Socket(family)
if err != nil {
return nil, err
}
// Send our conn fd to shadowsocks vpn thread for protection
if err := protectFD(sock); err != nil {
return nil, err
}
// If succeeded, this fd will be closed while we still need it.
// So we dial a new conn, replace its fd with this one
return fd.DialWithFD(sock, address)
}
func protectFD(fd int) error {
sock, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
if err != nil {
return err
}
var addr syscall.SockaddrUnix
addr.Name = "protect_path"
if err := (syscall.Connect(sock, &addr)); err != nil {
return err
}
if err := sendFD(sock, fd); err != nil {
return err
}
ret := []byte{9}
if n, err := syscall.Read(sock, ret); err != nil {
return err
} else if n != 1 {
return errors.New("protecting failed")
}
syscall.Close(sock)
if ret[0] != 0 {
return errors.New("protecting failed")
}
return nil
}
func sendTrafficStats(recv, send int64) error {
const errm = "sending traffic stats failed"
sock, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
if err != nil {
return err
}
var addr syscall.SockaddrUnix
addr.Name = "stat_path"
if err := (syscall.Connect(sock, &addr)); err != nil {
return err
}
payload := make([]byte, 16)
binary.LittleEndian.PutUint64(payload, uint64(send))
binary.LittleEndian.PutUint64(payload[8:], uint64(recv))
if n, err := syscall.Write(sock, payload); err != nil {
return err
} else if n != 16 {
return errors.New(errm)
}
ret := []byte{9}
if n, err := syscall.Read(sock, ret); err != nil {
return err
} else if n != 1 {
return errors.New(errm)
}
syscall.Close(sock)
if ret[0] != 0 {
return errors.New(errm)
}
return nil
}
var _int_value_one int = 1
var _little_endian = *(*byte)(unsafe.Pointer(&_int_value_one)) == 1
func sendFD(sock int, fd int) error {
cmsg := &syscall.Cmsghdr{
Level: syscall.SOL_SOCKET,
Type: syscall.SCM_RIGHTS,
}
const hdrsize = syscall.SizeofCmsghdr
ln := byte(hdrsize + strconv.IntSize/8)
h := (*[8]byte)(unsafe.Pointer(&cmsg.Len))
if _little_endian {
h[0] = ln
} else {
h[3+4*(^cmsg.Len<<32>>63)] = ln
}
buffer := make([]byte, cmsg.Len)
copy(buffer, (*[hdrsize]byte)(unsafe.Pointer(cmsg))[:])
*(*int)(unsafe.Pointer(&buffer[hdrsize])) = fd
return syscall.Sendmsg(sock, []byte{'!'}, buffer, nil, 0)
}