📜 ⬆️ ⬇️

VoIP telephony. Asterisk. Non-standard approach to everything. Part 1

Exactly a year ago, our former colleagues turned to us with a proposal to take part in modifying the operator's VoIP engine. The task was reduced to a complete rework of the personal account, ensuring the scaling of the system, creating a billing system, LCR, monitoring user expenses, controlling the duration of calls, analytics on calls. The story ended sadly, because The expanded functionality of the system we have laid down allegedly did not correspond to the TZ, which was not formalized on paper and is only in the heads of the managers of the operator. Due to the fact that managers did not want to pay for the developed functionality, which the customer really liked, we broke off the relationship. We did not have the NDA and we did not have a contract, so after consulting with colleagues we decided to put part of the developments into free access. I think this will be a series of articles. And let's start with the basic things and architecture.


Every administrator who has dealt with IP telephony at least once in his life knows that IP telephony services can be provided to the final subscriber in several ways:



Companies with offices in several countries or who wish to have a presence in another country can buy, for the convenience of their clients, a phone number in the UK, for example, and handle incoming calls in Moscow. At the same time, the operator who provided them with such a number may not be engaged in the delivery of calls to the UK. Some countries, such as the Republic of Belarus, do not provide numbers to non-residents.


So, we proceed from the following initial requirements:


  1. The server for receiving calls can be anywhere
  2. The number of servers is not limited.
  3. The number of telecom operators is not limited.
  4. Acceptance of a call can occur with any type of lease number (login / password or trunk)
  5. A call with a specific CallerID should occur only from that server and only from that account to which this CallerID is currently linked, or through an operator allowing you to change CallerID
  6. Calls between neighboring servers should be transparent without losing information about the called subscriber. For example, redirecting a London number to an internal, mobile or landline phone in another region
  7. For outgoing calls, depending on the tariff plan, the cheapest route should be chosen with reservation through more expensive ones. In order to control the quality of communication, it is necessary to ensure monitoring of ABR, ASR (statistical parameters determining the quality of communication in a given direction through a certain telephony node)
  8. If there are external users using the system, they should see their current balance and current conversations in realtime in realtime
  9. When the value of the current balance is less than a certain amount, the user should receive a voice message about the negative balance with the termination of the conversation
  10. The user must be able to make calls via a web browser (WebRTC)
  11. Processing servers for incoming and outgoing calls, as well as servers serving subscribers can be divided
  12. For analytics and call routing, geolocation of incoming and outgoing calls is required.

A small remark: the described configuration is universal, but is more suitable for services with a customer-oriented direction, such as call centers, in cases where customer personalization is necessary and the client is tied to one manager or a group of managers. Most of the described mechanisms are universal and can be effectively used in other configurations.


Quite a few requirements, so where to start? Asterisk will accept and make calls, Application Server will be responsible for preparing calls in python, we will store all working data in MariaDB, and most of the logic will be implemented in the form of procedures. This will allow us to maximally distance the logic of asterisk from unnecessary rules in the dialplan and provide scalability along with configuration unification.


Traffic


We will determine our traffic. We need a presence say in Cyprus, America, the UK and Russia. From the point of view of Russian traffic, it is more profitable to work through Russian telecom operators. For example Vestkol, IPPort and others. Russian (Moscow) operators also provide telephone numbers in codes 495 and 499 at a fairly reasonable price. We can provide a presence in Cyprus, in America and the United Kingdom by purchasing numbers from Zadarma, Multilel or someone else. Since international calls from Russian operators are quite expensive, you can buy traffic from foreign operators for example VoiceBuy or VoxBeam.


When connecting to the operator in the option "login / password" calls to our PBX will come from the IP address of the server of the operator on which the registration takes place. However, in the case of outgoing calls, the server address may be different.


When connecting to an operator via a SIP trunk, incoming calls can come from several servers from the operator's pool, and outgoing calls are made using a single DNS name, which in most cases contains several IP addresses. As we have already defined, the operator may be able to replace CallerID for an outgoing call, which can be regulated by a tariff plan or contract. Replacing CallerID (number substitution) in Russia for outgoing calls is prohibited. With foreign operators, the situation is much simpler and the replacement of CallerID is fully supported.


