2017-06-17 17:12:15 +00:00
|
|
|
import https from 'https';
|
|
|
|
import path from 'path';
|
|
|
|
import fs from 'fs';
|
|
|
|
import Promise from 'bluebird';
|
2017-04-05 06:02:31 +00:00
|
|
|
|
2017-06-17 17:12:15 +00:00
|
|
|
Promise.promisifyAll(fs);
|
2013-11-04 22:04:24 +00:00
|
|
|
|
2017-07-09 03:11:54 +00:00
|
|
|
const LOGGER = require('@calzoneman/jsli')('torlist');
|
2017-06-17 17:12:15 +00:00
|
|
|
const TOR_EXIT_LIST_URL = 'https://check.torproject.org/exit-addresses';
|
|
|
|
const TOR_EXIT_LIST_FILE = path.join(__dirname, '..', 'tor-exit-list.json');
|
|
|
|
const ONE_DAY = 24 * 3600 * 1000;
|
|
|
|
const TOR_EXIT_IPS = new Set();
|
2013-11-04 22:04:24 +00:00
|
|
|
|
2017-06-17 17:12:15 +00:00
|
|
|
function loadTorList() {
|
|
|
|
return fs.statAsync(TOR_EXIT_LIST_FILE).then(stats => {
|
|
|
|
if (new Date() - stats.mtime > ONE_DAY) {
|
|
|
|
LOGGER.info('Tor exit node list is older than 24h, re-downloading from %s',
|
|
|
|
TOR_EXIT_LIST_URL);
|
|
|
|
return loadTorListFromWebsite();
|
|
|
|
} else {
|
|
|
|
return loadTorListFromFile();
|
|
|
|
}
|
|
|
|
}).catch(error => {
|
|
|
|
if (error.code === 'ENOENT') {
|
|
|
|
LOGGER.info('File %s not found, downloading from %s',
|
|
|
|
TOR_EXIT_LIST_FILE,
|
|
|
|
TOR_EXIT_LIST_URL);
|
|
|
|
return loadTorListFromWebsite();
|
|
|
|
} else {
|
|
|
|
throw error;
|
2013-11-04 22:04:24 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-06-17 17:12:15 +00:00
|
|
|
function loadTorListFromWebsite() {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
https.get(TOR_EXIT_LIST_URL, res => {
|
|
|
|
if (res.statusCode !== 200) {
|
|
|
|
reject(new Error(`${res.statusCode} ${res.statusMessage}`));
|
2013-11-04 22:04:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-06-17 17:12:15 +00:00
|
|
|
let buffer = '';
|
|
|
|
res.on('data', data => buffer += data);
|
|
|
|
res.on('end', () => {
|
|
|
|
const exitNodes = buffer.split('\n').filter(line => {
|
|
|
|
return /^ExitAddress/.test(line);
|
|
|
|
}).map(line => {
|
|
|
|
return line.split(' ')[1];
|
|
|
|
});
|
|
|
|
|
|
|
|
fs.writeFileAsync(TOR_EXIT_LIST_FILE, JSON.stringify(exitNodes))
|
|
|
|
.then(() => {
|
|
|
|
LOGGER.info('Saved %s', TOR_EXIT_LIST_FILE);
|
|
|
|
}).catch(error => {
|
|
|
|
LOGGER.error('Unable to save %s: %s',
|
|
|
|
TOR_EXIT_LIST_FILE,
|
|
|
|
error.message);
|
|
|
|
});
|
|
|
|
resolve(exitNodes);
|
|
|
|
});
|
|
|
|
}).on('error', error => {
|
|
|
|
reject(error);
|
2018-04-07 22:30:30 +00:00
|
|
|
});
|
2013-11-04 22:04:24 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-06-17 17:12:15 +00:00
|
|
|
function loadTorListFromFile() {
|
|
|
|
LOGGER.info('Loading Tor exit list from %s', TOR_EXIT_LIST_FILE);
|
|
|
|
return fs.readFileAsync(TOR_EXIT_LIST_FILE).then(contents => {
|
|
|
|
return JSON.parse(String(contents));
|
|
|
|
});
|
|
|
|
}
|
2013-11-04 22:04:24 +00:00
|
|
|
|
2017-06-17 17:12:15 +00:00
|
|
|
loadTorList().then(exits => {
|
|
|
|
TOR_EXIT_IPS.clear();
|
|
|
|
exits.forEach(exit => {
|
|
|
|
TOR_EXIT_IPS.add(exit);
|
|
|
|
});
|
|
|
|
}).catch(error => {
|
|
|
|
LOGGER.error('Unable to load Tor exit list: %s', error.stack);
|
2014-08-15 02:42:13 +00:00
|
|
|
});
|
2013-11-04 22:04:24 +00:00
|
|
|
|
2017-06-17 17:12:15 +00:00
|
|
|
export function isTorExit(ip) {
|
|
|
|
return TOR_EXIT_IPS.has(ip);
|
2018-04-07 22:30:30 +00:00
|
|
|
}
|