blob: 9252b98ca4cd7d1f7257750748c7f403e62f18be [file] [log] [blame]
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001/*
2 * Some SDP file parsing...
3 *
4 * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * (C) 2009-2014 by On-Waves
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
Philipp Maier8970c492017-10-11 13:33:42 +020023#include <osmocom/core/msgb.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020024#include <osmocom/mgcp/mgcp.h>
25#include <osmocom/mgcp/mgcp_internal.h>
26#include <osmocom/mgcp/mgcp_msg.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010027#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierbc0346e2018-06-07 09:52:16 +020028#include <osmocom/mgcp/mgcp_codec.h>
Philipp Maier7e37ce62019-03-05 14:00:11 +010029#include <osmocom/mgcp/mgcp_sdp.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020030
31#include <errno.h>
32
Philipp Maierbc0346e2018-06-07 09:52:16 +020033/* A struct to store intermediate parsing results. The function
34 * mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
35 * codec information. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020036struct sdp_rtp_map {
37 /* the type */
38 int payload_type;
39 /* null, static or later dynamic codec name */
40 char *codec_name;
41 /* A pointer to the original line for later parsing */
42 char *map_line;
43
44 int rate;
45 int channels;
46};
47
Philipp Maierbc0346e2018-06-07 09:52:16 +020048/* Helper function to extrapolate missing codec parameters in a codec mao from
49 * an already filled in payload_type, called from: mgcp_parse_sdp_data() */
Philipp Maier8970c492017-10-11 13:33:42 +020050static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020051{
52 int i;
53
54 for (i = 0; i < used; ++i) {
55 switch (codecs[i].payload_type) {
56 case 0:
57 codecs[i].codec_name = "PCMU";
58 codecs[i].rate = 8000;
59 codecs[i].channels = 1;
60 break;
61 case 3:
62 codecs[i].codec_name = "GSM";
63 codecs[i].rate = 8000;
64 codecs[i].channels = 1;
65 break;
66 case 8:
67 codecs[i].codec_name = "PCMA";
68 codecs[i].rate = 8000;
69 codecs[i].channels = 1;
70 break;
71 case 18:
72 codecs[i].codec_name = "G729";
73 codecs[i].rate = 8000;
74 codecs[i].channels = 1;
75 break;
Philipp Maierbc0346e2018-06-07 09:52:16 +020076 default:
77 codecs[i].codec_name = NULL;
78 codecs[i].rate = 0;
79 codecs[i].channels = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020080 }
81 }
82}
83
Philipp Maierbc0346e2018-06-07 09:52:16 +020084/* Helper function to update codec map information with additional data from
85 * SDP, called from: mgcp_parse_sdp_data() */
Philipp Maier8970c492017-10-11 13:33:42 +020086static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
87 int payload, const char *audio_name)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020088{
89 int i;
90
91 for (i = 0; i < used; ++i) {
92 char audio_codec[64];
93 int rate = -1;
94 int channels = -1;
Philipp Maierbc0346e2018-06-07 09:52:16 +020095
96 /* Note: We can only update payload codecs that already exist
97 * in our codec list. If we get an unexpected payload type,
98 * we just drop it */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020099 if (codecs[i].payload_type != payload)
100 continue;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200101
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200102 if (sscanf(audio_name, "%63[^/]/%d/%d",
Philipp Maierd8d7b4b2017-10-18 11:45:35 +0200103 audio_codec, &rate, &channels) < 1) {
104 LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
105 audio_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200106 continue;
107 }
108
109 codecs[i].map_line = talloc_strdup(ctx, audio_name);
110 codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
111 codecs[i].rate = rate;
112 codecs[i].channels = channels;
113 return;
114 }
115
Philipp Maierd8d7b4b2017-10-18 11:45:35 +0200116 LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
117 audio_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200118}
119
Philipp Maierbc0346e2018-06-07 09:52:16 +0200120/* Extract payload types from SDP, also check for duplicates */
121static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
122 unsigned int codecs_len, char *sdp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200123{
Philipp Maierbc0346e2018-06-07 09:52:16 +0200124 char *str;
125 char *str_ptr;
126 char *pt_str;
127 unsigned int pt;
128 unsigned int count = 0;
129 unsigned int i;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200130
Philipp Maierbc0346e2018-06-07 09:52:16 +0200131 str = talloc_zero_size(ctx, strlen(sdp) + 1);
132 str_ptr = str;
133 strcpy(str_ptr, sdp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200134
Philipp Maierbc0346e2018-06-07 09:52:16 +0200135 str_ptr = strstr(str_ptr, "RTP/AVP ");
136 if (!str_ptr)
137 goto exit;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200138
Philipp Maierbc0346e2018-06-07 09:52:16 +0200139 pt_str = strtok(str_ptr, " ");
140 if (!pt_str)
141 goto exit;
142
143 while (1) {
144 /* Do not allow excessive payload types */
145 if (count > codecs_len)
146 goto error;
147
148 pt_str = strtok(NULL, " ");
149 if (!pt_str)
150 break;
151
152 pt = atoi(pt_str);
153
154 /* Do not allow duplicate payload types */
155 for (i = 0; i < count; i++)
156 if (codecs[i].payload_type == pt)
157 goto error;
158
159 codecs[count].payload_type = pt;
160 count++;
161 }
162
163exit:
164 talloc_free(str);
165 return count;
166error:
167 talloc_free(str);
168 return -EINVAL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200169}
170
Philipp Maier8970c492017-10-11 13:33:42 +0200171/*! Analyze SDP input string.
172 * \param[in] endp trunk endpoint.
173 * \param[out] conn associated rtp connection.
174 * \param[out] caller provided memory to store the parsing results.
Philipp Maier8970c492017-10-11 13:33:42 +0200175 *
176 * Note: In conn (conn->end) the function returns the packet duration,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200177 * rtp port, rtcp port and the codec information.
178 * \returns 0 on success, -1 on failure. */
Philipp Maier8970c492017-10-11 13:33:42 +0200179int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200180 struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200181{
Philipp Maierbc0346e2018-06-07 09:52:16 +0200182 struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
183 unsigned int codecs_used = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200184 char *line;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200185 unsigned int i;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200186 void *tmp_ctx = talloc_new(NULL);
Philipp Maier8970c492017-10-11 13:33:42 +0200187 struct mgcp_rtp_end *rtp;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200188
Philipp Maierc7a228a2017-10-18 11:42:25 +0200189 int payload;
190 int ptime, ptime2 = 0;
191 char audio_name[64];
192 int port, rc;
193 char ipv4[16];
194
Philipp Maier8970c492017-10-11 13:33:42 +0200195 OSMO_ASSERT(endp);
196 OSMO_ASSERT(conn);
197 OSMO_ASSERT(p);
198
199 rtp = &conn->end;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200200 memset(&codecs, 0, sizeof(codecs));
201
202 for_each_line(line, p->save) {
203 switch (line[0]) {
204 case 'o':
205 case 's':
206 case 't':
207 case 'v':
208 /* skip these SDP attributes */
209 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200210 case 'a':
Philipp Maiere7ae69a2019-03-05 15:17:36 +0100211 if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
212 codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
213 break;
214 }
215
216 if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200217 if (ptime2 > 0 && ptime2 != ptime)
218 rtp->packet_duration_ms = 0;
219 else
220 rtp->packet_duration_ms = ptime;
Philipp Maiere7ae69a2019-03-05 15:17:36 +0100221 break;
222 }
223
224 if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
Philipp Maierbc0346e2018-06-07 09:52:16 +0200225 rtp->maximum_packet_time = ptime2;
Philipp Maiere7ae69a2019-03-05 15:17:36 +0100226 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200227 }
228 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200229 case 'm':
Philipp Maierbc0346e2018-06-07 09:52:16 +0200230 rc = sscanf(line, "m=audio %d RTP/AVP", &port);
231 if (rc == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200232 rtp->rtp_port = htons(port);
233 rtp->rtcp_port = htons(port + 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200234 }
Philipp Maierbc0346e2018-06-07 09:52:16 +0200235
236 rc = pt_from_sdp(conn->conn, codecs,
237 ARRAY_SIZE(codecs), line);
238 if (rc > 0)
239 codecs_used = rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200240 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200241 case 'c':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200242
243 if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
244 inet_aton(ipv4, &rtp->addr);
245 }
246 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200247 default:
248 if (p->endp)
249 LOGP(DLMGCP, LOGL_NOTICE,
250 "Unhandled SDP option: '%c'/%d on 0x%x\n",
Philipp Maierc7a228a2017-10-18 11:42:25 +0200251 line[0], line[0],
252 ENDPOINT_NUMBER(p->endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200253 else
254 LOGP(DLMGCP, LOGL_NOTICE,
255 "Unhandled SDP option: '%c'/%d\n",
256 line[0], line[0]);
257 break;
258 }
259 }
Philipp Maierbc0346e2018-06-07 09:52:16 +0200260 OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200261
Philipp Maierbc0346e2018-06-07 09:52:16 +0200262 /* So far we have only set the payload type in the codec struct. Now we
263 * fill up the remaining fields of the codec description with some default
264 * information */
265 codecs_initialize(tmp_ctx, codecs, codecs_used);
266
267 /* Store parsed codec information */
Philipp Maierdbd70c72018-05-25 11:07:31 +0200268 for (i = 0; i < codecs_used; i++) {
Philipp Maierbc0346e2018-06-07 09:52:16 +0200269 rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
270 if (rc < 0)
271 LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200272 }
273
274 talloc_free(tmp_ctx);
Philipp Maierdbd70c72018-05-25 11:07:31 +0200275
Philipp Maierbc0346e2018-06-07 09:52:16 +0200276 LOGP(DLMGCP, LOGL_NOTICE,
277 "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
278 ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
279 rtp->packet_duration_ms);
280 if (codecs_used == 0)
281 LOGPC(DLMGCP, LOGL_NOTICE, "none");
282 for (i = 0; i < codecs_used; i++) {
283 LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
284 rtp->codecs[i].payload_type,
285 rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
286 LOGPC(DLMGCP, LOGL_NOTICE, " ");
287 }
288 LOGPC(DLMGCP, LOGL_NOTICE, "\n");
289
Philipp Maierdbd70c72018-05-25 11:07:31 +0200290 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200291}
292
Philipp Maier8482e832018-08-02 17:22:40 +0200293
294/* Add rtpmap string to the sdp payload, but only when the payload type falls
295 * into the dynamic payload type range */
296static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
297{
298 int rc;
299
300 if (payload_type >= 96 && payload_type <= 127) {
301 if (!audio_name)
302 return -EINVAL;
303 rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
304 if (rc < 0)
305 return -EINVAL;
306 }
307
308 return 0;
309}
310
Philipp Maier217d31d2019-03-05 14:48:56 +0100311/* Add audio strings to sdp payload */
Philipp Maier910189d2018-08-02 17:45:42 +0200312static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
313{
314 int rc;
315 unsigned int i;
316
317 if (payload_types_len < 0)
318 return -EINVAL;
319
320 rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
321 if (rc < 0)
322 return -EINVAL;
323
324 for (i = 0; i < payload_types_len; i++) {
325 rc = msgb_printf(sdp, " %d", payload_types[i]);
326 if (rc < 0)
327 return -EINVAL;
328 }
329
330 rc = msgb_printf(sdp, "\r\n");
331 if (rc < 0)
332 return -EINVAL;
333
334 return 0;
335}
336
Philipp Maier8970c492017-10-11 13:33:42 +0200337/*! Generate SDP response string.
338 * \param[in] endp trunk endpoint.
339 * \param[in] conn associated rtp connection.
340 * \param[out] sdp msg buffer to append resulting SDP string data.
341 * \param[in] addr IPV4 address string (e.g. 192.168.100.1).
342 * \returns 0 on success, -1 on failure. */
343int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
344 const struct mgcp_conn_rtp *conn, struct msgb *sdp,
345 const char *addr)
346{
347 const char *fmtp_extra;
348 const char *audio_name;
349 int payload_type;
350 int rc;
Philipp Maier910189d2018-08-02 17:45:42 +0200351 int payload_types[1];
Philipp Maier8970c492017-10-11 13:33:42 +0200352
353 OSMO_ASSERT(endp);
354 OSMO_ASSERT(conn);
355 OSMO_ASSERT(sdp);
356 OSMO_ASSERT(addr);
357
358 /* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
359 endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
360 &payload_type, &audio_name,
361 &fmtp_extra,
362 (struct mgcp_conn_rtp *)conn);
363
364 rc = msgb_printf(sdp,
365 "v=0\r\n"
Philipp Maier01d24a32017-11-21 17:26:09 +0100366 "o=- %s 23 IN IP4 %s\r\n"
Philipp Maier8970c492017-10-11 13:33:42 +0200367 "s=-\r\n"
368 "c=IN IP4 %s\r\n"
369 "t=0 0\r\n", conn->conn->id, addr, addr);
370
371 if (rc < 0)
372 goto buffer_too_small;
373
374 if (payload_type >= 0) {
Philipp Maier910189d2018-08-02 17:45:42 +0200375
376 payload_types[0] = payload_type;
377 rc = add_audio(sdp, payload_types, 1, conn->end.local_port);
Philipp Maier8970c492017-10-11 13:33:42 +0200378 if (rc < 0)
379 goto buffer_too_small;
380
Philipp Maier8482e832018-08-02 17:22:40 +0200381 if (endp->tcfg->audio_send_name) {
382 rc = add_rtpmap(sdp, payload_type, audio_name);
Philipp Maier8970c492017-10-11 13:33:42 +0200383 if (rc < 0)
384 goto buffer_too_small;
385 }
386
387 if (fmtp_extra) {
388 rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
389
390 if (rc < 0)
391 goto buffer_too_small;
392 }
393 }
394 if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
395 rc = msgb_printf(sdp, "a=ptime:%u\r\n",
396 conn->end.packet_duration_ms);
397 if (rc < 0)
398 goto buffer_too_small;
399 }
400
401 return 0;
402
403buffer_too_small:
404 LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
405 return -1;
406}