123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543 |
- /*
- * $Id$
- *
- * Copyright (C) 2001-2003 FhG Fokus
- *
- * This file is part of ser, a free SIP server.
- *
- * ser is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * For a license to use the ser software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- * [email protected]
- *
- * ser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * History:
- * -------
- * 2003-07-29: file created (bogdan)
- * 2004-06-14: flag CPL_IS_STATEFUL is set now immediately after the
- * transaction is created (bogdan)
- */
- #include "../../modules/tm/h_table.h"
- #include "../../parser/contact/parse_contact.h"
- #define duplicate_str( _orig_ , _new_ ) \
- do {\
- (_new_) = (str*)shm_malloc(sizeof(str)+(_orig_)->len);\
- if (!(_new_)) goto mem_error;\
- (_new_)->len = (_orig_)->len;\
- (_new_)->s = (char*)((_new_))+sizeof(str);\
- memcpy((_new_)->s,(_orig_)->s,(_orig_)->len);\
- } while(0)
- #define search_and_duplicate_hdr( _intr_ , _field_ , _name_ , _sfoo_ ) \
- do {\
- if (!(_intr_)->_field_) {\
- if (!(_intr_)->msg->_field_) { \
- if (parse_headers((_intr_)->msg,_name_,0)==-1) {\
- LOG(L_ERR,"ERROR:run_proxy: bad %llx hdr\n",_name_);\
- goto runtime_error;\
- } else if ( !(_intr_)->msg->_field_) {\
- (_intr_)->_field_ = STR_NOT_FOUND;\
- } else {\
- (_sfoo_) = &((_intr_)->msg->_field_->body);\
- duplicate_str( (_sfoo_) , (_intr_)->_field_ );\
- }\
- } else {\
- (_sfoo_) = &((_intr_)->msg->_field_->body);\
- duplicate_str( (_sfoo_) , (_intr_)->_field_ );\
- }\
- } else {\
- (_sfoo_) = (_intr_)->_field_;\
- duplicate_str( (_sfoo_) , (_intr_)->_field_ );\
- }\
- }while(0)
- static inline int parse_q(str *q, unsigned int *prio)
- {
- if (q->s[0]=='0')
- *prio=0;
- else if (q->s[0]=='1')
- *prio=10;
- else
- goto error;
- if (q->s[1]!='.')
- goto error;
- if (q->s[2]<'0' || q->s[2]>'9')
- goto error;
- *prio += q->s[2] - '0';
- if (*prio>10)
- goto error;
- return 0;
- error:
- LOG(L_ERR,"ERROR:cpl-c:parse_q:bad q param <%.*s>\n",q->len,q->s);
- return -1;
- }
- static inline int add_contacts_to_loc_set(struct sip_msg* msg,
- struct location **loc_set)
- {
- struct sip_uri uri;
- struct contact *contacts;
- unsigned int prio;
- /* we need to have the contact header */
- if (msg->contact==0) {
- /* find and parse the Contact header */
- if ((parse_headers(msg, HDR_CONTACT_F, 0)==-1) || (msg->contact==0) ) {
- LOG(L_ERR,"ERROR:cpl-c:add_contacts_to_loc_set: error parsing or "
- "no Contact hdr found!\n");
- goto error;
- }
- }
- /* extract from contact header the all the addresses */
- if (parse_contact( msg->contact )!=0) {
- LOG(L_ERR,"ERROR:cpl-c:add_contacts_to_loc_set: unable to parse "
- "Contact hdr!\n");
- goto error;
- }
- /* in contact hdr, in parsed attr, we should have a list of contacts */
- if ( msg->contact->parsed ) {
- contacts = ((struct contact_body*)msg->contact->parsed)->contacts;
- for( ; contacts ; contacts=contacts->next) {
- /* check if the contact is a valid sip uri */
- if (parse_uri( contacts->uri.s, contacts->uri.len , &uri)!=0) {
- continue;
- }
- /* convert the q param to int value (if any) */
- if (contacts->q) {
- if (parse_q( &(contacts->q->body), &prio )!=0)
- continue;
- } else {
- prio = 10; /* set default to minimum */
- }
- /* add the uri to location set */
- if (add_location( loc_set, &contacts->uri,prio, CPL_LOC_DUPL)!=0) {
- LOG(L_ERR,"ERROR:cpl-c:add_contacts_to_loc_set: unable to add "
- "<%.*s>\n",contacts->uri.len,contacts->uri.s);
- }
- }
- }
- return 0;
- error:
- return -1;
- }
- static void reply_callback( struct cell* t, int type, struct tmcb_params* ps)
- {
- struct cpl_interpreter *intr = (struct cpl_interpreter*)(*(ps->param));
- struct location *loc = 0;
- int rez;
- if (intr==0) {
- LOG(L_WARN,"WARNING:cpl-c:reply_callback: param=0 for callback %d,"
- " transaction=%p \n",type,t);
- return;
- }
- if (type&TMCB_RESPONSE_OUT) {
- /* the purpose of the final reply is to trash down the interpreter
- * structure! it's the safest place to do that, since this callback
- * it's called only once per transaction for final codes (>=200) ;-) */
- if (ps->code>=200) {
- DBG("DEBUG:cpl-c:final_reply: code=%d -------------->\n"
- " --------------------------> final reply received\n",
- ps->code);
- /* CPL interpretation done, call established -> destroy */
- free_cpl_interpreter( intr );
- /* set to zero the param callback*/
- *(ps->param) = 0;
- }
- return;
- } else if (!(type&TMCB_ON_FAILURE)) {
- LOG(L_ERR,"BUG:cpl-c:reply_callback: unknown type %d\n",type);
- goto exit;
- }
- DBG("DEBUG:cpl-c:negativ_reply: ------------------------------>\n"
- " ---------------------------------> negativ reply received\n");
- intr->flags |= CPL_PROXY_DONE;
- intr->msg = ps->req;
- /* if it's a redirect-> do I have to added to the location set ? */
- if (intr->proxy.recurse && (ps->code)/100==3) {
- DBG("DEBUG:cpl-c:negativ_reply: recurse level %d processing..\n",
- intr->proxy.recurse);
- intr->proxy.recurse--;
- /* get the locations from the Contact */
- add_contacts_to_loc_set( ps->rpl, &(intr->loc_set));
- switch (intr->proxy.ordering) {
- case SEQUENTIAL_VAL:
- /* update the last_to_proxy to last location from set */
- if (intr->proxy.last_to_proxy==0) {
- /* the pointer went through entire old set -> set it to the
- * updated set, from the beginning */
- if (intr->loc_set==0)
- /* the updated set is also empty -> proxy ended */
- break;
- intr->proxy.last_to_proxy = intr->loc_set;
- }
- while(intr->proxy.last_to_proxy->next)
- intr->proxy.last_to_proxy=intr->proxy.last_to_proxy->next;
- break;
- case PARALLEL_VAL:
- /* push the whole new location set to be proxy */
- intr->proxy.last_to_proxy = intr->loc_set;
- break;
- case FIRSTONLY_VAL:
- intr->proxy.last_to_proxy = 0;
- break;
- }
- }
- /* the current proxying failed -> do I have another location to try ?
- * This applies only for SERIAL forking or if RECURSE is set */
- if (intr->proxy.last_to_proxy) {
- /* continue proxying */
- DBG("DEBUG:cpl-c:failed_reply: resuming proxying....\n");
- switch (intr->proxy.ordering) {
- case PARALLEL_VAL:
- /* I get here only if I got a 3xx and RECURSE in on ->
- * forward to all location from location set */
- intr->proxy.last_to_proxy = 0;
- cpl_proxy_to_loc_set(intr->msg,&(intr->loc_set),intr->flags );
- break;
- case SEQUENTIAL_VAL:
- /* place a new branch to the next location from loc. set*/
- loc = remove_first_location( &(intr->loc_set) );
- /*print_location_set(intr->loc_set);*/
- /* update (if necessary) the last_to_proxy location */
- if (intr->proxy.last_to_proxy==loc)
- intr->proxy.last_to_proxy = 0;
- cpl_proxy_to_loc_set(intr->msg,&loc,intr->flags );
- break;
- default:
- LOG(L_CRIT,"BUG:cpl_c:failed_reply: unexpected ordering found "
- "when continuing proxying (%d)\n",intr->proxy.ordering);
- goto exit;
- }
- /* nothing more to be done */
- return;
- } else {
- /* done with proxying.... -> process the final response */
- DBG("DEBUG:cpl-c:failed_reply:final_reply: got a final %d\n",ps->code);
- intr->ip = 0;
- if (ps->code==486 || ps->code==600) {
- /* busy response */
- intr->ip = intr->proxy.busy;
- } else if (ps->code==408) {
- /* request timeout -> no response */
- intr->ip = intr->proxy.noanswer;
- } else if (((ps->code)/100)==3) {
- /* redirection */
- /* add to the location list all the addresses from Contact */
- add_contacts_to_loc_set( ps->rpl, &(intr->loc_set));
- print_location_set( intr->loc_set );
- intr->ip = intr->proxy.redirect;
- } else {
- /* generic failure */
- intr->ip = intr->proxy.failure;
- }
- if (intr->ip==0)
- intr->ip = (intr->proxy.default_)?
- intr->proxy.default_:DEFAULT_ACTION;
- if (intr->ip!=DEFAULT_ACTION)
- intr->ip = get_first_child( intr->ip );
- if( intr->ip==DEFAULT_ACTION)
- rez = run_default(intr);
- else
- rez = cpl_run_script(intr);
- switch ( rez ) {
- case SCRIPT_END:
- /* we don't need to free the interpreter here since it will
- * be freed in the final_reply callback */
- case SCRIPT_TO_BE_CONTINUED:
- return;
- case SCRIPT_RUN_ERROR:
- case SCRIPT_FORMAT_ERROR:
- goto exit;
- default:
- LOG(L_CRIT,"BUG:cpl-c:failed_reply: improper result %d\n",
- rez);
- goto exit;
- }
- }
- exit:
- /* in case of error the default response chosen by ser at the last
- * proxying will be forwarded to the UAC */
- free_cpl_interpreter( intr );
- /* set to zero the param callback*/
- *(ps->param) = 0;
- return;
- }
- static inline char *run_proxy( struct cpl_interpreter *intr )
- {
- unsigned short attr_name;
- unsigned short n;
- char *kid;
- char *p;
- int i;
- str *s;
- struct location *loc;
- int_str tmp;
- intr->proxy.ordering = PARALLEL_VAL;
- intr->proxy.recurse = (unsigned short)cpl_env.proxy_recurse;
- /* identify the attributes */
- for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) {
- get_basic_attr( p, attr_name, n, intr, script_error);
- switch (attr_name) {
- case TIMEOUT_ATTR:
- if (cpl_env.timer_avp.n || cpl_env.timer_avp.s.s) {
- tmp.n=(int)n;
- if ( add_avp( AVP_TRACK_TO | cpl_env.timer_avp_type,
- cpl_env.timer_avp, tmp)<0) {
- LOG(L_ERR,"ERROR:run_proxy: unable to set "
- "timer AVP\n");
- /* continue */
- }
- }
- break;
- case RECURSE_ATTR:
- switch (n) {
- case NO_VAL:
- intr->proxy.recurse = 0;
- break;
- case YES_VAL:
- /* already set as default */
- break;
- default:
- LOG(L_ERR,"ERROR:run_proxy: invalid value (%u) found"
- " for attr. RECURSE in PROXY node!\n",n);
- goto script_error;
- }
- break;
- case ORDERING_ATTR:
- if (n!=PARALLEL_VAL && n!=SEQUENTIAL_VAL && n!=FIRSTONLY_VAL){
- LOG(L_ERR,"ERROR:run_proxy: invalid value (%u) found"
- " for attr. ORDERING in PROXY node!\n",n);
- goto script_error;
- }
- intr->proxy.ordering = n;
- break;
- default:
- LOG(L_ERR,"ERROR:run_proxy: unknown attribute (%d) in"
- "PROXY node\n",attr_name);
- goto script_error;
- }
- }
- intr->proxy.busy = intr->proxy.noanswer = 0;
- intr->proxy.redirect = intr->proxy.failure = intr->proxy.default_ = 0;
- /* this is quite an "expensive" node to run, so let's make some checking
- * before getting deeply into it */
- for( i=0 ; i<NR_OF_KIDS(intr->ip) ; i++ ) {
- kid = intr->ip + KID_OFFSET(intr->ip,i);
- check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error);
- switch ( NODE_TYPE(kid) ) {
- case BUSY_NODE :
- intr->proxy.busy = kid;
- break;
- case NOANSWER_NODE:
- intr->proxy.noanswer = kid;
- break;
- case REDIRECTION_NODE:
- intr->proxy.redirect = kid;
- break;
- case FAILURE_NODE:
- intr->proxy.failure = kid;
- break;
- case DEFAULT_NODE:
- intr->proxy.default_ = kid;
- break;
- default:
- LOG(L_ERR,"ERROR:run_proxy: unknown output node type"
- " (%d) for PROXY node\n",NODE_TYPE(kid));
- goto script_error;
- }
- }
- /* if the location set if empty, I will go directly on failure/default */
- if (intr->loc_set==0) {
- DBG("DEBUG:run_proxy: location set found empty -> going on "
- "failure/default branch\n");
- if (intr->proxy.failure)
- return get_first_child(intr->proxy.failure);
- else if (intr->proxy.default_)
- return get_first_child(intr->proxy.default_);
- else return DEFAULT_ACTION;
- }
- /* if it's the first execution of a proxy node, force parsing of the needed
- * headers and duplicate them in shared memory */
- if (!(intr->flags&CPL_PROXY_DONE)) {
- /* user name is already in shared memory */
- /* requested URI - mandatory in SIP msg (cannot be STR_NOT_FOUND) */
- s = GET_RURI( intr->msg );
- duplicate_str( s , intr->ruri );
- intr->flags |= CPL_RURI_DUPLICATED;
- /* TO header - mandatory in SIP msg (cannot be STR_NOT_FOUND) */
- if (!intr->to) {
- if (!intr->msg->to &&
- (parse_headers(intr->msg,HDR_TO_F,0)==-1 || !intr->msg->to)) {
- LOG(L_ERR,"ERROR:run_proxy: bad msg or missing TO header\n");
- goto runtime_error;
- }
- s = &(get_to(intr->msg)->uri);
- } else {
- s = intr->to;
- }
- duplicate_str( s , intr->to );
- intr->flags |= CPL_TO_DUPLICATED;
- /* FROM header - mandatory in SIP msg (cannot be STR_NOT_FOUND) */
- if (!intr->from) {
- if (parse_from_header( intr->msg )==-1)
- goto runtime_error;
- s = &(get_from(intr->msg)->uri);
- } else {
- s = intr->from;
- }
- duplicate_str( s , intr->from );
- intr->flags |= CPL_FROM_DUPLICATED;
- /* SUBJECT header - optional in SIP msg (can be STR_NOT_FOUND) */
- if (intr->subject!=STR_NOT_FOUND) {
- search_and_duplicate_hdr(intr,subject,HDR_SUBJECT_F,s);
- if (intr->subject!=STR_NOT_FOUND)
- intr->flags |= CPL_SUBJECT_DUPLICATED;
- }
- /* ORGANIZATION header - optional in SIP msg (can be STR_NOT_FOUND) */
- if ( intr->organization!=STR_NOT_FOUND) {
- search_and_duplicate_hdr(intr,organization,HDR_ORGANIZATION_F,s);
- if ( intr->organization!=STR_NOT_FOUND)
- intr->flags |= CPL_ORGANIZATION_DUPLICATED;
- }
- /* USER_AGENT header - optional in SIP msg (can be STR_NOT_FOUND) */
- if (intr->user_agent!=STR_NOT_FOUND) {
- search_and_duplicate_hdr(intr,user_agent,HDR_USERAGENT_F,s);
- if (intr->user_agent!=STR_NOT_FOUND)
- intr->flags |= CPL_USERAGENT_DUPLICATED;
- }
- /* ACCEPT_LANGUAGE header - optional in SIP msg
- * (can be STR_NOT_FOUND) */
- if (intr->accept_language!=STR_NOT_FOUND) {
- search_and_duplicate_hdr(intr,accept_language,
- HDR_ACCEPTLANGUAGE_F,s);
- if (intr->accept_language!=STR_NOT_FOUND)
- intr->flags |= CPL_ACCEPTLANG_DUPLICATED;
- }
- /* PRIORITY header - optional in SIP msg (can be STR_NOT_FOUND) */
- if (intr->priority!=STR_NOT_FOUND) {
- search_and_duplicate_hdr(intr,priority,HDR_PRIORITY_F,s);
- if (intr->priority!=STR_NOT_FOUND)
- intr->flags |= CPL_PRIORITY_DUPLICATED;
- }
- /* now is the first time doing proxy, so I can still be stateless;
- * as proxy is done all the time stateful, I have to switch from
- * stateless to stateful if necessary. */
- if ( !(intr->flags&CPL_IS_STATEFUL) ) {
- i = cpl_fct.tmb.t_newtran( intr->msg );
- if (i<0) {
- LOG(L_ERR,"ERROR:cpl-c:run_proxy: failed to build new "
- "transaction!\n");
- goto runtime_error;
- } else if (i==0) {
- LOG(L_ERR,"ERROR:cpl-c:run_proxy: processed INVITE is a "
- "retransmission!\n");
- /* instead of generating an error is better just to break the
- * script by returning EO_SCRIPT */
- return EO_SCRIPT;
- }
- intr->flags |= CPL_IS_STATEFUL;
- }
- /* as I am interested in getting the responses back - I need to install
- * some callback functions for replies */
- if (cpl_fct.tmb.register_tmcb(intr->msg,0,
- TMCB_ON_FAILURE|TMCB_RESPONSE_OUT,reply_callback,(void*)intr, 0) <= 0){
- LOG(L_ERR, "ERROR:cpl_c:run_proxy: failed to register "
- "TMCB_RESPONSE_OUT callback\n");
- goto runtime_error;
- }
- }
- switch (intr->proxy.ordering) {
- case FIRSTONLY_VAL:
- /* forward the request only to the first address from loc. set */
- /* location set cannot be empty -> was checked before */
- loc = remove_first_location( &(intr->loc_set) );
- intr->proxy.last_to_proxy = 0;
- /* set the new ip before proxy -> otherwise race cond with rpls */
- intr->ip = CPL_TO_CONTINUE;
- if (cpl_proxy_to_loc_set(intr->msg,&loc,intr->flags )==-1)
- goto runtime_error;
- break;
- case PARALLEL_VAL:
- /* forward to all location from location set */
- intr->proxy.last_to_proxy = 0;
- /* set the new ip before proxy -> otherwise race cond with rpls */
- intr->ip = CPL_TO_CONTINUE;
- if (cpl_proxy_to_loc_set(intr->msg,&(intr->loc_set),intr->flags)
- ==-1)
- goto runtime_error;
- break;
- case SEQUENTIAL_VAL:
- /* forward the request one at the time to all addresses from
- * loc. set; location set cannot be empty -> was checked before */
- /* use the first location from set */
- loc = remove_first_location( &(intr->loc_set) );
- /* set as the last_to_proxy the last location from set */
- intr->proxy.last_to_proxy = intr->loc_set;
- while (intr->proxy.last_to_proxy&&intr->proxy.last_to_proxy->next)
- intr->proxy.last_to_proxy = intr->proxy.last_to_proxy->next;
- /* set the new ip before proxy -> otherwise race cond with rpls */
- intr->ip = CPL_TO_CONTINUE;
- if (cpl_proxy_to_loc_set(intr->msg,&loc,intr->flags)==-1)
- goto runtime_error;
- break;
- }
- return CPL_TO_CONTINUE;
- script_error:
- return CPL_SCRIPT_ERROR;
- mem_error:
- LOG(L_ERR,"ERROR:run_proxy: no more free shm memory\n");
- runtime_error:
- return CPL_RUNTIME_ERROR;
- }
|