/** * Drop all dialogs belonging to one AOR. * on deregistration for example. * @param aor - the public identity of the user * @returns the number of dialogs dropped */ int S_drop_all_dialogs(str aor) { s_dialog *d,*dn; int i,cnt=0;; LOG(L_DBG,"DBG:"M_NAME":S_drop_all_dialogs: Called for <%.*s>\n",aor.len,aor.s); for(i=0;i<s_dialogs_hash_size;i++){ d_lock(i); d = s_dialogs[i].head; while(d){ dn = d->next; if (d->direction == DLG_MOBILE_ORIGINATING && d->aor.len == aor.len && strncasecmp(d->aor.s,aor.s,aor.len)==0) { if (!terminate_s_dialog(d)) del_s_dialog(d); cnt++; } d = dn; } d_unlock(i); } //print_s_dialogs(L_INFO); return cnt; }
/** * Prints the dialog hash table. * @param log_level - the log_level to print with */ void print_p_dialogs(int log_level) { p_dialog *d; int i,j; if (debug<log_level) return; /* to avoid useless calls when nothing will be printed */ d_act_time(); LOG(log_level,"INF:"M_NAME":---------- P-CSCF Dialog List begin --------------\n"); for(i=0;i<p_dialogs_hash_size;i++){ d_lock(i); d = p_dialogs[i].head; while(d){ LOG(log_level,"INF:"M_NAME":[%4d] Dir:["ANSI_MAGENTA"%d"ANSI_GREEN "]\tCall-ID:<"ANSI_BLUE"%.*s"ANSI_GREEN ">\tAOR:"ANSI_RED"%d://%.*s:%d"ANSI_GREEN"\n" ,i, d->direction, d->call_id.len,d->call_id.s, d->transport,d->host.len,d->host.s,d->port); LOG(log_level,"INF:"M_NAME":\t\tMethod:["ANSI_MAGENTA"%d"ANSI_GREEN "] State:["ANSI_MAGENTA"%d"ANSI_GREEN "] Exp:["ANSI_MAGENTA"%4d"ANSI_GREEN"]\n", d->method,d->state, (int)(d->expires - d_time_now)); for(j=0;j<d->routes_cnt;j++) LOG(log_level,"INF:"M_NAME":\t\t RR: <"ANSI_YELLOW"%.*s"ANSI_GREEN">\n", d->routes[j].len,d->routes[j].s); d = d->next; } d_unlock(i); } LOG(log_level,"INF:"M_NAME":---------- P-CSCF Dialog List end --------------\n"); }
/** * Given a call-id, locate if its terminating,orginating or both * release the dialog involved and drop the dialog * @param callid - the Call-ID to release * @param reason - the Reason header to include in messages * @returns 0 on error, 1 on success */ int release_call(str callid,str reason) { s_dialog *d=0; unsigned int hash; int res = 0; d = get_s_dialog_dir(callid,DLG_MOBILE_ORIGINATING); if (d) { hash = d->hash; if (release_call_s(d,reason)>0) res = 1; goto done; } d = get_s_dialog_dir(callid,DLG_MOBILE_TERMINATING); if (d) { hash = d->hash; if (release_call_s(d,reason)>0) res = 1; goto done; } /*Neither ORGINATING nor TERMINATING is UNKNOWN!*/ /*or doesn't exist*/ /*drop it silently?*/ /*treat it as ORIGINATING or TERMINATING?*/ done: if (d) d_unlock(hash); return res; }
/** * Drop all dialogs belonging to one contact. * on deregistration for example. * @param host - host that originates/terminates this dialog * @param port - port that originates/terminates this dialog * @param transport - transport that originates/terminates this dialog * @returns the number of dialogs dropped */ int P_drop_all_dialogs(str host,int port, int transport) { p_dialog *d,*dn; int i,cnt=0;; LOG(L_DBG,"DBG:"M_NAME":P_drop_all_dialogs: Called for <%d://%.*s:%d>\n",transport,host.len,host.s,port); for(i=0;i<p_dialogs_hash_size;i++){ d_lock(i); d = p_dialogs[i].head; while(d){ dn = d->next; if (d->transport == transport && d->port == port && d->host.len == host.len && strncasecmp(d->host.s,host.s,host.len)==0) { del_p_dialog(d); cnt++; } d = dn; } d_unlock(i); } // print_p_dialogs(L_INFO); return cnt; }
/** * Finds out if a dialog is in the hash table. * @param call_id - dialog's call_id * @param dir - the direction of the dialog * @returns - 1 if the dialog exists, 0 if not * \note transport is ignored. */ int is_p_dialog_dir(str call_id,enum p_dialog_direction dir) { p_dialog *d=0; unsigned int hash = get_p_dialog_hash(call_id); d_lock(hash); d = p_dialogs[hash].head; while(d){ if (d->direction==dir && d->call_id.len == call_id.len && strncasecmp(d->call_id.s,call_id.s,call_id.len)==0) { d_unlock(hash); return 1; } d=d->next; } d_unlock(hash); return 0; }
/** * Finds out if a dialog is in the hash table. * @param call_id - dialog's call_id * @param dir - the direction of the dialog. if NULL, it doesn't matter * @returns - 1 if the dialog exists, 0 if not * \note transport is ignored. */ int is_lrf_dialog(str call_id,enum lrf_dialog_direction *dir) { lrf_dialog *d=0; unsigned int hash = get_lrf_dialog_hash(call_id); d_lock(hash); d = lrf_dialogs[hash].head; while(d){ if ((!dir || d->direction == *dir) && d->call_id.len == call_id.len && strncasecmp(d->call_id.s,call_id.s,call_id.len)==0) { d_unlock(hash); return 1; } d = d->next; } d_unlock(hash); return 0; }
/** * Releases a call from the on reply route block * called with any reply to an INVITE * useful in cases of rejecting a call when you are processing the SDP * or handling QoS things * @msg - the sip message being processed * @str1 - the first parameter "orig" or "term" * @str2 - [optional] the Reason header that you want to go to the messages * @returns - TRUE on success or FALSE on misscall and BREAK on error */ int P_release_call_onreply(struct sip_msg *msg,char *str1,char *str2) { enum p_dialog_direction dir; p_dialog *d=NULL; str callid; struct hdr_field *h1; str reason={NULL,0}; if (str2) { reason.s=str2; reason.len=strlen(str2); } else reason = _488_text_s; dir= (str1[0]=='o' || str1[0]=='O' || str1[0]=='0')? DLG_MOBILE_ORIGINATING : DLG_MOBILE_TERMINATING; if (msg->first_line.type== SIP_REQUEST) { LOG(L_ERR,"ERR: P_release_call_on_reply called with a request\n"); return CSCF_RETURN_FALSE; } callid=cscf_get_call_id(msg,&h1); if (is_p_dialog_dir(callid,dir)) { d=get_p_dialog_dir(callid,dir); if (msg->first_line.u.reply.statuscode > 199) { release_call_previous(d,RELEASE_CALL_WEIRD,488,reason); d_unlock(d->hash); return CSCF_RETURN_TRUE; } else { release_call_previous(d,RELEASE_CALL_EARLY,488,reason); d_unlock(d->hash); return CSCF_RETURN_TRUE; } } else { LOG(L_ERR,"ERR:"M_NAME "P_release_call_onreply : unable to find dialog\n"); return CSCF_RETURN_BREAK; } }
/** * Finds out if a dialog is in the hash table. * @param call_id - call_id of the dialog * @param aor - aor of the user * @returns 1 if found, 0 if not found */ int is_s_dialog(str call_id,str aor) { s_dialog *d=0; unsigned int hash = get_s_dialog_hash(call_id); d_lock(hash); d = s_dialogs[hash].head; while(d){ if (d->aor.len == aor.len && d->call_id.len == call_id.len && strncasecmp(d->aor.s,aor.s,aor.len)==0 && strncasecmp(d->call_id.s,call_id.s,call_id.len)==0) { d_unlock(hash); return 1; } d = d->next; } d_unlock(hash); return 0; }
/** * Callback function for BYE requests! * Identify the s_dialog, then see if one BYE has already been recieved * if yes drop it , if no, wait for the second */ void bye_response(struct cell *t,int type,struct tmcb_params *ps) { s_dialog *d; unsigned int hash; str call_id; enum s_dialog_direction dir; if (!ps->param) return; dir = *((enum s_dialog_direction *) *(ps->param)); shm_free(*ps->param); *ps->param = 0; call_id = t->callid; call_id.s+=9; call_id.len-=11; LOG(L_INFO,"DBG:"M_NAME":bye_response(): Received a %d response to BYE for a call release for <%.*s> DIR[%d].\n", ps->code, call_id.len,call_id.s,dir); d = get_s_dialog_dir(call_id,dir); if (!d) { LOG(L_ERR,"ERR:"M_NAME":bye_response(): Received a BYE response for a call release but there is no dialog for <%.*s> DIR[%d].\n", call_id.len,call_id.s,dir); return; } if (ps->code>=200){ if (d->state==DLG_STATE_TERMINATED_ONE_SIDE){ hash=d->hash; LOG(L_INFO,"INFO:"M_NAME":bye_response(): Received a response to second BYE. Dialog is dropped.\n"); del_s_dialog(d); d_unlock(hash); } else { hash=d->hash; d->state=DLG_STATE_TERMINATED_ONE_SIDE; d_unlock(hash); } } }
/** * Callback function for BYE requests! * Identify the s_dialog, then see if one BYE has already been recieved * if yes drop it , if no, wait for the second */ void confirmed_response(struct cell *t,int type,struct tmcb_params *ps) { p_dialog *d; unsigned int hash; str call_id; enum p_dialog_direction dir; if (!ps->param) return; dir = *((enum p_dialog_direction *) *(ps->param)); shm_free(*ps->param); *ps->param = 0; //call_id = cscf_get_call_id(ps->rpl,0); call_id = t->callid; call_id.s+=9; call_id.len-=11; LOG(L_INFO,"DBG:"M_NAME":confirmed_response(): Received a BYE for a call release for <%.*s> DIR[%d].\n", call_id.len,call_id.s,dir); d = get_p_dialog_dir(call_id,dir); if (!d) { LOG(L_ERR,"ERR:"M_NAME":confirmed_response(): Received a BYE for a call release but there is no dialog for <%.*s> DIR[%d].\n", call_id.len,call_id.s,dir); return; } if (ps->code>=200){ if (d->state==DLG_STATE_TERMINATED_ONE_SIDE){ hash=d->hash; del_p_dialog(d); d_unlock(hash); } else { hash=d->hash; d->state=DLG_STATE_TERMINATED_ONE_SIDE; d_unlock(hash); } } }
/** * Saves a dialog. * @param msg - the initial request * @param str1 - direction - "orig" or "term" * @param str2 - not used * @returns #CSCF_RETURN_TRUE if ok, #CSCF_RETURN_FALSE if not or #CSCF_RETURN_BREAK on error */ int S_save_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; s_dialog *d; str aor; str uri,tag; str ruri; enum s_dialog_direction dir = get_dialog_direction(str1); if (!find_dialog_aor(msg,dir,&aor)){ LOG(L_ERR,"ERR:"M_NAME":S_save_dialog(): Error retrieving %s contact\n",str1); return CSCF_RETURN_BREAK; } call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; LOG(L_INFO,"DBG:"M_NAME":S_save_dialog(%s): Call-ID <%.*s>\n",str1,call_id.len,call_id.s); if (is_s_dialog(call_id,aor)){ LOG(L_ERR,"ERR:"M_NAME":S_save_dialog: dialog already exists!\n"); return CSCF_RETURN_FALSE; } d = add_s_dialog(call_id,aor,dir); if (!d) return CSCF_RETURN_FALSE; d->method = get_dialog_method(msg->first_line.u.request.method); STR_SHM_DUP(d->method_str,msg->first_line.u.request.method,"shm"); d->first_cseq = cscf_get_cseq(msg,0); d->last_cseq = d->first_cseq; d->state = DLG_STATE_INITIAL; d->expires = d_act_time()+60; cscf_get_from_tag(msg,&tag); cscf_get_from_uri(msg,&uri); ruri=cscf_get_identity_from_ruri(msg); tmb.new_dlg_uac(&call_id, &tag, d->first_cseq,&uri, &ruri, &d->dialog_c); tmb.new_dlg_uas(msg,99,&d->dialog_s); d_unlock(d->hash); // print_s_dialogs(L_INFO); return CSCF_RETURN_TRUE; }
/** * Finds out if a dialog is in the hash table. * @param call_id - dialog's call_id * @param host - host that originates/terminates this dialog * @param port - port that originates/terminates this dialog * @param transport - transport that originates/terminates this dialog * @param dir - the direction of the dialog. if NULL, it doesn't matter * @returns - 1 if the dialog exists, 0 if not * \note transport is ignored. */ int is_p_dialog(str call_id,str host,int port, int transport,enum p_dialog_direction *dir) { p_dialog *d=0; unsigned int hash = get_p_dialog_hash(call_id); d_lock(hash); d = p_dialogs[hash].head; while(d){ if ((!dir || d->direction == *dir) && d->port == port && /* d->transport == transport &&*/ /* commented because of strange behaviour */ d->host.len == host.len && d->call_id.len == call_id.len && strncasecmp(d->host.s,host.s,host.len)==0 && strncasecmp(d->call_id.s,call_id.s,call_id.len)==0) { d_unlock(hash); return 1; } d = d->next; } d_unlock(hash); return 0; }
/** * Drops and deletes a dialog. * @param msg - the request/response * @param str1 - direction - "orig" or "term" * @param str2 - not used * @returns #CSCF_RETURN_TRUE if ok, #CSCF_RETURN_FALSE if not or #CSCF_RETURN_BREAK on error */ int S_drop_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; s_dialog *d; int hash; // struct sip_msg *req; enum s_dialog_direction dir = get_dialog_direction(str1); // if (!find_dialog_aor(msg,str1,&aor)){ // LOG(L_ERR,"ERR:"M_NAME":S_is_in_dialog(): Error retrieving %s contact\n",str1); // return CSCF_RETURN_BREAK; // } call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; LOG(L_DBG,"DBG:"M_NAME":S_drop_dialog(%s): Call-ID <%.*s> DIR[%d]\n", str1,call_id.len,call_id.s, dir); d = get_s_dialog_dir(call_id,dir); // if (!d && msg->first_line.type==SIP_REPLY){ // /* Try to get the dialog from the request */ // req = cscf_get_request_from_reply(msg); // if (!find_dialog_aor(req,str1,&aor)){ // LOG(L_ERR,"ERR:"M_NAME":S_update_dialog(%s): Error retrieving %s contact\n",str1,str1); // return CSCF_RETURN_BREAK; // } // d = get_s_dialog(call_id,aor); // } if (!d){ LOG(L_ERR,"ERR:"M_NAME":S_drop_dialog: dialog does not exists!\n"); return CSCF_RETURN_FALSE; } hash = d->hash; del_s_dialog(d); d_unlock(hash); print_s_dialogs(L_INFO); return CSCF_RETURN_TRUE; }
/** * Destroy the registrar */ void s_dialogs_destroy() { int i; s_dialog *d,*nd; for(i=0;i<s_dialogs_hash_size;i++){ d_lock(i); d = s_dialogs[i].head; while(d){ nd = d->next; free_s_dialog(d); d = nd; } d_unlock(i); lock_dealloc(s_dialogs[i].lock); } shm_free(s_dialogs); }
/** * Drops and deletes a dialog. * @param msg - the request/response * @param str1 - direction - "orig" or "term" * @param str2 - not used * @returns #CSCF_RETURN_TRUE if ok, #CSCF_RETURN_FALSE if not or #CSCF_RETURN_BREAK on error */ int P_drop_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; p_dialog *d; int hash; str host; int port,transport; struct sip_msg *req; enum p_dialog_direction dir; dir = get_dialog_direction(str1); if (msg->first_line.type==SIP_REPLY) req = cscf_get_request_from_reply(msg); else req = msg; if (!find_dialog_contact(req,dir,&host,&port,&transport)){ LOG(L_ERR,"ERR:"M_NAME":P_is_in_dialog(): Error retrieving %s contact\n",str1); return CSCF_RETURN_BREAK; } call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; LOG(L_DBG,"DBG:"M_NAME":P_drop_dialog(%s): Call-ID <%.*s> %d://%.*s:%d\n", str1,call_id.len,call_id.s, transport,host.len,host.s,port); d = get_p_dialog(call_id,host,port,transport,&dir); if (!d) d = get_p_dialog(call_id,host,port,transport,0); if (!d){ LOG(L_INFO,"INFO:"M_NAME":P_drop_dialog: dialog does not exists!\n"); return CSCF_RETURN_FALSE; } hash = d->hash; del_p_dialog(d); d_unlock(hash); print_p_dialogs(L_INFO); return CSCF_RETURN_TRUE; }
/** * Drops and deletes a dialog. * @param msg - the request/response * @param str1 - direction - "orig" or "term" * @param str2 - not used * @returns #CSCF_RETURN_TRUE if ok, #CSCF_RETURN_FALSE if not or #CSCF_RETURN_BREAK on error */ int LRF_drop_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; lrf_dialog *d; int hash; struct sip_msg *req; enum lrf_dialog_direction dir; str target_uri = {0,0}; dir = get_dialog_direction(str1); if (msg->first_line.type==SIP_REPLY) req = cscf_get_request_from_reply(msg); else req = msg; if (!find_dialog_contact(req,dir,&target_uri)){ LOG(L_ERR,"ERR:"M_NAME":LRF_is_in_dialog(): Error retrieving %s contact\n",str1); return CSCF_RETURN_BREAK; } call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; LOG(L_DBG,"DBG:"M_NAME":LRF_drop_dialog(%s): Call-ID <%.*s>: target <%.*s>\n", str1,call_id.len,call_id.s, target_uri.len, target_uri.s); d = get_lrf_dialog(call_id, &dir); if (!d) d = get_lrf_dialog(call_id, NULL); if (!d){ LOG(L_INFO,"INFO:"M_NAME":LRF_drop_dialog: dialog does not exists!\n"); return CSCF_RETURN_FALSE; } hash = d->hash; del_lrf_dialog(d); d_unlock(hash); print_lrf_dialogs(L_INFO); return CSCF_RETURN_TRUE; }
/** * Prints the list of dialogs. * @param log_level - level to log at */ void print_s_dialogs(int log_level) { s_dialog *d; int i; d_act_time(); LOG(log_level,"INF:"M_NAME":---------- S-CSCF Dialog List begin --------------\n"); for(i=0;i<s_dialogs_hash_size;i++){ d_lock(i); d = s_dialogs[i].head; while(d){ LOG(log_level,"INF:"M_NAME":[%4d] Call-ID:<%.*s>\t DIR[%d] AOR:<%.*s>\tMet:[%d]\tState:[%d] Exp:[%4d]\n",i, d->call_id.len,d->call_id.s,d->direction, d->aor.len,d->aor.s, d->method,d->state, (int)(d->expires - d_time_now)); d = d->next; } d_unlock(i); } LOG(log_level,"INF:"M_NAME":---------- S-CSCF Dialog List end --------------\n"); }
/** * Prints the list of dialogs. * @param log_level - level to log at */ void print_s_dialogs(int log_level) { s_dialog *d; int i; #ifdef SER_MOD_INTERFACE if (!is_printable(log_level)) #else if (debug<log_level) #endif return; /* to avoid useless calls when nothing will be printed */ d_act_time(); LOG(log_level,"INF:"M_NAME":---------- S-CSCF Dialog List begin --------------\n"); for(i=0;i<s_dialogs_hash_size;i++){ d_lock(i); d = s_dialogs[i].head; while(d){ LOG(log_level,"INF:"M_NAME":[%4d] Dir["ANSI_MAGENTA"%d"ANSI_GREEN "] Call-ID:<"ANSI_BLUE"%.*s"ANSI_GREEN "> AOR:<"ANSI_RED"%.*s"ANSI_GREEN ">\n",i, d->direction, d->call_id.len,d->call_id.s, d->aor.len,d->aor.s); LOG(log_level,"INF:"M_NAME":\t\tMethod:["ANSI_MAGENTA"%d"ANSI_GREEN "] State:["ANSI_MAGENTA"%d"ANSI_GREEN "] Exp:["ANSI_MAGENTA"%4d"ANSI_GREEN"] Ref:["ANSI_MAGENTA"%.*s"ANSI_GREEN"] Event:["ANSI_MAGENTA"%.*s"ANSI_GREEN"]\n", d->method, d->state, (int)(d->expires - d_time_now), d->refresher.len,d->refresher.s, d->event.len,d->event.s); d = d->next; } d_unlock(i); } LOG(log_level,"INF:"M_NAME":---------- S-CSCF Dialog List end --------------\n"); }
/** * The dialog timer looks for expired dialogs and removes them. * @param ticks - the current time * @param param - pointer to the dialogs list */ void dialog_timer(unsigned int ticks, void* param) { p_dialog *d,*dn; int i; #ifdef WITH_IMS_PM int dialog_cnt[DLG_METHOD_MAX+1]; for(i=0;i<=DLG_METHOD_MAX;i++) dialog_cnt[i]=0; #endif LOG(L_DBG,"DBG:"M_NAME":dialog_timer: Called at %d\n",ticks); if (!p_dialogs) p_dialogs = (p_dialog_hash_slot*)param; d_act_time(); for(i=0;i<p_dialogs_hash_size;i++){ d_lock(i); d = p_dialogs[i].head; while(d){ dn = d->next; if (d->expires<=d_time_now) { if (!terminate_p_dialog(d)) del_p_dialog(d); } #ifdef WITH_IMS_PM else dialog_cnt[d->method]++; #endif d = dn; } d_unlock(i); } print_p_dialogs(L_INFO); #ifdef WITH_IMS_PM for(i=0;i<=DLG_METHOD_MAX;i++) IMS_PM_LOG11(RD_NbrDialogs,get_dialog_method_str(i),dialog_cnt[i]); #endif }
/** * The dialog timer looks for expires dialogs and removes them * @param ticks - the current time * @param param - pointer to the dialogs list */ void dialog_timer(unsigned int ticks, void* param) { s_dialog *d,*dn; int i; LOG(L_DBG,"DBG:"M_NAME":dialog_timer: Called at %d\n",ticks); if (!s_dialogs) s_dialogs = (s_dialog_hash_slot*)param; d_act_time(); for(i=0;i<s_dialogs_hash_size;i++){ d_lock(i); d = s_dialogs[i].head; while(d){ dn = d->next; if (d->expires<=d_time_now) { if (!terminate_s_dialog(d)) del_s_dialog(d); } d = dn; } d_unlock(i); } print_s_dialogs(L_INFO); }
/** * Updates a dialog. * If the initial request was: * - INVITE - refreshes the expiration or looks for the BYE and destroys the dialog * if found * - SUBSCRIBE - looks for the Subscription-state in NOTIFY, refreshes the expiration * and if terminated destroys the dialog * - When adding more dialogs, add the refreshal methods here or they will expire and will * be destroyed. Also add the termination to reduce the memory consumption and improve the * performance. * @param msg - the request/response * @param str1 - direction - "orig" or "term" * @param str2 - not used * @returns #CSCF_RETURN_TRUE if ok, #CSCF_RETURN_FALSE if not or #CSCF_RETURN_BREAK on error */ int P_update_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; p_dialog *d; int response; int cseq; struct hdr_field *h=0; struct sip_msg *req=0; str host; int port,transport; int expires; str totag; time_t t_time=0; str ses_exp = {0,0}; str refresher = {0,0}; enum p_dialog_direction dir; dir = get_dialog_direction(str1); if (msg->first_line.type==SIP_REPLY) req = cscf_get_request_from_reply(msg); else req = msg; if (!find_dialog_contact(req,dir,&host,&port,&transport)){ LOG(L_ERR,"ERR:"M_NAME":P_update_dialog(%s): Error retrieving %s contact\n",str1,str1); return CSCF_RETURN_BREAK; } call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; LOG(L_DBG,"DBG:"M_NAME":P_update_dialog(%s): Call-ID <%.*s>\n",str1,call_id.len,call_id.s); d = get_p_dialog(call_id,host,port,transport,&dir); if (!d) d = get_p_dialog(call_id,host,port,transport,0); if (!d){ if (msg->first_line.type==SIP_REQUEST && msg->first_line.u.request.method.len == 3 && strncasecmp(msg->first_line.u.request.method.s,"ACK",3)){ /* to skip the ACK after a 4xx when the dialog was dropped already*/ return CSCF_RETURN_TRUE; }else{ LOG(L_CRIT,"ERR:"M_NAME":P_update_dialog: dialog does not exists!\n"); return CSCF_RETURN_FALSE; } } if (msg->first_line.type==SIP_REQUEST){ /* Request */ LOG(L_DBG,"DBG:"M_NAME":P_update_dialog(%s): Method <%.*s> \n",str1, msg->first_line.u.request.method.len,msg->first_line.u.request.method.s); cseq = cscf_get_cseq(msg,&h); if (cseq>d->last_cseq) d->last_cseq = cseq; if (get_dialog_method(msg->first_line.u.request.method) == DLG_METHOD_INVITE) { d->uac_supp_timer = supports_extension(msg, &str_ext_timer); ses_exp = cscf_get_session_expires_body(msg, &h); t_time = cscf_get_session_expires(ses_exp, &refresher); if (!t_time){ d->expires = d_act_time()+pcscf_dialogs_expiration_time; d->lr_session_expires = 0; } else { d->expires = d_act_time() + t_time; d->lr_session_expires = t_time; if (refresher.len) STR_SHM_DUP(d->refresher, refresher, "DIALOG_REFRESHER"); } } else if (d->method == DLG_METHOD_SUBSCRIBE && msg->first_line.u.request.method.len == 6 && strncasecmp(msg->first_line.u.request.method.s,"NOTIFY",6)==0) { // Subscription-State header is mandatory for NOTIFY. See RFC 3265, Section 7.2 expires = cscf_get_subscription_state(msg); if (expires >= 0) { d->expires = d_act_time()+expires; } else { d->expires = d_act_time()+pcscf_dialogs_expiration_time; } } else { expires = cscf_get_expires_hdr(msg); if (expires >= 0) { LOG(L_INFO,"DBG:"M_NAME":P_update_dialog(%.*s): Update expiration time to %d via Expire header 2\n",call_id.len,call_id.s,expires); d->expires = d_act_time()+expires; } else { LOG(L_INFO,"INF:"M_NAME": update_dialog(%.*s): d->expires+=pcscf_dialogs_expiration_time 4\n",call_id.len,call_id.s); d->expires = d_act_time()+pcscf_dialogs_expiration_time; } d->lr_session_expires = 0; } }else{ /* Reply */ response = msg->first_line.u.reply.statuscode; LOG(L_DBG,"DBG:"M_NAME":P_update_dialog(%s): <%d> \n",str1,response); cseq = cscf_get_cseq(msg,&h); if (cseq==0 || h==0) return CSCF_RETURN_FALSE; if (d->first_cseq==cseq && d->method_str.len == ((struct cseq_body *)h->parsed)->method.len && strncasecmp(d->method_str.s,((struct cseq_body *)h->parsed)->method.s,d->method_str.len)==0 && d->state < DLG_STATE_CONFIRMED){ /* reply to initial request */ if (response<200 && response>100){ save_dialog_routes(msg,str1,d); d->state = DLG_STATE_EARLY; d->expires = d_act_time()+300; cscf_get_to_tag(msg,&totag); tmb.update_dlg_uas(d->dialog_s,response,&totag); tmb.dlg_response_uac(d->dialog_c,msg,IS_NOT_TARGET_REFRESH); }else if (response>=200 && response<300){ save_dialog_routes(msg,str1,d); d->state = DLG_STATE_CONFIRMED; update_dialog_on_reply(msg, d); cscf_get_to_tag(msg,&totag); tmb.update_dlg_uas(d->dialog_s,response,&totag); tmb.dlg_response_uac(d->dialog_c,msg,IS_NOT_TARGET_REFRESH); }else if (response>300){ d->state = DLG_STATE_TERMINATED; d_unlock(d->hash); return P_drop_dialog(msg,str1,str2); } }else{ /* reply to subsequent request */ if (!req) req = cscf_get_request_from_reply(msg); /* destroy dialogs on specific methods */ switch (d->method){ case DLG_METHOD_OTHER: expires = cscf_get_expires_hdr(msg); if (expires >= 0) { d->expires = d_act_time()+expires; } else { d->expires = d_act_time()+pcscf_dialogs_expiration_time; } d->lr_session_expires = 0; break; case DLG_METHOD_INVITE: if (req && req->first_line.u.request.method.len==3 && strncasecmp(req->first_line.u.request.method.s,"BYE",3)==0){ d->state = DLG_STATE_TERMINATED; d_unlock(d->hash); return P_drop_dialog(msg,str1,str2); } update_dialog_on_reply(msg, d); break; case DLG_METHOD_SUBSCRIBE: // if (req && req->first_line.u.request.method.len==9 && // strncasecmp(req->first_line.u.request.method.s,"SUBSCRIBE",9)==0 && // cscf_get_expires_hdr(msg)==0){ // d->state = DLG_STATE_TERMINATED; // d_unlock(d->hash); // return P_drop_dialog(msg,str1,str2); // } if (req && req->first_line.u.request.method.len==6 && strncasecmp(req->first_line.u.request.method.s,"NOTIFY",6)==0){ expires = cscf_get_subscription_state(req); if (expires==0){ d->state = DLG_STATE_TERMINATED; d_unlock(d->hash); return P_drop_dialog(msg,str1,str2); }else if (expires>0){ d->expires = d_act_time() + expires; } } break; } if (cseq>d->last_cseq) d->last_cseq = cseq; } } d_unlock(d->hash); print_p_dialogs(L_INFO); return CSCF_RETURN_TRUE; out_of_memory: d_unlock(d->hash); return CSCF_RETURN_ERROR; }
/** * Releases a call from the on reply route block * called with any reply to an INVITE * useful in cases of rejecting a call when you are processing the SDP * or handling QoS things * @msg - the sip message being processed * @str1 - the first parameter "orig" or "term" * @str2 - [optional] the Reason header that you want to go to the messages * @returns - BREAK ... whatever happens this message is not relayed */ int P_release_call_onreply(struct sip_msg *msg,char *str1,char *str2) { enum p_dialog_direction dir; p_dialog *d=NULL; str callid; struct hdr_field *h1; str reason= {NULL,0}; unsigned int hash; struct cell* t; /*needed to distinguish between UPDATE and INVITE*/ LOG(L_INFO,ANSI_WHITE"P_release_call_on_reply\n"); if (str2) { reason.s=str2; reason.len=strlen(str2); } else reason = _488_text_s; dir= (str1[0]=='o' || str1[0]=='O' || str1[0]=='0')? DLG_MOBILE_ORIGINATING : DLG_MOBILE_TERMINATING; if (msg->first_line.type== SIP_REQUEST) { LOG(L_ERR,"ERR: P_release_call_on_reply called with a request\n"); return CSCF_RETURN_FALSE; } callid=cscf_get_call_id(msg,&h1); if (is_p_dialog_dir(callid,dir)) { d=get_p_dialog_dir(callid,dir); hash=d->hash; t=tmb.t_gett(); if (!t) { LOG(L_ERR,"P_release_call_onreply(): unable to get transaction\n"); return CSCF_RETURN_BREAK; } if (t->method.len==6 && memcmp(t->method.s,"INVITE",6)==0) { // If its an INVTE, the state depends on which reply we are processing if (msg->first_line.u.reply.statuscode > 199) { release_call_previous(d,RELEASE_CALL_WEIRD,488,reason); } else { release_call_previous(d,RELEASE_CALL_EARLY,488,reason); } // This means we already finished with the dialog if (d->pcc_session_id.s) shm_free(d->pcc_session_id.s); d->pcc_session_id.s=0; d->pcc_session_id.len=0; d_unlock(hash); return CSCF_RETURN_BREAK; } else { //UPDATE so early release_call_early(d,488,reason); d_unlock(hash); return CSCF_RETURN_BREAK; } } else { LOG(L_ERR,"ERR:"M_NAME "P_release_call_onreply : unable to find dialog\n"); return CSCF_RETURN_ERROR; } }
/** * Checks if the message follows the saved dialog routes. * @param msg - the SIP request * @param str1 - direction - "orig" or "term" * @param str2 - not used * @returns #CSCF_RETURN_TRUE if ok, #CSCF_RETURN_FALSE if not or #CSCF_RETURN_BREAK on error */ int P_follows_dialog_routes(struct sip_msg *msg,char *str1,char *str2) { int i; struct hdr_field *hdr=0; rr_t *r; p_dialog *d; str call_id,host; int port,transport; enum p_dialog_direction dir; dir = get_dialog_direction(str1); if (!find_dialog_contact(msg,dir,&host,&port,&transport)){ LOG(L_ERR,"ERR:"M_NAME":P_follows_dialog_routes(): Error retrieving %s contact\n",str1); return CSCF_RETURN_BREAK; } call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; LOG(L_DBG,"DBG:"M_NAME":P_follows_dialog_routes(%s): Call-ID <%.*s> %d://%.*s:%d\n", str1,call_id.len,call_id.s, transport,host.len,host.s,port); d = get_p_dialog(call_id,host,port,transport,&dir); if (!d) d = get_p_dialog(call_id,host,port,transport,0); if (!d){ LOG(L_ERR,"ERR:"M_NAME":P_follows_dialog_routes: dialog does not exists!\n"); return CSCF_RETURN_FALSE; } /* todo - fix this to match exactly the first request */ if (d->first_cseq == cscf_get_cseq(msg,0) && d->method == get_dialog_method(msg->first_line.u.request.method)){ LOG(L_INFO,"INF:"M_NAME":P_follows_dialog_routes: this looks like the initial request (retransmission?)!\n"); goto ok; } hdr = cscf_get_next_route(msg,0); r = 0; if (!hdr){ if (d->routes_cnt==0) goto ok; else goto nok; } r = (rr_t*) hdr->parsed; for(i=0;i<d->routes_cnt;i++){ LOG(L_DBG,"DBG:"M_NAME":P_follows_dialog_routes: must <%.*s>\n", d->routes[i].len,d->routes[i].s); if (!r) { hdr = cscf_get_next_route(msg,hdr); if (!hdr) goto nok; r = (rr_t*) hdr->parsed; } LOG(L_DBG,"DBG:"M_NAME":P_follows_dialog_routes: src %.*s\n", r->nameaddr.uri.len,r->nameaddr.uri.s); if (r->nameaddr.uri.len==d->routes[i].len && strncasecmp(r->nameaddr.uri.s, d->routes[i].s,d->routes[i].len)==0) { LOG(L_DBG,"DBG:"M_NAME":P_follows_dialog_routes: src match\n"); } else { LOG(L_DBG,"DBG:"M_NAME":P_follows_dialog_routes: found <%.*s>\n", r->nameaddr.uri.len,r->nameaddr.uri.s); goto nok; } r = r->next; } if (r) { LOG(L_DBG,"DBG:"M_NAME":P_follows_dialog_routes: still has some extra Routes\n"); goto nok; } else if (cscf_get_next_route(msg,hdr)) goto nok; ok: if (d) d_unlock(d->hash); return CSCF_RETURN_TRUE; nok: if (d) d_unlock(d->hash); return CSCF_RETURN_FALSE; }
/** * Updates a dialog. * If the initial request was: * - INVITE - refreshes the expiration or looks for the BYE and destroys the dialog * if found * - SUBSCRIBE - looks for the Subscription-state in NOTIFY, refreshes the expiration * and if terminated destroys the dialog * - When adding more dialogs, add the refreshal methods here or they will expire and will * be destroyed. Also add the termination to reduce the memory consumption and improve the * performance. * @param msg - the request/response * @param str1 - direction - "orig" or "term" * @param str2 - not used * @returns #CSCF_RETURN_TRUE if ok, #CSCF_RETURN_FALSE if not or #CSCF_RETURN_BREAK on error */ int S_update_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; s_dialog *d; int response; int cseq; struct hdr_field *h=0; struct sip_msg *req=0; int expires=0; str totag; time_t t_time; str ses_exp = {0,0}; str refresher = {0,0}; enum s_dialog_direction dir = get_dialog_direction(str1); // if (!find_dialog_aor(msg,str1,&aor)){ // req = cscf_get_request_from_reply(msg); // if (!find_dialog_aor(req,str1,&aor)){ // LOG(L_ERR,"ERR:"M_NAME":S_update_dialog(%s): Error retrieving %s contact\n",str1,str1); // return CSCF_RETURN_BREAK; // } // } call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; LOG(L_DBG,"DBG:"M_NAME":S_update_dialog(%s): Call-ID <%.*s>\n",str1,call_id.len,call_id.s); d = get_s_dialog_dir(call_id,dir); // if (!d && msg->first_line.type==SIP_REPLY){ // /* Try to get the dialog from the request */ // if (!req) req = cscf_get_request_from_reply(msg); // if (!find_dialog_aor(req,str1,&aor)){ // LOG(L_ERR,"ERR:"M_NAME":S_update_dialog(%s): Error retrieving %s contact\n",str1,str1); // return CSCF_RETURN_BREAK; // } // d = get_s_dialog_dir(call_id,aor); // } if (!d){ LOG(L_INFO,"INFO:"M_NAME":S_update_dialog: dialog does not exists!\n"); return CSCF_RETURN_FALSE; } if (msg->first_line.type==SIP_REQUEST){ /* Request */ LOG(L_DBG,"DBG:"M_NAME":S_update_dialog(%s): Method <%.*s> \n",str1, msg->first_line.u.request.method.len,msg->first_line.u.request.method.s); cseq = cscf_get_cseq(msg,&h); if (cseq>d->last_cseq) d->last_cseq = cseq; if (get_dialog_method(msg->first_line.u.request.method) == DLG_METHOD_INVITE) { d->uac_supp_timer = supports_extension(msg, &str_ext_timer); ses_exp = cscf_get_session_expires_body(msg, &h); t_time = cscf_get_session_expires(ses_exp, &refresher); if (!t_time) { d->expires = d_act_time()+scscf_dialogs_expiration_time; d->lr_session_expires = 0; } else { d->expires = d_act_time() + t_time; d->lr_session_expires = t_time; if (refresher.len) STR_SHM_DUP(d->refresher, refresher, "DIALOG_REFRESHER"); } } else if (d->method == DLG_METHOD_SUBSCRIBE && msg->first_line.u.request.method.len == 6 && strncasecmp(msg->first_line.u.request.method.s,"NOTIFY",6)==0) { // Subscription-State header is mandatory for NOTIFY. See RFC 3265, Section 7.2 expires = cscf_get_subscription_state(msg); if (expires >= 0) d->expires = d_act_time()+expires; else d->expires = d_act_time()+scscf_dialogs_expiration_time; } else { expires = cscf_get_expires_hdr(msg,0); if (expires >= 0) d->expires = d_act_time()+expires; else d->expires = d_act_time()+scscf_dialogs_expiration_time; d->lr_session_expires = 0; } }else{ /* Reply */ response = msg->first_line.u.reply.statuscode; LOG(L_DBG,"DBG:"M_NAME":S_update_dialog(%s): <%d> \n",str1,response); cseq = cscf_get_cseq(msg,&h); if (cseq==0 || h==0) return CSCF_RETURN_FALSE; if (d->first_cseq==cseq && d->method_str.len == ((struct cseq_body *)h->parsed)->method.len && strncasecmp(d->method_str.s,((struct cseq_body *)h->parsed)->method.s,d->method_str.len)==0 && d->state < DLG_STATE_CONFIRMED){ /* reply to initial request */ if (response<200){ d->state = DLG_STATE_EARLY; d->expires = d_act_time()+300; }else if (response>=200 && response<300){ d->state = DLG_STATE_CONFIRMED; update_dialog_on_reply(msg, d); /*I save the dialogs only here because * i only want to release confirmed dialogs*/ cscf_get_to_tag(msg,&totag); if (d->dialog_s){ tmb.update_dlg_uas(d->dialog_s,response,&totag); tmb.dlg_response_uac(d->dialog_c,msg,IS_NOT_TARGET_REFRESH); }else{ LOG(L_ERR,"ERR:S_update_dialog(): dialog_s for dialog was NULL!\n"); } }else if (response>300){ d->state = DLG_STATE_TERMINATED; d_unlock(d->hash); struct cell * t = tmb.t_gett(); if(t->nr_of_outgoings < 2) return S_drop_dialog(msg,str1,str2); } }else{ /* reply to subsequent request */ if (!req) req = cscf_get_request_from_reply(msg); /* destroy dialogs on specific methods */ switch (d->method){ case DLG_METHOD_OTHER: d->expires = d_act_time()+scscf_dialogs_expiration_time; d->lr_session_expires = 0; break; case DLG_METHOD_INVITE: if (req && req->first_line.u.request.method.len==3 && strncasecmp(req->first_line.u.request.method.s,"BYE",3)==0){ d->state = DLG_STATE_TERMINATED; d_unlock(d->hash); return S_drop_dialog(msg,str1,str2); } update_dialog_on_reply(msg, d); break; case DLG_METHOD_SUBSCRIBE: // if (req && req->first_line.u.request.method.len==9 && // strncasecmp(req->first_line.u.request.method.s,"SUBSCRIBE",9)==0 && // cscf_get_expires_hdr(msg)==0){ // d->state = DLG_STATE_TERMINATED; // d_unlock(d->hash); // return P_dros_dialog(msg,str1,str2); // } if (req && req->first_line.u.request.method.len==6 && strncasecmp(req->first_line.u.request.method.s,"NOTIFY",6)==0){ expires = cscf_get_subscription_state(req); if (expires==0){ d->state = DLG_STATE_TERMINATED; d_unlock(d->hash); return S_drop_dialog(msg,str1,str2); }else if (expires>0){ d->expires = d_act_time() + expires; } } else if (req && req->first_line.u.request.method.len==9 && strncasecmp(req->first_line.u.request.method.s,"SUBSCRIBE",9)==0){ expires = cscf_get_expires_hdr(msg,0); if (expires >= 0) d->expires = d_act_time()+expires; else d->expires = d_act_time()+scscf_dialogs_expiration_time; } break; } if (cseq>d->last_cseq) d->last_cseq = cseq; } } d_unlock(d->hash); print_s_dialogs(L_INFO); return CSCF_RETURN_TRUE; out_of_memory: if (d) d_unlock(d->hash); return CSCF_RETURN_ERROR; }
/** * Inserts the Route header containing the dialog routes to be enforced * @param msg - the SIP message to add to * @param str1 - the value to insert - !!! quoted if needed * @param str2 - not used * @returns #CSCF_RETURN_TRUE if ok, #CSCF_RETURN_FALSE if not or #CSCF_RETURN_BREAK on error */ int P_enforce_dialog_routes(struct sip_msg *msg,char *str1,char*str2) { int i; str newuri={0,0}; p_dialog *d; str call_id,host; int port,transport; str x; enum p_dialog_direction dir; dir = get_dialog_direction(str1); if (!find_dialog_contact(msg,dir,&host,&port,&transport)){ LOG(L_ERR,"ERR:"M_NAME":P_enforce_dialog_routes(): Error retrieving %s contact\n",str1); return CSCF_RETURN_BREAK; } call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; LOG(L_INFO,"DBG:"M_NAME":P_enforce_dialog_routes(%s): Call-ID <%.*s> %d://%.*s:%d\n", str1,call_id.len,call_id.s, transport,host.len,host.s,port); d = get_p_dialog(call_id,host,port,transport,&dir); if (!d) d = get_p_dialog(call_id,host,port,transport,0); if (!d){ LOG(L_ERR,"ERR:"M_NAME":P_enforce_dialog_routes: dialog does not exists!\n"); return CSCF_RETURN_FALSE; } if (!d->routes_cnt){ d_unlock(d->hash); return CSCF_RETURN_TRUE; } x.len = route_s.len + route_e.len + (d->routes_cnt-1)*route_1.len; for(i=0;i<d->routes_cnt;i++) x.len+=d->routes[i].len; x.s = pkg_malloc(x.len); if (!x.s){ LOG(L_ERR, "ERR:"M_NAME":P_enforce_dialog_routes: Error allocating %d bytes\n", x.len); x.len=0; d_unlock(d->hash); return CSCF_RETURN_ERROR; } x.len=0; STR_APPEND(x,route_s); for(i=0;i<d->routes_cnt;i++){ if (i) STR_APPEND(x,route_1); STR_APPEND(x,d->routes[i]); } STR_APPEND(x,route_e); newuri.s = pkg_malloc(d->routes[0].len); if (!newuri.s){ LOG(L_ERR, "ERR:"M_NAME":P_enforce_dialog_routes: Error allocating %d bytes\n", d->routes[0].len); d_unlock(d->hash); return CSCF_RETURN_ERROR; } newuri.len = d->routes[0].len; memcpy(newuri.s,d->routes[0].s,newuri.len); if (msg->dst_uri.s) pkg_free(msg->dst_uri.s); msg->dst_uri = newuri; //LOG(L_ERR,"%.*s",x.len,x.s); d_unlock(d->hash); if (cscf_add_header_first(msg,&x,HDR_ROUTE_T)) { if (cscf_del_all_headers(msg,HDR_ROUTE_T)) return CSCF_RETURN_TRUE; else { LOG(L_ERR,"ERR:"M_NAME":P_enforce_dialog_routes: new Route headers added, but failed to drop old ones.\n"); return CSCF_RETURN_ERROR; } } else { if (x.s) pkg_free(x.s); return CSCF_RETURN_ERROR; } }
/** * Creates a snapshots of the dialogs data and then calls the dumping function. * @returns 1 on success or 0 on failure */ int make_snapshot_dialogs() { bin_data x={0,0,0}; p_dialog *d; int i,k; time_t unique = time(0); FILE *f; switch (pcscf_persistency_mode) { case NO_PERSISTENCY: return 0; case WITH_FILES: f = bin_dump_to_file_create(pcscf_persistency_location,"pdialogs",unique); if (!f) return 0; for(i=0;i<p_dialogs_hash_size;i++){ if (!bin_alloc(&x,1024)) goto error; d_lock(i); d = p_dialogs[i].head; if (d){ while(d){ if (!bin_encode_p_dialog(&x,d)) goto error; d = d->next; } d_unlock(i); k = bind_dump_to_file_append(f,&x); if (k!=x.len) { LOG(L_ERR,"ERR:"M_NAME":make_snapshot_registrar: error while dumping to file - only wrote %d bytes of %d \n",k,x.len); d_unlock(i); bin_free(&x); return 0; } } else d_unlock(i); bin_free(&x); } return bind_dump_to_file_close(f,pcscf_persistency_location,"pdialogs",unique); break; case WITH_DATABASE_BULK: if (!bin_alloc(&x,1024)) goto error; for(i=0;i<p_dialogs_hash_size;i++){ d_lock(i); d = p_dialogs[i].head; while(d){ if (!bin_encode_p_dialog(&x,d)) goto error; d = d->next; } d_unlock(i); } return bin_dump_to_db(&x, P_DIALOGS); case WITH_DATABASE_CACHE: return bin_dump_to_db(NULL, P_DIALOGS); //ignore x, x is empty default: LOG(L_ERR,"ERR:"M_NAME":make_snapshot_registrar: Snapshot done but no such mode %d\n",pcscf_persistency_mode); return 0; } error: if (x.s) bin_free(&x); return 0; }
/** * Updates a dialog. * If the initial request was: * - INVITE - refreshes the expiration or looks for the BYE and destroys the dialog * if found * - SUBSCRIBE - looks for the Subscription-state in NOTIFY, refreshes the expiration * and if terminated destroys the dialog * - When adding more dialogs, add the refreshal methods here or they will expire and will * be destroyed. Also add the termination to reduce the memory consumption and improve the * performance. * @param msg - the request/response * @param str1 - direction - "orig" or "term" * @param str2 - not used * @returns #CSCF_RETURN_TRUE if ok, #CSCF_RETURN_FALSE if not or #CSCF_RETURN_BREAK on error */ int S_update_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; s_dialog *d; int response; int cseq; struct hdr_field *h=0; struct sip_msg *req=0; int expires=0; str totag; enum s_dialog_direction dir = get_dialog_direction(str1); // if (!find_dialog_aor(msg,str1,&aor)){ // req = cscf_get_request_from_reply(msg); // if (!find_dialog_aor(req,str1,&aor)){ // LOG(L_ERR,"ERR:"M_NAME":S_update_dialog(%s): Error retrieving %s contact\n",str1,str1); // return CSCF_RETURN_BREAK; // } // } call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; LOG(L_INFO,"DBG:"M_NAME":S_update_dialog(%s): Call-ID <%.*s>\n",str1,call_id.len,call_id.s); d = get_s_dialog_dir(call_id,dir); // if (!d && msg->first_line.type==SIP_REPLY){ // /* Try to get the dialog from the request */ // if (!req) req = cscf_get_request_from_reply(msg); // if (!find_dialog_aor(req,str1,&aor)){ // LOG(L_ERR,"ERR:"M_NAME":S_update_dialog(%s): Error retrieving %s contact\n",str1,str1); // return CSCF_RETURN_BREAK; // } // d = get_s_dialog_dir(call_id,aor); // } if (!d){ LOG(L_INFO,"INFO:"M_NAME":S_update_dialog: dialog does not exists!\n"); return CSCF_RETURN_FALSE; } if (msg->first_line.type==SIP_REQUEST){ /* Request */ LOG(L_DBG,"DBG:"M_NAME":S_update_dialog(%s): Method <%.*s> \n",str1, msg->first_line.u.request.method.len,msg->first_line.u.request.method.s); cseq = cscf_get_cseq(msg,&h); if (cseq>d->last_cseq) d->last_cseq = cseq; d->expires = d_act_time()+scscf_dialogs_expiration_time; }else{ /* Reply */ response = msg->first_line.u.reply.statuscode; LOG(L_DBG,"DBG:"M_NAME":S_update_dialog(%s): <%d> \n",str1,response); cseq = cscf_get_cseq(msg,&h); if (cseq==0 || h==0) return CSCF_RETURN_FALSE; if (d->first_cseq==cseq && d->method_str.len == ((struct cseq_body *)h->parsed)->method.len && strncasecmp(d->method_str.s,((struct cseq_body *)h->parsed)->method.s,d->method_str.len)==0 && d->state < DLG_STATE_CONFIRMED){ /* reply to initial request */ if (response<200){ d->state = DLG_STATE_EARLY; d->expires = d_act_time()+300; }else if (response>=200 && response<300){ d->state = DLG_STATE_CONFIRMED; d->expires = d_act_time()+scscf_dialogs_expiration_time; /*I save the dialogs only here because * i only want to release confirmed dialogs*/ cscf_get_to_tag(msg,&totag); tmb.update_dlg_uas(d->dialog_s,response,&totag); tmb.dlg_response_uac(d->dialog_c,msg,IS_NOT_TARGET_REFRESH); }else if (response>300){ d->state = DLG_STATE_TERMINATED; d_unlock(d->hash); return S_drop_dialog(msg,str1,str2); } }else{ /* reply to subsequent request */ if (!req) req = cscf_get_request_from_reply(msg); /* destroy dialogs on specific methods */ switch (d->method){ case DLG_METHOD_OTHER: d->expires = d_act_time()+scscf_dialogs_expiration_time; break; case DLG_METHOD_INVITE: if (req && req->first_line.u.request.method.len==3 && strncasecmp(req->first_line.u.request.method.s,"BYE",3)==0){ d->state = DLG_STATE_TERMINATED; d_unlock(d->hash); return S_drop_dialog(msg,str1,str2); } d->expires = d_act_time()+scscf_dialogs_expiration_time; break; case DLG_METHOD_SUBSCRIBE: // if (req && req->first_line.u.request.method.len==9 && // strncasecmp(req->first_line.u.request.method.s,"SUBSCRIBE",9)==0 && // cscf_get_expires_hdr(msg)==0){ // d->state = DLG_STATE_TERMINATED; // d_unlock(d->hash); // return P_dros_dialog(msg,str1,str2); // } if (req && req->first_line.u.request.method.len==6 && strncasecmp(req->first_line.u.request.method.s,"NOTIFY",6)==0){ expires = cscf_get_subscription_state(req); if (expires==0){ d->state = DLG_STATE_TERMINATED; d_unlock(d->hash); return S_drop_dialog(msg,str1,str2); }else if (expires>0){ d->expires = expires; } } break; } if (cseq>d->last_cseq) d->last_cseq = cseq; } } d_unlock(d->hash); // print_s_dialogs(L_INFO); return CSCF_RETURN_TRUE; }
/** * Saves a dialog. * @param msg - the initial request * @param str1 - direction - "orig" or "term" * @param str2 - not used * @returns #CSCF_RETURN_TRUE if ok, #CSCF_RETURN_FALSE if not or #CSCF_RETURN_BREAK on error */ int S_save_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; s_dialog *d; str aor; char buf1[256],buf2[256]; str uri,tag,ruri,x; time_t t_time; str ses_exp = {0,0}; str refresher = {0,0}; str event = {0,0}; struct hdr_field *h; unsigned int hash; enum s_dialog_direction dir = get_dialog_direction(str1); if (!find_dialog_aor(msg,dir,&aor)){ LOG(L_ERR,"ERR:"M_NAME":S_save_dialog(): Error retrieving %s contact\n",str1); return CSCF_RETURN_BREAK; } call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; LOG(L_INFO,"DBG:"M_NAME":S_save_dialog(%s): Call-ID <%.*s>\n",str1,call_id.len,call_id.s); if (is_s_dialog(call_id,aor,dir)){ LOG(L_ERR,"ERR:"M_NAME":S_save_dialog: dialog already exists!\n"); return CSCF_RETURN_TRUE; } d = add_s_dialog(call_id,aor,dir); if (!d) return CSCF_RETURN_FALSE; d->method = get_dialog_method(msg->first_line.u.request.method); STR_SHM_DUP(d->method_str,msg->first_line.u.request.method,"shm"); d->first_cseq = cscf_get_cseq(msg,0); d->last_cseq = d->first_cseq; d->state = DLG_STATE_INITIAL; d->uac_supp_timer = supports_extension(msg, &str_ext_timer); ses_exp = cscf_get_session_expires_body(msg, &h); t_time = cscf_get_session_expires(ses_exp, &refresher); if (!t_time) { d->expires = d_act_time() + 60; d->lr_session_expires = 0; } else { d->expires = d_act_time() + t_time; d->lr_session_expires = t_time; if (refresher.len) STR_SHM_DUP(d->refresher, refresher, "DIALOG_REFRESHER"); } cscf_get_from_tag(msg,&tag); cscf_get_from_uri(msg,&x); uri.len = snprintf(buf1,256,"<%.*s>",x.len,x.s); uri.s = buf1; cscf_get_to_uri(msg,&x); ruri.len = snprintf(buf2,256,"<%.*s>",x.len,x.s); ruri.s = buf2; tmb.new_dlg_uac(&call_id, &tag, d->first_cseq,&uri, &ruri, &d->dialog_c); tmb.new_dlg_uas(msg,99,&d->dialog_s); event = cscf_get_event(msg); if (event.len){ STR_SHM_DUP(d->event,event,"shm"); } else d->event = event; d_unlock(d->hash); print_s_dialogs(L_INFO); return CSCF_RETURN_TRUE; out_of_memory: if (d){ hash = d->hash; del_s_dialog(d); d_unlock(hash); } return CSCF_RETURN_ERROR; }
/** * Loads the dialogs data from the last snapshot. * @returns 1 on success or 0 on failure */ int load_snapshot_dialogs() { bin_data x; p_dialog *d; int k,max; FILE *f; switch (pcscf_persistency_mode){ case NO_PERSISTENCY: k=0; case WITH_FILES: f = bin_load_from_file_open(pcscf_persistency_location,"pdialogs"); if (!f) return 0; bin_alloc(&x,128*1024); k=bin_load_from_file_read(f,&x); max = x.max; x.max=0; LOG(L_INFO,"INFO:"M_NAME":load_snapshot_dialogs: max %d len %d\n",x.max,x.len); while(x.max<x.len){ d = bin_decode_p_dialog(&x); if (!d) return 0; LOG(L_INFO,"INFO:"M_NAME":load_snapshot_dialogs: Loaded p_dialog for <%.*s>\n",d->host.len,d->host.s); d_lock(d->hash); d->prev = p_dialogs[d->hash].tail; d->next = 0; if (p_dialogs[d->hash].tail) p_dialogs[d->hash].tail->next = d; p_dialogs[d->hash].tail = d; if (!p_dialogs[d->hash].head) p_dialogs[d->hash].head = d; d_unlock(d->hash); memmove(x.s,x.s+x.max,x.len-x.max); x.len -= x.max; x.max = max; k=bin_load_from_file_read(f,&x); max = x.max; x.max = 0; } bin_free(&x); bin_load_from_file_close(f); k = 1; break; case WITH_DATABASE_BULK: k=bin_load_from_db(&x, P_DIALOGS); x.max=0; LOG(L_INFO,"INFO:"M_NAME":load_snapshot_dialogs: max %d len %d\n",x.max,x.len); while(x.max<x.len){ d = bin_decode_p_dialog(&x); if (!d) return 0; LOG(L_INFO,"INFO:"M_NAME":load_snapshot_dialogs: Loaded p_dialog for <%.*s>\n",d->host.len,d->host.s); d_lock(d->hash); d->prev = p_dialogs[d->hash].tail; d->next = 0; if (p_dialogs[d->hash].tail) p_dialogs[d->hash].tail->next = d; p_dialogs[d->hash].tail = d; if (!p_dialogs[d->hash].head) p_dialogs[d->hash].head = d; d_unlock(d->hash); } bin_free(&x); break; case WITH_DATABASE_CACHE: k=bin_load_from_db(NULL, P_DIALOGS); //ignore x, x is empty break; default: LOG(L_ERR,"ERR:"M_NAME":load_snapshot_dialogs: Can't resume because no such mode %d\n",pcscf_persistency_mode); k=0; } if (!k) goto error; return 1; error: return 0; }