|
14 | 14 | from asyncio import CancelledError, StreamReader, StreamWriter
|
15 | 15 | from collections import namedtuple
|
16 | 16 | from contextlib import asynccontextmanager, suppress
|
17 |
| -from os import chown, getenv |
| 17 | + |
| 18 | +if sys.platform != 'win32': |
| 19 | + from os import chown |
| 20 | + |
| 21 | +from os import getenv |
18 | 22 | from pathlib import Path
|
19 | 23 | from socket import AF_INET6, create_connection
|
20 | 24 | from ssl import VerifyMode
|
|
33 | 37 | from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
|
34 | 38 | from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
35 | 39 | from opack import dumps
|
36 |
| -from pytun_pmd3 import TunTapDevice |
| 40 | + |
| 41 | +if sys.platform != 'win32': |
| 42 | + from pytun_pmd3 import TunTapDevice |
| 43 | +else: |
| 44 | + from pywintunx_pmd3 import TunTapDevice, set_logger |
| 45 | + |
37 | 46 | from qh3.asyncio import QuicConnectionProtocol
|
38 | 47 | from qh3.asyncio.client import connect as aioquic_connect
|
39 | 48 | from qh3.asyncio.protocol import QuicStreamHandler
|
|
65 | 74 | else:
|
66 | 75 | LOOKBACK_HEADER = b'\x00\x00\x86\xdd'
|
67 | 76 |
|
| 77 | +if sys.platform == 'win32': |
| 78 | + def wintun_logger(level: int, timestamp: int, message: str) -> None: |
| 79 | + logging.getLogger('wintun').info(message) |
| 80 | + |
| 81 | + set_logger(wintun_logger) |
| 82 | + |
68 | 83 | IPV6_HEADER_SIZE = 40
|
69 | 84 | UDP_HEADER_SIZE = 8
|
70 | 85 |
|
@@ -144,12 +159,18 @@ async def wait_closed(self) -> None:
|
144 | 159 | @asyncio_print_traceback
|
145 | 160 | async def tun_read_task(self) -> None:
|
146 | 161 | read_size = self.tun.mtu + len(LOOKBACK_HEADER)
|
147 |
| - async with aiofiles.open(self.tun.fileno(), 'rb', opener=lambda path, flags: path, buffering=0) as f: |
| 162 | + if sys.platform != 'win32': |
| 163 | + async with aiofiles.open(self.tun.fileno(), 'rb', opener=lambda path, flags: path, buffering=0) as f: |
| 164 | + while True: |
| 165 | + packet = await f.read(read_size) |
| 166 | + assert packet.startswith(LOOKBACK_HEADER) |
| 167 | + packet = packet[len(LOOKBACK_HEADER):] |
| 168 | + await self.send_packet_to_device(packet) |
| 169 | + else: |
148 | 170 | while True:
|
149 |
| - packet = await f.read(read_size) |
150 |
| - assert packet.startswith(LOOKBACK_HEADER) |
151 |
| - packet = packet[len(LOOKBACK_HEADER):] |
152 |
| - await self.send_packet_to_device(packet) |
| 171 | + packet = await asyncio.get_running_loop().run_in_executor(None, self.tun.read) |
| 172 | + if packet: |
| 173 | + await self.send_packet_to_device(packet) |
153 | 174 |
|
154 | 175 | def start_tunnel(self, address: str, mtu: int) -> None:
|
155 | 176 | self.tun = TunTapDevice()
|
@@ -402,7 +423,7 @@ def save_pair_record(self) -> None:
|
402 | 423 | 'private_key': self.ed25519_private_key.private_bytes_raw(),
|
403 | 424 | 'remote_unlock_host_key': self.remote_unlock_host_key
|
404 | 425 | }))
|
405 |
| - if getenv('SUDO_UID'): |
| 426 | + if getenv('SUDO_UID') and sys.platform != 'win32': |
406 | 427 | chown(self.pair_record_path, int(getenv('SUDO_UID')), int(getenv('SUDO_GID')))
|
407 | 428 |
|
408 | 429 | @property
|
|
0 commit comments