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; new_cell->is_invite=p_msg->REQ_METHOD==METHOD_INVITE; new_cell->on_negative=get_on_negative(); new_cell->on_reply=get_on_reply(); }
static inline struct cseq_body* sel_parse_cseq(struct sip_msg* msg) { if (!msg->cseq && (parse_headers(msg, HDR_CSEQ_F, 0) == -1)) { LM_ERR("Unable to parse CSeq header\n"); return 0; } if (!msg->cseq) { LM_DBG("No CSeq header field found\n"); return 0; } return get_cseq(msg); }
/* is the ACK (p_msg) in p_msg dialog-wise equal to the INVITE (t_msg) * except to-tags? */ static inline int partial_dlg_matching(struct sip_msg *t_msg, struct sip_msg *p_msg) { struct to_body *inv_from; if (!EQ_LEN(callid)) return 0; if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len) return 0; inv_from=get_from(t_msg); if (!inv_from) { LOG(L_ERR, "ERROR: partial_dlg_matching: INV/From not parsed\n"); return 0; } if (inv_from->tag_value.len!=get_from(p_msg)->tag_value.len) return 0; if (!EQ_STR(callid)) return 0; if (memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s, get_cseq(p_msg)->number.len)!=0) return 0; if (memcmp(inv_from->tag_value.s, get_from(p_msg)->tag_value.s, get_from(p_msg)->tag_value.len)!=0) return 0; return 1; }
int sca_get_msg_cseq_number( sip_msg_t *msg ) { int cseq; assert( msg != NULL ); if ( SCA_HEADER_EMPTY( msg->cseq )) { LM_ERR( "Empty Cseq header" ); return( -1 ); } if ( str2int( &(get_cseq( msg )->number), (unsigned int *)&cseq ) != 0 ) { LM_ERR( "Bad Cseq header: %.*s", STR_FMT( &msg->cseq->body )); return( -1 ); } return( cseq ); }
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; } } }
inline static int t_check_trans(struct sip_msg* msg, char *foo, char *bar) { struct cell *trans; struct cell *bkup; int ret; if (msg->REQ_METHOD==METHOD_CANCEL) { /* parse needed hdrs*/ if (check_transaction_quadruple(msg)==0) { LOG(L_ERR, "ERROR:tm:t_check_trans: too few headers\n"); return 0; /*drop request!*/ } if (!msg->hash_index) msg->hash_index = hash(msg->callid->body,get_cseq(msg)->number); /* performe lookup */ trans = t_lookupOriginalT( msg ); if (trans) { UNREF( trans ); return 1; } else { return -1; } } else { bkup = get_t(); ret = t_lookup_request( msg , 0); if ( (trans=get_t())!=0 ) UNREF(trans); set_t( bkup ); switch (ret) { case 1: /* transaction found */ return 1; case -2: /* e2e ACK found */ return 1; default: /* notfound */ return -1; } } return ret; }
/** * This callback is called on any response message in the lifespan of * the dialog. The callback is called just before the message is * copied to pkg memory so it is still mutable. * * @param did - The dialog structure. The pointer is used as an ID. * @param type - The reason for the callback. DLGCB_CONFIRMED * @param params - The sst information */ static void sst_dialog_response_fwded_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params) { struct sip_msg* msg = params->msg; int *param; short info_dirty = 0; /* * This test to see if the message is a response sould ALWAYS be * true. This callback should not get called for requests. But * lets be safe. */ if (msg->first_line.type != SIP_REPLY) return; sst_msg_info_t minfo = {0,0,0,0}; sst_info_t *info = (sst_info_t *)*(params->param); sst_info_t tmp_info; LM_DBG("Dialog seen REPLY %d %.*s\n", msg->first_line.u.reply.statuscode, msg->first_line.u.reply.reason.len, msg->first_line.u.reply.reason.s); /* * Need to check to see if it is a 422 response. If it is, * make sure our Min-SE: for this dialog is set at least as * large as in the Min-SE: in the reply 422 message. If not, * we will create an INVITE, 422 loop. */ if (msg->first_line.u.reply.statuscode == 422) { if (parse_msg_for_sst_info(msg, &minfo)) { LM_ERR("failed to prase sst information for thr 422 reply\n"); return; } /* Make sure we do not try to use anything smaller */ if (info->interval < minfo.min_se) CHECK_AND_UPDATE_SST_INFO(info, interval, minfo.min_se, info_dirty); goto update_info; /* There is nothing else to do with this */ } /* * We need to get the method this reply is for from the CSEQ * body. The RFC states we can only play with 2XX from the * INVITE or reINVTE/UPDATE. */ if (!msg->cseq && ((parse_headers(msg, HDR_CSEQ_F, 0) == -1) || !msg->cseq)) { LM_ERR("failed to parse CSeq\n"); return; } /* 2XX replies to INVITES only !*/ if (msg->first_line.u.reply.statuscode > 199 && msg->first_line.u.reply.statuscode < 300 && (get_cseq(msg)->method_id == METHOD_INVITE || get_cseq(msg)->method_id == METHOD_UPDATE)) { if (parse_msg_for_sst_info(msg, &minfo)) { LM_ERR("failed to parse sst information for the 2XX reply\n"); return; } LM_DBG("parsing 200 OK response %d / %d\n", minfo.supported, minfo.se); if (info->supported != SST_UAC) { CHECK_AND_UPDATE_SST_INFO_TMP(info, supported, (minfo.supported?SST_UAS:SST_UNDF),info_dirty, tmp_info); } if (minfo.se != 0) { if (sst_interval > minfo.min_se) CHECK_AND_UPDATE_SST_INFO(info, interval, sst_interval, info_dirty); else CHECK_AND_UPDATE_SST_INFO_TMP(info, interval, MAX(minfo.se, sst_min_se), info_dirty, tmp_info); LM_DBG("UAS supports timer\n"); set_dialog_lifetime(did, info->interval); } else { /* no se header found, we want to resquest it. */ if (info->supported == SST_UAC) { char se_buf[80]; LM_DBG("UAC supports timer\n"); LM_DBG("appending the Session-Expires: header to the 2XX reply." " UAC will deal with it.\n"); /* * GOOD! we can just insert the Session-Expires: * header and forward back to the UAC and it will * deal with refreshing the session. */ if (sst_interval > minfo.min_se) CHECK_AND_UPDATE_SST_INFO(info, interval, sst_interval, info_dirty); else CHECK_AND_UPDATE_SST_INFO_TMP(info, interval, MAX(minfo.se, sst_min_se), info_dirty, tmp_info); snprintf(se_buf, 80, "Session-Expires: %d;refresher=uac\r\n", info->interval); if (append_header(msg, se_buf)) { LM_ERR("failed to append Session-Expires header\n"); return; } /* Set the dialog timeout HERE */ set_dialog_lifetime(did, info->interval); } else { /* We are sunk, uac did not request it, and it * does not support it */ LM_DBG("UAC and UAS do not support timers!" " No session timers for this session.\n"); param = find_param_export("dialog", "default_timeout", INT_PARAM); CHECK_AND_UPDATE_SST_INFO_TMP(info, interval, param?*param:12*3600, info_dirty, tmp_info); set_dialog_lifetime(did, info->interval); } } } /* End of 2XX for an INVITE */ update_info: if (info_dirty){ str raw_info = {(char*)info, sizeof(sst_info_t)}; if (dlg_binds->store_dlg_value(did, &info_val_name, &raw_info) != 0) { LM_ERR("sst_info can't be updated\n"); } } }
void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps) { struct sip_msg* msg= NULL; int lexpire= 0; unsigned int cseq; ua_pres_t* presentity= NULL, *hentity= NULL; struct to_body *pto= NULL, *pfrom = NULL; int size= 0; int flag ; str record_route= {0, 0}; int rt; str contact; int initial_request = 0; if(ps==NULL || ps->param== NULL || *ps->param== NULL ) { LM_ERR("null callback parameter\n"); return; } LM_DBG("completed with status %d\n",ps->code) ; hentity= (ua_pres_t*)(*ps->param); flag= hentity->flag; if(hentity->flag & XMPP_INITIAL_SUBS) hentity->flag= XMPP_SUBSCRIBE; /* get dialog information from reply message: callid, to_tag, from_tag */ msg= ps->rpl; if(msg == NULL) { LM_ERR("no reply message found\n "); goto error; } if(msg== FAKED_REPLY) { /* delete record from hash_table and call registered functions */ if(hentity->call_id.s== NULL) /* if a new requets failed-> do nothing*/ { LM_DBG("initial Subscribe request failed\n"); goto done; } lock_get(&HashT->p_records[hentity->hash_index].lock); presentity = get_htable_safe(hentity->hash_index, hentity->local_index); if(presentity) { delete_htable_safe(presentity, hentity->hash_index); lock_release(&HashT->p_records[hentity->hash_index].lock); } lock_release(&HashT->p_records[hentity->hash_index].lock); goto done; } if ( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("when parsing headers\n"); goto done; } /*if initial request */ if(hentity->call_id.s== NULL) { initial_request = 1; if(ps->code>= 300) { LM_DBG("initial Subscribe request failed\n"); goto done; } if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot parse callid header\n"); goto done; } if (!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); goto done; } if (msg->from->parsed == NULL) { if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); goto done; } } pfrom = (struct to_body*)msg->from->parsed; if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0) { LM_ERR("no from tag value present\n"); goto done; } if( msg->to==NULL || msg->to->body.s==NULL) { LM_ERR("cannot parse TO header\n"); goto done; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); goto done; } if( pto->tag_value.s ==NULL || pto->tag_value.len == 0) { LM_ERR("no to tag value present\n"); goto done; } hentity->call_id= msg->callid->body; hentity->to_tag= pto->tag_value; hentity->from_tag= pfrom->tag_value; } /* extract the other necesary information for inserting a new record */ if(ps->rpl->expires && msg->expires->body.len > 0) { if (!msg->expires->parsed && (parse_expires(msg->expires) < 0)) { LM_ERR("cannot parse Expires header\n"); goto done; } lexpire = ((exp_body_t*)msg->expires->parsed)->val; LM_DBG("lexpire= %d\n", lexpire); } if(ps->code >= 300 ) { /* if an error code and a stored dialog delete it and try to send a subscription with type= INSERT_TYPE, else return*/ if(!initial_request) { subs_info_t subs; lock_get(&HashT->p_records[hentity->hash_index].lock); presentity = get_htable_safe(hentity->hash_index, hentity->local_index); if(presentity) { hentity->event= presentity->event; delete_htable_safe(presentity, hentity->hash_index); lock_release(&HashT->p_records[hentity->hash_index].lock); } lock_release(&HashT->p_records[hentity->hash_index].lock); memset(&subs, 0, sizeof(subs_info_t)); subs.pres_uri= hentity->pres_uri; subs.to_uri = hentity->to_uri; subs.watcher_uri= hentity->watcher_uri; subs.contact= &hentity->contact; if(hentity->remote_contact.s) subs.remote_target= &hentity->remote_contact; if(hentity->desired_expires== 0) subs.expires= -1; else if(hentity->desired_expires< (int)time(NULL)) subs.expires= 0; else subs.expires= hentity->desired_expires- (int)time(NULL)+ 3; subs.flag= INSERT_TYPE; subs.source_flag= flag; subs.event= hentity->event; subs.id= hentity->id; subs.outbound_proxy= hentity->outbound_proxy; subs.extra_headers= &hentity->extra_headers; subs.cb_param= hentity->cb_param; if(send_subscribe(&subs)< 0) { LM_ERR("when trying to send SUBSCRIBE\n"); goto done; } } goto done; } /*if a 2XX reply handle the two cases- an existing dialog and a new one*/ /* extract the contact */ if(msg->contact== NULL || msg->contact->body.s== NULL) { LM_ERR("no contact header found"); goto error; } if( parse_contact(msg->contact) <0 ) { LM_ERR(" cannot parse contact header\n"); goto error; } if(msg->contact->parsed == NULL) { LM_ERR("cannot parse contact header\n"); goto error; } contact = ((contact_body_t* )msg->contact->parsed)->contacts->uri; if(!initial_request) { /* do not delete the dialog - allow Notifies to be recognized as * inside a known dialog */ if (lexpire == 0) lexpire = 5; LM_DBG("*** Update expires\n"); update_htable(hentity->hash_index, hentity->local_index, lexpire, NULL, &contact); goto done; } /* if a new dialog -> insert */ if(lexpire== 0) { LM_DBG("expires= 0: no not insert\n"); goto done; } if( msg->cseq==NULL || msg->cseq->body.s==NULL) { LM_ERR("cannot parse cseq header\n"); goto done; } if( str2int( &(get_cseq(msg)->number), &cseq)< 0) { LM_ERR("while converting str to int\n"); goto done; } /*process record route and add it to a string*/ if (msg->record_route!=NULL) { rt = print_rr_body(msg->record_route, &record_route, 1, 0); if(rt != 0) { LM_ERR("parsing record route [%d]\n", rt); record_route.s=NULL; record_route.len=0; } } size= sizeof(ua_pres_t)+ 2*sizeof(str)+(hentity->pres_uri->len+ pto->uri.len+ pfrom->uri.len+ pto->tag_value.len+ pfrom->tag_value.len +msg->callid->body.len+ record_route.len+ hentity->contact.len+ hentity->id.len )*sizeof(char); presentity= (ua_pres_t*)shm_malloc(size); if(presentity== NULL) { LM_ERR("no more share memory\n"); if(record_route.s) pkg_free(record_route.s); goto done; } memset(presentity, 0, size); size= sizeof(ua_pres_t); presentity->pres_uri= (str*)( (char*)presentity+ size); size+= sizeof(str); presentity->pres_uri->s= (char*)presentity+ size; memcpy(presentity->pres_uri->s, hentity->pres_uri->s, hentity->pres_uri->len); presentity->pres_uri->len= hentity->pres_uri->len; size+= hentity->pres_uri->len; presentity->to_uri.s= (char*)presentity+ size; memcpy(presentity->to_uri.s, pto->uri.s, pto->uri.len); presentity->to_uri.len= pto->uri.len; size+= pto->uri.len; presentity->watcher_uri= (str*)( (char*)presentity+ size); size+= sizeof(str); presentity->watcher_uri->s= (char*)presentity+ size; memcpy(presentity->watcher_uri->s, pfrom->uri.s, pfrom->uri.len); presentity->watcher_uri->len= pfrom->uri.len; size+= pfrom->uri.len; presentity->call_id.s= (char*)presentity + size; memcpy(presentity->call_id.s,msg->callid->body.s, msg->callid->body.len); presentity->call_id.len= msg->callid->body.len; size+= presentity->call_id.len; presentity->to_tag.s= (char*)presentity + size; memcpy(presentity->to_tag.s,pto->tag_value.s, pto->tag_value.len); presentity->to_tag.len= pto->tag_value.len; size+= pto->tag_value.len; presentity->from_tag.s= (char*)presentity + size; memcpy(presentity->from_tag.s,pfrom->tag_value.s, pfrom->tag_value.len); presentity->from_tag.len= pfrom->tag_value.len; size+= pfrom->tag_value.len; if(record_route.len && record_route.s) { presentity->record_route.s= (char*)presentity + size; memcpy(presentity->record_route.s, record_route.s, record_route.len); presentity->record_route.len= record_route.len; size+= record_route.len; pkg_free(record_route.s); } presentity->contact.s= (char*)presentity + size; memcpy(presentity->contact.s, hentity->contact.s, hentity->contact.len); presentity->contact.len= hentity->contact.len; size+= hentity->contact.len; if(hentity->id.s) { presentity->id.s=(char*)presentity+ size; memcpy(presentity->id.s, hentity->id.s, hentity->id.len); presentity->id.len= hentity->id.len; size+= presentity->id.len; } if(hentity->extra_headers.s && hentity->extra_headers.len) { presentity->extra_headers.s= (char*)shm_malloc(hentity->extra_headers.len* sizeof(char)); if(presentity->extra_headers.s== NULL) { ERR_MEM(SHARE_MEM); } memcpy(presentity->extra_headers.s, hentity->extra_headers.s, hentity->extra_headers.len); presentity->extra_headers.len= hentity->extra_headers.len; } /* write the remote contact filed */ presentity->remote_contact.s= (char*)shm_malloc(contact.len* sizeof(char)); if(presentity->remote_contact.s== NULL) { ERR_MEM(SHARE_MEM); } memcpy(presentity->remote_contact.s, contact.s, contact.len); presentity->remote_contact.len= contact.len; presentity->event|= hentity->event; presentity->flag= hentity->flag; presentity->etag.s= NULL; presentity->cseq= cseq; presentity->desired_expires= hentity->desired_expires; presentity->expires= lexpire+ (int)time(NULL); if(BLA_SUBSCRIBE & presentity->flag) { LM_DBG("BLA_SUBSCRIBE FLAG inserted\n"); } LM_DBG("record for subscribe from %.*s to %.*s inserted in datatbase\n", presentity->watcher_uri->len, presentity->watcher_uri->s, presentity->pres_uri->len, presentity->pres_uri->s); insert_htable(presentity); done: if(hentity->ua_flag == REQ_OTHER) { hentity->flag= flag; run_pua_callbacks( hentity, msg); } error: if(hentity) { if(presentity) { if(presentity->extra_headers.s) shm_free(presentity->extra_headers.s); if(presentity->remote_contact.s) shm_free(presentity->remote_contact.s); } shm_free(hentity); hentity= NULL; } return; }
int th_update_hdr_replaces(sip_msg_t *msg) { struct hdr_field *hf = NULL; str replaces; str rcallid; struct lump* l; str out; LM_DBG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); if(th_param_mask_callid==0) return 0; if(!((get_cseq(msg)->method_id)&(METHOD_INVITE))) return 0; for (hf=msg->headers; hf; hf=hf->next) { if (hf->name.len==8 && strncasecmp(hf->name.s, "Replaces", 8)==0) break; } if(hf==NULL) return 0; replaces = hf->body; trim(&replaces); rcallid.s = replaces.s; for(rcallid.len=0; rcallid.len<replaces.len; rcallid.len++) { if(rcallid.s[rcallid.len]==';') break; } if(rcallid.len>th_callid_prefix.len && strncmp(rcallid.s, th_callid_prefix.s, th_callid_prefix.len)==0) { /* value encoded - decode it */ out.s = th_mask_decode(rcallid.s, rcallid.len, &th_callid_prefix, 0, &out.len); } else { /* value decoded - encode it */ out.s = th_mask_encode(rcallid.s, rcallid.len, &th_callid_prefix, &out.len); } if(out.s==NULL) { LM_ERR("cannot update Replaces callid\n"); return -1; } l=del_lump(msg, rcallid.s-msg->buf, rcallid.len, 0); if (l==0) { LM_ERR("failed deleting Replaces callid\n"); pkg_free(out.s); return -1; } if (insert_new_lump_after(l, out.s, out.len, 0)==0) { LM_ERR("could not insert new lump\n"); pkg_free(out.s); return -1; } return 0; }
/* Determine current transaction * * Found Not Found Error (e.g. parsing) * Return Value 1 0 -1 * T ptr 0 T_UNDEFINED */ int t_check( struct sip_msg* p_msg , int *param_branch ) { int local_branch; int canceled; /* is T still up-to-date ? */ DBG("DEBUG: t_check: msg id=%d global id=%d T start=%p\n", p_msg->id,global_msg_id,T); if ( p_msg->id != global_msg_id || T==T_UNDEFINED ) { global_msg_id = p_msg->id; T = T_UNDEFINED; /* transaction lookup */ if ( p_msg->first_line.type==SIP_REQUEST ) { /* force parsing all the needed headers*/ if (parse_headers(p_msg, HDR_EOH_F, 0 )==-1) { LOG(L_ERR, "ERROR: t_check: parsing error\n"); return -1; } /* 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 */, &canceled); } else { /* we need Via for branch and Cseq method to distinguish replies with the same branch/cseqNr (CANCEL) and we need all the WWW/Proxy Authenticate headers for 401 & 407 replies */ if (tm_aggregate_auth && (p_msg->REPLY_STATUS==401 || p_msg->REPLY_STATUS==407)){ if (parse_headers(p_msg, HDR_EOH_F,0)==-1){ LOG(L_WARN, "WARNING: the reply cannot be " "completely parsed\n"); /* try to continue, via1 & cseq are checked below */ } }else if ( parse_headers(p_msg, HDR_VIA1_F|HDR_CSEQ_F, 0 )==-1) { LOG(L_ERR, "ERROR: reply cannot be parsed\n"); return -1; } if ((p_msg->via1==0) || (p_msg->cseq==0)){ LOG(L_ERR, "ERROR: reply doesn't have a via or cseq header\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_F, 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->flags & (T_IN_AGONY)) { LOG( L_ERR, "ERROR: transaction %p scheduled for deletion " "and called from t_check (flags=%x)\n", T, T->flags); 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; }
/*! * \brief Function that is registered as TM callback and called on replies * * Function that is registered as TM callback and called on replies. It * parses the reply and set the appropriate event. This is then used to * update the dialog state, run eventual dialog callbacks and save or * update the necessary informations about the dialog. * \see next_state_dlg * \param t transaction, unused * \param type type of the entered callback * \param param saved dialog structure in the callback */ static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param) { struct dlg_cell *dlg; struct dlg_cell_out *dlg_out = 0; int new_state, old_state, unref, event; str to_tag, to_uri; struct sip_msg *req = param->req; struct sip_msg *rpl = param->rpl; struct dlg_entry_out* dlg_entry_out = 0; if (t && t->fwded_totags) LM_DBG("ONREPLY CALL_BACK from TM received and type is [%i] and TO is [%.*s]\n", type, t->fwded_totags->tag.len, t->fwded_totags->tag.s); else LM_DBG("ONREPLY CALL_BACK from TM received and type is [%i]\n", type); dlg = (struct dlg_cell *) (*param->param); if (shutdown_done || dlg == 0) return; if (t) { dlg->transaction = t; } LM_DBG("DLG dialogid is entry:id [%i:%i]\n", dlg->h_entry, dlg->h_id); if (type == TMCB_RESPONSE_FWDED) { // The state does not change, but the msg is mutable in this callback LM_DBG("TMCB_RESPONSE_FWDED from TM received"); run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); return; } if (type == TMCB_RESPONSE_OUT) { LM_DBG("TMCB_RESPONSE_OUT\n"); return; } if (type == TMCB_RESPONSE_READY) { if (rpl == FAKED_REPLY) { LM_DBG("Faked reply\n"); return; } // get to tag LM_DBG("Extracting to-tag from reply"); if (!rpl->to && ((parse_headers(rpl, HDR_TO_F, 0) < 0) || !rpl->to)) { LM_ERR("bad reply or missing TO hdr :-/\n"); to_tag.s = 0; to_tag.len = 0; } else { //populate to uri for this branch. to_uri = get_to(rpl)->uri; to_tag = get_to(rpl)->tag_value; if (to_tag.s == 0 || to_tag.len == 0) { LM_ERR("missing TAG param in TO hdr :-/\n"); to_tag.s = 0; to_tag.len = 0; //Here we assume that the transaction module timer will remove any early dialogs return; } } LM_DBG("Got to-tag from response: %.*s \n", to_tag.len, to_tag.s); } if (type == TMCB_DESTROY) event = DLG_EVENT_TDEL; else if (param->code < 200) event = DLG_EVENT_RPL1xx; else if (param->code < 300) event = DLG_EVENT_RPL2xx; else event = DLG_EVENT_RPL3xx; LM_DBG("Calling next_state_dlg and event is %i\n", event); next_state_dlg(dlg, event, &old_state, &new_state, &unref, &to_tag); if (type == TMCB_RESPONSE_READY) { LM_DBG("Checking if there is an existing dialog_out entry with same to-tag"); dlg_entry_out = &dlg->dlg_entry_out; lock_get(dlg->dlg_out_entries_lock); dlg_out = dlg_entry_out->first; LM_DBG("Scanning dlg_entry_out list for dlg_out"); while (dlg_out) { //Check if there is an already dialog_out entry with same To-tag if (dlg_out->to_tag.len == to_tag.len && memcmp(dlg_out->to_tag.s, to_tag.s, dlg_out->to_tag.len) == 0) { //Found a dialog_out entry with same to_tag! LM_DBG("Found dlg_out for to-tag: %.*s\n", dlg_out->to_tag.len, dlg_out->to_tag.s); break; } dlg_out = dlg_out->next; } lock_release(dlg->dlg_out_entries_lock); if (!dlg_out) { LM_DBG("No dlg_out entry found - creating a new dialog_out entry on dialog [%p]\n", dlg); dlg_out = build_new_dlg_out(dlg, &to_uri, &to_tag); link_dlg_out(dlg, dlg_out, 0); /* save callee's cseq, caller cseq, callee contact and callee record route*/ if (populate_leg_info(dlg, rpl, t, DLG_CALLEE_LEG, &to_tag) != 0) { LM_ERR("could not add further info to the dlg out\n"); } if (!dlg_out) { LM_ERR("failed to create new dialog out structure\n"); //TODO do something on this error! } } else { //This dlg_out already exists, update cseq and contact if present LM_DBG("dlg_out entry found - updating cseq's for dialog out [%p] for to-tag [%.*s] \n", dlg_out, dlg_out->to_tag.len, dlg_out->to_tag.s); if ((!rpl->cseq && parse_headers(rpl, HDR_CSEQ_F, 0) < 0) || !rpl->cseq || !rpl->cseq->parsed) { LM_ERR("bad sip message or missing CSeq hdr :-/\n"); } dlg_update_cseq(dlg, DLG_CALLEE_LEG, &((get_cseq(rpl))->number), &(dlg_out->to_tag)); /* extract the contact address to update if present*/ if (!rpl->contact && (parse_headers(rpl, HDR_CONTACT_F, 0) < 0 || !rpl->contact)) { LM_ERR("Can not update callee contact: bad sip message or missing Contact hdr\n"); } else if (parse_contact(rpl->contact) < 0 || ((contact_body_t *) rpl->contact->parsed)->contacts == NULL || ((contact_body_t *) rpl->contact->parsed)->contacts->next != NULL) { LM_ERR("Can not update callee contact: bad Contact HDR\n"); } else { str contact; contact = ((contact_body_t *) rpl->contact->parsed)->contacts->uri; dlg_update_contact(dlg, DLG_CALLEE_LEG, &contact, &(dlg_out->to_tag)); } } } if (new_state == DLG_STATE_EARLY) { run_dlg_callbacks(DLGCB_EARLY, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); return; } LM_DBG("new state is %i and old state is %i\n", new_state, old_state); if ((new_state == DLG_STATE_CONFIRMED) && (event == DLG_EVENT_RPL2xx)) { LM_DBG("dialog %p confirmed \n", dlg); //Remove all the other entries in dialog_out for the same dialog after TM expires the transaction //(not before in order to absorb late in-early-dialog requests). //remove all other dlg_out objects if (dlg_out) { if (d_tmb.register_tmcb(req, NULL, TMCB_DESTROY, unlink_dlgouts_from_cb, (void*) dlg, NULL) < 0) { LM_ERR("failed to register deletion delay function\n"); LM_DBG("Removing all other DLGs"); dlg_remove_dlg_out(dlg_out, dlg, 0); } else { //mark the outs for deletion dlg_remove_dlg_out(dlg_out, dlg, 1); } } else { LM_ERR("There is no dlg_out structure - this is bad\n"); //TODO: add error handling here } /* set start time */ dlg->start_ts = (unsigned int) (time(0)); /* save the settings to the database, * if realtime saving mode configured- save dialog now * else: the next time the timer will fire the update*/ dlg->dflags |= DLG_FLAG_NEW; if (0 != insert_dlg_timer(&dlg->tl, dlg->lifetime)) { LM_CRIT("Unable to insert dlg %p [%u:%u] on event %d [%d->%d] " "with clid '%.*s' and tags '%.*s' \n", dlg, dlg->h_entry, dlg->h_id, event, old_state, new_state, dlg->callid.len, dlg->callid.s, dlg->from_tag.len, dlg->from_tag.s); } else { ref_dlg(dlg, 1); } run_dlg_callbacks(DLGCB_CONFIRMED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); if (unref) unref_dlg(dlg, unref); return; } if (new_state == DLG_STATE_CONCURRENTLY_CONFIRMED && (old_state == DLG_STATE_CONFIRMED || old_state == DLG_STATE_CONCURRENTLY_CONFIRMED)) { //This is a concurrently confirmed call LM_DBG("This is a concurrently confirmed call."); //Create a new Dialog ID token “X” //Not sure how to do this so just going to use existing Did and add an X character to it str new_did; create_concurrent_did(dlg, &new_did); //assign new did to the created or updated dialog_out entry. update_dlg_out_did(dlg_out, &new_did); //Then, duplicate the dialog_in entry and set its Dialog ID value to new_did //for now rather just create new dlg structure with the correct params - this should be fixed if future use requires struct dlg_cell *new_dlg = 0; new_dlg = build_new_dlg(&(dlg->callid) /*callid*/, &(dlg->from_uri) /*from uri*/, &(dlg->from_tag)/*from_tag*/, &(dlg->req_uri) /*r-uri*/); //assign new did to dlg_in update_dlg_did(new_dlg, &new_did); if (new_dlg == 0) { LM_ERR("failed to create new dialog\n"); return; } //link the new_dlg with dlg_out object link_dlg_out(new_dlg, dlg_out, 0); } if (old_state != DLG_STATE_DELETED && new_state == DLG_STATE_DELETED) { LM_DBG("dialog %p failed (negative reply)\n", dlg); /* dialog setup not completed (3456XX) */ run_dlg_callbacks(DLGCB_FAILED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0); /* do unref */ if (unref) unref_dlg(dlg, unref); return; } if (unref) unref_dlg(dlg, unref); return; }
/* forwards a request to dst * parameters: * msg - sip msg * dst - destination name, if non-null it will be resolved and * send_info updated with the ip/port. Even if dst is non * null send_info must contain the protocol and if a non * default port or non srv. lookup is desired, the port must * be !=0 * port - used only if dst!=0 (else the port in send_info->to is used) * send_info - value/result partially filled dest_info structure: * - send_info->proto and comp are used * - send_info->to will be filled (dns) * - send_info->send_flags is filled from the message * - if the send_socket member is null, a send_socket will be * chosen automatically * WARNING: don't forget to zero-fill all the unused members (a non-zero * random id along with proto==PROTO_TCP can have bad consequences, same for * a bogus send_socket value) */ int forward_request(struct sip_msg* msg, str* dst, unsigned short port, struct dest_info* send_info) { unsigned int len; char* buf; char md5[MD5_LEN]; struct socket_info* orig_send_sock; /* initial send_sock */ int ret; struct ip_addr ip; /* debugging only */ char proto; #ifdef USE_DNS_FAILOVER struct socket_info* prev_send_sock; int err; struct dns_srv_handle dns_srv_h; prev_send_sock=0; err=0; #endif buf=0; orig_send_sock=send_info->send_sock; proto=send_info->proto; ret=0; if(dst){ #ifdef USE_DNS_FAILOVER if (cfg_get(core, core_cfg, use_dns_failover)){ dns_srv_handle_init(&dns_srv_h); err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port, &proto, dns_flags); if (err!=0){ LOG(L_ERR, "ERROR: forward_request: resolving \"%.*s\"" " failed: %s [%d]\n", dst->len, ZSW(dst->s), dns_strerror(err), err); ret=E_BAD_ADDRESS; goto error; } }else #endif if (sip_hostport2su(&send_info->to, dst, port, &proto)<0){ LOG(L_ERR, "ERROR: forward_request: bad host name %.*s," " dropping packet\n", dst->len, ZSW(dst->s)); ret=E_BAD_ADDRESS; goto error; } }/* dst */ send_info->send_flags=msg->fwd_send_flags; /* calculate branch for outbound request; if syn_branch is turned off, calculate is from transaction key, i.e., as an md5 of From/To/CallID/ CSeq exactly the same way as TM does; good for reboot -- than messages belonging to transaction lost due to reboot will still be forwarded with the same branch parameter and will be match-able downstream if it is turned on, we don't care about reboot; we simply put a simple value in there; better for performance */ if (syn_branch ) { memcpy(msg->add_to_branch_s, "z9hG4bKcydzigwkX", 16); msg->add_to_branch_len=16; } else { if (!char_msg_val( msg, md5 )) { /* parses transaction key */ LOG(L_ERR, "ERROR: forward_request: char_msg_val failed\n"); ret=E_UNSPEC; goto error; } msg->hash_index=hash( msg->callid->body, get_cseq(msg)->number); if (!branch_builder( msg->hash_index, 0, md5, 0 /* 0-th branch */, msg->add_to_branch_s, &msg->add_to_branch_len )) { LOG(L_ERR, "ERROR: forward_request: branch_builder failed\n"); ret=E_UNSPEC; goto error; } } /* try to send the message until success or all the ips are exhausted * (if dns lookup is performed && the dns cache used ) */ #ifdef USE_DNS_FAILOVER do{ #endif if (orig_send_sock==0) /* no forced send_sock => find it **/ send_info->send_sock=get_send_socket(msg, &send_info->to, proto); if (send_info->send_sock==0){ LOG(L_ERR, "forward_req: ERROR: cannot forward to af %d, proto %d " "no corresponding listening socket\n", send_info->to.s.sa_family, proto); ret=ser_error=E_NO_SOCKET; #ifdef USE_DNS_FAILOVER /* continue, maybe we find a socket for some other ip */ continue; #else goto error; #endif } #ifdef USE_DNS_FAILOVER if (prev_send_sock!=send_info->send_sock){ /* rebuild the message only if the send_sock changed */ prev_send_sock=send_info->send_sock; #endif if (buf) pkg_free(buf); send_info->proto=proto; buf = build_req_buf_from_sip_req(msg, &len, send_info, 0); if (!buf){ LOG(L_ERR, "ERROR: forward_request: building failed\n"); ret=E_OUT_OF_MEM; /* most probable */ goto error; } #ifdef USE_DNS_FAILOVER } #endif /* send it! */ DBG("Sending:\n%.*s.\n", (int)len, buf); DBG("orig. len=%d, new_len=%d, proto=%d\n", msg->len, len, send_info->proto ); if (run_onsend(msg, send_info, buf, len)==0){ su2ip_addr(&ip, &send_info->to); LOG(L_INFO, "forward_request: request to %s:%d(%d) dropped" " (onsend_route)\n", ip_addr2a(&ip), su_getport(&send_info->to), send_info->proto); ser_error=E_OK; /* no error */ ret=E_ADM_PROHIBITED; #ifdef USE_DNS_FAILOVER continue; /* try another ip */ #else goto error; /* error ? */ #endif } #ifdef USE_DST_BLACKLIST if (cfg_get(core, core_cfg, use_dst_blacklist)){ if (dst_is_blacklisted(send_info, msg)){ su2ip_addr(&ip, &send_info->to); LOG(L_DBG, "DEBUG: blacklisted destination:%s:%d (%d)\n", ip_addr2a(&ip), su_getport(&send_info->to), send_info->proto); ret=ser_error=E_SEND; #ifdef USE_DNS_FAILOVER continue; /* try another ip */ #else goto error; #endif } } #endif if (msg_send(send_info, buf, len)<0){ ret=ser_error=E_SEND; #ifdef USE_DST_BLACKLIST (void)dst_blacklist_add(BLST_ERR_SEND, send_info, msg); #endif #ifdef USE_DNS_FAILOVER continue; /* try another ip */ #else goto error; #endif }else{ ret=ser_error=E_OK; /* sent requests stats */ STATS_TX_REQUEST( msg->first_line.u.request.method_value ); /* exit succcesfully */ goto end; } #ifdef USE_DNS_FAILOVER }while(dst && cfg_get(core, core_cfg, use_dns_failover) && dns_srv_handle_next(&dns_srv_h, err) && ((err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port, &proto, dns_flags))==0)); if ((err!=0) && (err!=-E_DNS_EOR)){ LOG(L_ERR, "ERROR: resolving %.*s host name in uri" " failed: %s [%d] (dropping packet)\n", dst->len, ZSW(dst->s), dns_strerror(err), err); ret=ser_error=E_BAD_ADDRESS; goto error; } #endif error: STATS_TX_DROPS; end: #ifdef USE_DNS_FAILOVER if (dst && cfg_get(core, core_cfg, use_dns_failover)){ dns_srv_handle_put(&dns_srv_h); } #endif if (buf) pkg_free(buf); /* received_buf & line_buf will be freed in receive_msg by free_lump_list*/ #if defined STATS_REQ_FWD_OK || defined STATS_REQ_FWD_DROP if(ret==0) STATS_REQ_FWD_OK(); else STATS_REQ_FWD_DROP(); #endif /* STATS_REQ_FWD_* */ return ret; }
int tps_request_sent(sip_msg_t *msg, int dialog, int direction, int local) { tps_data_t mtsd; tps_data_t stsd; tps_data_t *ptsd; str lkey; memset(&mtsd, 0, sizeof(tps_data_t)); memset(&stsd, 0, sizeof(tps_data_t)); ptsd = &mtsd; if(tps_pack_request(msg, &mtsd)<0) { LM_ERR("failed to extract and pack the headers\n"); return -1; } if(direction==TPS_DIR_DOWNSTREAM) { lkey = get_from(msg)->tag_value; } else { lkey = get_to(msg)->tag_value; } tps_storage_lock_get(&lkey); if(dialog==0) { if(tps_storage_record(msg, ptsd)<0) { goto error; } } /* local generated requests */ if(local) { /* ACK and CANCEL go downstream */ if(get_cseq(msg)->method_id==METHOD_ACK || get_cseq(msg)->method_id==METHOD_CANCEL || local==2) { // th_mask_callid(&msg); goto done; } else { /* should be for upstream */ goto done; } } tps_remove_headers(msg, HDR_RECORDROUTE_T); tps_remove_headers(msg, HDR_CONTACT_T); tps_remove_headers(msg, HDR_VIA_T); tps_reinsert_via(msg, ptsd, &ptsd->x_via1); if(direction==TPS_DIR_UPSTREAM) { tps_reinsert_contact(msg, ptsd, &ptsd->as_contact); } else { tps_reinsert_contact(msg, ptsd, &ptsd->bs_contact); } done: tps_storage_lock_release(&lkey); return 0; error: tps_storage_lock_release(&lkey); return -1; }
int tps_request_sent(sip_msg_t *msg, int dialog, int local) { tps_data_t mtsd; tps_data_t btsd; tps_data_t stsd; tps_data_t *ptsd; str lkey; str xuuid; int direction = TPS_DIR_DOWNSTREAM; LM_DBG("handling outgoing request\n"); memset(&mtsd, 0, sizeof(tps_data_t)); memset(&btsd, 0, sizeof(tps_data_t)); memset(&stsd, 0, sizeof(tps_data_t)); ptsd = &mtsd; if(tps_pack_message(msg, &mtsd)<0) { LM_ERR("failed to extract and pack the headers\n"); return -1; } if(dialog!=0) { if(tps_get_xuuid(msg, &xuuid)<0) { LM_DBG("no x-uuid header - nothing to do\n"); return 0; } mtsd.a_uuid = xuuid; tps_remove_xuuid(msg); } lkey = msg->callid->body; tps_storage_lock_get(&lkey); if(tps_storage_load_branch(msg, &mtsd, &btsd)!=0) { if(tps_storage_record(msg, ptsd, dialog)<0) { goto error; } } else { ptsd = &btsd; } if(dialog!=0) { if(tps_storage_load_dialog(msg, &mtsd, &stsd)==0) { ptsd = &stsd; } } /* local generated requests */ if(local) { /* ACK and CANCEL go downstream */ if(get_cseq(msg)->method_id==METHOD_ACK || get_cseq(msg)->method_id==METHOD_CANCEL || local==2) { // ts_mask_callid(&msg); goto done; } else { /* should be for upstream */ goto done; } } tps_remove_headers(msg, HDR_RECORDROUTE_T); tps_remove_headers(msg, HDR_CONTACT_T); tps_remove_headers(msg, HDR_VIA_T); tps_reinsert_via(msg, &mtsd, &mtsd.x_via1); if(direction==TPS_DIR_UPSTREAM) { tps_reinsert_contact(msg, ptsd, &ptsd->as_contact); } else { tps_reinsert_contact(msg, ptsd, &ptsd->bs_contact); } if(dialog!=0) { tps_storage_end_dialog(msg, &mtsd, ptsd); } done: tps_storage_lock_release(&lkey); return 0; error: tps_storage_lock_release(&lkey); return -1; }
int tps_pack_message(sip_msg_t *msg, tps_data_t *ptsd) { hdr_field_t *hdr; via_body_t *via; rr_t *rr; int i; int vlen; int r2; if(ptsd->cp==NULL) { ptsd->cp = ptsd->cbuf; } i = 0; for(hdr=msg->h_via1; hdr; hdr=next_sibling_hdr(hdr)) { for(via=(struct via_body*)hdr->parsed; via; via=via->next) { i++; vlen = tps_skip_rw(via->name.s, via->bsize); if(ptsd->cp + vlen + 2 >= ptsd->cbuf + TPS_DATA_SIZE) { LM_ERR("no more spage to pack via headers\n"); return -1; } if(i>1) { *ptsd->cp = ','; ptsd->cp++; if(i>2) { ptsd->x_via2.len++; } } memcpy(ptsd->cp, via->name.s, vlen); if(i==1) { ptsd->x_via1.s = ptsd->cp; ptsd->x_via1.len = vlen; if(via->branch!=NULL) { ptsd->x_vbranch1.s = ptsd->x_via1.s + (via->branch->value.s - via->name.s); ptsd->x_vbranch1.len = via->branch->value.len; } } else { if(i==2) { ptsd->x_via2.s = ptsd->cp; } ptsd->x_via2.len += vlen; } ptsd->cp += vlen; } } LM_DBG("compacted headers - x_via1: [%.*s](%d) - x_via2: [%.*s](%d)" " - x_vbranch1: [%.*s](%d)\n", ptsd->x_via1.len, ZSW(ptsd->x_via1.s), ptsd->x_via1.len, ptsd->x_via2.len, ZSW(ptsd->x_via2.s), ptsd->x_via2.len, ptsd->x_vbranch1.len, ZSW(ptsd->x_vbranch1.s), ptsd->x_vbranch1.len); ptsd->a_rr.len = 0; ptsd->s_rr.len = 0; i = 0; r2 = 0; for(hdr=msg->record_route; hdr; hdr=next_sibling_hdr(hdr)) { if (parse_rr(hdr) < 0) { LM_ERR("failed to parse RR\n"); return -1; } for(rr =(rr_t*)hdr->parsed; rr; rr=rr->next) { i++; if(ptsd->cp + rr->nameaddr.uri.len + 4 >= ptsd->cbuf + TPS_DATA_SIZE) { LM_ERR("no more spage to pack rr headers\n"); return -1; } if(i>1) { if(i==2 &&r2==0) { ptsd->s_rr.len = ptsd->a_rr.len; } if(i==3 &&r2==1) { ptsd->s_rr.len = ptsd->a_rr.len; } *ptsd->cp = ','; ptsd->cp++; ptsd->a_rr.len++; } *ptsd->cp = '<'; if(i==1) { ptsd->a_rr.s = ptsd->cp; ptsd->s_rr.s = ptsd->cp; } if(i==2 && r2==0) { ptsd->a_rr.s = ptsd->cp; ptsd->a_rr.len = 0; } if(i==3 && r2==1) { ptsd->a_rr.s = ptsd->cp; ptsd->a_rr.len = 0; } ptsd->cp++; ptsd->a_rr.len++; memcpy(ptsd->cp, rr->nameaddr.uri.s, rr->nameaddr.uri.len); if(i==1) { ptsd->bs_contact.s = ptsd->cp; ptsd->bs_contact.len = rr->nameaddr.uri.len; if(_strnstr(ptsd->bs_contact.s, ";r2=on", ptsd->bs_contact.len)==0) { LM_DBG("single record routing by proxy\n"); ptsd->as_contact.s = ptsd->cp; ptsd->as_contact.len = rr->nameaddr.uri.len; } else { r2 = 1; } } else { if(i==2 && ptsd->as_contact.len==0) { LM_DBG("double record routing by proxy\n"); ptsd->as_contact.s = ptsd->cp; ptsd->as_contact.len = rr->nameaddr.uri.len; } } ptsd->a_rr.len += rr->nameaddr.uri.len; ptsd->cp += rr->nameaddr.uri.len; *ptsd->cp = '>'; ptsd->cp++; ptsd->a_rr.len++; } } if(i==1) { ptsd->s_rr.len = ptsd->a_rr.len; ptsd->a_rr.len = 0; } if(i==2 && r2==1) { ptsd->s_rr.len = ptsd->a_rr.len; ptsd->a_rr.len = 0; } LM_DBG("compacted headers - a_rr: [%.*s](%d) - b_rr: [%.*s](%d)" " - s_rr: [%.*s](%d)\n", ptsd->a_rr.len, ZSW(ptsd->a_rr.s), ptsd->a_rr.len, ptsd->b_rr.len, ZSW(ptsd->b_rr.s), ptsd->b_rr.len, ptsd->s_rr.len, ZSW(ptsd->s_rr.s), ptsd->s_rr.len); LM_DBG("compacted headers - as_contact: [%.*s](%d) - bs_contact: [%.*s](%d)\n", ptsd->as_contact.len, ZSW(ptsd->as_contact.s), ptsd->as_contact.len, ptsd->bs_contact.len, ZSW(ptsd->bs_contact.s), ptsd->bs_contact.len); ptsd->x_rr = ptsd->a_rr; ptsd->s_method_id = get_cseq(msg)->method_id; return 0; }
/** * This callback is called on any response message in the lifespan of * the dialog. The callback is called just before the message is * copied to pkg memory so it is still mutable. * * @param did - The dialog structure. The pointer is used as an ID. * @param type - The reason for the callback. DLGCB_CONFIRMED * @param params - The sst information */ static void sst_dialog_response_fwded_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params) { struct sip_msg* msg = params->msg; /* * This test to see if the message is a response sould ALWAYS be * true. This callback should not get called for requests. But * lets be safe. */ if (msg->first_line.type == SIP_REPLY) { sst_msg_info_t minfo = {0,0,0,0}; sst_info_t *info = (sst_info_t *)*(params->param); LM_DBG("Dialog seen REPLY %d %.*s\n", msg->first_line.u.reply.statuscode, msg->first_line.u.reply.reason.len, msg->first_line.u.reply.reason.s); /* * Need to check to see if it is a 422 response. If it is, * make sure our Min-SE: for this dialog is set at least as * large as in the Min-SE: in the reply 422 message. If not, * we will create an INVITE, 422 loop. */ if (msg->first_line.u.reply.statuscode == 422) { if (parse_msg_for_sst_info(msg, &minfo)) { LM_ERR("failed to prase sst information for thr 422 reply\n"); return; } /* Make sure we do not try to use anything smaller */ info->interval = MAX(info->interval, minfo.min_se); return; /* There is nothing else to do with this */ } /* * We need to get the method this reply is for from the CSEQ * body. The RFC states we can only play with 2XX from the * INVITE or reINVTE/UPDATE. */ if (!msg->cseq && ((parse_headers(msg, HDR_CSEQ_F, 0) == -1) || !msg->cseq)) { LM_ERR("failed to parse CSeq\n"); return; } /* 2XX replies to INVITES only !*/ if (msg->first_line.u.reply.statuscode > 199 && msg->first_line.u.reply.statuscode < 300 && (get_cseq(msg)->method_id == METHOD_INVITE || get_cseq(msg)->method_id == METHOD_UPDATE)) { if (parse_msg_for_sst_info(msg, &minfo)) { LM_ERR("failed to parse sst information for the 2XX reply\n"); return; } LM_DBG("parsing 200 OK response %d / %d\n", minfo.supported, minfo.se); if (info->supported != SST_UAC) { info->supported = (minfo.supported?SST_UAS:SST_UNDF); } if (minfo.se != 0) { if (sst_interval > minfo.min_se) info->interval = sst_interval; else info->interval = MAX(minfo.se, sst_min_se); LM_DBG("UAS supports timer\n"); if (set_timeout_avp(msg, info->interval)) { // FIXME: need an error message here return; } } else { /* no se header found, we want to resquest it. */ if (info->supported == SST_UAC) { char se_buf[80]; LM_DBG("UAC supports timer\n"); LM_DBG("appending the Session-Expires: header to the 2XX reply." " UAC will deal with it.\n"); /* * GOOD! we can just insert the Session-Expires: * header and forward back to the UAC and it will * deal with refreshing the session. */ if (sst_interval > minfo.min_se) info->interval = sst_interval; else info->interval = MAX(minfo.se, sst_min_se); snprintf(se_buf, 80, "Session-Expires: %d;refresher=uac\r\n", info->interval); if (append_header(msg, se_buf)) { LM_ERR("failed to append Session-Expires header\n"); return; } /* Set the dialog timeout HERE */ if (set_timeout_avp(msg, info->interval)) { return; } } else { /* We are sunk, uac did not request it, and it * does not support it */ LM_DBG("UAC and UAS do not support timers!" " No session timers for this session.\n"); /* Disable the dialog timeout HERE */ if (set_timeout_avp(msg, 0)) { return; } } } } /* End of 2XX for an INVITE */ } /* If the msg is a repsonse and not a request */ }
/*! \brief * Fills the common part (for all contacts) of the info structure */ static inline ucontact_info_t* pack_ci( struct sip_msg* _m, contact_t* _c, unsigned int _e, unsigned int _f, int _use_regid) { static ucontact_info_t ci; static str no_ua = str_init("n/a"); static str callid; static str path_received = {0,0}; static str path; static str received = {0,0}; static int received_found; static unsigned int allowed, allow_parsed; static struct sip_msg *m = 0; int_str val; if (_m!=0) { memset( &ci, 0, sizeof(ucontact_info_t)); /* Get callid of the message */ callid = _m->callid->body; trim_trailing(&callid); if (callid.len > CALLID_MAX_SIZE) { rerrno = R_CALLID_LEN; LM_ERR("callid too long\n"); goto error; } ci.callid = &callid; /* Get CSeq number of the message */ if (str2int(&get_cseq(_m)->number, (unsigned int*)&ci.cseq) < 0) { rerrno = R_INV_CSEQ; LM_ERR("failed to convert cseq number\n"); goto error; } /* set received socket */ if (_m->flags&sock_flag) { ci.sock = get_sock_val(_m); if (ci.sock==0) ci.sock = _m->rcv.bind_address; } else { ci.sock = _m->rcv.bind_address; } /* set tcp connection id */ if (_m->rcv.proto==PROTO_TCP || _m->rcv.proto==PROTO_TLS || _m->rcv.proto==PROTO_WS || _m->rcv.proto==PROTO_WSS) { ci.tcpconn_id = _m->rcv.proto_reserved1; } else { ci.tcpconn_id = -1; } /* additional info from message */ if (parse_headers(_m, HDR_USERAGENT_F, 0) != -1 && _m->user_agent && _m->user_agent->body.len>0 && _m->user_agent->body.len<MAX_UA_SIZE) { ci.user_agent = &_m->user_agent->body; } else { ci.user_agent = &no_ua; } /* extract Path headers */ if (path_enabled) { if (build_path_vector(_m, &path, &path_received) < 0) { rerrno = R_PARSE_PATH; goto error; } if (path.len && path.s) { ci.path = &path; if (path_mode != PATH_MODE_OFF) { /* save in msg too for reply */ if (set_path_vector(_m, &path) < 0) { rerrno = R_PARSE_PATH; goto error; } } } } ci.last_modified = act_time; /* set flags */ ci.flags = _f; getbflagsval(0, &ci.cflags); /* get received */ if (path_received.len && path_received.s) { ci.cflags |= ul.nat_flag; ci.received = path_received; } allow_parsed = 0; /* not parsed yet */ received_found = 0; /* not found yet */ m = _m; /* remember the message */ } else { memset( &ci.instance, 0, sizeof(str)); } if(_c!=0) { /* hook uri address - should be more than 'sip:' chars */ if(_c->uri.s!=NULL && _c->uri.len>4) ci.c = &_c->uri; /* Calculate q value of the contact */ if (m && m->id == q_override_msg_id) { ci.q = q_override_value; } else if (calc_contact_q(_c->q, &ci.q) < 0) { rerrno = R_INV_Q; LM_ERR("failed to calculate q\n"); goto error; } /* set expire time */ ci.expires = _e; /* Get methods of contact */ if (_c->methods) { if (parse_methods(&(_c->methods->body), &ci.methods) < 0) { rerrno = R_PARSE; LM_ERR("failed to parse contact methods\n"); goto error; } } else { /* check on Allow hdr */ if (allow_parsed == 0) { if (m && parse_allow( m ) != -1) { allowed = get_allow_methods(m); } else { allowed = ALL_METHODS; } allow_parsed = 1; } ci.methods = allowed; } /* get received */ if (ci.received.len==0) { if (_c->received) { ci.received = _c->received->body; } else { if (received_found==0) { memset(&val, 0, sizeof(int_str)); if (rcv_avp_name.n!=0 && search_first_avp(rcv_avp_type, rcv_avp_name, &val, 0) && val.s.len > 0) { if (val.s.len>RECEIVED_MAX_SIZE) { rerrno = R_CONTACT_LEN; LM_ERR("received too long\n"); goto error; } received = val.s; } else { received.s = 0; received.len = 0; } received_found = 1; } ci.received = received; } } if(_c->instance!=NULL && _c->instance->body.len>0) ci.instance = _c->instance->body; if(_use_regid && _c->instance!=NULL && _c->reg_id!=NULL && _c->reg_id->body.len>0) { if(str2int(&_c->reg_id->body, &ci.reg_id)<0 || ci.reg_id==0) { LM_ERR("invalid reg-id value\n"); goto error; } } if(sruid_next(&_reg_sruid)<0) goto error; ci.ruid = _reg_sruid.uid; LM_DBG("generated ruid is: %.*s\n", ci.ruid.len, ci.ruid.s); } return &ci; error: return 0; }
static void __dialog_sendpublish(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { str tag = {0,0}; struct to_body from; str peer_uri= {0, 0}; char flag = DLG_PUB_AB; str flag_str; struct to_body peer_to_body; str entity_uri= {0, 0}; int buf_len = 255; struct sip_msg* msg = _params->msg; flag_str.s = &flag; flag_str.len = 1; memset(&from, 0, sizeof(struct to_body)); memset(&peer_to_body, 0, sizeof(struct to_body)); from.uri = dlg->from_uri; peer_uri.len = buf_len; peer_uri.s = (char*)pkg_malloc(buf_len); if(peer_uri.s == NULL) { LM_ERR("No more memory\n"); goto error; } /* extract the peer_uri */ if(dlg_api.fetch_dlg_value(dlg, &peer_dlg_var, &peer_uri, 1) < 0 || peer_uri.len==0) { LM_ERR("Failed to fetch peer uri dialog variable\n"); goto error; } LM_DBG("peer_uri = %.*s\n", peer_uri.len, peer_uri.s); parse_to(peer_uri.s, peer_uri.s+peer_uri.len, &peer_to_body); if(peer_to_body.error != PARSE_OK) { LM_ERR("Failed to peer uri [%.*s]\n", peer_uri.len, peer_uri.s); goto error; } /* try to extract the flag */ dlg_api.fetch_dlg_value(dlg, &flag_dlg_var, &flag_str, 1); LM_DBG("flag = %c\n", flag); entity_uri.len = buf_len; entity_uri.s = (char*)pkg_malloc(buf_len); if(entity_uri.s == NULL) { LM_ERR("No more memory\n"); goto error; } /* check if entity is also custom */ if(dlg_api.fetch_dlg_value(dlg, &entity_dlg_var, &entity_uri, 1) == 0) { /* overwrite from with this value */ parse_to(entity_uri.s, entity_uri.s + entity_uri.len, &from); if(from.error != PARSE_OK) { LM_ERR("Wrong format for entity body\n"); goto error; } LM_DBG("entity_uri = %.*s\n", entity_uri.len, entity_uri.s); LM_DBG("from uri = %.*s\n", from.uri.len, from.uri.s); } switch (type) { case DLGCB_FAILED: case DLGCB_TERMINATED: case DLGCB_EXPIRED: LM_DBG("dialog over, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); if(flag == DLG_PUB_AB || flag == DLG_PUB_A) dialog_publish("terminated", &from, &peer_to_body, &(dlg->callid), 1, 0, 0, 0); if(flag == DLG_PUB_AB || flag == DLG_PUB_B) dialog_publish("terminated", &peer_to_body, &from, &(dlg->callid), 0, 0, 0, 0); break; case DLGCB_RESPONSE_WITHIN: if (get_cseq(msg)->method_id==METHOD_INVITE) { if (msg->flags & nopublish_flag) { LM_DBG("nopublish flag was set for this INVITE\n"); break; } LM_DBG("nopublish flag not set for this INVITE, will publish\n"); } else { /* no publish for non-INVITEs */ break; } case DLGCB_CONFIRMED: LM_DBG("dialog confirmed, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); if(flag == DLG_PUB_AB || flag == DLG_PUB_A) dialog_publish("confirmed", &from, &peer_to_body, &(dlg->callid), 1, dlg->lifetime, 0, 0); if(flag == DLG_PUB_AB || flag == DLG_PUB_B) dialog_publish("confirmed", &peer_to_body, &from, &(dlg->callid), 0, dlg->lifetime, 0, 0); break; case DLGCB_EARLY: LM_DBG("dialog is early, from=%.*s\n", from.uri.len, from.uri.s); if (include_tags) { /* get to tag*/ if ( !_params->msg->to && ((parse_headers(_params->msg, HDR_TO_F,0)<0) || !_params->msg->to) ) { LM_ERR("bad reply or missing TO hdr :-/\n"); tag.s = 0; tag.len = 0; } else { tag = get_to(_params->msg)->tag_value; if (tag.s==0 || tag.len==0) { LM_ERR("missing TAG param in TO hdr :-/\n"); tag.s = 0; tag.len = 0; } } if(flag == DLG_PUB_AB || flag == DLG_PUB_A) { if (caller_confirmed) { dialog_publish("confirmed", &from, &peer_to_body, &(dlg->callid), 1, dlg->lifetime, &(dlg->legs[DLG_CALLER_LEG].tag), &tag); } else { dialog_publish("early", &from, &peer_to_body, &(dlg->callid), 1, dlg->lifetime, &(dlg->legs[DLG_CALLER_LEG].tag), &tag); } } if(flag == DLG_PUB_AB || flag == DLG_PUB_B) { dialog_publish("early", &peer_to_body, &from, &(dlg->callid), 0, dlg->lifetime, &tag, &(dlg->legs[DLG_CALLER_LEG].tag)); } } else { if(flag == DLG_PUB_AB || flag == DLG_PUB_A) { if (caller_confirmed) { dialog_publish("confirmed", &from, &peer_to_body, &(dlg->callid), 1, dlg->lifetime, 0, 0); } else { dialog_publish("early", &from, &peer_to_body, &(dlg->callid), 1, dlg->lifetime, 0, 0); } } if(flag == DLG_PUB_AB || flag == DLG_PUB_B) { dialog_publish("early", &peer_to_body, &from, &(dlg->callid), 0, dlg->lifetime, 0, 0); } } break; default: LM_ERR("unhandled dialog callback type %d received, from=%.*s\n", type, dlg->from_uri.len, dlg->from_uri.s); if(flag == DLG_PUB_AB || flag == DLG_PUB_A) dialog_publish("terminated", &from, &peer_to_body, &(dlg->callid), 1, 0, 0, 0); if(flag == DLG_PUB_AB || flag == DLG_PUB_B) dialog_publish("terminated", &peer_to_body, &from, &(dlg->callid), 0, 0, 0, 0); } error: if(peer_uri.s) pkg_free(peer_uri.s); if(entity_uri.s) pkg_free(entity_uri.s); if (peer_to_body.param_lst) free_to_params(&peer_to_body); if (from.param_lst) free_to_params(&from); }
/* function lookups transaction being canceled by CANCEL in p_msg; * it returns: * 0 - transaction wasn't found * T - transaction found */ struct cell* t_lookupOriginalT( struct sip_msg* p_msg ) { struct cell *p_cell; unsigned int hash_index; struct sip_msg *t_msg; struct via_param *branch; int foo; int ret; /* start searching in the table */ if (!(p_msg->msg_flags & FL_HASH_INDEX)){ /* parse all*/ if (check_transaction_quadruple(p_msg)==0) { LOG(L_ERR, "ERROR: TM module: t_lookupOriginalT:" " too few headers\n"); /* stop processing */ return 0; } p_msg->hash_index=hash( p_msg->callid->body , get_cseq(p_msg)->number); p_msg->msg_flags|=FL_HASH_INDEX; } hash_index = p_msg->hash_index; DBG("DEBUG: t_lookupOriginalT: searching on hash entry %d\n",hash_index ); /* 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) { LOG(L_ERR, "ERROR: t_lookupOriginalT: no via\n"); 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(hash_index); ret=matching_3261(p_msg, &p_cell, /* we are seeking the original transaction -- * skip CANCEL transactions during search */ METHOD_CANCEL, &foo); if (ret==1) goto found; else goto notfound; } /* no cookies --proceed to old-fashioned pre-3261 t-matching */ 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 ) { t_msg = p_cell->uas.request; if (!t_msg) continue; /* skip UAC transactions */ /* we don't cancel CANCELs ;-) */ if (t_msg->REQ_METHOD==METHOD_CANCEL) continue; /* check lengths now */ if (!EQ_LEN(callid)) continue; if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len) continue; if (!EQ_LEN(from)) continue; #ifdef CANCEL_TAG if (!EQ_LEN(to)) continue; #else /* relaxed matching -- we don't care about to-tags anymore, * many broken UACs screw them up and ignoring them does not * actually hurt */ if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len) continue; #endif if (ruri_matching && !EQ_REQ_URI_LEN) continue; if (via1_matching && !EQ_VIA_LEN(via1)) continue; /* check the content now */ 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; #ifdef CANCEL_TAG if (!EQ_STR(to)) continue; #else if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s, get_to(t_msg)->uri.len)!=0) continue; #endif if (ruri_matching && !EQ_REQ_URI_STR) continue; if (via1_matching && !EQ_VIA_STR(via1)) continue; /* found */ goto found; } notfound: /* no transaction found */ DBG("DEBUG: t_lookupOriginalT: no CANCEL matching found! \n" ); UNLOCK_HASH(hash_index); DBG("DEBUG: t_lookupOriginalT completed\n"); return 0; found: DBG("DEBUG: t_lookupOriginalT: canceled transaction" " found (%p)! \n",p_cell ); REF_UNSAFE( p_cell ); UNLOCK_HASH(hash_index); DBG("DEBUG: t_lookupOriginalT completed\n"); return p_cell; }
int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked, int* cancel) { struct cell *p_cell; unsigned int isACK; struct sip_msg *t_msg; int ret; struct via_param *branch; int match_status; struct cell *e2e_ack_trans; /* parse all*/ if (check_transaction_quadruple(p_msg)==0) { LOG(L_ERR, "ERROR: TM module: t_lookup_request: too few headers\n"); set_t(0); /* stop processing */ return 0; } /* start searching into the table */ if (!(p_msg->msg_flags & FL_HASH_INDEX)){ p_msg->hash_index=hash( p_msg->callid->body , get_cseq(p_msg)->number); p_msg->msg_flags|=FL_HASH_INDEX; } isACK = p_msg->REQ_METHOD==METHOD_ACK; DBG("t_lookup_request: start searching: hash=%d, isACK=%d\n", p_msg->hash_index,isACK); /* assume not found */ ret=-1; e2e_ack_trans = 0; /* 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) { LOG(L_ERR, "ERROR: t_lookup_request: 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, cancel); 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 */ DBG("DEBUG: proceeding to pre-RFC3261 transaction matching\n"); *cancel=0; /* 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) { /* for non-ACKs we want same method matching, we * make an exception for pre-exisiting CANCELs because we * want to set *cancel */ if ((t_msg->REQ_METHOD!=p_msg->REQ_METHOD) && (t_msg->REQ_METHOD!=METHOD_CANCEL)) continue; /* compare lengths first */ 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; 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 (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 (!EQ_STR(to)) continue; if (ruri_matching && !EQ_REQ_URI_STR) continue; if (via1_matching && !EQ_VIA_STR(via1)) continue; if ((t_msg->REQ_METHOD==METHOD_CANCEL) && (p_msg->REQ_METHOD!=METHOD_CANCEL)){ /* we've matched an existing CANCEL */ *cancel=1; continue; } /* request matched ! */ DBG("DEBUG: non-ACK matched\n"); goto found; } else { /* it's an ACK request*/ /* ACK's relate only to INVITEs */ if (t_msg->REQ_METHOD!=METHOD_INVITE) 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; /* it is e2e ACK/200 */ if (p_cell->uas.status<300 && e2e_ack_trans==0) { /* all criteria for proxied ACK are ok */ if (p_cell->relayed_reply_branch!=-2) { e2e_ack_trans=p_cell; continue; } /* it's a local UAS transaction */ if (dlg_matching(p_cell, p_msg)) goto found; 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! */ DBG("DEBUG: non-2xx ACK matched\n"); goto found; } /* ACK */ } /* synonym loop */ notfound: if (e2e_ack_trans) { p_cell=e2e_ack_trans; goto e2e_ack; } /* no transaction found */ set_t(0); if (!leave_new_locked) { UNLOCK_HASH(p_msg->hash_index); } DBG("DEBUG: t_lookup_request: no transaction found\n"); return -1; e2e_ack: t_ack=p_cell; /* e2e proxied ACK */ set_t(0); if (!leave_new_locked) { UNLOCK_HASH(p_msg->hash_index); } DBG("DEBUG: t_lookup_request: 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 ); DBG("DEBUG: t_lookup_request: transaction found (T=%p)\n",T); return 1; }
/*! \brief * Fills the common part (for all contacts) of the info structure */ static inline ucontact_info_t* pack_ci( struct sip_msg* _m, contact_t* _c, unsigned int _e, unsigned int _f, unsigned int _flags) { static ucontact_info_t ci; static str no_ua = str_init("n/a"); static str callid; static str path_received = {0,0}; static str path; static str received = {0,0}; static int received_found; static unsigned int allowed, allow_parsed; static struct sip_msg *m = 0; int_str val; if (_m!=0) { memset( &ci, 0, sizeof(ucontact_info_t)); /* Get callid of the message */ callid = _m->callid->body; trim_trailing(&callid); if (callid.len > CALLID_MAX_SIZE) { rerrno = R_CALLID_LEN; LM_ERR("callid too long\n"); goto error; } ci.callid = &callid; /* Get CSeq number of the message */ if (str2int(&get_cseq(_m)->number, (unsigned int*)&ci.cseq) < 0) { rerrno = R_INV_CSEQ; LM_ERR("failed to convert cseq number\n"); goto error; } /* set received socket */ if ( _flags®_SAVE_SOCKET_FLAG) { ci.sock = get_sock_hdr(_m); if (ci.sock==0) ci.sock = _m->rcv.bind_address; } else { ci.sock = _m->rcv.bind_address; } /* additional info from message */ if (parse_headers(_m, HDR_USERAGENT_F, 0) != -1 && _m->user_agent && _m->user_agent->body.len>0 && _m->user_agent->body.len<UA_MAX_SIZE) { ci.user_agent = &_m->user_agent->body; } else { ci.user_agent = &no_ua; } /* extract Path headers */ if ( _flags®_SAVE_PATH_FLAG ) { if (build_path_vector(_m, &path, &path_received, _flags) < 0) { rerrno = R_PARSE_PATH; goto error; } if (path.len && path.s) { ci.path = &path; /* save in msg too for reply */ if (set_path_vector(_m, &path) < 0) { rerrno = R_PARSE_PATH; goto error; } } } ci.last_modified = act_time; /* set flags */ ci.flags = _f; ci.cflags = getb0flags(); /* get received */ if (path_received.len && path_received.s) { ci.cflags |= ul.nat_flag; ci.received = path_received; } allow_parsed = 0; /* not parsed yet */ received_found = 0; /* not found yet */ m = _m; /* remember the message */ } if(_c!=0) { /* Calculate q value of the contact */ if (calc_contact_q(_c->q, &ci.q) < 0) { rerrno = R_INV_Q; LM_ERR("failed to calculate q\n"); goto error; } /* set expire time */ ci.expires = _e; /* Get methods of contact */ if (_c->methods) { if (parse_methods(&(_c->methods->body), &ci.methods) < 0) { rerrno = R_PARSE; LM_ERR("failed to parse contact methods\n"); goto error; } } else { /* check on Allow hdr */ if (allow_parsed == 0) { if (m && parse_allow( m ) != -1) { allowed = get_allow_methods(m); } else { allowed = ALL_METHODS; } allow_parsed = 1; } ci.methods = allowed; } /* get received */ if (ci.received.len==0) { if (_c->received) { ci.received = _c->received->body; } else { if (received_found==0) { memset(&val, 0, sizeof(int_str)); if (rcv_avp_name>=0 && search_first_avp(rcv_avp_type, rcv_avp_name, &val, 0) && val.s.len > 0) { if (val.s.len>RECEIVED_MAX_SIZE) { rerrno = R_CONTACT_LEN; LM_ERR("received too long\n"); goto error; } received = val.s; } else { received.s = 0; received.len = 0; } received_found = 1; } ci.received = received; } } } return &ci; error: return 0; }
int tps_response_sent(sip_msg_t *msg) { tps_data_t mtsd; tps_data_t stsd; tps_data_t btsd; str lkey; int direction = TPS_DIR_UPSTREAM; str xvbranch = {0, 0}; LM_DBG("handling outgoing response\n"); memset(&mtsd, 0, sizeof(tps_data_t)); memset(&stsd, 0, sizeof(tps_data_t)); memset(&btsd, 0, sizeof(tps_data_t)); if(tps_get_xbranch(msg, &xvbranch)<0) { LM_DBG("no x-branch header - nothing to do\n"); return 0; } if(tps_pack_message(msg, &mtsd)<0) { LM_ERR("failed to extract and pack the headers\n"); return -1; } mtsd.x_vbranch1 = xvbranch; tps_remove_xbranch(msg); if(get_cseq(msg)->method_id==METHOD_MESSAGE) { tps_remove_headers(msg, HDR_RECORDROUTE_T); tps_remove_headers(msg, HDR_CONTACT_T); return 0; } lkey = msg->callid->body; tps_storage_lock_get(&lkey); if(tps_storage_load_branch(msg, &mtsd, &btsd)<0) { goto error; } LM_DBG("loaded dialog a_uuid [%.*s]\n", btsd.a_uuid.len, ZSW(btsd.a_uuid.s)); if(tps_storage_load_dialog(msg, &btsd, &stsd)<0) { goto error; } tps_storage_lock_release(&lkey); tps_remove_headers(msg, HDR_RECORDROUTE_T); tps_remove_headers(msg, HDR_CONTACT_T); if(direction==TPS_DIR_UPSTREAM) { tps_reinsert_contact(msg, &stsd, &stsd.as_contact); } else { tps_reinsert_contact(msg, &stsd, &stsd.bs_contact); } tps_reappend_rr(msg, &btsd, &btsd.x_rr); return 0; error: tps_storage_lock_release(&lkey); return -1; }
void dlg_bridge_tm_callback(struct cell *t, int type, struct tmcb_params *ps) { struct sip_msg *msg = NULL; dlg_transfer_ctx_t *dtc = NULL; struct dlg_cell *dlg = NULL; str s; str cseq; str empty = {"", 0}; if(ps->param==NULL || *ps->param==0) { LM_DBG("message id not received\n"); return; } dtc = *((dlg_transfer_ctx_t**)ps->param); if(dtc==NULL) return; LM_DBG("completed with status %d\n", ps->code); if(ps->code>=300) goto error; /* 2xx - build dialog/send refer */ msg = ps->rpl; if((msg->cseq==NULL || parse_headers(msg,HDR_CSEQ_F,0)<0) || msg->cseq==NULL || msg->cseq->parsed==NULL) { LM_ERR("bad sip message or missing CSeq hdr :-/\n"); goto error; } cseq = (get_cseq(msg))->number; if((msg->to==NULL && parse_headers(msg, HDR_TO_F,0)<0) || msg->to==NULL) { LM_ERR("bad request or missing TO hdr\n"); goto error; } if(parse_from_header(msg)) { LM_ERR("bad request or missing FROM hdr\n"); goto error; } if((msg->callid==NULL && parse_headers(msg,HDR_CALLID_F,0)<0) || msg->callid==NULL){ LM_ERR("bad request or missing CALLID hdr\n"); goto error; } s = msg->callid->body; trim(&s); /* some sanity checks */ if (s.len==0 || get_from(msg)->tag_value.len==0) { LM_ERR("invalid request -> callid (%d) or from TAG (%d) empty\n", s.len, get_from(msg)->tag_value.len); goto error; } dlg = build_new_dlg(&s /*callid*/, &(get_from(msg)->uri) /*from uri*/, &(get_to(msg)->uri) /*to uri*/, &(get_from(msg)->tag_value)/*from_tag*/, &(get_to(msg)->uri) /*use to as r-uri*/ ); if (dlg==0) { LM_ERR("failed to create new dialog\n"); goto error; } dtc->dlg = dlg; if (dlg_set_leg_info(dlg, &(get_from(msg)->tag_value), &empty, &dlg_bridge_controller, &cseq, DLG_CALLER_LEG)!=0) { LM_ERR("dlg_set_leg_info failed\n"); goto error; } if (populate_leg_info(dlg, msg, t, DLG_CALLEE_LEG, &(get_to(msg)->tag_value)) !=0) { LM_ERR("could not add further info to the dialog\n"); shm_free(dlg); goto error; } if(dlg_refer_callee(dtc)!=0) goto error; return; error: dlg_transfer_ctx_free(dtc); return; }
/* this is the "UAC" above transaction layer; if a final reply is received, it triggers a callback; note well -- it assumes it is entered locked with REPLY_LOCK and it returns unlocked! */ enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch, unsigned int msg_status, branch_bm_t *cancel_bitmap) { /* how to deal with replies for local transaction */ int local_store, local_winner; enum rps reply_status; struct sip_msg *winning_msg; int winning_code; int totag_retr; /* branch_bm_t cancel_bitmap; */ /* keep warning 'var might be used un-inited' silent */ winning_msg=0; winning_code=0; totag_retr=0; *cancel_bitmap=0; reply_status=t_should_relay_response( t, msg_status, branch, &local_store, &local_winner, cancel_bitmap, p_msg ); LM_DBG("branch=%d, save=%d, winner=%d\n", branch, local_store, local_winner ); if (local_store) { if (!store_reply(t, branch, p_msg)) goto error; } if (local_winner>=0) { winning_msg= branch==local_winner ? p_msg : t->uac[local_winner].reply; if (winning_msg==FAKED_REPLY) { winning_code = branch==local_winner ? msg_status : t->uac[local_winner].last_received; } else { winning_code=winning_msg->REPLY_STATUS; } t->uas.status = winning_code; stats_trans_rpl( winning_code, (winning_msg==FAKED_REPLY)?1:0 ); if (is_invite(t) && winning_msg!=FAKED_REPLY && winning_code>=200 && winning_code <300 && has_tran_tmcbs(t, TMCB_RESPONSE_OUT|TMCB_RESPONSE_PRE_OUT) ) { totag_retr=update_totag_set(t, winning_msg); } } UNLOCK_REPLIES(t); if ( local_winner >= 0 ) { if (winning_code < 200) { if (!totag_retr && has_tran_tmcbs(t,TMCB_LOCAL_RESPONSE_OUT)) { LM_DBG("Passing provisional reply %d to FIFO application\n", winning_code); run_trans_callbacks( TMCB_LOCAL_RESPONSE_OUT, t, 0, winning_msg, winning_code); } } else { LM_DBG("local transaction completed\n"); if (!totag_retr && has_tran_tmcbs(t,TMCB_LOCAL_COMPLETED) ) { run_trans_callbacks( TMCB_LOCAL_COMPLETED, t, 0, winning_msg, winning_code ); } } } return reply_status; error: which_cancel(t, cancel_bitmap); UNLOCK_REPLIES(t); cleanup_uac_timers(t); if ( get_cseq(p_msg)->method_id==METHOD_INVITE ) cancel_uacs( t, *cancel_bitmap ); put_on_wait(t); return RPS_ERROR; }
/*! * \brief Parse SIP message and populate leg informations * * Parse SIP message and populate leg informations. * \param dlg the dialog to add cseq, contact & record_route * \param msg sip message * \param t transaction * \param leg type of the call leg * \param tag SIP To tag * \return 0 on success, -1 on failure * \note for a request: get record route in normal order, for a reply get * in reverse order, skipping the ones from the request and the proxies' own */ int populate_leg_info(struct dlg_cell *dlg, struct sip_msg *msg, struct cell* t, unsigned int leg, str *tag) { unsigned int skip_recs; str cseq; str contact; str rr_set; struct socket_info* callee_bind_address = NULL; if (leg == DLG_CALLER_LEG) dlg->caller_bind_addr = msg->rcv.bind_address; else callee_bind_address = msg->rcv.bind_address; /* extract the cseq number as string from the request or response*/ //TO DO - can pair the cseqs here to make sure that the response and request are in sync 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"); goto error0; } cseq = (get_cseq(msg))->number; /* extract the contact address */ if (!msg->contact && (parse_headers(msg, HDR_CONTACT_F, 0) < 0 || !msg->contact)) { LM_ERR("bad sip message or missing Contact hdr\n"); goto error0; } if (parse_contact(msg->contact) < 0 || ((contact_body_t *) msg->contact->parsed)->contacts == NULL || ((contact_body_t *) msg->contact->parsed)->contacts->next != NULL) { LM_ERR("bad Contact HDR\n"); goto error0; } contact = ((contact_body_t *) msg->contact->parsed)->contacts->uri; /* extract the RR parts */ if (!msg->record_route && (parse_headers(msg, HDR_EOH_F, 0) < 0)) { LM_ERR("failed to parse record route header\n"); goto error0; } if (leg == DLG_CALLER_LEG) { skip_recs = 0; } else { skip_recs = 0; /* was the 200 OK received or local generated */ /*skip_recs = dlg->from_rr_nb + ((t->relayed_reply_branch >= 0) ? ((t->uac[t->relayed_reply_branch].flags & TM_UAC_FLAG_R2) ? 2 : ((t->uac[t->relayed_reply_branch].flags & TM_UAC_FLAG_RR) ? 1 : 0)) : 0); * */ } if (msg->record_route) { if (print_rr_body(msg->record_route, &rr_set, leg, &skip_recs) != 0) { LM_ERR("failed to print route records \n"); goto error0; } } else { rr_set.s = 0; rr_set.len = 0; } if (leg == DLG_CALLER_LEG) dlg->from_rr_nb = skip_recs; LM_DBG("route_set %.*s, contact %.*s, cseq %.*s and bind_addr %.*s\n", rr_set.len, rr_set.s, contact.len, contact.s, cseq.len, cseq.s, msg->rcv.bind_address->sock_str.len, msg->rcv.bind_address->sock_str.s); if (dlg_set_leg_info(dlg, tag, &rr_set, &contact, &cseq, callee_bind_address, leg) != 0) { LM_ERR("dlg_set_leg_info failed\n"); if (rr_set.s) pkg_free(rr_set.s); goto error0; } if (rr_set.s) pkg_free(rr_set.s); return 0; error0: return -1; }
/* * Message contained some contacts, but record with same address * of record was not found so we have to create a new record * and insert all contacts from the message that have expires * > 0 */ static inline int insert(struct sip_msg* _m, contact_t* _c, udomain_t* _d, str* _a, str *ua) { urecord_t* r = 0; ucontact_t* c; int e, cseq; qvalue_t q; str callid; unsigned int flags; str* recv; int_str rcv_avp; int_str val; int num; rcv_avp.n=rcv_avp_no; if (isflagset(_m, nat_flag) == 1) flags = FL_NAT; else flags = FL_NONE; flags |= mem_only; num = 0; while(_c) { if (calc_contact_expires(_m, _c->expires, &e) < 0) { LOG(L_ERR, "insert(): Error while calculating expires\n"); return -1; } /* Skip contacts with zero expires */ if (e == 0) goto skip; if (max_contacts && (num >= max_contacts)) { rerrno = R_TOO_MANY; ul.delete_urecord(_d, _a); return -1; } num++; if (r == 0) { if (ul.insert_urecord(_d, _a, &r) < 0) { rerrno = R_UL_NEW_R; LOG(L_ERR, "insert(): Can't insert new record structure\n"); return -2; } } /* Calculate q value of the contact */ if (calc_contact_q(_c->q, &q) < 0) { LOG(L_ERR, "insert(): Error while calculating q\n"); ul.delete_urecord(_d, _a); return -3; } /* Get callid of the message */ callid = _m->callid->body; trim_trailing(&callid); /* Get CSeq number of the message */ if (str2int(&get_cseq(_m)->number, (unsigned int*)&cseq) < 0) { rerrno = R_INV_CSEQ; LOG(L_ERR, "insert(): Error while converting cseq number\n"); ul.delete_urecord(_d, _a); return -4; } if (_c->received) { recv = &_c->received->body; } else if (search_first_avp(0, rcv_avp, &val)) { recv = val.s; } else { recv = 0; } if (ul.insert_ucontact(r, &_c->uri, e, q, &callid, cseq, flags, &c, ua, recv) < 0) { rerrno = R_UL_INS_C; LOG(L_ERR, "insert(): Error while inserting contact\n"); ul.delete_urecord(_d, _a); return -5; } skip: _c = get_next_contact(_c); } if (r) { if (!r->contacts) { ul.delete_urecord(_d, _a); } else { build_contact(r->contacts); } } return 0; }
void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps) { struct sip_msg* msg= NULL; int lexpire= 0; unsigned int cseq; ua_pres_t* presentity= NULL, *hentity= NULL; struct to_body *pto = NULL, TO = {0}, *pfrom = NULL; int size= 0; unsigned int hash_code; int flag ; str record_route= {0, 0}; int rt; str contact; int initial_request = 0; int end_transaction = 1; if( ps->param== NULL || *ps->param== NULL ) { LM_ERR("null callback parameter\n"); return; } if (dbmode == PUA_DB_ONLY && pua_dbf.start_transaction) { if (pua_dbf.start_transaction(pua_db, db_table_lock) < 0) { LM_ERR("in start_transaction\n"); goto error; } } LM_DBG("completed with status %d\n",ps->code) ; hentity= (ua_pres_t*)(*ps->param); hash_code= core_hash(hentity->pres_uri,hentity->watcher_uri, HASH_SIZE); flag= hentity->flag; if(hentity->flag & XMPP_INITIAL_SUBS) hentity->flag= XMPP_SUBSCRIBE; /* get dialog information from reply message: callid, to_tag, from_tag */ msg= ps->rpl; if(msg == NULL) { LM_ERR("no reply message found\n "); goto error; } if(msg== FAKED_REPLY) { struct hdr_field *callid = NULL, *from = NULL; struct to_body FROM = {0}; callid = (struct hdr_field *) pkg_malloc(sizeof(struct hdr_field)); if (callid == NULL) { LM_ERR("Out of memory\n"); goto faked_error; } memset(callid, 0, sizeof(struct hdr_field)); get_hdr_field(t->callid.s, t->callid.s + t->callid.len, callid); hentity->call_id = callid->body; from = (struct hdr_field *) pkg_malloc(sizeof(struct hdr_field)); if (from == NULL) { LM_ERR("Out of memory\n"); goto faked_error; } memset(from, 0, sizeof(struct hdr_field)); get_hdr_field(t->from.s, t->from.s + t->from.len, from); parse_to(from->body.s, from->body.s + from->body.len + 1, &FROM); if(FROM.uri.len <= 0) { LM_ERR("'From' header NOT parsed\n"); goto faked_error; } hentity->call_id = callid->body; hentity->from_tag = (&FROM)->tag_value; hentity->to_tag.s = NULL; hentity->to_tag.len = 0; find_and_delete_dialog(hentity, hash_code); faked_error: if (callid) pkg_free(callid); free_to_params(&FROM); if (from) pkg_free(from); goto done; } if ( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("when parsing headers\n"); goto error; } if(ps->rpl->expires && msg->expires->body.len > 0) { if (!msg->expires->parsed && (parse_expires(msg->expires) < 0)) { LM_ERR("cannot parse Expires header\n"); goto error; } lexpire = ((exp_body_t*)msg->expires->parsed)->val; LM_DBG("lexpire= %d\n", lexpire); } /*if initial request */ if(hentity->call_id.s== NULL) { initial_request = 1; if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot parse callid header\n"); goto error; } if (!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); goto error; } if (msg->from->parsed == NULL) { if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); goto error; } } pfrom = (struct to_body*)msg->from->parsed; if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0) { LM_ERR("no from tag value present\n"); goto error; } hentity->call_id= msg->callid->body; hentity->from_tag= pfrom->tag_value; if(ps->code >= 300 || lexpire == 0) { hentity->to_tag.s = NULL; hentity->to_tag.len = 0; find_and_delete_dialog(hentity, hash_code); goto done; } if( msg->to==NULL || msg->to->body.s==NULL) { LM_ERR("cannot parse TO header\n"); goto error; } if(msg->to->parsed != NULL) { pto = (struct to_body*)msg->to->parsed; LM_DBG("'To' header ALREADY PARSED: <%.*s>\n",pto->uri.len,pto->uri.s); } else { parse_to(msg->to->body.s,msg->to->body.s + msg->to->body.len + 1, &TO); if(TO.uri.len <= 0) { LM_ERR("'To' header NOT parsed\n"); goto error; } pto = &TO; } if( pto->tag_value.s ==NULL || pto->tag_value.len == 0) { LM_ERR("no to tag value present\n"); goto error; } hentity->to_tag= pto->tag_value; } if(ps->code >= 300 ) { /* if an error code and a stored dialog delete it and try to send a subscription with type= INSERT_TYPE, else return*/ subs_info_t subs; hentity->to_tag.s = NULL; hentity->to_tag.len = 0; find_and_delete_dialog(hentity, hash_code); if (dbmode == PUA_DB_ONLY && pua_dbf.end_transaction) { if (pua_dbf.end_transaction(pua_db) < 0) { LM_ERR("in end_transaction\n"); goto error; } } end_transaction = 0; /* Redirect if the response 3XX */ memset(&subs, 0, sizeof(subs_info_t)); subs.pres_uri= hentity->pres_uri; subs.watcher_uri= hentity->watcher_uri; subs.contact= &hentity->contact; if(hentity->remote_contact.s) subs.remote_target= &hentity->remote_contact; if(hentity->desired_expires== 0) subs.expires= -1; else if(hentity->desired_expires< (int)time(NULL)) subs.expires= 0; else subs.expires= hentity->desired_expires- (int)time(NULL)+ 3; subs.flag= INSERT_TYPE; subs.source_flag= flag; subs.event= hentity->event; subs.id= hentity->id; subs.outbound_proxy= hentity->outbound_proxy; subs.extra_headers= hentity->extra_headers; subs.cb_param= hentity->cb_param; if(send_subscribe(&subs)< 0) { LM_ERR("when trying to send SUBSCRIBE\n"); goto error; } goto done; } if(lexpire== 0 ) { LM_DBG("lexpire= 0 Delete from hash table"); find_and_delete_dialog(hentity, hash_code); goto done; } /* extract the contact */ if(msg->contact== NULL || msg->contact->body.s== NULL) { LM_ERR("no contact header found"); goto error; } if( parse_contact(msg->contact) <0 ) { LM_ERR(" cannot parse contact header\n"); goto error; } if(msg->contact->parsed == NULL) { LM_ERR("cannot parse contact header\n"); goto error; } contact = ((contact_body_t* )msg->contact->parsed)->contacts->uri; if( msg->cseq==NULL || msg->cseq->body.s==NULL) { LM_ERR("cannot parse cseq header\n"); goto error; } if( str2int( &(get_cseq(msg)->number), &cseq)< 0) { LM_ERR("while converting str to int\n"); goto error; } if(initial_request == 0) { hentity->cseq = cseq; find_and_update_dialog(hentity, hash_code, lexpire, &contact); goto done; } /*process record route and add it to a string*/ if (msg->record_route!=NULL) { rt = print_rr_body(msg->record_route, &record_route, 1, 0); if(rt != 0) { LM_ERR("parsing record route [%d]\n", rt); record_route.s=NULL; record_route.len=0; } } size= sizeof(ua_pres_t)+ 2*sizeof(str)+( pto->uri.len+ pfrom->uri.len+ pto->tag_value.len+ pfrom->tag_value.len +msg->callid->body.len+ record_route.len+ hentity->contact.len+ hentity->id.len )*sizeof(char); if(hentity->extra_headers) size+= sizeof(str)+ hentity->extra_headers->len*sizeof(char); presentity= (ua_pres_t*)shm_malloc(size); if(presentity== NULL) { LM_ERR("no more share memory\n"); goto error; } memset(presentity, 0, size); size= sizeof(ua_pres_t); presentity->pres_uri= (str*)( (char*)presentity+ size); size+= sizeof(str); presentity->pres_uri->s= (char*)presentity+ size; memcpy(presentity->pres_uri->s, pto->uri.s, pto->uri.len); presentity->pres_uri->len= pto->uri.len; size+= pto->uri.len; presentity->watcher_uri= (str*)( (char*)presentity+ size); size+= sizeof(str); presentity->watcher_uri->s= (char*)presentity+ size; memcpy(presentity->watcher_uri->s, pfrom->uri.s, pfrom->uri.len); presentity->watcher_uri->len= pfrom->uri.len; size+= pfrom->uri.len; presentity->call_id.s= (char*)presentity + size; memcpy(presentity->call_id.s,msg->callid->body.s, msg->callid->body.len); presentity->call_id.len= msg->callid->body.len; size+= presentity->call_id.len; presentity->to_tag.s= (char*)presentity + size; memcpy(presentity->to_tag.s,pto->tag_value.s, pto->tag_value.len); presentity->to_tag.len= pto->tag_value.len; size+= pto->tag_value.len; presentity->from_tag.s= (char*)presentity + size; memcpy(presentity->from_tag.s,pfrom->tag_value.s, pfrom->tag_value.len); presentity->from_tag.len= pfrom->tag_value.len; size+= pfrom->tag_value.len; if(record_route.len && record_route.s) { presentity->record_route.s= (char*)presentity + size; memcpy(presentity->record_route.s, record_route.s, record_route.len); presentity->record_route.len= record_route.len; size+= record_route.len; pkg_free(record_route.s); record_route.s = NULL; } presentity->contact.s= (char*)presentity + size; memcpy(presentity->contact.s, hentity->contact.s, hentity->contact.len); presentity->contact.len= hentity->contact.len; size+= hentity->contact.len; if(hentity->id.s) { presentity->id.s=(char*)presentity+ size; memcpy(presentity->id.s, hentity->id.s, hentity->id.len); presentity->id.len= hentity->id.len; size+= presentity->id.len; } if(hentity->extra_headers) { presentity->extra_headers= (str*)((char*)presentity+ size); size+= sizeof(str); presentity->extra_headers->s=(char*)presentity+ size; memcpy(presentity->extra_headers->s, hentity->extra_headers->s, hentity->extra_headers->len); presentity->extra_headers->len= hentity->extra_headers->len; size+= hentity->extra_headers->len; } /* write the remote contact filed */ presentity->remote_contact.s= (char*)shm_malloc(contact.len* sizeof(char)); if(presentity->remote_contact.s==NULL) { ERR_MEM(SHARE_MEM); } memcpy(presentity->remote_contact.s, contact.s, contact.len); presentity->remote_contact.len= contact.len; presentity->event|= hentity->event; presentity->flag= hentity->flag; presentity->etag.s= NULL; presentity->cseq= cseq; presentity->desired_expires= hentity->desired_expires; presentity->expires= lexpire+ (int)time(NULL); if(BLA_SUBSCRIBE & presentity->flag) { LM_DBG("BLA_SUBSCRIBE FLAG inserted\n"); } LM_DBG("record for subscribe from %.*s to %.*s inserted in datatbase\n", presentity->watcher_uri->len, presentity->watcher_uri->s, presentity->pres_uri->len, presentity->pres_uri->s); if (dbmode==PUA_DB_ONLY) { if (pua_dbf.end_transaction) { if (pua_dbf.end_transaction(pua_db) < 0) { LM_ERR("in end_transaction\n"); goto error; } } if (pua_dbf.start_transaction) { if (pua_dbf.start_transaction(pua_db, db_table_lock) < 0) { LM_ERR("in start_transaction\n"); goto error; } } if (convert_temporary_dialog_puadb(presentity) < 0) { LM_ERR("Could not convert temporary dialog into a dialog\n"); goto error; } } else { if (convert_temporary_dialog(presentity) < 0) { LM_ERR("Could not convert temporary dialog into a dialog\n"); goto error; } } done: if(hentity->ua_flag == REQ_OTHER) { hentity->flag= flag; run_pua_callbacks( hentity, msg); } if (dbmode == PUA_DB_ONLY && pua_dbf.end_transaction && end_transaction) { if (pua_dbf.end_transaction(pua_db) < 0) { LM_ERR("in end_transaction\n"); goto error; } } goto end; error: if (presentity) { if (presentity->remote_contact.s) shm_free(presentity->remote_contact.s); shm_free(presentity); } if(record_route.s) pkg_free(record_route.s); if (dbmode == PUA_DB_ONLY && pua_dbf.abort_transaction) { if (pua_dbf.abort_transaction(pua_db) < 0) LM_ERR("in abort_transaction\n"); } end: if(hentity) { shm_free(hentity); hentity= NULL; } free_to_params(&TO); return; }
/* Returns 0 - nothing found * 1 - T found */ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ) { struct cell* p_cell; unsigned int hash_index = 0; unsigned int entry_label = 0; unsigned int branch_id = 0; char *hashi, *branchi, *p, *n; int hashl, branchl; int scan_space; str cseq_method; str req_method; char *loopi; int loopl; char *syni; int synl; short is_cancel; /* make compiler warnings happy */ loopi=0; loopl=0; syni=0; synl=0; /* split the branch into pieces: loop_detection_check(ignored), hash_table_id, synonym_id, branch_id */ if (!(p_msg->via1 && p_msg->via1->branch && p_msg->via1->branch->value.s)) goto nomatch2; /* we do RFC 3261 tid matching and want to see first if there is * magic cookie in branch */ if (p_msg->via1->branch->value.len<=MCOOKIE_LEN) goto nomatch2; if (memcmp(p_msg->via1->branch->value.s, MCOOKIE, MCOOKIE_LEN)!=0) goto nomatch2; p=p_msg->via1->branch->value.s+MCOOKIE_LEN; scan_space=p_msg->via1->branch->value.len-MCOOKIE_LEN; /* hash_id */ n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR); hashl=n-p; scan_space-=hashl; if (!hashl || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2; hashi=p; p=n+1;scan_space--; if (!syn_branch) { /* md5 value */ n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR ); loopl = n-p; scan_space-= loopl; if (n==p || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2; loopi=p; p=n+1; scan_space--; } else { /* synonym id */ n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR); synl=n-p; scan_space-=synl; if (!synl || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2; syni=p; p=n+1;scan_space--; } /* branch id - should exceed the scan_space */ n=eat_token_end( p, p+scan_space ); branchl=n-p; if (!branchl ) goto nomatch2; branchi=p; /* sanity check */ if (reverse_hex2int(hashi, hashl, &hash_index)<0 ||hash_index>=TABLE_ENTRIES || reverse_hex2int(branchi, branchl, &branch_id)<0 ||branch_id>=MAX_BRANCHES || (syn_branch ? (reverse_hex2int(syni, synl, &entry_label))<0 : loopl!=MD5_LEN ) ) { DBG("DEBUG: t_reply_matching: poor reply labels %d label %d " "branch %d\n", hash_index, entry_label, branch_id ); goto nomatch2; } DBG("DEBUG: t_reply_matching: hash %d label %d branch %d\n", hash_index, entry_label, branch_id ); /* search the hash table list at entry 'hash_index'; lock the entry first */ cseq_method=get_cseq(p_msg)->method; is_cancel=cseq_method.len==CANCEL_LEN && memcmp(cseq_method.s, CANCEL, CANCEL_LEN)==0; LOCK_HASH(hash_index); for (p_cell = get_tm_table()->entrys[hash_index].first_cell; p_cell; p_cell=p_cell->next_cell) { /* first look if branch matches */ if (syn_branch) { if (p_cell->label != entry_label) continue; } else { if ( memcmp(p_cell->md5, loopi,MD5_LEN)!=0) continue; } /* sanity check ... too high branch ? */ if ( branch_id>=p_cell->nr_of_outgoings ) continue; /* does method match ? (remember -- CANCELs have the same branch as canceled transactions) */ req_method=p_cell->method; if ( /* method match */ ! ((cseq_method.len==req_method.len && memcmp( cseq_method.s, req_method.s, cseq_method.len )==0) /* or it is a local cancel */ || (is_cancel && is_invite(p_cell) /* commented out -- should_cancel_branch set it to BUSY_BUFFER to avoid collisions with replies; thus, we test here by buffer size */ /* && p_cell->uac[branch_id].local_cancel.buffer ))) */ && p_cell->uac[branch_id].local_cancel.buffer_len ))) continue; /* we passed all disqualifying factors .... the transaction has been matched ! */ set_t(p_cell); *p_branch =(int) branch_id; REF_UNSAFE( T ); UNLOCK_HASH(hash_index); DBG("DEBUG: t_reply_matching: reply matched (T=%p)!\n",T); /* if this is a 200 for INVITE, we will wish to store to-tags to be * able to distinguish retransmissions later and not to call * TMCB_RESPONSE_OUT uselessly; we do it only if callbacks are * enabled -- except callback customers, nobody cares about * retransmissions of multiple 200/INV or ACK/200s */ if (is_invite(p_cell) && p_msg->REPLY_STATUS>=200 && p_msg->REPLY_STATUS<300 && ( (!is_local(p_cell) && has_tran_tmcbs(p_cell,TMCB_RESPONSE_OUT|TMCB_E2EACK_IN) ) || (is_local(p_cell)&&has_tran_tmcbs(p_cell,TMCB_LOCAL_COMPLETED)) )) { if (parse_headers(p_msg, HDR_TO_F, 0)==-1) { LOG(L_ERR, "ERROR: t_reply_matching: to parsing failed\n"); } } if (!is_local(p_cell)) { run_trans_callbacks( TMCB_RESPONSE_IN, T, T->uas.request, p_msg, p_msg->REPLY_STATUS); } return 1; } /* for cycle */ /* nothing found */ UNLOCK_HASH(hash_index); DBG("DEBUG: t_reply_matching: no matching transaction exists\n"); nomatch2: DBG("DEBUG: t_reply_matching: failure to match a transaction\n"); *p_branch = -1; set_t(0); return -1; }
/* This function is called whenever a reply for our module is received; * we need to register this function on module initialization; * Returns : 0 - core router stops * 1 - core router relay statelessly */ int reply_received( struct sip_msg *p_msg ) { int msg_status; int last_uac_status; int branch; int reply_status; utime_t timer; /* has the transaction completed now and we need to clean-up? */ branch_bm_t cancel_bitmap; struct ua_client *uac; struct cell *t; struct usr_avp **backup_list; unsigned int has_reply_route; set_t(T_UNDEFINED); /* make sure we know the associated transaction ... */ if (t_check(p_msg, &branch ) == -1) goto not_found; /*... if there is none, tell the core router to fwd statelessly */ t = get_t(); if ((t == 0) || (t == T_UNDEFINED)) goto not_found; cancel_bitmap=0; msg_status=p_msg->REPLY_STATUS; uac=&t->uac[branch]; LM_DBG("org. status uas=%d, uac[%d]=%d local=%d is_invite=%d)\n", t->uas.status, branch, uac->last_received, is_local(t), is_invite(t)); last_uac_status=uac->last_received; if_update_stat( tm_enable_stats, tm_rcv_rpls , 1); /* it's a cancel which is not e2e ? */ if ( get_cseq(p_msg)->method_id==METHOD_CANCEL && is_invite(t) ) { /* ... then just stop timers */ reset_timer( &uac->local_cancel.retr_timer); if ( msg_status >= 200 ) { reset_timer( &uac->local_cancel.fr_timer); } LM_DBG("reply to local CANCEL processed\n"); goto done; } /* *** stop timers *** */ /* stop retransmission */ reset_timer(&uac->request.retr_timer); /* stop final response timer only if I got a final response */ if ( msg_status >= 200 ) { reset_timer( &uac->request.fr_timer); } /* acknowledge negative INVITE replies (do it before detailed * on_reply processing, which may take very long, like if it * is attempted to establish a TCP connection to a fail-over dst */ if (is_invite(t) && ((msg_status >= 300) || (is_local(t) && !no_autoack(t) && msg_status >= 200) )) { if (send_ack(p_msg, t, branch)!=0) LM_ERR("failed to send ACK (local=%s)\n", is_local(t)?"yes":"no"); } _tm_branch_index = branch; /* processing of on_reply block */ has_reply_route = (t->on_reply) || (t->uac[branch].on_reply); if (has_reply_route) { if (onreply_avp_mode) { /* lock the reply*/ LOCK_REPLIES( t ); /* set the as avp_list the one from transaction */ backup_list = set_avp_list(&t->user_avps); } else { backup_list = 0; } /* transfer transaction flag to branch context */ p_msg->flags = t->uas.request->flags; setb0flags(t->uac[branch].br_flags); /* run block - first per branch and then global one */ if ( t->uac[branch].on_reply && (run_top_route(onreply_rlist[t->uac[branch].on_reply].a,p_msg) &ACT_FL_DROP) && (msg_status<200) ) { if (onreply_avp_mode) { UNLOCK_REPLIES( t ); set_avp_list( backup_list ); } LM_DBG("dropping provisional reply %d\n", msg_status); goto done; } if ( t->on_reply && (run_top_route(onreply_rlist[t->on_reply].a,p_msg) &ACT_FL_DROP) && (msg_status<200) ) { if (onreply_avp_mode) { UNLOCK_REPLIES( t ); set_avp_list( backup_list ); } LM_DBG("dropping provisional reply %d\n", msg_status); goto done; } /* transfer current message context back to t */ t->uac[branch].br_flags = getb0flags(); t->uas.request->flags = p_msg->flags; if (onreply_avp_mode) /* restore original avp list */ set_avp_list( backup_list ); } if (!onreply_avp_mode || !has_reply_route) /* lock the reply*/ LOCK_REPLIES( t ); /* mark that the UAC received replies */ uac->flags |= T_UAC_HAS_RECV_REPLY; /* we fire a cancel on spot if (a) branch is marked "to be canceled" or (b) * the whole transaction was canceled (received cancel) and no cancel sent * yet on this branch; and of course, only if a provisional reply :) */ if (t->uac[branch].flags&T_UAC_TO_CANCEL_FLAG || ((t->flags&T_WAS_CANCELLED_FLAG) && !t->uac[branch].local_cancel.buffer.s)) { if ( msg_status < 200 ) /* reply for an UAC with a pending cancel -> do cancel now */ cancel_branch(t, branch); /* reset flag */ t->uac[branch].flags &= ~(T_UAC_TO_CANCEL_FLAG); } if (is_local(t)) { reply_status = local_reply(t,p_msg, branch,msg_status,&cancel_bitmap); if (reply_status == RPS_COMPLETED) { cleanup_uac_timers(t); if (is_invite(t)) cancel_uacs(t, cancel_bitmap); /* There is no need to call set_final_timer because we know * that the transaction is local */ put_on_wait(t); } } else { reply_status = relay_reply(t,p_msg,branch,msg_status,&cancel_bitmap); /* clean-up the transaction when transaction completed */ if (reply_status == RPS_COMPLETED) { /* no more UAC FR/RETR (if I received a 2xx, there may * be still pending branches ... */ cleanup_uac_timers(t); if (is_invite(t)) cancel_uacs(t, cancel_bitmap); /* FR for negative INVITES, WAIT anything else */ /* set_final_timer(t); */ } } if (reply_status!=RPS_PROVISIONAL) goto done; /* update FR/RETR timers on provisional replies */ if (msg_status < 200 && (restart_fr_on_each_reply || ((last_uac_status<msg_status) && ((msg_status >= 180) || (last_uac_status == 0))) ) ) { /* provisional now */ if (is_invite(t)) { /* invite: change FR to longer FR_INV, do not * attempt to restart retransmission any more */ backup_list = set_avp_list(&t->user_avps); if (!fr_inv_avp2timer(&timer)) { LM_DBG("FR_INV_TIMER = %lld\n", timer); set_timer(&uac->request.fr_timer, FR_INV_TIMER_LIST, &timer); } else { set_timer(& uac->request.fr_timer, FR_INV_TIMER_LIST, 0); } set_avp_list(backup_list); } else { /* non-invite: restart retransmissions (slow now) */ uac->request.retr_list = RT_T2; set_timer(&uac->request.retr_timer, RT_T2, 0); } } /* provisional replies */ done: /* we are done with the transaction, so unref it - the reference * was incremented by t_check() function -bogdan*/ t_unref(p_msg); /* don't try to relay statelessly neither on success * (we forwarded statefully) nor on error; on troubles, * simply do nothing; that will make the other party to * retransmit; hopefuly, we'll then be better off */ _tm_branch_index = 0; return 0; not_found: set_t(T_UNDEFINED); return 1; }
sip_trans* trans_bucket::match_reply(sip_msg* msg) { if(elmts.empty()) return NULL; assert(msg->via_p1); if(msg->via_p1->branch.len <= MAGIC_BRANCH_LEN){ // this cannot match... return NULL; } sip_trans* t = NULL; const char* branch = msg->via_p1->branch.s + MAGIC_BRANCH_LEN; int len = msg->via_p1->branch.len - MAGIC_BRANCH_LEN; assert(get_cseq(msg)); trans_list::iterator it = elmts.begin(); for(;it!=elmts.end();++it) { if((*it)->type != TT_UAC){ continue; } if((*it)->msg->via_p1->branch.len != msg->via_p1->branch.len) continue; if(get_cseq((*it)->msg)->num_str.len != get_cseq(msg)->num_str.len) continue; if(get_cseq((*it)->msg)->method_str.len != get_cseq(msg)->method_str.len) continue; if(memcmp((*it)->msg->via_p1->branch.s+MAGIC_BRANCH_LEN, branch,len)) continue; if(memcmp(get_cseq((*it)->msg)->num_str.s,get_cseq(msg)->num_str.s, get_cseq(msg)->num_str.len)) continue; if(memcmp(get_cseq((*it)->msg)->method_str.s,get_cseq(msg)->method_str.s, get_cseq(msg)->method_str.len)) continue; // found matching transaction t = *it; break; } return t; }