aboutsummaryrefslogtreecommitdiffstats
path: root/netwatch-dns.rsc
blob: e205081f6804f477c7323e468686de537891b195 (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!rsc by RouterOS
# RouterOS script: netwatch-dns
# Copyright (c) 2022-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# requires RouterOS, version=7.14
#
# monitor and manage dns/doh with netwatch
# https://git.eworm.de/cgit/routeros-scripts/about/doc/netwatch-dns.md

:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }

:do {
  :local ScriptName [ :jobname ];

  :global CertificateAvailable;
  :global EitherOr;
  :global IsDNSResolving;
  :global IsTimeSync;
  :global LogPrint;
  :global ParseKeyValueStore;
  :global ScriptLock;

  :if ([ $ScriptLock $ScriptName ] = false) do={
    :error false;
  }

  :local SettleTime (5m30s - [ /system/resource/get uptime ]);
  :if ($SettleTime > 0s) do={
    $LogPrint info $ScriptName ("System just booted, giving netwatch " . $SettleTime . " to settle.");
    :error true;
  }

  :local DnsServers ({});
  :local DnsFallback ({});
  :local DnsCurrent [ /ip/dns/get servers ];

  :foreach Host in=[ /tool/netwatch/find where comment~"\\bdns\\b" status="up" ] do={
    :local HostVal [ /tool/netwatch/get $Host ];
    :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];

    :if ($HostInfo->"disabled" != true) do={
      :if ($HostInfo->"dns" = true) do={
        :set DnsServers ($DnsServers, $HostVal->"host");
      }
      :if ($HostInfo->"dns-fallback" = true) do={
        :set DnsFallback ($DnsFallback, $HostVal->"host");
      }
    }
  }

  :if ([ :len $DnsServers ] > 0) do={
    :if ($DnsServers != $DnsCurrent) do={
      $LogPrint info $ScriptName ("Updating DNS servers: " . [ :tostr $DnsServers ]);
      /ip/dns/set servers=$DnsServers;
      /ip/dns/cache/flush;
    }
  } else={
    :if ([ :len $DnsFallback ] > 0) do={
      :if ($DnsFallback != $DnsCurrent) do={
        $LogPrint info $ScriptName ("Updating DNS servers to fallback: " . [ :tostr $DnsFallback ]);
        /ip/dns/set servers=$DnsFallback;
        /ip/dns/cache/flush;
      }
    }
  }

  :local DohCurrent [ /ip/dns/get use-doh-server ];
  :local DohServers ({});

  :if ([ :len $DohCurrent ] > 0 && [ $IsDNSResolving ] = false && [ $IsTimeSync ] = false) do={
    $LogPrint info $ScriptName ("Time is not sync, disabling DoH: " . $DohCurrent);
    /ip/dns/set use-doh-server="";
    :set DohCurrent "";
  }

  :foreach Host in=[ /tool/netwatch/find where comment~"\\bdoh\\b" status="up" ] do={
    :local HostVal [ /tool/netwatch/get $Host ];
    :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
    :local HostName [ /ip/dns/static/find where name address=($HostVal->"host") \
        (!type or type="A" or type="AAAA") !disabled !dynamic ];
    :if ([ :len $HostName ] > 0) do={
      :set HostName [ /ip/dns/static/get ($HostName->0) name ];
    }

    :if ($HostInfo->"doh" = true && $HostInfo->"disabled" != true) do={
      :if ([ :len ($HostInfo->"doh-url") ] = 0) do={
        :set ($HostInfo->"doh-url") ("https://" . [ $EitherOr $HostName ($HostVal->"host") ] . "/dns-query");
      }

      :if ($DohCurrent = $HostInfo->"doh-url") do={
        $LogPrint debug $ScriptName ("Current DoH server is still up: " . $DohCurrent);
        :error true;
      }

      :set ($DohServers->[ :len $DohServers ]) $HostInfo;
    }
  }

  :if ([ :len $DohCurrent ] > 0) do={
    $LogPrint info $ScriptName ("Current DoH server is down, disabling: " . $DohCurrent);
    /ip/dns/set use-doh-server="";
    /ip/dns/cache/flush;
  }

  :foreach DohServer in=$DohServers do={
    :if ([ :len ($DohServer->"doh-cert") ] > 0) do={
      :if ([ $CertificateAvailable ($DohServer->"doh-cert") ] = false) do={
        $LogPrint warning $ScriptName ("Downloading certificate failed, trying without.");
      }
    }

    :local Data false;
    :do {
      :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \
        http-header-field=({ "accept: application/dns-message" }) \
        url=(($DohServer->"doh-url") . "?dns=" . [ :convert to=base64 ([ :rndstr length=2 ] . \
        "\01\00" . "\00\01" . "\00\00" . "\00\00" . "\00\00" . "\09doh-check\05eworm\02de\00" . \
        "\00\10" . "\00\01") ]) as-value ]->"data");
    } on-error={
      $LogPrint warning $ScriptName ("Request to DoH server failed (network or certificate issue): " . \
        ($DohServer->"doh-url"));
    }

    :if ($Data != false) do={
      :if ([ :typeof [ :find $Data "doh-check-OK" ] ] = "num") do={
        /ip/dns/set use-doh-server=($DohServer->"doh-url") verify-doh-cert=yes;
        /ip/dns/cache/flush;
        $LogPrint info $ScriptName ("Setting DoH server: " . ($DohServer->"doh-url"));
        :error true;
      } else={
        $LogPrint warning $ScriptName ("Received unexpected response from DoH server: " . \
          ($DohServer->"doh-url"));
      }
    }
  }
} on-error={ }