Mateusz Mazurek – programista z pasją

Python, architektura, ciekawostki ze świata IT

Linux Programowanie

Własna centrala telefoniczna – VoIP

Cześć,
dziś pobawimy się we własną centralę telefoniczną. Piszę „pobawimy”, bo samo zagadnienie jest ogromnie złożone i pokażę tylko jak wykonać proste czynności, typu zadzwonić w ramach lokalnej sieci do siebie, stworzyć jakąś obsługę tych połączeń, czy jak to wygląda, jeśli chcemy zadzwonić na prawdziwe numery.

Potrzebny jest nam serwer – raczej szybszy niż wolniejszy i publiczny adres IP. Na tym serwerze będzie sobie siedzieć aplikacja która jest właśnie centralą telefoniczną – Asterisk, do tego dołożymy bazę danych Postgres i ewentualnie napiszemy proste skrypty w Pythonie/PHPie/czym tam sobie chcesz ;)

No dobra… Zacznijmy od tego, że na potrzeby tego wpisu przeinstalowałem swój VPS na CentOSa 7 64bit. Zaczynamy czysto, co znaczy że część zależności już możesz mieć zainstalowanych przy okazji instalacji innych programów.

Zaczniemy od instalacji Asteriska. A dokładniej od kompilacji Asteriska 13.13.1 :)

wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-13-current.tar.gz
tar -zxvf asterisk-13-current.tar.gz
cd asterisk-13.13.1/

Pobieramy i rozpakowujemy źródła wersji 13.13.1 – nie jest to najnowsza wersja, ale jedna z nowszych :)

Instalujemy zależności:

yum upgrade
yum install -y epel-release dmidecode gcc-c++ ncurses-devel libxml2-devel make wget openssl-devel newt-devel kernel-devel sqlite-devel libuuid-devel gtk2-devel jansson-devel binutils-devel
yum -y install patch

No i uruchamiamy konfigurację:

./configure --with-pjproject-bundled

Ten przełącznik jest dostępny w nowszych wersjach Asteriska i pozwala dociągnąć automatycznie bibliotekę zależną PjProject w której skład wchodzi PjSip, o którym nieco później.

Gdy to się skończy kompilujemy projekt:

make

Tutaj mogą pojawić się błędy, chociaż zainstalowanie wszystkich zależności według instrukcji powinno zabezpieczyć przed taką sytuacją. Więc jeśli się kłopoty się pojawią – google’uj.
Instalujemy skompilowane paczki:

 make install

i tworzymy przykładowe pliki konfiguracyjne:

make samples

Na chwilkę się zatrzymamy z Asteriskiem w tym momencie. Nim przejdziemy dalej skonfigurujmy jeszcze bazę danych – Postgresa.

Instalujemy:

yum install postgresql-server

U mnie w repo znajdowała się wersja 9.2. Każda wersja 9.x powinna bez problemu działać.
Inicjalizujemy bazę:

postgresql-setup initdb

Uruchamiany:

service postgresql start

I przelogowywujemy się na użytkownika postgres:

sudo su postgres

Teraz tworzymy super użytkownika o loginie „matt”:

 createuser -P -s -e matt

i dajemy mu hasło „test” :)

Będąc cały czas na użytkowniku postgres uruchamiany aplikację psql (interaktywny dostęp do bazy) i tworzymy bazę danych o nazwie asterisk:

create database asterisk; 

Ok, więc bazę mamy. :) Wracając do Asteriska – czas, aby wspomnieć jak on działa. Otóż wykorzystuje on protokół SIP, aby nawiązać sesję między dwoma klientami (zainicjować rozmowę). Natomiast same media (dźwięk) lecą protokołem RTP. Wspominam o tym, gdyż Asterisk posiada dwa sterowniki obsługi SIPa – chan_sip i pj_sip. Są to wymienne (do pewnego stopnia) moduły, które odpowiadają za rozmowę z dialekcie SIPowym z klientami.

