Русский
Русский
English
Статистика
Реклама

Sms

Свой личный SMS-шлюз. Часть 1 цели, задачи, сборка и тестирование

29.04.2021 12:05:53 | Автор: admin


Представляю вам цикл статей по созданию собственного шлюза для отправки SMS-сообщений.
В первой части мы определим цели и некоторые аспекты использования своего шлюза, настроим программное обеспечение для отправки SMS с использованием USB-модемов, а также рассмотрим несколько интересных вариантов отправки

Начнем мы статью с вопроса для чего отправлять SMS, ведь на дворе 2021 год? Да, мы уже привыкли к различным мессенджерам, уведомлениям в чат-боты, но SMS до сих пор обладает наиболее гарантированным уровнем доставки. Нет зависимости от наличия интернета и сообщение поступит даже в сети 2G и при слабом сигнале и на устройство без доступа к интернет. Таким образом, если планируется отправлять сообщения, которые гарантированно должны быть доставлены и критичны к времени доставки, то SMS это возможно лучший выбор.

Под SMS-шлюзом мы понимаем программно-аппаратное устройство подключенное к сети сотового оператора и позволяющее производить автоматизированную отправку сообщений.

Где можно применять это решение?


Самое простое и очевидное применение отправка уведомлений. Мы отслеживаем доступность различных сервисов и хотим знать о любых сбоях. Применение в системе умного дома позволит нам быть в курсе всех произошедших событиях. Да, многие IoT устройства умного дома оборудованы разъемом под сим-карту и могут сами отправлять SMS, но в этом случае на каждое устройство нужна отдельная сим-карта.
Мы можем проводить двухэтапную аутентификацию пользователей или производить валидацию введенного номера телефона. Регистрировать пользователей по номеру телефона, без необходимости создания логина и пароля. Отправлять сообщения о формировании заказа, отправке, получении, изменении баланса.

Но сообщения можно не только отправлять, их также можно получать и обрабатывать, что еще сильнее расширяет варианты применения. Все зависит только от вашей фантазии.

Стоит обратить особое внимание так как мы рассматриваем в статье вариант создания шлюза, то это позволяет отправлять сообщения сразу со всех описанных сервисов используя всего одно устройство.

Плюсы и минусы собственного шлюза


Как и любое решение, собственный шлюз обладает рядом достоинств и недостатков.
Мы не будем рассматривать плюсы с точки зрения точного сравнения с платными сервисами ввиду разного уровня получаемой услуги. Я приведу наиболее явные и вы сможете решить на сколько каждый пункт критичен именно для вас.

Плюс

  • Очень низкая цена за отправленное сообщение в районе 5 копеек за сообщение (при отправке 1000), против 2.65 на коммерческих шлюзах

Минусы

  • Отсутствует возможность задать имя в качестве отправителя будет номер телефона, а не Baton.ru, например. Но есть способ как решить это красиво и мы рассмотрим это в статье.
  • Отправка сообщения не чаще чем 1 раз в 10 секунд. Решается увеличением количества модемов.
  • Нет защиты от сбоев как на аппаратном, так и программном уровне. Объективности ради стоит отметить, что платные шлюзы тоже примерно раз в неделю сообщают о проблемах с доставкой определенным операторам. Мой опыт использования такого шлюза показывает всего один сбой за более чем 12 месяцев.

Итак, мы определили, что нам ожидать от собственного шлюза и мы готовы начать.

Устанавливаем Gammu, подключаем модемы


В качестве аппаратного ядра системы я буду использовать Orange Pi PC с Armbian просто потому, что он у меня есть и ничем не занят. Свою версию вы можете сделать на основе RPi, компьютера/сервера на Linux и даже виртуальной машины с проброшенными внутрь USB-портами это не имеет особого значения, главное мы будем использовать Linux.

Для общения с модемами я буду использовать Gammu очень мощный продукт позволяет общаться не только с классическими модемами, но и использовать телефоны в качестве модемов. Также этот продукт избавит нас от необходимости самим формировать пакеты сообщений, считать их длины, составлять и отправлять АТ-команды и самое главное позволить отправлять сообщения в PDU-формате.

Итак, подключимся к серверу, обновим систему и установим gammu:

ssh root@<IP_вашего_сервера>apt updateapt upgrade -yapt install gammu -y

Теперь нужно подключить модемы к USB-портам. Я буду использовать два модема, чтобы нагляднее показать чем отличаются настройки. Подключаем и смотрим какие порты они заняли.



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



На скрине выше видно, что установленный модем usb 5-1, Alcatel, имеет пять каналов. Теперь нам нужно определить какие из них используются для связи. Сделать это не сложно. Пишем в терминале:

screen /dev/ttyUSB1

В открывшемся окне вбиваем АТ и если в ответ получили ОК, то запоминаем этот порт это то, что нужно. Выходим CTRL+A затем D
Обратите внимание, что найдя один порт, все равно нужно проверить оставшиеся ttyUSB2, ttyUSB3, и далее. Например, используемый мной Alcatel, имеет 2 независимых канала на ttyUSB4 и ttyUSB5, что позволяет одновременно отправлять два SMS-сообщения через один модем.

Настройка Gammu


Итак мы определились с портами и теперь нужно настроить Gammu. Есть утилита конфигурации gammu-config, но мы не будем ее использовать, а запишем сразу данные в конфигурационный файл.

nano ~/.gammurc

дописываем в нижней части файла данные конфигураций. Каждый блок [gammu] это канал модема. [gammu] это канал по умолчанию, [gammu1] первый канал и т.д. Далее мы еще коснемся этого, когда будем отправлять сообщение. Итого я добавляю 3 доступных канала от двух своих модемов:

[gammu]port = /dev/ttyUSB0model = atconnection = atsynchronizetime = yeslogfile =logformat = nothinguse_locking = yesgammuloc =[gammu1]port = /dev/ttyUSB4model = atconnection = atsynchronizetime = yeslogfile =logformat = nothinguse_locking = yesgammuloc =[gammu2]port = /dev/ttyUSB5model = atconnection = atsynchronizetime = yeslogfile =logformat = nothinguse_locking = yesgammuloc =

Краткое описание настроек:

  • model тип модема и как gammu следует общаться с модемом, at с помощью AT-команд
  • connection = at тип соединения. Подключаемся на скорости 9600
  • use_locking говорит gammu, что нужно блокировать доступ к модему на время работы с ним. Иначе возможны различные ошибки и сбои


Вид настроек в терминале

Проверка работы


Gammu настроена и теперь мы можем протестировать отправку сообщений.

Базовая команда для отправки сообщения на английском и всего с одного модема выглядит следующим образом:

gammu sendsms TEXT +70001234567 -text "Test message"



Теперь рассмотрим варианты поинтереснее с использованием дополнительных аргументов

Если в системе несколько модемов, то добавляем нужный порт аргументом "-s <номер_порта>". Мы затрагивали этот момент, когда заполняли настройки. Нумерация начинается с 0 и в нашем случае это промежуток 0-2.

gammu -s 1 sendsms TEXT +70001234567 -text "Test message"

Отправка сообщений в PDU-формате (кириллица, прочие языки и спецсимволы) "-unicode"

gammu -s 1 sendsms TEXT +70001234567 -unicode -text "Тестовое сообщение"

Отправка сообщений в PDU-формате с автоматической разбивкой на несколько сообщений и последующей склейкой на телефоне "-autolen 5". Данный аргумент подразумевает использование числа, хотя объективно оно ни на что не влияет, по крайней мере у меня работает одинаково при любом числе, поэтому я решил, что сообщения длиной больше 5 у меня не будет и использую это число

gammu -s 1 sendsms TEXT +70001234567 -unicode -autolen 5 -text "Тестовое сообщение"

Есть очень интересный аргумент, который полезен, если вы отправляете одноразовые коды "-replacemessages 1". Суть его работы заключается в следующем мы указываем телефону, что при получении сообщения он должен заменить сообщений с ID 1, если оно имеется в полученных на то, что поступило сейчас. Таким образом телефон автоматически затирает все ранее полученные сообщения и в переписке всегда отображается только последнее полученное. Единственное, решить стоит его использовать или нет, желательно в самом начале, чтобы всегда заменять ID 1

gammu -s 1 sendsms TEXT +70001234567 -unicode -autolen 5 -flash -replacemessages 1 -text "Тестовое сообщение"



А теперь, самый интересный, на мой взгляд аргумент "-flash".

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

gammu -s 1 sendsms TEXT +70001234567 -unicode -autolen 5 -flash -replacemessages 1 -text "Тестовое сообщение"


Всё!

В следующей статье


В этой части статьи мы настроили отправку сообщений, но оно происходит вручную. Во второй части статьи мы напишем небольшое API (на PHP) для получения запросов по http, сохранении их в базу данных и автоматической отправке через активный шлюз и не только

Подробнее..

Свой личный SMS-шлюз. Часть 2 создаём API и форму отправки

03.05.2021 12:05:25 | Автор: admin

