blob: 16cf71f2171238302c72051cb90cf317f6f7592b [file] [log] [blame]
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +02001/* Asterisk's AMI interface functions in TTCN-3
2 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
3 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12/*
13 * https://docs.asterisk.org/Configuration/Interfaces/Asterisk-Manager-Interface-AMI/AMI-v2-Specification/
14 */
15module AMI_Functions {
16
17import from Misc_Helpers all;
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020018import from Osmocom_Types all;
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +020019import from IPL4asp_Types all;
20import from IPL4asp_PortType all;
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020021import from Socket_API_Definitions all;
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +020022import from TCCConversion_Functions all;
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020023
24modulepar {
25 float mp_ami_prompt_timeout := 10.0;
26}
27
28const charstring AMI_FIELD_ACTION := "Action";
Pau Espin Pedrol941ca3c2024-05-09 16:46:58 +020029const charstring AMI_FIELD_ACTION_ID := "ActionID";
Pau Espin Pedrol469c2f62024-05-22 17:48:17 +020030const charstring AMI_FIELD_CHAN_TYPE := "ChannelType";
31const charstring AMI_FIELD_DOMAIN := "Domain";
Pau Espin Pedrole98f4412024-05-17 19:33:46 +020032const charstring AMI_FIELD_EVENT := "Event";
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +020033const charstring AMI_FIELD_INFO := "Info";
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020034const charstring AMI_FIELD_RESPONSE := "Response";
Pau Espin Pedrol469c2f62024-05-22 17:48:17 +020035const charstring AMI_FIELD_SECRET := "Secret";
36const charstring AMI_FIELD_STATUS := "Status";
37const charstring AMI_FIELD_USERNAME := "Username";
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020038
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +020039/* Extensions: */
Pau Espin Pedrolddc4c702024-05-22 17:02:57 +020040const charstring AMI_FIELD_ALGORITHM := "Algorithm";
41const charstring AMI_FIELD_AUTN := "AUTN";
42const charstring AMI_FIELD_AUTS := "AUTS";
43const charstring AMI_FIELD_CK := "CK";
44const charstring AMI_FIELD_IK := "IK";
45const charstring AMI_FIELD_RAND := "RAND";
46const charstring AMI_FIELD_RES := "RES";
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +020047const charstring AMI_FIELD_REGISTRATION := "Registration";
48
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020049type record AMI_Field {
50 charstring key,
51 charstring val
Pau Espin Pedrolde7a4852024-04-19 16:20:45 +020052} with {
53 encode "TEXT"
54 variant "SEPARATOR(': ', ':\s+')"
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020055};
Pau Espin Pedrolde7a4852024-04-19 16:20:45 +020056
57type set of AMI_Field AMI_Msg with {
58 encode "TEXT"
59 variant "SEPARATOR('\r\n', '(\r\n)|[\n]')"
60 variant "END('\r\n', '(\r\n)|[\n]')"
61};
62
63external function enc_AMI_Msg(in AMI_Msg msg) return charstring
64 with { extension "prototype(convert) encode(TEXT)" }
65external function dec_AMI_Msg(in charstring stream) return AMI_Msg
66 with { extension "prototype(convert) decode(TEXT)" }
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020067
68template (value) AMI_Field
69ts_AMI_Field(template (value) charstring key,
70 template (value) charstring val) := {
71 key := key,
72 val := val
73};
74
75template (present) AMI_Field
76tr_AMI_Field(template (present) charstring key := ?,
77 template (present) charstring val := ?) := {
78 key := key,
79 val := val
80};
81
82/*
83 * Field Templates:
84 */
85
86template (value) AMI_Field
87ts_AMI_Field_Action(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ACTION, val);
88template (value) AMI_Field
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +020089ts_AMI_Field_ActionId(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ACTION_ID, val);
90template (value) AMI_Field
Pau Espin Pedrol469c2f62024-05-22 17:48:17 +020091ts_AMI_Field_ChannelType(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_CHAN_TYPE, val);
92template (value) AMI_Field
93ts_AMI_Field_Domain(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_DOMAIN, val);
94template (value) AMI_Field
Pau Espin Pedrole98f4412024-05-17 19:33:46 +020095ts_AMI_Field_Event(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_EVENT, val);
96template (value) AMI_Field
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +020097ts_AMI_Field_Info(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_INFO, val);
98template (value) AMI_Field
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020099ts_AMI_Field_Secret(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_SECRET, val);
Pau Espin Pedrol469c2f62024-05-22 17:48:17 +0200100template (value) AMI_Field
101ts_AMI_Field_Status(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_STATUS, val);
102template (value) AMI_Field
103ts_AMI_Field_Username(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_USERNAME, val);
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200104/* Extensions: */
105template (value) AMI_Field
Pau Espin Pedrolddc4c702024-05-22 17:02:57 +0200106ts_AMI_Field_Algorithm(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ALGORITHM, val);
107template (value) AMI_Field
108ts_AMI_Field_AUTN(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_AUTN, val);
109template (value) AMI_Field
110ts_AMI_Field_AUTS(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_AUTS, val);
111template (value) AMI_Field
112ts_AMI_Field_CK(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_CK, val);
113template (value) AMI_Field
114ts_AMI_Field_IK(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_IK, val);
115template (value) AMI_Field
116ts_AMI_Field_RAND(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_RAND, val);
117template (value) AMI_Field
118ts_AMI_Field_RES(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_RES, val);
119template (value) AMI_Field
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200120ts_AMI_Field_Registration(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_REGISTRATION, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200121
122template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +0200123tr_AMI_Field_Action(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_ACTION, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200124template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +0200125tr_AMI_Field_ActionId(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_ACTION_ID, val);
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200126template (present) AMI_Field
Pau Espin Pedrol469c2f62024-05-22 17:48:17 +0200127tr_AMI_Field_ChannelType(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_CHAN_TYPE, val);
128template (present) AMI_Field
129tr_AMI_Field_Domain(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_DOMAIN, val);
130template (present) AMI_Field
Pau Espin Pedrole98f4412024-05-17 19:33:46 +0200131tr_AMI_Field_Event(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_EVENT, val);
132template (present) AMI_Field
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200133tr_AMI_Field_Info(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_INFO, val);
134template (present) AMI_Field
Pau Espin Pedrol469c2f62024-05-22 17:48:17 +0200135tr_AMI_Field_Response(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_RESPONSE, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200136template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +0200137tr_AMI_Field_Secret(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_SECRET, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200138template (present) AMI_Field
Pau Espin Pedrol469c2f62024-05-22 17:48:17 +0200139tr_AMI_Field_Status(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_STATUS, val);
140template (present) AMI_Field
141tr_AMI_Field_Username(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_USERNAME, val);
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200142/* Extensions: */
143template (present) AMI_Field
Pau Espin Pedrolddc4c702024-05-22 17:02:57 +0200144tr_AMI_Field_Algorithm(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_ALGORITHM, val);
145template (present) AMI_Field
146tr_AMI_Field_AUTN(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_AUTN, val);
147template (present) AMI_Field
148tr_AMI_Field_AUTS(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_AUTS, val);
149template (present) AMI_Field
150tr_AMI_Field_CK(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_CK, val);
151template (present) AMI_Field
152tr_AMI_Field_IK(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_IK, val);
153template (present) AMI_Field
154tr_AMI_Field_RAND(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_RAND, val);
155template (present) AMI_Field
156tr_AMI_Field_RES(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_RES, val);
157template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +0200158tr_AMI_Field_Registration(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_REGISTRATION, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200159
160
161template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +0200162tr_AMI_Field_ResponseSuccess := tr_AMI_Field(pattern @nocase AMI_FIELD_RESPONSE, "Success");
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200163
164
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200165/***********************
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200166 * Message Templates:
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200167 ***********************/
168
169/*
170 * ACTIONS
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200171 */
172
Pau Espin Pedrolddc4c702024-05-22 17:02:57 +0200173/* Action: AuthResponse
174 * Registration: volte_ims
175 * AUTS: <value>
176 */
177template (value) AMI_Msg
178ts_AMI_Action_AuthResponse_AUTS(template (value) charstring registration,
179 template (value) charstring auts,
180 template (value) charstring action_id := "0001") := {
181 ts_AMI_Field_Action("AuthResponse"),
182 ts_AMI_Field_ActionId(action_id),
183 ts_AMI_Field_Registration(registration),
184 ts_AMI_Field_AUTS(auts)
185};
186/* Action: AuthResponse
187 * Registration: volte_ims
188 * RES: <value>
189 * CK: <value>
190 * IK: <value>
191 */
192template (value) AMI_Msg
193ts_AMI_Action_AuthResponse_RES(template (value) charstring registration,
194 template (value) charstring res,
195 template (value) charstring ck,
196 template (value) charstring ik,
197 template (value) charstring action_id := "0001") := {
198 ts_AMI_Field_Action("AuthResponse"),
199 ts_AMI_Field_ActionId(action_id),
200 ts_AMI_Field_Registration(registration),
201 ts_AMI_Field_RES(res),
202 ts_AMI_Field_CK(ck),
203 ts_AMI_Field_IK(ik)
204};
205
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200206/* Action: Login
207 * Username: <value>
208 * Secret: <value>
209 */
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200210template (value) AMI_Msg
Pau Espin Pedrol77976a62024-05-08 21:17:38 +0200211ts_AMI_Action_Login(charstring username,
212 charstring secret,
213 template (value) charstring action_id := "0001") := {
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200214 ts_AMI_Field_Action("Login"),
Pau Espin Pedrol77976a62024-05-08 21:17:38 +0200215 ts_AMI_Field_ActionId(action_id),
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200216 ts_AMI_Field_Username(username),
217 ts_AMI_Field_Secret(secret)
218};
219
220template (present) AMI_Msg
221tr_AMI_Action_Login(template(present) charstring username := ?,
Pau Espin Pedrol77976a62024-05-08 21:17:38 +0200222 template(present) charstring secret := ?,
223 template (present) charstring action_id := ?) := superset(
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200224 tr_AMI_Field_Action("Login"),
Pau Espin Pedrol77976a62024-05-08 21:17:38 +0200225 tr_AMI_Field_ActionId(action_id),
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200226 tr_AMI_Field_Username(username),
227 tr_AMI_Field_Secret(secret)
228);
229
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200230/* Action: PJSIPAccessNetworkInfo
231 * Registration: volte_ims
232 * Info: 3GPP-E-UTRAN-FDD; utran-cell-id-3gpp=2380100010000101
233 */
234template (value) AMI_Msg
235ts_AMI_Action_PJSIPAccessNetworkInfo(template (value) charstring registration := "volte_ims",
236 template (value) charstring info := "",
237 template (value) charstring action_id := "0001") := {
238 ts_AMI_Field_Action("PJSIPAccessNetworkInfo"),
239 ts_AMI_Field_ActionId(action_id),
240 ts_AMI_Field_Registration(registration),
241 ts_AMI_Field_Info(info)
242};
243function f_ami_gen_PJSIPAccessNetworkInfo_Info_EUTRAN(charstring uli_str) return charstring {
244 return "3GPP-E-UTRAN-FDD; utran-cell-id-3gpp=" & uli_str;
245}
246
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200247/* Action: PJSIPRegister
248 * ActionID: <value>
249 * Registration: volte_ims
250 */
251template (value) AMI_Msg
252ts_AMI_Action_PJSIPRegister(template (value) charstring registration := "volte_ims",
253 template (value) charstring action_id := "0001") := {
254 ts_AMI_Field_Action("PJSIPRegister"),
255 ts_AMI_Field_ActionId(action_id),
256 ts_AMI_Field_Registration(registration)
257};
258template (present) AMI_Msg
259tr_AMI_Action_PJSIPRegister(template (present) charstring registration := ?,
260 template (present) charstring action_id := ?) := {
261 tr_AMI_Field_Action("PJSIPRegister"),
262 tr_AMI_Field_ActionId(action_id),
263 tr_AMI_Field_Registration(registration)
264};
265
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200266/* Action: PJSIPUnregister
267 * ActionID: <value>
268 * Registration: volte_ims
269 */
270template (value) AMI_Msg
271ts_AMI_Action_PJSIPUnregister(template (value) charstring registration := "volte_ims",
272 template (value) charstring action_id := "0001") := {
273 ts_AMI_Field_Action("PJSIPUnregister"),
274 ts_AMI_Field_ActionId(action_id),
275 ts_AMI_Field_Registration(registration)
276};
277template (present) AMI_Msg
278tr_AMI_Action_PJSIPUnregister(template (present) charstring registration := ?,
279 template (present) charstring action_id := ?) := {
280 tr_AMI_Field_Action("PJSIPUnregister"),
281 tr_AMI_Field_ActionId(action_id),
282 tr_AMI_Field_Registration(registration)
283};
284
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200285/*
286 * RESPONSES
287 */
288
289/* Response: Success
290 */
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200291template (present) AMI_Msg
292tr_AMI_Response_Success := superset(
293 tr_AMI_Field_ResponseSuccess
294);
295
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200296/* Response: Success
297 * ActionId: <value>
298 */
299template (present) AMI_Msg
300tr_AMI_Response_Success_ActionId(template (present) charstring action_id := ?) := superset(
301 tr_AMI_Field_ResponseSuccess,
302 tr_AMI_Field_ActionId(action_id)
303);
304
Pau Espin Pedrole98f4412024-05-17 19:33:46 +0200305/*
306 * EVENTS
307 */
308template (present) AMI_Msg
309tr_AMI_Event(template (present) charstring ev_name := ?) := superset(
310 tr_AMI_Field_Event(ev_name)
311);
312
313/* Event: FullyBooted
314 * Privilege: system,all
315 * Status: Fully Booted
316 * Uptime: 4
317 * LastReload: 4 *
318 */
319template (present) AMI_Msg
320tr_AMI_Event_FullyBooted := tr_AMI_Event("FullyBooted");
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200321
Pau Espin Pedrolddc4c702024-05-22 17:02:57 +0200322/* Event: AuthRequest
323 * Privilege: <none>
324 * Registration: volte_ims
325 * Algorithm: AKAv1-MD5
326 * RAND: 14987631f65f8e3788a0798b6ebcd08e
327 * AUTN: f6e19a7ccb028000a06b19c9544516e5
328 */
329template (present) AMI_Msg
330tr_AMI_Event_AuthRequest(template (present) charstring registration := ?,
331 template (present) charstring algorithm := "AKAv1-MD5",
332 template (present) charstring rand := ?,
333 template (present) charstring autn := ?) := superset(
334 tr_AMI_Field_Event("AuthRequest"),
335 tr_AMI_Field_Registration(registration),
336 tr_AMI_Field_Algorithm(algorithm),
337 tr_AMI_Field_RAND(rand),
338 tr_AMI_Field_AUTN(autn)
339);
340
Pau Espin Pedrol469c2f62024-05-22 17:48:17 +0200341/* Event: Registry
342 * Privilege: system,all
343 * ChannelType: PJSIP
344 * Username: sip:238010000090828@172.18.155.103
345 * Domain: sip:172.18.155.103
346 * Status: Registered
347 */
348template (present) AMI_Msg
349tr_AMI_Event_Registry(template (present) charstring username := ?,
350 template (present) charstring domain := ?,
351 template (present) charstring status := ?,
352 template (present) charstring chan_type := "PJSIP") := superset(
353 tr_AMI_Field_Event("Registry"),
354 tr_AMI_Field_ChannelType(chan_type),
355 tr_AMI_Field_Username(username),
356 tr_AMI_Field_Domain(domain),
357 tr_AMI_Field_Status(status)
358);
359
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200360/***********************
361 * Adapter:
362 ***********************/
363
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200364type record AMI_Adapter_Parameters {
365 charstring remote_host,
366 IPL4asp_Types.PortNumber remote_port,
367 charstring local_host,
368 IPL4asp_Types.PortNumber local_port,
369 charstring welcome_str
370}
371
372const AMI_Adapter_Parameters c_default_AMI_Adapter_pars := {
373 remote_host := "127.0.0.1",
374 remote_port := 5038,
375 local_host := "0.0.0.0",
376 local_port := 0,
377 welcome_str := "Asterisk Call Manager/9.0.0\r\n"
378};
379
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200380type port AMI_Msg_PT message {
381 inout AMI_Msg;
382} with { extension "internal" };
383
384type component AMI_Adapter_CT {
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200385 port IPL4asp_PT IPL4;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200386 port AMI_Msg_PT CLIENT;
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200387 var AMI_Adapter_Parameters g_pars;
388
389 /* Connection identifier of the client itself */
390 var IPL4asp_Types.ConnectionId g_self_conn_id := -1;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200391}
392
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200393/* Function to use to connect as client to a remote IPA Server */
394private function f_AMI_Adapter_connect() runs on AMI_Adapter_CT {
395 var IPL4asp_Types.Result res;
396 map(self:IPL4, system:IPL4);
397 res := IPL4asp_PortType.f_IPL4_connect(IPL4, g_pars.remote_host, g_pars.remote_port,
398 g_pars.local_host, g_pars.local_port, 0, { tcp:={} });
399 if (not ispresent(res.connId)) {
400 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
401 log2str("Could not connect AMI socket from ", g_pars.local_host, " port ",
402 g_pars.local_port, " to ", g_pars.remote_host, " port ", g_pars.remote_port,
403 "; check your configuration"));
404 }
405 g_self_conn_id := res.connId;
406 log("AMI connected, ConnId=", g_self_conn_id)
407}
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200408
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200409private function f_ASP_RecvFrom_msg_to_charstring(ASP_RecvFrom rx_rf) return charstring {
410 return oct2char(rx_rf.msg);
411}
412
413/* Function to use to connect as client to a remote IPA Server */
414private function f_AMI_Adapter_wait_rx_welcome_str() runs on AMI_Adapter_CT {
415 var ASP_RecvFrom rx_rf;
416 var charstring rx_str;
417 timer Twelcome := 3.0;
418
419 Twelcome.start;
420 alt {
421 [] IPL4.receive(ASP_RecvFrom:?) -> value rx_rf {
422 rx_str := f_ASP_RecvFrom_msg_to_charstring(rx_rf);
423 if (g_pars.welcome_str != rx_str) {
424 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
425 log2str("AMI Welcome message mismatch: '", rx_str,
426 "' vs exp '", g_pars.welcome_str, "'"));
427 }
428 }
429 [] Twelcome.timeout {
430 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
431 log2str("AMI Welcome timeout"));
432 }
433 }
434 Twelcome.stop;
435 log("AMI Welcome message received: '", rx_str, "'");
436}
437
438private function dec_AMI_Msg_ext(charstring txt) return AMI_Msg {
439 log("AMI dec: '", txt, "'");
440 /* TEXT Enc/dec is not happy with empty values, workaround it: */
Pau Espin Pedrolcd20bbf2024-05-28 18:29:34 +0200441 var Misc_Helpers.ro_charstring fields := { "AccountCode", "AccountID", "Challenge", "DestExten", "Exten", "Extension", "Hint", "Value" };
442 var charstring patched_txt := txt;
443 for (var integer i := 0; i < lengthof(fields); i := i +1) {
444 patched_txt := f_str_replace(patched_txt, fields[i] & ": \r\n", "");
445 }
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200446
447 /* "AppData" field sometimes has a value containing separator ": ", which makes
448 * TEXT dec not happy. Workaround it for now by removing the whole field line:
449 * "AppData: 5,0502: Call pjsip endpoint from 0501\r\n"
450 */
451 var integer pos := f_strstr(patched_txt, "AppData: ", 0);
452 if (pos >= 0) {
453 var integer pos_end := f_strstr(patched_txt, "\r\n", pos) + lengthof("\r\n");
454 var charstring to_remove := substr(patched_txt, pos, pos_end - pos);
455 patched_txt := f_str_replace(patched_txt, to_remove, "");
456 }
457
458 log("AMI patched dec: '", patched_txt, "'");
459 return dec_AMI_Msg(patched_txt);
460}
461
462function f_AMI_Adapter_main(AMI_Adapter_Parameters pars := c_default_AMI_Adapter_pars)
463 runs on AMI_Adapter_CT {
464 var AMI_Msg msg;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200465 var charstring rx, buf := "";
466 var integer fd;
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200467 var ASP_RecvFrom rx_rf;
468 var ASP_Event rx_ev;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200469
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200470 g_pars := pars;
471
472 f_AMI_Adapter_connect();
473
474 f_AMI_Adapter_wait_rx_welcome_str();
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200475
476 while (true) {
477
478 alt {
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200479 [] IPL4.receive(ASP_RecvFrom:?) -> value rx_rf {
480 var charstring rx_str := f_ASP_RecvFrom_msg_to_charstring(rx_rf);
481 log("AMI rx: '", rx_str, "'");
482 buf := buf & rx_str;
483 log("AMI buf: '", buf, "'");
484
485 /* If several messages come together */
486 var boolean last_is_complete := f_str_endswith(buf, "\r\n\r\n");
487 var Misc_Helpers.ro_charstring msgs := f_str_split(buf, "\r\n\r\n");
488 log("AMI split: ", msgs);
489 if (lengthof(msgs) > 0) {
490 for (var integer i := 0; i < lengthof(msgs) - 1; i := i + 1) {
491 var charstring txt := msgs[i] & "\r\n";
492 msg := dec_AMI_Msg_ext(txt);
493 CLIENT.send(msg);
494 }
495 if (last_is_complete) {
496 var charstring txt := msgs[lengthof(msgs) - 1] & "\r\n";
497 msg := dec_AMI_Msg_ext(txt);
498 CLIENT.send(msg);
499 buf := "";
500 } else {
501 buf := msgs[lengthof(msgs) - 1];
502 }
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200503 }
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200504 log("AMI remain buf: '", buf, "'");
505 }
506 [] IPL4.receive(ASP_ConnId_ReadyToRelease:?) {
507 }
508
509 [] IPL4.receive(ASP_Event:?) -> value rx_ev {
510 log("Rx AMI ASP_Event: ", rx_ev);
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200511 }
512 [] CLIENT.receive(AMI_Msg:?) -> value msg {
513 /* TODO: in the future, queue Action if there's already one Action in transit, to fullfill AMI requirements. */
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200514 var charstring tx_txt := enc_AMI_Msg(msg) & "\r\n";
515
516 var ASP_SendTo tx := {
517 connId := g_self_conn_id,
518 remName := g_pars.remote_host,
519 remPort := g_pars.remote_port,
520 proto := { tcp := {} },
521 msg := char2oct(tx_txt)
522 };
523 IPL4.send(tx);
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200524 }
525 }
526 }
527}
528
529
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200530/*
531 * Functions:
532 */
533
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200534/* Generate a random "ActionId" value: */
535function f_gen_action_id() return charstring {
536 return hex2str(f_rnd_hexstring(16));
537}
538
539function f_ami_msg_find(AMI_Msg msg,
540 template (present) charstring key := ?)
541return template (omit) AMI_Field {
542 var integer i;
543
544 for (i := 0; i < lengthof(msg); i := i + 1) {
545 if (not ispresent(msg[i])) {
546 continue;
547 }
548 if (match(msg[i].key, key)) {
549 return msg[i];
550 }
551 }
552 return omit;
553}
554
555function f_ami_msg_find_or_fail(AMI_Msg msg,
556 template (present) charstring key := ?)
557return AMI_Field {
558 var template (omit) AMI_Field field;
559 field := f_ami_msg_find(msg, key);
560 if (istemplatekind(field, "omit")) {
561 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
562 log2str("Key ", key, " not found in ", msg));
563 }
564 return valueof(field);
565}
566
567function f_ami_msg_get_value(AMI_Msg msg,
568 template (present) charstring key := ?)
569return template (omit) charstring {
570 var template (omit) AMI_Field field;
571 field := f_ami_msg_find(msg, key);
572 if (istemplatekind(field, "omit")) {
573 return omit;
574 }
575 return field.val;
576}
577
578function f_ami_msg_get_value_or_fail(AMI_Msg msg,
579 template (present) charstring key := ?)
580return template charstring {
581 var AMI_Field field;
582 field := f_ami_msg_find_or_fail(msg, key);
583 return field.val;
584}
585
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200586function f_ami_transceive_ret(AMI_Msg_PT pt, template (value) AMI_Msg tx_msg, float rx_timeout := 10.0) return AMI_Msg {
587 var AMI_Msg rx_msg;
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200588 timer T;
589
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200590 T.start(rx_timeout);
591 pt.send(tx_msg);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200592 alt {
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200593 [] pt.receive(AMI_Msg:?) -> value rx_msg;
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200594 [] T.timeout {
595 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200596 log2str("AMI Response timeout: ", tx_msg));
597 }
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200598 }
599 T.stop;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200600 return rx_msg;
601
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200602}
603
Pau Espin Pedrole98f4412024-05-17 19:33:46 +0200604altstep as_ami_rx_ignore(AMI_Msg_PT pt)
605{
606 var AMI_Msg msg;
607 [] pt.receive(AMI_Msg:?) -> value msg {
608 log("Ignoring AMI message := ", msg);
609 repeat;
610 }
611}
612
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200613private altstep as_ami_rx_fail(AMI_Msg_PT pt, template AMI_Msg exp_msg := *)
614{
615 var AMI_Msg msg;
616 [] pt.receive(AMI_Msg:?) -> value msg {
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200617 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200618 log2str("Received unexpected AMI message := ", msg, "\nvs exp := ", exp_msg));
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200619 }
620}
621
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200622altstep as_ami_expect_msg(AMI_Msg_PT pt, template (present) AMI_Msg msg_expect, boolean fail_others := true)
623{
624 [] pt.receive(msg_expect);
625 [fail_others] as_ami_rx_fail(pt, msg_expect);
626}
627
628function f_ami_transceive_match(AMI_Msg_PT pt,
629 template (value) AMI_Msg tx_msg,
630 template (present) AMI_Msg exp_ret := ?,
631 boolean fail_others := true,
632 float rx_timeout := 10.0) return AMI_Msg {
633 var AMI_Msg rx_msg;
634 timer T;
635
636 T.start(rx_timeout);
637 pt.send(tx_msg);
638 alt {
639 [] pt.receive(exp_ret) -> value rx_msg;
640 [not fail_others] pt.receive(AMI_Msg:?) -> value rx_msg {
641 log("AMI: Ignoring Rx msg ", rx_msg);
642 repeat;
643 }
644 [fail_others] as_ami_rx_fail(pt, exp_ret);
645 [] T.timeout {
646 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
647 log2str("AMI Response timeout: ", tx_msg));
648 }
649 }
650 T.stop;
651 return rx_msg;
652}
653
654function f_ami_transceive_match_response_success(AMI_Msg_PT pt,
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200655 template (value) AMI_Msg tx_msg) {
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200656 var template (present) AMI_Msg exp_resp;
657 var template (omit) charstring action_id := f_ami_msg_get_value(valueof(tx_msg), AMI_FIELD_ACTION_ID);
658 if (isvalue(action_id)) {
659 exp_resp := tr_AMI_Response_Success_ActionId(action_id);
660 } else {
661 exp_resp := tr_AMI_Response_Success;
662 }
663 f_ami_transceive_match(pt, tx_msg, exp_resp);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200664}
665
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200666function f_ami_action_login(AMI_Msg_PT pt, charstring username, charstring secret) {
Pau Espin Pedrol77976a62024-05-08 21:17:38 +0200667 var charstring reg_action_id := f_gen_action_id();
668 f_ami_transceive_match_response_success(pt, ts_AMI_Action_Login(username, secret, reg_action_id));
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200669}
670
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200671function f_ami_action_PJSIPAccessNetworkInfo(AMI_Msg_PT pt,
672 template (value) charstring registration,
673 template (value) charstring info) {
674 var charstring reg_action_id := f_gen_action_id();
675 f_ami_transceive_match_response_success(pt, ts_AMI_Action_PJSIPAccessNetworkInfo(registration, info, reg_action_id));
676}
677
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200678function f_ami_action_PJSIPRegister(AMI_Msg_PT pt, charstring register) {
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200679 var charstring reg_action_id := f_gen_action_id();
680 f_ami_transceive_match_response_success(pt, ts_AMI_Action_PJSIPRegister(register, reg_action_id));
681}
682
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200683function f_ami_action_PJSIPUnregister(AMI_Msg_PT pt, charstring register) {
684 var charstring reg_action_id := f_gen_action_id();
685 f_ami_transceive_match_response_success(pt, ts_AMI_Action_PJSIPUnregister(register, reg_action_id));
686}
687
Pau Espin Pedrolddc4c702024-05-22 17:02:57 +0200688function f_ami_action_AuthResponse_AUTS(AMI_Msg_PT pt,
689 template (value) charstring registration,
690 template (value) charstring auts) {
691 var charstring reg_action_id := f_gen_action_id();
692 f_ami_transceive_match_response_success(pt, ts_AMI_Action_AuthResponse_AUTS(registration, auts, reg_action_id));
693}
694function f_ami_action_AuthResponse_RES(AMI_Msg_PT pt,
695 template (value) charstring registration,
696 template (value) charstring res,
697 template (value) charstring ck,
698 template (value) charstring ik) {
699 var charstring reg_action_id := f_gen_action_id();
700 f_ami_transceive_match_response_success(pt, ts_AMI_Action_AuthResponse_RES(registration, res, ck, ik, reg_action_id));
701}
702
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200703private function f_ami_selftest_decode(charstring txt) {
704 log("Text to decode: '", txt, "'");
705 var AMI_Msg msg := dec_AMI_Msg(txt);
706 log("AMI_Msg decoded: ", msg);
707}
708
709function f_ami_selftest() {
710 f_ami_selftest_decode("AppData: 5,0502: Call pjsip endpoint from 0501\r\n");
711}
712
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200713}