Автоматизация получения реестра запрещенных ресурсов средствами C#, OpenSSL и фильтрация средствами RouterOS на базе оборудования MikroTik

Внимание! Статья изменена согласно последних изменений на 19/03/2014
В работе будем руководствоваться памяткой оператора связи.
Работа состоит из следующих пунктов:
Для подачи запроса на получение выгрузки из реестра необходимо прикрепить файл запроса в формате XML. Файл имеет следующий вид:
Главное получить время в формате 2012-01-01T01:01:01.000+04:00 и сохранить файл с кодировкой windows-1251.
Код функции генерации запроса на C#:
Теперь сохраняем файл в кодировке windows-1251:
После создания запроса нам необходимо его подписать. Для этого воспользуемся криптографическим пакетом с открытым исходным кодом для работы с SSL/TLS — OpenSSL. Установим и настроем.
Качаем пакет с этого сайта — slproweb.com. В моем случае это был Win64 OpenSSL v1.0.1g.
Да эта сборка требует для работы установленного Visual C++ 2008 Redistributables, который можно скачать там же.
Устанавливаем. При установке в диалоге «Select Additional Tasks» следует выбрать «The OpenSSL” binaries (/bin) directory» более хитростей нет.
Далее переходим в папку куда установили: C:/OpenSSL/bin и редактируем файл openssl.cfg. В начало файла добавим:
В конце:
Все практически уже работает осталось настроить переменные среды:
OPENSSL_CONF = C:/OpenSSL/bin/openssl.cfg — полный путь к openssl.cfg
ну и в PATH += C:/OpenSSL/bin;
Теперь нам нужна ЭЦП. Приобрести ее можно в доверенном удостоверяющем центре. Ключ необходимо экспортировать в формате PKCS#12 из криптоконтейнера в Windows с помощью утилиты P12FromGostCSP
Файл PKCS#12 должен содержать сертификат и закрытый ключ! Проверить это можно командой:

