diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/additionalTab.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/additionalTab.js index c51f93f..26af68b 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/additionalTab.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/additionalTab.js @@ -196,7 +196,6 @@ function createAdditionalSection(mainSection, network) { o.rmempty = false; o.ucisection = 'main'; - // TODO(ampetelin): Can be moved to advanced settings in luci // Extra IPs and exclusions (main section) o = mainSection.taboption('basic', form.Flag, 'exclude_from_ip_enabled', _('IP for exclusion'), _('Specify local IP addresses that will never use the configured route')); o.default = '0'; diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/configSection.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/configSection.js index cc887f2..73136ac 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/configSection.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/configSection.js @@ -240,6 +240,44 @@ function createConfigSection(section, map, network) { return true; }; + o = s.taboption('basic', form.Flag, 'domain_resolver_enabled', _('Domain Resolver'), _('Enable built-in DNS resolver for domains handled by this section')); + o.default = '0'; + o.rmempty = false; + o.depends('mode', 'vpn'); + o.ucisection = s.section; + + o = s.taboption('basic', form.ListValue, 'domain_resolver_dns_type', _('Domain Resolver DNS Protocol Type'), _('Select DNS protocol for split')); + o.value('doh', _('DNS over HTTPS (DoH)')); + o.value('dot', _('DNS over TLS (DoT)')); + o.value('udp', _('UDP (Unprotected DNS)')); + o.default = 'udp'; + o.rmempty = false; + o.depends('domain_resolver_enabled', '1'); + o.ucisection = s.section; + + o = s.taboption('basic', form.Value, 'domain_resolver_dns_server', _('Domain Resolver Server'), _('Select or enter DNS server address')); + Object.entries(constants.DNS_SERVER_OPTIONS).forEach(([key, label]) => { + o.value(key, _(label)); + }); + o.default = '8.8.8.8'; + o.rmempty = false; + o.depends('domain_resolver_enabled', '1'); + o.ucisection = s.section; + o.validate = function (section_id, value) { + if (!value) { + return _('DNS server address cannot be empty'); + } + + const ipRegex = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}(:[0-9]{1,5})?$/; + const domainRegex = /^(?:https:\/\/)?([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,63}(:[0-9]{1,5})?(\/[^?#\s]*)?$/; + + if (!ipRegex.test(value) && !domainRegex.test(value)) { + return _('Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH'); + } + + return true; + }; + o = s.taboption('basic', form.Flag, 'community_lists_enabled', _('Community Lists')); o.default = '0'; o.rmempty = false; diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop index 0675076..7682190 100755 --- a/podkop/files/usr/bin/podkop +++ b/podkop/files/usr/bin/podkop @@ -717,15 +717,32 @@ configure_outbound_handler() { ;; vpn) log "Configuring outbound in VPN connection mode for the $section section" - local interface_name + local interface_name domain_resolver_enabled domain_resolver_dns_type domain_resolver_dns_server \ + outbound_tag domain_resolver_tag dns_domain_resolver + config_get interface_name "$section" "interface" + config_get domain_resolver_enabled "$section" "domain_resolver_enabled" + config_get domain_resolver_dns_type "$section" "domain_resolver_dns_type" + config_get domain_resolver_dns_server "$section" "domain_resolver_dns_server" if [ -z "$interface_name" ]; then log "VPN interface is not set. Aborted." "fatal" exit 1 fi - config=$(sing_box_cf_add_interface_outbound "$config" "$section" "$interface_name") + local outbound_tag + outbound_tag="$(get_outbound_tag_by_section "$section")" + + if [ "$domain_resolver_enabled" -eq 1 ]; then + if ! is_ipv4 "$domain_resolver_dns_server"; then + dns_domain_resolver=$SB_BOOTSTRAP_SERVER_TAG + fi + domain_resolver_tag="$(get_domain_resolver_tag "$section")" + config=$(sing_box_cf_add_dns_server "$config" "$domain_resolver_dns_type" "$domain_resolver_tag" \ + "$domain_resolver_dns_server" "$dns_domain_resolver" "$outbound_tag") + fi + + config=$(sing_box_cm_add_interface_outbound "$config" "$outbound_tag" "$interface_name" "$domain_resolver_tag") ;; block) log "Connection mode 'block' detected for the $section section – no outbound will be created (handled via reject route rules)" @@ -742,13 +759,12 @@ sing_box_configure_dns() { config=$(sing_box_cm_configure_dns "$config" "$SB_DNS_SERVER_TAG" "ipv4_only" true) log "Adding DNS Servers" "debug" - local dns_type dns_server bootstrap_dns_server dns_server_address dns_domain_resolver + local dns_type dns_server bootstrap_dns_server dns_domain_resolver config_get dns_type "main" "dns_type" "doh" config_get dns_server "main" "dns_server" "1.1.1.1" config_get bootstrap_dns_server "main" "bootstrap_dns_server" "77.88.8.8" - dns_server_address=$(url_get_host "$dns_server") - if ! is_ipv4 "$dns_server_address"; then + if ! is_ipv4 "$dns_server"; then dns_domain_resolver=$SB_BOOTSTRAP_SERVER_TAG fi diff --git a/podkop/files/usr/lib/helpers.sh b/podkop/files/usr/lib/helpers.sh index b0cfb22..a8d035a 100644 --- a/podkop/files/usr/lib/helpers.sh +++ b/podkop/files/usr/lib/helpers.sh @@ -97,6 +97,14 @@ get_outbound_tag_by_section() { echo "$section-$postfix" } +# Constructs and returns a domain resolver tag by appending a fixed postfix to the given section +get_domain_resolver_tag() { + local section="$1" + local postfix="domain-resolver" + + echo "$section-$postfix" +} + # Constructs and returns a ruleset tag using section, name, optional type, and a fixed postfix get_ruleset_tag() { local section="$1"