blob: b4da9eb4d3a7bc2ff288932691f3b1328e6193a5 [file] [log] [blame]
James Kuszmaul4a42b182021-01-17 11:32:46 -08001#include "description.h"
2#include "../dtls_fingerprint/fingerprint.h"
3#include "../dtls_parameters/parameters.h"
4#include "../peer_connection/connection.h"
5#include "../peer_connection_configuration/configuration.h"
6#include "../peer_connection_description/description.h"
7#include "../peer_connection_ice_candidate/candidate.h"
8#include <rawrtc/certificate.h>
9#include <rawrtc/config.h>
10#include <rawrtc/dtls_fingerprint.h>
11#include <rawrtc/dtls_parameters.h>
12#include <rawrtc/dtls_transport.h>
13#include <rawrtc/ice_parameters.h>
14#include <rawrtc/peer_connection_description.h>
15#include <rawrtc/peer_connection_ice_candidate.h>
16#include <rawrtcc/code.h>
17#include <rawrtcc/utils.h>
18#include <rawrtcdc/data_transport.h>
19#include <rawrtcdc/sctp_capabilities.h>
20#include <rawrtcdc/sctp_transport.h>
21#include <re.h>
22#include <string.h> // strlen
23
24#define DEBUG_MODULE "peer-connection-description"
25//#define RAWRTC_DEBUG_MODULE_LEVEL 7 // Note: Uncomment this to debug this module only
26#include <rawrtcc/debug.h>
27
28// Constants
29static uint16_t const discard_port = 9;
30static char const sdp_application_dtls_sctp_regex[] = "application [0-9]+ [^ ]+";
31static char const* const sdp_application_dtls_sctp_variants[] = {
32 "DTLS/SCTP",
33 "UDP/DTLS/SCTP",
34 "TCP/DTLS/SCTP",
35};
36static size_t const sdp_application_dtls_sctp_variants_length =
37 ARRAY_SIZE(sdp_application_dtls_sctp_variants);
38static char const sdp_group_regex[] = "group:BUNDLE [^]+";
39static char const sdp_mid_regex[] = "mid:[^]+";
40static char const sdp_ice_options_trickle[] = "ice-options:trickle";
41static char const sdp_ice_username_fragment_regex[] = "ice-ufrag:[^]+";
42static char const sdp_ice_password_regex[] = "ice-pwd:[^]+";
43static char const sdp_ice_lite[] = "ice-lite";
44static char const sdp_dtls_role_regex[] = "setup:[^]+";
45static enum rawrtc_dtls_role const map_enum_dtls_role[] = {
46 RAWRTC_DTLS_ROLE_AUTO,
47 RAWRTC_DTLS_ROLE_CLIENT,
48 RAWRTC_DTLS_ROLE_SERVER,
49};
50static char const* const map_str_dtls_role[] = {
51 "actpass",
52 "active",
53 "passive",
54};
55static size_t const map_dtls_role_length = ARRAY_SIZE(map_enum_dtls_role);
56static char const sdp_dtls_fingerprint_regex[] = "fingerprint:[^ ]+ [^]+";
57static char const sdp_sctp_port_sctmap_regex[] = "sctpmap:[0-9]+[^]*";
58static char const sdp_sctp_port_regex[] = "sctp-port:[0-9]+";
59static char const sdp_sctp_maximum_message_size_regex[] = "max-message-size:[0-9]+";
60static char const sdp_ice_end_of_candidates[] = "end-of-candidates";
61static char const sdp_ice_candidate_head[] = "candidate:";
62static size_t const sdp_ice_candidate_head_length = ARRAY_SIZE(sdp_ice_candidate_head);
63
64// Candidate line
65struct candidate_line {
66 struct le le;
67 struct pl line;
68};
69
70/*
71 * Set session boilerplate
72 */
73static enum rawrtc_code set_session_boilerplate(
74 struct mbuf* const sdp, // not checked
75 char const* const version, // not checked
76 uint32_t const id) {
77 int err;
78
79 // Write session boilerplate
80 err = mbuf_write_str(sdp, "v=0\r\n");
81 err |=
82 mbuf_printf(sdp, "o=sdpartanic-rawrtc-%s %" PRIu32 " 1 IN IP4 127.0.0.1\r\n", version, id);
83 err |= mbuf_write_str(sdp, "s=-\r\n");
84 err |= mbuf_write_str(sdp, "t=0 0\r\n");
85
86 // Done
87 return rawrtc_error_to_code(err);
88}
89
90/*
91 * Set session attributes on SDP.
92 */
93static enum rawrtc_code set_session_attributes(
94 struct mbuf* const sdp, // not checked
95 bool const trickle_ice,
96 char const* const bundled_mids) {
97 int err = 0;
98
99 // Trickle ICE
100 if (trickle_ice) {
101 err = mbuf_write_str(sdp, "a=ice-options:trickle\r\n");
102 }
103
104 // WebRTC identity not supported as of now
105
106 // Bundle media (we currently only support a single SCTP transport and nothing else)
107 if (bundled_mids) {
108 err |= mbuf_printf(sdp, "a=group:BUNDLE %s\r\n", bundled_mids);
109 }
110
111 // Done
112 return rawrtc_error_to_code(err);
113}
114
115/*
116 * Get general attributes from an SDP line.
117 */
118static enum rawrtc_code get_general_attributes(
119 char** const bundled_midsp, // de-referenced, not checked
120 char** const midp, // de-referenced, not checked
121 struct pl* const line // not checked
122) {
123 enum rawrtc_code error;
124 struct pl value;
125
126 // Bundle groups
127 if (!re_regex(line->p, line->l, sdp_group_regex, &value)) {
128 // Check if there is more than one group
129 if (pl_strchr(&value, ' ')) {
130 DEBUG_WARNING("Only one bundle group is supported\n");
131 error = RAWRTC_CODE_NOT_IMPLEMENTED;
132 return error;
133 }
134
135 // Copy group
136 error = rawrtc_error_to_code(pl_strdup(bundled_midsp, &value));
137 if (error) {
138 DEBUG_WARNING("Couldn't copy bundle group\n");
139 return error;
140 }
141 }
142
143 // Media line identification tag
144 if (!re_regex(line->p, line->l, sdp_mid_regex, &value)) {
145 // Copy 'mid'
146 error = rawrtc_error_to_code(pl_strdup(midp, &value));
147 if (error) {
148 DEBUG_WARNING("Couldn't copy 'mid'\n");
149 return error;
150 }
151 }
152
153 // Done
154 return RAWRTC_CODE_NO_VALUE;
155}
156
157/*
158 * Add ICE attributes to SDP media line.
159 */
160static enum rawrtc_code add_ice_attributes(
161 struct mbuf* const sdp, // not checked
162 struct rawrtc_peer_connection_context* const context // not checked
163) {
164 enum rawrtc_code error;
165 struct rawrtc_ice_parameters* parameters;
166 char* username_fragment = NULL;
167 char* password = NULL;
168 int err;
169
170 // Get ICE parameters
171 error = rawrtc_ice_gatherer_get_local_parameters(&parameters, context->ice_gatherer);
172 if (error) {
173 return error;
174 }
175
176 // Get values
177 error = rawrtc_ice_parameters_get_username_fragment(&username_fragment, parameters);
178 error |= rawrtc_ice_parameters_get_password(&password, parameters);
179 if (error) {
180 goto out;
181 }
182
183 // Set username fragment and password
184 err = mbuf_printf(sdp, "a=ice-ufrag:%s\r\n", username_fragment);
185 err |= mbuf_printf(sdp, "a=ice-pwd:%s\r\n", password);
186 error = rawrtc_error_to_code(err);
187
188out:
189 mem_deref(password);
190 mem_deref(username_fragment);
191 mem_deref(parameters);
192 return error;
193}
194
195/*
196 * Get ICE attributes from SDP line.
197 */
198static enum rawrtc_code get_ice_attributes(
199 bool* const trickle_icep, // de-referenced, not checked
200 char** const username_fragmentp, // de-referenced, not checked
201 char** const passwordp, // de-referenced, not checked
202 bool* const ice_litep, // de-referenced, not checked
203 struct pl* const line // not checked
204) {
205 struct pl value;
206
207 // ICE options trickle
208 if (pl_strcmp(line, sdp_ice_options_trickle) == 0) {
209 *trickle_icep = true;
210 return RAWRTC_CODE_SUCCESS;
211 }
212
213 // ICE username fragment
214 if (!re_regex(line->p, line->l, sdp_ice_username_fragment_regex, &value)) {
215 return rawrtc_sdprintf(username_fragmentp, "%r", &value);
216 }
217
218 // ICE password
219 if (!re_regex(line->p, line->l, sdp_ice_password_regex, &value)) {
220 return rawrtc_sdprintf(passwordp, "%r", &value);
221 }
222
223 // ICE lite
224 if (pl_strcmp(line, sdp_ice_lite) == 0) {
225 *ice_litep = true;
226 return RAWRTC_CODE_SUCCESS;
227 }
228
229 // Done
230 return RAWRTC_CODE_NO_VALUE;
231}
232
233/*
234 * Add DTLS fingerprint attributes to SDP media line.
235 */
236static enum rawrtc_code add_dtls_fingerprint_attributes(
237 struct mbuf* const sdp, // not checked
238 struct rawrtc_dtls_parameters* const parameters // not checked
239) {
240 enum rawrtc_code error;
241 struct rawrtc_dtls_fingerprints* fingerprints;
242 size_t i;
243 char* value = NULL;
244
245 // Get fingerprints
246 error = rawrtc_dtls_parameters_get_fingerprints(&fingerprints, parameters);
247 if (error) {
248 return error;
249 }
250
251 // Add fingerprints
252 for (i = 0; i < fingerprints->n_fingerprints; ++i) {
253 struct rawrtc_dtls_fingerprint* const fingerprint = fingerprints->fingerprints[i];
254 enum rawrtc_certificate_sign_algorithm sign_algorithm;
255
256 // Get sign algorithm
257 error = rawrtc_dtls_fingerprint_get_sign_algorithm(&sign_algorithm, fingerprint);
258 if (error) {
259 goto out;
260 }
261
262 // Get fingerprint value
263 error = rawrtc_dtls_fingerprint_get_value(&value, fingerprint);
264 if (error) {
265 goto out;
266 }
267
268 // Add fingerprint attribute
269 error = rawrtc_error_to_code(mbuf_printf(
270 sdp, "a=fingerprint:%s %s\r\n",
271 rawrtc_certificate_sign_algorithm_to_str(sign_algorithm), value));
272 if (error) {
273 goto out;
274 }
275 }
276
277 // Done
278 error = RAWRTC_CODE_SUCCESS;
279
280out:
281 // Un-reference
282 mem_deref(value);
283 mem_deref(fingerprints);
284 return error;
285}
286
287/*
288 * Get DTLS fingerprint attribute from an SDP line.
289 */
290static enum rawrtc_code get_dtls_fingerprint_attributes(
291 struct rawrtc_dtls_fingerprint** const fingerprintp, // de-referenced, not checked
292 struct pl* const line // not checked
293) {
294 struct pl algorithm_pl;
295 struct pl value_pl;
296 enum rawrtc_code error;
297 char* algorithm_str = NULL;
298 char* value_str = NULL;
299 enum rawrtc_certificate_sign_algorithm algorithm;
300
301 // Parse DTLS fingerprint
302 if (re_regex(line->p, line->l, sdp_dtls_fingerprint_regex, &algorithm_pl, &value_pl)) {
303 return RAWRTC_CODE_NO_VALUE;
304 }
305
306 // Copy certificate sign algorithm and value to string
307 error = rawrtc_sdprintf(&algorithm_str, "%r", &algorithm_pl);
308 if (error) {
309 goto out;
310 }
311 error = rawrtc_sdprintf(&value_str, "%r", &value_pl);
312 if (error) {
313 goto out;
314 }
315
316 // Convert certificate sign algorithm
317 error = rawrtc_str_to_certificate_sign_algorithm(&algorithm, algorithm_str);
318 if (error) {
319 // This is allowed to fail, some people still use SHA-1 and we don't support it. But there
320 // may be further fingerprints.
321 DEBUG_WARNING("Unsupported certificate sign algorithm: %r\n", &algorithm_pl);
322 error = RAWRTC_CODE_NO_VALUE;
323 goto out;
324 }
325
326 // Create DTLS fingerprint
327 error = rawrtc_dtls_fingerprint_create(fingerprintp, algorithm, value_str);
328 if (error) {
329 goto out;
330 }
331
332out:
333 // Un-reference
334 mem_deref(value_str);
335 mem_deref(algorithm_str);
336 return error;
337}
338
339/*
340 * Add DTLS transport attributes to SDP media line.
341 */
342static enum rawrtc_code add_dtls_attributes(
343 struct mbuf* const sdp, // not checked
344 struct rawrtc_peer_connection_context* const context, // not checked
345 bool const offering) {
346 enum rawrtc_code error;
347 struct rawrtc_dtls_parameters* parameters;
348 enum rawrtc_dtls_role role;
349 char const* setup_str;
350
351 // Get DTLS parameters
352 error = rawrtc_dtls_transport_get_local_parameters(&parameters, context->dtls_transport);
353 if (error) {
354 return error;
355 }
356
357 // Get DTLS role
358 error = rawrtc_dtls_parameters_get_role(&role, parameters);
359 if (error) {
360 goto out;
361 }
362
363 // Add setup attribute
364 if (offering) {
365 // Note: When offering, we MUST use 'actpass' as specified in JSEP
366 setup_str = "actpass";
367 } else {
368 switch (role) {
369 case RAWRTC_DTLS_ROLE_AUTO:
370 setup_str = "active";
371 break;
372 case RAWRTC_DTLS_ROLE_CLIENT:
373 setup_str = "active";
374 break;
375 case RAWRTC_DTLS_ROLE_SERVER:
376 setup_str = "passive";
377 break;
378 default:
379 error = RAWRTC_CODE_INVALID_STATE;
380 goto out;
381 }
382 }
383 error = rawrtc_error_to_code(mbuf_printf(sdp, "a=setup:%s\r\n", setup_str));
384 if (error) {
385 goto out;
386 }
387
388 // Add fingerprints
389 error = add_dtls_fingerprint_attributes(sdp, parameters);
390 if (error) {
391 goto out;
392 }
393
394 // Add (D)TLS ID
395 error = rawrtc_error_to_code(mbuf_printf(sdp, "a=tls-id:%s\r\n", context->dtls_id));
396 if (error) {
397 goto out;
398 }
399
400out:
401 mem_deref(parameters);
402 return error;
403}
404
405/*
406 * Get DTLS transport attribute from an SDP line.
407 */
408static enum rawrtc_code get_dtls_attributes(
409 enum rawrtc_dtls_role* const rolep, // de-referenced, not checked
410 struct list* const fingerprints, // not checked
411 struct pl* const line // not checked
412) {
413 enum rawrtc_code error;
414 struct pl role_pl;
415 struct rawrtc_dtls_fingerprint* fingerprint;
416
417 // DTLS role
418 if (!re_regex(line->p, line->l, sdp_dtls_role_regex, &role_pl)) {
419 size_t i;
420 for (i = 0; i < map_dtls_role_length; ++i) {
421 if (pl_strcmp(&role_pl, map_str_dtls_role[i]) == 0) {
422 *rolep = map_enum_dtls_role[i];
423 return RAWRTC_CODE_SUCCESS;
424 }
425 }
426 }
427
428 // DTLS fingerprint
429 error = get_dtls_fingerprint_attributes(&fingerprint, line);
430 if (!error) {
431 list_append(fingerprints, &fingerprint->le, fingerprint);
432 }
433 return error;
434}
435
436/*
437 * Add SCTP transport attributes to SDP session.
438 */
439static enum rawrtc_code add_sctp_attributes(
440 struct mbuf* const sdp, // not checked
441 struct rawrtc_sctp_transport* const transport, // not checked
442 struct rawrtc_peer_connection_context* const context, // not checked
443 bool const offering,
444 char const* const remote_media_line,
445 char const* const mid,
446 bool const sctp_sdp_05) {
447 enum rawrtc_code error;
448 uint16_t sctp_port;
449 uint16_t sctp_n_streams;
450 int err;
451
452 // Get SCTP port
453 error = rawrtc_sctp_transport_get_port(&sctp_port, transport);
454 if (error) {
455 return error;
456 }
457
458 // Get SCTP #streams
459 error = rawrtc_sctp_transport_get_n_streams(&sctp_n_streams, transport);
460 if (error) {
461 return error;
462 }
463
464 // Add media section
465 if (remote_media_line) {
466 // Just repeat the remote media line.
467 err = mbuf_printf(sdp, "m=%s\r\n", remote_media_line);
468 } else {
469 if (!sctp_sdp_05) {
470 // Note: We choose UDP here although communication may still happen over ICE-TCP
471 // candidates.
472 // See also: https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-25#section-12.2
473 err = mbuf_printf(
474 sdp, "m=application %" PRIu16 " UDP/DTLS/SCTP webrtc-datachannel\r\n",
475 discard_port);
476 } else {
477 err = mbuf_printf(
478 sdp, "m=application %" PRIu16 " DTLS/SCTP %" PRIu16 "\r\n", discard_port,
479 sctp_port);
480 }
481 }
482 // Add dummy 'c'-line
483 err |= mbuf_write_str(sdp, "c=IN IP4 0.0.0.0\r\n");
484 // Add 'mid' line (if any)
485 if (mid) {
486 err |= mbuf_printf(sdp, "a=mid:%s\r\n", mid);
487 }
488 // Add direction line
489 err |= mbuf_write_str(sdp, "a=sendrecv\r\n");
490 if (err) {
491 return rawrtc_error_to_code(err);
492 }
493
494 // Add ICE attributes
495 error = add_ice_attributes(sdp, context);
496 if (error) {
497 return error;
498 }
499
500 // Add DTLS attributes
501 error = add_dtls_attributes(sdp, context, offering);
502 if (error) {
503 return error;
504 }
505
506 // Set attributes
507 if (!sctp_sdp_05) {
508 // Set SCTP port
509 // Note: Last time I checked, Chrome wasn't able to cope with this
510 err = mbuf_printf(sdp, "a=sctp-port:%" PRIu16 "\r\n", sctp_port);
511 } else {
512 // Set SCTP port, upper layer protocol and number of streams
513 err = mbuf_printf(
514 sdp, "a=sctpmap:%" PRIu16 " webrtc-datachannel %" PRIu16 "\r\n", sctp_port,
515 sctp_n_streams);
516 }
517 if (err) {
518 return rawrtc_error_to_code(err);
519 }
520
521 // Set maximum message size
522 // Note: This isn't part of the 05 version but Firefox can only parse 'max-message-size' but
523 // doesn't understand the old 'sctpmap' one (from 06 to 21).
524 err = mbuf_write_str(sdp, "a=max-message-size:0\r\n");
525 error = rawrtc_error_to_code(err);
526 if (error) {
527 return error;
528 }
529
530 // Done
531 return RAWRTC_CODE_SUCCESS;
532}
533
534/*
535 * Get SCTP transport attributes from an SDP line.
536 */
537static enum rawrtc_code get_sctp_attributes(
538 uint16_t* const portp, // de-referenced, not checked
539 uint64_t* const max_message_sizep, // de-referenced, not checked
540 struct pl* const line // not checked
541) {
542 struct pl port_pl;
543 uint32_t port;
544 struct pl max_message_size_pl;
545
546 // SCTP port (from 'sctpmap' or 'sctp-port')
547 if (!re_regex(line->p, line->l, sdp_sctp_port_sctmap_regex, &port_pl, NULL) ||
548 !re_regex(line->p, line->l, sdp_sctp_port_regex, &port_pl)) {
549 port = pl_u32(&port_pl);
550
551 // Validate port
552 if (port == 0 || port > UINT16_MAX) {
553 DEBUG_WARNING("Invalid SCTP port: %" PRIu32 "\n", port);
554 return RAWRTC_CODE_INVALID_ARGUMENT;
555 }
556
557 // Set port & done
558 *portp = (uint16_t) port;
559 return RAWRTC_CODE_SUCCESS;
560 }
561
562 // SCTP maximum message size
563 // Note: Theoretically, there's another approach as part of 'sctmap' which has been deprecated
564 // but I doubt anyone ever implemented that.
565 if (!re_regex(line->p, line->l, sdp_sctp_maximum_message_size_regex, &max_message_size_pl)) {
566 *max_message_sizep = pl_u64(&max_message_size_pl);
567 return RAWRTC_CODE_SUCCESS;
568 }
569
570 // Done
571 return RAWRTC_CODE_NO_VALUE;
572}
573
574/*
575 * Get an ICE candidate from the description.
576 */
577static enum rawrtc_code get_ice_candidate_attributes(
578 struct list* const candidate_lines, // not checked
579 bool* const end_of_candidatesp, // de-referenced, not checked
580 struct pl* const line // not checked
581) {
582 bool add_candidate_line = false;
583 struct pl* use_line = NULL;
584
585 // ICE candidate
586 if (line->l >= sdp_ice_candidate_head_length) {
587 struct pl candidate_pl = {
588 .p = line->p,
589 .l = sdp_ice_candidate_head_length - 1,
590 };
591 if (pl_strcmp(&candidate_pl, sdp_ice_candidate_head) == 0) {
592 add_candidate_line = true;
593 use_line = line;
594 }
595 }
596
597 // End of candidates
598 if (!add_candidate_line && pl_strcmp(line, sdp_ice_end_of_candidates) == 0) {
599 add_candidate_line = true;
600 use_line = NULL;
601 *end_of_candidatesp = true;
602 }
603
604 // Create candidate line (if any)
605 if (add_candidate_line) {
606 struct candidate_line* const candidate_line = mem_zalloc(sizeof(*candidate_line), NULL);
607 if (!candidate_line) {
608 DEBUG_WARNING("Unable to create candidate line, no memory\n");
609 return RAWRTC_CODE_NO_MEMORY;
610 }
611
612 // Set fields
613 // Warning: The line is NOT copied - it's just a pointer to some memory provided by
614 // the caller!
615 if (use_line) {
616 candidate_line->line = *use_line;
617 }
618
619 // Add candidate line to list & done
620 list_append(candidate_lines, &candidate_line->le, candidate_line);
621 return RAWRTC_CODE_SUCCESS;
622 } else {
623 return RAWRTC_CODE_NO_VALUE;
624 }
625}
626
627/*
628 * Destructor for an existing peer connection description.
629 */
630static void rawrtc_peer_connection_description_destroy(void* arg) {
631 struct rawrtc_peer_connection_description* const description = arg;
632
633 // Un-reference
634 mem_deref(description->sdp);
635 mem_deref(description->sctp_capabilities);
636 mem_deref(description->dtls_parameters);
637 mem_deref(description->ice_parameters);
638 list_flush(&description->ice_candidates);
639 mem_deref(description->mid);
640 mem_deref(description->remote_media_line);
641 mem_deref(description->bundled_mids);
642 mem_deref(description->connection);
643}
644
645/*
646 * Create a description by creating an offer or answer.
647 */
648enum rawrtc_code rawrtc_peer_connection_description_create_internal(
649 struct rawrtc_peer_connection_description** const descriptionp, // de-referenced
650 struct rawrtc_peer_connection* const connection,
651 bool const offering) {
652 struct rawrtc_peer_connection_context* context;
653 struct rawrtc_peer_connection_description* remote_description;
654 struct rawrtc_peer_connection_description* local_description;
655 enum rawrtc_code error;
656 struct mbuf* sdp = NULL;
657 enum rawrtc_data_transport_type data_transport_type;
658 void* data_transport = NULL;
659
660 // Check arguments
661 if (!descriptionp || !connection) {
662 return RAWRTC_CODE_INVALID_ARGUMENT;
663 }
664
665 // Get context
666 context = &connection->context;
667
668 // Ensure a data transport has been set (otherwise, there would be nothing to do)
669 if (!context->data_transport) {
670 DEBUG_WARNING("No data transport set\n");
671 return RAWRTC_CODE_NO_VALUE;
672 }
673
674 // Ensure a remote description is available (when answering)
675 remote_description = connection->remote_description;
676 if (!offering && !remote_description) {
677 DEBUG_WARNING("No remote description set\n");
678 return RAWRTC_CODE_INVALID_ARGUMENT;
679 }
680
681 // Allocate
682 local_description =
683 mem_zalloc(sizeof(*local_description), rawrtc_peer_connection_description_destroy);
684 if (!local_description) {
685 return RAWRTC_CODE_NO_MEMORY;
686 }
687
688 // Set initial values
689 local_description->connection = mem_ref(connection); // Warning: Circular reference
690 local_description->end_of_candidates = false;
691 if (offering) {
692 local_description->type = RAWRTC_SDP_TYPE_OFFER;
693 local_description->trickle_ice = true;
694 error =
695 rawrtc_strdup(&local_description->bundled_mids, RAWRTC_PEER_CONNECTION_DESCRIPTION_MID);
696 if (error) {
697 goto out;
698 }
699 local_description->media_line_index = 0; // Since we only support one media line...
700 error = rawrtc_strdup(&local_description->mid, RAWRTC_PEER_CONNECTION_DESCRIPTION_MID);
701 if (error) {
702 goto out;
703 }
704 local_description->sctp_sdp_05 = connection->configuration->sctp_sdp_05;
705 } else {
706 local_description->type = RAWRTC_SDP_TYPE_ANSWER;
707 local_description->trickle_ice = remote_description->trickle_ice;
708 local_description->bundled_mids = mem_ref(remote_description->bundled_mids);
709 local_description->remote_media_line = mem_ref(remote_description->remote_media_line);
710 local_description->media_line_index = remote_description->media_line_index;
711 local_description->mid = mem_ref(remote_description->mid);
712 local_description->sctp_sdp_05 = remote_description->sctp_sdp_05;
713 }
714
715 // Create buffer for local description
716 sdp = mbuf_alloc(RAWRTC_PEER_CONNECTION_DESCRIPTION_DEFAULT_SIZE);
717 if (!sdp) {
718 error = RAWRTC_CODE_NO_MEMORY;
719 goto out;
720 }
721
722 // Set session boilerplate
723 error = set_session_boilerplate(sdp, RAWRTC_VERSION, rand_u32());
724 if (error) {
725 goto out;
726 }
727
728 // Set session attributes
729 error = set_session_attributes(
730 sdp, local_description->trickle_ice, local_description->bundled_mids);
731 if (error) {
732 goto out;
733 }
734
735 // Get data transport
736 error = rawrtc_data_transport_get_transport(
737 &data_transport_type, &data_transport, context->data_transport);
738 if (error) {
739 return error;
740 }
741
742 // Add data transport
743 switch (data_transport_type) {
744 case RAWRTC_DATA_TRANSPORT_TYPE_SCTP:
745 // Add SCTP transport
746 error = add_sctp_attributes(
747 sdp, data_transport, context, offering, local_description->remote_media_line,
748 local_description->mid, local_description->sctp_sdp_05);
749 if (error) {
750 goto out;
751 }
752 break;
753 default:
754 error = RAWRTC_CODE_UNKNOWN_ERROR;
755 goto out;
756 }
757
758 // Reference SDP
759 local_description->sdp = mem_ref(sdp);
760
761 // Debug
762 DEBUG_PRINTF(
763 "Description (internal):\n%H\n", rawrtc_peer_connection_description_debug,
764 local_description);
765
766out:
767 mem_deref(data_transport);
768 mem_deref(sdp);
769 if (error) {
770 mem_deref(local_description);
771 } else {
772 // Set pointer & done
773 *descriptionp = local_description;
774 }
775 return error;
776}
777
778/*
779 * Add an ICE candidate to the description.
780 */
781enum rawrtc_code rawrtc_peer_connection_description_add_candidate(
782 struct rawrtc_peer_connection_description* const description,
783 struct rawrtc_peer_connection_ice_candidate* const candidate // nullable
784) {
785 enum rawrtc_code error;
786
787 // Check arguments
788 if (!description) {
789 return RAWRTC_CODE_INVALID_ARGUMENT;
790 }
791
792 // Write candidate or end of candidates indication
793 if (candidate) {
794 char* candidate_sdp;
795
796 // Already written?
797 if (description->end_of_candidates) {
798 return RAWRTC_CODE_INVALID_STATE;
799 }
800
801 // Get candidate SDP
802 error = rawrtc_peer_connection_ice_candidate_get_sdp(&candidate_sdp, candidate);
803 if (error) {
804 return error;
805 }
806
807 // TODO: We would have to get the associated 'mid', media line index and username fragment
808 // as well and...
809 //
810 // * inject the candidate at the correct place (compare 'mid' or line index), and
811 // * compare the username fragment against the one that's currently active (once we
812 // support ICE restarts).
813
814 // Write candidate to SDP
815 // Note: We only have one media line, so it should be fine to append this to the end
816 error = rawrtc_error_to_code(mbuf_printf(description->sdp, "a=%s\r\n", candidate_sdp));
817 if (error) {
818 DEBUG_WARNING(
819 "Couldn't write candidate to description, reason: %s\n", rawrtc_code_to_str(error));
820 mem_deref(candidate_sdp);
821 return error;
822 }
823
824 // Debug
825 DEBUG_PRINTF("Added candidate line: %s\n", candidate_sdp);
826 mem_deref(candidate_sdp);
827 } else {
828 // Already written?
829 if (description->end_of_candidates) {
830 DEBUG_WARNING("End of candidates has already been written\n");
831 return RAWRTC_CODE_SUCCESS;
832 }
833
834 // Write end of candidates into SDP
835 error = rawrtc_error_to_code(mbuf_write_str(description->sdp, "a=end-of-candidates\r\n"));
836 if (error) {
837 return error;
838 }
839 description->end_of_candidates = true;
840
841 // Debug
842 DEBUG_PRINTF(
843 "Description (end-of-candidates):\n%H\n", rawrtc_peer_connection_description_debug,
844 description);
845 }
846
847 // Done
848 return RAWRTC_CODE_SUCCESS;
849}
850
851// Helper for parsing SDP attributes
852#define HANDLE_ATTRIBUTE(code) \
853 error = code; \
854 if (error == RAWRTC_CODE_SUCCESS) { \
855 break; \
856 } else if (error != RAWRTC_CODE_NO_VALUE) { \
857 goto out; \
858 break; \
859 }
860
861/*
862 * Create a description by parsing it from SDP.
863 * `*descriptionp` must be unreferenced.
864 */
865enum rawrtc_code rawrtc_peer_connection_description_create(
866 struct rawrtc_peer_connection_description** const descriptionp, // de-referenced
867 enum rawrtc_sdp_type const type,
868 char const* const sdp) {
869 enum rawrtc_code error;
870 struct rawrtc_peer_connection_description* remote_description;
871 char const* cursor;
872 bool media_line = false;
873 struct le* le;
874
875 // ICE parameters
876 char* ice_username_fragment = NULL;
877 char* ice_password = NULL;
878 bool ice_lite = false;
879
880 // DTLS parameters
881 enum rawrtc_dtls_role dtls_role = RAWRTC_DTLS_ROLE_AUTO;
882 struct list dtls_fingerprints = LIST_INIT;
883
884 // SCTP capabilities
885 uint64_t sctp_max_message_size = RAWRTC_PEER_CONNECTION_DESCRIPTION_DEFAULT_MAX_MESSAGE_SIZE;
886
887 // ICE candidate lines (temporarily stored, so it can be parsed later)
888 struct list ice_candidate_lines = LIST_INIT;
889
890 // Check arguments
891 if (!descriptionp || !sdp) {
892 return RAWRTC_CODE_INVALID_ARGUMENT;
893 }
894
895 // We only accept 'offer' or 'answer' at the moment
896 // TODO: Handle the other ones as well
897 if (type != RAWRTC_SDP_TYPE_OFFER && type != RAWRTC_SDP_TYPE_ANSWER) {
898 DEBUG_WARNING("Only 'offer' or 'answer' descriptions can be handled at the moment\n");
899 return RAWRTC_CODE_NOT_IMPLEMENTED;
900 }
901
902 // Allocate
903 remote_description =
904 mem_zalloc(sizeof(*remote_description), rawrtc_peer_connection_description_destroy);
905 if (!remote_description) {
906 return RAWRTC_CODE_NO_MEMORY;
907 }
908
909 // Set fields to initial values
910 remote_description->type = type;
911 remote_description->trickle_ice = false;
912 remote_description->media_line_index = 0; // Since we only support one media line...
913 remote_description->sctp_sdp_05 = true;
914 list_init(&remote_description->ice_candidates);
915 remote_description->sctp_port = RAWRTC_PEER_CONNECTION_DESCRIPTION_DEFAULT_SCTP_PORT;
916
917 // Find required session and media attributes
918 cursor = sdp;
919 while (*cursor != '\0') {
920 struct pl line;
921 char sdp_type;
922
923 // Ignore lines beginning with '\r' or '\n'
924 if (*cursor == '\r' || *cursor == '\n') {
925 ++cursor;
926 continue;
927 }
928
929 // Find next line or end of string
930 for (line.p = cursor, line.l = 0; *cursor != '\r' && *cursor != '\n' && *cursor != '\0';
931 ++cursor, ++line.l) {
932 }
933
934 // Get line type and move line cursor to value
935 if (line.l < 2) {
936 DEBUG_WARNING("Invalid SDP line: %r\n", &line);
937 break;
938 }
939 sdp_type = *line.p;
940 pl_advance(&line, 2);
941
942 // Are we interested in this line?
943 switch (sdp_type) {
944 case 'a': {
945 // Be aware we're using a macro here which does the following:
946 //
947 // * if the function returns 'success', break (and therefore don't continue
948 // parsing other attributes on this line).
949 // * if the function returns 'no value', do nothing (and therefore continue parsing
950 // other attributes on this line).
951 // * if the function returns anything else (which indicates an error), set 'error'
952 // and jump to 'out'.
953 HANDLE_ATTRIBUTE(get_general_attributes(
954 &remote_description->bundled_mids, &remote_description->mid, &line));
955 HANDLE_ATTRIBUTE(get_ice_attributes(
956 &remote_description->trickle_ice, &ice_username_fragment, &ice_password,
957 &ice_lite, &line));
958 HANDLE_ATTRIBUTE(get_dtls_attributes(&dtls_role, &dtls_fingerprints, &line));
959 HANDLE_ATTRIBUTE(get_sctp_attributes(
960 &remote_description->sctp_port, &sctp_max_message_size, &line));
961 HANDLE_ATTRIBUTE(get_ice_candidate_attributes(
962 &ice_candidate_lines, &remote_description->end_of_candidates, &line));
963 break;
964 }
965 case 'm': {
966 struct pl application;
967 size_t i;
968
969 // Ensure amount of media lines is exactly one
970 if (media_line) {
971 DEBUG_WARNING("Unable to handle more than one media line\n");
972 error = RAWRTC_CODE_NOT_IMPLEMENTED;
973 goto out;
974 }
975
976 // Parse media line
977 if (re_regex(line.p, line.l, sdp_application_dtls_sctp_regex, NULL, &application)) {
978 DEBUG_WARNING("Unsupport media line: %r\n", &line);
979 error = RAWRTC_CODE_NOT_IMPLEMENTED;
980 goto out;
981 }
982
983 // Check if the application matches some kind of DTLS/SCTP variant (ugh...)
984 for (i = 0; i < sdp_application_dtls_sctp_variants_length; ++i) {
985 if (pl_strcmp(&application, sdp_application_dtls_sctp_variants[i]) == 0) {
986 media_line = true;
987 }
988 }
989 if (!media_line) {
990 DEBUG_WARNING("Unsupported application on media line: %r\n", &application);
991 error = RAWRTC_CODE_NOT_IMPLEMENTED;
992 goto out;
993 }
994
995 // Copy media line
996 error = rawrtc_sdprintf(&remote_description->remote_media_line, "%r", &line);
997 if (error) {
998 goto out;
999 }
1000
1001 // Done
1002 break;
1003 }
1004 default:
1005 DEBUG_PRINTF(
1006 "Ignoring %s line: %c=%r\n", media_line ? "media" : "session", sdp_type, &line);
1007 break;
1008 }
1009 }
1010
1011 // Return 'no value' in case there was no media line
1012 if (!media_line) {
1013 error = RAWRTC_CODE_NO_VALUE;
1014 goto out;
1015 }
1016
1017 // Create ICE parameters (if possible)
1018 if (ice_username_fragment && ice_password) {
1019 error = rawrtc_ice_parameters_create(
1020 &remote_description->ice_parameters, ice_username_fragment, ice_password, ice_lite);
1021 if (error) {
1022 goto out;
1023 }
1024 }
1025
1026 // Create DTLS parameters (if possible)
1027 if (!list_isempty(&dtls_fingerprints)) {
1028 error = rawrtc_dtls_parameters_create_internal(
1029 &remote_description->dtls_parameters, dtls_role, &dtls_fingerprints);
1030 if (error) {
1031 goto out;
1032 }
1033 }
1034
1035 // Create SCTP capabilities
1036 error = rawrtc_sctp_capabilities_create(
1037 &remote_description->sctp_capabilities, sctp_max_message_size);
1038 if (error) {
1039 goto out;
1040 }
1041
1042 // Late parsing of ICE candidates.
1043 // Note: This is required since the 'mid' and the username fragment may be parsed after a
1044 // candidate has been found.
1045 for (le = list_head(&ice_candidate_lines); le != NULL; le = le->next) {
1046 struct candidate_line* const candidate_line = le->data;
1047
1048 // Create ICE candidate
1049 struct rawrtc_peer_connection_ice_candidate* candidate;
1050 error = rawrtc_peer_connection_ice_candidate_create_internal(
1051 &candidate, &candidate_line->line, remote_description->mid,
1052 &remote_description->media_line_index, ice_username_fragment);
1053 if (error) {
1054 goto out;
1055 }
1056
1057 // Add ICE candidate to the list
1058 DEBUG_PRINTF("Adding ICE candidate to description\n");
1059 list_append(&remote_description->ice_candidates, &candidate->le, candidate);
1060 }
1061
1062 // Copy SDP
1063 remote_description->sdp = mbuf_alloc(strlen(sdp));
1064 if (!remote_description->sdp) {
1065 error = RAWRTC_CODE_NO_MEMORY;
1066 goto out;
1067 }
1068 mbuf_write_str(remote_description->sdp, sdp);
1069
1070 // Debug
1071 DEBUG_PRINTF(
1072 "Description (parsed):\n%H\n", rawrtc_peer_connection_description_debug,
1073 remote_description);
1074
1075 // Done
1076 error = RAWRTC_CODE_SUCCESS;
1077
1078out:
1079 // Un-reference
1080 list_flush(&ice_candidate_lines);
1081 list_flush(&dtls_fingerprints);
1082 mem_deref(ice_password);
1083 mem_deref(ice_username_fragment);
1084 if (error) {
1085 mem_deref(remote_description);
1086 } else {
1087 // Set pointer & done
1088 *descriptionp = remote_description;
1089 }
1090 return error;
1091}