Представляю вам вторую часть из серии статей по созданию своего шлюза.
В первой части мы настроили Gammu, рассмотрели особо интересные параметры и успешно произвели отправку SMS сообщения. Сейчас нам предстоит задача посложнее создать некую программную прослойку (API), для того, чтобы можно было работать со шлюзом путем отправки запросов на этот API. В первую очередь это комфорт, во вторую большое количество дополнительных возможностей.

Если вы не знакомы с первой частью, советую сначала ознакомиться с ней:
Свой личный SMS-шлюз. Часть 1 цели, задачи, сборка и тестирование

Постановка задачи


Первое, что нужно для себя понять, какими свойствами и возможностями должен обладать наш код, что мы хотим от него получить и как с ним взаимодействовать. Для этого я поставил для себя пару вопросов и постарался максимально на них ответить.

На чем будем писать backend
Тут все просто, что умеем, на том и пишем, поэтому в моем случае PHP

Авторизация
Конечно. Сервис будет смотреть в интернет, поэтому авторизация обязательна.

Один пользователь одна sim-карта?
Конечно нет. У нас сервис для личного пользования и мы хотим иметь один логин, но при этом отправлять с нескольких номеров. Но если появится необходимость выделить один шлюз под конкретный сервис, мы должны иметь возможность добавления пользователей.

Как мы хотим общаться с этим API, откуда будут попадать запросы
Общение будет через POST/GET. Запросы могут отправляться различными устройствами, в том числе и теми, которые не умеют POST или заморочно реализовать, поэтому принимать и обрабатывать будем $_REQUEST. Также мы хотим иметь возможность отправки сообщений через простую форму на сайте.

Один запрос один адресат?
Нет. В одном запросе с одним текстовым сообщением должна быть возможность указать несколько адресатов. Суть этого понятна. Например я отслеживаю наличие ЭЭ на даче и в случае отключения хочу получить уведомление на все свои телефоны, а может даже телефон супруги почему бы и нет, ведь уведомление важное.

История отправленных сообщений
Конечно, история это наша важная составляющая жизни, поэтому ее мы всегда храним

Балансировка нагрузки на карты
Да. Мы обладаем чувством меры и будем отправлять с одной карты не более какого-то числа сообщений, а значит их нужно считать и перед выбором шлюза проверять на исчерпание лимита.

Задача на разработку поставлена, цель ясна, итак приступим


Первое что мы сделаем, определим структуру база данных. Без нее, при наших потребностях никак. Использовать будем MySQL.
Дальше нужно будет написать пару классов, к которым мы были обращаться.

Приступим к созданию БД и создание таблиц
Я буду использовать несколько таблиц для:

  • users данные пользователей
  • smsc_gateway данных шлюзов
  • gateway_smscount счётчик отправленных сообщений по каждому шлюза в конкретный месяц
  • sms_queue очередь отправки сообщений
  • sms_archive история сообщений

Структура БД дамп таблиц
# Дамп таблицы users# ------------------------------------------------------------CREATE TABLE `users` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `uuid` varchar(10) NOT NULL,  `login` varchar(50) NOT NULL,  `password` varchar(32) NOT NULL,  `comment` varchar(200) NOT NULL,  `status` varchar(11) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# Дамп таблицы smsc_gateway# ------------------------------------------------------------CREATE TABLE `smsc_gateway` (  `id` int(20) NOT NULL AUTO_INCREMENT,  `gw_phone` varchar(11) NOT NULL DEFAULT '',  `uuid` varchar(10) NOT NULL,  `host` varchar(15) NOT NULL DEFAULT '',  `port` varchar(5) NOT NULL,  `password` varchar(12) DEFAULT '',  `maxcount` varchar(6) NOT NULL,  `status` int(1) NOT NULL,  `gateway_id` int(2) DEFAULT NULL,  `state` varchar(11) DEFAULT NULL,  `comment` varchar(100) NOT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# Дамп таблицы gateway_smscount# ------------------------------------------------------------CREATE TABLE `gateway_smscount` (  `id` int(20) NOT NULL AUTO_INCREMENT,  `gw_phone` varchar(11) NOT NULL DEFAULT '',  `date` varchar(10) NOT NULL,  `count` int(6) NOT NULL DEFAULT '0',  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# Дамп таблицы sms_queue# ------------------------------------------------------------CREATE TABLE `sms_queue` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `uuid` varchar(11) NOT NULL DEFAULT '',  `dateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,  `status` varchar(10) DEFAULT NULL,  `data` varchar(500) NOT NULL DEFAULT '',  `phone` varchar(11) NOT NULL DEFAULT '',  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# Дамп таблицы sms_archive# ------------------------------------------------------------CREATE TABLE `sms_archive` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `gateway_id` varchar(11) DEFAULT NULL,  `dateTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,  `data` varchar(500) DEFAULT NULL,  `status` varchar(11) DEFAULT NULL,  `phone` varchar(11) DEFAULT NULL,  `uuid` varchar(11) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;



Стоит подробнее остановиться на таблице с данными шлюза и используемых полей smsc_gateway. Здесь мы используем:

  • uuid id пользователя, которому назначен данный шлюз
  • host ip-адрес компьютера с модемами для подключения по ssh
  • port порт мы тоже укажем, так как желательно не использовать стандартный 22-й, а также это позволит при необходимости использовать port forwarding
  • password пароль ssh
  • gw_phone фактической номер телефона
  • maxcount ограничение на количество отправляемых сообщений
  • status статус. 1 активен, 0 заблокирован.
  • gateway_id канал этого модема в Gammu (помните, у нас может быть несколько модемов)
  • state статус шлюза. lock заблокирован.
  • comment свободное поле с комментарием, чтобы просто делать заметки, если нужно

Классов будет всего 5:

  1. Авторизация пользователя Users_Auth.class.php
  2. Работа с PDO MYSQL_PDO.class.php
  3. Обработчик по работе с входящими данными SMS SMS_data_handle.class.php
  4. Работа с Gammu Gammu_SMS.class.php
  5. Возврат http ответов в json http_response.class.php

Дальше я буду объяснять как поток данных будет ходить по API опираясь на базу данных. Мне кажется так нагляднее и понятнее. Также я приведу куски этого кода под спойлерами.

В итоге мы получаем такую последовательность действий.

Пользователь отправляет запрос с параметрами:

.../smsc.php?login={user_name}&pass={user_password}&tel={phone_number}&msg={message}&flash=1&replacemessages_id=1

Значения flash и replacemessages мы рассматривали в прошлой статье. В {phone_number} можно указать несколько номеров телефонов через ",". + в номере телефона указывать не нужно, но обязательно указывать номер в международном формате (для России это 7). Так же в стоку можно добавить еще один параметр &attempts={число} количество попыток внутри одной отправки, то есть, если можем при отправке вернул ошибку, пытаться ли отправить тут же еще раз?

Вот, что происходит под капотом smsc.php

<?phprequire_once __DIR__.'/functions/config.php';XSS_secure();if (!Users_Auth::do($PDO)) http_response::return(401, ["description" => "User not found or login / password is incorrect"]);$sms_handle = new SMS_data_handle($PDO);$sms_handle->save();function XSS_secure() {     function replace($arr) {        $filter = array("<", ">");        $filter_replace = array("<", ">");         for ($i=0; $i < count($filter) ; $i++) {            $str = str_replace($filter[$i], $filter_replace[$i], $arr);        }    return $str;    }     if ($_GET) $_GET = replace($_GET);    if ($_POST) $_POST = replace($_POST); }?>

Первым делом мы подключаем файл с настройками config.php:

require_once __DIR__.'/functions/config.php';

Содержание файла:

<?php // ini_set('error_reporting', E_ALL);// ini_set('display_errors', 1);// ini_set('display_startup_errors', 1); spl_autoload_register(function ($class_name) {    require_once "classes/{$class_name}.class.php";}); $PDO_param = ['db_host' => '__', // database hostname or ip'db_name' => '__', // database name'db_user' => '__', // database username'db_pass' => '__', // databse user password'db_charset' => 'utf8','pdo_opt' => [        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_LAZY,        PDO::ATTR_EMULATE_PREPARES => false,        ]]; $PDO = new MYSQL_PDO($PDO_param); ?>

делаем небольшую проверку на XSS, а далее проверяем авторизацию, вызывая метод класса Users_Auth::do($PDO):

