From 357474df6f6b2c8f4ca62b4ef46333f0fd75633b Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 12 Mar 2019 14:21:24 -0400 Subject: [PATCH] TT#50652 add label= option to match call participants Change-Id: Ifc6cf8f630e9e5eb8cfb9b284e671a5ce4470963 --- README.md | 9 +-- daemon/call.c | 7 +++ daemon/call_interfaces.c | 94 ++++++++++++++++--------------- include/call.h | 1 + include/call_interfaces.h | 6 ++ t/auto-daemon-tests.pl | 114 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 183 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 4d5a698ae..9b3ee8fca 100644 --- a/README.md +++ b/README.md @@ -1389,8 +1389,9 @@ events (RFC 4733 type packets), respectively. Packets can be blocked for an entire call if only the `call-id` key is present in the message, or can be blocked directionally for individual participants. Participants can be selected by their SIP tag if the `from-tag` key -is included in the message, or they can be selected by their SDP media address if the `address` key is included -in the message. In the latter case, the address can be an IPv4 or IPv6 address, and any participant that is +is included in the message, they can be selected by their SDP media address if the `address` key is included +in the message, or they can be selected by the user-provided `label` if the `label` key is included in the +message. For an address, it can be an IPv4 or IPv6 address, and any participant that is found to have a matching address advertised as their SDP media address will have their originating RTP packets blocked (or unblocked). @@ -1422,8 +1423,8 @@ and stopped independently of each other. -------------------- Only available if compiled with transcoding support. The message must contain the key `call-id` and one -of the participant selection keys described under the `block DTMF` message (such as `from-tag` or -`address`). +of the participant selection keys described under the `block DTMF` message (such as `from-tag`, +`address`, or `label`). Starts playback of a provided media file to the selected call participant. The format of the media file can be anything that is supported by *ffmpeg*, for example a `.wav` or `.mp3` file. It will automatically diff --git a/daemon/call.c b/daemon/call.c index e92d443bf..e3f3b9ade 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -1798,6 +1798,11 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, __tos_change(call, flags); + if (flags && flags->label.s) { + call_str_cpy(call, &other_ml->label, &flags->label); + g_hash_table_replace(call->labels, &other_ml->label, other_ml); + } + ml_media = other_ml_media = NULL; for (media_iter = streams->head; media_iter; media_iter = media_iter->next) { @@ -2316,6 +2321,7 @@ static void __call_free(void *p) { g_hash_table_destroy(c->tags); g_hash_table_destroy(c->viabranches); + g_hash_table_destroy(c->labels); free_ssrc_hash(&c->ssrc_hash); while (c->streams.head) { @@ -2347,6 +2353,7 @@ static struct call *call_create(const str *callid) { rwlock_init(&c->master_lock); c->tags = g_hash_table_new(str_hash, str_equal); c->viabranches = g_hash_table_new(str_hash, str_equal); + c->labels = g_hash_table_new(str_hash, str_equal); call_str_cpy(c, &c->callid, callid); c->created = rtpe_now; c->dtls_cert = dtls_cert(); diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 11f4220e6..0e1d8c3c1 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -746,6 +746,13 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu call_ng_flags_list(out, input, "replace", call_ng_flags_replace, NULL); call_ng_flags_list(out, input, "supports", call_ng_flags_supports, NULL); + bencode_dictionary_get_str(input, "call-id", &out->call_id); + bencode_dictionary_get_str(input, "from-tag", &out->from_tag); + bencode_dictionary_get_str(input, "to-tag", &out->to_tag); + bencode_dictionary_get_str(input, "via-branch", &out->via_branch); + bencode_dictionary_get_str(input, "label", &out->label); + bencode_dictionary_get_str(input, "address", &out->address); + diridx = 0; if ((list = bencode_dictionary_get_expect(input, "direction", BENCODE_LIST))) { for (it = list->child; it && diridx < 2; it = it->sibling) @@ -900,8 +907,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input, bencode_item_t *output, enum call_opmode opmode, const char* addr, const endpoint_t *sin) { - str sdp, fromtag, totag = STR_NULL, callid, viabranch; - str label = STR_NULL; + str sdp; const char *errstr; GQueue parsed = G_QUEUE_INIT; GQueue streams = G_QUEUE_INIT; @@ -913,20 +919,18 @@ static const char *call_offer_answer_ng(bencode_item_t *input, if (!bencode_dictionary_get_str(input, "sdp", &sdp)) return "No SDP body in message"; - if (!bencode_dictionary_get_str(input, "call-id", &callid)) + + call_ng_process_flags(&flags, input, opmode); + + if (!flags.call_id.s) return "No call-id in message"; - if (!bencode_dictionary_get_str(input, "from-tag", &fromtag)) + if (!flags.from_tag.s) return "No from-tag in message"; - bencode_dictionary_get_str(input, "to-tag", &totag); if (opmode == OP_ANSWER) { - if (!totag.s) + if (!flags.to_tag.s) return "No to-tag in message"; - str_swap(&totag, &fromtag); + str_swap(&flags.to_tag, &flags.from_tag); } - bencode_dictionary_get_str(input, "via-branch", &viabranch); - bencode_dictionary_get_str(input, "label", &label); - - call_ng_process_flags(&flags, input, opmode); if (opmode == OP_OFFER) { enum load_limit_reasons limit = call_offer_session_limit(); @@ -955,7 +959,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input, goto out; /* OP_ANSWER; OP_OFFER && !IS_FOREIGN_CALL */ - call = call_get(&callid); + 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 @@ -969,12 +973,12 @@ static const char *call_offer_answer_ng(bencode_item_t *input, rwlock_unlock_w(&call->master_lock); call_destroy(call); obj_put(call); - call = call_get_or_create(&callid, CT_OWN_CALL); + call = call_get_or_create(&flags.call_id, CT_OWN_CALL); } } else { /* call == NULL, should create call */ - call = call_get_or_create(&callid, CT_OWN_CALL); + call = call_get_or_create(&flags.call_id, CT_OWN_CALL); } } @@ -993,7 +997,8 @@ static const char *call_offer_answer_ng(bencode_item_t *input, * need to hold a ref until we're done sending the reply */ call_bencode_hold_ref(call, output); - monologue = call_get_mono_dialogue(call, &fromtag, &totag, viabranch.s ? &viabranch : NULL); + monologue = call_get_mono_dialogue(call, &flags.from_tag, &flags.to_tag, + flags.via_branch.s ? &flags.via_branch : NULL); errstr = "Invalid dialogue association"; if (!monologue) { rwlock_unlock_w(&call->master_lock); @@ -1006,8 +1011,6 @@ static const char *call_offer_answer_ng(bencode_item_t *input, } else { monologue->tagtype = TO_TAG; } - if (label.s && !monologue->label.s) - call_str_cpy(call, &monologue->label, &label); chopper = sdp_chopper_new(&sdp); bencode_buffer_destroy_add(output->buffer, (free_func_t) sdp_chopper_destroy, chopper); @@ -1478,24 +1481,33 @@ const char *call_stop_recording_ng(bencode_item_t *input, bencode_item_t *output } static const char *media_block_match(struct call **call, struct call_monologue **monologue, - bencode_item_t *input) + struct sdp_ng_flags *flags, bencode_item_t *input) { - str callid; - str s; + struct sdp_ng_flags flags_store; + + if (!flags) + flags = &flags_store; *call = NULL; *monologue = NULL; - if (!bencode_dictionary_get_str(input, "call-id", &callid)) + call_ng_process_flags(flags, input, OP_OTHER); + + if (!flags->call_id.s) return "No call-id in message"; - *call = call_get_opmode(&callid, OP_OTHER); + *call = call_get_opmode(&flags->call_id, OP_OTHER); if (!*call) return "Unknown call-id"; - // directional block? - if (bencode_dictionary_get_str(input, "address", &s)) { + // directional? + if (flags->label.s) { + *monologue = g_hash_table_lookup((*call)->labels, &flags->label); + if (!*monologue) + return "No monologue matching the given label"; + } + else if (flags->address.s) { sockaddr_t addr; - if (sockaddr_parse_any_str(&addr, &s)) + if (sockaddr_parse_any_str(&addr, &flags->address)) return "Failed to parse network address"; // walk our structures to find a matching stream for (GList *l = (*call)->monologues.head; l; l = l->next) { @@ -1516,8 +1528,8 @@ static const char *media_block_match(struct call **call, struct call_monologue * found: ; } - else if (bencode_dictionary_get_str(input, "from-tag", &s)) { - *monologue = call_get_mono_dialogue(*call, &s, NULL, NULL); + else if (flags->from_tag.s) { + *monologue = call_get_mono_dialogue(*call, &flags->from_tag, NULL, NULL); if (!*monologue) return "From-tag given, but no such tag exists"; } @@ -1530,14 +1542,12 @@ const char *call_start_forwarding_ng(bencode_item_t *input, bencode_item_t *outp struct call *call; struct call_monologue *monologue; const char *errstr = NULL; - str metadata; + struct sdp_ng_flags flags; - errstr = media_block_match(&call, &monologue, input); + errstr = media_block_match(&call, &monologue, &flags, input); if (errstr) goto out; - bencode_dictionary_get_str(input, "metadata", &metadata); - if (monologue) { ilog(LOG_INFO, "Start forwarding for single party (tag '" STR_FORMAT ")", STR_FMT(&monologue->tag)); @@ -1548,7 +1558,7 @@ const char *call_start_forwarding_ng(bencode_item_t *input, bencode_item_t *outp call->rec_forwarding = 1; } - recording_start(call, NULL, &metadata); + recording_start(call, NULL, &flags.metadata); errstr = NULL; out: if (call) { @@ -1565,12 +1575,10 @@ const char *call_stop_forwarding_ng(bencode_item_t *input, bencode_item_t *outpu const char *errstr = NULL; struct sdp_ng_flags flags; - errstr = media_block_match(&call, &monologue, input); + errstr = media_block_match(&call, &monologue, &flags, input); if (errstr) goto out; - call_ng_process_flags(&flags, input, OP_OTHER); - if (monologue) { ilog(LOG_INFO, "Stop forwarding for single party (tag '" STR_FORMAT ")", STR_FMT(&monologue->tag)); @@ -1603,8 +1611,9 @@ const char *call_block_dtmf_ng(bencode_item_t *input, bencode_item_t *output) { struct call *call; struct call_monologue *monologue; const char *errstr = NULL; + struct sdp_ng_flags flags; - errstr = media_block_match(&call, &monologue, input); + errstr = media_block_match(&call, &monologue, &flags, input); if (errstr) goto out; @@ -1634,12 +1643,10 @@ const char *call_unblock_dtmf_ng(bencode_item_t *input, bencode_item_t *output) const char *errstr = NULL; struct sdp_ng_flags flags; - errstr = media_block_match(&call, &monologue, input); + errstr = media_block_match(&call, &monologue, &flags, input); if (errstr) goto out; - call_ng_process_flags(&flags, input, OP_OTHER); - if (monologue) { ilog(LOG_INFO, "Unblocking directional DTMF (tag '" STR_FORMAT ")", STR_FMT(&monologue->tag)); @@ -1670,8 +1677,9 @@ const char *call_block_media_ng(bencode_item_t *input, bencode_item_t *output) { struct call *call; struct call_monologue *monologue; const char *errstr = NULL; + struct sdp_ng_flags flags; - errstr = media_block_match(&call, &monologue, input); + errstr = media_block_match(&call, &monologue, &flags, input); if (errstr) goto out; @@ -1703,12 +1711,10 @@ const char *call_unblock_media_ng(bencode_item_t *input, bencode_item_t *output) const char *errstr = NULL; struct sdp_ng_flags flags; - errstr = media_block_match(&call, &monologue, input); + errstr = media_block_match(&call, &monologue, &flags, input); if (errstr) goto out; - call_ng_process_flags(&flags, input, OP_OTHER); - if (monologue) { ilog(LOG_INFO, "Unblocking directional media (tag '" STR_FORMAT ")", STR_FMT(&monologue->tag)); @@ -1742,7 +1748,7 @@ out: static const char *play_media_select_party(struct call **call, struct call_monologue **monologue, bencode_item_t *input) { - const char *err = media_block_match(call, monologue, input); + const char *err = media_block_match(call, monologue, NULL, input); if (err) return err; if (!*monologue) diff --git a/include/call.h b/include/call.h index 18232e4ff..c06066dde 100644 --- a/include/call.h +++ b/include/call.h @@ -362,6 +362,7 @@ struct call { GQueue medias; GHashTable *tags; GHashTable *viabranches; + GHashTable *labels; GQueue streams; GQueue stream_fds; GQueue endpoint_maps; diff --git a/include/call_interfaces.h b/include/call_interfaces.h index 3e59aed1f..bc3e84505 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -18,6 +18,10 @@ struct sockaddr_in6; struct sdp_ng_flags { enum call_opmode opmode; + str call_id; + str from_tag; + str to_tag; + str via_branch; str received_from_family; str received_from_address; str media_address; @@ -31,6 +35,8 @@ struct sdp_ng_flags { int tos; str record_call_str; str metadata; + str label; + str address; sockaddr_t xmlrpc_callback; GHashTable *codec_strip; GQueue codec_offer; diff --git a/t/auto-daemon-tests.pl b/t/auto-daemon-tests.pl index 2aec608e7..4c5836fd8 100755 --- a/t/auto-daemon-tests.pl +++ b/t/auto-daemon-tests.pl @@ -1564,6 +1564,120 @@ rcv($sock_b, -1, rtpm(8, $seq + 9, $ts + 160 * 4, $ssrc, $pcma_5)); +($sock_a, $sock_b) = new_call([qw(198.51.100.9 2020)], [qw(198.51.100.9 2022)]); + +offer('media playback, side A, select by label', { ICE => 'remove', replace => ['origin'], + label => 'foobar' }, < ['origin'], label => 'blah' }, < 'foobar', + blob => $wav_file }); +is $resp->{duration}, 100, 'media duration'; + +($seq, $ts, $ssrc) = rcv($sock_a, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); +rcv($sock_a, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); +rcv($sock_a, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); +rcv($sock_a, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); +rcv($sock_a, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); + + + + +($sock_a, $sock_b) = new_call([qw(198.51.100.9 2030)], [qw(198.51.100.9 2032)]); + +offer('media playback, side B, select by label', { ICE => 'remove', replace => ['origin'], + label => 'quux' }, < ['origin'], label => 'meh' }, < 'meh', blob => $wav_file }); +is $resp->{duration}, 100, 'media duration'; + +($seq, $ts, $ssrc) = rcv($sock_b, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); +rcv($sock_b, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); +rcv($sock_b, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); +rcv($sock_b, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); +rcv($sock_b, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); + + + + + ($sock_a, $sock_b) = new_call([qw(198.51.100.1 2050)], [qw(198.51.100.3 2052)]); offer('media playback, SRTP', { ICE => 'remove', replace => ['origin'], DTLS => 'off' }, <