blob: b0d1a9fb638cdce81c45eab245055b45e9d88542 [file] [log] [blame]
Philipp Maier87bd9be2017-08-22 16:35:41 +02001/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
2/* Message parser/generator utilities */
3
4/*
5 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
6 * (C) 2009-2012 by On-Waves
7 * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
19 *
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24
25#include <limits.h>
26
27#include <osmocom/mgcp/mgcp_internal.h>
Neels Hofmeyr67793542017-09-08 04:25:16 +020028#include <osmocom/mgcp/mgcp_common.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020029#include <osmocom/mgcp/mgcp_msg.h>
30#include <osmocom/mgcp/mgcp_conn.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010031#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020032
33/*! Display an mgcp message on the log output.
34 * \param[in] message mgcp message string
35 * \param[in] len message mgcp message string length
36 * \param[in] preamble string to display in logtext in front of each line */
37void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
38{
39 unsigned char line[80];
40 unsigned char *ptr;
41 unsigned int consumed = 0;
42 unsigned int consumed_line = 0;
43 unsigned int line_count = 0;
44
45 if (!log_check_level(DLMGCP, LOGL_DEBUG))
46 return;
47
48 while (1) {
49 memset(line, 0, sizeof(line));
50 ptr = line;
51 consumed_line = 0;
52 do {
53 if (*message != '\n' && *message != '\r') {
54 *ptr = *message;
55 ptr++;
56 }
57 message++;
58 consumed++;
59 consumed_line++;
60 } while (*message != '\n' && consumed < len
61 && consumed_line < sizeof(line));
62
63 if (strlen((const char *)line)) {
64 LOGP(DLMGCP, LOGL_DEBUG, "%s: line #%02u: %s\n",
65 preamble, line_count, line);
66 line_count++;
67 }
68
69 if (consumed >= len)
70 return;
71 }
72}
73
74/*! Parse connection mode.
75 * \param[in] mode as string (recvonly, sendrecv, sendonly or loopback)
76 * \param[in] endp pointer to endpoint (only used for log output)
77 * \param[out] associated connection to be modified accordingly
78 * \returns 0 on success, -1 on error */
79int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
80 struct mgcp_conn *conn)
81{
82 int ret = 0;
83
84 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +020085 LOGPCONN(conn, DLMGCP, LOGL_ERROR,
86 "missing connection mode\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +020087 return -1;
88 }
89 if (!conn)
90 return -1;
91 if (!endp)
92 return -1;
93
Pau Espin Pedrol17058482019-06-26 12:23:02 +020094 if (strcasecmp(mode, "recvonly") == 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +020095 conn->mode = MGCP_CONN_RECV_ONLY;
Pau Espin Pedrol17058482019-06-26 12:23:02 +020096 else if (strcasecmp(mode, "sendrecv") == 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +020097 conn->mode = MGCP_CONN_RECV_SEND;
Pau Espin Pedrol17058482019-06-26 12:23:02 +020098 else if (strcasecmp(mode, "sendonly") == 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +020099 conn->mode = MGCP_CONN_SEND_ONLY;
Pau Espin Pedrol17058482019-06-26 12:23:02 +0200100 else if (strcasecmp(mode, "loopback") == 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200101 conn->mode = MGCP_CONN_LOOPBACK;
102 else {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200103 LOGPCONN(conn, DLMGCP, LOGL_ERROR,
104 "unknown connection mode: '%s'\n", mode);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200105 ret = -1;
106 }
107
108 /* Special handling für RTP connections */
109 if (conn->type == MGCP_CONN_TYPE_RTP) {
110 conn->u.rtp.end.output_enabled =
111 conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
112 }
113
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200114 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200115
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200116 LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
117 mode, conn->mode);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200118
119 /* Special handling für RTP connections */
120 if (conn->type == MGCP_CONN_TYPE_RTP) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200121 LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n",
122 conn->u.rtp.end.output_enabled);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200123 }
124
125 /* The VTY might change the connection mode at any time, so we have
126 * to hold a copy of the original connection mode */
127 conn->mode_orig = conn->mode;
128
129 return ret;
130}
131
132/* We have a null terminated string with the endpoint name here. We only
133 * support two kinds. Simple ones as seen on the BSC level and the ones
134 * seen on the trunk side. (helper function for find_endpoint()) */
135static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
136 const char *mgcp)
137{
138 char *rest = NULL;
139 struct mgcp_trunk_config *tcfg;
140 int trunk, endp;
Philipp Maier207ab512018-02-02 14:19:26 +0100141 struct mgcp_endpoint *endp_ptr;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200142
143 trunk = strtoul(mgcp + 6, &rest, 10);
144 if (rest == NULL || rest[0] != '/' || trunk < 1) {
145 LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
146 return NULL;
147 }
148
149 endp = strtoul(rest + 1, &rest, 10);
150 if (rest == NULL || rest[0] != '@') {
151 LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
152 return NULL;
153 }
154
155 /* signalling is on timeslot 1 */
156 if (endp == 1)
157 return NULL;
158
159 tcfg = mgcp_trunk_num(cfg, trunk);
160 if (!tcfg) {
161 LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
162 trunk);
163 return NULL;
164 }
165
166 if (!tcfg->endpoints) {
167 LOGP(DLMGCP, LOGL_ERROR,
168 "Endpoints of trunk %d not allocated.\n", trunk);
169 return NULL;
170 }
171
172 if (endp < 1 || endp >= tcfg->number_endpoints) {
173 LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
174 mgcp);
175 return NULL;
176 }
177
Philipp Maier207ab512018-02-02 14:19:26 +0100178 endp_ptr = &tcfg->endpoints[endp];
179 endp_ptr->wildcarded_req = false;
180 return endp_ptr;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200181}
182
Philipp Maier55295f72018-01-15 14:00:28 +0100183/* Find an endpoint that is not in use. Do this by going through the endpoint
184 * array, check the callid. A callid nullpointer indicates that the endpoint
185 * is free */
186static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
187 unsigned int number_endpoints)
188{
189 struct mgcp_endpoint *endp;
190 unsigned int i;
191
192 for (i = 0; i < number_endpoints; i++) {
193 if (endpoints[i].callid == NULL) {
194 endp = &endpoints[i];
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200195 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
196 "found free endpoint\n");
Philipp Maier207ab512018-02-02 14:19:26 +0100197 endp->wildcarded_req = true;
Philipp Maier55295f72018-01-15 14:00:28 +0100198 return endp;
199 }
200 }
201
Philipp Maierb911b872018-02-02 11:48:16 +0100202 LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n");
Philipp Maier55295f72018-01-15 14:00:28 +0100203 return NULL;
204}
205
Philipp Maier12943ea2018-01-17 15:40:25 +0100206/* Check if the domain name, which is supplied with the endpoint name
207 * matches the configuration. */
208static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
209{
210 char *domain_to_check;
211
212 domain_to_check = strstr(mgcp, "@");
213 if (!domain_to_check)
214 return -EINVAL;
215
Neels Hofmeyr352eed02018-08-20 23:59:32 +0200216 /* Accept any domain if configured as "*" */
217 if (!strcmp(cfg->domain, "*"))
218 return 0;
219
Neels Hofmeyr96c31072018-12-19 00:31:48 +0100220 if (strcmp(domain_to_check+1, cfg->domain) != 0) {
221 LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain);
Philipp Maier12943ea2018-01-17 15:40:25 +0100222 return -EINVAL;
Neels Hofmeyr96c31072018-12-19 00:31:48 +0100223 }
Philipp Maier12943ea2018-01-17 15:40:25 +0100224
225 return 0;
226}
227
Philipp Maier87bd9be2017-08-22 16:35:41 +0200228/* Search the endpoint pool for the endpoint that had been selected via the
229 * MGCP message (helper function for mgcp_analyze_header()) */
230static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
Philipp Maiera49e32a2018-02-01 18:18:50 +0100231 const char *mgcp,
232 int *cause)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200233{
234 char *endptr = NULL;
235 unsigned int gw = INT_MAX;
Philipp Maier7f0966c2018-01-17 18:18:12 +0100236 const char *endpoint_number_str;
Philipp Maiera49e32a2018-02-01 18:18:50 +0100237 struct mgcp_endpoint *endp;
Harald Weltec39b1bf2020-03-08 11:29:39 +0100238 struct mgcp_trunk_config *virt_trunk = cfg->virt_trunk;
Philipp Maiera49e32a2018-02-01 18:18:50 +0100239
240 *cause = 0;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200241
Philipp Maier7f0966c2018-01-17 18:18:12 +0100242 /* Check if the domainname in the request is correct */
Philipp Maier12943ea2018-01-17 15:40:25 +0100243 if (check_domain_name(cfg, mgcp)) {
Neels Hofmeyr0a89e922018-08-20 22:39:53 +0200244 *cause = -500;
Philipp Maier12943ea2018-01-17 15:40:25 +0100245 return NULL;
246 }
247
Philipp Maier7f0966c2018-01-17 18:18:12 +0100248 /* Check if the E1 trunk is requested */
Philipp Maiera49e32a2018-02-01 18:18:50 +0100249 if (strncmp(mgcp, "ds/e1", 5) == 0) {
250 endp = find_e1_endpoint(cfg, mgcp);
251 if (!endp)
252 *cause = -500;
253 return endp;
254 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200255
Philipp Maier7f0966c2018-01-17 18:18:12 +0100256 /* Check if the virtual trunk is addressed (new, correct way with prefix) */
257 if (strncmp
258 (mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
259 strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) {
260 endpoint_number_str =
261 mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
262 if (endpoint_number_str[0] == '*') {
Harald Weltec39b1bf2020-03-08 11:29:39 +0100263 endp = find_free_endpoint(virt_trunk->endpoints,
264 virt_trunk->number_endpoints);
Philipp Maiera49e32a2018-02-01 18:18:50 +0100265 if (!endp)
266 *cause = -403;
267 return endp;
Philipp Maier7f0966c2018-01-17 18:18:12 +0100268 }
Philipp Maier7f0966c2018-01-17 18:18:12 +0100269 gw = strtoul(endpoint_number_str, &endptr, 16);
Harald Weltec39b1bf2020-03-08 11:29:39 +0100270 if (gw < virt_trunk->number_endpoints && endptr[0] == '@') {
271 endp = &virt_trunk->endpoints[gw];
Philipp Maier207ab512018-02-02 14:19:26 +0100272 endp->wildcarded_req = false;
273 return endp;
274 }
Philipp Maier55295f72018-01-15 14:00:28 +0100275 }
276
Philipp Maier7f0966c2018-01-17 18:18:12 +0100277 /* Deprecated method without prefix */
278 LOGP(DLMGCP, LOGL_NOTICE,
279 "Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n",
280 MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp);
Harald Welte33eafe02017-12-28 03:15:27 +0100281 gw = strtoul(mgcp, &endptr, 16);
Harald Weltec39b1bf2020-03-08 11:29:39 +0100282 if (gw < virt_trunk->number_endpoints && endptr[0] == '@') {
283 endp = &virt_trunk->endpoints[gw];
Philipp Maier207ab512018-02-02 14:19:26 +0100284 endp->wildcarded_req = false;
285 return endp;
286 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200287
288 LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
Philipp Maiera49e32a2018-02-01 18:18:50 +0100289 *cause = -500;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200290 return NULL;
291}
292
293/*! Analyze and parse the the hader of an MGCP messeage string.
294 * \param[out] pdata caller provided memory to store the parsing results
295 * \param[in] data mgcp message string
296 * \returns when the status line was complete and transaction_id and
297 * endp out parameters are set, -1 on error */
298int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
299{
300 int i = 0;
301 char *elem, *save = NULL;
Philipp Maiera49e32a2018-02-01 18:18:50 +0100302 int cause;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200303
304 /*! This function will parse the header part of the received
305 * MGCP message. The parsing results are stored in pdata.
306 * The function will also automatically search the pool with
307 * available endpoints in order to find an endpoint that matches
308 * the endpoint string in in the header */
309
310 OSMO_ASSERT(data);
311 pdata->trans = "000000";
312
313 for (elem = strtok_r(data, " ", &save); elem;
314 elem = strtok_r(NULL, " ", &save)) {
315 switch (i) {
316 case 0:
317 pdata->trans = elem;
318 break;
319 case 1:
Philipp Maiera49e32a2018-02-01 18:18:50 +0100320 pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200321 if (!pdata->endp) {
322 LOGP(DLMGCP, LOGL_ERROR,
323 "Unable to find Endpoint `%s'\n", elem);
Neels Hofmeyr0a89e922018-08-20 22:39:53 +0200324 OSMO_ASSERT(cause < 0);
Philipp Maiera49e32a2018-02-01 18:18:50 +0100325 return cause;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200326 }
327 break;
328 case 2:
Pau Espin Pedrol9a345922019-06-26 13:06:30 +0200329 if (strcasecmp("MGCP", elem)) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200330 LOGP(DLMGCP, LOGL_ERROR,
331 "MGCP header parsing error\n");
Harald Welteabbb6b92017-12-28 13:13:50 +0100332 return -510;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200333 }
334 break;
335 case 3:
336 if (strcmp("1.0", elem)) {
337 LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
338 "not supported\n", elem);
Harald Welteabbb6b92017-12-28 13:13:50 +0100339 return -528;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200340 }
341 break;
342 }
343 i++;
344 }
345
346 if (i != 4) {
347 LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
348 pdata->trans = "000000";
349 pdata->endp = NULL;
Harald Welteabbb6b92017-12-28 13:13:50 +0100350 return -510;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200351 }
352
353 return 0;
354}
355
356/*! Extract OSMUX CID from an MGCP parameter line (string).
357 * \param[in] line single parameter line from the MGCP message
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200358 * \returns OSMUX CID, -1 wildcard, -2 on error */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200359int mgcp_parse_osmux_cid(const char *line)
360{
361 int osmux_cid;
362
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200363
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200364 if (strcasecmp(line + 2, "Osmux: *") == 0) {
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200365 LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
366 return -1;
367 }
368
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200369 if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
Pau Espin Pedrolef6304e2019-04-23 13:24:37 +0200370 LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
371 line);
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200372 return -2;
Pau Espin Pedrolef6304e2019-04-23 13:24:37 +0200373 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200374
375 if (osmux_cid > OSMUX_CID_MAX) {
376 LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
377 osmux_cid, OSMUX_CID_MAX);
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200378 return -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200379 }
380 LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
381
382 return osmux_cid;
383}
384
385/*! Check MGCP parameter line (string) for plausibility.
386 * \param[in] endp pointer to endpoint (only used for log output)
387 * \param[in] line single parameter line from the MGCP message
388 * \returns 1 when line seems plausible, 0 on error */
389int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
390{
391 const size_t line_len = strlen(line);
392 if (line[0] != '\0' && line_len < 2) {
393 LOGP(DLMGCP, LOGL_ERROR,
394 "Wrong MGCP option format: '%s' on 0x%x\n",
395 line, ENDPOINT_NUMBER(endp));
396 return 0;
397 }
398
399 /* FIXME: A couple more checks wouldn't hurt... */
400
401 return 1;
402}
403
404/*! Check if the specified callid seems plausible.
405 * \param[in] endp pointer to endpoint
406 * \param{in] callid to verify
407 * \returns 1 when callid seems plausible, 0 on error */
408int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
409{
410 /*! This function compares the supplied callid with the called that is
411 * stored in the endpoint structure. */
412
413 if (!endp)
414 return -1;
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200415
416 /* Accept any CallID for "X-Osmo-IGN: C" */
417 if (endp->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID)
418 return 0;
419
Philipp Maier87bd9be2017-08-22 16:35:41 +0200420 if (!callid)
421 return -1;
422 if (!endp->callid)
423 return -1;
424
425 if (strcmp(endp->callid, callid) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200426 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
427 "CallIDs mismatch: '%s' != '%s'\n",
428 endp->callid, callid);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200429 return -1;
430 }
431
432 return 0;
433}
434
435/*! Check if the specified connection id seems plausible.
436 * \param[in] endp pointer to endpoint
437 * \param{in] connection id to verify
Neels Hofmeyreb72ff02018-09-03 23:00:07 +0200438 * \returns 0 when connection id is valid and exists, an RFC3435 error code on error.
Neels Hofmeyr8a91d2c2018-09-03 22:51:30 +0200439 */
Philipp Maier01d24a32017-11-21 17:26:09 +0100440int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200441{
Neels Hofmeyreb72ff02018-09-03 23:00:07 +0200442 /* For invalid conn_ids, return 510 "The transaction could not be executed, because some
443 * unspecified protocol error was detected." */
444
Philipp Maier01d24a32017-11-21 17:26:09 +0100445 /* Check for null identifiers */
446 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200447 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
448 "invalid ConnectionIdentifier (missing)\n");
Neels Hofmeyreb72ff02018-09-03 23:00:07 +0200449 return 510;
Philipp Maier01d24a32017-11-21 17:26:09 +0100450 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200451
Philipp Maier01d24a32017-11-21 17:26:09 +0100452 /* Check for empty connection identifiers */
453 if (strlen(conn_id) == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200454 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
455 "invalid ConnectionIdentifier (empty)\n");
Neels Hofmeyreb72ff02018-09-03 23:00:07 +0200456 return 510;
Philipp Maier01d24a32017-11-21 17:26:09 +0100457 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200458
Philipp Maier01d24a32017-11-21 17:26:09 +0100459 /* Check for over long connection identifiers */
Neels Hofmeyr5336f572018-09-03 22:05:48 +0200460 if (strlen(conn_id) > (MGCP_CONN_ID_MAXLEN-1)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200461 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
462 "invalid ConnectionIdentifier (too long: %zu > %d) 0x%s\n",
463 strlen(conn_id), MGCP_CONN_ID_MAXLEN-1, conn_id);
Neels Hofmeyreb72ff02018-09-03 23:00:07 +0200464 return 510;
Philipp Maier01d24a32017-11-21 17:26:09 +0100465 }
466
467 /* Check if connection exists */
468 if (mgcp_conn_get(endp, conn_id))
Philipp Maier87bd9be2017-08-22 16:35:41 +0200469 return 0;
470
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200471 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
472 "no connection found under ConnectionIdentifier 0x%s\n", conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200473
Neels Hofmeyreb72ff02018-09-03 23:00:07 +0200474 /* When the conn_id was not found, return error code 515 "The transaction refers to an incorrect
475 * connection-id (may have been already deleted)." */
476 return 515;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200477}
478
479/*! Extract individual lines from MCGP message.
480 * \param[in] str MGCP message string, consisting of multiple lines
481 * \param{in] saveptr pointer to next line in str
482 * \returns line, NULL when done */
483char *mgcp_strline(char *str, char **saveptr)
484{
485 char *result;
486
487 /*! The function must be called with *str set to the input string
488 * for the first line. After that saveptr will be initalized.
489 * all consecutive lines are extracted by calling the function
490 * with str set to NULL. When done, the function will return NULL
491 * to indicate that all lines have been parsed. */
492
493 if (str)
494 *saveptr = str;
495
496 result = *saveptr;
497
498 if (*saveptr != NULL) {
499 *saveptr = strpbrk(*saveptr, "\r\n");
500
501 if (*saveptr != NULL) {
502 char *eos = *saveptr;
503
504 if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n')
505 (*saveptr)++;
506 (*saveptr)++;
507 if ((*saveptr)[0] == '\0')
508 *saveptr = NULL;
509
510 *eos = '\0';
511 }
512 }
513
514 return result;
515}