|
|
|
|
@ -348,15 +348,6 @@ static bool sdp_manipulate_remove(const struct sdp_manipulations * sdp_manipulat
|
|
|
|
|
return false; /* means don't remove */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks whether an attribute removal request exists for a given session level.
|
|
|
|
|
* `attr_name` must be without `a=`.
|
|
|
|
|
*/
|
|
|
|
|
static bool sdp_manipulate_remove_c(const char *attr_name, const sdp_ng_flags *flags, enum media_type media_type) {
|
|
|
|
|
struct sdp_manipulations *sdp_manipulations = sdp_manipulations_get_by_id(flags->sdp_manipulations, media_type);
|
|
|
|
|
return sdp_manipulate_remove(sdp_manipulations, STR_PTR(attr_name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Adds values into a requested session level (global, audio, video)
|
|
|
|
|
*/
|
|
|
|
|
@ -397,31 +388,112 @@ static str *sdp_manipulations_subst(const struct sdp_manipulations * sdp_manipul
|
|
|
|
|
return cmd_subst_value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void append_str_attr_to_gstring(GString *s, const str * name, const str * value,
|
|
|
|
|
const sdp_ng_flags *flags, enum media_type media_type);
|
|
|
|
|
static void append_attr_int_to_gstring(GString *s, const char * value, const int additional,
|
|
|
|
|
|
|
|
|
|
__attribute__((nonnull(1, 2, 3, 4)))
|
|
|
|
|
static void append_str_attr_to_gstring(GString *s, const str *name, const str *value,
|
|
|
|
|
const sdp_ng_flags *flags, enum media_type media_type);
|
|
|
|
|
static void append_tagged_attr_to_gstring(GString *s, const char * name, const str *tag, const str * value,
|
|
|
|
|
__attribute__((nonnull(1, 2, 3)))
|
|
|
|
|
static void append_null_str_attr_to_gstring(GString *s, const str *name,
|
|
|
|
|
const sdp_ng_flags *flags, enum media_type media_type);
|
|
|
|
|
static void append_int_tagged_attr_to_gstring(GString *s, const char * name, unsigned int tag, const str * value,
|
|
|
|
|
__attribute__((nonnull(1, 2, 4, 5)))
|
|
|
|
|
static void append_int_tagged_str_attr_to_gstring(GString *s, const str *name, unsigned int tag, const str *value,
|
|
|
|
|
const sdp_ng_flags *flags, enum media_type media_type);
|
|
|
|
|
|
|
|
|
|
void sdp_append_str_attr(GString *s, const sdp_ng_flags *flags, enum media_type media_type,
|
|
|
|
|
const str *name, const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
#define append_int_tagged_attr_to_gstring(s, name, tag, value, flags, type) \
|
|
|
|
|
append_int_tagged_str_attr_to_gstring(s, STR_PTR(name), tag, value, flags, type)
|
|
|
|
|
#define append_v_attr_to_gstring(s, name, flags, type, fmt, ...) \
|
|
|
|
|
append_v_str_attr_to_gstring(s, STR_PTR(name), flags, type, fmt, ##__VA_ARGS__)
|
|
|
|
|
#define append_attr_int_to_gstring(s, name, value, flags, type) \
|
|
|
|
|
append_v_attr_to_gstring(s, name, flags, type, "%u", value)
|
|
|
|
|
#define append_attr_to_gstring(s, name, value, flags, type) \
|
|
|
|
|
append_str_attr_to_gstring(s, STR_PTR(name), value, flags, type)
|
|
|
|
|
#define append_null_attr_to_gstring(s, name, flags, type) \
|
|
|
|
|
append_null_str_attr_to_gstring(s, STR_PTR(name), flags, type)
|
|
|
|
|
#define append_gen_attr_to_gstring(s, name, value, flags, type) ({ \
|
|
|
|
|
if ((value)->len) \
|
|
|
|
|
append_str_attr_to_gstring(s, name, value, flags, type); \
|
|
|
|
|
else \
|
|
|
|
|
append_null_str_attr_to_gstring(s, name, flags, type); \
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
struct sdp_state {
|
|
|
|
|
GString *s;
|
|
|
|
|
size_t start;
|
|
|
|
|
struct sdp_manipulations *manip;
|
|
|
|
|
const sdp_ng_flags *flags;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Records the current state of the output SDP and writes the attribute lead-in `a=`
|
|
|
|
|
static struct sdp_state __attr_begin(GString *s, const sdp_ng_flags *flags, enum media_type media_type) {
|
|
|
|
|
struct sdp_manipulations *manip = sdp_manipulations_get_by_id(flags->sdp_manipulations, media_type);
|
|
|
|
|
|
|
|
|
|
g_string_append(s, "a=");
|
|
|
|
|
|
|
|
|
|
return (struct sdp_state) {
|
|
|
|
|
.s = s,
|
|
|
|
|
.start = s->len,
|
|
|
|
|
.manip = manip,
|
|
|
|
|
.flags = flags,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __attr_end(const struct sdp_state *state) {
|
|
|
|
|
g_string_append(state->s, "\r\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Checks for attribute removal or substitutions.
|
|
|
|
|
// If removal or substitution was done, returns true
|
|
|
|
|
static bool __attr_manip(const struct sdp_state *state) {
|
|
|
|
|
str attr = STR_LEN(state->s->str + state->start, state->s->len - state->start);
|
|
|
|
|
|
|
|
|
|
/* first check if the originally present attribute is to be removed */
|
|
|
|
|
if (sdp_manipulate_remove(state->manip, &attr)) {
|
|
|
|
|
// remove everything including the `a=`
|
|
|
|
|
g_string_truncate(state->s, state->start - 2);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
str *attr_subst = sdp_manipulations_subst(state->manip, &attr);
|
|
|
|
|
if (attr_subst) {
|
|
|
|
|
// rewind to `a=`, write complete attribute, and call it a day
|
|
|
|
|
g_string_truncate(state->s, state->start);
|
|
|
|
|
g_string_append_len(state->s, attr_subst->s, attr_subst->len);
|
|
|
|
|
__attr_end(state);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// continue...
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Appends attribute fragment (`name` or `name:tag` or `value`) to the output SDP.
|
|
|
|
|
* Includes substitute and remove SDP attribute manipulations.
|
|
|
|
|
* Return true if attribute was substituted or removed.
|
|
|
|
|
*/
|
|
|
|
|
__attribute__((nonnull(1, 2)))
|
|
|
|
|
static bool __attr_append_str(const struct sdp_state *state, const str *s) {
|
|
|
|
|
g_string_append_len(state->s, s->s, s->len);
|
|
|
|
|
return __attr_manip(state);
|
|
|
|
|
}
|
|
|
|
|
#define __attr_append(state, s) __attr_append_str(state, STR_PTR(s))
|
|
|
|
|
__attribute__((nonnull(1, 2)))
|
|
|
|
|
static bool __attr_append_v(const struct sdp_state *state, const char *fmt, va_list ap) {
|
|
|
|
|
g_string_append_vprintf(state->s, fmt, ap);
|
|
|
|
|
return __attr_manip(state);
|
|
|
|
|
}
|
|
|
|
|
__attribute__((format(printf, 2, 3)))
|
|
|
|
|
__attribute__((nonnull(1, 2)))
|
|
|
|
|
static bool __attr_append_f(const struct sdp_state *state, const char *fmt, ...) {
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
g_autoptr(GString) gs = g_string_new("");
|
|
|
|
|
g_string_vprintf(gs, fmt, ap);
|
|
|
|
|
bool ret = __attr_append_v(state, fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
append_str_attr_to_gstring(s, name, &STR_GS(gs), flags, media_type);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
INLINE void append_attr_to_gstring(GString *s, const char * name, const str * value,
|
|
|
|
|
const sdp_ng_flags *flags, enum media_type media_type)
|
|
|
|
|
{
|
|
|
|
|
append_str_attr_to_gstring(s, STR_PTR(name), value, flags, media_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
INLINE struct sdp_attribute *attr_get_by_id(struct sdp_attributes *a, enum attr_id id) {
|
|
|
|
|
return t_hash_table_lookup(a->id_hash, GINT_TO_POINTER(id));
|
|
|
|
|
}
|
|
|
|
|
@ -2120,7 +2192,7 @@ void sdp_insert_media_attributes(GString *gs, struct call_media *media, struct c
|
|
|
|
|
__auto_type s = l->data;
|
|
|
|
|
if (s->other == ATTR_OTHER_EXTMAP && flags->strip_extmap && !MEDIA_ISSET(source_media, PASSTHRU))
|
|
|
|
|
continue;
|
|
|
|
|
append_str_attr_to_gstring(gs, &s->strs.name, &s->strs.value, flags, source_media->type_id);
|
|
|
|
|
append_gen_attr_to_gstring(gs, &s->strs.name, &s->strs.value, flags, source_media->type_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void sdp_insert_monologue_attributes(GString *gs, struct call_monologue *ml, const sdp_ng_flags *flags) {
|
|
|
|
|
@ -2135,7 +2207,7 @@ void sdp_insert_monologue_attributes(GString *gs, struct call_monologue *ml, con
|
|
|
|
|
__auto_type s = l->data;
|
|
|
|
|
if (s->other == ATTR_OTHER_EXTMAP && flags->strip_extmap)
|
|
|
|
|
continue;
|
|
|
|
|
append_str_attr_to_gstring(gs, &s->strs.name, &s->strs.value, flags, MT_UNKNOWN);
|
|
|
|
|
append_gen_attr_to_gstring(gs, &s->strs.name, &s->strs.value, flags, MT_UNKNOWN);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void sdp_insert_all_attributes(GString *s, struct call_media *media, struct sdp_ng_flags *flags) {
|
|
|
|
|
@ -2145,18 +2217,17 @@ void sdp_insert_all_attributes(GString *s, struct call_media *media, struct sdp_
|
|
|
|
|
// so that we can print our own candidates first
|
|
|
|
|
if (a->attr == ATTR_END_OF_CANDIDATES)
|
|
|
|
|
continue;
|
|
|
|
|
append_str_attr_to_gstring(s, &a->strs.name, &a->strs.value, flags, media->type_id);
|
|
|
|
|
append_gen_attr_to_gstring(s, &a->strs.name, &a->strs.value, flags, media->type_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int insert_ice_address(GString *s, stream_fd *sfd, const sdp_ng_flags *flags) {
|
|
|
|
|
static bool insert_ice_address(const struct sdp_state *state, stream_fd *sfd, const sdp_ng_flags *flags) {
|
|
|
|
|
if (!is_addr_unspecified(&flags->media_address))
|
|
|
|
|
sockaddr_print_gstring(s, &flags->media_address);
|
|
|
|
|
sockaddr_print_gstring(state->s, &flags->media_address);
|
|
|
|
|
else
|
|
|
|
|
call_stream_address(s, sfd->stream, SAF_ICE, sfd->local_intf, false);
|
|
|
|
|
g_string_append_printf(s, " %u", sfd->socket.local.port);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
call_stream_address(state->s, sfd->stream, SAF_ICE, sfd->local_intf, false);
|
|
|
|
|
g_string_append_printf(state->s, " %u", sfd->socket.local.port);
|
|
|
|
|
return __attr_manip(state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int insert_raddr_rport(GString *s, stream_fd *sfd, const sdp_ng_flags *flags) {
|
|
|
|
|
@ -2216,22 +2287,36 @@ static void insert_candidate(GString *s, stream_fd *sfd,
|
|
|
|
|
unsigned long priority;
|
|
|
|
|
struct packet_stream *ps = sfd->stream;
|
|
|
|
|
const struct local_intf *ifa = sfd->local_intf;
|
|
|
|
|
g_autoptr(GString) s_dst = g_string_new("");
|
|
|
|
|
__auto_type state = __attr_begin(s, flags, (media ? media->type_id : MT_UNKNOWN));
|
|
|
|
|
if (__attr_append(&state, "candidate"))
|
|
|
|
|
return;
|
|
|
|
|
g_string_append_c(s, ':');
|
|
|
|
|
if (__attr_append_str(&state, &ifa->ice_foundation))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (local_pref == -1)
|
|
|
|
|
local_pref = ifa->unique_id;
|
|
|
|
|
|
|
|
|
|
priority = ice_priority_pref(type_pref, local_pref, ps->component);
|
|
|
|
|
g_string_append_printf(s_dst, "%u UDP %lu ", ps->component, priority);
|
|
|
|
|
insert_ice_address(s_dst, sfd, flags);
|
|
|
|
|
g_string_append(s_dst, " typ ");
|
|
|
|
|
g_string_append(s_dst, ice_candidate_type_str(type));
|
|
|
|
|
if (__attr_append_f(&state, " %u", ps->component))
|
|
|
|
|
return;
|
|
|
|
|
if (__attr_append(&state, " UDP"))
|
|
|
|
|
return;
|
|
|
|
|
if (__attr_append_f(&state, " %lu", priority))
|
|
|
|
|
return;
|
|
|
|
|
g_string_append_c(s, ' ');
|
|
|
|
|
if (insert_ice_address(&state, sfd, flags))
|
|
|
|
|
return;
|
|
|
|
|
g_string_append(s, " typ ");
|
|
|
|
|
g_string_append(s, ice_candidate_type_str(type));
|
|
|
|
|
if (__attr_manip(&state))
|
|
|
|
|
return;
|
|
|
|
|
/* raddr and rport are required for non-host candidates: rfc5245 section-15.1 */
|
|
|
|
|
if(type != ICT_HOST)
|
|
|
|
|
insert_raddr_rport(s_dst, sfd, flags);
|
|
|
|
|
|
|
|
|
|
append_tagged_attr_to_gstring(s, "candidate", &ifa->ice_foundation, &STR_GS(s_dst), flags,
|
|
|
|
|
(media ? media->type_id : MT_UNKNOWN));
|
|
|
|
|
insert_raddr_rport(s, sfd, flags);
|
|
|
|
|
if (__attr_manip(&state))
|
|
|
|
|
return;
|
|
|
|
|
__attr_end(&state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void insert_sfd_candidates(GString *s, struct packet_stream *ps,
|
|
|
|
|
@ -2247,6 +2332,26 @@ static void insert_sfd_candidates(GString *s, struct packet_stream *ps,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void insert_remote_candidates(GString *s, const sdp_ng_flags *flags, struct call_media *media, struct ice_agent *ag) {
|
|
|
|
|
g_auto(candidate_q) rc = TYPED_GQUEUE_INIT;
|
|
|
|
|
__auto_type state = __attr_begin(s, flags, media->type_id);
|
|
|
|
|
if (__attr_append(&state, "remote-candidates"))
|
|
|
|
|
return;
|
|
|
|
|
g_string_append_c(s, ':');
|
|
|
|
|
|
|
|
|
|
/* prepare remote-candidates */
|
|
|
|
|
ice_remote_candidates(&rc, ag);
|
|
|
|
|
for (__auto_type l = rc.head; l; l = l->next) {
|
|
|
|
|
if (l != rc.head)
|
|
|
|
|
g_string_append(s, " ");
|
|
|
|
|
__auto_type cand = l->data;
|
|
|
|
|
if (__attr_append_f(&state, "%lu %s %u", cand->component_id,
|
|
|
|
|
sockaddr_print_buf(&cand->endpoint.address), cand->endpoint.port))
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
__attr_end(&state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void insert_candidates(GString *s, struct packet_stream *rtp, struct packet_stream *rtcp,
|
|
|
|
|
const sdp_ng_flags *flags, struct call_media *source_media)
|
|
|
|
|
{
|
|
|
|
|
@ -2255,7 +2360,6 @@ static void insert_candidates(GString *s, struct packet_stream *rtp, struct pack
|
|
|
|
|
struct ice_agent *ag;
|
|
|
|
|
unsigned int type_pref, local_pref;
|
|
|
|
|
enum ice_candidate_type cand_type;
|
|
|
|
|
struct ice_candidate *cand;
|
|
|
|
|
|
|
|
|
|
media = rtp->media;
|
|
|
|
|
|
|
|
|
|
@ -2278,22 +2382,9 @@ static void insert_candidates(GString *s, struct packet_stream *rtp, struct pack
|
|
|
|
|
insert_candidate(s, rtcp->selected_sfd, type_pref, ifa->unique_id, cand_type, flags,
|
|
|
|
|
rtp->media);
|
|
|
|
|
|
|
|
|
|
if (flags->opmode == OP_OFFER && AGENT_ISSET(ag, CONTROLLING)) {
|
|
|
|
|
g_auto(candidate_q) rc = TYPED_GQUEUE_INIT;
|
|
|
|
|
g_autoptr(GString) s_dst = g_string_new("");
|
|
|
|
|
|
|
|
|
|
/* prepare remote-candidates */
|
|
|
|
|
ice_remote_candidates(&rc, ag);
|
|
|
|
|
for (__auto_type l = rc.head; l; l = l->next) {
|
|
|
|
|
if (l != rc.head)
|
|
|
|
|
g_string_append(s_dst, " ");
|
|
|
|
|
cand = l->data;
|
|
|
|
|
g_string_append_printf(s_dst, "%lu %s %u", cand->component_id,
|
|
|
|
|
sockaddr_print_buf(&cand->endpoint.address), cand->endpoint.port);
|
|
|
|
|
}
|
|
|
|
|
append_attr_to_gstring(s, "remote-candidates", &STR_GS(s_dst), flags,
|
|
|
|
|
rtp->media->type_id);
|
|
|
|
|
}
|
|
|
|
|
if (flags->opmode == OP_OFFER && AGENT_ISSET(ag, CONTROLLING))
|
|
|
|
|
insert_remote_candidates(s, flags, rtp->media, ag);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -2322,11 +2413,43 @@ static void insert_setup(GString *out, struct call_media *media, const sdp_ng_fl
|
|
|
|
|
append_attr_to_gstring(out, "setup", &actpass_str, flags, media->type_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void insert_fingerprint(GString *s, struct call_media *media, const sdp_ng_flags *flags,
|
|
|
|
|
const struct dtls_hash_func *hf, struct dtls_fingerprint *fp)
|
|
|
|
|
{
|
|
|
|
|
/* prepare fingerprint */
|
|
|
|
|
__auto_type state = __attr_begin(s, flags, media->type_id);
|
|
|
|
|
if (__attr_append(&state, "fingerprint"))
|
|
|
|
|
return;
|
|
|
|
|
g_string_append_c(s, ':');
|
|
|
|
|
if (__attr_append(&state, hf->name))
|
|
|
|
|
return;
|
|
|
|
|
g_string_append(s, " ");
|
|
|
|
|
|
|
|
|
|
unsigned char *p = fp->digest;
|
|
|
|
|
for (unsigned int i = 0; i < hf->num_bytes; i++)
|
|
|
|
|
g_string_append_printf(s, "%02X:", *p++);
|
|
|
|
|
g_string_truncate(s, s->len - 1);
|
|
|
|
|
|
|
|
|
|
__attr_end(&state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void insert_tls_id(GString *s, struct call_media *media, const sdp_ng_flags *flags, struct dtls_connection *dtls) {
|
|
|
|
|
/* prepare tls-id */
|
|
|
|
|
__auto_type state = __attr_begin(s, flags, media->type_id);
|
|
|
|
|
if (__attr_append(&state, "tls-id"))
|
|
|
|
|
return;
|
|
|
|
|
g_string_append_c(s, ':');
|
|
|
|
|
|
|
|
|
|
unsigned char *p = dtls->tls_id;
|
|
|
|
|
for (unsigned int i = 0; i < sizeof(dtls->tls_id); i++)
|
|
|
|
|
g_string_append_printf(s, "%02x", *p++);
|
|
|
|
|
|
|
|
|
|
__attr_end(&state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void insert_dtls(GString *s, struct call_media *media, struct dtls_connection *dtls,
|
|
|
|
|
const sdp_ng_flags *flags)
|
|
|
|
|
{
|
|
|
|
|
unsigned char *p;
|
|
|
|
|
int i;
|
|
|
|
|
const struct dtls_hash_func *hf;
|
|
|
|
|
call_t *call = media->call;
|
|
|
|
|
|
|
|
|
|
@ -2359,28 +2482,10 @@ static void insert_dtls(GString *s, struct call_media *media, struct dtls_connec
|
|
|
|
|
/* a=setup: */
|
|
|
|
|
insert_setup(s, media, flags, true);
|
|
|
|
|
|
|
|
|
|
/* prepare fingerprint */
|
|
|
|
|
g_autoptr(GString) s_dst = g_string_new("");
|
|
|
|
|
g_string_append(s_dst, hf->name);
|
|
|
|
|
g_string_append(s_dst, " ");
|
|
|
|
|
insert_fingerprint(s, media, flags, hf, fp);
|
|
|
|
|
|
|
|
|
|
p = fp->digest;
|
|
|
|
|
for (i = 0; i < hf->num_bytes; i++)
|
|
|
|
|
g_string_append_printf(s_dst, "%02X:", *p++);
|
|
|
|
|
g_string_truncate(s_dst, s_dst->len - 1);
|
|
|
|
|
|
|
|
|
|
append_attr_to_gstring(s, "fingerprint", &STR_GS(s_dst), flags, media->type_id);
|
|
|
|
|
|
|
|
|
|
if (dtls) {
|
|
|
|
|
/* prepare tls-id */
|
|
|
|
|
g_string_truncate(s_dst, 0);
|
|
|
|
|
|
|
|
|
|
p = dtls->tls_id;
|
|
|
|
|
for (i = 0; i < sizeof(dtls->tls_id); i++)
|
|
|
|
|
g_string_append_printf(s_dst, "%02x", *p++);
|
|
|
|
|
|
|
|
|
|
append_attr_to_gstring(s, "tls-id", &STR_GS(s_dst), flags, media->type_id);
|
|
|
|
|
}
|
|
|
|
|
if (dtls)
|
|
|
|
|
insert_tls_id(s, media, flags, dtls);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void insert_crypto1(GString *s, struct call_media *media, struct crypto_params_sdes *cps,
|
|
|
|
|
@ -2394,7 +2499,16 @@ static void insert_crypto1(GString *s, struct call_media *media, struct crypto_p
|
|
|
|
|
if (!cps->params.crypto_suite || !MEDIA_ISSET(media, SDES) || MEDIA_ISSET(media, PASSTHRU))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
g_autoptr(GString) s_dst = g_string_new("");
|
|
|
|
|
__auto_type a_s = __attr_begin(s, flags, media->type_id);
|
|
|
|
|
if (__attr_append(&a_s, "crypto"))
|
|
|
|
|
return;
|
|
|
|
|
if (__attr_append_f(&a_s, ":%u", cps->tag))
|
|
|
|
|
return;
|
|
|
|
|
g_string_append_c(s, ' ');
|
|
|
|
|
if (__attr_append(&a_s, cps->params.crypto_suite->name))
|
|
|
|
|
return;
|
|
|
|
|
if (__attr_append(&a_s, " inline:"))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
p = b64_buf;
|
|
|
|
|
p += g_base64_encode_step((unsigned char *) cps->params.master_key,
|
|
|
|
|
@ -2411,26 +2525,25 @@ static void insert_crypto1(GString *s, struct call_media *media, struct crypto_p
|
|
|
|
|
p--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_string_append(s_dst, cps->params.crypto_suite->name);
|
|
|
|
|
g_string_append(s_dst, " inline:");
|
|
|
|
|
g_string_append_len(s_dst, b64_buf, p - b64_buf);
|
|
|
|
|
if (__attr_append_str(&a_s, &STR_LEN(b64_buf, p - b64_buf)))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (flags->sdes_lifetime)
|
|
|
|
|
g_string_append(s_dst, "|2^31");
|
|
|
|
|
g_string_append(s, "|2^31");
|
|
|
|
|
if (cps->params.mki_len) {
|
|
|
|
|
ull = 0;
|
|
|
|
|
for (i = 0; i < cps->params.mki_len && i < sizeof(ull); i++)
|
|
|
|
|
ull |= (unsigned long long) cps->params.mki[cps->params.mki_len - i - 1] << (i * 8);
|
|
|
|
|
g_string_append_printf(s_dst, "|%llu:%u", ull, cps->params.mki_len);
|
|
|
|
|
g_string_append_printf(s, "|%llu:%u", ull, cps->params.mki_len);
|
|
|
|
|
}
|
|
|
|
|
if (cps->params.session_params.unencrypted_srtp)
|
|
|
|
|
g_string_append(s_dst, " UNENCRYPTED_SRTP");
|
|
|
|
|
g_string_append(s, " UNENCRYPTED_SRTP");
|
|
|
|
|
if (cps->params.session_params.unencrypted_srtcp)
|
|
|
|
|
g_string_append(s_dst, " UNENCRYPTED_SRTCP");
|
|
|
|
|
g_string_append(s, " UNENCRYPTED_SRTCP");
|
|
|
|
|
if (cps->params.session_params.unauthenticated_srtp)
|
|
|
|
|
g_string_append(s_dst, " UNAUTHENTICATED_SRTP");
|
|
|
|
|
g_string_append(s, " UNAUTHENTICATED_SRTP");
|
|
|
|
|
|
|
|
|
|
append_int_tagged_attr_to_gstring(s, "crypto", cps->tag, &STR_GS(s_dst), flags, media->type_id);
|
|
|
|
|
__attr_end(&a_s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void insert_crypto(GString *s, struct call_media *media, const sdp_ng_flags *flags) {
|
|
|
|
|
@ -2444,20 +2557,24 @@ static void insert_rtcp_attr(GString *s, struct packet_stream *ps, const sdp_ng_
|
|
|
|
|
{
|
|
|
|
|
if (flags->no_rtcp_attr)
|
|
|
|
|
return;
|
|
|
|
|
g_autoptr(GString) s_dst = g_string_new("");
|
|
|
|
|
__auto_type state = __attr_begin(s, flags, (media ? media->type_id : MT_UNKNOWN));
|
|
|
|
|
if (__attr_append(&state, "rtcp"))
|
|
|
|
|
return;
|
|
|
|
|
g_string_append_c(s, ':');
|
|
|
|
|
|
|
|
|
|
g_string_append_printf(s_dst, "%u", ps->selected_sfd->socket.local.port);
|
|
|
|
|
if (__attr_append_f(&state, "%u", ps->selected_sfd->socket.local.port))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (flags->full_rtcp_attr) {
|
|
|
|
|
g_string_append(s_dst, " IN ");
|
|
|
|
|
g_string_append(s, " IN ");
|
|
|
|
|
if (!is_addr_unspecified(&flags->media_address))
|
|
|
|
|
g_string_append_printf(s_dst, "%s %s",
|
|
|
|
|
g_string_append_printf(s, "%s %s",
|
|
|
|
|
flags->media_address.family->rfc_name,
|
|
|
|
|
sockaddr_print_buf(&flags->media_address));
|
|
|
|
|
else
|
|
|
|
|
call_stream_address(s_dst, ps, SAF_NG, NULL, false);
|
|
|
|
|
call_stream_address(s, ps, SAF_NG, NULL, false);
|
|
|
|
|
}
|
|
|
|
|
append_attr_to_gstring(s, "rtcp", &STR_GS(s_dst), flags, (media ? media->type_id : MT_UNKNOWN));
|
|
|
|
|
__attr_end(&state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -2544,95 +2661,59 @@ const char *sdp_get_sendrecv(struct call_media *media) {
|
|
|
|
|
return "inactive";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Appends attributes to the output SDP.
|
|
|
|
|
* Includes substitute and remove SDP attribute manipulations.
|
|
|
|
|
*/
|
|
|
|
|
static void generic_append_attr_to_gstring(GString *s, const str * attr, char separator, const str * value,
|
|
|
|
|
/* Appends attributes (`a=name:value`) to the output SDP */
|
|
|
|
|
static void append_str_attr_to_gstring(GString *s, const str *name, const str *value,
|
|
|
|
|
const sdp_ng_flags *flags, enum media_type media_type)
|
|
|
|
|
{
|
|
|
|
|
struct sdp_manipulations *sdp_manipulations = sdp_manipulations_get_by_id(flags->sdp_manipulations, media_type);
|
|
|
|
|
|
|
|
|
|
str * attr_subst = sdp_manipulations_subst(sdp_manipulations, attr);
|
|
|
|
|
|
|
|
|
|
/* first check if the originally present attribute is to be removed */
|
|
|
|
|
if (sdp_manipulate_remove(sdp_manipulations, attr))
|
|
|
|
|
__auto_type state = __attr_begin(s, flags, media_type);
|
|
|
|
|
if (__attr_append_str(&state, name))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
g_string_append(s, "a=");
|
|
|
|
|
|
|
|
|
|
/* then, if there remains something to be substituted, do that */
|
|
|
|
|
if (attr_subst)
|
|
|
|
|
g_string_append_len(s, attr_subst->s, attr_subst->len); // complete attribute
|
|
|
|
|
else {
|
|
|
|
|
gsize attr_start = s->len; // save beginning of complete attribute string
|
|
|
|
|
|
|
|
|
|
/* attr name */
|
|
|
|
|
g_string_append_len(s, attr->s, attr->len);
|
|
|
|
|
|
|
|
|
|
/* attr value */
|
|
|
|
|
if (value && value->len) {
|
|
|
|
|
g_string_append_c(s, separator);
|
|
|
|
|
g_string_append_len(s, value->s, value->len);
|
|
|
|
|
|
|
|
|
|
// check if the complete attribute string is marked for removal ...
|
|
|
|
|
str complete = STR_LEN(s->str + attr_start, s->len - attr_start);
|
|
|
|
|
if (sdp_manipulate_remove(sdp_manipulations, &complete))
|
|
|
|
|
{
|
|
|
|
|
// rewind and bail
|
|
|
|
|
g_string_truncate(s, attr_start - 2); // -2 for `a=`
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ... or substitution
|
|
|
|
|
attr_subst = sdp_manipulations_subst(sdp_manipulations, &complete);
|
|
|
|
|
if (attr_subst) {
|
|
|
|
|
// rewind and replace
|
|
|
|
|
g_string_truncate(s, attr_start);
|
|
|
|
|
g_string_append_len(s, attr_subst->s, attr_subst->len);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_string_append(s, "\r\n");
|
|
|
|
|
g_string_append_c(s, ':');
|
|
|
|
|
if (__attr_append_str(&state, value))
|
|
|
|
|
return;
|
|
|
|
|
__attr_end(&state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Appends attributes (`a=name:value`) to the output SDP */
|
|
|
|
|
static void append_str_attr_to_gstring(GString *s, const str * name, const str * value,
|
|
|
|
|
/* Appends attributes (`a=name`) to the output SDP */
|
|
|
|
|
static void append_null_str_attr_to_gstring(GString *s, const str *name,
|
|
|
|
|
const sdp_ng_flags *flags, enum media_type media_type)
|
|
|
|
|
{
|
|
|
|
|
generic_append_attr_to_gstring(s, name, ':', value, flags, media_type);
|
|
|
|
|
__auto_type state = __attr_begin(s, flags, media_type);
|
|
|
|
|
if (__attr_append_str(&state, name))
|
|
|
|
|
return;
|
|
|
|
|
__attr_end(&state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Appends attributes (`a=name:tag value`) to the output SDP */
|
|
|
|
|
static void append_tagged_attr_to_gstring(GString *s, const char * name, const str *tag, const str * value,
|
|
|
|
|
const sdp_ng_flags *flags, enum media_type media_type)
|
|
|
|
|
/* Appends attributes (`a=name:something`) to the output SDP */
|
|
|
|
|
void append_v_str_attr_to_gstring(GString *s, const str *name, const sdp_ng_flags *flags,
|
|
|
|
|
enum media_type media_type, const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
if (sdp_manipulate_remove_c(name, flags, media_type))
|
|
|
|
|
__auto_type state = __attr_begin(s, flags, media_type);
|
|
|
|
|
if (__attr_append_str(&state, name))
|
|
|
|
|
return;
|
|
|
|
|
g_string_append_c(s, ':');
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
bool ret = __attr_append_v(&state, fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
if (ret)
|
|
|
|
|
return;
|
|
|
|
|
g_autoptr(GString) n = g_string_new(name);
|
|
|
|
|
g_string_append_c(n, ':');
|
|
|
|
|
g_string_append_len(n, tag->s, tag->len);
|
|
|
|
|
generic_append_attr_to_gstring(s, &STR_GS(n), ' ', value, flags, media_type);
|
|
|
|
|
__attr_end(&state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Appends attributes (`a=name:uint value`) to the output SDP */
|
|
|
|
|
static void append_int_tagged_attr_to_gstring(GString *s, const char * name, unsigned int tag, const str * value,
|
|
|
|
|
static void append_int_tagged_str_attr_to_gstring(GString *s, const str *name, unsigned int tag, const str *value,
|
|
|
|
|
const sdp_ng_flags *flags, enum media_type media_type)
|
|
|
|
|
{
|
|
|
|
|
if (sdp_manipulate_remove_c(name, flags, media_type))
|
|
|
|
|
__auto_type state = __attr_begin(s, flags, media_type);
|
|
|
|
|
if (__attr_append_str(&state, name))
|
|
|
|
|
return;
|
|
|
|
|
g_autoptr(GString) n = g_string_new(name);
|
|
|
|
|
g_string_append_printf(n, ":%u", tag);
|
|
|
|
|
generic_append_attr_to_gstring(s, &STR_GS(n), ' ', value, flags, media_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Appends attributes to the output SDP */
|
|
|
|
|
static void append_attr_int_to_gstring(GString *s, const char * name, const int value,
|
|
|
|
|
const sdp_ng_flags *flags, enum media_type media_type)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
append_int_tagged_attr_to_gstring(s, name, value, NULL, flags, media_type);
|
|
|
|
|
if (__attr_append_f(&state, ":%u", tag))
|
|
|
|
|
return;
|
|
|
|
|
g_string_append_c(s, ' ');
|
|
|
|
|
if (__attr_append_str(&state, value))
|
|
|
|
|
return;
|
|
|
|
|
__attr_end(&state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct packet_stream *print_rtcp(GString *s, struct call_media *media, packet_stream_list *rtp_ps_link,
|
|
|
|
|
@ -2657,14 +2738,14 @@ static struct packet_stream *print_rtcp(GString *s, struct call_media *media, pa
|
|
|
|
|
IS_OP_OTHER(flags->opmode)))
|
|
|
|
|
{
|
|
|
|
|
insert_rtcp_attr(s, ps, flags, media);
|
|
|
|
|
append_attr_to_gstring(s, "rtcp-mux", NULL, flags, media->type_id);
|
|
|
|
|
append_null_attr_to_gstring(s, "rtcp-mux", flags, media->type_id);
|
|
|
|
|
ps_rtcp = NULL;
|
|
|
|
|
}
|
|
|
|
|
else if (ps_rtcp && flags->ice_option != ICE_FORCE_RELAY) {
|
|
|
|
|
insert_rtcp_attr(s, ps_rtcp, flags, media);
|
|
|
|
|
|
|
|
|
|
if (MEDIA_ISSET(media, RTCP_MUX))
|
|
|
|
|
append_attr_to_gstring(s, "rtcp-mux", NULL, flags, media->type_id);
|
|
|
|
|
append_null_attr_to_gstring(s, "rtcp-mux", flags, media->type_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
@ -2739,15 +2820,15 @@ static void print_sdp_media_section(GString *s, struct call_media *media,
|
|
|
|
|
{
|
|
|
|
|
/* answer must be recvonly (sendonly-to-recvonly) */
|
|
|
|
|
if (MEDIA_ISSET(source_media, REAL_SENDONLY))
|
|
|
|
|
append_attr_to_gstring(s, "recvonly", NULL, flags, media->type_id);
|
|
|
|
|
append_null_attr_to_gstring(s, "recvonly", flags, media->type_id);
|
|
|
|
|
/* answer must be inactive (inactive-to-inactive) */
|
|
|
|
|
else
|
|
|
|
|
append_attr_to_gstring(s, "inactive", NULL, flags, media->type_id);
|
|
|
|
|
append_null_attr_to_gstring(s, "inactive", flags, media->type_id);
|
|
|
|
|
/* clear flags for this MoH offer/answer exchange, so that future exchanges are real */
|
|
|
|
|
MEDIA_CLEAR(source_media, FAKE_SENDRECV);
|
|
|
|
|
MEDIA_CLEAR(source_media, REAL_SENDONLY);
|
|
|
|
|
} else {
|
|
|
|
|
append_attr_to_gstring(s, sdp_get_sendrecv(media), NULL, flags,
|
|
|
|
|
append_null_attr_to_gstring(s, sdp_get_sendrecv(media), flags,
|
|
|
|
|
media->type_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -2782,7 +2863,7 @@ static void print_sdp_media_section(GString *s, struct call_media *media,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((MEDIA_ISSET(media, TRICKLE_ICE) && media->ice_agent)) {
|
|
|
|
|
append_attr_to_gstring(s, "end-of-candidates", NULL, flags, media->type_id);
|
|
|
|
|
append_null_attr_to_gstring(s, "end-of-candidates", flags, media->type_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
@ -2889,12 +2970,12 @@ static void sdp_out_add_other(GString *out, struct call_monologue *monologue,
|
|
|
|
|
append_attr_to_gstring(out, "rtpengine", &rtpe_instance_id, flags, media->type_id);
|
|
|
|
|
#ifdef WITH_TRANSCODING
|
|
|
|
|
if (monologue->player && monologue->player->opts.moh && rtpe_config.moh_attr_name) {
|
|
|
|
|
append_attr_to_gstring(out, rtpe_config.moh_attr_name, NULL, flags, media->type_id);
|
|
|
|
|
append_null_attr_to_gstring(out, rtpe_config.moh_attr_name, flags, media->type_id);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
/* ice-lite */
|
|
|
|
|
if (media_has_ice && media_has_ice_lite_self)
|
|
|
|
|
append_attr_to_gstring(out, "ice-lite", NULL, flags, media->type_id);
|
|
|
|
|
append_null_attr_to_gstring(out, "ice-lite", flags, media->type_id);
|
|
|
|
|
|
|
|
|
|
/* group */
|
|
|
|
|
if (source_ml && source_ml->sdp_session_group.len && flags->ice_option == ICE_FORCE_RELAY)
|
|
|
|
|
@ -3081,7 +3162,7 @@ static void sdp_out_original_media_attributes(GString *out, struct call_media *m
|
|
|
|
|
rtcp_ps = NULL;
|
|
|
|
|
insert_candidates(out, rtp_ps, rtcp_ps, flags, source_media);
|
|
|
|
|
if (MEDIA_ISSET(source_media, END_OF_CANDIDATES))
|
|
|
|
|
append_attr_to_gstring(out, "end-of-candidates", NULL, flags, media->type_id);
|
|
|
|
|
append_null_attr_to_gstring(out, "end-of-candidates", flags, media->type_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|