diff --git a/src/modules/tm/doc/functions.xml b/src/modules/tm/doc/functions.xml index ddc7dd9ed46..3b20f3dab71 100644 --- a/src/modules/tm/doc/functions.xml +++ b/src/modules/tm/doc/functions.xml @@ -1763,6 +1763,54 @@ if (t_is_retr_async_reply()) { xlog("L_DBG", "Dropping retransmitted reply which is still currently suspended\n"); drop(); } +... + + + +
+ + <function>t_uac_send(method, ruri, nexthop, socket, headers, body)</function> + + + Send a UAC request. + + It returns true (1) if successful, false (-1) on failure. + The parameters: + + + method - SIP method. + + + + ruri - request URI. + + + + nexthop - destination URI (can be empty). + + + + socket - local send socket (can be empty). + + + + headers - SIP headers. At least From and To + have to be provided. It can include From/To tags, Call-ID, CSeq. If + body is provided, then Content-Type header must exist. + + + + body - SIP message body (can be empty). + + + + + + <function>t_uac_send</function> usage + +... +t_uac_send("OPTIONS", "sip:alice@kamailio.org", "", "", + "From: bob@kamailio.org;tag=2w3e\r\nTo: bob@kamailio.org", ""); ... diff --git a/src/modules/tm/rpc_uac.c b/src/modules/tm/rpc_uac.c index 3444cb527cd..06990cc2ba1 100644 --- a/src/modules/tm/rpc_uac.c +++ b/src/modules/tm/rpc_uac.c @@ -98,7 +98,7 @@ static int rpc_uac_check_msg(rpc_t *rpc, void* c, if (ch >= '0' && ch <= '9' ) { *cseq = (*cseq) * 10 + ch - '0'; } else { - DBG("check_msg: Found non-numerical in CSeq: <%i>='%c'\n", + LM_DBG("Found non-numerical in CSeq: <%i>='%c'\n", (unsigned int)ch, ch); rpc->fault(c, 400, "Non-numerical CSeq"); goto err; @@ -128,9 +128,6 @@ static int rpc_uac_check_msg(rpc_t *rpc, void* c, } - - - /** construct a "header block" from a header list. * * @return pkg_malloc'ed header block on success (with *l set to its length), @@ -184,8 +181,7 @@ static char *get_hfblock(str *uri, struct hdr_field *hf, int proto, uri2dst(&di, 0, uri, proto) #endif /* USE_DNS_FAILOVER */ == 0 ){ - LOG(L_ERR, "ERROR: get_hfblock: send_sock" - " failed\n"); + LM_ERR("send_sock failed\n"); goto error; } si_get_signaling_data(di.send_sock, &sock_name, &portname); @@ -208,7 +204,7 @@ static char *get_hfblock(str *uri, struct hdr_field *hf, int proto, } } /* possible substitute */ } /* substitution loop */ - DBG("get_hfblock: one more hf processed\n"); + LM_DBG("one more hf processed\n"); } /* header loop */ if(total_len==0) { @@ -219,7 +215,7 @@ static char *get_hfblock(str *uri, struct hdr_field *hf, int proto, /* construct a single header block now */ ret = pkg_malloc(total_len); if (!ret) { - LOG(L_ERR, "get_hfblock: no pkg mem for hf block\n"); + LM_ERR("no pkg mem for hf block\n"); goto error; } i = sl.next; @@ -329,7 +325,7 @@ static void rpc_print_uris(rpc_t* rpc, void* c, struct sip_msg* reply) } memset(dlg, 0, sizeof(dlg_t)); if (dlg_response_uac(dlg, reply, TARGET_REFRESH_UNKNOWN) < 0) { - ERR("failure while filling dialog structure\n"); + LM_ERR("failure while filling dialog structure\n"); free_dlg(dlg); return; } @@ -577,7 +573,6 @@ static void rpc_t_uac(rpc_t* rpc, void* c, int reply_wait) } - /** t_uac with no reply waiting. * @see rpc_t_uac. */ @@ -594,4 +589,200 @@ void rpc_t_uac_wait(rpc_t* rpc, void* c) rpc_t_uac(rpc, c, 1); } + +static int t_uac_check_msg(struct sip_msg* msg, + str* method, str* body, + int* fromtag, int *cseq_is, int* cseq, + str* callid) +{ + struct to_body* parsed_from; + struct cseq_body *parsed_cseq; + int i; + char ch; + + if (body->len && !msg->content_type) { + LM_ERR("Content-Type missing"); + goto err; + } + + if (body->len && msg->content_length) { + LM_ERR("Content-Length disallowed"); + goto err; + } + + if (!msg->to) { + LM_ERR("To missing"); + goto err; + } + + if (!msg->from) { + LM_ERR("From missing"); + goto err; + } + + /* we also need to know if there is from-tag and add it otherwise */ + if (parse_from_header(msg) < 0) { + LM_ERR("Error in From"); + goto err; + } + + parsed_from = (struct to_body*)msg->from->parsed; + *fromtag = parsed_from->tag_value.s && parsed_from->tag_value.len; + + *cseq = 0; + if (msg->cseq && (parsed_cseq = get_cseq(msg))) { + *cseq_is = 1; + for (i = 0; i < parsed_cseq->number.len; i++) { + ch = parsed_cseq->number.s[i]; + if (ch >= '0' && ch <= '9' ) { + *cseq = (*cseq) * 10 + ch - '0'; + } else { + DBG("check_msg: Found non-numerical in CSeq: <%i>='%c'\n", + (unsigned int)ch, ch); + LM_ERR("Non-numerical CSeq"); + goto err; + } + } + + if (parsed_cseq->method.len != method->len || + memcmp(parsed_cseq->method.s, method->s, method->len) !=0 ) { + LM_ERR("CSeq method mismatch"); + goto err; + } + } else { + *cseq_is = 0; + } + + if (msg->callid) { + callid->s = msg->callid->body.s; + callid->len = msg->callid->body.len; + } else { + callid->s = 0; + callid->len = 0; + } + return 0; + +err: + 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, sip_error, err_ret, fromtag, cseq_is, cseq; + char err_buf[MAX_REASON_LEN]; + 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; +} + /* vi: set ts=4 sw=4 tw=79:ai:cindent: */ diff --git a/src/modules/tm/rpc_uac.h b/src/modules/tm/rpc_uac.h index 524e024aa29..1f4c4e2c4fc 100644 --- a/src/modules/tm/rpc_uac.h +++ b/src/modules/tm/rpc_uac.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2009 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any @@ -17,12 +17,16 @@ #ifndef __rpc_uac_h #define __rpc_uac_h +#include "../../core/str.h" #include "../../core/rpc.h" void rpc_t_uac_start(rpc_t* rpc, void* c); void rpc_t_uac_wait(rpc_t* rpc, void* c); +int t_uac_send(str *method, str *ruri, str *nexthop, str *send_socket, + str *headers, str *body); + #endif /*__rpc_uac_h*/ /* vi: set ts=4 sw=4 tw=79:ai:cindent: */ diff --git a/src/modules/tm/tm.c b/src/modules/tm/tm.c index a223e7efda9..c6269c2fcc0 100644 --- a/src/modules/tm/tm.c +++ b/src/modules/tm/tm.c @@ -212,6 +212,8 @@ static int w_t_save_lumps(struct sip_msg* msg, char* foo, char* bar); static int w_t_check_trans(struct sip_msg* msg, char* foo, char* bar); static int w_t_is_set(struct sip_msg* msg, char* target, char* bar); static int w_t_use_uac_headers(sip_msg_t* msg, char* foo, char* bar); +static int w_t_uac_send(sip_msg_t* msg, char* pmethod, char* pruri, + char* pnexthop, char* psock, char *phdrs, char* pbody); /* by default the fr timers avps are not set, so that the avps won't be @@ -402,6 +404,8 @@ static cmd_export_t cmds[]={ ANY_ROUTE }, {"t_use_uac_headers", w_t_use_uac_headers, 0, 0, ANY_ROUTE }, + {"t_uac_send", (cmd_function)w_t_uac_send, 6, fixup_spve_all, + ANY_ROUTE }, {"t_load_contacts", t_load_contacts, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, @@ -2280,6 +2284,47 @@ static int w_t_use_uac_headers(sip_msg_t* msg, char* foo, char* bar) return 1; } +static int w_t_uac_send(sip_msg_t* msg, char* pmethod, char* pruri, + char* pnexthop, char* psock, char *phdrs, char* pbody) +{ + str method = STR_NULL; + str ruri = STR_NULL; + str nexthop = STR_NULL; + str send_socket = STR_NULL; + str headers = STR_NULL; + str body = STR_NULL; + + if(fixup_get_svalue(msg, (gparam_t*)pmethod, &method)!=0) { + LM_ERR("invalid method parameter"); + return -1; + } + if(fixup_get_svalue(msg, (gparam_t*)pruri, &ruri)!=0) { + LM_ERR("invalid ruri parameter"); + return -1; + } + if(fixup_get_svalue(msg, (gparam_t*)pnexthop, &nexthop)!=0) { + LM_ERR("invalid nexthop parameter"); + return -1; + } + if(fixup_get_svalue(msg, (gparam_t*)psock, &send_socket)!=0) { + LM_ERR("invalid send socket parameter"); + return -1; + } + if(fixup_get_svalue(msg, (gparam_t*)phdrs, &headers)!=0) { + LM_ERR("invalid headers parameter"); + return -1; + } + if(fixup_get_svalue(msg, (gparam_t*)pbody, &body)!=0) { + LM_ERR("invalid body parameter"); + return -1; + } + + if(t_uac_send(&method, &ruri, &nexthop, &send_socket, &headers, &body)<0) { + return -1; + } + return 1; +} + /* rpc docs */ static const char* rpc_cancel_doc[2] = {