company_banner

Asterisk: обратный вызов с использованием AMI

    Asterisk Manager Interface (AMI) – это программный интерфейс который позволяет внешним программам как управлять, так и контролировать систему Asterisk. AMI слушает соединения на TCP порту, по-умолчанию это 5038. Клиентская программа может соединяться с AMI, передавать команды в Asterisk, получать ответ о статусе выполнения команд.
    В данном посте мы рассмотрим использование AMI на примере решения определенной задачи: настроить Asterisk на генерацию звонков по заданному url, в котором должны устанавливаться параметры звонка.

    Настройка Asterisk AMI



    Первое что необходимо сделать, это включить AMI и завести пользователя с помощью которого клиентская программа будет аутентифицироваться:

    /etc/asterisk/manager.conf
    [general]
    enabled = yes
    port = 5038
    bindaddr = 0.0.0.0
    


    /etc/asterisk/manager.conf
    [c2call]
    secret=FrUyHn6FSaX
    deny=0.0.0.0/0.0.0.0
    permit=192.168.0.0/255.255.0.0
    read=system,call,log,verbose,command,agent,user,config,command,dtmf,reporting,cdr,dialplan,originate
    write=system,call,log,verbose,command,agent,user,config,command,dtmf,reporting,cdr,dialplan,originate
    


    Для применения изменений выполним reload:

    asterisk -rx "module reload manager"
    


    Теперь нам потребуется создать внутренний номер который, собственно, и будем соединять с вызываемым абонентом:

    /etc/asterisk/sip.conf
    [3200]
    deny=0.0.0.0/0.0.0.0
    permit=192.168.0.0/255.255.0.0
    secret=3200
    dtmfmode=rfc2833
    canreinvite=no
    context=OUT_IN1
    host=dynamic
    type=friend
    nat=yes
    port=5060
    qualify=yes
    callcounter=yes
    faxdetect=no
    


    После создания внутреннего номера, потребуется перечитать конфигурацию Asterisk:
    asterisk -rx "sip reload"
    


    На этом настройка Asterisk завершена.



    Скрипт обратного вызова



    Теперь переходим к созданию скрипта на PHP:

    callback.php
    <?php
    
    # --- define globals ---
    $strhost = "192.168.0.10";
    $strport = "5038";
    $timeout = "10";
    $num=$_REQUEST['num'];
    $cid=$_REQUEST['cid'];
    $c=$_REQUEST['c'];
    $p=$_REQUEST['p'];
    
    $errno=0 ;
    $errstr=0 ;
    
    $sconn = fsockopen ($strhost, $strport, &$errno, &$errstr, $timeout) or die("Connection to $strhost:$strport failed");
    
    if (!$sconn) { echo "$errstr ($errno)<br>\n"; } 
    
      else {
    
      fputs ($sconn, "Action: login\r\n");
      fputs ($sconn, "Username: c2call\r\n");
      fputs ($sconn, "Secret: FrUyHn6FSaX\r\n");
      fputs ($sconn, "Events: off\r\n\r\n");
      usleep(500);
    
      fputs ($sconn, "Action: Originate\r\n");
      fputs ($sconn, "Channel: SIP/$cid\r\n");
      fputs ($sconn, "Callerid: $cid\r\n");
      fputs ($sconn, "Timeout: 15000\r\n");
      fputs ($sconn, "Context: $c\r\n");
      fputs ($sconn, "Exten: $num\r\n");
      fputs ($sconn, "Priority: $p\r\n\r\n");
      fputs ($sconn, "Async: yes\r\n\r\n" );
      fputs ($sconn, "Action: Logoff\r\n\r\n");
      usleep (500);
    
      fclose ($sconn);
    
      } 
    
    ?>
    


    Теперь можно проверить генерацию вызова, используя следующий URL:
    http://domain.com/callback.php?p=1&c=OUT_EXT2&cid=3200&num=84951234567
    


    Результат мы видим в консоли:

    *CLI>   == Manager 'c2call' logged on from 192.168.0.11
            == Using SIP RTP CoS mark 5
             -- Executing [84951234567@OUT_EXT2:1] Dial("SIP/3200-0000000a", "SIP/84951234567@TRK1") in new stack
           == Manager 'c2call' logged off from 192.168.0.11
           == Using SIP RTP CoS mark 5
             -- Called SIP/84951234567@TRK1
             -- SIP/TRK1-0000000b is ringing
           == Spawn extension (OUT_EXT2, 84951234567, 1) exited non-zero on 'SIP/3200-0000000a'
    


    Приведу некоторые комментарии, которые, возможно, будут полезны практикующим:

    \r\n — возврат каретки с переводом строки (Carriage Return + Line Feed (CR+LF)). Обычно передается в результате нажатия клавиши Enter и обозначает завершение передачи команды.

    Events: off
    В данном случае мы отключаем отправку событий на это соединение интерфейса AMI. В основном его значение всегда off

    Channel: SIP/$cid
    Имя канала которому адресован вызов. В нашем случае вызов сначала поступит абоненту SIP/3200, как только он возьмет трубку, вызов бедет перенаправлен на номер 84951234567.

    Callerid: $cid
    Идентификатор вызывающего абонента, который должен быть задан для исходящего канала.

    Timeout: 15000
    Указываем время ожидания ответа на звонок — 15 секунд

    Async: yes
    Асинхронное формирование вызовов позволяет создавать один или более вызовов, не ожидая немедленного ответа.
    • +5
    • 44,8k
    • 9
    Southbridge 131,98
    Обеспечиваем стабильную работу серверов
    Поделиться публикацией
    Комментарии 9
    • +1
      Штатные средства Asterisk — это хорошо.
      Но! Есть целый ряд недочетов.

      1. Зачем такие разрешения для Originate? Я про /etc/asterisk/manager.conf и write=system,call,log,verbose,command,agent,user,config,command,dtmf,reporting,cdr,dialplan,originate. Разрешение system в случае взлома web сайта позволит выполнять шел скрипты из под id asterisk, а если админ телефонии допустил такой косячище, как запуск asterisk из-под рута, то атакующий получит рута, просто поломав php сайт.

      2. Давать разработчику web сайта телнет на свой астериск просто небезопасно :-) Плюс учить его командам AMI. Проще и чуть более правильно дать разработчику сайта AJAM — wiki.asterisk.org/wiki/display/AST/Asynchronous+Javascript+Asterisk+Manger+%28AJAM%29

      3. А самое правильное — это дать доступ через AMI прокси — www.voip-info.org/wiki/view/Asterisk+Manager+Proxy
      • 0
        А еще правильнее писать свой прокси (на перле/питоне например, это ну вообще не сложно) над прокси астериска и уже его давать под сайт :)
      • +1
        Есть готовые библиотеки для работы с AMI, лучше использовать их, чем, в частности, полагаться на то, что AMI отреагирует на логин за 500 мкс :)

        Да и вообще описанный функционал проще всего реализовать с помощью call-файлов
        • 0
          Колл-файлы генерить надо… А еще правильную дату создания им писать. Да и срабатывать колбэк будет не мгновенно в этом случае. А еще надо их ложить туда куда апач должен доступ иметь — а это чревато (конечно если именно апач используется) :)
          • 0
            Колл-файл генерируется из шаблона одной регуляркой, а уж в плане безопасности доступ к AMI куда сомнительней ;)
            • 0
              Кстати, коллбэк срабатывает мгновенно, как только * видит файл в папке с ctime меньше текущего времени. Настолько мгновенно, что надо создавать файл не сразу в спуле *, а в другом месте и только после этого переносить файл в спул.
              • 0
                Я, честно говоря уже не помню всех мелочей, но…

                Имеем одну линию.
                Идет запрос на перезвон, создается колл-файл, сразу перезвон.
                Идет второй запрос на перезвон (первый звонок не закончен), создаем колл-файл, и тут уже многое зависит от настроек астериска.
                А если у нас создается 10 запросов и нас волнует приоритетность их выполнения — мы оказываемся в полной, извините, жопе :)
                Собственно потому я предпочитал рулить пулом колбэков из под самописного демона на питоне который с менеджером работал.
          • 0
            А не подскажете, как при этом ещё отослать DTMF тому, кому звоним?
            • 0
              ARI дает контроль над всеми каналами.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое