captive Portal for bobinscope
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

126 lines
3.1 KiB

const express = require('express')
const app = express()
const exec = require('child_process').exec
const requestIp = require('request-ip')
require('colors')
const ByMac = {}
const ByIp = {}
const CFG = require('./config')
app.use(function (req, res, next) {
res.set('Access-Control-Allow-Origin', '*')
res.set('Access-Control-Allow-Methods', '*')
res.set('Access-Control-Allow-Headers', '*')
if (req.method === 'OPTIONS') {
return res.status(200).send()
}
req.clientIp = requestIp.getClientIp(req).split(':').slice(-1).pop()
// req.clientIp = '192.168.8.226'
findMacAddress(req.clientIp, function (pErr, pMac) {
req.clientMac = pMac || null
req.flagHandler = new FlagHandler(req.clientIp, req.clientMac)
try {
req.isIos = req.headers['user-agent'].indexOf('CaptiveNetworkSupport') !== -1 || req.headers['user-agent'].indexOf('iPhone') !== -1
} catch (e) {}
next()
})
})
app.use(function (req, res, next) {
if (CFG.quiet) {
return next()
}
let currentDate = new Date()
let isFlagged = req.flagHandler.getFlag()
if (req.hostname === 'detectportal.firefox.com') return next()
console.log(currentDate.toISOString().grey, '|', req.clientIp, '=>', req.clientMac || 'MacNotFound |', isFlagged ? 'OK'.green : 'ko'.red, '|', req.originalUrl)
console.log(req.hostname, req.headers['user-agent'])
next()
})
// API called to validate an MAC address
app.all('/validate', function (req, res) {
req.flagHandler.setFlag()
if (req.isIos) {
res.status(302).set({ Location: '/ready.html' }).end()
} else {
res.status(200).end()
}
})
// Serve assets
app.use(express.static(CFG.webapp.portal))
// Default callback
app.use(function (req, res, next) {
let flag = req.flagHandler.getFlag()
// If MAC has been flag by one /validate call
if (flag) {
// Case iOS
if (req.isIos) {
res.status(200).send('<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>')
} else {
res.status(204).end()
}
} else {
res.status(302).set({ Location: CFG.redirectTo }).end()
}
})
function findMacAddress (pIp, pCallback) {
exec(CFG.arpCmd, function (pCode, pStdout) {
console.log(pCode, pStdout)
let lines = pStdout.split('\n')
if (!lines.length) {
return pCallback('NotFound')
}
for (let i = 0; i < lines.length; i++) {
let line = lines[i]
if (line.indexOf(pIp) !== -1) {
// console.log(line)
let cols = line.split(' ').filter(function (str) { return !!str })
return pCallback(null, cols[2])
}
}
pCallback('NotFound')
})
}
class FlagHandler {
constructor (pIp, pMac) {
this.ip = pIp
this.mac = pMac
}
setFlag () {
if (!this.mac) {
ByIp[this.ip] = new Date()
} else {
ByMac[this.mac] = new Date()
}
}
getFlag () {
let flag = null
if (!this.mac) {
flag = ByIp[this.ip]
} else {
flag = ByMac[this.mac]
}
return flag || null
}
}
app.listen(CFG.port, function () {
console.log(`[>] PXPortal service running on: ${CFG.port}`)
})