Browse Source

MT#56128 RTPEngine: Add options for arbitrary SDP manipulations

New section of option flags has been introduced for SDP body
attributes manipulations.

Three levels of the SDP session are concerned:
- session level (global one)
- media level - audio
- media level - video

Three different actions are supported for now:
- add
- remove

The value of the command has a wildcard matching approach.

Other attributes apart `a=` can not be edited by this functionality.
So such headers as: `c=`, `s=`, `o=` cannot be touched.

Change-Id: I939d4582839096b2399f7ded865e91ff6eb960a4
(cherry picked from commit 3f06c18793)
pull/1614/head
Donat Zenichev 3 years ago
parent
commit
d7dbae8720
6 changed files with 750 additions and 58 deletions
  1. +139
    -0
      README.md
  2. +96
    -1
      daemon/call_interfaces.c
  3. +186
    -53
      daemon/sdp.c
  4. +1
    -1
      include/call_interfaces.h
  5. +20
    -0
      include/sdp.h
  6. +308
    -3
      t/auto-daemon-tests.pl

+ 139
- 0
README.md View File

@ -1765,6 +1765,145 @@ The following keys are understood:
This option is only processed in `offer` messages and ignored otherwise.
**Optionally included SDP attributes manipulations:**
`sdp-attr` contains a dictionary controlling various aspects of attribute lines (or `a=` lines).
An intention of these option flags is to control session (global) and media session level
attributes (`a=` lines). With help of which, it's possible to add and remove
specific attribute lines.
This does affect an outgoing SDP offer. So it's meant to manipulate body attributes,
which RTPEngine generates during the offer processing. In other words, it manipulates
what has been already prepared by the RTPEngine on its own, taking into account received offer.
Furthermore, it's quite important to remember, that the changes, which have been
applied to SDP body attributes, will be not quite taken into account by the RTPEngine.
This means, it's not the same, as if they would be originally given by the session originator.
It's just a text manipulation.
That's why this kind of flags must be used with a full carefulness, because,
if not, this can lead sometimes to the unwanted result.
Usage syntax:
"sdp-attr" :
{
"<media-type>":
{
"<command>": ["<value>", "<value>"],
"<command>": ["<value>", "<value>"]
},
"<media-type>":
{
"<command>": ["<value>", "<value>"],
"<command>": ["<value>", "<value>"]
}
}
Description:
* `<media-type>`
Defines a level of command application. One media type can be given only once per command.
`<media-type>` can have one of the following values:
- `none` or `global`
Applies to the session level (global) attributes, but not to any of the
media session specific attributes.
- `audio`
Applies to all currently present media sessions of audio type.
- `video`
Applies to all currently present media sessions of audio type.
* `<command>`
The command to be applied to the targeted attribute line(s).
Each command can be used multiple times within one media session scope.
- `add`
Adds a new `a=` line with a given value to the concerned media attributes list.
If the attribute with such value already exists within this scope of media session,
then no duplication is to be added, therefore the older one remains untouched
and nothing extra is being added.
Can take multiple values (so multiple attributes can added per one command).
- `remove`
Removes a specified `a=` line from the concerned media attributes list.
If such line has been not found, the attributes list remains untouched.
Can take multiple values (so multiple attributes can removed per one command).
* `<value>`
The `value` doesn't take the `a=` lvalue part, only what must go after an equal sign.
For `remove` and `substitute`, the value of the command has a wildcard matching
using a prefix. So that, all values caught by the prefix are affected.
No wild-cards and regex expressions accepted. Only a prefix or whole value are allowed.
One should remember that some attributes are allowed to be present multiple times,
as for example `a=ssrc:`. Therefore the RTPEngine does not expect specified `a=` lines
to be unique within concerned media scope (global, audio or video).
This leads to the next point — `remove` and `substitute` commands can affect just
a single attribute, as well as multiple attributes, depending on the uniqueness
of the value in the given command.
For example, for a removal of SSRC one might want to remove all `a=ssrc:` attributes
regardless of their content. On the other hand, one might want to remove all attributes
corresponding to one SSRC only — so a removal of all `a=ssrc:123456`, for example.
Thus, in case of intention to remove multiple attribute lines related to a specific
scope of session parameters, one should specify a prefix as a value,
which would catch all of them. And vice-versa, in case of intention to remove quite
a specific attribute, one should consider tight uniqueness of the value (so full value).
Examples:
* Add a new (single) attribute line to the session (global) level:
"sdp-attr" :
{
"none" :
{
"add" : [ "sendrecv" ]
}
}
* Add two new attribute lines to audio session and remove one for video session:
"sdp-attr" :
{
"audio" :
{
"add" : [ "ptime:20", "sendrecv" ]
},
"video":
{
"remove" : [ "rtpmap:101 telephone-event/8000" ]
}
}
* Remove all attributes related to SSRC of the audio session:
"sdp-attr" :
{
"audio":
{
"remove": [ "ssrc:" ]
}
}
An example of a complete `offer` request dictionary could be (SDP body abbreviated):
{ "command": "offer", "call-id": "cfBXzDSZqhYNcXM", "from-tag": "mS9rSAn0Cr",


+ 96
- 1
daemon/call_interfaces.c View File

@ -61,7 +61,8 @@ INLINE int call_ng_flags_prefix(struct sdp_ng_flags *out, str *s_ori, const char
static void call_ng_flags_str_ht(struct sdp_ng_flags *out, str *s, void *htp);
static void call_ng_flags_str_q_multi(struct sdp_ng_flags *out, str *s, void *qp);
static void ng_stats_ssrc(bencode_item_t *dict, struct ssrc_hash *ht);
static str *str_dup_escape(const str *s);
static void sdp_command_free(void * p);
static int call_stream_address_gstring(GString *o, struct packet_stream *ps, enum stream_address_format format) {
int len, ret;
@ -605,6 +606,84 @@ INLINE void ng_osrtp_option(struct sdp_ng_flags *out, str *s, void *dummy) {
}
}
INLINE void ng_sdp_attr_manipulations(GQueue *sdp_attr_manipulations, bencode_item_t *value) {
if (!value || value->type != BENCODE_DICTIONARY)
ilog(LOG_WARN, "SDP manipulations: Wrong type for this type of command.");
for (bencode_item_t *it = value->child; it; it = it->sibling)
{
bencode_item_t *command_action = it->sibling ? it->sibling : NULL;
enum media_type media;
str media_type;
if (!command_action) /* if no action, makes no sense to continue */
continue;
/* detect media type */
if (!bencode_get_str(it, &media_type))
continue;
media = (!str_cmp(&media_type, "none") || !str_cmp(&media_type, "global")) ?
MT_UNKNOWN : codec_get_type(&media_type);
for (bencode_item_t *it_c = command_action->child; it_c; it_c = it_c->sibling)
{
bencode_item_t *command_value = it_c->sibling ? it_c->sibling : NULL;
if (!command_value) /* if no value, makes no sense to continue */
continue;
/* detect command type */
str command_type;
if (!bencode_get_str(it_c, &command_type))
continue;
struct sdp_command * command_obj;
command_obj = g_slice_alloc0(sizeof(*command_obj));
command_obj->media_type_id = media;
switch (__csh_lookup(&command_type)) {
case CSH_LOOKUP("add"):
command_obj->command_type_id = CMD_ADD;
break;
case CSH_LOOKUP("remove"):
command_obj->command_type_id = CMD_REM;
break;
default:
ilog(LOG_WARN, "SDP manipulations: Unknown SDP manipulation command type.");
g_slice_free1(sizeof(*command_obj), command_obj);
continue;
}
GHashTable ** ht = &command_obj->command_values;
*ht = g_hash_table_new_full(str_case_hash, str_case_equal, free, NULL);
for (bencode_item_t *it_v = command_value->child; it_v; it_v = it_v->sibling)
{
/* detect command value */
str command_value;
if (!bencode_get_str(it_v, &command_value))
continue;
str *s_copy = str_dup_escape(&command_value);
g_hash_table_replace(*ht, s_copy, s_copy);
}
/* no values - is a bad command */
if (g_hash_table_size(command_obj->command_values) == 0)
{
ilog(LOG_WARN, "SDP manipulations: An issue with given value(s), can't handle it.");
sdp_command_free(command_obj);
/* this command had no values, but still continue checking other commands */
continue;
} else {
g_queue_push_tail(sdp_attr_manipulations, command_obj);
}
}
}
}
INLINE void ng_el_option(struct sdp_ng_flags *out, str *s, void *dummy) {
switch (__csh_lookup(s)) {
case CSH_LOOKUP("off"):
@ -1258,6 +1337,12 @@ static void call_ng_main_flags(struct sdp_ng_flags *out, str *key, bencode_item_
for (bencode_item_t *it = value->child; it && diridx < 2; it = it->sibling)
bencode_get_str(it, &out->direction[diridx++]);
break;
case CSH_LOOKUP("sdp-attr"):
case CSH_LOOKUP("SDP-attr"):
if (value->type != BENCODE_DICTIONARY)
break;
ng_sdp_attr_manipulations(&out->sdp_attr_manipulations, value);
break;
case CSH_LOOKUP("received from"):
case CSH_LOOKUP("received-from"):
if (value->type != BENCODE_LIST)
@ -1611,6 +1696,14 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu
call_ng_flags_init(out, opmode);
call_ng_dict_iter(out, input, call_ng_main_flags);
}
static void sdp_command_free(void * p)
{
struct sdp_command * attr = p;
g_hash_table_destroy(attr->command_values);
g_slice_free1(sizeof(*attr), attr);
}
void call_ng_free_flags(struct sdp_ng_flags *flags) {
if (flags->codec_except)
g_hash_table_destroy(flags->codec_except);
@ -1622,6 +1715,8 @@ void call_ng_free_flags(struct sdp_ng_flags *flags) {
g_hash_table_destroy(flags->sdes_only);
if (flags->frequencies)
g_array_free(flags->frequencies, true);
g_queue_clear_full(&flags->sdp_attr_manipulations, sdp_command_free);
g_queue_clear_full(&flags->from_tags, free);
g_queue_clear_full(&flags->codec_offer, free);
g_queue_clear_full(&flags->codec_transcode, free);


+ 186
- 53
daemon/sdp.c View File

@ -283,15 +283,85 @@ struct sdp_attribute { /* example: a=rtpmap:8 PCMA/8000 */
static char __id_buf[6*2 + 1]; // 6 hex encoded characters
const str rtpe_instance_id = STR_CONST_INIT(__id_buf);
static void attr_free(void *p);
static void attr_insert(struct sdp_attributes *attrs, struct sdp_attribute *attr);
static void attr_free(void *p);
INLINE void chopper_append_c(struct sdp_chopper *c, const char *s);
static void append_attr_to_gstring(GString *s, const char * name, const str * value);
static void append_attr_char_to_gstring(GString *s, const char * name, const char * value);
static void append_attr_int_to_gstring(GString *s, const char * name, const int * value);
static int sdp_manipulate_command_cmp(gconstpointer a, gconstpointer b) {
const struct sdp_command * sc = a;
const struct sdp_command_fictitious * sc_lookup = b;
if (sc->command_type_id != sc_lookup->command_type_id)
return 1;
if (sc->media_type_id != sc_lookup->media_type_id)
return 1;
/* here expected only lookup by one value */
if (NULL != sc_lookup->command_value) {
if (!g_hash_table_lookup(sc->command_values, sc_lookup->command_value))
return 1;
}
return 0;
}
/**
* Checks whether a given type of SDP manipulation exists for a given session level.
*/
static int sdp_manipulate_check(enum command_type command_type, GQueue *sdp_manipulate,
enum media_type media_type, str *attr_name) {
/* for now we only support session lvl, audio and media streams */
if (media_type == MT_OTHER)
{
ilog(LOG_WARNING, "SDP manipulations: Unsupported media type '%d', can't manipulate these attributes.",
media_type);
return 0;
}
struct sdp_command_fictitious fictitious = {command_type, media_type, attr_name};
if (g_queue_find_custom(sdp_manipulate, &fictitious, sdp_manipulate_command_cmp))
return 1;
return 0;
}
/**
* Adds values into a requested session level (global, audio, video)
*/
static void sdp_manipulations_add(struct sdp_chopper *chop, GQueue *sdp_manipulate,
enum media_type media_type) {
for (GList *l = sdp_manipulate->head; l; l = l->next)
{
struct sdp_command * command = l->data;
GList *ll = NULL;
if (command->command_type_id != CMD_ADD ||
command->media_type_id != media_type)
continue;
/* TODO: add lookup if not already in the body */
ll = g_hash_table_get_values(command->command_values);
for (GList *l = ll; l; l = l->next)
{
str * value = l->data;
chopper_append_c(chop, "a=");
chopper_append_c(chop, value->s);
chopper_append_c(chop, "\r\n");
}
if (ll)
g_list_free(ll);
}
}
static void append_attr_to_gstring(GString *s, char * name, const str * value,
struct sdp_ng_flags *flags, enum media_type media_type);
static void append_attr_char_to_gstring(GString *s, char * name, const char * value,
struct sdp_ng_flags *flags, enum media_type media_type);
static void append_attr_int_to_gstring(GString *s, char * name, const int * value,
struct sdp_ng_flags *flags, enum media_type media_type);
INLINE struct sdp_attribute *attr_get_by_id(struct sdp_attributes *a, int id) {
return g_hash_table_lookup(a->id_hash, &id);
@ -1123,7 +1193,6 @@ int sdp_parse(str *body, GQueue *sessions, const struct sdp_ng_flags *flags) {
struct sdp_attributes *attrs;
struct sdp_attribute *attr;
str *adj_s;
GQueue *attr_queue;
b = body->s;
end = str_end(body);
@ -2167,8 +2236,9 @@ void sdp_chopper_destroy_ret(struct sdp_chopper *chop, str *ret) {
sdp_chopper_destroy(chop);
}
/* processing existing session attributes (those present in offer/answer) */
static int process_session_attributes(struct sdp_chopper *chop, struct sdp_attributes *attrs,
struct sdp_ng_flags *flags)
struct sdp_ng_flags *flags, GQueue *sdp_manipulate)
{
GList *l;
struct sdp_attribute *attr;
@ -2176,6 +2246,10 @@ static int process_session_attributes(struct sdp_chopper *chop, struct sdp_attri
for (l = attrs->list.head; l; l = l->next) {
attr = l->data;
/* if attr is supposed to be removed don't add to the chop->output */
if (sdp_manipulate_check(CMD_REM, sdp_manipulate, MT_UNKNOWN, &attr->line_value))
goto strip;
switch (attr->attr) {
case ATTR_ICE:
case ATTR_ICE_UFRAG:
@ -2239,8 +2313,9 @@ strip:
return 0;
}
/* processing existing media attributes (those present in offer/answer) */
static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media *sdp,
struct sdp_ng_flags *flags, struct call_media *media)
struct sdp_ng_flags *flags, struct call_media *media, GQueue *sdp_manipulate)
{
GList *l;
struct sdp_attributes *attrs = &sdp->attributes;
@ -2253,6 +2328,10 @@ static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media *
if (MEDIA_ISSET(media, GENERATOR))
goto strip;
/* if attr is supposed to be removed don't add to the chop->output */
if (sdp_manipulate_check(CMD_REM, sdp_manipulate, sdp->media_type_id, &attr->line_value))
goto strip;
switch (attr->attr) {
case ATTR_ICE:
case ATTR_ICE_UFRAG:
@ -2394,7 +2473,7 @@ out:
static void insert_candidate(GString *s, struct stream_fd *sfd,
unsigned int type_pref, unsigned int local_pref, enum ice_candidate_type type,
struct sdp_ng_flags *flags)
struct sdp_ng_flags *flags, struct sdp_media *sdp_media)
{
unsigned long priority;
struct packet_stream *ps = sfd->stream;
@ -2416,20 +2495,21 @@ static void insert_candidate(GString *s, struct stream_fd *sfd,
insert_raddr_rport(s_dst, sfd, flags);
/* append to the chop->output */
append_attr_to_gstring(s, s_dst->str, NULL);
append_attr_to_gstring(s, s_dst->str, NULL, flags,
(sdp_media ? sdp_media->media_type_id : MT_UNKNOWN));
g_string_free(s_dst, TRUE);
}
static void insert_sfd_candidates(GString *s, struct packet_stream *ps,
unsigned int type_pref, unsigned int local_pref, enum ice_candidate_type type,
struct sdp_ng_flags *flags)
struct sdp_ng_flags *flags, struct sdp_media *sdp_media)
{
GList *l;
struct stream_fd *sfd;
for (l = ps->sfds.head; l; l = l->next) {
sfd = l->data;
insert_candidate(s, sfd, type_pref, local_pref, type, flags);
insert_candidate(s, sfd, type_pref, local_pref, type, flags, sdp_media);
if (local_pref != -1)
local_pref++;
@ -2463,9 +2543,9 @@ static void insert_candidates(GString *s, struct packet_stream *rtp, struct pack
if (ag && AGENT_ISSET(ag, COMPLETED)) {
ifa = rtp->selected_sfd->local_intf;
insert_candidate(s, rtp->selected_sfd, type_pref, ifa->unique_id, cand_type, flags);
insert_candidate(s, rtp->selected_sfd, type_pref, ifa->unique_id, cand_type, flags, sdp_media);
if (rtcp) /* rtcp-mux only possible in answer */
insert_candidate(s, rtcp->selected_sfd, type_pref, ifa->unique_id, cand_type, flags);
insert_candidate(s, rtcp->selected_sfd, type_pref, ifa->unique_id, cand_type, flags, sdp_media);
if (flags->opmode == OP_OFFER && AGENT_ISSET(ag, CONTROLLING)) {
GQueue rc;
@ -2483,7 +2563,7 @@ static void insert_candidates(GString *s, struct packet_stream *rtp, struct pack
sockaddr_print_buf(&cand->endpoint.address), cand->endpoint.port);
}
/* append to the chop->output */
append_attr_to_gstring(s, s_dst->str, NULL);
append_attr_to_gstring(s, s_dst->str, NULL, flags, (sdp_media ? sdp_media->media_type_id : MT_UNKNOWN));
g_string_free(s_dst, TRUE);
g_queue_clear(&rc);
@ -2491,13 +2571,14 @@ static void insert_candidates(GString *s, struct packet_stream *rtp, struct pack
return;
}
insert_sfd_candidates(s, rtp, type_pref, local_pref, cand_type, flags);
insert_sfd_candidates(s, rtp, type_pref, local_pref, cand_type, flags, sdp_media);
if (rtcp) /* rtcp-mux only possible in answer */
insert_sfd_candidates(s, rtcp, type_pref, local_pref, cand_type, flags);
insert_sfd_candidates(s, rtcp, type_pref, local_pref, cand_type, flags, sdp_media);
}
static void insert_dtls(GString *s, struct call_media *media, struct dtls_connection *dtls) {
static void insert_dtls(GString *s, struct call_media *media, struct dtls_connection *dtls,
struct sdp_ng_flags *flags) {
unsigned char *p;
int i;
const struct dtls_hash_func *hf;
@ -2541,7 +2622,7 @@ static void insert_dtls(GString *s, struct call_media *media, struct dtls_connec
else if (MEDIA_ISSET(media, SETUP_ACTIVE))
actpass = "active";
append_attr_char_to_gstring(s, "a=setup:", actpass);
append_attr_char_to_gstring(s, "a=setup:", actpass, flags, media->type_id);
/* prepare fingerprint */
g_string_append(s_dst, "a=fingerprint:");
@ -2554,7 +2635,7 @@ static void insert_dtls(GString *s, struct call_media *media, struct dtls_connec
g_string_truncate(s_dst, s_dst->len - 1);
/* append to the chop->output */
append_attr_to_gstring(s, s_dst->str, NULL);
append_attr_to_gstring(s, s_dst->str, NULL, flags, media->type_id);
g_string_free(s_dst, TRUE);
if (dtls) {
@ -2567,7 +2648,7 @@ static void insert_dtls(GString *s, struct call_media *media, struct dtls_connec
g_string_append_printf(s_dst, "%02x", *p++);
/* append to the chop->output */
append_attr_to_gstring(s, s_dst->str, NULL);
append_attr_to_gstring(s, s_dst->str, NULL, flags, media->type_id);
g_string_free(s_dst, TRUE);
}
}
@ -2623,16 +2704,18 @@ static void insert_crypto1(GString *s, struct call_media *media, struct crypto_p
g_string_append(s_dst, " UNAUTHENTICATED_SRTP");
/* append to the chop->output */
append_attr_to_gstring(s, s_dst->str, NULL);
append_attr_to_gstring(s, s_dst->str, NULL, flags, media->type_id);
g_string_free(s_dst, TRUE);
}
static void insert_crypto(GString *s, struct call_media *media, struct sdp_ng_flags *flags) {
if (!media->protocol || !media->protocol->srtp)
return;
for (GList *l = media->sdes_out.head; l; l = l->next)
insert_crypto1(s, media, l->data, flags);
}
static void insert_rtcp_attr(GString *s, struct packet_stream *ps, const struct sdp_ng_flags *flags) {
static void insert_rtcp_attr(GString *s, struct packet_stream *ps, struct sdp_ng_flags *flags,
struct sdp_media *sdp_media) {
if (flags && flags->no_rtcp_attr)
return;
GString * s_dst = g_string_new("");
@ -2650,7 +2733,7 @@ static void insert_rtcp_attr(GString *s, struct packet_stream *ps, const struct
g_string_append_printf(s_dst, " IN %.*s", len, buf);
}
/* append to the chop->output */
append_attr_to_gstring(s, s_dst->str, NULL);
append_attr_to_gstring(s, s_dst->str, NULL, flags, (sdp_media ? sdp_media->media_type_id : MT_UNKNOWN));
g_string_free(s_dst, TRUE);
}
@ -2709,8 +2792,19 @@ const char *sdp_get_sendrecv(struct call_media *media) {
}
/* A function used to append attributes to the output chop */
static void append_attr_to_gstring(GString *s, const char * name, const str * value)
static void append_attr_to_gstring(GString *s, char * name, const str * value,
struct sdp_ng_flags *flags, enum media_type media_type)
{
str attr;
str_init(&attr, name);
GQueue *sdp_manipulate = &flags->sdp_attr_manipulations;
/* take into account SDP arbitary manipulations */
if (sdp_manipulate_check(CMD_REM, sdp_manipulate, media_type, &attr)) {
ilog(LOG_DEBUG, "Cannot insert: '%s' because prevented by SDP manipulations", name);
return;
}
/* attr name */
if (name[0] != 'a' && name[1] != '=') {
g_string_append(s, "a=");
@ -2724,8 +2818,19 @@ static void append_attr_to_gstring(GString *s, const char * name, const str * va
}
/* A function used to append attributes to the output chop */
static void append_attr_char_to_gstring(GString *s, const char * name, const char * value)
static void append_attr_char_to_gstring(GString *s, char * name, const char * value,
struct sdp_ng_flags *flags, enum media_type media_type)
{
str attr;
str_init(&attr, name);
GQueue *sdp_manipulate = &flags->sdp_attr_manipulations;
/* take into account SDP arbitary manipulations */
if (sdp_manipulate_check(CMD_REM, sdp_manipulate, media_type, &attr)) {
ilog(LOG_DEBUG, "Cannot insert: '%s' because prevented by SDP manipulations", name);
return;
}
/* attr name */
if (name[0] != 'a' && name[1] != '=') {
g_string_append(s, "a=");
@ -2739,8 +2844,19 @@ static void append_attr_char_to_gstring(GString *s, const char * name, const cha
}
/* A function used to append attributes to the output chop */
static void append_attr_int_to_gstring(GString *s, const char * name, const int * value)
static void append_attr_int_to_gstring(GString *s, char * name, const int * value,
struct sdp_ng_flags *flags, enum media_type media_type)
{
str attr;
str_init(&attr, name);
GQueue *sdp_manipulate = &flags->sdp_attr_manipulations;
/* take into account SDP arbitary manipulations */
if (sdp_manipulate_check(CMD_REM, sdp_manipulate, media_type, &attr)) {
ilog(LOG_DEBUG, "Cannot insert: '%s' because prevented by SDP manipulations", name);
return;
}
/* attr name */
if (name[0] != 'a' && name[1] != '=') {
g_string_append(s, "a=");
@ -2753,12 +2869,8 @@ static void append_attr_int_to_gstring(GString *s, const char * name, const int
g_string_append(s, "\r\n");
}
void print_sendrecv(GString *s, struct call_media *media) {
append_attr_to_gstring(s, sdp_get_sendrecv(media), NULL);
}
struct packet_stream *print_rtcp(GString *s, struct call_media *media, GList *rtp_ps_link,
struct sdp_ng_flags *flags)
struct sdp_ng_flags *flags, struct sdp_media *sdp_media)
{
struct packet_stream *ps = rtp_ps_link->data;
struct packet_stream *ps_rtcp = NULL;
@ -2778,14 +2890,14 @@ struct packet_stream *print_rtcp(GString *s, struct call_media *media, GList *rt
|| ((flags->opmode == OP_OFFER || flags->opmode == OP_REQUEST)
&& flags->rtcp_mux_require)))
{
insert_rtcp_attr(s, ps, flags);
append_attr_to_gstring(s, "a=rtcp-mux", NULL);
insert_rtcp_attr(s, ps, flags, sdp_media);
append_attr_to_gstring(s, "a=rtcp-mux", NULL, flags, media->type_id);
ps_rtcp = NULL;
}
else if (ps_rtcp && (!flags || flags->ice_option != ICE_FORCE_RELAY)) {
insert_rtcp_attr(s, ps_rtcp, flags);
insert_rtcp_attr(s, ps_rtcp, flags, sdp_media);
if (MEDIA_ISSET(media, RTCP_MUX))
append_attr_to_gstring(s, "a=rtcp-mux", NULL);
append_attr_to_gstring(s, "a=rtcp-mux", NULL, flags, media->type_id);
}
}
else
@ -2801,9 +2913,9 @@ static void print_sdp_session_section(GString *s, struct sdp_ng_flags *flags,
bool media_has_ice_lite_self = MEDIA_ISSET(call_media, ICE_LITE_SELF);
if (flags->loop_protect)
append_attr_to_gstring(s, "a=rtpengine:", &rtpe_instance_id);
append_attr_to_gstring(s, "a=rtpengine:", &rtpe_instance_id, flags, MT_UNKNOWN);
if (media_has_ice && media_has_ice_lite_self)
append_attr_to_gstring(s, "a=ice-lite", NULL);
append_attr_to_gstring(s, "a=ice-lite", NULL, flags, MT_UNKNOWN);
}
static struct packet_stream *print_sdp_media_section(GString *s, struct call_media *media,
@ -2815,9 +2927,9 @@ static struct packet_stream *print_sdp_media_section(GString *s, struct call_med
struct packet_stream *ps_rtcp = NULL;
if (media->media_id.s)
append_attr_to_gstring(s, "a=mid:", &media->media_id);
append_attr_to_gstring(s, "a=mid:", &media->media_id, flags, media->type_id);
if (media->label.len && flags && flags->siprec)
append_attr_to_gstring(s, "a=label:", &media->label);
append_attr_to_gstring(s, "a=label:", &media->label, flags, media->type_id);
if (is_active) {
if (proto_is_rtp(media->protocol))
@ -2825,31 +2937,40 @@ static struct packet_stream *print_sdp_media_section(GString *s, struct call_med
insert_sdp_attributes(s, media);
/* print sendrecv */
if (!flags || !flags->original_sendrecv)
print_sendrecv(s, media);
append_attr_to_gstring(s, (char*)sdp_get_sendrecv(media), NULL, flags,
media->type_id);
ps_rtcp = print_rtcp(s, media, rtp_ps_link, flags);
ps_rtcp = print_rtcp(s, media, rtp_ps_link, flags, sdp_media);
insert_crypto(s, media, flags);
insert_dtls(s, media, dtls_ptr(rtp_ps->selected_sfd));
insert_dtls(s, media, dtls_ptr(rtp_ps->selected_sfd), flags);
if (proto_is_rtp(media->protocol) && media->ptime) {
append_attr_int_to_gstring(s, "a=ptime:", &media->ptime);
append_attr_int_to_gstring(s, "a=ptime:", &media->ptime, flags,
media->type_id);
}
if (MEDIA_ISSET(media, ICE) && media->ice_agent) {
append_attr_to_gstring(s, "a=ice-ufrag:", &media->ice_agent->ufrag[1]);
append_attr_to_gstring(s, "a=ice-pwd:", &media->ice_agent->pwd[1]);
append_attr_to_gstring(s, "a=ice-ufrag:", &media->ice_agent->ufrag[1], flags,
media->type_id);
append_attr_to_gstring(s, "a=ice-pwd:", &media->ice_agent->pwd[1], flags,
media->type_id);
}
if (MEDIA_ISSET(media, TRICKLE_ICE) && media->ice_agent)
append_attr_to_gstring(s, "a=ice-options:trickle", NULL);
if (MEDIA_ISSET(media, ICE))
if (MEDIA_ISSET(media, TRICKLE_ICE) && media->ice_agent) {
append_attr_to_gstring(s, "a=ice-options:trickle", NULL, flags,
media->type_id);
}
if (MEDIA_ISSET(media, ICE)) {
insert_candidates(s, rtp_ps, ps_rtcp, flags, sdp_media);
}
}
if ((MEDIA_ISSET(media, TRICKLE_ICE) && media->ice_agent) || force_end_of_ice)
append_attr_to_gstring(s, "a=end-of-candidates", NULL);
if ((MEDIA_ISSET(media, TRICKLE_ICE) && media->ice_agent) || force_end_of_ice) {
append_attr_to_gstring(s, "a=end-of-candidates", NULL, flags, media->type_id);
}
return ps_rtcp;
}
@ -2864,6 +2985,8 @@ static const char *replace_sdp_media_section(struct sdp_chopper *chop, struct ca
bool is_active = true;
GQueue *sdp_manipulate = &flags->sdp_attr_manipulations;
if (flags->ice_option != ICE_FORCE_RELAY && call_media->type_id != MT_MESSAGE) {
err = "failed to replace media type";
if (replace_media_type(chop, sdp_media, call_media))
@ -2899,7 +3022,7 @@ static const char *replace_sdp_media_section(struct sdp_chopper *chop, struct ca
}
err = "failed to process media attributes";
if (process_media_attributes(chop, sdp_media, flags, call_media))
if (process_media_attributes(chop, sdp_media, flags, call_media, sdp_manipulate))
goto error;
copy_up_to_end_of(chop, &sdp_media->s);
@ -2932,6 +3055,8 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu
m = monologue->medias.head;
media_index = 1;
GQueue *sdp_manipulate = &flags->sdp_attr_manipulations;
for (l = sessions->head; l; l = l->next) {
session = l->data;
err = "no matching session media";
@ -3020,7 +3145,7 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu
if (!MEDIA_ISSET(call_media, PASSTHRU)) {
err = "failed to process session attributes";
if (process_session_attributes(chop, &session->attributes, flags))
if (process_session_attributes(chop, &session->attributes, flags, sdp_manipulate))
goto error;
}
@ -3029,6 +3154,10 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu
/* add a list of important attrs to the session section */
print_sdp_session_section(chop->output, flags, call_media);
/* ADD arbitary SDP manipulations for a session sessions */
if (sdp_manipulate_check(CMD_ADD, sdp_manipulate, MT_UNKNOWN, NULL))
sdp_manipulations_add(chop, sdp_manipulate, MT_UNKNOWN);
for (k = session->media_streams.head; k; k = k->next) {
sdp_media = k->data;
@ -3102,6 +3231,10 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu
chopper_append_c(chop, "\r\n");
}
/* ADD arbitary SDP manipulations for audio/video media sessions */
if (sdp_manipulate_check(CMD_ADD, sdp_manipulate, sdp_media->media_type_id, NULL))
sdp_manipulations_add(chop, sdp_manipulate, sdp_media->media_type_id);
media_index++;
m = m->next;
}


+ 1
- 1
include/call_interfaces.h View File

@ -10,7 +10,6 @@
#include "call.h"
struct call;
struct call_stats;
struct streambuf_stream;
@ -58,6 +57,7 @@ struct sdp_ng_flags {
GQueue sdes_order; /* the order, in which crypto suites are being added to the SDP */
GQueue sdes_offerer_pref; /* preferred crypto suites to be selected for the offerer */
str dtls_fingerprint;
GQueue sdp_attr_manipulations; /* commands to manipulate attr lines in SDP */
enum {
ICE_DEFAULT = 0,
ICE_REMOVE,


+ 20
- 0
include/sdp.h View File

@ -1,11 +1,31 @@
#ifndef _SDP_H_
#define _SDP_H_
#include <glib.h>
#include "str.h"
#include "call.h"
#include "media_socket.h"
enum command_type {
CMD_ADD = 0,
CMD_REM,
CMD_SUBST,
};
/* command to manipulate attr in SDP body */
struct sdp_command {
enum command_type command_type_id; /* command to apply */
enum media_type media_type_id; /* scope (session, media audio, media video) */
GHashTable * command_values; /* a list of command values, values in str format */
};
/* This one is used only for lookups */
struct sdp_command_fictitious {
enum command_type command_type_id;
enum media_type media_type_id;
str * command_value;
};
struct ice_candidate;


+ 308
- 3
t/auto-daemon-tests.pl View File

@ -77,9 +77,6 @@ sub stun_succ {
return $packet;
};
new_call;
offer('legacy OSRTP offer, control',
@ -15370,6 +15367,314 @@ a=rtcp:PORT
a=crypto:3 AES_256_CM_HMAC_SHA1_80 inline:CRYPTO256
SDP
# Arbitary SDP manipulations
new_call;
offer('SDP attr manipulations - remove a= line on media audio', { ICE => 'remove', DTLS => 'off', SDES => [ 'nonew' ], 'sdp-attr' => { audio => { remove => ['test'] } }}, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/SAVP 0
c=IN IP4 198.51.100.1
a=sendrecv
a=test
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
answer('SDP attr manipulations - remove a= line on media audio', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/SAVP 0
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('SDP attr manipulations - remove a= line on global session', { ICE => 'remove', DTLS => 'off', SDES => [ 'nonew' ], 'sdp-attr' => { none => { remove => ['test'] } }}, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
a=test
m=audio 2000 RTP/SAVP 0
c=IN IP4 198.51.100.1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
answer('SDP attr manipulations - remove a= line on global session', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/SAVP 0
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('SDP attr manipulations - remove a= line on global session and media audio', { ICE => 'remove', DTLS => 'off', SDES => [ 'nonew' ], 'sdp-attr' => { audio => { remove => ['test2'] }, none => { remove => ['test1'] } }}, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
a=test1
m=audio 2000 RTP/SAVP 0
c=IN IP4 198.51.100.1
a=sendrecv
a=test2
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
answer('SDP attr manipulations - remove a= line on global session and media audio', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/SAVP 0
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('SDP attr manipulations - add a= line on global session and media audio', { ICE => 'remove', DTLS => 'off', SDES => [ 'nonew' ], 'sdp-attr' => { audio => { add => ['test1'] }, none => { add => ['test2'] } }}, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/SAVP 0
c=IN IP4 198.51.100.1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
a=test2
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
a=test1
SDP
answer('SDP attr manipulations - add a= line on global session and media audio', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/SAVP 0
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('SDP attr manipulations - add a= line for media audio, two times', { ICE => 'remove', DTLS => 'off', SDES => [ 'nonew' ], 'sdp-attr' => { audio => { add => ['test1', 'test2'] } }}, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/SAVP 0
c=IN IP4 198.51.100.1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
a=test1
a=test2
SDP
answer('SDP attr manipulations - add a= line for media audio, two times', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/SAVP 0
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('SDP attr manipulations - add a= line for media audio and remove one', { ICE => 'remove', DTLS => 'off', SDES => [ 'nonew' ], 'sdp-attr' => { audio => { add => ['test1'], remove => ['test2'] } }}, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/SAVP 0
c=IN IP4 198.51.100.1
a=sendrecv
a=test2
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
a=test1
SDP
answer('SDP attr manipulations - add a= line for media audio and remove one', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/SAVP 0
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('SDP attr manipulations - remove two a= lines from global session level', { ICE => 'remove', DTLS => 'off', SDES => [ 'nonew' ], 'sdp-attr' => { none => { remove => ['test1', 'test2'] } }}, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
a=test1
a=test2
m=audio 2000 RTP/SAVP 0
c=IN IP4 198.51.100.1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
answer('SDP attr manipulations - remove two a= lines from global session level', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/SAVP 0
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
# codec masking gh#664
new_call;


Loading…
Cancel
Save