1

Тема: VBS: мониторинг попаданий в DNSBL

Приветствую!

Ситуация - есть у нас один большой клиент. Можно сказать ключевой. И есть у него неприятная черта - его почтовый сервер на DNSBL прямо-таки молится.
В общем стоит попасть в блэклист баракуды и начинается коллапс с недоходящей до них почтой(ибо баллами они не пользуются, а блокируют безоговорочно). Т.к. в нескольких случаях ситуация возникала в пятницу под вечер, то наши сотрудники следуя традиции ("само исправится" - ведь даже менеджеры, свободно общающиеся на английском с клиентами, оказываются неспособны прочитать банальное уведомление о блокировке) сообщали о проблеме только в понедельник. А т.к. удаление из списка процесс не быстрый, то пол дня без возможности связаться с клиентом. Момент крайне неприятный.

Собственно для того чтобы оперативно отслеживать данную ситуацию и решил написать скриптик(в бою пока не был, но на IP из серверных логов потестировал). Надеюсь будет полезен не только мне.


' VB Script Document
Option Explicit

' http://www.barracudacentral.org/rbl/how-to-use
' http://ru.wikipedia.org/wiki/DNSBL
' если IP есть в блоклисте, то будет резолвиться хостнейм вида IP_наоборот.black.list.hostname
' например, для 192.149.23.63 на баракуде нужно проверять 63.23.149.192.b.barracudacentral.org
' резолвиться будет в адрес 127.0.0.x (обычно, но не обязательно)
' X - в зависимости от причины попадания в список(хотя официального документа на этот счёт видимо нет)
' http://www.spamhaus.org/faq/answers.lasso?section=DNSBL%20Usage#202 - расшифровка X для SPAMHOUSE

Dim RegPath, Write2Reg
RegPath = "HKLM\Software\VBS Scripts Settings\DNSBL_Monitoring" ' Храним тут
Write2Reg = true ' false - отключает запись в реестр(а вдруг будет нужно?)

' Параметры отправки почты
Dim smtp_host, smtp_auth, smtp_port, smtp_user, smtp_pass, from_email, to_email
smtp_host = "mail.mydomen.com"
smtp_auth = 0 ' если 1(т.е. с авторизацией), то пароль нужен обязательно
smtp_port = 25
smtp_user = "myusername"
smtp_pass = "mypassword"
from_email = "dnsbl.monitoring@ru.mydomen.com"
to_email = "my.name@ru.mydomen.com"

Dim DNSBL, Hosts, TestHost
TestHost = "8.8.8.8" ' "неумерающий" хост для проверки доступности интернета, например публичный ДНС гугла

Set DNSBL = CreateObject("Scripting.Dictionary") 'список блэклистов
DNSBL.Add "b.barracudacentral.org", "Вражина №1"
DNSBL.Add "sbl.spamhaus.org", "SBL Spamhaus"
DNSBL.Add "xbl.spamhaus.org", "XBL Spamhaus"
DNSBL.Add "pbl.spamhaus.org", "PBL Spamhaus"
'DNSBL.Add "zen.spamhaus.org", "ZEN Spamhaus"
' OFF потому что объединяет ответ не только с SBL, XBL, PBL, но и с cbl.abuseat.org
' к тому-же если IP числится только в cbl.abuseat.org(а не в SBL, XBL or PBL),
' то сайт выдаёт "LISTED", а по лукапу выходит NOTLISTED
' подробности - http://www.spamhaus.org/faq/answers.lasso?section=DNSBL%20Usage#252
DNSBL.Add "cbl.abuseat.org", "cbl.abuseat.org"
DNSBL.Add "bl.spamcop.net", "bl.spamcop.net"
DNSBL.Add "dnsbl.sorbs.net", "dnsbl.sorbs.net"
DNSBL.Add "web.dnsbl.sorbs.net", "web.dnsbl.sorbs.net"
DNSBL.Add "bl.tiopan.com", "bl.tiopan.com"

Set Hosts = CreateObject("Scripting.Dictionary") 'список проверяемых IP-адресов
Hosts.Add "94.100.177.6", "pop3.mail.ru"
Hosts.Add "74.125.39.109", "pop.gmail.com"
Hosts.Add "87.250.250.124", "imap.yandex.ru"
Hosts.Add "86.35.249.224", "Blocked IP2"
Hosts.Add "194.250.63.103", "Blocked IP"

'##############################################################################
'######                      все настройки выше                          ######
'##############################################################################
Dim TotalTimer
TotalTimer = Timer

' объекты:
Dim WshShell
Set WshShell = WScript.CreateObject("WScript.Shell")

Dim Key ' для возврата значений ключа реестра
Dim arrDNSBL, arrHosts
arrDNSBL = DNSBL.Keys
arrHosts = Hosts.Keys

Dim BBody, Need2Report
BBody = "<HTML> <meta http-equiv=""Content-Type"" content=""text/html; charset=windows-1251"" /> <b>DNSBL_Monitoring</b><br>начало работы скрипта: " & Now & "<hr>" ' "начинаем" тело
Need2Report = false ' если появятся новые данные по блокировкам, станет true


Dim i, j
For i = LBound(arrDNSBL) To UBound(arrDNSBL) 'пинганём список...
 DNSBL.Item(arrDNSBL(i)) = vbNullstring ' очистка перед заполнением
 For j = LBound(arrHosts) To UBound(arrHosts) 'пинганём список...
  'читаем соотв. ключ в реестре и проверяем что IP ещё не там
' ##############################################################################
  if Ping(TestHost) then ' инет есть, можно проверять BL
    Key = DoTheKey(RegPath, arrDNSBL(i), false, vbNullstring)
    'wscript.echo "Key = " & Key
'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    if StrComp(Key, vbNullstring, vbTextCompare) <> 0 then ' если ключ непустой - проверяем есть ли в нём IP, иначе переход на проверку хоста

      if InStr(1, Key, arrHosts(j), vbTextCompare) > 0 then 'если IP уже блокировался
        if not IsInBL(arrDNSBL(i), arrHosts(j)) then ' если более не в списке - уведомляем что IP удалён из BL
          BBody = BBody & "IP-адрес " & arrHosts(j) & "(" & Hosts.Item(arrHosts(j)) & ") был удалён из DNSBL: " & arrDNSBL(i) & "<br>"
          Need2Report = true
        end if
       else '<<2>> ранее IP в BL не числился
        if IsInBL(arrDNSBL(i), arrHosts(j)) then ' попал в список - уведомляем
          BBody = BBody & "IP-адрес " & arrHosts(j) & "(" & Hosts.Item(arrHosts(j)) & ") был занесён в DNSBL: " & arrDNSBL(i) & "<br>"
          Need2Report = true
        end if
      end if ' IP в списке?
       
    else '<1> если Key пустой => блоклист ранее не проверялся

     if IsInBL(arrDNSBL(i), arrHosts(j)) then ' попал в список - уведомляем
       BBody = BBody & "IP-адрес " & arrHosts(j) & "(" & Hosts.Item(arrHosts(j)) & ") был занесён в DNSBL: " & arrDNSBL(i) & "<br>"
       Need2Report = true
     end if

   end if
'                                      >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

     else ' или инета нет, или гугл накрылся :)

'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      wscript.echo "Паночка померла :)"
'                                      >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  end if ' жив ли гугл?
 Next 'j
 Key = DoTheKey(RegPath, arrDNSBL(i), true, DNSBL.Item(arrDNSBL(i)))
Next 'i
BBody = BBody & "<hr>" & Now & " => Проверка завершена. Продолжительность проверки: " & Timer - TotalTimer & " сек."
BBody = BBody & "</HTML>"

