📄 acc.c
字号:
/* * $Id: acc.c,v 1.16 2006/06/30 18:05:29 bogdan_iancu Exp $ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of openser, a free SIP server. * * openser 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 * * openser 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2003-04-04 grand acc cleanup (jiri) * 2003-11-04 multidomain support for mysql introduced (jiri) * 2004-06-06 updated to the new DB api, cleanup: acc_db_{bind, init,close) * added (andrei) * 2005-05-30 acc_extra patch commited (ramona) * 2005-06-28 multi leg call support added (bogdan) * 2006-01-13 detect_direction (for sequential requests) added (bogdan) */#include <stdio.h>#include <time.h>#include "../../dprint.h"#include "../../error.h"#include "../../ut.h" /* q_memchr */#include "../../mem/mem.h"#include "../../usr_avp.h"#include "../../parser/hf.h"#include "../../parser/msg_parser.h"#include "../../parser/parse_from.h"#include "../../parser/digest/digest.h"#include "../tm/t_funcs.h"#include "acc_mod.h"#include "acc.h"#include "acc_extra.h"#include "dict.h"#ifdef RAD_ACC#include <radiusclient-ng.h>#endif#ifdef DIAM_ACC#include "diam_dict.h"#include "diam_message.h"#include "diam_tcp.h"#define AA_REQUEST 265#define AA_ANSWER 265#define ACCOUNTING_REQUEST 271#define ACCOUNTING_ANSWER 271#define M_NAME "acc"#endif#define ATR(atr) atr_arr[cnt].s=A_##atr;\ atr_arr[cnt].len=A_##atr##_LEN;static str na={NA, NA_LEN};extern struct acc_extra *log_extra;#ifdef RAD_ACC/* caution: keep these aligned to RAD_ACC_FMT !! */static int rad_attr[] = { A_CALLING_STATION_ID, A_CALLED_STATION_ID, A_SIP_TRANSLATED_REQUEST_URI, A_ACCT_SESSION_ID, A_SIP_TO_TAG, A_SIP_FROM_TAG, A_SIP_CSEQ };extern struct acc_extra *rad_extra;#endif#ifdef DIAM_ACCextern char *diameter_client_host;extern int diameter_client_port;extern struct acc_extra *dia_extra;/* caution: keep these aligned to DIAM_ACC_FMT !! */static int diam_attr[] = { AVP_SIP_FROM_URI, AVP_SIP_TO_URI, AVP_SIP_OURI, AVP_SIP_CALLID, AVP_SIP_TO_TAG, AVP_SIP_FROM_TAG, AVP_SIP_CSEQ };#endif#ifdef SQL_ACCstatic db_func_t acc_dbf;static db_con_t* db_handle=0;extern struct acc_extra *db_extra;#endifstatic inline struct hdr_field *valid_to( struct cell *t, struct sip_msg *reply){ if (reply==FAKED_REPLY || !reply || !reply->to) return t->uas.request->to; return reply->to;}static inline str *cred_user(struct sip_msg *rq){ struct hdr_field* h; auth_body_t* cred; get_authorized_cred(rq->proxy_auth, &h); if (!h) get_authorized_cred(rq->authorization, &h); if (!h) return 0; cred=(auth_body_t*)(h->parsed); if (!cred || !cred->digest.username.user.len) return 0; return &cred->digest.username.user;}static inline str *cred_realm(struct sip_msg *rq){ str* realm; struct hdr_field* h; auth_body_t* cred; get_authorized_cred(rq->proxy_auth, &h); if (!h) get_authorized_cred(rq->authorization, &h); if (!h) return 0; cred=(auth_body_t*)(h->parsed); if (!cred) return 0; realm = GET_REALM(&cred->digest); if (!realm->len || !realm->s) { return 0; } return realm;}/* create an array of str's for accounting using a formatting string; * this is the heart of the accounting module -- it prints whatever * requested in a way, that can be used for syslog, radius, * sql, whatsoever */static int fmt2strar( char *fmt, /* what would you like to account ? */ struct sip_msg *rq, /* accounted message */ struct hdr_field *to_hdr, str *phrase, int *total_len, /* total length of accounted values */ int *attr_len, /* total length of accounted attribute names */ str **val_arr, /* that's the output -- must have MAX_ACC_COLUMNS */ str *atr_arr){#define get_from_to( _msg, _from, _to) \ do{ \ if (!from_to_set) {\ if(_msg->msg_flags&FL_REQ_UPSTREAM){\ DBG("DBUG:acc:fmt2strar: UPSTREAM flag set -> swap F/T\n"); \ /* swap from and to */ \ _from = to_hdr; \ _to = _msg->from; \ } else { \ _from = _msg->from; \ _to = to_hdr; \ } \ from_to_set = 1; \ } \ }while(0)#define get_ft_body( _ft_hdr) ((struct to_body*)_ft_hdr->parsed) int cnt, tl, al; struct to_body *ft_body; static struct sip_uri f_puri; static struct sip_uri t_puri; static str mycode; str *cr; struct cseq_body *cseq; int from_to_set; struct hdr_field *from , *to; cnt=tl=al=0; from_to_set = 0; from = to = 0; /* we don't care about parsing here; either the function * was called from script, in which case the wrapping function * is supposed to parse, or from reply processing in which case * TM should have preparsed from REQUEST_IN callback; what's not * here is replaced with NA */ while(*fmt) { if (cnt==ALL_LOG_FMT_LEN) { LOG(L_ERR, "ERROR: fmt2strar: too long formatting string\n"); return 0; } switch(*fmt) { case 'n': /* CSeq number */ if (rq->cseq && (cseq=get_cseq(rq)) && cseq->number.len) val_arr[cnt]=&cseq->number; else val_arr[cnt]=&na; ATR(CSEQ); break; case 'c': /* Callid */ val_arr[cnt]=rq->callid && rq->callid->body.len ? &rq->callid->body : &na; ATR(CALLID); break; case 'i': /* incoming uri */ val_arr[cnt]=&rq->first_line.u.request.uri; ATR(IURI); break; case 'm': /* method */ val_arr[cnt]=&rq->first_line.u.request.method; ATR(METHOD); break; case 'o': /* outgoing uri */ if (rq->new_uri.len) val_arr[cnt]=&rq->new_uri; else val_arr[cnt]=&rq->first_line.u.request.uri; ATR(OURI); break; case 'f': /* from-body */ get_from_to( rq, from, to); val_arr[cnt]=(from && from->body.len) ? &from->body : &na; ATR(FROM); break; case 'r': /* from-tag */ get_from_to( rq, from, to); if (from && (ft_body=get_ft_body(from)) && ft_body->tag_value.len) { val_arr[cnt]=&ft_body->tag_value; } else val_arr[cnt]=&na; ATR(FROMTAG); break; case 'U': /* digest, from-uri otherwise */ cr=cred_user(rq); if (cr) { ATR(UID); val_arr[cnt]=cr; break; } /* fallback to from-uri if digest unavailable ... */ case 'F': /* from-uri */ get_from_to( rq, from, to); if (from && (ft_body=get_ft_body(from)) && ft_body->uri.len) { val_arr[cnt]=&ft_body->uri; } else val_arr[cnt]=&na; ATR(FROMURI); break; case '0': /* from-user */ get_from_to( rq, from, to); val_arr[cnt]=&na; if (from && (ft_body=get_ft_body(from)) && ft_body->uri.len) { parse_uri(ft_body->uri.s, ft_body->uri.len, &f_puri); if (f_puri.user.len) val_arr[cnt]=&f_puri.user; } ATR(FROMUSER); break; case 'X': /* from-domain */ get_from_to( rq, from, to); val_arr[cnt]=&na; if (from && (ft_body=get_ft_body(from)) && ft_body->uri.len) { parse_uri(ft_body->uri.s, ft_body->uri.len, &f_puri); if (f_puri.host.len) val_arr[cnt]=&f_puri.host; } ATR(FROMDOMAIN); break; case 't': /* to-body */ get_from_to( rq, from, to); val_arr[cnt]=(to && to->body.len) ? &to->body : &na; ATR(TO); break; case 'd': /* to-tag */ get_from_to( rq, from, to); val_arr[cnt]=(to && (ft_body=get_ft_body(to)) && ft_body->tag_value.len) ? &ft_body->tag_value : &na; ATR(TOTAG); break; case 'T': /* to-uri */ get_from_to( rq, from, to); if (to && (ft_body=get_ft_body(to)) && ft_body->uri.len) { val_arr[cnt]=&ft_body->uri; } else val_arr[cnt]=&na; ATR(TOURI); break; case '1': /* to user */ get_from_to( rq, from, to); val_arr[cnt]=&na; if (to && (ft_body=get_ft_body(to)) && ft_body->uri.len) { parse_uri(ft_body->uri.s, ft_body->uri.len, &t_puri); if (t_puri.user.len) val_arr[cnt]=&t_puri.user; } ATR(TOUSER); break; case 'S': if (phrase->len>=3) { mycode.s=phrase->s;mycode.len=3; val_arr[cnt]=&mycode; } else val_arr[cnt]=&na; ATR(CODE); break; case 's': val_arr[cnt]=phrase; ATR(STATUS); break; case 'u': cr=cred_user(rq); val_arr[cnt]=cr?cr:&na; ATR(UID); break; case 'p': /* user part of request-uri */ val_arr[cnt]=rq->parsed_orig_ruri.user.len ? & rq->parsed_orig_ruri.user : &na; ATR(UP_IURI); break; case 'D': /* domain part of request-uri */ val_arr[cnt]=rq->parsed_orig_ruri.host.len ? & rq->parsed_orig_ruri.host : &na; ATR(RURI_DOMAIN); break; default: LOG(L_CRIT, "BUG: acc_log_request: unknown char: %c\n", *fmt); return 0; } /* switch (*fmt) */ tl+=val_arr[cnt]->len; al+=atr_arr[cnt].len; fmt++; cnt++; } /* while (*fmt) */ *total_len=tl; *attr_len=al; return cnt;}/**************************************************** * macros for embedded multi-leg logging ****************************************************/#define leg_loop_VARS \ struct usr_avp *dst; \ struct usr_avp *src; \ int_str dst_val; \ int_str src_val; \#define BEGIN_loop_all_legs \ src = search_first_avp(0,(int_str)src_avp_id,&src_val,0); \ dst = search_first_avp(0,(int_str)dst_avp_id,&dst_val,0); \ do { \ while (src && (src->flags&AVP_VAL_STR)==0 ) \ src = search_next_avp(src,&src_val); \ while (dst && (dst->flags&AVP_VAL_STR)==0 ) \ dst = search_next_avp(dst,&dst_val);#define END_loop_all_legs \ src = src?search_next_avp(src,&src_val):0; \ dst = dst?search_next_avp(dst,&dst_val):0; \ }while(src||dst); /* skip leading text and begin with first item's * separator ", " which will be overwritten by the * leading text later * *//******************************************** * acc_request ********************************************/#define MAX_LOG_SIZE 65536int acc_log_request( struct sip_msg *rq, struct hdr_field *to, str *txt, str *phrase){ static char log_msg[MAX_LOG_SIZE]; static str* val_arr[ALL_LOG_FMT_LEN+MAX_ACC_EXTRA]; static str atr_arr[ALL_LOG_FMT_LEN+MAX_ACC_EXTRA]; int len; char *p; int attr_cnt; int attr_len; int i; leg_loop_VARS; if (skip_cancel(rq)) return 1; attr_cnt=fmt2strar( log_fmt, rq, to, phrase, &len, &attr_len, val_arr, atr_arr); if (!attr_cnt) { LOG(L_ERR, "ERROR:acc:acc_log_request: fmt2strar failed\n"); return -1; } attr_cnt += extra2strar( log_extra, rq, &len, &attr_len, atr_arr+attr_cnt, val_arr+attr_cnt); if ( MAX_LOG_SIZE < len+ attr_len+ACC_LEN+txt->len+A_EOL_LEN + attr_cnt*(A_SEPARATOR_LEN+A_EQ_LEN)-A_SEPARATOR_LEN) { LOG(L_ERR, "ERROR:acc:acc_log_request: buffer to small\n"); return -1; } /* skip leading text and begin with first item's * separator ", " which will be overwritten by the * leading text later * */ p=log_msg+(ACC_LEN+txt->len-A_SEPARATOR_LEN); for (i=0; i<attr_cnt; i++) { memcpy(p, A_SEPARATOR, A_SEPARATOR_LEN ); p+=A_SEPARATOR_LEN; memcpy(p, atr_arr[i].s, atr_arr[i].len); p+=atr_arr[i].len; memcpy(p, A_EQ, A_EQ_LEN); p+=A_EQ_LEN; memcpy(p, val_arr[i]->s, val_arr[i]->len); p+=val_arr[i]->len; } if ( multileg_enabled ) { BEGIN_loop_all_legs; if (p+A_SEPARATOR_LEN+7+A_EQ_LEN+A_SEPARATOR_LEN+7+A_EQ_LEN +(src?src_val.s.len:NA_LEN)+(dst?dst_val.s.len:NA_LEN) > log_msg+MAX_LOG_SIZE ) { LOG(L_ERR, "ERROR:acc:acc_log_request: buffer to small\n"); return -1; } if (src) { memcpy(p, A_SEPARATOR"src_leg"A_EQ, A_SEPARATOR_LEN+7+A_EQ_LEN ); p+=A_SEPARATOR_LEN+7+A_EQ_LEN; memcpy(p, src_val.s.s, src_val.s.len); p+=src_val.s.len; } else { memcpy(p, A_SEPARATOR"src_leg"A_EQ NA, A_SEPARATOR_LEN+7+A_EQ_LEN+NA_LEN); p+=A_SEPARATOR_LEN+7+A_EQ_LEN+NA_LEN; } if (dst) { memcpy(p, A_SEPARATOR"dst_leg"A_EQ, A_SEPARATOR_LEN+7+A_EQ_LEN ); p+=A_SEPARATOR_LEN+7+A_EQ_LEN; memcpy(p, dst_val.s.s, dst_val.s.len); p+=dst_val.s.len; } else { memcpy(p, A_SEPARATOR"dst_leg"A_EQ NA,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -