Здравствуй, Хабрахабр! В этой статье я хочу поведать о том, как можно решить вопрос автоматического переключения режима приёма звонков в «Мультифоне» (gsm-to-gsm/gsm-to-sip) при регистрации смартфона на сервере Asterisk. Ниже будет рассказано, для чего мне это понадобилось, какие варианты решения рассматривались, и как в итоге было реализовано. Приведённый ниже пример был использован на базе домашнего сервера под управлением Debian Lenny вместе с Asterisk 1.6, но с большой вероятностью будет работать и на других распространённых Linux-платформах.
Являясь абонентом Мегафона, с недавнего времени стал пользоваться их VoIP-сервисом. Одной из особенностей сервиса является то, что кроме совершения исходящих, можно принимать входящие на мобильный номер звонки через SIP-клиент. Этой возможностью мне и захотелось воспользоваться, настроив подключение своего смартфона к домашнему серверу Asterisk через SIP. На самой процедуре регистрации, подключении к услуге и настройке SIP-аккаунта, я останавливаться не буду — про это уже достаточно написано. Хотелось сделать следующее: при подключении смартфона к серверу, все входящие сотовые звонки начинают поступать через SIP; при отключении от сервера должен восстанавливаться обычный режим приёма звонков телефоном (gsm-to-gsm). Решение должно быть максимально автоматизированным и требовать от меня не более одного движения — нажатия на кнопку подключения к серверу на рабочем столе смартфона.
Для реализации задуманного необходимо выполнение как минимум двух условий:
1. «Мультифон» должен предоставлять возможность переключения режима приёма сотовых звонков: приём на сотовый, приём в sip-клиент. Такая возможность у сервиса есть, и реализована она через отправку определённого https-запроса на сервер (подробнее о формате запроса можно прочитать перейдя по ссылке, указанной в конце статьи). Запрос можно выполнить из консоли (например при помощи curl или wget), что делает возможным его использование в скриптах.
2. Сервер должен «знать», что на Asterisk'e зарегистрировался определённый peer (смартфон), и при подключении/отключении выполнить https-запрос. Подробнее рассмотрим это вопрос ниже.
Необходимо определить момент, когда смартфон зарегистрируется на сервере. Изучив реакцию Asterisk'a на подключение клиента и доступные средства информирования о произошедшем событии, определил для себя 2 основных варианта решения: узнать о событии через AMI (Asterisk manager API) или обрабатывать через внешний скрипт список подключенных клиентов, которые Asterisk выводит по команде «sip show peers». Мне был ближе второй вариант, поэтому я решил остановиться на нём. (Кстати, сейчас подумал о том, что есть еще третий способ — парсить вывод файла /var/log/asterisk/full на предмет наличия строк «Registered SIP 'peername'» и «Unregistered SIP 'peername'». Возможно, так было бы даже проще, но я пошел по другому пути. Если вы знаете более простые варианты, расскажите, я их с удовольствием выслушаю).
Посмотрим, что пишет сервер:
В колонке «Host» у отключенных клиентов видим надпись (Unspecified), а у зарегистрированных — IP-адрес. Этих данных достаточно для того, чтобы определить состояние регистрации у клиента. Таким образом задача свелась к тому, чтобы написать скрипт, который будет опрашивать на сервере состояние необходимого sip-аккаунта (смартфон — mywifipeer), и если состояние его регистрации изменилось, отправлять https-запрос. Для контроля внесённых изменений, информацию о произошедшем событии будем дублировать в jabber. Далее скрипт помещается в cron и выполняется ежеминутно.
Поскольку скрипт будет работать по запросу, а не как демон, для определения того, что статус подключения изменился, необходимо иметь информацию о том, каким он был в момент предыдущего запуска. Если этого не сделать, наш скрипт будет DOS'ить мегафоновский сервер, отправляя ему запрос на установку GSM-режима каждый раз во время проверки, что плохо. Поэтому при первом вызове скрипт поместит информацию о текущем статусе ($peer_state_now) во внешний файл ($peer_state_last_file), а все последующие — будет читать информацию из этого файла ($peer_state_last) и сверяться с текущим статусом.
Для отправки http-запроса использовался curl, для уведомления в жаббер — sendxmpp ($xmpp_bin и $xmpp_jid). Перед использованием скрипта необходимо задать логин-пароль, соответствующий регистрационной записи Мультифон-SIP ($multifon_login и $multifon_password).
Осталось добавить запуск скрипта в /etc/crontab
Готово! Теперь можно проверить работоспособность всей системы. Подключаемся телефоном к серверу и через минуту получаем в jabber подтверждающее сообщение. Делаем тестовый звонок на телефон и убеждаемся в том, что все работает так, как и было задумано.
Мультифон: формат http-запроса, ответы сервера и обработка кодов
Вкратце о предыстории вопроса
Являясь абонентом Мегафона, с недавнего времени стал пользоваться их VoIP-сервисом. Одной из особенностей сервиса является то, что кроме совершения исходящих, можно принимать входящие на мобильный номер звонки через SIP-клиент. Этой возможностью мне и захотелось воспользоваться, настроив подключение своего смартфона к домашнему серверу Asterisk через SIP. На самой процедуре регистрации, подключении к услуге и настройке SIP-аккаунта, я останавливаться не буду — про это уже достаточно написано. Хотелось сделать следующее: при подключении смартфона к серверу, все входящие сотовые звонки начинают поступать через SIP; при отключении от сервера должен восстанавливаться обычный режим приёма звонков телефоном (gsm-to-gsm). Решение должно быть максимально автоматизированным и требовать от меня не более одного движения — нажатия на кнопку подключения к серверу на рабочем столе смартфона.
Что необходимо для выполнения задачи
Для реализации задуманного необходимо выполнение как минимум двух условий:
1. «Мультифон» должен предоставлять возможность переключения режима приёма сотовых звонков: приём на сотовый, приём в sip-клиент. Такая возможность у сервиса есть, и реализована она через отправку определённого https-запроса на сервер (подробнее о формате запроса можно прочитать перейдя по ссылке, указанной в конце статьи). Запрос можно выполнить из консоли (например при помощи curl или wget), что делает возможным его использование в скриптах.
2. Сервер должен «знать», что на Asterisk'e зарегистрировался определённый peer (смартфон), и при подключении/отключении выполнить https-запрос. Подробнее рассмотрим это вопрос ниже.
В поисках решения
Необходимо определить момент, когда смартфон зарегистрируется на сервере. Изучив реакцию Asterisk'a на подключение клиента и доступные средства информирования о произошедшем событии, определил для себя 2 основных варианта решения: узнать о событии через AMI (Asterisk manager API) или обрабатывать через внешний скрипт список подключенных клиентов, которые Asterisk выводит по команде «sip show peers». Мне был ближе второй вариант, поэтому я решил остановиться на нём. (Кстати, сейчас подумал о том, что есть еще третий способ — парсить вывод файла /var/log/asterisk/full на предмет наличия строк «Registered SIP 'peername'» и «Unregistered SIP 'peername'». Возможно, так было бы даже проще, но я пошел по другому пути. Если вы знаете более простые варианты, расскажите, я их с удовольствием выслушаю).
Посмотрим, что пишет сервер:
asterisk*CLI> sip show peers
Name/username Host Dyn Nat ACL Port Status
peer1/peer1 192.168.XXX.XXX D 5060 Unmonitored
peer2/peer2 (Unspecified) D 5060 Unmonitored
peer3/peer3 192.168.XXX.XXX D 5060 Unmonitored
multifon/7922XXXXXXX 193.201.229.35 5060 OK (34 ms)
sipnet/sipnet 212.53.40.40 5060 Unmonitored
mywifipeer/mywifipeer (Unspecified) D 0 Unmonitored
6 sip peers [Monitored: 1 online, 0 offline Unmonitored: 4 online, 1 offline]
В колонке «Host» у отключенных клиентов видим надпись (Unspecified), а у зарегистрированных — IP-адрес. Этих данных достаточно для того, чтобы определить состояние регистрации у клиента. Таким образом задача свелась к тому, чтобы написать скрипт, который будет опрашивать на сервере состояние необходимого sip-аккаунта (смартфон — mywifipeer), и если состояние его регистрации изменилось, отправлять https-запрос. Для контроля внесённых изменений, информацию о произошедшем событии будем дублировать в jabber. Далее скрипт помещается в cron и выполняется ежеминутно.
Поскольку скрипт будет работать по запросу, а не как демон, для определения того, что статус подключения изменился, необходимо иметь информацию о том, каким он был в момент предыдущего запуска. Если этого не сделать, наш скрипт будет DOS'ить мегафоновский сервер, отправляя ему запрос на установку GSM-режима каждый раз во время проверки, что плохо. Поэтому при первом вызове скрипт поместит информацию о текущем статусе ($peer_state_now) во внешний файл ($peer_state_last_file), а все последующие — будет читать информацию из этого файла ($peer_state_last) и сверяться с текущим статусом.
Для отправки http-запроса использовался curl, для уведомления в жаббер — sendxmpp ($xmpp_bin и $xmpp_jid). Перед использованием скрипта необходимо задать логин-пароль, соответствующий регистрационной записи Мультифон-SIP ($multifon_login и $multifon_password).
Скрипт asterisk_peer_check
#!/bin/bash
#
# press F1 for help
if [ -z "$1" ]; then
echo "Usage: $0 <peer_name>"
exit 1
fi
# determining the future...
peer=$1
path="/var/spool/asterisk/tmp"
peer_state_last_file="$path/peer_state_$peer"
# Multifon account
multifon_login="7922XXXXXXX@multifon.ru"
multifon_password="mypassword"
# Jabber account for report
xmpp_jid="change_me@jabberserver.ru"
xmpp_bin="sendxmpp"
#getting actual peer state from asterisk
peer_state_now=`asterisk -rx "sip show peers" | grep -i $peer | awk '{print $2}'`;
#getting previous peer state from file
if [ -f "$peer_state_last_file" ];
then
peer_state_last=`cat $peer_state_last_file`
else
#first run
peer_state_last=$peer_state_now
fi
#comparing actual peer status with previous
if [ "$peer_state_now" != "$peer_state_last" ]
then
#peer status changed
if [ "$peer_state_now" = "(Unspecified)" ]
then
#GSM-2-GSM
multifon_routing="0"
else
#GSM-2-SIP
multifon_routing="1"
fi
#changing multifon status
multifon_url="https://sm.megafon.ru/sm/client/routing/set?login=$multifon_login&password=$multifon_password&routing=$multifon_routing"
curl --silent $multifon_url >/dev/null
#jabber announce, if sendxmpp installed
which $xmpp_bin >/dev/null || [ $? -eq 0 ] && echo "[`hostname`] Asterisk: Megafon incoming calls set to $multifon_routing" | $xmpp_bin -i $xmpp_jid
fi
#writing actual peer state in file
echo $peer_state_now > $peer_state_last_file
#
Осталось добавить запуск скрипта в /etc/crontab
#
* * * * * root /etc/cron/asterisk_peer_check mywifipeer
#
Готово! Теперь можно проверить работоспособность всей системы. Подключаемся телефоном к серверу и через минуту получаем в jabber подтверждающее сообщение. Делаем тестовый звонок на телефон и убеждаемся в том, что все работает так, как и было задумано.
Ссылки:
Мультифон: формат http-запроса, ответы сервера и обработка кодов