forked from coolaj86/goldilocks.js
		
	
		
			
	
	
		
			123 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			123 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Much of this file was based on the `le-challenge-ddns` library (which we are not using
							 | 
						||
| 
								 | 
							
								// here because it's method of setting records requires things we don't really want).
							 | 
						||
| 
								 | 
							
								module.exports.create = function (deps, conf, utils) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function getReleventSessionId(domain) {
							 | 
						||
| 
								 | 
							
								    var sessId;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    utils.iterateAllModules(function (mod, domainList) {
							 | 
						||
| 
								 | 
							
								      // We return a truthy value in these cases because of the way the iterate function
							 | 
						||
| 
								 | 
							
								      // handles modules grouped by domain. By returning true we are saying these domains
							 | 
						||
| 
								 | 
							
								      // are "handled" and so if there are multiple modules we won't be given the rest.
							 | 
						||
| 
								 | 
							
								      if (sessId) { return true; }
							 | 
						||
| 
								 | 
							
								      if (domainList.indexOf(domain) < 0) { return true; }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // But if the domains are relevant but we don't know how to handle the module we
							 | 
						||
| 
								 | 
							
								      // return false to allow us to look at any other modules that might exist here.
							 | 
						||
| 
								 | 
							
								      if (mod.type !== 'dns@oauth3.org')  { return false; }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      sessId = mod.tokenId || mod.token_id;
							 | 
						||
| 
								 | 
							
								      return true;
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return sessId;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function get(args, domain, challenge, done) {
							 | 
						||
| 
								 | 
							
								    done(new Error("Challenge.get() does not need an implementation for dns-01. (did you mean Challenge.loopback?)"));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  // same as get, but external
							 | 
						||
| 
								 | 
							
								  function loopback(args, domain, challenge, done) {
							 | 
						||
| 
								 | 
							
								    var challengeDomain = (args.test || '') + args.acmeChallengeDns + domain;
							 | 
						||
| 
								 | 
							
								    require('dns').resolveTxt(challengeDomain, done);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var activeChallenges = {};
							 | 
						||
| 
								 | 
							
								  async function removeAsync(args, domain) {
							 | 
						||
| 
								 | 
							
								    var data = activeChallenges[domain];
							 | 
						||
| 
								 | 
							
								    if (!data) {
							 | 
						||
| 
								 | 
							
								      console.warn(new Error('cannot remove DNS challenge for ' + domain + ': already removed'));
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var session = await utils.getSession(data.sessId);
							 | 
						||
| 
								 | 
							
								    var directives = await deps.OAUTH3.discover(session.token.aud);
							 | 
						||
| 
								 | 
							
								    var apiOpts = {
							 | 
						||
| 
								 | 
							
								      api: 'dns.unset'
							 | 
						||
| 
								 | 
							
								    , session: session
							 | 
						||
| 
								 | 
							
								    , type: 'TXT'
							 | 
						||
| 
								 | 
							
								    , value: data.keyAuthDigest
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    await deps.OAUTH3.api(directives.api, Object.assign({}, apiOpts, data.splitDomain));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    delete activeChallenges[domain];
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  async function setAsync(args, domain, challenge, keyAuth) {
							 | 
						||
| 
								 | 
							
								    if (activeChallenges[domain]) {
							 | 
						||
| 
								 | 
							
								      await removeAsync(args, domain, challenge);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var sessId = getReleventSessionId(domain);
							 | 
						||
| 
								 | 
							
								    if (!sessId) {
							 | 
						||
| 
								 | 
							
								      throw new Error('no DDNS module handles the domain ' + domain);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    var session = await utils.getSession(sessId);
							 | 
						||
| 
								 | 
							
								    var directives = await deps.OAUTH3.discover(session.token.aud);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // I'm not sure what role challenge is supposed to play since even in the library
							 | 
						||
| 
								 | 
							
								    // this code is based on it was never used, but check for it anyway because ...
							 | 
						||
| 
								 | 
							
								    if (!challenge || keyAuth) {
							 | 
						||
| 
								 | 
							
								      console.warn(new Error('DDNS challenge missing challenge or keyAuth'));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    var keyAuthDigest = require('crypto').createHash('sha256').update(keyAuth || '').digest('base64')
							 | 
						||
| 
								 | 
							
								      .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var challengeDomain = (args.test || '') + args.acmeChallengeDns + domain;
							 | 
						||
| 
								 | 
							
								    var splitDomain = (await utils.splitDomains(directives.api, [challengeDomain]))[0];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var apiOpts = {
							 | 
						||
| 
								 | 
							
								      api: 'dns.set'
							 | 
						||
| 
								 | 
							
								    , session: session
							 | 
						||
| 
								 | 
							
								    , type: 'TXT'
							 | 
						||
| 
								 | 
							
								    , value: keyAuthDigest
							 | 
						||
| 
								 | 
							
								    , ttl: args.ttl || 0
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    await deps.OAUTH3.api(directives.api, Object.assign({}, apiOpts, splitDomain));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    activeChallenges[domain] = {
							 | 
						||
| 
								 | 
							
								      sessId
							 | 
						||
| 
								 | 
							
								    , keyAuthDigest
							 | 
						||
| 
								 | 
							
								    , splitDomain
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return new Promise(res => setTimeout(res, 1000));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // It might be slightly easier to use arguments and apply, but the library that will use
							 | 
						||
| 
								 | 
							
								  // this function counts the arguments we expect.
							 | 
						||
| 
								 | 
							
								  function set(a, b, c, d, done) {
							 | 
						||
| 
								 | 
							
								    setAsync(a, b, c, d).then(result => done(null, result), done);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  function remove(a, b, c, done) {
							 | 
						||
| 
								 | 
							
								    removeAsync(a, b, c).then(result => done(null, result), done);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function getOptions() {
							 | 
						||
| 
								 | 
							
								    return {
							 | 
						||
| 
								 | 
							
								      oauth3: 'oauth3.org'
							 | 
						||
| 
								 | 
							
								    , debug: conf.debug
							 | 
						||
| 
								 | 
							
								    , acmeChallengeDns: '_acme-challenge.'
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return {
							 | 
						||
| 
								 | 
							
								    getOptions
							 | 
						||
| 
								 | 
							
								  , set
							 | 
						||
| 
								 | 
							
								  , get
							 | 
						||
| 
								 | 
							
								  , remove
							 | 
						||
| 
								 | 
							
								  , loopback
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								};
							 |