Create a database.


Most of the data and tables published in this article are extracts from the core of the real database, with the exception of phone numbers and IP addresses


Due to the fact that we have multicurrency communication operators, the link to the currency table should be in the table of communication operators. A little later we will consider the possibility of multi-currency recalculation of tariffs of operators.


Currency table
CREATE TABLE `currency` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `iso` CHAR(3) NOT NULL DEFAULT '' COLLATE 'utf8mb4_unicode_ci', `name_en` VARCHAR(200) NOT NULL DEFAULT '' COLLATE 'utf8mb4_unicode_ci', `name_ru` VARCHAR(200) NOT NULL DEFAULT '' COLLATE 'utf8mb4_unicode_ci', `numcode` INT(3) UNSIGNED ZEROFILL NULL DEFAULT NULL COMMENT 'numcode for country', PRIMARY KEY (`id`), UNIQUE INDEX `iso` (`iso`) ) COMMENT=' , iso   ' COLLATE='utf8mb4_unicode_ci' ENGINE=InnoDB AUTO_INCREMENT=1 ; 

currency


idisoname_enname_runumcode
96RUBRussian rubleRussian ruble643
122USDUS DollarU.S. dollar840
156EUREuroEuro978

Table operators
 CREATE TABLE `providers` ( `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `providername` VARCHAR(50) NULL DEFAULT '0' COLLATE 'utf8mb4_unicode_ci', `currency_id` INT(10) UNSIGNED NOT NULL DEFAULT '0', `noncli_prefix` VARCHAR(10) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', `cli_prefix` VARCHAR(10) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', `premcli_prefix` VARCHAR(10) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', `cli_allowed` ENUM('Y','N') NOT NULL DEFAULT 'N' COLLATE 'utf8mb4_unicode_ci', `dynamic_calls` CHAR(1) NULL DEFAULT 'N' COLLATE 'utf8mb4_unicode_ci', `append_plus` TINYINT(1) NOT NULL DEFAULT '1', `dynamic_calls_caller_id` VARCHAR(30) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', PRIMARY KEY (`id`), INDEX `FK_providers_currency` (`currency_id`), CONSTRAINT `FK_providers_currency` FOREIGN KEY (`currency_id`) REFERENCES `currency` (`id`) ) COMMENT='       ' COLLATE='utf8mb4_unicode_ci' ENGINE=InnoDB AUTO_INCREMENT=1 ; 

providers


idprovidernamecurrency_idnoncli_prefixcli_prefixpremcli_prefixcli_alloweddynamic_callsappend_plusdynamic_calls_caller_id
3Zadarma96NNone
fourMultitel122NNone
7Westcall96YN074951815283
eightVoxbeam122001110300111010011102YYone
elevenVoicebuy122999199929993YYone
sixteenIPport96NNone

So. Operators.


Usually, foreign operators have 2-3 tariffs for which they deliver traffic to the called subscriber. You can read about the differences between CLI and NonCLI techniques here .


To select a tariff plan, the prefix ( noncli_prefix, cli_prefix, premcli_prefix ) is usually used , indicated before the called number in the process of forming the call line.


Description of fields:



In the case of WestCall and other operators providing a connection via a SIP trunk, you buy a pool of numbers and, when making calls, you can change the outgoing number to any number from this pool. IPPort, like other operators, when using the login / password scheme with a single purchased number, does not allow changing the outgoing number. Operators VoxBeam and VoiceBuy are used for outgoing calls for any directions and allow you to change the outgoing number to any other. The truth is there is one thing! And it lies in the fact that the end telecom operators delivering the call to the called subscriber can reject the call or change the caller's number to their own if the calling number corresponds to the internal number of the country or area. Those. for example, when calling from Russia to Ukraine (Kiev), we change CallerID to a Ukrainian number in Kiev and the telecom operator through which the call is being made can simply call the call, because it does not comply with the internal call processing policy "A call through an external incoming international trunk cannot contain internal numbers". Operators Zadarma and Multitel will be used as number providers and we will not send outgoing traffic through them, although we could have done so if we wanted to.


An important aspect of call processing is security. Quite often there are situations when an incorrectly configured IP telephony server allows other calls to pass through. To ensure the security of the circuits that process incoming and outgoing calls, in addition to separating operators into different contexts, it is worth creating a database of IP addresses of telecom operators providing us with communication services. Firstly, it will allow you to configure the export of addresses in the firewall and remove unnecessary attempts to “ring out” through us, and secondly, it will provide more complete information about how to connect to telecom operators.


Table of IP addresses of telecom operators
 CREATE TABLE `providers_ips` ( `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `provider_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', `ipaddress` INT(11) UNSIGNED NOT NULL DEFAULT '2130706433', `domainname` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', `direction` ENUM('IN','OUT','IN/OUT') NOT NULL DEFAULT 'IN' COLLATE 'utf8mb4_unicode_ci', `proto` ENUM('SIP','IAX2','H323') NOT NULL DEFAULT 'SIP' COLLATE 'utf8mb4_unicode_ci', PRIMARY KEY (`id`), INDEX `FK_providers_ips_providers` (`provider_id`), CONSTRAINT `FK_providers_ips_providers` FOREIGN KEY (`provider_id`) REFERENCES `providers` (`id`) ) COMMENT='      . direction    / ' COLLATE='utf8mb4_unicode_ci' ENGINE=InnoDB AUTO_INCREMENT=1 ; 

providers_ips


idprovider_idipaddressdomainnamedirectionproto
oneeight2130706433sbc.voxbeam.comOutSip
2eight160769432095.211.119.240INSip
732130706433sip.zadarma.comOutSip
sixteen33106773121proxy-1.fr.zadarma.comINSip
1733106773122proxy-2.fr.zadarma.comINSip
31sixteen150685236089.208.190.8INSip
33sixteen150685235789.208.190.5INSip
34sixteen1506852354sip.n1.ipport.netOutSip
35eleven2991415097sip.voicebuy.comOutSip
42four3514573416209.124.34.104OutSip
4473277775106195.94.225.2IN / OUTSip
51four3514573417209.124.34.105INSip
52four3514573446209.124.34.134INSip

Description of fields:



Thus, a bunch of operator and addresses will look like this.


vw_providers


idprovidernameprotoaadirection
sixteenIPportSip89.208.190.5 [89.208.190.5]IN
sixteenIPportSipsip.n1.ipport.net [89.208.190.2]Out
sixteenIPportSip89.208.190.8 [89.208.190.8]IN
fourMultitelSip80.97.55.105 [80.97.55.105]IN
fourMultitelSip209.124.34.104 [209.124.34.104]Out
fourMultitelSip41.218.96.199 [41.218.96.199]IN
elevenVoicebuySipsip.voicebuy.com [178.77.95.57]Out
eightVoxbeamSipsbc.voxbeam.com [127.0.0.1]Out
eightVoxbeamSip95.211.119.240 [95.211.119.240]IN
7WestcallSip195.94.225.2 [195.94.225.2]IN / OUT
3ZadarmaSipproxy-1.fr.zadarma.com [185.45.152.129]IN
3ZadarmaSipproxy-3.ri.zadarma.com [195.122.19.11]IN
3ZadarmaSipsiplv.zadarma.com [195.122.19.17]IN
3ZadarmaSipproxy-8.fr.zadarma.com [185.45.152.136]IN
3ZadarmaSipsip.zadarma.com [127.0.0.1]Out
3ZadarmaSipsiplv1.zadarma.com [195.122.19.17]IN
3ZadarmaSipmediarelay-1.zadarma.com [185.45.152.162]IN

What is it for ?


  1. An incoming call to the phone number we rented from a particular operator should come from the pool of IP addresses of the servers of this operator. Therefore, at the time of an incoming call, it is worth checking the number associated with the operator with the table of IP addresses of the operator
  2. The operator may have several servers processing outgoing calls from our server, so this should be taken into account when forming the call queue
  3. This scheme allows you to instantly receive a call string. In this case, replacing the domainname with the name of the trunk defined in sip.conf or iax.conf does not disrupt the operation of the system
  4. Numbers can be spread over several servers for fault tolerance, and can be rigidly assigned to each server. It makes no sense to serve the call on the server to which this number is not attached.

Let's try to get a call string for the number 74957777777.


Call string request
 SELECT CONCAT_WS('/', pips.proto, pips.domainname, CONCAT(IF(p.append_plus IS TRUE,'+',''), IFNULL(p.cli_prefix,''),74957777777)) AS dial_string FROM providers_ips AS pips LEFT JOIN providers AS p ON p.id=pips.provider_id WHERE pips.direction IN ('OUT', 'IN/OUT') 

Dialer prompt


dial_string
Sip / sip.zadarma.com/74957777777
Sip / westcall / 74957777777
Sip / sbc.voxbeam.com/001110174957777777
Sip / sip.voicebuy.com/999274957777777
SIP / sip.n1.ipport.net / + 74957777777

As we see, we received ready data for dialing, which need to be transferred to the Dial application of the Asterisk server. Let's stop on this for now.


To bind the numbers leased from the carrier, we need to specify a list of servers on which these numbers will be serviced.


Server table
 CREATE TABLE `servers` ( `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `servername` VARCHAR(50) NULL DEFAULT '0' COLLATE 'utf8mb4_unicode_ci', `location` INT(11) NOT NULL DEFAULT '0' COMMENT 'country code where server is located', `ipaddress` INT(11) NOT NULL DEFAULT '2130706433' COMMENT 'localhost by default', `comment` VARCHAR(50) NULL DEFAULT '0' COLLATE 'utf8mb4_unicode_ci', `sip` VARCHAR(50) NULL DEFAULT NULL COMMENT 'SIP URI' COLLATE 'utf8mb4_unicode_ci', `iax2` VARCHAR(50) NULL DEFAULT NULL COMMENT 'IAX2 address and user binding if rsa keys used' COLLATE 'utf8mb4_unicode_ci', `iax2control` VARCHAR(50) NULL DEFAULT NULL COMMENT 'IAX2 address and control user binding for pair and route control' COLLATE 'utf8mb4_unicode_ci', `protocol` ENUM('SIP','IAX2') NOT NULL DEFAULT 'SIP' COLLATE 'utf8mb4_unicode_ci', PRIMARY KEY (`id`) ) COMMENT='   ' COLLATE='utf8mb4_unicode_ci' ENGINE=InnoDB AUTO_INCREMENT=1 ; 

