diff --git a/include/re_hash.h b/include/re_hash.h index 60494ad..873fa34 100644 --- a/include/re_hash.h +++ b/include/re_hash.h @@ -18,6 +18,7 @@ struct le *hash_apply(const struct hash *h, list_apply_h *ah, void *arg); struct list *hash_list(const struct hash *h, uint32_t key); uint32_t hash_bsize(const struct hash *h); void hash_flush(struct hash *h); +void hash_clear(struct hash *h); uint32_t hash_valid_size(uint32_t size); diff --git a/include/re_list.h b/include/re_list.h index 1fd28a3..3045453 100644 --- a/include/re_list.h +++ b/include/re_list.h @@ -51,6 +51,7 @@ typedef bool (list_sort_h)(struct le *le1, struct le *le2, void *arg); void list_init(struct list *list); void list_flush(struct list *list); +void list_clear(struct list *list); void list_append(struct list *list, struct le *le, void *data); void list_prepend(struct list *list, struct le *le, void *data); void list_insert_before(struct list *list, struct le *le, struct le *ile, diff --git a/src/hash/hash.c b/src/hash/hash.c index 82f13e6..45aa11e 100644 --- a/src/hash/hash.c +++ b/src/hash/hash.c @@ -183,6 +183,23 @@ void hash_flush(struct hash *h) } +/** + * Clear a hashmap without dereferencing the elements + * + * @param h Hashmap table + */ +void hash_clear(struct hash *h) +{ + uint32_t i; + + if (!h) + return; + + for (i=0; ibsize; i++) + list_clear(&h->bucket[i]); +} + + /** * Calculate a valid hash size from a random size * diff --git a/src/list/list.c b/src/list/list.c index e8b4c21..5be764a 100644 --- a/src/list/list.c +++ b/src/list/list.c @@ -55,6 +55,31 @@ void list_flush(struct list *list) } +/** + * Clear a linked list without dereferencing the elements + * + * @param list Linked list + */ +void list_clear(struct list *list) +{ + struct le *le; + + if (!list) + return; + + le = list->head; + while (le) { + struct le *next = le->next; + le->list = NULL; + le->prev = le->next = NULL; + le->data = NULL; + le = next; + } + + list_init(list); +} + + /** * Append a list element to a linked list * diff --git a/src/sip/sip.c b/src/sip/sip.c index 8d41948..5005b23 100644 --- a/src/sip/sip.c +++ b/src/sip/sip.c @@ -38,7 +38,9 @@ static void destructor(void *arg) mem_deref(sip->ht_ctrans); hash_flush(sip->ht_strans); + hash_clear(sip->ht_strans_mrg); mem_deref(sip->ht_strans); + mem_deref(sip->ht_strans_mrg); hash_flush(sip->ht_conn); mem_deref(sip->ht_conn); diff --git a/src/sip/sip.h b/src/sip/sip.h index 1551539..94361d1 100644 --- a/src/sip/sip.h +++ b/src/sip/sip.h @@ -11,6 +11,7 @@ struct sip { struct list reql; struct hash *ht_ctrans; struct hash *ht_strans; + struct hash *ht_strans_mrg; struct hash *ht_conn; struct hash *ht_udpconn; struct dnsc *dnsc; diff --git a/src/sip/strans.c b/src/sip/strans.c index f2d4460..5d2a08f 100644 --- a/src/sip/strans.c +++ b/src/sip/strans.c @@ -29,6 +29,7 @@ enum state { struct sip_strans { struct le he; + struct le he_mrg; struct tmr tmr; struct tmr tmrg; struct sa dst; @@ -48,6 +49,7 @@ static void destructor(void *arg) struct sip_strans *st = arg; hash_unlink(&st->he); + hash_unlink(&st->he_mrg); tmr_cancel(&st->tmr); tmr_cancel(&st->tmrg); mem_deref(st->msg); @@ -112,6 +114,30 @@ static bool cmp_cancel_handler(struct le *le, void *arg) } +static bool cmp_merge_handler(struct le *le, void *arg) +{ + struct sip_strans *st = le->data; + const struct sip_msg *msg = arg; + + if (pl_cmp(&st->msg->cseq.met, &msg->cseq.met)) + return false; + + if (st->msg->cseq.num != msg->cseq.num) + return false; + + if (pl_cmp(&st->msg->callid, &msg->callid)) + return false; + + if (pl_cmp(&st->msg->from.tag, &msg->from.tag)) + return false; + + if (pl_cmp(&st->msg->ruri, &msg->ruri)) + return false; + + return true; +} + + static void dummy_handler(void *arg) { (void)arg; @@ -229,6 +255,16 @@ static bool request_handler(const struct sip_msg *msg, void *arg) return true; } + else if (!pl_isset(&msg->to.tag)) { + + st = list_ledata(hash_lookup(sip->ht_strans_mrg, + hash_joaat_pl(&msg->callid), + cmp_merge_handler, (void *)msg)); + if (st) { + (void)sip_reply(sip, msg, 482, "Loop Detected"); + return true; + } + } if (!pl_strcmp(&msg->met, "CANCEL")) return cancel_handler(sip, msg); @@ -264,6 +300,9 @@ int sip_strans_alloc(struct sip_strans **stp, struct sip *sip, hash_append(sip->ht_strans, hash_joaat_pl(&msg->via.branch), &st->he, st); + hash_append(sip->ht_strans_mrg, hash_joaat_pl(&msg->callid), + &st->he_mrg, st); + st->invite = !pl_strcmp(&msg->met, "INVITE"); st->msg = mem_ref((void *)msg); st->state = TRYING; @@ -367,6 +406,10 @@ int sip_strans_init(struct sip *sip, uint32_t sz) if (err) return err; + err = hash_alloc(&sip->ht_strans_mrg, sz); + if (err) + return err; + return hash_alloc(&sip->ht_strans, sz); }