среда, 13 мая 2015 г.

Управление zrtp

ZRTP ("Z" is a reference to its inventor, Zimmermann; "RTP" stands for Real-time Transport Protocol)[2] is described in the Internet Draft as a "key agreement protocol which performs Diffie–Hellman key exchange during call setup in-band in the Real-time Transport Protocol (RTP) media stream which has been established using some other signaling protocol such as Session Initiation Protocol (SIP). This generates a shared secret which is then used to generate keys and salt for a Secure RTP (SRTP) session." One of ZRTP's features is that it does not rely on SIP signaling for the key management, or on any servers at all. It supports opportunistic encryption by auto-sensing if the other VoIP client supports ZRTP.
http://en.wikipedia.org/wiki/ZRTP

ZRTP is SRTP based, but instead of using SIP to exchange keys, it exchanges keys within the media stream itself making it more secure. It does not require PKI.
https://wiki.freeswitch.org/wiki/ZRTP

Включение: в switch.conf:
<param name="rtp-enable-zrtp" value="true"/>

Есть 3 режима работы:

  1. trusted MITM, когда устанавливается 2 сессии с сервером, с транскодингом, 
  2. простой проброс rtp - media relay, и 
  3. прямой (direct, peer-to-peer) режим, когда rtp через сервер вообще не ходит.

Про первый режим
The PBX can operate as a trusted man-in-the-middle, terminating the media streams for both parties at the PBX. This also terminates the ZRTP encryption for both parties at the PBX. This is mathematically equivalent to a classic man-in-the-middle attack, but it's not really an attack if the clients trust the PBX and consent to this. ZRTP has a mechanism to allow a client to recognize a trusted PBX to act as a "friendly" man-in-the-middle. This allows conference mixing, transcoding, and lawful interception of plaintext media, all within the confines of the trusted PBX.
http://zfoneproject.com/faq.html
ну и в целом дока интересная и полезная.

Включить первый режим:
<action application="set" data="zrtp_enrollment=true"/>

второй режим (проксирование):
<param name="inbound-zrtp-passthru" value="true"/>

плюс
https://wiki.freeswitch.org/wiki/Proxy_Media

И напоследок дока с картинками
http://zfone.com/docs/asterisk/man/html/u_guide.html

четверг, 23 апреля 2015 г.

180, 183, early_media

Early media это информация, которая ходит только в 1 сторону, оно же обычно pre answer. Если медиа нет - телефон сам генерирует гудки, иначе передает то, что прилетает. А это может быть "абонент временно недоступен", просто какая-то информация...

Обычно при установке соединения идут сообщения тип 100 (trying), потом 180 ringing, без медиа, потом 183 session progress, с медиа. Но в рф сотовые и тут выпендрились и медиа у них уже в 180 летит, что несколько ломает поведение фрисвича, хотя по RFC это и допустимо.


Работа с хуками и стартап скриптами

Пример стартап скрипта
con = freeswitch.EventConsumer("CUSTOM","vm::maintenance")
for e in (function() return con:pop(1) end) do
        freeswitch.consoleLog("notice","event\n" .. e:serialize("xml") .. "\n")
        element = nil
        element = e:getHeader("VM-Action")
        if element then
                --
        end
end

Пример хук скрипта
freeswitch.consoleLog("notice","RECORD_STOP\n" .. event:serialize(""))
path = nil
path = event:getHeader("Record-File-Path")
итд.
То есть у нас изначально есть массив event, где лежат все значения ивента. Но при этом недоступны канальные переменные, только глобальные. Можно добавить свои поля в ивент через event:addHeader("API-FileSize", size), и даже сформировать новый ивент (читаем доку), но просто модификация полей не перенесет новые переменные в сессию.

Можно создать привязку к api так:
api = freeswitch.API()
и дальше делать запросы типа
req = urlencode(event:serialize("json"))
data = api:executeString("curl "..  url .. " content-type application/json post " .. req)
Опять же, сессионные переменные будут недоступны, то есть можно сделать eval на строку вида
path =  "$${base_dir}/recordings/archive/${strftime(%Y-%m-%d-%H-%M-%S)}_${caller_id_number}_${destination_number}.wav"
но ${caller_id_number}_${destination_number} будут пустые и на выходе останется только "_". Нужно подобрать подходящие поля в event. Что-то можно заранее в ивент (и сип заголовки) выставить через application=set data="sip_h_X-Caller=12345", появится поле X-Caller.

