Очень часто для отправки СМС из различных скриптов используются недорогие мобильные USB-модемы. Раньше использовались модемы, при подключении которых в системе появлялся один или несколько последовательных портов и для отправки СМС было необходимо отправлять AT-команды на один из этих портов, для чего можно было использовать пакет smstools или gsm-utils.
В последнее время набирают популярность модемы, при подключении которых в системе создаётся виртуальная сетевая карта, а управление модемом осуществляется через web-интерфейс (например модемы Huawei с прошивкой HiLink). Использовать привычные решения для отправки SMS с этими модемами не получится, однако это не означает что их нельзя использовать для отправки SMS из скриптов. Далее будет рассмотрен пример скрипта на Perl, который позволит отправлять SMS используя модемы Huawei с прошивкой HiLink.
Вобщем-то задача достаточно простая: в веб-интерфейсе есть функция отправки SMS и нужно просто выяснить какие запросы отправляются из браузера и потом реализовать отправку таких же запросов из скрипта. Для изучения были выбраны два модема Huawei E3272. Один с прошивкой от оператора «Билайн», другой - с прошивкой от татарстанского оператора «Летай».
В процессе изучения выяснилось что несмотря на то что оба модема одной и той же модели, они имеют определённые различия не только в оформлении веб-интерфейса, но и во внутреннем API, использующемся Web-интерфейсом.
Так на модеме с прошивкой от «Билайн» достаточно отправить запрос на URL "http://192.168.1.1/api/sms/send-sms" без всякой авторизации. На модеме с прошивкой от «Летай» предварительно надо "кинуть" запрос на "http://192.168.1.1/api/webserver/SesTokInfo" чтобы получить идентификатор сессии и авторизационный токен. Разобравшись с особенностями этих модемов был написан скрипт, листинг которого приведён ниже:
#!/usr/bin/perl use strict; use warnings; use diagnostics; use LWP::UserAgent; use HTML::Entities; use Time::HiRes qw (time); use POSIX; use Data::Dumper; use utf8; use Encode; # Собственно функция отправки СМС sub SendSMS { my $ip = shift; my $phone = shift; my $msg = shift; # Формируем запрос my $request_data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; $request_data .= "<request><Index>-1</Index><Phones><Phone>"; $request_data .= encode_entities($phone); $request_data .= "</Phone></Phones><Sca></Sca><Content>"; $request_data .= encode_entities(decode("utf8", $msg)); $request_data .= "</Content><Length>"; $request_data .= length($msg); $request_data .= "</Length><Reserved>1</Reserved><Date>"; $request_data .= encode_entities(strftime("%Y-%m-%d %H:%M:%S", localtime())); $request_data .= "</Date></request>"; # Сначала попробуем получить авторизационный токен и идентификатор сессии # ВАЖНО! Отсутствие токена не всегда является проблемой! my $ua = LWP::UserAgent->new( agent => "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36" ); my $response = $ua->get("http://$ip/api/webserver/SesTokInfo"); my $token; my $session_id; if ($response->content =~ m{<TokInfo>(.+?)</TokInfo>}) { $token = $1; } if ($response->content =~ m{<SesInfo>SessionID=(.+?)</SesInfo>}) { $session_id = $1; } # Создаём запрос my $req = HTTP::Request->new("POST" => "http://$ip/api/sms/send-sms"); # Указываем основные заголовки $req->header("Origin" => "http://$ip"); $req->header("Referer" => "http://$ip/html/smsinbox.html"); $req->header("Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8"); $req->header("X-Requested-With" => "XMLHttpRequest"); # Если есть токен то добавляем ещё и его $req->header(":__RequestVerificationToken" => $token) if $token; # Если есть идентификатор сессии $req->header("Cookie" => "SessionID=$session_id") if $session_id; # Добавляем данные к запросу $req->content($request_data); # Выполняем запрос $response = $ua->request($req); # Обрабатываем результат return $response->is_success && $response->content =~ m{<response>OK</response>}s ? 1 : 0; } my $ip = shift @ARGV; my $phone = shift @ARGV; my $msg = shift @ARGV; if (!defined($msg)) { print "Usage: $0 <Modem IP> <Phone Number> <Message>\n"; exit(); } # $ip не валидируем, потому что там может быть и доменное имя (например m.home) # пробуем валидировать $phone: if ($phone !~ m{^(?:\+7|8)?\d{10}$}) { print "Bad phone number!\n"; exit(); } # $msg не валидируем. Если до сюда дошли то значит он не пуст и этого достаточно my $result = SendSMS($ip, $phone, $msg); print $result ? "OK\n" : "ERROR\n";
Скрипт необходимо сохранить под любым удобным именем (например "sendsms_hilink.pl") и сделать исполняемым. Скрипт принимает три параметра: IP-адрес модема, номер телефона получателя и собственно текст сообщения. Например:
./sendsms_hilink.pl 192.168.1.1 +79112345678 "Тестовое сообщение"
В случае успеха скрипт напечатает на стандартный вывод "OK", в случае ошибки - "ERROR". Скорее всего скрипт будет работать и с другими модемами Huawei с прошивкой HiLink, но гарантировать что-либо тут трудно.
На этом всё. Приятной работы!
Anonymous 2018-03-15 15:53:42 (#)