MT#58535 rtpp_flags: introduce raw flags parsing

Add support of rtpp_flags parsing for the daemon.

From now on, it's possible to parse option flags
on the daemon side instead of the kamailio module.

It's identical to what the module does, but the
difference is:
- module sends general call identification such as:
  call-id, From/To tags, viabranch using bencode
- meanwhile all generic/non-generic option flags
  are added to the `rtpp_flags` bencode member as str
- parsing of that is done as usually using the
  `call_ng_main_flags()` /  `call_ng_flags_flags()`
  and additionally using new parser `parse_rtpp_flags()`

New file implementation and header introduced:
- control_ng_flags_parser.c
- control_ng_flags_parser.h

Otherwise no functional changes, and the parsing itself
remains working following the same algorithm.

Change-Id: I59e47fa1947e2aeaa0bbf3930a0f21d9a6d669ad
pull/1809/head
Donat Zenichev 1 year ago
parent c3e32b77e4
commit 36c19b9111

@ -88,7 +88,7 @@ endif
include ../lib/mqtt.Makefile
SRCS= main.c kernel.c helpers.c control_tcp.c call.c control_udp.c redis.c \
bencode.c cookie_cache.c udp_listener.c control_ng.strhash.c sdp.strhash.c stun.c rtcp.c \
bencode.c cookie_cache.c udp_listener.c control_ng_flags_parser.c control_ng.strhash.c sdp.strhash.c stun.c rtcp.c \
crypto.c rtp.c call_interfaces.strhash.c dtls.c log.c cli.c graphite.c ice.c \
media_socket.c homer.c recording.c statistics.c cdr.c ssrc.c iptables.c tcp_listener.c \
codec.c load.c dtmf.c timerthread.c media_player.c jitter_buffer.c t38.c websocket.c \

