mirror of
https://codeberg.org/doesnm/snailtrix.git
synced 2025-10-18 19:00:59 +00:00
173 lines
4.8 KiB
JavaScript
173 lines
4.8 KiB
JavaScript
|
|
const express = require("express");
|
||
|
|
const ed = require("@noble/ed25519");
|
||
|
|
const { sha512 } = require("@noble/hashes/sha2.js");
|
||
|
|
ed.hashes.sha512 = sha512;
|
||
|
|
const config = require("./config.json");
|
||
|
|
const fs = require("node:fs");
|
||
|
|
const { parse: parseUrl } = require("node:url");
|
||
|
|
const dns = require("node:dns");
|
||
|
|
const fetch = require("sync-fetch");
|
||
|
|
const app = express();
|
||
|
|
const deasync = require("deasync");
|
||
|
|
const keysToBase64 = (keys) => {
|
||
|
|
return {
|
||
|
|
secretKey: new Buffer(keys.secretKey)
|
||
|
|
.toString("base64")
|
||
|
|
.replace("+", "-")
|
||
|
|
.replace("/", "_")
|
||
|
|
.replace(/=+$/, ""),
|
||
|
|
publicKey: new Buffer(keys.publicKey)
|
||
|
|
.toString("base64")
|
||
|
|
.replace("+", "-")
|
||
|
|
.replace("/", "_")
|
||
|
|
.replace(/=+$/, ""),
|
||
|
|
};
|
||
|
|
};
|
||
|
|
const base64ToKeys = (keys) => {
|
||
|
|
const fixBase64 = (str) =>
|
||
|
|
str
|
||
|
|
.replace("-", "+")
|
||
|
|
.replace("_", "/")
|
||
|
|
.padEnd(str.length + ((4 - (str.length % 4)) % 4), "=");
|
||
|
|
return {
|
||
|
|
secretKey: new Uint8Array(Buffer.from(fixBase64(keys.secretKey), "base64")),
|
||
|
|
publicKey: new Uint8Array(Buffer.from(fixBase64(keys.publicKey), "base64")),
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
if (!config.secretKey) {
|
||
|
|
const keys = ed.keygen();
|
||
|
|
config.secretKey = keys.secretKey;
|
||
|
|
config.publicKey = keys.publicKey;
|
||
|
|
fs.writeFileSync(
|
||
|
|
"config.json",
|
||
|
|
JSON.stringify({ ...config, ...keysToBase64(config) }),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const delegation = (name) => {
|
||
|
|
const res = fetch(`https://${name}/.well-known/matrix/server`);
|
||
|
|
if (res.status === 200) {
|
||
|
|
const json = res.json();
|
||
|
|
if (!json["m.server"].includes(":")) {
|
||
|
|
return delegation(json["m.server"]);
|
||
|
|
}
|
||
|
|
return { domain: json["m.server"], host: json["m.server"] };
|
||
|
|
} else {
|
||
|
|
const resolve = deasync(dns.resolveSrv);
|
||
|
|
try {
|
||
|
|
const res = resolve(`_matrix-fed._tcp.${name}`)[0];
|
||
|
|
return {
|
||
|
|
domain: `${res.name}:${res.port}`,
|
||
|
|
host: name || res.name,
|
||
|
|
};
|
||
|
|
} catch (_e) {
|
||
|
|
try {
|
||
|
|
const res = resolve(`_matrix._tcp.${name}`)[0];
|
||
|
|
return { domain: `${res.name}:${res.port}`, host: name || res.name };
|
||
|
|
} catch (_e) {
|
||
|
|
return {
|
||
|
|
domain: `${name}:8448`,
|
||
|
|
host: name || res.name,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const makeSignedRequest = (origin, method, url) => {
|
||
|
|
const parsed = parseUrl(url);
|
||
|
|
let payload = {
|
||
|
|
method: method,
|
||
|
|
uri: parsed.path,
|
||
|
|
destination: parsed.hostname,
|
||
|
|
origin: origin,
|
||
|
|
};
|
||
|
|
payload = Object.fromEntries(
|
||
|
|
Object.entries(payload).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)),
|
||
|
|
);
|
||
|
|
const delegate = delegation(parsed.hostname);
|
||
|
|
const keys = base64ToKeys(config);
|
||
|
|
const signature = ed.sign(
|
||
|
|
new TextEncoder().encode(JSON.stringify(payload)),
|
||
|
|
keys.secretKey,
|
||
|
|
);
|
||
|
|
console.log(
|
||
|
|
`X-Matrix origin="${origin}",destination="${parsed.hostname}",key="ed25519:${config.publicKey.slice(0, 5)}",sig="${new Buffer(signature).toString("base64").replace("+", "-").replace("/", "_").replace(/=+$/, "")}"`,
|
||
|
|
);
|
||
|
|
return fetch(
|
||
|
|
`https://${delegate.domain}${parsed.path}`,
|
||
|
|
{
|
||
|
|
headers: {
|
||
|
|
Host: delegate.host,
|
||
|
|
Authorization: `X-Matrix origin="${origin}",destination="${parsed.hostname}",key="ed25519:${config.publicKey.slice(0, 5)}",sig="${new Buffer(signature).toString("base64").replace("+", "-").replace("/", "_").replace(/=+$/, "")}"`,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
app.get("/.well-known/matrix/server", (req, res) => {
|
||
|
|
return res.json({
|
||
|
|
"m.server": req.host.includes(":") ? req.host : `${req.host}:443`,
|
||
|
|
});
|
||
|
|
});
|
||
|
|
app.get("/_matrix/federation/v1/version", (_req, res) => {
|
||
|
|
res.json({
|
||
|
|
server: {
|
||
|
|
name: "idk",
|
||
|
|
version: "0.0.0.0.0.0.0.1",
|
||
|
|
},
|
||
|
|
});
|
||
|
|
});
|
||
|
|
app.get("/_matrix/key/v2/server", (req, res) => {
|
||
|
|
const payload = {
|
||
|
|
old_verify_keys: {},
|
||
|
|
server_name: req.host.replace(":443", ""),
|
||
|
|
valid_until_ts: Number.MAX_SAFE_INTEGER,
|
||
|
|
verify_keys: {},
|
||
|
|
};
|
||
|
|
payload.verify_keys[`ed25519:${config.publicKey.slice(0, 5)}`] = {
|
||
|
|
key: config.publicKey,
|
||
|
|
};
|
||
|
|
const keys = base64ToKeys(config);
|
||
|
|
const signature = ed.sign(
|
||
|
|
new TextEncoder().encode(
|
||
|
|
JSON.stringify(
|
||
|
|
Object.fromEntries(
|
||
|
|
Object.entries(payload).sort(([keyA], [keyB]) =>
|
||
|
|
keyA.localeCompare(keyB),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
keys.secretKey,
|
||
|
|
);
|
||
|
|
payload.signatures = {};
|
||
|
|
payload.signatures[req.host.replace(":443", "")] = {};
|
||
|
|
payload.signatures[req.host.replace(":443", "")][
|
||
|
|
`ed25519:${config.publicKey.slice(0, 5)}`
|
||
|
|
] = new Buffer(signature)
|
||
|
|
.toString("base64")
|
||
|
|
.replace("+", "-")
|
||
|
|
.replace("/", "_")
|
||
|
|
.replace(/=+$/, "");
|
||
|
|
res.json(payload);
|
||
|
|
});
|
||
|
|
app.get("/_test/version", (req, res) => {
|
||
|
|
const result = delegation(req.query.domain);
|
||
|
|
console.log(result);
|
||
|
|
res.json(
|
||
|
|
fetch(`https://${result.domain}/_matrix/federation/v1/version`, {
|
||
|
|
headers: { Host: result.host },
|
||
|
|
}).json(),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
app.get("/_test/alias", (req, res) => {
|
||
|
|
res.json(
|
||
|
|
makeSignedRequest(
|
||
|
|
req.host.replace(":443", ""),
|
||
|
|
"GET",
|
||
|
|
"https://yjsryd6gyhgl5eyikakf2xe5o4.srv.us/_matrix/federation/v1/query/directory?room_alias=%23ok%3Aorehus.club",
|
||
|
|
).json(),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
app.listen(3000, () => console.log("Listening at 3000"));
|