messenger-proxy 0.4.1__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.
- builder/__init__.py +0 -0
- builder/clients/__init__.py +0 -0
- builder/clients/csharp/MessengerClient/App.config +14 -0
- builder/clients/csharp/MessengerClient/Crypto.cs +85 -0
- builder/clients/csharp/MessengerClient/HTTPMessengerClient.cs +173 -0
- builder/clients/csharp/MessengerClient/Message.cs +410 -0
- builder/clients/csharp/MessengerClient/MessengerClient.cs +188 -0
- builder/clients/csharp/MessengerClient/MessengerClient.csproj +62 -0
- builder/clients/csharp/MessengerClient/MessengerClient.sln +25 -0
- builder/clients/csharp/MessengerClient/Program.cs +171 -0
- builder/clients/csharp/MessengerClient/Properties/AssemblyInfo.cs +36 -0
- builder/clients/csharp/MessengerClient/RemotePortForwarder.cs +86 -0
- builder/clients/csharp/MessengerClient/WebsocketMessengerClient.cs +218 -0
- builder/clients/csharp/README.md +41 -0
- builder/clients/nodejs/README.md +127 -0
- builder/clients/nodejs/builder.py +60 -0
- builder/clients/nodejs/electron +39 -0
- builder/clients/nodejs/requirements.txt +1 -0
- builder/clients/nodejs/templates/messenger-client.js +751 -0
- builder/clients/nodejs/webpack.conf.js +50 -0
- builder/clients/python/README.md +143 -0
- builder/clients/python/builder.py +70 -0
- builder/clients/python/messenger-client-http.py +746 -0
- builder/clients/python/messenger-client-ws.py +478 -0
- builder/clients/python/messenger-client.py +959 -0
- builder/clients/python/requirements.txt +1 -0
- builder/clients/python/templates/messenger-client.py +896 -0
- messenger/__init__.py +10 -0
- messenger/aes.py +20 -0
- messenger/convert.py +21 -0
- messenger/engine.py +76 -0
- messenger/forwarder_clients.py +186 -0
- messenger/forwarders.py +331 -0
- messenger/generator.py +60 -0
- messenger/http_ws_server.py +134 -0
- messenger/manager.py +737 -0
- messenger/message.py +267 -0
- messenger/messengers.py +226 -0
- messenger/output.py +31 -0
- messenger/resources/top_ports.txt +8320 -0
- messenger/scanner.py +179 -0
- messenger/text.py +35 -0
- messenger_proxy-0.4.1.data/scripts/messenger-builder +111 -0
- messenger_proxy-0.4.1.data/scripts/messenger-cli +30 -0
- messenger_proxy-0.4.1.dist-info/METADATA +109 -0
- messenger_proxy-0.4.1.dist-info/RECORD +49 -0
- messenger_proxy-0.4.1.dist-info/WHEEL +5 -0
- messenger_proxy-0.4.1.dist-info/licenses/LICENSE +28 -0
- messenger_proxy-0.4.1.dist-info/top_level.txt +2 -0
builder/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<configuration>
|
|
3
|
+
<startup>
|
|
4
|
+
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
|
5
|
+
</startup>
|
|
6
|
+
<runtime>
|
|
7
|
+
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
8
|
+
<dependentAssembly>
|
|
9
|
+
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
|
10
|
+
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
|
11
|
+
</dependentAssembly>
|
|
12
|
+
</assemblyBinding>
|
|
13
|
+
</runtime>
|
|
14
|
+
</configuration>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Linq;
|
|
3
|
+
using System.Text;
|
|
4
|
+
using System.Security.Cryptography;
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
namespace MessengerClient
|
|
8
|
+
{
|
|
9
|
+
internal class Crypto
|
|
10
|
+
{
|
|
11
|
+
private const int AesBlockByteSize = 128 / 8;
|
|
12
|
+
private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create();
|
|
13
|
+
|
|
14
|
+
public static byte[] Hash(string encryption_key)
|
|
15
|
+
{
|
|
16
|
+
using (SHA256 sha256 = SHA256.Create())
|
|
17
|
+
{
|
|
18
|
+
return sha256.ComputeHash(Encoding.ASCII.GetBytes(encryption_key));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public static byte[] Encrypt(byte[] key, ArraySegment<byte> plainText)
|
|
23
|
+
{
|
|
24
|
+
byte[] plainTextBytes = plainText.Array;
|
|
25
|
+
return Encrypt(key, plainTextBytes);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public static byte[] Encrypt(byte[] key, string plainText)
|
|
29
|
+
{
|
|
30
|
+
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
|
|
31
|
+
return Encrypt(key, plainTextBytes);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public static byte[] Encrypt(byte[] key, byte[] plainText)
|
|
35
|
+
{
|
|
36
|
+
using (var aes = Aes.Create())
|
|
37
|
+
{
|
|
38
|
+
|
|
39
|
+
byte[] iv = GenerateRandomBytes(AesBlockByteSize);
|
|
40
|
+
|
|
41
|
+
using (var encryptor = aes.CreateEncryptor(key, iv))
|
|
42
|
+
{
|
|
43
|
+
var cipherText = encryptor
|
|
44
|
+
.TransformFinalBlock(plainText, 0, plainText.Length);
|
|
45
|
+
|
|
46
|
+
var result = MergeArrays(iv, cipherText);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public static byte[] Decrypt(byte[] key, byte[] encryptedData)
|
|
53
|
+
{
|
|
54
|
+
using (var aes = Aes.Create())
|
|
55
|
+
{
|
|
56
|
+
var iv = encryptedData.Take(AesBlockByteSize).ToArray();
|
|
57
|
+
var cipherText = encryptedData.Skip(AesBlockByteSize).ToArray();
|
|
58
|
+
|
|
59
|
+
using (var encryptor = aes.CreateDecryptor(key, iv))
|
|
60
|
+
{
|
|
61
|
+
return encryptor.TransformFinalBlock(cipherText, 0, cipherText.Length);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private static byte[] GenerateRandomBytes(int numberOfBytes)
|
|
67
|
+
{
|
|
68
|
+
var randomBytes = new byte[numberOfBytes];
|
|
69
|
+
Random.GetBytes(randomBytes);
|
|
70
|
+
return randomBytes;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private static byte[] MergeArrays(params byte[][] arrays)
|
|
74
|
+
{
|
|
75
|
+
var merged = new byte[arrays.Sum(a => a.Length)];
|
|
76
|
+
var mergeIndex = 0;
|
|
77
|
+
for (int i = 0; i < arrays.GetLength(0); i++)
|
|
78
|
+
{
|
|
79
|
+
arrays[i].CopyTo(merged, mergeIndex);
|
|
80
|
+
mergeIndex += arrays[i].Length;
|
|
81
|
+
}
|
|
82
|
+
return merged;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
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 ConcurrentQueue<object> _downstreamMessages;
|
|
16
|
+
private string _messengerId;
|
|
17
|
+
|
|
18
|
+
public HTTPMessengerClient(string uri, byte[] encryptionKey, IWebProxy proxy = null)
|
|
19
|
+
{
|
|
20
|
+
_uri = uri;
|
|
21
|
+
_encryptionKey = encryptionKey;
|
|
22
|
+
|
|
23
|
+
var handler = new HttpClientHandler();
|
|
24
|
+
if (proxy != null)
|
|
25
|
+
{
|
|
26
|
+
handler.Proxy = proxy;
|
|
27
|
+
handler.UseProxy = true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_httpClient = new HttpClient(handler);
|
|
31
|
+
_downstreamMessages = new ConcurrentQueue<object>();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public override async Task ConnectAsync()
|
|
35
|
+
{
|
|
36
|
+
int retryDelaySeconds = 15;
|
|
37
|
+
int maxTotalWaitSeconds = 1800;
|
|
38
|
+
int elapsedSeconds = 0;
|
|
39
|
+
|
|
40
|
+
while (elapsedSeconds < maxTotalWaitSeconds)
|
|
41
|
+
{
|
|
42
|
+
try
|
|
43
|
+
{
|
|
44
|
+
Console.WriteLine($"Connecting to HTTP server at {_uri}");
|
|
45
|
+
|
|
46
|
+
var downstreamMessage = MessageBuilder.SerializeMessage(_encryptionKey, new CheckInMessage(""));
|
|
47
|
+
HttpContent content = new ByteArrayContent(downstreamMessage);
|
|
48
|
+
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
|
|
49
|
+
|
|
50
|
+
var response = await _httpClient.PostAsync(_uri, content);
|
|
51
|
+
response.EnsureSuccessStatusCode();
|
|
52
|
+
|
|
53
|
+
byte[] responseBytes = await response.Content.ReadAsByteArrayAsync();
|
|
54
|
+
|
|
55
|
+
var (_, parsedMessage) = MessageParser.DeserializeMessage(_encryptionKey, responseBytes);
|
|
56
|
+
|
|
57
|
+
if (parsedMessage is CheckInMessage checkInMsg)
|
|
58
|
+
{
|
|
59
|
+
_messengerId = checkInMsg.MessengerId;
|
|
60
|
+
Console.WriteLine($"[+] Connected to server with Messenger ID: {_messengerId}");
|
|
61
|
+
|
|
62
|
+
await PollServerAsync(); // Enter main loop
|
|
63
|
+
return; // Exit reconnect loop on success
|
|
64
|
+
}
|
|
65
|
+
else
|
|
66
|
+
{
|
|
67
|
+
throw new InvalidOperationException(
|
|
68
|
+
$"Expected a CheckInMessage, but got {parsedMessage.GetType().Name}"
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (Exception ex)
|
|
73
|
+
{
|
|
74
|
+
Console.WriteLine($"[!] Connection attempt failed: {ex.Message}");
|
|
75
|
+
elapsedSeconds += retryDelaySeconds;
|
|
76
|
+
if (elapsedSeconds >= maxTotalWaitSeconds)
|
|
77
|
+
{
|
|
78
|
+
Console.WriteLine("[-] Reconnect timeout after 30 minutes. Giving up.");
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Console.WriteLine("[*] Retrying connection in 15 seconds...");
|
|
83
|
+
await Task.Delay(retryDelaySeconds * 1000);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
private async Task PollServerAsync()
|
|
90
|
+
{
|
|
91
|
+
while (true)
|
|
92
|
+
{
|
|
93
|
+
try
|
|
94
|
+
{
|
|
95
|
+
var downstreamMessages = new List<object>();
|
|
96
|
+
|
|
97
|
+
CheckInMessage checkInMessage = new CheckInMessage(_messengerId);
|
|
98
|
+
downstreamMessages.Add(checkInMessage);
|
|
99
|
+
|
|
100
|
+
while (_downstreamMessages.TryDequeue(out var message))
|
|
101
|
+
{
|
|
102
|
+
downstreamMessages.Add(message);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
HttpContent content = new ByteArrayContent(SerializeMessages(_encryptionKey, downstreamMessages));
|
|
106
|
+
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
|
|
107
|
+
var response = await _httpClient.PostAsync(_uri, content);
|
|
108
|
+
|
|
109
|
+
if (!response.IsSuccessStatusCode)
|
|
110
|
+
{
|
|
111
|
+
Console.WriteLine($"Failed to poll server. HTTP {response.StatusCode}");
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
var responseData = await response.Content.ReadAsByteArrayAsync();
|
|
116
|
+
var messages = DeserializeMessages(_encryptionKey, responseData);
|
|
117
|
+
|
|
118
|
+
foreach (var message in messages)
|
|
119
|
+
{
|
|
120
|
+
_ = Task.Run(() => HandleMessageAsync(message));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
await Task.Delay(1000);
|
|
124
|
+
}
|
|
125
|
+
catch (Exception ex)
|
|
126
|
+
{
|
|
127
|
+
Console.WriteLine($"Error polling server: {ex.Message}");
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public override async Task SendDownstreamMessageAsync(object message)
|
|
134
|
+
{
|
|
135
|
+
try
|
|
136
|
+
{
|
|
137
|
+
_downstreamMessages.Enqueue(message);
|
|
138
|
+
}
|
|
139
|
+
catch (Exception ex)
|
|
140
|
+
{
|
|
141
|
+
Console.WriteLine($"Error enqueuing downstream message: {ex.Message}");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public override async Task HandleMessageAsync(object message)
|
|
146
|
+
{
|
|
147
|
+
switch (message)
|
|
148
|
+
{
|
|
149
|
+
case InitiateForwarderClientReq reqMessage:
|
|
150
|
+
await HandleInitiateForwarderClientReqAsync(reqMessage);
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
case InitiateForwarderClientRep repMessage:
|
|
154
|
+
await StreamAsync(repMessage.ForwarderClientId);
|
|
155
|
+
break;
|
|
156
|
+
|
|
157
|
+
case SendDataMessage sendDataMessage:
|
|
158
|
+
if (ForwarderClients.TryGetValue(sendDataMessage.ForwarderClientId, out var client))
|
|
159
|
+
{
|
|
160
|
+
await client.GetStream().WriteAsync(sendDataMessage.Data, 0, sendDataMessage.Data.Length);
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
|
|
164
|
+
case CheckInMessage checkInMessage:
|
|
165
|
+
break;
|
|
166
|
+
|
|
167
|
+
default:
|
|
168
|
+
Console.WriteLine("Unknown message type received");
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Linq;
|
|
3
|
+
using System.Text;
|
|
4
|
+
using System.Security.Cryptography;
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
// ----------------------------------------------------------------------
|
|
8
|
+
// 1. Message Classes (now using int instead of uint)
|
|
9
|
+
// ----------------------------------------------------------------------
|
|
10
|
+
public class CheckInMessage
|
|
11
|
+
{
|
|
12
|
+
public string MessengerId { get; }
|
|
13
|
+
public CheckInMessage(string messengerId)
|
|
14
|
+
{
|
|
15
|
+
MessengerId = messengerId;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public class InitiateForwarderClientReq
|
|
20
|
+
{
|
|
21
|
+
public string ForwarderClientId { get; }
|
|
22
|
+
public string IpAddress { get; }
|
|
23
|
+
public int Port { get; }
|
|
24
|
+
|
|
25
|
+
public InitiateForwarderClientReq(string forwarderClientId, string ipAddress, int port)
|
|
26
|
+
{
|
|
27
|
+
ForwarderClientId = forwarderClientId;
|
|
28
|
+
IpAddress = ipAddress;
|
|
29
|
+
Port = port;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public class InitiateForwarderClientRep
|
|
34
|
+
{
|
|
35
|
+
public string ForwarderClientId { get; }
|
|
36
|
+
public string BindAddress { get; }
|
|
37
|
+
public int BindPort { get; }
|
|
38
|
+
public int AddressType { get; }
|
|
39
|
+
public int Reason { get; }
|
|
40
|
+
|
|
41
|
+
public InitiateForwarderClientRep(
|
|
42
|
+
string forwarderClientId,
|
|
43
|
+
string bindAddress,
|
|
44
|
+
int bindPort,
|
|
45
|
+
int addressType,
|
|
46
|
+
int reason)
|
|
47
|
+
{
|
|
48
|
+
ForwarderClientId = forwarderClientId;
|
|
49
|
+
BindAddress = bindAddress;
|
|
50
|
+
BindPort = bindPort;
|
|
51
|
+
AddressType = addressType;
|
|
52
|
+
Reason = reason;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public class SendDataMessage
|
|
57
|
+
{
|
|
58
|
+
public string ForwarderClientId { get; }
|
|
59
|
+
public byte[] Data { get; }
|
|
60
|
+
|
|
61
|
+
public SendDataMessage(string forwarderClientId, byte[] data)
|
|
62
|
+
{
|
|
63
|
+
ForwarderClientId = forwarderClientId;
|
|
64
|
+
Data = data;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
// ----------------------------------------------------------------------
|
|
70
|
+
// 2. MessageParser: Reading/Decrypting Bytes
|
|
71
|
+
// ----------------------------------------------------------------------
|
|
72
|
+
public static class MessageParser
|
|
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
|
+
public static (uint Value, byte[] Remainder) ReadUInt32(byte[] data)
|
|
79
|
+
{
|
|
80
|
+
if (data.Length < 4)
|
|
81
|
+
throw new ArgumentException("Not enough bytes to read a 32-bit value.");
|
|
82
|
+
|
|
83
|
+
// Big-endian decode into a uint
|
|
84
|
+
uint value = (uint)((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
|
|
85
|
+
byte[] remainder = data.Skip(4).ToArray();
|
|
86
|
+
|
|
87
|
+
return (value, remainder);
|
|
88
|
+
}
|
|
89
|
+
|
|
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
|
+
public static (string Value, byte[] Remainder) ReadString(byte[] data)
|
|
98
|
+
{
|
|
99
|
+
var (length, remainder) = ReadUInt32(data);
|
|
100
|
+
if (remainder.Length < length)
|
|
101
|
+
throw new ArgumentException($"Not enough bytes to read string of length {length}.");
|
|
102
|
+
|
|
103
|
+
string s = Encoding.UTF8.GetString(remainder, 0, (int)length);
|
|
104
|
+
byte[] leftover = remainder.Skip((int)length).ToArray();
|
|
105
|
+
|
|
106
|
+
return (s, leftover);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// <summary>
|
|
110
|
+
/// Parses the payload of a 0x04 'CheckIn' message (no decryption).
|
|
111
|
+
/// </summary>
|
|
112
|
+
public static CheckInMessage ParseCheckIn(byte[] value)
|
|
113
|
+
{
|
|
114
|
+
var (messengerId, _) = ReadString(value);
|
|
115
|
+
return new CheckInMessage(messengerId);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/// <summary>
|
|
119
|
+
/// Parses the payload of a 0x01 'InitiateForwarderClientReq' message.
|
|
120
|
+
/// </summary>
|
|
121
|
+
public static InitiateForwarderClientReq ParseInitiateForwarderClientReq(byte[] value)
|
|
122
|
+
{
|
|
123
|
+
var (forwarderClientId, remainder) = ReadString(value);
|
|
124
|
+
var (ipAddress, remainder2) = ReadString(remainder);
|
|
125
|
+
var (port, remainder3) = ReadUInt32(remainder2);
|
|
126
|
+
|
|
127
|
+
// cast uint -> int
|
|
128
|
+
return new InitiateForwarderClientReq(
|
|
129
|
+
forwarderClientId,
|
|
130
|
+
ipAddress,
|
|
131
|
+
(int)port
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/// <summary>
|
|
136
|
+
/// Parses the payload of a 0x02 'InitiateForwarderClientRep' message.
|
|
137
|
+
/// </summary>
|
|
138
|
+
public static InitiateForwarderClientRep ParseInitiateForwarderClientRep(byte[] value)
|
|
139
|
+
{
|
|
140
|
+
var (forwarderClientId, remainder) = ReadString(value);
|
|
141
|
+
var (bindAddress, remainder2) = ReadString(remainder);
|
|
142
|
+
var (bindPort, remainder3) = ReadUInt32(remainder2);
|
|
143
|
+
var (addressType, remainder4) = ReadUInt32(remainder3);
|
|
144
|
+
var (reason, remainder5) = ReadUInt32(remainder4);
|
|
145
|
+
|
|
146
|
+
// cast all uint -> int
|
|
147
|
+
return new InitiateForwarderClientRep(
|
|
148
|
+
forwarderClientId,
|
|
149
|
+
bindAddress,
|
|
150
|
+
(int)bindPort,
|
|
151
|
+
(int)addressType,
|
|
152
|
+
(int)reason
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/// <summary>
|
|
157
|
+
/// Parses the payload of a 0x03 'SendDataMessage'.
|
|
158
|
+
/// </summary>
|
|
159
|
+
public static SendDataMessage ParseSendData(byte[] value)
|
|
160
|
+
{
|
|
161
|
+
var (forwarderClientId, remainder) = ReadString(value);
|
|
162
|
+
var (encodedData, remainder2) = ReadString(remainder);
|
|
163
|
+
|
|
164
|
+
byte[] rawData = Convert.FromBase64String(encodedData);
|
|
165
|
+
return new SendDataMessage(
|
|
166
|
+
forwarderClientId,
|
|
167
|
+
rawData
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
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
|
+
public static (byte[] leftover, object parsedMessage) DeserializeMessage(
|
|
180
|
+
byte[] encryptionKey,
|
|
181
|
+
byte[] rawData)
|
|
182
|
+
{
|
|
183
|
+
// 1) Read the message type
|
|
184
|
+
var (messageType, dataAfterType) = ReadUInt32(rawData);
|
|
185
|
+
|
|
186
|
+
// 2) Read the total message length
|
|
187
|
+
var (messageLength, dataAfterLength) = ReadUInt32(dataAfterType);
|
|
188
|
+
|
|
189
|
+
// 3) Extract the (messageLength - 8) payload bytes
|
|
190
|
+
int payloadLen = (int)messageLength - 8;
|
|
191
|
+
if (dataAfterLength.Length < payloadLen)
|
|
192
|
+
throw new ArgumentException("Not enough bytes in data for the payload.");
|
|
193
|
+
|
|
194
|
+
byte[] payload = dataAfterLength.Take(payloadLen).ToArray();
|
|
195
|
+
byte[] leftover = dataAfterLength.Skip(payloadLen).ToArray();
|
|
196
|
+
|
|
197
|
+
// 4) Decrypt or parse plaintext, depending on message type
|
|
198
|
+
object parsedMsg;
|
|
199
|
+
switch (messageType)
|
|
200
|
+
{
|
|
201
|
+
case 0x01:
|
|
202
|
+
{
|
|
203
|
+
byte[] decrypted = MessengerClient.Crypto.Decrypt(encryptionKey, payload);
|
|
204
|
+
parsedMsg = ParseInitiateForwarderClientReq(decrypted);
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case 0x02:
|
|
208
|
+
{
|
|
209
|
+
byte[] decrypted = MessengerClient.Crypto.Decrypt(encryptionKey, payload);
|
|
210
|
+
parsedMsg = ParseInitiateForwarderClientRep(decrypted);
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
case 0x03:
|
|
214
|
+
{
|
|
215
|
+
byte[] decrypted = MessengerClient.Crypto.Decrypt(encryptionKey, payload);
|
|
216
|
+
parsedMsg = ParseSendData(decrypted);
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case 0x04:
|
|
220
|
+
{
|
|
221
|
+
// According to the Python version, we do NOT decrypt for 0x04
|
|
222
|
+
parsedMsg = ParseCheckIn(payload);
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
default:
|
|
226
|
+
throw new ArgumentException($"Unknown message type: 0x{messageType:X}");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return (leftover, parsedMsg);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
// ----------------------------------------------------------------------
|
|
235
|
+
// 3. MessageBuilder: Creating/Encrypting Bytes
|
|
236
|
+
// ----------------------------------------------------------------------
|
|
237
|
+
public static class MessageBuilder
|
|
238
|
+
{
|
|
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
|
+
public static byte[] SerializeMessage(byte[] encryptionKey, object msg)
|
|
244
|
+
{
|
|
245
|
+
byte[] payload;
|
|
246
|
+
uint messageType;
|
|
247
|
+
|
|
248
|
+
switch (msg)
|
|
249
|
+
{
|
|
250
|
+
case InitiateForwarderClientReq req:
|
|
251
|
+
messageType = 0x01;
|
|
252
|
+
payload = MessengerClient.Crypto.Encrypt(
|
|
253
|
+
encryptionKey,
|
|
254
|
+
BuildInitiateForwarderClientReq(
|
|
255
|
+
req.ForwarderClientId,
|
|
256
|
+
req.IpAddress,
|
|
257
|
+
req.Port
|
|
258
|
+
)
|
|
259
|
+
);
|
|
260
|
+
break;
|
|
261
|
+
|
|
262
|
+
case InitiateForwarderClientRep rep:
|
|
263
|
+
messageType = 0x02;
|
|
264
|
+
payload = MessengerClient.Crypto.Encrypt(
|
|
265
|
+
encryptionKey,
|
|
266
|
+
BuildInitiateForwarderClientRep(
|
|
267
|
+
rep.ForwarderClientId,
|
|
268
|
+
rep.BindAddress,
|
|
269
|
+
rep.BindPort,
|
|
270
|
+
rep.AddressType,
|
|
271
|
+
rep.Reason
|
|
272
|
+
)
|
|
273
|
+
);
|
|
274
|
+
break;
|
|
275
|
+
|
|
276
|
+
case SendDataMessage sdm:
|
|
277
|
+
messageType = 0x03;
|
|
278
|
+
payload = MessengerClient.Crypto.Encrypt(
|
|
279
|
+
encryptionKey,
|
|
280
|
+
BuildSendData(
|
|
281
|
+
sdm.ForwarderClientId,
|
|
282
|
+
sdm.Data
|
|
283
|
+
)
|
|
284
|
+
);
|
|
285
|
+
break;
|
|
286
|
+
|
|
287
|
+
case CheckInMessage cim:
|
|
288
|
+
messageType = 0x04;
|
|
289
|
+
// The Python code does not encrypt CheckInMessage
|
|
290
|
+
payload = BuildCheckInMessage(cim.MessengerId);
|
|
291
|
+
break;
|
|
292
|
+
|
|
293
|
+
default:
|
|
294
|
+
throw new ArgumentException($"Unknown message type: {msg.GetType().Name}");
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return BuildMessage(messageType, payload);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/// <summary>
|
|
301
|
+
/// Packs [message_type, total_length, payload] together in big-endian format.
|
|
302
|
+
/// </summary>
|
|
303
|
+
public static byte[] BuildMessage(uint messageType, byte[] payload)
|
|
304
|
+
{
|
|
305
|
+
// total_length = 8 (header) + payload length
|
|
306
|
+
uint messageLength = (uint)(8 + payload.Length);
|
|
307
|
+
|
|
308
|
+
// Build the 8-byte header (all big-endian)
|
|
309
|
+
byte[] header = new byte[8];
|
|
310
|
+
WriteUInt32(header, 0, messageType); // message_type
|
|
311
|
+
WriteUInt32(header, 4, messageLength); // total_length
|
|
312
|
+
|
|
313
|
+
// Concatenate header + payload
|
|
314
|
+
return Combine(header, payload);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/// <summary>
|
|
318
|
+
/// Encodes a string with a 4-byte length prefix (big-endian), plus UTF-8 data.
|
|
319
|
+
/// </summary>
|
|
320
|
+
public static byte[] BuildString(string value)
|
|
321
|
+
{
|
|
322
|
+
byte[] encoded = Encoding.UTF8.GetBytes(value);
|
|
323
|
+
byte[] lengthBytes = new byte[4];
|
|
324
|
+
WriteUInt32(lengthBytes, 0, (uint)encoded.Length);
|
|
325
|
+
|
|
326
|
+
return Combine(lengthBytes, encoded);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
public static byte[] BuildCheckInMessage(string messengerId)
|
|
330
|
+
{
|
|
331
|
+
return BuildString(messengerId);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
public static byte[] BuildInitiateForwarderClientReq(
|
|
335
|
+
string forwarderClientId,
|
|
336
|
+
string ipAddress,
|
|
337
|
+
int port)
|
|
338
|
+
{
|
|
339
|
+
var part1 = BuildString(forwarderClientId);
|
|
340
|
+
var part2 = BuildString(ipAddress);
|
|
341
|
+
|
|
342
|
+
// We'll still write it as a uint on the wire
|
|
343
|
+
byte[] part3 = new byte[4];
|
|
344
|
+
WriteUInt32(part3, 0, (uint)port);
|
|
345
|
+
|
|
346
|
+
return Combine(part1, part2, part3);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
public static byte[] BuildInitiateForwarderClientRep(
|
|
350
|
+
string forwarderClientId,
|
|
351
|
+
string bindAddress,
|
|
352
|
+
int bindPort,
|
|
353
|
+
int addressType,
|
|
354
|
+
int reason)
|
|
355
|
+
{
|
|
356
|
+
var part1 = BuildString(forwarderClientId);
|
|
357
|
+
var part2 = BuildString(bindAddress);
|
|
358
|
+
|
|
359
|
+
// Next 3 fields => 3*4 bytes => 12 bytes
|
|
360
|
+
byte[] part3 = new byte[12];
|
|
361
|
+
WriteUInt32(part3, 0, (uint)bindPort);
|
|
362
|
+
WriteUInt32(part3, 4, (uint)addressType);
|
|
363
|
+
WriteUInt32(part3, 8, (uint)reason);
|
|
364
|
+
|
|
365
|
+
return Combine(part1, part2, part3);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
public static byte[] BuildSendData(
|
|
369
|
+
string forwarderClientId,
|
|
370
|
+
byte[] data)
|
|
371
|
+
{
|
|
372
|
+
var part1 = BuildString(forwarderClientId);
|
|
373
|
+
string encodedData = Convert.ToBase64String(data);
|
|
374
|
+
var part2 = BuildString(encodedData);
|
|
375
|
+
|
|
376
|
+
return Combine(part1, part2);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ----------------------------------------------------------------------
|
|
380
|
+
// Helper: Write a uint32 to a byte array in big-endian
|
|
381
|
+
// ----------------------------------------------------------------------
|
|
382
|
+
public static void WriteUInt32(byte[] buffer, int offset, uint value)
|
|
383
|
+
{
|
|
384
|
+
buffer[offset] = (byte)((value >> 24) & 0xFF);
|
|
385
|
+
buffer[offset + 1] = (byte)((value >> 16) & 0xFF);
|
|
386
|
+
buffer[offset + 2] = (byte)((value >> 8) & 0xFF);
|
|
387
|
+
buffer[offset + 3] = (byte)(value & 0xFF);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// ----------------------------------------------------------------------
|
|
391
|
+
// Helper: Concatenate multiple byte arrays
|
|
392
|
+
// ----------------------------------------------------------------------
|
|
393
|
+
public static byte[] Combine(params byte[][] arrays)
|
|
394
|
+
{
|
|
395
|
+
int totalLength = 0;
|
|
396
|
+
foreach (var arr in arrays)
|
|
397
|
+
totalLength += arr.Length;
|
|
398
|
+
|
|
399
|
+
byte[] result = new byte[totalLength];
|
|
400
|
+
int offset = 0;
|
|
401
|
+
|
|
402
|
+
foreach (var arr in arrays)
|
|
403
|
+
{
|
|
404
|
+
Buffer.BlockCopy(arr, 0, result, offset, arr.Length);
|
|
405
|
+
offset += arr.Length;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return result;
|
|
409
|
+
}
|
|
410
|
+
}
|