Skip to content

Instantly share code, notes, and snippets.

@InvoxiPlayGames
Created October 9, 2021 06:30
Show Gist options
  • Save InvoxiPlayGames/9d7eb710b3865d37285402f770e2a193 to your computer and use it in GitHub Desktop.
Save InvoxiPlayGames/9d7eb710b3865d37285402f770e2a193 to your computer and use it in GitHub Desktop.
STUN IP request thing in NodeJS (bad)
var dgram = require("dgram");
var udp4 = dgram.createSocket("udp4");
var udp6 = dgram.createSocket("udp6");
var stunServer = "stun.l.google.com";
var stunPort = 19302;
function makeSTUNRequest() {
var buffer = Buffer.alloc(20);
buffer.writeUInt16BE(0x0001, 0); // STUN request
buffer.writeUInt16BE(0x0000, 2); // message size (0 bytes)
buffer.writeUInt32BE(0x2112a442, 4); // magic cookie (always 0x2112a442)
for (var i = 8; i < 20; i++) {
var randInt = Math.floor(Math.random() * 255);
buffer.writeUInt8(randInt, i); // write transaction ID
}
return buffer;
}
function parseSTUNResponse(buf, rinfo) {
if (buf.length < 20) {
console.log(`Bad response (packet length ${buf.length}}): ${buf.toString('hex')}`);
return;
}
// parse the STUN header
var messageType = buf.readUInt16BE(0);
var messageSize = buf.readUInt16BE(2);
var magicCookie = buf.readUInt32BE(4);
var transID = Buffer.alloc(12);
for (var i = 8; i < 20; i++) transID[i-8] = buf.readUInt8(i);
// read the STUN message
if (messageSize <= 0) {
console.log(`Bad response (no message): ${buf.toString('hex')}`);
return;
}
var message = Buffer.alloc(messageSize);
for (var i = 0; i < messageSize; i++) message[i] = buf.readUInt8(20+i);
// parse the STUN message
for (var i = 0; i < messageSize;) {
// read attribute header
var attribute = message.readUInt16BE(i + 0);
var attributeLength = message.readUInt16BE(i + 2);
i += 4;
if (attribute == 0x0001) { // MAPPED-ADDRESS
var reserved = message.readUInt8(i + 0);
var addressFamily = message.readUInt8(i + 1);
if (addressFamily == 1) { // IPv4
var port = message.readUInt16BE(i + 2);
var ip1 = message.readUInt8(i + 4);
var ip2 = message.readUInt8(i + 5);
var ip3 = message.readUInt8(i + 6);
var ip4 = message.readUInt8(i + 7);
console.log(`MAPPED-ADDRESS IPv4: ${ip1}.${ip2}.${ip3}.${ip4}:${port}`);
} else if (addressFamily == 2) { // IPv6
var port = message.readUInt16BE(i + 2);
var ip = "";
// TODO: there's got to be a cleaner way to do this...
ip += message.readUInt16BE(i + 4).toString(16) + ":";
ip += message.readUInt16BE(i + 6).toString(16) + ":";
ip += message.readUInt16BE(i + 8).toString(16) + ":";
ip += message.readUInt16BE(i + 10).toString(16) + ":";
ip += message.readUInt16BE(i + 12).toString(16) + ":";
ip += message.readUInt16BE(i + 14).toString(16) + ":";
ip += message.readUInt16BE(i + 16).toString(16) + ":";
ip += message.readUInt16BE(i + 18).toString(16);
console.log(`MAPPED-ADDRESS IPv6: [${ip}]:${port}`);
} else {
console.log(`Unknown MAPPED-ADDRESS family ${addressFamily}: ${message.slice(i+2, i+attributeLength).toString('hex')}`);
}
} else if (attribute == 0x0020) { // XOR-MAPPED-ADDRESS
var reserved = message.readUInt8(i + 0);
var addressFamily = message.readUInt8(i + 1);
if (addressFamily == 1) { // IPv4
var port = message.readUInt16BE(i + 2) ^ 0x2112;
var ip1 = message.readUInt8(i + 4) ^ 0x21;
var ip2 = message.readUInt8(i + 5) ^ 0x12;
var ip3 = message.readUInt8(i + 6) ^ 0xa4;
var ip4 = message.readUInt8(i + 7) ^ 0x42;
console.log(`XOR-MAPPED-ADDRESS IPv4: ${ip1}.${ip2}.${ip3}.${ip4}:${port}`);
} else if (addressFamily == 2) { // IPv6
var port = message.readUInt16BE(i + 2) ^ 0x2112;
var ip = "";
// TODO: there's got to be a cleaner way to do this...
ip += (message.readUInt16BE(i + 4) ^ 0x2112).toString(16) + ":";
ip += (message.readUInt16BE(i + 6) ^ 0xa442).toString(16) + ":";
ip += (message.readUInt16BE(i + 8) ^ transID.readUInt16BE(0)).toString(16) + ":";
ip += (message.readUInt16BE(i + 10) ^ transID.readUInt16BE(2)).toString(16) + ":";
ip += (message.readUInt16BE(i + 12) ^ transID.readUInt16BE(4)).toString(16) + ":";
ip += (message.readUInt16BE(i + 14) ^ transID.readUInt16BE(6)).toString(16) + ":";
ip += (message.readUInt16BE(i + 16) ^ transID.readUInt16BE(8)).toString(16) + ":";
ip += (message.readUInt16BE(i + 18) ^ transID.readUInt16BE(10)).toString(16);
console.log(`XOR-MAPPED-ADDRESS IPv6: [${ip}]:${port}`);
} else {
console.log(`Unknown XOR-MAPPED-ADDRESS family ${addressFamily}: ${message.slice(i+2, i+attributeLength).toString('hex')}`);
}
} else {
console.log(`Unknown attribute ${attribute.toString(16)} (length ${attributeLength}): ${message.slice(i, i+attributeLength).toString('hex')}`);
}
i += attributeLength;
}
}
udp4.on("message", parseSTUNResponse);
udp6.on("message", parseSTUNResponse);
console.log("Sending STUN request to server...");
udp4.send(makeSTUNRequest(), 0, 20, stunPort, stunServer);
udp6.send(makeSTUNRequest(), 0, 20, stunPort, stunServer);
setTimeout(process.exit, 2000)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment