hourly update ip address

This commit is contained in:
AJ ONeal 2015-10-05 19:51:58 +00:00
parent 2166090e33
commit 7d17139a4b
7 changed files with 280 additions and 6 deletions

View File

@ -64,5 +64,5 @@ cli.main(function (args, options) {
console.log(JSON.stringify(data, null, ' ')); console.log(JSON.stringify(data, null, ' '));
console.log('Test with'); console.log('Test with');
console.log('dig ' + options.hostname + ' ' + options.type); console.log('dig ' + options.hostname + ' ' + options.type);
}) });
}); });

1
dyndns-token.sample.js Normal file
View File

@ -0,0 +1 @@
module.exports = { token: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" };

View File

@ -11,6 +11,7 @@ module.exports.update = function (opts) {
var options; var options;
var hostname = opts.updater || 'redirect-www.org'; var hostname = opts.updater || 'redirect-www.org';
var port = opts.port || 65443; var port = opts.port || 65443;
var req;
options = { options = {
host: hostname host: hostname
@ -49,7 +50,7 @@ module.exports.update = function (opts) {
options.agent = new https.Agent(options); options.agent = new https.Agent(options);
https.request(options, function(res) { req = https.request(options, function(res) {
var textData = ''; var textData = '';
res.on('error', function (err) { res.on('error', function (err) {
@ -60,8 +61,22 @@ module.exports.update = function (opts) {
// console.log(chunk.toString()); // console.log(chunk.toString());
}); });
res.on('end', function () { res.on('end', function () {
resolve(textData); var err;
try {
resolve(JSON.parse(textData));
} catch(e) {
err = new Error("Unparsable Server Response");
err.code = 'E_INVALID_SERVER_RESPONSE';
err.data = textData;
reject(err);
}
}); });
}).end(JSON.stringify(opts.ddns, null, ' ')); });
req.on('error', function () {
reject(err);
});
req.end(JSON.stringify(opts.ddns, null, ' '));
}); });
}; };

101
lib/ddns-updater.js Normal file
View File

@ -0,0 +1,101 @@
'use strict';
var updateIp = require('../holepunch/helpers/update-ip.js').update;
// TODO XXX use API + storage
var token = require('../dyndns-token.js').token;
/*
* @param {string[]} hostnames - A list of hostnames
* @param {Object[]} addresses - A list of { address: <ip-address>, family: <4|6> }
*/
function update(hostnames, addresses) {
// TODO use not-yet-built API to get and store tokens
// TODO use API to add and remove nameservers
var services = [
// TODO XXX don't disable cacert checking
{ hostname: 'ns1.redirect-www.org', port: 65443, cacert: false }
, { hostname: 'ns2.redirect-www.org', port: 65443, cacert: false }
];
var answers = [];
var promises;
var results = [];
var PromiseA;
hostnames.forEach(function (hostname) {
addresses.forEach(function (address) {
var answer = {
"name": hostname
, "value": address.address
, "type": null
, "priority": null
, "token": token
};
if (4 === address.family) {
answer.type = 'A';
}
else if (6 === address.family) {
answer.type = 'AAAA';
}
else {
console.error('[ERROR] unspported address:');
console.error(address);
return;
}
answers.push(answer);
});
});
promises = services.map(function (service, i) {
return updateIp({
updater: service.hostname
, port: service.port
, cacert: service.cacert
, token: token
, ddns: answers
}).then(function (data) {
results[i] = { service: service, data: data };
return data;
}).error(function (err) {
results[i] = { service: service, error: err };
});
});
PromiseA = require('bluebird').Promise;
return PromiseA.all(promises).then(function () {
return results;
});
}
module.exports.update = function () {
var allMap = {};
var hostnames = require('../redirects.json').reduce(function (all, redirect) {
if (!allMap[redirect.from.hostname]) {
allMap[redirect.from.hostname] = true;
all.push(redirect.from.hostname);
}
if (!all[redirect.to.hostname]) {
allMap[redirect.to.hostname] = true;
all.push(redirect.to.hostname);
}
return all;
}, []);
return require('./ip-checker').getExternalAddresses().then(function (result) {
//console.log(Object.keys(allMap), result);
//console.log(hostnames)
//console.log(result.addresses);
console.log('[IP CHECKER] hostnames.length', hostnames.length);
console.log('[IP CHECKER] result.addresses.length', result.addresses.length);
return update(hostnames, result.addresses);
});
};
if (require.main === module) {
module.exports.update().then(function (results) {
console.log('results');
console.log(results);
});
}

126
lib/ip-checker.js Normal file
View File

