6 * Copyright (C) 2013 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"
32 #include "sipe-utils.h"
35 append_attribute(struct sdpmedia
*media
, gchar
*attr
)
37 gchar
**parts
= g_strsplit(attr
+ 2, ":", 2);
44 media
->attributes
= sipe_utils_nameval_add(media
->attributes
,
46 parts
[1] ? parts
[1] : "");
52 parse_attributes(struct sdpmsg
*smsg
, gchar
*msg
) {
53 gchar
**lines
= g_strsplit(msg
, "\r\n", 0);
56 while (*ptr
!= NULL
) {
57 if (g_str_has_prefix(*ptr
, "o=")) {
58 gchar
**parts
= g_strsplit(*ptr
+ 2, " ", 6);
59 smsg
->ip
= g_strdup(parts
[5]);
61 } else if (g_str_has_prefix(*ptr
, "m=")) {
62 gchar
**parts
= g_strsplit(*ptr
+ 2, " ", 3);
63 struct sdpmedia
*media
= g_new0(struct sdpmedia
, 1);
65 smsg
->media
= g_slist_append(smsg
->media
, media
);
67 media
->name
= g_strdup(parts
[0]);
68 media
->port
= atoi(parts
[1]);
72 while (*(++ptr
) && !g_str_has_prefix(*ptr
, "m=")) {
74 if (g_str_has_prefix(*ptr
, "a=")) {
75 if (!append_attribute(media
, *ptr
)) {
92 static struct sdpcandidate
* sdpcandidate_copy(struct sdpcandidate
*candidate
);
93 static void sdpcandidate_free(struct sdpcandidate
*candidate
);
95 static SipeComponentType
96 parse_component(const gchar
*str
)
99 case 1: return SIPE_COMPONENT_RTP
;
100 case 2: return SIPE_COMPONENT_RTCP
;
101 default: return SIPE_COMPONENT_NONE
;
106 base64_pad(const gchar
* str
)
108 size_t str_len
= strlen(str
);
109 int mod
= str_len
% 4;
112 gchar
*result
= NULL
;
114 gchar
*ptr
= result
= g_malloc(str_len
+ pad
+ 1);
116 memcpy(ptr
, str
, str_len
);
118 memset(ptr
, '=', pad
);
124 return g_strdup(str
);
128 parse_append_candidate_draft_6(gchar
**tokens
, GSList
*candidates
)
130 struct sdpcandidate
*candidate
= g_new0(struct sdpcandidate
, 1);
132 candidate
->username
= base64_pad(tokens
[0]);
133 candidate
->component
= parse_component(tokens
[1]);
134 candidate
->password
= base64_pad(tokens
[2]);
136 if (sipe_strequal(tokens
[3], "UDP"))
137 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
138 else if (sipe_strequal(tokens
[3], "TCP"))
139 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
;
141 sdpcandidate_free(candidate
);
145 candidate
->priority
= atoi(tokens
[4] + 2);
146 candidate
->ip
= g_strdup(tokens
[5]);
147 candidate
->port
= atoi(tokens
[6]);
149 candidates
= g_slist_append(candidates
, candidate
);
151 // draft 6 candidates are both active and passive
152 if (candidate
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
) {
153 candidate
= sdpcandidate_copy(candidate
);
154 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
155 candidates
= g_slist_append(candidates
, candidate
);
162 parse_append_candidate_rfc_5245(gchar
**tokens
, GSList
*candidates
)
164 struct sdpcandidate
*candidate
= g_new0(struct sdpcandidate
, 1);
166 candidate
->foundation
= g_strdup(tokens
[0]);
167 candidate
->component
= parse_component(tokens
[1]);
169 if (sipe_strequal(tokens
[2], "UDP"))
170 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
171 else if (sipe_strequal(tokens
[2], "TCP-ACT"))
172 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
;
173 else if (sipe_strequal(tokens
[2], "TCP-PASS"))
174 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
176 sdpcandidate_free(candidate
);
180 candidate
->priority
= atoi(tokens
[3]);
181 candidate
->ip
= g_strdup(tokens
[4]);
182 candidate
->port
= atoi(tokens
[5]);
184 if (sipe_strequal(tokens
[7], "host"))
185 candidate
->type
= SIPE_CANDIDATE_TYPE_HOST
;
186 else if (sipe_strequal(tokens
[7], "relay"))
187 candidate
->type
= SIPE_CANDIDATE_TYPE_RELAY
;
188 else if (sipe_strequal(tokens
[7], "srflx"))
189 candidate
->type
= SIPE_CANDIDATE_TYPE_SRFLX
;
190 else if (sipe_strequal(tokens
[7], "prflx"))
191 candidate
->type
= SIPE_CANDIDATE_TYPE_PRFLX
;
193 sdpcandidate_free(candidate
);
197 return g_slist_append(candidates
, candidate
);
201 parse_candidates(GSList
*attrs
, SipeIceVersion
*ice_version
)
203 GSList
*candidates
= NULL
;
207 while ((attr
= sipe_utils_nameval_find_instance(attrs
, "candidate", i
++))) {
208 gchar
**tokens
= g_strsplit_set(attr
, " ", 0);
210 if (sipe_strequal(tokens
[6], "typ")) {
211 candidates
= parse_append_candidate_rfc_5245(tokens
, candidates
);
213 *ice_version
= SIPE_ICE_RFC_5245
;
215 candidates
= parse_append_candidate_draft_6(tokens
, candidates
);
217 *ice_version
= SIPE_ICE_DRAFT_6
;
224 *ice_version
= SIPE_ICE_NO_ICE
;
226 if (*ice_version
== SIPE_ICE_RFC_5245
) {
227 const gchar
*username
= sipe_utils_nameval_find(attrs
, "ice-ufrag");
228 const gchar
*password
= sipe_utils_nameval_find(attrs
, "ice-pwd");
230 if (username
&& password
) {
232 for (i
= candidates
; i
; i
= i
->next
) {
233 struct sdpcandidate
*c
= i
->data
;
234 c
->username
= g_strdup(username
);
235 c
->password
= g_strdup(password
);
244 create_legacy_candidates(gchar
*ip
, guint16 port
)
246 struct sdpcandidate
*candidate
;
247 GSList
*candidates
= NULL
;
249 candidate
= g_new0(struct sdpcandidate
, 1);
250 candidate
->foundation
= g_strdup("1");
251 candidate
->component
= SIPE_COMPONENT_RTP
;
252 candidate
->type
= SIPE_CANDIDATE_TYPE_HOST
;
253 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
254 candidate
->ip
= g_strdup(ip
);
255 candidate
->port
= port
;
257 candidates
= g_slist_append(candidates
, candidate
);
259 candidate
= g_new0(struct sdpcandidate
, 1);
260 candidate
->foundation
= g_strdup("1");
261 candidate
->component
= SIPE_COMPONENT_RTCP
;
262 candidate
->type
= SIPE_CANDIDATE_TYPE_HOST
;
263 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
264 candidate
->ip
= g_strdup(ip
);
265 candidate
->port
= port
+ 1;
267 candidates
= g_slist_append(candidates
, candidate
);
273 parse_codecs(GSList
*attrs
, SipeMediaType type
)
277 GSList
*codecs
= NULL
;
279 while ((attr
= sipe_utils_nameval_find_instance(attrs
, "rtpmap", i
++))) {
280 struct sdpcodec
*codec
= g_new0(struct sdpcodec
, 1);
281 gchar
**tokens
= g_strsplit_set(attr
, " /", 3);
286 codec
->id
= atoi(tokens
[0]);
287 codec
->name
= g_strdup(tokens
[1]);
288 codec
->clock_rate
= atoi(tokens
[2]);
291 // TODO: more secure and effective implementation
292 while((params
= sipe_utils_nameval_find_instance(attrs
, "fmtp", j
++))) {
293 gchar
**tokens
= g_strsplit_set(params
, " ", 0);
294 gchar
**next
= tokens
+ 1;
296 if (atoi(tokens
[0]) == codec
->id
) {
301 if (sscanf(*next
, "%[a-zA-Z0-9]=%s", name
, value
) == 2)
302 codec
->parameters
= sipe_utils_nameval_add(codec
->parameters
, name
, value
);
311 codecs
= g_slist_append(codecs
, codec
);
319 sdpmsg_parse_msg(gchar
*msg
)
321 struct sdpmsg
*smsg
= g_new0(struct sdpmsg
, 1);
324 if (!parse_attributes(smsg
, msg
)) {
329 for (i
= smsg
->media
; i
; i
= i
->next
) {
330 struct sdpmedia
*media
= i
->data
;
333 media
->candidates
= parse_candidates(media
->attributes
,
336 if (!media
->candidates
&& media
->port
!= 0) {
337 // No a=candidate in SDP message, this seems to be MSOC 2005
338 media
->candidates
= create_legacy_candidates(smsg
->ip
, media
->port
);
341 if (sipe_strequal(media
->name
, "audio"))
342 type
= SIPE_MEDIA_AUDIO
;
343 else if (sipe_strequal(media
->name
, "video"))
344 type
= SIPE_MEDIA_VIDEO
;
346 // Unknown media type
351 media
->codecs
= parse_codecs(media
->attributes
, type
);
358 codecs_to_string(GSList
*codecs
)
360 GString
*result
= g_string_new(NULL
);
362 for (; codecs
; codecs
= codecs
->next
) {
363 struct sdpcodec
*c
= codecs
->data
;
364 GSList
*params
= c
->parameters
;
366 g_string_append_printf(result
,
367 "a=rtpmap:%d %s/%d\r\n",
373 g_string_append_printf(result
, "a=fmtp:%d", c
->id
);
375 for (; params
; params
= params
->next
) {
376 struct sipnameval
* par
= params
->data
;
377 g_string_append_printf(result
, " %s=%s",
378 par
->name
, par
->value
);
381 g_string_append(result
, "\r\n");
385 return g_string_free(result
, FALSE
);
389 codec_ids_to_string(GSList
*codecs
)
391 GString
*result
= g_string_new(NULL
);
393 for (; codecs
; codecs
= codecs
->next
) {
394 struct sdpcodec
*c
= codecs
->data
;
395 g_string_append_printf(result
, " %d", c
->id
);
398 return g_string_free(result
, FALSE
);
402 base64_unpad(const gchar
*str
)
404 gchar
*result
= g_strdup(str
);
407 for (ptr
= result
+ strlen(result
); ptr
!= result
; --ptr
) {
408 if (*(ptr
- 1) != '=') {
418 candidate_sort_cb(struct sdpcandidate
*c1
, struct sdpcandidate
*c2
)
420 int cmp
= sipe_strcompare(c1
->foundation
, c2
->foundation
);
422 cmp
= sipe_strcompare(c1
->username
, c2
->username
);
424 cmp
= c1
->component
- c2
->component
;
431 candidates_to_string(GSList
*candidates
, SipeIceVersion ice_version
)
433 GString
*result
= g_string_new("");
435 GSList
*processed_tcp_candidates
= NULL
;
437 candidates
= g_slist_copy(candidates
);
438 candidates
= g_slist_sort(candidates
, (GCompareFunc
)candidate_sort_cb
);
440 for (i
= candidates
; i
; i
= i
->next
) {
441 struct sdpcandidate
*c
= i
->data
;
442 const gchar
*protocol
;
444 gchar
*related
= NULL
;
446 if (ice_version
== SIPE_ICE_RFC_5245
) {
448 switch (c
->protocol
) {
449 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
:
450 protocol
= "TCP-ACT";
452 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
:
453 protocol
= "TCP-PASS";
455 case SIPE_NETWORK_PROTOCOL_UDP
:
459 /* error unknown/unsupported type */
460 protocol
= "UNKNOWN";
465 case SIPE_CANDIDATE_TYPE_HOST
:
468 case SIPE_CANDIDATE_TYPE_RELAY
:
470 related
= g_strdup_printf("raddr %s rport %d ",
474 case SIPE_CANDIDATE_TYPE_SRFLX
:
476 related
= g_strdup_printf("raddr %s rport %d",
480 case SIPE_CANDIDATE_TYPE_PRFLX
:
484 /* error unknown/unsupported type */
489 g_string_append_printf(result
,
490 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
498 related
? related
: "");
501 } else if (ice_version
== SIPE_ICE_DRAFT_6
) {
505 switch (c
->protocol
) {
506 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
:
507 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
: {
508 GSList
*prev_cand
= processed_tcp_candidates
;
509 for (; prev_cand
; prev_cand
= prev_cand
->next
) {
510 struct sdpcandidate
*c2
= (struct sdpcandidate
*)prev_cand
->data
;
512 if (sipe_strequal(c
->ip
, c2
->ip
) &&
513 c
->component
== c2
->component
) {
522 processed_tcp_candidates
=
523 g_slist_append(processed_tcp_candidates
, c
);
527 case SIPE_NETWORK_PROTOCOL_UDP
:
531 /* unknown/unsupported type, ignore */
540 username
= base64_unpad(c
->username
);
541 password
= base64_unpad(c
->password
);
543 g_string_append_printf(result
,
544 "a=candidate:%s %u %s %s 0.%u %s %d\r\n",
558 g_slist_free(candidates
);
559 g_slist_free(processed_tcp_candidates
);
561 return g_string_free(result
, FALSE
);
565 remote_candidates_to_string(GSList
*candidates
, SipeIceVersion ice_version
)
567 GString
*result
= g_string_new("");
569 candidates
= g_slist_copy(candidates
);
570 candidates
= g_slist_sort(candidates
, (GCompareFunc
)candidate_sort_cb
);
573 if (ice_version
== SIPE_ICE_RFC_5245
) {
575 g_string_append(result
, "a=remote-candidates:");
577 for (i
= candidates
; i
; i
= i
->next
) {
578 struct sdpcandidate
*c
= i
->data
;
579 g_string_append_printf(result
, "%u %s %u ",
580 c
->component
, c
->ip
, c
->port
);
583 g_string_append(result
, "\r\n");
584 } else if (ice_version
== SIPE_ICE_DRAFT_6
) {
585 struct sdpcandidate
*c
= candidates
->data
;
586 g_string_append_printf(result
, "a=remote-candidate:%s\r\n",
591 g_slist_free(candidates
);
593 return g_string_free(result
, FALSE
);
597 attributes_to_string(GSList
*attributes
)
599 GString
*result
= g_string_new("");
601 for (; attributes
; attributes
= attributes
->next
) {
602 struct sipnameval
*a
= attributes
->data
;
603 g_string_append_printf(result
, "a=%s", a
->name
);
604 if (!sipe_strequal(a
->value
, ""))
605 g_string_append_printf(result
, ":%s", a
->value
);
606 g_string_append(result
, "\r\n");
609 return g_string_free(result
, FALSE
);
613 media_to_string(const struct sdpmsg
*msg
, const struct sdpmedia
*media
)
617 gchar
*media_conninfo
= NULL
;
619 gchar
*codecs_str
= NULL
;
620 gchar
*codec_ids_str
= codec_ids_to_string(media
->codecs
);
622 gchar
*candidates_str
= NULL
;
623 gchar
*remote_candidates_str
= NULL
;
625 gchar
*tcp_setup_str
= NULL
;
626 gchar
*attributes_str
= NULL
;
627 gchar
*credentials
= NULL
;
629 gboolean uses_tcp_transport
= FALSE
;
631 if (media
->port
!= 0) {
632 if (!sipe_strequal(msg
->ip
, media
->ip
)) {
633 media_conninfo
= g_strdup_printf("c=IN IP4 %s\r\n", media
->ip
);
636 codecs_str
= codecs_to_string(media
->codecs
);
637 candidates_str
= candidates_to_string(media
->candidates
, msg
->ice_version
);
638 remote_candidates_str
= remote_candidates_to_string(media
->remote_candidates
,
641 if (media
->remote_candidates
) {
642 struct sdpcandidate
*c
= media
->remote_candidates
->data
;
644 c
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
||
645 c
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
646 if (uses_tcp_transport
) {
647 tcp_setup_str
= g_strdup_printf(
648 "a=connection:existing\r\n"
650 (c
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
) ? "passive" : "active");
654 attributes_str
= attributes_to_string(media
->attributes
);
656 if (msg
->ice_version
== SIPE_ICE_RFC_5245
&& media
->candidates
) {
657 struct sdpcandidate
*c
= media
->candidates
->data
;
659 credentials
= g_strdup_printf("a=ice-ufrag:%s\r\n"
666 media_str
= g_strdup_printf("m=%s %d %sRTP/AVP%s\r\n"
674 media
->name
, media
->port
, uses_tcp_transport
? "TCP/" : "", codec_ids_str
,
675 media_conninfo
? media_conninfo
: "",
676 candidates_str
? candidates_str
: "",
677 remote_candidates_str
? remote_candidates_str
: "",
678 tcp_setup_str
? tcp_setup_str
: "",
679 codecs_str
? codecs_str
: "",
680 attributes_str
? attributes_str
: "",
681 credentials
? credentials
: "");
683 g_free(media_conninfo
);
685 g_free(codec_ids_str
);
686 g_free(candidates_str
);
687 g_free(remote_candidates_str
);
688 g_free(tcp_setup_str
);
689 g_free(attributes_str
);
696 sdpmsg_to_string(const struct sdpmsg
*msg
)
698 GString
*body
= g_string_new(NULL
);
701 g_string_append_printf(
704 "o=- 0 0 IN IP4 %s\r\n"
712 for (i
= msg
->media
; i
; i
= i
->next
) {
713 gchar
*media_str
= media_to_string(msg
, i
->data
);
714 g_string_append(body
, media_str
);
718 return g_string_free(body
, FALSE
);
721 static struct sdpcandidate
*
722 sdpcandidate_copy(struct sdpcandidate
*candidate
)
725 struct sdpcandidate
*copy
= g_new0(struct sdpcandidate
, 1);
727 copy
->foundation
= g_strdup(candidate
->foundation
);
728 copy
->component
= candidate
->component
;
729 copy
->type
= candidate
->type
;
730 copy
->protocol
= candidate
->protocol
;
731 copy
->priority
= candidate
->priority
;
732 copy
->ip
= g_strdup(candidate
->ip
);
733 copy
->port
= candidate
->port
;
734 copy
->base_ip
= g_strdup(candidate
->base_ip
);
735 copy
->base_port
= candidate
->base_port
;
736 copy
->username
= g_strdup(candidate
->username
);
737 copy
->password
= g_strdup(candidate
->password
);
745 sdpcandidate_free(struct sdpcandidate
*candidate
)
748 g_free(candidate
->foundation
);
749 g_free(candidate
->ip
);
750 g_free(candidate
->base_ip
);
751 g_free(candidate
->username
);
752 g_free(candidate
->password
);
758 sdpcodec_free(struct sdpcodec
*codec
)
762 sipe_utils_nameval_free(codec
->parameters
);
768 sdpmedia_free(struct sdpmedia
*media
)
774 sipe_utils_nameval_free(media
->attributes
);
776 sipe_utils_slist_free_full(media
->candidates
,
777 (GDestroyNotify
) sdpcandidate_free
);
778 sipe_utils_slist_free_full(media
->codecs
,
779 (GDestroyNotify
) sdpcodec_free
);
780 sipe_utils_slist_free_full(media
->remote_candidates
,
781 (GDestroyNotify
) sdpcandidate_free
);
788 sdpmsg_free(struct sdpmsg
*msg
)
792 sipe_utils_slist_free_full(msg
->media
,
793 (GDestroyNotify
) sdpmedia_free
);