aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--Makefile5
-rw-r--r--_doc/rescached.cfg.adoc13
-rw-r--r--_www/.gitignore4
-rw-r--r--_www/package-lock.json598
-rw-r--r--_www/package.json21
-rw-r--r--_www/public/favicon.pngbin0 -> 12685 bytes
-rw-r--r--_www/public/global.css66
-rw-r--r--_www/public/index.html17
-rw-r--r--_www/rollup.config.js71
-rw-r--r--_www/src/App.svelte53
-rw-r--r--_www/src/Environment.svelte248
-rw-r--r--_www/src/InputAddress.svelte48
-rw-r--r--_www/src/InputNumber.svelte35
-rw-r--r--_www/src/LabelHint.svelte46
-rw-r--r--_www/src/main.js10
-rw-r--r--cmd/rescached/main.go21
-rw-r--r--cmd/rescached/rescached.cfg28
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--internal/generate_memfs.go31
-rw-r--r--options.go74
-rw-r--r--rescached.go51
-rw-r--r--rescached_httpd.go144
24 files changed, 1547 insertions, 48 deletions
diff --git a/.gitignore b/.gitignore
index b2ea592..4335f28 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/Makefile b/Makefile
index f723d09..0d9ba2c 100644
--- a/Makefile
+++ b/Makefile
@@ -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
new file mode 100644
index 0000000..0a3c077
--- /dev/null
+++ b/_www/public/favicon.png
Binary files differ
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
diff --git a/go.mod b/go.mod
index 529f86e..3945da1 100644
--- a/go.mod
+++ b/go.mod
@@ -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
diff --git a/go.sum b/go.sum
index 7e77a65..5e3ab57 100644
--- a/go.sum
+++ b/go.sum
@@ -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)
+ }
+}
diff --git a/options.go b/options.go
index ae9cf6f..93fb499 100644
--- a/options.go
+++ b/options.go
@@ -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)
+}