chan_sip jest starą biblioteką, pisaną jeszcze w czasach, gdy 100 jednoczesnych połączeń VoIPowych to max jaki można było osiągnąć. Natomiast PjSip jest znacznie nowszym sterownikiem, która już bierze poprawkę na temat wydajności i jest aktywnie rozwijany.

A że mamy 2017 rok to użyjemy PjSipa ;)

Kilka linijek wyżej stworzyliśmy w folderze /etc/asterisk przykładowe pliki konfiguracyjne. W tym folderze będziemy musieli dokonać kilku zmian :)

Plik pjsip.conf:
Dodajemy do tego pliku definicję transportu:

[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0

Plik modules.conf:
Dodajemy moduły które chcemy ładować:

noload => chan_sip.so
load => res_pjsip.so
noload => res_pjsip_pubsub.so
load => res_pjsip_session.so
load => chan_pjsip.so
load => res_pjsip_exten_state.so
load = res_pjsip_log_forwarder.so

Teraz należy jeszcze raz wrócić do katalogu ze ściągniętymi źródłami Asteriska i w folderze asterisk-13.13.1/contrib/ast-db-manage znajdziemy skrypt, który stworzy nam schemat bazy danych.

Będąc w tym folderze tworzymy plik konfiguracyjny kopiując plik przykładowy:

cp config.ini.sample config.ini

i dopisujemy w nim:

sqlalchemy.url = postgresql://matt:test@localhost/asterisk

zakomentowując przy tym wszystkie podobnie wyglądające linijki. Wypełniamy tym samym config danymi do bazy danych.

Schemat jest tworzony biblioteką pythonową – alembic. Aby ją zainstalować potrzebujemy systemu paczek – pip. Zróbmy to komendą:

wget https://bootstrap.pypa.io/get-pip.py
python get-pip.py

Następnie instalujemy alembic’a:

 pip install alembic 

Iiii dostajemy błąd :) Brak psychopg2 – więc instalujemy pierw zależności do niego:

sudo yum install postgresql-libs
sudo yum install postgresql-devel
sudo yum install python-devel

I następnie

 pip install psycopg2

Co powoduje iż komenda

 pip install alembic 

daje teraz nam inny błąd :P tym razem alembic (a w sumie to SqlAlchemy – orm na bazie którego jest on zbudowany) nie może połączyć się z postgresem ;) i ma rację! Nie dokonfigurowaliśmy go :P

W celu poprawienia błędu musimy przeedytować plik

nano /var/lib/pgsql/data/pg_hba.conf 

przechodzimy na sam dół i zmieniamy wszystkie „ident” na „md5” pozwalając tym samym na autoryzację hasłem.

Po tym zabiegu i restarcie postgresa możemy wykonać skrypt alembic’a komendą:

 alembic -c config.ini upgrade head

co stworzy nam schemat bazy danych.

Musimy jeszcze wrócić do Asteriska i powiedzieć mu, że wszystkich użytkowników naszej centrali będziemy trzymać w bazie postgresa.

Należy do pliku /etc/asterisk/extconfig.conf dokleić taką treść:

ps_endpoints => pgsql,asterisk
ps_auths => pgsql,asterisk
ps_aors => pgsql,asterisk
ps_domain_aliases => pgsql,asterisk
ps_endpoint_id_ips => pgsql,asterisk
ps_contacts => pgsql,asterisk

I możemy uruchomić Asteriska komendą:

 asterisk

i komendą

 asterisk -r

podłączamy się do zdalnej konsoli.

Oficjalnie zakończyliśmy przygotowywanie sobie środowiska do pracy. Teraz więc pobawmy się tym.

Zacznijmy sobie od stworzenia numerów na które będzie można zarejestrować się później. Przechodzimy więc do konsoli postgresa:

psql -U matt -d asterisk

i podajemy hasło „test”.

I wykonujemy tam takie INSERTy:

