📜 ⬆️ ⬇️

VoIP Part 1

Admit it, have you ever had a desire to listen to what friends, wives, husbands, etc. are talking about? without you? It would be cool to dial the phone number, and that the connection itself was established and not wait for the user to answer the call. I already actively use this functionality and this, I tell you, is dragging its feet. Implementing it within the framework of one mobile application, I have been looking for ready-made solutions for a long time, but, of course, I did not find anything. As a result, over 2 pm (6 hours), I managed to accomplish this task. But first things first…



Let's form the task: Realization of the possibility of forced calls to customers of the VoIP-network. Clients work on mobile phones IOS and Android.
')
Conditionally let's split the task into the north side and the client side. In this part I will write about the server. So, let's begin…

Before deploying my own server, I was looking for some free counterparts. It was required to me that within the VoIP network the provider did not take money and provided the API for registering clients. No, this is not, or one or the other. Well, I'm not ready to pay, even $ 0.003 per minute, I would have everything at once and for free. Only one way out, become the most VoIP provider.

We deploy a virtual machine
I suggest you first decide what the server is doing. I did not record telephone conversations and therefore I decided to limit myself to a conventional virtual machine with a small hard disk, a mandatory static IP address, CPU: 400 MHz and RAM: 512 MB.
Nakatil Debian on it and executed a number of standard commands

apt-get update && apt-get upgrade - upgrade versions of packages.

apt-get install asterisk - install the latest version of telephony from the repository.

apt-get install asterisk-mysql - the ability to store users in the database and synchronize with Asterisk

apt-get install mysql-server5 - install the MySQL database server.

apt-get install mc - install midnight commander, for ease of editing configuration files.

If at this stage there were any errors or something did not work out, then you can not read further, first read about installing packages in Debian.

Deploy and configure Asterisk
Asterisk from the package is fully functional; I personally did not need to build from source. Before setting, let's define what we want from it.
The ability to receive calls within our VoiP network.
The subscriber can be anywhere, he does not need to be physically on the same subnet, between the server and the client, the Internet, with its gateways, NATs, and God knows what.
Store users in a special database.
My beautiful name (phone number) in my case ivan@bestmyfamily.com

Determined the requirements, let's proceed to the configuration.
Open the MC and go to the directory / etc / asterisk

Find the file asterisk.conf , press f4 and add these lines to the end of the file

[compat]
pbx_realtime = 1.6
res_agi = 1.6
app_set = 1.6

This will allow us to synchronize with the database in real time. Immediately answer the question, why did I put the storage of users in a separate database. It's very simple, I'll add them to a simple PHP script from another server. This is extremely necessary if your server, like mine, accepts 10 new users per minute. I just physically can not bring them all to the configuration file.

At the beginning of this file in the [options] section you can set two parameters:

verbose = 64
debug = 64

now in the console, after running the asterisk -r command, we will see everything that happens with our server, so to speak, in real time.

Now we set up call reception. In our folder of Asterisk configuration files, we find the extensions.conf file and add to its end

[myfamily] # Here you can write anything,
# is the name of your call plan,
# but it must match your context field in Sip.conf

exten => _ [az]., 1, Dial (SIP / $ {EXTEN}, 60)
exten => _ [AZ]., 2, Dial (SIP / $ {EXTEN}, 60)

Let's take it through the example of a normal call.
ivan@bestmyfamily.com calls his wife by calling Zaya@bestmyfamily.com

The call goes to the server and the following happens:
_ [az]. - here this Asterisk is looking for what to do with this phone number. Here he will substitute our “Zaya”. And since Zaya is with a capital letter, it will go to the next line. Which line to go to indicate you, after the comma, the sequence number. Accordingly, all calls in my case are run through the first line from # 1 and, if such a number is not found, then we send it to # 2.
In our case, Zaya comes up to the second line and we can do something with it, for example, turn on her waiting music, send it to some department or something else. This can all be done by special teams. The simplest of them - Dial. It is simply trying to reach the user through the given channels in the allotted time.
We specified the SIP channel and 60 seconds of waiting. If we translate this line of SIP / $ {EXTEN}, 60 into Russian, we get the following: send a call to this phone SIP/Zaya@bestmyfamily.com and wait 60 seconds.

It remains for us to configure the most important file for SIP telephony - sip.conf. Here you need to be careful, I had to jump with a tambourine for a couple of hours to make it work. And if everything works from the same subnet, this does not mean that you can normally receive or connect two subscribers in different networks for NAT. I set up the work as follows:



[general]
allowguest = no
allowoverlap = no
context = myfamily; default context for incoming calls
bindport = 5060; UDP port that is listening to asterisk
bindaddr = 0.0.0.0

pedantic = no
directmedia = no
rtptimeout = 10
rtpholdtimeout = 300

; for realtime
; they force asterisk to cache data
; and the sip show peers command will display normally
; all registered realtime users
rtcachefriends = yes
rtcache = yes

; connection to the database; in general, it is not necessary to write them here from version 1.2
dbhost = 127.0.0.1
dbname = ***
dbuser = ***
dbpass = ***
dbport = 3306

