blob: f2204e160e81490f1baf289d428f1db06daa9228 [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>
38
Pau Espin Pedrol1b1d7ed2019-05-23 16:48:24 +020039#ifndef OSMUX_CID_MAX
40#define OSMUX_CID_MAX 255 /* FIXME: use OSMUX_CID_MAX from libosmo-netif? */
41#endif
42
Philipp Maier704c4f02018-06-07 18:51:31 +020043/* Codec descripton for dynamic payload types (SDP) */
Neels Hofmeyr47642f22019-02-27 05:56:53 +010044const struct value_string osmo_mgcpc_codec_names[] = {
Philipp Maier704c4f02018-06-07 18:51:31 +020045 { CODEC_PCMU_8000_1, "PCMU/8000/1" },
46 { CODEC_GSM_8000_1, "GSM/8000/1" },
47 { CODEC_PCMA_8000_1, "PCMA/8000/1" },
48 { CODEC_G729_8000_1, "G729/8000/1" },
49 { CODEC_GSMEFR_8000_1, "GSM-EFR/8000/1" },
50 { CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
51 { CODEC_AMR_8000_1, "AMR/8000/1" },
52 { CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
53 { 0, NULL },
54};
55
56/* Get encoding name from a full codec string e,g.
57 * ("CODEC/8000/2" => returns "CODEC") */
58static char *extract_codec_name(const char *str)
59{
60 static char buf[64];
61 unsigned int i;
62
63 if (!str)
64 return NULL;
65
66 /* FIXME osmo_strlcpy */
67 osmo_strlcpy(buf, str, sizeof(buf));
68
69 for (i = 0; i < strlen(buf); i++) {
70 if (buf[i] == '/')
71 buf[i] = '\0';
72 }
73
74 return buf;
75}
76
77/*! Map a string to a codec.
78 * \ptmap[in] str input string (e.g "GSM/8000/1", "GSM/8000" or "GSM")
79 * \returns codec that corresponds to the given string representation. */
80enum mgcp_codecs map_str_to_codec(const char *str)
81{
82 unsigned int i;
83 char *codec_name;
84 char str_buf[64];
85
86 osmo_strlcpy(str_buf, extract_codec_name(str), sizeof(str_buf));
87
Neels Hofmeyr47642f22019-02-27 05:56:53 +010088 for (i = 0; i < ARRAY_SIZE(osmo_mgcpc_codec_names); i++) {
89 codec_name = extract_codec_name(osmo_mgcpc_codec_names[i].str);
Philipp Maier704c4f02018-06-07 18:51:31 +020090 if (!codec_name)
91 continue;
92 if (strcmp(codec_name, str_buf) == 0)
Neels Hofmeyr47642f22019-02-27 05:56:53 +010093 return osmo_mgcpc_codec_names[i].value;
Philipp Maier704c4f02018-06-07 18:51:31 +020094 }
95
96 return -1;
97}
98
99/* Check the ptmap for illegal mappings */
Neels Hofmeyr84274e42019-04-27 19:08:36 +0200100static int check_ptmap(const struct ptmap *ptmap)
Philipp Maier704c4f02018-06-07 18:51:31 +0200101{
102 /* Check if there are mappings that leave the IANA assigned dynamic
103 * payload type range. Under normal conditions such mappings should
104 * not occur */
105
106 /* Its ok to have a 1:1 mapping in the statically defined
107 * range, this won't hurt */
108 if (ptmap->codec == ptmap->pt)
109 return 0;
110
111 if (ptmap->codec < 96 || ptmap->codec > 127)
112 goto error;
113 if (ptmap->pt < 96 || ptmap->pt > 127)
114 goto error;
115
116 return 0;
117error:
118 LOGP(DLMGCP, LOGL_ERROR,
119 "ptmap contains illegal mapping: codec=%u maps to pt=%u\n",
120 ptmap->codec, ptmap->pt);
121 return -1;
122}
123
124/*! Map a codec to a payload type.
125 * \ptmap[in] payload pointer to payload type map with specified payload types.
126 * \ptmap[in] ptmap_len length of the payload type map.
127 * \ptmap[in] codec the codec for which the payload type should be looked up.
128 * \returns assigned payload type */
Neels Hofmeyr84274e42019-04-27 19:08:36 +0200129unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
Philipp Maier704c4f02018-06-07 18:51:31 +0200130 enum mgcp_codecs codec)
131{
132 unsigned int i;
133
134 /*! Note: If the payload type map is empty or the codec is not found
135 * in the map, then a 1:1 mapping is performed. If the codec falls
136 * into the statically defined range or if the mapping table isself
137 * tries to map to the statically defined range, then the mapping
138 * is also ignored and a 1:1 mapping is performed instead. */
139
140 /* we may return the codec directly since enum mgcp_codecs directly
141 * corresponds to the statićally assigned payload types */
142 if (codec < 96 || codec > 127)
143 return codec;
144
145 for (i = 0; i < ptmap_len; i++) {
146 /* Skip illegal map entries */
147 if (check_ptmap(ptmap) == 0 && ptmap->codec == codec)
148 return ptmap->pt;
149 ptmap++;
150 }
151
152 /* If nothing is found, do not perform any mapping */
153 return codec;
154}
155
156/*! Map a payload type to a codec.
157 * \ptmap[in] payload pointer to payload type map with specified payload types.
158 * \ptmap[in] ptmap_len length of the payload type map.
159 * \ptmap[in] payload type for which the codec should be looked up.
160 * \returns codec that corresponds to the specified payload type */
161enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
162 unsigned int pt)
163{
164 unsigned int i;
165
166 /*! Note: If the payload type map is empty or the payload type is not
167 * found in the map, then a 1:1 mapping is performed. If the payload
168 * type falls into the statically defined range or if the mapping
169 * table isself tries to map to the statically defined range, then
170 * the mapping is also ignored and a 1:1 mapping is performed
171 * instead. */
172
173 /* See also note in map_codec_to_pt() */
174 if (pt < 96 || pt > 127)
175 return pt;
176
177 for (i = 0; i < ptmap_len; i++) {
178 if (check_ptmap(ptmap) == 0 && ptmap->pt == pt)
179 return ptmap->codec;
180 ptmap++;
181 }
182
183 /* If nothing is found, do not perform any mapping */
184 return pt;
185}
186
Philipp Maier275ac972018-01-20 00:58:16 +0100187/*! Initalize MGCP client configuration struct with default values.
188 * \param[out] conf Client configuration.*/
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200189void mgcp_client_conf_init(struct mgcp_client_conf *conf)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200190{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200191 /* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */
192 *conf = (struct mgcp_client_conf){
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200193 .local_addr = NULL,
194 .local_port = -1,
195 .remote_addr = NULL,
196 .remote_port = -1,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200197 };
198}
199
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200200static void mgcp_client_handle_response(struct mgcp_client *mgcp,
201 struct mgcp_response_pending *pending,
202 struct mgcp_response *response)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200203{
204 if (!pending) {
205 LOGP(DLMGCP, LOGL_ERROR,
206 "Cannot handle NULL response\n");
207 return;
208 }
209 if (pending->response_cb)
210 pending->response_cb(response, pending->priv);
211 else
Neels Hofmeyred0c1aa2018-12-21 03:07:53 +0100212 LOGP(DLMGCP, LOGL_DEBUG, "MGCP response ignored (NULL cb)\n");
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200213 talloc_free(pending);
214}
215
216static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
217{
218 int comment_pos;
219 char *end;
220
221 if (mgcp_msg_terminate_nul(msg))
222 goto response_parse_failure;
223
224 r->body = (char *)msg->data;
225
Harald Welte9bf7c532017-11-17 14:14:31 +0100226 if (sscanf(r->body, "%3d %u %n",
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200227 &r->head.response_code, &r->head.trans_id,
228 &comment_pos) != 2)
229 goto response_parse_failure;
230
Philipp Maierabe8c892018-01-20 00:15:12 +0100231 osmo_strlcpy(r->head.comment, r->body + comment_pos, sizeof(r->head.comment));
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200232 end = strchr(r->head.comment, '\r');
233 if (!end)
234 goto response_parse_failure;
235 /* Mark the end of the comment */
236 *end = '\0';
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200237 return 0;
238
239response_parse_failure:
240 LOGP(DLMGCP, LOGL_ERROR,
241 "Failed to parse MGCP response header\n");
242 return -EINVAL;
243}
244
245/* TODO undup against mgcp_protocol.c:mgcp_check_param() */
246static bool mgcp_line_is_valid(const char *line)
247{
248 const size_t line_len = strlen(line);
249 if (line[0] == '\0')
250 return true;
251
252 if (line_len < 2
253 || line[1] != '=') {
254 LOGP(DLMGCP, LOGL_ERROR,
255 "Wrong MGCP option format: '%s'\n",
256 line);
257 return false;
258 }
259
260 return true;
261}
262
Philipp Maier704c4f02018-06-07 18:51:31 +0200263/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
264static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200265{
Philipp Maier704c4f02018-06-07 18:51:31 +0200266 char *pt_str;
267 unsigned int pt;
268 unsigned int count = 0;
269 unsigned int i;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200270
Philipp Maier704c4f02018-06-07 18:51:31 +0200271 /* Extract port information */
272 if (sscanf(line, "m=audio %hu", &r->audio_port) != 1)
273 goto response_parse_failure_port;
Philipp Maier10f32db2017-12-13 12:34:34 +0100274 if (r->audio_port == 0)
Philipp Maier704c4f02018-06-07 18:51:31 +0200275 goto response_parse_failure_port;
Philipp Maier10f32db2017-12-13 12:34:34 +0100276
Philipp Maier704c4f02018-06-07 18:51:31 +0200277 /* Extract payload types */
278 line = strstr(line, "RTP/AVP ");
279 if (!line)
280 goto exit;
281
282 pt_str = strtok(line, " ");
283 while (1) {
284 /* Do not allow excessive payload types */
285 if (count > ARRAY_SIZE(r->codecs))
286 goto response_parse_failure_pt;
287
288 pt_str = strtok(NULL, " ");
289 if (!pt_str)
290 break;
291 pt = atoi(pt_str);
292
293 /* Do not allow duplicate payload types */
294 for (i = 0; i < count; i++)
295 if (r->codecs[i] == pt)
296 goto response_parse_failure_pt;
297
298 /* Note: The payload type we store may not necessarly match
299 * the codec types we have defined in enum mgcp_codecs. To
300 * ensure that the end result only contains codec types which
301 * match enum mgcp_codecs, we will go through afterwards and
302 * remap the affected entries with the inrofmation we learn
303 * from rtpmap */
304 r->codecs[count] = pt;
305 count++;
306 }
307
308 r->codecs_len = count;
309
310exit:
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200311 return 0;
312
Philipp Maier704c4f02018-06-07 18:51:31 +0200313response_parse_failure_port:
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200314 LOGP(DLMGCP, LOGL_ERROR,
Philipp Maier704c4f02018-06-07 18:51:31 +0200315 "Failed to parse SDP parameter port (%s)\n", line);
Philipp Maier06da85e2017-10-05 18:49:24 +0200316 return -EINVAL;
Philipp Maier704c4f02018-06-07 18:51:31 +0200317
318response_parse_failure_pt:
319 LOGP(DLMGCP, LOGL_ERROR,
320 "Failed to parse SDP parameter payload types (%s)\n", line);
321 return -EINVAL;
322}
323
324/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
325static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *line)
326{
327 unsigned int pt;
328 char codec_resp[64];
329 unsigned int codec;
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +0200330
331
Philipp Maier704c4f02018-06-07 18:51:31 +0200332 if (strstr(line, "ptime")) {
333 if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
334 goto response_parse_failure_ptime;
335 } else if (strstr(line, "rtpmap")) {
336 if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) {
337 /* The MGW may assign an own payload type in the
338 * response if the choosen codec falls into the IANA
339 * assigned dynamic payload type range (96-127).
340 * Normally the MGW should obey the 3gpp payload type
341 * assignments, which are fixed, so we likely wont see
342 * anything unexpected here. In order to be sure that
343 * we will now check the codec string and if the result
344 * does not match to what is IANA / 3gpp assigned, we
345 * will create an entry in the ptmap table so we can
346 * lookup later what has been assigned. */
347 codec = map_str_to_codec(codec_resp);
348 if (codec != pt) {
349 if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) {
350 r->ptmap[r->ptmap_len].pt = pt;
351 r->ptmap[r->ptmap_len].codec = codec;
352 r->ptmap_len++;
353 } else
354 goto response_parse_failure_rtpmap;
355 }
356
357 } else
358 goto response_parse_failure_rtpmap;
359 }
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +0200360
Philipp Maier704c4f02018-06-07 18:51:31 +0200361 return 0;
362
363response_parse_failure_ptime:
364 LOGP(DLMGCP, LOGL_ERROR,
365 "Failed to parse SDP parameter, invalid ptime (%s)\n", line);
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +0200366 return -EINVAL;
Philipp Maier704c4f02018-06-07 18:51:31 +0200367response_parse_failure_rtpmap:
368 LOGP(DLMGCP, LOGL_ERROR,
369 "Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +0200370 return -EINVAL;
Philipp Maier06da85e2017-10-05 18:49:24 +0200371}
372
373/* Parse a line like "c=IN IP4 10.11.12.13" */
374static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line)
375{
376 struct in_addr ip_test;
377
378 if (strlen(line) < 16)
379 goto response_parse_failure;
380
381 /* The current implementation strictly supports IPV4 only ! */
382 if (memcmp("c=IN IP4 ", line, 9) != 0)
383 goto response_parse_failure;
384
385 /* Extract IP-Address */
Philipp Maierf8bfbe82017-11-23 19:32:31 +0100386 osmo_strlcpy(r->audio_ip, line + 9, sizeof(r->audio_ip));
Philipp Maier06da85e2017-10-05 18:49:24 +0200387
388 /* Check IP-Address */
389 if (inet_aton(r->audio_ip, &ip_test) == 0)
390 goto response_parse_failure;
391
392 return 0;
393
394response_parse_failure:
395 LOGP(DLMGCP, LOGL_ERROR,
396 "Failed to parse MGCP response header (audio ip)\n");
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200397 return -EINVAL;
398}
399
Pau Espin Pedrol91088c32019-04-24 21:02:40 +0200400/*! Extract OSMUX CID from an MGCP parameter line (string).
401 * \param[in] line single parameter line from the MGCP message
402 * \returns OSMUX CID, -1 wildcard, -2 on error
403 * FIXME: This is a copy of function in mgcp_msg.c. Have some common.c file between both libs?
404 */
405static int mgcp_parse_osmux_cid(const char *line)
406{
407 int osmux_cid;
408
409
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200410 if (strcasecmp(line + 2, "Osmux: *") == 0) {
Pau Espin Pedrol91088c32019-04-24 21:02:40 +0200411 LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
412 return -1;
413 }
414
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200415 if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
Pau Espin Pedrol91088c32019-04-24 21:02:40 +0200416 LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
417 line);
418 return -2;
419 }
420
Pau Espin Pedrol91088c32019-04-24 21:02:40 +0200421 if (osmux_cid > OSMUX_CID_MAX) { /* OSMUX_CID_MAX from libosmo-netif */
422 LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
423 osmux_cid, OSMUX_CID_MAX);
424 return -2;
425 }
426 LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
427
428 return osmux_cid;
429}
430
Philipp Maier3b12e1b2018-01-18 15:16:13 +0100431/* A new section is marked by a double line break, check a few more
432 * patterns as there may be variants */
433static char *mgcp_find_section_end(char *string)
434{
435 char *rc;
436
437 rc = strstr(string, "\n\n");
438 if (rc)
439 return rc;
440
441 rc = strstr(string, "\n\r\n\r");
442 if (rc)
443 return rc;
444
445 rc = strstr(string, "\r\n\r\n");
446 if (rc)
447 return rc;
448
449 return NULL;
450}
451
Philipp Maier275ac972018-01-20 00:58:16 +0100452/*! Parse body (SDP) parameters of the MGCP response
453 * \param[in,out] r Response data
454 * \returns 0 on success, -EINVAL on error. */
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200455int mgcp_response_parse_params(struct mgcp_response *r)
456{
457 char *line;
458 int rc;
Neels Hofmeyr0793d2f2018-02-21 14:55:34 +0100459 char *data;
Philipp Maiere9d645b2018-01-19 23:54:08 +0100460 char *data_ptr;
Philipp Maier704c4f02018-06-07 18:51:31 +0200461 int i;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200462
Philipp Maiere9d645b2018-01-19 23:54:08 +0100463 /* Since this functions performs a destructive parsing, we create a
464 * local copy of the body data */
Neels Hofmeyr0793d2f2018-02-21 14:55:34 +0100465 OSMO_ASSERT(r->body);
466 data = talloc_strdup(r, r->body);
Philipp Maiere9d645b2018-01-19 23:54:08 +0100467 OSMO_ASSERT(data);
Philipp Maier55295f72018-01-15 14:00:28 +0100468
Philipp Maiere9d645b2018-01-19 23:54:08 +0100469 /* Find beginning of the parameter (SDP) section */
470 data_ptr = mgcp_find_section_end(data);
Neels Hofmeyr10835332018-02-21 14:55:34 +0100471 if (!data_ptr) {
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200472 LOGP(DLMGCP, LOGL_ERROR,
Neels Hofmeyr0793d2f2018-02-21 14:55:34 +0100473 "MGCP response: cannot find start of SDP parameters\n");
Philipp Maiere9d645b2018-01-19 23:54:08 +0100474 rc = -EINVAL;
475 goto exit;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200476 }
477
Neels Hofmeyr0793d2f2018-02-21 14:55:34 +0100478 /* data_ptr now points to the beginning of the section-end-marker; for_each_non_empty_line()
479 * skips any \r and \n characters for free, so we don't need to skip the marker. */
480
Philipp Maiere9d645b2018-01-19 23:54:08 +0100481 for_each_non_empty_line(line, data_ptr) {
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200482 if (!mgcp_line_is_valid(line))
483 return -EINVAL;
484
485 switch (line[0]) {
Philipp Maier704c4f02018-06-07 18:51:31 +0200486 case 'a':
487 rc = mgcp_parse_audio_ptime_rtpmap(r, line);
488 if (rc)
489 goto exit;
490 break;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200491 case 'm':
Philipp Maier704c4f02018-06-07 18:51:31 +0200492 rc = mgcp_parse_audio_port_pt(r, line);
Philipp Maier06da85e2017-10-05 18:49:24 +0200493 if (rc)
Philipp Maiere9d645b2018-01-19 23:54:08 +0100494 goto exit;
Philipp Maier06da85e2017-10-05 18:49:24 +0200495 break;
496 case 'c':
497 rc = mgcp_parse_audio_ip(r, line);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200498 if (rc)
Philipp Maiere9d645b2018-01-19 23:54:08 +0100499 goto exit;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200500 break;
501 default:
502 /* skip unhandled parameters */
503 break;
504 }
505 }
Philipp Maiere9d645b2018-01-19 23:54:08 +0100506
Philipp Maier704c4f02018-06-07 18:51:31 +0200507 /* See also note in mgcp_parse_audio_port_pt() */
508 for (i = 0; i < r->codecs_len; i++)
509 r->codecs[i] = map_pt_to_codec(r->ptmap, r->ptmap_len, r->codecs[i]);
510
Philipp Maiere9d645b2018-01-19 23:54:08 +0100511 rc = 0;
512exit:
513 talloc_free(data);
514 return rc;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200515}
516
Philipp Maier55295f72018-01-15 14:00:28 +0100517/* Parse a line like "X: something" */
518static int mgcp_parse_head_param(char *result, unsigned int result_len,
519 char label, const char *line)
Philipp Maierffd75e42017-11-22 11:44:50 +0100520{
Philipp Maier55295f72018-01-15 14:00:28 +0100521 char label_string[4];
Neels Hofmeyr23e7bf12018-09-03 21:26:22 +0200522 size_t rc;
Philipp Maier55295f72018-01-15 14:00:28 +0100523
524 /* Detect empty parameters */
Philipp Maierffd75e42017-11-22 11:44:50 +0100525 if (strlen(line) < 4)
526 goto response_parse_failure;
527
Philipp Maier55295f72018-01-15 14:00:28 +0100528 /* Check if the label matches */
529 snprintf(label_string, sizeof(label_string), "%c: ", label);
530 if (memcmp(label_string, line, 3) != 0)
Philipp Maierffd75e42017-11-22 11:44:50 +0100531 goto response_parse_failure;
532
Philipp Maier55295f72018-01-15 14:00:28 +0100533 /* Copy payload part of the string to destinations (the label string
534 * is always 3 chars long) */
Neels Hofmeyr23e7bf12018-09-03 21:26:22 +0200535 rc = osmo_strlcpy(result, line + 3, result_len);
536 if (rc >= result_len) {
537 LOGP(DLMGCP, LOGL_ERROR,
538 "Failed to parse MGCP response (parameter label: %c):"
539 " the received conn ID is too long: %zu, maximum is %u characters\n",
540 label, rc, result_len - 1);
541 return -ENOSPC;
542 }
Philipp Maierffd75e42017-11-22 11:44:50 +0100543 return 0;
544
545response_parse_failure:
546 LOGP(DLMGCP, LOGL_ERROR,
Philipp Maier55295f72018-01-15 14:00:28 +0100547 "Failed to parse MGCP response (parameter label: %c)\n", label);
Philipp Maierffd75e42017-11-22 11:44:50 +0100548 return -EINVAL;
549}
550
551/* Parse MGCP parameters of the response */
552static int parse_head_params(struct mgcp_response *r)
553{
554 char *line;
555 int rc = 0;
556 OSMO_ASSERT(r->body);
Philipp Maier55295f72018-01-15 14:00:28 +0100557 char *data;
558 char *data_ptr;
559 char *data_end;
Philipp Maierffd75e42017-11-22 11:44:50 +0100560
Philipp Maier55295f72018-01-15 14:00:28 +0100561 /* Since this functions performs a destructive parsing, we create a
562 * local copy of the body data */
Philipp Maier1fc69992018-01-31 15:32:55 +0100563 data = talloc_zero_size(r, strlen(r->body)+1);
Philipp Maier55295f72018-01-15 14:00:28 +0100564 OSMO_ASSERT(data);
565 data_ptr = data;
566 osmo_strlcpy(data, r->body, strlen(r->body));
567
568 /* If there is an SDP body attached, prevent for_each_non_empty_line()
569 * into running in there, we are not yet interested in the parameters
570 * stored there. */
Pau Espin Pedrolc12bfb72019-04-16 17:23:09 +0200571 data_end = mgcp_find_section_end(data);
Philipp Maierffd75e42017-11-22 11:44:50 +0100572 if (data_end)
573 *data_end = '\0';
574
Philipp Maier55295f72018-01-15 14:00:28 +0100575 for_each_non_empty_line(line, data_ptr) {
Philipp Maierffd75e42017-11-22 11:44:50 +0100576 switch (line[0]) {
Philipp Maier55295f72018-01-15 14:00:28 +0100577 case 'Z':
578 rc = mgcp_parse_head_param(r->head.endpoint,
579 sizeof(r->head.endpoint),
580 'Z', line);
581 if (rc)
582 goto exit;
Philipp Maier771b26a2018-01-31 13:53:11 +0100583
584 /* A specific endpoint identifier returned by the MGW
585 * must not contain any wildcard characters */
586 if (strstr(r->head.endpoint, "*") != NULL) {
587 rc = -EINVAL;
588 goto exit;
589 }
Philipp Maier3261dc72018-01-31 14:03:13 +0100590
591 /* A specific endpoint identifier returned by the MGW
592 * must contain an @ character */
593 if (strstr(r->head.endpoint, "@") == NULL) {
594 rc = -EINVAL;
595 goto exit;
596 }
Philipp Maier55295f72018-01-15 14:00:28 +0100597 break;
Philipp Maierffd75e42017-11-22 11:44:50 +0100598 case 'I':
Philipp Maier55295f72018-01-15 14:00:28 +0100599 rc = mgcp_parse_head_param(r->head.conn_id,
600 sizeof(r->head.conn_id),
601 'I', line);
Philipp Maierffd75e42017-11-22 11:44:50 +0100602 if (rc)
603 goto exit;
604 break;
Pau Espin Pedrol91088c32019-04-24 21:02:40 +0200605 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200606 case 'x':
607 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};