insert into ps_aors (id, max_contacts) values (22444444, 1);
insert into ps_aors (id, max_contacts) values (22555555, 1);
insert into ps_auths (id, auth_type, password, username) values (22444444, 'userpass', 1, 22444444);
insert into ps_auths (id, auth_type, password, username) values (22555555, 'userpass', 2, 22555555);
insert into ps_endpoints (id, transport, aors, auth, context, disallow, allow, direct_media) values (22444444, 'transport-udp', '22444444', '22444444', 'testing', 'all', 'ulaw,alaw', 'no');
insert into ps_endpoints (id, transport, aors, auth, context, disallow, allow, direct_media) values (22555555, 'transport-udp', '22555555', '22555555', 'testing', 'all', 'ulaw,alaw', 'no');

Co spowoduje dodanie nam dwóch numerów telefonów:
22444444
22555555

W TYM WPISIE OPISUJĘ DZIAŁANIE PRAWDZIWEJ CENTRALI VOIP. ZE WZGLĘDÓW WIZERUNKOWYCH WYBRAŁEM NUMERY KTÓRE NIE MOGĄ ISTNIEĆ, ALE SPOKOJNIE MOŻESZ UŻYWAĆ PRAWDZIWYCH NUMERÓW – A W PRZYPADKU SPIĘCIA SIĘ Z INNYM OPERATOREM – DOSTAĆ NUMER UNIKALNY I WYJŚĆ Z ROZMOWAMI POZA TEGO ASTERISKA.

Aby to mogło działać należy jeszcze wykonać takiego update’a:

update ps_endpoints set ice_support='yes', rtp_symmetric='yes';

co zapewni nam poprawne działanie gdy jedna ze stron jest za NATem.

Nie wiem jak Wasze systemy, ale mój miał domyślnie włączone iptables i poblokowane porty, więc jeszcze dodam info jak je odblokować:

 touch /etc/iptables.firewall.rules

Takim poleceniem tworzymy pusty plik. I otwierając go dowolnym edytorem wklejamy tam taką zawartość:

*filter

# Allow all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT -d 127.0.0.0/8 -j REJECT

# Accept all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow all outbound traffic - you can modify this to only allow certain traffic
-A OUTPUT -j ACCEPT

# Allow SSH connections
#
# The -dport number should be the same port number you set in sshd_config, ie 8050
#
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT

# SIP on UDP port 5060, 5061 for secure signaling. Used for signals such as "hang up"
-A INPUT -p udp -m udp --dport 5060 -j ACCEPT
-A INPUT -p udp -m udp --dport 5061 -j ACCEPT

# IAX2- the IAX protocol - comment out if you don't plan to use IAX
# -A INPUT -p udp -m udp --dport 4569 -j ACCEPT

# IAX - old IAX protocol, uncomment if needed for legacy systems.
# -A INPUT -p udp -m udp --dport 5036 -j ACCEPT

# RTP - the media stream - you can change this in /etc/asterisk/rtp.conf
-A INPUT -p udp -m udp --dport 10000:20000 -j ACCEPT

# MGCP - if you use media gateway control protocol in your configuration
-A INPUT -p udp -m udp --dport 2727 -j ACCEPT

# Uncomment these lines if you plan to use FreePBX to manage Asterisk
# -A INPUT -p tcp --dport 80 -j ACCEPT
# -A INPUT -p tcp --dport 443 -j ACCEPT

# Allow ping
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT

# Log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7

# Drop all other inbound - default deny unless explicitly allowed policy
-A INPUT -j DROP
-A FORWARD -j DROP

COMMIT

co pozwoli nam dostać się naszego Asteriska z zewnątrz. Zapisujemy te ustawienia:

 sudo iptables-restore < /etc/iptables.firewall.rules

A więc mamy centralę i dwa numery. Fajnie by było ich użyć ;)

Przy telefonie tradycyjnym sytuacja wyglądała tak, że operator przypisywał Ci jakiś numer. Tutaj jest podobnie, tylko że operatorem jesteśmy my. Musimy mieć jeszcze telefon. Oczywiście może to być fizyczne urządzenie (tak zwany hardphone), jak np. Yealink:

I wtedy taki telefon ma swój panel admina gdzie możesz go skonfigurować, podłączyć kablem do internetu i dzwonić :P