The rest of the settings we will already specify in the database for each user.

We have the last res_config_mysql.c file, here we specify the settings for connecting to the database.

[general]
dbhost = 127.0.0.1
dbname = ***
dbuser = ***
dbpass = ***
dbport = 3306

Now we are doing an Asterisk reboot with the /etc/init.d/asterisk restart command if there are no errors, then you can be happy, the server is configured and ready to handle calls.

MySQL setup
Our server is ready to receive calls, but there are no callers. It is necessary to register and store new users somewhere. For these purposes, we create a database with the following table:

CREATE TABLE `sipusers` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`accountcode` VARCHAR(20) NULL DEFAULT NULL,
`disallow` VARCHAR(100) NULL DEFAULT 'all',
`allow` VARCHAR(100) NULL DEFAULT 'g729;ilbc;gsm;ulaw;alaw',
`allowoverlap` ENUM('yes','no') NULL DEFAULT 'yes',
`allowsubscribe` ENUM('yes','no') NULL DEFAULT 'yes',
`allowtransfer` VARCHAR(3) NULL DEFAULT NULL,
`amaflags` VARCHAR(13) NULL DEFAULT NULL,
`autoframing` VARCHAR(3) NULL DEFAULT NULL,
`auth` VARCHAR(40) NULL DEFAULT NULL,
`buggymwi` ENUM('yes','no') NULL DEFAULT 'no',
`callgroup` VARCHAR(10) NULL DEFAULT NULL,
`callerid` VARCHAR(80) NULL DEFAULT NULL,
`cid_number` VARCHAR(40) NULL DEFAULT NULL,
`fullname` VARCHAR(40) NULL DEFAULT NULL,
`call-limit` INT(8) NULL DEFAULT '0',
`callingpres` VARCHAR(80) NULL DEFAULT NULL,
`canreinvite` CHAR(6) NULL DEFAULT 'yes',
`context` VARCHAR(80) NULL DEFAULT NULL,
`defaultip` VARCHAR(15) NULL DEFAULT NULL,
`dtmfmode` VARCHAR(7) NULL DEFAULT NULL,
`fromuser` VARCHAR(80) NULL DEFAULT NULL,
`fromdomain` VARCHAR(80) NULL DEFAULT NULL,
`fullcontact` VARCHAR(80) NULL DEFAULT NULL,
`g726nonstandard` ENUM('yes','no') NULL DEFAULT 'no',
`host` VARCHAR(31) NOT NULL DEFAULT 'dynamic',
`insecure` VARCHAR(20) NULL DEFAULT NULL,
`ipaddr` VARCHAR(15) NOT NULL DEFAULT '',
`language` CHAR(2) NULL DEFAULT NULL,
`lastms` VARCHAR(20) NULL DEFAULT NULL,
`mailbox` VARCHAR(50) NULL DEFAULT NULL,
`maxcallbitrate` INT(8) NULL DEFAULT '384',
`mohsuggest` VARCHAR(80) NULL DEFAULT NULL,
`md5secret` VARCHAR(80) NULL DEFAULT NULL,
`musiconhold` VARCHAR(100) NULL DEFAULT NULL,
`name` VARCHAR(80) NOT NULL DEFAULT '',
`nat` VARCHAR(30) NOT NULL DEFAULT 'no',
`outboundproxy` VARCHAR(80) NULL DEFAULT NULL,
`deny` VARCHAR(95) NULL DEFAULT NULL,
`permit` VARCHAR(95) NULL DEFAULT NULL,
`pickupgroup` VARCHAR(10) NULL DEFAULT NULL,
`port` VARCHAR(5) NOT NULL DEFAULT '',
`progressinband` ENUM('yes','no','never') NULL DEFAULT 'no',
`promiscredir` ENUM('yes','no') NULL DEFAULT 'no',
`qualify` CHAR(3) NULL DEFAULT NULL,
`regexten` VARCHAR(80) NOT NULL DEFAULT '',
`regseconds` INT(11) NOT NULL DEFAULT '0',
`rfc2833compensate` ENUM('yes','no') NULL DEFAULT 'no',
`rtptimeout` CHAR(3) NULL DEFAULT NULL,
`rtpholdtimeout` CHAR(3) NULL DEFAULT NULL,
`secret` VARCHAR(80) NULL DEFAULT NULL,
`sendrpid` ENUM('yes','no') NULL DEFAULT 'yes',
`setvar` VARCHAR(100) NOT NULL DEFAULT '',
`subscribecontext` VARCHAR(80) NULL DEFAULT NULL,
`subscribemwi` VARCHAR(3) NULL DEFAULT NULL,
`t38pt_udptl` ENUM('yes','no') NULL DEFAULT 'no',
`trustrpid` ENUM('yes','no') NULL DEFAULT 'no',
`type` VARCHAR(6) NOT NULL DEFAULT 'friend',
`useclientcode` ENUM('yes','no') NULL DEFAULT 'no',
`defaultuser` VARCHAR(80) NOT NULL DEFAULT '',
`usereqphone` VARCHAR(3) NOT NULL DEFAULT 'no',
`videosupport` ENUM('yes','no') NULL DEFAULT 'yes',
`vmexten` VARCHAR(80) NULL DEFAULT NULL,
`useragent` VARCHAR(80) NULL DEFAULT NULL,
`regserver` VARCHAR(80) NULL DEFAULT NULL,
`callbackextension` VARCHAR(80) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `name` (`name`),
INDEX `name_2` (`name`)
)
COLLATE='cp1251_general_ci'
ENGINE=MyISAM
ROW_FORMAT=DYNAMIC
AUTO_INCREMENT=1;