class Users_Auth{    static function do($PDO)    {      if (!@$_REQUEST['login'] || !@$_REQUEST['pass']) http_response::return(403, ['description' => 'Check your login and password']);     // Проверяем авторизацию      $user = $PDO->query("SELECT id, status FROM users WHERE login= ? AND password= ?", [$_REQUEST['login'], md5(trim($_REQUEST['pass']))]);      if ($user->rowCount() == 0)          return 0;          // http_response::return(401, ["description" => "User not found. Login and password is incorrect"]);      $row = $user->fetch();      if ($row->status != 'active')          return 0;          // http_response::return(401, ["description" => "User status: {$row->status}"]);      return 1;    }}

Если получили false авторизация не удалась, возвращаем код и описание ошибки в json, если необходимо.

Если авторизация успешная вызываем $sms_handle->save(), проверяем переданы ли обязательные параметры телефон и текст сообщения, проверяем в БД статус пользователя, разбираем строку запроса и приводим в нужный нам вид, удаляем пробелы и "+" из номера телефона, а также разделяем их по запятой. Таким образом получаем массив номеров телефонов и текста сообщения, которое нужно на них отправить. Делаем из этого json и сохраняем в таблицу очереди на отправку. Проверка на наличие телефона обязательна. Если попытаться отправлять сообщения без указания номера телефона, возникнет ошибка в Gammu, и шлюз будет занят на несколько секунд. Когда шлюз освободится возникнет аналогичная повторная ситуация, что в свою очередь создаст так называемую пробку и последующие сообщения из очереди просто не смогут уйти.

public function save() {if (empty($_REQUEST['tel'])) http_response::return(400, ["success" => false, "description" => "Phone number is empty"]);if (empty($_REQUEST['msg'])) http_response::return(400, ["success" => false, "description" => "Message is empty"]);$msg = $_REQUEST['msg'];$search = array(' ', '+');$replace = array('', '');$phone_array = explode(",", str_replace($search, $replace, $_REQUEST['tel']));$query = $this->PDO->query("SELECT uuid FROM users WHERE login = ?", [$_REQUEST['login']]);$user_uuid = $query->fetch();$data = [    'message' => $msg,    'flash' => @$_REQUEST['flash'],    'replacemessages_id' => @$_REQUEST['replacemessages_id'],    'attempts' => @$_REQUEST['attempts']];$data['attempts'] = (@$_REQUEST['attempts']) ?: 1;foreach ($phone_array as $phone) {    $data['phone'] = $phone;    $this->PDO->query("INSERT INTO sms_queue SET uuid= ?, data= ?, phone= ?", [$user_uuid->uuid, json_encode($data, JSON_UNESCAPED_UNICODE), $phone]);}http_response::return(200, ["success" => true, "description" => "Saved to queue"]);  }

Дальше мы используем простой скрипт, который поставим в cron и будем вызывать раз в 5-10 секунд (по вкусу) send_queue.php

<?php require_once __DIR__.'/config.php'; $sms_handle = new SMS_data_handle($PDO);$sms_handle->send(); /*add follow lines to cron  crontab -edon't forget to replace <full_path_to> to your really path* * * * * ( php /<full_path_to>/send_queue.php )* * * * * ( sleep 10 ; php /<full_path_to>/send_queue.php )* * * * * ( sleep 20 ; php /<full_path_to>/send_queue.php )* * * * * ( sleep 30 ; php /<full_path_to>/send_queue.php )* * * * * ( sleep 40 ; php /<full_path_to>/send_queue.php )* * * * * ( sleep 50 ; php /<full_path_to>/send_queue.php )*/?>

Он будет обращаться к методу класса обработчика сообщений SMS_data_handle->send(). Здесь уже начинается самое интересное.

Мы получаем сообщение за последние 10 минут без тегов статуса. Если нашли такое, ставим на него тег process и берём в работу.

Извлекаем из тела json uuid пользователя, обращаемся к таблице и получаем список активных шлюзов. Идем в таблицу со счётчиком и проверяем, не превышен ли лимит на отправку. Если мы получили активный шлюз и счётчик не превышен, ставим на него тег lock, чтобы никакой другой процесс уже не смог параллельно к нему обратиться. Все вызовы происходит внутри метода send(), но логика раскидана по другим методам класса. По указанному выше описанию работы метода эти обращения легко видны.

Далее мы создаем объект класса $send_proc = new Gammu_SMS($param) с параметрами и обращаемся к методу $send_proc->send($attr) с атрибутами

Весь код метода send():

public function send($с = 1) {     $sended_sms = 0;    for ($i = 0; $i < $с; $i++) {       $sms_queue = $this->get_sms_queue(1);      if (!$sms_queue->rowCount()) http_response::return(200, ["description" => "Nothing to do. Sent. count: {$sended_sms}"]);       $sms_count = $sms_queue->rowCount();      $msg_row = $sms_queue->fetch();       $this->PDO->query("UPDATE sms_queue SET status = ? WHERE id = ?", ["process", $msg_row->id]);       $user_gateway = ($this->get_gateway($msg_row->uuid));       if (!$user_gateway) {      $this->PDO->query("UPDATE sms_queue SET status = NULL WHERE id= ?", [$msg_row->id]);      http_response::return(403, ["description" => "Not active gateways or get limit of message count"]);      }       $this->gateway_lock($user_gateway->id);       $param = [      'host' => $user_gateway->host,      'port' => $user_gateway->port,      'login' => 'root',      'password' => $user_gateway->password,      ];       $sms_data = json_decode($msg_row->data);      $sms_data->message = $sms_data->message;       $attr = [      'phone' => $sms_data->phone,      'message' => $sms_data->message,      'attempts' => $sms_data->attempts,      'flash' => $sms_data->flash,      'replacemessages_id' => $sms_data->replacemessages_id,      'gateway' => $user_gateway->id,      ];      // sleep(5);      $send_proc = new Gammu_SMS($param);      if ($send_proc->send($attr)) {      $this->sms_2archive($msg_row->id, $user_gateway->id);      $this->update_gwcount($user_gateway->gw_phone);      $sended_sms++;       } else {      $this->PDO->query("UPDATE sms_queue SET status = NULL WHERE id= ?", [$msg_row->id]);      }     $this->gateway_release($user_gateway->id);     }     http_response::return(200, ["success" => true, "description" => "Message sent. Count: {$sended_sms}"]);   }

Если объект вернул true, то переносим сообщение в архив и увеличиваем счётчик отправленных сообщений. Иначе снимаем тег proccess и через некоторое время будет повторная попытка отправки по cron.

Особо внимательные заметили, что мы вызываем метод с дефолтным параметром равным одному send($с = 1). Параметр $c заложен на перспективу и позволяет нам, в случае необходимости получать пачку сообщений из базы данных и обрабатывать их отправку в цикле. Для этого в файле, вызываемом в cron нужно в вызове метода указать число сообщений для выборки их БД $sms_handle->send({число});

Обратим внимание еще на один момент. В файле smsc.php есть возможность отправлять сообщения сразу после того, как оно было добавлено в БД. Для этого нужно раскомментировать следующую строку:

// $sms_handle->send();

Это позволит нам отказаться от cron, но есть один нюанс желательно использовать этот метод, если вы отправляете сообщения только на один номер и запросы к шлюзу не могут быть чаще чем раз в 30 секунд. Иначе возможны ошибки связанные с наложением запросов и если шлюз занят, то сообщение не отправится.

Теперь наш шлюз работает через API и умеет отправлять сообщения.

Ну и бонусом мы сделаем простую форму для отправки сообщений с сайта. Ее код не нуждается в пояснении, она просто принимает от вас тест и отправляет POST-запрос на указанный нами скрипт. Единственное в блоке отправки ajax нужно заменить url: "/<*.php>" на адрес вашего скрипта smsc.php

Итак подведем итоги проделанной работы. Мы создали аппаратную платформу, научились отправлять сообщения через терминал и расширили возможности системы собственным API для легкого доступа к шлюзу устройств способных отправлять GET/POST-запросы. Хранить историю и балансировать нагрузку между картами и прочее. Все это сильно упрощает работы со шлюзом и позволяет хранить все в одном сервисе.
Внимание, я не претендую на великолепную красоту кода и буду рад любой объективной критике для понимания своих ошибок (в случае наличия) и совершенствования навыков.
Репозиторий данного проекта на Github

Подробнее..

Как мы боролись с фейковыми аккаунтами на сайте знакомств

18.02.2021 22:12:39 | Автор: admin

С ростом популярности сервиса мы столкнулись с нереально большим количеством мошенников, которые регистрировали фейковые аккаунты и спамили других пользователей (порно, наркотики и вся остальная запрещёнка). Дошло до того, что таких регистраций стало в несколько раз больше, чем настоящих пользователей. Жалобы и отток нормальных посетителей положили начало нашей борьбе.

Изначально подтверждение регистрации было через email (сервис существует с 2007 года), но с ростом посещаемости мы заметили аномально резкий всплеск подозрительных регистраций. Решили внедрить подтверждение ключевых действий с аккаунтом, привязав их к мобильному телефону.

Отправка SMS на короткий номер

Первый вариант верификации был через отправку пользователем исходящего sms на короткий номер. Люди отправляли сообщение на самый дешевый короткий номер, тем самым подтверждали свой. В 2010 году это казалось решением проблемы, не требующим существенных затрат. Мы были одни из первых, кто это внедрил, а тогда больше половины пользователей принципиально не хотели светить свой номер мобильного телефона. Мало того, отправка на номер для пользователя была платной, хоть и минимальной стоимости около 2 (сумму забирал себе оператор), и требовала дополнительного действия. Вдобавок: поддерживались не все операторы, а сами короткие номера тогда часто ассоциировались с мошенниками. Хлынули жалобы, конверсия регистраций упала. Стало понятно, что решение выбрано неудачно.

Подтверждение SMS с кодом

Решили внедрить отправку клиенту sms с кодом. Несмотря на существенные расходы в пределах 50000 (около 1,500$ по тому курсу) на подтверждения и восстановления паролей, команда вздохнула с облегчением и о проблеме надолго забыли. Время и трудозатраты на борьбу со спамерами изматывали сильнее, чем дополнительные расходы.

Всё изменилось ближе к 2015 году, когда сотовые операторы ввели интерконнект. До этого исходящие сообщения стоили очень дешево (от 10 копеек) из-за возможности отправлять смски через региональных операторов в обход прямых подключений, не теряя в качестве доставки. После интерконнекта операторы стали блокировать отправку sms из других сетей своим абонентам, если она происходила от незарегистрированного имени отправителя. У заказчиков и агрегаторов не было выхода, кроме как подключаться напрямую и отправлять через прямые официальные каналы, которые в целом были всегда, но стоили до 10 раз дороже. Для усиления эффекта основные операторы выкупили почти всех тех мелких, через которых можно было отправлять сообщения дешево. Когда рынок стал монополизирован, операторы еще сильнее подняли цены на смски и продолжают это делать каждый год.

После повышения цен затраты увеличились на порядок: до 300000-400000 в месяц. Рост количества пользователей и платные плюшки ещё позволяли нести такие издержки, но шутки про то, что затраты на sms превышают затраты на несколько десятков серверов уже перестали быть смешными. Но других вариантов не было: безопасность аккаунта и блокировка спамеров важнее, приходилось платить.

Тем временем география пользователей расширилась за пределы России, в первую очередь за счет СНГ. А это, в свою очередь, совпало с желанием заработать и у операторов соседних стран: Украина, Беларусь, Казахстан, Киргизия и других. Они решили, что 1-2 за sms это не предел, давайте повысим до 7-13! Так было введено разделение трафика на национальный и международный с разницей в несколько раз.

Примеры:

Страна\Стоимость SMS

Национальные

Международные

Казахстан

1,5

7

Беларусь

0,75

11

Украина

1

5

Под международный трафик автоматически попадают все известные международные бренды, все проекты, которые не имеют в данной стране юр.лица и национального домена. Некоторые страны (особенно Казахстан) еще изобретательнее подошли к вопросу: если у проекта есть сайт в другой доменной зоне (например, не только site.kz, но еще и .ru), проект не может быть согласован по национальному тарифу, несмотря на то, что sms отправляются от имени местного проекта и только по Казахстану местным жителям. Попробуешь обойти огромный штраф и блокировка имени.

Звонок с кодом голосом

Расходы на sms перевалили за психологический барьер 1 млн в месяц, что само по себе всерьез заставляло задуматься над альтернативами. Но вместе с ростом расходов вернулась и старая проблема в новом обличии. Теперь благодаря куче ?сервисов по приему sms в выдаче Яндекса на виртуальные номера стало легко зарегистрировать аккаунт для любой социальной сети, включая VK и Facebook.

Так мы начали работать над аутентификацией через звонки. Идея пришла к нам несколько лет назад и была внедрена как дополнительный резервный способ подтверждения номера для некоторых стран. Действие от клиента требовалось такое же, как с sms ввести код, который мы отправляем, только в случае со звонками код диктовался голосом при ответе на звонок.

Как это выглядело на сайтеКак это выглядело на сайте

Но и тут есть подводные камни: по России и некоторым странам звонки работают хорошо, но из-за международной тарификации стоимость минуты по некоторым направлениям превышает 20-30 (например: Беларусь, Украина и др). Вторая проблема качество доставки: операторы могут фильтровать трафик по длительности соединения. Звонки определяются операторами, как спам и фильтруются, а значит пользователь не получает свой код подтверждения.

Еще был Viber :)

И предупреждение с угрозой штрафа в 5000 евро за отправку запрещенного контента. Пока мы тестировали новый способ и часть пользователей получали приветственное сообщение при регистрации вида Здравствуйте, %username%!, пранкеры подсуетились и начали оформлять левые регистрации на чужие номера. Людям пришли сообщения вида Здравствуйте, Жопа:

Детализация из кабинета оператораДетализация из кабинета оператора

Заметили быстро и лавочку прикрыли, но собрали негатив. Сам же Viber показал низкое проникновение (порядка 15-25%) и экономическую нецелесообразность из-за минимального платежа в ~30000, который сгорает в конце месяца, если пакет услуг на эту сумму, при цене сервисного сообщения в ~50 копеек, не был использован.

Звонок с кодом в номере

В итоге пришли к подтверждению регистраций звонком без соединения, когда кодом являются последние 4 цифры номера, с которого происходит звонок пользователю.

Как выглядит форма в мобильном приложенииКак выглядит форма в мобильном приложении

Манила цена сервисов: от 10 копеек за такой звонок. Протестировав их оказалось, что за эту стоимость звонки доставляются не всегда, в некоторые страны звонки через них в принципе не работают. Интересно, что у некоторых провайдеров звонок стоит от 4, что даже дороже sms. Как выяснилось позже - дешевые сервисы использовали серые каналы, подменяли исходящие номера на чужие или несуществующие и сами не платили за трафик. Клиент, перезвонивший по такому номеру, мог обнаружить на другом конце прачечную или секс-шоп.

Стало понятно, что гарантия доставки может быть лишь при наличии своих пулов номеров и белых каналов, а значит и экономии в 20 раз не получится. В партнерстве с greensms.ru под нас запустили сервис со своими номерами и резервными каналами, а позже добавили в API и выложили SDK к нему на разных языках на GitHub. В процессе оказалось, что даже при нулевой продолжительности соединения с абонентом, вышестоящие операторы связи произвольно тарифицируют количество секунд разговора (кстати, кто сталкивался с подобным и смог решить это с оператором или на стороне Asterisk напишите, пожалуйста, в комментариях).В итоге удалось добиться стабильных результатов при стоимости в 40-60 копеек.

Сначала пользователи встретили нововведения замешательством. Конверсия при использовании sms >95%, звонки сейчас показывают ~85%. Еще год назад конверсия была, примерно на 10% хуже, но стала расти по мере распространенности метода и с изменениями в интерфейсе.

Статистика конверсии по странам в админкеСтатистика конверсии по странам в админке

Люди не понимали, где брать 4 цифры, в каком номере, сбрасывали вызов, запрашивали повторный и потом всё равно обращались в поддержку, приходилось всё объяснять, показывать.

Сейчас проще: всё больше проектов используют этот способ, люди привыкли и не так удивляются. Кстати, теперь еще и Яндекс их внедрил для подтверждения номера при входе на новом устройстве в свой аккаунт.

Совет: обязательно размещать рядом с полем ввода цифр скриншот телефона с выделенными 4 цифрами, которые нужно будет ввести пользователю.
Где допустимо стоит подключать автоматическое считывание цифр номера при регистрации из приложения.

Количество фейковых регистраций упало до минимума. Но новый метод привлекает новые попытки использовать его в своих целях: спам звонками, попытки спарсить базу телефонов пользователей и прочее непотребство, требующее превентивных мер.

Финальная битва между добром и интернетом

А не будет её финальной. Чем популярнее становится сервис, тем больше желающих использовать его в плохих целях. И на новые методы защиты находятся новые методы нападения. Так что ждем новых ухищрений, может с помощью eSIM?

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

Подробнее..

От SMS до AWS выбор технологии управления в зависимости от проекта

04.09.2020 18:19:39 | Автор: admin
Пост ориентирован на людей, размышляющих над созданием первой системы управления. Опытных специалистов может заинтересовать взгляд сверху на разные технологии управления Интернета вещей, выводы и короткий прогноз в заключении.



Задача


Мы в Synergy Team разрабатываем и выпускаем промышленные контроллеры. Они предназначены для учета ресурсов, управления объектами энергетики и других применений, которые принято называть Интернетом вещей (IoT). Часто нашим заказчикам не нужны просто контроллеры. Им нужно комплексное решение, включающее систему управления.

И эта система должна быть близка к оптимальной: быстрой, легкой и надежной. Но, согласно известному анекдоту, одновременно выполнить все эти требования невозможно. Нужно искать баланс в зависимости от задачи, что мы и попробовали сделать.


В этом посте мы ограничились классом задач, когда IoT контроллеры напрямую подключаются к управляющей системе (Directly-connected IoT). Итак, наша задача разработать систему управления, которая должна:
  • передавать данные от датчиков, подключенных к контроллерам,
  • передавать события (например, о превышении температуры, открытии двери, потере связи и т.п.),
  • передавать команды управления к исполнительным устройствам,

Если система состоит из большого количества объектов и/или выполняет ответственные задачи, она дополнительно должна:
  • постоянно контролировать наличие связи с объектами,
  • собирать статистику по своей работе,
  • обновлять настройки и программное обеспечение контроллеров (по запросу);
  • иметь защиту от несанкционированного доступа. Для государственных и крупных частных компаний система также должна соответствовать законодательству по работе с объектами критической информационной инфраструктуры (КИИ). В частности, требованиям 187-ФЗ, ФСБ, ФСТЭК, приказам Минкомсвязи и т.п.

Управление без выделенного сервера


Для нескольких объектов задача просто решается с управлением по GSM сети посредством SMS команд или звонков. Этот подход был популярен в начале 2010х, а его плюсы и минусы описаны, например, здесь. Для большинства серьезных систем этот подход на сегодня теряет актуальность.


Чуть более сложным является ручное управление контроллерами по выделенным IP через WEB или командную строку (CLI). Контроллеры должны быть постоянно включены в сеть, иметь статические белые или серые IP адреса. Альтернативно можно использовать динамические IP c привязкой к статическим доменным именам по технологии DynDNS или аналогичным. Это неплохо работает, если объектов мало и к надежности не предъявляется особых требований.

Недостатки:
  • неудобно, если WEB страницы от всех контроллеров нельзя разместить на экране(ах) диспетчера;
  • большая абонентская плата за статические IP адреса;
  • сложно настроить неподготовленным пользователям, когда устройства расположены за NAT;
  • долго согласовывать с оператором связи выделение пула адресов и доступ в IP подсеть. Например, для организации GSM APN у нас уходили недели;
  • неудобно, поскольку диспетчеру необходимо анализировать данные на мониторе глазами;
  • высокий риск несанкционированного доступа к контроллерам при использовании сетей общего пользования (Интернет).

Для быстрых демонстраций, контроля за несколькими объектами достаточно использовать управление по IP, SMS или телефонным звонкам.

Здесь мы не рассматривали ситуацию, когда на одном и том же компьютере работает диспетчер и установлена система управления, поскольку считаем ее вырожденным случаем следующего пункта.

Управление через выделенный сервер


В оставшихся случаях нужен сервер с выделенным IP, по которому будут подключаться контроллеры, диспетчеры и сторонние системы.

Если контроллеры передают данные только в направлении сервера, например, счетчики воды или простейшие навигационные устройства (трекеры), достаточно однонаправленного соединения. При этом контроллерам достаточно знать IP сервера.

В нашей задаче для обновления ПО контроллера и передачи команд нужен двухсторонний обмен данными. Для организации такого обмена необходимо:
  • периодическое считывание настроек или команд с сервера по инициативе контроллера (допустимо, если не нужна быстрая реакция на команды), либо
  • использование на контроллерах статических IP или доменных имен, чтобы сервер мог достучаться до них самостоятельно, либо
  • использование протоколов связи, предусматривающих установку туннеля. В частности, MQTT, EGTS или программных брокеров (надстроек над протоколами прошлых поколений). При этом контроллерам не нужны статические IP. После включения, потери связи или перезагрузки контроллеры инициативно установят и будут поддерживать туннельное соединение с сервером.


Разработка на базе коробочных продуктов


Когда речь заходит про систему управления на базе проверенного и надежного решения, в первую очередь, вспоминают про SCADA. И это полностью оправдано, когда речь идет об автоматизации производства или большом объекте энергетики. Словом, когда в системе содержится большое количество датчиков и исполнительных устройств разного типа. Для таких систем требуется разработка уникальных для каждого объекта мнемосхем и алгоритмов взаимодействия оборудования, что превосходно продумано в SCADA.

Если же нужен мониторинг и/или управление однотипными объектами (как в предложенной задаче), то использование SCADA может сделать решение слишком тяжелым из-за сложности настройки, трудоемкости добавления типовых объектов и повышенных требований к производительности сервера. Лучше использовать одну из специализированных коробочных систем, представленных на рынке, наиболее подходящую для задачи. Например:
  • систему мониторинга и управления сетевым оборудованием Network management system (SNMP, TR-069, CLI);
  • систему автоматизированного учета тепла, электричества, газа, воды. Для краткости АСКУЭ;
  • систему навигационного трекинга подвижных объектов с контролем бортовых систем;
  • систему управления климатом (вентиляция, кондиционирование и отопление) HVAC;
  • систему умный дом/офис/здание;
  • систему управления объектами энергетики: подстанциями, наружным (уличным) освещением, зарядными станциями для электротранспорта;
  • систему контроля доступа и охранно-пожарной безопасности, и т.д.

Перечисленные системы часто перекрывают функционал друг друга. Например, на базе АСКУЭ можно реализовать управление наружным освещением, а на базе Умного дома сделать управление климатом и доступом. Расширение функционала живых систем происходит постоянно. Появляются новые API для интеграции коробочных продуктов в решения заказчиков, а также поддержка пользовательских функций в коробочном ПО. Огромным плюсом коробочных продуктов является тот факт, что их можно купить и поставить на свой сервер, получив отчуждаемое решение, работающее в закрытом контуре заказчика.

При наличии планов по продаже собственной системы на свободном рынке надо понимать, что из сегодняшнего поставщика коробочного ПО завтра может вырасти конкурент. Кроме этого, использование коробочных систем несет в себе такие дополнительные риски, как:
  • повышение цены лицензий,
  • слабый контроль над системой (нет исходников, даже если есть, крайне сложно поддерживать),
  • многие системы можно использовать только в качестве облачного сервиса, что может быть неприемлемым для больших и/или государственных проектов.

Разработка на базе IoT платформ


Если использование коробочного ПО невозможно, хорошей идеей будет разработка на одной из популярных IoT платформ. Такие платформы содержат универсальные модули, необходимые в любой IoT системе для:
  • регистрации и удаления IoT устройств,
  • безопасной аутентификации IoT устройств и поддержания связи с ними,
  • работы с данными (базы данных, оптимизированные для разных задач),
  • регистрации пользователей и управления правами доступа,
  • формирования аналитики по собранным данным,
  • формирования уведомлений для пользователей (SMS, E-Mail, push сообщения, ),
  • хранения последних данных, считанных с IoT устройств,
  • выполнения различных действий по событиям,
  • визуализации данных и т.п.

Архитектура системы на IoT платформе практически не зависит от ее размера. Все задачи по балансировке нагрузки, устранению тормозов баз данных и тяжелых функций берет на себя платформа. Кроме этого, серверная часть приложения быстро собирается из кубиков микросервисов. Сокращается как время выхода на рынок, так и стоимость решения, напрямую зависящая от оплаты работы программистов.

Систему на IoT платформе можно разработать за минимальное время. Ее архитектура не будет сильно меняться при увеличении размера.

Плюсы разработки на облачной IoT платформе:
  • пилотный проект можно запустить с небольшими затратами. В AWS для большинства сервисов есть лимиты бесплатного использования. В Яндекс.Облаке предусмотрен тестовый период и стартовый грант.
  • гибкая тарификация, например, стоимость сервиса базы данных зависит от количества запросов, а стоимость MQTT-брокера от количества обработанных сообщений. Это особенно выгодно на старте, когда надо считать каждую копейку.
  • IoT платформы протестированы на миллионах устройств. Можно быть уверенным в масштабируемости (решение не придется сильно переписывать при увеличении количества устройств) и архитектурной грамотности решения (если советоваться с инженерами по поддержке используемых IoT платформ).
  • информационная безопасность обеспечивается самой платформой на различных уровнях.
  • задачу первичной разработки можно отдать на аутсорс. В перспективе решение смогут сопровождать не только разработчики изначальной системы, как это бывает обычно. Ведь IoT платформы имеют мощную техподдержку, подробно документированы, а количество разработчиков, умеющих с ними работать, неуклонно растет.

Недостатки:

  • компоненты IoT платформ работают только на мощностях их владельцев, создать полностью отчуждаемую систему, которая будет работать в ЦОД заказчика, возможно в редких случаях;
  • стоимость использования IoT платформы для крупных проектов может быть выше стоимости аренды виртуальных машин в ЦОД;
  • миграция с одной IoT платформы на другую связана с изменением приличного объема кода. Хотя сейчас наметилась тенденция к стандартизации API;
  • далеко не все IoT платформы развернуты в ЦОДах внутри России, что делает невозможным их использование в интересах государственных заказчиков.

Полностью своя разработка


Если недостатки предыдущих решений являются критическими, необходима разработка собственного решения. И как бы ни был хорош план, первые версии системы будут кривоваты.

Собственные решения часто реализуют на базе таких open-source систем, как ThingsBoard, OpenSCADA, MajorDoMo, Home Assistant, iobroker, Nokia glial. Для небольших проектов они могут оказаться слишком тяжелыми из-за:
  • большого срока разработки и соответствующих финансовых затрат. Обычно счет идет на человеко-годы;
  • c ростом количества объектов будут возникать узкие места в виде медленных баз данных, сборщиков, генераторов отчетов и т.п., для разрешения которых потребуется изменение архитектуры и дополнение ее баллансировщиками и дублирующими ресурсами;
  • расходов на постоянное администрирование и техническую поддержку.

В то же время:
Своя разработка оправдана либо для госзаказа, либо проектов с амбициозными планами и большими бюджетами, либо, когда независимость и безопасность критически важна.

Если нужна быстрая демонстрация (MVP), то ее можно сделать на IoT платформе или коробочном ПО, взяв на вооружение проверенные временем подходы, параллельно разрабатывая свое большое решение.

Заключение


На основе сделанного анализа разных систем предлагаем следующий алгоритм выбора системы управления.



Для демонстраций и маленьких IoT проектов на несколько объектов можно использовать прямое управление по IP, SMS или через GSM звонки. В остальных случаях необходима система верхнего уровня. Использование SCADA оправдано в промышленной автоматизации и на больших объектах энергетики. Для учета ресурсов, управления сетевым оборудованием, трекинга, охраны, умного дома и т.п. удобно использовать коробочное решение нужной специализации. Разработка систем на IoT платформах сложнее, но дает больше перспектив и гибкости. Решения на зарубежных IoT платформах серьезно ограничены по масштабу и областям применений в России. Самой сложной является полностью своя разработка. И она оправдана только для госзаказа и самых амбициозных проектов. При этом для быстрых демонстраций и копирования лучших практик будет полезно параллельно с собственной разработкой сделать эскизный проект на IoT платформе.

Напоследок хотим поделиться небольшим прогнозом:

  • в облачных IoT платформах стоит ждать появление преднастроенных шаблонов Умного дома, АСКУЭ, NMS, СКУД и т.п. Это еще больше упростит использование IoT платформ и привлечет к ним еще большую аудиторию.
  • традиционные разработчики SCADA и других коробочных решений предложат больше инструментов для внешних разработчиков, которые хорошо зарекомендовали себя в IoT платформах. Закрытые коробочные решения вряд ли выдержат рыночную конкуренцию.
  • отечественные IoT платформы для государственных и больших частных заказчиков будут развиваться еще активнее.
  • API разных IoT платформ станут со временем более похожими друг на друга. Из-за этого переход с одной IoT платформы на другую будет упрощаться.

В следующем, более техническом, посте мы расскажем о нашем проекте по мониторингу систем связи на платформе AWS и контроллерах GIC.

Обязательно напишите в комментариях, если мы упустили что-то важное. Одна из целей нашего блога получить конструктивный feedback.
Подробнее..

Конвертация скрипта Bash в код С для отправки СМС через usb модем HUAWEI E3372

17.06.2020 16:18:48 | Автор: admin
Несколько моих проектов отправляют СМС и в последнее время после обновлений сервера, а может и ряда других причин, отправка СМС стала почти невозможной.

Старенький USB модем HUAWEI (марку не буду разглашать) перестал стабильно висеть на одном COM порту и временами переподключался на другие порты, совсем отключался и терял антенну.

Да и ситуация с библиотекой GSMComm была непонятной и болезненной.

GSMComm это пакет для телефонов GSM, в основном для выполнения задач, связанных с SMS.
www.nuget.org/packages/GSMComm последня версия 1.21.1 от 10.10.2015 года.


Поиск по интерент показал, что есть возможность использовать встроенный функционал WEB API новых модемов HUAWEI, более эффективно, чем старый подход с AT командами реализованный в GSMComm.

Выяснилось, что есть прекрасный usb модем HUAWEI E3372, который почти хакерским способом способен отправлять СМС как из скрипта (Curl + Bash), так и из кода (Python, Perl), и, как я предположил, из C#.

Самое печальное, что компания HUAWEI не предоставляет никакой документации как это сделать и все найденные методы имели экспериментальный харктер и зависели от семейства устройств.

В общем, опираясь на найденный материал, не гарантирующий работу кода с момедом, был приобретен HUAWEI E3372.

Не углублясь в эксперименты с Python или Perl я решил попробовать разобраться с вариантами Bash + Curl.

В общем, после нескольких экспериментов был найден работающий код под MS Windows 10 + Git Bash for MS Windows.

Скрипт

curl -b session.txt -c session.txt http://192.168.8.1/html/index.html > /dev/null 2>&1#TOKEN=$(curl -s -b session.txt -c session.txt http://192.168.8.1/html/smsinbox.html)TOKEN=$(echo $TOKEN | cut -d'"' -f 10)echo $TOKEN > token.txtNUMBER=$1MESSAGE=$2LENGTH=${#MESSAGE}TIME=$(date +"%Y-%m-%d %T")TOKEN=$(<token.txt)SMS="<?xml version='1.0' encoding='UTF-8'?><request><Index>-1</Index><Phones><Phone>$NUMBER</Phone></Phones><Sca/><Content>$MESSAGE</Content><Length>$LENGTH</Length><Reserved>1</Reserved><Date>$TIME</Date></request>"echo $SMScurl -v -b session.txt -c session.txt -H "X-Requested-With: XMLHttpRequest" --data "$SMS" http://192.168.8.1/api/sms/send-sms --header "__RequestVerificationToken: $TOKEN" --header "Content-Type:text/xml" --header "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" --header "Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3"


Скрипт удивительным образом работал. И это было уже счастье, так как деньги были потрачены на модем не зря!

Осталось только понять КАК же он работает. Почитав документацию по Curl и Bash (ну по bash я не читал так догадался) прояснилась работа скрипта. Привожу этот же скрипт с моими комментариями.

Скрипт с комментариями

# https://stackoverflow.com/questions/28070500/grab-current-sessions-cookie-with-curl/28070870# Содержимое файла session.txt определяется опцией -b# Сделать GET запрос и получить куки в первый раз и записать их в файлcurl -b session.txt -c session.txt http://192.168.8.1/html/index.html > /dev/null 2>&1# Сделать GET запрос и получить куки во второй раз и записать в файл и сохранить содержимое страницы HTML в переменную TOKEN как строкуTOKEN=$(curl -b session.txt -c session.txt http://192.168.8.1/html/smsinbox.html)# Извлчеь из переменной значение метатега из <meta name="csrf_token" content="b/XNeODpHCthQXEOEjBNkICn2n7e9v4e"/> и перезаписать в ту же переменную TOKENTOKEN=$(echo $TOKEN | cut -d'"' -f 10)# Отобразить на экранеecho "$TOKEN"# сохранить подстроку в файлеecho $TOKEN > token.txt# Получить два параметра командной строки: (1) номер телефона и (2) текст СМСNUMBER=$1MESSAGE=$2# Получить количество символов в текстеLENGTH=${#MESSAGE}# Получить текущее время и отформатировать егоTIME=$(date +"%Y-%m-%d %T")# Загрузить содержимое файла в переменнуюTOKEN=$(<token.txt)# Сфоромировать текст для отправки СМС как XML SMS="<?xml version='1.0' encoding='UTF-8'?><request><Index>-1</Index><Phones><Phone>$NUMBER</Phone></Phones><Sca/><Content>$MESSAGE</Content><Length>$LENGTH</Length><Reserved>1</Reserved><Date>$TIME</Date></request>"# Отобразить переменную с текстом СМС на экране echo $SMS# Сделать POST для отправки СМСcurl -v -b session.txt -c session.txt -H "X-Requested-With: XMLHttpRequest" --data "$SMS" http://192.168.8.1/api/sms/send-sms --header "__RequestVerificationToken: $TOKEN" --header "Content-Type:text/xml"

Скажу честно, что СМС на русском я не смог добиться. Приходит абракадабра. Так что этот вопрос остался открытым и если
у кого-то есть желание закрыть тему отправки СМС на русском милости просим, дерзайте. )))

