messenger-proxy 0.4.2__tar.gz → 0.4.4__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.
- {messenger_proxy-0.4.2/messenger_proxy.egg-info → messenger_proxy-0.4.4}/PKG-INFO +4 -4
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/README.md +3 -3
- messenger_proxy-0.4.4/builder/clients/csharp/README.md +137 -0
- messenger_proxy-0.4.4/builder/clients/csharp/builder.py +68 -0
- {messenger_proxy-0.4.2/builder/clients/csharp → messenger_proxy-0.4.4/builder/clients/csharp/templates}/MessengerClient/Crypto.cs +5 -4
- messenger_proxy-0.4.4/builder/clients/csharp/templates/MessengerClient/HTTPMessengerClient.cs +174 -0
- {messenger_proxy-0.4.2/builder/clients/csharp → messenger_proxy-0.4.4/builder/clients/csharp/templates}/MessengerClient/Message.cs +7 -66
- {messenger_proxy-0.4.2/builder/clients/csharp → messenger_proxy-0.4.4/builder/clients/csharp/templates}/MessengerClient/MessengerClient.cs +19 -12
- messenger_proxy-0.4.4/builder/clients/csharp/templates/MessengerClient/MessengerClient.csproj +14 -0
- messenger_proxy-0.4.4/builder/clients/csharp/templates/MessengerClient/Program.cs +197 -0
- {messenger_proxy-0.4.2/builder/clients/csharp → messenger_proxy-0.4.4/builder/clients/csharp/templates}/MessengerClient/RemotePortForwarder.cs +3 -19
- messenger_proxy-0.4.2/builder/clients/csharp/MessengerClient/WebsocketMessengerClient.cs → messenger_proxy-0.4.4/builder/clients/csharp/templates/MessengerClient/WebSocketMessengerClient.cs +74 -47
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/nodejs/README.md +29 -14
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/nodejs/builder.py +18 -5
- messenger_proxy-0.4.2/builder/clients/nodejs/electron → messenger_proxy-0.4.4/builder/clients/nodejs/templates/electron-main.js +10 -12
- messenger_proxy-0.4.4/builder/clients/nodejs/templates/electron-renderer.html +10 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/nodejs/templates/messenger-client.js +129 -51
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/python/README.md +3 -3
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/python/builder.py +2 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/python/templates/messenger-client.py +55 -33
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/__init__.py +1 -1
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4/messenger_proxy.egg-info}/PKG-INFO +4 -4
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger_proxy.egg-info/SOURCES.txt +12 -12
- messenger_proxy-0.4.2/builder/clients/csharp/MessengerClient/HTTPMessengerClient.cs +0 -173
- messenger_proxy-0.4.2/builder/clients/csharp/MessengerClient/MessengerClient.csproj +0 -62
- messenger_proxy-0.4.2/builder/clients/csharp/MessengerClient/MessengerClient.sln +0 -25
- messenger_proxy-0.4.2/builder/clients/csharp/MessengerClient/Program.cs +0 -171
- messenger_proxy-0.4.2/builder/clients/csharp/MessengerClient/Properties/AssemblyInfo.cs +0 -36
- messenger_proxy-0.4.2/builder/clients/csharp/README.md +0 -41
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/LICENSE +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/MANIFEST.in +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/__init__.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/__init__.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/csharp/.gitignore +0 -0
- {messenger_proxy-0.4.2/builder/clients/csharp → messenger_proxy-0.4.4/builder/clients/csharp/templates}/MessengerClient/App.config +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/nodejs/.gitignore +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/nodejs/requirements.txt +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/nodejs/webpack.conf.js +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/python/.github/workflows/python-version-tests.yml +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/python/.gitignore +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/builder/clients/python/requirements.txt +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/aes.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/convert.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/engine.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/forwarder_clients.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/forwarders.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/generator.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/http_ws_server.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/manager.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/message.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/messengers.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/output.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/resources/top_ports.txt +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/scanner.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger/text.py +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger-builder +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger-cli +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger_proxy.egg-info/dependency_links.txt +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger_proxy.egg-info/requires.txt +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/messenger_proxy.egg-info/top_level.txt +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/setup.cfg +0 -0
- {messenger_proxy-0.4.2 → messenger_proxy-0.4.4}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: messenger-proxy
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: Messenger uses a client-server architecture to establish a SOCKS5 tunnel. Once the client connects, the server will create a local SOCKS5 tunnel that can be used to interact with the local network the client is connected to.
|
|
5
5
|
Home-page: https://github.com/skylerknecht/messenger
|
|
6
6
|
Author: Skyler Knecht
|
|
@@ -67,7 +67,7 @@ operator~# messenger-cli -e readme
|
|
|
67
67
|
| |\/| |/ _ \/ __/ __|/ _ \ '_ \ / _` |/ _ \ '__|
|
|
68
68
|
| | | | __/\__ \__ \ __/ | | | (_| | __/ |
|
|
69
69
|
|_| |_|\___||___/___/\___|_| |_|\__, |\___|_|
|
|
70
|
-
by Skyler Knecht and Kevin Clark |___/ v0.4.
|
|
70
|
+
by Skyler Knecht and Kevin Clark |___/ v0.4.4
|
|
71
71
|
[*] The AES encryption key is readme
|
|
72
72
|
[*] Waiting for messengers on http+ws://0.0.0.0:8080/
|
|
73
73
|
(messenger)~#
|
|
@@ -108,8 +108,8 @@ operator~# ./client.py
|
|
|
108
108
|
| Clients | Messenger Builder | Protocols | Local/Remote Port Forwarding | SOCKS5 TCP | SOCKS5 UDP |
|
|
109
109
|
|--------------------------------------------------------------------|-------------------|-------------------|------------------------------|------------|---------------|
|
|
110
110
|
| [Python](https://github.com/skylerknecht/messenger-client-python) | Supported | HTTP & WebSockets | Supported | Supported | Not Supported |
|
|
111
|
-
| [C#](https://github.com/skylerknecht/messenger-client-
|
|
112
|
-
| [Node JS](https://github.com/skylerknecht/messenger-client-nodejs) |
|
|
111
|
+
| [C#](https://github.com/skylerknecht/messenger-client-csharp) | Supported | HTTP & WebSockets | Supported | Supported | Not Supported |
|
|
112
|
+
| [Node JS](https://github.com/skylerknecht/messenger-client-nodejs) | Supported | HTTP & WebSockets | Supported | Supported | Not Supported |
|
|
113
113
|
|
|
114
114
|
## Credits
|
|
115
115
|
|
|
@@ -37,7 +37,7 @@ operator~# messenger-cli -e readme
|
|
|
37
37
|
| |\/| |/ _ \/ __/ __|/ _ \ '_ \ / _` |/ _ \ '__|
|
|
38
38
|
| | | | __/\__ \__ \ __/ | | | (_| | __/ |
|
|
39
39
|
|_| |_|\___||___/___/\___|_| |_|\__, |\___|_|
|
|
40
|
-
by Skyler Knecht and Kevin Clark |___/ v0.4.
|
|
40
|
+
by Skyler Knecht and Kevin Clark |___/ v0.4.4
|
|
41
41
|
[*] The AES encryption key is readme
|
|
42
42
|
[*] Waiting for messengers on http+ws://0.0.0.0:8080/
|
|
43
43
|
(messenger)~#
|
|
@@ -78,8 +78,8 @@ operator~# ./client.py
|
|
|
78
78
|
| Clients | Messenger Builder | Protocols | Local/Remote Port Forwarding | SOCKS5 TCP | SOCKS5 UDP |
|
|
79
79
|
|--------------------------------------------------------------------|-------------------|-------------------|------------------------------|------------|---------------|
|
|
80
80
|
| [Python](https://github.com/skylerknecht/messenger-client-python) | Supported | HTTP & WebSockets | Supported | Supported | Not Supported |
|
|
81
|
-
| [C#](https://github.com/skylerknecht/messenger-client-
|
|
82
|
-
| [Node JS](https://github.com/skylerknecht/messenger-client-nodejs) |
|
|
81
|
+
| [C#](https://github.com/skylerknecht/messenger-client-csharp) | Supported | HTTP & WebSockets | Supported | Supported | Not Supported |
|
|
82
|
+
| [Node JS](https://github.com/skylerknecht/messenger-client-nodejs) | Supported | HTTP & WebSockets | Supported | Supported | Not Supported |
|
|
83
83
|
|
|
84
84
|
## Credits
|
|
85
85
|
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# C# Messenger Client
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Client is a .NET Framework 4.7.2 Messenger Client targeting Windows environments. The framework is preinstalled on Windows 10/11 and most enterprise systems, requiring no additional runtime to deploy.
|
|
6
|
+
|
|
7
|
+
## Primary Capabilities
|
|
8
|
+
|
|
9
|
+
| Capability | Support Status |
|
|
10
|
+
|----------------------------|--------------------------------------------------------|
|
|
11
|
+
| Transports | HTTP and WebSockets |
|
|
12
|
+
| Encryption | AES-256-CBC with random IV prefix. |
|
|
13
|
+
| Reconnection procedure | Defaults to five (5) attempts over sixty (60) seconds. |
|
|
14
|
+
| SOCKS5 TCP | Supported |
|
|
15
|
+
| SOCKS5 UDP | Not Supported |
|
|
16
|
+
|
|
17
|
+
## Client-Specific Capabilities
|
|
18
|
+
|
|
19
|
+
| Capability | Support Status |
|
|
20
|
+
|-------------------------------|--------------------------------------------------------------------------------------------------------|
|
|
21
|
+
| Compilation | Outputs a project directory requiring compilation with `dotnet build` (.NET SDK). |
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
operator~# python builder.py --encryption-key test
|
|
27
|
+
[+] Wrote C# client to 'MessengerClient'
|
|
28
|
+
|
|
29
|
+
operator~# cd MessengerClient/MessengerClient
|
|
30
|
+
operator~# dotnet build -c Release
|
|
31
|
+
operator~# bin\Release\net472\MessengerClient.exe
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
To build the client, execute `builder.py` or `messenger-builder` from the [Messenger Repository](https://github.com/skylerknecht/messenger).
|
|
37
|
+
|
|
38
|
+
Both scripts accept the same options and will generate a C# Messenger Client project directory. If provided options, the builder scripts
|
|
39
|
+
will hard-code the options into the source files. Once built, the operator can specify command-line arguments that will override
|
|
40
|
+
the hardcoded options. The output is a .NET Framework 4.7.2 project that can be compiled with
|
|
41
|
+
`dotnet build` on any platform. Those options and their definitions are shown below.
|
|
42
|
+
|
|
43
|
+
## Client Options
|
|
44
|
+
|
|
45
|
+
| Option | Flag | Default Value |
|
|
46
|
+
|-----------------------------------------------|---------------------------|------------------------|
|
|
47
|
+
| [Server URL](#server-url) | `--server-url` | localhost:8080 |
|
|
48
|
+
| [Encryption Key](#encryption-key) | `--encryption-key` | None |
|
|
49
|
+
| [User Agent](#user-agent) | `--user-agent` | [Specified Here](https://github.com/skylerknecht/messenger-client-csharp/blob/main/builder.py#L7) |
|
|
50
|
+
| [Proxy](#proxy) | `--proxy` | None |
|
|
51
|
+
| [Remote Port Forwards](#remote-port-forwards) | `--remote-port-forwards` | None |
|
|
52
|
+
| [Retry Duration](#retry-duration) | `--retry-duration` | One Minute |
|
|
53
|
+
| [Retry Attempts](#retry-attempts) | `--retry-attempts` | Five |
|
|
54
|
+
| [Name](#name) | `--name` | MessengerClient |
|
|
55
|
+
|
|
56
|
+
### Server URL
|
|
57
|
+
|
|
58
|
+
Once the Messenger Server is running, the operator will be provided a server URL that can be set with `--server-url`.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
builder.py --server-url http://localhost:8080
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The client will attempt to establish a connection to the server based on the protocol specified in the server URL. For HTTP, leave the protocol as
|
|
65
|
+
`http://`, for websockets use `ws://`. Given that the server is listening with SSL encryption, provide the SSL
|
|
66
|
+
alternative to each protocol.
|
|
67
|
+
|
|
68
|
+
#### Encryption Key
|
|
69
|
+
|
|
70
|
+
Messenger Server will also provide an encryption key upon startup that can be hardcoded.
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
builder.py --encryption-key SuP3rs_crEtk3y
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Since the server expects encryption, the default will likely cause issues; therefore, the client outputs an
|
|
77
|
+
error.
|
|
78
|
+
|
|
79
|
+
#### User Agent
|
|
80
|
+
|
|
81
|
+
For HTTP-based protocols, the operator can control the user-agent header.
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
builder.py --user-agent "Test User Agent"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### Proxy
|
|
88
|
+
|
|
89
|
+
Enterprise environments typically have outbound proxies. Operators can provide a proxy using the HTTP-proxy schema.
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
builder.py --proxy http://user:password@localhost:8080
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### Remote Port Forwards
|
|
96
|
+
|
|
97
|
+
Messenger expects clients to attempt to set up remote port forwards on the client side. The operator can specify multiple port forwards
|
|
98
|
+
with the schema `LISTENING-HOST:LISTENING-PORT:DESTINATION-HOST:DESTINATION-PORT`.
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
builder.py --remote-port-forwards localhost:8080:remotehost:8080
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
This will forward all local connections on 8080 to a remote host on 8080. Given that the operator has not permitted the connection server-side,
|
|
105
|
+
they will see the following message.
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
[!] Messenger `test` has no Remote Port Forwarder configured for remotehost:8080, denying forward!
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Retry Duration
|
|
112
|
+
|
|
113
|
+
Clients will disconnect for various reasons. Given that the client does not completely exit, it will attempt to reconnect. Operators can
|
|
114
|
+
control how long the client will attempt to reconnect by specifying a retry duration. This value is expected to be in seconds. For example,
|
|
115
|
+
if the retry duration is set to 120, then the client will attempt to reconnect for two minutes.
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
builder.py --retry-duration 100
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
To disable reconnection attempts, set the retry attempts option to 0.
|
|
122
|
+
|
|
123
|
+
#### Retry Attempts
|
|
124
|
+
|
|
125
|
+
In combination with the retry duration, retry attempts determine the minimum time the client waits between reconnection attempts.
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
builder.py --retry-attempts 100
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### Name
|
|
132
|
+
|
|
133
|
+
The build process outputs a project directory, and operators can control its name.
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
builder.py --name CustomClient
|
|
137
|
+
```
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import shutil
|
|
3
|
+
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
|
6
|
+
|
|
7
|
+
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"
|
|
8
|
+
|
|
9
|
+
def add_arguments(parser):
|
|
10
|
+
builder = parser.add_argument_group("Builder options")
|
|
11
|
+
builder.add_argument("--name", default="MessengerClient",
|
|
12
|
+
help="Name of the output directory.")
|
|
13
|
+
|
|
14
|
+
cfg = parser.add_argument_group("Client configuration")
|
|
15
|
+
cfg.add_argument("--server-url", default="localhost:8080",
|
|
16
|
+
help="Server URL the client should connect to.")
|
|
17
|
+
cfg.add_argument("-e", "--encryption-key", default="",
|
|
18
|
+
help="AES encryption key to embed (optional).")
|
|
19
|
+
cfg.add_argument("--user-agent", default=USER_AGENT,
|
|
20
|
+
help="Custom HTTP/WebSocket User-Agent string (optional).")
|
|
21
|
+
cfg.add_argument("--proxy", default="",
|
|
22
|
+
help="Proxy to use (optional).")
|
|
23
|
+
cfg.add_argument("--remote-port-forwards", nargs="*", default=[],
|
|
24
|
+
help="Space delimited remote port forwards LISTENING-IP:LISTENING-PORT:REMOTE-IP:REMOTE-PORT (optional).")
|
|
25
|
+
|
|
26
|
+
retry = parser.add_argument_group("Retry behavior")
|
|
27
|
+
retry.add_argument("--retry-duration", type=float, default=60.0,
|
|
28
|
+
help="Total time to retry connecting.")
|
|
29
|
+
retry.add_argument("--retry-attempts", type=int, default=5,
|
|
30
|
+
help="Number of retry attempts before giving up.")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def build(args):
|
|
34
|
+
template_dir = Path(__file__).resolve().parent / "templates"
|
|
35
|
+
if not template_dir.is_dir():
|
|
36
|
+
raise RuntimeError(f"Template directory not found: {template_dir}")
|
|
37
|
+
|
|
38
|
+
env = Environment(
|
|
39
|
+
loader=FileSystemLoader(str(template_dir)),
|
|
40
|
+
autoescape=select_autoescape(enabled_extensions=("j2",)),
|
|
41
|
+
trim_blocks=True,
|
|
42
|
+
lstrip_blocks=True,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
out_dir = Path(args.name)
|
|
46
|
+
|
|
47
|
+
for src in sorted(template_dir.rglob("*")):
|
|
48
|
+
if src.is_dir():
|
|
49
|
+
continue
|
|
50
|
+
rel = src.relative_to(template_dir)
|
|
51
|
+
dest = out_dir / rel
|
|
52
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
|
|
54
|
+
if src.suffix == ".cs":
|
|
55
|
+
template = env.get_template(str(rel).replace("\\", "/"))
|
|
56
|
+
rendered = template.render(**vars(args))
|
|
57
|
+
dest.write_text(rendered, encoding="utf-8")
|
|
58
|
+
else:
|
|
59
|
+
shutil.copy2(src, dest)
|
|
60
|
+
|
|
61
|
+
print(f"[+] Wrote C# client to '{out_dir}'")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
parser = argparse.ArgumentParser(usage=argparse.SUPPRESS)
|
|
66
|
+
add_arguments(parser)
|
|
67
|
+
parsed_args = parser.parse_args()
|
|
68
|
+
build(parsed_args)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
using System;
|
|
2
2
|
using System.Linq;
|
|
3
3
|
using System.Text;
|
|
4
4
|
using System.Security.Cryptography;
|
|
@@ -15,13 +15,14 @@ namespace MessengerClient
|
|
|
15
15
|
{
|
|
16
16
|
using (SHA256 sha256 = SHA256.Create())
|
|
17
17
|
{
|
|
18
|
-
return sha256.ComputeHash(Encoding.
|
|
18
|
+
return sha256.ComputeHash(Encoding.UTF8.GetBytes(encryption_key));
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
public static byte[] Encrypt(byte[] key, ArraySegment<byte> plainText)
|
|
23
23
|
{
|
|
24
|
-
byte[] plainTextBytes = plainText.
|
|
24
|
+
byte[] plainTextBytes = new byte[plainText.Count];
|
|
25
|
+
Array.Copy(plainText.Array, plainText.Offset, plainTextBytes, 0, plainText.Count);
|
|
25
26
|
return Encrypt(key, plainTextBytes);
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -82,4 +83,4 @@ namespace MessengerClient
|
|
|
82
83
|
return merged;
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
|
-
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Concurrent;
|
|
3
|
+
using System.Collections.Generic;
|
|
4
|
+
using System.Net;
|
|
5
|
+
using System.Net.Http;
|
|
6
|
+
using System.Threading.Tasks;
|
|
7
|
+
|
|
8
|
+
namespace MessengerClient
|
|
9
|
+
{
|
|
10
|
+
public class HTTPMessengerClient : MessengerClient
|
|
11
|
+
{
|
|
12
|
+
private readonly string _uri;
|
|
13
|
+
private readonly HttpClient _httpClient;
|
|
14
|
+
private readonly byte[] _encryptionKey;
|
|
15
|
+
private readonly double _retryDuration;
|
|
16
|
+
private readonly int _retryAttempts;
|
|
17
|
+
private readonly ConcurrentQueue<object> _downstreamMessages;
|
|
18
|
+
private string _messengerId;
|
|
19
|
+
|
|
20
|
+
public HTTPMessengerClient(string uri, byte[] encryptionKey, string userAgent, double retryDuration, int retryAttempts, IWebProxy proxy = null)
|
|
21
|
+
{
|
|
22
|
+
_uri = uri;
|
|
23
|
+
_encryptionKey = encryptionKey;
|
|
24
|
+
_retryDuration = retryDuration;
|
|
25
|
+
_retryAttempts = retryAttempts;
|
|
26
|
+
|
|
27
|
+
var handler = new HttpClientHandler();
|
|
28
|
+
if (proxy != null)
|
|
29
|
+
{
|
|
30
|
+
handler.Proxy = proxy;
|
|
31
|
+
handler.UseProxy = true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_httpClient = new HttpClient(handler);
|
|
35
|
+
_httpClient.DefaultRequestHeaders.Add("User-Agent", userAgent);
|
|
36
|
+
_downstreamMessages = new ConcurrentQueue<object>();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public override async Task ConnectAsync()
|
|
40
|
+
{
|
|
41
|
+
int retryDelay = (int)((_retryDuration / _retryAttempts) * 1000);
|
|
42
|
+
int consecutiveFailures = 0;
|
|
43
|
+
|
|
44
|
+
while (consecutiveFailures < _retryAttempts)
|
|
45
|
+
{
|
|
46
|
+
try
|
|
47
|
+
{
|
|
48
|
+
Console.WriteLine($"Connecting to HTTP server at {_uri}");
|
|
49
|
+
|
|
50
|
+
var downstreamMessage = MessageBuilder.SerializeMessage(_encryptionKey, new CheckInMessage(_messengerId));
|
|
51
|
+
HttpContent content = new ByteArrayContent(downstreamMessage);
|
|
52
|
+
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
|
|
53
|
+
|
|
54
|
+
var response = await _httpClient.PostAsync(_uri, content);
|
|
55
|
+
response.EnsureSuccessStatusCode();
|
|
56
|
+
|
|
57
|
+
if (string.IsNullOrEmpty(_messengerId))
|
|
58
|
+
{
|
|
59
|
+
byte[] responseBytes = await response.Content.ReadAsByteArrayAsync();
|
|
60
|
+
var (_, parsedMessage) = MessageParser.DeserializeMessage(_encryptionKey, responseBytes);
|
|
61
|
+
|
|
62
|
+
if (parsedMessage is CheckInMessage checkInMsg)
|
|
63
|
+
{
|
|
64
|
+
_messengerId = checkInMsg.MessengerId;
|
|
65
|
+
Console.WriteLine($"[+] Connected with Messenger ID: {_messengerId}");
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
{
|
|
69
|
+
throw new InvalidOperationException(
|
|
70
|
+
$"Expected CheckInMessage, got {parsedMessage.GetType().Name}"
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
consecutiveFailures = 0;
|
|
76
|
+
await PollServerAsync();
|
|
77
|
+
}
|
|
78
|
+
catch (Exception ex)
|
|
79
|
+
{
|
|
80
|
+
consecutiveFailures++;
|
|
81
|
+
Console.WriteLine($"[!] Connection failed: {ex.Message}");
|
|
82
|
+
if (consecutiveFailures < _retryAttempts)
|
|
83
|
+
{
|
|
84
|
+
Console.WriteLine("[*] Retrying connection...");
|
|
85
|
+
await Task.Delay(retryDelay);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
Console.WriteLine($"[-] Reconnect failed after {_retryAttempts} attempts. Giving up.");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
private async Task PollServerAsync()
|
|
95
|
+
{
|
|
96
|
+
while (true)
|
|
97
|
+
{
|
|
98
|
+
var downstreamMessages = new List<object>();
|
|
99
|
+
|
|
100
|
+
CheckInMessage checkInMessage = new CheckInMessage(_messengerId);
|
|
101
|
+
downstreamMessages.Add(checkInMessage);
|
|
102
|
+
|
|
103
|
+
while (_downstreamMessages.TryDequeue(out var message))
|
|
104
|
+
{
|
|
105
|
+
downstreamMessages.Add(message);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
HttpContent content = new ByteArrayContent(SerializeMessages(_encryptionKey, downstreamMessages));
|
|
109
|
+
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
|
|
110
|
+
var response = await _httpClient.PostAsync(_uri, content);
|
|
111
|
+
|
|
112
|
+
if (!response.IsSuccessStatusCode)
|
|
113
|
+
throw new HttpRequestException($"Poll failed: HTTP {response.StatusCode}");
|
|
114
|
+
|
|
115
|
+
var responseData = await response.Content.ReadAsByteArrayAsync();
|
|
116
|
+
var messages = DeserializeMessages(_encryptionKey, responseData);
|
|
117
|
+
|
|
118
|
+
foreach (var msg in messages)
|
|
119
|
+
{
|
|
120
|
+
_ = Task.Run(() => HandleMessageAsync(msg));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
await Task.Delay(1000);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public override Task SendDownstreamMessageAsync(object message)
|
|
128
|
+
{
|
|
129
|
+
_downstreamMessages.Enqueue(message);
|
|
130
|
+
return Task.CompletedTask;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public override async Task HandleMessageAsync(object message)
|
|
134
|
+
{
|
|
135
|
+
switch (message)
|
|
136
|
+
{
|
|
137
|
+
case InitiateForwarderClientReq reqMessage:
|
|
138
|
+
await HandleInitiateForwarderClientReqAsync(reqMessage);
|
|
139
|
+
break;
|
|
140
|
+
|
|
141
|
+
case InitiateForwarderClientRep repMessage:
|
|
142
|
+
if (!ForwarderClients.TryGetValue(repMessage.ForwarderClientId, out var repClient))
|
|
143
|
+
break;
|
|
144
|
+
if (repMessage.Reason != 0)
|
|
145
|
+
{
|
|
146
|
+
if (ForwarderClients.TryRemove(repMessage.ForwarderClientId, out var denied))
|
|
147
|
+
denied.Close();
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
await StreamAsync(repMessage.ForwarderClientId);
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
case SendDataMessage sendDataMessage:
|
|
154
|
+
if (sendDataMessage.Data.Length == 0)
|
|
155
|
+
{
|
|
156
|
+
if (ForwarderClients.TryRemove(sendDataMessage.ForwarderClientId, out var closedClient))
|
|
157
|
+
closedClient.Close();
|
|
158
|
+
}
|
|
159
|
+
else if (ForwarderClients.TryGetValue(sendDataMessage.ForwarderClientId, out var client))
|
|
160
|
+
{
|
|
161
|
+
await client.GetStream().WriteAsync(sendDataMessage.Data, 0, sendDataMessage.Data.Length);
|
|
162
|
+
}
|
|
163
|
+
break;
|
|
164
|
+
|
|
165
|
+
case CheckInMessage checkInMessage:
|
|
166
|
+
break;
|
|
167
|
+
|
|
168
|
+
default:
|
|
169
|
+
Console.WriteLine("Unknown message type received");
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
using System;
|
|
2
2
|
using System.Linq;
|
|
3
3
|
using System.Text;
|
|
4
4
|
using System.Security.Cryptography;
|
|
@@ -71,29 +71,17 @@ public class SendDataMessage
|
|
|
71
71
|
// ----------------------------------------------------------------------
|
|
72
72
|
public static class MessageParser
|
|
73
73
|
{
|
|
74
|
-
/// <summary>
|
|
75
|
-
/// Read the first 4 bytes of <paramref name="data"/> as a big-endian uint32.
|
|
76
|
-
/// Returns (uintValue, leftoverBytes).
|
|
77
|
-
/// </summary>
|
|
78
74
|
public static (uint Value, byte[] Remainder) ReadUInt32(byte[] data)
|
|
79
75
|
{
|
|
80
76
|
if (data.Length < 4)
|
|
81
77
|
throw new ArgumentException("Not enough bytes to read a 32-bit value.");
|
|
82
78
|
|
|
83
|
-
// Big-endian decode into a uint
|
|
84
79
|
uint value = (uint)((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
|
|
85
80
|
byte[] remainder = data.Skip(4).ToArray();
|
|
86
81
|
|
|
87
82
|
return (value, remainder);
|
|
88
83
|
}
|
|
89
84
|
|
|
90
|
-
/// <summary>
|
|
91
|
-
/// Reads a length-prefixed UTF-8 string:
|
|
92
|
-
/// 1) read a uint32 length
|
|
93
|
-
/// 2) read 'length' bytes
|
|
94
|
-
/// 3) decode UTF-8
|
|
95
|
-
/// Returns (stringValue, leftoverBytes).
|
|
96
|
-
/// </summary>
|
|
97
85
|
public static (string Value, byte[] Remainder) ReadString(byte[] data)
|
|
98
86
|
{
|
|
99
87
|
var (length, remainder) = ReadUInt32(data);
|
|
@@ -106,25 +94,18 @@ public static class MessageParser
|
|
|
106
94
|
return (s, leftover);
|
|
107
95
|
}
|
|
108
96
|
|
|
109
|
-
/// <summary>
|
|
110
|
-
/// Parses the payload of a 0x04 'CheckIn' message (no decryption).
|
|
111
|
-
/// </summary>
|
|
112
97
|
public static CheckInMessage ParseCheckIn(byte[] value)
|
|
113
98
|
{
|
|
114
99
|
var (messengerId, _) = ReadString(value);
|
|
115
100
|
return new CheckInMessage(messengerId);
|
|
116
101
|
}
|
|
117
102
|
|
|
118
|
-
/// <summary>
|
|
119
|
-
/// Parses the payload of a 0x01 'InitiateForwarderClientReq' message.
|
|
120
|
-
/// </summary>
|
|
121
103
|
public static InitiateForwarderClientReq ParseInitiateForwarderClientReq(byte[] value)
|
|
122
104
|
{
|
|
123
105
|
var (forwarderClientId, remainder) = ReadString(value);
|
|
124
106
|
var (ipAddress, remainder2) = ReadString(remainder);
|
|
125
107
|
var (port, remainder3) = ReadUInt32(remainder2);
|
|
126
108
|
|
|
127
|
-
// cast uint -> int
|
|
128
109
|
return new InitiateForwarderClientReq(
|
|
129
110
|
forwarderClientId,
|
|
130
111
|
ipAddress,
|
|
@@ -132,9 +113,6 @@ public static class MessageParser
|
|
|
132
113
|
);
|
|
133
114
|
}
|
|
134
115
|
|
|
135
|
-
/// <summary>
|
|
136
|
-
/// Parses the payload of a 0x02 'InitiateForwarderClientRep' message.
|
|
137
|
-
/// </summary>
|
|
138
116
|
public static InitiateForwarderClientRep ParseInitiateForwarderClientRep(byte[] value)
|
|
139
117
|
{
|
|
140
118
|
var (forwarderClientId, remainder) = ReadString(value);
|
|
@@ -143,7 +121,6 @@ public static class MessageParser
|
|
|
143
121
|
var (addressType, remainder4) = ReadUInt32(remainder3);
|
|
144
122
|
var (reason, remainder5) = ReadUInt32(remainder4);
|
|
145
123
|
|
|
146
|
-
// cast all uint -> int
|
|
147
124
|
return new InitiateForwarderClientRep(
|
|
148
125
|
forwarderClientId,
|
|
149
126
|
bindAddress,
|
|
@@ -153,9 +130,6 @@ public static class MessageParser
|
|
|
153
130
|
);
|
|
154
131
|
}
|
|
155
132
|
|
|
156
|
-
/// <summary>
|
|
157
|
-
/// Parses the payload of a 0x03 'SendDataMessage'.
|
|
158
|
-
/// </summary>
|
|
159
133
|
public static SendDataMessage ParseSendData(byte[] value)
|
|
160
134
|
{
|
|
161
135
|
var (forwarderClientId, remainder) = ReadString(value);
|
|
@@ -168,33 +142,23 @@ public static class MessageParser
|
|
|
168
142
|
);
|
|
169
143
|
}
|
|
170
144
|
|
|
171
|
-
/// <summary>
|
|
172
|
-
/// High-level parse entrypoint:
|
|
173
|
-
/// 1) read the message_type (uint32)
|
|
174
|
-
/// 2) read the message_length (uint32)
|
|
175
|
-
/// 3) slice out the encrypted payload
|
|
176
|
-
/// 4) decrypt or parse plaintext
|
|
177
|
-
/// Returns (leftoverBytes, parsedMessage).
|
|
178
|
-
/// </summary>
|
|
179
145
|
public static (byte[] leftover, object parsedMessage) DeserializeMessage(
|
|
180
146
|
byte[] encryptionKey,
|
|
181
147
|
byte[] rawData)
|
|
182
148
|
{
|
|
183
|
-
// 1) Read the message type
|
|
184
149
|
var (messageType, dataAfterType) = ReadUInt32(rawData);
|
|
185
|
-
|
|
186
|
-
// 2) Read the total message length
|
|
187
150
|
var (messageLength, dataAfterLength) = ReadUInt32(dataAfterType);
|
|
188
151
|
|
|
189
|
-
|
|
190
|
-
|
|
152
|
+
if (messageLength < 8)
|
|
153
|
+
throw new ArgumentException("Invalid message: length field too small.");
|
|
154
|
+
|
|
155
|
+
int payloadLen = (int)(messageLength - 8);
|
|
191
156
|
if (dataAfterLength.Length < payloadLen)
|
|
192
157
|
throw new ArgumentException("Not enough bytes in data for the payload.");
|
|
193
158
|
|
|
194
159
|
byte[] payload = dataAfterLength.Take(payloadLen).ToArray();
|
|
195
160
|
byte[] leftover = dataAfterLength.Skip(payloadLen).ToArray();
|
|
196
161
|
|
|
197
|
-
// 4) Decrypt or parse plaintext, depending on message type
|
|
198
162
|
object parsedMsg;
|
|
199
163
|
switch (messageType)
|
|
200
164
|
{
|
|
@@ -218,7 +182,6 @@ public static class MessageParser
|
|
|
218
182
|
}
|
|
219
183
|
case 0x04:
|
|
220
184
|
{
|
|
221
|
-
// According to the Python version, we do NOT decrypt for 0x04
|
|
222
185
|
parsedMsg = ParseCheckIn(payload);
|
|
223
186
|
break;
|
|
224
187
|
}
|
|
@@ -236,10 +199,6 @@ public static class MessageParser
|
|
|
236
199
|
// ----------------------------------------------------------------------
|
|
237
200
|
public static class MessageBuilder
|
|
238
201
|
{
|
|
239
|
-
/// <summary>
|
|
240
|
-
/// Given a message object (one of our 4 types), build the fully formed
|
|
241
|
-
/// byte array (header + possibly encrypted payload).
|
|
242
|
-
/// </summary>
|
|
243
202
|
public static byte[] SerializeMessage(byte[] encryptionKey, object msg)
|
|
244
203
|
{
|
|
245
204
|
byte[] payload;
|
|
@@ -286,7 +245,6 @@ public static class MessageBuilder
|
|
|
286
245
|
|
|
287
246
|
case CheckInMessage cim:
|
|
288
247
|
messageType = 0x04;
|
|
289
|
-
// The Python code does not encrypt CheckInMessage
|
|
290
248
|
payload = BuildCheckInMessage(cim.MessengerId);
|
|
291
249
|
break;
|
|
292
250
|
|
|
@@ -297,26 +255,17 @@ public static class MessageBuilder
|
|
|
297
255
|
return BuildMessage(messageType, payload);
|
|
298
256
|
}
|
|
299
257
|
|
|
300
|
-
/// <summary>
|
|
301
|
-
/// Packs [message_type, total_length, payload] together in big-endian format.
|
|
302
|
-
/// </summary>
|
|
303
258
|
public static byte[] BuildMessage(uint messageType, byte[] payload)
|
|
304
259
|
{
|
|
305
|
-
// total_length = 8 (header) + payload length
|
|
306
260
|
uint messageLength = (uint)(8 + payload.Length);
|
|
307
261
|
|
|
308
|
-
// Build the 8-byte header (all big-endian)
|
|
309
262
|
byte[] header = new byte[8];
|
|
310
|
-
WriteUInt32(header, 0, messageType);
|
|
311
|
-
WriteUInt32(header, 4, messageLength);
|
|
263
|
+
WriteUInt32(header, 0, messageType);
|
|
264
|
+
WriteUInt32(header, 4, messageLength);
|
|
312
265
|
|
|
313
|
-
// Concatenate header + payload
|
|
314
266
|
return Combine(header, payload);
|
|
315
267
|
}
|
|
316
268
|
|
|
317
|
-
/// <summary>
|
|
318
|
-
/// Encodes a string with a 4-byte length prefix (big-endian), plus UTF-8 data.
|
|
319
|
-
/// </summary>
|
|
320
269
|
public static byte[] BuildString(string value)
|
|
321
270
|
{
|
|
322
271
|
byte[] encoded = Encoding.UTF8.GetBytes(value);
|
|
@@ -339,7 +288,6 @@ public static class MessageBuilder
|
|
|
339
288
|
var part1 = BuildString(forwarderClientId);
|
|
340
289
|
var part2 = BuildString(ipAddress);
|
|
341
290
|
|
|
342
|
-
// We'll still write it as a uint on the wire
|
|
343
291
|
byte[] part3 = new byte[4];
|
|
344
292
|
WriteUInt32(part3, 0, (uint)port);
|
|
345
293
|
|
|
@@ -356,7 +304,6 @@ public static class MessageBuilder
|
|
|
356
304
|
var part1 = BuildString(forwarderClientId);
|
|
357
305
|
var part2 = BuildString(bindAddress);
|
|
358
306
|
|
|
359
|
-
// Next 3 fields => 3*4 bytes => 12 bytes
|
|
360
307
|
byte[] part3 = new byte[12];
|
|
361
308
|
WriteUInt32(part3, 0, (uint)bindPort);
|
|
362
309
|
WriteUInt32(part3, 4, (uint)addressType);
|
|
@@ -376,9 +323,6 @@ public static class MessageBuilder
|
|
|
376
323
|
return Combine(part1, part2);
|
|
377
324
|
}
|
|
378
325
|
|
|
379
|
-
// ----------------------------------------------------------------------
|
|
380
|
-
// Helper: Write a uint32 to a byte array in big-endian
|
|
381
|
-
// ----------------------------------------------------------------------
|
|
382
326
|
public static void WriteUInt32(byte[] buffer, int offset, uint value)
|
|
383
327
|
{
|
|
384
328
|
buffer[offset] = (byte)((value >> 24) & 0xFF);
|
|
@@ -387,9 +331,6 @@ public static class MessageBuilder
|
|
|
387
331
|
buffer[offset + 3] = (byte)(value & 0xFF);
|
|
388
332
|
}
|
|
389
333
|
|
|
390
|
-
// ----------------------------------------------------------------------
|
|
391
|
-
// Helper: Concatenate multiple byte arrays
|
|
392
|
-
// ----------------------------------------------------------------------
|
|
393
334
|
public static byte[] Combine(params byte[][] arrays)
|
|
394
335
|
{
|
|
395
336
|
int totalLength = 0;
|