blob: c84f32cd89039f6662f3aec3a5ea4add98b5ceb4 [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':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200211 if (sscanf(line, "a=rtpmap:%d %63s",
212 &payload, audio_name) == 2) {
Philipp Maierc7a228a2017-10-18 11:42:25 +0200213 codecs_update(tmp_ctx, codecs,
214 codecs_used, payload, audio_name);
215 } else
216 if (sscanf
217 (line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200218 if (ptime2 > 0 && ptime2 != ptime)
219 rtp->packet_duration_ms = 0;
220 else
221 rtp->packet_duration_ms = ptime;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200222 } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
223 rtp->maximum_packet_time = ptime2;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200224 }
225 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200226 case 'm':
Philipp Maierbc0346e2018-06-07 09:52:16 +0200227 rc = sscanf(line, "m=audio %d RTP/AVP", &port);
228 if (rc == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200229 rtp->rtp_port = htons(port);
230 rtp->rtcp_port = htons(port + 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200231 }
Philipp Maierbc0346e2018-06-07 09:52:16 +0200232
233 rc = pt_from_sdp(conn->conn, codecs,
234 ARRAY_SIZE(codecs), line);
235 if (rc > 0)
236 codecs_used = rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200237 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200238 case 'c':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200239
240 if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
241 inet_aton(ipv4, &rtp->addr);
242 }
243 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200244 default:
245 if (p->endp)
246 LOGP(DLMGCP, LOGL_NOTICE,
247 "Unhandled SDP option: '%c'/%d on 0x%x\n",
Philipp Maierc7a228a2017-10-18 11:42:25 +0200248 line[0], line[0],
249 ENDPOINT_NUMBER(p->endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200250 else
251 LOGP(DLMGCP, LOGL_NOTICE,
252 "Unhandled SDP option: '%c'/%d\n",
253 line[0], line[0]);
254 break;
255 }
256 }
Philipp Maierbc0346e2018-06-07 09:52:16 +0200257 OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200258
Philipp Maierbc0346e2018-06-07 09:52:16 +0200259 /* So far we have only set the payload type in the codec struct. Now we
260 * fill up the remaining fields of the codec description with some default
261 * information */
262 codecs_initialize(tmp_ctx, codecs, codecs_used);
263
264 /* Store parsed codec information */
Philipp Maierdbd70c72018-05-25 11:07:31 +0200265 for (i = 0; i < codecs_used; i++) {
Philipp Maierbc0346e2018-06-07 09:52:16 +0200266 rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
267 if (rc < 0)
268 LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200269 }
270
271 talloc_free(tmp_ctx);
Philipp Maierdbd70c72018-05-25 11:07:31 +0200272
Philipp Maierbc0346e2018-06-07 09:52:16 +0200273 LOGP(DLMGCP, LOGL_NOTICE,
274 "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
275 ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
276 rtp->packet_duration_ms);
277 if (codecs_used == 0)
278 LOGPC(DLMGCP, LOGL_NOTICE, "none");
279 for (i = 0; i < codecs_used; i++) {
280 LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
281 rtp->codecs[i].payload_type,
282 rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
283 LOGPC(DLMGCP, LOGL_NOTICE, " ");
284 }
285 LOGPC(DLMGCP, LOGL_NOTICE, "\n");
286
Philipp Maierdbd70c72018-05-25 11:07:31 +0200287 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200288}
289
Philipp Maier8482e832018-08-02 17:22:40 +0200290
291/* Add rtpmap string to the sdp payload, but only when the payload type falls
292 * into the dynamic payload type range */
293static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
294{
295 int rc;
296
297 if (payload_type >= 96 && payload_type <= 127) {
298 if (!audio_name)
299 return -EINVAL;
300 rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
301 if (rc < 0)
302 return -EINVAL;
303 }
304
305 return 0;
306}
307
Philipp Maier217d31d2019-03-05 14:48:56 +0100308/* Add audio strings to sdp payload */
Philipp Maier910189d2018-08-02 17:45:42 +0200309static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
310{
311 int rc;
312 unsigned int i;
313
314 if (payload_types_len < 0)
315 return -EINVAL;
316
317 rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
318 if (rc < 0)
319 return -EINVAL;
320
321 for (i = 0; i < payload_types_len; i++) {
322 rc = msgb_printf(sdp, " %d", payload_types[i]);
323 if (rc < 0)
324 return -EINVAL;
325 }
326
327 rc = msgb_printf(sdp, "\r\n");
328 if (rc < 0)
329 return -EINVAL;
330
331 return 0;
332}
333
Philipp Maier8970c492017-10-11 13:33:42 +0200334/*! Generate SDP response string.
335 * \param[in] endp trunk endpoint.
336 * \param[in] conn associated rtp connection.
337 * \param[out] sdp msg buffer to append resulting SDP string data.
338 * \param[in] addr IPV4 address string (e.g. 192.168.100.1).
339 * \returns 0 on success, -1 on failure. */
340int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
341 const struct mgcp_conn_rtp *conn, struct msgb *sdp,
342 const char *addr)
343{
344 const char *fmtp_extra;
345 const char *audio_name;
346 int payload_type;
347 int rc;
Philipp Maier910189d2018-08-02 17:45:42 +0200348 int payload_types[1];
Philipp Maier8970c492017-10-11 13:33:42 +0200349
350 OSMO_ASSERT(endp);
351 OSMO_ASSERT(conn);
352 OSMO_ASSERT(sdp);
353 OSMO_ASSERT(addr);
354
355 /* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
356 endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
357 &payload_type, &audio_name,
358 &fmtp_extra,
359 (struct mgcp_conn_rtp *)conn);
360
361 rc = msgb_printf(sdp,
362 "v=0\r\n"
Philipp Maier01d24a32017-11-21 17:26:09 +0100363 "o=- %s 23 IN IP4 %s\r\n"
Philipp Maier8970c492017-10-11 13:33:42 +0200364 "s=-\r\n"
365 "c=IN IP4 %s\r\n"
366 "t=0 0\r\n", conn->conn->id, addr, addr);
367
368 if (rc < 0)
369 goto buffer_too_small;
370
371 if (payload_type >= 0) {
Philipp Maier910189d2018-08-02 17:45:42 +0200372
373 payload_types[0] = payload_type;
374 rc = add_audio(sdp, payload_types, 1, conn->end.local_port);
Philipp Maier8970c492017-10-11 13:33:42 +0200375 if (rc < 0)
376 goto buffer_too_small;
377
Philipp Maier8482e832018-08-02 17:22:40 +0200378 if (endp->tcfg->audio_send_name) {
379 rc = add_rtpmap(sdp, payload_type, audio_name);
Philipp Maier8970c492017-10-11 13:33:42 +0200380 if (rc < 0)
381 goto buffer_too_small;
382 }
383
384 if (fmtp_extra) {
385 rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
386
387 if (rc < 0)
388 goto buffer_too_small;
389 }
390 }
391 if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
392 rc = msgb_printf(sdp, "a=ptime:%u\r\n",
393 conn->end.packet_duration_ms);
394 if (rc < 0)
395 goto buffer_too_small;
396 }
397
398 return 0;
399
400buffer_too_small:
401 LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
402 return -1;
403}