appium-ios-tuntap 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +20 -3
- package/binding.gyp +1 -1
- package/lib/platform/create-platform.js +3 -0
- package/lib/platform/require-admin.d.ts +17 -0
- package/lib/platform/require-admin.js +32 -0
- package/lib/platform/windows.d.ts +13 -0
- package/lib/platform/windows.js +195 -0
- package/lib/tunnel.d.ts +1 -1
- package/package.json +7 -3
- package/prebuilds/darwin-arm64/appium-ios-tuntap.node +0 -0
- package/prebuilds/darwin-x64/appium-ios-tuntap.node +0 -0
- package/prebuilds/win32-arm64/appium-ios-tuntap.node +0 -0
- package/prebuilds/win32-x64/appium-ios-tuntap.node +0 -0
- package/scripts/fetch-wintun.mjs +70 -0
- package/src/native/wintun_loader.cc +26 -0
- package/vendor/wintun/LICENSE.txt +84 -0
- package/vendor/wintun/README.md +37 -0
- package/vendor/wintun/bin/amd64/wintun.dll +0 -0
- package/vendor/wintun/bin/arm/wintun.dll +0 -0
- package/vendor/wintun/bin/arm64/wintun.dll +0 -0
- package/vendor/wintun/bin/x86/wintun.dll +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [0.4.0](https://github.com/appium/appium-ios-tuntap/compare/v0.3.0...v0.4.0) (2026-05-30)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* implement Windows (WinTun) JavaScript platform layer ([#44](https://github.com/appium/appium-ios-tuntap/issues/44)) ([da2a9bb](https://github.com/appium/appium-ios-tuntap/commit/da2a9bba1d7226f67ce0f00c533df72164aac858))
|
|
6
|
+
|
|
1
7
|
## [0.3.0](https://github.com/appium/appium-ios-tuntap/compare/v0.2.5...v0.3.0) (2026-05-22)
|
|
2
8
|
|
|
3
9
|
### Features
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# TunTap Bridge
|
|
2
2
|
|
|
3
|
-
A native TUN/TAP interface module for Node.js that works on
|
|
3
|
+
A native TUN/TAP interface module for Node.js that works on macOS, Linux, and Windows, with enhanced error handling, signal management, and thread safety.
|
|
4
4
|
|
|
5
5
|
## Description
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ This module provides a Node.js interface to TUN/TAP virtual network devices, all
|
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
- **Cross-platform**: Works on macOS (utun)
|
|
11
|
+
- **Cross-platform**: Works on macOS (utun), Linux (TUN/TAP), and Windows (WinTun)
|
|
12
12
|
- **TypeScript support**: Full TypeScript definitions included
|
|
13
13
|
- **Signal handling**: Graceful shutdown on SIGINT/SIGTERM
|
|
14
14
|
- **Thread safety**: Safe to use from multiple Node.js worker threads
|
|
@@ -88,6 +88,14 @@ On Linux, the module requires:
|
|
|
88
88
|
sudo pacman -S linux-headers
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
### Windows
|
|
92
|
+
|
|
93
|
+
On Windows the module uses [WinTun](https://www.wintun.net/) (the same userspace TUN driver shipped with WireGuard). Requirements:
|
|
94
|
+
|
|
95
|
+
1. **`wintun.dll`**: ships with the package. The official signed binaries for `amd64`, `arm64`, `x86`, and `arm` are bundled under `vendor/wintun/bin/<arch>/wintun.dll`; the addon discovers the right one automatically based on its own compile-time architecture. No download or copy step is required.
|
|
96
|
+
2. **Administrator privileges**: required to create the kernel adapter and configure addresses/routes via `netsh`. Launch your shell with **Run as administrator**.
|
|
97
|
+
3. **Build toolchain (only if compiling from source)**: Visual Studio Build Tools 2022 with the C++ workload, the Windows 10 SDK, and Python 3.x on `PATH`.
|
|
98
|
+
|
|
91
99
|
## Usage
|
|
92
100
|
|
|
93
101
|
### Basic Usage
|
|
@@ -207,7 +215,7 @@ socket.connect(port, host, async () => {
|
|
|
207
215
|
|
|
208
216
|
#### Properties
|
|
209
217
|
- `name: string` - The device name (e.g., 'utun0', 'tun0')
|
|
210
|
-
- `fd: number` - The file descriptor
|
|
218
|
+
- `fd: number` - The native file descriptor on POSIX (macOS/Linux). Returns `-1` on Windows; Wintun does not expose a numeric file descriptor.
|
|
211
219
|
|
|
212
220
|
### Error Types
|
|
213
221
|
|
|
@@ -294,3 +302,12 @@ This ensures the signal handler works as intended.
|
|
|
294
302
|
## License
|
|
295
303
|
|
|
296
304
|
Apache-2.0
|
|
305
|
+
|
|
306
|
+
### Third-party software
|
|
307
|
+
|
|
308
|
+
This package redistributes the official signed **WinTun** DLLs (version 0.14.1) from [wintun.net](https://www.wintun.net/) under the bundled-binary license shipped by the WinTun project. The unmodified binaries and the upstream license live under [vendor/wintun/](vendor/wintun/):
|
|
309
|
+
|
|
310
|
+
- `vendor/wintun/bin/{amd64,arm64,x86,arm}/wintun.dll`
|
|
311
|
+
- `vendor/wintun/LICENSE.txt` — the upstream WinTun license; required when redistributing the DLL
|
|
312
|
+
|
|
313
|
+
Maintainers can refresh the bundled binaries with `npm run refresh:wintun` after bumping `WINTUN_VERSION` in [scripts/fetch-wintun.mjs](scripts/fetch-wintun.mjs).
|
package/binding.gyp
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DarwinTunTapPlatform } from './darwin.js';
|
|
2
2
|
import { LinuxTunTapPlatform } from './linux.js';
|
|
3
3
|
import { UnsupportedTunTapPlatform } from './unsupported.js';
|
|
4
|
+
import { WindowsTunTapPlatform } from './windows.js';
|
|
4
5
|
/** @internal Built-in {@link TunTapPlatform} for a Node `process.platform` value. */
|
|
5
6
|
export function createTunTapPlatform(platform) {
|
|
6
7
|
switch (platform) {
|
|
@@ -8,6 +9,8 @@ export function createTunTapPlatform(platform) {
|
|
|
8
9
|
return new DarwinTunTapPlatform();
|
|
9
10
|
case 'linux':
|
|
10
11
|
return new LinuxTunTapPlatform();
|
|
12
|
+
case 'win32':
|
|
13
|
+
return new WindowsTunTapPlatform();
|
|
11
14
|
default:
|
|
12
15
|
return new UnsupportedTunTapPlatform(platform);
|
|
13
16
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Throws {@link TunTapPermissionError} unless the current process is an
|
|
3
|
+
* elevated (Administrator) Windows process. Mirrors `assertEffectiveRoot`
|
|
4
|
+
* from {@link ./require-root.ts} for POSIX.
|
|
5
|
+
*/
|
|
6
|
+
export declare function assertAdminOnWindows(): Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* Returns true when the current process is running with Administrator
|
|
9
|
+
* privileges on Windows. Implementation runs `net session` (which always
|
|
10
|
+
* exists, regardless of locale) and inspects the exit code.
|
|
11
|
+
*
|
|
12
|
+
* The result is memoized for the lifetime of the process; admin status cannot
|
|
13
|
+
* change between calls without restarting the shell.
|
|
14
|
+
*/
|
|
15
|
+
export declare const isAdministrator: (() => Promise<boolean>) & {
|
|
16
|
+
cache: Map<unknown, Promise<boolean>>;
|
|
17
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { util } from '@appium/support';
|
|
2
|
+
import { TunTapPermissionError } from '../errors.js';
|
|
3
|
+
import { execFileAsync } from './exec.js';
|
|
4
|
+
/**
|
|
5
|
+
* Throws {@link TunTapPermissionError} unless the current process is an
|
|
6
|
+
* elevated (Administrator) Windows process. Mirrors `assertEffectiveRoot`
|
|
7
|
+
* from {@link ./require-root.ts} for POSIX.
|
|
8
|
+
*/
|
|
9
|
+
export async function assertAdminOnWindows() {
|
|
10
|
+
if (await isAdministrator()) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
throw new TunTapPermissionError('TUN interface configuration and routing require Administrator privileges on Windows. ' +
|
|
14
|
+
'Re-launch the shell with "Run as administrator".');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Returns true when the current process is running with Administrator
|
|
18
|
+
* privileges on Windows. Implementation runs `net session` (which always
|
|
19
|
+
* exists, regardless of locale) and inspects the exit code.
|
|
20
|
+
*
|
|
21
|
+
* The result is memoized for the lifetime of the process; admin status cannot
|
|
22
|
+
* change between calls without restarting the shell.
|
|
23
|
+
*/
|
|
24
|
+
export const isAdministrator = util.memoize(async function isAdministratorUncached() {
|
|
25
|
+
try {
|
|
26
|
+
await execFileAsync('net', ['session'], { windowsHide: true });
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TunTapInterfaceStats, TunTapPlatform } from './types.js';
|
|
2
|
+
/** Windows implementation backed by `netsh` for configuration/routing and
|
|
3
|
+
* PowerShell `Get-NetAdapterStatistics` for byte counters. */
|
|
4
|
+
export declare class WindowsTunTapPlatform implements TunTapPlatform {
|
|
5
|
+
/** @inheritdoc */
|
|
6
|
+
configure(interfaceName: string, address: string, mtu: number): Promise<void>;
|
|
7
|
+
/** @inheritdoc */
|
|
8
|
+
addRoute(interfaceName: string, destination: string): Promise<void>;
|
|
9
|
+
/** @inheritdoc */
|
|
10
|
+
removeRoute(interfaceName: string, destination: string): Promise<void>;
|
|
11
|
+
/** @inheritdoc */
|
|
12
|
+
getStats(interfaceName: string): Promise<TunTapInterfaceStats>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { TunTapError } from '../errors.js';
|
|
2
|
+
import { log } from '../logger.js';
|
|
3
|
+
import { assertAdminOnWindows } from './require-admin.js';
|
|
4
|
+
import { execFileAsync } from './exec.js';
|
|
5
|
+
/** Tightly-restricted character set for adapter names passed into PowerShell. */
|
|
6
|
+
const SAFE_NAME_RE = /^[A-Za-z0-9_\- ]+$/;
|
|
7
|
+
/** Phrases that indicate `netsh` could not find the requested route/address. */
|
|
8
|
+
const MISSING_TARGET_HINTS = [
|
|
9
|
+
'element not found',
|
|
10
|
+
'cannot find',
|
|
11
|
+
'no matching',
|
|
12
|
+
'does not exist',
|
|
13
|
+
'not found',
|
|
14
|
+
];
|
|
15
|
+
/** Windows implementation backed by `netsh` for configuration/routing and
|
|
16
|
+
* PowerShell `Get-NetAdapterStatistics` for byte counters. */
|
|
17
|
+
export class WindowsTunTapPlatform {
|
|
18
|
+
/** @inheritdoc */
|
|
19
|
+
async configure(interfaceName, address, mtu) {
|
|
20
|
+
await assertAdminOnWindows();
|
|
21
|
+
assertSafeAdapterName(interfaceName);
|
|
22
|
+
log.debug(`[win] configure: interface=${interfaceName} address=${address} mtu=${mtu}`);
|
|
23
|
+
await addIpv6Address(interfaceName, address);
|
|
24
|
+
await setIpv6Mtu(interfaceName, mtu);
|
|
25
|
+
}
|
|
26
|
+
/** @inheritdoc */
|
|
27
|
+
async addRoute(interfaceName, destination) {
|
|
28
|
+
await assertAdminOnWindows();
|
|
29
|
+
assertSafeAdapterName(interfaceName);
|
|
30
|
+
log.debug(`[win] addRoute: interface=${interfaceName} destination=${destination}`);
|
|
31
|
+
await addIpv6Route(interfaceName, destination);
|
|
32
|
+
// WinTun presents as an Ethernet adapter, so Windows requires Neighbor
|
|
33
|
+
// Discovery (NDP) before it will send packets through the interface.
|
|
34
|
+
// For /128 host routes we seed a static neighbor entry so NDP is bypassed
|
|
35
|
+
// and the first connection attempt is not silently dropped.
|
|
36
|
+
if (destination.endsWith('/128')) {
|
|
37
|
+
const address = destination.slice(0, -4);
|
|
38
|
+
await addStaticNeighbor(interfaceName, address);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** @inheritdoc */
|
|
42
|
+
async removeRoute(interfaceName, destination) {
|
|
43
|
+
await assertAdminOnWindows();
|
|
44
|
+
assertSafeAdapterName(interfaceName);
|
|
45
|
+
await deleteIpv6Route(interfaceName, destination);
|
|
46
|
+
}
|
|
47
|
+
/** @inheritdoc */
|
|
48
|
+
async getStats(interfaceName) {
|
|
49
|
+
assertSafeAdapterName(interfaceName);
|
|
50
|
+
const script = `Get-NetAdapterStatistics -Name '${interfaceName}' ` +
|
|
51
|
+
'| Select-Object ReceivedBytes,SentBytes,ReceivedUnicastPackets,' +
|
|
52
|
+
'SentUnicastPackets,ReceivedDiscardedPackets,OutboundDiscardedPackets ' +
|
|
53
|
+
'| ConvertTo-Json -Compress';
|
|
54
|
+
const { stdout } = await execFileAsync('powershell', [
|
|
55
|
+
'-NoProfile',
|
|
56
|
+
'-NonInteractive',
|
|
57
|
+
'-ExecutionPolicy',
|
|
58
|
+
'Bypass',
|
|
59
|
+
'-Command',
|
|
60
|
+
script,
|
|
61
|
+
]);
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(stdout);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
throw new TunTapError(`Failed to parse Get-NetAdapterStatistics output: ${stdout.trim()}`);
|
|
68
|
+
}
|
|
69
|
+
const num = (key) => {
|
|
70
|
+
const value = parsed[key];
|
|
71
|
+
const n = typeof value === 'number' ? value : parseInt(String(value ?? ''), 10);
|
|
72
|
+
return Number.isFinite(n) ? n : 0;
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
rxBytes: num('ReceivedBytes'),
|
|
76
|
+
rxPackets: num('ReceivedUnicastPackets'),
|
|
77
|
+
rxErrors: num('ReceivedDiscardedPackets'),
|
|
78
|
+
txBytes: num('SentBytes'),
|
|
79
|
+
txPackets: num('SentUnicastPackets'),
|
|
80
|
+
txErrors: num('OutboundDiscardedPackets'),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Validates an adapter name before embedding in a PowerShell expression. */
|
|
85
|
+
function assertSafeAdapterName(interfaceName) {
|
|
86
|
+
if (!SAFE_NAME_RE.test(interfaceName)) {
|
|
87
|
+
throw new TunTapError(`Refusing to use adapter name with unsupported characters: ${JSON.stringify(interfaceName)}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function isMissingTargetError(err) {
|
|
91
|
+
const message = String(err?.message ?? '').toLowerCase();
|
|
92
|
+
return MISSING_TARGET_HINTS.some((hint) => message.includes(hint));
|
|
93
|
+
}
|
|
94
|
+
async function addIpv6Address(interfaceName, address) {
|
|
95
|
+
try {
|
|
96
|
+
const r = await execFileAsync('netsh', [
|
|
97
|
+
'interface',
|
|
98
|
+
'ipv6',
|
|
99
|
+
'add',
|
|
100
|
+
'address',
|
|
101
|
+
`interface=${interfaceName}`,
|
|
102
|
+
`address=${address}/64`,
|
|
103
|
+
'store=active',
|
|
104
|
+
]);
|
|
105
|
+
log.debug(`[win] add address ok: ${r.stdout.trim() || '(no output)'}`);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const message = err.message ?? '';
|
|
109
|
+
log.warn(`[win] add address err: ${message}`);
|
|
110
|
+
if (!/already exists|object already/i.test(message)) {
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
log.warn(`Address ${address} may already be configured on ${interfaceName}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function setIpv6Mtu(interfaceName, mtu) {
|
|
117
|
+
try {
|
|
118
|
+
const r = await execFileAsync('netsh', [
|
|
119
|
+
'interface',
|
|
120
|
+
'ipv6',
|
|
121
|
+
'set',
|
|
122
|
+
'subinterface',
|
|
123
|
+
interfaceName,
|
|
124
|
+
`mtu=${mtu}`,
|
|
125
|
+
'store=active',
|
|
126
|
+
]);
|
|
127
|
+
log.debug(`[win] set mtu ok: ${r.stdout.trim() || '(no output)'}`);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
log.warn(`[win] set mtu err: ${err.message ?? err}`);
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function addIpv6Route(interfaceName, destination) {
|
|
135
|
+
try {
|
|
136
|
+
const r = await execFileAsync('netsh', [
|
|
137
|
+
'interface',
|
|
138
|
+
'ipv6',
|
|
139
|
+
'add',
|
|
140
|
+
'route',
|
|
141
|
+
destination,
|
|
142
|
+
interfaceName,
|
|
143
|
+
'store=active',
|
|
144
|
+
]);
|
|
145
|
+
log.debug(`[win] add route ok: ${r.stdout.trim() || '(no output)'}`);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
const message = err.message ?? '';
|
|
149
|
+
log.warn(`[win] add route err: ${message}`);
|
|
150
|
+
if (/already exists|object already/i.test(message)) {
|
|
151
|
+
log.debug(`Route to ${destination} already exists`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function deleteIpv6Route(interfaceName, destination) {
|
|
158
|
+
try {
|
|
159
|
+
await execFileAsync('netsh', [
|
|
160
|
+
'interface',
|
|
161
|
+
'ipv6',
|
|
162
|
+
'delete',
|
|
163
|
+
'route',
|
|
164
|
+
destination,
|
|
165
|
+
interfaceName,
|
|
166
|
+
'store=active',
|
|
167
|
+
]);
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
if (isMissingTargetError(err)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
throw err;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function addStaticNeighbor(interfaceName, address) {
|
|
177
|
+
log.debug(`[win] addStaticNeighbor: interface=${interfaceName} address=${address}`);
|
|
178
|
+
try {
|
|
179
|
+
const r = await execFileAsync('netsh', [
|
|
180
|
+
'interface',
|
|
181
|
+
'ipv6',
|
|
182
|
+
'add',
|
|
183
|
+
'neighbor',
|
|
184
|
+
interfaceName,
|
|
185
|
+
address,
|
|
186
|
+
'00-00-00-00-00-01',
|
|
187
|
+
'store=active',
|
|
188
|
+
]);
|
|
189
|
+
log.debug(`[win] add neighbor ok: ${r.stdout.trim() || '(no output)'}`);
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
const msg = err.message ?? String(err);
|
|
193
|
+
log.warn(`[win] add neighbor err: ${msg}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
package/lib/tunnel.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-ios-tuntap",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Native TUN/TAP interface module for Node.js",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -15,16 +15,19 @@
|
|
|
15
15
|
"format": "prettier -w ./src ./test",
|
|
16
16
|
"format:check": "prettier --check ./src ./test",
|
|
17
17
|
"install": "node-gyp-build",
|
|
18
|
+
"refresh:wintun": "node scripts/fetch-wintun.mjs",
|
|
18
19
|
"prepare": "npm run build",
|
|
19
20
|
"test": "npm run test:integration && npm run test:unit",
|
|
20
|
-
"test:unit": "mocha
|
|
21
|
-
"test:integration": "mocha
|
|
21
|
+
"test:unit": "mocha \"test/unit/**/*.spec.mjs\" --exit --timeout 2m",
|
|
22
|
+
"test:integration": "mocha \"test/integration/**/*.spec.mjs\" --exit --timeout 2m"
|
|
22
23
|
},
|
|
23
24
|
"files": [
|
|
24
25
|
"src/tuntap.cc",
|
|
25
26
|
"src/native",
|
|
26
27
|
"lib",
|
|
27
28
|
"prebuilds",
|
|
29
|
+
"scripts",
|
|
30
|
+
"vendor",
|
|
28
31
|
"binding.gyp",
|
|
29
32
|
"package.json",
|
|
30
33
|
"README.md",
|
|
@@ -56,6 +59,7 @@
|
|
|
56
59
|
"@semantic-release/changelog": "^6.0.3",
|
|
57
60
|
"@semantic-release/git": "^10.0.1",
|
|
58
61
|
"@types/node": "^25.0.1",
|
|
62
|
+
"commander": "^14.0.3",
|
|
59
63
|
"conventional-changelog-conventionalcommits": "^9.0.0",
|
|
60
64
|
"mocha": "^11.7.5",
|
|
61
65
|
"prebuildify": "^6.0.1",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Maintainer-only helper that refreshes the bundled WinTun binaries committed
|
|
2
|
+
// under vendor/wintun/. The npm `install` hook does NOT invoke this script —
|
|
3
|
+
// the package ships with the official signed DLLs already checked in. Run
|
|
4
|
+
// `npm run refresh:wintun -- --version <semver>` to pull a different release.
|
|
5
|
+
|
|
6
|
+
import {fs, logger, net, tempDir, zip} from '@appium/support';
|
|
7
|
+
import {Command} from 'commander';
|
|
8
|
+
import {join, dirname} from 'node:path';
|
|
9
|
+
import {fileURLToPath} from 'node:url';
|
|
10
|
+
|
|
11
|
+
const log = logger.getLogger('refresh-wintun');
|
|
12
|
+
|
|
13
|
+
const DEFAULT_WINTUN_VERSION = '0.14.1';
|
|
14
|
+
const BUNDLED_ARCHES = ['amd64', 'arm64', 'x86', 'arm'];
|
|
15
|
+
|
|
16
|
+
const rootDir = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
17
|
+
const vendorDir = join(rootDir, 'vendor', 'wintun');
|
|
18
|
+
|
|
19
|
+
async function deployDll(arch, extractDir) {
|
|
20
|
+
const destDir = join(vendorDir, 'bin', arch);
|
|
21
|
+
await fs.mkdir(destDir, {recursive: true});
|
|
22
|
+
const src = join(extractDir, 'wintun', 'bin', arch, 'wintun.dll');
|
|
23
|
+
const dest = join(destDir, 'wintun.dll');
|
|
24
|
+
await fs.copyFile(src, dest);
|
|
25
|
+
log.info(`wintun.dll (${arch}) -> ${dest}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function deployLicense(extractDir) {
|
|
29
|
+
const src = join(extractDir, 'wintun', 'LICENSE.txt');
|
|
30
|
+
const dest = join(vendorDir, 'LICENSE.txt');
|
|
31
|
+
await fs.copyFile(src, dest);
|
|
32
|
+
log.info(`LICENSE.txt -> ${dest}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function refreshWintun(version) {
|
|
36
|
+
const url = `https://www.wintun.net/builds/wintun-${version}.zip`;
|
|
37
|
+
const tmpDir = await tempDir.openDir();
|
|
38
|
+
try {
|
|
39
|
+
const zipPath = join(tmpDir, 'wintun.zip');
|
|
40
|
+
const extractDir = join(tmpDir, 'out');
|
|
41
|
+
await fs.mkdir(extractDir, {recursive: true});
|
|
42
|
+
|
|
43
|
+
log.info(`Downloading WinTun ${version}...`);
|
|
44
|
+
await net.downloadFile(url, zipPath);
|
|
45
|
+
await zip.extractAllTo(zipPath, extractDir);
|
|
46
|
+
|
|
47
|
+
await fs.mkdir(vendorDir, {recursive: true});
|
|
48
|
+
await Promise.all([
|
|
49
|
+
...BUNDLED_ARCHES.map((arch) => deployDll(arch, extractDir)),
|
|
50
|
+
deployLicense(extractDir),
|
|
51
|
+
]);
|
|
52
|
+
} finally {
|
|
53
|
+
await fs.rimraf(tmpDir);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const program = new Command();
|
|
58
|
+
program
|
|
59
|
+
.name('refresh-wintun')
|
|
60
|
+
.description('Refresh the bundled WinTun binaries under vendor/wintun/')
|
|
61
|
+
.option(
|
|
62
|
+
'-v, --version <semver>',
|
|
63
|
+
'WinTun release version to download',
|
|
64
|
+
DEFAULT_WINTUN_VERSION,
|
|
65
|
+
)
|
|
66
|
+
.action(async (options) => {
|
|
67
|
+
await refreshWintun(options.version);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await program.parseAsync(process.argv);
|
|
@@ -8,6 +8,22 @@ namespace {
|
|
|
8
8
|
|
|
9
9
|
constexpr LPCWSTR kWintunDllName = L"wintun.dll";
|
|
10
10
|
|
|
11
|
+
// Compile-time arch slug used to find the bundled DLL under
|
|
12
|
+
// vendor/wintun/bin/<arch>/wintun.dll. The .node addon is built per-arch, so
|
|
13
|
+
// the arch of the host process is the same as the arch of this translation
|
|
14
|
+
// unit.
|
|
15
|
+
#if defined(_M_X64) || defined(__x86_64__)
|
|
16
|
+
constexpr LPCWSTR kVendoredArch = L"amd64";
|
|
17
|
+
#elif defined(_M_ARM64) || defined(__aarch64__)
|
|
18
|
+
constexpr LPCWSTR kVendoredArch = L"arm64";
|
|
19
|
+
#elif defined(_M_IX86) || defined(__i386__)
|
|
20
|
+
constexpr LPCWSTR kVendoredArch = L"x86";
|
|
21
|
+
#elif defined(_M_ARM) || defined(__arm__)
|
|
22
|
+
constexpr LPCWSTR kVendoredArch = L"arm";
|
|
23
|
+
#else
|
|
24
|
+
constexpr LPCWSTR kVendoredArch = L"amd64";
|
|
25
|
+
#endif
|
|
26
|
+
|
|
11
27
|
// Returns the directory that contains the addon module that this code is
|
|
12
28
|
// linked into. Used to locate `wintun.dll` shipped next to `tuntap.node`.
|
|
13
29
|
std::wstring GetAddonDirectory() {
|
|
@@ -75,6 +91,16 @@ bool WintunApi::Load(std::string& error) {
|
|
|
75
91
|
if (TryLoadFrom(local.c_str())) {
|
|
76
92
|
return ResolveEntryPoints(error);
|
|
77
93
|
}
|
|
94
|
+
|
|
95
|
+
// Bundled fallback: the package ships the official signed DLL under
|
|
96
|
+
// vendor/wintun/bin/<arch>/wintun.dll. Both build/Release and
|
|
97
|
+
// prebuilds/<plat>-<arch> are two directories below the package root,
|
|
98
|
+
// so the same relative path works in either install layout.
|
|
99
|
+
std::wstring vendored = addon_dir + L"\\..\\..\\vendor\\wintun\\bin\\" +
|
|
100
|
+
kVendoredArch + L"\\" + kWintunDllName;
|
|
101
|
+
if (TryLoadFrom(vendored.c_str())) {
|
|
102
|
+
return ResolveEntryPoints(error);
|
|
103
|
+
}
|
|
78
104
|
}
|
|
79
105
|
|
|
80
106
|
// Fall back to the OS-default search list (Application dir, System32, …)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Prebuilt Binaries License
|
|
2
|
+
-------------------------
|
|
3
|
+
|
|
4
|
+
1. DEFINITIONS. "Software" means the precise contents of the "wintun.dll"
|
|
5
|
+
files that are included in the .zip file that contains this document as
|
|
6
|
+
downloaded from wintun.net/builds.
|
|
7
|
+
|
|
8
|
+
2. LICENSE GRANT. WireGuard LLC grants to you a non-exclusive and
|
|
9
|
+
non-transferable right to use Software for lawful purposes under certain
|
|
10
|
+
obligations and limited rights as set forth in this agreement.
|
|
11
|
+
|
|
12
|
+
3. RESTRICTIONS. Software is owned and copyrighted by WireGuard LLC. It is
|
|
13
|
+
licensed, not sold. Title to Software and all associated intellectual
|
|
14
|
+
property rights are retained by WireGuard. You must not:
|
|
15
|
+
a. reverse engineer, decompile, disassemble, extract from, or otherwise
|
|
16
|
+
modify the Software;
|
|
17
|
+
b. modify or create derivative work based upon Software in whole or in
|
|
18
|
+
parts, except insofar as only the API interfaces of the "wintun.h" file
|
|
19
|
+
distributed alongside the Software (the "Permitted API") are used;
|
|
20
|
+
c. remove any proprietary notices, labels, or copyrights from the Software;
|
|
21
|
+
d. resell, redistribute, lease, rent, transfer, sublicense, or otherwise
|
|
22
|
+
transfer rights of the Software without the prior written consent of
|
|
23
|
+
WireGuard LLC, except insofar as the Software is distributed alongside
|
|
24
|
+
other software that uses the Software only via the Permitted API;
|
|
25
|
+
e. use the name of WireGuard LLC, the WireGuard project, the Wintun
|
|
26
|
+
project, or the names of its contributors to endorse or promote products
|
|
27
|
+
derived from the Software without specific prior written consent.
|
|
28
|
+
|
|
29
|
+
4. LIMITED WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF
|
|
30
|
+
ANY KIND. WIREGUARD LLC HEREBY EXCLUDES AND DISCLAIMS ALL IMPLIED OR
|
|
31
|
+
STATUTORY WARRANTIES, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
32
|
+
FOR A PARTICULAR PURPOSE, QUALITY, NON-INFRINGEMENT, TITLE, RESULTS,
|
|
33
|
+
EFFORTS, OR QUIET ENJOYMENT. THERE IS NO WARRANTY THAT THE PRODUCT WILL BE
|
|
34
|
+
ERROR-FREE OR WILL FUNCTION WITHOUT INTERRUPTION. YOU ASSUME THE ENTIRE
|
|
35
|
+
RISK FOR THE RESULTS OBTAINED USING THE PRODUCT. TO THE EXTENT THAT
|
|
36
|
+
WIREGUARD LLC MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW,
|
|
37
|
+
THE SCOPE AND DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER
|
|
38
|
+
SUCH LAW. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
|
|
39
|
+
WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR
|
|
40
|
+
A PARTICULAR PURPOSE OR NON-INFRINGEMENT ARE DISCLAIMED, EXCEPT TO THE
|
|
41
|
+
EXTENT THAT THESE DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
|
|
42
|
+
|
|
43
|
+
5. LIMITATION OF LIABILITY. To the extent not prohibited by law, in no event
|
|
44
|
+
WireGuard LLC or any third-party-developer will be liable for any lost
|
|
45
|
+
revenue, profit or data or for special, indirect, consequential, incidental
|
|
46
|
+
or punitive damages, however caused regardless of the theory of liability,
|
|
47
|
+
arising out of or related to the use of or inability to use Software, even
|
|
48
|
+
if WireGuard LLC has been advised of the possibility of such damages.
|
|
49
|
+
Solely you are responsible for determining the appropriateness of using
|
|
50
|
+
Software and accept full responsibility for all risks associated with its
|
|
51
|
+
exercise of rights under this agreement, including but not limited to the
|
|
52
|
+
risks and costs of program errors, compliance with applicable laws, damage
|
|
53
|
+
to or loss of data, programs or equipment, and unavailability or
|
|
54
|
+
interruption of operations. The foregoing limitations will apply even if
|
|
55
|
+
the above stated warranty fails of its essential purpose. You acknowledge,
|
|
56
|
+
that it is in the nature of software that software is complex and not
|
|
57
|
+
completely free of errors. In no event shall WireGuard LLC or any
|
|
58
|
+
third-party-developer be liable to you under any theory for any damages
|
|
59
|
+
suffered by you or any user of Software or for any special, incidental,
|
|
60
|
+
indirect, consequential or similar damages (including without limitation
|
|
61
|
+
damages for loss of business profits, business interruption, loss of
|
|
62
|
+
business information or any other pecuniary loss) arising out of the use or
|
|
63
|
+
inability to use Software, even if WireGuard LLC has been advised of the
|
|
64
|
+
possibility of such damages and regardless of the legal or quitable theory
|
|
65
|
+
(contract, tort, or otherwise) upon which the claim is based.
|
|
66
|
+
|
|
67
|
+
6. TERMINATION. This agreement is affected until terminated. You may
|
|
68
|
+
terminate this agreement at any time. This agreement will terminate
|
|
69
|
+
immediately without notice from WireGuard LLC if you fail to comply with
|
|
70
|
+
the terms and conditions of this agreement. Upon termination, you must
|
|
71
|
+
delete Software and all copies of Software and cease all forms of
|
|
72
|
+
distribution of Software.
|
|
73
|
+
|
|
74
|
+
7. SEVERABILITY. If any provision of this agreement is held to be
|
|
75
|
+
unenforceable, this agreement will remain in effect with the provision
|
|
76
|
+
omitted, unless omission would frustrate the intent of the parties, in
|
|
77
|
+
which case this agreement will immediately terminate.
|
|
78
|
+
|
|
79
|
+
8. RESERVATION OF RIGHTS. All rights not expressly granted in this agreement
|
|
80
|
+
are reserved by WireGuard LLC. For example, WireGuard LLC reserves the
|
|
81
|
+
right at any time to cease development of Software, to alter distribution
|
|
82
|
+
details, features, specifications, capabilities, functions, licensing
|
|
83
|
+
terms, release dates, APIs, ABIs, general availability, or other
|
|
84
|
+
characteristics of the Software.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# WinTun (vendored)
|
|
2
|
+
|
|
3
|
+
This directory contains the unmodified, official signed [WinTun](https://www.wintun.net/) binaries that this package loads at runtime on Windows.
|
|
4
|
+
|
|
5
|
+
- **Upstream:** https://www.wintun.net/
|
|
6
|
+
- **Version:** 0.14.1
|
|
7
|
+
- **Source archive:** https://www.wintun.net/builds/wintun-0.14.1.zip
|
|
8
|
+
- **License:** see [`LICENSE.txt`](./LICENSE.txt). The DLLs are redistributed under the upstream prebuilt-binary license, which permits bundling unmodified copies alongside software that uses the official `wintun.h` API.
|
|
9
|
+
|
|
10
|
+
## Layout
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
vendor/wintun/
|
|
14
|
+
LICENSE.txt # Upstream license — MUST ship with the DLLs
|
|
15
|
+
bin/amd64/wintun.dll # x64 (Intel/AMD)
|
|
16
|
+
bin/arm64/wintun.dll # ARM64
|
|
17
|
+
bin/x86/wintun.dll # 32-bit Intel/AMD
|
|
18
|
+
bin/arm/wintun.dll # 32-bit ARM
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The package's CI prebuild matrix only produces `win32-x64` and `win32-arm64` artifacts, so most users only ever load the `amd64` or `arm64` DLL. The 32-bit binaries are bundled for completeness so anyone compiling the native addon from source on `_M_IX86` / `_M_ARM` finds a matching DLL via the addon's compile-time arch lookup.
|
|
22
|
+
|
|
23
|
+
The native addon (`src/native/wintun_loader.cc`) discovers the DLL through this layout, so no install-time copy step is required.
|
|
24
|
+
|
|
25
|
+
## Refreshing the binaries
|
|
26
|
+
|
|
27
|
+
Maintainers can pull the latest official release with:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npm run refresh:wintun
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This invokes [`scripts/fetch-wintun.mjs`](../../scripts/fetch-wintun.mjs), which downloads the ZIP from `wintun.net`, extracts the per-architecture DLLs and `LICENSE.txt` into this directory, and overwrites the existing files. Bump `WINTUN_VERSION` in that script before running.
|
|
34
|
+
|
|
35
|
+
## Do not modify
|
|
36
|
+
|
|
37
|
+
The DLLs MUST be redistributed unmodified. Do not recompile, repackage, or rename them. If you need to update WinTun, run the refresh script against a new upstream release rather than hand-editing files in this directory.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|