From 36c19b911194d417b42072623f787039e4dd9308 Mon Sep 17 00:00:00 2001 From: Donat Zenichev Date: Fri, 5 Jan 2024 14:55:14 +0100 Subject: [PATCH] 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 --- daemon/Makefile | 2 +- daemon/call_interfaces.c | 14 + daemon/control_ng.c | 4 + daemon/control_ng_flags_parser.c | 481 ++++++++++++++++++++++++++++++ include/control_ng_flags_parser.h | 17 ++ t/.gitignore | 1 + t/Makefile | 6 +- 7 files changed, 521 insertions(+), 4 deletions(-) create mode 100644 daemon/control_ng_flags_parser.c create mode 100644 include/control_ng_flags_parser.h diff --git a/daemon/Makefile b/daemon/Makefile index 4bde18625..4de366b63 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -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 \ diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index a42097bf5..d446249c5 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.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); diff --git a/daemon/control_ng.c b/daemon/control_ng.c index a7fba21cd..f75cfd54c 100644 --- a/daemon/control_ng.c +++ b/daemon/control_ng.c @@ -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; diff --git a/daemon/control_ng_flags_parser.c b/daemon/control_ng_flags_parser.c new file mode 100644 index 000000000..1af1d3d40 --- /dev/null +++ b/daemon/control_ng_flags_parser.c @@ -0,0 +1,481 @@ +#include "control_ng_flags_parser.h" + +#include +#include +#include +#include + +#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; +} diff --git a/include/control_ng_flags_parser.h b/include/control_ng_flags_parser.h new file mode 100644 index 000000000..fff455c89 --- /dev/null +++ b/include/control_ng_flags_parser.h @@ -0,0 +1,17 @@ +#ifndef _CONTROL_NG_FLAGS_PARSER_H_ +#define _CONTROL_NG_FLAGS_PARSER_H_ + +#include + +#include "bencode.h" +#include "obj.h" +#include "str.h" + +/** + * Parse flags in raw format and return bencode. + * Syntax: + * rtpp_flags: flag1=, flag2- ... + */ +void parse_rtpp_flags(const str * rtpp_flags, bencode_item_t * dict); + +#endif \ No newline at end of file diff --git a/t/.gitignore b/t/.gitignore index 67373fb58..ba39b24d1 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -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 diff --git a/t/Makefile b/t/Makefile index 545b8fa16..810a9c11b 100644 --- a/t/Makefile +++ b/t/Makefile @@ -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 \