/** * Find out if a message is within a saved dialog. * @param msg - the SIP message * @param str1 - the direction of the dialog ("orig"/"term") * @param str2 - not used * @returns #CSCF_RETURN_TRUE if in, #CSCF_RETURN_FALSE else or #CSCF_RETURN_BREAK on error */ int P_is_in_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; str 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_is_in_dialog(%s): Error retrieving %s contact\n",str1,str1); return CSCF_RETURN_BREAK; } //print_p_dialog(L_ERR); call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; if (is_p_dialog(call_id,host,port,transport,0)) { return CSCF_RETURN_TRUE; } else return CSCF_RETURN_FALSE; }
/** * 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; }
/** * Find out if a message is within a saved dialog. * @param msg - the SIP message * @param str1 - the direction of the dialog ("orig"/"term") * @param str2 - not used * @returns #CSCF_RETURN_TRUE if in, #CSCF_RETURN_FALSE else or #CSCF_RETURN_BREAK on error */ int LRF_is_in_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; enum lrf_dialog_direction dir; dir = get_dialog_direction(str1); str target_uri={0,0}; if (!find_dialog_contact(msg,dir,&target_uri)){ LOG(L_ERR,"ERR:"M_NAME":LRF_is_in_dialog(%s): Error retrieving %s contact\n",str1,str1); return CSCF_RETURN_BREAK; } //print_lrf_dialog(L_ERR); call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; if (is_lrf_dialog(call_id, 0)) { return CSCF_RETURN_TRUE; } else return CSCF_RETURN_FALSE; }
/** * 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 P_save_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; p_dialog *d; str host; time_t t_time; str ses_exp = {0,0}; str refresher = {0,0}; int port,transport; char buf1[256],buf2[256]; str tag,ruri,uri,x; struct hdr_field *h; unsigned int hash; 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_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_save_dialog(%s): Call-ID <%.*s>\n",str1,call_id.len,call_id.s); if (is_p_dialog(call_id,host,port,transport,&dir)){ LOG(L_ERR,"ERR:"M_NAME":P_save_dialog: dialog already exists!\n"); return CSCF_RETURN_TRUE; } d = add_p_dialog(call_id,host,port,transport); 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"); } d->direction=dir; 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); d_unlock(d->hash); print_p_dialogs(L_INFO); return CSCF_RETURN_TRUE; out_of_memory: if (d){ hash = d->hash; del_p_dialog(d); d_unlock(hash); } return CSCF_RETURN_FALSE; }
/** * 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; } }
/** * 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 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; }