From 8e207e02e41d3d0b2bdfa33018f2740cb5fae0ed Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 10 Dec 2019 12:45:32 +0200 Subject: [PATCH 01/50] Add RTPE owner address to be stored into the call redis storage This will contains the IP address RTPEngine advertised for this call. --- daemon/redis.c | 3 +++ include/call.h | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/daemon/redis.c b/daemon/redis.c index be496325bc..58fe0f1cc4 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1596,6 +1596,8 @@ static void json_restore_call(struct redis *r, const str *callid, enum call_type c->created_from = call_strdup(c, id.s); if (!redis_hash_get_str(&id, &call, "created_from_addr")) sockaddr_parse_any_str(&c->created_from_addr, &id); + if (!redis_hash_get_str(&id, &call, "rtpe_connection_addr")) + call_str_cpy(c, &c->rtpe_connection_addr, &id); if (!redis_hash_get_int(&i, &call, "block_dtmf")) c->block_dtmf = i ? 1 : 0; if (!redis_hash_get_int(&i, &call, "block_media")) @@ -1879,6 +1881,7 @@ char* redis_encode_json(struct call *c) { JSON_SET_SIMPLE("ml_deleted","%ld",(long int) c->ml_deleted); JSON_SET_SIMPLE_CSTR("created_from",c->created_from); JSON_SET_SIMPLE_CSTR("created_from_addr",sockaddr_print_buf(&c->created_from_addr)); + JSON_SET_SIMPLE_STR("rtpe_connection_addr", &c->rtpe_connection_addr); JSON_SET_SIMPLE("redis_hosted_db","%u",c->redis_hosted_db); JSON_SET_SIMPLE_STR("recording_metadata",&c->metadata); JSON_SET_SIMPLE("block_dtmf","%i",c->block_dtmf ? 1 : 0); diff --git a/include/call.h b/include/call.h index 022ed83916..2dace93472 100644 --- a/include/call.h +++ b/include/call.h @@ -383,7 +383,8 @@ struct call { char *created_from; sockaddr_t created_from_addr; sockaddr_t xmlrpc_callback; - + str rtpe_connection_addr; + unsigned int redis_hosted_db; unsigned int foreign_call; // created_via_redis_notify call From 3adf514929e2e34b9516cd02e1a36809b6851ab8 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 10 Dec 2019 15:48:45 +0200 Subject: [PATCH 02/50] refactor network address formatting to a standalone function so it can be reused --- daemon/call_interfaces.c | 10 ++++++++++ daemon/sdp.c | 13 ++++--------- include/call_interfaces.h | 1 + 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 92ef5d3aa3..818dd442ac 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1945,3 +1945,13 @@ int call_interfaces_init() { return 0; } + + +void format_network_address(str* o, struct packet_stream *ps, struct sdp_ng_flags *flags, int keep_unspec) { + if (!is_addr_unspecified(&flags->parsed_media_address)) + o->len = sprintf(o->s, "%s %s", + flags->parsed_media_address.family->rfc_name, + sockaddr_print_buf(&flags->parsed_media_address)); + else + call_stream_address46(o->s, ps, SAF_NG, &o->len, NULL, keep_unspec); +} diff --git a/daemon/sdp.c b/daemon/sdp.c index 00fa92e98f..acace8ac48 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -1571,7 +1571,7 @@ static int replace_network_address(struct sdp_chopper *chop, struct network_addr struct packet_stream *ps, struct sdp_ng_flags *flags, int keep_unspec) { char buf[64]; - int len; + str res = { buf, 0 }; struct packet_stream *sink = packet_stream_sink(ps); if (is_addr_unspecified(&address->parsed) @@ -1583,14 +1583,9 @@ static int replace_network_address(struct sdp_chopper *chop, struct network_addr if (flags->media_address.s && is_addr_unspecified(&flags->parsed_media_address)) __parse_address(&flags->parsed_media_address, NULL, NULL, &flags->media_address); - - if (!is_addr_unspecified(&flags->parsed_media_address)) - len = sprintf(buf, "%s %s", - flags->parsed_media_address.family->rfc_name, - sockaddr_print_buf(&flags->parsed_media_address)); - else - call_stream_address46(buf, ps, SAF_NG, &len, NULL, keep_unspec); - chopper_append(chop, buf, len); + + format_network_address(&res, ps, flags, keep_unspec); + chopper_append(chop, res.s, res.len); if (skip_over(chop, &address->address)) return -1; diff --git a/include/call_interfaces.h b/include/call_interfaces.h index f225e7450f..2b81797eb8 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -124,5 +124,6 @@ void ng_call_stats(struct call *call, const str *fromtag, const str *totag, benc int call_interfaces_init(void); +void format_network_address(str* o, struct packet_stream *ps, struct sdp_ng_flags *flags, int keep_unspec); #endif From 9328f025647aafd595b844a5036ef236b1a2f344 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 10 Dec 2019 16:28:01 +0200 Subject: [PATCH 03/50] populate rtpe_connection_addr with our address during offer processing --- daemon/call.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/daemon/call.c b/daemon/call.c index a21fc84c63..e0a1760bec 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2018,6 +2018,11 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, ice_update(media->ice_agent, NULL); /* this is in case rtcp-mux has changed */ recording_setup_media(media); + + if (!call->rtpe_connection_addr.len) { + call->rtpe_connection_addr.s = call_malloc(call, 64); + format_network_address(&call->rtpe_connection_addr, media->streams.head->data, flags, 0); + } } return 0; From 5f62bfd58c245f5d90fe7f9d2d3b65e335ce79ea Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Wed, 11 Dec 2019 11:40:20 +0200 Subject: [PATCH 04/50] for foreign calls use owner rtpengine connection address for network address instead of generating an address from a the static configuration --- daemon/call_interfaces.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 818dd442ac..58e25b7edc 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1952,6 +1952,8 @@ void format_network_address(str* o, struct packet_stream *ps, struct sdp_ng_flag o->len = sprintf(o->s, "%s %s", flags->parsed_media_address.family->rfc_name, sockaddr_print_buf(&flags->parsed_media_address)); + else if (IS_FOREIGN_CALL(ps->call) && ps->call->rtpe_connection_addr.len) + o->len = sprintf(o->s, "%s", ps->call->rtpe_connection_addr.s); else call_stream_address46(o->s, ps, SAF_NG, &o->len, NULL, keep_unspec); } From 1960acd309119305ed0c6157e2f55f7e9c9ba16e Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Mon, 6 Jan 2020 22:40:41 +0200 Subject: [PATCH 05/50] store rtpe call owner address per media instead of per-call So each media can have different network interfaces --- daemon/call.c | 19 ++++++++++++++----- daemon/call_interfaces.c | 4 ++-- daemon/redis.c | 9 +++++---- include/call.h | 2 +- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index e0a1760bec..742c8c13a0 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -1796,6 +1796,18 @@ static void __update_media_id(struct call_media *media, struct call_media *other } } +static void __update_rtpe_address(struct call_media* media, struct sdp_ng_flags *flags) { + struct packet_stream *ps; + + if (media->rtpe_connection_addr.len || !media->streams.head) + return; + + ps = media->streams.head->data; + media->rtpe_connection_addr.s = call_malloc(media->call, 64); + format_network_address(&media->rtpe_connection_addr, ps, flags, 0); + rlog(LOG_INFO, "Stored media address %s",media->rtpe_connection_addr.s); +} + /* called with call->master_lock held in W */ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, struct sdp_ng_flags *flags) @@ -2018,11 +2030,8 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, ice_update(media->ice_agent, NULL); /* this is in case rtcp-mux has changed */ recording_setup_media(media); - - if (!call->rtpe_connection_addr.len) { - call->rtpe_connection_addr.s = call_malloc(call, 64); - format_network_address(&call->rtpe_connection_addr, media->streams.head->data, flags, 0); - } + __update_rtpe_address(media, flags); + __update_rtpe_address(other_media, flags); } return 0; diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 58e25b7edc..77b5266a22 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1952,8 +1952,8 @@ void format_network_address(str* o, struct packet_stream *ps, struct sdp_ng_flag o->len = sprintf(o->s, "%s %s", flags->parsed_media_address.family->rfc_name, sockaddr_print_buf(&flags->parsed_media_address)); - else if (IS_FOREIGN_CALL(ps->call) && ps->call->rtpe_connection_addr.len) - o->len = sprintf(o->s, "%s", ps->call->rtpe_connection_addr.s); + else if (IS_FOREIGN_CALL(ps->call) && ps->media && ps->media->rtpe_connection_addr.len) + o->len = sprintf(o->s, "%s", ps->media->rtpe_connection_addr.s); else call_stream_address46(o->s, ps, SAF_NG, &o->len, NULL, keep_unspec); } diff --git a/daemon/redis.c b/daemon/redis.c index 58fe0f1cc4..50e213f219 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1302,6 +1302,9 @@ static int json_medias(struct call *c, struct redis_list *medias, JsonReader *ro "media_flags")) return -1; + if (!redis_hash_get_str(&s, rh, "rtpe_addr")) + call_str_cpy(c, &med->rtpe_connection_addr, &s); + if (redis_hash_get_sdes_params(&med->sdes_in, rh, "sdes_in") < 0) return -1; if (redis_hash_get_sdes_params(&med->sdes_out, rh, "sdes_out") < 0) @@ -1596,8 +1599,6 @@ static void json_restore_call(struct redis *r, const str *callid, enum call_type c->created_from = call_strdup(c, id.s); if (!redis_hash_get_str(&id, &call, "created_from_addr")) sockaddr_parse_any_str(&c->created_from_addr, &id); - if (!redis_hash_get_str(&id, &call, "rtpe_connection_addr")) - call_str_cpy(c, &c->rtpe_connection_addr, &id); if (!redis_hash_get_int(&i, &call, "block_dtmf")) c->block_dtmf = i ? 1 : 0; if (!redis_hash_get_int(&i, &call, "block_media")) @@ -1881,7 +1882,6 @@ char* redis_encode_json(struct call *c) { JSON_SET_SIMPLE("ml_deleted","%ld",(long int) c->ml_deleted); JSON_SET_SIMPLE_CSTR("created_from",c->created_from); JSON_SET_SIMPLE_CSTR("created_from_addr",sockaddr_print_buf(&c->created_from_addr)); - JSON_SET_SIMPLE_STR("rtpe_connection_addr", &c->rtpe_connection_addr); JSON_SET_SIMPLE("redis_hosted_db","%u",c->redis_hosted_db); JSON_SET_SIMPLE_STR("recording_metadata",&c->metadata); JSON_SET_SIMPLE("block_dtmf","%i",c->block_dtmf ? 1 : 0); @@ -2043,7 +2043,8 @@ char* redis_encode_json(struct call *c) { JSON_SET_SIMPLE_STR("logical_intf",&media->logical_intf->name); JSON_SET_SIMPLE("ptime","%i",media->ptime); JSON_SET_SIMPLE("media_flags","%u",media->media_flags); - + JSON_SET_SIMPLE_STR("rtpe_addr", &media->rtpe_connection_addr); + json_update_sdes_params(builder, "media", media->unique_id, "sdes_in", &media->sdes_in); json_update_sdes_params(builder, "media", media->unique_id, "sdes_out", diff --git a/include/call.h b/include/call.h index 2dace93472..dda3170cfd 100644 --- a/include/call.h +++ b/include/call.h @@ -327,6 +327,7 @@ struct call_media { int ptime; // either from SDP or overridden volatile unsigned int media_flags; + str rtpe_connection_addr; }; /* half a dialogue */ @@ -383,7 +384,6 @@ struct call { char *created_from; sockaddr_t created_from_addr; sockaddr_t xmlrpc_callback; - str rtpe_connection_addr; unsigned int redis_hosted_db; unsigned int foreign_call; // created_via_redis_notify call From cac1f5ffdd342c0e6d5219bab2acdcedee648309 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Mon, 6 Jan 2020 22:47:27 +0200 Subject: [PATCH 06/50] socket_t now also models "foreign ports" ports owned by another server are not bound locally, we just know what they are --- daemon/media_socket.c | 20 ++++++++++++-------- daemon/redis.c | 23 +++++++++++++++-------- lib/socket.c | 22 ++++++++++++++++++++++ lib/socket.h | 2 ++ 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/daemon/media_socket.c b/daemon/media_socket.c index 0d786baadf..57c25f8c75 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -754,7 +754,9 @@ static void release_port(socket_t *r, struct intf_spec *spec) { iptables_del_rule(r); - if (close_socket(r) == 0) { + if (r->is_foreign) { + __C_DBG("port %u is foreign so release is not needed"); + } else if (close_socket(r) == 0) { __C_DBG("port %u is released", port); bit_array_clear(pp->ports_used, port); g_atomic_int_inc(&pp->free_ports); @@ -1969,14 +1971,16 @@ struct stream_fd *stream_fd_new(socket_t *fd, struct call *call, const struct lo __C_DBG("stream_fd_new localport=%d", sfd->socket.local.port); - ZERO(pi); - pi.fd = sfd->socket.fd; - pi.obj = &sfd->obj; - pi.readable = stream_fd_readable; - pi.closed = stream_fd_closed; + if (!sfd->socket.is_foreign) { + ZERO(pi); + pi.fd = sfd->socket.fd; + pi.obj = &sfd->obj; + pi.readable = stream_fd_readable; + pi.closed = stream_fd_closed; - if (poller_add_item(rtpe_poller, &pi)) - ilog(LOG_ERR, "Failed to add stream_fd to poller"); + if (poller_add_item(rtpe_poller, &pi)) + ilog(LOG_ERR, "Failed to add stream_fd to poller"); + } return sfd; } diff --git a/daemon/redis.c b/daemon/redis.c index 50e213f219..3d19bf742d 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1154,14 +1154,21 @@ static int redis_sfds(struct call *c, struct redis_list *sfds) { if (!loc) goto err; - err = "failed to open ports"; - if (__get_consecutive_ports(&q, 1, port, loc->spec, &c->callid)) - goto err; - err = "no port returned"; - sock = g_queue_pop_head(&q); - if (!sock) - goto err; - set_tos(sock, c->tos); + if (IS_FOREIGN_CALL(c)) { + sock = g_slice_alloc0(sizeof(*sock)); + err = "failed to register foreign port"; + if (create_foreign_socket(sock, SOCK_DGRAM, port, &loc->spec->local_address.addr)) + goto err; + } else { + err = "failed to open ports"; + if (__get_consecutive_ports(&q, 1, port, loc->spec, &c->callid)) + goto err; + err = "no port returned"; + sock = g_queue_pop_head(&q); + if (!sock) + goto err; + set_tos(sock, c->tos); + } sfd = stream_fd_new(sock, c, loc); sfds->ptrs[i] = sfd; diff --git a/lib/socket.c b/lib/socket.c index 7d4aa23ade..8401987cc6 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -673,6 +673,28 @@ int open_socket(socket_t *r, int type, unsigned int port, const sockaddr_t *sa) return -1; } +int create_foreign_socket(socket_t *r, int type, unsigned int port, const sockaddr_t *sa) { + ZERO(*r); + r->is_foreign = 1; + r->family = sa->family; + r->fd = -1; // Make sure no one tries to close a real fd + + if (port > 0xffff) { + __C_DBG("create foreign socket fail, port=%d > 0xfffffd", port); + goto fail; + } + + r->local.port = port; + r->local.address = *sa; + + __C_DBG("create foreign socket success, port=%d", port); + + return 0; + + fail: + return -1; +} + int connect_socket(socket_t *r, int type, const endpoint_t *ep) { sockfamily_t *fam; diff --git a/lib/socket.h b/lib/socket.h index e33e238b90..7de07e5c11 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -96,6 +96,7 @@ struct socket { sockfamily_t *family; endpoint_t local; endpoint_t remote; + int is_foreign; }; @@ -202,6 +203,7 @@ int connect_socket(socket_t *r, int type, const endpoint_t *ep); int connect_socket_nb(socket_t *r, int type, const endpoint_t *ep); // 1 == in progress int connect_socket_retry(socket_t *r); // retries connect() while in progress int close_socket(socket_t *r); +int create_foreign_socket(socket_t *r, int type, unsigned int port, const sockaddr_t * sa); sockfamily_t *get_socket_family_rfc(const str *s); sockfamily_t *__get_socket_family_enum(enum socket_families); From c08a065f0563185237f39b8d73735496701d1f50 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 7 Jan 2020 18:07:35 +0200 Subject: [PATCH 07/50] update primary with endpoint new details added by alternates Currently only missing endpoint addresses, but I need more --- daemon/redis.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 3d19bf742d..302a3375a9 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -81,6 +81,7 @@ static int redisCommandNR(redisContext *r, const char *fmt, ...) static int redis_check_conn(struct redis *r); static void json_restore_call(struct redis *r, const str *id, enum call_type type); +static void redis_update_call_details(struct redis *r, struct call *call); static int redis_connect(struct redis *r, int wait); static void redis_pipe(struct redis *r, const char *fmt, ...) { @@ -365,8 +366,9 @@ void on_redis_notification(redisAsyncContext *actx, void *reply, void *privdata) if (IS_FOREIGN_CALL(c)) call_destroy(c); else { - rlog(LOG_WARN, "Redis-Notifier: Ignoring SET received for OWN call: %s\n", rr->element[2]->str); - goto err; + rlog(LOG_WARN, "Trying to read new endpoints from redis"); + redis_update_call_details(r, c); + goto err; // this no longer an error, but we'll still go there to bypass json_restore_call } } json_restore_call(r, &callid, CT_FOREIGN_CALL); @@ -1696,6 +1698,88 @@ static void json_restore_call(struct redis *r, const str *callid, enum call_type obj_put(c); } +static void redis_update_call_details(struct redis *r, struct call *c) { + redisReply* rr_jsonStr; + struct redis_list streams; + struct redis_hash call, streamrh; + struct endpoint endpoint, advertised_endpoint; + unsigned ps_flags; + GList *pk; + struct packet_stream *ps; + + const char *err = 0; + int i, updated = 0; + JsonReader *root_reader =0; + JsonParser *parser =0; + + rr_jsonStr = redis_get(r, REDIS_REPLY_STRING, "GET " PB, STR(&c->callid)); + err = "could not retrieve JSON data from redis"; + if (!rr_jsonStr) + goto err1; + + parser = json_parser_new(); + err = "could not parse JSON data"; + if (!json_parser_load_from_data (parser, rr_jsonStr->str, -1, NULL)) + goto err1; + root_reader = json_reader_new (json_parser_get_root (parser)); + err = "could not read JSON data"; + if (!root_reader) + goto err1; + + if (json_get_hash(&call, "json", -1, root_reader)) + goto err1; + + err = "'streams' incomplete"; + if (json_get_list_hash(&streams, "stream", &call, "num_streams", root_reader)) + goto err1; + + // review call streams and only update where needed + for (i = 0, pk = c->streams.head; pk && (i < streams.len); pk = pk->next, i++) { + streamrh = streams.rh[i]; + ps = pk->data; + + ZERO(endpoint); + err = "could not read stream flags"; + if (redis_hash_get_unsigned((unsigned int *) &ps_flags, &streamrh, "ps_flags")) + return err2; + err = "could not read stream endpoint"; + if (redis_hash_get_endpoint(&endpoint, &streamrh, "endpoint")) + goto err2; + err = "could not read stream advertised_endpoint"; + if (redis_hash_get_endpoint(&advertised_endpoint, &streamrh, "advertised_endpoint")) + goto err2; + + if (!ps->endpoint.port && endpoint.port && endpoint.address.family->af) { + ps->endpoint = endpoint; + ps->advertised_endpoint = advertised_endpoint; + ps->ps_flags = ps_flags; + updated = 1; + } + } + + if (updated) + rlog(LOG_INFO, "Updated stream endpoints from Redis"); + + updated = 0; + + err = NULL; +err2: + json_destroy_list(&streams); +err1: + if (root_reader) + g_object_unref (root_reader); + if (parser) + g_object_unref (parser); + if (rr_jsonStr) + freeReplyObject(rr_jsonStr); + log_info_clear(); + if (err) { + rlog(LOG_WARNING, "Failed to update endpoints for call ID '" STR_FORMAT_M "' from Redis: %s", + STR_FMT_M(&c->callid), + err); + } +} + struct thread_ctx { GQueue r_q; mutex_t r_m; From a28662a19505d0df6f864a11e8b2708297ea593b Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Wed, 8 Jan 2020 19:49:51 +0200 Subject: [PATCH 08/50] giving up on using the redis.c JSON parsing logic - so Im rolling my own, saner logic with separation of data, policy and mechanism --- daemon/redis.c | 593 ++++++++++++++++++++++++++++++++++++++++++++++++ include/redis.h | 81 ++++++- 2 files changed, 669 insertions(+), 5 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 302a3375a9..bbfb5ce27a 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -84,6 +84,11 @@ static void json_restore_call(struct redis *r, const str *id, enum call_type typ static void redis_update_call_details(struct redis *r, struct call *call); static int redis_connect(struct redis *r, int wait); +/* New mode JSON parsing support. Use with care */ +static redis_call_t* redis_call_create(const str*, JsonNode*); +static void redis_call_free(void*); +/* End of new mode JSON parsing support */ + static void redis_pipe(struct redis *r, const char *fmt, ...) { va_list ap; @@ -2370,3 +2375,591 @@ void redis_wipe(struct redis *r) { redisCommandNR(r->ctx, "DEL calls"); mutex_unlock(&r->lock); } + +/* New mode JSON parsing support. Use with care */ + +static str* str_dup_charptr(const char *c) { + str temp = { .s = (char*)c, .len = strlen(c) }; + return str_dup(&temp); +} + +static str* json_reader_get_str(JsonReader *reader, const char *key) { + const gchar *strval; + str *out = NULL; + json_reader_read_member(reader, key); + strval = json_reader_get_string_value(reader); + json_reader_end_member(reader); + if (strval) + out = str_dup_charptr(strval); + return out; +} + +/* +static str* json_reader_get_str_element(JsonReader *reader, unsigned idx) { + const gchar *strval = NULL; + str *out = NULL; + json_reader_read_element(reader, idx); + strval = json_reader_get_string_value(reader); + json_reader_end_element(reader); + if (strval) + out = str_dup_charptr(strval); + return out; +} +*/ + +static long long json_reader_get_ll_element(JsonReader *reader, unsigned idx) { + const gchar *strval; + JsonNode *node; + long long out = -1; + if (json_reader_read_element(reader, idx)) { + node = json_reader_get_value(reader); + if (json_node_get_value_type(node) == G_TYPE_STRING) { + strval = json_node_get_string(node); + if (strval) + out = strtoll(strval, NULL, 10); + } else { + out = json_reader_get_int_value(reader); + } + } + json_reader_end_element(reader); + return out; +} + +static void redis_call_media_stream_fd_free(void *rcmsf) { + redis_call_media_stream_fd_t *streamfdref = rcmsf; + if (!streamfdref) + return; + if (streamfdref->pref_family) + free(streamfdref->pref_family); + if (streamfdref->logical_intf) + free(streamfdref->logical_intf); +} + +static redis_call_media_stream_fd_t *redis_call_media_stream_fd_create(unsigned unique_id, JsonNode *json) { + redis_call_media_stream_fd_t *streamfdref = NULL; + JsonReader *reader = NULL; + + long long llval = 0; + + reader = json_reader_new(json); + streamfdref = obj_alloc0("redis_call_media_stream_fd", sizeof(*streamfdref), redis_call_media_stream_fd_free); + streamfdref->unique_id = unique_id; + if ((llval = json_reader_get_ll(reader, "stream")) >= 0) + streamfdref->stream_unique_id = llval; + else + goto fail; + streamfdref->pref_family = json_reader_get_str(reader, "pref_family"); + if ((llval = json_reader_get_ll(reader, "localport")) >= 0) + streamfdref->localport = llval; + streamfdref->logical_intf = json_reader_get_str(reader, "logical_intf"); + if ((llval = json_reader_get_ll(reader, "local_intf_uid")) >= 0) + streamfdref->logical_intf_uid = llval; + + goto done; + +fail: + if (streamfdref) { + obj_put(streamfdref); + streamfdref = NULL; + } + +done: + if (reader) + g_object_unref(reader); + return streamfdref; +} + +static void redis_call_media_stream_free(void *rcms) { + redis_call_media_stream_t *streamref = rcms; + if (!streamref) + return; + if (streamref->endpoint) + free(streamref->endpoint); + if (streamref->advertised_endpoint) + free(streamref->advertised_endpoint); + if (streamref->fds) + g_queue_free_full(streamref->fds, __obj_put); +} + +static redis_call_media_stream_t *redis_call_media_stream_create(unsigned unique_id, JsonNode *json, GQueue *sfds) { + redis_call_media_stream_t *streamref = NULL; + redis_call_media_stream_fd_t *streamfdref; + JsonReader *reader = NULL; + + long long llval = 0; + unsigned idx; + + reader = json_reader_new(json); + streamref = obj_alloc0("redis_call_media_stream", sizeof(*streamref), redis_call_media_stream_free); + streamref->unique_id = unique_id; + if ((llval = json_reader_get_ll(reader, "media")) >= 0) + streamref->media_unique_id = llval; + else + goto fail; + if ((llval = json_reader_get_ll(reader, "sfd")) >= 0) + streamref->selected_sfd = llval; + if ((llval = json_reader_get_ll(reader, "rtp_sink")) >= 0) + streamref->rtp_sink = llval < 1000 ? llval : -1; /* old code writes -1 as u_int32_t */ + if ((llval = json_reader_get_ll(reader, "rtcp_sink")) >= 0) + streamref->rtcp_sink = llval < 1000 ? llval : -1; /* old code writes -1 as u_int32_t */ + if ((llval = json_reader_get_ll(reader, "rtcp_sibling")) >= 0) + streamref->rtcp_sibling = llval < 1000 ? llval : -1; /* old code writes -1 as u_int32_t */ + if ((llval = json_reader_get_ll(reader, "last_packet")) >= 0) + streamref->last_packet = llval; + if ((llval = json_reader_get_ll(reader, "ps_flags")) >= 0) + streamref->ps_flags = llval; + if ((llval = json_reader_get_ll(reader, "component")) >= 0) + streamref->component = llval; + streamref->endpoint = json_reader_get_str(reader, "endpoint"); + streamref->advertised_endpoint = json_reader_get_str(reader, "advertised_endpoint"); + if ((llval = json_reader_get_ll(reader, "stats-packets")) >= 0) + streamref->stats_packets = llval; + if ((llval = json_reader_get_ll(reader, "stats-bytes")) >= 0) + streamref->stats_bytes = llval; + if ((llval = json_reader_get_ll(reader, "stats-errors")) >= 0) + streamref->stats_errors = llval; + + /* grab my fds */ + streamref->fds = g_queue_new(); + for (idx = 0; idx < g_queue_get_length(sfds); idx++) { + streamfdref = g_queue_peek_nth(sfds, idx); + if (streamfdref && streamfdref->stream_unique_id == streamref->unique_id) + g_queue_push_tail(streamref->fds, g_queue_pop_nth(sfds, idx)); + } + + goto done; + +fail: + if (streamref) { + obj_put(streamref); + streamref = NULL; + } + +done: + if (reader) + g_object_unref(reader); + return streamref; +} + +static void redis_call_media_tag_free(void *rcmt) { + redis_call_media_tag_t *tagref = rcmt; + if (!tagref) + return; + if (tagref->tag) + free(tagref->tag); + /* we don't free `other_tag` - it should be owned by some media */ +} + +static redis_call_media_tag_t *redis_call_media_tag_create(unsigned unique_id, JsonNode *json) { + redis_call_media_tag_t *tagref = NULL; + JsonReader *reader = NULL; + + long long llval = 0; + + reader = json_reader_new(json); + tagref = obj_alloc0("redis_call_media_tag", sizeof(*tagref), redis_call_media_tag_free); + tagref->unique_id = unique_id; + if ((llval = json_reader_get_ll(reader, "created")) >= 0) + tagref->created = llval; + else + goto fail; + if ((llval = json_reader_get_ll(reader, "active")) >= 0) + tagref->active = llval ? TRUE : FALSE; + if ((llval = json_reader_get_ll(reader, "deleted")) >= 0) + tagref->deleted = llval ? TRUE : FALSE; + if ((llval = json_reader_get_ll(reader, "block_dtmf")) >= 0) + tagref->block_dtmf = llval ? TRUE : FALSE; + if ((llval = json_reader_get_ll(reader, "block_media")) >= 0) + tagref->block_media = llval ? TRUE : FALSE; + tagref->tag = json_reader_get_str(reader, "tag"); + + goto done; + +fail: + if (tagref) { + obj_put(tagref); + tagref = NULL; + } + +done: + if (reader) + g_object_unref(reader); + return tagref; +} + +static void redis_call_media_free(void* rcm) { + redis_call_media_t *mediaref = rcm; + if (!mediaref) + return; + if (mediaref->type) + free(mediaref->type); + if (mediaref->protocol) + free(mediaref->protocol); + if (mediaref->desired_family) + free(mediaref->desired_family); + if (mediaref->logical_intf) + free(mediaref->logical_intf); + if (mediaref->rtpe_addr) + free(mediaref->rtpe_addr); + if (mediaref->tag) + obj_put(mediaref->tag); + if (mediaref->streams) + g_queue_free_full(mediaref->streams, __obj_put); +} + +static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode *json, GQueue *tags, GQueue *streams) { + redis_call_media_t *mediaref = NULL; + redis_call_media_tag_t *tagref = NULL; + redis_call_media_stream_t *streamref = NULL; + JsonReader *reader = NULL; + + long long llval = 0; + unsigned idx; + + reader = json_reader_new(json); + mediaref = obj_alloc0("redis_call_media", sizeof(*mediaref), redis_call_media_free); + mediaref->unique_id = unique_id; + if ((llval = json_reader_get_ll(reader, "tag")) >= 0) { + tagref = g_queue_pop_nth(tags, llval); + if (!tagref) + goto fail; + mediaref->tag = tagref; + } + if ((llval = json_reader_get_ll(reader, "index")) >= 0) + mediaref->index = llval; + mediaref->type = json_reader_get_str(reader, "type"); + mediaref->protocol = json_reader_get_str(reader, "protocol"); + mediaref->desired_family = json_reader_get_str(reader, "desired_family"); + mediaref->logical_intf = json_reader_get_str(reader, "logical_intf"); + if ((llval = json_reader_get_ll(reader, "ptime")) >= 0) + mediaref->ptime = llval; + if ((llval = json_reader_get_ll(reader, "media_flags")) >= 0) + mediaref->media_flags = llval; + mediaref->rtpe_addr = json_reader_get_str(reader, "rtpe_addr"); + + /* grab my streams */ + mediaref->streams = g_queue_new(); + for (idx = 0; idx < g_queue_get_length(streams); idx++) { + streamref = g_queue_peek_nth(streams, idx); + if (streamref && streamref->media_unique_id == mediaref->unique_id) + g_queue_push_tail(mediaref->streams, g_queue_pop_nth(streams, idx)); + } + + goto done; + +fail: + if (mediaref) { + obj_put(mediaref); + mediaref = NULL; + } + +done: + if (reader) + g_object_unref(reader); + return mediaref; +} + +static void redis_call_free(void* rc) { + redis_call_t *callref = rc; + if (!callref) + return; + if (callref->media) + g_queue_free_full(callref->media, __obj_put); + if (callref->call_id) + free(callref->call_id); + if (callref->created_from) + free(callref->created_from); + if (callref->created_from_addr) + free(callref->created_from_addr); + if (callref->recording_metadata) + free(callref->recording_metadata); +} + +static redis_call_t* redis_call_create_from_metadata(const str* callid, JsonNode* json) { + redis_call_t *callref = NULL; + JsonReader *reader = NULL; + + long long llval = 0; + + reader = json_reader_new(json); + callref = obj_alloc0("redis_call", sizeof(*callref), redis_call_free); + callref->call_id = str_dup(callid); + if ((llval = json_reader_get_ll(reader, "created")) >= 0) + callref->created = llval; + else + goto fail; + if ((llval = json_reader_get_ll(reader, "last_signal")) >= 0) + callref->last_signal = llval; + if ((llval = json_reader_get_ll(reader, "deleted")) >= 0) + callref->deleted = llval ? TRUE : FALSE; + if ((llval = json_reader_get_ll(reader, "ml_deleted")) >= 0) + callref->ml_deleted = llval ? TRUE : FALSE; + callref->created_from = json_reader_get_str(reader, "created_from"); + callref->created_from_addr = json_reader_get_str(reader, "created_from_addr"); + json_reader_end_member(reader); + if ((llval = json_reader_get_ll(reader, "redis_hosted_db")) >= 0) + callref->redis_hosted_db = llval; + if ((llval = json_reader_get_ll(reader, "block_dtmf")) >= 0) + callref->block_dtmf = llval ? TRUE : FALSE; + if ((llval = json_reader_get_ll(reader, "block_media")) >= 0) + callref->block_media = llval ? TRUE : FALSE; + + goto done; + +fail: + if (callref) { + obj_put(callref); + callref = NULL; + } + +done: + if (reader) + g_object_unref(reader); + return callref; +} + +static int redis_call_match_tags(redis_call_media_tag_t *tag, GQueue *call_tags, JsonNode* json) { + JsonReader *reader; + redis_call_media_tag_t *other_tag; + int status = 1; + unsigned num_others, other_idx, other_tagid; + + reader = json_reader_new(json); + if (!json_reader_is_array(reader)) + goto fail; + + num_others = json_reader_count_elements(reader); + if (num_others < 0) + goto fail; + + for (other_idx = 0; other_idx < num_others; other_idx++) { + other_tagid = json_reader_get_ll_element(reader, other_idx); + if (other_tagid < 0) + goto fail; + other_tag = g_queue_peek_nth(call_tags, other_tagid); + if (!other_tag) + goto fail; + tag->other_tag = other_tag; + other_tag->other_tag = tag; + } + + goto done; + +fail: + status = 0; + +done: + if (reader) + g_object_unref(reader); + return status; +} + +static GQueue *redis_call_read_tags(JsonReader *reader) { + GQueue *call_tags = NULL; + unsigned tag_idx, other_tags_idx; + str *tag_field = NULL; + redis_call_media_tag_t *tag = NULL; + + call_tags = g_queue_new(); + for (tag_idx = 0; ; tag_idx++) { + tag_field = str_sprintf("tag-%u", tag_idx); + if (!json_reader_read_member(reader, tag_field->s)) { + break; /* no more tags */ + } + tag = redis_call_media_tag_create(tag_idx, json_reader_get_value(reader)); + if (!tag) + goto fail; + g_queue_push_tail(call_tags, tag); + json_reader_end_member(reader); + free(tag_field); + } + + for (other_tags_idx = 0; other_tags_idx < tag_idx; other_tags_idx++) { + json_reader_end_member(reader); + free(tag_field); + tag_field = str_sprintf("other_tags-%d", other_tags_idx); + if (json_reader_read_member(reader, tag_field->s)) { + tag = g_queue_peek_nth(call_tags, other_tags_idx); + if (!tag) /* shouldn't actually happen, but we're sanity-first! */ + goto fail; + if (!redis_call_match_tags(tag, call_tags, json_reader_get_value(reader))) + goto fail; + } /* missing other_tags list is treated like an empty list */ + } + + goto done; + +fail: + if (call_tags) { + g_queue_free_full(call_tags, __obj_put); + call_tags = NULL; + } + +done: + if (tag_field) + free(tag_field); + json_reader_end_member(reader); + return call_tags; +} + +static GQueue *redis_call_read_stream_fds(JsonReader *reader) { + GQueue *call_sfds; + unsigned sfd_idx; + str* sfd_field; + redis_call_media_stream_fd_t* sfd; + + call_sfds = g_queue_new(); + for (sfd_idx = 0; ; sfd_idx++) { + sfd_field = str_sprintf("sfd-%u", sfd_idx); + if (!json_reader_read_member(reader, sfd_field->s)) + goto done; /* no more sfds */ + sfd = redis_call_media_stream_fd_create(sfd_idx, json_reader_get_value(reader)); + if (!sfd) + goto fail; + g_queue_push_tail(call_sfds, sfd); + json_reader_end_member(reader); + free(sfd_field); + } + + /* we shouldn't reach this point, but just playing it safe */ + sfd_field = NULL; + goto done; + +fail: + if (call_sfds) { + g_queue_free_full(call_sfds, __obj_put); + call_sfds = NULL; + } + +done: + if (sfd_field) + free(sfd_field); + json_reader_end_member(reader); + return call_sfds; +} + +static GQueue *redis_call_read_streams(JsonReader *reader) { + GQueue *call_streams, *call_sfds = NULL; + unsigned stream_idx; + str *stream_field; + redis_call_media_stream_t *stream; + + if (!(call_sfds = redis_call_read_stream_fds(reader))) + goto fail; + call_streams = g_queue_new(); + for (stream_idx = 0; ; stream_idx++) { + stream_field = str_sprintf("stream-%u", stream_idx); + if (!json_reader_read_member(reader, stream_field->s)) + goto done; /* no more streams */ + stream = redis_call_media_stream_create(stream_idx, json_reader_get_value(reader), call_sfds); + if (!stream) + goto fail; + g_queue_push_tail(call_streams, stream); + json_reader_end_member(reader); + free(stream_field); + } + + /* we shouldn't reach this point, but just playing it safe */ + stream_field = NULL; + goto done; + +fail: + if (call_streams) { + g_queue_free_full(call_streams, __obj_put); + call_streams = NULL; + } + +done: + if (stream_field) + free(stream_field); + if (call_sfds) + g_queue_free_full(call_sfds, __obj_put); + json_reader_end_member(reader); + return call_streams; +} + +static GQueue *redis_call_read_media(JsonReader *reader) { + int media_idx; + str *media_field = NULL; + GQueue *call_media, *call_tags = NULL, *call_streams = NULL; + redis_call_media_t *media = NULL; + + if (!(call_tags = redis_call_read_tags(reader))) + goto fail; + if (!(call_streams = redis_call_read_streams(reader))) + goto fail; + call_media = g_queue_new(); + for (media_idx = 0; ; media_idx++) { + media_field = str_sprintf("media-%u", media_idx); + if (!json_reader_read_member(reader, media_field->s)) { + goto done; /* no more media */ + } + media = redis_call_media_create(media_idx, json_reader_get_value(reader), call_tags, call_streams); + if (!media) + goto fail; + g_queue_push_tail(call_media, media); + json_reader_end_member(reader); + free(media_field); + } + + /* not supposed to get here, but just making sure */ + media_field = NULL; + goto done; + +fail: + if (call_media) + g_queue_free_full(call_media, __obj_put); + +done: + json_reader_end_member(reader); + if (media_field) + free(media_field); + if (call_tags) + g_queue_free_full(call_tags, __obj_put); + if (call_streams) + g_queue_free_full(call_streams, __obj_put); + return call_media; +} + +static redis_call_t* redis_call_create(const str* callid, JsonNode* json) { + redis_call_t* callref = NULL; + JsonReader* root_reader = NULL; + + const char *err = 0; + + root_reader = json_reader_new(json); + if (!json_reader_read_member(root_reader, "json")) { + err = "Could not find call data"; + goto fail; + } + callref = redis_call_create_from_metadata(callid, json_reader_get_value(root_reader)); + json_reader_end_member(root_reader); + if (!callref) { + err = "Failed to read call data"; + goto fail; + } + if (!(callref->media = redis_call_read_media(root_reader))) { + err = "Failed to read call media"; + goto fail; + } + + goto done; + +fail: + if (callref) { + obj_put(callref); + callref = NULL; + } + if (err) { + rlog(LOG_WARNING, "Failed to read call data '" STR_FORMAT_M "' from Redis: %s", + STR_FMT_M(callid), + err); + } + +done: + if (root_reader) + g_object_unref(root_reader); + return callref; +} + +/* End of new mode JSON parsing support */ diff --git a/include/redis.h b/include/redis.h index 864a7e6680..fec8c5996f 100644 --- a/include/redis.h +++ b/include/redis.h @@ -146,10 +146,81 @@ int redis_reconnect(struct redis* r); } - - - - - +/* New mode JSON parsing support. Use with care */ + +typedef struct redis_call_media_stream_fd { + struct obj obj; + unsigned unique_id; + unsigned stream_unique_id; + str* pref_family; + unsigned localport; + str* logical_intf; + unsigned logical_intf_uid; +} redis_call_media_stream_fd_t; + +typedef struct redis_call_media_stream { + struct obj obj; + unsigned unique_id; + unsigned media_unique_id; + unsigned selected_sfd; + int rtp_sink; + int rtcp_sink; + int rtcp_sibling; + unsigned last_packet; + unsigned ps_flags; + unsigned component; + str* endpoint; + str* advertised_endpoint; + unsigned stats_packets; + unsigned stats_bytes; + unsigned stats_errors; + GQueue* fds; +} redis_call_media_stream_t; + +struct redis_call_media_tag; + +typedef struct redis_call_media_tag { + struct obj obj; + unsigned unique_id; + unsigned long created; + gboolean active; + gboolean deleted; + gboolean block_dtmf; + gboolean block_media; + str* tag; + struct redis_call_media_tag* other_tag; +} redis_call_media_tag_t; + +typedef struct redis_call_media { + struct obj obj; + unsigned index; + unsigned unique_id; + str* type; + str* protocol; + str* desired_family; + str* logical_intf; + unsigned ptime; + unsigned media_flags; + str* rtpe_addr; + redis_call_media_tag_t* tag; + GQueue* streams; +} redis_call_media_t; + +typedef struct redis_call { + struct obj obj; + str* call_id; + unsigned long long created; + unsigned long last_signal; + unsigned tos; + gboolean deleted; + gboolean ml_deleted; + str* created_from; + str* created_from_addr; + unsigned redis_hosted_db; + str* recording_metadata; + gboolean block_dtmf; + gboolean block_media; + GQueue* media; +} redis_call_t; #endif From 7d6f2785487c4ef7e08dfa96661eebcf7286ed63 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Wed, 8 Jan 2020 20:46:35 +0200 Subject: [PATCH 09/50] various fixes and missing features to new JSON model --- daemon/redis.c | 61 +++++++++++++++++++++++++++++++++++-------------- include/redis.h | 2 ++ 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index bbfb5ce27a..18f9e4584b 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -87,6 +87,8 @@ static int redis_connect(struct redis *r, int wait); /* New mode JSON parsing support. Use with care */ static redis_call_t* redis_call_create(const str*, JsonNode*); static void redis_call_free(void*); +static GQueue* redis_call_get_streams(redis_call_t*); +static void redis_call_obj_put(void*); /* End of new mode JSON parsing support */ static void redis_pipe(struct redis *r, const char *fmt, ...) { @@ -2425,6 +2427,10 @@ static long long json_reader_get_ll_element(JsonReader *reader, unsigned idx) { return out; } +static void redis_call_obj_put(void* o) { + obj_put_o(o); +} + static void redis_call_media_stream_fd_free(void *rcmsf) { redis_call_media_stream_fd_t *streamfdref = rcmsf; if (!streamfdref) @@ -2478,7 +2484,7 @@ static void redis_call_media_stream_free(void *rcms) { if (streamref->advertised_endpoint) free(streamref->advertised_endpoint); if (streamref->fds) - g_queue_free_full(streamref->fds, __obj_put); + g_queue_free_full(streamref->fds, redis_call_obj_put); } static redis_call_media_stream_t *redis_call_media_stream_create(unsigned unique_id, JsonNode *json, GQueue *sfds) { @@ -2524,7 +2530,7 @@ static redis_call_media_stream_t *redis_call_media_stream_create(unsigned unique for (idx = 0; idx < g_queue_get_length(sfds); idx++) { streamfdref = g_queue_peek_nth(sfds, idx); if (streamfdref && streamfdref->stream_unique_id == streamref->unique_id) - g_queue_push_tail(streamref->fds, g_queue_pop_nth(sfds, idx)); + g_queue_push_tail(streamref->fds, obj_get(streamfdref)); } goto done; @@ -2547,6 +2553,10 @@ static void redis_call_media_tag_free(void *rcmt) { return; if (tagref->tag) free(tagref->tag); + if (tagref->viabranch) + free(tagref->viabranch); + if (tagref->label) + free(tagref->label); /* we don't free `other_tag` - it should be owned by some media */ } @@ -2572,6 +2582,8 @@ static redis_call_media_tag_t *redis_call_media_tag_create(unsigned unique_id, J if ((llval = json_reader_get_ll(reader, "block_media")) >= 0) tagref->block_media = llval ? TRUE : FALSE; tagref->tag = json_reader_get_str(reader, "tag"); + tagref->viabranch = json_reader_get_str(reader, "viabranch"); + tagref->label = json_reader_get_str(reader, "label"); goto done; @@ -2604,7 +2616,7 @@ static void redis_call_media_free(void* rcm) { if (mediaref->tag) obj_put(mediaref->tag); if (mediaref->streams) - g_queue_free_full(mediaref->streams, __obj_put); + g_queue_free_full(mediaref->streams, redis_call_obj_put); } static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode *json, GQueue *tags, GQueue *streams) { @@ -2620,10 +2632,10 @@ static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode mediaref = obj_alloc0("redis_call_media", sizeof(*mediaref), redis_call_media_free); mediaref->unique_id = unique_id; if ((llval = json_reader_get_ll(reader, "tag")) >= 0) { - tagref = g_queue_pop_nth(tags, llval); + tagref = g_queue_peek_nth(tags, llval); if (!tagref) goto fail; - mediaref->tag = tagref; + mediaref->tag = obj_get(tagref); } if ((llval = json_reader_get_ll(reader, "index")) >= 0) mediaref->index = llval; @@ -2642,7 +2654,7 @@ static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode for (idx = 0; idx < g_queue_get_length(streams); idx++) { streamref = g_queue_peek_nth(streams, idx); if (streamref && streamref->media_unique_id == mediaref->unique_id) - g_queue_push_tail(mediaref->streams, g_queue_pop_nth(streams, idx)); + g_queue_push_tail(mediaref->streams, obj_get(streamref)); } goto done; @@ -2664,7 +2676,7 @@ static void redis_call_free(void* rc) { if (!callref) return; if (callref->media) - g_queue_free_full(callref->media, __obj_put); + g_queue_free_full(callref->media, redis_call_obj_put); if (callref->call_id) free(callref->call_id); if (callref->created_from) @@ -2791,7 +2803,7 @@ static GQueue *redis_call_read_tags(JsonReader *reader) { fail: if (call_tags) { - g_queue_free_full(call_tags, __obj_put); + g_queue_free_full(call_tags, redis_call_obj_put); call_tags = NULL; } @@ -2827,7 +2839,7 @@ static GQueue *redis_call_read_stream_fds(JsonReader *reader) { fail: if (call_sfds) { - g_queue_free_full(call_sfds, __obj_put); + g_queue_free_full(call_sfds, redis_call_obj_put); call_sfds = NULL; } @@ -2839,9 +2851,9 @@ static GQueue *redis_call_read_stream_fds(JsonReader *reader) { } static GQueue *redis_call_read_streams(JsonReader *reader) { - GQueue *call_streams, *call_sfds = NULL; + GQueue *call_streams = NULL, *call_sfds = NULL; unsigned stream_idx; - str *stream_field; + str *stream_field = NULL; redis_call_media_stream_t *stream; if (!(call_sfds = redis_call_read_stream_fds(reader))) @@ -2865,7 +2877,7 @@ static GQueue *redis_call_read_streams(JsonReader *reader) { fail: if (call_streams) { - g_queue_free_full(call_streams, __obj_put); + g_queue_free_full(call_streams, redis_call_obj_put); call_streams = NULL; } @@ -2873,7 +2885,7 @@ static GQueue *redis_call_read_streams(JsonReader *reader) { if (stream_field) free(stream_field); if (call_sfds) - g_queue_free_full(call_sfds, __obj_put); + g_queue_free_full(call_sfds, redis_call_obj_put); json_reader_end_member(reader); return call_streams; } @@ -2881,7 +2893,7 @@ static GQueue *redis_call_read_streams(JsonReader *reader) { static GQueue *redis_call_read_media(JsonReader *reader) { int media_idx; str *media_field = NULL; - GQueue *call_media, *call_tags = NULL, *call_streams = NULL; + GQueue *call_media = NULL, *call_tags = NULL, *call_streams = NULL; redis_call_media_t *media = NULL; if (!(call_tags = redis_call_read_tags(reader))) @@ -2908,16 +2920,16 @@ static GQueue *redis_call_read_media(JsonReader *reader) { fail: if (call_media) - g_queue_free_full(call_media, __obj_put); + g_queue_free_full(call_media, redis_call_obj_put); done: json_reader_end_member(reader); if (media_field) free(media_field); if (call_tags) - g_queue_free_full(call_tags, __obj_put); + g_queue_free_full(call_tags, redis_call_obj_put); if (call_streams) - g_queue_free_full(call_streams, __obj_put); + g_queue_free_full(call_streams, redis_call_obj_put); return call_media; } @@ -2962,4 +2974,19 @@ static redis_call_t* redis_call_create(const str* callid, JsonNode* json) { return callref; } +GQueue* redis_call_get_streams(redis_call_t* callref) { + GQueue* streams; + redis_call_media_t *media; + unsigned midx, sidx; + + streams = g_queue_new(); + for (midx = 0; midx < g_queue_get_length(callref->media); midx++) { + media = g_queue_peek_nth(callref->media, midx); + for (sidx = 0; sidx < g_queue_get_length(media->streams); sidx++) { + g_queue_push_tail(streams, obj_get((redis_call_media_stream_t*)g_queue_peek_nth(media->streams, sidx))); + } + } + + return streams; +} /* End of new mode JSON parsing support */ diff --git a/include/redis.h b/include/redis.h index fec8c5996f..7122b1f7d5 100644 --- a/include/redis.h +++ b/include/redis.h @@ -188,6 +188,8 @@ typedef struct redis_call_media_tag { gboolean block_dtmf; gboolean block_media; str* tag; + str* viabranch; + str* label; struct redis_call_media_tag* other_tag; } redis_call_media_tag_t; From 8746e34d9a818d11954bea0fe63b620718dfd6ab Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Wed, 8 Jan 2020 20:49:15 +0200 Subject: [PATCH 10/50] use new model for updating stream information and tags of local calls --- daemon/redis.c | 136 ++++++++++++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 59 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 18f9e4584b..087edcc2e8 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1705,86 +1705,104 @@ static void json_restore_call(struct redis *r, const str *callid, enum call_type obj_put(c); } -static void redis_update_call_details(struct redis *r, struct call *c) { - redisReply* rr_jsonStr; - struct redis_list streams; - struct redis_hash call, streamrh; - struct endpoint endpoint, advertised_endpoint; - unsigned ps_flags; - GList *pk; +static int redis_update_call_streams(struct call *c, redis_call_t *redis_call) { + GQueue* redis_call_streams; + redis_call_media_stream_t *stream; + unsigned int i, updated = 0; struct packet_stream *ps; + GList *pk; + struct endpoint endpoint, advertised_endpoint; + + if (!(redis_call_streams = redis_call_get_streams(redis_call))) + return -1; + // review call streams and only update where needed + for (i = 0, pk = c->streams.head; pk && (i < redis_call_streams->length); pk = pk->next, i++) { + ps = pk->data; + ZERO(endpoint); + stream = g_queue_peek_nth(redis_call_streams, i); + endpoint_parse_any(&endpoint, stream->endpoint->s); + endpoint_parse_any(&advertised_endpoint, stream->advertised_endpoint->s); + + if (!ps->endpoint.port && endpoint.port && endpoint.address.family->af) { + ps->endpoint = endpoint; + ps->advertised_endpoint = advertised_endpoint; + ps->ps_flags = stream->ps_flags; + updated = 1; + } + } + + if (updated) + rlog(LOG_INFO, "Updated stream endpoints and flags from Redis"); + g_queue_free_full(redis_call_streams, redis_call_obj_put); + return 0; +} - const char *err = 0; - int i, updated = 0; - JsonReader *root_reader =0; - JsonParser *parser =0; +static int redis_update_call_tags(struct call *c, redis_call_t *redis_call) { + unsigned midx, updated = 0; + redis_call_media_t *media; + // need to combine redis_tags and json_link_tags: + // - read sets of linked tags + // - for each tag that doesn't exist in g_hash_table_lookup(call->tags, tag), and that has a linked tag that does, + // call call_get_monologue(call, exist_tag, not_exist_tag, NULL) + + for (midx = 0; midx < redis_call->media->length; midx++) { + media = g_queue_peek_nth(redis_call->media, midx); + if (media->tag && media->tag->tag && media->tag->other_tag && media->tag->other_tag->tag && + g_hash_table_lookup(c->tags, media->tag->tag) && + !g_hash_table_lookup(c->tags, media->tag->other_tag->tag)) { + call_get_mono_dialogue(c, media->tag->tag, media->tag->other_tag->tag, media->tag->viabranch); + updated = 1; + } + } + + if (updated) + rlog(LOG_INFO, "Updated monologue tags from Redis"); + return 0; +} + +static void redis_update_call_details(struct redis *r, struct call *c) { + redisReply* rr_jsonStr; + redis_call_t *redis_call = NULL; + const char *err = NULL; + JsonParser *parser = NULL; rr_jsonStr = redis_get(r, REDIS_REPLY_STRING, "GET " PB, STR(&c->callid)); err = "could not retrieve JSON data from redis"; if (!rr_jsonStr) - goto err1; + goto fail; parser = json_parser_new(); err = "could not parse JSON data"; if (!json_parser_load_from_data (parser, rr_jsonStr->str, -1, NULL)) - goto err1; - root_reader = json_reader_new (json_parser_get_root (parser)); + goto fail; + redis_call = redis_call_create(&c->callid, json_parser_get_root(parser)); err = "could not read JSON data"; - if (!root_reader) - goto err1; - - if (json_get_hash(&call, "json", -1, root_reader)) - goto err1; - - err = "'streams' incomplete"; - if (json_get_list_hash(&streams, "stream", &call, "num_streams", root_reader)) - goto err1; - - // review call streams and only update where needed - for (i = 0, pk = c->streams.head; pk && (i < streams.len); pk = pk->next, i++) { - streamrh = streams.rh[i]; - ps = pk->data; + if (!redis_call) + goto fail; - ZERO(endpoint); - err = "could not read stream flags"; - if (redis_hash_get_unsigned((unsigned int *) &ps_flags, &streamrh, "ps_flags")) - return err2; - err = "could not read stream endpoint"; - if (redis_hash_get_endpoint(&endpoint, &streamrh, "endpoint")) - goto err2; - err = "could not read stream advertised_endpoint"; - if (redis_hash_get_endpoint(&advertised_endpoint, &streamrh, "advertised_endpoint")) - goto err2; + err = "failed to update stream data"; + if (redis_update_call_streams(c, redis_call)) + goto fail; - if (!ps->endpoint.port && endpoint.port && endpoint.address.family->af) { - ps->endpoint = endpoint; - ps->advertised_endpoint = advertised_endpoint; - ps->ps_flags = ps_flags; - updated = 1; - } - } + err = "failed to update tag data"; + if (redis_update_call_tags(c, redis_call)) + goto fail; - if (updated) - rlog(LOG_INFO, "Updated stream endpoints from Redis"); + goto done; - updated = 0; +fail: + rlog(LOG_WARNING, "Failed to update endpoints for call ID '" STR_FORMAT_M "' from Redis: %s", + STR_FMT_M(&c->callid), + err); - err = NULL; -err2: - json_destroy_list(&streams); -err1: - if (root_reader) - g_object_unref (root_reader); +done: + if (redis_call) + obj_put(redis_call); if (parser) g_object_unref (parser); if (rr_jsonStr) freeReplyObject(rr_jsonStr); log_info_clear(); - if (err) { - rlog(LOG_WARNING, "Failed to update endpoints for call ID '" STR_FORMAT_M "' from Redis: %s", - STR_FMT_M(&c->callid), - err); - } } struct thread_ctx { From 59a7dfd012c5da76ef4a5a7ca22c3d7174498c9f Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Thu, 9 Jan 2020 17:38:48 +0200 Subject: [PATCH 11/50] refactor json support routines and new parsing architecture out of redis.c and into their own reusable objects --- daemon/Makefile | 3 +- daemon/json-helpers.c | 96 ++++++ daemon/redis-json.c | 586 +++++++++++++++++++++++++++++++++++++ daemon/redis.c | 641 +---------------------------------------- include/json-helpers.h | 54 ++++ include/redis-json.h | 123 ++++++++ include/redis.h | 83 +----- t/.gitignore | 2 + t/Makefile | 6 +- 9 files changed, 875 insertions(+), 719 deletions(-) create mode 100644 daemon/json-helpers.c create mode 100644 daemon/redis-json.c create mode 100644 include/json-helpers.h create mode 100644 include/redis-json.h diff --git a/daemon/Makefile b/daemon/Makefile index 91b7edc7aa..0afb626706 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -127,7 +127,8 @@ SRCS= main.c kernel.c poller.c aux.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 \ 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 + codec.c load.c dtmf.c timerthread.c media_player.c \ + redis-json.c json-helpers.c LIBSRCS= loglib.c auxlib.c rtplib.c str.c socket.c streambuf.c ssllib.c dtmflib.c ifeq ($(with_transcoding),yes) LIBSRCS+= codeclib.c resample.c diff --git a/daemon/json-helpers.c b/daemon/json-helpers.c new file mode 100644 index 0000000000..7619d8dd57 --- /dev/null +++ b/daemon/json-helpers.c @@ -0,0 +1,96 @@ +#include "json-helpers.h" + +/** + * Helper method to create `str` instances from C-strings that glib uses often + * @param c C-style string where `strlen()` reports correct length. + * @return `str` string. Release using `free()`. + */ +static str *str_dup_charptr(const char *c) { + str temp = { .s = (char*)c, .len = strlen(c) }; + return str_dup(&temp); +} + +/** + * Helper method to test if the current node the reader is pointing to is a string value. + * Similar to `json_reader_is_value()` but will only return non-zero for values that are string values. + * @param reader JSON reader that is not in an error state + * @return non-zero if the current node is a string value and the reader is not in an error state. zero otherwise. + */ +static int json_reader_is_string(JsonReader* reader) { + JsonNode *node; + + node = json_reader_get_value(reader); + if (json_node_get_value_type(node) == G_TYPE_STRING) + return 1; + return 0; +} + +str *json_reader_get_str(JsonReader *reader, const char *key) { + const gchar *strval; + str *out = NULL; + json_reader_read_member(reader, key); + strval = json_reader_get_string_value(reader); + json_reader_end_member(reader); + if (strval) + out = str_dup_charptr(strval); + return out; +} + +str *json_reader_get_str_element(JsonReader *reader, unsigned idx) { + const gchar *strval = NULL; + str *out = NULL; + json_reader_read_element(reader, idx); + strval = json_reader_get_string_value(reader); + json_reader_end_element(reader); + if (strval) + out = str_dup_charptr(strval); + return out; +} + +long long json_reader_get_ll_element(JsonReader *reader, unsigned idx) { + str *strval; + long long out = -1; + if (json_reader_read_element(reader, idx)) { + if (json_reader_is_string(reader)) { + json_reader_end_element(reader); + strval = json_reader_get_str_element(reader, idx); + if (strval) { + out = strtoll(strval->s, NULL, 10); + free(strval); + } + } else { + out = json_reader_get_int_value(reader); + json_reader_end_element(reader); + } + } else + json_reader_end_element(reader); + return out; +} + +str *json_reader_get_string_value_uri_enc(JsonReader *reader) { + const char *s = json_reader_get_string_value(reader); + if (!s) + return NULL; + str *out = str_uri_decode_len(s, strlen(s)); + return out; +} + +long long json_reader_get_ll(JsonReader *reader, const char *key) { + long long r = -1; + + if (!json_reader_read_member(reader, key)) { + json_reader_end_member(reader); + return r; + } + if (json_reader_is_string(reader)) { + str *ret = json_reader_get_string_value_uri_enc(reader); + json_reader_end_member(reader); + r = strtoll(ret->s, NULL, 10); + free(ret); + return r; + } + /* not a string, lets assume integer */ + r = json_reader_get_int_value(reader); + json_reader_end_member(reader); + return r; +} diff --git a/daemon/redis-json.c b/daemon/redis-json.c new file mode 100644 index 0000000000..e2c1af29a0 --- /dev/null +++ b/daemon/redis-json.c @@ -0,0 +1,586 @@ +#include "redis-json.h" + +#include "json-helpers.h" +#include "log_funcs.h" + +#define rlog(l, x...) ilog(l | LOG_FLAG_RESTORE, x) + +#define JSON_UPDATE_NUM_FIELD_IF_SET(reader, key, field) {\ + long long llval = json_reader_get_ll(reader, key); \ + if (llval >= 0) field = llval; \ +} + +#define JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(reader, key, field) {\ + long long llval = json_reader_get_ll(reader, key); \ + if (llval >= 0) field = llval; \ + else goto fail; \ +} + +/** + * Helper for using obj_put as a (*GDestroyNotify) parameter for glib. + * + * Use it to cleanup `GQueue*`s returned from redis-json calls. + * @param o gpointerdata that references a struct that extends `struct obj` + */ +void gdestroy_obj_put(void* o) { + obj_put_o(o); +} + +/** + * Retrieve a list of all `redis_call_media_stream_t` across all media in the call. + * @param callref a pointer to the `redis_call_t` data + */ +GQueue* redis_call_get_streams(redis_call_t* callref) { + GQueue* streams; + redis_call_media_t *media; + unsigned midx, sidx; + + streams = g_queue_new(); + for (midx = 0; midx < g_queue_get_length(callref->media); midx++) { + media = g_queue_peek_nth(callref->media, midx); + for (sidx = 0; sidx < g_queue_get_length(media->streams); sidx++) { + g_queue_push_tail(streams, obj_get((redis_call_media_stream_t*)g_queue_peek_nth(media->streams, sidx))); + } + } + + return streams; +} + + +static void redis_call_media_stream_fd_free(void *rcmsf) { + redis_call_media_stream_fd_t *streamfdref = rcmsf; + if (!streamfdref) + return; + if (streamfdref->pref_family) + free(streamfdref->pref_family); + if (streamfdref->logical_intf) + free(streamfdref->logical_intf); +} + +static redis_call_media_stream_fd_t *redis_call_media_stream_fd_create(unsigned unique_id, JsonNode *json) { + redis_call_media_stream_fd_t *streamfdref = NULL; + JsonReader *reader = NULL; + + long long llval = 0; + + reader = json_reader_new(json); + streamfdref = obj_alloc0("redis_call_media_stream_fd", sizeof(*streamfdref), redis_call_media_stream_fd_free); + streamfdref->unique_id = unique_id; + JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(reader, "stream", streamfdref->stream_unique_id); + streamfdref->pref_family = json_reader_get_str(reader, "pref_family"); + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "localport", streamfdref->localport); + streamfdref->logical_intf = json_reader_get_str(reader, "logical_intf"); + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "local_intf_uid", streamfdref->logical_intf_uid); + + goto done; + +fail: + if (streamfdref) { + obj_put(streamfdref); + streamfdref = NULL; + } + +done: + if (reader) + g_object_unref(reader); + return streamfdref; +} + +static void redis_call_media_stream_free(void *rcms) { + redis_call_media_stream_t *streamref = rcms; + if (!streamref) + return; + if (streamref->endpoint) + free(streamref->endpoint); + if (streamref->advertised_endpoint) + free(streamref->advertised_endpoint); + if (streamref->fds) + g_queue_free_full(streamref->fds, gdestroy_obj_put); +} + +static redis_call_media_stream_t *redis_call_media_stream_create(unsigned unique_id, JsonNode *json, GQueue *sfds) { + redis_call_media_stream_t *streamref = NULL; + redis_call_media_stream_fd_t *streamfdref; + JsonReader *reader = NULL; + + long long llval = 0; + unsigned idx; + + reader = json_reader_new(json); + streamref = obj_alloc0("redis_call_media_stream", sizeof(*streamref), redis_call_media_stream_free); + streamref->unique_id = unique_id; + JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(reader, "media", streamref->media_unique_id); + if ((llval = json_reader_get_ll(reader, "sfd")) >= 0) + streamref->selected_sfd = llval; + if ((llval = json_reader_get_ll(reader, "rtp_sink")) >= 0) + /* old code writes -1 as u_int32_t */ + streamref->rtp_sink = llval < 1000 ? llval : -1; + if ((llval = json_reader_get_ll(reader, "rtcp_sink")) >= 0) + /* old code writes -1 as u_int32_t */ + streamref->rtcp_sink = llval < 1000 ? llval : -1; + if ((llval = json_reader_get_ll(reader, "rtcp_sibling")) >= 0) + /* old code writes -1 as u_int32_t */ + streamref->rtcp_sibling = llval < 1000 ? llval : -1; + if ((llval = json_reader_get_ll(reader, "last_packet")) >= 0) + streamref->last_packet = llval; + if ((llval = json_reader_get_ll(reader, "ps_flags")) >= 0) + streamref->ps_flags = llval; + if ((llval = json_reader_get_ll(reader, "component")) >= 0) + streamref->component = llval; + streamref->endpoint = json_reader_get_str(reader, "endpoint"); + streamref->advertised_endpoint = json_reader_get_str(reader, "advertised_endpoint"); + if ((llval = json_reader_get_ll(reader, "stats-packets")) >= 0) + streamref->stats_packets = llval; + if ((llval = json_reader_get_ll(reader, "stats-bytes")) >= 0) + streamref->stats_bytes = llval; + if ((llval = json_reader_get_ll(reader, "stats-errors")) >= 0) + streamref->stats_errors = llval; + + /* grab my fds */ + streamref->fds = g_queue_new(); + for (idx = 0; idx < g_queue_get_length(sfds); idx++) { + streamfdref = g_queue_peek_nth(sfds, idx); + if (streamfdref && streamfdref->stream_unique_id == streamref->unique_id) + g_queue_push_tail(streamref->fds, obj_get(streamfdref)); + } + + goto done; + +fail: + if (streamref) { + obj_put(streamref); + streamref = NULL; + } + +done: + if (reader) + g_object_unref(reader); + return streamref; +} + +static void redis_call_media_tag_free(void *rcmt) { + redis_call_media_tag_t *tagref = rcmt; + if (!tagref) + return; + if (tagref->tag) + free(tagref->tag); + if (tagref->viabranch) + free(tagref->viabranch); + if (tagref->label) + free(tagref->label); + /* we don't free `other_tag` - it should be owned by some media */ +} + +static redis_call_media_tag_t *redis_call_media_tag_create(unsigned unique_id, JsonNode *json) { + redis_call_media_tag_t *tagref = NULL; + JsonReader *reader = NULL; + + long long llval = 0; + + reader = json_reader_new(json); + tagref = obj_alloc0("redis_call_media_tag", sizeof(*tagref), redis_call_media_tag_free); + tagref->unique_id = unique_id; + if ((llval = json_reader_get_ll(reader, "created")) >= 0) + tagref->created = llval; + else + goto fail; + if ((llval = json_reader_get_ll(reader, "active")) >= 0) + tagref->active = llval ? TRUE : FALSE; + if ((llval = json_reader_get_ll(reader, "deleted")) >= 0) + tagref->deleted = llval ? TRUE : FALSE; + if ((llval = json_reader_get_ll(reader, "block_dtmf")) >= 0) + tagref->block_dtmf = llval ? TRUE : FALSE; + if ((llval = json_reader_get_ll(reader, "block_media")) >= 0) + tagref->block_media = llval ? TRUE : FALSE; + tagref->tag = json_reader_get_str(reader, "tag"); + tagref->viabranch = json_reader_get_str(reader, "viabranch"); + tagref->label = json_reader_get_str(reader, "label"); + + goto done; + +fail: + if (tagref) { + obj_put(tagref); + tagref = NULL; + } + +done: + if (reader) + g_object_unref(reader); + return tagref; +} + +static void redis_call_media_free(void* rcm) { + redis_call_media_t *mediaref = rcm; + if (!mediaref) + return; + if (mediaref->type) + free(mediaref->type); + if (mediaref->protocol) + free(mediaref->protocol); + if (mediaref->desired_family) + free(mediaref->desired_family); + if (mediaref->logical_intf) + free(mediaref->logical_intf); + if (mediaref->rtpe_addr) + free(mediaref->rtpe_addr); + if (mediaref->tag) + obj_put(mediaref->tag); + if (mediaref->streams) + g_queue_free_full(mediaref->streams, gdestroy_obj_put); +} + +static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode *json, GQueue *tags, GQueue *streams) { + redis_call_media_t *mediaref = NULL; + redis_call_media_tag_t *tagref = NULL; + redis_call_media_stream_t *streamref = NULL; + JsonReader *reader = NULL; + + long long llval = 0; + unsigned idx; + + reader = json_reader_new(json); + mediaref = obj_alloc0("redis_call_media", sizeof(*mediaref), redis_call_media_free); + mediaref->unique_id = unique_id; + if ((llval = json_reader_get_ll(reader, "tag")) >= 0) { + tagref = g_queue_peek_nth(tags, llval); + if (!tagref) + goto fail; + mediaref->tag = obj_get(tagref); + } + if ((llval = json_reader_get_ll(reader, "index")) >= 0) + mediaref->index = llval; + mediaref->type = json_reader_get_str(reader, "type"); + mediaref->protocol = json_reader_get_str(reader, "protocol"); + mediaref->desired_family = json_reader_get_str(reader, "desired_family"); + mediaref->logical_intf = json_reader_get_str(reader, "logical_intf"); + if ((llval = json_reader_get_ll(reader, "ptime")) >= 0) + mediaref->ptime = llval; + if ((llval = json_reader_get_ll(reader, "media_flags")) >= 0) + mediaref->media_flags = llval; + mediaref->rtpe_addr = json_reader_get_str(reader, "rtpe_addr"); + + /* grab my streams */ + mediaref->streams = g_queue_new(); + for (idx = 0; idx < g_queue_get_length(streams); idx++) { + streamref = g_queue_peek_nth(streams, idx); + if (streamref && streamref->media_unique_id == mediaref->unique_id) + g_queue_push_tail(mediaref->streams, obj_get(streamref)); + } + + goto done; + +fail: + if (mediaref) { + obj_put(mediaref); + mediaref = NULL; + } + +done: + if (reader) + g_object_unref(reader); + return mediaref; +} + +static void redis_call_free(void* rc) { + redis_call_t *callref = rc; + if (!callref) + return; + if (callref->media) + g_queue_free_full(callref->media, gdestroy_obj_put); + if (callref->call_id) + free(callref->call_id); + if (callref->created_from) + free(callref->created_from); + if (callref->created_from_addr) + free(callref->created_from_addr); + if (callref->recording_metadata) + free(callref->recording_metadata); +} + +static redis_call_t* redis_call_create_from_metadata(const str* callid, JsonNode* json) { + redis_call_t *callref = NULL; + JsonReader *reader = NULL; + + long long llval = 0; + + reader = json_reader_new(json); + callref = obj_alloc0("redis_call", sizeof(*callref), redis_call_free); + callref->call_id = str_dup(callid); + if ((llval = json_reader_get_ll(reader, "created")) >= 0) + callref->created = llval; + else + goto fail; + if ((llval = json_reader_get_ll(reader, "last_signal")) >= 0) + callref->last_signal = llval; + if ((llval = json_reader_get_ll(reader, "deleted")) >= 0) + callref->deleted = llval ? TRUE : FALSE; + if ((llval = json_reader_get_ll(reader, "ml_deleted")) >= 0) + callref->ml_deleted = llval ? TRUE : FALSE; + callref->created_from = json_reader_get_str(reader, "created_from"); + callref->created_from_addr = json_reader_get_str(reader, "created_from_addr"); + json_reader_end_member(reader); + if ((llval = json_reader_get_ll(reader, "redis_hosted_db")) >= 0) + callref->redis_hosted_db = llval; + if ((llval = json_reader_get_ll(reader, "block_dtmf")) >= 0) + callref->block_dtmf = llval ? TRUE : FALSE; + if ((llval = json_reader_get_ll(reader, "block_media")) >= 0) + callref->block_media = llval ? TRUE : FALSE; + + goto done; + +fail: + if (callref) { + obj_put(callref); + callref = NULL; + } + +done: + if (reader) + g_object_unref(reader); + return callref; +} + +static int redis_call_match_tags(redis_call_media_tag_t *tag, GQueue *call_tags, JsonNode* json) { + JsonReader *reader; + redis_call_media_tag_t *other_tag; + int status = 1; + unsigned num_others, other_idx, other_tagid; + + reader = json_reader_new(json); + if (!json_reader_is_array(reader)) + goto fail; + + num_others = json_reader_count_elements(reader); + if (num_others < 0) + goto fail; + + for (other_idx = 0; other_idx < num_others; other_idx++) { + other_tagid = json_reader_get_ll_element(reader, other_idx); + if (other_tagid < 0) + goto fail; + other_tag = g_queue_peek_nth(call_tags, other_tagid); + if (!other_tag) + goto fail; + tag->other_tag = other_tag; + other_tag->other_tag = tag; + } + + goto done; + +fail: + status = 0; + +done: + if (reader) + g_object_unref(reader); + return status; +} + +static GQueue *redis_call_read_tags(JsonReader *reader) { + GQueue *call_tags = NULL; + unsigned tag_idx, other_tags_idx; + str *tag_field = NULL; + redis_call_media_tag_t *tag = NULL; + + call_tags = g_queue_new(); + for (tag_idx = 0; ; tag_idx++) { + tag_field = str_sprintf("tag-%u", tag_idx); + if (!json_reader_read_member(reader, tag_field->s)) { + break; /* no more tags */ + } + tag = redis_call_media_tag_create(tag_idx, json_reader_get_value(reader)); + if (!tag) + goto fail; + g_queue_push_tail(call_tags, tag); + json_reader_end_member(reader); + free(tag_field); + } + + for (other_tags_idx = 0; other_tags_idx < tag_idx; other_tags_idx++) { + json_reader_end_member(reader); + free(tag_field); + tag_field = str_sprintf("other_tags-%d", other_tags_idx); + if (json_reader_read_member(reader, tag_field->s)) { + tag = g_queue_peek_nth(call_tags, other_tags_idx); + if (!tag) /* shouldn't actually happen, but we're sanity-first! */ + goto fail; + if (!redis_call_match_tags(tag, call_tags, json_reader_get_value(reader))) + goto fail; + } /* missing other_tags list is treated like an empty list */ + } + + goto done; + +fail: + if (call_tags) { + g_queue_free_full(call_tags, gdestroy_obj_put); + call_tags = NULL; + } + +done: + if (tag_field) + free(tag_field); + json_reader_end_member(reader); + return call_tags; +} + +static GQueue *redis_call_read_stream_fds(JsonReader *reader) { + GQueue *call_sfds; + unsigned sfd_idx; + str* sfd_field; + redis_call_media_stream_fd_t* sfd; + + call_sfds = g_queue_new(); + for (sfd_idx = 0; ; sfd_idx++) { + sfd_field = str_sprintf("sfd-%u", sfd_idx); + if (!json_reader_read_member(reader, sfd_field->s)) + goto done; /* no more sfds */ + sfd = redis_call_media_stream_fd_create(sfd_idx, json_reader_get_value(reader)); + if (!sfd) + goto fail; + g_queue_push_tail(call_sfds, sfd); + json_reader_end_member(reader); + free(sfd_field); + } + + /* we shouldn't reach this point, but just playing it safe */ + sfd_field = NULL; + goto done; + +fail: + if (call_sfds) { + g_queue_free_full(call_sfds, gdestroy_obj_put); + call_sfds = NULL; + } + +done: + if (sfd_field) + free(sfd_field); + json_reader_end_member(reader); + return call_sfds; +} + +static GQueue *redis_call_read_streams(JsonReader *reader) { + GQueue *call_streams = NULL, *call_sfds = NULL; + unsigned stream_idx; + str *stream_field = NULL; + redis_call_media_stream_t *stream; + + if (!(call_sfds = redis_call_read_stream_fds(reader))) + goto fail; + call_streams = g_queue_new(); + for (stream_idx = 0; ; stream_idx++) { + stream_field = str_sprintf("stream-%u", stream_idx); + if (!json_reader_read_member(reader, stream_field->s)) + goto done; /* no more streams */ + stream = redis_call_media_stream_create(stream_idx, json_reader_get_value(reader), call_sfds); + if (!stream) + goto fail; + g_queue_push_tail(call_streams, stream); + json_reader_end_member(reader); + free(stream_field); + } + + /* we shouldn't reach this point, but just playing it safe */ + stream_field = NULL; + goto done; + +fail: + if (call_streams) { + g_queue_free_full(call_streams, gdestroy_obj_put); + call_streams = NULL; + } + +done: + if (stream_field) + free(stream_field); + if (call_sfds) + g_queue_free_full(call_sfds, gdestroy_obj_put); + json_reader_end_member(reader); + return call_streams; +} + +static GQueue *redis_call_read_media(JsonReader *reader) { + int media_idx; + str *media_field = NULL; + GQueue *call_media = NULL, *call_tags = NULL, *call_streams = NULL; + redis_call_media_t *media = NULL; + + if (!(call_tags = redis_call_read_tags(reader))) + goto fail; + if (!(call_streams = redis_call_read_streams(reader))) + goto fail; + call_media = g_queue_new(); + for (media_idx = 0; ; media_idx++) { + media_field = str_sprintf("media-%u", media_idx); + if (!json_reader_read_member(reader, media_field->s)) { + goto done; /* no more media */ + } + media = redis_call_media_create(media_idx, json_reader_get_value(reader), call_tags, call_streams); + if (!media) + goto fail; + g_queue_push_tail(call_media, media); + json_reader_end_member(reader); + free(media_field); + } + + /* not supposed to get here, but just making sure */ + media_field = NULL; + goto done; + +fail: + if (call_media) + g_queue_free_full(call_media, gdestroy_obj_put); + +done: + json_reader_end_member(reader); + if (media_field) + free(media_field); + if (call_tags) + g_queue_free_full(call_tags, gdestroy_obj_put); + if (call_streams) + g_queue_free_full(call_streams, gdestroy_obj_put); + return call_media; +} + +redis_call_t* redis_call_create(const str* callid, JsonNode* json) { + redis_call_t* callref = NULL; + JsonReader* root_reader = NULL; + + const char *err = 0; + + root_reader = json_reader_new(json); + if (!json_reader_read_member(root_reader, "json")) { + err = "Could not find call data"; + goto fail; + } + callref = redis_call_create_from_metadata(callid, json_reader_get_value(root_reader)); + json_reader_end_member(root_reader); + if (!callref) { + err = "Failed to read call data"; + goto fail; + } + if (!(callref->media = redis_call_read_media(root_reader))) { + err = "Failed to read call media"; + goto fail; + } + + goto done; + +fail: + if (callref) { + obj_put(callref); + callref = NULL; + } + if (err) { + rlog(LOG_WARNING, "Failed to read call data '" STR_FORMAT_M "' from Redis: %s", + STR_FMT_M(callid), + err); + } + +done: + if (root_reader) + g_object_unref(root_reader); + return callref; +} diff --git a/daemon/redis.c b/daemon/redis.c index 087edcc2e8..51dedeb8bc 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -32,6 +32,8 @@ #include "ssrc.h" #include "main.h" #include "codec.h" +#include "redis-json.h" +#include "json-helpers.h" struct redis *rtpe_redis; struct redis *rtpe_redis_write; @@ -84,13 +86,6 @@ static void json_restore_call(struct redis *r, const str *id, enum call_type typ static void redis_update_call_details(struct redis *r, struct call *call); static int redis_connect(struct redis *r, int wait); -/* New mode JSON parsing support. Use with care */ -static redis_call_t* redis_call_create(const str*, JsonNode*); -static void redis_call_free(void*); -static GQueue* redis_call_get_streams(redis_call_t*); -static void redis_call_obj_put(void*); -/* End of new mode JSON parsing support */ - static void redis_pipe(struct redis *r, const char *fmt, ...) { va_list ap; @@ -771,23 +766,6 @@ INLINE void json_builder_add_string_value_uri_enc(JsonBuilder *builder, const ch str_uri_encode_len(enc, tmp, len); json_builder_add_string_value(builder,enc); } -INLINE str *json_reader_get_string_value_uri_enc(JsonReader *root_reader) { - const char *s = json_reader_get_string_value(root_reader); - if (!s) - return NULL; - str *out = str_uri_decode_len(s, strlen(s)); - return out; // must be free'd -} -// XXX rework restore procedure to use functions like this everywhere and eliminate the GHashTable -INLINE long long json_reader_get_ll(JsonReader *root_reader, const char *key) { - if (!json_reader_read_member(root_reader, key)) - return -1; - str *ret = json_reader_get_string_value_uri_enc(root_reader); - long long r = strtoll(ret->s, NULL, 10); - free(ret); - json_reader_end_member(root_reader); - return r; -} static int json_get_hash(struct redis_hash *out, const char *key, unsigned int id, JsonReader *root_reader) @@ -1733,7 +1711,7 @@ static int redis_update_call_streams(struct call *c, redis_call_t *redis_call) { if (updated) rlog(LOG_INFO, "Updated stream endpoints and flags from Redis"); - g_queue_free_full(redis_call_streams, redis_call_obj_put); + g_queue_free_full(redis_call_streams, gdestroy_obj_put); return 0; } @@ -2395,616 +2373,3 @@ void redis_wipe(struct redis *r) { redisCommandNR(r->ctx, "DEL calls"); mutex_unlock(&r->lock); } - -/* New mode JSON parsing support. Use with care */ - -static str* str_dup_charptr(const char *c) { - str temp = { .s = (char*)c, .len = strlen(c) }; - return str_dup(&temp); -} - -static str* json_reader_get_str(JsonReader *reader, const char *key) { - const gchar *strval; - str *out = NULL; - json_reader_read_member(reader, key); - strval = json_reader_get_string_value(reader); - json_reader_end_member(reader); - if (strval) - out = str_dup_charptr(strval); - return out; -} - -/* -static str* json_reader_get_str_element(JsonReader *reader, unsigned idx) { - const gchar *strval = NULL; - str *out = NULL; - json_reader_read_element(reader, idx); - strval = json_reader_get_string_value(reader); - json_reader_end_element(reader); - if (strval) - out = str_dup_charptr(strval); - return out; -} -*/ - -static long long json_reader_get_ll_element(JsonReader *reader, unsigned idx) { - const gchar *strval; - JsonNode *node; - long long out = -1; - if (json_reader_read_element(reader, idx)) { - node = json_reader_get_value(reader); - if (json_node_get_value_type(node) == G_TYPE_STRING) { - strval = json_node_get_string(node); - if (strval) - out = strtoll(strval, NULL, 10); - } else { - out = json_reader_get_int_value(reader); - } - } - json_reader_end_element(reader); - return out; -} - -static void redis_call_obj_put(void* o) { - obj_put_o(o); -} - -static void redis_call_media_stream_fd_free(void *rcmsf) { - redis_call_media_stream_fd_t *streamfdref = rcmsf; - if (!streamfdref) - return; - if (streamfdref->pref_family) - free(streamfdref->pref_family); - if (streamfdref->logical_intf) - free(streamfdref->logical_intf); -} - -static redis_call_media_stream_fd_t *redis_call_media_stream_fd_create(unsigned unique_id, JsonNode *json) { - redis_call_media_stream_fd_t *streamfdref = NULL; - JsonReader *reader = NULL; - - long long llval = 0; - - reader = json_reader_new(json); - streamfdref = obj_alloc0("redis_call_media_stream_fd", sizeof(*streamfdref), redis_call_media_stream_fd_free); - streamfdref->unique_id = unique_id; - if ((llval = json_reader_get_ll(reader, "stream")) >= 0) - streamfdref->stream_unique_id = llval; - else - goto fail; - streamfdref->pref_family = json_reader_get_str(reader, "pref_family"); - if ((llval = json_reader_get_ll(reader, "localport")) >= 0) - streamfdref->localport = llval; - streamfdref->logical_intf = json_reader_get_str(reader, "logical_intf"); - if ((llval = json_reader_get_ll(reader, "local_intf_uid")) >= 0) - streamfdref->logical_intf_uid = llval; - - goto done; - -fail: - if (streamfdref) { - obj_put(streamfdref); - streamfdref = NULL; - } - -done: - if (reader) - g_object_unref(reader); - return streamfdref; -} - -static void redis_call_media_stream_free(void *rcms) { - redis_call_media_stream_t *streamref = rcms; - if (!streamref) - return; - if (streamref->endpoint) - free(streamref->endpoint); - if (streamref->advertised_endpoint) - free(streamref->advertised_endpoint); - if (streamref->fds) - g_queue_free_full(streamref->fds, redis_call_obj_put); -} - -static redis_call_media_stream_t *redis_call_media_stream_create(unsigned unique_id, JsonNode *json, GQueue *sfds) { - redis_call_media_stream_t *streamref = NULL; - redis_call_media_stream_fd_t *streamfdref; - JsonReader *reader = NULL; - - long long llval = 0; - unsigned idx; - - reader = json_reader_new(json); - streamref = obj_alloc0("redis_call_media_stream", sizeof(*streamref), redis_call_media_stream_free); - streamref->unique_id = unique_id; - if ((llval = json_reader_get_ll(reader, "media")) >= 0) - streamref->media_unique_id = llval; - else - goto fail; - if ((llval = json_reader_get_ll(reader, "sfd")) >= 0) - streamref->selected_sfd = llval; - if ((llval = json_reader_get_ll(reader, "rtp_sink")) >= 0) - streamref->rtp_sink = llval < 1000 ? llval : -1; /* old code writes -1 as u_int32_t */ - if ((llval = json_reader_get_ll(reader, "rtcp_sink")) >= 0) - streamref->rtcp_sink = llval < 1000 ? llval : -1; /* old code writes -1 as u_int32_t */ - if ((llval = json_reader_get_ll(reader, "rtcp_sibling")) >= 0) - streamref->rtcp_sibling = llval < 1000 ? llval : -1; /* old code writes -1 as u_int32_t */ - if ((llval = json_reader_get_ll(reader, "last_packet")) >= 0) - streamref->last_packet = llval; - if ((llval = json_reader_get_ll(reader, "ps_flags")) >= 0) - streamref->ps_flags = llval; - if ((llval = json_reader_get_ll(reader, "component")) >= 0) - streamref->component = llval; - streamref->endpoint = json_reader_get_str(reader, "endpoint"); - streamref->advertised_endpoint = json_reader_get_str(reader, "advertised_endpoint"); - if ((llval = json_reader_get_ll(reader, "stats-packets")) >= 0) - streamref->stats_packets = llval; - if ((llval = json_reader_get_ll(reader, "stats-bytes")) >= 0) - streamref->stats_bytes = llval; - if ((llval = json_reader_get_ll(reader, "stats-errors")) >= 0) - streamref->stats_errors = llval; - - /* grab my fds */ - streamref->fds = g_queue_new(); - for (idx = 0; idx < g_queue_get_length(sfds); idx++) { - streamfdref = g_queue_peek_nth(sfds, idx); - if (streamfdref && streamfdref->stream_unique_id == streamref->unique_id) - g_queue_push_tail(streamref->fds, obj_get(streamfdref)); - } - - goto done; - -fail: - if (streamref) { - obj_put(streamref); - streamref = NULL; - } - -done: - if (reader) - g_object_unref(reader); - return streamref; -} - -static void redis_call_media_tag_free(void *rcmt) { - redis_call_media_tag_t *tagref = rcmt; - if (!tagref) - return; - if (tagref->tag) - free(tagref->tag); - if (tagref->viabranch) - free(tagref->viabranch); - if (tagref->label) - free(tagref->label); - /* we don't free `other_tag` - it should be owned by some media */ -} - -static redis_call_media_tag_t *redis_call_media_tag_create(unsigned unique_id, JsonNode *json) { - redis_call_media_tag_t *tagref = NULL; - JsonReader *reader = NULL; - - long long llval = 0; - - reader = json_reader_new(json); - tagref = obj_alloc0("redis_call_media_tag", sizeof(*tagref), redis_call_media_tag_free); - tagref->unique_id = unique_id; - if ((llval = json_reader_get_ll(reader, "created")) >= 0) - tagref->created = llval; - else - goto fail; - if ((llval = json_reader_get_ll(reader, "active")) >= 0) - tagref->active = llval ? TRUE : FALSE; - if ((llval = json_reader_get_ll(reader, "deleted")) >= 0) - tagref->deleted = llval ? TRUE : FALSE; - if ((llval = json_reader_get_ll(reader, "block_dtmf")) >= 0) - tagref->block_dtmf = llval ? TRUE : FALSE; - if ((llval = json_reader_get_ll(reader, "block_media")) >= 0) - tagref->block_media = llval ? TRUE : FALSE; - tagref->tag = json_reader_get_str(reader, "tag"); - tagref->viabranch = json_reader_get_str(reader, "viabranch"); - tagref->label = json_reader_get_str(reader, "label"); - - goto done; - -fail: - if (tagref) { - obj_put(tagref); - tagref = NULL; - } - -done: - if (reader) - g_object_unref(reader); - return tagref; -} - -static void redis_call_media_free(void* rcm) { - redis_call_media_t *mediaref = rcm; - if (!mediaref) - return; - if (mediaref->type) - free(mediaref->type); - if (mediaref->protocol) - free(mediaref->protocol); - if (mediaref->desired_family) - free(mediaref->desired_family); - if (mediaref->logical_intf) - free(mediaref->logical_intf); - if (mediaref->rtpe_addr) - free(mediaref->rtpe_addr); - if (mediaref->tag) - obj_put(mediaref->tag); - if (mediaref->streams) - g_queue_free_full(mediaref->streams, redis_call_obj_put); -} - -static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode *json, GQueue *tags, GQueue *streams) { - redis_call_media_t *mediaref = NULL; - redis_call_media_tag_t *tagref = NULL; - redis_call_media_stream_t *streamref = NULL; - JsonReader *reader = NULL; - - long long llval = 0; - unsigned idx; - - reader = json_reader_new(json); - mediaref = obj_alloc0("redis_call_media", sizeof(*mediaref), redis_call_media_free); - mediaref->unique_id = unique_id; - if ((llval = json_reader_get_ll(reader, "tag")) >= 0) { - tagref = g_queue_peek_nth(tags, llval); - if (!tagref) - goto fail; - mediaref->tag = obj_get(tagref); - } - if ((llval = json_reader_get_ll(reader, "index")) >= 0) - mediaref->index = llval; - mediaref->type = json_reader_get_str(reader, "type"); - mediaref->protocol = json_reader_get_str(reader, "protocol"); - mediaref->desired_family = json_reader_get_str(reader, "desired_family"); - mediaref->logical_intf = json_reader_get_str(reader, "logical_intf"); - if ((llval = json_reader_get_ll(reader, "ptime")) >= 0) - mediaref->ptime = llval; - if ((llval = json_reader_get_ll(reader, "media_flags")) >= 0) - mediaref->media_flags = llval; - mediaref->rtpe_addr = json_reader_get_str(reader, "rtpe_addr"); - - /* grab my streams */ - mediaref->streams = g_queue_new(); - for (idx = 0; idx < g_queue_get_length(streams); idx++) { - streamref = g_queue_peek_nth(streams, idx); - if (streamref && streamref->media_unique_id == mediaref->unique_id) - g_queue_push_tail(mediaref->streams, obj_get(streamref)); - } - - goto done; - -fail: - if (mediaref) { - obj_put(mediaref); - mediaref = NULL; - } - -done: - if (reader) - g_object_unref(reader); - return mediaref; -} - -static void redis_call_free(void* rc) { - redis_call_t *callref = rc; - if (!callref) - return; - if (callref->media) - g_queue_free_full(callref->media, redis_call_obj_put); - if (callref->call_id) - free(callref->call_id); - if (callref->created_from) - free(callref->created_from); - if (callref->created_from_addr) - free(callref->created_from_addr); - if (callref->recording_metadata) - free(callref->recording_metadata); -} - -static redis_call_t* redis_call_create_from_metadata(const str* callid, JsonNode* json) { - redis_call_t *callref = NULL; - JsonReader *reader = NULL; - - long long llval = 0; - - reader = json_reader_new(json); - callref = obj_alloc0("redis_call", sizeof(*callref), redis_call_free); - callref->call_id = str_dup(callid); - if ((llval = json_reader_get_ll(reader, "created")) >= 0) - callref->created = llval; - else - goto fail; - if ((llval = json_reader_get_ll(reader, "last_signal")) >= 0) - callref->last_signal = llval; - if ((llval = json_reader_get_ll(reader, "deleted")) >= 0) - callref->deleted = llval ? TRUE : FALSE; - if ((llval = json_reader_get_ll(reader, "ml_deleted")) >= 0) - callref->ml_deleted = llval ? TRUE : FALSE; - callref->created_from = json_reader_get_str(reader, "created_from"); - callref->created_from_addr = json_reader_get_str(reader, "created_from_addr"); - json_reader_end_member(reader); - if ((llval = json_reader_get_ll(reader, "redis_hosted_db")) >= 0) - callref->redis_hosted_db = llval; - if ((llval = json_reader_get_ll(reader, "block_dtmf")) >= 0) - callref->block_dtmf = llval ? TRUE : FALSE; - if ((llval = json_reader_get_ll(reader, "block_media")) >= 0) - callref->block_media = llval ? TRUE : FALSE; - - goto done; - -fail: - if (callref) { - obj_put(callref); - callref = NULL; - } - -done: - if (reader) - g_object_unref(reader); - return callref; -} - -static int redis_call_match_tags(redis_call_media_tag_t *tag, GQueue *call_tags, JsonNode* json) { - JsonReader *reader; - redis_call_media_tag_t *other_tag; - int status = 1; - unsigned num_others, other_idx, other_tagid; - - reader = json_reader_new(json); - if (!json_reader_is_array(reader)) - goto fail; - - num_others = json_reader_count_elements(reader); - if (num_others < 0) - goto fail; - - for (other_idx = 0; other_idx < num_others; other_idx++) { - other_tagid = json_reader_get_ll_element(reader, other_idx); - if (other_tagid < 0) - goto fail; - other_tag = g_queue_peek_nth(call_tags, other_tagid); - if (!other_tag) - goto fail; - tag->other_tag = other_tag; - other_tag->other_tag = tag; - } - - goto done; - -fail: - status = 0; - -done: - if (reader) - g_object_unref(reader); - return status; -} - -static GQueue *redis_call_read_tags(JsonReader *reader) { - GQueue *call_tags = NULL; - unsigned tag_idx, other_tags_idx; - str *tag_field = NULL; - redis_call_media_tag_t *tag = NULL; - - call_tags = g_queue_new(); - for (tag_idx = 0; ; tag_idx++) { - tag_field = str_sprintf("tag-%u", tag_idx); - if (!json_reader_read_member(reader, tag_field->s)) { - break; /* no more tags */ - } - tag = redis_call_media_tag_create(tag_idx, json_reader_get_value(reader)); - if (!tag) - goto fail; - g_queue_push_tail(call_tags, tag); - json_reader_end_member(reader); - free(tag_field); - } - - for (other_tags_idx = 0; other_tags_idx < tag_idx; other_tags_idx++) { - json_reader_end_member(reader); - free(tag_field); - tag_field = str_sprintf("other_tags-%d", other_tags_idx); - if (json_reader_read_member(reader, tag_field->s)) { - tag = g_queue_peek_nth(call_tags, other_tags_idx); - if (!tag) /* shouldn't actually happen, but we're sanity-first! */ - goto fail; - if (!redis_call_match_tags(tag, call_tags, json_reader_get_value(reader))) - goto fail; - } /* missing other_tags list is treated like an empty list */ - } - - goto done; - -fail: - if (call_tags) { - g_queue_free_full(call_tags, redis_call_obj_put); - call_tags = NULL; - } - -done: - if (tag_field) - free(tag_field); - json_reader_end_member(reader); - return call_tags; -} - -static GQueue *redis_call_read_stream_fds(JsonReader *reader) { - GQueue *call_sfds; - unsigned sfd_idx; - str* sfd_field; - redis_call_media_stream_fd_t* sfd; - - call_sfds = g_queue_new(); - for (sfd_idx = 0; ; sfd_idx++) { - sfd_field = str_sprintf("sfd-%u", sfd_idx); - if (!json_reader_read_member(reader, sfd_field->s)) - goto done; /* no more sfds */ - sfd = redis_call_media_stream_fd_create(sfd_idx, json_reader_get_value(reader)); - if (!sfd) - goto fail; - g_queue_push_tail(call_sfds, sfd); - json_reader_end_member(reader); - free(sfd_field); - } - - /* we shouldn't reach this point, but just playing it safe */ - sfd_field = NULL; - goto done; - -fail: - if (call_sfds) { - g_queue_free_full(call_sfds, redis_call_obj_put); - call_sfds = NULL; - } - -done: - if (sfd_field) - free(sfd_field); - json_reader_end_member(reader); - return call_sfds; -} - -static GQueue *redis_call_read_streams(JsonReader *reader) { - GQueue *call_streams = NULL, *call_sfds = NULL; - unsigned stream_idx; - str *stream_field = NULL; - redis_call_media_stream_t *stream; - - if (!(call_sfds = redis_call_read_stream_fds(reader))) - goto fail; - call_streams = g_queue_new(); - for (stream_idx = 0; ; stream_idx++) { - stream_field = str_sprintf("stream-%u", stream_idx); - if (!json_reader_read_member(reader, stream_field->s)) - goto done; /* no more streams */ - stream = redis_call_media_stream_create(stream_idx, json_reader_get_value(reader), call_sfds); - if (!stream) - goto fail; - g_queue_push_tail(call_streams, stream); - json_reader_end_member(reader); - free(stream_field); - } - - /* we shouldn't reach this point, but just playing it safe */ - stream_field = NULL; - goto done; - -fail: - if (call_streams) { - g_queue_free_full(call_streams, redis_call_obj_put); - call_streams = NULL; - } - -done: - if (stream_field) - free(stream_field); - if (call_sfds) - g_queue_free_full(call_sfds, redis_call_obj_put); - json_reader_end_member(reader); - return call_streams; -} - -static GQueue *redis_call_read_media(JsonReader *reader) { - int media_idx; - str *media_field = NULL; - GQueue *call_media = NULL, *call_tags = NULL, *call_streams = NULL; - redis_call_media_t *media = NULL; - - if (!(call_tags = redis_call_read_tags(reader))) - goto fail; - if (!(call_streams = redis_call_read_streams(reader))) - goto fail; - call_media = g_queue_new(); - for (media_idx = 0; ; media_idx++) { - media_field = str_sprintf("media-%u", media_idx); - if (!json_reader_read_member(reader, media_field->s)) { - goto done; /* no more media */ - } - media = redis_call_media_create(media_idx, json_reader_get_value(reader), call_tags, call_streams); - if (!media) - goto fail; - g_queue_push_tail(call_media, media); - json_reader_end_member(reader); - free(media_field); - } - - /* not supposed to get here, but just making sure */ - media_field = NULL; - goto done; - -fail: - if (call_media) - g_queue_free_full(call_media, redis_call_obj_put); - -done: - json_reader_end_member(reader); - if (media_field) - free(media_field); - if (call_tags) - g_queue_free_full(call_tags, redis_call_obj_put); - if (call_streams) - g_queue_free_full(call_streams, redis_call_obj_put); - return call_media; -} - -static redis_call_t* redis_call_create(const str* callid, JsonNode* json) { - redis_call_t* callref = NULL; - JsonReader* root_reader = NULL; - - const char *err = 0; - - root_reader = json_reader_new(json); - if (!json_reader_read_member(root_reader, "json")) { - err = "Could not find call data"; - goto fail; - } - callref = redis_call_create_from_metadata(callid, json_reader_get_value(root_reader)); - json_reader_end_member(root_reader); - if (!callref) { - err = "Failed to read call data"; - goto fail; - } - if (!(callref->media = redis_call_read_media(root_reader))) { - err = "Failed to read call media"; - goto fail; - } - - goto done; - -fail: - if (callref) { - obj_put(callref); - callref = NULL; - } - if (err) { - rlog(LOG_WARNING, "Failed to read call data '" STR_FORMAT_M "' from Redis: %s", - STR_FMT_M(callid), - err); - } - -done: - if (root_reader) - g_object_unref(root_reader); - return callref; -} - -GQueue* redis_call_get_streams(redis_call_t* callref) { - GQueue* streams; - redis_call_media_t *media; - unsigned midx, sidx; - - streams = g_queue_new(); - for (midx = 0; midx < g_queue_get_length(callref->media); midx++) { - media = g_queue_peek_nth(callref->media, midx); - for (sidx = 0; sidx < g_queue_get_length(media->streams); sidx++) { - g_queue_push_tail(streams, obj_get((redis_call_media_stream_t*)g_queue_peek_nth(media->streams, sidx))); - } - } - - return streams; -} -/* End of new mode JSON parsing support */ diff --git a/include/json-helpers.h b/include/json-helpers.h new file mode 100644 index 0000000000..85904ea349 --- /dev/null +++ b/include/json-helpers.h @@ -0,0 +1,54 @@ +#ifndef __JSON_HELPERS_H__ +#define __JSON_HELPERS_H__ + +#include + +#include "str.h" + +/** + * Retrieve a string value from a JSON object (in the root of the reader) according to a key. + * @param reader glib JsonReader that has the target object as its current node. + * @param key name of the string value to retrieve + * @return `str` string created from the string value, or `NULL` if no such value was found, + * the reader is in an error state or not pointing to a JSON object. Release using `free()`. + */ +str* json_reader_get_str(JsonReader *reader, const char *key); + +/** + * Retrieve an integer value from a JSON object (in the root of the reader) according to a key. + * @param reader glib JsonReader that has the target object as its current node. + * @param key name of the integer value to retrieve + * @return integer value, if found, -1 otherwise. + * The widest possible "native" storage is used but depending on the original content, this might still result in data loss. + */ +long long json_reader_get_ll(JsonReader *reader, const char *key); + +/** + * Retrieve a string value from a JSON list (in the root of the reader) according to an index. + * This would also work on a JSON object, by retrieving values from keys ordered by storage order (but it is just weird). + * @param reader glib JsonReader that has the target list as its current node. + * @param idx index to the string value to retrieve + * @return `str` string created from the string value, or `NULL` if no such value was found, + * the reader is in an error state or not pointing to a JSON list or object. Release using `free()`. + */ +str* json_reader_get_str_element(JsonReader *reader, unsigned idx); + +/** + * Retrieve an integer value from a JSON list (in the root of the reader) according to an index. + * If the value is stored as a string, this call will run `strtoll` on it and return the result. + * @param reader glib JsonReader that has the target list as its current node. + * @param idx index to the string value to retrieve + * @return integer value, if found, -1 otherwise. + * The widest possible "native" storage is used but depending on the original content, this might still result in data loss. + */ +long long json_reader_get_ll_element(JsonReader *reader, unsigned idx); + +/** + * Retrieve the current string value from a JSON reader and decode its URI encoding. + * @param reader glib JsonReader whose current node is a string value + * @return `str` string containing URI decoded value, or `NULL` if the reader is an error state, the current node is not + * a string value or the string value is not a valid URI encoded value. Release using `free()`. + */ +str *json_reader_get_string_value_uri_enc(JsonReader *reader); + +#endif /* __JSON_HELPERS_H__ */ diff --git a/include/redis-json.h b/include/redis-json.h new file mode 100644 index 0000000000..a274411544 --- /dev/null +++ b/include/redis-json.h @@ -0,0 +1,123 @@ +#ifndef __REDIS_JSON_H__ +#define __REDIS_JSON_H__ + +#include +#include + +#include "obj.h" +#include "str.h" + +/** + * Document object model for mapping call data to storable JSON. + * Currently used by the Redis driver. + * + * There is some confusion about the correct way to map the call data structures + * to JSON and the code in redis.h/c uses a a set of "enumerated object collection" + * to store the hierarchical call data instead of a more traditional object heirarchy. + * + * The model here suggest an object heirarchy where ownership relationships are implied + * by containment. + **/ + +typedef struct redis_call_media_stream_fd { + struct obj obj; + unsigned unique_id; + unsigned stream_unique_id; + str* pref_family; + unsigned localport; + str* logical_intf; + unsigned logical_intf_uid; +} redis_call_media_stream_fd_t; + +typedef struct redis_call_media_stream { + struct obj obj; + unsigned unique_id; + unsigned media_unique_id; + unsigned selected_sfd; + int rtp_sink; + int rtcp_sink; + int rtcp_sibling; + unsigned last_packet; + unsigned ps_flags; + unsigned component; + str* endpoint; + str* advertised_endpoint; + unsigned stats_packets; + unsigned stats_bytes; + unsigned stats_errors; + GQueue* fds; +} redis_call_media_stream_t; + +struct redis_call_media_tag; + +typedef struct redis_call_media_tag { + struct obj obj; + unsigned unique_id; + unsigned long created; + gboolean active; + gboolean deleted; + gboolean block_dtmf; + gboolean block_media; + str* tag; + str* viabranch; + str* label; + struct redis_call_media_tag* other_tag; +} redis_call_media_tag_t; + +typedef struct redis_call_media { + struct obj obj; + unsigned index; + unsigned unique_id; + str* type; + str* protocol; + str* desired_family; + str* logical_intf; + unsigned ptime; + unsigned media_flags; + str* rtpe_addr; + redis_call_media_tag_t* tag; + GQueue* streams; +} redis_call_media_t; + +typedef struct redis_call { + struct obj obj; + str* call_id; + unsigned long long created; + unsigned long last_signal; + unsigned tos; + gboolean deleted; + gboolean ml_deleted; + str* created_from; + str* created_from_addr; + unsigned redis_hosted_db; + str* recording_metadata; + gboolean block_dtmf; + gboolean block_media; + GQueue* media; +} redis_call_t; + +/** + * Parse the JSON node into a `redis_call_t` data structure. + * @param callid the Call's Call-ID that was used as the key for originally storing the call + * @param json the glib-json parsed JSON data + * @return loaded call object model. Release using `obj_put()` + */ +redis_call_t* redis_call_create(const str *callid, JsonNode *json); + +/** + * Retrieve a list of references to `redis_call_media_stream_t` across all media in the call. + * + * @param callref a pointer to the `redis_call_t` data + * @return list of call streams. Release using `q_queue_free_full(list, gdestroy_obj_put)` + */ +GQueue* redis_call_get_streams(redis_call_t *callref); + +/** + * Helper for using obj_put as a (*GDestroyNotify) parameter for glib. + * + * Use it to cleanup `GQueue*`s returned from redis-json calls. + * @param o gpointerdata that references a struct that extends `struct obj` + */ +void gdestroy_obj_put(void* o); + +#endif /* __REDIS_JSON_H__ */ diff --git a/include/redis.h b/include/redis.h index 7122b1f7d5..864a7e6680 100644 --- a/include/redis.h +++ b/include/redis.h @@ -146,83 +146,10 @@ int redis_reconnect(struct redis* r); } -/* New mode JSON parsing support. Use with care */ - -typedef struct redis_call_media_stream_fd { - struct obj obj; - unsigned unique_id; - unsigned stream_unique_id; - str* pref_family; - unsigned localport; - str* logical_intf; - unsigned logical_intf_uid; -} redis_call_media_stream_fd_t; - -typedef struct redis_call_media_stream { - struct obj obj; - unsigned unique_id; - unsigned media_unique_id; - unsigned selected_sfd; - int rtp_sink; - int rtcp_sink; - int rtcp_sibling; - unsigned last_packet; - unsigned ps_flags; - unsigned component; - str* endpoint; - str* advertised_endpoint; - unsigned stats_packets; - unsigned stats_bytes; - unsigned stats_errors; - GQueue* fds; -} redis_call_media_stream_t; - -struct redis_call_media_tag; - -typedef struct redis_call_media_tag { - struct obj obj; - unsigned unique_id; - unsigned long created; - gboolean active; - gboolean deleted; - gboolean block_dtmf; - gboolean block_media; - str* tag; - str* viabranch; - str* label; - struct redis_call_media_tag* other_tag; -} redis_call_media_tag_t; - -typedef struct redis_call_media { - struct obj obj; - unsigned index; - unsigned unique_id; - str* type; - str* protocol; - str* desired_family; - str* logical_intf; - unsigned ptime; - unsigned media_flags; - str* rtpe_addr; - redis_call_media_tag_t* tag; - GQueue* streams; -} redis_call_media_t; - -typedef struct redis_call { - struct obj obj; - str* call_id; - unsigned long long created; - unsigned long last_signal; - unsigned tos; - gboolean deleted; - gboolean ml_deleted; - str* created_from; - str* created_from_addr; - unsigned redis_hosted_db; - str* recording_metadata; - gboolean block_dtmf; - gboolean block_media; - GQueue* media; -} redis_call_t; + + + + + #endif diff --git a/t/.gitignore b/t/.gitignore index f686cdefba..3e23ca631e 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -48,6 +48,8 @@ const_str_hash-test.strhash tests-preload.so timerthread.c media_player.c +redis-json.c +json-helpers.c dtmflib.c test-dtmf-detect *-test diff --git a/t/Makefile b/t/Makefile index 1885435fe7..d13b9616cf 100644 --- a/t/Makefile +++ b/t/Makefile @@ -70,7 +70,8 @@ LIBSRCS+= codeclib.c resample.c socket.c streambuf.c dtmflib.c DAEMONSRCS+= codec.c call.c ice.c kernel.c media_socket.c stun.c bencode.c poller.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 + media_player.c \ + redis-json.c json-helpers.c HASHSRCS+= call_interfaces.c control_ng.c sdp.c endif @@ -128,7 +129,8 @@ transcode-test: transcode-test.o $(COMMONOBJS) codeclib.o resample.o codec.o ssr rtcp.o redis.o iptables.o graphite.o call_interfaces.strhash.o sdp.strhash.o rtp.o crypto.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 dtmflib.o + media_player.o dtmflib.o \ + redis-json.o json-helpers.o payload-tracker-test: payload-tracker-test.o $(COMMONOBJS) ssrc.o aux.o auxlib.o rtp.o crypto.o codeclib.o \ resample.o dtmflib.o From 0ffccef10218c496b8285ca88d69116034a13d0e Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Thu, 9 Jan 2020 17:55:00 +0200 Subject: [PATCH 12/50] finish using macros for reading int fields, for slightly better readability --- daemon/redis-json.c | 105 ++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 67 deletions(-) diff --git a/daemon/redis-json.c b/daemon/redis-json.c index e2c1af29a0..f5b8c4df63 100644 --- a/daemon/redis-json.c +++ b/daemon/redis-json.c @@ -6,8 +6,19 @@ #define rlog(l, x...) ilog(l | LOG_FLAG_RESTORE, x) #define JSON_UPDATE_NUM_FIELD_IF_SET(reader, key, field) {\ - long long llval = json_reader_get_ll(reader, key); \ - if (llval >= 0) field = llval; \ +long long llval = json_reader_get_ll(reader, key); \ +if (llval >= 0) field = llval; \ +} + +#define JSON_UPDATE_BOOL_FIELD_IF_SET(reader, key, field) {\ +long long llval = json_reader_get_ll(reader, key); \ +if (llval >= 0) field = llval ? TRUE : FALSE; \ +} + +/** For use with fields that support -1 (for "not set"), but are stored in JSON as unsigned int */ +#define JSON_UPDATE_SIGNED_NUM_FIELD_IF_SET(reader, key, field) {\ +long long llval = json_reader_get_ll(reader, key); \ +if (llval >= 0) field = llval < 1000 ? llval : -1; \ } #define JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(reader, key, field) {\ @@ -61,8 +72,6 @@ static redis_call_media_stream_fd_t *redis_call_media_stream_fd_create(unsigned redis_call_media_stream_fd_t *streamfdref = NULL; JsonReader *reader = NULL; - long long llval = 0; - reader = json_reader_new(json); streamfdref = obj_alloc0("redis_call_media_stream_fd", sizeof(*streamfdref), redis_call_media_stream_fd_free); streamfdref->unique_id = unique_id; @@ -103,38 +112,24 @@ static redis_call_media_stream_t *redis_call_media_stream_create(unsigned unique redis_call_media_stream_fd_t *streamfdref; JsonReader *reader = NULL; - long long llval = 0; unsigned idx; reader = json_reader_new(json); streamref = obj_alloc0("redis_call_media_stream", sizeof(*streamref), redis_call_media_stream_free); streamref->unique_id = unique_id; JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(reader, "media", streamref->media_unique_id); - if ((llval = json_reader_get_ll(reader, "sfd")) >= 0) - streamref->selected_sfd = llval; - if ((llval = json_reader_get_ll(reader, "rtp_sink")) >= 0) - /* old code writes -1 as u_int32_t */ - streamref->rtp_sink = llval < 1000 ? llval : -1; - if ((llval = json_reader_get_ll(reader, "rtcp_sink")) >= 0) - /* old code writes -1 as u_int32_t */ - streamref->rtcp_sink = llval < 1000 ? llval : -1; - if ((llval = json_reader_get_ll(reader, "rtcp_sibling")) >= 0) - /* old code writes -1 as u_int32_t */ - streamref->rtcp_sibling = llval < 1000 ? llval : -1; - if ((llval = json_reader_get_ll(reader, "last_packet")) >= 0) - streamref->last_packet = llval; - if ((llval = json_reader_get_ll(reader, "ps_flags")) >= 0) - streamref->ps_flags = llval; - if ((llval = json_reader_get_ll(reader, "component")) >= 0) - streamref->component = llval; + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "sfd", streamref->selected_sfd); + JSON_UPDATE_SIGNED_NUM_FIELD_IF_SET(reader, "rtp_sink", streamref->rtp_sink); + JSON_UPDATE_SIGNED_NUM_FIELD_IF_SET(reader, "rtcp_sink", streamref->rtcp_sink); + JSON_UPDATE_SIGNED_NUM_FIELD_IF_SET(reader, "rtcp_sibling", streamref->rtcp_sibling); + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "last_packet", streamref->last_packet); + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "ps_flags", streamref->ps_flags); + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "component", streamref->component); streamref->endpoint = json_reader_get_str(reader, "endpoint"); streamref->advertised_endpoint = json_reader_get_str(reader, "advertised_endpoint"); - if ((llval = json_reader_get_ll(reader, "stats-packets")) >= 0) - streamref->stats_packets = llval; - if ((llval = json_reader_get_ll(reader, "stats-bytes")) >= 0) - streamref->stats_bytes = llval; - if ((llval = json_reader_get_ll(reader, "stats-errors")) >= 0) - streamref->stats_errors = llval; + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "stats-packets", streamref->stats_packets); + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "stats-bytes", streamref->stats_bytes); + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "stats-errors", streamref->stats_errors); /* grab my fds */ streamref->fds = g_queue_new(); @@ -175,23 +170,14 @@ static redis_call_media_tag_t *redis_call_media_tag_create(unsigned unique_id, J redis_call_media_tag_t *tagref = NULL; JsonReader *reader = NULL; - long long llval = 0; - reader = json_reader_new(json); tagref = obj_alloc0("redis_call_media_tag", sizeof(*tagref), redis_call_media_tag_free); tagref->unique_id = unique_id; - if ((llval = json_reader_get_ll(reader, "created")) >= 0) - tagref->created = llval; - else - goto fail; - if ((llval = json_reader_get_ll(reader, "active")) >= 0) - tagref->active = llval ? TRUE : FALSE; - if ((llval = json_reader_get_ll(reader, "deleted")) >= 0) - tagref->deleted = llval ? TRUE : FALSE; - if ((llval = json_reader_get_ll(reader, "block_dtmf")) >= 0) - tagref->block_dtmf = llval ? TRUE : FALSE; - if ((llval = json_reader_get_ll(reader, "block_media")) >= 0) - tagref->block_media = llval ? TRUE : FALSE; + JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(reader, "created", tagref->created); + JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "active", tagref->active); + JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "deleted", tagref->deleted); + JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "block_dtmf", tagref->block_dtmf); + JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "block_media", tagref->block_media); tagref->tag = json_reader_get_str(reader, "tag"); tagref->viabranch = json_reader_get_str(reader, "viabranch"); tagref->label = json_reader_get_str(reader, "label"); @@ -248,16 +234,13 @@ static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode goto fail; mediaref->tag = obj_get(tagref); } - if ((llval = json_reader_get_ll(reader, "index")) >= 0) - mediaref->index = llval; + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "index", mediaref->index); mediaref->type = json_reader_get_str(reader, "type"); mediaref->protocol = json_reader_get_str(reader, "protocol"); mediaref->desired_family = json_reader_get_str(reader, "desired_family"); mediaref->logical_intf = json_reader_get_str(reader, "logical_intf"); - if ((llval = json_reader_get_ll(reader, "ptime")) >= 0) - mediaref->ptime = llval; - if ((llval = json_reader_get_ll(reader, "media_flags")) >= 0) - mediaref->media_flags = llval; + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "ptime", mediaref->ptime); + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "media_flags", mediaref->media_flags); mediaref->rtpe_addr = json_reader_get_str(reader, "rtpe_addr"); /* grab my streams */ @@ -302,30 +285,18 @@ static redis_call_t* redis_call_create_from_metadata(const str* callid, JsonNode redis_call_t *callref = NULL; JsonReader *reader = NULL; - long long llval = 0; - reader = json_reader_new(json); callref = obj_alloc0("redis_call", sizeof(*callref), redis_call_free); callref->call_id = str_dup(callid); - if ((llval = json_reader_get_ll(reader, "created")) >= 0) - callref->created = llval; - else - goto fail; - if ((llval = json_reader_get_ll(reader, "last_signal")) >= 0) - callref->last_signal = llval; - if ((llval = json_reader_get_ll(reader, "deleted")) >= 0) - callref->deleted = llval ? TRUE : FALSE; - if ((llval = json_reader_get_ll(reader, "ml_deleted")) >= 0) - callref->ml_deleted = llval ? TRUE : FALSE; + JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(reader, "created", callref->created); + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "last_signal", callref->last_signal); + JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "deleted", callref->deleted); + JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "ml_deleted", callref->ml_deleted); callref->created_from = json_reader_get_str(reader, "created_from"); callref->created_from_addr = json_reader_get_str(reader, "created_from_addr"); - json_reader_end_member(reader); - if ((llval = json_reader_get_ll(reader, "redis_hosted_db")) >= 0) - callref->redis_hosted_db = llval; - if ((llval = json_reader_get_ll(reader, "block_dtmf")) >= 0) - callref->block_dtmf = llval ? TRUE : FALSE; - if ((llval = json_reader_get_ll(reader, "block_media")) >= 0) - callref->block_media = llval ? TRUE : FALSE; + JSON_UPDATE_NUM_FIELD_IF_SET(reader, "redis_hosted_db", callref->redis_hosted_db); + JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "block_dtmf", callref->block_dtmf); + JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "block_media", callref->block_media); goto done; From 93a18731fa95ccfaa672964ebe07cff1457ab4df Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Thu, 9 Jan 2020 17:55:22 +0200 Subject: [PATCH 13/50] other_tags should be ref managed as well --- daemon/redis-json.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/daemon/redis-json.c b/daemon/redis-json.c index f5b8c4df63..370ec1219b 100644 --- a/daemon/redis-json.c +++ b/daemon/redis-json.c @@ -163,7 +163,8 @@ static void redis_call_media_tag_free(void *rcmt) { free(tagref->viabranch); if (tagref->label) free(tagref->label); - /* we don't free `other_tag` - it should be owned by some media */ + if (tagref->other_tag) + obj_put(tagref->other_tag); } static redis_call_media_tag_t *redis_call_media_tag_create(unsigned unique_id, JsonNode *json) { @@ -333,8 +334,8 @@ static int redis_call_match_tags(redis_call_media_tag_t *tag, GQueue *call_tags, other_tag = g_queue_peek_nth(call_tags, other_tagid); if (!other_tag) goto fail; - tag->other_tag = other_tag; - other_tag->other_tag = tag; + tag->other_tag = obj_get(other_tag); + other_tag->other_tag = obj_get(tag); } goto done; From ae6b271bc076e8c20915e17ac9d358fd6818053a Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Thu, 9 Jan 2020 19:59:18 +0200 Subject: [PATCH 14/50] added useful "get node by name" helper to grab an entire object or array --- daemon/json-helpers.c | 8 ++++++++ include/json-helpers.h | 18 ++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/daemon/json-helpers.c b/daemon/json-helpers.c index 7619d8dd57..51885500ef 100644 --- a/daemon/json-helpers.c +++ b/daemon/json-helpers.c @@ -94,3 +94,11 @@ long long json_reader_get_ll(JsonReader *reader, const char *key) { json_reader_end_member(reader); return r; } + +JsonNode* json_reader_get_node(JsonReader *reader, const char *key) { + JsonNode* nodeval; + json_reader_read_member(reader, key); + nodeval = json_reader_get_value(reader); + json_reader_end_member(reader); + return nodeval; +} diff --git a/include/json-helpers.h b/include/json-helpers.h index 85904ea349..609a97e745 100644 --- a/include/json-helpers.h +++ b/include/json-helpers.h @@ -6,7 +6,7 @@ #include "str.h" /** - * Retrieve a string value from a JSON object (in the root of the reader) according to a key. + * Retrieve a string value from a JSON object according to a key. * @param reader glib JsonReader that has the target object as its current node. * @param key name of the string value to retrieve * @return `str` string created from the string value, or `NULL` if no such value was found, @@ -15,7 +15,7 @@ str* json_reader_get_str(JsonReader *reader, const char *key); /** - * Retrieve an integer value from a JSON object (in the root of the reader) according to a key. + * Retrieve an integer value from a JSON object according to a key. * @param reader glib JsonReader that has the target object as its current node. * @param key name of the integer value to retrieve * @return integer value, if found, -1 otherwise. @@ -24,7 +24,7 @@ str* json_reader_get_str(JsonReader *reader, const char *key); long long json_reader_get_ll(JsonReader *reader, const char *key); /** - * Retrieve a string value from a JSON list (in the root of the reader) according to an index. + * Retrieve a string value from a JSON list according to an index. * This would also work on a JSON object, by retrieving values from keys ordered by storage order (but it is just weird). * @param reader glib JsonReader that has the target list as its current node. * @param idx index to the string value to retrieve @@ -34,7 +34,7 @@ long long json_reader_get_ll(JsonReader *reader, const char *key); str* json_reader_get_str_element(JsonReader *reader, unsigned idx); /** - * Retrieve an integer value from a JSON list (in the root of the reader) according to an index. + * Retrieve an integer value from a JSON list according to an index. * If the value is stored as a string, this call will run `strtoll` on it and return the result. * @param reader glib JsonReader that has the target list as its current node. * @param idx index to the string value to retrieve @@ -51,4 +51,14 @@ long long json_reader_get_ll_element(JsonReader *reader, unsigned idx); */ str *json_reader_get_string_value_uri_enc(JsonReader *reader); +/** + * Retrieve a JSON node from a JSON object according to a key. + * The node can be any node, but this call will be mostly useful to get an object or list to be fed into + * `json_reader_new()`. + * @param reader glib JsonReader that has the target object as its current node. + * @param key name of the object or list value to retrieve + * @return JSON node retrieved, if found, or `NULL` otherwise + */ +JsonNode* json_reader_get_node(JsonReader *reader, const char *key); + #endif /* __JSON_HELPERS_H__ */ From cc1b63897d86ee36c4d931447497b729baf58e57 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Thu, 9 Jan 2020 19:59:54 +0200 Subject: [PATCH 15/50] added payload updates to local call updates after an alternate added data --- daemon/redis-json.c | 106 +++++++++++++++++++++++++++++++++++++------ daemon/redis.c | 45 ++++++++++++++++++ include/redis-json.h | 13 ++++-- 3 files changed, 147 insertions(+), 17 deletions(-) diff --git a/daemon/redis-json.c b/daemon/redis-json.c index 370ec1219b..76a15c4d78 100644 --- a/daemon/redis-json.c +++ b/daemon/redis-json.c @@ -153,6 +153,23 @@ static redis_call_media_stream_t *redis_call_media_stream_create(unsigned unique return streamref; } +static void redis_call_rtp_payload_type_free(void *rcrpt) { + redis_call_rtp_payload_type_t *payloadref = rcrpt; + if (!payloadref) + return; + if (payloadref->codec_str) + free(payloadref->codec_str); +} + +static redis_call_rtp_payload_type_t *redis_call_rtp_payload_type_create(unsigned payload_type, str* payload_string) { + redis_call_rtp_payload_type_t *payloadref; + + payloadref = obj_alloc0("redis_call_rtp_payload_type", sizeof(*payloadref), redis_call_rtp_payload_type_free); + payloadref->payload_type = payload_type; + payloadref->codec_str = str_dup(payload_string); + return payloadref; +} + static void redis_call_media_tag_free(void *rcmt) { redis_call_media_tag_t *tagref = rcmt; if (!tagref) @@ -215,9 +232,58 @@ static void redis_call_media_free(void* rcm) { obj_put(mediaref->tag); if (mediaref->streams) g_queue_free_full(mediaref->streams, gdestroy_obj_put); + if (mediaref->codec_prefs_recv) + g_queue_free_full(mediaref->codec_prefs_recv, gdestroy_obj_put); + if (mediaref->codec_prefs_send) + g_queue_free_full(mediaref->codec_prefs_send, gdestroy_obj_put); +} + +static GQueue *redis_call_media_read_payloads(JsonNode* payloadTypes) { + GQueue *out; + JsonReader* reader = NULL; + redis_call_rtp_payload_type_t *payload; + unsigned payload_count; + str* payload_str = NULL; + str ptype; + unsigned idx, pt; + + /* read payloads */ + reader = json_reader_new(payloadTypes); + out = g_queue_new(); + payload_count = json_reader_count_elements(reader); + for (idx = 0; idx < payload_count; idx++) { + payload_str = json_reader_get_str_element(reader, idx); + if (str_token(&ptype, payload_str, '/')) + goto fail; + + pt = str_to_ui(&ptype, 0); + payload = redis_call_rtp_payload_type_create(pt, payload_str); + if (!payload) + goto fail; + g_queue_push_tail(out, payload); + + free(payload_str); + payload_str = NULL; + } + + goto done; + +fail: + if (out) { + g_queue_free_full(out, gdestroy_obj_put); + out = NULL; + } + +done: + if (payload_str) + free(payload_str); + if (reader) + g_object_unref(reader); + return out; } -static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode *json, GQueue *tags, GQueue *streams) { +static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode *json, GQueue *tags, GQueue *streams, + JsonNode *streamIds, JsonNode *endpointMaps, JsonNode *payloadTypesRecv, JsonNode *payloadTypesSend) { redis_call_media_t *mediaref = NULL; redis_call_media_tag_t *tagref = NULL; redis_call_media_stream_t *streamref = NULL; @@ -252,6 +318,11 @@ static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode g_queue_push_tail(mediaref->streams, obj_get(streamref)); } + if (!(mediaref->codec_prefs_recv = redis_call_media_read_payloads(payloadTypesRecv))) + goto fail; + if (!(mediaref->codec_prefs_send = redis_call_media_read_payloads(payloadTypesSend))) + goto fail; + goto done; fail: @@ -446,7 +517,7 @@ static GQueue *redis_call_read_streams(JsonReader *reader) { stream_field = str_sprintf("stream-%u", stream_idx); if (!json_reader_read_member(reader, stream_field->s)) goto done; /* no more streams */ - stream = redis_call_media_stream_create(stream_idx, json_reader_get_value(reader), call_sfds); + stream = redis_call_media_stream_create(stream_idx, json_reader_get_value(reader), call_sfds); if (!stream) goto fail; g_queue_push_tail(call_streams, stream); @@ -475,30 +546,40 @@ static GQueue *redis_call_read_streams(JsonReader *reader) { static GQueue *redis_call_read_media(JsonReader *reader) { int media_idx; - str *media_field = NULL; GQueue *call_media = NULL, *call_tags = NULL, *call_streams = NULL; + JsonNode *mediaNode, *streamIdsNode, *endpointMapsNode, *payloadTypesRecvNode, *payloadTypesSendNode; redis_call_media_t *media = NULL; + char fieldname[50]; + if (!(call_tags = redis_call_read_tags(reader))) goto fail; if (!(call_streams = redis_call_read_streams(reader))) goto fail; call_media = g_queue_new(); for (media_idx = 0; ; media_idx++) { - media_field = str_sprintf("media-%u", media_idx); - if (!json_reader_read_member(reader, media_field->s)) { - goto done; /* no more media */ - } - media = redis_call_media_create(media_idx, json_reader_get_value(reader), call_tags, call_streams); + snprintf(fieldname, sizeof(fieldname), "media-%u", media_idx); + mediaNode = json_reader_get_node(reader, fieldname); + if (!mediaNode) /* no more media */ + goto done; + snprintf(fieldname, sizeof(fieldname), "streams-%u", media_idx); + streamIdsNode = json_reader_get_node(reader, fieldname); + snprintf(fieldname, sizeof(fieldname), "maps-%u", media_idx); + endpointMapsNode = json_reader_get_node(reader, fieldname); + snprintf(fieldname, sizeof(fieldname), "payload_types-%u", media_idx); + payloadTypesRecvNode = json_reader_get_node(reader, fieldname); + snprintf(fieldname, sizeof(fieldname), "payload_types_send-%u", media_idx); + payloadTypesSendNode = json_reader_get_node(reader, fieldname); + if (!streamIdsNode || !endpointMapsNode || !payloadTypesRecvNode || !payloadTypesSendNode) + goto fail; + media = redis_call_media_create(media_idx, mediaNode, call_tags, call_streams, streamIdsNode, + endpointMapsNode, payloadTypesRecvNode, payloadTypesSendNode); if (!media) goto fail; g_queue_push_tail(call_media, media); - json_reader_end_member(reader); - free(media_field); } /* not supposed to get here, but just making sure */ - media_field = NULL; goto done; fail: @@ -506,9 +587,6 @@ static GQueue *redis_call_read_media(JsonReader *reader) { g_queue_free_full(call_media, gdestroy_obj_put); done: - json_reader_end_member(reader); - if (media_field) - free(media_field); if (call_tags) g_queue_free_full(call_tags, gdestroy_obj_put); if (call_streams) diff --git a/daemon/redis.c b/daemon/redis.c index 51dedeb8bc..5b7a86f32d 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1738,6 +1738,47 @@ static int redis_update_call_tags(struct call *c, redis_call_t *redis_call) { return 0; } +static int redis_update_call_media_codecs(struct call_media *cm, GQueue* redis_codec_list, + void(*adder_func)(struct call_media *media, struct rtp_payload_type *pt)) { + unsigned pidx, updates = 0; + redis_call_rtp_payload_type_t *payload; + + if (!redis_codec_list) + return updates; /* nothing to add */ + for (pidx = 0; pidx < redis_codec_list->length; pidx++) { + payload = g_queue_peek_nth(redis_codec_list, pidx); + struct rtp_payload_type *pt = codec_make_payload_type(payload->codec_str, cm); + if (!pt) + continue; /* oops? */ + pt->payload_type = payload->payload_type; + adder_func(cm, pt); + updates++; + } + return updates; +} + +static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) { + unsigned updated = 0; + redis_call_media_t *media; + + GList *l; + for (l = c->medias.head; l; l = l->next) { + struct call_media *m = l->data; + media = g_queue_peek_nth(redis_call->media, m->unique_id); + if (!media) + continue; /* weird... */ + if (m->codecs_prefs_recv.length == 0 && + redis_update_call_media_codecs(m, media->codec_prefs_recv, __rtp_payload_type_add_recv) > 0) + updated = 1; + if (m->codecs_prefs_send.length == 0 && + redis_update_call_media_codecs(m, media->codec_prefs_send, __rtp_payload_type_add_send) > 0) + updated = 1; + } + if (updated) + rlog(LOG_INFO, "Updated media codecs from Redis"); + return 0; +} + static void redis_update_call_details(struct redis *r, struct call *c) { redisReply* rr_jsonStr; redis_call_t *redis_call = NULL; @@ -1766,6 +1807,10 @@ static void redis_update_call_details(struct redis *r, struct call *c) { if (redis_update_call_tags(c, redis_call)) goto fail; + err = "failed to update payload data"; + if (redis_update_call_payloads(c, redis_call)) + goto fail; + goto done; fail: diff --git a/include/redis-json.h b/include/redis-json.h index a274411544..bece192ec0 100644 --- a/include/redis-json.h +++ b/include/redis-json.h @@ -45,9 +45,14 @@ typedef struct redis_call_media_stream { unsigned stats_packets; unsigned stats_bytes; unsigned stats_errors; - GQueue* fds; + GQueue* fds; /**< list of redis_call_media_stream_fd_t */ } redis_call_media_stream_t; +typedef struct redis_call_rtp_payload_type { + unsigned payload_type; + str* codec_str; +} redis_call_rtp_payload_type_t; + struct redis_call_media_tag; typedef struct redis_call_media_tag { @@ -76,7 +81,9 @@ typedef struct redis_call_media { unsigned media_flags; str* rtpe_addr; redis_call_media_tag_t* tag; - GQueue* streams; + GQueue* streams; /**< list of redis_call_media_stream_t */ + GQueue* codec_prefs_recv; /**< list of redis_call_rtp_payload_type_t */ + GQueue* codec_prefs_send; /**< list of redis_call_rtp_payload_type_t */ } redis_call_media_t; typedef struct redis_call { @@ -93,7 +100,7 @@ typedef struct redis_call { str* recording_metadata; gboolean block_dtmf; gboolean block_media; - GQueue* media; + GQueue* media; /**< list of redis_call_media_t */ } redis_call_t; /** From 986549c6e89ba0ceb984337fa29bae5a9733835c Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Sun, 19 Jan 2020 17:52:54 +0200 Subject: [PATCH 16/50] redone JSON parsing using the lower-level, but much better, JsonObject/Array API (JsonReader is a bit of a shitshow for anything other than stream-reading, and possibly not even that - why was it even used in the first place?) --- daemon/json-helpers.c | 54 +++++++++ daemon/redis-json.c | 252 ++++++++++++++++++----------------------- daemon/redis.c | 2 +- include/json-helpers.h | 45 +++++++- include/redis-json.h | 1 + 5 files changed, 209 insertions(+), 145 deletions(-) diff --git a/daemon/json-helpers.c b/daemon/json-helpers.c index 51885500ef..34fae966a1 100644 --- a/daemon/json-helpers.c +++ b/daemon/json-helpers.c @@ -36,6 +36,24 @@ str *json_reader_get_str(JsonReader *reader, const char *key) { return out; } +str *json_object_get_str(JsonObject* json, const char *key) { + const gchar *strval; + str *out = NULL; + strval = json_object_get_string_member(json, key); + if (strval) + out = str_dup_charptr(strval); + return out; +} + +str *json_array_get_str(JsonArray *json, unsigned idx) { + const gchar *strval; + str *out = NULL; + strval = json_array_get_string_element(json, idx); + if (strval) + out = str_dup_charptr(strval); + return out; +} + str *json_reader_get_str_element(JsonReader *reader, unsigned idx) { const gchar *strval = NULL; str *out = NULL; @@ -67,6 +85,24 @@ long long json_reader_get_ll_element(JsonReader *reader, unsigned idx) { return out; } +long long json_array_get_ll(JsonArray *json, unsigned idx) { + long long out = -1; + JsonNode *member; + + if (json_array_get_length(json) >= idx) + return out; + + member = json_array_get_element(json, idx); + if (json_node_get_value_type(member) == G_TYPE_STRING) { + str *strval = json_array_get_str(json, idx); + out = strtoll(strval->s, NULL, 10); + free(strval); + return out; + } + + return json_array_get_int_element(json, idx); // returns gint64 +} + str *json_reader_get_string_value_uri_enc(JsonReader *reader) { const char *s = json_reader_get_string_value(reader); if (!s) @@ -95,6 +131,24 @@ long long json_reader_get_ll(JsonReader *reader, const char *key) { return r; } +long long json_object_get_ll(JsonObject *json, const char *key) { + long long r = -1; + JsonNode *member; + + if (!json_object_has_member(json, key)) + return r; + + member = json_object_get_member(json, key); + if (json_node_get_value_type(member) == G_TYPE_STRING) { + str *ret = json_object_get_str(json, key); + r = strtoll(ret->s, NULL, 10); + free(ret); + return r; + } + /* not a string, lets assume integer */ + return json_object_get_int_member(json, key); // returns gint64 +} + JsonNode* json_reader_get_node(JsonReader *reader, const char *key) { JsonNode* nodeval; json_reader_read_member(reader, key); diff --git a/daemon/redis-json.c b/daemon/redis-json.c index 76a15c4d78..05c60a3fa4 100644 --- a/daemon/redis-json.c +++ b/daemon/redis-json.c @@ -3,26 +3,26 @@ #include "json-helpers.h" #include "log_funcs.h" -#define rlog(l, x...) ilog(l | LOG_FLAG_RESTORE, x) +#define rlog(l, x...) printf(x) -#define JSON_UPDATE_NUM_FIELD_IF_SET(reader, key, field) {\ -long long llval = json_reader_get_ll(reader, key); \ -if (llval >= 0) field = llval; \ +#define JSON_UPDATE_NUM_FIELD_IF_SET(json, key, field) {\ + long long llval = json_object_get_ll(json, key); \ + if (llval >= 0) field = llval; \ } -#define JSON_UPDATE_BOOL_FIELD_IF_SET(reader, key, field) {\ -long long llval = json_reader_get_ll(reader, key); \ -if (llval >= 0) field = llval ? TRUE : FALSE; \ +#define JSON_UPDATE_BOOL_FIELD_IF_SET(json, key, field) {\ + long long llval = json_object_get_ll(json, key); \ + if (llval >= 0) field = llval ? TRUE : FALSE; \ } /** For use with fields that support -1 (for "not set"), but are stored in JSON as unsigned int */ -#define JSON_UPDATE_SIGNED_NUM_FIELD_IF_SET(reader, key, field) {\ -long long llval = json_reader_get_ll(reader, key); \ -if (llval >= 0) field = llval < 1000 ? llval : -1; \ +#define JSON_UPDATE_SIGNED_NUM_FIELD_IF_SET(json, key, field) {\ + long long llval = json_object_get_ll(json, key); \ + if (llval >= 0) field = llval < 1000 ? llval : -1; \ } -#define JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(reader, key, field) {\ - long long llval = json_reader_get_ll(reader, key); \ +#define JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(json, key, field) {\ + long long llval = json_object_get_ll(json, key); \ if (llval >= 0) field = llval; \ else goto fail; \ } @@ -68,18 +68,16 @@ static void redis_call_media_stream_fd_free(void *rcmsf) { free(streamfdref->logical_intf); } -static redis_call_media_stream_fd_t *redis_call_media_stream_fd_create(unsigned unique_id, JsonNode *json) { +static redis_call_media_stream_fd_t *redis_call_media_stream_fd_create(unsigned unique_id, JsonObject *json) { redis_call_media_stream_fd_t *streamfdref = NULL; - JsonReader *reader = NULL; - reader = json_reader_new(json); streamfdref = obj_alloc0("redis_call_media_stream_fd", sizeof(*streamfdref), redis_call_media_stream_fd_free); streamfdref->unique_id = unique_id; - JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(reader, "stream", streamfdref->stream_unique_id); - streamfdref->pref_family = json_reader_get_str(reader, "pref_family"); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "localport", streamfdref->localport); - streamfdref->logical_intf = json_reader_get_str(reader, "logical_intf"); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "local_intf_uid", streamfdref->logical_intf_uid); + JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(json, "stream", streamfdref->stream_unique_id); + streamfdref->pref_family = json_object_get_str(json, "pref_family"); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "localport", streamfdref->localport); + streamfdref->logical_intf = json_object_get_str(json, "logical_intf"); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "local_intf_uid", streamfdref->logical_intf_uid); goto done; @@ -90,8 +88,6 @@ static redis_call_media_stream_fd_t *redis_call_media_stream_fd_create(unsigned } done: - if (reader) - g_object_unref(reader); return streamfdref; } @@ -107,29 +103,27 @@ static void redis_call_media_stream_free(void *rcms) { g_queue_free_full(streamref->fds, gdestroy_obj_put); } -static redis_call_media_stream_t *redis_call_media_stream_create(unsigned unique_id, JsonNode *json, GQueue *sfds) { +static redis_call_media_stream_t *redis_call_media_stream_create(unsigned unique_id, JsonObject *json, GQueue *sfds) { redis_call_media_stream_t *streamref = NULL; redis_call_media_stream_fd_t *streamfdref; - JsonReader *reader = NULL; unsigned idx; - reader = json_reader_new(json); streamref = obj_alloc0("redis_call_media_stream", sizeof(*streamref), redis_call_media_stream_free); streamref->unique_id = unique_id; - JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(reader, "media", streamref->media_unique_id); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "sfd", streamref->selected_sfd); - JSON_UPDATE_SIGNED_NUM_FIELD_IF_SET(reader, "rtp_sink", streamref->rtp_sink); - JSON_UPDATE_SIGNED_NUM_FIELD_IF_SET(reader, "rtcp_sink", streamref->rtcp_sink); - JSON_UPDATE_SIGNED_NUM_FIELD_IF_SET(reader, "rtcp_sibling", streamref->rtcp_sibling); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "last_packet", streamref->last_packet); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "ps_flags", streamref->ps_flags); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "component", streamref->component); - streamref->endpoint = json_reader_get_str(reader, "endpoint"); - streamref->advertised_endpoint = json_reader_get_str(reader, "advertised_endpoint"); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "stats-packets", streamref->stats_packets); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "stats-bytes", streamref->stats_bytes); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "stats-errors", streamref->stats_errors); + JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(json, "media", streamref->media_unique_id); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "sfd", streamref->selected_sfd); + JSON_UPDATE_SIGNED_NUM_FIELD_IF_SET(json, "rtp_sink", streamref->rtp_sink); + JSON_UPDATE_SIGNED_NUM_FIELD_IF_SET(json, "rtcp_sink", streamref->rtcp_sink); + JSON_UPDATE_SIGNED_NUM_FIELD_IF_SET(json, "rtcp_sibling", streamref->rtcp_sibling); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "last_packet", streamref->last_packet); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "ps_flags", streamref->ps_flags); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "component", streamref->component); + streamref->endpoint = json_object_get_str(json, "endpoint"); + streamref->advertised_endpoint = json_object_get_str(json, "advertised_endpoint"); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "stats-packets", streamref->stats_packets); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "stats-bytes", streamref->stats_bytes); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "stats-errors", streamref->stats_errors); /* grab my fds */ streamref->fds = g_queue_new(); @@ -148,8 +142,6 @@ static redis_call_media_stream_t *redis_call_media_stream_create(unsigned unique } done: - if (reader) - g_object_unref(reader); return streamref; } @@ -184,21 +176,19 @@ static void redis_call_media_tag_free(void *rcmt) { obj_put(tagref->other_tag); } -static redis_call_media_tag_t *redis_call_media_tag_create(unsigned unique_id, JsonNode *json) { +static redis_call_media_tag_t *redis_call_media_tag_create(unsigned unique_id, JsonObject *json) { redis_call_media_tag_t *tagref = NULL; - JsonReader *reader = NULL; - reader = json_reader_new(json); tagref = obj_alloc0("redis_call_media_tag", sizeof(*tagref), redis_call_media_tag_free); tagref->unique_id = unique_id; - JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(reader, "created", tagref->created); - JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "active", tagref->active); - JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "deleted", tagref->deleted); - JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "block_dtmf", tagref->block_dtmf); - JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "block_media", tagref->block_media); - tagref->tag = json_reader_get_str(reader, "tag"); - tagref->viabranch = json_reader_get_str(reader, "viabranch"); - tagref->label = json_reader_get_str(reader, "label"); + JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(json, "created", tagref->created); + JSON_UPDATE_BOOL_FIELD_IF_SET(json, "active", tagref->active); + JSON_UPDATE_BOOL_FIELD_IF_SET(json, "deleted", tagref->deleted); + JSON_UPDATE_BOOL_FIELD_IF_SET(json, "block_dtmf", tagref->block_dtmf); + JSON_UPDATE_BOOL_FIELD_IF_SET(json, "block_media", tagref->block_media); + tagref->tag = json_object_get_str(json, "tag"); + tagref->viabranch = json_object_get_str(json, "viabranch"); + tagref->label = json_object_get_str(json, "label"); goto done; @@ -209,8 +199,6 @@ static redis_call_media_tag_t *redis_call_media_tag_create(unsigned unique_id, J } done: - if (reader) - g_object_unref(reader); return tagref; } @@ -238,7 +226,7 @@ static void redis_call_media_free(void* rcm) { g_queue_free_full(mediaref->codec_prefs_send, gdestroy_obj_put); } -static GQueue *redis_call_media_read_payloads(JsonNode* payloadTypes) { +static GQueue *redis_call_media_read_payloads(JsonArray* payload_types) { GQueue *out; JsonReader* reader = NULL; redis_call_rtp_payload_type_t *payload; @@ -248,11 +236,10 @@ static GQueue *redis_call_media_read_payloads(JsonNode* payloadTypes) { unsigned idx, pt; /* read payloads */ - reader = json_reader_new(payloadTypes); out = g_queue_new(); - payload_count = json_reader_count_elements(reader); + payload_count = json_array_get_length(payload_types); for (idx = 0; idx < payload_count; idx++) { - payload_str = json_reader_get_str_element(reader, idx); + payload_str = json_array_get_str(payload_types, idx); if (str_token(&ptype, payload_str, '/')) goto fail; @@ -282,33 +269,31 @@ static GQueue *redis_call_media_read_payloads(JsonNode* payloadTypes) { return out; } -static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode *json, GQueue *tags, GQueue *streams, - JsonNode *streamIds, JsonNode *endpointMaps, JsonNode *payloadTypesRecv, JsonNode *payloadTypesSend) { +static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonObject *json, GQueue *tags, GQueue *streams, + JsonArray *stream_ids_ar, JsonArray *endpoint_maps_ar, JsonArray *payload_types_recv_ar, JsonArray *payload_types_send_ar) { redis_call_media_t *mediaref = NULL; redis_call_media_tag_t *tagref = NULL; redis_call_media_stream_t *streamref = NULL; - JsonReader *reader = NULL; long long llval = 0; unsigned idx; - reader = json_reader_new(json); mediaref = obj_alloc0("redis_call_media", sizeof(*mediaref), redis_call_media_free); mediaref->unique_id = unique_id; - if ((llval = json_reader_get_ll(reader, "tag")) >= 0) { + if ((llval = json_object_get_ll(json, "tag")) >= 0) { tagref = g_queue_peek_nth(tags, llval); if (!tagref) goto fail; mediaref->tag = obj_get(tagref); } - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "index", mediaref->index); - mediaref->type = json_reader_get_str(reader, "type"); - mediaref->protocol = json_reader_get_str(reader, "protocol"); - mediaref->desired_family = json_reader_get_str(reader, "desired_family"); - mediaref->logical_intf = json_reader_get_str(reader, "logical_intf"); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "ptime", mediaref->ptime); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "media_flags", mediaref->media_flags); - mediaref->rtpe_addr = json_reader_get_str(reader, "rtpe_addr"); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "index", mediaref->index); + mediaref->type = json_object_get_str(json, "type"); + mediaref->protocol = json_object_get_str(json, "protocol"); + mediaref->desired_family = json_object_get_str(json, "desired_family"); + mediaref->logical_intf = json_object_get_str(json, "logical_intf"); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "ptime", mediaref->ptime); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "media_flags", mediaref->media_flags); + mediaref->rtpe_addr = json_object_get_str(json, "rtpe_addr"); /* grab my streams */ mediaref->streams = g_queue_new(); @@ -318,9 +303,9 @@ static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode g_queue_push_tail(mediaref->streams, obj_get(streamref)); } - if (!(mediaref->codec_prefs_recv = redis_call_media_read_payloads(payloadTypesRecv))) + if (!(mediaref->codec_prefs_recv = redis_call_media_read_payloads(payload_types_recv_ar))) goto fail; - if (!(mediaref->codec_prefs_send = redis_call_media_read_payloads(payloadTypesSend))) + if (!(mediaref->codec_prefs_send = redis_call_media_read_payloads(payload_types_send_ar))) goto fail; goto done; @@ -332,8 +317,6 @@ static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonNode } done: - if (reader) - g_object_unref(reader); return mediaref; } @@ -353,22 +336,20 @@ static void redis_call_free(void* rc) { free(callref->recording_metadata); } -static redis_call_t* redis_call_create_from_metadata(const str* callid, JsonNode* json) { +static redis_call_t* redis_call_create_from_metadata(const str* callid, JsonObject* json) { redis_call_t *callref = NULL; - JsonReader *reader = NULL; - reader = json_reader_new(json); callref = obj_alloc0("redis_call", sizeof(*callref), redis_call_free); callref->call_id = str_dup(callid); - JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(reader, "created", callref->created); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "last_signal", callref->last_signal); - JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "deleted", callref->deleted); - JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "ml_deleted", callref->ml_deleted); - callref->created_from = json_reader_get_str(reader, "created_from"); - callref->created_from_addr = json_reader_get_str(reader, "created_from_addr"); - JSON_UPDATE_NUM_FIELD_IF_SET(reader, "redis_hosted_db", callref->redis_hosted_db); - JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "block_dtmf", callref->block_dtmf); - JSON_UPDATE_BOOL_FIELD_IF_SET(reader, "block_media", callref->block_media); + JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(json, "created", callref->created); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "last_signal", callref->last_signal); + JSON_UPDATE_BOOL_FIELD_IF_SET(json, "deleted", callref->deleted); + JSON_UPDATE_BOOL_FIELD_IF_SET(json, "ml_deleted", callref->ml_deleted); + callref->created_from = json_object_get_str(json, "created_from"); + callref->created_from_addr = json_object_get_str(json, "created_from_addr"); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "redis_hosted_db", callref->redis_hosted_db); + JSON_UPDATE_BOOL_FIELD_IF_SET(json, "block_dtmf", callref->block_dtmf); + JSON_UPDATE_BOOL_FIELD_IF_SET(json, "block_media", callref->block_media); goto done; @@ -379,27 +360,20 @@ static redis_call_t* redis_call_create_from_metadata(const str* callid, JsonNode } done: - if (reader) - g_object_unref(reader); return callref; } -static int redis_call_match_tags(redis_call_media_tag_t *tag, GQueue *call_tags, JsonNode* json) { - JsonReader *reader; +static int redis_call_match_tags(redis_call_media_tag_t *tag, GQueue *call_tags, JsonArray *json) { redis_call_media_tag_t *other_tag; int status = 1; unsigned num_others, other_idx, other_tagid; - reader = json_reader_new(json); - if (!json_reader_is_array(reader)) - goto fail; - - num_others = json_reader_count_elements(reader); + num_others = json_array_get_length(json); if (num_others < 0) goto fail; for (other_idx = 0; other_idx < num_others; other_idx++) { - other_tagid = json_reader_get_ll_element(reader, other_idx); + other_tagid = json_array_get_ll(json, other_idx); if (other_tagid < 0) goto fail; other_tag = g_queue_peek_nth(call_tags, other_tagid); @@ -415,40 +389,39 @@ static int redis_call_match_tags(redis_call_media_tag_t *tag, GQueue *call_tags, status = 0; done: - if (reader) - g_object_unref(reader); return status; } -static GQueue *redis_call_read_tags(JsonReader *reader) { +static GQueue *redis_call_read_tags(JsonObject *json) { GQueue *call_tags = NULL; unsigned tag_idx, other_tags_idx; str *tag_field = NULL; redis_call_media_tag_t *tag = NULL; + JsonObject *tag_object = NULL; + JsonArray *othertags_ar = NULL; call_tags = g_queue_new(); for (tag_idx = 0; ; tag_idx++) { tag_field = str_sprintf("tag-%u", tag_idx); - if (!json_reader_read_member(reader, tag_field->s)) { + tag_object = json_object_get_object_member(json, tag_field->s); + if (!tag_object) break; /* no more tags */ - } - tag = redis_call_media_tag_create(tag_idx, json_reader_get_value(reader)); + tag = redis_call_media_tag_create(tag_idx, tag_object); if (!tag) goto fail; g_queue_push_tail(call_tags, tag); - json_reader_end_member(reader); free(tag_field); } for (other_tags_idx = 0; other_tags_idx < tag_idx; other_tags_idx++) { - json_reader_end_member(reader); free(tag_field); tag_field = str_sprintf("other_tags-%d", other_tags_idx); - if (json_reader_read_member(reader, tag_field->s)) { + othertags_ar = json_object_get_array_member(json, tag_field->s); + if (othertags_ar) { tag = g_queue_peek_nth(call_tags, other_tags_idx); if (!tag) /* shouldn't actually happen, but we're sanity-first! */ goto fail; - if (!redis_call_match_tags(tag, call_tags, json_reader_get_value(reader))) + if (!redis_call_match_tags(tag, call_tags, othertags_ar)) goto fail; } /* missing other_tags list is treated like an empty list */ } @@ -464,26 +437,26 @@ static GQueue *redis_call_read_tags(JsonReader *reader) { done: if (tag_field) free(tag_field); - json_reader_end_member(reader); return call_tags; } -static GQueue *redis_call_read_stream_fds(JsonReader *reader) { +static GQueue *redis_call_read_stream_fds(JsonObject *json) { GQueue *call_sfds; unsigned sfd_idx; str* sfd_field; + JsonObject *sfd_object; redis_call_media_stream_fd_t* sfd; call_sfds = g_queue_new(); for (sfd_idx = 0; ; sfd_idx++) { sfd_field = str_sprintf("sfd-%u", sfd_idx); - if (!json_reader_read_member(reader, sfd_field->s)) + sfd_object = json_object_get_object_member(json, sfd_field->s); + if (!sfd_object) goto done; /* no more sfds */ - sfd = redis_call_media_stream_fd_create(sfd_idx, json_reader_get_value(reader)); + sfd = redis_call_media_stream_fd_create(sfd_idx, sfd_object); if (!sfd) goto fail; g_queue_push_tail(call_sfds, sfd); - json_reader_end_member(reader); free(sfd_field); } @@ -500,28 +473,28 @@ static GQueue *redis_call_read_stream_fds(JsonReader *reader) { done: if (sfd_field) free(sfd_field); - json_reader_end_member(reader); return call_sfds; } -static GQueue *redis_call_read_streams(JsonReader *reader) { +static GQueue *redis_call_read_streams(JsonObject *json) { GQueue *call_streams = NULL, *call_sfds = NULL; unsigned stream_idx; str *stream_field = NULL; + JsonObject *stream_object = NULL; redis_call_media_stream_t *stream; - if (!(call_sfds = redis_call_read_stream_fds(reader))) + if (!(call_sfds = redis_call_read_stream_fds(json))) goto fail; call_streams = g_queue_new(); for (stream_idx = 0; ; stream_idx++) { stream_field = str_sprintf("stream-%u", stream_idx); - if (!json_reader_read_member(reader, stream_field->s)) + stream_object = json_object_get_object_member(json, stream_field->s); + if (!stream_object) goto done; /* no more streams */ - stream = redis_call_media_stream_create(stream_idx, json_reader_get_value(reader), call_sfds); + stream = redis_call_media_stream_create(stream_idx, stream_object, call_sfds); if (!stream) goto fail; g_queue_push_tail(call_streams, stream); - json_reader_end_member(reader); free(stream_field); } @@ -540,40 +513,40 @@ static GQueue *redis_call_read_streams(JsonReader *reader) { free(stream_field); if (call_sfds) g_queue_free_full(call_sfds, gdestroy_obj_put); - json_reader_end_member(reader); return call_streams; } -static GQueue *redis_call_read_media(JsonReader *reader) { +static GQueue *redis_call_read_media(JsonObject *json) { int media_idx; GQueue *call_media = NULL, *call_tags = NULL, *call_streams = NULL; - JsonNode *mediaNode, *streamIdsNode, *endpointMapsNode, *payloadTypesRecvNode, *payloadTypesSendNode; + JsonObject *media_object = NULL; + JsonArray *stream_ids_ar = NULL, *endpoint_maps_ar = NULL, *payload_types_recv_ar = NULL, *payload_types_send_ar = NULL; redis_call_media_t *media = NULL; char fieldname[50]; - if (!(call_tags = redis_call_read_tags(reader))) + if (!(call_tags = redis_call_read_tags(json))) goto fail; - if (!(call_streams = redis_call_read_streams(reader))) + if (!(call_streams = redis_call_read_streams(json))) goto fail; call_media = g_queue_new(); for (media_idx = 0; ; media_idx++) { snprintf(fieldname, sizeof(fieldname), "media-%u", media_idx); - mediaNode = json_reader_get_node(reader, fieldname); - if (!mediaNode) /* no more media */ + media_object = json_object_get_object_member(json, fieldname); + if (!media_object) /* no more media */ goto done; snprintf(fieldname, sizeof(fieldname), "streams-%u", media_idx); - streamIdsNode = json_reader_get_node(reader, fieldname); + stream_ids_ar = json_object_get_array_member(json, fieldname); snprintf(fieldname, sizeof(fieldname), "maps-%u", media_idx); - endpointMapsNode = json_reader_get_node(reader, fieldname); + endpoint_maps_ar = json_object_get_array_member(json, fieldname); snprintf(fieldname, sizeof(fieldname), "payload_types-%u", media_idx); - payloadTypesRecvNode = json_reader_get_node(reader, fieldname); + payload_types_recv_ar = json_object_get_array_member(json, fieldname); snprintf(fieldname, sizeof(fieldname), "payload_types_send-%u", media_idx); - payloadTypesSendNode = json_reader_get_node(reader, fieldname); - if (!streamIdsNode || !endpointMapsNode || !payloadTypesRecvNode || !payloadTypesSendNode) + payload_types_send_ar = json_object_get_array_member(json, fieldname); + if (!stream_ids_ar || !endpoint_maps_ar || !payload_types_recv_ar || !payload_types_send_ar) goto fail; - media = redis_call_media_create(media_idx, mediaNode, call_tags, call_streams, streamIdsNode, - endpointMapsNode, payloadTypesRecvNode, payloadTypesSendNode); + media = redis_call_media_create(media_idx, media_object, call_tags, call_streams, stream_ids_ar, + endpoint_maps_ar, payload_types_recv_ar, payload_types_send_ar); if (!media) goto fail; g_queue_push_tail(call_media, media); @@ -595,23 +568,24 @@ static GQueue *redis_call_read_media(JsonReader *reader) { } redis_call_t* redis_call_create(const str* callid, JsonNode* json) { - redis_call_t* callref = NULL; - JsonReader* root_reader = NULL; + redis_call_t *callref = NULL; + JsonObject *root = NULL; + JsonObject *metadata = NULL; const char *err = 0; - root_reader = json_reader_new(json); - if (!json_reader_read_member(root_reader, "json")) { + root = json_node_get_object(json); + metadata = json_object_get_object_member(root, "json"); + if (!metadata) { err = "Could not find call data"; goto fail; } - callref = redis_call_create_from_metadata(callid, json_reader_get_value(root_reader)); - json_reader_end_member(root_reader); + callref = redis_call_create_from_metadata(callid, metadata); if (!callref) { err = "Failed to read call data"; goto fail; } - if (!(callref->media = redis_call_read_media(root_reader))) { + if (!(callref->media = redis_call_read_media(root))) { err = "Failed to read call media"; goto fail; } @@ -630,7 +604,5 @@ redis_call_t* redis_call_create(const str* callid, JsonNode* json) { } done: - if (root_reader) - g_object_unref(root_reader); return callref; } diff --git a/daemon/redis.c b/daemon/redis.c index 5b7a86f32d..150817433b 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1814,7 +1814,7 @@ static void redis_update_call_details(struct redis *r, struct call *c) { goto done; fail: - rlog(LOG_WARNING, "Failed to update endpoints for call ID '" STR_FORMAT_M "' from Redis: %s", + rlog(LOG_WARNING, "Failed to update data for call ID '" STR_FORMAT_M "' from Redis: %s", STR_FMT_M(&c->callid), err); diff --git a/include/json-helpers.h b/include/json-helpers.h index 609a97e745..d0d05f09a9 100644 --- a/include/json-helpers.h +++ b/include/json-helpers.h @@ -12,7 +12,25 @@ * @return `str` string created from the string value, or `NULL` if no such value was found, * the reader is in an error state or not pointing to a JSON object. Release using `free()`. */ -str* json_reader_get_str(JsonReader *reader, const char *key); +str *json_reader_get_str(JsonReader *reader, const char *key); + +/** + * Retrieve a string value from a JSON object, using the JsonObject API, according to a key. + * @param json glib JsonObject from which to get the string + * @param key name of the string value to retrieve + * @return `str` string created from the string value, or `NULL` if no such value was found, + * the reader is in an error state or not pointing to a JSON object. Release using `free()`. + */ +str *json_object_get_str(JsonObject *json, const char *key); + +/** + * Retrieve a string value from a JSON list, using the JsonArray API, according to a key. + * @param json glib JsonArray from which to get the string + * @param idx index to the string value to retrieve + * @return `str` string created from the string value, or `NULL` if no such value was found, + * the reader is in an error state or not pointing to a JSON object. Release using `free()`. + */ +str *json_array_get_str(JsonArray *json, unsigned idx); /** * Retrieve an integer value from a JSON object according to a key. @@ -23,15 +41,24 @@ str* json_reader_get_str(JsonReader *reader, const char *key); */ long long json_reader_get_ll(JsonReader *reader, const char *key); +/** + * Retrieve an integer value from a JSON object, using the JsonObject API, according to a key. + * @param json glib JsonObject from which to get the integer + * @param key name of the integer value to retrieve + * @return integer value, if found, -1 otherwise. + * The widest possible "native" storage is used but depending on the original content, this might still result in data loss. + */ +long long json_object_get_ll(JsonObject *json, const char *key); + /** * Retrieve a string value from a JSON list according to an index. * This would also work on a JSON object, by retrieving values from keys ordered by storage order (but it is just weird). - * @param reader glib JsonReader that has the target list as its current node. + * @param reader glib JsonReader that has the target list as its current node * @param idx index to the string value to retrieve * @return `str` string created from the string value, or `NULL` if no such value was found, * the reader is in an error state or not pointing to a JSON list or object. Release using `free()`. */ -str* json_reader_get_str_element(JsonReader *reader, unsigned idx); +str *json_reader_get_str_element(JsonReader *reader, unsigned idx); /** * Retrieve an integer value from a JSON list according to an index. @@ -43,6 +70,16 @@ str* json_reader_get_str_element(JsonReader *reader, unsigned idx); */ long long json_reader_get_ll_element(JsonReader *reader, unsigned idx); +/** + * Retrieve an integer value from a JSON list, using the JsonArray API according to an index. + * If the value is stored as a string, this call will run `strtoll` on it and return the result. + * @param json glib JsonArray from which to get the integer.. + * @param idx index to the string value to retrieve + * @return integer value, if found, -1 otherwise. + * The widest possible "native" storage is used but depending on the original content, this might still result in data loss. + */ +long long json_array_get_ll(JsonArray *json, unsigned idx); + /** * Retrieve the current string value from a JSON reader and decode its URI encoding. * @param reader glib JsonReader whose current node is a string value @@ -59,6 +96,6 @@ str *json_reader_get_string_value_uri_enc(JsonReader *reader); * @param key name of the object or list value to retrieve * @return JSON node retrieved, if found, or `NULL` otherwise */ -JsonNode* json_reader_get_node(JsonReader *reader, const char *key); +JsonNode *json_reader_get_node(JsonReader *reader, const char *key); #endif /* __JSON_HELPERS_H__ */ diff --git a/include/redis-json.h b/include/redis-json.h index bece192ec0..9671c7bc21 100644 --- a/include/redis-json.h +++ b/include/redis-json.h @@ -49,6 +49,7 @@ typedef struct redis_call_media_stream { } redis_call_media_stream_t; typedef struct redis_call_rtp_payload_type { + struct obj obj; unsigned payload_type; str* codec_str; } redis_call_rtp_payload_type_t; From 4cd817caa63e2931f302b1c59d77df13be67c787 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Mon, 20 Jan 2020 21:51:43 +0200 Subject: [PATCH 17/50] more error logs --- daemon/redis-json.c | 114 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 90 insertions(+), 24 deletions(-) diff --git a/daemon/redis-json.c b/daemon/redis-json.c index 05c60a3fa4..24458234a1 100644 --- a/daemon/redis-json.c +++ b/daemon/redis-json.c @@ -3,8 +3,6 @@ #include "json-helpers.h" #include "log_funcs.h" -#define rlog(l, x...) printf(x) - #define JSON_UPDATE_NUM_FIELD_IF_SET(json, key, field) {\ long long llval = json_object_get_ll(json, key); \ if (llval >= 0) field = llval; \ @@ -235,18 +233,24 @@ static GQueue *redis_call_media_read_payloads(JsonArray* payload_types) { str ptype; unsigned idx, pt; + char *err = 0; + /* read payloads */ out = g_queue_new(); payload_count = json_array_get_length(payload_types); for (idx = 0; idx < payload_count; idx++) { payload_str = json_array_get_str(payload_types, idx); - if (str_token(&ptype, payload_str, '/')) + if (str_token(&ptype, payload_str, '/')) { + err = "No payload type in payload data"; goto fail; + } pt = str_to_ui(&ptype, 0); payload = redis_call_rtp_payload_type_create(pt, payload_str); - if (!payload) + if (!payload) { + err = "Failed to create payload"; goto fail; + } g_queue_push_tail(out, payload); free(payload_str); @@ -260,6 +264,9 @@ static GQueue *redis_call_media_read_payloads(JsonArray* payload_types) { g_queue_free_full(out, gdestroy_obj_put); out = NULL; } + if (err) { + ilog(LOG_WARNING, "Failed to read call data from Redis: %s", err); + } done: if (payload_str) @@ -275,6 +282,7 @@ static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonObjec redis_call_media_tag_t *tagref = NULL; redis_call_media_stream_t *streamref = NULL; + char *err = 0; long long llval = 0; unsigned idx; @@ -282,8 +290,10 @@ static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonObjec mediaref->unique_id = unique_id; if ((llval = json_object_get_ll(json, "tag")) >= 0) { tagref = g_queue_peek_nth(tags, llval); - if (!tagref) + if (!tagref) { + err = "Failed to find referenced tag when creating media"; goto fail; + } mediaref->tag = obj_get(tagref); } JSON_UPDATE_NUM_FIELD_IF_SET(json, "index", mediaref->index); @@ -303,10 +313,14 @@ static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonObjec g_queue_push_tail(mediaref->streams, obj_get(streamref)); } - if (!(mediaref->codec_prefs_recv = redis_call_media_read_payloads(payload_types_recv_ar))) + if (!(mediaref->codec_prefs_recv = redis_call_media_read_payloads(payload_types_recv_ar))) { + err = "Failed to read recv payloads"; goto fail; - if (!(mediaref->codec_prefs_send = redis_call_media_read_payloads(payload_types_send_ar))) + } + if (!(mediaref->codec_prefs_send = redis_call_media_read_payloads(payload_types_send_ar))) { + err = "Failed to read send payloads"; goto fail; + } goto done; @@ -315,6 +329,9 @@ static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonObjec obj_put(mediaref); mediaref = NULL; } + if (err) { + ilog(LOG_WARNING, "Failed to read call data from Redis: %s", err); + } done: return mediaref; @@ -368,17 +385,25 @@ static int redis_call_match_tags(redis_call_media_tag_t *tag, GQueue *call_tags, int status = 1; unsigned num_others, other_idx, other_tagid; + char *err = 0; + num_others = json_array_get_length(json); - if (num_others < 0) + if (num_others < 0) { + err = "Negative number of other tags members?!?"; goto fail; + } for (other_idx = 0; other_idx < num_others; other_idx++) { other_tagid = json_array_get_ll(json, other_idx); - if (other_tagid < 0) + if (other_tagid < 0) { + err = "Failed to read other tag id from tag list"; goto fail; + } other_tag = g_queue_peek_nth(call_tags, other_tagid); - if (!other_tag) + if (!other_tag) { + err = "Other tag id doesn't exist in tag list!"; goto fail; + } tag->other_tag = obj_get(other_tag); other_tag->other_tag = obj_get(tag); } @@ -387,6 +412,9 @@ static int redis_call_match_tags(redis_call_media_tag_t *tag, GQueue *call_tags, fail: status = 0; + if (err) { + ilog(LOG_WARNING, "Failed to read call data from Redis: %s", err); + } done: return status; @@ -400,6 +428,8 @@ static GQueue *redis_call_read_tags(JsonObject *json) { JsonObject *tag_object = NULL; JsonArray *othertags_ar = NULL; + char *err = 0; + call_tags = g_queue_new(); for (tag_idx = 0; ; tag_idx++) { tag_field = str_sprintf("tag-%u", tag_idx); @@ -407,8 +437,10 @@ static GQueue *redis_call_read_tags(JsonObject *json) { if (!tag_object) break; /* no more tags */ tag = redis_call_media_tag_create(tag_idx, tag_object); - if (!tag) + if (!tag) { + err = "Failed to create media tag"; goto fail; + } g_queue_push_tail(call_tags, tag); free(tag_field); } @@ -419,10 +451,14 @@ static GQueue *redis_call_read_tags(JsonObject *json) { othertags_ar = json_object_get_array_member(json, tag_field->s); if (othertags_ar) { tag = g_queue_peek_nth(call_tags, other_tags_idx); - if (!tag) /* shouldn't actually happen, but we're sanity-first! */ + if (!tag) {/* shouldn't actually happen, but we're sanity-first! */ + err = "The world is insane - no matching tag and other tag"; goto fail; - if (!redis_call_match_tags(tag, call_tags, othertags_ar)) + } + if (!redis_call_match_tags(tag, call_tags, othertags_ar)) { + err = "Failed to match call tags and other tags"; goto fail; + } } /* missing other_tags list is treated like an empty list */ } @@ -433,6 +469,9 @@ static GQueue *redis_call_read_tags(JsonObject *json) { g_queue_free_full(call_tags, gdestroy_obj_put); call_tags = NULL; } + if (err) { + ilog(LOG_WARNING, "Failed to read call data from Redis: %s", err); + } done: if (tag_field) @@ -447,6 +486,8 @@ static GQueue *redis_call_read_stream_fds(JsonObject *json) { JsonObject *sfd_object; redis_call_media_stream_fd_t* sfd; + char *err = 0; + call_sfds = g_queue_new(); for (sfd_idx = 0; ; sfd_idx++) { sfd_field = str_sprintf("sfd-%u", sfd_idx); @@ -454,8 +495,10 @@ static GQueue *redis_call_read_stream_fds(JsonObject *json) { if (!sfd_object) goto done; /* no more sfds */ sfd = redis_call_media_stream_fd_create(sfd_idx, sfd_object); - if (!sfd) + if (!sfd) { + err = "Failed to create stream fd"; goto fail; + } g_queue_push_tail(call_sfds, sfd); free(sfd_field); } @@ -469,6 +512,9 @@ static GQueue *redis_call_read_stream_fds(JsonObject *json) { g_queue_free_full(call_sfds, gdestroy_obj_put); call_sfds = NULL; } + if (err) { + ilog(LOG_WARNING, "Failed to read call data from Redis: %s", err); + } done: if (sfd_field) @@ -483,8 +529,12 @@ static GQueue *redis_call_read_streams(JsonObject *json) { JsonObject *stream_object = NULL; redis_call_media_stream_t *stream; - if (!(call_sfds = redis_call_read_stream_fds(json))) + char *err = 0; + + if (!(call_sfds = redis_call_read_stream_fds(json))) { + err = "Failed to read stream fds"; goto fail; + } call_streams = g_queue_new(); for (stream_idx = 0; ; stream_idx++) { stream_field = str_sprintf("stream-%u", stream_idx); @@ -492,8 +542,10 @@ static GQueue *redis_call_read_streams(JsonObject *json) { if (!stream_object) goto done; /* no more streams */ stream = redis_call_media_stream_create(stream_idx, stream_object, call_sfds); - if (!stream) + if (!stream) { + err = "Failed to create call media stream"; goto fail; + } g_queue_push_tail(call_streams, stream); free(stream_field); } @@ -507,6 +559,9 @@ static GQueue *redis_call_read_streams(JsonObject *json) { g_queue_free_full(call_streams, gdestroy_obj_put); call_streams = NULL; } + if (err) { + ilog(LOG_WARNING, "Failed to read call data from Redis: %s", err); + } done: if (stream_field) @@ -523,12 +578,17 @@ static GQueue *redis_call_read_media(JsonObject *json) { JsonArray *stream_ids_ar = NULL, *endpoint_maps_ar = NULL, *payload_types_recv_ar = NULL, *payload_types_send_ar = NULL; redis_call_media_t *media = NULL; + char *err = 0; char fieldname[50]; - if (!(call_tags = redis_call_read_tags(json))) + if (!(call_tags = redis_call_read_tags(json))) { + err = "Failed to read call tags"; goto fail; - if (!(call_streams = redis_call_read_streams(json))) + } + if (!(call_streams = redis_call_read_streams(json))) { + err = "Failed to read call streams"; goto fail; + } call_media = g_queue_new(); for (media_idx = 0; ; media_idx++) { snprintf(fieldname, sizeof(fieldname), "media-%u", media_idx); @@ -543,12 +603,16 @@ static GQueue *redis_call_read_media(JsonObject *json) { payload_types_recv_ar = json_object_get_array_member(json, fieldname); snprintf(fieldname, sizeof(fieldname), "payload_types_send-%u", media_idx); payload_types_send_ar = json_object_get_array_member(json, fieldname); - if (!stream_ids_ar || !endpoint_maps_ar || !payload_types_recv_ar || !payload_types_send_ar) + if (!stream_ids_ar || !endpoint_maps_ar || !payload_types_recv_ar || !payload_types_send_ar) { + err = "Missing streams, maps or payloads"; goto fail; + } media = redis_call_media_create(media_idx, media_object, call_tags, call_streams, stream_ids_ar, endpoint_maps_ar, payload_types_recv_ar, payload_types_send_ar); - if (!media) + if (!media) { + err = "Failed to create call media"; goto fail; + } g_queue_push_tail(call_media, media); } @@ -558,6 +622,9 @@ static GQueue *redis_call_read_media(JsonObject *json) { fail: if (call_media) g_queue_free_full(call_media, gdestroy_obj_put); + if (err) { + ilog(LOG_WARNING, "Failed to read call data from Redis: %s", err); + } done: if (call_tags) @@ -572,7 +639,7 @@ redis_call_t* redis_call_create(const str* callid, JsonNode* json) { JsonObject *root = NULL; JsonObject *metadata = NULL; - const char *err = 0; + char *err = 0; root = json_node_get_object(json); metadata = json_object_get_object_member(root, "json"); @@ -598,9 +665,8 @@ redis_call_t* redis_call_create(const str* callid, JsonNode* json) { callref = NULL; } if (err) { - rlog(LOG_WARNING, "Failed to read call data '" STR_FORMAT_M "' from Redis: %s", - STR_FMT_M(callid), - err); + ilog(LOG_WARNING, "Failed to read call data '" STR_FORMAT_M "' from Redis: %s", + STR_FMT_M(callid), err); } done: From 95f217453cc8dda578037fc1a412ea52e31d6fd7 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 21 Jan 2020 00:30:33 +0200 Subject: [PATCH 18/50] fixed inverse sanity test Now the world is sane again --- daemon/json-helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/json-helpers.c b/daemon/json-helpers.c index 34fae966a1..756a7f13e2 100644 --- a/daemon/json-helpers.c +++ b/daemon/json-helpers.c @@ -89,7 +89,7 @@ long long json_array_get_ll(JsonArray *json, unsigned idx) { long long out = -1; JsonNode *member; - if (json_array_get_length(json) >= idx) + if (idx >= json_array_get_length(json)) return out; member = json_array_get_element(json, idx); From 3f76b45c10e201e2629435a9ae1923d405d1022a Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 21 Jan 2020 13:13:02 +0200 Subject: [PATCH 19/50] Always use remote server payload negotiation results if available --- daemon/redis.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 150817433b..289cc63dc7 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1767,10 +1767,10 @@ static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) media = g_queue_peek_nth(redis_call->media, m->unique_id); if (!media) continue; /* weird... */ - if (m->codecs_prefs_recv.length == 0 && + if (g_queue_get_length(media->codec_prefs_recv) > 0 && redis_update_call_media_codecs(m, media->codec_prefs_recv, __rtp_payload_type_add_recv) > 0) updated = 1; - if (m->codecs_prefs_send.length == 0 && + if (g_queue_get_length(media->codec_prefs_recv) > 0 && redis_update_call_media_codecs(m, media->codec_prefs_send, __rtp_payload_type_add_send) > 0) updated = 1; } From 48b07adbf1eff4f251f7930922c2c3649590f772 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 21 Jan 2020 14:38:13 +0200 Subject: [PATCH 20/50] try to avoid "node is null" assertions --- daemon/json-helpers.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon/json-helpers.c b/daemon/json-helpers.c index 756a7f13e2..a39b8c70c9 100644 --- a/daemon/json-helpers.c +++ b/daemon/json-helpers.c @@ -39,7 +39,8 @@ str *json_reader_get_str(JsonReader *reader, const char *key) { str *json_object_get_str(JsonObject* json, const char *key) { const gchar *strval; str *out = NULL; - strval = json_object_get_string_member(json, key); + if (json_object_has_member(json, key)) + strval = json_object_get_string_member(json, key); if (strval) out = str_dup_charptr(strval); return out; From f5bc466d0803f5630d9bb0427011dce831419dfb Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 21 Jan 2020 15:53:54 +0200 Subject: [PATCH 21/50] read and update media endpoint maps --- daemon/redis-json.c | 94 ++++++++++++++++++++++++++++++++++++++++++-- daemon/redis.c | 33 +++++++++++++++- include/redis-json.h | 11 ++++++ 3 files changed, 134 insertions(+), 4 deletions(-) diff --git a/daemon/redis-json.c b/daemon/redis-json.c index 24458234a1..ac17cf7a98 100644 --- a/daemon/redis-json.c +++ b/daemon/redis-json.c @@ -160,6 +160,31 @@ static redis_call_rtp_payload_type_t *redis_call_rtp_payload_type_create(unsigne return payloadref; } +static void redis_call_media_endpoint_map_free(void *rcmem) { + redis_call_media_endpoint_map_t *mapref = rcmem; + if (!mapref) + return; + if (mapref->intf_preferred_family) + free(mapref->intf_preferred_family); + if (mapref->logical_intf) + free(mapref->logical_intf); + if (mapref->endpoint) + free(mapref->endpoint); +} + +static redis_call_media_endpoint_map_t *redis_call_media_endpoint_map_create(unsigned unique_id, JsonObject *json) { + redis_call_media_endpoint_map_t *mapref; + + mapref = obj_alloc0("redis_call_media_endpoint_map", sizeof(*mapref), redis_call_media_endpoint_map_free); + mapref->unique_id = unique_id; + mapref->wildcard = json_object_get_ll(json, "wildcard"); + JSON_UPDATE_NUM_FIELD_IF_SET(json, "num_ports", mapref->num_ports); + mapref->intf_preferred_family = json_object_get_str(json, "intf_preferred_family"); + mapref->logical_intf = json_object_get_str(json, "logical_intf"); + mapref->endpoint = json_object_get_str(json, "endpoint"); + return mapref; +} + static void redis_call_media_tag_free(void *rcmt) { redis_call_media_tag_t *tagref = rcmt; if (!tagref) @@ -216,6 +241,8 @@ static void redis_call_media_free(void* rcm) { free(mediaref->rtpe_addr); if (mediaref->tag) obj_put(mediaref->tag); + if (mediaref->endpoint_maps) + g_queue_free_full(mediaref->endpoint_maps, gdestroy_obj_put); if (mediaref->streams) g_queue_free_full(mediaref->streams, gdestroy_obj_put); if (mediaref->codec_prefs_recv) @@ -277,10 +304,12 @@ static GQueue *redis_call_media_read_payloads(JsonArray* payload_types) { } static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonObject *json, GQueue *tags, GQueue *streams, - JsonArray *stream_ids_ar, JsonArray *endpoint_maps_ar, JsonArray *payload_types_recv_ar, JsonArray *payload_types_send_ar) { + JsonArray *stream_ids_ar, GQueue* endpoint_maps, JsonArray *endpoint_maps_ar, JsonArray *payload_types_recv_ar, + JsonArray *payload_types_send_ar) { redis_call_media_t *mediaref = NULL; redis_call_media_tag_t *tagref = NULL; redis_call_media_stream_t *streamref = NULL; + redis_call_media_endpoint_map_t *mapref = NULL; char *err = 0; long long llval = 0; @@ -321,6 +350,16 @@ static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonObjec err = "Failed to read send payloads"; goto fail; } + mediaref->endpoint_maps = g_queue_new(); + for (idx = 0; idx < json_array_get_length(endpoint_maps_ar); idx++) { + unsigned map_id = json_array_get_ll(endpoint_maps_ar, idx); + mapref = g_queue_peek_nth(endpoint_maps, map_id); + if (!mapref) { + err = "Failed to find endpoint map for media"; + goto fail; + } + g_queue_push_tail(mediaref->endpoint_maps, obj_get(mapref)); + } goto done; @@ -571,9 +610,52 @@ static GQueue *redis_call_read_streams(JsonObject *json) { return call_streams; } +static GQueue *redis_call_read_media_endpoint_maps(JsonObject *json) { + GQueue *media_endpoint_maps; + unsigned endpoint_map_idx; + str *endpoint_map_field = NULL; + JsonObject *endpoint_map_object = NULL; + redis_call_media_endpoint_map_t *map; + + char *err = 0; + + media_endpoint_maps = g_queue_new(); + for (endpoint_map_idx =0; ; endpoint_map_idx++) { + endpoint_map_field = str_sprintf("map-%u", endpoint_map_idx); + endpoint_map_object = json_object_get_object_member(json, endpoint_map_field->s); + if (!endpoint_map_object) + goto done; /* no more maps */ + map = redis_call_media_endpoint_map_create(endpoint_map_idx, endpoint_map_object); + if (!map) { + err = "Failed to create call media endpoint map"; + goto fail; + } + g_queue_push_tail(media_endpoint_maps, map); + free(endpoint_map_field); + } + + /* we shouldn't reach this point, but just playing it safe */ + endpoint_map_field = NULL; + goto done; + +fail: + if (media_endpoint_maps) { + g_queue_free_full(media_endpoint_maps, gdestroy_obj_put); + media_endpoint_maps = NULL; + } + if (err) { + ilog(LOG_WARNING, "Failed to read call data from Redis: %s", err); + } + +done: + if (endpoint_map_field) + free(endpoint_map_field); + return media_endpoint_maps; +} + static GQueue *redis_call_read_media(JsonObject *json) { int media_idx; - GQueue *call_media = NULL, *call_tags = NULL, *call_streams = NULL; + GQueue *call_media = NULL, *call_tags = NULL, *call_streams = NULL, *media_endpoint_maps = NULL; JsonObject *media_object = NULL; JsonArray *stream_ids_ar = NULL, *endpoint_maps_ar = NULL, *payload_types_recv_ar = NULL, *payload_types_send_ar = NULL; redis_call_media_t *media = NULL; @@ -589,6 +671,10 @@ static GQueue *redis_call_read_media(JsonObject *json) { err = "Failed to read call streams"; goto fail; } + if (!(media_endpoint_maps = redis_call_read_media_endpoint_maps(json))) { + err = "Failed to read call media endpoint maps"; + goto fail; + } call_media = g_queue_new(); for (media_idx = 0; ; media_idx++) { snprintf(fieldname, sizeof(fieldname), "media-%u", media_idx); @@ -608,7 +694,7 @@ static GQueue *redis_call_read_media(JsonObject *json) { goto fail; } media = redis_call_media_create(media_idx, media_object, call_tags, call_streams, stream_ids_ar, - endpoint_maps_ar, payload_types_recv_ar, payload_types_send_ar); + media_endpoint_maps, endpoint_maps_ar, payload_types_recv_ar, payload_types_send_ar); if (!media) { err = "Failed to create call media"; goto fail; @@ -631,6 +717,8 @@ static GQueue *redis_call_read_media(JsonObject *json) { g_queue_free_full(call_tags, gdestroy_obj_put); if (call_streams) g_queue_free_full(call_streams, gdestroy_obj_put); + if (media_endpoint_maps) + g_queue_free_full(media_endpoint_maps, gdestroy_obj_put); return call_media; } diff --git a/daemon/redis.c b/daemon/redis.c index 289cc63dc7..299f97edd0 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1759,11 +1759,12 @@ static int redis_update_call_media_codecs(struct call_media *cm, GQueue* redis_c static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) { unsigned updated = 0; + struct call_media *m = NULL; redis_call_media_t *media; GList *l; for (l = c->medias.head; l; l = l->next) { - struct call_media *m = l->data; + m = l->data; media = g_queue_peek_nth(redis_call->media, m->unique_id); if (!media) continue; /* weird... */ @@ -1779,6 +1780,32 @@ static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) return 0; } +static int redis_update_call_maps(struct call *c, redis_call_t *redis_call) { + struct call_media *m = NULL; + struct endpoint_map *ep; + redis_call_media_t *media; + redis_call_media_endpoint_map_t *rcep; + GList *ml, *epl, *rcepl; + + for (ml = c->medias.head; ml; ml = ml->next) { + m = ml->data; + media = g_queue_peek_nth(redis_call->media, m->unique_id); + if (!media) + continue; /* weird... */ + for (epl = m->endpoint_maps.head; epl; epl = epl->next) { + ep = epl->data; + for (rcepl = media->endpoint_maps->head; rcepl; rcepl->next) { + rcep = rcepl->data; + if (rcep->unique_id != ep->unique_id) + continue; + ep->wildcard = rcep->wildcard; + endpoint_parse_any(&ep->endpoint, rcep->endpoint->s); + } + } + } + return 0; +} + static void redis_update_call_details(struct redis *r, struct call *c) { redisReply* rr_jsonStr; redis_call_t *redis_call = NULL; @@ -1811,6 +1838,10 @@ static void redis_update_call_details(struct redis *r, struct call *c) { if (redis_update_call_payloads(c, redis_call)) goto fail; + err = "failed to update maps"; + if (redis_update_call_maps(c, redis_call)) + goto fail; + goto done; fail: diff --git a/include/redis-json.h b/include/redis-json.h index 9671c7bc21..ef34bf89f8 100644 --- a/include/redis-json.h +++ b/include/redis-json.h @@ -54,6 +54,16 @@ typedef struct redis_call_rtp_payload_type { str* codec_str; } redis_call_rtp_payload_type_t; +typedef struct redis_call_media_endpoint_map { + struct obj obj; + unsigned unique_id; + int wildcard; + unsigned num_ports; + str* intf_preferred_family; + str* logical_intf; + str* endpoint; +} redis_call_media_endpoint_map_t; + struct redis_call_media_tag; typedef struct redis_call_media_tag { @@ -82,6 +92,7 @@ typedef struct redis_call_media { unsigned media_flags; str* rtpe_addr; redis_call_media_tag_t* tag; + GQueue* endpoint_maps; /**< list of redis_call_media_endpoint_map_t */ GQueue* streams; /**< list of redis_call_media_stream_t */ GQueue* codec_prefs_recv; /**< list of redis_call_rtp_payload_type_t */ GQueue* codec_prefs_send; /**< list of redis_call_rtp_payload_type_t */ From 766ced88b555533fe554ea75081fc2b0dd25dacd Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 21 Jan 2020 16:57:40 +0200 Subject: [PATCH 22/50] replace prefered codecs with those loaded from the database - assume the other side know what they are doing --- daemon/redis.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 299f97edd0..50e9ea469e 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1768,12 +1768,21 @@ static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) media = g_queue_peek_nth(redis_call->media, m->unique_id); if (!media) continue; /* weird... */ - if (g_queue_get_length(media->codec_prefs_recv) > 0 && - redis_update_call_media_codecs(m, media->codec_prefs_recv, __rtp_payload_type_add_recv) > 0) - updated = 1; - if (g_queue_get_length(media->codec_prefs_recv) > 0 && - redis_update_call_media_codecs(m, media->codec_prefs_send, __rtp_payload_type_add_send) > 0) - updated = 1; + /* replace codec prefs with those loaded from the database. */ + /* TODO: ATM the database does not encode them correctly, so we lose some data. */ + /* codec prefs destruction code copy-pasted from call.c - this should probably be refactored for reuse */ + if (g_queue_get_length(media->codec_prefs_recv) > 0) { + g_hash_table_destroy(m->codecs_recv); + g_hash_table_destroy(m->codec_names_recv); + g_queue_clear_full(&m->codecs_prefs_recv, (GDestroyNotify) payload_type_free); + updated += redis_update_call_media_codecs(m, media->codec_prefs_recv, __rtp_payload_type_add_recv); + } + if (g_queue_get_length(media->codec_prefs_recv) > 0) { + g_hash_table_destroy(m->codecs_send); + g_hash_table_destroy(m->codec_names_send); + g_queue_clear_full(&m->codecs_prefs_send, (GDestroyNotify) payload_type_free); + updated += redis_update_call_media_codecs(m, media->codec_prefs_send, __rtp_payload_type_add_send); + } } if (updated) rlog(LOG_INFO, "Updated media codecs from Redis"); From 121bc40f147404357e065a56b5113fbf8d1204d7 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 21 Jan 2020 17:32:17 +0200 Subject: [PATCH 23/50] dont kill the codec data structure, just clear it --- daemon/redis.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 50e9ea469e..9a3bfbada0 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1770,16 +1770,16 @@ static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) continue; /* weird... */ /* replace codec prefs with those loaded from the database. */ /* TODO: ATM the database does not encode them correctly, so we lose some data. */ - /* codec prefs destruction code copy-pasted from call.c - this should probably be refactored for reuse */ + /* maybe convert codec prefs cleanup code to use __delete_x_codec (which is currently static) */ if (g_queue_get_length(media->codec_prefs_recv) > 0) { - g_hash_table_destroy(m->codecs_recv); - g_hash_table_destroy(m->codec_names_recv); + g_hash_table_remove_all(m->codecs_recv); + g_hash_table_remove_all(m->codec_names_recv); g_queue_clear_full(&m->codecs_prefs_recv, (GDestroyNotify) payload_type_free); updated += redis_update_call_media_codecs(m, media->codec_prefs_recv, __rtp_payload_type_add_recv); } if (g_queue_get_length(media->codec_prefs_recv) > 0) { - g_hash_table_destroy(m->codecs_send); - g_hash_table_destroy(m->codec_names_send); + g_hash_table_remove_all(m->codecs_send); + g_hash_table_remove_all(m->codec_names_send); g_queue_clear_full(&m->codecs_prefs_send, (GDestroyNotify) payload_type_free); updated += redis_update_call_media_codecs(m, media->codec_prefs_send, __rtp_payload_type_add_send); } From 665503c281c9cfbfab77af5a634f48c46cc07e79 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 21 Jan 2020 18:13:46 +0200 Subject: [PATCH 24/50] Fixed some broken copy&paste Also debug log when we decide to update codecs on local call --- daemon/redis.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 9a3bfbada0..9e8a35978b 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1772,12 +1772,18 @@ static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) /* TODO: ATM the database does not encode them correctly, so we lose some data. */ /* maybe convert codec prefs cleanup code to use __delete_x_codec (which is currently static) */ if (g_queue_get_length(media->codec_prefs_recv) > 0) { + rlog(LOG_DEBUG, "['" STR_FORMAT_M "'] media %u: replacing %d local codec prefs recv with %d remote codec prefs", + STR_FMT_M(&c->callid), m->unique_id, g_queue_get_length(&m->codecs_prefs_recv), + g_queue_get_length(media->codec_prefs_recv)); g_hash_table_remove_all(m->codecs_recv); g_hash_table_remove_all(m->codec_names_recv); g_queue_clear_full(&m->codecs_prefs_recv, (GDestroyNotify) payload_type_free); updated += redis_update_call_media_codecs(m, media->codec_prefs_recv, __rtp_payload_type_add_recv); } - if (g_queue_get_length(media->codec_prefs_recv) > 0) { + if (g_queue_get_length(media->codec_prefs_send) > 0) { + rlog(LOG_DEBUG, "['" STR_FORMAT_M "'] media %u: replacing %d local codec prefs send with %d remote codec prefs", + STR_FMT_M(&c->callid), m->unique_id, g_queue_get_length(&m->codecs_prefs_send), + g_queue_get_length(media->codec_prefs_send)); g_hash_table_remove_all(m->codecs_send); g_hash_table_remove_all(m->codec_names_send); g_queue_clear_full(&m->codecs_prefs_send, (GDestroyNotify) payload_type_free); @@ -1803,7 +1809,7 @@ static int redis_update_call_maps(struct call *c, redis_call_t *redis_call) { continue; /* weird... */ for (epl = m->endpoint_maps.head; epl; epl = epl->next) { ep = epl->data; - for (rcepl = media->endpoint_maps->head; rcepl; rcepl->next) { + for (rcepl = media->endpoint_maps->head; rcepl; rcepl = rcepl->next) { rcep = rcepl->data; if (rcep->unique_id != ep->unique_id) continue; From 7cfd63003f226a0c675634ad7d882a8fb6ce99ac Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 21 Jan 2020 19:05:40 +0200 Subject: [PATCH 25/50] fixed reading str from object in case there is no such member --- daemon/json-helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/json-helpers.c b/daemon/json-helpers.c index a39b8c70c9..60ff9c6e0a 100644 --- a/daemon/json-helpers.c +++ b/daemon/json-helpers.c @@ -37,7 +37,7 @@ str *json_reader_get_str(JsonReader *reader, const char *key) { } str *json_object_get_str(JsonObject* json, const char *key) { - const gchar *strval; + const gchar *strval = NULL; str *out = NULL; if (json_object_has_member(json, key)) strval = json_object_get_string_member(json, key); From f1728d1b6f20cfc53e5e2eb92f7c859cb18706ea Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 21 Jan 2020 20:44:10 +0200 Subject: [PATCH 26/50] dont try to update call if there isnt any new information --- daemon/redis.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/daemon/redis.c b/daemon/redis.c index 9e8a35978b..6a6a77e39d 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1841,6 +1841,13 @@ static void redis_update_call_details(struct redis *r, struct call *c) { if (!redis_call) goto fail; + if (c->last_signal == redis_call->last_signal) { + rlog(LOG_INFO, "Ignoring Redis notification without update"); + goto done; + } + + c->last_signal = redis_call->last_signal; + err = "failed to update stream data"; if (redis_update_call_streams(c, redis_call)) goto fail; From 83af8205aeffa087c7a68a322eb6ac849c520c7f Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 21 Jan 2020 20:45:29 +0200 Subject: [PATCH 27/50] dont update codecs if the recieved data is the same length --- daemon/redis.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 6a6a77e39d..56b29aeab4 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1771,7 +1771,7 @@ static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) /* replace codec prefs with those loaded from the database. */ /* TODO: ATM the database does not encode them correctly, so we lose some data. */ /* maybe convert codec prefs cleanup code to use __delete_x_codec (which is currently static) */ - if (g_queue_get_length(media->codec_prefs_recv) > 0) { + if (g_queue_get_length(media->codec_prefs_recv) != g_queue_get_length(&m->codecs_prefs_recv)) { rlog(LOG_DEBUG, "['" STR_FORMAT_M "'] media %u: replacing %d local codec prefs recv with %d remote codec prefs", STR_FMT_M(&c->callid), m->unique_id, g_queue_get_length(&m->codecs_prefs_recv), g_queue_get_length(media->codec_prefs_recv)); @@ -1780,7 +1780,7 @@ static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) g_queue_clear_full(&m->codecs_prefs_recv, (GDestroyNotify) payload_type_free); updated += redis_update_call_media_codecs(m, media->codec_prefs_recv, __rtp_payload_type_add_recv); } - if (g_queue_get_length(media->codec_prefs_send) > 0) { + if (g_queue_get_length(media->codec_prefs_send) != g_queue_get_length(&m->codecs_prefs_send)) { rlog(LOG_DEBUG, "['" STR_FORMAT_M "'] media %u: replacing %d local codec prefs send with %d remote codec prefs", STR_FMT_M(&c->callid), m->unique_id, g_queue_get_length(&m->codecs_prefs_send), g_queue_get_length(media->codec_prefs_send)); From 4b2599218e3e1a351abf421d44343341091bc5fc Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 21 Jan 2020 23:09:19 +0200 Subject: [PATCH 28/50] update media flags and ptime --- daemon/redis.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/daemon/redis.c b/daemon/redis.c index 56b29aeab4..7e7bce12fe 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1817,6 +1817,10 @@ static int redis_update_call_maps(struct call *c, redis_call_t *redis_call) { endpoint_parse_any(&ep->endpoint, rcep->endpoint->s); } } + /* update some media fields here, while we have the media */ + if (!m->ptime) + m->ptime = media->ptime; + m->media_flags = media->media_flags; } return 0; } From 628220eba78ab62d9883f046e489777d22af692d Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Wed, 22 Jan 2020 19:41:30 +0200 Subject: [PATCH 29/50] access json_object properly to not cause asserts --- daemon/redis-json.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/daemon/redis-json.c b/daemon/redis-json.c index ac17cf7a98..a47d915cba 100644 --- a/daemon/redis-json.c +++ b/daemon/redis-json.c @@ -472,9 +472,9 @@ static GQueue *redis_call_read_tags(JsonObject *json) { call_tags = g_queue_new(); for (tag_idx = 0; ; tag_idx++) { tag_field = str_sprintf("tag-%u", tag_idx); - tag_object = json_object_get_object_member(json, tag_field->s); - if (!tag_object) + if (!json_object_has_member(json, tag_field->s)) break; /* no more tags */ + tag_object = json_object_get_object_member(json, tag_field->s); tag = redis_call_media_tag_create(tag_idx, tag_object); if (!tag) { err = "Failed to create media tag"; @@ -530,9 +530,9 @@ static GQueue *redis_call_read_stream_fds(JsonObject *json) { call_sfds = g_queue_new(); for (sfd_idx = 0; ; sfd_idx++) { sfd_field = str_sprintf("sfd-%u", sfd_idx); - sfd_object = json_object_get_object_member(json, sfd_field->s); - if (!sfd_object) + if (!json_object_has_member(json, sfd_field->s)) goto done; /* no more sfds */ + sfd_object = json_object_get_object_member(json, sfd_field->s); sfd = redis_call_media_stream_fd_create(sfd_idx, sfd_object); if (!sfd) { err = "Failed to create stream fd"; @@ -577,9 +577,9 @@ static GQueue *redis_call_read_streams(JsonObject *json) { call_streams = g_queue_new(); for (stream_idx = 0; ; stream_idx++) { stream_field = str_sprintf("stream-%u", stream_idx); - stream_object = json_object_get_object_member(json, stream_field->s); - if (!stream_object) + if (!json_object_has_member(json, stream_field->s)) goto done; /* no more streams */ + stream_object = json_object_get_object_member(json, stream_field->s); stream = redis_call_media_stream_create(stream_idx, stream_object, call_sfds); if (!stream) { err = "Failed to create call media stream"; @@ -622,9 +622,9 @@ static GQueue *redis_call_read_media_endpoint_maps(JsonObject *json) { media_endpoint_maps = g_queue_new(); for (endpoint_map_idx =0; ; endpoint_map_idx++) { endpoint_map_field = str_sprintf("map-%u", endpoint_map_idx); - endpoint_map_object = json_object_get_object_member(json, endpoint_map_field->s); - if (!endpoint_map_object) + if (!json_object_has_member(json, endpoint_map_field->s)) goto done; /* no more maps */ + endpoint_map_object = json_object_get_object_member(json, endpoint_map_field->s); map = redis_call_media_endpoint_map_create(endpoint_map_idx, endpoint_map_object); if (!map) { err = "Failed to create call media endpoint map"; @@ -678,9 +678,9 @@ static GQueue *redis_call_read_media(JsonObject *json) { call_media = g_queue_new(); for (media_idx = 0; ; media_idx++) { snprintf(fieldname, sizeof(fieldname), "media-%u", media_idx); - media_object = json_object_get_object_member(json, fieldname); - if (!media_object) /* no more media */ + if (!json_object_has_member(json, fieldname)) /* no more media */ goto done; + media_object = json_object_get_object_member(json, fieldname); snprintf(fieldname, sizeof(fieldname), "streams-%u", media_idx); stream_ids_ar = json_object_get_array_member(json, fieldname); snprintf(fieldname, sizeof(fieldname), "maps-%u", media_idx); From bb7ce60102dcabf31740096133082725703a68d0 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Sun, 26 Jan 2020 16:36:14 +0200 Subject: [PATCH 30/50] update codec handlers after we update media payloads This should set up the transcoder properly --- daemon/redis.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 7e7bce12fe..26f329dfeb 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1757,8 +1757,23 @@ static int redis_update_call_media_codecs(struct call_media *cm, GQueue* redis_c return updates; } +static void redis_update_call_codec_handlers(struct call_media *media) { + struct call_monologue *ml = media->monologue; + struct call_monologue *other_ml = ml->active_dialogue; + + for (GList *l = other_ml->medias.head; l; l = l->next) { + struct call_media *other_m = l->data; + if (other_m->index == media->index) { + rlog(LOG_INFO, "['" STR_FORMAT_M "'] media %u: updating codec handlers", + STR_FMT_M(&media->call->callid), media->unique_id); + codec_handlers_update(media, other_m, NULL); + break; + } + } +} + static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) { - unsigned updated = 0; + unsigned updated = 0, media_updates = 0; struct call_media *m = NULL; redis_call_media_t *media; @@ -1768,6 +1783,7 @@ static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) media = g_queue_peek_nth(redis_call->media, m->unique_id); if (!media) continue; /* weird... */ + media_updates = updated; /* replace codec prefs with those loaded from the database. */ /* TODO: ATM the database does not encode them correctly, so we lose some data. */ /* maybe convert codec prefs cleanup code to use __delete_x_codec (which is currently static) */ @@ -1789,6 +1805,9 @@ static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) g_queue_clear_full(&m->codecs_prefs_send, (GDestroyNotify) payload_type_free); updated += redis_update_call_media_codecs(m, media->codec_prefs_send, __rtp_payload_type_add_send); } + if (updated != media_updates) { + redis_update_call_codec_handlers(m); + } } if (updated) rlog(LOG_INFO, "Updated media codecs from Redis"); @@ -1860,14 +1879,14 @@ static void redis_update_call_details(struct redis *r, struct call *c) { if (redis_update_call_tags(c, redis_call)) goto fail; - err = "failed to update payload data"; - if (redis_update_call_payloads(c, redis_call)) - goto fail; - err = "failed to update maps"; if (redis_update_call_maps(c, redis_call)) goto fail; + err = "failed to update payload data"; + if (redis_update_call_payloads(c, redis_call)) + goto fail; + goto done; fail: From 03acc60aa84ac885ebe9d8de3d3eb5c02aac9191 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 28 Jan 2020 12:55:39 +0200 Subject: [PATCH 31/50] prevent memory leaks in json_restore_call() and actually do load crypto_params_sdes from JSON --- daemon/redis.c | 1 + 1 file changed, 1 insertion(+) diff --git a/daemon/redis.c b/daemon/redis.c index 26f329dfeb..d241872967 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1091,6 +1091,7 @@ static int redis_hash_get_sdes_params(GQueue *out, const struct redis_hash *h, c return 0; return -1; } + g_queue_push_tail(out, cps); snprintf(key, sizeof(key), "%s-%u", k, iter++); kk = key; From b768bc419db00a407c0cede616d431b34a2fd123 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 28 Jan 2020 12:53:02 +0200 Subject: [PATCH 32/50] add helpers to retrieve URI encoded data (likely binary) from json objects --- daemon/json-helpers.c | 8 ++++++++ include/json-helpers.h | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/daemon/json-helpers.c b/daemon/json-helpers.c index 60ff9c6e0a..b132693207 100644 --- a/daemon/json-helpers.c +++ b/daemon/json-helpers.c @@ -46,6 +46,14 @@ str *json_object_get_str(JsonObject* json, const char *key) { return out; } +str *json_object_get_str_uri_enc(JsonObject *json, const char *key) { + str *strval = NULL; + strval = json_object_get_str(json, key); + if (!strval) + return NULL; + return str_uri_decode_len(strval->s, strval->len); +} + str *json_array_get_str(JsonArray *json, unsigned idx) { const gchar *strval; str *out = NULL; diff --git a/include/json-helpers.h b/include/json-helpers.h index d0d05f09a9..2ddbddffa9 100644 --- a/include/json-helpers.h +++ b/include/json-helpers.h @@ -23,6 +23,16 @@ str *json_reader_get_str(JsonReader *reader, const char *key); */ str *json_object_get_str(JsonObject *json, const char *key); +/** + * Retrieve a string value from a JSON object, using the JsonObject API, according to a key, decoding the + * URI encoded value. The resulting buffer might contain NULL values. + * @param json glib JsonObject from which to get the string + * @param key name of the string value to retrieve + * @return `str` string created from the string value, or `NULL` if no such value was found, + * the reader is in an error state or not pointing to a JSON object. Release using `free()`. + */ +str *json_object_get_str_uri_enc(JsonObject *json, const char *key); + /** * Retrieve a string value from a JSON list, using the JsonArray API, according to a key. * @param json glib JsonArray from which to get the string From 4c6c1c6245f11bbf1eb29750fe01d68878816560 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 28 Jan 2020 12:53:34 +0200 Subject: [PATCH 33/50] Also update local calls with new crypto params --- daemon/redis-json.c | 119 +++++++++++++++++++++++++++++ daemon/redis.c | 174 +++++++++++++++++++++++++++++-------------- include/redis-json.h | 19 +++++ 3 files changed, 255 insertions(+), 57 deletions(-) diff --git a/daemon/redis-json.c b/daemon/redis-json.c index a47d915cba..9bef4230b2 100644 --- a/daemon/redis-json.c +++ b/daemon/redis-json.c @@ -225,6 +225,64 @@ static redis_call_media_tag_t *redis_call_media_tag_create(unsigned unique_id, J return tagref; } +static void redis_call_media_sdes_free(void *rcms) { + redis_call_media_sdes_t *sdesref = rcms; + if (!sdesref) + return; + if (sdesref->crypto_suite_name) + free(sdesref->crypto_suite_name); + if (sdesref->master_key) + free(sdesref->master_key); + if (sdesref->master_salt) + free(sdesref->master_salt); + if (sdesref->mki) + free(sdesref->mki); +} + +static redis_call_media_sdes_t *redis_call_media_sdes_create(const char *prefix, JsonObject* json) { + redis_call_media_sdes_t *sdesref; + str *fieldname; + + sdesref = obj_alloc0("redis_call_media_sdes", sizeof(*sdesref), redis_call_media_sdes_free); + fieldname = str_sprintf("%s_tag", prefix); + JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(json, fieldname->s, sdesref->tag); + free(fieldname); + fieldname = str_sprintf("%s-crypto_suite", prefix); + sdesref->crypto_suite_name = json_object_get_str(json, fieldname->s); + free(fieldname); + fieldname = str_sprintf("%s-master_key", prefix); + sdesref->master_key = json_object_get_str_uri_enc(json, fieldname->s); + free(fieldname); + fieldname = str_sprintf("%s-master_salt", prefix); + sdesref->master_salt = json_object_get_str_uri_enc(json, fieldname->s); + free(fieldname); + fieldname = str_sprintf("%s-mki", prefix); + sdesref->mki = json_object_get_str(json, fieldname->s); + free(fieldname); + fieldname = str_sprintf("%s-unenc-srtp", prefix); + JSON_UPDATE_NUM_FIELD_IF_SET(json, fieldname->s, sdesref->session_params.unencrypted_srtp); + free(fieldname); + fieldname = str_sprintf("%s-unenc-srtcp", prefix); + JSON_UPDATE_NUM_FIELD_IF_SET(json, fieldname->s, sdesref->session_params.unencrypted_srtcp); + free(fieldname); + fieldname = str_sprintf("%s-unauth-srtp", prefix); + JSON_UPDATE_NUM_FIELD_IF_SET(json, fieldname->s, sdesref->session_params.unauthenticated_srtp); + + goto done; + +fail: + ilog(LOG_WARNING, "Failed to read crypto params %s from Redis", prefix); + if (sdesref) { + obj_put(sdesref); + sdesref = NULL; + } + +done: + if (fieldname) + free(fieldname); + return sdesref; +} + static void redis_call_media_free(void* rcm) { redis_call_media_t *mediaref = rcm; if (!mediaref) @@ -249,6 +307,14 @@ static void redis_call_media_free(void* rcm) { g_queue_free_full(mediaref->codec_prefs_recv, gdestroy_obj_put); if (mediaref->codec_prefs_send) g_queue_free_full(mediaref->codec_prefs_send, gdestroy_obj_put); + if (mediaref->sdes_in) + g_queue_free_full(mediaref->sdes_in, gdestroy_obj_put); + if (mediaref->sdes_out) + g_queue_free_full(mediaref->sdes_out, gdestroy_obj_put); + if (mediaref->fingerprint.hash_func_name) + free(mediaref->fingerprint.hash_func_name); + if (mediaref->fingerprint.fingerprint) + free(mediaref->fingerprint.fingerprint); } static GQueue *redis_call_media_read_payloads(JsonArray* payload_types) { @@ -303,6 +369,51 @@ static GQueue *redis_call_media_read_payloads(JsonArray* payload_types) { return out; } +static GQueue* redis_call_media_try_read_sdes(const char* prefix, JsonObject *json) { + /* unlike all the other shit not-really-JSON that redis.c pulls, this time it encodes a list of items into the media + * object itself, where the list index is encoded as "-%u" between the "type prefix" and the field name - but only + * if its the second or later element. That code is fscking insane */ + GQueue *out = NULL; + redis_call_media_sdes_t *sdesref = NULL; + str *testfield = NULL; + int idx; + + /* check if we have any sdes for prefix */ + testfield = str_sprintf("%s_tag", prefix); + if (!json_object_has_member(json, testfield->s)) + goto done; /* nope */ + + out = g_queue_new(); + sdesref = redis_call_media_sdes_create(prefix, json); + if (!sdesref) { /* shouldn't happen, because we tested, but JSON might be broken */ + ilog(LOG_WARNING, "crypto params %s are broken", prefix); + goto fail; + } + + g_queue_push_tail(out, sdesref); + for (idx = 1; ; idx++) { + free(testfield); + testfield = str_sprintf("%s-%u", prefix, idx); + sdesref = redis_call_media_sdes_create(testfield->s, json); + if (!sdesref) /* no more crypto params */ + break; + g_queue_push_tail(out, sdesref); + } + + goto done; + +fail: + if (out) { + g_queue_free_full(out, gdestroy_obj_put); + out = NULL; + } + +done: + if (testfield) + free(testfield); + return out; +} + static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonObject *json, GQueue *tags, GQueue *streams, JsonArray *stream_ids_ar, GQueue* endpoint_maps, JsonArray *endpoint_maps_ar, JsonArray *payload_types_recv_ar, JsonArray *payload_types_send_ar) { @@ -334,6 +445,14 @@ static redis_call_media_t *redis_call_media_create(unsigned unique_id, JsonObjec JSON_UPDATE_NUM_FIELD_IF_SET(json, "media_flags", mediaref->media_flags); mediaref->rtpe_addr = json_object_get_str(json, "rtpe_addr"); + /* try to read crypto params, if exist */ + mediaref->sdes_in = redis_call_media_try_read_sdes("sdes_in", json); /* we get NULL on failure, which is what we want */ + mediaref->sdes_out = redis_call_media_try_read_sdes("sdes_out", json); + if (json_object_has_member(json, "hash_func")) { + mediaref->fingerprint.hash_func_name = json_object_get_str(json, "hash_func"); + mediaref->fingerprint.fingerprint = json_object_get_str_uri_enc(json, "fingerprint"); + } + /* grab my streams */ mediaref->streams = g_queue_new(); for (idx = 0; idx < g_queue_get_length(streams); idx++) { diff --git a/daemon/redis.c b/daemon/redis.c index d241872967..3a78d1eab2 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1773,75 +1773,139 @@ static void redis_update_call_codec_handlers(struct call_media *media) { } } -static int redis_update_call_payloads(struct call *c, redis_call_t *redis_call) { - unsigned updated = 0, media_updates = 0; - struct call_media *m = NULL; - redis_call_media_t *media; +static int redis_update_call_payloads(struct call_media *m, redis_call_media_t *media) { + unsigned updated = 0; + + /* replace codec prefs with those loaded from the database. */ + /* TODO: ATM the database does not encode them correctly, so we lose some data. */ + /* maybe convert codec prefs cleanup code to use __delete_x_codec (which is currently static) */ + if (g_queue_get_length(media->codec_prefs_recv) != g_queue_get_length(&m->codecs_prefs_recv)) { + rlog(LOG_INFO, "['" STR_FORMAT_M "'] media %u: replacing %d local codec prefs recv with %d remote codec prefs", + STR_FMT_M(&m->call->callid), m->unique_id, g_queue_get_length(&m->codecs_prefs_recv), + g_queue_get_length(media->codec_prefs_recv)); + g_hash_table_remove_all(m->codecs_recv); + g_hash_table_remove_all(m->codec_names_recv); + g_queue_clear_full(&m->codecs_prefs_recv, (GDestroyNotify) payload_type_free); + updated += redis_update_call_media_codecs(m, media->codec_prefs_recv, __rtp_payload_type_add_recv); + } + if (g_queue_get_length(media->codec_prefs_send) != g_queue_get_length(&m->codecs_prefs_send)) { + rlog(LOG_INFO, "['" STR_FORMAT_M "'] media %u: replacing %d local codec prefs send with %d remote codec prefs", + STR_FMT_M(&m->call->callid), m->unique_id, g_queue_get_length(&m->codecs_prefs_send), + g_queue_get_length(media->codec_prefs_send)); + g_hash_table_remove_all(m->codecs_send); + g_hash_table_remove_all(m->codec_names_send); + g_queue_clear_full(&m->codecs_prefs_send, (GDestroyNotify) payload_type_free); + updated += redis_update_call_media_codecs(m, media->codec_prefs_send, __rtp_payload_type_add_send); + } + if (updated) { + redis_update_call_codec_handlers(m); + rlog(LOG_INFO, "Updated media %u codecs from Redis", m->unique_id); + } + return 0; +} - GList *l; - for (l = c->medias.head; l; l = l->next) { - m = l->data; - media = g_queue_peek_nth(redis_call->media, m->unique_id); - if (!media) - continue; /* weird... */ - media_updates = updated; - /* replace codec prefs with those loaded from the database. */ - /* TODO: ATM the database does not encode them correctly, so we lose some data. */ - /* maybe convert codec prefs cleanup code to use __delete_x_codec (which is currently static) */ - if (g_queue_get_length(media->codec_prefs_recv) != g_queue_get_length(&m->codecs_prefs_recv)) { - rlog(LOG_DEBUG, "['" STR_FORMAT_M "'] media %u: replacing %d local codec prefs recv with %d remote codec prefs", - STR_FMT_M(&c->callid), m->unique_id, g_queue_get_length(&m->codecs_prefs_recv), - g_queue_get_length(media->codec_prefs_recv)); - g_hash_table_remove_all(m->codecs_recv); - g_hash_table_remove_all(m->codec_names_recv); - g_queue_clear_full(&m->codecs_prefs_recv, (GDestroyNotify) payload_type_free); - updated += redis_update_call_media_codecs(m, media->codec_prefs_recv, __rtp_payload_type_add_recv); +static int redis_update_call_maps(struct call_media *m, redis_call_media_t *media) { + struct endpoint_map *ep; + redis_call_media_endpoint_map_t *rcep; + GList *epl, *rcepl; + + for (epl = m->endpoint_maps.head; epl; epl = epl->next) { + ep = epl->data; + for (rcepl = media->endpoint_maps->head; rcepl; rcepl = rcepl->next) { + rcep = rcepl->data; + if (rcep->unique_id != ep->unique_id) + continue; + ep->wildcard = rcep->wildcard; + endpoint_parse_any(&ep->endpoint, rcep->endpoint->s); } - if (g_queue_get_length(media->codec_prefs_send) != g_queue_get_length(&m->codecs_prefs_send)) { - rlog(LOG_DEBUG, "['" STR_FORMAT_M "'] media %u: replacing %d local codec prefs send with %d remote codec prefs", - STR_FMT_M(&c->callid), m->unique_id, g_queue_get_length(&m->codecs_prefs_send), - g_queue_get_length(media->codec_prefs_send)); - g_hash_table_remove_all(m->codecs_send); - g_hash_table_remove_all(m->codec_names_send); - g_queue_clear_full(&m->codecs_prefs_send, (GDestroyNotify) payload_type_free); - updated += redis_update_call_media_codecs(m, media->codec_prefs_send, __rtp_payload_type_add_send); + } + /* update some media fields here, while we have the media */ + if (!m->ptime) + m->ptime = media->ptime; + m->media_flags = media->media_flags; + return 0; +} + +static void redis_update_call_crypto_sync_sdes_params(GQueue *m_sdes_q, GQueue *redis_sdes_q) { + redis_call_media_sdes_t *redis_sdes; + + crypto_params_sdes_queue_clear(m_sdes_q); + for (GList *l = redis_sdes_q->head; l; l = l->next) { + redis_sdes = l->data; + /** copied and modified from sdp.c:sdp_streams() */ + struct crypto_params_sdes *cps = g_slice_alloc0(sizeof(*cps)); + g_queue_push_tail(m_sdes_q, cps); + + if (redis_sdes->crypto_suite_name) + cps->params.crypto_suite = crypto_find_suite(redis_sdes->crypto_suite_name); + if (redis_sdes->mki) { + cps->params.mki_len = redis_sdes->mki->len; + if (cps->params.mki_len) { + cps->params.mki = malloc(cps->params.mki_len); + memcpy(cps->params.mki, redis_sdes->mki->s, cps->params.mki_len); + } } - if (updated != media_updates) { - redis_update_call_codec_handlers(m); + cps->tag = redis_sdes->tag; + if (redis_sdes->master_key) + memcpy(cps->params.master_key, redis_sdes->master_key->s, redis_sdes->master_key->len); + if (redis_sdes->master_salt) + memcpy(cps->params.master_salt, redis_sdes->master_salt->s, redis_sdes->master_salt->len); + cps->params.session_params = redis_sdes->session_params; + } +} + +static int redis_update_call_crypto(struct call_media *m, redis_call_media_t *media) { + const struct dtls_hash_func *found_hash_func; + + if (media->fingerprint.hash_func_name && !m->fingerprint.hash_func) { + /* json_restore_call() doesn't do this - should we? */ + found_hash_func = dtls_find_hash_func(media->fingerprint.hash_func_name); + if (found_hash_func) { + rlog(LOG_DEBUG, "Updating crypto for call ID '" STR_FORMAT_M "', media %u from Redis", + STR_FMT_M(&m->call->callid), m->unique_id); + m->fingerprint.hash_func = found_hash_func; + memcpy(m->fingerprint.digest, media->fingerprint.fingerprint->s, media->fingerprint.fingerprint->len); } } - if (updated) - rlog(LOG_INFO, "Updated media codecs from Redis"); + + if (media->sdes_in && m->sdes_in.length != media->sdes_in->length) + redis_update_call_crypto_sync_sdes_params(&m->sdes_in, media->sdes_in); + if (media->sdes_out && m->sdes_out.length != media->sdes_out->length) + redis_update_call_crypto_sync_sdes_params(&m->sdes_in, media->sdes_in); + return 0; } -static int redis_update_call_maps(struct call *c, redis_call_t *redis_call) { +static int redis_update_call_media(struct call *c, redis_call_t* redis_call) { struct call_media *m = NULL; - struct endpoint_map *ep; redis_call_media_t *media; - redis_call_media_endpoint_map_t *rcep; - GList *ml, *epl, *rcepl; + GList *ml; for (ml = c->medias.head; ml; ml = ml->next) { m = ml->data; media = g_queue_peek_nth(redis_call->media, m->unique_id); - if (!media) + if (!media) { + rlog(LOG_WARNING, "Failed to update data for call ID '" STR_FORMAT_M "' from Redis: missing media %u", + STR_FMT_M(&c->callid), m->unique_id); continue; /* weird... */ - for (epl = m->endpoint_maps.head; epl; epl = epl->next) { - ep = epl->data; - for (rcepl = media->endpoint_maps->head; rcepl; rcepl = rcepl->next) { - rcep = rcepl->data; - if (rcep->unique_id != ep->unique_id) - continue; - ep->wildcard = rcep->wildcard; - endpoint_parse_any(&ep->endpoint, rcep->endpoint->s); - } } - /* update some media fields here, while we have the media */ - if (!m->ptime) - m->ptime = media->ptime; - m->media_flags = media->media_flags; + if (redis_update_call_maps(m, media)) { + rlog(LOG_WARNING, "Failed to update data for call ID '" STR_FORMAT_M "' from Redis: error in update maps", + STR_FMT_M(&c->callid)); + return -1; + } + if (redis_update_call_payloads(m, media)) { + rlog(LOG_WARNING, "Failed to update data for call ID '" STR_FORMAT_M "' from Redis: error in update payloads", + STR_FMT_M(&c->callid)); + return -1; + } + if (redis_update_call_crypto(m, media)) { + rlog(LOG_WARNING, "Failed to update data for call ID '" STR_FORMAT_M "' from Redis: error in update crypto", + STR_FMT_M(&c->callid)); + return -1; + } } + return 0; } @@ -1880,12 +1944,8 @@ static void redis_update_call_details(struct redis *r, struct call *c) { if (redis_update_call_tags(c, redis_call)) goto fail; - err = "failed to update maps"; - if (redis_update_call_maps(c, redis_call)) - goto fail; - err = "failed to update payload data"; - if (redis_update_call_payloads(c, redis_call)) + if (redis_update_call_media(c, redis_call)) goto fail; goto done; diff --git a/include/redis-json.h b/include/redis-json.h index ef34bf89f8..7f10d2ded2 100644 --- a/include/redis-json.h +++ b/include/redis-json.h @@ -6,6 +6,7 @@ #include "obj.h" #include "str.h" +#include "crypto.h" /** * Document object model for mapping call data to storable JSON. @@ -80,6 +81,21 @@ typedef struct redis_call_media_tag { struct redis_call_media_tag* other_tag; } redis_call_media_tag_t; +typedef struct redis_call_media_sdes { + struct obj obj; + str* crypto_suite_name; + str* master_key; + str* master_salt; + str* mki; + struct crypto_session_params session_params; + unsigned tag; +} redis_call_media_sdes_t; + +struct redis_call_media_fingerprint { + str* hash_func_name; + str* fingerprint; +}; + typedef struct redis_call_media { struct obj obj; unsigned index; @@ -96,6 +112,9 @@ typedef struct redis_call_media { GQueue* streams; /**< list of redis_call_media_stream_t */ GQueue* codec_prefs_recv; /**< list of redis_call_rtp_payload_type_t */ GQueue* codec_prefs_send; /**< list of redis_call_rtp_payload_type_t */ + GQueue* sdes_in; /**< list of redis_call_media_sdes_t */ + GQueue* sdes_out; /**< list of redis_call_media_sdes_t */ + struct redis_call_media_fingerprint fingerprint; /* not an object reference */ } redis_call_media_t; typedef struct redis_call { From 16415ecd8b47bbab312ef53167519c222b13ca82 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 28 Jan 2020 17:04:03 +0200 Subject: [PATCH 34/50] when encoding more than 1 sdes param set per direction, even if we dont number the first param set, the second set should be numbered start from 1 (the first set is 0) --- daemon/redis.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 3a78d1eab2..a964828cd6 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1076,7 +1076,7 @@ static int redis_hash_get_sdes_params(GQueue *out, const struct redis_hash *h, c char key[32], tagkey[64]; const char *kk = k; unsigned int tag; - unsigned int iter = 0; + unsigned int iter = 1; while (1) { snprintf(tagkey, sizeof(tagkey), "%s_tag", kk); @@ -2078,7 +2078,7 @@ static int json_update_sdes_params(JsonBuilder *builder, const char *pref, const char *k, GQueue *q) { char tmp[2048]; - unsigned int iter = 0; + unsigned int iter = 1; char keybuf[32]; const char *key = k; From 19a39ec0feea7fa9a54c3ce8388f1eff29d3cd1d Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Wed, 29 Jan 2020 11:30:17 +0200 Subject: [PATCH 35/50] test if sdes param set exists before attempting to read it I dont need the extra warnings on read failure every time, especially when expected --- daemon/redis-json.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/daemon/redis-json.c b/daemon/redis-json.c index 9bef4230b2..19722228bd 100644 --- a/daemon/redis-json.c +++ b/daemon/redis-json.c @@ -375,7 +375,7 @@ static GQueue* redis_call_media_try_read_sdes(const char* prefix, JsonObject *js * if its the second or later element. That code is fscking insane */ GQueue *out = NULL; redis_call_media_sdes_t *sdesref = NULL; - str *testfield = NULL; + str *testfield = NULL, *prefixfield = NULL; int idx; /* check if we have any sdes for prefix */ @@ -393,10 +393,17 @@ static GQueue* redis_call_media_try_read_sdes(const char* prefix, JsonObject *js g_queue_push_tail(out, sdesref); for (idx = 1; ; idx++) { free(testfield); - testfield = str_sprintf("%s-%u", prefix, idx); - sdesref = redis_call_media_sdes_create(testfield->s, json); - if (!sdesref) /* no more crypto params */ + prefixfield = str_sprintf("%s-%u", prefix, idx); + /* check if we have more sdes for prefix */ + testfield = str_sprintf("%s_tag", prefixfield->s); + if (!json_object_has_member(json, testfield->s)) + goto done; /* nope */ + sdesref = redis_call_media_sdes_create(prefixfield->s, json); + if (!sdesref) { + ilog(LOG_WARNING, "crypto params %s are broken", prefixfield->s); break; + } + free(prefixfield); g_queue_push_tail(out, sdesref); } @@ -411,6 +418,8 @@ static GQueue* redis_call_media_try_read_sdes(const char* prefix, JsonObject *js done: if (testfield) free(testfield); + if (prefixfield) + free(prefixfield); return out; } From e767e59538efa6c34c755978de1ca262422475dd Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Wed, 29 Jan 2020 15:24:16 +0200 Subject: [PATCH 36/50] when reading unsigned from redis, use strtoul because otherwise we lose data --- daemon/redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/redis.c b/daemon/redis.c index a964828cd6..1e969915b2 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -873,7 +873,7 @@ static struct timeval strtotimeval(const char *c, char **endp, int base) { define_get_int_type(time_t, time_t, strtoull); define_get_int_type(timeval, struct timeval, strtotimeval); define_get_int_type(int, int, strtol); -define_get_int_type(unsigned, unsigned int, strtol); +define_get_int_type(unsigned, unsigned int, strtoul); //define_get_int_type(u16, u_int16_t, strtol); //define_get_int_type(u64, u_int64_t, strtoull); define_get_int_type(a64, atomic64, strtoa64); From b0b0288b8d3e6f486c274af5e7f268d3930000f9 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Wed, 29 Jan 2020 16:08:46 +0200 Subject: [PATCH 37/50] only try to parse endpoints if there is something to parse otherwise, lets try to avoid parsing "" as "0.0.0.0:0", as that doesnt make sense. --- daemon/redis.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 1e969915b2..b7b9eaf3e4 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -923,8 +923,8 @@ static int redis_hash_get_endpoint(struct endpoint *out, const struct redis_hash if (redis_hash_get_str(&s, h, k)) return -1; - if (endpoint_parse_any(out, s.s)) - return -1; + if (s.len && endpoint_parse_any(out, s.s)) + return -1; return 0; } @@ -1699,8 +1699,10 @@ static int redis_update_call_streams(struct call *c, redis_call_t *redis_call) { ps = pk->data; ZERO(endpoint); stream = g_queue_peek_nth(redis_call_streams, i); - endpoint_parse_any(&endpoint, stream->endpoint->s); - endpoint_parse_any(&advertised_endpoint, stream->advertised_endpoint->s); + if (stream->endpoint->len) + endpoint_parse_any(&endpoint, stream->endpoint->s); + if (stream->advertised_endpoint->len) + endpoint_parse_any(&advertised_endpoint, stream->advertised_endpoint->s); if (!ps->endpoint.port && endpoint.port && endpoint.address.family->af) { ps->endpoint = endpoint; @@ -1816,7 +1818,8 @@ static int redis_update_call_maps(struct call_media *m, redis_call_media_t *medi if (rcep->unique_id != ep->unique_id) continue; ep->wildcard = rcep->wildcard; - endpoint_parse_any(&ep->endpoint, rcep->endpoint->s); + if (rcep->endpoint->len) + endpoint_parse_any(&ep->endpoint, rcep->endpoint->s); } } /* update some media fields here, while we have the media */ From 14abc076cf3e7aa10d502f933b24610de346d2a6 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Wed, 29 Jan 2020 16:46:35 +0200 Subject: [PATCH 38/50] fixed payload type encoding/decoding by having codec.c create the encoded format in the same way it will later parse it, then just use it in redis.c --- daemon/codec.c | 12 +++++++++++- daemon/redis.c | 15 +++++++-------- include/codec.h | 1 + 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/daemon/codec.c b/daemon/codec.c index b0f8d86a2e..d1ee199727 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -1157,7 +1157,17 @@ void codec_packet_free(void *pp) { g_slice_free1(sizeof(*p), p); } - +str *codec_print_payload_type(const struct rtp_payload_type* pt) { + return str_sprintf( + "%s/" /* encoding */ + "%u/" /* clock_rate */ + "%i/" /* channels */ + "%i/" /* bitrate (opts) */ + "%i/" /* ptime (extra_opts) */ + "%s/" /* format_parameters(fmt_params) */ + /* the last part must end with '/', otherwise codec_make_payload_type won't read it*/ + ,pt->encoding.s, pt->clock_rate, pt->channels, pt->bitrate, pt->ptime, pt->format_parameters.s); +} struct rtp_payload_type *codec_make_payload_type(const str *codec_str, struct call_media *media) { str codec_fmt = *codec_str; diff --git a/daemon/redis.c b/daemon/redis.c index b7b9eaf3e4..13fdee7ee2 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -2139,6 +2139,7 @@ char* redis_encode_json(struct call *c) { struct call_monologue *ml, *ml2; JsonBuilder *builder = json_builder_new (); struct recording *rec = 0; + str *ptbuf; char tmp[2048]; @@ -2362,10 +2363,9 @@ char* redis_encode_json(struct call *c) { json_builder_begin_array (builder); for (m = media->codecs_prefs_recv.head; m; m = m->next) { pt = m->data; - JSON_ADD_STRING("%u/" STR_FORMAT "/%u/" STR_FORMAT "/" STR_FORMAT "/%i/%i", - pt->payload_type, STR_FMT(&pt->encoding), - pt->clock_rate, STR_FMT(&pt->encoding_parameters), - STR_FMT(&pt->format_parameters), pt->bitrate, pt->ptime); + ptbuf = codec_print_payload_type(pt); + JSON_ADD_STRING("%u/" STR_FORMAT, pt->payload_type, STR_FMT(ptbuf)); + free(ptbuf); } json_builder_end_array (builder); @@ -2374,10 +2374,9 @@ char* redis_encode_json(struct call *c) { json_builder_begin_array (builder); for (m = media->codecs_prefs_send.head; m; m = m->next) { pt = m->data; - JSON_ADD_STRING("%u/" STR_FORMAT "/%u/" STR_FORMAT "/" STR_FORMAT "/%i/%i", - pt->payload_type, STR_FMT(&pt->encoding), - pt->clock_rate, STR_FMT(&pt->encoding_parameters), - STR_FMT(&pt->format_parameters), pt->bitrate, pt->ptime); + ptbuf = codec_print_payload_type(pt); + JSON_ADD_STRING("%u/" STR_FORMAT, pt->payload_type, STR_FMT(ptbuf)); + free(ptbuf); } json_builder_end_array (builder); } diff --git a/include/codec.h b/include/codec.h index 17e0bc0193..a0e9aade08 100644 --- a/include/codec.h +++ b/include/codec.h @@ -60,6 +60,7 @@ void codec_packet_free(void *); void codec_rtp_payload_types(struct call_media *media, struct call_media *other_media, GQueue *types, struct sdp_ng_flags *flags); +str *codec_print_payload_type(const struct rtp_payload_type* pt); // special return value `(void *) 0x1` to signal type mismatch struct rtp_payload_type *codec_make_payload_type(const str *codec_str, struct call_media *media); void codec_init_payload_type(struct rtp_payload_type *, struct call_media *); From 5392f752711fee6ef7fd30769d5dc9f5df72cc3a Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Tue, 11 Feb 2020 20:46:25 +0200 Subject: [PATCH 39/50] use new parser to update dtls information instead of the missing implementation in json_media() --- daemon/redis.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/daemon/redis.c b/daemon/redis.c index 13fdee7ee2..6f8538c027 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -84,6 +84,7 @@ static int redisCommandNR(redisContext *r, const char *fmt, ...) static int redis_check_conn(struct redis *r); static void json_restore_call(struct redis *r, const str *id, enum call_type type); static void redis_update_call_details(struct redis *r, struct call *call); +static int redis_update_call_crypto(struct call_media *m, redis_call_media_t *media); static int redis_connect(struct redis *r, int wait); static void redis_pipe(struct redis *r, const char *fmt, ...) { @@ -1644,6 +1645,25 @@ static void json_restore_call(struct redis *r, const str *callid, enum call_type recording_start(c, s.s, &meta); } + /* because json_medias() failed to implement dtls updates, and I can't suffer to re-implement it their way, just use our new parser */ + redis_call_t *redis_call = redis_call_create(&c->callid, json_parser_get_root(parser)); + err = "new parser failed to parse JSON data"; + if (!redis_call) + goto err1; + for (GList *ml = c->medias.head; ml; ml = ml->next) { + struct call_media *m = ml->data; + redis_call_media_t *media = g_queue_peek_nth(redis_call->media, m->unique_id); + if (!media) { + rlog(LOG_WARNING, "Failed to update data for call ID '" STR_FORMAT_M "' from Redis: missing media %u", + STR_FMT_M(&c->callid), m->unique_id); + continue; /* weird... */ + } + if (redis_update_call_crypto(m, media)) { + rlog(LOG_WARNING, "Failed to update data for call ID '" STR_FORMAT_M "' from Redis: error in update crypto", + STR_FMT_M(&c->callid)); + } + } + err = NULL; err8: From ddf379bf6164c3028d37c3009d29444880a4c1a6 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Wed, 12 Feb 2020 13:55:20 +0200 Subject: [PATCH 40/50] store last_signal as timeval we need better resolution to properly ignore high speed redis updates being sent on the shared channel (now that updates are sent both ways) --- daemon/call.c | 4 ++-- daemon/call_interfaces.c | 2 +- daemon/cdr.c | 2 +- daemon/cli.c | 2 +- daemon/media_socket.c | 2 +- daemon/redis-json.c | 15 +++++++++++++-- daemon/redis.c | 8 ++++---- include/call.h | 2 +- include/redis-json.h | 4 ++-- 9 files changed, 26 insertions(+), 15 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 742c8c13a0..dfc22c668a 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -150,7 +150,7 @@ static void call_timer_iterator(struct call *c, struct iterator_helper *hlp) { } if (c->deleted && rtpe_now.tv_sec >= c->deleted - && c->last_signal <= c->deleted) + && c->last_signal.tv_sec <= c->deleted) goto delete; if (c->ml_deleted && rtpe_now.tv_sec >= c->ml_deleted) { @@ -1830,7 +1830,7 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, monologue = other_ml->active_dialogue; call = monologue->call; - call->last_signal = rtpe_now.tv_sec; + call->last_signal = rtpe_now; call->deleted = 0; __C_DBG("this="STR_FORMAT" other="STR_FORMAT, STR_FMT(&monologue->tag), STR_FMT(&other_ml->tag)); diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 77b5266a22..87ad37cc71 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1361,7 +1361,7 @@ void ng_call_stats(struct call *call, const str *fromtag, const str *totag, benc bencode_dictionary_add_integer(output, "created", call->created.tv_sec); bencode_dictionary_add_integer(output, "created_us", call->created.tv_usec); - bencode_dictionary_add_integer(output, "last signal", call->last_signal); + bencode_dictionary_add_integer(output, "last signal", call->last_signal.tv_sec); ng_stats_ssrc(bencode_dictionary_add_dictionary(output, "SSRC"), call->ssrc_hash); tags = bencode_dictionary_add_dictionary(output, "tags"); diff --git a/daemon/cdr.c b/daemon/cdr.c index be13145692..59d619f080 100644 --- a/daemon/cdr.c +++ b/daemon/cdr.c @@ -58,7 +58,7 @@ void cdr_update_entry(struct call* c) { ADJUSTLEN(printlen,cdrbufend,cdrbufcur); printlen = snprintf(cdrbufcur,CDRBUFREMAINDER,"created_from=%s, ", c->created_from); ADJUSTLEN(printlen,cdrbufend,cdrbufcur); - printlen = snprintf(cdrbufcur,CDRBUFREMAINDER,"last_signal=%llu, ", (unsigned long long)c->last_signal); + printlen = snprintf(cdrbufcur,CDRBUFREMAINDER,"last_signal=%llu, ", (unsigned long long)c->last_signal.tv_sec); ADJUSTLEN(printlen,cdrbufend,cdrbufcur); printlen = snprintf(cdrbufcur,CDRBUFREMAINDER,"tos=%u, ", (unsigned int)c->tos); ADJUSTLEN(printlen,cdrbufend,cdrbufcur); diff --git a/daemon/cli.c b/daemon/cli.c index dab17ea539..b0480c239c 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -613,7 +613,7 @@ static void cli_incoming_list_callid(str *instr, struct streambuf *replybuffer) } streambuf_printf(replybuffer, "\ncallid: %60s | deletionmark:%4s | created:%12i | proxy:%s | tos:%u | last_signal:%llu | redis_keyspace:%i | foreign:%s\n\n", - c->callid.s , c->ml_deleted?"yes":"no", (int)c->created.tv_sec, c->created_from, (unsigned int)c->tos, (unsigned long long)c->last_signal, c->redis_hosted_db, IS_FOREIGN_CALL(c)?"yes":"no"); + c->callid.s , c->ml_deleted?"yes":"no", (int)c->created.tv_sec, c->created_from, (unsigned int)c->tos, (unsigned long long)c->last_signal.tv_sec, c->redis_hosted_db, IS_FOREIGN_CALL(c)?"yes":"no"); for (l = c->monologues.head; l; l = l->next) { ml = l->data; diff --git a/daemon/media_socket.c b/daemon/media_socket.c index 57c25f8c75..0b172f06b6 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -1613,7 +1613,7 @@ static int media_packet_address_check(struct packet_handler_ctx *phc) /* wait at least 3 seconds after last signal before committing to a particular * endpoint address */ - if (!phc->mp.call->last_signal || rtpe_now.tv_sec <= phc->mp.call->last_signal + 3) + if (!phc->mp.call->last_signal.tv_sec || rtpe_now.tv_sec <= phc->mp.call->last_signal.tv_sec + 3) goto update_peerinfo; confirm_now: diff --git a/daemon/redis-json.c b/daemon/redis-json.c index 19722228bd..7476489e8d 100644 --- a/daemon/redis-json.c +++ b/daemon/redis-json.c @@ -25,6 +25,17 @@ else goto fail; \ } +#define JSON_UPDATE_TVAL_FIELD_IF_SET(json, key, field) {\ + long long llval = json_object_get_ll(json, key); \ + if (llval >= 0) timeval_from_us(&field, llval); \ +} + +#define JSON_UPDATE_TVAL_FIELD_IF_SET_OR_FAIL(json, key, field) {\ + long long llval = json_object_get_ll(json, key); \ + if (llval >= 0) timeval_from_us(&field, llval); \ + else goto fail; \ +} + /** * Helper for using obj_put as a (*GDestroyNotify) parameter for glib. * @@ -525,8 +536,8 @@ static redis_call_t* redis_call_create_from_metadata(const str* callid, JsonObje callref = obj_alloc0("redis_call", sizeof(*callref), redis_call_free); callref->call_id = str_dup(callid); - JSON_UPDATE_NUM_FIELD_IF_SET_OR_FAIL(json, "created", callref->created); - JSON_UPDATE_NUM_FIELD_IF_SET(json, "last_signal", callref->last_signal); + JSON_UPDATE_TVAL_FIELD_IF_SET_OR_FAIL(json, "created", callref->created); + JSON_UPDATE_TVAL_FIELD_IF_SET(json, "last_signal", callref->last_signal); JSON_UPDATE_BOOL_FIELD_IF_SET(json, "deleted", callref->deleted); JSON_UPDATE_BOOL_FIELD_IF_SET(json, "ml_deleted", callref->ml_deleted); callref->created_from = json_object_get_str(json, "created_from"); diff --git a/daemon/redis.c b/daemon/redis.c index 6f8538c027..19ba8f71bf 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1557,7 +1557,7 @@ static void json_restore_call(struct redis *r, const str *callid, enum call_type goto err1; err = "call already exists"; - if (c->last_signal) + if (c->last_signal.tv_sec) goto err2; err = "'call' data incomplete"; @@ -1583,7 +1583,7 @@ static void json_restore_call(struct redis *r, const str *callid, enum call_type if (redis_hash_get_timeval(&c->created, &call, "created")) goto err8; err = "missing 'last signal' timestamp"; - if (redis_hash_get_time_t(&c->last_signal, &call, "last_signal")) + if (redis_hash_get_timeval(&c->last_signal, &call, "last_signal")) goto err8; if (redis_hash_get_int(&i, &call, "tos")) c->tos = 184; @@ -1952,7 +1952,7 @@ static void redis_update_call_details(struct redis *r, struct call *c) { if (!redis_call) goto fail; - if (c->last_signal == redis_call->last_signal) { + if (timeval_us(&c->last_signal) == timeval_us(&redis_call->last_signal)) { rlog(LOG_INFO, "Ignoring Redis notification without update"); goto done; } @@ -2171,7 +2171,7 @@ char* redis_encode_json(struct call *c) { { JSON_SET_SIMPLE("created","%lli", timeval_us(&c->created)); - JSON_SET_SIMPLE("last_signal","%ld",(long int) c->last_signal); + JSON_SET_SIMPLE("last_signal","%lli",timeval_us(&c->last_signal)); JSON_SET_SIMPLE("tos","%u",(int) c->tos); JSON_SET_SIMPLE("deleted","%ld",(long int) c->deleted); JSON_SET_SIMPLE("num_sfds","%u",g_queue_get_length(&c->stream_fds)); diff --git a/include/call.h b/include/call.h index dda3170cfd..6f917716cc 100644 --- a/include/call.h +++ b/include/call.h @@ -377,7 +377,7 @@ struct call { str callid; struct timeval created; - time_t last_signal; + struct timeval last_signal; time_t deleted; time_t ml_deleted; unsigned char tos; diff --git a/include/redis-json.h b/include/redis-json.h index 7f10d2ded2..5b1d78659e 100644 --- a/include/redis-json.h +++ b/include/redis-json.h @@ -120,8 +120,8 @@ typedef struct redis_call_media { typedef struct redis_call { struct obj obj; str* call_id; - unsigned long long created; - unsigned long last_signal; + struct timeval created; + struct timeval last_signal; unsigned tos; gboolean deleted; gboolean ml_deleted; From cd432e9edb2a660df50c18a713b0f4049b966c02 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Wed, 12 Feb 2020 14:38:11 +0200 Subject: [PATCH 41/50] handle foreign updates like local updates (to ignore self-updates) refactor the handling of the redis "set" event so that if we answer for a foreign call and send an update, we can also ignore our update. In the future we may be able to completely drop the json_restore_call() path and use the more flexible redis-json implementation for all reading --- daemon/redis.c | 125 +++++++++++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 50 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 19ba8f71bf..0e25b31b14 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -82,8 +82,7 @@ static int redisCommandNR(redisContext *r, const char *fmt, ...) #define REDIS_FMT(x) (int) (x)->len, (x)->str static int redis_check_conn(struct redis *r); -static void json_restore_call(struct redis *r, const str *id, enum call_type type); -static void redis_update_call_details(struct redis *r, struct call *call); +static void redis_update_call(str *callid, struct redis *r, struct call *call); static int redis_update_call_crypto(struct call_media *m, redis_call_media_t *media); static int redis_connect(struct redis *r, int wait); @@ -366,15 +365,8 @@ void on_redis_notification(redisAsyncContext *actx, void *reply, void *privdata) c = call_get(&callid); if (c) { rwlock_unlock_w(&c->master_lock); - if (IS_FOREIGN_CALL(c)) - call_destroy(c); - else { - rlog(LOG_WARN, "Trying to read new endpoints from redis"); - redis_update_call_details(r, c); - goto err; // this no longer an error, but we'll still go there to bypass json_restore_call - } } - json_restore_call(r, &callid, CT_FOREIGN_CALL); + redis_update_call(&callid, r, c); } if (strncmp(rr->element[3]->str,"del",3)==0) { @@ -1525,8 +1517,7 @@ static int json_build_ssrc(struct call *c, JsonReader *root_reader) { return 0; } -static void json_restore_call(struct redis *r, const str *callid, enum call_type type) { - redisReply* rr_jsonStr; +static void json_restore_call(JsonParser *parser, const str *callid, enum call_type type) { struct redis_hash call; struct redis_list tags, sfds, streams, medias, maps; struct call *c = NULL; @@ -1535,17 +1526,7 @@ static void json_restore_call(struct redis *r, const str *callid, enum call_type const char *err = 0; int i; JsonReader *root_reader =0; - JsonParser *parser =0; - rr_jsonStr = redis_get(r, REDIS_REPLY_STRING, "GET " PB, STR(callid)); - err = "could not retrieve JSON data from redis"; - if (!rr_jsonStr) - goto err1; - - parser = json_parser_new(); - err = "could not parse JSON data"; - if (!json_parser_load_from_data (parser, rr_jsonStr->str, -1, NULL)) - goto err1; root_reader = json_reader_new (json_parser_get_root (parser)); err = "could not read JSON data"; if (!root_reader) @@ -1683,11 +1664,6 @@ static void json_restore_call(struct redis *r, const str *callid, enum call_type err1: if (root_reader) g_object_unref (root_reader); - if (parser) - g_object_unref (parser); - if (rr_jsonStr) - freeReplyObject(rr_jsonStr); - log_info_clear(); if (err) { rlog(LOG_WARNING, "Failed to restore call ID '" STR_FORMAT_M "' from Redis: %s", STR_FMT_M(callid), @@ -1881,7 +1857,6 @@ static int redis_update_call_crypto(struct call_media *m, redis_call_media_t *me const struct dtls_hash_func *found_hash_func; if (media->fingerprint.hash_func_name && !m->fingerprint.hash_func) { - /* json_restore_call() doesn't do this - should we? */ found_hash_func = dtls_find_hash_func(media->fingerprint.hash_func_name); if (found_hash_func) { rlog(LOG_DEBUG, "Updating crypto for call ID '" STR_FORMAT_M "', media %u from Redis", @@ -1932,29 +1907,12 @@ static int redis_update_call_media(struct call *c, redis_call_t* redis_call) { return 0; } -static void redis_update_call_details(struct redis *r, struct call *c) { - redisReply* rr_jsonStr; - redis_call_t *redis_call = NULL; +static void redis_update_call_details(redis_call_t *redis_call, struct call *c) { const char *err = NULL; - JsonParser *parser = NULL; - - rr_jsonStr = redis_get(r, REDIS_REPLY_STRING, "GET " PB, STR(&c->callid)); - err = "could not retrieve JSON data from redis"; - if (!rr_jsonStr) - goto fail; - parser = json_parser_new(); - err = "could not parse JSON data"; - if (!json_parser_load_from_data (parser, rr_jsonStr->str, -1, NULL)) - goto fail; - redis_call = redis_call_create(&c->callid, json_parser_get_root(parser)); - err = "could not read JSON data"; - if (!redis_call) - goto fail; - - if (timeval_us(&c->last_signal) == timeval_us(&redis_call->last_signal)) { + if (timeval_us(&c->last_signal) >= timeval_us(&redis_call->last_signal)) { rlog(LOG_INFO, "Ignoring Redis notification without update"); - goto done; + return; } c->last_signal = redis_call->last_signal; @@ -1971,13 +1929,53 @@ static void redis_update_call_details(struct redis *r, struct call *c) { if (redis_update_call_media(c, redis_call)) goto fail; - goto done; + return; fail: rlog(LOG_WARNING, "Failed to update data for call ID '" STR_FORMAT_M "' from Redis: %s", STR_FMT_M(&c->callid), err); +} + +static void redis_update_call(str *callid, struct redis *r, struct call *call) { + redisReply* rr_jsonStr; + redis_call_t *redis_call = NULL; + const char *err = NULL; + JsonParser *parser = NULL; + + rr_jsonStr = redis_get(r, REDIS_REPLY_STRING, "GET " PB, STR(callid)); + err = "could not retrieve JSON data from redis"; + if (!rr_jsonStr) + goto fail; + + rlog(LOG_INFO, "Received call '" STR_FORMAT_M "' data from redis, call looks like this: %s", + STR_FMT_M(callid), rr_jsonStr->str); + + parser = json_parser_new(); + err = "could not parse JSON data"; + if (!json_parser_load_from_data (parser, rr_jsonStr->str, -1, NULL)) + goto fail; + redis_call = redis_call_create(callid, json_parser_get_root(parser)); + err = "could not read JSON data"; + if (!redis_call) + goto fail; + + if (!call) { /* a new call published by the primary */ + /* call the old reader (for now) as it knows how to create a call from scratch */ + /* TODO: verify the new reader in call create scenarios and dump the old code */ + rlog(LOG_INFO, "Creating a new call from redis"); + json_restore_call(parser, callid, CT_FOREIGN_CALL); + goto done; + } + + rlog(LOG_INFO, "Updating call data from redis"); + redis_update_call_details(redis_call, call); + goto done; + +fail: + rlog(LOG_WARNING, "Failed to update data for call ID '" STR_FORMAT_M "' from Redis: %s", + STR_FMT_M(callid), err); done: if (redis_call) obj_put(redis_call); @@ -1988,6 +1986,33 @@ static void redis_update_call_details(struct redis *r, struct call *c) { log_info_clear(); } +static void json_parse_end_restore_call(struct redis *r, str *callid, enum call_type type) { + redisReply* rr_jsonStr; + const char *err = NULL; + JsonParser *parser = NULL; + + rr_jsonStr = redis_get(r, REDIS_REPLY_STRING, "GET " PB, STR(callid)); + err = "could not retrieve JSON data from redis"; + if (!rr_jsonStr) + goto fail; + + parser = json_parser_new(); + err = "could not parse JSON data"; + if (!json_parser_load_from_data (parser, rr_jsonStr->str, -1, NULL)) + goto fail; + json_restore_call(parser, callid, type); + goto done; +fail: + rlog(LOG_WARNING, "Failed to restore call ID '" STR_FORMAT_M "' from Redis: %s", + STR_FMT_M(callid), err); +done: + if (parser) + g_object_unref (parser); + if (rr_jsonStr) + freeReplyObject(rr_jsonStr); + log_info_clear(); +} + struct thread_ctx { GQueue r_q; mutex_t r_m; @@ -2006,7 +2031,7 @@ static void restore_thread(void *call_p, void *ctx_p) { r = g_queue_pop_head(&ctx->r_q); mutex_unlock(&ctx->r_m); - json_restore_call(r, &callid, CT_OWN_CALL); + json_parse_end_restore_call(r, &callid, CT_OWN_CALL); mutex_lock(&ctx->r_m); g_queue_push_tail(&ctx->r_q, r); From a992290c3a5ddc99c2ec8943ffb55bee50adb8b6 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Thu, 13 Feb 2020 11:57:57 +0200 Subject: [PATCH 42/50] fixed incorrect copypasta for crypto update --- daemon/redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/redis.c b/daemon/redis.c index 0e25b31b14..9573f24229 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1869,7 +1869,7 @@ static int redis_update_call_crypto(struct call_media *m, redis_call_media_t *me if (media->sdes_in && m->sdes_in.length != media->sdes_in->length) redis_update_call_crypto_sync_sdes_params(&m->sdes_in, media->sdes_in); if (media->sdes_out && m->sdes_out.length != media->sdes_out->length) - redis_update_call_crypto_sync_sdes_params(&m->sdes_in, media->sdes_in); + redis_update_call_crypto_sync_sdes_params(&m->sdes_out, media->sdes_out); return 0; } From d30ff16db71bf1923644db1c434f37987983828b Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Thu, 13 Feb 2020 14:47:47 +0200 Subject: [PATCH 43/50] read stream last_packet time, dont just fake it --- daemon/redis.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/daemon/redis.c b/daemon/redis.c index 9573f24229..86106d0e4a 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1165,6 +1165,7 @@ static int redis_streams(struct call *c, struct redis_list *streams) { unsigned int i; struct redis_hash *rh; struct packet_stream *ps; + unsigned int ps_last_packet; for (i = 0; i < streams->len; i++) { rh = &streams->rh[i]; @@ -1173,7 +1174,8 @@ static int redis_streams(struct call *c, struct redis_list *streams) { if (!ps) return -1; - atomic64_set_na(&ps->last_packet, time(NULL)); + redis_hash_get_unsigned(&ps_last_packet, rh, "last_packet"); + atomic64_set_na(&ps->last_packet, ps_last_packet); if (redis_hash_get_unsigned((unsigned int *) &ps->ps_flags, rh, "ps_flags")) return -1; if (redis_hash_get_unsigned((unsigned int *) &ps->component, rh, "component")) From 642329d4210eb0d071e75cdf26b99637f5bd2b30 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Thu, 13 Feb 2020 17:31:09 +0200 Subject: [PATCH 44/50] do crypto reinit after updated sdes params --- daemon/redis.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 86106d0e4a..53de8e41a7 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1857,6 +1857,8 @@ static void redis_update_call_crypto_sync_sdes_params(GQueue *m_sdes_q, GQueue * static int redis_update_call_crypto(struct call_media *m, redis_call_media_t *media) { const struct dtls_hash_func *found_hash_func; + int needReinitCrypto = 0; + AUTO_CLEANUP_BUF(paramsbuf); if (media->fingerprint.hash_func_name && !m->fingerprint.hash_func) { found_hash_func = dtls_find_hash_func(media->fingerprint.hash_func_name); @@ -1868,10 +1870,36 @@ static int redis_update_call_crypto(struct call_media *m, redis_call_media_t *me } } - if (media->sdes_in && m->sdes_in.length != media->sdes_in->length) + if (media->sdes_in && m->sdes_in.length != media->sdes_in->length) { + rlog(LOG_DEBUG, "Need update input crypto"); redis_update_call_crypto_sync_sdes_params(&m->sdes_in, media->sdes_in); - if (media->sdes_out && m->sdes_out.length != media->sdes_out->length) + ++needReinitCrypto; + } + if (media->sdes_out && m->sdes_out.length != media->sdes_out->length) { + rlog(LOG_DEBUG, "Need update output crypto"); redis_update_call_crypto_sync_sdes_params(&m->sdes_out, media->sdes_out); + ++needReinitCrypto; + } + + if (!needReinitCrypto) + return 0; + + /* re-init crypto context after update. No easy access to call.c's methods so here is a copy of __init_stream() */ + for (GList *ml = m->streams.head; ml; ml = ml->next) { + struct packet_stream* ps = ml->data; + for (GList *l = ps->sfds.head; l; l = l->next) { + struct stream_fd *sfd = l->data; + struct crypto_params_sdes *cps = m->sdes_in.head ? m->sdes_in.head->data : NULL; + crypto_init(&sfd->crypto, cps ? &cps->params : NULL); + ilog(LOG_DEBUG, "[%s] Initialized incoming SRTP with SDES crypto params: %s%s%s", + endpoint_print_buf(&sfd->socket.local), + FMT_M(crypto_params_sdes_dump(cps, ¶msbuf))); + } + struct crypto_params_sdes *cps = m->sdes_out.head ? m->sdes_out.head->data : NULL; + crypto_init(&ps->crypto, cps ? &cps->params : NULL); + ilog(LOG_DEBUG, "[%i] Initialized outgoing SRTP with SDES crypto params: %s%s%s", + ps->component, FMT_M(crypto_params_sdes_dump(cps, ¶msbuf))); + } return 0; } From 7a21daf5d897c251cb1d34cb7453374923765bf0 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Sun, 16 Feb 2020 11:17:07 +0200 Subject: [PATCH 45/50] cleanup addressing packet streams --- daemon/redis.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 53de8e41a7..49dfd6bc21 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1685,7 +1685,7 @@ static void json_restore_call(JsonParser *parser, const str *callid, enum call_t static int redis_update_call_streams(struct call *c, redis_call_t *redis_call) { GQueue* redis_call_streams; redis_call_media_stream_t *stream; - unsigned int i, updated = 0; + unsigned int updated = 0; struct packet_stream *ps; GList *pk; struct endpoint endpoint, advertised_endpoint; @@ -1693,10 +1693,10 @@ static int redis_update_call_streams(struct call *c, redis_call_t *redis_call) { if (!(redis_call_streams = redis_call_get_streams(redis_call))) return -1; // review call streams and only update where needed - for (i = 0, pk = c->streams.head; pk && (i < redis_call_streams->length); pk = pk->next, i++) { + for (pk = c->streams.head; pk; pk = pk->next) { ps = pk->data; ZERO(endpoint); - stream = g_queue_peek_nth(redis_call_streams, i); + stream = g_queue_peek_nth(redis_call_streams, ps->unique_id); if (stream->endpoint->len) endpoint_parse_any(&endpoint, stream->endpoint->s); if (stream->advertised_endpoint->len) From 48abafe6932d5e8333ef11d4affcecaf0554b768 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Sun, 16 Feb 2020 14:07:21 +0200 Subject: [PATCH 46/50] remove double push that got in in one of the merges --- daemon/redis.c | 1 - 1 file changed, 1 deletion(-) diff --git a/daemon/redis.c b/daemon/redis.c index 71eb51b3c1..3dd85cd687 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1086,7 +1086,6 @@ static int redis_hash_get_sdes_params(GQueue *out, const struct redis_hash *h, c } g_queue_push_tail(out, cps); - g_queue_push_tail(out, cps); snprintf(key, sizeof(key), "%s-%u", k, iter++); kk = key; } From 4de34035b9216687fd648d23ba15f0684661b451 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Sun, 16 Feb 2020 16:35:05 +0200 Subject: [PATCH 47/50] add missing forward declaration that was lost in the merge --- daemon/redis.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon/redis.c b/daemon/redis.c index 3dd85cd687..ab6934f595 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -85,6 +85,7 @@ static int redis_check_conn(struct redis *r); static void redis_update_call(str *callid, struct redis *r, struct call *call); static int redis_update_call_crypto(struct call_media *m, redis_call_media_t *media); static int redis_connect(struct redis *r, int wait); +static char* redis_encode_json(struct call *c); static void redis_pipe(struct redis *r, const char *fmt, ...) { va_list ap; @@ -2202,7 +2203,7 @@ static void json_update_dtls_fingerprint(JsonBuilder *builder, const char *pref, * encodes the few (k,v) pairs for one call under one json structure */ -char* redis_encode_json(struct call *c) { +static char* redis_encode_json(struct call *c) { GList *l=0,*k=0, *m=0, *n=0; struct endpoint_map *ep; From 5f882f947bcd0ab887cdbcd37329017cec11b6b7 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Wed, 18 Mar 2020 18:33:51 +0200 Subject: [PATCH 48/50] correctly handle foreign server update of a re-invite offer on existing call id --- daemon/redis.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/daemon/redis.c b/daemon/redis.c index ab6934f595..93f2d60104 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1992,6 +1992,13 @@ static void redis_update_call(str *callid, struct redis *r, struct call *call) { if (!redis_call) goto fail; + if (call && timeval_us(&redis_call->created) > timeval_us(&call->created)) { + rlog(LOG_INFO, "Foreign server took ownership of call!"); + call_destroy(call); + obj_put(call); + call = NULL; + } + if (!call) { /* a new call published by the primary */ /* call the old reader (for now) as it knows how to create a call from scratch */ /* TODO: verify the new reader in call create scenarios and dump the old code */ From 0977a95108083e091e538b5280185506ba59abb0 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Thu, 19 Mar 2020 15:55:45 +0200 Subject: [PATCH 49/50] Revert "correctly handle foreign server update of a re-invite offer on existing call id" This reverts commit 9b9a1655efa4fc84c88ea35f0da468f8091be8ad. After removing "call restart in case of repeat offer", this is no longer needed. --- daemon/redis.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 93f2d60104..ab6934f595 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1992,13 +1992,6 @@ static void redis_update_call(str *callid, struct redis *r, struct call *call) { if (!redis_call) goto fail; - if (call && timeval_us(&redis_call->created) > timeval_us(&call->created)) { - rlog(LOG_INFO, "Foreign server took ownership of call!"); - call_destroy(call); - obj_put(call); - call = NULL; - } - if (!call) { /* a new call published by the primary */ /* call the old reader (for now) as it knows how to create a call from scratch */ /* TODO: verify the new reader in call create scenarios and dump the old code */ From 0db425735952908cd275258361a06fc5a87dceb2 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Thu, 19 Mar 2020 15:55:18 +0200 Subject: [PATCH 50/50] Dont restart call on second offer Reverts commit e04367036d48437ca1eedafd83536b021c6c3eae. If we get a second offer, and it is a retry, new redis data allows us to reply with the original server reply, so no need to restart the session. --- daemon/call_interfaces.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 87ad37cc71..3905ba514b 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -965,22 +965,8 @@ static const char *call_offer_answer_ng(bencode_item_t *input, /* OP_ANSWER; OP_OFFER && !IS_FOREIGN_CALL */ call = call_get(&flags.call_id); - /* Failover scenario because of timeout on offer response: siprouter tries - * to establish session with another rtpengine2 even though rtpengine1 - * might have persisted part of the session. rtpengine2 deletes previous - * call in memory and recreates an OWN call in redis */ - // SDP fragments for trickle ICE must always operate on an existing call if (opmode == OP_OFFER && !flags.fragment) { - if (call) { - if (IS_FOREIGN_CALL(call)) { - /* destroy call and create new one */ - rwlock_unlock_w(&call->master_lock); - call_destroy(call); - obj_put(call); - call = call_get_or_create(&flags.call_id, CT_OWN_CALL); - } - } - else { + if (!call) { /* call == NULL, should create call */ call = call_get_or_create(&flags.call_id, CT_OWN_CALL); }