Mercurial > hg > freeDiameter
view extensions/rt_ereg/rtereg.c @ 1554:566bb46cc73f
Updated copyright information
author | Sebastien Decugis <sdecugis@freediameter.net> |
---|---|
date | Tue, 06 Oct 2020 21:34:53 +0800 |
parents | c48725959e63 |
children |
line wrap: on
line source
/********************************************************************************************************* * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * * Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * * permitted provided that the following conditions are met: * * * * * Redistributions of source code must retain the above * * copyright notice, this list of conditions and the * * following disclaimer. * * * * * Redistributions in binary form must reproduce the above * * copyright notice, this list of conditions and the * * following disclaimer in the documentation and/or other * * materials provided with the distribution. * * * * * Neither the name of the WIDE Project or NICT nor the * * names of its contributors may be used to endorse or * * promote products derived from this software without * * specific prior written permission of WIDE Project and * * NICT. * * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *********************************************************************************************************/ /* * This extension allows to perform some pattern-matching on an AVP * and send the message to a server accordingly. * See rt_ereg.conf.sample file for the format of the configuration file. */ #include <pthread.h> #include <signal.h> #include "rtereg.h" static pthread_rwlock_t rte_lock; #define MODULE_NAME "rt_ereg" static char *rt_ereg_config_file; /* The configuration structure */ struct rtereg_conf *rtereg_conf; int rtereg_conf_size; #ifndef HAVE_REG_STARTEND static char * buf = NULL; static size_t bufsz; static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; #endif /* HAVE_REG_STARTEND */ static int rtereg_init(void); static int rtereg_init_config(void); static void rtereg_fini(void); void rtereg_conf_free(struct rtereg_conf *config_struct, int config_size) { int i, j; /* Destroy the data */ for (j=0; j<config_size; j++) { if (config_struct[j].rules) { for (i = 0; i < config_struct[j].rules_nb; i++) { free(config_struct[j].rules[i].pattern); free(config_struct[j].rules[i].server); regfree(&config_struct[j].rules[i].preg); } } free(config_struct[j].avps); free(config_struct[j].rules); } free(config_struct); } static int proceed(char * value, size_t len, struct fd_list * candidates, int conf) { int i; for (i = 0; i < rtereg_conf[conf].rules_nb; i++) { /* Does this pattern match the value? */ struct rtereg_rule * r = &rtereg_conf[conf].rules[i]; int err = 0; struct fd_list * c; LOG_D("[rt_ereg] attempting pattern matching of '%.*s' with rule '%s'", (int)len, value, r->pattern); #ifdef HAVE_REG_STARTEND { regmatch_t pmatch[1]; memset(pmatch, 0, sizeof(pmatch)); pmatch[0].rm_so = 0; pmatch[0].rm_eo = len; err = regexec(&r->preg, value, 0, pmatch, REG_STARTEND); } #else /* HAVE_REG_STARTEND */ { /* We have a 0-terminated string */ err = regexec(&r->preg, value, 0, NULL, 0); } #endif /* HAVE_REG_STARTEND */ if (err == REG_NOMATCH) continue; if (err != 0) { char * errstr; size_t bl; /* Error while compiling the regex */ LOG_E("[rt_ereg] error while executing the regular expression '%s':", r->pattern); /* Get the error message size */ bl = regerror(err, &r->preg, NULL, 0); /* Alloc the buffer for error message */ CHECK_MALLOC( errstr = malloc(bl) ); /* Get the error message content */ regerror(err, &r->preg, errstr, bl); LOG_E("\t%s", errstr); /* Free the buffer, return the error */ free(errstr); return (err == REG_ESPACE) ? ENOMEM : EINVAL; } /* From this point, the expression matched the AVP value */ LOG_D("[rt_ereg] Match: '%s' to value '%.*s' => '%s' += %d", r->pattern, (int)len, value, r->server, r->score); for (c = candidates->next; c != candidates; c = c->next) { struct rtd_candidate * cand = (struct rtd_candidate *)c; if (strcmp(r->server, cand->diamid) == 0) { cand->score += r->score; break; } } }; return 0; } static int find_avp(msg_or_avp *where, int conf_index, int level, struct fd_list * candidates) { struct dict_object *what; struct dict_avp_data dictdata; struct avp *nextavp = NULL; struct avp_hdr *avp_hdr = NULL; /* iterate over all AVPs and try to find a match */ // for (i = 0; i<rtereg_conf[j].level; i++) { if (level > rtereg_conf[conf_index].level) { LOG_E("internal error, dug too deep"); return 1; } what = rtereg_conf[conf_index].avps[level]; CHECK_FCT(fd_dict_getval(what, &dictdata)); CHECK_FCT(fd_msg_browse(where, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL)); while (nextavp) { CHECK_FCT(fd_msg_avp_hdr(nextavp, &avp_hdr)); if ((avp_hdr->avp_code == dictdata.avp_code) && (avp_hdr->avp_vendor == dictdata.avp_vendor)) { if (level != rtereg_conf[conf_index].level - 1) { LOG_D("[rt_ereg] found grouped AVP %d (vendor %d), digging deeper", avp_hdr->avp_code, avp_hdr->avp_vendor); CHECK_FCT(find_avp(nextavp, conf_index, level+1, candidates)); } else { struct dictionary * dict; LOG_D("[rt_ereg] found AVP %d (vendor %d)", avp_hdr->avp_code, avp_hdr->avp_vendor); CHECK_FCT(fd_dict_getdict(what, &dict)); CHECK_FCT_DO(fd_msg_parse_dict(nextavp, dict, NULL), /* nothing */); if (avp_hdr->avp_value != NULL) { LOG_A("avp_hdr->avp_value NOT NULL, matching"); #ifndef HAVE_REG_STARTEND int ret; /* Lock the buffer */ CHECK_POSIX( pthread_mutex_lock(&mtx) ); /* Augment the buffer if needed */ if (avp_hdr->avp_value->os.len >= bufsz) { char *newbuf; CHECK_MALLOC_DO( newbuf = realloc(buf, avp_hdr->avp_value->os.len + 1), { pthread_mutex_unlock(&mtx); return ENOMEM; } ); /* Update buffer and buffer size */ buf = newbuf; bufsz = avp_hdr->avp_value->os.len + 1; } /* Copy the AVP value */ memcpy(buf, avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len); buf[avp_hdr->avp_value->os.len] = '\0'; /* Now apply the rules */ ret = proceed(buf, avp_hdr->avp_value->os.len, candidates, conf_index); CHECK_POSIX(pthread_mutex_unlock(&mtx)); CHECK_FCT(ret); #else /* HAVE_REG_STARTEND */ CHECK_FCT( proceed((char *) avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len, candidates, conf_index) ); #endif /* HAVE_REG_STARTEND */ } } } CHECK_FCT(fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL)); } return 0; } /* The callback called on new messages */ static int rtereg_out(void * cbdata, struct msg ** pmsg, struct fd_list * candidates) { msg_or_avp *where; int j, ret; LOG_A("[rt_ereg] rtereg_out arguments: %p %p %p", cbdata, *pmsg, candidates); CHECK_PARAMS(pmsg && *pmsg && candidates); if (pthread_rwlock_rdlock(&rte_lock) != 0) { fd_log_notice("%s: read-lock failed, skipping handler", MODULE_NAME); return 0; } ret = 0; /* Check if it is worth processing the message */ if (!FD_IS_LIST_EMPTY(candidates)) { /* Now search the AVPs in the message */ for (j=0; j<rtereg_conf_size; j++) { where = *pmsg; LOG_D("[rt_ereg] iterating over AVP group %d", j); if ((ret=find_avp(where, j, 0, candidates)) != 0) { break; } } } if (pthread_rwlock_unlock(&rte_lock) != 0) { fd_log_notice("%s: read-unlock failed after rtereg_out, exiting", MODULE_NAME); exit(1); } return ret; } /* handler */ static struct fd_rt_out_hdl * rtereg_hdl = NULL; static volatile int in_signal_handler = 0; /* signal handler */ static void sig_hdlr(void) { struct rtereg_conf *old_config; int old_config_size; if (in_signal_handler) { fd_log_error("%s: already handling a signal, ignoring new one", MODULE_NAME); return; } in_signal_handler = 1; if (pthread_rwlock_wrlock(&rte_lock) != 0) { fd_log_error("%s: locking failed, aborting config reload", MODULE_NAME); return; } /* save old config in case reload goes wrong */ old_config = rtereg_conf; old_config_size = rtereg_conf_size; rtereg_conf = NULL; rtereg_conf_size = 0; if (rtereg_init_config() != 0) { fd_log_notice("%s: error reloading configuration, restoring previous configuration", MODULE_NAME); rtereg_conf = old_config; rtereg_conf_size = old_config_size; } else { rtereg_conf_free(old_config, old_config_size); } if (pthread_rwlock_unlock(&rte_lock) != 0) { fd_log_error("%s: unlocking failed after config reload, exiting", MODULE_NAME); exit(1); } fd_log_notice("%s: reloaded configuration, %d AVP group%s defined", MODULE_NAME, rtereg_conf_size, rtereg_conf_size != 1 ? "s" : ""); in_signal_handler = 0; } /* entry point */ static int rtereg_entry(char * conffile) { LOG_A("[rt_ereg] started with conffile '%p'", conffile); rt_ereg_config_file = conffile; if (rtereg_init() != 0) { return 1; } /* Register reload callback */ CHECK_FCT(fd_event_trig_regcb(SIGUSR1, MODULE_NAME, sig_hdlr)); fd_log_notice("%s: configured, %d AVP group%s defined", MODULE_NAME, rtereg_conf_size, rtereg_conf_size != 1 ? "s" : ""); return 0; } static int rtereg_init_config(void) { /* Initialize the configuration */ if ((rtereg_conf=calloc(sizeof(*rtereg_conf), 1)) == NULL) { LOG_E("[rt_ereg] malloc failured"); return 1; } rtereg_conf_size = 1; /* Parse the configuration file */ CHECK_FCT( rtereg_conf_handle(rt_ereg_config_file) ); return 0; } /* Load */ static int rtereg_init(void) { int ret; pthread_rwlock_init(&rte_lock, NULL); if (pthread_rwlock_wrlock(&rte_lock) != 0) { fd_log_notice("%s: write-lock failed, aborting", MODULE_NAME); return EDEADLK; } if ((ret=rtereg_init_config()) != 0) { pthread_rwlock_unlock(&rte_lock); return ret; } if (pthread_rwlock_unlock(&rte_lock) != 0) { fd_log_notice("%s: write-unlock failed, aborting", MODULE_NAME); return EDEADLK; } /* Register the callback */ CHECK_FCT( fd_rt_out_register( rtereg_out, NULL, 1, &rtereg_hdl ) ); /* We're done */ return 0; } /* Unload */ static void rtereg_fini(void) { /* Unregister the cb */ CHECK_FCT_DO( fd_rt_out_unregister ( rtereg_hdl, NULL ), /* continue */ ); #ifndef HAVE_REG_STARTEND free(buf); buf = NULL; bufsz = 0; #endif /* HAVE_REG_STARTEND */ if (pthread_rwlock_wrlock(&rte_lock) != 0) { fd_log_error("%s: write-locking failed in fini, giving up", MODULE_NAME); return; } /* Destroy the data */ rtereg_conf_free(rtereg_conf, rtereg_conf_size); rtereg_conf = NULL; rtereg_conf_size = 0; if (pthread_rwlock_unlock(&rte_lock) != 0) { fd_log_error("%s: write-unlocking failed in fini", MODULE_NAME); return; } /* Done */ return ; } void fd_ext_fini(void) { rtereg_fini(); } EXTENSION_ENTRY(MODULE_NAME, rtereg_entry);