Ale dla naszych potrzeb skorzystamy z programowego telefonu (softphone), czyli aplikacji, która udaje telefon. Troszeczkę jak Skype. Od siebie proponuję program Zoiper. Instalacja jest prosta. Po uruchomieniu wygląda tak:

Teraz musimy na niego zarejestrować nasz numer :)

Z menu Settings wybieramy element Create a new account i dostajemy taki efekt:

Gdzie wybieramy SIP :)

I następnie program prosi nas o login, hasło i adres naszej centrali. Nasze numer, przypomnę:
22444444
22555555

mają hasła, odpowiednio, „1” i „2” ;)

Więc podajemy je tam. User@Host to będzie numer@adresIPServera. I to samo robimy dla drugiego numeru ;)

Gratuluję! Masz centralę i dwa zarejestrowane numery.

Teraz musimy się troszkę cofnąć . Jak tworzyłem INSERTami te numery, to dla kolumny „context” wpisałem wartość „testing”. Jest to bardzo ważne – kolumna ta określa od którego kontekstu zaczyna się przetwarzanie dialplanu. Co to jest dialplan? Jest to jakby cała ścieżka jaką przechodzi połączenie wychodzące z naszego numeru.  Jest to relatywnie proste do osiągnięcia – wystarczy stworzyć właśnie ten dialplan.

Aby to zrobić otwieramy plik /etc/asterisk/extensions.conf

i na końcu dopisujemy:

[testing]
exten => _22.,1,NoOp()
same => n,Dial(PJSIP/${EXTEN})

Oznacza to, że połączenie wychodzące z naszego numeru wpada na ten kontekst. I teraz w tym kontekście mamy exteny – w tym przypadku aż jeden exten. Pierwszy jego argument to pattern sprawdzający, czy numer na który dzwonimy pasuje do tego extenu. Jeśli pasuje, to wykonywane są dalsze instrukcje z słówkiem „same” aż do napotkania innej definicji extenu. Drugi argument dla extenu to priorytet a trzeci to instrukcja do wykonania. Dla definicji extenu zazwyczaj daje się „NoOp()” jako instrukcję. Pattern tutaj to „_22.” co można przetłumaczyć jako „numer zaczynający się do 22 i mający dalej dowolną ilość liczb, większą niż 0.” Czyli wg. tego możemy zadzwonić z 22444444 na 22555555 :) a przynajmniej tak wykonane połączenie wpadnie na ten exten, co spowoduje wykonanie linijki

same => n,Dial(PJSIP/${EXTEN})

w której korzystamy z funkcji „Dial” – co po polsku znaczy „zadzwoń” na numer podany po ukośniku. A w naszym przypadku po ukośniku mamy odwołanie do zmiennej ${EXTEN}, która przechowuje numer, na który dzwonimy. Tak prosty dialplan pozwala nam dzwonić.
Teraz jak zadzwonisz z numeru na naszej centrali na drugi numer to ten drugi numer zacznie dzwonić :)

Ogólnie fajnie, ale w prawdziwym życiu liczą się pieniądze. Więc co zrobić, żeby rozmowę można było jakoś rozliczyć? Odpowiedzią są CDRy (Call Detailed Records). CDRy zawierają informację o tym, kto do kogo dzwonił, ile rozmowa trwała itp.

Pierwszym krokiem do uruchomieniach ich jest stworzenie tabelki:

CREATE TABLE cdr (
calldate timestamp NOT NULL ,
clid varchar (80) NOT NULL ,
src varchar (80) NOT NULL ,
dst varchar (80) NOT NULL ,
dcontext varchar (80) NOT NULL ,
channel varchar (80) NOT NULL ,
dstchannel varchar (80) NOT NULL ,
lastapp varchar (80) NOT NULL ,
lastdata varchar (80) NOT NULL ,
duration int NOT NULL ,
billsec int NOT NULL ,
disposition varchar (45) NOT NULL ,
amaflags int NOT NULL ,
accountcode varchar (20) NOT NULL ,
uniqueid varchar (150) NOT NULL ,
userfield varchar (255) NOT NULL
);

Edytujemy też plik /etc/asterisk/cdr_pgsql.conf i tam w sekcji global wpisujemy dane dostępowe do bazy:

