-
Notifications
You must be signed in to change notification settings - Fork 3
/
parse.go
258 lines (217 loc) · 5.63 KB
/
parse.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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
package wguser
import (
"bufio"
"bytes"
"encoding/hex"
"fmt"
"io"
"net"
"os"
"strconv"
"time"
"github.com/bi-zone/ruwireguard-go/wgctrl/wgtypes"
)
// The WireGuard userspace configuration protocol is described here:
// https://www.wireguard.com/xplatform/#cross-platform-userspace-implementation.
// getDevice gathers device information from a device specified by its path
// and returns a Device.
func (c *Client) getDevice(device string) (*wgtypes.Device, error) {
conn, err := c.dial(device)
if err != nil {
return nil, err
}
defer conn.Close()
// Get information about this device.
if _, err := io.WriteString(conn, "get=1\n\n"); err != nil {
return nil, err
}
// Parse the device from the incoming data stream.
d, err := parseDevice(conn)
if err != nil {
return nil, err
}
// TODO(mdlayher): populate interface index too?
d.Name = deviceName(device)
d.Type = wgtypes.Userspace
return d, nil
}
// parseDevice parses a Device and its Peers from an io.Reader.
func parseDevice(r io.Reader) (*wgtypes.Device, error) {
var dp deviceParser
s := bufio.NewScanner(r)
for s.Scan() {
b := s.Bytes()
if len(b) == 0 {
// Empty line, done parsing.
break
}
// All data is in key=value format.
kvs := bytes.Split(b, []byte("="))
if len(kvs) != 2 {
return nil, fmt.Errorf("wguser: invalid key=value pair: %q", string(b))
}
dp.Parse(string(kvs[0]), string(kvs[1]))
}
if err := s.Err(); err != nil {
return nil, err
}
return dp.Device()
}
// A deviceParser accumulates information about a Device and its Peers.
type deviceParser struct {
d wgtypes.Device
err error
parsePeers bool
peers int
hsSec, hsNano int
}
// Device returns a Device or any errors that were encountered while parsing
// a Device.
func (dp *deviceParser) Device() (*wgtypes.Device, error) {
if dp.err != nil {
return nil, dp.err
}
// Compute remaining fields of the Device now that all parsing is done.
dp.d.PublicKey = dp.d.PrivateKey.PublicKey()
return &dp.d, nil
}
// Parse parses a single key/value pair into fields of a Device.
func (dp *deviceParser) Parse(key, value string) {
switch key {
case "errno":
// 0 indicates success, anything else returns an error number that matches
// definitions from errno.h.
if errno := dp.parseInt(value); errno != 0 {
// TODO(mdlayher): return actual errno on Linux?
dp.err = os.NewSyscallError("read", fmt.Errorf("wguser: errno=%d", errno))
return
}
case "public_key":
// We've either found the first peer or the next peer. Stop parsing
// Device fields and start parsing Peer fields, including the public
// key indicated here.
dp.parsePeers = true
dp.peers++
dp.d.Peers = append(dp.d.Peers, wgtypes.Peer{
PublicKey: dp.parseKey(value),
})
return
}
// Are we parsing peer fields?
if dp.parsePeers {
dp.peerParse(key, value)
return
}
// Device field parsing.
switch key {
case "private_key":
dp.d.PrivateKey = dp.parseKey(value)
case "listen_port":
dp.d.ListenPort = dp.parseInt(value)
case "fwmark":
dp.d.FirewallMark = dp.parseInt(value)
}
}
// curPeer returns the current Peer being parsed so its fields can be populated.
func (dp *deviceParser) curPeer() *wgtypes.Peer {
return &dp.d.Peers[dp.peers-1]
}
// peerParse parses a key/value field into the current Peer.
func (dp *deviceParser) peerParse(key, value string) {
p := dp.curPeer()
switch key {
case "preshared_key":
p.PresharedKey = dp.parseKey(value)
case "endpoint":
p.Endpoint = dp.parseAddr(value)
case "last_handshake_time_sec":
dp.hsSec = dp.parseInt(value)
case "last_handshake_time_nsec":
dp.hsNano = dp.parseInt(value)
// Assume that we've seen both seconds and nanoseconds and populate this
// field now. However, if both fields were set to 0, assume we have never
// had a successful handshake with this peer, and return a zero-value
// time.Time to our callers.
if dp.hsSec > 0 && dp.hsNano > 0 {
p.LastHandshakeTime = time.Unix(int64(dp.hsSec), int64(dp.hsNano))
}
case "tx_bytes":
p.TransmitBytes = dp.parseInt64(value)
case "rx_bytes":
p.ReceiveBytes = dp.parseInt64(value)
case "persistent_keepalive_interval":
p.PersistentKeepaliveInterval = time.Duration(dp.parseInt(value)) * time.Second
case "allowed_ip":
cidr := dp.parseCIDR(value)
if cidr != nil {
p.AllowedIPs = append(p.AllowedIPs, *cidr)
}
case "protocol_version":
p.ProtocolVersion = dp.parseInt(value)
}
}
// parseKey parses a Key from a hex string.
func (dp *deviceParser) parseKey(s string) wgtypes.Key {
if dp.err != nil {
return wgtypes.Key{}
}
b, err := hex.DecodeString(s)
if err != nil {
dp.err = err
return wgtypes.Key{}
}
key, err := wgtypes.NewKey(b)
if err != nil {
dp.err = err
return wgtypes.Key{}
}
return key
}
// parseInt parses an integer from a string.
func (dp *deviceParser) parseInt(s string) int {
if dp.err != nil {
return 0
}
v, err := strconv.Atoi(s)
if err != nil {
dp.err = err
return 0
}
return v
}
// parseInt64 parses an int64 from a string.
func (dp *deviceParser) parseInt64(s string) int64 {
if dp.err != nil {
return 0
}
v, err := strconv.ParseInt(s, 10, 64)
if err != nil {
dp.err = err
return 0
}
return v
}
// parseAddr parses a UDP address from a string.
func (dp *deviceParser) parseAddr(s string) *net.UDPAddr {
if dp.err != nil {
return nil
}
addr, err := net.ResolveUDPAddr("udp", s)
if err != nil {
dp.err = err
return nil
}
return addr
}
// parseInt parses an address CIDR from a string.
func (dp *deviceParser) parseCIDR(s string) *net.IPNet {
if dp.err != nil {
return nil
}
_, cidr, err := net.ParseCIDR(s)
if err != nil {
dp.err = err
return nil
}
return cidr
}