153 lines
4.2 KiB
JavaScript
153 lines
4.2 KiB
JavaScript
|
/**
|
||
|
* web/webserver.js - functions for serving web content
|
||
|
*
|
||
|
* @author Calvin Montgomery <cyzon@cyzon.us>
|
||
|
*/
|
||
|
|
||
|
var path = require('path');
|
||
|
var net = require('net');
|
||
|
var express = require('express');
|
||
|
var webroot = path.join(__dirname, '..', 'www');
|
||
|
var sendJade = require('./jade').sendJade;
|
||
|
var Server = require('../server');
|
||
|
var $util = require('../utilities');
|
||
|
var Logger = require('../logger');
|
||
|
|
||
|
var httplog = new Logger.Logger(path.join(__dirname, '..', '..', 'http.log'));
|
||
|
|
||
|
var suspiciousPath = (/admin|adm|\.\.|\/etc\/passwd|\\x5c|%5c|0x5c|setup|install|php|pma|blog|sql|scripts|aspx?|database/ig);
|
||
|
/**
|
||
|
* Determines whether a request is suspected of being illegitimate
|
||
|
*/
|
||
|
function isSuspicious(req) {
|
||
|
// ZmEu is a penetration script
|
||
|
if (req.header('user-agent') &&
|
||
|
req.header('user-agent').toLowerCase() === 'zmeu') {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (req.path.match(suspiciousPath)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Extracts an IP address from a request. Uses X-Forwarded-For if the IP is localhost
|
||
|
*/
|
||
|
function ipForRequest(req) {
|
||
|
var ip = req.ip;
|
||
|
if (ip === '127.0.0.1' || ip === '::1') {
|
||
|
var xforward = req.header('x-forwarded-for');
|
||
|
if (typeof xforward !== 'string' || !net.isIP(xforward)) {
|
||
|
return ip;
|
||
|
} else {
|
||
|
return xforward;
|
||
|
}
|
||
|
}
|
||
|
return ip;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Logs an HTTP request
|
||
|
*/
|
||
|
function logRequest(req, status) {
|
||
|
if (status === undefined) {
|
||
|
status = 200;
|
||
|
}
|
||
|
|
||
|
httplog.log([
|
||
|
ipForRequest(req),
|
||
|
req.route.method.toUpperCase(),
|
||
|
req.path,
|
||
|
status,
|
||
|
req.header('user-agent')
|
||
|
].join(' '));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handles a GET request for /r/:channel - serves channel.html
|
||
|
*/
|
||
|
function handleChannel(req, res) {
|
||
|
if (!$util.isValidChannelName(req.params.channel)) {
|
||
|
logRequest(req, 404);
|
||
|
res.status(404);
|
||
|
res.send('Invalid channel name "' + req.params.channel + '"');
|
||
|
return;
|
||
|
}
|
||
|
logRequest(req);
|
||
|
var loginName = false;
|
||
|
if (req.cookies.auth) {
|
||
|
loginName = req.cookies.auth.split(':')[0];
|
||
|
}
|
||
|
var inst = Server.getServer();
|
||
|
var iourl = '';
|
||
|
sendJade(res, 'channel', {
|
||
|
channelName: req.params.channel,
|
||
|
layout: 'hd',
|
||
|
loggedIn: loginName !== false,
|
||
|
loginName: loginName,
|
||
|
/*ioUrl: 'http://' + inst.config.sio.domain + ':' + inst.config.sio.port*/
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handles a request for the index page
|
||
|
*/
|
||
|
function handleIndex(req, res) {
|
||
|
logRequest(req);
|
||
|
var loginName = false;
|
||
|
if (req.cookies.auth) {
|
||
|
loginName = req.cookies.auth.split(':')[0];
|
||
|
}
|
||
|
sendJade(res, 'index', {
|
||
|
loggedIn: loginName !== false,
|
||
|
loginName: loginName
|
||
|
});
|
||
|
}
|
||
|
|
||
|
module.exports = {
|
||
|
/**
|
||
|
* Initializes webserver callbacks
|
||
|
*
|
||
|
* @param app - The express instance to initialize
|
||
|
*/
|
||
|
init: function (app) {
|
||
|
app.use(express.json());
|
||
|
app.use(express.urlencoded());
|
||
|
app.use(express.cookieParser());
|
||
|
/* Order here is important
|
||
|
* Since I placed /r/:channel above *, the function will
|
||
|
* not apply to the /r/:channel route. This prevents
|
||
|
* duplicate logging, since /r/:channel's callback does
|
||
|
* its own logging
|
||
|
*/
|
||
|
app.get('/r/:channel', handleChannel);
|
||
|
app.get('/', handleIndex);
|
||
|
app.all('*', function (req, res, next) {
|
||
|
if (isSuspicious(req)) {
|
||
|
logRequest(req, 403);
|
||
|
res.status(403);
|
||
|
if (req.header('user-agent').toLowerCase() === 'zmeu') {
|
||
|
res.send('This server disallows requests from ZmEu.');
|
||
|
} else {
|
||
|
res.send('The request ' + req.route.method.toUpperCase() + ' ' +
|
||
|
req.path + ' looks pretty fishy to me. Double check that ' +
|
||
|
'you typed it correctly.');
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
logRequest(req);
|
||
|
next();
|
||
|
});
|
||
|
app.use(express.static('www'));
|
||
|
require('./auth').init(app);
|
||
|
require('./account').init(app);
|
||
|
},
|
||
|
|
||
|
logRequest: logRequest,
|
||
|
|
||
|
ipForRequest: ipForRequest
|
||
|
};
|