#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 string */ static bool get_key_val(str * key, str * val, str *in_out) { if (!str_token_sep(key, in_out, ' ')) return false; // key=value ? str k; if (!str_token_sep(&k, key, '=')) return true; *val = *key; *key = k; 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 `received-from` */ static bool parse_received_from(str * key, str * val, bencode_buffer_t * buf, sdp_ng_flags * out, enum call_opmode opmode) { bencode_item_t * item; int ip_af = AF_UNSPEC; str ipfamily, s; if(str_key_val_prefix(key, "received-from", val, &s)) { item = bencode_list(buf); 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); call_ng_main_flags(out, &STR_CONST_INIT("received-from"), item, opmode); return true; } return false; } static bool parse_codec_to_dict(str * key, str * val, const char *cmp1, const char *cmp2, const char * dictstr, sdp_ng_flags * out, bencode_buffer_t * buf, enum call_opmode opmode) { 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(buf); bencode_list_add_str(dictp, &s); call_ng_codec_flags(out, &STR_INIT(dictstr), dictp, opmode); return true; } /* parse codec related flags */ static bool parse_codecs(enum call_opmode opmode, sdp_ng_flags * out, bencode_buffer_t * buf, str * key, str * val) { if (parse_codec_to_dict(key, val, "transcode", "codec-transcode", "transcode", out, buf, opmode) || parse_codec_to_dict(key, val, "codec-strip", NULL, "strip", out, buf, opmode) || parse_codec_to_dict(key, val, "codec-offer", NULL, "offer", out, buf, opmode) || parse_codec_to_dict(key, val, "codec-mask", NULL, "mask", out, buf, opmode) || parse_codec_to_dict(key, val, "codec-set", NULL, "set", out, buf, opmode) || parse_codec_to_dict(key, val, "codec-accept", NULL, "accept", out, buf, opmode) || parse_codec_to_dict(key, val, "codec-except", NULL, "except", out, buf, opmode)) { return true; } return false; } /* prase transport, such as for example RTP/AVP */ static void parse_transports(sdp_ng_flags *out, bencode_buffer_t *buf, enum call_opmode opmode, unsigned int transport) { const char * val = transports[transport & 0x007]; if (!val) return; call_ng_main_flags(out, &STR_CONST_INIT("transport-protocol"), bencode_string(buf, val), opmode); } #if 0 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; } #endif /** * 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_buffer_t * buf, enum call_opmode opmode, sdp_ng_flags * out) { str remainder, key, val; bencode_item_t * direction; unsigned int transport = 0; if (!rtpp_flags->s) return; remainder = *rtpp_flags; direction = bencode_list(buf); while (remainder.len) { /* skip spaces */ while (remainder.len && remainder.s[0] == ' ') str_shift(&remainder, 1); /* set key and val */ if (!get_key_val(&key, &val, &remainder)) break; /* specific received-from parsing */ if (parse_received_from(&key, &val, buf, out, opmode)) goto next; /* codecs have own specific parsing as well */ if (parse_codecs(opmode, out, buf, &key, &val)) 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; /* 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 7: /* transport */ if (!val.s && str_eq(&key, "RTP/AVP")) transport = 0x100; /* 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 (val.s && str_eq(&key, "from-tag")) { out->directional = 1; /* explicitly add directional for this case */ goto generic; } /* 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, &val); else goto generic; goto next; break; case 12: if (val.s && str_eq(&key, "delete-delay")) { call_ng_main_flags(out, &key, bencode_integer(buf, atoi(val.s)), opmode); } 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) call_ng_flags_flags(out, &key, NULL); /* generic flags with value, but no particular processing */ else call_ng_main_flags(out, &key, bencode_str(buf, &val), opmode); next:; } /* define transport */ if (transport) parse_transports(out, buf, opmode, transport); /* add directions to the root dict */ if (direction && direction->child) call_ng_direction_flag(out, direction); }