Понимание работы скрипта принесло свою пользу и приблизило к написанию кода на C#.

Было понятно, что в нем должно быть также 3 запроса и должны они делать то же самое что и благословенный Curl. Поэтому в коде приведен Curl, а ниже, аналогичный ему C# код.

Код С# для WinForms

private void button1_Click(object sender, EventArgs e){   var ip = "192.168.8.1"; // IP адрес который выдает модем в браузере после установки   var phone = "+70000000000"; // Номер телефона   var msg = "Привет!!! СМС работает!!!";   var result = SendSMS(ip, phone, msg);   if (result)   {       //TODO  Сохранить в БД, например   }   else   {       //TODO  Сохранить в БД, например   }}private bool SendSMS(string ip, string phone, string msg){    try    {        /* curl -b session.txt -c session.txt http://192.168.8.1/html/index.html > /dev/null 2>&1 */        Cookie firstCookie = null;        Cookie secondCookie = null;        string token = string.Empty;        //В первый раз получить куки        var cookieContainer = new CookieContainer();        var uri = new Uri($"http://{ip}/html/index.html");        using (var httpClientHandler = new HttpClientHandler { CookieContainer = cookieContainer })        {            using (var httpClient = new HttpClient(httpClientHandler))            {                httpClient.GetAsync(uri).Wait();                var all = cookieContainer.GetCookies(uri);                firstCookie = all[0];            }        }        /*        TOKEN=$(curl -s -b session.txt -c session.txt http://192.168.8.1/html/smsinbox.html)        TOKEN=$(echo $TOKEN | cut -d'"' -f 10)        echo $TOKEN > token.txt         */    // И спользуя куки из первого запроса получить страницу и извлечь из нее токен        if (firstCookie != null)        {            var cookieContainer2 = new CookieContainer();            cookieContainer2.Add(firstCookie); // Поместить в конейнер куки из первого запроса к сайту            var uri2 = new Uri($"http://{ip}/html/smsinbox.html");            using (var httpClientHandler = new HttpClientHandler            {                CookieContainer = cookieContainer2            })            {                using (var httpClient = new HttpClient(httpClientHandler))                {                    var html = httpClient.GetStringAsync(uri2).Result; // Получить страницу HTML                    var all = cookieContainer2.GetCookies(uri2);                    secondCookie = all[0];                    var doc = new HtmlAgilityPack.HtmlDocument(); // Используем HtmlAgilityPack чтобы преобразовать текст HTML в структурный вид                     doc.LoadHtml(html);                    var items = doc.DocumentNode.SelectNodes("//meta");                    if (items.Count >= 2) // Получить второй по счету meta тег.                    {                        token = items[1].GetAttributeValue("content", ""); // Получить значение метатега. Не спрашивайтепочему второй метатаг с токеном рабочий - не знаю )))                    }                }            }            // Когда в наличии есть куки и токен делаем отправку СМС через запрос POST            if (!string.IsNullOrEmpty(token))            {                var msgLength = msg.Length;                var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); //    TIME=$(date +"%Y-%m-%d %T")                var sms = $"<?xml version='1.0' encoding='UTF-8'?><request><Index>-1</Index><Phones><Phone>{phone}</Phone></Phones><Sca/><Content>{msg}</Content><Length>{msgLength}</Length><Reserved>1</Reserved><Date>{time}</Date></request>";                /*# Сделать POST для отправки СМС                  curl -v -b session.txt -c session.txt -H "X-Requested-With: XMLHttpRequest" --data "$SMS"                     http://192.168.8.1/api/sms/send-sms  --header "__RequestVerificationToken: $TOKEN"  --header "Content-Type:text/xml"                 */                var uri3 = new Uri($"http://{ip}/api/sms/send-sms");                var client = new RestSharp.RestClient { BaseUrl = uri3 }; // Используем RestSharp для запроса (дело вкуса)                var request = new RestSharp.RestRequest(RestSharp.Method.POST);                // Формируем свой заголовой запроса - ничего лишненго все по примеру из Curl    request.AddHeader("__RequestVerificationToken", token);                var ses = secondCookie.ToString();                request.AddCookie("cookie", ses);                request.AddHeader("Content-Type", "text/xml");                request.AddHeader("X-Requested-With", "XMLHttpRequest");                request.AddParameter("text/html", sms, RestSharp.ParameterType.RequestBody);                RestSharp.IRestResponse response = client.Execute(request);                if (response.IsSuccessful)                {                    var xmlDoc = new XmlDocument();                    xmlDoc.LoadXml(response.Content); // <?xml version="1.0" encoding="UTF-8"?><response> OK </response>                    var responseElemenets = xmlDoc.GetElementsByTagName("response");                    var resultOK = responseElemenets[0].InnerXml.ToLower();                    return resultOK == "ok"; // Ну вот и признак того, что СМС отправлено, но без отчета о доставке.                 }            }        }    }    catch (Exception)    {        //TODO в лог ошибку;    }    return false;}


Как только у вас в руках рабочий C# код вы всегда можете его улучшить.

В моем случае он работает как часы и для количества СМС в минуту вполне годится. )))

