/* * set dialog's request uri and destination uri (optional) */ int set_dlg_target(dlg_t* _d, str* _ruri, str* _duri) { if (!_d || !_ruri) { LOG(L_ERR, "set_dlg_target(): Invalid parameter value\n"); return -1; } if (_d->rem_target.s) shm_free(_d->rem_target.s); if (_d->dst_uri.s) { shm_free(_d->dst_uri.s); _d->dst_uri.s = 0; _d->dst_uri.len = 0; } if (str_duplicate(&_d->rem_target, _ruri)) return -1; if (_duri && _duri->len) { if (str_duplicate(&_d->dst_uri, _duri)) return -1; } if (calculate_hooks(_d) < 0) { LOG(L_ERR, "set_dlg_target(): Error while calculating hooks\n"); return -1; } return 0; }
/* * Handle dialog in DLG_EARLY state, we will be processing either * next provisional response or a final response */ static inline int dlg_early_resp_uac(dlg_t* _d, struct sip_msg* _m) { int code; code = _m->first_line.u.reply.statuscode; if (code < 200) { /* We are in early state already, do nothing */ } else if ((code >= 200) && (code <= 299)) { /* Warning - we can handle here response for non-initial request (for * example UPDATE within early INVITE/BYE dialog) and move into * confirmed state may be error! But this depends on dialog type... */ /* Same as in dlg_new_resp_uac */ /* A final response, update the structures and transit * into DLG_CONFIRMED */ if (response2dlg(_m, _d) < 0) return -1; _d->state = DLG_CONFIRMED; if (calculate_hooks(_d) < 0) { LOG(L_ERR, "dlg_early_resp_uac(): Error while calculating hooks\n"); return -2; } } else { /* Else terminate the dialog */ _d->state = DLG_DESTROYED; /* Signalize the termination with positive return value */ return 1; } return 0; }
/* * Establishing a new dialog, UAS side */ int new_dlg_uas(struct sip_msg* _req, int _code, /*str* _tag,*/ dlg_t** _d) { dlg_t* res; str tag; if (!_req || /*!_tag ||*/ !_d) { LOG(L_ERR, "new_dlg_uas(): Invalid parameter value\n"); return -1; } if (_code > 299) { DBG("new_dlg_uas(): Status code >= 300, no dialog created\n"); } res = (dlg_t*)shm_malloc(sizeof(dlg_t)); if (res == 0) { LOG(L_ERR, "new_dlg_uac(): No memory left\n"); return -3; } /* Clear everything */ memset(res, 0, sizeof(dlg_t)); if (request2dlg(_req, res) < 0) { LOG(L_ERR, "new_dlg_uas(): Error while converting request to dialog\n"); free_dlg(res); return -4; } if (_code > 100) { tag.s = tm_tags; tag.len = TOTAG_VALUE_LEN; calc_crc_suffix(_req, tm_tag_suffix); if (str_duplicate(&res->id.loc_tag, &tag) < 0) { free_dlg(res); return -5; } } *_d = res; if (_code < 100) (*_d)->state = DLG_NEW; else if (_code < 200) (*_d)->state = DLG_EARLY; else (*_d)->state = DLG_CONFIRMED; if (calculate_hooks(*_d) < 0) { LOG(L_ERR, "new_dlg_uas(): Error while calculating hooks\n"); free_dlg(res); return -6; } #ifdef DIALOG_CALLBACKS run_new_dlg_callbacks(DLG_CB_UAS, res, _req); #endif return 0; }
/* * Create a new dialog */ int new_dlg_uac(str* _cid, str* _ltag, unsigned int _lseq, str* _luri, str* _ruri, dlg_t** _d) { dlg_t* res; str generated_cid; str generated_ltag; if (!_cid) { /* if not given, compute new one */ generate_callid(&generated_cid); _cid = &generated_cid; } if (_cid && (!_ltag)) { /* if not given, compute new one */ generate_fromtag(&generated_ltag, _cid); _ltag = &generated_ltag; } if (_lseq == 0) _lseq = DEFAULT_CSEQ; if (!_cid || !_ltag || !_luri || !_ruri || !_d) { LOG(L_ERR, "new_dlg_uac(): Invalid parameter value\n"); return -1; } res = (dlg_t*)shm_malloc(sizeof(dlg_t)); if (res == 0) { LOG(L_ERR, "new_dlg_uac(): No memory left\n"); return -2; } /* Clear everything */ memset(res, 0, sizeof(dlg_t)); /* Make a copy of Call-ID */ if (str_duplicate(&res->id.call_id, _cid) < 0) return -3; /* Make a copy of local tag (usually From tag) */ if (str_duplicate(&res->id.loc_tag, _ltag) < 0) return -4; /* Make a copy of local URI (usually From) */ if (str_duplicate(&res->loc_uri, _luri) < 0) return -5; /* Make a copy of remote URI (usually To) */ if (str_duplicate(&res->rem_uri, _ruri) < 0) return -6; /* Make a copy of local sequence (usually CSeq) */ res->loc_seq.value = _lseq; /* And mark it as set */ res->loc_seq.is_set = 1; *_d = res; if (calculate_hooks(*_d) < 0) { LOG(L_ERR, "new_dlg_uac(): Error while calculating hooks\n"); /* FIXME: free everything here */ shm_free(res); return -2; } #ifdef DIALOG_CALLBACKS run_new_dlg_callbacks(DLG_CB_UAC, res, 0); #endif return 0; }
/* * Handle dialog in DLG_CONFIRMED state, we will be processing * a response to a request sent within a dialog */ static inline int dlg_confirmed_resp_uac(dlg_t* _d, struct sip_msg* _m, target_refresh_t is_target_refresh) { int code; str contact; code = _m->first_line.u.reply.statuscode; /* Dialog has been already confirmed, that means we received * a response to a request sent within the dialog. We will * update remote target URI if and only if the message sent was * a target refresher. */ /* IF we receive a 481 response, terminate the dialog because * the remote peer indicated that it didn't have the dialog * state anymore, signal this termination with a positive return * value */ if (code == 481) { _d->state = DLG_DESTROYED; return 1; } /* Do nothing if not 2xx */ if ((code < 200) || (code >= 300)) return 0; if (refresh_dialog_resp(_m, is_target_refresh)) { /* Get contact if any and update remote target */ if (parse_headers(_m, HDR_CONTACT_F, 0) == -1) { LOG(L_ERR, "dlg_confirmed_resp_uac(): Error while parsing headers\n"); return -2; } /* Try to extract contact URI */ if (get_contact_uri(_m, &contact) < 0) return -3; /* If there is a contact URI */ if (contact.len) { /* Free old remote target and destination uri if any */ if (_d->rem_target.s) shm_free(_d->rem_target.s); if (_d->dst_uri.s) { shm_free(_d->dst_uri.s); _d->dst_uri.s = 0; _d->dst_uri.len = 0; } /* Duplicate new remote target */ if (str_duplicate(&_d->rem_target, &contact) < 0) return -4; } if (calculate_hooks(_d) < 0) return -1; } return 0; }
/* * Handle dialog in DLG_NEW state, we will be processing the * first response */ static inline int dlg_new_resp_uac(dlg_t* _d, struct sip_msg* _m) { int code; /* * Dialog is in DLG_NEW state, we will copy remote * target URI, remote tag if present, and route-set * if present. And we will transit into DLG_CONFIRMED * if the response was 2xx and to DLG_DESTROYED if the * request was a negative final response. */ code = _m->first_line.u.reply.statuscode; if (code < 200) { /* A provisional response, do nothing, we could * update remote tag and route set but we will do that * for a positive final response anyway and I don't want * bet on presence of these fields in provisional responses * * Send a request to [email protected] if you need to update * the structures here */ /* * Alberto Diez .. i might need to update the remote tag here? * and the route set? maybe i do want all that */ if (response2dlg(_m, _d) < 0) return -1; } else if ((code >= 200) && (code < 299)) { /* A final response, update the structures and transit * into DLG_CONFIRMED */ if (response2dlg(_m, _d) < 0) return -1; _d->state = DLG_CONFIRMED; if (calculate_hooks(_d) < 0) { LOG(L_ERR, "dlg_new_resp_uac(): Error while calculating hooks\n"); return -2; } } else { /* * A negative final response, mark the dialog as destroyed * Again, I do not update the structures here because it * makes no sense to me, a dialog shouldn't be used after * it is destroyed */ _d->state = DLG_DESTROYED; /* Signalize the termination with positive return value */ return 1; } return 0; }
/* * UAS side - update a dialog from a request */ int dlg_request_uas(dlg_t* _d, struct sip_msg* _m, target_refresh_t is_target_refresh) { str contact; int cseq; if (!_d || !_m) { LOG(L_ERR, "dlg_request_uas(): Invalid parameter value\n"); return -1; } /* We must check if the request is not out of order or retransmission * first, if so then we will not update anything */ if (parse_headers(_m, HDR_CSEQ_F, 0) == -1) { LOG(L_ERR, "dlg_request_uas(): Error while parsing headers\n"); return -2; } if (get_cseq_value(_m, (unsigned int*)&cseq) < 0) return -3; if (_d->rem_seq.is_set && (cseq <= _d->rem_seq.value)) return 0; /* Neither out of order nor retransmission -> update */ _d->rem_seq.value = cseq; _d->rem_seq.is_set = 1; /* We will als update remote target URI if the message * is target refresher */ if (refresh_dialog_req(_m, is_target_refresh)) { /* target refresher */ if (parse_headers(_m, HDR_CONTACT_F, 0) == -1) { LOG(L_ERR, "dlg_request_uas(): Error while parsing headers\n"); return -4; } if (get_contact_uri(_m, &contact) < 0) return -5; if (contact.len) { if (_d->rem_target.s) shm_free(_d->rem_target.s); if (_d->dst_uri.s) { shm_free(_d->dst_uri.s); _d->dst_uri.s = 0; _d->dst_uri.len = 0; } if (str_duplicate(&_d->rem_target, &contact) < 0) return -6; } if (calculate_hooks(_d) < 0) return -1; } return 0; }
/* * wrapper to calculate_hooks * added by dcm */ int w_calculate_hooks(dlg_t* _d) { return calculate_hooks(_d); }
/*Actions are composed as follows: * (the action length and type as always= 5 bytes) * 4:uac_id * * int request(str* method, str* req_uri, str* to, str* from, str* headers, str* body, transaction_cb c, void* cp) * TODO performance speedup: instead of using * dynamically allocated memory for headers,body,totag,reason and my_msg * use static buffers. * */ int ac_uac_req(as_p the_as,unsigned char processor_id,unsigned int flags,char *action,int len) { unsigned int cseq; char err_buf[MAX_REASON_LEN]; struct sip_msg *my_msg; struct to_body *fb,*tb; struct cseq_body *cseqb; struct as_uac_param *the_param; dlg_t *my_dlg; int k,retval,uac_id,sip_error,ret,err_ret; long clen; str headers,body,fake_uri; uac_req_t uac_r; headers.s=body.s=fake_uri.s=NULL; my_dlg=NULL; my_msg=NULL; the_param=NULL; k=clen=0; net2hostL(uac_id,action,k); if(!(headers.s=pkg_malloc(MAX_HEADER))){ LM_ERR("Out of Memory!!"); goto error; } headers.len=0; LM_DBG("Action UAC Message: uac_id:%d processor_id=%d\n",uac_id,processor_id); if (!(my_msg = parse_ac_msg(HDR_EOH_F,action+k,len-k))) { LM_ERR("out of memory!\n"); goto error; } if(my_msg->first_line.type==SIP_REPLY){ LM_ERR("trying to create a UAC with a SIP response!!\n"); goto error; } if(parse_headers(my_msg,HDR_EOH_F,0)==-1){ LM_ERR("ERROR:seas:ac_uac_req:parsing headers\n"); goto error; } if(parse_from_header(my_msg)<0){ LM_ERR("parsing from header ! \n"); goto error; } if(check_transaction_quadruple(my_msg)==0){ as_action_fail_resp(uac_id,SE_UAC,"Headers missing (to,from,call-id,cseq)?",0); LM_ERR("Headers missing (to,from,call-id,cseq)?"); goto error; } if(!(get_from(my_msg)) || !(get_from(my_msg)->tag_value.s) || !(get_from(my_msg)->tag_value.len)){ as_action_fail_resp(uac_id,SE_UAC,"From tag missing",0); LM_ERR("From tag missing"); goto error; } fb=my_msg->from->parsed; tb=my_msg->to->parsed; cseqb=my_msg->cseq->parsed; if(0!=(str2int(&cseqb->number,&cseq))){ LM_DBG("unable to parse CSeq\n"); goto error; } if(my_msg->first_line.u.request.method_value != METHOD_ACK && my_msg->first_line.u.request.method_value != METHOD_CANCEL) { /** we trick req_within */ cseq--; } if(seas_f.tmb.new_dlg_uac(&(my_msg->callid->body),&(fb->tag_value),cseq,\ &(fb->uri),&(tb->uri),&my_dlg) < 0) { as_action_fail_resp(uac_id,SE_UAC,"Error creating new dialog",0); LM_ERR("Error while creating new dialog\n"); goto error; } if(seas_f.tmb.dlg_add_extra(my_dlg,&(fb->display),&(tb->display)) < 0 ) { as_action_fail_resp(uac_id,SE_UAC, "Error adding the display names to the new dialog",0); LM_ERR("failed to add display names to the new dialog\n"); goto error; } if(tb->tag_value.s && tb->tag_value.len) shm_str_dup(&my_dlg->id.rem_tag,&tb->tag_value); /**Awful hack: to be able to set our own CSeq, from_tag and call-ID we have * to use req_within instead of req_outside (it sets it's own CSeq,Call-ID * and ftag), so we have to simulate that the dialog is already in completed * state so... */ server_signature=0; my_dlg->state = DLG_CONFIRMED; if(0>(headers.len=extract_allowed_headers(my_msg,1,-1,HDR_CONTENTLENGTH_F|HDR_ROUTE_F|HDR_TO_F|HDR_FROM_F|HDR_CALLID_F|HDR_CSEQ_F,headers.s,MAX_HEADER))) { LM_ERR("Unable to extract allowed headers!!\n"); goto error; } headers.s[headers.len]=0; /*let's get the body*/ if(my_msg->content_length) clen=(long)get_content_length(my_msg); if(clen!=0){ if(!(body.s=pkg_malloc(clen))){ LM_ERR("Out of Memory!"); goto error; } memcpy(body.s,get_body(my_msg),clen); body.len=clen; body.s[clen]=0; LM_DBG("Trying to construct a Sip Request with: body:%d[%.*s] headers:%d[%.*s]\n",\ body.len,body.len,body.s,headers.len,headers.len,headers.s); /*t_reply_with_body un-ref-counts the transaction, so dont use it anymore*/ }else{ body.s=NULL; body.len=0; } /*Now... create the UAC !! * it would be great to know the hash_index and the label that have been assigned * to our newly created cell, but t_uac does not leave any way for us to know... * only that when that transaction transitions its state (ie. a response is received, * a timeout is reached, etc...) the callback will be called with the given parameter. * * So the only way we have to know who we are, is passing as a parameter a structure with * 2 pointers: one to the app_server and the other, the identifier of the UAC (uac_id). * */ if(!(the_param=shm_malloc(sizeof(struct as_uac_param)))){ LM_ERR("out of shared memory\n"); goto error; } the_param->who=my_as; the_param->uac_id=uac_id; the_param->processor_id=processor_id; the_param->destroy_cb_set=0; shm_str_dup(&my_dlg->rem_target,&my_msg->first_line.u.request.uri); if (my_msg->route) { if (parse_rr(my_msg->route) < 0) { LM_ERR( "Error while parsing Route body\n"); goto error; } /* TODO route_set should be a shm copy of my_msg->route->parsed */ my_dlg->route_set=(rr_t*)my_msg->route->parsed; /** this SHOULD be: shm_duplicate_rr(&my_dlg->route_set,my_msg->route->parsed); * but it will last more... */ } calculate_hooks(my_dlg); if(flags & SPIRAL_FLAG){ memcpy(headers.s+headers.len,SPIRAL_HDR CRLF,SPIRAL_HDR_LEN + CRLF_LEN); headers.len+=SPIRAL_HDR_LEN+CRLF_LEN; headers.s[headers.len]=0; fake_uri.s=pkg_malloc(200); fake_uri.len=print_local_uri(the_as,processor_id,fake_uri.s,200); if(fake_uri.len<0){ LM_ERR("printing local uri\n"); goto error; } my_dlg->hooks.next_hop=&fake_uri; } /* Kamailio and OpenSIPs seem to have diverged quite a bit on flags and events notified to UACs. Let's see if kamailio gets it right by now, if not this is a TODO: check PASS_PROVISIONAL my_dlg->T_flags=T_NO_AUTO_ACK|T_PASS_PROVISIONAL_FLAG ; this is the same as (TMCB_DONT_ACK|TMCB_LOCAL_RESPONSE_OUT) in Kamailio */ set_uac_req(&uac_r, &(my_msg->first_line.u.request.method), &headers, &body, my_dlg,TMCB_DONT_ACK|TMCB_LOCAL_RESPONSE_OUT, uac_cb, (void*)the_param); ret=seas_f.tmb.t_request_within(&uac_r); /** now undo all the fakes we have put in my_dlg*/ /*because my_dlg->route_set should be shm but we fake it (its pkg_mem)*/ my_dlg->route_set=(rr_t *)0; if (ret < 0) { err_ret = err2reason_phrase(ret,&sip_error,err_buf, sizeof(err_buf), "SEAS/UAC"); LM_ERR("failed to send the [%.*s] request\n",uac_r.method->len,uac_r.method->s); LM_ERR("Error on request_within %s\n",err_buf ); if(err_ret > 0) { as_action_fail_resp(uac_id,ret,err_buf,0); }else{ as_action_fail_resp(uac_id,E_UNSPEC,"500 SEAS/UAC error",0); } goto error; } retval=0; goto exit; error: retval = -1; if(the_param) shm_free(the_param); exit: seas_f.tmb.free_dlg(my_dlg); if(headers.s) pkg_free(headers.s); if(body.s) pkg_free(body.s); if(fake_uri.s) pkg_free(fake_uri.s); if(my_msg){ if(my_msg->headers) free_hdr_field_lst(my_msg->headers); pkg_free(my_msg); } return retval; }