And immediately add two of our users. I add them through a special PHP script to work with the SIP database:

 <?php // //    SIP  // class M_SIP { private static $instance; //  . const host = "bestmyfamily.com"; const user = "***"; const password = "***"; const db = "***"; public $dbSIP; //    . // // . // public static function Instance() { if(self::$instance == null) self::$instance = new M_SIP(); return self::$instance; } // //  // function __construct() { //     $this->dbSIP = mysqli_connect(self::host, self::user, self::password) or exit; $result = mysqli_select_db($this->dbSIP, self::db) or exit; mysqli_query($this->dbSIP, 'SET NAMES utf8'); } //     public function RegUserOnServer($pid, $uid) { $arr = array( 'accountcode' => Null, 'disallow' => 'all', 'allow' => 'ulaw;alaw', 'allowoverlap' => 'no', 'allowsubscribe' => 'no', 'allowtransfer' => Null, 'amaflags' => Null, 'autoframing' => Null, 'auth' => Null, 'buggymwi' => 'no', 'callgroup' => Null, 'callerid' => Null, 'cid_number' => Null, 'fullname' => Null, 'call-limit' => 0, 'callingpres' => Null, 'canreinvite' => "no", 'context' => 'myfamily', 'defaultip' => Null, 'dtmfmode' => Null, 'fromuser' => Null, 'fromdomain' => Null, 'fullcontact' => Null, 'g726nonstandard' => 'no', 'host' => 'dynamic', 'insecure' => Null, 'ipaddr' => Null, 'language' => 'en', 'lastms' => 0, 'mailbox' => Null, 'maxcallbitrate' => 384, 'mohsuggest' => Null, 'md5secret' => Null, 'musiconhold' => Null, 'name' => "", 'nat' => 'force_rport,comedia', 'outboundproxy' => Null, 'deny' => Null, 'permit' => Null, 'pickupgroup' => Null, 'port' => '', 'progressinband' => 'no', 'promiscredir' => 'no', 'qualify' => Null, 'regexten' => 1000001, 'regseconds' => 0, 'rfc2833compensate' => 'no', 'rtptimeout' => Null, 'rtpholdtimeout' => Null, 'secret' => $uid, 'sendrpid' => 'yes', 'setvar' => '', 'subscribecontext' => Null, 'subscribemwi' => Null, 't38pt_udptl' => 'no', 'trustrpid' => 'no', 'type' => 'friend', 'useclientcode' => 'no', 'defaultuser' => "", 'usereqphone' => 'no', 'videosupport' => 'no', 'vmexten' => Null, 'useragent' => Null, 'regserver' => Null, 'callbackextension' => Null ); return $this->Insert('sipusers', $arr); } // //   // $table -   // $object -      "  - " //  -    // private function Insert($table, $object, $isReplace = false) { $columns = array(); $values = array(); foreach ($object as $key => $value) { $key = mysqli_real_escape_string($this->dbSIP, $key); $columns[] = "`" . $key . "`"; if ($value === null) { $values[] = 'NULL'; } else { $value = mysqli_real_escape_string($this->dbSIP, $value); $values[] = "'$value'"; } } $columns_s = implode(',', $columns); $values_s = implode(',', $values); //   ? if ($isReplace) $query = "REPLACE INTO $table ($columns_s) VALUES ($values_s)"; else $query = "INSERT IGNORE INTO $table ($columns_s) VALUES ($values_s)"; $result = mysqli_query($this->dbSIP, $query); if (!$result) die($this->SqlError(mysqli_error($this->dbSIP))); $id = mysqli_insert_id($this->dbSIP); return $id; } // //   SQL // function SqlError($error) { return json_encode(array( 'code' => 99, 'error' => $error ) ); } } 


The last step in the configuration is to buy our name and link it to the ip address. I started the third level domain for these purposes voip.bestmyfamily.com and attached an ip address to it. If you forget this step, your phone numbers will not be so beautiful ivan@192.168.1.1: 5060

For testing, you can install any SIP phones. Of course, they are not so cool and will not, on the sly, pick up the phone to whom we are calling. But you have set up the usual telephone connection. In the second part, we will write our magic clients on Android and IOS.
Immediately make a reservation that you may have problems with approval in the apple. We sooo long passed the process of coordinating the application. These are not standard 2 weeks, we studied the code and tested the functionality before placing the application. As a result, agreed on the ability to run wiretapping only with the child mode.

Source: https://habr.com/ru/post/282187/


All Articles