📄 t_lookup.c
字号:
/* in case, we act as UAS for INVITE and reply with 200, * we will need to run dialog-matching for subsequent * ACK, for which we need From-tag; We also need from-tag * in case people want to have proxied e2e ACKs accounted */ if (p_msg->REQ_METHOD==METHOD_INVITE && parse_from_header(p_msg)==-1) { LOG(L_ERR, "ERROR: t_check: from parsing failed\n"); return -1; } t_lookup_request( p_msg , 0 /* unlock before returning */ ); } else { /* we need Via for branch and Cseq method to distinguish replies with the same branch/cseqNr (CANCEL) */ if ( parse_headers(p_msg, HDR_VIA1|HDR_CSEQ, 0 )==-1 || !p_msg->via1 || !p_msg->cseq ) { LOG(L_ERR, "ERROR: reply cannot be parsed\n"); return -1; } /* if that is an INVITE, we will also need to-tag for later ACK matching */ if ( get_cseq(p_msg)->method.len==INVITE_LEN && memcmp( get_cseq(p_msg)->method.s, INVITE, INVITE_LEN )==0 ) { if (parse_headers(p_msg, HDR_TO, 0)==-1 || !p_msg->to) { LOG(L_ERR, "ERROR: INVITE reply cannot be parsed\n"); return -1; } } t_reply_matching( p_msg , param_branch!=0?param_branch:&local_branch ); }#ifdef EXTRA_DEBUG if ( T && T!=T_UNDEFINED && T->damocles) { LOG( L_ERR, "ERROR: transaction %p scheduled for deletion " "and called from t_check\n", T); abort(); }#endif DBG("DEBUG: t_check: msg id=%d global id=%d T end=%p\n", p_msg->id,global_msg_id,T); } else { if (T) DBG("DEBUG: t_check: T already found!\n"); else DBG("DEBUG: t_check: T previously sought and not found\n"); } return T ? (T==T_UNDEFINED ? -1 : 1 ) : 0;}int init_rb( struct retr_buf *rb, struct sip_msg *msg){ /*struct socket_info* send_sock;*/ struct via_body* via; int proto; int backup_mhomed; via=msg->via1; if (!reply_to_via) { update_sock_struct_from_ip( &rb->dst.to, msg ); proto=msg->rcv.proto; } else { /*init retrans buffer*/ if (update_sock_struct_from_via( &(rb->dst.to), msg, via )==-1) { LOG(L_ERR, "ERROR: init_rb: cannot lookup reply dst: %.*s\n", via->host.len, via->host.s ); ser_error=E_BAD_VIA; return 0; } proto=via->proto; } rb->dst.proto=proto; rb->dst.proto_reserved1=msg->rcv.proto_reserved1; /* turn off mhomed for generating replies -- they are ideally sent to where request came from to make life with NATs and other beasts easier */ backup_mhomed=mhomed; mhomed=0; mhomed=backup_mhomed; /* use for sending replies the incoming interface of the request -bogdan */ /*send_sock=get_send_socket(msg, &rb->dst.to, proto); if (send_sock==0) { LOG(L_ERR, "ERROR: init_rb: cannot fwd to af %d, proto %d " "no socket\n", rb->dst.to.s.sa_family, proto); ser_error=E_BAD_VIA; return 0; }*/ rb->dst.send_sock=msg->rcv.bind_address; return 1;}static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg){ struct sip_msg *shm_msg; shm_msg=new_cell->uas.request; new_cell->from.s=shm_msg->from->name.s; new_cell->from.len=HF_LEN(shm_msg->from); new_cell->to.s=shm_msg->to->name.s; new_cell->to.len=HF_LEN(shm_msg->to); new_cell->callid.s=shm_msg->callid->name.s; new_cell->callid.len=HF_LEN(shm_msg->callid); new_cell->cseq_n.s=shm_msg->cseq->name.s; new_cell->cseq_n.len=get_cseq(shm_msg)->number.s +get_cseq(shm_msg)->number.len -shm_msg->cseq->name.s; new_cell->method=new_cell->uas.request->first_line.u.request.method; if (p_msg->REQ_METHOD==METHOD_INVITE) new_cell->flags |= T_IS_INVITE_FLAG; new_cell->on_negative=get_on_negative(); new_cell->on_reply=get_on_reply();}static inline int new_t(struct sip_msg *p_msg){ struct cell *new_cell; /* for ACK-dlw-wise matching, we want From-tags */ if (p_msg->REQ_METHOD==METHOD_INVITE && parse_from_header(p_msg)<0) { LOG(L_ERR, "ERROR: new_t: no valid From in INVITE\n"); return E_BAD_REQ; } /* make sure uri will be parsed before cloning */ if (parse_sip_msg_uri(p_msg)<0) { LOG(L_ERR, "ERROR: new_t: uri invalid\n"); return E_BAD_REQ; } /* add new transaction */ new_cell = build_cell( p_msg ) ; if ( !new_cell ){ LOG(L_ERR, "ERROR: new_t: out of mem:\n"); return E_OUT_OF_MEM; } insert_into_hash_table_unsafe( new_cell, p_msg->hash_index ); set_t(new_cell); INIT_REF_UNSAFE(T); /* init pointers to headers needed to construct local requests such as CANCEL/ACK */ init_new_t(new_cell, p_msg); return 1;}/* atomic "new_tran" construct; it returns: <0 on error +1 if a request did not match a transaction - it that was an ack, the calling function shall forward statelessly - otherwise it means, a new transaction was introduced and the calling function shall reply/relay/whatever_appropriate 0 on retransmission*/int t_newtran( struct sip_msg* p_msg ){ int lret, my_err; /* is T still up-to-date ? */ DBG("DEBUG: t_newtran: msg id=%d , global msg id=%d ," " T on entrance=%p\n",p_msg->id,global_msg_id,T); if ( T && T!=T_UNDEFINED ) { LOG(L_ERR, "ERROR: t_newtran: " "transaction already in process %p\n", T ); return E_SCRIPT; } global_msg_id = p_msg->id; T = T_UNDEFINED; /* first of all, parse everything -- we will store in shared memory and need to have all headers ready for generating potential replies later; parsing later on demand is not an option since the request will be in shmem and applying parse_headers to it would intermix shmem with pkg_mem */ if (parse_headers(p_msg, HDR_EOH, 0 )) { LOG(L_ERR, "ERROR: t_newtran: parse_headers failed\n"); return E_BAD_REQ; } if ((p_msg->parsed_flag & HDR_EOH)!=HDR_EOH) { LOG(L_ERR, "ERROR: t_newtran: EoH not parsed\n"); return E_OUT_OF_MEM; } /* t_lookup_requests attempts to find the transaction; it also calls check_transaction_quadruple -> it is safe to assume we have from/callid/cseq/to */ lret = t_lookup_request( p_msg, 1 /* leave locked if not found */ ); /* on error, pass the error in the stack ... nothing is locked yet if 0 is returned */ if (lret==0) return E_BAD_TUPEL; /* transaction found, it's a retransmission */ if (lret>0) { if (p_msg->REQ_METHOD==METHOD_ACK) { t_release_transaction(T); } else { t_retransmit_reply(T); } /* things are done -- return from script */ return 0; } /* from now on, be careful -- hash table is locked */ if (lret==-2) { /* was it an e2e ACK ? if so, trigger a callback */ /* no callbacks? complete quickly */ if ( !has_tran_tmcbs(t_ack,TMCB_E2EACK_IN) ) { UNLOCK_HASH(p_msg->hash_index); return 1; } REF_UNSAFE(t_ack); UNLOCK_HASH(p_msg->hash_index); /* we don't call from within REPLY_LOCK -- that introduces * a race condition; however, it is so unlikely and the * impact is so small (callback called multiple times of * multiple ACK/200s received in parallel), that we do not * better waste time in locks */ if (unmatched_totag(t_ack, p_msg)) { run_trans_callbacks( TMCB_E2EACK_IN , t_ack, p_msg, 0, -p_msg->REQ_METHOD ); } UNREF(t_ack); return 1; } /* transaction not found, it's a new request (lret<0, lret!=-2); establish a new transaction ... */ if (p_msg->REQ_METHOD==METHOD_ACK) { /* ... unless it is in ACK */ my_err=1; goto new_err; } my_err=new_t(p_msg); if (my_err<0) { LOG(L_ERR, "ERROR: t_newtran: new_t failed\n"); goto new_err; } UNLOCK_HASH(p_msg->hash_index); /* now, when the transaction state exists, check if there is a meaningful Via and calculate it; better do it now than later: state is established so that subsequent retransmissions will be absorbed and will not possibly block during Via DNS resolution; doing it later would only burn more CPU as if there is an error, we cannot relay later whatever comes out of the the transaction */ if (!init_rb( &T->uas.response, p_msg)) { LOG(L_ERR, "ERROR: t_newtran: unresolvable via1\n"); put_on_wait( T ); t_unref(p_msg); return E_BAD_VIA; } return 1;new_err: UNLOCK_HASH(p_msg->hash_index); return my_err;}int t_unref( struct sip_msg* p_msg ){ enum kill_reason kr; if (T==T_UNDEFINED || T==T_NULL_CELL) return -1; if (p_msg->first_line.type==SIP_REQUEST){ kr=get_kr(); if (kr==0 ||(p_msg->REQ_METHOD==METHOD_ACK && !(kr & REQ_RLSD))) { LOG(L_WARN, "WARNING: script writer didn't release transaction\n"); t_release_transaction(T); } } UNREF( T ); set_t(T_UNDEFINED); return 1;}int t_get_trans_ident(struct sip_msg* p_msg, unsigned int* hash_index, unsigned int* label){ struct cell* t; if(t_check(p_msg,0) != 1){ LOG(L_ERR,"ERROR: t_get_trans_ident: no transaction found\n"); return -1; } t = get_t(); if(!t){ LOG(L_ERR,"ERROR: t_get_trans_ident: transaction found is NULL\n"); return -1; } *hash_index = t->hash_index; *label = t->label; return 1;}int t_lookup_ident(struct cell ** trans, unsigned int hash_index, unsigned int label){ struct cell* p_cell; if(hash_index >= TABLE_ENTRIES){ LOG(L_ERR,"ERROR: t_lookup_ident: invalid hash_index=%u\n",hash_index); return -1; } LOCK_HASH(hash_index); /* all the transactions from the entry are compared */ for ( p_cell = get_tm_table()->entrys[hash_index].first_cell; p_cell; p_cell = p_cell->next_cell ) { if(p_cell->label == label){ REF_UNSAFE(p_cell); UNLOCK_HASH(hash_index); set_t(p_cell); *trans=p_cell; DBG("DEBUG: t_lookup_ident: transaction found\n"); return 1; } } UNLOCK_HASH(hash_index); set_t(0); *trans=p_cell; DBG("DEBUG: t_lookup_ident: transaction not found\n"); return -1;}int t_is_local(struct sip_msg* p_msg){ struct cell* t; if(t_check(p_msg,0) != 1){ LOG(L_ERR,"ERROR: t_is_local: no transaction found\n"); return -1; } t = get_t(); if(!t){ LOG(L_ERR,"ERROR: t_is_local: transaction found is NULL\n"); return -1; } return is_local(t);}/* lookup a transaction by callid and cseq, parameters are pure * header field content only, e.g. "123@10.0.0.1" and "11" */int t_lookup_callid(struct cell ** trans, str callid, str cseq) { struct cell* p_cell; unsigned 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; /* 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=hash(callid, cseq); if(hash_index >= TABLE_ENTRIES){ LOG(L_ERR,"ERROR: t_lookup_callid: 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); DBG("created comparable call_id header field: >%.*s<\n", (int)(endpos - callid_header), callid_header); endpos = print_cseq_mini(cseq_header, &cseq, &invite_method); DBG("created comparable cseq header field: >%.*s<\n", (int)(endpos - cseq_header), cseq_header); LOCK_HASH(hash_index); DBG("just locked hash index %u, looking for transactions there:\n", hash_index); /* all the transactions from the entry are compared */ for ( p_cell = get_tm_table()->entrys[hash_index].first_cell; p_cell; p_cell = p_cell->next_cell ) { /* compare complete header fields, casecmp to make sure invite=INVITE */ 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) ) { 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; DBG("DEBUG: t_lookup_callid: transaction found.\n"); return 1; } 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); DBG("DEBUG: t_lookup_callid: transaction not found.\n"); return -1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -