6 * Copyright (C) 2013-2017 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2010 Jakub Adam <jakub.adam@ktknet.cz>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #include "sipe-backend.h"
31 #include "sipe-core.h"
33 #include "sipe-utils.h"
36 append_attribute(struct sdpmedia
*media
, gchar
*attr
)
38 gchar
**parts
= g_strsplit(attr
+ 2, ":", 2);
45 media
->attributes
= sipe_utils_nameval_add(media
->attributes
,
47 parts
[1] ? parts
[1] : "");
53 parse_attributes(struct sdpmsg
*smsg
, const gchar
*msg
) {
54 gchar
**lines
= g_strsplit(msg
, "\r\n", 0);
57 while (*ptr
!= NULL
) {
58 if (g_str_has_prefix(*ptr
, "o=")) {
59 gchar
**parts
= g_strsplit(*ptr
+ 2, " ", 6);
60 if (g_strv_length(parts
) != 6) {
66 smsg
->ip
= g_strdup(parts
[5]);
68 } else if (g_str_has_prefix(*ptr
, "m=")) {
70 struct sdpmedia
*media
;
72 parts
= g_strsplit(*ptr
+ 2, " ", 3);
73 if (g_strv_length(parts
) < 3) {
79 media
= g_new0(struct sdpmedia
, 1);
81 smsg
->media
= g_slist_append(smsg
->media
, media
);
83 media
->name
= g_strdup(parts
[0]);
84 media
->port
= atoi(parts
[1]);
85 media
->encryption_active
=
86 g_strstr_len(parts
[2], -1, "/SAVP") != NULL
;
90 while (*(++ptr
) && !g_str_has_prefix(*ptr
, "m=")) {
92 if (g_str_has_prefix(*ptr
, "a=")) {
93 if (!append_attribute(media
, *ptr
)) {
110 static struct sdpcandidate
* sdpcandidate_copy(struct sdpcandidate
*candidate
);
112 static SipeComponentType
113 parse_component(const gchar
*str
)
116 case 1: return SIPE_COMPONENT_RTP
;
117 case 2: return SIPE_COMPONENT_RTCP
;
118 default: return SIPE_COMPONENT_NONE
;
123 base64_pad(const gchar
* str
)
125 size_t str_len
= strlen(str
);
126 int mod
= str_len
% 4;
129 gchar
*result
= NULL
;
131 gchar
*ptr
= result
= g_malloc(str_len
+ pad
+ 1);
133 memcpy(ptr
, str
, str_len
);
135 memset(ptr
, '=', pad
);
141 return g_strdup(str
);
145 parse_append_candidate_draft_6(gchar
**tokens
, GSList
**candidates
)
147 struct sdpcandidate
*candidate
;
149 if (g_strv_length(tokens
) < 7 || strlen(tokens
[4]) < 3) {
153 candidate
= g_new0(struct sdpcandidate
, 1);
155 candidate
->username
= base64_pad(tokens
[0]);
156 candidate
->component
= parse_component(tokens
[1]);
157 candidate
->password
= base64_pad(tokens
[2]);
159 if (sipe_strequal(tokens
[3], "UDP"))
160 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
161 else if (sipe_strequal(tokens
[3], "TCP"))
162 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
;
164 sdpcandidate_free(candidate
);
168 candidate
->priority
= atoi(tokens
[4] + 2);
169 candidate
->ip
= g_strdup(tokens
[5]);
170 candidate
->port
= atoi(tokens
[6]);
172 *candidates
= g_slist_append(*candidates
, candidate
);
174 // draft 6 candidates are both active and passive
175 if (candidate
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
) {
176 candidate
= sdpcandidate_copy(candidate
);
177 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
178 *candidates
= g_slist_append(*candidates
, candidate
);
185 parse_append_candidate_rfc_5245(gchar
**tokens
, GSList
**candidates
)
187 struct sdpcandidate
*candidate
;
189 if (g_strv_length(tokens
) < 8) {
193 candidate
= g_new0(struct sdpcandidate
, 1);
194 candidate
->foundation
= g_strdup(tokens
[0]);
195 candidate
->component
= parse_component(tokens
[1]);
197 if (sipe_strcase_equal(tokens
[2], "UDP"))
198 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
199 else if (sipe_strcase_equal(tokens
[2], "TCP-ACT"))
200 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
;
201 else if (sipe_strcase_equal(tokens
[2], "TCP-PASS"))
202 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
204 sdpcandidate_free(candidate
);
208 candidate
->priority
= atoi(tokens
[3]);
209 candidate
->ip
= g_strdup(tokens
[4]);
210 candidate
->port
= atoi(tokens
[5]);
212 if (sipe_strcase_equal(tokens
[7], "host"))
213 candidate
->type
= SIPE_CANDIDATE_TYPE_HOST
;
214 else if (sipe_strcase_equal(tokens
[7], "relay"))
215 candidate
->type
= SIPE_CANDIDATE_TYPE_RELAY
;
216 else if (sipe_strcase_equal(tokens
[7], "srflx"))
217 candidate
->type
= SIPE_CANDIDATE_TYPE_SRFLX
;
218 else if (sipe_strcase_equal(tokens
[7], "prflx"))
219 candidate
->type
= SIPE_CANDIDATE_TYPE_PRFLX
;
221 sdpcandidate_free(candidate
);
225 *candidates
= g_slist_append(*candidates
, candidate
);
231 parse_candidates(GSList
*attrs
, SipeIceVersion
*ice_version
, GSList
**candidates
)
236 g_return_val_if_fail(*candidates
== NULL
, FALSE
);
238 *ice_version
= SIPE_ICE_NO_ICE
;
240 while ((attr
= sipe_utils_nameval_find_instance(attrs
, "candidate", i
++))) {
241 gchar
**tokens
= g_strsplit_set(attr
, " ", 0);
244 if (g_strv_length(tokens
) < 7) {
249 if (sipe_strequal(tokens
[6], "typ")) {
250 parsed_ok
= parse_append_candidate_rfc_5245(tokens
,
253 *ice_version
= SIPE_ICE_RFC_5245
;
255 parsed_ok
= parse_append_candidate_draft_6(tokens
,
258 *ice_version
= SIPE_ICE_DRAFT_6
;
268 if (*ice_version
== SIPE_ICE_RFC_5245
) {
269 const gchar
*username
= sipe_utils_nameval_find(attrs
, "ice-ufrag");
270 const gchar
*password
= sipe_utils_nameval_find(attrs
, "ice-pwd");
272 if (username
&& password
) {
274 for (i
= *candidates
; i
; i
= i
->next
) {
275 struct sdpcandidate
*c
= i
->data
;
276 c
->username
= g_strdup(username
);
277 c
->password
= g_strdup(password
);
286 create_legacy_candidates(gchar
*ip
, guint16 port
)
288 struct sdpcandidate
*candidate
;
289 GSList
*candidates
= NULL
;
291 candidate
= g_new0(struct sdpcandidate
, 1);
292 candidate
->foundation
= g_strdup("1");
293 candidate
->component
= SIPE_COMPONENT_RTP
;
294 candidate
->type
= SIPE_CANDIDATE_TYPE_HOST
;
295 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
296 candidate
->ip
= g_strdup(ip
);
297 candidate
->port
= port
;
299 candidates
= g_slist_append(candidates
, candidate
);
301 candidate
= g_new0(struct sdpcandidate
, 1);
302 candidate
->foundation
= g_strdup("1");
303 candidate
->component
= SIPE_COMPONENT_RTCP
;
304 candidate
->type
= SIPE_CANDIDATE_TYPE_HOST
;
305 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
306 candidate
->ip
= g_strdup(ip
);
307 candidate
->port
= port
+ 1;
309 candidates
= g_slist_append(candidates
, candidate
);
315 parse_codec_parameters(GSList
*attrs
, struct sdpcodec
*codec
)
320 while((params
= sipe_utils_nameval_find_instance(attrs
, "fmtp", i
++))) {
324 tokens
= g_strsplit(params
, " ", 0);
325 if (g_strv_length(tokens
) < 1) {
330 if (atoi(tokens
[0]) != codec
->id
) {
335 for (param
= tokens
+ 1; *param
; ++param
) {
336 gchar
**nameval
= g_strsplit(*param
, "=", 2);
338 if (g_strv_length(nameval
) != 2) {
344 sipe_utils_nameval_add(codec
->parameters
,
359 parse_codecs(GSList
*attrs
, SipeMediaType type
, GSList
**codecs
)
364 while ((attr
= sipe_utils_nameval_find_instance(attrs
, "rtpmap", i
++))) {
365 struct sdpcodec
*codec
;
368 tokens
= g_strsplit_set(attr
, " /", 4);
369 if (g_strv_length(tokens
) < 3) {
374 codec
= g_new0(struct sdpcodec
, 1);
375 codec
->id
= atoi(tokens
[0]);
376 codec
->name
= g_strdup(tokens
[1]);
377 codec
->clock_rate
= atoi(tokens
[2]);
380 if (type
== SIPE_MEDIA_AUDIO
) {
381 codec
->channels
= tokens
[3] ? atoi(tokens
[3]) : 1;
386 if (!parse_codec_parameters(attrs
, codec
)) {
387 sdpcodec_free(codec
);
391 *codecs
= g_slist_append(*codecs
, codec
);
398 parse_encryption_key(GSList
*attrs
, guchar
**key
, int *key_id
)
403 while ((attr
= sipe_utils_nameval_find_instance(attrs
, "crypto", i
++))) {
404 gchar
**tokens
= g_strsplit_set(attr
, " :|", 6);
406 if (tokens
[0] && tokens
[1] && tokens
[2] && tokens
[3] && tokens
[4] &&
407 sipe_strcase_equal(tokens
[1], "AES_CM_128_HMAC_SHA1_80") &&
408 sipe_strequal(tokens
[2], "inline") &&
411 *key
= g_base64_decode(tokens
[3], &key_len
);
412 if (key_len
!= SIPE_SRTP_KEY_LEN
) {
416 *key_id
= atoi(tokens
[0]);
428 sdpmsg_parse_msg(const gchar
*msg
)
430 struct sdpmsg
*smsg
= g_new0(struct sdpmsg
, 1);
433 if (!parse_attributes(smsg
, msg
)) {
438 smsg
->ice_version
= SIPE_ICE_NO_ICE
;
439 for (i
= smsg
->media
; i
; i
= i
->next
) {
440 struct sdpmedia
*media
= i
->data
;
442 SipeIceVersion detected_ice_version
;
444 if (!parse_candidates(media
->attributes
, &detected_ice_version
,
445 &media
->candidates
)) {
450 if (media
->port
!= 0) {
451 smsg
->ice_version
= detected_ice_version
;
453 if (!media
->candidates
) {
454 // No a=candidate in SDP message, this seems to be MSOC 2005
455 media
->candidates
= create_legacy_candidates(smsg
->ip
, media
->port
);
459 if (sipe_strequal(media
->name
, "audio"))
460 type
= SIPE_MEDIA_AUDIO
;
461 else if (sipe_strequal(media
->name
, "video"))
462 type
= SIPE_MEDIA_VIDEO
;
463 else if (sipe_strequal(media
->name
, "data"))
464 type
= SIPE_MEDIA_APPLICATION
;
465 else if (sipe_strequal(media
->name
, "applicationsharing"))
466 type
= SIPE_MEDIA_APPLICATION
;
468 // Unknown media type
473 if (!parse_codecs(media
->attributes
, type
, &media
->codecs
)) {
478 parse_encryption_key(media
->attributes
, &media
->encryption_key
,
479 &media
->encryption_key_id
);
486 codecs_to_string(GSList
*codecs
)
488 GString
*result
= g_string_new(NULL
);
490 for (; codecs
; codecs
= codecs
->next
) {
491 struct sdpcodec
*c
= codecs
->data
;
492 GSList
*params
= c
->parameters
;
494 g_string_append_printf(result
,
495 "a=rtpmap:%d %s/%d\r\n",
501 GString
*param_str
= g_string_new(NULL
);
502 int written_params
= 0;
504 g_string_append_printf(param_str
, "a=fmtp:%d", c
->id
);
506 for (; params
; params
= params
->next
) {
507 struct sipnameval
* par
= params
->data
;
508 if (sipe_strequal(par
->name
, "farsight-send-profile")) {
509 // Lync AVMCU doesn't like this property.
513 g_string_append_printf(param_str
, " %s=%s",
514 par
->name
, par
->value
);
518 g_string_append(param_str
, "\r\n");
520 if (written_params
> 0) {
521 g_string_append(result
, param_str
->str
);
524 g_string_free(param_str
, TRUE
);
528 return g_string_free(result
, FALSE
);
532 codec_ids_to_string(GSList
*codecs
)
534 GString
*result
= g_string_new(NULL
);
536 for (; codecs
; codecs
= codecs
->next
) {
537 struct sdpcodec
*c
= codecs
->data
;
538 g_string_append_printf(result
, " %d", c
->id
);
541 return g_string_free(result
, FALSE
);
545 base64_unpad(const gchar
*str
)
547 gchar
*result
= g_strdup(str
);
550 for (ptr
= result
+ strlen(result
); ptr
!= result
; --ptr
) {
551 if (*(ptr
- 1) != '=') {
561 candidates_to_string(GSList
*candidates
, SipeIceVersion ice_version
)
563 GString
*result
= g_string_new("");
565 GSList
*processed_tcp_candidates
= NULL
;
567 for (i
= candidates
; i
; i
= i
->next
) {
568 struct sdpcandidate
*c
= i
->data
;
569 const gchar
*protocol
;
571 gchar
*related
= NULL
;
573 if (ice_version
== SIPE_ICE_RFC_5245
) {
575 switch (c
->protocol
) {
576 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
:
577 protocol
= "TCP-ACT";
579 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
:
580 protocol
= "TCP-PASS";
582 case SIPE_NETWORK_PROTOCOL_UDP
:
586 /* error unknown/unsupported type */
587 protocol
= "UNKNOWN";
592 case SIPE_CANDIDATE_TYPE_HOST
:
595 case SIPE_CANDIDATE_TYPE_RELAY
:
598 case SIPE_CANDIDATE_TYPE_SRFLX
:
601 case SIPE_CANDIDATE_TYPE_PRFLX
:
605 /* error unknown/unsupported type */
611 case SIPE_CANDIDATE_TYPE_RELAY
:
612 case SIPE_CANDIDATE_TYPE_SRFLX
:
613 case SIPE_CANDIDATE_TYPE_PRFLX
:
614 related
= g_strdup_printf("raddr %s rport %d",
622 g_string_append_printf(result
,
623 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
631 related
? related
: "");
634 } else if (ice_version
== SIPE_ICE_DRAFT_6
) {
638 switch (c
->protocol
) {
639 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
:
640 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
: {
641 GSList
*prev_cand
= processed_tcp_candidates
;
642 for (; prev_cand
; prev_cand
= prev_cand
->next
) {
643 struct sdpcandidate
*c2
= (struct sdpcandidate
*)prev_cand
->data
;
645 if (sipe_strequal(c
->ip
, c2
->ip
) &&
646 c
->component
== c2
->component
) {
655 processed_tcp_candidates
=
656 g_slist_append(processed_tcp_candidates
, c
);
660 case SIPE_NETWORK_PROTOCOL_UDP
:
664 /* unknown/unsupported type, ignore */
673 username
= base64_unpad(c
->username
);
674 password
= base64_unpad(c
->password
);
676 g_string_append_printf(result
,
677 "a=candidate:%s %u %s %s 0.%u %s %d\r\n",
691 g_slist_free(processed_tcp_candidates
);
693 return g_string_free(result
, FALSE
);
697 remote_candidates_sort_cb(struct sdpcandidate
*c1
, struct sdpcandidate
*c2
)
699 return c1
->component
- c2
->component
;
703 remote_candidates_to_string(GSList
*candidates
, SipeIceVersion ice_version
)
705 GString
*result
= g_string_new("");
708 // Sort the candidates by increasing component IDs.
709 candidates
= g_slist_sort(candidates
,
710 (GCompareFunc
)remote_candidates_sort_cb
);
712 if (ice_version
== SIPE_ICE_RFC_5245
) {
714 g_string_append(result
, "a=remote-candidates:");
716 for (i
= candidates
; i
; i
= i
->next
) {
717 struct sdpcandidate
*c
= i
->data
;
718 g_string_append_printf(result
, "%u %s %u ",
719 c
->component
, c
->ip
, c
->port
);
722 g_string_append(result
, "\r\n");
723 } else if (ice_version
== SIPE_ICE_DRAFT_6
) {
724 struct sdpcandidate
*c
= candidates
->data
;
725 g_string_append_printf(result
, "a=remote-candidate:%s\r\n",
730 return g_string_free(result
, FALSE
);
734 attributes_to_string(GSList
*attributes
)
736 GString
*result
= g_string_new("");
738 for (; attributes
; attributes
= attributes
->next
) {
739 struct sipnameval
*a
= attributes
->data
;
740 g_string_append_printf(result
, "a=%s", a
->name
);
741 if (!sipe_strequal(a
->value
, ""))
742 g_string_append_printf(result
, ":%s", a
->value
);
743 g_string_append(result
, "\r\n");
746 return g_string_free(result
, FALSE
);
750 media_to_string(const struct sdpmsg
*msg
, const struct sdpmedia
*media
)
754 gchar
*transport_profile
= NULL
;
756 gchar
*media_conninfo
= NULL
;
758 gchar
*codecs_str
= NULL
;
759 gchar
*codec_ids_str
= codec_ids_to_string(media
->codecs
);
761 gchar
*candidates_str
= NULL
;
762 gchar
*remote_candidates_str
= NULL
;
764 gchar
*attributes_str
= NULL
;
765 gchar
*credentials
= NULL
;
767 gchar
*crypto
= NULL
;
769 gboolean uses_tcp_transport
= TRUE
;
771 if (media
->port
!= 0) {
772 if (!sipe_strequal(msg
->ip
, media
->ip
)) {
773 media_conninfo
= g_strdup_printf("c=IN %s %s\r\n",
774 sipe_utils_ip_sdp_address_marker(media
->ip
),
778 codecs_str
= codecs_to_string(media
->codecs
);
779 candidates_str
= candidates_to_string(media
->candidates
, msg
->ice_version
);
780 remote_candidates_str
= remote_candidates_to_string(media
->remote_candidates
,
783 if (media
->remote_candidates
) {
784 struct sdpcandidate
*c
= media
->remote_candidates
->data
;
786 c
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
||
787 c
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
||
788 c
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_SO
;
790 GSList
*candidates
= media
->candidates
;
791 for (; candidates
; candidates
= candidates
->next
) {
792 struct sdpcandidate
*c
= candidates
->data
;
793 if (c
->protocol
== SIPE_NETWORK_PROTOCOL_UDP
) {
794 uses_tcp_transport
= FALSE
;
800 attributes_str
= attributes_to_string(media
->attributes
);
802 if (msg
->ice_version
== SIPE_ICE_RFC_5245
&& media
->candidates
) {
803 struct sdpcandidate
*c
= media
->candidates
->data
;
805 credentials
= g_strdup_printf("a=ice-ufrag:%s\r\n"
811 if (media
->encryption_key
) {
812 gchar
*key_encoded
= g_base64_encode(media
->encryption_key
, SIPE_SRTP_KEY_LEN
);
813 crypto
= g_strdup_printf("a=crypto:%d AES_CM_128_HMAC_SHA1_80 inline:%s|2^31\r\n",
814 media
->encryption_key_id
, key_encoded
);
819 transport_profile
= g_strdup_printf("%sRTP/%sAVP",
820 uses_tcp_transport
? "TCP/" : "",
821 media
->encryption_active
? "S" : "");
823 media_str
= g_strdup_printf("m=%s %d %s%s\r\n"
831 media
->name
, media
->port
, transport_profile
, codec_ids_str
,
832 media_conninfo
? media_conninfo
: "",
833 candidates_str
? candidates_str
: "",
834 crypto
? crypto
: "",
835 remote_candidates_str
? remote_candidates_str
: "",
836 codecs_str
? codecs_str
: "",
837 attributes_str
? attributes_str
: "",
838 credentials
? credentials
: "");
840 g_free(transport_profile
);
841 g_free(media_conninfo
);
843 g_free(codec_ids_str
);
844 g_free(candidates_str
);
845 g_free(remote_candidates_str
);
846 g_free(attributes_str
);
854 sdpmsg_to_string(const struct sdpmsg
*msg
)
856 GString
*body
= g_string_new(NULL
);
858 const gchar
*marker
= sipe_utils_ip_sdp_address_marker(msg
->ip
);
860 g_string_append_printf(
863 "o=- 0 0 IN %s %s\r\n"
872 for (i
= msg
->media
; i
; i
= i
->next
) {
873 gchar
*media_str
= media_to_string(msg
, i
->data
);
874 g_string_append(body
, media_str
);
878 return g_string_free(body
, FALSE
);
881 static struct sdpcandidate
*
882 sdpcandidate_copy(struct sdpcandidate
*candidate
)
885 struct sdpcandidate
*copy
= g_new0(struct sdpcandidate
, 1);
887 copy
->foundation
= g_strdup(candidate
->foundation
);
888 copy
->component
= candidate
->component
;
889 copy
->type
= candidate
->type
;
890 copy
->protocol
= candidate
->protocol
;
891 copy
->priority
= candidate
->priority
;
892 copy
->ip
= g_strdup(candidate
->ip
);
893 copy
->port
= candidate
->port
;
894 copy
->base_ip
= g_strdup(candidate
->base_ip
);
895 copy
->base_port
= candidate
->base_port
;
896 copy
->username
= g_strdup(candidate
->username
);
897 copy
->password
= g_strdup(candidate
->password
);
905 sdpcandidate_free(struct sdpcandidate
*candidate
)
908 g_free(candidate
->foundation
);
909 g_free(candidate
->ip
);
910 g_free(candidate
->base_ip
);
911 g_free(candidate
->username
);
912 g_free(candidate
->password
);
918 sdpcodec_free(struct sdpcodec
*codec
)
922 sipe_utils_nameval_free(codec
->parameters
);
928 sdpmedia_free(struct sdpmedia
*media
)
934 sipe_utils_nameval_free(media
->attributes
);
936 sipe_utils_slist_free_full(media
->candidates
,
937 (GDestroyNotify
) sdpcandidate_free
);
938 sipe_utils_slist_free_full(media
->codecs
,
939 (GDestroyNotify
) sdpcodec_free
);
940 sipe_utils_slist_free_full(media
->remote_candidates
,
941 (GDestroyNotify
) sdpcandidate_free
);
943 g_free(media
->encryption_key
);
950 sdpmsg_free(struct sdpmsg
*msg
)
954 sipe_utils_slist_free_full(msg
->media
,
955 (GDestroyNotify
) sdpmedia_free
);