Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions apps/dokploy/__test__/notifications/fixtures/socks-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { createConnection, createServer, type Socket } from "node:net";

export const startSocksServer = async (
targetPort: number,
options: {
credentials?: { username: string; password: string };
rejectConnect?: boolean;
hangHandshake?: boolean;
listenHost?: string;
} = {},
) => {
const sockets = new Set<Socket>();
const destinations: { host: string; port: number }[] = [];
const credentials: { username: string; password: string }[] = [];
let connections = 0;
const track = (socket: Socket) => {
sockets.add(socket);
socket.once("close", () => sockets.delete(socket));
socket.on("error", () => socket.destroy());
};
const server = createServer((socket) => {
connections++;
track(socket);
if (options.hangHandshake) {
socket.resume();
return;
}
let buffer = Buffer.alloc(0);
let phase: "greeting" | "auth" | "connect" | "tunnel" = "greeting";
socket.on("data", function receive(chunk: Buffer) {
buffer = Buffer.concat([buffer, chunk]);
if (phase === "greeting") {
if (buffer.length < 2 || buffer.length < 2 + buffer[1]!) return;
const methods = buffer.subarray(2, 2 + buffer[1]!);
const method = options.credentials ? 2 : 0;
if (buffer[0] !== 5 || !methods.includes(method)) {
socket.end(Buffer.from([5, 255]));
return;
}
buffer = buffer.subarray(2 + buffer[1]!);
phase = options.credentials ? "auth" : "connect";
socket.write(Buffer.from([5, method]));
}
if (phase === "auth") {
if (buffer.length < 2) return;
const usernameLength = buffer[1]!;
if (buffer.length < 3 + usernameLength) return;
const passwordLength = buffer[2 + usernameLength]!;
if (buffer.length < 3 + usernameLength + passwordLength) return;
const received = {
username: buffer.subarray(2, 2 + usernameLength).toString(),
password: buffer
.subarray(3 + usernameLength, 3 + usernameLength + passwordLength)
.toString(),
};
credentials.push(received);
if (
received.username !== options.credentials?.username ||
received.password !== options.credentials?.password
) {
socket.end(Buffer.from([1, 1]));
return;
}
buffer = buffer.subarray(3 + usernameLength + passwordLength);
phase = "connect";
socket.write(Buffer.from([1, 0]));
}
if (phase !== "connect" || buffer.length < 5) return;
// Accept only domain-name destinations, so tests also assert remote DNS.
if (buffer[0] !== 5 || buffer[1] !== 1 || buffer[3] !== 3) {
socket.end(Buffer.from([5, 8, 0, 1, 0, 0, 0, 0, 0, 0]));
return;
}
const hostLength = buffer[4]!;
if (buffer.length < 7 + hostLength) return;
destinations.push({
host: buffer.subarray(5, 5 + hostLength).toString(),
port: buffer.readUInt16BE(5 + hostLength),
});
if (options.rejectConnect) {
socket.end(Buffer.from([5, 5, 0, 1, 0, 0, 0, 0, 0, 0]));
return;
}
phase = "tunnel";
socket.removeListener("data", receive);
const remaining = buffer.subarray(7 + hostLength);
const upstream = createConnection(targetPort, "127.0.0.1", () => {
socket.write(Buffer.from([5, 0, 0, 1, 127, 0, 0, 1, 0, 0]));
if (remaining.length) upstream.write(remaining);
socket.pipe(upstream);
upstream.pipe(socket);
});
track(upstream);
socket.once("close", () => upstream.destroy());
upstream.once("close", () => socket.destroy());
});
});
const listenHost = options.listenHost ?? "127.0.0.1";
await new Promise<void>((resolve, reject) => {
server.once("error", reject);
server.listen(0, listenHost, resolve);
});
const address = server.address();
if (!address || typeof address === "string")
throw new Error("Missing address");
return {
url: `socks5://${listenHost.includes(":") ? `[${listenHost}]` : listenHost}:${address.port}`,
destinations,
credentials,
sockets,
get connections() {
return connections;
},
close: async () => {
for (const socket of sockets) socket.destroy();
await new Promise<void>((resolve) => server.close(() => resolve()));
},
};
};
20 changes: 20 additions & 0 deletions apps/dokploy/__test__/notifications/fixtures/telegram-cert.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDNjCCAh6gAwIBAgIUL/V0+cq27gA2SprS+YmQ1J/xF3MwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQYXBpLnRlbGVncmFtLm9yZzAgFw0yNjEwMjExODEyNTRa
GA8yMTI2MDkyNzE4MTI1NFowGzEZMBcGA1UEAwwQYXBpLnRlbGVncmFtLm9yZzCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK6sL4ynFxF+1WAqDF41jK7A
VH+sx1Ht52vlg3AfOUF0vAuIKxnmQfriTnDKRrbxWiPCguNNpNjr+oFy/DmtpG6O
3R4OEC81hEPkoyAavgwy7ap0rKJ53H+RPepPJwmLEWy5MAbhjhz47svahnqxjN+X
55dvK++gOUl4fHGdTdFcuCJZ0+TfX0t5fTyocg3MfaxeMkkqr6N8rO4a93qepUce
jCiz4twLdPyJuUgQ7wzXtKK4JiVP4GYEriN0AHg46KFbL1YDLfBWF228CGApus7C
QQUdNC5qLvhFuhRRj3i7ZI7a1/qrDR6embXqcv64ozd8UVpRyHam7TPiYg0rbX8C
AwEAAaNwMG4wHQYDVR0OBBYEFBkv2ITM6QAllw05SOwq7ItegwMlMB8GA1UdIwQY
MBaAFBkv2ITM6QAllw05SOwq7ItegwMlMA8GA1UdEwEB/wQFMAMBAf8wGwYDVR0R
BBQwEoIQYXBpLnRlbGVncmFtLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEAU0jZqyJK
KnUMN6yXdkk97Iq+oFuOUoUKt7Z2CecQmTRmpkDNo/hTXMGdvJQXLUaJfNw1JBz5
nTGdtOpTXV0Qe5uvk7L34uS0HNPRGDge2AKzAgdzwWlg6VWWHWnfCa8OSaElcLbb
lkxmsFXVCMsTgKH9h4MqH5ib/gcYIC/jcThLSfE5kWYjwFcA+zn5B7lp5wL6iG+S
1oNnHMLbX6Djov9ashqpw4+wsA94SI1cpiO0JS6PsjnGEgcysCpkz+MD7KH/Iswb
mY/Q7g8F9nNhU2TAZoDe5VL0XfjeTx66oSTq9J+Yj1V0AEMSLRnYFDMQQC1jANkO
8PVWAo4c9bYZBw==
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions apps/dokploy/__test__/notifications/fixtures/telegram-key.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCurC+MpxcRftVg
KgxeNYyuwFR/rMdR7edr5YNwHzlBdLwLiCsZ5kH64k5wyka28VojwoLjTaTY6/qB
cvw5raRujt0eDhAvNYRD5KMgGr4MMu2qdKyiedx/kT3qTycJixFsuTAG4Y4c+O7L
2oZ6sYzfl+eXbyvvoDlJeHxxnU3RXLgiWdPk319LeX08qHINzH2sXjJJKq+jfKzu
Gvd6nqVHHowos+LcC3T8iblIEO8M17SiuCYlT+BmBK4jdAB4OOihWy9WAy3wVhdt
vAhgKbrOwkEFHTQuai74RboUUY94u2SO2tf6qw0enpm16nL+uKM3fFFaUch2pu0z
4mINK21/AgMBAAECggEAGMHP6tQYnsKxfzPxKn3Vhu6SAMHzmEcAyNgFb/laS07p
LjXqbKfS9zpnMBuuDSpFLTyvAi+UQKiltKj1sJPOHVtuDLSlPO5iukq9h4SIJkDQ
/cVpXeKpdYKXr3jw3qyRgi2h7dUUqEJCJQOVPEOjugyU5xpQibdVwfZBUSB2EfPM
iaTvgwcd/MUOVA8G09H/JOCXgt9Q7nO/TOSkrTPj8JTDkS6I+Ra8oOrYx2VAFxFV
c/QlAFmsgtu3IvjhyGm+kfXhU1xVhdOiGvvX+HRAqM0N83aL8W3iAs6kvibzReCd
L9frLfDJ6ff4TfPsKwDniAglGxcqxtn8oINIWQclVQKBgQDWdSkGp2YV7GauwSYO
3mfIvLI9OHNhDUefOmw2S9WqvE+6tLN5vmJxwfJSz1SKl2OIpaIK8ldPaz5HCnWi
3xT4KZoGmsbL6N57QsUp3BMph64itS7EJemI/HxAeRdjVMK/q0l9ve7qgPsfAzhW
CEHBcXNrD/YoIrKAX3CfMxdzywKBgQDQghsfGnubFv5meiYrmdNeyFnU5jIdcviz
DHWe8ZhyPGhtxSwOhwQlv4Jd2jp/gTb+InpbXQutqyvEuQ6M0ZUcN9usUH8MkzXa
kvx7x1xJWDqRGoeIAadx/bK7aVMX4lOXGdbzMcMnOsU4DQt8nvDrzqIaBx9XremX
DT97V/L+nQKBgHefan73qQ3Zi30258+rZyClgJZ9gn5ENgsvP2UZ79kIqUBvwxzJ
gYgKsuxWEcYso6x5F2Ybv2/hVh+ySePLo5q5Od0tQG5FOrRujiPHSjkEIbHidm+a
5iUvEO+1m8IgaXEYTioBY98SiwDFfwseF++bJNI3WQsSL5dDpUJn8VD5AoGAFlMn
5j+mpmdi28QOXDK/L/HRtHrlOMP7nughm+AQS4LuR3qKChAowIXtxnC8PLSwF6vH
YArpWjHuoMSTqz4xcfEFI0EHKWiFpp6ATBx/ZBb/RlQJ2OUccZcRUQGo9PC1tIL+
wdE9LPEc9rBm33QBJa5dKt5CW9fyH8s6VdGH4f0CgYAjdNA3bEE+Yh/I01xjMgnT
3grunN6/RHQsJtWdvWmpTsOjtG0b/WRuEYAr12esPoc7FC+qxQM4le7eQcub3ak9
K55IA07apg+jEY4mIh4ftSgybFnuh6+DyF0hZYn/BGkaPgOH3EiyJYDpBoSnnUpz
sR+gwzMo9u4OdoBbum3O8w==
-----END PRIVATE KEY-----
Loading