servers


idservernamelocationipaddresscommentsipiax2iax2controlprotocol
0undefined021307064330Sip
oneasterisk-macomnet18935571297290SIP / 212.5.126.1IAX2 / macomnet @ macomnetIAX2 / macomnetcontrol @ macomnetIAX2
2asterisk-msm18915068078090SIP / 89.208.16.1IAX2 / msm @ msmIAX2 / msmcontrol @ msmIAX2
fiveasterisk-corbina18913992348170SIP / 83.102.161.1IAX2 / corbina @ corbinaIAX2 / corbinacontrol @ corbinaIAX2

Description of fields:



What is this table for and why is it so strange? For the inter-server connection of asterisk servers, it is most convenient to use the IAX protocol. There are several reasons:


  1. all transit traffic between servers can be encrypted
  2. Unlike the SIP protocol, it is easier to get communication operators through the firewall (all you need is 1 UDP port)
  3. the inability to connect to the attacker with the server without the presence of encryption keys
  4. the ability to transfer data between servers. for example, session variables. In the SIP protocol, you can add data to the headers of session packets, but often superfluous headers are simply “cleaned” by intermediate servers from the packets. In the IAX protocol, you can transfer the necessary variables in the stream before establishing the connection.
  5. precise binding of the host and user / key combinations to the context on the server

