status tm_hremove(struct tm_hash_t *hashtable, char *key){ unsigned hash; struct tm_hash_entry_t *entry, *tmpentry; hash = tm_hash(key, hashtable->size - 1); entry = hashtable->table[hash]; if(entry && strcmp(entry->key, key) == 0){ tmpentry = entry->next; free(entry); hashtable->table[hash] = tmpentry; return OK; }else{ while(entry){ if(entry->next && strcmp(entry->next->key, key) == 0){ tmpentry = entry->next; entry->next = entry->next->next; free(tmpentry); return OK; } entry = entry->next; } } return ERROR; }
struct tm_hinsert(struct tm_hash_t *hashtable, char *key, char *data, unsigned length, unsigned lifetime, short mode ){ unsigned hash; struct tm_hash_entry_t *entry, *tmpentry; time_t currtime; hash = tm_hash(key, hashtable->size - 1); if(hash < 0 || hash >= hashtable->size){ return ERROR; } if(tm_hfind(hashtable, key) != NULL){ if(mode == MODE_ADD){ return SUCCESS; }else{ tm_hremove(hashtable, key); } } if((entry = malloc( sizeof(struct tm_hash_entry_t))) == NULL){ return ERROR; } currtime = time( (time_t *)NULL); entry->key = key; entry->data = data; entry->length = length; entry->created = currtime; entry->expired = currtime + lifetime; entry->next = hashtable->table[hash] != NULL ? hashtable->table[hash] : NULL; hashtable->table[hash] = entry; return OK; }
/* Return a case-sensitive hash of string contents. */ unsigned int tm_string_hash(tm_string *in_string, unsigned int table_size) { ASSERT(in_string); ASSERT(in_string->contents); return(tm_hash(in_string->contents, table_size)); }
static inline unsigned int dlg2hash( dlg_t* dlg ) { str cseq_nr; unsigned int hashid; cseq_nr.s=int2str(dlg->loc_seq.value, &cseq_nr.len); hashid = tm_hash(dlg->id.call_id, cseq_nr); LM_DBG("%d\n", hashid); return hashid; }
struct tm_hash_entry_t *tm_hfind(struct tm_hash_t *hashtable, char *key){ unsigned hash; struct tm_hash_entry_t *entry; hash = tm_hash(key, hashtable->size - 1); if(hash < 0 || hash >= hashtable->size){ return NULL; } for(entry = hashtable->table[hash]; entry != NULL; entry = entry->next){ if(strcmp(entry->key, key) == 0){ return entry; } } return NULL; }
inline static int t_check_trans(struct sip_msg* msg) { struct cell *trans; if (msg->REQ_METHOD==METHOD_CANCEL) { /* parse needed hdrs*/ if (check_transaction_quadruple(msg)==0) { LM_ERR("too few headers\n"); return 0; /*drop request!*/ } if (!msg->hash_index) msg->hash_index = tm_hash(msg->callid->body,get_cseq(msg)->number); /* performe lookup */ trans = t_lookupOriginalT( msg ); return trans?1:-1; } else { trans = get_t(); if (trans==NULL) return -1; if (trans!=T_UNDEFINED) return 1; switch ( t_lookup_request( msg , 0) ) { case 1: /* transaction found -> is it local ACK? */ if (msg->REQ_METHOD==METHOD_ACK) return 1; /* .... else -> retransmission */ trans = get_t(); t_retransmit_reply(trans); UNREF(trans); set_t(0); return 0; case -2: /* e2e ACK found */ return 1; default: /* notfound */ return -1; } } }
int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked ) { struct cell *p_cell; unsigned int isACK; struct sip_msg *t_msg; struct via_param *branch; int match_status; isACK = p_msg->REQ_METHOD==METHOD_ACK; if (isACK) { if (e2eack_T==NULL) return -1; if (e2eack_T!=T_UNDEFINED) return -2; } /* parse all*/ if (check_transaction_quadruple(p_msg)==0) { LM_ERR("too few headers\n"); set_t(0); /* stop processing */ return 0; } /* start searching into the table */ if (!p_msg->hash_index) p_msg->hash_index=tm_hash( p_msg->callid->body , get_cseq(p_msg)->number ) ; LM_DBG("start searching: hash=%d, isACK=%d\n", p_msg->hash_index,isACK); /* first of all, look if there is RFC3261 magic cookie in branch; if * so, we can do very quick matching and skip the old-RFC bizzar * comparison of many header fields */ if (!p_msg->via1) { LM_ERR("no via\n"); set_t(0); return 0; } branch=p_msg->via1->branch; if (branch && branch->value.s && branch->value.len>MCOOKIE_LEN && memcmp(branch->value.s,MCOOKIE,MCOOKIE_LEN)==0) { /* huhuhu! the cookie is there -- let's proceed fast */ LOCK_HASH(p_msg->hash_index); match_status=matching_3261(p_msg,&p_cell, /* skip transactions with different method; otherwise CANCEL * would match the previous INVITE trans. */ isACK ? ~METHOD_INVITE: ~p_msg->REQ_METHOD); switch(match_status) { case 0: goto notfound; /* no match */ case 1: goto found; /* match */ case 2: goto e2e_ack; /* e2e proxy ACK */ } } /* ok -- it's ugly old-fashioned transaction matching -- it is * a bit simplified to be fast -- we don't do all the comparisons * of parsed uri, which was simply too bloated */ LM_DBG("proceeding to pre-RFC3261 transaction matching\n"); /* lock the whole entry*/ LOCK_HASH(p_msg->hash_index); /* all the transactions from the entry are compared */ for ( p_cell = get_tm_table()->entrys[p_msg->hash_index].first_cell; p_cell; p_cell = p_cell->next_cell ) { t_msg = p_cell->uas.request; if (!t_msg) continue; /* skip UAC transactions */ if (!isACK) { /* compare lengths first */ if (!EQ_LEN(callid)) continue; if (!EQ_LEN(cseq)) continue; if (!EQ_LEN(from)) continue; if (!EQ_LEN(to)) continue; if (ruri_matching && !EQ_REQ_URI_LEN) continue; if (via1_matching && !EQ_VIA_LEN(via1)) continue; /* length ok -- move on */ if (!EQ_STR(callid)) continue; if (!EQ_STR(cseq)) continue; if (!EQ_STR(from)) continue; if (!EQ_STR(to)) continue; if (ruri_matching && !EQ_REQ_URI_STR) continue; if (via1_matching && !EQ_VIA_STR(via1)) continue; /* request matched ! */ LM_DBG("non-ACK matched\n"); goto found; } else { /* it's an ACK request*/ /* ACK's relate only to INVITEs - and we look only for 2 types of * INVITEs : (a) negative INVITEs or (b) pozitive UAS INVITEs */ if ( t_msg->REQ_METHOD!=METHOD_INVITE || !(p_cell->uas.status>=300 || (p_cell->nr_of_outgoings==0 && p_cell->uas.status>=200)) ) continue; /* From|To URI , CallID, CSeq # must be always there */ /* compare lengths now */ if (!EQ_LEN(callid)) continue; /* CSeq only the number without method ! */ if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len) continue; if (! EQ_LEN(from)) continue; /* To only the uri -- to many UACs screw up tags */ if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len) continue; if (!EQ_STR(callid)) continue; if (memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s, get_cseq(p_msg)->number.len)!=0) continue; if (!EQ_STR(from)) continue; if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s, get_to(t_msg)->uri.len)!=0) continue; if (p_cell->uas.status<300) { /* it's a 2xx local UAS transaction */ if (dlg_matching(p_cell, p_msg)) goto e2e_ack; continue; } /* it is not an e2e ACK/200 -- perhaps it is * local negative case; in which case we will want * more elements to match: r-uri and via; allow * mismatching r-uri as an config option for broken * UACs */ if (ruri_matching && !EQ_REQ_URI_LEN ) continue; if (via1_matching && !EQ_VIA_LEN(via1)) continue; if (ruri_matching && !EQ_REQ_URI_STR) continue; if (via1_matching && !EQ_VIA_STR(via1)) continue; /* wow -- we survived all the check! we matched! */ LM_DBG("non-2xx ACK matched\n"); goto found; } /* ACK */ } /* synonym loop */ notfound: /* no transaction found */ set_t(0); e2eack_T = NULL; if (!leave_new_locked || isACK) { UNLOCK_HASH(p_msg->hash_index); } LM_DBG("no transaction found\n"); return -1; e2e_ack: REF_UNSAFE( p_cell ); UNLOCK_HASH(p_msg->hash_index); e2eack_T = p_cell; set_t(0); LM_DBG("e2e proxy ACK found\n"); return -2; found: set_t(p_cell); REF_UNSAFE( T ); set_kr(REQ_EXIST); UNLOCK_HASH( p_msg->hash_index ); LM_DBG("transaction found (T=%p)\n",T); if (has_tran_tmcbs( T, TMCB_MSG_MATCHED_IN) ) run_trans_callbacks( TMCB_MSG_MATCHED_IN, T, p_msg, 0,0); return 1; }
/* lookup a transaction by callid and cseq, parameters are pure * header field content only, e.g. "[email protected]" and "11" */ int t_lookup_callid(struct cell ** trans, str callid, str cseq) { struct cell* p_cell; unsigned int hash_index; /* I use MAX_HEADER, not sure if this is a good choice... */ char callid_header[MAX_HEADER]; char cseq_header[MAX_HEADER]; /* save return value of print_* functions here */ char* endpos; UNUSED(endpos); /* need method, which is always INVITE in our case */ /* CANCEL is only useful after INVITE */ str invite_method; char* invite_string = INVITE; invite_method.s = invite_string; invite_method.len = INVITE_LEN; /* lookup the hash index where the transaction is stored */ hash_index=tm_hash(callid, cseq); if(hash_index >= TM_TABLE_ENTRIES){ LM_ERR("invalid hash_index=%u\n",hash_index); return -1; } /* create header fields the same way tm does itself, then compare headers */ endpos = print_callid_mini(callid_header, callid); LM_DBG("created comparable call_id header field: >%.*s<\n", (int)(endpos - callid_header), callid_header); endpos = print_cseq_mini(cseq_header, &cseq, &invite_method); LM_DBG("created comparable cseq header field: >%.*s<\n", (int)(endpos - cseq_header), cseq_header); LOCK_HASH(hash_index); /* all the transactions from the entry are compared */ p_cell = get_tm_table()->entrys[hash_index].first_cell; for ( ; p_cell; p_cell = p_cell->next_cell ) { /* compare complete header fields, casecmp to make sure invite=INVITE */ LM_DBG(" <%.*s> <%.*s>\n", p_cell->callid.len, p_cell->callid.s, p_cell->cseq_n.len,p_cell->cseq_n.s); if ( (strncmp(callid_header, p_cell->callid.s, p_cell->callid.len) == 0) && (strncasecmp(cseq_header, p_cell->cseq_n.s, p_cell->cseq_n.len) == 0) ) { LM_DBG("we have a match: callid=>>%.*s<< cseq=>>%.*s<<\n", p_cell->callid.len,p_cell->callid.s, p_cell->cseq_n.len, p_cell->cseq_n.s); REF_UNSAFE(p_cell); UNLOCK_HASH(hash_index); set_t(p_cell); *trans=p_cell; LM_DBG("transaction found.\n"); return 1; } LM_DBG("NO match: callid=%.*s cseq=%.*s\n", p_cell->callid.len, p_cell->callid.s, p_cell->cseq_n.len, p_cell->cseq_n.s); } UNLOCK_HASH(hash_index); LM_DBG("transaction not found.\n"); return -1; }