/** * Check if the message is from the AS. * Inserts route headers and set the dst_uri * @param msg - the message to check * @param str1 - the direction of the request orig/term * @param str2 - not used * @returns #ISC_RETURN_TRUE if session should continue, #ISC_RETURN_FALSE if not, #ISC_RETURN_BREAK on error */ int ISC_is_session_continued(struct sip_msg *msg,char *str1,char *str2) { int ret = ISC_RETURN_FALSE; isc_mark old_mark; struct sip_msg *req; req = cscf_get_request_from_reply(msg); if (!req) { LOG(L_ERR,"ERR:"M_NAME":ISC_is_session_continued(): There is no transaction \n"); return ISC_RETURN_FALSE; } if (!isc_is_initial_request(req)) { LOG(L_ERR,"ERR:"M_NAME":ISC_is_session_continued(): This is no initial request \n"); return ISC_RETURN_FALSE; } /* starting or resuming? */ if (isc_mark_get_from_lump(req,&old_mark)){ LOG(L_INFO,"INFO:"M_NAME":ISC_is_session_continued(): Message returned handling [%d] \n",old_mark.handling); if (old_mark.handling == IFC_SESSION_CONTINUED) ret = ISC_RETURN_TRUE; else ret = ISC_RETURN_FALSE; } else { LOG(L_ERR,"ERR:"M_NAME":ISC_is_session_continued(): mark not found in lump \n"); ret = ISC_RETURN_ERROR; } return ret; }
/** * enforce a response coming from a UE to contain the same Via headers sent in the corresponding request * @param rpl - the SIP reply * @param str1 - not used * @param str2 - not used * @returns true if ok, false if not or error */ int P_enforce_via_list(struct sip_msg *rpl,char *str1, char *str2) { static struct hdr_field * h = NULL; str hdr; cscf_del_all_headers(rpl, HDR_VIA_T); struct sip_msg *req = cscf_get_request_from_reply(rpl); if (!req){ LOG(L_ERR,"ERR:"M_NAME":P_enforce_via_list: No transactional request found.\n"); return CSCF_RETURN_FALSE; } h = cscf_get_next_via_hdr(req,0); while (h) { hdr.len = h->body.len + via_hdr_s.len + via_hdr_e.len; hdr.s = pkg_malloc(hdr.len); if (!hdr.s) { LOG(L_ERR, "ERR:"M_NAME":P_enforce_via_list: cannot alloc bytes : %d", hdr.len); } hdr.len=0; STR_APPEND(hdr, via_hdr_s); STR_APPEND(hdr, h->body); STR_APPEND(hdr, via_hdr_e); cscf_add_header_first(rpl, &hdr, HDR_VIA_T); h = cscf_get_next_via_hdr(req,h); } return CSCF_RETURN_TRUE; }
/** * Checks if a response coming from a UE contains the same Via headers sent in the corresponding request * @param rpl - the SIP reply * @param str1 - not used * @param str2 - not used * @returns true if ok, false if not or error */ int P_follows_via_list(struct sip_msg *rpl,char *str1, char *str2) { int h_req_pos, h_rpl_pos,indx_rpl, indx_req; struct hdr_field *h_req=NULL, *h_out_req, *h_rpl=NULL, *h_out_rpl; str via_req={0,0}, via_rpl={0,0}; struct sip_msg *req = cscf_get_request_from_reply(rpl); if (!req){ LOG(L_ERR,"ERR:"M_NAME":P_follows_via_list: No transactional request found.\n"); return CSCF_RETURN_ERROR; } indx_rpl = indx_req = 0; //get via headers via_rpl = cscf_get_next_via_str(rpl, 0, 0, &h_out_rpl, &h_rpl_pos); while (via_rpl.len) { if (indx_rpl > 0) //first header from reply shouldn't be checked { if (indx_req == 0) { via_req = cscf_get_next_via_str(req, 0, 0, &h_out_req, &h_req_pos); if (!via_req.len || !cscf_str_via_matching(&via_req, &via_rpl)) { LOG(L_INFO,"DBG:"M_NAME":P_follows_via_list: first via not matching <%.*s>!=<%.*s>\n", via_req.len,via_req.s,via_rpl.len,via_rpl.s); return CSCF_RETURN_FALSE; } } else { if (!via_req.len || (via_req.len!=via_rpl.len) || (strncasecmp(via_req.s, via_rpl.s, via_req.len))) { LOG(L_INFO,"DBG:"M_NAME":P_follows_via_list: not matching <%.*s>!=<%.*s>\n", via_req.len,via_req.s,via_rpl.len,via_rpl.s); return CSCF_RETURN_FALSE; } } indx_req++; h_req = h_out_req; if (!h_req) { break; } via_req = cscf_get_next_via_str(req, h_req, h_req_pos , &h_out_req, &h_req_pos); } indx_rpl++; h_rpl = h_out_rpl; if (!h_rpl) break; via_rpl = cscf_get_next_via_str(rpl, h_rpl, h_rpl_pos , &h_out_rpl, &h_rpl_pos); } if (h_out_req || h_out_rpl) //remaining headers ... { LOG(L_INFO,"DBG:"M_NAME":P_follows_via_list: header count not matching \n"); return CSCF_RETURN_FALSE; } return CSCF_RETURN_TRUE; }
/** * Enforce a response coming from a UE to contain the same Record Route headers sent in the * corresponding request. * @param msg - the SIP reply * @param str1 - not used * @param str2 - not used * @returns #CSCF_RETURN_TRUE on success, #CSCF_RETURN_ERROR on error */ int P_enforce_record_routes(struct sip_msg *msg,char *str1, char *str2) { str hdr = {0,0}; str rr_req = {0,0}; struct sip_msg *req = cscf_get_request_from_reply(msg); LOG(L_INFO,"INF:"M_NAME":P_enforce_record_routes(): Enforcing RR in %d reply with the request ones\n", msg->first_line.u.reply.statuscode); if (!req){ LOG(L_ERR,"ERR:"M_NAME":P_enforce_record_routes(): No transactional request found.\n"); return CSCF_RETURN_ERROR; } if(!cscf_del_all_headers(msg, HDR_RECORDROUTE_T)){ LOG(L_ERR,"ERR:"M_NAME":P_enforce_record_routes(): error while deleting headers\n"); return CSCF_RETURN_ERROR; } rr_req = cscf_get_record_routes(req); if(rr_req.len){ hdr.len = pcscf_record_route_mt.len + s_record_route_s.len + rr_req.len+s_record_route_e.len; if(!(hdr.s = pkg_malloc(hdr.len))){ LOG(L_ERR,"ERR:"M_NAME":P_enforce_record_routes(): Unable to allocate memory for hdr\n"); goto out_of_memory; } hdr.len = 0; STR_APPEND(hdr,pcscf_record_route_mt); STR_APPEND(hdr,s_record_route_s); STR_APPEND(hdr,rr_req); STR_APPEND(hdr,s_record_route_e); }else{ LOG(L_ERR,"ERR:"M_NAME":P_enforce_record_routes(): Unable to get record routes - the RR should not be empty...\n"); // still, let it continue, maybe it was empty on purpose //return CSCF_RETURN_ERROR; STR_PKG_DUP(hdr,pcscf_record_route_mt,"pkg"); } if(!cscf_add_header_first(msg,&hdr,HDR_RECORDROUTE_T)){ LOG(L_ERR,"ERR:"M_NAME":P_enforce_record_routes(): Unable to add header\n"); if (hdr.s) pkg_free(hdr.s); return CSCF_RETURN_FALSE; } return CSCF_RETURN_TRUE; out_of_memory: return CSCF_RETURN_ERROR; }
/** * 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; }
/* * delete the user data of the current user if exists(the URI is the R-URI of the current OPTIONS msg) * @param msg: current OPTIONS request * @param str1: not used * @param str2: not used */ int LRF_del_user_data(struct sip_msg* msg, char*str1, char*str2){ str user_uri; user_d * d; unsigned int hash; str service; if (msg->first_line.u.request.method.len!=7|| memcmp(msg->first_line.u.request.method.s,"OPTIONS",7)!=0){ LOG(L_WARN,"WARN:"M_NAME":LRF_del_user_data: The method is not an OPTIONS, trying to replace the message\n"); msg = cscf_get_request_from_reply(NULL); if(! msg || msg->first_line.type!=SIP_REQUEST || msg->first_line.u.request.method.len!=7|| memcmp(msg->first_line.u.request.method.s,"OPTIONS",7)!=0){ LOG(L_ERR,"BUG:"M_NAME":LRF_del_user_data: The new message is not an OPTIONS request either\n"); return CSCF_RETURN_ERROR; } } service = cscf_get_headers_content(msg , service_hdr_name); if(!service.len || !service.s){ LOG(L_ERR, "ERR:"M_NAME":LRF_del_user_data: could not find the service header in the OPTIONS, or could not be parsed\n"); return CSCF_RETURN_FALSE; } str callid = cscf_get_call_id(msg, NULL); if(!callid.s || !callid.len){ LOG(L_ERR, "ERR:"M_NAME":LRF_get_psap: could not find the callid header in the OPTIONS request\n"); return CSCF_RETURN_FALSE; } user_uri = msg->first_line.u.request.uri; d = get_user_data(user_uri, service, callid); if(!d) return CSCF_RETURN_FALSE; if(d->loc_subscr) del_loc_subscription((loc_subscription*)d->loc_subscr); hash = d->hash; del_user_data(d); lrf_unlock(hash); 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; }
/* Send a transaction reply * could be used after to send replies to the OPTIONS trans * @param msg - (possible)the OPTIONS request from the ECSCF * @param str1 - reply code * @param str2 - reply reason * @return CSCF_RETURN_TRUE if ok, or CSCF_RETURN_FALSE if error */ int LRF_options_empty_repl(struct sip_msg* msg, char*str1, char*str2){ LOG(L_INFO, "INFO:"M_NAME":LRF_options_empty_repl\n"); if (msg->first_line.u.request.method.len!=7|| memcmp(msg->first_line.u.request.method.s,"OPTIONS",7)!=0){ LOG(L_WARN,"WARN:"M_NAME":LRF_options_empty_repl: The method is not an OPTIONS, trying to replace the message\n"); msg = cscf_get_request_from_reply(NULL); if(! msg || msg->first_line.type!=SIP_REQUEST || msg->first_line.u.request.method.len!=7|| memcmp(msg->first_line.u.request.method.s,"OPTIONS",7)!=0){ LOG(L_ERR,"BUG:"M_NAME":LRF_options_empty_repl: The new message is not an OPTIONS request either\n"); return CSCF_RETURN_ERROR; } } cscf_reply_transactional(msg, 400, str2); return CSCF_RETURN_TRUE; }
//static int check_user_natted(struct sip *msg, struct sip_uri *uri){ int P_SDP_manipulate(struct sip_msg *msg,char *str1,char *str2) { int response = CSCF_RETURN_FALSE ; int method; struct sip_msg *req=0; if (!pcscf_nat_enable || !rtpproxy_enable) return CSCF_RETURN_FALSE; if( check_content_type(msg) ) { if (msg->first_line.type == SIP_REQUEST) method = msg->first_line.u.request.method_value; else { req = cscf_get_request_from_reply(msg); if (req){ method = req->first_line.u.request.method_value; }else method=METHOD_UNDEF; } switch(method) { case METHOD_INVITE: if (msg->first_line.type == SIP_REQUEST){ /* on INVITE */ /* check the sdp if it has a 1918 */ if(1) { /* get rtp_proxy/nathelper to open ports - get a iovec*/ response = force_rtp_proxy2_f(msg,"","") ; LOG(L_CRIT,"DBG:"M_NAME":P_SDP_manipulate: ... rtp proxy done\n"); } else { /* using public ip */ LOG(L_CRIT,"DBG:"M_NAME":P_SDP_manipulate: ... found public network in SDP.\n"); response = CSCF_RETURN_FALSE ; } }else{ if (msg->first_line.u.reply.statuscode == 183 || (msg->first_line.u.reply.statuscode >= 200 && msg->first_line.u.reply.statuscode < 300)) { if(1) { /* sdp_1918(msg) */ /* str1 & str2 must be something */ response = force_rtp_proxy2_f(msg, "", "") ; LOG(L_CRIT,"DBG:"M_NAME":P_SDP_manipulate: ... rtp proxy done\n"); } else { /* public ip found */ response = CSCF_RETURN_FALSE ; } break; } } break ; case METHOD_BYE: case METHOD_CANCEL: LOG(L_CRIT,"DBG:"M_NAME":P_SDP_manipulate: on BYE/CANCEL...\n"); if (msg->first_line.type == SIP_REQUEST){ /* request/response not acceptable */ response = unforce_rtp_proxy_f(msg,-1) ; } break; default: response = CSCF_RETURN_FALSE; break; } } else { LOG(L_ERR, "ERROR:check_content_type: parse error:" "see the content_type block\n"); response = -1 ; } return response ; }
/** * Process the 200 response for REGISTER and creates the first Security-Associations. * The rest of the SA are not set. * could come over that one. * @param rpl - the 200 response * @param str1 - not used * @param str2 - not used * @returns 1 if ok, 0 if not */ int P_security_200(struct sip_msg *rpl,char *str1, char *str2) { struct sip_msg *req; str sec_hdr; r_security_type sec_type; float sec_q; struct hdr_field *h; struct via_body *vb; r_contact *c; r_ipsec *i; int expires; unsigned long s_hash; char out_rpl[256],out_req[256],inc_rpl[256]; if (!pcscf_use_ipsec &&!pcscf_use_tls) goto ret_false; req = cscf_get_request_from_reply(rpl); if (!req){ LOG(L_ERR,"ERR:"M_NAME":P_security_200: No transactional request found.\n"); goto error; } sec_hdr = cscf_get_pref_security_header(req,s_security_client, &sec_type,&sec_q); if (!sec_hdr.len) { LOG(L_DBG,"DBG:"M_NAME":P_security_200: No Security-Verify header found.\n"); goto error; } /* find the expires (reg or dereg?) */ expires = cscf_get_max_expires(req); /* get the IPSec info from the registrar */ vb = cscf_get_first_via(req,&h); LOG(L_DBG,"DBG:"M_NAME":P_security_200: Looking for <%d://%.*s:%d> \n", vb->proto,vb->host.len,vb->host.s,vb->port); c = get_r_contact(vb->host,vb->port,vb->proto); r_act_time(); if (!c){ LOG(L_ERR,"ERR:"M_NAME":P_security_200: Contact not found\n"); goto error; } if (c->security_temp){ if (c->security && c->security->type == SEC_TLS && (c->security->data.tls && c->security->data.tls->port_tls==req->rcv.src_port&& c->security->data.tls->session_hash!=0 && c->security->data.tls->session_hash == tls_get_session_hash(req))){ /* don't replace security when doing an integrity protected REGISTER with * possible attack-garbage from security_temp */ P_security_drop(c,c->security_temp); free_r_security(c->security_temp); c->security_temp = 0; } else { if (c->security) { P_security_drop(c,c->security); free_r_security(c->security); } c->security = c->security_temp; c->security_temp = 0; } } switch(sec_type){ case SEC_NONE: break; case SEC_TLS: if (c->security && pcscf_use_tls) { r_tls *tls; int port_tls = req->rcv.src_port; s_hash = get_tls_session_hash(req); if (!s_hash){ LOG(L_ERR,"ERR:"M_NAME":P_security_200: Session Hash could not be obtained !\n"); r_unlock(c->hash); goto error; } tls = new_r_tls(port_tls, s_hash); if (!tls) goto error; c->security->data.tls = tls; } r_unlock(c->hash); break; case SEC_IPSEC: if (!r_valid_contact(c)||!c->security||!c->security->data.ipsec ){ LOG(L_DBG,"DBG:"M_NAME":P_security_200: Contact expired or no IPSec info\n"); r_unlock(c->hash); goto error; } i = c->security->data.ipsec; /* P_Out_Rpl */ sprintf(out_rpl,"%s %.*s %d %s %d %d %.*s %.*s %.*s %.*s", pcscf_ipsec_P_Out_Rpl, c->host.len,c->host.s, i->port_uc, pcscf_ipsec_host, pcscf_ipsec_port_s, i->spi_uc, i->ealg.len,i->ealg.s, i->ck.len,i->ck.s, i->alg.len,i->alg.s, i->ik.len,i->ik.s ); /* P_Out_Req */ sprintf(out_req,"%s %.*s %d %s %d %d %.*s %.*s %.*s %.*s", pcscf_ipsec_P_Out_Req, c->host.len,c->host.s, i->port_us, pcscf_ipsec_host, pcscf_ipsec_port_c, i->spi_us, i->ealg.len,i->ealg.s, i->ck.len,i->ck.s, i->alg.len,i->alg.s, i->ik.len,i->ik.s ); /* P_Out_Inc_Rpl */ sprintf(inc_rpl,"%s %.*s %d %s %d %d %.*s %.*s %.*s %.*s", pcscf_ipsec_P_Inc_Rpl, c->host.len,c->host.s, i->port_us, pcscf_ipsec_host, pcscf_ipsec_port_c, i->spi_pc, i->ealg.len,i->ealg.s, i->ck.len,i->ck.s, i->alg.len,i->alg.s, i->ik.len,i->ik.s ); if (expires<=0) { /* Deregister */ c->reg_state = DEREGISTERED; r_act_time(); c->expires = time_now + 60; } r_unlock(c->hash); //print_r(L_CRIT); /* run the IPSec scripts */ /* Registration */ execute_cmd(out_rpl); execute_cmd(out_req); execute_cmd(inc_rpl); break; } return CSCF_RETURN_TRUE; ret_false: return CSCF_RETURN_FALSE; error: return CSCF_RETURN_ERROR; }
/** * Process the 401 response for REGISTER and creates the first Security-Associations. * IPSEc: Only the SA for P_Inc_Req - Incoming Requests is set now as the next REGISTER * could come over that one. * @param rpl - the 401 response * @param str1 - not used * @param str2 - not used * @returns 1 if ok, 0 if not */ int P_security_401(struct sip_msg *rpl,char *str1, char *str2) { struct sip_msg *req; struct hdr_field *hdr; str sec_hdr,sec_srv={0,0}; r_security_type sec_type; char cmd[256]; r_contact *c; r_ipsec *ipsec; float sec_q=-1; str auth; if (!pcscf_use_ipsec &&!pcscf_use_tls) goto ret_false; req = cscf_get_request_from_reply(rpl); if (!req){ LOG(L_ERR,"ERR:"M_NAME":P_security_401: No transactional request found.\n"); goto error; } auth = cscf_get_authenticate(rpl,&hdr); if (!auth.len){ LOG(L_ERR,"ERR:"M_NAME":P_security_401: No WWW-Authenticate header found.\n"); goto ret_false; } sec_hdr = cscf_get_pref_security_header(req,s_security_client, &sec_type,&sec_q); if (!sec_hdr.len) { LOG(L_DBG,"DBG:"M_NAME":P_security_401: No Security-Client header found.\n"); goto ret_false; } LOG(L_INFO,"DBG:"M_NAME":P_security_401: Security-Client header found : <%.*s>.\n", sec_hdr.len, sec_hdr.s); /* save data into registrar */ c = save_contact_security(req, auth, sec_hdr, sec_type, sec_q); if (!c) goto error; switch(sec_type){ case SEC_NONE: break; case SEC_TLS: /* try to add the Security-Server header */ sec_srv.len = s_security_server_s.len+sec_hdr.len+s_security_server_e.len; sec_srv.s = pkg_malloc(sec_srv.len); if (!sec_srv.s){ LOG(L_ERR,"ERR:"M_NAME":P_security_401: Error allocating %d pkg bytes \n",sec_srv.len); goto error; } sec_srv.len=0; STR_APPEND(sec_srv,s_security_server_s); STR_APPEND(sec_srv,sec_hdr); STR_APPEND(sec_srv,s_security_server_e); if (!cscf_add_header(rpl,&sec_srv,HDR_OTHER_T)) { LOG(L_ERR,"ERR:"M_NAME":P_security_401: Error adding header <%.*s> \n",sec_srv.len,sec_srv.s); pkg_free(sec_srv.s); goto error; } break; case SEC_IPSEC: ipsec = c->security_temp->data.ipsec; /* try to add the Security-Server header */ sprintf(cmd,"Security-Server: ipsec-3gpp; ealg=%.*s; alg=%.*s; spi-c=%d; spi-s=%d; port-c=%d; port-s=%d; q=0.1\r\n", ipsec->r_ealg.len,ipsec->r_ealg.s, ipsec->r_alg.len,ipsec->r_alg.s, ipsec->spi_pc,ipsec->spi_ps, pcscf_ipsec_port_c,pcscf_ipsec_port_s); sec_srv.len = strlen(cmd); sec_srv.s = pkg_malloc(sec_srv.len); if (!sec_srv.s){ LOG(L_ERR,"ERR:"M_NAME":P_security_401: Error allocating %d pkg bytes \n",sec_srv.len); goto error; } memcpy(sec_srv.s,cmd,sec_srv.len); if (!cscf_add_header(rpl,&sec_srv,HDR_OTHER_T)) { LOG(L_ERR,"ERR:"M_NAME":P_security_401: Error adding header <%.*s> \n",sec_srv.len,sec_srv.s); pkg_free(sec_srv.s); goto error; } /* run the IPSec script */ /* P_Inc_Req */ sprintf(cmd,"%s %.*s %d %s %d %d %.*s %.*s %.*s %.*s", pcscf_ipsec_P_Inc_Req, c->host.len,c->host.s, ipsec->port_uc, pcscf_ipsec_host, pcscf_ipsec_port_s, ipsec->spi_ps, ipsec->ealg.len,ipsec->ealg.s, ipsec->ck.len,ipsec->ck.s, ipsec->alg.len,ipsec->alg.s, ipsec->ik.len,ipsec->ik.s); r_unlock(c->hash); execute_cmd(cmd); break; } return CSCF_RETURN_TRUE; ret_false: return CSCF_RETURN_FALSE; error: return CSCF_RETURN_ERROR; }
/** * 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; }
/** * Process the 200 response for REGISTER and creates the first Security-Associations. * The rest of the SA are not set. * could come over that one. * @param rpl - the 200 response * @param str1 - not used * @param str2 - not used * @returns 1 if ok, 0 if not */ int P_IPSec_200(struct sip_msg *rpl,char *str1, char *str2) { struct sip_msg *req; struct hdr_field *hdr; str sec_cli; struct hdr_field *h; struct via_body *vb; r_contact *c; r_ipsec *i; int expires; char out_rpl[256],out_req[256],inc_rpl[256]; req = cscf_get_request_from_reply(rpl); if (!req){ LOG(L_ERR,"ERR:"M_NAME":P_ipsec_200: No transactional request found.\n"); goto error; } /* just to jump out if no IPSec is employed - the info is already saved */ sec_cli = cscf_get_security_client(req,&hdr); if (!sec_cli.len){ LOG(L_DBG,"DBG:"M_NAME":P_ipsec_200: No Security-Client header found.\n"); goto error; } /* find the expires (reg or dereg?) */ expires = cscf_get_max_expires(req); /* get the IPSec info from the registrar */ vb = cscf_get_first_via(req,&h); // if (!h||!h->parsed){ // LOG(L_ERR,"ERR:"M_NAME":P_ipsec_200: Error extracting sender's id.\n"); // goto error; // } // vb = (struct via_body*) h->parsed; LOG(L_DBG,"DBG:"M_NAME":P_ipsec_200: Looking for <%d://%.*s:%d> \n", vb->proto,vb->host.len,vb->host.s,vb->port); c = get_r_contact(vb->host,vb->port,vb->proto); r_act_time(); if (!c){ LOG(L_ERR,"ERR:"M_NAME":P_ipsec_200: Contact not found\n"); goto error; } if (!r_valid_contact(c)||!c->ipsec){ LOG(L_DBG,"DBG:"M_NAME":P_ipsec_200: Contact expired or no IPSec info\n"); r_unlock(c->hash); goto error; } i = c->ipsec; /* P_Out_Rpl */ sprintf(out_rpl,"%s %.*s %d %s %d %d %.*s %.*s %.*s %.*s", pcscf_ipsec_P_Out_Rpl, c->host.len,c->host.s, i->port_uc, pcscf_ipsec_host, pcscf_ipsec_port_s, i->spi_uc, i->ealg.len,i->ealg.s, i->ck.len,i->ck.s, i->alg.len,i->alg.s, i->ik.len,i->ik.s ); /* P_Out_Req */ sprintf(out_req,"%s %.*s %d %s %d %d %.*s %.*s %.*s %.*s", pcscf_ipsec_P_Out_Req, c->host.len,c->host.s, i->port_us, pcscf_ipsec_host, pcscf_ipsec_port_c, i->spi_us, i->ealg.len,i->ealg.s, i->ck.len,i->ck.s, i->alg.len,i->alg.s, i->ik.len,i->ik.s ); /* P_Out_Inc_Rpl */ sprintf(inc_rpl,"%s %.*s %d %s %d %d %.*s %.*s %.*s %.*s", pcscf_ipsec_P_Inc_Rpl, c->host.len,c->host.s, i->port_us, pcscf_ipsec_host, pcscf_ipsec_port_c, i->spi_pc, i->ealg.len,i->ealg.s, i->ck.len,i->ck.s, i->alg.len,i->alg.s, i->ik.len,i->ik.s ); if (expires<=0) { /* Deregister */ c->reg_state = DEREGISTERED; r_act_time(); c->expires = time_now + 60; } r_unlock(c->hash); //print_r(L_CRIT); /* run the IPSec scripts */ /* Registration */ execute_cmd(out_rpl); execute_cmd(out_req); execute_cmd(inc_rpl); return 1; error: return 0; }
/** * Process the 401 response for REGISTER and creates the first Security-Associations. * Only the SA for P_Inc_Req - Incoming Requests is set now as the next REGISTER * could come over that one. * @param rpl - the 401 response * @param str1 - not used * @param str2 - not used * @returns 1 if ok, 0 if not */ int P_IPSec_401(struct sip_msg *rpl,char *str1, char *str2) { struct sip_msg *req; str auth; struct hdr_field *hdr; str sec_cli,sec_srv={0,0}; str ck,ik; char ck_c[64],ik_c[64]; str ck_esp={ck_c,0},ik_esp={ik_c,0}; str alg,ealg; str alg_setkey,ealg_setkey; str tmp; unsigned int spi_uc,spi_us; unsigned int spi_pc,spi_ps; int port_uc,port_us; char cmd[256]; str ue; contact_body_t *contact; struct sip_uri uri; req = cscf_get_request_from_reply(rpl); if (!req){ LOG(L_ERR,"ERR:"M_NAME":P_ipsec_401: No transactional request found.\n"); goto error; } auth = cscf_get_authenticate(rpl,&hdr); if (!auth.len){ LOG(L_ERR,"ERR:"M_NAME":P_ipsec_401: No authorization header found.\n"); goto error; } sec_cli = cscf_get_security_client(req,&hdr); if (!sec_cli.len){ LOG(L_DBG,"DBG:"M_NAME":P_ipsec_401: No Security-Client header found.\n"); goto error; } /* first we look for CK, IK */ get_qparam(auth,s_ck,ck); LOG(L_DBG,"DBG:"M_NAME":P_ipsec_401: CK: <%.*s>\n", ck.len,ck.s); get_qparam(auth,s_ik,ik); LOG(L_DBG,"DBG:"M_NAME":P_ipsec_401: IK: <%.*s>\n", ik.len,ik.s); /* then for algorithms */ get_param(sec_cli,s_ealg,ealg); LOG(L_DBG,"DBG:"M_NAME":P_ipsec_401: Enc Algorithm: <%.*s>\n", ealg.len,ealg.s); get_param(sec_cli,s_alg,alg); LOG(L_DBG,"DBG:"M_NAME":P_ipsec_401: Int Algorithm: <%.*s>\n", alg.len,alg.s); /* and for spis */ get_param(sec_cli,s_spi_c,tmp); strtoint(tmp,spi_uc); LOG(L_DBG,"DBG:"M_NAME":P_ipsec_401: SPI-C: %d\n", spi_uc); get_param(sec_cli,s_spi_s,tmp); strtoint(tmp,spi_us); LOG(L_DBG,"DBG:"M_NAME":P_ipsec_401: SPI-S: %d\n", spi_us); /* and for ports */ get_param(sec_cli,s_port_c,tmp); strtoint(tmp,port_uc); LOG(L_DBG,"DBG:"M_NAME":P_ipsec_401: Port-C: %d\n", port_uc); get_param(sec_cli,s_port_s,tmp); strtoint(tmp,port_us); LOG(L_DBG,"DBG:"M_NAME":P_ipsec_401: Port-S: %d\n", port_us); contact = cscf_parse_contacts(req); if (!contact) { LOG(L_ERR,"ERR:"M_NAME":P_ipsec_401: Message contains no Contact!\n"); goto error; } if (contact->contacts){ ue = contact->contacts->uri; if (parse_uri(ue.s,ue.len,&uri)){ LOG(L_ERR,"ERR:"M_NAME":P_ipsec_401: Error parsing uri <%.*s>\n",ue.len,ue.s); goto error; } ue = uri.host; if (uri.port_no==0) port_uc=5060; }else ue = req->via1->host; LOG(L_DBG,"DBG:"M_NAME":P_ipsec_401: UE IP: <%.*s> \n",ue.len,ue.s); spi_pc=get_next_spi(); spi_ps=get_next_spi(); ck_esp.s[ck_esp.len++]='0'; ck_esp.s[ck_esp.len++]='x'; if (ealg.len == s_des_in.len && strncasecmp(ealg.s,s_des_in.s,ealg.len)==0) { memcpy(ck_esp.s+ck_esp.len,ck.s,32);ck_esp.len+=32; memcpy(ck_esp.s+ck_esp.len,ck.s,16);ck_esp.len+=16; ealg_setkey = s_des_out; } else if (ealg.len == s_aes_in.len && strncasecmp(ealg.s,s_aes_in.s,ealg.len)==0) { memcpy(ck_esp.s+ck_esp.len,ck.s,ck.len);ck_esp.len+=ck.len; ealg_setkey = s_aes_out; }else { memcpy(ck_esp.s+ck_esp.len,ck.s,ck.len);ck_esp.len+=ck.len; ealg_setkey = s_null_out; ealg = s_null_out; } ik_esp.s[ik_esp.len++]='0'; ik_esp.s[ik_esp.len++]='x'; if (alg.len == s_md5_in.len && strncasecmp(alg.s,s_md5_in.s,alg.len)==0) { memcpy(ik_esp.s+ik_esp.len,ik.s,ik.len);ik_esp.len+=ik.len; alg_setkey = s_md5_out; } else if (alg.len == s_sha_in.len && strncasecmp(alg.s,s_sha_in.s,alg.len)==0) { memcpy(ik_esp.s+ik_esp.len,ik.s,ik.len);ik_esp.len+=ik.len; memcpy(ik_esp.s+ik_esp.len,"00000000",8);ik_esp.len+=8; alg_setkey = s_sha_out; }else{ LOG(L_ERR,"ERR:"M_NAME":P_ipsec_401: Unknown Integrity algorithm <%.*s>\n",alg.len,alg.s); goto error; } /* try to add the Security-Server header */ sprintf(cmd,"Security-Server: ipsec-3gpp; ealg=%.*s; alg=%.*s; spi-c=%d; spi-s=%d; port-c=%d; port-s=%d; q=0.1\r\n", ealg.len,ealg.s, alg.len,alg.s, spi_pc,spi_ps, pcscf_ipsec_port_c,pcscf_ipsec_port_s); sec_srv.len = strlen(cmd); sec_srv.s = pkg_malloc(sec_srv.len); if (!sec_srv.s){ LOG(L_ERR,"ERR:"M_NAME":P_ipsec_401: Error allocating %d pkg bytes \n",sec_srv.len); goto error; } memcpy(sec_srv.s,cmd,sec_srv.len); if (!cscf_add_header(rpl,&sec_srv,HDR_OTHER_T)) { LOG(L_ERR,"ERR:"M_NAME":P_ipsec_401: Error adding header <%.*s> \n",sec_srv.len,sec_srv.s); pkg_free(sec_srv.s); goto error; } /* run the IPSec script */ /* P_Inc_Req */ sprintf(cmd,"%s %.*s %d %s %d %d %.*s %.*s %.*s %.*s", pcscf_ipsec_P_Inc_Req, ue.len,ue.s, port_uc, pcscf_ipsec_host, pcscf_ipsec_port_s, spi_ps, ealg_setkey.len,ealg_setkey.s, ck_esp.len,ck_esp.s, alg_setkey.len,alg_setkey.s, ik_esp.len,ik_esp.s); execute_cmd(cmd); /* save data into registrar */ save_contact_ipsec(req,rpl, spi_uc,spi_us,spi_pc,spi_ps,port_uc,port_us, ealg_setkey,ck_esp,alg_setkey,ik_esp); return 1; error: 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; 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; }
/** * Checks if a response on the MT side includes all the required Record Routes, as present in the request * @param msg - the SIP reply to check * @param str1 - not used * @param str2 - not used * @return #CSCF_RETURN_TRUE on match, #CSCF_RETURN_FALSE if not matching */ int P_follows_record_routes(struct sip_msg *msg, char *str1, char *str2) { struct hdr_field *temp_hdr_req = NULL,*temp_hdr_rpl = NULL; struct sip_msg * req = NULL; rr_t * rr_req = NULL, *rr_req_i = NULL; rr_t * rr_rpl = NULL; LOG(L_INFO,"INF:"M_NAME":P_follows_record_routes(): Checking if RR in %d reply match the request ones\n", msg->first_line.u.reply.statuscode); // Get the first RR in reply temp_hdr_rpl = cscf_get_next_record_route(msg,temp_hdr_rpl); if (!temp_hdr_rpl || !(rr_rpl = (rr_t*)temp_hdr_rpl->parsed)){ LOG(L_ERR,"ERR:"M_NAME":P_follows_record_routes(): No RR's found in reply.\n"); goto nok; } // Checking first RR - this is a special case because the saved request does not include it if (pcscf_record_route_mt_uri.len != rr_rpl->nameaddr.uri.len || strncmp(pcscf_record_route_mt_uri.s,rr_rpl->nameaddr.uri.s,pcscf_record_route_mt_uri.len)!=0){ LOG(L_ERR,"ERR:"M_NAME":P_follows_record_routes(): First RR did not match P-CSCF MT = %.*s , Rpl = %.*s \n", pcscf_record_route_mt_uri.len,pcscf_record_route_mt_uri.s,rr_rpl->nameaddr.uri.len,rr_rpl->nameaddr.uri.s); goto nok; } rr_rpl = rr_rpl->next; if (!rr_rpl){ temp_hdr_rpl = cscf_get_next_record_route(msg,temp_hdr_rpl); if (temp_hdr_rpl) rr_rpl = (rr_t*)temp_hdr_rpl->parsed; } // Find the request if(!(req = cscf_get_request_from_reply(msg))) { LOG(L_ERR,"ERR:"M_NAME":P_follows_record_routes(): No transactional request found.\n"); goto nok; } temp_hdr_req = cscf_get_next_record_route(req,temp_hdr_req); if (temp_hdr_req) { rr_req = (rr_t*)temp_hdr_req->parsed; rr_req_i = rr_req; } // Cycle through the request/reply headers and compare while(rr_rpl && rr_req_i) { if(rr_req_i->nameaddr.uri.len != rr_rpl->nameaddr.uri.len || strncmp(rr_req_i->nameaddr.uri.s,rr_rpl->nameaddr.uri.s,rr_req_i->nameaddr.uri.len)!=0){ LOG(L_ERR,"ERR:"M_NAME":P_follows_record_routes(): Mismatch in RR URIs Req = %.*s , Rpl = %.*s \n", rr_req_i->nameaddr.uri.len,rr_req_i->nameaddr.uri.s,rr_rpl->nameaddr.uri.len,rr_rpl->nameaddr.uri.s); goto nok; } rr_rpl = rr_rpl->next; if (!rr_rpl){ temp_hdr_rpl = cscf_get_next_record_route(msg,temp_hdr_rpl); if (temp_hdr_rpl) rr_rpl = (rr_t*)temp_hdr_rpl->parsed; } rr_req_i = rr_req_i->next; if (!rr_req_i){ if (rr_req){ free_rr(&rr_req); temp_hdr_req->parsed = NULL; } temp_hdr_req = cscf_get_next_record_route(req,temp_hdr_req); if (temp_hdr_req) { rr_req = (rr_t*)temp_hdr_req->parsed; rr_req_i = rr_req; } } } if(rr_rpl){ LOG(L_ERR,"ERR:"M_NAME":P_follows_record_routes(): More RR headers in reply than in request\n"); goto nok; } if(rr_req){ LOG(L_ERR,"ERR:"M_NAME":P_follows_record_routes(): More RR headers in request than in reply\n"); goto nok; } if (rr_req){ free_rr(&rr_req); temp_hdr_req->parsed = NULL; } return CSCF_RETURN_TRUE; nok: if (rr_req){ free_rr(&rr_req); temp_hdr_req->parsed = NULL; } 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; 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; }
/* Send the OPTIONS response to the E-CSCF * could be used after a LOCSIP NOTIFY is received * @param msg - the OPTIONS request from the ECSCF * @param str1 - not used * @param str2 - not used * @return CSCF_RETURN_TRUE if ok, or CSCF_RETURN_FALSE if error */ int LRF_call_query_resp(struct sip_msg* msg, char*str1, char*str2){ str user_uri; user_d * d= NULL; str service; struct cell * trans = NULL; str resp_body = {0,0}; str headers = {0,0}; unsigned int hash_index, label; LOG(L_INFO, "INFO:"M_NAME":LRF_call_query_resp\n"); if (msg->first_line.u.request.method.len!=7|| memcmp(msg->first_line.u.request.method.s,"OPTIONS",7)!=0){ LOG(L_WARN,"WARN:"M_NAME":LRF_call_query_resp: The method is not an OPTIONS, trying to replace the message\n"); msg = cscf_get_request_from_reply(NULL); if(! msg || msg->first_line.type!=SIP_REQUEST || msg->first_line.u.request.method.len!=7|| memcmp(msg->first_line.u.request.method.s,"OPTIONS",7)!=0){ LOG(L_ERR,"BUG:"M_NAME":LRF_call_query_resp: The new message is not an OPTIONS request either\n"); return CSCF_RETURN_ERROR; } } service = cscf_get_headers_content(msg, service_hdr_name); if(!service.len || !service.s){ LOG(L_ERR, "ERR:"M_NAME":LRF_call_query_resp: could not find the service header in the OPTIONS, or could not be parsed\n"); return CSCF_RETURN_FALSE; } str callid = cscf_get_call_id(msg, NULL); if(!callid.s || !callid.len){ LOG(L_ERR, "ERR:"M_NAME":LRF_get_psap: could not find the callid header in the OPTIONS request\n"); return CSCF_RETURN_FALSE; } user_uri = msg->first_line.u.request.uri; d = get_user_data(user_uri, service, callid); if(!d) { LOG(L_ERR, "ERR:"M_NAME":LRF_call_query_resp: could not found user data for uri %.*s and service %.*s\n", user_uri.len, user_uri.s, service.len, service.s); goto error; } if(!d->psap_uri.len || !d->psap_uri.s){ LOG(L_ERR, "ERR: "M_NAME":LRF_call_query_resp: null psap uri\n"); goto error; } if(d->loc && d->locsip_loc){ LOG(L_DBG, "DBG: "M_NAME":LRF_call_query_resp: LRF has location information from the LOCSIP server\n"); if(get_options_resp_body(&resp_body, d)){ LOG(L_ERR, "ERR:"M_NAME":LRF_call_query_resp:could not get the OPTIONS response body\n"); goto error; } } if(get_options_resp_headers(&headers, d)){ LOG(L_ERR, "ERR:"M_NAME":LRF_call_query_resp:could not get the OPTIONS response headers\n"); goto error; } //get crt trans if(cscf_get_transaction(msg, &hash_index, &label)<0){ LOG(L_ERR, "ERR:"M_NAME":LRF_call_query_resp: could not get the trans from the message\n"); goto error; } if(tmb.t_lookup_ident(&trans, hash_index, label)!=1){ LOG(L_ERR, "ERR:"M_NAME":LRF_call_query_resp: could not get the trans from the hash and index\n"); goto error; } if(tmb.t_reply_with_body(trans, 200, "OK - PSAP found", resp_body.s, headers.s, "lrf" )!= 1){ LOG(L_ERR, "ERR:"M_NAME":LRF_call_query_resp: could not send the response\n"); goto error2; } #ifndef TM_DEL_UNREF LOG(L_ERR, "ERR:"M_NAME":LRF_call_query_resp: options ref count %i\n", trans->ref_count); #endif lrf_unlock(d->hash); if(resp_body.s) pkg_free(resp_body.s); if(headers.s) pkg_free(headers.s); return CSCF_RETURN_TRUE; error2: //tmb.t_unref_ident(trans->hash_index, trans->label); error: LOG(L_DBG, "DBG: "M_NAME":LRF_call_query_resp: error label\n"); if(d) lrf_unlock(d->hash); LOG(L_DBG, "DBG: "M_NAME":LRF_call_query_resp: error label\n"); //if(resp_body.s) pkg_free(resp_body.s); LOG(L_DBG, "DBG: "M_NAME":LRF_call_query_resp: error label\n"); //if(headers.s) pkg_free(headers.s); LOG(L_DBG, "DBG: "M_NAME":LRF_call_query_resp: error label\n"); return CSCF_RETURN_FALSE; }