Надеюсь эта статься принелса пользу и в профессиональном и экономическом смыслах.

Душевно благодарю!
Подробнее..

Сможет ли RCS заменить SMS?

23.04.2021 12:19:27 | Автор: admin

Когда вы в последний раз переписывались по SMS? Если не считать смс-рассылок и уведомлений от вашего сотового оператора, то вы, скорей всего, давно не пользовались службой коротких сообщений. SMS-сервис безнадежно устарел и пользуется до сих пор по одной причине сотовым операторам невыгодно его отключать. SMS приносит немалый доход для операторов, в первую очередь от корпоративных клиентов. И несмотря на неудобство пользования этим сервисом (отсутствие чатов, голосовых сообщений, отправки файлов; ограниченность набора символов) и чертовски высокую цену по сравнению с мессенджерами, SMS все еще в обиходе. Эту ситуацию пытались исправить американские сотовые операторы, внедряя новый универсальный сервис текстовых сообщений Cross-Carrier Messaging Initiative (CCMI). Но не смогли (или не захотели).

SMS vs Мессенджеры


Изначально SMS был универсальным форматом общения на мобильных телефонах, но уже устарел пользователи постепенно перешли на мессенджеры: WhatsApp, Telegram, Facebook Messenger или iMessage на iPhone. Они не только позволяют отправлять собеседнику практически любые виды информации, но и в большинстве случаев стоят дешевле, так как обрабатываются онлайн.