@ -0,0 +1,126 @@
"use strict";
var PromiseA = require('bluebird').Promise;
var ifaces = require('os').networkInterfaces();
var dns = PromiseA.promisifyAll(require('dns'));
var https = require('https');
function getExternalAddresses() {
var iftypes = {};
Object.keys(ifaces).forEach(function (ifname) {
ifaces[ifname].forEach(function (iface) {
if (iface.internal) {
return;
}
/*
if (/^(::|f[cde])/.test(iface.address)) {
console.log('non-public ipv6');
return;
}
*/
iftypes[iface.family] = true;
});
});
var now = Date.now();
return PromiseA.all([
dns.lookupAsync('api.ipify.org', { family: 4/*, all: true*/ }).then(function (ans) {
iftypes.IPv4 = { address: ans[0], family: ans[1], time: Date.now() - now };
}).error(function () {
//console.log('no ipv4', Date.now() - now);
iftypes.IPv4 = false;
})
, dns.lookupAsync('api.ipify.org', { family: 6/*, all: true*/ }).then(function (ans) {
iftypes.IPv6 = { address: ans[0], family: ans[1], time: Date.now() - now };
}).error(function () {
//console.log('no ipv6', Date.now() - now);
iftypes.IPv6 = false;
})
]).then(function () {
var requests = [];
if (iftypes.IPv4) {
requests.push(new PromiseA(function (resolve) {
var req = https.request({
method: 'GET'
, hostname: iftypes.IPv4.address
, port: 443
, headers: {
Host: 'api.ipify.org'
}
, path: '/'
//, family: 4
// TODO , localAddress: <<external_ipv4>>
}, function (res) {
var result = '';
res.on('error', function (/*err*/) {
resolve(null);
});
res.on('data', function (chunk) {
result += chunk.toString('utf8');
});
res.on('end', function () {
resolve({ address: result, family: 4/*, wan: result === iftypes.IPv4.localAddress*/, time: iftypes.IPv4.time });
});
});
req.on('error', function () {
resolve(null);
});
req.end();
}));
}
if (iftypes.IPv6) {
requests.push(new PromiseA(function (resolve) {
var req = https.request({
method: 'GET'
, hostname: iftypes.IPv6.address
, port: 443
, headers: {
Host: 'api.ipify.org'
}
, path: '/'
//, family: 6
// TODO , localAddress: <<external_ipv6>>
}, function (res) {
var result = '';
res.on('error', function (/*err*/) {
resolve(null);
});
res.on('data', function (chunk) {
result += chunk.toString('utf8');
});
res.on('end', function () {
resolve({ address: result, family: 6/*, wan: result === iftypes.IPv6.localAaddress*/, time: iftypes.IPv4.time });
});
});
req.on('error', function () {
resolve(null);
});
req.end();
}));
}
return PromiseA.all(requests).then(function (ips) {
ips = ips.filter(function (ip) {
return ip;
});
return {
addresses: ips
, time: Date.now() - now
};
});
});
}
exports.getExternalAddresses = getExternalAddresses;

View File

@ -8,7 +8,9 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
var path = require('path'); var path = require('path');
var dummyCerts; var dummyCerts;
var serveFavicon; var serveFavicon;
var secureContexts = {}; var secureContexts = {};
var loopbackApp;
var loopbackToken = require('crypto').randomBytes(32).toString('hex');
function loadDummyCerts() { function loadDummyCerts() {
if (dummyCerts) { if (dummyCerts) {
@ -294,9 +296,20 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
}; };
} }
function getLoopbackApp() {
return function (req, res) {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.end(JSON.stringify({ "success": true, "token": loopbackToken }));
};
}
function getAppContext(domaininfo) { function getAppContext(domaininfo) {
var localApp; var localApp;
if ('loopback.daplie.invalid' === domaininfo.dirname) {
return getLoopbackApp();
}
try { try {
// TODO live reload required modules // TODO live reload required modules
localApp = require(path.join(vhostsdir, domaininfo.dirname, 'app.js')); localApp = require(path.join(vhostsdir, domaininfo.dirname, 'app.js'));
@ -589,5 +602,23 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
}); });
} }
function updateIps() {
console.log('[UPDATE IP]');
require('./ddns-updater').update().then(function (results) {
results.forEach(function (result) {
if (result.error) {
console.error(result);
} else {
console.log('[SUCCESS]', result.service.hostname);
}
});
}).error(function (err) {
console.error('[UPDATE IP] ERROR');
console.error(err);
});
}
// TODO check the IP every 5 minutes and update it every hour
setInterval(updateIps, 60 * 60 * 1000);
updateIps();
return runServer(); return runServer();
}; };

View File

@ -87,7 +87,7 @@
"negotiator": "^0.5.1", "negotiator": "^0.5.1",
"node-pre-gyp": "^0.6.4", "node-pre-gyp": "^0.6.4",
"node-uuid": "1.x", "node-uuid": "1.x",
"nodemailer": "1.x", "nodemailer": "^1.4.0",
"nodemailer-mailgun-transport": "1.x", "nodemailer-mailgun-transport": "1.x",
"oauth": "0.9.x", "oauth": "0.9.x",
"oauth2orize": "git://github.com/coolaj86/oauth2orize.git#v1.0.1+scope.1", "oauth2orize": "git://github.com/coolaj86/oauth2orize.git#v1.0.1+scope.1",