blob: 1c3c89afed44ff92b23cfc06074a9d4f6e3f6ff9 [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>
Philipp Maier993ea6b2020-08-04 18:26:50 +020025#include <osmocom/mgcp/osmux.h>
26#include <osmocom/mgcp/mgcp_conn.h>
27#include <osmocom/mgcp/mgcp_protocol.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020028#include <osmocom/mgcp/mgcp_msg.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010029#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020030#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maierbc0346e2018-06-07 09:52:16 +020031#include <osmocom/mgcp/mgcp_codec.h>
Philipp Maier7e37ce62019-03-05 14:00:11 +010032#include <osmocom/mgcp/mgcp_sdp.h>
Philipp Maier993ea6b2020-08-04 18:26:50 +020033#include <osmocom/mgcp/mgcp_protocol.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020034
35#include <errno.h>
Pau Espin Pedrolc5c14302019-07-23 16:19:41 +020036#include <stdlib.h>
37#include <limits.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020038
Philipp Maier228e5912019-03-05 13:56:59 +010039/* Two structs to store intermediate parsing results. The function
40 * mgcp_parse_sdp_data() is using the following two structs as temporary
41 * storage for parsing the SDP codec information. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020042struct sdp_rtp_map {
43 /* the type */
44 int payload_type;
45 /* null, static or later dynamic codec name */
46 char *codec_name;
47 /* A pointer to the original line for later parsing */
48 char *map_line;
49
50 int rate;
51 int channels;
52};
Philipp Maier228e5912019-03-05 13:56:59 +010053struct sdp_fmtp_param {
54 int payload_type;
55 struct mgcp_codec_param param;
56};
57
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020058
Philipp Maierbc0346e2018-06-07 09:52:16 +020059/* Helper function to extrapolate missing codec parameters in a codec mao from
60 * an already filled in payload_type, called from: mgcp_parse_sdp_data() */
Philipp Maier8970c492017-10-11 13:33:42 +020061static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020062{
63 int i;
64
65 for (i = 0; i < used; ++i) {
66 switch (codecs[i].payload_type) {
67 case 0:
68 codecs[i].codec_name = "PCMU";
69 codecs[i].rate = 8000;
70 codecs[i].channels = 1;
71 break;
72 case 3:
73 codecs[i].codec_name = "GSM";
74 codecs[i].rate = 8000;
75 codecs[i].channels = 1;
76 break;
77 case 8:
78 codecs[i].codec_name = "PCMA";
79 codecs[i].rate = 8000;
80 codecs[i].channels = 1;
81 break;
82 case 18:
83 codecs[i].codec_name = "G729";
84 codecs[i].rate = 8000;
85 codecs[i].channels = 1;
86 break;
Philipp Maierbc0346e2018-06-07 09:52:16 +020087 default:
88 codecs[i].codec_name = NULL;
89 codecs[i].rate = 0;
90 codecs[i].channels = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020091 }
92 }
93}
94
Philipp Maierbc0346e2018-06-07 09:52:16 +020095/* Helper function to update codec map information with additional data from
96 * SDP, called from: mgcp_parse_sdp_data() */
Philipp Maier8970c492017-10-11 13:33:42 +020097static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
98 int payload, const char *audio_name)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020099{
100 int i;
101
102 for (i = 0; i < used; ++i) {
103 char audio_codec[64];
104 int rate = -1;
105 int channels = -1;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200106
107 /* Note: We can only update payload codecs that already exist
108 * in our codec list. If we get an unexpected payload type,
109 * we just drop it */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200110 if (codecs[i].payload_type != payload)
111 continue;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200112
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200113 if (sscanf(audio_name, "%63[^/]/%d/%d",
Philipp Maierd8d7b4b2017-10-18 11:45:35 +0200114 audio_codec, &rate, &channels) < 1) {
115 LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
116 audio_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200117 continue;
118 }
119
120 codecs[i].map_line = talloc_strdup(ctx, audio_name);
121 codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
122 codecs[i].rate = rate;
123 codecs[i].channels = channels;
124 return;
125 }
126
Philipp Maierd8d7b4b2017-10-18 11:45:35 +0200127 LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
128 audio_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200129}
130
Philipp Maierbc0346e2018-06-07 09:52:16 +0200131/* Extract payload types from SDP, also check for duplicates */
132static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
133 unsigned int codecs_len, char *sdp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200134{
Philipp Maierbc0346e2018-06-07 09:52:16 +0200135 char *str;
136 char *str_ptr;
137 char *pt_str;
Pau Espin Pedrolc5c14302019-07-23 16:19:41 +0200138 char *pt_end;
Pau Espin Pedrola2b1c5e2019-07-26 14:13:14 +0200139 unsigned long int pt;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200140 unsigned int count = 0;
141 unsigned int i;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200142
Philipp Maierbc0346e2018-06-07 09:52:16 +0200143 str = talloc_zero_size(ctx, strlen(sdp) + 1);
144 str_ptr = str;
145 strcpy(str_ptr, sdp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200146
Philipp Maierbc0346e2018-06-07 09:52:16 +0200147 str_ptr = strstr(str_ptr, "RTP/AVP ");
148 if (!str_ptr)
149 goto exit;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200150
Philipp Maierbc0346e2018-06-07 09:52:16 +0200151 pt_str = strtok(str_ptr, " ");
152 if (!pt_str)
153 goto exit;
154
155 while (1) {
156 /* Do not allow excessive payload types */
157 if (count > codecs_len)
158 goto error;
159
160 pt_str = strtok(NULL, " ");
161 if (!pt_str)
162 break;
163
Pau Espin Pedrolc5c14302019-07-23 16:19:41 +0200164 errno = 0;
165 pt = strtoul(pt_str, &pt_end, 0);
166 if ((errno == ERANGE && pt == ULONG_MAX) || (errno && !pt) ||
167 pt_str == pt_end)
168 goto error;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200169
Pau Espin Pedrola2b1c5e2019-07-26 14:13:14 +0200170 if (pt >> 7) /* PT is 7 bit field, higher values not allowed */
171 goto error;
172
Philipp Maierbc0346e2018-06-07 09:52:16 +0200173 /* Do not allow duplicate payload types */
174 for (i = 0; i < count; i++)
175 if (codecs[i].payload_type == pt)
176 goto error;
177
178 codecs[count].payload_type = pt;
179 count++;
180 }
181
182exit:
183 talloc_free(str);
184 return count;
185error:
186 talloc_free(str);
187 return -EINVAL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200188}
189
Philipp Maier228e5912019-03-05 13:56:59 +0100190/* Extract fmtp parameters from SDP, called from: mgcp_parse_sdp_data() */
191static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp)
192{
193 char *str;
194 char *str_ptr;
195 char *param_str;
196 unsigned int pt;
197 unsigned int count = 0;
198 char delimiter;
199 unsigned int amr_octet_aligned;
200
201 memset(fmtp_param, 0, sizeof(*fmtp_param));
202
203 str = talloc_zero_size(ctx, strlen(sdp) + 1);
204 str_ptr = str;
205 strcpy(str_ptr, sdp);
206
207 /* Check if the input string begins with an fmtp token */
208 str_ptr = strstr(str_ptr, "fmtp:");
209 if (!str_ptr)
210 goto exit;
211 str_ptr += 5;
212
213 /* Extract payload type */
214 if (sscanf(str_ptr, "%u ", &pt) != 1)
215 goto error;
216 fmtp_param->payload_type = pt;
217
218 /* Advance pointer to the beginning of the parameter section and
219 * tokenize string */
220 str_ptr = strstr(str_ptr, " ");
221 if (!str_ptr)
222 goto error;
223 str_ptr++;
224
225 param_str = strtok(str_ptr, " ");
226 if (!param_str)
227 goto exit;
228
229 while (1) {
230 /* Make sure that we don't get trapped in an endless loop */
231 if (count > 256)
232 goto error;
233
234 /* Chop off delimiters ';' at the end */
235 delimiter = str_ptr[strlen(str_ptr) - 1];
236 if (delimiter == ';' || delimiter == ',')
237 str_ptr[strlen(str_ptr) - 1] = '\0';
238
239 /* AMR octet aligned parameter */
240 if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
241 fmtp_param->param.amr_octet_aligned_present = true;
242 fmtp_param->param.amr_octet_aligned = false;
243 if (amr_octet_aligned == 1)
244 fmtp_param->param.amr_octet_aligned = true;
245
246 }
247
248 param_str = strtok(NULL, " ");
249 if (!param_str)
250 break;
251 count++;
252 }
253
254exit:
255 talloc_free(str);
256 return 0;
257error:
258 talloc_free(str);
259 return -EINVAL;
260}
261
262/* Pick optional fmtp parameters by payload type, if there are no fmtp
263 * parameters, a nullpointer is returned */
264static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
265{
266 unsigned int i;
267
268 for (i = 0; i < fmtp_params_len; i++) {
269 if (fmtp_params[i].payload_type == pt)
270 return &fmtp_params[i].param;
271 }
272
273 return NULL;
274}
275
Philipp Maier8970c492017-10-11 13:33:42 +0200276/*! Analyze SDP input string.
277 * \param[in] endp trunk endpoint.
278 * \param[out] conn associated rtp connection.
279 * \param[out] caller provided memory to store the parsing results.
Philipp Maier8970c492017-10-11 13:33:42 +0200280 *
281 * Note: In conn (conn->end) the function returns the packet duration,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200282 * rtp port, rtcp port and the codec information.
283 * \returns 0 on success, -1 on failure. */
Philipp Maier8970c492017-10-11 13:33:42 +0200284int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200285 struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200286{
Philipp Maierbc0346e2018-06-07 09:52:16 +0200287 struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
288 unsigned int codecs_used = 0;
Philipp Maier228e5912019-03-05 13:56:59 +0100289 struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
290 unsigned int fmtp_used = 0;
291 struct mgcp_codec_param *codec_param;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200292 char *line;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200293 unsigned int i;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200294 void *tmp_ctx = talloc_new(NULL);
Philipp Maier8970c492017-10-11 13:33:42 +0200295 struct mgcp_rtp_end *rtp;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200296
Philipp Maierc7a228a2017-10-18 11:42:25 +0200297 int payload;
298 int ptime, ptime2 = 0;
299 char audio_name[64];
300 int port, rc;
301 char ipv4[16];
302
Philipp Maier8970c492017-10-11 13:33:42 +0200303 OSMO_ASSERT(endp);
304 OSMO_ASSERT(conn);
305 OSMO_ASSERT(p);
306
307 rtp = &conn->end;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200308 memset(&codecs, 0, sizeof(codecs));
309
310 for_each_line(line, p->save) {
311 switch (line[0]) {
312 case 'o':
313 case 's':
314 case 't':
315 case 'v':
316 /* skip these SDP attributes */
317 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200318 case 'a':
Philipp Maiere7ae69a2019-03-05 15:17:36 +0100319 if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
320 codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
321 break;
322 }
323
324 if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200325 if (ptime2 > 0 && ptime2 != ptime)
326 rtp->packet_duration_ms = 0;
327 else
328 rtp->packet_duration_ms = ptime;
Philipp Maiere7ae69a2019-03-05 15:17:36 +0100329 break;
330 }
331
332 if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
Philipp Maierbc0346e2018-06-07 09:52:16 +0200333 rtp->maximum_packet_time = ptime2;
Philipp Maiere7ae69a2019-03-05 15:17:36 +0100334 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200335 }
Philipp Maier228e5912019-03-05 13:56:59 +0100336
337 if (strncmp("a=fmtp:", line, 6) == 0) {
338 rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line);
339 if (rc >= 0)
340 fmtp_used++;
341 break;
342 }
343
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200344 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200345 case 'm':
Philipp Maierbc0346e2018-06-07 09:52:16 +0200346 rc = sscanf(line, "m=audio %d RTP/AVP", &port);
347 if (rc == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200348 rtp->rtp_port = htons(port);
349 rtp->rtcp_port = htons(port + 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200350 }
Philipp Maierbc0346e2018-06-07 09:52:16 +0200351
352 rc = pt_from_sdp(conn->conn, codecs,
353 ARRAY_SIZE(codecs), line);
354 if (rc > 0)
355 codecs_used = rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200356 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200357 case 'c':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200358
359 if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
360 inet_aton(ipv4, &rtp->addr);
361 }
362 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200363 default:
364 if (p->endp)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200365 /* TODO: Check spec: We used the bare endpoint number before,
366 * now we use the endpoint name as a whole? Is this allowed? */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200367 LOGP(DLMGCP, LOGL_NOTICE,
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200368 "Unhandled SDP option: '%c'/%d on %s\n",
369 line[0], line[0], endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200370 else
371 LOGP(DLMGCP, LOGL_NOTICE,
372 "Unhandled SDP option: '%c'/%d\n",
373 line[0], line[0]);
374 break;
375 }
376 }
Philipp Maierbc0346e2018-06-07 09:52:16 +0200377 OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200378
Philipp Maierbc0346e2018-06-07 09:52:16 +0200379 /* So far we have only set the payload type in the codec struct. Now we
380 * fill up the remaining fields of the codec description with some default
381 * information */
382 codecs_initialize(tmp_ctx, codecs, codecs_used);
383
384 /* Store parsed codec information */
Philipp Maierdbd70c72018-05-25 11:07:31 +0200385 for (i = 0; i < codecs_used; i++) {
Philipp Maier228e5912019-03-05 13:56:59 +0100386 codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
387 rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200388 if (rc < 0)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200389 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200390 }
391
392 talloc_free(tmp_ctx);
Philipp Maierdbd70c72018-05-25 11:07:31 +0200393
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200394 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200395 "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
396 ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
397 rtp->packet_duration_ms);
398 if (codecs_used == 0)
399 LOGPC(DLMGCP, LOGL_NOTICE, "none");
400 for (i = 0; i < codecs_used; i++) {
401 LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
402 rtp->codecs[i].payload_type,
403 rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
404 LOGPC(DLMGCP, LOGL_NOTICE, " ");
405 }
406 LOGPC(DLMGCP, LOGL_NOTICE, "\n");
407
Philipp Maierdbd70c72018-05-25 11:07:31 +0200408 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200409}
410
Philipp Maier8482e832018-08-02 17:22:40 +0200411
412/* Add rtpmap string to the sdp payload, but only when the payload type falls
413 * into the dynamic payload type range */
414static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
415{
416 int rc;
417
418 if (payload_type >= 96 && payload_type <= 127) {
419 if (!audio_name)
420 return -EINVAL;
421 rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
422 if (rc < 0)
423 return -EINVAL;
424 }
425
426 return 0;
427}
428
Philipp Maier217d31d2019-03-05 14:48:56 +0100429/* Add audio strings to sdp payload */
Philipp Maier910189d2018-08-02 17:45:42 +0200430static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
431{
432 int rc;
433 unsigned int i;
434
Philipp Maier910189d2018-08-02 17:45:42 +0200435 rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
436 if (rc < 0)
437 return -EINVAL;
438
439 for (i = 0; i < payload_types_len; i++) {
440 rc = msgb_printf(sdp, " %d", payload_types[i]);
441 if (rc < 0)
442 return -EINVAL;
443 }
444
445 rc = msgb_printf(sdp, "\r\n");
446 if (rc < 0)
447 return -EINVAL;
448
449 return 0;
450}
451
Philipp Maier228e5912019-03-05 13:56:59 +0100452/* Add fmtp strings to sdp payload */
453static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
454 const char *fmtp_extra)
455{
456 unsigned int i;
457 int rc;
458 int fmtp_extra_pt = -1;
459 char *fmtp_extra_pars = "";
460
461 /* When no fmtp parameters ara available but an fmtp extra string
462 * is configured, just add the fmtp extra string */
463 if (fmtp_params_len == 0 && fmtp_extra) {
464 return msgb_printf(sdp, "%s\r\n", fmtp_extra);
465 }
466
467 /* When there is fmtp extra configured we dissect it in order to drop
468 * in the configured extra parameters at the right place when
469 * generating the fmtp strings. */
470 if (fmtp_extra) {
471 if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
472 fmtp_extra_pt = -1;
473
474 fmtp_extra_pars = strstr(fmtp_extra, " ");
475
476 if (!fmtp_extra_pars)
477 fmtp_extra_pars = "";
478 else
479 fmtp_extra_pars++;
480 }
481
482 for (i = 0; i < fmtp_params_len; i++) {
483 rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
Vadim Yanitskiy13fae782020-07-07 14:12:26 +0700484 if (rc < 0)
485 return -EINVAL;
Philipp Maier228e5912019-03-05 13:56:59 +0100486
487 /* Add amr octet align parameter */
488 if (fmtp_params[i].param.amr_octet_aligned_present) {
489 if (fmtp_params[i].param.amr_octet_aligned)
490 rc = msgb_printf(sdp, " octet-align=1");
491 else
492 rc = msgb_printf(sdp, " octet-align=0");
493 if (rc < 0)
494 return -EINVAL;
495 }
496
497 /* Append extra parameters from fmtp extra */
498 if (fmtp_params[i].payload_type == fmtp_extra_pt) {
499 rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
500 if (rc < 0)
501 return -EINVAL;
502 }
503
Vadim Yanitskiyb7d395d2020-07-07 14:09:45 +0700504 rc = msgb_printf(sdp, "\r\n");
Philipp Maier228e5912019-03-05 13:56:59 +0100505 if (rc < 0)
506 return -EINVAL;
507 }
508
509 return 0;
510}
511
Philipp Maier8970c492017-10-11 13:33:42 +0200512/*! Generate SDP response string.
513 * \param[in] endp trunk endpoint.
514 * \param[in] conn associated rtp connection.
515 * \param[out] sdp msg buffer to append resulting SDP string data.
516 * \param[in] addr IPV4 address string (e.g. 192.168.100.1).
517 * \returns 0 on success, -1 on failure. */
518int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
519 const struct mgcp_conn_rtp *conn, struct msgb *sdp,
520 const char *addr)
521{
Philipp Maier58128252019-03-06 11:28:18 +0100522 const struct mgcp_rtp_codec *codec;
Philipp Maier8970c492017-10-11 13:33:42 +0200523 const char *fmtp_extra;
524 const char *audio_name;
525 int payload_type;
Philipp Maier228e5912019-03-05 13:56:59 +0100526 struct sdp_fmtp_param fmtp_param;
Philipp Maier8970c492017-10-11 13:33:42 +0200527 int rc;
Philipp Maier910189d2018-08-02 17:45:42 +0200528 int payload_types[1];
Pau Espin Pedrola93c6e92019-05-06 15:23:57 +0200529 int local_port;
Philipp Maier228e5912019-03-05 13:56:59 +0100530 struct sdp_fmtp_param fmtp_params[1];
531 unsigned int fmtp_params_len = 0;
Philipp Maier8970c492017-10-11 13:33:42 +0200532
533 OSMO_ASSERT(endp);
534 OSMO_ASSERT(conn);
535 OSMO_ASSERT(sdp);
536 OSMO_ASSERT(addr);
537
538 /* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
539 endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
Philipp Maier58128252019-03-06 11:28:18 +0100540 &codec, &fmtp_extra,
Philipp Maier8970c492017-10-11 13:33:42 +0200541 (struct mgcp_conn_rtp *)conn);
542
Philipp Maier58128252019-03-06 11:28:18 +0100543 audio_name = codec->audio_name;
544 payload_type = codec->payload_type;
545
Philipp Maier8970c492017-10-11 13:33:42 +0200546 rc = msgb_printf(sdp,
547 "v=0\r\n"
Philipp Maier01d24a32017-11-21 17:26:09 +0100548 "o=- %s 23 IN IP4 %s\r\n"
Philipp Maier8970c492017-10-11 13:33:42 +0200549 "s=-\r\n"
550 "c=IN IP4 %s\r\n"
551 "t=0 0\r\n", conn->conn->id, addr, addr);
552
553 if (rc < 0)
554 goto buffer_too_small;
555
556 if (payload_type >= 0) {
Philipp Maier910189d2018-08-02 17:45:42 +0200557
558 payload_types[0] = payload_type;
Pau Espin Pedrola93c6e92019-05-06 15:23:57 +0200559 if (mgcp_conn_rtp_is_osmux(conn))
560 local_port = endp->cfg->osmux_port;
561 else
562 local_port = conn->end.local_port;
563 rc = add_audio(sdp, payload_types, 1, local_port);
Philipp Maier8970c492017-10-11 13:33:42 +0200564 if (rc < 0)
565 goto buffer_too_small;
566
Philipp Maier14b27a82020-06-02 20:15:30 +0200567 if (endp->trunk->audio_send_name) {
Philipp Maier8482e832018-08-02 17:22:40 +0200568 rc = add_rtpmap(sdp, payload_type, audio_name);
Philipp Maier8970c492017-10-11 13:33:42 +0200569 if (rc < 0)
570 goto buffer_too_small;
571 }
572
Philipp Maier228e5912019-03-05 13:56:59 +0100573 if (codec->param_present) {
574 fmtp_param.payload_type = payload_type;
575 fmtp_param.param = codec->param;
576 fmtp_params[0] = fmtp_param;
577 fmtp_params_len = 1;
Philipp Maier8970c492017-10-11 13:33:42 +0200578 }
Philipp Maier228e5912019-03-05 13:56:59 +0100579 rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
580 if (rc < 0)
581 goto buffer_too_small;
Philipp Maier8970c492017-10-11 13:33:42 +0200582 }
Philipp Maier14b27a82020-06-02 20:15:30 +0200583 if (conn->end.packet_duration_ms > 0 && endp->trunk->audio_send_ptime) {
Philipp Maier8970c492017-10-11 13:33:42 +0200584 rc = msgb_printf(sdp, "a=ptime:%u\r\n",
585 conn->end.packet_duration_ms);
586 if (rc < 0)
587 goto buffer_too_small;
588 }
589
590 return 0;
591
592buffer_too_small:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200593 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
Philipp Maier8970c492017-10-11 13:33:42 +0200594 return -1;
595}