freeDiameter/ 0000755 0001750 0001750 00000000000 13324704611 012463 5 ustar ruben ruben freeDiameter/INSTALL.pkgsrc 0000644 0001750 0001750 00000001205 13324704440 015002 0 ustar ruben ruben The simplest way to install on NetBSD, DragonFly, MirBSD or other
pkgsrc platforms is:
pkg_add freeDiameter
If no binary package exists:
cd /usr/pkgsrc/net/freeDiameter
make install
If you want to build and install freeDiameter from the repository,
you'll need to install:
pkg_add bison pkg-config libidn gnutls libgcrypt mercurial
and follow the usual installation note.
NetBSD does not support SCTP, so you have to add
-DDISABLE_SCTP:BOOL=ON
to the cmake command line.
For the optional extensions, you need mysql*-client, postgres*-client,
libxml2. For the debugging extensions, you also need swig and a
python -- install as usual.
freeDiameter/INSTALL 0000644 0001750 0001750 00000002663 13324704440 013523 0 ustar ruben ruben This package uses CMake (cmake.org) as building system. You'll need the cmake tool in order
to generate the Makefiles for your platform. You can also select which extensions must be built
with cmake. After the initial cmake configuration, you will need several tools such as make, a
C compiler (tested mostly with gcc), flex (> 2.5.4), bison, ...
You may retrieve the source package in several ways:
- as a pre-packaged version for your platform. Check the website for supported platforms.
- as a tarball containing the source.
- directly from the repository using Mercurial tool:
hg clone http://www.freediameter.net/hg/freeDiameter
In order to update later:
hg pull -u
Building in a separate directory is recommended:
# mkdir build
# cd build
# cmake ../
# make
You can pass options to instruct cmake about which components to compile on the command-line.
You can also use a CMake front-end (for example ccmake or cmake-gui).
If your cmake installation is recent, the edit_cache target is a good help as well:
# mkdir build
# cd build
# cmake ../
# make help
# make edit_cache
# make
You can disable the unit tests by doing:
# cmake -DBUILD_TESTING:BOOL=OFF ../
# make
When the tests are enabled, you can run them with:
# make test
See the INSTALL.* files for additional platform-specific information.
Note that the most up-to-date INSTALL file is the Ubuntu one. You may want to read there
in addition to your own platform file.
freeDiameter/INSTALL.Ubuntu 0000644 0001750 0001750 00000007537 13324704440 015011 0 ustar ruben ruben See http://www.freediameter.net/trac/wiki/DebRepository for instructions on how to
install freeDiameter from existing package.
See INSTALL file for general instructions on building freeDiameter from sources.
The remaining of this file contains specific instructions for building the freeDiameter
package files for Debian and Ubuntu systems.
freeDiameter is shipped with the necessary files to generate the Debian package.
All related files are in the directory "contrib/debian" of the distribution.
As a first step, you have to link this directory from your top-level dir:
ln -s contrib/debian
============================================
The following packages are required to compile freeDiameter from source:
cmake make gcc g++ flex bison libsctp1 libsctp-dev libgnutls-dev libgcrypt-dev libidn11-dev
(note that libidn and libsctp can be avoided by defining DISABLE_SCTP and DIAMID_IDNA_REJECT)
Additionnaly, these ones may be useful:
mercurial gdb
Extensions additional dependencies:
app_acct:
libpq-dev
app_sip:
app_diameap:
libmysqlclient-dev
dict_legacy_xml:
libxml2-dev
dbg_interactive:
swig python-dev
============================================
If your debhelper environment is recent (> 7.3.9 for cmake support),
the following commands should generate the freeDiameter packages for you:
# Install the dependencies for building the source:
sudo apt-get -y install mercurial cmake make gcc g++ bison flex libsctp-dev libgnutls-dev libgcrypt-dev libidn11-dev ssl-cert debhelper fakeroot \
libpq-dev libmysqlclient-dev libxml2-dev swig python-dev
# Retrieve the latest version of the source package
cd
hg clone http://www.freediameter.net/hg/freeDiameter
cd freeDiameter
# Prepare for Debian package
ln -s contrib/debian
# Build the packages
fakeroot dh binary
# Install the packages
cd ..
sudo dpkg -i *.deb
# The following lines may help you getting started.... (execute as root)
ln -s /etc/ssl/certs/ssl-cert-snakeoil.pem /etc/ssl/certs/freeDiameter.pem
ln -s /etc/ssl/private/ssl-cert-snakeoil.key /etc/ssl/private/freeDiameter.key
zcat /usr/share/doc/freediameter-daemon/examples/freediameter.conf.sample.gz > /etc/freeDiameter/freeDiameter.conf
echo "Identity=\"localhost.localdomain\";" >> /etc/freeDiameter/freeDiameter.conf
freeDiameterd
===========================================
freeDiameter is tested regularly on all current Ubuntu distributions since Intrepid (compiled from source as described below)
Ubuntu Hardy support is known to be broken.
The Debian package has been generated with success on Debian (>=Squeeze) and Ubuntu (>=Karmic).
===========================================-
Step by step instructions without using the debhelper tools:
1) Install all packages dependencies
# sudo apt-get install mercurial cmake make gcc g++ bison flex libsctp-dev libgnutls-dev libgcrypt-dev libidn11-dev
2) (OPTION) If you will compile modules that require postgresql, also install:
# sudo apt-get install libpq-dev
and for mysql:
# sudo apt-get install libmysqlclient-dev
3) (OPTION) If you want a more user-friendly interface, install this:
# sudo apt-get install cmake-curses-gui
4) Retrieve the source files
# hg clone http://www.freediameter.net/hg/freeDiameter
5) Create a build directory, and enter it
# mkdir fDbuild
# cd fDbuild
6) Configure and generate the Makefiles
# cmake ../freeDiameter
# make edit_cache
7) Compile all files:
# make
8) (OPTION) See available targets:
# make help
9) (OPTION) Check the software has a correct basic behavior on your environment. -- only if you did not disable the tests in step 6
# make test
10) (OPTION) Install the software in configured locations:
# sudo make install
===========================================
Note: On Ubuntu Quantal Quetzal, you may have to force use of python 2.7
library for the dbg_interactive extension, as on some systems CMake selects the 3.2 library
by default.
freeDiameter/INSTALL.Fedora 0000644 0001750 0001750 00000002261 13324704440 014714 0 ustar ruben ruben ### Very short install notes for Fedora (tested on Fedora 13 and 14)
# Please refer to other INSTALL files for more detailed instructions (e.g. extension-specific notes).
Dependencies on Fedora 13 (from minimal system):
# yum install cmake make gcc gcc-c++ flex bison lksctp-tools-devel gnutls-devel libgcrypt-devel libidn-devel
In addition, if you have not already retrieved the latest source:
# yum install mercurial
Following dependencies are optional, depending on which extensions you plan to compile
app_acct:
# yum install postgresql-devel
optional:
# yum install postgresql-server
app_sip:
app_diameap:
# yum install mysql-devel
optional:
# yum install mysql-server
dict_legacy_xml:
# yum install libxml2 libxml2-devel
dbg_interactive:
# yum install swig python-devel
NOTE: You may need to change something in SELinux to allow binding the SCTP socket on Diameter port.
The following command is the very naive way to do it, you can probably do better (feedback appreciated!)
# setenforce Permissive
There is currently no Fedora-specific package for freeDiameter.
You are welcome to contribute packaging scripts.
Please contact dev@freediameter.net for more details.
freeDiameter/include/ 0000755 0001750 0001750 00000000000 13324704440 014106 5 ustar ruben ruben freeDiameter/include/freeDiameter/ 0000755 0001750 0001750 00000000000 13324704466 016512 5 ustar ruben ruben freeDiameter/include/freeDiameter/freeDiameter-host.h.in 0000644 0001750 0001750 00000010461 13324704440 022631 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2015, 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. *
*********************************************************************************************************/
/* Configuration from compile-time */
#ifndef FD_IS_CONFIG
#define FD_IS_CONFIG
#ifdef __cplusplus
extern "C" {
#endif
#cmakedefine HAVE_NTOHLL
#cmakedefine HAVE_MALLOC_H
#cmakedefine HAVE_SIGNALENT_H
#cmakedefine HAVE_AI_ADDRCONFIG
#cmakedefine HAVE_CLOCK_GETTIME
#cmakedefine HAVE_STRNDUP
#cmakedefine HAVE_PTHREAD_BAR
#cmakedefine HOST_BIG_ENDIAN @HOST_BIG_ENDIAN@
#cmakedefine DISABLE_SCTP
#cmakedefine DEBUG_SCTP
#cmakedefine DEBUG_WITH_META
#cmakedefine SCTP_USE_MAPPED_ADDRESSES
#cmakedefine SCTP_CONNECTX_4_ARGS
#cmakedefine SKIP_DLCLOSE
#cmakedefine DIAMID_IDNA_IGNORE
#cmakedefine DIAMID_IDNA_REJECT
#cmakedefine DISABLE_PEER_EXPIRY
#cmakedefine WORKAROUND_ACCEPT_INVALID_VSAI
#cmakedefine GNUTLS_VERSION_210
#cmakedefine GNUTLS_VERSION_212
#cmakedefine GNUTLS_VERSION_300
#cmakedefine GNUTLS_VERSION_310
#cmakedefine ERRORS_ON_TODO
#cmakedefine DEBUG
#cmakedefine FD_PROJECT_BINARY "@FD_PROJECT_BINARY@"
#cmakedefine FD_PROJECT_NAME "@FD_PROJECT_NAME@"
#cmakedefine FD_PROJECT_VERSION_MAJOR @FD_PROJECT_VERSION_MAJOR@
#ifndef FD_PROJECT_VERSION_MAJOR
# define FD_PROJECT_VERSION_MAJOR 0
#endif /*FD_PROJECT_VERSION_MAJOR*/
#cmakedefine FD_PROJECT_VERSION_MINOR @FD_PROJECT_VERSION_MINOR@
#ifndef FD_PROJECT_VERSION_MINOR
# define FD_PROJECT_VERSION_MINOR 0
#endif /*FD_PROJECT_VERSION_MINOR*/
#cmakedefine FD_PROJECT_VERSION_REV @FD_PROJECT_VERSION_REV@
#ifndef FD_PROJECT_VERSION_REV
# define FD_PROJECT_VERSION_REV 0
#endif /*FD_PROJECT_VERSION_REV*/
#cmakedefine FD_PROJECT_VERSION_API @FD_PROJECT_VERSION_API@
#ifndef FD_PROJECT_VERSION_API
# define FD_PROJECT_VERSION_API 0
#endif /*FD_PROJECT_VERSION_API*/
#cmakedefine FD_PROJECT_COPYRIGHT "@FD_PROJECT_COPYRIGHT@"
#cmakedefine DEFAULT_CONF_PATH "@DEFAULT_CONF_PATH@"
#cmakedefine DEFAULT_EXTENSIONS_PATH "@DEFAULT_EXTENSIONS_PATH@"
#ifndef FD_DEFAULT_CONF_FILENAME
#define FD_DEFAULT_CONF_FILENAME "freeDiameter.conf"
#endif /* FD_DEFAULT_CONF_FILENAME */
/* Maximum number of hooks handlers that can be registered. Make this compilation option if needed */
#define FD_HOOK_HANDLE_LIMIT 5
#ifdef __cplusplus
}
#endif
#endif /* FD_IS_CONFIG */
freeDiameter/include/freeDiameter/libfdcore.h 0000644 0001750 0001750 00000164672 13324704466 020634 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2016, 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. *
*********************************************************************************************************/
#ifndef _LIBFDCORE_H
#define _LIBFDCORE_H
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#include
/* GNUTLS version */
#ifndef GNUTLS_VERSION
#define GNUTLS_VERSION LIBGNUTLS_VERSION
#endif /* GNUTLS_VERSION */
/* GNUTLS calls debug level */
#ifndef GNUTLS_DBG_LEVEL
#define GNUTLS_DBG_LEVEL ANNOYING
#endif /* GNUTLS_DBG_LEVEL */
/* Check the return value of a GNUTLS function, log and propagate */
#define CHECK_GNUTLS_GEN( faillevel, __call__, __fallback__ ) { \
CHECK_PRELUDE(__call__); \
if (__ret__ < 0) { \
LOG(faillevel, "TLS ERROR: in '%s' :\t%s", #__call__ , gnutls_strerror(__ret__)); \
__fallback__; \
} \
}
/* we use this macro to help debugging gnutls usage issues -- just change the content to display what you need */
#define GNUTLS_TRACE( __call__) { \
TRACE_CALL("Check: %s", #__call__ ); \
(__call__); \
}
#ifndef EXCLUDE_DEPRECATED
/* Macro for transition, replace with CHECK_GNUTLS_GEN */
#define CHECK_GNUTLS_DO( __call__, __fallback__ ) \
CHECK_GNUTLS_GEN( FD_LOG_ERROR, __call__, __fallback__ )
#endif /* EXCLUDE_DEPRECATED */
/*============================================================*/
/* INITIALIZATION */
/*============================================================*/
/* Initialize the libfdcore internals. This also initializes libfdproto */
int fd_core_initialize(void);
/* A string describing the version of the library */
extern const char fd_core_version[];
/* Parse the freeDiameter.conf configuration file, load the extensions */
int fd_core_parseconf(const char * conffile);
/* Start the server & client threads */
int fd_core_start(void);
/* Block until the framework has completed its initialization -- useful for extensions */
int fd_core_waitstartcomplete(void);
/* Initialize shutdown of the framework */
int fd_core_shutdown(void);
/* Wait for the shutdown to be complete -- this should always be called after fd_core_shutdown */
int fd_core_wait_shutdown_complete(void);
/*============================================================*/
/* CONFIG */
/*============================================================*/
/* Structure to hold the configuration of the freeDiameter daemon */
#define EYEC_CONFIG 0xC011F16
struct fd_config {
int cnf_eyec; /* Eye catcher: EYEC_CONFIG */
const char *cnf_file; /* Configuration file to parse, default is DEFAULT_CONF_FILE */
DiamId_t cnf_diamid; /* Diameter Identity of the local peer (FQDN -- ASCII) */
size_t cnf_diamid_len;/* cached length of the previous string */
DiamId_t cnf_diamrlm; /* Diameter realm of the local peer, default to realm part of cnf_diamid */
size_t cnf_diamrlm_len;/* length of the previous string */
unsigned int cnf_timer_tc; /* The value in seconds of the default Tc timer */
unsigned int cnf_timer_tw; /* The value in seconds of the default Tw timer */
uint16_t cnf_port; /* the local port for legacy Diameter (default: 3868) in host byte order */
uint16_t cnf_port_tls; /* the local port for Diameter/TLS (default: 5868) in host byte order */
uint16_t cnf_port_3436; /* Open an additional server port to listen to old TLS/SCTP clients (RFC3436, freeDiameter versions < 1.2.0) */
uint16_t cnf_sctp_str; /* default max number of streams for SCTP associations (def: 30) */
struct fd_list cnf_endpoints; /* the local endpoints to bind the server to. list of struct fd_endpoint. default is empty (bind all). After servers are started, this is the actual list of endpoints including port information. */
int cnf_thr_srv; /* Number of threads per servers handling the connection state machines */
struct fd_list cnf_apps; /* Applications locally supported (except relay, see flags). Use fd_disp_app_support to add one. list of struct fd_app. */
uint16_t cnf_dispthr; /* Number of dispatch threads to create */
struct {
unsigned no_fwd : 1; /* the peer does not relay messages (0xffffff app id) */
unsigned no_ip4 : 1; /* disable IP */
unsigned no_ip6 : 1; /* disable IPv6 */
unsigned no_tcp : 1; /* disable use of TCP */
unsigned no_sctp: 1; /* disable the use of SCTP */
unsigned pr_tcp : 1; /* prefer TCP over SCTP */
unsigned tls_alg: 1; /* TLS algorithm for initiated cnx. 0: separate port. 1: inband-security (old) */
} cnf_flags;
struct {
int tls_disabled;
/* Credentials parameters (backup) */
char * cert_file;
char * key_file;
char * ca_file;
int ca_file_nr;
char * crl_file;
char * prio_string;
unsigned int dh_bits;
char * dh_file;
/* GNUTLS parameters */
gnutls_priority_t prio_cache;
gnutls_dh_params_t dh_cache;
/* GNUTLS server credential(s) */
gnutls_certificate_credentials_t credentials; /* contains local cert + trust anchors */
#ifdef GNUTLS_VERSION_300
gnutls_x509_trust_list_t trustlist; /* the logic to check local certificate has changed */
#endif /* GNUTLS_VERSION_300 */
} cnf_sec_data;
uint32_t cnf_orstateid; /* The value to use in Origin-State-Id, default to random value */
struct dictionary *cnf_dict; /* pointer to the global dictionary */
struct fifo *cnf_main_ev; /* events for the daemon's main (struct fd_event items) */
};
extern struct fd_config *fd_g_config; /* The pointer to access the global configuration, initalized in main */
/*============================================================*/
/* PEERS */
/*============================================================*/
/* States of a peer */
enum peer_state {
/* Stable states */
STATE_NEW = 0, /* The peer has been just been created, PSM thread not started yet */
STATE_OPEN, /* Connexion established */
/* Peer state machine */
STATE_CLOSED, /* No connection established, will re-attempt after TcTimer. */
STATE_CLOSING, /* the connection is being shutdown (DPR/DPA in progress) */
STATE_WAITCNXACK, /* Attempting to establish transport-level connection */
STATE_WAITCNXACK_ELEC, /* Received a CER from this same peer on an incoming connection (other peer object), while we were waiting for cnx ack */
STATE_WAITCEA, /* Connection established, CER sent, waiting for CEA */
/* STATE_WAITRETURNS_ELEC, */ /* This state is not stable and therefore deprecated:
We have sent a CER on our initiated connection, and received a CER from the remote peer on another connection. Election.
If we win the election, we must disconnect the initiated connection and send a CEA on the other => we go to OPEN state.
If we lose, we disconnect the other connection (receiver) and fallback to WAITCEA state. */
STATE_OPEN_HANDSHAKE, /* TLS Handshake and validation are in progress in open state -- we use it only for debug purpose, it is never displayed */
/* Failover state machine */
STATE_SUSPECT, /* A DWR was sent and not answered within TwTime. Failover in progress. */
STATE_REOPEN, /* Connection has been re-established, waiting for 3 DWR/DWA exchanges before putting back to service */
/* Ordering issues with multistream & state machine. -- see top of p_psm.c for explanation */
STATE_OPEN_NEW, /* after CEA is sent, until a new message is received. Force ordering in this state */
STATE_CLOSING_GRACE, /* after DPA is sent or received, give a short delay for messages in the pipe to be received. */
/* Error state */
STATE_ZOMBIE /* The PSM thread is not running anymore; it must be re-started or peer should be deleted. */
#define STATE_MAX STATE_ZOMBIE
};
/* The following macro is called in freeDiameter/p_psm.c */
#define DECLARE_STATE_STR() \
const char *peer_state_str[] = { \
"STATE_NEW" \
, "STATE_OPEN" \
, "STATE_CLOSED" \
, "STATE_CLOSING" \
, "STATE_WAITCNXACK" \
, "STATE_WAITCNXACK_ELEC" \
, "STATE_WAITCEA" \
, "STATE_OPEN_HANDSHAKE" \
, "STATE_SUSPECT" \
, "STATE_REOPEN" \
, "STATE_OPEN_NEW" \
, "STATE_CLOSING_GRACE" \
, "STATE_ZOMBIE" \
};
extern const char *peer_state_str[];
#define STATE_STR(state) \
(((unsigned)(state)) <= STATE_MAX ? peer_state_str[((unsigned)(state)) ] : "")
/* Constants for the peer_info structure below */
#define PI_P3_DEFAULT 0 /* Use any available protocol */
#define PI_P3_IP 1 /* Use only IP to connect to this peer */
#define PI_P3_IPv6 2 /* resp, IPv6 */
#define PI_P4_DEFAULT 0 /* Attempt any available protocol */
#define PI_P4_TCP 1 /* Only use TCP */
#define PI_P4_SCTP 2 /* Only use SCTP */
#define PI_ALGPREF_SCTP 0 /* SCTP is attempted first (default) */
#define PI_ALGPREF_TCP 1 /* TCP is attempted first */
#define PI_SEC_DEFAULT 0 /* New TLS security (handshake after connection, protecting also CER/CEA) */
#define PI_SEC_NONE 1 /* Transparent security with this peer (IPsec) */
#define PI_SEC_TLS_OLD 2 /* Old TLS security (use Inband-Security-Id AVP during CER/CEA) */
/* Set sec = 3 to authorize use of (Inband-Security-Id == NONE) with this peer, sec = 2 only authorizing TLS */
#define PI_SCTPSEC_DEF 0 /* Use DTLS over SCTP to connect to this peer (default) */
#define PI_SCTPSEC_3436 1 /* Use TLS over SCTP to connect to this peer (RFC3436) */
#define PI_EXP_NONE 0 /* the peer entry does not expire */
#define PI_EXP_INACTIVE 1 /* the peer entry expires (i.e. is deleted) after pi_lft seconds without activity */
#define PI_PRST_NONE 0 /* the peer entry is deleted after disconnection / error */
#define PI_PRST_ALWAYS 1 /* the peer entry is persistant (will be kept as ZOMBIE in case of error) */
/* Information about a remote peer */
struct peer_info {
DiamId_t pi_diamid; /* (supposedly) UTF-8, \0 terminated. The Diameter Identity of the remote peer. */
size_t pi_diamidlen; /* cached length of pi_diamid */
struct {
struct {
unsigned pro3 :2; /* PI_P3_* */
unsigned pro4 :2; /* PI_P4_* */
unsigned alg :1; /* PI_ALGPREF_* */
unsigned sec :2; /* PI_SEC_* */
unsigned sctpsec :1; /* PI_SCTPSEC_* */
unsigned exp :1; /* PI_EXP_* */
unsigned persist :1; /* PI_PRST_* */
} pic_flags; /* Flags influencing the connection to the remote peer */
DiamId_t pic_realm; /* If configured, the daemon will check the received realm in CER/CEA matches this. */
uint16_t pic_port; /* port to connect to. 0: default. */
uint32_t pic_lft; /* lifetime of this peer when inactive (see pic_flags.exp definition) */
int pic_tctimer; /* use this value for TcTimer instead of global, if != 0 */
int pic_twtimer; /* use this value for TwTimer instead of global, if != 0 */
char * pic_priority; /* Priority string for GnuTLS if we don't use the default */
} config; /* Configured data (static for this peer entry) */
struct {
/* enum peer_state pir_state; */
/* Since 1.1.0, read the state with fd_peer_getstate(peer). */
DiamId_t pir_realm; /* The received realm in CER/CEA. */
size_t pir_realmlen; /* length of the realm */
uint32_t pir_vendorid; /* Content of the Vendor-Id AVP, or 0 by default */
uint32_t pir_orstate; /* Origin-State-Id value */
os0_t pir_prodname; /* copy of Product-Name AVP (\0 terminated) */
uint32_t pir_firmrev; /* Content of the Firmware-Revision AVP */
int pir_relay; /* The remote peer advertized the relay application */
struct fd_list pir_apps; /* applications advertised by the remote peer, except relay (pi_flags.relay) */
int pir_isi; /* Inband-Security-Id advertised (PI_SEC_* bits) */
uint32_t pir_lastDC; /* The last Disconnect-Cause value received */
int pir_proto; /* The L4 protocol currently used with the peer (IPPROTO_TCP or IPPROTO_SCTP) */
const gnutls_datum_t *pir_cert_list; /* The (valid) credentials that the peer has presented, or NULL if TLS is not used */
/* This is inspired from http://www.gnu.org/software/gnutls/manual/gnutls.html#ex_003ax509_002dinfo
see there for example of using this data */
unsigned int pir_cert_list_size; /* Number of certificates in the list */
} runtime; /* Data populated after connection, may change between 2 connections -- not used by fd_peer_add */
struct fd_list pi_endpoints; /* Endpoint(s) of the remote peer (configured, discovered, or advertized). list of struct fd_endpoint. DNS resolved if empty. */
};
struct peer_hdr {
struct fd_list chain; /* Link into the list of all the peers, ordered by their Diameter Id (fd_os_cmp) */
struct peer_info info; /* The public data */
/* This header is followed by more data in the private peer structure definition */
};
/* the global list of peers.
Since we are not expecting so many connections, we don't use a hash, but it might be changed.
The list items are peer_hdr structures (actually, fd_peer, but the cast is OK) */
extern struct fd_list fd_g_peers;
extern pthread_rwlock_t fd_g_peers_rw; /* protect the list */
/*
* FUNCTION: fd_peer_add
*
* PARAMETERS:
* info : Information to create the peer.
* orig_dbg : A string indicating the origin of the peer information, for debug (ex: conf, redirect, ...)
* cb : optional, a callback to call (once) when the peer connection is established or failed
* cb_data : opaque data to pass to the callback.
*
* DESCRIPTION:
* Add a peer to the list of peers to which the daemon must maintain a connexion.
*
* The content of info parameter is copied, except for the list of endpoints if
* not empty, which is simply moved into the created object. It means that the list
* items must have been malloc'd, so that they can be freed.
*
* If cb is not null, the callback is called when the connection is in OPEN state or
* when an error has occurred. The callback should use the pi_state information to
* determine which one it is. If the first parameter of the called callback is NULL, it
* means that the peer is being destroyed before attempt success / failure.
* cb is called to allow freeing cb_data in * this case.
*
* The orig_dbg string is only useful for easing debug, and can be left to NULL.
*
* RETURN VALUE:
* 0 : The peer is added.
* EINVAL : A parameter is invalid.
* EEXIST : A peer with the same Diameter-Id is already in the list.
* (other standard errors may be returned, too, with their standard meaning. Example:
* ENOMEM : Memory allocation for the new object element failed.)
*/
int fd_peer_add ( struct peer_info * info, const char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data );
/*
* FUNCTION: fd_peer_getbyid
*
* PARAMETERS:
* diamid : an UTF8 string describing the diameter Id of the peer to seek
* diamidlen : length of the diamid
* igncase : perform an almost-case-insensitive search? (slower)
* peer : The peer is stored here if it exists.
*
* DESCRIPTION:
* Search a peer by its Diameter-Id.
*
* RETURN VALUE:
* 0 : *peer has been updated (to NULL if the peer is not found).
* !0 : An error occurred.
*/
int fd_peer_getbyid( DiamId_t diamid, size_t diamidlen, int igncase, struct peer_hdr ** peer );
/*
* FUNCTION: fd_peer_get_state
*
* PARAMETERS:
* peer : The peer which state to read
*
* DESCRIPTION:
* Returns the current state of the peer.
*
* RETURN VALUE:
* -1 : peer is invalid
* >=0 : the state of the peer at the time of reading.
*/
int fd_peer_get_state(struct peer_hdr *peer);
/*
* FUNCTION: fd_peer_cnx_proto_info
*
* PARAMETERS:
* peer : The peer which information to be read
* buf : Where to store the protocol information
* len : available space in bug
*
* DESCRIPTION:
* Creates a string describing the current connection to this peer, e.g.: "TCP,TLS,soc#3".
*
* RETURN VALUE:
* 0 : buffer was written
* >=0 : error code.
*/
int fd_peer_cnx_proto_info(struct peer_hdr *peer, char * buf, size_t len);
/*
* FUNCTION: fd_peer_get_load_pending
*
* PARAMETERS:
* peer : The peer which load to read
* to_receive : (out) number of requests sent to this peer without matching answer yet.
* to_send : (out) number of requests received from this peer and not yet answered.
*
* DESCRIPTION:
* Returns the current number of requests sent to this peer
* that have not been answered yet. This is an empirical indication
* of the workload of this peer.
*
* RETURN VALUE:
* 0 : The load parameter has been updated. (it should have a positive value always)
* !0 : An error occurred
*/
int fd_peer_get_load_pending(struct peer_hdr *peer, long * to_receive, long * to_send);
/*
* FUNCTION: fd_peer_validate_register
*
* PARAMETERS:
* peer_validate : Callback as defined below.
*
* DESCRIPTION:
* Add a callback to authorize / reject incoming peer connections.
* All registered callbacks are called until a callback sets auth = -1 or auth = 1.
* If no callback returns a clear decision, the default behavior is applied (reject unknown connections)
* The callbacks are called in FILO order of their registration.
*
* RETURN VALUE:
* 0 : The callback is added.
* !0 : An error occurred.
*/
int fd_peer_validate_register ( int (*peer_validate)(struct peer_info * /* info */, int * /* auth */, int (**cb2)(struct peer_info *)) );
/*
* CALLBACK: peer_validate
*
* PARAMETERS:
* info : Structure containing information about the peer attempting the connection.
* auth : Store there the result if the peer is accepted (1), rejected (-1), or unknown (0).
* cb2 : If != NULL and in case of PI_SEC_TLS_OLD, another callback to call after handshake (if auth = 1).
*
* DESCRIPTION:
* This callback is called when a new connection is being established from an unknown peer,
* after the CER is received. An extension must register such callback with peer_validate_register.
*
* The callback can learn if the peer has sent Inband-Security-Id AVPs in runtime.pir_isi fields.
* It can also learn if a handshake has already been performed in runtime.pir_cert_list field.
* The callback must set the value of config.pic_flags.sec appropriately to allow a connection without TLS.
*
* If the old TLS mechanism is used,
* the extension may also need to check the credentials provided during the TLS
* exchange (remote certificate). For this purpose, it may set the address of a new callback
* to be called once the handshake is completed. This new callback receives the information
* structure as parameter (with pir_cert_list set) and returns 0 if the credentials are correct,
* or an error code otherwise. If the error code is received, the connection is closed and the
* peer is destroyed.
* Note that freeDiameter already achieves some usual checks. The callback may be used to enforce
* additional restrictions.
*
* RETURN VALUE:
* 0 : The authorization decision has been written in the location pointed by auth.
* !0 : An error occurred.
*/
/*============================================================*/
/* MESSAGES */
/*============================================================*/
/*
* FUNCTION: fd_msg_send, fd_msg_send_timeout
*
* PARAMETERS:
* pmsg : Location of the message to be sent on the network (set to NULL on function return to avoid double deletion).
* anscb : A callback to be called when corresponding answer is received, when sending a request (not used with answers)
* anscb_data : opaque data to be passed back to the anscb (or expirecb) when it is called.
* expirecb : (only for fd_msg_send_timeout) If the request did not get an answer before timeout, this callback is called.
* timeout : (only for fd_msg_send_timeout) sets the absolute time until when to wait for an answer. Past this time,
* the expirecb is called with the request and the answer will be discarded if received later.
*
* DESCRIPTION:
* Sends a message on the network. (actually simply queues it in a global queue, to be picked by a daemon's thread)
* For requests, the end-to-end id must be set (see fd_msg_get_eteid / MSGFL_ALLOC_ETEID).
* For answers, the message must be created with function fd_msg_new_answer_from_req.
*
* The routing module will handle sending to the correct peer, usually based on the Destination-Realm / Destination-Host AVP.
*
* If the msg is a request, there are two ways of receiving the answer:
* - either having registered a callback in the dispatch module (see fd_disp_register)
* - or provide a anscb callback here. If such callback is provided, it is called before the dispatch callbacks.
* The prototype for this anscb callback function is:
* void anscb(void * data, struct msg ** answer)
* where:
* data : opaque data that was registered along with the callback.
* answer : location of the pointer to the answer.
* note1: on function return, if *answer is not NULL, the message is passed to the dispatch module for regular callbacks.
* otherwise, the callback must take care of freeing the message (fd_msg_free).
* note2: the opaque data is not freed by the daemon in any case, extensions should ensure clean handling in fd_ext_fini.
*
* If no callback is registered to handle an answer, the message is discarded and an error is logged.
*
* fd_msg_send_timeout is similar to fd_msg_send, except that it takes two additional arguments "expirecb" and "timeout".
* If the message parameter is an answer, there is no difference with fd_msg_send.
* Otherwise, if the corresponding answer (or error) is received before the timeout date elapses, everything occurs as with fd_msg_send.
* Otherwise, the request is removed from the queue (meaning the matching answer will be discarded upon reception) and passed to the expirecb
* function. Upon return, if the *msg parameter is not NULL, it is freed (not passed to other callbacks).
* expirecb is called in a dedicated thread.
*
* The prototype for the expirecb callback function is:
* void expirecb(void * data, struct peer_hdr * sentto, struct msg ** request)
* where:
* data : opaque data that was registered along with the callback.
* sentto : pointer to the peer to which the message was sent and no answer received within timeout.
* request: location of the pointer to the request that was not answered.
*
* RETURN VALUE:
* 0 : The message has been queued for sending (sending may fail asynchronously).
* EINVAL : A parameter is invalid (ex: anscb provided but message is not a request).
* ...
*/
int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data );
int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout );
/*
* FUNCTION: fd_msg_rescode_set
*
* PARAMETERS:
* msg : A msg object -- it must be an answer.
* rescode : The name of the returned error code (ex: "DIAMETER_INVALID_AVP")
* errormsg : (optional) human-readable error message to put in Error-Message AVP
* optavp : (optional) If provided, the content will be put inside a Failed-AVP
* type_id : 0 => nothing; 1 => adds Origin-Host and Origin-Realm with local info. 2=> adds Error-Reporting-Host.
*
* DESCRIPTION:
* This function adds a Result-Code AVP to a message, and optionally
* - sets the 'E' error flag in the header,
* - adds Error-Message, Error-Reporting-Host and Failed-AVP AVPs.
*
* RETURN VALUE:
* 0 : Operation complete.
* !0 : an error occurred.
*/
int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id );
/* Add Origin-Host, Origin-Realm, (if osi) Origin-State-Id AVPS at the end of the message */
int fd_msg_add_origin ( struct msg * msg, int osi );
/* Generate a new Session-Id and add it at the beginning of the message (opt is added at the end of the sid if provided) */
int fd_msg_new_session( struct msg * msg, os0_t opt, size_t optlen );
/* Parse a message against our dictionary,
return 0 in case of success.
log parsing error & return error code in case of failure in parsing.
In addition, if the error code is EBADMSG (the message does not follow our dictionary)
if *msg was a request, *msg is NULL and *error contains the error message ready to send back on return
if *msg was an answer, *msg is untouched and *error==*msg if *msg was an error message, *error is null otherwise */
int fd_msg_parse_or_error( struct msg ** msg, struct msg **error );
/*============================================================*/
/* DISPATCH */
/*============================================================*/
/*
* FUNCTION: fd_disp_app_support
*
* PARAMETERS:
* app : The dictionary object corresponding to the Application.
* vendor : (Optional) the dictionary object of a Vendor to claim support in Vendor-Specific-Application-Id
* auth : Support auth app part.
* acct : Support acct app part.
*
* DESCRIPTION:
* Registers an application to be advertized in CER/CEA exchanges.
* Messages with an application-id matching a registered value are passed to the dispatch module,
* while other messages are simply relayed or an error is returned (if local node does not relay)
*
* RETURN VALUE:
* 0 : The application support is registered.
* EINVAL : A parameter is invalid.
*/
int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int auth, int acct );
/* Note: if we want to support capabilities updates, we'll have to add possibility to remove an app as well... */
/*============================================================*/
/* ROUTING */
/*============================================================*/
/* This file contains the definitions of types and functions involved in the routing decisions in freeDiameter,
* and that can be called by extensions.
*
* Three different type of messages must be distinguished:
* - Messages received, and the peer is final recipient (IN messages)
* - Messages received, and the peer is not final recipient (FWD messages)
* - Message is locally generated (OUT messages)
*
* There are three global message queues (in queues.c) and also peers-specific queues (in struct fd_peer).
*
* (*) IN messages processing details:
* - the message is received from the remote peer, a FDEVP_CNX_MSG_RECV event is generated for the peer.
* - the PSM thread parses the buffer, does some verifications, handles non routable messages (fd_msg_is_routable)
* - routable messages are queued in the fd_g_incoming global queue.
* - a thread (routing-in) picks the message and takes the decision if it is handled locally or forwarded,
* based on local capabilities (registered by extensions with fd_disp_app_support).
* - If the message is handled locally, it is queued in fd_g_local.
* - Another thread (dispatch.c) will handle this message and pass it to registered callbacks (see fd_disp_register in libfreeDiameter.h).
*
* (*) FWD messages details:
* - The process is the same as for IN messages, until the routing-in threads makes its decision that the message is not handled locally.
* - If the local peer does not relay message, an error DIAMETER_APPLICATION_UNSUPPORTED is returned.
* - All callbacks registered with fd_rt_fwd_register are called for the message (see below).
* - these callbacks will typically do proxying work. Note that adding the route-record is handled by the daemon.
* - Once all callbacks have been called, the message is queued in the global fd_g_outgoing queue.
* - The remaining processing is the same as for OUT messages, as described below.
*
* (*) OUT messages details:
* - The message are picked from fd_g_outgoing (they are queued there as result of forwarding process or call to fd_msg_send.)
* - The (routing-out) thread builds a list of possible destinations for the message, as follow:
* - create a list of all known peers in the "OPEN" state.
* - remove from that list all peers that are in a Route-Record AVP of the message, to avoid routing loops.
* - remove also all peers that have previously replied an error message for this message.
* - If the list is empty, create an error UNABLE_TO_DELIVER (note: should we trig dynamic discovery here???) and reply.
* - Otherwise, call all callbacks registered by function fd_rt_out_register, with the list of peers and the message.
* - Order the resulting list of peers by score (see below), and sent the message to the peer with highest (positive) score.
* - in case the peer is no longer in the "OPEN" state, send the message to the second peer in the list.
* - if no peer is in OPEN state anymore, restart the process of creating the list.
* - Once a peer has been selected, the message is queued into that peer's outgoing queue.
*
* The following functions allow an extension to register or remove a callback as described above.
*/
/********** Forwarding callbacks: for Proxy operations ***********/
/* Handle to registered callback */
struct fd_rt_fwd_hdl;
/* Message direction for the callback */
enum fd_rt_fwd_dir {
RT_FWD_REQ = 1, /* The callback will be called on forwarded requests only */
RT_FWD_ALL = 2, /* The callback will be called on all forwarded messages (requests and answers )*/
RT_FWD_ANS = 3 /* The callback will be called on answers and errors only */
};
/*
* FUNCTION: fd_rt_fwd_register
*
* PARAMETERS:
* rt_fwd_cb : The callback function to register (see prototype below).
* cbdata : Pointer to pass to the callback when it is called. The data is opaque to the daemon.
* dir : One of the RT_FWD_* directions defined above.
* handler : On success, a handler to the registered callback is stored here.
* This handler will be used to unregister the cb.
*
* DESCRIPTION:
* Register a new callback for forwarded messages. See explanations above.
* Note that there is no guaranteed order for the callbacks calls.
*
* RETURN VALUE:
* 0 : The callback is registered.
* EINVAL : A parameter is invalid.
* ENOMEM : Not enough memory to complete the operation
*/
int fd_rt_fwd_register ( int (*rt_fwd_cb)(void * cbdata, struct msg ** msg), void * cbdata, enum fd_rt_fwd_dir dir, struct fd_rt_fwd_hdl ** handler );
/*
* CALLBACK: rt_fwd_cb
*
* PARAMETERS:
* data : pointer to some data that was passed when the callback was registered (optional).
* msg : The message that is being forwarded.
*
* DESCRIPTION:
* This callback is called when a message is forwarded to another peer. It may for example add a Proxy-Info AVP.
* The callback may also choose to handle the message in a more complex form. In that case, it must set *msg = NULL
* and handle it differently. In such case, the forwarding thread will stop processing this message.
*
* RETURN VALUE:
* 0 : Operation complete.
* !0 : An error occurred -- will result in daemon's termination.
*/
/*
* FUNCTION: fd_rt_fwd_unregister
*
* PARAMETERS:
* handler : The handler of the callback that must be unregistered.
* cbdata : Will receive the data registered with the callback, that can be freed if needed.
*
* DESCRIPTION:
* Removes a callback from the list of registered callbacks.
*
* RETURN VALUE:
* 0 : The callback is unregistered.
* EINVAL : A parameter is invalid.
*/
int fd_rt_fwd_unregister ( struct fd_rt_fwd_hdl * handler, void ** cbdata );
/********** Out callbacks: for next hop routing decision operations ***********/
/* Handle to registered callback */
struct fd_rt_out_hdl;
enum fd_rt_out_score {
FD_SCORE_NO_DELIVERY = -70, /* We should not send this message to this candidate */
FD_SCORE_SENT_REDIRECT = -60, /* If this peer previously sent a Redirect indication that applies to this message */
FD_SCORE_INI = -2, /* All candidates are initialized with this value */
FD_SCORE_LOAD_BALANCE = 1, /* Use this to differentiate between several peers with the same score */
FD_SCORE_DEFAULT = 5, /* The peer is a default route for all messages */
FD_SCORE_DEFAULT_REALM = 10, /* The peer is a default route for this realm */
FD_SCORE_REALM = 15, /* The peer belongs to Destination-Realm of the message */
FD_SCORE_REDIR_HOST = 25, /* If there is a redirect rule with ALL_HOST for these message and peer */
FD_SCORE_REDIR_APP = 30, /* If there is a redirect rule with ALL_APPLICATION for these message and peer */
FD_SCORE_REDIR_REALM = 35, /* If there is a redirect rule with ALL_REALM for these message and peer */
FD_SCORE_REDIR_REALM_APP = 40, /* If there is a redirect rule with REALM_AND_APPLICATION for these message and peer */
FD_SCORE_REDIR_USER = 45, /* If there is a redirect rule with ALL_USER for these message and peer */
FD_SCORE_REDIR_SESSION = 50, /* If there is a redirect rule with ALL_SESSION for these message and peer */
FD_SCORE_REDIR_ONCE = 55, /* If there is a redirect rule with DONT_CACHE for these message and peer */
FD_SCORE_FINALDEST = 100 /* If the peer is the final recipient of the message (i.e. matching Destination-Host), it receives a big score. */
};
/*
* FUNCTION: fd_rt_out_register
*
* PARAMETERS:
* rt_out_cb : The callback function to register (see prototype below).
* cbdata : Pointer to pass to the callback when it is called. The data is opaque to the daemon.
* priority : Order for calling this callback. The callbacks are called in reverse priority order (higher priority = called sooner).
* handler : On success, a handler to the registered callback is stored here.
* This handler will be used to unregister the cb.
*
* DESCRIPTION:
* Register a new callback to handle OUT routing decisions. See explanations above.
*
* RETURN VALUE:
* 0 : The callback is registered.
* EINVAL : A parameter is invalid.
* ENOMEM : Not enough memory to complete the operation
*/
int fd_rt_out_register ( int (*rt_out_cb)(void * cbdata, struct msg ** pmsg, struct fd_list * candidates), void * cbdata, int priority, struct fd_rt_out_hdl ** handler );
/*
* CALLBACK: rt_out_cb
*
* PARAMETERS:
* cbdata : pointer to some data that was registered with the callback.
* pmsg : pointer to the message that must be sent. upon return if *msg is NULL, the processing stops and the message is not sent.
* list : The list of peers to which the message may be sent to, as returned by fd_rtd_candidate_extract
*
* DESCRIPTION:
* This callback must attribute a score (preferably from FD_SCORE_*) to each candidate peer in the list.
* Once all registered callbacks have been called, the message is sent to the candidate with the highest score.
* Note that each callback must *add* its locally-attributed score to the candidate current "score" parameter, not replace it!
* Note also that this callback must be re-entrant since it may be called by several threads at the same time
* (for different messages)
*
* RETURN VALUE:
* 0 : Operation complete.
* !0 : An error occurred.
*/
/*
* FUNCTION: fd_rt_out_unregister
*
* PARAMETERS:
* handler : The handler of the callback that must be unregistered.
* cbdata : Will receive the data registered with the callback, that can be freed if needed.
*
* DESCRIPTION:
* Removes a callback from the list of registered callbacks.
*
* RETURN VALUE:
* 0 : The callback is unregistered.
* EINVAL : A parameter is invalid.
*/
int fd_rt_out_unregister ( struct fd_rt_out_hdl * handler, void ** cbdata );
/*============================================================*/
/* EVENTS */
/*============================================================*/
struct fd_event {
int code; /* codespace depends on the queue */
size_t size;
void *data;
};
/* Daemon's codespace: 1000->1999 (1500->1999 defined in fdcore-internal.h) */
enum {
FDEV_TERMINATE_INT= 1000 /* request to terminate. DO NOT USE. Use fd_core_shutdown() instead. */
,FDEV_TRIGGER /* Trigger available for extensions. size is sizeof(int), data is int * */
};
int fd_event_send(struct fifo *queue, int code, size_t datasz, void * data);
int fd_event_get(struct fifo *queue, int * code, size_t * datasz, void ** data);
int fd_event_timedget(struct fifo *queue, struct timespec * timeout, int timeoutcode, int * code, size_t * datasz, void ** data);
void fd_event_destroy(struct fifo **queue, void (*free_cb)(void * data));
const char * fd_ev_str(int event);
/* for extensions */
int fd_event_trig_regcb(int trigger_val, const char * module, void (*cb)(void));
#ifndef SWIG
DECLARE_FD_DUMP_PROTOTYPE(fd_event_trig_dump);
/* The "old" FD_EV_DUMP_* events are replaced with direct calls to the following dump functions */
DECLARE_FD_DUMP_PROTOTYPE(fd_conf_dump);
DECLARE_FD_DUMP_PROTOTYPE(fd_ext_dump);
#else /* SWIG */
DECLARE_FD_DUMP_PROTOTYPE_simple(fd_event_trig_dump);
DECLARE_FD_DUMP_PROTOTYPE_simple(fd_conf_dump);
DECLARE_FD_DUMP_PROTOTYPE_simple(fd_ext_dump);
#endif /* SWIG */
DECLARE_FD_DUMP_PROTOTYPE(fd_servers_dump, int details);
DECLARE_FD_DUMP_PROTOTYPE(fd_peer_dump_list, int details);
DECLARE_FD_DUMP_PROTOTYPE(fd_peer_dump, struct peer_hdr * p, int details);
/*============================================================*/
/* ENDPOINTS */
/*============================================================*/
struct fd_endpoint {
struct fd_list chain; /* link in cnf_endpoints list */
union {
sSS ss; /* the socket information. List is always ordered by ss value (memcmp) -- see fd_ep_add_merge */
sSA4 sin;
sSA6 sin6;
sSA sa;
}
#ifdef SWIG /* nested anonymous unions are not supported yet */
s
#endif /* SWIG */
;
#define EP_FL_CONF (1 << 0) /* This endpoint is statically configured in a configuration file */
#define EP_FL_DISC (1 << 1) /* This endpoint was resolved from the Diameter Identity or other DNS query */
#define EP_FL_ADV (1 << 2) /* This endpoint was advertized in Diameter CER/CEA exchange */
#define EP_FL_LL (1 << 3) /* Lower layer mechanism provided this endpoint */
#define EP_FL_PRIMARY (1 << 4) /* This endpoint is primary in a multihomed SCTP association */
#define EP_ACCEPTALL (1 << 15) /* This flag allows bypassing the address filter in fd_ep_add_merge. */
uint32_t flags; /* Additional information about the endpoint */
/* To add: a validity timestamp for DNS records ? How do we retrieve this lifetime from DNS ? */
};
int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, uint32_t flags );
int fd_ep_filter( struct fd_list * list, uint32_t flags );
int fd_ep_filter_family( struct fd_list * list, int af );
int fd_ep_clearflags( struct fd_list * list, uint32_t flags );
DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump_one, int preamble, struct fd_endpoint * ep );
DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump, int preamble, int indent, struct fd_list * eps );
/*============================================================*/
/* APPLICATIONS IDs */
/*============================================================*/
struct fd_app {
struct fd_list chain; /* link in cnf_apps list. List ordered by appid. */
struct {
unsigned auth : 1;
unsigned acct : 1;
} flags;
vendor_id_t vndid; /* if not 0, Vendor-Specific-App-Id AVP will be used */
application_id_t appid; /* The identifier of the application */
};
int fd_app_merge(struct fd_list * list, application_id_t aid, vendor_id_t vid, int auth, int acct);
int fd_app_check(struct fd_list * list, application_id_t aid, struct fd_app **detail);
int fd_app_check_common(struct fd_list * list1, struct fd_list * list2, int * common_found);
int fd_app_empty(struct fd_list * list);
/*============================================================*/
/* MONITORING */
/*============================================================*/
/* These functions allow an extension to collect state information about the
* framework, as well as being hooked at some key checkpoints in the processing
* for logging or statistics purpose.
*/
/* CALLBACK: fd_hook_cb
*
* PARAMETERS:
* type : The type of hook that triggered this call, in case same cb is registered for several hooks.
* msg : If relevant, the pointer to the message triggering the call. NULL otherwise.
* peer : If relevant, the pointer to the peer associated with the call. NULL otherwise.
* other : For some callbacks, the remaining information is passed in this parameter. See each hook detail.
* permsgdata : Structure associated with a given message, across several hooks.
* A different structure is associated with requests and corresponding answers.
* See fd_hook_data_hdl below for details.
* If no fd_hook_data_hdl is registered with this callback, this parameter is always NULL
* regdata : Data pointer stored at registration, opaque for the framework.
*
* DESCRIPTION:
* When such callback is registered with fd_hook_register function, it will be called on matching events with
* the parameters as described in the list of fd_hook_type below. One can use this mechanism for e.g.:
* - log completely the messages for safety / backup
* - create statistics information on the throughput
* - ...
*
* IMPORTANT: the callback MUST NOT change the memory pointed by the different parameters (peer, message, ...)
*
* RETURN VALUE:
* none.
*/
/* The available hooks in the framework */
enum fd_hook_type {
HOOK_DATA_RECEIVED = 0,
/* Hook called as soon as a message has been received from the network, after TLS & boundary processing.
- {msg} is NULL.
- {peer} is NULL.
- {other} is a pointer to a struct fd_cnx_rcvdata containing the received buffer.
- {permsgdata} points to either a new empty structure allocated for this message (cf. fd_hook_data_hdl), or NULL if no hdl is registered.
*/
HOOK_MESSAGE_RECEIVED,
/* Hook called when a message has been received and the structure has been parsed successfully (list of AVPs).
- {msg} points to the parsed message. At this time, the objects have not been dictionary resolved. If you
try to call fd_msg_parse_dict, it will slow down the operation of a relay agent.
- {peer} is set if the message is received from a peer's connection, and NULL if the message is from a new client
connected and not yet identified
- {other} is NULL, or a char * identifying the connection when {peer} is null.
- {permsgdata} points to either a new empty structure allocated for this message or the one passed to HOOK_DATA_RECEIVED if used.
*/
HOOK_MESSAGE_LOCAL,
/* Hook called when a request message has been created locally by an extension and is being sent.
- {msg} points to the message.
- {peer} is NULL
- {other} is NULL
- {permsgdata} points to a new empty structure allocated for this request (cf. fd_hook_data_hdl)
*/
HOOK_MESSAGE_SENDING,
/* Hook called when a message is about to be sent to a peer. The message pointer cannot be modified here, but the content of the message
could still be changed (for example add or remove some AVP. This is the last chance.
- {msg} points to the message. Some objects may not have been dictionary resolved. If you
try to call fd_msg_parse_dict, it will slow down the operation of the instance.
- {peer} is the one the message is being sent to.
- {other} is NULL.
- {permsgdata} points to existing structure if any, or a new structure otherwise.
*/
HOOK_MESSAGE_SENT,
/* Hook called when a message has been sent to a peer. The message might be freed as soon as the hook function returns,
so it is not safe to store the pointer for asynchronous processing.
- {msg} points to the sent message. Again, the objects may not have been dictionary resolved. If you
try to call fd_msg_parse_dict, it will slow down the operation of a relay agent.
- {peer} is set if the message is sent to a peer's connection, and NULL if the message is sent to a new client
connected and not yet identified, or being rejected
- {other} is NULL.
- {permsgdata} points to existing structure if any, or a new structure otherwise.
*/
HOOK_MESSAGE_FAILOVER,
/* Hook called when a message that was sent to a peer is being requeued, because e.g. the connection was torn down.
In that case the message will go again through the routing process.
- {msg} points to the corresponding request message (the answer is discarded). Again, the objects may not have been dictionary resolved. If you
try to call fd_msg_parse_dict, it might slow down the operation of a relay agent, although this hook is not on the normal execution path.
- {peer} is the peer this message was previously sent to.
- {other} is NULL.
- {permsgdata} points to existing structure if any, or a new structure otherwise.
*/
HOOK_MESSAGE_PARSING_ERROR,
/* Hook called when a message being processed cannot be parsed successfully.
- {msg} points to the message if buffer was parsed successfully, or NULL otherwise. You should not call fd_msg_parse_dict on this in any case.
- {peer} is NULL or the peer that received the message. If NULL and the message is not NULL, you can still retrieve the source from the message itself.
- {other} is a char * pointer to the error message (human-readable) if {msg} is not NULL, a pointer to struct fd_cnx_rcvdata containing the received buffer otherwise.
- {permsgdata} points to existing structure associated with this message (or new structure if no previous hook was registered).
*/
HOOK_MESSAGE_ROUTING_ERROR,
/* Hook called when a message being processed by the routing thread meets an error such as no remaining available peer for sending, based on routing callbacks decisions (maybe after retries).
- {msg} points to the message. Again, the objects may not have been dictionary resolved. If you
try to call fd_msg_parse_dict, it might slow down the operation of a relay agent, although this hook is not on the normal execution path.
- {peer} is NULL.
- {other} is a char * pointer to the error message (human-readable).
- {permsgdata} points to existing structure associated with this message (or new structure if no previous hook was registered).
*/
HOOK_MESSAGE_ROUTING_FORWARD,
/* Hook called when a received message is deemed to be not handled locally by the routing_dispatch process.
The decision of knowing which peer it will be sent to is not made yet (or if an error will be returned).
The hook is trigged before the callbacks registered with fd_rt_fwd_register are called.
- {msg} points to the message. Again, the objects may not have been dictionary resolved.
If you try to call fd_msg_parse_dict, it will slow down the operation of a relay agent.
- {peer} is NULL.
- {other} is NULL.
- {permsgdata} points to existing structure associated with this message (or new structure if no previous hook was registered).
*/
HOOK_MESSAGE_ROUTING_LOCAL,
/* Hook called when a received message is handled locally by the routing_dispatch process (i.e., not forwarded).
The hook is trigged before the callbacks registered with fd_disp_register are called.
- {msg} points to the message. Here, the message has been already parsed completely & successfully.
- {peer} is NULL.
- {other} is NULL.
- {permsgdata} points to existing structure associated with this message (or new structure if no previous hook was registered).
*/
HOOK_MESSAGE_DROPPED,
/* Hook called when a message is being discarded by the framework because of some error condition (normal or abnormal).
It is probably a good idea to log this for analysis / backup.
- {msg} points to the message, which will be freed as soon as the hook returns.
- {peer} may be NULL or a peer related to the event.
- {other} is a char * pointer to the error message (human-readable).
- {permsgdata} points to existing structure associated with this message (or new structure if no previous hook was registered).
*/
HOOK_PEER_CONNECT_FAILED,
/* Hook called when a connection attempt to/from a remote peer has failed. This hook is also called when the peer was in OPEN state and the connection is broken.
- {msg} may be NULL (lower layer error, e.g. connection timeout) or points to a message showing the error (either invalid incoming message, or the CEA message sent or received with an error code).
- {peer} may be NULL for incoming requests from unknown peers being rejected, otherwise it points to the peer structure associated with the attempt.
- {other} is a char * pointer to the error message (human-readable).
- {permsgdata} is always NULL for this hook.
*/
HOOK_PEER_CONNECT_SUCCESS,
/* Hook called when a connection attempt to/from a remote peer has succeeded (the peer moves to OPEN_HANDSHAKE or OPEN state).
In case of deprecated TLS handshake after the CER/CEA exchange, this hook can still be followed by HOOK_PEER_CONNECT_FAILED if TLS handshake fails.
- {msg} points to the CEA message sent or received (with a success code) -- in case it is sent, you can always get access to the matching CER.
- {peer} points to the peer structure.
- {other} is NULL.
- {permsgdata} is always NULL for this hook.
*/
HOOK_MESSAGE_PARSING_ERROR2,
/* Hook called after an error message has been generated due to a dictionary parsing error.
- {msg} points to the error message that has been generated.
- {peer} is NULL. You can still retrieve the source from the message itself.
- {other} is NULL
- {permsgdata} points to existing structure associated with this message (or new structure if no previous hook was registered).
Use this hook if you need to populate more data in the error being returned, from the error message.
(e.g. some AVP need to be added to replies even if error case.
*/
#define HOOK_LAST HOOK_MESSAGE_PARSING_ERROR2
};
/* Type of the {permsgdata} pointer. It is up to each extension to define its own structure. This is opaque for the framework. */
struct fd_hook_permsgdata;
/* A handle that will be associated with the extension, and with the permsgdata structures. */
struct fd_hook_data_hdl;
/* The following structure is what is passed to the HOOK_DATA_RECEIVED hook */
struct fd_cnx_rcvdata {
size_t length;
uint8_t * buffer; /* internal note: the buffer is padded with a struct fd_msg_pmdl, not accounted for in length */
};
/* Function to register a new fd_hook_data_hdl. Should be called by your extension init function.
* The arguments are the functions called to initialize a new fd_hook_permsgdata and to free this structure when the corresponding message is being freed.
*/
/*
* FUNCTION: fd_hook_data_register
*
* PARAMETERS:
* permsgdata_size : the size of the fd_hook_permsgdata structure.
* permsgdata_init_cb : function called to initialize a new fd_hook_permsgdata structure, when a hook will be called for a message that does not have such structure yet.
* The memory is already allocated and blanked, so you can pass NULL if no further handling is required.
* permsgdata_fini_cb : function called when a message is being disposed. It should free the resources associated with the fd_hook_permsgdata.
* You can pass NULL if no special handling is required. The memory of the permsgdata structure itself will be freed by the framework.
* new_handle : On success, a handler to the registered callback is stored here.
* This handler will be used to unregister the cb.
*
* DESCRIPTION:
* Register a new fd_hook_data_hdl. This handle is used during hooks registration (see below) in order to associate data with the messages, to allow keeping tracking of the message easily.
* Note that these handlers are statically allocated and cannot be unregistered. FD_HOOK_HANDLE_LIMIT handlers can be registered at maximum (recompile libfdproto if you change this value)
*
* RETURN VALUE:
* 0 : The callback is registered.
* EINVAL : A parameter is invalid.
* ENOSPC : Too many handles already registered. You may need to increase the limit in the code.
*/
int fd_hook_data_register(
size_t permsgdata_size,
void (*permsgdata_init_cb) (struct fd_hook_permsgdata *),
void (*permsgdata_fini_cb) (struct fd_hook_permsgdata *),
struct fd_hook_data_hdl **new_handle
);
/* A handler associated with a registered hook callback (for cleanup) */
struct fd_hook_hdl;
/* Helper for building a mask of hooks for registration */
#define HOOK_MASK(hooklist...) fd_hook_mask_helper(0, ## hooklist, -1)
/*
* FUNCTION: fd_hook_register
*
* PARAMETERS:
* type_mask : A bitmask of fd_hook_type bits for which this cb is registered, e.g. HOOK_MASK( HOOK_MESSAGE_RECEIVED, HOOK_MESSAGE_SENT )
* fd_hook_cb : The callback function to register (see prototype above).
* regdata : Pointer to pass to the callback when it is called. The data is opaque to the daemon.
* data_hdl : If permsgdata is requested for the hooks, a handler registered with fd_hook_data_register. NULL otherwise.
* handler : On success, a handler to the registered callback is stored here.
* This handler can be used to unregister the cb.
*
* DESCRIPTION:
* Register a new hook in the framework. See explanations above.
*
* RETURN VALUE:
* 0 : The callback is registered.
* EEXIST : Another callback is already registered for this type of hook (HOOK_DATA_RECEIVED).
* EINVAL : A parameter is invalid.
* ENOMEM : Not enough memory to complete the operation
*/
int fd_hook_register ( uint32_t type_mask,
void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata),
void *regdata,
struct fd_hook_data_hdl *data_hdl,
struct fd_hook_hdl ** handler );
/* Remove a hook registration */
int fd_hook_unregister( struct fd_hook_hdl * handler );
/* Use the following function to retrieve any pmd structure associated with a request matching the current answer. Returns NULL in case of error / no such structure */
struct fd_hook_permsgdata * fd_hook_get_request_pmd(struct fd_hook_data_hdl *data_hdl, struct msg * answer);
/* The following is used by HOOK_MASK macro */
uint32_t fd_hook_mask_helper(int dummy, ...);
/*============================================================*/
/*
* The following allows an extension to retrieve stat information on the different fifo queues involved in the freeDiameter framework.
* There are three global queues, plus per-peer queues.
* This information can be used to build SNMP-like data for example, or quickly get a status of the framework to find the loaded path of execution / bottlenecks.
*/
enum fd_stat_type {
/* For the following, no peer is associated with the stat */
STAT_G_LOCAL= 1, /* Get statistics for the global queue of messages processed by local extensions */
STAT_G_INCOMING, /* Get statistics for the global queue of received messages to be processed by routing_in thread */
STAT_G_OUTGOING, /* Get statistics for the global queue of messages to be processed by routing_out thread */
/* For the following, the peer must be provided */
STAT_P_PSM, /* Peer state machine queue (events to be processed for this peer, including received messages) */
STAT_P_TOSEND, /* Queue of messages for sending to this peer */
};
/*
* FUNCTION: fd_stat_getstats
*
* PARAMETERS:
* stat : Which queue is being queried
* peer : (depending on the stat parameter) which peer is being queried
* current_count : (out) The number of items in the queue currently
* limit_count : (out) The max number of items the queue accepts before becoming blocking -- 0 means no max.
* highest_count : (out) The highest count the queue has reached since startup
* total_count : (out) Total number of items that this queue has processed (always growing, use deltas for monitoring)
* total : (out) Cumulated time all items spent in this queue, including blocking time (always growing, use deltas for monitoring)
* blocking : (out) Cumulated time threads trying to post new items were blocked (queue full).
* last : (out) For the last element retrieved from the queue, how long it took between posting (including blocking) and poping
*
* DESCRIPTION:
* Get statistics information about a given queue.
* Any of the (out) parameters can be NULL if not requested.
*
* RETURN VALUE:
* 0 : The callback is registered.
* EINVAL : A parameter is invalid.
*/
int fd_stat_getstats(enum fd_stat_type stat, struct peer_hdr * peer,
int * current_count, int * limit_count, int * highest_count, long long * total_count,
struct timespec * total, struct timespec * blocking, struct timespec * last);
/*============================================================*/
/* EOF */
/*============================================================*/
#ifdef __cplusplus
}
#endif
#endif /* _LIBFDCORE_H */
freeDiameter/include/freeDiameter/version.h.in 0000644 0001750 0001750 00000000271 13324704440 020745 0 ustar ruben ruben /* Following variable is expended at build time based on the result of "hg id" command */
#cmakedefine FD_PROJECT_VERSION_HG
#define FD_PROJECT_VERSION_HG_VAL "@FD_PROJECT_VERSION_HG@"
freeDiameter/include/freeDiameter/extension.h 0000644 0001750 0001750 00000006547 13324704440 020703 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
#ifndef _EXTENSION_H
#define _EXTENSION_H
#ifdef __cplusplus
extern "C" {
#endif
/* Include definition of freeDiameter API */
#include
#include
/* Macro that define the entry point of the extension */
#define EXTENSION_ENTRY(_name, _function, _depends...) \
const char *fd_ext_depends[] = { _name , ## _depends , NULL }; \
static int extension_loaded = 0; \
int fd_ext_init(int major, int minor, char * conffile) { \
if ((major != FD_PROJECT_VERSION_MAJOR) \
|| (minor != FD_PROJECT_VERSION_MINOR)) { \
TRACE_ERROR("This extension (" _name ") was compiled for a different version of freeDiameter."); \
TRACE_DEBUG(INFO, "daemon %d.%d != ext %d.%d", \
major, minor, \
FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR); \
return EINVAL; \
} \
if (extension_loaded) { \
TRACE_ERROR("Extension (" _name ") cannot be loaded twice!"); \
return ENOTSUP; \
} \
extension_loaded++; \
return (_function)(conffile); \
}
#ifdef __cplusplus
}
#endif
#endif /* _EXTENSION_H */
freeDiameter/include/freeDiameter/libfdproto.h 0000644 0001750 0001750 00000372234 13324704440 021032 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2015, 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 S_OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
*********************************************************************************************************/
/* This file contains the definitions of functions and types used by the libfreeDiameter library.
*
* This library is meant to be used by both the freeDiameter daemon and its extensions.
* It provides the tools to manipulate Diameter messages and related data.
* This file should always be included as #include
*
* If any change is made to this file, you must increment the FD_PROJECT_VERSION_API version.
*
* The file contains the following parts:
* DEBUG
* MACROS
* OCTET STRINGS
* THREADS
* LISTS
* DICTIONARY
* SESSIONS
* MESSAGES
* DISPATCH
* QUEUES
*/
#ifndef _LIBFDPROTO_H
#define _LIBFDPROTO_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef FD_IS_CONFIG
#error "You must include 'freeDiameter-host.h' before this file."
#endif /* FD_IS_CONFIG */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /* for basename */
#ifdef SWIG
#define _ATTRIBUTE_PRINTFLIKE_(_f,_v)
#else
#define _ATTRIBUTE_PRINTFLIKE_(_f,_v) __attribute__ ((format (printf, _f, _v)))
#endif /* SWIG */
/* Remove some deprecated warnings from some gnutls versions, when possible */
#if defined(__GNUC__)
# define GCC_DIAG_DO_PRAGMA(x) _Pragma (#x)
# define GCC_DIAG_PRAGMA(x) GCC_DIAG_DO_PRAGMA(GCC diagnostic x)
# if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406 /* 4.6.x */
# define GCC_DIAG_OFF(x) GCC_DIAG_PRAGMA(push) \
GCC_DIAG_PRAGMA(ignored x)
# define GCC_DIAG_ON(x) GCC_DIAG_PRAGMA(pop)
# else /* older */
# define GCC_DIAG_OFF(x) GCC_DIAG_PRAGMA(ignored x)
# define GCC_DIAG_ON(x) GCC_DIAG_PRAGMA(warning x)
# endif
#else
# define GCC_DIAG_OFF(x)
# define GCC_DIAG_ON(x)
#endif
/*============================================================*/
/* CONSTANTS */
/*============================================================*/
#define DIAMETER_PORT 3868
#define DIAMETER_SECURE_PORT 5868
/*============================================================*/
/* INIT */
/*============================================================*/
/* This function must be called first, before any call to another library function */
int fd_libproto_init(void); /* note if you are using libfdcore, it handles this already */
/* Call this one when the application terminates, to destroy internal threads */
void fd_libproto_fini(void);
/* Retrieve the version of the binary */
extern const char fd_libproto_version[];
/*============================================================*/
/* DEBUG */
/*============================================================*/
/*
* FUNCTION: fd_log
*
* PARAMETERS:
* loglevel : Integer, how important the message is. Valid values are macros FD_LOG_*
* format : Same format string as in the printf function
* ... : Same list as printf
*
* DESCRIPTION:
* Write information to log.
* The format and arguments may contain UTF-8 encoded data. The
* output medium is expected to support this encoding.
*
* RETURN VALUE:
* None.
*/
void fd_log ( int, const char *, ... ) _ATTRIBUTE_PRINTFLIKE_(2,3);
#ifndef SWIG
void fd_log_va( int, const char *, va_list);
#endif /* SWIG */
/* these are internal objects of the debug facility,
might be useful to control the behavior from outside */
extern pthread_mutex_t fd_log_lock;
extern char * fd_debug_one_function;
extern char * fd_debug_one_file;
/*
* FUNCTION: fd_log_threadname
*
* PARAMETERS:
* name : \0-terminated string containing a name to identify the current thread.
*
* DESCRIPTION:
* Name the current thread, useful for debugging multi-threaded problems.
*
* This function assumes that a global thread-specific key called "fd_log_thname" exists
* in the address space of the current process.
*
* RETURN VALUE:
* None.
*/
void fd_log_threadname ( const char * name );
extern pthread_key_t fd_log_thname;
/*
* FUNCTION: fd_log_time
*
* PARAMETERS:
* ts : The timestamp to log, or NULL for "now"
* buf : An array where the time must be stored
* len : size of the buffer
* incl_date : The day of year is included in the output
* incl_ms : millisecond value is included in the output
*
* DESCRIPTION:
* Writes the timestamp (in human readable format) in a buffer.
*
* RETURN VALUE:
* pointer to buf.
*/
char * fd_log_time ( struct timespec * ts, char * buf, size_t len, int incl_date, int incl_ms );
/*
* FUNCTION: fd_log_handler_register
* MACRO:
*
* PARAMETERS:
* loglevel : priority of the message
* format : Same format string as in the printf function
* va_list : Argument list
*
* DESCRIPTION:
* Register an external method for logging purposes.
*
* RETURN VALUE:
* int : Success or failure
*/
int fd_log_handler_register ( void (*logger)(int loglevel, const char * format, va_list args) );
/*
* FUNCTION: fd_log_handler_unregister
* MACRO:
*
* PARAMETERS:
*
* DESCRIPTION:
* Unregister the external logging function.
*
* RETURN VALUE:
* int : Success or failure
*/
int fd_log_handler_unregister ( void );
/* All dump functions follow this same prototype:
* PARAMETERS:
* buf : *buf can be NULL on entry, it will be malloc'd. Otherwise it is realloc'd if needed.
* len : the current size of the buffer (in/out)
* offset: (optional) if provided, starts writing dump at offset in the buffer, and updated upon exit. if NULL, starts at offset O.
*
* RETURN VALUE:
* *buf upon success, NULL upon failure.
*
* REMARKS:
* - After the buffer has been used, it should be freed.
* - Depending on the function, the created string may be multi-line. However, it should never be terminated with a '\n'.
*/
#define DECLARE_FD_DUMP_PROTOTYPE( function_name, args... ) \
char * function_name(char ** buf, size_t *len, size_t *offset, ##args)
#ifdef SWIG
#define DECLARE_FD_DUMP_PROTOTYPE_simple( function_name ) \
char * function_name(char ** buf, size_t *len, size_t *offset)
#endif /* SWIG */
/* Helper functions for the *dump functions that add into a buffer */
DECLARE_FD_DUMP_PROTOTYPE( fd_dump_extend, const char * format, ... ) _ATTRIBUTE_PRINTFLIKE_(4,5);
DECLARE_FD_DUMP_PROTOTYPE( fd_dump_extend_hexdump, uint8_t *data, size_t datalen, size_t trunc, size_t wrap );
/* Some helpers macro for writing such *_dump routine */
#define FD_DUMP_STD_PARAMS buf, len, offset
#define FD_DUMP_HANDLE_OFFSET() size_t o = 0; if (!offset) offset = &o; if (buf && (*buf) && !(*offset)) **buf='\0'
#define FD_DUMP_HANDLE_TRAIL() while ((*buf) && (*offset > 0) && ((*buf)[*offset - 1] == '\n')) { *offset -= 1; (*buf)[*offset] = '\0'; }
/*============================================================*/
/* DEBUG MACROS */
/*============================================================*/
#ifndef ASSERT
#define ASSERT(x) assert(x)
#endif /* ASSERT */
/* log levels definitions, that are passed to the logger */
#define FD_LOG_ANNOYING 0 /* very verbose loops and such "overkill" traces. Only active when the framework is compiled in DEBUG mode. */
#define FD_LOG_DEBUG 1 /* Get a detailed sense of what is going on in the framework. Use this level for normal debug */
#define FD_LOG_NOTICE 3 /* Normal execution states worth noting */
#define FD_LOG_ERROR 5 /* Recoverable or expected error conditions */
#define FD_LOG_FATAL 6 /* Unrecoverable error, e.g. malloc fail, etc. that requires the framework to shutdown */
/* The level used by the default logger, can be changed by command-line arguments. Ignored for other loggers. */
extern int fd_g_debug_lvl;
/* Some portability code to get nice function name in __PRETTY_FUNCTION__ */
#if (!defined( __func__)) && (__STDC_VERSION__ < 199901L)
# if __GNUC__ >= 2
# define __func__ __FUNCTION__
# else /* __GNUC__ >= 2 */
# define __func__ ""
# endif /* __GNUC__ >= 2 */
#endif /*(!defined( __func__)) && (__STDC_VERSION__ < 199901L) */
#ifndef __PRETTY_FUNCTION__
#define __PRETTY_FUNCTION__ __func__
#endif /* __PRETTY_FUNCTION__ */
/* A version of __FILE__ without the full path. This is specific to each C file being compiled */
static char * file_bname = NULL;
static char * file_bname_init(char * full) { file_bname = basename(full); return file_bname; }
#define __STRIPPED_FILE__ (file_bname ?: file_bname_init((char *)__FILE__))
/* In DEBUG mode, we add meta-information along each trace. This makes multi-threading problems easier to debug. */
#if (defined(DEBUG) && defined(DEBUG_WITH_META))
# define STD_TRACE_FMT_STRING "pid:%s in %s@%s:%d: "
# define STD_TRACE_FMT_ARGS , ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed"), __PRETTY_FUNCTION__, __STRIPPED_FILE__, __LINE__
#else /* DEBUG && DEBUG_WITH_META */
# define STD_TRACE_FMT_STRING ""
# define STD_TRACE_FMT_ARGS
#endif /* DEBUG && DEBUG_WITH_META */
/*************************
The general debug macro
*************************/
#define LOG(printlevel,format,args... ) \
fd_log((printlevel), STD_TRACE_FMT_STRING format STD_TRACE_FMT_ARGS, ## args)
/*
* Use the following macros in the code to get traces with location & pid in debug mode:
*/
#ifdef DEBUG
# define LOG_A(format,args... ) \
do { if ((fd_debug_one_function && !strcmp(fd_debug_one_function, __PRETTY_FUNCTION__)) \
|| (fd_debug_one_file && !strcmp(fd_debug_one_file, __STRIPPED_FILE__) ) ) { \
LOG(FD_LOG_DEBUG,"[DBG_MATCH] " format,##args); \
} else { \
LOG(FD_LOG_ANNOYING,format,##args); \
} } while (0)
#else /* DEBUG */
# define LOG_A(format,args... ) /* not defined in release */
#endif /* DEBUG */
/* Debug information useful to follow in detail what is going on */
#define LOG_D(format,args... ) \
LOG(FD_LOG_DEBUG, format, ##args)
/* Report a normal message that is useful for normal admin monitoring */
#define LOG_N(format,args... ) \
LOG(FD_LOG_NOTICE, format,##args)
/* Report an error */
#define LOG_E(format,args... ) \
LOG(FD_LOG_ERROR, format, ##args)
/* Report a fatal error */
#define LOG_F(format,args... ) \
LOG(FD_LOG_FATAL, format, ##args)
/*************
Derivatives
************/
/* Trace a binary buffer content */
#define LOG_BUFFER(printlevel, prefix, buf, bufsz, suffix ) { \
int __i; \
size_t __sz = (size_t)(bufsz); \
uint8_t * __buf = (uint8_t *)(buf); \
char __strbuf[1024+1]; \
for (__i = 0; (__i < __sz) && (__i<(sizeof(__strbuf)/2)); __i++) { \
sprintf(__strbuf + (2 * __i), "%02hhx", __buf[__i]); \
} \
fd_log(printlevel, STD_TRACE_FMT_STRING "%s%s%s" STD_TRACE_FMT_ARGS, \
(prefix), __strbuf, (suffix)); \
}
/* Split a multi-line buffer into separate calls to the LOG function. */
#define LOG_SPLIT(printlevel, per_line_prefix, mlbuf, per_line_suffix ) { \
char * __line = (mlbuf), *__next; \
char * __p = (per_line_prefix), *__s = (per_line_suffix); \
while ((__next = strchr(__line, '\n')) != NULL) { \
LOG(printlevel, "%s%.*s%s", __p ?:"", (int)(__next - __line), __line, __s ?:""); \
__line = __next + 1; \
} \
LOG(printlevel, "%s%s%s", __p ?:"", __line, __s ?:""); \
}
/* Helper for function entry -- for very detailed trace of the execution */
#define TRACE_ENTRY(_format,_args... ) \
LOG_A("[enter] %s(" _format ") {" #_args "}", __PRETTY_FUNCTION__, ##_args );
/* Helper for debugging by adding traces -- for debuging a specific location of the code */
#define TRACE_HERE() \
LOG_F(" -- debug checkpoint %d -- ", fd_breakhere());
int fd_breakhere(void);
/* Helper for tracing the CHECK_* macros below -- very very verbose code execution! */
#define TRACE_CALL( str... ) \
LOG_A( str )
/* For development only, to keep track of TODO locations in the code */
#ifndef ERRORS_ON_TODO
# define TODO( _msg, _args... ) \
LOG_F( "TODO: " _msg , ##_args);
#else /* ERRORS_ON_TODO */
# define TODO( _msg, _args... ) \
"TODO" = _msg ## _args; /* just a stupid compilation error to spot the todo */
#endif /* ERRORS_ON_TODO */
/*============================================================*/
/* ERROR CHECKING MACRO */
/*============================================================*/
/* Macros to check a return value and branch out in case of error.
* These macro additionally provide the logging information.
*
* The name "__ret__" is always available in the __fallback__ parameter and contains the error code.
*/
#define CHECK_PRELUDE(__call__) \
int __ret__; \
TRACE_CALL("Check: %s", #__call__ ); \
__ret__ = (__call__)
#define DEFAULT_FB return __ret__;
/* System check: error case if < 0, error value in errno */
#define CHECK_SYS_GEN( faillevel, __call__, __fallback__ ) { \
CHECK_PRELUDE(__call__); \
if (__ret__ < 0) { \
__ret__ = errno; \
LOG(faillevel, "ERROR: in '%s' :\t%s", #__call__ , strerror(__ret__)); \
__fallback__; \
} \
}
/* Check the return value of a function and execute fallback in case of error or special value */
#define CHECK_FCT_GEN2( faillevel, __call__, __speval__, __fallback1__, __fallback2__ ) { \
CHECK_PRELUDE(__call__); \
if (__ret__ != 0) { \
if (__ret__ == (__speval__)) { \
__fallback1__; \
} else { \
LOG(faillevel, "ERROR: in '%s' :\t%s", #__call__ , strerror(__ret__)); \
__fallback2__; \
} \
} \
}
/* Check the return value of a function and execute fallback in case of error (return value different from 0) */
#define CHECK_FCT_GEN( faillevel, __call__, __fallback__) \
CHECK_FCT_GEN2( faillevel, (__call__), 0, , (__fallback__) )
/* Check that a memory allocator did not return NULL, otherwise log an error and execute fallback */
#define CHECK_MALLOC_GEN( faillevel, __call__, __fallback__ ) { \
void * __ptr__; \
TRACE_CALL("Check: %s", #__call__ ); \
__ptr__ = (void *)(__call__); \
if (__ptr__ == NULL) { \
int __ret__ = errno; \
LOG(faillevel, "ERROR: in '%s' :\t%s", #__call__ , strerror(__ret__)); \
__fallback__; \
} \
}
/* Check parameters at function entry, execute fallback on error */
#define CHECK_PARAMS_GEN( faillevel, __bool__, __fallback__ ) { \
TRACE_CALL("Check: %s", #__bool__ ); \
if ( ! (__bool__) ) { \
int __ret__ = EINVAL; \
LOG(faillevel, "ERROR: invalid parameter '%s'", #__bool__ ); \
__fallback__; \
} \
}
/*============================================================*/
/* COMPATIBILITY MACROS, TO BE REMOVED */
/*============================================================*/
/* Redefine the old macros for transition of the code */
#ifndef EXCLUDE_DEPRECATED
#define MARK_DEPRECATED /* __attribute__ ((deprecated)) */
enum old_levels {
NONE = 0,
INFO = 1,
FULL = 2,
ANNOYING = 4,
FCTS = 6,
CALL = 9
} MARK_DEPRECATED;
static __inline__ int old_TRACE_BOOL( enum old_levels level, const char * file, const char * func ) MARK_DEPRECATED
{
if ((fd_debug_one_function && !strcmp(fd_debug_one_function, func))
|| (fd_debug_one_file && !strcmp(fd_debug_one_file, file) ))
return 2; /* Level override */
if ((int)level <= fd_g_debug_lvl)
return 1; /* Normal level */
return 0; /* No trace */
}
#define TRACE_BOOL(level) old_TRACE_BOOL((level), __STRIPPED_FILE__, __PRETTY_FUNCTION__)
#ifndef SWIG
static __inline__ void fd_log_deprecated( int level, const char *format, ... ) MARK_DEPRECATED
{
va_list ap;
va_start(ap, format);
fd_log_va(level, format, ap);
va_end(ap);
}
#else /* SWIG */
void fd_log_deprecated( int level, const char *format, ... );
#endif /* SWIG */
static __inline__ void replace_me() MARK_DEPRECATED { }
#define TRACE_BUFFER(...) replace_me();
#define TRACE_NOTICE(...) replace_me();
/* Use the LOG_* instead, or use the new *_dump functions when dumping an object */
#define fd_log_debug(format,args...) fd_log_deprecated(FD_LOG_DEBUG, format, ## args)
#define fd_log_notice(format,args...) fd_log_deprecated(FD_LOG_NOTICE, format, ## args)
#define fd_log_error(format,args...) fd_log_deprecated(FD_LOG_ERROR, format, ## args)
/* old macro for traces. To be replaced by appropriate LOG_* macros. */
# define TRACE_DEBUG(oldlevel, format,args... ) { \
int __l__; \
if ((__l__ = TRACE_BOOL(oldlevel))) { \
if (oldlevel <= NONE) { LOG_E(format,##args); } \
else if (oldlevel <= INFO) { LOG_N(format,##args); } \
else if (__l__ == 2) { LOG_N(format,##args); } \
else if (oldlevel <= FULL) { LOG_D(format,##args); } \
else { LOG_A(format,##args); } \
} }
/* the following macro must be replaced with LOG_E or LOG_F */
# define TRACE_ERROR LOG_E
/* The following macros are missing the faillevel information, which indicates at what log level the error case should be displayed. */
# define CHECK_SYS_DO( __call__, __fallback__ ) { \
CHECK_PRELUDE(__call__); \
if (__ret__ < 0) { \
__ret__ = errno; \
TRACE_ERROR("ERROR: in '%s' :\t%s", #__call__ , strerror(__ret__)); \
__fallback__; \
} \
}
# define CHECK_SYS( __call__ ) \
CHECK_SYS_DO( (__call__), return __ret__ )
# define CHECK_POSIX_DO2( __call__, __speval__, __fallback1__, __fallback2__ ) { \
CHECK_PRELUDE(__call__); \
if (__ret__ != 0) { \
if (__ret__ == (__speval__)) { \
__fallback1__; \
} else { \
TRACE_ERROR("ERROR: in '%s' :\t%s", #__call__ , strerror(__ret__)); \
__fallback2__; \
} \
} \
}
# define CHECK_POSIX_DO( __call__, __fallback__ ) \
CHECK_POSIX_DO2( (__call__), 0, , __fallback__ )
# define CHECK_POSIX( __call__ ) \
CHECK_POSIX_DO( (__call__), return __ret__ )
# define CHECK_MALLOC_DO( __call__, __fallback__ ) { \
void * __ptr__; \
TRACE_CALL("Check: %s", #__call__ ); \
__ptr__ = (void *)(__call__); \
if (__ptr__ == NULL) { \
int __ret__ = errno; \
TRACE_ERROR("ERROR: in '%s' :\t%s", #__call__ , strerror(__ret__)); \
__fallback__; \
} \
}
# define CHECK_MALLOC( __call__ ) \
CHECK_MALLOC_DO( (__call__), return __ret__ )
# define CHECK_PARAMS_DO( __bool__, __fallback__ ) { \
TRACE_CALL("Check: %s", #__bool__ ); \
if ( ! (__bool__) ) { \
int __ret__ = EINVAL; \
TRACE_ERROR("ERROR: Invalid parameter '%s', %d", #__bool__, __ret__ ); \
__fallback__; \
} \
}
# define CHECK_PARAMS( __bool__ ) \
CHECK_PARAMS_DO( (__bool__), return __ret__ )
# define CHECK_FCT_DO CHECK_POSIX_DO
# define CHECK_FCT CHECK_POSIX
#endif /* EXCLUDE_DEPRECATED */
/*============================================================*/
/* Optimized code: remove all debugging code */
/*============================================================*/
#ifdef STRIP_DEBUG_CODE
#undef LOG_D
#undef LOG_N
#undef LOG_E
#undef LOG_F
#undef LOG_BUFFER
#define LOG_D(format,args... ) /* noop */
#define LOG_N(format,args...) fd_log(FD_LOG_NOTICE, format, ## args)
#define LOG_E(format,args...) fd_log(FD_LOG_ERROR, format, ## args)
#define LOG_F(format,args...) fd_log(FD_LOG_FATAL, format, ## args)
#define LOG_BUFFER(printlevel, level, prefix, buf, bufsz, suffix ) { \
if (printlevel > FD_LOG_DEBUG) { \
int __i; \
size_t __sz = (size_t)(bufsz); \
uint8_t * __buf = (uint8_t *)(buf); \
char * __strbuf[1024+1]; \
for (__i = 0; (__i < __sz) && (__i<(sizeof(__strbuf)/2); __i++) { \
sprintf(__strbuf + (2 * __i), "%02.2hhx", __buf[__i]); \
} \
fd_log(printlevel, prefix"%s"suffix, __strbuf); \
}
#endif /* STRIP_DEBUG_CODE */
/*============================================================*/
/* OTHER MACROS */
/*============================================================*/
/* helper macros (pre-processor hacks to allow macro arguments) */
#define __tostr( arg ) #arg
#define _stringize( arg ) __tostr( arg )
#define __agr( arg1, arg2 ) arg1 ## arg2
#define _aggregate( arg1, arg2 ) __agr( arg1, arg2 )
/* Some aliases to socket addresses structures */
#define sSS struct sockaddr_storage
#define sSA struct sockaddr
#define sSA4 struct sockaddr_in
#define sSA6 struct sockaddr_in6
/* The sockaddr length of a sSS structure */
#define sSAlen( _sa_ ) \
( (socklen_t) ( (((sSA *)_sa_)->sa_family == AF_INET) ? (sizeof(sSA4)) : \
((((sSA *)_sa_)->sa_family == AF_INET6) ? (sizeof(sSA6)) : \
0 ) ) )
#define sSAport( _sa_ ) \
( (socklen_t) ( (((sSA *)_sa_)->sa_family == AF_INET) ? (((sSA4 *)(_sa_))->sin_port) : \
((((sSA *)_sa_)->sa_family == AF_INET6) ? (((sSA6 *)(_sa_))->sin6_port) : \
0 ) ) )
DECLARE_FD_DUMP_PROTOTYPE(fd_sa_dump, sSA * sa, int flags);
#define sSA_DUMP_STRLEN (INET6_ADDRSTRLEN + 1 + 32 + 2)
void fd_sa_sdump_numeric(char * buf /* must be at least sSA_DUMP_STRLEN */, sSA * sa);
/* A l4 protocol name (TCP / SCTP) */
#ifdef DISABLE_SCTP
#define IPPROTO_NAME( _proto ) \
(((_proto) == IPPROTO_TCP) ? "TCP" : \
"Unknown")
#else /* DISABLE_SCTP */
#define IPPROTO_NAME( _proto ) \
( ((_proto) == IPPROTO_TCP) ? "TCP" : \
(((_proto) == IPPROTO_SCTP) ? "SCTP" : \
"Unknown"))
#endif /* DISABLE_SCTP */
/* Define the value of IP loopback address */
#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK inet_addr("127.0.0.1")
#endif /* INADDR_LOOPBACK */
#ifndef INADDR_BROADCAST
#define INADDR_BROADCAST ((in_addr_t) 0xffffffff)
#endif /* INADDR_BROADCAST */
/* An IP equivalent to IN6_IS_ADDR_LOOPBACK */
#ifndef IN_IS_ADDR_LOOPBACK
#define IN_IS_ADDR_LOOPBACK(a) \
((((long int) (a)->s_addr) & ntohl(0xff000000)) == ntohl(0x7f000000))
#endif /* IN_IS_ADDR_LOOPBACK */
/* An IP equivalent to IN6_IS_ADDR_UNSPECIFIED */
#ifndef IN_IS_ADDR_UNSPECIFIED
#define IN_IS_ADDR_UNSPECIFIED(a) \
(((long int) (a)->s_addr) == 0x00000000)
#endif /* IN_IS_ADDR_UNSPECIFIED */
/* create a V4MAPPED address */
#define IN6_ADDR_V4MAP( a6, a4 ) { \
((uint32_t *)(a6))[0] = 0; \
((uint32_t *)(a6))[1] = 0; \
((uint32_t *)(a6))[2] = htonl(0xffff); \
((uint32_t *)(a6))[3] = (uint32_t)(a4); \
}
/* Retrieve a v4 value from V4MAPPED address ( takes a s6_addr as param) */
#define IN6_ADDR_V4UNMAP( a6 ) \
(((in_addr_t *)(a6))[3])
/* We provide macros to convert 64 bit values to and from network byte-order, on systems where it is not already provided. */
#ifndef HAVE_NTOHLL /* Defined by the cmake step, if the ntohll symbol is defined on the system */
# if HOST_BIG_ENDIAN
/* In big-endian systems, we don't have to change the values, since the order is the same as network */
# define ntohll(x) (x)
# define htonll(x) (x)
# else /* HOST_BIG_ENDIAN */
/* For these systems, we must reverse the bytes. Use ntohl and htonl on sub-32 blocs, and inverse these blocs. */
# define ntohll(x) (typeof (x))( (((uint64_t)ntohl( (uint32_t)(x))) << 32 ) | ((uint64_t) ntohl( ((uint64_t)(x)) >> 32 )))
# define htonll(x) (typeof (x))( (((uint64_t)htonl( (uint32_t)(x))) << 32 ) | ((uint64_t) htonl( ((uint64_t)(x)) >> 32 )))
# endif /* HOST_BIG_ENDIAN */
#endif /* HAVE_NTOHLL */
/* This macro will give the next multiple of 4 for an integer (used for padding sizes of AVP). */
#define PAD4(_x) ((_x) + ( (4 - (_x)) & 3 ) )
/* Useful to display any value as (safe) ASCII (will garbage UTF-8 output...) */
#define ASCII(_c) ( ((_c < 32) || (_c > 127)) ? ( _c ? '?' : ' ' ) : _c )
/* Compare timespec structures */
#define TS_IS_INFERIOR( ts1, ts2 ) \
( ((ts1)->tv_sec < (ts2)->tv_sec ) \
|| (((ts1)->tv_sec == (ts2)->tv_sec ) && ((ts1)->tv_nsec < (ts2)->tv_nsec) ))
/* Compute diff between two timespecs (pointers) */
#define TS_DIFFERENCE( tsdiff, tsstart, tsend ) { \
if ((tsend)->tv_nsec < (tsstart)->tv_nsec ) { \
(tsdiff)->tv_sec = (tsend)->tv_sec - (tsstart)->tv_sec - 1; \
(tsdiff)->tv_nsec = (tsend)->tv_nsec + 1000000000 - (tsstart)->tv_nsec; \
} else { \
(tsdiff)->tv_sec = (tsend)->tv_sec - (tsstart)->tv_sec; \
(tsdiff)->tv_nsec = (tsend)->tv_nsec - (tsstart)->tv_nsec; \
}}
/* This gives a good size for buffered reads */
#ifndef BUFSIZ
#define BUFSIZ 96
#endif /* BUFSIZ */
/* This gives the length of a const string */
#define CONSTSTRLEN( str ) (sizeof(str) - 1)
/*============================================================*/
/* PORTABILITY */
/*============================================================*/
#ifndef HAVE_CLOCK_GETTIME
#define CLOCK_REALTIME 0
#include
int clock_gettime(int clk_id, struct timespec* ts);
#endif /* HAVE_CLOCK_GETTIME */
#ifndef HAVE_STRNDUP
char * strndup (char *str, size_t len);
#endif /* HAVE_STRNDUP */
/*============================================================*/
/* BINARY STRINGS */
/*============================================================*/
/* Compute a hash value of a binary string.
The hash must remain local to this machine, there is no guarantee that same input
will give same output on a different system (endianness) */
uint32_t fd_os_hash ( uint8_t * string, size_t len );
/* This type used for binary strings that contain no \0 except as their last character.
It means some string operations can be used on it. */
typedef uint8_t * os0_t;
/* Same as strdup but for os0_t strings */
os0_t os0dup_int(os0_t s, size_t l);
#define os0dup( _s, _l) (void *)os0dup_int((os0_t)(_s), _l)
/* Check that an octet string value can be used as os0_t */
static __inline__ int fd_os_is_valid_os0(uint8_t * os, size_t oslen) {
/* The only situation where it is not valid is when it contains a \0 inside the octet string */
return (memchr(os, '\0', oslen) == NULL);
}
/* The following type denotes a verified DiameterIdentity value (that contains only pure letters, digits, hyphen, dot) */
typedef char * DiamId_t;
/* Maximum length of a hostname we accept */
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 512
#endif /* HOST_NAME_MAX */
/* Check if a binary string contains a valid Diameter Identity value.
rfc3588 states explicitely that such a Diameter Identity consists only of ASCII characters. */
int fd_os_is_valid_DiameterIdentity(uint8_t * os, size_t ossz);
/* The following function validates a string as a Diameter Identity or applies the IDNA transformation on it
if *inoutsz is != 0 on entry, *id may not be \0-terminated.
memory has the following meaning: 0: *id can be realloc'd. 1: *id must be malloc'd on output (was static)
*/
int fd_os_validate_DiameterIdentity(char ** id, size_t * inoutsz, int memory);
/* Create an order relationship for binary strings (not needed to be \0 terminated).
It does NOT mimic strings relationships so that it is more efficient. It is case sensitive.
(the strings are actually first ordered by their lengh, then by their bytes contents)
returns: -1 if os1 < os2; +1 if os1 > os2; 0 if they are equal */
int fd_os_cmp_int(os0_t os1, size_t os1sz, os0_t os2, size_t os2sz);
#define fd_os_cmp(_o1, _l1, _o2, _l2) fd_os_cmp_int((os0_t)(_o1), _l1, (os0_t)(_o2), _l2)
/* A roughly case-insensitive variant, which actually only compares ASCII chars (0-127) in a case-insentitive maneer
-- it does not support locales where a lowercase letter uses more space than upper case, such as ß -> ss
It is slower than fd_os_cmp.
Note that the result is NOT the same as strcasecmp !!!
This function gives the same order as fd_os_cmp, except when it finds 2 strings to be equal.
However this is not always sufficient:
for example fd_os_cmp gives: "Ac" < "aB" < "aa"
if you attempt to fd_os_almostcasesrch "Aa" you will actually have to go past "aB" which is > "Aa".
Therefore you can use the maybefurther parameter.
This parameter is 1 on return if os1 may have been stored further that os2 (assuming os2 values are ordered by fd_os_cmp)
and 0 if we are sure that it is not the case.
When looping through a list of fd_os_cmp classified values, this parameter must be used to stop looping, in addition to the comp result.
*/
int fd_os_almostcasesrch_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz, int * maybefurther);
#define fd_os_almostcasesrch(_o1, _l1, _o2, _l2, _mb) fd_os_almostcasesrch_int((os0_t)(_o1), _l1, (os0_t)(_o2), _l2, _mb)
/* Analyze a DiameterURI and return its components.
Return EINVAL if the URI is not valid.
*diamid is malloc'd on function return and must be freed (it is processed by fd_os_validate_DiameterIdentity).
*secure is 0 (no security) or 1 (security enabled) on return.
*port is 0 (default) or a value in host byte order on return.
*transport is 0 (default) or IPPROTO_* on return.
*proto is 0 (default) or 'd' (diameter), 'r' (radius), or 't' (tacacs+) on return.
*/
int fd_os_parse_DiameterURI(uint8_t * uri, size_t urisz, DiamId_t * diamid, size_t * diamidlen, int * secure, uint16_t * port, int * transport, char *proto);
/*============================================================*/
/* THREADS */
/*============================================================*/
/* Terminate a thread */
static __inline__ int fd_thr_term(pthread_t * th)
{
void * th_ret = NULL;
CHECK_PARAMS(th);
/* Test if it was already terminated */
if (*th == (pthread_t)NULL)
return 0;
/* Cancel the thread if it is still running - ignore error if it was already terminated */
(void) pthread_cancel(*th);
/* Then join the thread */
CHECK_POSIX( pthread_join(*th, &th_ret) );
if (th_ret == PTHREAD_CANCELED) {
TRACE_DEBUG(ANNOYING, "The thread %p was canceled", (void *)*th);
} else {
TRACE_DEBUG(CALL, "The thread %p returned %p", (void *)*th, th_ret);
}
/* Clean the location */
*th = (pthread_t)NULL;
return 0;
}
/*************
Cancelation cleanup handlers for common objects
*************/
static __inline__ void fd_cleanup_mutex( void * mutex )
{
CHECK_POSIX_DO( pthread_mutex_unlock((pthread_mutex_t *)mutex), /* */);
}
static __inline__ void fd_cleanup_rwlock( void * rwlock )
{
CHECK_POSIX_DO( pthread_rwlock_unlock((pthread_rwlock_t *)rwlock), /* */);
}
static __inline__ void fd_cleanup_buffer( void * buffer )
{
free(buffer);
}
static __inline__ void fd_cleanup_socket(void * sockptr)
{
if (sockptr && (*(int *)sockptr > 0)) {
CHECK_SYS_DO( close(*(int *)sockptr), /* ignore */ );
*(int *)sockptr = -1;
}
}
/*============================================================*/
/* LISTS */
/*============================================================*/
/* The following structure represents a chained list element */
struct fd_list {
struct fd_list *next; /* next element in the list */
struct fd_list *prev; /* previous element in the list */
struct fd_list *head; /* head of the list */
void *o; /* additional pointer, used for any purpose (ex: start of the parent object) */
};
/* Initialize a list element */
#define FD_LIST_INITIALIZER( _list_name ) \
{ .next = & _list_name, .prev = & _list_name, .head = & _list_name, .o = NULL }
#define FD_LIST_INITIALIZER_O( _list_name, _obj ) \
{ .next = & _list_name, .prev = & _list_name, .head = & _list_name, .o = _obj }
void fd_list_init ( struct fd_list * list, void * obj );
/* Return boolean, true if the list is empty */
#define FD_IS_LIST_EMPTY( _list ) ((((struct fd_list *)(_list))->head == (_list)) && (((struct fd_list *)(_list))->next == (_list)))
/* Insert an item in a list at known position */
void fd_list_insert_after ( struct fd_list * ref, struct fd_list * item );
void fd_list_insert_before ( struct fd_list * ref, struct fd_list * item );
/* Move all elements from a list at the end of another */
void fd_list_move_end(struct fd_list * ref, struct fd_list * senti);
/* Insert an item in an ordered list -- ordering function must be provided. If duplicate object found, EEXIST and it is returned in ref_duplicate */
int fd_list_insert_ordered( struct fd_list * head, struct fd_list * item, int (*cmp_fct)(void *, void *), void ** ref_duplicate);
/* Unlink an item from a list */
void fd_list_unlink ( struct fd_list * item );
/*============================================================*/
/* DICTIONARY */
/*============================================================*/
/* Structure that contains the complete dictionary definitions */
struct dictionary;
/* Structure that contains a dictionary object */
struct dict_object;
/* Types of object in the dictionary. */
enum dict_object_type {
DICT_VENDOR = 1, /* Vendor */
DICT_APPLICATION, /* Diameter Application */
DICT_TYPE, /* AVP data type */
DICT_ENUMVAL, /* Named constant (value of an enumerated AVP type) */
DICT_AVP, /* AVP */
DICT_COMMAND, /* Diameter Command */
DICT_RULE /* a Rule for AVP in command or grouped AVP */
#define DICT_TYPE_MAX DICT_RULE
};
/* Initialize a dictionary */
int fd_dict_init(struct dictionary ** dict);
/* Destroy a dictionary */
int fd_dict_fini(struct dictionary ** dict);
/*
* FUNCTION: fd_dict_new
*
* PARAMETERS:
* dict : Pointer to the dictionnary where the object is created
* type : What kind of object must be created
* data : pointer to the data for the object.
* type parameter is used to determine the type of data (see below for detail).
* parent : a reference to a parent object, if needed.
* ref : upon successful creation, reference to new object is stored here if !null.
*
* DESCRIPTION:
* Create a new object in the dictionary.
* See following object sections in this header file for more information on data and parent parameters format.
*
* RETURN VALUE:
* 0 : The object is created in the dictionary.
* EINVAL : A parameter is invalid.
* EEXIST : This object is already defined in the dictionary (with conflicting data).
* If "ref" is not NULL, it points to the existing element on return.
* (other standard errors may be returned, too, with their standard meaning. Example:
* ENOMEM : Memory allocation for the new object element failed.)
*/
int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * data, struct dict_object * parent, struct dict_object ** ref );
/*
* FUNCTION: fd_dict_search
*
* PARAMETERS:
* dict : Pointer to the dictionnary where the object is searched
* type : type of object that is being searched
* criteria : how the object must be searched. See object-related sections below for more information.
* what : depending on criteria, the data that must be searched.
* result : On successful return, pointer to the object is stored here.
* retval : this value is returned if the object is not found and result is not NULL.
*
* DESCRIPTION:
* Perform a search in the dictionary.
* See the object-specific sections below to find how to look for each objects.
* If the "result" parameter is NULL, the function is used to check if an object is in the dictionary.
* Otherwise, a reference to the object is stored in result if found.
* If result is not NULL and the object is not found, retval is returned (should be 0 or ENOENT usually)
*
* RETURN VALUE:
* 0 : The object has been found in the dictionary, or *result is NULL.
* EINVAL : A parameter is invalid.
* ENOENT : No matching object has been found, and result was NULL.
*/
int fd_dict_search ( struct dictionary * dict, enum dict_object_type type, int criteria, const void * what, struct dict_object ** result, int retval );
/* Special case: get the generic error command object */
int fd_dict_get_error_cmd(struct dictionary * dict, struct dict_object ** obj);
/*
* FUNCTION: fd_dict_getval
*
* PARAMETERS:
* object : Pointer to a dictionary object.
* data : pointer to a structure to hold the data for the object.
* The type is the same as "data" parameter in fd_dict_new function.
*
* DESCRIPTION:
* Retrieve content of a dictionary object.
* See following object sections in this header file for more information on data and parent parameters format.
*
* RETURN VALUE:
* 0 : The content of the object has been retrieved.
* EINVAL : A parameter is invalid.
*/
int fd_dict_getval ( struct dict_object * object, void * val);
int fd_dict_gettype ( struct dict_object * object, enum dict_object_type * type);
int fd_dict_getdict ( struct dict_object * object, struct dictionary ** dict);
/* Debug functions */
DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump_object, struct dict_object * obj);
DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump, struct dictionary * dict);
/* Function to access full contents of the dictionary, see doc in dictionary.c */
int fd_dict_getlistof(int criteria, void * parent, struct fd_list ** sentinel);
/* Function to remove an entry from the dictionary.
This cannot be used if the object has children (for example a vendor with vendor-specific AVPs).
In such case, the children must be removed first. */
int fd_dict_delete(struct dict_object * obj);
/*
***************************************************************************
*
* Vendor object
*
* These types are used to manage vendors in the dictionary
*
***************************************************************************
*/
/* Type to hold a Vendor ID: "SMI Network Management Private Enterprise Codes" (RFC3232) */
typedef uint32_t vendor_id_t;
/* Type to hold data associated to a vendor */
struct dict_vendor_data {
vendor_id_t vendor_id; /* ID of a vendor */
char * vendor_name; /* The name of this vendor */
};
/* The criteria for searching a vendor object in the dictionary */
enum {
VENDOR_BY_ID = 10, /* "what" points to a vendor_id_t */
VENDOR_BY_NAME, /* "what" points to a char * */
VENDOR_OF_APPLICATION, /* "what" points to a struct dict_object containing an application (see below) */
VENDOR_OF_AVP, /* "what" points to a struct dict_object containing an avp (see below) */
};
/***
* API usage :
Note: the value of "vendor_name" is copied when the object is created, and the string may be disposed afterwards.
On the other side, when value is retrieved with dict_getval, the string is not copied and MUST NOT be freed. It will
be freed automatically along with the object itself with call to dict_fini later.
- fd_dict_new:
The "parent" parameter is not used for vendors.
Sample code to create a vendor:
{
int ret;
struct dict_object * myvendor;
struct dict_vendor_data myvendordata = { 23455, "my vendor name" }; -- just an example...
ret = fd_dict_new ( dict, DICT_VENDOR, &myvendordata, NULL, &myvendor );
}
- fd_dict_search:
Sample codes to look for a vendor object, by its id or name:
{
int ret;
struct dict_object * vendor_found;
vendor_id_t vendorid = 23455;
ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_BY_ID, &vendorid, &vendor_found, ENOENT);
- or -
ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_BY_NAME, "my vendor name", &vendor_found, ENOENT);
}
- fd_dict_getval:
Sample code to retrieve the data from a vendor object:
{
int ret;
struct dict_object * myvendor;
struct dict_vendor_data myvendordata;
ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_BY_NAME, "my vendor name", &myvendor, ENOENT);
ret = fd_dict_getval ( myvendor, &myvendordata );
printf("my vendor id: %d\n", myvendordata.vendor_id );
}
*/
/* Special function: */
uint32_t * fd_dict_get_vendorid_list(struct dictionary * dict);
/*
***************************************************************************
*
* Application object
*
* These types are used to manage Diameter applications in the dictionary
*
***************************************************************************
*/
/* Type to hold a Diameter application ID: IANA assigned value for this application. */
typedef uint32_t application_id_t;
/* Type to hold data associated to an application */
struct dict_application_data {
application_id_t application_id; /* ID of the application */
char * application_name; /* The name of this application */
};
/* The criteria for searching an application object in the dictionary */
enum {
APPLICATION_BY_ID = 20, /* "what" points to a application_id_t */
APPLICATION_BY_NAME, /* "what" points to a char * */
APPLICATION_OF_TYPE, /* "what" points to a struct dict_object containing a type object (see below) */
APPLICATION_OF_COMMAND /* "what" points to a struct dict_object containing a command (see below) */
};
/***
* API usage :
The "parent" parameter of dict_new may point to a vendor object to inform of what vendor defines the application.
for standard-track applications, the "parent" parameter should be NULL.
The vendor associated to an application is retrieved with VENDOR_OF_APPLICATION search criteria on vendors.
- fd_dict_new:
Sample code for application creation:
{
int ret;
struct dict_object * vendor;
struct dict_object * appl;
struct dict_vendor_data vendor_data = {
23455,
"my vendor name"
};
struct dict_application_data app_data = {
9789,
"my vendor's application"
};
ret = fd_dict_new ( dict, DICT_VENDOR, &vendor_data, NULL, &vendor );
ret = fd_dict_new ( dict, DICT_APPLICATION, &app_data, vendor, &appl );
}
- fd_dict_search:
Sample code to retrieve the vendor of an application
{
int ret;
struct dict_object * vendor, * appli;
ret = fd_dict_search ( dict, DICT_APPLICATION, APPLICATION_BY_NAME, "my vendor's application", &appli, ENOENT);
ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_OF_APPLICATION, appli, &vendor, ENOENT);
}
- fd_dict_getval:
Sample code to retrieve the data from an application object:
{
int ret;
struct dict_object * appli;
struct dict_application_data appl_data;
ret = fd_dict_search ( dict, DICT_APPLICATION, APPLICATION_BY_NAME, "my vendor's application", &appli, ENOENT);
ret = fd_dict_getval ( appli, &appl_data );
printf("my application id: %s\n", appl_data.application_id );
}
*/
/*
***************************************************************************
*
* Type object
*
* These types are used to manage AVP data types in the dictionary
*
***************************************************************************
*/
/* Type to store any AVP value */
union avp_value {
struct {
uint8_t *data; /* bytes buffer */
size_t len; /* length of the data buffer */
} os; /* Storage for an octet string */
int32_t i32; /* integer 32 */
int64_t i64; /* integer 64 */
uint32_t u32; /* unsigned 32 */
uint64_t u64; /* unsigned 64 */
float f32; /* float 32 */
double f64; /* float 64 */
};
/* These are the basic AVP types defined in RFC3588bis */
enum dict_avp_basetype {
AVP_TYPE_GROUPED,
AVP_TYPE_OCTETSTRING,
AVP_TYPE_INTEGER32,
AVP_TYPE_INTEGER64,
AVP_TYPE_UNSIGNED32,
AVP_TYPE_UNSIGNED64,
AVP_TYPE_FLOAT32,
AVP_TYPE_FLOAT64
#define AVP_TYPE_MAX AVP_TYPE_FLOAT64
};
/* Callbacks that can be associated with a derived type to easily interpret the AVP value. */
/*
* CALLBACK: dict_avpdata_interpret
*
* PARAMETERS:
* val : Pointer to the AVP value that must be interpreted.
* interpreted : The result of interpretation is stored here. The format and meaning depends on each type.
*
* DESCRIPTION:
* This callback can be provided with a derived type in order to facilitate the interpretation of formated data.
* For example, when an AVP of type "Address" is received, it can be used to convert the octetstring into a struct sockaddr.
* This callback is not called directly, but through the message's API msg_avp_value_interpret function.
*
* RETURN VALUE:
* 0 : Operation complete.
* !0 : An error occurred, the error code is returned.
*/
typedef int (*dict_avpdata_interpret) (union avp_value * value, void * interpreted);
/*
* CALLBACK: dict_avpdata_encode
*
* PARAMETERS:
* data : The formated data that must be stored in the AVP value.
* val : Pointer to the AVP value storage area where the data must be stored.
*
* DESCRIPTION:
* This callback can be provided with a derived type in order to facilitate the encoding of formated data.
* For example, it can be used to convert a struct sockaddr in an AVP value of type Address.
* This callback is not called directly, but through the message's API msg_avp_value_encode function.
* If the callback is defined for an OctetString based type, the created string must be malloc'd. free will be called
* automatically later.
*
* RETURN VALUE:
* 0 : Operation complete.
* !0 : An error occurred, the error code is returned.
*/
typedef int (*dict_avpdata_encode) (void * data, union avp_value * val);
/*
* CALLBACK: dict_avpdata_check
*
* PARAMETERS:
* val : Pointer to the AVP value that was received and needs to be sanity checked.
* data : a parameter stored in the type structure (to enable more generic check functions)
* error_msg: upon erroneous value, a string describing the error can be returned here (it will be strcpy by caller). This description will be returned in the error message, if any.
*
* DESCRIPTION:
* This callback can be provided with a derived type in order to improve the operation of the
* fd_msg_parse_dict function. When this callback is present, the value of the AVP that has
* been parsed is passed to this function for finer granularity check. For example for some
* speccific AVP, the format of an OCTETSTRING value can be further checked, or the
* interger value can be verified.
*
* RETURN VALUE:
* 0 : The value is valid.
* !0 : An error occurred, the error code is returned. It is advised to return EINVAL on incorrect val
*/
typedef int (*dict_avpdata_check) (void * data, union avp_value * val, char ** error_msg);
/* Type to hold data associated to a derived AVP data type */
struct dict_type_data {
enum dict_avp_basetype type_base; /* How the data of such AVP must be interpreted */
char * type_name; /* The name of this type */
dict_avpdata_interpret type_interpret;/* cb to convert the AVP value in more comprehensive format (or NULL) */
dict_avpdata_encode type_encode; /* cb to convert formatted data into an AVP value (or NULL) */
DECLARE_FD_DUMP_PROTOTYPE((*type_dump), union avp_value * val); /* cb called by fd_msg_dump_* for this type of data (if != NULL). Returned string must be freed. */
dict_avpdata_check type_check;
void * type_check_param;
};
/* The criteria for searching a type object in the dictionary */
enum {
TYPE_BY_NAME = 30, /* "what" points to a char * */
TYPE_OF_ENUMVAL, /* "what" points to a struct dict_object containing an enumerated constant (DICT_ENUMVAL, see below). */
TYPE_OF_AVP /* "what" points to a struct dict_object containing an AVP object. */
};
/****
Callbacks defined in libfdproto/dictionary_functions.c file -- see that file for usage.
*/
/* Convert an Address type AVP into a struct sockaddr_storage */
int fd_dictfct_Address_encode(void * data, union avp_value * avp_value);
int fd_dictfct_Address_interpret(union avp_value * avp_value, void * interpreted);
DECLARE_FD_DUMP_PROTOTYPE(fd_dictfct_Address_dump, union avp_value * avp_value);
/* Display the content of an AVP of type UTF8String in the log file */
DECLARE_FD_DUMP_PROTOTYPE(fd_dictfct_UTF8String_dump, union avp_value * avp_value);
/* For Time AVPs, map with time_t value directly */
int fd_dictfct_Time_encode(void * data, union avp_value * avp_value);
int fd_dictfct_Time_interpret(union avp_value * avp_value, void * interpreted);
DECLARE_FD_DUMP_PROTOTYPE(fd_dictfct_Time_dump, union avp_value * avp_value);
/* For string AVP, the following type_check function provides simple basic check for specific characters presence, e.g. use "@." for trivial email address check */
int fd_dictfct_CharInOS_check(void * data, union avp_value * val, char ** error_msg);
/****/
/***
* API usage :
- fd_dict_new:
The "parent" parameter may point to an application object, when a type is defined by a Diameter application.
Sample code:
{
int ret;
struct dict_object * mytype;
struct dict_type_data mytypedata =
{
AVP_TYPE_OCTETSTRING,
"Address",
NULL,
NULL
};
ret = fd_dict_new ( dict, DICT_TYPE, &mytypedata, NULL, &mytype );
}
- fd_dict_search:
Sample code:
{
int ret;
struct dict_object * address_type;
ret = fd_dict_search ( dict, DICT_TYPE, TYPE_BY_NAME, "Address", &address_type, ENOENT);
}
*/
/*
***************************************************************************
*
* Enumerated values object
*
* These types are used to manage named constants of some AVP,
* for enumerated types. freeDiameter allows constants for types others than Unsigned32
*
***************************************************************************
*/
/* Type to hold data of named constants for AVP */
struct dict_enumval_data {
char * enum_name; /* The name of this constant */
union avp_value enum_value; /* Value of the constant. Union term depends on parent type's base type. */
};
/* The criteria for searching a constant in the dictionary */
enum {
ENUMVAL_BY_STRUCT = 40, /* "what" points to a struct dict_enumval_request as defined below */
ENUMVAL_BY_NAME, /* This cannot be used for researches */
ENUMVAL_BY_VALUE /* This cannot be used for researches */
};
struct dict_enumval_request {
/* Identifier of the parent type, one of the following must not be NULL */
struct dict_object *type_obj;
char * type_name;
/* Search criteria for the constant */
struct dict_enumval_data search; /* search.enum_value is used only if search.enum_name == NULL */
};
/***
* API usage :
- fd_dict_new:
The "parent" parameter must point to a derived type object.
Sample code to create a type "Boolean" with two constants "True" and "False":
{
int ret;
struct dict_object * type_boolean;
struct dict_type_data type_boolean_data =
{
AVP_TYPE_INTEGER32,
"Boolean",
NULL,
NULL
};
struct dict_enumval_data boolean_false =
{
.enum_name="False",
.enum_value.i32 = 0
};
struct dict_enumval_data boolean_true =
{
.enum_name="True",
.enum_value.i32 = -1
};
ret = fd_dict_new ( dict, DICT_TYPE, &type_boolean_data, NULL, &type_boolean );
ret = fd_dict_new ( dict, DICT_ENUMVAL, &boolean_false, type_boolean, NULL );
ret = fd_dict_new ( dict, DICT_ENUMVAL, &boolean_true , type_boolean, NULL );
}
- fd_dict_search:
Sample code to look for a constant name, by its value:
{
int ret;
struct dict_object * value_found;
struct dict_enumval_request boolean_by_value =
{
.type_name = "Boolean",
.search.enum_name=NULL,
.search.enum_value.i32 = -1
};
ret = fd_dict_search ( dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &boolean_by_value, &value_found, ENOENT);
}
- fd_dict_getval:
Sample code to retrieve the data from a constant object:
{
int ret;
struct dict_object * value_found;
struct dict_enumval_data boolean_data = NULL;
struct dict_enumval_request boolean_by_value =
{
.type_name = "Boolean",
.search.enum_name=NULL,
.search.enum_value.i32 = 0
};
ret = fd_dict_search ( dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &boolean_by_value, &value_found, ENOENT);
ret = fd_dict_getval ( value_found, &boolean_data );
printf(" Boolean with value 0: %s", boolean_data.enum_name );
}
*/
/*
***************************************************************************
*
* AVP object
*
* These objects are used to manage AVP definitions in the dictionary
*
***************************************************************************
*/
/* Type to hold an AVP code. For vendor 0, these codes are assigned by IANA. Otherwise, it is managed by the vendor */
typedef uint32_t avp_code_t;
/* Values of AVP flags */
#define AVP_FLAG_VENDOR 0x80
#define AVP_FLAG_MANDATORY 0x40
#define AVP_FLAG_RESERVED3 0x20
#define AVP_FLAG_RESERVED4 0x10
#define AVP_FLAG_RESERVED5 0x08
#define AVP_FLAG_RESERVED6 0x04
#define AVP_FLAG_RESERVED7 0x02
#define AVP_FLAG_RESERVED8 0x01
/* For dumping flags and values */
#define DUMP_AVPFL_str "%c%c%s%s%s%s%s%s"
#define DUMP_AVPFL_val(_val) (_val & AVP_FLAG_VENDOR)?'V':'-' , (_val & AVP_FLAG_MANDATORY)?'M':'-', \
(_val & AVP_FLAG_RESERVED3)?"3":"", (_val & AVP_FLAG_RESERVED4)?"4":"", \
(_val & AVP_FLAG_RESERVED5)?"5":"", (_val & AVP_FLAG_RESERVED6)?"6":"", (_val & AVP_FLAG_RESERVED7)?"7":"", (_val & AVP_FLAG_RESERVED8)?"8":""
/* Type to hold data associated to an avp */
struct dict_avp_data {
avp_code_t avp_code; /* Code of the avp */
vendor_id_t avp_vendor; /* Vendor of the AVP, or 0 */
char * avp_name; /* Name of this AVP */
uint8_t avp_flag_mask; /* Mask of fixed AVP flags */
uint8_t avp_flag_val; /* Values of the fixed flags */
enum dict_avp_basetype avp_basetype; /* Basic type of data found in the AVP */
};
/* The criteria for searching an avp object in the dictionary */
enum {
AVP_BY_CODE = 50, /* "what" points to an avp_code_t, vendor is always 0 */
AVP_BY_NAME, /* "what" points to a char *, vendor is always 0 */
AVP_BY_NAME_ALL_VENDORS,/* "what" points to a string. Might be quite slow... */
AVP_BY_STRUCT, /* "what" points to a struct dict_avp_request_ex (see below) */
/* kept for backward compatibility, better use AVP_BY_STRUCT above instead */
AVP_BY_CODE_AND_VENDOR, /* "what" points to a struct dict_avp_request (see below), where avp_vendor and avp_code are set */
AVP_BY_NAME_AND_VENDOR /* "what" points to a struct dict_avp_request (see below), where avp_vendor and avp_name are set */
};
/* Struct used for some researchs */
struct dict_avp_request_ex {
struct {
/* Only one of the following fields must be set. */
struct dict_object * vendor; /* most efficient if already known, set to NULL to ignore */
vendor_id_t vendor_id; /* set to 0 to ignore -- prefer AVP_BY_CODE or AVP_BY_NAME for vendor 0 */
const char * vendor_name; /* set to NULL to ignore */
} avp_vendor;
struct {
/* Only one of the following fields must be set */
avp_code_t avp_code; /* set to 0 to ignore */
const char * avp_name; /* set to NULL to ignore */
} avp_data;
};
struct dict_avp_request {
vendor_id_t avp_vendor;
avp_code_t avp_code;
char * avp_name;
};
/***
* API usage :
If "parent" parameter is not NULL during AVP creation, it must point to a DICT_TYPE object.
The extended type is then attached to the AVP. In case where it is an enumerated type, the value of
AVP is automatically interpreted in debug messages, and in message checks.
The derived type of an AVP can be retrieved with: dict_search ( DICT_TYPE, TYPE_OF_AVP, avp, ... )
To create the rules (ABNF) for children of Grouped AVP, see the DICT_RULE related part.
- fd_dict_new:
Sample code for AVP creation:
{
int ret;
struct dict_object * user_name_avp;
struct dict_object * boolean_type;
struct dict_object * sample_boolean_avp;
struct dict_avp_data user_name_data = {
1, // code
0, // vendor
"User-Name", // name
AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, // fixed mask: V and M values must always be defined as follow. other flags can be set or cleared
AVP_FLAG_MANDATORY, // the V flag must be cleared, the M flag must be set.
AVP_TYPE_OCTETSTRING // User-Name AVP contains OctetString data (further precision such as UTF8String can be given with a parent derived type)
};
struct dict_avp_data sample_boolean_data = {
31337,
23455,
"Sample-Boolean",
AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY,
AVP_FLAG_VENDOR,
AVP_TYPE_INTEGER32 // This MUST be the same as parent type's
};
-- Create an AVP with a base type --
ret = fd_dict_new ( dict, DICT_AVP, &user_name_data, NULL, &user_name_avp );
-- Create an AVP with a derived type --
ret = fd_dict_search ( dict, DICT_TYPE, TYPE_BY_NAME, "Boolean", &boolean_type, ENOENT);
ret = fd_dict_new ( dict, DICT_AVP, &sample_boolean_data , boolean_type, &sample_boolean_avp );
}
- fd_dict_search:
Sample code to look for an AVP
{
int ret;
struct dict_object * avp_username;
struct dict_object * avp_sampleboolean;
struct dict_avp_request avpvendorboolean =
{
.avp_vendor = 23455,
.avp_name = "Sample-Boolean"
};
ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME, "User-Name", &avp_username, ENOENT);
ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &avpvendorboolean, &avp_sampleboolean, ENOENT);
-- this would also work, but be slower, because it has to search all vendor dictionaries --
ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "Sample-Boolean", &avp_sampleboolean, ENOENT);
}
- fd_dict_getval:
Sample code to retrieve the data from an AVP object:
{
int ret;
struct dict_object * avp_username;
struct dict_avp_data user_name_data;
ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME, "User-Name", &avp_username, ENOENT);
ret = fd_dict_getval ( avp_username, &user_name_data );
printf("User-Name code: %d\n", user_name_data.avp_code );
}
*/
/*
***************************************************************************
*
* Command object
*
* These types are used to manage commands objects in the dictionary
*
***************************************************************************
*/
/* Type to hold a Diameter command code: IANA assigned values. 0x0-0x7fffff=standard, 0x800000-0xfffffd=vendors, 0xfffffe-0xffffff=experimental */
typedef uint32_t command_code_t;
/* Values of command flags */
#define CMD_FLAG_REQUEST 0x80
#define CMD_FLAG_PROXIABLE 0x40
#define CMD_FLAG_ERROR 0x20
#define CMD_FLAG_RETRANSMIT 0x10
#define CMD_FLAG_RESERVED5 0x08
#define CMD_FLAG_RESERVED6 0x04
#define CMD_FLAG_RESERVED7 0x02
#define CMD_FLAG_RESERVED8 0x01
/* For dumping flags and values */
#define DUMP_CMDFL_str "%c%c%c%c%s%s%s%s"
#define DUMP_CMDFL_val(_val) (_val & CMD_FLAG_REQUEST)?'R':'-' , (_val & CMD_FLAG_PROXIABLE)?'P':'-' , (_val & CMD_FLAG_ERROR)?'E':'-' , (_val & CMD_FLAG_RETRANSMIT)?'T':'-', \
(_val & CMD_FLAG_RESERVED5)?"5":"", (_val & CMD_FLAG_RESERVED6)?"6":"", (_val & CMD_FLAG_RESERVED7)?"7":"", (_val & CMD_FLAG_RESERVED8)?"8":""
/* Type to hold data associated to a command */
struct dict_cmd_data {
command_code_t cmd_code; /* code of the command */
char * cmd_name; /* Name of the command */
uint8_t cmd_flag_mask; /* Mask of fixed-value flags */
uint8_t cmd_flag_val; /* values of the fixed flags */
};
/* The criteria for searching an avp object in the dictionary */
enum {
CMD_BY_NAME = 60, /* "what" points to a char * */
CMD_BY_CODE_R, /* "what" points to a command_code_t. The "Request" command is returned. */
CMD_BY_CODE_A, /* "what" points to a command_code_t. The "Answer" command is returned. */
CMD_ANSWER /* "what" points to a struct dict_object of a request command. The corresponding "Answer" command is returned. */
};
/***
* API usage :
The "parent" parameter of dict_new may point to an application object to inform of what application defines the command.
The application associated to a command is retrieved with APPLICATION_OF_COMMAND search criteria on applications.
To create the rules for children of commands, see the DICT_RULE related part.
Note that the "Request" and "Answer" commands are two independant objects. This allows to have different rules for each.
- fd_dict_new:
Sample code for command creation:
{
int ret;
struct dict_object * cer;
struct dict_object * cea;
struct dict_cmd_data ce_data = {
257, // code
"Capabilities-Exchange-Request", // name
CMD_FLAG_REQUEST, // mask
CMD_FLAG_REQUEST // value. Only the "R" flag is constrained here, set.
};
ret = fd_dict_new (dict, DICT_COMMAND, &ce_data, NULL, &cer );
ce_data.cmd_name = "Capabilities-Exchange-Answer";
ce_data.cmd_flag_val = 0; // Same constraint on "R" flag, but this time it must be cleared.
ret = fd_dict_new ( dict, DICT_COMMAND, &ce_data, NULL, &cea );
}
- fd_dict_search:
Sample code to look for a command
{
int ret;
struct dict_object * cer, * cea;
command_code_t code = 257;
ret = fd_dict_search ( dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer, ENOENT);
ret = fd_dict_search ( dict, DICT_COMMAND, CMD_BY_CODE_R, &code, &cer, ENOENT);
}
- fd_dict_getval:
Sample code to retrieve the data from a command object:
{
int ret;
struct dict_object * cer;
struct dict_object * cea;
struct dict_cmd_data cea_data;
ret = fd_dict_search ( dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer, ENOENT);
ret = fd_dict_search ( dict, DICT_COMMAND, CMD_ANSWER, cer, &cea, ENOENT);
ret = fd_dict_getval ( cea, &cea_data );
printf("Answer to CER: %s\n", cea_data.cmd_name );
}
*/
/*
***************************************************************************
*
* Rule object
*
* These objects are used to manage rules in the dictionary (ABNF implementation)
* This is used for checking messages validity (more powerful than a DTD)
*
***************************************************************************
*/
/* This defines the kind of rule that is defined */
enum rule_position {
RULE_FIXED_HEAD = 1, /* The AVP must be at the head of the group. The rule_order field is used to specify the position. */
RULE_REQUIRED, /* The AVP must be present in the parent, but its position is not defined. */
RULE_OPTIONAL, /* The AVP may be present in the message. Used to specify a max number of occurences for example */
RULE_FIXED_TAIL /* The AVP must be at the end of the group. The rule_order field is used to specify the position. */
};
/* Content of a RULE object data */
struct dict_rule_data {
struct dict_object *rule_avp; /* Pointer to the AVP object that is concerned by this rule */
enum rule_position rule_position; /* The position in which the rule_avp must appear in the parent */
unsigned rule_order; /* for RULE_FIXED_* rules, the place. 1,2,3.. for HEAD rules; ...,3,2,1 for TAIL rules. */
int rule_min; /* Minimum number of occurences. -1 means "default": 0 for optional rules, 1 for other rules */
int rule_max; /* Maximum number of occurences. -1 means no maximum. 0 means the AVP is forbidden. */
};
/* The criteria for searching a rule in the dictionary */
enum {
RULE_BY_AVP_AND_PARENT = 70 /* "what" points to a struct dict_rule_request -- see below. This is used to query "what is the rule for this AVP in this group?" */
};
/* Structure for querying the dictionary about a rule */
struct dict_rule_request {
struct dict_object *rule_parent; /* The grouped avp or command to which the rule apply */
struct dict_object *rule_avp; /* The AVP concerned by this rule */
};
/***
* API usage :
The "parent" parameter can not be NULL. It points to the object (grouped avp or command) to which this rule apply (i.e. for which the ABNF is defined).
- fd_dict_new:
Sample code for rule creation. Let's create the Proxy-Info grouped AVP for example.
{
int ret;
struct dict_object * proxy_info_avp;
struct dict_object * proxy_host_avp;
struct dict_object * proxy_state_avp;
struct dict_object * diameteridentity_type;
struct dict_rule_data rule_data;
struct dict_type_data di_type_data = { AVP_TYPE_OCTETSTRING, "DiameterIdentity", NULL, NULL };
struct dict_avp_data proxy_info_data = { 284, 0, "Proxy-Info", AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_GROUPED };
struct dict_avp_data proxy_host_data = { 280, 0, "Proxy-Host", AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_OCTETSTRING };
struct dict_avp_data proxy_state_data = { 33, 0, "Proxy-State",AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_OCTETSTRING };
-- Create the parent AVP
ret = fd_dict_new ( dict, DICT_AVP, &proxy_info_data, NULL, &proxy_info_avp );
-- Create the first child AVP.
ret = fd_dict_new ( dict, DICT_TYPE, &di_type_data, NULL, &diameteridentity_type );
ret = fd_dict_new ( dict, DICT_AVP, &proxy_host_data, diameteridentity_type, &proxy_host_avp );
-- Create the other child AVP
ret = fd_dict_new ( dict, DICT_AVP, &proxy_state_data, NULL, &proxy_state_avp );
-- Now we can create the rules. Both children AVP are mandatory.
rule_data.rule_position = RULE_REQUIRED;
rule_data.rule_min = -1;
rule_data.rule_max = -1;
rule_data.rule_avp = proxy_host_avp;
ret = fd_dict_new ( dict, DICT_RULE, &rule_data, proxy_info_avp, NULL );
rule_data.rule_avp = proxy_state_avp;
ret = fd_dict_new ( dict, DICT_RULE, &rule_data, proxy_info_avp, NULL );
}
- fd_dict_search and fd_dict_getval are similar to previous examples.
*/
/* Define some hard-coded values */
/* Application */
#define AI_RELAY 0xffffffff
/* Commands Codes */
#define CC_CAPABILITIES_EXCHANGE 257
#define CC_RE_AUTH 258
#define CC_ACCOUNTING 271
#define CC_ABORT_SESSION 274
#define CC_SESSION_TERMINATION 275
#define CC_DEVICE_WATCHDOG 280
#define CC_DISCONNECT_PEER 282
/* AVPs (Vendor 0) */
#define AC_USER_NAME 1
#define AC_PROXY_STATE 33
#define AC_HOST_IP_ADDRESS 257
#define AC_AUTH_APPLICATION_ID 258
#define AC_ACCT_APPLICATION_ID 259
#define AC_VENDOR_SPECIFIC_APPLICATION_ID 260
#define AC_REDIRECT_HOST_USAGE 261
#define AC_REDIRECT_MAX_CACHE_TIME 262
#define AC_SESSION_ID 263
#define AC_ORIGIN_HOST 264
#define AC_SUPPORTED_VENDOR_ID 265
#define AC_VENDOR_ID 266
#define AC_FIRMWARE_REVISION 267
#define AC_RESULT_CODE 268
#define AC_PRODUCT_NAME 269
#define AC_DISCONNECT_CAUSE 273
#define ACV_DC_REBOOTING 0
#define ACV_DC_BUSY 1
#define ACV_DC_NOT_FRIEND 2
#define AC_ORIGIN_STATE_ID 278
#define AC_FAILED_AVP 279
#define AC_PROXY_HOST 280
#define AC_ERROR_MESSAGE 281
#define AC_ROUTE_RECORD 282
#define AC_DESTINATION_REALM 283
#define AC_PROXY_INFO 284
#define AC_REDIRECT_HOST 292
#define AC_DESTINATION_HOST 293
#define AC_ERROR_REPORTING_HOST 294
#define AC_ORIGIN_REALM 296
#define AC_INBAND_SECURITY_ID 299
#define ACV_ISI_NO_INBAND_SECURITY 0
#define ACV_ISI_TLS 1
/* Error codes from Base protocol
(reference: http://www.iana.org/assignments/aaa-parameters/aaa-parameters.xml#aaa-parameters-4)
Note that currently, rfc3588bis-26 has some different values for some of these
*/
#define ER_DIAMETER_MULTI_ROUND_AUTH 1001
#define ER_DIAMETER_SUCCESS 2001
#define ER_DIAMETER_LIMITED_SUCCESS 2002
#define ER_DIAMETER_COMMAND_UNSUPPORTED 3001 /* 5019 ? */
#define ER_DIAMETER_UNABLE_TO_DELIVER 3002
#define ER_DIAMETER_REALM_NOT_SERVED 3003
#define ER_DIAMETER_TOO_BUSY 3004
#define ER_DIAMETER_LOOP_DETECTED 3005
#define ER_DIAMETER_REDIRECT_INDICATION 3006
#define ER_DIAMETER_APPLICATION_UNSUPPORTED 3007
#define ER_DIAMETER_INVALID_HDR_BITS 3008 /* 5020 ? */
#define ER_DIAMETER_INVALID_AVP_BITS 3009 /* 5021 ? */
#define ER_DIAMETER_UNKNOWN_PEER 3010 /* 5018 ? */
#define ER_DIAMETER_AUTHENTICATION_REJECTED 4001
#define ER_DIAMETER_OUT_OF_SPACE 4002
#define ER_ELECTION_LOST 4003
#define ER_DIAMETER_AVP_UNSUPPORTED 5001
#define ER_DIAMETER_UNKNOWN_SESSION_ID 5002
#define ER_DIAMETER_AUTHORIZATION_REJECTED 5003
#define ER_DIAMETER_INVALID_AVP_VALUE 5004
#define ER_DIAMETER_MISSING_AVP 5005
#define ER_DIAMETER_RESOURCES_EXCEEDED 5006
#define ER_DIAMETER_CONTRADICTING_AVPS 5007
#define ER_DIAMETER_AVP_NOT_ALLOWED 5008
#define ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES 5009
#define ER_DIAMETER_NO_COMMON_APPLICATION 5010
#define ER_DIAMETER_UNSUPPORTED_VERSION 5011
#define ER_DIAMETER_UNABLE_TO_COMPLY 5012
#define ER_DIAMETER_INVALID_BIT_IN_HEADER 5013 /* 3011 ? */
#define ER_DIAMETER_INVALID_AVP_LENGTH 5014
#define ER_DIAMETER_INVALID_MESSAGE_LENGTH 5015 /* 3012 ? */
#define ER_DIAMETER_INVALID_AVP_BIT_COMBO 5016 /* deprecated? */
#define ER_DIAMETER_NO_COMMON_SECURITY 5017
/*============================================================*/
/* SESSIONS */
/*============================================================*/
/* Modules that want to associate a state with a Session-Id must first register a handler of this type */
struct session_handler;
/* This opaque structure represents a session associated with a Session-Id */
struct session;
/* The state information that a module associate with a session -- each module defines its own data format */
struct sess_state; /* declare this in your own extension */
typedef DECLARE_FD_DUMP_PROTOTYPE((*session_state_dump), struct sess_state * st);
/* The following function must be called to activate the session expiry mechanism */
int fd_sess_start(void);
/*
* FUNCTION: fd_sess_handler_create
*
* PARAMETERS:
* handler : location where the new handler must be stored.
* cleanup : a callback function that must be called when the session with associated data is destroyed.
* dumper : if not NULL, will be called during fd_sess_dump to display the data associated with a session. NULL otherwise.
* opaque : A pointer that is passed to the cleanup callback -- the content is never examined by the framework.
*
* DESCRIPTION:
* Create a new session handler. This is needed by a module to associate a state with a session object.
* The cleanup handler is called when the session timeout expires, or fd_sess_destroy is called. It must free
* the state associated with the session, and eventually trig other actions (send a STR, ...).
*
* RETURN VALUE:
* 0 : The new handler has been created.
* EINVAL : A parameter is invalid.
* ENOMEM : Not enough memory to complete the operation
*/
int fd_sess_handler_create ( struct session_handler ** handler, void (*cleanup)(struct sess_state * state, os0_t sid, void * opaque), session_state_dump dumper, void * opaque );
/*
* FUNCTION: fd_sess_handler_destroy
*
* PARAMETERS:
* handler : location of an handler created by fd_sess_handler_create.
* opaque : the opaque pointer registered with the callback is restored here (if ! NULL).
*
* DESCRIPTION:
* This destroys a session handler (typically called when an application is shutting down).
* If sessions states are registered with this handler, the cleanup callback is called on them.
*
* RETURN VALUE:
* 0 : The handler was destroyed.
* EINVAL : A parameter is invalid.
* ENOMEM : Not enough memory to complete the operation
*/
int fd_sess_handler_destroy ( struct session_handler ** handler, void **opaque );
/*
* FUNCTION: fd_sess_new
*
* PARAMETERS:
* session : The location where the session object will be created upon success.
* diamid : a Diameter Identity, or NULL.
* diamidlen : if diamid is \0-terminated, this can be 0. Otherwise, the length of diamid.
* opt : Additional string, or NULL. Usage is described below.
* optlen : if opt is \0-terminated, this can be 0. Otherwise, the length of opt.
*
* DESCRIPTION:
* Create a new session object. The Session-Id string associated with this session is generated as follow:
* If diamId parameter is provided, the string is created according to the RFC: ;;[;opt] where
* diamId is a Diameter Identity.
* high32 and low32 are the parts of a monotonic 64 bits counter initialized to (time, 0) at startup.
* opt is an optional string that can be concatenated to the identifier.
* If diamId is NULL, the string is exactly the content of opt.
*
* RETURN VALUE:
* 0 : The session is created, the initial msg refcount is 1.
* EINVAL : A parameter is invalid.
* EALREADY : A session with the same name already exists (returned in *session), the msg refcount is increased.
* ENOMEM : Not enough memory to complete the operation
*/
int fd_sess_new ( struct session ** session, DiamId_t diamid, size_t diamidlen, uint8_t * opt, size_t optlen );
/*
* FUNCTION: fd_sess_fromsid
*
* PARAMETERS:
* sid : pointer to a string containing a Session-Id (should be UTF-8).
* len : length of the sid string (which does not need to be '\0'-terminated)
* session : On success, pointer to the session object created / retrieved.
* isnew : if not NULL, set to 1 on return if the session object has been created, 0 if it was simply retrieved.
*
* DESCRIPTION:
* Retrieve a session object from a Session-Id string. In case no session object was previously existing with this
* id, a new object is silently created (equivalent to fd_sess_new with flag SESSION_NEW_FULL).
*
* RETURN VALUE:
* 0 : The session parameter has been updated.
* EINVAL : A parameter is invalid.
* ENOMEM : Not enough memory to complete the operation
*/
int fd_sess_fromsid ( uint8_t * sid, size_t len, struct session ** session, int * isnew);
/* only use the following in specific situations, e.g. app_radgw extension. They are normally handled by the framework only */
int fd_sess_fromsid_msg ( uint8_t * sid, size_t len, struct session ** session, int * isnew);
int fd_sess_ref_msg ( struct session * session );
/*
* FUNCTION: fd_sess_getsid
*
* PARAMETERS:
* session : Pointer to a session object.
* sid : On success, the location of the sid is stored here.
*
* DESCRIPTION:
* Retrieve the session identifier (Session-Id) corresponding to a session object.
* The returned sid is a \0-terminated binary string which might be UTF-8 (but there is no guarantee in the framework).
* It may be used for example to set the value of an AVP.
* Note that the sid string is not copied, just its reference... do not free it!
*
* RETURN VALUE:
* 0 : The sid & len parameters have been updated.
* EINVAL : A parameter is invalid.
*/
int fd_sess_getsid ( struct session * session, os0_t * sid, size_t * sidlen );
/*
* FUNCTION: fd_sess_settimeout
*
* PARAMETERS:
* session : The session for which to set the timeout.
* timeout : The date when the session times out.
*
* DESCRIPTION:
* Set the lifetime for a given session object. This function may be
* called several times on the same object to update the timeout value.
* When the timeout date is reached, the cleanup handler of each
* module that registered data with this session is called, then the
* session is cleared.
*
* There is a possible race condition between cleanup of the session
* and use of its data; applications should ensure that they are not
* using data from a session that is about to expire / expired.
*
* RETURN VALUE:
* 0 : The session timeout has been updated.
* EINVAL : A parameter is invalid.
*/
int fd_sess_settimeout( struct session * session, const struct timespec * timeout );
/*
* FUNCTION: fd_sess_destroy
*
* PARAMETERS:
* session : Pointer to a session object.
*
* DESCRIPTION:
* Destroys all associated states of a session, if any.
* Equivalent to a session timeout expired, but the effect is immediate.
* The session itself is marked as deleted, and will be freed when it is not referenced
* by any message anymore.
*
* RETURN VALUE:
* 0 : The session no longer exists.
* EINVAL : A parameter is invalid.
*/
int fd_sess_destroy ( struct session ** session );
/*
* FUNCTION: fd_sess_reclaim
*
* PARAMETERS:
* session : Pointer to a session object.
*
* DESCRIPTION:
* Equivalent to fd_sess_destroy, only if no session_state is associated with the session.
* Otherwise, this function has no effect (except that it sets *session to NULL).
*
* RETURN VALUE:
* 0 : The session was reclaimed.
* EINVAL : A parameter is invalid.
*/
int fd_sess_reclaim ( struct session ** session );
/*
* FUNCTION: fd_sess_state_store
*
* PARAMETERS:
* handler : The handler with which the state is registered.
* session : The session object with which the state is registered.
* state : An application state (opaque data) to store with the session.
*
* DESCRIPTION:
* Stores an application state with a session. This state can later be retrieved
* with fd_sess_state_retrieve, or implicitly in the cleanup handler when the session
* is destroyed.
*
* RETURN VALUE:
* 0 : The state has been stored.
* EINVAL : A parameter is invalid.
* EALREADY : Data was already associated with this session and client.
* ENOMEM : Not enough memory to complete the operation
*/
int fd_sess_state_store ( struct session_handler * handler, struct session * session, struct sess_state ** state );
/*
* FUNCTION: fd_sess_state_retrieve
*
* PARAMETERS:
* handler : The handler with which the state was registered.
* session : The session object with which the state was registered.
* state : Location where the state must be saved if it is found.
*
* DESCRIPTION:
* Retrieves a state saved by fd_sess_state_store.
* After this function has been called, the state is no longer associated with
* the session. A new call to fd_sess_state_store must be performed in order to
* store again the data with the session.
*
* RETURN VALUE:
* 0 : *state is updated (NULL or points to the state if it was found).
* EINVAL : A parameter is invalid.
*/
int fd_sess_state_retrieve ( struct session_handler * handler, struct session * session, struct sess_state ** state );
/* For debug */
DECLARE_FD_DUMP_PROTOTYPE(fd_sess_dump, struct session * session, int with_states);
DECLARE_FD_DUMP_PROTOTYPE(fd_sess_dump_hdl, struct session_handler * handler);
/* For statistics / monitoring: get the number of struct session in memory */
int fd_sess_getcount(uint32_t *cnt);
/*============================================================*/
/* ROUTING */
/*============================================================*/
/* The following functions are helpers for the routing module.
The routing data is stored in the message itself. */
/* Structure that contains the routing data for a message */
struct rt_data;
/* Following functions are helpers to create the routing data of a message */
int fd_rtd_init(struct rt_data ** rtd);
void fd_rtd_free(struct rt_data ** rtd);
/* Add a peer to the candidates list. */
int fd_rtd_candidate_add(struct rt_data * rtd, DiamId_t peerid, size_t peeridlen, DiamId_t realm, size_t realmlen);
/* Remove a peer from the candidates (if it is found). The search is case-insensitive. */
void fd_rtd_candidate_del(struct rt_data * rtd, uint8_t * id, size_t idsz);
/* Extract the list of valid candidates, and initialize their scores to 0 */
void fd_rtd_candidate_extract(struct rt_data * rtd, struct fd_list ** candidates, int ini_score);
/* If a peer returned a protocol error for this message, save it so that we don't try to send it there again. Optionally retrieve the current list of candidates. */
int fd_rtd_error_add(struct rt_data * rtd, DiamId_t sentto, size_t senttolen, uint8_t * origin, size_t originsz, uint32_t rcode, struct fd_list ** candidates, int * sendingattemtps);
/* Only retrieve the number of times this message has been processed by the routing-out mechanism (i.e. number of times it was failed over) */
int fd_rtd_get_nb_attempts(struct rt_data * rtd, int * sendingattemtps);
/* The extracted list items have the following structure: */
struct rtd_candidate {
struct fd_list chain; /* link in the list returned by the previous fcts */
DiamId_t diamid; /* the diameter Id of the peer */
size_t diamidlen; /* cached size of the diamid string */
DiamId_t realm; /* the diameter realm of the peer */
size_t realmlen; /* cached size of realm */
int score; /* the current routing score for this peer, see fd_rt_out_register definition for details */
};
/* Reorder the list of peers by score */
int fd_rtd_candidate_reorder(struct fd_list * candidates);
/* Note : it is fine for a callback to add a new entry in the candidates list after the list has been extracted. The diamid must then be malloc'd. */
/* Beware that this could lead to routing loops */
/*============================================================*/
/* MESSAGES */
/*============================================================*/
/* The following types are opaque */
struct msg; /* A message: command with children AVPs (possibly grand children) */
struct avp; /* AVP object */
/* Some details about chaining:
*
* A message is made of a header ( msg ) and 0 or more AVPs ( avp ).
* The structure is a kind of tree, where some AVPs (grouped AVPs) can contain other AVPs.
* Example:
* msg
* |-avp
* |-gavp
* | |-avp
* | |-avp
* | \-avp
* |-avp
* \-avp
*
*/
/* The following type is used to point to either a msg or an AVP */
typedef void msg_or_avp;
/* The Diameter protocol version */
#define DIAMETER_VERSION 1
/* In the two following types, some fields are marked (READONLY).
* This means that the content of these fields will be overwritten by the daemon so modifying it is useless.
*/
/* The following structure represents the header of a message. All data is in host byte order. */
struct msg_hdr {
uint8_t msg_version; /* (READONLY) Version of Diameter: must be DIAMETER_VERSION. */
uint32_t msg_length; /* (READONLY)(3 bytes) indicates the length of the message */
uint8_t msg_flags; /* Message flags: CMD_FLAG_* */
command_code_t msg_code; /* (3 bytes) the command-code. See dictionary-api.h for more detail */
application_id_t msg_appl; /* The application issuing this message */
uint32_t msg_hbhid; /* The Hop-by-Hop identifier of the message */
uint32_t msg_eteid; /* The End-to-End identifier of the message */
};
/* The following structure represents the visible content of an AVP. All data is in host byte order. */
struct avp_hdr {
avp_code_t avp_code; /* the AVP Code */
uint8_t avp_flags; /* AVP_FLAG_* flags */
uint32_t avp_len; /* (READONLY)(Only 3 bytes are used) the length of the AVP as described in the RFC */
vendor_id_t avp_vendor; /* Only used if AVP_FLAG_VENDOR is present */
union avp_value *avp_value; /* pointer to the value of the AVP. NULL means that the value is not set / not understood.
One should not directly change this value. Use the msg_avp_setvalue function instead.
The content of the pointed structure can be changed directly, with this restriction:
if the AVP is an OctetString, and you change the value of the pointer avp_value->os.data, then
you must call free() on the previous value, and the new one must be free()-able.
*/
};
/* The following enum is used to browse inside message hierarchy (msg, gavp, avp) */
enum msg_brw_dir {
MSG_BRW_NEXT = 1, /* Get the next element at the same level, or NULL if this is the last element. */
MSG_BRW_PREV, /* Get the previous element at the same level, or NULL if this is the first element. */
MSG_BRW_FIRST_CHILD, /* Get the first child AVP of this element, if any. */
MSG_BRW_LAST_CHILD, /* Get the last child AVP of this element, if any. */
MSG_BRW_PARENT, /* Get the parent element of this element, if any. Only the msg_t object has no parent. */
MSG_BRW_WALK /* This is equivalent to FIRST_CHILD or NEXT or PARENT->next, first that is not NULL. Use this to walk inside all AVPs. */
};
/* Some flags used in the functions below */
#define AVPFL_SET_BLANK_VALUE 0x01 /* When creating an AVP, initialize its value to a blank area */
#define AVPFL_SET_RAWDATA_FROM_AVP 0x02 /* When creating an AVP, initialize its rawdata area from an existing AVP -- it is only blank padding (for error reporting) */
#define AVPFL_MAX AVPFL_SET_RAWDATA_FROM_AVP /* The biggest valid flag value */
#define MSGFL_ALLOC_ETEID 0x01 /* When creating a message, a new end-to-end ID is allocated and set in the message */
#define MSGFL_ANSW_ERROR 0x02 /* When creating an answer message, set the 'E' bit and use the generic error ABNF instead of command-specific ABNF */
#define MSGFL_ANSW_NOSID 0x04 /* When creating an answer message, do not add the Session-Id even if present in request */
#define MSGFL_ANSW_NOPROXYINFO 0x08 /* When creating an answer message, do not add the Proxy-Info AVPs presents in request */
#define MSGFL_MAX MSGFL_ANSW_NOPROXYINFO /* The biggest valid flag value */
/**************************************************/
/* Message creation, manipulation, disposal */
/**************************************************/
/*
* FUNCTION: fd_msg_avp_new
*
* PARAMETERS:
* model : Pointer to a DICT_AVP dictionary object describing the avp to create, or NULL if flags are used.
* flags : Flags to use in creation (AVPFL_*, see above).
* avp : Upon success, pointer to the new avp is stored here. It points to reference AVP upon function call when flags are used.
*
* DESCRIPTION:
* Create a new AVP instance.
*
* RETURN VALUE:
* 0 : The AVP is created.
* EINVAL : A parameter is invalid.
* (other standard errors may be returned, too, with their standard meaning. Example:
* ENOMEM : Memory allocation for the new avp failed.)
*/
int fd_msg_avp_new ( struct dict_object * model, int flags, struct avp ** avp );
/*
* FUNCTION: fd_msg_new
*
* PARAMETERS:
* model : Pointer to a DICT_COMMAND dictionary object describing the message to create, or NULL.
* flags : combination of MSGFL_* flags.
* msg : Upon success, pointer to the new message is stored here.
*
* DESCRIPTION:
* Create a new empty Diameter message.
*
* RETURN VALUE:
* 0 : The message is created.
* EINVAL : A parameter is invalid.
* (other standard errors may be returned, too, with their standard meaning. Example:
* ENOMEM : Memory allocation for the new message failed.)
*/
int fd_msg_new ( struct dict_object * model, int flags, struct msg ** msg );
/*
* FUNCTION: msg_new_answer_from_req
*
* PARAMETERS:
* dict : Pointer to the dictionary containing the model of the query.
* msg : The location of the query on function call. Updated by the location of answer message on return.
* flag : Pass MSGFL_ANSW_ERROR to indicate if the answer is an error message (will set the 'E' bit)
* : See other MSGFL_ANSW_* definition above for other flags.
*
* DESCRIPTION:
* This function creates the empty answer message corresponding to a request.
* The header is set properly (R flag, ccode, appid, hbhid, eteid)
* The Session-Id AVP is copied if present.
* The calling code should usually call fd_msg_rescode_set function on the answer.
* Upon return, the original query may be retrieved by calling fd_msg_answ_getq on the message.
*
* RETURN VALUE:
* 0 : Operation complete.
* !0 : an error occurred.
*/
int fd_msg_new_answer_from_req ( struct dictionary * dict, struct msg ** msg, int flag );
/*
* FUNCTION: fd_msg_browse
*
* PARAMETERS:
* reference : Pointer to a struct msg or struct avp.
* dir : Direction for browsing
* found : If not NULL, updated with the element that has been found, if any, or NULL if no element was found / an error occurred.
* depth : If not NULL, points to an integer representing the "depth" of this object in the tree. This is a relative value, updated on return.
*
* DESCRIPTION:
* Explore the content of a message object (hierarchy). If "found" is null, only error checking is performed.
* If "depth" is provided, it is updated as follow on successful function return:
* - not modified for MSG_BRW_NEXT and MSG_BRW_PREV.
* - *depth = *depth + 1 for MSG_BRW_FIRST_CHILD and MSG_BRW_LAST_CHILD.
* - *depth = *depth - 1 for MSG_BRW_PARENT.
* - *depth = *depth + X for MSG_BRW_WALK, with X between 1 (returned the 1st child) and -N (returned the Nth parent's next).
*
* RETURN VALUE:
* 0 : found has been updated (if non NULL).
* EINVAL : A parameter is invalid.
* ENOENT : No element has been found where requested, and "found" was NULL (otherwise, *found is set to NULL and 0 is returned).
*/
int fd_msg_browse_internal ( msg_or_avp * reference, enum msg_brw_dir dir, msg_or_avp ** found, int * depth );
/* Macro to avoid having to cast the third parameter everywhere */
#define fd_msg_browse( ref, dir, found, depth ) \
fd_msg_browse_internal( (ref), (dir), (void *)(found), (depth) )
/*
* FUNCTION: fd_msg_avp_add
*
* PARAMETERS:
* reference : Pointer to a valid msg or avp.
* dir : location where the new AVP should be inserted, relative to the reference. MSG_BRW_PARENT and MSG_BRW_WALK are not valid.
* avp : pointer to the AVP object that must be inserted.
*
* DESCRIPTION:
* Adds an AVP into an object that can contain it: grouped AVP or message.
* Note that the added AVP will be freed at the same time as the object it is added to,
* so it should not be freed after the call to this function.
*
* RETURN VALUE:
* 0 : The AVP has been added.
* EINVAL : A parameter is invalid.
*/
int fd_msg_avp_add ( msg_or_avp * reference, enum msg_brw_dir dir, struct avp *avp);
/*
* FUNCTION: fd_msg_search_avp
*
* PARAMETERS:
* msg : The message structure in which to search the AVP.
* what : The dictionary model of the AVP to search.
* avp : location where the AVP reference is stored if found.
*
* DESCRIPTION:
* Search the first top-level AVP of a given model inside a message.
* Note: only the first instance of the AVP is returned by this function.
* Note: only top-level AVPs are searched, not inside grouped AVPs.
* Use msg_browse if you need more advanced research features.
*
* RETURN VALUE:
* 0 : The AVP has been found.
* EINVAL : A parameter is invalid.
* ENOENT : No AVP has been found, and "avp" was NULL (otherwise, *avp is set to NULL and 0 returned).
*/
int fd_msg_search_avp ( struct msg * msg, struct dict_object * what, struct avp ** avp );
/*
* FUNCTION: fd_msg_free
*
* PARAMETERS:
* object : pointer to the message or AVP object that must be unlinked and freed.
*
* DESCRIPTION:
* Unlink and free a message or AVP object and its children.
* If the object is an AVP linked into a message, the AVP is removed before being freed.
*
* RETURN VALUE:
* 0 : The message has been freed.
* EINVAL : A parameter is invalid.
*/
int fd_msg_free ( msg_or_avp * object );
/***************************************/
/* Dump functions */
/***************************************/
/*
* FUNCTION: fd_msg_dump_*
*
* PARAMETERS:
* see definition of DECLARE_FD_DUMP_PROTOTYPE,
* obj : A msg or avp object to dump.
* dict : the dictionary to use if parsing is requested (optional)
* force_parsing: by default these functions do not parse the object but dump hexa values in that case.
* use !0 to force parsing. If parsing fails, the hexa dump is still provided.
* recurse : allow the function to go through the children objects if any to dump more information. might require parsing.
*
* DESCRIPTION:
* These functions dump the content of a message or avp into a buffer
* either recursively or only the object itself.
*
* RETURN VALUE:
* - see DECLARE_FD_DUMP_PROTOTYPE,
*/
/* one-line dump with only short information */
DECLARE_FD_DUMP_PROTOTYPE( fd_msg_dump_summary, msg_or_avp *obj, struct dictionary *dict, int force_parsing, int recurse );
/* one-line dump with all the contents of the message */
DECLARE_FD_DUMP_PROTOTYPE( fd_msg_dump_full, msg_or_avp *obj, struct dictionary *dict, int force_parsing, int recurse );
/* multi-line human-readable dump similar to wireshark output */
DECLARE_FD_DUMP_PROTOTYPE( fd_msg_dump_treeview, msg_or_avp *obj, struct dictionary *dict, int force_parsing, int recurse );
/*********************************************/
/* Message metadata management functions */
/*********************************************/
/*
* FUNCTION: fd_msg_model
*
* PARAMETERS:
* reference : Pointer to a valid msg or avp.
* model : on success, pointer to the dictionary model of this command or AVP. NULL if the model is unknown.
*
* DESCRIPTION:
* Retrieve the dictionary object describing this message or avp. If the object is unknown or the fd_msg_parse_dict has not been called,
* *model is set to NULL.
*
* RETURN VALUE:
* 0 : The model has been set.
* EINVAL : A parameter is invalid.
*/
int fd_msg_model ( msg_or_avp * reference, struct dict_object ** model );
/*
* FUNCTION: fd_msg_hdr
*
* PARAMETERS:
* msg : Pointer to a valid message object.
* pdata : Upon success, pointer to the msg_hdr structure of this message. The fields may be modified.
*
* DESCRIPTION:
* Retrieve location of modifiable section of a message.
*
* RETURN VALUE:
* 0 : The location has been written.
* EINVAL : A parameter is invalid.
*/
int fd_msg_hdr ( struct msg *msg, struct msg_hdr ** pdata );
/*
* FUNCTION: fd_msg_avp_hdr
*
* PARAMETERS:
* avp : Pointer to a valid avp object.
* pdata : Upon success, pointer to the avp_hdr structure of this avp. The fields may be modified.
*
* DESCRIPTION:
* Retrieve location of modifiable data of an avp.
*
* RETURN VALUE:
* 0 : The location has been written.
* EINVAL : A parameter is invalid.
*/
int fd_msg_avp_hdr ( struct avp *avp, struct avp_hdr ** pdata );
/*
* FUNCTION: fd_msg_answ_associate, fd_msg_answ_getq, fd_msg_answ_detach
*
* PARAMETERS:
* answer : the received answer message
* query : the corresponding query that had been sent
*
* DESCRIPTION:
* fd_msg_answ_associate associates a query msg with the received answer.
* Query is retrieved with fd_msg_answ_getq.
* If answer message is freed, the query is also freed.
* If the msg_answ_detach function is called, the association is removed.
* This is meant to be called from the daemon only.
*
* RETURN VALUE:
* 0 : ok
* EINVAL: a parameter is invalid
*/
int fd_msg_answ_associate( struct msg * answer, struct msg * query );
int fd_msg_answ_getq ( struct msg * answer, struct msg ** query );
int fd_msg_answ_detach ( struct msg * answer );
/*
* FUNCTION: fd_msg_anscb_associate, fd_msg_anscb_get
*
* PARAMETERS:
* msg : the request message
* anscb : the callback to associate with the message
* data : the data to pass to the callback
* expirecb : the expiration callback to associate with the message
* timeout : (optional, use NULL if no timeout) a timeout associated with calling the cb.
*
* DESCRIPTION:
* Associate or retrieve callbacks with an message.
* This is meant to be called from the daemon only.
*
* RETURN VALUE:
* 0 : ok
* EINVAL: a parameter is invalid
*/
int fd_msg_anscb_associate( struct msg * msg, void ( *anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout );
int fd_msg_anscb_get( struct msg * msg, void (**anscb)(void *, struct msg **), void (**expirecb)(void *, DiamId_t, size_t, struct msg **), void ** data );
int fd_msg_anscb_reset(struct msg * msg, int clear_anscb, int clear_expirecb);
struct timespec *fd_msg_anscb_gettimeout( struct msg * msg ); /* returns NULL or a valid non-0 timespec */
/*
* FUNCTION: fd_msg_rt_associate, fd_msg_rt_get
*
* PARAMETERS:
* msg : the query message to be sent
* list : the ordered list of possible next-peers
*
* DESCRIPTION:
* Associate a routing list with a query, and retrieve it.
* If the message is freed, the list is also freed.
*
* RETURN VALUE:
* 0 : ok
* EINVAL: a parameter is invalid
*/
int fd_msg_rt_associate( struct msg * msg, struct rt_data * rtd );
int fd_msg_rt_get ( struct msg * msg, struct rt_data ** rtd );
/*
* FUNCTION: fd_msg_is_routable
*
* PARAMETERS:
* msg : A msg object.
*
* DESCRIPTION:
* This function returns a boolean telling if a given message is routable in the Diameter network,
* or if it is a local link message only (ex: CER/CEA, DWR/DWA, ...).
*
* RETURN VALUE:
* 0 : The message is not routable / an error occurred.
* 1 : The message is routable.
*/
int fd_msg_is_routable ( struct msg * msg );
/*
* FUNCTION: fd_msg_source_(g/s)et
*
* PARAMETERS:
* msg : A msg object.
* diamid,len : The diameter id of the peer from which this message was received.
* dict : a dictionary with definition of Route-Record AVP (for fd_msg_source_setrr)
*
* DESCRIPTION:
* Store or retrieve the diameted id of the peer from which this message was received.
* Will be used for example by the routing module to add the Route-Record AVP in forwarded requests,
* or to direct answers to the appropriate peer.
*
* RETURN VALUE:
* 0 : Operation complete.
* !0 : an error occurred.
*/
int fd_msg_source_set( struct msg * msg, DiamId_t diamid, size_t diamidlen );
int fd_msg_source_setrr( struct msg * msg, DiamId_t diamid, size_t diamidlen, struct dictionary * dict );
int fd_msg_source_get( struct msg * msg, DiamId_t *diamid, size_t * diamidlen );
/*
* FUNCTION: fd_msg_eteid_get
*
* PARAMETERS:
* -
*
* DESCRIPTION:
* Get a new unique end-to-end id value for the local peer.
*
* RETURN VALUE:
* The new assigned value. No error code is defined.
*/
uint32_t fd_msg_eteid_get ( void );
/*
* FUNCTION: fd_msg_sess_get
*
* PARAMETERS:
* dict : the dictionary that contains the Session-Id AVP definition
* msg : A valid message.
* session : Location to store the session pointer when retrieved.
* isnew : Indicates if the session has been created.
*
* DESCRIPTION:
* This function retrieves or creates the session object corresponding to a message.
* If the message does not contain a Session-Id AVP, *session == NULL on return.
* Note that the Session-Id AVP must never be modified after created in a message.
*
* RETURN VALUE:
* 0 : success
* !0 : standard error code.
*/
int fd_msg_sess_get(struct dictionary * dict, struct msg * msg, struct session ** session, int * isnew);
/* This one is used by the libfdcore, you should use fd_msg_new_session rather than fd_sess_new, when possible */
int fd_msg_sess_set(struct msg * msg, struct session * session);
/* Helper for the hooks mechanism, for use from libfdcore */
struct fd_msg_pmdl {
struct fd_list sentinel; /* if the sentinel.o field is NULL, the structure is not initialized. Otherwise it points to the cleanup function in libfdcore. */
pthread_mutex_t lock;
};
struct fd_msg_pmdl * fd_msg_pmdl_get(struct msg * msg);
/***************************************/
/* Manage AVP values */
/***************************************/
/*
* FUNCTION: fd_msg_avp_setvalue
*
* PARAMETERS:
* avp : Pointer to a valid avp object with a NULL avp_value pointer. The model must be known.
* value : pointer to an avp_value. The content will be COPIED into the internal storage area.
* If data type is an octetstring, the data is also copied.
* If value is a NULL pointer, the previous data is erased and value is unset in the AVP.
*
* DESCRIPTION:
* Initialize the avp_value field of an AVP header.
*
* RETURN VALUE:
* 0 : The avp_value pointer has been set.
* EINVAL : A parameter is invalid.
*/
int fd_msg_avp_setvalue ( struct avp *avp, union avp_value *value );
/*
* FUNCTION: fd_msg_avp_value_encode
*
* PARAMETERS:
* avp : Pointer to a valid avp object with a NULL avp_value. The model must be known.
* data : Pointer to the data that must be encoded as AVP value and stored in the AVP.
* This is only valid for AVPs of derived type for which type_data_encode callback is set. (ex: Address type)
*
* DESCRIPTION:
* Initialize the avp_value field of an AVP object from formatted data, using the AVP's type "type_data_encode" callback.
*
* RETURN VALUE:
* 0 : The avp_value has been set.
* EINVAL : A parameter is invalid.
* ENOTSUP : There is no appropriate callback registered with this AVP's type.
*/
int fd_msg_avp_value_encode ( void *data, struct avp *avp );
/*
* FUNCTION: fd_msg_avp_value_interpret
*
* PARAMETERS:
* avp : Pointer to a valid avp object with a non-NULL avp_value value.
* data : Upon success, formatted interpretation of the AVP value is stored here.
*
* DESCRIPTION:
* Interpret the content of an AVP of Derived type and store the result in data pointer. The structure
* of the data pointer is dependent on the AVP type. This function calls the "type_data_interpret" callback
* of the type.
*
* RETURN VALUE:
* 0 : The avp_value has been set.
* EINVAL : A parameter is invalid.
* ENOTSUP : There is no appropriate callback registered with this AVP's type.
*/
int fd_msg_avp_value_interpret ( struct avp *avp, void *data );
/***************************************/
/* Message parsing functions */
/***************************************/
/*
* FUNCTION: fd_msg_bufferize
*
* PARAMETERS:
* msg : A valid msg object. All AVPs must have a value set.
* buffer : Upon success, this points to a buffer (malloc'd) containing the message ready for network transmission (or security transformations).
* The buffer may be freed after use.
* len : if not NULL, the size of the buffer is written here. In any case, this size is updated in the msg header.
*
* DESCRIPTION:
* Renders a message in memory as a buffer that can be sent over the network to the next peer.
*
* RETURN VALUE:
* 0 : The location has been written.
* EINVAL : The buffer does not contain a valid Diameter message.
* ENOMEM : Unable to allocate enough memory to create the buffer object.
*/
int fd_msg_bufferize ( struct msg * msg, uint8_t ** buffer, size_t * len );
/*
* FUNCTION: fd_msg_parse_buffer
*
* PARAMETERS:
* buffer : Pointer to a buffer containing a message received from the network.
* buflen : the size in bytes of the buffer.
* msg : Upon success, this points to a valid msg object. No AVP value is resolved in this object, nor grouped AVP.
*
* DESCRIPTION:
* This function parses a buffer an creates a msg object to represent the structure of the message.
* Since no dictionary lookup is performed, the values of the AVPs are not interpreted. To interpret the values,
* the returned message object must be passed to fd_msg_parse_dict function.
* The buffer pointer is saved inside the message and will be freed when not needed anymore.
*
* RETURN VALUE:
* 0 : The location has been written.
* ENOMEM : Unable to allocate enough memory to create the msg object.
* EBADMSG : The buffer does not contain a valid Diameter message (or is truncated).
* EINVAL : A parameter is invalid.
*/
int fd_msg_parse_buffer ( uint8_t ** buffer, size_t buflen, struct msg ** msg );
/* Parsing Error Information structure */
struct fd_pei {
char * pei_errcode; /* name of the error code to use */
struct avp * pei_avp; /* pointer to invalid (in original message) or missing AVP (to be freed) */
int pei_avp_free; /* Set to 1 if the pei_avp must be freed */
char * pei_message; /* Overwrite default message if needed */
int pei_protoerr; /* do we set the 'E' bit in the error message ? */
};
/*
* FUNCTION: fd_msg_parse_dict
*
* PARAMETERS:
* object : A msg or AVP object as returned by fd_msg_parse_buffer.
* dict : the dictionary containing the objects definitions to use for resolving all AVPs.
* error_info : If not NULL, will contain the detail about error upon return. May be used to generate an error reply.
*
* DESCRIPTION:
* This function looks up for the command and each children AVP definitions in the dictionary.
* If the dictionary definition is found, avp_model is set and the value of the AVP is interpreted accordingly and:
* - for grouped AVPs, the children AVP are created and interpreted also.
* - for numerical AVPs, the value is converted to host byte order and saved in the avp_value field.
* - for octetstring AVPs, the string is copied into a new buffer and its address is saved in avp_value.
* If the dictionary definition is not found, avp_model is set to NULL and
* the content of the AVP is saved as an octetstring in an internal structure. avp_value is NULL.
* As a result, after this function has been called, there is no more dependency of the msg object to the message buffer, that is freed.
*
* RETURN VALUE:
* 0 : The message has been fully parsed as described.
* EINVAL : The msg parameter is invalid for this operation.
* ENOMEM : Unable to allocate enough memory to complete the operation.
* ENOTSUP : No dictionary definition for the command or one of the mandatory AVP was found.
*/
int fd_msg_parse_dict ( msg_or_avp * object, struct dictionary * dict, struct fd_pei * error_info );
/*
* FUNCTION: fd_msg_parse_rules
*
* PARAMETERS:
* object : A msg or grouped avp object that must be verified.
* dict : The dictionary containing the rules definitions.
* error_info : If not NULL, the first problem information will be saved here.
*
* DESCRIPTION:
* Check that the children of the object do not conflict with the dictionary rules (ABNF compliance).
*
* RETURN VALUE:
* 0 : The message has been fully parsed and complies to the defined rules.
* EBADMSG : A conflict was detected, or a mandatory AVP is unknown in the dictionary.
* EINVAL : The msg or avp object is invalid for this operation.
* ENOMEM : Unable to allocate enough memory to complete the operation.
*/
int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct fd_pei * error_info);
/*
* FUNCTION: fd_msg_update_length
*
* PARAMETERS:
* object : Pointer to a valid msg or avp.
*
* DESCRIPTION:
* Update the length field of the object passed as parameter.
* As a side effect, all children objects are also updated. Therefore, all avp_value fields of
* the children AVPs must be set, or an error will occur.
*
* RETURN VALUE:
* 0 : The size has been recomputed.
* EINVAL : A parameter is invalid.
*/
int fd_msg_update_length ( msg_or_avp * object );
/*============================================================*/
/* DISPATCH */
/*============================================================*/
/* Dispatch module (passing incoming messages to extensions registered callbacks)
* is split between the library and the daemon.
*
* The library provides the support for associating dispatch callbacks with
* dictionary objects.
*
* The daemon is responsible for calling the callbacks for a message when appropriate.
*
*
* The dispatch module has two main roles:
* - help determine if a message can be handled locally (during the routing step)
* This decision involves only the application-id of the message.
* - pass the message to the callback(s) that will handle it (during the dispatch step)
*
* The first role is handled by the daemon.
*
* About the second, these are the possibilities for registering a dispatch callback:
*
* -> For All messages.
* This callback is called for all messages that are handled locally. This should be used only
* for debug purpose.
*
* -> by AVP value (constants only).
* This callback will be called when a message is received and contains an AVP with a specified enumerated value.
*
* -> by AVP.
* This callback will be called when the received message contains a certain AVP.
*
* -> by command-code.
* This callback will be called when the message is a specific command (and 'R' flag).
*
* -> by application.
* This callback will be called when the message has a specific application-id.
*
* ( by vendor: would this be useful? it may be added later)
*/
enum disp_how {
DISP_HOW_ANY = 1, /* Any message. This should be only used for debug. */
DISP_HOW_APPID, /* Any message with the specified application-id */
DISP_HOW_CC, /* Messages of the specified command-code (request or answer). App id may be specified. */
DISP_HOW_AVP, /* Messages containing a specific AVP. Command-code and App id may be specified. */
DISP_HOW_AVP_ENUMVAL /* Messages containing a specific AVP with a specific enumerated value. Command-code and App id may be specified. */
};
/*
* Several criteria may be selected at the same time, for example command-code AND application id.
*
* If several callbacks are registered for the same object, they are called in the order they were registered.
* The order in which the callbacks are called is:
* DISP_HOW_ANY
* DISP_HOW_AVP_ENUMVAL & DISP_HOW_AVP
* DISP_HOW_CC
* DISP_HOW_APPID
*/
/* When a callback is registered, a "when" argument is passed in addition to the disp_how value,
* to specify which values the criteria must match. */
struct disp_when {
struct dict_object * app;
struct dict_object * command;
struct dict_object * avp;
struct dict_object * value;
};
/* Note that all the dictionary objects should really belong to the same dictionary!
*
* Here is the details on this "when" argument, depending on the disp_how value.
*
* DISP_HOW_ANY.
* In this case, "when" must be NULL.
*
* DISP_HOW_APPID.
* Only the "app_id" field must be set, other fields are ignored. It points to a dictionary object of type DICT_APPLICATION.
*
* DISP_HOW_CC.
* The "command" field must be defined and point to a dictionary object of type DICT_COMMAND.
* The "app_id" may be also set. In the case it is set, it restricts the callback to be called only with this command-code and app id.
* The other fields are ignored.
*
* DISP_HOW_AVP.
* The "avp" field of the structure must be set and point to a dictionary object of type DICT_AVP.
* The "app_id" field may be set to restrict the messages matching to a specific app id.
* The "command" field may also be set to a valid DICT_COMMAND object.
* The content of the "value" field is ignored.
*
* DISP_HOW_AVP_ENUMVAL.
* All fields have the same constraints and meaning as in DISP_REG_AVP. In addition, the "value" field must be set
* and points to a valid DICT_ENUMVAL object.
*
* Here is a sumary of the fields: ( M : must be set; m : may be set; 0 : ignored )
* field: app_id command avp value
* APPID : M 0 0 0
* CC : m M 0 0
* AVP : m m M 0
* ENUMVA: m m M M
*/
enum disp_action {
DISP_ACT_CONT, /* The next handler should be called, unless *msg == NULL. */
DISP_ACT_SEND, /* The updated message must be sent. No further callback is called. */
DISP_ACT_ERROR /* An error must be created and sent as a reply -- not valid for callbacks, only for fd_msg_dispatch. */
};
/* The callbacks that are registered have the following prototype:
* int dispatch_callback( struct msg ** msg, struct avp * avp, struct session * session, enum disp_action * action );
*
* CALLBACK: dispatch_callback
*
* PARAMETERS:
* msg : the received message on function entry. may be updated to answer on return (see description)
* avp : for callbacks registered with DISP_HOW_AVP or DISP_HOW_AVP_ENUMVAL, direct link to the triggering AVP.
* session : if the message contains a Session-Id AVP, the corresponding session object, NULL otherwise.
* opaque : An opaque pointer that is registered along the session handler.
* action : upon return, this tells the daemon what to do next.
*
* DESCRIPTION:
* Called when a received message matchs the condition for which the callback was registered.
* This callback may do any kind of processing on the message, including:
* - create an answer for a request.
* - proxy a request or message, add / remove the Proxy-Info AVP, then forward the message.
* - update a routing table or start a connection with a new peer, then forward the message.
* - ...
*
* When *action == DISP_ACT_SEND on callback return, the msg pointed by *msg is passed to the routing module for sending.
* When *action == DISP_ACT_CONT, the next registered callback is called.
* When the last callback gives also DISP_ACT_CONT action value, a default handler is called. It's behavior is as follow:
* - if the message is an answer, it is discarded.
* - if the message is a request, it is passed again to the routing stack, and marked as non-local handling.
*
* RETURN VALUE:
* 0 : The callback executed successfully and updated *action appropriately.
* !0 : standard errors. In case of error, the message is discarded.
*/
/* This structure represents a handler for a registered callback, allowing its de-registration */
struct disp_hdl;
/*
* FUNCTION: fd_disp_register
*
* PARAMETERS:
* cb : The callback function to register (see dispatch_callback description above).
* how : How the callback must be registered.
* when : Values that must match, depending on the how argument.
* opaque : A pointer that is passed back to the handler. The content is not interpreted by the framework.
* handle : On success, a handler to the registered callback is stored here if not NULL.
* This handler can be used to unregister the cb.
*
* DESCRIPTION:
* Register a new callback to handle messages delivered locally.
*
* RETURN VALUE:
* 0 : The callback is registered.
* EINVAL : A parameter is invalid.
* ENOMEM : Not enough memory to complete the operation
*/
int fd_disp_register ( int (*cb)( struct msg **, struct avp *, struct session *, void *, enum disp_action *),
enum disp_how how, struct disp_when * when, void * opaque, struct disp_hdl ** handle );
/*
* FUNCTION: fd_disp_unregister
*
* PARAMETERS:
* handle : Location of the handle of the callback that must be unregistered.
* opaque : If not NULL, the opaque data that was registered is restored here.
*
* DESCRIPTION:
* Removes a callback previously registered by fd_disp_register.
*
* RETURN VALUE:
* 0 : The callback is unregistered.
* EINVAL : A parameter is invalid.
*/
int fd_disp_unregister ( struct disp_hdl ** handle, void ** opaque );
/* Destroy all handlers */
void fd_disp_unregister_all ( void );
/*
* FUNCTION: fd_msg_dispatch
*
* PARAMETERS:
* msg : A msg object that have already been fd_msg_parse_dict.
* session : The session corresponding to this object, if any.
* action : Upon return, the action that must be taken on the message
* error_code : Upon return with action == DISP_ACT_ERROR, contains the error (such as "DIAMETER_UNABLE_TO_COMPLY")
* drop_reason : if set on return, the message must be freed for this reason.
* drop_msg : if drop_reason is set, this points to the message to be freed while *msg is NULL.
*
* DESCRIPTION:
* Call all handlers registered for a given message.
* The session must have already been resolved on entry.
* The msg pointed may be updated during this process.
* Upon return, the action parameter points to what must be done next.
*
* RETURN VALUE:
* 0 : Success.
* EINVAL : A parameter is invalid.
* (other errors)
*/
int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, char ** error_code, char ** drop_reason, struct msg ** drop_msg );
/*============================================================*/
/* QUEUES */
/*============================================================*/
/* Management of FIFO queues of elements */
/* A queue is an opaque object */
struct fifo;
/*
* FUNCTION: fd_fifo_new
*
* PARAMETERS:
* queue : Upon success, a pointer to the new queue is saved here.
* max : max number of items in the queue. Above this number, adding a new item becomes a
* blocking operation. Use 0 to disable this maximum.
*
* DESCRIPTION:
* Create a new empty queue.
*
* RETURN VALUE :
* 0 : The queue has been initialized successfully.
* EINVAL : The parameter is invalid.
* ENOMEM : Not enough memory to complete the creation.
*/
int fd_fifo_new ( struct fifo ** queue, int max );
/*
* FUNCTION: fd_fifo_del
*
* PARAMETERS:
* queue : Pointer to an empty queue to delete.
*
* DESCRIPTION:
* Destroys a queue. This is only possible if no thread is waiting for an element,
* and the queue is empty.
*
* RETURN VALUE:
* 0 : The queue has been destroyed successfully.
* EINVAL : The parameter is invalid.
*/
int fd_fifo_del ( struct fifo ** queue );
/*
* FUNCTION: fd_fifo_move
*
* PARAMETERS:
* oldq : Location of a FIFO that is to be emptied.
* newq : A FIFO that will receive the old data.
* loc_update : if non NULL, a place to store the pointer to new FIFO atomically with the move.
*
* DESCRIPTION:
* Empties a queue and move its content to another one atomically.
*
* RETURN VALUE:
* 0 : The queue has been destroyed successfully.
* EINVAL : A parameter is invalid.
*/
int fd_fifo_move ( struct fifo * oldq, struct fifo * newq, struct fifo ** loc_update );
/*
* FUNCTION: fd_fifo_getstats
*
* PARAMETERS:
* queue : The queue from which to retrieve the information.
* current_count : How many items in the queue at the time of execution. This changes each time an item is pushed or poped.
* limit_count : The maximum number of items allowed in this queue. This is specified during queue creation.
* highest_count : The maximum number of items this queue has contained. This enables to see if limit_count count was reached.
* total_count : the total number of items that went through the queue (already pop'd). Always increasing.
* total : Cumulated time all items spent in this queue, including blocking time (always growing, use deltas for monitoring)
* blocking : Cumulated time threads trying to post new items were blocked (queue full).
* last : For the last element retrieved from the queue, how long it take between posting (including blocking) and poping
*
* DESCRIPTION:
* Retrieve the timing information associated with a queue, for monitoring purpose.
*
* RETURN VALUE:
* 0 : The statistics have been updated.
* EINVAL : A parameter is invalid.
*/
int fd_fifo_getstats( struct fifo * queue, int * current_count, int * limit_count, int * highest_count, long long * total_count,
struct timespec * total, struct timespec * blocking, struct timespec * last);
/*
* FUNCTION: fd_fifo_length
*
* PARAMETERS:
* queue : The queue from which to retrieve the number of elements.
*
* DESCRIPTION:
* Retrieve the number of elements in a queue, without error checking.
*
* RETURN VALUE:
* The number of items currently queued.
*/
int fd_fifo_length ( struct fifo * queue );
/*
* FUNCTION: fd_fifo_setthrhd
*
* PARAMETERS:
* queue : The queue for which the thresholds are being set.
* data : An opaque pointer that is passed to h_cb and l_cb callbacks.
* high : The high-level threshold. If the number of elements in the queue increase to this value, h_cb is called.
* h_cb : if not NULL, a callback to call when the queue lengh is bigger than "high".
* low : The low-level threshold. Must be < high.
* l_cb : If the number of elements decrease to low, this callback is called.
*
* DESCRIPTION:
* This function allows to adjust the number of producer / consumer threads of a queue.
* If the consumer are slower than the producers, the number of elements in the queue increase.
* By setting a "high" value, we allow a callback to be called when this number is too high.
* The typical use would be to create an additional consumer thread in this callback.
* If the queue continues to grow, the callback will be called again when the length is 2 * high, then 3*high, ... N * high
* (the callback itself should implement a limit on the number of consumers that can be created)
* When the queue starts to decrease, and the number of elements go under ((N - 1) * high + low, the l_cb callback is called
* and would typially stop one of the consumer threads. If the queue continues to reduce, l_cb is again called at (N-2)*high + low,
* and so on.
*
* Since there is no destructor for the data pointer, if cleanup operations are required, they should be performed in
* l_cb when the length of the queue is becoming < low.
*
* Note that the callbacks are called synchronously, during fd_fifo_post or fd_fifo_get. Their operation should be quick.
*
* RETURN VALUE:
* 0 : The thresholds have been set
* EINVAL : A parameter is invalid.
*/
int fd_fifo_setthrhd ( struct fifo * queue, void * data, uint16_t high, void (*h_cb)(struct fifo *, void **), uint16_t low, void (*l_cb)(struct fifo *, void **) );
/*
* FUNCTION: fd_fifo_post
*
* PARAMETERS:
* queue : The queue in which the element must be posted.
* item : The element that is put in the queue.
*
* DESCRIPTION:
* An element is added in a queue. Elements are retrieved from the queue in FIFO order
* with the fd_fifo_get, fd_fifo_tryget, or fd_fifo_timedget functions.
*
* RETURN VALUE:
* 0 : The element is queued.
* EINVAL : A parameter is invalid.
* ENOMEM : Not enough memory to complete the operation.
*/
int fd_fifo_post_int ( struct fifo * queue, void ** item );
#define fd_fifo_post(queue, item) \
fd_fifo_post_int((queue), (void *)(item))
/* Similar function but does not block. It can cause the number of items in the queue to exceed the maximum set. Do not use for normal operation,
only for failure recovery for example. */
int fd_fifo_post_noblock( struct fifo * queue, void ** item );
/*
* FUNCTION: fd_fifo_get
*
* PARAMETERS:
* queue : The queue from which the first element must be retrieved.
* item : On return, the first element of the queue is stored here.
*
* DESCRIPTION:
* This function retrieves the first element from a queue. If the queue is empty, the function will block the
* thread until a new element is posted to the queue, or until the thread is canceled (in which case the
* function does not return).
*
* RETURN VALUE:
* 0 : A new element has been retrieved.
* EINVAL : A parameter is invalid.
*/
int fd_fifo_get_int ( struct fifo * queue, void ** item );
#define fd_fifo_get(queue, item) \
fd_fifo_get_int((queue), (void *)(item))
/*
* FUNCTION: fd_fifo_tryget
*
* PARAMETERS:
* queue : The queue from which the element must be retrieved.
* item : On return, the first element of the queue is stored here.
*
* DESCRIPTION:
* This function is similar to fd_fifo_get, except that it will not block if
* the queue is empty, but return EWOULDBLOCK instead.
*
* RETURN VALUE:
* 0 : A new element has been retrieved.
* EINVAL : A parameter is invalid.
* EWOULDBLOCK : The queue was empty.
*/
int fd_fifo_tryget_int ( struct fifo * queue, void ** item );
#define fd_fifo_tryget(queue, item) \
fd_fifo_tryget_int((queue), (void *)(item))
/*
* FUNCTION: fd_fifo_timedget
*
* PARAMETERS:
* queue : The queue from which the element must be retrieved.
* item : On return, the element is stored here.
* abstime : the absolute time until which we allow waiting for an item.
*
* DESCRIPTION:
* This function is similar to fd_fifo_get, except that it will block if the queue is empty
* only until the absolute time abstime (see pthread_cond_timedwait for + info).
* If the queue is still empty when the time expires, the function returns ETIMEDOUT
*
* RETURN VALUE:
* 0 : A new item has been retrieved.
* EINVAL : A parameter is invalid.
* ETIMEDOUT : The time out has passed and no item has been received.
*/
int fd_fifo_timedget_int ( struct fifo * queue, void ** item, const struct timespec *abstime );
#define fd_fifo_timedget(queue, item, abstime) \
fd_fifo_timedget_int((queue), (void *)(item), (abstime))
/*
* FUNCTION: fd_fifo_select
*
* PARAMETERS:
* queue : The queue to test.
* abstime : the absolute time until which we can block waiting for an item. If NULL, the function returns immediatly.
*
* DESCRIPTION:
* This function is similar to select(), it waits for data to be available in the queue
* until the abstime is expired.
* Upon function entry, even if abstime is already expired the data availability is tested.
*
* RETURN VALUE:
* 0 : timeout expired without available data.
* <0 : An error occurred (e.g., -EINVAL...)
* >0 : data is available. The next call to fd_fifo_get will not block.
*/
int fd_fifo_select ( struct fifo * queue, const struct timespec *abstime );
/* Dump a fifo list and optionally its inner elements -- beware of deadlocks! */
typedef DECLARE_FD_DUMP_PROTOTYPE((*fd_fifo_dump_item_cb), void * item); /* This function should be 1 line if possible, or use indent level. Ends with '\n' */
DECLARE_FD_DUMP_PROTOTYPE(fd_fifo_dump, char * name, struct fifo * queue, fd_fifo_dump_item_cb dump_item);
#ifdef __cplusplus
}
#endif
#endif /* _LIBFDPROTO_H */
freeDiameter/include/freeDiameter/CMakeLists.txt 0000644 0001750 0001750 00000023542 13324704440 021250 0 ustar ruben ruben #CMake configuration for freeDiameter include directory
Project("freeDiameter includes directory" C)
########################
# Configurable parameters
# Disable SCTP support completely ?
OPTION(DISABLE_SCTP "Disable SCTP support?" OFF)
IF (NOT DISABLE_SCTP)
OPTION(DEBUG_SCTP "Verbose SCTP (for debug)?" OFF)
OPTION(SCTP_USE_MAPPED_ADDRESSES "Use v6-mapped v4 addresses in SCTP (workaround some SCTP limitations)?" OFF)
ENDIF (NOT DISABLE_SCTP)
# Find TODO items in the code easily ?
OPTION(ERRORS_ON_TODO "(development) Generate compilation errors on TODO items ?" OFF)
# In DEBUG mode, each log can contain pid, calling function and file for easy debug. Set to ON to display this information.
OPTION(DEBUG_WITH_META "Show calling location in logs?" OFF)
# Create the absolute path for searching extensions
SET(DEFAULT_EXTENSIONS_PATH ${CMAKE_INSTALL_PREFIX}/${INSTALL_EXTENSIONS_SUFFIX})
# IDNA considerations
OPTION(DIAMID_IDNA_IGNORE "Ignore completely invalid characters in Diameter Identities (process blindly)?" OFF)
IF (NOT DIAMID_IDNA_IGNORE)
OPTION (DIAMID_IDNA_REJECT "Reject internationalized Diameter Identities, do not attempt to convert it (stringprep) ?" OFF)
ENDIF (NOT DIAMID_IDNA_IGNORE)
# Disable expiration of connections with dynamically connected peers as per RFC 3539 ? (default is enabled)
# Note: if someone needs, we could also make the delay configurable here...
OPTION(DISABLE_PEER_EXPIRY "Disable RFC3539 Peers Connections Expiration after inactivity?" OFF)
# The following workaround increases compatibility with some implementations without breaking anything in freeDiameter,
# so it can be enabled without risk. We keep it disabled by default anyway for those people who use freeDiameter to check the
# compliancy of their implementation with the Diameter RFC...
OPTION(WORKAROUND_ACCEPT_INVALID_VSAI "Do not reject a CER/CEA with a Vendor-Specific-Application-Id AVP containing both Auth- and Acct- application AVPs?" OFF)
MARK_AS_ADVANCED(DISABLE_SCTP DEBUG_SCTP SCTP_USE_MAPPED_ADDRESSES ERRORS_ON_TODO DEBUG_WITH_META DIAMID_IDNA_IGNORE DIAMID_IDNA_REJECT DISABLE_PEER_EXPIRY WORKAROUND_ACCEPT_INVALID_VSAI)
########################
### System checks part
INCLUDE (CheckLibraryExists)
INCLUDE (CheckFunctionExists)
INCLUDE (CheckIncludeFiles)
INCLUDE (CheckSymbolExists)
INCLUDE (CheckCSourceCompiles)
INCLUDE (TestBigEndian)
### System checks -- mandatory support
# We need the getopt_long function
CHECK_FUNCTION_EXISTS (getopt_long HAVE_LONG_OPTIONS)
IF (NOT HAVE_LONG_OPTIONS)
MESSAGE(SEND_ERROR "The getopt_long function is not found, please add needed library in build system")
ENDIF (NOT HAVE_LONG_OPTIONS)
# getifaddrs ?
CHECK_FUNCTION_EXISTS (getifaddrs HAVE_GETIFADDRS)
IF (NOT HAVE_GETIFADDRS)
MESSAGE(SEND_ERROR "The getifaddrs function is currently required by freeDiameter.")
ENDIF (NOT HAVE_GETIFADDRS)
### System checks -- for freeDiameter-host.h
# Check byte ordering
TEST_BIG_ENDIAN(HOST_BIG_ENDIAN)
# Check if ntohll is provided on the system
CHECK_SYMBOL_EXISTS(ntohll netinet/in.h HAVE_NTOHLL)
# malloc.h ?
CHECK_INCLUDE_FILES (malloc.h HAVE_MALLOC_H)
# strndup ? Missing on OS X
CHECK_FUNCTION_EXISTS (strndup HAVE_STRNDUP)
### System checks -- for includes / link
# pthreads
INCLUDE(FindThreads)
SET(CMAKE_THREAD_LIBS_INIT ${CMAKE_THREAD_LIBS_INIT} PARENT_SCOPE)
# clock_gettime
SET(HAVE_CLOCK_GETTIME "")
CHECK_FUNCTION_EXISTS (clock_gettime HAVE_NATIVE_CLOCK_GETTIME)
IF (HAVE_NATIVE_CLOCK_GETTIME)
SET(CLOCK_GETTIME_LIBS "")
SET(HAVE_CLOCK_GETTIME 1)
ELSE (HAVE_NATIVE_CLOCK_GETTIME)
CHECK_LIBRARY_EXISTS (rt clock_gettime "" HAVE_LIBRT)
IF (HAVE_LIBRT)
SET(CLOCK_GETTIME_LIBS "-lrt")
SET(HAVE_CLOCK_GETTIME 1)
ELSE (HAVE_LIBRT)
CHECK_LIBRARY_EXISTS (posix4 clock_gettime "" HAVE_LIBPOSIX4)
IF (HAVE_LIBPOSIX4)
SET(CLOCK_GETTIME_LIBS "-lposix4")
SET(HAVE_CLOCK_GETTIME 1)
ENDIF (HAVE_LIBPOSIX4)
ENDIF (HAVE_LIBRT)
ENDIF (HAVE_NATIVE_CLOCK_GETTIME)
SET(CLOCK_GETTIME_LIBS ${CLOCK_GETTIME_LIBS} PARENT_SCOPE)
# dlopen and dlclose: CMAKE_DL_LIBS
# We need the sctp_connectx function among others
# We need the IPPROTO_SCTP symbol from sys/socket.h, netinet/in.h or netinet/sctp.h
IF(NOT DISABLE_SCTP)
CHECK_FUNCTION_EXISTS(sctp_connectx HAVE_NATIVE_SCTP)
IF(NOT HAVE_NATIVE_SCTP)
FIND_PACKAGE(SCTP REQUIRED)
ENDIF(NOT HAVE_NATIVE_SCTP)
# Now check the number of args of this function, since it changed between Ubuntu 9.04 and 9.10
SET(CHECK_SCTP_CONNECTX_4_ARGS_SOURCE_CODE "
#include
#include
int main() {
return sctp_connectx(0, NULL, 0, NULL);
}
")
SET(CMAKE_REQUIRED_INCLUDES ${SCTP_INCLUDE_DIR})
SET(CMAKE_REQUIRED_LIBRARIES ${SCTP_LIBRARIES})
CHECK_C_SOURCE_COMPILES("${CHECK_SCTP_CONNECTX_4_ARGS_SOURCE_CODE}" SCTP_CONNECTX_4_ARGS)
ELSE (NOT DISABLE_SCTP)
MESSAGE(STATUS "Disabled SCTP support.")
ENDIF(NOT DISABLE_SCTP)
SET(SCTP_INCLUDE_DIR ${SCTP_INCLUDE_DIR} PARENT_SCOPE)
SET(SCTP_LIBRARIES ${SCTP_LIBRARIES} PARENT_SCOPE)
# IDNA process: we use libidn from GNU (unless the function & header files are included in libc)
IF(NOT DIAMID_IDNA_IGNORE AND NOT DIAMID_IDNA_REJECT)
FIND_PACKAGE(IDNA)
SET(CHECK_IDNA_SOURCE_CODE "
#include
int main() {
return idna_to_ascii_8z(NULL, NULL, 0);
}
")
SET(CMAKE_REQUIRED_INCLUDES ${IDNA_INCLUDE_DIR})
SET(CMAKE_REQUIRED_LIBRARIES ${IDNA_LIBRARIES})
CHECK_C_SOURCE_COMPILES("${CHECK_IDNA_SOURCE_CODE}" HAS_IDNA_SUPPORT)
IF(NOT HAS_IDNA_SUPPORT)
MESSAGE(SEND_ERROR "Unable to find idna.h header or idna_to_ascii_8z function, please install libidn-dev or equivalent, or set DIAMID_IDNA_IGNORE or DIAMID_IDNA_REJECT")
ENDIF(NOT HAS_IDNA_SUPPORT)
ELSE (NOT DIAMID_IDNA_IGNORE AND NOT DIAMID_IDNA_REJECT)
MESSAGE(STATUS "Non-default Internationalized Domain Names (IDN) behavior selected (no stringprep).")
ENDIF(NOT DIAMID_IDNA_IGNORE AND NOT DIAMID_IDNA_REJECT)
SET(IDNA_INCLUDE_DIR ${IDNA_INCLUDE_DIR} PARENT_SCOPE)
SET(IDNA_LIBRARIES ${IDNA_LIBRARIES} PARENT_SCOPE)
# Require GNU TLS for building the library
FIND_PACKAGE(GnuTLS REQUIRED)
SET(GNUTLS_INCLUDE_DIR ${GNUTLS_INCLUDE_DIR} PARENT_SCOPE)
SET(GNUTLS_LIBRARIES ${GNUTLS_LIBRARIES} PARENT_SCOPE)
find_path(GCRYPT_INCLUDE_DIR NAMES gcrypt.h)
If ( NOT GCRYPT_INCLUDE_DIR )
MESSAGE(SEND_ERROR "Unable to find gcrypt.h, please install libgcrypt-dev or equivalent")
Endif ( NOT GCRYPT_INCLUDE_DIR )
MARK_AS_ADVANCED(GCRYPT_INCLUDE_DIR)
SET(GCRYPT_INCLUDE_DIR ${GCRYPT_INCLUDE_DIR} PARENT_SCOPE)
# Also we need libgcrypt to... display its version :(
find_library(GCRYPT_LIBRARY
NAMES gcrypt
)
If ( NOT GCRYPT_LIBRARY )
MESSAGE(SEND_ERROR "Unable to find libgcrypt, please install libgcrypt or equivalent")
Endif ( NOT GCRYPT_LIBRARY )
SET(GCRYPT_LIBRARY ${GCRYPT_LIBRARY} PARENT_SCOPE)
# Check if AI_ADDRCONFIG is available on the system
CHECK_SYMBOL_EXISTS(AI_ADDRCONFIG "netdb.h" HAVE_AI_ADDRCONFIG)
# Check if barriers are available (for test_fifo)
SET(CMAKE_REQUIRED_INCLUDES "pthread.h")
SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
CHECK_FUNCTION_EXISTS (pthread_barrier_wait HAVE_PTHREAD_BAR)
SET(HAVE_PTHREAD_BAR ${HAVE_PTHREAD_BAR} PARENT_SCOPE)
##########################
# Additional hg version when relevant, stored in version.h
if (EXISTS "${CMAKE_SOURCE_DIR}/.hg")
# Search for hg binary to use
FIND_PROGRAM(HGCOMMAND hg)
if (HGCOMMAND)
# Ok, add the custom target so that hg is executed at every build
ADD_CUSTOM_TARGET(version_information
COMMAND ${CMAKE_COMMAND} -D HGCOMMAND="${HGCOMMAND}" -D SRC="${CMAKE_CURRENT_SOURCE_DIR}/version.h.in" -D DST="${CMAKE_CURRENT_BINARY_DIR}/version.h" -P "${CMAKE_SOURCE_DIR}/cmake/Modules/GetVersionWithHg.cmake"
DEPENDS "${CMAKE_SOURCE_DIR}/.hg/dirstate"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Retrieving version of the hg repository"
)
else (HGCOMMAND)
# Display at least "unknown" rev in this case
SET(FD_PROJECT_VERSION_HG "unknown")
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
ADD_CUSTOM_TARGET(version_information DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/version.h)
endif(HGCOMMAND)
else (EXISTS "${CMAKE_SOURCE_DIR}/.hg")
# We use the pure version number without extension
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
ADD_CUSTOM_TARGET(version_information DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/version.h)
endif (EXISTS "${CMAKE_SOURCE_DIR}/.hg")
##########################
# LFDPROTO_LIBS = libraries required by the libfdproto.
SET(LFDPROTO_LIBS ${CLOCK_GETTIME_LIBS} ${CMAKE_THREAD_LIBS_INIT} ${IDNA_LIBRARIES} PARENT_SCOPE)
# And includes paths
SET(LFDPROTO_INCLUDES ${IDNA_INCLUDE_DIR} PARENT_SCOPE)
# Dependencies: the libraries required by any code linking to libfdproto.
SET(LFDPROTO_LINK_INTERFACES ${CMAKE_THREAD_LIBS_INIT} PARENT_SCOPE)
# LFDCORE_LIBS = libraries required by the libfdcore (in addition to libfdproto and its dependencies)
SET(LFDCORE_LIBS ${CLOCK_GETTIME_LIBS} ${CMAKE_DL_LIBS} ${SCTP_LIBRARIES} ${GCRYPT_LIBRARY} ${GNUTLS_LIBRARIES} PARENT_SCOPE)
# And includes paths
SET(LFDCORE_INCLUDES ${SCTP_INCLUDE_DIR} ${GNUTLS_INCLUDE_DIR} ${GCRYPT_INCLUDE_DIR} PARENT_SCOPE)
# And dependencies
SET(LFDCORE_LINK_INTERFACES "" PARENT_SCOPE)
# We don't force other libraries, the programs will link with what it needs
# (such as libgnutls if it uses GNUTLS_DEBUG() macro
# or libfdproto if it uses some of its interfaces directly)
# See freeDiameterd/CMakeLists.txt for an example.
##########################
# Generate the host.h file
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/freeDiameter-host.h.in ${CMAKE_CURRENT_BINARY_DIR}/freeDiameter-host.h)
####
## INSTALL section ##
# The headers from this directory are required to develop new extensions for freeDiameter.
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/freeDiameter-host.h libfdproto.h libfdcore.h extension.h
DESTINATION ${INSTALL_HEADERS_SUFFIX}
COMPONENT freeDiameter-dev)
freeDiameter/INSTALL.FreeBSD 0000644 0001750 0001750 00000006654 13324704440 014740 0 ustar ruben ruben See INSTALL file for general instructions on building freeDiameter.
------------------
QUICK INSTRUCTIONS
------------------
On FreeBSD-8.0 at least the following ports packages were required (should be same on FreeBSD-7.x):
cmake flex bison gnutls
Then the cmake command had to look like:
cmake -DFLEX_EXECUTABLE:FILEPATH=/usr/local/bin/flex -DSCTP_USE_MAPPED_ADDRESSES:BOOL=ON ...
---------------------
COMPLETE INSTRUCTIONS
---------------------
1) If you don't have freeBSD yet:
Install minimal system + ports using initial installer /usr/sbin/sysinstall
2) Install 'cmake'
a) from sources:
# cd /usr/ports/devel/cmake
# make install
b) or from package (don't forget to set your PACKAGEROOT appropriately. Ex: set PACKAGEROOT=ftp://ftp.jp.freebsd.org):
# pkg_add -v -r cmake
3) Install 'mercurial' (optional)
(replace 'cmake' by 'mercurial' in the previous command)
4) Install 'flex' and 'bison', same way.
5) Install 'gnutls' and 'libidn', same way also.
6) Retrieve freeDiameter source code:
# cd ~
# /usr/local/bin/hg clone http://www.freediameter.net/hg/freeDiameter/
6b) Make a build directory
# mkdir fD-build
# cd fD-build
7) Run cmake for freeDiameter (add other flags as you see fit, see INSTALL for more details)
# /usr/local/bin/cmake -DFLEX_EXECUTABLE:FILEPATH=/usr/local/bin/flex -DSCTP_USE_MAPPED_ADDRESSES:BOOL=ON ../freeDiameter
8) Compile, optionnaly test
# make
# make test
9) Install
# make install
10) Run (the install path can be changed during cmake step):
# /usr/local/bin/freeDiameterd --help
------------------------------
app_acct test ADDITIONAL STEPS
------------------------------
Here is additional steps in order to run the test for app_acct
1) Install the required packages
# pkg_add -v -r postgresql84-client
# pkg_add -v -r postgresql84-server
2) Prepare access to the database:
# echo postgresql_enable=YES >> /etc/rc.conf
# /usr/local/etc/rc.d/postgresql initdb
# su - pgsql
> /usr/local/bin/pg_ctl -D /usr/local/pgsql/data -l logfile start
> /usr/local/bin/createuser root
Shall the new user be allowed to create databases? (y/n) y
> logout
# /usr/local/bin/createdb root
# /usr/local/bin/psql root
root=# CREATE TABLE incoming_test (
root(# recorded_on timestamp with time zone NOT NULL,
root(# "Accounting-Record-Type" integer,
root(# "Session-Id" bytea,
root(# "Accounting-Record-Number" integer,
root(# "Route-Record1" bytea,
root(# "Route-Record2" bytea,
root(# "Route-Record3" bytea,
root(# "Route-Record4" bytea );
root=# \q
3) configure the sources
# make edit_cache
BUILD_APP_ACCT:BOOL=ON
TEST_APP_ACCT:BOOL=ON
TEST_APP_ACCT_CONNINFO:STRING="user=root dbname=root"
4) run the test
# tests/testappacct -d
or
# make test
----------------------------------
Segmentation fault on program exit
----------------------------------
In case you encounter a segmentation fault on program termination like this:
[...]
freeDiameterd framework is terminated.
Segmentation fault: 11 (core dumped)
with a backtrace like this:
#0 0x0000000801a414a0 in ?? ()
#1 0x0000000800f99274 in __cxa_finalize () from /lib/libc.so.7
#2 0x0000000800f460b7 in exit () from /lib/libc.so.7
You may try to add the following compilation directive in cmake:
-DSKIP_DLCLOSE:BOOL=ON
Note: this segmentation fault was experienced with:
- FreeBSD 8.1
- 64 bits architecture only.
- Profiling mode only.
freeDiameter/libfdproto/ 0000755 0001750 0001750 00000000000 13324704466 014637 5 ustar ruben ruben freeDiameter/libfdproto/ostr.c 0000644 0001750 0001750 00000037350 13324704440 015772 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
#include "fdproto-internal.h"
#if (!defined(DIAMID_IDNA_IGNORE) && !defined(DIAMID_IDNA_REJECT))
/* Process IDNA with stringprep -- See RFC5890 -- and libidn documentation... */
#include /* idna_to_ascii_8z() */
#endif /* !defined(DIAMID_IDNA_IGNORE) && !defined(DIAMID_IDNA_REJECT) */
/* Similar to strdup with (must have been verified) os0_t */
os0_t os0dup_int(os0_t s, size_t l) {
os0_t r;
CHECK_MALLOC_DO( r = malloc(l+1), return NULL );
if (l)
memcpy(r, s, l); /* this might be faster than a strcpy or strdup because it can work with 32 or 64b blocks */
r[l] = '\0';
return r;
}
/* case sensitive comparison, fast */
int fd_os_cmp_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz)
{
ASSERT( os1 && os2);
if (os1sz < os2sz)
return -1;
if (os1sz > os2sz)
return 1;
return os1sz ? memcmp(os1, os2, os1sz) : 0;
}
/* a local version of tolower() that does not depend on LC_CTYPE locale */
static inline uint8_t asciitolower(uint8_t a)
{
if ((a >= 'A') && (a <= 'Z'))
return a + 32 /* == 'a' - 'A' */;
return a;
}
/* less sensitive to case, slower. */
/* the semantics of "maybefurther" assume you are searching for os1 in a list of elements ordered, each element passed as os2 */
int fd_os_almostcasesrch_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz, int *maybefurther)
{
int i;
int res = 0;
ASSERT( os1 && os2);
if (maybefurther)
*maybefurther = 0;
if (os1sz < os2sz)
return -1;
if (maybefurther)
*maybefurther = 1;
if (os1sz > os2sz)
return 1;
for (i = 0; i < os1sz; i++) {
if (os1[i] == os2[i])
continue;
if (!res)
res = os1[i] < os2[i] ? -1 : 1;
if (asciitolower(os1[i]) == asciitolower(os2[i]))
continue;
return res;
}
return 0;
}
/* Check if the string contains only ASCII */
int fd_os_is_valid_DiameterIdentity(uint8_t * os, size_t ossz)
{
#ifdef DIAMID_IDNA_IGNORE
/* Allow anything */
#else /* DIAMID_IDNA_IGNORE */
int i;
/* Allow only letters, digits, hyphen, dot */
for (i=0; i < ossz; i++) {
if (os[i] > 'z')
break;
if (os[i] >= 'a')
continue;
if ((os[i] >= 'A') && (os[i] <= 'Z'))
continue;
if ((os[i] == '-') || (os[i] == '.'))
continue;
if ((os[i] >= '0') && (os[i] <= '9'))
continue;
break;
}
if (i < ossz) {
int nb = 1;
/* To get a better display, check if the invalid char is UTF-8 */
if ((os[i] & 0xE0) == 0xC0 /* 110xxxxx */) {
if ((i < ossz - 1) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */))
nb = 2;
goto disp;
}
if ((os[i] & 0xF0) == 0xE0 /* 1110xxxx */) {
if ((i < ossz - 2) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */)
&& ((os[i + 2] & 0xC0) == 0x80 /* 10xxxxxx */))
nb = 3;
goto disp;
}
if ((os[i] & 0xF8) == 0xF0 /* 11110xxx */) {
if ((i < ossz - 3) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */)
&& ((os[i + 2] & 0xC0) == 0x80 /* 10xxxxxx */)
&& ((os[i + 3] & 0xC0) == 0x80 /* 10xxxxxx */))
nb = 4;
goto disp;
}
if ((os[i] & 0xFC) == 0xF8 /* 111110xx */) {
if ((i < ossz - 4) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */)
&& ((os[i + 2] & 0xC0) == 0x80 /* 10xxxxxx */)
&& ((os[i + 3] & 0xC0) == 0x80 /* 10xxxxxx */)
&& ((os[i + 4] & 0xC0) == 0x80 /* 10xxxxxx */))
nb = 5;
goto disp;
}
if ((os[i] & 0xFE) == 0xFC /* 1111110x */) {
if ((i < ossz - 5) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */)
&& ((os[i + 2] & 0xC0) == 0x80 /* 10xxxxxx */)
&& ((os[i + 3] & 0xC0) == 0x80 /* 10xxxxxx */)
&& ((os[i + 4] & 0xC0) == 0x80 /* 10xxxxxx */)
&& ((os[i + 5] & 0xC0) == 0x80 /* 10xxxxxx */))
nb = 6;
goto disp;
}
/* otherwise, we just display the hex code */
TRACE_DEBUG(INFO, "Invalid character (0x%hhX) at offset %d in DiameterIdentity '%.*s'", os[i], i+1, (int)ossz, os);
return 0;
disp:
TRACE_DEBUG(INFO, "Invalid character '%.*s' at offset %d in DiameterIdentity '%.*s'", nb, os + i, i+1, (int)ossz, os);
return 0;
}
#endif /* DIAMID_IDNA_IGNORE */
return 1;
}
/* The following function validates a string as a Diameter Identity or applies the IDNA transformation on it
if *inoutsz is != 0 on entry, *id may not be \0-terminated.
memory has the following meaning: 0: *id can be realloc'd. 1: *id must be malloc'd on output (was static)
*/
int fd_os_validate_DiameterIdentity(char ** id, size_t * inoutsz, int memory)
{
#if !defined(DIAMID_IDNA_IGNORE) && !defined(DIAMID_IDNA_REJECT)
int gotsize = 0;
#endif /* defined(DIAMID_IDNA_IGNORE) || defined(DIAMID_IDNA_REJECT) */
TRACE_ENTRY("%p %p", id, inoutsz);
CHECK_PARAMS( id && *id && inoutsz );
if (!*inoutsz)
*inoutsz = strlen(*id);
#if !defined(DIAMID_IDNA_IGNORE) && !defined(DIAMID_IDNA_REJECT)
else
gotsize = 1;
#endif /* defined(DIAMID_IDNA_IGNORE) || defined(DIAMID_IDNA_REJECT) */
#ifndef DIAMID_IDNA_IGNORE
if (!fd_os_is_valid_DiameterIdentity((os0_t)*id, *inoutsz)) {
#ifdef DIAMID_IDNA_REJECT
TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity!", *id);
TRACE_DEBUG(INFO, "Returning EINVAL since fD is compiled with option DIAMID_IDNA_REJECT.");
return EINVAL;
#else /* DIAMID_IDNA_REJECT */
char *processed;
int ret;
if (gotsize) { /* make it \0-terminated */
if (memory) {
CHECK_MALLOC( *id = os0dup(*id, *inoutsz) );
memory = 0;
} else {
CHECK_MALLOC( *id = realloc(*id, *inoutsz + 1) );
(*id)[*inoutsz] = '0';
}
}
ret = idna_to_ascii_8z ( *id, &processed, IDNA_USE_STD3_ASCII_RULES );
if (ret == IDNA_SUCCESS) {
TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity, it was changed to '%s'", *id, processed);
if (memory == 0)
free(*id);
*id = processed;
*inoutsz = strlen(processed);
/* Done! */
} else {
TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity and cannot be sanitanized: %s", *id, idna_strerror (ret));
return EINVAL;
}
#endif /* DIAMID_IDNA_REJECT */
} else
#endif /* ! DIAMID_IDNA_IGNORE */
{
if (memory == 1) {
CHECK_MALLOC( *id = os0dup(*id, *inoutsz) );
}
}
return 0;
}
/* Analyze a DiameterURI and return its components.
Return EINVAL if the URI is not valid.
*diamid is malloc'd on function return and must be freed (it is processed by fd_os_validate_DiameterIdentity).
*secure is 0 (no security) or 1 (security enabled) on return.
*port is 0 (default) or a value in host byte order on return.
*transport is 0 (default) or IPPROTO_* on return.
*proto is 0 (default) or 'd' (diameter), 'r' (radius), or 't' (tacacs+) on return.
*/
int fd_os_parse_DiameterURI(uint8_t * uri, size_t urisz, DiamId_t * diamid, size_t * diamidlen, int * secure, uint16_t * port, int * transport, char *proto)
{
size_t offset = 0;
DiamId_t fqdn = NULL;
size_t fqdnlen;
TRACE_ENTRY("%p %zd %p %p %p %p %p %p", uri, urisz, diamid, diamidlen, secure, port, transport, proto);
CHECK_PARAMS( uri && urisz );
CHECK_PARAMS( urisz > 7 ); /* "aaa" + "://" + something else at least */
/* Initialize values */
if (secure)
*secure = 0;
if (port)
*port = 0;
if (transport)
*transport = 0;
if (proto)
*proto = 0;
/* Check the beginning */
if (memcmp( uri, "aaa", 3)) {
TRACE_DEBUG(INFO, "Invalid DiameterURI prefix: got '%.*s', expected 'aaa'", 3, uri);
return EINVAL;
}
offset += 3;
/* Secure? */
if (uri[offset] == (uint8_t)'s') {
if (secure)
*secure = 1;
offset += 1;
}
/* Remaining of URI marker */
if (memcmp( uri + offset, "://", 3)) {
TRACE_DEBUG(INFO, "Invalid DiameterURI prefix: got '%.*s', expected 'aaa://' or 'aaas://'", (int)offset + 3, uri);
return EINVAL;
}
offset += 3;
/* This is the start of the FQDN */
fqdn = (DiamId_t)uri + offset;
for ( ; offset < urisz ; offset++ ) {
/* Stop only when we find ':' or ';' */
if ((uri[offset] == (uint8_t)':') || (uri[offset] == (uint8_t)';'))
break;
}
fqdnlen = offset - (fqdn - (DiamId_t)uri);
CHECK_FCT(fd_os_validate_DiameterIdentity(&fqdn, &fqdnlen, 1));
if (diamid)
*diamid = fqdn;
else
free(fqdn);
if (diamidlen)
*diamidlen = fqdnlen;
if (offset == urisz)
return 0; /* Finished */
/* Is there a port ? */
if (uri[offset] == ':') {
uint16_t p = 0;
do {
offset++;
if (offset == urisz)
break;
uint32_t t = (uint32_t)((char)uri[offset] - '0');
if (t > 9)
break; /* we did not get a digit */
t += p * 10; /* the port is specified in decimal base */
if (t >= (1<<16)) {
TRACE_DEBUG(INFO, "Invalid DiameterURI: port value is too big.");
return EINVAL;
}
p = t;
} while (1);
if (port)
*port = p;
}
if (offset == urisz)
return 0; /* Finished */
/* Is there a transport? */
if ( (urisz - offset > CONSTSTRLEN(";transport="))
&& !strncasecmp((char *)uri + offset, ";transport=", CONSTSTRLEN(";transport=")) ) {
offset += CONSTSTRLEN(";transport=");
if (urisz - offset < 3) {
TRACE_DEBUG(INFO, "Invalid DiameterURI: transport string is too short, ignored.");
return 0;
}
if (!strncasecmp((char *)uri + offset, "tcp", CONSTSTRLEN("tcp"))) {
if (transport)
*transport = IPPROTO_TCP;
offset += CONSTSTRLEN("tcp");
goto after_transport;
}
if (!strncasecmp((char *)uri + offset, "udp", CONSTSTRLEN("udp"))) {
if (transport)
*transport = IPPROTO_UDP;
offset += CONSTSTRLEN("udp");
goto after_transport;
}
if ((urisz - offset > 3) && !strncasecmp((char *)uri + offset, "sctp", CONSTSTRLEN("sctp"))) {
if (transport) {
#ifndef DISABLE_SCTP
*transport = IPPROTO_SCTP;
#else /* DISABLE_SCTP */
TRACE_DEBUG(INFO, "Received DiameterURI with 'transport=sctp' but DISABLE_SCTP was selected");
*transport = 0;
#endif /* DISABLE_SCTP */
}
offset += CONSTSTRLEN("sctp");
goto after_transport;
}
TRACE_DEBUG(INFO, "Invalid DiameterURI: transport string is not recognized ('%.*s').", (int)(urisz - offset), uri + offset);
return EINVAL;
}
after_transport:
if (offset == urisz)
return 0; /* Finished */
/* Is there a protocol? */
if ( ((urisz - offset) > CONSTSTRLEN(";protocol="))
&& (!strncasecmp((char *)uri + offset, ";protocol=", CONSTSTRLEN(";protocol="))) ) {
offset += CONSTSTRLEN(";protocol=");
if ( ((urisz - offset) >= CONSTSTRLEN("diameter"))
&& (!strncasecmp((char *)uri + offset, "diameter", CONSTSTRLEN("diameter"))) ) {
if (proto)
*proto = 'd';
offset += CONSTSTRLEN("diameter");
goto after_proto;
}
if ( ((urisz - offset) >= CONSTSTRLEN("radius"))
&& (!strncasecmp((char *)uri + offset, "radius", CONSTSTRLEN("radius"))) ) {
if (proto)
*proto = 'r';
offset += CONSTSTRLEN("radius");
goto after_proto;
}
if ( ((urisz - offset) >= CONSTSTRLEN("tacacs+"))
&& (!strncasecmp((char *)uri + offset, "tacacs+", CONSTSTRLEN("tacacs+"))) ) {
if (proto)
*proto = 't';
offset += CONSTSTRLEN("tacacs+");
goto after_proto;
}
TRACE_DEBUG(INFO, "Invalid DiameterURI: protocol string is not recognized ('%.*s').", (int)(urisz - offset), uri + offset);
return EINVAL;
}
after_proto:
if (offset == urisz)
return 0; /* Finished */
TRACE_DEBUG(INFO, "Invalid DiameterURI: final part of string is not recognized ('%.*s').", (int)(urisz - offset), uri + offset);
return EINVAL;
}
/********************************************************************************************************/
/* Hash function -- credits to Austin Appleby, thank you ^^ */
/* See http://murmurhash.googlepages.com for more information on this function */
/* the strings are NOT always aligned properly (ex: received in RADIUS message), so we use the aligned MurmurHash2 function as needed */
#define _HASH_MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; }
uint32_t fd_os_hash ( uint8_t * string, size_t len )
{
uint32_t hash = len;
uint8_t * data = string;
const unsigned int m = 0x5bd1e995;
const int r = 24;
int align = (long)string & 3;
if (!align || (len < 4)) {
/* In case data is aligned, MurmurHash2 function */
while(len >= 4)
{
/* Mix 4 bytes at a time into the hash */
uint32_t k = *(uint32_t *)data; /* We don't care about the byte order */
_HASH_MIX(hash, k, m);
data += 4;
len -= 4;
}
/* Handle the last few bytes of the input */
switch(len) {
case 3: hash ^= data[2] << 16;
case 2: hash ^= data[1] << 8;
case 1: hash ^= data[0];
hash *= m;
}
} else {
/* Unaligned data, use alignment-safe slower version */
/* Pre-load the temp registers */
uint32_t t = 0, d = 0;
switch(align)
{
case 1: t |= data[2] << 16;
case 2: t |= data[1] << 8;
case 3: t |= data[0];
}
t <<= (8 * align);
data += 4-align;
len -= 4-align;
/* From this point, "data" can be read by chunks of 4 bytes */
int sl = 8 * (4-align);
int sr = 8 * align;
/* Mix */
while(len >= 4)
{
uint32_t k;
d = *(unsigned int *)data;
k = (t >> sr) | (d << sl);
_HASH_MIX(hash, k, m);
t = d;
data += 4;
len -= 4;
}
/* Handle leftover data in temp registers */
d = 0;
if(len >= align)
{
uint32_t k;
switch(align)
{
case 3: d |= data[2] << 16;
case 2: d |= data[1] << 8;
case 1: d |= data[0];
}
k = (t >> sr) | (d << sl);
_HASH_MIX(hash, k, m);
data += align;
len -= align;
/* Handle tail bytes */
switch(len)
{
case 3: hash ^= data[2] << 16;
case 2: hash ^= data[1] << 8;
case 1: hash ^= data[0];
hash *= m;
};
}
else
{
switch(len)
{
case 3: d |= data[2] << 16;
case 2: d |= data[1] << 8;
case 1: d |= data[0];
case 0: hash ^= (t >> sr) | (d << sl);
hash *= m;
}
}
}
/* Do a few final mixes of the hash to ensure the last few
bytes are well-incorporated. */
hash ^= hash >> 13;
hash *= m;
hash ^= hash >> 15;
return hash;
}
freeDiameter/libfdproto/dispatch.c 0000644 0001750 0001750 00000020544 13324704440 016577 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
#include "fdproto-internal.h"
/* The dispatch module in the library is quite simple: callbacks are saved in a global list
* in no particular order. In addition, they are also linked from the dictionary objects they
* refer to. */
/* Protection for the lists managed in this module. */
pthread_rwlock_t fd_disp_lock = PTHREAD_RWLOCK_INITIALIZER;
/* List of all registered handlers -- useful if we want to cleanup properly at some point... */
static struct fd_list all_handlers = FD_LIST_INITIALIZER( all_handlers );
/* List of handlers registered for DISP_HOW_ANY. Other handlers are stored in the dictionary */
static struct fd_list any_handlers = FD_LIST_INITIALIZER( any_handlers );
/* The structure to store a callback */
struct disp_hdl {
int eyec; /* Eye catcher, DISP_EYEC */
struct fd_list all; /* link in the all_handlers list */
struct fd_list parent;/* link in dictionary cb_list or in any_handlers */
enum disp_how how; /* Copy of registration parameter */
struct disp_when when; /* Copy of registration parameter */
int (*cb)( struct msg **, struct avp *, struct session *, void *, enum disp_action *); /* The callback itself */
void *opaque; /* opaque data passed back to the callback */
};
#define DISP_EYEC 0xD15241C1
#define VALIDATE_HDL( _hdl ) \
( ( ( _hdl ) != NULL ) && ( ((struct disp_hdl *)( _hdl ))->eyec == DISP_EYEC ) )
/**************************************************************************************/
/* Call CBs from a given list (any_handlers if cb_list is NULL) -- must have locked fd_disp_lock before */
int fd_disp_call_cb_int( struct fd_list * cb_list, struct msg ** msg, struct avp *avp, struct session *sess, enum disp_action *action,
struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu,
char ** drop_reason, struct msg ** drop_msg)
{
struct fd_list * senti, *li;
int r;
TRACE_ENTRY("%p %p %p %p %p %p %p %p %p", cb_list, msg, avp, sess, action, obj_app, obj_cmd, obj_avp, obj_enu);
CHECK_PARAMS(msg && action);
senti = cb_list;
if (!senti)
senti = &any_handlers;
for (li = senti->next; li != senti; li = li->next) {
struct disp_hdl * hdl = (struct disp_hdl *)(li->o);
TRACE_DEBUG(ANNOYING, "when: %p %p %p %p", hdl->when.app, hdl->when.command, hdl->when.avp, hdl->when.value);
/* Check this handler matches this message / avp */
if (hdl->when.app && (hdl->when.app != obj_app))
continue;
if (hdl->when.command && (hdl->when.command != obj_cmd))
continue;
if (hdl->when.avp && (hdl->when.avp != obj_avp))
continue;
if (hdl->when.value && (hdl->when.value != obj_enu))
continue;
/* We have a match, the cb must be called. */
CHECK_FCT_DO( (r = (*hdl->cb)(msg, avp, sess, hdl->opaque, action)),
{
*drop_reason = "Internal error: a DISPATCH callback returned an error";
*drop_msg = *msg;
*msg = NULL;
}
);
if (*action != DISP_ACT_CONT)
break;
if ( *msg == NULL )
break;
}
/* We're done on this list */
return 0;
}
/**************************************************************************************/
/* Create a new handler and link it */
int fd_disp_register ( int (*cb)( struct msg **, struct avp *, struct session *, void *, enum disp_action *),
enum disp_how how, struct disp_when * when, void * opaque, struct disp_hdl ** handle )
{
struct fd_list * cb_list = NULL;
struct disp_hdl * new;
struct dict_object * type_enum = NULL, * type_avp;
struct dictionary * dict = NULL;
TRACE_ENTRY("%p %d %p %p", cb, how, when, handle);
CHECK_PARAMS( cb && ( (how == DISP_HOW_ANY) || when ));
switch (how) {
case DISP_HOW_ANY:
cb_list = &any_handlers;
break;
case DISP_HOW_APPID:
CHECK_FCT( fd_dict_disp_cb(DICT_APPLICATION, when->app, &cb_list) );
break;
case DISP_HOW_CC:
CHECK_FCT( fd_dict_disp_cb(DICT_COMMAND, when->command, &cb_list) );
break;
case DISP_HOW_AVP_ENUMVAL:
CHECK_FCT( fd_dict_disp_cb(DICT_ENUMVAL, when->value, &cb_list) ); /* cb_list is then overwritten */
CHECK_FCT( fd_dict_getdict(when->value, &dict) );
CHECK_FCT( fd_dict_search(dict, DICT_TYPE, TYPE_OF_ENUMVAL, when->value, &type_enum, EINVAL) );
case DISP_HOW_AVP:
CHECK_FCT( fd_dict_disp_cb(DICT_AVP, when->avp, &cb_list) );
if (dict) {
CHECK_FCT( fd_dict_search(dict, DICT_TYPE, TYPE_OF_AVP, when->avp, &type_avp, EINVAL) );
if (type_enum) {
CHECK_PARAMS( type_enum == type_avp );
}
}
break;
default:
CHECK_PARAMS(how = 0);
}
/* We might further check optional fields, but we trust the caller ^^ */
/* Create the new handler */
CHECK_MALLOC( new = malloc( sizeof(struct disp_hdl) ) );
memset(new, 0, sizeof(struct disp_hdl));
new->eyec = DISP_EYEC;
fd_list_init(&new->all, new);
fd_list_init(&new->parent, new);
new->how = how;
switch (how) {
case DISP_HOW_ANY:
/* there is no "when" in that case */
break;
case DISP_HOW_AVP_ENUMVAL:
new->when.value = when->value;
case DISP_HOW_AVP:
new->when.avp = when->avp;
case DISP_HOW_CC:
new->when.command = when->command;
case DISP_HOW_APPID:
new->when.app = when->app;
}
new->cb = cb;
new->opaque = opaque;
/* Now, link this new element in the appropriate lists */
CHECK_POSIX( pthread_rwlock_wrlock(&fd_disp_lock) );
fd_list_insert_before(&all_handlers, &new->all);
fd_list_insert_before(cb_list, &new->parent);
CHECK_POSIX( pthread_rwlock_unlock(&fd_disp_lock) );
/* We're done */
if (handle)
*handle = new;
return 0;
}
/* Delete a handler */
int fd_disp_unregister ( struct disp_hdl ** handle, void ** opaque )
{
struct disp_hdl * del;
TRACE_ENTRY("%p", handle);
CHECK_PARAMS( handle && VALIDATE_HDL(*handle) );
del = *handle;
*handle = NULL;
CHECK_POSIX( pthread_rwlock_wrlock(&fd_disp_lock) );
fd_list_unlink(&del->all);
fd_list_unlink(&del->parent);
CHECK_POSIX( pthread_rwlock_unlock(&fd_disp_lock) );
if (opaque)
*opaque = del->opaque;
free(del);
return 0;
}
/* Delete all handlers */
void fd_disp_unregister_all ( void )
{
TRACE_ENTRY("");
while (!FD_IS_LIST_EMPTY(&all_handlers)) {
CHECK_FCT_DO( fd_disp_unregister((void *)&(all_handlers.next->o), NULL), /* continue */ );
}
return;
}
freeDiameter/libfdproto/utils.c 0000644 0001750 0001750 00000007130 13324704440 016134 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
#include "fdproto-internal.h"
DECLARE_FD_DUMP_PROTOTYPE(fd_sa_dump, sSA * sa, int flags)
{
char addrbuf[INET6_ADDRSTRLEN];
char servbuf[32];
int rc;
FD_DUMP_HANDLE_OFFSET();
servbuf[0] = 0;
if (sa) {
if (sSAport(sa)) {
rc = getnameinfo(sa, sSAlen( sa ), addrbuf, sizeof(addrbuf), servbuf, sizeof(servbuf), flags);
} else {
rc = getnameinfo(sa, sSAlen( sa ), addrbuf, sizeof(addrbuf), NULL, 0, flags);
}
if (rc) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s", gai_strerror(rc)), return NULL);
} else {
if (servbuf[0]) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s(%s)", &addrbuf[0], &servbuf[0]), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s", &addrbuf[0]), return NULL);
}
}
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(NULL / ANY)"), return NULL);
}
return *buf;
}
void fd_sa_sdump_numeric(char * buf /* must be at least sSA_DUMP_STRLEN */, sSA * sa)
{
char addrbuf[INET6_ADDRSTRLEN];
char servbuf[32];
if (sa) {
int rc = getnameinfo(sa, sSAlen( sa ), addrbuf, sizeof(addrbuf), servbuf, sizeof(servbuf), NI_NUMERICHOST | NI_NUMERICSERV);
if (rc) {
snprintf(buf, sSA_DUMP_STRLEN, "%s", gai_strerror(rc));
} else {
snprintf(buf, sSA_DUMP_STRLEN, "%s(%s)", addrbuf, servbuf);
}
} else {
snprintf(buf, sSA_DUMP_STRLEN, "(NULL / ANY)");
}
}
freeDiameter/libfdproto/init.c 0000644 0001750 0001750 00000005756 13324704440 015753 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
#include "fdproto-internal.h"
/* function to free the threadnames */
static void freelogstr(void * str) {
if (TRACE_BOOL(ANNOYING)) {
if (str) {
fd_log_debug("(Thread '%s' terminating)", (char *)str);
}
}
free(str);
}
/* Initialize library variables and threads */
int fd_libproto_init()
{
int ret = 0;
/* Create the thread key that contains thread name for debug messages */
ret = pthread_key_create(&fd_log_thname, freelogstr);
if (ret != 0) {
fprintf(stderr, "Error initializing the libfreeDiameter library: %s\n", strerror(ret) );
return ret;
}
/* Initialize the modules that need it */
fd_msg_eteid_init();
CHECK_FCT( fd_sess_init() );
return 0;
}
/* Stop all threads created in the library */
void fd_libproto_fini(void)
{
fd_sess_fini();
}
freeDiameter/libfdproto/portability.c 0000644 0001750 0001750 00000005672 13324704440 017347 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2012, 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. *
*********************************************************************************************************/
#include "fdproto-internal.h"
/* Replacement for clock_gettime for the Mac OS */
#ifndef HAVE_CLOCK_GETTIME
int clock_gettime(int clk_id, struct timespec* ts)
{
struct timeval tv;
gettimeofday (&tv, NULL);
ts->tv_sec = tv.tv_sec;
ts->tv_nsec = tv.tv_usec * 1000;
return 0;
}
#endif /* HAVE_CLOCK_GETTIME */
/* Replacement for strndup for the Mac OS */
#ifndef HAVE_STRNDUP
char * strndup (char *str, size_t len)
{
char * output;
size_t outlen;
output = memchr(str, 0, len);
if (output == NULL) {
outlen = len;
} else {
outlen = output - str;
}
CHECK_MALLOC_DO( output = malloc (outlen + 1), return NULL );
output[outlen] = '\0';
memcpy (output, str, outlen);
return output;
}
#endif /* HAVE_STRNDUP */
freeDiameter/libfdproto/fdproto-internal.h 0000644 0001750 0001750 00000006763 13324704440 020303 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2013, 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 file contains the definitions for internal use in the freeDiameter protocol library */
#ifndef _LIBFDPROTO_INTERNAL_H
#define _LIBFDPROTO_INTERNAL_H
#include
#include
/* Internal to the library */
extern const char * type_base_name[];
void fd_msg_eteid_init(void);
int fd_sess_init(void);
void fd_sess_fini(void);
/* Iterator on the rules of a parent object */
int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(void *, struct dict_rule_data *) );
/* Dispatch / messages / dictionary API */
int fd_dict_disp_cb(enum dict_object_type type, struct dict_object *obj, struct fd_list ** cb_list);
DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump_avp_value, union avp_value *avp_value, struct dict_object * model, int indent, int header);
int fd_disp_call_cb_int( struct fd_list * cb_list, struct msg ** msg, struct avp *avp, struct session *sess, enum disp_action *action,
struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu,
char ** drop_reason, struct msg ** drop_msg);
extern pthread_rwlock_t fd_disp_lock;
/* Messages / sessions API */
int fd_sess_reclaim_msg ( struct session ** session );
#endif /* _LIBFDPROTO_INTERNAL_H */
freeDiameter/libfdproto/sessions.c 0000644 0001750 0001750 00000075236 13324704440 016656 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
/* Sessions module.
*
* Basic functionalities to help implementing User sessions state machines from RFC3588.
*/
#include "fdproto-internal.h"
/*********************** Parameters **********************/
/* Size of the hash table containing the session objects (pow of 2. ex: 6 => 2^6 = 64). must be between 0 and 31. */
#ifndef SESS_HASH_SIZE
#define SESS_HASH_SIZE 6
#endif /* SESS_HASH_SIZE */
/* Default lifetime of a session, in seconds. (31 days = 2678400 seconds) */
#ifndef SESS_DEFAULT_LIFETIME
#define SESS_DEFAULT_LIFETIME 2678400
#endif /* SESS_DEFAULT_LIFETIME */
/********************** /Parameters **********************/
/* Eyescatchers definitions */
#define SH_EYEC 0x53554AD1
#define SD_EYEC 0x5355D474
#define SI_EYEC 0x53551D
/* Macro to check an object is valid */
#define VALIDATE_SH( _obj ) ( ((_obj) != NULL) && ( ((struct session_handler *)(_obj))->eyec == SH_EYEC) )
#define VALIDATE_SI( _obj ) ( ((_obj) != NULL) && ( ((struct session *)(_obj))->eyec == SI_EYEC) )
/* Handlers registered by users of the session module */
struct session_handler {
int eyec; /* An eye catcher also used to ensure the object is valid, must be SH_EYEC */
int id; /* A unique integer to identify this handler */
void (*cleanup)(struct sess_state *, os0_t, void *); /* The cleanup function to be called for cleaning a state */
session_state_dump state_dump; /* dumper function */
void *opaque; /* a value that is passed as is to the cleanup callback */
};
static int hdl_id = 0; /* A global counter to initialize the id field */
static pthread_mutex_t hdl_lock = PTHREAD_MUTEX_INITIALIZER; /* lock to protect hdl_id; we could use atomic operations otherwise (less portable) */
/* Data structures linked from the sessions, containing the applications states */
struct state {
int eyec; /* Must be SD_EYEC */
struct sess_state *state; /* The state registered by the application, never NULL (or the whole object is deleted) */
struct fd_list chain; /* Chaining in the list of session's states ordered by hdl->id */
union {
struct session_handler *hdl; /* The handler for which this state was registered */
os0_t sid; /* For deleted state, the sid of the session it belong to */
};
};
/* Session object, one for each value of Session-Id AVP */
struct session {
int eyec; /* Eyecatcher, SI_EYEC */
os0_t sid; /* The \0-terminated Session-Id */
size_t sidlen; /* cached length of sid */
uint32_t hash; /* computed hash of sid */
struct fd_list chain_h;/* chaining in the hash table of sessions. */
struct timespec timeout;/* Timeout date for the session */
struct fd_list expire; /* List of expiring sessions, ordered by timeouts. */
pthread_mutex_t stlock; /* A lock to protect the list of states associated with this session */
struct fd_list states; /* Sentinel for the list of states of this session. */
int msg_cnt;/* Reference counter for the messages pointing to this session */
int is_destroyed; /* boolean telling if fd_sess_detroy has been called on this */
};
/* Sessions hash table, to allow fast sid to session retrieval */
static struct {
struct fd_list sentinel; /* sentinel element for this sublist. The sublist is ordered by hash value, then fd_os_cmp(sid). */
pthread_mutex_t lock; /* the mutex for this sublist -- we might probably change it to rwlock for a little optimization */
} sess_hash [ 1 << SESS_HASH_SIZE ] ;
#define H_MASK( __hash ) ((__hash) & (( 1 << SESS_HASH_SIZE ) - 1))
#define H_LIST( _hash ) (&(sess_hash[H_MASK(_hash)].sentinel))
#define H_LOCK( _hash ) (&(sess_hash[H_MASK(_hash)].lock ))
static uint32_t sess_cnt = 0; /* counts all active session (that are in the expiry list) */
/* The following are used to generate sid values that are eternaly unique */
static uint32_t sid_h; /* initialized to the current time in fd_sess_init */
static uint32_t sid_l; /* incremented each time a session id is created */
static pthread_mutex_t sid_lock = PTHREAD_MUTEX_INITIALIZER;
/* Expiring sessions management */
static struct fd_list exp_sentinel = FD_LIST_INITIALIZER(exp_sentinel); /* list of sessions ordered by their timeout date */
static pthread_mutex_t exp_lock = PTHREAD_MUTEX_INITIALIZER; /* lock protecting the list. */
static pthread_cond_t exp_cond = PTHREAD_COND_INITIALIZER; /* condvar used by the expiry mecahinsm. */
static pthread_t exp_thr = (pthread_t)NULL; /* The expiry thread that handles cleanup of expired sessions */
/* Hierarchy of the locks, to avoid deadlocks:
* hash lock > state lock > expiry lock
* i.e. state lock can be taken while holding the hash lock, but not while holding the expiry lock.
* As well, the hash lock cannot be taken while holding a state lock.
*/
/********************************************************************************************************/
/* Initialize a session object. It is not linked now. sid must be already malloc'ed. The hash has already been computed. */
static struct session * new_session(os0_t sid, size_t sidlen, uint32_t hash)
{
struct session * sess;
TRACE_ENTRY("%p %zd", sid, sidlen);
CHECK_PARAMS_DO( sid && sidlen, return NULL );
CHECK_MALLOC_DO( sess = malloc(sizeof(struct session)), return NULL );
memset(sess, 0, sizeof(struct session));
sess->eyec = SI_EYEC;
sess->sid = sid;
sess->sidlen = sidlen;
sess->hash = hash;
fd_list_init(&sess->chain_h, sess);
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &sess->timeout), return NULL );
sess->timeout.tv_sec += SESS_DEFAULT_LIFETIME;
fd_list_init(&sess->expire, sess);
CHECK_POSIX_DO( pthread_mutex_init(&sess->stlock, NULL), return NULL );
fd_list_init(&sess->states, sess);
return sess;
}
/* destroy the session object. It should really be already unlinked... */
static void del_session(struct session * s)
{
ASSERT(FD_IS_LIST_EMPTY(&s->states));
free(s->sid);
fd_list_unlink(&s->chain_h);
fd_list_unlink(&s->expire);
CHECK_POSIX_DO( pthread_mutex_destroy(&s->stlock), /* continue */ );
free(s);
}
/* The expiry thread */
static void * exp_fct(void * arg)
{
fd_log_threadname ( "Session/expire" );
TRACE_ENTRY( "" );
do {
struct timespec now;
struct session * first;
CHECK_POSIX_DO( pthread_mutex_lock(&exp_lock), break );
pthread_cleanup_push( fd_cleanup_mutex, &exp_lock );
again:
/* Check if there are expiring sessions available */
if (FD_IS_LIST_EMPTY(&exp_sentinel)) {
/* Just wait for a change or cancelation */
CHECK_POSIX_DO( pthread_cond_wait( &exp_cond, &exp_lock ), break /* this might not pop the cleanup handler, but since we ASSERT(0), it is not the big issue... */ );
/* Restart the loop on wakeup */
goto again;
}
/* Get the pointer to the session that expires first */
first = (struct session *)(exp_sentinel.next->o);
ASSERT( VALIDATE_SI(first) );
/* Get the current time */
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), break );
/* If first session is not expired, we just wait until it happens */
if ( TS_IS_INFERIOR( &now, &first->timeout ) ) {
CHECK_POSIX_DO2( pthread_cond_timedwait( &exp_cond, &exp_lock, &first->timeout ),
ETIMEDOUT, /* ETIMEDOUT is a normal error, continue */,
/* on other error, */ break );
/* on wakeup, loop */
goto again;
}
/* Now, the first session in the list is expired; destroy it */
pthread_cleanup_pop( 0 );
CHECK_POSIX_DO( pthread_mutex_unlock(&exp_lock), break );
CHECK_FCT_DO( fd_sess_destroy( &first ), break );
} while (1);
TRACE_DEBUG(INFO, "A system error occurred in session module! Expiry thread is terminating...");
ASSERT(0);
return NULL;
}
/********************************************************************************************************/
/* Initialize the session module */
int fd_sess_init(void)
{
int i;
TRACE_ENTRY( "" );
/* Initialize the global counters */
sid_h = (uint32_t) time(NULL);
sid_l = 0;
/* Initialize the hash table */
for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) {
fd_list_init( &sess_hash[i].sentinel, NULL );
CHECK_POSIX( pthread_mutex_init(&sess_hash[i].lock, NULL) );
}
return 0;
}
/* Run this when initializations are complete. */
int fd_sess_start(void)
{
/* Start session garbage collector (expiry) */
CHECK_POSIX( pthread_create(&exp_thr, NULL, exp_fct, NULL) );
return 0;
}
/* Terminate */
void fd_sess_fini(void)
{
TRACE_ENTRY("");
CHECK_FCT_DO( fd_thr_term(&exp_thr), /* continue */ );
/* Destroy all sessions in the hash table, and the hash table itself? -- How to do it without a race condition ? */
return;
}
/* Create a new handler */
int fd_sess_handler_create ( struct session_handler ** handler, void (*cleanup)(struct sess_state *, os0_t, void *), session_state_dump dumper, void * opaque )
{
struct session_handler *new;
TRACE_ENTRY("%p %p", handler, cleanup);
CHECK_PARAMS( handler && cleanup );
CHECK_MALLOC( new = malloc(sizeof(struct session_handler)) );
memset(new, 0, sizeof(struct session_handler));
CHECK_POSIX( pthread_mutex_lock(&hdl_lock) );
new->id = ++hdl_id;
CHECK_POSIX( pthread_mutex_unlock(&hdl_lock) );
new->eyec = SH_EYEC;
new->cleanup = cleanup;
new->state_dump = dumper;
new->opaque = opaque;
*handler = new;
return 0;
}
/* Destroy a handler, and all states attached to this handler. This operation is very slow but we don't care since it's rarely used.
* Note that it's better to call this function after all sessions have been deleted... */
int fd_sess_handler_destroy ( struct session_handler ** handler, void ** opaque )
{
struct session_handler * del;
/* place to save the list of states to be cleaned up. We do it after finding them to avoid deadlocks. the "o" field becomes a copy of the sid. */
struct fd_list deleted_states = FD_LIST_INITIALIZER( deleted_states );
int i;
TRACE_ENTRY("%p", handler);
CHECK_PARAMS( handler && VALIDATE_SH(*handler) );
del = *handler;
*handler = NULL;
del->eyec = 0xdead; /* The handler is not valid anymore for any other operation */
/* Now find all sessions with data registered for this handler, and move this data to the deleted_states list. */
for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) {
struct fd_list * li_si;
CHECK_POSIX( pthread_mutex_lock(&sess_hash[i].lock) );
for (li_si = sess_hash[i].sentinel.next; li_si != &sess_hash[i].sentinel; li_si = li_si->next) { /* for each session in the hash line */
struct fd_list * li_st;
struct session * sess = (struct session *)(li_si->o);
CHECK_POSIX( pthread_mutex_lock(&sess->stlock) );
for (li_st = sess->states.next; li_st != &sess->states; li_st = li_st->next) { /* for each state in this session */
struct state * st = (struct state *)(li_st->o);
/* The list is ordered */
if (st->hdl->id < del->id)
continue;
if (st->hdl->id == del->id) {
/* This state belongs to the handler we are deleting, move the item to the deleted_states list */
fd_list_unlink(&st->chain);
st->sid = sess->sid;
fd_list_insert_before(&deleted_states, &st->chain);
}
break;
}
CHECK_POSIX( pthread_mutex_unlock(&sess->stlock) );
}
CHECK_POSIX( pthread_mutex_unlock(&sess_hash[i].lock) );
}
/* Now, delete all states after calling their cleanup handler */
while (!FD_IS_LIST_EMPTY(&deleted_states)) {
struct state * st = (struct state *)(deleted_states.next->o);
TRACE_DEBUG(FULL, "Calling cleanup handler for session '%s' and data %p", st->sid, st->state);
(*del->cleanup)(st->state, st->sid, del->opaque);
fd_list_unlink(&st->chain);
free(st);
}
if (opaque)
*opaque = del->opaque;
/* Free the handler */
free(del);
return 0;
}
/* Create a new session object with the default timeout value, and link it. The refcount is increased by 1, whether the session existed or not */
int fd_sess_new ( struct session ** session, DiamId_t diamid, size_t diamidlen, uint8_t * opt, size_t optlen )
{
os0_t sid = NULL;
size_t sidlen;
uint32_t hash;
struct session * sess;
struct fd_list * li;
int found = 0;
int ret = 0;
TRACE_ENTRY("%p %p %zd %p %zd", session, diamid, diamidlen, opt, optlen);
CHECK_PARAMS( session && (diamid || opt) );
if (diamid) {
if (!diamidlen) {
diamidlen = strlen(diamid);
}
/* We check if the string is a valid DiameterIdentity */
CHECK_PARAMS( fd_os_is_valid_DiameterIdentity((uint8_t *)diamid, diamidlen) );
} else {
diamidlen = 0;
}
if (opt) {
if (!optlen) {
optlen = strlen((char *)opt);
} else {
CHECK_PARAMS( fd_os_is_valid_os0(opt, optlen) );
}
} else {
optlen = 0;
}
/* Ok, first create the identifier for the string */
if (diamid == NULL) {
/* opt is the full string */
CHECK_MALLOC( sid = os0dup(opt, optlen) );
sidlen = optlen;
} else {
uint32_t sid_h_cpy;
uint32_t sid_l_cpy;
/* ";;[;opt]" */
sidlen = diamidlen;
sidlen += 22; /* max size of ';;' */
if (opt)
sidlen += 1 + optlen; /* ';opt' */
sidlen++; /* space for the final \0 also */
CHECK_MALLOC( sid = malloc(sidlen) );
CHECK_POSIX( pthread_mutex_lock(&sid_lock) );
if ( ++sid_l == 0 ) /* overflow */
++sid_h;
sid_h_cpy = sid_h;
sid_l_cpy = sid_l;
CHECK_POSIX( pthread_mutex_unlock(&sid_lock) );
if (opt) {
sidlen = snprintf((char*)sid, sidlen, "%.*s;%u;%u;%.*s", (int)diamidlen, diamid, sid_h_cpy, sid_l_cpy, (int)optlen, opt);
} else {
sidlen = snprintf((char*)sid, sidlen, "%.*s;%u;%u", (int)diamidlen, diamid, sid_h_cpy, sid_l_cpy);
}
}
hash = fd_os_hash(sid, sidlen);
/* Now find the place to add this object in the hash table. */
CHECK_POSIX( pthread_mutex_lock( H_LOCK(hash) ) );
pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(hash) );
for (li = H_LIST(hash)->next; li != H_LIST(hash); li = li->next) {
int cmp;
struct session * s = (struct session *)(li->o);
/* The list is ordered by hash and sid (in case of collisions) */
if (s->hash < hash)
continue;
if (s->hash > hash)
break;
cmp = fd_os_cmp(s->sid, s->sidlen, sid, sidlen);
if (cmp < 0)
continue;
if (cmp > 0)
break;
/* A session with the same sid was already in the hash table */
found = 1;
*session = s;
break;
}
/* If the session did not exist, we can create it & link it in global tables */
if (!found) {
CHECK_MALLOC_DO(sess = new_session(sid, sidlen, hash),
{
ret = ENOMEM;
free(sid);
goto out;
} );
fd_list_insert_before(li, &sess->chain_h); /* hash table */
sess->msg_cnt++;
} else {
free(sid);
CHECK_POSIX( pthread_mutex_lock(&(*session)->stlock) );
(*session)->msg_cnt++;
CHECK_POSIX( pthread_mutex_unlock(&(*session)->stlock) );
/* it was found: was it previously destroyed? */
if ((*session)->is_destroyed == 0) {
ret = EALREADY;
goto out;
} else {
/* the session was marked destroyed, let's re-activate it. */
sess = *session;
sess->is_destroyed = 0;
/* update the expiry time */
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &sess->timeout), { ASSERT(0); } );
sess->timeout.tv_sec += SESS_DEFAULT_LIFETIME;
}
}
/* We must insert in the expiry list */
CHECK_POSIX( pthread_mutex_lock( &exp_lock ) );
pthread_cleanup_push( fd_cleanup_mutex, &exp_lock );
/* Find the position in that list. We take it in reverse order */
for (li = exp_sentinel.prev; li != &exp_sentinel; li = li->prev) {
struct session * s = (struct session *)(li->o);
if (TS_IS_INFERIOR( &s->timeout, &sess->timeout ) )
break;
}
fd_list_insert_after( li, &sess->expire );
sess_cnt++;
/* We added a new expiring element, we must signal */
if (li == &exp_sentinel) {
CHECK_POSIX_DO( pthread_cond_signal(&exp_cond), { ASSERT(0); } ); /* if it fails, we might not pop the cleanup handlers, but this should not happen -- and we'd have a serious problem otherwise */
}
/* We're done with the locked part */
pthread_cleanup_pop(0);
CHECK_POSIX_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); } ); /* if it fails, we might not pop the cleanup handler, but this should not happen -- and we'd have a serious problem otherwise */
out:
;
pthread_cleanup_pop(0);
CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) );
if (ret) /* in case of error */
return ret;
*session = sess;
return 0;
}
/* Find or create a session -- the msg refcount is increased */
int fd_sess_fromsid_msg ( uint8_t * sid, size_t len, struct session ** session, int * new)
{
int ret;
TRACE_ENTRY("%p %zd %p %p", sid, len, session, new);
CHECK_PARAMS( sid && session );
if (!fd_os_is_valid_os0(sid,len)) {
TRACE_DEBUG(INFO, "Warning: a Session-Id value contains \\0 chars... (len:%zd, begin:'%.*s') => Debug messages may be truncated.", len, (int)len, sid);
}
/* All the work is done in sess_new */
ret = fd_sess_new ( session, NULL, 0, sid, len );
switch (ret) {
case 0:
case EALREADY:
break;
default:
CHECK_FCT(ret);
}
if (new)
*new = ret ? 0 : 1;
return 0;
}
/* Get the sid of a session */
int fd_sess_getsid ( struct session * session, os0_t * sid, size_t * sidlen )
{
TRACE_ENTRY("%p %p", session, sid);
CHECK_PARAMS( VALIDATE_SI(session) && sid );
*sid = session->sid;
if (sidlen)
*sidlen = session->sidlen;
return 0;
}
/* Change the timeout value of a session */
int fd_sess_settimeout( struct session * session, const struct timespec * timeout )
{
struct fd_list * li;
TRACE_ENTRY("%p %p", session, timeout);
CHECK_PARAMS( VALIDATE_SI(session) && timeout );
/* Lock -- do we need to lock the hash table as well? I don't think so... */
CHECK_POSIX( pthread_mutex_lock( &exp_lock ) );
pthread_cleanup_push( fd_cleanup_mutex, &exp_lock );
/* Update the timeout */
fd_list_unlink(&session->expire);
memcpy(&session->timeout, timeout, sizeof(struct timespec));
/* Find the new position in expire list. We take it in normal order */
for (li = exp_sentinel.next; li != &exp_sentinel; li = li->next) {
struct session * s = (struct session *)(li->o);
if (TS_IS_INFERIOR( &s->timeout, &session->timeout ) )
continue;
break;
}
fd_list_insert_before( li, &session->expire );
/* We added a new expiring element, we must signal if it was in first position */
if (session->expire.prev == &exp_sentinel) {
CHECK_POSIX_DO( pthread_cond_signal(&exp_cond), { ASSERT(0); /* so that we don't have a pending cancellation handler */ } );
}
/* We're done */
pthread_cleanup_pop(0);
CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) );
return 0;
}
/* Destroy the states associated to a session, and mark it destroyed. */
int fd_sess_destroy ( struct session ** session )
{
struct session * sess;
int destroy_now;
os0_t sid;
int ret = 0;
/* place to save the list of states to be cleaned up. We do it after finding them to avoid deadlocks. the "o" field becomes a copy of the sid. */
struct fd_list deleted_states = FD_LIST_INITIALIZER( deleted_states );
TRACE_ENTRY("%p", session);
CHECK_PARAMS( session && VALIDATE_SI(*session) );
sess = *session;
*session = NULL;
/* Lock the hash line */
CHECK_POSIX( pthread_mutex_lock( H_LOCK(sess->hash) ) );
pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) );
/* Unlink from the expiry list */
CHECK_POSIX_DO( pthread_mutex_lock( &exp_lock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } );
if (!FD_IS_LIST_EMPTY(&sess->expire)) {
sess_cnt--;
fd_list_unlink( &sess->expire ); /* no need to signal the condition here */
}
CHECK_POSIX_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } );
/* Now move all states associated to this session into deleted_states */
CHECK_POSIX_DO( pthread_mutex_lock( &sess->stlock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } );
while (!FD_IS_LIST_EMPTY(&sess->states)) {
struct state * st = (struct state *)(sess->states.next->o);
fd_list_unlink(&st->chain);
fd_list_insert_before(&deleted_states, &st->chain);
}
CHECK_POSIX_DO( pthread_mutex_unlock( &sess->stlock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } );
/* Mark the session as destroyed */
destroy_now = (sess->msg_cnt == 0);
if (destroy_now) {
fd_list_unlink( &sess->chain_h );
sid = sess->sid;
} else {
sess->is_destroyed = 1;
CHECK_MALLOC_DO( sid = os0dup(sess->sid, sess->sidlen), ret = ENOMEM );
}
pthread_cleanup_pop(0);
CHECK_POSIX( pthread_mutex_unlock( H_LOCK(sess->hash) ) );
if (ret)
return ret;
/* Now, really delete the states */
while (!FD_IS_LIST_EMPTY(&deleted_states)) {
struct state * st = (struct state *)(deleted_states.next->o);
fd_list_unlink(&st->chain);
TRACE_DEBUG(FULL, "Calling handler %p cleanup for state %p registered with session '%s'", st->hdl, st, sid);
(*st->hdl->cleanup)(st->state, sid, st->hdl->opaque);
free(st);
}
/* Finally, destroy the session itself, if it is not referrenced by any message anymore */
if (destroy_now) {
del_session(sess);
} else {
free(sid);
}
return 0;
}
/* Destroy a session if it is not used */
int fd_sess_reclaim ( struct session ** session )
{
struct session * sess;
uint32_t hash;
int destroy_now = 0;
TRACE_ENTRY("%p", session);
CHECK_PARAMS( session && VALIDATE_SI(*session) );
sess = *session;
hash = sess->hash;
*session = NULL;
CHECK_POSIX( pthread_mutex_lock( H_LOCK(hash) ) );
pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(hash) );
CHECK_POSIX_DO( pthread_mutex_lock( &sess->stlock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } );
pthread_cleanup_push( fd_cleanup_mutex, &sess->stlock );
CHECK_POSIX_DO( pthread_mutex_lock( &exp_lock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } );
/* We only do something if the states list is empty */
if (FD_IS_LIST_EMPTY(&sess->states)) {
/* In this case, we do as in destroy */
fd_list_unlink( &sess->expire );
destroy_now = (sess->msg_cnt == 0);
if (destroy_now) {
fd_list_unlink(&sess->chain_h);
} else {
/* just mark it as destroyed, it will be freed when the last message stops referencing it */
sess->is_destroyed = 1;
}
}
CHECK_POSIX_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } );
pthread_cleanup_pop(0);
CHECK_POSIX_DO( pthread_mutex_unlock( &sess->stlock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } );
pthread_cleanup_pop(0);
CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) );
if (destroy_now)
del_session(sess);
return 0;
}
/* Save a state information with a session */
int fd_sess_state_store ( struct session_handler * handler, struct session * session, struct sess_state ** state )
{
struct state *new;
struct fd_list * li;
int already = 0;
int ret = 0;
TRACE_ENTRY("%p %p %p", handler, session, state);
CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && (!session->is_destroyed) && state );
/* Lock the session state list */
CHECK_POSIX( pthread_mutex_lock(&session->stlock) );
pthread_cleanup_push( fd_cleanup_mutex, &session->stlock );
/* Create the new state object */
CHECK_MALLOC_DO(new = malloc(sizeof(struct state)), { ret = ENOMEM; goto out; } );
memset(new, 0, sizeof(struct state));
new->eyec = SD_EYEC;
new->state= *state;
fd_list_init(&new->chain, new);
new->hdl = handler;
/* find place for this state in the list */
for (li = session->states.next; li != &session->states; li = li->next) {
struct state * st = (struct state *)(li->o);
/* The list is ordered by handler's id */
if (st->hdl->id < handler->id)
continue;
if (st->hdl->id == handler->id) {
TRACE_DEBUG(INFO, "A state was already stored for session '%s' and handler '%p', at location %p", session->sid, st->hdl, st->state);
already = EALREADY;
}
break;
}
if (!already) {
fd_list_insert_before(li, &new->chain);
*state = NULL;
} else {
free(new);
}
out:
;
pthread_cleanup_pop(0);
CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
return ret ?: already;
}
/* Get the data back */
int fd_sess_state_retrieve ( struct session_handler * handler, struct session * session, struct sess_state ** state )
{
struct fd_list * li;
struct state * st = NULL;
TRACE_ENTRY("%p %p %p", handler, session, state);
CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && state );
*state = NULL;
/* Lock the session state list */
CHECK_POSIX( pthread_mutex_lock(&session->stlock) );
pthread_cleanup_push( fd_cleanup_mutex, &session->stlock );
/* find the state in the list */
for (li = session->states.next; li != &session->states; li = li->next) {
st = (struct state *)(li->o);
/* The list is ordered by handler's id */
if (st->hdl->id > handler->id)
break;
}
/* If we found the state */
if (st && (st->hdl == handler)) {
fd_list_unlink(&st->chain);
*state = st->state;
free(st);
}
pthread_cleanup_pop(0);
CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
return 0;
}
/* For the messages module */
int fd_sess_fromsid ( uint8_t * sid, size_t len, struct session ** session, int * new)
{
TRACE_ENTRY("%p %zd %p %p", sid, len, session, new);
CHECK_PARAMS( sid && len && session );
/* Get the session object */
CHECK_FCT( fd_sess_fromsid_msg ( sid, len, session, new) );
/* Decrease the refcount */
CHECK_POSIX( pthread_mutex_lock(&(*session)->stlock) );
(*session)->msg_cnt--; /* was increased in fd_sess_new */
CHECK_POSIX( pthread_mutex_unlock(&(*session)->stlock) );
/* Done */
return 0;
}
int fd_sess_ref_msg ( struct session * session )
{
TRACE_ENTRY("%p", session);
CHECK_PARAMS( VALIDATE_SI(session) );
/* Update the msg refcount */
CHECK_POSIX( pthread_mutex_lock(&session->stlock) );
session->msg_cnt++;
CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
return 0;
}
int fd_sess_reclaim_msg ( struct session ** session )
{
int reclaim;
uint32_t hash;
TRACE_ENTRY("%p", session);
CHECK_PARAMS( session && VALIDATE_SI(*session) );
/* Lock the hash line to avoid possibility that session is freed while we are reclaiming */
hash = (*session)->hash;
CHECK_POSIX( pthread_mutex_lock( H_LOCK(hash)) );
pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(hash) );
/* Update the msg refcount */
CHECK_POSIX( pthread_mutex_lock(&(*session)->stlock) );
reclaim = (*session)->msg_cnt;
(*session)->msg_cnt = reclaim - 1;
CHECK_POSIX( pthread_mutex_unlock(&(*session)->stlock) );
/* Ok, now unlock the hash line */
pthread_cleanup_pop( 0 );
CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) );
/* and reclaim if no message references the session anymore */
if (reclaim == 1) {
CHECK_FCT(fd_sess_reclaim ( session ));
} else {
*session = NULL;
}
return 0;
}
/* Dump functions */
DECLARE_FD_DUMP_PROTOTYPE(fd_sess_dump, struct session * session, int with_states)
{
FD_DUMP_HANDLE_OFFSET();
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{session}(@%p): ", session), return NULL);
if (!VALIDATE_SI(session)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL);
} else {
char timebuf[30];
struct tm tm;
strftime(timebuf, sizeof(timebuf), "%D,%T", localtime_r( &session->timeout.tv_sec , &tm ));
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'(%zd) h:%x m:%d d:%d to:%s.%06ld",
session->sid, session->sidlen, session->hash, session->msg_cnt, session->is_destroyed,
timebuf, session->timeout.tv_nsec/1000),
return NULL);
if (with_states) {
struct fd_list * li;
CHECK_POSIX_DO( pthread_mutex_lock(&session->stlock), /* ignore */ );
pthread_cleanup_push( fd_cleanup_mutex, &session->stlock );
for (li = session->states.next; li != &session->states; li = li->next) {
struct state * st = (struct state *)(li->o);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {state i:%d}(@%p): ", st->hdl->id, st), return NULL);
if (st->hdl->state_dump) {
CHECK_MALLOC_DO( (*st->hdl->state_dump)( FD_DUMP_STD_PARAMS, st->state),
fd_dump_extend( FD_DUMP_STD_PARAMS, "[dumper error]"));
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "<%p>", st->state), return NULL);
}
}
pthread_cleanup_pop(0);
CHECK_POSIX_DO( pthread_mutex_unlock(&session->stlock), /* ignore */ );
}
}
return *buf;
}
DECLARE_FD_DUMP_PROTOTYPE(fd_sess_dump_hdl, struct session_handler * handler)
{
FD_DUMP_HANDLE_OFFSET();
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{sesshdl}(@%p): ", handler), return NULL);
if (!VALIDATE_SH(handler)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "i:%d cl:%p d:%p o:%p", handler->id, handler->cleanup, handler->state_dump, handler->opaque), return NULL);
}
return *buf;
}
int fd_sess_getcount(uint32_t *cnt)
{
CHECK_PARAMS(cnt);
CHECK_POSIX( pthread_mutex_lock( &exp_lock ) );
*cnt = sess_cnt;
CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) );
return 0;
}
freeDiameter/libfdproto/version.c 0000644 0001750 0001750 00000005324 13324704440 016464 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
#include "fdproto-internal.h"
#include
#ifdef FD_PROJECT_VERSION_HG
# define FD_LIBFDPROTO_VERSION \
_stringize(FD_PROJECT_VERSION_MAJOR) "." _stringize(FD_PROJECT_VERSION_MINOR) "." _stringize(FD_PROJECT_VERSION_REV) "-" FD_PROJECT_VERSION_HG_VAL
#else
# define FD_LIBFDPROTO_VERSION \
_stringize(FD_PROJECT_VERSION_MAJOR) "." _stringize(FD_PROJECT_VERSION_MINOR) "." _stringize(FD_PROJECT_VERSION_REV)
#endif
const char fd_libproto_version[] = FD_LIBFDPROTO_VERSION;
freeDiameter/libfdproto/rt_data.c 0000644 0001750 0001750 00000026146 13324704440 016422 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
/* Routing module helpers.
*
* This file provides support for the rt_data structure manipulation.
*/
#include "fdproto-internal.h"
/* Structure that contains the routing data for a message */
struct rt_data {
int extracted; /* if 0, candidates is ordered by diamid, otherwise the order is unspecified. This also counts the number of times the message was (re-)sent, as a side effect */
struct fd_list candidates; /* All the candidates. Items are struct rtd_candidate. */
struct fd_list errors; /* All errors received from other peers for this message */
};
/* Items of the errors list */
struct rtd_error {
struct fd_list chain; /* link in the list, ordered by nexthop (fd_os_cmp) */
DiamId_t nexthop;/* the peer the message was sent to */
size_t nexthoplen; /* cached string length */
DiamId_t erh; /* the origin of the error */
size_t erhlen; /* cached string length */
uint32_t code; /* the error code */
};
/* Create a new structure to store routing data */
int fd_rtd_init(struct rt_data ** rtd)
{
struct rt_data *new;
TRACE_ENTRY("%p", rtd);
CHECK_PARAMS(rtd);
/* Alloc the structure */
CHECK_MALLOC( new = malloc(sizeof(struct rt_data)) );
memset(new, 0, sizeof(struct rt_data) );
fd_list_init(&new->candidates, new);
fd_list_init(&new->errors, new);
*rtd = new;
return 0;
}
/* Destroy the routing data */
void fd_rtd_free(struct rt_data ** rtd)
{
struct rt_data *old;
TRACE_ENTRY("%p", rtd);
CHECK_PARAMS_DO(rtd, return );
old = *rtd;
*rtd = NULL;
while (!FD_IS_LIST_EMPTY(&old->candidates)) {
struct rtd_candidate * c = (struct rtd_candidate *) old->candidates.next;
fd_list_unlink(&c->chain);
free(c->diamid);
free(c->realm);
free(c);
}
while (!FD_IS_LIST_EMPTY(&old->errors)) {
struct rtd_error * c = (struct rtd_error *) old->errors.next;
fd_list_unlink(&c->chain);
free(c->nexthop);
free(c->erh);
free(c);
}
free(old);
return;
}
/* Add a peer to the candidates list. The source is our local peer list, so no need to care for the case here. */
int fd_rtd_candidate_add(struct rt_data * rtd, DiamId_t peerid, size_t peeridlen, DiamId_t realm, size_t realmlen)
{
struct fd_list * prev;
struct rtd_candidate * new;
TRACE_ENTRY("%p %p %zd %p %zd", rtd, peerid, peeridlen, realm, realmlen);
CHECK_PARAMS(rtd && peerid && peeridlen);
/* Since the peers are ordered when they are added (fd_g_activ_peers) we search for the position from the end -- this should be efficient */
for (prev = rtd->candidates.prev; prev != &rtd->candidates; prev = prev->prev) {
struct rtd_candidate * cp = (struct rtd_candidate *) prev;
int cmp = fd_os_cmp(peerid, peeridlen, cp->diamid, cp->diamidlen);
if (cmp > 0)
break;
if (cmp == 0)
/* The candidate is already in the list */
return 0;
}
/* Create the new entry */
CHECK_MALLOC( new = malloc(sizeof(struct rtd_candidate)) );
memset(new, 0, sizeof(struct rtd_candidate) );
fd_list_init(&new->chain, new);
CHECK_MALLOC( new->diamid = os0dup(peerid, peeridlen) )
new->diamidlen = peeridlen;
if (realm) {
CHECK_MALLOC( new->realm = os0dup(realm, realmlen) )
new->realmlen = realmlen;
}
/* insert in the list at the correct position */
fd_list_insert_after(prev, &new->chain);
return 0;
}
/* Remove a peer from the candidates (if it is found). Case insensitive search since the names are received from other peers */
void fd_rtd_candidate_del(struct rt_data * rtd, uint8_t * id, size_t idsz)
{
struct fd_list * li;
TRACE_ENTRY("%p %p %zd", rtd, id, idsz);
CHECK_PARAMS_DO( rtd && id && idsz, return );
if (!fd_os_is_valid_DiameterIdentity(id, idsz))
/* it cannot be in the list */
return;
for (li = rtd->candidates.next; li != &rtd->candidates; li = li->next) {
struct rtd_candidate * c = (struct rtd_candidate *) li;
int cont;
int cmp = fd_os_almostcasesrch(id, idsz, c->diamid, c->diamidlen, &cont);
if (!cmp) {
/* Found it! Remove it */
fd_list_unlink(&c->chain);
free(c->diamid);
free(c->realm);
free(c);
break;
}
if (cont)
continue;
/* The list is guaranteed to be ordered only if not extracted */
if (! rtd->extracted)
break;
}
return;
}
/* If a peer returned a protocol error for this message, save it so that we don't try to send it there again.
Case insensitive search since the names are received from other peers*/
int fd_rtd_error_add(struct rt_data * rtd, DiamId_t sentto, size_t senttolen, uint8_t * origin, size_t originsz, uint32_t rcode, struct fd_list ** candidates, int * sendingattemtps)
{
struct fd_list * li;
int match = 0;
TRACE_ENTRY("%p %p %zd %p %zd %u %p %p", rtd, sentto, senttolen, origin, originsz, rcode, candidates, sendingattemtps);
CHECK_PARAMS( rtd && sentto && senttolen ); /* origin may be NULL */
/* First add the new error entry */
for (li = rtd->errors.next; li != &rtd->errors; li = li->next) {
struct rtd_error * e = (struct rtd_error *) li;
int cmp = fd_os_cmp(sentto, senttolen, e->nexthop, e->nexthoplen);
if (cmp > 0)
continue;
if (!cmp)
match = 1;
break;
}
/* If we already had this entry, we should not have sent the message again to this peer... anyway, let's close our eyes. */
/* in the normal case, we save the error */
if (!match) {
/* Add a new entry in the error list */
struct rtd_error * new;
CHECK_MALLOC( new = malloc(sizeof(struct rtd_error)) );
memset(new, 0, sizeof(struct rtd_error));
fd_list_init(&new->chain, NULL);
CHECK_MALLOC(new->nexthop = os0dup(sentto, senttolen));
new->nexthoplen = senttolen;
if (origin) {
if (!originsz) {
originsz=strlen((char *)origin);
} else {
if (!fd_os_is_valid_DiameterIdentity(origin, originsz)){
TRACE_DEBUG(FULL, "Received error %d from peer with invalid Origin-Host AVP, not saved", rcode);
origin = NULL;
goto after_origin;
}
}
CHECK_MALLOC( new->erh = (DiamId_t)os0dup(origin, originsz) );
new->erhlen = originsz;
}
after_origin:
new->code = rcode;
fd_list_insert_before(li, &new->chain);
}
/* Finally, remove this (these) peers from the candidate list */
fd_rtd_candidate_del(rtd, (os0_t)sentto, senttolen);
if (origin)
fd_rtd_candidate_del(rtd, origin, originsz);
if (candidates)
*candidates = &rtd->candidates;
if (sendingattemtps)
*sendingattemtps = rtd->extracted;
/* Done! */
return 0;
}
/* Only retrieve the number of times this message has been processed by the routing-out mechanism (i.e. number of times it was failed over) */
int fd_rtd_get_nb_attempts(struct rt_data * rtd, int * sendingattemtps)
{
TRACE_ENTRY("%p %p", rtd, sendingattemtps);
CHECK_PARAMS( rtd && sendingattemtps );
*sendingattemtps = rtd->extracted;
/* Done! */
return 0;
}
/* Extract the list of valid candidates, and initialize their scores */
void fd_rtd_candidate_extract(struct rt_data * rtd, struct fd_list ** candidates, int ini_score)
{
struct fd_list * li;
TRACE_ENTRY("%p %p", rtd, candidates);
CHECK_PARAMS_DO( candidates, return );
CHECK_PARAMS_DO( rtd, { *candidates = NULL; return; } );
*candidates = &rtd->candidates;
/* Reset all scores to INITIAL score */
for (li = rtd->candidates.next; li != &rtd->candidates; li = li->next) {
struct rtd_candidate * c = (struct rtd_candidate *) li;
c->score = ini_score;
}
rtd->extracted += 1;
return;
}
/* Reorder the list of peers. If several peer have the same highest score, they are randomized. */
int fd_rtd_candidate_reorder(struct fd_list * candidates)
{
struct fd_list unordered = FD_LIST_INITIALIZER(unordered), *li;
struct fd_list highest = FD_LIST_INITIALIZER(highest);
int hs = -1;
TRACE_ENTRY("%p", candidates);
CHECK_PARAMS( candidates );
/* First, move all items from candidates to the undordered list */
fd_list_move_end(&unordered, candidates);
/* Now extract each element from unordered and add it back to list ordered by score */
while (!FD_IS_LIST_EMPTY(&unordered)) {
struct rtd_candidate * c = (struct rtd_candidate *) unordered.next;
fd_list_unlink(&c->chain);
/* If this candidate has a higher score than the previous ones */
if (c->score > hs) {
/* Then we move the previous high score items at end of the list */
fd_list_move_end(candidates, &highest);
/* And the new high score is set */
hs = c->score;
}
/* If this candidate equals the higher score, add it into highest list at a random place */
if (c->score == hs) {
if (rand() & 1) {
fd_list_insert_after(&highest, &c->chain);
} else {
fd_list_insert_before(&highest, &c->chain);
}
/* Otherwise, insert at normal place in the list */
} else {
/* Find the position in ordered candidates list */
for (li = candidates->next; li != candidates; li = li->next) {
struct rtd_candidate * cnext = (struct rtd_candidate *) li;
if (cnext->score >= c->score)
break;
}
/* Add the element there */
fd_list_insert_before(li, &c->chain);
}
}
/* Now simply move back all the "highest" candidates at the end of the list */
fd_list_move_end(candidates, &highest);
return 0;
}
freeDiameter/libfdproto/log.c 0000644 0001750 0001750 00000023302 13324704440 015554 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2015, 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. *
*********************************************************************************************************/
#include "fdproto-internal.h"
#include
pthread_mutex_t fd_log_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_key_t fd_log_thname;
int fd_g_debug_lvl = FD_LOG_NOTICE;
static void fd_internal_logger( int, const char *, va_list );
static int use_colors = 0; /* 0: not init, 1: yes, 2: no */
/* These may be used to pass specific debug requests via the command-line parameters */
char * fd_debug_one_function = NULL;
char * fd_debug_one_file = NULL;
/* Useless function, only to ease setting up a breakpoint in gdb (break fd_breakhere) -- use TRACE_HERE */
int fd_breaks = 0;
int fd_breakhere(void) { return ++fd_breaks; }
/* Allow passing of the log and debug information from base stack to extensions */
void (*fd_logger)( int loglevel, const char * format, va_list args ) = fd_internal_logger;
/* Register an external call back for tracing and debug */
int fd_log_handler_register( void (*logger)(int loglevel, const char * format, va_list args) )
{
CHECK_PARAMS( logger );
if ( fd_logger != fd_internal_logger )
{
return EALREADY; /* only one registration allowed */
}
else
{
fd_logger = logger;
}
return 0;
}
/* Implement a simple reset function here */
int fd_log_handler_unregister ( void )
{
fd_logger = fd_internal_logger;
return 0; /* Successfull in all cases. */
}
static void fd_cleanup_mutex_silent( void * mutex )
{
(void)pthread_mutex_unlock((pthread_mutex_t *)mutex);
}
static void fd_internal_logger( int printlevel, const char *format, va_list ap )
{
char buf[25];
/* Do we need to trace this ? */
if (printlevel < fd_g_debug_lvl)
return;
/* add timestamp */
printf("%s ", fd_log_time(NULL, buf, sizeof(buf),
#if (defined(DEBUG) && defined(DEBUG_WITH_META))
1, 1
#else /* (defined(DEBUG) && defined(DEBUG_WITH_META)) */
0, 0
#endif /* (defined(DEBUG) && defined(DEBUG_WITH_META)) */
));
/* Use colors on stdout ? */
if (!use_colors) {
if (isatty(STDOUT_FILENO))
use_colors = 1;
else
use_colors = 2;
}
switch(printlevel) {
case FD_LOG_ANNOYING: printf("%s A ", (use_colors == 1) ? "\e[0;37m" : ""); break;
case FD_LOG_DEBUG: printf("%s DBG ", (use_colors == 1) ? "\e[0;37m" : ""); break;
case FD_LOG_NOTICE: printf("%sNOTI ", (use_colors == 1) ? "\e[1;37m" : ""); break;
case FD_LOG_ERROR: printf("%sERROR ", (use_colors == 1) ? "\e[0;31m" : ""); break;
case FD_LOG_FATAL: printf("%sFATAL! ", (use_colors == 1) ? "\e[0;31m" : ""); break;
default: printf("%s ??? ", (use_colors == 1) ? "\e[0;31m" : "");
}
vprintf(format, ap);
if (use_colors == 1)
printf("\e[00m");
printf("\n");
fflush(stdout);
}
/* Log a debug message */
void fd_log ( int loglevel, const char * format, ... )
{
va_list ap;
(void)pthread_mutex_lock(&fd_log_lock);
pthread_cleanup_push(fd_cleanup_mutex_silent, &fd_log_lock);
va_start(ap, format);
fd_logger(loglevel, format, ap);
va_end(ap);
pthread_cleanup_pop(0);
(void)pthread_mutex_unlock(&fd_log_lock);
}
/* Log a debug message */
void fd_log_va ( int loglevel, const char * format, va_list args )
{
(void)pthread_mutex_lock(&fd_log_lock);
pthread_cleanup_push(fd_cleanup_mutex_silent, &fd_log_lock);
fd_logger(loglevel, format, args);
pthread_cleanup_pop(0);
(void)pthread_mutex_unlock(&fd_log_lock);
}
/* Function to set the thread's friendly name */
void fd_log_threadname ( const char * name )
{
void * val = NULL;
TRACE_ENTRY("%p(%s)", name, name?:"/");
/* First, check if a value is already assigned to the current thread */
val = pthread_getspecific(fd_log_thname);
if (TRACE_BOOL(ANNOYING)) {
if (val) {
fd_log_debug("(Thread '%s' renamed to '%s')", (char *)val, name?:"(nil)");
} else {
fd_log_debug("(Thread %p named '%s')", (void *)pthread_self(), name?:"(nil)");
}
}
if (val != NULL) {
free(val);
}
/* Now create the new string */
if (name == NULL) {
CHECK_POSIX_DO( pthread_setspecific(fd_log_thname, NULL), /* continue */);
return;
}
CHECK_MALLOC_DO( val = strdup(name), return );
CHECK_POSIX_DO( pthread_setspecific(fd_log_thname, val), /* continue */);
return;
}
/* Write time into a buffer */
char * fd_log_time ( struct timespec * ts, char * buf, size_t len, int incl_date, int incl_ms )
{
int ret;
size_t offset = 0;
struct timespec tp;
struct tm tm;
/* Get current time */
if (!ts) {
ret = clock_gettime(CLOCK_REALTIME, &tp);
if (ret != 0) {
snprintf(buf, len, "%s", strerror(ret));
return buf;
}
ts = &tp;
}
offset += strftime(buf + offset, len - offset, incl_date?"%D,%T":"%T", localtime_r( &ts->tv_sec , &tm ));
if (incl_ms)
offset += snprintf(buf + offset, len - offset, ".%6.6ld", ts->tv_nsec / 1000);
return buf;
}
static size_t sys_mempagesz = 0;
static size_t get_mempagesz(void) {
if (!sys_mempagesz) {
sys_mempagesz = sysconf(_SC_PAGESIZE); /* We alloc buffer by memory pages for efficiency. This can be readjusted if too memory consuming */
if (sys_mempagesz <= 0)
sys_mempagesz = 256; /* default size if above call failed */
}
return sys_mempagesz;
}
/* Helper function for fd_*_dump. Prints the format string from 'offset' into '*buf', extends if needed. The location of buf can be updated by this function. */
char * fd_dump_extend(char ** buf, size_t *len, size_t *offset, const char * format, ... )
{
va_list ap;
int to_write;
size_t o = 0;
size_t mempagesz = get_mempagesz();
/* we do not TRACE_ENTRY this one on purpose */
CHECK_PARAMS_DO(buf && len, return NULL);
if (*buf == NULL) {
CHECK_MALLOC_DO(*buf = malloc(mempagesz), return NULL);
*len = mempagesz;
}
if (offset)
o = *offset;
va_start(ap, format);
to_write = vsnprintf(*buf + o, *len - o, format, ap);
va_end(ap);
if (to_write + o >= *len) {
/* There was no room in the buffer, we extend and redo */
size_t new_len = (((to_write + o) / mempagesz) + 1) * mempagesz;
CHECK_MALLOC_DO(*buf = realloc(*buf, new_len), return NULL);
*len = new_len;
va_start(ap, format);
to_write = vsnprintf(*buf + o, *len - o, format, ap);
va_end(ap);
}
if (offset)
*offset += to_write;
return *buf;
}
char * fd_dump_extend_hexdump(char ** buf, size_t *len, size_t *offset, uint8_t *data, size_t datalen, size_t trunc, size_t wrap )
{
int truncated = 0;
size_t towrite = 0;
size_t o = 0;
int i;
char * p;
size_t mempagesz = get_mempagesz();
#define TRUNK_MARK "[...]"
CHECK_PARAMS_DO(buf && len && data, return NULL);
if (trunc && (datalen > trunc)) {
datalen = trunc;
truncated = 1;
}
towrite = datalen * 2;
if (wrap)
towrite += datalen / wrap; /* add 1 '\n' every wrap byte */
if (truncated)
towrite += CONSTSTRLEN(TRUNK_MARK);
if (offset)
o = *offset;
if (*buf == NULL) {
/* Directly allocate the size we need */
*len = (((towrite + o) / mempagesz) + 1 ) * mempagesz;
CHECK_MALLOC_DO(*buf = malloc(*len), return NULL);
} else if ((towrite + o) >= *len) {
/* There is no room in the buffer, we extend and redo */
size_t new_len = (((towrite + o) / mempagesz) + 1) * mempagesz;
CHECK_MALLOC_DO(*buf = realloc(*buf, new_len), return NULL);
*len = new_len;
}
p = *buf + o;
for (i = 0; i < datalen; i++) {
sprintf(p, "%02hhX", data[i]);
p+=2;
if ((wrap) && ((i+1) % wrap == 0)) {
*p++='\n'; *p ='\0'; /* we want to ensure the buffer is always 0-terminated */
}
}
if (truncated)
memcpy(p, TRUNK_MARK, CONSTSTRLEN(TRUNK_MARK));
if (offset)
*offset += towrite;
return *buf;
}
freeDiameter/libfdproto/dictionary_functions.c 0000644 0001750 0001750 00000027724 13324704440 021244 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2015, 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. *
*********************************************************************************************************/
#include "fdproto-internal.h"
#include
/* This file contains helpers functions to be reused as callbacks in the struct dict_type_data structure.
There are three callbacks there:
- type_encode :
- type_interpret :
Those two callbacks allow to manipulate more natural structures of data in the code, and to
map transparently these natural structures with the AVP-encoded format by calling the functions
msg_avp_value_encode or msg_avp_value_interpret.
- type_dump :
This callback if provided gives a more human-readable debug information.
*/
/****************************/
/* Address AVP type */
/****************************/
/* The interpret and encode functions work with a "struct sockaddr_storage" pointer for mapping
the contents of the AVP */
int fd_dictfct_Address_encode(void * data, union avp_value * avp_value)
{
sSS * ss = (sSS *) data;
uint16_t AddressType = 0;
size_t size = 0;
unsigned char * buf = NULL;
TRACE_ENTRY("%p %p", data, avp_value);
CHECK_PARAMS( data && avp_value );
switch (ss->ss_family) {
case AF_INET:
{
/* We are encoding an IP address */
sSA4 * sin = (sSA4 *)ss;
AddressType = 1;/* see http://www.iana.org/assignments/address-family-numbers/ */
size = 6; /* 2 for AddressType + 4 for data */
CHECK_MALLOC( buf = malloc(size) );
/* may not work because of alignment: *(uint32_t *)(buf+2) = htonl(sin->sin_addr.s_addr); */
memcpy(buf + 2, &sin->sin_addr.s_addr, 4);
}
break;
case AF_INET6:
{
/* We are encoding an IPv6 address */
sSA6 * sin6 = (sSA6 *)ss;
AddressType = 2;/* see http://www.iana.org/assignments/address-family-numbers/ */
size = 18; /* 2 for AddressType + 16 for data */
CHECK_MALLOC( buf = malloc(size) );
/* The order is already good here */
memcpy(buf + 2, &sin6->sin6_addr.s6_addr, 16);
}
break;
default:
CHECK_PARAMS( AddressType = 0 );
}
*(uint16_t *)buf = htons(AddressType);
avp_value->os.len = size;
avp_value->os.data = buf;
return 0;
}
int fd_dictfct_Address_interpret(union avp_value * avp_value, void * interpreted)
{
uint16_t AddressType = 0;
unsigned char * buf;
TRACE_ENTRY("%p %p", avp_value, interpreted);
CHECK_PARAMS( avp_value && interpreted && (avp_value->os.len >= 2) );
AddressType = ntohs(*(uint16_t *)avp_value->os.data);
buf = &avp_value->os.data[2];
switch (AddressType) {
case 1 /* IP */:
{
sSA4 * sin = (sSA4 *)interpreted;
CHECK_PARAMS( avp_value->os.len == 6 );
sin->sin_family = AF_INET;
/* sin->sin_addr.s_addr = ntohl( * (uint32_t *) buf); -- may not work because of bad alignment */
memcpy(&sin->sin_addr.s_addr, buf, 4);
}
break;
case 2 /* IP6 */:
{
sSA6 * sin6 = (sSA6 *)interpreted;
CHECK_PARAMS( avp_value->os.len == 18 );
sin6->sin6_family = AF_INET6;
memcpy(&sin6->sin6_addr.s6_addr, buf, 16);
}
break;
default:
CHECK_PARAMS( AddressType = 0 );
}
return 0;
}
/* Dump the content of an Address AVP */
DECLARE_FD_DUMP_PROTOTYPE(fd_dictfct_Address_dump, union avp_value * avp_value)
{
union {
sSA sa;
sSS ss;
sSA4 sin;
sSA6 sin6;
} s;
uint16_t fam;
FD_DUMP_HANDLE_OFFSET();
memset(&s, 0, sizeof(s));
/* The first two octets represent the address family, http://www.iana.org/assignments/address-family-numbers/ */
if (avp_value->os.len < 2) {
CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "[invalid length: %zd]", avp_value->os.len), return NULL);
return *buf;
}
/* Following octets are the address in network byte order already */
fam = avp_value->os.data[0] << 8 | avp_value->os.data[1];
switch (fam) {
case 1:
/* IP */
s.sa.sa_family = AF_INET;
if ((avp_value->os.len != 6) && (avp_value->os.len != 8)) {
CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "[invalid IP length: %zd]", avp_value->os.len), return NULL);
return *buf;
}
memcpy(&s.sin.sin_addr.s_addr, avp_value->os.data + 2, 4);
if (avp_value->os.len == 8)
memcpy(&s.sin.sin_port, avp_value->os.data + 6, 2);
break;
case 2:
/* IP6 */
s.sa.sa_family = AF_INET6;
if ((avp_value->os.len != 18) && (avp_value->os.len != 20)) {
CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "[invalid IP6 length: %zd]", avp_value->os.len), return NULL);
return *buf;
}
memcpy(&s.sin6.sin6_addr.s6_addr, avp_value->os.data + 2, 16);
if (avp_value->os.len == 20)
memcpy(&s.sin6.sin6_port, avp_value->os.data + 18, 2);
break;
case 8:
/* E.164 */
CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "%.*s", (int)(avp_value->os.len-2), avp_value->os.data+2), return NULL);
return *buf;
default:
CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "[unsupported family: 0x%hx]", fam), return NULL);
return *buf;
}
return fd_sa_dump(FD_DUMP_STD_PARAMS, &s.sa, NI_NUMERICHOST);
}
/*******************************/
/* UTF8String AVP type */
/*******************************/
/* Dump the AVP in a natural human-readable format. This dumps the complete length of the AVP, it is up to the caller to truncate if needed */
DECLARE_FD_DUMP_PROTOTYPE(fd_dictfct_UTF8String_dump, union avp_value * avp_value)
{
size_t l;
FD_DUMP_HANDLE_OFFSET();
l = avp_value->os.len;
/* Just in case the string ends in invalid UTF-8 chars, we shorten it */
while ((l > 0) && (avp_value->os.data[l - 1] & 0x80)) {
/* this byte is start or cont. of multibyte sequence, as we do not know the next byte we need to delete it. */
l--;
if (avp_value->os.data[l] & 0x40)
break; /* This was a start byte, we can stop the loop */
}
CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "\"%.*s\"", (int)l, (char *)avp_value->os.data), return NULL);
return *buf;
}
/*******************************/
/* Time AVP type */
/*******************************/
/* The interpret and encode functions work with a "time_t" pointer for mapping
the contents of the AVP */
/* Unix Epoch starts 1970-01-01, NTP 0 is at 1900-01-01 */
#define DIFF_EPOCH_TO_NTP ((365*(1970-1900) + 17ul) * 24 * 60 * 60)
static int diameter_string_to_time_t(const char *str, size_t len, time_t *result) {
time_t time_stamp;
CHECK_PARAMS(len == 4);
time_stamp = (((unsigned long)(str[0]&0xff))<<24) + ((str[1]&0xff)<<16) + ((str[2]&0xff)<<8) + ((str[3]&0xff));
time_stamp -= DIFF_EPOCH_TO_NTP;
#ifdef FIX__NEEDED_FOR_YEAR_2036_AND_LATER
/* NTP overflows in 2036; after that, values start at zero again */
#define NTP_OVERFLOW_CORRECTION (0x100000000ull)
/* XXX: debug and find correct conversion */
if (str[0] & 0x80 == 0x00) {
time_stamp += NTP_OVERFLOW_CORRECTION;
}
#endif
*result = time_stamp;
return 0;
}
static int time_t_to_diameter_string(time_t time_stamp, char **result) {
uint64_t out = time_stamp;
char *conv;
/* XXX: 2036 fix */
out += DIFF_EPOCH_TO_NTP;
CHECK_PARAMS( (out >> 32) == 0);
CHECK_MALLOC(conv=(char *)malloc(5));
conv[0] = (out>>24) & 0xff;
conv[1] = (out>>16) & 0xff;
conv[2] = (out>> 8) & 0xff;
conv[3] = out & 0xff;
conv[4] = '\0';
*result = conv;
return 0;
}
int fd_dictfct_Time_encode(void * data, union avp_value * avp_value)
{
char * buf;
size_t len;
TRACE_ENTRY("%p %p", data, avp_value);
CHECK_PARAMS( data && avp_value );
CHECK_FCT( time_t_to_diameter_string( *((time_t *)data), &buf) );
/* FIXME: return len from the function above? */ len = 4;
avp_value->os.len = len;
avp_value->os.data = (uint8_t *)buf;
return 0;
}
int fd_dictfct_Time_interpret(union avp_value * avp_value, void * interpreted)
{
TRACE_ENTRY("%p %p", avp_value, interpreted);
CHECK_PARAMS( avp_value && interpreted );
return diameter_string_to_time_t((const char *)avp_value->os.data, avp_value->os.len, interpreted);
}
static void _format_offs (long offset, char *buf) {
int offs_hours, offs_minutes, sgn = 1;
if (offset < 0) {
offset = -offset;
sgn = 1;
}
offs_hours = (int)(offset/3600);
offs_minutes = (offset%3600)/60;
char* s = buf;
*(s++) = sgn == 1 ? '+' : '-';
*(s++) = (char)(offs_hours/10) + '0';
*(s++) = offs_hours%10 + '0';
if (offs_minutes == 0) {
*(s++) = '\0';
} else {
*(s++) = (char)(offs_minutes/10) + '0';
*(s++) = offs_minutes%10 + '0';
*(s++) = '\0';
}
}
DECLARE_FD_DUMP_PROTOTYPE(fd_dictfct_Time_dump, union avp_value * avp_value)
{
time_t val;
struct tm conv;
char tz_buf[7];
FD_DUMP_HANDLE_OFFSET();
if (avp_value->os.len != 4) {
CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "[invalid length: %zd]", avp_value->os.len), return NULL);
return *buf;
}
if (diameter_string_to_time_t((char *)avp_value->os.data, avp_value->os.len, &val) != 0) {
CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "[time conversion error]"), return NULL);
return *buf;
}
CHECK_MALLOC_DO( localtime_r(&val, &conv), return NULL);
_format_offs(conv.tm_gmtoff, tz_buf);
CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "%d%02d%02dT%02d%02d%02d%s", conv.tm_year+1900, conv.tm_mon+1, conv.tm_mday, conv.tm_hour, conv.tm_min, conv.tm_sec, tz_buf), return NULL);
return *buf;
}
/* Check that a given AVP value contains all the characters from data in the same order */
static char error_message[80];
int fd_dictfct_CharInOS_check(void * data, union avp_value * val, char ** error_msg)
{
char * inChar = data;
char * inData = (char *)val->os.data;
int i = 0;
CHECK_PARAMS(data);
while (*inChar != '\0') {
while (i < val->os.len) {
if (*inChar == inData[i++]) {
inChar++;
break;
}
}
if (i >= val->os.len)
break;
}
if (*inChar == '\0')
return 0;
if (error_msg) {
snprintf(error_message, sizeof(error_message), "Could not find '%c' in AVP", *inChar);
*error_msg = error_message;
}
return EBADMSG;
}
freeDiameter/libfdproto/CMakeLists.txt 0000644 0001750 0001750 00000002342 13324704440 017370 0 ustar ruben ruben # Name of the subproject
Project("libfdproto" C)
# Configuration for newer cmake
cmake_policy(VERSION 2.6)
if (POLICY CMP0022)
cmake_policy(SET CMP0022 OLD)
endif (POLICY CMP0022)
# List of source files for the library
SET(LFDPROTO_SRC
fdproto-internal.h
dictionary.c
dictionary_functions.c
dispatch.c
fifo.c
init.c
lists.c
log.c
messages.c
ostr.c
portability.c
rt_data.c
sessions.c
utils.c
version.c
)
# Save the list of files for testcases in the core's directory
SET(LFDPROTO_SRC ${LFDPROTO_SRC} PARENT_SCOPE)
# Include path
INCLUDE_DIRECTORIES(${LFDPROTO_INCLUDES})
# Build as a shared library
ADD_LIBRARY(libfdproto SHARED ${LFDPROTO_SRC})
ADD_DEPENDENCIES(libfdproto version_information)
# Avoid the liblib name, and set the version
SET_TARGET_PROPERTIES(libfdproto PROPERTIES
OUTPUT_NAME "fdproto"
SOVERSION ${FD_PROJECT_VERSION_API}
VERSION ${FD_PROJECT_VERSION_MAJOR}.${FD_PROJECT_VERSION_MINOR}.${FD_PROJECT_VERSION_REV}
LINK_INTERFACE_LIBRARIES "${LFDPROTO_LINK_INTERFACES}")
# The library itself needs other libraries
TARGET_LINK_LIBRARIES(libfdproto ${LFDPROTO_LIBS})
####
## INSTALL section ##
INSTALL(TARGETS libfdproto
LIBRARY DESTINATION ${INSTALL_LIBRARY_SUFFIX}
COMPONENT freeDiameter-common)
freeDiameter/libfdproto/messages.c 0000644 0001750 0001750 00000270375 13324704466 016630 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2015, 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. *
*********************************************************************************************************/
/* Messages module.
*
* This module allows to manipulate the msg and avp structures that represents a Diameter message in memory.
*/
#include "fdproto-internal.h"
#include
/* Type of object */
enum msg_objtype {
MSG_MSG = 1,
MSG_AVP
};
/* Chaining of elements as a free hierarchy */
struct msg_avp_chain {
struct fd_list chaining; /* Chaining information at this level. */
struct fd_list children; /* sentinel for the children of this object */
enum msg_objtype type; /* Type of this object, _MSG_MSG or _MSG_AVP */
};
/* Return the chain information from an AVP or MSG. Since it's the first field, we just cast */
#define _C(_x) ((struct msg_avp_chain *)(_x))
/* Some details about chaining:
*
* A message is made of a header ( msg ) and 0 or more AVPs ( avp ).
* The structure is a kind of tree, where some AVPs (grouped AVPs) can contain other AVPs.
* Example:
* msg
* |-avp
* |-gavp
* | |-avp
* | |-avp
* | \-avp
* |-avp
* \-avp
*
* Each item (msg or avp) structure begins with a msg_avp_chain structure.
* The element at the top of the hierarchy (msg in our example) has all the fields of its "chaining" equal to the same value.
*
* All elements at the same level are linked by their "chaining" list.
* The "children" list is the sentinel for the lists of children of this element.
*/
/* The following definitions are used to recognize objects in memory. */
#define MSG_MSG_EYEC (0x11355463)
#define MSG_AVP_EYEC (0x11355467)
/* The following structure represents an AVP instance. */
struct avp {
struct msg_avp_chain avp_chain; /* Chaining information of this AVP */
int avp_eyec; /* Must be equal to MSG_AVP_EYEC */
struct dict_object *avp_model; /* If not NULL, pointer to the dictionary object of this avp */
struct {
avp_code_t mnf_code;
vendor_id_t mnf_vendor;
} avp_model_not_found; /* When model resolution has failed, store a copy of the data here to avoid searching again */
struct avp_hdr avp_public; /* AVP data that can be managed by other modules */
uint8_t *avp_source; /* If the message was parsed from a buffer, pointer to the AVP data start in the buffer. */
uint8_t *avp_rawdata; /* when the data can not be interpreted, the raw data is copied here. The header is not part of it. */
size_t avp_rawlen; /* The length of the raw buffer. */
union avp_value avp_storage; /* To avoid many alloc/free, store the integer values here and set avp_public.avp_data to &storage */
int avp_mustfreeos; /* 1 if an octetstring is malloc'd in avp_storage and must be freed. */
};
/* Macro to compute the AVP header size */
#define AVPHDRSZ_NOVEND 8
#define AVPHDRSZ_VENDOR 12
#define GETAVPHDRSZ( _flag ) ((_flag & AVP_FLAG_VENDOR) ? AVPHDRSZ_VENDOR : AVPHDRSZ_NOVEND)
/* Macro to cast a msg_avp_t */
#define _A(_x) ((struct avp *)(_x))
/* Check the type and eyecatcher */
#define CHECK_AVP(_x) ((_x) && (_C(_x)->type == MSG_AVP) && (_A(_x)->avp_eyec == MSG_AVP_EYEC))
/* The following structure represents an instance of a message (command and children AVPs). */
struct msg {
struct msg_avp_chain msg_chain; /* List of the AVPs in the message */
int msg_eyec; /* Must be equal to MSG_MSG_EYEC */
struct dict_object *msg_model; /* If not NULL, pointer to the dictionary object of this message */
struct {
command_code_t mnf_code;
uint8_t mnf_flags;
} msg_model_not_found; /* When model resolution has failed, store a copy of the data here to avoid searching again */
struct msg_hdr msg_public; /* Message data that can be managed by extensions. */
uint8_t *msg_rawbuffer; /* data buffer that was received, saved during fd_msg_parse_buffer and freed in fd_msg_parse_dict */
int msg_routable; /* Is this a routable message? (0: undef, 1: routable, 2: non routable) */
struct msg *msg_query; /* the associated query if the message is a received answer */
int msg_associated; /* and the counter part information in the query, to avoid double free */
struct rt_data *msg_rtdata; /* Routing list for the query */
struct session *msg_sess; /* Cached message session if any */
struct {
void (*anscb)(void *, struct msg **);
void (*expirecb)(void *, DiamId_t, size_t, struct msg **);
void * data;
struct timespec timeout;
} msg_cb; /* Callback to be called when an answer is received, or timeout expires, if not NULL */
DiamId_t msg_src_id; /* Diameter Id of the peer this message was received from. This string is malloc'd and must be freed */
size_t msg_src_id_len; /* cached length of this string */
struct fd_msg_pmdl msg_pmdl; /* list of permessagedata structures. */
};
/* Macro to compute the message header size */
#define GETMSGHDRSZ() 20
/* Macro to cast a msg_avp_t */
#define _M(_x) ((struct msg *)(_x))
/* Check the type and eyecatcher */
#define CHECK_MSG(_x) ((_x) && (_C(_x)->type == MSG_MSG) && (_M(_x)->msg_eyec == MSG_MSG_EYEC))
#define VALIDATE_OBJ(_x) ( (CHECK_MSG(_x)) || (CHECK_AVP(_x)) )
/* Macro to validate a MSGFL_ value */
#define CHECK_AVPFL(_fl) ( ((_fl) & (- (AVPFL_MAX << 1) )) == 0 )
#define CHECK_MSGFL(_fl) ( ((_fl) & (- (MSGFL_MAX << 1) )) == 0 )
/* initial sizes of AVP from their types, in bytes. */
static int avp_value_sizes[] = {
0, /* AVP_TYPE_GROUPED: size is dynamic */
0, /* AVP_TYPE_OCTETSTRING: size is dynamic */
4, /* AVP_TYPE_INTEGER32: size is 32 bits */
8, /* AVP_TYPE_INTEGER64: size is 64 bits */
4, /* AVP_TYPE_UNSIGNED32: size is 32 bits */
8, /* AVP_TYPE_UNSIGNED64: size is 64 bits */
4, /* AVP_TYPE_FLOAT32: size is 32 bits */
8 /* AVP_TYPE_FLOAT64: size is 64 bits */
};
#define CHECK_BASETYPE( _type ) ( ((_type) <= AVP_TYPE_MAX) && ((_type) >= 0) )
#define GETINITIALSIZE( _type, _vend ) (avp_value_sizes[ CHECK_BASETYPE(_type) ? (_type) : 0] + GETAVPHDRSZ(_vend))
/* Forward declaration */
static int parsedict_do_msg(struct dictionary * dict, struct msg * msg, int only_hdr, struct fd_pei *error_info);
/***************************************************************************************************************/
/* Creating objects */
/* Initialize a msg_avp_chain structure */
static void init_chain(struct msg_avp_chain * chain, int type)
{
fd_list_init( &chain->chaining, (void *)chain);
fd_list_init( &chain->children, (void *)chain);
chain->type = type;
}
/* Initialize a new AVP object */
static void init_avp ( struct avp * avp )
{
TRACE_ENTRY("%p", avp);
memset(avp, 0, sizeof(struct avp));
init_chain( &avp->avp_chain, MSG_AVP);
avp->avp_eyec = MSG_AVP_EYEC;
}
/* Initialize a new MSG object */
static void init_msg ( struct msg * msg )
{
TRACE_ENTRY("%p", msg);
memset(msg, 0, sizeof(struct msg));
init_chain( &msg->msg_chain, MSG_MSG);
msg->msg_eyec = MSG_MSG_EYEC;
fd_list_init(&msg->msg_pmdl.sentinel, NULL);
CHECK_POSIX_DO( pthread_mutex_init(&msg->msg_pmdl.lock, NULL), );
}
/* Create a new AVP instance */
int fd_msg_avp_new ( struct dict_object * model, int flags, struct avp ** avp )
{
struct avp *new = NULL;
TRACE_ENTRY("%p %x %p", model, flags, avp);
/* Check the parameters */
CHECK_PARAMS( avp && CHECK_AVPFL(flags) );
if (model) {
enum dict_object_type dicttype;
CHECK_PARAMS( (fd_dict_gettype(model, &dicttype) == 0) && (dicttype == DICT_AVP) );
}
/* Create a new object */
CHECK_MALLOC( new = malloc (sizeof(struct avp)) );
/* Initialize the fields */
init_avp(new);
if (model) {
struct dict_avp_data dictdata;
CHECK_FCT_DO( fd_dict_getval(model, &dictdata), { free(new); return __ret__; } );
new->avp_model = model;
new->avp_public.avp_code = dictdata.avp_code;
new->avp_public.avp_flags = dictdata.avp_flag_val;
new->avp_public.avp_len = GETINITIALSIZE(dictdata.avp_basetype, dictdata.avp_flag_val );
new->avp_public.avp_vendor = dictdata.avp_vendor;
}
if (flags & AVPFL_SET_BLANK_VALUE) {
new->avp_public.avp_value = &new->avp_storage;
}
if (flags & AVPFL_SET_RAWDATA_FROM_AVP) {
new->avp_rawlen = (*avp)->avp_public.avp_len - GETAVPHDRSZ( (*avp)->avp_public.avp_flags );
if (new->avp_rawlen) {
CHECK_MALLOC_DO( new->avp_rawdata = malloc(new->avp_rawlen), { free(new); return __ret__; } );
memset(new->avp_rawdata, 0x00, new->avp_rawlen);
}
}
/* The new object is ready, return */
*avp = new;
return 0;
}
/* Create a new message instance */
int fd_msg_new ( struct dict_object * model, int flags, struct msg ** msg )
{
struct msg * new = NULL;
TRACE_ENTRY("%p %x %p", model, flags, msg);
/* Check the parameters */
CHECK_PARAMS( msg && CHECK_MSGFL(flags) );
if (model) {
enum dict_object_type dicttype;
CHECK_PARAMS( (fd_dict_gettype(model, &dicttype) == 0) && (dicttype == DICT_COMMAND) );
}
/* Create a new object */
CHECK_MALLOC( new = malloc (sizeof(struct msg)) );
/* Initialize the fields */
init_msg(new);
new->msg_public.msg_version = DIAMETER_VERSION;
new->msg_public.msg_length = GETMSGHDRSZ(); /* This will be updated later */
if (model) {
struct dictionary *dict;
struct dict_cmd_data dictdata;
struct dict_object *dictappl;
CHECK_FCT_DO( fd_dict_getdict(model, &dict), { free(new); return __ret__; } );
CHECK_FCT_DO( fd_dict_getval(model, &dictdata), { free(new); return __ret__; } );
new->msg_model = model;
new->msg_public.msg_flags = dictdata.cmd_flag_val;
new->msg_public.msg_code = dictdata.cmd_code;
/* Initialize application from the parent, if any */
CHECK_FCT_DO( fd_dict_search( dict, DICT_APPLICATION, APPLICATION_OF_COMMAND, model, &dictappl, 0), { free(new); return __ret__; } );
if (dictappl != NULL) {
struct dict_application_data appdata;
CHECK_FCT_DO( fd_dict_getval(dictappl, &appdata), { free(new); return __ret__; } );
new->msg_public.msg_appl = appdata.application_id;
}
}
if (flags & MSGFL_ALLOC_ETEID) {
new->msg_public.msg_eteid = fd_msg_eteid_get();
}
/* The new object is ready, return */
*msg = new;
return 0;
}
static int bufferize_avp(unsigned char * buffer, size_t buflen, size_t * offset, struct avp * avp);
static int parsebuf_list(unsigned char * buf, size_t buflen, struct fd_list * head);
static int parsedict_do_chain(struct dictionary * dict, struct fd_list * head, int mandatory, struct fd_pei *error_info);
/* Create answer from a request */
int fd_msg_new_answer_from_req ( struct dictionary * dict, struct msg ** msg, int flags )
{
struct dict_object * model = NULL;
struct msg *qry, *ans;
struct session * sess = NULL;
TRACE_ENTRY("%p %x", msg, flags);
/* Check the parameters */
CHECK_PARAMS( msg );
qry = *msg;
CHECK_PARAMS( CHECK_MSG(qry) && (qry->msg_public.msg_flags & CMD_FLAG_REQUEST) );
if (! (flags & MSGFL_ANSW_NOSID)) {
/* Get the session of the message */
CHECK_FCT_DO( fd_msg_sess_get(dict, qry, &sess, NULL), /* ignore an error */ );
}
/* Find the model for the answer */
if (flags & MSGFL_ANSW_ERROR) {
/* The model is the generic error format */
CHECK_FCT( fd_dict_get_error_cmd(dict, &model) );
} else {
/* The model is the answer corresponding to the query. It supposes that these are defined in the dictionary */
CHECK_FCT_DO( parsedict_do_msg( dict, qry, 1, NULL), /* continue */ );
if (qry->msg_model) {
CHECK_FCT( fd_dict_search ( dict, DICT_COMMAND, CMD_ANSWER, qry->msg_model, &model, EINVAL ) );
}
}
/* Create the answer */
CHECK_FCT( fd_msg_new( model, flags, &ans ) );
/* Set informations in the answer as in the query */
ans->msg_public.msg_code = qry->msg_public.msg_code; /* useful for MSGFL_ANSW_ERROR */
ans->msg_public.msg_appl = qry->msg_public.msg_appl;
ans->msg_public.msg_eteid = qry->msg_public.msg_eteid;
ans->msg_public.msg_hbhid = qry->msg_public.msg_hbhid;
/* Add the Session-Id AVP if session is known */
if (sess && dict) {
static struct dict_object * sess_id_avp = NULL;
os0_t sid;
size_t sidlen;
struct avp * avp;
union avp_value val;
if (!sess_id_avp) {
CHECK_FCT_DO( fd_dict_search( dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &sess_id_avp, ENOENT), { free(ans); return __ret__; } );
}
CHECK_FCT_DO( fd_sess_getsid ( sess, &sid, &sidlen ), { free(ans); return __ret__; } );
CHECK_FCT_DO( fd_msg_avp_new ( sess_id_avp, 0, &avp ), { free(ans); return __ret__; } );
val.os.data = sid;
val.os.len = sidlen;
CHECK_FCT_DO( fd_msg_avp_setvalue( avp, &val ), { free(avp); free(ans); return __ret__; } );
CHECK_FCT_DO( fd_msg_avp_add( ans, MSG_BRW_FIRST_CHILD, avp ), { free(avp); free(ans); return __ret__; } );
ans->msg_sess = sess;
CHECK_FCT_DO( fd_sess_ref_msg(sess), { free(ans); return __ret__; } );
}
/* Add all Proxy-Info AVPs from the query if any */
if (! (flags & MSGFL_ANSW_NOPROXYINFO)) {
struct avp * avp;
struct fd_pei pei;
struct fd_list avpcpylist = FD_LIST_INITIALIZER(avpcpylist);
CHECK_FCT_DO( fd_msg_browse(qry, MSG_BRW_FIRST_CHILD, &avp, NULL) , { free(ans); return __ret__; } );
while (avp) {
if ( (avp->avp_public.avp_code == AC_PROXY_INFO)
&& (avp->avp_public.avp_vendor == 0) ) {
/* We found a Proxy-Info, need to duplicate it in the answer */
/* In order to avoid dealing with all different possibilities of states, we just create a buffer then parse it */
unsigned char * buf = NULL;
size_t offset = 0;
/* Create a buffer with the content of the AVP. This is easier than going through the list */
CHECK_FCT_DO( fd_msg_update_length(avp), { free(ans); return __ret__; } );
CHECK_MALLOC_DO( buf = malloc(avp->avp_public.avp_len), { free(ans); return __ret__; } );
CHECK_FCT_DO( bufferize_avp(buf, avp->avp_public.avp_len, &offset, avp), { free(buf); free(ans); return __ret__; } );
/* Now we parse this buffer to create a copy AVP */
CHECK_FCT_DO( parsebuf_list(buf, avp->avp_public.avp_len, &avpcpylist), { free(buf); free(ans); return __ret__; } );
/* Parse dictionary objects now to remove the dependency on the buffer */
CHECK_FCT_DO( parsedict_do_chain(dict, &avpcpylist, 0, &pei), { /* leaking the avpcpylist -- this should never happen anyway */ free(buf); free(ans); return __ret__; } );
/* Done for this AVP */
free(buf);
/* We move this AVP now so that we do not parse again in next loop */
fd_list_move_end(&ans->msg_chain.children, &avpcpylist);
}
/* move to next AVP in the message, we can have several Proxy-Info instances */
CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), { free(ans); return __ret__; } );
}
}
/* associate with query */
ans->msg_query = qry;
qry->msg_associated = 1;
/* Done */
*msg = ans;
return 0;
}
/***************************************************************************************************************/
/* Explore a message */
int fd_msg_browse_internal ( msg_or_avp * reference, enum msg_brw_dir dir, msg_or_avp ** found, int * depth )
{
struct msg_avp_chain *result = NULL;
int diff = 0;
struct fd_list *li = NULL;
TRACE_ENTRY("%p %d %p %p", reference, dir, found, depth);
/* Initialize the "found" result if any */
if (found)
*found = NULL;
/* Check the parameters */
CHECK_PARAMS( VALIDATE_OBJ(reference) );
TRACE_DEBUG(FCTS, "chaining(%p): nxt:%p prv:%p hea:%p top:%p",
&_C(reference)->chaining,
_C(reference)->chaining.next,
_C(reference)->chaining.prev,
_C(reference)->chaining.head,
_C(reference)->chaining.o);
TRACE_DEBUG(FCTS, "children(%p): nxt:%p prv:%p hea:%p top:%p",
&_C(reference)->children,
_C(reference)->children.next,
_C(reference)->children.prev,
_C(reference)->children.head,
_C(reference)->children.o);
/* Now search */
switch (dir) {
case MSG_BRW_NEXT:
/* Check the reference is an AVP */
CHECK_PARAMS( _C(reference)->type == MSG_AVP );
li = &_C(reference)->chaining;
/* Check if the next element is not the sentinel ( ==> the parent) */
if (li->next != li->head)
result = _C(li->next->o);
break;
case MSG_BRW_PREV:
/* Check the reference is an AVP */
CHECK_PARAMS( _C(reference)->type == MSG_AVP );
li = &_C(reference)->chaining;
/* Check if the prev element is not the sentinel ( ==> the parent) */
if (li->prev != li->head)
result = _C(li->prev->o);
break;
case MSG_BRW_FIRST_CHILD:
li = &_C(reference)->children;
if (! FD_IS_LIST_EMPTY(li)) {
result = _C(li->next->o);
diff = 1;
}
break;
case MSG_BRW_LAST_CHILD:
li = &_C(reference)->children;
if (! FD_IS_LIST_EMPTY(li)) {
result = _C(li->prev->o);
diff = 1;
}
break;
case MSG_BRW_PARENT:
/* If the object is not chained, it has no parent */
li = &_C(reference)->chaining;
if (li != li->head) {
/* The sentinel is the parent's children list */
result = _C(li->head->o);
diff = -1;
}
break;
case MSG_BRW_WALK:
/* First, try to find a child */
li = &_C(reference)->children;
if ( ! FD_IS_LIST_EMPTY(li) ) {
result = _C(li->next->o);
diff = 1;
break;
}
/* Then try to find a "next" at this level or one of the parent's */
li = &_C(reference)->chaining;
do {
/* If this element has a "next" element, return it */
if (li->next != li->head) {
result = _C(li->next->o);
break;
}
/* otherwise, check if we have a parent */
if (li == li->head) {
/* no parent */
break;
}
/* Go to the parent's chaining information and loop */
diff -= 1;
li = &_C(li->head->o)->chaining;
} while (1);
break;
default:
/* Other directions are invalid */
CHECK_PARAMS( dir = 0 );
}
/* Save the found object, if any */
if (found && result)
*found = (void *)result;
/* Modify the depth according to the walk direction */
if (depth && diff)
(*depth) += diff;
/* Return ENOENT if found was NULL */
if ((!found) && (!result))
return ENOENT;
else
return 0;
}
/* Add an AVP into a tree */
int fd_msg_avp_add ( msg_or_avp * reference, enum msg_brw_dir dir, struct avp *avp)
{
TRACE_ENTRY("%p %d %p", reference, dir, avp);
/* Check the parameters */
CHECK_PARAMS( VALIDATE_OBJ(reference) && CHECK_AVP(avp) && FD_IS_LIST_EMPTY(&avp->avp_chain.chaining) );
/* Now insert */
switch (dir) {
case MSG_BRW_NEXT:
/* Check the reference is an AVP -- we do not chain AVPs at same level as msgs. */
CHECK_PARAMS( _C(reference)->type == MSG_AVP );
/* Insert the new avp after the reference */
fd_list_insert_after( &_A(reference)->avp_chain.chaining, &avp->avp_chain.chaining );
break;
case MSG_BRW_PREV:
/* Check the reference is an AVP */
CHECK_PARAMS( _C(reference)->type == MSG_AVP );
/* Insert the new avp before the reference */
fd_list_insert_before( &_A(reference)->avp_chain.chaining, &avp->avp_chain.chaining );
break;
case MSG_BRW_FIRST_CHILD:
/* Insert the new avp after the children sentinel */
fd_list_insert_after( &_C(reference)->children, &avp->avp_chain.chaining );
break;
case MSG_BRW_LAST_CHILD:
/* Insert the new avp before the children sentinel */
fd_list_insert_before( &_C(reference)->children, &avp->avp_chain.chaining );
break;
default:
/* Other directions are invalid */
CHECK_PARAMS( dir = 0 );
}
return 0;
}
/* Search a given AVP model in a message */
int fd_msg_search_avp ( struct msg * msg, struct dict_object * what, struct avp ** avp )
{
struct avp * nextavp;
struct dict_avp_data dictdata;
enum dict_object_type dicttype;
TRACE_ENTRY("%p %p %p", msg, what, avp);
CHECK_PARAMS( CHECK_MSG(msg) && what );
CHECK_PARAMS( (fd_dict_gettype(what, &dicttype) == 0) && (dicttype == DICT_AVP) );
CHECK_FCT( fd_dict_getval(what, &dictdata) );
/* Loop on all top AVPs */
CHECK_FCT( fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL) );
while (nextavp) {
if ( (nextavp->avp_public.avp_code == dictdata.avp_code)
&& (nextavp->avp_public.avp_vendor == dictdata.avp_vendor) ) /* always 0 if no V flag */
break;
/* Otherwise move to next AVP in the message */
CHECK_FCT( fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL) );
}
if (avp)
*avp = nextavp;
if (avp && nextavp) {
struct dictionary * dict;
CHECK_FCT( fd_dict_getdict( what, &dict) );
CHECK_FCT_DO( fd_msg_parse_dict( nextavp, dict, NULL ), /* nothing */ );
}
if (avp || nextavp)
return 0;
else
return ENOENT;
}
/***************************************************************************************************************/
/* Deleting objects */
/* Destroy and free an AVP or message */
static int destroy_obj (struct msg_avp_chain * obj )
{
TRACE_ENTRY("%p", obj);
/* Check the parameter is a valid object */
CHECK_PARAMS( VALIDATE_OBJ(obj) && FD_IS_LIST_EMPTY( &obj->children ) );
/* Unlink this object if needed */
fd_list_unlink( &obj->chaining );
/* Free the octetstring if needed */
if ((obj->type == MSG_AVP) && (_A(obj)->avp_mustfreeos == 1)) {
free(_A(obj)->avp_storage.os.data);
}
/* Free the rawdata if needed */
if ((obj->type == MSG_AVP) && (_A(obj)->avp_rawdata != NULL)) {
free(_A(obj)->avp_rawdata);
}
if ((obj->type == MSG_MSG) && (_M(obj)->msg_rawbuffer != NULL)) {
free(_M(obj)->msg_rawbuffer);
}
if ((obj->type == MSG_MSG) && (_M(obj)->msg_src_id != NULL)) {
free(_M(obj)->msg_src_id);
}
if ((obj->type == MSG_MSG) && (_M(obj)->msg_rtdata != NULL)) {
fd_rtd_free(&_M(obj)->msg_rtdata);
}
if ((obj->type == MSG_MSG) && (_M(obj)->msg_sess != NULL)) {
CHECK_FCT_DO( fd_sess_reclaim_msg ( &_M(obj)->msg_sess ), /* continue */);
}
if ((obj->type == MSG_MSG) && (_M(obj)->msg_pmdl.sentinel.o != NULL)) {
((void (*)(struct fd_msg_pmdl *))_M(obj)->msg_pmdl.sentinel.o)(&_M(obj)->msg_pmdl);
}
/* free the object */
free(obj);
return 0;
}
/* Destroy an object and all its children */
static void destroy_tree(struct msg_avp_chain * obj)
{
struct fd_list *rem;
TRACE_ENTRY("%p", obj);
/* Destroy any subtree */
while ( (rem = obj->children.next) != &obj->children)
destroy_tree(_C(rem->o));
/* Then unlink and destroy the object */
CHECK_FCT_DO( destroy_obj(obj), /* nothing */ );
}
/* Free an object and its tree */
int fd_msg_free ( msg_or_avp * object )
{
TRACE_ENTRY("%p", object);
if (object == NULL)
return 0;
if (CHECK_MSG(object)) {
if (_M(object)->msg_query) {
_M(_M(object)->msg_query)->msg_associated = 0;
CHECK_FCT( fd_msg_free( _M(object)->msg_query ) );
_M(object)->msg_query = NULL;
} else {
if (_M(object)->msg_associated) {
TRACE_DEBUG(INFO, "Not freeing query %p referenced in an answer (will be freed along the answer).", object);
return 0;
}
}
}
destroy_tree(_C(object));
return 0;
}
/***************************************************************************************************************/
/* Debug functions: dumping */
/* messages and AVP formatters */
typedef DECLARE_FD_DUMP_PROTOTYPE( (*msg_dump_formatter_msg), struct msg * msg );
typedef DECLARE_FD_DUMP_PROTOTYPE( (*msg_dump_formatter_avp), struct avp * avp, int level, int first, int last );
/* Core function to process the dumping */
static DECLARE_FD_DUMP_PROTOTYPE( msg_dump_process, msg_dump_formatter_msg msg_format, msg_dump_formatter_avp avp_format, msg_or_avp *obj, struct dictionary *dict, int force_parsing, int recurse )
{
FD_DUMP_HANDLE_OFFSET();
if (!VALIDATE_OBJ(obj)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID MESSAGE OR AVP @%p", obj), return NULL);
return *buf;
}
if (force_parsing) {
(void) fd_msg_parse_dict(obj, dict, NULL);
}
switch (_C(obj)->type) {
case MSG_AVP:
CHECK_MALLOC_DO( (*avp_format)(FD_DUMP_STD_PARAMS, (struct avp *)obj, 0, 1, 1), return NULL);
break;
case MSG_MSG:
CHECK_MALLOC_DO( (*msg_format)(FD_DUMP_STD_PARAMS, (struct msg *)obj), return NULL);
break;
default:
ASSERT(0);
free(*buf);
*buf = NULL;
return NULL;
}
if (recurse) {
struct avp * avp = NULL;
int first = 1;
CHECK_FCT_DO( fd_msg_browse ( obj, MSG_BRW_FIRST_CHILD, &avp, NULL ), avp = NULL );
while (avp) {
struct avp * nextavp = NULL;
CHECK_FCT_DO( fd_msg_browse ( avp, MSG_BRW_NEXT, &nextavp, NULL ), nextavp = NULL );
CHECK_MALLOC_DO( (*avp_format)(FD_DUMP_STD_PARAMS, avp, 1, first, nextavp ? 0 : 1), return NULL);
avp = nextavp;
first = 0;
};
}
return *buf;
}
/*
* Tree View message dump
*/
static DECLARE_FD_DUMP_PROTOTYPE( msg_format_treeview, struct msg * msg )
{
if (!CHECK_MSG(msg)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID MESSAGE"), return NULL);
return *buf;
}
if (!msg->msg_model) {
if (msg->msg_model_not_found.mnf_code) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(not found in dictionary)\n"), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(not searched in dictionary)\n"), return NULL);
}
} else {
enum dict_object_type dicttype;
struct dict_cmd_data dictdata;
if (fd_dict_gettype(msg->msg_model, &dicttype) || (dicttype != DICT_COMMAND)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(invalid model information)\n"), return NULL);
} else if (fd_dict_getval(msg->msg_model, &dictdata)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(error getting model information)\n"), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'\n", dictdata.cmd_name), return NULL);
}
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Version: 0x%02hhX\n", msg->msg_public.msg_version), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Length: %d\n", msg->msg_public.msg_length), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Flags: 0x%02hhX (" DUMP_CMDFL_str ")\n", msg->msg_public.msg_flags, DUMP_CMDFL_val(msg->msg_public.msg_flags)), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Command Code: %u\n", msg->msg_public.msg_code), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " ApplicationId: %d\n", msg->msg_public.msg_appl), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Hop-by-Hop Identifier: 0x%08X\n", msg->msg_public.msg_hbhid), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " End-to-End Identifier: 0x%08X\n", msg->msg_public.msg_eteid), return NULL);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " {internal data}: src:%s(%zd) rwb:%p rt:%d cb:%p,%p(%p) qry:%p asso:%d sess:%p", msg->msg_src_id?:"(nil)", msg->msg_src_id_len, msg->msg_rawbuffer, msg->msg_routable, msg->msg_cb.anscb, msg->msg_cb.expirecb, msg->msg_cb.data, msg->msg_query, msg->msg_associated, msg->msg_sess), return NULL);
return *buf;
}
static DECLARE_FD_DUMP_PROTOTYPE( avp_format_treeview, struct avp * avp, int level, int first, int last )
{
char * name;
struct dict_avp_data dictdata;
struct dict_avp_data *dictinfo = NULL;
struct dict_vendor_data vendordata;
struct dict_vendor_data *vendorinfo = NULL;
if (level) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
}
if (!CHECK_AVP(avp)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID AVP"), return NULL);
return *buf;
}
if (level) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%*sAVP: ", level * 3, ""), return NULL);
}
if (!avp->avp_model) {
if (avp->avp_model_not_found.mnf_code) {
name = "(not found in dictionary)";
} else {
name = "(not searched in dictionary)";
}
} else {
enum dict_object_type dicttype;
if (fd_dict_gettype(avp->avp_model, &dicttype) || (dicttype != DICT_AVP)) {
name = "(invalid model information)";
} else if (fd_dict_getval(avp->avp_model, &dictdata)) {
name = "(error getting model information)";
} else {
name = dictdata.avp_name;
dictinfo = &dictdata;
if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
struct dictionary * dict;
struct dict_object * vendor;
if ((!fd_dict_getdict(avp->avp_model, &dict))
&& (!fd_dict_search(dict, DICT_VENDOR, VENDOR_OF_AVP, avp->avp_model, &vendor, ENOENT))
&& (!fd_dict_getval(vendor, &vendordata))) {
vendorinfo = &vendordata;
}
}
}
}
if (dictinfo) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'(%u)", name, avp->avp_public.avp_code), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%u%s", avp->avp_public.avp_code, name), return NULL);
}
if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
if (vendorinfo) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " vend='%s'(%u)", vendorinfo->vendor_name, avp->avp_public.avp_vendor), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " vend=%u", avp->avp_public.avp_vendor), return NULL);
}
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " l=%d f=" DUMP_AVPFL_str " val=", avp->avp_public.avp_len, DUMP_AVPFL_val(avp->avp_public.avp_flags)), return NULL);
if (dictinfo && (dictinfo->avp_basetype == AVP_TYPE_GROUPED)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(grouped)"), return NULL);
if (level) {
struct avp * inavp = NULL;
int first = 1;
CHECK_FCT_DO( fd_msg_browse ( avp, MSG_BRW_FIRST_CHILD, &inavp, NULL ), inavp = NULL );
while (inavp) {
struct avp * nextavp = NULL;
CHECK_FCT_DO( fd_msg_browse ( inavp, MSG_BRW_NEXT, &nextavp, NULL ), inavp = NULL );
CHECK_MALLOC_DO( avp_format_treeview(FD_DUMP_STD_PARAMS, inavp, level + 1, first, nextavp ? 0 : 1), return NULL);
inavp = nextavp;
first = 0;
};
}
} else {
if (avp->avp_public.avp_value) {
CHECK_MALLOC_DO( fd_dict_dump_avp_value(FD_DUMP_STD_PARAMS, avp->avp_public.avp_value, avp->avp_model, 0, 0), return NULL);
} else if (avp->avp_rawdata) {
CHECK_MALLOC_DO( fd_dump_extend_hexdump(FD_DUMP_STD_PARAMS, avp->avp_rawdata, avp->avp_rawlen, 0, 0), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(not set)"), return NULL);
}
}
return *buf;
}
/* multi-line human-readable dump similar to wireshark output */
DECLARE_FD_DUMP_PROTOTYPE( fd_msg_dump_treeview, msg_or_avp *obj, struct dictionary *dict, int force_parsing, int recurse )
{
return msg_dump_process(FD_DUMP_STD_PARAMS, msg_format_treeview, avp_format_treeview, obj, dict, force_parsing, recurse);
}
/*
* One-line dumper for compact but complete traces
*/
static DECLARE_FD_DUMP_PROTOTYPE( msg_format_full, struct msg * msg )
{
int success = 0;
struct dict_cmd_data dictdata;
if (!CHECK_MSG(msg)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID MESSAGE"), return NULL);
return *buf;
}
if (!msg->msg_model) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(no model) "), return NULL);
} else {
enum dict_object_type dicttype=0;
if (fd_dict_gettype(msg->msg_model, &dicttype) || (dicttype != DICT_COMMAND)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(invalid model %d) ", dicttype), return NULL);
} else if (fd_dict_getval(msg->msg_model, &dictdata)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(error getting model data) "), return NULL);
} else {
success = 1;
}
}
if (msg->msg_public.msg_appl) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS,
"%s(%u/%u)[" DUMP_CMDFL_str "], Length=%u, Hop-By-Hop-Id=0x%08x, End-to-End=0x%08x",
success ? dictdata.cmd_name : "unknown", msg->msg_public.msg_appl, msg->msg_public.msg_code, DUMP_CMDFL_val(msg->msg_public.msg_flags),
msg->msg_public.msg_length, msg->msg_public.msg_hbhid, msg->msg_public.msg_eteid), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS,
"%s(%u)[" DUMP_CMDFL_str "], Length=%u, Hop-By-Hop-Id=0x%08x, End-to-End=0x%08x",
success ? dictdata.cmd_name : "unknown", msg->msg_public.msg_code, DUMP_CMDFL_val(msg->msg_public.msg_flags),
msg->msg_public.msg_length, msg->msg_public.msg_hbhid, msg->msg_public.msg_eteid), return NULL);
}
return *buf;
}
static DECLARE_FD_DUMP_PROTOTYPE( avp_format_full, struct avp * avp, int level, int first, int last )
{
int success = 0;
struct dict_avp_data dictdata;
if (level) {
if ((first) && ((*buf)[*offset - 1] == '=')) {
/* We are first AVP of a grouped AVP */
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{ "), return NULL);
} else {
/* We follow another AVP, or a message header */
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, ", { "), return NULL);
}
}
if (!CHECK_AVP(avp)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID AVP"), return NULL);
goto end;
}
if (avp->avp_model) {
enum dict_object_type dicttype;
if (fd_dict_gettype(avp->avp_model, &dicttype) || (dicttype != DICT_AVP)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(invalid model: %d) ", dicttype), return NULL);
} else if (fd_dict_getval(avp->avp_model, &dictdata)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(error getting model data) "), return NULL);
} else {
success = 1;
}
}
if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s(%u/%u)[" DUMP_AVPFL_str "]=",
success ? dictdata.avp_name : "unknown", avp->avp_public.avp_vendor, avp->avp_public.avp_code, DUMP_AVPFL_val(avp->avp_public.avp_flags)), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s(%u)[" DUMP_AVPFL_str "]=",
success ? dictdata.avp_name : "unknown", avp->avp_public.avp_code, DUMP_AVPFL_val(avp->avp_public.avp_flags)), return NULL);
}
if (success && (dictdata.avp_basetype == AVP_TYPE_GROUPED)) {
if (level) {
struct avp * inavp = NULL;
int first = 1;
CHECK_FCT_DO( fd_msg_browse ( avp, MSG_BRW_FIRST_CHILD, &inavp, NULL ), inavp = NULL );
while (inavp) {
struct avp * nextavp = NULL;
CHECK_FCT_DO( fd_msg_browse ( inavp, MSG_BRW_NEXT, &nextavp, NULL ), inavp = NULL );
CHECK_MALLOC_DO( avp_format_full(FD_DUMP_STD_PARAMS, inavp, level + 1, first, nextavp ? 0 : 1), return NULL);
inavp = nextavp;
first = 0;
};
}
} else {
if (avp->avp_public.avp_value) {
CHECK_MALLOC_DO( fd_dict_dump_avp_value(FD_DUMP_STD_PARAMS, avp->avp_public.avp_value, avp->avp_model, 0, 0), return NULL);
} else if (avp->avp_rawdata) {
CHECK_MALLOC_DO( fd_dump_extend_hexdump(FD_DUMP_STD_PARAMS, avp->avp_rawdata, avp->avp_rawlen, 0, 0), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(not set)"), return NULL);
}
}
end:
if (level) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " }"), return NULL);
}
return *buf;
}
/* one-line dump with all the contents of the message */
DECLARE_FD_DUMP_PROTOTYPE( fd_msg_dump_full, msg_or_avp *obj, struct dictionary *dict, int force_parsing, int recurse )
{
return msg_dump_process(FD_DUMP_STD_PARAMS, msg_format_full, avp_format_full, obj, dict, force_parsing, recurse);
}
/*
* One-line dumper for compact but complete traces
*/
static DECLARE_FD_DUMP_PROTOTYPE( msg_format_summary, struct msg * msg )
{
if (!CHECK_MSG(msg)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID MESSAGE"), return NULL);
return *buf;
}
if (!msg->msg_model) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(no model)"), return NULL);
} else {
enum dict_object_type dicttype;
struct dict_cmd_data dictdata;
if (fd_dict_gettype(msg->msg_model, &dicttype) || (dicttype != DICT_COMMAND) || (fd_dict_getval(msg->msg_model, &dictdata))) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(model error)"), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'", dictdata.cmd_name), return NULL);
}
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%u/%u f:" DUMP_CMDFL_str " src:'%s' len:%d",
msg->msg_public.msg_appl, msg->msg_public.msg_code, DUMP_CMDFL_val(msg->msg_public.msg_flags), msg->msg_src_id?:"(nil)", msg->msg_public.msg_length), return NULL);
return *buf;
}
static DECLARE_FD_DUMP_PROTOTYPE( avp_format_summary, struct avp * avp, int level, int first, int last )
{
char * name;
struct dict_avp_data dictdata;
struct dict_avp_data *dictinfo = NULL;
struct dict_vendor_data vendordata;
struct dict_vendor_data *vendorinfo = NULL;
if (level) {
if (first) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " {"), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, ","), return NULL);
}
}
if (!CHECK_AVP(avp)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID AVP"), return NULL);
goto end;
}
if (!level) {
/* We have been called to explicitely dump this AVP, so we parse its name if available */
if (!avp->avp_model) {
name = "(no model)";
} else {
enum dict_object_type dicttype;
if (fd_dict_gettype(avp->avp_model, &dicttype) || (dicttype != DICT_AVP) || (fd_dict_getval(avp->avp_model, &dictdata))) {
name = "(model error)";
} else {
name = dictdata.avp_name;
dictinfo = &dictdata;
if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
struct dictionary * dict;
struct dict_object * vendor;
if ((!fd_dict_getdict(avp->avp_model, &dict))
&& (!fd_dict_search(dict, DICT_VENDOR, VENDOR_OF_AVP, avp->avp_model, &vendor, ENOENT))
&& (!fd_dict_getval(vendor, &vendordata))) {
vendorinfo = &vendordata;
}
}
}
}
if (dictinfo) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'(%u)", name, avp->avp_public.avp_code), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%u%s", avp->avp_public.avp_code, name), return NULL);
}
if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
if (vendorinfo) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " V='%s'(%u)", vendorinfo->vendor_name, avp->avp_public.avp_vendor), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " V=%u", avp->avp_public.avp_vendor), return NULL);
}
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " L=%d F=" DUMP_AVPFL_str " V=", avp->avp_public.avp_len, DUMP_AVPFL_val(avp->avp_public.avp_flags)), return NULL);
if ((!dictinfo) || (dictinfo->avp_basetype != AVP_TYPE_GROUPED)) {
if (avp->avp_public.avp_value) {
CHECK_MALLOC_DO( fd_dict_dump_avp_value(FD_DUMP_STD_PARAMS, avp->avp_public.avp_value, avp->avp_model, 0, 0), return NULL);
} else if (avp->avp_rawdata) {
CHECK_MALLOC_DO( fd_dump_extend_hexdump(FD_DUMP_STD_PARAMS, avp->avp_rawdata, avp->avp_rawlen, 0, 0), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(not set)"), return NULL);
}
}
} else {
/* For embedded AVPs, we only display (vendor,) code & length */
if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "V:%u/", avp->avp_public.avp_vendor), return NULL);
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "C:%u/l:%d", avp->avp_public.avp_code, avp->avp_public.avp_len), return NULL);
}
end:
if ((level) && (last)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "}"), return NULL);
}
return *buf;
}
/* This one only prints a short display, does not go into the complete tree */
DECLARE_FD_DUMP_PROTOTYPE( fd_msg_dump_summary, msg_or_avp *obj, struct dictionary *dict, int force_parsing, int recurse )
{
return msg_dump_process(FD_DUMP_STD_PARAMS, msg_format_summary, avp_format_summary, obj, dict, force_parsing, recurse);
}
/***************************************************************************************************************/
/* Simple meta-data management */
/* Retrieve the model of an object */
int fd_msg_model ( msg_or_avp * reference, struct dict_object ** model )
{
TRACE_ENTRY("%p %p", reference, model);
/* Check the parameters */
CHECK_PARAMS( model && VALIDATE_OBJ(reference) );
/* copy the model reference */
switch (_C(reference)->type) {
case MSG_AVP:
*model = _A(reference)->avp_model;
break;
case MSG_MSG:
*model = _M(reference)->msg_model;
break;
default:
CHECK_PARAMS(0);
}
return 0;
}
/* Retrieve the address of the msg_public field of a message */
int fd_msg_hdr ( struct msg *msg, struct msg_hdr **pdata )
{
TRACE_ENTRY("%p %p", msg, pdata);
CHECK_PARAMS( CHECK_MSG(msg) && pdata );
*pdata = &msg->msg_public;
return 0;
}
/* Retrieve the address of the avp_public field of an avp */
int fd_msg_avp_hdr ( struct avp *avp, struct avp_hdr **pdata )
{
TRACE_ENTRY("%p %p", avp, pdata);
CHECK_PARAMS( CHECK_AVP(avp) && pdata );
*pdata = &avp->avp_public;
return 0;
}
/* Associate answers and queries */
int fd_msg_answ_associate( struct msg * answer, struct msg * query )
{
TRACE_ENTRY( "%p %p", answer, query );
CHECK_PARAMS( CHECK_MSG(answer) && CHECK_MSG(query) && (answer->msg_query == NULL ) );
answer->msg_query = query;
query->msg_associated = 1;
return 0;
}
int fd_msg_answ_getq( struct msg * answer, struct msg ** query )
{
TRACE_ENTRY( "%p %p", answer, query );
CHECK_PARAMS( CHECK_MSG(answer) && query );
*query = answer->msg_query;
return 0;
}
int fd_msg_answ_detach( struct msg * answer )
{
TRACE_ENTRY( "%p", answer );
CHECK_PARAMS( CHECK_MSG(answer) );
answer->msg_query->msg_associated = 0;
answer->msg_query = NULL;
return 0;
}
/* Associate / get answer callbacks */
int fd_msg_anscb_associate( struct msg * msg, void ( *anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout )
{
TRACE_ENTRY("%p %p %p %p", msg, anscb, expirecb, data);
/* Check the parameters */
CHECK_PARAMS( CHECK_MSG(msg) );
if (! (msg->msg_public.msg_flags & CMD_FLAG_REQUEST ))
return anscb ? EINVAL : 0; /* we associate with requests only */
CHECK_PARAMS( (anscb == NULL) || (msg->msg_cb.anscb == NULL) ); /* We are not overwriting a cb */
CHECK_PARAMS( (expirecb == NULL) || (msg->msg_cb.expirecb == NULL) ); /* We are not overwriting a cb */
/* Associate callback and data with the message, if any */
if (anscb) {
msg->msg_cb.anscb = anscb;
msg->msg_cb.data = data;
}
if (expirecb) {
msg->msg_cb.expirecb = expirecb;
if (timeout) {
memcpy(&msg->msg_cb.timeout, timeout, sizeof(struct timespec));
}
}
return 0;
}
/* Remove a callback */
int fd_msg_anscb_reset(struct msg * msg, int clear_anscb, int clear_expirecb)
{
TRACE_ENTRY("%p %d %d", msg, clear_anscb, clear_expirecb);
/* Check the parameters */
CHECK_PARAMS( CHECK_MSG(msg) );
if (clear_anscb) {
msg->msg_cb.anscb = NULL;
msg->msg_cb.data = NULL;
}
if (clear_expirecb) {
msg->msg_cb.expirecb = NULL;
memset(&msg->msg_cb.timeout, 0, sizeof(struct timespec));
}
return 0;
}
int fd_msg_anscb_get( struct msg * msg, void (**anscb)(void *, struct msg **), void (**expirecb)(void *, DiamId_t, size_t, struct msg **), void ** data )
{
TRACE_ENTRY("%p %p %p %p", msg, anscb, expirecb, data);
/* Check the parameters */
CHECK_PARAMS( CHECK_MSG(msg) );
/* Copy the result */
if (anscb)
*anscb = msg->msg_cb.anscb;
if (data)
*data = msg->msg_cb.data;
if (expirecb)
*expirecb = msg->msg_cb.expirecb;
return 0;
}
struct timespec *fd_msg_anscb_gettimeout( struct msg * msg )
{
TRACE_ENTRY("%p", msg);
/* Check the parameters */
CHECK_PARAMS_DO( CHECK_MSG(msg), return NULL );
if (!msg->msg_cb.timeout.tv_sec) {
return NULL;
}
return &msg->msg_cb.timeout;
}
/* Associate routing lists */
int fd_msg_rt_associate( struct msg * msg, struct rt_data * rtd )
{
TRACE_ENTRY( "%p %p", msg, rtd );
CHECK_PARAMS( CHECK_MSG(msg) && rtd );
msg->msg_rtdata = rtd;
return 0;
}
int fd_msg_rt_get( struct msg * msg, struct rt_data ** rtd )
{
TRACE_ENTRY( "%p %p", msg, rtd );
CHECK_PARAMS( CHECK_MSG(msg) && rtd );
*rtd = msg->msg_rtdata;
return 0;
}
/* Find if a message is routable */
int fd_msg_is_routable ( struct msg * msg )
{
TRACE_ENTRY("%p", msg);
CHECK_PARAMS_DO( CHECK_MSG(msg), return 0 /* pretend the message is not routable */ );
if ( ! msg->msg_routable ) {
/* To define if a message is routable, we rely on the "PXY" flag (for application 0). */
msg->msg_routable = ((msg->msg_public.msg_appl != 0) || (msg->msg_public.msg_flags & CMD_FLAG_PROXIABLE)) ? 1 : 2;
/* Note : the 'real' criteria according to the Diameter I-D is that the message is
routable if and only if the "Destination-Realm" AVP is required by the command ABNF.
We could make a test for this here, but it's more computational work and our test
seems accurate (until proven otherwise...) */
}
return (msg->msg_routable == 1) ? 1 : 0;
}
/* cache the dictionary model for next function to avoid re-searching at every incoming message */
static struct dict_object *cached_avp_rr_model = NULL;
static struct dictionary *cached_avp_rr_dict = NULL;
static pthread_mutex_t cached_avp_rr_lock = PTHREAD_MUTEX_INITIALIZER;
/* Associate source peer */
int fd_msg_source_set( struct msg * msg, DiamId_t diamid, size_t diamidlen )
{
TRACE_ENTRY( "%p %p %zd", msg, diamid, diamidlen);
/* Check we received a valid message */
CHECK_PARAMS( CHECK_MSG(msg) );
/* Cleanup any previous source */
free(msg->msg_src_id); msg->msg_src_id = NULL; msg->msg_src_id_len = 0;
/* If the request is to cleanup the source, we are done */
if (diamid == NULL) {
return 0;
}
/* Otherwise save the new informations */
CHECK_MALLOC( msg->msg_src_id = os0dup(diamid, diamidlen) );
msg->msg_src_id_len = diamidlen;
/* done */
return 0;
}
/* Associate source peer */
int fd_msg_source_setrr( struct msg * msg, DiamId_t diamid, size_t diamidlen, struct dictionary * dict )
{
struct dict_object *avp_rr_model = NULL;
avp_code_t code = AC_ROUTE_RECORD;
struct avp *avp;
union avp_value val;
TRACE_ENTRY( "%p %p %zd %p", msg, diamid, diamidlen, dict);
/* Check we received a valid message */
CHECK_PARAMS( CHECK_MSG(msg) && dict );
/* Lock the cached values */
CHECK_POSIX( pthread_mutex_lock(&cached_avp_rr_lock) );
if (cached_avp_rr_dict == dict) {
avp_rr_model = cached_avp_rr_model;
}
CHECK_POSIX( pthread_mutex_unlock(&cached_avp_rr_lock) );
/* If it was not cached */
if (!avp_rr_model) {
/* Find the model for Route-Record in the dictionary */
CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE, &code, &avp_rr_model, ENOENT) );
/* Now cache this result */
CHECK_POSIX( pthread_mutex_lock(&cached_avp_rr_lock) );
cached_avp_rr_dict = dict;
cached_avp_rr_model = avp_rr_model;
CHECK_POSIX( pthread_mutex_unlock(&cached_avp_rr_lock) );
}
/* Create the AVP with this model */
CHECK_FCT( fd_msg_avp_new ( avp_rr_model, 0, &avp ) );
/* Set the AVP value with the diameter id */
memset(&val, 0, sizeof(val));
val.os.data = (uint8_t *)diamid;
val.os.len = diamidlen;
CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
/* Add the AVP in the message */
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
/* done */
return 0;
}
int fd_msg_source_get( struct msg * msg, DiamId_t* diamid, size_t * diamidlen )
{
TRACE_ENTRY( "%p %p %p", msg, diamid, diamidlen);
/* Check we received valid parameters */
CHECK_PARAMS( CHECK_MSG(msg) );
CHECK_PARAMS( diamid );
/* Copy the informations */
*diamid = msg->msg_src_id;
if (diamidlen)
*diamidlen = msg->msg_src_id_len;
/* done */
return 0;
}
/* Associate a session with a message, use only when the session was just created */
int fd_msg_sess_set(struct msg * msg, struct session * session)
{
TRACE_ENTRY("%p %p", msg, session);
/* Check we received valid parameters */
CHECK_PARAMS( CHECK_MSG(msg) );
CHECK_PARAMS( session );
CHECK_PARAMS( msg->msg_sess == NULL );
msg->msg_sess = session;
return 0;
}
/* Retrieve the session of the message */
int fd_msg_sess_get(struct dictionary * dict, struct msg * msg, struct session ** session, int * new)
{
struct avp * avp;
TRACE_ENTRY("%p %p %p", msg, session, new);
/* Check we received valid parameters */
CHECK_PARAMS( CHECK_MSG(msg) );
CHECK_PARAMS( session );
/* If we already resolved the session, just send it back */
if (msg->msg_sess) {
*session = msg->msg_sess;
if (new)
*new = 0;
return 0;
}
/* OK, we have to search for Session-Id AVP -- it is usually the first AVP, but let's be permissive here */
/* -- note: we accept messages that have not yet been dictionary parsed... */
CHECK_FCT( fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
while (avp) {
if ( (avp->avp_public.avp_code == AC_SESSION_ID)
&& (avp->avp_public.avp_vendor == 0) )
break;
/* Otherwise move to next AVP in the message */
CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
}
if (!avp) {
TRACE_DEBUG(FULL, "No Session-Id AVP found in message %p", msg);
*session = NULL;
return 0;
}
if (!avp->avp_model) {
CHECK_FCT( fd_msg_parse_dict ( avp, dict, NULL ) );
}
ASSERT( avp->avp_public.avp_value );
/* Resolve the session and we are done */
if (avp->avp_public.avp_value->os.len > 0) {
CHECK_FCT( fd_sess_fromsid_msg ( avp->avp_public.avp_value->os.data, avp->avp_public.avp_value->os.len, &msg->msg_sess, new) );
*session = msg->msg_sess;
} else {
TRACE_DEBUG(FULL, "Session-Id AVP with 0-byte length found in message %p", msg);
*session = NULL;
}
return 0;
}
/* Retrieve the location of the pmd list for the message; return NULL if failed */
struct fd_msg_pmdl * fd_msg_pmdl_get(struct msg * msg)
{
CHECK_PARAMS_DO( CHECK_MSG(msg), return NULL );
return &msg->msg_pmdl;
}
/******************* End-to-end counter *********************/
static uint32_t fd_eteid;
static pthread_mutex_t fd_eteid_lck = PTHREAD_MUTEX_INITIALIZER;
void fd_msg_eteid_init(void)
{
uint32_t t = (uint32_t)time(NULL);
srand48(t);
fd_eteid = (t << 20) | ((uint32_t)lrand48() & ( (1 << 20) - 1 ));
}
uint32_t fd_msg_eteid_get ( void )
{
uint32_t ret;
CHECK_POSIX_DO( pthread_mutex_lock(&fd_eteid_lck), /* continue */ );
ret = fd_eteid ++;
CHECK_POSIX_DO( pthread_mutex_unlock(&fd_eteid_lck), /* continue */ );
return ret;
}
/***************************************************************************************************************/
/* Manage AVPs values */
/* Set the value of an AVP */
int fd_msg_avp_setvalue ( struct avp *avp, union avp_value *value )
{
enum dict_avp_basetype type = -1;
TRACE_ENTRY("%p %p", avp, value);
/* Check parameter */
CHECK_PARAMS( CHECK_AVP(avp) && avp->avp_model );
/* Retrieve information from the AVP model */
{
enum dict_object_type dicttype;
struct dict_avp_data dictdata;
CHECK_PARAMS( (fd_dict_gettype(avp->avp_model, &dicttype) == 0) && (dicttype == DICT_AVP) );
CHECK_FCT( fd_dict_getval(avp->avp_model, &dictdata) );
type = dictdata.avp_basetype;
CHECK_PARAMS( type != AVP_TYPE_GROUPED );
}
/* First, clean any previous value */
if (avp->avp_mustfreeos != 0) {
free(avp->avp_storage.os.data);
avp->avp_mustfreeos = 0;
}
memset(&avp->avp_storage, 0, sizeof(union avp_value));
/* If the request was to delete a value: */
if (!value) {
avp->avp_public.avp_value = NULL;
return 0;
}
/* Now we have to set the value */
memcpy(&avp->avp_storage, value, sizeof(union avp_value));
/* Duplicate an octetstring if needed. */
if (type == AVP_TYPE_OCTETSTRING) {
CHECK_MALLOC( avp->avp_storage.os.data = os0dup(value->os.data, value->os.len) );
avp->avp_mustfreeos = 1;
}
/* Set the data pointer of the public part */
avp->avp_public.avp_value = &avp->avp_storage;
return 0;
}
/* Set the value of an AVP, using formatted data */
int fd_msg_avp_value_encode ( void *data, struct avp *avp )
{
enum dict_avp_basetype type = -1;
struct dict_type_data type_data;
TRACE_ENTRY("%p %p", data, avp);
/* Check parameter */
CHECK_PARAMS( CHECK_AVP(avp) && avp->avp_model );
/* Retrieve information from the AVP model and it's parent type */
{
enum dict_object_type dicttype;
struct dict_avp_data dictdata;
struct dictionary * dict;
struct dict_object * parenttype = NULL;
/* First check the base type of the AVP */
CHECK_PARAMS( (fd_dict_gettype(avp->avp_model, &dicttype) == 0) && (dicttype == DICT_AVP) );
CHECK_FCT( fd_dict_getval(avp->avp_model, &dictdata) );
type = dictdata.avp_basetype;
CHECK_PARAMS( type != AVP_TYPE_GROUPED );
/* Then retrieve information about the parent's type (= derived type) */
CHECK_FCT( fd_dict_getdict( avp->avp_model, &dict ) );
CHECK_FCT( fd_dict_search( dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &parenttype, EINVAL) );
CHECK_FCT( fd_dict_getval(parenttype, &type_data) );
if (type_data.type_encode == NULL) {
TRACE_DEBUG(INFO, "This AVP type does not provide a callback to encode formatted data. ENOTSUP.");
return ENOTSUP;
}
}
/* Ok, now we can encode the value */
/* First, clean any previous value */
if (avp->avp_mustfreeos != 0) {
free(avp->avp_storage.os.data);
avp->avp_mustfreeos = 0;
}
avp->avp_public.avp_value = NULL;
memset(&avp->avp_storage, 0, sizeof(union avp_value));
/* Now call the type's callback to encode the data */
CHECK_FCT( (*type_data.type_encode)(data, &avp->avp_storage) );
/* If an octetstring has been allocated, let's mark it to be freed */
if (type == AVP_TYPE_OCTETSTRING)
avp->avp_mustfreeos = 1;
/* Set the data pointer of the public part */
avp->avp_public.avp_value = &avp->avp_storage;
return 0;
}
/* Interpret the value of an AVP into formatted data */
int fd_msg_avp_value_interpret ( struct avp *avp, void *data )
{
struct dict_type_data type_data;
TRACE_ENTRY("%p %p", avp, data);
/* Check parameter */
CHECK_PARAMS( CHECK_AVP(avp) && avp->avp_model && avp->avp_public.avp_value );
/* Retrieve information about the AVP parent type */
{
struct dictionary * dict;
struct dict_object * parenttype = NULL;
CHECK_FCT( fd_dict_getdict( avp->avp_model, &dict ) );
CHECK_FCT( fd_dict_search( dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &parenttype, EINVAL) );
CHECK_FCT( fd_dict_getval(parenttype, &type_data) );
if (type_data.type_interpret == NULL) {
TRACE_DEBUG(INFO, "This AVP type does not provide a callback to interpret value in formatted data. ENOTSUP.");
return ENOTSUP;
}
}
/* Ok, now we can interpret the value */
CHECK_FCT( (*type_data.type_interpret)(avp->avp_public.avp_value, data) );
return 0;
}
/***************************************************************************************************************/
/* Creating a buffer from memory objects (bufferize a struct msg) */
/* Following macros are used to store 32 and 64 bit fields into a buffer in network byte order */
#define PUT_in_buf_32( _u32data, _bufptr ) { \
*(uint32_t *)(_bufptr) = htonl((uint32_t)(_u32data)); \
}
/* The location is not on 64b boundary, so we split the writing in two operations to avoid sigbus */
#define PUT_in_buf_64( _u64data, _bufptr ) { \
uint64_t __v = htonll((uint64_t)(_u64data)); \
memcpy(_bufptr, &__v, sizeof(__v)); \
}
/* Write a message header in the buffer */
static int bufferize_msg(unsigned char * buffer, size_t buflen, size_t * offset, struct msg * msg)
{
TRACE_ENTRY("%p %zd %p %p", buffer, buflen, offset, msg);
if ((buflen - *offset) < GETMSGHDRSZ())
return ENOSPC;
if (*offset & 0x3)
return EFAULT; /* We are supposed to start on 32 bit boundaries */
PUT_in_buf_32(msg->msg_public.msg_length, buffer + *offset);
buffer[*offset] = msg->msg_public.msg_version;
*offset += 4;
PUT_in_buf_32(msg->msg_public.msg_code, buffer + *offset);
buffer[*offset] = msg->msg_public.msg_flags;
*offset += 4;
PUT_in_buf_32(msg->msg_public.msg_appl, buffer + *offset);
*offset += 4;
PUT_in_buf_32(msg->msg_public.msg_hbhid, buffer + *offset);
*offset += 4;
PUT_in_buf_32(msg->msg_public.msg_eteid, buffer + *offset);
*offset += 4;
return 0;
}
static int bufferize_chain(unsigned char * buffer, size_t buflen, size_t * offset, struct fd_list * list);
/* Write an AVP in the buffer */
static int bufferize_avp(unsigned char * buffer, size_t buflen, size_t * offset, struct avp * avp)
{
struct dict_avp_data dictdata;
TRACE_ENTRY("%p %zd %p %p", buffer, buflen, offset, avp);
if ((buflen - *offset) < avp->avp_public.avp_len)
return ENOSPC;
/* Write the header */
PUT_in_buf_32(avp->avp_public.avp_code, buffer + *offset);
*offset += 4;
PUT_in_buf_32(avp->avp_public.avp_len, buffer + *offset);
buffer[*offset] = avp->avp_public.avp_flags;
*offset += 4;
if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
PUT_in_buf_32(avp->avp_public.avp_vendor, buffer + *offset);
*offset += 4;
}
/* Then we must write the AVP value */
if (avp->avp_model == NULL) {
/* In the case where we don't know the type of AVP, just copy the raw data or source */
CHECK_PARAMS( avp->avp_source || avp->avp_rawdata );
if ( avp->avp_rawdata != NULL ) {
/* the content was stored in rawdata */
memcpy(&buffer[*offset], avp->avp_rawdata, avp->avp_rawlen);
*offset += PAD4(avp->avp_rawlen);
} else {
/* the message was not parsed completely */
size_t datalen = avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags);
memcpy(&buffer[*offset], avp->avp_source, datalen);
*offset += PAD4(datalen);
}
} else {
/* The AVP is defined in the dictionary */
CHECK_FCT( fd_dict_getval(avp->avp_model, &dictdata) );
CHECK_PARAMS( ( dictdata.avp_basetype == AVP_TYPE_GROUPED ) || avp->avp_public.avp_value );
switch (dictdata.avp_basetype) {
case AVP_TYPE_GROUPED:
return bufferize_chain(buffer, buflen, offset, &avp->avp_chain.children);
case AVP_TYPE_OCTETSTRING:
if (avp->avp_public.avp_value->os.len)
memcpy(&buffer[*offset], avp->avp_public.avp_value->os.data, avp->avp_public.avp_value->os.len);
*offset += PAD4(avp->avp_public.avp_value->os.len);
break;
case AVP_TYPE_INTEGER32:
PUT_in_buf_32(avp->avp_public.avp_value->i32, buffer + *offset);
*offset += 4;
break;
case AVP_TYPE_INTEGER64:
PUT_in_buf_64(avp->avp_public.avp_value->i64, buffer + *offset);
*offset += 8;
break;
case AVP_TYPE_UNSIGNED32:
PUT_in_buf_32(avp->avp_public.avp_value->u32, buffer + *offset);
*offset += 4;
break;
case AVP_TYPE_UNSIGNED64:
PUT_in_buf_64(avp->avp_public.avp_value->u64, buffer + *offset);
*offset += 8;
break;
case AVP_TYPE_FLOAT32:
/* We read the f32 as "u32" here to avoid casting to uint make decimals go away.
The alternative would be something like "*(uint32_t *)(& f32)" but
then the compiler complains about strict-aliasing rules. */
PUT_in_buf_32(avp->avp_public.avp_value->u32, buffer + *offset);
*offset += 4;
break;
case AVP_TYPE_FLOAT64:
/* Same remark as previously */
PUT_in_buf_64(avp->avp_public.avp_value->u64, buffer + *offset);
*offset += 8;
break;
default:
ASSERT(0);
}
}
return 0;
}
/* Write a chain of AVPs in the buffer */
static int bufferize_chain(unsigned char * buffer, size_t buflen, size_t * offset, struct fd_list * list)
{
struct fd_list * avpch;
TRACE_ENTRY("%p %zd %p %p", buffer, buflen, offset, list);
for (avpch = list->next; avpch != list; avpch = avpch->next) {
/* Bufferize the AVP */
CHECK_FCT( bufferize_avp(buffer, buflen, offset, _A(avpch->o)) );
}
return 0;
}
/* Create the message buffer, in network-byte order. We browse the tree twice, this could be probably improved if needed */
int fd_msg_bufferize ( struct msg * msg, unsigned char ** buffer, size_t * len )
{
int ret = 0;
unsigned char * buf = NULL;
size_t offset = 0;
TRACE_ENTRY("%p %p %p", msg, buffer, len);
/* Check the parameters */
CHECK_PARAMS( buffer && CHECK_MSG(msg) );
/* Update the length. This also checks that all AVP have their values set */
CHECK_FCT( fd_msg_update_length(msg) );
/* Now allocate a buffer to store the message */
CHECK_MALLOC( buf = malloc(msg->msg_public.msg_length) );
/* Clear the memory, so that the padding is always 0 (should not matter) */
memset(buf, 0, msg->msg_public.msg_length);
/* Write the message header in the buffer */
CHECK_FCT_DO( ret = bufferize_msg(buf, msg->msg_public.msg_length, &offset, msg),
{
free(buf);
return ret;
} );
/* Write the list of AVPs */
CHECK_FCT_DO( ret = bufferize_chain(buf, msg->msg_public.msg_length, &offset, &msg->msg_chain.children),
{
free(buf);
return ret;
} );
ASSERT(offset == msg->msg_public.msg_length); /* or the msg_update_length is buggy */
if (len) {
*len = offset;
}
*buffer = buf;
return 0;
}
/***************************************************************************************************************/
/* Parsing buffers and building AVP objects lists (not parsing the AVP values which requires dictionary knowledge) */
/* Parse a buffer containing a supposed list of AVPs */
static int parsebuf_list(unsigned char * buf, size_t buflen, struct fd_list * head)
{
size_t offset = 0;
TRACE_ENTRY("%p %zd %p", buf, buflen, head);
while (offset < buflen) {
struct avp * avp;
if (buflen - offset < AVPHDRSZ_NOVEND) {
TRACE_DEBUG(INFO, "truncated buffer: remaining only %zd bytes", buflen - offset);
return EBADMSG;
}
/* Create a new AVP object */
CHECK_MALLOC( avp = malloc (sizeof(struct avp)) );
init_avp(avp);
/* Initialize the header */
avp->avp_public.avp_code = ntohl(*(uint32_t *)(buf + offset));
avp->avp_public.avp_flags = buf[offset + 4];
avp->avp_public.avp_len = ((uint32_t)buf[offset+5]) << 16 | ((uint32_t)buf[offset+6]) << 8 | ((uint32_t)buf[offset+7]) ;
offset += 8;
if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
if (buflen - offset < 4) {
TRACE_DEBUG(INFO, "truncated buffer: remaining only %zd bytes for vendor and data", buflen - offset);
free(avp);
return EBADMSG;
}
avp->avp_public.avp_vendor = ntohl(*(uint32_t *)(buf + offset));
offset += 4;
}
/* Check there is enough remaining data in the buffer */
if ( (avp->avp_public.avp_len > GETAVPHDRSZ(avp->avp_public.avp_flags))
&& (buflen - offset < avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags))) {
TRACE_DEBUG(INFO, "truncated buffer: remaining only %zd bytes for data, and avp data size is %d",
buflen - offset,
avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags));
free(avp);
return EBADMSG;
}
/* buf[offset] is now the beginning of the data */
avp->avp_source = &buf[offset];
/* Now eat the data and eventual padding */
offset += PAD4(avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags));
/* And insert this avp in the list, at the end */
fd_list_insert_before( head, &avp->avp_chain.chaining );
}
return 0;
}
/* Create a message object from a buffer. Dictionary objects are not resolved, AVP contents are not interpreted, buffer is saved in msg */
int fd_msg_parse_buffer ( unsigned char ** buffer, size_t buflen, struct msg ** msg )
{
struct msg * new = NULL;
int ret = 0;
uint32_t msglen = 0;
unsigned char * buf;
TRACE_ENTRY("%p %zd %p", buffer, buflen, msg);
CHECK_PARAMS( buffer && *buffer && msg && (buflen >= GETMSGHDRSZ()) );
buf = *buffer;
if ( buf[0] != DIAMETER_VERSION) {
TRACE_DEBUG(INFO, "Invalid version in message: %d (supported: %d)", buf[0], DIAMETER_VERSION);
return EBADMSG;
}
msglen = ntohl(*(uint32_t *)buf) & 0x00ffffff;
if ( buflen < msglen ) {
TRACE_DEBUG(INFO, "Truncated message (%zd / %d)", buflen, msglen );
return EBADMSG;
}
/* Create a new object */
CHECK_MALLOC( new = malloc (sizeof(struct msg)) );
/* Initialize the fields */
init_msg(new);
/* Now read from the buffer */
new->msg_public.msg_version = buf[0];
new->msg_public.msg_length = msglen;
new->msg_public.msg_flags = buf[4];
new->msg_public.msg_code = ntohl(*(uint32_t *)(buf+4)) & 0x00ffffff;
new->msg_public.msg_appl = ntohl(*(uint32_t *)(buf+8));
new->msg_public.msg_hbhid = ntohl(*(uint32_t *)(buf+12));
new->msg_public.msg_eteid = ntohl(*(uint32_t *)(buf+16));
/* Parse the AVP list */
CHECK_FCT_DO( ret = parsebuf_list(buf + GETMSGHDRSZ(), buflen - GETMSGHDRSZ(), &new->msg_chain.children), { destroy_tree(_C(new)); return ret; } );
/* Parsing successful */
new->msg_rawbuffer = buf;
*buffer = NULL;
*msg = new;
return 0;
}
/***************************************************************************************************************/
/* Parsing messages and AVP with dictionary information */
/* Resolve dictionary objects of the cmd and avp instances, from their headers.
* When the model is found, the data is interpreted from the avp_source buffer and copied to avp_storage.
* When the model is not found, the data is copied as rawdata and saved (in case we FW the message).
* Therefore, after this function has been called, the source buffer can be freed.
* For command, if the dictionary model is not found, an error is returned.
*/
static char error_message[256];
/* Process an AVP. If we are not in recheck, the avp_source must be set. */
static int parsedict_do_avp(struct dictionary * dict, struct avp * avp, int mandatory, struct fd_pei *error_info)
{
struct dict_avp_data dictdata;
struct dict_type_data derivedtypedata;
struct dict_object * avp_derived_type = NULL;
uint8_t * source;
TRACE_ENTRY("%p %p %d %p", dict, avp, mandatory, error_info);
/* First check we received an AVP as input */
CHECK_PARAMS( CHECK_AVP(avp) );
if (avp->avp_model != NULL) {
/* the model has already been resolved. we do check it is still valid */
CHECK_FCT( fd_dict_getval(avp->avp_model, &dictdata) );
if ( avp->avp_public.avp_code == dictdata.avp_code ) {
/* Ok then just process the children if any */
return parsedict_do_chain(dict, &avp->avp_chain.children, mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY), error_info);
} else {
/* We just erase the old model */
avp->avp_model = NULL;
}
}
/* Check if we already searched for this model without success */
if ((avp->avp_model_not_found.mnf_code != avp->avp_public.avp_code)
|| (avp->avp_model_not_found.mnf_vendor != avp->avp_public.avp_vendor)) {
/* Now try and resolve the model from the avp code and vendor */
if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
struct dict_avp_request_ex avpreq;
memset(&avpreq, 0, sizeof(avpreq));
avpreq.avp_vendor.vendor_id = avp->avp_public.avp_vendor;
avpreq.avp_data.avp_code = avp->avp_public.avp_code;
CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_STRUCT, &avpreq, &avp->avp_model, 0));
} else {
/* no vendor */
CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE, &avp->avp_public.avp_code, &avp->avp_model, 0));
}
if (!avp->avp_model) {
avp->avp_model_not_found.mnf_code = avp->avp_public.avp_code;
avp->avp_model_not_found.mnf_vendor = avp->avp_public.avp_vendor;
}
}
/* First handle the case where we have not found this AVP in the dictionary */
if (!avp->avp_model) {
if (mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY)) {
TRACE_DEBUG(INFO, "Unsupported mandatory AVP found");
if (error_info) {
error_info->pei_errcode = "DIAMETER_AVP_UNSUPPORTED";
error_info->pei_avp = avp;
} else {
char * buf = NULL;
size_t buflen;
CHECK_MALLOC(fd_msg_dump_treeview(&buf, &buflen, NULL, avp, NULL, 0, 0));
LOG_E("Unsupported AVP: %s", buf);
free(buf);
}
return ENOTSUP;
}
if (avp->avp_source) {
/* we must copy the data from the source to the internal buffer area */
CHECK_PARAMS( !avp->avp_rawdata );
avp->avp_rawlen = avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags );
if (avp->avp_rawlen) {
CHECK_MALLOC( avp->avp_rawdata = malloc(avp->avp_rawlen) );
memcpy(avp->avp_rawdata, avp->avp_source, avp->avp_rawlen);
}
avp->avp_source = NULL;
TRACE_DEBUG(FULL, "Unsupported optional AVP found, raw source data saved in avp_rawdata.");
}
return 0;
}
/* Ok we have resolved the object. Now we need to interpret its content. */
CHECK_FCT( fd_dict_getval(avp->avp_model, &dictdata) );
if (avp->avp_rawdata) {
/* This happens if the dictionary object was defined after the first check */
avp->avp_source = avp->avp_rawdata;
}
/* A bit of sanity here... */
ASSERT(CHECK_BASETYPE(dictdata.avp_basetype));
/* Check the size is valid */
if ((avp_value_sizes[dictdata.avp_basetype] != 0) &&
(avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ) != avp_value_sizes[dictdata.avp_basetype])) {
TRACE_DEBUG(INFO, "The AVP size is not suitable for the type");
if (error_info) {
error_info->pei_errcode = "DIAMETER_INVALID_AVP_LENGTH";
error_info->pei_avp = avp;
snprintf(error_message, sizeof(error_message), "I expected a size of %d for this AVP according to my dictionary", avp_value_sizes[dictdata.avp_basetype]);
error_info->pei_message = error_message;
} else {
char * buf = NULL;
size_t buflen;
CHECK_MALLOC(fd_msg_dump_treeview(&buf, &buflen, NULL, avp, NULL, 0, 0));
LOG_E("Invalid length AVP: %s", buf);
free(buf);
}
avp->avp_model = NULL;
return EBADMSG;
}
source = avp->avp_source;
avp->avp_source = NULL;
/* Now get the value inside */
switch (dictdata.avp_basetype) {
case AVP_TYPE_GROUPED: {
int ret;
/* This is a grouped AVP, so let's parse the list of AVPs inside */
CHECK_FCT_DO( ret = parsebuf_list(source, avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ), &avp->avp_chain.children),
{
if ((ret == EBADMSG) && (error_info)) {
error_info->pei_errcode = "DIAMETER_INVALID_AVP_VALUE";
error_info->pei_avp = avp;
snprintf(error_message, sizeof(error_message), "I cannot parse this AVP as a Grouped AVP");
error_info->pei_message = error_message;
}
avp->avp_source = source;
return ret;
} );
return parsedict_do_chain(dict, &avp->avp_chain.children, mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY), error_info);
}
case AVP_TYPE_OCTETSTRING:
/* We just have to copy the string into the storage area */
CHECK_PARAMS_DO( avp->avp_public.avp_len >= GETAVPHDRSZ( avp->avp_public.avp_flags ),
{
if (error_info) {
error_info->pei_errcode = "DIAMETER_INVALID_AVP_LENGTH";
error_info->pei_avp = avp;
}
avp->avp_source = source;
return EBADMSG;
} );
avp->avp_storage.os.len = avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags );
CHECK_MALLOC( avp->avp_storage.os.data = os0dup(source, avp->avp_storage.os.len) );
avp->avp_mustfreeos = 1;
break;
case AVP_TYPE_INTEGER32:
avp->avp_storage.i32 = (int32_t)ntohl(*(uint32_t *)source);
break;
case AVP_TYPE_INTEGER64:
/* the storage might not be aligned on 64b boundary, so no direct indirection here is possible */
{
uint64_t __stor;
memcpy(&__stor, source, sizeof(__stor));
avp->avp_storage.i64 = (int64_t)ntohll(__stor);
}
break;
case AVP_TYPE_UNSIGNED32:
case AVP_TYPE_FLOAT32: /* For float, we must not cast, or the value is changed. Instead we use implicit cast by changing the member of the union */
avp->avp_storage.u32 = (uint32_t)ntohl(*(uint32_t *)source);
break;
case AVP_TYPE_UNSIGNED64:
case AVP_TYPE_FLOAT64: /* same as 32 bits */
{
uint64_t __stor;
memcpy(&__stor, source, sizeof(__stor));
avp->avp_storage.u64 = (uint64_t)ntohll(__stor);
}
break;
}
/* Is there a derived type check function ? */
CHECK_FCT ( fd_dict_search ( dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &avp_derived_type, 0) );
if (avp_derived_type) {
CHECK_FCT( fd_dict_getval(avp_derived_type, &derivedtypedata) );
if (derivedtypedata.type_check != NULL) {
char * err;
int ret = (*derivedtypedata.type_check)( derivedtypedata.type_check_param, &avp->avp_storage, &err );
if (ret != 0) {
TRACE_DEBUG(INFO, "The AVP failed to pass the dictionary validation");
if (error_info) {
error_info->pei_errcode = "DIAMETER_INVALID_AVP_VALUE";
error_info->pei_avp = avp;
strncpy(error_message, err, sizeof(error_message));
error_info->pei_message = error_message;
} else {
char * buf = NULL;
size_t buflen;
CHECK_MALLOC(fd_msg_dump_treeview(&buf, &buflen, NULL, avp, NULL, 0, 0));
LOG_E("Invalid AVP: %s", buf);
free(buf);
}
return ret; /* should we just return EBADMSG? */
}
}
}
/* The value is now set, so set the data pointer and return 0 */
avp->avp_public.avp_value = &avp->avp_storage;
return 0;
}
/* Process a list of AVPs */
static int parsedict_do_chain(struct dictionary * dict, struct fd_list * head, int mandatory, struct fd_pei *error_info)
{
struct fd_list * avpch;
TRACE_ENTRY("%p %p %d %p", dict, head, mandatory, error_info);
/* Sanity check */
ASSERT ( head == head->head );
/* Now process the list */
for (avpch=head->next; avpch != head; avpch = avpch->next) {
CHECK_FCT( parsedict_do_avp(dict, _A(avpch->o), mandatory, error_info) );
}
/* Done */
return 0;
}
/* Process a msg header. */
static int parsedict_do_msg(struct dictionary * dict, struct msg * msg, int only_hdr, struct fd_pei *error_info)
{
int ret = 0;
TRACE_ENTRY("%p %p %d %p", dict, msg, only_hdr, error_info);
CHECK_PARAMS( CHECK_MSG(msg) );
/* First, check if we already have a model. */
if (msg->msg_model != NULL) {
/* Check if this model is still valid for the message data */
enum dict_object_type dicttype;
struct dict_cmd_data data;
ASSERT(((fd_dict_gettype(msg->msg_model, &dicttype) == 0) && (dicttype == DICT_COMMAND)));
(void)fd_dict_getval( msg->msg_model, &data);
if ((data.cmd_code != msg->msg_public.msg_code)
|| ((data.cmd_flag_val & data.cmd_flag_mask) != (msg->msg_public.msg_flags && data.cmd_flag_mask))) {
msg->msg_model = NULL;
} else {
goto chain;
}
}
/* Check if we already searched for this model without success */
if ((msg->msg_model_not_found.mnf_code == msg->msg_public.msg_code)
&& (msg->msg_model_not_found.mnf_flags == msg->msg_public.msg_flags)) {
goto no_model;
} else {
msg->msg_model_not_found.mnf_code = 0;
}
/* Look for the model from the header */
CHECK_FCT_DO( ret = fd_dict_search ( dict, DICT_COMMAND,
(msg->msg_public.msg_flags & CMD_FLAG_REQUEST) ? CMD_BY_CODE_R : CMD_BY_CODE_A,
&msg->msg_public.msg_code,
&msg->msg_model, ENOTSUP),
{
if (ret == ENOTSUP) {
/* update the model not found info */
msg->msg_model_not_found.mnf_code = msg->msg_public.msg_code;
msg->msg_model_not_found.mnf_flags = msg->msg_public.msg_flags;
goto no_model;
}
return ret;
} );
chain:
if (!only_hdr) {
/* Then process the children */
ret = parsedict_do_chain(dict, &msg->msg_chain.children, 1, error_info);
/* Free the raw buffer if any */
if ((ret == 0) && (msg->msg_rawbuffer != NULL)) {
free(msg->msg_rawbuffer);
msg->msg_rawbuffer=NULL;
}
}
return ret;
no_model:
if (error_info) {
error_info->pei_errcode = "DIAMETER_COMMAND_UNSUPPORTED";
error_info->pei_protoerr = 1;
}
return ENOTSUP;
}
int fd_msg_parse_dict ( msg_or_avp * object, struct dictionary * dict, struct fd_pei *error_info )
{
TRACE_ENTRY("%p %p %p", dict, object, error_info);
CHECK_PARAMS( VALIDATE_OBJ(object) );
if (error_info)
memset(error_info, 0, sizeof(struct fd_pei));
switch (_C(object)->type) {
case MSG_MSG:
return parsedict_do_msg(dict, _M(object), 0, error_info);
case MSG_AVP:
return parsedict_do_avp(dict, _A(object), 0, error_info);
default:
ASSERT(0);
}
return EINVAL;
}
/***************************************************************************************************************/
/* Parsing messages and AVP for rules (ABNF) compliance */
/* This function is used to get stats (first occurence position, last occurence position, number of occurences)
of AVP instances of a given model in a chain of AVP */
static void parserules_stat_avps( struct dict_object * model_avp, struct fd_list *list, int * count, int * firstpos, int * lastpos)
{
struct fd_list * li;
int curpos = 0; /* The current position in the list */
TRACE_ENTRY("%p %p %p %p %p", model_avp, list, count, firstpos, lastpos);
*count = 0; /* number of instances found */
*firstpos = 0; /* position of the first instance */
*lastpos = 0; /* position of the last instance, starting from the end */
for (li = list->next; li != list; li = li->next) {
/* Increment the current position counter */
curpos++;
/* If we previously saved a "lastpos" information, increment it */
if (*lastpos != 0)
(*lastpos)++;
/* Check the type of the next AVP. We can compare the references directly, it is safe. */
if (_A(li->o)->avp_model == model_avp) {
/* This AVP is of the type we are searching */
(*count)++;
/* If we don't have yet a "firstpos", save it */
if (*firstpos == 0)
*firstpos = curpos;
/* Reset the lastpos */
(*lastpos) = 1;
}
}
}
/* We use this structure as parameter for the next function */
struct parserules_data {
struct fd_list * sentinel; /* Sentinel of the list of children AVP */
struct fd_pei * pei; /* If the rule conflicts, save the error here */
};
/* Create an empty AVP of a given model (to use in Failed-AVP) */
static struct avp * empty_avp(struct dict_object * model_avp)
{
struct avp * avp = NULL;
struct dict_avp_data avp_info;
union avp_value val;
unsigned char os[1] = { '\0' };
/* Create an instance */
CHECK_FCT_DO( fd_msg_avp_new(model_avp, 0, &avp ), return NULL );
/* Type of the AVP */
CHECK_FCT_DO( fd_dict_getval(model_avp, &avp_info), return NULL );
/* Set an initial size */
avp->avp_public.avp_len = GETAVPHDRSZ( avp->avp_public.avp_flags ) + avp_value_sizes[avp_info.avp_basetype];
/* Prepare the empty value */
memset(&val, 0, sizeof(val));
switch (avp_info.avp_basetype) {
case AVP_TYPE_OCTETSTRING:
val.os.data = os;
val.os.len = sizeof(os);
avp->avp_public.avp_len += val.os.len;
case AVP_TYPE_INTEGER32:
case AVP_TYPE_INTEGER64:
case AVP_TYPE_UNSIGNED32:
case AVP_TYPE_UNSIGNED64:
case AVP_TYPE_FLOAT32:
case AVP_TYPE_FLOAT64:
CHECK_FCT_DO( fd_msg_avp_setvalue(avp, &val), return NULL );
case AVP_TYPE_GROUPED:
/* For AVP_TYPE_GROUPED we don't do anything */
break;
default:
ASSERT(0); /* not handled */
}
return avp;
}
/* Check that a list of AVPs is compliant with a given rule -- will be iterated on the list of rules */
static int parserules_check_one_rule(void * data, struct dict_rule_data *rule)
{
int count, first, last, min;
struct parserules_data * pr_data = data;
char * avp_name = "";
TRACE_ENTRY("%p %p", data, rule);
/* Get statistics of the AVP concerned by this rule in the parent instance */
parserules_stat_avps( rule->rule_avp, pr_data->sentinel, &count, &first, &last);
if (TRACE_BOOL(INFO))
{
struct dict_avp_data avpdata;
int ret;
ret = fd_dict_getval(rule->rule_avp, &avpdata);
if (ret == 0)
avp_name = avpdata.avp_name;
TRACE_DEBUG(ANNOYING, "Checking rule: p:%d(%d) m/M:%2d/%2d. Counted %d (first: %d, last:%d) of AVP '%s'",
rule->rule_position,
rule->rule_order,
rule->rule_min,
rule->rule_max,
count,
first,
last,
avp_name
);
}
/* Now check the rule is not conflicting */
/* Check the "min" value */
if ((min = rule->rule_min) == -1) {
if (rule->rule_position == RULE_OPTIONAL)
min = 0;
else
min = 1;
}
if (count < min) {
fd_log_error("Conflicting rule: the number of occurences (%d) is < the rule min (%d) for '%s'.", count, min, avp_name);
if (pr_data->pei) {
pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP";
pr_data->pei->pei_avp = empty_avp(rule->rule_avp);
pr_data->pei->pei_avp_free = 1;
}
return EBADMSG;
}
/* Check the "max" value */
if ((rule->rule_max != -1) && (count > rule->rule_max)) {
fd_log_error("Conflicting rule: the number of occurences (%d) is > the rule max (%d) for '%s'.", count, rule->rule_max, avp_name);
if (pr_data->pei) {
if (rule->rule_max == 0)
pr_data->pei->pei_errcode = "DIAMETER_AVP_NOT_ALLOWED";
else
pr_data->pei->pei_errcode = "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
pr_data->pei->pei_avp = empty_avp(rule->rule_avp); /* Well we are supposed to return the (max + 1)th instance of the AVP instead... Pfff... */ TODO("Improve...");
pr_data->pei->pei_avp_free = 1;
}
return EBADMSG;
}
/* Check the position and order (if relevant) */
switch (rule->rule_position) {
case RULE_OPTIONAL:
case RULE_REQUIRED:
/* No special position constraints */
break;
case RULE_FIXED_HEAD:
/* Since "0*1" is a valid rule specifier, we only reject cases where the AVP appears *after* its fixed position */
if (first > rule->rule_order) {
fd_log_error("Conflicting rule: the FIXED_HEAD AVP appears first in (%d) position, the rule requires (%d) for '%s'.", first, rule->rule_order, avp_name);
if (pr_data->pei) {
pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP";
pr_data->pei->pei_message = "AVP was not in its fixed position";
pr_data->pei->pei_avp = empty_avp(rule->rule_avp);
pr_data->pei->pei_avp_free = 1;
}
return EBADMSG;
}
break;
case RULE_FIXED_TAIL:
/* Since "0*1" is a valid rule specifier, we only reject cases where the AVP appears *before* its fixed position */
if (last > rule->rule_order) { /* We have a ">" here because we count in reverse order (i.e. from the end) */
fd_log_error("Conflicting rule: the FIXED_TAIL AVP appears last in (%d) position, the rule requires (%d) for '%s'.", last, rule->rule_order, avp_name);
if (pr_data->pei) {
pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP";
pr_data->pei->pei_message = "AVP was not in its fixed position";
pr_data->pei->pei_avp = empty_avp(rule->rule_avp);
pr_data->pei->pei_avp_free = 1;
}
return EBADMSG;
}
break;
default:
/* What is this position ??? */
ASSERT(0);
return ENOTSUP;
}
/* We've checked all the parameters */
return 0;
}
/* Check the rules recursively */
static int parserules_do ( struct dictionary * dict, msg_or_avp * object, struct fd_pei *error_info, int mandatory)
{
struct parserules_data data;
struct dict_object * model = NULL;
TRACE_ENTRY("%p %p %p %d", dict, object, error_info, mandatory);
/* object has already been checked and dict-parsed when we are called. */
/* First, handle the cases where there is no model */
{
if (CHECK_MSG(object)) {
if ( _M(object)->msg_public.msg_flags & CMD_FLAG_ERROR ) {
/* The case of error messages: the ABNF is different */
CHECK_FCT( fd_dict_get_error_cmd(dict, &model) );
} else {
model = _M(object)->msg_model;
}
/* Commands MUST be supported in the dictionary */
if (model == NULL) {
TRACE_DEBUG(INFO, "Message with no dictionary model. EBADMSG");
if (error_info) {
error_info->pei_errcode = "DIAMETER_COMMAND_UNSUPPORTED";
error_info->pei_protoerr = 1;
}
return EBADMSG;
}
}
/* AVP with the 'M' flag must also be recognized in the dictionary -- except inside an optional grouped AVP */
if (CHECK_AVP(object) && ((model = _A(object)->avp_model) == NULL)) {
if ( mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) {
/* Return an error in this case */
TRACE_DEBUG(INFO, "Mandatory AVP with no dictionary model. EBADMSG");
if (error_info) {
error_info->pei_errcode = "DIAMETER_AVP_UNSUPPORTED";
error_info->pei_avp = object;
}
return EBADMSG;
} else {
/* We don't know any rule for this object, so assume OK */
TRACE_DEBUG(FULL, "Unknown informational AVP, ignoring...");
return 0;
}
}
}
/* At this point we know "model" is set and points to the object's model */
/* If we are an AVP with no children, just return OK */
if (CHECK_AVP(object)) {
struct dict_avp_data dictdata;
CHECK_FCT( fd_dict_getval(model, &dictdata) );
if (dictdata.avp_basetype != AVP_TYPE_GROUPED) {
/* This object has no children and no rules */
return 0;
}
}
/* If this object has children, first check the rules for all its children */
{
int is_child_mand = 0;
struct fd_list * ch = NULL;
if ( CHECK_MSG(object)
|| (mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) )
is_child_mand = 1;
for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) {
CHECK_FCT( parserules_do ( dict, _C(ch->o), error_info, is_child_mand ) );
}
}
/* Now check all rules of this object */
data.sentinel = &_C(object)->children;
data.pei = error_info;
CHECK_FCT( fd_dict_iterate_rules ( model, &data, parserules_check_one_rule ) );
return 0;
}
int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct fd_pei *error_info)
{
TRACE_ENTRY("%p %p %p", object, dict, error_info);
if (error_info)
memset(error_info, 0, sizeof(struct fd_pei));
/* Resolve the dictionary objects when missing. This also validates the object. */
CHECK_FCT( fd_msg_parse_dict ( object, dict, error_info ) );
/* Call the recursive function */
return parserules_do ( dict, object, error_info, 1 ) ;
}
/***************************************************************************************************************/
/* Compute the lengh of an object and its subtree. */
int fd_msg_update_length ( msg_or_avp * object )
{
size_t sz = 0;
struct dict_object * model;
union {
struct dict_cmd_data cmddata;
struct dict_avp_data avpdata;
} dictdata;
TRACE_ENTRY("%p", object);
/* Get the model of the object. This also validates the object */
CHECK_FCT( fd_msg_model ( object, &model ) );
/* Get the information of the model */
if (model) {
CHECK_FCT( fd_dict_getval(model, &dictdata) );
} else {
/* For unknown AVP, just don't change the size */
if (_C(object)->type == MSG_AVP)
return 0;
}
/* Deal with easy cases: AVPs without children */
if ((_C(object)->type == MSG_AVP) && (dictdata.avpdata.avp_basetype != AVP_TYPE_GROUPED)) {
/* Sanity check */
ASSERT(FD_IS_LIST_EMPTY(&_A(object)->avp_chain.children));
/* Now check that the data is set in the AVP */
CHECK_PARAMS( _A(object)->avp_public.avp_value );
sz = GETAVPHDRSZ( _A(object)->avp_public.avp_flags );
switch (dictdata.avpdata.avp_basetype) {
case AVP_TYPE_OCTETSTRING:
sz += _A(object)->avp_public.avp_value->os.len;
break;
case AVP_TYPE_INTEGER32:
case AVP_TYPE_INTEGER64:
case AVP_TYPE_UNSIGNED32:
case AVP_TYPE_UNSIGNED64:
case AVP_TYPE_FLOAT32:
case AVP_TYPE_FLOAT64:
sz += avp_value_sizes[dictdata.avpdata.avp_basetype];
break;
default:
/* Something went wrong... */
ASSERT(0);
}
}
else /* message or grouped AVP */
{
struct fd_list * ch = NULL;
/* First, compute the header size */
if (_C(object)->type == MSG_AVP) {
sz = GETAVPHDRSZ( _A(object)->avp_public.avp_flags );
} else {
sz = GETMSGHDRSZ( );
}
/* Recurse in all children and update the sz information */
for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) {
CHECK_FCT( fd_msg_update_length ( ch->o ) );
/* Add the padded size to the parent */
sz += PAD4( _A(ch->o)->avp_public.avp_len );
}
}
/* When we arrive here, the "sz" variable contains the size to write in the object */
if (_C(object)->type == MSG_AVP)
_A(object)->avp_public.avp_len = sz;
else
_M(object)->msg_public.msg_length = sz;
return 0;
}
/***************************************************************************************************************/
/* Macro to check if further callbacks must be called */
#define TEST_ACTION_STOP() \
if ((*msg == NULL) || (*action != DISP_ACT_CONT)) \
goto out;
/* Call all dispatch callbacks for a given message */
int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, char ** error_code, char ** drop_reason, struct msg ** drop_msg)
{
struct dictionary * dict;
struct dict_object * app;
struct dict_object * cmd;
struct avp * avp;
struct fd_list * cb_list;
int ret = 0, r2;
TRACE_ENTRY("%p %p %p %p", msg, session, action, error_code);
CHECK_PARAMS( msg && CHECK_MSG(*msg) && action);
if (error_code)
*error_code = NULL;
if (drop_reason)
*drop_reason = NULL;
*action = DISP_ACT_CONT;
/* Take the dispatch lock */
CHECK_FCT( pthread_rwlock_rdlock(&fd_disp_lock) );
pthread_cleanup_push( fd_cleanup_rwlock, &fd_disp_lock );
/* First, call the DISP_HOW_ANY callbacks */
CHECK_FCT_DO( ret = fd_disp_call_cb_int( NULL, msg, NULL, session, action, NULL, NULL, NULL, NULL, drop_reason, drop_msg ), goto out );
TEST_ACTION_STOP();
/* If we don't know the model at this point, we stop cause we cannot get the dictionary. It's invalid: an error should already have been trigged by ANY callbacks */
CHECK_PARAMS_DO(cmd = (*msg)->msg_model, { ret = EINVAL; goto out; } );
/* Now resolve message application */
CHECK_FCT_DO( ret = fd_dict_getdict( cmd, &dict ), goto out );
CHECK_FCT_DO( ret = fd_dict_search( dict, DICT_APPLICATION, APPLICATION_BY_ID, &(*msg)->msg_public.msg_appl, &app, 0 ), goto out );
if (app == NULL) {
if ((*msg)->msg_public.msg_flags & CMD_FLAG_REQUEST) {
if (error_code)
*error_code = "DIAMETER_APPLICATION_UNSUPPORTED";
*action = DISP_ACT_ERROR;
} else {
*drop_reason = "Internal error: Received this answer to a local query with an unsupported application";
*drop_msg = *msg;
*msg = NULL;
}
goto out;
}
/* So start browsing the message */
CHECK_FCT_DO( ret = fd_msg_browse( *msg, MSG_BRW_FIRST_CHILD, &avp, NULL ), goto out );
while (avp != NULL) {
/* For unknown AVP, we don't have a callback registered, so just skip */
if (avp->avp_model) {
struct dict_object * enumval = NULL;
/* Get the list of callback for this AVP */
CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_AVP, avp->avp_model, &cb_list), goto out );
/* We search enumerated values only in case of non-grouped AVP */
if ( avp->avp_public.avp_value ) {
struct dict_object * type;
/* Check if the AVP has a constant value */
CHECK_FCT_DO( ret = fd_dict_search(dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &type, 0), goto out );
if (type) {
struct dict_enumval_request req;
memset(&req, 0, sizeof(struct dict_enumval_request));
req.type_obj = type;
memcpy( &req.search.enum_value, avp->avp_public.avp_value, sizeof(union avp_value) );
CHECK_FCT_DO( ret = fd_dict_search(dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enumval, 0), goto out );
}
}
/* Call the callbacks */
CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, avp, session, action, app, cmd, avp->avp_model, enumval, drop_reason, drop_msg ), goto out );
TEST_ACTION_STOP();
}
/* Go to next AVP */
CHECK_FCT_DO( ret = fd_msg_browse( avp, MSG_BRW_WALK, &avp, NULL ), goto out );
}
/* Now call command and application callbacks */
CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_COMMAND, cmd, &cb_list), goto out );
CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, NULL, session, action, app, cmd, NULL, NULL, drop_reason, drop_msg ), goto out );
TEST_ACTION_STOP();
if (app) {
CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_APPLICATION, app, &cb_list), goto out );
CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, NULL, session, action, app, cmd, NULL, NULL, drop_reason, drop_msg ), goto out );
TEST_ACTION_STOP();
}
out:
; /* some systems would complain without this */
pthread_cleanup_pop(0);
CHECK_POSIX_DO(r2 = pthread_rwlock_unlock(&fd_disp_lock), /* ignore */ );
return ret ?: r2;
}
freeDiameter/libfdproto/dictionary.c 0000644 0001750 0001750 00000240513 13324704466 017155 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2015, 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. *
*********************************************************************************************************/
#include "fdproto-internal.h"
#include
/* Names of the base types */
const char * type_base_name[] = { /* must keep in sync with dict_avp_basetype */
"GROUPED", /* AVP_TYPE_GROUPED */
"OCTETSTRING", /* AVP_TYPE_OCTETSTRING */
"INTEGER32", /* AVP_TYPE_INTEGER32 */
"INTEGER64", /* AVP_TYPE_INTEGER64 */
"UNSIGNED32", /* AVP_TYPE_UNSIGNED32 */
"UNSIGNED64", /* AVP_TYPE_UNSIGNED64 */
"FLOAT32", /* AVP_TYPE_FLOAT32 */
"FLOAT64" /* AVP_TYPE_FLOAT64 */
};
/* The number of lists in an object */
#define NB_LISTS_PER_OBJ 3
/* Some eye catchers definitions */
#define OBJECT_EYECATCHER (0x0b13c7)
#define DICT_EYECATCHER (0x00d1c7)
/* Definition of the dictionary objects */
struct dict_object {
enum dict_object_type type; /* What type of object is this? */
int objeyec;/* eyecatcher for this object */
int typeyec;/* eyecatcher for this type of object */
struct dictionary *dico; /* The dictionary this object belongs to */
union {
struct dict_vendor_data vendor; /* datastr_len = strlen(vendor_name) */
struct dict_application_data application; /* datastr_len = strlen(application_name) */
struct dict_type_data type; /* datastr_len = strlen(type_name) */
struct dict_enumval_data enumval; /* datastr_len = strlen(enum_name) */
struct dict_avp_data avp; /* datastr_len = strlen(avp_name) */
struct dict_cmd_data cmd; /* datastr_len = strlen(cmd_name) */
struct dict_rule_data rule; /* datastr_len = 0 */
} data; /* The data of this object */
size_t datastr_len; /* cached length of the string inside the data. Saved when the object is created. */
struct dict_object * parent; /* The parent of this object, if any */
struct fd_list list[NB_LISTS_PER_OBJ];/* used to chain objects.*/
/* More information about the lists :
- the use for each list depends on the type of object. See detail below.
- a sentinel for a list has its 'o' field cleared. (this is the criteria to detect end of a loop)
- The lists are always ordered. The criteria are described below. the functions to order them are referenced in dict_obj_info
- The dict_lock must be held for any list operation.
=> VENDORS:
list[0]: list of the vendors, ordered by their id. The sentinel is g_dict_vendors (vendor with id 0)
list[1]: sentinel for the list of AVPs from this vendor, ordered by AVP code.
list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name (fd_os_cmp).
=> APPLICATIONS:
list[0]: list of the applications, ordered by their id. The sentinel is g_dict_applications (application with id 0)
list[1]: not used
list[2]: not used.
=> TYPES:
list[0]: list of the types, ordered by their names. The sentinel is g_list_types.
list[1]: sentinel for the type_enum list of this type, ordered by their constant name (fd_os_cmp).
list[2]: sentinel for the type_enum list of this type, ordered by their constant value.
=> TYPE_ENUMS:
list[0]: list of the contants for a given type, ordered by the constant name (fd_os_cmp). Sentinel is a (list[1]) element of a TYPE object.
list[1]: list of the contants for a given type, ordered by the constant value. Sentinel is a (list[2]) element of a TYPE object.
list[2]: not used
=> AVPS:
list[0]: list of the AVP from a given vendor, ordered by avp code. Sentinel is a list[1] element of a VENDOR object.
list[1]: list of the AVP from a given vendor, ordered by avp name (fd_os_cmp). Sentinel is a list[2] element of a VENDOR object.
list[2]: sentinel for the rule list that apply to this AVP.
=> COMMANDS:
list[0]: list of the commands, ordered by their names (fd_os_cmp). The sentinel is g_list_cmd_name.
list[1]: list of the commands, ordered by their command code and 'R' flag. The sentinel is g_list_cmd_code.
list[2]: sentinel for the rule list that apply to this command.
=> RULES:
list[0]: list of the rules for a given (grouped) AVP or Command, ordered by the AVP vendor & code to which they refer. sentinel is list[2] of a command or (grouped) avp.
list[1]: not used
list[2]: not used.
*/
/* Sentinel for the dispatch callbacks */
struct fd_list disp_cbs;
};
/* Definition of the dictionary structure */
struct dictionary {
int dict_eyec; /* Eye-catcher for the dictionary (DICT_EYECATCHER) */
pthread_rwlock_t dict_lock; /* The global rwlock for the dictionary */
struct dict_object dict_vendors; /* Sentinel for the list of vendors, corresponding to vendor 0 */
struct dict_object dict_applications; /* Sentinel for the list of applications, corresponding to app 0 */
struct fd_list dict_types; /* Sentinel for the list of types */
struct fd_list dict_cmd_name; /* Sentinel for the list of commands, ordered by names */
struct fd_list dict_cmd_code; /* Sentinel for the list of commands, ordered by codes */
struct dict_object dict_cmd_error; /* Special command object for answers with the 'E' bit set */
int dict_count[DICT_TYPE_MAX + 1]; /* Number of objects of each type */
};
/* Forward declarations of dump functions */
static DECLARE_FD_DUMP_PROTOTYPE(dump_vendor_data, void * data );
static DECLARE_FD_DUMP_PROTOTYPE(dump_application_data, void * data );
static DECLARE_FD_DUMP_PROTOTYPE(dump_type_data, void * data );
/* the dump function for enum has a different prototype since it need the datatype */
static DECLARE_FD_DUMP_PROTOTYPE(dump_avp_data, void * data );
static DECLARE_FD_DUMP_PROTOTYPE(dump_command_data, void * data );
static DECLARE_FD_DUMP_PROTOTYPE(dump_rule_data, void * data );
/* Forward declarations of search functions */
static int search_vendor ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
static int search_application ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
static int search_type ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
static int search_enumval ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
static int search_avp ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
static int search_cmd ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
static int search_rule ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
/* The following array contains lot of data about the different types of objects, for automated handling */
static struct {
enum dict_object_type type; /* information for this type */
char * name; /* string describing this object, for debug */
size_t datasize; /* The size of the data structure */
int parent; /* 0: never; 1: may; 2: must */
enum dict_object_type parenttype; /* The type of the parent, when relevant */
int eyecatcher; /* A kind of signature for this object */
DECLARE_FD_DUMP_PROTOTYPE( (*dump_data), void * data ); /* The function to dump the data section */
int (*search_fct)(struct dictionary * dict, int criteria, const void * what, struct dict_object **result );; /* The function to search an object of this type */
int haslist[NB_LISTS_PER_OBJ]; /* Tell if this list is used */
} dict_obj_info[] = { { 0, "(error)", 0, 0, 0, 0, NULL, NULL, {0, 0, 0} }
/* type name datasize parent parenttype
eyecatcher dump_data search_fct, haslist[] */
,{ DICT_VENDOR, "VENDOR", sizeof(struct dict_vendor_data), 0, 0,
OBJECT_EYECATCHER + 1, dump_vendor_data, search_vendor, { 1, 0, 0 } }
,{ DICT_APPLICATION, "APPLICATION", sizeof(struct dict_application_data), 1, DICT_VENDOR,
OBJECT_EYECATCHER + 2, dump_application_data, search_application, { 1, 0, 0 } }
,{ DICT_TYPE, "TYPE", sizeof(struct dict_type_data), 1, DICT_APPLICATION,
OBJECT_EYECATCHER + 3, dump_type_data, search_type, { 1, 0, 0 } }
,{ DICT_ENUMVAL, "ENUMVAL", sizeof(struct dict_enumval_data), 2, DICT_TYPE,
OBJECT_EYECATCHER + 4, NULL, search_enumval, { 1, 1, 0 } }
,{ DICT_AVP, "AVP", sizeof(struct dict_avp_data), 1, DICT_TYPE,
OBJECT_EYECATCHER + 5, dump_avp_data, search_avp, { 1, 1, 0 } }
,{ DICT_COMMAND, "COMMAND", sizeof(struct dict_cmd_data), 1, DICT_APPLICATION,
OBJECT_EYECATCHER + 6, dump_command_data, search_cmd, { 1, 1, 0 } }
,{ DICT_RULE, "RULE", sizeof(struct dict_rule_data), 2, -1 /* special case: grouped avp or command */,
OBJECT_EYECATCHER + 7, dump_rule_data, search_rule, { 1, 0, 0 } }
};
/* Macro to verify a "type" value */
#define CHECK_TYPE( type ) ( ((type) > 0) && ((type) <= DICT_TYPE_MAX) )
/* Cast macro */
#define _O( object ) ((struct dict_object *) (object))
/* Get information line for a given object */
#define _OBINFO(object) (dict_obj_info[CHECK_TYPE(_O(object)->type) ? _O(object)->type : 0])
/*******************************************************************************************************/
/*******************************************************************************************************/
/* */
/* Objects management */
/* */
/*******************************************************************************************************/
/*******************************************************************************************************/
/* Functions to manage the objects creation and destruction. */
/* Duplicate a string inplace, save its length */
#define DUP_string_len( str, plen ) { \
*(plen) = strlen((str)); \
str = os0dup( str, *(plen)); \
}
/* Initialize an object */
static void init_object( struct dict_object * obj, enum dict_object_type type )
{
int i;
TRACE_ENTRY("%p %d", obj, type);
/* Clean the object first */
memset ( obj, 0, sizeof(struct dict_object));
CHECK_PARAMS_DO( CHECK_TYPE(type), return );
obj->type = type;
obj->objeyec = OBJECT_EYECATCHER;
obj->typeyec = _OBINFO(obj).eyecatcher;
/* We don't initialize the data nor the parent here */
/* Now init the lists */
for (i=0; ilist[i], obj);
else
fd_list_init(&obj->list[i], NULL);
}
fd_list_init(&obj->disp_cbs, NULL);
}
/* Initialize the "data" part of an object */
static int init_object_data(struct dict_object * dest, void * source, enum dict_object_type type)
{
TRACE_ENTRY("%p %p %d", dest, source, type);
CHECK_PARAMS( dest && source && CHECK_TYPE(type) );
/* Generic: copy the full data structure */
memcpy( &dest->data, source, dict_obj_info[type].datasize );
/* Then strings must be duplicated, not copied */
/* This function might be simplified by always defining the "name" field as the first field of the structures, but... it's error-prone */
switch (type) {
case DICT_VENDOR:
DUP_string_len( dest->data.vendor.vendor_name, &dest->datastr_len );
break;
case DICT_APPLICATION:
DUP_string_len( dest->data.application.application_name, &dest->datastr_len );
break;
case DICT_TYPE:
DUP_string_len( dest->data.type.type_name, &dest->datastr_len );
break;
case DICT_ENUMVAL:
DUP_string_len( dest->data.enumval.enum_name, &dest->datastr_len );
break;
case DICT_AVP:
DUP_string_len( dest->data.avp.avp_name, &dest->datastr_len );
break;
case DICT_COMMAND:
DUP_string_len( dest->data.cmd.cmd_name, &dest->datastr_len );
break;
default:
/* Nothing to do for RULES */
;
}
return 0;
}
/* Check that an object is valid (1: OK, 0: error) */
static int verify_object( struct dict_object * obj )
{
TRACE_ENTRY("%p", obj);
CHECK_PARAMS_DO( obj
&& (obj->objeyec == OBJECT_EYECATCHER)
&& CHECK_TYPE(obj->type)
&& (obj->typeyec == dict_obj_info[obj->type].eyecatcher),
{
if (obj) {
TRACE_DEBUG(FULL, "Invalid object: %p, obj->objeyec: %x/%x, obj->type: %d, obj->objeyec: %x/%x, obj->typeyec: %x/%x",
obj,
obj->objeyec, OBJECT_EYECATCHER,
obj->type,
obj->objeyec, OBJECT_EYECATCHER,
obj->typeyec, _OBINFO(obj).eyecatcher);
} else {
TRACE_DEBUG(FULL, "Invalid object : NULL pointer");
}
return 0;
} );
/* The object is probably valid. */
return 1;
}
/* Free the data associated to an object */
static void destroy_object_data(struct dict_object * obj)
{
/* TRACE_ENTRY("%p", obj); */
switch (obj->type) {
case DICT_VENDOR:
free( obj->data.vendor.vendor_name );
break;
case DICT_APPLICATION:
free( obj->data.application.application_name );
break;
case DICT_TYPE:
free( obj->data.type.type_name );
break;
case DICT_ENUMVAL:
free( obj->data.enumval.enum_name );
break;
case DICT_AVP:
free( obj->data.avp.avp_name );
break;
case DICT_COMMAND:
free( obj->data.cmd.cmd_name );
break;
default:
/* nothing to do */
;
}
}
/* Forward declaration */
static void destroy_object(struct dict_object * obj);
/* Destroy all objects in a list - the lock must be held */
static void destroy_list(struct fd_list * head)
{
/* TRACE_ENTRY("%p", head); */
/* loop in the list */
while (!FD_IS_LIST_EMPTY(head))
{
/* When destroying the object, it is unlinked from the list */
destroy_object(_O(head->next->o));
}
}
/* Free an object and its sublists */
static void destroy_object(struct dict_object * obj)
{
int i;
/* TRACE_ENTRY("%p", obj); */
/* Update global count */
if (obj->dico)
obj->dico->dict_count[obj->type]--;
/* Mark the object as invalid */
obj->objeyec = 0xdead;
/* First, destroy the data associated to the object */
destroy_object_data(obj);
for (i=0; ilist[i] );
else
/* This is either a sentinel or unused (=emtpy) list, let's destroy it */
destroy_list( &obj->list[i] );
}
/* Unlink all elements from the dispatch list; they will be freed when callback is unregistered */
CHECK_POSIX_DO( pthread_rwlock_wrlock(&fd_disp_lock), /* continue */ );
while (!FD_IS_LIST_EMPTY(&obj->disp_cbs)) {
fd_list_unlink( obj->disp_cbs.next );
}
CHECK_POSIX_DO( pthread_rwlock_unlock(&fd_disp_lock), /* continue */ );
/* Last, destroy the object */
free(obj);
}
/*******************************************************************************************************/
/*******************************************************************************************************/
/* */
/* Compare functions */
/* */
/*******************************************************************************************************/
/*******************************************************************************************************/
/* Compare two values */
#define ORDER_scalar( i1, i2 ) \
((i1 < i2 ) ? -1 : ( i1 > i2 ? 1 : 0 ))
/* Compare two vendor objects by their id (checks already performed) */
static int order_vendor_by_id ( struct dict_object *o1, struct dict_object *o2 )
{
TRACE_ENTRY("%p %p", o1, o2);
return ORDER_scalar( o1->data.vendor.vendor_id, o2->data.vendor.vendor_id );
}
/* Compare two application objects by their id (checks already performed) */
static int order_appli_by_id ( struct dict_object *o1, struct dict_object *o2 )
{
TRACE_ENTRY("%p %p", o1, o2);
return ORDER_scalar( o1->data.application.application_id, o2->data.application.application_id );
}
/* Compare two type objects by their name (checks already performed) */
static int order_type_by_name ( struct dict_object *o1, struct dict_object *o2 )
{
TRACE_ENTRY("%p %p", o1, o2);
return fd_os_cmp( o1->data.type.type_name, o1->datastr_len, o2->data.type.type_name, o2->datastr_len );
}
/* Compare two type_enum objects by their names (checks already performed) */
static int order_enum_by_name ( struct dict_object *o1, struct dict_object *o2 )
{
TRACE_ENTRY("%p %p", o1, o2);
return fd_os_cmp( o1->data.enumval.enum_name, o1->datastr_len, o2->data.enumval.enum_name, o2->datastr_len );
}
/* Compare two type_enum objects by their values (checks already performed) */
static int order_enum_by_val ( struct dict_object *o1, struct dict_object *o2 )
{
TRACE_ENTRY("%p %p", o1, o2);
/* The comparison function depends on the type of data */
switch ( o1->parent->data.type.type_base ) {
case AVP_TYPE_OCTETSTRING:
return fd_os_cmp( o1->data.enumval.enum_value.os.data, o1->data.enumval.enum_value.os.len,
o2->data.enumval.enum_value.os.data, o2->data.enumval.enum_value.os.len);
case AVP_TYPE_INTEGER32:
return ORDER_scalar( o1->data.enumval.enum_value.i32, o2->data.enumval.enum_value.i32 );
case AVP_TYPE_INTEGER64:
return ORDER_scalar( o1->data.enumval.enum_value.i64, o2->data.enumval.enum_value.i64 );
case AVP_TYPE_UNSIGNED32:
return ORDER_scalar( o1->data.enumval.enum_value.u32, o2->data.enumval.enum_value.u32 );
case AVP_TYPE_UNSIGNED64:
return ORDER_scalar( o1->data.enumval.enum_value.u64, o2->data.enumval.enum_value.u64 );
case AVP_TYPE_FLOAT32:
return ORDER_scalar( o1->data.enumval.enum_value.f32, o2->data.enumval.enum_value.f32 );
case AVP_TYPE_FLOAT64:
return ORDER_scalar( o1->data.enumval.enum_value.f64, o2->data.enumval.enum_value.f64 );
case AVP_TYPE_GROUPED:
default:
ASSERT(0);
}
return 0;
}
/* Compare two avp objects by their codes (checks already performed) */
static int order_avp_by_code ( struct dict_object *o1, struct dict_object *o2 )
{
TRACE_ENTRY("%p %p", o1, o2);
return ORDER_scalar( o1->data.avp.avp_code, o2->data.avp.avp_code );
}
/* Compare two avp objects by their names (checks already performed) */
static int order_avp_by_name ( struct dict_object *o1, struct dict_object *o2 )
{
TRACE_ENTRY("%p %p", o1, o2);
return fd_os_cmp( o1->data.avp.avp_name, o1->datastr_len, o2->data.avp.avp_name, o2->datastr_len );
}
/* Compare two command objects by their names (checks already performed) */
static int order_cmd_by_name ( struct dict_object *o1, struct dict_object *o2 )
{
TRACE_ENTRY("%p %p", o1, o2);
return fd_os_cmp( o1->data.cmd.cmd_name, o1->datastr_len, o2->data.cmd.cmd_name, o2->datastr_len );
}
/* Compare two command objects by their codes and flags (request or answer) (checks already performed) */
static int order_cmd_by_codefl( struct dict_object *o1, struct dict_object *o2 )
{
uint8_t fl1, fl2;
int cmp = 0;
TRACE_ENTRY("%p %p", o1, o2);
cmp = ORDER_scalar( o1->data.cmd.cmd_code, o2->data.cmd.cmd_code );
if (cmp)
return cmp;
/* Same command code, we must compare the value of the 'R' flag */
fl1 = o1->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST;
fl2 = o2->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST;
/* We want requests first, so we reverse the operators here */
return ORDER_scalar(fl2, fl1);
}
/* Compare two rule object by the AVP vendor & code that they refer (checks already performed) */
static int order_rule_by_avpvc ( struct dict_object *o1, struct dict_object *o2 )
{
TRACE_ENTRY("%p %p", o1, o2);
return ORDER_scalar(o1->data.rule.rule_avp->data.avp.avp_vendor, o2->data.rule.rule_avp->data.avp.avp_vendor)
?: ORDER_scalar(o1->data.rule.rule_avp->data.avp.avp_code, o2->data.rule.rule_avp->data.avp.avp_code) ;
}
/*******************************************************************************************************/
/*******************************************************************************************************/
/* */
/* Search functions */
/* */
/*******************************************************************************************************/
/*******************************************************************************************************/
/* Functions used to search for objects in the lists, according to some criteria */
/* On a general note, if result is not NULL, ENOENT is not returned but *result is NULL. */
/* The following macros assume that "what", "ret", "result" (variables), and "end" (label) exist
in the local context where they are called. They are meant to be called only from the functions that follow. */
/* For searchs of type "xxx_OF_xxx": children's parent or default parent */
#define SEARCH_childs_parent( type_of_child, default_parent ) { \
struct dict_object *__child = (struct dict_object *) what; \
CHECK_PARAMS_DO( verify_object(__child) && \
(__child->type == (type_of_child)), \
{ ret = EINVAL; goto end; } ); \
ret = 0; \
if (result) \
*result = (__child->parent ? __child->parent :(default_parent));\
}
/* For search of strings in lists. isindex= 1 if the string is the ordering key of the list */
/* it is expected that object->datastr_len is the length of the datafield parameter */
#define SEARCH_os0_l( str, len, sentinel, datafield, isindex ) { \
char * __str = (char *) (str); \
size_t __strlen = (size_t)(len); \
int __cmp; \
struct fd_list * __li; \
ret = 0; \
for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \
__cmp = fd_os_cmp(__str, __strlen, \
_O(__li->o)->data. datafield, _O(__li->o)->datastr_len);\
if (__cmp == 0) { \
if (result) \
*result = _O(__li->o); \
goto end; \
} \
if ((isindex) && (__cmp < 0)) \
break; \
} \
if (result) \
*result = NULL; \
else \
ret = ENOENT; \
}
/* When len is not provided */
#define SEARCH_os0( str, sentinel, datafield, isindex ) { \
char * _str = (char *) (str); \
size_t _strlen = strlen(_str); \
SEARCH_os0_l( _str, _strlen, sentinel, datafield, isindex ); \
}
/* For search of octetstrings in lists. */
#define SEARCH_os( str, strlen, sentinel, osdatafield, isindex ) { \
uint8_t * __str = (uint8_t *) (str); \
size_t __strlen = (size_t)(strlen); \
int __cmp; \
struct fd_list * __li; \
ret = 0; \
for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \
__cmp = fd_os_cmp(__str, __strlen, \
_O(__li->o)->data. osdatafield .data, \
_O(__li->o)->data. osdatafield .len); \
if (__cmp == 0) { \
if (result) \
*result = _O(__li->o); \
goto end; \
} \
if ((isindex) && (__cmp < 0)) \
break; \
} \
if (result) \
*result = NULL; \
else \
ret = ENOENT; \
}
/* For search of AVP name in rule lists -- the list is not ordered by AVP names! */
#define SEARCH_ruleavpname( str, strlen, sentinel ) { \
char * __str = (char *) (str); \
size_t __strlen = (size_t) (strlen); \
int __cmp; \
struct fd_list * __li; \
ret = 0; \
for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \
__cmp = fd_os_cmp(__str, __strlen, \
_O(__li->o)->data.rule.rule_avp->data.avp.avp_name, \
_O(__li->o)->data.rule.rule_avp->datastr_len); \
if (__cmp == 0) { \
if (result) \
*result = _O(__li->o); \
goto end; \
} \
} \
if (result) \
*result = NULL; \
else \
ret = ENOENT; \
}
/* For search of scalars in lists. isindex= 1 if the value is the ordering key of the list */
#define SEARCH_scalar( value, sentinel, datafield, isindex, defaultobj ) { \
int __cmp; \
struct fd_list * __li; \
ret = 0; \
if ( ((defaultobj) != NULL) \
&& (_O(defaultobj)->data. datafield == value)) { \
if (result) \
*result = _O(defaultobj); \
goto end; \
} \
for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \
__cmp= ORDER_scalar(value, _O(__li->o)->data. datafield ); \
if (__cmp == 0) { \
if (result) \
*result = _O(__li->o); \
goto end; \
} \
if ((isindex) && (__cmp < 0)) \
break; \
} \
if (result) \
*result = NULL; \
else \
ret = ENOENT; \
}
/* For search of commands in lists by code and flag. R_flag_val = 0 or CMD_FLAG_REQUEST */
#define SEARCH_codefl( value, R_flag_val, sentinel) { \
int __cmp; \
struct fd_list * __li; \
ret = 0; \
for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \
__cmp = ORDER_scalar(value, \
_O(__li->o)->data.cmd.cmd_code ); \
if (__cmp == 0) { \
uint8_t __mask, __val; \
__mask = _O(__li->o)->data.cmd.cmd_flag_mask; \
__val = _O(__li->o)->data.cmd.cmd_flag_val; \
if ( ! (__mask & CMD_FLAG_REQUEST) ) \
continue; \
if ( ( __val & CMD_FLAG_REQUEST ) != R_flag_val ) \
continue; \
if (result) \
*result = _O(__li->o); \
goto end; \
} \
if (__cmp < 0) \
break; \
} \
if (result) \
*result = NULL; \
else \
ret = ENOENT; \
}
/* For searchs of type "xxx_OF_xxx": if the search object is sentinel list for the "what" object */
#define SEARCH_sentinel( type_of_what, what_list_nr, sentinel_list_nr ) { \
struct dict_object *__what = (struct dict_object *) what; \
CHECK_PARAMS_DO( verify_object(__what) && \
(__what->type == (type_of_what)), \
{ ret = EINVAL; goto end; } ); \
ret = 0; \
if (result) { \
/* this is similar to the "container_of" */ \
*result = (struct dict_object *)((char *)(__what->list[what_list_nr].head) - \
(size_t)&(((struct dict_object *)0)->list[sentinel_list_nr])); \
} \
}
static int search_vendor ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
{
int ret = 0;
vendor_id_t id;
TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
switch (criteria) {
case VENDOR_BY_ID:
id = *(vendor_id_t *) what;
SEARCH_scalar( id, &dict->dict_vendors.list[0], vendor.vendor_id, 1, &dict->dict_vendors );
break;
case VENDOR_BY_NAME:
/* "what" is a vendor name */
SEARCH_os0( what, &dict->dict_vendors.list[0], vendor.vendor_name, 0);
break;
case VENDOR_OF_APPLICATION:
/* "what" should be an application object */
SEARCH_childs_parent( DICT_APPLICATION, &dict->dict_vendors );
break;
case VENDOR_OF_AVP:
/* "what" should be an avp object */
SEARCH_sentinel( DICT_AVP, 0, 1 );
break;
default:
/* Invalid criteria */
CHECK_PARAMS( criteria = 0 );
}
end:
return ret;
}
static int search_application ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
{
int ret = 0;
application_id_t id;
TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
switch (criteria) {
case APPLICATION_BY_ID:
id = *(application_id_t *) what;
SEARCH_scalar( id, &dict->dict_applications.list[0], application.application_id, 1, &dict->dict_applications );
break;
case APPLICATION_BY_NAME:
/* "what" is an application name */
SEARCH_os0( what, &dict->dict_applications.list[0], application.application_name, 0);
break;
case APPLICATION_OF_TYPE:
/* "what" should be a type object */
SEARCH_childs_parent( DICT_TYPE, &dict->dict_applications );
break;
case APPLICATION_OF_COMMAND:
/* "what" should be a command object */
SEARCH_childs_parent( DICT_COMMAND, &dict->dict_applications );
break;
default:
/* Invalid criteria */
CHECK_PARAMS( criteria = 0 );
}
end:
return ret;
}
static int search_type ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
{
int ret = 0;
TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
switch (criteria) {
case TYPE_BY_NAME:
/* "what" is a type name */
SEARCH_os0( what, &dict->dict_types, type.type_name, 1);
break;
case TYPE_OF_ENUMVAL:
/* "what" should be a type_enum object */
SEARCH_childs_parent( DICT_ENUMVAL, NULL );
break;
case TYPE_OF_AVP:
/* "what" should be an avp object */
SEARCH_childs_parent( DICT_AVP, NULL );
break;
default:
/* Invalid criteria */
CHECK_PARAMS( criteria = 0 );
}
end:
return ret;
}
static int search_enumval ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
{
int ret = 0;
TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
switch (criteria) {
case ENUMVAL_BY_STRUCT:
{
struct dict_object * parent = NULL;
struct dict_enumval_request * _what = (struct dict_enumval_request *) what;
CHECK_PARAMS( _what && ( _what->type_obj || _what->type_name ) );
if (_what->type_obj != NULL) {
parent = _what->type_obj;
CHECK_PARAMS( verify_object(parent) && (parent->type == DICT_TYPE) );
} else {
/* We received only the type name, we must find it first */
CHECK_FCT_DO( search_type( dict, TYPE_BY_NAME, _what->type_name, &parent ),
CHECK_PARAMS( 0 ) );
}
/* From here the "parent" object is valid */
if ( _what->search.enum_name != NULL ) {
/* We are looking for this string */
SEARCH_os0( _what->search.enum_name, &parent->list[1], enumval.enum_name, 1 );
} else {
/* We are looking for the value in enum_value */
switch (parent->data.type.type_base) {
case AVP_TYPE_OCTETSTRING:
SEARCH_os( _what->search.enum_value.os.data,
_what->search.enum_value.os.len,
&parent->list[2],
enumval.enum_value.os ,
1 );
break;
case AVP_TYPE_INTEGER32:
SEARCH_scalar( _what->search.enum_value.i32,
&parent->list[2],
enumval.enum_value.i32,
1,
(struct dict_object *)NULL);
break;
case AVP_TYPE_INTEGER64:
SEARCH_scalar( _what->search.enum_value.i64,
&parent->list[2],
enumval.enum_value.i64,
1,
(struct dict_object *)NULL);
break;
case AVP_TYPE_UNSIGNED32:
SEARCH_scalar( _what->search.enum_value.u32,
&parent->list[2],
enumval.enum_value.u32,
1,
(struct dict_object *)NULL);
break;
case AVP_TYPE_UNSIGNED64:
SEARCH_scalar( _what->search.enum_value.u64,
&parent->list[2],
enumval.enum_value.u64,
1,
(struct dict_object *)NULL);
break;
case AVP_TYPE_FLOAT32:
SEARCH_scalar( _what->search.enum_value.f32,
&parent->list[2],
enumval.enum_value.f32,
1,
(struct dict_object *)NULL);
break;
case AVP_TYPE_FLOAT64:
SEARCH_scalar( _what->search.enum_value.f64,
&parent->list[2],
enumval.enum_value.f64,
1,
(struct dict_object *)NULL);
break;
default:
/* Invalid parent type basetype */
CHECK_PARAMS( parent = NULL );
}
}
}
break;
default:
/* Invalid criteria */
CHECK_PARAMS( criteria = 0 );
}
end:
return ret;
}
static int search_avp ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
{
int ret = 0;
TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
switch (criteria) {
case AVP_BY_CODE:
{
avp_code_t code;
code = *(avp_code_t *) what;
SEARCH_scalar( code, &dict->dict_vendors.list[1], avp.avp_code, 1, (struct dict_object *)NULL );
}
break;
case AVP_BY_NAME:
/* "what" is the AVP name, vendor 0 */
SEARCH_os0( what, &dict->dict_vendors.list[2], avp.avp_name, 1);
break;
case AVP_BY_CODE_AND_VENDOR:
case AVP_BY_NAME_AND_VENDOR:
{
struct dict_avp_request * _what = (struct dict_avp_request *) what;
struct dict_object * vendor = NULL;
CHECK_PARAMS( (criteria != AVP_BY_NAME_AND_VENDOR) || _what->avp_name );
/* Now look for the vendor first */
CHECK_FCT( search_vendor( dict, VENDOR_BY_ID, &_what->avp_vendor, &vendor ) );
if (vendor == NULL) {
if (result)
*result = NULL;
else
ret = ENOENT;
goto end;
}
/* We now have our vendor = head of the appropriate avp list */
if (criteria == AVP_BY_NAME_AND_VENDOR) {
SEARCH_os0( _what->avp_name, &vendor->list[2], avp.avp_name, 1);
} else {
/* AVP_BY_CODE_AND_VENDOR */
SEARCH_scalar( _what->avp_code, &vendor->list[1], avp.avp_code, 1, (struct dict_object *)NULL );
}
}
break;
case AVP_BY_STRUCT:
{
struct dict_avp_request_ex * _what = (struct dict_avp_request_ex *) what;
struct dict_object * vendor = NULL;
CHECK_PARAMS( _what->avp_vendor.vendor || _what->avp_vendor.vendor_id || _what->avp_vendor.vendor_name );
CHECK_PARAMS( _what->avp_data.avp_code || _what->avp_data.avp_name );
/* Now look for the vendor first */
if (_what->avp_vendor.vendor) {
CHECK_PARAMS( ! _what->avp_vendor.vendor_id && ! _what->avp_vendor.vendor_name );
vendor = _what->avp_vendor.vendor;
} else if (_what->avp_vendor.vendor_id) {
CHECK_PARAMS( ! _what->avp_vendor.vendor_name );
CHECK_FCT( search_vendor( dict, VENDOR_BY_ID, &_what->avp_vendor.vendor_id, &vendor ) );
} else {
CHECK_FCT( search_vendor( dict, VENDOR_BY_NAME, _what->avp_vendor.vendor_name, &vendor ) );
}
if (vendor == NULL) {
if (result)
*result = NULL;
else
ret = ENOENT;
goto end;
}
/* We now have our vendor = head of the appropriate avp list */
if (_what->avp_data.avp_code) {
CHECK_PARAMS( ! _what->avp_data.avp_name );
SEARCH_scalar( _what->avp_data.avp_code, &vendor->list[1], avp.avp_code, 1, (struct dict_object *)NULL );
} else {
SEARCH_os0( _what->avp_data.avp_name, &vendor->list[2], avp.avp_name, 1);
}
}
break;
case AVP_BY_NAME_ALL_VENDORS:
{
struct fd_list * li;
size_t wl = strlen((char *)what);
/* First, search for vendor 0 */
SEARCH_os0_l( what, wl, &dict->dict_vendors.list[2], avp.avp_name, 1);
/* If not found, loop for all vendors, until found */
for (li = dict->dict_vendors.list[0].next; li != &dict->dict_vendors.list[0]; li = li->next) {
SEARCH_os0_l( what, wl, &_O(li->o)->list[2], avp.avp_name, 1);
}
}
break;
default:
/* Invalid criteria */
CHECK_PARAMS( criteria = 0 );
}
end:
return ret;
}
static int search_cmd ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
{
int ret = 0;
TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
switch (criteria) {
case CMD_BY_NAME:
/* "what" is a command name */
SEARCH_os0( what, &dict->dict_cmd_name, cmd.cmd_name, 1);
break;
case CMD_BY_CODE_R:
case CMD_BY_CODE_A:
{
command_code_t code;
uint8_t searchfl = 0;
/* The command code that we are searching */
code = *(command_code_t *) what;
/* The flag (request or answer) of the command we are searching */
if (criteria == CMD_BY_CODE_R) {
searchfl = CMD_FLAG_REQUEST;
}
/* perform the search */
SEARCH_codefl( code, searchfl, &dict->dict_cmd_code );
}
break;
case CMD_ANSWER:
{
/* "what" is a command object of type "request" */
struct dict_object * req = (struct dict_object *) what;
struct dict_object * ans = NULL;
CHECK_PARAMS( verify_object(req)
&& (req->type == DICT_COMMAND)
&& (req->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST)
&& (req->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST) );
/* The answer is supposed to be the next element in the list, if it exists */
ans = req->list[1].next->o;
if ( ans == NULL ) {
TRACE_DEBUG( FULL, "the request was the last element in the list" );
ret = ENOENT;
goto end;
}
/* Now check that the ans element is really the correct one */
if ( (ans->data.cmd.cmd_code != req->data.cmd.cmd_code)
|| (!(ans->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST))
|| ( ans->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST ) ) {
TRACE_DEBUG( FULL, "the answer does not follow the request in the list" );
ret = ENOENT;
goto end;
}
if (result)
*result = ans;
ret = 0;
}
break;
default:
/* Invalid criteria */
CHECK_PARAMS( criteria = 0 );
}
end:
return ret;
}
static int search_rule ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
{
int ret = 0;
TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
switch (criteria) {
case RULE_BY_AVP_AND_PARENT:
{
struct dict_object * parent = NULL;
struct dict_object * avp = NULL;
struct dict_rule_request * _what = (struct dict_rule_request *) what;
CHECK_PARAMS( _what
&& (parent = _what->rule_parent)
&& (avp = _what->rule_avp ) );
CHECK_PARAMS( verify_object(parent)
&& ((parent->type == DICT_COMMAND)
|| ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED))) );
CHECK_PARAMS( verify_object(avp) && (avp->type == DICT_AVP) );
/* Perform the search */
SEARCH_ruleavpname( avp->data.avp.avp_name, avp->datastr_len, &parent->list[2]);
}
break;
default:
/* Invalid criteria */
CHECK_PARAMS( criteria = 0 );
}
end:
return ret;
}
/*******************************************************************************************************/
/*******************************************************************************************************/
/* */
/* Dump / debug functions */
/* */
/*******************************************************************************************************/
/*******************************************************************************************************/
/* The following functions are used to debug the module, and allow to print out the content of the dictionary */
static DECLARE_FD_DUMP_PROTOTYPE(dump_vendor_data, void * data )
{
struct dict_vendor_data * vendor = (struct dict_vendor_data *)data;
return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: %-6u \"%s\"", vendor->vendor_id, vendor->vendor_name);
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_application_data, void * data )
{
struct dict_application_data * appli = (struct dict_application_data *) data;
return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: %-6u \"%s\"", appli->application_id, appli->application_name);
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_type_data, void * data )
{
struct dict_type_data * type = ( struct dict_type_data * ) data;
return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: %-12s \"%s\"",
type_base_name[type->type_base],
type->type_name);
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_enumval_data, struct dict_enumval_data * enumval, enum dict_avp_basetype type )
{
const int LEN_MAX = 20;
CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "data: (%-12s) \"%s\" -> ", type_base_name[type], enumval->enum_name), return NULL);
switch (type) {
case AVP_TYPE_OCTETSTRING:
{
int i, n=LEN_MAX;
if (enumval->enum_value.os.len < LEN_MAX)
n = enumval->enum_value.os.len;
for (i=0; i < n; i++)
CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "0x%2hhX/'%c' ", enumval->enum_value.os.data[i], ASCII(enumval->enum_value.os.data[i])), return NULL);
if (n == LEN_MAX)
CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "..."), return NULL);
}
break;
case AVP_TYPE_INTEGER32:
CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%i", enumval->enum_value.i32), return NULL);
break;
case AVP_TYPE_INTEGER64:
CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%"PRId64, enumval->enum_value.i64), return NULL);
break;
case AVP_TYPE_UNSIGNED32:
CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%u", enumval->enum_value.u32), return NULL);
break;
case AVP_TYPE_UNSIGNED64:
CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%"PRIu64, enumval->enum_value.u64), return NULL);
break;
case AVP_TYPE_FLOAT32:
CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%f", enumval->enum_value.f32), return NULL);
break;
case AVP_TYPE_FLOAT64:
CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%g", enumval->enum_value.f64), return NULL);
break;
default:
CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "??? (ERROR unknown type %d)", type), return NULL);
}
return *buf;
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_avp_data, void * data )
{
struct dict_avp_data * avp = (struct dict_avp_data * ) data;
return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: v/m:" DUMP_AVPFL_str "/" DUMP_AVPFL_str ", %12s, %-6u \"%s\"",
DUMP_AVPFL_val(avp->avp_flag_val),
DUMP_AVPFL_val(avp->avp_flag_mask),
type_base_name[avp->avp_basetype],
avp->avp_code,
avp->avp_name );
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_command_data, void * data )
{
struct dict_cmd_data * cmd = (struct dict_cmd_data *) data;
return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: v/m:" DUMP_CMDFL_str "/" DUMP_CMDFL_str ", %-6u \"%s\"",
DUMP_CMDFL_val(cmd->cmd_flag_val), DUMP_CMDFL_val(cmd->cmd_flag_mask), cmd->cmd_code, cmd->cmd_name);
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_rule_data, void * data )
{
struct dict_rule_data * rule = (struct dict_rule_data * )data;
return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: pos:%d ord:%d m/M:%2d/%2d avp:\"%s\"",
rule->rule_position,
rule->rule_order,
rule->rule_min,
rule->rule_max,
rule->rule_avp->data.avp.avp_name);
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_object, struct dict_object * obj, int parents, int depth, int indent );
static DECLARE_FD_DUMP_PROTOTYPE(dump_list, struct fd_list * sentinel, int parents, int depth, int indent )
{
struct fd_list * li = sentinel;
/* We don't lock here, the caller must have taken the dictionary lock for reading already */
if (FD_IS_LIST_EMPTY(sentinel)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n%*s{empty list}", indent, ""), return NULL);
} else {
while (li->next != sentinel)
{
li = li->next;
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, _O(li->o), parents, depth, indent ), return NULL);
}
}
return *buf;
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_object, struct dict_object * obj, int parents, int depth, int indent )
{
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%*s{dictobj}(@%p): ", indent, "", obj), return NULL);
if (!verify_object(obj)) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL);
return *buf;
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s p:%p ",
_OBINFO(obj).name,
obj->parent), return NULL);
if (obj->type == DICT_ENUMVAL) {
CHECK_MALLOC_DO( dump_enumval_data ( FD_DUMP_STD_PARAMS, &obj->data.enumval, obj->parent->data.type.type_base ), return NULL);
} else {
CHECK_MALLOC_DO( _OBINFO(obj).dump_data(FD_DUMP_STD_PARAMS, &obj->data), return NULL);
}
if (parents) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n%*sparent:", indent + 1, ""), return NULL);
CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, obj->parent, parents-1, 0, 0 ), return NULL);
}
if (depth) {
int i;
for (i=0; ilist[i].o == NULL) && (obj->list[i].next != &obj->list[i])) {
CHECK_MALLOC_DO( dump_list(FD_DUMP_STD_PARAMS, &obj->list[i], 0, depth - 1, indent + 2), return NULL);
break; /* we get duplicate information sorted by another criteria otherwise, which is not very useful */
}
}
}
return *buf;
}
DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump_object, struct dict_object * obj)
{
FD_DUMP_HANDLE_OFFSET();
CHECK_MALLOC_DO( dump_object(FD_DUMP_STD_PARAMS, obj, 1, 2, 0), return NULL);
return *buf;
}
DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump, struct dictionary * dict)
{
int i;
struct fd_list * li;
FD_DUMP_HANDLE_OFFSET();
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{dictionary}(@%p): ", dict), return NULL);
if ((dict == NULL) || (dict->dict_eyec != DICT_EYECATCHER)) {
return fd_dump_extend(FD_DUMP_STD_PARAMS, "INVALID/NULL");
}
CHECK_POSIX_DO( pthread_rwlock_rdlock( &dict->dict_lock ), /* ignore */ );
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : VENDORS / AVP / RULES}\n", dict), goto error);
CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, &dict->dict_vendors, 0, 3, 3 ), goto error);
for (li = dict->dict_vendors.list[0].next; li != &dict->dict_vendors.list[0]; li = li->next) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, li->o, 0, 3, 3 ), goto error);
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : APPLICATIONS}\n", dict), goto error);
CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, &dict->dict_applications, 0, 1, 3 ), goto error);
for (li = dict->dict_applications.list[0].next; li != &dict->dict_applications.list[0]; li = li->next) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, li->o, 0, 1, 3 ), goto error);
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : TYPES / ENUMVAL}", dict), goto error);
CHECK_MALLOC_DO( dump_list(FD_DUMP_STD_PARAMS, &dict->dict_types, 0, 2, 3 ), goto error);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : COMMANDS / RULES}", dict), goto error);
CHECK_MALLOC_DO( dump_list(FD_DUMP_STD_PARAMS, &dict->dict_cmd_code, 0, 0, 3 ), goto error);
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : statistics}", dict), goto error);
for (i=1; i<=DICT_TYPE_MAX; i++)
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n %5d: %s", dict->dict_count[i], dict_obj_info[i].name), goto error);
CHECK_POSIX_DO( pthread_rwlock_unlock( &dict->dict_lock ), /* ignore */ );
return *buf;
error:
/* Free the rwlock */
CHECK_POSIX_DO( pthread_rwlock_unlock( &dict->dict_lock ), /* ignore */ );
return NULL;
}
/**************************** Dump AVP values ********************************/
/* Default dump functions */
static DECLARE_FD_DUMP_PROTOTYPE(dump_val_os, union avp_value * value)
{
int i;
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "<"), return NULL);
for (i = 0; i < value->os.len; i++) {
if (i == 1024) { /* Dump only up to 1024 bytes of the buffer */
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "[...] (len=%zd)", value->os.len), return NULL);
break;
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s%02hhX", (i==0 ? "" : " "), value->os.data[i]), return NULL);
}
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, ">"), return NULL);
return *buf;
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_val_i32, union avp_value * value)
{
return fd_dump_extend( FD_DUMP_STD_PARAMS, "%i (0x%x)", value->i32, value->i32);
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_val_i64, union avp_value * value)
{
return fd_dump_extend( FD_DUMP_STD_PARAMS, "%" PRId64 " (0x%" PRIx64 ")", value->i64, value->i64);
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_val_u32, union avp_value * value)
{
return fd_dump_extend( FD_DUMP_STD_PARAMS, "%u (0x%x)", value->u32, value->u32);
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_val_u64, union avp_value * value)
{
return fd_dump_extend( FD_DUMP_STD_PARAMS, "%" PRIu64 " (0x%" PRIx64 ")", value->u64, value->u64);
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_val_f32, union avp_value * value)
{
return fd_dump_extend( FD_DUMP_STD_PARAMS, "%f", value->f32);
}
static DECLARE_FD_DUMP_PROTOTYPE(dump_val_f64, union avp_value * value)
{
return fd_dump_extend( FD_DUMP_STD_PARAMS, "%g", value->f64);
}
/* Get the dump function for basic dict_avp_basetype */
static DECLARE_FD_DUMP_PROTOTYPE((*get_default_dump_val_cb(enum dict_avp_basetype datatype)), union avp_value *)
{
switch (datatype) {
case AVP_TYPE_OCTETSTRING:
return &dump_val_os;
case AVP_TYPE_INTEGER32:
return &dump_val_i32;
case AVP_TYPE_INTEGER64:
return &dump_val_i64;
case AVP_TYPE_UNSIGNED32:
return &dump_val_u32;
case AVP_TYPE_UNSIGNED64:
return &dump_val_u64;
case AVP_TYPE_FLOAT32:
return &dump_val_f32;
case AVP_TYPE_FLOAT64:
return &dump_val_f64;
case AVP_TYPE_GROUPED:
TRACE_DEBUG(FULL, "error: grouped AVP with a value!");
}
return NULL;
}
/* indent inside an object (duplicate from messages.c) */
#define INOBJHDR "%*s "
#define INOBJHDRVAL indent<0 ? 1 : indent, indent<0 ? "-" : "|"
typedef DECLARE_FD_DUMP_PROTOTYPE((*dump_val_cb_t), union avp_value *);
/* Formatter for the AVP value dump line */
static DECLARE_FD_DUMP_PROTOTYPE(dump_avp_val, union avp_value *avp_value,
dump_val_cb_t def_dump_val_cb,
dump_val_cb_t dump_val_cb,
enum dict_avp_basetype datatype,
char * type_name,
char * const_name,
int indent,
int header)
{
if (header) {
/* Header for all AVP values dumps: */
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, INOBJHDR "value ", INOBJHDRVAL), return NULL);
/* If the type is provided, write it */
if (type_name) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "t: '%s' ", type_name), return NULL);
}
/* Always give the base datatype anyway */
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(%s) ", type_base_name[datatype]), return NULL);
/* Now, the value */
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "v: "), return NULL);
}
if (const_name) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s' (", const_name), return NULL);
}
if (dump_val_cb) {
CHECK_MALLOC_DO( (*dump_val_cb)( FD_DUMP_STD_PARAMS, avp_value), fd_dump_extend( FD_DUMP_STD_PARAMS, "(dump failed)"));
} else {
CHECK_MALLOC_DO( (*def_dump_val_cb)( FD_DUMP_STD_PARAMS, avp_value), return NULL);
}
if (const_name) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, ")"), return NULL);
}
/* Done! */
return *buf;
}
/* Dump the value of an AVP of known type into the returned str */
DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump_avp_value, union avp_value *avp_value, struct dict_object * model, int indent, int header)
{
DECLARE_FD_DUMP_PROTOTYPE((*dump_val_cb), union avp_value *avp_value) = NULL;
struct dict_object * type = NULL;
char * type_name = NULL;
char * const_name = NULL;
FD_DUMP_HANDLE_OFFSET();
/* Handle invalid parameters */
if (!avp_value) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(avp value not set)"), return NULL);
return *buf;
}
if (!model) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(model not set)"), return NULL);
return *buf;
}
if (! ( verify_object(model) && (model->type == DICT_AVP) )) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(invalid model)"), return NULL);
return *buf;
}
/* Get the type definition of this AVP */
type = model->parent;
if (type) {
struct dict_enumval_request request;
struct dict_object * enumval = NULL;
type_name = type->data.type.type_name;
/* overwrite the dump function ? */
if (type->data.type.type_dump)
dump_val_cb = type->data.type.type_dump;
/* Now check if the AVP value matches a constant */
memset(&request, 0, sizeof(request));
request.type_obj = type;
memcpy(&request.search.enum_value, avp_value, sizeof(union avp_value));
/* bypass checks */
if ((search_enumval( type->dico, ENUMVAL_BY_STRUCT, &request, &enumval ) == 0) && (enumval)) {
/* We found a constant, get its name */
const_name = enumval->data.enumval.enum_name;
}
}
/* And finally, dump the value */
CHECK_MALLOC_DO( dump_avp_val(FD_DUMP_STD_PARAMS, avp_value, get_default_dump_val_cb(model->data.avp.avp_basetype), dump_val_cb, model->data.avp.avp_basetype, type_name, const_name, indent, header), return NULL );
return *buf;
}
/*******************************************************************************************************/
/*******************************************************************************************************/
/* */
/* Exported functions */
/* */
/*******************************************************************************************************/
/*******************************************************************************************************/
/* These are the functions exported outside libfreeDiameter. */
/* Get the data associated to an object */
int fd_dict_gettype ( struct dict_object * object, enum dict_object_type * type)
{
TRACE_ENTRY("%p %p", object, type);
CHECK_PARAMS( type && verify_object(object) );
/* Copy the value and return */
*type = object->type;
return 0;
}
int fd_dict_getdict ( struct dict_object * object, struct dictionary ** dict)
{
TRACE_ENTRY("%p %p", object, dict);
CHECK_PARAMS( dict && verify_object(object) );
/* Copy the value and return */
*dict = object->dico;
return 0;
}
/* Get the data associated to an object */
int fd_dict_getval ( struct dict_object * object, void * val)
{
TRACE_ENTRY("%p %p", object, val);
CHECK_PARAMS( val && verify_object(object) );
/* Copy the value and return */
memcpy(val, &object->data, _OBINFO(object).datasize);;
return 0;
}
/* Add a new object in the dictionary */
int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * data, struct dict_object * parent, struct dict_object **ref )
{
int ret = 0;
struct dict_object * new = NULL;
struct dict_object * vendor = NULL;
struct dict_object * locref = NULL;
TRACE_ENTRY("%p %d(%s) %p %p %p", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, data, parent, ref);
/* Check parameters */
CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) && data );
/* Check the "parent" parameter */
switch (dict_obj_info[type].parent) {
case 0: /* parent is forbidden */
CHECK_PARAMS_DO( parent == NULL, goto error_param );
case 1: /* parent is optional */
if (parent == NULL)
break;
case 2: /* parent is mandatory */
CHECK_PARAMS_DO( verify_object(parent), goto error_param );
if (type == DICT_RULE ) { /* Special case : grouped AVP or Command parents are allowed */
CHECK_PARAMS_DO( (parent->type == DICT_COMMAND )
|| ( (parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED ) ), goto error_param );
} else {
CHECK_PARAMS_DO( parent->type == dict_obj_info[type].parenttype, goto error_param );
}
}
/* For AVP object, we must also check that the "vendor" referenced exists */
if (type == DICT_AVP) {
CHECK_FCT_DO( fd_dict_search( dict, DICT_VENDOR, VENDOR_BY_ID, &(((struct dict_avp_data *)data)->avp_vendor), (void*)&vendor, ENOENT ),
{ TRACE_DEBUG(INFO, "Unable to find vendor '%d' referenced in the AVP data", ((struct dict_avp_data *)data)->avp_vendor); goto error_param; } );
/* Also check if a parent is provided, that the type are the same */
if (parent) {
CHECK_PARAMS_DO( parent->data.type.type_base == ((struct dict_avp_data *)data)->avp_basetype, goto error_param );
}
}
/* For RULE object, we must also check that the "avp" referenced exists */
if (type == DICT_RULE) {
CHECK_PARAMS_DO( verify_object(((struct dict_rule_data *)data)->rule_avp), goto error_param );
CHECK_PARAMS_DO( ((struct dict_rule_data *)data)->rule_avp->type == DICT_AVP, goto error_param );
}
/* For COMMAND object, check that the 'R' flag is fixed */
if (type == DICT_COMMAND) {
CHECK_PARAMS_DO( ((struct dict_cmd_data *)data)->cmd_flag_mask & CMD_FLAG_REQUEST, goto error_param );
}
/* We have to check that the new values are not equal to the sentinels */
if (type == DICT_VENDOR) {
CHECK_PARAMS_DO( ((struct dict_vendor_data *)data)->vendor_id != 0, goto error_param );
}
if (type == DICT_APPLICATION) {
CHECK_PARAMS_DO( ((struct dict_application_data *)data)->application_id != 0, goto error_param );
}
/* Parameters are valid, create the new object */
CHECK_MALLOC( new = malloc(sizeof(struct dict_object)) );
/* Initialize the data of the new object */
init_object(new, type);
init_object_data(new, data, type);
new->dico = dict;
new->parent = parent;
/* We will change the dictionary => acquire the write lock */
CHECK_POSIX_DO( ret = pthread_rwlock_wrlock(&dict->dict_lock), goto error_free );
/* Now link the object -- this also checks that no object with same keys already exists */
switch (type) {
case DICT_VENDOR:
/* A vendor object is linked in the g_dict_vendors.list[0], by their id */
ret = fd_list_insert_ordered ( &dict->dict_vendors.list[0], &new->list[0], (int (*)(void*, void *))order_vendor_by_id, (void **)&locref );
if (ret)
goto error_unlock;
break;
case DICT_APPLICATION:
/* An application object is linked in the g_dict_applciations.list[0], by their id */
ret = fd_list_insert_ordered ( &dict->dict_applications.list[0], &new->list[0], (int (*)(void*, void *))order_appli_by_id, (void **)&locref );
if (ret)
goto error_unlock;
break;
case DICT_TYPE:
/* A type object is linked in g_list_types by its name */
ret = fd_list_insert_ordered ( &dict->dict_types, &new->list[0], (int (*)(void*, void *))order_type_by_name, (void **)&locref );
if (ret)
goto error_unlock;
break;
case DICT_ENUMVAL:
/* A type_enum object is linked in it's parent 'type' object lists 1 and 2 by its name and values */
ret = fd_list_insert_ordered ( &parent->list[1], &new->list[0], (int (*)(void*, void *))order_enum_by_name, (void **)&locref );
if (ret)
goto error_unlock;
ret = fd_list_insert_ordered ( &parent->list[2], &new->list[1], (int (*)(void*, void *))order_enum_by_val, (void **)&locref );
if (ret) {
fd_list_unlink(&new->list[0]);
goto error_unlock;
}
break;
case DICT_AVP:
/* An avp object is linked in lists 1 and 2 of its vendor, by code and name */
ret = fd_list_insert_ordered ( &vendor->list[1], &new->list[0], (int (*)(void*, void *))order_avp_by_code, (void **)&locref );
if (ret)
goto error_unlock;
ret = fd_list_insert_ordered ( &vendor->list[2], &new->list[1], (int (*)(void*, void *))order_avp_by_name, (void **)&locref );
if (ret) {
fd_list_unlink(&new->list[0]);
goto error_unlock;
}
break;
case DICT_COMMAND:
/* A command object is linked in g_list_cmd_name and g_list_cmd_code by its name and code */
ret = fd_list_insert_ordered ( &dict->dict_cmd_code, &new->list[1], (int (*)(void*, void *))order_cmd_by_codefl, (void **)&locref );
if (ret)
goto error_unlock;
ret = fd_list_insert_ordered ( &dict->dict_cmd_name, &new->list[0], (int (*)(void*, void *))order_cmd_by_name, (void **)&locref );
if (ret) {
fd_list_unlink(&new->list[1]);
goto error_unlock;
}
break;
case DICT_RULE:
/* A rule object is linked in list[2] of its parent command or AVP by the name of the AVP it refers */
ret = fd_list_insert_ordered ( &parent->list[2], &new->list[0], (int (*)(void*, void *))order_rule_by_avpvc, (void **)&locref );
if (ret)
goto error_unlock;
break;
default:
ASSERT(0);
}
/* A new object has been created, increment the global counter */
dict->dict_count[type]++;
/* Unlock the dictionary */
CHECK_POSIX_DO( ret = pthread_rwlock_unlock(&dict->dict_lock), goto error_free );
/* Save the pointer to the new object */
if (ref)
*ref = new;
return 0;
error_param:
ret = EINVAL;
goto all_errors;
error_unlock:
CHECK_POSIX_DO( pthread_rwlock_unlock(&dict->dict_lock), /* continue */ );
if (ret == EEXIST) {
/* We have a duplicate key in locref. Check if the pointed object is the same or not */
switch (type) {
case DICT_VENDOR:
TRACE_DEBUG(FULL, "Vendor %s already in dictionary", new->data.vendor.vendor_name);
/* if we are here, it means the two vendors id are identical */
if (fd_os_cmp(locref->data.vendor.vendor_name, locref->datastr_len,
new->data.vendor.vendor_name, new->datastr_len)) {
TRACE_DEBUG(INFO, "Conflicting vendor name: %s", new->data.vendor.vendor_name);
break;
}
/* Otherwise (same name), we consider the function succeeded, since the (same) object is in the dictionary */
ret = 0;
break;
case DICT_APPLICATION:
TRACE_DEBUG(FULL, "Application %s already in dictionary", new->data.application.application_name);
/* got same id */
if (fd_os_cmp(locref->data.application.application_name, locref->datastr_len,
new->data.application.application_name, new->datastr_len)) {
TRACE_DEBUG(FULL, "Conflicting application name");
break;
}
ret = 0;
break;
case DICT_TYPE:
TRACE_DEBUG(FULL, "Type %s already in dictionary", new->data.type.type_name);
/* got same name */
if (locref->data.type.type_base != new->data.type.type_base) {
TRACE_DEBUG(FULL, "Conflicting base type");
break;
}
/* discard new definition only it a callback is provided and different from the previous one */
if ((new->data.type.type_interpret) && (locref->data.type.type_interpret != new->data.type.type_interpret)) {
TRACE_DEBUG(FULL, "Conflicting interpret cb");
break;
}
if ((new->data.type.type_encode) && (locref->data.type.type_encode != new->data.type.type_encode)) {
TRACE_DEBUG(FULL, "Conflicting encode cb");
break;
}
if ((new->data.type.type_dump) && (locref->data.type.type_dump != new->data.type.type_dump)) {
TRACE_DEBUG(FULL, "Conflicting dump cb");
break;
}
ret = 0;
break;
case DICT_ENUMVAL:
TRACE_DEBUG(FULL, "Enum %s already in dictionary", new->data.enumval.enum_name);
/* got either same name or same value. We check that both are true */
if (order_enum_by_name(locref, new)) {
TRACE_DEBUG(FULL, "Conflicting enum name");
break;
}
if (order_enum_by_val(locref, new)) {
TRACE_DEBUG(FULL, "Conflicting enum value");
break;
}
ret = 0;
break;
case DICT_AVP:
TRACE_DEBUG(FULL, "AVP %s already in dictionary", new->data.avp.avp_name);
/* got either same name or code */
if (order_avp_by_code(locref, new)) {
TRACE_DEBUG(FULL, "Conflicting AVP code");
break;
}
if (order_avp_by_name(locref, new)) {
TRACE_DEBUG(FULL, "Conflicting AVP name");
break;
}
if (locref->data.avp.avp_vendor != new->data.avp.avp_vendor) {
TRACE_DEBUG(FULL, "Conflicting AVP vendor");
break;
}
if (locref->data.avp.avp_flag_mask != new->data.avp.avp_flag_mask) {
TRACE_DEBUG(FULL, "Conflicting AVP flags mask");
break;
}
if ((locref->data.avp.avp_flag_val & locref->data.avp.avp_flag_mask) != (new->data.avp.avp_flag_val & new->data.avp.avp_flag_mask)) {
TRACE_DEBUG(FULL, "Conflicting AVP flags value");
break;
}
if (locref->data.avp.avp_basetype != new->data.avp.avp_basetype) {
TRACE_DEBUG(FULL, "Conflicting AVP base type");
break;
}
ret = 0;
break;
case DICT_COMMAND:
TRACE_DEBUG(FULL, "Command %s already in dictionary", new->data.cmd.cmd_name);
/* We got either same name, or same code + R flag */
if (order_cmd_by_name(locref, new)) {
TRACE_DEBUG(FULL, "Conflicting command name");
break;
}
if (locref->data.cmd.cmd_code != new->data.cmd.cmd_code) {
TRACE_DEBUG(FULL, "Conflicting command code");
break;
}
if (locref->data.cmd.cmd_flag_mask != new->data.cmd.cmd_flag_mask) {
TRACE_DEBUG(FULL, "Conflicting command flags mask %hhx:%hhx", locref->data.cmd.cmd_flag_mask, new->data.cmd.cmd_flag_mask);
break;
}
if ((locref->data.cmd.cmd_flag_val & locref->data.cmd.cmd_flag_mask) != (new->data.cmd.cmd_flag_val & new->data.cmd.cmd_flag_mask)) {
TRACE_DEBUG(FULL, "Conflicting command flags value");
break;
}
ret = 0;
break;
case DICT_RULE:
/* Both rules point to the same AVPs (code & vendor) */
if (locref->data.rule.rule_position != new->data.rule.rule_position) {
TRACE_DEBUG(FULL, "Conflicting rule position");
break;
}
if ( ((locref->data.rule.rule_position == RULE_FIXED_HEAD) ||
(locref->data.rule.rule_position == RULE_FIXED_TAIL))
&& (locref->data.rule.rule_order != new->data.rule.rule_order)) {
TRACE_DEBUG(FULL, "Conflicting rule order");
break;
}
if (locref->data.rule.rule_min != new->data.rule.rule_min) {
int r1 = locref->data.rule.rule_min;
int r2 = new->data.rule.rule_min;
int p = locref->data.rule.rule_position;
if ( ((r1 != -1) && (r2 != -1)) /* none of the definitions contains the "default" value */
|| ((p == RULE_OPTIONAL) && (r1 != 0) && (r2 != 0)) /* the other value is not 0 for an optional rule */
|| ((r1 != 1) && (r2 != 1)) /* the other value is not 1 for another rule */
) {
TRACE_DEBUG(FULL, "Conflicting rule min");
break;
}
}
if (locref->data.rule.rule_max != new->data.rule.rule_max) {
TRACE_DEBUG(FULL, "Conflicting rule max");
break;
}
ret = 0;
break;
}
if (!ret) {
TRACE_DEBUG(FULL, "An existing object with the same data was found, ignoring the error...");
}
if (ref)
*ref = locref;
}
all_errors:
if (ret != 0) {
char * buf = NULL;
size_t len = 0, offset=0;
if (type == DICT_ENUMVAL) {
CHECK_MALLOC( dump_enumval_data ( &buf, &len, &offset, data, parent->data.type.type_base ));
} else {
CHECK_MALLOC( dict_obj_info[CHECK_TYPE(type) ? type : 0].dump_data(&buf, &len, &offset, data) );
}
TRACE_DEBUG(INFO, "An error occurred while adding the following data in the dictionary: %s", buf);
if (ret == EEXIST) {
offset=0;
CHECK_MALLOC( dump_object(&buf, &len, &offset, locref, 0, 0, 0) );
TRACE_DEBUG(INFO, "Conflicting entry in the dictionary: %s", buf);
}
free(buf);
}
error_free:
free(new);
return ret;
}
int fd_dict_delete(struct dict_object * obj)
{
int i;
struct dictionary * dict;
int ret=0;
/* check params */
CHECK_PARAMS( verify_object(obj) && obj->dico);
dict = obj->dico;
/* Lock the dictionary for change */
CHECK_POSIX( pthread_rwlock_wrlock(&dict->dict_lock) );
/* check the object is not sentinel for another list */
for (i=0; ilist[i]))) {
/* There are children, this is not good */
ret = EINVAL;
TRACE_DEBUG (FULL, "Cannot delete object, list %d not empty:", i);
#if 0
dump_list(&obj->list[i], 0,0,0);
#endif
break;
}
}
/* ok, now destroy the object */
if (!ret)
destroy_object(obj);
/* Unlock */
CHECK_POSIX( pthread_rwlock_unlock(&dict->dict_lock) );
return ret;
}
int fd_dict_search ( struct dictionary * dict, enum dict_object_type type, int criteria, const void * what, struct dict_object **result, int retval )
{
int ret = 0;
TRACE_ENTRY("%p %d(%s) %d %p %p %d", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, criteria, what, result, retval);
/* Check param */
CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) );
/* Lock the dictionary for reading */
CHECK_POSIX( pthread_rwlock_rdlock(&dict->dict_lock) );
/* Now call the type-specific search function */
ret = dict_obj_info[type].search_fct (dict, criteria, what, result);
/* Unlock */
CHECK_POSIX( pthread_rwlock_unlock(&dict->dict_lock) );
/* Update the return value as needed */
if ((result != NULL) && (*result == NULL))
ret = retval;
return ret;
}
/* Function to retrieve list of objects in the dictionary. Use with care (read only).
All returned list must be accessed like this:
for (li = sentinel->next; li != sentinel; li=li->next) {
struct dict_object * obj = li->o;
...
}
The following criteria are allowed, with corresponding parent.
The parent is either struct dictionary * or struct dict_object *
VENDOR_BY_ID : (parent = dictionary) returns list of vendors ordered by ID
APPLICATION_BY_ID : (parent = dictionary) returns list of applications ordered by ID
** for these two lists, the Vendor with id 0 and applciation with id 0 are excluded.
You must resolve them separatly with dict_search.
TYPE_BY_NAME : (parent = dictionary) returns list of types ordered by name (osstring order)
ENUMVAL_BY_NAME : (parent = type object) return list of constants for this type ordered by name (osstring order)
ENUMVAL_BY_VALUE : (parent = type object) return list of constants for this type ordered by values
AVP_BY_NAME : (parent = vendor object) return list of AVP for this vendor ordered by name (osstring order)
AVP_BY_CODE : (parent = vendor object) return list of AVP for this vendor ordered by code
CMD_BY_NAME : (parent = dictionary) returns list of commands ordered by name (osstring order)
CMD_BY_CODE_R : (parent = dictionary) returns list of commands ordered by code
RULE_BY_AVP_AND_PARENT: (parent = command or grouped AVP object) return list of rules for this object ordered by AVP vendor/code
All other criteria are rejected.
*/
int fd_dict_getlistof(int criteria, void * parent, struct fd_list ** sentinel)
{
struct dictionary * dict = parent;
struct dict_object * obj_parent = parent;
TRACE_ENTRY("%i %p %p", criteria, parent, sentinel);
CHECK_PARAMS(sentinel && parent);
switch(criteria) {
case VENDOR_BY_ID: /* parent must be the dictionary */
CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
*sentinel = &dict->dict_vendors.list[0];
break;
case APPLICATION_BY_ID: /* parent must be the dictionary */
CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
*sentinel = &dict->dict_applications.list[0];
break;
case TYPE_BY_NAME: /* parent must be the dictionary */
CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
*sentinel = &dict->dict_types;
break;
case ENUMVAL_BY_NAME: /* parent must be a type object */
CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_TYPE));
*sentinel = &obj_parent->list[1];
break;
case ENUMVAL_BY_VALUE: /* parent must be a type object */
CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_TYPE));
*sentinel = &obj_parent->list[2];
break;
case AVP_BY_NAME: /* parent must be a VENDOR object */
CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_VENDOR));
*sentinel = &obj_parent->list[2];
break;
case AVP_BY_CODE: /* parent must be a VENDOR object */
CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_VENDOR));
*sentinel = &obj_parent->list[1];
break;
case CMD_BY_NAME: /* parent must be the dictionary */
CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
*sentinel = &dict->dict_cmd_name;
break;
case CMD_BY_CODE_R: /* parent must be the dictionary */
CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
*sentinel = &dict->dict_cmd_code;
break;
case RULE_BY_AVP_AND_PARENT: /* parent must be command or grouped AVP */
CHECK_PARAMS(verify_object(obj_parent));
CHECK_PARAMS( (obj_parent->type == DICT_COMMAND) ||
((obj_parent->type == DICT_AVP)
&& (obj_parent->data.avp.avp_basetype == AVP_TYPE_GROUPED)) );
*sentinel = &obj_parent->list[2];
break;
default:
CHECK_PARAMS(0);
}
return 0;
}
/*******************************************************************************************************/
/*******************************************************************************************************/
/* */
/* The init/fini functions */
/* */
/*******************************************************************************************************/
/*******************************************************************************************************/
/* Initialize the dictionary */
int fd_dict_init ( struct dictionary ** dict)
{
struct dictionary * new = NULL;
TRACE_ENTRY("%p", dict);
/* Sanity checks */
ASSERT( (sizeof(type_base_name) / sizeof(type_base_name[0])) == (AVP_TYPE_MAX + 1) );
ASSERT( (sizeof(dict_obj_info) / sizeof(dict_obj_info[0])) == (DICT_TYPE_MAX + 1) );
CHECK_PARAMS(dict);
/* Allocate the memory for the dictionary */
CHECK_MALLOC( new = malloc(sizeof(struct dictionary)) );
memset(new, 0, sizeof(struct dictionary));
new->dict_eyec = DICT_EYECATCHER;
/* Initialize the lock for the dictionary */
CHECK_POSIX( pthread_rwlock_init(&new->dict_lock, NULL) );
/* Initialize the sentinel for vendors and AVP lists */
init_object( &new->dict_vendors, DICT_VENDOR );
#define NO_VENDOR_NAME "(no vendor)"
new->dict_vendors.data.vendor.vendor_name = NO_VENDOR_NAME;
new->dict_vendors.datastr_len = CONSTSTRLEN(NO_VENDOR_NAME);
/* new->dict_vendors.list[0].o = NULL; *//* overwrite since element is also sentinel for this list. */
new->dict_vendors.dico = new;
/* Initialize the sentinel for applications */
init_object( &new->dict_applications, DICT_APPLICATION );
#define APPLICATION_0_NAME "Diameter Common Messages"
new->dict_applications.data.application.application_name = APPLICATION_0_NAME;
new->dict_applications.datastr_len = CONSTSTRLEN(APPLICATION_0_NAME);
/* new->dict_applications.list[0].o = NULL; *//* overwrite since since element is also sentinel for this list. */
new->dict_applications.dico = new;
/* Initialize the sentinel for types */
fd_list_init ( &new->dict_types, NULL );
/* Initialize the sentinels for commands */
fd_list_init ( &new->dict_cmd_name, NULL );
fd_list_init ( &new->dict_cmd_code, NULL );
/* Initialize the error command object */
init_object( &new->dict_cmd_error, DICT_COMMAND );
#define GENERIC_ERROR_NAME "(generic error format)"
new->dict_cmd_error.data.cmd.cmd_name = GENERIC_ERROR_NAME;
new->dict_cmd_error.datastr_len = CONSTSTRLEN(GENERIC_ERROR_NAME);
new->dict_cmd_error.data.cmd.cmd_flag_mask=CMD_FLAG_ERROR | CMD_FLAG_REQUEST | CMD_FLAG_RETRANSMIT;
new->dict_cmd_error.data.cmd.cmd_flag_val =CMD_FLAG_ERROR;
new->dict_cmd_error.dico = new;
*dict = new;
/* Done */
return 0;
}
/* Destroy a dictionary */
int fd_dict_fini ( struct dictionary ** dict)
{
int i;
TRACE_ENTRY("");
CHECK_PARAMS( dict && *dict && ((*dict)->dict_eyec == DICT_EYECATCHER) );
/* Acquire the write lock to make sure no other operation is ongoing */
CHECK_POSIX( pthread_rwlock_wrlock(&(*dict)->dict_lock) );
/* Empty all the lists, free the elements */
destroy_list ( &(*dict)->dict_cmd_error.list[2] );
destroy_list ( &(*dict)->dict_cmd_code );
destroy_list ( &(*dict)->dict_cmd_name );
destroy_list ( &(*dict)->dict_types );
for (i=0; i< NB_LISTS_PER_OBJ; i++) {
destroy_list ( &(*dict)->dict_applications.list[i] );
destroy_list ( &(*dict)->dict_vendors.list[i] );
}
/* Dictionary is empty, now destroy the lock */
CHECK_POSIX( pthread_rwlock_unlock(&(*dict)->dict_lock) );
CHECK_POSIX( pthread_rwlock_destroy(&(*dict)->dict_lock) );
free(*dict);
*dict = NULL;
return 0;
}
/*******************************************************************************************************/
/*******************************************************************************************************/
/* */
/* Other functions */
/* */
/*******************************************************************************************************/
/*******************************************************************************************************/
/* Iterate a callback on the rules for an object */
int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(void *, struct dict_rule_data *) )
{
int ret = 0;
struct fd_list * li;
TRACE_ENTRY("%p %p %p", parent, data, cb);
/* Check parameters */
CHECK_PARAMS( verify_object(parent) );
CHECK_PARAMS( (parent->type == DICT_COMMAND)
|| ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED)) );
TRACE_DEBUG (FULL, "Iterating on rules of %s: '%s'.",
_OBINFO(parent).name,
parent->type == DICT_COMMAND ?
parent->data.cmd.cmd_name
: parent->data.avp.avp_name);
/* Acquire the read lock */
CHECK_POSIX( pthread_rwlock_rdlock(&parent->dico->dict_lock) );
/* go through the list and call the cb on each rule data */
for (li = &(parent->list[2]); li->next != &(parent->list[2]); li = li->next) {
ret = (*cb)(data, &(_O(li->next->o)->data.rule));
if (ret != 0)
break;
}
/* Release the lock */
CHECK_POSIX( pthread_rwlock_unlock(&parent->dico->dict_lock) );
return ret;
}
/* Create the list of vendors. Returns a 0-terminated array, that must be freed after use. Returns NULL on error. */
uint32_t * fd_dict_get_vendorid_list(struct dictionary * dict)
{
uint32_t * ret = NULL;
int i = 0;
struct fd_list * li;
TRACE_ENTRY();
/* Acquire the read lock */
CHECK_POSIX_DO( pthread_rwlock_rdlock(&dict->dict_lock), return NULL );
/* Allocate an array to contain all the elements */
CHECK_MALLOC_DO( ret = calloc( dict->dict_count[DICT_VENDOR] + 1, sizeof(uint32_t) ), goto out );
/* Copy the vendors IDs */
for (li = dict->dict_vendors.list[0].next; li != &(dict->dict_vendors.list[0]); li = li->next) {
ret[i] = _O(li->o)->data.vendor.vendor_id;
i++;
ASSERT( i <= dict->dict_count[DICT_VENDOR] );
}
out:
/* Release the lock */
CHECK_POSIX_DO( pthread_rwlock_unlock(&dict->dict_lock), return NULL );
return ret;
}
/* Return the location of the cb list for an object, after checking its type */
int fd_dict_disp_cb(enum dict_object_type type, struct dict_object *obj, struct fd_list ** cb_list)
{
TRACE_ENTRY("%d %p %p", type, obj, cb_list);
CHECK_PARAMS( verify_object(obj) );
CHECK_PARAMS( _OBINFO(obj).type == type );
CHECK_PARAMS( cb_list );
*cb_list = &obj->disp_cbs;
return 0;
}
int fd_dict_get_error_cmd(struct dictionary * dict, struct dict_object **obj)
{
TRACE_ENTRY("%p %p", dict, obj);
CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && obj );
*obj = &dict->dict_cmd_error;
return 0;
}
freeDiameter/libfdproto/lists.c 0000644 0001750 0001750 00000012506 13324704440 016135 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
#include "fdproto-internal.h"
/* Initialize a list element */
void fd_list_init ( struct fd_list * list, void * obj )
{
memset(list, 0, sizeof(struct fd_list));
list->next = list;
list->prev = list;
list->head = list;
list->o = obj;
}
#define CHECK_SINGLE( li ) { \
ASSERT( ((struct fd_list *)(li))->next == (li) ); \
ASSERT( ((struct fd_list *)(li))->prev == (li) ); \
ASSERT( ((struct fd_list *)(li))->head == (li) ); \
}
/* insert after a reference, checks done */
static void list_insert_after( struct fd_list * ref, struct fd_list * item )
{
item->prev = ref;
item->next = ref->next;
item->head = ref->head;
ref->next->prev = item;
ref->next = item;
}
/* insert after a reference */
void fd_list_insert_after ( struct fd_list * ref, struct fd_list * item )
{
ASSERT(item != NULL);
ASSERT(ref != NULL);
CHECK_SINGLE ( item );
ASSERT(ref->head != item);
list_insert_after(ref, item);
}
/* Move all elements of list senti at the end of list ref */
void fd_list_move_end(struct fd_list * ref, struct fd_list * senti)
{
struct fd_list * li;
ASSERT(ref->head == ref);
ASSERT(senti->head == senti);
if (senti->next == senti)
return;
for (li = senti->next; li != senti; li = li->next)
li->head = ref;
senti->next->prev = ref->prev;
ref->prev->next = senti->next;
senti->prev->next = ref;
ref->prev = senti->prev;
senti->prev = senti;
senti->next = senti;
}
/* insert before a reference, checks done */
static void list_insert_before ( struct fd_list * ref, struct fd_list * item )
{
item->prev = ref->prev;
item->next = ref;
item->head = ref->head;
ref->prev->next = item;
ref->prev = item;
}
/* insert before a reference */
void fd_list_insert_before ( struct fd_list * ref, struct fd_list * item )
{
ASSERT(item != NULL);
ASSERT(ref != NULL);
CHECK_SINGLE ( item );
ASSERT(ref->head != item);
list_insert_before(ref, item);
}
/* Insert an item in an ordered list -- ordering function provided. If duplicate object found, it is returned in ref_duplicate */
int fd_list_insert_ordered( struct fd_list * head, struct fd_list * item, int (*cmp_fct)(void *, void *), void ** ref_duplicate)
{
struct fd_list * ptr = head;
int cmp;
/* Some debug sanity checks */
ASSERT(head != NULL);
ASSERT(item != NULL);
ASSERT(cmp_fct != NULL);
ASSERT(head->head == head);
CHECK_SINGLE ( item );
/* loop in the list */
while (ptr->next != head)
{
/* Compare the object to insert with the next object in list */
cmp = cmp_fct( item->o, ptr->next->o );
if (!cmp) {
/* An element with the same key already exists */
if (ref_duplicate != NULL)
*ref_duplicate = ptr->next->o;
return EEXIST;
}
if (cmp < 0)
break; /* We must insert the element here */
ptr = ptr->next;
}
/* Now insert the element between ptr and ptr->next */
list_insert_after( ptr, item );
/* Ok */
return 0;
}
/* Unlink an object */
void fd_list_unlink ( struct fd_list * item )
{
ASSERT(item != NULL);
if (item->head == item)
return;
/* unlink */
item->next->prev = item->prev;
item->prev->next = item->next;
/* sanitize */
item->next = item;
item->prev = item;
item->head = item;
}
freeDiameter/libfdproto/fifo.c 0000644 0001750 0001750 00000051753 13324704440 015731 0 ustar ruben ruben /*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis *
* *
* Copyright (c) 2013, 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. *
*********************************************************************************************************/
/* FIFO queues module.
*
* The threads that call these functions must be in the cancellation state PTHREAD_CANCEL_ENABLE and type PTHREAD_CANCEL_DEFERRED.
* This is the default state and type on thread creation.
*
* In order to destroy properly a queue, the application must:
* -> shutdown any process that can add into the queue first.
* -> pthread_cancel any thread that could be waiting on the queue.
* -> consume any element that is in the queue, using fd_qu_tryget_int.
* -> then destroy the queue using fd_mq_del.
*/
#include "fdproto-internal.h"
/* Definition of a FIFO queue object */
struct fifo {
int eyec; /* An eye catcher, also used to check a queue is valid. FIFO_EYEC */
pthread_mutex_t mtx; /* Mutex protecting this queue */
pthread_cond_t cond_pull; /* condition variable for pulling threads */
pthread_cond_t cond_push; /* condition variable for pushing threads */
struct fd_list list; /* sentinel for the list of elements */
int count; /* number of objects in the list */
int thrs; /* number of threads waiting for a new element (when count is 0) */
int max; /* maximum number of items to accept if not 0 */
int thrs_push; /* number of threads waitnig to push an item */
uint16_t high; /* High level threshold (see libfreeDiameter.h for details) */
uint16_t low; /* Low level threshhold */
void *data; /* Opaque pointer for threshold callbacks */
void (*h_cb)(struct fifo *, void **); /* The callbacks */
void (*l_cb)(struct fifo *, void **);
int highest;/* The highest count value for which h_cb has been called */
int highest_ever; /* The max count value this queue has reached (for tweaking) */
long long total_items; /* Cumulated number of items that went through this fifo (excluding current count), always increasing. */
struct timespec total_time; /* Cumulated time all items spent in this queue, including blocking time (always growing, use deltas for monitoring) */
struct timespec blocking_time; /* Cumulated time threads trying to post new items were blocked (queue full). */
struct timespec last_time; /* For the last element retrieved from the queue, how long it take between posting (including blocking) and poping */
};
struct fifo_item {
struct fd_list item;
struct timespec posted_on;
};
/* The eye catcher value */
#define FIFO_EYEC 0xe7ec1130
/* Macro to check a pointer */
#define CHECK_FIFO( _queue ) (( (_queue) != NULL) && ( (_queue)->eyec == FIFO_EYEC) )
/* Create a new queue, with max number of items -- use 0 for no max */
int fd_fifo_new ( struct fifo ** queue, int max )
{
struct fifo * new;
TRACE_ENTRY( "%p", queue );
CHECK_PARAMS( queue );
/* Create a new object */
CHECK_MALLOC( new = malloc (sizeof (struct fifo) ) );
/* Initialize the content */
memset(new, 0, sizeof(struct fifo));
new->eyec = FIFO_EYEC;
CHECK_POSIX( pthread_mutex_init(&new->mtx, NULL) );
CHECK_POSIX( pthread_cond_init(&new->cond_pull, NULL) );
CHECK_POSIX( pthread_cond_init(&new->cond_push, NULL) );
new->max = max;
fd_list_init(&new->list, NULL);
/* We're done */
*queue = new;
return 0;
}
/* Dump the content of a queue */
DECLARE_FD_DUMP_PROTOTYPE(fd_fifo_dump, char * name, struct fifo * queue, fd_fifo_dump_item_cb dump_item)
{
FD_DUMP_HANDLE_OFFSET();
if (name) {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'(@%p): ", name, queue), return NULL);
} else {
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{fifo}(@%p): ", queue), return NULL);
}
if (!CHECK_FIFO( queue )) {
return fd_dump_extend(FD_DUMP_STD_PARAMS, "INVALID/NULL");
}
CHECK_POSIX_DO( pthread_mutex_lock( &queue->mtx ), /* continue */ );
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "items:%d,%d,%d threads:%d,%d stats:%lld/%ld.%06ld,%ld.%06ld,%ld.%06ld thresholds:%d,%d,%d,%p,%p,%p",
queue->count, queue->highest_ever, queue->max,
queue->thrs, queue->thrs_push,
queue->total_items,(long)queue->total_time.tv_sec,(long)(queue->total_time.tv_nsec/1000),(long)queue->blocking_time.tv_sec,(long)(queue->blocking_time.tv_nsec/1000),(long)queue->last_time.tv_sec,(long)(queue->last_time.tv_nsec/1000),
queue->high, queue->low, queue->highest, queue->h_cb, queue->l_cb, queue->data),
goto error);
if (dump_item) {
struct fd_list * li;
int i = 0;
for (li = queue->list.next; li != &queue->list; li = li->next) {
struct fifo_item * fi = (struct fifo_item *)li;
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n [#%i](@%p)@%ld.%06ld: ",
i++, fi->item.o, (long)fi->posted_on.tv_sec,(long)(fi->posted_on.tv_nsec/1000)),
goto error);
CHECK_MALLOC_DO( (*dump_item)(FD_DUMP_STD_PARAMS, fi->item.o), goto error);
}
}
CHECK_POSIX_DO( pthread_mutex_unlock( &queue->mtx ), /* continue */ );
return *buf;
error:
CHECK_POSIX_DO( pthread_mutex_unlock( &queue->mtx ), /* continue */ );
return NULL;
}
/* Delete a queue. It must be empty. */
int fd_fifo_del ( struct fifo ** queue )
{
struct fifo * q;
int loops = 0;
TRACE_ENTRY( "%p", queue );
CHECK_PARAMS( queue && CHECK_FIFO( *queue ) );
q = *queue;
CHECK_POSIX( pthread_mutex_lock( &q->mtx ) );
if ((q->count != 0) || (q->data != NULL)) {
TRACE_DEBUG(INFO, "The queue cannot be destroyed (%d, %p)", q->count, q->data);
CHECK_POSIX_DO( pthread_mutex_unlock( &q->mtx ), /* no fallback */ );
return EINVAL;
}
/* Ok, now invalidate the queue */
q->eyec = 0xdead;
/* Have all waiting threads return an error */
while (q->thrs) {
CHECK_POSIX( pthread_mutex_unlock( &q->mtx ));
CHECK_POSIX( pthread_cond_signal(&q->cond_pull) );
usleep(1000);
CHECK_POSIX( pthread_mutex_lock( &q->mtx ) );
ASSERT( ++loops < 20 ); /* detect infinite loops */
}
/* sanity check */
ASSERT(FD_IS_LIST_EMPTY(&q->list));
/* And destroy it */
CHECK_POSIX( pthread_mutex_unlock( &q->mtx ) );
CHECK_POSIX_DO( pthread_cond_destroy( &q->cond_pull ), );
CHECK_POSIX_DO( pthread_cond_destroy( &q->cond_push ), );
CHECK_POSIX_DO( pthread_mutex_destroy( &q->mtx ), );
free(q);
*queue = NULL;
return 0;
}
/* Move the content of old into new, and update loc_update atomically. We leave the old queue empty but valid */
int fd_fifo_move ( struct fifo * old, struct fifo * new, struct fifo ** loc_update )
{
int loops = 0;
TRACE_ENTRY("%p %p %p", old, new, loc_update);
CHECK_PARAMS( CHECK_FIFO( old ) && CHECK_FIFO( new ));
CHECK_PARAMS( ! old->data );
if (new->high) {
TODO("Implement support for thresholds in fd_fifo_move...");
}
/* Update loc_update */
if (loc_update)
*loc_update = new;
/* Lock the queues */
CHECK_POSIX( pthread_mutex_lock( &old->mtx ) );
CHECK_PARAMS_DO( (! old->thrs_push), {
pthread_mutex_unlock( &old->mtx );
return EINVAL;
} );
CHECK_POSIX( pthread_mutex_lock( &new->mtx ) );
/* Any waiting thread on the old queue returns an error */
old->eyec = 0xdead;
while (old->thrs) {
CHECK_POSIX( pthread_mutex_unlock( &old->mtx ));
CHECK_POSIX( pthread_cond_signal( &old->cond_pull ) );
usleep(1000);
CHECK_POSIX( pthread_mutex_lock( &old->mtx ) );
ASSERT( loops < 20 ); /* detect infinite loops */
}
/* Move all data from old to new */
fd_list_move_end( &new->list, &old->list );
if (old->count && (!new->count)) {
CHECK_POSIX( pthread_cond_signal(&new->cond_pull) );
}
new->count += old->count;
/* Reset old */
old->count = 0;
old->eyec = FIFO_EYEC;
/* Merge the stats in the new queue */
new->total_items += old->total_items;
old->total_items = 0;
new->total_time.tv_nsec += old->total_time.tv_nsec;
new->total_time.tv_sec += old->total_time.tv_sec + (new->total_time.tv_nsec / 1000000000);
new->total_time.tv_nsec %= 1000000000;
old->total_time.tv_nsec = 0;
old->total_time.tv_sec = 0;
new->blocking_time.tv_nsec += old->blocking_time.tv_nsec;
new->blocking_time.tv_sec += old->blocking_time.tv_sec + (new->blocking_time.tv_nsec / 1000000000);
new->blocking_time.tv_nsec %= 1000000000;
old->blocking_time.tv_nsec = 0;
old->blocking_time.tv_sec = 0;
/* Unlock, we're done */
CHECK_POSIX( pthread_mutex_unlock( &new->mtx ) );
CHECK_POSIX( pthread_mutex_unlock( &old->mtx ) );
return 0;
}
/* Get the information on the queue */
int fd_fifo_getstats( struct fifo * queue, int * current_count, int * limit_count, int * highest_count, long long * total_count,
struct timespec * total, struct timespec * blocking, struct timespec * last)
{
TRACE_ENTRY( "%p %p %p %p %p %p %p %p", queue, current_count, limit_count, highest_count, total_count, total, blocking, last);
/* Check the parameters */
CHECK_PARAMS( CHECK_FIFO( queue ) );
/* lock the queue */
CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) );
if (current_count)
*current_count = queue->count;
if (limit_count)
*limit_count = queue->max;
if (highest_count)
*highest_count = queue->highest_ever;
if (total_count)
*total_count = queue->total_items;
if (total)
memcpy(total, &queue->total_time, sizeof(struct timespec));
if (blocking)
memcpy(blocking, &queue->blocking_time, sizeof(struct timespec));
if (last)
memcpy(last, &queue->last_time, sizeof(struct timespec));
/* Unlock */
CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) );
/* Done */
return 0;
}
/* alternate version with no error checking */
int fd_fifo_length ( struct fifo * queue )
{
if ( !CHECK_FIFO( queue ) )
return 0;
return queue->count; /* Let's hope it's read atomically, since we are not locking... */
}
/* Set the thresholds of the queue */
int fd_fifo_setthrhd ( struct fifo * queue, void * data, uint16_t high, void (*h_cb)(struct fifo *, void **), uint16_t low, void (*l_cb)(struct fifo *, void **) )
{
TRACE_ENTRY( "%p %p %hu %p %hu %p", queue, data, high, h_cb, low, l_cb );
/* Check the parameters */
CHECK_PARAMS( CHECK_FIFO( queue ) && (high > low) && (queue->data == NULL) );
/* lock the queue */
CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) );
/* Save the values */
queue->high = high;
queue->low = low;
queue->data = data;
queue->h_cb = h_cb;
queue->l_cb = l_cb;
/* Unlock */
CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) );
/* Done */
return 0;
}
/* This handler is called when a thread is blocked on a queue, and cancelled */
static void fifo_cleanup_push(void * queue)
{
struct fifo * q = (struct fifo *)queue;
TRACE_ENTRY( "%p", queue );
/* The thread has been cancelled, therefore it does not wait on the queue anymore */
q->thrs_push--;
/* Now unlock the queue, and we're done */
CHECK_POSIX_DO( pthread_mutex_unlock( &q->mtx ), /* nothing */ );
/* End of cleanup handler */
return;
}
/* Post a new item in the queue */
int fd_fifo_post_internal ( struct fifo * queue, void ** item, int skip_max )
{
struct fifo_item * new;
int call_cb = 0;
struct timespec posted_on, queued_on;
/* Get the timing of this call */
CHECK_SYS( clock_gettime(CLOCK_REALTIME, &posted_on) );
/* lock the queue */
CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) );
if ((!skip_max) && (queue->max)) {
while (queue->count >= queue->max) {
int ret = 0;
/* We have to wait for an item to be pulled */
queue->thrs_push++ ;
pthread_cleanup_push( fifo_cleanup_push, queue);
ret = pthread_cond_wait( &queue->cond_push, &queue->mtx );
pthread_cleanup_pop(0);
queue->thrs_push-- ;
ASSERT( ret == 0 );
}
}
/* Create a new list item */
CHECK_MALLOC_DO( new = malloc (sizeof (struct fifo_item)) , {
pthread_mutex_unlock( &queue->mtx );
return ENOMEM;
} );
fd_list_init(&new->item, *item);
*item = NULL;
/* Add the new item at the end */
fd_list_insert_before( &queue->list, &new->item);
queue->count++;
if (queue->highest_ever < queue->count)
queue->highest_ever = queue->count;
if (queue->high && ((queue->count % queue->high) == 0)) {
call_cb = 1;
queue->highest = queue->count;
}
/* store timing */
memcpy(&new->posted_on, &posted_on, sizeof(struct timespec));
/* update queue timing info "blocking time" */
{
long long blocked_ns;
CHECK_SYS( clock_gettime(CLOCK_REALTIME, &queued_on) );
blocked_ns = (queued_on.tv_sec - posted_on.tv_sec) * 1000000000;
blocked_ns += (queued_on.tv_nsec - posted_on.tv_nsec);
blocked_ns += queue->blocking_time.tv_nsec;
queue->blocking_time.tv_sec += blocked_ns / 1000000000;
queue->blocking_time.tv_nsec = blocked_ns % 1000000000;
}
/* Signal if threads are asleep */
if (queue->thrs > 0) {
CHECK_POSIX( pthread_cond_signal(&queue->cond_pull) );
}
if (queue->thrs_push > 0) {
/* cascade */
CHECK_POSIX( pthread_cond_signal(&queue->cond_push) );
}
/* Unlock */
CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) );
/* Call high-watermark cb as needed */
if (call_cb && queue->h_cb)
(*queue->h_cb)(queue, &queue->data);
/* Done */
return 0;
}
/* Post a new item in the queue */
int fd_fifo_post_int ( struct fifo * queue, void ** item )
{
TRACE_ENTRY( "%p %p", queue, item );
/* Check the parameters */
CHECK_PARAMS( CHECK_FIFO( queue ) && item && *item );
return fd_fifo_post_internal ( queue,item, 0 );
}
/* Post a new item in the queue, not blocking */
int fd_fifo_post_noblock ( struct fifo * queue, void ** item )
{
TRACE_ENTRY( "%p %p", queue, item );
/* Check the parameters */
CHECK_PARAMS( CHECK_FIFO( queue ) && item && *item );
return fd_fifo_post_internal ( queue,item, 1 );
}
/* Pop the first item from the queue */
static void * mq_pop(struct fifo * queue)
{
void * ret = NULL;
struct fifo_item * fi;
struct timespec now;
ASSERT( ! FD_IS_LIST_EMPTY(&queue->list) );
fi = (struct fifo_item *)(queue->list.next);
ret = fi->item.o;
fd_list_unlink(&fi->item);
queue->count--;
queue->total_items++;
/* Update the timings */
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), goto skip_timing );
{
long long elapsed = (now.tv_sec - fi->posted_on.tv_sec) * 1000000000;
elapsed += now.tv_nsec - fi->posted_on.tv_nsec;
queue->last_time.tv_sec = elapsed / 1000000000;
queue->last_time.tv_nsec = elapsed % 1000000000;
elapsed += queue->total_time.tv_nsec;
queue->total_time.tv_sec += elapsed / 1000000000;
queue->total_time.tv_nsec = elapsed % 1000000000;
}
skip_timing:
free(fi);
if (queue->thrs_push) {
CHECK_POSIX_DO( pthread_cond_signal( &queue->cond_push ), );
}
return ret;
}
/* Check if the low watermark callback must be called. */
static __inline__ int test_l_cb(struct fifo * queue)
{
if ((queue->high == 0) || (queue->low == 0) || (queue->l_cb == 0))
return 0;
if (((queue->count % queue->high) == queue->low) && (queue->highest > queue->count)) {
queue->highest -= queue->high;
return 1;
}
return 0;
}
/* Try poping an item */
int fd_fifo_tryget_int ( struct fifo * queue, void ** item )
{
int wouldblock = 0;
int call_cb = 0;
TRACE_ENTRY( "%p %p", queue, item );
/* Check the parameters */
CHECK_PARAMS( CHECK_FIFO( queue ) && item );
/* lock the queue */
CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) );
/* Check queue status */
if (queue->count > 0) {
got_item:
/* There are elements in the queue, so pick the first one */
*item = mq_pop(queue);
call_cb = test_l_cb(queue);
} else {
if (queue->thrs_push > 0) {
/* A thread is trying to push something, let's give it a chance */
CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) );
CHECK_POSIX( pthread_cond_signal( &queue->cond_push ) );
usleep(1000);
CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) );
if (queue->count > 0)
goto got_item;
}
wouldblock = 1;
*item = NULL;
}
/* Unlock */
CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) );
/* Call low watermark callback as needed */
if (call_cb)
(*queue->l_cb)(queue, &queue->data);
/* Done */
return wouldblock ? EWOULDBLOCK : 0;
}
/* This handler is called when a thread is blocked on a queue, and cancelled */
static void fifo_cleanup(void * queue)
{
struct fifo * q = (struct fifo *)queue;
TRACE_ENTRY( "%p", queue );
/* The thread has been cancelled, therefore it does not wait on the queue anymore */
q->thrs--;
/* Now unlock the queue, and we're done */
CHECK_POSIX_DO( pthread_mutex_unlock( &q->mtx ), /* nothing */ );
/* End of cleanup handler */
return;
}
/* The internal function for fd_fifo_timedget and fd_fifo_get */
static int fifo_tget ( struct fifo * queue, void ** item, int istimed, const struct timespec *abstime)
{
int call_cb = 0;
int ret = 0;
/* Check the parameters */
CHECK_PARAMS( CHECK_FIFO( queue ) && item && (abstime || !istimed) );
/* Initialize the return value */
*item = NULL;
/* lock the queue */
CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) );
awaken:
/* Check queue status */
if (!CHECK_FIFO( queue )) {
/* The queue is being destroyed */
CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) );
TRACE_DEBUG(FULL, "The queue is being destroyed -> EPIPE");
return EPIPE;
}
if (queue->count > 0) {
/* There are items in the queue, so pick the first one */
*item = mq_pop(queue);
call_cb = test_l_cb(queue);
} else {
/* We have to wait for a new item */
queue->thrs++ ;
pthread_cleanup_push( fifo_cleanup, queue);
if (istimed) {
ret = pthread_cond_timedwait( &queue->cond_pull, &queue->mtx, abstime );
} else {
ret = pthread_cond_wait( &queue->cond_pull, &queue->mtx );
}
pthread_cleanup_pop(0);
queue->thrs-- ;
if (ret == 0)
goto awaken; /* test for spurious wake-ups */
/* otherwise (ETIMEDOUT / other error) just continue */
}
/* Unlock */
CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) );
/* Call low watermark callback as needed */
if (call_cb)
(*queue->l_cb)(queue, &queue->data);
/* Done */
return ret;
}
/* Get the next available item, block until there is one */
int fd_fifo_get_int ( struct fifo * queue, void ** item )
{
TRACE_ENTRY( "%p %p", queue, item );
return fifo_tget(queue, item, 0, NULL);
}
/* Get the next available item, block until there is one, or the timeout expires */
int fd_fifo_timedget_int ( struct fifo * queue, void ** item, const struct timespec *abstime )
{
TRACE_ENTRY( "%p %p %p", queue, item, abstime );
return fifo_tget(queue, item, 1, abstime);
}
/* Test if data is available in the queue, without pulling it */
int fd_fifo_select ( struct fifo * queue, const struct timespec *abstime )
{
int ret = 0;
TRACE_ENTRY( "%p %p", queue, abstime );
CHECK_PARAMS_DO( CHECK_FIFO( queue ), return -EINVAL );
/* lock the queue */
CHECK_POSIX_DO( pthread_mutex_lock( &queue->mtx ), return -__ret__ );
awaken:
ret = (queue->count > 0 ) ? queue->count : 0;
if ((ret == 0) && (abstime != NULL)) {
/* We have to wait for a new item */
queue->thrs++ ;
pthread_cleanup_push( fifo_cleanup, queue);
ret = pthread_cond_timedwait( &queue->cond_pull, &queue->mtx, abstime );
pthread_cleanup_pop(0);
queue->thrs-- ;
if (ret == 0)
goto awaken; /* test for spurious wake-ups */
if (ret == ETIMEDOUT)
ret = 0;
else
ret = -ret;
}
/* Unlock */
CHECK_POSIX_DO( pthread_mutex_unlock( &queue->mtx ), return -__ret__ );
return ret;
}
freeDiameter/INSTALL.OSX 0000644 0001750 0001750 00000004714 13324704440 014172 0 ustar ruben ruben See INSTALL file for general instructions on building freeDiameter.
------------------
QUICK INSTRUCTIONS
------------------
Starting from version 1.1.5, freeDiameter is available through Homebrew:
1) Install Homebrew
a) Install Command Line Tools for Xcode or Xcode:
- Command Line Tools (your OS X Version) for Xcode:
https://developer.apple.com/downloads (Free Apple Developer ID Registration Required)
- or Xcode:
https://itunes.apple.com/us/app/xcode/id497799835 (Free App Store Account Required)
b) Install Homebrew
$ ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)"
2) Install freeDiameter:
$ brew install freediameter
After installation is done "Caveats" section will provide information
how to start freeDiameterd through launchd at system startup.
To view this information again:
$ brew info freediameter
To learn more about freeDiameter configuration options, read:
http://www.freediameter.net/trac/wiki/Configuration
For more information on available extension and how to configure them, read:
http://www.freediameter.net/trac/wiki/Extensions
------------
FROM SOURCES
------------
1) Install Homebrew:
See QUICK INSTRUCTIONS Step #1 above.
2) Install dependencies:
$ brew install mercurial cmake gnutls libgcrypt postgres mysql libidn
Notes:
* GNU IDN (libidn) is optional, see below
* When libusrsctp package is available, you can use it as well.
However, it requires some rework on the source code as libusrsctp is not fully
compliant with the other SCTP stack.
3) Retrieve source code:
$ hg clone http://www.freediameter.net/hg/freeDiameter
4) Enter source folder:
$ cd freeDiameter
5) Create build folder:
$ mkdir freeDiameter-build
6) Enter build folder:
$ cd freeDiameter-build
7) Configure:
$ cmake .. -DDISABLE_SCTP:BOOL=ON
or for ncurses-based configuration:
$ ccmake .. -DDISABLE_SCTP:BOOL=ON
Notes:
* GNU IDN (Support for International Domain Names) can be disabled with:
-DDIAMID_IDNA_IGNORE=ON or -DDIAMID_IDNA_REJECT=ON
* For more information on available configuration options, read:
http://www.freediameter.net/trac/wiki/Installation
8) Compile:
$ make
9) Install (might require to be root):
$ make install
------------------------------------------------
These instructions have been tested on OS X:
- 10.6.8 (Snow Leopard)
- 10.7.5 (Lion)
- 10.8.2 (Mountain Lion)
freeDiameter/contrib/ 0000755 0001750 0001750 00000000000 13324704440 014123 5 ustar ruben ruben freeDiameter/contrib/app_acct_tools/ 0000755 0001750 0001750 00000000000 13324704440 017115 5 ustar ruben ruben freeDiameter/contrib/app_acct_tools/display_stats.php 0000644 0001750 0001750 00000020534 13324704440 022515 0 ustar ruben ruben
Accounting Data
Statistical accounting data
Note well: this page displays only data about terminated sessions.
= '".pg_escape_string($START_TIME)."'";
}
if ($END_TIME) {
if ($sql_cond)
$sql_cond .= " AND ";
$sql_cond .= "sess_start =< '".pg_escape_string($END_TIME)."'";
}
/* Function to format download size (from php.net) */
function human_readable( $size )
{
$count = 0;
$format = array("B","KB","MB","GB","TB","PB","EB","ZB","YB");
while(($size/1024)>1 && $count<8)
{
$size=$size/1024;
$count++;
}
if( $size >= 100 ) $decimals = 0;
elseif ($size >= 10 ) $decimals = 1;
else $decimals = 2;
$return = number_format($size,$decimals,'.',' ')." ".$format[$count];
return $return;
}
?>
No data is available in the selected period.
\n";
die("\n");
}
$data = pg_fetch_array($result, null, PGSQL_ASSOC);
?>
Total accounted data |
First session started: | ".$data["first_sess"]." | \n";
echo " Last session started: | ".$data["last_sess"]." |
\n";
echo " Total data downloaded: | ".human_readable($data["total_down"])." |
\n";
echo " Total data uploaded: | ".human_readable($data["total_up"])." |
\n";
echo " Number of users who connected at least once: | ".$data["nb_users"]." |
\n";
echo " Number of access points involved: | ".$data["nb_ap"]." |
\n";
echo " \n";
echo "
\n";
pg_free_result($result);
?>
Top-5 downloading users |
\n";
echo " ".htmlentities(pg_unescape_bytea($data["user_name"]))." | \n";
echo " ".human_readable($data["total_down"])." | \n";
echo " \n";
}
pg_free_result($result);
?>
Top-5 uploading users |
\n";
echo " ".htmlentities(pg_unescape_bytea($data["user_name"]))." | \n";
echo " ".human_readable($data["total_up"])." | \n";
echo " \n";
}
pg_free_result($result);
?>
Top-8 access devices (by number of users) |
\n";
echo " ".htmlentities(pg_unescape_bytea($data["nas_info"]))." | \n";
echo " ".$data["unc"]." | \n";
echo " \n";
}
pg_free_result($result);
?>
Top-8 access devices (by traffic: up+down) |
\n";
echo " ".htmlentities(pg_unescape_bytea($data["nas_info"]))." | \n";
echo " ".human_readable($data["traffic"])." | \n";
echo " \n";
}
pg_free_result($result);
?>
Data generated by scripts from the freeDiameter project.