if Need2Report then call SendMail("DNSBL Monitoring Report", BBody) ' если были подвижки в списках, надо уведомить

'###############################################################################
function IsInBL(DNSBL_name, HOST2Chk) 'Оцениваем результаты пинга
 if Ping(RevIP(HOST2Chk) & "." & DNSBL_name)  then 'если до сих пор в BL то...
   ' переформируем список заблокированных хостов для этого BL
   IsInBL = true
   if DNSBL.Item(DNSBL_name) = vbNullstring then
     DNSBL.Item(DNSBL_name) = HOST2Chk
    else
     DNSBL.Item(DNSBL_name) = DNSBL.Item(DNSBL_name) & "|" & HOST2Chk
   end if
  else
   IsInBL = false
 end if
end function
'###############################################################################
Function Ping(Ip2Ping) 'пингуем хост
 Dim Hosts(), strComputer, objWMIService, colPings, objStatus
 Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
 Set colPings = objWMIService.ExecQuery _
    ("Select * From Win32_PingStatus where Address = '" & Ip2Ping & "'")
 For Each objStatus In colPings
  If IsNull(objStatus.StatusCode) Or objStatus.StatusCode<>0 Then
    'в принципе, уже не пингуется... но дополнительно проверим и IP(зарезолвился или нет)
    If objStatus.ProtocolAddress = vbNullstring Then Ping = False
   Else
    if StrComp(TestHost, Ip2Ping, vbTextcompare) <> 0 then
     WScript.Echo "ping host " & Ip2Ping & " .... LISTED"
     wscript.echo "Return IP: " & objStatus.ProtocolAddress
    end if
    Ping = True
  End If
 Next
End Function
'###############################################################################
Function RevIP(IP) ' "переворачиваем" IP-адрес
 Dim arrIPs, i
 RevIP = vbNullString
 arrIPs = Split(IP, ".", -1, vbTextCompare)
 If UBound(arrIPs) > 3 Then wscript.echo "Неверный IP! " & IP : wscript.quit
 For i = UBound(arrIPs) To LBound(arrIPs) step -1
  If arrIPs(i) > 254 Then wscript.echo "Неверный IP! " & IP : wscript.quit
  If i = 3 Then RevIP = arrIPs(i) Else RevIP = RevIP & "." & arrIPs(i)
 Next
End Function
'###############################################################################
' чтение-создание ключей в реестре
' если Make = true, то записываем значение KeyValue в ключ
' если Make = false, то пытаемся считать ключ и при его отсутствии создаём с значением KeyValue
Function DoTheKey(RegPath, RegKey, Make, KeyValue)
If Make and not Write2Reg Then ' Есди писАть в реестр запрещено, то писАть в реестр нельзя
  Exit Function