approximate iax.conf config on one of the servers
 [macomnet] type=user username=macomnet auth=rsa inkeys=asterisk-corbina:asterisk-msm context=incoming_dialer encryption=yes qualify=yes disallow=all allow=gsm allow=ulaw [msm] type=peer host=89.208.16.1 username=msm auth=rsa outkey=asterisk-macomnet encryption=yes qualify=yes disallow=all allow=gsm allow=ulaw trunk=yes [corbina] type=peer host=83.102.161.1 username=corbina auth=rsa outkey=asterisk-macomnet encryption=yes qualify=yes disallow=all allow=gsm allow=ulaw trunk=yes 

With the servers initially figured out. Now you need to bind the rented numbers to a specific server. This is necessary for correct call routing.


Number table

CREATE TABLE numbers_pool (
id INT (11) UNSIGNED NOT NULL AUTO_INCREMENT,
number VARCHAR (50) NULL DEFAULT '0' COMMENT 'The rented number' COLLATE 'is utf8mb4_unicode_ci',
provider_id INT (11) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Attaching a number to the operator',
server_id INT (11) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Attaching a number to the server',
enabled BIT (1) NOT NULL DEFAULT b'1 'COMMENT' Enabling / disabling number ',
direction ENUM ('IN', 'OUT', 'IN / OUT') NULL DEFAULT 'IN' COMMENT 'The default call direction is' COLLATE 'utf8mb4_unicode_ci',
virtualflag BIT (1) NOT NULL DEFAULT b'1 'COMMENT' Virtual or real number ',
echotest TINYINT (1) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Testing the number 0/1 - off / on',
PRIMARY KEY ( id )
UNIQUE INDEX number ( number ),
INDEX FK_number_assignment_copy_providers ( provider_id ),
INDEX FK_number_assignment_servers ( server_id ),
INDEX number btree ( number )
CONSTRAINT FK_provider FOREIGN KEY ( provider_id ) REFERENCES providers ( id ),
CONSTRAINT FK_server FOREIGN KEY ( server_id ) REFERENCES servers ( id )
)
COLLATE = 'utf8mb4_unicode_ci'
ENGINE = InnoDB
AUTO_INCREMENT = 1
;


