exposr 0.5.2__py3-none-any.whl
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.
- client/__init__.py +1 -0
- client/config.py +43 -0
- client/main.py +75 -0
- client/tcp/__init__.py +1 -0
- client/tcp/connection.py +86 -0
- client/tcp/tunnel.py +44 -0
- client/udp/__init__.py +1 -0
- client/udp/connection.py +76 -0
- client/udp/tunnel.py +68 -0
- common/__init__.py +1 -0
- common/cli.py +60 -0
- common/logger.py +21 -0
- common/protocol.py +10 -0
- exposr-0.5.2.dist-info/METADATA +862 -0
- exposr-0.5.2.dist-info/RECORD +30 -0
- exposr-0.5.2.dist-info/WHEEL +5 -0
- exposr-0.5.2.dist-info/entry_points.txt +2 -0
- exposr-0.5.2.dist-info/top_level.txt +3 -0
- server/__init__.py +1 -0
- server/config.py +34 -0
- server/control.py +96 -0
- server/data.py +29 -0
- server/main.py +33 -0
- server/ports.py +26 -0
- server/tcp/__init__.py +1 -0
- server/tcp/ports.py +24 -0
- server/tcp/tunnel.py +58 -0
- server/udp/__init__.py +1 -0
- server/udp/ports.py +24 -0
- server/udp/tunnel.py +72 -0
client/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Exposr client package."""
|
client/config.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import secrets
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
CONFIG_PATH = Path.home() / ".exposr" / "config.json"
|
|
7
|
+
TOKEN_PATH = CONFIG_PATH.parent / "agent_token.txt"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def load_config():
|
|
11
|
+
if not CONFIG_PATH.exists():
|
|
12
|
+
return {"server_host": "", "agent_token": ""}
|
|
13
|
+
try:
|
|
14
|
+
with CONFIG_PATH.open("r", encoding="utf-8") as config_file:
|
|
15
|
+
config = json.load(config_file)
|
|
16
|
+
except (OSError, json.JSONDecodeError):
|
|
17
|
+
return {"server_host": "", "agent_token": ""}
|
|
18
|
+
return {
|
|
19
|
+
"server_host": str(config.get("server_host", "")).strip(),
|
|
20
|
+
"agent_token": str(config.get("agent_token", "")).strip(),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def set_server(server_host):
|
|
25
|
+
agent_token = secrets.token_urlsafe(32)
|
|
26
|
+
CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
27
|
+
with CONFIG_PATH.open("w", encoding="utf-8") as config_file:
|
|
28
|
+
json.dump(
|
|
29
|
+
{"server_host": server_host.strip(), "agent_token": agent_token},
|
|
30
|
+
config_file,
|
|
31
|
+
indent=2,
|
|
32
|
+
)
|
|
33
|
+
config_file.write("\n")
|
|
34
|
+
TOKEN_PATH.write_text(f"{agent_token}\n", encoding="utf-8")
|
|
35
|
+
return agent_token
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_server_host():
|
|
39
|
+
return load_config()["server_host"]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_agent_token():
|
|
43
|
+
return load_config()["agent_token"]
|
client/main.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from client.config import get_agent_token, get_server_host, set_server
|
|
4
|
+
from client.tcp.connection import AgentConfig as TcpAgentConfig, connect_to_server as connect_tcp
|
|
5
|
+
from client.udp.connection import AgentConfig as UdpAgentConfig, connect_to_server as connect_udp
|
|
6
|
+
from common.cli import parse_arguments
|
|
7
|
+
from common.logger import error, info
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main():
|
|
11
|
+
args = parse_arguments()
|
|
12
|
+
if args.command == "server":
|
|
13
|
+
from server.config import get_agent_token as get_server_agent_token
|
|
14
|
+
from server.config import init_token
|
|
15
|
+
|
|
16
|
+
if args.server_command == "init-token":
|
|
17
|
+
if not args.agent_token.strip():
|
|
18
|
+
error("Agent token cannot be empty")
|
|
19
|
+
return
|
|
20
|
+
init_token(args.agent_token)
|
|
21
|
+
info("Agent token saved to ~/.exposr/config.json")
|
|
22
|
+
elif args.server_command == "start":
|
|
23
|
+
if not get_server_agent_token():
|
|
24
|
+
error("Server token is not configured. Run: exposr server init-token <token>")
|
|
25
|
+
return
|
|
26
|
+
from server.main import main as start_server
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
asyncio.run(start_server())
|
|
30
|
+
except KeyboardInterrupt:
|
|
31
|
+
print()
|
|
32
|
+
info("Exposr server stopped")
|
|
33
|
+
return
|
|
34
|
+
if args.command == "config":
|
|
35
|
+
if args.config_command == "set-server":
|
|
36
|
+
set_server(args.server_host)
|
|
37
|
+
info(f"Server address saved: {args.server_host}")
|
|
38
|
+
info("Agent token generated and saved to ~/.exposr/agent_token.txt")
|
|
39
|
+
return
|
|
40
|
+
server_host = args.server_host or get_server_host()
|
|
41
|
+
agent_token = get_agent_token()
|
|
42
|
+
if not server_host:
|
|
43
|
+
error(
|
|
44
|
+
"Server IP is not configured. "
|
|
45
|
+
"Run: exposr config set-server <server-ip>"
|
|
46
|
+
)
|
|
47
|
+
return
|
|
48
|
+
if not agent_token:
|
|
49
|
+
error(
|
|
50
|
+
"Agent token is not configured. "
|
|
51
|
+
"Run: exposr config set-server <server-ip>"
|
|
52
|
+
)
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
config_class = UdpAgentConfig if args.command == "udp" else TcpAgentConfig
|
|
56
|
+
connect = connect_udp if args.command == "udp" else connect_tcp
|
|
57
|
+
config = config_class(
|
|
58
|
+
server_host=server_host,
|
|
59
|
+
agent_token=agent_token,
|
|
60
|
+
control_port=args.control_port,
|
|
61
|
+
data_port=args.data_port,
|
|
62
|
+
local_host=args.local_host,
|
|
63
|
+
local_port=args.local_port,
|
|
64
|
+
preferred_public_port=args.public_port or 25565,
|
|
65
|
+
explicit_public_port=args.public_port is not None,
|
|
66
|
+
)
|
|
67
|
+
try:
|
|
68
|
+
asyncio.run(connect(config))
|
|
69
|
+
except KeyboardInterrupt:
|
|
70
|
+
print()
|
|
71
|
+
info("Exposr stopped")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
if __name__ == "__main__":
|
|
75
|
+
main()
|
client/tcp/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""TCP client transport implementation."""
|
client/tcp/connection.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import random
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from common.logger import connected, error, trying
|
|
6
|
+
from common.protocol import CONNECT, REGISTER, REGISTERED, command
|
|
7
|
+
from client.tcp.tunnel import handle_connection
|
|
8
|
+
|
|
9
|
+
RANDOM_PORT_START = 20000
|
|
10
|
+
RANDOM_PORT_END = 30000
|
|
11
|
+
MAX_PORT_ATTEMPTS = 100
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class AgentConfig:
|
|
16
|
+
server_host: str
|
|
17
|
+
agent_token: str
|
|
18
|
+
control_port: int
|
|
19
|
+
data_port: int
|
|
20
|
+
local_host: str
|
|
21
|
+
local_port: int
|
|
22
|
+
preferred_public_port: int
|
|
23
|
+
explicit_public_port: bool
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def register_port(reader, writer, port, config):
|
|
27
|
+
trying(f"Trying to register public port {port}")
|
|
28
|
+
writer.write(command(REGISTER, f"{port} {config.agent_token}"))
|
|
29
|
+
await writer.drain()
|
|
30
|
+
response = await reader.readline()
|
|
31
|
+
if not response:
|
|
32
|
+
error("Server disconnected while registering")
|
|
33
|
+
return False
|
|
34
|
+
if response.startswith(REGISTERED.encode()):
|
|
35
|
+
connected(f"Public tunnel active: {config.server_host}:{port}")
|
|
36
|
+
connected(f"Forwarding to: {config.local_host}:{config.local_port}")
|
|
37
|
+
return True
|
|
38
|
+
error(f"Port {port} unavailable")
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def connect_to_server(config):
|
|
43
|
+
while True:
|
|
44
|
+
writer = None
|
|
45
|
+
try:
|
|
46
|
+
trying(f"Connecting to Exposr {config.server_host}:{config.control_port}")
|
|
47
|
+
reader, writer = await asyncio.open_connection(
|
|
48
|
+
config.server_host, config.control_port
|
|
49
|
+
)
|
|
50
|
+
connected("Connected to Exposr control server")
|
|
51
|
+
public_port = config.preferred_public_port
|
|
52
|
+
registered = await register_port(reader, writer, public_port, config)
|
|
53
|
+
if not registered and config.explicit_public_port:
|
|
54
|
+
error(f"Requested public port {public_port} is unavailable")
|
|
55
|
+
raise RuntimeError(f"Port {public_port} is unavailable")
|
|
56
|
+
attempts = 0
|
|
57
|
+
while not registered:
|
|
58
|
+
attempts += 1
|
|
59
|
+
if attempts > MAX_PORT_ATTEMPTS:
|
|
60
|
+
error("Could not find an available public port after 100 attempts")
|
|
61
|
+
raise RuntimeError("No public ports available")
|
|
62
|
+
public_port = random.randint(RANDOM_PORT_START, RANDOM_PORT_END)
|
|
63
|
+
trying(f"Trying random public port {public_port}")
|
|
64
|
+
registered = await register_port(reader, writer, public_port, config)
|
|
65
|
+
while True:
|
|
66
|
+
command_line = await reader.readline()
|
|
67
|
+
if not command_line:
|
|
68
|
+
error("Server disconnected")
|
|
69
|
+
break
|
|
70
|
+
parts = command_line.decode().strip().split()
|
|
71
|
+
if len(parts) == 2 and parts[0] == CONNECT:
|
|
72
|
+
trying(f"Setting up connection {parts[1][:8]}")
|
|
73
|
+
asyncio.create_task(handle_connection(parts[1], config))
|
|
74
|
+
except asyncio.CancelledError:
|
|
75
|
+
raise
|
|
76
|
+
except Exception as exc:
|
|
77
|
+
error(f"Control connection error: {exc}")
|
|
78
|
+
finally:
|
|
79
|
+
if writer is not None:
|
|
80
|
+
writer.close()
|
|
81
|
+
try:
|
|
82
|
+
await writer.wait_closed()
|
|
83
|
+
except Exception:
|
|
84
|
+
pass
|
|
85
|
+
trying("Retrying connection in 5 seconds")
|
|
86
|
+
await asyncio.sleep(5)
|
client/tcp/tunnel.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from common.logger import connected, error, trying
|
|
4
|
+
from common.protocol import command, DATA
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def pipe(reader, writer):
|
|
8
|
+
try:
|
|
9
|
+
while True:
|
|
10
|
+
data = await reader.read(65536)
|
|
11
|
+
if not data:
|
|
12
|
+
break
|
|
13
|
+
writer.write(data)
|
|
14
|
+
await writer.drain()
|
|
15
|
+
except Exception:
|
|
16
|
+
pass
|
|
17
|
+
finally:
|
|
18
|
+
try:
|
|
19
|
+
writer.close()
|
|
20
|
+
await writer.wait_closed()
|
|
21
|
+
except Exception:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def handle_connection(connection_id, config):
|
|
26
|
+
connected(f"New connection ({connection_id[:8]})")
|
|
27
|
+
try:
|
|
28
|
+
trying(f"Connecting to local service {config.local_host}:{config.local_port}")
|
|
29
|
+
local_reader, local_writer = await asyncio.open_connection(
|
|
30
|
+
config.local_host, config.local_port
|
|
31
|
+
)
|
|
32
|
+
trying(f"Opening data connection to {config.server_host}:{config.data_port}")
|
|
33
|
+
data_reader, data_writer = await asyncio.open_connection(
|
|
34
|
+
config.server_host, config.data_port
|
|
35
|
+
)
|
|
36
|
+
data_writer.write(command(DATA, connection_id))
|
|
37
|
+
await data_writer.drain()
|
|
38
|
+
connected(f"Tunnel established -> {config.local_host}:{config.local_port}")
|
|
39
|
+
await asyncio.gather(
|
|
40
|
+
pipe(data_reader, local_writer),
|
|
41
|
+
pipe(local_reader, data_writer),
|
|
42
|
+
)
|
|
43
|
+
except Exception as exc:
|
|
44
|
+
error(f"Connection failed: {exc}")
|
client/udp/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Placeholder package for future UDP client transport support."""
|
client/udp/connection.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import random
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from common.logger import connected, error, trying
|
|
6
|
+
from common.protocol import CONNECT, REGISTER, REGISTERED, UDP, command
|
|
7
|
+
from client.udp.tunnel import handle_connection
|
|
8
|
+
|
|
9
|
+
RANDOM_PORT_START = 20000
|
|
10
|
+
RANDOM_PORT_END = 30000
|
|
11
|
+
MAX_PORT_ATTEMPTS = 100
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class AgentConfig:
|
|
16
|
+
server_host: str
|
|
17
|
+
agent_token: str
|
|
18
|
+
control_port: int
|
|
19
|
+
data_port: int
|
|
20
|
+
local_host: str
|
|
21
|
+
local_port: int
|
|
22
|
+
preferred_public_port: int
|
|
23
|
+
explicit_public_port: bool
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def register_port(reader, writer, port, config):
|
|
27
|
+
trying(f"Trying to register UDP public port {port}")
|
|
28
|
+
writer.write(command(REGISTER, f"{port} {config.agent_token} {UDP}"))
|
|
29
|
+
await writer.drain()
|
|
30
|
+
response = await reader.readline()
|
|
31
|
+
if response.startswith(REGISTERED.encode()):
|
|
32
|
+
connected(f"UDP tunnel active: {config.server_host}:{port}")
|
|
33
|
+
connected(f"Forwarding UDP to: {config.local_host}:{config.local_port}")
|
|
34
|
+
return True
|
|
35
|
+
error(f"UDP port {port} unavailable")
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def connect_to_server(config):
|
|
40
|
+
while True:
|
|
41
|
+
writer = None
|
|
42
|
+
try:
|
|
43
|
+
trying(f"Connecting to Exposr {config.server_host}:{config.control_port}")
|
|
44
|
+
reader, writer = await asyncio.open_connection(config.server_host, config.control_port)
|
|
45
|
+
connected("Connected to Exposr control server")
|
|
46
|
+
public_port = config.preferred_public_port
|
|
47
|
+
registered = await register_port(reader, writer, public_port, config)
|
|
48
|
+
if not registered and config.explicit_public_port:
|
|
49
|
+
raise RuntimeError(f"UDP port {public_port} is unavailable")
|
|
50
|
+
attempts = 0
|
|
51
|
+
while not registered:
|
|
52
|
+
attempts += 1
|
|
53
|
+
if attempts > MAX_PORT_ATTEMPTS:
|
|
54
|
+
raise RuntimeError("No UDP public ports available")
|
|
55
|
+
public_port = random.randint(RANDOM_PORT_START, RANDOM_PORT_END)
|
|
56
|
+
registered = await register_port(reader, writer, public_port, config)
|
|
57
|
+
while True:
|
|
58
|
+
line = await reader.readline()
|
|
59
|
+
if not line:
|
|
60
|
+
break
|
|
61
|
+
parts = line.decode().strip().split()
|
|
62
|
+
if len(parts) == 2 and parts[0] == CONNECT:
|
|
63
|
+
asyncio.create_task(handle_connection(parts[1], config))
|
|
64
|
+
except asyncio.CancelledError:
|
|
65
|
+
raise
|
|
66
|
+
except Exception as exc:
|
|
67
|
+
error(f"UDP control connection error: {exc}")
|
|
68
|
+
finally:
|
|
69
|
+
if writer is not None:
|
|
70
|
+
writer.close()
|
|
71
|
+
try:
|
|
72
|
+
await writer.wait_closed()
|
|
73
|
+
except Exception:
|
|
74
|
+
pass
|
|
75
|
+
trying("Retrying UDP connection in 5 seconds")
|
|
76
|
+
await asyncio.sleep(5)
|
client/udp/tunnel.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import struct
|
|
3
|
+
|
|
4
|
+
from common.logger import connected, error, trying
|
|
5
|
+
from common.protocol import DATA, command
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def pack_datagram(data):
|
|
9
|
+
return struct.pack("!I", len(data)) + data
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def read_datagram(reader):
|
|
13
|
+
header = await reader.readexactly(4)
|
|
14
|
+
size = struct.unpack("!I", header)[0]
|
|
15
|
+
if size > 65535:
|
|
16
|
+
raise ValueError("UDP datagram is too large")
|
|
17
|
+
return await reader.readexactly(size)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LocalUdpProtocol(asyncio.DatagramProtocol):
|
|
21
|
+
def __init__(self, queue):
|
|
22
|
+
self.queue = queue
|
|
23
|
+
|
|
24
|
+
def datagram_received(self, data, address):
|
|
25
|
+
self.queue.put_nowait(data)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def handle_connection(connection_id, config):
|
|
29
|
+
data_writer = None
|
|
30
|
+
local_transport = None
|
|
31
|
+
try:
|
|
32
|
+
trying(f"Connecting to local UDP service {config.local_host}:{config.local_port}")
|
|
33
|
+
queue = asyncio.Queue()
|
|
34
|
+
loop = asyncio.get_running_loop()
|
|
35
|
+
local_transport, _ = await loop.create_datagram_endpoint(
|
|
36
|
+
lambda: LocalUdpProtocol(queue),
|
|
37
|
+
remote_addr=(config.local_host, config.local_port),
|
|
38
|
+
)
|
|
39
|
+
trying(f"Opening UDP data connection to {config.server_host}:{config.data_port}")
|
|
40
|
+
data_reader, data_writer = await asyncio.open_connection(
|
|
41
|
+
config.server_host, config.data_port
|
|
42
|
+
)
|
|
43
|
+
data_writer.write(command(DATA, connection_id))
|
|
44
|
+
await data_writer.drain()
|
|
45
|
+
connected(f"UDP tunnel established -> {config.local_host}:{config.local_port}")
|
|
46
|
+
|
|
47
|
+
async def send_local_datagrams():
|
|
48
|
+
while True:
|
|
49
|
+
data = await queue.get()
|
|
50
|
+
data_writer.write(pack_datagram(data))
|
|
51
|
+
await data_writer.drain()
|
|
52
|
+
|
|
53
|
+
async def receive_local_datagrams():
|
|
54
|
+
while True:
|
|
55
|
+
local_transport.sendto(await read_datagram(data_reader))
|
|
56
|
+
|
|
57
|
+
await asyncio.gather(send_local_datagrams(), receive_local_datagrams())
|
|
58
|
+
except Exception as exc:
|
|
59
|
+
error(f"UDP connection failed: {exc}")
|
|
60
|
+
finally:
|
|
61
|
+
if local_transport is not None:
|
|
62
|
+
local_transport.close()
|
|
63
|
+
if data_writer is not None:
|
|
64
|
+
data_writer.close()
|
|
65
|
+
try:
|
|
66
|
+
await data_writer.wait_closed()
|
|
67
|
+
except Exception:
|
|
68
|
+
pass
|
common/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Shared Exposr helpers."""
|
common/cli.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def parse_arguments(argv=None):
|
|
5
|
+
parser = argparse.ArgumentParser(
|
|
6
|
+
prog="exposr",
|
|
7
|
+
description="Exposr TCP and UDP tunneling agent",
|
|
8
|
+
)
|
|
9
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
10
|
+
|
|
11
|
+
def add_tunnel_arguments(tunnel_parser):
|
|
12
|
+
tunnel_parser.add_argument("local_port", type=int)
|
|
13
|
+
tunnel_parser.add_argument(
|
|
14
|
+
"public_port",
|
|
15
|
+
nargs="?",
|
|
16
|
+
type=int,
|
|
17
|
+
default=None,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Accept configuration flags both before and after the subcommand.
|
|
21
|
+
tunnel_parser.add_argument("--server-host", dest="server_host", default=argparse.SUPPRESS)
|
|
22
|
+
tunnel_parser.add_argument("--control-port", dest="control_port", type=int, default=argparse.SUPPRESS)
|
|
23
|
+
tunnel_parser.add_argument("--data-port", dest="data_port", type=int, default=argparse.SUPPRESS)
|
|
24
|
+
tunnel_parser.add_argument("--local-host", dest="local_host", default=argparse.SUPPRESS)
|
|
25
|
+
|
|
26
|
+
tcp_parser = subparsers.add_parser(
|
|
27
|
+
"tcp", help="Expose a local TCP port to the internet"
|
|
28
|
+
)
|
|
29
|
+
add_tunnel_arguments(tcp_parser)
|
|
30
|
+
|
|
31
|
+
udp_parser = subparsers.add_parser(
|
|
32
|
+
"udp", help="Expose a local UDP port to the internet"
|
|
33
|
+
)
|
|
34
|
+
add_tunnel_arguments(udp_parser)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
config_parser = subparsers.add_parser("config", help="Manage Exposr configuration")
|
|
38
|
+
config_subparsers = config_parser.add_subparsers(dest="config_command", required=True)
|
|
39
|
+
set_server_parser = config_subparsers.add_parser(
|
|
40
|
+
"set-server", help="Set the Exposr relay server address"
|
|
41
|
+
)
|
|
42
|
+
set_server_parser.add_argument("server_host")
|
|
43
|
+
|
|
44
|
+
server_parser = subparsers.add_parser("server", help="Manage the Exposr relay server")
|
|
45
|
+
server_subparsers = server_parser.add_subparsers(
|
|
46
|
+
dest="server_command", required=True
|
|
47
|
+
)
|
|
48
|
+
init_token_parser = server_subparsers.add_parser(
|
|
49
|
+
"init-token", help="Set the relay server agent token"
|
|
50
|
+
)
|
|
51
|
+
init_token_parser.add_argument("agent_token")
|
|
52
|
+
server_subparsers.add_parser("start", help="Start the Exposr relay server")
|
|
53
|
+
|
|
54
|
+
parser.add_argument("--server-host", default=None)
|
|
55
|
+
parser.add_argument("--control-port", type=int, default=9000)
|
|
56
|
+
parser.add_argument("--data-port", type=int, default=9001)
|
|
57
|
+
parser.add_argument("--local-host", default="127.0.0.1")
|
|
58
|
+
|
|
59
|
+
args = parser.parse_args(argv)
|
|
60
|
+
return args
|
common/logger.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
GREEN = "\033[92m"
|
|
2
|
+
YELLOW = "\033[93m"
|
|
3
|
+
RED = "\033[91m"
|
|
4
|
+
BLUE = "\033[94m"
|
|
5
|
+
RESET = "\033[0m"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def connected(message):
|
|
9
|
+
print(f"{GREEN}[CONNECTED]{RESET} {message}")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def trying(message):
|
|
13
|
+
print(f"{YELLOW}[TRYING]{RESET} {message}")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def error(message):
|
|
17
|
+
print(f"{RED}[ERROR]{RESET} {message}")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def info(message):
|
|
21
|
+
print(f"{BLUE}[INFO]{RESET} {message}")
|