modules_k/cpl-c/cpl_proxy.h

00001 /*
00002  * $Id$
00003  *
00004  * Copyright (C) 2001-2003 FhG Fokus
00005  *
00006  * This file is part of Kamailio, a free SIP server.
00007  *
00008  * Kamailio is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version
00012  *
00013  * Kamailio is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  * History:
00023  * -------
00024  * 2003-07-29: file created (bogdan)
00025  * 2004-06-14: flag CPL_IS_STATEFUL is set now immediately after the 
00026  *             transaction is created (bogdan)
00027  */
00028 
00029 #include "../../modules/tm/h_table.h"
00030 #include "../../parser/contact/parse_contact.h"
00031 
00032 
00033 #define duplicate_str( _orig_ , _new_ ) \
00034         do {\
00035                 (_new_) = (str*)shm_malloc(sizeof(str)+(_orig_)->len);\
00036                 if (!(_new_)) goto mem_error;\
00037                 (_new_)->len = (_orig_)->len;\
00038                 (_new_)->s = (char*)((_new_))+sizeof(str);\
00039                 memcpy((_new_)->s,(_orig_)->s,(_orig_)->len);\
00040         } while(0)
00041 
00042 #define search_and_duplicate_hdr( _intr_ , _field_ , _name_ , _sfoo_ ) \
00043         do {\
00044                 if (!(_intr_)->_field_) {\
00045                         if (!(_intr_)->msg->_field_) { \
00046                                 if (parse_headers((_intr_)->msg,_name_,0)==-1) {\
00047                                         LM_ERR("bad %llx hdr\n",_name_);\
00048                                         goto runtime_error;\
00049                                 } else if ( !(_intr_)->msg->_field_) {\
00050                                         (_intr_)->_field_ = STR_NOT_FOUND;\
00051                                 } else {\
00052                                         (_sfoo_) = &((_intr_)->msg->_field_->body);\
00053                                         duplicate_str( (_sfoo_) , (_intr_)->_field_ );\
00054                                 }\
00055                         } else {\
00056                                 (_sfoo_) = &((_intr_)->msg->_field_->body);\
00057                                 duplicate_str( (_sfoo_) , (_intr_)->_field_ );\
00058                         }\
00059                 } else {\
00060                         (_sfoo_) = (_intr_)->_field_;\
00061                         duplicate_str( (_sfoo_) , (_intr_)->_field_ );\
00062                 }\
00063         }while(0)
00064 
00065 
00066 
00067 static inline int parse_q(str *q, unsigned int *prio)
00068 {
00069         if (q->s[0]=='0')
00070                 *prio=0;
00071         else if (q->s[0]=='1')
00072                 *prio=10;
00073         else
00074                 goto error;
00075         if (q->s[1]!='.')
00076                 goto error;
00077         if (q->s[2]<'0' || q->s[2]>'9')
00078                 goto error;
00079         *prio += q->s[2] - '0';
00080         if (*prio>10)
00081                 goto error;
00082 
00083         return 0;
00084 error:
00085         LM_ERR("bad q param <%.*s>\n",q->len,q->s);
00086         return -1;
00087 }
00088 
00089 
00090 
00091 static inline int add_contacts_to_loc_set(struct sip_msg* msg,
00092                                                                                                         struct location **loc_set)
00093 {
00094         struct sip_uri uri;
00095         struct contact *contacts;
00096         unsigned int prio;
00097 
00098         /* we need to have the contact header */
00099         if (msg->contact==0) {
00100                 /* find and parse the Contact header */
00101                 if ((parse_headers(msg, HDR_CONTACT_F, 0)==-1) || (msg->contact==0)) {
00102                         LM_ERR("error parsing or no Contact hdr found!\n");
00103                         goto error;
00104                 }
00105         }
00106 
00107         /* extract from contact header the all the addresses */
00108         if (parse_contact( msg->contact )!=0) {
00109                 LM_ERR("unable to parse Contact hdr!\n");
00110                 goto error;
00111         }
00112 
00113         /* in contact hdr, in parsed attr, we should have a list of contacts */
00114         if ( msg->contact->parsed ) {
00115                 contacts = ((struct contact_body*)msg->contact->parsed)->contacts;
00116                 for( ; contacts ; contacts=contacts->next) {
00117                         /* check if the contact is a valid sip uri */
00118                         if (parse_uri( contacts->uri.s, contacts->uri.len , &uri)!=0) {
00119                                 continue;
00120                         }
00121                         /* convert the q param to int value (if any) */
00122                         if (contacts->q) {
00123                                 if (parse_q( &(contacts->q->body), &prio )!=0)
00124                                         continue;
00125                         } else {
00126                                 prio = 10; /* set default to minimum */
00127                         }
00128                         /* add the uri to location set */
00129                         if (add_location(loc_set,&contacts->uri,0,prio,CPL_LOC_DUPL)!=0) {
00130                                 LM_ERR("unable to add <%.*s>\n",
00131                                         contacts->uri.len,contacts->uri.s);
00132                         }
00133                 }
00134         }
00135 
00136         return 0;
00137 error:
00138         return -1;
00139 }
00140 
00141 
00142 
00143 static void reply_callback( struct cell* t, int type, struct tmcb_params* ps)
00144 {
00145         struct cpl_interpreter *intr = (struct cpl_interpreter*)(*(ps->param));
00146         struct location        *loc  = 0;
00147         int rez;
00148 
00149         if (intr==0) {
00150                 LM_WARN("param=0 for callback %d, transaction=%p \n",type,t);
00151                 return;
00152         }
00153 
00154         if (type&TMCB_RESPONSE_OUT) {
00155                 /* the purpose of the final reply is to trash down the interpreter
00156                  * structure! it's the safest place to do that, since this callback
00157                  * it's called only once per transaction for final codes (>=200) ;-) */
00158                 if (ps->code>=200) {
00159                         LM_DBG("code=%d, final reply received\n", ps->code);
00160                         /* CPL interpretation done, call established -> destroy */
00161                         free_cpl_interpreter( intr );
00162                         /* set to zero the param callback*/
00163                         *(ps->param) = 0;
00164                 }
00165                 return;
00166         } else if (! (type&TMCB_ON_FAILURE)) {
00167                 LM_ERR("unknown type %d\n",type);
00168                 goto exit;
00169         }
00170 
00171         LM_DBG("negativ reply received\n");
00172 
00173         intr->flags |= CPL_PROXY_DONE;
00174         intr->msg = ps->req;
00175 
00176         /* is the negative reply triggered by a cancel from UAC side? */
00177         if (was_cancelled(t)) {
00178                 /* stop whole interpretation */
00179                 return;
00180         }
00181 
00182         /* if it's a redirect-> do I have to added to the location set ? */
00183         if (intr->proxy.recurse && (ps->code)/100==3) {
00184                 LM_DBG("recurse level %d processing..\n",intr->proxy.recurse);
00185                 intr->proxy.recurse--;
00186                 /* get the locations from the Contact */
00187                 add_contacts_to_loc_set( ps->rpl, &(intr->loc_set));
00188                 switch (intr->proxy.ordering) {
00189                         case SEQUENTIAL_VAL:
00190                                 /* update the last_to_proxy to last location from set */
00191                                 if (intr->proxy.last_to_proxy==0) {
00192                                         /* the pointer went through entire old set -> set it to the
00193                                          * updated set, from the beginning  */
00194                                         if (intr->loc_set==0)
00195                                                 /* the updated set is also empty -> proxy ended */
00196                                                 break;
00197                                         intr->proxy.last_to_proxy = intr->loc_set;
00198                                 }
00199                                 while(intr->proxy.last_to_proxy->next)
00200                                         intr->proxy.last_to_proxy=intr->proxy.last_to_proxy->next;
00201                                 break;
00202                         case PARALLEL_VAL:
00203                                 /* push the whole new location set to be proxy */
00204                                 intr->proxy.last_to_proxy = intr->loc_set;
00205                                 break;
00206                         case FIRSTONLY_VAL:
00207                                 intr->proxy.last_to_proxy = 0;
00208                                 break;
00209                 }
00210         }
00211 
00212         /* the current proxying failed -> do I have another location to try ?
00213          * This applies only for SERIAL forking or if RECURSE is set */
00214         if (intr->proxy.last_to_proxy && !(no_new_branches(t)) ) {
00215                 /* continue proxying */
00216                 LM_DBG("resuming proxying....\n");
00217                 switch (intr->proxy.ordering) {
00218                         case PARALLEL_VAL:
00219                                 /* I get here only if I got a 3xx and RECURSE in on ->
00220                                  * forward to all location from location set */
00221                                 intr->proxy.last_to_proxy = 0;
00222                                 cpl_proxy_to_loc_set(intr->msg,&(intr->loc_set),intr->flags );
00223                                 break;
00224                         case SEQUENTIAL_VAL:
00225                                 /* place a new branch to the next location from loc. set*/
00226                                 loc = remove_first_location( &(intr->loc_set) );
00227                                 /*print_location_set(intr->loc_set);*/
00228                                 /* update (if necessary) the last_to_proxy location  */
00229                                 if (intr->proxy.last_to_proxy==loc)
00230                                         intr->proxy.last_to_proxy = 0;
00231                                 cpl_proxy_to_loc_set(intr->msg,&loc,intr->flags );
00232                                 break;
00233                         default:
00234                                 LM_CRIT("unexpected ordering found "
00235                                         "when continuing proxying (%d)\n",intr->proxy.ordering);
00236                                 goto exit;
00237                 }
00238                 /* nothing more to be done */
00239                 return;
00240         } else {
00241                 /* done with proxying.... -> process the final response */
00242                 LM_DBG("final_reply: got a final %d\n",ps->code);
00243                 intr->ip = 0;
00244                 if (ps->code==486 || ps->code==600) {
00245                         /* busy response */
00246                         intr->ip = intr->proxy.busy;
00247                 } else if (ps->code==408) {
00248                         /* request timeout -> no response */
00249                         intr->ip = intr->proxy.noanswer;
00250                 } else if (((ps->code)/100)==3) {
00251                         /* redirection */
00252                         /* add to the location list all the addresses from Contact */
00253                         add_contacts_to_loc_set( ps->rpl, &(intr->loc_set));
00254                         print_location_set( intr->loc_set );
00255                         intr->ip = intr->proxy.redirect;
00256                 } else {
00257                         /* generic failure */
00258                         intr->ip = intr->proxy.failure;
00259                 }
00260 
00261                 if (intr->ip==0)
00262                         intr->ip = (intr->proxy.default_)?
00263                                 intr->proxy.default_:DEFAULT_ACTION;
00264                 if (intr->ip!=DEFAULT_ACTION)
00265                         intr->ip = get_first_child( intr->ip );
00266 
00267                 if( intr->ip==DEFAULT_ACTION)
00268                         rez = run_default(intr);
00269                 else
00270                         rez = cpl_run_script(intr);
00271                 switch ( rez ) {
00272                         case SCRIPT_END:
00273                                 /* we don't need to free the interpreter here since it will 
00274                                  * be freed in the final_reply callback */
00275                         case SCRIPT_TO_BE_CONTINUED:
00276                                 return;
00277                         case SCRIPT_RUN_ERROR:
00278                         case SCRIPT_FORMAT_ERROR:
00279                                 goto exit;
00280                         default:
00281                                 LM_CRIT("improper result %d\n",
00282                                         rez);
00283                                 goto exit;
00284                 }
00285         }
00286 
00287 exit:
00288         /* in case of error the default response chosen by ser at the last
00289          * proxying will be forwarded to the UAC */
00290         free_cpl_interpreter( intr );
00291         /* set to zero the param callback*/
00292         *(ps->param) = 0;
00293         return;
00294 }
00295 
00296 
00297 
00298 static inline char *run_proxy( struct cpl_interpreter *intr )
00299 {
00300         unsigned short attr_name;
00301         unsigned short n;
00302         int_str is_val;
00303         char *kid;
00304         char *p;
00305         int i;
00306         str *s;
00307         struct location *loc;
00308 
00309         intr->proxy.ordering = PARALLEL_VAL;
00310         intr->proxy.recurse = (unsigned short)cpl_env.proxy_recurse;
00311 
00312         /* identify the attributes */
00313         for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) {
00314                 get_basic_attr( p, attr_name, n, intr, script_error);
00315                 switch (attr_name) {
00316                         case TIMEOUT_ATTR:
00317                                 if (cpl_env.timer_avp.n || cpl_env.timer_avp.s.s) {
00318                                         is_val.n = n;
00319                                         if ( add_avp( cpl_env.timer_avp_type,
00320                                         cpl_env.timer_avp, is_val)<0) {
00321                                                 LM_ERR("unable to set timer AVP\n");
00322                                                 /* continue */
00323                                         }
00324                                 }
00325                                 break;
00326                         case RECURSE_ATTR:
00327                                 switch (n) {
00328                                         case NO_VAL:
00329                                                 intr->proxy.recurse = 0;
00330                                                 break;
00331                                         case YES_VAL:
00332                                                 /* already set as default */
00333                                                 break;
00334                                         default:
00335                                                 LM_ERR("invalid value (%u) found"
00336                                                         " for attr. RECURSE in PROXY node!\n",n);
00337                                                 goto script_error;
00338                                 }
00339                                 break;
00340                         case ORDERING_ATTR:
00341                                 if (n!=PARALLEL_VAL && n!=SEQUENTIAL_VAL && n!=FIRSTONLY_VAL){
00342                                         LM_ERR("invalid value (%u) found"
00343                                                 " for attr. ORDERING in PROXY node!\n",n);
00344                                         goto script_error;
00345                                 }
00346                                 intr->proxy.ordering = n;
00347                                 break;
00348                         default:
00349                                 LM_ERR("unknown attribute (%d) in"
00350                                         "PROXY node\n",attr_name);
00351                                 goto script_error;
00352                 }
00353         }
00354 
00355         intr->proxy.busy = intr->proxy.noanswer = 0;
00356         intr->proxy.redirect = intr->proxy.failure = intr->proxy.default_ = 0;
00357 
00358         /* this is quite an "expensive" node to run, so let's make some checking
00359          * before getting deeply into it */
00360         for( i=0 ; i<NR_OF_KIDS(intr->ip) ; i++ ) {
00361                 kid = intr->ip + KID_OFFSET(intr->ip,i);
00362                 check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error);
00363                 switch ( NODE_TYPE(kid) ) {
00364                         case BUSY_NODE :
00365                                 intr->proxy.busy = kid;
00366                                 break;
00367                         case NOANSWER_NODE:
00368                                 intr->proxy.noanswer = kid;
00369                                 break;
00370                         case REDIRECTION_NODE:
00371                                 intr->proxy.redirect = kid;
00372                                 break;
00373                         case FAILURE_NODE:
00374                                 intr->proxy.failure = kid;
00375                                 break;
00376                         case DEFAULT_NODE:
00377                                 intr->proxy.default_ = kid;
00378                                 break;
00379                         default:
00380                                 LM_ERR("unknown output node type"
00381                                         " (%d) for PROXY node\n",NODE_TYPE(kid));
00382                                 goto script_error;
00383                 }
00384         }
00385 
00386         /* if the location set if empty, I will go directly on failure/default */
00387         if (intr->loc_set==0) {
00388                 LM_DBG("location set found empty -> going on "
00389                         "failure/default branch\n");
00390                         if (intr->proxy.failure)
00391                                 return get_first_child(intr->proxy.failure);
00392                         else if (intr->proxy.default_)
00393                                 return get_first_child(intr->proxy.default_);
00394                         else return DEFAULT_ACTION;
00395         }
00396 
00397         /* if it's the first execution of a proxy node, force parsing of the needed
00398          * headers and duplicate them in shared memory */
00399         if (!(intr->flags&CPL_PROXY_DONE)) {
00400                 /* user name is already in shared memory */
00401                 /* requested URI - mandatory in SIP msg (cannot be STR_NOT_FOUND) */
00402                 s = GET_RURI( intr->msg );
00403                 duplicate_str( s , intr->ruri );
00404                 intr->flags |= CPL_RURI_DUPLICATED;
00405                 /* TO header - mandatory in SIP msg (cannot be STR_NOT_FOUND) */
00406                 if (!intr->to) {
00407                         if (!intr->msg->to &&
00408                         (parse_headers(intr->msg,HDR_TO_F,0)==-1 || !intr->msg->to)) {
00409                                 LM_ERR("bad msg or missing TO header\n");
00410                                 goto runtime_error;
00411                         }
00412                         s = &(get_to(intr->msg)->uri);
00413                 } else {
00414                         s = intr->to;
00415                 }
00416                 duplicate_str( s , intr->to );
00417                 intr->flags |= CPL_TO_DUPLICATED;
00418                 /* FROM header - mandatory in SIP msg (cannot be STR_NOT_FOUND) */
00419                 if (!intr->from) {
00420                         if (parse_from_header( intr->msg )<0)
00421                                 goto runtime_error;
00422                         s = &(get_from(intr->msg)->uri);
00423                 } else {
00424                         s = intr->from;
00425                 }
00426                 duplicate_str( s , intr->from );
00427                 intr->flags |= CPL_FROM_DUPLICATED;
00428                 /* SUBJECT header - optional in SIP msg (can be STR_NOT_FOUND) */
00429                 if (intr->subject!=STR_NOT_FOUND) {
00430                         search_and_duplicate_hdr(intr,subject,HDR_SUBJECT_F,s);
00431                         if (intr->subject!=STR_NOT_FOUND)
00432                                 intr->flags |= CPL_SUBJECT_DUPLICATED;
00433                 }
00434                 /* ORGANIZATION header - optional in SIP msg (can be STR_NOT_FOUND) */
00435                 if ( intr->organization!=STR_NOT_FOUND) {
00436                         search_and_duplicate_hdr(intr,organization,HDR_ORGANIZATION_F,s);
00437                         if ( intr->organization!=STR_NOT_FOUND)
00438                                 intr->flags |= CPL_ORGANIZATION_DUPLICATED;
00439                 }
00440                 /* USER_AGENT header - optional in SIP msg (can be STR_NOT_FOUND) */
00441                 if (intr->user_agent!=STR_NOT_FOUND) {
00442                         search_and_duplicate_hdr(intr,user_agent,HDR_USERAGENT_F,s);
00443                         if (intr->user_agent!=STR_NOT_FOUND)
00444                                 intr->flags |= CPL_USERAGENT_DUPLICATED;
00445                 }
00446                 /* ACCEPT_LANGUAGE header - optional in SIP msg
00447                  * (can be STR_NOT_FOUND) */
00448                 if (intr->accept_language!=STR_NOT_FOUND) {
00449                         search_and_duplicate_hdr(intr,accept_language,
00450                                 HDR_ACCEPTLANGUAGE_F,s);
00451                         if (intr->accept_language!=STR_NOT_FOUND)
00452                                 intr->flags |= CPL_ACCEPTLANG_DUPLICATED;
00453                 }
00454                 /* PRIORITY header - optional in SIP msg (can be STR_NOT_FOUND) */
00455                 if (intr->priority!=STR_NOT_FOUND) {
00456                         search_and_duplicate_hdr(intr,priority,HDR_PRIORITY_F,s);
00457                         if (intr->priority!=STR_NOT_FOUND)
00458                                 intr->flags |= CPL_PRIORITY_DUPLICATED;
00459                 }
00460 
00461                 /* now is the first time doing proxy, so I can still be stateless;
00462                  * as proxy is done all the time stateful, I have to switch from
00463                  * stateless to stateful if necessary.  */
00464                 if ( !(intr->flags&CPL_IS_STATEFUL) ) {
00465                         i = cpl_fct.tmb.t_newtran( intr->msg );
00466                         if (i<0) {
00467                                 LM_ERR("failed to build new transaction!\n");
00468                                 goto runtime_error;
00469                         } else if (i==0) {
00470                                 LM_ERR("processed INVITE is a retransmission!\n");
00471                                 /* instead of generating an error is better just to break the
00472                                  * script by returning EO_SCRIPT */
00473                                 return EO_SCRIPT;
00474                         }
00475                         intr->flags |= CPL_IS_STATEFUL;
00476                 }
00477 
00478                 /* as I am interested in getting the responses back - I need to install
00479                  * some callback functions for replies  */
00480                 if (cpl_fct.tmb.register_tmcb(intr->msg,0,
00481                 TMCB_ON_FAILURE|TMCB_RESPONSE_OUT,reply_callback,(void*)intr,0) <= 0 ) {
00482                         LM_ERR("failed to register TMCB_RESPONSE_OUT callback\n");
00483                         goto runtime_error;
00484                 }
00485         }
00486 
00487         switch (intr->proxy.ordering) {
00488                 case FIRSTONLY_VAL:
00489                         /* forward the request only to the first address from loc. set */
00490                         /* location set cannot be empty -> was checked before */
00491                         loc = remove_first_location( &(intr->loc_set) );
00492                         intr->proxy.last_to_proxy = 0;
00493                         /* set the new ip before proxy -> otherwise race cond with rpls */
00494                         intr->ip = CPL_TO_CONTINUE;
00495                         if (cpl_proxy_to_loc_set(intr->msg,&loc,intr->flags )==-1)
00496                                 goto runtime_error;
00497                         break;
00498                 case PARALLEL_VAL:
00499                         /* forward to all location from location set */
00500                         intr->proxy.last_to_proxy = 0;
00501                         /* set the new ip before proxy -> otherwise race cond with rpls */
00502                         intr->ip = CPL_TO_CONTINUE;
00503                         if (cpl_proxy_to_loc_set(intr->msg,&(intr->loc_set),intr->flags)
00504                         ==-1)
00505                                 goto runtime_error;
00506                         break;
00507                 case SEQUENTIAL_VAL:
00508                         /* forward the request one at the time to all addresses from
00509                          * loc. set; location set cannot be empty -> was checked before */
00510                         /* use the first location from set */
00511                         loc = remove_first_location( &(intr->loc_set) );
00512                         /* set as the last_to_proxy the last location from set */
00513                         intr->proxy.last_to_proxy = intr->loc_set;
00514                         while (intr->proxy.last_to_proxy&&intr->proxy.last_to_proxy->next)
00515                                 intr->proxy.last_to_proxy = intr->proxy.last_to_proxy->next;
00516                         /* set the new ip before proxy -> otherwise race cond with rpls */
00517                         intr->ip = CPL_TO_CONTINUE;
00518                         if (cpl_proxy_to_loc_set(intr->msg,&loc,intr->flags)==-1)
00519                                 goto runtime_error;
00520                         break;
00521         }
00522 
00523         return CPL_TO_CONTINUE;
00524 script_error:
00525         return CPL_SCRIPT_ERROR;
00526 mem_error:
00527         LM_ERR("no more free shm memory\n");
00528 runtime_error:
00529         return CPL_RUNTIME_ERROR;
00530 }