numbers_pool


idnumberprovider_idserver_idenableddirectionvirtualflagechotest
12537167859001fouroneoneINone0
12637167859002fouroneoneINone0
2784971122954000fourone0INone0
279749518150007oneoneIN / OUT00
280749518150017oneoneIN / OUT00
281749518150027oneoneIN / OUT00
42674951339501320IN / OUTone0
427749513395023one0IN / OUTone0
51574957952301sixteenoneoneIN / OUT00
51674957952302sixteen2oneIN / OUT00
529442038070121elevenoneoneINone0
531442038070123elevenoneoneINone0

Description of fields:



At the time of developing the system, the number of leased numbers was about 400, the number of servers was 4. To test call routing, they even added a Multiphone from Megaphone. When using "real" numbers associated with the login / password, it is necessary to ensure the implementation of the call through the correct dialpyr. Since asterisk cannot know through which dialpere a call should be routed, it is necessary to create a table of connections.


Table of numbers and dialpeer
 CREATE TABLE `gates` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `numbers_pool_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', `serverid` INT(11) UNSIGNED NOT NULL DEFAULT '0', `gatename` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', `gatenumber` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', `cid_support` ENUM('Y','N') NULL DEFAULT 'Y' COLLATE 'utf8mb4_unicode_ci', `contextname` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', `comment` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', PRIMARY KEY (`id`), UNIQUE INDEX `name_number` (`gatename`, `gatenumber`), INDEX `FK_gates_servers` (`serverid`), INDEX `FK_gates_numbers_pool` (`numbers_pool_id`), CONSTRAINT `FK_gates_numbers_pool` FOREIGN KEY (`numbers_pool_id`) REFERENCES `numbers_pool` (`id`), CONSTRAINT `FK_gates_servers` FOREIGN KEY (`serverid`) REFERENCES `servers` (`id`) ) COMMENT='List of gates on servers' COLLATE='utf8mb4_unicode_ci' ENGINE=InnoDB AUTO_INCREMENT=1 ; 

gates


idnumbers_pool_idserveridgatenamegatenumbercid_supportcontextnamecomment
one516onesip_peer_msm_230274957952302Ysip_msm_74957952302
2515onesip_peer_msm_230174957952301Ysip_msm_74957952301
eight279onewestcall74951815000Ywestcall
9280onewestcall74951815001Ywestcall
ten281onewestcall74951815002Ywestcall

Description of fields:



At this point, the setting of the base for service calls is almost complete. I deliberately do not consider now the work of the tariffiser and call control systems, as this is a very big topic that should be put in a separate article.


Due to the fact that the main call processing and call routing logic is removed from Asterisk to the Application Server, and the procedures are in MariaDB, it is necessary to provide mechanisms for debugging and controlling the operation of the procedures. To do this, you can use the following scheme.
To store debug information, create an additional debug database and create a debug_records table there .


 CREATE TABLE `debug_records` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `logtime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `procedure_name` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_unicode_ci', `debug_text` TEXT NOT NULL COLLATE 'utf8mb4_unicode_ci', PRIMARY KEY (`id`) ) COLLATE='utf8mb4_unicode_ci' ENGINE=InnoDB AUTO_INCREMENT=1 ; 