[global]
hostname=localhost
port=5432
dbname=asterisk
password=test
user=matt
appname=asterisk ; Postgres application_name support (optional). Whitespace not allowed.
table=cdr ;SQL table where CDRs will be inserted
;encoding=LATIN9 ; Encoding of logged characters in Asterisk
;timezone=UTC ; Uncomment if you want datetime fields in UTC/GMT

 

i restartujemy Asteriska:

asterisk -rx "core restart now"

Wykonajmy sobie połączenie z jednego numeru na drugi. Wtedy zobaczmy CDRy:

psql -U matt -d asterisk

i SQLka:

select * from cdr order by calldate;

No i wynik:

1
2
3
4
5
6
7
8
asterisk=# select * from cdr order by calldate;
calldate | clid | src | dst | dcontext | channel | dstchannel | lastapp | lastdata | duration |
billsec | disposition | amaflags | accountcode | uniqueid | userfield
---------------------+----------------+-----------+-----------+----------+--------------------------+--------------------------+---------+-----------------+----------+-
--------+-------------+----------+-------------+---------------+-----------
2017-01-29 12:52:09 | "" | 22444444 | 22555555 | testing | PJSIP/22444444-0000001c | PJSIP/22555555-0000001d | Dial | PJSIP/22555555 | 7 |
6 | ANSWERED | 3 | | 1485694329.63 |
(1 wiersz)

Mamy datę rozmowy, osobę dzwoniącą (kandydata do zapłacenia za rozmowę) i numer na który dzwoniła. Mamy też kolumnę duration, w której mamy info o długości rozmowy w sekundach – dokładnie całej długości rozmowy, czyli też czasu między „ringingiem”, a faktycznym odebraniem. Mamy też kolumnę billsec – czyli faktyczną długość rozmowy, która może być podstawą do wystawienia rachunku. Poza tym mamy tez nazwę kanału czy kontekstu – ogólnie sporo ważnych informacji.

No dobra – ale po co nam centrala która może tylko dzwonić? Zazwyczaj jednak chcemy dać więcej funkcjonalności. Dajmy na to np. kody specjalne. Zbudujmy prosty kod specjalny który jak się na niego zadzwoni to odtworzy jakąś zapowiedź i przekieruje po kilku sekundach na inny numer.

Wracamy więc do pliku extensions.conf i dopisujemy tam:

exten => 10*,1,NoOp()
same => n,Playback(hello)
same => n,Wait(3)
same => n,Dial(local/22444444@next/n,30,r)
same => n,Hangup()

Do naszego poprzedniego kontekstu i dodajemy nowy kontekst:

[next]
exten => _22.,1,NoOp()
same => n,Dial(PJSIP/${EXTEN})

Nasz nowy exten ma pattern „10*” co oznacza że jeśli z naszego numeru zadzwonimy na numer „10*” to wpadniemy na ten exten. Dalej mamy funkcję Playback(), która odtwarza plik dźwiękowy (zazwyczaj w formacie gsm (można na ten format konwertować MPtrójki) z folderu /var/lib/asterisk/sounds/en – jest tam ich sporo, więc można sobie pokombinować.

Dalej mamy funkcję Wait(sec) która podtrzymuje rozmowę na jakiś okres czasu, taki sleep. Po tym dzwonimy na numer 22444444 na kontekście next który wygląda dokładnie tak jak kontekst testing co oczywiście nie ma żadnego kompletnie sensu ale pokazuje że tzw local channel’em możemy przechodzić pomiędzy kontekstami.

Teraz gdy z numeru 22555555 zadzwonimy na 10* to pierw usłyszymy „hello”, potem chwilka przerwy i przekieruje nas na numer 22444444, który zacznie dzwonić. Zerknijmy na CDRy:

1
2
3
4
5
6
7
8
9
10
asterisk=# select * from cdr order by calldate;
calldate | clid | src | dst | dcontext | channel | dstchannel | lastapp | lastda
ta | duration | billsec | disposition | amaflags | accountcode | uniqueid | userfield
---------------------+----------------+-----------+-----------+----------+---------------------------------+---------------------------------+---------+----------------
-------------+----------+---------+-------------+----------+-------------+--------------+-----------
2017-01-29 13:51:58 | "" | 22555555 | 10* | testing | PJSIP/22555555-00000002 | Local/22444444@next-00000001;1 | Dial | local/22444444
@next/n,30,r | 18 | 18 | ANSWERED | 3 | | 1485697918.6 |
2017-01-29 13:52:02 | "" | 22555555 | 22444444 | next | Local/22444444@next-00000001;2 | PJSIP/22444444-00000003 | Dial | PJSIP/22444444
| 14 | 7 | ANSWERED | 3 | | 1485697922.8 |
(2 wiersze)

I mamy dwa wiersze – pierwszy odpowiada za połączenie z numeru 22555555 na numer 10*. Widzimy że połączenie zostało odebrane od razu przez Asteriska (duration-billsec = 0) i trwało 15 sekund. Tyle trwała cała rozmowa z 10*, a podczas niej był dial na inny kontekst, z czego mamy drugieg CDRa, który odpowiada właśnie za tego diala – i trwał on 7 sekund.

Z dialplanami można robić ogrom rzeczy – od transferów, przez nagrywanie rozmów czy chociażby łączenie dwóch rozmów w jedną – po więcej odsyłam do dokumentacji Asteriska.

W gruncie rzeczy możemy teraz zrobić już bardzo wiele – ale nadal o jedną rzecz za mało. :) Przydałoby się żeby podczas inicjowania połączenia sprawdzać, czy użytkownik ma możliwość wykonać tego połączenia – np czy ma wykupione środki w modelu prepaid czy chociażby umowa jest nadal aktualna czy cokolwiek innego – mam na myśli – potrzebujemy z dialplanu dostać się do bazy danych – i mamy do tego aplikację AGI.

Zmieńmy więc nasz dialplan na taki:

[testing]
exten => _22.,1,NoOp()
same => n,Dial(PJSIP/${EXTEN})

exten => 10*,1,NoOp()
same => n,Playback(hello)
same => n,Wait(3)
same => n,AGI(agiscript.php)
same => n,Dial(local/22444444@next/n,30,r)
same => n,Hangup()

[next]
exten => _22.,1,NoOp()
same => n,Dial(PJSIP/${EXTEN})

Nic się nie zmieniło poza jedną linijką:

same => n,AGI(agiscript.php)

która jest hiper istotna w tym wszystkim ponieważ uruchamia ona plik PHP’a do pisze wartości zmiennych na jego standardowe wejście – daje to w sumie nieograniczone możliwości! Gdyż z takiego skryptu jesteśmy w stanie sterować dialplanem – tzn że nasz skrypt może pisać do Asteriska komendy jakie ma wykonać. My napiszemy prosty skrypt logujący kto do kogo dzwonił. Oczywiście jest on mało przydatny gdy mamy CDRy, ale chce po prostu pokazać idę.

A więc w folderze /var/lib/asterisk/agi-bin tworzymy plik o nazwie agiscript.php i wklejamy tam taki kod:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/php

<?php
$in = fopen("php://stdin","r");

function read() {
    global $in;
    $input = str_replace("\n", "", fgets($in, 4096));
    return $input;
}

$_AGI = [];

while ($env=read()) {
    $s = split(": ",$env);
    $key = str_replace("agi_","",$s[0]);
    $value = trim($s[1]);
    $_AGI[$key] = $value;
    if (($env == "") || ($env == "\n")) {
        break;
    }
}

$logfile = realpath('/var/log/aa.log');

$log = $_AGI['callerid'] . ' -> ' . $_AGI['extension'] . "\n";

file_put_contents($logfile, $log, FILE_APPEND | LOCK_EX);

?>

Otwiera ten skrypt standardowe wejście do czytania i wczytuje z niego wszystko aż do spotkania pustej linii lub znaku nowej linii i zapisuje znalezione zmienne do tablicy $_AGI, której kluczem jest nazwa zmiennej bez przedrostka AGI, a wartość wartość owej zmiennej. Następne do zmiennej $log zapisujemy konkatenację „callerid” (czyli osoby dzwoniącej), znaku „->” oraz numeru docelowego. Całość tak stworzonej linii dopisujemy do jakże enigmatycznego pliku nazwanego 'aa.log’ w folderze /var/log ;)

