import opts from 'config' import Notifier from '@uci-utils/notify' import Route53 from '@uci-utils/route53' import pIp from 'public-ip' import { lookup as clookup } from 'dns' import { promisify } from 'util' const lookup = promisify(clookup) import { CronJob } from 'cron' import to from 'await-to-js' import { info } from 'console' const rname = process.argv[2] || opts.domain if (!rname) { console.log('exiting, must supply (sub)domain record name to check') console.log(JSON.stringify(opts, null, 3)) process.exit() } const verbose = (opts.verbose ||'').length const interval = (opts.lookup || {}).interval || 10 // seconds const timeout = (opts.lookup || {}).timeout || 5 // minutes if (verbose) console.log(JSON.stringify(opts, null, 3)) let notify=()=>{} // noop const route53 = new Route53(opts.aws ? opts.aws : {profile:'default'}) ; (async () => { if (opts.notify){ const notifier = await Notifier.create(opts.notify) notify = notifier.send.bind(notifier) } if (opts.cron && !opts.debug) { console.log('setting up check cron job every', opts.cron,'minutes') const cronstr = `0 */${opts.cron} * * * *` const job = new CronJob(cronstr, checkIP) console.log('cron job starting with:',cronstr) job.start() } else { console.log('running check a single time') checkIP() } })().catch(err => { console.log('FATAL: IP Updater! \n',err) process.exitCode = 1 process.kill(process.pid, 'SIGINT') // this will restart via systemd unit }) async function checkIP () { let curr = await pIp.v4() if (opts.dev) { let ip = curr.split('.') ip[0] = ip[0] - Math.floor(Math.random() * 10) curr = ip.join('.') console.log('development run: generated dummy current ip', curr) } const saved = await route53.getZoneRecordValue(rname) if (opts.debug) notify('checking for change in public ip',timestamp()) if (curr !== saved) { let msg=`${timestamp()} Network:${opts.network}, a new public ip has been detected for ${rname} from ${saved} to ${curr}` console.log(msg) notify(msg) const res=JSON.stringify(await route53.updateZoneRecordValue(rname,curr),null,3) console.log(res) notify(res) const fail = setTimeout(()=>{ clearInterval(check) msg=`${timestamp()}, ${opts.network}: FATAL the pending change of ip address was not realized in ${timeout} minutes. Human attention required` console.log(msg) notify(msg) },timeout*1000*60) const check = setInterval(async ()=>{ let [err,info] = await to(lookup(rname)) if (err) { clearInterval(check) clearTimeout(fail) msg=`FATAL Error in lookup of' ${rname}, ${err}, aborting check, human intervention required` console.log(msg) notify(msg) } else { if (curr === info.address) { msg=`Update Succeeded, lookup of ${rname} is now ${curr}` clearInterval(check) clearTimeout(fail) console.log(msg) notify(msg) } else { msg=`Update Pending, will check again in ${interval} seconds` if (verbose || opts.debug ) { notify(msg) console.log(msg) } } } } ,interval*1000) } else console.log('Public IP has not changed, nothing to do', saved) } function timestamp() { return new Date().toLocaleString().replace('T', ' ').substring(0, 19) }