updated the DDNS and loopback to use async/await
This commit is contained in:
parent
a625ee9db9
commit
cfcc1acb8c
231
lib/ddns.js
231
lib/ddns.js
|
@ -16,145 +16,126 @@ module.exports.create = function (deps, conf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDeviceAddress(addr) {
|
async function getSession() {
|
||||||
return deps.storage.owners.all().then(function (sessions) {
|
var sessions = await deps.storage.owners.all();
|
||||||
return sessions.filter(function (sess) {
|
var session = sessions.filter(function (sess) {
|
||||||
return sess.token.scp.indexOf('dns') >= 0;
|
return sess.token.scp.indexOf('dns') >= 0;
|
||||||
})[0];
|
})[0];
|
||||||
}).then(function (session) {
|
|
||||||
if (!session) {
|
|
||||||
return PromiseA.reject(new Error('no sessions with DNS grants'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The OAUTH3 library stores some things on the root session object that we usually
|
if (!session) {
|
||||||
// just leave inside the token, but we need to pull those out before we use it here
|
throw new Error('no sessions with DNS grants');
|
||||||
session.provider_uri = session.provider_uri || session.token.provider_uri || session.token.iss;
|
}
|
||||||
session.client_uri = session.client_uri || session.token.azp;
|
|
||||||
session.scope = session.scope || session.token.scp;
|
|
||||||
|
|
||||||
return OAUTH3.discover(session.token.aud).then(function (directives) {
|
// The OAUTH3 library stores some things on the root session object that we usually
|
||||||
return request({
|
// just leave inside the token, but we need to pull those out before we use it here
|
||||||
url: directives.api+'/api/com.daplie.domains/acl/devices/' + conf.device.hostname
|
session.provider_uri = session.provider_uri || session.token.provider_uri || session.token.iss;
|
||||||
, method: 'POST'
|
session.client_uri = session.client_uri || session.token.azp;
|
||||||
, headers: {
|
session.scope = session.scope || session.token.scp;
|
||||||
'Authorization': 'Bearer ' + session.refresh_token
|
return session;
|
||||||
, 'Accept': 'application/json; charset=utf-8'
|
|
||||||
}
|
|
||||||
, json: {
|
|
||||||
addresses: [
|
|
||||||
{ value: addr, type: dnsType(addr) }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}).then(function () {
|
|
||||||
return OAUTH3.api(directives.api, {session: session, api: 'dns.list'}).then(function (list) {
|
|
||||||
return list.filter(function (record) {
|
|
||||||
return record.device === conf.device.hostname;
|
|
||||||
}).map(function (record) {
|
|
||||||
var split = record.zone.split('.');
|
|
||||||
return {
|
|
||||||
tld: split.slice(1).join('.')
|
|
||||||
, sld: split[0]
|
|
||||||
, sub: record.host.slice(0, -(record.zone.length + 1))
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}).then(function (domains) {
|
|
||||||
var common = {
|
|
||||||
api: 'devices.detach'
|
|
||||||
, session: session
|
|
||||||
, device: conf.device.hostname
|
|
||||||
};
|
|
||||||
|
|
||||||
return PromiseA.all(domains.map(function (record) {
|
|
||||||
return OAUTH3.api(directives.api, Object.assign({}, common, record));
|
|
||||||
})).then(function () {
|
|
||||||
return domains;
|
|
||||||
});
|
|
||||||
}).then(function (domains) {
|
|
||||||
var common = {
|
|
||||||
api: 'devices.attach'
|
|
||||||
, session: session
|
|
||||||
, device: conf.device.hostname
|
|
||||||
, ip: addr
|
|
||||||
, ttl: 300
|
|
||||||
};
|
|
||||||
|
|
||||||
return PromiseA.all(domains.map(function (record) {
|
|
||||||
return OAUTH3.api(directives.api, Object.assign({}, common, record));
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDeviceAddresses() {
|
async function setDeviceAddress(addr) {
|
||||||
return deps.storage.owners.all().then(function (sessions) {
|
var session = await getSession();
|
||||||
return sessions.filter(function (sess) {
|
var directives = await OAUTH3.discover(session.token.aud);
|
||||||
return sess.token.scp.indexOf('dns') >= 0;
|
|
||||||
})[0];
|
|
||||||
}).then(function (session) {
|
|
||||||
if (!session) {
|
|
||||||
return PromiseA.reject(new Error('no sessions with DNS grants'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return OAUTH3.discover(session.token.aud).then(function (directives) {
|
// Set the address of the device to our public address.
|
||||||
return request({
|
await request({
|
||||||
url: directives.api+'/api/org.oauth3.dns/acl/devices'
|
url: directives.api+'/api/com.daplie.domains/acl/devices/' + conf.device.hostname
|
||||||
, method: 'GET'
|
, method: 'POST'
|
||||||
, headers: {
|
, headers: {
|
||||||
'Authorization': 'Bearer ' + session.refresh_token
|
'Authorization': 'Bearer ' + session.refresh_token
|
||||||
, 'Accept': 'application/json; charset=utf-8'
|
, 'Accept': 'application/json; charset=utf-8'
|
||||||
}
|
}
|
||||||
, json: true
|
, json: {
|
||||||
});
|
addresses: [
|
||||||
}).then(function (result) {
|
{ value: addr, type: dnsType(addr) }
|
||||||
if (!result.body) {
|
]
|
||||||
return PromiseA.reject(new Error('No response body in request for device addresses'));
|
}
|
||||||
}
|
|
||||||
if (result.body.error) {
|
|
||||||
var err = new Error(result.body.error.message);
|
|
||||||
return PromiseA.reject(Object.assign(err, result.body.error));
|
|
||||||
}
|
|
||||||
return result.body.devices.filter(function (dev) {
|
|
||||||
return dev.name === conf.device.hostname;
|
|
||||||
})[0];
|
|
||||||
}).then(function (dev) {
|
|
||||||
return (dev || {}).addresses || [];
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Then update all of the records attached to our hostname, first removing the old records
|
||||||
|
// to remove the reference to the old address, then creating new records for the same domains
|
||||||
|
// using our new address.
|
||||||
|
var allDns = OAUTH3.api(directives.api, {session: session, api: 'dns.list'});
|
||||||
|
var ourDomains = allDns.filter(function (record) {
|
||||||
|
return record.device === conf.device.hostname;
|
||||||
|
}).map(function (record) {
|
||||||
|
var zoneSplit = record.zone.split('.');
|
||||||
|
return {
|
||||||
|
tld: zoneSplit.slice(1).join('.')
|
||||||
|
, sld: zoneSplit[0]
|
||||||
|
, sub: record.host.slice(0, -(record.zone.length + 1))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
var common = {
|
||||||
|
api: 'devices.detach'
|
||||||
|
, session: session
|
||||||
|
, device: conf.device.hostname
|
||||||
|
};
|
||||||
|
await PromiseA.all(ourDomains.map(function (record) {
|
||||||
|
return OAUTH3.api(directives.api, Object.assign({}, common, record));
|
||||||
|
}));
|
||||||
|
|
||||||
|
common = {
|
||||||
|
api: 'devices.attach'
|
||||||
|
, session: session
|
||||||
|
, device: conf.device.hostname
|
||||||
|
, ip: addr
|
||||||
|
, ttl: 300
|
||||||
|
};
|
||||||
|
await PromiseA.all(ourDomains.map(function (record) {
|
||||||
|
return OAUTH3.api(directives.api, Object.assign({}, common, record));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDeviceAddresses() {
|
||||||
|
var session = await getSession();
|
||||||
|
var directives = await OAUTH3.discover(session.token.aud);
|
||||||
|
|
||||||
|
var result = await request({
|
||||||
|
url: directives.api+'/api/org.oauth3.dns/acl/devices'
|
||||||
|
, method: 'GET'
|
||||||
|
, headers: {
|
||||||
|
'Authorization': 'Bearer ' + session.refresh_token
|
||||||
|
, 'Accept': 'application/json; charset=utf-8'
|
||||||
|
}
|
||||||
|
, json: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.body) {
|
||||||
|
throw new Error('No response body in request for device addresses');
|
||||||
|
}
|
||||||
|
if (result.body.error) {
|
||||||
|
throw Object.assign(new Error('error getting device list'), result.body.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dev = result.body.devices.filter(function (dev) {
|
||||||
|
return dev.name === conf.device.hostname;
|
||||||
|
})[0];
|
||||||
|
return (dev || {}).addresses || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
var publicAddress;
|
var publicAddress;
|
||||||
function recheckPubAddr() {
|
async function recheckPubAddr() {
|
||||||
if (!conf.ddns.enabled) {
|
if (!conf.ddns.enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
deps.storage.owners.all().then(function (sessions) {
|
var session = await getSession();
|
||||||
return sessions.filter(function (sess) {
|
var directives = await OAUTH3.discover(session.token.aud);
|
||||||
return sess.token.scp.indexOf('dns') >= 0;
|
var addr = await deps.loopback.checkPublicAddr(directives.api);
|
||||||
})[0];
|
|
||||||
}).then(function (session) {
|
|
||||||
if (!session) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OAUTH3.discover(session.token.aud).then(function (directives) {
|
if (publicAddress === addr) {
|
||||||
return deps.loopback.checkPublicAddr(directives.api);
|
return;
|
||||||
}).then(function (addr) {
|
}
|
||||||
if (publicAddress !== addr) {
|
|
||||||
if (conf.debug) {
|
if (conf.debug) {
|
||||||
console.log('previous public address',publicAddress, 'does not match current public address', addr);
|
console.log('previous public address',publicAddress, 'does not match current public address', addr);
|
||||||
}
|
}
|
||||||
publicAddress = addr;
|
|
||||||
setDeviceAddress(addr);
|
await setDeviceAddress(addr);
|
||||||
}
|
publicAddress = addr;
|
||||||
}, function (err) {
|
|
||||||
if (conf.debug) {
|
|
||||||
console.error('error getting public address', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
recheckPubAddr();
|
recheckPubAddr();
|
||||||
|
|
110
lib/loopback.js
110
lib/loopback.js
|
@ -5,81 +5,81 @@ module.exports.create = function (deps, conf) {
|
||||||
var request = PromiseA.promisify(require('request'));
|
var request = PromiseA.promisify(require('request'));
|
||||||
var pending = {};
|
var pending = {};
|
||||||
|
|
||||||
function checkPublicAddr(host) {
|
async function checkPublicAddr(host) {
|
||||||
return request({
|
var result = await request({
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
, url: host+'/api/org.oauth3.tunnel/checkip'
|
, url: host+'/api/org.oauth3.tunnel/checkip'
|
||||||
, json: true
|
, json: true
|
||||||
}).then(function (result) {
|
|
||||||
if (!result.body) {
|
|
||||||
return PromiseA.reject(new Error('No response body in request for public address'));
|
|
||||||
}
|
|
||||||
if (result.body.error) {
|
|
||||||
var err = new Error(result.body.error.message);
|
|
||||||
return PromiseA.reject(Object.assign(err, result.body.error));
|
|
||||||
}
|
|
||||||
return result.body.address;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!result.body) {
|
||||||
|
throw new Error('No response body in request for public address');
|
||||||
|
}
|
||||||
|
if (result.body.error) {
|
||||||
|
// Note that the error on the body will probably have a message that overwrites the default
|
||||||
|
throw Object.assign(new Error('error in check IP response'), result.body.error);
|
||||||
|
}
|
||||||
|
return result.body.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkSinglePort(host, address, port) {
|
async function checkSinglePort(host, address, port) {
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
var token = crypto.randomBytes(8).toString('hex');
|
var token = crypto.randomBytes(8).toString('hex');
|
||||||
var keyAuth = crypto.randomBytes(32).toString('hex');
|
var keyAuth = crypto.randomBytes(32).toString('hex');
|
||||||
pending[token] = keyAuth;
|
pending[token] = keyAuth;
|
||||||
|
|
||||||
var opts = {
|
var reqObj = {
|
||||||
address: address
|
|
||||||
, port: port
|
|
||||||
, token: token
|
|
||||||
, keyAuthorization: keyAuth
|
|
||||||
, iat: Date.now()
|
|
||||||
};
|
|
||||||
|
|
||||||
return request({
|
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
, url: host+'/api/org.oauth3.tunnel/loopback'
|
, url: host+'/api/org.oauth3.tunnel/loopback'
|
||||||
, json: opts
|
, json: {
|
||||||
})
|
address: address
|
||||||
.then(function (result) {
|
, port: port
|
||||||
delete pending[token];
|
, token: token
|
||||||
if (!result.body) {
|
, keyAuthorization: keyAuth
|
||||||
return PromiseA.reject(new Error('No response body in loopback request for port '+port));
|
, iat: Date.now()
|
||||||
}
|
}
|
||||||
// If the loopback requests don't go to us then there are all kinds of ways it could
|
};
|
||||||
// error, but none of them really provide much extra information so we don't do
|
|
||||||
// anything that will break the PromiseA.all out and mask the other results.
|
var result;
|
||||||
if (conf.debug && result.body.error) {
|
try {
|
||||||
console.log('error on remote side of port '+port+' loopback', result.body.error);
|
result = await request(reqObj);
|
||||||
}
|
} catch (err) {
|
||||||
return !!result.body.success;
|
|
||||||
}, function (err) {
|
|
||||||
delete pending[token];
|
delete pending[token];
|
||||||
throw err;
|
throw err;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
delete pending[token];
|
||||||
|
if (!result.body) {
|
||||||
|
throw new Error('No response body in loopback request for port '+port);
|
||||||
|
}
|
||||||
|
// If the loopback requests don't go to us then there are all kinds of ways it could
|
||||||
|
// error, but none of them really provide much extra information so we don't do
|
||||||
|
// anything that will break the PromiseA.all out and mask the other results.
|
||||||
|
if (conf.debug && result.body.error) {
|
||||||
|
console.log('error on remote side of port '+port+' loopback', result.body.error);
|
||||||
|
}
|
||||||
|
return !!result.body.success;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loopback(provider) {
|
async function loopback(provider) {
|
||||||
return deps.OAUTH3.discover(provider).then(function (directives) {
|
var directives = await deps.OAUTH3.discover(provider);
|
||||||
return checkPublicAddr(directives.api).then(function (address) {
|
var address = await checkPublicAddr(directives.api);
|
||||||
console.log('checking to see if', address, 'gets back to us');
|
console.log('checking to see if', address, 'gets back to us');
|
||||||
var ports = require('./servers').listeners.tcp.list();
|
|
||||||
return PromiseA.all(ports.map(function (port) {
|
|
||||||
return checkSinglePort(directives.api, address, port);
|
|
||||||
}))
|
|
||||||
.then(function (values) {
|
|
||||||
if (conf.debug) {
|
|
||||||
console.log('remaining loopback tokens', pending);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = {error: null, address: address};
|
var ports = require('./servers').listeners.tcp.list();
|
||||||
ports.forEach(function (port, ind) {
|
var values = await PromiseA.all(ports.map(function (port) {
|
||||||
result[port] = values[ind];
|
return checkSinglePort(directives.api, address, port);
|
||||||
});
|
}));
|
||||||
return result;
|
|
||||||
});
|
if (conf.debug) {
|
||||||
});
|
console.log('remaining loopback tokens', pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = {error: null, address: address};
|
||||||
|
ports.forEach(function (port, ind) {
|
||||||
|
result[port] = values[ind];
|
||||||
});
|
});
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
loopback.checkPublicAddr = checkPublicAddr;
|
loopback.checkPublicAddr = checkPublicAddr;
|
||||||
|
|
Loading…
Reference in New Issue