@ -30,6 +30,7 @@
#include "dtmf.h"
#include "codec.h"
#include "dtmf.h"
#include "control_ng_flags_parser.h"
typedef union {
const struct sdp_attr_helper *attr_helper;
@ -1821,6 +1822,19 @@ static void call_ng_main_flags(sdp_ng_flags *out, str *key, bencode_item_t *valu
case CSH_LOOKUP("RTCP-mux"):
call_ng_flags_str_list(out, value, call_ng_flags_rtcp_mux, NULL);
break;
case CSH_LOOKUP("rtpp-flags"):
case CSH_LOOKUP("rtpp_flags"):
/* new dictionary to store rtpp_flags */
bencode_item_t * dict;
dict = bencode_dictionary(value->buffer);
assert(dict != NULL);
/* s - parse rtpp flags */
parse_rtpp_flags(&s, dict);
if (dict && dict->child)
call_ng_dict_iter(out, dict, call_ng_main_flags); /* recursive */
break;
case CSH_LOOKUP("SDES"):
case CSH_LOOKUP("sdes"):
call_ng_flags_str_list(out, value, ng_sdes_option, NULL);

@ -170,12 +170,15 @@ static void control_ng_process_payload(str *reply, str *data, const endpoint_t *
if (data->len <= 0)
goto err_send;
/* Bencode dictionary */
if (data->s[0] == 'd') {
dict = bencode_decode_expect_str(&ngbuf->buffer, data, BENCODE_DICTIONARY);
errstr = "Could not decode bencode dictionary";
if (!dict)
goto err_send;
}
/* JSON */
else if (data->s[0] == '{') {
collapse_func = bencode_collapse_str_json;
JsonParser *json = json_parser_new();
@ -188,6 +191,7 @@ static void control_ng_process_payload(str *reply, str *data, const endpoint_t *
if (!dict || dict->type != BENCODE_DICTIONARY)
goto err_send;
}
else {
errstr = "Invalid NG data format";
goto err_send;

@ -0,0 +1,481 @@
#include "control_ng_flags_parser.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ctype.h>
#include "log.h"
#include "log_funcs.h"
/**
* Data structures.
*/
static const char *transports[] = {
[0x00] = "RTP/AVP",
[0x01] = "RTP/SAVP",
[0x02] = "RTP/AVPF",
[0x03] = "RTP/SAVPF",
[0x04] = "UDP/TLS/RTP/SAVP",
[0x06] = "UDP/TLS/RTP/SAVPF",
};
/**
* Helpers.
*/
static int get_ip_type(char *str_addr)
{
struct addrinfo hint, *info = NULL;
int ret;
memset(&hint, '\0', sizeof hint);
hint.ai_family = PF_UNSPEC;
hint.ai_flags = AI_NUMERICHOST;
ret = getaddrinfo(str_addr, NULL, &hint, &info);
if(ret) {
/* Invalid ip addinfos */
return -1;
}
if(info->ai_family == AF_INET) {
ilogs(control, LOG_DEBUG, "%s is an ipv4 addinfos", str_addr);
} else if(info->ai_family == AF_INET6) {
ilogs(control, LOG_DEBUG, "%s is an ipv6 addinfos", str_addr);
} else {
ilogs(control, LOG_DEBUG, "%s is an unknown addinfos format AF=%d", str_addr,
info->ai_family);
freeaddrinfo(info);
return -1;
}
ret = info->ai_family;
freeaddrinfo(info);
return ret;
}
/* parsing of key and val from pure char array */
static bool get_key_val(str * key, str * val, const char * start, char ** eptr)
{
key->s = (void *)start;
val->len = key->len = -1;
val->s = NULL;
*eptr = strpbrk(key->s, " =");
if(!*eptr) {
*eptr = key->s + strlen(key->s);
}
/* for those flags with key=value syntax */
else if(**eptr == '=') {
key->len = *eptr - key->s;
val->s = *eptr + 1;
*eptr = strchr(val->s, ' ');
if(!*eptr)
*eptr = val->s + strlen(val->s);
val->len = *eptr - val->s;
}
if(key->len == -1)
key->len = *eptr - key->s;
if(!key->len)
return false;
return true;
}
static inline int str_eq(const str *p, const char *q)
{
int l = strlen(q);
if(p->len != l)
return 0;
if(memcmp(p->s, q, l))
return 0;
return 1;
}
static inline int str_prefix(const str *p, const char *q, str *out)
{
int l = strlen(q);
if(p->len < l)
return 0;
if(memcmp(p->s, q, l))
return 0;
*out = *p;
out->s += l;
out->len -= l;
return 1;
}
/* handle either "foo-bar" or "foo=bar" from flags */
static int str_key_val_prefix(const str * p, const char * q,
const str * v, str * out)
{
if(str_eq(p, q)) {
if(!v->s || !v->len)
return 0;
*out = *v;
return 1;
}
if(!str_prefix(p, q, out))
return 0;
if(out->len < 2)
return 0;
if(*out->s != '-')
return 0;
out->s++;
out->len--;
return 1;
}
/**
* Work with bencode objects.
*/
/* parse flags, which have their own sub-list */
static bool new_list_to_dict(const char * key_name,
str * key,
str * val,
str * s,
bencode_buffer_t * buf,
bencode_item_t * dict,
bool received_from /* whether received-from is parsed */ )
{
bencode_item_t * item;
int ip_af = AF_UNSPEC;
str ipfamily;
if(str_key_val_prefix(key, key_name, val, s)) {
item = bencode_list(buf);
if (received_from) { /* only for received-from parsing */
ip_af = get_ip_type(s->s);
ipfamily.len = 3;
ipfamily.s = (ip_af == AF_INET) ? "IP4" : "IP6";
bencode_list_add_str(item, &ipfamily);
}
bencode_list_add_str(item, s);
bencode_dictionary_add(dict, key_name, item); /* root dict */
return true;
}
return false;
}
static bool parse_codec_to_dict(str * key, str * val, const char *cmp1, const char *cmp2,
const char * dictstr, bencode_item_t * codec_dict, bencode_item_t * root_dict)
{
str s;
bencode_item_t * dictp;
if(!str_key_val_prefix(key, cmp1, val, &s)) {
if(!cmp2)
return false;
if(!str_key_val_prefix(key, cmp2, val, &s))
return false;
}
dictp = bencode_list(root_dict->buffer);
bencode_dictionary_add(codec_dict, dictstr, dictp);
bencode_list_add_str(dictp, &s);
return true;
}
/* parse codec related flags */
static bool parse_codecs(str * key, str * val, bencode_item_t * codec_dict, bencode_item_t * root_dict)
{
if (parse_codec_to_dict(key, val, "transcode",
"codec-transcode", "transcode", codec_dict, root_dict) ||
parse_codec_to_dict(key, val, "codec-strip",
NULL, "strip", codec_dict, root_dict) ||
parse_codec_to_dict(key, val, "codec-offer",
NULL, "offer", codec_dict, root_dict) ||
parse_codec_to_dict(key, val, "codec-mask",
NULL, "mask", codec_dict, root_dict) ||
parse_codec_to_dict(key, val, "codec-set",
NULL, "set", codec_dict, root_dict) ||
parse_codec_to_dict(key, val, "codec-accept",
NULL, "accept", codec_dict, root_dict) ||
parse_codec_to_dict(key, val, "codec-except",
NULL, "except", codec_dict, root_dict))
{
return true;
}
return false;
}
/* prase transport, such as for example RTP/AVP */
static void parse_transports(unsigned int transport, bencode_item_t * root_dict)
{
const char * val = transports[transport & 0x007];
if (!val)
return;
bencode_dictionary_add(root_dict, "transport-protocol",
bencode_string(bencode_item_buffer(root_dict), val));
}
/* parse repacketize */
static void parse_repacketize(str * val, bencode_item_t * root_dict)
{
int packetize = 0;
while (isdigit(*val->s)) {
packetize *= 10;
packetize += *val->s - '0';
val->s++;
}
if(!packetize)
return;
bencode_dictionary_add_integer(root_dict, "repacketize", packetize);
}
static bool parse_str_flag(str * key, str * val, const char * name,
bencode_item_t * root_dict)
{
if(str_eq(key, name)) {
if (val->s) {
bencode_dictionary_str_add_str(root_dict, key, val);
return true;
}
}
return false;
}
/**
* Parse flags from bencode string into given bencode dictionary.
*
* Params:
* @param rtpp_flags - raw str rtpp_flags
* @param dict - root dict to store encoded flags
*/
void parse_rtpp_flags(const str * rtpp_flags, bencode_item_t * root_dict)
{
char * start, * end, * eptr, c;
str key, val, s;
bencode_item_t * codec, * direction, * flags;
bencode_buffer_t * buf;
unsigned int transport = 0;
if (!rtpp_flags->s)
return;
/* ensure rtpp_flags always null terminated */
c = rtpp_flags->s[rtpp_flags->len];
rtpp_flags->s[rtpp_flags->len] = '\0';
buf = root_dict->buffer;
start = rtpp_flags->s;
end = rtpp_flags->s + rtpp_flags->len;
codec = bencode_dictionary(buf);
direction = bencode_list(buf);
flags = bencode_list(buf);
while (start < end)
{
/* skip spaces */
while(*start == ' ')
start++;
/* set key and val */
if (!get_key_val(&key, &val, start, &eptr))
break;
/* check for items which have their own sub-list */
if (new_list_to_dict("replace", &key, &val, &s, buf, root_dict, false) ||
new_list_to_dict("SDES", &key, &val, &s, buf, root_dict, false) ||
new_list_to_dict("T38", &key, &val, &s, buf, root_dict, false) ||
new_list_to_dict("T.38", &key, &val, &s, buf, root_dict, false) ||
new_list_to_dict("rtcp-mux", &key, &val, &s, buf, root_dict, false) ||
new_list_to_dict("received-from", &key, &val, &s, buf, root_dict, true))
{
goto next;
}
/* codecs have own specific parsing as well */
if (parse_codecs(&key, &val, codec, root_dict))
goto next;
/* parse other generic flags */
switch (key.len)
{
case 3:
/* transport */
if (!val.s && str_eq(&key, "RTP"))
transport = (transport | 0x100) & ~0x001;
else if (!val.s && str_eq(&key, "AVP"))
transport = (transport | 0x100) & ~0x002;
/* TOS */
else if (str_eq(&key, "TOS")) {
if (!val.s)
ilogs(control, LOG_DEBUG, "Error processing flag '"STR_FORMAT"' (will be ignored)", STR_FMT(&key));
else
bencode_dictionary_add_integer(root_dict, "TOS", atoi(val.s));
}
/* other non-defined flags */
else
goto generic;
goto next;
break;
case 4:
/* transport */
if (!val.s && str_eq(&key, "SRTP"))
transport |= 0x101;
else if (!val.s && str_eq(&key, "AVPF"))
transport |= 0x102;
else if (!val.s && str_eq(&key, "DTLS"))
transport |= 0x104;
/* other non-defined flags */
else
goto generic;
goto next;
break;
case 6:
/* to-tag can be overriden, but originally could have been provided already */
if (str_eq(&key, "to-tag")) {
if (!val.s)
ilogs(control, LOG_DEBUG, "Error processing flag '"STR_FORMAT"' (will be ignored)", STR_FMT(&key));
else
bencode_dictionary_add_str(root_dict, "to-tag", &val);
}
else
goto generic;
goto next;
break;
case 7:
/* transport */
if (!val.s && str_eq(&key, "RTP/AVP"))
transport = 0x100;
/* call-id */
else if (str_eq(&key, "call-id")) {
if (!val.s)
ilogs(control, LOG_DEBUG, "Error processing flag '"STR_FORMAT"' (will be ignored)", STR_FMT(&key));
else
bencode_dictionary_add_str(root_dict, "call-id", &val);
}
/* other non-defined flags */
else
goto generic;
goto next;
break;
case 8:
/* transport */
if (!val.s && str_eq(&key, "RTP/AVPF"))
transport = 0x102;
else if (!val.s && str_eq(&key, "RTP/SAVP"))
transport = 0x101;
/* from-tag can be overriden, but originally has to be provided */
else if (str_eq(&key, "from-tag")) {
if (!val.s)
ilogs(control, LOG_DEBUG, "Error processing flag '"STR_FORMAT"' (will be ignored)", STR_FMT(&key));
else
bencode_dictionary_add_str(root_dict, "from-tag", &val);
}
/* direction */
else if (str_eq(&key, "internal") || str_eq(&key, "external"))
bencode_list_add_str(direction, &key);
/* other non-defined flags */
else
goto generic;
goto next;
break;
case 9:
/* transport */
if (!val.s && str_eq(&key, "RTP/SAVPF"))
transport = 0x103;
/* direction */
else if (str_eq(&key, "direction"))
bencode_list_add_str(direction, &key);
else
goto generic;
goto next;
break;
case 10:
/* via-branch can be overriddem here.
* but here it takes only actual value of via branch.
* other things, such as: auto, extra, next etc. are disallowed */
if (str_eq(&key, "via-branch")) {
if (!val.s)
ilogs(control, LOG_DEBUG, "Error processing flag '"STR_FORMAT"' (will be ignored)", STR_FMT(&key));
else
bencode_dictionary_add_str(root_dict, "via-branch", &val);
}
else
goto generic;
goto next;
break;
case 11:
/* repacketize */
if (str_eq(&key, "repacketize")) {
if (!val.s)
ilogs(control, LOG_DEBUG, "Error processing flag '"STR_FORMAT"' (will be ignored)", STR_FMT(&key));
else
parse_repacketize(&val, root_dict);
}
else
goto generic;
goto next;
break;
case 12:
if (str_eq(&key, "delete-delay")) {
if (!val.s)
ilogs(control, LOG_DEBUG, "Error processing flag '"STR_FORMAT"' (will be ignored)", STR_FMT(&key));
else
bencode_dictionary_add_integer(root_dict, "delete delay", atoi(val.s));
} else
goto generic;
goto next;
break;
case 16:
/* transport */
if (!val.s && str_eq(&key, "UDP/TLS/RTP/SAVP"))
transport = 0x104;
else
goto generic;
goto next;
break;
case 17:
/* transport */
if (!val.s && str_eq(&key, "UDP/TLS/RTP/SAVPF"))
transport = 0x106;
else
goto generic;
goto next;
break;
}
generic:
/* generic one key flags */
if (!val.s)
bencode_list_add_str(flags, &key);
/* generic flags with value, but no particular processing */
else
bencode_dictionary_str_add_str(root_dict, &key, &val);
next:
start = eptr;
}
/* define transport */
if (transport)
parse_transports(transport, root_dict);
/* add codecs to the root dict */
if (codec && codec->child)
bencode_dictionary_add(root_dict, "codec", codec);
/* add directions to the root dict */
if (direction && direction->child)
bencode_dictionary_add(root_dict, "direction", direction);
/* add one-key flags to the root dict */
if (flags && flags->child)
bencode_dictionary_add(root_dict, "flags", flags);
rtpp_flags->s[rtpp_flags->len] = c;
}

@ -0,0 +1,17 @@
#ifndef _CONTROL_NG_FLAGS_PARSER_H_
#define _CONTROL_NG_FLAGS_PARSER_H_
#include <string.h>
#include "bencode.h"
#include "obj.h"
#include "str.h"
/**
* Parse flags in raw format and return bencode.
* Syntax:
* rtpp_flags: flag1=<value>, flag2-<value> ...
*/
void parse_rtpp_flags(const str * rtpp_flags, bencode_item_t * dict);
#endif

1
t/.gitignore vendored

@ -19,6 +19,7 @@ call_interfaces.c
cdr.c
codec.c
control_ng.c
control_ng_flags_parser.c
cookie_cache.c
dtls.c
graphite.c

@ -77,7 +77,7 @@ ifeq ($(RTPENGINE_EXTENDED_TESTS),1)
SRCS+= test-amr-decode.c test-amr-encode.c
endif
LIBSRCS+= codeclib.strhash.c resample.c socket.c streambuf.c dtmflib.c poller.c
DAEMONSRCS+= codec.c call.c ice.c kernel.c media_socket.c stun.c bencode.c \
DAEMONSRCS+= control_ng_flags_parser.c codec.c call.c ice.c kernel.c media_socket.c stun.c bencode.c \
dtls.c recording.c statistics.c rtcp.c redis.c iptables.c graphite.c \
cookie_cache.c udp_listener.c homer.c load.c cdr.c dtmf.c timerthread.c \
media_player.c jitter_buffer.c t38.c tcp_listener.c mqtt.c websocket.c cli.c \
@ -213,7 +213,7 @@ aead-aes-crypt: aead-aes-crypt.o $(COMMONOBJS) crypto.o
test-stats: test-stats.o $(COMMONOBJS) codeclib.strhash.o resample.o codec.o ssrc.o call.o ice.o helpers.o \
kernel.o media_socket.o stun.o bencode.o socket.o poller.o dtls.o recording.o statistics.o \
rtcp.o redis.o iptables.o graphite.o call_interfaces.strhash.o sdp.strhash.o rtp.o crypto.o \
control_ng.strhash.o graphite.o \
control_ng_flags_parser.o control_ng.strhash.o graphite.o \
streambuf.o cookie_cache.o udp_listener.o homer.o load.o cdr.o dtmf.o timerthread.o \
media_player.o jitter_buffer.o dtmflib.o t38.o tcp_listener.o mqtt.o janus.strhash.o \
websocket.o cli.o mvr2s_x64_avx2.o mvr2s_x64_avx512.o audio_player.o mix_buffer.o \
@ -222,7 +222,7 @@ test-stats: test-stats.o $(COMMONOBJS) codeclib.strhash.o resample.o codec.o ssr
test-transcode: test-transcode.o $(COMMONOBJS) codeclib.strhash.o resample.o codec.o ssrc.o call.o ice.o helpers.o \
kernel.o media_socket.o stun.o bencode.o socket.o poller.o dtls.o recording.o statistics.o \
rtcp.o redis.o iptables.o graphite.o call_interfaces.strhash.o sdp.strhash.o rtp.o crypto.o \
control_ng.strhash.o \
control_ng_flags_parser.o control_ng.strhash.o \
streambuf.o cookie_cache.o udp_listener.o homer.o load.o cdr.o dtmf.o timerthread.o \
media_player.o jitter_buffer.o dtmflib.o t38.o tcp_listener.o mqtt.o janus.strhash.o websocket.o \
cli.o mvr2s_x64_avx2.o mvr2s_x64_avx512.o audio_player.o mix_buffer.o \

Loading…
Cancel
Save