const express = require('express'); const app = express(); const bodyParser = require('body-parser'); // const config = require('./config'); const log4js = require('log4js'); const logger = log4js.getLogger(); const urlparser = require('url'); const URL = require('url').URL; const http = require('http'); const https = require('https'); const apicache = require('apicache'); const request = require('request'); const ipfilter = require('express-ipfilter').IpFilter; const IpDeniedError = require('express-ipfilter').IpDeniedError; const port = process.env.PORT || 8080; logger.level = 'debug'; // app.use(compression()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ 'extended': true })); apicache.options({ 'debug': true }); const cache = apicache.middleware; // app.use(cache('15 minutes')); const ips = ['212.71.255.44', '82.35.75.161', '54.146.189.113', '52.90.194.191', '52.71.167.171']; ips.push('82.46.161.156'); // flat // 54.146.189.113, 52.90.194.191, 52.71.167.171 is for ifttt const complexUrls = new Map(); app.use(ipfilter(ips, { 'mode': 'allow' })); app.use((err, req, res, _next) => { const thisErr = Object.assign({}, err); thisErr.originalUrl = req.originalUrl; console.error('Error handler', thisErr); if(thisErr instanceof IpDeniedError) res.status(401); else res.status(thisErr.status || 500); res.status(403).end(); }); const bouncer = ['phpmyadmin', 'phpMyadmin', 'phpMyAdmin', 'phpmyAdmin', 'phpmyadmin2', 'phpmyadmin3', 'phpmyadmin4', '2phpmyadmin', 'phpmy', 'phppma', 'myadmin', 'shopdb', 'MyAdmin', 'program', 'PMA', 'dbadmin', 'pma', 'db', 'admin', 'mysql', 'database', 'sqlmanager', 'mysqlmanager', 'php-myadmin', 'phpmy-admin', 'mysqladmin', 'mysql-admin', 'phpMyAdmin2', 'phpMyAdmin3', 'phpMyAdmin4', 'phpMyAdmin-3', 'php-my-admin', 'PMA2011', 'PMA2012', 'PMA2013', 'PMA2014', 'PMA2015', 'PMA2016', 'PMA2017', 'PMA2018', 'pma2011', 'pma2012', 'pma2013', 'pma2014', 'pma2015', 'pma2016', 'pma2017', 'pma2018', 'phpmyadmin2011', 'phpmyadmin2012', 'phpmyadmin2013', 'phpmyadmin2014', 'phpmyadmin2015', 'phpmyadmin2016', 'phpmyadmin2017', 'phpmyadmin2018', 'phpmanager']; const httpStart = RegExp('(http|ftp|https):\\/\\/'); function getUrl (req, res) { let final; const firstUrl = req.params.encoded_id; // let theUrl; logger.debug('Want', firstUrl); const theUrl = complexUrls.get(firstUrl) || firstUrl; if (theUrl === undefined || bouncer.indexOf(theUrl) !== -1 || theUrl === '') { logger.warn(`You're not getting in ${theUrl}`); res.status(400).send(''); return; } if (theUrl !== firstUrl) logger.info(`>>> Using ${theUrl} instead of ${firstUrl}`); const options = urlparser.parse(theUrl); options.followAllRedirects = true; options.headers = { 'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36' }; // console.log('Options', options); logger.info(`>> getting ${theUrl}`); function urlQuery (callback) { try { let count = 0; final = options.href; if (options.protocol === 'https:') https.request(options, responseHandler).end(); else http.request(options, responseHandler).end(); function responseHandler(response) { response.setEncoding('utf8'); if (response.statusCode === 302 || response.statusCode === 301) { body = []; logger.debug('>> follow', response.headers.location, count); let rUrl; if (!httpStart.test(response.headers.location)) { // logger.warn('response.headers.location is not a valid url...'); logger.info(`Trying to fix for ${theUrl}`); logger.warn(response.headers.location); logger.debug(response.headers); const tempUrl = new URL(theUrl); // logger.debug('theUrl:', theUrl); // logger.debug('tempurl:', tempUrl); rUrl = new URL(response.headers.location, tempUrl.origin); logger.debug(rUrl.href); } else rUrl = urlparser.parse(response.headers.location); rUrl.followAllRedirects = true; rUrl.headers = { 'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36' }; count++; final = rUrl.href; if (rUrl.protocol === 'https:') https.request(rUrl, responseHandler).end(); else http.request(rUrl, responseHandler).end(); } let data = ''; response.on('data', chunk => { data += chunk; }); response.on('end', () => { if (response.statusCode !== 302 && response.statusCode !== 301) callback(data); }); response.on('error', e => { logger.error(e); }); } } catch (e) { logger.error(e); } } urlQuery(a => { // logger.info(a); logger.info(`Got result for ${theUrl}`); logger.info(`Actually used: ${final}`); if (theUrl !== final) complexUrls.set(theUrl, final); // res.setHeader('Content-Type', 'application/json'); res.send(a); }); } function getUrlV2(req, res) { let final; const firstUrl = req.params.encoded_id; // let theUrl; logger.debug('Want', firstUrl); const theUrl = complexUrls.get(firstUrl) || firstUrl; if (theUrl === undefined || bouncer.indexOf(theUrl) !== -1 || theUrl === '') { logger.warn(`You're not getting in ${theUrl}`); res.status(400).send(''); return; } if (theUrl !== firstUrl) logger.info(`>>> Using ${theUrl} instead of ${firstUrl}`); const options = urlparser.parse(theUrl); options.followAllRedirects = true; options.headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36' }; // console.log('Options', options); logger.info(`V2 >> getting ${theUrl}`); request({ 'url' : theUrl, 'proxy' : 'http://us-wa.proxymesh.com:31280', 'tunnel' : true }, (err, _res, body) => { console.log(err); console.log(body); if (!err) res.send(body); else res.send(err); }); } app.get('/:encoded_id', cache('15 minutes'), getUrlV2); process.on('uncaughtException', function (err) { console.error(err); }); const server = app.listen(port, () => { logger.info(`Server listening on port ${port}`); });