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 \