Files
d4rkbot/tools/udp-pruefen.mjs
D4rkst3randClaude Opus 5 228853febd
Deploy / check (push) Canceled after 0s
Deploy / deploy (push) Canceled after 0s
Radio: die zweite Sackgasse von der ersten trennen — und UDP messbar machen
Aus dem Betrieb: alle drei Beitrittsschritte durch, trotzdem "signalling".
Das ist kein Widerspruch. In @discordjs/voice gibt es zwei Wege dorthin:

  - `configureNetworking` steigt aus, wenn der Endpunkt leer ist. Discord
    schickt das bei einem Regionswechsel; kommt keine zweite Meldung, bleibt
    die Verbindung fuer immer stehen.
  - Scheitert der Austausch mit dem Sprachserver, wirft die Bibliothek die
    Verbindung nach "signalling" zurueck und faengt von vorn an. Von aussen
    sieht das genauso aus.

Deshalb schreibt der Adapter jetzt den Endpunkt mit, und die Verbindung
protokolliert ihren Weg. War sie einmal bei "connecting" und ist
zurueckgefallen, ist es der UDP-Austausch — dann steht der Endpunkt in der
Meldung, damit man ihn von Hand pruefen kann.

Dazu tools/udp-pruefen.mjs: im Container aufrufen und es sagt, ob UDP
ueberhaupt rauskommt und ob auch auf hohen Ports. Genau das unterscheidet
"Firewall zu" von "Firewall laesst nur die ueblichen Ports durch" — und
Discord-Sprache braucht die hohen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 12:15:14 +02:00

97 lines
4.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Kommt UDP aus diesem Container heraus?
//
// Discord-Sprache läuft über UDP auf hohen Zielports. Ist das dicht, sitzt
// der Bot sichtbar im Sprachkanal und schweigt — im Log steht dann
// „Der Austausch mit dem Sprachserver scheitert".
//
// Aufruf im Container:
// docker exec -it <container> node tools/udp-pruefen.mjs
//
// Geprüft wird in zwei Stufen, weil das den Unterschied ausmacht:
// 1. UDP auf Port 53 (DNS) — geht überhaupt UDP raus?
// 2. UDP auf einen hohen Port (STUN, 19302) — oder nur die üblichen?
//
// Beide grün und der Bot schweigt trotzdem? Dann liegt es nicht am UDP.
import dgram from 'node:dgram';
/** Ein Paket hinschicken und auf Antwort warten */
function versuch({ host, port, paket, timeoutMs = 4000 }) {
return new Promise((fertig) => {
const sock = dgram.createSocket('udp4');
const start = Date.now();
const schluss = (ergebnis) => {
clearTimeout(timer);
try { sock.close(); } catch { /* schon zu */ }
fertig(ergebnis);
};
const timer = setTimeout(() => schluss({ ok: false, grund: 'keine Antwort' }), timeoutMs);
sock.on('message', () => schluss({ ok: true, ms: Date.now() - start }));
sock.on('error', (e) => schluss({ ok: false, grund: e.message }));
sock.send(paket, port, host, (e) => {
if (e) schluss({ ok: false, grund: `Senden ging nicht: ${e.message}` });
});
});
}
// Eine DNS-Anfrage nach example.com — kürzestmöglich von Hand gebaut
const dnsPaket = Buffer.concat([
Buffer.from([0x12, 0x34, 0x01, 0x00, 0, 1, 0, 0, 0, 0, 0, 0]),
Buffer.from([7]), Buffer.from('example'),
Buffer.from([3]), Buffer.from('com'),
Buffer.from([0, 0, 1, 0, 1]),
]);
// Eine STUN-Bindungsanfrage: 20 Byte Kopf, fester Magic Cookie
const stunPaket = Buffer.concat([
Buffer.from([0x00, 0x01, 0x00, 0x00]),
Buffer.from([0x21, 0x12, 0xa4, 0x42]),
Buffer.from('d4rkbotudpprue'.slice(0, 12)),
]);
const proben = [
{ name: 'UDP raus (DNS)', host: '1.1.1.1', port: 53, paket: dnsPaket },
{ name: 'UDP raus (DNS, zweiter)', host: '8.8.8.8', port: 53, paket: dnsPaket },
{ name: 'UDP auf hohem Port (STUN)', host: 'stun.l.google.com', port: 19302, paket: stunPaket },
{ name: 'UDP auf hohem Port (STUN 2)', host: 'stun1.l.google.com', port: 19302, paket: stunPaket },
];
console.log('Prüfe ausgehendes UDP …\n');
const ergebnisse = [];
for (const p of proben) {
const r = await versuch(p);
ergebnisse.push({ ...p, ...r });
console.log(` ${r.ok ? '✅' : '❌'} ${p.name.padEnd(28)} ${p.host}:${p.port} `
+ (r.ok ? `${r.ms} ms` : r.grund));
}
const niedrig = ergebnisse.filter((r) => r.port < 1024).some((r) => r.ok);
const hoch = ergebnisse.filter((r) => r.port >= 1024).some((r) => r.ok);
console.log('');
if (hoch) {
console.log('UDP kommt auf hohen Ports raus. Wenn das Radio trotzdem schweigt,');
console.log('liegt es nicht an ausgehendem UDP — dann den Endpunkt aus dem Log');
console.log('mit dem nächsten Abschnitt prüfen.');
} else if (niedrig) {
console.log('UDP geht nur auf niedrigen Ports raus. Genau das bricht Discord-Sprache:');
console.log('die Sprachserver antworten auf hohen Ports. In der Firewall ausgehendes');
console.log('UDP zu 5000065535 freigeben.');
} else {
console.log('Es kommt gar kein UDP heraus. Discord-Sprache kann so nicht funktionieren.');
console.log('Ausgehendes UDP in der Firewall des Rootservers freigeben.');
}
// Endpunkt aus dem Log mitgeben: node tools/udp-pruefen.mjs frankfurt1234.discord.media
const ziel = process.argv[2];
if (ziel) {
console.log(`\nZusätzlich: ${ziel} auf Port 50000 …`);
// Discord antwortet auf ein Zufallspaket nicht — hier zählt nur, ob das
// Senden selbst durchgeht und ob eine Fehlermeldung zurückkommt.
const r = await versuch({ host: ziel, port: 50000, paket: Buffer.alloc(74), timeoutMs: 3000 });
console.log(r.ok
? ' Antwort erhalten (ungewöhnlich, aber gut).'
: ` ${r.grund === 'keine Antwort'
? 'keine Antwort — das ist hier normal, ein Fehler waere aussagekräftiger'
: `Fehler: ${r.grund}`}`);
}