aboutsummaryrefslogblamecommitdiffstats
path: root/global-functions
blob: 1b73b0a70afb5cdd90a1f036262ed88e0559a2dc (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                   
                                                         

                  
                                
                                
                                            
                                          

                                                
 
                      
                             
 
                                



                                                                                       
 

                                               
 
                                      
       
                                    

     
                  
 




                                   
                   
 


                              
                                                                   
                                              
                                                                    
   
                              
 
                                        
                                  
 
                                 
 
                    
                      
                                                                                 
                                                    
                                                               
                                                      
                                             



                                                                  




                                                  
                              
                               
                               
 

                         
 
                                         
         


                                                  
                                                                
                                                     


                                                       




                                
                               





                               
 
                                                                          
                                                                       
                                                                                      
                                                                              
                                                                                           



                                                           
 
                                           
                                                                             


                               
                               



                                       
                                          

 
                         
                           
 
                               
       
                  
                                                       
                                                                   
                                                                                             


                             
 

















                                                       
                                      

                               
                               
                               
                        
                      
 

                                                                                                    
 
                                                                      

                                                     
                                                               
 
                                                     
       
                                                    
                                                                             
                        
                          
              
                                               
                  
   
 



                                                                          
               
 








                                                                              















                                                                  














                                                                       











                                                            





                                   
#!rsc
# RouterOS script: global-functions
# Copyright (c) 2013-2019 Christian Hesse <mail@eworm.de>
#
# global functions

# expected configuration version
:global ExpectedConfigVersion 8;

# global variables not to be changed by user
:global SentConfigChangesNotification "-";
:global SentRouterosUpdateNotification "-";
:global SentLteFirmwareUpgradeNotification "-";
:global Identity [ / system identity get name ];

# url encoding
:global UrlEncode do={
  :local Input [ :tostr $1 ];
  :local Return "";

  :if ([ :len $Input ] > 0) do={
    :local Chars " !\"#\$%&'()*+,-./:;<=>\?@[\\]^_`{|}~";
    :local Subs { "%20"; "%21"; "%22"; "%23"; "%24"; "%25"; "%26"; "%27"; "%28"; "%29";
                  "%2A"; "%2B"; "%2C"; "%2D"; "%2E"; "%2F"; "%3A"; "%3B"; "%3C"; "%3D";
                  "%3E"; "%3F"; "%40"; "%5B"; "%5C"; "%5D"; "%5E"; "%5F"; "%60"; "%7B";
                  "%7C"; "%7D"; "%7E" };

    :for I from=0 to=([ :len $Input ] - 1) do={
      :local Char [ :pick $Input $I ];
      :local Replace [ :find $Chars $Char ];

      :if ([ :len $Replace ] > 0) do={
        :set Char ($Subs->$Replace);
      }
      :set Return ($Return . $Char);
    }
  }

  :return $Return;
}

# character replace
:global CharacterReplace do={
  :local String [ :tostr $1 ];
  :local ReplaceFrom [ :tostr $2 ];
  :local ReplaceWith [ :tostr $3 ];
  :local Len [ :len $ReplaceFrom ];
  :local Return "";

  :if ($ReplaceFrom = "") do={
    :return $String;
  }

  :while ([ :typeof [ :find $String $ReplaceFrom ] ] != "nil") do={
    :local Pos [ :find $String $ReplaceFrom ];
    :set Return ($Return . [ :pick $String 0 $Pos ] . $ReplaceWith);
    :set String [ :pick $String ($Pos + $Len) 999 ];
  }

  :return ($Return . $String);
}

# check and import required certificates
:global CertificateAvailable do={
  :local CommonName [ :tostr $1 ];

  :global ScriptUpdatesBaseUrl;
  :global ScriptUpdatesUrlSuffix;

  :global UrlEncode;
  :global WaitForFile;

  :if ([ / certificate print count-only where common-name=$CommonName ] = 0) do={
    :log info ("Certificate with CommonName " . $CommonName . \
      " not available, downloading and importing.");
    :do {
      :local LocalFileName ($CommonName . ".pem");
      :local UrlFileName ([ $UrlEncode $CommonName ] . ".pem");
      / tool fetch check-certificate=yes-without-crl \
        ($ScriptUpdatesBaseUrl . "certs/" . \
        $UrlFileName . $ScriptUpdatesUrlSuffix) \
        dst-path=$LocalFileName;
      $WaitForFile $LocalFileName;
      / certificate import file-name=$LocalFileName passphrase="";
      / file remove $LocalFileName;
    } on-error={
      :log warning "Failed imprting certificate!";
    }
  }
}

# send notification via e-mail
:global SendEMail do={
  :local Subject [ :tostr $1 ];
  :local Message [ :tostr $2 ];
  :local Attach  [ :tostr $3 ];

  :global Identity;
  :global EmailGeneralTo;
  :global EmailGeneralCc;

  :if ([ :len $EmailGeneralTo ] > 0) do={
    :do {
      :local Signature [ / system note get note ];
      :if ([ :len $Signature ] > 0) do={
        :set Signature ("\n-- \n" . $Signature);
      }
      / tool e-mail send to=$EmailGeneralTo cc=$EmailGeneralCc \
        subject=("[" . $Identity . "] " . $Subject) \
        body=($Message . $Signature) file=$Attach;
    } on-error={
      :log warning "Failed sending notification mail!";
    }
  }
}

# send notification via telegram
:global SendTelegram do={
  :local Subject [ :tostr $1 ];
  :local Message [ :tostr $2 ];
  :local Silent  [ :tostr $3 ];

  :global Identity;
  :global TelegramTokenId;
  :global TelegramChatId;

  :global UrlEncode;
  :global CertificateAvailable;

  :if ([ :len $TelegramTokenId ] > 0 && [ :len $TelegramChatId ] > 0) do={
    $CertificateAvailable "Go Daddy Secure Certificate Authority - G2";
    :do {
      / tool fetch check-certificate=yes-without-crl keep-result=no http-method=post \
        ("https://api.telegram.org/bot" . $TelegramTokenId . "/sendMessage") \
        http-data=("chat_id=" . $TelegramChatId . "&disable_notification=" . $Silent . \
        "&text=" . [ $UrlEncode ("[" . $Identity . "] " . $Subject . "\n\n" . $Message) ]);
    } on-error={
      :log warning "Failed sending telegram notification!";
    }
  }
}

# send notification via e-mail and telegram
# Note that attachment is ignored for telegram, silent is ignored for e-mail!
:global SendNotification do={
  :local Subject [ :tostr $1 ];
  :local Message [ :tostr $2 ];
  :local Attach  [ :tostr $3 ];
  :local Silent  [ :tostr $4 ];

  :global SendEMail;
  :global SendTelegram;

  $SendEMail $Subject $Message $Attach;
  $SendTelegram $Subject $Message $Silent;
}


# get MAC vendor
:global GetMacVendor do={
  :local Mac [ :tostr $1 ];

  :global CertificateAvailable;

  :do {
    :local Vendor;
    $CertificateAvailable "Let's Encrypt Authority X3";
    :set Vendor ([ / tool fetch check-certificate=yes-without-crl \
        ("https://api.macvendors.com/" . [ :pick $Mac 0 8 ]) output=user as-value ]->"data");
    :return $Vendor;
  } on-error={
    :return "unknown vendor";
  }
}

# clean file path
:global CleanFilePath do={
  :local Path [ :tostr $1 ];

  :global CharacterReplace;

  :while ($Path ~ "//") do={
    :set $Path [ $CharacterReplace $Path "//" "/" ];
  }
  :if ([ :pick $Path 0 ] = "/") do={
    :set Path [ :pick $Path 1 [ :len $Path ] ];
  }
  :if ([ :pick $Path ([ :len $Path ] - 1) ] = "/") do={
    :set Path [ :pick $Path 0 ([ :len $Path ] - 1) ];
  }

  :return $Path;
}

# download package from upgrade server
:global DownloadPackage do={
  :local PkgName [ :tostr $1 ];
  :local PkgVer  [ :tostr $2 ];
  :local PkgArch [ :tostr $3 ];
  :local PkgDir  [ :tostr $4 ];

  :global CertificateAvailable;
  :global CleanFilePath;
  :global WaitForFile;

  :if ([ :len $PkgName ] = 0) do={ return false; }
  :if ([ :len $PkgVer  ] = 0) do={ :set PkgVer  [ / system package update get installed-version ]; }
  :if ([ :len $PkgArch ] = 0) do={ :set PkgArch [ / system resource get architecture-name ]; }

  :local PkgFile ($PkgName . "-" . $PkgVer . "-" . $PkgArch . ".npk");
  :if ($PkgArch = "x86_64") do={
    :set PkgFile ($PkgName . "-" . $PkgVer . ".npk");
  }
  :local PkgDest [ $CleanFilePath ($PkgDir . "/" . $PkgFile) ];

  $CertificateAvailable "Let's Encrypt Authority X3";
  :do {
    / tool fetch check-certificate=yes-without-crl \
      ("https://upgrade.mikrotik.com/routeros/" . $PkgVer . "/" . $PkgFile) \
      dst-path=$PkgDest;
    $WaitForFile $PkgDest;
  } on-error={
    / file remove [ find where name=$PkgDest ];
    :return false;
  }

  :if ([ / file get [ find where name=$PkgDest ] type ] != "package") do={
    / file remove [ find where name=$PkgDest ];
    :return false;
  }

  :return true;
}

# lock script against multiple invocation
:global ScriptLock do={
  :local Script [ :tostr $1 ];

  :if ([ / system script job print count-only where script=$Script ] > 1) do={
    :log debug ("Script " . $Script . " started more than once... Aborting.");
    :error "Locked."
  }
}

# wait for file to be available
:global WaitForFile do={
  :global CleanFilePath;

  :local FileName [ $CleanFilePath [ :tostr $1 ] ];
  :local I 0;

  :while ([ file print count-only where name=$FileName ] = 0) do={
    :if ($I > 20) do={
      :return false;
    }
    :delay 100ms;
    :set I ($I + 1);
  }
  :return true;
}

# parse key value store
:global ParseKeyValueStore do={
  :global CharacterReplace;

  :local Source $1;
  :if ([ :typeof $Source ] != "array") do={
    :set Source [ :tostr $1 ];
  }
  :local Result [ :toarray "" ];
  :foreach KeyValue in=[ :toarray $Source ] do={
    :set KeyValue [ :toarray [ $CharacterReplace $KeyValue "=" "," ] ];
    :set ($Result->($KeyValue->0)) ($KeyValue->1);
  }
  :return $Result;
}

# generate random number
# Warning: This is a *very* weak algorithm and in *no way*
# useful for cryptography or similar!
:global GetRandom do={
  :local Max ([ :tonum $1 ] + 1);
  :local Sum 0;

  :foreach Interface in=[ /interface find ] do={
    :set Sum ($Sum + [ /interface get $Interface tx-byte ]);
  }
  :return ($Sum % $Max);
}

# delay a random amount of seconds
:global RandomDelay do={
  :global GetRandom;

  :delay ([ $GetRandom $1 ] . "s");
}