acc_logic.c

Go to the documentation of this file.
00001 /*
00002  * $Id: acc_logic.c 5666 2009-03-03 12:13:46Z henningw $
00003  * 
00004  * Accounting module logic
00005  *
00006  * Copyright (C) 2001-2003 FhG Fokus
00007  * Copyright (C) 2006 Voice Sistem SRL
00008  *
00009  * This file is part of Kamailio, a free SIP server.
00010  *
00011  * Kamailio is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version
00015  *
00016  * Kamailio is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00024  *
00025  * History:
00026  * -------
00027  * 2006-09-19  forked from the acc_mod.c file during a big re-structuring
00028  *             of acc module (bogdan)
00029  */
00030 
00031 /*! \file
00032  * \ingroup acc
00033  * \brief Acc:: Logic
00034  *
00035  * - Module: \ref acc
00036  */
00037 
00038 #include <stdio.h>
00039 #include <string.h>
00040 
00041 #include "../../dprint.h"
00042 #include "../../parser/parse_from.h"
00043 #include "../../parser/parse_content.h"
00044 #include "../tm/tm_load.h"
00045 #include "../rr/api.h"
00046 #include "acc.h"
00047 #include "acc_mod.h"
00048 #include "acc_logic.h"
00049 
00050 extern struct tm_binds tmb;
00051 extern struct rr_binds rrb;
00052 
00053 struct acc_enviroment acc_env;
00054 
00055 
00056 #define is_acc_flag_set(_rq,_flag)  (((_rq)->flags)&(_flag))
00057 #define reset_acc_flag(_rq,_flag)   (_rq)->flags &= ~(_flag)
00058 
00059 #define is_failed_acc_on(_rq)  is_acc_flag_set(_rq,failed_transaction_flag)
00060 
00061 #define is_log_acc_on(_rq)     is_acc_flag_set(_rq,log_flag)
00062 #define is_log_mc_on(_rq)      is_acc_flag_set(_rq,log_missed_flag)
00063 
00064 #ifdef SQL_ACC
00065    #define is_db_acc_on(_rq)     is_acc_flag_set(_rq,db_flag)
00066    #define is_db_mc_on(_rq)      is_acc_flag_set(_rq,db_missed_flag)
00067 #else
00068    #define is_db_acc_on(_rq)     (0)
00069    #define is_db_mc_on(_rq)      (0)
00070 #endif
00071 
00072 #ifdef RAD_ACC
00073    #define is_rad_acc_on(_rq)     is_acc_flag_set(_rq,radius_flag)
00074    #define is_rad_mc_on(_rq)      is_acc_flag_set(_rq,radius_missed_flag)
00075 #else
00076    #define is_rad_acc_on(_rq)     (0)
00077    #define is_rad_mc_on(_rq)      (0)
00078 #endif
00079 
00080 
00081 #ifdef DIAM_ACC
00082    #define is_diam_acc_on(_rq)     is_acc_flag_set(_rq,diameter_flag)
00083    #define is_diam_mc_on(_rq)      is_acc_flag_set(_rq,diameter_missed_flag)
00084 #else
00085    #define is_diam_acc_on(_rq)     (0)
00086    #define is_diam_mc_on(_rq)      (0)
00087 #endif
00088 
00089 #define is_acc_on(_rq) \
00090    ( (is_log_acc_on(_rq)) || (is_db_acc_on(_rq)) \
00091    || (is_rad_acc_on(_rq)) || (is_diam_acc_on(_rq)) )
00092 
00093 #define is_mc_on(_rq) \
00094    ( (is_log_mc_on(_rq)) || (is_db_mc_on(_rq)) \
00095    || (is_rad_mc_on(_rq)) || (is_diam_mc_on(_rq)) )
00096 
00097 #define skip_cancel(_rq) \
00098    (((_rq)->REQ_METHOD==METHOD_CANCEL) && report_cancels==0)
00099 
00100 
00101 
00102 
00103 static void tmcb_func( struct cell* t, int type, struct tmcb_params *ps );
00104 
00105 
00106 static inline struct hdr_field* get_rpl_to( struct cell *t,
00107                                           struct sip_msg *reply)
00108 {
00109    if (reply==FAKED_REPLY || !reply || !reply->to)
00110       return t->uas.request->to;
00111    else
00112       return reply->to;
00113 }
00114 
00115 
00116 static inline void env_set_to(struct hdr_field *to)
00117 {
00118    acc_env.to = to;
00119 }
00120 
00121 
00122 static inline void env_set_text(char *p, int len)
00123 {
00124    acc_env.text.s = p;
00125    acc_env.text.len = len;
00126 }
00127 
00128 
00129 static inline void env_set_code_status( int code, struct sip_msg *reply)
00130 {
00131    static char code_buf[INT2STR_MAX_LEN];
00132 
00133    acc_env.code = code;
00134    if (reply==FAKED_REPLY || reply==NULL) {
00135       /* code */
00136       acc_env.code_s.s =
00137          int2bstr((unsigned long)code, code_buf, &acc_env.code_s.len);
00138       /* reason */
00139       acc_env.reason.s = error_text(code);
00140       acc_env.reason.len = strlen(acc_env.reason.s);
00141    } else {
00142       acc_env.code_s = reply->first_line.u.reply.status;
00143       acc_env.reason = reply->first_line.u.reply.reason;
00144    }
00145 }
00146 
00147 
00148 static inline void env_set_comment(struct acc_param *accp)
00149 {
00150    acc_env.code = accp->code;
00151    acc_env.code_s = accp->code_s;
00152    acc_env.reason = accp->reason;
00153 }
00154 
00155 
00156 static inline int acc_preparse_req(struct sip_msg *req)
00157 {
00158    if ( (parse_headers(req,HDR_CALLID_F|HDR_CSEQ_F|HDR_FROM_F|HDR_TO_F,0)<0)
00159    || (parse_from_header(req)<0 ) ) {
00160       LM_ERR("failed to preparse request\n");
00161       return -1;
00162    }
00163    return 0;
00164 }
00165 
00166 
00167 
00168 int w_acc_log_request(struct sip_msg *rq, char *comment, char *foo)
00169 {
00170    if (acc_preparse_req(rq)<0)
00171       return -1;
00172    env_set_to( rq->to );
00173    env_set_comment((struct acc_param*)comment);
00174    env_set_text( ACC_REQUEST, ACC_REQUEST_LEN);
00175    return acc_log_request(rq);
00176 }
00177 
00178 
00179 #ifdef SQL_ACC
00180 int w_acc_db_request(struct sip_msg *rq, char *comment, char *table)
00181 {
00182    if (!table) {
00183       LM_ERR("db support not configured\n");
00184       return -1;
00185    }
00186    if (acc_preparse_req(rq)<0)
00187       return -1;
00188    env_set_to( rq->to );
00189    env_set_comment((struct acc_param*)comment);
00190    env_set_text(table, strlen(table));
00191    return acc_db_request(rq);
00192 }
00193 #endif
00194 
00195 
00196 #ifdef RAD_ACC
00197 int w_acc_rad_request(struct sip_msg *rq, char *comment, char *foo)
00198 {
00199    if (acc_preparse_req(rq)<0)
00200       return -1;
00201    env_set_to( rq->to );
00202    env_set_comment((struct acc_param*)comment);
00203    return acc_rad_request(rq);
00204 }
00205 #endif
00206 
00207 
00208 #ifdef DIAM_ACC
00209 int w_acc_diam_request(struct sip_msg *rq, char *comment, char *foo)
00210 {
00211    if (acc_preparse_req(rq)<0)
00212       return -1;
00213    env_set_to( rq->to );
00214    env_set_comment((struct acc_param*)comment);
00215    return acc_diam_request(rq);
00216 }
00217 #endif
00218 
00219 
00220 /* prepare message and transaction context for later accounting */
00221 void acc_onreq( struct cell* t, int type, struct tmcb_params *ps )
00222 {
00223    int tmcb_types;
00224    int is_invite;
00225 
00226    if ( ps->req && !skip_cancel(ps->req) &&
00227    (is_acc_on(ps->req) || is_mc_on(ps->req)) ) {
00228       /* do some parsing in advance */
00229       if (acc_preparse_req(ps->req)<0)
00230          return;
00231       is_invite = (ps->req->REQ_METHOD==METHOD_INVITE)?1:0;
00232       /* install additional handlers */
00233       tmcb_types =
00234          /* report on completed transactions */
00235          TMCB_RESPONSE_OUT |
00236          /* account e2e acks if configured to do so */
00237          ((report_ack && is_acc_on(ps->req))?TMCB_E2EACK_IN:0) |
00238          /* get incoming replies ready for processing */
00239          TMCB_RESPONSE_IN |
00240          /* report on missed calls */
00241          ((is_invite && is_mc_on(ps->req))?TMCB_ON_FAILURE:0) ;
00242       if (tmb.register_tmcb( 0, t, tmcb_types, tmcb_func, 0, 0 )<=0) {
00243          LM_ERR("cannot register additional callbacks\n");
00244          return;
00245       }
00246       /* if required, determine request direction */
00247       if( detect_direction && !rrb.is_direction(ps->req,RR_FLOW_UPSTREAM) ) {
00248          LM_DBG("detected an UPSTREAM req -> flaging it\n");
00249          ps->req->msg_flags |= FL_REQ_UPSTREAM;
00250       }
00251    }
00252 }
00253 
00254 
00255 
00256 /* is this reply of interest for accounting ? */
00257 static inline int should_acc_reply(struct sip_msg *req,struct sip_msg *rpl,
00258                                                    int code)
00259 {
00260    /* negative transactions reported otherwise only if explicitly 
00261     * demanded */
00262    if ( !is_failed_acc_on(req) && code >=300 )
00263       return 0;
00264    if ( !is_acc_on(req) )
00265       return 0;
00266    if ( code<200 && !(early_media &&
00267    parse_headers(rpl,HDR_CONTENTLENGTH_F, 0)==0 && rpl->content_length &&
00268    get_content_length(rpl)>0 ) )
00269       return 0;
00270 
00271    return 1; /* seed is through, we will account this reply */
00272 }
00273 
00274 
00275 
00276 /* parse incoming replies before cloning */
00277 static inline void acc_onreply_in(struct cell *t, struct sip_msg *req,
00278                                  struct sip_msg *reply, int code)
00279 {
00280    /* don't parse replies in which we are not interested */
00281    /* missed calls enabled ? */
00282    if ( (reply && reply!=FAKED_REPLY) && (should_acc_reply(req,reply,code)
00283    || (is_invite(t) && code>=300 && is_mc_on(req))) ) {
00284       parse_headers(reply, HDR_TO_F, 0 );
00285    }
00286 }
00287 
00288 
00289 
00290 /* initiate a report if we previously enabled MC accounting for this t */
00291 static inline void on_missed(struct cell *t, struct sip_msg *req,
00292                                  struct sip_msg *reply, int code)
00293 {
00294    str new_uri_bk;
00295    int flags_to_reset = 0;
00296 
00297    /* set as new_uri the last branch */
00298    new_uri_bk = req->new_uri;
00299    req->new_uri = t->uac[t->nr_of_outgoings-1].uri;
00300    req->parsed_uri_ok = 0;
00301    /* set env variables */
00302    env_set_to( get_rpl_to(t,reply) );
00303    env_set_code_status( code, reply);
00304 
00305    /* we report on missed calls when the first
00306     * forwarding attempt fails; we do not wish to
00307     * report on every attempt; so we clear the flags; 
00308     */
00309 
00310    if (is_log_mc_on(req)) {
00311       env_set_text( ACC_MISSED, ACC_MISSED_LEN);
00312       acc_log_request( req );
00313       flags_to_reset |= log_missed_flag;
00314    }
00315 #ifdef SQL_ACC
00316    if (is_db_mc_on(req)) {
00317       env_set_text(db_table_mc.s, db_table_mc.len);
00318       acc_db_request( req );
00319       flags_to_reset |= db_missed_flag;
00320    }
00321 #endif
00322 #ifdef RAD_ACC
00323    if (is_rad_mc_on(req)) {
00324       acc_rad_request( req );
00325       flags_to_reset |= radius_missed_flag;
00326    }
00327 #endif
00328 /* DIAMETER */
00329 #ifdef DIAM_ACC
00330    if (is_diam_mc_on(req)) {
00331       acc_diam_request( req );
00332       flags_to_reset |= diameter_missed_flag;
00333    }
00334 #endif
00335 
00336    /* Reset the accounting missed_flags
00337     * These can't be reset in the blocks above, because
00338     * it would skip accounting if the flags are identical
00339     */
00340    reset_acc_flag( req, flags_to_reset );
00341 
00342    req->new_uri = new_uri_bk;
00343    req->parsed_uri_ok = 0;
00344 }
00345 
00346 
00347 
00348 /* initiate a report if we previously enabled accounting for this t */
00349 static inline void acc_onreply( struct cell* t, struct sip_msg *req,
00350                                  struct sip_msg *reply, int code)
00351 {
00352    str new_uri_bk;
00353 
00354    /* acc_onreply is bound to TMCB_REPLY which may be called
00355       from _reply, like when FR hits; we should not miss this
00356       event for missed calls either */
00357    if (is_invite(t) && code>=300 && is_mc_on(req) )
00358       on_missed(t, req, reply, code);
00359 
00360    if (!should_acc_reply(req, reply, code))
00361       return;
00362 
00363    /* for reply processing, set as new_uri the winning branch */
00364    if (t->relayed_reply_branch>=0) {
00365       new_uri_bk = req->new_uri;
00366       req->new_uri = t->uac[t->relayed_reply_branch].uri;
00367       req->parsed_uri_ok = 0;
00368    } else {
00369       new_uri_bk.len = -1;
00370       new_uri_bk.s = 0;
00371    }
00372    /* set env variables */
00373    env_set_to( get_rpl_to(t,reply) );
00374    env_set_code_status( code, reply);
00375 
00376    if ( is_log_acc_on(req) ) {
00377       env_set_text( ACC_ANSWERED, ACC_ANSWERED_LEN);
00378       acc_log_request(req);
00379    }
00380 #ifdef SQL_ACC
00381    if (is_db_acc_on(req)) {
00382       env_set_text( db_table_acc.s, db_table_acc.len);
00383       acc_db_request(req);
00384    }
00385 #endif
00386 #ifdef RAD_ACC
00387    if (is_rad_acc_on(req))
00388       acc_rad_request(req);
00389 #endif
00390 /* DIAMETER */
00391 #ifdef DIAM_ACC
00392    if (is_diam_acc_on(req))
00393       acc_diam_request(req);
00394 #endif
00395 
00396    if (new_uri_bk.len>=0) {
00397       req->new_uri = new_uri_bk;
00398       req->parsed_uri_ok = 0;
00399    }
00400 }
00401 
00402 
00403 
00404 static inline void acc_onack( struct cell* t, struct sip_msg *req,
00405       struct sip_msg *ack, int code)
00406 {
00407    if (acc_preparse_req(ack)<0)
00408       return;
00409 
00410    /* set env variables */
00411    env_set_to( ack->to?ack->to:req->to );
00412    env_set_code_status( t->uas.status, 0 );
00413 
00414    if (is_log_acc_on(req)) {
00415       env_set_text( ACC_ACKED, ACC_ACKED_LEN);
00416       acc_log_request( ack );
00417    }
00418 #ifdef SQL_ACC
00419    if (is_db_acc_on(req)) {
00420       env_set_text( db_table_acc.s, db_table_acc.len);
00421       acc_db_request( ack );
00422    }
00423 #endif
00424 #ifdef RAD_ACC
00425    if (is_rad_acc_on(req)) {
00426       acc_rad_request(ack);
00427    }
00428 #endif
00429 /* DIAMETER */
00430 #ifdef DIAM_ACC
00431    if (is_diam_acc_on(req)) {
00432       acc_diam_request(ack);
00433    }
00434 #endif
00435    
00436 }
00437 
00438 
00439 
00440 static void tmcb_func( struct cell* t, int type, struct tmcb_params *ps )
00441 {
00442    if (type&TMCB_RESPONSE_OUT) {
00443       acc_onreply( t, ps->req, ps->rpl, ps->code);
00444    } else if (type&TMCB_E2EACK_IN) {
00445       acc_onack( t, t->uas.request, ps->req, ps->code);
00446    } else if (type&TMCB_ON_FAILURE) {
00447       on_missed( t, ps->req, ps->rpl, ps->code);
00448    } else if (type&TMCB_RESPONSE_IN) {
00449       acc_onreply_in( t, ps->req, ps->rpl, ps->code);
00450    }
00451 }
00452 

Generated on Thu May 17 12:00:24 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6