320 Commits

Author SHA1 Message Date
Jason A. Donenfeld
7c97fdb1e3 version: bump snapshot 2019-09-08 10:56:55 -05:00
Jason A. Donenfeld
84b5a4d83d main: simplify warnings 2019-09-08 10:56:00 -05:00
Jason A. Donenfeld
4cd06c0925 tun: openbsd: check for interface already being up
In some cases, we operate on an already-up interface, or the user brings
up the interface before we start monitoring. For those situations, we
should first check if the interface is already up.

This still technically races between the initial check and the start of
the route loop, but fixing that is a bit ugly and probably not worth it
at the moment.

Reported-by: Theo Buehler <tb@theobuehler.org>
2019-09-07 00:13:23 -05:00
Jason A. Donenfeld
d12eb91f9a namespaceapi: AddSIDToBoundaryDescriptor modifies the handle 2019-09-05 21:48:21 -06:00
Jason A. Donenfeld
73d3bd9cd5 wintun: take mutex first always
This prevents an ABA deadlock with setupapi's internal locks.
2019-09-01 21:32:28 -06:00
Jason A. Donenfeld
f3dba4c194 wintun: consider abandoned mutexes as released 2019-09-01 21:25:47 -06:00
Jason A. Donenfeld
7937840f96 ipc: windows: use protected prefix 2019-08-31 07:48:42 -06:00
Jason A. Donenfeld
e4b957183c winpipe: enforce ownership of client connection 2019-08-30 13:21:47 -06:00
Jason A. Donenfeld
950ca2ba8c wintun: put mutex into private namespace 2019-08-30 11:03:21 -06:00
Jason A. Donenfeld
df2bf34373 namespaceapi: fix mistake 2019-08-30 09:59:36 -06:00
Simon Rozman
a12b765784 namespaceapi: initial version
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-08-30 15:34:17 +02:00
Jason A. Donenfeld
14df9c3e75 wintun: take mutex so that deletion uses the right name 2019-08-30 15:34:17 +02:00
Jason A. Donenfeld
353f0956bc wintun: move ring constants into module 2019-08-29 13:22:17 -06:00
Jason A. Donenfeld
fa7763c268 wintun: delete all interfaces is not used anymore 2019-08-29 12:22:15 -06:00
Jason A. Donenfeld
d94bae8348 wintun: Wintun->Interface 2019-08-29 12:20:40 -06:00
Jason A. Donenfeld
7689d09336 wintun: keep reference to pool in wintun object 2019-08-29 12:13:16 -06:00
Simon Rozman
69c26dc258 wintun: introduce adapter pools
This makes wintun package reusable for non-WireGuard applications.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-08-29 18:00:44 +02:00
Jason A. Donenfeld
e862131d3c wintun: simplify rename logic 2019-08-28 19:31:20 -06:00
Jason A. Donenfeld
da28a3e9f3 wintun: give better errors when ndis interface listing fails 2019-08-28 08:39:26 -06:00
Jason A. Donenfeld
3bf3322b2c wintun: also check for numbered suffix and friendly name 2019-08-28 08:08:07 -06:00
Simon Rozman
7305b4ce93 wintun: upgrade deleting all interfaces and make it reusable
DeleteAllInterfaces() didn't check if SPDRP_DEVICEDESC == "WireGuard
Tunnel". It deleted _all_ Wintun adapters, not just WireGuard's.

Furthermore, the DeleteAllInterfaces() was upgraded into a new function
called DeleteMatchingInterfaces() for selectively deletion. This will
be used by WireGuard to clean stale Wintun adapters.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-08-28 11:39:01 +02:00
Jason A. Donenfeld
26fb615b11 wintun: cleanup earlier 2019-08-27 11:59:15 -06:00
Jason A. Donenfeld
7fbb24afaa wintun: rename duplicate adapters instead of ourselves 2019-08-27 11:59:15 -06:00
Jason A. Donenfeld
d9008ac35c wintun: match suffix numbers 2019-08-26 14:46:43 -06:00
Jason A. Donenfeld
f8198c0428 device: getsockname on linux to determine port
It turns out Go isn't passing the pointer properly so we wound up with a
zero port every time.
2019-08-25 12:45:13 -06:00
Jason A. Donenfeld
0c540ad60e wintun: make description consistent across fields 2019-08-24 12:29:17 +02:00
Jason A. Donenfeld
3cedc22d7b wintun: try multiple names until one isn't a duplicate 2019-08-22 08:52:59 +02:00
Jason A. Donenfeld
68fea631d8 wintun: use nci.dll directly instead of buggy netshell 2019-08-21 09:16:12 +02:00
Jason A. Donenfeld
ef23100a4f wintun: set friendly a bit better
This is still wrong, but NETSETUPPKEY_Driver_FriendlyName seems a bit
tricky to use.
2019-08-20 16:06:55 +02:00
Jason A. Donenfeld
eb786cd7c1 wintun: also set friendly name after setting interface name 2019-08-19 10:12:50 +02:00
Jason A. Donenfeld
333de75370 wintun: defer requires unique variable 2019-08-19 10:12:50 +02:00
Jason A. Donenfeld
d20459dc69 wintun: set adapter description name 2019-08-19 10:12:50 +02:00
Jason A. Donenfeld
01786286c1 tun: windows: don't spin unless we really need it 2019-08-19 10:12:50 +02:00
Jason A. Donenfeld
b16dba47a7 version: bump snapshot 2019-08-05 19:29:12 +02:00
Jason A. Donenfeld
4be9630ddc device: drop lock before expiring keys 2019-08-05 17:46:34 +02:00
Jason A. Donenfeld
4e3018a967 uapi: skip peers with invalid keys 2019-08-05 16:57:41 +02:00
Jason A. Donenfeld
b4010123f7 tun: windows: spin for only a millisecond/80
Performance stays the same as before.
2019-08-03 19:11:21 +02:00
Simon Rozman
1ff37e2b07 wintun: merge opening device registry key
This also introduces waiting for key to appear on initial access.

See if this resolves the issue caused by HDD power-up delay resulting in
failure to create the adapter.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-08-02 16:08:49 +02:00
Simon Rozman
f5e54932e6 wintun: simplify checking reboot requirement
We never checked checkReboot() reported error anyway.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-08-02 16:08:49 +02:00
Simon Rozman
73698066d1 wintun: refactor err == nil error checking
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-08-02 15:18:58 +02:00
Jason A. Donenfeld
05ece4d167 wintun: handle error for deadgwdetect 2019-08-02 14:37:09 +02:00
Jason A. Donenfeld
6d78f89557 tun: darwin: do not attempt to close tun.event twice
Previously it was possible for this to race. It turns out we really
don't need to set anything to -1 anyway.
2019-08-02 12:24:17 +02:00
Jason A. Donenfeld
a2249449d6 wintun: get interface path properly with cfgmgr 2019-07-23 14:58:46 +02:00
Jason A. Donenfeld
eeeac287ef tun: windows: style 2019-07-23 11:45:48 +02:00
Jason A. Donenfeld
b5a7cbf069 wintun: simplify resolution of dev node 2019-07-23 11:45:13 +02:00
Jason A. Donenfeld
50cd522cb0 wintun: enable sharing of pnp node 2019-07-22 17:01:27 +02:00
Jason A. Donenfeld
5ba866a5c8 tun: windows: close event handle on shutdown 2019-07-22 09:37:20 +02:00
Jason A. Donenfeld
2f101fedec ipc: windows: match SDDL of WDK and make monkeyable 2019-07-19 15:34:26 +02:00
Jason A. Donenfeld
3341e2d444 tun: windows: get rid of retry logic
Things work fine on Windows 8.
2019-07-19 14:01:34 +02:00
Jason A. Donenfeld
1b550f6583 tun: windows: use specific IOCTL code 2019-07-19 08:30:19 +02:00
Jason A. Donenfeld
7bc0e11831 device: do not crash on nil'd bind in windows binding 2019-07-18 19:34:45 +02:00
Jason A. Donenfeld
31ff9c02fe tun: windows: open file at startup time 2019-07-18 19:27:27 +02:00
Jason A. Donenfeld
1e39c33ab1 tun: windows: silently drop packet when ring is full 2019-07-18 15:48:34 +02:00
Jason A. Donenfeld
6c50fedd8e tun: windows: switch to NDIS device object 2019-07-18 12:26:57 +02:00
Jason A. Donenfeld
298d759f3e wintun: calculate path of NDIS device object symbolic link 2019-07-18 10:25:20 +02:00
Michael Zeltner
4d5819183e tun: openbsd: don't change MTU when it's already the expected size
Allows for running wireguard-go as non-root user.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-07-18 10:25:20 +02:00
Jason A. Donenfeld
9ea9a92117 tun: windows: spin for a bit before falling back to event object 2019-07-18 10:25:20 +02:00
Simon Rozman
2e24e7dcae tun: windows: implement ring buffers
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-07-17 14:32:13 +02:00
Jason A. Donenfeld
a961aacc9f device: immediately rekey all peers after changing device private key
Reported-by: Derrick Pallas <derrick@pallas.us>
2019-07-11 17:37:35 +02:00
Jason A. Donenfeld
b0cf53b078 README: update windows info 2019-07-08 14:52:49 +02:00
Jason A. Donenfeld
5c3d333f10 tun: windows: registration of write buffer no longer required 2019-07-05 14:17:48 +02:00
Jason A. Donenfeld
d8448f8a02 tun: windows: decrease alignment to 4 2019-07-05 07:53:19 +02:00
Jason A. Donenfeld
13abbdf14b tun: windows: delay initial write
Otherwise we provoke Wintun 0.3.
2019-07-04 22:41:42 +02:00
Jason A. Donenfeld
f361e59001 device: receive: uniform message for source address check 2019-07-01 15:24:50 +02:00
Jason A. Donenfeld
b844f1b3cc tun: windows: packetNum is unused 2019-07-01 15:23:44 +02:00
Jason A. Donenfeld
dd8817f50e device: receive: simplify flush loop 2019-07-01 15:23:24 +02:00
Jason A. Donenfeld
5e6eff81b6 tun: windows: inform wintun of maximum buffer length for writes 2019-06-26 13:27:48 +02:00
Jason A. Donenfeld
c69d026649 tun: windows: never retry open on Windows 10 2019-06-18 17:51:29 +02:00
Matt Layher
1f48971a80 tun: remove TUN prefix from types to reduce stutter elsewhere
Signed-off-by: Matt Layher <mdlayher@gmail.com>
2019-06-14 18:35:57 +02:00
Jason A. Donenfeld
3371f8dac6 device: update transfer counters correctly
The rule is to always update them to the full packet size minus UDP/IP
encapsulation for all authenticated packet types.
2019-06-11 18:13:52 +02:00
Jason A. Donenfeld
41fdbf0971 wintun: increase registry timeout 2019-06-11 00:33:07 +02:00
Jason A. Donenfeld
03eee4a778 wintun: add helper for cleaning up 2019-06-10 11:34:59 +02:00
Jason A. Donenfeld
700860f8e6 wintun: simplify error matching and remove dumb comments 2019-06-10 11:10:49 +02:00
Jason A. Donenfeld
a304f69e0d wintun: fix comments and remove hwnd param
This now looks more idiomatic.
2019-06-10 11:03:36 +02:00
Simon Rozman
baafe92888 setupapi: add SetDeviceRegistryPropertyString description
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-06-10 10:43:04 +02:00
Simon Rozman
a1a97d1e41 setupapi: unify ERROR_INSUFFICIENT_BUFFER handling
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-06-10 10:43:03 +02:00
Jason A. Donenfeld
e924280baa wintun: allow controlling GUID 2019-06-10 10:43:02 +02:00
Jason A. Donenfeld
bb3f1932fa setupapi: add DeviceInstanceID() 2019-06-10 10:43:01 +02:00
Jason A. Donenfeld
eaf17becfa global: fixup TODO comment spacing 2019-06-06 23:00:15 +02:00
Jason A. Donenfeld
6d8b68c8f3 wintun: guid functions are upstream 2019-06-06 22:39:20 +02:00
Simon Rozman
c2ed133df8 wintun: simplify DeleteInterface method signature
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-06-06 08:58:26 +02:00
Jason A. Donenfeld
108c37a056 wintun: don't run HrRenameConnection in separate thread
It's very slow, but unfortunately we haven't a choice. NLA needs this to
have completed.
2019-06-05 13:09:20 +02:00
Simon Rozman
e4b0ef29a1 tun: windows: obsolete 256 packets per exchange buffer limitation
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-06-05 11:55:28 +02:00
Simon Rozman
625e445b22 setupapi, wintun: replace syscall with golang.org/x/sys/windows
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-06-04 14:54:56 +02:00
Simon Rozman
85b85e62e5 wintun: set DI_QUIETINSTALL flag for GUI-less device management
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-06-04 14:45:23 +02:00
Simon Rozman
014f736480 setupapi: define PropChangeParams struct
This structure is required for calling DIF_PROPERTYCHANGE installer
class.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-06-04 14:45:23 +02:00
Matt Layher
43a4589043 device: remove redundant return statements
More staticcheck fixes:

$ staticcheck ./... | grep S1023
device/noise-helpers.go:45:2: redundant return statement (S1023)
device/noise-helpers.go:54:2: redundant return statement (S1023)
device/noise-helpers.go:64:2: redundant return statement (S1023)

Signed-off-by: Matt Layher <mdlayher@gmail.com>
2019-06-04 13:01:52 +02:00
Matt Layher
8d76ac8cc4 device: use bytes.Equal for equality check, simplify assertEqual
Signed-off-by: Matt Layher <mdlayher@gmail.com>
2019-06-04 13:01:52 +02:00
Matt Layher
18b6627f33 device, ratelimiter: replace uses of time.Now().Sub() with time.Since()
Simplification found by staticcheck:

$ staticcheck ./... | grep S1012
device/cookie.go:90:5: should use time.Since instead of time.Now().Sub (S1012)
device/cookie.go:127:5: should use time.Since instead of time.Now().Sub (S1012)
device/cookie.go:242:5: should use time.Since instead of time.Now().Sub (S1012)
device/noise-protocol.go:304:13: should use time.Since instead of time.Now().Sub (S1012)
device/receive.go:82:46: should use time.Since instead of time.Now().Sub (S1012)
device/send.go:132:5: should use time.Since instead of time.Now().Sub (S1012)
device/send.go:139:5: should use time.Since instead of time.Now().Sub (S1012)
device/send.go:235:59: should use time.Since instead of time.Now().Sub (S1012)
device/send.go:393:9: should use time.Since instead of time.Now().Sub (S1012)
ratelimiter/ratelimiter.go:79:10: should use time.Since instead of time.Now().Sub (S1012)
ratelimiter/ratelimiter.go:87:10: should use time.Since instead of time.Now().Sub (S1012)

Change applied using:

$ find . -type f -name "*.go" -exec sed -i "s/Now().Sub(/Since(/g" {} \;

Signed-off-by: Matt Layher <mdlayher@gmail.com>
2019-06-03 22:15:41 +02:00
Matt Layher
80ef2a42e6 ipc/winpipe: go fmt
Signed-off-by: Matt Layher <mdlayher@gmail.com>
2019-06-03 22:15:36 +02:00
Jason A. Donenfeld
da61947ec3 tun: windows: mitigate infinite loop in Flush()
It's possible that for whatever reason, we keep returning EOF, resulting
in repeated close/open/write operations, except with empty packets.
2019-05-31 16:55:03 +02:00
Jason A. Donenfeld
d9f995209c device: add SendKeepalivesToPeersWithCurrentKeypair for handover 2019-05-30 15:16:16 +02:00
Jason A. Donenfeld
d0ab883ada tai64n: account for whitening in test 2019-05-29 18:44:53 +02:00
Matt Layher
32912dc778 device, tun: rearrange code and fix device tests
Signed-off-by: Matt Layher <mdlayher@gmail.com>
2019-05-29 18:34:55 +02:00
Jason A. Donenfeld
d4034e5f8a wintun: remove extra / 2019-05-26 02:20:01 +02:00
Jason A. Donenfeld
fbcd995ec1 device: darwin actually doesn't need bound interfaces 2019-05-25 18:10:52 +02:00
Jason A. Donenfeld
e7e286ba6c device: make initiations per second match kernel implementation 2019-05-25 02:07:18 +02:00
Jason A. Donenfeld
f70546bc2e device: timers: add jitter on ack failure reinitiation 2019-05-24 13:48:25 +02:00
Simon Rozman
6a0a3a5406 wintun: revise GetInterface()
- Make foreign interface found error numeric to ease condition
  detection.
- Update GetInterface() documentation.
- Make tun.CreateTUN() quit when foreign interface found before
  attempting to create a Wintun interface with a duplicate name.
  Creation is futile.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-05-24 09:29:57 +02:00
Jason A. Donenfeld
8fdcf5ee30 wintun: never return nil, nil 2019-05-23 15:25:53 +02:00
Jason A. Donenfeld
a74a29bc93 ipc: use simplified fork of winio 2019-05-23 15:16:02 +02:00
Simon Rozman
dc9bbec9db setupapi: trim "Get" from getters
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-05-22 19:31:52 +02:00
Jason A. Donenfeld
a6dbe4f475 wintun: don't try to flush interface, but rather delete 2019-05-17 16:06:02 +02:00
Jason A. Donenfeld
c718f3940d device: fail to give bind if it doesn't exist 2019-05-17 15:35:20 +02:00
Jason A. Donenfeld
95c70b8032 wintun: make certain methods private 2019-05-17 15:01:08 +02:00
Jason A. Donenfeld
583ebe99f1 version: bump snapshot 2019-05-17 10:28:04 +02:00
Jason A. Donenfeld
a6dd282600 makefile: do not show warning on non-linux 2019-05-17 10:27:51 +02:00
Simon Rozman
7d5f5bcc0d wintun: change acronyms to uppercase
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-05-17 10:22:34 +02:00
Jason A. Donenfeld
3bf41b06ae global: regroup all imports 2019-05-14 09:09:52 +02:00
Jason A. Donenfeld
3147f00089 wintun: registry: fix nits 2019-05-11 17:25:48 +02:00
Simon Rozman
6c1b66802f wintun: registry: revise value reading
- Make getStringValueRetry() reusable for reading any value type. This
  merges code from GetIntegerValueWait().
- expandString() >> toString() and extend to support REG_MULTI_SZ
  (to return first value of REG_MULTI_SZ). Furthermore, doing our own
  UTF-16 to UTF-8 conversion works around a bug in windows/registry's
  GetStringValue() non-zero terminated string handling.
- Provide toInteger() analogous to toString()
- GetStringValueWait() tolerates and reads REG_MULTI_SZ too now. It
  returns REG_MULTI_SZ[0], making GetFirstStringValueWait() redundant.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-05-11 17:14:37 +02:00
Jason A. Donenfeld
5669ed326f wintun: call HrRenameConnection in another thread 2019-05-10 21:31:37 +02:00
Jason A. Donenfeld
2d847a38a2 wintun: add LUID accessor 2019-05-10 21:30:23 +02:00
Jason A. Donenfeld
7a8553aef0 wintun: enumerate faster by using COMPATDRIVER instead of CLASSDRIVER 2019-05-10 20:30:59 +02:00
Jason A. Donenfeld
a6045ac042 wintun: destroy devinfolist after usage 2019-05-10 20:19:11 +02:00
Simon Rozman
1c92b48415 wintun: registry: replace REG_NOTIFY with NOTIFY
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-05-10 18:09:20 +02:00
Jason A. Donenfeld
c267965bf8 wintun: IpConfig is a MULTI_SZ, and fix errors 2019-05-10 18:06:49 +02:00
Jason A. Donenfeld
1bf1dadf15 wintun: poll for device key
It's actually pretty hard to guess where it is.
2019-05-10 17:34:03 +02:00
Jason A. Donenfeld
f9dcfccbb7 wintun: fix scope of error object 2019-05-10 16:59:24 +02:00
Simon Rozman
7e962a9932 wintun: wait for interface registry key on device creation
By using RegNotifyChangeKeyValue(). Also disable dead gateway detection.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-05-10 16:43:58 +02:00
Jason A. Donenfeld
586112b5d7 conn: remove scope when sanity checking IP address format 2019-05-09 15:42:35 +02:00
Simon Rozman
dcb8f1aa6b wintun: fix GUID leading zero padding
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-05-09 12:16:21 +02:00
Jason A. Donenfeld
b16b0e4cf7 mod: update deps 2019-05-03 09:37:29 +02:00
Jason A. Donenfeld
81ca08f1b3 setupapi: safer aliasing of slice types 2019-05-03 09:34:00 +02:00
Jason A. Donenfeld
2e988467c2 wintun: work around GetInterface staleness bug 2019-05-03 00:42:36 +02:00
Jason A. Donenfeld
46dbf54040 wintun: don't retry when not creating
The only time we're trying to counteract the race condition is when
we're creating a driver. When we're simply looking up all drivers, it
doesn't make sense to retry.
2019-05-02 23:53:15 +02:00
Jason A. Donenfeld
247e14693a wintun: try harder to open registry key
This sucks. Can we please find a deterministic way of doing this
instead?
2019-04-29 14:00:49 +02:00
Jason A. Donenfeld
3945a299ff go.mod: use vendored winio 2019-04-29 08:09:38 +02:00
Jason A. Donenfeld
bb42ec7d18 tun: freebsd: work around numerous kernel panics on shutdown
There are numerous race conditions. But even this will crash it:

while true; do ifconfig tun0 create; ifconfig tun0 destroy; done

It seems like LLv6 is related, which we're not using anyway, so
explicitly disable it on the interface.
2019-04-23 18:00:23 +09:00
Simon Rozman
f1dc167901 setupapi: Fix struct size mismatches
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-04-19 10:08:11 +02:00
Jason A. Donenfeld
c7a26dfef3 setupapi: actually fix padding by rounding up to sizeof(void*) 2019-04-19 10:19:00 +09:00
Jason A. Donenfeld
d024393335 tun: darwin: write routeSocket variable in helper
Otherwise the race detector "complains".
2019-04-19 07:53:19 +09:00
Jason A. Donenfeld
d9078fe772 main: revise warnings 2019-04-19 07:48:09 +09:00
Jason A. Donenfeld
d3dd991e4e device: send: check packet length before freeing element 2019-04-18 23:23:03 +09:00
Simon Rozman
5811447b38 setupapi: Revise DrvInfoDetailData struct size calculation
Go adds trailing padding to DrvInfoDetailData struct in GOARCH=386 which
confuses SetupAPI expecting exactly sizeof(SP_DRVINFO_DETAIL_DATA).

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-04-18 10:39:22 +02:00
Jason A. Donenfeld
e0a8c22aa6 windows: use proper constants from updated x/sys 2019-04-13 02:02:02 +02:00
Jason A. Donenfeld
0b77bf78cd conn: linux: RTA_MARK has moved to x/sys 2019-04-13 02:01:20 +02:00
Simon Rozman
ef5f3ad80a tun: windows: Adopt new error codes returned by Wintun
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-04-11 19:38:11 +02:00
Simon Rozman
a291fdd746 tun: windows: do not sleep after OPERATION_ABORTED on write
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-04-11 19:37:04 +02:00
Jason A. Donenfeld
d50e390904 main_windows: use proper version constant 2019-04-09 10:45:40 +02:00
Jason A. Donenfeld
18fa270472 version: put version in right place 2019-04-09 10:39:48 +02:00
Jason A. Donenfeld
f156a53ff4 version: bump snapshot 2019-04-09 07:37:22 +02:00
Jason A. Donenfeld
e680008700 tun: windows: do not sleep after OPERATION_ABORTED 2019-04-09 07:36:03 +02:00
Simon Rozman
767c86f8cb tun: windows: Retry R/W on ERROR_OPERATION_ABORTED
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-04-04 09:20:18 +02:00
Simon Rozman
421c1f9143 tun: windows: Attempt to reopen handle on all errors
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-04-03 05:41:38 +02:00
Jason A. Donenfeld
ac25702eaf wintun: rename device using undocumented API that netsh.exe uses 2019-04-01 12:04:44 +02:00
Jason A. Donenfeld
92f8474832 wintun: add more retry loops 2019-04-01 09:07:43 +02:00
Jason A. Donenfeld
2e0ed4614a tun: windows: cancel ongoing reads on closing and delete after close
This reverts commit 52ec440d79 and adds
some spice.
2019-03-26 16:14:32 +01:00
Jason A. Donenfeld
2fa80c0cb7 wintun: query for NetCfgInstanceId several times 2019-03-22 16:48:40 -06:00
Jason A. Donenfeld
52ec440d79 tun: windows: delete interface before deleting file handles 2019-03-22 16:45:58 -06:00
Simon Rozman
2faf2dcf90 tun: windows: Make adapter rename asynchronous
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-22 16:36:30 +01:00
Simon Rozman
41c30a7279 tun: windows: Adapter devices renamed to WINTUN<LUID Index>
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-22 15:29:14 +01:00
Simon Rozman
4b1db1d39b tun: windows: Increase unavailable adapter timeout to 30sec
5 seconds was too short when debugging.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-22 13:52:51 +01:00
Simon Rozman
a80db5e65e tun: windows: Make writing persistent too
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-22 13:52:51 +01:00
Simon Rozman
9748a52073 tun: windows: Fix paused adapter test
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-22 13:52:51 +01:00
Jason A. Donenfeld
317d716d66 tun: windows: just open two file handles 2019-03-21 15:20:09 -06:00
Jason A. Donenfeld
6440f010ee receive: implement flush semantics 2019-03-21 14:45:41 -06:00
Jason A. Donenfeld
49ea0c9b1a tun: windows: add dummy overlapped events back
These seem basically wrong to me, but we get crashes without them.
2019-03-21 02:29:09 -06:00
Jason A. Donenfeld
ca59b60aa7 tun: windows: use new constants in sys 2019-03-20 23:42:30 -06:00
Jason A. Donenfeld
c050c6e60f uapi: remove unhelpful log messages 2019-03-20 23:40:20 -06:00
Simon Rozman
91b4e909bb wintun: Use native Win32 API for I/O
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-21 00:56:45 +01:00
Jason A. Donenfeld
2c51d6af48 uapi: report endpoint error 2019-03-19 00:34:04 -06:00
Jason A. Donenfeld
03f2e2614a tun: windows: wintun does iocp 2019-03-18 02:42:45 -06:00
Jason A. Donenfeld
b0e0ab308d tun: windows: temporary hack for forcing MTU 2019-03-13 02:52:32 -06:00
Jason A. Donenfeld
66fb5caf02 wintun: Poll more often 2019-03-10 03:47:54 +01:00
Jason A. Donenfeld
3dd9a0535f uapi: make ipcerror conform to interface 2019-03-10 02:49:44 +01:00
Simon Rozman
c2a2b8d739 wintun: Make errors more descriptive
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-08 10:03:57 +01:00
Simon Rozman
70449f1a97 wintun: Return correct reboot-req flag on CreateInterface() error too
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-08 10:03:57 +01:00
Simon Rozman
33c3528430 wintun: Fix double-quoted strings escaping on output
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-08 10:03:57 +01:00
Simon Rozman
30ab07e354 wintun: Introduce SetupAPI enumerator and machineName consts
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-08 10:03:57 +01:00
Odd Stranne
a6d5ef82f4 Windows: Apply strict security descriptor on pipe server
Signed-off-by: Odd Stranne <odd@mullvad.net>
2019-03-08 10:03:56 +01:00
Jason A. Donenfeld
5c7cc256e3 uapi: windows: work out pipe semantics
Pipes can be arranged like this, so that's fine. We also apply a strict
SDDL that can't be inherited and only gives access to local system.

Developed-with: Odd Stranne <odd@mullvad.net>
2019-03-08 01:40:54 +01:00
Simon Rozman
368dea72fe wintun: Cleanup
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-07 21:12:20 +01:00
Simon Rozman
9b22255cad wintun: Refactor network registry key name generation
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-07 21:12:20 +01:00
Simon Rozman
11f5780250 wintun: Revise interface creation wait
DIF_INSTALLDEVICE returns almost immediately, while the device
installation continues in the background. It might take a while, before
all registry keys and values are populated.

Previously, wireguard-go waited for HKLM\SYSTEM\CurrentControlSet\
Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\<id> registry key
only.

Followed by a SetInterfaceName() method of Wintun struct which tried to
access HKLM\SYSTEM\CurrentControlSet\Control\Network\
{4D36E972-E325-11CE-BFC1-08002BE10318}\<id>\Connection registry key
might not be available yet.

This commit loops until both registry keys are available before
returning from CreateInterface() function.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-07 21:12:20 +01:00
Jason A. Donenfeld
26af6c4651 receive: squelch tear down error 2019-03-07 02:03:48 +01:00
Jason A. Donenfeld
92f72f5aa6 tun: linux: work out netpoll trick 2019-03-07 01:51:41 +01:00
Simon Rozman
1fdf7b19a3 wintun: Resolve some of golint warnings
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-04 16:37:11 +01:00
Simon Rozman
a1aabb21ae Elaborate the failing step when forwarding errors on return
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-04 16:37:11 +01:00
Simon Rozman
9041d38e2d Simplify reading NetCfgInstanceId from registry
As querying non-existing registry value and reading non-existing
registry string value both return ERROR_FILE_NOT_FOUND, we can
use later only.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-04 16:37:11 +01:00
Simon Rozman
cddfd9a0d8 Unify interface-specific network registry key open
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-03-04 16:37:11 +01:00
Jason A. Donenfeld
68f0721c6a tun: import mobile particularities 2019-03-04 16:37:11 +01:00
Jason A. Donenfeld
b8e85267cf boundif: introduce API for socket binding 2019-03-04 16:37:11 +01:00
Jason A. Donenfeld
69f0fe67b6 global: begin modularization 2019-03-03 05:00:40 +01:00
Jason A. Donenfeld
d435be35ca tun: windows: expose GUID 2019-03-01 00:11:12 +01:00
Jason A. Donenfeld
967d1a0f3d tun: allow special methods in NativeTun 2019-03-01 00:05:57 +01:00
Jason A. Donenfeld
88ff67fb6f tun: linux: netpoll is broken for tun's epoll
So this mostly reverts the switch to Sysconn for Linux.

Issue: https://github.com/golang/go/issues/30426
2019-02-27 04:38:26 +01:00
Jason A. Donenfeld
971be13e77 tun: linux: netlink sock needs cleaning up but file will be gc'd 2019-02-27 04:11:41 +01:00
Jason A. Donenfeld
366cbd11a4 tun: use netpoll instead of rwcancel
The new sysconn function of Go 1.12 makes this possible:

package main

import "log"
import "os"
import "unsafe"
import "time"
import "syscall"
import "sync"
import "golang.org/x/sys/unix"

func main() {
	fd, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0)
	if err != nil {
		log.Fatal(err)
	}

	var ifr [unix.IFNAMSIZ + 64]byte
	copy(ifr[:], []byte("cheese"))
	*(*uint16)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = unix.IFF_TUN

	var errno syscall.Errno
	s, _ := fd.SyscallConn()
	s.Control(func(fd uintptr) {
		_, _, errno = unix.Syscall(
			unix.SYS_IOCTL,
			fd,
			uintptr(unix.TUNSETIFF),
			uintptr(unsafe.Pointer(&ifr[0])),
		)
	})
	if errno != 0 {
		log.Fatal(errno)
	}

	b := [4]byte{}
	wait := sync.WaitGroup{}
	wait.Add(1)
	go func() {
		_, err := fd.Read(b[:])
		log.Print("Read errored: ", err)
		wait.Done()
	}()
	time.Sleep(time.Second)
	log.Print("Closing")
	err = fd.Close()
	if err != nil {
		log.Print("Close errored: " , err)
	}
	wait.Wait()
	log.Print("Exiting")
}
2019-02-27 01:52:55 +01:00
Jason A. Donenfeld
ab0f442daf tun: use sysconn instead of .Fd with Go 1.12 2019-02-27 01:34:11 +01:00
Jason A. Donenfeld
66524c1f7e Rearrange imports 2019-02-22 20:59:43 +01:00
Jason A. Donenfeld
6e4460ae65 device: send persistent keepalive when bringing up device
Reported-by: Marcelo Bello
2019-02-22 19:33:28 +01:00
Simon Rozman
d002eff155 wintun: Read/write packet size from/to exchange buffer directly
Driver <-> user-space communication is local and using native endian.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-22 16:16:14 +01:00
Simon Rozman
e06a8f8f9f wintun: Make two-step slicing a one step
Stop relying to Go compiler optimizations and calculate the end offset
directly.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-22 16:11:33 +01:00
Simon Rozman
ac4944a708 wintun: Write exchange buffer increased back to 1MiB
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-20 20:13:33 +01:00
Simon Rozman
2491f9d454 wintun: Migrate from unsafe buffer handling to encoding/binary
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-20 20:10:24 +01:00
Simon Rozman
8091c6474a wintun: Adopt new packet data alignment
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-20 19:56:10 +01:00
Simon Rozman
040da43889 wintun: Cleanup
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-20 18:38:18 +01:00
Simon Rozman
b7025b5627 wintun: Add TUN device locking
In case reading from TUN device detected TUN device was closed, it
closed the file handle and set tunFile to nil. The tunFile is
automatically reopened on retry, but... If another packet comes in the
WireGuard calls Write() method. With tunFile set to nil, this will
cause access violation.

Therefore, locking was introduced.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-20 13:12:08 +01:00
Simon Rozman
6581cfb885 wintun: Move exchange buffer in separate struct on heap
This allows buffer alignment and keeps it together with its meta-data.

Furthermore, the write buffer has been reduced - as long as we flush
after _every_ write, we don't need a 1MiB write buffer.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-20 11:41:37 +01:00
Simon Rozman
4863089120 wintun: Switch to dynamic packet sizes
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-19 18:50:42 +01:00
Jason A. Donenfeld
42c6d0e261 Change package path 2019-02-18 05:11:39 +01:00
Jason A. Donenfeld
f7170e5de2 Bump dependencies for ARM ChaCha20 2019-02-14 10:59:54 +01:00
Simon Rozman
b719a09a26 wintun: Auto-calculate TUN exchange buffer size
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-08 15:21:24 +01:00
Simon Rozman
f05f52637f wintun: Simplify Read method()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-08 14:31:05 +01:00
Simon Rozman
713477cfb1 wintun: Make constants private and adopt Go recommended case
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-08 08:55:23 +01:00
Simon Rozman
5981d5cacf wintun: Check for user close in read loop regardless the load
Do the WaitForSingleObject() always to provide high-load responsiveness.

Reorder events so TUN_SIGNAL_CLOSE has priority over
TUN_SIGNAL_DATA_AVAIL, to provide high-load responsiveness at all.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-08 08:48:35 +01:00
Simon Rozman
b13739ada2 wintun: Adjust tunRWQueue.left member to match Wintun driver
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-08 07:32:12 +01:00
Simon Rozman
c4988999ac setupapi: Merge _SP_DRVINFO_DETAIL_DATA and DrvInfoDetailData
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-07 23:50:43 +01:00
Simon Rozman
b662896cf4 setupapi: Merge SP_DRVINFO_DATA and DrvInfoData
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-07 23:50:43 +01:00
Simon Rozman
0525f6b112 setupapi: Rename SP_REMOVEDEVICE_PARAMS to RemoveDeviceParams
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-07 23:50:43 +01:00
Simon Rozman
9d830826c5 setupapi: Rename SP_CLASSINSTALL_HEADER to ClassInstallHeader
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-07 23:50:43 +01:00
Simon Rozman
bd963497da setupapi: Merge _SP_DEVINSTALL_PARAMS and DevInstallParams
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-07 23:50:30 +01:00
Simon Rozman
05d25fd1b7 setupapi: Merge _SP_DEVINFO_LIST_DETAIL_DATA and DevInfoListDetailData
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-07 23:49:50 +01:00
Simon Rozman
6d2729dccc setupapi: Rename SP_DEVINFO_DATA to DevInfoData
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-07 22:43:02 +01:00
Simon Rozman
d87cbeeb2f wintun: Detect if a foreign interface with the same name exists
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-07 22:02:51 +01:00
Simon Rozman
043b7e8013 wintun: Clean excessive setupapi.DevInfo.GetDeviceInfoListDetail() call
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-07 20:49:41 +01:00
Simon Rozman
ef48d4fa95 wintun: Explain rationale behind case-insensitive interface names
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-07 19:42:59 +01:00
Simon Rozman
f7276ed522 wintun: Implement TODO in TestSetupDiGetDeviceRegistryProperty()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-07 18:59:34 +01:00
Jason A. Donenfeld
c4b43e35a7 wintun: add FlushInterface stub 2019-02-07 18:24:28 +01:00
Jason A. Donenfeld
2efafecab5 main_windows: Get iface name from argument 2019-02-07 15:44:07 +01:00
Jason A. Donenfeld
fac1fbcd72 wintun: Compare values of GUID, not pointers, when removing 2019-02-07 04:49:15 +01:00
Jason A. Donenfeld
52aa00f3ba main_windows: Catch more exit events 2019-02-07 04:42:35 +01:00
Jason A. Donenfeld
ea59177f1c wintun: Introduce new package for obscuring Windows bits 2019-02-07 04:39:59 +01:00
Jason A. Donenfeld
306d08e692 tun_windows: Style 2019-02-07 04:08:05 +01:00
Jason A. Donenfeld
3b7a4fa3ef setupapi: Lower case params 2019-02-07 03:46:31 +01:00
Jason A. Donenfeld
223685875f setupapi: Do not export the toGo/toWindows functions 2019-02-07 02:56:31 +01:00
Jason A. Donenfeld
652158ec3c setupapi: Pass pointers instead of values 2019-02-07 02:37:19 +01:00
Simon Rozman
cb2bc4b34c tun_windows: Introduce preliminary TUN interface creation
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-06 22:30:14 +01:00
Simon Rozman
46279ad0f9 tun_windows: Stop checking minimum size of received TUN packets
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-06 20:22:04 +01:00
Simon Rozman
73df1c0871 setupapi: Add DrvInfoDetailData.IsCompatible() to simplify HID detection
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-06 20:18:44 +01:00
Simon Rozman
069016bbc4 setupapi: Add SP_DRVINFO_DATA.IsNewer() method to simplify comparison
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-06 20:17:47 +01:00
Simon Rozman
3c29434a79 setupapi: Make toUTF16() public and add UTF16ToBuf() counterpart
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-06 20:15:40 +01:00
Jason A. Donenfeld
c599bf9497 Fix up errors and paths 2019-02-05 22:06:25 +09:00
Jason A. Donenfeld
f7f63765d1 conn: close ipv4 socket when ipv6 socket fails 2019-02-05 21:55:33 +09:00
Simon Rozman
3e8f2e3fa5 setupapi: Add support for driver info lists
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 16:29:17 +01:00
Simon Rozman
7b636380e5 setupapi: Move Go<>Windows struct marshaling to types_windows.go
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 14:03:28 +01:00
Simon Rozman
99a3b628e9 setupapi: Add support for SetupDi(Get|Set)DeviceRegistryProperty()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
e7ffce0d21 setupapi: Introduce DevInfo methods for cleaner code
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
35f72239ac Add support for setupapi.SetupDi(Get|Set)SelectedDevice()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
c15cbefc12 Reorder data-types and functions to match SetupAPI.h
Adding functions with non-consistent order made setupapi package a mess.
While we could reorder data-types and functions by alphabet - it would
make searching easier - it would put ...Get... and ...Set... functions
quite apart.

Therefore, the SetupAPI.h order was adopted.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
dd998ca86a Add support for setupapi.SetupDiCreateDeviceInfo()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
024a4916c2 Add support for setupapi.setupDiCreateDeviceInfoListEx()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
963be8e993 Stop accessing SetupDiGetDeviceInfoListDetail() output on error
The data returned by SetupDiGetDeviceInfoListDetail() is nil on error
which will cause the test to crash should the function fail.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
e821cdabd2 Unify certain variable names
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
38c7acd70f Simplify SetupDiEnumDeviceInfo() synopsis
The SetupDiEnumDeviceInfo() now returns a SP_DEVINFO_DATA rather than
taking it on input to fill it on return.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
20f1512b7c Change generic local variable names with meaningful replacements
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
348b4e9f7c Add support for setupapi.SetupDiClassGuidsFromNameEx()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
f81882ee8b Clean an unused constant
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
3e0e61dd26 Replace SetupDiClassNameFromGuid() with SetupDiClassNameFromGuidEx()
The former is only a subset of the later. To minimize future
maintenance, we'll provide support for extended version only.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
9635a0b3a6 Add support for setupapi.SetupDiClassNameFromGuid()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
90b6938ca0 Stop checking for valid handle in DevInfo.Close()
User should not have called or deferred the Close() method should
SetupDiGetClassDevsEx() return an error (and invalid handle). And even
if user does that, a SetupDiDestroyDeviceInfoList(INVALID_HANDLE_VALUE)
is harmless. It just returns ERROR_INVALID_HANDLE - we have a unit test
for this in TestSetupDiDestroyDeviceInfoList().

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
269944002f Add support for setupapi.SetupDiCallClassInstaller()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
a5a1ece32f Add support for setupapi.SetupDi(Get|Set)ClassInstallParams()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
f1d5db6547 Add support for setupapi.SetupDi(Get|Set)DeviceInstallParams()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
dce5192d86 Add support for setupapi.SetupDiOpenDevRegKey()
Furthermore setupapi.DevInfoData has been obsoleted.
SetupDiEnumDeviceInfo() fills existing SP_DEVINFO_DATA structure now.
As other functions of SetupAPI use SP_DEVINFO_DATA, converting it to
DevInfoData and back would hurt performance.

Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
955d8dfe04 Add support for setupapi.SetupDiEnumDeviceInfo()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
25e18d01e6 Update exported types and functions annotations
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
45959c116a Add support for setupapi.SetupDiGetDeviceInfoListDetail()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
d41bc015cc Finish support for setupapi.SetupDiGetClassDevsEx()
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Simon Rozman
31949136df Introduce SetupAPI - Windows device and driver management API
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-02-05 12:59:42 +01:00
Jason A. Donenfeld
6f76edd045 Import windows scafolding 2019-02-05 12:59:42 +01:00
Jason A. Donenfeld
3af9aa88a3 noise: store clamped key instead of raw key 2019-02-05 12:59:42 +01:00
Jason A. Donenfeld
a5ca02d79a tai64n: whiten nano seconds
Avoid being too precise of a time oracle.
2019-02-05 12:59:42 +01:00
Jason A. Donenfeld
2b7562abbb uapi: Simpler function signature 2019-02-05 12:59:42 +01:00
Jason A. Donenfeld
89d2c5ed7a Extend structs rather than embed, when possible 2019-02-05 12:59:42 +01:00
Jason A. Donenfeld
dff424baf8 Update copyright 2019-02-05 12:59:42 +01:00
Jason A. Donenfeld
6e61c369e8 Properly bubble up setsockopt error from closure 2018-12-25 22:56:36 +01:00
Jason A. Donenfeld
8fde8334dc version: bump snapshot 2018-12-22 17:34:23 +01:00
Jason A. Donenfeld
a8326ae753 Make error messages consistent 2018-12-19 00:35:53 +01:00
Jason A. Donenfeld
05cc0c8298 Freebsd is finally normal in sys/unix 2018-12-11 18:33:13 +01:00
Jason A. Donenfeld
c967f15e44 Separate out mark setting for Windows 2018-12-11 18:29:46 +01:00
Jason A. Donenfeld
5ace0fdfe2 Use upstream's xchacha20poly1305 2018-12-10 04:23:17 +01:00
Jason A. Donenfeld
849fa400e9 Update go x/ libraries
Android 9's Bionic disallows inotify_init with seccomp, so we want the
latest unix change, and while we're at it, we update the others too.

Reported-by: Berk D. Demir <bdd@mindcast.org>
Go CL: https://go-review.googlesource.com/c/sys/+/153318
Fixes: https://lists.zx2c4.com/pipermail/wireguard/2018-December/003642.html
2018-12-10 04:04:19 +01:00
Jason A. Donenfeld
651744561e tun: remove nonblock hack for linux
This is no longer necessary and actually breaks things

Reported-by: Chris Branch <cbranch@cloudflare.com>
2018-12-06 17:17:51 +01:00
Jason A. Donenfeld
4fd55daafe tai64n: use proper nanoseconds offset
The code before was obviously wrong.

Reported-by: Vlad Krasnov <vlad@cloudflare.com>
2018-11-08 03:58:01 +01:00
Jason A. Donenfeld
276bf973e8 Use darwin tun on ios 2018-11-06 16:24:35 +01:00
Jason A. Donenfeld
c37c4ece9e uapi: typo 2018-11-05 05:46:27 +01:00
Jason A. Donenfeld
b803276061 receive: make started status uniform 2018-11-01 19:54:25 +01:00
Jason A. Donenfeld
8be1fc9c00 send: do not unlock already freed object 2018-10-18 18:15:24 +02:00
Jason A. Donenfeld
738d027f0b version: bump snapshot 2018-10-18 02:38:29 +02:00
Jason A. Donenfeld
60848b9c72 Makefile: rename default to all 2018-10-17 21:45:16 +02:00
Jason A. Donenfeld
2e772194cf tun: only call .Fd() once
Doing so tends to make the tunnel blocking, so we only retrieve it once
before we call SetNonblock, and then cache the result.
2018-10-17 21:31:42 +02:00
Jason A. Donenfeld
85b2378a07 Use go modules always 2018-10-12 01:45:33 +02:00
Jason A. Donenfeld
fddb949002 Do not build if nothing to do 2018-10-12 01:12:56 +02:00
Jason A. Donenfeld
5d6083df7e Switch to go modules 2018-10-09 18:13:56 +02:00
Jason A. Donenfeld
b41922e5c8 version: bump snapshot 2018-10-01 17:58:31 +02:00
Jason A. Donenfeld
dbb72402f2 Adding missing queueconstants file 2018-10-01 16:11:31 +02:00
Chris Branch
7c971d7ef4 Fix transport message length check
wireguard-go has a bad length check in its transport message handling.
Although it cannot be exploited because of another length check earlier in the
function, this should be fixed regardless.
2018-09-25 05:18:11 +02:00
Jason A. Donenfeld
70bcf9ecb8 Make it easy to restrict queue sizes more 2018-09-25 02:31:02 +02:00
Jason A. Donenfeld
ebc7541953 Fix shutdown races 2018-09-24 01:52:02 +02:00
Jason A. Donenfeld
833597b585 More pooling 2018-09-24 00:37:43 +02:00
Jason A. Donenfeld
cf81a28dd3 Fixup buffer freeing 2018-09-22 05:43:03 +02:00
Jason A. Donenfeld
942abf948a send: more precise padding calculation 2018-09-16 23:42:31 +02:00
Jason A. Donenfeld
47d1140361 device: preallocated buffers scheme
Not useful now but quite possibly later.
2018-09-16 23:10:19 +02:00
Jason A. Donenfeld
39d6e4f2f1 Change queueing drop order and fix memory leaks
If the queues are full, we drop the present packet, which is better for
network traffic flow. Also, we try to fix up the memory leaks with not
putting buffers from our shared pool.
2018-09-16 21:50:58 +02:00
Jason A. Donenfeld
1c02557013 send: use accessor function for buffer pool 2018-09-16 18:49:19 +02:00
Mathias Hall-Andersen
32d2148835 Fixed port overwrite issue on kernels without ipv6
Fixed an issue in CreateBind for Linux:
If ipv6 was not supported the error code would be
correctly identified as EAFNOSUPPORT and ipv4 binding attempted.
However the port would be set to 0,
which results in the subsequent create4 call requesting
a random port rather than the one provided to CreateBind.

This issue was identified by:
Kent Friis <leeloored@gmx.com>
2018-09-16 18:49:19 +02:00
Jason A. Donenfeld
5be541d147 global: fix up copyright headers 2018-09-16 18:49:19 +02:00
Jason A. Donenfeld
063becdc73 uapi: insert peer version placeholder
While we don't want people to ever use old protocols, people will
complain if the API "changes", so explicitly make the unset protocol
mean the latest, and add a dummy mechanism of specifying the protocol on
a per-peer basis, which we hope nobody actually ever uses.
2018-09-02 23:04:47 -06:00
Jason A. Donenfeld
15da869b31 Fix duplicate copyright line 2018-07-30 05:14:17 +02:00
Jason A. Donenfeld
3ad3e83c7a uapi: allow overriding socket directory at compile time 2018-07-24 14:32:35 +02:00
Jason A. Donenfeld
2e13b7b0fb send: better debug message for failed data packet 2018-07-16 16:05:36 +02:00
Jason A. Donenfeld
6b3b1c3b91 version: bump snapshot 2018-06-13 16:22:16 +02:00
Jason A. Donenfeld
6a5d0e2bcd Support IPv6-less kernels 2018-06-12 01:32:46 +02:00
Jason A. Donenfeld
0ba551807f Do not build tun device on ios 2018-06-09 03:31:17 +02:00
Jason A. Donenfeld
99d5aeeb27 Fix duplicated wording 2018-06-02 17:36:35 +02:00
Jason A. Donenfeld
a050431f26 Makefile: export PWD for OpenBSD's ksh(1)
Interestingly, ksh(1) on OpenBSD does not export PWD by default, and it
also has a notion of the "logical cwd" vs the "physical cwd", with the
latter being passed to chdir, but the former being stored in the
non-exported PWD and displayed to the user. This means that if you `cd`
into a directory that's comprised of symlinks, exec'd processes will see
the physical path. Observe:

  # ksh
  # mkdir a
  # ln -s a b
  # cd b
  # pwd
  /root/b
  # ksh -c pwd
  /root/a

The fact of separating physical and logical paths is not too uncommon
for shells (bash does it too), but not exporting PWD is very odd.

Since this is common behavior for many shells, libraries that return the
working directory will do something strange: they `stat(".")` and then
`stat(getenv("PWD"))`, and if these point to the same inode, they roll
with the value of `getenv("PWD")`, or otherwise fallback to asking the
kernel for the cwd.

Since PWD was not exported by ksh(1), Go's dep utility did not understand
it was operating inside of our faked GOPATH and became upset.

This patch works around the whole situation by simply exporting PWD
before executing dep.
2018-06-02 16:36:12 +02:00
Jason A. Donenfeld
0c976003c8 version: bump snapshot 2018-05-31 02:26:07 +02:00
Jason A. Donenfeld
955e89839f Print version number in log 2018-05-30 01:09:18 +02:00
Jason A. Donenfeld
a4cd0216c0 Update deps 2018-05-28 01:39:37 +02:00
Jason A. Donenfeld
1d7845a600 Fix typo in timers 2018-05-27 22:55:15 +02:00
Jason A. Donenfeld
5079298ce2 Disable broadcast mode on *BSD
Keeping it on makes IPv6 problematic and confuses routing daemons.
2018-05-27 22:55:15 +02:00
Jason A. Donenfeld
fc3a7635e5 Disappointing anti-sticky experiment 2018-05-27 22:55:15 +02:00
Jason A. Donenfeld
2496cdd8e6 Fix tests 2018-05-24 19:58:16 +02:00
Jason A. Donenfeld
4365b4583f Trick for being extra sensitive to route changes 2018-05-24 18:21:14 +02:00
Jason A. Donenfeld
bbf320c477 Back to sticky sockets on android 2018-05-24 17:53:00 +02:00
Jason A. Donenfeld
625d59da14 Do not build on Linux 2018-05-24 16:41:42 +02:00
Jason A. Donenfeld
2f2eca8947 Catch EINTR 2018-05-24 15:36:29 +02:00
Jason A. Donenfeld
66f6ca3e4a Remove old makefile artifact 2018-05-24 03:13:46 +02:00
105 changed files with 7417 additions and 2216 deletions

351
COPYING
View File

@@ -1,338 +1,17 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

42
Gopkg.lock generated
View File

@@ -1,42 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = [
"blake2s",
"chacha20poly1305",
"curve25519",
"internal/chacha20",
"poly1305"
]
revision = "1a580b3eff7814fc9b40602fd35256c63b50f491"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"bpf",
"internal/iana",
"internal/socket",
"ipv4",
"ipv6"
]
revision = "9ef9f5bb98a1fdc41f8cf6c250a4404b4085e389"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = [
"cpu",
"unix"
]
revision = "88eb85aaee56831ad49eaf7aa80d73de9814cde2"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "d85ae9d2b4afafc3d7535505c46368cbbbec350cf876616302c1bcf44f6ec103"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -1,15 +0,0 @@
[[constraint]]
branch = "master"
name = "golang.org/x/crypto"
[[constraint]]
branch = "master"
name = "golang.org/x/net"
[[constraint]]
branch = "master"
name = "golang.org/x/sys"
[prune]
go-tests = true
unused-packages = true

View File

@@ -1,51 +1,28 @@
PREFIX ?= /usr
DESTDIR ?=
BINDIR ?= $(PREFIX)/bin
export GO111MODULE := on
ifeq ($(shell go env GOOS),linux)
ifeq ($(wildcard .git),)
$(error Do not build this for Linux. Instead use the Linux kernel module. See wireguard.com/install/ for more info.)
else
$(shell printf 'package main\nconst UseTheKernelModuleInstead = 0xdeadbabe\n' > ireallywantobuildon_linux.go)
endif
endif
all: generate-version-and-build
all: wireguard-go
MAKEFLAGS += --no-print-directory
export GOPATH := $(CURDIR)/.gopath
export PATH := $(PATH):$(CURDIR)/.gopath/bin
GO_IMPORT_PATH := git.zx2c4.com/wireguard-go
version.go:
generate-version-and-build:
@export GIT_CEILING_DIRECTORIES="$(realpath $(CURDIR)/..)" && \
tag="$$(git describe --dirty 2>/dev/null)" && \
ver="$$(printf 'package main\nconst WireGuardGoVersion = "%s"\n' "$$tag")" && \
[ "$$(cat $@ 2>/dev/null)" != "$$ver" ] && \
echo "$$ver" > $@ && \
git update-index --assume-unchanged $@ || true
ver="$$(printf 'package device\nconst WireGuardGoVersion = "%s"\n' "$$tag")" && \
[ "$$(cat device/version.go 2>/dev/null)" != "$$ver" ] && \
echo "$$ver" > device/version.go && \
git update-index --assume-unchanged device/version.go || true
@$(MAKE) wireguard-go
.gopath/.created:
rm -rf .gopath
mkdir -p $(dir .gopath/src/$(GO_IMPORT_PATH))
ln -s ../../.. .gopath/src/$(GO_IMPORT_PATH)
touch $@
vendor/.created: Gopkg.toml Gopkg.lock | .gopath/.created
command -v dep >/dev/null || go get -v github.com/golang/dep/cmd/dep
cd .gopath/src/$(GO_IMPORT_PATH) && dep ensure -vendor-only -v
touch $@
wireguard-go: $(wildcard *.go) $(wildcard */*.go) .gopath/.created vendor/.created version.go
go build $(GO_BUILD_EXTRA_ARGS) -v $(GO_IMPORT_PATH)
wireguard-go: $(wildcard *.go) $(wildcard */*.go)
go build -v -o "$@"
install: wireguard-go
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 wireguard-go "$(DESTDIR)$(BINDIR)/wireguard-go"
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 "$<" "$(DESTDIR)$(BINDIR)/wireguard-go"
clean:
rm -f wireguard-go
update-dep: | .gopath/.created
command -v dep >/dev/null || go get -v github.com/golang/dep/cmd/dep
cd .gopath/src/$(GO_IMPORT_PATH) && dep ensure -update -v
.PHONY: clean install update-dep version.go
.PHONY: all clean install generate-version-and-build

View File

@@ -2,8 +2,6 @@
This is an implementation of WireGuard in Go.
***WARNING:*** This is a work in progress and not ready for prime time, with no official "releases" yet. It is extremely rough around the edges and leaves much to be desired. There are bugs and we are not yet in a position to make claims about its security. Beware.
## Usage
Most Linux kernel WireGuard users are used to adding an interface with `ip link add wg0 type wireguard`. With wireguard-go, instead simply run:
@@ -36,7 +34,7 @@ This runs on macOS using the utun driver. It does not yet support sticky sockets
### Windows
It is currently a work in progress to strip out the beginnings of an experiment done with the OpenVPN tuntap driver and instead port to the new UWP APIs for tunnels. In other words, this does not *yet* work on Windows.
This runs on Windows, but you should instead use it from the more [fully featured Windows app](https://git.zx2c4.com/wireguard-windows/about/), which uses this as a module.
### FreeBSD
@@ -48,7 +46,7 @@ This will run on OpenBSD. It does not yet support sticky sockets. Fwmark is mapp
## Building
This requires an installation of [go](https://golang.org) and of [dep](https://github.com/golang/dep). If dep is not installed, it will be downloaded and built as part of the build process.
This requires an installation of [go](https://golang.org) ≥ 1.12.
```
$ git clone https://git.zx2c4.com/wireguard-go
@@ -58,27 +56,22 @@ $ make
## License
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
---------------------------------------------------------------------------
Additional Permissions For Submission to Apple App Store: Provided that you
are otherwise in compliance with the GPLv2 for each covered work you convey
(including without limitation making the Corresponding Source available in
compliance with Section 3 of the GPLv2), you are granted the additional
the additional permission to convey through the Apple App Store
non-source executable versions of the Program as incorporated into each
applicable covered work as Executable Versions only under the Mozilla
Public License version 2.0 (https://www.mozilla.org/en-US/MPL/2.0/).
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"errors"

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"math/rand"

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"math/rand"

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import "errors"

44
device/boundif_android.go Normal file
View File

@@ -0,0 +1,44 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package device
import "errors"
func (device *Device) PeekLookAtSocketFd4() (fd int, err error) {
nb, ok := device.net.bind.(*nativeBind)
if !ok {
return 0, errors.New("no socket exists")
}
sysconn, err := nb.ipv4.SyscallConn()
if err != nil {
return
}
err = sysconn.Control(func(f uintptr) {
fd = int(f)
})
if err != nil {
return
}
return
}
func (device *Device) PeekLookAtSocketFd6() (fd int, err error) {
nb, ok := device.net.bind.(*nativeBind)
if !ok {
return 0, errors.New("no socket exists")
}
sysconn, err := nb.ipv6.SyscallConn()
if err != nil {
return
}
err = sysconn.Control(func(f uintptr) {
fd = int(f)
})
if err != nil {
return
}
return
}

62
device/boundif_windows.go Normal file
View File

@@ -0,0 +1,62 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package device
import (
"encoding/binary"
"errors"
"unsafe"
"golang.org/x/sys/windows"
)
const (
sockoptIP_UNICAST_IF = 31
sockoptIPV6_UNICAST_IF = 31
)
func (device *Device) BindSocketToInterface4(interfaceIndex uint32) error {
/* MSDN says for IPv4 this needs to be in net byte order, so that it's like an IP address with leading zeros. */
bytes := make([]byte, 4)
binary.BigEndian.PutUint32(bytes, interfaceIndex)
interfaceIndex = *(*uint32)(unsafe.Pointer(&bytes[0]))
if device.net.bind == nil {
return errors.New("Bind is not yet initialized")
}
sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn()
if err != nil {
return err
}
err2 := sysconn.Control(func(fd uintptr) {
err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, sockoptIP_UNICAST_IF, int(interfaceIndex))
})
if err2 != nil {
return err2
}
if err != nil {
return err
}
return nil
}
func (device *Device) BindSocketToInterface6(interfaceIndex uint32) error {
sysconn, err := device.net.bind.(*nativeBind).ipv6.SyscallConn()
if err != nil {
return err
}
err2 := sysconn.Control(func(fd uintptr) {
err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, sockoptIPV6_UNICAST_IF, int(interfaceIndex))
})
if err2 != nil {
return err2
}
if err != nil {
return err
}
return nil
}

View File

@@ -1,16 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"errors"
"net"
"strings"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"net"
)
const (
@@ -42,13 +43,18 @@ type Endpoint interface {
}
func parseEndpoint(s string) (*net.UDPAddr, error) {
// ensure that the host is an IP address
host, _, err := net.SplitHostPort(s)
if err != nil {
return nil, err
}
if i := strings.LastIndexByte(host, '%'); i > 0 && strings.IndexByte(host, ':') >= 0 {
// Remove the scope, if any. ResolveUDPAddr below will use it, but here we're just
// trying to make sure with a small sanity test that this is a real IP address and
// not something that's likely to incur DNS lookups.
host = host[:i]
}
if ip := net.ParseIP(host); ip == nil {
return nil, errors.New("Failed to parse IP address: " + host)
}
@@ -79,8 +85,8 @@ func unsafeCloseBind(device *Device) error {
func (device *Device) BindSetMark(mark uint32) error {
device.net.mutex.Lock()
defer device.net.mutex.Unlock()
device.net.Lock()
defer device.net.Unlock()
// check if modified
@@ -99,23 +105,23 @@ func (device *Device) BindSetMark(mark uint32) error {
// clear cached source addresses
device.peers.mutex.RLock()
device.peers.RLock()
for _, peer := range device.peers.keyMap {
peer.mutex.Lock()
defer peer.mutex.Unlock()
peer.Lock()
defer peer.Unlock()
if peer.endpoint != nil {
peer.endpoint.ClearSrc()
}
}
device.peers.mutex.RUnlock()
device.peers.RUnlock()
return nil
}
func (device *Device) BindUpdate() error {
device.net.mutex.Lock()
defer device.net.mutex.Unlock()
device.net.Lock()
defer device.net.Unlock()
// close existing sockets
@@ -149,15 +155,15 @@ func (device *Device) BindUpdate() error {
// clear cached source addresses
device.peers.mutex.RLock()
device.peers.RLock()
for _, peer := range device.peers.keyMap {
peer.mutex.Lock()
defer peer.mutex.Unlock()
peer.Lock()
defer peer.Unlock()
if peer.endpoint != nil {
peer.endpoint.ClearSrc()
}
}
device.peers.mutex.RUnlock()
device.peers.RUnlock()
// start receiving routines
@@ -174,8 +180,8 @@ func (device *Device) BindUpdate() error {
}
func (device *Device) BindClose() error {
device.net.mutex.Lock()
device.net.Lock()
err := unsafeCloseBind(device)
device.net.mutex.Unlock()
device.net.Unlock()
return err
}

View File

@@ -1,17 +1,16 @@
// +build !linux android
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"golang.org/x/sys/unix"
"net"
"runtime"
"os"
"syscall"
)
/* This code is meant to be a temporary solution
@@ -21,14 +20,14 @@ import (
* See conn_linux.go for an implementation on the linux platform.
*/
type NativeBind struct {
type nativeBind struct {
ipv4 *net.UDPConn
ipv6 *net.UDPConn
}
type NativeEndpoint net.UDPAddr
var _ Bind = (*NativeBind)(nil)
var _ Bind = (*nativeBind)(nil)
var _ Endpoint = (*NativeEndpoint)(nil)
func CreateEndpoint(s string) (Endpoint, error) {
@@ -87,36 +86,57 @@ func listenNet(network string, port int) (*net.UDPConn, int, error) {
return conn, uaddr.Port, nil
}
func extractErrno(err error) error {
opErr, ok := err.(*net.OpError)
if !ok {
return nil
}
syscallErr, ok := opErr.Err.(*os.SyscallError)
if !ok {
return nil
}
return syscallErr.Err
}
func CreateBind(uport uint16, device *Device) (Bind, uint16, error) {
var err error
var bind NativeBind
var bind nativeBind
port := int(uport)
bind.ipv4, port, err = listenNet("udp4", port)
if err != nil {
if err != nil && extractErrno(err) != syscall.EAFNOSUPPORT {
return nil, 0, err
}
bind.ipv6, port, err = listenNet("udp6", port)
if err != nil {
if err != nil && extractErrno(err) != syscall.EAFNOSUPPORT {
bind.ipv4.Close()
bind.ipv4 = nil
return nil, 0, err
}
return &bind, uint16(port), nil
}
func (bind *NativeBind) Close() error {
err1 := bind.ipv4.Close()
err2 := bind.ipv6.Close()
func (bind *nativeBind) Close() error {
var err1, err2 error
if bind.ipv4 != nil {
err1 = bind.ipv4.Close()
}
if bind.ipv6 != nil {
err2 = bind.ipv6.Close()
}
if err1 != nil {
return err1
}
return err2
}
func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
func (bind *nativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
if bind.ipv4 == nil {
return 0, nil, syscall.EAFNOSUPPORT
}
n, endpoint, err := bind.ipv4.ReadFromUDP(buff)
if endpoint != nil {
endpoint.IP = endpoint.IP.To4()
@@ -124,64 +144,27 @@ func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
return n, (*NativeEndpoint)(endpoint), err
}
func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
func (bind *nativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
if bind.ipv6 == nil {
return 0, nil, syscall.EAFNOSUPPORT
}
n, endpoint, err := bind.ipv6.ReadFromUDP(buff)
return n, (*NativeEndpoint)(endpoint), err
}
func (bind *NativeBind) Send(buff []byte, endpoint Endpoint) error {
func (bind *nativeBind) Send(buff []byte, endpoint Endpoint) error {
var err error
nend := endpoint.(*NativeEndpoint)
if nend.IP.To4() != nil {
if bind.ipv4 == nil {
return syscall.EAFNOSUPPORT
}
_, err = bind.ipv4.WriteToUDP(buff, (*net.UDPAddr)(nend))
} else {
if bind.ipv6 == nil {
return syscall.EAFNOSUPPORT
}
_, err = bind.ipv6.WriteToUDP(buff, (*net.UDPAddr)(nend))
}
return err
}
var fwmarkIoctl int
func init() {
switch runtime.GOOS {
case "linux", "android":
fwmarkIoctl = 36 /* unix.SO_MARK */
case "freebsd":
fwmarkIoctl = 0x1015 /* unix.SO_USER_COOKIE */
case "openbsd":
fwmarkIoctl = 0x1021 /* unix.SO_RTABLE */
}
}
func (bind *NativeBind) SetMark(mark uint32) error {
if fwmarkIoctl == 0 {
return nil
}
fd4, err1 := bind.ipv4.SyscallConn()
fd6, err2 := bind.ipv6.SyscallConn()
if err1 != nil {
return err1
}
if err2 != nil {
return err2
}
err3 := fd4.Control(func(fd uintptr) {
err1 = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
})
err4 := fd6.Control(func(fd uintptr) {
err2 = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
})
if err1 != nil {
return err1
}
if err2 != nil {
return err2
}
if err3 != nil {
return err3
}
if err4 != nil {
return err4
}
return nil
}

View File

@@ -1,9 +1,8 @@
// +build !android
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*
* This implements userspace semantics of "sticky sockets", modeled after
* WireGuard's kernelspace implementation. This is more or less a straight port
@@ -15,16 +14,22 @@
* So this code is remains platform dependent.
*/
package main
package device
import (
"git.zx2c4.com/wireguard-go/rwcancel"
"errors"
"golang.org/x/sys/unix"
"net"
"strconv"
"sync"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
"golang.zx2c4.com/wireguard/rwcancel"
)
const (
FD_ERR = -1
)
type IPv4Source struct {
@@ -59,7 +64,7 @@ func (endpoint *NativeEndpoint) dst6() *unix.SockaddrInet6 {
return (*unix.SockaddrInet6)(unsafe.Pointer(&endpoint.dst[0]))
}
type NativeBind struct {
type nativeBind struct {
sock4 int
sock6 int
netlinkSock int
@@ -68,7 +73,7 @@ type NativeBind struct {
}
var _ Endpoint = (*NativeEndpoint)(nil)
var _ Bind = (*NativeBind)(nil)
var _ Bind = (*nativeBind)(nil)
func CreateEndpoint(s string) (Endpoint, error) {
var end NativeEndpoint
@@ -123,9 +128,10 @@ func createNetlinkRouteSocket() (int, error) {
}
func CreateBind(port uint16, device *Device) (*NativeBind, uint16, error) {
func CreateBind(port uint16, device *Device) (*nativeBind, uint16, error) {
var err error
var bind NativeBind
var bind nativeBind
var newPort uint16
bind.netlinkSock, err = createNetlinkRouteSocket()
if err != nil {
@@ -139,21 +145,40 @@ func CreateBind(port uint16, device *Device) (*NativeBind, uint16, error) {
go bind.routineRouteListener(device)
bind.sock6, port, err = create6(port)
// attempt ipv6 bind, update port if succesful
bind.sock6, newPort, err = create6(port)
if err != nil {
if err != syscall.EAFNOSUPPORT {
bind.netlinkCancel.Cancel()
return nil, port, err
return nil, 0, err
}
} else {
port = newPort
}
bind.sock4, port, err = create4(port)
// attempt ipv4 bind, update port if succesful
bind.sock4, newPort, err = create4(port)
if err != nil {
if err != syscall.EAFNOSUPPORT {
bind.netlinkCancel.Cancel()
unix.Close(bind.sock6)
return nil, 0, err
}
return &bind, port, err
} else {
port = newPort
}
if bind.sock4 == FD_ERR && bind.sock6 == FD_ERR {
return nil, 0, errors.New("ipv4 and ipv6 not supported")
}
return &bind, port, nil
}
func (bind *NativeBind) SetMark(value uint32) error {
func (bind *nativeBind) SetMark(value uint32) error {
if bind.sock6 != -1 {
err := unix.SetsockoptInt(
bind.sock6,
unix.SOL_SOCKET,
@@ -164,8 +189,10 @@ func (bind *NativeBind) SetMark(value uint32) error {
if err != nil {
return err
}
}
err = unix.SetsockoptInt(
if bind.sock4 != -1 {
err := unix.SetsockoptInt(
bind.sock4,
unix.SOL_SOCKET,
unix.SO_MARK,
@@ -175,6 +202,7 @@ func (bind *NativeBind) SetMark(value uint32) error {
if err != nil {
return err
}
}
bind.lastMark = value
return nil
@@ -186,10 +214,15 @@ func closeUnblock(fd int) error {
return unix.Close(fd)
}
func (bind *NativeBind) Close() error {
err1 := closeUnblock(bind.sock6)
err2 := closeUnblock(bind.sock4)
err3 := bind.netlinkCancel.Cancel()
func (bind *nativeBind) Close() error {
var err1, err2, err3 error
if bind.sock6 != -1 {
err1 = closeUnblock(bind.sock6)
}
if bind.sock4 != -1 {
err2 = closeUnblock(bind.sock4)
}
err3 = bind.netlinkCancel.Cancel()
if err1 != nil {
return err1
@@ -200,8 +233,11 @@ func (bind *NativeBind) Close() error {
return err3
}
func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
func (bind *nativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
var end NativeEndpoint
if bind.sock6 == -1 {
return 0, nil, syscall.EAFNOSUPPORT
}
n, err := receive6(
bind.sock6,
buff,
@@ -210,8 +246,11 @@ func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
return n, &end, err
}
func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
func (bind *nativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
var end NativeEndpoint
if bind.sock4 == -1 {
return 0, nil, syscall.EAFNOSUPPORT
}
n, err := receive4(
bind.sock4,
buff,
@@ -220,11 +259,17 @@ func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
return n, &end, err
}
func (bind *NativeBind) Send(buff []byte, end Endpoint) error {
func (bind *nativeBind) Send(buff []byte, end Endpoint) error {
nend := end.(*NativeEndpoint)
if !nend.isV6 {
if bind.sock4 == -1 {
return syscall.EAFNOSUPPORT
}
return send4(bind.sock4, nend, buff)
} else {
if bind.sock6 == -1 {
return syscall.EAFNOSUPPORT
}
return send6(bind.sock6, nend, buff)
}
}
@@ -312,7 +357,7 @@ func create4(port uint16) (int, uint16, error) {
)
if err != nil {
return -1, 0, err
return FD_ERR, 0, err
}
addr := unix.SockaddrInet4{
@@ -343,7 +388,12 @@ func create4(port uint16) (int, uint16, error) {
return unix.Bind(fd, &addr)
}(); err != nil {
unix.Close(fd)
return -1, 0, err
return FD_ERR, 0, err
}
sa, err := unix.Getsockname(fd)
if err == nil {
addr.Port = sa.(*unix.SockaddrInet4).Port
}
return fd, uint16(addr.Port), err
@@ -360,7 +410,7 @@ func create6(port uint16) (int, uint16, error) {
)
if err != nil {
return -1, 0, err
return FD_ERR, 0, err
}
// set sockopts and bind
@@ -402,7 +452,12 @@ func create6(port uint16) (int, uint16, error) {
}(); err != nil {
unix.Close(fd)
return -1, 0, err
return FD_ERR, 0, err
}
sa, err := unix.Getsockname(fd)
if err == nil {
addr.Port = sa.(*unix.SockaddrInet6).Port
}
return fd, uint16(addr.Port), err
@@ -548,7 +603,7 @@ func receive6(sock int, buff []byte, end *NativeEndpoint) (int, error) {
return size, nil
}
func (bind *NativeBind) routineRouteListener(device *Device) {
func (bind *nativeBind) routineRouteListener(device *Device) {
type peerEndpointPtr struct {
peer *Peer
endpoint *Endpoint
@@ -563,7 +618,7 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
var msgn int
for {
msgn, _, _, _, err = unix.Recvmsg(bind.netlinkSock, msg[:], nil, 0)
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
if err == nil || !rwcancel.RetryAfterError(err) {
break
}
if !bind.netlinkCancel.ReadyRead() {
@@ -610,17 +665,17 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
if !ok {
break
}
pePtr.peer.mutex.Lock()
pePtr.peer.Lock()
if &pePtr.peer.endpoint != pePtr.endpoint {
pePtr.peer.mutex.Unlock()
pePtr.peer.Unlock()
break
}
if uint32(pePtr.peer.endpoint.(*NativeEndpoint).src4().ifindex) == ifidx {
pePtr.peer.mutex.Unlock()
pePtr.peer.Unlock()
break
}
pePtr.peer.endpoint.(*NativeEndpoint).ClearSrc()
pePtr.peer.mutex.Unlock()
pePtr.peer.Unlock()
}
attr = attr[attrhdr.Len:]
}
@@ -631,16 +686,16 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
reqPeer = make(map[uint32]peerEndpointPtr)
reqPeerLock.Unlock()
go func() {
device.peers.mutex.RLock()
device.peers.RLock()
i := uint32(1)
for _, peer := range device.peers.keyMap {
peer.mutex.RLock()
peer.RLock()
if peer.endpoint == nil || peer.endpoint.(*NativeEndpoint) == nil {
peer.mutex.RUnlock()
peer.RUnlock()
continue
}
if peer.endpoint.(*NativeEndpoint).isV6 || peer.endpoint.(*NativeEndpoint).src4().ifindex == 0 {
peer.mutex.RUnlock()
peer.RUnlock()
break
}
nlmsg := struct {
@@ -675,7 +730,7 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
peer.endpoint.(*NativeEndpoint).src4().src,
unix.RtAttr{
Len: 8,
Type: 0x10, //unix.RTA_MARK TODO: add this to x/sys/unix
Type: unix.RTA_MARK,
},
uint32(bind.lastMark),
}
@@ -686,14 +741,14 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
endpoint: &peer.endpoint,
}
reqPeerLock.Unlock()
peer.mutex.RUnlock()
peer.RUnlock()
i++
_, err := bind.netlinkCancel.Write((*[unsafe.Sizeof(nlmsg)]byte)(unsafe.Pointer(&nlmsg))[:])
if err != nil {
break
}
}
device.peers.mutex.RUnlock()
device.peers.RUnlock()
}()
}
remain = remain[hdr.Len:]

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"time"
@@ -23,22 +22,18 @@ const (
RejectAfterTime = time.Second * 180
KeepaliveTimeout = time.Second * 10
CookieRefreshTime = time.Second * 120
HandshakeInitationRate = time.Second / 20
HandshakeInitationRate = time.Second / 50
PaddingMultiple = 16
)
/* Implementation specific constants */
const (
QueueOutboundSize = 1024
QueueInboundSize = 1024
QueueHandshakeSize = 1024
MaxSegmentSize = (1 << 16) - 1 // largest possible UDP datagram
MinMessageSize = MessageKeepaliveSize // minimum size of transport message (keepalive)
MaxMessageSize = MaxSegmentSize // maximum size of transport message
MaxContentSize = MaxSegmentSize - MessageTransportSize // maximum size of transport message content
)
/* Implementation constants */
const (
UnderLoadQueueSize = QueueHandshakeSize / 8
UnderLoadAfterTime = time.Second // how long does the device remain under load after detected

View File

@@ -1,23 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"git.zx2c4.com/wireguard-go/xchacha20poly1305"
"crypto/hmac"
"crypto/rand"
"golang.org/x/crypto/blake2s"
"golang.org/x/crypto/chacha20poly1305"
"sync"
"time"
"golang.org/x/crypto/blake2s"
"golang.org/x/crypto/chacha20poly1305"
)
type CookieChecker struct {
mutex sync.RWMutex
sync.RWMutex
mac1 struct {
key [blake2s.Size]byte
}
@@ -29,7 +28,7 @@ type CookieChecker struct {
}
type CookieGenerator struct {
mutex sync.RWMutex
sync.RWMutex
mac1 struct {
key [blake2s.Size]byte
}
@@ -43,8 +42,8 @@ type CookieGenerator struct {
}
func (st *CookieChecker) Init(pk NoisePublicKey) {
st.mutex.Lock()
defer st.mutex.Unlock()
st.Lock()
defer st.Unlock()
// mac1 state
@@ -68,8 +67,8 @@ func (st *CookieChecker) Init(pk NoisePublicKey) {
}
func (st *CookieChecker) CheckMAC1(msg []byte) bool {
st.mutex.RLock()
defer st.mutex.RUnlock()
st.RLock()
defer st.RUnlock()
size := len(msg)
smac2 := size - blake2s.Size128
@@ -85,10 +84,10 @@ func (st *CookieChecker) CheckMAC1(msg []byte) bool {
}
func (st *CookieChecker) CheckMAC2(msg []byte, src []byte) bool {
st.mutex.RLock()
defer st.mutex.RUnlock()
st.RLock()
defer st.RUnlock()
if time.Now().Sub(st.mac2.secretSet) > CookieRefreshTime {
if time.Since(st.mac2.secretSet) > CookieRefreshTime {
return false
}
@@ -121,21 +120,21 @@ func (st *CookieChecker) CreateReply(
src []byte,
) (*MessageCookieReply, error) {
st.mutex.RLock()
st.RLock()
// refresh cookie secret
if time.Now().Sub(st.mac2.secretSet) > CookieRefreshTime {
st.mutex.RUnlock()
st.mutex.Lock()
if time.Since(st.mac2.secretSet) > CookieRefreshTime {
st.RUnlock()
st.Lock()
_, err := rand.Read(st.mac2.secret[:])
if err != nil {
st.mutex.Unlock()
st.Unlock()
return nil, err
}
st.mac2.secretSet = time.Now()
st.mutex.Unlock()
st.mutex.RLock()
st.Unlock()
st.RLock()
}
// derive cookie
@@ -160,26 +159,21 @@ func (st *CookieChecker) CreateReply(
_, err := rand.Read(reply.Nonce[:])
if err != nil {
st.mutex.RUnlock()
st.RUnlock()
return nil, err
}
xchacha20poly1305.Encrypt(
reply.Cookie[:0],
&reply.Nonce,
cookie[:],
msg[smac1:smac2],
&st.mac2.encryptionKey,
)
xchapoly, _ := chacha20poly1305.NewX(st.mac2.encryptionKey[:])
xchapoly.Seal(reply.Cookie[:0], reply.Nonce[:], cookie[:], msg[smac1:smac2])
st.mutex.RUnlock()
st.RUnlock()
return reply, nil
}
func (st *CookieGenerator) Init(pk NoisePublicKey) {
st.mutex.Lock()
defer st.mutex.Unlock()
st.Lock()
defer st.Unlock()
func() {
hash, _ := blake2s.New256(nil)
@@ -199,8 +193,8 @@ func (st *CookieGenerator) Init(pk NoisePublicKey) {
}
func (st *CookieGenerator) ConsumeReply(msg *MessageCookieReply) bool {
st.mutex.Lock()
defer st.mutex.Unlock()
st.Lock()
defer st.Unlock()
if !st.mac2.hasLastMAC1 {
return false
@@ -208,13 +202,8 @@ func (st *CookieGenerator) ConsumeReply(msg *MessageCookieReply) bool {
var cookie [blake2s.Size128]byte
_, err := xchacha20poly1305.Decrypt(
cookie[:0],
&msg.Nonce,
msg.Cookie[:],
st.mac2.lastMAC1[:],
&st.mac2.encryptionKey,
)
xchapoly, _ := chacha20poly1305.NewX(st.mac2.encryptionKey[:])
_, err := xchapoly.Open(cookie[:0], msg.Nonce[:], msg.Cookie[:], st.mac2.lastMAC1[:])
if err != nil {
return false
@@ -235,8 +224,8 @@ func (st *CookieGenerator) AddMacs(msg []byte) {
mac1 := msg[smac1:smac2]
mac2 := msg[smac2:]
st.mutex.Lock()
defer st.mutex.Unlock()
st.Lock()
defer st.Unlock()
// set mac1
@@ -250,7 +239,7 @@ func (st *CookieGenerator) AddMacs(msg []byte) {
// set mac2
if time.Now().Sub(st.mac2.cookieSet) > CookieRefreshTime {
if time.Since(st.mac2.cookieSet) > CookieRefreshTime {
return
}

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"testing"

View File

@@ -1,18 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"git.zx2c4.com/wireguard-go/ratelimiter"
"git.zx2c4.com/wireguard-go/tun"
"runtime"
"sync"
"sync/atomic"
"time"
"golang.zx2c4.com/wireguard/ratelimiter"
"golang.zx2c4.com/wireguard/tun"
)
const (
@@ -30,7 +30,7 @@ type Device struct {
state struct {
starting sync.WaitGroup
stopping sync.WaitGroup
mutex sync.Mutex
sync.Mutex
changing AtomicBool
current bool
}
@@ -38,20 +38,20 @@ type Device struct {
net struct {
starting sync.WaitGroup
stopping sync.WaitGroup
mutex sync.RWMutex
sync.RWMutex
bind Bind // bind interface
port uint16 // listening port
fwmark uint32 // mark value (0 = disabled)
}
staticIdentity struct {
mutex sync.RWMutex
sync.RWMutex
privateKey NoisePrivateKey
publicKey NoisePublicKey
}
peers struct {
mutex sync.RWMutex
sync.RWMutex
keyMap map[NoisePublicKey]*Peer
}
@@ -67,7 +67,12 @@ type Device struct {
}
pool struct {
messageBuffers sync.Pool
messageBufferPool *sync.Pool
messageBufferReuseChan chan *[MaxMessageSize]byte
inboundElementPool *sync.Pool
inboundElementReuseChan chan *QueueInboundElement
outboundElementPool *sync.Pool
outboundElementReuseChan chan *QueueOutboundElement
}
queue struct {
@@ -81,7 +86,7 @@ type Device struct {
}
tun struct {
device tun.TUNDevice
device tun.Device
mtu int32
}
}
@@ -89,7 +94,7 @@ type Device struct {
/* Converts the peer into a "zombie", which remains in the peer map,
* but processes no packets and does not exists in the routing table.
*
* Must hold device.peers.mutex.
* Must hold device.peers.Mutex
*/
func unsafeRemovePeer(device *Device, peer *Peer, key NoisePublicKey) {
@@ -113,13 +118,13 @@ func deviceUpdateState(device *Device) {
// compare to current state of device
device.state.mutex.Lock()
device.state.Lock()
newIsUp := device.isUp.Get()
if newIsUp == device.state.current {
device.state.changing.Set(false)
device.state.mutex.Unlock()
device.state.Unlock()
return
}
@@ -128,29 +133,33 @@ func deviceUpdateState(device *Device) {
switch newIsUp {
case true:
if err := device.BindUpdate(); err != nil {
device.log.Error.Printf("Unable to update bind: %v\n", err)
device.isUp.Set(false)
break
}
device.peers.mutex.RLock()
device.peers.RLock()
for _, peer := range device.peers.keyMap {
peer.Start()
if peer.persistentKeepaliveInterval > 0 {
peer.SendKeepalive()
}
device.peers.mutex.RUnlock()
}
device.peers.RUnlock()
case false:
device.BindClose()
device.peers.mutex.RLock()
device.peers.RLock()
for _, peer := range device.peers.keyMap {
peer.Stop()
}
device.peers.mutex.RUnlock()
device.peers.RUnlock()
}
// update state variables
device.state.current = newIsUp
device.state.changing.Set(false)
device.state.mutex.Unlock()
device.state.Unlock()
// check for state change in the mean time
@@ -192,18 +201,22 @@ func (device *Device) IsUnderLoad() bool {
}
func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
// lock required resources
device.staticIdentity.mutex.Lock()
defer device.staticIdentity.mutex.Unlock()
device.staticIdentity.Lock()
defer device.staticIdentity.Unlock()
device.peers.mutex.Lock()
defer device.peers.mutex.Unlock()
if sk.Equals(device.staticIdentity.privateKey) {
return nil
}
device.peers.Lock()
defer device.peers.Unlock()
lockedPeers := make([]*Peer, 0, len(device.peers.keyMap))
for _, peer := range device.peers.keyMap {
peer.handshake.mutex.RLock()
defer peer.handshake.mutex.RUnlock()
lockedPeers = append(lockedPeers, peer)
}
// remove peers with matching public keys
@@ -225,8 +238,8 @@ func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
rmKey := device.staticIdentity.privateKey.IsZero()
expiredPeers := make([]*Peer, 0, len(device.peers.keyMap))
for key, peer := range device.peers.keyMap {
handshake := &peer.handshake
if rmKey {
@@ -237,21 +250,22 @@ func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
if isZero(handshake.precomputedStaticStatic[:]) {
unsafeRemovePeer(device, peer, key)
} else {
expiredPeers = append(expiredPeers, peer)
}
}
for _, peer := range lockedPeers {
peer.handshake.mutex.RUnlock()
}
for _, peer := range expiredPeers {
peer.ExpireCurrentKeypairs()
}
return nil
}
func (device *Device) GetMessageBuffer() *[MaxMessageSize]byte {
return device.pool.messageBuffers.Get().(*[MaxMessageSize]byte)
}
func (device *Device) PutMessageBuffer(msg *[MaxMessageSize]byte) {
device.pool.messageBuffers.Put(msg)
}
func NewDevice(tunDevice tun.TUNDevice, logger *Logger) *Device {
func NewDevice(tunDevice tun.Device, logger *Logger) *Device {
device := new(Device)
device.isUp.Set(false)
@@ -275,11 +289,7 @@ func NewDevice(tunDevice tun.TUNDevice, logger *Logger) *Device {
device.indexTable.Init()
device.allowedips.Reset()
device.pool.messageBuffers = sync.Pool{
New: func() interface{} {
return new([MaxMessageSize]byte)
},
}
device.PopulatePools()
// create queues
@@ -318,16 +328,15 @@ func NewDevice(tunDevice tun.TUNDevice, logger *Logger) *Device {
}
func (device *Device) LookupPeer(pk NoisePublicKey) *Peer {
device.peers.mutex.RLock()
defer device.peers.mutex.RUnlock()
device.peers.RLock()
defer device.peers.RUnlock()
return device.peers.keyMap[pk]
}
func (device *Device) RemovePeer(key NoisePublicKey) {
device.peers.mutex.Lock()
defer device.peers.mutex.Unlock()
device.peers.Lock()
defer device.peers.Unlock()
// stop peer and remove from routing
peer, ok := device.peers.keyMap[key]
@@ -337,8 +346,8 @@ func (device *Device) RemovePeer(key NoisePublicKey) {
}
func (device *Device) RemoveAllPeers() {
device.peers.mutex.Lock()
defer device.peers.mutex.Unlock()
device.peers.Lock()
defer device.peers.Unlock()
for key, peer := range device.peers.keyMap {
unsafeRemovePeer(device, peer, key)
@@ -375,8 +384,8 @@ func (device *Device) Close() {
device.log.Info.Println("Device closing")
device.state.changing.Set(true)
device.state.mutex.Lock()
defer device.state.mutex.Unlock()
device.state.Lock()
defer device.state.Unlock()
device.tun.device.Close()
device.BindClose()
@@ -385,10 +394,11 @@ func (device *Device) Close() {
close(device.signals.stop)
device.RemoveAllPeers()
device.state.stopping.Wait()
device.FlushPacketQueues()
device.RemoveAllPeers()
device.rate.limiter.Close()
device.state.changing.Set(false)
@@ -398,3 +408,20 @@ func (device *Device) Close() {
func (device *Device) Wait() chan struct{} {
return device.signals.stop
}
func (device *Device) SendKeepalivesToPeersWithCurrentKeypair() {
if device.isClosed.Get() {
return
}
device.peers.RLock()
for _, peer := range device.peers.keyMap {
peer.keypairs.RLock()
sendKeepalive := peer.keypairs.current != nil && !peer.keypairs.current.created.Add(RejectAfterTime).Before(time.Now())
peer.keypairs.RUnlock()
if sendKeepalive {
peer.SendKeepalive()
}
}
device.peers.RUnlock()
}

68
device/device_test.go Normal file
View File

@@ -0,0 +1,68 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package device
/* Create two device instances and simulate full WireGuard interaction
* without network dependencies
*/
import (
"bytes"
"testing"
)
func TestDevice(t *testing.T) {
// prepare tun devices for generating traffic
tun1 := newDummyTUN("tun1")
tun2 := newDummyTUN("tun2")
_ = tun1
_ = tun2
// prepare endpoints
end1, err := CreateDummyEndpoint()
if err != nil {
t.Error("failed to create endpoint:", err.Error())
}
end2, err := CreateDummyEndpoint()
if err != nil {
t.Error("failed to create endpoint:", err.Error())
}
_ = end1
_ = end2
// create binds
}
func randDevice(t *testing.T) *Device {
sk, err := newPrivateKey()
if err != nil {
t.Fatal(err)
}
tun := newDummyTUN("dummy")
logger := NewLogger(LogLevelError, "")
device := NewDevice(tun, logger)
device.SetPrivateKey(sk)
return device
}
func assertNil(t *testing.T, err error) {
if err != nil {
t.Fatal(err)
}
}
func assertEqual(t *testing.T, a, b []byte) {
if !bytes.Equal(a, b) {
t.Fatal(a, "!=", b)
}
}

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"math/rand"

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"crypto/rand"
@@ -19,7 +18,7 @@ type IndexTableEntry struct {
}
type IndexTable struct {
mutex sync.RWMutex
sync.RWMutex
table map[uint32]IndexTableEntry
}
@@ -30,20 +29,20 @@ func randUint32() (uint32, error) {
}
func (table *IndexTable) Init() {
table.mutex.Lock()
defer table.mutex.Unlock()
table.Lock()
defer table.Unlock()
table.table = make(map[uint32]IndexTableEntry)
}
func (table *IndexTable) Delete(index uint32) {
table.mutex.Lock()
defer table.mutex.Unlock()
table.Lock()
defer table.Unlock()
delete(table.table, index)
}
func (table *IndexTable) SwapIndexForKeypair(index uint32, keypair *Keypair) {
table.mutex.Lock()
defer table.mutex.Unlock()
table.Lock()
defer table.Unlock()
entry, ok := table.table[index]
if !ok {
return
@@ -66,19 +65,19 @@ func (table *IndexTable) NewIndexForHandshake(peer *Peer, handshake *Handshake)
// check if index used
table.mutex.RLock()
table.RLock()
_, ok := table.table[index]
table.mutex.RUnlock()
table.RUnlock()
if ok {
continue
}
// check again while locked
table.mutex.Lock()
table.Lock()
_, found := table.table[index]
if found {
table.mutex.Unlock()
table.Unlock()
continue
}
table.table[index] = IndexTableEntry{
@@ -86,13 +85,13 @@ func (table *IndexTable) NewIndexForHandshake(peer *Peer, handshake *Handshake)
handshake: handshake,
keypair: nil,
}
table.mutex.Unlock()
table.Unlock()
return index, nil
}
}
func (table *IndexTable) Lookup(id uint32) IndexTableEntry {
table.mutex.RLock()
defer table.mutex.RUnlock()
table.RLock()
defer table.RUnlock()
return table.table[id]
}

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"net"

View File

@@ -1,15 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"encoding/hex"
"golang.org/x/crypto/blake2s"
"testing"
"golang.org/x/crypto/blake2s"
)
type KDFTest struct {

View File

@@ -1,16 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"git.zx2c4.com/wireguard-go/replay"
"crypto/cipher"
"sync"
"time"
"golang.zx2c4.com/wireguard/replay"
)
/* Due to limitations in Go and /x/crypto there is currently
@@ -32,15 +32,15 @@ type Keypair struct {
}
type Keypairs struct {
mutex sync.RWMutex
sync.RWMutex
current *Keypair
previous *Keypair
next *Keypair
}
func (kp *Keypairs) Current() *Keypair {
kp.mutex.RLock()
defer kp.mutex.RUnlock()
kp.RLock()
defer kp.RUnlock()
return kp.current
}

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"io"

12
device/mark_default.go Normal file
View File

@@ -0,0 +1,12 @@
// +build !linux,!openbsd,!freebsd
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package device
func (bind *nativeBind) SetMark(mark uint32) error {
return nil
}

65
device/mark_unix.go Normal file
View File

@@ -0,0 +1,65 @@
// +build android openbsd freebsd
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package device
import (
"runtime"
"golang.org/x/sys/unix"
)
var fwmarkIoctl int
func init() {
switch runtime.GOOS {
case "linux", "android":
fwmarkIoctl = 36 /* unix.SO_MARK */
case "freebsd":
fwmarkIoctl = 0x1015 /* unix.SO_USER_COOKIE */
case "openbsd":
fwmarkIoctl = 0x1021 /* unix.SO_RTABLE */
}
}
func (bind *nativeBind) SetMark(mark uint32) error {
var operr error
if fwmarkIoctl == 0 {
return nil
}
if bind.ipv4 != nil {
fd, err := bind.ipv4.SyscallConn()
if err != nil {
return err
}
err = fd.Control(func(fd uintptr) {
operr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
})
if err == nil {
err = operr
}
if err != nil {
return err
}
}
if bind.ipv6 != nil {
fd, err := bind.ipv6.SyscallConn()
if err != nil {
return err
}
err = fd.Control(func(fd uintptr) {
operr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
})
if err == nil {
err = operr
}
if err != nil {
return err
}
}
return nil
}

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"sync/atomic"
@@ -18,11 +17,11 @@ const (
)
type AtomicBool struct {
flag int32
int32
}
func (a *AtomicBool) Get() bool {
return atomic.LoadInt32(&a.flag) == AtomicTrue
return atomic.LoadInt32(&a.int32) == AtomicTrue
}
func (a *AtomicBool) Swap(val bool) bool {
@@ -30,7 +29,7 @@ func (a *AtomicBool) Swap(val bool) bool {
if val {
flag = AtomicTrue
}
return atomic.SwapInt32(&a.flag, flag) == AtomicTrue
return atomic.SwapInt32(&a.int32, flag) == AtomicTrue
}
func (a *AtomicBool) Set(val bool) {
@@ -38,7 +37,7 @@ func (a *AtomicBool) Set(val bool) {
if val {
flag = AtomicTrue
}
atomic.StoreInt32(&a.flag, flag)
atomic.StoreInt32(&a.int32, flag)
}
func min(a, b uint) uint {

View File

@@ -1,18 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"crypto/hmac"
"crypto/rand"
"crypto/subtle"
"hash"
"golang.org/x/crypto/blake2s"
"golang.org/x/crypto/curve25519"
"hash"
)
/* KDF related functions.
@@ -42,7 +42,6 @@ func HMAC2(sum *[blake2s.Size]byte, key, in0, in1 []byte) {
func KDF1(t0 *[blake2s.Size]byte, key, input []byte) {
HMAC1(t0, key, input)
HMAC1(t0, t0[:], []byte{0x1})
return
}
func KDF2(t0, t1 *[blake2s.Size]byte, key, input []byte) {
@@ -51,7 +50,6 @@ func KDF2(t0, t1 *[blake2s.Size]byte, key, input []byte) {
HMAC1(t0, prk[:], []byte{0x1})
HMAC2(t1, prk[:], t0[:], []byte{0x2})
setZero(prk[:])
return
}
func KDF3(t0, t1, t2 *[blake2s.Size]byte, key, input []byte) {
@@ -61,7 +59,6 @@ func KDF3(t0, t1, t2 *[blake2s.Size]byte, key, input []byte) {
HMAC2(t1, prk[:], t0[:], []byte{0x2})
HMAC2(t2, prk[:], t1[:], []byte{0x3})
setZero(prk[:])
return
}
func isZero(val []byte) bool {
@@ -79,12 +76,14 @@ func setZero(arr []byte) {
}
}
func newPrivateKey() (sk NoisePrivateKey, err error) {
// clamping: https://cr.yp.to/ecdh.html
_, err = rand.Read(sk[:])
func (sk *NoisePrivateKey) clamp() {
sk[0] &= 248
sk[31] &= 127
sk[31] |= 64
sk[31] = (sk[31] & 127) | 64
}
func newPrivateKey() (sk NoisePrivateKey, err error) {
_, err = rand.Read(sk[:])
sk.clamp()
return
}

View File

@@ -1,19 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2015-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"git.zx2c4.com/wireguard-go/tai64n"
"errors"
"sync"
"time"
"golang.org/x/crypto/blake2s"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/poly1305"
"sync"
"time"
"golang.zx2c4.com/wireguard/tai64n"
)
const (
@@ -90,7 +90,7 @@ type MessageTransport struct {
type MessageCookieReply struct {
Type uint32
Receiver uint32
Nonce [24]byte
Nonce [chacha20poly1305.NonceSizeX]byte
Cookie [blake2s.Size128 + poly1305.TagSize]byte
}
@@ -155,8 +155,8 @@ func init() {
func (device *Device) CreateMessageInitiation(peer *Peer) (*MessageInitiation, error) {
device.staticIdentity.mutex.RLock()
defer device.staticIdentity.mutex.RUnlock()
device.staticIdentity.RLock()
defer device.staticIdentity.RUnlock()
handshake := &peer.handshake
handshake.mutex.Lock()
@@ -242,8 +242,8 @@ func (device *Device) ConsumeMessageInitiation(msg *MessageInitiation) *Peer {
return nil
}
device.staticIdentity.mutex.RLock()
defer device.staticIdentity.mutex.RUnlock()
device.staticIdentity.RLock()
defer device.staticIdentity.RUnlock()
mixHash(&hash, &InitialHash, device.staticIdentity.publicKey[:])
mixHash(&hash, &hash, msg.Ephemeral[:])
@@ -301,7 +301,7 @@ func (device *Device) ConsumeMessageInitiation(msg *MessageInitiation) *Peer {
var ok bool
ok = timestamp.After(handshake.lastTimestamp)
ok = ok && time.Now().Sub(handshake.lastInitiationConsumption) > HandshakeInitationRate
ok = ok && time.Since(handshake.lastInitiationConsumption) > HandshakeInitationRate
handshake.mutex.RUnlock()
if !ok {
return nil
@@ -424,8 +424,8 @@ func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
// lock private key for reading
device.staticIdentity.mutex.RLock()
defer device.staticIdentity.mutex.RUnlock()
device.staticIdentity.RLock()
defer device.staticIdentity.RUnlock()
// finish 3-way DH
@@ -555,8 +555,8 @@ func (peer *Peer) BeginSymmetricSession() error {
// rotate key pairs
keypairs := &peer.keypairs
keypairs.mutex.Lock()
defer keypairs.mutex.Unlock()
keypairs.Lock()
defer keypairs.Unlock()
previous := keypairs.previous
next := keypairs.next
@@ -587,8 +587,8 @@ func (peer *Peer) ReceivedWithKeypair(receivedKeypair *Keypair) bool {
if keypairs.next != receivedKeypair {
return false
}
keypairs.mutex.Lock()
defer keypairs.mutex.Unlock()
keypairs.Lock()
defer keypairs.Unlock()
if keypairs.next != receivedKeypair {
return false
}

View File

@@ -1,15 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"crypto/subtle"
"encoding/hex"
"errors"
"golang.org/x/crypto/chacha20poly1305"
)
@@ -46,8 +46,10 @@ func (key NoisePrivateKey) Equals(tar NoisePrivateKey) bool {
return subtle.ConstantTimeCompare(key[:], tar[:]) == 1
}
func (key *NoisePrivateKey) FromHex(src string) error {
return loadExactHex(key[:], src)
func (key *NoisePrivateKey) FromHex(src string) (err error) {
err = loadExactHex(key[:], src)
key.clamp()
return
}
func (key NoisePrivateKey) ToHex() string {

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"bytes"

View File

@@ -1,16 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"encoding/base64"
"errors"
"fmt"
"sync"
"sync/atomic"
"time"
)
@@ -20,7 +20,7 @@ const (
type Peer struct {
isRunning AtomicBool
mutex sync.RWMutex // Mostly protects endpoint, but is generally taken whenever we modify peer
sync.RWMutex // Mostly protects endpoint, but is generally taken whenever we modify peer
keypairs Keypairs
handshake Handshake
device *Device
@@ -58,7 +58,7 @@ type Peer struct {
}
routines struct {
mutex sync.Mutex // held when stopping / starting routines
sync.Mutex // held when stopping / starting routines
starting sync.WaitGroup // routines pending start
stopping sync.WaitGroup // routines pending stop
stop chan struct{} // size 0, stop all go routines in peer
@@ -68,18 +68,17 @@ type Peer struct {
}
func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
if device.isClosed.Get() {
return nil, errors.New("device closed")
}
// lock resources
device.staticIdentity.mutex.RLock()
defer device.staticIdentity.mutex.RUnlock()
device.staticIdentity.RLock()
defer device.staticIdentity.RUnlock()
device.peers.mutex.Lock()
defer device.peers.mutex.Unlock()
device.peers.Lock()
defer device.peers.Unlock()
// check if over limit
@@ -90,8 +89,8 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
// create peer
peer := new(Peer)
peer.mutex.Lock()
defer peer.mutex.Unlock()
peer.Lock()
defer peer.Unlock()
peer.cookieGenerator.Init(pk)
peer.device = device
@@ -103,20 +102,28 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
if ok {
return nil, errors.New("adding existing peer")
}
device.peers.keyMap[pk] = peer
// pre-compute DH
handshake := &peer.handshake
handshake.mutex.Lock()
handshake.remoteStatic = pk
handshake.precomputedStaticStatic = device.staticIdentity.privateKey.sharedSecret(pk)
ssIsZero := isZero(handshake.precomputedStaticStatic[:])
handshake.remoteStatic = pk
handshake.mutex.Unlock()
// reset endpoint
peer.endpoint = nil
// conditionally add
if !ssIsZero {
device.peers.keyMap[pk] = peer
} else {
return nil, nil
}
// start peer
if peer.device.isUp.Get() {
@@ -127,21 +134,25 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
}
func (peer *Peer) SendBuffer(buffer []byte) error {
peer.device.net.mutex.RLock()
defer peer.device.net.mutex.RUnlock()
peer.device.net.RLock()
defer peer.device.net.RUnlock()
if peer.device.net.bind == nil {
return errors.New("no bind")
}
peer.mutex.RLock()
defer peer.mutex.RUnlock()
peer.RLock()
defer peer.RUnlock()
if peer.endpoint == nil {
return errors.New("no known endpoint for peer")
}
return peer.device.net.bind.Send(buffer, peer.endpoint)
err := peer.device.net.bind.Send(buffer, peer.endpoint)
if err == nil {
atomic.AddUint64(&peer.stats.txBytes, uint64(len(buffer)))
}
return err
}
func (peer *Peer) String() string {
@@ -163,8 +174,8 @@ func (peer *Peer) Start() {
// prevent simultaneous start/stop operations
peer.routines.mutex.Lock()
defer peer.routines.mutex.Unlock()
peer.routines.Lock()
defer peer.routines.Unlock()
if peer.isRunning.Get() {
return
@@ -208,14 +219,14 @@ func (peer *Peer) ZeroAndFlushAll() {
// clear key pairs
keypairs := &peer.keypairs
keypairs.mutex.Lock()
keypairs.Lock()
device.DeleteKeypair(keypairs.previous)
device.DeleteKeypair(keypairs.current)
device.DeleteKeypair(keypairs.next)
keypairs.previous = nil
keypairs.current = nil
keypairs.next = nil
keypairs.mutex.Unlock()
keypairs.Unlock()
// clear handshake state
@@ -228,6 +239,25 @@ func (peer *Peer) ZeroAndFlushAll() {
peer.FlushNonceQueue()
}
func (peer *Peer) ExpireCurrentKeypairs() {
handshake := &peer.handshake
handshake.mutex.Lock()
peer.device.indexTable.Delete(handshake.localIndex)
handshake.Clear()
handshake.mutex.Unlock()
peer.handshake.lastSentHandshake = time.Now().Add(-(RekeyTimeout + time.Second))
keypairs := &peer.keypairs
keypairs.Lock()
if keypairs.current != nil {
keypairs.current.sendNonce = RejectAfterMessages
}
if keypairs.next != nil {
keypairs.next.sendNonce = RejectAfterMessages
}
keypairs.Unlock()
}
func (peer *Peer) Stop() {
// prevent simultaneous start/stop operations
@@ -238,8 +268,8 @@ func (peer *Peer) Stop() {
peer.routines.starting.Wait()
peer.routines.mutex.Lock()
defer peer.routines.mutex.Unlock()
peer.routines.Lock()
defer peer.routines.Unlock()
peer.device.log.Debug.Println(peer, "- Stopping...")
@@ -258,3 +288,14 @@ func (peer *Peer) Stop() {
peer.ZeroAndFlushAll()
}
var RoamingDisabled bool
func (peer *Peer) SetEndpointFromPacket(endpoint Endpoint) {
if RoamingDisabled {
return
}
peer.Lock()
peer.endpoint = endpoint
peer.Unlock()
}

89
device/pools.go Normal file
View File

@@ -0,0 +1,89 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package device
import "sync"
func (device *Device) PopulatePools() {
if PreallocatedBuffersPerPool == 0 {
device.pool.messageBufferPool = &sync.Pool{
New: func() interface{} {
return new([MaxMessageSize]byte)
},
}
device.pool.inboundElementPool = &sync.Pool{
New: func() interface{} {
return new(QueueInboundElement)
},
}
device.pool.outboundElementPool = &sync.Pool{
New: func() interface{} {
return new(QueueOutboundElement)
},
}
} else {
device.pool.messageBufferReuseChan = make(chan *[MaxMessageSize]byte, PreallocatedBuffersPerPool)
for i := 0; i < PreallocatedBuffersPerPool; i += 1 {
device.pool.messageBufferReuseChan <- new([MaxMessageSize]byte)
}
device.pool.inboundElementReuseChan = make(chan *QueueInboundElement, PreallocatedBuffersPerPool)
for i := 0; i < PreallocatedBuffersPerPool; i += 1 {
device.pool.inboundElementReuseChan <- new(QueueInboundElement)
}
device.pool.outboundElementReuseChan = make(chan *QueueOutboundElement, PreallocatedBuffersPerPool)
for i := 0; i < PreallocatedBuffersPerPool; i += 1 {
device.pool.outboundElementReuseChan <- new(QueueOutboundElement)
}
}
}
func (device *Device) GetMessageBuffer() *[MaxMessageSize]byte {
if PreallocatedBuffersPerPool == 0 {
return device.pool.messageBufferPool.Get().(*[MaxMessageSize]byte)
} else {
return <-device.pool.messageBufferReuseChan
}
}
func (device *Device) PutMessageBuffer(msg *[MaxMessageSize]byte) {
if PreallocatedBuffersPerPool == 0 {
device.pool.messageBufferPool.Put(msg)
} else {
device.pool.messageBufferReuseChan <- msg
}
}
func (device *Device) GetInboundElement() *QueueInboundElement {
if PreallocatedBuffersPerPool == 0 {
return device.pool.inboundElementPool.Get().(*QueueInboundElement)
} else {
return <-device.pool.inboundElementReuseChan
}
}
func (device *Device) PutInboundElement(msg *QueueInboundElement) {
if PreallocatedBuffersPerPool == 0 {
device.pool.inboundElementPool.Put(msg)
} else {
device.pool.inboundElementReuseChan <- msg
}
}
func (device *Device) GetOutboundElement() *QueueOutboundElement {
if PreallocatedBuffersPerPool == 0 {
return device.pool.outboundElementPool.Get().(*QueueOutboundElement)
} else {
return <-device.pool.outboundElementReuseChan
}
}
func (device *Device) PutOutboundElement(msg *QueueOutboundElement) {
if PreallocatedBuffersPerPool == 0 {
device.pool.outboundElementPool.Put(msg)
} else {
device.pool.outboundElementReuseChan <- msg
}
}

View File

@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package device
/* Reduce memory consumption for Android */
const (
QueueOutboundSize = 1024
QueueInboundSize = 1024
QueueHandshakeSize = 1024
MaxSegmentSize = 2200
PreallocatedBuffersPerPool = 4096
)

View File

@@ -0,0 +1,16 @@
// +build !android,!ios
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package device
const (
QueueOutboundSize = 1024
QueueInboundSize = 1024
QueueHandshakeSize = 1024
MaxSegmentSize = (1 << 16) - 1 // largest possible UDP datagram
PreallocatedBuffersPerPool = 0 // Disable and allow for infinite memory growth
)

View File

@@ -0,0 +1,18 @@
// +build ios
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package device
/* Fit within memory limits for iOS's Network Extension API, which has stricter requirements */
const (
QueueOutboundSize = 1024
QueueInboundSize = 1024
QueueHandshakeSize = 1024
MaxSegmentSize = 1700
PreallocatedBuffersPerPool = 1024
)

View File

@@ -1,22 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"bytes"
"encoding/binary"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"net"
"strconv"
"sync"
"sync/atomic"
"time"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
type QueueHandshakeElement struct {
@@ -28,7 +28,7 @@ type QueueHandshakeElement struct {
type QueueInboundElement struct {
dropped int32
mutex sync.Mutex
sync.Mutex
buffer *[MaxMessageSize]byte
packet []byte
counter uint64
@@ -44,59 +44,29 @@ func (elem *QueueInboundElement) IsDropped() bool {
return atomic.LoadInt32(&elem.dropped) == AtomicTrue
}
func (device *Device) addToInboundQueue(
queue chan *QueueInboundElement,
element *QueueInboundElement,
) {
for {
func (device *Device) addToInboundAndDecryptionQueues(inboundQueue chan *QueueInboundElement, decryptionQueue chan *QueueInboundElement, element *QueueInboundElement) bool {
select {
case queue <- element:
return
default:
case inboundQueue <- element:
select {
case old := <-queue:
old.Drop()
case decryptionQueue <- element:
return true
default:
element.Drop()
element.Unlock()
return false
}
}
default:
device.PutInboundElement(element)
return false
}
}
func (device *Device) addToDecryptionQueue(
queue chan *QueueInboundElement,
element *QueueInboundElement,
) {
for {
func (device *Device) addToHandshakeQueue(queue chan QueueHandshakeElement, element QueueHandshakeElement) bool {
select {
case queue <- element:
return
return true
default:
select {
case old := <-queue:
// drop & release to potential consumer
old.Drop()
old.mutex.Unlock()
default:
}
}
}
}
func (device *Device) addToHandshakeQueue(
queue chan QueueHandshakeElement,
element QueueHandshakeElement,
) {
for {
select {
case queue <- element:
return
default:
select {
case elem := <-queue:
device.PutMessageBuffer(elem.buffer)
default:
}
}
return false
}
}
@@ -109,7 +79,7 @@ func (peer *Peer) keepKeyFreshReceiving() {
return
}
keypair := peer.keypairs.Current()
if keypair != nil && keypair.isInitiator && time.Now().Sub(keypair.created) > (RejectAfterTime-KeepaliveTimeout-RekeyTimeout) {
if keypair != nil && keypair.isInitiator && time.Since(keypair.created) > (RejectAfterTime-KeepaliveTimeout-RekeyTimeout) {
peer.timers.sentLastMinuteHandshake.Set(true)
peer.SendHandshakeInitiation(false)
}
@@ -128,7 +98,7 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
device.net.stopping.Done()
}()
logDebug.Println("Routine: receive incoming IPv" + strconv.Itoa(IP) + " - starting")
logDebug.Println("Routine: receive incoming IPv" + strconv.Itoa(IP) + " - started")
device.net.starting.Done()
// receive datagrams until conn is closed
@@ -155,6 +125,7 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
}
if err != nil {
device.PutMessageBuffer(buffer)
return
}
@@ -177,7 +148,7 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
// check size
if len(packet) < MessageTransportType {
if len(packet) < MessageTransportSize {
continue
}
@@ -199,24 +170,24 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
}
// create work element
peer := value.peer
elem := &QueueInboundElement{
packet: packet,
buffer: buffer,
keypair: keypair,
dropped: AtomicFalse,
endpoint: endpoint,
}
elem.mutex.Lock()
elem := device.GetInboundElement()
elem.packet = packet
elem.buffer = buffer
elem.keypair = keypair
elem.dropped = AtomicFalse
elem.endpoint = endpoint
elem.counter = 0
elem.Mutex = sync.Mutex{}
elem.Lock()
// add to decryption queues
if peer.isRunning.Get() {
device.addToDecryptionQueue(device.queue.decryption, elem)
device.addToInboundQueue(peer.queue.inbound, elem)
if device.addToInboundAndDecryptionQueues(peer.queue.inbound, device.queue.decryption, elem) {
buffer = device.GetMessageBuffer()
}
}
continue
@@ -236,7 +207,7 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
}
if okay {
device.addToHandshakeQueue(
if (device.addToHandshakeQueue(
device.queue.handshake,
QueueHandshakeElement{
msgType: msgType,
@@ -244,10 +215,11 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
packet: packet,
endpoint: endpoint,
},
)
)) {
buffer = device.GetMessageBuffer()
}
}
}
}
func (device *Device) RoutineDecryption() {
@@ -308,8 +280,9 @@ func (device *Device) RoutineDecryption() {
)
if err != nil {
elem.Drop()
device.PutMessageBuffer(elem.buffer)
}
elem.mutex.Unlock()
elem.Unlock()
}
}
}
@@ -322,18 +295,26 @@ func (device *Device) RoutineHandshake() {
logError := device.log.Error
logDebug := device.log.Debug
var elem QueueHandshakeElement
var ok bool
defer func() {
logDebug.Println("Routine: handshake worker - stopped")
device.state.stopping.Done()
if elem.buffer != nil {
device.PutMessageBuffer(elem.buffer)
}
}()
logDebug.Println("Routine: handshake worker - started")
device.state.starting.Done()
var elem QueueHandshakeElement
var ok bool
for {
if elem.buffer != nil {
device.PutMessageBuffer(elem.buffer)
elem.buffer = nil
}
select {
case elem, ok = <-device.queue.handshake:
case <-device.signals.stop:
@@ -371,7 +352,10 @@ func (device *Device) RoutineHandshake() {
// consume reply
if peer := entry.peer; peer.isRunning.Get() {
peer.cookieGenerator.ConsumeReply(&reply)
logDebug.Println("Receiving cookie response from ", elem.endpoint.DstToString())
if !peer.cookieGenerator.ConsumeReply(&reply) {
logDebug.Println("Could not decrypt invalid cookie response")
}
}
continue
@@ -440,12 +424,10 @@ func (device *Device) RoutineHandshake() {
peer.timersAnyAuthenticatedPacketReceived()
// update endpoint
peer.mutex.Lock()
peer.endpoint = elem.endpoint
peer.mutex.Unlock()
peer.SetEndpointFromPacket(elem.endpoint)
logDebug.Println(peer, "- Received handshake initiation")
atomic.AddUint64(&peer.stats.rxBytes, uint64(len(elem.packet)))
peer.SendHandshakeResponse()
@@ -473,12 +455,10 @@ func (device *Device) RoutineHandshake() {
}
// update endpoint
peer.mutex.Lock()
peer.endpoint = elem.endpoint
peer.mutex.Unlock()
peer.SetEndpointFromPacket(elem.endpoint)
logDebug.Println(peer, "- Received handshake response")
atomic.AddUint64(&peer.stats.rxBytes, uint64(len(elem.packet)))
// update timers
@@ -512,9 +492,17 @@ func (peer *Peer) RoutineSequentialReceiver() {
logError := device.log.Error
logDebug := device.log.Debug
var elem *QueueInboundElement
defer func() {
logDebug.Println(peer, "- Routine: sequential receiver - stopped")
peer.routines.stopping.Done()
if elem != nil {
if !elem.IsDropped() {
device.PutMessageBuffer(elem.buffer)
}
device.PutInboundElement(elem)
}
}()
logDebug.Println(peer, "- Routine: sequential receiver - started")
@@ -522,21 +510,27 @@ func (peer *Peer) RoutineSequentialReceiver() {
peer.routines.starting.Done()
for {
if elem != nil {
if !elem.IsDropped() {
device.PutMessageBuffer(elem.buffer)
}
device.PutInboundElement(elem)
elem = nil
}
var elemOk bool
select {
case <-peer.routines.stop:
return
case elem, ok := <-peer.queue.inbound:
if !ok {
case elem, elemOk = <-peer.queue.inbound:
if !elemOk {
return
}
}
// wait for decryption
elem.mutex.Lock()
elem.Lock()
if elem.IsDropped() {
continue
@@ -549,10 +543,7 @@ func (peer *Peer) RoutineSequentialReceiver() {
}
// update endpoint
peer.mutex.Lock()
peer.endpoint = elem.endpoint
peer.mutex.Unlock()
peer.SetEndpointFromPacket(elem.endpoint)
// check if using new keypair
if peer.ReceivedWithKeypair(elem.keypair) {
@@ -566,6 +557,7 @@ func (peer *Peer) RoutineSequentialReceiver() {
peer.keepKeyFreshReceiving()
peer.timersAnyAuthenticatedPacketTraversal()
peer.timersAnyAuthenticatedPacketReceived()
atomic.AddUint64(&peer.stats.rxBytes, uint64(len(elem.packet)+MinMessageSize))
// check for keepalive
@@ -627,8 +619,8 @@ func (peer *Peer) RoutineSequentialReceiver() {
src := elem.packet[IPv6offsetSrc : IPv6offsetSrc+net.IPv6len]
if device.allowedips.LookupIPv6(src) != peer {
logInfo.Println(
"IPv6 packet with disallowed source address from",
peer,
"sent packet with disallowed IPv6 source",
)
continue
}
@@ -641,14 +633,15 @@ func (peer *Peer) RoutineSequentialReceiver() {
// write to tun device
offset := MessageTransportOffsetContent
atomic.AddUint64(&peer.stats.rxBytes, uint64(len(elem.packet)))
_, err := device.tun.device.Write(
elem.buffer[:offset+len(elem.packet)],
offset)
device.PutMessageBuffer(elem.buffer)
_, err := device.tun.device.Write(elem.buffer[:offset+len(elem.packet)], offset)
if len(peer.queue.inbound) == 0 {
err = device.tun.device.Flush()
if err != nil {
peer.device.log.Error.Printf("Unable to flush packets: %v", err)
}
}
if err != nil && !device.isClosed.Get() {
logError.Println("Failed to write packet to TUN device:", err)
}
}
}
}

View File

@@ -1,21 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"bytes"
"encoding/binary"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"net"
"sync"
"sync/atomic"
"time"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
/* Outbound flow
@@ -44,7 +44,7 @@ import (
type QueueOutboundElement struct {
dropped int32
mutex sync.Mutex
sync.Mutex
buffer *[MaxMessageSize]byte // slice holding the packet data
packet []byte // slice of "buffer" (always!)
nonce uint64 // nonce for encryption
@@ -53,10 +53,14 @@ type QueueOutboundElement struct {
}
func (device *Device) NewOutboundElement() *QueueOutboundElement {
return &QueueOutboundElement{
dropped: AtomicFalse,
buffer: device.pool.messageBuffers.Get().(*[MaxMessageSize]byte),
}
elem := device.GetOutboundElement()
elem.dropped = AtomicFalse
elem.buffer = device.GetMessageBuffer()
elem.Mutex = sync.Mutex{}
elem.nonce = 0
elem.keypair = nil
elem.peer = nil
return elem
}
func (elem *QueueOutboundElement) Drop() {
@@ -67,10 +71,7 @@ func (elem *QueueOutboundElement) IsDropped() bool {
return atomic.LoadInt32(&elem.dropped) == AtomicTrue
}
func addToOutboundQueue(
queue chan *QueueOutboundElement,
element *QueueOutboundElement,
) {
func addToNonceQueue(queue chan *QueueOutboundElement, element *QueueOutboundElement, device *Device) {
for {
select {
case queue <- element:
@@ -78,30 +79,28 @@ func addToOutboundQueue(
default:
select {
case old := <-queue:
old.Drop()
device.PutMessageBuffer(old.buffer)
device.PutOutboundElement(old)
default:
}
}
}
}
func addToEncryptionQueue(
queue chan *QueueOutboundElement,
element *QueueOutboundElement,
) {
for {
func addToOutboundAndEncryptionQueues(outboundQueue chan *QueueOutboundElement, encryptionQueue chan *QueueOutboundElement, element *QueueOutboundElement) {
select {
case queue <- element:
case outboundQueue <- element:
select {
case encryptionQueue <- element:
return
default:
select {
case old := <-queue:
// drop & release to potential consumer
old.Drop()
old.mutex.Unlock()
element.Drop()
element.peer.device.PutMessageBuffer(element.buffer)
element.Unlock()
}
default:
}
}
element.peer.device.PutMessageBuffer(element.buffer)
element.peer.device.PutOutboundElement(element)
}
}
@@ -118,6 +117,8 @@ func (peer *Peer) SendKeepalive() bool {
peer.device.log.Debug.Println(peer, "- Sending keepalive packet")
return true
default:
peer.device.PutMessageBuffer(elem.buffer)
peer.device.PutOutboundElement(elem)
return false
}
}
@@ -128,14 +129,14 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
}
peer.handshake.mutex.RLock()
if time.Now().Sub(peer.handshake.lastSentHandshake) < RekeyTimeout {
if time.Since(peer.handshake.lastSentHandshake) < RekeyTimeout {
peer.handshake.mutex.RUnlock()
return nil
}
peer.handshake.mutex.RUnlock()
peer.handshake.mutex.Lock()
if time.Now().Sub(peer.handshake.lastSentHandshake) < RekeyTimeout {
if time.Since(peer.handshake.lastSentHandshake) < RekeyTimeout {
peer.handshake.mutex.Unlock()
return nil
}
@@ -206,7 +207,7 @@ func (peer *Peer) SendHandshakeResponse() error {
func (device *Device) SendHandshakeCookie(initiatingElem *QueueHandshakeElement) error {
device.log.Debug.Println("Sending cookie reply to:", initiatingElem.endpoint.DstToString())
device.log.Debug.Println("Sending cookie response for denied handshake message for", initiatingElem.endpoint.DstToString())
sender := binary.LittleEndian.Uint32(initiatingElem.packet[4:8])
reply, err := device.cookieChecker.CreateReply(initiatingElem.packet, sender, initiatingElem.endpoint.DstToBytes())
@@ -231,7 +232,7 @@ func (peer *Peer) keepKeyFreshSending() {
return
}
nonce := atomic.LoadUint64(&keypair.sendNonce)
if nonce > RekeyAfterMessages || (keypair.isInitiator && time.Now().Sub(keypair.created) > RekeyAfterTime) {
if nonce > RekeyAfterMessages || (keypair.isInitiator && time.Since(keypair.created) > RekeyAfterTime) {
peer.SendHandshakeInitiation(false)
}
}
@@ -243,8 +244,6 @@ func (peer *Peer) keepKeyFreshSending() {
*/
func (device *Device) RoutineReadFromTUN() {
elem := device.NewOutboundElement()
logDebug := device.log.Debug
logError := device.log.Error
@@ -256,7 +255,14 @@ func (device *Device) RoutineReadFromTUN() {
logDebug.Println("Routine: TUN reader - started")
device.state.starting.Done()
var elem *QueueOutboundElement
for {
if elem != nil {
device.PutMessageBuffer(elem.buffer)
device.PutOutboundElement(elem)
}
elem = device.NewOutboundElement()
// read packet
@@ -268,6 +274,8 @@ func (device *Device) RoutineReadFromTUN() {
logError.Println("Failed to read packet from TUN device:", err)
device.Close()
}
device.PutMessageBuffer(elem.buffer)
device.PutOutboundElement(elem)
return
}
@@ -309,8 +317,8 @@ func (device *Device) RoutineReadFromTUN() {
if peer.queue.packetInNonceQueueIsAwaitingKey.Get() {
peer.SendHandshakeInitiation(false)
}
addToOutboundQueue(peer.queue.nonce, elem)
elem = device.NewOutboundElement()
addToNonceQueue(peer.queue.nonce, elem, device)
elem = nil
}
}
}
@@ -334,22 +342,25 @@ func (peer *Peer) RoutineNonce() {
device := peer.device
logDebug := device.log.Debug
defer func() {
logDebug.Println(peer, "- Routine: nonce worker - stopped")
peer.queue.packetInNonceQueueIsAwaitingKey.Set(false)
peer.routines.stopping.Done()
}()
flush := func() {
for {
select {
case <-peer.queue.nonce:
case elem := <-peer.queue.nonce:
device.PutMessageBuffer(elem.buffer)
device.PutOutboundElement(elem)
default:
return
}
}
}
defer func() {
flush()
logDebug.Println(peer, "- Routine: nonce worker - stopped")
peer.queue.packetInNonceQueueIsAwaitingKey.Set(false)
peer.routines.stopping.Done()
}()
peer.routines.starting.Done()
logDebug.Println(peer, "- Routine: nonce worker - started")
@@ -379,7 +390,7 @@ func (peer *Peer) RoutineNonce() {
keypair = peer.keypairs.Current()
if keypair != nil && keypair.sendNonce < RejectAfterMessages {
if time.Now().Sub(keypair.created) < RejectAfterTime {
if time.Since(keypair.created) < RejectAfterTime {
break
}
}
@@ -403,10 +414,14 @@ func (peer *Peer) RoutineNonce() {
logDebug.Println(peer, "- Obtained awaited keypair")
case <-peer.signals.flushNonceQueue:
device.PutMessageBuffer(elem.buffer)
device.PutOutboundElement(elem)
flush()
goto NextPacket
case <-peer.routines.stop:
device.PutMessageBuffer(elem.buffer)
device.PutOutboundElement(elem)
return
}
}
@@ -421,17 +436,17 @@ func (peer *Peer) RoutineNonce() {
if elem.nonce >= RejectAfterMessages {
atomic.StoreUint64(&keypair.sendNonce, RejectAfterMessages)
device.PutMessageBuffer(elem.buffer)
device.PutOutboundElement(elem)
goto NextPacket
}
elem.keypair = keypair
elem.dropped = AtomicFalse
elem.mutex.Lock()
elem.Lock()
// add to parallel and sequential queue
addToEncryptionQueue(device.queue.encryption, elem)
addToOutboundQueue(peer.queue.outbound, elem)
addToOutboundAndEncryptionQueues(peer.queue.outbound, device.queue.encryption, elem)
}
}
}
@@ -448,6 +463,19 @@ func (device *Device) RoutineEncryption() {
logDebug := device.log.Debug
defer func() {
for {
select {
case elem, ok := <-device.queue.encryption:
if ok && !elem.IsDropped() {
elem.Drop()
device.PutMessageBuffer(elem.buffer)
elem.Unlock()
}
default:
goto out
}
}
out:
logDebug.Println("Routine: encryption worker - stopped")
device.state.stopping.Done()
}()
@@ -490,11 +518,13 @@ func (device *Device) RoutineEncryption() {
// pad content to multiple of 16
mtu := int(atomic.LoadInt32(&device.tun.mtu))
rem := len(elem.packet) % PaddingMultiple
if rem > 0 {
for i := 0; i < PaddingMultiple-rem && len(elem.packet) < mtu; i++ {
elem.packet = append(elem.packet, 0)
lastUnit := len(elem.packet) % mtu
paddedSize := (lastUnit + PaddingMultiple - 1) & ^(PaddingMultiple - 1)
if paddedSize > mtu {
paddedSize = mtu
}
for i := len(elem.packet); i < paddedSize; i++ {
elem.packet = append(elem.packet, 0)
}
// encrypt content and release to consumer
@@ -506,7 +536,7 @@ func (device *Device) RoutineEncryption() {
elem.packet,
nil,
)
elem.mutex.Unlock()
elem.Unlock()
}
}
}
@@ -521,8 +551,24 @@ func (peer *Peer) RoutineSequentialSender() {
device := peer.device
logDebug := device.log.Debug
logError := device.log.Error
defer func() {
for {
select {
case elem, ok := <-peer.queue.outbound:
if ok {
if !elem.IsDropped() {
device.PutMessageBuffer(elem.buffer)
elem.Drop()
}
device.PutOutboundElement(elem)
}
default:
goto out
}
}
out:
logDebug.Println(peer, "- Routine: sequential sender - stopped")
peer.routines.stopping.Done()
}()
@@ -543,8 +589,9 @@ func (peer *Peer) RoutineSequentialSender() {
return
}
elem.mutex.Lock()
elem.Lock()
if elem.IsDropped() {
device.PutOutboundElement(elem)
continue
}
@@ -553,18 +600,17 @@ func (peer *Peer) RoutineSequentialSender() {
// send message and return buffer to pool
length := uint64(len(elem.packet))
err := peer.SendBuffer(elem.packet)
device.PutMessageBuffer(elem.buffer)
if err != nil {
logDebug.Println("Failed to send authenticated packet to peer", peer)
continue
}
atomic.AddUint64(&peer.stats.txBytes, length)
if len(elem.packet) != MessageKeepaliveSize {
peer.timersDataSent()
}
device.PutMessageBuffer(elem.buffer)
device.PutOutboundElement(elem)
if err != nil {
logError.Println(peer, "- Failed to send data packet", err)
continue
}
peer.keepKeyFreshSending()
}
}

View File

@@ -1,11 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2015-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*
* This is based heavily on timers.c from the kernel implementation.
*/
package main
package device
import (
"math/rand"
@@ -19,7 +19,7 @@ import (
*/
type Timer struct {
timer *time.Timer
*time.Timer
modifyingLock sync.RWMutex
runningLock sync.Mutex
isPending bool
@@ -27,7 +27,7 @@ type Timer struct {
func (peer *Peer) NewTimer(expirationFunction func(*Peer)) *Timer {
timer := &Timer{}
timer.timer = time.AfterFunc(time.Hour, func() {
timer.Timer = time.AfterFunc(time.Hour, func() {
timer.runningLock.Lock()
timer.modifyingLock.Lock()
@@ -42,21 +42,21 @@ func (peer *Peer) NewTimer(expirationFunction func(*Peer)) *Timer {
expirationFunction(peer)
timer.runningLock.Unlock()
})
timer.timer.Stop()
timer.Stop()
return timer
}
func (timer *Timer) Mod(d time.Duration) {
timer.modifyingLock.Lock()
timer.isPending = true
timer.timer.Reset(d)
timer.Reset(d)
timer.modifyingLock.Unlock()
}
func (timer *Timer) Del() {
timer.modifyingLock.Lock()
timer.isPending = false
timer.timer.Stop()
timer.Stop()
timer.modifyingLock.Unlock()
}
@@ -79,7 +79,7 @@ func (peer *Peer) timersActive() bool {
func expiredRetransmitHandshake(peer *Peer) {
if atomic.LoadUint32(&peer.timers.handshakeAttempts) > MaxTimerHandshakes {
peer.device.log.Debug.Printf("%s: Handshake did not complete after %d attempts, giving up\n", peer, MaxTimerHandshakes+2)
peer.device.log.Debug.Printf("%s - Handshake did not complete after %d attempts, giving up\n", peer, MaxTimerHandshakes+2)
if peer.timersActive() {
peer.timers.sendKeepalive.Del()
@@ -98,14 +98,14 @@ func expiredRetransmitHandshake(peer *Peer) {
}
} else {
atomic.AddUint32(&peer.timers.handshakeAttempts, 1)
peer.device.log.Debug.Printf("%s: Handshake did not complete after %d seconds, retrying (try %d)\n", peer, int(RekeyTimeout.Seconds()), atomic.LoadUint32(&peer.timers.handshakeAttempts)+1)
peer.device.log.Debug.Printf("%s - Handshake did not complete after %d seconds, retrying (try %d)\n", peer, int(RekeyTimeout.Seconds()), atomic.LoadUint32(&peer.timers.handshakeAttempts)+1)
/* We clear the endpoint address src address, in case this is the cause of trouble. */
peer.mutex.Lock()
peer.Lock()
if peer.endpoint != nil {
peer.endpoint.ClearSrc()
}
peer.mutex.Unlock()
peer.Unlock()
peer.SendHandshakeInitiation(true)
}
@@ -122,19 +122,19 @@ func expiredSendKeepalive(peer *Peer) {
}
func expiredNewHandshake(peer *Peer) {
peer.device.log.Debug.Printf("%s: Retrying handshake because we stopped hearing back after %d seconds\n", peer, int((KeepaliveTimeout + RekeyTimeout).Seconds()))
peer.device.log.Debug.Printf("%s - Retrying handshake because we stopped hearing back after %d seconds\n", peer, int((KeepaliveTimeout + RekeyTimeout).Seconds()))
/* We clear the endpoint address src address, in case this is the cause of trouble. */
peer.mutex.Lock()
peer.Lock()
if peer.endpoint != nil {
peer.endpoint.ClearSrc()
}
peer.mutex.Unlock()
peer.Unlock()
peer.SendHandshakeInitiation(false)
}
func expiredZeroKeyMaterial(peer *Peer) {
peer.device.log.Debug.Printf(":%s Removing all keys, since we haven't received a new one in %d seconds\n", peer, int((RejectAfterTime * 3).Seconds()))
peer.device.log.Debug.Printf("%s - Removing all keys, since we haven't received a new one in %d seconds\n", peer, int((RejectAfterTime * 3).Seconds()))
peer.ZeroAndFlushAll()
}
@@ -147,7 +147,7 @@ func expiredPersistentKeepalive(peer *Peer) {
/* Should be called after an authenticated data packet is sent. */
func (peer *Peer) timersDataSent() {
if peer.timersActive() && !peer.timers.newHandshake.IsPending() {
peer.timers.newHandshake.Mod(KeepaliveTimeout + RekeyTimeout)
peer.timers.newHandshake.Mod(KeepaliveTimeout + RekeyTimeout + time.Millisecond*time.Duration(rand.Int31n(RekeyTimeoutJitterMaxMs)))
}
}

View File

@@ -1,14 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"git.zx2c4.com/wireguard-go/tun"
"sync/atomic"
"golang.zx2c4.com/wireguard/tun"
)
const DefaultMTU = 1420
@@ -23,7 +23,7 @@ func (device *Device) RoutineTUNEventReader() {
device.state.starting.Done()
for event := range device.tun.device.Events() {
if event&tun.TUNEventMTUUpdate != 0 {
if event&tun.EventMTUUpdate != 0 {
mtu, err := device.tun.device.MTU()
old := atomic.LoadInt32(&device.tun.mtu)
if err != nil {
@@ -38,13 +38,13 @@ func (device *Device) RoutineTUNEventReader() {
}
}
if event&tun.TUNEventUp != 0 && !setUp {
if event&tun.EventUp != 0 && !setUp {
logInfo.Println("Interface set up")
setUp = true
device.Up()
}
if event&tun.TUNEventDown != 0 && setUp {
if event&tun.EventDown != 0 && setUp {
logInfo.Println("Interface set down")
setUp = false
device.Down()

56
device/tun_test.go Normal file
View File

@@ -0,0 +1,56 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package device
import (
"errors"
"os"
"golang.zx2c4.com/wireguard/tun"
)
// newDummyTUN creates a dummy TUN device with the specified name.
func newDummyTUN(name string) tun.Device {
return &dummyTUN{
name: name,
packets: make(chan []byte, 100),
events: make(chan tun.Event, 10),
}
}
// A dummyTUN is a tun.Device which is used in unit tests.
type dummyTUN struct {
name string
mtu int
packets chan []byte
events chan tun.Event
}
func (d *dummyTUN) Events() chan tun.Event { return d.events }
func (*dummyTUN) File() *os.File { return nil }
func (*dummyTUN) Flush() error { return nil }
func (d *dummyTUN) MTU() (int, error) { return d.mtu, nil }
func (d *dummyTUN) Name() (string, error) { return d.name, nil }
func (d *dummyTUN) Close() error {
close(d.events)
close(d.packets)
return nil
}
func (d *dummyTUN) Read(b []byte, offset int) (int, error) {
buf, ok := <-d.packets
if !ok {
return 0, errors.New("device closed")
}
copy(b[offset:], buf)
return len(buf), nil
}
func (d *dummyTUN) Write(b []byte, offset int) (int, error) {
d.packets <- b[offset:]
return len(b), nil
}

View File

@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package device
import (
"bufio"
@@ -15,26 +14,23 @@ import (
"strings"
"sync/atomic"
"time"
"golang.zx2c4.com/wireguard/ipc"
)
type IPCError struct {
Code int64
int64
}
func (s *IPCError) Error() string {
return fmt.Sprintf("IPC error: %d", s.Code)
func (s IPCError) Error() string {
return fmt.Sprintf("IPC error: %d", s.int64)
}
func (s *IPCError) ErrorCode() int64 {
return s.Code
func (s IPCError) ErrorCode() int64 {
return s.int64
}
func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
device.log.Debug.Println("UAPI: Processing get operation")
// create lines
func (device *Device) IpcGetOperation(socket *bufio.Writer) *IPCError {
lines := make([]string, 0, 100)
send := func(line string) {
lines = append(lines, line)
@@ -44,14 +40,14 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
// lock required resources
device.net.mutex.RLock()
defer device.net.mutex.RUnlock()
device.net.RLock()
defer device.net.RUnlock()
device.staticIdentity.mutex.RLock()
defer device.staticIdentity.mutex.RUnlock()
device.staticIdentity.RLock()
defer device.staticIdentity.RUnlock()
device.peers.mutex.RLock()
defer device.peers.mutex.RUnlock()
device.peers.RLock()
defer device.peers.RUnlock()
// serialize device related values
@@ -70,11 +66,12 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
// serialize each peer state
for _, peer := range device.peers.keyMap {
peer.mutex.RLock()
defer peer.mutex.RUnlock()
peer.RLock()
defer peer.RUnlock()
send("public_key=" + peer.handshake.remoteStatic.ToHex())
send("preshared_key=" + peer.handshake.presharedKey.ToHex())
send("protocol_version=1")
if peer.endpoint != nil {
send("endpoint=" + peer.endpoint.DstToString())
}
@@ -101,16 +98,14 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
for _, line := range lines {
_, err := socket.WriteString(line + "\n")
if err != nil {
return &IPCError{
Code: ipcErrorIO,
}
return &IPCError{ipc.IpcErrorIO}
}
}
return nil
}
func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
func (device *Device) IpcSetOperation(socket *bufio.Reader) *IPCError {
scanner := bufio.NewScanner(socket)
logError := device.log.Error
logDebug := device.log.Debug
@@ -130,7 +125,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
}
parts := strings.Split(line, "=")
if len(parts) != 2 {
return &IPCError{Code: ipcErrorProtocol}
return &IPCError{ipc.IpcErrorProtocol}
}
key := parts[0]
value := parts[1]
@@ -145,7 +140,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
err := sk.FromHex(value)
if err != nil {
logError.Println("Failed to set private_key:", err)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
logDebug.Println("UAPI: Updating private key")
device.SetPrivateKey(sk)
@@ -157,20 +152,20 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
port, err := strconv.ParseUint(value, 10, 16)
if err != nil {
logError.Println("Failed to parse listen_port:", err)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
// update port and rebind
logDebug.Println("UAPI: Updating listen port")
device.net.mutex.Lock()
device.net.Lock()
device.net.port = uint16(port)
device.net.mutex.Unlock()
device.net.Unlock()
if err := device.BindUpdate(); err != nil {
logError.Println("Failed to set listen_port:", err)
return &IPCError{Code: ipcErrorPortInUse}
return &IPCError{ipc.IpcErrorPortInUse}
}
case "fwmark":
@@ -187,14 +182,14 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
if err != nil {
logError.Println("Invalid fwmark", err)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
logDebug.Println("UAPI: Updating fwmark")
if err := device.BindSetMark(uint32(fwmark)); err != nil {
logError.Println("Failed to update fwmark:", err)
return &IPCError{Code: ipcErrorPortInUse}
return &IPCError{ipc.IpcErrorPortInUse}
}
case "public_key":
@@ -205,14 +200,14 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
case "replace_peers":
if value != "true" {
logError.Println("Failed to set replace_peers, invalid value:", value)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
logDebug.Println("UAPI: Removing all peers")
device.RemoveAllPeers()
default:
logError.Println("Invalid UAPI device key:", key)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
}
@@ -227,14 +222,14 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
err := publicKey.FromHex(value)
if err != nil {
logError.Println("Failed to get peer by public key:", err)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
// ignore peer with public key of device
device.staticIdentity.mutex.RLock()
device.staticIdentity.RLock()
dummy = device.staticIdentity.publicKey.Equals(publicKey)
device.staticIdentity.mutex.RUnlock()
device.staticIdentity.RUnlock()
if dummy {
peer = &Peer{}
@@ -246,10 +241,15 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
peer, err = device.NewPeer(publicKey)
if err != nil {
logError.Println("Failed to create new peer:", err)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
if peer == nil {
dummy = true
peer = &Peer{}
} else {
logDebug.Println(peer, "- UAPI: Created")
}
}
case "remove":
@@ -257,7 +257,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
if value != "true" {
logError.Println("Failed to set remove, invalid value:", value)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
if !dummy {
logDebug.Println(peer, "- UAPI: Removing")
@@ -278,7 +278,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
if err != nil {
logError.Println("Failed to set preshared key:", err)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
case "endpoint":
@@ -288,8 +288,8 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
logDebug.Println(peer, "- UAPI: Updating endpoint")
err := func() error {
peer.mutex.Lock()
defer peer.mutex.Unlock()
peer.Lock()
defer peer.Unlock()
endpoint, err := CreateEndpoint(value)
if err != nil {
return err
@@ -299,20 +299,20 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
}()
if err != nil {
logError.Println("Failed to set endpoint:", value)
return &IPCError{Code: ipcErrorInvalid}
logError.Println("Failed to set endpoint:", err, ":", value)
return &IPCError{ipc.IpcErrorInvalid}
}
case "persistent_keepalive_interval":
// update persistent keepalive interval
logDebug.Println(peer, "- UAPI: Updating persistent keepalive interva")
logDebug.Println(peer, "- UAPI: Updating persistent keepalive interval")
secs, err := strconv.ParseUint(value, 10, 16)
if err != nil {
logError.Println("Failed to set persistent keepalive interval:", err)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
old := peer.persistentKeepaliveInterval
@@ -323,7 +323,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
if old == 0 && secs != 0 {
if err != nil {
logError.Println("Failed to get tun device status:", err)
return &IPCError{Code: ipcErrorIO}
return &IPCError{ipc.IpcErrorIO}
}
if device.isUp.Get() && !dummy {
peer.SendKeepalive()
@@ -336,7 +336,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
if value != "true" {
logError.Println("Failed to replace allowedips, invalid value:", value)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
if dummy {
@@ -352,7 +352,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
_, network, err := net.ParseCIDR(value)
if err != nil {
logError.Println("Failed to set allowed ip:", err)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
if dummy {
@@ -362,9 +362,16 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
ones, _ := network.Mask.Size()
device.allowedips.Insert(network.IP, uint(ones), peer)
case "protocol_version":
if value != "1" {
logError.Println("Invalid protocol version:", value)
return &IPCError{ipc.IpcErrorInvalid}
}
default:
logError.Println("Invalid UAPI peer key:", key)
return &IPCError{Code: ipcErrorInvalid}
return &IPCError{ipc.IpcErrorInvalid}
}
}
}
@@ -372,7 +379,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
return nil
}
func ipcHandle(device *Device, socket net.Conn) {
func (device *Device) IpcHandle(socket net.Conn) {
// create buffered read/writer
@@ -397,12 +404,10 @@ func ipcHandle(device *Device, socket net.Conn) {
switch op {
case "set=1\n":
device.log.Debug.Println("UAPI: Set operation")
status = ipcSetOperation(device, buffered)
status = device.IpcSetOperation(buffered.Reader)
case "get=1\n":
device.log.Debug.Println("UAPI: Get operation")
status = ipcGetOperation(device, buffered)
status = device.IpcGetOperation(buffered.Writer)
default:
device.log.Error.Println("Invalid UAPI operation:", op)

3
device/version.go Normal file
View File

@@ -0,0 +1,3 @@
package device
const WireGuardGoVersion = "0.0.20190908"

View File

@@ -1,49 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
*/
package main
/* Create two device instances and simulate full WireGuard interaction
* without network dependencies
*/
import "testing"
func TestDevice(t *testing.T) {
// prepare tun devices for generating traffic
tun1, err := CreateDummyTUN("tun1")
if err != nil {
t.Error("failed to create tun:", err.Error())
}
tun2, err := CreateDummyTUN("tun2")
if err != nil {
t.Error("failed to create tun:", err.Error())
}
_ = tun1
_ = tun2
// prepare endpoints
end1, err := CreateDummyEndpoint()
if err != nil {
t.Error("failed to create endpoint:", err.Error())
}
end2, err := CreateDummyEndpoint()
if err != nil {
t.Error("failed to create endpoint:", err.Error())
}
_ = end1
_ = end2
// create binds
}

View File

@@ -1,15 +0,0 @@
// +build !android
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
package main
const DoNotUseThisProgramOnLinux = UseTheKernelModuleInstead
// --------------------------------------------------------
// Do not use this on Linux. Instead use the kernel module.
// See wireguard.com/install for more information.
// --------------------------------------------------------

10
go.mod Normal file
View File

@@ -0,0 +1,10 @@
module golang.zx2c4.com/wireguard
go 1.12
require (
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
golang.org/x/sys v0.0.0-20190830023255-19e00faab6ad
golang.org/x/text v0.3.2
)

14
go.sum Normal file
View File

@@ -0,0 +1,14 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM=
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190830023255-19e00faab6ad h1:cCejgArrk10gX6kFqjWeLwXD7aVMqWoRpyUCaaJSggc=
golang.org/x/sys v0.0.0-20190830023255-19e00faab6ad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@@ -1,92 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
*/
package main
import (
"bytes"
"errors"
"os"
"testing"
)
/* Helpers for writing unit tests
*/
type DummyTUN struct {
name string
mtu int
packets chan []byte
events chan TUNEvent
}
func (tun *DummyTUN) File() *os.File {
return nil
}
func (tun *DummyTUN) Name() (string, error) {
return tun.name, nil
}
func (tun *DummyTUN) MTU() (int, error) {
return tun.mtu, nil
}
func (tun *DummyTUN) Write(d []byte, offset int) (int, error) {
tun.packets <- d[offset:]
return len(d), nil
}
func (tun *DummyTUN) Close() error {
close(tun.events)
close(tun.packets)
return nil
}
func (tun *DummyTUN) Events() chan TUNEvent {
return tun.events
}
func (tun *DummyTUN) Read(d []byte, offset int) (int, error) {
t, ok := <-tun.packets
if !ok {
return 0, errors.New("device closed")
}
copy(d[offset:], t)
return len(t), nil
}
func CreateDummyTUN(name string) (TUNDevice, error) {
var dummy DummyTUN
dummy.mtu = 0
dummy.packets = make(chan []byte, 100)
dummy.events = make(chan TUNEvent, 10)
return &dummy, nil
}
func assertNil(t *testing.T, err error) {
if err != nil {
t.Fatal(err)
}
}
func assertEqual(t *testing.T, a []byte, b []byte) {
if bytes.Compare(a, b) != 0 {
t.Fatal(a, "!=", b)
}
}
func randDevice(t *testing.T) *Device {
sk, err := newPrivateKey()
if err != nil {
t.Fatal(err)
}
tun, _ := CreateDummyTUN("dummy")
logger := NewLogger(LogLevelError, "")
device := NewDevice(tun, logger)
device.SetPrivateKey(sk)
return device
}

View File

@@ -1,29 +1,30 @@
// +build darwin freebsd openbsd
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package ipc
import (
"errors"
"fmt"
"golang.org/x/sys/unix"
"net"
"os"
"path"
"unsafe"
"golang.org/x/sys/unix"
)
var socketDirectory = "/var/run/wireguard"
const (
ipcErrorIO = -int64(unix.EIO)
ipcErrorProtocol = -int64(unix.EPROTO)
ipcErrorInvalid = -int64(unix.EINVAL)
ipcErrorPortInUse = -int64(unix.EADDRINUSE)
socketDirectory = "/var/run/wireguard"
IpcErrorIO = -int64(unix.EIO)
IpcErrorProtocol = -int64(unix.EPROTO)
IpcErrorInvalid = -int64(unix.EINVAL)
IpcErrorPortInUse = -int64(unix.EADDRINUSE)
socketName = "%s.sock"
)

View File

@@ -1,27 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
package ipc
import (
"git.zx2c4.com/wireguard-go/rwcancel"
"errors"
"fmt"
"golang.org/x/sys/unix"
"net"
"os"
"path"
"golang.org/x/sys/unix"
"golang.zx2c4.com/wireguard/rwcancel"
)
var socketDirectory = "/var/run/wireguard"
const (
ipcErrorIO = -int64(unix.EIO)
ipcErrorProtocol = -int64(unix.EPROTO)
ipcErrorInvalid = -int64(unix.EINVAL)
ipcErrorPortInUse = -int64(unix.EADDRINUSE)
socketDirectory = "/var/run/wireguard"
IpcErrorIO = -int64(unix.EIO)
IpcErrorProtocol = -int64(unix.EPROTO)
IpcErrorInvalid = -int64(unix.EINVAL)
IpcErrorPortInUse = -int64(unix.EADDRINUSE)
socketName = "%s.sock"
)

80
ipc/uapi_windows.go Normal file
View File

@@ -0,0 +1,80 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package ipc
import (
"net"
"golang.zx2c4.com/wireguard/ipc/winpipe"
)
// TODO: replace these with actual standard windows error numbers from the win package
const (
IpcErrorIO = -int64(5)
IpcErrorProtocol = -int64(71)
IpcErrorInvalid = -int64(22)
IpcErrorPortInUse = -int64(98)
)
type UAPIListener struct {
listener net.Listener // unix socket listener
connNew chan net.Conn
connErr chan error
kqueueFd int
keventFd int
}
func (l *UAPIListener) Accept() (net.Conn, error) {
for {
select {
case conn := <-l.connNew:
return conn, nil
case err := <-l.connErr:
return nil, err
}
}
}
func (l *UAPIListener) Close() error {
return l.listener.Close()
}
func (l *UAPIListener) Addr() net.Addr {
return l.listener.Addr()
}
/* SDDL_DEVOBJ_SYS_ALL from the WDK */
var UAPISecurityDescriptor = "O:SYD:P(A;;GA;;;SY)"
func UAPIListen(name string) (net.Listener, error) {
config := winpipe.PipeConfig{
SecurityDescriptor: UAPISecurityDescriptor,
}
listener, err := winpipe.ListenPipe(`\\.\pipe\ProtectedPrefix\Administrators\WireGuard\`+name, &config)
if err != nil {
return nil, err
}
uapi := &UAPIListener{
listener: listener,
connNew: make(chan net.Conn, 1),
connErr: make(chan error, 1),
}
go func(l *UAPIListener) {
for {
conn, err := l.listener.Accept()
if err != nil {
l.connErr <- err
break
}
l.connNew <- conn
}
}(uapi)
return uapi, nil
}

322
ipc/winpipe/file.go Normal file
View File

@@ -0,0 +1,322 @@
// +build windows
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2005 Microsoft
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package winpipe
import (
"errors"
"io"
"runtime"
"sync"
"sync/atomic"
"syscall"
"time"
)
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
type atomicBool int32
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
func (b *atomicBool) swap(new bool) bool {
var newInt int32
if new {
newInt = 1
}
return atomic.SwapInt32((*int32)(b), newInt) == 1
}
const (
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
)
var (
ErrFileClosed = errors.New("file has already been closed")
ErrTimeout = &timeoutError{}
)
type timeoutError struct{}
func (e *timeoutError) Error() string { return "i/o timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }
type timeoutChan chan struct{}
var ioInitOnce sync.Once
var ioCompletionPort syscall.Handle
// ioResult contains the result of an asynchronous IO operation
type ioResult struct {
bytes uint32
err error
}
// ioOperation represents an outstanding asynchronous Win32 IO
type ioOperation struct {
o syscall.Overlapped
ch chan ioResult
}
func initIo() {
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
if err != nil {
panic(err)
}
ioCompletionPort = h
go ioCompletionProcessor(h)
}
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
// It takes ownership of this handle and will close it if it is garbage collected.
type win32File struct {
handle syscall.Handle
wg sync.WaitGroup
wgLock sync.RWMutex
closing atomicBool
socket bool
readDeadline deadlineHandler
writeDeadline deadlineHandler
}
type deadlineHandler struct {
setLock sync.Mutex
channel timeoutChan
channelLock sync.RWMutex
timer *time.Timer
timedout atomicBool
}
// makeWin32File makes a new win32File from an existing file handle
func makeWin32File(h syscall.Handle) (*win32File, error) {
f := &win32File{handle: h}
ioInitOnce.Do(initIo)
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
if err != nil {
return nil, err
}
err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE)
if err != nil {
return nil, err
}
f.readDeadline.channel = make(timeoutChan)
f.writeDeadline.channel = make(timeoutChan)
return f, nil
}
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
return makeWin32File(h)
}
// closeHandle closes the resources associated with a Win32 handle
func (f *win32File) closeHandle() {
f.wgLock.Lock()
// Atomically set that we are closing, releasing the resources only once.
if !f.closing.swap(true) {
f.wgLock.Unlock()
// cancel all IO and wait for it to complete
cancelIoEx(f.handle, nil)
f.wg.Wait()
// at this point, no new IO can start
syscall.Close(f.handle)
f.handle = 0
} else {
f.wgLock.Unlock()
}
}
// Close closes a win32File.
func (f *win32File) Close() error {
f.closeHandle()
return nil
}
// prepareIo prepares for a new IO operation.
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
func (f *win32File) prepareIo() (*ioOperation, error) {
f.wgLock.RLock()
if f.closing.isSet() {
f.wgLock.RUnlock()
return nil, ErrFileClosed
}
f.wg.Add(1)
f.wgLock.RUnlock()
c := &ioOperation{}
c.ch = make(chan ioResult)
return c, nil
}
// ioCompletionProcessor processes completed async IOs forever
func ioCompletionProcessor(h syscall.Handle) {
for {
var bytes uint32
var key uintptr
var op *ioOperation
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
if op == nil {
panic(err)
}
op.ch <- ioResult{bytes, err}
}
}
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
// the operation has actually completed.
func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
if err != syscall.ERROR_IO_PENDING {
return int(bytes), err
}
if f.closing.isSet() {
cancelIoEx(f.handle, &c.o)
}
var timeout timeoutChan
if d != nil {
d.channelLock.Lock()
timeout = d.channel
d.channelLock.Unlock()
}
var r ioResult
select {
case r = <-c.ch:
err = r.err
if err == syscall.ERROR_OPERATION_ABORTED {
if f.closing.isSet() {
err = ErrFileClosed
}
} else if err != nil && f.socket {
// err is from Win32. Query the overlapped structure to get the winsock error.
var bytes, flags uint32
err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags)
}
case <-timeout:
cancelIoEx(f.handle, &c.o)
r = <-c.ch
err = r.err
if err == syscall.ERROR_OPERATION_ABORTED {
err = ErrTimeout
}
}
// runtime.KeepAlive is needed, as c is passed via native
// code to ioCompletionProcessor, c must remain alive
// until the channel read is complete.
runtime.KeepAlive(c)
return int(r.bytes), err
}
// Read reads from a file handle.
func (f *win32File) Read(b []byte) (int, error) {
c, err := f.prepareIo()
if err != nil {
return 0, err
}
defer f.wg.Done()
if f.readDeadline.timedout.isSet() {
return 0, ErrTimeout
}
var bytes uint32
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
n, err := f.asyncIo(c, &f.readDeadline, bytes, err)
runtime.KeepAlive(b)
// Handle EOF conditions.
if err == nil && n == 0 && len(b) != 0 {
return 0, io.EOF
} else if err == syscall.ERROR_BROKEN_PIPE {
return 0, io.EOF
} else {
return n, err
}
}
// Write writes to a file handle.
func (f *win32File) Write(b []byte) (int, error) {
c, err := f.prepareIo()
if err != nil {
return 0, err
}
defer f.wg.Done()
if f.writeDeadline.timedout.isSet() {
return 0, ErrTimeout
}
var bytes uint32
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
n, err := f.asyncIo(c, &f.writeDeadline, bytes, err)
runtime.KeepAlive(b)
return n, err
}
func (f *win32File) SetReadDeadline(deadline time.Time) error {
return f.readDeadline.set(deadline)
}
func (f *win32File) SetWriteDeadline(deadline time.Time) error {
return f.writeDeadline.set(deadline)
}
func (f *win32File) Flush() error {
return syscall.FlushFileBuffers(f.handle)
}
func (f *win32File) Fd() uintptr {
return uintptr(f.handle)
}
func (d *deadlineHandler) set(deadline time.Time) error {
d.setLock.Lock()
defer d.setLock.Unlock()
if d.timer != nil {
if !d.timer.Stop() {
<-d.channel
}
d.timer = nil
}
d.timedout.setFalse()
select {
case <-d.channel:
d.channelLock.Lock()
d.channel = make(chan struct{})
d.channelLock.Unlock()
default:
}
if deadline.IsZero() {
return nil
}
timeoutIO := func() {
d.timedout.setTrue()
close(d.channel)
}
now := time.Now()
duration := deadline.Sub(now)
if deadline.After(now) {
// Deadline is in the future, set a timer to wait
d.timer = time.AfterFunc(duration, timeoutIO)
} else {
// Deadline is in the past. Cancel all pending IO now.
timeoutIO()
}
return nil
}

9
ipc/winpipe/mksyscall.go Normal file
View File

@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2005 Microsoft
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package winpipe
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go pipe.go sd.go file.go

532
ipc/winpipe/pipe.go Normal file
View File

@@ -0,0 +1,532 @@
// +build windows
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2005 Microsoft
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package winpipe
import (
"context"
"errors"
"fmt"
"io"
"net"
"os"
"runtime"
"syscall"
"time"
"unsafe"
)
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile
//sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl
type ioStatusBlock struct {
Status, Information uintptr
}
type objectAttributes struct {
Length uintptr
RootDirectory uintptr
ObjectName *unicodeString
Attributes uintptr
SecurityDescriptor *securityDescriptor
SecurityQoS uintptr
}
type unicodeString struct {
Length uint16
MaximumLength uint16
Buffer uintptr
}
type securityDescriptor struct {
Revision byte
Sbz1 byte
Control uint16
Owner uintptr
Group uintptr
Sacl uintptr
Dacl uintptr
}
type ntstatus int32
func (status ntstatus) Err() error {
if status >= 0 {
return nil
}
return rtlNtStatusToDosError(status)
}
const (
cERROR_PIPE_BUSY = syscall.Errno(231)
cERROR_NO_DATA = syscall.Errno(232)
cERROR_PIPE_CONNECTED = syscall.Errno(535)
cERROR_SEM_TIMEOUT = syscall.Errno(121)
cSECURITY_SQOS_PRESENT = 0x100000
cSECURITY_ANONYMOUS = 0
cPIPE_TYPE_MESSAGE = 4
cPIPE_READMODE_MESSAGE = 2
cFILE_OPEN = 1
cFILE_CREATE = 2
cFILE_PIPE_MESSAGE_TYPE = 1
cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2
cSE_DACL_PRESENT = 4
)
var (
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
// This error should match net.errClosing since docker takes a dependency on its text.
ErrPipeListenerClosed = errors.New("use of closed network connection")
errPipeWriteClosed = errors.New("pipe has been closed for write")
)
type win32Pipe struct {
*win32File
path string
}
type win32MessageBytePipe struct {
win32Pipe
writeClosed bool
readEOF bool
}
type pipeAddress string
func (f *win32Pipe) LocalAddr() net.Addr {
return pipeAddress(f.path)
}
func (f *win32Pipe) RemoteAddr() net.Addr {
return pipeAddress(f.path)
}
func (f *win32Pipe) SetDeadline(t time.Time) error {
f.SetReadDeadline(t)
f.SetWriteDeadline(t)
return nil
}
// CloseWrite closes the write side of a message pipe in byte mode.
func (f *win32MessageBytePipe) CloseWrite() error {
if f.writeClosed {
return errPipeWriteClosed
}
err := f.win32File.Flush()
if err != nil {
return err
}
_, err = f.win32File.Write(nil)
if err != nil {
return err
}
f.writeClosed = true
return nil
}
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
// they are used to implement CloseWrite().
func (f *win32MessageBytePipe) Write(b []byte) (int, error) {
if f.writeClosed {
return 0, errPipeWriteClosed
}
if len(b) == 0 {
return 0, nil
}
return f.win32File.Write(b)
}
// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
// mode pipe will return io.EOF, as will all subsequent reads.
func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
if f.readEOF {
return 0, io.EOF
}
n, err := f.win32File.Read(b)
if err == io.EOF {
// If this was the result of a zero-byte read, then
// it is possible that the read was due to a zero-size
// message. Since we are simulating CloseWrite with a
// zero-byte message, ensure that all future Read() calls
// also return EOF.
f.readEOF = true
} else if err == syscall.ERROR_MORE_DATA {
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
// and the message still has more bytes. Treat this as a success, since
// this package presents all named pipes as byte streams.
err = nil
}
return n, err
}
func (s pipeAddress) Network() string {
return "pipe"
}
func (s pipeAddress) String() string {
return string(s)
}
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) {
for {
select {
case <-ctx.Done():
return syscall.Handle(0), ctx.Err()
default:
h, err := createFile(*path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
if err == nil {
return h, nil
}
if err != cERROR_PIPE_BUSY {
return h, &os.PathError{Err: err, Op: "open", Path: *path}
}
// Wait 10 msec and try again. This is a rather simplistic
// view, as we always try each 10 milliseconds.
time.Sleep(time.Millisecond * 10)
}
}
}
// DialPipe connects to a named pipe by path, timing out if the connection
// takes longer than the specified duration. If timeout is nil, then we use
// a default timeout of 2 seconds. (We do not use WaitNamedPipe.)
func DialPipe(path string, timeout *time.Duration, expectedOwner *syscall.SID) (net.Conn, error) {
var absTimeout time.Time
if timeout != nil {
absTimeout = time.Now().Add(*timeout)
} else {
absTimeout = time.Now().Add(time.Second * 2)
}
ctx, _ := context.WithDeadline(context.Background(), absTimeout)
conn, err := DialPipeContext(ctx, path, expectedOwner)
if err == context.DeadlineExceeded {
return nil, ErrTimeout
}
return conn, err
}
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
// cancellation or timeout.
func DialPipeContext(ctx context.Context, path string, expectedOwner *syscall.SID) (net.Conn, error) {
var err error
var h syscall.Handle
h, err = tryDialPipe(ctx, &path)
if err != nil {
return nil, err
}
if expectedOwner != nil {
var realOwner *syscall.SID
var realSd uintptr
err = getSecurityInfo(h, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &realOwner, nil, nil, nil, &realSd)
if err != nil {
syscall.Close(h)
return nil, err
}
defer localFree(realSd)
if !equalSid(realOwner, expectedOwner) {
syscall.Close(h)
return nil, syscall.ERROR_ACCESS_DENIED
}
}
var flags uint32
err = getNamedPipeInfo(h, &flags, nil, nil, nil)
if err != nil {
syscall.Close(h)
return nil, err
}
f, err := makeWin32File(h)
if err != nil {
syscall.Close(h)
return nil, err
}
// If the pipe is in message mode, return a message byte pipe, which
// supports CloseWrite().
if flags&cPIPE_TYPE_MESSAGE != 0 {
return &win32MessageBytePipe{
win32Pipe: win32Pipe{win32File: f, path: path},
}, nil
}
return &win32Pipe{win32File: f, path: path}, nil
}
type acceptResponse struct {
f *win32File
err error
}
type win32PipeListener struct {
firstHandle syscall.Handle
path string
config PipeConfig
acceptCh chan (chan acceptResponse)
closeCh chan int
doneCh chan int
}
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
path16, err := syscall.UTF16FromString(path)
if err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
var oa objectAttributes
oa.Length = unsafe.Sizeof(oa)
var ntPath unicodeString
if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
defer localFree(ntPath.Buffer)
oa.ObjectName = &ntPath
// The security descriptor is only needed for the first pipe.
if first {
if sd != nil {
len := uint32(len(sd))
sdb := localAlloc(0, len)
defer localFree(sdb)
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
} else {
// Construct the default named pipe security descriptor.
var dacl uintptr
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
return 0, fmt.Errorf("getting default named pipe ACL: %s", err)
}
defer localFree(dacl)
sdb := &securityDescriptor{
Revision: 1,
Control: cSE_DACL_PRESENT,
Dacl: dacl,
}
oa.SecurityDescriptor = sdb
}
}
typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS)
if c.MessageMode {
typ |= cFILE_PIPE_MESSAGE_TYPE
}
disposition := uint32(cFILE_OPEN)
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE)
if first {
disposition = cFILE_CREATE
// By not asking for read or write access, the named pipe file system
// will put this pipe into an initially disconnected state, blocking
// client connections until the next call with first == false.
access = syscall.SYNCHRONIZE
}
timeout := int64(-50 * 10000) // 50ms
var (
h syscall.Handle
iosb ioStatusBlock
)
err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err()
if err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
runtime.KeepAlive(ntPath)
return h, nil
}
func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
h, err := makeServerPipeHandle(l.path, nil, &l.config, false)
if err != nil {
return nil, err
}
f, err := makeWin32File(h)
if err != nil {
syscall.Close(h)
return nil, err
}
return f, nil
}
func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
p, err := l.makeServerPipe()
if err != nil {
return nil, err
}
// Wait for the client to connect.
ch := make(chan error)
go func(p *win32File) {
ch <- connectPipe(p)
}(p)
select {
case err = <-ch:
if err != nil {
p.Close()
p = nil
}
case <-l.closeCh:
// Abort the connect request by closing the handle.
p.Close()
p = nil
err = <-ch
if err == nil || err == ErrFileClosed {
err = ErrPipeListenerClosed
}
}
return p, err
}
func (l *win32PipeListener) listenerRoutine() {
closed := false
for !closed {
select {
case <-l.closeCh:
closed = true
case responseCh := <-l.acceptCh:
var (
p *win32File
err error
)
for {
p, err = l.makeConnectedServerPipe()
// If the connection was immediately closed by the client, try
// again.
if err != cERROR_NO_DATA {
break
}
}
responseCh <- acceptResponse{p, err}
closed = err == ErrPipeListenerClosed
}
}
syscall.Close(l.firstHandle)
l.firstHandle = 0
// Notify Close() and Accept() callers that the handle has been closed.
close(l.doneCh)
}
// PipeConfig contain configuration for the pipe listener.
type PipeConfig struct {
// SecurityDescriptor contains a Windows security descriptor in SDDL format.
SecurityDescriptor string
// MessageMode determines whether the pipe is in byte or message mode. In either
// case the pipe is read in byte mode by default. The only practical difference in
// this implementation is that CloseWrite() is only supported for message mode pipes;
// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
// transferred to the reader (and returned as io.EOF in this implementation)
// when the pipe is in message mode.
MessageMode bool
// InputBufferSize specifies the size the input buffer, in bytes.
InputBufferSize int32
// OutputBufferSize specifies the size the input buffer, in bytes.
OutputBufferSize int32
}
// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
// The pipe must not already exist.
func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
var (
sd []byte
err error
)
if c == nil {
c = &PipeConfig{}
}
if c.SecurityDescriptor != "" {
sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor)
if err != nil {
return nil, err
}
}
h, err := makeServerPipeHandle(path, sd, c, true)
if err != nil {
return nil, err
}
l := &win32PipeListener{
firstHandle: h,
path: path,
config: *c,
acceptCh: make(chan (chan acceptResponse)),
closeCh: make(chan int),
doneCh: make(chan int),
}
go l.listenerRoutine()
return l, nil
}
func connectPipe(p *win32File) error {
c, err := p.prepareIo()
if err != nil {
return err
}
defer p.wg.Done()
err = connectNamedPipe(p.handle, &c.o)
_, err = p.asyncIo(c, nil, 0, err)
if err != nil && err != cERROR_PIPE_CONNECTED {
return err
}
return nil
}
func (l *win32PipeListener) Accept() (net.Conn, error) {
ch := make(chan acceptResponse)
select {
case l.acceptCh <- ch:
response := <-ch
err := response.err
if err != nil {
return nil, err
}
if l.config.MessageMode {
return &win32MessageBytePipe{
win32Pipe: win32Pipe{win32File: response.f, path: l.path},
}, nil
}
return &win32Pipe{win32File: response.f, path: l.path}, nil
case <-l.doneCh:
return nil, ErrPipeListenerClosed
}
}
func (l *win32PipeListener) Close() error {
select {
case l.closeCh <- 1:
<-l.doneCh
case <-l.doneCh:
}
return nil
}
func (l *win32PipeListener) Addr() net.Addr {
return pipeAddress(l.path)
}

36
ipc/winpipe/sd.go Normal file
View File

@@ -0,0 +1,36 @@
// +build windows
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2005 Microsoft
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package winpipe
import (
"unsafe"
)
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
//sys localFree(mem uintptr) = LocalFree
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
//sys getSecurityInfo(handle syscall.Handle, objectType uint32, securityInformation uint32, owner **syscall.SID, group **syscall.SID, dacl *uintptr, sacl *uintptr, sd *uintptr) (ret error) = advapi32.GetSecurityInfo
//sys equalSid(sid1 *syscall.SID, sid2 *syscall.SID) (isEqual bool) = advapi32.EqualSid
const (
SE_FILE_OBJECT = 1
OWNER_SECURITY_INFORMATION = 1
)
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
var sdBuffer uintptr
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
if err != nil {
return nil, err
}
defer localFree(sdBuffer)
sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
return sd, nil
}

View File

@@ -0,0 +1,290 @@
// Code generated by 'go generate'; DO NOT EDIT.
package winpipe
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return nil
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
modntdll = windows.NewLazySystemDLL("ntdll.dll")
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
procCreateFileW = modkernel32.NewProc("CreateFileW")
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
procLocalFree = modkernel32.NewProc("LocalFree")
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo")
procEqualSid = modadvapi32.NewProc("EqualSid")
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
)
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(name)
if err != nil {
return
}
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
}
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
handle = syscall.Handle(r0)
if handle == syscall.InvalidHandle {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(name)
if err != nil {
return
}
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
}
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
handle = syscall.Handle(r0)
if handle == syscall.InvalidHandle {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
ptr = uintptr(r0)
return
}
func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) {
r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0)
status = ntstatus(r0)
return
}
func rtlNtStatusToDosError(status ntstatus) (winerr error) {
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
if r0 != 0 {
winerr = syscall.Errno(r0)
}
return
}
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) {
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0)
status = ntstatus(r0)
return
}
func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) {
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
status = ntstatus(r0)
return
}
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(str)
if err != nil {
return
}
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
}
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func localFree(mem uintptr) {
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
return
}
func getSecurityDescriptorLength(sd uintptr) (len uint32) {
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
len = uint32(r0)
return
}
func getSecurityInfo(handle syscall.Handle, objectType uint32, securityInformation uint32, owner **syscall.SID, group **syscall.SID, dacl *uintptr, sacl *uintptr, sd *uintptr) (ret error) {
r0, _, _ := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(sd)), 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}
func equalSid(sid1 *syscall.SID, sid2 *syscall.SID) (isEqual bool) {
r0, _, _ := syscall.Syscall(procEqualSid.Addr(), 2, uintptr(unsafe.Pointer(sid1)), uintptr(unsafe.Pointer(sid2)), 0)
isEqual = r0 != 0
return
}
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
newport = syscall.Handle(r0)
if newport == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
var _p0 uint32
if wait {
_p0 = 1
} else {
_p0 = 0
}
r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}

89
main.go
View File

@@ -1,19 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0
// +build !windows
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
import (
"git.zx2c4.com/wireguard-go/tun"
"fmt"
"os"
"os/signal"
"runtime"
"strconv"
"syscall"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/ipc"
"golang.zx2c4.com/wireguard/tun"
)
const (
@@ -33,56 +37,32 @@ func printUsage() {
}
func warning() {
if os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1" {
if runtime.GOOS != "linux" || os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1" {
return
}
shouldQuit := false
fmt.Fprintln(os.Stderr, "WARNING WARNING WARNING WARNING WARNING WARNING WARNING")
fmt.Fprintln(os.Stderr, "W G")
fmt.Fprintln(os.Stderr, "W This is alpha software. It will very likely not G")
fmt.Fprintln(os.Stderr, "W do what it is supposed to do, and things may go G")
fmt.Fprintln(os.Stderr, "W horribly wrong. You have been warned. Proceed G")
fmt.Fprintln(os.Stderr, "W at your own risk. G")
if runtime.GOOS == "linux" {
shouldQuit = os.Getenv("WG_I_PREFER_BUGGY_USERSPACE_TO_POLISHED_KMOD") != "1"
fmt.Fprintln(os.Stderr, "W G")
fmt.Fprintln(os.Stderr, "W Furthermore, you are running this software on a G")
fmt.Fprintln(os.Stderr, "W Linux kernel, which is probably unnecessary and G")
fmt.Fprintln(os.Stderr, "W foolish. This is because the Linux kernel has G")
fmt.Fprintln(os.Stderr, "W built-in first class support for WireGuard, and G")
fmt.Fprintln(os.Stderr, "W this support is much more refined than this G")
fmt.Fprintln(os.Stderr, "W program. For more information on installing the G")
fmt.Fprintln(os.Stderr, "W kernel module, please visit: G")
fmt.Fprintln(os.Stderr, "W You are running this software on a Linux kernel, G")
fmt.Fprintln(os.Stderr, "W which is probably unnecessary and misguided. This G")
fmt.Fprintln(os.Stderr, "W is because the Linux kernel has built-in first G")
fmt.Fprintln(os.Stderr, "W class support for WireGuard, and this support is G")
fmt.Fprintln(os.Stderr, "W much more refined than this slower userspace G")
fmt.Fprintln(os.Stderr, "W implementation. For more information on G")
fmt.Fprintln(os.Stderr, "W installing the kernel module, please visit: G")
fmt.Fprintln(os.Stderr, "W https://www.wireguard.com/install G")
if shouldQuit {
fmt.Fprintln(os.Stderr, "W G")
fmt.Fprintln(os.Stderr, "W If you still want to use this program, against G")
fmt.Fprintln(os.Stderr, "W the sage advice here, please first export this G")
fmt.Fprintln(os.Stderr, "W environment variable: G")
fmt.Fprintln(os.Stderr, "W WG_I_PREFER_BUGGY_USERSPACE_TO_POLISHED_KMOD=1 G")
}
}
fmt.Fprintln(os.Stderr, "W G")
fmt.Fprintln(os.Stderr, "WARNING WARNING WARNING WARNING WARNING WARNING WARNING")
if shouldQuit {
os.Exit(1)
}
}
func main() {
if len(os.Args) == 2 && os.Args[1] == "--version" {
fmt.Printf("wireguard-go v%s\n\nUserspace WireGuard daemon for %s-%s.\nInformation available at https://www.wireguard.com.\nCopyright (C) Jason A. Donenfeld <Jason@zx2c4.com>.\n", WireGuardGoVersion, runtime.GOOS, runtime.GOARCH)
fmt.Printf("wireguard-go v%s\n\nUserspace WireGuard daemon for %s-%s.\nInformation available at https://www.wireguard.com.\nCopyright (C) Jason A. Donenfeld <Jason@zx2c4.com>.\n", device.WireGuardGoVersion, runtime.GOOS, runtime.GOARCH)
return
}
warning()
// parse arguments
var foreground bool
var interfaceName string
if len(os.Args) < 2 || len(os.Args) > 3 {
@@ -118,23 +98,23 @@ func main() {
logLevel := func() int {
switch os.Getenv("LOG_LEVEL") {
case "debug":
return LogLevelDebug
return device.LogLevelDebug
case "info":
return LogLevelInfo
return device.LogLevelInfo
case "error":
return LogLevelError
return device.LogLevelError
case "silent":
return LogLevelSilent
return device.LogLevelSilent
}
return LogLevelInfo
return device.LogLevelInfo
}()
// open TUN device (or use supplied fd)
tun, err := func() (tun.TUNDevice, error) {
tun, err := func() (tun.Device, error) {
tunFdStr := os.Getenv(ENV_WG_TUN_FD)
if tunFdStr == "" {
return tun.CreateTUN(interfaceName, DefaultMTU)
return tun.CreateTUN(interfaceName, device.DefaultMTU)
}
// construct tun device from supplied fd
@@ -144,8 +124,13 @@ func main() {
return nil, err
}
err = syscall.SetNonblock(int(fd), true)
if err != nil {
return nil, err
}
file := os.NewFile(uintptr(fd), "")
return tun.CreateTUNFromFile(file, DefaultMTU)
return tun.CreateTUNFromFile(file, device.DefaultMTU)
}()
if err == nil {
@@ -155,11 +140,13 @@ func main() {
}
}
logger := NewLogger(
logger := device.NewLogger(
logLevel,
fmt.Sprintf("(%s) ", interfaceName),
)
logger.Info.Println("Starting wireguard-go version", device.WireGuardGoVersion)
logger.Debug.Println("Debug log enabled")
if err != nil {
@@ -172,7 +159,7 @@ func main() {
fileUAPI, err := func() (*os.File, error) {
uapiFdStr := os.Getenv(ENV_WG_UAPI_FD)
if uapiFdStr == "" {
return UAPIOpen(interfaceName)
return ipc.UAPIOpen(interfaceName)
}
// use supplied fd
@@ -198,7 +185,7 @@ func main() {
env = append(env, fmt.Sprintf("%s=4", ENV_WG_UAPI_FD))
env = append(env, fmt.Sprintf("%s=1", ENV_WG_PROCESS_FOREGROUND))
files := [3]*os.File{}
if os.Getenv("LOG_LEVEL") != "" && logLevel != LogLevelSilent {
if os.Getenv("LOG_LEVEL") != "" && logLevel != device.LogLevelSilent {
files[0], _ = os.Open(os.DevNull)
files[1] = os.Stdout
files[2] = os.Stderr
@@ -238,14 +225,14 @@ func main() {
return
}
device := NewDevice(tun, logger)
device := device.NewDevice(tun, logger)
logger.Info.Println("Device started")
errs := make(chan error)
term := make(chan os.Signal, 1)
uapi, err := UAPIListen(interfaceName, fileUAPI)
uapi, err := ipc.UAPIListen(interfaceName, fileUAPI)
if err != nil {
logger.Error.Println("Failed to listen on uapi socket:", err)
os.Exit(ExitSetupFailed)
@@ -258,7 +245,7 @@ func main() {
errs <- err
return
}
go ipcHandle(device, conn)
go device.IpcHandle(conn)
}
}()

94
main_windows.go Normal file
View File

@@ -0,0 +1,94 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/ipc"
"golang.zx2c4.com/wireguard/tun"
)
const (
ExitSetupSuccess = 0
ExitSetupFailed = 1
)
func main() {
if len(os.Args) != 2 {
os.Exit(ExitSetupFailed)
}
interfaceName := os.Args[1]
fmt.Fprintln(os.Stderr, "Warning: this is a test program for Windows, mainly used for debugging this Go package. For a real WireGuard for Windows client, the repo you want is <https://git.zx2c4.com/wireguard-windows/>, which includes this code as a module.")
logger := device.NewLogger(
device.LogLevelDebug,
fmt.Sprintf("(%s) ", interfaceName),
)
logger.Info.Println("Starting wireguard-go version", device.WireGuardGoVersion)
logger.Debug.Println("Debug log enabled")
tun, err := tun.CreateTUN(interfaceName)
if err == nil {
realInterfaceName, err2 := tun.Name()
if err2 == nil {
interfaceName = realInterfaceName
}
} else {
logger.Error.Println("Failed to create TUN device:", err)
os.Exit(ExitSetupFailed)
}
device := device.NewDevice(tun, logger)
device.Up()
logger.Info.Println("Device started")
uapi, err := ipc.UAPIListen(interfaceName)
if err != nil {
logger.Error.Println("Failed to listen on uapi socket:", err)
os.Exit(ExitSetupFailed)
}
errs := make(chan error)
term := make(chan os.Signal, 1)
go func() {
for {
conn, err := uapi.Accept()
if err != nil {
errs <- err
return
}
go device.IpcHandle(conn)
}
}()
logger.Info.Println("UAPI listener started")
// wait for program to terminate
signal.Notify(term, os.Interrupt)
signal.Notify(term, os.Kill)
signal.Notify(term, syscall.SIGTERM)
select {
case <-term:
case <-errs:
case <-device.Wait():
}
// clean up
uapi.Close()
device.Close()
logger.Info.Println("Shutting down")
}

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package ratelimiter
@@ -20,21 +20,21 @@ const (
)
type RatelimiterEntry struct {
mutex sync.Mutex
sync.Mutex
lastTime time.Time
tokens int64
}
type Ratelimiter struct {
mutex sync.RWMutex
sync.RWMutex
stopReset chan struct{}
tableIPv4 map[[net.IPv4len]byte]*RatelimiterEntry
tableIPv6 map[[net.IPv6len]byte]*RatelimiterEntry
}
func (rate *Ratelimiter) Close() {
rate.mutex.Lock()
defer rate.mutex.Unlock()
rate.Lock()
defer rate.Unlock()
if rate.stopReset != nil {
close(rate.stopReset)
@@ -42,8 +42,8 @@ func (rate *Ratelimiter) Close() {
}
func (rate *Ratelimiter) Init() {
rate.mutex.Lock()
defer rate.mutex.Unlock()
rate.Lock()
defer rate.Unlock()
// stop any ongoing garbage collection routine
@@ -71,23 +71,23 @@ func (rate *Ratelimiter) Init() {
}
case <-ticker.C:
func() {
rate.mutex.Lock()
defer rate.mutex.Unlock()
rate.Lock()
defer rate.Unlock()
for key, entry := range rate.tableIPv4 {
entry.mutex.Lock()
if time.Now().Sub(entry.lastTime) > garbageCollectTime {
entry.Lock()
if time.Since(entry.lastTime) > garbageCollectTime {
delete(rate.tableIPv4, key)
}
entry.mutex.Unlock()
entry.Unlock()
}
for key, entry := range rate.tableIPv6 {
entry.mutex.Lock()
if time.Now().Sub(entry.lastTime) > garbageCollectTime {
entry.Lock()
if time.Since(entry.lastTime) > garbageCollectTime {
delete(rate.tableIPv6, key)
}
entry.mutex.Unlock()
entry.Unlock()
}
if len(rate.tableIPv4) == 0 && len(rate.tableIPv6) == 0 {
@@ -109,7 +109,7 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
IPv4 := ip.To4()
IPv6 := ip.To16()
rate.mutex.RLock()
rate.RLock()
if IPv4 != nil {
copy(keyIPv4[:], IPv4)
@@ -119,7 +119,7 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
entry = rate.tableIPv6[keyIPv6]
}
rate.mutex.RUnlock()
rate.RUnlock()
// make new entry if not found
@@ -127,7 +127,7 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
entry = new(RatelimiterEntry)
entry.tokens = maxTokens - packetCost
entry.lastTime = time.Now()
rate.mutex.Lock()
rate.Lock()
if IPv4 != nil {
rate.tableIPv4[keyIPv4] = entry
if len(rate.tableIPv4) == 1 && len(rate.tableIPv6) == 0 {
@@ -139,13 +139,13 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
rate.stopReset <- struct{}{}
}
}
rate.mutex.Unlock()
rate.Unlock()
return true
}
// add tokens to entry
entry.mutex.Lock()
entry.Lock()
now := time.Now()
entry.tokens += now.Sub(entry.lastTime).Nanoseconds()
entry.lastTime = now
@@ -157,9 +157,9 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
if entry.tokens > packetCost {
entry.tokens -= packetCost
entry.mutex.Unlock()
entry.Unlock()
return true
}
entry.mutex.Unlock()
entry.Unlock()
return false
}

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package ratelimiter

View File

@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package replay

View File

@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package replay

22
rwcancel/fdset.go Normal file
View File

@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package rwcancel
import "golang.org/x/sys/unix"
type fdSet struct {
unix.FdSet
}
func (fdset *fdSet) set(i int) {
bits := 32 << (^uint(0) >> 63)
fdset.Bits[i/bits] |= 1 << uint(i%bits)
}
func (fdset *fdSet) check(i int) bool {
bits := 32 << (^uint(0) >> 63)
return (fdset.Bits[i/bits] & (1 << uint(i%bits))) != 0
}

View File

@@ -1,24 +0,0 @@
// +build !freebsd
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
package rwcancel
import "golang.org/x/sys/unix"
type fdSet struct {
fdset unix.FdSet
}
func (fdset *fdSet) set(i int) {
bits := 32 << (^uint(0) >> 63)
fdset.fdset.Bits[i/bits] |= 1 << uint(i%bits)
}
func (fdset *fdSet) check(i int) bool {
bits := 32 << (^uint(0) >> 63)
return (fdset.fdset.Bits[i/bits] & (1 << uint(i%bits))) != 0
}

View File

@@ -1,22 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
package rwcancel
import "golang.org/x/sys/unix"
type fdSet struct {
fdset unix.FdSet
}
func (fdset *fdSet) set(i int) {
bits := 32 << (^uint(0) >> 63)
fdset.fdset.X__fds_bits[i/bits] |= 1 << uint(i%bits)
}
func (fdset *fdSet) check(i int) bool {
bits := 32 << (^uint(0) >> 63)
return (fdset.fdset.X__fds_bits[i/bits] & (1 << uint(i%bits))) != 0
}

View File

@@ -1,15 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package rwcancel
import (
"errors"
"golang.org/x/sys/unix"
"os"
"syscall"
"golang.org/x/sys/unix"
)
func max(a, b int) int {
@@ -40,15 +41,16 @@ func NewRWCancel(fd int) (*RWCancel, error) {
return &rwcancel, nil
}
/* https://golang.org/src/crypto/rand/eagain.go */
func ErrorIsEAGAIN(err error) bool {
func RetryAfterError(err error) bool {
if pe, ok := err.(*os.PathError); ok {
if errno, ok := pe.Err.(syscall.Errno); ok && errno == syscall.EAGAIN {
err = pe.Err
}
if errno, ok := err.(syscall.Errno); ok {
switch errno {
case syscall.EAGAIN, syscall.EINTR:
return true
}
}
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EAGAIN {
return true
}
return false
}
@@ -58,7 +60,7 @@ func (rw *RWCancel) ReadyRead() bool {
fdset := fdSet{}
fdset.set(rw.fd)
fdset.set(closeFd)
err := unixSelect(max(rw.fd, closeFd)+1, &fdset.fdset, nil, nil, nil)
err := unixSelect(max(rw.fd, closeFd)+1, &fdset.FdSet, nil, nil, nil)
if err != nil {
return false
}
@@ -73,7 +75,7 @@ func (rw *RWCancel) ReadyWrite() bool {
fdset := fdSet{}
fdset.set(rw.fd)
fdset.set(closeFd)
err := unixSelect(max(rw.fd, closeFd)+1, nil, &fdset.fdset, nil, nil)
err := unixSelect(max(rw.fd, closeFd)+1, nil, &fdset.FdSet, nil, nil)
if err != nil {
return false
}
@@ -86,7 +88,7 @@ func (rw *RWCancel) ReadyWrite() bool {
func (rw *RWCancel) Read(p []byte) (n int, err error) {
for {
n, err := unix.Read(rw.fd, p)
if err == nil || !ErrorIsEAGAIN(err) {
if err == nil || !RetryAfterError(err) {
return n, err
}
if !rw.ReadyRead() {
@@ -98,7 +100,7 @@ func (rw *RWCancel) Read(p []byte) (n int, err error) {
func (rw *RWCancel) Write(p []byte) (n int, err error) {
for {
n, err := unix.Write(rw.fd, p)
if err == nil || !ErrorIsEAGAIN(err) {
if err == nil || !RetryAfterError(err) {
return n, err
}
if !rw.ReadyWrite() {

View File

@@ -1,8 +1,8 @@
// +build !linux
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package rwcancel

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package rwcancel

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package tai64n
@@ -12,7 +12,8 @@ import (
)
const TimestampSize = 12
const base = uint64(4611686018427387914)
const base = uint64(0x400000000000000a)
const whitenerMask = uint32(0x1000000 - 1)
type Timestamp [TimestampSize]byte
@@ -20,7 +21,7 @@ func Now() Timestamp {
var tai64n Timestamp
now := time.Now()
secs := base + uint64(now.Unix())
nano := uint32(now.UnixNano())
nano := uint32(now.Nanosecond()) &^ whitenerMask
binary.BigEndian.PutUint64(tai64n[:], secs)
binary.BigEndian.PutUint32(tai64n[8:], nano)
return tai64n

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package tai64n
@@ -15,11 +15,15 @@ import (
*/
func TestMonotonic(t *testing.T) {
old := Now()
for i := 0; i < 10000; i++ {
time.Sleep(time.Nanosecond)
for i := 0; i < 50; i++ {
next := Now()
if next.After(old) {
t.Error("Whitening insufficient")
}
time.Sleep(time.Duration(whitenerMask)/time.Nanosecond + 1)
next = Now()
if !next.After(old) {
t.Error("TAI64N, not monotonically increasing on nano-second scale")
t.Error("Not monotonically increasing on whitened nano-second scale")
}
old = next
}

24
tun/operateonfd.go Normal file
View File

@@ -0,0 +1,24 @@
// +build !windows
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package tun
import (
"fmt"
)
func (tun *NativeTun) operateOnFd(fn func(fd uintptr)) {
sysconn, err := tun.tunFile.SyscallConn()
if err != nil {
tun.errors <- fmt.Errorf("unable to find sysconn for tunfile: %s", err.Error())
return
}
err = sysconn.Control(fn)
if err != nil {
tun.errors <- fmt.Errorf("unable to control sysconn for tunfile: %s", err.Error())
}
}

View File

@@ -1,27 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package tun
import "os"
type TUNEvent int
const (
TUNEventUp = 1 << iota
TUNEventDown
TUNEventMTUUpdate
import (
"os"
)
type TUNDevice interface {
type Event int
const (
EventUp = 1 << iota
EventDown
EventMTUUpdate
)
type Device interface {
File() *os.File // returns the file descriptor of the device
Read([]byte, int) (int, error) // read a packet from the device (without any additional headers)
Write([]byte, int) (int, error) // writes a packet to the device (without any additional headers)
Flush() error // flush all previous writes to the device
MTU() (int, error) // returns the MTU of the device
Name() (string, error) // fetches and returns the current name
Events() chan TUNEvent // returns a constant channel of events related to the device
Events() chan Event // returns a constant channel of events related to the device
Close() error // stops the device and closes the event channel
}

View File

@@ -1,21 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package tun
import (
"git.zx2c4.com/wireguard-go/rwcancel"
"errors"
"fmt"
"golang.org/x/net/ipv6"
"golang.org/x/sys/unix"
"io/ioutil"
"net"
"os"
"syscall"
"unsafe"
"golang.org/x/net/ipv6"
"golang.org/x/sys/unix"
)
const utunControlName = "com.apple.net.utun_control"
@@ -33,18 +32,17 @@ type sockaddrCtl struct {
scReserved [5]uint32
}
type nativeTun struct {
type NativeTun struct {
name string
fd *os.File
rwcancel *rwcancel.RWCancel
events chan TUNEvent
tunFile *os.File
events chan Event
errors chan error
routeSocket int
}
var sockaddrCtlSize uintptr = 32
func (tun *nativeTun) routineRouteListener(tunIfindex int) {
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
var (
statusUp bool
statusMTU int
@@ -54,8 +52,12 @@ func (tun *nativeTun) routineRouteListener(tunIfindex int) {
data := make([]byte, os.Getpagesize())
for {
retry:
n, err := unix.Read(tun.routeSocket, data)
if err != nil {
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
goto retry
}
tun.errors <- err
return
}
@@ -81,22 +83,22 @@ func (tun *nativeTun) routineRouteListener(tunIfindex int) {
// Up / Down event
up := (iface.Flags & net.FlagUp) != 0
if up != statusUp && up {
tun.events <- TUNEventUp
tun.events <- EventUp
}
if up != statusUp && !up {
tun.events <- TUNEventDown
tun.events <- EventDown
}
statusUp = up
// MTU changes
if iface.MTU != statusMTU {
tun.events <- TUNEventMTUUpdate
tun.events <- EventMTUUpdate
}
statusMTU = iface.MTU
}
}
func CreateTUN(name string, mtu int) (TUNDevice, error) {
func CreateTUN(name string, mtu int) (Device, error) {
ifIndex := -1
if name != "utun" {
_, err := fmt.Sscanf(name, "utun%d", &ifIndex)
@@ -150,29 +152,32 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
return nil, fmt.Errorf("SYS_CONNECT: %v", errno)
}
err = syscall.SetNonblock(fd, true)
if err != nil {
return nil, err
}
tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu)
if err == nil && name == "utun" {
fname := os.Getenv("WG_TUN_NAME_FILE")
if fname != "" {
ioutil.WriteFile(fname, []byte(tun.(*nativeTun).name+"\n"), 0400)
ioutil.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400)
}
}
return tun, err
}
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
tun := &nativeTun{
fd: file,
events: make(chan TUNEvent, 10),
errors: make(chan error, 1),
func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
tun := &NativeTun{
tunFile: file,
events: make(chan Event, 10),
errors: make(chan error, 5),
}
name, err := tun.Name()
if err != nil {
tun.fd.Close()
tun.tunFile.Close()
return nil, err
}
@@ -184,47 +189,45 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
return iface.Index, nil
}()
if err != nil {
tun.fd.Close()
return nil, err
}
tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd()))
if err != nil {
tun.fd.Close()
tun.tunFile.Close()
return nil, err
}
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
if err != nil {
tun.fd.Close()
tun.tunFile.Close()
return nil, err
}
go tun.routineRouteListener(tunIfindex)
if mtu > 0 {
err = tun.setMTU(mtu)
if err != nil {
tun.Close()
return nil, err
}
}
return tun, nil
}
func (tun *nativeTun) Name() (string, error) {
func (tun *NativeTun) Name() (string, error) {
var ifName struct {
name [16]byte
}
ifNameSize := uintptr(16)
_, _, errno := unix.Syscall6(
var errno syscall.Errno
tun.operateOnFd(func(fd uintptr) {
_, _, errno = unix.Syscall6(
unix.SYS_GETSOCKOPT,
uintptr(tun.fd.Fd()),
fd,
2, /* #define SYSPROTO_CONTROL 2 */
2, /* #define UTUN_OPT_IFNAME 2 */
uintptr(unsafe.Pointer(&ifName)),
uintptr(unsafe.Pointer(&ifNameSize)), 0)
})
if errno != 0 {
return "", fmt.Errorf("SYS_GETSOCKOPT: %v", errno)
@@ -234,21 +237,21 @@ func (tun *nativeTun) Name() (string, error) {
return tun.name, nil
}
func (tun *nativeTun) File() *os.File {
return tun.fd
func (tun *NativeTun) File() *os.File {
return tun.tunFile
}
func (tun *nativeTun) Events() chan TUNEvent {
func (tun *NativeTun) Events() chan Event {
return tun.events
}
func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
select {
case err := <-tun.errors:
return 0, err
default:
buff := buff[offset-4:]
n, err := tun.fd.Read(buff[:])
n, err := tun.tunFile.Read(buff[:])
if n < 4 {
return 0, err
}
@@ -256,19 +259,7 @@ func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
}
}
func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
for {
n, err := tun.doRead(buff, offset)
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
return n, err
}
if !tun.rwcancel.ReadyRead() {
return 0, errors.New("tun device closed")
}
}
}
func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
// reserve space for header
@@ -288,30 +279,30 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
// write
return tun.fd.Write(buff)
return tun.tunFile.Write(buff)
}
func (tun *nativeTun) Close() error {
var err3 error
err1 := tun.rwcancel.Cancel()
err2 := tun.fd.Close()
func (tun *NativeTun) Flush() error {
// TODO: can flushing be implemented by buffering and using sendmmsg?
return nil
}
func (tun *NativeTun) Close() error {
var err2 error
err1 := tun.tunFile.Close()
if tun.routeSocket != -1 {
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
err3 = unix.Close(tun.routeSocket)
tun.routeSocket = -1
err2 = unix.Close(tun.routeSocket)
} else if tun.events != nil {
close(tun.events)
}
if err1 != nil {
return err1
}
if err2 != nil {
return err2
}
return err3
}
func (tun *nativeTun) setMTU(n int) error {
func (tun *NativeTun) setMTU(n int) error {
// open datagram socket
@@ -348,7 +339,7 @@ func (tun *nativeTun) setMTU(n int) error {
return nil
}
func (tun *nativeTun) MTU() (int, error) {
func (tun *NativeTun) MTU() (int, error) {
// open datagram socket

View File

@@ -1,27 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package tun
import (
"git.zx2c4.com/wireguard-go/rwcancel"
"bytes"
"errors"
"fmt"
"golang.org/x/net/ipv6"
"golang.org/x/sys/unix"
"net"
"os"
"syscall"
"unsafe"
"golang.org/x/net/ipv6"
"golang.org/x/sys/unix"
)
// _TUNSIFHEAD, value derived from sys/net/{if_tun,ioccom}.h
// const _TUNSIFHEAD = ((0x80000000) | (((4) & ((1 << 13) - 1) ) << 16) | (uint32(byte('t')) << 8) | (96))
const _TUNSIFHEAD = 0x80047460
const _TUNSIFMODE = 0x8004745e
const _TUNSIFPID = 0x2000745f
const (
_TUNSIFHEAD = 0x80047460
_TUNSIFMODE = 0x8004745e
_TUNSIFPID = 0x2000745f
)
// TODO: move into x/sys/unix
const (
SIOCGIFINFO_IN6 = 0xc048696c
SIOCSIFINFO_IN6 = 0xc048696d
ND6_IFF_AUTO_LINKLOCAL = 0x20
ND6_IFF_NO_DAD = 0x100
)
// Iface status string max len
const _IFSTATMAX = 800
@@ -32,7 +43,7 @@ const SIZEOF_UINTPTR = 4 << (^uintptr(0) >> 32 & 1)
type ifreq_ptr struct {
Name [unix.IFNAMSIZ]byte
Data uintptr
Pad0 [24 - SIZEOF_UINTPTR]byte
Pad0 [16 - SIZEOF_UINTPTR]byte
}
// Structure for iface mtu get/set ioctls
@@ -48,16 +59,32 @@ type ifstat struct {
Ascii [_IFSTATMAX]byte
}
type nativeTun struct {
// Structures for nd6 flag manipulation
type in6_ndireq struct {
Name [unix.IFNAMSIZ]byte
Linkmtu uint32
Maxmtu uint32
Basereachable uint32
Reachable uint32
Retrans uint32
Flags uint32
Recalctm int
Chlim uint8
Initialized uint8
Randomseed0 [8]byte
Randomseed1 [8]byte
Randomid [8]byte
}
type NativeTun struct {
name string
fd *os.File
rwcancel *rwcancel.RWCancel
events chan TUNEvent
tunFile *os.File
events chan Event
errors chan error
routeSocket int
}
func (tun *nativeTun) routineRouteListener(tunIfindex int) {
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
var (
statusUp bool
statusMTU int
@@ -67,8 +94,12 @@ func (tun *nativeTun) routineRouteListener(tunIfindex int) {
data := make([]byte, os.Getpagesize())
for {
retry:
n, err := unix.Read(tun.routeSocket, data)
if err != nil {
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
goto retry
}
tun.errors <- err
return
}
@@ -94,16 +125,16 @@ func (tun *nativeTun) routineRouteListener(tunIfindex int) {
// Up / Down event
up := (iface.Flags & net.FlagUp) != 0
if up != statusUp && up {
tun.events <- TUNEventUp
tun.events <- EventUp
}
if up != statusUp && !up {
tun.events <- TUNEventDown
tun.events <- EventDown
}
statusUp = up
// MTU changes
if iface.MTU != statusMTU {
tun.events <- TUNEventMTUUpdate
tun.events <- EventMTUUpdate
}
statusMTU = iface.MTU
}
@@ -188,23 +219,18 @@ func tunName(fd uintptr) (string, error) {
// Destroy a named system interface
func tunDestroy(name string) error {
// open control socket
// Open control socket.
var fd int
fd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
0,
)
if err != nil {
return err
}
defer unix.Close(fd)
// do ioctl call
var ifr [32]byte
copy(ifr[:], name)
_, _, errno := unix.Syscall(
@@ -213,7 +239,6 @@ func tunDestroy(name string) error {
uintptr(unix.SIOCIFDESTROY),
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return fmt.Errorf("failed to destroy interface %s: %s", name, errno.Error())
}
@@ -221,7 +246,7 @@ func tunDestroy(name string) error {
return nil
}
func CreateTUN(name string, mtu int) (TUNDevice, error) {
func CreateTUN(name string, mtu int) (Device, error) {
if len(name) > unix.IFNAMSIZ-1 {
return nil, errors.New("interface name too long")
}
@@ -232,68 +257,99 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
return nil, fmt.Errorf("interface %s already exists", name)
}
tunfile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0)
tunFile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0)
if err != nil {
return nil, err
}
tunfd := tunfile.Fd()
assignedName, err := tunName(tunfd)
tun := NativeTun{tunFile: tunFile}
var assignedName string
tun.operateOnFd(func(fd uintptr) {
assignedName, err = tunName(fd)
})
if err != nil {
tunfile.Close()
tunFile.Close()
return nil, err
}
// Enable ifhead mode, otherwise tun will complain if it gets a non-AF_INET packet
ifheadmode := 1
_, _, errno := unix.Syscall(
var errno syscall.Errno
tun.operateOnFd(func(fd uintptr) {
_, _, errno = unix.Syscall(
unix.SYS_IOCTL,
uintptr(tunfd),
fd,
uintptr(_TUNSIFHEAD),
uintptr(unsafe.Pointer(&ifheadmode)),
)
})
if errno != 0 {
return nil, fmt.Errorf("error %s", errno.Error())
tunFile.Close()
tunDestroy(assignedName)
return nil, fmt.Errorf("Unable to put into IFHEAD mode: %v", errno)
}
// Set TUN iface to broadcast mode. TUN inferfaces on freebsd come up in point to point by default
ifmodemode := unix.IFF_BROADCAST
_, _, errno = unix.Syscall(
unix.SYS_IOCTL,
uintptr(tunfd),
uintptr(_TUNSIFMODE),
uintptr(unsafe.Pointer(&ifmodemode)),
)
if errno != 0 {
return nil, fmt.Errorf("error %s", errno.Error())
}
// Rename tun interface
// Open control socket
// Open control sockets
confd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
0,
)
if err != nil {
tunFile.Close()
tunDestroy(assignedName)
return nil, err
}
defer unix.Close(confd)
confd6, err := unix.Socket(
unix.AF_INET6,
unix.SOCK_DGRAM,
0,
)
if err != nil {
tunFile.Close()
tunDestroy(assignedName)
return nil, err
}
defer unix.Close(confd6)
// set up struct for iface rename
// Disable link-local v6, not just because WireGuard doesn't do that anyway, but
// also because there are serious races with attaching and detaching LLv6 addresses
// in relation to interface lifetime within the FreeBSD kernel.
var ndireq in6_ndireq
copy(ndireq.Name[:], assignedName)
_, _, errno = unix.Syscall(
unix.SYS_IOCTL,
uintptr(confd6),
uintptr(SIOCGIFINFO_IN6),
uintptr(unsafe.Pointer(&ndireq)),
)
if errno != 0 {
tunFile.Close()
tunDestroy(assignedName)
return nil, fmt.Errorf("Unable to get nd6 flags for %s: %v", assignedName, errno)
}
ndireq.Flags = ndireq.Flags &^ ND6_IFF_AUTO_LINKLOCAL
ndireq.Flags = ndireq.Flags | ND6_IFF_NO_DAD
_, _, errno = unix.Syscall(
unix.SYS_IOCTL,
uintptr(confd6),
uintptr(SIOCSIFINFO_IN6),
uintptr(unsafe.Pointer(&ndireq)),
)
if errno != 0 {
tunFile.Close()
tunDestroy(assignedName)
return nil, fmt.Errorf("Unable to set nd6 flags for %s: %v", assignedName, errno)
}
// Rename the interface
var newnp [unix.IFNAMSIZ]byte
copy(newnp[:], name)
var ifr ifreq_ptr
copy(ifr.Name[:], assignedName)
ifr.Data = uintptr(unsafe.Pointer(&newnp[0]))
//do actual ioctl to rename iface
_, _, errno = unix.Syscall(
unix.SYS_IOCTL,
uintptr(confd),
@@ -301,25 +357,25 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
uintptr(unsafe.Pointer(&ifr)),
)
if errno != 0 {
tunfile.Close()
tunDestroy(name)
return nil, fmt.Errorf("failed to rename %s to %s: %s", assignedName, name, errno.Error())
tunFile.Close()
tunDestroy(assignedName)
return nil, fmt.Errorf("Failed to rename %s to %s: %v", assignedName, name, errno)
}
return CreateTUNFromFile(tunfile, mtu)
return CreateTUNFromFile(tunFile, mtu)
}
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
tun := &nativeTun{
fd: file,
events: make(chan TUNEvent, 10),
tun := &NativeTun{
tunFile: file,
events: make(chan Event, 10),
errors: make(chan error, 1),
}
name, err := tun.Name()
if err != nil {
tun.fd.Close()
tun.tunFile.Close()
return nil, err
}
@@ -331,19 +387,13 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
return iface.Index, nil
}()
if err != nil {
tun.fd.Close()
return nil, err
}
tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd()))
if err != nil {
tun.fd.Close()
tun.tunFile.Close()
return nil, err
}
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
if err != nil {
tun.fd.Close()
tun.tunFile.Close()
return nil, err
}
@@ -358,8 +408,12 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
return tun, nil
}
func (tun *nativeTun) Name() (string, error) {
name, err := tunName(tun.fd.Fd())
func (tun *NativeTun) Name() (string, error) {
var name string
var err error
tun.operateOnFd(func(fd uintptr) {
name, err = tunName(fd)
})
if err != nil {
return "", err
}
@@ -367,21 +421,21 @@ func (tun *nativeTun) Name() (string, error) {
return name, nil
}
func (tun *nativeTun) File() *os.File {
return tun.fd
func (tun *NativeTun) File() *os.File {
return tun.tunFile
}
func (tun *nativeTun) Events() chan TUNEvent {
func (tun *NativeTun) Events() chan Event {
return tun.events
}
func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
select {
case err := <-tun.errors:
return 0, err
default:
buff := buff[offset-4:]
n, err := tun.fd.Read(buff[:])
n, err := tun.tunFile.Read(buff[:])
if n < 4 {
return 0, err
}
@@ -389,19 +443,7 @@ func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
}
}
func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
for {
n, err := tun.doRead(buff, offset)
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
return n, err
}
if !tun.rwcancel.ReadyRead() {
return 0, errors.New("tun device closed")
}
}
}
func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
// reserve space for header
@@ -421,17 +463,21 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
// write
return tun.fd.Write(buff)
return tun.tunFile.Write(buff)
}
func (tun *nativeTun) Close() error {
var err4 error
err1 := tun.rwcancel.Cancel()
err2 := tun.fd.Close()
err3 := tunDestroy(tun.name)
func (tun *NativeTun) Flush() error {
// TODO: can flushing be implemented by buffering and using sendmmsg?
return nil
}
func (tun *NativeTun) Close() error {
var err3 error
err1 := tun.tunFile.Close()
err2 := tunDestroy(tun.name)
if tun.routeSocket != -1 {
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
err4 = unix.Close(tun.routeSocket)
err3 = unix.Close(tun.routeSocket)
tun.routeSocket = -1
} else if tun.events != nil {
close(tun.events)
@@ -442,13 +488,10 @@ func (tun *nativeTun) Close() error {
if err2 != nil {
return err2
}
if err3 != nil {
return err3
}
return err4
}
func (tun *nativeTun) setMTU(n int) error {
func (tun *NativeTun) setMTU(n int) error {
// open datagram socket
var fd int
@@ -485,7 +528,7 @@ func (tun *nativeTun) setMTU(n int) error {
return nil
}
func (tun *nativeTun) MTU() (int, error) {
func (tun *NativeTun) MTU() (int, error) {
// open datagram socket
fd, err := unix.Socket(

View File

@@ -1,29 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
/* Copyright 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
package tun
/* Implementation of the TUN device interface for linux
*/
import (
"git.zx2c4.com/wireguard-go/rwcancel"
"bytes"
"errors"
"fmt"
"golang.org/x/net/ipv6"
"golang.org/x/sys/unix"
"net"
"os"
"strconv"
"sync"
"syscall"
"time"
"unsafe"
"golang.org/x/net/ipv6"
"golang.org/x/sys/unix"
"golang.zx2c4.com/wireguard/rwcancel"
)
const (
@@ -31,13 +29,12 @@ const (
ifReqSize = unix.IFNAMSIZ + 64
)
type nativeTun struct {
fd *os.File
fdCancel *rwcancel.RWCancel
type NativeTun struct {
tunFile *os.File
index int32 // if index
name string // name of interface
errors chan error // async error handling
events chan TUNEvent // device related events
events chan Event // device related events
nopi bool // the device was pased IFF_NO_PI
netlinkSock int
netlinkCancel *rwcancel.RWCancel
@@ -45,23 +42,31 @@ type nativeTun struct {
statusListenersShutdown chan struct{}
}
func (tun *nativeTun) File() *os.File {
return tun.fd
func (tun *NativeTun) File() *os.File {
return tun.tunFile
}
func (tun *nativeTun) routineHackListener() {
func (tun *NativeTun) routineHackListener() {
defer tun.hackListenerClosed.Unlock()
/* This is needed for the detection to work across network namespaces
* If you are reading this and know a better method, please get in touch.
*/
fd := int(tun.fd.Fd())
for {
_, err := unix.Write(fd, nil)
sysconn, err := tun.tunFile.SyscallConn()
if err != nil {
return
}
err2 := sysconn.Control(func(fd uintptr) {
_, err = unix.Write(int(fd), nil)
})
if err2 != nil {
return
}
switch err {
case unix.EINVAL:
tun.events <- TUNEventUp
tun.events <- EventUp
case unix.EIO:
tun.events <- TUNEventDown
tun.events <- EventDown
default:
return
}
@@ -89,7 +94,7 @@ func createNetlinkSocket() (int, error) {
return sock, nil
}
func (tun *nativeTun) routineNetlinkListener() {
func (tun *NativeTun) routineNetlinkListener() {
defer func() {
unix.Close(tun.netlinkSock)
tun.hackListenerClosed.Lock()
@@ -102,7 +107,7 @@ func (tun *nativeTun) routineNetlinkListener() {
var msgn int
for {
msgn, _, _, _, err = unix.Recvmsg(tun.netlinkSock, msg[:], nil, 0)
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
if err == nil || !rwcancel.RetryAfterError(err) {
break
}
if !tun.netlinkCancel.ReadyRead() {
@@ -143,14 +148,14 @@ func (tun *nativeTun) routineNetlinkListener() {
}
if info.Flags&unix.IFF_RUNNING != 0 {
tun.events <- TUNEventUp
tun.events <- EventUp
}
if info.Flags&unix.IFF_RUNNING == 0 {
tun.events <- TUNEventDown
tun.events <- EventDown
}
tun.events <- TUNEventMTUUpdate
tun.events <- EventMTUUpdate
default:
remain = remain[hdr.Len:]
@@ -159,21 +164,17 @@ func (tun *nativeTun) routineNetlinkListener() {
}
}
func (tun *nativeTun) isUp() (bool, error) {
func (tun *NativeTun) isUp() (bool, error) {
inter, err := net.InterfaceByName(tun.name)
return inter.Flags&net.FlagUp != 0, err
}
func getDummySock() (int, error) {
return unix.Socket(
func getIFIndex(name string) (int32, error) {
fd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
0,
)
}
func getIFIndex(name string) (int32, error) {
fd, err := getDummySock()
if err != nil {
return 0, err
}
@@ -196,10 +197,8 @@ func getIFIndex(name string) (int32, error) {
return *(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])), nil
}
func (tun *nativeTun) setMTU(n int) error {
func (tun *NativeTun) setMTU(n int) error {
// open datagram socket
fd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
@@ -231,10 +230,8 @@ func (tun *nativeTun) setMTU(n int) error {
return nil
}
func (tun *nativeTun) MTU() (int, error) {
func (tun *NativeTun) MTU() (int, error) {
// open datagram socket
fd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
@@ -258,23 +255,32 @@ func (tun *nativeTun) MTU() (int, error) {
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return 0, errors.New("failed to get MTU of TUN device: " + strconv.FormatInt(int64(errno), 10))
return 0, errors.New("failed to get MTU of TUN device: " + errno.Error())
}
return int(*(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ]))), nil
}
func (tun *nativeTun) Name() (string, error) {
func (tun *NativeTun) Name() (string, error) {
sysconn, err := tun.tunFile.SyscallConn()
if err != nil {
return "", err
}
var ifr [ifReqSize]byte
_, _, errno := unix.Syscall(
var errno syscall.Errno
err = sysconn.Control(func(fd uintptr) {
_, _, errno = unix.Syscall(
unix.SYS_IOCTL,
tun.fd.Fd(),
fd,
uintptr(unix.TUNGETIFF),
uintptr(unsafe.Pointer(&ifr[0])),
)
})
if err != nil {
return "", errors.New("failed to get name of TUN device: " + err.Error())
}
if errno != 0 {
return "", errors.New("failed to get name of TUN device: " + strconv.FormatInt(int64(errno), 10))
return "", errors.New("failed to get name of TUN device: " + errno.Error())
}
nullStr := ifr[:]
i := bytes.IndexByte(nullStr, 0)
@@ -285,7 +291,7 @@ func (tun *nativeTun) Name() (string, error) {
return tun.name, nil
}
func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
if tun.nopi {
buff = buff[offset:]
@@ -310,19 +316,24 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
// write
return tun.fd.Write(buff)
return tun.tunFile.Write(buff)
}
func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
func (tun *NativeTun) Flush() error {
// TODO: can flushing be implemented by buffering and using sendmmsg?
return nil
}
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
select {
case err := <-tun.errors:
return 0, err
default:
if tun.nopi {
return tun.fd.Read(buff[offset:])
return tun.tunFile.Read(buff[offset:])
} else {
buff := buff[offset-4:]
n, err := tun.fd.Read(buff[:])
n, err := tun.tunFile.Read(buff[:])
if n < 4 {
return 0, err
}
@@ -331,23 +342,11 @@ func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
}
}
func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
for {
n, err := tun.doRead(buff, offset)
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
return n, err
}
if !tun.fdCancel.ReadyRead() {
return 0, errors.New("tun device closed")
}
}
}
func (tun *nativeTun) Events() chan TUNEvent {
func (tun *NativeTun) Events() chan Event {
return tun.events
}
func (tun *nativeTun) Close() error {
func (tun *NativeTun) Close() error {
var err1 error
if tun.statusListenersShutdown != nil {
close(tun.statusListenersShutdown)
@@ -357,41 +356,20 @@ func (tun *nativeTun) Close() error {
} else if tun.events != nil {
close(tun.events)
}
err2 := tun.fd.Close()
err3 := tun.fdCancel.Cancel()
err2 := tun.tunFile.Close()
if err1 != nil {
return err1
}
if err2 != nil {
return err2
}
return err3
}
func CreateTUN(name string, mtu int) (TUNDevice, error) {
// open clone device
// HACK: we open it as a raw Fd first, so that f.nonblock=false
// when we make it into a file object.
func CreateTUN(name string, mtu int) (Device, error) {
nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0)
if err != nil {
return nil, err
}
err = unix.SetNonblock(nfd, true)
if err != nil {
return nil, err
}
fd := os.NewFile(uintptr(nfd), cloneDevicePath)
if err != nil {
return nil, err
}
// create new device
var ifr [ifReqSize]byte
var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI (disabled for TUN status hack)
nameBytes := []byte(name)
@@ -403,36 +381,37 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
fd.Fd(),
uintptr(nfd),
uintptr(unix.TUNSETIFF),
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return nil, errno
}
err = unix.SetNonblock(nfd, true)
// Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line.
fd := os.NewFile(uintptr(nfd), cloneDevicePath)
if err != nil {
return nil, err
}
return CreateTUNFromFile(fd, mtu)
}
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
tun := &nativeTun{
fd: file,
events: make(chan TUNEvent, 5),
func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
tun := &NativeTun{
tunFile: file,
events: make(chan Event, 5),
errors: make(chan error, 5),
statusListenersShutdown: make(chan struct{}),
nopi: false,
}
var err error
tun.fdCancel, err = rwcancel.NewRWCancel(int(file.Fd()))
if err != nil {
tun.fd.Close()
return nil, err
}
_, err = tun.Name()
if err != nil {
tun.fd.Close()
return nil, err
}
@@ -445,12 +424,11 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
tun.netlinkSock, err = createNetlinkSocket()
if err != nil {
tun.fd.Close()
return nil, err
}
tun.netlinkCancel, err = rwcancel.NewRWCancel(tun.netlinkSock)
if err != nil {
tun.fd.Close()
unix.Close(tun.netlinkSock)
return nil, err
}
@@ -460,9 +438,28 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
err = tun.setMTU(mtu)
if err != nil {
tun.Close()
unix.Close(tun.netlinkSock)
return nil, err
}
return tun, nil
}
func CreateUnmonitoredTUNFromFD(fd int) (Device, string, error) {
err := unix.SetNonblock(fd, true)
if err != nil {
return nil, "", err
}
file := os.NewFile(uintptr(fd), "/dev/tun")
tun := &NativeTun{
tunFile: file,
events: make(chan Event, 5),
errors: make(chan error, 5),
nopi: true,
}
name, err := tun.Name()
if err != nil {
return nil, "", err
}
return tun, name, nil
}

View File

@@ -1,21 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
*/
package tun
import (
"git.zx2c4.com/wireguard-go/rwcancel"
"errors"
"fmt"
"golang.org/x/net/ipv6"
"golang.org/x/sys/unix"
"io/ioutil"
"net"
"os"
"syscall"
"unsafe"
"golang.org/x/net/ipv6"
"golang.org/x/sys/unix"
)
// Structure for iface mtu get/set ioctls
@@ -27,16 +26,15 @@ type ifreq_mtu struct {
const _TUNSIFMODE = 0x8004745d
type nativeTun struct {
type NativeTun struct {
name string
fd *os.File
rwcancel *rwcancel.RWCancel
events chan TUNEvent
tunFile *os.File
events chan Event
errors chan error
routeSocket int
}
func (tun *nativeTun) routineRouteListener(tunIfindex int) {
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
var (
statusUp bool
statusMTU int
@@ -44,10 +42,42 @@ func (tun *nativeTun) routineRouteListener(tunIfindex int) {
defer close(tun.events)
check := func() bool {
iface, err := net.InterfaceByIndex(tunIfindex)
if err != nil {
tun.errors <- err
return true
}
// Up / Down event
up := (iface.Flags & net.FlagUp) != 0
if up != statusUp && up {
tun.events <- EventUp
}
if up != statusUp && !up {
tun.events <- EventDown
}
statusUp = up
// MTU changes
if iface.MTU != statusMTU {
tun.events <- EventMTUUpdate
}
statusMTU = iface.MTU
return false
}
if check() {
return
}
data := make([]byte, os.Getpagesize())
for {
n, err := unix.Read(tun.routeSocket, data)
if err != nil {
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
continue
}
tun.errors <- err
return
}
@@ -63,36 +93,15 @@ func (tun *nativeTun) routineRouteListener(tunIfindex int) {
if ifindex != tunIfindex {
continue
}
iface, err := net.InterfaceByIndex(ifindex)
if err != nil {
tun.errors <- err
if check() {
return
}
// Up / Down event
up := (iface.Flags & net.FlagUp) != 0
if up != statusUp && up {
tun.events <- TUNEventUp
}
if up != statusUp && !up {
tun.events <- TUNEventDown
}
statusUp = up
// MTU changes
if iface.MTU != statusMTU {
tun.events <- TUNEventMTUUpdate
}
statusMTU = iface.MTU
}
}
func errorIsEBUSY(err error) bool {
if pe, ok := err.(*os.PathError); ok {
if errno, ok := pe.Err.(syscall.Errno); ok && errno == syscall.EBUSY {
return true
}
err = pe.Err
}
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EBUSY {
return true
@@ -100,7 +109,7 @@ func errorIsEBUSY(err error) bool {
return false
}
func CreateTUN(name string, mtu int) (TUNDevice, error) {
func CreateTUN(name string, mtu int) (Device, error) {
ifIndex := -1
if name != "tun" {
_, err := fmt.Sscanf(name, "tun%d", &ifIndex)
@@ -127,42 +136,28 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
return nil, err
}
// Set TUN iface to broadcast mode
ifmodemode := unix.IFF_BROADCAST
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
uintptr(tunfile.Fd()),
uintptr(_TUNSIFMODE),
uintptr(unsafe.Pointer(&ifmodemode)),
)
if errno != 0 {
return nil, fmt.Errorf("error %s", errno.Error())
}
tun, err := CreateTUNFromFile(tunfile, mtu)
if err == nil && name == "tun" {
fname := os.Getenv("WG_TUN_NAME_FILE")
if fname != "" {
ioutil.WriteFile(fname, []byte(tun.(*nativeTun).name+"\n"), 0400)
ioutil.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400)
}
}
return tun, err
}
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
tun := &nativeTun{
fd: file,
events: make(chan TUNEvent, 10),
func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
tun := &NativeTun{
tunFile: file,
events: make(chan Event, 10),
errors: make(chan error, 1),
}
name, err := tun.Name()
if err != nil {
tun.fd.Close()
tun.tunFile.Close()
return nil, err
}
@@ -174,35 +169,32 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
return iface.Index, nil
}()
if err != nil {
tun.fd.Close()
return nil, err
}
tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd()))
if err != nil {
tun.fd.Close()
tun.tunFile.Close()
return nil, err
}
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
if err != nil {
tun.fd.Close()
tun.tunFile.Close()
return nil, err
}
go tun.routineRouteListener(tunIfindex)
currentMTU, err := tun.MTU()
if err != nil || currentMTU != mtu {
err = tun.setMTU(mtu)
if err != nil {
tun.Close()
return nil, err
}
}
return tun, nil
}
func (tun *nativeTun) Name() (string, error) {
gostat, err := tun.fd.Stat()
func (tun *NativeTun) Name() (string, error) {
gostat, err := tun.tunFile.Stat()
if err != nil {
tun.name = ""
return "", err
@@ -212,21 +204,21 @@ func (tun *nativeTun) Name() (string, error) {
return tun.name, nil
}
func (tun *nativeTun) File() *os.File {
return tun.fd
func (tun *NativeTun) File() *os.File {
return tun.tunFile
}
func (tun *nativeTun) Events() chan TUNEvent {
func (tun *NativeTun) Events() chan Event {
return tun.events
}
func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
select {
case err := <-tun.errors:
return 0, err
default:
buff := buff[offset-4:]
n, err := tun.fd.Read(buff[:])
n, err := tun.tunFile.Read(buff[:])
if n < 4 {
return 0, err
}
@@ -234,19 +226,7 @@ func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
}
}
func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
for {
n, err := tun.doRead(buff, offset)
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
return n, err
}
if !tun.rwcancel.ReadyRead() {
return 0, errors.New("tun device closed")
}
}
}
func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
// reserve space for header
@@ -266,16 +246,20 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
// write
return tun.fd.Write(buff)
return tun.tunFile.Write(buff)
}
func (tun *nativeTun) Close() error {
var err3 error
err1 := tun.rwcancel.Cancel()
err2 := tun.fd.Close()
func (tun *NativeTun) Flush() error {
// TODO: can flushing be implemented by buffering and using sendmmsg?
return nil
}
func (tun *NativeTun) Close() error {
var err2 error
err1 := tun.tunFile.Close()
if tun.routeSocket != -1 {
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
err3 = unix.Close(tun.routeSocket)
err2 = unix.Close(tun.routeSocket)
tun.routeSocket = -1
} else if tun.events != nil {
close(tun.events)
@@ -283,13 +267,10 @@ func (tun *nativeTun) Close() error {
if err1 != nil {
return err1
}
if err2 != nil {
return err2
}
return err3
}
func (tun *nativeTun) setMTU(n int) error {
func (tun *NativeTun) setMTU(n int) error {
// open datagram socket
var fd int
@@ -326,7 +307,7 @@ func (tun *nativeTun) setMTU(n int) error {
return nil
}
func (tun *nativeTun) MTU() (int, error) {
func (tun *NativeTun) MTU() (int, error) {
// open datagram socket
fd, err := unix.Socket(

260
tun/tun_windows.go Normal file
View File

@@ -0,0 +1,260 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2018-2019 WireGuard LLC. All Rights Reserved.
*/
package tun
import (
"errors"
"fmt"
"os"
"sync/atomic"
"time"
"unsafe"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/tun/wintun"
)
const (
rateMeasurementGranularity = uint64((time.Second / 2) / time.Nanosecond)
spinloopRateThreshold = 800000000 / 8 // 800mbps
spinloopDuration = uint64(time.Millisecond / 80 / time.Nanosecond) // ~1gbit/s
)
type rateJuggler struct {
current uint64
nextByteCount uint64
nextStartTime int64
changing int32
}
type NativeTun struct {
wt *wintun.Interface
handle windows.Handle
close bool
rings wintun.RingDescriptor
events chan Event
errors chan error
forcedMTU int
rate rateJuggler
}
const WintunPool = wintun.Pool("WireGuard")
//go:linkname procyield runtime.procyield
func procyield(cycles uint32)
//go:linkname nanotime runtime.nanotime
func nanotime() int64
//
// CreateTUN creates a Wintun interface with the given name. Should a Wintun
// interface with the same name exist, it is reused.
//
func CreateTUN(ifname string) (Device, error) {
return CreateTUNWithRequestedGUID(ifname, nil)
}
//
// CreateTUNWithRequestedGUID creates a Wintun interface with the given name and
// a requested GUID. Should a Wintun interface with the same name exist, it is reused.
//
func CreateTUNWithRequestedGUID(ifname string, requestedGUID *windows.GUID) (Device, error) {
var err error
var wt *wintun.Interface
// Does an interface with this name already exist?
wt, err = WintunPool.GetInterface(ifname)
if err == nil {
// If so, we delete it, in case it has weird residual configuration.
_, err = wt.DeleteInterface()
if err != nil {
return nil, fmt.Errorf("Unable to delete already existing Wintun interface: %v", err)
}
}
wt, _, err = WintunPool.CreateInterface(ifname, requestedGUID)
if err != nil {
return nil, fmt.Errorf("Unable to create Wintun interface: %v", err)
}
tun := &NativeTun{
wt: wt,
handle: windows.InvalidHandle,
events: make(chan Event, 10),
errors: make(chan error, 1),
forcedMTU: 1500,
}
err = tun.rings.Init()
if err != nil {
tun.Close()
return nil, fmt.Errorf("Error creating events: %v", err)
}
tun.handle, err = tun.wt.Register(&tun.rings)
if err != nil {
tun.Close()
return nil, fmt.Errorf("Error registering rings: %v", err)
}
return tun, nil
}
func (tun *NativeTun) Name() (string, error) {
return tun.wt.Name()
}
func (tun *NativeTun) File() *os.File {
return nil
}
func (tun *NativeTun) Events() chan Event {
return tun.events
}
func (tun *NativeTun) Close() error {
tun.close = true
if tun.rings.Send.TailMoved != 0 {
windows.SetEvent(tun.rings.Send.TailMoved) // wake the reader if it's sleeping
}
if tun.handle != windows.InvalidHandle {
windows.CloseHandle(tun.handle)
}
tun.rings.Close()
var err error
if tun.wt != nil {
_, err = tun.wt.DeleteInterface()
}
close(tun.events)
return err
}
func (tun *NativeTun) MTU() (int, error) {
return tun.forcedMTU, nil
}
// TODO: This is a temporary hack. We really need to be monitoring the interface in real time and adapting to MTU changes.
func (tun *NativeTun) ForceMTU(mtu int) {
tun.forcedMTU = mtu
}
// Note: Read() and Write() assume the caller comes only from a single thread; there's no locking.
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
retry:
select {
case err := <-tun.errors:
return 0, err
default:
}
if tun.close {
return 0, os.ErrClosed
}
buffHead := atomic.LoadUint32(&tun.rings.Send.Ring.Head)
if buffHead >= wintun.PacketCapacity {
return 0, os.ErrClosed
}
start := nanotime()
shouldSpin := atomic.LoadUint64(&tun.rate.current) >= spinloopRateThreshold && uint64(start-atomic.LoadInt64(&tun.rate.nextStartTime)) <= rateMeasurementGranularity*2
var buffTail uint32
for {
buffTail = atomic.LoadUint32(&tun.rings.Send.Ring.Tail)
if buffHead != buffTail {
break
}
if tun.close {
return 0, os.ErrClosed
}
if !shouldSpin || uint64(nanotime()-start) >= spinloopDuration {
windows.WaitForSingleObject(tun.rings.Send.TailMoved, windows.INFINITE)
goto retry
}
procyield(1)
}
if buffTail >= wintun.PacketCapacity {
return 0, os.ErrClosed
}
buffContent := tun.rings.Send.Ring.Wrap(buffTail - buffHead)
if buffContent < uint32(unsafe.Sizeof(wintun.PacketHeader{})) {
return 0, errors.New("incomplete packet header in send ring")
}
packet := (*wintun.Packet)(unsafe.Pointer(&tun.rings.Send.Ring.Data[buffHead]))
if packet.Size > wintun.PacketSizeMax {
return 0, errors.New("packet too big in send ring")
}
alignedPacketSize := wintun.PacketAlign(uint32(unsafe.Sizeof(wintun.PacketHeader{})) + packet.Size)
if alignedPacketSize > buffContent {
return 0, errors.New("incomplete packet in send ring")
}
copy(buff[offset:], packet.Data[:packet.Size])
buffHead = tun.rings.Send.Ring.Wrap(buffHead + alignedPacketSize)
atomic.StoreUint32(&tun.rings.Send.Ring.Head, buffHead)
tun.rate.update(uint64(packet.Size))
return int(packet.Size), nil
}
func (tun *NativeTun) Flush() error {
return nil
}
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
if tun.close {
return 0, os.ErrClosed
}
packetSize := uint32(len(buff) - offset)
tun.rate.update(uint64(packetSize))
alignedPacketSize := wintun.PacketAlign(uint32(unsafe.Sizeof(wintun.PacketHeader{})) + packetSize)
buffHead := atomic.LoadUint32(&tun.rings.Receive.Ring.Head)
if buffHead >= wintun.PacketCapacity {
return 0, os.ErrClosed
}
buffTail := atomic.LoadUint32(&tun.rings.Receive.Ring.Tail)
if buffTail >= wintun.PacketCapacity {
return 0, os.ErrClosed
}
buffSpace := tun.rings.Receive.Ring.Wrap(buffHead - buffTail - wintun.PacketAlignment)
if alignedPacketSize > buffSpace {
return 0, nil // Dropping when ring is full.
}
packet := (*wintun.Packet)(unsafe.Pointer(&tun.rings.Receive.Ring.Data[buffTail]))
packet.Size = packetSize
copy(packet.Data[:packetSize], buff[offset:])
atomic.StoreUint32(&tun.rings.Receive.Ring.Tail, tun.rings.Receive.Ring.Wrap(buffTail+alignedPacketSize))
if atomic.LoadInt32(&tun.rings.Receive.Ring.Alertable) != 0 {
windows.SetEvent(tun.rings.Receive.TailMoved)
}
return int(packetSize), nil
}
// LUID returns Windows interface instance ID.
func (tun *NativeTun) LUID() uint64 {
return tun.wt.LUID()
}
func (rate *rateJuggler) update(packetLen uint64) {
now := nanotime()
total := atomic.AddUint64(&rate.nextByteCount, packetLen)
period := uint64(now - atomic.LoadInt64(&rate.nextStartTime))
if period >= rateMeasurementGranularity {
if !atomic.CompareAndSwapInt32(&rate.changing, 0, 1) {
return
}
atomic.StoreInt64(&rate.nextStartTime, now)
atomic.StoreUint64(&rate.current, total*uint64(time.Second/time.Nanosecond)/period)
atomic.StoreUint64(&rate.nextByteCount, 0)
atomic.StoreInt32(&rate.changing, 0)
}
}

View File

@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package iphlpapi
import "golang.org/x/sys/windows"
//sys convertInterfaceLUIDToGUID(interfaceLUID *uint64, interfaceGUID *windows.GUID) (ret error) = iphlpapi.ConvertInterfaceLuidToGuid
//sys convertInterfaceAliasToLUID(interfaceAlias *uint16, interfaceLUID *uint64) (ret error) = iphlpapi.ConvertInterfaceAliasToLuid
func InterfaceGUIDFromAlias(alias string) (*windows.GUID, error) {
var luid uint64
var guid windows.GUID
err := convertInterfaceAliasToLUID(windows.StringToUTF16Ptr(alias), &luid)
if err != nil {
return nil, err
}
err = convertInterfaceLUIDToGUID(&luid, &guid)
if err != nil {
return nil, err
}
return &guid, nil
}

View File

@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package iphlpapi
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go conversion_windows.go

View File

@@ -0,0 +1,60 @@
// Code generated by 'go generate'; DO NOT EDIT.
package iphlpapi
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return nil
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
procConvertInterfaceLuidToGuid = modiphlpapi.NewProc("ConvertInterfaceLuidToGuid")
procConvertInterfaceAliasToLuid = modiphlpapi.NewProc("ConvertInterfaceAliasToLuid")
)
func convertInterfaceLUIDToGUID(interfaceLUID *uint64, interfaceGUID *windows.GUID) (ret error) {
r0, _, _ := syscall.Syscall(procConvertInterfaceLuidToGuid.Addr(), 2, uintptr(unsafe.Pointer(interfaceLUID)), uintptr(unsafe.Pointer(interfaceGUID)), 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}
func convertInterfaceAliasToLUID(interfaceAlias *uint16, interfaceLUID *uint64) (ret error) {
r0, _, _ := syscall.Syscall(procConvertInterfaceAliasToLuid.Addr(), 2, uintptr(unsafe.Pointer(interfaceAlias)), uintptr(unsafe.Pointer(interfaceLUID)), 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}

View File

@@ -0,0 +1,99 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package wintun
import (
"encoding/hex"
"errors"
"fmt"
"sync"
"unsafe"
"golang.org/x/crypto/blake2s"
"golang.org/x/sys/windows"
"golang.org/x/text/unicode/norm"
"golang.zx2c4.com/wireguard/ipc/winpipe"
"golang.zx2c4.com/wireguard/tun/wintun/namespaceapi"
)
var (
wintunObjectSecurityAttributes *windows.SecurityAttributes
hasInitializedNamespace bool
initializingNamespace sync.Mutex
)
func initializeNamespace() error {
initializingNamespace.Lock()
defer initializingNamespace.Unlock()
if hasInitializedNamespace {
return nil
}
sd, err := winpipe.SddlToSecurityDescriptor("O:SYD:P(A;;GA;;;SY)")
if err != nil {
return fmt.Errorf("SddlToSecurityDescriptor failed: %v", err)
}
wintunObjectSecurityAttributes = &windows.SecurityAttributes{
Length: uint32(len(sd)),
SecurityDescriptor: uintptr(unsafe.Pointer(&sd[0])),
}
sid, err := windows.CreateWellKnownSid(windows.WinLocalSystemSid)
if err != nil {
return fmt.Errorf("CreateWellKnownSid(LOCAL_SYSTEM) failed: %v", err)
}
boundary, err := namespaceapi.CreateBoundaryDescriptor("Wintun")
if err != nil {
return fmt.Errorf("CreateBoundaryDescriptor failed: %v", err)
}
err = boundary.AddSid(sid)
if err != nil {
return fmt.Errorf("AddSIDToBoundaryDescriptor failed: %v", err)
}
for {
_, err = namespaceapi.CreatePrivateNamespace(wintunObjectSecurityAttributes, boundary, "Wintun")
if err == windows.ERROR_ALREADY_EXISTS {
_, err = namespaceapi.OpenPrivateNamespace(boundary, "Wintun")
if err == windows.ERROR_PATH_NOT_FOUND {
continue
}
}
if err != nil {
return fmt.Errorf("Create/OpenPrivateNamespace failed: %v", err)
}
break
}
hasInitializedNamespace = true
return nil
}
func (pool Pool) takeNameMutex() (windows.Handle, error) {
err := initializeNamespace()
if err != nil {
return 0, err
}
const mutexLabel = "WireGuard Adapter Name Mutex Stable Suffix v1 jason@zx2c4.com"
b2, _ := blake2s.New256(nil)
b2.Write([]byte(mutexLabel))
b2.Write(norm.NFC.Bytes([]byte(string(pool))))
mutexName := `Wintun\Wintun-Name-Mutex-` + hex.EncodeToString(b2.Sum(nil))
mutex, err := windows.CreateMutex(wintunObjectSecurityAttributes, false, windows.StringToUTF16Ptr(mutexName))
if err != nil {
err = fmt.Errorf("Error creating name mutex: %v", err)
return 0, err
}
event, err := windows.WaitForSingleObject(mutex, windows.INFINITE)
if err != nil {
windows.CloseHandle(mutex)
return 0, fmt.Errorf("Error waiting on name mutex: %v", err)
}
if event != windows.WAIT_OBJECT_0 && event != windows.WAIT_ABANDONED {
windows.CloseHandle(mutex)
return 0, errors.New("Error with event trigger of name mutex")
}
return mutex, nil
}

View File

@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package namespaceapi
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go namespaceapi_windows.go

View File

@@ -0,0 +1,83 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package namespaceapi
import "golang.org/x/sys/windows"
//sys createBoundaryDescriptor(name *uint16, flags uint32) (handle windows.Handle, err error) = kernel32.CreateBoundaryDescriptorW
//sys deleteBoundaryDescriptor(boundaryDescriptor windows.Handle) = kernel32.DeleteBoundaryDescriptor
//sys addSIDToBoundaryDescriptor(boundaryDescriptor *windows.Handle, requiredSid *windows.SID) (err error) = kernel32.AddSIDToBoundaryDescriptor
//sys createPrivateNamespace(privateNamespaceAttributes *windows.SecurityAttributes, boundaryDescriptor windows.Handle, aliasPrefix *uint16) (handle windows.Handle, err error) = kernel32.CreatePrivateNamespaceW
//sys openPrivateNamespace(boundaryDescriptor windows.Handle, aliasPrefix *uint16) (handle windows.Handle, err error) = kernel32.OpenPrivateNamespaceW
//sys closePrivateNamespace(handle windows.Handle, flags uint32) (err error) = kernel32.ClosePrivateNamespace
// BoundaryDescriptor represents a boundary that defines how the objects in the namespace are to be isolated.
type BoundaryDescriptor windows.Handle
// CreateBoundaryDescriptor creates a boundary descriptor.
func CreateBoundaryDescriptor(name string) (BoundaryDescriptor, error) {
name16, err := windows.UTF16PtrFromString(name)
if err != nil {
return 0, err
}
handle, err := createBoundaryDescriptor(name16, 0)
if err != nil {
return 0, err
}
return BoundaryDescriptor(handle), nil
}
// Delete deletes the specified boundary descriptor.
func (bd BoundaryDescriptor) Delete() {
deleteBoundaryDescriptor(windows.Handle(bd))
}
// AddSid adds a security identifier (SID) to the specified boundary descriptor.
func (bd *BoundaryDescriptor) AddSid(requiredSid *windows.SID) error {
return addSIDToBoundaryDescriptor((*windows.Handle)(bd), requiredSid)
}
// PrivateNamespace represents a private namespace. Duh?!
type PrivateNamespace windows.Handle
// CreatePrivateNamespace creates a private namespace.
func CreatePrivateNamespace(privateNamespaceAttributes *windows.SecurityAttributes, boundaryDescriptor BoundaryDescriptor, aliasPrefix string) (PrivateNamespace, error) {
aliasPrefix16, err := windows.UTF16PtrFromString(aliasPrefix)
if err != nil {
return 0, err
}
handle, err := createPrivateNamespace(privateNamespaceAttributes, windows.Handle(boundaryDescriptor), aliasPrefix16)
if err != nil {
return 0, err
}
return PrivateNamespace(handle), nil
}
// OpenPrivateNamespace opens a private namespace.
func OpenPrivateNamespace(boundaryDescriptor BoundaryDescriptor, aliasPrefix string) (PrivateNamespace, error) {
aliasPrefix16, err := windows.UTF16PtrFromString(aliasPrefix)
if err != nil {
return 0, err
}
handle, err := openPrivateNamespace(windows.Handle(boundaryDescriptor), aliasPrefix16)
if err != nil {
return 0, err
}
return PrivateNamespace(handle), nil
}
// ClosePrivateNamespaceFlags describes flags that are used by PrivateNamespace's Close() method.
type ClosePrivateNamespaceFlags uint32
const (
// PrivateNamespaceFlagDestroy makes the close to destroy the namespace.
PrivateNamespaceFlagDestroy = ClosePrivateNamespaceFlags(0x1)
)
// Close closes an open namespace handle.
func (pns PrivateNamespace) Close(flags ClosePrivateNamespaceFlags) error {
return closePrivateNamespace(windows.Handle(pns), uint32(flags))
}

View File

@@ -0,0 +1,116 @@
// Code generated by 'go generate'; DO NOT EDIT.
package namespaceapi
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return nil
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procCreateBoundaryDescriptorW = modkernel32.NewProc("CreateBoundaryDescriptorW")
procDeleteBoundaryDescriptor = modkernel32.NewProc("DeleteBoundaryDescriptor")
procAddSIDToBoundaryDescriptor = modkernel32.NewProc("AddSIDToBoundaryDescriptor")
procCreatePrivateNamespaceW = modkernel32.NewProc("CreatePrivateNamespaceW")
procOpenPrivateNamespaceW = modkernel32.NewProc("OpenPrivateNamespaceW")
procClosePrivateNamespace = modkernel32.NewProc("ClosePrivateNamespace")
)
func createBoundaryDescriptor(name *uint16, flags uint32) (handle windows.Handle, err error) {
r0, _, e1 := syscall.Syscall(procCreateBoundaryDescriptorW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(flags), 0)
handle = windows.Handle(r0)
if handle == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func deleteBoundaryDescriptor(boundaryDescriptor windows.Handle) {
syscall.Syscall(procDeleteBoundaryDescriptor.Addr(), 1, uintptr(boundaryDescriptor), 0, 0)
return
}
func addSIDToBoundaryDescriptor(boundaryDescriptor *windows.Handle, requiredSid *windows.SID) (err error) {
r1, _, e1 := syscall.Syscall(procAddSIDToBoundaryDescriptor.Addr(), 2, uintptr(unsafe.Pointer(boundaryDescriptor)), uintptr(unsafe.Pointer(requiredSid)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func createPrivateNamespace(privateNamespaceAttributes *windows.SecurityAttributes, boundaryDescriptor windows.Handle, aliasPrefix *uint16) (handle windows.Handle, err error) {
r0, _, e1 := syscall.Syscall(procCreatePrivateNamespaceW.Addr(), 3, uintptr(unsafe.Pointer(privateNamespaceAttributes)), uintptr(boundaryDescriptor), uintptr(unsafe.Pointer(aliasPrefix)))
handle = windows.Handle(r0)
if handle == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func openPrivateNamespace(boundaryDescriptor windows.Handle, aliasPrefix *uint16) (handle windows.Handle, err error) {
r0, _, e1 := syscall.Syscall(procOpenPrivateNamespaceW.Addr(), 2, uintptr(boundaryDescriptor), uintptr(unsafe.Pointer(aliasPrefix)), 0)
handle = windows.Handle(r0)
if handle == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func closePrivateNamespace(handle windows.Handle, flags uint32) (err error) {
r1, _, e1 := syscall.Syscall(procClosePrivateNamespace.Addr(), 2, uintptr(handle), uintptr(flags), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}

View File

@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package nci
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go nci_windows.go

View File

@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package nci
import "golang.org/x/sys/windows"
//sys nciSetConnectionName(guid *windows.GUID, newName *uint16) (ret error) = nci.NciSetConnectionName
//sys nciGetConnectionName(guid *windows.GUID, destName *uint16, inDestNameBytes uint32, outDestNameBytes *uint32) (ret error) = nci.NciGetConnectionName
func SetConnectionName(guid *windows.GUID, newName string) error {
newName16, err := windows.UTF16PtrFromString(newName)
if err != nil {
return err
}
return nciSetConnectionName(guid, newName16)
}
func ConnectionName(guid *windows.GUID) (string, error) {
var name [0x400]uint16
err := nciGetConnectionName(guid, &name[0], uint32(len(name)*2), nil)
if err != nil {
return "", err
}
return windows.UTF16ToString(name[:]), nil
}

View File

@@ -0,0 +1,60 @@
// Code generated by 'go generate'; DO NOT EDIT.
package nci
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return nil
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modnci = windows.NewLazySystemDLL("nci.dll")
procNciSetConnectionName = modnci.NewProc("NciSetConnectionName")
procNciGetConnectionName = modnci.NewProc("NciGetConnectionName")
)
func nciSetConnectionName(guid *windows.GUID, newName *uint16) (ret error) {
r0, _, _ := syscall.Syscall(procNciSetConnectionName.Addr(), 2, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(newName)), 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}
func nciGetConnectionName(guid *windows.GUID, destName *uint16, inDestNameBytes uint32, outDestNameBytes *uint32) (ret error) {
r0, _, _ := syscall.Syscall6(procNciGetConnectionName.Addr(), 4, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(destName)), uintptr(inDestNameBytes), uintptr(unsafe.Pointer(outDestNameBytes)), 0, 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}

View File

@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package registry
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zregistry_windows.go registry_windows.go

View File

@@ -0,0 +1,272 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package registry
import (
"errors"
"fmt"
"runtime"
"strings"
"time"
"unsafe"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
const (
// REG_NOTIFY_CHANGE_NAME notifies the caller if a subkey is added or deleted.
REG_NOTIFY_CHANGE_NAME uint32 = 0x00000001
// REG_NOTIFY_CHANGE_ATTRIBUTES notifies the caller of changes to the attributes of the key, such as the security descriptor information.
REG_NOTIFY_CHANGE_ATTRIBUTES uint32 = 0x00000002
// REG_NOTIFY_CHANGE_LAST_SET notifies the caller of changes to a value of the key. This can include adding or deleting a value, or changing an existing value.
REG_NOTIFY_CHANGE_LAST_SET uint32 = 0x00000004
// REG_NOTIFY_CHANGE_SECURITY notifies the caller of changes to the security descriptor of the key.
REG_NOTIFY_CHANGE_SECURITY uint32 = 0x00000008
// REG_NOTIFY_THREAD_AGNOSTIC indicates that the lifetime of the registration must not be tied to the lifetime of the thread issuing the RegNotifyChangeKeyValue call. Note: This flag value is only supported in Windows 8 and later.
REG_NOTIFY_THREAD_AGNOSTIC uint32 = 0x10000000
)
//sys regNotifyChangeKeyValue(key windows.Handle, watchSubtree bool, notifyFilter uint32, event windows.Handle, asynchronous bool) (regerrno error) = advapi32.RegNotifyChangeKeyValue
func OpenKeyWait(k registry.Key, path string, access uint32, timeout time.Duration) (registry.Key, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
deadline := time.Now().Add(timeout)
pathSpl := strings.Split(path, "\\")
for i := 0; ; i++ {
keyName := pathSpl[i]
isLast := i+1 == len(pathSpl)
event, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return 0, fmt.Errorf("Error creating event: %v", err)
}
defer windows.CloseHandle(event)
var key registry.Key
for {
err = regNotifyChangeKeyValue(windows.Handle(k), false, REG_NOTIFY_CHANGE_NAME, windows.Handle(event), true)
if err != nil {
return 0, fmt.Errorf("Setting up change notification on registry key failed: %v", err)
}
var accessFlags uint32
if isLast {
accessFlags = access
} else {
accessFlags = registry.NOTIFY
}
key, err = registry.OpenKey(k, keyName, accessFlags)
if err == windows.ERROR_FILE_NOT_FOUND || err == windows.ERROR_PATH_NOT_FOUND {
timeout := time.Until(deadline) / time.Millisecond
if timeout < 0 {
timeout = 0
}
s, err := windows.WaitForSingleObject(event, uint32(timeout))
if err != nil {
return 0, fmt.Errorf("Unable to wait on registry key: %v", err)
}
if s == uint32(windows.WAIT_TIMEOUT) { // windows.WAIT_TIMEOUT status const is misclassified as error in golang.org/x/sys/windows
return 0, errors.New("Timeout waiting for registry key")
}
} else if err != nil {
return 0, fmt.Errorf("Error opening registry key %v: %v", path, err)
} else {
if isLast {
return key, nil
}
defer key.Close()
break
}
}
k = key
}
}
func WaitForKey(k registry.Key, path string, timeout time.Duration) error {
key, err := OpenKeyWait(k, path, registry.NOTIFY, timeout)
if err != nil {
return err
}
key.Close()
return nil
}
//
// getValue is more or less the same as windows/registry's getValue.
//
func getValue(k registry.Key, name string, buf []byte) (value []byte, valueType uint32, err error) {
var name16 *uint16
name16, err = windows.UTF16PtrFromString(name)
if err != nil {
return
}
n := uint32(len(buf))
for {
err = windows.RegQueryValueEx(windows.Handle(k), name16, nil, &valueType, (*byte)(unsafe.Pointer(&buf[0])), &n)
if err == nil {
value = buf[:n]
return
}
if err != windows.ERROR_MORE_DATA {
return
}
if n <= uint32(len(buf)) {
return
}
buf = make([]byte, n)
}
}
//
// getValueRetry function reads any value from registry. It waits for
// the registry value to become available or returns error on timeout.
//
// Key must be opened with at least QUERY_VALUE|NOTIFY access.
//
func getValueRetry(key registry.Key, name string, buf []byte, timeout time.Duration) ([]byte, uint32, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
event, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return nil, 0, fmt.Errorf("Error creating event: %v", err)
}
defer windows.CloseHandle(event)
deadline := time.Now().Add(timeout)
for {
err := regNotifyChangeKeyValue(windows.Handle(key), false, REG_NOTIFY_CHANGE_LAST_SET, windows.Handle(event), true)
if err != nil {
return nil, 0, fmt.Errorf("Setting up change notification on registry value failed: %v", err)
}
buf, valueType, err := getValue(key, name, buf)
if err == windows.ERROR_FILE_NOT_FOUND || err == windows.ERROR_PATH_NOT_FOUND {
timeout := time.Until(deadline) / time.Millisecond
if timeout < 0 {
timeout = 0
}
s, err := windows.WaitForSingleObject(event, uint32(timeout))
if err != nil {
return nil, 0, fmt.Errorf("Unable to wait on registry value: %v", err)
}
if s == uint32(windows.WAIT_TIMEOUT) { // windows.WAIT_TIMEOUT status const is misclassified as error in golang.org/x/sys/windows
return nil, 0, errors.New("Timeout waiting for registry value")
}
} else if err != nil {
return nil, 0, fmt.Errorf("Error reading registry value %v: %v", name, err)
} else {
return buf, valueType, nil
}
}
}
func toString(buf []byte, valueType uint32, err error) (string, error) {
if err != nil {
return "", err
}
var value string
switch valueType {
case registry.SZ, registry.EXPAND_SZ, registry.MULTI_SZ:
if len(buf) == 0 {
return "", nil
}
value = windows.UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(&buf[0]))[:len(buf)/2])
default:
return "", registry.ErrUnexpectedType
}
if valueType != registry.EXPAND_SZ {
// Value does not require expansion.
return value, nil
}
valueExp, err := registry.ExpandString(value)
if err != nil {
// Expanding failed: return original sting value.
return value, nil
}
// Return expanded value.
return valueExp, nil
}
func toInteger(buf []byte, valueType uint32, err error) (uint64, error) {
if err != nil {
return 0, err
}
switch valueType {
case registry.DWORD:
if len(buf) != 4 {
return 0, errors.New("DWORD value is not 4 bytes long")
}
var val uint32
copy((*[4]byte)(unsafe.Pointer(&val))[:], buf)
return uint64(val), nil
case registry.QWORD:
if len(buf) != 8 {
return 0, errors.New("QWORD value is not 8 bytes long")
}
var val uint64
copy((*[8]byte)(unsafe.Pointer(&val))[:], buf)
return val, nil
default:
return 0, registry.ErrUnexpectedType
}
}
//
// GetStringValueWait function reads a string value from registry. It waits
// for the registry value to become available or returns error on timeout.
//
// Key must be opened with at least QUERY_VALUE|NOTIFY access.
//
// If the value type is REG_EXPAND_SZ the environment variables are expanded.
// Should expanding fail, original string value and nil error are returned.
//
// If the value type is REG_MULTI_SZ only the first string is returned.
//
func GetStringValueWait(key registry.Key, name string, timeout time.Duration) (string, error) {
return toString(getValueRetry(key, name, make([]byte, 256), timeout))
}
//
// GetStringValue function reads a string value from registry.
//
// Key must be opened with at least QUERY_VALUE access.
//
// If the value type is REG_EXPAND_SZ the environment variables are expanded.
// Should expanding fail, original string value and nil error are returned.
//
// If the value type is REG_MULTI_SZ only the first string is returned.
//
func GetStringValue(key registry.Key, name string) (string, error) {
return toString(getValue(key, name, make([]byte, 256)))
}
//
// GetIntegerValueWait function reads a DWORD32 or QWORD value from registry.
// It waits for the registry value to become available or returns error on
// timeout.
//
// Key must be opened with at least QUERY_VALUE|NOTIFY access.
//
func GetIntegerValueWait(key registry.Key, name string, timeout time.Duration) (uint64, error) {
return toInteger(getValueRetry(key, name, make([]byte, 8), timeout))
}

View File

@@ -0,0 +1,103 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package registry
import (
"testing"
"time"
"golang.org/x/sys/windows/registry"
)
const keyRoot = registry.CURRENT_USER
const pathRoot = "Software\\WireGuardRegistryTest"
const path = pathRoot + "\\foobar"
const pathFake = pathRoot + "\\raboof"
func Test_WaitForKey(t *testing.T) {
registry.DeleteKey(keyRoot, path)
registry.DeleteKey(keyRoot, pathRoot)
go func() {
time.Sleep(time.Second * 1)
key, _, err := registry.CreateKey(keyRoot, pathFake, registry.QUERY_VALUE)
if err != nil {
t.Errorf("Error creating registry key: %v", err)
}
key.Close()
registry.DeleteKey(keyRoot, pathFake)
key, _, err = registry.CreateKey(keyRoot, path, registry.QUERY_VALUE)
if err != nil {
t.Errorf("Error creating registry key: %v", err)
}
key.Close()
}()
err := WaitForKey(keyRoot, path, time.Second*2)
if err != nil {
t.Errorf("Error waiting for registry key: %v", err)
}
registry.DeleteKey(keyRoot, path)
registry.DeleteKey(keyRoot, pathRoot)
err = WaitForKey(keyRoot, path, time.Second*1)
if err == nil {
t.Error("Registry key notification expected to timeout but it succeeded.")
}
}
func Test_GetValueWait(t *testing.T) {
registry.DeleteKey(keyRoot, path)
registry.DeleteKey(keyRoot, pathRoot)
go func() {
time.Sleep(time.Second * 1)
key, _, err := registry.CreateKey(keyRoot, path, registry.SET_VALUE)
if err != nil {
t.Errorf("Error creating registry key: %v", err)
}
time.Sleep(time.Second * 1)
key.SetStringValue("name1", "eulav")
key.SetExpandStringValue("name2", "value")
time.Sleep(time.Second * 1)
key.SetDWordValue("name3", ^uint32(123))
key.SetDWordValue("name4", 123)
key.Close()
}()
key, err := OpenKeyWait(keyRoot, path, registry.QUERY_VALUE|registry.NOTIFY, time.Second*2)
if err != nil {
t.Errorf("Error waiting for registry key: %v", err)
}
valueStr, err := GetStringValueWait(key, "name2", time.Second*2)
if err != nil {
t.Errorf("Error waiting for registry value: %v", err)
}
if valueStr != "value" {
t.Errorf("Wrong value read: %v", valueStr)
}
_, err = GetStringValueWait(key, "nonexisting", time.Second*1)
if err == nil {
t.Error("Registry value notification expected to timeout but it succeeded.")
}
valueInt, err := GetIntegerValueWait(key, "name4", time.Second*2)
if err != nil {
t.Errorf("Error waiting for registry value: %v", err)
}
if valueInt != 123 {
t.Errorf("Wrong value read: %v", valueInt)
}
_, err = GetIntegerValueWait(key, "nonexisting", time.Second*1)
if err == nil {
t.Error("Registry value notification expected to timeout but it succeeded.")
}
key.Close()
registry.DeleteKey(keyRoot, path)
registry.DeleteKey(keyRoot, pathRoot)
}

View File

@@ -0,0 +1,63 @@
// Code generated by 'go generate'; DO NOT EDIT.
package registry
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return nil
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
procRegNotifyChangeKeyValue = modadvapi32.NewProc("RegNotifyChangeKeyValue")
)
func regNotifyChangeKeyValue(key windows.Handle, watchSubtree bool, notifyFilter uint32, event windows.Handle, asynchronous bool) (regerrno error) {
var _p0 uint32
if watchSubtree {
_p0 = 1
} else {
_p0 = 0
}
var _p1 uint32
if asynchronous {
_p1 = 1
} else {
_p1 = 0
}
r0, _, _ := syscall.Syscall6(procRegNotifyChangeKeyValue.Addr(), 5, uintptr(key), uintptr(_p0), uintptr(notifyFilter), uintptr(event), uintptr(_p1), 0)
if r0 != 0 {
regerrno = syscall.Errno(r0)
}
return
}

View File

@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package wintun
import (
"unsafe"
"golang.org/x/sys/windows"
)
const (
PacketAlignment = 4 // Number of bytes packets are aligned to in rings
PacketSizeMax = 0xffff // Maximum packet size
PacketCapacity = 0x800000 // Ring capacity, 8MiB
PacketTrailingSize = uint32(unsafe.Sizeof(PacketHeader{})) + ((PacketSizeMax + (PacketAlignment - 1)) &^ (PacketAlignment - 1)) - PacketAlignment
ioctlRegisterRings = (51820 << 16) | (0x970 << 2) | 0 /*METHOD_BUFFERED*/ | (0x3 /*FILE_READ_DATA | FILE_WRITE_DATA*/ << 14)
)
type PacketHeader struct {
Size uint32
}
type Packet struct {
PacketHeader
Data [PacketSizeMax]byte
}
type Ring struct {
Head uint32
Tail uint32
Alertable int32
Data [PacketCapacity + PacketTrailingSize]byte
}
type RingDescriptor struct {
Send, Receive struct {
Size uint32
Ring *Ring
TailMoved windows.Handle
}
}
// Wrap returns value modulo ring capacity
func (rb *Ring) Wrap(value uint32) uint32 {
return value & (PacketCapacity - 1)
}
// Aligns a packet size to PacketAlignment
func PacketAlign(size uint32) uint32 {
return (size + (PacketAlignment - 1)) &^ (PacketAlignment - 1)
}
func (descriptor *RingDescriptor) Init() (err error) {
descriptor.Send.Size = uint32(unsafe.Sizeof(Ring{}))
descriptor.Send.Ring = &Ring{}
descriptor.Send.TailMoved, err = windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return
}
descriptor.Receive.Size = uint32(unsafe.Sizeof(Ring{}))
descriptor.Receive.Ring = &Ring{}
descriptor.Receive.TailMoved, err = windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
windows.CloseHandle(descriptor.Send.TailMoved)
return
}
return
}
func (descriptor *RingDescriptor) Close() {
if descriptor.Send.TailMoved != 0 {
windows.CloseHandle(descriptor.Send.TailMoved)
descriptor.Send.TailMoved = 0
}
if descriptor.Send.TailMoved != 0 {
windows.CloseHandle(descriptor.Receive.TailMoved)
descriptor.Receive.TailMoved = 0
}
}
func (wintun *Interface) Register(descriptor *RingDescriptor) (windows.Handle, error) {
handle, err := wintun.handle()
if err != nil {
return 0, err
}
var bytesReturned uint32
err = windows.DeviceIoControl(handle, ioctlRegisterRings, (*byte)(unsafe.Pointer(descriptor)), uint32(unsafe.Sizeof(*descriptor)), nil, 0, &bytesReturned, nil)
if err != nil {
return 0, err
}
return handle, nil
}

View File

@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package setupapi
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsetupapi_windows.go setupapi_windows.go

View File

@@ -0,0 +1,506 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package setupapi
import (
"encoding/binary"
"fmt"
"runtime"
"unsafe"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
//sys setupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName *uint16, reserved uintptr) (handle DevInfo, err error) [failretval==DevInfo(windows.InvalidHandle)] = setupapi.SetupDiCreateDeviceInfoListExW
// SetupDiCreateDeviceInfoListEx function creates an empty device information set on a remote or a local computer and optionally associates the set with a device setup class.
func SetupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName string) (deviceInfoSet DevInfo, err error) {
var machineNameUTF16 *uint16
if machineName != "" {
machineNameUTF16, err = windows.UTF16PtrFromString(machineName)
if err != nil {
return
}
}
return setupDiCreateDeviceInfoListEx(classGUID, hwndParent, machineNameUTF16, 0)
}
//sys setupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo, deviceInfoSetDetailData *DevInfoListDetailData) (err error) = setupapi.SetupDiGetDeviceInfoListDetailW
// SetupDiGetDeviceInfoListDetail function retrieves information associated with a device information set including the class GUID, remote computer handle, and remote computer name.
func SetupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo) (deviceInfoSetDetailData *DevInfoListDetailData, err error) {
data := &DevInfoListDetailData{}
data.size = sizeofDevInfoListDetailData
return data, setupDiGetDeviceInfoListDetail(deviceInfoSet, data)
}
// DeviceInfoListDetail method retrieves information associated with a device information set including the class GUID, remote computer handle, and remote computer name.
func (deviceInfoSet DevInfo) DeviceInfoListDetail() (*DevInfoListDetailData, error) {
return SetupDiGetDeviceInfoListDetail(deviceInfoSet)
}
//sys setupDiCreateDeviceInfo(deviceInfoSet DevInfo, DeviceName *uint16, classGUID *windows.GUID, DeviceDescription *uint16, hwndParent uintptr, CreationFlags DICD, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiCreateDeviceInfoW
// SetupDiCreateDeviceInfo function creates a new device information element and adds it as a new member to the specified device information set.
func SetupDiCreateDeviceInfo(deviceInfoSet DevInfo, deviceName string, classGUID *windows.GUID, deviceDescription string, hwndParent uintptr, creationFlags DICD) (deviceInfoData *DevInfoData, err error) {
deviceNameUTF16, err := windows.UTF16PtrFromString(deviceName)
if err != nil {
return
}
var deviceDescriptionUTF16 *uint16
if deviceDescription != "" {
deviceDescriptionUTF16, err = windows.UTF16PtrFromString(deviceDescription)
if err != nil {
return
}
}
data := &DevInfoData{}
data.size = uint32(unsafe.Sizeof(*data))
return data, setupDiCreateDeviceInfo(deviceInfoSet, deviceNameUTF16, classGUID, deviceDescriptionUTF16, hwndParent, creationFlags, data)
}
// CreateDeviceInfo method creates a new device information element and adds it as a new member to the specified device information set.
func (deviceInfoSet DevInfo) CreateDeviceInfo(deviceName string, classGUID *windows.GUID, deviceDescription string, hwndParent uintptr, creationFlags DICD) (*DevInfoData, error) {
return SetupDiCreateDeviceInfo(deviceInfoSet, deviceName, classGUID, deviceDescription, hwndParent, creationFlags)
}
//sys setupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex uint32, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiEnumDeviceInfo
// SetupDiEnumDeviceInfo function returns a DevInfoData structure that specifies a device information element in a device information set.
func SetupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex int) (*DevInfoData, error) {
data := &DevInfoData{}
data.size = uint32(unsafe.Sizeof(*data))
return data, setupDiEnumDeviceInfo(deviceInfoSet, uint32(memberIndex), data)
}
// EnumDeviceInfo method returns a DevInfoData structure that specifies a device information element in a device information set.
func (deviceInfoSet DevInfo) EnumDeviceInfo(memberIndex int) (*DevInfoData, error) {
return SetupDiEnumDeviceInfo(deviceInfoSet, memberIndex)
}
// SetupDiDestroyDeviceInfoList function deletes a device information set and frees all associated memory.
//sys SetupDiDestroyDeviceInfoList(deviceInfoSet DevInfo) (err error) = setupapi.SetupDiDestroyDeviceInfoList
// Close method deletes a device information set and frees all associated memory.
func (deviceInfoSet DevInfo) Close() error {
return SetupDiDestroyDeviceInfoList(deviceInfoSet)
}
//sys SetupDiBuildDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) = setupapi.SetupDiBuildDriverInfoList
// BuildDriverInfoList method builds a list of drivers that is associated with a specific device or with the global class driver list for a device information set.
func (deviceInfoSet DevInfo) BuildDriverInfoList(deviceInfoData *DevInfoData, driverType SPDIT) error {
return SetupDiBuildDriverInfoList(deviceInfoSet, deviceInfoData, driverType)
}
//sys SetupDiCancelDriverInfoSearch(deviceInfoSet DevInfo) (err error) = setupapi.SetupDiCancelDriverInfoSearch
// CancelDriverInfoSearch method cancels a driver list search that is currently in progress in a different thread.
func (deviceInfoSet DevInfo) CancelDriverInfoSearch() error {
return SetupDiCancelDriverInfoSearch(deviceInfoSet)
}
//sys setupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex uint32, driverInfoData *DrvInfoData) (err error) = setupapi.SetupDiEnumDriverInfoW
// SetupDiEnumDriverInfo function enumerates the members of a driver list.
func SetupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex int) (*DrvInfoData, error) {
data := &DrvInfoData{}
data.size = uint32(unsafe.Sizeof(*data))
return data, setupDiEnumDriverInfo(deviceInfoSet, deviceInfoData, driverType, uint32(memberIndex), data)
}
// EnumDriverInfo method enumerates the members of a driver list.
func (deviceInfoSet DevInfo) EnumDriverInfo(deviceInfoData *DevInfoData, driverType SPDIT, memberIndex int) (*DrvInfoData, error) {
return SetupDiEnumDriverInfo(deviceInfoSet, deviceInfoData, driverType, memberIndex)
}
//sys setupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) = setupapi.SetupDiGetSelectedDriverW
// SetupDiGetSelectedDriver function retrieves the selected driver for a device information set or a particular device information element.
func SetupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (*DrvInfoData, error) {
data := &DrvInfoData{}
data.size = uint32(unsafe.Sizeof(*data))
return data, setupDiGetSelectedDriver(deviceInfoSet, deviceInfoData, data)
}
// SelectedDriver method retrieves the selected driver for a device information set or a particular device information element.
func (deviceInfoSet DevInfo) SelectedDriver(deviceInfoData *DevInfoData) (*DrvInfoData, error) {
return SetupDiGetSelectedDriver(deviceInfoSet, deviceInfoData)
}
//sys SetupDiSetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) = setupapi.SetupDiSetSelectedDriverW
// SetSelectedDriver method sets, or resets, the selected driver for a device information element or the selected class driver for a device information set.
func (deviceInfoSet DevInfo) SetSelectedDriver(deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) error {
return SetupDiSetSelectedDriver(deviceInfoSet, deviceInfoData, driverInfoData)
}
//sys setupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData, driverInfoDetailData *DrvInfoDetailData, driverInfoDetailDataSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDriverInfoDetailW
// SetupDiGetDriverInfoDetail function retrieves driver information detail for a device information set or a particular device information element in the device information set.
func SetupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (*DrvInfoDetailData, error) {
reqSize := uint32(2048)
for {
buf := make([]byte, reqSize)
data := (*DrvInfoDetailData)(unsafe.Pointer(&buf[0]))
data.size = sizeofDrvInfoDetailData
err := setupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData, data, uint32(len(buf)), &reqSize)
if err == windows.ERROR_INSUFFICIENT_BUFFER {
continue
}
if err != nil {
return nil, err
}
data.size = reqSize
return data, nil
}
}
// DriverInfoDetail method retrieves driver information detail for a device information set or a particular device information element in the device information set.
func (deviceInfoSet DevInfo) DriverInfoDetail(deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (*DrvInfoDetailData, error) {
return SetupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData)
}
//sys SetupDiDestroyDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) = setupapi.SetupDiDestroyDriverInfoList
// DestroyDriverInfoList method deletes a driver list.
func (deviceInfoSet DevInfo) DestroyDriverInfoList(deviceInfoData *DevInfoData, driverType SPDIT) error {
return SetupDiDestroyDriverInfoList(deviceInfoSet, deviceInfoData, driverType)
}
//sys setupDiGetClassDevsEx(classGUID *windows.GUID, Enumerator *uint16, hwndParent uintptr, Flags DIGCF, deviceInfoSet DevInfo, machineName *uint16, reserved uintptr) (handle DevInfo, err error) [failretval==DevInfo(windows.InvalidHandle)] = setupapi.SetupDiGetClassDevsExW
// SetupDiGetClassDevsEx function returns a handle to a device information set that contains requested device information elements for a local or a remote computer.
func SetupDiGetClassDevsEx(classGUID *windows.GUID, enumerator string, hwndParent uintptr, flags DIGCF, deviceInfoSet DevInfo, machineName string) (handle DevInfo, err error) {
var enumeratorUTF16 *uint16
if enumerator != "" {
enumeratorUTF16, err = windows.UTF16PtrFromString(enumerator)
if err != nil {
return
}
}
var machineNameUTF16 *uint16
if machineName != "" {
machineNameUTF16, err = windows.UTF16PtrFromString(machineName)
if err != nil {
return
}
}
return setupDiGetClassDevsEx(classGUID, enumeratorUTF16, hwndParent, flags, deviceInfoSet, machineNameUTF16, 0)
}
// SetupDiCallClassInstaller function calls the appropriate class installer, and any registered co-installers, with the specified installation request (DIF code).
//sys SetupDiCallClassInstaller(installFunction DI_FUNCTION, deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiCallClassInstaller
// CallClassInstaller member calls the appropriate class installer, and any registered co-installers, with the specified installation request (DIF code).
func (deviceInfoSet DevInfo) CallClassInstaller(installFunction DI_FUNCTION, deviceInfoData *DevInfoData) error {
return SetupDiCallClassInstaller(installFunction, deviceInfoSet, deviceInfoData)
}
//sys setupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (key windows.Handle, err error) [failretval==windows.InvalidHandle] = setupapi.SetupDiOpenDevRegKey
// SetupDiOpenDevRegKey function opens a registry key for device-specific configuration information.
func SetupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, scope DICS_FLAG, hwProfile uint32, keyType DIREG, samDesired uint32) (registry.Key, error) {
handle, err := setupDiOpenDevRegKey(deviceInfoSet, deviceInfoData, scope, hwProfile, keyType, samDesired)
return registry.Key(handle), err
}
// OpenDevRegKey method opens a registry key for device-specific configuration information.
func (deviceInfoSet DevInfo) OpenDevRegKey(DeviceInfoData *DevInfoData, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (registry.Key, error) {
return SetupDiOpenDevRegKey(deviceInfoSet, DeviceInfoData, Scope, HwProfile, KeyType, samDesired)
}
//sys setupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyRegDataType *uint32, propertyBuffer *byte, propertyBufferSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDeviceRegistryPropertyW
// SetupDiGetDeviceRegistryProperty function retrieves a specified Plug and Play device property.
func SetupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP) (value interface{}, err error) {
reqSize := uint32(256)
for {
var dataType uint32
buf := make([]byte, reqSize)
err = setupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &dataType, &buf[0], uint32(len(buf)), &reqSize)
if err == windows.ERROR_INSUFFICIENT_BUFFER {
continue
}
if err != nil {
return
}
return getRegistryValue(buf[:reqSize], dataType)
}
}
func getRegistryValue(buf []byte, dataType uint32) (interface{}, error) {
switch dataType {
case windows.REG_SZ:
ret := windows.UTF16ToString(bufToUTF16(buf))
runtime.KeepAlive(buf)
return ret, nil
case windows.REG_EXPAND_SZ:
ret, err := registry.ExpandString(windows.UTF16ToString(bufToUTF16(buf)))
runtime.KeepAlive(buf)
return ret, err
case windows.REG_BINARY:
return buf, nil
case windows.REG_DWORD_LITTLE_ENDIAN:
return binary.LittleEndian.Uint32(buf), nil
case windows.REG_DWORD_BIG_ENDIAN:
return binary.BigEndian.Uint32(buf), nil
case windows.REG_MULTI_SZ:
bufW := bufToUTF16(buf)
a := []string{}
for i := 0; i < len(bufW); {
j := i + wcslen(bufW[i:])
if i < j {
a = append(a, windows.UTF16ToString(bufW[i:j]))
}
i = j + 1
}
runtime.KeepAlive(buf)
return a, nil
case windows.REG_QWORD_LITTLE_ENDIAN:
return binary.LittleEndian.Uint64(buf), nil
default:
return nil, fmt.Errorf("Unsupported registry value type: %v", dataType)
}
}
// bufToUTF16 function reinterprets []byte buffer as []uint16
func bufToUTF16(buf []byte) []uint16 {
sl := struct {
addr *uint16
len int
cap int
}{(*uint16)(unsafe.Pointer(&buf[0])), len(buf) / 2, cap(buf) / 2}
return *(*[]uint16)(unsafe.Pointer(&sl))
}
// utf16ToBuf function reinterprets []uint16 as []byte
func utf16ToBuf(buf []uint16) []byte {
sl := struct {
addr *byte
len int
cap int
}{(*byte)(unsafe.Pointer(&buf[0])), len(buf) * 2, cap(buf) * 2}
return *(*[]byte)(unsafe.Pointer(&sl))
}
func wcslen(str []uint16) int {
for i := 0; i < len(str); i++ {
if str[i] == 0 {
return i
}
}
return len(str)
}
// DeviceRegistryProperty method retrieves a specified Plug and Play device property.
func (deviceInfoSet DevInfo) DeviceRegistryProperty(deviceInfoData *DevInfoData, property SPDRP) (interface{}, error) {
return SetupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property)
}
//sys setupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffer *byte, propertyBufferSize uint32) (err error) = setupapi.SetupDiSetDeviceRegistryPropertyW
// SetupDiSetDeviceRegistryProperty function sets a Plug and Play device property for a device.
func SetupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffers []byte) error {
return setupDiSetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &propertyBuffers[0], uint32(len(propertyBuffers)))
}
// SetDeviceRegistryProperty function sets a Plug and Play device property for a device.
func (deviceInfoSet DevInfo) SetDeviceRegistryProperty(deviceInfoData *DevInfoData, property SPDRP, propertyBuffers []byte) error {
return SetupDiSetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, propertyBuffers)
}
// SetDeviceRegistryPropertyString method sets a Plug and Play device property string for a device.
func (deviceInfoSet DevInfo) SetDeviceRegistryPropertyString(deviceInfoData *DevInfoData, property SPDRP, str string) error {
str16, err := windows.UTF16FromString(str)
if err != nil {
return err
}
err = SetupDiSetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, utf16ToBuf(append(str16, 0)))
runtime.KeepAlive(str16)
return err
}
//sys setupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) = setupapi.SetupDiGetDeviceInstallParamsW
// SetupDiGetDeviceInstallParams function retrieves device installation parameters for a device information set or a particular device information element.
func SetupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (*DevInstallParams, error) {
params := &DevInstallParams{}
params.size = uint32(unsafe.Sizeof(*params))
return params, setupDiGetDeviceInstallParams(deviceInfoSet, deviceInfoData, params)
}
// DeviceInstallParams method retrieves device installation parameters for a device information set or a particular device information element.
func (deviceInfoSet DevInfo) DeviceInstallParams(deviceInfoData *DevInfoData) (*DevInstallParams, error) {
return SetupDiGetDeviceInstallParams(deviceInfoSet, deviceInfoData)
}
//sys setupDiGetDeviceInstanceId(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, instanceId *uint16, instanceIdSize uint32, instanceIdRequiredSize *uint32) (err error) = setupapi.SetupDiGetDeviceInstanceIdW
// SetupDiGetDeviceInstanceId function retrieves the instance ID of the device.
func SetupDiGetDeviceInstanceId(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (string, error) {
reqSize := uint32(1024)
for {
buf := make([]uint16, reqSize)
err := setupDiGetDeviceInstanceId(deviceInfoSet, deviceInfoData, &buf[0], uint32(len(buf)), &reqSize)
if err == windows.ERROR_INSUFFICIENT_BUFFER {
continue
}
if err != nil {
return "", err
}
return windows.UTF16ToString(buf), nil
}
}
// DeviceInstanceID method retrieves the instance ID of the device.
func (deviceInfoSet DevInfo) DeviceInstanceID(deviceInfoData *DevInfoData) (string, error) {
return SetupDiGetDeviceInstanceId(deviceInfoSet, deviceInfoData)
}
// SetupDiGetClassInstallParams function retrieves class installation parameters for a device information set or a particular device information element.
//sys SetupDiGetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetClassInstallParamsW
// ClassInstallParams method retrieves class installation parameters for a device information set or a particular device information element.
func (deviceInfoSet DevInfo) ClassInstallParams(deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) error {
return SetupDiGetClassInstallParams(deviceInfoSet, deviceInfoData, classInstallParams, classInstallParamsSize, requiredSize)
}
//sys SetupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) = setupapi.SetupDiSetDeviceInstallParamsW
// SetDeviceInstallParams member sets device installation parameters for a device information set or a particular device information element.
func (deviceInfoSet DevInfo) SetDeviceInstallParams(deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) error {
return SetupDiSetDeviceInstallParams(deviceInfoSet, deviceInfoData, deviceInstallParams)
}
// SetupDiSetClassInstallParams function sets or clears class install parameters for a device information set or a particular device information element.
//sys SetupDiSetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) (err error) = setupapi.SetupDiSetClassInstallParamsW
// SetClassInstallParams method sets or clears class install parameters for a device information set or a particular device information element.
func (deviceInfoSet DevInfo) SetClassInstallParams(deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) error {
return SetupDiSetClassInstallParams(deviceInfoSet, deviceInfoData, classInstallParams, classInstallParamsSize)
}
//sys setupDiClassNameFromGuidEx(classGUID *windows.GUID, className *uint16, classNameSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) = setupapi.SetupDiClassNameFromGuidExW
// SetupDiClassNameFromGuidEx function retrieves the class name associated with a class GUID. The class can be installed on a local or remote computer.
func SetupDiClassNameFromGuidEx(classGUID *windows.GUID, machineName string) (className string, err error) {
var classNameUTF16 [MAX_CLASS_NAME_LEN]uint16
var machineNameUTF16 *uint16
if machineName != "" {
machineNameUTF16, err = windows.UTF16PtrFromString(machineName)
if err != nil {
return
}
}
err = setupDiClassNameFromGuidEx(classGUID, &classNameUTF16[0], MAX_CLASS_NAME_LEN, nil, machineNameUTF16, 0)
if err != nil {
return
}
className = windows.UTF16ToString(classNameUTF16[:])
return
}
//sys setupDiClassGuidsFromNameEx(className *uint16, classGuidList *windows.GUID, classGuidListSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) = setupapi.SetupDiClassGuidsFromNameExW
// SetupDiClassGuidsFromNameEx function retrieves the GUIDs associated with the specified class name. This resulting list contains the classes currently installed on a local or remote computer.
func SetupDiClassGuidsFromNameEx(className string, machineName string) ([]windows.GUID, error) {
classNameUTF16, err := windows.UTF16PtrFromString(className)
if err != nil {
return nil, err
}
var machineNameUTF16 *uint16
if machineName != "" {
machineNameUTF16, err = windows.UTF16PtrFromString(machineName)
if err != nil {
return nil, err
}
}
reqSize := uint32(4)
for {
buf := make([]windows.GUID, reqSize)
err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], uint32(len(buf)), &reqSize, machineNameUTF16, 0)
if err == windows.ERROR_INSUFFICIENT_BUFFER {
continue
}
if err != nil {
return nil, err
}
return buf[:reqSize], nil
}
}
//sys setupDiGetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiGetSelectedDevice
// SetupDiGetSelectedDevice function retrieves the selected device information element in a device information set.
func SetupDiGetSelectedDevice(deviceInfoSet DevInfo) (*DevInfoData, error) {
data := &DevInfoData{}
data.size = uint32(unsafe.Sizeof(*data))
return data, setupDiGetSelectedDevice(deviceInfoSet, data)
}
// SelectedDevice method retrieves the selected device information element in a device information set.
func (deviceInfoSet DevInfo) SelectedDevice() (*DevInfoData, error) {
return SetupDiGetSelectedDevice(deviceInfoSet)
}
// SetupDiSetSelectedDevice function sets a device information element as the selected member of a device information set. This function is typically used by an installation wizard.
//sys SetupDiSetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiSetSelectedDevice
// SetSelectedDevice method sets a device information element as the selected member of a device information set. This function is typically used by an installation wizard.
func (deviceInfoSet DevInfo) SetSelectedDevice(deviceInfoData *DevInfoData) error {
return SetupDiSetSelectedDevice(deviceInfoSet, deviceInfoData)
}
//sys cm_Get_Device_Interface_List_Size(len *uint32, interfaceClass *windows.GUID, deviceID *uint16, flags uint32) (ret uint32) = CfgMgr32.CM_Get_Device_Interface_List_SizeW
//sys cm_Get_Device_Interface_List(interfaceClass *windows.GUID, deviceID *uint16, buffer *uint16, bufferLen uint32, flags uint32) (ret uint32) = CfgMgr32.CM_Get_Device_Interface_ListW
func CM_Get_Device_Interface_List(deviceID string, interfaceClass *windows.GUID, flags uint32) ([]string, error) {
deviceID16, err := windows.UTF16PtrFromString(deviceID)
if err != nil {
return nil, err
}
var buf []uint16
var buflen uint32
for {
if ret := cm_Get_Device_Interface_List_Size(&buflen, interfaceClass, deviceID16, flags); ret != CR_SUCCESS {
return nil, fmt.Errorf("CfgMgr error: 0x%x", ret)
}
buf = make([]uint16, buflen)
if ret := cm_Get_Device_Interface_List(interfaceClass, deviceID16, &buf[0], buflen, flags); ret == CR_SUCCESS {
break
} else if ret != CR_BUFFER_SMALL {
return nil, fmt.Errorf("CfgMgr error: 0x%x", ret)
}
}
var interfaces []string
for i := 0; i < len(buf); {
j := i + wcslen(buf[i:])
if i < j {
interfaces = append(interfaces, windows.UTF16ToString(buf[i:j]))
}
i = j + 1
}
if interfaces == nil {
return nil, fmt.Errorf("no interfaces found")
}
return interfaces, nil
}

View File

@@ -0,0 +1,488 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package setupapi
import (
"runtime"
"strings"
"testing"
"golang.org/x/sys/windows"
)
var deviceClassNetGUID = windows.GUID{Data1: 0x4d36e972, Data2: 0xe325, Data3: 0x11ce, Data4: [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}
var computerName string
func init() {
computerName, _ = windows.ComputerName()
}
func TestSetupDiCreateDeviceInfoListEx(t *testing.T) {
devInfoList, err := SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, "")
if err != nil {
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
} else {
devInfoList.Close()
}
devInfoList, err = SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, computerName)
if err != nil {
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
} else {
devInfoList.Close()
}
devInfoList, err = SetupDiCreateDeviceInfoListEx(nil, 0, "")
if err != nil {
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx(nil): %s", err.Error())
} else {
devInfoList.Close()
}
}
func TestSetupDiGetDeviceInfoListDetail(t *testing.T) {
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
if err != nil {
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
}
defer devInfoList.Close()
data, err := devInfoList.DeviceInfoListDetail()
if err != nil {
t.Errorf("Error calling SetupDiGetDeviceInfoListDetail: %s", err.Error())
} else {
if data.ClassGUID != deviceClassNetGUID {
t.Error("SetupDiGetDeviceInfoListDetail returned different class GUID")
}
if data.RemoteMachineHandle != windows.Handle(0) {
t.Error("SetupDiGetDeviceInfoListDetail returned non-NULL remote machine handle")
}
if data.RemoteMachineName() != "" {
t.Error("SetupDiGetDeviceInfoListDetail returned non-NULL remote machine name")
}
}
devInfoList, err = SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), computerName)
if err != nil {
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
}
defer devInfoList.Close()
data, err = devInfoList.DeviceInfoListDetail()
if err != nil {
t.Errorf("Error calling SetupDiGetDeviceInfoListDetail: %s", err.Error())
} else {
if data.ClassGUID != deviceClassNetGUID {
t.Error("SetupDiGetDeviceInfoListDetail returned different class GUID")
}
if data.RemoteMachineHandle == windows.Handle(0) {
t.Error("SetupDiGetDeviceInfoListDetail returned NULL remote machine handle")
}
if data.RemoteMachineName() != computerName {
t.Error("SetupDiGetDeviceInfoListDetail returned different remote machine name")
}
}
data = &DevInfoListDetailData{}
data.SetRemoteMachineName("foobar")
if data.RemoteMachineName() != "foobar" {
t.Error("DevInfoListDetailData.(Get|Set)RemoteMachineName() differ")
}
}
func TestSetupDiCreateDeviceInfo(t *testing.T) {
devInfoList, err := SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, computerName)
if err != nil {
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
}
defer devInfoList.Close()
deviceClassNetName, err := SetupDiClassNameFromGuidEx(&deviceClassNetGUID, computerName)
if err != nil {
t.Errorf("Error calling SetupDiClassNameFromGuidEx: %s", err.Error())
}
devInfoData, err := devInfoList.CreateDeviceInfo(deviceClassNetName, &deviceClassNetGUID, "This is a test device", 0, DICD_GENERATE_ID)
if err != nil {
// Access denied is expected, as the SetupDiCreateDeviceInfo() require elevation to succeed.
if errWin, ok := err.(windows.Errno); !ok || errWin != windows.ERROR_ACCESS_DENIED {
t.Errorf("Error calling SetupDiCreateDeviceInfo: %s", err.Error())
}
} else if devInfoData.ClassGUID != deviceClassNetGUID {
t.Error("SetupDiCreateDeviceInfo returned different class GUID")
}
}
func TestSetupDiEnumDeviceInfo(t *testing.T) {
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
if err != nil {
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
}
defer devInfoList.Close()
for i := 0; true; i++ {
data, err := devInfoList.EnumDeviceInfo(i)
if err != nil {
if errWin, ok := err.(windows.Errno); ok && errWin == windows.ERROR_NO_MORE_ITEMS {
break
}
continue
}
if data.ClassGUID != deviceClassNetGUID {
t.Error("SetupDiEnumDeviceInfo returned different class GUID")
}
_, err = devInfoList.DeviceInstanceID(data)
if err != nil {
t.Errorf("Error calling SetupDiGetDeviceInstanceId: %s", err.Error())
}
}
}
func TestDevInfo_BuildDriverInfoList(t *testing.T) {
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
if err != nil {
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
}
defer devInfoList.Close()
for i := 0; true; i++ {
deviceData, err := devInfoList.EnumDeviceInfo(i)
if err != nil {
if errWin, ok := err.(windows.Errno); ok && errWin == windows.ERROR_NO_MORE_ITEMS {
break
}
continue
}
const driverType SPDIT = SPDIT_COMPATDRIVER
err = devInfoList.BuildDriverInfoList(deviceData, driverType)
if err != nil {
t.Errorf("Error calling SetupDiBuildDriverInfoList: %s", err.Error())
}
defer devInfoList.DestroyDriverInfoList(deviceData, driverType)
var selectedDriverData *DrvInfoData
for j := 0; true; j++ {
driverData, err := devInfoList.EnumDriverInfo(deviceData, driverType, j)
if err != nil {
if errWin, ok := err.(windows.Errno); ok && errWin == windows.ERROR_NO_MORE_ITEMS {
break
}
continue
}
if driverData.DriverType == 0 {
continue
}
if !driverData.IsNewer(windows.Filetime{}, 0) {
t.Error("Driver should have non-zero date and version")
}
if !driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime}, 0) {
t.Error("Driver should have non-zero date and version")
}
if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime + 1}, 0) {
t.Error("Driver should report newer version on high-date-time")
}
if !driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime}, 0) {
t.Error("Driver should have non-zero version")
}
if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime + 1}, 0) {
t.Error("Driver should report newer version on low-date-time")
}
if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime}, driverData.DriverVersion) {
t.Error("Driver should not be newer than itself")
}
if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime}, driverData.DriverVersion+1) {
t.Error("Driver should report newer version on version")
}
err = devInfoList.SetSelectedDriver(deviceData, driverData)
if err != nil {
t.Errorf("Error calling SetupDiSetSelectedDriver: %s", err.Error())
} else {
selectedDriverData = driverData
}
driverDetailData, err := devInfoList.DriverInfoDetail(deviceData, driverData)
if err != nil {
t.Errorf("Error calling SetupDiGetDriverInfoDetail: %s", err.Error())
}
if driverDetailData.IsCompatible("foobar-aab6e3a4-144e-4786-88d3-6cec361e1edd") {
t.Error("Invalid HWID compatibitlity reported")
}
if !driverDetailData.IsCompatible(strings.ToUpper(driverDetailData.HardwareID())) {
t.Error("HWID compatibitlity missed")
}
a := driverDetailData.CompatIDs()
for k := range a {
if !driverDetailData.IsCompatible(strings.ToUpper(a[k])) {
t.Error("HWID compatibitlity missed")
}
}
}
selectedDriverData2, err := devInfoList.SelectedDriver(deviceData)
if err != nil {
t.Errorf("Error calling SetupDiGetSelectedDriver: %s", err.Error())
} else if *selectedDriverData != *selectedDriverData2 {
t.Error("SetupDiGetSelectedDriver should return driver selected with SetupDiSetSelectedDriver")
}
}
data := &DrvInfoData{}
data.SetDescription("foobar")
if data.Description() != "foobar" {
t.Error("DrvInfoData.(Get|Set)Description() differ")
}
data.SetMfgName("foobar")
if data.MfgName() != "foobar" {
t.Error("DrvInfoData.(Get|Set)MfgName() differ")
}
data.SetProviderName("foobar")
if data.ProviderName() != "foobar" {
t.Error("DrvInfoData.(Get|Set)ProviderName() differ")
}
}
func TestSetupDiGetClassDevsEx(t *testing.T) {
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "PCI", 0, DIGCF_PRESENT, DevInfo(0), computerName)
if err != nil {
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
} else {
devInfoList.Close()
}
devInfoList, err = SetupDiGetClassDevsEx(nil, "", 0, DIGCF_PRESENT, DevInfo(0), "")
if err != nil {
if errWin, ok := err.(windows.Errno); !ok || errWin != windows.ERROR_INVALID_PARAMETER {
t.Errorf("SetupDiGetClassDevsEx(nil, ...) should fail with ERROR_INVALID_PARAMETER")
}
} else {
devInfoList.Close()
t.Errorf("SetupDiGetClassDevsEx(nil, ...) should fail")
}
}
func TestSetupDiOpenDevRegKey(t *testing.T) {
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
if err != nil {
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
}
defer devInfoList.Close()
for i := 0; true; i++ {
data, err := devInfoList.EnumDeviceInfo(i)
if err != nil {
if errWin, ok := err.(windows.Errno); ok && errWin == windows.ERROR_NO_MORE_ITEMS {
break
}
continue
}
key, err := devInfoList.OpenDevRegKey(data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, windows.KEY_READ)
if err != nil {
t.Errorf("Error calling SetupDiOpenDevRegKey: %s", err.Error())
}
defer key.Close()
}
}
func TestSetupDiGetDeviceRegistryProperty(t *testing.T) {
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
if err != nil {
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
}
defer devInfoList.Close()
for i := 0; true; i++ {
data, err := devInfoList.EnumDeviceInfo(i)
if err != nil {
if errWin, ok := err.(windows.Errno); ok && errWin == windows.ERROR_NO_MORE_ITEMS {
break
}
continue
}
val, err := devInfoList.DeviceRegistryProperty(data, SPDRP_CLASS)
if err != nil {
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CLASS): %s", err.Error())
} else if class, ok := val.(string); !ok || strings.ToLower(class) != "net" {
t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASS) should return \"Net\"")
}
val, err = devInfoList.DeviceRegistryProperty(data, SPDRP_CLASSGUID)
if err != nil {
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID): %s", err.Error())
} else if valStr, ok := val.(string); !ok {
t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID) should return string")
} else {
classGUID, err := windows.GUIDFromString(valStr)
if err != nil {
t.Errorf("Error parsing GUID returned by SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID): %s", err.Error())
} else if classGUID != deviceClassNetGUID {
t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID) should return %x", deviceClassNetGUID)
}
}
val, err = devInfoList.DeviceRegistryProperty(data, SPDRP_COMPATIBLEIDS)
if err != nil {
// Some devices have no SPDRP_COMPATIBLEIDS.
if errWin, ok := err.(windows.Errno); !ok || errWin != windows.ERROR_INVALID_DATA {
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_COMPATIBLEIDS): %s", err.Error())
}
}
val, err = devInfoList.DeviceRegistryProperty(data, SPDRP_CONFIGFLAGS)
if err != nil {
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CONFIGFLAGS): %s", err.Error())
}
val, err = devInfoList.DeviceRegistryProperty(data, SPDRP_DEVICE_POWER_DATA)
if err != nil {
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_DEVICE_POWER_DATA): %s", err.Error())
}
}
}
func TestSetupDiGetDeviceInstallParams(t *testing.T) {
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
if err != nil {
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
}
defer devInfoList.Close()
for i := 0; true; i++ {
data, err := devInfoList.EnumDeviceInfo(i)
if err != nil {
if errWin, ok := err.(windows.Errno); ok && errWin == windows.ERROR_NO_MORE_ITEMS {
break
}
continue
}
_, err = devInfoList.DeviceInstallParams(data)
if err != nil {
t.Errorf("Error calling SetupDiGetDeviceInstallParams: %s", err.Error())
}
}
params := &DevInstallParams{}
params.SetDriverPath("foobar")
if params.DriverPath() != "foobar" {
t.Error("DevInstallParams.(Get|Set)DriverPath() differ")
}
}
func TestSetupDiClassNameFromGuidEx(t *testing.T) {
deviceClassNetName, err := SetupDiClassNameFromGuidEx(&deviceClassNetGUID, "")
if err != nil {
t.Errorf("Error calling SetupDiClassNameFromGuidEx: %s", err.Error())
} else if strings.ToLower(deviceClassNetName) != "net" {
t.Errorf("SetupDiClassNameFromGuidEx(%x) should return \"Net\"", deviceClassNetGUID)
}
deviceClassNetName, err = SetupDiClassNameFromGuidEx(&deviceClassNetGUID, computerName)
if err != nil {
t.Errorf("Error calling SetupDiClassNameFromGuidEx: %s", err.Error())
} else if strings.ToLower(deviceClassNetName) != "net" {
t.Errorf("SetupDiClassNameFromGuidEx(%x) should return \"Net\"", deviceClassNetGUID)
}
_, err = SetupDiClassNameFromGuidEx(nil, "")
if err != nil {
if errWin, ok := err.(windows.Errno); !ok || errWin != windows.ERROR_INVALID_USER_BUFFER {
t.Errorf("SetupDiClassNameFromGuidEx(nil) should fail with ERROR_INVALID_USER_BUFFER")
}
} else {
t.Errorf("SetupDiClassNameFromGuidEx(nil) should fail")
}
}
func TestSetupDiClassGuidsFromNameEx(t *testing.T) {
ClassGUIDs, err := SetupDiClassGuidsFromNameEx("Net", "")
if err != nil {
t.Errorf("Error calling SetupDiClassGuidsFromNameEx: %s", err.Error())
} else {
found := false
for i := range ClassGUIDs {
if ClassGUIDs[i] == deviceClassNetGUID {
found = true
break
}
}
if !found {
t.Errorf("SetupDiClassGuidsFromNameEx(\"Net\") should return %x", deviceClassNetGUID)
}
}
ClassGUIDs, err = SetupDiClassGuidsFromNameEx("foobar-34274a51-a6e6-45f0-80d6-c62be96dd5fe", computerName)
if err != nil {
t.Errorf("Error calling SetupDiClassGuidsFromNameEx: %s", err.Error())
} else if len(ClassGUIDs) != 0 {
t.Errorf("SetupDiClassGuidsFromNameEx(\"foobar-34274a51-a6e6-45f0-80d6-c62be96dd5fe\") should return an empty GUID set")
}
}
func TestSetupDiGetSelectedDevice(t *testing.T) {
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
if err != nil {
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
}
defer devInfoList.Close()
for i := 0; true; i++ {
data, err := devInfoList.EnumDeviceInfo(i)
if err != nil {
if errWin, ok := err.(windows.Errno); ok && errWin == windows.ERROR_NO_MORE_ITEMS {
break
}
continue
}
err = devInfoList.SetSelectedDevice(data)
if err != nil {
t.Errorf("Error calling SetupDiSetSelectedDevice: %s", err.Error())
}
data2, err := devInfoList.SelectedDevice()
if err != nil {
t.Errorf("Error calling SetupDiGetSelectedDevice: %s", err.Error())
} else if *data != *data2 {
t.Error("SetupDiGetSelectedDevice returned different data than was set by SetupDiSetSelectedDevice")
}
}
err = devInfoList.SetSelectedDevice(nil)
if err != nil {
if errWin, ok := err.(windows.Errno); !ok || errWin != windows.ERROR_INVALID_PARAMETER {
t.Errorf("SetupDiSetSelectedDevice(nil) should fail with ERROR_INVALID_USER_BUFFER")
}
} else {
t.Errorf("SetupDiSetSelectedDevice(nil) should fail")
}
}
func TestUTF16ToBuf(t *testing.T) {
buf := []uint16{0x0123, 0x4567, 0x89ab, 0xcdef}
buf2 := utf16ToBuf(buf)
if len(buf)*2 != len(buf2) ||
cap(buf)*2 != cap(buf2) ||
buf2[0] != 0x23 || buf2[1] != 0x01 ||
buf2[2] != 0x67 || buf2[3] != 0x45 ||
buf2[4] != 0xab || buf2[5] != 0x89 ||
buf2[6] != 0xef || buf2[7] != 0xcd {
t.Errorf("SetupDiSetSelectedDevice(nil) should fail with ERROR_INVALID_USER_BUFFER")
}
runtime.KeepAlive(buf)
}

View File

@@ -0,0 +1,568 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package setupapi
import (
"strings"
"unsafe"
"golang.org/x/sys/windows"
)
const (
MAX_DEVICE_ID_LEN = 200
MAX_DEVNODE_ID_LEN = MAX_DEVICE_ID_LEN
MAX_GUID_STRING_LEN = 39 // 38 chars + terminator null
MAX_CLASS_NAME_LEN = 32
MAX_PROFILE_LEN = 80
MAX_CONFIG_VALUE = 9999
MAX_INSTANCE_VALUE = 9999
CONFIGMG_VERSION = 0x0400
)
//
// Define maximum string length constants
//
const (
ANYSIZE_ARRAY = 1
LINE_LEN = 256 // Windows 9x-compatible maximum for displayable strings coming from a device INF.
MAX_INF_STRING_LENGTH = 4096 // Actual maximum size of an INF string (including string substitutions).
MAX_INF_SECTION_NAME_LENGTH = 255 // For Windows 9x compatibility, INF section names should be constrained to 32 characters.
MAX_TITLE_LEN = 60
MAX_INSTRUCTION_LEN = 256
MAX_LABEL_LEN = 30
MAX_SERVICE_NAME_LEN = 256
MAX_SUBTITLE_LEN = 256
)
const (
// SP_MAX_MACHINENAME_LENGTH defines maximum length of a machine name in the format expected by ConfigMgr32 CM_Connect_Machine (i.e., "\\\\MachineName\0").
SP_MAX_MACHINENAME_LENGTH = windows.MAX_PATH + 3
)
// HSPFILEQ is type for setup file queue
type HSPFILEQ uintptr
// DevInfo holds reference to device information set
type DevInfo windows.Handle
// DevInfoData is a device information structure (references a device instance that is a member of a device information set)
type DevInfoData struct {
size uint32
ClassGUID windows.GUID
DevInst uint32 // DEVINST handle
_ uintptr
}
// DevInfoListDetailData is a structure for detailed information on a device information set (used for SetupDiGetDeviceInfoListDetail which supercedes the functionality of SetupDiGetDeviceInfoListClass).
type DevInfoListDetailData struct {
size uint32 // Warning: unsafe.Sizeof(DevInfoListDetailData) > sizeof(SP_DEVINFO_LIST_DETAIL_DATA) when GOARCH == 386 => use sizeofDevInfoListDetailData const.
ClassGUID windows.GUID
RemoteMachineHandle windows.Handle
remoteMachineName [SP_MAX_MACHINENAME_LENGTH]uint16
}
func (data *DevInfoListDetailData) RemoteMachineName() string {
return windows.UTF16ToString(data.remoteMachineName[:])
}
func (data *DevInfoListDetailData) SetRemoteMachineName(remoteMachineName string) error {
str, err := windows.UTF16FromString(remoteMachineName)
if err != nil {
return err
}
copy(data.remoteMachineName[:], str)
return nil
}
// DI_FUNCTION is function type for device installer
type DI_FUNCTION uint32
const (
DIF_SELECTDEVICE DI_FUNCTION = 0x00000001
DIF_INSTALLDEVICE DI_FUNCTION = 0x00000002
DIF_ASSIGNRESOURCES DI_FUNCTION = 0x00000003
DIF_PROPERTIES DI_FUNCTION = 0x00000004
DIF_REMOVE DI_FUNCTION = 0x00000005
DIF_FIRSTTIMESETUP DI_FUNCTION = 0x00000006
DIF_FOUNDDEVICE DI_FUNCTION = 0x00000007
DIF_SELECTCLASSDRIVERS DI_FUNCTION = 0x00000008
DIF_VALIDATECLASSDRIVERS DI_FUNCTION = 0x00000009
DIF_INSTALLCLASSDRIVERS DI_FUNCTION = 0x0000000A
DIF_CALCDISKSPACE DI_FUNCTION = 0x0000000B
DIF_DESTROYPRIVATEDATA DI_FUNCTION = 0x0000000C
DIF_VALIDATEDRIVER DI_FUNCTION = 0x0000000D
DIF_DETECT DI_FUNCTION = 0x0000000F
DIF_INSTALLWIZARD DI_FUNCTION = 0x00000010
DIF_DESTROYWIZARDDATA DI_FUNCTION = 0x00000011
DIF_PROPERTYCHANGE DI_FUNCTION = 0x00000012
DIF_ENABLECLASS DI_FUNCTION = 0x00000013
DIF_DETECTVERIFY DI_FUNCTION = 0x00000014
DIF_INSTALLDEVICEFILES DI_FUNCTION = 0x00000015
DIF_UNREMOVE DI_FUNCTION = 0x00000016
DIF_SELECTBESTCOMPATDRV DI_FUNCTION = 0x00000017
DIF_ALLOW_INSTALL DI_FUNCTION = 0x00000018
DIF_REGISTERDEVICE DI_FUNCTION = 0x00000019
DIF_NEWDEVICEWIZARD_PRESELECT DI_FUNCTION = 0x0000001A
DIF_NEWDEVICEWIZARD_SELECT DI_FUNCTION = 0x0000001B
DIF_NEWDEVICEWIZARD_PREANALYZE DI_FUNCTION = 0x0000001C
DIF_NEWDEVICEWIZARD_POSTANALYZE DI_FUNCTION = 0x0000001D
DIF_NEWDEVICEWIZARD_FINISHINSTALL DI_FUNCTION = 0x0000001E
DIF_INSTALLINTERFACES DI_FUNCTION = 0x00000020
DIF_DETECTCANCEL DI_FUNCTION = 0x00000021
DIF_REGISTER_COINSTALLERS DI_FUNCTION = 0x00000022
DIF_ADDPROPERTYPAGE_ADVANCED DI_FUNCTION = 0x00000023
DIF_ADDPROPERTYPAGE_BASIC DI_FUNCTION = 0x00000024
DIF_TROUBLESHOOTER DI_FUNCTION = 0x00000026
DIF_POWERMESSAGEWAKE DI_FUNCTION = 0x00000027
DIF_ADDREMOTEPROPERTYPAGE_ADVANCED DI_FUNCTION = 0x00000028
DIF_UPDATEDRIVER_UI DI_FUNCTION = 0x00000029
DIF_FINISHINSTALL_ACTION DI_FUNCTION = 0x0000002A
)
// DevInstallParams is device installation parameters structure (associated with a particular device information element, or globally with a device information set)
type DevInstallParams struct {
size uint32
Flags DI_FLAGS
FlagsEx DI_FLAGSEX
hwndParent uintptr
InstallMsgHandler uintptr
InstallMsgHandlerContext uintptr
FileQueue HSPFILEQ
_ uintptr
_ uint32
driverPath [windows.MAX_PATH]uint16
}
func (params *DevInstallParams) DriverPath() string {
return windows.UTF16ToString(params.driverPath[:])
}
func (params *DevInstallParams) SetDriverPath(driverPath string) error {
str, err := windows.UTF16FromString(driverPath)
if err != nil {
return err
}
copy(params.driverPath[:], str)
return nil
}
// DI_FLAGS is SP_DEVINSTALL_PARAMS.Flags values
type DI_FLAGS uint32
const (
// Flags for choosing a device
DI_SHOWOEM DI_FLAGS = 0x00000001 // support Other... button
DI_SHOWCOMPAT DI_FLAGS = 0x00000002 // show compatibility list
DI_SHOWCLASS DI_FLAGS = 0x00000004 // show class list
DI_SHOWALL DI_FLAGS = 0x00000007 // both class & compat list shown
DI_NOVCP DI_FLAGS = 0x00000008 // don't create a new copy queue--use caller-supplied FileQueue
DI_DIDCOMPAT DI_FLAGS = 0x00000010 // Searched for compatible devices
DI_DIDCLASS DI_FLAGS = 0x00000020 // Searched for class devices
DI_AUTOASSIGNRES DI_FLAGS = 0x00000040 // No UI for resources if possible
// Flags returned by DiInstallDevice to indicate need to reboot/restart
DI_NEEDRESTART DI_FLAGS = 0x00000080 // Reboot required to take effect
DI_NEEDREBOOT DI_FLAGS = 0x00000100 // ""
// Flags for device installation
DI_NOBROWSE DI_FLAGS = 0x00000200 // no Browse... in InsertDisk
// Flags set by DiBuildDriverInfoList
DI_MULTMFGS DI_FLAGS = 0x00000400 // Set if multiple manufacturers in class driver list
// Flag indicates that device is disabled
DI_DISABLED DI_FLAGS = 0x00000800 // Set if device disabled
// Flags for Device/Class Properties
DI_GENERALPAGE_ADDED DI_FLAGS = 0x00001000
DI_RESOURCEPAGE_ADDED DI_FLAGS = 0x00002000
// Flag to indicate the setting properties for this Device (or class) caused a change so the Dev Mgr UI probably needs to be updated.
DI_PROPERTIES_CHANGE DI_FLAGS = 0x00004000
// Flag to indicate that the sorting from the INF file should be used.
DI_INF_IS_SORTED DI_FLAGS = 0x00008000
// Flag to indicate that only the the INF specified by SP_DEVINSTALL_PARAMS.DriverPath should be searched.
DI_ENUMSINGLEINF DI_FLAGS = 0x00010000
// Flag that prevents ConfigMgr from removing/re-enumerating devices during device
// registration, installation, and deletion.
DI_DONOTCALLCONFIGMG DI_FLAGS = 0x00020000
// The following flag can be used to install a device disabled
DI_INSTALLDISABLED DI_FLAGS = 0x00040000
// Flag that causes SetupDiBuildDriverInfoList to build a device's compatible driver
// list from its existing class driver list, instead of the normal INF search.
DI_COMPAT_FROM_CLASS DI_FLAGS = 0x00080000
// This flag is set if the Class Install params should be used.
DI_CLASSINSTALLPARAMS DI_FLAGS = 0x00100000
// This flag is set if the caller of DiCallClassInstaller does NOT want the internal default action performed if the Class installer returns ERROR_DI_DO_DEFAULT.
DI_NODI_DEFAULTACTION DI_FLAGS = 0x00200000
// Flags for device installation
DI_QUIETINSTALL DI_FLAGS = 0x00800000 // don't confuse the user with questions or excess info
DI_NOFILECOPY DI_FLAGS = 0x01000000 // No file Copy necessary
DI_FORCECOPY DI_FLAGS = 0x02000000 // Force files to be copied from install path
DI_DRIVERPAGE_ADDED DI_FLAGS = 0x04000000 // Prop provider added Driver page.
DI_USECI_SELECTSTRINGS DI_FLAGS = 0x08000000 // Use Class Installer Provided strings in the Select Device Dlg
DI_OVERRIDE_INFFLAGS DI_FLAGS = 0x10000000 // Override INF flags
DI_PROPS_NOCHANGEUSAGE DI_FLAGS = 0x20000000 // No Enable/Disable in General Props
DI_NOSELECTICONS DI_FLAGS = 0x40000000 // No small icons in select device dialogs
DI_NOWRITE_IDS DI_FLAGS = 0x80000000 // Don't write HW & Compat IDs on install
)
// DI_FLAGSEX is SP_DEVINSTALL_PARAMS.FlagsEx values
type DI_FLAGSEX uint32
const (
DI_FLAGSEX_CI_FAILED DI_FLAGSEX = 0x00000004 // Failed to Load/Call class installer
DI_FLAGSEX_FINISHINSTALL_ACTION DI_FLAGSEX = 0x00000008 // Class/co-installer wants to get a DIF_FINISH_INSTALL action in client context.
DI_FLAGSEX_DIDINFOLIST DI_FLAGSEX = 0x00000010 // Did the Class Info List
DI_FLAGSEX_DIDCOMPATINFO DI_FLAGSEX = 0x00000020 // Did the Compat Info List
DI_FLAGSEX_FILTERCLASSES DI_FLAGSEX = 0x00000040
DI_FLAGSEX_SETFAILEDINSTALL DI_FLAGSEX = 0x00000080
DI_FLAGSEX_DEVICECHANGE DI_FLAGSEX = 0x00000100
DI_FLAGSEX_ALWAYSWRITEIDS DI_FLAGSEX = 0x00000200
DI_FLAGSEX_PROPCHANGE_PENDING DI_FLAGSEX = 0x00000400 // One or more device property sheets have had changes made to them, and need to have a DIF_PROPERTYCHANGE occur.
DI_FLAGSEX_ALLOWEXCLUDEDDRVS DI_FLAGSEX = 0x00000800
DI_FLAGSEX_NOUIONQUERYREMOVE DI_FLAGSEX = 0x00001000
DI_FLAGSEX_USECLASSFORCOMPAT DI_FLAGSEX = 0x00002000 // Use the device's class when building compat drv list. (Ignored if DI_COMPAT_FROM_CLASS flag is specified.)
DI_FLAGSEX_NO_DRVREG_MODIFY DI_FLAGSEX = 0x00008000 // Don't run AddReg and DelReg for device's software (driver) key.
DI_FLAGSEX_IN_SYSTEM_SETUP DI_FLAGSEX = 0x00010000 // Installation is occurring during initial system setup.
DI_FLAGSEX_INET_DRIVER DI_FLAGSEX = 0x00020000 // Driver came from Windows Update
DI_FLAGSEX_APPENDDRIVERLIST DI_FLAGSEX = 0x00040000 // Cause SetupDiBuildDriverInfoList to append a new driver list to an existing list.
DI_FLAGSEX_PREINSTALLBACKUP DI_FLAGSEX = 0x00080000 // not used
DI_FLAGSEX_BACKUPONREPLACE DI_FLAGSEX = 0x00100000 // not used
DI_FLAGSEX_DRIVERLIST_FROM_URL DI_FLAGSEX = 0x00200000 // build driver list from INF(s) retrieved from URL specified in SP_DEVINSTALL_PARAMS.DriverPath (empty string means Windows Update website)
DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS DI_FLAGSEX = 0x00800000 // Don't include old Internet drivers when building a driver list. Ignored on Windows Vista and later.
DI_FLAGSEX_POWERPAGE_ADDED DI_FLAGSEX = 0x01000000 // class installer added their own power page
DI_FLAGSEX_FILTERSIMILARDRIVERS DI_FLAGSEX = 0x02000000 // only include similar drivers in class list
DI_FLAGSEX_INSTALLEDDRIVER DI_FLAGSEX = 0x04000000 // only add the installed driver to the class or compat driver list. Used in calls to SetupDiBuildDriverInfoList
DI_FLAGSEX_NO_CLASSLIST_NODE_MERGE DI_FLAGSEX = 0x08000000 // Don't remove identical driver nodes from the class list
DI_FLAGSEX_ALTPLATFORM_DRVSEARCH DI_FLAGSEX = 0x10000000 // Build driver list based on alternate platform information specified in associated file queue
DI_FLAGSEX_RESTART_DEVICE_ONLY DI_FLAGSEX = 0x20000000 // only restart the device drivers are being installed on as opposed to restarting all devices using those drivers.
DI_FLAGSEX_RECURSIVESEARCH DI_FLAGSEX = 0x40000000 // Tell SetupDiBuildDriverInfoList to do a recursive search
DI_FLAGSEX_SEARCH_PUBLISHED_INFS DI_FLAGSEX = 0x80000000 // Tell SetupDiBuildDriverInfoList to do a "published INF" search
)
// ClassInstallHeader is the first member of any class install parameters structure. It contains the device installation request code that defines the format of the rest of the install parameters structure.
type ClassInstallHeader struct {
size uint32
InstallFunction DI_FUNCTION
}
func MakeClassInstallHeader(installFunction DI_FUNCTION) *ClassInstallHeader {
hdr := &ClassInstallHeader{InstallFunction: installFunction}
hdr.size = uint32(unsafe.Sizeof(*hdr))
return hdr
}
// DICS_STATE specifies values indicating a change in a device's state
type DICS_STATE uint32
const (
DICS_ENABLE DICS_STATE = 0x00000001 // The device is being enabled.
DICS_DISABLE DICS_STATE = 0x00000002 // The device is being disabled.
DICS_PROPCHANGE DICS_STATE = 0x00000003 // The properties of the device have changed.
DICS_START DICS_STATE = 0x00000004 // The device is being started (if the request is for the currently active hardware profile).
DICS_STOP DICS_STATE = 0x00000005 // The device is being stopped. The driver stack will be unloaded and the CSCONFIGFLAG_DO_NOT_START flag will be set for the device.
)
// DICS_FLAG specifies the scope of a device property change
type DICS_FLAG uint32
const (
DICS_FLAG_GLOBAL DICS_FLAG = 0x00000001 // make change in all hardware profiles
DICS_FLAG_CONFIGSPECIFIC DICS_FLAG = 0x00000002 // make change in specified profile only
DICS_FLAG_CONFIGGENERAL DICS_FLAG = 0x00000004 // 1 or more hardware profile-specific changes to follow (obsolete)
)
// PropChangeParams is a structure corresponding to a DIF_PROPERTYCHANGE install function.
type PropChangeParams struct {
ClassInstallHeader ClassInstallHeader
StateChange DICS_STATE
Scope DICS_FLAG
HwProfile uint32
}
// DI_REMOVEDEVICE specifies the scope of the device removal
type DI_REMOVEDEVICE uint32
const (
DI_REMOVEDEVICE_GLOBAL DI_REMOVEDEVICE = 0x00000001 // Make this change in all hardware profiles. Remove information about the device from the registry.
DI_REMOVEDEVICE_CONFIGSPECIFIC DI_REMOVEDEVICE = 0x00000002 // Make this change to only the hardware profile specified by HwProfile. this flag only applies to root-enumerated devices. When Windows removes the device from the last hardware profile in which it was configured, Windows performs a global removal.
)
// RemoveDeviceParams is a structure corresponding to a DIF_REMOVE install function.
type RemoveDeviceParams struct {
ClassInstallHeader ClassInstallHeader
Scope DI_REMOVEDEVICE
HwProfile uint32
}
// DrvInfoData is driver information structure (member of a driver info list that may be associated with a particular device instance, or (globally) with a device information set)
type DrvInfoData struct {
size uint32
DriverType uint32
_ uintptr
description [LINE_LEN]uint16
mfgName [LINE_LEN]uint16
providerName [LINE_LEN]uint16
DriverDate windows.Filetime
DriverVersion uint64
}
func (data *DrvInfoData) Description() string {
return windows.UTF16ToString(data.description[:])
}
func (data *DrvInfoData) SetDescription(description string) error {
str, err := windows.UTF16FromString(description)
if err != nil {
return err
}
copy(data.description[:], str)
return nil
}
func (data *DrvInfoData) MfgName() string {
return windows.UTF16ToString(data.mfgName[:])
}
func (data *DrvInfoData) SetMfgName(mfgName string) error {
str, err := windows.UTF16FromString(mfgName)
if err != nil {
return err
}
copy(data.mfgName[:], str)
return nil
}
func (data *DrvInfoData) ProviderName() string {
return windows.UTF16ToString(data.providerName[:])
}
func (data *DrvInfoData) SetProviderName(providerName string) error {
str, err := windows.UTF16FromString(providerName)
if err != nil {
return err
}
copy(data.providerName[:], str)
return nil
}
// IsNewer method returns true if DrvInfoData date and version is newer than supplied parameters.
func (data *DrvInfoData) IsNewer(driverDate windows.Filetime, driverVersion uint64) bool {
if data.DriverDate.HighDateTime > driverDate.HighDateTime {
return true
}
if data.DriverDate.HighDateTime < driverDate.HighDateTime {
return false
}
if data.DriverDate.LowDateTime > driverDate.LowDateTime {
return true
}
if data.DriverDate.LowDateTime < driverDate.LowDateTime {
return false
}
if data.DriverVersion > driverVersion {
return true
}
if data.DriverVersion < driverVersion {
return false
}
return false
}
// DrvInfoDetailData is driver information details structure (provides detailed information about a particular driver information structure)
type DrvInfoDetailData struct {
size uint32 // Warning: unsafe.Sizeof(DrvInfoDetailData) > sizeof(SP_DRVINFO_DETAIL_DATA) when GOARCH == 386 => use sizeofDrvInfoDetailData const.
InfDate windows.Filetime
compatIDsOffset uint32
compatIDsLength uint32
_ uintptr
sectionName [LINE_LEN]uint16
infFileName [windows.MAX_PATH]uint16
drvDescription [LINE_LEN]uint16
hardwareID [ANYSIZE_ARRAY]uint16
}
func (data *DrvInfoDetailData) SectionName() string {
return windows.UTF16ToString(data.sectionName[:])
}
func (data *DrvInfoDetailData) InfFileName() string {
return windows.UTF16ToString(data.infFileName[:])
}
func (data *DrvInfoDetailData) DrvDescription() string {
return windows.UTF16ToString(data.drvDescription[:])
}
func (data *DrvInfoDetailData) HardwareID() string {
if data.compatIDsOffset > 1 {
bufW := data.getBuf()
return windows.UTF16ToString(bufW[:wcslen(bufW)])
}
return ""
}
func (data *DrvInfoDetailData) CompatIDs() []string {
a := make([]string, 0)
if data.compatIDsLength > 0 {
bufW := data.getBuf()
bufW = bufW[data.compatIDsOffset : data.compatIDsOffset+data.compatIDsLength]
for i := 0; i < len(bufW); {
j := i + wcslen(bufW[i:])
if i < j {
a = append(a, windows.UTF16ToString(bufW[i:j]))
}
i = j + 1
}
}
return a
}
func (data *DrvInfoDetailData) getBuf() []uint16 {
len := (data.size - uint32(unsafe.Offsetof(data.hardwareID))) / 2
sl := struct {
addr *uint16
len int
cap int
}{&data.hardwareID[0], int(len), int(len)}
return *(*[]uint16)(unsafe.Pointer(&sl))
}
// IsCompatible method tests if given hardware ID matches the driver or is listed on the compatible ID list.
func (data *DrvInfoDetailData) IsCompatible(hwid string) bool {
hwidLC := strings.ToLower(hwid)
if strings.ToLower(data.HardwareID()) == hwidLC {
return true
}
a := data.CompatIDs()
for i := range a {
if strings.ToLower(a[i]) == hwidLC {
return true
}
}
return false
}
// DICD flags control SetupDiCreateDeviceInfo
type DICD uint32
const (
DICD_GENERATE_ID DICD = 0x00000001
DICD_INHERIT_CLASSDRVS DICD = 0x00000002
)
//
// SPDIT flags to distinguish between class drivers and
// device drivers.
// (Passed in 'DriverType' parameter of driver information list APIs)
//
type SPDIT uint32
const (
SPDIT_NODRIVER SPDIT = 0x00000000
SPDIT_CLASSDRIVER SPDIT = 0x00000001
SPDIT_COMPATDRIVER SPDIT = 0x00000002
)
// DIGCF flags control what is included in the device information set built by SetupDiGetClassDevs
type DIGCF uint32
const (
DIGCF_DEFAULT DIGCF = 0x00000001 // only valid with DIGCF_DEVICEINTERFACE
DIGCF_PRESENT DIGCF = 0x00000002
DIGCF_ALLCLASSES DIGCF = 0x00000004
DIGCF_PROFILE DIGCF = 0x00000008
DIGCF_DEVICEINTERFACE DIGCF = 0x00000010
)
// DIREG specifies values for SetupDiCreateDevRegKey, SetupDiOpenDevRegKey, and SetupDiDeleteDevRegKey.
type DIREG uint32
const (
DIREG_DEV DIREG = 0x00000001 // Open/Create/Delete device key
DIREG_DRV DIREG = 0x00000002 // Open/Create/Delete driver key
DIREG_BOTH DIREG = 0x00000004 // Delete both driver and Device key
)
//
// SPDRP specifies device registry property codes
// (Codes marked as read-only (R) may only be used for
// SetupDiGetDeviceRegistryProperty)
//
// These values should cover the same set of registry properties
// as defined by the CM_DRP codes in cfgmgr32.h.
//
// Note that SPDRP codes are zero based while CM_DRP codes are one based!
//
type SPDRP uint32
const (
SPDRP_DEVICEDESC SPDRP = 0x00000000 // DeviceDesc (R/W)
SPDRP_HARDWAREID SPDRP = 0x00000001 // HardwareID (R/W)
SPDRP_COMPATIBLEIDS SPDRP = 0x00000002 // CompatibleIDs (R/W)
SPDRP_SERVICE SPDRP = 0x00000004 // Service (R/W)
SPDRP_CLASS SPDRP = 0x00000007 // Class (R--tied to ClassGUID)
SPDRP_CLASSGUID SPDRP = 0x00000008 // ClassGUID (R/W)
SPDRP_DRIVER SPDRP = 0x00000009 // Driver (R/W)
SPDRP_CONFIGFLAGS SPDRP = 0x0000000A // ConfigFlags (R/W)
SPDRP_MFG SPDRP = 0x0000000B // Mfg (R/W)
SPDRP_FRIENDLYNAME SPDRP = 0x0000000C // FriendlyName (R/W)
SPDRP_LOCATION_INFORMATION SPDRP = 0x0000000D // LocationInformation (R/W)
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME SPDRP = 0x0000000E // PhysicalDeviceObjectName (R)
SPDRP_CAPABILITIES SPDRP = 0x0000000F // Capabilities (R)
SPDRP_UI_NUMBER SPDRP = 0x00000010 // UiNumber (R)
SPDRP_UPPERFILTERS SPDRP = 0x00000011 // UpperFilters (R/W)
SPDRP_LOWERFILTERS SPDRP = 0x00000012 // LowerFilters (R/W)
SPDRP_BUSTYPEGUID SPDRP = 0x00000013 // BusTypeGUID (R)
SPDRP_LEGACYBUSTYPE SPDRP = 0x00000014 // LegacyBusType (R)
SPDRP_BUSNUMBER SPDRP = 0x00000015 // BusNumber (R)
SPDRP_ENUMERATOR_NAME SPDRP = 0x00000016 // Enumerator Name (R)
SPDRP_SECURITY SPDRP = 0x00000017 // Security (R/W, binary form)
SPDRP_SECURITY_SDS SPDRP = 0x00000018 // Security (W, SDS form)
SPDRP_DEVTYPE SPDRP = 0x00000019 // Device Type (R/W)
SPDRP_EXCLUSIVE SPDRP = 0x0000001A // Device is exclusive-access (R/W)
SPDRP_CHARACTERISTICS SPDRP = 0x0000001B // Device Characteristics (R/W)
SPDRP_ADDRESS SPDRP = 0x0000001C // Device Address (R)
SPDRP_UI_NUMBER_DESC_FORMAT SPDRP = 0x0000001D // UiNumberDescFormat (R/W)
SPDRP_DEVICE_POWER_DATA SPDRP = 0x0000001E // Device Power Data (R)
SPDRP_REMOVAL_POLICY SPDRP = 0x0000001F // Removal Policy (R)
SPDRP_REMOVAL_POLICY_HW_DEFAULT SPDRP = 0x00000020 // Hardware Removal Policy (R)
SPDRP_REMOVAL_POLICY_OVERRIDE SPDRP = 0x00000021 // Removal Policy Override (RW)
SPDRP_INSTALL_STATE SPDRP = 0x00000022 // Device Install State (R)
SPDRP_LOCATION_PATHS SPDRP = 0x00000023 // Device Location Paths (R)
SPDRP_BASE_CONTAINERID SPDRP = 0x00000024 // Base ContainerID (R)
SPDRP_MAXIMUM_PROPERTY SPDRP = 0x00000025 // Upper bound on ordinals
)
const (
CR_SUCCESS = 0x0
CR_BUFFER_SMALL = 0x1a
)
const (
CM_GET_DEVICE_INTERFACE_LIST_PRESENT = 0 // only currently 'live' device interfaces
CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES = 1 // all registered device interfaces, live or not
)

View File

@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package setupapi
const (
sizeofDevInfoListDetailData uint32 = 550
sizeofDrvInfoDetailData uint32 = 1570
)

View File

@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package setupapi
const (
sizeofDevInfoListDetailData uint32 = 560
sizeofDrvInfoDetailData uint32 = 1584
)

View File

@@ -0,0 +1,398 @@
// Code generated by 'go generate'; DO NOT EDIT.
package setupapi
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return nil
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modsetupapi = windows.NewLazySystemDLL("setupapi.dll")
modCfgMgr32 = windows.NewLazySystemDLL("CfgMgr32.dll")
procSetupDiCreateDeviceInfoListExW = modsetupapi.NewProc("SetupDiCreateDeviceInfoListExW")
procSetupDiGetDeviceInfoListDetailW = modsetupapi.NewProc("SetupDiGetDeviceInfoListDetailW")
procSetupDiCreateDeviceInfoW = modsetupapi.NewProc("SetupDiCreateDeviceInfoW")
procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo")
procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList")
procSetupDiBuildDriverInfoList = modsetupapi.NewProc("SetupDiBuildDriverInfoList")
procSetupDiCancelDriverInfoSearch = modsetupapi.NewProc("SetupDiCancelDriverInfoSearch")
procSetupDiEnumDriverInfoW = modsetupapi.NewProc("SetupDiEnumDriverInfoW")
procSetupDiGetSelectedDriverW = modsetupapi.NewProc("SetupDiGetSelectedDriverW")
procSetupDiSetSelectedDriverW = modsetupapi.NewProc("SetupDiSetSelectedDriverW")
procSetupDiGetDriverInfoDetailW = modsetupapi.NewProc("SetupDiGetDriverInfoDetailW")
procSetupDiDestroyDriverInfoList = modsetupapi.NewProc("SetupDiDestroyDriverInfoList")
procSetupDiGetClassDevsExW = modsetupapi.NewProc("SetupDiGetClassDevsExW")
procSetupDiCallClassInstaller = modsetupapi.NewProc("SetupDiCallClassInstaller")
procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey")
procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW")
procSetupDiSetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiSetDeviceRegistryPropertyW")
procSetupDiGetDeviceInstallParamsW = modsetupapi.NewProc("SetupDiGetDeviceInstallParamsW")
procSetupDiGetDeviceInstanceIdW = modsetupapi.NewProc("SetupDiGetDeviceInstanceIdW")
procSetupDiGetClassInstallParamsW = modsetupapi.NewProc("SetupDiGetClassInstallParamsW")
procSetupDiSetDeviceInstallParamsW = modsetupapi.NewProc("SetupDiSetDeviceInstallParamsW")
procSetupDiSetClassInstallParamsW = modsetupapi.NewProc("SetupDiSetClassInstallParamsW")
procSetupDiClassNameFromGuidExW = modsetupapi.NewProc("SetupDiClassNameFromGuidExW")
procSetupDiClassGuidsFromNameExW = modsetupapi.NewProc("SetupDiClassGuidsFromNameExW")
procSetupDiGetSelectedDevice = modsetupapi.NewProc("SetupDiGetSelectedDevice")
procSetupDiSetSelectedDevice = modsetupapi.NewProc("SetupDiSetSelectedDevice")
procCM_Get_Device_Interface_List_SizeW = modCfgMgr32.NewProc("CM_Get_Device_Interface_List_SizeW")
procCM_Get_Device_Interface_ListW = modCfgMgr32.NewProc("CM_Get_Device_Interface_ListW")
)
func setupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName *uint16, reserved uintptr) (handle DevInfo, err error) {
r0, _, e1 := syscall.Syscall6(procSetupDiCreateDeviceInfoListExW.Addr(), 4, uintptr(unsafe.Pointer(classGUID)), uintptr(hwndParent), uintptr(unsafe.Pointer(machineName)), uintptr(reserved), 0, 0)
handle = DevInfo(r0)
if handle == DevInfo(windows.InvalidHandle) {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo, deviceInfoSetDetailData *DevInfoListDetailData) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiGetDeviceInfoListDetailW.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoSetDetailData)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiCreateDeviceInfo(deviceInfoSet DevInfo, DeviceName *uint16, classGUID *windows.GUID, DeviceDescription *uint16, hwndParent uintptr, CreationFlags DICD, deviceInfoData *DevInfoData) (err error) {
r1, _, e1 := syscall.Syscall9(procSetupDiCreateDeviceInfoW.Addr(), 7, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(DeviceName)), uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(DeviceDescription)), uintptr(hwndParent), uintptr(CreationFlags), uintptr(unsafe.Pointer(deviceInfoData)), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex uint32, deviceInfoData *DevInfoData) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiEnumDeviceInfo.Addr(), 3, uintptr(deviceInfoSet), uintptr(memberIndex), uintptr(unsafe.Pointer(deviceInfoData)))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func SetupDiDestroyDeviceInfoList(deviceInfoSet DevInfo) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiDestroyDeviceInfoList.Addr(), 1, uintptr(deviceInfoSet), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func SetupDiBuildDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiBuildDriverInfoList.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func SetupDiCancelDriverInfoSearch(deviceInfoSet DevInfo) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiCancelDriverInfoSearch.Addr(), 1, uintptr(deviceInfoSet), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex uint32, driverInfoData *DrvInfoData) (err error) {
r1, _, e1 := syscall.Syscall6(procSetupDiEnumDriverInfoW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType), uintptr(memberIndex), uintptr(unsafe.Pointer(driverInfoData)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiGetSelectedDriverW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func SetupDiSetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiSetSelectedDriverW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData, driverInfoDetailData *DrvInfoDetailData, driverInfoDetailDataSize uint32, requiredSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procSetupDiGetDriverInfoDetailW.Addr(), 6, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)), uintptr(unsafe.Pointer(driverInfoDetailData)), uintptr(driverInfoDetailDataSize), uintptr(unsafe.Pointer(requiredSize)))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func SetupDiDestroyDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiDestroyDriverInfoList.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetClassDevsEx(classGUID *windows.GUID, Enumerator *uint16, hwndParent uintptr, Flags DIGCF, deviceInfoSet DevInfo, machineName *uint16, reserved uintptr) (handle DevInfo, err error) {
r0, _, e1 := syscall.Syscall9(procSetupDiGetClassDevsExW.Addr(), 7, uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(Enumerator)), uintptr(hwndParent), uintptr(Flags), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(machineName)), uintptr(reserved), 0, 0)
handle = DevInfo(r0)
if handle == DevInfo(windows.InvalidHandle) {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func SetupDiCallClassInstaller(installFunction DI_FUNCTION, deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiCallClassInstaller.Addr(), 3, uintptr(installFunction), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (key windows.Handle, err error) {
r0, _, e1 := syscall.Syscall6(procSetupDiOpenDevRegKey.Addr(), 6, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(Scope), uintptr(HwProfile), uintptr(KeyType), uintptr(samDesired))
key = windows.Handle(r0)
if key == windows.InvalidHandle {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyRegDataType *uint32, propertyBuffer *byte, propertyBufferSize uint32, requiredSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyRegDataType)), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), uintptr(unsafe.Pointer(requiredSize)), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffer *byte, propertyBufferSize uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procSetupDiSetDeviceRegistryPropertyW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiGetDeviceInstallParamsW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams)))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetDeviceInstanceId(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, instanceId *uint16, instanceIdSize uint32, instanceIdRequiredSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procSetupDiGetDeviceInstanceIdW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(instanceId)), uintptr(instanceIdSize), uintptr(unsafe.Pointer(instanceIdRequiredSize)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func SetupDiGetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procSetupDiGetClassInstallParamsW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize), uintptr(unsafe.Pointer(requiredSize)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func SetupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiSetDeviceInstallParamsW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams)))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func SetupDiSetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procSetupDiSetClassInstallParamsW.Addr(), 4, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiClassNameFromGuidEx(classGUID *windows.GUID, className *uint16, classNameSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) {
r1, _, e1 := syscall.Syscall6(procSetupDiClassNameFromGuidExW.Addr(), 6, uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(className)), uintptr(classNameSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiClassGuidsFromNameEx(className *uint16, classGuidList *windows.GUID, classGuidListSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) {
r1, _, e1 := syscall.Syscall6(procSetupDiClassGuidsFromNameExW.Addr(), 6, uintptr(unsafe.Pointer(className)), uintptr(unsafe.Pointer(classGuidList)), uintptr(classGuidListSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiGetSelectedDevice.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func SetupDiSetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiSetSelectedDevice.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func cm_Get_Device_Interface_List_Size(len *uint32, interfaceClass *windows.GUID, deviceID *uint16, flags uint32) (ret uint32) {
r0, _, _ := syscall.Syscall6(procCM_Get_Device_Interface_List_SizeW.Addr(), 4, uintptr(unsafe.Pointer(len)), uintptr(unsafe.Pointer(interfaceClass)), uintptr(unsafe.Pointer(deviceID)), uintptr(flags), 0, 0)
ret = uint32(r0)
return
}
func cm_Get_Device_Interface_List(interfaceClass *windows.GUID, deviceID *uint16, buffer *uint16, bufferLen uint32, flags uint32) (ret uint32) {
r0, _, _ := syscall.Syscall6(procCM_Get_Device_Interface_ListW.Addr(), 5, uintptr(unsafe.Pointer(interfaceClass)), uintptr(unsafe.Pointer(deviceID)), uintptr(unsafe.Pointer(buffer)), uintptr(bufferLen), uintptr(flags), 0)
ret = uint32(r0)
return
}

Some files were not shown because too many files have changed in this diff Show More