wolsocketproxy 0.1.0__tar.gz
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.
- wolsocketproxy-0.1.0/LICENSE +13 -0
- wolsocketproxy-0.1.0/PKG-INFO +94 -0
- wolsocketproxy-0.1.0/README.md +69 -0
- wolsocketproxy-0.1.0/pyproject.toml +69 -0
- wolsocketproxy-0.1.0/wolsocketproxy/__init__.py +44 -0
- wolsocketproxy-0.1.0/wolsocketproxy/__main__.py +4 -0
- wolsocketproxy-0.1.0/wolsocketproxy/monitor.py +48 -0
- wolsocketproxy-0.1.0/wolsocketproxy/proxy.py +175 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2024 Song Fuchang
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: wolsocketproxy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A socket proxy with wake-on-lan feature.
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Author: Song Fuchang
|
|
7
|
+
Author-email: song.fc@gmail.com
|
|
8
|
+
Requires-Python: >=3.11,<4.0
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Intended Audience :: System Administrators
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: System :: Networking
|
|
18
|
+
Requires-Dist: dataclass-wizard (==0.28.0)
|
|
19
|
+
Requires-Dist: icmplib (==3.0.4)
|
|
20
|
+
Requires-Dist: wakeonlan (==3.1.0)
|
|
21
|
+
Project-URL: Changelog, https://github.com/notsyncing/wol-socket-proxy/releases
|
|
22
|
+
Project-URL: Issues, https://github.com/notsyncing/wol-socket-proxy/issues
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# wol-socket-proxy
|
|
26
|
+
|
|
27
|
+
A socket proxy with wake-on-lan feature.
|
|
28
|
+
|
|
29
|
+
It can forward TCP(bidirectional) and UDP(send-only) traffic from local machine to remote machine, and send a magic packet to wake it (wake-on-lan) before forwarding traffic if the remote machine does not respond to ICMP ping.
|
|
30
|
+
|
|
31
|
+
## Requirements
|
|
32
|
+
|
|
33
|
+
Python 3.11+
|
|
34
|
+
|
|
35
|
+
The remote machine must accept and respond to ICMP ping requests.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
You can install it from PyPI or use a prebuilt docker image.
|
|
40
|
+
|
|
41
|
+
### Install from PyPI
|
|
42
|
+
|
|
43
|
+
Simply install it:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
pip install wolsocketproxy
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then create a config file:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
cp wolsocketproxy.conf.example /etc/wolsocketproxy.conf
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Modify the config file as your requirements.
|
|
56
|
+
|
|
57
|
+
Finally, run it
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
wolsocketproxy
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Use prebuilt docker image
|
|
64
|
+
|
|
65
|
+
See `docker/docker-compose.yml` for more details.
|
|
66
|
+
|
|
67
|
+
## Config
|
|
68
|
+
|
|
69
|
+
The `wolsocketproxy.conf` has the following structure:
|
|
70
|
+
|
|
71
|
+
```json5
|
|
72
|
+
{
|
|
73
|
+
// NOTE: Comments are not allowed in actual config file
|
|
74
|
+
// Define forwarding routes
|
|
75
|
+
"routes": [
|
|
76
|
+
{
|
|
77
|
+
"local_address": "0.0.0.0", // The local address to listen
|
|
78
|
+
"local_port": 12345, // The local port to listen
|
|
79
|
+
"target_address": "192.168.0.100", // The target address forwarding to
|
|
80
|
+
"target_port": 22, // The target port forwarding to
|
|
81
|
+
"protocol": "tcp" // The protocol to use
|
|
82
|
+
},
|
|
83
|
+
// ... more routes ...
|
|
84
|
+
],
|
|
85
|
+
|
|
86
|
+
// Tell the program about the MAC address of each IP in routes
|
|
87
|
+
"mac_mappings": {
|
|
88
|
+
// Key is IP address, and value is MAC address, case-insensitive
|
|
89
|
+
"192.168.0.100": "11:22:33:44:55:66",
|
|
90
|
+
// ... more items ...
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# wol-socket-proxy
|
|
2
|
+
|
|
3
|
+
A socket proxy with wake-on-lan feature.
|
|
4
|
+
|
|
5
|
+
It can forward TCP(bidirectional) and UDP(send-only) traffic from local machine to remote machine, and send a magic packet to wake it (wake-on-lan) before forwarding traffic if the remote machine does not respond to ICMP ping.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
Python 3.11+
|
|
10
|
+
|
|
11
|
+
The remote machine must accept and respond to ICMP ping requests.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
You can install it from PyPI or use a prebuilt docker image.
|
|
16
|
+
|
|
17
|
+
### Install from PyPI
|
|
18
|
+
|
|
19
|
+
Simply install it:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
pip install wolsocketproxy
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then create a config file:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
cp wolsocketproxy.conf.example /etc/wolsocketproxy.conf
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Modify the config file as your requirements.
|
|
32
|
+
|
|
33
|
+
Finally, run it
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
wolsocketproxy
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Use prebuilt docker image
|
|
40
|
+
|
|
41
|
+
See `docker/docker-compose.yml` for more details.
|
|
42
|
+
|
|
43
|
+
## Config
|
|
44
|
+
|
|
45
|
+
The `wolsocketproxy.conf` has the following structure:
|
|
46
|
+
|
|
47
|
+
```json5
|
|
48
|
+
{
|
|
49
|
+
// NOTE: Comments are not allowed in actual config file
|
|
50
|
+
// Define forwarding routes
|
|
51
|
+
"routes": [
|
|
52
|
+
{
|
|
53
|
+
"local_address": "0.0.0.0", // The local address to listen
|
|
54
|
+
"local_port": 12345, // The local port to listen
|
|
55
|
+
"target_address": "192.168.0.100", // The target address forwarding to
|
|
56
|
+
"target_port": 22, // The target port forwarding to
|
|
57
|
+
"protocol": "tcp" // The protocol to use
|
|
58
|
+
},
|
|
59
|
+
// ... more routes ...
|
|
60
|
+
],
|
|
61
|
+
|
|
62
|
+
// Tell the program about the MAC address of each IP in routes
|
|
63
|
+
"mac_mappings": {
|
|
64
|
+
// Key is IP address, and value is MAC address, case-insensitive
|
|
65
|
+
"192.168.0.100": "11:22:33:44:55:66",
|
|
66
|
+
// ... more items ...
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "wolsocketproxy"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A socket proxy with wake-on-lan feature."
|
|
5
|
+
authors = ["Song Fuchang <song.fc@gmail.com>"]
|
|
6
|
+
license = "Apache-2.0"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Development Status :: 5 - Production/Stable",
|
|
10
|
+
"Intended Audience :: Developers",
|
|
11
|
+
"Intended Audience :: System Administrators",
|
|
12
|
+
"Topic :: System :: Networking"
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[tool.poetry.urls]
|
|
16
|
+
Changelog = "https://github.com/notsyncing/wol-socket-proxy/releases"
|
|
17
|
+
Issues = "https://github.com/notsyncing/wol-socket-proxy/issues"
|
|
18
|
+
|
|
19
|
+
[tool.poetry.dependencies]
|
|
20
|
+
python = "^3.11"
|
|
21
|
+
wakeonlan = "3.1.0"
|
|
22
|
+
dataclass-wizard = "0.28.0"
|
|
23
|
+
icmplib = "3.0.4"
|
|
24
|
+
|
|
25
|
+
[tool.poetry.dev-dependencies]
|
|
26
|
+
mypy = { version = "*", python = ">=3.11" }
|
|
27
|
+
ruff = { version = "*", python = ">=3.11" }
|
|
28
|
+
|
|
29
|
+
[tool.poetry.scripts]
|
|
30
|
+
wolsocketproxy = "wolsocketproxy:main"
|
|
31
|
+
|
|
32
|
+
[tool.ruff]
|
|
33
|
+
line-length = 120
|
|
34
|
+
src = ["wolsocketproxy"]
|
|
35
|
+
target-version = "py311"
|
|
36
|
+
lint.select = ["ALL"]
|
|
37
|
+
lint.ignore = [
|
|
38
|
+
"COM812",
|
|
39
|
+
"D100",
|
|
40
|
+
"D101",
|
|
41
|
+
"D102",
|
|
42
|
+
"D103",
|
|
43
|
+
"D104",
|
|
44
|
+
"D107",
|
|
45
|
+
"D203",
|
|
46
|
+
"D211",
|
|
47
|
+
"D213",
|
|
48
|
+
"EM102",
|
|
49
|
+
"FBT001",
|
|
50
|
+
"FBT003",
|
|
51
|
+
"ISC001",
|
|
52
|
+
"TRY003",
|
|
53
|
+
"TRY201",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[tool.mypy]
|
|
57
|
+
disallow_untyped_defs = true # Functions need to be annotated
|
|
58
|
+
warn_unused_ignores = true
|
|
59
|
+
ignore_missing_imports = true
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
[[tool.poetry.source]]
|
|
63
|
+
name = "mirrors"
|
|
64
|
+
url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/"
|
|
65
|
+
priority = "primary"
|
|
66
|
+
|
|
67
|
+
[build-system]
|
|
68
|
+
requires = ["poetry-core"]
|
|
69
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
from argparse import ArgumentParser
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import dataclass_wizard
|
|
7
|
+
|
|
8
|
+
from wolsocketproxy.proxy import Proxy, ProxyConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main() -> None:
|
|
12
|
+
logging.basicConfig(level=logging.INFO)
|
|
13
|
+
log = logging.getLogger()
|
|
14
|
+
|
|
15
|
+
parser = ArgumentParser(prog="wolsocketproxy", description="A socket proxy with wake-on-lan feature.")
|
|
16
|
+
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"-c",
|
|
19
|
+
"--config",
|
|
20
|
+
help="The config file to use, default lookup order: /etc/wolsocketproxy.conf, ./wolsocketproxy.conf",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
args = parser.parse_args()
|
|
24
|
+
config_path: Path
|
|
25
|
+
|
|
26
|
+
if "config" in args and args.config is not None:
|
|
27
|
+
config_path = Path(args.config)
|
|
28
|
+
else:
|
|
29
|
+
config_path = Path("/etc/wolsocketproxy.conf")
|
|
30
|
+
|
|
31
|
+
if not config_path.exists():
|
|
32
|
+
config_path = Path("./wolsocketproxy.conf")
|
|
33
|
+
|
|
34
|
+
if not config_path.exists():
|
|
35
|
+
log.error("Config file path %s does not exist!", config_path)
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
config_data = json.loads(config_path.read_text())
|
|
39
|
+
config = dataclass_wizard.fromdict(ProxyConfig, config_data)
|
|
40
|
+
|
|
41
|
+
log.info("Loaded config from %s", config_path)
|
|
42
|
+
|
|
43
|
+
proxy = Proxy(config)
|
|
44
|
+
proxy.start()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
from collections.abc import Collection
|
|
4
|
+
from logging import Logger
|
|
5
|
+
from threading import Thread
|
|
6
|
+
|
|
7
|
+
from icmplib import multiping
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Monitor:
|
|
11
|
+
_log: Logger = logging.getLogger()
|
|
12
|
+
_ip_state: dict[str, bool]
|
|
13
|
+
|
|
14
|
+
def __init__(self, watching_ip_list: Collection[str]) -> None:
|
|
15
|
+
self._ip_state = {}
|
|
16
|
+
|
|
17
|
+
for ip in watching_ip_list:
|
|
18
|
+
self._ip_state[ip] = False
|
|
19
|
+
|
|
20
|
+
def start(self) -> None:
|
|
21
|
+
t = Thread(target=self.__check_ip_state)
|
|
22
|
+
t.daemon = True
|
|
23
|
+
t.start()
|
|
24
|
+
|
|
25
|
+
def __check_ip_state(self) -> None:
|
|
26
|
+
self._log.info("IP monitor is started.")
|
|
27
|
+
|
|
28
|
+
while True:
|
|
29
|
+
ip_list = self._ip_state.keys()
|
|
30
|
+
results = multiping(ip_list, count=3, timeout=2, privileged=False)
|
|
31
|
+
|
|
32
|
+
for result in results:
|
|
33
|
+
original_state = self._ip_state[result.address]
|
|
34
|
+
self._ip_state[result.address] = result.is_alive
|
|
35
|
+
|
|
36
|
+
if original_state != result.is_alive:
|
|
37
|
+
if original_state is False:
|
|
38
|
+
self._log.info("Target %s now online.", result.address)
|
|
39
|
+
else:
|
|
40
|
+
self._log.info("Target %s now offline.", result.address)
|
|
41
|
+
|
|
42
|
+
time.sleep(1)
|
|
43
|
+
|
|
44
|
+
def report_availablity(self, ip: str, available: bool) -> None:
|
|
45
|
+
self._ip_state[ip] = available
|
|
46
|
+
|
|
47
|
+
def is_available(self, ip: str) -> bool:
|
|
48
|
+
return self._ip_state.get(ip, False)
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import contextlib
|
|
3
|
+
import logging
|
|
4
|
+
import time
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from logging import Logger
|
|
7
|
+
from typing import Any, Literal, override
|
|
8
|
+
|
|
9
|
+
import wakeonlan
|
|
10
|
+
|
|
11
|
+
from wolsocketproxy.monitor import Monitor
|
|
12
|
+
|
|
13
|
+
WOL_MAX_WAIT_COUNT = 60
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class ProxyRoute:
|
|
18
|
+
local_address: str
|
|
19
|
+
local_port: int
|
|
20
|
+
target_address: str
|
|
21
|
+
target_port: int
|
|
22
|
+
protocol: Literal["tcp", "udp"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class ProxyConfig:
|
|
27
|
+
routes: list[ProxyRoute]
|
|
28
|
+
mac_mappings: dict[str, str]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ProxyUdpProtocol(asyncio.DatagramProtocol):
|
|
32
|
+
_proxy: "Proxy"
|
|
33
|
+
_monitor: Monitor
|
|
34
|
+
_transport: asyncio.transports.DatagramTransport
|
|
35
|
+
_target_address: str
|
|
36
|
+
_target_port: int
|
|
37
|
+
_target_pair: tuple[str, int]
|
|
38
|
+
|
|
39
|
+
def __init__(self, proxy: "Proxy", monitor: Monitor, target_address: str, target_port: int) -> None:
|
|
40
|
+
self._proxy = proxy
|
|
41
|
+
self._monitor = monitor
|
|
42
|
+
self._target_address = target_address
|
|
43
|
+
self._target_port = target_port
|
|
44
|
+
self._target_pair = (target_address, target_port)
|
|
45
|
+
|
|
46
|
+
@override
|
|
47
|
+
def connection_made(self, transport: asyncio.transports.DatagramTransport) -> None: # type: ignore[override]
|
|
48
|
+
self._transport = transport
|
|
49
|
+
|
|
50
|
+
@override
|
|
51
|
+
def datagram_received(self, data: bytes, addr: tuple[str | Any, int]) -> None:
|
|
52
|
+
if addr == self._target_address:
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
if not self._monitor.is_available(self._target_address):
|
|
56
|
+
self._proxy._wake_up_target(self._target_address) # noqa: SLF001
|
|
57
|
+
|
|
58
|
+
self._transport.sendto(data, self._target_pair)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Proxy:
|
|
62
|
+
_log: Logger = logging.getLogger()
|
|
63
|
+
_config: ProxyConfig
|
|
64
|
+
_monitor: Monitor
|
|
65
|
+
_routes: list[ProxyRoute]
|
|
66
|
+
_mac_mappings: dict[str, str]
|
|
67
|
+
|
|
68
|
+
def __init__(self, config: ProxyConfig) -> None:
|
|
69
|
+
self._config = config
|
|
70
|
+
self._routes = config.routes
|
|
71
|
+
self._mac_mappings = config.mac_mappings
|
|
72
|
+
|
|
73
|
+
target_ip_set = set()
|
|
74
|
+
|
|
75
|
+
for r in self._routes:
|
|
76
|
+
target_ip_set.add(r.target_address)
|
|
77
|
+
|
|
78
|
+
self._monitor = Monitor(watching_ip_list=target_ip_set)
|
|
79
|
+
|
|
80
|
+
def start(self) -> None:
|
|
81
|
+
self._monitor.start()
|
|
82
|
+
|
|
83
|
+
for route in self._routes:
|
|
84
|
+
self.__create_route(route)
|
|
85
|
+
|
|
86
|
+
loop = asyncio.get_event_loop()
|
|
87
|
+
|
|
88
|
+
self._log.info("Proxy server started.")
|
|
89
|
+
|
|
90
|
+
with contextlib.suppress(KeyboardInterrupt):
|
|
91
|
+
loop.run_forever()
|
|
92
|
+
|
|
93
|
+
self._log.info("Proxy server is stopping...")
|
|
94
|
+
|
|
95
|
+
loop.close()
|
|
96
|
+
|
|
97
|
+
def __create_route(self, route: ProxyRoute) -> None:
|
|
98
|
+
if route.protocol == "tcp":
|
|
99
|
+
self.__create_tcp_route(route)
|
|
100
|
+
elif route.protocol == "udp":
|
|
101
|
+
self.__create_udp_route(route)
|
|
102
|
+
else:
|
|
103
|
+
raise ValueError(
|
|
104
|
+
f"Unsupported protocol {route.protocol} in route from {route.local_address}:{route.local_port} to "
|
|
105
|
+
f"{route.target_address}:{route.target_port}"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def __create_tcp_route(self, route: ProxyRoute) -> None:
|
|
109
|
+
cr = asyncio.start_server(
|
|
110
|
+
self.__make_tcp_route_handler(route.target_address, route.target_port),
|
|
111
|
+
route.local_address,
|
|
112
|
+
route.local_port,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
loop = asyncio.get_event_loop()
|
|
116
|
+
loop.run_until_complete(cr)
|
|
117
|
+
|
|
118
|
+
self._log.info(
|
|
119
|
+
f"Created {route.protocol} proxy from {route.local_address}:{route.local_port} to "
|
|
120
|
+
f"{route.target_address}:{route.target_port}"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
async def __pipe(self, target_address: str, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
|
|
124
|
+
try:
|
|
125
|
+
while not reader.at_eof():
|
|
126
|
+
writer.write(await reader.read(2048))
|
|
127
|
+
except ConnectionResetError:
|
|
128
|
+
self._log.warning("Connection reset by target %s", target_address)
|
|
129
|
+
finally:
|
|
130
|
+
writer.close()
|
|
131
|
+
|
|
132
|
+
def __make_tcp_route_handler(self, target_address: str, target_port: int) -> Any: # noqa: ANN401
|
|
133
|
+
async def handler(local_reader: asyncio.StreamReader, local_writer: asyncio.StreamWriter) -> None:
|
|
134
|
+
if not self._monitor.is_available(target_address):
|
|
135
|
+
self._wake_up_target(target_address)
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
target_reader, target_writer = await asyncio.open_connection(target_address, target_port)
|
|
139
|
+
except OSError as e:
|
|
140
|
+
self._monitor.report_availablity(target_address, False)
|
|
141
|
+
self._log.error("Unable to open connection to %s:%d", target_address, target_port)
|
|
142
|
+
raise e
|
|
143
|
+
|
|
144
|
+
send_pipe = self.__pipe(target_address, local_reader, target_writer)
|
|
145
|
+
recv_pipe = self.__pipe(target_address, target_reader, local_writer)
|
|
146
|
+
await asyncio.gather(send_pipe, recv_pipe)
|
|
147
|
+
|
|
148
|
+
return handler
|
|
149
|
+
|
|
150
|
+
def __create_udp_route(self, route: ProxyRoute) -> None:
|
|
151
|
+
loop = asyncio.get_event_loop()
|
|
152
|
+
|
|
153
|
+
cr = loop.create_datagram_endpoint(
|
|
154
|
+
lambda: ProxyUdpProtocol(self, self._monitor, route.target_address, route.target_port),
|
|
155
|
+
local_addr=(route.local_address, route.local_port),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
loop.run_until_complete(cr)
|
|
159
|
+
|
|
160
|
+
def _wake_up_target(self, target_address: str) -> None:
|
|
161
|
+
target_mac_addr = self._mac_mappings[target_address]
|
|
162
|
+
|
|
163
|
+
self._log.info("Waking up target %s at %s", target_address, target_mac_addr)
|
|
164
|
+
|
|
165
|
+
wakeonlan.send_magic_packet(target_mac_addr)
|
|
166
|
+
|
|
167
|
+
count = 0
|
|
168
|
+
|
|
169
|
+
while not self._monitor.is_available(target_address):
|
|
170
|
+
count = count + 1
|
|
171
|
+
time.sleep(1)
|
|
172
|
+
|
|
173
|
+
if count > WOL_MAX_WAIT_COUNT:
|
|
174
|
+
self._log.warning("Target %s still not online after %d seconds!", target_address, count)
|
|
175
|
+
return
|