Którego zawartość po 2 rozmowach będzie wyglądać tak:

22555555 -> 10*
22555555 -> 10*

W takim skrypcie możemy napisać wszystko co się nam podoba – co daje nieograniczone możliwości.

Powoli kończąc ten wpis chciałem jeszcze dodać, że dużo informacji do debuggowania daje konsola asteriskowa odpalana poleceniem:

asterisk -r

To tam szukamy ewentualnych komunikatów o błędach. Powinien być tam również cały przebieg dialplanu dla każdego połączenia. Jeśli na konsoli nic się nie pojawia, to upewnij się, że w pliku /etc/asterisk/asterisk.conf w sekcji options masz ustawione:

verbose = 3
debug = 3

i zrestartuj Asteriska.

Historyczne logi są dostępne w pliku /var/log/asterisk/messages – więc tam szukaj ewentualnych problemów.

Kończąc ten przydługi wpis chciałem dodać, iż nie opisałem tutaj nawet 1/10 możliwości Asteriska – więc zapraszam do dodawania komentarzy, które rozszerzyłyby wpis.

Dzięki za wizytę,
Mateusz Mazurek

A może wolisz nowości na mail?

Subskrybuj
Powiadom o
guest

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.

7 komentarzy
Inline Feedbacks
View all comments
Kuba

Zdecydowanie prościej i szybciej jest użyć wersji z GUI np. FreePBX. To samo, ale w przystępnej formie. Chyba, że ktoś jest masochista i lubi sobie klepać polecenia :)

Alex

Żeby rejestracja klientów SIP zadziałała, trzeba w pliku /etc/asterisk/sorcery.conf dodać wpis o następującej treści:

[res_pjsip]
endpoint=realtime,ps_endpoints
auth=realtime,ps_auths
aor=realtime,ps_aors
domain_alias=realtime,ps_domain_aliases

Jeżeli tego nie zrobić, to Asterisk nie zaciągnie konfiguracji endpointów z bazy danych PostgreSQL.

Sprawdzić czy konfiguracja endpointów została poprawnie załadowana można logując się do konsoli Asteriska (sudo asterisk -r) i wykonując polecenie: pjsip show endpoints

Wynik tego polecenia kiedy tylko klient 22444444 jest zarejestrowany:
https://pastebin.com/raw/gm33ZH2A

Alex

Jeśli po wykonaniu połączenia na numer 10* i przekierowaniu na 22444444 w bazie polecenie SELECT * FROM cdr; nie zwraca wyników, to najprawdopodobniej problem wynika z powodu kodowania.

W mojej konsoli Asteriska poleciał taki błąd:
ERROR[4247]: cdr_pgsql.c:440 pgsql_log: Reason: ERROR: character with byte sequence 0xc5 0x81 in encoding „UTF8” has no equivalent in encoding „LATIN9”

Do naprawy wystarczyło w pliku /etc/asterisk/cdr_pgsql.conf w sekcji [global] dodać:
encoding=UTF8

Alex

Skrypt w blogu wykonuje się w nieskończonność, ponieważ zapomniałeś obciąć z obu stron dane wejściowe, to powoduje, że puste linie z dwiema spacjami (” „), które otrzymuje skrypt zwracają true w linijce while ($env=read()), co z kolei powoduje następujące rzeczy:

* skrypt nigdy nie wykona operacji flush i dlatego dane nie trafią do logu
* Asterisk nie nawiąże połączenia z klientem, do którego dzwonimy, ponieważ czeka na zakończenie wykonania skryptu
* zawieszenie połączenia w kliencie, z którego dzwonimy, nie poskutkuje zapisowi danych do logu

Działający skrypt napisany w Python: https://pastebin.com/etKG5qF7
Wynik tego skryptu: https://pastebin.com/nxkxvivc