6 * Copyright (C) 2013-2015 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
, 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 smsg
->ip
= g_strdup(parts
[5]);
62 } else if (g_str_has_prefix(*ptr
, "m=")) {
63 gchar
**parts
= g_strsplit(*ptr
+ 2, " ", 3);
64 struct sdpmedia
*media
= g_new0(struct sdpmedia
, 1);
66 smsg
->media
= g_slist_append(smsg
->media
, media
);
68 media
->name
= g_strdup(parts
[0]);
69 media
->port
= atoi(parts
[1]);
70 media
->encryption_active
=
71 g_strstr_len(parts
[2], -1, "/SAVP") != NULL
;
75 while (*(++ptr
) && !g_str_has_prefix(*ptr
, "m=")) {
77 if (g_str_has_prefix(*ptr
, "a=")) {
78 if (!append_attribute(media
, *ptr
)) {
95 static struct sdpcandidate
* sdpcandidate_copy(struct sdpcandidate
*candidate
);
96 static void sdpcandidate_free(struct sdpcandidate
*candidate
);
98 static SipeComponentType
99 parse_component(const gchar
*str
)
102 case 1: return SIPE_COMPONENT_RTP
;
103 case 2: return SIPE_COMPONENT_RTCP
;
104 default: return SIPE_COMPONENT_NONE
;
109 base64_pad(const gchar
* str
)
111 size_t str_len
= strlen(str
);
112 int mod
= str_len
% 4;
115 gchar
*result
= NULL
;
117 gchar
*ptr
= result
= g_malloc(str_len
+ pad
+ 1);
119 memcpy(ptr
, str
, str_len
);
121 memset(ptr
, '=', pad
);
127 return g_strdup(str
);
131 parse_append_candidate_draft_6(gchar
**tokens
, GSList
*candidates
)
133 struct sdpcandidate
*candidate
= g_new0(struct sdpcandidate
, 1);
135 candidate
->username
= base64_pad(tokens
[0]);
136 candidate
->component
= parse_component(tokens
[1]);
137 candidate
->password
= base64_pad(tokens
[2]);
139 if (sipe_strequal(tokens
[3], "UDP"))
140 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
141 else if (sipe_strequal(tokens
[3], "TCP"))
142 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
;
144 sdpcandidate_free(candidate
);
148 candidate
->priority
= atoi(tokens
[4] + 2);
149 candidate
->ip
= g_strdup(tokens
[5]);
150 candidate
->port
= atoi(tokens
[6]);
152 candidates
= g_slist_append(candidates
, candidate
);
154 // draft 6 candidates are both active and passive
155 if (candidate
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
) {
156 candidate
= sdpcandidate_copy(candidate
);
157 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
158 candidates
= g_slist_append(candidates
, candidate
);
165 parse_append_candidate_rfc_5245(gchar
**tokens
, GSList
*candidates
)
167 struct sdpcandidate
*candidate
= g_new0(struct sdpcandidate
, 1);
169 candidate
->foundation
= g_strdup(tokens
[0]);
170 candidate
->component
= parse_component(tokens
[1]);
172 if (sipe_strcase_equal(tokens
[2], "UDP"))
173 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
174 else if (sipe_strcase_equal(tokens
[2], "TCP-ACT"))
175 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
;
176 else if (sipe_strcase_equal(tokens
[2], "TCP-PASS"))
177 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
179 sdpcandidate_free(candidate
);
183 candidate
->priority
= atoi(tokens
[3]);
184 candidate
->ip
= g_strdup(tokens
[4]);
185 candidate
->port
= atoi(tokens
[5]);
187 if (sipe_strcase_equal(tokens
[7], "host"))
188 candidate
->type
= SIPE_CANDIDATE_TYPE_HOST
;
189 else if (sipe_strcase_equal(tokens
[7], "relay"))
190 candidate
->type
= SIPE_CANDIDATE_TYPE_RELAY
;
191 else if (sipe_strcase_equal(tokens
[7], "srflx"))
192 candidate
->type
= SIPE_CANDIDATE_TYPE_SRFLX
;
193 else if (sipe_strcase_equal(tokens
[7], "prflx"))
194 candidate
->type
= SIPE_CANDIDATE_TYPE_PRFLX
;
196 sdpcandidate_free(candidate
);
200 candidates
= g_slist_append(candidates
, candidate
);
202 // TCP-ACT candidates are both active and passive
203 if (candidate
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
) {
204 candidate
= sdpcandidate_copy(candidate
);
205 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
206 candidates
= g_slist_append(candidates
, candidate
);
212 parse_candidates(GSList
*attrs
, SipeIceVersion
*ice_version
)
214 GSList
*candidates
= NULL
;
218 while ((attr
= sipe_utils_nameval_find_instance(attrs
, "candidate", i
++))) {
219 gchar
**tokens
= g_strsplit_set(attr
, " ", 0);
221 if (sipe_strequal(tokens
[6], "typ")) {
222 candidates
= parse_append_candidate_rfc_5245(tokens
, candidates
);
224 *ice_version
= SIPE_ICE_RFC_5245
;
226 candidates
= parse_append_candidate_draft_6(tokens
, candidates
);
228 *ice_version
= SIPE_ICE_DRAFT_6
;
235 *ice_version
= SIPE_ICE_NO_ICE
;
237 if (*ice_version
== SIPE_ICE_RFC_5245
) {
238 const gchar
*username
= sipe_utils_nameval_find(attrs
, "ice-ufrag");
239 const gchar
*password
= sipe_utils_nameval_find(attrs
, "ice-pwd");
241 if (username
&& password
) {
243 for (i
= candidates
; i
; i
= i
->next
) {
244 struct sdpcandidate
*c
= i
->data
;
245 c
->username
= g_strdup(username
);
246 c
->password
= g_strdup(password
);
255 create_legacy_candidates(gchar
*ip
, guint16 port
)
257 struct sdpcandidate
*candidate
;
258 GSList
*candidates
= NULL
;
260 candidate
= g_new0(struct sdpcandidate
, 1);
261 candidate
->foundation
= g_strdup("1");
262 candidate
->component
= SIPE_COMPONENT_RTP
;
263 candidate
->type
= SIPE_CANDIDATE_TYPE_HOST
;
264 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
265 candidate
->ip
= g_strdup(ip
);
266 candidate
->port
= port
;
268 candidates
= g_slist_append(candidates
, candidate
);
270 candidate
= g_new0(struct sdpcandidate
, 1);
271 candidate
->foundation
= g_strdup("1");
272 candidate
->component
= SIPE_COMPONENT_RTCP
;
273 candidate
->type
= SIPE_CANDIDATE_TYPE_HOST
;
274 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
275 candidate
->ip
= g_strdup(ip
);
276 candidate
->port
= port
+ 1;
278 candidates
= g_slist_append(candidates
, candidate
);
284 parse_codecs(GSList
*attrs
, SipeMediaType type
)
288 GSList
*codecs
= NULL
;
290 while ((attr
= sipe_utils_nameval_find_instance(attrs
, "rtpmap", i
++))) {
291 struct sdpcodec
*codec
= g_new0(struct sdpcodec
, 1);
292 gchar
**tokens
= g_strsplit_set(attr
, " /", 3);
297 codec
->id
= atoi(tokens
[0]);
298 codec
->name
= g_strdup(tokens
[1]);
299 codec
->clock_rate
= atoi(tokens
[2]);
302 // TODO: more secure and effective implementation
303 while((params
= sipe_utils_nameval_find_instance(attrs
, "fmtp", j
++))) {
304 gchar
**tokens
= g_strsplit_set(params
, " ", 0);
305 gchar
**next
= tokens
+ 1;
307 if (atoi(tokens
[0]) == codec
->id
) {
312 if (sscanf(*next
, "%[a-zA-Z0-9]=%s", name
, value
) == 2)
313 codec
->parameters
= sipe_utils_nameval_add(codec
->parameters
, name
, value
);
322 codecs
= g_slist_append(codecs
, codec
);
330 parse_encryption_key(GSList
*attrs
, guchar
**key
, int *key_id
)
335 while ((attr
= sipe_utils_nameval_find_instance(attrs
, "crypto", i
++))) {
336 gchar
**tokens
= g_strsplit_set(attr
, " :|", 6);
338 if (tokens
[0] && tokens
[1] && tokens
[2] && tokens
[3] && tokens
[4] &&
339 sipe_strcase_equal(tokens
[1], "AES_CM_128_HMAC_SHA1_80") &&
340 sipe_strequal(tokens
[2], "inline") &&
343 *key
= g_base64_decode(tokens
[3], &key_len
);
344 if (key_len
!= SIPE_SRTP_KEY_LEN
) {
348 *key_id
= atoi(tokens
[0]);
360 sdpmsg_parse_msg(gchar
*msg
)
362 struct sdpmsg
*smsg
= g_new0(struct sdpmsg
, 1);
365 if (!parse_attributes(smsg
, msg
)) {
370 for (i
= smsg
->media
; i
; i
= i
->next
) {
371 struct sdpmedia
*media
= i
->data
;
374 media
->candidates
= parse_candidates(media
->attributes
,
377 if (!media
->candidates
&& media
->port
!= 0) {
378 // No a=candidate in SDP message, this seems to be MSOC 2005
379 media
->candidates
= create_legacy_candidates(smsg
->ip
, media
->port
);
382 if (sipe_strequal(media
->name
, "audio"))
383 type
= SIPE_MEDIA_AUDIO
;
384 else if (sipe_strequal(media
->name
, "video"))
385 type
= SIPE_MEDIA_VIDEO
;
387 // Unknown media type
392 media
->codecs
= parse_codecs(media
->attributes
, type
);
393 parse_encryption_key(media
->attributes
, &media
->encryption_key
,
394 &media
->encryption_key_id
);
401 codecs_to_string(GSList
*codecs
)
403 GString
*result
= g_string_new(NULL
);
405 for (; codecs
; codecs
= codecs
->next
) {
406 struct sdpcodec
*c
= codecs
->data
;
407 GSList
*params
= c
->parameters
;
409 g_string_append_printf(result
,
410 "a=rtpmap:%d %s/%d\r\n",
416 GString
*param_str
= g_string_new(NULL
);
417 int written_params
= 0;
419 g_string_append_printf(param_str
, "a=fmtp:%d", c
->id
);
421 for (; params
; params
= params
->next
) {
422 struct sipnameval
* par
= params
->data
;
423 if (sipe_strequal(par
->name
, "farsight-send-profile")) {
424 // Lync AVMCU doesn't like this property.
428 g_string_append_printf(param_str
, " %s=%s",
429 par
->name
, par
->value
);
433 g_string_append(param_str
, "\r\n");
435 if (written_params
> 0) {
436 g_string_append(result
, param_str
->str
);
439 g_string_free(param_str
, TRUE
);
443 return g_string_free(result
, FALSE
);
447 codec_ids_to_string(GSList
*codecs
)
449 GString
*result
= g_string_new(NULL
);
451 for (; codecs
; codecs
= codecs
->next
) {
452 struct sdpcodec
*c
= codecs
->data
;
453 g_string_append_printf(result
, " %d", c
->id
);
456 return g_string_free(result
, FALSE
);
460 base64_unpad(const gchar
*str
)
462 gchar
*result
= g_strdup(str
);
465 for (ptr
= result
+ strlen(result
); ptr
!= result
; --ptr
) {
466 if (*(ptr
- 1) != '=') {
476 candidates_to_string(GSList
*candidates
, SipeIceVersion ice_version
)
478 GString
*result
= g_string_new("");
480 GSList
*processed_tcp_candidates
= NULL
;
482 for (i
= candidates
; i
; i
= i
->next
) {
483 struct sdpcandidate
*c
= i
->data
;
484 const gchar
*protocol
;
486 gchar
*related
= NULL
;
488 if (ice_version
== SIPE_ICE_RFC_5245
) {
490 switch (c
->protocol
) {
491 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
:
492 protocol
= "TCP-ACT";
494 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
:
495 protocol
= "TCP-PASS";
497 case SIPE_NETWORK_PROTOCOL_UDP
:
501 /* error unknown/unsupported type */
502 protocol
= "UNKNOWN";
507 case SIPE_CANDIDATE_TYPE_HOST
:
510 case SIPE_CANDIDATE_TYPE_RELAY
:
513 case SIPE_CANDIDATE_TYPE_SRFLX
:
516 case SIPE_CANDIDATE_TYPE_PRFLX
:
520 /* error unknown/unsupported type */
526 case SIPE_CANDIDATE_TYPE_RELAY
:
527 case SIPE_CANDIDATE_TYPE_SRFLX
:
528 case SIPE_CANDIDATE_TYPE_PRFLX
:
529 related
= g_strdup_printf("raddr %s rport %d",
537 g_string_append_printf(result
,
538 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
546 related
? related
: "");
549 } else if (ice_version
== SIPE_ICE_DRAFT_6
) {
553 switch (c
->protocol
) {
554 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
:
555 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
: {
556 GSList
*prev_cand
= processed_tcp_candidates
;
557 for (; prev_cand
; prev_cand
= prev_cand
->next
) {
558 struct sdpcandidate
*c2
= (struct sdpcandidate
*)prev_cand
->data
;
560 if (sipe_strequal(c
->ip
, c2
->ip
) &&
561 c
->component
== c2
->component
) {
570 processed_tcp_candidates
=
571 g_slist_append(processed_tcp_candidates
, c
);
575 case SIPE_NETWORK_PROTOCOL_UDP
:
579 /* unknown/unsupported type, ignore */
588 username
= base64_unpad(c
->username
);
589 password
= base64_unpad(c
->password
);
591 g_string_append_printf(result
,
592 "a=candidate:%s %u %s %s 0.%u %s %d\r\n",
606 g_slist_free(processed_tcp_candidates
);
608 return g_string_free(result
, FALSE
);
612 remote_candidates_to_string(GSList
*candidates
, SipeIceVersion ice_version
)
614 GString
*result
= g_string_new("");
617 if (ice_version
== SIPE_ICE_RFC_5245
) {
619 g_string_append(result
, "a=remote-candidates:");
621 for (i
= candidates
; i
; i
= i
->next
) {
622 struct sdpcandidate
*c
= i
->data
;
623 g_string_append_printf(result
, "%u %s %u ",
624 c
->component
, c
->ip
, c
->port
);
627 g_string_append(result
, "\r\n");
628 } else if (ice_version
== SIPE_ICE_DRAFT_6
) {
629 struct sdpcandidate
*c
= candidates
->data
;
630 g_string_append_printf(result
, "a=remote-candidate:%s\r\n",
635 return g_string_free(result
, FALSE
);
639 attributes_to_string(GSList
*attributes
)
641 GString
*result
= g_string_new("");
643 for (; attributes
; attributes
= attributes
->next
) {
644 struct sipnameval
*a
= attributes
->data
;
645 g_string_append_printf(result
, "a=%s", a
->name
);
646 if (!sipe_strequal(a
->value
, ""))
647 g_string_append_printf(result
, ":%s", a
->value
);
648 g_string_append(result
, "\r\n");
651 return g_string_free(result
, FALSE
);
655 media_to_string(const struct sdpmsg
*msg
, const struct sdpmedia
*media
)
659 gchar
*transport_profile
= NULL
;
661 gchar
*media_conninfo
= NULL
;
663 gchar
*codecs_str
= NULL
;
664 gchar
*codec_ids_str
= codec_ids_to_string(media
->codecs
);
666 gchar
*candidates_str
= NULL
;
667 gchar
*remote_candidates_str
= NULL
;
669 gchar
*attributes_str
= NULL
;
670 gchar
*credentials
= NULL
;
672 gchar
*crypto
= NULL
;
674 gboolean uses_tcp_transport
= TRUE
;
676 if (media
->port
!= 0) {
677 if (!sipe_strequal(msg
->ip
, media
->ip
)) {
678 media_conninfo
= g_strdup_printf("c=IN IP4 %s\r\n", media
->ip
);
681 codecs_str
= codecs_to_string(media
->codecs
);
682 candidates_str
= candidates_to_string(media
->candidates
, msg
->ice_version
);
683 remote_candidates_str
= remote_candidates_to_string(media
->remote_candidates
,
686 if (media
->remote_candidates
) {
687 struct sdpcandidate
*c
= media
->remote_candidates
->data
;
689 c
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
||
690 c
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
||
691 c
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_SO
;
693 GSList
*candidates
= media
->candidates
;
694 for (; candidates
; candidates
= candidates
->next
) {
695 struct sdpcandidate
*c
= candidates
->data
;
696 if (c
->protocol
== SIPE_NETWORK_PROTOCOL_UDP
) {
697 uses_tcp_transport
= FALSE
;
703 attributes_str
= attributes_to_string(media
->attributes
);
705 if (msg
->ice_version
== SIPE_ICE_RFC_5245
&& media
->candidates
) {
706 struct sdpcandidate
*c
= media
->candidates
->data
;
708 credentials
= g_strdup_printf("a=ice-ufrag:%s\r\n"
714 if (media
->encryption_key
) {
715 gchar
*key_encoded
= g_base64_encode(media
->encryption_key
, SIPE_SRTP_KEY_LEN
);
716 crypto
= g_strdup_printf("a=crypto:%d AES_CM_128_HMAC_SHA1_80 inline:%s|2^31\r\n",
717 media
->encryption_key_id
, key_encoded
);
722 transport_profile
= g_strdup_printf("%sRTP/%sAVP",
723 uses_tcp_transport
? "TCP/" : "",
724 media
->encryption_active
? "S" : "");
726 media_str
= g_strdup_printf("m=%s %d %s%s\r\n"
734 media
->name
, media
->port
, transport_profile
, codec_ids_str
,
735 media_conninfo
? media_conninfo
: "",
736 candidates_str
? candidates_str
: "",
737 crypto
? crypto
: "",
738 remote_candidates_str
? remote_candidates_str
: "",
739 codecs_str
? codecs_str
: "",
740 attributes_str
? attributes_str
: "",
741 credentials
? credentials
: "");
743 g_free(transport_profile
);
744 g_free(media_conninfo
);
746 g_free(codec_ids_str
);
747 g_free(candidates_str
);
748 g_free(remote_candidates_str
);
749 g_free(attributes_str
);
757 sdpmsg_to_string(const struct sdpmsg
*msg
)
759 GString
*body
= g_string_new(NULL
);
762 g_string_append_printf(
765 "o=- 0 0 IN IP4 %s\r\n"
773 for (i
= msg
->media
; i
; i
= i
->next
) {
774 gchar
*media_str
= media_to_string(msg
, i
->data
);
775 g_string_append(body
, media_str
);
779 return g_string_free(body
, FALSE
);
782 static struct sdpcandidate
*
783 sdpcandidate_copy(struct sdpcandidate
*candidate
)
786 struct sdpcandidate
*copy
= g_new0(struct sdpcandidate
, 1);
788 copy
->foundation
= g_strdup(candidate
->foundation
);
789 copy
->component
= candidate
->component
;
790 copy
->type
= candidate
->type
;
791 copy
->protocol
= candidate
->protocol
;
792 copy
->priority
= candidate
->priority
;
793 copy
->ip
= g_strdup(candidate
->ip
);
794 copy
->port
= candidate
->port
;
795 copy
->base_ip
= g_strdup(candidate
->base_ip
);
796 copy
->base_port
= candidate
->base_port
;
797 copy
->username
= g_strdup(candidate
->username
);
798 copy
->password
= g_strdup(candidate
->password
);
806 sdpcandidate_free(struct sdpcandidate
*candidate
)
809 g_free(candidate
->foundation
);
810 g_free(candidate
->ip
);
811 g_free(candidate
->base_ip
);
812 g_free(candidate
->username
);
813 g_free(candidate
->password
);
819 sdpcodec_free(struct sdpcodec
*codec
)
823 sipe_utils_nameval_free(codec
->parameters
);
829 sdpmedia_free(struct sdpmedia
*media
)
835 sipe_utils_nameval_free(media
->attributes
);
837 sipe_utils_slist_free_full(media
->candidates
,
838 (GDestroyNotify
) sdpcandidate_free
);
839 sipe_utils_slist_free_full(media
->codecs
,
840 (GDestroyNotify
) sdpcodec_free
);
841 sipe_utils_slist_free_full(media
->remote_candidates
,
842 (GDestroyNotify
) sdpcandidate_free
);
844 g_free(media
->encryption_key
);
851 sdpmsg_free(struct sdpmsg
*msg
)
855 sipe_utils_slist_free_full(msg
->media
,
856 (GDestroyNotify
) sdpmedia_free
);