@@ -18,6 +18,7 @@ BOX_DNS_ENHANCED_MODE=""
1818BOX_DNS_COEXIST_MODE=" "
1919BOX_IPV6_ENABLED=" "
2020BOX_CAMPUS_DNS_MODE=" "
21+ BOX_CAMPUS_DNS_SUFFIX_POLICY=" "
2122BOX_TAILSCALE_IFACE=" "
2223BOX_TAILNET_IPV4_CIDR=" "
2324BOX_TAILNET_IPV6_CIDR=" "
@@ -119,6 +120,7 @@ config_defaults() {
119120 BOX_DNS_COEXIST_MODE=" preserve_tailnet"
120121 BOX_IPV6_ENABLED=" true"
121122 BOX_CAMPUS_DNS_MODE=" auto"
123+ BOX_CAMPUS_DNS_SUFFIX_POLICY=" org_only"
122124 BOX_TAILSCALE_IFACE=" tailscale0"
123125 BOX_TAILNET_IPV4_CIDR=" 100.64.0.0/10"
124126 BOX_TAILNET_IPV6_CIDR=" fd7a:115c:a1e0::/48"
@@ -486,6 +488,14 @@ normalize_org_dns_mode() {
486488 esac
487489}
488490
491+ normalize_org_dns_suffix_policy () {
492+ case " ${1:- } " in
493+ " " |org|org_only) printf ' org_only\n' ;;
494+ best_match|link) printf ' best_match\n' ;;
495+ * ) printf ' %s\n' " ${1:- } " ;;
496+ esac
497+ }
498+
489499box_active_default_iface () {
490500 local ip_cmd=" ${BOX_IP_CMD:- ip} "
491501 command -v " ${ip_cmd} " > /dev/null 2>&1 || return 1
@@ -508,6 +518,28 @@ box_active_link_dns_servers() {
508518 '
509519}
510520
521+ box_dns_link_ifaces () {
522+ local resolvectl_cmd=" ${BOX_RESOLVECTL_CMD:- resolvectl} "
523+ command -v " ${resolvectl_cmd} " > /dev/null 2>&1 || return 1
524+ " ${resolvectl_cmd} " dns 2> /dev/null | awk '
525+ /^Link [0-9]+ \([^)]*\):/ {
526+ iface = $3
527+ gsub(/^\(/, "", iface)
528+ gsub(/\):$/, "", iface)
529+ has_ipv4 = 0
530+ for (i = 4; i <= NF; i++) {
531+ if ($i ~ /^([0-9]{1,3}\.){3}[0-9]{1,3}$/) {
532+ has_ipv4 = 1
533+ break
534+ }
535+ }
536+ if (iface != "" && has_ipv4) {
537+ print iface
538+ }
539+ }
540+ '
541+ }
542+
511543box_probe_host_via_dns () {
512544 local server=" ${1:- } "
513545 local host=" ${2:- } "
@@ -528,6 +560,101 @@ box_is_private_ipv4() {
528560 return 1
529561}
530562
563+ box_org_dns_probe_count () {
564+ local probe_host count=0
565+ for probe_host in " ${BOX_CAMPUS_DNS_PROBE_HOSTS[@]} " ; do
566+ [[ -n " ${probe_host} " ]] || continue
567+ (( count += 1 ))
568+ done
569+ printf ' %s\n' " ${count} "
570+ }
571+
572+ box_org_dns_probe_score_for_iface () {
573+ local iface=" ${1:- } "
574+ local dns_server probe_host answer
575+ local -a dns_servers=()
576+ local score=0
577+
578+ [[ -n " ${iface} " ]] || {
579+ printf ' 0\n'
580+ return 0
581+ }
582+
583+ mapfile -t dns_servers < <( box_active_link_dns_servers " ${iface} " || true)
584+ if [[ " ${# dns_servers[@]} " -eq 0 ]]; then
585+ printf ' 0\n'
586+ return 0
587+ fi
588+
589+ for probe_host in " ${BOX_CAMPUS_DNS_PROBE_HOSTS[@]} " ; do
590+ [[ -n " ${probe_host} " ]] || continue
591+ for dns_server in " ${dns_servers[@]} " ; do
592+ answer=" $( box_probe_host_via_dns " ${dns_server} " " ${probe_host} " || true) "
593+ if [[ -n " ${answer} " ]] && box_is_private_ipv4 " ${answer} " ; then
594+ (( score += 1 ))
595+ break
596+ fi
597+ done
598+ done
599+
600+ printf ' %s\n' " ${score} "
601+ }
602+
603+ box_detect_org_dns_iface () {
604+ local iface required_score score default_iface first_full_match=" "
605+ local -a candidate_ifaces=()
606+
607+ required_score=" $( box_org_dns_probe_count) "
608+ [[ " ${required_score} " -gt 0 ]] || return 1
609+ default_iface=" $( box_active_default_iface || true) "
610+
611+ mapfile -t candidate_ifaces < <( box_dns_link_ifaces || true)
612+ if [[ " ${# candidate_ifaces[@]} " -eq 0 ]]; then
613+ [[ -n " ${default_iface} " ]] && candidate_ifaces=(" ${default_iface} " )
614+ fi
615+
616+ for iface in " ${candidate_ifaces[@]} " ; do
617+ [[ -n " ${iface} " ]] || continue
618+ score=" $( box_org_dns_probe_score_for_iface " ${iface} " ) "
619+ if [[ " ${score} " -eq " ${required_score} " ]]; then
620+ if [[ -z " ${first_full_match} " ]]; then
621+ first_full_match=" ${iface} "
622+ fi
623+ if [[ -n " ${default_iface} " && " ${iface} " == " ${default_iface} " ]]; then
624+ printf ' %s\n' " ${iface} "
625+ return 0
626+ fi
627+ fi
628+ done
629+
630+ [[ -n " ${first_full_match} " ]] || return 1
631+ printf ' %s\n' " ${first_full_match} "
632+ }
633+
634+ box_best_org_dns_candidate_iface () {
635+ local iface best_iface=" " score best_score=0 default_iface
636+ local -a candidate_ifaces=()
637+ default_iface=" $( box_active_default_iface || true) "
638+
639+ mapfile -t candidate_ifaces < <( box_dns_link_ifaces || true)
640+ if [[ " ${# candidate_ifaces[@]} " -eq 0 ]]; then
641+ [[ -n " ${default_iface} " ]] && candidate_ifaces=(" ${default_iface} " )
642+ fi
643+
644+ for iface in " ${candidate_ifaces[@]} " ; do
645+ [[ -n " ${iface} " ]] || continue
646+ score=" $( box_org_dns_probe_score_for_iface " ${iface} " ) "
647+ if [[ " ${score} " -gt " ${best_score} " ]] || \
648+ ([[ " ${score} " -eq " ${best_score} " ]] && [[ -n " ${default_iface} " && " ${iface} " == " ${default_iface} " ]] && [[ " ${best_iface} " != " ${default_iface} " ]]); then
649+ best_score=" ${score} "
650+ best_iface=" ${iface} "
651+ fi
652+ done
653+
654+ [[ -n " ${best_iface} " ]] || return 1
655+ printf ' %s\n' " ${best_iface} "
656+ }
657+
531658box_org_dns_mode_configured () {
532659 local mode
533660 if [[ " ${# BOX_CAMPUS_DNS_SUFFIXES[@]} " -eq 0 ]]; then
@@ -539,8 +666,7 @@ box_org_dns_mode_configured() {
539666}
540667
541668box_detect_org_dns_mode () {
542- local configured_mode iface dns_server probe_host answer
543- local -a dns_servers=()
669+ local configured_mode detected_iface
544670
545671 configured_mode=" $( box_org_dns_mode_configured) "
546672 case " ${configured_mode} " in
@@ -550,28 +676,24 @@ box_detect_org_dns_mode() {
550676 ;;
551677 esac
552678
553- iface=" $( box_active_default_iface || true) "
554- mapfile -t dns_servers < <( box_active_link_dns_servers " ${iface} " || true)
555- if [[ " ${# dns_servers[@]} " -eq 0 ]]; then
679+ detected_iface=" $( box_detect_org_dns_iface || true) "
680+ if [[ -n " ${detected_iface} " ]]; then
681+ printf ' org\n'
682+ else
556683 printf ' public\n'
557- return 0
558684 fi
559-
560- for probe_host in " ${BOX_CAMPUS_DNS_PROBE_HOSTS[@]} " ; do
561- [[ -n " ${probe_host} " ]] || continue
562- for dns_server in " ${dns_servers[@]} " ; do
563- answer=" $( box_probe_host_via_dns " ${dns_server} " " ${probe_host} " || true) "
564- if [[ -n " ${answer} " ]] && box_is_private_ipv4 " ${answer} " ; then
565- printf ' org\n'
566- return 0
567- fi
568- done
569- done
570-
571- printf ' public\n'
572685}
573686
574687box_org_dns_status_iface () {
688+ local active_mode detected_iface
689+ active_mode=" $( box_detect_org_dns_mode) "
690+ if [[ " ${active_mode} " == " org" ]]; then
691+ detected_iface=" $( box_detect_org_dns_iface || true) "
692+ if [[ -n " ${detected_iface} " ]]; then
693+ printf ' %s\n' " ${detected_iface} "
694+ return 0
695+ fi
696+ fi
575697 box_active_default_iface || true
576698}
577699
@@ -660,6 +782,15 @@ validate_config() {
660782 ;;
661783 esac
662784
785+ case " $( normalize_org_dns_suffix_policy " ${BOX_CAMPUS_DNS_SUFFIX_POLICY} " ) " in
786+ org_only|best_match) ;;
787+ * )
788+ log " ERROR" " config" " E_CONFIG_ORG_DNS_SUFFIX_POLICY" \
789+ " network org_dns_suffix_policy must be org_only|best_match: ${BOX_CAMPUS_DNS_SUFFIX_POLICY} "
790+ return " ${E_CONFIG} "
791+ ;;
792+ esac
793+
663794 case " ${BOX_DNS_COEXIST_MODE} " in
664795 preserve_tailnet|strict_box) ;;
665796 * )
@@ -870,7 +1001,7 @@ load_config() {
8701001 log " WARN" " config" " W_CONFIG_DEFAULTS" " no box.toml found; using defaults"
8711002 validate_config
8721003 export BOX_CONFIG_FILE BOX_CONFIG_SOURCE
873- export BOX_CORE BOX_NETWORK_MODE BOX_TPROXY_PORT BOX_REDIR_PORT BOX_DNS_PORT BOX_DNS_HIJACK_MODE BOX_DNS_ENHANCED_MODE BOX_DNS_COEXIST_MODE BOX_IPV6_ENABLED BOX_CAMPUS_DNS_MODE
1004+ export BOX_CORE BOX_NETWORK_MODE BOX_TPROXY_PORT BOX_REDIR_PORT BOX_DNS_PORT BOX_DNS_HIJACK_MODE BOX_DNS_ENHANCED_MODE BOX_DNS_COEXIST_MODE BOX_IPV6_ENABLED BOX_CAMPUS_DNS_MODE BOX_CAMPUS_DNS_SUFFIX_POLICY
8741005 export BOX_TAILSCALE_IFACE BOX_TAILNET_IPV4_CIDR BOX_TAILNET_IPV6_CIDR BOX_TAILSCALE_DNS_RESOLVER BOX_TAILSCALE_FWMARK BOX_TAILSCALE_ROUTE_TABLE
8751006 export BOX_FIREWALL_BACKEND BOX_ROUTE_TABLE BOX_ROUTE_PREF BOX_FWMARK BOX_BYPASS_PRIVATE_IP BOX_BYPASS_CN_IP BOX_BYPASS_CN_FILE
8761007 export BOX_POLICY_ENABLED BOX_POLICY_PROXY_MODE BOX_POLICY_DEBOUNCE_SECONDS BOX_POLICY_USE_MODULE_ON_WIFI_DISCONNECT BOX_POLICY_DISABLE_MARKER
@@ -907,6 +1038,7 @@ load_config() {
9071038 BOX_DNS_COEXIST_MODE=" $( config_read_value " ${BOX_CONFIG_FILE} " " network" " dns_coexist_mode" || printf ' %s' " ${BOX_DNS_COEXIST_MODE} " ) "
9081039 BOX_IPV6_ENABLED=" $( config_read_value " ${BOX_CONFIG_FILE} " " network" " ipv6" || printf ' %s' " ${BOX_IPV6_ENABLED} " ) "
9091040 BOX_CAMPUS_DNS_MODE=" $( config_read_value_first " ${BOX_CONFIG_FILE} " " network" " org_dns_mode" " campus_dns_mode" || printf ' %s' " ${BOX_CAMPUS_DNS_MODE} " ) "
1041+ BOX_CAMPUS_DNS_SUFFIX_POLICY=" $( config_read_value_first " ${BOX_CONFIG_FILE} " " network" " org_dns_suffix_policy" " campus_dns_suffix_policy" || printf ' %s' " ${BOX_CAMPUS_DNS_SUFFIX_POLICY} " ) "
9101042 BOX_TAILSCALE_IFACE=" $( config_read_value " ${BOX_CONFIG_FILE} " " network" " tailscale_iface" || printf ' %s' " ${BOX_TAILSCALE_IFACE} " ) "
9111043 BOX_TAILNET_IPV4_CIDR=" $( config_read_value " ${BOX_CONFIG_FILE} " " network" " tailnet_ipv4_cidr" || printf ' %s' " ${BOX_TAILNET_IPV4_CIDR} " ) "
9121044 BOX_TAILNET_IPV6_CIDR=" $( config_read_value " ${BOX_CONFIG_FILE} " " network" " tailnet_ipv6_cidr" || printf ' %s' " ${BOX_TAILNET_IPV6_CIDR} " ) "
@@ -917,6 +1049,7 @@ load_config() {
9171049 mapfile -t BOX_CAMPUS_DNS_PROBE_HOSTS < <( config_read_array_first " ${BOX_CONFIG_FILE} " " network" " org_dns_probe_hosts" " campus_dns_probe_hosts" || printf ' %s\n' " ${BOX_CAMPUS_DNS_PROBE_HOSTS[@]} " )
9181050 mapfile -t BOX_CAMPUS_DNS_PUBLIC_SERVERS < <( config_read_array_first " ${BOX_CONFIG_FILE} " " network" " org_dns_public_servers" " campus_dns_public_servers" || printf ' %s\n' " ${BOX_CAMPUS_DNS_PUBLIC_SERVERS[@]} " )
9191051 BOX_CAMPUS_DNS_MODE=" $( normalize_org_dns_mode " ${BOX_CAMPUS_DNS_MODE} " ) "
1052+ BOX_CAMPUS_DNS_SUFFIX_POLICY=" $( normalize_org_dns_suffix_policy " ${BOX_CAMPUS_DNS_SUFFIX_POLICY} " ) "
9201053
9211054 BOX_FIREWALL_BACKEND=" $( config_read_value " ${BOX_CONFIG_FILE} " " firewall" " backend" || printf ' %s' " ${BOX_FIREWALL_BACKEND} " ) "
9221055 BOX_ROUTE_TABLE=" $( config_read_value " ${BOX_CONFIG_FILE} " " firewall" " route_table" || printf ' %s' " ${BOX_ROUTE_TABLE} " ) "
@@ -1008,7 +1141,7 @@ load_config() {
10081141 fi
10091142
10101143 export BOX_CONFIG_FILE BOX_CONFIG_SOURCE
1011- export BOX_CORE BOX_NETWORK_MODE BOX_TPROXY_PORT BOX_REDIR_PORT BOX_DNS_PORT BOX_DNS_HIJACK_MODE BOX_DNS_ENHANCED_MODE BOX_DNS_COEXIST_MODE BOX_IPV6_ENABLED BOX_CAMPUS_DNS_MODE
1144+ export BOX_CORE BOX_NETWORK_MODE BOX_TPROXY_PORT BOX_REDIR_PORT BOX_DNS_PORT BOX_DNS_HIJACK_MODE BOX_DNS_ENHANCED_MODE BOX_DNS_COEXIST_MODE BOX_IPV6_ENABLED BOX_CAMPUS_DNS_MODE BOX_CAMPUS_DNS_SUFFIX_POLICY
10121145 export BOX_TAILSCALE_IFACE BOX_TAILNET_IPV4_CIDR BOX_TAILNET_IPV6_CIDR BOX_TAILSCALE_DNS_RESOLVER BOX_TAILSCALE_FWMARK BOX_TAILSCALE_ROUTE_TABLE
10131146 export BOX_FIREWALL_BACKEND BOX_ROUTE_TABLE BOX_ROUTE_PREF BOX_FWMARK BOX_BYPASS_PRIVATE_IP BOX_BYPASS_CN_IP BOX_BYPASS_CN_FILE
10141147 export BOX_POLICY_ENABLED BOX_POLICY_PROXY_MODE BOX_POLICY_DEBOUNCE_SECONDS BOX_POLICY_USE_MODULE_ON_WIFI_DISCONNECT BOX_POLICY_DISABLE_MARKER
0 commit comments