/* * Send an initial request that will start a dialog * WARNING: writes uac_r->dialog */ int req_outside(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_hop) { str callid, fromtag; if (check_params(uac_r, to, from) < 0) goto err; generate_callid(&callid); generate_fromtag(&fromtag, &callid); if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &uac_r->dialog) < 0) { LOG(L_ERR, "req_outside(): Error while creating new dialog\n"); goto err; } if (ruri) { uac_r->dialog->rem_target.s = ruri->s; uac_r->dialog->rem_target.len = ruri->len; /* hooks will be set from w_calculate_hooks */ } if (next_hop) uac_r->dialog->dst_uri = *next_hop; w_calculate_hooks(uac_r->dialog); return t_uac(uac_r); err: /* callback parameter must be freed outside of tm module if (cbp) shm_free(cbp); */ return -1; }
/* * Send a transactional request, no dialogs involved */ int request(str* m, str* ruri, str* to, str* from, str* h, str* b, transaction_cb c, void* cp) { str callid, fromtag; dlg_t* dialog; int res; if (check_params(m, to, from, &dialog) < 0) goto err; generate_callid(&callid); generate_fromtag(&fromtag, &callid); if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &dialog) < 0) { LOG(L_ERR, "req_outside(): Error while creating temorary dialog\n"); goto err; } if (ruri) { dialog->rem_target.s = ruri->s; dialog->rem_target.len = ruri->len; dialog->hooks.request_uri = &dialog->rem_target; } res = t_uac(m, h, b, dialog, c, cp); dialog->rem_target.s = 0; free_dlg(dialog); return res; err: if (cp) shm_free(cp); return -1; }
/* * Send a transactional request, no dialogs involved */ int request(str* m, str* ruri, str* to, str* from, str* h, str* b, str *oburi, transaction_cb cb, void* cbp,release_tmcb_param release_func) { str callid, fromtag; dlg_t* dialog; int res; if (check_params(m, to, from, &dialog) < 0) goto err; generate_callid(&callid); generate_fromtag(&fromtag, &callid); if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &dialog) < 0) { LM_ERR("failed to create temporary dialog\n"); goto err; } if (ruri) { dialog->rem_target.s = ruri->s; dialog->rem_target.len = ruri->len; dialog->hooks.request_uri = &dialog->rem_target; } if (oburi && oburi->s) dialog->hooks.next_hop = oburi; w_calculate_hooks(dialog); res = t_uac(m, h, b, dialog, cb, cbp, release_func); dialog->rem_target.s = 0; free_dlg(dialog); return res; err: return -1; }
/* * Send a transactional request, no dialogs involved * WARNING: writes uac_r->dialog */ int request(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_hop) { str callid, fromtag; dlg_t* dialog; int res; if (check_params(uac_r, to, from) < 0) goto err; if (uac_r->callid == NULL || uac_r->callid->len <= 0) generate_callid(&callid); else callid = *uac_r->callid; generate_fromtag(&fromtag, &callid); if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &dialog) < 0) { LOG(L_ERR, "request(): Error while creating temporary dialog\n"); goto err; } if (ruri) { dialog->rem_target.s = ruri->s; dialog->rem_target.len = ruri->len; /* hooks will be set from w_calculate_hooks */ } if (next_hop) dialog->dst_uri = *next_hop; w_calculate_hooks(dialog); /* WARNING: * to be clean it should be called * set_dlg_target(dialog, ruri, next_hop); * which sets both uris if given [but it duplicates them in shm!] * * but in this case the _ruri parameter in set_dlg_target * must be optional (it is needed now) and following hacks * dialog->rem_target.s = 0; * dialog->dst_uri.s = 0; * before freeing dialog here must be removed */ uac_r->dialog = dialog; if(uac_r->ssock!=NULL && uac_r->ssock->len>0 && uac_r->dialog->send_sock==NULL) { /* set local send socket */ uac_r->dialog->send_sock = lookup_local_socket(uac_r->ssock); } res = t_uac(uac_r); dialog->rem_target.s = 0; dialog->dst_uri.s = 0; free_dlg(dialog); uac_r->dialog = 0; return res; err: /* callback parameter must be freed outside of tm module if (cp) shm_free(cp); */ return -1; }
/* * Send a transactional request, no dialogs involved */ int request(str* m, str* ruri, str* to, str* from, str* h, str* b, str *next_hop, transaction_cb c, void* cp) { str callid, fromtag; dlg_t* dialog; int res; if (check_params(m, to, from, &dialog) < 0) goto err; generate_callid(&callid); generate_fromtag(&fromtag, &callid); if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &dialog) < 0) { LOG(L_ERR, "request(): Error while creating temporary dialog\n"); goto err; } if (ruri) { dialog->rem_target.s = ruri->s; dialog->rem_target.len = ruri->len; /* hooks will be set from w_calculate_hooks */ } if (next_hop) dialog->dst_uri = *next_hop; w_calculate_hooks(dialog); /* WARNING: * to be clean it should be called * set_dlg_target(dialog, ruri, next_hop); * which sets both uris if given [but it duplicates them in shm!] * * but in this case the _ruri parameter in set_dlg_target * must be optional (it is needed now) and following hacks * dialog->rem_target.s = 0; * dialog->dst_uri.s = 0; * before freeing dialog here must be removed */ res = t_uac(m, h, b, dialog, c, cp); dialog->rem_target.s = 0; dialog->dst_uri.s = 0; free_dlg(dialog); return res; err: if (cp) shm_free(cp); return -1; }
/* * Send a message within a dialog */ int req_within(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb completion_cb, void* cbp) { if (!method || !dialog) { LOG(L_ERR, "req_within: Invalid parameter value\n"); goto err; } if ((method->len == 3) && (!memcmp("ACK", method->s, 3))) goto send; if ((method->len == 6) && (!memcmp("CANCEL", method->s, 6))) goto send; dialog->loc_seq.value++; /* Increment CSeq */ send: return t_uac(method, headers, body, dialog, completion_cb, cbp); err: if (cbp) shm_free(cbp); return -1; }
/* * Send a message within a dialog */ int req_within(uac_req_t *uac_r) { if (!uac_r || !uac_r->method || !uac_r->dialog) { LOG(L_ERR, "req_within: Invalid parameter value\n"); goto err; } if ((uac_r->method->len == 3) && (!memcmp("ACK", uac_r->method->s, 3))) goto send; if ((uac_r->method->len == 6) && (!memcmp("CANCEL", uac_r->method->s, 6))) goto send; uac_r->dialog->loc_seq.value++; /* Increment CSeq */ send: return t_uac(uac_r); err: /* callback parameter must be freed outside of tm module if (cbp) shm_free(cbp); */ return -1; }
/* * Send an initial request that will start a dialog */ int req_outside(str* method, str* to, str* from, str* headers, str* body, dlg_t** dialog, transaction_cb cb, void* cbp) { str callid, fromtag; if (check_params(method, to, from, dialog) < 0) goto err; generate_callid(&callid); generate_fromtag(&fromtag, &callid); if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, dialog) < 0) { LOG(L_ERR, "req_outside(): Error while creating new dialog\n"); goto err; } return t_uac(method, headers, body, *dialog, cb, cbp); err: if (cbp) shm_free(cbp); return -1; }
/* * Send an initial request that will start a dialog */ int req_outside(str* method, str* to, str* from, str* headers, str* body, dlg_t** dialog, transaction_cb cb, void* cbp,release_tmcb_param release_func) { str callid, fromtag; if (check_params(method, to, from, dialog) < 0) goto err; generate_callid(&callid); generate_fromtag(&fromtag, &callid); if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, dialog) < 0) { LM_ERR("failed to create new dialog\n"); goto err; } return t_uac(method, headers, body, *dialog, cb, cbp, release_func); err: return -1; }
/* * Send a message within a dialog */ int req_within(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb completion_cb,void* cbp,release_tmcb_param release_func) { if (!method || !dialog) { LM_ERR("invalid parameter value\n"); goto err; } if (dialog->state != DLG_CONFIRMED) { LM_ERR("dialog is not confirmed yet\n"); goto err; } if ( !( ((method->len == 3) && !memcmp("ACK", method->s, 3)) || ((method->len == 6) && !memcmp("CANCEL", method->s, 6)) ) ) { dialog->loc_seq.value++; /* Increment CSeq */ } return t_uac(method, headers, body, dialog, completion_cb, cbp, release_func); err: return -1; }
inline static int w_t_new_request(struct sip_msg* msg, char *p_method, char *p_ruri, char *p_from, char *p_to, char *p_body, char *p_ctx) { #define CONTENT_TYPE_HDR "Content-Type: " #define CONTENT_TYPE_HDR_LEN (sizeof(CONTENT_TYPE_HDR)-1) static dlg_t dlg; struct usr_avp **avp_list; str ruri; str method; str body; str headers; str s; int_str ctx; char *p; memset( &dlg, 0, sizeof(dlg_t)); /* evaluate the parameters */ /* method */ if ( fixup_get_svalue(msg, (gparam_p)p_method, &method)<0 ) { LM_ERR("failed to extract METHOD param\n"); return -1; } LM_DBG("setting METHOD to <%.*s>\n", method.len, method.s); /* ruri - next hop is the same as RURI */ dlg.hooks.next_hop = dlg.hooks.request_uri = &ruri; if ( fixup_get_svalue(msg, (gparam_p)p_ruri, &ruri)<0 ) { LM_ERR("failed to extract RURI param\n"); return -1; } LM_DBG("setting RURI to <%.*s>\n", dlg.hooks.next_hop->len, dlg.hooks.next_hop->s); /* FROM URI + display */ if ( fixup_get_svalue(msg, (gparam_p)p_from, &s)<0 ) { LM_ERR("failed to extract FROM param\n"); return -1; } if ( (p=q_memrchr(s.s, ' ', s.len))==NULL ) { /* no display, only FROM URI */ dlg.loc_uri = s; dlg.loc_dname.s = NULL; dlg.loc_dname.len = 0; } else { /* display + URI */ dlg.loc_uri.s = p+1; dlg.loc_uri.len = s.s+s.len - dlg.loc_uri.s; dlg.loc_dname.s = s.s; dlg.loc_dname.len = p - s.s; } LM_DBG("setting FROM to <%.*s> + <%.*s>\n", dlg.loc_dname.len, dlg.loc_dname.s, dlg.loc_uri.len, dlg.loc_uri.s); /* TO URI + display */ if ( fixup_get_svalue(msg, (gparam_p)p_to, &s)<0 ) { LM_ERR("failed to extract TO param\n"); return -1; } if ( (p=q_memrchr(s.s, ' ', s.len))==NULL ) { /* no display, only TO URI */ dlg.rem_uri = s; dlg.rem_dname.s = NULL; dlg.rem_dname.len = 0; } else { /* display + URI */ dlg.rem_uri.s = p+1; dlg.rem_uri.len = s.s+s.len - dlg.rem_uri.s; dlg.rem_dname.s = s.s; dlg.rem_dname.len = p - s.s; } LM_DBG("setting TO to <%.*s> + <%.*s>\n", dlg.rem_dname.len, dlg.rem_dname.s, dlg.rem_uri.len, dlg.rem_uri.s); /* BODY and Content-Type */ if (p_body!=NULL) { if ( fixup_get_svalue(msg, (gparam_p)p_body, &body)<0 ) { LM_ERR("failed to extract BODY param\n"); return -1; } if ( (p=q_memchr(body.s, ' ', body.len))==NULL ) { LM_ERR("Content Type not found in the beginning of body <%.*s>\n", body.len, body.s); return -1; } /* build the Content-type header */ headers.len = CONTENT_TYPE_HDR_LEN + (p-body.s) + CRLF_LEN; if ( (headers.s=(char*)pkg_malloc(headers.len))==NULL ) { LM_ERR("failed to get pkg mem (needed %d)\n",headers.len); return -1; } memcpy( headers.s, CONTENT_TYPE_HDR, CONTENT_TYPE_HDR_LEN); memcpy( headers.s+CONTENT_TYPE_HDR_LEN, body.s, p-body.s); memcpy( headers.s+CONTENT_TYPE_HDR_LEN+(p-body.s), CRLF, CRLF_LEN); /* set the body */ body.len = body.s + body.len - (p+1); body.s = p + 1; LM_DBG("setting BODY to <%.*s> <%.*s>\n", headers.len, headers.s, body.len, body.s ); } else { body.s = NULL; body.len = 0; headers.s = NULL; headers.len = 0; } /* context value */ if (p_ctx!=NULL) { if ( fixup_get_svalue(msg, (gparam_p)p_ctx, &ctx.s)<0 ) { LM_ERR("failed to extract BODY param\n"); if (p_body) pkg_free(headers.s); return -1; } LM_DBG("setting CTX AVP to <%.*s>\n", ctx.s.len, ctx.s.s); avp_list = set_avp_list( &dlg.avps ); if (!add_avp( AVP_VAL_STR, uac_ctx_avp_id, ctx)) LM_ERR("failed to add ctx AVP, ignorring...\n"); set_avp_list( avp_list ); } /* add cseq */ dlg.loc_seq.value = DEFAULT_CSEQ; dlg.loc_seq.is_set = 1; /* add callid */ generate_callid(&dlg.id.call_id); /* add FROM tag */ generate_fromtag(&dlg.id.loc_tag, &dlg.id.call_id); /* TO tag is empty as this is a initial request */ dlg.id.rem_tag.s = NULL; dlg.id.rem_tag.len = 0; /* do the actual sending now */ if ( t_uac( &method, headers.s?&headers:NULL, body.s?&body:NULL, &dlg, 0, 0, 0) <= 0 ) { LM_ERR("failed to send the request out\n"); if (headers.s) pkg_free(headers.s); if (dlg.avps) destroy_avp_list(&dlg.avps); return -1; } /* success -> do cleanup */ if (headers.s) pkg_free(headers.s); return 1; }
int t_uac_send(str *method, str *ruri, str *nexthop, str *send_socket, str *headers, str *body) { str hfb, callid; struct sip_uri p_uri, pnexthop; struct sip_msg faked_msg; struct socket_info* ssock; str saddr; int sport, sproto; int ret, fromtag, cseq_is, cseq; dlg_t dlg; uac_req_t uac_req; ret = -1; /* check and parse parameters */ if (method->len<=0){ LM_ERR("Empty method"); return -1; } if (parse_uri(ruri->s, ruri->len, &p_uri)<0){ LM_ERR("Invalid request uri \"%s\"", ruri->s); return -1; } if (nexthop->len==1 && nexthop->s[0]=='.'){ /* empty nextop */ nexthop->len=0; nexthop->s=0; }else if (nexthop->len==0){ nexthop->s=0; }else if (parse_uri(nexthop->s, nexthop->len, &pnexthop)<0){ LM_ERR("Invalid next-hop uri \"%s\"", nexthop->s); return -1; } ssock=0; saddr.s=0; saddr.len=0; if (send_socket->len==1 && send_socket->s[0]=='.'){ /* empty send socket */ send_socket->len=0; }else if (send_socket->len && (parse_phostport(send_socket->s, &saddr.s, &saddr.len, &sport, &sproto)!=0 || /* check also if it's not a MH addr. */ saddr.len==0 || saddr.s[0]=='(') ){ LM_ERR("Invalid send socket \"%s\"", send_socket->s); return -1; }else if (saddr.len && (ssock=grep_sock_info(&saddr, sport, sproto))==0){ LM_ERR("No local socket for \"%s\"", send_socket->s); return -1; } /* check headers using the SIP parser to look in the header list */ memset(&faked_msg, 0, sizeof(struct sip_msg)); faked_msg.len=headers->len; faked_msg.buf=faked_msg.unparsed=headers->s; if (parse_headers(&faked_msg, HDR_EOH_F, 0)==-1){ LM_ERR("Invalid headers"); return -1; } /* at this moment all the parameters are parsed => more sanity checks */ if (t_uac_check_msg(&faked_msg, method, body, &fromtag, &cseq_is, &cseq, &callid)<0) { LM_ERR("checking values failed\n"); goto error; } hfb.s=get_hfblock(nexthop->len? nexthop: ruri, faked_msg.headers, PROTO_NONE, ssock, &hfb.len); if (hfb.s==0){ LM_ERR("out of memory"); goto error; } /* proceed to transaction creation */ memset(&dlg, 0, sizeof(dlg_t)); /* fill call-id if call-id present or else generate a callid */ if (callid.s && callid.len) dlg.id.call_id=callid; else generate_callid(&dlg.id.call_id); /* We will not fill in dlg->id.rem_tag because * if present it will be printed within To HF */ /* Generate fromtag if not present */ if (!fromtag) { generate_fromtag(&dlg.id.loc_tag, &dlg.id.call_id); } /* Fill in CSeq */ if (cseq_is) dlg.loc_seq.value = cseq; else dlg.loc_seq.value = DEFAULT_CSEQ; dlg.loc_seq.is_set = 1; dlg.loc_uri = faked_msg.from->body; dlg.rem_uri = faked_msg.to->body; dlg.rem_target = *ruri; dlg.dst_uri = *nexthop; dlg.send_sock=ssock; memset(&uac_req, 0, sizeof(uac_req)); uac_req.method=method; uac_req.headers=&hfb; uac_req.body=body->len?body:0; uac_req.dialog=&dlg; ret = t_uac(&uac_req); if (ret <= 0) { LM_ERR("UAC error"); goto error01; } error01: if (hfb.s) pkg_free(hfb.s); error: if (faked_msg.headers) free_hdr_field_lst(faked_msg.headers); return ret; }
/** rpc t_uac version- * It expects the following list of strings as parameters: * method * request_uri * dst_uri (next hop) -- can be empty (either "" or ".", which is still * supported for backwards compatibility with fifo) * send_socket (socket from which the message will be sent) * headers (message headers separated by CRLF, at least From and To * must be present) * body (optional, might be null or completely missing) * * If all the parameters are ok it will call t_uac() using them. * Note: this version will wait for the transaction final reply * only if reply_wait is set to 1. Otherwise the rpc reply will be sent * immediately and it will be success if the paremters were ok and t_uac did * not report any error. * Note: reply waiting (reply_wait==1) is not yet supported. * @param rpc - rpc handle * @param c - rpc current context * @param reply_wait - if 1 do not generate a rpc reply until final response * for the transaction arrives, if 0 immediately send * an rpc reply (see above). */ static void rpc_t_uac(rpc_t* rpc, void* c, int reply_wait) { /* rpc params */ str method, ruri, nexthop, send_socket, headers, body; /* other internal vars.*/ str hfb, callid; struct sip_uri p_uri, pnexthop; struct sip_msg faked_msg; struct socket_info* ssock; str saddr; int sport, sproto; int ret, sip_error, err_ret, fromtag, cseq_is, cseq; char err_buf[MAX_REASON_LEN]; dlg_t dlg; uac_req_t uac_req; rpc_delayed_ctx_t* dctx; body.s=0; body.len=0; dctx=0; if (reply_wait && (rpc->capabilities == 0 || !(rpc->capabilities(c) & RPC_DELAYED_REPLY))) { rpc->fault(c, 600, "Reply wait/async mode not supported" " by this rpc transport"); return; } ret=rpc->scan(c, "SSSSS*S", &method, &ruri, &nexthop, &send_socket, &headers, &body); if (ret<5 && ! (-ret == 5)){ rpc->fault(c, 400, "too few parameters (%d/5)", ret?ret:-ret); return; } /* check and parse parameters */ if (method.len==0){ rpc->fault(c, 400, "Empty method"); return; } if (parse_uri(ruri.s, ruri.len, &p_uri)<0){ rpc->fault(c, 400, "Invalid request uri \"%s\"", ruri.s); return; } /* old fifo & unixsock backwards compatibility for nexthop: '.' is still allowed */ if (nexthop.len==1 && nexthop.s[0]=='.'){ /* empty nextop */ nexthop.len=0; nexthop.s=0; }else if (nexthop.len==0){ nexthop.s=0; }else if (parse_uri(nexthop.s, nexthop.len, &pnexthop)<0){ rpc->fault(c, 400, "Invalid next-hop uri \"%s\"", nexthop.s); return; } /* kamailio backwards compatibility for send_socket: '.' is still allowed for an empty socket */ ssock=0; saddr.s=0; saddr.len=0; if (send_socket.len==1 && send_socket.s[0]=='.'){ /* empty send socket */ send_socket.len=0; }else if (send_socket.len && (parse_phostport(send_socket.s, &saddr.s, &saddr.len, &sport, &sproto)!=0 || /* check also if it's not a MH addr. */ saddr.len==0 || saddr.s[0]=='(') ){ rpc->fault(c, 400, "Invalid send socket \"%s\"", send_socket.s); return; }else if (saddr.len && (ssock=grep_sock_info(&saddr, sport, sproto))==0){ rpc->fault(c, 400, "No local socket for \"%s\"", send_socket.s); return; } /* check headers using the SIP parser to look in the header list */ memset(&faked_msg, 0, sizeof(struct sip_msg)); faked_msg.len=headers.len; faked_msg.buf=faked_msg.unparsed=headers.s; if (parse_headers(&faked_msg, HDR_EOH_F, 0)==-1){ rpc->fault(c, 400, "Invalid headers"); return; } /* at this moment all the parameters are parsed => more sanity checks */ if (rpc_uac_check_msg(rpc, c, &faked_msg, &method, &body, &fromtag, &cseq_is, &cseq, &callid)<0) goto error; hfb.s=get_hfblock(nexthop.len? &nexthop: &ruri, faked_msg.headers, PROTO_NONE, ssock, &hfb.len); if (hfb.s==0){ rpc->fault(c, 500, "out of memory"); goto error; } /* proceed to transaction creation */ memset(&dlg, 0, sizeof(dlg_t)); /* fill call-id if call-id present or else generate a callid */ if (callid.s && callid.len) dlg.id.call_id=callid; else generate_callid(&dlg.id.call_id); /* We will not fill in dlg->id.rem_tag because * if present it will be printed within To HF */ /* Generate fromtag if not present */ if (!fromtag) { generate_fromtag(&dlg.id.loc_tag, &dlg.id.call_id); } /* Fill in CSeq */ if (cseq_is) dlg.loc_seq.value = cseq; else dlg.loc_seq.value = DEFAULT_CSEQ; dlg.loc_seq.is_set = 1; dlg.loc_uri = faked_msg.from->body; dlg.rem_uri = faked_msg.to->body; dlg.rem_target = ruri; dlg.dst_uri = nexthop; dlg.send_sock=ssock; memset(&uac_req, 0, sizeof(uac_req)); uac_req.method=&method; uac_req.headers=&hfb; uac_req.body=body.len?&body:0; uac_req.dialog=&dlg; if (reply_wait){ dctx=rpc->delayed_ctx_new(c); if (dctx==0){ rpc->fault(c, 500, "internal error: failed to create context"); return; } uac_req.cb=rpc_uac_callback; uac_req.cbp=dctx; uac_req.cb_flags=TMCB_LOCAL_COMPLETED; /* switch to dctx, in case adding the callback fails and we want to still send a reply */ rpc=&dctx->rpc; c=dctx->reply_ctx; } ret = t_uac(&uac_req); if (ret <= 0) { err_ret = err2reason_phrase(ret, &sip_error, err_buf, sizeof(err_buf), "RPC/UAC") ; if (err_ret > 0 ) { rpc->fault(c, sip_error, "%s", err_buf); } else { rpc->fault(c, 500, "RPC/UAC error"); } if (dctx) rpc->delayed_ctx_close(dctx); goto error01; } error01: if (hfb.s) pkg_free(hfb.s); error: if (faked_msg.headers) free_hdr_field_lst(faked_msg.headers); }