diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5d47c21 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..38163df --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,17 @@ +{ + "deno.codeLens.implementations": true, + "deno.codeLens.referencesAllFunctions": true, + "deno.codeLens.references": true, + "deno.codeLens.test": true, + "deno.enable": true, + "deno.config": "./deno.json", + "deno.lint": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[typescript]": { + "editor.defaultFormatter": "denoland.vscode-deno" + }, + "editor.formatOnSave": true, + "[sql]": { + "editor.formatOnSave": false + } +} diff --git a/README.md b/README.md index c1864a0..965b945 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,23 @@ A Node.js program use dns.he.net as DDNS service. ## Configuration +```sh +OPTIONS: + +-d, --domain + Set domain + +-t, --token + Set he.net auth token + + --delaytime + Set loop delay time, ms. + +-h, --help +``` + Set env: - USEDDNS_DOMAIN - USEDDNS_HE_TOKEN +- [OPTION] USEDDNS_DELAY_TIME diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..8a8cc6f --- /dev/null +++ b/deno.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + "strict": true + }, + "lint": { + "files": { + "include": [ + "src/", + "mod.ts" + ] + }, + "rules": { + "tags": [ + "recommended" + ], + "include": [ + "eqeqeq", + "explicit-module-boundary-types", + "no-throw-literal" + ], + "exclude": [ + "ban-unused-ignore" + ] + } + }, + "fmt": { + "files": { + "include": [ + "src/", + "mod.ts" + ] + }, + "options": { + "useTabs": false, + "lineWidth": 80, + "indentWidth": 2, + "singleQuote": true + } + } +} diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..ffbb1a5 --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,62 @@ +import { parse } from 'https://deno.land/std@0.161.0/flags/mod.ts'; +import { checkIP } from './useddns.ts'; + +const args = parse< + Partial< + { + d: string; + domain: string; + t: string; + token: string; + delaytime: number; + h: boolean; + help: boolean; + } + > +>( + Deno.args, +); + +const isHelp = args.h ?? args.help; +if (isHelp) { + console.log(` +OPTIONS: + +-d, --domain + Set domain + +-t, --token + Set he.net auth token + + --delaytime + Set loop delay time, ms. + +-h, --help`); + Deno.exit(); +} + +const domain = args.d ?? args.domain ?? Deno.env.get('USEDDNS_DOMAIN'); +const token = args.t ?? args.token ?? Deno.env.get('USEDDNS_HE_TOKEN'); +const delay = Number( + args.delaytime ?? Deno.env.get('USEDDNS_DELAY_TIME') ?? 5000, +); + +if (!domain || !token) { + throw new Error( + 'Please set cli args or configure environment variables first.', + ); +} + +console.log({ domain, token }); + +setInterval(async () => { + const [err, res] = await checkIP(token, domain); + const d = new Date(); + if (err) { + console.error(d, err); + } else { + if (res !== 'eq') { + console.log(d, res); + } + } +}, delay); diff --git a/src/useddns.test.ts b/src/useddns.test.ts new file mode 100644 index 0000000..42b60fa --- /dev/null +++ b/src/useddns.test.ts @@ -0,0 +1,17 @@ +import { assert } from 'https://deno.land/std@0.161.0/testing/asserts.ts'; +import { getCurrentIP, getDNSIP } from './useddns.ts'; + +Deno.test('getCurrentIP', async () => { + const res = await getCurrentIP(); + console.log(res); + assert(res.length > 0); +}); + +Deno.test('getDNSIP', async () => { + const res = await getDNSIP( + 'www.google.com', + { dns: '1.1.1.1' }, + ); + console.log(res); + assert(res.length > 0); +}); diff --git a/src/useddns.ts b/src/useddns.ts new file mode 100644 index 0000000..7ef40ea --- /dev/null +++ b/src/useddns.ts @@ -0,0 +1,51 @@ +import { run } from 'https://deno.land/x/somefn@v0.20.0/deno/run.ts'; + +export async function getIP(params: string): Promise { + const { res } = await run(`dig ${params} +short -4`); + return res.replace(/(\r\n|\n|\r)/gm, ''); +} + +export function getCurrentIP(): Promise { + return getIP('@ns1-1.akamaitech.net ANY whoami.akamai.net'); +} + +export function getDNSIP( + domain: string, + { dns = '1.1.1.1' }: { dns?: string } = {}, +): Promise { + return getIP(`@${dns} A ${domain}`); +} + +export async function checkIP( + token: string, + domain: string, +): Promise<[err: Error] | [err: null, res: string]> { + try { + const currentIP = await getCurrentIP(); + const DNSIP = await getDNSIP(domain); + if (!currentIP || !DNSIP) { + throw new Error( + 'Did not get IP: ' + JSON.stringify({ currentIP, DNSIP }), + ); + } + if (currentIP !== DNSIP) { + console.log(JSON.stringify({ currentIP, DNSIP })); + const url = new URL('https://dyn.dns.he.net/nic/update'); + url.searchParams.set('hostname', domain); + url.searchParams.set('password', token); + url.searchParams.set('myip', currentIP); + const res = await fetch( + url, + { method: 'GET' }, + ); + console.log('res', res.status, res.headers, await res.text()); + return [null, await res.text()]; + } + return [null, 'eq']; + } catch (err) { + if (err instanceof Error) { + return [err]; + } + return [new Error(`unknown ${err}`)]; + } +}