Skip to content

Commit

Permalink
dialog: keep a mapping between INVITE&ACK
Browse files Browse the repository at this point in the history
This prevents concurrency bugs that would generate an ACK with a higher
CSeq than the INVITE. Addresses #1071

(cherry picked from commit 80ff319)
  • Loading branch information
razvancrainea committed Aug 5, 2023
1 parent d29fea7 commit a01896e
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 12 deletions.
84 changes: 73 additions & 11 deletions modules/dialog/dlg_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
#include "../../parser/contact/contact.h"
#include "../../parser/contact/parse_contact.h"
#include "../../parser/parse_rr.h"
#include "../../parser/parse_cseq.h"
#include "../../parser/parse_hname2.h"
#include "../../parser/parser_f.h"
#include "../tm/tm_load.h"
Expand Down Expand Up @@ -1755,6 +1754,74 @@ static inline int switch_cseqs(struct dlg_cell *dlg,unsigned int leg_no)
return ret;
}

static void dlg_leg_push_cseq_map(struct dlg_cell *dlg, unsigned int leg,
struct sip_msg *msg)
{
struct dlg_leg_cseq_map *map;
unsigned int msg_cseq;

if((!msg->cseq && (parse_headers(msg,HDR_CSEQ_F,0)<0 || !msg->cseq)) ||
!msg->cseq->parsed){
LM_ERR("bad sip message or missing CSeq hdr :-/\n");
return;
}
if (str2int(&get_cseq(msg)->number, &msg_cseq) < 0) {
LM_ERR("invalid CSeq number [%.*s]\n",
get_cseq(msg)->number.len, get_cseq(msg)->number.s);
return;
}
map = shm_malloc(sizeof *map);
if (!map) {
LM_ERR("oom for cseq map\n");
return;
}
memset(map, 0, sizeof *map);
map->msg = msg_cseq;
if (dlg->legs[leg].last_gen_cseq)
map->gen = dlg->legs[leg].last_gen_cseq;
else
map->gen = msg_cseq;
map->next = dlg->legs[leg].cseq_maps;
dlg->legs[leg].cseq_maps = map;
}

static unsigned int dlg_leg_get_cseq(struct dlg_cell *dlg, unsigned int leg,
struct sip_msg *msg)
{
struct dlg_leg_cseq_map *map, *tmp;
unsigned int msg_cseq;
int distance = 10;

if((!msg->cseq && (parse_headers(msg,HDR_CSEQ_F,0)<0 || !msg->cseq)) ||
!msg->cseq->parsed){
LM_ERR("bad sip message or missing CSeq hdr :-/\n");
return 0;
}
if (str2int(&get_cseq(msg)->number, &msg_cseq) < 0) {
LM_ERR("invalid CSeq number [%.*s]\n",
get_cseq(msg)->number.len, get_cseq(msg)->number.s);
return 0;
}
for (map = dlg->legs[leg].cseq_maps; map; map = map->next) {
if (map->msg != msg_cseq)
continue;
msg_cseq = map->gen; /* value to be returned */
/* cleanup older values, since they are no longer needed */
for (tmp = map, map = map->next; map && distance > 0;
tmp = map, map = map->next, distance--);
if (map) {
tmp->next = NULL;
do {
tmp = map;
map = map->next;
shm_free(tmp);
} while (map);
}
return msg_cseq;
}
return 0;
}

static inline void log_bogus_dst_leg(struct dlg_cell *dlg)
{
if (ctx_lastdstleg_get()>=dlg->legs_no[DLG_LEGS_USED])
Expand Down Expand Up @@ -2094,7 +2161,7 @@ void dlg_onroute(struct sip_msg* req, str *route_params, void *param)
if (req->first_line.u.request.method_value == METHOD_INVITE) {
/* save INVITE cseq, in case any requests follow after this
( pings or other in-dialog requests until the ACK comes in */
dlg->legs[dst_leg].last_inv_gen_cseq = dlg->legs[dst_leg].last_gen_cseq;
dlg_leg_push_cseq_map(dlg, dst_leg, req);

/* Received RE-INVITE where we mangle the CSEQ due to existing pings sent
*
Expand All @@ -2113,11 +2180,7 @@ void dlg_onroute(struct sip_msg* req, str *route_params, void *param)
if (req->first_line.u.request.method_value == METHOD_INVITE) {
/* we did not generate any pings yet - still we need to store the INV cseq,
in case there's a race between the ACK for the INVITE and sending of new pings */
if (str2int(&((struct cseq_body *)req->cseq->parsed)->number,
&dlg->legs[dst_leg].last_inv_gen_cseq) < 0)
LM_ERR("invalid INVITE cseq [%.*s]\n",
((struct cseq_body *)req->cseq->parsed)->number.len,
((struct cseq_body *)req->cseq->parsed)->number.s);
dlg_leg_push_cseq_map(dlg, dst_leg, req);
}

dlg_unlock( d_table, d_entry );
Expand All @@ -2136,10 +2199,9 @@ void dlg_onroute(struct sip_msg* req, str *route_params, void *param)
dlg_lock (d_table, d_entry);

if (dlg->legs[dst_leg].last_gen_cseq ||
dlg->legs[dst_leg].last_inv_gen_cseq ) {
if (dlg->legs[dst_leg].last_inv_gen_cseq)
update_val = dlg->legs[dst_leg].last_inv_gen_cseq;
else
dlg->legs[dst_leg].cseq_maps) {
update_val = dlg_leg_get_cseq(dlg, dst_leg, req);
if (update_val == 0)
update_val = dlg->legs[dst_leg].last_gen_cseq;
dlg_unlock( d_table, d_entry );

Expand Down
7 changes: 7 additions & 0 deletions modules/dialog/dlg_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ int init_dlg_table(unsigned int size)

static inline void free_dlg_dlg(struct dlg_cell *dlg)
{
struct dlg_leg_cseq_map *map, *tmp;
struct dlg_val *dv;
unsigned int i;

Expand Down Expand Up @@ -203,6 +204,12 @@ static inline void free_dlg_dlg(struct dlg_cell *dlg)
shm_free(dlg->legs[i].tmp_out_sdp.s);
if (dlg->legs[i].tmp_in_sdp.s)
shm_free(dlg->legs[i].tmp_in_sdp.s);
/* destroy dialog mappings as well */
for (map = dlg->legs[i].cseq_maps; map;) {
tmp = map;
map = map->next;
shm_free(tmp);
}
}
shm_free(dlg->legs);
}
Expand Down
6 changes: 5 additions & 1 deletion modules/dialog/dlg_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@
#define DLG_DIR_DOWNSTREAM 1
#define DLG_DIR_UPSTREAM 2

struct dlg_leg_cseq_map {
unsigned int msg, gen;
struct dlg_leg_cseq_map *next;
};

struct dlg_leg {
int id;
Expand All @@ -102,7 +106,7 @@ struct dlg_leg {
str route_uris[64];
int nr_uris;
unsigned int last_gen_cseq; /* FIXME - think this can be atomic_t to avoid locking */
unsigned int last_inv_gen_cseq; /* used when translating ACKs */
struct dlg_leg_cseq_map *cseq_maps; /* used when translating ACKs */
char reply_received;
char reinvite_confirmed;
struct socket_info *bind_addr;
Expand Down

0 comments on commit a01896e

Please sign in to comment.