diff options
| -rw-r--r-- | .gitignore | 5 | ||||
| -rw-r--r-- | Makefile | 5 | ||||
| -rw-r--r-- | _doc/rescached.cfg.adoc | 13 | ||||
| -rw-r--r-- | _www/.gitignore | 4 | ||||
| -rw-r--r-- | _www/package-lock.json | 598 | ||||
| -rw-r--r-- | _www/package.json | 21 | ||||
| -rw-r--r-- | _www/public/favicon.png | bin | 0 -> 12685 bytes | |||
| -rw-r--r-- | _www/public/global.css | 66 | ||||
| -rw-r--r-- | _www/public/index.html | 17 | ||||
| -rw-r--r-- | _www/rollup.config.js | 71 | ||||
| -rw-r--r-- | _www/src/App.svelte | 53 | ||||
| -rw-r--r-- | _www/src/Environment.svelte | 248 | ||||
| -rw-r--r-- | _www/src/InputAddress.svelte | 48 | ||||
| -rw-r--r-- | _www/src/InputNumber.svelte | 35 | ||||
| -rw-r--r-- | _www/src/LabelHint.svelte | 46 | ||||
| -rw-r--r-- | _www/src/main.js | 10 | ||||
| -rw-r--r-- | cmd/rescached/main.go | 21 | ||||
| -rw-r--r-- | cmd/rescached/rescached.cfg | 28 | ||||
| -rw-r--r-- | go.mod | 2 | ||||
| -rw-r--r-- | go.sum | 4 | ||||
| -rw-r--r-- | internal/generate_memfs.go | 31 | ||||
| -rw-r--r-- | options.go | 74 | ||||
| -rw-r--r-- | rescached.go | 51 | ||||
| -rw-r--r-- | rescached_httpd.go | 144 |
24 files changed, 1547 insertions, 48 deletions
@@ -1,10 +1,11 @@ /README.html /TODO -/cover.html -/cover.out /_doc/benchmark.html /_doc/rescached.cfg.5.gz /_doc/resolver.1.gz +/cmd/rescached/memfs.go +/cover.html +/cover.out /heap* /rescached /rescached.1.gz @@ -57,9 +57,12 @@ coverbrowse: $(COVER_HTML) lint: -golangci-lint run --enable-all ./... -$(RESCACHED_BIN): $(SRC) +$(RESCACHED_BIN): cmd/rescached/memfs.go $(SRC) go build $(DEBUG) ./cmd/rescached +cmd/rescached/memfs.go: internal/generate_memfs.go _www/public/* + go run ./internal/generate_memfs.go + $(RESOLVER_BIN): $(SRC) go build $(DEBUG) ./cmd/resolver diff --git a/_doc/rescached.cfg.adoc b/_doc/rescached.cfg.adoc index 4a28bcb..0408441 100644 --- a/_doc/rescached.cfg.adoc +++ b/_doc/rescached.cfg.adoc @@ -19,7 +19,6 @@ rescached.cfg - Configuration for rescached service == DESCRIPTION These file configure the behaviour of *rescached*(1) service. -In those file you can see some comment for any option and some possible value. This section will explain more about each option and how they effect +rescached+. @@ -37,6 +36,13 @@ in square bracket: This group of options contain the main configuration that related to rescached. +[[wui.listen]] +==== +wui.listen+ + +Format:: [host]:port +Default:: 127.0.0.1:5380 +Description:: The address to listen for web user interface. + [[dir.hosts]] ==== +dir.hosts+ @@ -158,7 +164,7 @@ Description:: Port to serve DNS over HTTP. Format:: Number Default:: 853 -Description:: Port to listen for DNS over TLS. +Description:: Port to serve DNS over TLS. [[tls.certificate]] ==== +tls.certificate+ @@ -202,6 +208,7 @@ Description:: Delay for pruning caches. Every N seconds/minutes/hours, rescached will traverse all caches and remove response that has not been accessed less than +cache.prune_threshold+. +Its value must be equal or greater than 1 hour (3600 seconds). [[cache.prune_threshold]] ==== +cache.prune_threshold+ @@ -209,7 +216,7 @@ caches and remove response that has not been accessed less than Format:: Duration with time unit. Valid time units are "s", "m", "h". Default:: -1h Description:: The duration when the cache will be considered expired. -Its value must be negative and less than -1 minute. +Its value must be negative and greater or equal than -1 hour (-3600 seconds). == EXAMPLE diff --git a/_www/.gitignore b/_www/.gitignore new file mode 100644 index 0000000..da93220 --- /dev/null +++ b/_www/.gitignore @@ -0,0 +1,4 @@ +/node_modules/ +/public/build/ + +.DS_Store diff --git a/_www/package-lock.json b/_www/package-lock.json new file mode 100644 index 0000000..c5093e1 --- /dev/null +++ b/_www/package-lock.json @@ -0,0 +1,598 @@ +{ + "name": "svelte-app", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", + "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "dev": true + }, + "@babel/highlight": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@polka/url": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz", + "integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==" + }, + "@rollup/plugin-commonjs": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-11.0.2.tgz", + "integrity": "sha512-MPYGZr0qdbV5zZj8/2AuomVpnRVXRU5XKXb3HVniwRoRCreGlf5kOE081isNWeiLIi6IYkwTX9zE0/c7V8g81g==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.0.0", + "estree-walker": "^1.0.1", + "is-reference": "^1.1.2", + "magic-string": "^0.25.2", + "resolve": "^1.11.0" + } + }, + "@rollup/plugin-node-resolve": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz", + "integrity": "sha512-RxtSL3XmdTAE2byxekYLnx+98kEUOrPHF/KRVjLH+DEIHy6kjIw7YINQzn+NXiH/NTrQLAwYs0GWB+csWygA9Q==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.0.8", + "@types/resolve": "0.0.8", + "builtin-modules": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.14.2" + } + }, + "@rollup/pluginutils": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.10.tgz", + "integrity": "sha512-d44M7t+PjmMrASHbhgpSbVgtL6EFyX7J4mYxwQ/c5eoaE6N2VgCgEcWVzNnwycIloti+/MpwFr8qfw+nRw00sw==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + } + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/node": { + "version": "13.13.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.5.tgz", + "integrity": "sha512-3ySmiBYJPqgjiHA7oEaIo2Rzz0HrOZ7yrNO5HWyaE5q0lQ3BppDZ3N53Miz8bw2I7gh1/zir2MGVZBvpb1zq9g==", + "dev": true + }, + "@types/resolve": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", + "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "acorn": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", + "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtin-modules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chokidar": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", + "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "console-clear": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/console-clear/-/console-clear-1.1.1.tgz", + "integrity": "sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=" + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-reference": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz", + "integrity": "sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw==", + "dev": true, + "requires": { + "@types/estree": "0.0.39" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "livereload": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.1.tgz", + "integrity": "sha512-9g7sua11kkyZNo2hLRCG3LuZZwqexoyEyecSlV8cAsfAVVCZqLzVir6XDqmH0r+Vzgnd5LrdHDMyjtFnJQLAYw==", + "dev": true, + "requires": { + "chokidar": "^3.3.0", + "livereload-js": "^3.1.0", + "opts": ">= 1.2.0", + "ws": "^6.2.1" + } + }, + "livereload-js": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.2.2.tgz", + "integrity": "sha512-xhScbNeC687ZINjEf/bD+BMiPx4s4q0mehcLb3zCc8+mykOtmaBR4vqzyIV9rIGdG9JjHaT0LiFdscvivCjX1Q==", + "dev": true + }, + "local-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/local-access/-/local-access-1.0.1.tgz", + "integrity": "sha512-ykt2pgN0aqIy6KQC1CqdWTWkmUwNgaOS6dcpHVjyBJONA+Xi7AtSB1vuxC/U/0tjIP3wcRudwQk1YYzUvzk2bA==" + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "mime": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.5.tgz", + "integrity": "sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w==" + }, + "mri": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.5.tgz", + "integrity": "sha512-d2RKzMD4JNyHMbnbWnznPaa8vbdlq/4pNZ3IgdaGrVbBhebBsGUUE/6qorTMYNS6TwuH3ilfOlD2bf4Igh8CKg==" + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "opts": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/opts/-/opts-1.2.7.tgz", + "integrity": "sha512-hwZhzGGG/GQ7igxAVFOEun2N4fWul31qE9nfBdCnZGQCB5+L7tN9xZ+94B4aUpLOJx/of3zZs5XsuubayQYQjA==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "rollup": { + "version": "1.32.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz", + "integrity": "sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/node": "*", + "acorn": "^7.1.0" + } + }, + "rollup-plugin-livereload": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-livereload/-/rollup-plugin-livereload-1.3.0.tgz", + "integrity": "sha512-abyqXaB21+nFHo+vJULBqfzNx6zXABC19UyvqgDfdoxR/8pFAd041GO+GIUe8ZYC2DbuMUmioh1Lvbk14YLZgw==", + "dev": true, + "requires": { + "livereload": "^0.9.1" + } + }, + "rollup-plugin-svelte": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-5.2.1.tgz", + "integrity": "sha512-wc93cN66sRpX6uFljVFqvWT6NU3V5ab/uLXKt2UiARuexFU/ctolzkmdXM7WM5iKdTX9scToS9sabJTJV4DUMA==", + "dev": true, + "requires": { + "require-relative": "^0.8.7", + "rollup-pluginutils": "^2.8.2", + "sourcemap-codec": "^1.4.8" + } + }, + "rollup-plugin-terser": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-5.3.0.tgz", + "integrity": "sha512-XGMJihTIO3eIBsVGq7jiNYOdDMb3pVxuzY0uhOE/FM4x/u9nQgr3+McsjzqBn3QfHIpNSZmFnpoKAwHBEcsT7g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "jest-worker": "^24.9.0", + "rollup-pluginutils": "^2.8.2", + "serialize-javascript": "^2.1.2", + "terser": "^4.6.2" + } + }, + "rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "requires": { + "estree-walker": "^0.6.1" + }, + "dependencies": { + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + } + } + }, + "sade": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.7.3.tgz", + "integrity": "sha512-m4BctppMvJ60W1dXnHq7jMmFe3hPJZDAH85kQ3ACTo7XZNVUuTItCQ+2HfyaMeV5cKrbw7l4vD/6We3GBxvdJw==", + "requires": { + "mri": "^1.1.0" + } + }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "dev": true + }, + "sirv": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-0.4.2.tgz", + "integrity": "sha512-dQbZnsMaIiTQPZmbGmktz+c74zt/hyrJEB4tdp2Jj0RNv9J6B/OWR5RyrZEvIn9fyh9Zlg2OlE2XzKz6wMKGAw==", + "requires": { + "@polka/url": "^0.5.0", + "mime": "^2.3.1" + } + }, + "sirv-cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/sirv-cli/-/sirv-cli-0.4.5.tgz", + "integrity": "sha512-Fl6icSm0EwPrXSGid2xphMp//WNTSX2yENRAGnJuuZNmdc8LvE/BtdZD3MPn28ifAfDqTMwbB3dpcZojAIOiBg==", + "requires": { + "console-clear": "^1.1.0", + "get-port": "^3.2.0", + "kleur": "^3.0.0", + "local-access": "^1.0.1", + "sade": "^1.4.0", + "sirv": "^0.4.2", + "tinydate": "^1.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "svelte": { + "version": "3.22.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.22.2.tgz", + "integrity": "sha512-DxumO0+vvHA6NSc2jtVty08I8lFI43q8P2zX6JxZL8J1kqK5NVjad6TRM/twhnWXC+QScnwkZ15O6X1aTsEKTA==", + "dev": true + }, + "terser": { + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.13.tgz", + "integrity": "sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "tinydate": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinydate/-/tinydate-1.2.0.tgz", + "integrity": "sha512-3GwPk8VhDFnUZ2TrgkhXJs6hcMAIIw4x/xkz+ayK6dGoQmp2nUwKzBXK0WnMsqkh6vfUhpqQicQF3rbshfyJkg==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } +} diff --git a/_www/package.json b/_www/package.json new file mode 100644 index 0000000..267b670 --- /dev/null +++ b/_www/package.json @@ -0,0 +1,21 @@ +{ + "name": "svelte-app", + "version": "1.0.0", + "scripts": { + "build": "rollup -c", + "dev": "rollup -c -w", + "start": "sirv public" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "11.0.2", + "@rollup/plugin-node-resolve": "^7.0.0", + "rollup": "^1.20.0", + "rollup-plugin-livereload": "^1.0.0", + "rollup-plugin-svelte": "^5.0.3", + "rollup-plugin-terser": "^5.1.2", + "svelte": "^3.0.0" + }, + "dependencies": { + "sirv-cli": "^0.4.4" + } +} diff --git a/_www/public/favicon.png b/_www/public/favicon.png Binary files differnew file mode 100644 index 0000000..0a3c077 --- /dev/null +++ b/_www/public/favicon.png diff --git a/_www/public/global.css b/_www/public/global.css new file mode 100644 index 0000000..ec905f5 --- /dev/null +++ b/_www/public/global.css @@ -0,0 +1,66 @@ +html, body { + position: relative; + width: 100%; + height: 100%; +} + +body { + color: #333; + margin: 0; + padding: 8px; + box-sizing: border-box; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; +} + +a { + color: rgb(0,100,200); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +a:visited { + color: rgb(0,80,160); +} + +label { + display: block; +} + +input, button, select, textarea { + font-family: inherit; + font-size: inherit; + padding: 0.4em; + margin: 0 0 0.5em 0; + box-sizing: border-box; + border: 1px solid #ccc; + border-radius: 2px; +} + +input:disabled { + color: #ccc; +} + +input[type="range"] { + height: 0; +} + +button { + color: #333; + background-color: #f4f4f4; + outline: none; +} + +button:disabled { + color: #999; +} + +button:not(:disabled):active { + background-color: #ddd; +} + +button:focus { + border-color: #666; +} diff --git a/_www/public/index.html b/_www/public/index.html new file mode 100644 index 0000000..e8e6f92 --- /dev/null +++ b/_www/public/index.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width,initial-scale=1" /> + + <title>rescached</title> + + <link rel="icon" type="image/png" href="/favicon.png" /> + <link rel="stylesheet" href="/global.css" /> + <link rel="stylesheet" href="/build/bundle.css" /> + + <script defer src="/build/bundle.js"></script> + </head> + + <body></body> +</html> diff --git a/_www/rollup.config.js b/_www/rollup.config.js new file mode 100644 index 0000000..ce3c9eb --- /dev/null +++ b/_www/rollup.config.js @@ -0,0 +1,71 @@ +import svelte from 'rollup-plugin-svelte'; +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import livereload from 'rollup-plugin-livereload'; +import { terser } from 'rollup-plugin-terser'; + +const production = !process.env.ROLLUP_WATCH; + +export default { + input: 'src/main.js', + output: { + sourcemap: true, + format: 'iife', + name: 'app', + file: 'public/build/bundle.js' + }, + plugins: [ + svelte({ + // enable run-time checks when not in production + dev: !production, + // we'll extract any component CSS out into + // a separate file - better for performance + css: css => { + css.write('public/build/bundle.css'); + } + }), + + // If you have external dependencies installed from + // npm, you'll most likely need these plugins. In + // some cases you'll need additional configuration - + // consult the documentation for details: + // https://github.com/rollup/plugins/tree/master/packages/commonjs + resolve({ + browser: true, + dedupe: ['svelte'] + }), + commonjs(), + + // In dev mode, call `npm run start` once + // the bundle has been generated + !production && serve(), + + // Watch the `public` directory and refresh the + // browser on changes when not in production + !production && livereload('public'), + + // If we're building for production (npm run build + // instead of npm run dev), minify + production && terser() + ], + watch: { + clearScreen: false + } +}; + +function serve() { + let started = false; + + return { + writeBundle() { + if (!started) { + started = true; + + require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { + stdio: ['ignore', 'inherit', 'inherit'], + shell: true + }); + } + } + }; +} diff --git a/_www/src/App.svelte b/_www/src/App.svelte new file mode 100644 index 0000000..3cde46c --- /dev/null +++ b/_www/src/App.svelte @@ -0,0 +1,53 @@ +<script> + import Environment from './Environment.svelte'; + + export let name; + let state; + + function showEnvironment() { + if (state === 'environment') { + state = ''; + } else { + state = 'environment'; + } + } +</script> + +<style> + div.main { + padding: 1em; + max-width: 800px; + margin: 0px auto; + } + + h1 { + color: #ff3e00; + text-transform: uppercase; + font-size: normal; + font-weight: 100; + } + + @media (width: 640px) { + div.main { + max-width: none; + } + } +</style> + +<div class="main"> + <h1> {name} </h1> + <nav class="menu"> + <a href="#" on:click={showEnvironment}> + Environment + </a> + </nav> + + {#if state === 'environment'} + <Environment/> + {:else if state === 'hosts'} + {:else} + <p> + Welcome to rescached! + </p> + {/if} +</div> diff --git a/_www/src/Environment.svelte b/_www/src/Environment.svelte new file mode 100644 index 0000000..cab0c5f --- /dev/null +++ b/_www/src/Environment.svelte @@ -0,0 +1,248 @@ +<script> + import { onMount } from 'svelte'; + + import LabelHint from "./LabelHint.svelte"; + import InputNumber from "./InputNumber.svelte"; + import InputAddress from "./InputAddress.svelte"; + + const nanoSeconds = 1000000000; + let apiEnvironment = "http://127.0.0.1:5380/api/environment" + let env = { + NameServers: [], + }; + + onMount(async () => { + const res = await fetch(apiEnvironment); + let got = await res.json(); + got.PruneDelay = got.PruneDelay / nanoSeconds; + got.PruneThreshold = got.PruneThreshold / nanoSeconds; + env = Object.assign(env, got) + }); + + function addNameServer() { + env.NameServers = [...env.NameServers, ''] + } + + function deleteNameServer(ns) { + for (let x = 0; x < env.NameServers.length; x++) { + if (env.NameServers[x] === ns) { + env.NameServers.splice(x, 1); + env.NameServers = env.NameServers; + break; + } + } + } + + async function updateEnvironment() { + let got = {}; + + Object.assign(got, env) + got.PruneDelay = got.PruneDelay * nanoSeconds; + got.PruneThreshold = got.PruneThreshold * nanoSeconds; + + const res = await fetch(apiEnvironment, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(got), + }); + + const resJSON = await res.json() + + console.log(resJSON); + } +</script> + +<style> + input { + width: 100%; + } + .input-deletable { + width: 100%; + } + .input-deletable > input { + float: left; + max-width: calc(100% - 80px); + } + .input-deletable > button { + float: left; + width: 80px; + } + .input-suffix input { + width: 70%; + } + .input-suffix input[type="checkbox"] { + width: auto; + } + .input-suffix .suffix { + width: 30%; + } +</style> + +<div class="environment"> +<h2> + / Environment +</h2> + +<p> +This page allow you to change the rescached environment. +Upon save, the rescached service will be restarted. +</p> + +<h3>rescached</h3> +<div> + <LabelHint + target="FileResolvConf" + title="System resolv.conf" + info="A path to dynamically generated resolv.conf(5) by +resolvconf(8). If set, the nameserver values in referenced file will +replace 'parent' value and 'parent' will become a fallback in +case the referenced file being deleted or can not be parsed." + ></LabelHint> + <input name="FileResolvConf" bind:value={env.FileResolvConf}> + + <LabelHint + target="Debug" + title="Debug level" + info="This option only used for debugging program or if user +want to monitor what kind of traffic goes in and out of rescached." + ></LabelHint> + <InputNumber min=0 max=3 bind:val={env.Debug} unit=""> + </InputNumber> +</div> + +<h3>DNS server</h3> +<div> + <LabelHint + target="NameServers" + title="Name servers" + info="List of parent DNS servers." + ></LabelHint> + {#each env.NameServers as ns} + <div class="input-deletable"> + <input bind:value={ns}> + <button on:click={deleteNameServer(ns)}> + Delete + </button> + </div> + {/each} + <button on:click={addNameServer}> + Add + </button> + + <LabelHint + target="ListenAddress" + title="Listen address" + info="Address in local network where rescached will +listening for query from client through UDP and TCP. +<br/> +If you want rescached to serve a query from another host in your local +network, change this value to <tt>0.0.0.0:53</tt>." + ></LabelHint> + <InputAddress + bind:value={env.ListenAddress} + ></InputAddress> + + <LabelHint + target="HTTPPort" + title="HTTP listen port" + info="Port to serve DNS over HTTP" + ></LabelHint> + <InputNumber min=0 max=65535 bind:val={env.HTTPPort} unit=""> + </InputNumber> + + <LabelHint + target="TLSPort" + title="TLS listen port" + info="Port to listen for DNS over TLS" + ></LabelHint> + <InputNumber min=0 max=65535 bind:val={env.TLSPort} unit=""> + </InputNumber> + + <LabelHint + target="TLSCertFile" + title="TLS certificate" + info="Path to certificate file to serve DNS over TLS and +HTTPS"></LabelHint> + <input name="TLSCertFile" bind:value={env.TLSCertFile}> + + <LabelHint + target="TLSPrivateKey" + title="TLS private key" + info="Path to certificate private key file to serve DNS over TLS and +HTTPS." + ></LabelHint> + <input name="TLSPrivateKey" bind:value={env.TLSPrivateKey}> + + <LabelHint + target="TLSAllowInsecure" + title="TLS allow insecure" + info="If its true, allow serving DoH and DoT with self signed +certificate." + ></LabelHint> + <div class="input-suffix"> + <input + name="TLSAllowInsecure" + type=checkbox + bind:checked={env.TLSAllowInsecure} + > + <span class="suffix"> + Yes + </span> + </div> + + <LabelHint + target="DoHBehindProxy" + title="DoH behind proxy" + info="If its true, serve DNS over HTTP only, even if +certificate files is defined. +This allow serving DNS request forwarded by another proxy server." + ></LabelHint> + <div class="input-suffix"> + <input + name="DoHBehindProxy" + type=checkbox + bind:checked={env.DoHBehindProxy} + > + <span class="suffix"> + Yes + </span> + </div> + + <LabelHint + target="PruneDelay" + title="Prune delay" + info="Delay for pruning caches. +Every N seconds, rescached will traverse all caches and remove response that +has not been accessed less than cache.prune_threshold. +Its value must be equal or greater than 1 hour (3600 seconds). +" + ></LabelHint> + <InputNumber + min=3600 + max=36000 + bind:val={env.PruneDelay} + unit="Seconds" + ></InputNumber> + + <LabelHint + target="PruneThreshold" + title="Prune threshold" + info="The duration when the cache will be considered expired. +Its value must be negative and greater or equal than -1 hour (-3600 seconds)." + ></LabelHint> + <InputNumber + min=-36000 + max=-3600 + bind:val={env.PruneThreshold} + unit="Seconds" + ></InputNumber> +</div> + +<div> + <button on:click={updateEnvironment}> + Save + </button> +</div> +</div> diff --git a/_www/src/InputAddress.svelte b/_www/src/InputAddress.svelte new file mode 100644 index 0000000..014004a --- /dev/null +++ b/_www/src/InputAddress.svelte @@ -0,0 +1,48 @@ +<script> + export let value = ""; + let isInvalid = false; + let error = ""; + + function onBlur() { + const ipport = value.split(":"); + if (ipport.length !== 2) { + isInvalid = true; + return; + } + const ip = ipport[0]; + if (ip.length > 0) { + const nums = ip.split("."); + if (nums.length != 4) { + isInvalid = true; + error = "invalid IP address"; + return; + } + } + const port = parseInt(ipport[1]); + if (isNaN(port) || port <= 0 || port >= 65535) { + isInvalid = true; + error = "invalid port number"; + return; + } + isInvalid = false; + value = ip +":"+ port; + } +</script> + +<style> + .invalid { + color: red; + } +</style> + +<div class="input-address"> + <input + type="text" + bind:value={value} + on:blur={onBlur} + class:invalid={isInvalid} + > + {#if isInvalid} + <span class="invalid">{error}</span> + {/if} +</div> diff --git a/_www/src/InputNumber.svelte b/_www/src/InputNumber.svelte new file mode 100644 index 0000000..6efee06 --- /dev/null +++ b/_www/src/InputNumber.svelte @@ -0,0 +1,35 @@ +<script> + export let min; + export let max; + export let val = 0; + export let unit; + + function onChange() { + value = +value + if (isNaN(value)) { + value = max + } else if (value < min) { + value = min + } else if (value > max) { + value = max + } + } +</script> + +<style> + .input-number input { + width: 70%; + } + .input-number .suffix { + width: 30%; + } +</style> + +<div class="input-number"> + <input type="number" on:change={onChange} bind:value={val}> + {#if unit !== ''} + <span class="suffix"> + {unit} + </span> + {/if} +</div> diff --git a/_www/src/LabelHint.svelte b/_www/src/LabelHint.svelte new file mode 100644 index 0000000..72b2786 --- /dev/null +++ b/_www/src/LabelHint.svelte @@ -0,0 +1,46 @@ +<script> + export let target; + export let title; + export let info; + let showInfo = false; +</script> + +<style> + label.label-hint { + margin-top: 1em; + max-width: 100%; + } + .label-hint-title { + margin-bottom: 4px; + } + .label-hint-toggle { + border-radius: 50%; + border: 1px solid grey; + cursor: pointer; + display: inline-block; + float: right; + font-size: 12px; + height: 14px; + line-height: 14px; + padding: 2px; + text-align: center; + width: 14px; + } + .label-hint-info { + background-color: #eee; + border-radius: 8px; + margin: 8px 0px; + padding: 1em; + } +</style> + +<label for={target} class="label-hint"></label> +<div class="label-hint-title"> + {title}: + <span class="label-hint-toggle" on:click={() => showInfo = !showInfo}> + ? + </span> +</div> +{#if showInfo} +<div class="label-hint-info">{@html info}</div> +{/if} diff --git a/_www/src/main.js b/_www/src/main.js new file mode 100644 index 0000000..756243d --- /dev/null +++ b/_www/src/main.js @@ -0,0 +1,10 @@ +import App from "./App.svelte" + +const app = new App({ + target: document.body, + props: { + name: "rescached", + }, +}) + +export default app diff --git a/cmd/rescached/main.go b/cmd/rescached/main.go index 8d259f4..09f6e1d 100644 --- a/cmd/rescached/main.go +++ b/cmd/rescached/main.go @@ -33,9 +33,12 @@ func main() { log.Fatal(err) } - go run(rcd) + err = rcd.Start() + if err != nil { + log.Fatal(err) + } - if debug.Value >= 2 { + if debug.Value >= 3 { go debugRuntime() } @@ -60,17 +63,3 @@ func debugRuntime() { memHeap.DiffHeapObjects) } } - -func run(rcd *rescached.Server) { - defer func() { - err := recover() - if err != nil { - log.Println("panic: ", err) - } - }() - - err := rcd.Start() - if err != nil { - log.Println(err) - } -} diff --git a/cmd/rescached/rescached.cfg b/cmd/rescached/rescached.cfg index 9f403b0..0df343b 100644 --- a/cmd/rescached/rescached.cfg +++ b/cmd/rescached/rescached.cfg @@ -5,28 +5,34 @@ ## [rescached] -#dir.hosts=/etc/rescached/hosts.d -#dir.master=/etc/rescached/master.d -#file.resolvconf=/etc/rescached/resolv.conf -#debug=0 +dir.hosts= +dir.master= +file.resolvconf= +debug=0 [dns "server"] #parent=udp://18.136.35.199 #parent=tcp://18.136.35.199 -parent=https://kilabit.info/dns-query listen = 127.0.0.1:53 ## Uncomment line below if you want to serve DNS to other computers. #listen = 0.0.0.0:53 #http.port = 443 +http.port = 0 + #tls.port = 853 +tls.port = 0 + +#tls.certificate = /etc/rescached/localhost.cert.pem +tls.certificate = -#tls.certificate = /etc/rescached/localhost.cert.pem -#tls.private_key = /etc/rescached/localhost.key.pem -#tls.allow_insecure = false +#tls.private_key = /etc/rescached/localhost.key.pem +tls.private_key = -#doh.behind_proxy = false +tls.allow_insecure = false +doh.behind_proxy = false -#cache.prune_delay = 1h -#cache.prune_threshold = -1h +cache.prune_delay = 1h0m0s +cache.prune_threshold = -1h0m0s +parent = https://kilabit.info/dns-query @@ -2,6 +2,6 @@ module github.com/shuLhan/rescached-go/v3 go 1.13 -require github.com/shuLhan/share v0.15.0 +require github.com/shuLhan/share v0.15.1-0.20200516135503-9e0bd9a6fefc //replace github.com/shuLhan/share => ../share @@ -1,5 +1,5 @@ -github.com/shuLhan/share v0.15.0 h1:VxLmHI426yYSJg8tMKSc9HmhQcF0L8O0Tr3ixtgzUYY= -github.com/shuLhan/share v0.15.0/go.mod h1:mpa0ub5qmuko/muUlOROOqLCSHKU76GzuAR/sUaSwRo= +github.com/shuLhan/share v0.15.1-0.20200516135503-9e0bd9a6fefc h1:DqO6rIUITvdYjT/T/357cnZohfdWMyhjLfLBD3FeVPY= +github.com/shuLhan/share v0.15.1-0.20200516135503-9e0bd9a6fefc/go.mod h1:mpa0ub5qmuko/muUlOROOqLCSHKU76GzuAR/sUaSwRo= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= diff --git a/internal/generate_memfs.go b/internal/generate_memfs.go new file mode 100644 index 0000000..552b924 --- /dev/null +++ b/internal/generate_memfs.go @@ -0,0 +1,31 @@ +// Copyright 2020, Shulhan <ms@kilabit.info>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "log" + + "github.com/shuLhan/share/lib/memfs" +) + +func main() { + includes := []string{ + `.*\.html`, + `.*\.js`, + `.*\.css`, + } + + mfs, err := memfs.New("_www/public/", includes, nil, true) + if err != nil { + log.Fatal(err) + } + + err = mfs.GoGenerate("main", "cmd/rescached/memfs.go", memfs.EncodingGzip) + if err != nil { + log.Fatal(err) + } +} @@ -8,6 +8,7 @@ import ( "fmt" "io/ioutil" "log" + "strconv" "github.com/shuLhan/share/lib/debug" "github.com/shuLhan/share/lib/dns" @@ -16,11 +17,37 @@ import ( libstrings "github.com/shuLhan/share/lib/strings" ) +const ( + defWuiAddress = "127.0.0.1:5380" +) + +const ( + sectionNameDNS = "dns" + sectionNameRescached = "rescached" + + subNameServer = "server" + + keyDebug = "debug" + keyFileResolvConf = "file.resolvconf" + + keyCachePruneDelay = "cache.prune_delay" + keyCachePruneThreshold = "cache.prune_threshold" + keyDohBehindProxy = "doh.behind_proxy" + keyHTTPPort = "http.port" + keyListen = "listen" + keyParent = "parent" + keyTLSAllowInsecure = "tls.allow_insecure" + keyTLSCertificate = "tls.certificate" + keyTLSPort = "tls.port" + keyTLSPrivateKey = "tls.private_key" +) + // // Options for running rescached. // type Options struct { dns.ServerOptions + WuiListen string `ini:"rescached::wui.listen"` DirHosts string `ini:"rescached::dir.hosts"` DirMaster string `ini:"rescached::dir.master"` FileResolvConf string `ini:"rescached::file.resolvconf"` @@ -43,7 +70,7 @@ func loadOptions(file string) (opts *Options) { err = ini.Unmarshal(cfg, opts) if err != nil { - log.Println("rescached: loadOptions %q: %s", file, err) + log.Printf("rescached: loadOptions %q: %s", file, err) return opts } @@ -68,6 +95,9 @@ func NewOptions() *Options { // init check and initialize the Options instance with default values. // func (opts *Options) init() { + if len(opts.WuiListen) == 0 { + opts.WuiListen = defWuiAddress + } if len(opts.ListenAddress) == 0 { opts.ListenAddress = "127.0.0.1:53" } @@ -106,3 +136,45 @@ func (opts *Options) loadResolvConf() (ok bool, err error) { return true, nil } + +// +// write the options values back to file. +// +func (opts *Options) write(file string) (err error) { + in, err := ini.Open(file) + if err != nil { + return fmt.Errorf("write: %w", err) + } + + in.Set(sectionNameRescached, "", keyFileResolvConf, opts.FileResolvConf) + in.Set(sectionNameRescached, "", keyDebug, strconv.Itoa(opts.Debug)) + + in.UnsetAll(sectionNameDNS, subNameServer, keyParent) + for _, ns := range opts.NameServers { + in.Add(sectionNameDNS, subNameServer, keyParent, ns) + } + + in.Set(sectionNameDNS, subNameServer, keyListen, + opts.ServerOptions.ListenAddress) + + in.Set(sectionNameDNS, subNameServer, keyHTTPPort, + strconv.Itoa(int(opts.ServerOptions.HTTPPort))) + + in.Set(sectionNameDNS, subNameServer, keyTLSPort, + strconv.Itoa(int(opts.ServerOptions.TLSPort))) + in.Set(sectionNameDNS, subNameServer, keyTLSCertificate, + opts.ServerOptions.TLSCertFile) + in.Set(sectionNameDNS, subNameServer, keyTLSPrivateKey, + opts.ServerOptions.TLSPrivateKey) + in.Set(sectionNameDNS, subNameServer, keyTLSAllowInsecure, + fmt.Sprintf("%t", opts.ServerOptions.TLSAllowInsecure)) + in.Set(sectionNameDNS, subNameServer, keyDohBehindProxy, + fmt.Sprintf("%t", opts.ServerOptions.DoHBehindProxy)) + + in.Set(sectionNameDNS, subNameServer, keyCachePruneDelay, + fmt.Sprintf("%s", opts.ServerOptions.PruneDelay)) + in.Set(sectionNameDNS, subNameServer, keyCachePruneThreshold, + fmt.Sprintf("%s", opts.ServerOptions.PruneThreshold)) + + return in.Save(file) +} diff --git a/rescached.go b/rescached.go index ccb2e39..74fc949 100644 --- a/rescached.go +++ b/rescached.go @@ -8,9 +8,11 @@ package rescached import ( "fmt" "log" + "sync" "github.com/shuLhan/share/lib/debug" "github.com/shuLhan/share/lib/dns" + "github.com/shuLhan/share/lib/http" libio "github.com/shuLhan/share/lib/io" ) @@ -20,6 +22,9 @@ type Server struct { dns *dns.Server opts *Options rcWatcher *libio.Watcher + + httpd *http.Server + httpdRunner sync.Once } // @@ -32,21 +37,16 @@ func New(fileConfig string) (srv *Server, err error) { fmt.Printf("rescached: config: %+v\n", opts) } - dnsServer, err := dns.NewServer(&opts.ServerOptions) - if err != nil { - return nil, err - } - - dnsServer.LoadHostsDir(opts.DirHosts) - dnsServer.LoadMasterDir(opts.DirMaster) - dnsServer.LoadHostsFile("") - srv = &Server{ fileConfig: fileConfig, - dns: dnsServer, opts: opts, } + err = srv.httpdInit() + if err != nil { + return nil, err + } + return srv, nil } @@ -55,6 +55,15 @@ func New(fileConfig string) (srv *Server, err error) { // it. // func (srv *Server) Start() (err error) { + srv.dns, err = dns.NewServer(&srv.opts.ServerOptions) + if err != nil { + return err + } + + srv.dns.LoadHostsDir(srv.opts.DirHosts) + srv.dns.LoadMasterDir(srv.opts.DirMaster) + srv.dns.LoadHostsFile("") + if len(srv.opts.FileResolvConf) > 0 { srv.rcWatcher, err = libio.NewWatcher( srv.opts.FileResolvConf, 0, srv.watchResolvConf) @@ -63,7 +72,27 @@ func (srv *Server) Start() (err error) { } } - return srv.dns.ListenAndServe() + go func() { + srv.httpdRunner.Do(srv.httpdRun) + }() + + go srv.run() + + return nil +} + +func (srv *Server) run() { + defer func() { + err := recover() + if err != nil { + log.Println("panic: ", err) + } + }() + + err := srv.dns.ListenAndServe() + if err != nil { + log.Println(err) + } } // diff --git a/rescached_httpd.go b/rescached_httpd.go new file mode 100644 index 0000000..4ded760 --- /dev/null +++ b/rescached_httpd.go @@ -0,0 +1,144 @@ +// Copyright 2020, Shulhan <ms@kilabit.info>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rescached + +import ( + "encoding/json" + "fmt" + "log" + stdhttp "net/http" + + liberrors "github.com/shuLhan/share/lib/errors" + "github.com/shuLhan/share/lib/http" +) + +const ( + defHTTPDRootDir = "_www/public/" +) + +func (srv *Server) httpdInit() (err error) { + opts := &http.ServerOptions{ + Root: defHTTPDRootDir, + Address: srv.opts.WuiListen, + Includes: []string{ + `.*\.css`, + `.*\.html`, + `.*\.js`, + }, + CORSAllowOrigins: []string{ + "http://127.0.0.1:5000", + }, + CORSAllowHeaders: []string{ + http.HeaderContentType, + }, + } + + srv.httpd, err = http.NewServer(opts) + if err != nil { + return fmt.Errorf("newHTTPServer: %w", err) + } + + err = srv.httpdRegisterEndpoints() + if err != nil { + return fmt.Errorf("newHTTPServer: %w", err) + } + + return nil +} + +func (srv *Server) httpdRegisterEndpoints() (err error) { + epAPIGetEnvironment := &http.Endpoint{ + Method: http.RequestMethodGet, + Path: "/api/environment", + RequestType: http.RequestTypeJSON, + ResponseType: http.ResponseTypeJSON, + Call: srv.httpdAPIGetEnvironment, + } + + err = srv.httpd.RegisterEndpoint(epAPIGetEnvironment) + if err != nil { + return err + } + + epAPIPostEnvironment := &http.Endpoint{ + Method: http.RequestMethodPost, + Path: "/api/environment", + RequestType: http.RequestTypeJSON, + ResponseType: http.ResponseTypeJSON, + Call: srv.httpdAPIPostEnvironment, + } + + err = srv.httpd.RegisterEndpoint(epAPIPostEnvironment) + if err != nil { + return err + } + + return nil +} + +func (srv *Server) httpdRun() { + defer func() { + err := recover() + if err != nil { + log.Printf("httpServer: %s", err) + } + }() + + log.Printf("=== rescached: httpd listening at %s", srv.opts.WuiListen) + + err := srv.httpd.Start() + if err != nil { + log.Printf("httpServer.run: %s", err) + } +} + +func (srv *Server) httpdAPIGetEnvironment( + httpRes stdhttp.ResponseWriter, req *stdhttp.Request, reqBody []byte, +) ( + resBody []byte, err error, +) { + return json.Marshal(srv.opts) +} + +func (srv *Server) httpdAPIPostEnvironment( + httpRes stdhttp.ResponseWriter, req *stdhttp.Request, reqBody []byte, +) ( + resBody []byte, err error, +) { + newOpts := new(Options) + err = json.Unmarshal(reqBody, newOpts) + if err != nil { + return nil, err + } + + newOpts.init() + + fmt.Printf("new options: %+v\n", newOpts) + + res := &liberrors.E{ + Code: stdhttp.StatusOK, + Message: "Restarting DNS server", + } + + err = newOpts.write(srv.fileConfig) + if err != nil { + log.Println("httpdAPIPostEnvironment:", err.Error()) + res.Code = stdhttp.StatusInternalServerError + res.Message = err.Error() + return json.Marshal(res) + } + + srv.opts = newOpts + + srv.Stop() + err = srv.Start() + if err != nil { + log.Println("httpdAPIPostEnvironment:", err.Error()) + res.Code = stdhttp.StatusInternalServerError + res.Message = err.Error() + } + + return json.Marshal(res) +} |
