cpl_proxy.h

Go to the documentation of this file.
00001 /*
00002  * $Id: cpl_proxy.h 5376 2008-12-17 19:07:14Z osas $
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 "../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 }

Generated on Mon May 21 18:00:25 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6