appium-ios-tuntap 0.2.4 → 0.3.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 +12 -0
- package/binding.gyp +24 -5
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/appium-ios-tuntap.node +0 -0
- package/prebuilds/darwin-x64/appium-ios-tuntap.node +0 -0
- package/prebuilds/linux-arm64/appium-ios-tuntap.node +0 -0
- package/prebuilds/linux-x64/appium-ios-tuntap.node +0 -0
- package/src/native/handle.cc +59 -0
- package/src/native/handle.h +30 -0
- package/src/native/posix_tun_backend.h +58 -0
- package/src/native/posix_uv_poll_loop.cc +137 -0
- package/src/native/posix_uv_poll_loop.h +55 -0
- package/src/native/tun_backend.h +55 -12
- package/src/native/tun_backend_darwin.cc +32 -25
- package/src/native/tun_backend_linux.cc +35 -27
- package/src/native/tun_backend_windows.cc +370 -0
- package/src/native/wintun_loader.cc +192 -0
- package/src/native/wintun_loader.h +93 -0
- package/src/tuntap.cc +75 -137
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [0.3.0](https://github.com/appium/appium-ios-tuntap/compare/v0.2.5...v0.3.0) (2026-05-22)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* add Windows (WinTun) native backend ([#43](https://github.com/appium/appium-ios-tuntap/issues/43)) ([565b4c1](https://github.com/appium/appium-ios-tuntap/commit/565b4c1cfd2ddf4956ed32ade4f8208cd0d4f0f6)), closes [#ifdef](https://github.com/appium/appium-ios-tuntap/issues/ifdef)
|
|
6
|
+
|
|
7
|
+
## [0.2.5](https://github.com/appium/appium-ios-tuntap/compare/v0.2.4...v0.2.5) (2026-05-14)
|
|
8
|
+
|
|
9
|
+
### Code Refactoring
|
|
10
|
+
|
|
11
|
+
* backends own fd, polling, and lifecycle ([#42](https://github.com/appium/appium-ios-tuntap/issues/42)) ([f089944](https://github.com/appium/appium-ios-tuntap/commit/f08994477cb9909b597386d0893abbf9b6a3c137))
|
|
12
|
+
|
|
1
13
|
## [0.2.4](https://github.com/appium/appium-ios-tuntap/compare/v0.2.3...v0.2.4) (2026-05-13)
|
|
2
14
|
|
|
3
15
|
### Code Refactoring
|
package/binding.gyp
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
{
|
|
4
4
|
"target_name": "tuntap",
|
|
5
5
|
"sources": [
|
|
6
|
-
"src/tuntap.cc"
|
|
7
|
-
"src/native/file_descriptor.cc"
|
|
6
|
+
"src/tuntap.cc"
|
|
8
7
|
],
|
|
9
8
|
"include_dirs": [
|
|
10
9
|
"<!@(node -p \"require('node-addon-api').include\")"
|
|
@@ -21,7 +20,7 @@
|
|
|
21
20
|
"-Wno-unused-parameter",
|
|
22
21
|
"-fPIC"
|
|
23
22
|
],
|
|
24
|
-
"cflags_cc": [
|
|
23
|
+
"cflags_cc": [
|
|
25
24
|
"-std=c++17",
|
|
26
25
|
"-Wno-vla-extension",
|
|
27
26
|
"-O3",
|
|
@@ -49,7 +48,7 @@
|
|
|
49
48
|
]
|
|
50
49
|
},
|
|
51
50
|
"msvs_settings": {
|
|
52
|
-
"VCCLCompilerTool": {
|
|
51
|
+
"VCCLCompilerTool": {
|
|
53
52
|
"ExceptionHandling": 1,
|
|
54
53
|
"AdditionalOptions": [
|
|
55
54
|
"/std:c++17",
|
|
@@ -57,13 +56,15 @@
|
|
|
57
56
|
]
|
|
58
57
|
}
|
|
59
58
|
},
|
|
60
|
-
"defines": [
|
|
59
|
+
"defines": [
|
|
61
60
|
"NAPI_CPP_EXCEPTIONS",
|
|
62
61
|
"NAPI_VERSION=8"
|
|
63
62
|
],
|
|
64
63
|
"conditions": [
|
|
65
64
|
["OS=='linux'", {
|
|
66
65
|
"sources": [
|
|
66
|
+
"src/native/file_descriptor.cc",
|
|
67
|
+
"src/native/posix_uv_poll_loop.cc",
|
|
67
68
|
"src/native/tun_backend_linux.cc"
|
|
68
69
|
],
|
|
69
70
|
"cflags": [
|
|
@@ -78,6 +79,8 @@
|
|
|
78
79
|
}],
|
|
79
80
|
["OS=='mac'", {
|
|
80
81
|
"sources": [
|
|
82
|
+
"src/native/file_descriptor.cc",
|
|
83
|
+
"src/native/posix_uv_poll_loop.cc",
|
|
81
84
|
"src/native/tun_backend_darwin.cc"
|
|
82
85
|
],
|
|
83
86
|
"xcode_settings": {
|
|
@@ -86,6 +89,22 @@
|
|
|
86
89
|
"-framework", "CoreFoundation"
|
|
87
90
|
]
|
|
88
91
|
}
|
|
92
|
+
}],
|
|
93
|
+
["OS=='win'", {
|
|
94
|
+
"sources": [
|
|
95
|
+
"src/native/handle.cc",
|
|
96
|
+
"src/native/wintun_loader.cc",
|
|
97
|
+
"src/native/tun_backend_windows.cc"
|
|
98
|
+
],
|
|
99
|
+
"libraries": [
|
|
100
|
+
"iphlpapi.lib",
|
|
101
|
+
"ws2_32.lib"
|
|
102
|
+
],
|
|
103
|
+
"defines": [
|
|
104
|
+
"_WIN32_WINNT=0x0A00",
|
|
105
|
+
"WIN32_LEAN_AND_MEAN",
|
|
106
|
+
"NOMINMAX"
|
|
107
|
+
]
|
|
89
108
|
}]
|
|
90
109
|
]
|
|
91
110
|
}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#ifdef _WIN32
|
|
2
|
+
|
|
3
|
+
#include "handle.h"
|
|
4
|
+
|
|
5
|
+
namespace {
|
|
6
|
+
|
|
7
|
+
bool IsRealHandle(HANDLE handle) {
|
|
8
|
+
return handle != nullptr && handle != INVALID_HANDLE_VALUE;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
} // namespace
|
|
12
|
+
|
|
13
|
+
Handle::Handle() : handle_(nullptr) {}
|
|
14
|
+
|
|
15
|
+
Handle::Handle(HANDLE handle) : handle_(handle) {}
|
|
16
|
+
|
|
17
|
+
Handle::~Handle() {
|
|
18
|
+
if (IsRealHandle(handle_)) {
|
|
19
|
+
::CloseHandle(handle_);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
Handle::Handle(Handle&& other) noexcept : handle_(other.handle_) {
|
|
24
|
+
other.handle_ = nullptr;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Handle& Handle::operator=(Handle&& other) noexcept {
|
|
28
|
+
if (this != &other) {
|
|
29
|
+
if (IsRealHandle(handle_)) {
|
|
30
|
+
::CloseHandle(handle_);
|
|
31
|
+
}
|
|
32
|
+
handle_ = other.handle_;
|
|
33
|
+
other.handle_ = nullptr;
|
|
34
|
+
}
|
|
35
|
+
return *this;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
HANDLE Handle::get() const {
|
|
39
|
+
return handle_;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
HANDLE Handle::release() {
|
|
43
|
+
HANDLE temp = handle_;
|
|
44
|
+
handle_ = nullptr;
|
|
45
|
+
return temp;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
bool Handle::is_valid() const {
|
|
49
|
+
return IsRealHandle(handle_);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
void Handle::reset(HANDLE handle) {
|
|
53
|
+
if (IsRealHandle(handle_)) {
|
|
54
|
+
::CloseHandle(handle_);
|
|
55
|
+
}
|
|
56
|
+
handle_ = handle;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#endif
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#ifdef _WIN32
|
|
4
|
+
|
|
5
|
+
#include <windows.h>
|
|
6
|
+
|
|
7
|
+
// RAII wrapper for a Win32 `HANDLE`. Mirrors `FileDescriptor` so backends can
|
|
8
|
+
// rely on the same lifetime semantics regardless of OS.
|
|
9
|
+
class Handle {
|
|
10
|
+
public:
|
|
11
|
+
Handle();
|
|
12
|
+
explicit Handle(HANDLE handle);
|
|
13
|
+
~Handle();
|
|
14
|
+
|
|
15
|
+
Handle(const Handle&) = delete;
|
|
16
|
+
Handle& operator=(const Handle&) = delete;
|
|
17
|
+
|
|
18
|
+
Handle(Handle&& other) noexcept;
|
|
19
|
+
Handle& operator=(Handle&& other) noexcept;
|
|
20
|
+
|
|
21
|
+
HANDLE get() const;
|
|
22
|
+
HANDLE release();
|
|
23
|
+
bool is_valid() const;
|
|
24
|
+
void reset(HANDLE handle = nullptr);
|
|
25
|
+
|
|
26
|
+
private:
|
|
27
|
+
HANDLE handle_;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
#endif
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#if defined(__APPLE__) || defined(__linux__)
|
|
4
|
+
|
|
5
|
+
#include <string>
|
|
6
|
+
#include <utility>
|
|
7
|
+
#include <vector>
|
|
8
|
+
|
|
9
|
+
#include "file_descriptor.h"
|
|
10
|
+
#include "posix_uv_poll_loop.h"
|
|
11
|
+
#include "tun_backend.h"
|
|
12
|
+
|
|
13
|
+
// Shared base class for POSIX TUN backends (Darwin, Linux). Owns the file
|
|
14
|
+
// descriptor, the assigned interface name, and the libuv poll loop. Concrete
|
|
15
|
+
// subclasses implement only the platform-specific OpenDevice, ReadPacket, and
|
|
16
|
+
// WritePacket.
|
|
17
|
+
class PosixTunBackend : public TunPlatformBackend {
|
|
18
|
+
public:
|
|
19
|
+
void CloseDevice() override {
|
|
20
|
+
poll_loop_.Stop();
|
|
21
|
+
fd_.reset();
|
|
22
|
+
interface_name_.clear();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
bool IsOpen() const override { return fd_.is_valid(); }
|
|
26
|
+
|
|
27
|
+
bool StartReceiveLoop(uv_loop_t* loop,
|
|
28
|
+
size_t buffer_size,
|
|
29
|
+
PacketCallback on_packet,
|
|
30
|
+
ErrorCallback on_error,
|
|
31
|
+
std::string& error) override {
|
|
32
|
+
if (!fd_.is_valid()) {
|
|
33
|
+
error = "Device not open";
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return poll_loop_.Start(
|
|
37
|
+
loop,
|
|
38
|
+
fd_.get(),
|
|
39
|
+
buffer_size,
|
|
40
|
+
[this](size_t size, std::vector<uint8_t>& out, std::string& err) {
|
|
41
|
+
return ReadPacket(size, out, err);
|
|
42
|
+
},
|
|
43
|
+
std::move(on_packet),
|
|
44
|
+
std::move(on_error),
|
|
45
|
+
error);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
void StopReceiveLoop() override { poll_loop_.Stop(); }
|
|
49
|
+
|
|
50
|
+
int GetNativeFd() const override { return fd_.get(); }
|
|
51
|
+
|
|
52
|
+
protected:
|
|
53
|
+
FileDescriptor fd_;
|
|
54
|
+
std::string interface_name_;
|
|
55
|
+
PosixUvPollLoop poll_loop_;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
#endif
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#if defined(__APPLE__) || defined(__linux__)
|
|
2
|
+
|
|
3
|
+
#include "posix_uv_poll_loop.h"
|
|
4
|
+
|
|
5
|
+
#include <cstdio>
|
|
6
|
+
#include <errno.h>
|
|
7
|
+
#include <fcntl.h>
|
|
8
|
+
#include <string.h>
|
|
9
|
+
#include <utility>
|
|
10
|
+
|
|
11
|
+
PosixUvPollLoop::~PosixUvPollLoop() {
|
|
12
|
+
Stop();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
bool PosixUvPollLoop::Start(uv_loop_t* loop,
|
|
16
|
+
int fd,
|
|
17
|
+
size_t buffer_size,
|
|
18
|
+
ReadFn read_fn,
|
|
19
|
+
TunPlatformBackend::PacketCallback on_packet,
|
|
20
|
+
TunPlatformBackend::ErrorCallback on_error,
|
|
21
|
+
std::string& error) {
|
|
22
|
+
if (handle_) {
|
|
23
|
+
error = "Receive loop already started";
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (!loop || fd < 0 || buffer_size == 0) {
|
|
27
|
+
error = "Invalid receive-loop parameters";
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
auto state = std::make_unique<State>();
|
|
32
|
+
state->buffer_size = buffer_size;
|
|
33
|
+
state->read_fn = std::move(read_fn);
|
|
34
|
+
state->on_packet = std::move(on_packet);
|
|
35
|
+
state->on_error = std::move(on_error);
|
|
36
|
+
state->owner = this;
|
|
37
|
+
|
|
38
|
+
auto handle = std::make_unique<uv_poll_t>();
|
|
39
|
+
if (uv_poll_init(loop, handle.get(), fd) != 0) {
|
|
40
|
+
error = "Failed to initialize poll handle";
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
handle->data = state.get();
|
|
45
|
+
if (uv_poll_start(handle.get(), UV_READABLE, &PosixUvPollLoop::OnPoll) != 0) {
|
|
46
|
+
uv_close(reinterpret_cast<uv_handle_t*>(handle.release()),
|
|
47
|
+
[](uv_handle_t* h) { delete reinterpret_cast<uv_poll_t*>(h); });
|
|
48
|
+
error = "Failed to start polling";
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
state_ = std::move(state);
|
|
53
|
+
handle_ = handle.release();
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
void PosixUvPollLoop::Stop() {
|
|
58
|
+
if (!handle_) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
uv_poll_stop(handle_);
|
|
63
|
+
handle_->data = nullptr;
|
|
64
|
+
uv_close(reinterpret_cast<uv_handle_t*>(handle_),
|
|
65
|
+
&PosixUvPollLoop::OnHandleClosed);
|
|
66
|
+
handle_ = nullptr;
|
|
67
|
+
state_.reset();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
void PosixUvPollLoop::OnPoll(uv_poll_t* handle, int status, int events) {
|
|
71
|
+
auto* state = static_cast<State*>(handle->data);
|
|
72
|
+
if (!state) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Tear down the loop and notify the owner of a terminal condition. The
|
|
77
|
+
// callback is copied locally first because Stop() destroys `state` (and the
|
|
78
|
+
// std::function it owns) synchronously.
|
|
79
|
+
auto handle_terminal = [&](const std::string& msg) {
|
|
80
|
+
auto cb = state->on_error;
|
|
81
|
+
PosixUvPollLoop* owner = state->owner;
|
|
82
|
+
if (owner) {
|
|
83
|
+
owner->Stop();
|
|
84
|
+
}
|
|
85
|
+
if (cb) {
|
|
86
|
+
cb(msg);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
if (status < 0) {
|
|
91
|
+
handle_terminal(std::string("Poll error: ") + uv_strerror(status));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!(events & UV_READABLE) || !state->read_fn) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
std::vector<uint8_t> packet;
|
|
100
|
+
std::string error;
|
|
101
|
+
ReadPacketStatus rs = state->read_fn(state->buffer_size, packet, error);
|
|
102
|
+
|
|
103
|
+
switch (rs) {
|
|
104
|
+
case ReadPacketStatus::Data:
|
|
105
|
+
if (state->on_packet) {
|
|
106
|
+
state->on_packet(std::move(packet));
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
case ReadPacketStatus::NoData:
|
|
110
|
+
return;
|
|
111
|
+
case ReadPacketStatus::Closed:
|
|
112
|
+
handle_terminal("Device closed");
|
|
113
|
+
return;
|
|
114
|
+
case ReadPacketStatus::Error:
|
|
115
|
+
handle_terminal(error);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
void PosixUvPollLoop::OnHandleClosed(uv_handle_t* handle) {
|
|
121
|
+
delete reinterpret_cast<uv_poll_t*>(handle);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
bool SetNonBlocking(int fd, std::string& error) {
|
|
125
|
+
int flags = fcntl(fd, F_GETFL, 0);
|
|
126
|
+
if (flags < 0) {
|
|
127
|
+
error = std::string("Failed to get file descriptor flags: ") + strerror(errno);
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
|
|
131
|
+
error = std::string("Failed to set non-blocking mode: ") + strerror(errno);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#endif
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#if defined(__APPLE__) || defined(__linux__)
|
|
4
|
+
|
|
5
|
+
#include <cstddef>
|
|
6
|
+
#include <functional>
|
|
7
|
+
#include <memory>
|
|
8
|
+
#include <string>
|
|
9
|
+
#include <vector>
|
|
10
|
+
|
|
11
|
+
#include <uv.h>
|
|
12
|
+
|
|
13
|
+
#include "tun_backend.h"
|
|
14
|
+
|
|
15
|
+
class PosixUvPollLoop {
|
|
16
|
+
public:
|
|
17
|
+
using ReadFn = std::function<ReadPacketStatus(size_t,
|
|
18
|
+
std::vector<uint8_t>&,
|
|
19
|
+
std::string&)>;
|
|
20
|
+
|
|
21
|
+
PosixUvPollLoop() = default;
|
|
22
|
+
~PosixUvPollLoop();
|
|
23
|
+
|
|
24
|
+
PosixUvPollLoop(const PosixUvPollLoop&) = delete;
|
|
25
|
+
PosixUvPollLoop& operator=(const PosixUvPollLoop&) = delete;
|
|
26
|
+
|
|
27
|
+
bool Start(uv_loop_t* loop,
|
|
28
|
+
int fd,
|
|
29
|
+
size_t buffer_size,
|
|
30
|
+
ReadFn read_fn,
|
|
31
|
+
TunPlatformBackend::PacketCallback on_packet,
|
|
32
|
+
TunPlatformBackend::ErrorCallback on_error,
|
|
33
|
+
std::string& error);
|
|
34
|
+
|
|
35
|
+
void Stop();
|
|
36
|
+
|
|
37
|
+
private:
|
|
38
|
+
struct State {
|
|
39
|
+
size_t buffer_size = 0;
|
|
40
|
+
ReadFn read_fn;
|
|
41
|
+
TunPlatformBackend::PacketCallback on_packet;
|
|
42
|
+
TunPlatformBackend::ErrorCallback on_error;
|
|
43
|
+
PosixUvPollLoop* owner = nullptr;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
static void OnPoll(uv_poll_t* handle, int status, int events);
|
|
47
|
+
static void OnHandleClosed(uv_handle_t* handle);
|
|
48
|
+
|
|
49
|
+
uv_poll_t* handle_ = nullptr;
|
|
50
|
+
std::unique_ptr<State> state_;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
bool SetNonBlocking(int fd, std::string& error);
|
|
54
|
+
|
|
55
|
+
#endif
|
package/src/native/tun_backend.h
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
-
#if !defined(__linux__) && !defined(__APPLE__)
|
|
4
|
-
#error "appium-ios-tuntap native addon supports only Linux and
|
|
3
|
+
#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
|
|
4
|
+
#error "appium-ios-tuntap native addon supports only Linux, macOS, and Windows"
|
|
5
5
|
#endif
|
|
6
6
|
|
|
7
7
|
#include <cstddef>
|
|
8
8
|
#include <cstdint>
|
|
9
|
+
#include <functional>
|
|
9
10
|
#include <memory>
|
|
10
11
|
#include <string>
|
|
11
|
-
#include <sys/types.h>
|
|
12
12
|
#include <vector>
|
|
13
13
|
|
|
14
|
-
#
|
|
14
|
+
#ifdef _WIN32
|
|
15
|
+
#include <BaseTsd.h>
|
|
16
|
+
using ssize_t = SSIZE_T;
|
|
17
|
+
#else
|
|
18
|
+
#include <sys/types.h>
|
|
19
|
+
#endif
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
FileDescriptor fd;
|
|
18
|
-
std::string interface_name;
|
|
19
|
-
};
|
|
21
|
+
#include <uv.h>
|
|
20
22
|
|
|
21
23
|
enum class ReadPacketStatus {
|
|
22
24
|
Data,
|
|
@@ -25,13 +27,54 @@ enum class ReadPacketStatus {
|
|
|
25
27
|
Error,
|
|
26
28
|
};
|
|
27
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Backend abstraction that hides OS-specific TUN device handling from the
|
|
32
|
+
* N-API surface.
|
|
33
|
+
*
|
|
34
|
+
* Each backend owns:
|
|
35
|
+
* - its native handle (POSIX file descriptor or Win32 `HANDLE`)
|
|
36
|
+
* - the receive-loop primitive it needs (libuv `uv_poll_t` on POSIX, a
|
|
37
|
+
* dedicated worker thread plus a Win32 event on Windows)
|
|
38
|
+
*/
|
|
28
39
|
class TunPlatformBackend {
|
|
29
40
|
public:
|
|
41
|
+
// Invoked once per packet read by the receive loop. Always called on a
|
|
42
|
+
// background thread (libuv loop thread on POSIX, worker thread on Windows);
|
|
43
|
+
// the caller in `tuntap.cc` is responsible for marshalling onto the JS
|
|
44
|
+
// thread via `Napi::ThreadSafeFunction`.
|
|
45
|
+
using PacketCallback = std::function<void(std::vector<uint8_t>)>;
|
|
46
|
+
|
|
47
|
+
// Invoked at most once when the receive loop encounters a fatal error and
|
|
48
|
+
// stops. The receive loop must not deliver any further packets afterwards.
|
|
49
|
+
using ErrorCallback = std::function<void(const std::string&)>;
|
|
50
|
+
|
|
30
51
|
virtual ~TunPlatformBackend() = default;
|
|
31
|
-
|
|
32
|
-
virtual
|
|
33
|
-
|
|
52
|
+
|
|
53
|
+
virtual bool OpenDevice(const std::string& requested_name,
|
|
54
|
+
std::string& out_interface_name,
|
|
55
|
+
std::string& error) = 0;
|
|
56
|
+
virtual void CloseDevice() = 0;
|
|
57
|
+
virtual bool IsOpen() const = 0;
|
|
58
|
+
|
|
59
|
+
virtual ReadPacketStatus ReadPacket(size_t max_payload_size,
|
|
60
|
+
std::vector<uint8_t>& out,
|
|
61
|
+
std::string& error) = 0;
|
|
62
|
+
virtual ssize_t WritePacket(const uint8_t* data,
|
|
63
|
+
size_t length,
|
|
64
|
+
std::string& error) = 0;
|
|
65
|
+
|
|
66
|
+
// Begin asynchronous packet delivery. `loop` is supplied by Node-API and is
|
|
67
|
+
// used by POSIX backends for `uv_poll_init`; Windows ignores it.
|
|
68
|
+
virtual bool StartReceiveLoop(uv_loop_t* loop,
|
|
69
|
+
size_t buffer_size,
|
|
70
|
+
PacketCallback on_packet,
|
|
71
|
+
ErrorCallback on_error,
|
|
72
|
+
std::string& error) = 0;
|
|
73
|
+
virtual void StopReceiveLoop() = 0;
|
|
74
|
+
|
|
75
|
+
// Returns the underlying POSIX file descriptor when one exists. Backends
|
|
76
|
+
// without a numeric fd (e.g. Wintun on Windows) return `-1`.
|
|
77
|
+
virtual int GetNativeFd() const { return -1; }
|
|
34
78
|
};
|
|
35
79
|
|
|
36
80
|
std::unique_ptr<TunPlatformBackend> CreatePlatformBackend();
|
|
37
|
-
|
|
@@ -14,30 +14,23 @@
|
|
|
14
14
|
#include <netinet/in.h>
|
|
15
15
|
#include <netinet6/in6_var.h>
|
|
16
16
|
|
|
17
|
-
#include <
|
|
17
|
+
#include <utility>
|
|
18
|
+
|
|
19
|
+
#include "file_descriptor.h"
|
|
20
|
+
#include "posix_tun_backend.h"
|
|
21
|
+
#include "posix_uv_poll_loop.h"
|
|
18
22
|
|
|
19
23
|
#define UTUN_CONTROL_NAME "com.apple.net.utun_control"
|
|
20
24
|
|
|
21
25
|
namespace {
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
int flags = fcntl(fd, F_GETFL, 0);
|
|
25
|
-
if (flags < 0) {
|
|
26
|
-
error = std::string("Failed to get file descriptor flags: ") + strerror(errno);
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
|
|
30
|
-
error = std::string("Failed to set non-blocking mode: ") + strerror(errno);
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
class DarwinTunBackend : public TunPlatformBackend {
|
|
27
|
+
class DarwinTunBackend : public PosixTunBackend {
|
|
37
28
|
public:
|
|
38
29
|
static constexpr size_t kUtunHeaderSize = 4;
|
|
39
30
|
|
|
40
|
-
bool OpenDevice(const std::string& requested_name,
|
|
31
|
+
bool OpenDevice(const std::string& requested_name,
|
|
32
|
+
std::string& out_interface_name,
|
|
33
|
+
std::string& error) override {
|
|
41
34
|
struct ctl_info ctl_info;
|
|
42
35
|
struct sockaddr_ctl socket_addr;
|
|
43
36
|
|
|
@@ -84,14 +77,22 @@ public:
|
|
|
84
77
|
return false;
|
|
85
78
|
}
|
|
86
79
|
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
fd_ = std::move(temp_fd);
|
|
81
|
+
interface_name_ = std::string(interface_name);
|
|
82
|
+
out_interface_name = interface_name_;
|
|
89
83
|
return true;
|
|
90
84
|
}
|
|
91
85
|
|
|
92
|
-
ReadPacketStatus ReadPacket(
|
|
86
|
+
ReadPacketStatus ReadPacket(size_t max_payload_size,
|
|
87
|
+
std::vector<uint8_t>& out,
|
|
88
|
+
std::string& error) override {
|
|
89
|
+
if (!fd_.is_valid()) {
|
|
90
|
+
error = "Device not open";
|
|
91
|
+
return ReadPacketStatus::Error;
|
|
92
|
+
}
|
|
93
|
+
|
|
93
94
|
out.resize(max_payload_size + kUtunHeaderSize);
|
|
94
|
-
ssize_t bytes_read = read(
|
|
95
|
+
ssize_t bytes_read = read(fd_.get(), out.data(), out.size());
|
|
95
96
|
if (bytes_read < 0) {
|
|
96
97
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
97
98
|
out.clear();
|
|
@@ -116,21 +117,28 @@ public:
|
|
|
116
117
|
return ReadPacketStatus::Data;
|
|
117
118
|
}
|
|
118
119
|
|
|
119
|
-
ssize_t WritePacket(
|
|
120
|
+
ssize_t WritePacket(const uint8_t* data,
|
|
121
|
+
size_t length,
|
|
122
|
+
std::string& error) override {
|
|
123
|
+
if (!fd_.is_valid()) {
|
|
124
|
+
error = "Device not open";
|
|
125
|
+
return -1;
|
|
126
|
+
}
|
|
127
|
+
|
|
120
128
|
std::vector<uint8_t> frame(length + kUtunHeaderSize);
|
|
121
129
|
uint32_t family = htonl(AF_INET6);
|
|
122
130
|
memcpy(frame.data(), &family, kUtunHeaderSize);
|
|
123
131
|
memcpy(frame.data() + kUtunHeaderSize, data, length);
|
|
124
132
|
|
|
125
|
-
ssize_t bytes_written = write(
|
|
133
|
+
ssize_t bytes_written = write(fd_.get(), frame.data(), frame.size());
|
|
126
134
|
if (bytes_written < 0) {
|
|
127
135
|
error = std::string("Write error: ") + strerror(errno);
|
|
128
136
|
return -1;
|
|
129
137
|
}
|
|
130
138
|
|
|
131
139
|
return bytes_written > static_cast<ssize_t>(kUtunHeaderSize)
|
|
132
|
-
|
|
133
|
-
|
|
140
|
+
? bytes_written - static_cast<ssize_t>(kUtunHeaderSize)
|
|
141
|
+
: 0;
|
|
134
142
|
}
|
|
135
143
|
|
|
136
144
|
private:
|
|
@@ -168,4 +176,3 @@ std::unique_ptr<TunPlatformBackend> CreatePlatformBackend() {
|
|
|
168
176
|
}
|
|
169
177
|
|
|
170
178
|
#endif
|
|
171
|
-
|