End If
If Make Then ' если задача записать, то:
  WshShell.RegWrite RegPath & "\" & RegKey, KeyValue, "REG_SZ"
 Else ' если только прочесть, то всё немного сложнее :)
  On Error Resume Next ' половим ошибки при отсутствии ключа(вариант что прав на чтения не хватает не рассматривается)
  DoTheKey = WshShell.RegRead(RegPath & "\" & RegKey)
  If Err.Number <> 0 Then
    wscript.echo "не найден ключ или раздел в реестре: " & RegPath & "\" & RegKey
    Err.Clear   ' Clear the error.
    WshShell.RegWrite RegPath & "\" & RegKey, KeyValue, "REG_SZ" ' если ключа нет, то создаём его и заносим в него переданное значение(KeyValue)
      If Err.Number = 0 Then
        wscript.echo "в реестре создан ключ: [" & RegPath & "\" & RegKey & "] со значением [" & KeyValue & "]."
        DoTheKey = WshShell.RegRead(RegPath & "\" & RegKey)
      End If
    Else
'    wscript.echo "Значение ключа " & RegKey & " = [" & WshShell.RegRead(RegPath & "\" & RegKey) & "]"
    DoTheKey = WshShell.RegRead(RegPath & "\" & RegKey)
  End If
  Err.Clear   ' Clear the error.
  On Error Goto 0
End If
End Function
'###############################################################################
Sub SendMail(Subject, Body)' отправка уведомления через CDO
Dim iMsg, iConf, Flds
  Set iMsg=CreateObject("CDO.Message")
  Set iConf=CreateObject("CDO.Configuration")
  Set Flds=iConf.Fields
'http://msdn.microsoft.com/en-us/library/ms873037(EXCHG.65).aspx
'The mechanism to use to send messages.
' cdoSendUsingPort (2)
  Flds.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
'The authentication mechanism to use when authenticating to a SMTP service over the network.
'This field is relevant only if the http://schemas.microsoft.com/cdo/configuration/sendusing field is set to cdoSendUsingPort.
  Flds.Item("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = 10
  Flds.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = smtp_auth
  Flds.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = smtp_host
  Flds.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = smtp_port
  Flds.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = smtp_user
  Flds.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = smtp_pass
  Flds.Item("http://schemas.microsoft.com/cdo/configuration/languagecode") = "ru" '?
  Flds.Update
  iMsg.Configuration = iConf
  iMsg.To = to_email
  iMsg.From = from_email
  iMsg.Subject = Subject
  iMsg.BodyPart.Charset = "windows-1251"
  iMsg.HtmlBody =  Body
  iMsg.Send

Set iMsg=Nothing
Set iConf=Nothing
Set Flds=Nothing
End Sub

Принцип работы простой: два словаря(блэклисты и IP офисов и сервера), проверяем присутствие IP в чёрном списке(или исчезновение оттуда) и уведомляем об этом на почту. Список текущих блокировок хранится в реестре(я сохраняю в HKLM, чтобы можно было в любой момент без выкрутасов посмотреть что там ). Если IP уже числится в реестре, то по нему повторных отчётов не шлётся. Ну и чтобы для проверки наличия интернета пингуем гугл.
При отсутсвии ветки/параметра в реестре они создаются автоматически, в процессе работы параметры из этой ветки могут перезаписываться - несмотря на п.6.5 отключать запись в реестр по умолчанию не стал(переменная Write2Reg - рудимент, присутствующий у меня везде где есть зработа с реестром ). Во-первых, данная ветка по умолчанию отсутствует, во-вторых, без неё полноценно скрипт функционировать не будет.

Метод проверки есть в скрипте, на википедии, в FAQ спамхауса(ссылки в коментариях скрипта), поэтому дублировать не буду.

Единственное что не протестировал - ситуацию с удалением IP из DNSBL - своих IP в блоках не имею(ттт), а чужие разблокировать не буду(ибо скорее всего спамерские - из логов сервера брал) - если кто-то может протестировать, буду рад. Или буду ждать попадания наших IP в BL.

Список DNSBL можно посмотреть здесь(кстати, удобный инструмент для ручной проверки попадания в BL).

P.S. Остался правда один момент - команда вроде такой "nslookup -type=ANY 103.63.250.194.b.barracudacentral.org" даёт больше информации, и если в случае с баракудой пользы от ней немного(выдаёт только ссылку на проверку IP), то для спамхауза это не так. У спамхауза в возвращаемом адресе "закодирован" как минимум список в который IP попал - но это в теории. В практике в спамхаузом пока не срабатывает  (для IP из PBL-списка), впрочем я так до конца не понял систему взаимодействия их BL. Отсюда вопрос: есть ли под VBScript класс позволяющий выполнять запросы к DNS-серверу? В MSDN пока наткнулся на DNS WMI Provider, но беглый осмотр навёл на мысль что он предназначен для управления MS DNS-сервером. Глубже пока не копал - текущего скрипта мне достаточно(подробности через nslookup узнаю)... В идеале обойтись без сторонних контролов