Есть ещё метод вызова, через lua или luarun, тогда доступны сессионные переменные через массив session, самый простой пример
session:execute("playback", mySound)
в отличии от api:execute, выполняется в контексте текущей сессии, где доступны все переменные.

Также нужно быть предельно аккуратными с freeswitch.EventConsumer -- если такая конструкция появится в хуке на ивент, фрисвич начнёт падать. Точно актуально для 1.4.15, и на момент выхода 1.4.18 никто даже не пытался понять причин. И что самое противное -- падать будет не сразу, а спустя N дней, и при этом КАК БЫ работать. То есть поступает звонок, в логе есть инвайт, есть ответ на него... и всё. ringing уже не будет. Но cancel примет и обработает!

Ну и помним, что стартап скрипты перечитываются ТОЛЬКО при рестарте всего фрисвича целиком! Увы, просто рестартовать модуль lua нельзя. А вот хуки - читаются при каждом обращении к ним, поэтому для повышения скорости их лучше помещать в tmpfs/shm

линки
https://freeswitch.org/confluence/display/FREESWITCH/mod_lua
https://freeswitch.org/confluence/display/FREESWITCH/Lua+API+Reference

среда, 22 апреля 2015 г.

установка таймаутов

Переменные, которые управляют таймаутами:
timeout
leg_timeout
call_timeout (deprecated?)
originate_timeout
progress_timeout

bridge_answer_timeout - таймаут, когда имеется early_media. Имеет баг - в отличии от других таймаутов, через set дропает ногу А с ALLOTTED_TIMEOUT. Ставить или на ногу Б через [], {}, или export

Почти ко всем таймаутам есть предупреждение
WARNING: Beware that if you are not using {ignore_early_media=true} (...) is no longer applicable as soon as early media signal is received.

Ну и несколько специфичных
conference_auto_outcall_timeout
park_timeout
leg_progress_timeout

rtp_hold_timeout_sec
rtp_timeout_sec
SOFIA_SESSION_TIMEOUT

почти вся информация есть тут

про early_media надо сказать отдельно, будет дальше.

генерация тонов

Есть для этого спец модуль tone_stream, общий формат
tone_stream://[L=x;][v=y;]%(<on-duration>, <off-duration>, <freq-1> [, freq-2] [, freq-3] [, freq-n] [;loops=x])

Пример использования
<action application="playback" data="tone_stream://L=100;%(100,100,350,440)" />

Примеры

Непрерывный тон
tone_stream://%(10000,0,350,440)

Звонок формата сша, сам тон в константе
tone_stream://$${us-ring}
оно же
tone_stream://%(2000,4000,440,480)
также интересны ru-ring, uk-ring

опционально ;loops=-1 или {loops=-1}tone...

сложный тон (2 тона)
tone_stream://%(275,10,600);%(275,100,300)

бывает даже так
tone_stream://path=${conf_dir}/tetris.ttml

Более подробное описание и совсем сложные примеры:
https://wiki.freeswitch.org/wiki/TGML

lua json библиотеки

список библиотек
http://lua-users.org/wiki/JsonModules

Также есть pure lua, которая просто копируется в проект
http://regex.info/blog/lua/json
Особенность загрузки - через
JSON = (loadfile "JSON.lua")()
Если будет проблема с загрузкой - тут лучше указать полный путь. Через require не грузится.
Модуль достаточно медленный, но если надо быстро распарсить результат curl - скорости достаточно.

Сравнение производительности
http://www.kyne.com.au/~mark/software/lua-json-performance.html
http://lua-users.org/wiki/JsonModules
наиболее интересным выглядит mp-CJSON / Lua CJSON

понедельник, 20 апреля 2015 г.

lua, execute и curl - получить код ответа

1)
session:execute("curl", some_url)
Код будет в curl_response_code

2)
local response = api:execute("curl", url .. " json timeout 2 get ")
resp = json.decode(response)

if  resp.status_code ~= 200 then ...