Но универсального мессенджера нет: аудитория распределилась по нескольким сервисам, а для полноценного общения порой приходится устанавливать популярные приложения. И если на iOS есть встроенный iMessage, то после нескольких неудачных попыток выпустить свой мессенджер для Android-смартфонов Google планирует глобально изменить рынок текстовых сообщений.


Крупнейшие операторы сотовой связи США, в лице AT&T, Verizon, T-Mobile и Sprint, объявили в 2019 году, что они совместно запустят службу для обмена сообщениями Rich Communications Services (RCS) к 2020 году. Но в мае 2020 года представители Verizon заявили: Хотя мы не вправе выступать от имени CCMI, и дата запуска сервиса официально не объявлена, владельцы Cross Carrier Messaging Initiative решили прекратить совместное предприятие. Тем не менее, мы по-прежнему привержены делу улучшения качества обмена сообщениями для клиентов, включая повышение доступности RCS.

В таком положении дел нет ничего неожиданного.

RCS впервые появился в 2008 году, когда GSMA продавала его под брендом Joyn. Была надежда, что операторы Vodafone и Verizon будут использовать RCS для создания всевозможных совместимых мультимедийных функций, подобных WhatsApp от Facebook. И хотя технология получила некоторое распространение в развитых странах, например в Японии, в США она остается непопулярной.

