blob: f4ecb037a0ba3356f710adc651ec1574b5e9664f [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>
28#include <osmocom/mgcp/mgcp_msg.h>
29#include <osmocom/mgcp/mgcp_conn.h>
30
31/*! Display an mgcp message on the log output.
32 * \param[in] message mgcp message string
33 * \param[in] len message mgcp message string length
34 * \param[in] preamble string to display in logtext in front of each line */
35void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
36{
37 unsigned char line[80];
38 unsigned char *ptr;
39 unsigned int consumed = 0;
40 unsigned int consumed_line = 0;
41 unsigned int line_count = 0;
42
43 if (!log_check_level(DLMGCP, LOGL_DEBUG))
44 return;
45
46 while (1) {
47 memset(line, 0, sizeof(line));
48 ptr = line;
49 consumed_line = 0;
50 do {
51 if (*message != '\n' && *message != '\r') {
52 *ptr = *message;
53 ptr++;
54 }
55 message++;
56 consumed++;
57 consumed_line++;
58 } while (*message != '\n' && consumed < len
59 && consumed_line < sizeof(line));
60
61 if (strlen((const char *)line)) {
62 LOGP(DLMGCP, LOGL_DEBUG, "%s: line #%02u: %s\n",
63 preamble, line_count, line);
64 line_count++;
65 }
66
67 if (consumed >= len)
68 return;
69 }
70}
71
72/*! Parse connection mode.
73 * \param[in] mode as string (recvonly, sendrecv, sendonly or loopback)
74 * \param[in] endp pointer to endpoint (only used for log output)
75 * \param[out] associated connection to be modified accordingly
76 * \returns 0 on success, -1 on error */
77int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
78 struct mgcp_conn *conn)
79{
80 int ret = 0;
81
82 if (!mode) {
83 LOGP(DLMGCP, LOGL_ERROR,
84 "endpoint:%x missing connection mode\n",
85 ENDPOINT_NUMBER(endp));
86 return -1;
87 }
88 if (!conn)
89 return -1;
90 if (!endp)
91 return -1;
92
93 if (strcmp(mode, "recvonly") == 0)
94 conn->mode = MGCP_CONN_RECV_ONLY;
95 else if (strcmp(mode, "sendrecv") == 0)
96 conn->mode = MGCP_CONN_RECV_SEND;
97 else if (strcmp(mode, "sendonly") == 0)
98 conn->mode = MGCP_CONN_SEND_ONLY;
99 else if (strcmp(mode, "loopback") == 0)
100 conn->mode = MGCP_CONN_LOOPBACK;
101 else {
102 LOGP(DLMGCP, LOGL_ERROR,
103 "endpoint:%x unknown connection mode: '%s'\n",
104 ENDPOINT_NUMBER(endp), mode);
105 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
114 LOGP(DLMGCP, LOGL_DEBUG,
115 "endpoint:%x conn:%s\n",
116 ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn));
117
118 LOGP(DLMGCP, LOGL_DEBUG,
119 "endpoint:%x connection mode '%s' %d\n",
120 ENDPOINT_NUMBER(endp), mode, conn->mode);
121
122 /* Special handling für RTP connections */
123 if (conn->type == MGCP_CONN_TYPE_RTP) {
124 LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x output_enabled %d\n",
125 ENDPOINT_NUMBER(endp), conn->u.rtp.end.output_enabled);
126 }
127
128 /* The VTY might change the connection mode at any time, so we have
129 * to hold a copy of the original connection mode */
130 conn->mode_orig = conn->mode;
131
132 return ret;
133}
134
135/* We have a null terminated string with the endpoint name here. We only
136 * support two kinds. Simple ones as seen on the BSC level and the ones
137 * seen on the trunk side. (helper function for find_endpoint()) */
138static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
139 const char *mgcp)
140{
141 char *rest = NULL;
142 struct mgcp_trunk_config *tcfg;
143 int trunk, endp;
144
145 trunk = strtoul(mgcp + 6, &rest, 10);
146 if (rest == NULL || rest[0] != '/' || trunk < 1) {
147 LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
148 return NULL;
149 }
150
151 endp = strtoul(rest + 1, &rest, 10);
152 if (rest == NULL || rest[0] != '@') {
153 LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
154 return NULL;
155 }
156
157 /* signalling is on timeslot 1 */
158 if (endp == 1)
159 return NULL;
160
161 tcfg = mgcp_trunk_num(cfg, trunk);
162 if (!tcfg) {
163 LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
164 trunk);
165 return NULL;
166 }
167
168 if (!tcfg->endpoints) {
169 LOGP(DLMGCP, LOGL_ERROR,
170 "Endpoints of trunk %d not allocated.\n", trunk);
171 return NULL;
172 }
173
174 if (endp < 1 || endp >= tcfg->number_endpoints) {
175 LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
176 mgcp);
177 return NULL;
178 }
179
180 return &tcfg->endpoints[endp];
181}
182
183/* Search the endpoint pool for the endpoint that had been selected via the
184 * MGCP message (helper function for mgcp_analyze_header()) */
185static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
186 const char *mgcp)
187{
188 char *endptr = NULL;
189 unsigned int gw = INT_MAX;
190
191 if (strncmp(mgcp, "ds/e1", 5) == 0)
192 return find_e1_endpoint(cfg, mgcp);
193
194 gw = strtoul(mgcp, &endptr, 16);
195 if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@')
196 return &cfg->trunk.endpoints[gw];
197
198 LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
199 return NULL;
200}
201
202/*! Analyze and parse the the hader of an MGCP messeage string.
203 * \param[out] pdata caller provided memory to store the parsing results
204 * \param[in] data mgcp message string
205 * \returns when the status line was complete and transaction_id and
206 * endp out parameters are set, -1 on error */
207int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
208{
209 int i = 0;
210 char *elem, *save = NULL;
211
212 /*! This function will parse the header part of the received
213 * MGCP message. The parsing results are stored in pdata.
214 * The function will also automatically search the pool with
215 * available endpoints in order to find an endpoint that matches
216 * the endpoint string in in the header */
217
218 OSMO_ASSERT(data);
219 pdata->trans = "000000";
220
221 for (elem = strtok_r(data, " ", &save); elem;
222 elem = strtok_r(NULL, " ", &save)) {
223 switch (i) {
224 case 0:
225 pdata->trans = elem;
226 break;
227 case 1:
228 pdata->endp = find_endpoint(pdata->cfg, elem);
229 if (!pdata->endp) {
230 LOGP(DLMGCP, LOGL_ERROR,
231 "Unable to find Endpoint `%s'\n", elem);
232 return -1;
233 }
234 break;
235 case 2:
236 if (strcmp("MGCP", elem)) {
237 LOGP(DLMGCP, LOGL_ERROR,
238 "MGCP header parsing error\n");
239 return -1;
240 }
241 break;
242 case 3:
243 if (strcmp("1.0", elem)) {
244 LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
245 "not supported\n", elem);
246 return -1;
247 }
248 break;
249 }
250 i++;
251 }
252
253 if (i != 4) {
254 LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
255 pdata->trans = "000000";
256 pdata->endp = NULL;
257 return -1;
258 }
259
260 return 0;
261}
262
263/*! Extract OSMUX CID from an MGCP parameter line (string).
264 * \param[in] line single parameter line from the MGCP message
265 * \returns OSMUX CID, -1 on error */
266int mgcp_parse_osmux_cid(const char *line)
267{
268 int osmux_cid;
269
270 if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1)
271 return -1;
272
273 if (osmux_cid > OSMUX_CID_MAX) {
274 LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
275 osmux_cid, OSMUX_CID_MAX);
276 return -1;
277 }
278 LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
279
280 return osmux_cid;
281}
282
283/*! Check MGCP parameter line (string) for plausibility.
284 * \param[in] endp pointer to endpoint (only used for log output)
285 * \param[in] line single parameter line from the MGCP message
286 * \returns 1 when line seems plausible, 0 on error */
287int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
288{
289 const size_t line_len = strlen(line);
290 if (line[0] != '\0' && line_len < 2) {
291 LOGP(DLMGCP, LOGL_ERROR,
292 "Wrong MGCP option format: '%s' on 0x%x\n",
293 line, ENDPOINT_NUMBER(endp));
294 return 0;
295 }
296
297 /* FIXME: A couple more checks wouldn't hurt... */
298
299 return 1;
300}
301
302/*! Check if the specified callid seems plausible.
303 * \param[in] endp pointer to endpoint
304 * \param{in] callid to verify
305 * \returns 1 when callid seems plausible, 0 on error */
306int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
307{
308 /*! This function compares the supplied callid with the called that is
309 * stored in the endpoint structure. */
310
311 if (!endp)
312 return -1;
313 if (!callid)
314 return -1;
315 if (!endp->callid)
316 return -1;
317
318 if (strcmp(endp->callid, callid) != 0) {
319 LOGP(DLMGCP, LOGL_ERROR,
320 "endpoint:%x CallIDs does not match '%s' != '%s'\n",
321 ENDPOINT_NUMBER(endp), endp->callid, callid);
322 return -1;
323 }
324
325 return 0;
326}
327
328/*! Check if the specified connection id seems plausible.
329 * \param[in] endp pointer to endpoint
330 * \param{in] connection id to verify
331 * \returns 1 when connection id seems plausible, 0 on error */
332int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *ci)
333{
334 uint32_t id;
335
336 if (!endp)
337 return -1;
338
339 id = strtoul(ci, NULL, 10);
340
341 if (mgcp_conn_get(endp, id))
342 return 0;
343
344 LOGP(DLMGCP, LOGL_ERROR,
345 "endpoint:%x No connection found under ConnectionIdentifier %u\n",
346 ENDPOINT_NUMBER(endp), id);
347
348 return -1;
349}
350
351/*! Extract individual lines from MCGP message.
352 * \param[in] str MGCP message string, consisting of multiple lines
353 * \param{in] saveptr pointer to next line in str
354 * \returns line, NULL when done */
355char *mgcp_strline(char *str, char **saveptr)
356{
357 char *result;
358
359 /*! The function must be called with *str set to the input string
360 * for the first line. After that saveptr will be initalized.
361 * all consecutive lines are extracted by calling the function
362 * with str set to NULL. When done, the function will return NULL
363 * to indicate that all lines have been parsed. */
364
365 if (str)
366 *saveptr = str;
367
368 result = *saveptr;
369
370 if (*saveptr != NULL) {
371 *saveptr = strpbrk(*saveptr, "\r\n");
372
373 if (*saveptr != NULL) {
374 char *eos = *saveptr;
375
376 if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n')
377 (*saveptr)++;
378 (*saveptr)++;
379 if ((*saveptr)[0] == '\0')
380 *saveptr = NULL;
381
382 *eos = '\0';
383 }
384 }
385
386 return result;
387}
388
389/*! Parse CI from a given string.
390 * \param[out] caller provided memory to store the result
391 * \param{in] string containing the connection id
392 * \returns 0 on success, -1 on error */
393int mgcp_parse_ci(uint32_t *conn_id, const char *ci)
394{
395
396 OSMO_ASSERT(conn_id);
397
398 if (!ci)
399 return -1;
400
401 *conn_id = strtoul(ci, NULL, 10);
402
403 return 0;
404}