overview.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. // Copyright 2023 MOSSDeF, Stan Grishin <stangri@melmac.ca>
  2. // This code wouldn't have been possible without help from:
  3. // - [@jow-](https://github.com/jow-)
  4. // - [@stokito](https://github.com/stokito)
  5. // - [@vsviridov](https://github.com/vsviridov)
  6. // noinspection JSAnnotator
  7. "use strict";
  8. "require form";
  9. "require rpc";
  10. "require view";
  11. "require https-dns-proxy.status as hdp";
  12. var pkg = hdp.pkg;
  13. return view.extend({
  14. load: function () {
  15. return Promise.all([
  16. L.resolveDefault(hdp.getPlatformSupport(pkg.Name), {}),
  17. L.resolveDefault(hdp.getProviders(pkg.Name), {}),
  18. L.resolveDefault(L.uci.load(pkg.Name), {}),
  19. L.resolveDefault(L.uci.load("dhcp"), {}),
  20. ]);
  21. },
  22. render: function (data) {
  23. var reply = {
  24. platform: (data[0] && data[0][pkg.Name]) || {
  25. http2_support: null,
  26. http3_support: null,
  27. },
  28. providers: (data[1] && data[1][pkg.Name]) || { providers: [] },
  29. };
  30. reply.providers.sort(function (a, b) {
  31. return _(a.title).localeCompare(_(b.title));
  32. });
  33. reply.providers.push({
  34. title: "Custom",
  35. template: "{option}",
  36. params: { option: { type: "text" } },
  37. });
  38. var status, m, s, o, p;
  39. var text;
  40. status = new hdp.status();
  41. m = new form.Map(pkg.Name, _("HTTPS DNS Proxy - Configuration"));
  42. s = m.section(form.NamedSection, "config", pkg.Name);
  43. o = s.option(
  44. form.ListValue,
  45. "dnsmasq_config_update_option",
  46. _("Update DNSMASQ Config on Start/Stop"),
  47. _(
  48. "If update option is selected, the %s'DNS Forwards' section of DHCP and DNS%s will be automatically updated to use selected DoH providers (%smore information%s)."
  49. ).format(
  50. '<a href="' + L.url("admin", "network", "dhcp") + '">',
  51. "</a>",
  52. '<a href="' + pkg.URL + "#default-settings" + '" target="_blank">',
  53. "</a>"
  54. )
  55. );
  56. o.value("*", _("Update all configs"));
  57. o.value("+", _("Update select configs"));
  58. o.value("-", _("Do not update configs"));
  59. o.default = "*";
  60. o.retain = true;
  61. o.cfgvalue = function (section_id) {
  62. let val = this.map.data.get(
  63. this.map.config,
  64. section_id,
  65. "dnsmasq_config_update"
  66. );
  67. if (val && val[0]) {
  68. switch (val[0]) {
  69. case "*":
  70. case "-":
  71. return val[0];
  72. default:
  73. return "+";
  74. }
  75. } else return "*";
  76. };
  77. o.write = function (section_id, formvalue) {
  78. L.uci.set(pkg.Name, section_id, "dnsmasq_config_update", formvalue);
  79. };
  80. o = s.option(
  81. form.MultiValue,
  82. "dnsmasq_config_update",
  83. _("Select the DNSMASQ Configs to update")
  84. );
  85. Object.values(L.uci.sections("dhcp", "dnsmasq")).forEach(function (
  86. element
  87. ) {
  88. var description;
  89. var key;
  90. if (element[".name"] === L.uci.resolveSID("dhcp", element[".name"])) {
  91. key = element[".index"];
  92. description = "dnsmasq[" + element[".index"] + "]";
  93. } else {
  94. key = element[".name"];
  95. description = element[".name"];
  96. }
  97. o.value(key, description);
  98. });
  99. o.depends("dnsmasq_config_update_option", "+");
  100. o.retain = true;
  101. o = s.option(
  102. form.ListValue,
  103. "force_dns",
  104. _("Force Router DNS"),
  105. _(
  106. "Forces Router DNS use on local devices, also known as DNS Hijacking. Only works on `lan` interface by default (%smore information%s)."
  107. ).format(
  108. '<a href="' + pkg.URL + "#force_dns" + '" target="_blank">',
  109. "</a>"
  110. )
  111. );
  112. o.value("0", _("Let local devices use their own DNS servers if set"));
  113. o.value("1", _("Force Router DNS server to all local devices"));
  114. o.default = "1";
  115. o = s.option(
  116. form.ListValue,
  117. "canary_domains_icloud",
  118. _("Canary Domains iCloud"),
  119. _(
  120. "Blocks access to iCloud Private Relay resolvers, forcing local devices to use router for DNS resolution (%smore information%s)."
  121. ).format(
  122. '<a href="' + pkg.URL + "#canary_domains_icloud" + '" target="_blank">',
  123. "</a>"
  124. )
  125. );
  126. o.value("0", _("Let local devices use iCloud Private Relay"));
  127. o.value("1", _("Force Router DNS server to all local devices"));
  128. o.depends("force_dns", "1");
  129. o.default = "1";
  130. o = s.option(
  131. form.ListValue,
  132. "canary_domains_mozilla",
  133. _("Canary Domains Mozilla"),
  134. _(
  135. "Blocks access to Mozilla Encrypted resolvers, forcing local devices to use router for DNS resolution (%smore information%s)."
  136. ).format(
  137. '<a href="' +
  138. pkg.URL +
  139. "#canary_domains_mozilla" +
  140. '" target="_blank">',
  141. "</a>"
  142. )
  143. );
  144. o.value("0", _("Let local devices use Mozilla Private Relay"));
  145. o.value("1", _("Force Router DNS server to all local devices"));
  146. o.depends("force_dns", "1");
  147. o.default = "1";
  148. text = "";
  149. if (!reply.platform.http2_support)
  150. text +=
  151. _(
  152. "Please note that %s is not supported on this system (%smore information%s)."
  153. ).format(
  154. "<i>HTTP/2</i>",
  155. '<a href="' + pkg.URL + "#http2-support" + '" target="_blank">',
  156. "</a>"
  157. ) + "<br />";
  158. if (!reply.platform.http3_support)
  159. text +=
  160. _(
  161. "Please note that %s is not supported on this system (%smore information%s)."
  162. ).format(
  163. "<i>HTTP/3 (QUIC)</i>",
  164. '<a href="' + pkg.URL + "#http3-quic-support" + '" target="_blank">',
  165. "</a>"
  166. ) + "<br />";
  167. s = m.section(
  168. form.GridSection,
  169. "https-dns-proxy",
  170. _("HTTPS DNS Proxy - Instances"),
  171. text
  172. );
  173. s.rowcolors = true;
  174. s.sortable = true;
  175. s.anonymous = true;
  176. s.addremove = true;
  177. s.sectiontitle = (section_id) => {
  178. var provText;
  179. var found;
  180. reply.providers.forEach((prov) => {
  181. var option;
  182. let regexp = pkg.templateToRegexp(prov.template);
  183. let resolver = L.uci.get(pkg.Name, section_id, "resolver_url");
  184. resolver = resolver === undefined ? null : resolver;
  185. if (!found && resolver && regexp.test(resolver)) {
  186. found = true;
  187. provText = _(prov.title);
  188. let match = resolver.match(regexp);
  189. if (match[1] != null) {
  190. if (
  191. prov.params &&
  192. prov.params.option &&
  193. prov.params.option.options
  194. ) {
  195. prov.params.option.options.forEach((opt) => {
  196. if (opt.value === match[1]) {
  197. option = _(opt.description);
  198. }
  199. });
  200. provText += " (" + option + ")";
  201. } else {
  202. if (match[1] !== "") provText += " (" + match[1] + ")";
  203. }
  204. }
  205. }
  206. });
  207. return provText || _("Unknown");
  208. };
  209. var _provider;
  210. _provider = s.option(form.ListValue, "_provider", _("Provider"));
  211. _provider.modalonly = true;
  212. _provider.cfgvalue = function (section_id) {
  213. let resolver = this.map.data.get(
  214. this.map.config,
  215. section_id,
  216. "resolver_url"
  217. );
  218. if (resolver === undefined || resolver === null) return null;
  219. let found;
  220. let ret;
  221. reply.providers.forEach((prov, i) => {
  222. let regexp = pkg.templateToRegexp(prov.template);
  223. if (!found && regexp.test(resolver)) {
  224. found = true;
  225. ret = prov.template;
  226. }
  227. });
  228. return ret || "";
  229. };
  230. _provider.write = function (section_id, formvalue) {
  231. L.uci.set(pkg.Name, section_id, "resolver_url", formvalue);
  232. };
  233. reply.providers.forEach((prov, i) => {
  234. if (prov.http2_only && !reply.platform.http2_support) return;
  235. if (prov.http3_only && !reply.platform.http3_support) return;
  236. _provider.value(prov.template, _(prov.title));
  237. if (
  238. prov.params &&
  239. prov.params.option &&
  240. prov.params.option.type &&
  241. prov.params.option.type === "select"
  242. ) {
  243. let optName = prov.params.option.description || _("Parameter");
  244. var _paramList = s.option(form.ListValue, "_paramList_" + i, optName);
  245. _paramList.template = prov.template;
  246. _paramList.modalonly = true;
  247. if (prov.params.option.default) {
  248. _paramList.default = prov.params.option.default;
  249. }
  250. prov.params.option.options.forEach((opt) => {
  251. let val = opt.value || "";
  252. let descr = opt.description || "";
  253. _paramList.value(val, descr);
  254. });
  255. _paramList.depends("_provider", prov.template);
  256. _paramList.write = function (section_id, formvalue) {
  257. let template = this.map.data.get(
  258. this.map.config,
  259. section_id,
  260. "resolver_url"
  261. );
  262. if (!formvalue && _paramList.template !== template) return 0;
  263. let resolver = pkg.templateToResolver(_paramList.template, {
  264. option: formvalue || "",
  265. });
  266. L.uci.set(pkg.Name, section_id, "resolver_url", resolver);
  267. };
  268. _paramList.remove = _paramList.write;
  269. } else if (
  270. prov.params &&
  271. prov.params.option &&
  272. prov.params.option.type &&
  273. prov.params.option.type === "text"
  274. ) {
  275. let optName = prov.params.option.description || _("Parameter");
  276. var _paramText = s.option(form.Value, "_paramText_" + i, optName);
  277. _paramText.template = prov.template;
  278. _paramText.modalonly = true;
  279. _paramText.depends("_provider", prov.template);
  280. _paramText.optional = !(
  281. prov.params.option.default && prov.params.option.default !== ""
  282. );
  283. _paramText.cfgvalue = function (section_id) {
  284. let resolver = this.map.data.get(
  285. this.map.config,
  286. section_id,
  287. "resolver_url"
  288. );
  289. if (resolver === undefined || resolver === null) return null;
  290. let regexp = pkg.templateToRegexp(prov.template);
  291. let match = resolver.match(regexp);
  292. return (match && match[1]) || null;
  293. };
  294. _paramText.write = function (section_id, formvalue) {
  295. let template = this.map.data.get(
  296. this.map.config,
  297. section_id,
  298. "resolver_url"
  299. );
  300. if (!formvalue && _paramText.template !== template) return 0;
  301. let resolver = pkg.templateToResolver(_paramText.template, {
  302. option: formvalue || "",
  303. });
  304. L.uci.set(pkg.Name, section_id, "resolver_url", resolver);
  305. };
  306. _paramText.remove = _paramText.write;
  307. }
  308. });
  309. o = s.option(form.Value, "bootstrap_dns", _("Bootstrap DNS"));
  310. o.default = "";
  311. o.modalonly = true;
  312. o.optional = true;
  313. o = s.option(form.Value, "listen_addr", _("Listen Address"));
  314. o.datatype = "ipaddr";
  315. o.default = "";
  316. o.optional = true;
  317. o.placeholder = "127.0.0.1";
  318. o = s.option(form.Value, "listen_port", _("Listen Port"));
  319. o.datatype = "port";
  320. o.default = "";
  321. o.optional = true;
  322. o.placeholder = "5053";
  323. o = s.option(form.Value, "user", _("Run As User"));
  324. o.default = "";
  325. o.modalonly = true;
  326. o.optional = true;
  327. o = s.option(form.Value, "group", _("Run As Group"));
  328. o.default = "";
  329. o.modalonly = true;
  330. o.optional = true;
  331. o = s.option(form.Value, "dscp_codepoint", _("DSCP Codepoint"));
  332. o.datatype = "and(uinteger, range(0,63))";
  333. o.default = "";
  334. o.modalonly = true;
  335. o.optional = true;
  336. o = s.option(form.Value, "verbosity", _("Logging Verbosity"));
  337. o.datatype = "and(uinteger, range(0,4))";
  338. o.default = "";
  339. o.modalonly = true;
  340. o.optional = true;
  341. o = s.option(form.Value, "logfile", _("Logging File Path"));
  342. o.default = "";
  343. o.modalonly = true;
  344. o.optional = true;
  345. o = s.option(form.Value, "polling_interval", _("Polling Interval"));
  346. o.datatype = "and(uinteger, range(5,3600))";
  347. o.default = "";
  348. o.modalonly = true;
  349. o.optional = true;
  350. o = s.option(form.Value, "proxy_server", _("Proxy Server"));
  351. o.default = "";
  352. o.modalonly = true;
  353. o.optional = true;
  354. o = s.option(form.ListValue, "use_http1", _("Use HTTP/1"));
  355. o.modalonly = true;
  356. o.optional = true;
  357. o.rmempty = true;
  358. o.value("", _("Use negotiated HTTP version"));
  359. o.value("1", _("Force use of HTTP/1"));
  360. o.default = "";
  361. o = s.option(
  362. form.ListValue,
  363. "use_ipv6_resolvers_only",
  364. _("Use IPv6 resolvers")
  365. );
  366. o.modalonly = true;
  367. o.optional = true;
  368. o.rmempty = true;
  369. o.value("", _("Use any family DNS resolvers"));
  370. o.value("1", _("Force use of IPv6 DNS resolvers"));
  371. o.default = "";
  372. return Promise.all([status.render(), m.render()]);
  373. },
  374. });