blob: 4f66e85fc37f4a52f847fd22e19663e4670b1f8b [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>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020029
30#include <errno.h>
31
Philipp Maierbc0346e2018-06-07 09:52:16 +020032/* A struct to store intermediate parsing results. The function
33 * mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
34 * codec information. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020035struct sdp_rtp_map {
36 /* the type */
37 int payload_type;
38 /* null, static or later dynamic codec name */
39 char *codec_name;
40 /* A pointer to the original line for later parsing */
41 char *map_line;
42
43 int rate;
44 int channels;
45};
46
Philipp Maierbc0346e2018-06-07 09:52:16 +020047/* Helper function to extrapolate missing codec parameters in a codec mao from
48 * an already filled in payload_type, called from: mgcp_parse_sdp_data() */
Philipp Maier8970c492017-10-11 13:33:42 +020049static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020050{
51 int i;
52
53 for (i = 0; i < used; ++i) {
54 switch (codecs[i].payload_type) {
55 case 0:
56 codecs[i].codec_name = "PCMU";
57 codecs[i].rate = 8000;
58 codecs[i].channels = 1;
59 break;
60 case 3:
61 codecs[i].codec_name = "GSM";
62 codecs[i].rate = 8000;
63 codecs[i].channels = 1;
64 break;
65 case 8:
66 codecs[i].codec_name = "PCMA";
67 codecs[i].rate = 8000;
68 codecs[i].channels = 1;
69 break;
70 case 18:
71 codecs[i].codec_name = "G729";
72 codecs[i].rate = 8000;
73 codecs[i].channels = 1;
74 break;
Philipp Maierbc0346e2018-06-07 09:52:16 +020075 default:
76 codecs[i].codec_name = NULL;
77 codecs[i].rate = 0;
78 codecs[i].channels = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020079 }
80 }
81}
82
Philipp Maierbc0346e2018-06-07 09:52:16 +020083/* Helper function to update codec map information with additional data from
84 * SDP, called from: mgcp_parse_sdp_data() */
Philipp Maier8970c492017-10-11 13:33:42 +020085static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
86 int payload, const char *audio_name)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020087{
88 int i;
89
90 for (i = 0; i < used; ++i) {
91 char audio_codec[64];
92 int rate = -1;
93 int channels = -1;
Philipp Maierbc0346e2018-06-07 09:52:16 +020094
95 /* Note: We can only update payload codecs that already exist
96 * in our codec list. If we get an unexpected payload type,
97 * we just drop it */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020098 if (codecs[i].payload_type != payload)
99 continue;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200100
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200101 if (sscanf(audio_name, "%63[^/]/%d/%d",
Philipp Maierd8d7b4b2017-10-18 11:45:35 +0200102 audio_codec, &rate, &channels) < 1) {
103 LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
104 audio_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200105 continue;
106 }
107
108 codecs[i].map_line = talloc_strdup(ctx, audio_name);
109 codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
110 codecs[i].rate = rate;
111 codecs[i].channels = channels;
112 return;
113 }
114
Philipp Maierd8d7b4b2017-10-18 11:45:35 +0200115 LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
116 audio_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200117}
118
Philipp Maierbc0346e2018-06-07 09:52:16 +0200119/* Extract payload types from SDP, also check for duplicates */
120static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
121 unsigned int codecs_len, char *sdp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200122{
Philipp Maierbc0346e2018-06-07 09:52:16 +0200123 char *str;
124 char *str_ptr;
125 char *pt_str;
126 unsigned int pt;
127 unsigned int count = 0;
128 unsigned int i;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200129
Philipp Maierbc0346e2018-06-07 09:52:16 +0200130 str = talloc_zero_size(ctx, strlen(sdp) + 1);
131 str_ptr = str;
132 strcpy(str_ptr, sdp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200133
Philipp Maierbc0346e2018-06-07 09:52:16 +0200134 str_ptr = strstr(str_ptr, "RTP/AVP ");
135 if (!str_ptr)
136 goto exit;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200137
Philipp Maierbc0346e2018-06-07 09:52:16 +0200138 pt_str = strtok(str_ptr, " ");
139 if (!pt_str)
140 goto exit;
141
142 while (1) {
143 /* Do not allow excessive payload types */
144 if (count > codecs_len)
145 goto error;
146
147 pt_str = strtok(NULL, " ");
148 if (!pt_str)
149 break;
150
151 pt = atoi(pt_str);
152
153 /* Do not allow duplicate payload types */
154 for (i = 0; i < count; i++)
155 if (codecs[i].payload_type == pt)
156 goto error;
157
158 codecs[count].payload_type = pt;
159 count++;
160 }
161
162exit:
163 talloc_free(str);
164 return count;
165error:
166 talloc_free(str);
167 return -EINVAL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200168}
169
Philipp Maier8970c492017-10-11 13:33:42 +0200170/*! Analyze SDP input string.
171 * \param[in] endp trunk endpoint.
172 * \param[out] conn associated rtp connection.
173 * \param[out] caller provided memory to store the parsing results.
Philipp Maier8970c492017-10-11 13:33:42 +0200174 *
175 * Note: In conn (conn->end) the function returns the packet duration,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200176 * rtp port, rtcp port and the codec information.
177 * \returns 0 on success, -1 on failure. */
Philipp Maier8970c492017-10-11 13:33:42 +0200178int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200179 struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200180{
Philipp Maierbc0346e2018-06-07 09:52:16 +0200181 struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
182 unsigned int codecs_used = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200183 char *line;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200184 unsigned int i;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200185 void *tmp_ctx = talloc_new(NULL);
Philipp Maier8970c492017-10-11 13:33:42 +0200186 struct mgcp_rtp_end *rtp;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200187
Philipp Maierc7a228a2017-10-18 11:42:25 +0200188 int payload;
189 int ptime, ptime2 = 0;
190 char audio_name[64];
191 int port, rc;
192 char ipv4[16];
193
Philipp Maier8970c492017-10-11 13:33:42 +0200194 OSMO_ASSERT(endp);
195 OSMO_ASSERT(conn);
196 OSMO_ASSERT(p);
197
198 rtp = &conn->end;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200199 memset(&codecs, 0, sizeof(codecs));
200
201 for_each_line(line, p->save) {
202 switch (line[0]) {
203 case 'o':
204 case 's':
205 case 't':
206 case 'v':
207 /* skip these SDP attributes */
208 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200209 case 'a':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200210 if (sscanf(line, "a=rtpmap:%d %63s",
211 &payload, audio_name) == 2) {
Philipp Maierc7a228a2017-10-18 11:42:25 +0200212 codecs_update(tmp_ctx, codecs,
213 codecs_used, payload, audio_name);
214 } else
215 if (sscanf
216 (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 Maierbc0346e2018-06-07 09:52:16 +0200221 } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
222 rtp->maximum_packet_time = ptime2;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200223 }
224 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200225 case 'm':
Philipp Maierbc0346e2018-06-07 09:52:16 +0200226 rc = sscanf(line, "m=audio %d RTP/AVP", &port);
227 if (rc == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200228 rtp->rtp_port = htons(port);
229 rtp->rtcp_port = htons(port + 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200230 }
Philipp Maierbc0346e2018-06-07 09:52:16 +0200231
232 rc = pt_from_sdp(conn->conn, codecs,
233 ARRAY_SIZE(codecs), line);
234 if (rc > 0)
235 codecs_used = rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200236 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200237 case 'c':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200238
239 if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
240 inet_aton(ipv4, &rtp->addr);
241 }
242 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200243 default:
244 if (p->endp)
245 LOGP(DLMGCP, LOGL_NOTICE,
246 "Unhandled SDP option: '%c'/%d on 0x%x\n",
Philipp Maierc7a228a2017-10-18 11:42:25 +0200247 line[0], line[0],
248 ENDPOINT_NUMBER(p->endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200249 else
250 LOGP(DLMGCP, LOGL_NOTICE,
251 "Unhandled SDP option: '%c'/%d\n",
252 line[0], line[0]);
253 break;
254 }
255 }
Philipp Maierbc0346e2018-06-07 09:52:16 +0200256 OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200257
Philipp Maierbc0346e2018-06-07 09:52:16 +0200258 /* So far we have only set the payload type in the codec struct. Now we
259 * fill up the remaining fields of the codec description with some default
260 * information */
261 codecs_initialize(tmp_ctx, codecs, codecs_used);
262
263 /* Store parsed codec information */
Philipp Maierdbd70c72018-05-25 11:07:31 +0200264 for (i = 0; i < codecs_used; i++) {
Philipp Maierbc0346e2018-06-07 09:52:16 +0200265 rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
266 if (rc < 0)
267 LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200268 }
269
270 talloc_free(tmp_ctx);
Philipp Maierdbd70c72018-05-25 11:07:31 +0200271
Philipp Maierbc0346e2018-06-07 09:52:16 +0200272 LOGP(DLMGCP, LOGL_NOTICE,
273 "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
274 ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
275 rtp->packet_duration_ms);
276 if (codecs_used == 0)
277 LOGPC(DLMGCP, LOGL_NOTICE, "none");
278 for (i = 0; i < codecs_used; i++) {
279 LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
280 rtp->codecs[i].payload_type,
281 rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
282 LOGPC(DLMGCP, LOGL_NOTICE, " ");
283 }
284 LOGPC(DLMGCP, LOGL_NOTICE, "\n");
285
Philipp Maierdbd70c72018-05-25 11:07:31 +0200286 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200287}
288
Philipp Maier8482e832018-08-02 17:22:40 +0200289
290/* Add rtpmap string to the sdp payload, but only when the payload type falls
291 * into the dynamic payload type range */
292static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
293{
294 int rc;
295
296 if (payload_type >= 96 && payload_type <= 127) {
297 if (!audio_name)
298 return -EINVAL;
299 rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
300 if (rc < 0)
301 return -EINVAL;
302 }
303
304 return 0;
305}
306
Philipp Maier910189d2018-08-02 17:45:42 +0200307/* Add audio string to sdp payload */
308static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
309{
310 int rc;
311 unsigned int i;
312
313 if (payload_types_len < 0)
314 return -EINVAL;
315
316 rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
317 if (rc < 0)
318 return -EINVAL;
319
320 for (i = 0; i < payload_types_len; i++) {
321 rc = msgb_printf(sdp, " %d", payload_types[i]);
322 if (rc < 0)
323 return -EINVAL;
324 }
325
326 rc = msgb_printf(sdp, "\r\n");
327 if (rc < 0)
328 return -EINVAL;
329
330 return 0;
331}
332
Philipp Maier8970c492017-10-11 13:33:42 +0200333/*! Generate SDP response string.
334 * \param[in] endp trunk endpoint.
335 * \param[in] conn associated rtp connection.
336 * \param[out] sdp msg buffer to append resulting SDP string data.
337 * \param[in] addr IPV4 address string (e.g. 192.168.100.1).
338 * \returns 0 on success, -1 on failure. */
339int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
340 const struct mgcp_conn_rtp *conn, struct msgb *sdp,
341 const char *addr)
342{
343 const char *fmtp_extra;
344 const char *audio_name;
345 int payload_type;
346 int rc;
Philipp Maier910189d2018-08-02 17:45:42 +0200347 int payload_types[1];
Philipp Maier8970c492017-10-11 13:33:42 +0200348
349 OSMO_ASSERT(endp);
350 OSMO_ASSERT(conn);
351 OSMO_ASSERT(sdp);
352 OSMO_ASSERT(addr);
353
354 /* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
355 endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
356 &payload_type, &audio_name,
357 &fmtp_extra,
358 (struct mgcp_conn_rtp *)conn);
359
360 rc = msgb_printf(sdp,
361 "v=0\r\n"
Philipp Maier01d24a32017-11-21 17:26:09 +0100362 "o=- %s 23 IN IP4 %s\r\n"
Philipp Maier8970c492017-10-11 13:33:42 +0200363 "s=-\r\n"
364 "c=IN IP4 %s\r\n"
365 "t=0 0\r\n", conn->conn->id, addr, addr);
366
367 if (rc < 0)
368 goto buffer_too_small;
369
370 if (payload_type >= 0) {
Philipp Maier910189d2018-08-02 17:45:42 +0200371
372 payload_types[0] = payload_type;
373 rc = add_audio(sdp, payload_types, 1, conn->end.local_port);
Philipp Maier8970c492017-10-11 13:33:42 +0200374 if (rc < 0)
375 goto buffer_too_small;
376
Philipp Maier8482e832018-08-02 17:22:40 +0200377 if (endp->tcfg->audio_send_name) {
378 rc = add_rtpmap(sdp, payload_type, audio_name);
Philipp Maier8970c492017-10-11 13:33:42 +0200379 if (rc < 0)
380 goto buffer_too_small;
381 }
382
383 if (fmtp_extra) {
384 rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
385
386 if (rc < 0)
387 goto buffer_too_small;
388 }
389 }
390 if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
391 rc = msgb_printf(sdp, "a=ptime:%u\r\n",
392 conn->end.packet_duration_ms);
393 if (rc < 0)
394 goto buffer_too_small;
395 }
396
397 return 0;
398
399buffer_too_small:
400 LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
401 return -1;
402}