acc_logic.c

Go to the documentation of this file.
00001 /*
00002  * $Id$
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 
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 "../../modules/tm/tm_load.h"
00045 #include "../rr/api.h"
00046 #include "../../flags.h"
00047 #include "acc.h"
00048 #include "acc_api.h"
00049 #include "acc_mod.h"
00050 #include "acc_logic.h"
00051 
00052 extern struct tm_binds tmb;
00053 extern struct rr_binds rrb;
00054 
00055 struct acc_enviroment acc_env;
00056 
00057 
00058 #define is_acc_flag_set(_rq,_flag)  (((_flag) != -1) && (isflagset((_rq), (_flag)) == 1))
00059 #define reset_acc_flag(_rq,_flag)   (resetflag((_rq), (_flag)))
00060 
00061 #define is_failed_acc_on(_rq)  is_acc_flag_set(_rq,failed_transaction_flag)
00062 
00063 #define is_log_acc_on(_rq)     is_acc_flag_set(_rq,log_flag)
00064 #define is_log_mc_on(_rq)      is_acc_flag_set(_rq,log_missed_flag)
00065 
00066 #ifdef SQL_ACC
00067         #define is_db_acc_on(_rq)     is_acc_flag_set(_rq,db_flag)
00068         #define is_db_mc_on(_rq)      is_acc_flag_set(_rq,db_missed_flag)
00069 #else
00070         #define is_db_acc_on(_rq)     (0)
00071         #define is_db_mc_on(_rq)      (0)
00072 #endif
00073 
00074 #ifdef RAD_ACC
00075         #define is_rad_acc_on(_rq)     is_acc_flag_set(_rq,radius_flag)
00076         #define is_rad_mc_on(_rq)      is_acc_flag_set(_rq,radius_missed_flag)
00077 #else
00078         #define is_rad_acc_on(_rq)     (0)
00079         #define is_rad_mc_on(_rq)      (0)
00080 #endif
00081 
00082 
00083 #ifdef DIAM_ACC
00084         #define is_diam_acc_on(_rq)     is_acc_flag_set(_rq,diameter_flag)
00085         #define is_diam_mc_on(_rq)      is_acc_flag_set(_rq,diameter_missed_flag)
00086 #else
00087         #define is_diam_acc_on(_rq)     (0)
00088         #define is_diam_mc_on(_rq)      (0)
00089 #endif
00090 
00091 #define is_acc_on(_rq) \
00092         ( (is_log_acc_on(_rq)) || (is_db_acc_on(_rq)) \
00093         || (is_rad_acc_on(_rq)) || (is_diam_acc_on(_rq)) )
00094 
00095 #define is_mc_on(_rq) \
00096         ( (is_log_mc_on(_rq)) || (is_db_mc_on(_rq)) \
00097         || (is_rad_mc_on(_rq)) || (is_diam_mc_on(_rq)) )
00098 
00099 #define skip_cancel(_rq) \
00100         (((_rq)->REQ_METHOD==METHOD_CANCEL) && report_cancels==0)
00101 
00102 #define is_acc_prepare_on(_rq) \
00103         (is_acc_flag_set(_rq,acc_prepare_flag))
00104 
00105 
00106 
00107 static void tmcb_func( struct cell* t, int type, struct tmcb_params *ps );
00108 
00109 
00110 static inline struct hdr_field* get_rpl_to( struct cell *t,
00111                                                                                                                 struct sip_msg *reply)
00112 {
00113         if (reply==FAKED_REPLY || !reply || !reply->to)
00114                 return t->uas.request->to;
00115         else
00116                 return reply->to;
00117 }
00118 
00119 
00120 static inline void env_set_to(struct hdr_field *to)
00121 {
00122         acc_env.to = to;
00123 }
00124 
00125 
00126 static inline void env_set_text(char *p, int len)
00127 {
00128         acc_env.text.s = p;
00129         acc_env.text.len = len;
00130 }
00131 
00132 
00133 static inline void env_set_code_status( int code, struct sip_msg *reply)
00134 {
00135         static char code_buf[INT2STR_MAX_LEN];
00136 
00137         acc_env.code = code;
00138         if (reply==FAKED_REPLY || reply==NULL) {
00139                 /* code */
00140                 acc_env.code_s.s =
00141                         int2bstr((unsigned long)code, code_buf, &acc_env.code_s.len);
00142                 /* reason */
00143                 acc_env.reason.s = error_text(code);
00144                 acc_env.reason.len = strlen(acc_env.reason.s);
00145         } else {
00146                 acc_env.code_s = reply->first_line.u.reply.status;
00147                 acc_env.reason = reply->first_line.u.reply.reason;
00148         }
00149 }
00150 
00151 
00152 static inline void env_set_comment(struct acc_param *accp)
00153 {
00154         acc_env.code = accp->code;
00155         acc_env.code_s = accp->code_s;
00156         acc_env.reason = accp->reason;
00157 }
00158 
00159 
00160 static inline int acc_preparse_req(struct sip_msg *req)
00161 {
00162         if ( (parse_headers(req,HDR_CALLID_F|HDR_CSEQ_F|HDR_FROM_F|HDR_TO_F,0)<0)
00163         || (parse_from_header(req)<0 ) ) {
00164                 LM_ERR("failed to preparse request\n");
00165                 return -1;
00166         }
00167         return 0;
00168 }
00169 
00170 
00171 
00172 int w_acc_log_request(struct sip_msg *rq, char *comment, char *foo)
00173 {
00174         if (acc_preparse_req(rq)<0)
00175                 return -1;
00176         env_set_to( rq->to );
00177         env_set_comment((struct acc_param*)comment);
00178         env_set_text( ACC_REQUEST, ACC_REQUEST_LEN);
00179         return acc_log_request(rq);
00180 }
00181 
00182 
00183 #ifdef SQL_ACC
00184 int w_acc_db_request(struct sip_msg *rq, char *comment, char *table)
00185 {
00186         if (!table) {
00187                 LM_ERR("db support not configured\n");
00188                 return -1;
00189         }
00190         if (acc_preparse_req(rq)<0)
00191                 return -1;
00192         env_set_to( rq->to );
00193         env_set_comment((struct acc_param*)comment);
00194         env_set_text(table, strlen(table));
00195         return acc_db_request(rq);
00196 }
00197 #endif
00198 
00199 
00200 #ifdef RAD_ACC
00201 int w_acc_rad_request(struct sip_msg *rq, char *comment, char *foo)
00202 {
00203         if (acc_preparse_req(rq)<0)
00204                 return -1;
00205         env_set_to( rq->to );
00206         env_set_comment((struct acc_param*)comment);
00207         return acc_rad_request(rq);
00208 }
00209 #endif
00210 
00211 
00212 #ifdef DIAM_ACC
00213 int w_acc_diam_request(struct sip_msg *rq, char *comment, char *foo)
00214 {
00215         if (acc_preparse_req(rq)<0)
00216                 return -1;
00217         env_set_to( rq->to );
00218         env_set_comment((struct acc_param*)comment);
00219         return acc_diam_request(rq);
00220 }
00221 #endif
00222 
00223 
00224 
00225 /* prepare message and transaction context for later accounting */
00226 void acc_onreq( struct cell* t, int type, struct tmcb_params *ps )
00227 {
00228         int tmcb_types;
00229         int is_invite;
00230 
00231         if ( ps->req && !skip_cancel(ps->req) &&
00232                         ( is_acc_on(ps->req) || is_mc_on(ps->req)
00233                                 || is_acc_prepare_on(ps->req) ) ) {
00234                 /* do some parsing in advance */
00235                 if (acc_preparse_req(ps->req)<0)
00236                         return;
00237                 is_invite = (ps->req->REQ_METHOD==METHOD_INVITE)?1:0;
00238                 /* install additional handlers */
00239                 tmcb_types =
00240                         /* report on completed transactions */
00241                         TMCB_RESPONSE_OUT |
00242                         /* account e2e acks if configured to do so */
00243                         ((report_ack && is_acc_on(ps->req))?TMCB_E2EACK_IN:0) |
00244                         /* get incoming replies ready for processing */
00245                         TMCB_RESPONSE_IN |
00246                         /* report on missed calls */
00247                         ((is_invite && (is_mc_on(ps->req)
00248                                         || is_acc_prepare_on(ps->req)))?TMCB_ON_FAILURE:0);
00249                 if (tmb.register_tmcb( 0, t, tmcb_types, tmcb_func, 0, 0 )<=0) {
00250                         LM_ERR("cannot register additional callbacks\n");
00251                         return;
00252                 }
00253                 /* if required, determine request direction */
00254                 if( detect_direction && !rrb.is_direction(ps->req,RR_FLOW_UPSTREAM) ) {
00255                         LM_DBG("detected an UPSTREAM req -> flaging it\n");
00256                         ps->req->msg_flags |= FL_REQ_UPSTREAM;
00257                 }
00258         }
00259 }
00260 
00261 
00262 
00263 /* is this reply of interest for accounting ? */
00264 static inline int should_acc_reply(struct sip_msg *req, struct sip_msg *rpl,
00265                                    int code)
00266 {
00267     unsigned int i;
00268 
00269         /* negative transactions reported otherwise only if explicitly 
00270          * demanded */
00271 
00272     if (code >= 300) {
00273         if (!is_failed_acc_on(req)) return 0;
00274         i = 0;
00275         while (failed_filter[i] != 0) {
00276             if (failed_filter[i] == code) return 0;
00277             i++;
00278         }
00279         return 1;
00280     }
00281 
00282     if ( !is_acc_on(req) )
00283         return 0;
00284         
00285     if ( code<200 && !(early_media &&
00286                        parse_headers(rpl,HDR_CONTENTLENGTH_F, 0) == 0 &&
00287                        rpl->content_length && get_content_length(rpl) > 0))
00288         return 0;
00289 
00290     return 1; /* seed is through, we will account this reply */
00291 }
00292 
00293 
00294 
00295 /* parse incoming replies before cloning */
00296 static inline void acc_onreply_in(struct cell *t, struct sip_msg *req,
00297                                                                                         struct sip_msg *reply, int code)
00298 {
00299         /* don't parse replies in which we are not interested */
00300         /* missed calls enabled ? */
00301         if ( (reply && reply!=FAKED_REPLY) && (should_acc_reply(req,reply,code)
00302         || (is_invite(t) && code>=300 && is_mc_on(req))) ) {
00303                 parse_headers(reply, HDR_TO_F, 0 );
00304         }
00305 }
00306 
00307 
00308 
00309 /* initiate a report if we previously enabled MC accounting for this t */
00310 static inline void on_missed(struct cell *t, struct sip_msg *req,
00311                                                                                         struct sip_msg *reply, int code)
00312 {
00313         str new_uri_bk = {0, 0};
00314         int flags_to_reset = 0;
00315 
00316         /* set as new_uri the last branch */
00317         if (t->relayed_reply_branch>=0) {
00318                 new_uri_bk = req->new_uri;
00319                 req->new_uri = t->uac[t->relayed_reply_branch].uri;
00320                 req->parsed_uri_ok = 0;
00321         } else {
00322                 new_uri_bk.len = -1;
00323                 new_uri_bk.s = 0;
00324         }
00325 
00326         /* set env variables */
00327         env_set_to( get_rpl_to(t,reply) );
00328         env_set_code_status( code, reply);
00329 
00330         /* we report on missed calls when the first
00331          * forwarding attempt fails; we do not wish to
00332          * report on every attempt; so we clear the flags; 
00333          */
00334 
00335         if (is_log_mc_on(req)) {
00336                 env_set_text( ACC_MISSED, ACC_MISSED_LEN);
00337                 acc_log_request( req );
00338                 flags_to_reset |= log_missed_flag;
00339         }
00340 #ifdef SQL_ACC
00341         if (is_db_mc_on(req)) {
00342                 env_set_text(db_table_mc.s, db_table_mc.len);
00343                 acc_db_request( req );
00344                 flags_to_reset |= db_missed_flag;
00345         }
00346 #endif
00347 #ifdef RAD_ACC
00348         if (is_rad_mc_on(req)) {
00349                 acc_rad_request( req );
00350                 flags_to_reset |= radius_missed_flag;
00351         }
00352 #endif
00353 /* DIAMETER */
00354 #ifdef DIAM_ACC
00355         if (is_diam_mc_on(req)) {
00356                 acc_diam_request( req );
00357                 flags_to_reset |= diameter_missed_flag;
00358         }
00359 #endif
00360 
00361         /* run extra acc engines */
00362         acc_run_engines(req, 1, &flags_to_reset);
00363 
00364         /* Reset the accounting missed_flags
00365          * These can't be reset in the blocks above, because
00366          * it would skip accounting if the flags are identical
00367          */
00368         reset_acc_flag( req, flags_to_reset );
00369 
00370         if (new_uri_bk.len>=0) {
00371                 req->new_uri = new_uri_bk;
00372                 req->parsed_uri_ok = 0;
00373         }
00374 
00375 }
00376 
00377 
00378 
00379 /* initiate a report if we previously enabled accounting for this t */
00380 static inline void acc_onreply( struct cell* t, struct sip_msg *req,
00381                                                                                         struct sip_msg *reply, int code)
00382 {
00383         str new_uri_bk;
00384 
00385         /* acc_onreply is bound to TMCB_REPLY which may be called
00386            from _reply, like when FR hits; we should not miss this
00387            event for missed calls either */
00388         if (is_invite(t) && code>=300 && is_mc_on(req) )
00389                 on_missed(t, req, reply, code);
00390 
00391         if (!should_acc_reply(req, reply, code))
00392                 return;
00393 
00394         /* for reply processing, set as new_uri the winning branch */
00395         if (t->relayed_reply_branch>=0) {
00396                 new_uri_bk = req->new_uri;
00397                 req->new_uri = t->uac[t->relayed_reply_branch].uri;
00398                 req->parsed_uri_ok = 0;
00399         } else {
00400                 new_uri_bk.len = -1;
00401                 new_uri_bk.s = 0;
00402         }
00403         /* set env variables */
00404         env_set_to( get_rpl_to(t,reply) );
00405         env_set_code_status( code, reply);
00406 
00407         if ( is_log_acc_on(req) ) {
00408                 env_set_text( ACC_ANSWERED, ACC_ANSWERED_LEN);
00409                 acc_log_request(req);
00410         }
00411 #ifdef SQL_ACC
00412         if (is_db_acc_on(req)) {
00413                 env_set_text( db_table_acc.s, db_table_acc.len);
00414                 acc_db_request(req);
00415         }
00416 #endif
00417 #ifdef RAD_ACC
00418         if (is_rad_acc_on(req))
00419                 acc_rad_request(req);
00420 #endif
00421 /* DIAMETER */
00422 #ifdef DIAM_ACC
00423         if (is_diam_acc_on(req))
00424                 acc_diam_request(req);
00425 #endif
00426 
00427         /* run extra acc engines */
00428         acc_run_engines(req, 0, NULL);
00429 
00430         if (new_uri_bk.len>=0) {
00431                 req->new_uri = new_uri_bk;
00432                 req->parsed_uri_ok = 0;
00433         }
00434 }
00435 
00436 
00437 
00438 static inline void acc_onack( struct cell* t, struct sip_msg *req,
00439                 struct sip_msg *ack, int code)
00440 {
00441         if (acc_preparse_req(ack)<0)
00442                 return;
00443 
00444         /* set env variables */
00445         env_set_to( ack->to?ack->to:req->to );
00446         env_set_code_status( t->uas.status, 0 );
00447 
00448         if (is_log_acc_on(req)) {
00449                 env_set_text( ACC_ACKED, ACC_ACKED_LEN);
00450                 acc_log_request( ack );
00451         }
00452 #ifdef SQL_ACC
00453         if (is_db_acc_on(req)) {
00454                 env_set_text( db_table_acc.s, db_table_acc.len);
00455                 acc_db_request( ack );
00456         }
00457 #endif
00458 #ifdef RAD_ACC
00459         if (is_rad_acc_on(req)) {
00460                 acc_rad_request(ack);
00461         }
00462 #endif
00463 /* DIAMETER */
00464 #ifdef DIAM_ACC
00465         if (is_diam_acc_on(req)) {
00466                 acc_diam_request(ack);
00467         }
00468 #endif
00469 
00470         /* run extra acc engines */
00471         acc_run_engines(req, 0, NULL);
00472         
00473 }
00474 
00475 
00479 int acc_api_exec(struct sip_msg *rq, acc_engine_t *eng,
00480                 acc_param_t* comment)
00481 {
00482         acc_info_t inf;
00483         if (acc_preparse_req(rq)<0)
00484                 return -1;
00485         env_set_to(rq->to);
00486         env_set_comment(comment);
00487         memset(&inf, 0, sizeof(acc_info_t));
00488         inf.env  = &acc_env;
00489         acc_api_set_arrays(&inf);
00490         return eng->acc_req(rq, &inf);
00491 }
00492 
00493 
00494 static void tmcb_func( struct cell* t, int type, struct tmcb_params *ps )
00495 {
00496         LM_DBG("acc callback called for t(%p) event type %d, reply code %d\n",
00497                         t, type, ps->code);
00498         if (type&TMCB_RESPONSE_OUT) {
00499                 acc_onreply( t, ps->req, ps->rpl, ps->code);
00500         } else if (type&TMCB_E2EACK_IN) {
00501                 acc_onack( t, t->uas.request, ps->req, ps->code);
00502         } else if (type&TMCB_ON_FAILURE) {
00503                 on_missed( t, ps->req, ps->rpl, ps->code);
00504         } else if (type&TMCB_RESPONSE_IN) {
00505                 acc_onreply_in( t, ps->req, ps->rpl, ps->code);
00506         }
00507 }
00508