В 2014 году компания Jibe разработала платформу, которая объединяла RCS-сети провайдеров. В 2015 году её приобрела Google и начала продвигать среди мобильных операторов. При поддержке Google ассоциация GSMA в ноябре 2016 года выпустила универсальный протокол, который стал первым шагом для объединения операторов, производителей устройств и разработчиков ПО.

Это согласованный в телеком-отрасли стандарт, который позволяет общаться абонентам различных операторов и стран. Он получил поддержку более 55 компаний и операторов связи, среди которых AT&T, Verizon, Samsung, LG, Microsoft, Google, Huawei. Российские операторы Билайн, МТС, Tele2, МегаФон также поддержали инициативу универсального протокола RCS.


Мы стремимся обеспечить совместимость RCS с другими провайдерами, заявили в T-Mobile. Клиенты T-Mobile с устройствами Android в настоящее время могут пользоваться сообщениями RCS в нашей сети, а также со многими другими клиентами по всему миру, взаимодействуя через Google.

T-Mobile единственный американский оператор, который добился каких-то положительных результатов от RCS с момента создания CCMI. Оператор впервые запустил RCS в 2015 году, а в 2020 году он объявил о сделке с Google по поставке RCS для своих предоплаченных клиентов. Затем, в марте 2021 года, T-Mobile заявил, что достиг соглашения с Google, чтобы сделать Google Messages (Сообщения) приложением для обмена сообщениями по умолчанию на всех своих устройствах Android. T-Mobile US является третьим по размеру американским оператором сотовой связи, а его клиентами считаются более 80 млн человек.

Google продолжает продвигать RCS в США и других странах, в надежде заработать на рекламе (кто бы сомневался) посредством этого сервиса. Это неудивительно, учитывая эффективность таких услуг в таких странах, как Япония. Согласно Synchronoss, японские потребители открывают около 85% сообщений RCS и кликают около 60% это значительно лучше, чем 0,001% пользователей, которые кликают на мобильные рекламные баннеры.

До отказа от Allo и перехода к стандартному приложению Сообщения у Google было сразу четыре приложения Hangouts, Allo, Duo и Сообщения, с беспорядком в которых нужно было ещё разобраться.

Если Hangouts переориентировали на корпоративный рынок для конкуренции со Slack, то Allo разрабатывали с нуля как новое приложение для общения с уникальными возможностями (интеграция Google Assistant).


Оно не смогло заинтересовать владельцев Android Allo установили в сотни раз меньше чем WhatsApp и Facebook Messenger. По словам представителей Google, пользователи устали от разнообразия мессенджеров, боялись отсутствия поддержки в долгосрочной перспективе и уходили к основным представителям амбассадорам мессенджеров на рынке.
iMessage, который Google в том числе рассматривала в качестве примера, был встроен в iPhone, WhatsApp позволял избежать платы за SMS и стал первым, кто воспользовался push-уведомлениями, а у Facebook Messenger огромная база пользователей соцсети.

У Allo не было стратегии по привлечению новых пользователей. Единственное, что реализовала Google, предложение скачать Allo через push-уведомление, если отправитель отправил сообщение через мессенджер.
Эксперимент с Allo не сработал, и в Google решили начать заново с чистого листа использовать SMS-приложение Сообщения, которое есть практически у всех пользователей Android, и обновить его до современного уровня. По его оценке, ежегодно отправляются 8 трлн SMS-сообщений это значительная аудитория людей, которая может перейти на более качественное общение через RCS.
В апреле 2018 года Google отменила разработку мессенджера Allo, чтобы сфокусироваться на Chat (по мнению Google, более удобное для пользователей название RCS) встроенном протоколе RCS в стандартном приложении Сообщения на Android.

