|
|
|
@ -31,6 +31,17 @@
|
|
|
|
|
#include "dtmf.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct fragment_key {
|
|
|
|
|
str call_id;
|
|
|
|
|
str from_tag;
|
|
|
|
|
};
|
|
|
|
|
struct sdp_fragment {
|
|
|
|
|
struct ng_buffer *ngbuf;
|
|
|
|
|
struct timeval received;
|
|
|
|
|
GQueue streams;
|
|
|
|
|
struct sdp_ng_flags flags;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static pcre *info_re;
|
|
|
|
|
static pcre_extra *info_ree;
|
|
|
|
|
static pcre *streams_re;
|
|
|
|
@ -39,6 +50,9 @@ static pcre_extra *streams_ree;
|
|
|
|
|
int trust_address_def;
|
|
|
|
|
int dtls_passive_def;
|
|
|
|
|
|
|
|
|
|
static mutex_t sdp_fragments_lock;
|
|
|
|
|
static GHashTable *sdp_fragments;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
INLINE int call_ng_flags_prefix(struct sdp_ng_flags *out, str *s_ori, const char *prefix,
|
|
|
|
|
void (*cb)(struct sdp_ng_flags *, str *, void *), void *ptr);
|
|
|
|
@ -1169,6 +1183,98 @@ static enum load_limit_reasons call_offer_session_limit(void) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void fragment_free(struct sdp_fragment *frag) {
|
|
|
|
|
streams_free(&frag->streams);
|
|
|
|
|
call_ng_free_flags(&frag->flags);
|
|
|
|
|
obj_put(frag->ngbuf);
|
|
|
|
|
g_slice_free1(sizeof(*frag), frag);
|
|
|
|
|
}
|
|
|
|
|
static void fragment_key_free(void *p) {
|
|
|
|
|
struct fragment_key *k = p;
|
|
|
|
|
free(k->call_id.s);
|
|
|
|
|
free(k->from_tag.s);
|
|
|
|
|
g_slice_free1(sizeof(*k), k);
|
|
|
|
|
}
|
|
|
|
|
static void queue_sdp_fragment(struct ng_buffer *ngbuf, GQueue *streams, struct sdp_ng_flags *flags) {
|
|
|
|
|
ilog(LOG_DEBUG, "Queuing up SDP fragment for " STR_FORMAT_M "/" STR_FORMAT_M,
|
|
|
|
|
STR_FMT_M(&flags->call_id), STR_FMT_M(&flags->from_tag));
|
|
|
|
|
|
|
|
|
|
struct fragment_key *k = g_slice_alloc0(sizeof(*k));
|
|
|
|
|
str_init_dup_str(&k->call_id, &flags->call_id);
|
|
|
|
|
str_init_dup_str(&k->from_tag, &flags->from_tag);
|
|
|
|
|
|
|
|
|
|
struct sdp_fragment *frag = g_slice_alloc0(sizeof(*frag));
|
|
|
|
|
frag->received = rtpe_now;
|
|
|
|
|
frag->ngbuf = obj_get(ngbuf);
|
|
|
|
|
frag->streams = *streams;
|
|
|
|
|
frag->flags = *flags;
|
|
|
|
|
g_queue_init(streams);
|
|
|
|
|
ZERO(*flags);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&sdp_fragments_lock);
|
|
|
|
|
GQueue *frags = g_hash_table_lookup_queue_new(sdp_fragments, k, fragment_key_free);
|
|
|
|
|
g_queue_push_tail(frags, frag);
|
|
|
|
|
mutex_unlock(&sdp_fragments_lock);
|
|
|
|
|
}
|
|
|
|
|
#define MAX_FRAG_AGE 3000000
|
|
|
|
|
static void dequeue_sdp_fragments(struct call_monologue *monologue) {
|
|
|
|
|
struct fragment_key k;
|
|
|
|
|
ZERO(k);
|
|
|
|
|
k.call_id = monologue->call->callid;
|
|
|
|
|
k.from_tag = monologue->tag;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&sdp_fragments_lock);
|
|
|
|
|
GQueue *frags = g_hash_table_lookup(sdp_fragments, &k);
|
|
|
|
|
if (!frags) {
|
|
|
|
|
mutex_unlock(&sdp_fragments_lock);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_hash_table_remove(sdp_fragments, &k);
|
|
|
|
|
// we own the queue now
|
|
|
|
|
mutex_unlock(&sdp_fragments_lock);
|
|
|
|
|
|
|
|
|
|
struct sdp_fragment *frag;
|
|
|
|
|
while ((frag = g_queue_pop_head(frags))) {
|
|
|
|
|
if (timeval_diff(&rtpe_now, &frag->received) > MAX_FRAG_AGE)
|
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
|
|
ilog(LOG_DEBUG, "Dequeuing SDP fragment for " STR_FORMAT_M "/" STR_FORMAT_M,
|
|
|
|
|
STR_FMT_M(&k.call_id), STR_FMT_M(&k.from_tag));
|
|
|
|
|
|
|
|
|
|
monologue_offer_answer(monologue, &frag->streams, &frag->flags);
|
|
|
|
|
|
|
|
|
|
next:
|
|
|
|
|
fragment_free(frag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_queue_free(frags);
|
|
|
|
|
}
|
|
|
|
|
static gboolean fragment_check_cleanup(void *k, void *v, void *p) {
|
|
|
|
|
int all = GPOINTER_TO_INT(p);
|
|
|
|
|
struct fragment_key *key = k;
|
|
|
|
|
GQueue *frags = v;
|
|
|
|
|
if (!key || !frags)
|
|
|
|
|
return TRUE;
|
|
|
|
|
while (frags->length) {
|
|
|
|
|
struct sdp_fragment *frag = frags->head->data;
|
|
|
|
|
if (!all && timeval_diff(&rtpe_now, &frag->received) <= MAX_FRAG_AGE)
|
|
|
|
|
break;
|
|
|
|
|
g_queue_pop_head(frags);
|
|
|
|
|
fragment_free(frag);
|
|
|
|
|
}
|
|
|
|
|
if (!frags->length) {
|
|
|
|
|
g_queue_free(frags);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
static void fragments_cleanup(int all) {
|
|
|
|
|
mutex_lock(&sdp_fragments_lock);
|
|
|
|
|
g_hash_table_foreach_remove(sdp_fragments, fragment_check_cleanup, GINT_TO_POINTER(all));
|
|
|
|
|
mutex_unlock(&sdp_fragments_lock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *call_offer_answer_ng(struct ng_buffer *ngbuf, bencode_item_t *input,
|
|
|
|
|
bencode_item_t *output, enum call_opmode opmode, const char* addr,
|
|
|
|
|
const endpoint_t *sin)
|
|
|
|
@ -1227,18 +1333,16 @@ static const char *call_offer_answer_ng(struct ng_buffer *ngbuf, bencode_item_t
|
|
|
|
|
/* 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) {
|
|
|
|
|
/* call == NULL, should create call */
|
|
|
|
|
call = call_get_or_create(&flags.call_id, 0);
|
|
|
|
|
}
|
|
|
|
|
if (!call && opmode == OP_OFFER && flags.fragment) {
|
|
|
|
|
queue_sdp_fragment(ngbuf, &streams, &flags);
|
|
|
|
|
errstr = NULL;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opmode == OP_OFFER && !call)
|
|
|
|
|
call = call_get_or_create(&flags.call_id, 0);
|
|
|
|
|
|
|
|
|
|
errstr = "Unknown call-id";
|
|
|
|
|
if (!call)
|
|
|
|
|
goto out;
|
|
|
|
@ -1289,12 +1393,21 @@ static const char *call_offer_answer_ng(struct ng_buffer *ngbuf, bencode_item_t
|
|
|
|
|
call->drop_traffic = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int do_dequeue = 1;
|
|
|
|
|
|
|
|
|
|
ret = monologue_offer_answer(monologue, &streams, &flags);
|
|
|
|
|
if (!ret) {
|
|
|
|
|
// SDP fragments for trickle ICE are consumed with no replacement returned
|
|
|
|
|
if (!flags.fragment)
|
|
|
|
|
ret = sdp_replace(chopper, &parsed, monologue->active_dialogue, &flags);
|
|
|
|
|
}
|
|
|
|
|
else if (ret == ERROR_NO_ICE_AGENT && flags.fragment) {
|
|
|
|
|
queue_sdp_fragment(ngbuf, &streams, &flags);
|
|
|
|
|
ret = 0;
|
|
|
|
|
do_dequeue = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// streams and flags are invalid after here
|
|
|
|
|
|
|
|
|
|
struct recording *recording = call->recording;
|
|
|
|
|
if (recording != NULL) {
|
|
|
|
@ -1305,6 +1418,9 @@ static const char *call_offer_answer_ng(struct ng_buffer *ngbuf, bencode_item_t
|
|
|
|
|
recording_response(recording, output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (do_dequeue)
|
|
|
|
|
dequeue_sdp_fragments(monologue);
|
|
|
|
|
|
|
|
|
|
rwlock_unlock_w(&call->master_lock);
|
|
|
|
|
|
|
|
|
|
if (!flags.no_redis_update) {
|
|
|
|
@ -2252,6 +2368,26 @@ void call_interfaces_free() {
|
|
|
|
|
pcre_free_study(streams_ree);
|
|
|
|
|
streams_ree = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fragments_cleanup(1);
|
|
|
|
|
g_hash_table_destroy(sdp_fragments);
|
|
|
|
|
sdp_fragments = NULL;
|
|
|
|
|
mutex_destroy(&sdp_fragments_lock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void call_interfaces_timer() {
|
|
|
|
|
fragments_cleanup(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned static int frag_key_hash(const void *A) {
|
|
|
|
|
const struct fragment_key *a = A;
|
|
|
|
|
return str_hash(&a->call_id) ^ str_hash(&a->from_tag);
|
|
|
|
|
}
|
|
|
|
|
static int frag_key_eq(const void *A, const void *B) {
|
|
|
|
|
const struct fragment_key *a = A;
|
|
|
|
|
const struct fragment_key *b = B;
|
|
|
|
|
return str_equal(&a->call_id, &b->call_id)
|
|
|
|
|
&& str_equal(&a->from_tag, &b->from_tag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int call_interfaces_init() {
|
|
|
|
@ -2268,5 +2404,8 @@ int call_interfaces_init() {
|
|
|
|
|
return -1;
|
|
|
|
|
streams_ree = pcre_study(streams_re, 0, &errptr);
|
|
|
|
|
|
|
|
|
|
sdp_fragments = g_hash_table_new_full(frag_key_hash, frag_key_eq, fragment_key_free, NULL);
|
|
|
|
|
mutex_init(&sdp_fragments_lock);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|