To write to this data table, create a debug procedure.


 CREATE DEFINER=`root`@`localhost` PROCEDURE `debug`( IN `ProcedureName` VARCHAR(50), IN `DebugText` TEXT ) LANGUAGE SQL NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER COMMENT 'Save debug info to log' BEGIN DECLARE DebugEnabled TINYINT DEFAULT False; SET DebugEnabled=True; IF DebugEnabled THEN INSERT INTO debug_records (procedure_name,debug_text) VALUES (ProcedureName,DebugText); COMMIT; END IF; END 

Now debugging and control of the procedure at the development stage becomes a pleasure. At the beginning of each procedure, we define two variables, the first is the name of the procedure, the second is the flag defining whether debugging is enabled or not. And then in various places where the procedure may behave incorrectly - we first add an entry to the debug log.


 CREATE DEFINER=`root`@`localhost` PROCEDURE `usp_gettypeofcall`( IN `IncomingPhoneNumber` VARCHAR(30), IN `TargetGateNumber` VARCHAR(30), IN `CHANID` VARCHAR(60), IN `SYSTEMNAME` VARCHAR(30) ) LANGUAGE SQL NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER COMMENT '        ' BEGIN DECLARE ProcedureName VARCHAR(50) DEFAULT 'usp_gettypeofcall'; DECLARE ProcedureDebug TINYINT DEFAULT True; IF ProcedureDebug THEN CALL debug.debug(ProcedureName,CONCAT_WS(' ','Call from ',IncomingPhoneNumber,'to',TargetGateNumber,'at server',SYSTEMNAME,'with channelid',CHANID)); END IF; END 

I draw your attention to the fact that the use of this logic is not recommended !!! in production mode at high loads, since the output of debugging information can greatly reduce the speed.


Configure Asterisk


Let us proceed to setting up the Asterisk server. The standard procedure for handling calls that fall into a public context, in most existing configurations, is as follows.


 [public] exten => 74951815000,1,NoOp(Incoming Call to ${EXTEN}) same => n,Dial(SIP/1000,60) same => n,Hangup() 

handling outgoing calls from the dialout context looks like this


 [dialout] exten => _X.,1,NoOp(Outgoing Call to ${EXTEN}) same => n,Dial(SIP/dialpeer_name/${EXTEN},60) same => n,Hangup()  [dialout] exten => _X.,1,NoOp(Outgoing Call to ${EXTEN}) same => n,Dial(SIP/sip.n1.ipport.net/${EXTEN},60) same => n,Hangup()  [dialout] exten => _X.,1,NoOp(Outgoing Call to ${EXTEN}) same => n,Dial(SIP/${EXTEN}@sip.n1.ipport.net,60) same => n,Hangup() 