Несмотря на то, что на телефонах Samsung установлено собственное приложение для SMS (которое также поддерживает RCS), к апрелю 2018 года каждый месяц прибавлялось до 100 млн новых пользователей Android, которые так или иначе пользуются SMS универсальным способом отправки сообщений любому владельцу смартфона.

План Google намного сложнее, чем запуск очередного мессенджера компании пришлось собрать более 40 операторов и около 10 производителей, чтобы разработать новый стандарт. Он должен был гарантировать, что чат будет везде работать одинаково и с одинаково большим набором функций. И большинство компаний в альянсе конкуренты между собой, которые не доверяют как друг другу, так и Google.

Но Google смогла объединить их (как показывает история, пока что безрезультатно): для SMS так или иначе найдётся замена, и либо операторы примут участие в замене технологии, либо будут продолжать наблюдать, как люди уходят в Apple, Facebook и к другим разработчикам мессенджеров.

RCS действительно важен для Google, однако RCS в США и других странах страдает от проблемы курицы и яйца, когда операторы не поддерживают услугу, потому что клиенты ее не используют, и наоборот.


Первая версия RCS была представлена в 2012 году, на её основе мобильные операторы США разработали проприетарную версию протокола, которая работала только между абонентами одной сети. Из-за этого RCS долгое время не могла стать популярной пользователи даже не знали, что вместо SMS появился новый формат, и не использовали его.

Конечно, есть еще одно серьёзное препятствие, с которым сталкивается RCS: Apple не поддерживает его, а Apple контролирует половину рынка смартфонов в США.

Тем не менее, услуга может получить поддержку 5G, которая требует от операторов установки технологии IMS (IP Multimedia Subsystem), которая упрощает взаимодействие с RCS.

После года ожидания Google в конце июня 2019 года начала самостоятельно предоставлять услугу RCS-сообщений в приложении, не дожидаясь действий CCMI. Google обошла колебания операторов и развернула приложение Google Messages, подключённое к его сети Jibe (хотя будет использована сеть оператора, если он совместим с универсальным профилем). Это шаг, который означает, что клиентам не нужно ждать, пока их операторы связи начнут работу, которую они должны были сделать пять лет назад. T-Mobile фактически передала бразды правления своим решением, приняв Messages в качестве SMS-приложения по умолчанию для всех телефонов T-Mobile, подключив всех своих клиентов к сети RCS от Google.

Универсальный мессенджер


RCS мертворождённая технология, но ее пытаются реанимировать. Попытка внедрить свое Messages на базе RCS, Google хочет конкурировать с Apple и его iMessages. Google высокотехнологичная и многогранная компания обладающая целым рядом интегрированных сервисов, для налаживания коммуникации, в то же время единой, многофункциональной платформы, как iMessage у них до сих пор нет.


Возьмем к примеру iMessage от Apple:

  • Работает на всех устройствах от iPhone до MacBook;
  • В случае, если у получателя нет iMessage, пользователь все равно увидит сообщение, но в форме СМС;
  • SMS от iMessage отличаются по цвету.
  • Интеграция с другими сервисами: с 2016-го в iMessage появились мини-приложения, с помощью них можно отправлять деньги, играть внеболбшие игры и т.д.;
  • Есть остальные функции, стандартные для современного мессенджера: видеозвонки, стикеры, аудиосообщения и др.
  • И конечно они предустановлены.

Попытки Google ворваться в игру:

  • 2005 Google Talk. До появления смартфонов был популярный чат для компьютеров. В Android также появилось приложение Google Talk, которое продержалось до 2011 года.
  • 2011 Google Plus. Компания сделала свою социальную сеть, которая также предустанавливалась на Android смартфоны. В ней для чата были доступны два приложения: Huddles для переписки и Hangouts для видеозвонков.
  • 2013 Google Hangouts. Компания отделяет Hangouts от Google Plus, и сосредотачивает переписку и звонки в одном приложении, добавив SMS на манер iMessage.
  • 2016 Google Allo/Duo. Hangouts из-за своей непопулярности (WhatsApp, iMessage и Facebook Messenger успели отхватить большую аудиторию) переделывают в корпоративный мессенджер а-ля Slack, а его место занимает Allo приложение для чата с искусственным интеллектом. Звонки переходят в Google Duo.
  • Настоящее время Allo также провалился, хотя Duo используют довольно часто, отмечая его простоту.

И это только самые известные. Были и менее популярные:

  • Meebo мессенджер, которые существовал с 2005 по 2012 годы, пока Google не выкупила его, чтобы слить команду с разработчиками Google+;
  • Google Buzz платформа для микроблоггинга и обмена сообщениями, который был интегрировал в Gmail. Проработал с февраля 2010 по декабрь 2011 года;
  • Google Spaces сервис для групповых чатов, в который были интегрироваы такие сервисы как Google Search и YouTube. Работал с мая 2016 по апрель 2017 года;
  • Google wave;
  • YouTube Messages небольшой мессенджер был встроен в приложение YouTube, где пользователи могли отправлять ссылки на ролики внутри хостинга и отправлять сообщения. Работал с 2017 по 2019 годы.

Чем RCS лучше SMS?


Вы получаете смс не только от ваших контактов, но и от вашего сотового оператора, от банка, где у вас открыть счет, от разных организаций и сервисных центров. Но в отличие от контактов, которым вы можете отправить смс в ответ, то написать банку вы не сможете. И люди используют мессенджеры как самый быстрый и простой способ общаться в среде, где нет ничего лишнего. Теперь таким образом можно коммуницировать не только с друзьями, но и с брендами, получать доступ к самым нужным услугам, минуя поисковики и сайты.

Нет почти никаких новшеств с точки зрения контента и методов взаимодействия с помощью SMS. Например, отправка SMS или фотографий MMS группе пользователей связана с множеством неудобств.



Зачем нужен RCS когда есть WhatsApp, Telegram и другие? И нужен ли?


RCS это стандарт обмена сообщениями, который по сути является преемником SMS. Он поддерживает такие вещи, как уведомления о прочтении (подумать только, до чего дошёл прогресс), медиафайлы и групповые беседы, но он не работает сам по себе, как обычное приложение для чата, которое подключается к централизованным серверам. В соответствии с его разработкой RCS позволяет каждой сети оператора развёртывать свою собственную, немного отличающуюся версию и соединять их между собой по крайней мере, при условии, что все они совместимы со стандартами универсального профиля. До CCMI у разных операторов были свои системы (которые могли быть довольно небезопасными), и некоторые из них не работали с другими, поэтому абоненты получали преимущества обмена сообщениями RCS только в определенных приложениях и при разговоре с людьми внутри своей сети оператора связи.

В теории, при соблюдении ряда условий, протокол RCS может стать унифицированной платформой, которой уже четверть века успешно служит SMS. Функциональные, красочные, условно конфиденциальные но при этом разъединённые мессенджеры привносят некий дискомфорт и хаос в нашу жизнь. Конечно, продукт который увязал бы в единую, современную систему миллиарды пользователей мог бы легко прижиться. На практике же, позиция одного из ведущих игроков рынка корпорация Apple, который не заинтересован в усилении своего конкурента, скорее всего, будет неизменна. Apple и в дальнейшем не откажется от существующих SMS.

Однако пока не решён ряд проблем: слабая распространенность формата RCS и его вариативность, поскольку многие компании не пытаются придерживаться принятого международного стандарта (универсального профиля). Появление CCMI как раз решило бы эту проблему и сделала бы соблюдение универсального профиля выгодным. Но есть и другие минусы: RCS-формат не имеет сквозного шифрования, поэтому спецслужбы могут свободно требовать содержимое переписок.

Android работает на 75% смартфонов по всему миру, и на запланированный Google переход от SMS к RCS могут обратить внимание регуляторы. RCS-чаты в Сообщениях, которые обрабатываются компанией, а не операторами, станут ещё одним сервисом и приложением Google, попадающими под антимонопольное законодательство Европы.

Компания считает, что обмен сообщениями основная функция Android-смартфона и её нужно улучшить любой ценой, поэтому берёт на себя такую ответственность. В случае успеха следующим шагом Google станет добавление в универсальный протокол RCS сквозное шифрование.

Технически нет ничего, что могло бы ей помешать, но для этого компании снова придётся сотрудничать с GSMA и, возможно, конфликтовать с правительствами. Ведь многие спецслужбы будут недовольны, если стандартный способ обмена сообщениями на 75% телефонов в мире уйдёт в тень, как называет сквозное шифрование ФБР.

Если бы новость о массовом внедрении RCS, на смену устаревшей SMS, была бы озвучена во второй половине 2000-х, это могло бы стать действительно ярким событием, однако с тех пор прошло слишком много времени. Даже если RCS удастся заменить SMS, то вот заменить другие мессенджеры, вроде того же Telegram, вряд ли удастся. Однако единый протокол обмена будет полезен и остальному миру, так как убьет необходимость устанавливать несколько разных чатов для всех своих друзей и знакомых.



На правах рекламы


Быстрые серверы для любых целей это про наши эпичные серверы. Максимальная конфигурация 128 ядер CPU, 512 ГБ RAM, 4000 ГБ NVMe.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru