Skip to content

Instantly share code, notes, and snippets.

@antonilol
Last active September 23, 2024 15:11
Show Gist options
  • Save antonilol/941ce3c44aaf5bee35669f69239b5727 to your computer and use it in GitHub Desktop.
Save antonilol/941ce3c44aaf5bee35669f69239b5727 to your computer and use it in GitHub Desktop.
// Copyright (c) 2024 Antoni Spaanderman
//
// wifi_in_de_trein.js
// GitHub Gist link: https://gist.github.com/antonilol/941ce3c44aaf5bee35669f69239b5727
// Version: 1.0.1
const http = require('http');
const DETECTPORTAL = 'http://detectportal.firefox.com/canonical.html';
// utility function to send an HTTP request
// to keep it simple, only method and url
// the callback gets the http request and response objects and a resolve function
// call resolve (with values) to return from the async function
function httpRequest(method, url, cb) {
return new Promise(resolve => {
const req = http.request(url, { method });
req
.on('response', res => cb(req, res, resolve))
.on('error', err => {
console.log('request error, no network?');
console.log(err);
process.exit(1);
})
.end();
});
}
function getLoginUrl() {
return httpRequest('GET', DETECTPORTAL, (req, res, resolve) => {
if (res.statusCode == 200) {
resolve({ needsLogin: false });
} else if (res.statusCode == 302) {
resolve({ needsLogin: true, loginUrl: res.headers.location });
} else {
console.log(`statusCode != 302, but ${req.res.statusCode}`);
process.exit(1);
}
});
}
function getCsrfTokenFromLoginPage(loginUrl) {
const CSRF_TOKEN_SEARCH_STRING = 'csrfToken" value="';
return httpRequest('GET', loginUrl, (req, res, resolve) => {
if (res.statusCode != 200) {
console.log(`login page returned status code ${res.statusCode}`);
process.exit(1);
}
const len = res.headers['content-length'];
let data = '';
let dataLen = 0;
let foundSearchString = false;
let foundCsrfToken = false;
if (len != 0) {
const numLen = Math.floor(Math.log10(len)) + 1;
console.log(`receiving login page 0.0% (${' '.repeat(numLen - 1)}0/${len} bytes)`);
res.on('data', chunk => {
data += chunk;
dataLen += chunk.length;
console.log(
`receiving login page ${(dataLen * 100 / len).toFixed(1).padStart(5)}% ` +
`(${dataLen.toString().padStart(numLen)}/${len} bytes)`
);
if (!foundSearchString) {
while (data.length >= CSRF_TOKEN_SEARCH_STRING.length) {
if (data.startsWith(CSRF_TOKEN_SEARCH_STRING)) {
foundSearchString = true;
data = data.slice(CSRF_TOKEN_SEARCH_STRING.length);
break;
}
data = data.slice(1);
}
}
if (foundSearchString) {
const tokenEnd = data.indexOf('"');
if (tokenEnd != -1) {
const csrfToken = data.slice(0, tokenEnd);
foundCsrfToken = true;
if (dataLen != len) {
console.log('CSRF token already found, no need to finish the request');
}
if (tokenEnd != 32) {
console.log(`Warning: unusual CSRF token length: ${tokenEnd}, expected 32`);
}
req.abort();
resolve(csrfToken);
}
}
});
}
res.on('end', () => {
if (!foundCsrfToken) {
throw new Error("csrfToken not found");
}
});
});
}
function login(csrfToken) {
return httpRequest(
'POST',
`http://portal.nstrein.ns.nl/nstrein:main/internet?csrfToken=${csrfToken}`,
(req, res, resolve) => {
let data = '';
res.on('data', chunk => {
data += chunk;
});
res.on('end', () => {
resolve();
});
},
);
}
main();
async function main() {
console.log('requesting login url from detectportal...');
const { loginUrl, needsLogin } = await getLoginUrl();
if (!needsLogin) {
console.log('no login needed');
process.exit(0);
}
if (!loginUrl.startsWith('http://portal.nstrein.ns.nl/')) {
console.log('unknown network, login manually');
process.exit(1);
}
console.log('getting login page...');
// get CSRF token, here a 16 byte hex encoded string (32 hex chars)
const csrfToken = await getCsrfTokenFromLoginPage(loginUrl);
for (let i = 0; i < 5; i++) {
console.log('logging in...');
await login(csrfToken);
console.log('checking login status...');
if ((await getLoginUrl()).needsLogin) {
console.log('login failed');
} else {
console.log('login successful');
process.exit(0);
}
}
console.log('giving up');
process.exit(1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment