From 8ff8b238edc55b3d3e6cc223bdbfcf6eb4247b16 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Mon, 29 Jun 2026 15:42:35 +0300 Subject: [PATCH] Backup for MikroGig created successfully at Jun 29 15:42:35. --- .../20260629/20260629154231-Binary.backup | Bin 0 -> 177026 bytes MikroGig/20260629/20260629154231-Config.rsc | 2776 +++++++++++++ .../20260629/20260629154231-Diff-Config.rsc | 4 + .../20260629/20260629144003-Config.rsc | 3693 +++++++++++++++++ .../20260629/20260629150002-Config.rsc | 3693 +++++++++++++++++ .../20260629/20260629152001-Config.rsc | 3692 ++++++++++++++++ MikroMaster/tmp/Diff-Config.rsc | 2 - 7 files changed, 13858 insertions(+), 2 deletions(-) create mode 100644 MikroGig/20260629/20260629154231-Binary.backup create mode 100644 MikroGig/20260629/20260629154231-Config.rsc create mode 100644 MikroGig/history/20260629/20260629154231-Diff-Config.rsc create mode 100644 MikroMaster/20260629/20260629144003-Config.rsc create mode 100644 MikroMaster/20260629/20260629150002-Config.rsc create mode 100644 MikroMaster/20260629/20260629152001-Config.rsc delete mode 100644 MikroMaster/tmp/Diff-Config.rsc diff --git a/MikroGig/20260629/20260629154231-Binary.backup b/MikroGig/20260629/20260629154231-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..2d27539d3b90a80effbfde7eda3dc7696fb49ba3 GIT binary patch literal 177026 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