Далее преобразовать его в PEM. В OpenSSL это делается так (через командную строку — может запросить пароль которым защищен Ключ PKCS#12):

Все теперь мы в состоянии подписать наш запрос.
Подписать запрос можно через OpenSSL следующей командой:

Реализация функции подписи файла на C#:
Теперь проверим нашу работу. Подтверждение подлинности ЭП: gosuslugi.ru, выбираем — Подтверждение подлинности электронного документа. ЭП — отсоединенная, в формате PKCS#7
Можно проверить и средствами OpenSSL, но для этого нужно извлечь сертификат издателя, которая предоставила ЭЦП и конвертировать ее в PEM формат (сертификат можно еще взять тут):
И проверяем нашу подпись:
В результате мы увидим строчку:
а в файле validrequest.xml будет лежать содержимое request.xml.
Переходим непосредственно к запросу дампа реестра запрещенных ресурсов.
В ручную все просто: Форма подачи запроса, отправляем файл запроса и его подпись (C:/request.xml и C:/request.xml.sign)

Если все нормально то результатом будет — Идентификатор запроса, с помощью его можно проверять результат обработки запроса. Как правило если все нормально то реестр будет выгружен минут через 5 в ZIP формате в архиве будет два файла dump.xml — дамп реестра и его цифровая подпись.
Проверить файл можно OpenSSL или gosuslugi.ru:
В результате мы увидим строчку:
а в файле validdump.xml будет лежать содержимое dump.xml.
Теперь автоматизируем данный процесс. Для выгрузки есть сервис работающий по протоколу доступа SOAP, адрес: сервиса. WSDL схема доступна по адресу: WSDL схема.
Сервис состоит из 4-х методов:
Возможные значения тега resultComment:
Информация по обратной связи для разрешения проблем приведена в Памятке оператору связи в этом разделе.
Логика работы с сервисом:
Добавляем в ресурсы сервис, передавая адрес WSDL схемы.
Код функций getLastDumpDate, sendRequest, getResult
Например отправим запрос:
Временную метку последнего обновления выгрузки из реестра можно конвертировать из UNIX Timestamp в DateTime вот так:
Надо распаковать архив:
В результате получаем дамп реестра C:/register/dump.xml
Пример содержимого дампа:
Для этого создадим два класса: первый RegisterDump будет содержать поле UpdateTime и список объектов content, Второй ItemRegisterDump представляет один объект content со всеми полями.
Парсим:
Теперь блокируем все это добро на нашем MikroTik.
Делаем мы это с помощью layer7-protocol и добавляем в фильтры.
Вот пример:




Все теперь все пакеты для подсети 192.168.0.0/24 приходящие по TCP на 80 порт с содержанием подстроки chelaxe.ru отбрасываются.
В комментарии надпись register добавляется не с проста. Она нужна нам будет для удаления всех правил прежде чем добавить обновленные.
Вот скрипт который удалит все записи:

Общение с MikroTik роутером будет посредством API, необходимую библиотеку с примерами нам уже написали: wiki/API C#
Используя эту библиотеку (класс) и принцип блокирования контента разобранный выше реализуем все это на C#
В примере я использовал блокировку по домену, но это не совсем верно. Для блокировки по URL можно использовать следующие регулярное выражение для layer7-protocol:
Причем выражение должно быть в нижнем регистре при использовании версий RouterOS из 5 ветки, в следствии допущенной ошибки MikroTik.
Исходники доступны здесь.
При создании руководствовался:
Памятка оператора связи
OpenSSL 1.0.1с + libGOST.so + КриптоПРО + ключ из ruToken
Как мы получали доступ к базе реестра запрещенных ресурсов
Роскомнадзор объявил о введении в работу реестра сайтов с «пиратскими» фильмами
RegExr intuitive tool for learning, writing, and testing Regular Expressions

Внимание! Статья изменена согласно последних изменений на 19/03/2014
Вводное
В работе будем руководствоваться памяткой оператора связи.
Работа состоит из следующих пунктов:
- Создание запроса
- Установка и настройка OpenSSL
- Подпись запроса
- Подача запроса и получение результата обработки запроса
- Обработка результата
- Добавление в фильтр на MikroTik`е
Создание запроса
Для подачи запроса на получение выгрузки из реестра необходимо прикрепить файл запроса в формате XML. Файл имеет следующий вид:
<?xml version="1.0" encoding="windows-1251"?> <request> <requestTime>2012-01-01T01:01:01.000+04:00</requestTime> <operatorName><![CDATA[Наименование оператора]]></operatorName> <inn>1234567890</inn> <ogrn>1234567890123</ogrn> <email>email@email.ru</email> </request>
- requestTime – дата и время формирования запроса с указанием временной зоны;
- operatorName – полное наименование оператора связи;
- inn – ИНН оператора связи (10 цифр для юридических лиц, 12 цифр для ИП);
- ogrn – ОГРН оператора связи (13 цифр для юридических лиц, 15 цифр для ИП);
- email – электронный адрес технического специалиста, ответственного за использование механизма получения выгрузки; может
использоваться для оперативной обратной связи в случае возникновения технических вопросов или проблем. Необязательный параметр.
Главное получить время в формате 2012-01-01T01:01:01.000+04:00 и сохранить файл с кодировкой windows-1251.
Код функции генерации запроса на C#:
public static String GeneratingRequest(String operatorName, String inn, String ogrn, String email) { String result = "<?xml version=\"1.0\" encoding=\"windows-1251\"?>"; result += "<request><requestTime>"; result += DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffzzz"); result += "</requestTime><operatorName>"; result += "<![CDATA[" + operatorName + "]]>"; result += "</operatorName><inn>"; result += inn; result += "</inn><ogrn>"; result += ogrn; result += "</ogrn><email>"; result += email; result += "</email></request>"; return result; }
Теперь сохраняем файл в кодировке windows-1251:
String Request = GeneratingRequest("Наименование оператора", "1234567890", "1234567890123", "email@email.ru") StreamWriter swRequest = new StreamWriter(@"C:\request.xml", false, Encoding.GetEncoding("Windows-1251")); swRequest.Write(Request); swRequest.Close();
После создания запроса нам необходимо его подписать. Для этого воспользуемся криптографическим пакетом с открытым исходным кодом для работы с SSL/TLS — OpenSSL. Установим и настроем.
OpenSSL
Качаем пакет с этого сайта — slproweb.com. В моем случае это был Win64 OpenSSL v1.0.1g.
Да эта сборка требует для работы установленного Visual C++ 2008 Redistributables, который можно скачать там же.
Устанавливаем. При установке в диалоге «Select Additional Tasks» следует выбрать «The OpenSSL” binaries (/bin) directory» более хитростей нет.
Далее переходим в папку куда установили: C:/OpenSSL/bin и редактируем файл openssl.cfg. В начало файла добавим:
openssl_conf = openssl_defВ конце:
[openssl_def]engines=engine_section[engine_section]gost=gost_section[gost_section]engine_id=gostdynamic_path = C:/OpenSSL/bin/gost.dlldefault_algorithms=ALLВсе практически уже работает осталось настроить переменные среды:
OPENSSL_CONF = C:/OpenSSL/bin/openssl.cfg — полный путь к openssl.cfg
ну и в PATH += C:/OpenSSL/bin;
Теперь нам нужна ЭЦП. Приобрести ее можно в доверенном удостоверяющем центре. Ключ необходимо экспортировать в формате PKCS#12 из криптоконтейнера в Windows с помощью утилиты P12FromGostCSP
Почему P12FromGostCSP
Цитирую комментарий xtron из статьи Взаимодействие php-soap на linux с авторизацией по сертификатам с использованием алгоритмов ГОСТ
Не все так просто как кажется на первый взгляд. На самом деле сертификат, экспортируемый через стандартный диалог просмотра сертификата не распознается openssl. Получается такая ошибка:
MAC verified OK
Bag Attributes
localKeyID: 01 00 00 00
friendlyName: REGISTRY\\mstaff
Microsoft CSP Name: Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider
Error outputting keys and certificates
140017040754368:error:06074079:digital envelope routines:EVP_PBE_CipherInit:unknown pbe algorithm:evp_pbe.c:167:TYPE=1.2.840.113549.1.12.1.80
140017040754368:error:23077073:PKCS12 routines:PKCS12_pbe_crypt:pkcs12 algor cipherinit error:p12_decr.c:83:
140017040754368:error:2306A075:PKCS12 routines:PKCS12_item_decrypt_d2i:pkcs12 pbe crypt error:p12_decr.c:130:
Вот как раз эта утилита и позволяет избежать такой ошибки. Цитирую разработчиков данной утилиты:
Контейнер PKCS#12, создаваемый утилитой P12FromGostCSP полностью совместим с аналогичными контейнерами, создаваемыми ООО «КриптоКом» (в рамках проекта openssl) и ООО «Топ Кросс», чего, к сожалению, не скажешь о контейнере, создаваемом программными средствами, входящими в состав КриптоПро CSP (начиная с версии R3).
Просмотреть ASN1-структуры контейнера PKCS#12, созданного средствами КриптоПро CSP R3, и контейнеров, созданных другими средствами, удобно утилитами openssl или lirssl следующего вида:
#openssl asn1parse –inform DER –in <контейнер PKCS#12>
Если вы сравните эти структуры, то вам сразу бросится в глаза, что, например, вместо алгоритма хэширования ГОСТ Р 34.11-94 в контейнере от КриптоПро используется SHA1. Еще более интересный результат вы получите, если попробуете посмотреть содержимое контейнера, выполнив следующую команду:
#openssl pkcs12 –in <контейнер PKCS#12>
Файл PKCS#12 должен содержать сертификат и закрытый ключ! Проверить это можно командой:
openssl.exe pkcs12 -in C:/key.pfx -nodes

Далее преобразовать его в PEM. В OpenSSL это делается так (через командную строку — может запросить пароль которым защищен Ключ PKCS#12):
openssl.exe pkcs12 -in C:/key.pfx -out C:/key.pem -nodes -clcerts

Все теперь мы в состоянии подписать наш запрос.
Подпись запроса
Подписать запрос можно через OpenSSL следующей командой:
openssl.exe smime -sign -in C:/request.xml -out C:/request.xml.sign -signer C:/key.pem -outform DER

Реализация функции подписи файла на C#:
public static Boolean SignRequest() { Boolean ret = true; String OpenSSLPath = @"C:\OpenSSL\bin"; String RequestPath = @"C:\request.xml"; String SignRequestPath = @"C:\request.xml.sign"; String KeyPEMPath = @"C:\key.pem"; try { Process cmdProcess = new Process(); /* * Строку ниже можно убрать * если переменная среды PATH * имеет путь до OpenSSL */ cmdProcess.StartInfo.WorkingDirectory = OpenSSLPath; cmdProcess.StartInfo.FileName = "openssl.exe"; cmdProcess.StartInfo.Arguments = String.Format("smime -sign -in {0} -out {1} -signer {2} -outform DER", RequestPath, SignRequestPath, KeyPEMPath); cmdProcess.StartInfo.CreateNoWindow = true; cmdProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; cmdProcess.Start(); //Спасибо dimzon541 if (!cmdProcess.WaitForExit(5000)) { cmdProcess.Kill(); ret = false; } } catch (Exception) { ret = false; } return ret; }
Теперь проверим нашу работу. Подтверждение подлинности ЭП: gosuslugi.ru, выбираем — Подтверждение подлинности электронного документа. ЭП — отсоединенная, в формате PKCS#7
Можно проверить и средствами OpenSSL, но для этого нужно извлечь сертификат издателя, которая предоставила ЭЦП и конвертировать ее в PEM формат (сертификат можно еще взять тут):
openssl x509 -inform der -in C:/most.cer -out C:/most.pem
И проверяем нашу подпись:
openssl.exe smime -verify -in C:/request.xml.sign -content C:/request.xml -CAfile C:/most.pem -inform DER -out C:/validrequest.xml
В результате мы увидим строчку:
Verification successful
а в файле validrequest.xml будет лежать содержимое request.xml.
Переходим непосредственно к запросу дампа реестра запрещенных ресурсов.
Подача запроса и выгрузка реестра
В ручную все просто: Форма подачи запроса, отправляем файл запроса и его подпись (C:/request.xml и C:/request.xml.sign)

Если все нормально то результатом будет — Идентификатор запроса, с помощью его можно проверять результат обработки запроса. Как правило если все нормально то реестр будет выгружен минут через 5 в ZIP формате в архиве будет два файла dump.xml — дамп реестра и его цифровая подпись.
Проверить файл можно OpenSSL или gosuslugi.ru:
openssl.exe smime -verify -in C:/dump.xml.sig -content C:/dump.xml -CAfile C:/cartk.pem -inform DER -out C:/validdump.xml
В результате мы увидим строчку:
Verification successful
а в файле validdump.xml будет лежать содержимое dump.xml.
Теперь автоматизируем данный процесс. Для выгрузки есть сервис работающий по протоколу доступа SOAP, адрес: сервиса. WSDL схема доступна по адресу: WSDL схема.
Сервис состоит из 4-х методов:
- getLastDumpDateEx
Метод предназначен для получения временной метки последнего обновления выгрузки из реестра. В ответ присылает lastDumpDate — long дата приходит в формате UNIX Timestamp, но не в секундах, а в миллисекундах, а так же lastDumpDateUrgently — Момент времени, когда в выгрузку последний раз были внесены изменения, требующие незамедлительного реагирования (так же приходит в формате UNIX Timestamp, но не в секундах, а в миллисекундах). - getLastDumpDate
Оставлен для совместимости. Аналогичен getLastDumpDateEx, но возвращает только один параметр lastDumpDate., long дата приходит в формате UNIX Timestamp, но не в секундах, а в миллисекундах. - sendRequest
Метод предназначен для направления запроса на получение выгрузки из реестра, Принимает requestFile и signatureFile в base64Binary формате — файл запроса и его подписи (C:/request.xml и C:/request.xml.sign) В ответ присылает result — Результат обработки запроса в boolean формате и если все удачно, то code — Идентификатор запроса, строка по которой необходимо получить выгрузку из реестра в формате string. Так же resultComment — Комментарий к результату обработки запроса в формате string. - getResult
Метод предназначен для получения результата обработки запроса — выгрузки из реестра, принимает code — Идентификатор запроса, строка по которой необходимо получить выгрузку из реестра в формате string. В ответ присылает result — Результат обработки запроса в boolean формате и если все удачно, то registerZipArchive — Файл zip-архив с выгрузкой из реестра в формате base64Binary. Так же resultComment — Комментарий к результату обработки запроса в формате string.
Возможные значения тега resultComment:
- запрос обрабатывается
- неверный алгоритм ЭП
- неверный формат ЭП
- недействительный сертификат ЭП
- некорректное значение ЭП
- ошибка проверки сертификата ЭП
- у заявителя отсутствует лицензия, дающая право оказывать услуги по предоставлению доступа к информационно-телекоммуникационной сети Интернет
Информация по обратной связи для разрешения проблем приведена в Памятке оператору связи в этом разделе.
Логика работы с сервисом:
- Проверить, обновилась ли выгрузка из реестра. Для этого вызвать метод getLastDumpDateEx и сравнить полученное значение со значением, полученным на предыдущей итерации. В случае если значение lastDumpDateUrgently изменилось, то незамедлительно запросить обновленную выгрузку. В остальных случаях обновлять выгрузку на усмотрение, но не реже одного раза в сутки.
- В случае, если выгрузка обновилась, направить запрос на получение выгрузки с использованием метода sendRequest.
- Через несколько минут вызвать метод getResult для получения результата обработки запроса. В случае если запрос не обработан еще (см. содержимое поля resultComment) повторить шаг 3 через несколько минут.
Добавляем в ресурсы сервис, передавая адрес WSDL схемы.
Код функций getLastDumpDate, sendRequest, getResult
MaxReceivedMessageSize
Недавно стали появляться ошибки вида: Превышена квота максимального размера сообщения для входящих сообщений (65536). Для увеличения квоты используйте свойство MaxReceivedMessageSize соответствующего элемента привязки.
Ввиду чего пришлось увеличить свойство MaxReceivedMessageSize.
Комментарий службы поддержки:
Ввиду чего пришлось увеличить свойство MaxReceivedMessageSize.
Комментарий службы поддержки:
На данный момент ответ метода getResult содержит xml-блок данных, размер которого чуть больше 65536 байт.
public static Int64 LastDumpDate() { Int64 lastDumpDate = 0; BasicHttpBinding HttpBinding = new BasicHttpBinding(); HttpBinding.MaxReceivedMessageSize = 1*1024*1024*1024; //1Gb using (ChannelFactory<ServiceReference.OperatorRequestPortType> scf = new ChannelFactory<ServiceReference.OperatorRequestPortType>( HttpBinding, new EndpointAddress("http://vigruzki.rkn.gov.ru/services/OperatorRequest/"))) { ServiceReference.OperatorRequestPortType channel = scf.CreateChannel(); ServiceReference.getLastDumpDateResponse glddr = channel.getLastDumpDate(new ServiceReference.getLastDumpDateRequest()); lastDumpDate = glddr.lastDumpDate; } return lastDumpDate; } public static Boolean SendRequest(out String resultComment, out String code, Byte[] requestFile, Byte[] signatureFile) { Boolean result = false; code = null; BasicHttpBinding HttpBinding = new BasicHttpBinding(); HttpBinding.MaxReceivedMessageSize = 1*1024*1024*1024; //1Gb using (ChannelFactory<ServiceReference.OperatorRequestPortType> scf = new ChannelFactory<ServiceReference.OperatorRequestPortType>( HttpBinding, new EndpointAddress("http://vigruzki.rkn.gov.ru/services/OperatorRequest/"))) { ServiceReference.OperatorRequestPortType channel = scf.CreateChannel(); ServiceReference.sendRequestRequestBody srrb = new ServiceReference.sendRequestRequestBody(); srrb.requestFile = requestFile; srrb.signatureFile = signatureFile; ServiceReference.sendRequestResponse srr = channel.sendRequest(new ServiceReference.sendRequestRequest(srrb)); resultComment = srr.Body.resultComment; if (result = srr.Body.result) { code = srr.Body.code; } } return result; } public static Boolean GetResult(out String resultComment, out Byte[] registerZipArchive, String code) { Boolean result = false; registerZipArchive = null; BasicHttpBinding HttpBinding = new BasicHttpBinding(); HttpBinding.MaxReceivedMessageSize = 1*1024*1024*1024; //1Gb using (ChannelFactory<ServiceReference.OperatorRequestPortType> scf = new ChannelFactory<ServiceReference.OperatorRequestPortType>( HttpBinding, new EndpointAddress("http://vigruzki.rkn.gov.ru/services/OperatorRequest/"))) { ServiceReference.OperatorRequestPortType channel = scf.CreateChannel(); ServiceReference.getResultRequestBody grrb = new ServiceReference.getResultRequestBody(); grrb.code = code; ServiceReference.getResultResponse grr = channel.getResult(new ServiceReference.getResultRequest(grrb)); resultComment = grr.Body.resultComment; if (result = grr.Body.result) { registerZipArchive = grr.Body.registerZipArchive; } } return result; }
Например отправим запрос:
String resultComment, code; if(SendRequest(out resultComment, out code, File.ReadAllBytes(@"C:/request.xml"), File.ReadAllBytes(@"C:/request.xml.sign"))) { //... Все удачно }
Временную метку последнего обновления выгрузки из реестра можно конвертировать из UNIX Timestamp в DateTime вот так:
DateTime LastDumpDate = (new DateTime(1970, 1, 1, 0, 0, 0, 0)).AddSeconds(LastDumpDate()/1000);
Разбираем дамп реестра
Надо распаковать архив:
// Byte[] registerZipArchive - Получен при удачной выгрузке GetResult(); File.WriteAllBytes(@"C:/register.zip", registerZipArchive); ZipFile.ExtractToDirectory(@"C:/register.zip", @"C:/register");
В результате получаем дамп реестра C:/register/dump.xml
Пример содержимого дампа:
<?xml version="1.0" encoding="windows-1251"?> <reg:register updateTime="2014-02-02T12:00:00+04:00" xmlns:reg="http://rsoc.ru" xmlns:tns="http://rsoc.ru" updateTimeUrgently="2014-02-01T11:00:00"> <content id="68" includeTime="2013-12-01T10:00:05"> <decision date="2013-12-01" number="9" org="Роспотребнадзор"/> <url><![CDATA[http://site1.com/index.php]]></url> <domain><![CDATA[site1.com]]></domain> <ip>1.1.1.1</ip> </content> <content id="68" includeTime="2013-12-01T10:00:05"> <decision date="2013-12-01" number="9" org="Мосгорсуд"/> <url><![CDATA[http://site2.com/page1.php]]></url> <url><![CDATA[http://site2.com/page2.php]]></url> <url><![CDATA[http://site2.com/page3.php]]></url> <domain><![CDATA[site2.com]]></domain> <ip>1.1.1.1</ip> <ip>1.1.1.2</ip> </content> <content id="9999" includeTime="2014-02-01T15:17:51" urgencyType="1"> <decision date="2014-02-01" number="номер документа" org="Генпрокуратура"/> <url><![CDATA[http://site3.com/page1.html]]></url> <domain><![CDATA[site3.com]]></domain> <ip>1.2.3.4</ip> </content> </reg:register>
Парсим XML
Для этого создадим два класса: первый RegisterDump будет содержать поле UpdateTime и список объектов content, Второй ItemRegisterDump представляет один объект content со всеми полями.
public class RegisterDump { /* * <reg:register updateTime="2013-07-15T10:05:00+04:00" xmlns:reg="http://rsoc.ru" xmlns:tns="http://rsoc.ru"> * <content></content> * <content></content> * ... * <content></content> * </reg:register> */ public List<ItemRegisterDump> Items { get; set; } public String UpdateTime { get; set; } public RegisterDump() { this.Items = new List<ItemRegisterDump>(); this.UpdateTime = String.Empty; } public RegisterDump(String UpdateTime, List<ItemRegisterDump> Items) { this.Items = Items; this.UpdateTime = UpdateTime; } } public class ItemRegisterDump { /* * <content id="60" includeTime="2013-01-12T16:33:38"> * <decision date="2013-11-03" number="МИ-6" org="РосКосМопсПопс"/> * <url><![CDATA[http://habrahabr.ru/post/187574/]]></url> * <ip>123.45.67.89</ip> * </content> * <content id="69" includeTime="2013-05-12T12:43:34"> * <decision date="2013-10-02" number="ФБИ" org="СФНК"/> * <domain><![CDATA[chelaxe.ru]]></domain> * <ip>123.45.67.89</ip> * <ip>87.65.43.210</ip> * </content> */ public String id { get; set; } public String includeTime { get; set; } public String date { get; set; } public String number { get; set; } public String org { get; set; } public List<String> url { get; set; } public List<String> domain { get; set; } public List<String> ip { get; set; } public ItemRegisterDump() { id = String.Empty; includeTime = String.Empty; date = String.Empty; number = String.Empty; org = String.Empty; url = new List<String>(); domain = new List<String>(); ip = new List<String>(); } }
Парсим:
RegisterDump Register = new RegisterDump(); String dumpfile = @"C:/register/dump.xml"; XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(dumpfile); Register.UpdateTime = xmlDoc.GetElementsByTagName("reg:register")[0].Attributes.GetNamedItem("updateTime").InnerText; XmlNodeList content = xmlDoc.GetElementsByTagName("content"); for (int i = 0; i < content.Count; i++) { ItemRegisterDump item = new ItemRegisterDump(); item.id = content[i].Attributes.GetNamedItem("id").InnerText; item.includeTime = content[i].Attributes.GetNamedItem("includeTime").InnerText; foreach (XmlNode node in content[i].ChildNodes) { switch(node.Name) { case "decision": item.date = node.Attributes.GetNamedItem("date").InnerText; item.number = node.Attributes.GetNamedItem("number").InnerText; item.org = node.Attributes.GetNamedItem("org").InnerText; break; case "url": item.url.Add(node.InnerText); break; case "domain": item.domain.Add(node.InnerText); break; case "ip": item.ip.Add(node.InnerText); break; } } Register.Items.Add(item); }
Теперь блокируем все это добро на нашем MikroTik.
Блокируем средством MikroTik
Делаем мы это с помощью layer7-protocol и добавляем в фильтры.
Вот пример:
/ip firewall layer7-protocol add name=12 comment=register regexp=^.+(chelaxe.ru).*$/ip firewall filter add action=drop chain=forward disabled=no dst-port=80 layer7-protocol=12 protocol=tcp src-address=192.168.0.0/24 comment=register



Все теперь все пакеты для подсети 192.168.0.0/24 приходящие по TCP на 80 порт с содержанием подстроки chelaxe.ru отбрасываются.
В комментарии надпись register добавляется не с проста. Она нужна нам будет для удаления всех правил прежде чем добавить обновленные.
Вот скрипт который удалит все записи:
/ip firewall layer7-protocol remove [find comment=register]/ip firewall filter remove [find comment=register]
Общение с MikroTik роутером будет посредством API, необходимую библиотеку с примерами нам уже написали: wiki/API C#
Используя эту библиотеку (класс) и принцип блокирования контента разобранный выше реализуем все это на C#
public static Boolean AddFilterL7(String ip, String username, String password, RegisterDump dump, String SRCAddress) { Boolean ret = true; try { //Класс MK смотри здесь http://wiki.mikrotik.com/wiki/API_in_C_Sharp MK mikrotik = new MK(IPAddress.Parse(ip).ToString()); if (mikrotik.Login(username, password)) { mikrotik.Send("/system/script/add"); mikrotik.Send("=name=cleaner"); mikrotik.Send("=source=/ip firewall layer7-protocol remove [find comment=register]\n/ip firewall filter remove [find comment=register]", true); mikrotik.Read(); mikrotik.Send("/system/script/run"); mikrotik.Send("=number=cleaner", true); mikrotik.Read(); /* Cleaner * /ip firewall layer7-protocol remove [find comment=register] * /ip firewall filter remove [find comment=register] */ foreach (ItemRegisterDump item in dump.Items) { for (Int32 i = 0; i < item.domain.Count; i++ ) { mikrotik.Send("/ip/firewall/layer7-protocol/add"); mikrotik.Send("=name=" + item.id + "_" + i); mikrotik.Send("=comment=register"); mikrotik.Send("=regexp=^.+(" + item.domain[i] + ").*$", true); mikrotik.Read(); mikrotik.Send("/ip/firewall/filter/add"); mikrotik.Send("=action=drop"); mikrotik.Send("=chain=forward"); mikrotik.Send("=disabled=no"); mikrotik.Send("=dst-port=80"); mikrotik.Send("=layer7-protocol=" + item.id + "_" + i); mikrotik.Send("=protocol=tcp"); mikrotik.Send("=src-address=" + SRCAddress); mikrotik.Send("=comment=register", true); mikrotik.Read(); } } } } catch (Exception) { ret = false; } return ret; }
В примере я использовал блокировку по домену, но это не совсем верно. Для блокировки по URL можно использовать следующие регулярное выражение для layer7-protocol:
^.*(/summary).*(chelaxe.ru).*$Причем выражение должно быть в нижнем регистре при использовании версий RouterOS из 5 ветки, в следствии допущенной ошибки MikroTik.
Исходники доступны здесь.
При создании руководствовался:
Памятка оператора связи
OpenSSL 1.0.1с + libGOST.so + КриптоПРО + ключ из ruToken
Как мы получали доступ к базе реестра запрещенных ресурсов
Роскомнадзор объявил о введении в работу реестра сайтов с «пиратскими» фильмами
RegExr intuitive tool for learning, writing, and testing Regular Expressions