In general, who is as accustomed and as you like. As we see with such an organization of outgoing calls, there is practically no information about the status of the call. We modified Dialplan call processing and added service functions to it.


 [service] ; GetIP subroutine exten => getip,1,Set(TESTAT=${CUT(SIP_HEADER(From),@,2)}) same => n,GotoIf($["${TESTAT}" != ""]?hasat) same => n,Set(FROM_IP=${CUT(CUT(SIP_HEADER(From),>,1),:,2)}) same => n,Goto(gotip) same => 20(hasat),Set(FROM_IP=${CUT(CUT(CUT(SIP_HEADER(From),@,2),>,1),:,1)}) same => n(gotip),NoOp(Incoming Server IP is ${FROM_IP}) same => n,Return() exten => set_handler,1,Set(CHANNEL(hangup_handler_push)=service,outbound_handler,1) same => n,AGI(/usr/local/etc/asterisk_scripts/create_channel_record.py) same => n,Return() ; Set Hangup handler for channel exten => outbound_handler,1,NoOp(Hungup handler python started) same => n,AGI(/usr/local/etc/asterisk_scripts/hangup.py) same => n,HangupCauseClear() same => n,Return() exten => no_more_paths,1,NoOp(No more dial paths) same => n,Hangup() [predial] exten => s,1,NoOp(PreDial handler python started) same => n,AGI(/usr/local/etc/asterisk_scripts/predial.py) same => n,Return() [public] exten => _X.,1,GoSub(service,getip,1) same => n,AGI(/usr/local/etc/asterisk_scripts/incoming.py) exten => _+X.,1,GoSub(service,getip,1) same => n,AGI(/usr/local/etc/asterisk_scripts/incoming.py) [users_context] exten => _X.,1,NoOp() same => n,AGI(/usr/local/etc/asterisk_scripts/make_a_route.py) [make_a_call] exten => h,1,NoOp(Hangup) exten => _.,1,NoOp(${EXTEN}) same => n,SET(__LoopCount=1) same => n(try),AGI(/usr/local/etc/asterisk_scripts/incoming_dialer.py) same => n,Dial(${DIALSTRING},60,b(service^set_handler^1)U(predial)) same => n,SET(__LoopCount=${IF($[${HANGUPCAUSE}=17]?10:${LoopCount})}) same => n,Set(__LoopCount=${INC(LoopCount)}) same => n,NoOp(Current LoopCount ${LoopCount}) same => n,GotoIf($["${LoopCount}" < 10]?try) same => n,Hangup() [redirect] exten => h,1,NoOp(Hangup) exten => _.,1,NoOp(${ForwardPath}) same => n,Dial(${ForwardPath}/${EXTEN},60,b(service^set_handler^1)) same => n,Hangup() 

The [service] context contains service functions such as:



The [predial] context is invoked before the callers are connected when they successfully pick up the handset on the called side.
All external calls fall into the [public] context.
All calls that need to be redirected from server to server fall into the [redirect] context.
The context [users_context] is the main context of users for preparing routes for calls.
The context [make_a_call] is the main context when making an outgoing call.


Let us consider in more detail the procedure for processing an incoming call:


  1. The call comes from the operator's server and falls into the context of [public]
  2. Called the procedure for determining the IP address of the server from which the call came based on the headers of the SIP packet
  3. Calls the AGI application incoming.py that performs the initial processing of the incoming call.
  4. The internal logic of the application connects to the database and sends the initial parameters to the connection check procedure: the server’s IP address, the called number, the server identifier. The server identifier is unique, corresponds to the server table and is defined in the asterisk.conf file.
  5. , . . . , "".

[users_context] [make_a_call] .


:


  1. [users_context]
  2. AGI make_a_route.py , , , . , :
    • CallerID
    • ...
      make_a_route.py [make_a_call] [redirect]
  3. [redirect] , , , .
  4. [make_a_call] . 10 . , 1 , .
  5. AGI incoming_dialer.py , , AGI make_a_route.py , DialString .
  6. , Dial , U b. b , . AGI , . , , . U , .. . .
  7. 17(), .
  8. . ABR, ASR
  9. , [no_more_paths] ,

Asterisk . , Asterisk, , RSA IAX . .


AGI Python Application . AGI . , AGI HTTP Application Server uwsgi . , , CURL .


, LCR .


© Aborche 2017
Aborche


')

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


All Articles