=e znu+J7yI(b!W!JhB5FENydCoF znPnVl6EIxe@5sriWlV8d+F!FP)7xU-i0I8d?F7~xg7|dkvz6qF>!l=BWiIe7A{qL1 zp&3dnA-2Hr20oP1Hj{m_wm7{uw(ls8p`=G7FBJ_g14LkDo>%#^RsgO)w`yvToSFdr z^HpGsJXu@vGeFxPZI};Q>FI0V(>kV%djo`(Kr}bG+tYHUHQtoIWnKhu>dqtODMHHU zgQrRhWtF)V$f9s&WVNQ< z%;Io_s@|>^$p=V+ZJ#a!uh7G$LZZO+*MHI)Xi=(Po4$=AvvODYQoT!Kn|sKI=J=zfpomrcSW3tS}$G4NnjFyG(-_ylBi*j=TZr~ANuY$gQ$ zHxt`O{AQxlWHa$~Q8D9>JV&|i&h7bZakXLZ0(HH%Yu9C;L0`Ac2m?FZxoq0{r$Wz3 zyH1&x{Iq9J138%Pq6`?Ljh{!n4E-5SvIB5qGTwr$@D{t}pFX(2`8#XEOqesaZ>2ec z!V_KmE6iXX9BlmS8;QefbJZi_K*Oy;V3wu!zUDa@i}_aMzAWXO-Wqjt&AroZ+7QO_dugPuD8s!2hA zxv@<%Qf0Yrm(yOa7!78vgNyVgY!yFNl88rpQNsew5Q=7Wr*P=Dm?7&iCBFay7K(|{ zF1EZYJH}-*r)B)!DR`^dso{;p-J&a4vl0lPJ()E2Jv#nGO`Iij12oSPZ_9n5AhSfK z_^RXH$yYn)!`^5iC?iK3SxuFIeg&=gHv!j&tGqfrmDA8v97SDh^SD1#I{}+xunjqE_rLX8 zO!;qLjGLcRzKg|T`?x^C%`>4+L5Yi6@c2FBdlmhYH^rTLVIZuz3p*DIOp5m9A@aEzxgK69@dz6u zOrIOF?4j@GTuIkL<-0t%Fa|g}BI8-vQije5>|Brj}qfKw*TD(~~=G;)5>rak_y8v&?dTL)xapiWI>^kY#-pBS;O#10#)*NNq z%4`Y-;$sY2Ei-J#ly~GeYz=b!V(U@nfpF8xc(yZYR)KtgcBP{)Ec6NHK(tn99Mi-u z8qP#Knt%o?^APk-t&W05Qu`~e3tvn&ryYoyX1ITE!6WsO(7I+c2*g!0|DnW(0wYxfNQk1xj#K%)B|fJ=52V|!s}_(#Z=$qmkBs_ZZSlY zZnSv#I;Gp)nm7pLDG0 zaL0PSo7vWU;Y`i{RrYs`;hhqsU2E*o*efd8$ zJ?Hxth6}EPmjQ~pYHdfPJiU8#{$M%-Sqa9`1wY-V63bK}J{RoN%Aq2%`E&Z4C{ni%ih;pNxM!_5)MsbgF~*-dqE{Ndc3J$V_dvavB}m2s`@$v{F^t^^ zvX9AxE>Jhn{6fiyR@<_^I&%gC!Asd8nrVybY_swvWHlsIV)CzwV9Ms`d+`EX5J8BN8ls>Q64Lv*Q25v(_`R|e+> z-choZhFe!Ont&;RsBx4Mwl+M+;@hgaVoit zyk^_>w%_up*x?rk_qTC4E6RXQcji)-6&NnkZGTq2X5XgeqwnIO&|%X`FH^rZ&KMY- z9*}eUP^g+rnphp=JHH%Aw@Oqf+ot+BSY=WrnO{)bs|fr!f5~Ae(dAEB$x!Khw7-SM zPIddHrcTCc`~&S5Fwok(c9;ykOmPrz?$?D1M%GTr!{(tro0tDhU%xqRP{$r8ta;{m zTjn|p_SYDp&Y>=FVLn!XR3dz0Cwk&OAE3Nh*`#%V{ryLg9n@4os>09aNigl#;F;zA zJ!-B|NcwxXuWdKMSwQTD4b0c%?>NCB;}u!anIc4)v|umiK8EWbA58wow)a61A4z3U zuvJ+Vu6GnTOuP^dTkbtm*|FqJ3c!b?0C~2j`wQo=H*Z56-G&ZciyCOaxq!K~!0`7N zi=E0Q)9u6c1^TJOh|qL)d97s_Wu8I4xx8Zmcfdx+4!Xj%#13qGlN=)^dKOPJXY^ zW*OsyVbWq}DMMvE`-nP|0-_BY($R0IjAm;i^okq$eIHA=GpYXvk8q*)+RJ#1!B9!` zs9^JS&hL2>nRU4Y3p05du2Kl)#8?URE*bp3im)?&Q=$O+Nc&Uh&wQKYIld9@;T87T z0>27#m$XuKEw3Gu$;?2nzT1AKt7sfS^ zWV#shgdP+dlvQfe(Rq3L*_B)ZR%gx81yOy4?9Yx_4Xb$;qp4ZA)lH9xF5ygr6IC>#Wpyn}dRxg2HJlb@Fan&?$=o?$eKchx2|P`y|-SwXO-$vnRw z?>xo9Pq2qv(QxLVD@Z=B?K9A8(t*BK=utLvs+kf|`uh>xg5PpWXnuiZS5k@(b0Z(WYsM-r)+j9#<`-&(u8vnpHH`6*wgzC*y zS;GFVb+rw0P146Xn`JHflH6=L2zfE90jM!@5Y&6@`e0RQJu@MQsU8QbeY3m500ZJ@ z1G?H8oYF8|gw>9vQKB`oip8fv48hHT@XOYM~ZlE`dLF4y;$h5OS}j|VSKU)5m~pHU1MuSa zPib-nfzC`vc{@M~oYZ4%;EQi~!GLNuGK)WWSx4Hnm*2H_01oC7_e<^m z#?OV&&y^?Z1<5QadiNhpl_>Pod2?EZT{ajXwb}a(Cnqa2R!B77YDePy=b7xh#)|GX zM&?7~aeUefYYj_$md`83_=Wsg%~<@k^eSf*+iK2Q*~#X8{C|FT2-*Qbx~62YG%HmT zMTH(4pPz_P^zeuczPcs1vrX$IX+qSd7acG;v4<-a?f?t!A4t^i)1Ku4X@qsqNChvd zR(0woHJXi|QF>v#ce5NFboB8(J4!+=xF5JxW(trFs`onh$J;!{BWn-Iombu-&6E9bWym-2~1FlM!p=Kedt*x5~*)#+M z?dxZBH+stsn|@UV`%1<)OaN;)3@@(e4O{i(093ph(Cdb^*EILlexHPZs!1lfDMClh zCxvNN`gFaR;9!i>G37X`Fl|8VR`&d#c!mi9Ejk^+TBtNbHsJlx?#9{870w8}^Y}@0 ziPQ@j@aQ7jI}U5Fq`3n;#C4gv|pBHCY{^ z%hEejYg4a(R*Ykf^h>v|L^?vN=&N~IMa?_QEwAmbv^m`i+ z7iAkXC)R%kTPWroQg1HXZ-};GMBA*$CSU4MdUh!h&Lu-`|XYe7~A(CsH zIr1@%FS^?sD;nC%K}_#(r{#C?KHO1WX73VylW=e1cdwSlDAdKT_|~QrQ8ufLQ9vN5 zU&kgs7|2lvLhA8jooWi8(-#7i8x+SI9Q`xP^1lXTsZ}I+FSAQDLL-zXrBy-Yue-R z%mBfHm$7h`MjEl2IS=>jA?){+Y5SSs?^086P zi5j`TsX7|6tNYTK{)~TyWC2D>+arJ0{P~y(Am?dCw6Jt-A+OG;GcC6;_Q@BmN=pZl zJ1BFSXOn~C%q9BCubB?`TBxP_lyR_?f@5eump9$T(qud+y%m;7(@*907r+e0&z{lp z%iX@~P~Kki1_mwQ{IQX;)EId3QV#RZu2)K;aCH&!g9Onf`M;d~J!x`6=&%|O@yTiv z56#B?Xv=fpx2)M^pQRf{ps23=S_Pq1PL7O9Mioi(xjH6&9fWtWH9NOn-4TpJ9Q*M4uhyAxT=cW1eBDT~yGA7DtE!*g$T%QtKPtR+=zF^y? zrGdz(SS?Tl1<8U}h(Zd#nTGs~8KM-SXrq130?D$LTlrpt@q7$YL(%T5LQ?aa*km`X zsdF>zAVa}%>tq1B*}}Tz=MG=1T7Fr*n<<^6h183BMUPE>^Wc+iP$}x`&nEYo=dUzy z9VwXvJ@;UBJ)b@C+N=fD>aHw4{NMIF`@yc?|IU;t8@wkg)R+K^uqiDV1f9=1^Aa=G zsw~2;;hQ6gzY1dE{tM@R@(CMH7KuM9KwLYpbY9eCKcOn79B*SZ2$(djRJI6X?0mSS zytD-?*J`w!FtV|Dx`Bp z9pzqZUGcIn+kO`tJ6!RlwCBA1_-sLYHVhvj9D$e})lh>m3d^z%2M~k392Ar8tY>1; zgGieZ)7%t0=_ss(T1+fbvKig?+1KTWS#i<*1-NsN>_V(0Gn$~bzmAOC zzGh;$qUb!F&@g0t#zbyN2~<~>*kWNIN(6*XLJ{~QT=xU=X-*Wh)HPoBE>k?!fwypwhxb)d&hJfl~VCei9z7Y^pHW4KD8e&Nd!iL>9K zB9y+Qx2h1i%=IR@59aI<;;*bLRy2++KjAgW1+*S@SbswPyKv`*p;)#Cw~hD!mPQmU z*0QG|G}~s-yWy;bfvb3b%mV*oe^;^AVTs9cC%DvQXJZ56Gs>IbW%+o*|KrO{wnG?0 zOgwfkJFF;QzgW4~sz={pP-4$*YAT%>EeSoWN9T*`iSDuzFY@e zDw|g|uFa7m>yJv9@omN%Kxqa0Py9;=wTdakO7P3%0%VtZW8wbUoF_16KNAIo9Tprn zlvFRPJMqSz%F36GJY^3>5O1yyI9R^B@v$eL-;ro8z}G^83RfN>XSj91B2o2=!do|7 z{+5<(ueu#OJ3@^^Qvyo7`KVtce`e@9OD-tM(C)IFHiYoA;7m1lfauqxW%CTUpb15i z^~8kNO&w?)M%Usz37UXfwf$6c7)-;)5v$Osq89F+4~AoBhmqoM8sRb`IOGV-xg^~h z>?kX5+i@&~Z3#huLv7GqOZ5Nc0)EfTcA$JxCltn(ZEzH@u2osrxOAn=^)cmuCCnqx zG>m7Q{u&UHZ|-;d#(_DQ0RmHZmu{&tunL)()#LS6l!5aa0wxmc`_u??(l9di++L@^ zNgqwiD!oLZVT3y~iQ&RaXixQkECe(hgYARmuUm(ox$M+Ux6m3}q8T06G)69vLH5Il z&7W_5c9hd6U7fM7X&2=zK+JPaRG)rOS?u6sh@1nHwet#{%*Uh`V4Cen?O9w*$1b!=BDq5%0ZgSZDT47I<@46i)(T$lp?wzm4hF*m zlMi)6kX4vrLI4SYwGu++pAiXaW8HsnuFaFBn=*xX{$XFgNOWdx%+S1AKYM6L7}tqr zP*TX`k3+bW$J*Khhb2T8EON}t%a{-SfZ9f!==;T+xY@Z5W@Y*|(*{Wrv07cSq9EMi z0zpLAm_Fy#?A8}C+l!O#$> z03^m!2q*`qm5lLlDO_wn((6a#O>TZcWtic=7wrcsQ-vPNh<$i`inulClS0Lt5 zQvz1j!6JI*XV8Y)9e|>XJ(i8`j*r}Xmy?>@Zs0PrR^3F{1Jo$oq>~$EyqLN~$}1gr z*1=LcTfEvC94;dm+n$?3u?v0bR93Q9!C#D81B(10kuaty>vYyG+OM5Wetp3^jh&!8 zo&O4x_Mxw-Wom|7<+gy}_G<23Vl1doiSV29k)vRWc^fj|6eNC|Id4TXCRF`8i0ng% zd1z(#hd$Fo?B(>p08#V~$)M&+GEKUGQmmfHT$GsGq`hgXX{NZdbAR{qT?wE^Bz;Ev z6T8bm8jIrPRK6zr8mr>AlOX9MpisLWT;QfG{?T)GnXC_Z;p;0Y6`|CA-x(b??cD#BUm$W(cTKkqhdzko6MPjh=AuqYV|mKkU=L3^WPBj z>+0}&o#r7YkvQ&*wrBtnn~o}jGCx>X*e_BN4 z<+nf7uqw<1s#)jOc9xc;gbH%YUVar-UErXmdmfAl#FM2zj$w`_vid3h-}VrHsV-uc z9x9JCMCQT~jST|J<8Z$;f8Ntqkv|ALyy1Hqc|{A+Th9UsZf$o@>jwJ`T1$ja;UQsa z8YrVHdK1w&9V2yqi%Eji?2Vykm0Q`gWcqS^>h4@;Kin~NT+%=)pV2dI04*JzjQsF> zoMLT#aAi+^wIc!H-F3Ze&frNk7cOpVm!vO+)c>Ub0bft*-5$L8p;Oigf@u$A#(B8G z3J!e+_$38{>PYR0PPRruwe|8pMNy7#54Y@s>}!OmLivZ9|A9I04%=(v=7o$M8p`N1 z_5sTf+tYF?!z0;v_U!!5ZFaBaa~U;H6aEv%Rqa{FyomZ2gp3p@#3exB_+phAb)Cs_ zYXG)XP$mhI&)wQwGAyP{PT?SJ%JS7HOH!IkrV%odS}U8 zXG2AUSnWi^SgzOf_+4Npx-FAac%fsVa^oiHMN&>Pr=({ZMNK7(A8OG{T@*uiNmQP4 zA}KAj1D1K#5{Ud0@G=fCRi}(T+v=wR_x;fTw1o!!bK*vQ1G}N%S$87x<`Ouk_^Wnl zO{U{Z{b78N$o3!+0nOOe#NiRfDa>0D%UfvPj^za=GJf9{0B*s9_ds{f`r4Ga6+fC4 zN}cwd4Sp*JFqdcA)cI6O7RYpKbiZ`vUn^Ho{JGyqZ*9On71B>1mL_|3T!A0IP-J@^ zz#4qKGwzEtkGS>suYMruH{*!5?9NF*!uP7Tay8dZ>Ktrzw5ZwW830`{S2s0TSwfop z1*j1Nic$ZgA1bJ4wh_9wc4Gb7XWMlGdQ+}O>Sk)CUd4tN;Z4np*d}1^t?M_yEhOZAYEA3T zcXZcJ#Hei?jKTXRnzApv3e!s8xe2|-d7HO!oVR*!@w;tf=b=|FH5C^0T{9BizM^`T zg82Lf*3Q&pJ2Ld6<^3pzW@1<*Xhbh+S5}V?aUnlzT1!vx>WW~)CkF3kR$6!dal-_B zPlngq*r%AxRIHG;xNyy}2R+E?6ohsRam4m#5y)7!hZQRUTDA>Sf~_%3P$5AP;2MUQ z=zX&_-Q;+7%6Br229G3L7HvCGcfpTNDFoGMyMatnuyx}}S(b&Pe-M2ZrTPy}YP>dM ztiO2QsvO7;;Sm5>0*QX(e%}s#7+UxUiLw^t#>r{Z!6mt&)T~JhQ>{)F)_&2B=m7Og z^s;EryC8rVE#>3v5w@LY;SqzBBxnY8%~!bryrT*K)NQ$@(eaFak1EG)?(fy$;^dNV z4dkC;2MzGv7Cs*gKb%;HXE7?Cc=TW7d$-hv=j@X-dVo6r0`R_eY6~u+->Iy()`p0Y zj(~~+L8CN;M~#!1VVg@2U|-!3UMKCe3r+|&-r9F4a2)nTp;8&j$)E%aDWj0^3&W z{*8BNgWK_$@G8BtB2=Chtb<&(lva^Yu|`)-Z4iyMth)duim0Z}E&;HUPHKv07zRD& z!$fG9BX3vd2daalbuTmQ!Q5qRIf$+_hy!UEV(mYhHpL>K@?X@c_8P9w*im>x=+-2~ z-zvFW3@U)~YLLA*EFD5uL8S*;jqG3WLJWeiw|ALrJRZnS5E179G&@)L=wD}cj3kfT z1x4QMS0Pz>L6Zd!56OF{{W8fZ`ub$Lq39K&YvsN^lYl8@Gn2WSB`rA}4xz*6J<$x# z!`6{`QPR}>l}3i7W3vYWN)#ZTd6_8z{Qlm$MPg{$n^OcHu$W1FtrxmwrC?=yZyq}f z(@lQC%%cHo>Y4j@OLo#yXV}VCs=+27o-*vzY2GC8qs3BC94uw!Ym!o;Kw_xBDo%7dm%ee|Dk5;yFI=OqHaP>w+1TUa3(&weA&PJ61<3=T*=ZnNscE- z9Oo2DkHA7f)ySiw^@k(_=B4Kk(Np+5Rqn4a3qk9fLr6X65))P5IO z8MORtZp_sYgyMuNZtZ(l6FDd#lzS1v;}UB`2BVA4(hpK{3E9V3tL$FM&tIW%2^w(! z#JYQ5yqs>>p(8}44}oM?bw#u$8X|v_>JPdD2oUY!BX`#P2UjAMs_ExOTZIH{TJ>tB zy@hdr5G!y~`V$0-S!J`oQU5M>L0#y$W2y~uJU~qsy7d8uz~lNgjZ45gyk49WHX~6}B`oe&KkMVMXA)g%KyxPs5cZ>4 zr5~gAP9CzbINZs2b_czL9dngV4t5zeybak015ZtKfnbHhZ9ep0_QNth=YGqU#gwKX z9%dxX({QI7oO1?%$ARpI@e4<#`q}oo^GL0AP?!?pxcLK>`wtvV{yaUy(g$G1wwTfe z`|3$=GcdpdY|NU3Atgo`_V zl#Z35RZPaZEL)39w0Fg}+L&dC99iH|+&AX*7%Ufn2R~3p;ERLzO2k?&O{Thr3;-8E?E!-#?>M#Avbc0gy#>EL_+S)MD0h%JRr3mdrD>S|nQ)lI-6W!c z?BtR=p@VWWnG-@+$;SvM61eV`i`!_y$u1VUF1W->)p57NkL=C90y2eO-wPQbHj)BG zXp;4ca|7jhL?*JUh|#o#A*VAo%#=e1###=3qy0QJ3z_-+Ya#Ox@zmGR0^yXfg9o+; zE9Cm=%ZLn>C=k%kpCOft-QbEWXyTLvhxnon>wNNA;Z-(V8g;6`xWwqV7>x}W1On8^ zR3vC`1Y`ic(PQIrVIO8$2>bJPzD-wlu(C~133P;l$<|$>Og0X}>^8K-s!kO}qHat zTtr!Va<5xE%d~X^<2WVCs)@Q>I%7UIt8*MZH)*e==>d=`<$g8nF7OAGDPXGWlf2-*4et(P+tGapP_VT2BWw!foa zGzTlwh*6^8s8BzfMsbUG6+5rGA-^u$$}YLYU_sJaq|)Y_fV$y_KoP3>puIVKknj&+ zMyoIn8VM(a4MtB$hFbwQQ!Yr;3`E0?!r~3T1V*jP4VRP4$H~T~A;jkI5$wp9)VLjP zXE?^G9bG6i-$U4DZp_(g3UY>zehGuGc!YAl$X$V1rxHNom3-kiV=W)iWlM&0N|d+& zT3l;GVFAatpL53*I0L;yE(w4J|A!_88b^zMOq*l(Hri363y}Hq?h%u6EBgc(Q(>t} zt7l$4(j`&Gyi!yFMQN`{b~z=0;Y0LyL0|-ns(n^*z$F5>ajenNLOj zThgI6K)$4`j%h-ylCl~ZmooX`t0fw0sx=#I(g)xzx2irvs0f|w)9idy6nBcOy#;$A`%?dE(hy7b((%QJ@$SPzP{w`D+-{F;*+763{$BjfDn9 z-(Sqgr1vtiB=g**B0`_{M|Ogq@?NU4ddO^@z-IdYzOJDt;We^BAZg;s-r;nX6-3o2 z#O|niA6?r>wx1^q7KiW;+$ByguNt9Ju`h$!;$$H8EWU~>)wUG9T=S&rKgEz#*Jr7X zID79GiBGFulc_-kS15B1-OdlKp_Hj~YAq6Gn`|a##r2tBBn^bV27Dai-#_L)Y~nk~ zBYm`JNn<=JYwXz*S&bVqY8@D|c5EBJm2Ij<-4n`s!x4Cvs7L~NFwBH{t6hr3u=Ngu zeflX{DI)&N2;1Zqs6tiuEu$!BlvJClSop=#9MM=sZj(XltmcQr3VShKScESa$1hvL z%qCt8Et^jB*jijtU9n~F{cVKWCX*#ax_9e?fGi4@mq4(Rh`Z}M!;Ku(1p z6M_ZAmG2%~n>y|_07gFlnnJEF4nKQad35c@g4)|z$7}NXn^P=0{%j>~2+!KCjxv~L zmazG?Jw`L*61Gl#J@Fx)o%Fzx?AopqiLvs`4s)7tK*|BY+`&+S%)+4jme$UbYD^P@ z>Df;bCpZzkC^vp_pJ14&{fB$ghl>lN057wdvQ!oW&etFx3L#I4eX^W1rNeURGM%Db zN#f`@R}oCDN_4~ShFhtiaaPrTjvSsqTVN_`)vXIQKl44;9G^0^Kjz|jtW+UKky0AF zkTm7#W{&BT;)+@iUT`A(K_(1W#fTI|I$dHOmF?NMWX>X-T)kR`h?LLSUi~;|l`Q!F z)E$rMkg^?-kA5`0lx!5a9fD`FuZ9oz>_L;~AEEzGuFVTPaI&mv$;&lVwu{=q#;Ap3 zdb>E+@pH)0_3{#iLq!Q>yWgz^*X&kcA|IjVtjdUs7Pz&SKf+5XOB^_>k?xx2bT@Pg zph*4LqJ~Ud26P`obxkcnil00$XyU-(w4Vm{(Or1$GcmUhjq{@;>#@`EE zReF#fOyexQSZ~o=z#`)tqaNMie+{kNB-Ze}S_7rl$irJ#U4>yi<8&`_k!9mr*MF?Q z{kX`M*`gOX z{yVK7`ZrUS%cW;=Wsd-X%?(?xZR$Tph&+EP#2c6OdX+3Zu1om0g0X0`Y>xd0 zu;Z`Y8662xnj=>#@b2zNq?rV!y`yGRMFP-hK}E9|Clv7uxx=~s8=bpcjLb3FTkpV7IsKtGb-M9?df_-IZ39rOnT&7d`@;frl2 zH_ETEyY{?bpD6LSJCmueky!yxNroUxlUa8reCCn0_Wk^We|Wn_llSad3?b!D)XaT~ zL7hgUgMPlF_;}q}v2{NeH#mF; z1$Dp}TT*}MV6LBom2~}qxStOgRYJ01u9P@I@BoyzQJA;mPy5S^Sm`%b^SW)d8iQwf zS)Hy0=|HviI8I$2C1Qb1 zoug2+BQiqwsA~67QE#j4Cp@L5-m@Nia8c2(aGotVLI1gIMS92AsaO-Vy)rde%(nITu5xYGu2k8~0jI9X2fa`)S|c52 zB->8fxPkNf+n&tXwuO$aU4TCt}s?L*4%G<4B|QyGOO**=1Jv8zV0rOhYH^n~nx z{@_EsJr@!n*kJnEV!4FL^)gq(@5Zw)uemztfB~<1Pm!yoigvOXK}hx}Y9wr_d3nwS zV@(wrwNMz(W`Yui>$f=l2R;$qq67Md5{jnOMboRtAno@_T*Wg0l08I)^4BwLooV|A z_N&F%POjYKr0D3sMpVw_04D6`&%B8ze-tDqHDGRZUeKkU;EgCHYE|4wtdPIPh9lS? zb6IY))b`j7&Z9(hk!^}9!^!k0tlFW&Uh}UYYO=c>yE+p*?VGM>!8}}rSqKZ*K7mD; zv@AyM0a(NL*E=F!e@`CsyX^nTZSD&%$x7eSP%3eBiFl5{LSXKE(7J5Ap0)7zTmQ!-eyBkP2)xT^v<%j5C`7a6wKdK|UR< zxpaZlr}XCF%g^!R>CTR_u%IzBeloxKT&}1y*7*5?Evc!mBGF1wZe0=(4uhJ$fVYQM zTce|aDWLtlKxJMEFVXIdEdC7-FZrn*i#k@ost|;AZy@u1pIUq#{Dsm$tDBXd=)4_o z4|7`H%x&=_#nXkS^LWp@G%&}>Jt^eI%QOdW@@qgrLAw(-s9&zkiT&C}q|fz9_x@9l zk;qy4VSZxl&+<&SNN$!^=5BW0KW44e!jRPk9>j3mj#41M?4>)$1E`1tmO1BRziqP6 z9M5wFx~VeC_Fk!n|BwG7Y(-zfem%|yVrssNcfdTGGz@Dlq2 z*8pcDzF)t?@uBTqmV)A8r9U^AI64f$i;n1- zT5_#rQ(n0IoK9zi3dLBG+UlLA`2PSbGf;^Mt_l?-HTz)i3;-oVJx7E_1HahjpkD^58Cvr>E0TO+pSF>yoJrj?>T2Ua>&nNg&aU8&mMi z09lF*Cg60Yd{yJm3gkb9lcBvF(;N{K~$+oNNXw}058hrJz zt?Mr1S6j|Ay6CuolqsQ=4J#BixcQ?^pB%5#noI%?(F>j^G5TTM#Q6H9Ki{|SELTZZ zDY$F~K&42KF>2j1>*v?dv}dV2}ZXnEkRtJ^Ea)k2_5 zTkm-Fkj~Th%=n{oTUv|i zZ|BG{%YmsgPq9VM@Y~I3HP5{5{{E_aZx2;3|6hvLlpfd9 z5bI{*Ezg-I2LYLn6>uEaWkIajK!XWSwjT_5ND%*RlH5FIAF)&s33YgKpo6|4X5~4L z=cs0;olRDgEODzgnf& zWiA6BqR4yzrx+FZl*=n|GELa{B$03CrC;V@(zn12!R#HO7M=mV;E>uDuuVV{oGbwU zKLEh|7v!5S5fLlfYcz|ec($0x%+5$Fsmn><7%qD`(zskccqnSFiLwX{yWSV9Vs7SMaUgrSX^OrVSmAvLEMVs^T zniGu<)V=Xdn$Leitio1J>M%8!MzP-Bk z?jO5C&xUYUSbNBvp1ibz;9=u%2fCLj9&a5ve@hyrB@x0OReRag_|Y#OUr|D4?e1@H z&d2<-186_>8cn-EFH?Z}{C0ZiYjgR|9+||)d4t8x;BtG|D@qZ z@4!M(A$RyqQB>&Li~~<22_;8cT1_k@J$rEyM&v-F!>Hf`Qb+zZjx_73#uwe*-}#@E zKQvVo9ZT-q2V*!%so74dXrD1Lk4D-PQjJ#-l}s)+CmsjI>E&C1ri21fFsP-6JUKi} zn_h_%yw1|!GjXn#jS6&P;yJHiO*(pL%7;qZmTKbvy)+XgD*c9fgOpnqI2Tpk*0}1~ zIc=#To$sPx)r7R~4}7IRqOEh7twjH~`PJFWcScX0<<8vukLJs#GdaRf0G+GVy)i{H zt08FQjssVhBbKWRDW9lRqV{3%EB6TtjNm}Rx;eI23rq`R>y;@ z7tkI*;s@y{Qmg?*jtU^;P1@L@4E>Y*OL{Oyp}Jf2%#BSc-9$Q32gEz7mOb8 zX#%s4in(8rjR({WLm9i1&mfT{eM2JFn>l&uSObIYQe}JyYqe?B%`V3yDK_FmOZS9~ zx&P{-3Tl{AA64AYyV(ouI$p7FEbzWK+WhqtI$~R(i@d!Z41@j5nnQ2K8jM9eZ@WgF zLqs~)zVn3&cK^^UrdiZdd}X*QIxA(cG{aQ}<)VQ0GR@cpXMPy!R?>;EbRxEF-1A5oLFwSl1Z|M(T z6d!&`Ki=@7!Iy8zR)-zkzCLC2wqA73ZgG}1wq$@ezU|-SO0gyMxYy;Uw=SN5JP5F$ zlOv&kfAoK@M|8E5TBw^)-c>iI%+Cv6y1|JBT;Zi7gMd27l=6`9b;I9XRU!Ulc7 zi3=W_)3V^u3*7mk)LMTigoBI7ZsM0KAr>Mq=gzKEm$P^pg z$mTNwgq@c3F(b}~`5fZk912Nb>LQLb&wU=&5Z`M;8%^@Rba5E2`e1=c2&d{$t zYpVTW!4{vYgLOY;&eqQ%<>t&m1p-8($FUL|xoY`QAXFcsMSpg~rLlvot}o}UDgg&{ z)LY#$OhRh)Z<5DJ1b{%H1YWQffnP+EoQfDx=YOROu4FWJ;SkuS4eL5Eo~}{@3RfY{ zsi{Xnh?Y8QRtVhB<&MPc>Bs&c3Skblqf|a0^<%MeMA?QNMg-#%hRcifwFIQV?3k25 z*1-ktYYgj6DXKJEWq45d3>TrmP0uN7WOU#BX{PBgC3ZIrTF9?@o`FKGy@qq^XCHGA zOS=afjb>>$MQ^ZEnm3S`QjolQ%7BPbG=T0c!C3v}eOu`S(zrH|LfeTWFP!w5-}-U3 z^5zu=%e*Jj-f7K6ljKqX+t0`?4cx+$H#0o?>C*lu~?)IbU)6 zE0{CDT8uM#h|M)lG+o8sXvT=IpdtdC0~({urCo}4L=u6e`*+?h5@Y$O5HI-MP@Np| zjB+n5N;C7rrSbSRR!4~_%ZJ^`cD<3_9_$IVQfhb7Jh2Nf0rE z4dpU66k8xu-a#xg|_tu#<2?k(C7sBZwxUlp|$o4FlHoDd_eZL9*W~D#w(yQQ> zjXIGA77#N>uiWm1jYLiG5=VX;yk&@p@X)5Py)BTV%LOX+q+HK!Fpuuuxy$HYqaJ7i z4Ko$$=J|>~E&p$A-CsVmh&uvXqBY#huBFT+@Fkch_BwK>_Oa$iJ2b(az4Eifu1J0E z7@U6K*EDpDJ&&Jr*4EbI{AY{NHCes1sn{1yA1~2hvoyZ#gycS1u?!Z``klg&8C(~v zND&A&N!JFpdU-W~gdVSR`}R!WD5R3JJ-2~gW9)lvf>-}%#Jx1U_m5Sye;M~sb>C(9 zL~(EsK7v6M(L*+NFNTV_`a9Z<05qc?Ndr@wZ6(eMf)2`>6+OmWC%&SKo>lm4>HItF z50)G1pkx2+uTzzKDn^1)n z!Kkt#7Ff3}wQ1KJe0fI)P!>RI2U}*E3qM<+vPFXxqDs^jFO49uBA$2CpOYQ-X}~VY z9{0})I}&ONSE3LJn9!mvba7=nMRW9IA(KlSz<4F{TD@lH#wZHd>M;#3rcXLhtDdVv zMiO9l6HN||m2l5T5z_76aT;V_i)YD^ShD=-F5yhfAjvEBaN(mik#OM4reEl50Dpa2 z1eZs=K>yj|1WLcAy0)bs<#;oEQluMAn6{FYyzH8>RRzr+?yA7AZ=P*ie0wC8p{2AM z?k%@>-uE6MM?#gXp_4?TcL69gHy;Pt{CUHNV(`xW{bt7&3&L)2S{PU@=4uJ{OQ%g^ zvBUgJ7HkKG^!91CKaiZ17=i1aSP!f~(Gb{7l}%ak6mv;{?vQ_i#C4`R&C3?MIIa-x zoj#8oC{P0dv{4bb0Q-VV7-o#mtddkA8)!^a?CGeo~m$yv>wGnVa-w$T7C=)Sn9Z1NdbO7$D!8^9Y=JMQB zCRpZZOk0wCZVTo&;f;5FrWo&qGM;&N=X0Zju(415F{>r7?F?!T%!ipPqP~$wD-);+ zjIXjy%iph9U`~7-2bQ*F^ZXT@_z}I7|3-bkk@xTkh(a(HkFK;63UjwsNbhLxRyB9l z1QwI~Fx!n%e&X>OgAw1B$uw%fd)ZJ?FTU6ONqcD|;;PLHd(Tvavv6{vHZk*iCVR8{ z45LIOs!S=*h&%7F3u(}(3a z{TEvF^6{?Spa9;zhL9mziCfKbZFX$7eO(Fji45yGTZFmr<` z%Ymmre)vK`<0)IkmV%l#7QMT&H$hvVcs$;@zQiIR$`b`y5V!(vnd|K^zz1 z5N1+8m|I>{;9S>=Zp*0Ljt}9f+e;lxVL0`5)Qh?^V3qMp!29>rVF36$ddA;m#0~*C zxWSZ+6}>FLRXXfmjn3(PUG4-jW$rTVBIE0HCrp3qm=A@nvI%So6;V;NFy1x9d)Stm zyo&565mZr%9h*P{)zq{r^cqtZ(p0bf{_kF`@;zLiM+6-R^NC9MjK`cpDw6XUe~nggcTgiVY2_S$B@v3=2Z9gTMiDwKAKLK zxCFrjhvWmN$__WXgzQjNXY;@`6wyw8qNc#46F&c9!8kxNpV!F)C{9TEjbrC%1FAG^ zh`1%s`dGARDe_Vlk^9#+6y8@Sf3jHgM{1e3y2y?Pcd(3361|2n@pl18qjn6Snv-& zcyPwPQ~n092p8B!Y$(dL&pk0mD#LZ0xmu+%E$j2!xs?r5TaJ0IBKOJNG~zH}zG3#? z*wMI<)G3GFTkbZQfm^K%9SrUc6{Tb)XI@-Ql9Y$n@cRI}7oP!lw^fbKfRVGGgPOdI z^S4_!j8DjxAcY?tft-4P-Ce?>%>M6IHz!k#gwB5_BRLs1`kjpGaklanMTe`L8ee!( zTloFnzUL_!vW&DIWD51`O-Jh*%p*P*j9VaWclU2v`s`i8Yx-O8o%+SFN@v)o=DlAi z&!Ghq7N!+IS?Mvb|G}G4L#y$yy0p#`Fhh^%T*bgXxUtFH=l_nSGFuXHQ*D86&B7Ic z%SxyB?me~moXtYc2Kaq)wJU>v%V)uLyR35}dfUPSq7N8H3*6L`5v~L=QqCsy9VmIPOD$o=|b=Gj}wskKQXrPmtB&>4z{5S>!-9<L^*_mJX3S9|XD|$dIU_o#Hk_&`Sf0~tRp+KNKed?tl>Qh^B9bP0+ko!(n zwAVH+L#q~NME!Y$gsSxJ0B&WT{Z+l<$=X&k{{$=K$D`WN{Tq(4*hGeM-e`lurzq}O zUmgjnT9k8gjyzVU$gQxx@ zQt_}i7LR-35@dl}#?Z*uPVrlC)bnXC!ufC9=Lp^)tLVk^~Om?-IonIesx?_%a|-^yPyxd=OD5wONesjDh8HZQ;or zo)G=dw^j}l3FPJ;k;h|o*38VV871}@Qj;U%w(jOE^()r79$EsUstyS(-k!aG8&nSH z>3z*VhzNy@HU|!LfxpskS%0#5dl<)gs~@t(GHYEGH>=6dBhc(|k8;AR!{z{^_b1xo; zHa>lfdS;*9T@kP}a;vL9j|T!ROA_k(5+4tebr@)l=FtVAX!<;g%LCGtL65pWj&YQx zX0NneuCvU$;y}FPt2C+c^-&#b!Rxf4$~cuK_fCb7sljbwDNh;ZRVUjLTHn=0^gIpF z7rx`<0Zuu#8X01$p{xb~R;s$MQDnByo2H=+yc$u1f(_w8)`voXoqFb^tiSu=W@qur z4|g+mmPwt8`<=s|zw>%x+bF7$Na|dOHvBDfONn7r!RK;L2%hj$2xL^m0WgP%l9L{P zbOSAO2GS;9a)d?k$H2r7nQECaS~oZ%jbRJ~SHJ5~OH}>iXlLH=v}QyPZBw4`pf2^A z^#hB;Lhkns1h|xT@;|qtWM?IiI+p_#37>OiW$G0$B5DOu!j6$t(FvpAA)@Lkv!6H% zmzSs9eqo$D_D#d=Pu41E0C|1Vfio$~3;k#0Lug`>&b~-l_0q2R^i1Xo8yygd$G4b- z;y?4B+RfNx?a<0&^|v{OHvq%#H)=?seF=<0&hgeYr<{LYDOY2!5L}a^pb>#T$3+yN zZq|rw*qd~oE*)eWE`f_sH^-JZ3zHKC1zR$U3At;OnLPmIH8TczRjcippOZoD`%Fi} zY8ZYCd50AQbAQ5&X_na0$@E4cY(#uURZ{6t8w9g|8jqP)s>|Z9=Y`!MZ(cZ8&E$Nx z2Me=hAw8T>YFaNRAa+?U8#fON&0uHaqZ?0qCf5xF$|8K;<6vgHn9?pFMe%>@^1Ta? z;UT8%Xag%a94Mvp>D7HGl!t_VGc~eVk{YF1;Fr|@fmh&e32eFk{_SuSFoVrXrIYbN za~RMj8dBXU^0HeGh_a!akn)Z}=ENqoMjmXvS9m*;a6QibL(87YE#I%7)GnissV(n7 z?AwkRo&!wnnJo;(Fu_5lJ;y60hg*c`#2c;@h)U#rd)EPo*x87`nx6W-13kSKOu03b zS~ZsSZ3g@i=Xg;sD`0PWzTsNoC#)km-_&G5%aGb&E0$=98p&?4x$o9QR8Jw5S#Vz< zV#IerhPv#&FOrU(Q3DD-JkWhmZ^{gm>r({FdzPoFUKFiUtd?#eG@9TE=FG!VKpx3R zq-1^hN64r2UkfzaqX4yR>)plJT)rkVg=5k7>44O18aat^;(G$uBYP4s8jXxB!Bc-mdW3j zyk3f{H;?1)em%`o%}V};c-5MdTE${>DlimPzt^QG^cy5lt#Z1%_)>#N99Far5R%ct z7rSlYC>@J$jn%!W)f-mYZYS$xQRUJ+`;+pREV3^#axdnp`o$aLiahOXDq;8^lE7WA zReook8Skp%k1xv%%u96|wmEgP0FERqy|JYi`TU%Bawe;bqT@J0DAzSKjJev`*KJ!8 zMnC2fi+7>qt>gG;d9H0MW=-y=#wyuC4TO7^YBE{O5k0=Qitw;9a5+(k!C2UHXBfOb z0O4jBcmDdzS&q}v+8E;T(&Jt=3cPZcff>#xfuqkr$;wRP#@<=g*w+rPfFCZl`qLYo z^2nDO%!pJAG%h#+R?C0JN*ec_%L#E}olCd%zH_p4>syMt&ow!+g?C3bhvo*8JBBZC2s>Hoa}QEY3z1yCq$EP~W$_BK$_`8h64* zTuMWK8aetUmM5#m_E;*(U2W=d&H@_DrKPS->MF#id^t&T$ofVw0c@%RsP>ailMd_X zg!tku=`_o6a(#w~f2&P6I0g|xuori}d~F9j((s)@+}v!k^or#^)6l}}n!DOKZC@q> z$Z%oNxHs3<4=+aiXTI3@4-F0Isaq$zx|l4;PMrLs@n`0Zetk+>CUuBit2Jmt56uBJH@ip6_Yfu@QP zAVzXaMD_0Xdxvc#9?_m{FUl)9OhXW!=XfL@A>P3Li3Q>uVr2Lmj#j!Ta1p)H^*ku& zEOPxvx%F3vO(UTUMeH(1A7R*(4Ob`oXgk|&hzo>)Mqr(1;Ko6DZ--=PL@9YwHsju9 zc-;xBr$^D>KKApI;rL>TB%j$8D6#fY&8}ua3=9cVoCOyMUbj@~hD%nKdbpsY39@`f zz^fbE%Ayfjc-$W(VnaM-60qT6d_RHXf2M1D9DBB1AX+1=V9xxu)vR;_B>KMB39&nQ z-OD}JuJfbm0)A%Kv=1Gc;5>0pjf?A3f4yhQeG{pEC94=F%`+pn z$E~^)GZ7swko=C4c_)rr#B#lZXO{pKBxAWjG`_077w8z`w@0uJu*`{FWiw$Y8>}w8%hZdF&ej2l^oWq)nBM< z>*;P74pctYD?sRxYK2pa*127URf`4?t6}zIECgtJ;z(xk+~aM%f+@2#OGQhd;4c*~ z)kzcn{xR*{)Yf=m#pPQ(HcyH=?WY;e0I8)N{|$1(8loi>9V7uU6T|g*uxhkkvp@?> zNsJs2iUw5iuf#LdrWng;hS2LV9txQnx97`j+ZRtPemdopUTGD2mO$NdbC0)XsR&7; z*~xMKMI8mkiMFMrw5>V}PqV3TGn#=Cm|q~5ynHD`QbvB5b>O~<0NJ%3yyF!wxi4d8 z7Q_YK<|!)}R{v<{i(XAfkc5)(Y0V1c`Lz!W@0Z(RaCsYk9K^V0h4Q_>At2s zZHQ&(t3-A-Is;+!RVY-}8b~{$<`LxwwuGcj0V9Lt9N8)EYqF<225pU@WR@^ltvZLn zjp5~ycg{CE0j%Tnzp#;)G`}&}{5l;y?C5mv$x($ zPhy}g_wGffz>_<0@fVu5?loSE{c;L?96da9N<<3cdLel)Iq87Zc8wb zArmzNc`8rd(2W&)rm_cSzUwn9!(r>jl^Aj>K&clgXY&=G;4j*-qL{*1^5N!URY%o- zx^_j5A-R|#-ic6oMwOG)4RK&Rr@?q*+*K?5MyGhx5t zqy&p)Lz|brmg$1U{~@`iCLM(}c36OQcASluLsm{YO8O{~OW)wti<#k05%|o>S*Y5T zm--z!?kG6Xtyj59AVgi*Sfy##xV%uFc=C)LmGihFBXrYiw`{ zT1Y3>!QK03N0H8h2!^ybbs@L_Pk1~F-d{F=g8VqHX_m?dP~~)hJN_Vg@@`k%Yr!9i z0gn0o@b*_&9qU||?Lga1uF-!iU4&PmWjTvh5o`@Ha0?*Lk#^fHQ;iaOMuJ$W3?e8u zVLCcHW=7QTPfClh00|dBu)3T^V4sggW`d*_|8i3$(*_yEcAWg@LkgPHdN+d-AJ$yT zPeTH#PbuT&AT3>0y3v#NDaOaSc(SieOC9sW(Y~k~pl~s1`!Dbxa{nv=c;bahspQ3Z zE;v{xN4Bxb*Nn4uftJb zZKmAv40vYFM&-kI>R#ne>SE*n%e`t}Eg5}Yz9RJ8=@X$&-4QY+z-U-&Hs;rbS68yz5WNypAP!}0#GM1< zDaLVd4zL~E$NQuV#4mvRnQ@{ov*v($J+Sje@CMm}_$Ft=JgFvI(|G!F_Ml6Jj2w;^ z8rtmvfP>Qj#G!>afTBifMItLd2}xy6J^em_q0}lpn1rc!J_njV+I}l9k6e|WWOtRN ze2fwb(m_k?+*Y}t`yGbky`eJp5KIMh3v!qI2*|?Le}daLn1cjW?EOsd)-@#e+_jn* zb+{J19k!r?%7*gEC4%jytlK;M_(NMIp+c5ge*ZA_=GVDN~}@`{NdTe12PFPs2^#z2#& zjfNUm)%y9y0I68`{w>}M0DgqK z{4}(C=3pj8vl~C~9GUV()NeGgrk{?hsXf7Sf4YP}686^4Gg;igXs=5jVyY^=)aKRG zHF{e-F=7$;#ChLMoR^O6RJ`hBO(~scb5C& zJp06}EE@Xy&D&D=xWF{|x#ItNTYZ*^s|S1ep4t+OpiszdkZ0?wUy$Vo%%|H992l}2 zE}s@wyJJzSXHJoF`}jWnM#lyE8}joqbB4z6cL;61tLny3pSfYkpTGoBtN-19q0qYYieq9zz9zc(g=0 z>ztj}`hAUYApv}Z%)~gGAV^i`uOQIqnyS)lHX*vU)`G*_o9X*JZnicUetDsbM+fs5 zn~`QpUOoZc&;0C7RVdDoALSer8 zr}?^2KWKCX7okj7-fdx$RisUV!uK_PgJr+4?9rhTi%!|cd)HlQK z1u|fD8W)#K+M!H>HhGaaNHWu1?a;*}hc%6G+)Ih!*V#(CZ+X*!@}=HG>hDC#t&IHi zoZb}i+wL4q6ZG$ec}h}Yc52Pq2VxoPLXw`80P1J=@&cz%KDqae$iY4WXFbT4)<{H5 zfl~@qV5(LWL;T2Vk@H+lj|7E@$9@x6Unip+yCtV>xnjk#02v^pwfq;W9RjF8y>A8N zUAP_ecdA2kA(o6QjPY{(@Wii_VxO_QtDHU}6y+2(Kwu{XS1DX!pSXAV?k_XX>@Frr zxevWSm*H%JEobi&rGLTWm6bq8QWb8VR0j>5LSG^3(n&uh;X8Jc-IeFg-@BSG-n=Wp zu9I~Hkq(M7#P(h*1?#X8M(01j;n!HmTuTsL*A`r@t^Vo$k6i$KTUNdfM-CnIE5-u}7xU^wO9Zn`?t>BH zF!$FpFWkP}?r}7AR@I>3*;i|1^m(=Jl`MI+Mc#-)YHD9TwoV)b7g$nhD4@k$j-udi znGlB+1hoAh|6{toLUV2&!Jf#tt7-+~EuX@Y7>jyDbYDkaMhID(LF+6_{Bw6l2F*+5 z_t=bLB(y$YSMNNjpbwttUtpedMGQ0g>yGCcPI*J-GL6uo^g@wVJ&+1EuZP2Z2SO;delS0p~2(U-b_{df1W9 zn|!lqc<%LnigRiv3a0=M__CzO7k0n>t5C!NgI(C3zs*YCUbwFNUu(frvCiEa9F>*w zp>dA`$ANdii@ez0O`A6S){ZBKC!Lw(`g>1Bb&{xJ9mqLERLPI8>UP52_+Sf81r=D; zkNu#%(MT%UOksA*DtKv4vi&o6%?7XLL0ZgX4bjpo-$lvc_WN~ht9?WU0-W(~1{ux5 z&4T!=$Yiu>3V0z)tpJ_v)eOk^Ws??KH|h|! zTTcq^$l+>Xfuy-T8K)ColH=7yJj!3eu>+p&&v)Dw2OtLSLO8~#_j`VAI~TenPabd7Ws?g(wP$eK12gLxc0Pmg;}T(dqPnlobuOM?w)BS0)BUd8Pi=g5 zdIB=-6L0Fx@oTo@37hTKuFPsDQP0gDRfj2ERv}B&&%lqH47fw>T2fCAL`W%b{EJd@<#=~Ll z^33zPNQisuMmNF?u9+9#nNV`^ey$o(l#3mN}#2$#`T>14_Eq?+I5sFeNk4j3)|g$zDC!ITBVnmVF+fe*{6huL~rEY z0}X3pj+|(a&7WKyC=k<#>%PHXCLAs1+)=%D|D_8Jm<*t*f7tB%eHKfjje&M@luzGQIDkvZ)z?Rx5p6>&D)xjM?t~$AoB6niR*f1A>>5Pty z{0fM%D0L9_SzE^N^qV)g4WY1*4O*e+t0y^<;>9(TGaN>E!+;Dthht?#XvdRQg$i58 zD8*}dQ^5b=mXXIui!i*du|hv#lnn_rJ4+%c6oQqT28`K}fY=G?QlXZ9#hw|SpXBkr zcxs*gb%u~db68wVLB;V4>&&y~03J$wnIVYmP7$dYd{IbzC*X?1<>yH_8pYGaneSQ@Z0irVU!?)7uX1?_{Q{{!e7` zVW?kTsX=q*muD0c`9eF^w6fB8irWpVu*Pm{y)LF_zU@8;&k}^b+Pk<2au}&-1cO1q zPGT~*y|8BXTI>TFPm7Z`%pYq}Y%01}^WOMYUdGLC2g;HWT5gtc3!Ur%HAV0ao@DOg9k3yAFkTe`F-^U#dgma8S=scoNLJnq+6UZpe%Q+L{RebkdG@VU8D&7 z@gY%4hyDkK14WKgd{ndR)#_hrshkVs@f-bZ>fM%OcFNVmhka5spj;|c@6n)qru~Aw z))ypxoIr+IU~(V7jOV2hJhnm{reV;`FvQK7hMHEt`ziIJXQW2<5~ zL2r)YcPA|-2`4Uh8AU?Ing-oh^L}${5^-yG2`oSCka| zca1BPEUpY2IbSO>A+YEyl1A^k6GxE}fOEz>ht1!x&#KS#hr80E7!hAc#*2w$?qwL; zfNMs>S6}LBXON!?8P`dr;!Z6XFP68XdwiDv)r+DF<<;5}4MBI0&w}TcXVMGPh9;ey?^m+uy59 zShtJELK;If*gg~A&qfQ~wKLiz&R>{Y_Y3)_TN0Zm{j~=#s40$2T`FG$I4f#%<}jym!SNX9)B-&DCn0cP)(8icf8)}nOQK8?TIt|*qqf4 zbfc_L@T_STAb`?6|0T=2Ciw_5wrHhTUHAW$bazU~FVo%7@bDhTjeA94YWXrz`n4z^ zAY+hsv8AfiaCoi;BW&vS2O{)DPI==?Jo2;>nLfD%)F{ze(&|%HWK{zjjyOeZM4vxU z5OfCbQ>*%#_^}fQRn*!7$Ovb=-4H*q+N1u8>NNHe?8nAo-P8@@OaC8(N7SO6FH$r5 zRsVS27`|*rQdc06TC4a?3r;?b?lDB{{f?KWV`qdYAxfmn#KLwcdKKL}D;Sw6lcpj! zu0GPPTuMsh#1mPt5tU^E7qUZ|CDI}s%R&3FeVSw}d`d}BRUbs8LV?(H4b38q=N`cC z5?ogvY<7X|6!5X7i2s=DD}s=w>5lpw#;c{D6!BGden@O&FAaMX2z;3MXwQjJ%?(Ku zm1Mu zeYBrtVh}^98|j_~+SK>{Rzhqt+BS^82lgQ@=NWrEJ_-Ue8>g71+)@~oqnW!YKdIQ) z>sCj%y(^!GuAE?r*p(;Bk8={)r$L3H28B$46Pu@fQ`|gW3T3}A0muIz=F_7N;ttWu z=VA*IJXpSTSzxWX?a!Vmzf@Qw6TErIAbo!;7U`QSHjq;IbcvCYlcg-en%R54mgNNLsFj(@I zIJOqXf35h5fu0sp0opMkM_|~G#8o#0p2EQ=JXy_=@G)n>QJ`31E)sG7Zz>iQ39pZH zpU1B73Faf?i)03N$C#p-Ew0C*$0qmYIe6q$q>c&Lm3F_Uqq|yf%gR1n!+piiKY!)_ z&nrUc-CmlGoluRk;wSScLv} zFm~>>LD*c%b()_{J6BZnzz*%*^3Y0M~HtRu>?O!>6^vLu6O`=V>{`>cEHb8Rv6fo@jm4MJ5{BohmCDxO+^Wh#fQat56$ zYF$aZw7ZC|oV>SEl9y6D4!?*|6qFu2kgTjNJ%5@UZJ1{Jqtz~1*;bXEglk6O4Nhg zL*Jg9gS;dnO0_EF$3F@mb$!$tLs;8YF8{TLE{ka&Wo-U-7=tAs05ac@tWqlc%AT-d;&^2m}1(r?OxzPb_jjM z35Z5H(cz=9N>AM<&Ou0yGk4EHKJ6+Q9Z+@|gMCk&-NR-)Z?Y)_^Am zJ#ca?ex*Ii@BnSF%s{59hr6Dds*ELk!U@~~7-Yn9jII&Sn`?co84zrz*RUfo*A;M( z;WEur*hWl@t$lnfH0I8L*FBRReOPp=vBCS;RNR6>1K2Bmtd>i+(H^3fH`iI)4lr zp@(6HR#H5w;8ehJ@TtGIM^RkG;tY=6;7lvl~s$FxT0R@rt4UEy1vsls&z8JOTp`26#H3)Uhti; z>z)l(3}Q0o*sBikkVziihX{{8PVwXbsAb<1G0gpC87#Oa6y25~1_rUBp2k4p;rf z!*PStQS2ME7Rz}Mdmn7pljm5-=#m@j;ye(^!;3K?W`1!MDFT?sfk+XKPdWyfVg;u+ zE@}}|?QWxt-z$B@|7>SFJLqQFc@cXc@uc*bw*xR1hrgi4U>g+M&gko?TQ2|?*f{f~c`1b~0oQPyqV9e8n zc-aE?>;?9)m7@@W`hyCNKa%rBx?@-y1NBEt75lh5n>A!ptA&Cjv&7*h5##G?Nj?B4 z_Z$bHuR$LQpOL+};~WO;xeZqlA3>ojmP@N&&9#7+lVHb9e-xK!}I+?qgQcXqKR&7WG;*p1Aa6^-r_*hhu;R zWlXCGga|=l2})irvbpl=5>S2RF;uMSxa6N`&jP=E3|CuJ8>{!V)KrVdTU!o?F!*Pb zx-G|)pP)e?tEW@v;@;9uKIb%VR^)X^?K4sTMjz@5r9yG~#~llW*UXb9XNy~mfynx` zfWkI4I1j#22pbY(ul+o1uH8&L2bUEB&4^{_>y;30!ECzgqHJ1Ax+5k=(nTxsw@}*| zRpZVF@b)4?D2VW(h$}apDfU;CY!)p(&YzxUhE2Ot0V9JY$UpB~i4?_!yXDb>b$_T__ylpv; zaNd~Ml~Be+r%QOds%0!>Xl|D1q|@DXO&)9MH{3^eC@Xdt=5F~|>nYfx#GjOE%!Zv` zpNL+@>q1pOwKt31BwwTNgzu$QKcrt3`D7}|d{d!!H48vg^$=N^chiYKOy6^<7*^~F z$tp~xQ&3n1iinoA$hD_TD$mV0*JSZ4D?*N9l^WVKZvu&yF{^!=mo>*Vy^>zoi#?!+ z+-*vAQKO3}j4KD)DZMd0(4^yq3h1Y^#oFfoA<8n2fW+{mw^B?v1*Nx5gI1AM^p^ zfAhW22eqV?Zipb4yc7HT7!IAf?Ld1c4+>@0*UjKJMmVe)3a?vnhJ+@%QJG?oxv`wn zdQkTy-f;pUS}6fRs{s%Fh<_-_6!aJ-p&9Fh^3$GT8tn~GBgAG2D3w8bZB$dg!z+KZ zg>3+~6nd^;0fxU97axa+p)NJxTjN+v12hbJ^P8Sd2O z9GO`I<1(jGWdG-f9Lb!uF(JjnM_!OQpUYdd%R5Ux| z*&n2ZvNv68Lk;{qI;ln#rA6*Pm1n>^1Fr0%KV%>W^ zA)^ypqL;tyqS`gn6E$gZ^LFtEZL~1O)`0|)b^mvNNm|!4%Au$t^;I;_MIqXgZ~NP% zQMlodOs)U|K>fdV2$LVziZ75Lc1x*ym>F_LKPpsZlichY;LD2axBec^{6F&|t@>}! zK8q+%062y-Bl_K?ZIqp(%%r$`>pi9E^w+N3^FwT+_PCrhLvztlX@E3PF-0|l$Jvwk zBwzuYj9ER=ffR}@hG#>h*j~tCY1u{gkD)39dS}v~X*|{^4#p#xEIOsg4@U&xBV00? zC*>6-T8jSL6LzyT%pWx^(77c8O#Ha60R1sGn;G&&M#hE^O>lhRVOhP`1iO=berPs1 z<4*pMb%CTXXKaVWq=8CNQV}t|DWdn*&yMIdlhyWypzK(Bi%|J^oH{`Z=(NNJmv6~q zFK>#Z%;@@mjT{sTW6c6bt0~5?J;G`2d+wl4YsGiD5fj)-9)-$*4i52pBgQ|R<+ldlEY@zpP80BR`G>)(CxT*aclbJi}TMu8e*Zyy=%eS~paqFmYk!2_S&L$-Lx(+Jtr>07HiuIyG-YO(ax!cwmlH5Z#c_9o;d zS-0Oya(gqC-TkR6Otc{Kg?`cQXLXq+ym&Gfhc!}4Ej2bRCz$b>@F2Qg zXR#Ulps4~3Yqk=&Dhbf&aJAYJ`M1?8QggJCGOc+p+b5NcNBlS80kan4c1r-vB%V3B zJL9p@7W&oyACq$B?bPk$(FCoBpaWWXX^9%|tG`r!7A^hlqufGG@SH3jT2}%E9sRa{fLFbJDN0#f~|xIHJMYAJE4*yLP9M zV_MCd?CC5inxsjxx3j&Vf$d)Js$Q)hL0uA?0vc}uz+HO12zEJK@Cx_oisi6gT=Dbt z3{3B$79@edPkAX3sxj(EycueVee&^+V_I})sI*$>^)UT;FN_hr)b z2G8xeUh7V>y&W(w?E{s8T5vNsPVsz{C?vckfAiJkn!jC0Mwgv*BM|sx9gF#o|w^qMgrU-=b;-dTPcKM=j`7 zyblo%oq44(-|zx9LE|xypU|{tY~jI$(%c;|Mi?|gjj(s3B*4hFCOpsPH|2Ed-|Hz+ zr%vA1ZEL)eYmC$9o}}@0Z~X{G90on^c=#Ioily!%U%(hKzM`4?k9SJLxv06q{#@T^ zzz%#u4cxAN2#ff{1sE-C#;(V=LEQH;@Rw7c1Ux%joCbvd*c`ZhMq8mZ z&ZQ5%6wicb9$#9O@xzphlz(!NXm1u_C4HRRKf%M5S^`GSQ1%4;i687!^CISm-MbyN zbb(8|0wFl47MBcBk?l2pgrtoKvM*+ACeN))i$Nije za9HQ8zWeN)Bq#iA3O8c8i?pCu(NlbWN0Sk_)+GyO9j(@E6rmTGQsvqwxYX^W>T@`; zDvVniK$eN^(%c`7TD*Vx;#Xl4gqzS5iR$H5!!j&utskg17;#xEFrY}ec>mI?A5S}PEk?J6g zqC4}9Ys(}0*p`6R@>xN7A61&)2mh_R?t(v=T@fim7htrJW5stImd|C(YkB2$87!)` zVgC}YrqEft0*P`i2BOb^fv$|Pg9`d-cj{Tr9}A(7aGjB!2#A6gNWA~qO2kl*D>wu( zu=Jk}CR%|3i%pGm)XsuCPhE2&R54ELZuD|DfRVOC$UJ-uzg{jmG*l)n#`#R6^DnT%HcD>;+Y~9kMjm!FIcU@6@=R8;(`G!-0R;-kFLafcr za2`;V%W|t;nC<#8{E5#5TIb9A+z{@aJK3;odoArB=?u`BRLN$ovbSGA#W)TlbV8S$ zdoIxXh2SJcC;nV29b4FFh0N#^93KRWo9mysAY48B2t~ht5a<4;O?#W=?)>ZOe=&>+ zST@?5oBNMPH&)S{ur>C}LQcV~B0I=YsNKKbh0mJku||R#6g`c3sAKcCOS9;O8vQBf zr;fwco6uYG2e7uxj!{Gut`V0WxbA8l@x$CDzd?9d-1mWePg%!)901I`_W}sVC6?sc zmzPH+8jY?k*Bk_W0Sfvgzvh}R#%@x{?W17Q`Wr_~3d}vOSiO{(-?s>J3v(zGm>Mbw zB5x*>nqHt($c(h?nlceI1mH8tIPY}<3)1?^O4Z_q@9dz!3e)6qJ)Tta#SE{%|FA$0 z_2_^C;;!$V8vGOB(rZZzo`QvxR@&qJ_7oq`>9^|v>vjvlp}Qq9!nofFF^===ou%PQ z&+Thp)&DK9OM_uthah7CC-;yqKZOOLa2ucXH+Z#sk6VBq4i@6ng^IF$wK)PE;HaIP zhm-H{5eqgRdwBEUq5;8$*cNNr3s};FXmDLwToiv?QtT)Vz@I@ZIhbJ|artAK;1zOsYgPY2y-_D;qb2w8#to1>7^G(Og|ZmwmbT3Lp-PNBS<>J%P7;z3Vc4IJ;@>mlPw_vdGN<%KqKfpB0d z!ELwF_$Mdow0|*oWGpcXmlH!?YuyRSX_&$@;mcChmPUBaon`La6hUYS3nAIEp|{nIn!5k?m_jKclYfG5`3+tokaxRm(3;GRAN+O-%7mF)vw?EvIy*lL|f~o{EqZ3Mt;TZLeHe12AzQa+U0i zRy9c5Av!4(+*-~wY-$Z3-G1X~QUOgZF8c7rKuy12;IXW2if&?FwnzuU1!B#=n-(@7 zW`cXgxAV6Klx^0)5%M*xJZaJL=AoU8LGYgZ0hMBfI=7oNFiZ8_-j0kwJ(YqgL}3CI zvEm_j1Jt`$jbJbn2%>bf;7&=j=(a@un}=yWpvrL*yZ%jec8$J|PKW?iH7w{FZr5RA z!M^Lhc242^oCKxCvqVk^5L@L(_-5Saas5-!fX#Xtjvly*<=`{s4nS#k*?rY}XGFA3 zf7m{6oI-~pf0|6ri;~-$F>O1e>Mqlae!(wd@ip~Owkm55w5T)AGl>$El}ORa^_Kl) z)trT{iU{^ViBTeU@ps4+dfY|0Pcw?>1)y($1Vv3N|4ebo2(i-i^3ld2Vz=Sf#QQ zVg4{|x6X6G*z+lgd#Dr%0Ut&_agLf*m--jK;NvVEdTXb|z|e%2N`NW- zpk{v5CKR?mNT^P6 zX-y|L!?eL*3^N7>^04|j(ibA-eeTTC?#zXf_q@dzj!*;wu`z|Mx|10ibky?*H&=F{ z!zWD>jGzI%{9<(((gr1aeGW3M$&m*$?uwA{`(N5Wy80`CLUM<#BDB|`m6kTC$>dPH z)iIDIAeb%NL&i2J&kKWD_(mXGS@bNE1K{G{{8P<= zO2#4uz{AzIgLiSNd!wf|VAkfC%hkNYkf6&M4LYb7U~Z+I$2yW4IT$2Y!G-yOp@fG& z#i|8PUQ4XG6W2V-Fse0W7!;if1oX4K-{qees6|@{j%xeq0-bK=OG=BX?>(;wS`Nkq zP=L7%Vf-lCVXdNRk@ayi4sMVU!P495{}DdIB1S$0mXjD8Fk@pCd;02oW=)I6lofQc zGT~VO-A5NT+d6wLz1HgTSX{t^r9b!hd_%sv=ZqtORY8IqT@r1U_sQQT7J?+E`iPJ7 zt><*{cWWaH11`j#^4pdvaQ=*8iX&b@ZkEH5!UH$Zxegw+Jz+Tpwp?;&$)O;96*@RH zTN%CvVa-^>he;%_MMxVcygD^Ul(bpMVxD8Z{IQ63DU9%YPOqZ{fFP)c{2qNRev6f)8|p`zQ=1`ith7(lS%N)-K?k{K zsWK;1@C@>+YD&$#B=U*`Mk0t+{aH-cKye1CEcG1OG4o0;ZJyXT5R`ysx^Aq~8N4#r z_u!z7c;l{RE-%`Da$?U)%TEz$t7K}-i^LQ zAirlX#sVY(dlJe7X*Dher`RK;eBb8MX?eev6Wg?Ly=hyDZ82MSjb8F2fV2Dxs$8MS zp)LfU3P>xn5Ml4XgN#0)k_%D&gvovdIkNB%hgyp#E5Bsm4KVbQpaaPDu>(OErrP%^ zVRX+{4073pK5jnG-~n+CvgwDp{=K$SbaamN4h5Nnr9YD{gywKEO@wYu4x_voueyK&syVIOz~#76(y*VDvrMuD2Mp=kl2>QvbS1ck)ka7r@7Pg^lu2&GvG1 zudpO0U%_dR*+Kit&Sxn62miT)woK;Ojs2mmUUwnL#9(9hC6waE(s=i+p}h zMr{xoHDdJ>6x*#dCY=;NAIY#61Jn)yzmWT@EH|E!S1zu#{mYA{!xpree+$GJP$ffl z`e%LG|Fw}q@-8UpN`p8K-~nd%v>hzM8j||g2V$N+FQA+Wt=k_&@gi=kRM3WujHJYM z1F~Gy=JVjMc{%Lhc*3g#Nbomtx7M)piTK62zf*j{@eN`Z9CdwS@%!<)qJ>)>K#dM1 zKE;^bV^n!V(0RjuXjVmeSzX!=T`?$2fv0JPr}O}dgM%){{UG76FNOFK)qn;qkd+x4 zr?ch+T;H%xhVHPVq83W`HB?))d@$_rexh|6ci54_@C(& zUY^i+&eZ&R^E^2@rK2xexxp7xBLS$hom83a}by}*tOl}tQA4^68K$CttzN5 zw6iEt-lGL33qcjYLM^u_<)icOMxrDOeC>k+SbZ>uIno}a87<5J@Qs*5ejC&EtE*hsS17vcM8?pEFuLT^qq4@_R*V6jUI)FMH^xL@4Ke}jMMD^ ze&I()k4B1%T_u$rWGkWs_C$valGQ=0R|eXD3|au@qmB3hKZ&`h```*;o$6i>l*@i% zP$V0gw!wC&21X1}j!H=D>zq_EW8XN>9j%DT)o(xYxCjUo-9Ii_Z%3J#V!KL zEC|nn0mt7&YHB1n?85pHRNhWxNIK*SoHSmc=+_s^wC2=xUxNgmEC9wE9P1|{nOfF_ zf!MT;ta)1)q(7UGBdgff=+^d~%lR2)ZG+h>A03TpKqrd|NPnyfcY-xM8P#moNZ{t4yi#0oRj=F7@@ zkmRTv*K;5n+#3~HpSdgkLkUo2hW12&N!^`+`>|;b&B|a*^Z(i zx3$9B2(`n>%)NibYad?=i(qE6VftyO>=i1_oCR!Uz(KYbH7G>}A}zl=uRLrUTTS!N z2XWhQBdT~UI3Xaaxi8T%Q=HSl0GR!VJp!yVgGFsNPwQ_^JBH{>Ob^a5J!MG;27`kJ zzZ$BGb2+A-pemVb816;9mZFI)`Kkbd^4qPNW!hFthl5CvZ60X9W%!NcOb=IlLTQ5E zVKBW!vjwZC4S)F@f?e_=jm4u9&o@bmU!%kq@QQV_Dw$lP{Pb4}1dt78hqIgmjF+7t zP;9Y0Xn4Tm3IZZ`kH)s$q|>G*|A&VU(mldK8m_8u^lHz1jnqE5uY`Un+5PC<3)Z#_PcbKR6`?w2jM3tVWbVD6K~m0=GW1C?kVr1ya#2G+O}T`8ZgYjSdA#)O}W+X)J)l z68N`JNBa3N+i82Avk^}~FynzE3uZ%cVRpuW`y^KXsZabT6n3($isdndt4dIHW&nef zz@%#lRMQ2>HaV}yyRmKycy<#DXP;<8U=xCB1zO4gmgm*__dVf2nm_l!Ce~PVbvJmXt>T^t-o@H)nhZ zkL_n{y=g|#tiz`Qcs%(B0*iIQ4#7SgK{p!?Xng~ECSlP|eXJf7$QhO8bkoth3rEpV z9Y5*2DT|o(VsZT|OP!SDA!oz78i=RO@6vGyZS3ho<#sROLlmfx6ILSOZ{$W{_Tm%r zUl{KL|5^SH-_)kowdcjrSiE>u(v-;J%U3P~z{5zKzq0IZ z=dA?fL=pdFs5u}YoH@jcUw93ZAab<_cFormtrVGkL}QQkODU)A6_D23)Xr1S)6E9j z*U}dgfAV5JBq5nxM?o~ptOakPP~|?g^E8}Gd}a)8u$3PA18q&fhN>Y7m_!AQUw`Y8 zHG_4A-eVEMxIqY(%sA9OtbAv}kqKr!ay^stme}&Yj|PQ3zGW1=m&S@)hiOesTZ-3L zX1Ji5a14~KiLh)0S7SV&loDsJ#BES`{;EiZdb(MLVr=N<-W9tEtl8H*%}sRCT2fi2 zO$BBLePPIXS`UM@?zmC6ZTZa2Xn1vDwb$9Fv5?e&-OlJ49p>q2MHCV40zuMoYfu%n z|A%(MT|CJo%aW0G{q--I0|{MyFWw~*GwbKCDOo=rNdvk?&=}rC08|IK9J%%1a?n7w zhMhbXqk13Ry}z_?@|y9Q_!1&P`pyM`Q4-~DH3dWbi_mKS`T^CsqHZSblrPU69c-8y zyjR1FI8P@t-d%g6m*i?vzTgToaIJ)Bt_pLHJt*c5X#&hO*0d z^b+R9Mh=b2ym0xvnce-Utd&C=B=8=U(p7*LEvoDL839O$Q+;GFfd8<;ppU@T1Y&^X zTP^#a%~^=Em^-IF17Ep;%?9+Z{^NZzVPQOr+`omjkuR|7&hM)8h}x+?{k0jQTJt=& zhC6QV0Oz>sReWDNL2sFCAP+py>%(*-iN>u1Ywx#>+`V|90fA#wVGv zNmA(5ZdUX~CN;zkxNof*MS-ZKZDu)Cd$hUV)uPjlIr`irs1ahsyzx7Qp^8hJl|?ZJ z1D@)3N+4{BNRTesmS@(NT)t9!DktO=$n&^E=gAP*qGtz-L*KnwaYm3D4x3e8b{qlS3L#L8mWWkbYFutweY6!LVR>fj#EX zfArO4R24l7k;ZFeY>hE5#~RJ$?pe>?h4e;mchneCbP)I%^SYaZy_Ty0U`e6cfv`5F zPL!74xm!ysK%J(!$j;V>^baM4k^c;~SkQC`bc>a$f3dv4Z53`9?|EwfQmJh3KsGirZk z3)3mUs2x83=dSmED+HsHu<-btqeSYRL0HBp&CetFUDT=70=_~X3M12vWI>#+g@N=S%0 z#m5QH45%0%d_#BD*Xec2eW#ikGr~+}9_q!nnTb4Ee^9WmrW!=&b=U0>(mTd?p+gy6 z8E_GsMg6HJt>yv@o$~^-0D{j+8A5X+Y=NR5^X5~dWK}3tRd3vAt$W+yq@O#s6^;4Sj zDk+#Z&_^LGbQRH%O@`o#mu1)6D}51904J!D#{jR>P*#J29wEIui9RBLOD~g5=1&Uw zjl6w(kf6tWmn<88B;Ogni8x_<8V@Wi!0OjRWQRT-@Seh@m&@uUmO{(A02PQlt9lco|u8`V_#ce%v$2uXKQC7gZdWelks=ymY8I&|znpaamiidPo%?0Za z$_G9ndTrDIy?iNc$nDA_obe?_oQmEMG@yMb-d^4NPeJ@>_F{ z@ym?TT(1XGi@pgrr4Gw4)a?_!Nr3$Z>U3AfZn!c~L>u>vGH8~i@zGuMIwRxc1xyFr z{hBau9)Mj-f0B?Sa7-!DUTK1wi=b>7^L0<_h_#QNJedQ;wb*pI-sVd4K%$^0N!{nL zGx6H?-*an37QjpFsUE~@!|$o^HN@B5B%KlmI)(w8oAM0uK%5>H`Enokvf$dd10a2U z9`-}rqK$SPfd=$a;^>+>*#*T^hbN?H2DDPO46$K63-9(bTy8G1J~MxLRk6WY4;d*g zWNOBP-YM3`K2z{A-l@Q7q=`(^C_}-kQ6fRF_Jbh`#tKW^GlO&ktjSQs3LDC;_tK(J zpYriEVSM29C?ppnrjP0H;zbNT!)aoYTl$ri3W&Lm3a^iLWx-cUz7Uzo?&Z zM>!wDT~UN4t7|9dRUAkNy4MP!(qhHPFtpf^a69#m*?H#V-1PKb8pai-Y7l3vL7SVG z2KAS{3{#_ISM>6NWT{G9H-Blg`QhxkTaY%TahKxb)H?*g5!D%azrQVjB ze5oY89NACIB}$A#WZ|*@7GrdXV#uUu(E&jyl_M^8Dtykk>a6P3QhhGZz)ti>u~r>^ zSKq(6IL+fk^Gl3g9W%kT(CuJ2KKh2as`jyiJ%Jnc(UcSq1Z-;Nr;$R%*34LJaddyJ zapIR$IppPrBXmsKyuh5%z6)dgPx>4us1!lSBLb6{L~#POsfLA^k-#6$(h68EXTXBe zZBk83RP+nWp-uODiUw-|xqVX>9H5Na=KB;;kS zWLLGLel=lEE ze;#gV0o|_FDThd;IHZb3=r%`7`rg;cVoV@Q@Hax%?(H_Ac8WChqbWr$eA|6St8bk*ae|Bpy~@Y`G{H z)u)z@{`CG{ApEBGLVK;^NcHhpp#zZwJyAKQBhQDBU*V@56Ne9*Ts<;AjrXAQ3(DpZT*Z(hxBrbjzY+E( z&RcTm0^TOfhi6O_4ha`e=Bg2Tmj!B))$JCuVR7)yC4I{-q!+U6Yf&A1wHAcD2R}ag zyG}dbn{!ASgIx{o-6I;mNcTFGicXfFx+v6hu`^?gB}>2Z?1WSLvHG zh3pNLU=SlIqTR>|-))c@`iEwB_;7U?Zm#i(p%yv9?NIhn7j`_a$OOV?thf6vVh-nA z`HPcL^%4`dpB;x7)l`>kP$B_;2_PweWgd6$1)v)nGi2s{3{D4rBkkBdHhHeQT@ znTd>W%HF_-N4fd4I$Kq9ryr?MQ!DDa(jwgdFPG= z%)(gG^3Gp`--M1dYL^%5*XRS@lM&D$K4C` zU(&u04S8z$BHCSSC;znXEE;=(qi`WMCZ>P#@M4x-_rKji;35g5v*$~h36)46x(Xj` zm7+m3ormT66U1l=ar<(I1=typ6w?R?Ex89n?S-{5f|tm)1L==ojADAFYj za@%I^kl^c%v^b3kQY@c^8>_{)s&)YhUzlg_X66xkod%PY-Y?g#(r zB2=)IhFSlJ9wmQQS6GN`AEm8tXtu3LI{l=Wt^gw$+qdAn@v|Wehhl3kjNxxR;BLCp zZ;%b+6y25H6|KU|dTYo2~uk1vNFAGHG7I72y{kl$T6$k6{= zi{mOUWB3;ZVT0AvKf^)@E*P*sLDf{?dE_}NwJBpU{1f_7fv^@S8sPSDEHW<*?3T4! z;i9CY$*_z1+j`-fcT#+ywnwOpCEqKKA5=79xE!jt`(a(LNnI)f3hot(;reR*6<2SY zA{nP#A_qTKNf4$iC;T+YBj9ylmkDI!ZgSO?t2A}I9InNfG{+?ujh8#FB+V!y9snJ3 z$@;MBZPLV2s~{`$KoI( zWP9;V<@O}N{zm19`$fwXZcZ?RU5ShN1Mt;6aHV7=dl#0P&||;SphXOvh5Pj)@5KLQ zlzoJ`b)V04Ht;28>{K`+0 z-~z>g27Jj4k4}_@6bT#gjunG`9zo3{GSM+*J}$0$TQ6yS9H$b@xdU{Z)MSN>4s2~&|5My zjBT#WX*o4;s7lK+Q)epbgH5k1?)V=g2=U>6QE_04rYVKL$OYeE4|Y^${#?zGTrmv# zkrbQVtT5pj%60esh3ltsYLF#=eiCWP#2GR~5L>KbQvN6jXV?*q8i-xIAc-C&-vSCb zK1OgM3(_0CuP7B{Px#~J?7_w}@_kxkyKgAt8fedx4&=uLN?ge*5qQJ6%j5Hs2RIoL zj~j%flGsU!r;?;S*=pVBqxT#q@upmPuzb~e-%{@Ztvs%KT# zvw0XCDI8M$7!vh|olq^-Z0q7MK|AeWE6tD`pfj`h{7$gzO;LT~N6IAh1iEny2J#~Z ziMKkO^!EQ~6cK3jtEPMq?k3bT|0lMORw1@m@3v=7d%SszmbT^A*ZdbJhsiGTaBgkf zkx=B*rFr>MT;*cnskeuP#?R{Mt+QMm;TnUf-G;J%)B%)w6G3zB{C8EQ^+OCE2X~cs^ia+^_bGSXo#*|O_+ogR=S8P= zmnf}x?lI~~L1#Mk$2PUFl77QEB3~T(L-SRCXIxNSWCe>B@rBnpypFP_=0%NF`!y5t zd4MNU+jWpFtv0g^QQp7!5-z7XE8enEzwdg>vfBM@k`)al)T_n(L|6%-d^_@D8qV91 zdIIZbZexG>{XhAx|2*d_PU2%CpU|kZ;D$2}7LjgpX(Ge_0Od&s3yHgIADlBfQcJcrHLAV|34i!CTq&!X*w$8S;0jR{5VM2TAYK?jD1-h6gW0 zU#kG&eLB*_T2R-T6KV-c2;s7EML*AMSw5jY@FYew3|9ml`#NtN%HI{I^703~TT?D8 z_Np4U0acY4k^&FAFoHmtD0yMV+~LTqB8qGcj-d?3k6|qV@Z0dGNwVwwTo5L8pKX99 za(eMO-ZFN!tO$vXL$*^lF2AU=u0&NbS-^fV_ZbBn>V zOi}ev&lU_hC(zm|_Bvdd9n`G4$9D0EXwTMGjIDSraJ>2e^*;&#FZIYe*ejVxF{Mi1 z>_6SnPf^Tssl@4EOp^!~87DRnS+RCmfG<<6KLr5CM)X45mXHM}EL@z7pVF6ffcV~n zMKlAT*$Jw}&!;+t(f2{`&aGWsR1t>qGnag=_1Wh1yk%DNGOcQlVL@v=Q1G6avWO zUI9%Jbz6xU1*f5RoESAz{^8|Z*MT;O1uP!Ljev+35M#Kw9*iM{AKr`JOCcJm2@^_ z6RI;HJ1mn(sXok50FYh{9l_oc^6{O3cic`w^)V28Xc%b1SzdXQpl+u0YOI8kjQ`BQ(ZVh6w z3qdYbatCw7oA5BZKEU{#QoxE?`2Agkc&eIbyX4k;K8}DQOSVOCBi{@1>d=~)IRWpb6a4HDkJKTuyO(#v(@^1=_)*LP+cYLJbM4Xx2Q&^&S> zhpgJf_igqI5N{)1g7wv52Gsm5j95m=14ahwBVeZH>XnOcq-NKINwE|zDALYI_OuRf z#9k=y*k9{mw0>m1VBv52x-~%-6PjdHUv10di>vN2F~5V9eqD<46bQL~oIo(67kAd^;ijgwkkjCvcWuRo=43bueB_a8zhWSv4|Do0G!PLi3+ z5aXF(AzloGV8>ri{X`Zf$pRG~3{MYUTM$tt$zk>crf^xJs>}MgTGwY|`E*+sIZbZ~Ue7@b%x_TwaIBz@x4@szJvE}3jJ+_mJILN&!9f&!baDSQ zW1%K20PA(Ka1JCxNc$q9x^9uEO^2%8zfkOCjhTOh6=tjpzLJlPcOidl&apP>i%(h8 z4~E|avwc*#ZHM#AFtUi#zC|dfSLlO3lScL}cT!P2NZ-3ge-f z>{uNPB|=LR^)En?>HnJ2PLTnhQ!O&?h9F3R`(B{6Bjb?1jIKKzUU|G7i;};FwbdFu zCua^SIg%q^df$vko%p--3)$MRwrjZ^B^wqGZ^K^2*MmGo6h+KT@k-|~@wi&~e+K>4 ztA_GIsmqyIcW>KAc;0X@la-&=itWsJpWy?<`eFs)k_rkKTAK%=2*+be>LwBc`ik26 zklihG4uG9!Ei@2%fG^v_7IBC&)$y}W z$iV4YdG|cZUb+Dt-qF%J)~dnXFDE_0!q5y^SzHr##eBwN6_UY!l#)iz`vql5<*oGv zSYzt`!L?<>!L23vt3n9_>SpW&gIc!l=dIS#*Xr7s#pWW;(Afd4!AdmM zI;+TtZC#Sh2JEw5-e?&MkM@XB8VVmC60H)TduZmAB2mMlr-kfl6%sMz_Dk-+-?5T| zHbD80nW~z%n`T?rsDv37TvJ)I({HFdnm+4I1>t435>01AUq1z7fo^%VXDGx3rOXQC zxhV*3UyZ`^G?ymUm>if6;s+*e5~esOJk~Q41cj%t z0IfTfg$gdz(bj$YkV-tLy}-H_uD_hLR97!%%!4I|1hw(fjH<8=aaqGKA2KmiJC->Q z*adf&iqN;sSb6m5!{qVvK{Q`$u+v0#pgZ~nhiIeLWBr4Az9bh?QR%$$RmiUGd^XZA`=p_-T2&O`poRca6*$3YaTiv`Jwl8nbrE}K=RsNBVc~M& zacE7pYlUj)R=>Zw+lR5cbSgTV`)1BSCto^W;h(VP6a1Ht`6isM3&d% z$ckHF##^|WmS?56q%``eH8D>Rb<;Kp^-^Js4Cw(lhxwAwS}70(m~d!Ml7!+U&_oaL zeLW!EvXCr4QU3~>ELa15OzOJzJbPvU5z&mM@E~;+Z0~;-K%54E$$C27pVEeNMdq0( zXKVz2oP0&(2u2)Gv9kSm9w~Zh78s|OGQYjc5jlHDwKi3CReU^)4fegrie%@pWOZvL zt&~g1R9UA0Nt33IT-|I@)kGm?l_tC<$9awFt?Q@;2VS zjLM0=g#&Y}Xa6SMw|@%|;EJRfr{;zi*rDZE_vXErWz zE;GN|bB_mwm&JHfEUcJCQN$~wA&gseX|pJPwqZEHYna<(Hk{U*e;hA&Yl_qcF*Y$E zg>L{~!#ltfU4N<@9(6*g{PQz;*;sw;Uo0kB!&8ZF5_R< zd|eM}Pu`666gVUn zD5ZD;7e-cqDU^JU_>eGPa(AKtoLGz$4+y)vm^b8{mC)MWch2?w6|8hDCN;7sr)wcC zr=57rQydLh1%r(q%ucj*_XbEr;NG&NSlPr46TJ+7nC5|kt&t8QOId&ke=V!mYqYg( zB&?kEeL`EMuC;}iEnBziM7XUH)QU%pYpUtg{SsnG5d+=IT4X_mK(CH#VN@qBSShX) zi-k+Y@Na&Crki$O_ZSSid9g{KVIDoL>Wo+l5)E9F#Bp~#L1q1!{vHG_KP-;E!m*tF zJ8m!T^-}FKaSfX%IM`raW@r~8*;QHB4I5oA z#zOYxU`Q6Eto9Zy`7Z3nHZ{$pbB@LIjC$NL@s`Jff9;)dV?&v4#R65ygQa8lnP_N6 zf(Yr`#Udo$VuwX4eH6?&ouH0&%XFe4QV z(m%xQ6G5=IFH(L;*$PG{Q|#?t2XLB)ZL&e5<}v?id1m15_hY(laqnpb6!8Gck}gk7 zp~|Z`oW;GbISM-(A2-hE2AXteRZZ+&Y_9gTakCB5O>FwX2no_5uxx{d4)XQ=N#YDP zb~Lq4lpyMoi_rI0F?}^(Z~k!Ty`305`9`l5q;4UD%`o<>=4hhkmZa^t2z&ht;I7FU zXX->2_c=LhY~zvMli2--zKoU#caCb8R6JS(!7O|98c<#?evi3r%F@{WvA;=Sc499+Pr)L+iVpw$|OW zy(C#MR_3GIk1FMhacs|jHs4BAn&A3b?;ygJdsRJ=?YH9_o=g_;CWZY{U+M?YzVp&` zlu7O2-I9}w$$W=qoTB(~X8|z7nHU1$;4R&E(T$(%TJ?GZu7ZBRv~2rmGAs<88BmTE z3)|WBbp&8Ow@o3I({7Ui)yuVOqk9pR!hJI&4H%EXbtwd8OERZn2K=v5(eW6?R5W#i zj2H*eC(@R8$bjDzdIDXG`LG*%_s11z;a$S9#XJ;8WZfmjPa-}~x2G`Oxv}NhObHZ& z`e-oa`&E#$WCo(dll-~%U?@7)X>+J5EbS+1lR0TFT?DhJ?u9Br@~vFqyqrxW)Rg49 z6T;doaV9{}(jOEAH^VVuIs_=WUE4#99+Q@vP^15>nH?|!55mb_Nv5V0!?hcyho8ly zip1*{Q9K|S?gGDsp0j{c;p?xKP zB6{(ZPSD38hCbbnU2FU7BKp20L|M8a-kNjXf|2;I1Z8 ze@`L4qLPsq=`o8XZ$4lLS@>1ZF#uEJkgIW^p#h+?b=z{FDeI8W#|&L$%>yDZ{0g0T zof$9k4Z*FEOLE@$##v{?(+=wa#dYY_=PxRU?RTlImj#U<%$D+YPO16raYDH5=3P)Q zGwygQ))m&kfTeJK3M2)o^Y}UjyAsBk@nDg$z1l>?dP6kzFv@Nrx#B%J7>_^e04{9p zz)E3~Yz&R0!1@SRvSLJlR7EMKO@%5C1Ji2GHtA^@z7BIJaDy^?FS=HthJZpx z9Q@SO?*M}S7eZ>8|3V2f?P{^6`o&hyUwhu4LL9qX;(=?^@jY?^SFc!H4D^``)>4RV zEvQlgl)k_59o3Y__HB}Y#x5_J31lu?@0+LGz8|aL9}_nOv(mq7cOiStswyNx=kT(R zrb2Ry;1nR^2zmQHJV)wa0qnmDt5=%L<2M8H!ppV>*)k9591&UB8^Rpnz z@d)bedQw5ZAU8gTe3pIbu+>d9+d{@BFrZxObF4%&|Ej&9iSejFIbMscTZh9R?;UP$ zu7hqdCmGh@D6_kkx(7~PN>(a6yHwTsK@*a1o~;6bojSxsc5ZCuTRdpf{0RnO!*Ts5 zw`7D`aoeC3?Y+No!DI5#cXmr)hO}s)cFQ@>Y-Nx`kAxcUTTJYF@&EuXHHT&m*}QS( zP2QAIJO9u&iCAVHvjL<>jDj1Ag8}ayd>O}MWFoKM-x60F33d{u`{sCrw+}a>j8fqZ zMCLsRJoQ}CU!WRf_aUB0<$$Ha0ATYGEpgH9F>sNN_+6fb^4T;DB?pRFX!+q>x8d6i zsTp(NQQi}}#3Exy+$aYgly>3gWr( zsz3eY&VW5Z+SQ&qNt^~@QarTlZW7hsemb3D>meAjtWx}Wndj*K9(rI+6u&+pEy1o;CYyF2UOlR}7k zQ9U3-67P~u1Zqm>SaHEC9PCp#;lt}aRcV>iJ^G;RBr5=LZ=ATHPyHX21Np*{>y|RLavOED>2(j(Up@w4X=m(l3Xp5hVOhYRZ> zPE8HdBfZA7tt1O+8@S}x#2}{FI#BI{f&=VkjLv#`_a{6no9826x6c;f8t+N7UcSa- zDKm~+?O`UI$2|<$$HvR7xLFFpWjqY$st390ck5#7QppXOe?v(|e{j=}SXT%bOchlA z1rMCmh3$apKl`Xs5FlQPg1k>Dj`yt@KhY2yYk-zg96a{znr&IF8Iz8>!GDGP8`CA- zO}Laj$qut1v@qMv0vLMinj#AM^2Fvk4+N85%|oz~B@VmB9R7438xyL|gsAVTL{KwP zr(7E6zi#zPG?d0xW!^w@~T1!9`$G_sOq>bKrA}|RN9xdpK>YjLd`abJx&FtBE?7oC= z!|$+X?etXeD@G6nvOb&|3mil6i&cPSg&S)sRCutZySk{VfXND&bGQlI8;f*+l!pWY zyE*aef)?cq;CndI+T1)q6S2OHl;oDOf>vztrO*%gfbVs5y)~^O{w<(s9K6p2O6}bP zvleQ8*#7WIHC&eK`&USTdwS+Vg#JoAU+LWRg`6M0Mpz@Lx?M>)9{Q(8$u%YP!HNbk zD#c_Zt(LDzWc{ImP5ytK-S&oI9r(iVeHwab>GN)nzicAE6f_|nN`UaL&&8t+9l&^NdY-m>{dQQ)elJsU)fkI}t>gVD7OO#6+C#5$Q>L~mQQ^jB^?dLjSA*S) zQvAe95V83?EvVAn zSf;`^8pui{B4SckkkT58S_=A?k21(Nm;vcYO^J9KK_ZWhO7DZX|HO$lJdc@>Sp`5( zd)h3>ALo(s69*LC>D1nQOE$4!`sL=p>SBVymuPm5<6OrunnTmcuZL-DhbsY#xd!x6 z5T_DfY1o#;^r7cm(S;&ne}P(&D8!^tnpmwdL;x7xYg;@+>u`7D5icg$mir37h53QQ zz@cs)vYI}qidp&%<^ zy^@3Bl;G3c>oVMKcXu9DX;-A&8(xS!Xko&}e+_M{x%xcS=|A}_4? z>Kz4P=rc*dZeo;+>0NQ5$8k$N6;gUZ3@4a$5$m%23OS2(hcV+B8Jlh`q3P;N#onm< zBNXbgr-dMUhV?%>9+Di+c&|Gjqe7D3|NYtmsE&@g*MP32`o+ZG1#fqJ2Cn;8KEb&0 z@#%uxdAoDUD>ZK8${gc~&d|~gd>TzPhQjJuA8su@K$S{&}&2>p&y>th&q%L2ZFoyQ5 z>vUfgfIvA^9^S6nL>r5z>+xkxGwP$Q%1(<5P2VV|ai(9F7Hx2B{hHGZo%=L|d6uIj z3$qJhzN%hpS6bc=H*X#IQFGbt)NOJBw*BE+Wru)#^GVbNJ}9Pl4NBigri0!Gx|~9 zQ8}GX$r9{cV%?X`wa>M=8@{y4>VN-D4XeUT>!GfA$0D-o>yT7y3#aG6DwWLtPH>sbD83n*(xEor&(`znwCf>m!rCaN~02I z|E6ZS@Qvfwq+3zQ0)s7Yf(b#m?%Fo*1@}*-lYEHM+)v0vuW=r$Y8o_HK3dchSuWCL zWLnlfH2P8zAz)~=)3#_mplD~@-F-`WkV;^TqKx1^u3BM9(b$Au(AnR6mFK`fGc@FU zPQftb>vp@`g16r598V1?8MC{F#?+AW>^|_T2KwYJ>B;JO9Hlc4a}ATLK9b>I|9EHc zKfWh*fysJ|AuR{YM%~oFbpr-Z9;2FP+$wQ$`i{#llC@$SoE5sbH!oa?IXDqHq#hhK z1Gg7igX!MKy9uq(XK*0rB~0#!?pcGgZ@$!Q{E5dFYx|+e8QB%4XS(0zt69&w%9@bq z%jwJD0Q>AeLt!ol%1`LXxL;rYEHrFRgwcf-RhlzLpFTBxPN^U!9RG@U(Oj{kKh722 z=OOhEq)z}UwQ@1-Cg;TX^GQ`l^9&;4_gNHXcQh)O*HHBqX!WN=&5ev@4CUH%EyvuJ z{}hRGM6GMuW!RJ3WwdxYzw=E1*j?PF^y5a}i;~6IBt5tn49;a?-SL#Ew`Snih4@6- zQa6N!F1Hb{ay}_Dw^*=S5x{(^?%2jdfnf0Uk#k!xP)B;Udh!^p*tnXC>=7?_Xy4_2 zc`HmZ1j^nz(fD*3t)aaKdi~|&affrHdw<|PhjZXgYkR?Z(U0Q~@f9GS8erIpS4@F+ z^68SNCaS(g5DC2dbnd9=_f@#a4r?x3-Te8rjkf95TLry2-zoWlNHRienm~{z_e+o# z-NX6qH{+$%vQ^+#i=A=Mei3-Vmf}9v=EFav5}P5vw@Ic`h~=hS{udJcCH!4dc$i^9Mw@j3F&2nV)i-vc0D=T0Utit# zdwmYe0k{reznq%D`He8hVN4aKHZf2}z({?yRJ~f2_@6%_^`B`EV}pSAGlEOLDT@=u zOghKt|IY8=*Sy9z5A4B3q_3h$R4&!6e1vvzcC^)f&*RMjMqoegtn=F6I62(M9q}4u zyvFgLi^);L(jg2nD`gPnOYIMb3uvXqE=Vv6$VQQ4kU;B$0;fn;@NOjdv#&5D^pcP| zUn?;qcffC=7#gZ`6NT|-v=u%1AZbCX+!mRvDU?Ll%BIJ_=^7EWJ#mAq<54d?fh4sy z!PSc)1#}ew8tI|s+_ar~aFt3sfVDKcDu93aQ))}c-QLt`9!pclCTF#9ax8JLQvpfB zQOnnDF&>^(w0wFyQvangY#c>^a^8GKv6PuWlSd*}x{cQ<-U2N|bsAzw%k}tIEwFhq z+LudxVvbN>wERC(fQ!of%l{rv;)-7$FnZW~rI;3s!|9EX!-Oo@x~nKiA)KquHM|tN zA_{yO_&<8UGE%Nl!KE@}t@VPDLf9wo#Vz!45DP8^nTC2C;0G24epi4z{5tyMk3xbb zO_a@>w{Hd>7aj-{jmwg`h?4aO%bu^#ZXFYFfR6pZoE?UjAU`q!nk!XsM|~H( zlpIupX~j5|Pwe{coUw6>`k=~);K8VLN-Vdab=NOHCKL4vtz8#C)Sm8A;vUgN)WH;% zC^?1%Xd0N58x1F5BJT`2Wh2MO9J~b9y)hZOog#780u|@D;-oI5oU}=)aAnF_`Sa^U zNZe5y(>|0Cch8u-)mOfeqQ>YQE%cP~#-S7)37QJxjA6Y@x&+`47J`}9 z$j^ORNO1QC@`yJBEOMoO-_N<4^l1K0yjFC_ z;qqz_52QBG`jass?AW|n5dI^)on@*CH_Z?rhw`wmf(>+-^|z+E&UfdvI2U`i-by7Q z_M`PUv%A0uLpOLs+!m_sxuF}*c7Hbw@KF8^25VN(XjWHnN#BeD$+*8Y5PH>9xi44w z;%e(wixv8%)XNhHf@Rr0!EmG(LCO4ejt)aoBFH;{xU`?d^u>+V?{m7Zyb#(zj*XW4 ze*-G4-p*7&7Pmxdt}=DBZldS)E9rn@=t(3m)5P;lnqZ3ogh%I({3)>B97cH?i$G7D zcQ!48KAkMt}V17 zMPa%x9ZV(8j=)A3Mm+uG_f*g>v;DmJ-Vc9lzn?9Gr#ydh`V9_+KR$z?^K1p6(4CMQ zt3JvaKW2HcSU*yhP0?`^({`+z;HXUUPTZ{TyQ!Ci`avRytaEL~<3?Iad+%P9uD#!` z*`u1Y0Xt#V2tOD4q)aqCp1(r5A#p;hK;jpqIF!_k9wMbRg1lX4{9pfr&kv7?sE0`@ zWKZTdK;wD;c1_{H9g`|F#W-gx9Gbh{75Ek|#;{=ypX6~sXK638SUg4Wo5WOJy0^)+ zaX{}4S=YFxD^OKf)*h9zXU0=3B+cCnJGo{IzqbvVXJ@Vg{k=q1rG_$QzfoDO8sUw+!s9B0xbz z8(|4@02e>T+#<_E8<!w%McP># z`Q-aG6fq;^cR%~@+zoB zk82iwT=vFeTEI%QHLRF2)-IZHsdyrFG~6bfoqO>YIdVGD!wOpInD)DNb7YeXhdl zZnCPlW4$!r=_cqv8>;BKYhVmT5=;VzSD4rS_^2{3G2&kh-bpVrByT_UbOh6EiLZ~n zJiRSy5qslY=n{5(EMUS%XT?iN=oZDNH@qauj;}RUvv_2bxK#-F*A?4M71N+I^q^XV zUMI`(d@gTr@q5~MzDH(AY5&FaCC0q*BR}<4!c324@rOkAgLWHP%7v+`{Bic6N1iTy zOGyZ2#(hZOOehvxGEf=BFqtVVt^g;dUvl9|(nn}5rooznf!WY;=~vgO0^S%2E}yAQ zCmPBRLJLPYh*gI_AG5PHOF`N0E`*Yp${&WTh28^)#H9=kY75zy01LCQ*D&!fgO#Rj zCIWQwywR<7irXdjqjYvxqKu=zYAOJ z3~OQn#QLs{Pi#SX0;Y77|y!?Yj$Mz#`cD*lN?s=R`Lu(co*U zW$|5pjzSkKiOHJeEO<5Xlfu(htd-zOl180dF*BV#l{rx^N5pQ7lr^vuXMGq0zrgKM z$4p|#FWZibQc0sD4|Q})rZ5fTKz37~e6nO*FQ14&<4~s~!D_V<03(d6zqn`n?=>De+DW}3*#I;@+#{v;nb zumE(s9WEGL(@$xHY0d>IijV57Mx;9C7QZ}cp)2e2xb*^+LjyP--0s!;!fBw(+Z zTiXi_evM><+Ljl}$dCHuREO?tNooTQosduzb|X;|?D1DtuR1U-xj9Yv@Ysi%ASzu% zGQ^AMwcrM3s}3^?78FH;{>aTp?hv8uRu$i}#yHSR(2iOI!~JBuzzB-iYDHFMyvtG+?e~vjbG>xA zmT$)<&3J=&M~twTm)ap=vF1C*d1vcoxdgM_?IVa#E4jh>%BHQQ_WI-nH_4plhK%yF z74HU|j|Id9H3dcE#gRIwngy$o0yoWNMEBqfeT(}+gvU}|+wJRy$Brr(bCFWT0z#GonYo9QWXvWf&5O}|7QQZ%gr}`pc%|0D4uWue zAsa&rdqlz`CWS3@uvtg4M}(f=I#PDEq1gc;Y;cm_es+KI4LKeP#Gi#;D{lmDr^d9> zH^!a7OEAp)b=Es`32}aqXUtW{5jXc4Cu*J{l!^LcHIfba%QkvYI4t>Hp+wy1;*(p0?jqvSw$p|NZfp1vfvE z<8I4_O?P0_kqbH=5JDt&*^2Dwe5amLbZ;6!(Xz?3(JYq>DFsOc9@46%2eCe-C(4)L zlJUY>!4Zr+mrq?~5yJ3kBQp=}uWpK6m4MTUyUKU#=7ZoNdtc^8w5|#Soc|HiE(33g z#&Fy>19ct&88a^4<@FK2?PeS1!>oT@KIF_`dXDfmy1}29<$v#jvO# zl0+3bc88oCs~PWhidI`aTw~@~PJ5o&)(?MoaRMtSBeEJ-;JQ<&i!9gVxh-b))H(+* zKQAFcgJ|kOu(;UBv#Wr9L!~sM;J;vxXo>=u=WA0|O8OM#1l=)p}`Bq@0p zMeOb77DU*I@WHusy>bEREiJpPZQmX?9b!Y^Z9ai}9Y#y6^EL!KMI)2ROz}irUmsys zvyRDv^wgQFx?X?N3i38+w)}vv(GNmBM!lD|Nugx!9FQ2ZHSE1>n{O%a$5o`2Xa(L+ zLn(!}`n~C2TZ-rCD;uiLBvM1WAQNpPF&cN2czt9H>Pex9*9>5bZUIep(e_6`JMi^< ztlE}mLnl6^J2|UY0zMiEmsfNAd&L4LOGhtfbY8WpYSj2*1zP%In_Go%0x!H1V6Nyv zKd?+9G#w^J)j~(Zb9H5y@u;Q=T!U5Jua-X{{h{P>b-buz{v~w@BLG{N1(9JCy40R| z^UW*-xTwv8(m6FW4Dob(hPv?HOUaXFBq7%_9iz3Dp}!uY@;^Qg6wkw574wEBFdnSQ z$fJ6gHE-ocOyOnSp;b?YV7sn6a?i^Kso2xQB-lMsm0eaMsvWX)1={;!37N$)W=BzW zv;a$$ZJ{lw8~ThpaF7&~b7U_gb3H!0O8>C?b3{(qh@~wJCPpmk{+BI9tJ_ zG&Y0B3Jqb+Vbj92bKAs1U`|s4erT9^z%Y}d^CMd+J5)n4XJhDrlJU}mM?y5GaO;O7 zZUlG=XLDO<4soSncNnag5;YFDsZ5zuZ+0ziSb*9_E2j{VKay!BqA*#EebPzk0&h6N zlP55b=pyky?;?UHwVktkK# zD%fotvM}8KxZsMP3qTq|x1l!~2bhBo59xaaxRkqqgVzmDN6IWY8JsXxE*D9-`o=r?+afWF1h}Oo z2zCOK-`AYQNas@rnsV5U^IG9zmtj{QAw&E@oY(wmF)) zpgCc{tJBg0^MG1$v%^rd6ymy&`dE@e{56SB7icrOhI*(y;wWT+6ZA^uride75_(r- zcbAY+YM^}(7OLXxLop47ON3$3SXwfj`npojGp+UbzJCBs<3+yHwo{L}Wn>vDD$Ba= z@%8>pKkGPI2F7GsU5ksYH+nDi3aJ$XK@Fa?_j*6(8s%?*m^UJ!#ykUcNx~ZRu*vvo zWfm#-nB``MOrom(n)_UF4aG|MlyE>_-TEKTeum&?y$^$QYI8kZP6;03R6UEApQhSm z4PNByydeYk`yUrl6iH**Bj0_osZK*$0(jkA1}lmKCn`o+-bVam$ihwu7}gbse?PV{ z%6hMn`mQ@TBf-ir52~sw{Mm#jR_ir8vf0kTt?|ffp`}{nZ-5HzX32m-PZ#X;9lDvp z)DUW&6g@4F9V_h+JR26=b&Q}OasV;$?Uwh86gOCWB|~}JYA9FB;kZH~+YYo{LP6iX zA6szAW4&7DSBszseePoy1~OB)NGx!<8VGEWg}8Zyv3?xD#T5En_TV-9l1U2P17ah7ejhJB{hBUKzaFIBYiAu(`O?5M6%RiPwS$*H<+F_?NO}g_ z)a@j~ZWqecc6m2AfUc$=U3(9njlLpP^m8D;FWom?n|`hHhuAYMat5`jl{0lgviA7w zO0$rs7&#SXOoo?;Qy3f1JlkFEpA>i_-DT7XLfNIOwW?i&nw4X)mqYqM{9*dEJx%!Y z-n8LvhjOU?N-JmqftkL8`KbyDv=|%x)Nu_gJ;c6cto73{SVyb^OJ4#MzDjaR(7-Zu zrQAUqr11u-QcaKbI%#fII+udHXKgJz8Rf0A_mx`WzSUgq8+iC7!+HG$_jHRl89oh= zxbbe>Cx*iFaoWhymespyu#|cghD37v$#;2inH~sQ7HN+w)z>Vp+*`D_?6SOoBM2-HmnMaDnYL#%56j#*P&wmJiEQaYgIdkwEJe z(PzaBT;+xEq-!4ZlkD!*lrx8eE;)(1VIHLbw3{H?ax={Z4R(^VT%?fHWb&-K8Tkk2 zto2@U^IKkAh@Tr^FoeSAv-6G^kSI1M&7pB#sE|*vPJEb}T)T**?othV@&xi4mOiyb zzwiGS>4S@Dn?~Z*qow9fK7v4U9wf}YL}@UoyUl{Yv>+hZa+MtHk5xbJXF4c@@NQV1 zN210y4kMGiCTZ)@9rQh;JH``w)Yy~SxB&~+-d2Ec_mE`0(Bo~Cq;ioxr=CK@u#NG( z6ADb?8@P)5PsILOv{+MK!YBNd_e{4nO+l*eCj#pGrxMSa`KZ>#Q<)1?h7G z(9SnF`HBT-sns@JzOXk(O19Ydn6Z?4sq#lq!6^3!*2-AOIVlJxhdE@P0!P zX&k~X&ADIbd4B;VI3wHUE6zgmaLIiFrTLLQX$$els^EEbZ*+zI@Ow?aiA!$&gEb&@ znompHZQ94@qvFZpg`Td#guVqrgxD}x^49{>aLUX0yowLV4wgeqqq=^bI{!MJ?}Eo4 zKnWLxdjUt>%jMw1&@DwfHtlhA5{HITJMF6hF;yxU9voir8)D-B+{rB^DP@weh}sma z{3-J%*0-5DvbH1qe*6P`N?rK@u>N1?4G)F7Adb$=^&gT^XfGMt*TAkyF~WMXLvE4SiQ1DLkqP|(uQ02bpSIGG7eE8Qo%hy41{ z^kFiiT=(qL>#nx;7ay_Ra{&Zyn8Qx9|pGseggc2wv zX|Vj0lrhW0@k+Q+X80PWu*UgilzaU$cVRLA**_$D@vhu(Uia$9GQ|l;MG*zX)zhNm z4Zg;aFVvUn*%%rAAYSy}G>ZTlt3_p|6UwNv#otkEH7z)dl^beL@3*xk2k%?N(Crm-0I zmd+EWWe9j55(!~GKql#%GjU3k6&fw;$%Dy_1xtE}M{Vrq;fFaNrc*)x)ou}Any>}W zh4C$=tL5ys_zpMbI!WH~pdF5_$aW2go8?1_*C6%e`cQ=Lt<0gS2WWpoyWDT(>q$0m zar=TNtzNMVI#*H1XGK(GVet!zn1?tZYF!ge*#u%@V+Ex(^L50X*e z9HoY)as1fl`fdmUX`i`{e*&)}MsUvZm_4aVtoK zfYtbkw^jrXdc`R16MCZloo}8SDjg2%bz^mbD(=`MJ0dRkVbQWOuXO8i6VIO10%9m| z+NWIhP^Q0+jNxOBnUqL)ttsSnzywyH3qw@_h-XlvhK$8Xvbnw1S20McAdXlOZXb5TEo1vl z^(t_G+Gx%H0zo7Eb#lh%6q7>ldhu9jzXZ4E&k-uVO!T}~q^K-2w3AWd_l<|Jsh8Tn zYDCbJuJWpbLoxEFstEe=f9b&&D@bmpb!AN0i`JEn{0!&^nJelXG?>q}-o0{9$d`i) ze=wVOCuhWJ;w#h9*hAz@5GluX-rsmpgbH;_Ix2s#?1~Ectb;z4uDuW7LjqjC{DI#h2X(Ik(_Z z?mvgXWyLdkd-z=)%+3?gDkTv{6N}Ni+7N zWIHpXSTe`TlIpfl7`}EG51#6_hop{=DG32aM+Y2O*yRcD>$ei3m{#?yf=6-skUtU3 z>BHZk`}PcrXW-tauo$_M%X#y?E;)kxYU}LMM&VTOoF}4M#i4lt-S~78a%0l(zsQpk z36J8F{diwCh8rRp4p*YMc5)g2+iS4GZzb>9H3>;>_wUw{MX zfU@|0WXlsD?Nw>(*r*sHW{yh#0mm1XVKk(66;#d5*fuN!fFj3EJZQKQa%&*jLuZ}r zkapb=D_Mv&>hrS=x#-44XQ<9#mNswn89 zcI3v#yH^iBLF7 zF?0zYBdVQ^xjL=92_FF4Lo5~OLkD0S6yp_7*6&r>j$%Ktel%`3GD9i+;nNVM{9%$ zk(=0@RfT+gX1so4t~EGUMEF@+n%)zUHd_AyOaW&+11-;p6VF$~e&EI#MF_^C4YR_j z!U1Ts4@9k}hl@cicuWF2zkDA`Ur;)M{DKe1_6h?)t(%L66)avAD1IteReMLwlReVv zpkI6_I$U%MuU%B)T0yk+y5i%j95Jn7GZiFc2DZj0%5UFWqsBDKqEL(dp+Uw3C>tMn1=F<+)=9j( zo|7u^$^oghP<(dv4+JrpDtsrE0J&92rD>B37{M7f(mXLwv%@Su;Rr`)55BjohVp6D zv&lZ?icmhtbvmlPuVMdXBXxH}`gEp;Z1RK$(_b)aYc2mYL#3DzD)1(X91je>#3lgu zc!H;=c@7Ruu+p=Wn(- z$5qSni6D^GGHl}=?_%bLvs?IB7)a)l;aN2XA*^{MOIkH0#58_xAf>Cq4o`C=wyRXCegW@%Q z#-neK8=LwZfEmH>O;(NT>Q47~79>cNMJU1z6O1Mn3fmhq_aB#kpDs+OvwHV~opq7u z4LO4)3L3qe%eIWrgpyD4&R*hXMgkvS6z6b(TwqqdMF-9aG7BL^~vIj3hrtIwO#T1}z ztp*n3U*Wqg?#9m3t!}2?tm6y`Y#D3_0a}KZIp#6yRj#R=H`J;`T$qMQ4ferHGJnU@ z4f+`L4ZoiykXY*I8#I$}#<6;;NOX}vOIxi8f;wGW<0V{P2k+DKCX_#kK(3U&{vgwRr?*y7MW_ca zwfpjS)Bw??g=`D=qN{Z={kl-FMhu0R=I1bN=h;aqD}?XBegZDV$%&?sj(Nv!CPj_V zEt3lWJZJoPDIAx4zVbS`$o-i_^Ji2yG3e*JRkD~1d7ml6F_z}saaWD+-~FYE6zV|W zjV&S}9g$ZB#0l;u-J75lP6}eXs)dQhAgH*c6Zy<;W#BHS(xx2aYR0e`9mA0Wb-vEt zM~RjiaN?55tr*pi!GLct=@aa7m+w}LYh}O_s%-Y99yAVS0H5DyBF`O#kGUXqMQLsQ zOqBM({2--wD}AX25D+eLvv1$G^YqR58B9sBMAdD=*>`5$(Fuc>6O^DW-c1|clGpBK zJDJss?8)JhBMeSd=2H9bqhnZ7@>TsyUS}o4>fbj#5;5=3q;-;B3t|ARzgESRp?&>8 z{JyyWqT~QbMIS*{kwN7qdmau*PpHP~kEuwBDm0fjQKKl?T++_viv1^KA>v3m5t(iC zNW(F_%)HJ4SAz#^BEl249?9NE^lCDF=Ir$2S&nOUOv7bp+AbN1BXABqk90>@k%^hP z!_b!_6`5Me8rNYsC37iy=Ksa4nWH)b{psk}OdqYkhH*xoB?~mWVgzb`8W8+4rujM} zm{D%;y*}zD$*yd%qJ3J&m<60J7a){ufUB?BrFn%N|vijyqVw6=U`%~0j9=o1`Ug*C9cVs*l--fLP5bK^v_Jo#8L7rxwJV9F*JJLFoz|1y?uk0^l z%lz23Pa#jtgSLMb>2Zw}p9Ecd6%=L;VPggUc8gUhS^lyWen+s^5%4z;L2`|FTYF$P zsO!;HCAomggK;YJr#znn@G!EW6FRi)4S)#DtTn2{THKMMmo4^1$r@*F>3%FY9w9M8W@ z$DsPd1! zn@yGn_*tZPn0I9JQ`gV2cosWGy$Zv+P4GG*fj$3D7A<}88s7C%W0jRDo6lOgn&%4P z05aIQFwE%So_?08Is}A$G0c((c-x%XX}PKUYyr!%ud5+f3*yv6umd|l@JU;|A;s5| zf=FY$>c8|mh?}%m;Q>1sr68JycEamc*r?bo2nBVH%c+zLO9m)n5HT8qPL_uDCpmXQ z@Ocr5QlHf{Va71YE@e}US}EIh$IM^d0FxX0>NC?Yr_g=5$b5~wf{ zE5GT+?DbJ!H)~5h`dJ;R<|=R$^xf}FoFN?_ml}*jj$sxV=MiokpL~1HY7Lp!uJ=^i zHQ%u=_zM5j-~N#}qsED?P6T^6Rqs64?BzehR~(ODM%BP7PJ;PDt;>%c6Og+_G5u*H z-UA(p73fL=HuryGazZjHTx;&zq1{_Zwtm0-hv9@U4p*6Ab1nZ%~K_OVzOUYdA zmh~nyV$^9PwFqfO@sd;D=0nDr0U;GJUK;4}I|Lt3g7j*AA{4@ zGGNRORQ9spBN#sZWUBq1kB>_pk-3TO$cbb;>OKk~L2dO=&;izi`WI@>47WAgT(f!i zwtnr}IGj6s+{69EZKwA07f>?YMNY{^U^e?~4I|U0u-g&|i-L~4vz^wPR>6v@P3O9T zH+F`Bz0Um2k;T0aY)-iPDib6V9w;qW%9 zP6y7Rqp?1JboL7~oQxAkan+zQ+ZHt5dW?@CeyBy0NI`|@nysGP2mdtZsRTM5HMmi# z_8JlxGy(}KgCfopf%8$d_XoF{+g*XvJGxp@Y8d``(sMEeFrLRNjIN70%LGJsLFM4> zLtx?~FCZlSJ-Tl>srj05UW3kq0873LxgxG3*5a8p!zF^hjz%1e4P6ZNJ*=Bvbu?Wy zt?dB2F3^E1ib*rxL(JIs40I{W<2%mM@#Qf0QT-gA-R(O`0X_qoM1q(xN%XT4FYoM2`nbD5d*+}am`(fL)HhYhX zP0;(eg}f+sU3}o9vewzZ0#!Cr5`fhOVO4kl5wLti>v3Y{$c)NtHWWp5awvn!C3q3= zb<0~n=gi~&Af-DiYe5>-w+H$f!(p)Dd1ixo%;!x~V*^8^|G_|);7`g1Jm`QNz~Sf> z5(Da(5l)QHnvfh^oVQrcwgT~Is!()xBMfA-ZkYMpzHs{JT(((bu8RjC6r*AptHNQm zokT|VwgWwI9#*gj;C!!x&wTU{{(1krPj6>|9FV!_LkzIntt7!i zBfEL{*{lWg2V2RGgGe3qZNmUwgg{s!n4o}f>B5K@b zw>jBM%-M>tCg$sk9mELft^1<6th|6t0$^1J5Xv>j&@D&wS;5qiO^iBNTT zWm2ftGh?$X5!49)ex9WYOw-_q2*e#*U$|An>9hbrXGbJIwK@}|;Xmc)4rbZxEKg&A zY*RrB0Vn*pF(q(lF}73JYp~K{1i9X5az* zpKIM;FnlTFW}$J@{m3y_3E#xW;GCywzXh~x%wS=CZVV__#7t#2w6qGvyr25Hr7<3$ z!_3UjTF@rLOEvzVWPVFx}t)lWHKFONjL9NOK~N67zB1GHXbi3tPP;0pBm5i7I` z8sou2n9(Z&>D^ildg5SjsU9tof%ZFRs6!WkF%v=Oc=+y-4SCF7d6u^=cAvh}?9uUc zd<%@?)tqUyL(FhgGG;`-hLIP9+b|-2q=-JQI2HCbS*k&FgY#!)m$nia&-;7KZW842 zMyGQEc@h6yqYln*cDC^BnXb!kAe^r@<*MZ1hzpNX@u-mq(F2sKsjukl@tb@ZPY{Y{ zD1~%M+EJ1p0s{gLNE2}#4n8X$i}?!p?zF5UPSCDYfT6ar@V%GA)ZVaWK-+0NtHVHc z5IVR`{{pR=6T{JhlXoyRC6LCMJf!YyN?)+FLZ|VEc*QAlDCR;k&K!e%`D!8knh=4J z;u-n_q!;jzHFiIc<*&K=8jqq}_Io`%3#Urcf-FP;uK<|oyIYB25WS(ybV2PCXrJ`z zJMg(@uIE%qh32cBt-Q0Qf9Wu?^O=5N;_!sRF7RFeP17e;DmjnM7k}U67Iy5Nd|xrXrHa`qRsOFh*AkO8fvsr38b?zSc0F` zM~h3T=>WLf)gaol?8}G5+sO`7WWT0+UC=keI+;+Rlu}a5%)H{CLG!WLIyo>yHS`lg zR`8`az~fKq6O^haG1X?^QW)~gw1+J%Ja|d}h)>0>Z$?Z}TyN>>WkA@1p^naapq4Tm zqpOVc@SrtdAGCdftRPF6UK{mRwKFVB@0;St*!fYa7Hze;N}#&iWo*|hMBO!-1#F_M zf`>eRBl#DX#v`9Usvv<)fdPBT+AITkiJ(>v!wb^$QyaDLg0T@5zOKe{kNay4uxT%D z$1t-iXITv1{~JX3aNfPyd3D-3@KXmZCIr^dZD!ihI^9-sjQ7uC)n-h~eVEX?j6?bf zt}Hj>v&_t{^q_tQq1VN{!95n2hnqDeTJ5ht;w}{uHcHwV5`5}j$fC8B*W?ho4i8T$ zYOd;?0cy9rn7z1G%IbnlgAN$}^H$vCiYC{VMI}Zew}-H3xZKv8M>+R!AZ@p&=e`ll z*R$x{bi*RxpVP^y{80?;jp0M{k+w~- z48p2MoMlIIAwY-9ojv{Q_=I}jNn{3~COD;_0Kr*7*n82xM7rKdOJZyU>Ck{ZlcCgb zfx}8UYx;Um_CJ`S#n5FB23ts9$VsdyKCK zSGaqJDBbd{fE?|pLN@!@u7w$3Uo&`6k;!fVGwerziEWGbH)qZ6t1j?wDFpaDE?Vg% z@L;Ii1x7LkoJCW6+=GLQkbnG2hTCeMY<(PId>+@BF5{7! zK2Sw1>+1yqz#sdENJ=SY_(vQEGkf;oDTNHonSl>~}O>Id#C^|szJWV-ysuksRIVG@_2AgX|JjkV=+ zYJ*}=C!r2f)ckR-K{i|{jDaB%Z~4yAL-S^cqaH8vfLX9d& zHIZTWDybxuS&mSq7r1Oqc=}7P$A{6waGd@YJwL{^!ZQj$BfY88u7Jdh7k)xMBcwS& ze{}1;rufwBEVXhH;0enJ>&i@u+efS=7@9&aD<{%qTo+W-;m>D!)F0=?zy`*mHtuXj zq5%SVa!V!(O9H~6gFzAzp!|tELmaL1Ywwo=O&zFzzMmBilZi&vi$n9?H-n33 zTdR5!Ku81VCv58HrfML)(VsFB#>*gH^=SI7MxO5pU*(~Xlp2LLr-hs5&Y?1PhkojS zgB{-+<<}+KH9ko?3{+cB(=M7!dnf{0Ye>5rT3xT#DhKw^nn~9rY=VELsUo@Q?tUTK zkh5w%cSfBZSt)`4CDemmNx?Tqkqd3mkNL{ZNdk6%Wn68-E0y(FSJSe*a5ovdZ{zgu zQ#i(Yr$HF9b8+||ww_~_mR+IklxjZx32*Tu_ykVqzp(aggy<9_e??R=DX&A2ol%GL zF@9bSw+U#ZVd8UrjJp|28a>a3>9n`nn|5`PtY$m0d1`CtA=-3$ADU1G-JH`tgt>#$ zpIMjRn5fB$R;;+!ZGM>&hR$Q&dbfkNtqR4=SB2RHv1F#r7Q1LQN2ROwV2WOT^DtAC zIPXEh5FV;X7YUSb+bonIWwh|ZqDi;GKf=qji8;^gr7f4D8OFmKLQ+TLMb+h%mP=@W zJ7eIQ|B)Q`w^iXP?uHy@}98WY%2-s7~I#V&i#u zsMb+epJ8Dz2}JJp2#g>WyHS;T&CC@Y5$wIx>B{3zI9ip^+Iv+KD3m`R!*I91+XHq zgK$a0P`g|<(#YJ}9w*S)hC*;8v<5Z)-%_OEqMxsDON5Rx3{{X30>C6AX-)Wf?|G)p zv1_yMM#QXLl*MMoCMIzhgy#X16VG=a&1Zf75j18UA98|#R18Zb)eW-;V!G+&)9Jy) z{D^0Hc;sraozy-gkA3`Z%-JooV|6It<^8A5X#2SizI$xXd&Qz^-+%4QGCG}4f@B#2 z8&+23ckioH37LYUrE88h7kl}?=b-Q9C4QGz8z@TSoxrr0ZxrJz?y)f9!B$JK z02ptmpw4*aVDNc4`amA0R#$!%Q4rYe(N|P7BjMPhg@J&9vlLS^qXW!y9#z%j#kQ0a zwU>7MqrDNNGEommi?q$Cd2? zH~dDn&T9>J4>Wlp-Oz~N{ApagwBs+yc3y;8%L-J}hfzKi8R$fPs3QV@_6YzkkAU+KPW)oN({xliboj}uh zFgN7)FR`eBR4V`Wrt2vzdy=)gC{~F;JaUyfs(W0V%T2U5w$&4}T?uVlR8680N_RI- z4RpOC*6-8l3fr9NYBmdLL-#xhT5lB_9=>$(5MwsiOk1MBu)&W9=5C9>`~deo6g%53 zt~GNI7RA|OYZ4w)P&ek@HFI>GU_jm)QE28QEjN(m=!ii@24Ro}gb;&opE%HE9x$;h@Zc-fk!Wl` zDAU2ARw+OmZRm@I>gT*vkqmLdGo!cqg1ZmZBtOEkC2431I0-I>3mu*moPq7F6tGQr zl9>?PikQcrj#F?H4P_QsX!mUc1{R!88znVQufr}+}w-EUI*)~n-JnmPDilQlH+f_7_hhklc-)sFY-u-j|vw;W8 z7Q)|RtB&3P6*yePkd?Vz5Ong9(QrjztKkiLEcbR4kI^{;t<(xZly~ncjYO($nDk^Y zbF`1|MF?upDm{4p3;G@Mf5ZA;d5|8p9nf1S;3}@Tm=%`k$?j6EosfTfl0GD2{Cx8&MkAB21@?aSEIhn8XSf)6+xo-C#w z;i}4|Ssm67kng|v=V(AbR_uB`;CRxEpG^=KYHb?B(f^jd1St=~KVg0RFIey_7R5k) zgZmS{VJLul%wotuqgJb3wHx^ccl;NNRbKp;WyeXg8`B_KZW)lACkAHuYVL7rVv9m@ z;-aCpCutydKwG_OSVyi*#cv2M5HnV{KQ+sp_Hm2TW{J-7>{rfNlq?yyoH+MIThu&SvCQ>2iK_ne)bRml+A*<06UG8kqey!Fu&+` zpbQr}jP$YP$|}2zm`;Bb^W`3xo5s~j`nnm*;!{2gIp{6olAE&{pBgl zt@>1{y~4Q7fw4ttvdbUNfA!x3z5ccSo*F9J@GXZcqQ#(m2k&V3_aa~CTF7}&wo zP>h(i<)IE*0PWuyZjkqlZl3Hsw+QRMNSVfzA(Kq!{|9k0=T0AH;E35vY|1<^Wj13- z0)a6sbc^|LcFJ|*U<4YGsKgLEXfsLpHZ=P3G2vdyGGGco+%h|TTOFQSXL~{x#^Qwz z4y|?7R-)+#PQB3!I+nNTTeixSAe^1ZbPk*6q^o4r-X65&8g60n3Ka7Fm~fJs;eJyg z{ao}t!~>1~*ZT<w{_UdOiN zG%{$v+gcld;+jx|oJA^CO`)xRTES!(=q_Qy2#||+6E}}L@}C>0j=%W!oJ4^@av47m z1iyT@oxJ!uqZ;&S5F)D9KbOq$Jc*+<-=Egq^%N2FK-&w?5WTv+AYhTG2GzF|_KS1C zY6!oj{8PKWEeflizOX4ZZ3UhOUcyp!(TLti&S`O2=*7 z0gM#0m==)#tkf{8B91+r!`}#p5s%=VYIsq?Qa_2r%P*cKZ)k)t4UiT5J17vUG1Z*O zVQSxkj|O^*i%VIy9<(p01DEcy926~LAcQYm5Jm!D=fIJn-5lATo+*i{tN^f|QkNwD z2?KNN1F%xzMTn^mMh?ikQY0pb_8iBb( zOxr#hOmk4<{L>e_%RU(WVZa~-ulevBxqO55XUf>vcHza_VcDTq7v;n=@XDQOYCL>C z925#R4S!y(1vdBbo+ck+lDTTk(nQs_+;h=${sw07^_F*+#R3Rq0iiAVSDw)030Wi( zrpM|2?NpL?Jjf;X^zOP%4yO%RC&`5iW!W`~4^+`zkp0_B{x%9v`bP62rdCu*_U29_ zsCm?p6SDwKXU##^_Q>QH_fWB;;w>jVPbR(^ua;}FxcjbG7Y2{dKrr{M)Z4QyCc$L} zzMgT(>RbLEh{JBS5uDZgG;`Q+|90G$=WZKzP$ewFXoz#Bg zGp-KU$n5K7;!;mtFFdK(pMi6oM{UGKO`o&NMY+)FmT&IHFz&A~IidJOAWzQtv@3yt zBC7BR5(88sZ%jd3*?;Dhx)*T~H8DG!Z$oD$sTG~Q93)rPsMMI@|arW zxbNeddjc+9aKD>9Xc{{OCdN{@ef<#wn;q!w0u%**%a-@wr^a^!uwpU z&!Mo^j6QuoK2UumRZ-{*;#+sVwh=f#eZ-B^#};b#OQ95wW4@wUQ?8Fc(9%J;Vg#X7 zKaTlKSF(&e5h-n3U-}jSTZ}*;CYVo#AQ~jmp$0XzjP%Fxf}FxYGUrNh{0fGXx*B)H zop!h8d?ukW0)A#Tl9^7?sQk`^^4~wZ(fUbwZeerPP)%D zObR9>h?HJzZvCZW0)@ETJZgOKIy?LQpW8qR?9O!X&|G&lfHRG~Z%&^{LEzOytLaUB zi#^w5#PxjYx)}BXEe=3kpcxLG@Qrcw&HD zOCVm&IV+f%g8y*V!aZLO6{6d2;(>jT;*o4db@8q@zw-$w24X#1w67+rhMvbkPm6^u zA7OLgwvjySOQDBAhp-LC5$ZRAHPB^keZ9@ni`39SH z=P9M}EuilGC84k=2IjyDMnfpd5vYu{go_CT{$%4Ze2 zOl*V^kZ^OkCPe0wIof4_5QEF)WE{1D>3zo;5o{crIoQErSaKD}JoC0=b8T>_+sG)v z=ULdf+1u4t?{cm{WYAkLVri1XS{CgC7+BjZz9%bpFj98Nze7>el_7_HhD=&94t+gv z3}ca5!bObG*6sgHW}`=9x4omwKwxvhb=zIulQTI7<8?=s*G^jpcYcHVE+rfI;=!Pi zhama+d&5S*$44NujEO=cL!Cg{u3zwRwG zpc#&fS?aIMozP)0_!Sw(xipF8GMsb42gScy;yFn2krmpRt6f1so4D*lK{yy2o$Wre z+{4+eMK>ZZ0>DcF0{99v9jm|SlBW#9@|1}7Q-CS7fwbg)-(i>8Ys$A0V-my_t+$8f zIzUGGp2ffra3IAuw%g89^`qq&^71?M5+@>qwca%xX4a|GG+zfEsNy&CdPB3cKt%%! zbT~x`93(*&@95cQ5YlT`h%?~K>~?L$a=F`-!D+W}G*=VBDp&}KxduseLTDdlMw>n9 zmGQ8Ap?zNu(BWZmd74_aEp znK1j_%_FD>g_~>P?AE5-2dUP#cg8dF(BLYIq*kL)h^BG&i5;fRgi)npQ^)08ZitD@ ziGYG&ga@HG&pq6-pOp4w)7=dKTTztR7RCMx@{pi8(q6}#>|urT1qWb1w=nELz(NDa z@V@mD{x%eI7h;PM{*y$=>5DA!CDx7y}fK{G{tS{%d{(?_W}WDGo@PL68FZ`8=37L#8d$72*54 zq6#|KCsSx~s{g^bxivkZk3Xf~b0L&;)F~nZZc$1KaRB^O>XiS=4)KEr4)9suA3%lt z#_OHQ)q|wx90N;NRRMp}N7-vED%kV5mjD=DHa0ydo$YQRjs-nl>W!pdZYiLNwzle* zlM_fG(GQ%jmekIVgEtc66Z`z*!bQ|@pBr@MZ>A=}aVz?JOeeJ}zCsi>qqvQSxWaqo z_824(GWcl^4SZy0GX%7ho^Igq_^lWuZ-;N|d3NX=t zXRN5;{xAe$5Pt~Lcr>Lz>583&7$ls%*>w<27(Z9CALjq9d9(oG?}VW7C3FvWz6!kI zAPh*$U}(ZxMx)i+n$d~!MCZaAjLbp^LtsZ(&Nr|#uVNi($NfrGl!Q%;nXysfMAfaN zooXTKqlDX<|G)?qvs8MjiB=Lb%U+;y^0s4#g49K{nzY{`jRKlU(Lh=sS)>s}`Nslh z^aEXngOmj|#8BR>%bjmEh~Iqr<5g-I4eB6G17Y~uNCk%k=BcC~6B0KLG6WcC+zTsh zREXCb@Z7!qHkc6S=NC3(yvr?BhemTAUA@p(f(4{1lHpmi({*R8DkXJyLY(Zka;lLS zL}3a9-qOMm!X#%qsV-^y+Y7Ju9&1Bx-LxzIhm` z|1IvYr;eTG0i{;j&n@%>nbVhau>4PQztFeT+m>b_7@{fNBQxqC6}E<)hBQAeRFFBe zBqK=yv}D_gQn_w-tFFA?=wW@@tsGacRS32c6HPdmH*kC=w=sGVNou~Qqyn!u=$uO~ ziQ6VQ8^4@cQw7ldGXN8N-F6zOizTi*K)dlRLb(2OiB}O>ka}SBA~Vh~)fx6~<07A^ zBWdWgr2+ZW(he(0ND!jLvpJ(Rvdjm6w2<`JaUO={BL$YX`!Pvk8LU1l<3E{C`(6Ah zdz{qJbU5Q=pVjZ8YL9SDgVnr}NTbV#gE==j-sA@sz_j2ek8y8bNWkS_qm^xJFCnAr z1Amv%Y#d4x*7%RWM8dk``?gTt)i_!-N`5Po#DAu@X2;mU5NAaK@+*3-WCaU}koX80 zFki+zGXd+oPw7k$M^<9X$6r8uh6z|M z>mfLBLR}ox3ps4rcvh-C;b@4$`(khru@w*V6iK)}?o?Q?nbFLOpXa09RWt=5sxe0}qp>=oC7S%g@2` zOWwy*_7^h~q+9TX-7KpNQm0LO^)#OTE2?VTrE{XzLRD6Cja4_6OSi<1BwZW7vun_M z^-so$bjp>|d9D;_IMyEgw*y%KcL^v7joy0bCc8jPkA{C~lT8Edi*VloY3WsUohg_@VN+CkF8UaEG zdTe||wP#)pHQ-N7NP*IOW7x}9gg|@oRn1GY|CloiVbpG@&u83EnlGf@TALXmp_usg z27n=ktP3x|O|Eu&EsAd{)5-xe>N!e}t#NWV5zm$a=g+_y@EeDOHk*gAZ{LVYCQHq{crsD|sGu^B5h5d8^*LIQDN2fA~*f z5=pD3PP6nY$(&&pVN&YB2N;AQdY;E~J8g%HYv$DF&*aLH3{EwxIs7uRD=PWUJYdXr z(tBO91hKQ+XOE-FH(VE1v;`)$pIKS^bLm9xxtkoTMNyqRLUF%lTAyf!9EFPYIm4iV z4hBk7g+*dYP8gtx?i&3XmXv$w!;C&hfY(k?Nu(G-T4c=73^){q4~tvl{QB zOT4E*NsDh+2i@M5jg%^TWYbP66=Vqlm zlx9>zbzRi!FvX@G007HbPyp>|f{TdvWhJ;t{3U^yxEKiQV@O571;u** z$3I$0DG-!pu|uWB{C9-05l33wZKt)F39xxtKbchhI(VQ60i?2c-Hi4;G!2TI#iZJb zsAhl%e`)5ghz-z#AJwtpj>H%ONVgN0m|EBt2CwJR^Yp$>93u9bAI)W!EZ-}Cc`>Ui z76)zjNsRzid1b;{B&P&gX(4iyaXUd;ikUYiv*&7a`9YDYI>h#`VwcM-3BYV0rf|hf z2Drh3MN*!g8ise-hg5I%$;CO+iOuJo7gB8bY6)7Xtj1ChjelTFgpmgXofN2BSQ)WF zwonLH^tWDi`6hPzawTQODH%qrdkP(;C;YDh5W%AF+3}FDmf~fxb6-HQN=eZ6!Y>Mr zafr{MFJF|(#3_Q_xz+(pG`V(ueMS*hrtPL@CAk@rd54@(6B%dsOq2KH8MM<{)iWd! zx}5)@E~x|#fwQd~T{+IV6b6jR1+q?H%HEHbw1R|X5wne@Jivv8y+d10y4VF#lea03 z0AgeApcGYyFfE^Z*Cl}Me376nG_lI2)Ylfr~@{5cq+fNhkZ#U${)#kon2yW!Kpv2n^)0k(?X8{1g zn<8jSGl-qo$v0pPdPs_AKL@dU`#xtM#UmLPS4RF}`E9+{YF{Wn;0II^s|9|H7LhQH zh{ik0urMdGRY6?PVim9uVQ#V;i`Oeg?Sk|y?wG5PcXUQr5K^CEmU=YHh?D2+4NfYHyZB#MG* z?xGf@)BZ3{p$BtAhkCfjBd_6TM&<-tiN6jP zPV?BxBOpr^4Vm*+f%wv?XHa;L1!Sp~as{+M*#+D**t^O@U_C_$M471p+Z4%$FlEbB z;)+4Hf>9lTN4bW?O$iY7NF1n=SCCTJTBCIZGL{54DPA`WvMoS(+#P&y=yo}d-!JZ{ zoxqXrjQKp{H3&L&1IVqxIB9`>%>_WEzD^dbwZ{x8!K5X+O5pu;fN+khY5i!540 zWz5E#FL0FXv-qi3{0FBL&sn5rK#Y|7Nitk&%sA$sC9R?tU)u%lnXM2(Mcx|BJGw{* zq{SgLKIt05`f+sKVDCvc=LphX?Z>g?$on5P7W0bm9cFGQBC7QW-&v$|l)Co&{4*e~ z)+tM4Xv9$lB}>C5LgKAD#T35hslw0fIjy*F^js}%8TZcVxz}i#0`O3=XhY)k3eifY zp)ltO0@tb{#f#AJQtOdqh26%<5*8oz1EY8sd$V8d`)e`vHbgXCEVguP@ zM2hgtsFWw<(lgDN|7WMe3vGscK`pK#bvTCq%iT2OD6mpFsJ5mjecRrE59JF zX1?Tq@VcbIECaMorPuMpPdSlj6o0fmVJoIj$ zB}NOtxRQV4UW(k8ssJ5D-y|_ewZ00e>V;@v2g{Fb!@+!?{WA6a&Bw8+6PuZ{ zrYz&+ahSYUabFZ`r9=x76pze9$NG$^;(e#k5+F!9963Y<>k-4X&ctyx>TGFPW{B|p zgjoEjO%-7$vh;BcT~{{s7y4)hmbCdxco~RVT6Zm3uJ~8ftxX=#*dbA5Gyf!OqK4qM zVW3AOci2-{YeEZ%%jhdFdwITG$$8S-dt(mGm+}xm=(oN|AU2F?OGacJ5xnd- zto<0*t?s*Vp*DGl(cuZiAOs^(W4U6ie0sM|)<^kZ9?q#}0!NtaI#*ES93~Mi#n5ur z(9#?@?F1kS2`L`ff3UBXO>M--5?2Wlgw!V5CrsHv7}Z1ZTNw`hF?E+WG;6`dj7U}@ z{-5?Px$_}yk2B}T)r7p`LoODJY}XsLx@ey0bM6B+6wBZIi=508pX9$x>Jcx@m}{|C z60_Plp`eoGYrpt~^&>`nfTrzZ-I%5kp4sXRd_8}Nw{>p}4dO(?Y=3Hn7iSud*g2gS zR+R2vbD3nrwBe($u`}0P?X*(T0XD)%K zqIm(ke-K_E!pU+uO(8CaCksa?EHqV}OVU_0;)_vF-v;hcQ=GlIkBoxogI^&`>{xjm_`u!8ZAAQFu2tzCXz-Kf0%s^KFf+Va!$ST zS6Y5%fH0-L8MFKkwom{LxnYkrM(xOd8aSh$*6IwFl+8rX)Al>zz5Dt zdjif>Hl4xR!zJIbWHQD^WGAJjJRbnT6?PyvL^v0EWKjm!#vakjToEyI3$;PCP`k8W zi=S_-cSJplV$!*U?YG=dt;5<=zTAM5%772^0Zv0M!i8R0c0Ab$Hu&R56>JwQBcT<` z5;DM^Ju1V7Z?nSc)@Qme*2+E3J8^X%cI!ZUIaQ1_uC8(N>o)t1@pG!hzU)c!-_+D` zoYA79SSs6o2$ie$G$e`zz&WK>4ddvs5Lsg5+I~)6S={$0_862qHv?f{<+}Gh%XkMV zlNMy*$ufsMJ0OaCVLDoNg=4_apM>duJqZKaQB~s+4Y!&ML>YbmEq|`EdIfTN9Nr!1 z<{T;7`v^CQ-|0qJiefgtAx_6MVaB5vB%y}aQWq%_VC-gYQvh0qumlKE~q&EDRdDs2N z7+@>`kSyud^$Uqf#kTVqpNArjA^qF6CQ`e?se$4PSm$cgW(fv2EAn4|*tgBhROH$# z8nN=SjS`D^t|w4Kc@ok&Il|7}3V}I{s=I#BQ$7xln%nCWz&CyI%xlRXAZ_75_+$v< z!%k)gaU|$!b$PA);7|9e55n#bO3JV1`^70;Pf>07x)Zb1HybPms>1!2mt`(oKTa3v zd8KF;h<cDc}-*!Dz1uTM>RbgG7I4=?$HGTznd(7ppl-${b^;k`w`e`9A%Gc3@yz&P!ilnjK~AMwjDXap z#m!~6c9EFyJ1sV_?PL|2T|+v~xIh`zDf>fzWHe;z!?Hf~NKOFk9s;IJ`EG>B_rln{ zql3#WD{xhuqAOmsc4}m;4;zmlmT)SilZ%l2=Hm-;WAJh3x;r&n6Y}o_YL91_iRXh% zup29@Q{-!yxl}VLl6XJ}BPv@78w*+Midr|5t45_+tozsTwOt5GC^K#=Wtz!=a`j1* zI`FDLw64ODWr#FxvI?kbqpPZekx(G3!WxBg^Mk78kR5RNgLUQZ&ryOUG(9j5%KcD8 zO13vt52E|DsN`XiIXxkwe4w-0?sLN-yF#5O0z!c5FDzU`+y2CP z=4yJ+^pBspjn+z|zx#sKOT^X$*v8GM+&z}7G(?@+d41YnTfZ&8DxoVN-x=n!C0iZ8 zGt*z2%V`Adv9J36PM%%m-d?8-b|rkpACyg$ioT_V?}cvwOaQ71Zh&>=15qP--Q2QngMDr#}etwFVaS`+nwEF|Pf#;%O!D_LL`ilLg4L9b`A%dUm8 zHp5;^JsAc&bt3~j45z4pB%8%&wTUK|`72kcM+V8P3IfT2+k)`x?RRd4Cy#Nc=_G+} zs{-udeR)HS4@dmcS{l~aKIgP-m*uWO?H{#zo2q>0|F9H1{Xy=*_cYx@m){&uVYZP@ zPFVUdxqUBN$eE?!tc0R=SZMYZ|3fdaY*&VC=VGJZJbOCpv0g7&hhHFp8Ps%xvndIYK+<$Nm6(y8cN0V95= zY1Ty&mt0=W9*SW@#?Ja&h~BAogIhaXV3&6tJ?XaSXL(4`0a4$i6f8(HLk|3a^d!_f z-lBn6e2theT^a9SmZOtz7L!1Fg{|OtFicE;S?UoI9P+wm%0ug8T_^MhvI=K>um+zMmvHup0B@%giZve)v)25 zv_*d&%)s@s7gh19>!LH4kvmOw67v8UV_UTe$yB;=T9gWXZ;fM9B#CJl*+A~oe zdYUQ!;P>@}p6e9i3T#t6EnoUM+-V z#RUz7T5Cw4`tsXb^Z3l2k>5`NUTgJauGGB+$vk(}$~9>skoY3xl6@Cq?mI@<+Pm)z zH&qEf(#Y>30#;C$aVv9=Dz=zH{^%0Zrd=%V_2=svvznk*W~{ZBwJiX5G-ovxn>WoI z>kB{VMsh!Ch>F3F^pDz;IBC^ zBx$n9ZD5f8I~^8~Pilo-a0?L~<3lsKCI@CS=~7f(brOQ#M{Gu;#|(cb>>FKG^Lfk@g0^SyNYGb`6B?z>i_aUspJo-l*~Me(tP z>icDnD&MtK4Njeq-*>Snu6{o@LSIU6#^aQsJ>?>tl8uioBk)2Hw;n;dMWY=F*F>Os zC`ySezV*+ttqB`jrQE9(fUgF5ZWSW!EYc8|7x(J3M!pF$pHY~9|9VNQQI0=v;G<8HFH@Kz`4FvhHYGZAvsJ~0kE~KXuFEInuc&6Z;59I{& zfaFpirQx)1`PBoeMnuTHUv-=4QkF17$hvh~3HeE~uv2ZQu9LO65`4l%I8-u;Z!n}W zPgF$qkn?MEK2|8p-clUdJ<2q1TB{@sWtQZp94+_YdEzi`fppReOc|h0fvUtZ?044% z&1v<@sLftt#>mtUgx*$#QTYDEGGbB%29)wD`-O(JRugsKtAtUNt1zpS5dj&wcTjiXBU^6p@^Td+2ml`}6eqjo1)bK*T7BdX zUu_mW!2pAmgxV33f2I|OYfdhQUJ_0Ee@PeXfA6-nVvX74Vc0N#Nf}@*{b06L#S1X; z9yS4s(Qn<$0z*ZO={%=?pkzSBoQWya?L=;e`}@QtLBz@q{Ys@T2gp5jC&kb*{O17e zRK4)#$=)WTaRjKctBp55^qq6Rv7?g42r=bbO&OiC@^8Yr6hji%8G#6ZiJ7>YWRMR%mDY)o-V6txT3w z?g0R+Uzm^^UE@Z;sD$0fiL$A!LJ}!ZB>J|C8RUe(4`IrZ&yRk*&lbrHQoiZ1Zk|@xq6gA&YJ0*YZu4U$&}v!Rya5)eWywz$X&?HkeOScbR5VLi2;ZZ54YJn$&C^iS+Q z4Xgqb+ZI^^MqFWNp~0P|Evo4J_Pzz#uFX_4oI0&A6#Gp(ym9cf5-0?Y12))I+vrY_ z_$yD2CIe>I3gKncj@;;a^NYJBX&+`~)y97BmK_;OJ|*Pqp3lqXJmech)-io+nCAjW zFc(P)JV12p9QH_)h8-DDLtwDLDn=q7Lgd$&OJhVAO-#*Ym205^o_si289S*(RjwO) z!=CgJ^qTw&sdKAaLkmabcGkVg83}SiZARIlVJX@)-$!SuXA{lTlxfpj<1fD>3%U^H z^FpJQ5lo;%jsy((@+mltYGaQaG~ka9e?;Vv?Sp_^y9V1y6?U~KY#QYY9P=D>(=@MA z#x4uATmd|}|0$Ib<2B!`nRm%vHe_5oa1_FaKj(%Q8;O)=ky~8$;&=e)9SdE` zXH`ZpE(ldz5S2(I@_0t}(?iS+Z_zmPEYTeX;&~rwG{mHiBRMMBP#%k9C0}EXg9^1S zz;q6sIR(4f=mqr^Q=hFS75tHnvNTDK9AsD?A0P^H6F`ph`N+)AsM$BsFyd5dHF)c* zSv&s^8WW-FN8+bX!q${Sxqa}>HKgfW?j7&TZV`fwHhKl1mBWEVa z-ylq{`leqVL#=M;}(nTGsP>ScYH^58XAC?tETs!OuPP7<6*mi)4x237m*y2GcG02x*hFbUPFM+0TFNj15U`I2_fr zNRJNzfaNZD5v(K3Oq$9fnEKen6+cltlHk$?|8QjNSS-<+irDO$_1D%v3Qtm^`x-00 z$#k;W+;2NA+-w{+DfhFoNBm=((Ig;J3IISP_))$D(qoiIrrA(ps%ZBj)5@vs2>>E2 zK9gY1-XfTggY!EisObgKu_bg5`^izMp<3VYzs{qUE9BvMQ~Kg1{&3SWX*ABbS(kAl zxQdR)itHr&85CBo`<3NSP-94bEBA$!u9|e9OjI&O5Hb19tm%t=>^c(2v$DcK?CpFf zEBvM_;HN(n8K`P$Mz&<}|At4;9VI$`Bj$ zx&AhP`9R1Xx67G?oEMUv>9%kWNi+y>bGmZs4m?-316Ok*W$Ofw9~WI8=g;Vj@##nF-E1rs#=YlvN;dj^Vq9W7rN@Une8*>I&y9;-Ay% z?5y&-mT%307(jsJ!b19^Nx07Z>3+Iy94}9%_t;xQW?=2*c@Pvo4cyV64&HtH?zR_TmhhQ0@I(xN&AR!LBe~hrw9!@BaR*+P#NxGhoWAn31#o#56)&XiSV|fq2da*%Npo0qZoAAxPHR7~A!v>b zL$O!f9=?^xr}0^TFp`!14LP4P61tK2on@(qJLpWOh1?q91S{qp-l9xFcrAJdL^sgF zn;{-<$#R3w`Z%z*oWLswn!auHYG2ls#QA3pbB$8@V5p98&(lI|>Newm8Xt9gncaD@ z(Li(80Ds3C6Yl?J)yXBNg4To;qieA!gPY`$YGMuz|JYYn-NcvrN zm~HfpC?TUOW^O3GTs$S}(BxBejG;qfi z|F_-vYgVuk3C@Km5$n=^5Jnfc(GyfHENjpb_Z?vc;8b9XUN?oz0C-qFY_1^5vOXV0 zkI?6sN=TO)+RVaEzK5F&M>!TayI>PKK4@V`@7fumiuO}Sj9|j5alqdO6`LFy;fTNi z9(BCUr>xn7Y?j0?V6Pp@(ku+JZzDV|WrtAeFfO-P?47Vg7e#v_?XI;tW z1;I&3IG09oFMJc!TKJqp5i2t2N4tN7ky$X{m|j`&-23a%3ubPoJjihhVGA3TT!0k3 zUtVDL0QceL1xhY2^5x?}5)*e~&$Bz3H+mXa`}PTb`LzJ>wb^2$9vk7Qg9}Aa2mn0K)#cxII`REkIW|n% zW_*I(nD*v4-?5z8398?A-Yp*+U3=wjNbgr>Kxge|wYCtP7nf1fSAo7s-djO|G{b~m z*6DOIE-pqC!zVl-tUK`0Nx~2l#9GiyPPRAf6Y3a!RQT0{-Gc8~MEwzdeg-@zq~z=m zN1wUZe%}^0dfATIeVDVxh``^1r=^+|LHYfsP4yWUM=lzO15R0+3Ds1-*Y( zZjI5d#+c~dJVp9^$6q}OVvV>d9sR5q@?YA^vv|fMVZ{jXHcrVasM6yvj&=#dE7?jc zMVF4=_b}YEfU#Ej(|(<~y0C4y?4YubWz8b}NI*O5U2y$bL2cf*T0I1Pr!hwy&5l_MHy)8%YubwY8m{7_=-t~P;yb>BOHspTH%{QsY)aqini$TZKfHZ0dcWTZhI4z9BBwn|#7OT6a$ENVczh@M* z#v`+cZS9c^|FW%|Lp}-JO(=tu!O=V0cwaE4;e^0cTbH>3H)|=d$+JP+5@fH@zVYUi zZ|jSok6Dgnd8R}N+6%3-KRtI3mY_ z!iyC5_n(H}hz}Y8+?sxHE$NeHwMGXUPMTKRzdc6Hxi6DKP?*N~e zZ0t**uCuWEJKU+~h}a!jq?x{xb`#eB*Dw_wGQr@Ai4RQNiI1 zQUv*>=|X^-G@%GLn(ivM+urnm7R+1IVhWf+?eUShE^!tK&pfETwYkA(Zs4kEWjj{! zb&GojQPx7|)|p6v)@iS7@{$nwyjoN4f~--mtkVg5R$#0z%HelN?t5Ht^wdEi(uVU_uJGE>}t6*AIRyM`0c^8PoH7a=Vr zyoQTz+ioO+Uqv;>+ty`)@4$0cAJFi{qug;!r7x{qUeBT|<6IT6y8%RSzG6l7B^GXT zq+e)6xBIB7)El0+ANH)3BD!5)@d*jfk?IvV!!1rUAUwQyQ|E{eR$vj}_tExI*bUk8 zp$BED5YyNu0?4EXD4=S7oz;&Ben7=lmJ(x5*GkR9P#*MUIroNqb|=N zk3~dSS#GayGF~Tv z*tLzwxkDR9<%9*aNNB80p=X0r>XI&ZfMo0YTJ43a#elL1+YYa|VQ8Ze?d81Ki#B5u zXR1O~eS0R_QI)=Xz=w$(X=poRlRhi1sDUBe2_&iK`nn_ZJlZ{YHhkw&+oqajU^8dY zkFH1%j1(OVvswgrJHr5xCNv!SbEMAmGbmEP7zNz1_Pa6(H;4!pcT`1GB$Dml$bnxM# zw{ulwtAc^LFWZV5fLws9nZO{2#VZxkTY$5};^liNnLrHF6tlhdJlb-p+oVj}%78(M z^XMCloLNmCIn?&76{ws9%}RZy0evP?FBT|5H=f)F^8}<0PR&MZEL#@2_KlES-kvhd zUBep?u*W3!2-RJ?l}p}EpxzJ`atPNt%Z-U|wAx4b8&S3(Hs%lMeox)o*JIQUcL*N5 zBsPh55e<~LnK?dQIJ0OssI1ICdkuKZ#n1tf<{QzY5qMncseJ^UfE zlo*)uLMBX$OjAny18<6F8Mp}O9aFi@3&OeyS7{h6rFC62QhP!sw&S{AYlpv)7vM#F zwkRN_ttlX<%k=0=oq-+w;u&nrbjzjMVA0`Xo~+bT-%{ zNr73&3S9qBH)%@PXHw!;x!?cQW^uNA7vRBsfWK(*LK`M2j1@ttFrNH6!GeU4GUuRD zqW}N;S=Jy~y#2Mgch=Ic2!PC@e4$^-A>wG);vRM-!B;cPHrrDiG+UQsk~S~(KM4)< zlYK__WZ5)O3y$|VPMKKu@1y_^tr3^ow2O(p9%*>fa@*ArvG(K9Pq=~nA*{L@QQ&NKKK!O_d4bLgo5HRHd4VRkc24+k#`Fg^ozha=ZvfEK!vJYy^qq6&y`>*QLB z#_jCh}YD77c36$zcjRqGAFPkw2(ee<)#5&%p%43=^APssI^uE`=W4^$;rCWI< z?xeatiZ#jrBw;d!JwhKz_Vu$W_>vuFkxdI_jR?@ct8vuGs(5-q(R%qVv%BbbuV*K8 zFI)ZxbT}H3_*Fg>Mpc53w1g&S&!_x72YX{uiY|6r+~(72?RlzZ(e)D&^h*T~jOJpU zWW5{|NoM~04Ty3)*+LItyuU2!6qR8C>X%Hkv&LN#1H*Zb3O-w zYws!6vC{=n23S2XLb4S~1W_YeuOn9@&G`t997xI|Oc7oGw--hZtHBl)=9H?daIFm_HN!4(4hVI{>Ve?3dZ`4BGs|H_qamm0*|-4NR$Tm4w4(G$4c)X8r$XgO%pZ)#1-3j6?YEE2*6|bMH9|9O zscNsGghh3DcW)WecEvh^w6J8Be)};*->~{g@MI=Ly5g2TH-sN$`zf}hZFXsM7h7Sj zWMwdDpY8>apQHTbbv(8}SaE8^nDY(m!i2qIeGB9TH{z(ArU!ATEQzfJCo?No9~NAvMHVlffJ-d|FJ`OuZ6Yrh!e6h# z0$K8why81r+}3MbpHuV@s1&If(@mDpHej*U0hMzPfvP|ZVawY&tN*){DQvx4@c;Bo z_6{P<^sGCEw zy!%u764d7S(M|{ou$ucGiMhOdF-FH6=WQ$}S#w`I)AQM(y7xf0ok)8x%K_(HirB>q zLE*bb6otQC{^6k_=G|*rjl?5jdXAv2sGQOgJ3|8cB=pcZ7%V)rmQ7T*T`TcHid8_{ zq4KcWcqB}S%6C}kmL+RB%k|D;J}_kzz+Il|EoE?F?LYIp2C;Pq!p+j*mbVFcZbBnA z4S;Ks`1?9gt3;)wH#o#f=IPL)hkRJ@(XRSQjQG^AtU({Sf&t8HdiF_Kp|B!q9>jxK zI*(vRY5p#n>1Wc#-FM@-B+7<$iR0d&FfSdkKGc0&1>SOKl3 z=s{u=xHN^8JY|!7+8vF$3NVH#lYab(Km#s(nbafyxoD6IQwt*KmdT2*r$62JK2QHx zqGOrzZEEpB6Moq(%XN~pzx4OxIzhLTjxDEQb|RzUj`a<5Wp<*6{EobomHF z%E+tYbNnk-2vs@SAX69_9dHi zApu2#gkuXal3(4lH&c+E0pGn12>w`*G)F?IqLG{jO=}0=mTs#1(*4VaR~GGUDG&WO zHkuYdEXFksCq?vUbN)mqDv6JG!@4~&bzo@AQAmp=%|LjuBHO)U;V?hU^+58p0x8#S z4|QUJJZ+!nzRr`vD@4wl9ym}p^?>HyjMG5`EDi{o?sl`Uj~zdrCv zfeVH!pYw2-1i4EsL=>Shto;|vdBrk}Y#~@QgYo8eqoO=vtaOE}t=Aru2nj~l=Z>02 zRCIsVm~9QJjUH^mMN` z?j{A@qRSlQioCf_WyMaqvvB!v#)WcNvOaV{c+d!RL9m%TKtOiqr(A=!v~c$YnGlUT zDd@%CEx*2Nv_3uv6Iv1cDRj{9e2zUPltVsRH+9c34X53Mv?D@))g9rnfn~oY=N&)8 zHcL7`r#cUzt54uvwS4snd%Y5%P|ZeVI|=;%`_W#jkA4N3L}@8tzMUSJQQ!>uT?~4k zh~6~NXqMf){glo$>p&vN(utu4vB+#Cb}5WGABaXY=S!e%awqQW%0K0ci7j+6YcG1_BG0f3tE!9fNQ@0N*rF!t2xR%ZCx;7C9)$vtHJNBt^?JHuZ% z1P9y)tC&CCScslT8Av9q$oR+^gfaM)#D1Rh1enmA`^6ZgM1gdo5xCa)HbNkNtG#)7 znTW^!XJ4R(segtd2V5PzW;NSm3K%uXi-Uj$w$zLDm|Wgw$pO$p0>CXPM1KORQMCCH zb3nVI^j+96xRTVqGdfX$wxa7kyJ6}H>aW4$#~$Zu&`YfegPJ(P_VE-rUvKI4B-k%5 za%IE)g$^N8C6=ctOYH7WK85+};gfZ%CiF436=DPlyJ#dyD>s-Clctzq5gYTb$4XYc zW55(pb^Kt z-a0Y>i0sllEa#j6bs|j2L5jttxm_w`6&B?hEJfAPrHbN^e5$oU)QxI_G-ilB1Z9dF zaBu@W#~THEYF)a4QW!Deb9SY_al^2&IO}0SKWIgT_0gsbd@w+>1W|IWUj^ipG5BKDe27RZi&Y zw9@U)nMF<&h<|g0?QvI`wMa;sl^{o0>5p%hX<5+;8}3W)xjNX3mi) z_#oXQOvvadYn;C@d!-d82a*L+ypi=Z2jTZ2b@vNZFqsNQI6&^lVXexI5n6>DLC4>Y zR(9CunMQHYeJ0t#(rs?!a#GjxUjaDk(_-q#&6=hBc)NFYEs2OrFty(7lv-+kVup;b zNZHp7Mj1}BE(zfrWlU8+4yLF}Xhb!-sJEw=4|XB(TKf`i%9j(akmAsO_y$OFo;Z~T zfEqofQZ=fU-mxJiY8QRw0KO~?knB@m7|{KAvvY06K$Hq-#9OjsFTx|9#!|?0wC*XM zFFiR*HwF|2Dp}qKPH>0=nwTE}`k+uWSn7-<&}@7Ua90;Q1^CDuX0ET?eRTOwRp;hj z$={`@mpwppe;bnS9x2~ks<^Q4PSw~mSMiXvjqtbaw#{M9!XION*0LLpJRGrw=dx4EBDI;Xo;p^p%Zg9h( zC7y0!x9XKD6L8Z*m{+)#S+(a|WR(#PaN_bHVWFzV)JVq6&#aCJm8f~th_laJxQ!c$ z*FBe!3$cNFX?B5C>0$!I0& zSFt=@4|1M5*e3GPtAX>d$tL&32B+}%g3mZ!$JCG(j(r|?HA5^&Yh7WX1OB>zQ@&OJ zY(L0%Q;p~X#qzf zAC+(J`tD>fJ3FB{dIW`83AX&tnr6T#uP#>}S*gcGpLmZe%+n^u)PYrmp^dd;`Qu|h zOgOOq_NwdanW?8MjOU)gwc1x*_XbQ}Jl&pWhK!lmuwT21>*)n&583@}fm~V#Rhm|F z(k8fR1y`iyVX~y0+-@BJ0_2Q$Mlr%@Z{EnbB?T#gbGtZriV|b^0d_h`hNbC(`&mY=Ne|s&Si4OHjV$5OMmdm9x zM?>O#zwJJeL8T02{R_o>Do+Em(gQK)v=c$vj4h4_=A zexCyQh%g?yx-Gn^Md0pUt>e3)#dSZq63}(nB-FdZk^*~vm69%31d^PztNi>(q30+zL|qS!ZB@(!fD zaT&iYg-~5h)Aqa;S#}ukJf3>GRm1L08vG$18G!fKR(h( zOb`5ht_Ju?eXmf}G#3Y345_QHK5V5#XL}X#=rs2Sq^p+!SK8AzIJy|x9NDxg6fM|=0yAV#7SP5?@m->JZB`sEwCCLskSuu{zH zSEAu{4okfbOUaNy(UyE=|DDn?Hl1mmrhQ4@+uYQt?GfO;<3?HR$Pnl{*@s1X#47cq z2UI+Hj~$7P;&~}&+XRyyzfqYTmZVYldv7(CusM?E12xz!Quaxj^C2jFnA3R%=or8L zwDHZgZ&z%F>;|vgO1m63K|Qy)PjcoPmDCpkQiR6i+RFGQaK=*3iDYXY8WpZYDz9u0 zK*E9bH{4_0b+mKMO3FSr@P^K0QSC;u_@A1DH3i16d!l2&DmNpD4%{0EPGt z@RMNK@;UVx=i-v}DlX0d8;-39#Yumo(-_e+MVgz+BOfP30fg+7WqByofe7SE0v&Lt zhiqf0qR+QmFb`rRBz9j%3n^%ke$0YnO&uMK%y2x#6F5B)pxpN+RJI27CYfSxnL_;F zXRiW&ejHw6;9L~Sg5smqa**#9~t zRAwwJ49T_JTRKg_q5V26M=^npIdi=NEnrp{=vYuqJw21oF@n}68AdaZ1>1=aNxM8TyDB|#-R__u>0 z%4dZP^+`tuQ(wCK0fIckK6dg4Z7YQ+s{VjqX6bxKuBhJFn{_4&0!mVgG>86h7e+^M zOX#CQ(eVDT9_&;qjli~pJA}6b8!umuXDW@QvcT_$EBH^N1#WnF)1+Z#%JruzQw(+E z7Lz9sz!Ztc8WCB(BLkNDILV>X4OjUxF$JJmw3S2I)ccdq?35D#Q7 z?mmf964jRSmg1XAh}_* zYGTibW|=6$3XJ}A%{p3Z2 zQU_UywzR43^hGIhMEXd!nqm>6ry9EYm?OL-hN(bRiaw7+LrCII;l7vy&REzurHaa( zFT3=FoNG#H%!zy-{X0X#Y7hn9*=qIpr6OJQJZl)TtXLXEa=>RZ=8Tp;(DjUogiZ{3 zvK9xpqdx7S=J+x~3H?HmLjOt9X(u;PLPN&fz`WAFDiFWQ60U!QRUr5nxe?ei5teOR*JbT_;}Eg2hL_B zx4NlIF{{}G9K@-Qs$mMumojS_-6lej1m8Npu;Gb`3jwq9?fT^(S7Gfp@#k?X-Gws( zJD-We4YvtG5o0Eaof=m@ijlm!Az}h1Z>l^5!uVUCoAe6RwFIacz_Dc;dxm_@{sS+b z+`vWi4BLCTmlq0aLb|SPgp9y|JFe^e1uhioro|9E0VQ0WO>HI;7%*M?+UE= zmvAQ5_lM-*%hfD0NP|PB9{U3c&p+%nW{WJ@zkxL$DTKRxg)$oh7$fbZleBWdTPMUg zDIBZI`9MSm2P-&+KxhhwuW(gZsfU|JLjN`-RjFc#i0E!9M`p#djtFHV3;WHV0IZ@9;ySFjD^AMMy zB-14c)kLR>Ml1OkvBJwncL7Vc0;ypZS_nEqc8%-%SUkFnD6fOdSUxoW=D<}HS%H+1 zxY@1x6rAC_llGBfFsNXiZDWWg%l*{=(~PUQozA!c=y^9)OG@jW02$|zIJnorbdSSz z6PCvo4EJ>XZN@@{DTGv?FW~?BW4Dd&pZvKk>D+eKEY?+r0 zTI8jzRFPBW;~f)CI$sVRH#K3I$25w)6VWZjEm+pawoL9jrL5z(g%7tgcU;Uyvv#$H zr;L7v)aIh`$Qe(WBbq0Ly%B&))j z&?UoP5OIm0sCf7Hsz+|(ZHq)hIma73iEKb~_OYefYCYjzNdKl9ARyzukO=w)Coh?*Rj=?Qv zzVB|Dov6apeMhEGj^=`nkPO@0fRckNVO(Bodt+d~UFv7^*6^ZbTN9P+AS?`1C|UHn z3`x~^qTN`>K?Q7`=s*r>`jaRNW0m6{Kik;r;Ijf9Kv{ql%*eGd|@ zEq<5pMFrd5WqT0S+E>!yl9uUV77C)X7Ly`*1{8Rq>bk{aLG2|-*cbdNYkhKxIy)L$ zfpV0%UiyIb>eW(2atUR5j@TFB&X>W?`)&S*Ig8g`B?3C0YD3!pt^tvO(J~QRxVU6JGJE7SEln^`MmE9d?!^dJmTF znTi6v{rc@u5ghBCnBkt-uA?u;F4~=yGd%J*rV0pns(mFR!UYJUeMY&-qML-|k^;N; zdH#(?D}iTt=XvPp5J>u-5%7Of!G|3n4RtjqGjBzh+<#`x-P*Fcs{2j~C6ltSG(`Qu z;SB5VlVE1io|#%|;}ay8DNDmI<6C>Y?Y|2>gYqCJcYw%NbB><0j*mM&G(ob=9m577 zW4L5-+G+t$MbzY&Nzm7vCImCr(CgF{*#BL2=I+4X61F&W!TctXeNA^V_*0;k%w|fG zlr4>IEk$!o*}+hAVDz@t-~v8tY-Z%5rMxG?w#W$jTo9@rh!ewg)KJQqwx|ICXr*t? zxmH2ReA`e5Lye}S$C&GIz&(Vv7`bKUpaFpcPZ;9?vhQKj<2$Vdoj=kH(fos-Ca#J6 z>ph4;s%F(p2>q%y_EQC0SfFuy1_D0mI9@atTRy3Vy!KRJ9s3UD$??3Mcb9y(Uz%TY z-Q|0WT~~L+74dW5QEv$TMrfA$5x}Fu`}jZpHjr~(&QBHdJFASNkUU5FNyCKAFRgX; zK9mvMfIW!uH$4AP-!G~tWHB3iE(klHids;Aq;x$Dx<&B!qU!rLM@LE5#i2@)J34Ph z)rSYV5xB`ZvtRv(v%79*>GmXKz))h$6H98OwdVpI_1VH0CI%}@+dC7`04GW-fgtGe zccg>QZ~>AE0J%}SWxu|%N}Yaic0H; z{V&Ep?yT^nlIrye`7b5Nlf?lK8I7Td7FWQ z!@pl2YS)c1rohPv>9baVZ(*o2Er;VoNdaWe@b@nR-L^y<@LlCgoIy)95B26#u*2-i zI{gEPg#$Sx*=W;R+^VejbLISdk+yv$twRFV)m1U06->%uslrM0Zzlf|;@JbINc6WH z5lr(J55g}|7m>7@6Zp*Bj3c!8y#W57!DDv$}8*lJRrxY zbT^n<*?IL~E)bK$*4icaYKFPI6>G|Fn>R16##02xaSoykiO6)g`()j>rEI?7tE14A z?WPsx?|CJ!8CkeD2Svk3_2-&E8P(?P#!4tD9z4m|pQo+XP)=T@i~Tj5=9nmrL;v2W zGwtNVD?JwEB_mN<(gGqlUDTj>5bY>qmtc5X7$qMfr32X%aD5Z<@AsG4bhK#{QTu&j z7|NK+{6h0$J)0b!d>Pg9+VhK?5AH@RhX>i+z3nA$2f$uc5W|&60qU|^#8-sA9*ce^ zzx-RF^GrVmgOGX>=uMdbs}G_kM9rIS6f_MA7tlV!X5(xvyQ9e`?B4*y0NqY|hDzJV zwwd~F3$lsEjQ_v-s(x?{v_U9pDmFh{7MiGJr=XQ9PBA0ky4_VokdZy7MFGWA9N9Xe zSdF_8r1iO=FXEL3ES2`)tF3X>-g?E*x&nTP@L@vw%sqd=9?YE@U{yTOB2D>DpQWWy zBno4CUI+%d1w>YRz}978RS{kkhs+{=3Rh^GAu{JMCC2U+g3Ex-u?>JEma+wSf{4F! zMl@(|yYn@tC#)EY|@l8Ec zHssyF12bXMLCBYb+VKv)rrWu%W_L~He3zt$TTXecU&l5yk|fKqxaX~oZt zES+7-wuO2gag98`i)adL=etjD&#rW($Nx4wf@0J6af2Ij(P=QsTrahSi~tT#8grz8 zs@^d}O>rh14HyR=<|nRmz%Q`US^!mh63i`{Z1~t>`%PP3vl$eM577ByPz!tm274bd;SzgO zcXK(Q-AnBe_`a4ST-5|63$itHb7*=pUhJoFhuOD>UdG;3gcg$-6o^tJl(4z)uwtI6h^%D21iIUZW75*QxT>I^{H zvFHlu#$*Cw9o^Z{bC$TPl&*s7*MwCoqlkuQ(Y2xQpf17^7wcC0I9F?T`6WH=guqI^ zOGHP#tLnWg&o>1SbTfq$-8MJZV*Apws4iOf$FIln!X zL{=lBMWNxM)7y|n3q+OS?2|qvXETP+m@2x?M@V##G!n7l!Egcr79%_1niHmsZ=dw5 zm>#~l$aLWSG^61fIcPE&uSL7x3{EboodT_IvsXyVI3=9GK0I+Az*sACD70Fvx}gg4H{cJ*&v{Ue aS3s8Bb0Lt}eSgl5fDIJdL>7n3jrF`yU(QSb literal 0 HcmV?d00001 diff --git a/MikroGig/20260629/20260629154231-Config.rsc b/MikroGig/20260629/20260629154231-Config.rsc new file mode 100644 index 0000000..eaccf44 --- /dev/null +++ b/MikroGig/20260629/20260629154231-Config.rsc @@ -0,0 +1,2776 @@ +# 2026-06-29 14:42:32 by RouterOS 7.23.1 +# software id = 2961-C4ZZ +# +# model = RBD52G-5HacD2HnD +# serial number = HCX084RAVFA +/interface bridge +add admin-mac=18:FD:74:6E:58:14 auto-mac=no name=Bridge-LAN port-cost-mode=\ + short +add name=br-awg +/interface wireless +# managed by CAPsMAN +# channel: 2442/20-Ce/gn(17dBm), SSID: vados, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + name=wlan1 ssid=vados wps-mode=disabled +# managed by CAPsMAN +# channel: 5200/20-eCee/ac/P(20dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + name=wlan2 ssid=vados5g wps-mode=disabled +/interface ethernet +set [ find default-name=ether1 ] mac-address=48:8F:5A:8E:1A:BC +/interface veth +add address=10.9.9.2/24 container-mac-address=78:60:DB:55:90:E3 dhcp=no \ + gateway=10.9.9.1 gateway6="" mac-address=78:60:DB:55:90:E2 name=veth-awg +/disk +add mount-point-template="[tmpfs]" slot=ram tmpfs-max-size=64 type=tmpfs +/interface list +add name=WAN +add name=LAN +add exclude=LAN include=WAN name=Internet +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroTik +/routing table +add disabled=no fib name=r_to_vpn +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/723/6e812143\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 10:45:05 29.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment="Download custom Certificates By Routers Identity " \ + dont-require-permissions=no name=mod/AM-Certs-By-Ident owner=\ + mod/AM-Certs-By-Ident policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Certs-By-Ident\r\ + \n# Script comment: Download custom Certificates By Routers Identity \r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n#:local ScriptName [ :jobname ];\r\ + \n#\r\ + \n# CloudFlare\r\ + \n#/tool/fetch mode=https url=https://cacerts.digicert.com/DigiCertGlobalR\ + ootCA.crt.pem;\r\ + \n#/certificate/import file-name=DigiCertGlobalRootCA.crt.pem passphrase=\ + \"\";\r\ + \n#/tool/ fetch mode=https url=https://cacerts.digicert.com/DigiCertGlobal\ + G2TLSRSASHA2562020CA1-1.crt.pem;\r\ + \n#/certificate/import file-name=DigiCertGlobalG2TLSRSASHA2562020CA1-1.crt\ + .pem passphrase=\"\";\r\ + \n# Quad9\r\ + \n#/tool/fetch mode=https url=\"https://cacerts.digicert.com/DigiCertGloba\ + lG3TLSECCSHA3842020CA1-1.crt.pem\"\r\ + \n#/certificate/import file-name=DigiCertGlobalG3TLSECCSHA3842020CA1-1.crt\ + .pem passphrase=\"\";\r\ + \n:global tmpAddCerts true; \r\ + \n\r\ + \n:global GlobalScriptsUrl;\r\ + \n:local UrlSuffix [/system/identity/get name];\r\ + \n:local UrlDir \"certs/\";\r\ + \n:local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\");\r\ + \n\r\ + \n:log error \$CertUrl;\r\ + \n\r\ + \n:local CertName \"DigiCert-Global-Root-CA.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-Root-CA\" [ find where common-na\ + me=\"DigiCert Global Root CA\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Assured-ID-Root-CA.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Assured-ID-Root-CA\" [ find where commo\ + n-name=\"DigiCert Assured ID Root CA\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Assured-ID-Root-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Assured-ID-Root-G2\" [ find where commo\ + n-name=\"DigiCert Assured ID Root G3\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Assured-ID-Root-G3.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Assured-ID-Root-G3\" [ find where commo\ + n-name=\"DigiCert Assured ID Root G3\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-G2-TLS-RSA-SHA256-2020-CA1.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-G2-TLS-RSA-SHA256-2020-CA1\" [ f\ + ind where common-name=\"DigiCert Global G2 TLS RSA SHA256 2020 CA1\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-G3-TLS-ECC-SHA384-2020-CA1.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-G3-TLS-ECC-SHA384-2020-CA1\" [ f\ + ind where common-name=\"DigiCert Global G3 TLS ECC SHA384 2020 CA1\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-Root-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-Root-G2\" [ find where common-na\ + me=\"DigiCert Global Root G2\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-Root-G3.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-Root-G3\" [ find where common-na\ + me=\"DigiCert Global Root G3\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-TLS-ECC-P384-Root-G5.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-TLS-ECC-P384-Root-G5\" [ find where com\ + mon-name=\"DigiCert TLS ECC P384 Root G5\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-TLS-RSA4096-Root-G5.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-TLS-RSA4096-Root-G5\" [ find where comm\ + on-name=\"DigiCert TLS RSA4096 Root G5\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Trusted-Root-G4.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Trusted-Root-G4\" [ find where common-n\ + ame=\"DigiCert Trusted Root G4\" ];\r\ + \n\r\ + \n:set CertName \"GTS-Root-R4.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"GTS-Root-R4\" [ find where common-name=\"GTS Roo\ + t R4\" ];\r\ + \n\r\ + \n:set CertName \"Go-Daddy-Root-Certificate-Authority-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"Go-Daddy-Root-Certificate-Authority-G2\" [ find \ + where common-name=\"Go Daddy Root Certificate Authority - G2\" ];\r\ + \n\r\ + \n:set CertName \"ISRG-Root-X1.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"ISRG-Root-X1\" [ find where common-name=\"ISRG R\ + oot X1\" ];\r\ + \n\r\ + \n:set CertName \"Root-YE.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"Root-YE\" [ find where common-name=\"Root YE\" ]\ + ;\r\ + \n\r\ + \n:set CertName \"Starfield-Root-Certificate-Authority-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"Starfield-Root-Certificate-Authority-G2\" [ find\ + \_where common-name=\"Starfield Root Certificate Authority - G2\" ];\r\ + \n\r\ + \n:set CertName \"USERTrust-RSA-Certification-Authority.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"USERTrust-RSA-Certification-Authority\" [ find w\ + here common-name=\"USERTrust RSA Certification Authority\" ];\r\ + \n\r\ + \n:global tmpAddCerts (a);\r\ + \n\r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#:set CertName \"GTS-Root-RX.pem\";\r\ + \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ + \r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#internet\r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#:set CertName \"ISRG-Root-X2.pem\";\r\ + \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ + \r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n\r\ + \n\r\ + \n#/ip/dns/set allow-remote-requests=yes doh-max-concurrent-queries=100 do\ + h-max-server-connections=20 use-doh-server=https://security.cloudflare-dns\ + .com/dns-query verify-doh-cert=yes;\r\ + \n#/ip/dns/static remove [find where address=1.1.1.1];\r\ + \n#/ip/dns/static add address=1.1.1.1 name=security.cloudflare-dns.com com\ + ment=\"cloudflare-dns IPv4 1\";\r\ + \n#/ip/dns/ static remove [find where address=1.0.0.1];\r\ + \n#/ip/dns/static add address=1.0.0.1 name=security.cloudflare-dns.com com\ + ment=\"cloudflare-dns IPv4 2\"\r\ + \n#/ip dns static add address=2606:4700:4700::1111 name=security.cloudflar\ + e-dns.com type=AAAA\r\ + \n#/ip dns static add address=2606:4700:4700::1001 name=security.cloudflar\ + e-dns.com type=AAAA\r\ + \n#/ip/dns set servers=\"1.0.0.1, 8.8.4.4\";\r\ + \n#/ip/dns set servers=\"\";" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n:global GlobalScriptsUrl;\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n" +add dont-require-permissions=no name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + :put \"Download image, and save him to usb1/tmp\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-arm.tar\" dst-\ + path=usb1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 \\\ + \n action=dst-nat to-addresses=10.9.9.2 to-ports=55055 in-interface=eth\ + er1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 \\\ + \n action=dst-nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether\ + 1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=usb1/tmp/awg2-webui.tar interface=veth-awg hostname=\ + awg2-webui \\\ + \nroot-dir=usb1/awg2-webui envlist=awgui-env start-on-boot=yes logging=yes\ + \_shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +/container +# killed due to out of memory +add cpu-list=cpu1,cpu3 domain-name=vados.ru envlists=awgui-env file=\ + usb1/tmp/awg2-webui.tar hostname=mikronuc interface=veth-awg layer-dir=\ + /usb1/docker logging=yes memory-high=12.0MiB memory-max=16.0MiB name=\ + awg2-webui root-dir=/usb1/awg-webui shm-size=8.0MiB start-on-boot=yes \ + tmpfs=/tmpfs:512.0MiB:01000 +/container config +set layer-dir=/usb1/docker memory-high=2048.0KiB memory-max=2048.0KiB \ + registry-url=https://registry-1.docker.io tmpdir=/tmp username=amsternl +/container envs +add key=AWG_ENDPOINT list=awgui-env value=10.30.30.10:443 +add key=SECRET_KEY list=awgui-env value=RanDomSecret32SymboLs2325272931q +add key=WEB_PASS list=awgui-env value=zoomzoomrulezz +add key=WEB_USER list=awgui-env value=vados +/disk settings +set auto-media-interface=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether1 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=ether4 +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +add bridge=*D interface=*C +add bridge=*F interface=*E +add bridge=*11 interface=*10 +add bridge=*13 interface=*12 +add bridge=*15 interface=*14 +add bridge=br-awg interface=veth-awg +/ip neighbor discovery-settings +set discover-interface-list=Internet +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes \ + forward=no +/interface detect-internet +set detect-interface-list=static internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether2 list=LAN +add interface=ether3 list=LAN +add interface=ether4 list=LAN +add interface=ether5 list=LAN +add interface=wlan2 list=LAN +add interface=wlan1 list=LAN +add interface=ether1 list=Internet +add interface=Bridge-LAN list=LAN +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-18FD746E5814 discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan1,wlan2 lock-to-caps-man=yes +/ip address +add address=10.30.30.86/24 interface=ether1 network=10.30.30.0 +add address=10.9.9.1/24 interface=br-awg network=10.9.9.0 +/ip dns +set allow-remote-requests=yes servers=10.30.30.33,109.107.177.79 +/ip firewall nat +add action=masquerade chain=srcnat src-address=10.9.9.0/24 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=443 in-interface=ether1 protocol=udp \ + to-addresses=10.9.9.2 to-ports=55055 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=9443 in-interface=ether1 protocol=\ + tcp to-addresses=10.9.9.2 to-ports=443 +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip hotspot walled-garden +add server=*1 +add server=*1 +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=ping disabled=no distance=2 dst-address=0.0.0.0/0 gateway=\ + ether1 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set graphs-plain=no rest-plain=no webfig-plain=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=109.107.177.79 interface=*A type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 nd +set [ find default=yes ] advertise-dns=yes +/system clock +set time-zone-name=Europe/Amsterdam +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroGig +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=10.30.30.81 +add address=10.30.30.10 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com diff --git a/MikroGig/history/20260629/20260629154231-Diff-Config.rsc b/MikroGig/history/20260629/20260629154231-Diff-Config.rsc new file mode 100644 index 0000000..85b3217 --- /dev/null +++ b/MikroGig/history/20260629/20260629154231-Diff-Config.rsc @@ -0,0 +1,4 @@ +12c12 +< # channel: 2442/20-Ce/gn(17dBm), SSID: vados, local forwarding +--- +> # channel: 2422/20-Ce/gn(17dBm), SSID: vados, local forwarding diff --git a/MikroMaster/20260629/20260629144003-Config.rsc b/MikroMaster/20260629/20260629144003-Config.rsc new file mode 100644 index 0000000..d903ec5 --- /dev/null +++ b/MikroMaster/20260629/20260629144003-Config.rsc @@ -0,0 +1,3693 @@ +# 2026-06-29 14:40:09 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2422/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/720/6faffeff\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 02:10:04 29.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add disabled=yes interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:2 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.33,109.107.177.79 domain=WORKGROUP gateway=10.30.30.81 netmask=\ + 24 ntp-server=10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=109.107.177.79,10.30.30.33,8.8.4.4 \ + verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface="ether1 - Unet" protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + disabled=yes new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=*401 routing-mark=!main src-address=10.30.30.0/24 \ + src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN routing-mark=!main src-address=10.30.30.0/24 +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +#error exporting "/ip/ipsec/key/psk" (timeout) +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-plain=no rest-secure=no scep-plain=no \ + webfig-plain=no webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/20260629/20260629150002-Config.rsc b/MikroMaster/20260629/20260629150002-Config.rsc new file mode 100644 index 0000000..76585a4 --- /dev/null +++ b/MikroMaster/20260629/20260629150002-Config.rsc @@ -0,0 +1,3693 @@ +# 2026-06-29 15:00:07 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2422/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/720/6faffeff\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 02:10:04 29.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add disabled=yes interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:2 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.33,109.107.177.79 domain=WORKGROUP gateway=10.30.30.81 netmask=\ + 24 ntp-server=10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=109.107.177.79,10.30.30.33,8.8.4.4 \ + verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface="ether1 - Unet" protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + disabled=yes new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=*401 routing-mark=!main src-address=10.30.30.0/24 \ + src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN routing-mark=!main src-address=10.30.30.0/24 +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-plain=no rest-secure=no scep-plain=no \ + webfig-plain=no webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing settings +set single-process=no +#error exporting "/snmp/community" (timeout) +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/20260629/20260629152001-Config.rsc b/MikroMaster/20260629/20260629152001-Config.rsc new file mode 100644 index 0000000..89cb1e8 --- /dev/null +++ b/MikroMaster/20260629/20260629152001-Config.rsc @@ -0,0 +1,3692 @@ +# 2026-06-29 15:20:06 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2422/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/720/6faffeff\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 02:10:04 29.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add disabled=yes interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:2 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.33,109.107.177.79 domain=WORKGROUP gateway=10.30.30.81 netmask=\ + 24 ntp-server=10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=109.107.177.79,10.30.30.33,8.8.4.4 \ + verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface="ether1 - Unet" protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + disabled=yes new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=*401 routing-mark=!main src-address=10.30.30.0/24 \ + src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN routing-mark=!main src-address=10.30.30.0/24 +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-plain=no rest-secure=no scep-plain=no \ + webfig-plain=no webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/tmp/Diff-Config.rsc b/MikroMaster/tmp/Diff-Config.rsc deleted file mode 100644 index 9a463cc..0000000 --- a/MikroMaster/tmp/Diff-Config.rsc +++ /dev/null @@ -1,2 +0,0 @@ -3633a3634 -> #error exporting "/system/routerboard/settings" (timeout) -- 2.52.0