Last active
September 23, 2024 15:11
-
-
Save antonilol/941ce3c44aaf5bee35669f69239b5727 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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