/** * 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; }
/** * 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 from AS, #ISC_RETURN_FALSE if not, #ISC_RETURN_BREAK on error */ int ISC_from_AS(struct sip_msg *msg,char *str1,char *str2) { int ret = ISC_RETURN_FALSE; isc_mark old_mark; enum dialog_direction dir = get_dialog_direction(str1); if (dir==DLG_MOBILE_UNKNOWN) return ISC_RETURN_BREAK; if (!isc_is_initial_request(msg)) return ISC_RETURN_FALSE; /* starting or resuming? */ if (isc_mark_get_from_msg(msg,&old_mark)){ LOG(L_INFO,"INFO:"M_NAME":ISC_from_AS(%s): Message returned s=%d;h=%d;d=%d\n", str1,old_mark.skip,old_mark.handling,old_mark.direction); if (old_mark.direction==IFC_ORIGINATING_SESSION && dir!=DLG_MOBILE_ORIGINATING) ret = ISC_RETURN_FALSE; else if ((old_mark.direction==IFC_TERMINATING_SESSION||old_mark.direction==IFC_TERMINATING_UNREGISTERED) && dir!=DLG_MOBILE_TERMINATING) ret = ISC_RETURN_FALSE; else ret = ISC_RETURN_TRUE; } else { ret = ISC_RETURN_FALSE; } return ret; }
/** * Check if we already did record-route * @param msg - the SIP message to add to * @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_is_record_routed(struct sip_msg *msg,char *str1,char *str2) { str rr; str u = {0,0},scheme={0,0},scscf={0,0}; struct hdr_field *hdr=0; rr_t *rr_s; enum s_dialog_direction dir = get_dialog_direction(str1); switch (dir){ case DLG_MOBILE_ORIGINATING: STR_PKG_DUP(rr,scscf_record_route_mo,"pkg"); break; case DLG_MOBILE_TERMINATING: STR_PKG_DUP(rr,scscf_record_route_mt,"pkg"); break; default: u.s = str1; u.len = strlen(str1); if (scscf_name_str.len>4 && strncasecmp(scscf_name_str.s,"sip:",4)==0){ scheme.s = scscf_name_str.s; scheme.len = 4; }else if (scscf_name_str.len>5 && strncasecmp(scscf_name_str.s,"sips:",5)==0){ scheme.s = scscf_name_str.s; scheme.len = 4; } scscf.s = scheme.s+scheme.len; scscf.len = scscf_name_str.len - scheme.len; rr.len = scheme.len+u.len+1+scscf.len; rr.s = pkg_malloc(rr.len); if (!rr.s){ LOG(L_ERR,"ERR:"M_NAME":S_record_route: error allocating %d bytes!\n",rr.len); return CSCF_RETURN_BREAK; } rr.len = 0; STR_APPEND(rr,scheme); STR_APPEND(rr,u); rr.s[rr.len++]='@'; STR_APPEND(rr,scscf); } for(hdr = cscf_get_next_record_route(msg,(struct hdr_field*) 0); hdr ; hdr = cscf_get_next_record_route(msg,hdr)){ for (rr_s = (rr_t *)hdr->parsed;rr_s; rr_s = rr_s->next) if (rr_s->nameaddr.uri.len == rr.len && strncasecmp(rr_s->nameaddr.uri.s,rr.s,rr.len)==0){ pkg_free(rr.s); return CSCF_RETURN_TRUE; } } pkg_free(rr.s); return CSCF_RETURN_FALSE; out_of_memory: return CSCF_RETURN_ERROR; }
/** * 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 from AS, #ISC_RETURN_FALSE if not, #ISC_RETURN_BREAK on error */ int isc_from_as(struct sip_msg *msg, char *str1, char *str2) { int ret = ISC_RETURN_FALSE; isc_mark old_mark; str s = {0, 0}; //sometimes s is populated by an ims_getter method cscf_get_terminating_user that alloc memory that must be free-ed at the end int free_s = 0; enum dialog_direction dir = get_dialog_direction(str1); if (dir == DLG_MOBILE_UNKNOWN) return ISC_RETURN_BREAK; if (!cscf_is_initial_request(msg)) return ISC_RETURN_FALSE; /* starting or resuming? */ if (isc_mark_get_from_msg(msg, &old_mark)) { LM_DBG("Message returned s=%d;h=%d;d=%d\n", old_mark.skip, old_mark.handling, old_mark.direction); /*according to spec 24.229, 5.4.3.3 if the URI is different then the RURI is retargeted and we can do one of 2 things, * a) mark as originating session and forward according to normal RURI procedures, * b) run IFC criteria on new retrageturi and route accordingly. * * We will therefore leave it in the hands of the config writer to decide. We will make them aware here that retargeting has happened * by retirning a special error code ISC_RETURN_RETARGET(-2). */ if (dir == DLG_MOBILE_TERMINATING) { cscf_get_terminating_user(msg, &s); //sometimes s is populated by an ims_getter method cscf_get_terminating_user that alloc memory that must be free-ed at the end free_s = 1; if (memcmp(old_mark.aor.s, s.s, s.len) != 0) { LM_DBG("This is a new call....... RURI has been retargeted\n"); return ISC_RETURN_RETARGET; } } if (old_mark.direction == IFC_ORIGINATING_SESSION && dir != DLG_MOBILE_ORIGINATING) ret = ISC_RETURN_FALSE; else if ((old_mark.direction == IFC_TERMINATING_SESSION || old_mark.direction == IFC_TERMINATING_UNREGISTERED) && dir != DLG_MOBILE_TERMINATING) ret = ISC_RETURN_FALSE; else ret = ISC_RETURN_TRUE; } else { ret = ISC_RETURN_FALSE; } if (old_mark.aor.s) pkg_free(old_mark.aor.s); if (s.s && free_s == 1) shm_free(s.s); // shm_malloc in cscf_get_terminating_user return ret; }
/** * Record routes, with given user as parameter. * @param msg - the SIP message to add to * @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_record_route(struct sip_msg *msg,char *str1,char *str2) { str rr={0,0}; str u = {0,0},scheme={0,0},scscf={0,0}; enum s_dialog_direction dir = get_dialog_direction(str1); switch (dir){ case DLG_MOBILE_ORIGINATING: STR_PKG_DUP(rr,scscf_record_route_mo,"pkg"); break; case DLG_MOBILE_TERMINATING: STR_PKG_DUP(rr,scscf_record_route_mt,"pkg"); break; default: u.s = str1; u.len = strlen(str1); if (scscf_name_str.len>4 && strncasecmp(scscf_name_str.s,"sip:",4)==0){ scheme.s = scscf_name_str.s; scheme.len = 4; }else if (scscf_name_str.len>5 && strncasecmp(scscf_name_str.s,"sips:",5)==0){ scheme.s = scscf_name_str.s; scheme.len = 4; } scscf.s = scheme.s+scheme.len; scscf.len = scscf_name_str.len - scheme.len; rr.len = s_record_route_s.len+scheme.len+u.len+1+scscf.len+s_record_route_e.len; rr.s = pkg_malloc(rr.len); if (!rr.s){ LOG(L_ERR,"ERR:"M_NAME":S_record_route: error allocating %d bytes!\n",rr.len); return CSCF_RETURN_BREAK; } rr.len = 0; STR_APPEND(rr,s_record_route_s); STR_APPEND(rr,scheme); STR_APPEND(rr,u); rr.s[rr.len++]='@'; STR_APPEND(rr,scscf); STR_APPEND(rr,s_record_route_e); } if (cscf_add_header_first(msg,&rr,HDR_RECORDROUTE_T)) return CSCF_RETURN_TRUE; else{ if (rr.s) pkg_free(rr.s); return CSCF_RETURN_BREAK; } out_of_memory: return CSCF_RETURN_BREAK; }
/** * 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; }
/** * 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; }
/** * 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 S_is_in_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; enum s_dialog_direction dir = get_dialog_direction(str1); enum s_dialog_direction dirmsg = find_dialog_route_dir(msg); // LOG(L_CRIT,"%d - %d\n",dir,dirmsg); if (dir!=dirmsg) return CSCF_RETURN_FALSE; // print_s_dialogs(L_ERR); call_id = cscf_get_call_id(msg,0); if (!call_id.len){ return CSCF_RETURN_FALSE; } if (is_s_dialog_dir(call_id,dir)) return CSCF_RETURN_TRUE; else return CSCF_RETURN_FALSE; }
/** * 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; }
/* Wrapper to send AAR from config file - this only allows for AAR for calls - not register, which uses r_rx_aar_register * return: 1 - success, <=0 failure. 2 - message not a AAR generating message (ie proceed without PCC if you wish) */ static int w_rx_aar(struct sip_msg *msg, char *route, char* str1, char* bar) { int ret = CSCF_RETURN_ERROR; struct cell *t; AAASession* auth_session; rx_authsessiondata_t* rx_authdata_p = 0; str *rx_session_id; str callid = {0, 0}; str ftag = {0, 0}; str ttag = {0, 0}; str route_name; cfg_action_t* cfg_action = 0; saved_transaction_t* saved_t_data = 0; //data specific to each contact's AAR async call char* direction = str1; if (fixup_get_svalue(msg, (gparam_t*) route, &route_name) != 0) { LM_ERR("no async route block for assign_server_unreg\n"); return -1; } LM_DBG("Looking for route block [%.*s]\n", route_name.len, route_name.s); int ri = route_get(&main_rt, route_name.s); if (ri < 0) { LM_ERR("unable to find route block [%.*s]\n", route_name.len, route_name.s); return -1; } cfg_action = main_rt.rlist[ri]; if (cfg_action == NULL) { LM_ERR("empty action lists in route block [%.*s]\n", route_name.len, route_name.s); return -1; } LM_DBG("Rx AAR called\n"); //create the default return code AVP create_return_code(ret); //We don't ever do AAR on request for calling scenario... if (msg->first_line.type != SIP_REPLY) { LM_DBG("Can't do AAR for call session in request\n"); return CSCF_RETURN_ERROR; } //is it appropriate to send AAR at this stage? t = tmb.t_gett(); if (t == NULL || t == T_UNDEFINED) { LM_WARN("Cannot get transaction for AAR based on SIP Request\n"); //goto aarna; return CSCF_RETURN_ERROR; } //we dont apply QoS if its not a reply to an INVITE! or UPDATE or PRACK! if ((t->method.len == 5 && memcmp(t->method.s, "PRACK", 5) == 0) || (t->method.len == 6 && (memcmp(t->method.s, "INVITE", 6) == 0 || memcmp(t->method.s, "UPDATE", 6) == 0))) { if (cscf_get_content_length(msg) == 0 || cscf_get_content_length(t->uas.request) == 0) { LM_DBG("No SDP offer answer -> therefore we can not do Rx AAR"); //goto aarna; //AAR na if we dont have offer/answer pair return CSCF_RETURN_ERROR; } } else { LM_DBG("Message is not response to INVITE, PRACK or UPDATE -> therefore we do not Rx AAR"); return CSCF_RETURN_ERROR; } /* get callid, from and to tags to be able to identify dialog */ callid = cscf_get_call_id(msg, 0); if (callid.len <= 0 || !callid.s) { LM_ERR("unable to get callid\n"); return CSCF_RETURN_ERROR; } if (!cscf_get_from_tag(msg, &ftag)) { LM_ERR("Unable to get ftag\n"); return CSCF_RETURN_ERROR; } if (!cscf_get_to_tag(msg, &ttag)) { LM_ERR("Unable to get ttag\n"); return CSCF_RETURN_ERROR; } //check to see that this is not a result of a retransmission in reply route only if (msg->cseq == NULL && ((parse_headers(msg, HDR_CSEQ_F, 0) == -1) || (msg->cseq == NULL))) { LM_ERR("No Cseq header found - aborting\n"); return CSCF_RETURN_ERROR; } saved_t_data = (saved_transaction_t*) shm_malloc(sizeof (saved_transaction_t)); if (!saved_t_data) { LM_ERR("Unable to allocate memory for transaction data, trying to send AAR\n"); return CSCF_RETURN_ERROR; } memset(saved_t_data, 0, sizeof (saved_transaction_t)); saved_t_data->act = cfg_action; //OTHER parms need after async response set here //store call id saved_t_data->callid.s = (char*) shm_malloc(callid.len + 1); if (!saved_t_data->callid.s) { LM_ERR("no more memory trying to save transaction state : callid\n"); shm_free(saved_t_data); return CSCF_RETURN_ERROR; } memset(saved_t_data->callid.s, 0, callid.len + 1); memcpy(saved_t_data->callid.s, callid.s, callid.len); saved_t_data->callid.len = callid.len; //store ttag saved_t_data->ttag.s = (char*) shm_malloc(ttag.len + 1); if (!saved_t_data->ttag.s) { LM_ERR("no more memory trying to save transaction state : ttag\n"); shm_free(saved_t_data); return CSCF_RETURN_ERROR; } memset(saved_t_data->ttag.s, 0, ttag.len + 1); memcpy(saved_t_data->ttag.s, ttag.s, ttag.len); saved_t_data->ttag.len = ttag.len; //store ftag saved_t_data->ftag.s = (char*) shm_malloc(ftag.len + 1); if (!saved_t_data->ftag.s) { LM_ERR("no more memory trying to save transaction state : ftag\n"); shm_free(saved_t_data); return CSCF_RETURN_ERROR; } memset(saved_t_data->ftag.s, 0, ftag.len + 1); memcpy(saved_t_data->ftag.s, ftag.s, ftag.len); saved_t_data->ftag.len = ftag.len; //store branch int branch; if (tmb.t_check( msg , &branch )==-1){ LOG(L_ERR, "ERROR: t_suspend: failed find UAC branch\n"); return CSCF_RETURN_ERROR; } //Check that we dont already have an auth session for this specific dialog //if not we create a new one and attach it to the dialog (via session ID). enum dialog_direction dlg_direction = get_dialog_direction(direction); if (dlg_direction == DLG_MOBILE_ORIGINATING) { rx_session_id = dlgb.get_dlg_var(&callid, &ftag, &ttag, &orig_session_key); } else { rx_session_id = dlgb.get_dlg_var(&callid, &ftag, &ttag, &term_session_key); } if (!rx_session_id || rx_session_id->len <= 0 || !rx_session_id->s) { LM_DBG("New AAR session for this dialog in mode %s\n", direction); //create new diameter auth session int ret = create_new_callsessiondata(&callid, &ftag, &ttag, &rx_authdata_p); if (!ret) { LM_DBG("Unable to create new media session data parcel\n"); goto error; } auth_session = cdpb.AAACreateClientAuthSession(1, callback_for_cdp_session, rx_authdata_p); //returns with a lock if (!auth_session) { LM_ERR("Rx: unable to create new Rx Media Session\n"); if (auth_session) cdpb.AAASessionsUnlock(auth_session->hash); if (rx_authdata_p) { shm_free(rx_authdata_p); rx_authdata_p = 0; } goto error; } //attach new cdp auth session to dlg for this direction if (dlg_direction == DLG_MOBILE_ORIGINATING) { dlgb.set_dlg_var(&callid, &ftag, &ttag, &orig_session_key, &auth_session->id); } else { dlgb.set_dlg_var(&callid, &ftag, &ttag, &term_session_key, &auth_session->id); } LM_DBG("Attached CDP auth session [%.*s] for Rx to dialog in %s mode\n", auth_session->id.len, auth_session->id.s, direction); } else { LM_DBG("Update AAR session for this dialog in mode %s\n", direction); if (saved_t_data) free_saved_transaction_global_data(saved_t_data); //only free global data if no AARs were sent. if one was sent we have to rely on the callback (CDP) to free create_return_code(CSCF_RETURN_TRUE); return CSCF_RETURN_TRUE; } LM_DBG("Suspending SIP TM transaction\n"); if (tmb.t_suspend(msg, &saved_t_data->tindex, &saved_t_data->tlabel) < 0) { LM_ERR("failed to suspend the TM processing\n"); free_saved_transaction_global_data(saved_t_data); return CSCF_RETURN_ERROR; } LM_DBG("Sending Rx AAR"); ret = rx_send_aar(t->uas.request, msg, auth_session, direction, saved_t_data); if (!ret) { LM_ERR("Failed to send AAR\n"); tmb.t_cancel_suspend(saved_t_data->tindex, saved_t_data->tlabel); goto error; } else { LM_DBG("Successful async send of AAR\n"); return CSCF_RETURN_BREAK; //on success we break - because rest of cfg file will be executed by async process } error: LM_ERR("Error trying to send AAR (calling)\n"); if (saved_t_data) free_saved_transaction_global_data(saved_t_data); //only free global data if no AARs were sent. if one was sent we have to rely on the callback (CDP) to free //otherwise the callback will segfault 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; }
/** * 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; } }
/** * 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; }
/** * Checks if there is a match. * 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 found, #ISC_RETURN_FALSE if not, #ISC_RETURN_BREAK on error */ int isc_match_filter(struct sip_msg *msg, char *str1, udomain_t* d) { int k = 0; isc_match *m = NULL; str s = {0, 0}; //sometimes s is populated by an ims_getter method cscf_get_terminating_user that alloc memory that must be free-ed at the end int free_s = 0; int ret = ISC_RETURN_FALSE; isc_mark new_mark, old_mark; enum dialog_direction dir = get_dialog_direction(str1); LM_INFO("Checking triggers\n"); if (dir == DLG_MOBILE_UNKNOWN) return ISC_RETURN_BREAK; if (!cscf_is_initial_request(msg)) return ISC_RETURN_FALSE; /* starting or resuming? */ memset(&old_mark, 0, sizeof (isc_mark)); memset(&new_mark, 0, sizeof (isc_mark)); if (isc_mark_get_from_msg(msg, &old_mark)) { LM_DBG("Message returned s=%d;h=%d;d=%d;a=%.*s\n", old_mark.skip, old_mark.handling, old_mark.direction, old_mark.aor.len, old_mark.aor.s); } else { LM_DBG("Starting triggering\n"); } if (is_route_type(FAILURE_ROUTE)) { /* need to find the handling for the failed trigger */ if (dir == DLG_MOBILE_ORIGINATING) { k = cscf_get_originating_user(msg, &s); if (k) { k = isc_is_registered(&s, d); if (k == IMPU_NOT_REGISTERED) { ret = ISC_RETURN_FALSE; goto done; } new_mark.direction = IFC_ORIGINATING_SESSION; LM_DBG("Orig User <%.*s> [%d]\n", s.len, s.s, k); } else goto done; } if (dir == DLG_MOBILE_TERMINATING) { k = cscf_get_terminating_user(msg, &s); //sometimes s is populated by an ims_getter method cscf_get_terminating_user that alloc memory that must be free-ed at the end free_s = 1; if (k) { k = isc_is_registered(&s, d); //LOG(L_DBG,"after isc_is_registered in ISC_match_filter\n"); if (k == IMPU_REGISTERED) { new_mark.direction = IFC_TERMINATING_SESSION; } else { new_mark.direction = IFC_TERMINATING_UNREGISTERED; } LM_DBG("Term User <%.*s> [%d]\n", s.len, s.s, k); } else { goto done; } } struct cell * t = isc_tmb.t_gett(); LM_CRIT("SKIP: %d\n", old_mark.skip); int index = old_mark.skip; for (k = 0; k < t->nr_of_outgoings; k++) { m = isc_checker_find(s, new_mark.direction, index, msg, isc_is_registered(&s, d), d); if (m) { index = m->index; if (k < t->nr_of_outgoings - 1) isc_free_match(m); } else { LM_ERR("On failure, previously matched trigger no longer matches?!\n"); ret = ISC_RETURN_BREAK; goto done; } } if (m->default_handling == IFC_SESSION_TERMINATED) { /* Terminate the session */ LM_DBG("Terminating session.\n"); isc_tmb.t_reply(msg, IFC_AS_UNAVAILABLE_STATUS_CODE, "AS Contacting Failed - iFC terminated dialog"); LM_DBG("Responding with %d to URI: %.*s\n", IFC_AS_UNAVAILABLE_STATUS_CODE, msg->first_line.u.request.uri.len, msg->first_line.u.request.uri.s); isc_free_match(m); ret = ISC_RETURN_BREAK; goto done; } /* skip the failed triggers (IFC_SESSION_CONTINUED) */ old_mark.skip = index + 1; isc_free_match(m); isc_mark_drop_route(msg); } LM_DBG("Checking if ISC is for originating user\n"); /* originating leg */ if (dir == DLG_MOBILE_ORIGINATING) { k = cscf_get_originating_user(msg, &s); LM_DBG("ISC is for Orig user\n"); if (k) { LM_DBG("Orig user is [%.*s]\n", s.len, s.s); k = isc_is_registered(&s, d); if (k == IMPU_NOT_REGISTERED) { LM_DBG("User is not registered\n"); return ISC_RETURN_FALSE; } LM_DBG("Orig User <%.*s> [%d]\n", s.len, s.s, k); //CHECK if this is a new call (According to spec if the new uri and old mark URI are different then this is a new call and should //be triggered accordingly LM_DBG("Checking if RURI has changed...comparing: <%.*s> and <%.*s>\n", old_mark.aor.len, old_mark.aor.s, s.len, s.s); if ((old_mark.aor.len == s.len) && memcmp(old_mark.aor.s, s.s, s.len) != 0) { LM_DBG("This is a new call....... trigger accordingly\n"); m = isc_checker_find(s, old_mark.direction, 0, msg, isc_is_registered(&s, d), d); } else { m = isc_checker_find(s, old_mark.direction, old_mark.skip, msg, isc_is_registered(&s, d), d); } if (m) { new_mark.direction = IFC_ORIGINATING_SESSION; new_mark.skip = m->index + 1; new_mark.handling = m->default_handling; new_mark.aor = s; ret = isc_forward(msg, m, &new_mark); isc_free_match(m); goto done; } } goto done; } LM_DBG("Checking if ISC is for terminating user\n"); /* terminating leg */ if (dir == DLG_MOBILE_TERMINATING) { k = cscf_get_terminating_user(msg, &s); //sometimes s is populated by an ims_getter method cscf_get_terminating_user that alloc memory that must be free-ed at the end free_s = 1; LM_DBG("ISC is for Term user\n"); if (k) { k = isc_is_registered(&s, d); if (k == IMPU_REGISTERED) { new_mark.direction = IFC_TERMINATING_SESSION; } else { new_mark.direction = IFC_TERMINATING_UNREGISTERED; } LM_DBG("Term User <%.*s> [%d]\n", s.len, s.s, k); //CHECK if this is a new call (According to spec if the new uri and old mark URI are different then this is a new call and should //be triggered accordingly LM_DBG("Checking if RURI has changed...comparing: <%.*s> and <%.*s>\n", old_mark.aor.len, old_mark.aor.s, s.len, s.s); if ((old_mark.aor.len == s.len) && memcmp(old_mark.aor.s, s.s, s.len) != 0) { LM_DBG("This is a new call....... trigger accordingly\n"); m = isc_checker_find(s, new_mark.direction, 0, msg, isc_is_registered(&s, d), d); } else { LM_DBG("Resuming triggering\n"); m = isc_checker_find(s, new_mark.direction, old_mark.skip, msg, isc_is_registered(&s, d), d); } if (m) { new_mark.skip = m->index + 1; new_mark.handling = m->default_handling; new_mark.aor = s; ret = isc_forward(msg, m, &new_mark); isc_free_match(m); goto done; } } goto done; } done: if (s.s && free_s == 1) shm_free(s.s); // shm_malloc in cscf_get_terminating_user if (old_mark.aor.s) pkg_free(old_mark.aor.s); return ret; }
/** * Checks if there is a match. * 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 found, #ISC_RETURN_FALSE if not, #ISC_RETURN_BREAK on error */ int ISC_match_filter(struct sip_msg *msg,char *str1,char *str2) { int k; isc_match *m; str s={0,0}; int ret = ISC_RETURN_FALSE; isc_mark new_mark,old_mark; enum dialog_direction dir = get_dialog_direction(str1); LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Checking triggers\n",str1); if (dir==DLG_MOBILE_UNKNOWN) return ISC_RETURN_BREAK; if (!isc_is_initial_request(msg)) return ISC_RETURN_FALSE; /* starting or resuming? */ memset(&old_mark,0,sizeof(isc_mark)); if (isc_mark_get_from_msg(msg,&old_mark)){ LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Message returned s=%d;h=%d;d=%d\n", str1,old_mark.skip,old_mark.handling,old_mark.direction); } else { LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Starting triggering\n",str1); } /* originating leg */ if (dir==DLG_MOBILE_ORIGINATING){ k = isc_get_originating_user(msg,&s); if (k){ k = isc_is_registered(&s); if (k==NOT_REGISTERED) return ISC_MSG_NOT_FORWARDED; LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Orig User <%.*s> [%d]\n",str1, s.len,s.s,k); m = isc_checker_find(s,old_mark.direction,old_mark.skip,msg); if (m){ new_mark.direction = IFC_ORIGINATING_SESSION; new_mark.skip = m->index+1; new_mark.handling = m->default_handling; ret = isc_forward(msg,m,&new_mark); isc_free_match(m); return ret; } } return ret; } /* terminating leg */ if (dir==DLG_MOBILE_TERMINATING){ k = isc_get_terminating_user(msg,&s); if (k){ k = isc_is_registered(&s); if (k==REGISTERED) { new_mark.direction = IFC_TERMINATING_SESSION; } else { new_mark.direction = IFC_TERMINATING_UNREGISTERED; } LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Orig User <%.*s> [%d]\n",str1, s.len,s.s,k); m = isc_checker_find(s,new_mark.direction,old_mark.skip,msg); if (m){ new_mark.skip = m->index+1; new_mark.handling = m->default_handling; ret = isc_forward(msg,m,&new_mark); isc_free_match(m); if (s.s) pkg_free(s.s); return ret; } } if (s.s) pkg_free(s.s); return ret; } return ret; }
/** * Checks if there is a match. * 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 found, #ISC_RETURN_FALSE if not, #ISC_RETURN_BREAK on error */ int ISC_match_filter(struct sip_msg *msg,char *str1,char *str2) { int k = 0; isc_match *m = NULL; str s={0,0}; int ret = ISC_RETURN_FALSE; isc_mark new_mark,old_mark; enum dialog_direction dir = get_dialog_direction(str1); LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Checking triggers\n",str1); if (dir==DLG_MOBILE_UNKNOWN) return ISC_RETURN_BREAK; if (!isc_is_initial_request(msg)) return ISC_RETURN_FALSE; /* starting or resuming? */ memset(&old_mark,0,sizeof(isc_mark)); memset(&new_mark,0,sizeof(isc_mark)); if (isc_mark_get_from_msg(msg,&old_mark)){ LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Message returned s=%d;h=%d;d=%d;a=%.*s\n", str1,old_mark.skip,old_mark.handling,old_mark.direction,old_mark.aor.len,old_mark.aor.s); } else { LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Starting triggering\n",str1); } if ( #ifdef SER_MOD_INTERFACE is_route_type(FAILURE_ROUTE) #else *isc_tmb.route_mode==MODE_ONFAILURE #endif ){ LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): failure\n",str1); /* need to find the handling for the failed trigger */ if (dir==DLG_MOBILE_ORIGINATING){ k = isc_get_originating_user(msg,&old_mark,&s); if (k){ k = isc_is_registered(&s); if (k==NOT_REGISTERED) { ret = ISC_MSG_NOT_FORWARDED; goto done; } new_mark.direction = IFC_ORIGINATING_SESSION; LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Orig User <%.*s> [%d]\n",str1, s.len,s.s,k); } else goto done; } if (dir==DLG_MOBILE_TERMINATING){ k = isc_get_terminating_user(msg,&old_mark,&s); if (k){ k = isc_is_registered(&s); //LOG(L_DBG,"after isc_is_registered in ISC_match_filter\n"); if (k==REGISTERED) { new_mark.direction = IFC_TERMINATING_SESSION; } else { new_mark.direction = IFC_TERMINATING_UNREGISTERED; } LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Term User <%.*s> [%d]\n",str1, s.len,s.s,k); } else { goto done; } } struct cell * t = isc_tmb.t_gett(); LOG(L_CRIT,"SKIP: %d\n",old_mark.skip); int index = old_mark.skip; for (k=0;k<t->nr_of_outgoings;k++) { m = isc_checker_find(s,new_mark.direction,index,msg,isc_is_registered(&s)); if (m) { index = m->index; if (k < t->nr_of_outgoings - 1) isc_free_match(m); } else { LOG(L_ERR,"ERR:"M_NAME":ISC_match_filter(%s): On failure, previously matched trigger no longer matches?!\n", str1); ret = ISC_RETURN_BREAK; goto done; } } if (m->default_handling==IFC_SESSION_TERMINATED) { /* Terminate the session */ DBG("DEBUG:"M_NAME":ISC_match_filter(%s): Terminating session.\n", str1); isc_tmb.t_reply(msg,IFC_AS_UNAVAILABLE_STATUS_CODE, "AS Contacting Failed - iFC terminated dialog"); LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Responding with %d " "to URI: %.*s\n",str1, IFC_AS_UNAVAILABLE_STATUS_CODE, msg->first_line.u.request.uri.len, msg->first_line.u.request.uri.s); isc_free_match(m); ret = ISC_RETURN_BREAK; goto done; } /* skip the failed triggers (IFC_SESSION_CONTINUED) */ old_mark.skip = index + 1; isc_free_match(m); isc_mark_drop_route(msg); } /* originating leg */ if (dir==DLG_MOBILE_ORIGINATING){ k = isc_get_originating_user(msg,&old_mark,&s); if (k){ k = isc_is_registered(&s); if (k==NOT_REGISTERED) return ISC_MSG_NOT_FORWARDED; LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Orig User <%.*s> [%d]\n",str1, s.len,s.s,k); m = isc_checker_find(s,old_mark.direction,old_mark.skip,msg,isc_is_registered(&s)); if (m){ new_mark.direction = IFC_ORIGINATING_SESSION; new_mark.skip = m->index+1; new_mark.handling = m->default_handling; new_mark.aor = s; ret = isc_forward(msg,m,&new_mark); isc_free_match(m); goto done; } } goto done; } /* terminating leg */ if (dir==DLG_MOBILE_TERMINATING){ k = isc_get_terminating_user(msg,&old_mark,&s); if (k){ k = isc_is_registered(&s); if (k==REGISTERED) { new_mark.direction = IFC_TERMINATING_SESSION; } else { new_mark.direction = IFC_TERMINATING_UNREGISTERED; } LOG(L_INFO,"INFO:"M_NAME":ISC_match_filter(%s): Term User <%.*s> [%d]\n",str1, s.len,s.s,k); m = isc_checker_find(s,new_mark.direction,old_mark.skip,msg,isc_is_registered(&s)); if (m){ new_mark.skip = m->index+1; new_mark.handling = m->default_handling; new_mark.aor = s; ret = isc_forward(msg,m,&new_mark); isc_free_match(m); goto done; } } goto done; } done: if (old_mark.aor.s) pkg_free(old_mark.aor.s); return ret; }
/** * 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; }
int rx_send_aar(struct sip_msg *req, struct sip_msg *res, AAASession* auth, char* direction, saved_transaction_t* saved_t_data) { AAAMessage* aar = 0; str identifier; int identifier_type; AAA_AVP* avp = 0; char x[4]; int ret = 0; str ip; uint16_t ip_version; //we get ip and identifier for the auth session data rx_authsessiondata_t* p_session_data = 0; p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data; identifier = p_session_data->identifier; identifier_type = p_session_data->identifier_type; ip = p_session_data->ip; ip_version = p_session_data->ip_version; /* find direction for AAR (orig/term) */ //need this to add the media component details enum dialog_direction dlg_direction = get_dialog_direction(direction); if (dlg_direction == DLG_MOBILE_UNKNOWN) { LM_DBG("Asked to send AAR for unknown direction.....Aborting...\n"); goto error; } aar = cdpb.AAACreateRequest(IMS_Rx, IMS_AAR, Flag_Proxyable, auth); LM_DBG("Created aar request...\n"); if (!aar) goto error; /*Adding AVPs*/ LM_DBG("Adding auth app id AVP...\n"); /* Add Auth-Application-Id AVP */ if (!rx_add_auth_application_id_avp(aar, IMS_Rx)) goto error; if (!rx_add_vendor_specific_application_id_group(aar, IMS_vendor_id_3GPP, IMS_Rx)) goto error; LM_DBG("Adding dest realm if not there already...\n"); /* Add Destination-Realm AVP, if not already there */ avp = cdpb.AAAFindMatchingAVP(aar, aar->avpList.head, AVP_Destination_Realm, 0, AAA_FORWARD_SEARCH); if (!avp) { str realm = rx_dest_realm; if (realm.len && !rx_add_destination_realm_avp(aar, realm)) goto error; } LM_DBG("Adding AF App identifier...\n"); /* Add AF-Application-Identifier AVP */ str af_id = {0, 0}; af_id = IMS_Serv_AVP_val; if (!rx_add_avp(aar, af_id.s, af_id.len, AVP_IMS_AF_Application_Identifier, AAA_AVP_FLAG_MANDATORY, IMS_vendor_id_3GPP, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; LM_DBG("Adding service info status...\n"); /* Add Service-Info-Status AVP, if prelimiary * by default(when absent): final status is considered*/ if (!res) { set_4bytes(x, AVP_EPC_Service_Info_Status_Preliminary_Service_Information); if (!rx_add_avp(aar, x, 4, AVP_IMS_Service_Info_Status, AAA_AVP_FLAG_MANDATORY, IMS_vendor_id_3GPP, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; } /* Add Auth lifetime AVP */LM_DBG("auth_lifetime %u\n", rx_auth_expiry); //TODO check why this is 0 all the time if (rx_auth_expiry) { set_4bytes(x, rx_auth_expiry); if (!rx_add_avp(aar, x, 4, AVP_Authorization_Lifetime, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; } LM_DBG("Adding subscription id...\n"); rx_add_subscription_id_avp(aar, identifier, identifier_type); LM_DBG("Adding reservation priority...\n"); /* Add Reservation Priority AVP*/ set_4bytes(x, 0); if (!rx_add_avp(aar, x, 4, AVP_ETSI_Reservation_Priority, AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_ETSI, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; LM_DBG("Adding media component...\n"); //Note we add this AVP first as it gets the IP address which we need to create the auth session //Could and maybe should have a separate method that retrieves the IP from SDP - TODO /*---------- 2. Create and add Media-Component-Description AVP ----------*/ /* * See 3GPP TS29214 * * <Media-Component-Description> = {Media-Component-Number} * [Media-Sub-Component] * [AF-Application-Identifier] * [Media-Type] * [Max-Requested-Bandwidth-UL] * [Max-Requested-Bandwidth-DL] * [Flow-Status] * [Reservation-Priority] (Not used yet) * [RS-Bandwidth] * [RR-Bandwidth] * *[Codec-Data] */ add_media_components(aar, req, res, dlg_direction, auth); LM_DBG("Adding framed ip address [%.*s]\n", ip.len, ip.s); /* Add Framed IP address AVP*/ if (!rx_add_framed_ip_avp(&aar->avpList, ip, ip_version)) { LM_ERR("Unable to add framed IP AVP\n"); goto error; } /* Add specific action AVP's */ rx_add_specific_action_avp(aar, 1); // CHARGING_CORRELATION_EXCHANGE rx_add_specific_action_avp(aar, 2); // INDICATION_OF_LOSS_OF_BEARER rx_add_specific_action_avp(aar, 3); // INDICATION_RECOVERY_OF_BEARER rx_add_specific_action_avp(aar, 4); // INDICATION_RELEASE_OF_BEARER rx_add_specific_action_avp(aar, 5); // INDICATION_ESTABLISHMENT_OF_BEARER (now void) rx_add_specific_action_avp(aar, 6); // IP-CAN_CHANGE rx_add_specific_action_avp(aar, 12); // ACCESS_NETWORK_INFO_REPORT show_callsessiondata(p_session_data); LM_DBG("Unlocking AAA session...\n"); if (auth) cdpb.AAASessionsUnlock(auth->hash); LM_DBG("sending AAR to PCRF\n"); if (rx_forced_peer.len) ret = cdpb.AAASendMessageToPeer(aar, &rx_forced_peer, (void*) async_aar_callback, (void*) saved_t_data); else ret = cdpb.AAASendMessage(aar, (void*) async_aar_callback, (void*) saved_t_data); return ret; error: LM_ERR("unexpected error\n"); if (aar) cdpb.AAAFreeMessage(&aar); if (auth) { cdpb.AAASessionsUnlock(auth->hash); cdpb.AAADropAuthSession(auth); auth = 0; } return ret; }
AAAMessage *rx_send_aar(struct sip_msg *req, struct sip_msg *res, AAASession* auth, str* callid, str* ftag, str* ttag, char* direction, rx_authsessiondata_t **rx_authdata) { AAAMessage* aar = 0; AAAMessage* aaa = 0; AAA_AVP* avp = 0; char x[4]; str ip; uint16_t ip_version; /* find direction for AAR (orig/term) */ //need this to add the media component details enum dialog_direction dlg_direction = get_dialog_direction(direction); if (dlg_direction == DLG_MOBILE_UNKNOWN) { LM_DBG("Asked to send AAR for unknown direction.....Aborting...\n"); goto error; } aar = cdpb.AAACreateRequest(IMS_Rx, IMS_AAR, Flag_Proxyable, auth); LM_DBG("Created aar request...\n"); if (!aar) goto error; /*Adding AVPs*/ LM_DBG("Adding auth app id AVP...\n"); /* Add Auth-Application-Id AVP */ if (!rx_add_auth_application_id_avp(aar, IMS_Rx)) goto error; if (!rx_add_vendor_specific_application_id_group(aar, IMS_vendor_id_3GPP, IMS_Rx)) goto error; LM_DBG("Adding dest realm if not there already...\n"); /* Add Destination-Realm AVP, if not already there */ avp = cdpb.AAAFindMatchingAVP(aar, aar->avpList.head, AVP_Destination_Realm, 0, AAA_FORWARD_SEARCH); if (!avp) { str realm = rx_dest_realm; if (realm.len && !rx_add_destination_realm_avp(aar, realm)) goto error; } LM_DBG("Adding AF App identifier...\n"); /* Add AF-Application-Identifier AVP */ str af_id = {0, 0}; af_id = IMS_Serv_AVP_val; if (!rx_add_avp(aar, af_id.s, af_id.len, AVP_IMS_AF_Application_Identifier, AAA_AVP_FLAG_MANDATORY, IMS_vendor_id_3GPP, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; LM_DBG("Adding service info status...\n"); /* Add Service-Info-Status AVP, if prelimiary * by default(when absent): final status is considered*/ if (!res) { set_4bytes(x, AVP_EPC_Service_Info_Status_Preliminary_Service_Information); if (!rx_add_avp(aar, x, 4, AVP_IMS_Service_Info_Status, AAA_AVP_FLAG_MANDATORY, IMS_vendor_id_3GPP, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; } /* Add Auth lifetime AVP */LM_DBG("auth_lifetime %u\n", rx_auth_expiry); //TODO check why this is 0 all the time if (rx_auth_expiry) { set_4bytes(x, rx_auth_expiry); if (!rx_add_avp(aar, x, 4, AVP_Authorization_Lifetime, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; } LM_DBG("Adding subscription id...\n"); /* Add Subscription ID AVP*/ int identifier_type = AVP_Subscription_Id_Type_SIP_URI; //we only do IMPU now //to get the SIP URI I use the dlg direction - if its mo I get the from uri from the req, if its mt I get the to uri from the req str identifier; if (dlg_direction == DLG_MOBILE_ORIGINATING) { cscf_get_from_uri(req, &identifier); } else { cscf_get_to_uri(req, &identifier); } rx_add_subscription_id_avp(aar, identifier, identifier_type); LM_DBG("Adding reservation priority...\n"); /* Add Reservation Priority AVP*/ set_4bytes(x, 0); if (!rx_add_avp(aar, x, 4, AVP_ETSI_Reservation_Priority, AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_ETSI, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; LM_DBG("Adding media component...\n"); //Note we add this AVP first as it gets the IP address which we need to create the auth session //Could and maybe should have a separate method that retrieves the IP from SDP - TODO /*---------- 2. Create and add Media-Component-Description AVP ----------*/ /* * See 3GPP TS29214 * * <Media-Component-Description> = {Media-Component-Number} * [Media-Sub-Component] * [AF-Application-Identifier] * [Media-Type] * [Max-Requested-Bandwidth-UL] * [Max-Requested-Bandwidth-DL] * [Flow-Status] * [Reservation-Priority] (Not used yet) * [RS-Bandwidth] * [RR-Bandwidth] * *[Codec-Data] */ add_media_components(aar, req, res, dlg_direction, &ip, &ip_version); LM_DBG("Adding framed ip address [%.*s]\n", ip.len, ip.s); /* Add Framed IP address AVP*/ if (!rx_add_framed_ip_avp(&aar->avpList, ip, ip_version)) { LM_ERR("Unable to add framed IP AVP\n"); goto error; } LM_DBG("Unlocking AAA session...\n"); if (auth) cdpb.AAASessionsUnlock(auth->hash); LM_DBG("sending AAR to PCRF\n"); if (rx_forced_peer.len) aaa = cdpb.AAASendRecvMessageToPeer(aar, &rx_forced_peer); else aaa = cdpb.AAASendRecvMessage(aar); return aaa; error: LM_ERR("unexpected error\n"); if (aar) cdpb.AAAFreeMessage(&aar); if (auth) { cdpb.AAASessionsUnlock(auth->hash); cdpb.AAADropAuthSession(auth); auth = 0; } return NULL; }