blob: fd188c3a3a441461dabd52d9a5b19b47797c33ba [file] [log] [blame]
Neels Hofmeyre9920f22017-07-10 15:07:22 +02001/* mgcp_utils - common functions to setup an MGCP connection
2 */
3/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
Harald Welte220f4872018-02-04 09:04:16 +01007 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
Neels Hofmeyre9920f22017-07-10 15:07:22 +02009 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte220f4872018-02-04 09:04:16 +010014 * GNU General Public License for more details.
Neels Hofmeyre9920f22017-07-10 15:07:22 +020015 *
Harald Welte220f4872018-02-04 09:04:16 +010016 * You should have received a copy of the GNU General Public License
Neels Hofmeyre9920f22017-07-10 15:07:22 +020017 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include <osmocom/core/linuxlist.h>
22#include <osmocom/core/select.h>
23#include <osmocom/core/write_queue.h>
24#include <osmocom/core/msgb.h>
25#include <osmocom/core/logging.h>
Philipp Maier1dc6be62017-10-05 18:25:37 +020026#include <osmocom/core/byteswap.h>
Harald Welte8890dfa2017-11-17 15:09:30 +010027#include <osmocom/core/socket.h>
Neels Hofmeyre9920f22017-07-10 15:07:22 +020028
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020029#include <osmocom/mgcp_client/mgcp_client.h>
30#include <osmocom/mgcp_client/mgcp_client_internal.h>
Neels Hofmeyre9920f22017-07-10 15:07:22 +020031
32#include <netinet/in.h>
33#include <arpa/inet.h>
34
35#include <errno.h>
36#include <unistd.h>
37#include <string.h>
Pau Espin Pedrol166077e2019-06-26 12:21:38 +020038#include <ctype.h>
Neels Hofmeyre9920f22017-07-10 15:07:22 +020039
Pau Espin Pedrol1b1d7ed2019-05-23 16:48:24 +020040#ifndef OSMUX_CID_MAX
41#define OSMUX_CID_MAX 255 /* FIXME: use OSMUX_CID_MAX from libosmo-netif? */
42#endif
43
Philipp Maier704c4f02018-06-07 18:51:31 +020044/* Codec descripton for dynamic payload types (SDP) */
Neels Hofmeyr47642f22019-02-27 05:56:53 +010045const struct value_string osmo_mgcpc_codec_names[] = {
Philipp Maier704c4f02018-06-07 18:51:31 +020046 { CODEC_PCMU_8000_1, "PCMU/8000/1" },
47 { CODEC_GSM_8000_1, "GSM/8000/1" },
48 { CODEC_PCMA_8000_1, "PCMA/8000/1" },
49 { CODEC_G729_8000_1, "G729/8000/1" },
50 { CODEC_GSMEFR_8000_1, "GSM-EFR/8000/1" },
51 { CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
52 { CODEC_AMR_8000_1, "AMR/8000/1" },
53 { CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
54 { 0, NULL },
55};
56
57/* Get encoding name from a full codec string e,g.
58 * ("CODEC/8000/2" => returns "CODEC") */
59static char *extract_codec_name(const char *str)
60{
61 static char buf[64];
62 unsigned int i;
63
64 if (!str)
65 return NULL;
66
67 /* FIXME osmo_strlcpy */
68 osmo_strlcpy(buf, str, sizeof(buf));
69
70 for (i = 0; i < strlen(buf); i++) {
71 if (buf[i] == '/')
72 buf[i] = '\0';
73 }
74
75 return buf;
76}
77
78/*! Map a string to a codec.
79 * \ptmap[in] str input string (e.g "GSM/8000/1", "GSM/8000" or "GSM")
80 * \returns codec that corresponds to the given string representation. */
81enum mgcp_codecs map_str_to_codec(const char *str)
82{
83 unsigned int i;
84 char *codec_name;
85 char str_buf[64];
86
87 osmo_strlcpy(str_buf, extract_codec_name(str), sizeof(str_buf));
88
Neels Hofmeyr47642f22019-02-27 05:56:53 +010089 for (i = 0; i < ARRAY_SIZE(osmo_mgcpc_codec_names); i++) {
90 codec_name = extract_codec_name(osmo_mgcpc_codec_names[i].str);
Philipp Maier704c4f02018-06-07 18:51:31 +020091 if (!codec_name)
92 continue;
93 if (strcmp(codec_name, str_buf) == 0)
Neels Hofmeyr47642f22019-02-27 05:56:53 +010094 return osmo_mgcpc_codec_names[i].value;
Philipp Maier704c4f02018-06-07 18:51:31 +020095 }
96
97 return -1;
98}
99
100/* Check the ptmap for illegal mappings */
Neels Hofmeyr84274e42019-04-27 19:08:36 +0200101static int check_ptmap(const struct ptmap *ptmap)
Philipp Maier704c4f02018-06-07 18:51:31 +0200102{
103 /* Check if there are mappings that leave the IANA assigned dynamic
104 * payload type range. Under normal conditions such mappings should
105 * not occur */
106
107 /* Its ok to have a 1:1 mapping in the statically defined
108 * range, this won't hurt */
109 if (ptmap->codec == ptmap->pt)
110 return 0;
111
112 if (ptmap->codec < 96 || ptmap->codec > 127)
113 goto error;
114 if (ptmap->pt < 96 || ptmap->pt > 127)
115 goto error;
116
117 return 0;
118error:
119 LOGP(DLMGCP, LOGL_ERROR,
120 "ptmap contains illegal mapping: codec=%u maps to pt=%u\n",
121 ptmap->codec, ptmap->pt);
122 return -1;
123}
124
125/*! Map a codec to a payload type.
126 * \ptmap[in] payload pointer to payload type map with specified payload types.
127 * \ptmap[in] ptmap_len length of the payload type map.
128 * \ptmap[in] codec the codec for which the payload type should be looked up.
129 * \returns assigned payload type */
Neels Hofmeyr84274e42019-04-27 19:08:36 +0200130unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
Philipp Maier704c4f02018-06-07 18:51:31 +0200131 enum mgcp_codecs codec)
132{
133 unsigned int i;
134
135 /*! Note: If the payload type map is empty or the codec is not found
136 * in the map, then a 1:1 mapping is performed. If the codec falls
137 * into the statically defined range or if the mapping table isself
138 * tries to map to the statically defined range, then the mapping
139 * is also ignored and a 1:1 mapping is performed instead. */
140
141 /* we may return the codec directly since enum mgcp_codecs directly
142 * corresponds to the statićally assigned payload types */
143 if (codec < 96 || codec > 127)
144 return codec;
145
146 for (i = 0; i < ptmap_len; i++) {
147 /* Skip illegal map entries */
148 if (check_ptmap(ptmap) == 0 && ptmap->codec == codec)
149 return ptmap->pt;
150 ptmap++;
151 }
152
153 /* If nothing is found, do not perform any mapping */
154 return codec;
155}
156
157/*! Map a payload type to a codec.
158 * \ptmap[in] payload pointer to payload type map with specified payload types.
159 * \ptmap[in] ptmap_len length of the payload type map.
160 * \ptmap[in] payload type for which the codec should be looked up.
161 * \returns codec that corresponds to the specified payload type */
162enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
163 unsigned int pt)
164{
165 unsigned int i;
166
167 /*! Note: If the payload type map is empty or the payload type is not
168 * found in the map, then a 1:1 mapping is performed. If the payload
169 * type falls into the statically defined range or if the mapping
170 * table isself tries to map to the statically defined range, then
171 * the mapping is also ignored and a 1:1 mapping is performed
172 * instead. */
173
174 /* See also note in map_codec_to_pt() */
175 if (pt < 96 || pt > 127)
176 return pt;
177
178 for (i = 0; i < ptmap_len; i++) {
179 if (check_ptmap(ptmap) == 0 && ptmap->pt == pt)
180 return ptmap->codec;
181 ptmap++;
182 }
183
184 /* If nothing is found, do not perform any mapping */
185 return pt;
186}
187
Philipp Maier275ac972018-01-20 00:58:16 +0100188/*! Initalize MGCP client configuration struct with default values.
189 * \param[out] conf Client configuration.*/
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200190void mgcp_client_conf_init(struct mgcp_client_conf *conf)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200191{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200192 /* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */
193 *conf = (struct mgcp_client_conf){
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200194 .local_addr = NULL,
195 .local_port = -1,
196 .remote_addr = NULL,
197 .remote_port = -1,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200198 };
199}
200
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200201static void mgcp_client_handle_response(struct mgcp_client *mgcp,
202 struct mgcp_response_pending *pending,
203 struct mgcp_response *response)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200204{
205 if (!pending) {
206 LOGP(DLMGCP, LOGL_ERROR,
207 "Cannot handle NULL response\n");
208 return;
209 }
210 if (pending->response_cb)
211 pending->response_cb(response, pending->priv);
212 else
Neels Hofmeyred0c1aa2018-12-21 03:07:53 +0100213 LOGP(DLMGCP, LOGL_DEBUG, "MGCP response ignored (NULL cb)\n");
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200214 talloc_free(pending);
215}
216
217static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
218{
219 int comment_pos;
220 char *end;
221
222 if (mgcp_msg_terminate_nul(msg))
223 goto response_parse_failure;
224
225 r->body = (char *)msg->data;
226
Harald Welte9bf7c532017-11-17 14:14:31 +0100227 if (sscanf(r->body, "%3d %u %n",
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200228 &r->head.response_code, &r->head.trans_id,
229 &comment_pos) != 2)
230 goto response_parse_failure;
231
Philipp Maierabe8c892018-01-20 00:15:12 +0100232 osmo_strlcpy(r->head.comment, r->body + comment_pos, sizeof(r->head.comment));
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200233 end = strchr(r->head.comment, '\r');
234 if (!end)
235 goto response_parse_failure;
236 /* Mark the end of the comment */
237 *end = '\0';
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200238 return 0;
239
240response_parse_failure:
241 LOGP(DLMGCP, LOGL_ERROR,
242 "Failed to parse MGCP response header\n");
243 return -EINVAL;
244}
245
246/* TODO undup against mgcp_protocol.c:mgcp_check_param() */
247static bool mgcp_line_is_valid(const char *line)
248{
249 const size_t line_len = strlen(line);
250 if (line[0] == '\0')
251 return true;
252
253 if (line_len < 2
254 || line[1] != '=') {
255 LOGP(DLMGCP, LOGL_ERROR,
256 "Wrong MGCP option format: '%s'\n",
257 line);
258 return false;
259 }
260
261 return true;
262}
263
Philipp Maier704c4f02018-06-07 18:51:31 +0200264/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
265static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200266{
Philipp Maier704c4f02018-06-07 18:51:31 +0200267 char *pt_str;
268 unsigned int pt;
269 unsigned int count = 0;
270 unsigned int i;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200271
Philipp Maier704c4f02018-06-07 18:51:31 +0200272 /* Extract port information */
273 if (sscanf(line, "m=audio %hu", &r->audio_port) != 1)
274 goto response_parse_failure_port;
Philipp Maier10f32db2017-12-13 12:34:34 +0100275 if (r->audio_port == 0)
Philipp Maier704c4f02018-06-07 18:51:31 +0200276 goto response_parse_failure_port;
Philipp Maier10f32db2017-12-13 12:34:34 +0100277
Philipp Maier704c4f02018-06-07 18:51:31 +0200278 /* Extract payload types */
279 line = strstr(line, "RTP/AVP ");
280 if (!line)
281 goto exit;
282
283 pt_str = strtok(line, " ");
284 while (1) {
285 /* Do not allow excessive payload types */
286 if (count > ARRAY_SIZE(r->codecs))
287 goto response_parse_failure_pt;
288
289 pt_str = strtok(NULL, " ");
290 if (!pt_str)
291 break;
292 pt = atoi(pt_str);
293
294 /* Do not allow duplicate payload types */
295 for (i = 0; i < count; i++)
296 if (r->codecs[i] == pt)
297 goto response_parse_failure_pt;
298
299 /* Note: The payload type we store may not necessarly match
300 * the codec types we have defined in enum mgcp_codecs. To
301 * ensure that the end result only contains codec types which
302 * match enum mgcp_codecs, we will go through afterwards and
303 * remap the affected entries with the inrofmation we learn
304 * from rtpmap */
305 r->codecs[count] = pt;
306 count++;
307 }
308
309 r->codecs_len = count;
310
311exit:
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200312 return 0;
313
Philipp Maier704c4f02018-06-07 18:51:31 +0200314response_parse_failure_port:
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200315 LOGP(DLMGCP, LOGL_ERROR,
Philipp Maier704c4f02018-06-07 18:51:31 +0200316 "Failed to parse SDP parameter port (%s)\n", line);
Philipp Maier06da85e2017-10-05 18:49:24 +0200317 return -EINVAL;
Philipp Maier704c4f02018-06-07 18:51:31 +0200318
319response_parse_failure_pt:
320 LOGP(DLMGCP, LOGL_ERROR,
321 "Failed to parse SDP parameter payload types (%s)\n", line);
322 return -EINVAL;
323}
324
325/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
326static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *line)
327{
328 unsigned int pt;
329 char codec_resp[64];
330 unsigned int codec;
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +0200331
332
Philipp Maier704c4f02018-06-07 18:51:31 +0200333 if (strstr(line, "ptime")) {
334 if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
335 goto response_parse_failure_ptime;
336 } else if (strstr(line, "rtpmap")) {
337 if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) {
338 /* The MGW may assign an own payload type in the
339 * response if the choosen codec falls into the IANA
340 * assigned dynamic payload type range (96-127).
341 * Normally the MGW should obey the 3gpp payload type
342 * assignments, which are fixed, so we likely wont see
343 * anything unexpected here. In order to be sure that
344 * we will now check the codec string and if the result
345 * does not match to what is IANA / 3gpp assigned, we
346 * will create an entry in the ptmap table so we can
347 * lookup later what has been assigned. */
348 codec = map_str_to_codec(codec_resp);
349 if (codec != pt) {
350 if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) {
351 r->ptmap[r->ptmap_len].pt = pt;
352 r->ptmap[r->ptmap_len].codec = codec;
353 r->ptmap_len++;
354 } else
355 goto response_parse_failure_rtpmap;
356 }
357
358 } else
359 goto response_parse_failure_rtpmap;
360 }
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +0200361
Philipp Maier704c4f02018-06-07 18:51:31 +0200362 return 0;
363
364response_parse_failure_ptime:
365 LOGP(DLMGCP, LOGL_ERROR,
366 "Failed to parse SDP parameter, invalid ptime (%s)\n", line);
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +0200367 return -EINVAL;
Philipp Maier704c4f02018-06-07 18:51:31 +0200368response_parse_failure_rtpmap:
369 LOGP(DLMGCP, LOGL_ERROR,
370 "Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +0200371 return -EINVAL;
Philipp Maier06da85e2017-10-05 18:49:24 +0200372}
373
374/* Parse a line like "c=IN IP4 10.11.12.13" */
375static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line)
376{
377 struct in_addr ip_test;
378
379 if (strlen(line) < 16)
380 goto response_parse_failure;
381
382 /* The current implementation strictly supports IPV4 only ! */
383 if (memcmp("c=IN IP4 ", line, 9) != 0)
384 goto response_parse_failure;
385
386 /* Extract IP-Address */
Philipp Maierf8bfbe82017-11-23 19:32:31 +0100387 osmo_strlcpy(r->audio_ip, line + 9, sizeof(r->audio_ip));
Philipp Maier06da85e2017-10-05 18:49:24 +0200388
389 /* Check IP-Address */
390 if (inet_aton(r->audio_ip, &ip_test) == 0)
391 goto response_parse_failure;
392
393 return 0;
394
395response_parse_failure:
396 LOGP(DLMGCP, LOGL_ERROR,
397 "Failed to parse MGCP response header (audio ip)\n");
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200398 return -EINVAL;
399}
400
Pau Espin Pedrol91088c32019-04-24 21:02:40 +0200401/*! Extract OSMUX CID from an MGCP parameter line (string).
402 * \param[in] line single parameter line from the MGCP message
403 * \returns OSMUX CID, -1 wildcard, -2 on error
404 * FIXME: This is a copy of function in mgcp_msg.c. Have some common.c file between both libs?
405 */
406static int mgcp_parse_osmux_cid(const char *line)
407{
408 int osmux_cid;
409
410
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200411 if (strcasecmp(line + 2, "Osmux: *") == 0) {
Pau Espin Pedrol91088c32019-04-24 21:02:40 +0200412 LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
413 return -1;
414 }
415
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200416 if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
Pau Espin Pedrol91088c32019-04-24 21:02:40 +0200417 LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
418 line);
419 return -2;
420 }
421
Pau Espin Pedrol91088c32019-04-24 21:02:40 +0200422 if (osmux_cid > OSMUX_CID_MAX) { /* OSMUX_CID_MAX from libosmo-netif */
423 LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
424 osmux_cid, OSMUX_CID_MAX);
425 return -2;
426 }
427 LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
428
429 return osmux_cid;
430}
431
Philipp Maier3b12e1b2018-01-18 15:16:13 +0100432/* A new section is marked by a double line break, check a few more
433 * patterns as there may be variants */
434static char *mgcp_find_section_end(char *string)
435{
436 char *rc;
437
438 rc = strstr(string, "\n\n");
439 if (rc)
440 return rc;
441
442 rc = strstr(string, "\n\r\n\r");
443 if (rc)
444 return rc;
445
446 rc = strstr(string, "\r\n\r\n");
447 if (rc)
448 return rc;
449
450 return NULL;
451}
452
Philipp Maier275ac972018-01-20 00:58:16 +0100453/*! Parse body (SDP) parameters of the MGCP response
454 * \param[in,out] r Response data
455 * \returns 0 on success, -EINVAL on error. */
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200456int mgcp_response_parse_params(struct mgcp_response *r)
457{
458 char *line;
459 int rc;
Neels Hofmeyr0793d2f2018-02-21 14:55:34 +0100460 char *data;
Philipp Maiere9d645b2018-01-19 23:54:08 +0100461 char *data_ptr;
Philipp Maier704c4f02018-06-07 18:51:31 +0200462 int i;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200463
Philipp Maiere9d645b2018-01-19 23:54:08 +0100464 /* Since this functions performs a destructive parsing, we create a
465 * local copy of the body data */
Neels Hofmeyr0793d2f2018-02-21 14:55:34 +0100466 OSMO_ASSERT(r->body);
467 data = talloc_strdup(r, r->body);
Philipp Maiere9d645b2018-01-19 23:54:08 +0100468 OSMO_ASSERT(data);
Philipp Maier55295f72018-01-15 14:00:28 +0100469
Philipp Maiere9d645b2018-01-19 23:54:08 +0100470 /* Find beginning of the parameter (SDP) section */
471 data_ptr = mgcp_find_section_end(data);
Neels Hofmeyr10835332018-02-21 14:55:34 +0100472 if (!data_ptr) {
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200473 LOGP(DLMGCP, LOGL_ERROR,
Neels Hofmeyr0793d2f2018-02-21 14:55:34 +0100474 "MGCP response: cannot find start of SDP parameters\n");
Philipp Maiere9d645b2018-01-19 23:54:08 +0100475 rc = -EINVAL;
476 goto exit;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200477 }
478
Neels Hofmeyr0793d2f2018-02-21 14:55:34 +0100479 /* data_ptr now points to the beginning of the section-end-marker; for_each_non_empty_line()
480 * skips any \r and \n characters for free, so we don't need to skip the marker. */
481
Philipp Maiere9d645b2018-01-19 23:54:08 +0100482 for_each_non_empty_line(line, data_ptr) {
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200483 if (!mgcp_line_is_valid(line))
484 return -EINVAL;
485
486 switch (line[0]) {
Philipp Maier704c4f02018-06-07 18:51:31 +0200487 case 'a':
488 rc = mgcp_parse_audio_ptime_rtpmap(r, line);
489 if (rc)
490 goto exit;
491 break;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200492 case 'm':
Philipp Maier704c4f02018-06-07 18:51:31 +0200493 rc = mgcp_parse_audio_port_pt(r, line);
Philipp Maier06da85e2017-10-05 18:49:24 +0200494 if (rc)
Philipp Maiere9d645b2018-01-19 23:54:08 +0100495 goto exit;
Philipp Maier06da85e2017-10-05 18:49:24 +0200496 break;
497 case 'c':
498 rc = mgcp_parse_audio_ip(r, line);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200499 if (rc)
Philipp Maiere9d645b2018-01-19 23:54:08 +0100500 goto exit;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200501 break;
502 default:
503 /* skip unhandled parameters */
504 break;
505 }
506 }
Philipp Maiere9d645b2018-01-19 23:54:08 +0100507
Philipp Maier704c4f02018-06-07 18:51:31 +0200508 /* See also note in mgcp_parse_audio_port_pt() */
509 for (i = 0; i < r->codecs_len; i++)
510 r->codecs[i] = map_pt_to_codec(r->ptmap, r->ptmap_len, r->codecs[i]);
511
Philipp Maiere9d645b2018-01-19 23:54:08 +0100512 rc = 0;
513exit:
514 talloc_free(data);
515 return rc;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200516}
517
Philipp Maier55295f72018-01-15 14:00:28 +0100518/* Parse a line like "X: something" */
519static int mgcp_parse_head_param(char *result, unsigned int result_len,
520 char label, const char *line)
Philipp Maierffd75e42017-11-22 11:44:50 +0100521{
Philipp Maier55295f72018-01-15 14:00:28 +0100522 char label_string[4];
Neels Hofmeyr23e7bf12018-09-03 21:26:22 +0200523 size_t rc;
Philipp Maier55295f72018-01-15 14:00:28 +0100524
525 /* Detect empty parameters */
Philipp Maierffd75e42017-11-22 11:44:50 +0100526 if (strlen(line) < 4)
527 goto response_parse_failure;
528
Philipp Maier55295f72018-01-15 14:00:28 +0100529 /* Check if the label matches */
530 snprintf(label_string, sizeof(label_string), "%c: ", label);
531 if (memcmp(label_string, line, 3) != 0)
Philipp Maierffd75e42017-11-22 11:44:50 +0100532 goto response_parse_failure;
533
Philipp Maier55295f72018-01-15 14:00:28 +0100534 /* Copy payload part of the string to destinations (the label string
535 * is always 3 chars long) */
Neels Hofmeyr23e7bf12018-09-03 21:26:22 +0200536 rc = osmo_strlcpy(result, line + 3, result_len);
537 if (rc >= result_len) {
538 LOGP(DLMGCP, LOGL_ERROR,
539 "Failed to parse MGCP response (parameter label: %c):"
540 " the received conn ID is too long: %zu, maximum is %u characters\n",
541 label, rc, result_len - 1);
542 return -ENOSPC;
543 }
Philipp Maierffd75e42017-11-22 11:44:50 +0100544 return 0;
545
546response_parse_failure:
547 LOGP(DLMGCP, LOGL_ERROR,
Philipp Maier55295f72018-01-15 14:00:28 +0100548 "Failed to parse MGCP response (parameter label: %c)\n", label);
Philipp Maierffd75e42017-11-22 11:44:50 +0100549 return -EINVAL;
550}
551
552/* Parse MGCP parameters of the response */
553static int parse_head_params(struct mgcp_response *r)
554{
555 char *line;
556 int rc = 0;
557 OSMO_ASSERT(r->body);
Philipp Maier55295f72018-01-15 14:00:28 +0100558 char *data;
559 char *data_ptr;
560 char *data_end;
Philipp Maierffd75e42017-11-22 11:44:50 +0100561
Philipp Maier55295f72018-01-15 14:00:28 +0100562 /* Since this functions performs a destructive parsing, we create a
563 * local copy of the body data */
Philipp Maier1fc69992018-01-31 15:32:55 +0100564 data = talloc_zero_size(r, strlen(r->body)+1);
Philipp Maier55295f72018-01-15 14:00:28 +0100565 OSMO_ASSERT(data);
566 data_ptr = data;
567 osmo_strlcpy(data, r->body, strlen(r->body));
568
569 /* If there is an SDP body attached, prevent for_each_non_empty_line()
570 * into running in there, we are not yet interested in the parameters
571 * stored there. */
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +0200572 data_end = mgcp_find_section_end(data);
Philipp Maierffd75e42017-11-22 11:44:50 +0100573 if (data_end)
574 *data_end = '\0';
575
Philipp Maier55295f72018-01-15 14:00:28 +0100576 for_each_non_empty_line(line, data_ptr) {
Pau Espin Pedrol166077e2019-06-26 12:21:38 +0200577 switch (toupper(line[0])) {
Philipp Maier55295f72018-01-15 14:00:28 +0100578 case 'Z':
579 rc = mgcp_parse_head_param(r->head.endpoint,
580 sizeof(r->head.endpoint),
581 'Z', line);
582 if (rc)
583 goto exit;
Philipp Maier771b26a2018-01-31 13:53:11 +0100584
585 /* A specific endpoint identifier returned by the MGW
586 * must not contain any wildcard characters */
587 if (strstr(r->head.endpoint, "*") != NULL) {
588 rc = -EINVAL;
589 goto exit;
590 }
Philipp Maier3261dc72018-01-31 14:03:13 +0100591
592 /* A specific endpoint identifier returned by the MGW
593 * must contain an @ character */
594 if (strstr(r->head.endpoint, "@") == NULL) {
595 rc = -EINVAL;
596 goto exit;
597 }
Philipp Maier55295f72018-01-15 14:00:28 +0100598 break;
Philipp Maierffd75e42017-11-22 11:44:50 +0100599 case 'I':
Philipp Maier55295f72018-01-15 14:00:28 +0100600 rc = mgcp_parse_head_param(r->head.conn_id,
601 sizeof(r->head.conn_id),
602 'I', line);
Philipp Maierffd75e42017-11-22 11:44:50 +0100603 if (rc)
604 goto exit;
605 break;
Pau Espin Pedrol91088c32019-04-24 21:02:40 +0200606 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200607 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol91088c32019-04-24 21:02:40 +0200608 rc = mgcp_parse_osmux_cid(line);
609 if (rc < 0) {
610 /* -1: we don't want wildcards in response. -2: error */
611 rc = -EINVAL;
612 goto exit;
613 }
614 r->head.x_osmo_osmux_use = true;
615 r->head.x_osmo_osmux_cid = (uint8_t) rc;
616 rc = 0;
617 break;
618 }
619 /* Ignore unknown X-headers */
620 break;
Philipp Maierffd75e42017-11-22 11:44:50 +0100621 default:
622 /* skip unhandled parameters */
623 break;
624 }
625 }
626exit:
Philipp Maier55295f72018-01-15 14:00:28 +0100627 talloc_free(data);
Philipp Maierffd75e42017-11-22 11:44:50 +0100628 return rc;
629}
630
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200631static struct mgcp_response_pending *mgcp_client_response_pending_get(
632 struct mgcp_client *mgcp,
Neels Hofmeyrc8f37cb2017-11-30 13:43:11 +0100633 mgcp_trans_id_t trans_id)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200634{
635 struct mgcp_response_pending *pending;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200636 llist_for_each_entry(pending, &mgcp->responses_pending, entry) {
Neels Hofmeyrc8f37cb2017-11-30 13:43:11 +0100637 if (pending->trans_id == trans_id) {
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200638 llist_del(&pending->entry);
639 return pending;
640 }
641 }
642 return NULL;
643}
644
645/* Feed an MGCP message into the receive processing.
646 * Parse the head and call any callback registered for the transaction id found
647 * in the MGCP message. This is normally called directly from the internal
648 * mgcp_do_read that reads from the socket connected to the MGCP gateway. This
649 * function is published mainly to be able to feed data from the test suite.
650 */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200651int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200652{
Philipp Maier1fc69992018-01-31 15:32:55 +0100653 struct mgcp_response *r;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200654 struct mgcp_response_pending *pending;
655 int rc;
656
Philipp Maier1fc69992018-01-31 15:32:55 +0100657 r = talloc_zero(mgcp, struct mgcp_response);
658 OSMO_ASSERT(r);
659
660 rc = mgcp_response_parse_head(r, msg);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200661 if (rc) {
Philipp Maierffd75e42017-11-22 11:44:50 +0100662 LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head)\n");
Philipp Maier1fc69992018-01-31 15:32:55 +0100663 rc = 1;
664 goto error;
Philipp Maierffd75e42017-11-22 11:44:50 +0100665 }
666
Philipp Maier1fc69992018-01-31 15:32:55 +0100667 rc = parse_head_params(r);
Philipp Maierffd75e42017-11-22 11:44:50 +0100668 if (rc) {
669 LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n");
Philipp Maier1fc69992018-01-31 15:32:55 +0100670 rc = 1;
671 goto error;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200672 }
673
Philipp Maier1fc69992018-01-31 15:32:55 +0100674 pending = mgcp_client_response_pending_get(mgcp, r->head.trans_id);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200675 if (!pending) {
676 LOGP(DLMGCP, LOGL_ERROR,
677 "Cannot find matching MGCP transaction for trans_id %d\n",
Philipp Maier1fc69992018-01-31 15:32:55 +0100678 r->head.trans_id);
679 rc = -ENOENT;
680 goto error;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200681 }
682
Philipp Maier1fc69992018-01-31 15:32:55 +0100683 mgcp_client_handle_response(mgcp, pending, r);
684 rc = 0;
685
686error:
687 talloc_free(r);
688 return rc;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200689}
690
691static int mgcp_do_read(struct osmo_fd *fd)
692{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200693 struct mgcp_client *mgcp = fd->data;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200694 struct msgb *msg;
695 int ret;
696
697 msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
698 if (!msg) {
699 LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n");
700 return -1;
701 }
702
703 ret = read(fd->fd, msg->data, 4096 - 128);
704 if (ret <= 0) {
Neels Hofmeyr0a403792018-12-12 03:10:18 +0100705 LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %s: %d='%s'\n", osmo_sock_get_name2(fd->fd),
706 errno, strerror(errno));
707
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200708 msgb_free(msg);
709 return -1;
710 } else if (ret > 4096 - 128) {
Neels Hofmeyr0a403792018-12-12 03:10:18 +0100711 LOGP(DLMGCP, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200712 msgb_free(msg);
713 return -1;
Harald Welte9bf7c532017-11-17 14:14:31 +0100714 }
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200715
716 msg->l2h = msgb_put(msg, ret);
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200717 ret = mgcp_client_rx(mgcp, msg);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200718 talloc_free(msg);
719 return ret;
720}
721
722static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
723{
724 int ret;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200725
Neels Hofmeyr0a403792018-12-12 03:10:18 +0100726 LOGP(DLMGCP, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n",
727 osmo_sock_get_name2(fd->fd), msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len)));
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200728
729 ret = write(fd->fd, msg->data, msg->len);
730 if (ret != msg->len)
Neels Hofmeyr0a403792018-12-12 03:10:18 +0100731 LOGP(DLMGCP, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
732 osmo_sock_get_name2(fd->fd), errno, strerror(errno),
Neels Hofmeyr32d15cc2018-12-12 03:05:33 +0100733 msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len)));
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200734 return ret;
735}
736
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200737struct mgcp_client *mgcp_client_init(void *ctx,
738 struct mgcp_client_conf *conf)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200739{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200740 struct mgcp_client *mgcp;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200741
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200742 mgcp = talloc_zero(ctx, struct mgcp_client);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200743
744 INIT_LLIST_HEAD(&mgcp->responses_pending);
745 INIT_LLIST_HEAD(&mgcp->inuse_endpoints);
746
747 mgcp->next_trans_id = 1;
748
749 mgcp->actual.local_addr = conf->local_addr ? conf->local_addr :
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200750 MGCP_CLIENT_LOCAL_ADDR_DEFAULT;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200751 mgcp->actual.local_port = conf->local_port >= 0 ? (uint16_t)conf->local_port :
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200752 MGCP_CLIENT_LOCAL_PORT_DEFAULT;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200753
754 mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr :
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200755 MGCP_CLIENT_REMOTE_ADDR_DEFAULT;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200756 mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200757 MGCP_CLIENT_REMOTE_PORT_DEFAULT;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200758
Neels Hofmeyrac69ea92018-12-19 00:27:50 +0100759 if (osmo_strlcpy(mgcp->actual.endpoint_domain_name, conf->endpoint_domain_name,
760 sizeof(mgcp->actual.endpoint_domain_name))
761 >= sizeof(mgcp->actual.endpoint_domain_name)) {
762 LOGP(DLMGCP, LOGL_ERROR, "MGCP client: endpoint domain name is too long, max length is %zu: '%s'\n",
763 sizeof(mgcp->actual.endpoint_domain_name) - 1, conf->endpoint_domain_name);
764 talloc_free(mgcp);
765 return NULL;
766 }
767 LOGP(DLMGCP, LOGL_NOTICE, "MGCP client: using endpoint domain '@%s'\n", mgcp_client_endpoint_domain(mgcp));
768
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200769 return mgcp;
770}
771
Philipp Maier6a26c162018-08-01 16:37:06 +0200772static int init_socket(struct mgcp_client *mgcp)
773{
774 int rc;
775 struct osmo_wqueue *wq;
776 int i;
777
778 wq = &mgcp->wq;
779
780 for (i = 0; i < 100; i++) {
781
782 /* Initalize socket with the currently configured port
783 * number */
784 rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
785 mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
786 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
787 if (rc > 0)
788 return rc;
789
790 /* If there is a different port than the default port
791 * configured then we assume that the user has choosen
792 * that port conciously and we will not try to resolve
793 * this by silently choosing a different port. */
Philipp Maier9a7ccc32018-08-09 11:41:19 +0200794 if (mgcp->actual.local_port != MGCP_CLIENT_LOCAL_PORT_DEFAULT && i == 0)
Philipp Maier6a26c162018-08-01 16:37:06 +0200795 return -EINVAL;
796
797 /* Choose a new port number to try next */
798 LOGP(DLMGCP, LOGL_NOTICE,
Neels Hofmeyr0a403792018-12-12 03:10:18 +0100799 "MGCPGW failed to bind to %s:%u, retrying with port %u\n",
800 mgcp->actual.local_addr, mgcp->actual.local_port, mgcp->actual.local_port + 1);
Philipp Maier6a26c162018-08-01 16:37:06 +0200801 mgcp->actual.local_port++;
802 }
803
Neels Hofmeyr0a403792018-12-12 03:10:18 +0100804 LOGP(DLMGCP, LOGL_FATAL, "MGCPGW failed to find a port to bind on %i times.\n", i);
Philipp Maier6a26c162018-08-01 16:37:06 +0200805 return -EINVAL;
806}
807
Philipp Maier275ac972018-01-20 00:58:16 +0100808/*! Initalize client connection (opens socket only, no request is sent yet)
809 * \param[in,out] mgcp MGCP client descriptor.
810 * \returns 0 on success, -EINVAL on error. */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200811int mgcp_client_connect(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200812{
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200813 struct sockaddr_in addr;
814 struct osmo_wqueue *wq;
815 int rc;
816
817 if (!mgcp) {
818 LOGP(DLMGCP, LOGL_FATAL, "MGCPGW client not initialized properly\n");
819 return -EINVAL;
820 }
821
822 wq = &mgcp->wq;
823
Philipp Maier6a26c162018-08-01 16:37:06 +0200824 rc = init_socket(mgcp);
Harald Welte8890dfa2017-11-17 15:09:30 +0100825 if (rc < 0) {
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200826 LOGP(DLMGCP, LOGL_FATAL,
Harald Welte8890dfa2017-11-17 15:09:30 +0100827 "Failed to initialize socket %s:%u -> %s:%u for MGCP GW: %s\n",
828 mgcp->actual.local_addr, mgcp->actual.local_port,
829 mgcp->actual.remote_addr, mgcp->actual.remote_port, strerror(errno));
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200830 goto error_close_fd;
831 }
832
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200833 inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200834 mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
835
836 osmo_wqueue_init(wq, 10);
837 wq->bfd.when = BSC_FD_READ;
838 wq->bfd.data = mgcp;
839 wq->read_cb = mgcp_do_read;
840 wq->write_cb = mgcp_do_write;
841
Neels Hofmeyr0a403792018-12-12 03:10:18 +0100842 LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s\n", osmo_sock_get_name2(wq->bfd.fd));
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200843
844 return 0;
845error_close_fd:
846 close(wq->bfd.fd);
847 wq->bfd.fd = -1;
848 return rc;
849}
850
Philipp Maier275ac972018-01-20 00:58:16 +0100851/*! Get the IP-Aaddress of the associated MGW as string.
852 * \param[in] mgcp MGCP client descriptor.
853 * \returns a pointer to the address string. */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200854const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200855{
856 return mgcp->actual.remote_addr;
857}
858
Philipp Maier275ac972018-01-20 00:58:16 +0100859/*! Get the IP-Port of the associated MGW.
860 * \param[in] mgcp MGCP client descriptor.
861 * \returns port number. */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200862uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200863{
864 return mgcp->actual.remote_port;
865}
866
Philipp Maier275ac972018-01-20 00:58:16 +0100867/*! Get the IP-Aaddress of the associated MGW as its numeric representation.
868 * \param[in] mgcp MGCP client descriptor.
869 * \returns IP-Address as 32 bit integer (network byte order) */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200870uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200871{
872 return mgcp->remote_addr;
873}
874
Neels Hofmeyrac69ea92018-12-19 00:27:50 +0100875/* To compose endpoint names, usually for CRCX, use this as domain name.
876 * For example, snprintf("rtpbridge\*@%s", mgcp_client_endpoint_domain(mgcp)). */
877const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp)
878{
879 return mgcp->actual.endpoint_domain_name[0] ? mgcp->actual.endpoint_domain_name : "mgw";
880}
881
Pau Espin Pedrolca0818c2019-04-16 17:23:38 +0200882static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, char *name)
Neels Hofmeyrac69ea92018-12-19 00:27:50 +0100883{
884 static char endpoint[MGCP_ENDPOINT_MAXLEN];
885 int rc;
886
Pau Espin Pedrolca0818c2019-04-16 17:23:38 +0200887 rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp));
Neels Hofmeyrac69ea92018-12-19 00:27:50 +0100888 if (rc > sizeof(endpoint) - 1) {
Pau Espin Pedrolca0818c2019-04-16 17:23:38 +0200889 LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '%s@%s'\n",
890 sizeof(endpoint) - 1, name, mgcp_client_endpoint_domain(mgcp));
Neels Hofmeyrac69ea92018-12-19 00:27:50 +0100891 return NULL;
892 }
893 if (rc < 1) {
894 LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP endpoint name\n");
895 return NULL;
896 }
897 return endpoint;
898}
899
Pau Espin Pedrolca0818c2019-04-16 17:23:38 +0200900const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
901{
902 return _mgcp_client_name_append_domain(mgcp, "rtpbridge/*");
903}
904
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200905struct mgcp_response_pending * mgcp_client_pending_add(
906 struct mgcp_client *mgcp,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200907 mgcp_trans_id_t trans_id,
908 mgcp_response_cb_t response_cb,
909 void *priv)
910{
911 struct mgcp_response_pending *pending;
912
913 pending = talloc_zero(mgcp, struct mgcp_response_pending);
914 pending->trans_id = trans_id;
915 pending->response_cb = response_cb;
916 pending->priv = priv;
917 llist_add_tail(&pending->entry, &mgcp->responses_pending);
918
919 return pending;
920}
921
922/* Send the MGCP message in msg to the MGCP GW and handle a response with
923 * response_cb. NOTE: the response_cb still needs to call
924 * mgcp_response_parse_params(response) to get the parsed parameters -- to
925 * potentially save some CPU cycles, only the head line has been parsed when
Neels Hofmeyrc8f37cb2017-11-30 13:43:11 +0100926 * the response_cb is invoked.
927 * Before the priv pointer becomes invalid, e.g. due to transaction timeout,
928 * mgcp_client_cancel() needs to be called for this transaction.
929 */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200930int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
931 mgcp_response_cb_t response_cb, void *priv)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200932{
933 struct mgcp_response_pending *pending;
934 mgcp_trans_id_t trans_id;
935 int rc;
936
937 trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID];
938 if (!trans_id) {
939 LOGP(DLMGCP, LOGL_ERROR,
940 "Unset transaction id in mgcp send request\n");
941 talloc_free(msg);
942 return -EINVAL;
943 }
944
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200945 pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200946
947 if (msgb_l2len(msg) > 4096) {
948 LOGP(DLMGCP, LOGL_ERROR,
949 "Cannot send, MGCP message too large: %u\n",
950 msgb_l2len(msg));
951 msgb_free(msg);
952 rc = -EINVAL;
953 goto mgcp_tx_error;
954 }
955
956 rc = osmo_wqueue_enqueue(&mgcp->wq, msg);
957 if (rc) {
958 LOGP(DLMGCP, LOGL_FATAL, "Could not queue message to MGCP GW\n");
959 msgb_free(msg);
960 goto mgcp_tx_error;
961 } else
Neels Hofmeyred0c1aa2018-12-21 03:07:53 +0100962 LOGP(DLMGCP, LOGL_DEBUG, "Queued %u bytes for MGCP GW\n",
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200963 msgb_l2len(msg));
964 return 0;
965
966mgcp_tx_error:
967 /* Pass NULL to response cb to indicate an error */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200968 mgcp_client_handle_response(mgcp, pending, NULL);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200969 return -1;
970}
971
Philipp Maier275ac972018-01-20 00:58:16 +0100972/*! Cancel a pending transaction.
973 * \param[in] mgcp MGCP client descriptor.
974 * \param[in,out] trans_id Transaction id.
975 * \returns 0 on success, -ENOENT on error.
976 *
Neels Hofmeyrc8f37cb2017-11-30 13:43:11 +0100977 * Should a priv pointer passed to mgcp_client_tx() become invalid, this function must be called. In
978 * practical terms, if the caller of mgcp_client_tx() wishes to tear down a transaction without having
979 * received a response this function must be called. The trans_id can be obtained by calling
Philipp Maier275ac972018-01-20 00:58:16 +0100980 * mgcp_msg_trans_id() on the msgb produced by mgcp_msg_gen(). */
Neels Hofmeyrc8f37cb2017-11-30 13:43:11 +0100981int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
982{
983 struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id);
984 if (!pending) {
Philipp Maier275ac972018-01-20 00:58:16 +0100985 /*! Note: it is not harmful to cancel a transaction twice. */
Neels Hofmeyred0c1aa2018-12-21 03:07:53 +0100986 LOGP(DLMGCP, LOGL_ERROR, "Cannot cancel, no such transaction: %u\n", trans_id);
Neels Hofmeyrc8f37cb2017-11-30 13:43:11 +0100987 return -ENOENT;
988 }
Neels Hofmeyred0c1aa2018-12-21 03:07:53 +0100989 LOGP(DLMGCP, LOGL_DEBUG, "Canceled transaction %u\n", trans_id);
Neels Hofmeyrc8f37cb2017-11-30 13:43:11 +0100990 talloc_free(pending);
991 return 0;
Philipp Maier275ac972018-01-20 00:58:16 +0100992 /*! We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent
993 * out and is no longer in the wqueue. If it still is in the wqueue, then sending MGCP messages
994 * per se is broken and the program should notice so by a full wqueue. Even if this was called
995 * before we had a chance to send out the message and it is still going to be sent, we will just
996 * ignore the reply to it later. Removing a msgb from the wqueue here would just introduce more
997 * bug surface in terms of failing to update wqueue API's counters or some such.
Neels Hofmeyrc8f37cb2017-11-30 13:43:11 +0100998 */
999}
1000
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +02001001static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +02001002{
1003 /* avoid zero trans_id to distinguish from unset trans_id */
1004 if (!mgcp->next_trans_id)
1005 mgcp->next_trans_id ++;
1006 return mgcp->next_trans_id ++;
1007}
1008
Philipp Maier1dc6be62017-10-05 18:25:37 +02001009#define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
1010 MGCP_MSG_PRESENCE_CALL_ID | \
Philipp Maier1dc6be62017-10-05 18:25:37 +02001011 MGCP_MSG_PRESENCE_CONN_MODE)
1012#define MGCP_MDCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
Philipp Maier490cbaa2018-01-22 17:32:38 +01001013 MGCP_MSG_PRESENCE_CALL_ID | \
Philipp Maier1dc6be62017-10-05 18:25:37 +02001014 MGCP_MSG_PRESENCE_CONN_ID)
1015#define MGCP_DLCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
1016#define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
1017#define MGCP_RSIP_MANDATORY 0 /* none */
1018
Philipp Maier704c4f02018-06-07 18:51:31 +02001019/* Helper function for mgcp_msg_gen(): Add LCO information to MGCP message */
1020static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
1021{
1022 unsigned int i;
1023 int rc = 0;
1024 const char *codec;
1025 unsigned int pt;
1026
1027 rc += msgb_printf(msg, "L:");
1028
1029 if (mgcp_msg->ptime)
1030 rc += msgb_printf(msg, " p:%u,", mgcp_msg->ptime);
1031
1032 if (mgcp_msg->codecs_len) {
1033 rc += msgb_printf(msg, " a:");
1034 for (i = 0; i < mgcp_msg->codecs_len; i++) {
1035 pt = mgcp_msg->codecs[i];
Neels Hofmeyr47642f22019-02-27 05:56:53 +01001036 codec = get_value_string_or_null(osmo_mgcpc_codec_names, pt);
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +02001037
Philipp Maier704c4f02018-06-07 18:51:31 +02001038 /* Note: Use codec descriptors from enum mgcp_codecs
1039 * in mgcp_client only! */
1040 OSMO_ASSERT(codec);
1041 rc += msgb_printf(msg, "%s", extract_codec_name(codec));
1042 if (i < mgcp_msg->codecs_len - 1)
1043 rc += msgb_printf(msg, ";");
1044 }
1045 rc += msgb_printf(msg, ",");
1046 }
1047
1048 rc += msgb_printf(msg, " nt:IN\r\n");
1049
1050 return rc;
1051}
1052
1053/* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */
1054static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_client *mgcp)
1055{
1056 unsigned int i;
1057 int rc = 0;
1058 char local_ip[INET_ADDRSTRLEN];
1059 const char *codec;
1060 unsigned int pt;
1061
1062 /* Add separator to mark the beginning of the SDP block */
1063 rc += msgb_printf(msg, "\r\n");
1064
1065 /* Add SDP protocol version */
1066 rc += msgb_printf(msg, "v=0\r\n");
1067
1068 /* Determine local IP-Address */
1069 if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
1070 LOGP(DLMGCP, LOGL_ERROR,
1071 "Could not determine local IP-Address!\n");
1072 msgb_free(msg);
1073 return -2;
1074 }
1075
1076 /* Add owner/creator (SDP) */
1077 rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
1078 mgcp_msg->call_id, local_ip);
1079
1080 /* Add session name (none) */
1081 rc += msgb_printf(msg, "s=-\r\n");
1082
1083 /* Add RTP address and port */
1084 if (mgcp_msg->audio_port == 0) {
1085 LOGP(DLMGCP, LOGL_ERROR,
1086 "Invalid port number, can not generate MGCP message\n");
1087 msgb_free(msg);
1088 return -2;
1089 }
1090 if (strlen(mgcp_msg->audio_ip) <= 0) {
1091 LOGP(DLMGCP, LOGL_ERROR,
1092 "Empty ip address, can not generate MGCP message\n");
1093 msgb_free(msg);
1094 return -2;
1095 }
1096 rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
1097
1098 /* Add time description, active time (SDP) */
1099 rc += msgb_printf(msg, "t=0 0\r\n");
1100
1101 rc += msgb_printf(msg, "m=audio %u RTP/AVP", mgcp_msg->audio_port);
1102 for (i = 0; i < mgcp_msg->codecs_len; i++) {
1103 pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
1104 rc += msgb_printf(msg, " %u", pt);
1105
1106 }
1107 rc += msgb_printf(msg, "\r\n");
1108
Philipp Maier228e5912019-03-05 13:56:59 +01001109 /* Add optional codec parameters (fmtp) */
1110 if (mgcp_msg->param_present) {
1111 for (i = 0; i < mgcp_msg->codecs_len; i++) {
1112 /* The following is only applicable for AMR */
1113 if (mgcp_msg->codecs[i] != CODEC_AMR_8000_1 && mgcp_msg->codecs[i] != CODEC_AMRWB_16000_1)
1114 continue;
1115 pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
1116 if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned)
1117 rc += msgb_printf(msg, "a=fmtp:%u octet-align=1\r\n", pt);
1118 else if (mgcp_msg->param.amr_octet_aligned_present && !mgcp_msg->param.amr_octet_aligned)
1119 rc += msgb_printf(msg, "a=fmtp:%u octet-align=0\r\n", pt);
1120 }
1121 }
1122
Philipp Maier704c4f02018-06-07 18:51:31 +02001123 for (i = 0; i < mgcp_msg->codecs_len; i++) {
1124 pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +02001125
Philipp Maier704c4f02018-06-07 18:51:31 +02001126 /* Note: Only dynamic payload type from the range 96-127
1127 * require to be explained further via rtpmap. All others
1128 * are implcitly definedby the number in m=audio */
1129 if (pt >= 96 && pt <= 127) {
Neels Hofmeyr47642f22019-02-27 05:56:53 +01001130 codec = get_value_string_or_null(osmo_mgcpc_codec_names, mgcp_msg->codecs[i]);
Philipp Maier704c4f02018-06-07 18:51:31 +02001131
1132 /* Note: Use codec descriptors from enum mgcp_codecs
1133 * in mgcp_client only! */
1134 OSMO_ASSERT(codec);
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +02001135
Philipp Maier704c4f02018-06-07 18:51:31 +02001136 rc += msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec);
1137 }
1138 }
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +02001139
Philipp Maier704c4f02018-06-07 18:51:31 +02001140 if (mgcp_msg->ptime)
1141 rc += msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime);
1142
1143 return rc;
1144}
1145
Philipp Maier275ac972018-01-20 00:58:16 +01001146/*! Generate an MGCP message
1147 * \param[in] mgcp MGCP client descriptor.
1148 * \param[in] mgcp_msg Message description
1149 * \returns message buffer on success, NULL on error. */
Philipp Maier1dc6be62017-10-05 18:25:37 +02001150struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
1151{
1152 mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
1153 uint32_t mandatory_mask;
1154 struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
1155 int rc = 0;
Philipp Maier704c4f02018-06-07 18:51:31 +02001156 int rc_sdp;
1157 bool use_sdp = false;
Pau Espin Pedrol900cd652019-04-24 22:06:22 +02001158 char buf[32];
Philipp Maier1dc6be62017-10-05 18:25:37 +02001159
1160 msg->l2h = msg->data;
1161 msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
1162
1163 /* Add command verb */
1164 switch (mgcp_msg->verb) {
1165 case MGCP_VERB_CRCX:
1166 mandatory_mask = MGCP_CRCX_MANDATORY;
1167 rc += msgb_printf(msg, "CRCX %u", trans_id);
1168 break;
1169 case MGCP_VERB_MDCX:
1170 mandatory_mask = MGCP_MDCX_MANDATORY;
1171 rc += msgb_printf(msg, "MDCX %u", trans_id);
1172 break;
1173 case MGCP_VERB_DLCX:
1174 mandatory_mask = MGCP_DLCX_MANDATORY;
1175 rc += msgb_printf(msg, "DLCX %u", trans_id);
1176 break;
1177 case MGCP_VERB_AUEP:
1178 mandatory_mask = MGCP_AUEP_MANDATORY;
1179 rc += msgb_printf(msg, "AUEP %u", trans_id);
1180 break;
1181 case MGCP_VERB_RSIP:
1182 mandatory_mask = MGCP_RSIP_MANDATORY;
1183 rc += msgb_printf(msg, "RSIP %u", trans_id);
1184 break;
1185 default:
1186 LOGP(DLMGCP, LOGL_ERROR,
1187 "Invalid command verb, can not generate MGCP message\n");
1188 msgb_free(msg);
1189 return NULL;
1190 }
1191
1192 /* Check if mandatory fields are missing */
1193 if (!((mgcp_msg->presence & mandatory_mask) == mandatory_mask)) {
1194 LOGP(DLMGCP, LOGL_ERROR,
1195 "One or more missing mandatory fields, can not generate MGCP message\n");
1196 msgb_free(msg);
1197 return NULL;
1198 }
1199
1200 /* Add endpoint name */
Philipp Maier7bc55522017-12-10 22:52:22 +01001201 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_ENDPOINT) {
1202 if (strlen(mgcp_msg->endpoint) <= 0) {
1203 LOGP(DLMGCP, LOGL_ERROR,
1204 "Empty endpoint name, can not generate MGCP message\n");
1205 msgb_free(msg);
1206 return NULL;
1207 }
Philipp Maier3aa81572018-01-31 14:13:45 +01001208
1209 if (strstr(mgcp_msg->endpoint, "@") == NULL) {
1210 LOGP(DLMGCP, LOGL_ERROR,
1211 "Endpoint name (%s) lacks separator (@), can not generate MGCP message\n",
1212 mgcp_msg->endpoint);
1213 msgb_free(msg);
1214 }
1215
Philipp Maier1dc6be62017-10-05 18:25:37 +02001216 rc += msgb_printf(msg, " %s", mgcp_msg->endpoint);
Philipp Maier7bc55522017-12-10 22:52:22 +01001217 }
Philipp Maier1dc6be62017-10-05 18:25:37 +02001218
1219 /* Add protocol version */
1220 rc += msgb_printf(msg, " MGCP 1.0\r\n");
1221
1222 /* Add call id */
1223 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CALL_ID)
1224 rc += msgb_printf(msg, "C: %x\r\n", mgcp_msg->call_id);
1225
1226 /* Add connection id */
Philipp Maier7bc55522017-12-10 22:52:22 +01001227 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID) {
1228 if (strlen(mgcp_msg->conn_id) <= 0) {
1229 LOGP(DLMGCP, LOGL_ERROR,
1230 "Empty connection id, can not generate MGCP message\n");
1231 msgb_free(msg);
1232 return NULL;
1233 }
Philipp Maier01d24a32017-11-21 17:26:09 +01001234 rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
Philipp Maier7bc55522017-12-10 22:52:22 +01001235 }
Philipp Maier1dc6be62017-10-05 18:25:37 +02001236
Philipp Maier704c4f02018-06-07 18:51:31 +02001237 /* Using SDP makes sense when a valid IP/Port combination is specifiec,
1238 * if we do not know this information yet, we fall back to LCO */
1239 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
1240 && mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)
1241 use_sdp = true;
1242
1243 /* Add local connection options (LCO) */
1244 if (!use_sdp
1245 && (mgcp_msg->verb == MGCP_VERB_CRCX
1246 || mgcp_msg->verb == MGCP_VERB_MDCX))
1247 rc += add_lco(msg, mgcp_msg);
Philipp Maier1dc6be62017-10-05 18:25:37 +02001248
1249 /* Add mode */
1250 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
1251 rc +=
1252 msgb_printf(msg, "M: %s\r\n",
1253 mgcp_client_cmode_name(mgcp_msg->conn_mode));
1254
Neels Hofmeyre6d8e912018-08-23 16:36:48 +02001255 /* Add X-Osmo-IGN */
1256 if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_IGN)
1257 && (mgcp_msg->x_osmo_ign != 0))
1258 rc +=
1259 msgb_printf(msg, MGCP_X_OSMO_IGN_HEADER "%s\r\n",
1260 mgcp_msg->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID ? " C": "");
1261
Pau Espin Pedrol900cd652019-04-24 22:06:22 +02001262 /* Add X-Osmo-Osmux */
1263 if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID)) {
Pau Espin Pedrol1b1d7ed2019-05-23 16:48:24 +02001264 if (mgcp_msg->x_osmo_osmux_cid < -1 || mgcp_msg->x_osmo_osmux_cid > OSMUX_CID_MAX) {
1265 LOGP(DLMGCP, LOGL_ERROR,
1266 "Wrong Osmux CID %d, can not generate MGCP message\n",
1267 mgcp_msg->x_osmo_osmux_cid);
1268 msgb_free(msg);
1269 return NULL;
1270 }
Pau Espin Pedrol900cd652019-04-24 22:06:22 +02001271 snprintf(buf, sizeof(buf), " %d", mgcp_msg->x_osmo_osmux_cid);
1272 rc +=
1273 msgb_printf(msg, MGCP_X_OSMO_OSMUX_HEADER "%s\r\n",
1274 mgcp_msg->x_osmo_osmux_cid == -1 ? " *": buf);
1275 }
1276
1277
Philipp Maier704c4f02018-06-07 18:51:31 +02001278 /* Add session description protocol (SDP) */
1279 if (use_sdp
1280 && (mgcp_msg->verb == MGCP_VERB_CRCX
1281 || mgcp_msg->verb == MGCP_VERB_MDCX)) {
1282 rc_sdp = add_sdp(msg, mgcp_msg, mgcp);
1283 if (rc_sdp == -2)
Philipp Maier9d25d7a2018-01-22 17:31:10 +01001284 return NULL;
Philipp Maier704c4f02018-06-07 18:51:31 +02001285 else
1286 rc += rc_sdp;
Philipp Maier1dc6be62017-10-05 18:25:37 +02001287 }
1288
1289 if (rc != 0) {
1290 LOGP(DLMGCP, LOGL_ERROR,
1291 "message buffer to small, can not generate MGCP message\n");
1292 msgb_free(msg);
1293 msg = NULL;
1294 }
1295
1296 return msg;
1297}
1298
Philipp Maier275ac972018-01-20 00:58:16 +01001299/*! Retrieve the MGCP transaction ID from a msgb generated by mgcp_msg_gen()
1300 * \param[in] msg message buffer
1301 * \returns Transaction id. */
Neels Hofmeyrc8f37cb2017-11-30 13:43:11 +01001302mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg)
1303{
1304 return (mgcp_trans_id_t)msg->cb[MSGB_CB_MGCP_TRANS_ID];
1305}
1306
Philipp Maier275ac972018-01-20 00:58:16 +01001307/*! Get the configuration parameters a given MGCP client instance
1308 * \param[in] mgcp MGCP client descriptor.
1309 * \returns configuration */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +02001310struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +02001311{
1312 return &mgcp->actual;
1313}
Neels Hofmeyrd95ab1e2017-09-22 00:52:54 +02001314
1315const struct value_string mgcp_client_connection_mode_strs[] = {
1316 { MGCP_CONN_NONE, "none" },
1317 { MGCP_CONN_RECV_SEND, "sendrecv" },
1318 { MGCP_CONN_SEND_ONLY, "sendonly" },
1319 { MGCP_CONN_RECV_ONLY, "recvonly" },
1320 { MGCP_CONN_LOOPBACK, "loopback" },
1321 { 0, NULL }
1322};