Initial commit
Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..eaaaa94
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,117 @@
+# Test directory
+PROJECT("freeDiameter tests" C)
+
+# give the possibility to configure the timeout duration for the tests
+OPTION(TEST_TIMEOUT "Timeout for the tests, in seconds (default: 120)?")
+IF(TEST_TIMEOUT)
+ ADD_DEFINITIONS(-DTEST_TIMEOUT=${TEST_TIMEOUT})
+ENDIF(TEST_TIMEOUT)
+
+# default command line for the tests
+OPTION(OPT_TEST_ARGUMENTS "Specify the default arguments for the tests" OFF)
+IF(OPT_TEST_ARGUMENTS)
+ SET(TEST_ARGUMENTS ${OPT_TEST_ARGUMENTS})
+ELSE(OPT_TEST_ARGUMENTS)
+ SET(TEST_ARGUMENTS "")
+ENDIF(OPT_TEST_ARGUMENTS)
+
+
+#############################
+# List the test cases
+SET(TEST_LIST
+ testsctp
+ testostr
+ testfifo
+ testpeers
+ testdict
+ testmesg
+ testmesg_stress
+ testsess
+ testdisp
+ testcnx
+ testloadext
+)
+
+#############################
+# Some parameters for the tests
+
+ADD_DEFINITIONS(-DTEST_DEBUG)
+ADD_DEFINITIONS(-DBUILD_DIR="${CMAKE_BINARY_DIR}")
+
+INCLUDE_DIRECTORIES( "../libfdproto" )
+INCLUDE_DIRECTORIES( "../libfdcore" )
+INCLUDE_DIRECTORIES(${LFDCORE_INCLUDES})
+
+
+SET(testcnx_ADDITIONAL_LIB ${CLOCK_GETTIME_LIBS})
+SET(testfifo_ADDITIONAL_LIB ${CLOCK_GETTIME_LIBS})
+SET(testsess_ADDITIONAL_LIB ${CLOCK_GETTIME_LIBS})
+SET(testloadext_ADDITIONAL_LIB ${CMAKE_DL_LIBS})
+SET(testmesg_stress_ADDITIONAL_LIB ${CLOCK_GETTIME_LIBS} ${CMAKE_DL_LIBS})
+
+##############################
+# App_acct test
+
+IF(BUILD_APP_ACCT OR ALL_EXTENSIONS)
+ OPTION(TEST_APP_ACCT "Test app_acct extension? (Requires a configured database, see testappacct.c for details)" OFF)
+ IF(TEST_APP_ACCT)
+
+ OPTION(TEST_APP_ACCT_CONNINFO "The connection string to the database")
+ IF(TEST_APP_ACCT_CONNINFO)
+ ADD_DEFINITIONS(-DTEST_CONNINFO="${TEST_APP_ACCT_CONNINFO}")
+ ENDIF(TEST_APP_ACCT_CONNINFO)
+
+ SET(TEST_LIST ${TEST_LIST} testappacct)
+
+ # Extension dependencies
+ FIND_PACKAGE(PostgreSQL REQUIRED)
+ INCLUDE_DIRECTORIES(${POSTGRESQL_INCLUDE_DIR})
+ SET(testappacct_ADDITIONAL_LIB ${POSTGRESQL_LIBRARIES})
+
+ # List of source files, copied from the extension CMakeLists.
+ BISON_FILE(../extensions/app_acct/acct_conf.y)
+ FLEX_FILE(../extensions/app_acct/acct_conf.l)
+ #SET_SOURCE_FILES_PROPERTIES(lex.acct_conf.c acct_conf.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}")
+
+ SET( APP_ACCT_SRC
+ app_acct.h
+ app_acct.c
+ acct_db.c
+ acct_records.c
+ )
+ SET( APP_ACCT_SRC_GEN
+ lex.acct_conf.c
+ acct_conf.tab.c
+ acct_conf.tab.h
+ )
+
+ # The extension headers
+ INCLUDE_DIRECTORIES( "../extensions/app_acct" )
+
+ SET(testappacct_ADDITIONAL "")
+
+ FOREACH( SRC_FILE ${APP_ACCT_SRC})
+ SET(testappacct_ADDITIONAL ${testappacct_ADDITIONAL} "../extensions/app_acct/${SRC_FILE}")
+ ENDFOREACH(SRC_FILE)
+
+ FOREACH( SRC_FILE ${APP_ACCT_SRC_GEN})
+ SET(testappacct_ADDITIONAL ${testappacct_ADDITIONAL} "${CMAKE_CURRENT_BINARY_DIR}/../extensions/app_acct/${SRC_FILE}")
+ ENDFOREACH(SRC_FILE)
+
+ ENDIF(TEST_APP_ACCT)
+ENDIF(BUILD_APP_ACCT OR ALL_EXTENSIONS)
+
+
+#############################
+# Compile each test
+FOREACH( TEST ${TEST_LIST} )
+ ADD_EXECUTABLE(${TEST} ${TEST}.c tests.h ${${TEST}_ADDITIONAL})
+ TARGET_LINK_LIBRARIES(${TEST} libfdproto libfdcore ${GNUTLS_LIBRARIES} ${GCRYPT_LIBRARY} ${${TEST}_ADDITIONAL_LIB})
+ ADD_TEST(${TEST} ${EXECUTABLE_OUTPUT_PATH}/${TEST} ${TEST_ARGUMENTS})
+ENDFOREACH( TEST )
+
+
+####
+## INSTALL section ##
+
+# we do not install the tests
diff --git a/tests/testappacct.c b/tests/testappacct.c
new file mode 100644
index 0000000..2ec94f3
--- /dev/null
+++ b/tests/testappacct.c
@@ -0,0 +1,278 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 "tests.h"
+
+/* The connection string to the database */
+#ifndef TEST_CONNINFO
+#error "Please specify the conninfo information"
+#endif /* TEST_CONNINFO */
+
+/* The table used for tests. This table will receive the following instructions:
+DROP TABLE <table>;
+CREATE TABLE <table>
+(
+ recorded_on timestamp with time zone NOT NULL,
+ "Accounting-Record-Type" integer,
+ "Session-Id" bytea,
+ "Accounting-Record-Number" integer,
+ "Route-Record1" bytea,
+ "Route-Record2" bytea,
+ "Route-Record3" bytea,
+ "Route-Record4" bytea
+);
+*/
+#define TABLE "incoming_test"
+
+#include "app_acct.h"
+#include <libpq-fe.h>
+
+static int add_avp_in_conf(char * avpname, int multi)
+{
+ struct acct_conf_avp *new;
+ struct dict_object * dict;
+ struct dict_avp_data dictdata;
+
+ /* Validate the avp name first */
+ CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, avpname, &dict, ENOENT) );
+ CHECK_FCT( fd_dict_getval( dict, &dictdata ));
+
+ /* Create a new entry */
+ CHECK_MALLOC( new = malloc(sizeof(struct acct_conf_avp)) );
+ memset(new, 0, sizeof(struct acct_conf_avp));
+ fd_list_init(&new->chain, NULL);
+ new->avpname = avpname;
+ new->avpobj = dict;
+ new->avptype = dictdata.avp_basetype;
+ new->multi = multi;
+
+ /* Add this new entry at the end of the list */
+ fd_list_insert_before( &acct_config->avps, &new->chain );
+
+ return 0;
+}
+
+#define LOCAL_ID "test.app.acct"
+#define LOCAL_REALM "app.acct"
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+ extern pthread_key_t connk; /* in acct_db.c */
+ PGconn *conn;
+ extern int fd_ext_init(int major, int minor, char * conffile); /* defined in include's extension.h */
+ extern void fd_ext_fini(void); /* defined in the extension itself */
+ struct msg * msg;
+ os0_t sess_bkp;
+ size_t sess_bkp_len;
+
+ /* First, initialize the daemon modules */
+ INIT_FD();
+ fd_g_config->cnf_diamid = strdup(LOCAL_ID);
+ fd_g_config->cnf_diamid_len = CONSTSTRLEN(LOCAL_ID);
+ fd_g_config->cnf_diamrlm = strdup(LOCAL_REALM);
+ fd_g_config->cnf_diamrlm_len = CONSTSTRLEN(LOCAL_REALM);
+
+ CHECK( 0, fd_queues_init() );
+ CHECK( 0, fd_msg_init() );
+ CHECK( 0, fd_rtdisp_init() );
+
+ /* Initialize the extension configuration for the test */
+ {
+ CHECK( 0, acct_conf_init() );
+ acct_config->conninfo = strdup(TEST_CONNINFO);
+ acct_config->tablename = strdup(TABLE);
+ acct_config->tsfield = strdup("recorded_on");
+ CHECK( 0, add_avp_in_conf(strdup("Session-Id"), 0) );
+ CHECK( 0, add_avp_in_conf(strdup("Accounting-Record-Type"), 0) );
+ CHECK( 0, add_avp_in_conf(strdup("Accounting-Record-Number"), 0) );
+ CHECK( 0, add_avp_in_conf(strdup("Route-Record"), 4) );
+
+ /* Now, call the one of the extension */
+ CHECK( 0, fd_ext_init(FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR,NULL) );
+ conn = pthread_getspecific(connk);
+ }
+
+ /* Drop and recreate the table for the test */
+ {
+ PGresult * res;
+ CHECK( CONNECTION_OK, PQstatus(conn) );
+
+ res = PQexec(conn, "DROP TABLE " TABLE ";");
+ CHECK( PGRES_COMMAND_OK, PQresultStatus(res) );
+ PQclear(res);
+
+ res = PQexec(conn, "CREATE TABLE " TABLE " ( "
+ " recorded_on timestamp with time zone NOT NULL, "
+ " \"Accounting-Record-Type\" integer, "
+ " \"Session-Id\" bytea, "
+ " \"Accounting-Record-Number\" integer, "
+ " \"Route-Record1\" bytea, "
+ " \"Route-Record2\" bytea, "
+ " \"Route-Record3\" bytea, "
+ " \"Route-Record4\" bytea "
+ ");"
+ );
+ CHECK( PGRES_COMMAND_OK, PQresultStatus(res) );
+ PQclear(res);
+ }
+
+ /* OK, we are ready to test now. Create an ACR message that will pass the ABNF check */
+ {
+ struct dict_object * d = NULL;
+ struct avp *avp = NULL;
+ union avp_value avp_val;
+
+ /* Now find the ACR dictionary object */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &d, ENOENT ) );
+
+ /* Create the instance */
+ CHECK( 0, fd_msg_new ( d, MSGFL_ALLOC_ETEID, &msg ) );
+
+ /* App id */
+ {
+ struct msg_hdr * h;
+ CHECK( 0, fd_msg_hdr( msg, &h ) );
+ h->msg_appl = 3;
+ }
+
+ /* sid */
+ {
+ struct session * sess = NULL;
+ os0_t s;
+ CHECK( 0, fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, NULL, 0) );
+ CHECK( 0, fd_sess_getsid(sess, &s, &sess_bkp_len) );
+ CHECK( 1, (sess_bkp = os0dup(s, sess_bkp_len)) ? 1 : 0);
+
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &d, ENOENT ) );
+ CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) );
+ memset(&avp_val, 0, sizeof(avp_val));
+ avp_val.os.data = sess_bkp;
+ avp_val.os.len = sess_bkp_len;
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) );
+ CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_FIRST_CHILD, avp) );
+ }
+
+ /* Origin-* */
+ CHECK( 0, fd_msg_add_origin(msg, 1) );
+
+ /* Destination-Realm */
+ {
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &d, ENOENT ) );
+ CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) );
+ memset(&avp_val, 0, sizeof(avp_val));
+ avp_val.os.data = (unsigned char *)fd_g_config->cnf_diamrlm;
+ avp_val.os.len = fd_g_config->cnf_diamrlm_len;
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) );
+ CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) );
+ }
+
+ /* Accounting-Record-Type */
+ {
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Type", &d, ENOENT ) );
+ CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) );
+ memset(&avp_val, 0, sizeof(avp_val));
+ avp_val.u32 = 2;
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) );
+ CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) );
+ }
+
+ /* Accounting-Record-Number */
+ {
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Number", &d, ENOENT ) );
+ CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) );
+ memset(&avp_val, 0, sizeof(avp_val));
+ avp_val.u32 = 2;
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) );
+ CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) );
+ }
+
+ /* Route-Record */
+ {
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &d, ENOENT ) );
+ CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) );
+ memset(&avp_val, 0, sizeof(avp_val));
+ avp_val.os.data = (unsigned char *)"peer1";
+ avp_val.os.len = strlen((char *)avp_val.os.data);
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) );
+ CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) );
+
+ CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) );
+ memset(&avp_val, 0, sizeof(avp_val));
+ avp_val.os.data = (unsigned char *)"peer2";
+ avp_val.os.len = strlen((char *)avp_val.os.data);
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) );
+ CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) );
+ }
+
+ /* Source */
+ CHECK( 0, fd_msg_source_set( msg, "peer3", CONSTSTRLEN("peer3") ) );
+ CHECK( 0, fd_msg_source_setrr( msg, "peer3", CONSTSTRLEN("peer3"), fd_g_config->cnf_dict ) );
+ }
+
+ /* Now, have the daemon handle this */
+ CHECK( 0, fd_fifo_post(fd_g_incoming, &msg) );
+
+ /* It is picked by the dispatch module, the extension handles the query, inserts the records in the DB, send creates the answer.
+ Once the answer is ready, it is sent to "peer3" which is not available of course; then the message is simply destroyed.
+ We wait 1 second for this to happen... */
+ sleep(1);
+
+ /* Now, check the record was actually registered properly */
+ {
+ PGresult * res;
+ uint8_t * bs;
+ char * es;
+ size_t l;
+
+ res = PQexec(conn, "SELECT \"Session-Id\" from " TABLE ";");
+ CHECK( PGRES_TUPLES_OK, PQresultStatus(res) );
+
+ /* We also check that the Session-Id we retrieve is the same as what we generated earlier (not trashed in the process) */
+ es = PQgetvalue(res, 0, 0);
+ bs = PQunescapeBytea((uint8_t *)es, &l);
+
+ CHECK( 0, fd_os_cmp(bs, l, sess_bkp, sess_bkp_len) );
+
+ PQclear(res);
+ PQfreemem(bs);
+ }
+
+ /* That's all for the tests yet */
+ free(sess_bkp);
+
+ PASSTEST();
+}
+
diff --git a/tests/testcnx.c b/tests/testcnx.c
new file mode 100644
index 0000000..e1826c9
--- /dev/null
+++ b/tests/testcnx.c
@@ -0,0 +1,2009 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 "tests.h"
+
+#ifndef TEST_PORT
+#define TEST_PORT 3868
+#endif /* TEST_PORT */
+
+#ifndef NB_STREAMS
+#define NB_STREAMS 10
+#endif /* NB_STREAMS */
+
+#ifndef GNUTLS_DEFAULT_PRIORITY
+# define GNUTLS_DEFAULT_PRIORITY "NORMAL"
+#endif /* GNUTLS_DEFAULT_PRIORITY */
+
+#ifndef GNUTLS_DEFAULT_DHBITS
+# define GNUTLS_DEFAULT_DHBITS 1024
+#endif /* GNUTLS_DEFAULT_DHBITS */
+
+
+/* The cryptographic data */
+static char ca_data[] = "-----BEGIN CERTIFICATE-----\n"
+ "MIIEqjCCA5KgAwIBAgIJANKgDwdlDYQDMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD\n"
+ "VQQGEwJKUDEOMAwGA1UECAwFVG9reW8xEDAOBgNVBAcMB0tvZ2FuZWkxDTALBgNV\n"
+ "BAoMBFdJREUxDzANBgNVBAsMBkFBQSBXRzEfMB0GA1UEAwwWY2hhdnJvdXguY293\n"
+ "YWRkaWN0Lm9yZzEiMCAGCSqGSIb3DQEJARYTc2RlY3VnaXNAbmljdC5nby5qcDAe\n"
+ "Fw0wOTEwMDUwODUxNDRaFw0xOTEwMDMwODUxNDRaMIGUMQswCQYDVQQGEwJKUDEO\n"
+ "MAwGA1UECAwFVG9reW8xEDAOBgNVBAcMB0tvZ2FuZWkxDTALBgNVBAoMBFdJREUx\n"
+ "DzANBgNVBAsMBkFBQSBXRzEfMB0GA1UEAwwWY2hhdnJvdXguY293YWRkaWN0Lm9y\n"
+ "ZzEiMCAGCSqGSIb3DQEJARYTc2RlY3VnaXNAbmljdC5nby5qcDCCASIwDQYJKoZI\n"
+ "hvcNAQEBBQADggEPADCCAQoCggEBAM5c6w4NnngTvGNWcJzbo0Kklp+kvUNQNgGu\n"
+ "myvz826qPp07HTSyJrIcgFnuYDR0Nd130Ot9u5osqpQhHTvolxDE87Tii8i3hJSj\n"
+ "TTY9K0ZwGb4AZ6QkuyMXS1jtOY657HqjpGZqT/2Syh0i7dM/hqSXFw0SPbyq+W1H\n"
+ "SVFWa1CTkPywFWAzwdr5WKah77uZ1dxWqgPgUdcZOiIQtLRp5n3fg40Nwso5YdwS\n"
+ "64+ebBX1pkhrCQ8AGc8O61Ep1JTXcO7jqQmPgzjiN+FeostI1Dp73S3MqleTAHjR\n"
+ "hqZ77VF7nkroMM9btMHJBaxnfwc2ewULUJwnuOiGWrvMq/9Z4J8CAwEAAaOB/DCB\n"
+ "+TAdBgNVHQ4EFgQUkqpVn7N3gmiJ7X5zQ2bki+7qv4UwgckGA1UdIwSBwTCBvoAU\n"
+ "kqpVn7N3gmiJ7X5zQ2bki+7qv4WhgZqkgZcwgZQxCzAJBgNVBAYTAkpQMQ4wDAYD\n"
+ "VQQIDAVUb2t5bzEQMA4GA1UEBwwHS29nYW5laTENMAsGA1UECgwEV0lERTEPMA0G\n"
+ "A1UECwwGQUFBIFdHMR8wHQYDVQQDDBZjaGF2cm91eC5jb3dhZGRpY3Qub3JnMSIw\n"
+ "IAYJKoZIhvcNAQkBFhNzZGVjdWdpc0BuaWN0LmdvLmpwggkA0qAPB2UNhAMwDAYD\n"
+ "VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAJy0XLk8j8YLSTt2/VMy9TAUx\n"
+ "esXUiZj0Ung+gkr7A1K0NnwYxDzG2adMhf13upHoydu2ErLMmD6F77x+QuY/q7nc\n"
+ "ZvO0tvcoAP6ToSDwiypU5dnTmnfkgwVwzFkNCi1sGRosEm8c/c/8MfK0I0nVdj1/\n"
+ "BIkIG7tTDVi9JvkWYl0UlSKWTZKrntVwCmscfC02DGb+GoLbO9+QmiNM5Y3yOYZ4\n"
+ "Pc7SSoKLL0rwJBmpPNs7boYsweeSuCAVu0shRfgC90odXcej2EN5ETfCuU1evXNW\n"
+ "5cA+zZsDK/nWJwxBaW0CxAHX579FElFWlK4+BnzhZRdDhmJDnN5dh4ekJGM6Lg==\n"
+ "-----END CERTIFICATE-----\n";
+
+/* Client:
+ Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 5 (0x5)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=chavroux.cowaddict.org/emailAddress=sdecugis@nict.go.jp
+ Validity
+ Not Before: Oct 27 04:04:05 2009 GMT
+ Not After : Oct 25 04:04:05 2019 GMT
+ Subject: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=client.test/emailAddress=client@test
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:bd:eb:50:1e:9d:7a:cd:9d:bb:e7:bc:4e:38:4a:
+ b2:cc:9e:b4:89:77:01:ef:d1:c6:19:29:00:fe:ce:
+ 3c:62:05:13:b1:8c:ff:31:7a:0f:c1:2e:4b:3c:0c:
+ 40:1e:36:4e:76:da:0a:64:43:fc:1e:ea:0c:97:b2:
+ 57:9c:9c:8c:90:bd:eb:23:7b:b8:b7:5c:03:ed:6f:
+ 48:55:8a:88:08:38:c5:cd:33:b7:ab:a8:3a:6f:7f:
+ 13:10:65:a5:50:b9:f4:8b:cc:2e:e9:79:58:a6:11:
+ f0:58:45:41:ef:36:b3:35:cb:14:ec:82:0c:ad:11:
+ 6a:ea:64:ef:28:a2:6e:47:45
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ BE:B3:89:4F:9D:8F:6C:20:C4:D0:3E:6A:05:11:82:50:54:49:70:A2
+ X509v3 Authority Key Identifier:
+ keyid:92:AA:55:9F:B3:77:82:68:89:ED:7E:73:43:66:E4:8B:EE:EA:BF:85
+
+ Signature Algorithm: sha1WithRSAEncryption
+ a3:88:f5:15:b5:ad:20:60:a1:85:19:3f:b9:5e:1e:be:31:7f:
+ 84:7a:c2:18:3a:63:6a:67:1f:46:86:4d:10:d6:1d:ad:a2:c8:
+ 0b:95:33:fa:e4:05:f4:b8:70:34:77:f7:85:6e:70:46:ac:39:
+ 54:a9:5f:ea:5e:d1:33:bb:c9:a3:42:81:41:90:25:b5:92:8b:
+ e8:6e:3e:97:06:dd:9a:cc:29:61:34:5a:d3:1c:5d:ad:d1:a3:
+ eb:6a:47:b4:d0:c2:17:89:e1:e2:2d:36:18:50:1a:e7:d4:fc:
+ 38:2e:47:0b:39:50:87:2f:aa:07:64:f8:9a:4d:47:01:da:10:
+ d8:97:c7:a6:13:bc:0e:ca:63:c1:f2:09:fb:f8:6a:a4:5f:08:
+ b5:ad:ed:4f:71:b9:89:7f:43:27:85:72:e7:8d:a8:4a:cc:f6:
+ 36:ca:8a:ae:82:b5:a8:42:41:99:87:84:7c:f0:90:fd:ca:96:
+ 37:a2:e0:d9:fa:dd:a4:c9:f1:50:b7:e5:e6:8f:af:83:8c:23:
+ b6:20:cc:66:e3:08:60:13:02:8f:42:3a:07:91:a7:38:b2:72:
+ 16:fd:bd:a9:60:f0:e2:9f:23:f3:c0:99:e3:17:bc:00:7c:b3:
+ 89:9c:ea:fa:3e:f6:69:a1:98:c2:ec:46:da:70:b6:f9:c3:93:
+ a7:fc:36:dd
+*/
+static char client_cert_data[] ="-----BEGIN CERTIFICATE-----\n"
+ "MIIDiTCCAnGgAwIBAgIBBTANBgkqhkiG9w0BAQUFADCBlDELMAkGA1UEBhMCSlAx\n"
+ "DjAMBgNVBAgMBVRva3lvMRAwDgYDVQQHDAdLb2dhbmVpMQ0wCwYDVQQKDARXSURF\n"
+ "MQ8wDQYDVQQLDAZBQUEgV0cxHzAdBgNVBAMMFmNoYXZyb3V4LmNvd2FkZGljdC5v\n"
+ "cmcxIjAgBgkqhkiG9w0BCQEWE3NkZWN1Z2lzQG5pY3QuZ28uanAwHhcNMDkxMDI3\n"
+ "MDQwNDA1WhcNMTkxMDI1MDQwNDA1WjCBgTELMAkGA1UEBhMCSlAxDjAMBgNVBAgM\n"
+ "BVRva3lvMRAwDgYDVQQHDAdLb2dhbmVpMQ0wCwYDVQQKDARXSURFMQ8wDQYDVQQL\n"
+ "DAZBQUEgV0cxFDASBgNVBAMMC2NsaWVudC50ZXN0MRowGAYJKoZIhvcNAQkBFgtj\n"
+ "bGllbnRAdGVzdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvetQHp16zZ27\n"
+ "57xOOEqyzJ60iXcB79HGGSkA/s48YgUTsYz/MXoPwS5LPAxAHjZOdtoKZEP8HuoM\n"
+ "l7JXnJyMkL3rI3u4t1wD7W9IVYqICDjFzTO3q6g6b38TEGWlULn0i8wu6XlYphHw\n"
+ "WEVB7zazNcsU7IIMrRFq6mTvKKJuR0UCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglg\n"
+ "hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O\n"
+ "BBYEFL6ziU+dj2wgxNA+agURglBUSXCiMB8GA1UdIwQYMBaAFJKqVZ+zd4Joie1+\n"
+ "c0Nm5Ivu6r+FMA0GCSqGSIb3DQEBBQUAA4IBAQCjiPUVta0gYKGFGT+5Xh6+MX+E\n"
+ "esIYOmNqZx9Ghk0Q1h2tosgLlTP65AX0uHA0d/eFbnBGrDlUqV/qXtEzu8mjQoFB\n"
+ "kCW1kovobj6XBt2azClhNFrTHF2t0aPrake00MIXieHiLTYYUBrn1Pw4LkcLOVCH\n"
+ "L6oHZPiaTUcB2hDYl8emE7wOymPB8gn7+GqkXwi1re1PcbmJf0MnhXLnjahKzPY2\n"
+ "yoqugrWoQkGZh4R88JD9ypY3ouDZ+t2kyfFQt+Xmj6+DjCO2IMxm4whgEwKPQjoH\n"
+ "kac4snIW/b2pYPDinyPzwJnjF7wAfLOJnOr6PvZpoZjC7EbacLb5w5On/Dbd\n"
+ "-----END CERTIFICATE-----\n";
+static char client_priv_data[] ="-----BEGIN RSA PRIVATE KEY-----\n"
+ "MIICXgIBAAKBgQC961AenXrNnbvnvE44SrLMnrSJdwHv0cYZKQD+zjxiBROxjP8x\n"
+ "eg/BLks8DEAeNk522gpkQ/we6gyXslecnIyQvesje7i3XAPtb0hViogIOMXNM7er\n"
+ "qDpvfxMQZaVQufSLzC7peVimEfBYRUHvNrM1yxTsggytEWrqZO8oom5HRQIDAQAB\n"
+ "AoGBAIYnsOLPby3LnC5n8AEHkyHDgdgQvsd/MSYYtuFHIZRD7dNfu+xhQru9TdvO\n"
+ "84Pj7K07/FczRuc3gUmu6wBv/UIP9To15RHZh+/n537nybGus5S4IYKVvap477To\n"
+ "0rQDf9ec27iw77gxb7moQ9Otuxwbv0h0Z+1EVLI8d8jHOq0BAkEA9YNr0R+7KXBS\n"
+ "48yT43g5HpOFkTZzNXWVdpSvYGneb56wslk5Eatp235I4uz/a7Rej5v99W0M3nSe\n"
+ "/AgHfYn75QJBAMYH/pBx/WkrLj+pPaARlNwInCIC5zUhr6B0IKCt2tvy5eyuc5sd\n"
+ "AoTFaU+cSI+ZqsRzY8jMKkonktxBg48oJ+ECQQCt4AtlqcFVkbVCm8pJGQXq/7Ni\n"
+ "qlthiwr1Vkv2TkQ4bPza8pGWT/3Cc2ePPyWN08n8jw+G11p72cAW4mDbqfN5AkEA\n"
+ "mNYKrkiLn+NnqlJf8W4gSUGL3uQGtYbuGRQHKnuDckWhFm39YzWcgAQsJvkjN1EN\n"
+ "7thvpsWLzfeE7ODTPGVtgQJATObxYJOt6rms3fAStwuXW3ET77TA1ja4XsUEe5Yu\n"
+ "JpcQOruJb9XwndqzNbL0dSUePb9gFiBCGKYOyreNTTRTmw==\n"
+ "-----END RSA PRIVATE KEY-----\n";
+
+/* Server:
+ Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4 (0x4)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=chavroux.cowaddict.org/emailAddress=sdecugis@nict.go.jp
+ Validity
+ Not Before: Oct 27 04:03:39 2009 GMT
+ Not After : Oct 25 04:03:39 2019 GMT
+ Subject: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=serv.test/emailAddress=serv@test
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:a6:f7:1c:a9:90:5b:fa:c8:f6:a3:04:0c:d0:8b:
+ 45:c3:90:f7:2d:c2:c9:d7:bd:66:8a:7c:1c:51:89:
+ 40:9e:cd:70:57:cb:00:47:a3:e8:76:8b:00:b3:c9:
+ c3:0d:b1:b9:2a:08:9f:52:92:82:d3:18:c1:d8:d1:
+ b8:1e:fd:71:fe:23:ec:19:e9:6d:9d:fd:ae:88:bc:
+ 39:44:7a:37:ad:c6:88:d1:64:7c:b1:d4:3c:a9:30:
+ c4:de:51:02:c4:48:4f:25:3e:2f:93:ae:25:32:66:
+ 9a:dc:f4:44:45:ff:7f:12:49:97:0d:01:8d:13:9a:
+ d3:8f:9e:2d:62:95:02:0a:c7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 0C:33:C4:7F:39:D0:34:FF:F8:61:A1:46:8B:49:1D:A3:57:B3:4D:58
+ X509v3 Authority Key Identifier:
+ keyid:92:AA:55:9F:B3:77:82:68:89:ED:7E:73:43:66:E4:8B:EE:EA:BF:85
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 87:f5:49:a6:04:f9:98:9a:f1:1a:68:ce:06:ae:4c:0c:08:eb:
+ ba:98:e7:3f:df:22:7f:35:88:1d:b7:8a:f3:89:a3:68:0d:53:
+ 45:eb:23:a1:dd:6b:dc:b0:80:58:0c:10:0b:49:74:ea:a8:b6:
+ 8c:2e:c6:73:dc:7a:74:c7:59:3e:79:5a:d2:5c:15:0b:f1:d8:
+ 19:37:2a:c0:22:75:10:3f:4c:e9:a1:e0:eb:b2:9e:09:70:3d:
+ 2a:4c:fe:9c:99:36:4b:aa:6c:e1:8b:9c:aa:e1:29:1f:49:6b:
+ 14:db:12:ae:cf:68:4a:dd:03:e1:3b:ad:79:b4:54:84:1d:bb:
+ ac:45:c4:85:f1:03:65:65:96:23:ae:e7:97:3c:5c:db:ce:55:
+ 34:5d:c3:73:ec:cd:f6:0f:a5:81:5f:c2:ab:a3:42:fa:36:7f:
+ 83:ef:db:0f:cd:62:0b:ea:d9:4f:73:35:68:5f:23:d5:0a:be:
+ ff:7f:23:9a:af:0d:a5:f8:3e:3a:f0:63:1c:e1:d2:96:81:cf:
+ 7b:5a:6b:d0:9b:67:56:9e:aa:a9:e8:f1:6c:fb:54:2b:1a:f4:
+ ef:16:5a:be:1d:a9:c8:d6:cc:f7:42:8c:fe:83:2c:84:8c:80:
+ fb:1c:88:f6:35:1c:ae:43:72:fa:68:30:9c:25:8b:db:2c:84:
+ 87:76:9d:b9
+*/
+static char server_cert_data[] ="-----BEGIN CERTIFICATE-----\n"
+ "MIIDhDCCAmygAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBlDELMAkGA1UEBhMCSlAx\n"
+ "DjAMBgNVBAgMBVRva3lvMRAwDgYDVQQHDAdLb2dhbmVpMQ0wCwYDVQQKDARXSURF\n"
+ "MQ8wDQYDVQQLDAZBQUEgV0cxHzAdBgNVBAMMFmNoYXZyb3V4LmNvd2FkZGljdC5v\n"
+ "cmcxIjAgBgkqhkiG9w0BCQEWE3NkZWN1Z2lzQG5pY3QuZ28uanAwHhcNMDkxMDI3\n"
+ "MDQwMzM5WhcNMTkxMDI1MDQwMzM5WjB9MQswCQYDVQQGEwJKUDEOMAwGA1UECAwF\n"
+ "VG9reW8xEDAOBgNVBAcMB0tvZ2FuZWkxDTALBgNVBAoMBFdJREUxDzANBgNVBAsM\n"
+ "BkFBQSBXRzESMBAGA1UEAwwJc2Vydi50ZXN0MRgwFgYJKoZIhvcNAQkBFglzZXJ2\n"
+ "QHRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKb3HKmQW/rI9qMEDNCL\n"
+ "RcOQ9y3Cyde9Zop8HFGJQJ7NcFfLAEej6HaLALPJww2xuSoIn1KSgtMYwdjRuB79\n"
+ "cf4j7BnpbZ39roi8OUR6N63GiNFkfLHUPKkwxN5RAsRITyU+L5OuJTJmmtz0REX/\n"
+ "fxJJlw0BjROa04+eLWKVAgrHAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4\n"
+ "QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBQM\n"
+ "M8R/OdA0//hhoUaLSR2jV7NNWDAfBgNVHSMEGDAWgBSSqlWfs3eCaIntfnNDZuSL\n"
+ "7uq/hTANBgkqhkiG9w0BAQUFAAOCAQEAh/VJpgT5mJrxGmjOBq5MDAjrupjnP98i\n"
+ "fzWIHbeK84mjaA1TResjod1r3LCAWAwQC0l06qi2jC7Gc9x6dMdZPnla0lwVC/HY\n"
+ "GTcqwCJ1ED9M6aHg67KeCXA9Kkz+nJk2S6ps4YucquEpH0lrFNsSrs9oSt0D4Tut\n"
+ "ebRUhB27rEXEhfEDZWWWI67nlzxc285VNF3Dc+zN9g+lgV/Cq6NC+jZ/g+/bD81i\n"
+ "C+rZT3M1aF8j1Qq+/38jmq8Npfg+OvBjHOHSloHPe1pr0JtnVp6qqejxbPtUKxr0\n"
+ "7xZavh2pyNbM90KM/oMshIyA+xyI9jUcrkNy+mgwnCWL2yyEh3aduQ==\n"
+ "-----END CERTIFICATE-----\n";
+static char server_priv_data[] ="-----BEGIN RSA PRIVATE KEY-----\n"
+ "MIICXQIBAAKBgQCm9xypkFv6yPajBAzQi0XDkPctwsnXvWaKfBxRiUCezXBXywBH\n"
+ "o+h2iwCzycMNsbkqCJ9SkoLTGMHY0bge/XH+I+wZ6W2d/a6IvDlEejetxojRZHyx\n"
+ "1DypMMTeUQLESE8lPi+TriUyZprc9ERF/38SSZcNAY0TmtOPni1ilQIKxwIDAQAB\n"
+ "AoGAZv3Ddm0P79CLIt9asEFY1VvUvSuMqkGwwPfx1/HcJJkBFYapM4fN22G/Gyf3\n"
+ "47ifSWhsLtklTeXVnVMwSh14dJaJQuSEnaFnUUWfjiRbEAXZnMFwAIiaszEZbPap\n"
+ "NUNpcGl06FZrphYAMkjOVUfjCjfOZDAvL4JGpo271Zx4l0ECQQDYoFFQpBCPx0PK\n"
+ "TWUmvatXI/Amo94XkGfofbdeeI8PiAJBO5UI6rmjjIVwsJwO9dQb/IlP1/OnBeJv\n"
+ "p9YW5uixAkEAxVAOKu7mpGu0Q/K2iEUUYDX9YHf253kgkdIDF4iZk4Tcecjoxuru\n"
+ "fIWu9dMtyDVV+HT2X4cNEnO1/oS3kJII9wJBAJkdwDwiqz4lV6o/yFZ4zAoc8dsu\n"
+ "CoZXYMq5SYox5tTQit928OHLn4mVgqBjhPsiEVnyx0+zUZpmE2ZemHm5nxECQHfE\n"
+ "FBVzVYRP6+eil7E3XRrZKqc3qiLunxpkA4RxYebtKnaxwLmdOI1VB9InEQ8JcNmT\n"
+ "BUkOzJx6p+mJ3XJfchkCQQDWmbMYYJajsjlS4YpdUUj7cBSotA6vtkNVHFr0/ak/\n"
+ "S+tLkMNuruaInWizK+BKYTIJLlQDf5u5NTrw41vye5Hv\n"
+ "-----END RSA PRIVATE KEY-----\n";
+
+/* Expired:
+ Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 6 (0x6)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=chavroux.cowaddict.org/emailAddress=sdecugis@nict.go.jp
+ Validity
+ Not Before: Oct 27 04:06:35 2009 GMT
+ Not After : Oct 28 04:06:35 2009 GMT
+ Subject: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=expired.test/emailAddress=expired@test
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:e3:17:15:54:85:dc:cf:c7:a0:32:4a:49:7d:55:
+ 75:9b:29:15:db:7e:87:17:d9:0e:65:44:53:d7:19:
+ 37:27:c7:c6:fe:c6:dc:72:2b:dc:86:1a:ff:24:6c:
+ 63:3f:75:9c:0a:14:e1:70:06:79:d4:b9:26:d4:68:
+ 4c:28:38:ba:34:60:56:02:3d:94:55:4a:1f:4e:5a:
+ f0:a5:71:4c:3e:71:69:39:ad:bc:aa:55:35:fb:73:
+ 5b:5f:6c:30:71:8e:8a:b6:a5:06:cc:ee:dd:29:c7:
+ 52:0d:a7:9c:0f:a1:ba:52:11:e2:1b:b9:74:6b:08:
+ 87:11:d2:ec:a9:ac:63:63:4f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 1C:AF:66:42:5B:AD:AA:A5:9B:D9:AE:3A:C1:5A:AC:2F:CC:CE:22:6C
+ X509v3 Authority Key Identifier:
+ keyid:92:AA:55:9F:B3:77:82:68:89:ED:7E:73:43:66:E4:8B:EE:EA:BF:85
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 60:8f:55:55:59:82:0f:64:cb:b8:11:c8:44:ce:bf:69:07:0d:
+ be:c2:34:be:42:6a:78:15:39:9f:be:8a:17:d6:43:42:c9:7c:
+ f1:6d:5d:aa:c3:1b:4d:b0:f0:b6:73:46:2a:87:cd:55:56:a3:
+ 6d:cc:de:a8:28:6a:53:85:9e:e5:68:b7:3c:f5:72:13:7b:d0:
+ 21:f2:91:49:35:e0:37:1e:28:19:d5:1b:cc:e1:32:1e:7f:b0:
+ 86:df:43:a4:47:0f:29:0b:eb:51:60:9a:f5:ca:50:f4:2d:59:
+ cd:fc:50:9d:29:ed:45:98:de:a2:5c:d1:b5:7a:34:ad:7a:73:
+ 48:8b:a2:9b:89:8e:4a:2e:2a:04:19:d6:62:6a:0d:f0:96:f2:
+ f0:d0:22:77:3b:7f:b1:2a:f4:3b:17:47:5e:38:07:09:65:ad:
+ 1d:ea:46:69:6a:96:b6:6b:3b:5c:cc:6e:30:d7:cb:53:69:59:
+ c2:63:78:2b:03:d4:d4:f7:17:29:99:9a:43:ff:78:0a:af:42:
+ c5:b3:8d:09:38:5b:30:70:28:c1:97:ab:fd:7f:87:9a:ec:f2:
+ 97:44:ff:f5:b9:41:30:d1:c6:32:98:69:34:c4:39:30:6f:e2:
+ d3:b2:70:97:66:ee:41:f5:ae:0f:09:f0:ed:60:96:67:a9:8a:
+ cd:d6:95:f2
+*/
+static char expired_cert_data[]="-----BEGIN CERTIFICATE-----\n"
+ "MIIDizCCAnOgAwIBAgIBBjANBgkqhkiG9w0BAQUFADCBlDELMAkGA1UEBhMCSlAx\n"
+ "DjAMBgNVBAgMBVRva3lvMRAwDgYDVQQHDAdLb2dhbmVpMQ0wCwYDVQQKDARXSURF\n"
+ "MQ8wDQYDVQQLDAZBQUEgV0cxHzAdBgNVBAMMFmNoYXZyb3V4LmNvd2FkZGljdC5v\n"
+ "cmcxIjAgBgkqhkiG9w0BCQEWE3NkZWN1Z2lzQG5pY3QuZ28uanAwHhcNMDkxMDI3\n"
+ "MDQwNjM1WhcNMDkxMDI4MDQwNjM1WjCBgzELMAkGA1UEBhMCSlAxDjAMBgNVBAgM\n"
+ "BVRva3lvMRAwDgYDVQQHDAdLb2dhbmVpMQ0wCwYDVQQKDARXSURFMQ8wDQYDVQQL\n"
+ "DAZBQUEgV0cxFTATBgNVBAMMDGV4cGlyZWQudGVzdDEbMBkGCSqGSIb3DQEJARYM\n"
+ "ZXhwaXJlZEB0ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjFxVUhdzP\n"
+ "x6AySkl9VXWbKRXbfocX2Q5lRFPXGTcnx8b+xtxyK9yGGv8kbGM/dZwKFOFwBnnU\n"
+ "uSbUaEwoOLo0YFYCPZRVSh9OWvClcUw+cWk5rbyqVTX7c1tfbDBxjoq2pQbM7t0p\n"
+ "x1INp5wPobpSEeIbuXRrCIcR0uyprGNjTwIDAQABo3sweTAJBgNVHRMEAjAAMCwG\n"
+ "CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV\n"
+ "HQ4EFgQUHK9mQlutqqWb2a46wVqsL8zOImwwHwYDVR0jBBgwFoAUkqpVn7N3gmiJ\n"
+ "7X5zQ2bki+7qv4UwDQYJKoZIhvcNAQEFBQADggEBAGCPVVVZgg9ky7gRyETOv2kH\n"
+ "Db7CNL5CangVOZ++ihfWQ0LJfPFtXarDG02w8LZzRiqHzVVWo23M3qgoalOFnuVo\n"
+ "tzz1chN70CHykUk14DceKBnVG8zhMh5/sIbfQ6RHDykL61FgmvXKUPQtWc38UJ0p\n"
+ "7UWY3qJc0bV6NK16c0iLopuJjkouKgQZ1mJqDfCW8vDQInc7f7Eq9DsXR144Bwll\n"
+ "rR3qRmlqlrZrO1zMbjDXy1NpWcJjeCsD1NT3FymZmkP/eAqvQsWzjQk4WzBwKMGX\n"
+ "q/1/h5rs8pdE//W5QTDRxjKYaTTEOTBv4tOycJdm7kH1rg8J8O1glmepis3WlfI=\n"
+ "-----END CERTIFICATE-----\n";
+static char expired_priv_data[]="-----BEGIN RSA PRIVATE KEY-----\n"
+ "MIICXgIBAAKBgQDjFxVUhdzPx6AySkl9VXWbKRXbfocX2Q5lRFPXGTcnx8b+xtxy\n"
+ "K9yGGv8kbGM/dZwKFOFwBnnUuSbUaEwoOLo0YFYCPZRVSh9OWvClcUw+cWk5rbyq\n"
+ "VTX7c1tfbDBxjoq2pQbM7t0px1INp5wPobpSEeIbuXRrCIcR0uyprGNjTwIDAQAB\n"
+ "AoGASwPoDui9XYHTIGm7xwRA+kVjLAOq+qy//aHJlEeHGcP7r1PfpHNqwH4QhGat\n"
+ "jlv6dLYbFld9TVDwS8A8UBkVIPLWnCysd5tF2A4C5akx6ouW6HliW/JheYrgl8AV\n"
+ "PVeR3bm91UbnpC0ABVlw87jp1Ovyr60Suo4jsoJz+CyTa2ECQQD0LJWpnwn1jIlR\n"
+ "DGkLi7F3E70JJcdhTWzBjGFD+Na+/2ZO0MKLhK+O1WUkKa0oi+e5P1JOnGIpTI8c\n"
+ "BJOO415RAkEA7hauapYuqGI/auSPH8/nFB5z1G94RTxo2a5THKcG5MqS/8N3ubFj\n"
+ "i2PPS0lEYVjqoHEsZUsMnDmXp6KDKMAfnwJBAIp+T1UqM8fmsmwaEerOjRXxSCNM\n"
+ "Hk5+T9Vn/jNDjOpAipLhrbbcx4bIWtmsGd8Jm6Fi3RhhcvvhxLorjlZZeEECQQCf\n"
+ "IaPD88sNmlUewdLzhUbCiLQMadCuHflKfRxpyy1tYAQuVFxCTdDlynkzra25ju+K\n"
+ "+vmcXjP4evnk/lbBtt+rAkEAgOr4Apgs3nMppngPV5yFx0NDqH2n8PlEAM1Il4Qs\n"
+ "IuuK18v0KwlUGAfEEmCiNh1e1qkLmD0CnI2QjYAjcLQUhw==\n"
+ "-----END RSA PRIVATE KEY-----\n";
+
+/* Unknown CA certificate :
+ Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=chavroux.cowaddict.org/emailAddress=sdecugis@nict.go.jp
+ Validity
+ Not Before: Oct 28 08:04:40 2009 GMT
+ Not After : Oct 28 08:04:40 2010 GMT
+ Subject: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=unknown.cs/emailAddress=unknown@ca
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:e6:3a:d5:8a:14:c8:15:d0:f0:5c:03:c3:af:33:
+ 51:2c:17:b7:65:ac:45:e8:48:2d:ae:70:fd:7c:79:
+ 3a:c7:80:c8:50:53:d0:19:d8:3a:26:a8:16:4d:4c:
+ 04:17:09:df:69:9b:59:2b:89:c8:e0:60:bb:1d:37:
+ 82:d2:3f:17:39:c9:8f:5d:76:e1:0f:6e:08:9a:8f:
+ 16:4a:ea:83:86:f9:bd:15:14:56:68:87:79:05:f9:
+ 5f:66:11:bd:22:46:26:64:be:57:16:51:66:41:50:
+ ac:f2:b1:ca:d0:38:11:4b:4c:b2:ee:25:36:6e:d3:
+ b9:63:72:c4:84:82:1c:2b:27
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ BA:5A:9D:D2:B0:4B:72:D6:1F:00:11:0B:B5:7B:59:DF:08:38:81:BE
+ X509v3 Authority Key Identifier:
+ keyid:52:C5:A4:63:B8:DB:AC:F2:92:34:2F:72:56:71:C8:11:8E:76:E6:DF
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 90:8f:3b:bd:e3:a1:ca:6a:92:a6:fd:f0:64:ae:46:83:32:35:
+ 61:80:57:8b:30:12:70:02:e1:51:d9:87:c8:af:d9:4b:b9:6d:
+ bf:ab:86:5f:19:1f:dc:af:84:67:bf:3c:bf:33:f3:7c:c6:81:
+ 7b:e4:e9:26:1d:bc:d6:8c:ab:72:94:7f:85:33:95:d9:24:ec:
+ fd:7b:d2:fd:50:3e:e5:61:4f:75:51:ae:c6:4a:ec:df:cf:aa:
+ 73:a5:08:f7:f3:9a:40:66:48:f0:8e:9b:43:b1:30:f3:e3:c8:
+ 36:3f:68:36:6a:1c:aa:16:40:49:b4:73:9a:71:f1:17:6c:0b:
+ d3:e1:a7:b7:40:de:2c:3c:36:7c:d4:dd:d6:94:c9:d7:5f:f5:
+ ae:35:56:e8:cc:65:9c:bb:3d:e8:7a:ca:0e:ed:78:03:41:cb:
+ fd:80:81:de:f9:de:b2:14:4b:81:24:36:de:29:c1:06:11:86:
+ 8c:a9:b0:0c:c7:57:cf:79:a7:3a:84:0c:27:dc:86:6d:cb:44:
+ 2d:26:dc:7e:fb:17:d6:b2:3d:31:03:d3:f1:ab:5d:91:5d:94:
+ e4:94:88:70:96:b3:7c:0f:15:fe:c8:c6:4d:99:37:ab:09:0c:
+ da:ba:b6:0e:fa:5e:bb:4b:ce:04:21:06:09:a9:2c:27:86:76:
+ cc:ee:73:6f
+*/
+static char notrust_ca_data[] = "-----BEGIN CERTIFICATE-----\n"
+ "MIIEqjCCA5KgAwIBAgIJAP3UMghSlH9PMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD\n"
+ "VQQGEwJKUDEOMAwGA1UECAwFVG9reW8xEDAOBgNVBAcMB0tvZ2FuZWkxDTALBgNV\n"
+ "BAoMBFdJREUxDzANBgNVBAsMBkFBQSBXRzEfMB0GA1UEAwwWY2hhdnJvdXguY293\n"
+ "YWRkaWN0Lm9yZzEiMCAGCSqGSIb3DQEJARYTc2RlY3VnaXNAbmljdC5nby5qcDAe\n"
+ "Fw0wOTEwMjgwODAzNDRaFw0xOTEwMjYwODAzNDRaMIGUMQswCQYDVQQGEwJKUDEO\n"
+ "MAwGA1UECAwFVG9reW8xEDAOBgNVBAcMB0tvZ2FuZWkxDTALBgNVBAoMBFdJREUx\n"
+ "DzANBgNVBAsMBkFBQSBXRzEfMB0GA1UEAwwWY2hhdnJvdXguY293YWRkaWN0Lm9y\n"
+ "ZzEiMCAGCSqGSIb3DQEJARYTc2RlY3VnaXNAbmljdC5nby5qcDCCASIwDQYJKoZI\n"
+ "hvcNAQEBBQADggEPADCCAQoCggEBALKW9iSUggF5mbvYe1Xk128Csfiijx+fwH5y\n"
+ "ZqWrHNt0YG/tZSwyCDMWBLXTeuYsntg5y0mcpsrN8v02tvrPiCzDfRPyz3mG68us\n"
+ "DPEEgQ1kqL2Gsti2DUcsdyZcDM+4rgsWRivgOTVyoNimv5f+xgmPYoElkgelLwZK\n"
+ "WxGt1VCebOxP3qZA3hSHWE1hJgL4svful7RD1PbwPzidxJKITyAiJoPKWQA9cjSa\n"
+ "gVzRQ7S4vmYALJn7xe+dMFRcfAK8RMv7/gJF6Rw7zufW0DIZK98KZs6aL0lmMPVk\n"
+ "f31N2uvndf+cjy0n4luwEoXY+TeJZY205lbwHrzR0rH75FSm0RsCAwEAAaOB/DCB\n"
+ "+TAdBgNVHQ4EFgQUUsWkY7jbrPKSNC9yVnHIEY525t8wgckGA1UdIwSBwTCBvoAU\n"
+ "UsWkY7jbrPKSNC9yVnHIEY525t+hgZqkgZcwgZQxCzAJBgNVBAYTAkpQMQ4wDAYD\n"
+ "VQQIDAVUb2t5bzEQMA4GA1UEBwwHS29nYW5laTENMAsGA1UECgwEV0lERTEPMA0G\n"
+ "A1UECwwGQUFBIFdHMR8wHQYDVQQDDBZjaGF2cm91eC5jb3dhZGRpY3Qub3JnMSIw\n"
+ "IAYJKoZIhvcNAQkBFhNzZGVjdWdpc0BuaWN0LmdvLmpwggkA/dQyCFKUf08wDAYD\n"
+ "VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEACANo6IR3OQlQaXHJaprVVDvl\n"
+ "oMJC0FRbVCK503sbmWTJL98UqxRdsTZNIL07gXlK0oUKyiNijIXiLG8d5IlUrDxF\n"
+ "H/Vsu6s8k3/PpAUVeiO2oygWqvU5NGvt0jg54MrOJKhYYPWrzbmHty+cAXyoNzOR\n"
+ "+W5RX6HRQgxvZWQq2Ok46VX622R1nNjFmCBYT7I7/gWG+hkbIAoH6d9sULLjpC+B\n"
+ "bI+L/N7ac9/Og8pGIgpUI60Gn5zO93+E+Nhg+1BlcDHGnQD6vFNs8LYp5CCX/Zj1\n"
+ "tWFVXZnx58odaU3M4t9/ZQnkZdx9YJIroETbN0PoqlnSagBjgUvbWwn4YCotCA==\n"
+ "-----END CERTIFICATE-----\n";
+
+static char notrust_cert_data[]="-----BEGIN CERTIFICATE-----\n"
+ "MIIDhjCCAm6gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlDELMAkGA1UEBhMCSlAx\n"
+ "DjAMBgNVBAgMBVRva3lvMRAwDgYDVQQHDAdLb2dhbmVpMQ0wCwYDVQQKDARXSURF\n"
+ "MQ8wDQYDVQQLDAZBQUEgV0cxHzAdBgNVBAMMFmNoYXZyb3V4LmNvd2FkZGljdC5v\n"
+ "cmcxIjAgBgkqhkiG9w0BCQEWE3NkZWN1Z2lzQG5pY3QuZ28uanAwHhcNMDkxMDI4\n"
+ "MDgwNDQwWhcNMTAxMDI4MDgwNDQwWjB/MQswCQYDVQQGEwJKUDEOMAwGA1UECAwF\n"
+ "VG9reW8xEDAOBgNVBAcMB0tvZ2FuZWkxDTALBgNVBAoMBFdJREUxDzANBgNVBAsM\n"
+ "BkFBQSBXRzETMBEGA1UEAwwKdW5rbm93bi5jczEZMBcGCSqGSIb3DQEJARYKdW5r\n"
+ "bm93bkBjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5jrVihTIFdDwXAPD\n"
+ "rzNRLBe3ZaxF6EgtrnD9fHk6x4DIUFPQGdg6JqgWTUwEFwnfaZtZK4nI4GC7HTeC\n"
+ "0j8XOcmPXXbhD24Imo8WSuqDhvm9FRRWaId5BflfZhG9IkYmZL5XFlFmQVCs8rHK\n"
+ "0DgRS0yy7iU2btO5Y3LEhIIcKycCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n"
+ "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n"
+ "FLpandKwS3LWHwARC7V7Wd8IOIG+MB8GA1UdIwQYMBaAFFLFpGO426zykjQvclZx\n"
+ "yBGOdubfMA0GCSqGSIb3DQEBBQUAA4IBAQCQjzu946HKapKm/fBkrkaDMjVhgFeL\n"
+ "MBJwAuFR2YfIr9lLuW2/q4ZfGR/cr4Rnvzy/M/N8xoF75OkmHbzWjKtylH+FM5XZ\n"
+ "JOz9e9L9UD7lYU91Ua7GSuzfz6pzpQj385pAZkjwjptDsTDz48g2P2g2ahyqFkBJ\n"
+ "tHOacfEXbAvT4ae3QN4sPDZ81N3WlMnXX/WuNVbozGWcuz3oesoO7XgDQcv9gIHe\n"
+ "+d6yFEuBJDbeKcEGEYaMqbAMx1fPeac6hAwn3IZty0QtJtx++xfWsj0xA9Pxq12R\n"
+ "XZTklIhwlrN8DxX+yMZNmTerCQzaurYO+l67S84EIQYJqSwnhnbM7nNv\n"
+ "-----END CERTIFICATE-----\n";
+static char notrust_priv_data[]="-----BEGIN RSA PRIVATE KEY-----\n"
+ "MIICXQIBAAKBgQDmOtWKFMgV0PBcA8OvM1EsF7dlrEXoSC2ucP18eTrHgMhQU9AZ\n"
+ "2DomqBZNTAQXCd9pm1kricjgYLsdN4LSPxc5yY9dduEPbgiajxZK6oOG+b0VFFZo\n"
+ "h3kF+V9mEb0iRiZkvlcWUWZBUKzyscrQOBFLTLLuJTZu07ljcsSEghwrJwIDAQAB\n"
+ "AoGAeRec1SGVE5Rvt5XrSK0vFofq2DlCE6hTDpszWFLTDbe4pDdRDybhfw+Nm15O\n"
+ "EGgK8BrbTcEMvKdkAzv9POQeLDE8JImgesHZFxN3jnkK+b762BGRDt57DzvMJsfj\n"
+ "1LBle+UBnZB1CvjrINvu+tNMVPlUpjIstbpMq0D+s01+ijECQQD8MHTv/M+Uc86u\n"
+ "1SFywgs+eQPQ8g0OoTLxzqo6YhW8FtwLjoRCZx2TNQS5gYBuQrixd/yE0Spfv9aS\n"
+ "UtlAaOc1AkEA6bVufggHVHcgiWqS8CHzb6g/GRxQixVshOsoVLMkCSz04zlwIfXF\n"
+ "c03hh5RJVv7jmuBmhHbayujMgvinw75oawJAQb9oXUDt5Wgj1FTgeYi5YbovEoRo\n"
+ "fw3ruDsHCl2UCQt0ptarCJzVixFhf/ORRi3C9RGxFfdqMrhS+qb62N4AmQJBALYU\n"
+ "T1BLiwJoiWXmLTJ/EP0V9Irov2uMtm5cE6DhrJqlduksz8r1gu7RZ3tMsVLg5Iy+\n"
+ "dcCQJOffNa54caQUTZ8CQQDTs/70Nr6F6ktrtmtU/S7lIitpQJCu9u/SPyBYPmFZ\n"
+ "9Axy6Ee66Php+eWDNP4Ln4axrapD0732wD8DcmGDVHij\n"
+ "-----END RSA PRIVATE KEY-----\n";
+
+/* Diffie-Hellman parameters, generated with GNUTLS certtool:
+certtool --generate-dh-params
+ Generator: 06
+
+ Prime: ea:c3:75:0b:32:cf:d9:17:98:5c:da:d1
+ e0:1d:b9:7c:be:29:60:b0:6f:68:a9:f6
+ 8d:75:05:59:69:04:ae:39:7c:2b:74:04
+ 3c:e2:da:28:8a:9b:93:aa:67:05:a7:3e
+ 06:3e:0d:31:63:88:55:ad:5a:bd:41:22
+ b7:58:a7:45:b3:d5:03:ad:de:3c:8d:69
+ 42:bf:84:3d:c1:90:e7:39:6a:4b:87:01
+ 19:e5:f3:a4:e5:8e:e2:45:d5:0c:6b:17
+ 22:2b:2e:50:83:91:0c:5b:82:fc:72:27
+ 49:3b:9f:29:11:53:c7:90:b8:8d:87:73
+ 1a:7b:05:ab:cb:30:59:16:71:30:60:1b
+ 4c:80:15:3a:a2:d3:47:b7:4a:61:de:64
+ 7e:79:de:88:53:b7:7a:c6:a2:9a:bb:55
+ 40:2d:7a:71:c7:41:b5:29:df:d7:5c:fb
+ 42:e4:d8:5e:0b:99:d3:3c:93:0f:33:51
+ 8b:f4:60:e4:c5:b5:58:21:c0:51:c4:43
+ 25:7c:37:fe:5c:d3:62:6c:2a:af:a7:2a
+ 82:d5:d3:e2:bb:5d:ad:84:15:f6:78:d9
+ d5:a8:f7:f0:48:5c:8d:e0:3d:04:ac:cf
+ aa:34:3f:5d:f2:0d:3d:ee:ec:b8:d8:e8
+ ad:dc:d3:40:59:a0:fd:45:62:47:63:c0
+ bd:f5:df:8b
+*/
+static char dh_params_data[] = "-----BEGIN DH PARAMETERS-----\n"
+ "MIIBCAKCAQEA6sN1CzLP2ReYXNrR4B25fL4pYLBvaKn2jXUFWWkErjl8K3QEPOLa\n"
+ "KIqbk6pnBac+Bj4NMWOIVa1avUEit1inRbPVA63ePI1pQr+EPcGQ5zlqS4cBGeXz\n"
+ "pOWO4kXVDGsXIisuUIORDFuC/HInSTufKRFTx5C4jYdzGnsFq8swWRZxMGAbTIAV\n"
+ "OqLTR7dKYd5kfnneiFO3esaimrtVQC16ccdBtSnf11z7QuTYXguZ0zyTDzNRi/Rg\n"
+ "5MW1WCHAUcRDJXw3/lzTYmwqr6cqgtXT4rtdrYQV9njZ1aj38EhcjeA9BKzPqjQ/\n"
+ "XfINPe7suNjordzTQFmg/UViR2PAvfXfiwIBBg==\n"
+ "-----END DH PARAMETERS-----\n";
+
+
+/* List server endpoints */
+static struct fd_list eps = FD_LIST_INITIALIZER(eps);
+
+/* Pass parameters to the connect thread */
+struct connect_flags {
+ int proto;
+ int expect_failure; /* 0 or 1 */
+};
+
+/* Client's side of the connection established from a separate thread */
+static void * connect_thr(void * arg)
+{
+ struct connect_flags * cf = arg;
+ struct cnxctx * cnx = NULL;
+
+ fd_log_threadname ( "testcnx:connect" );
+
+ /* Connect to the server */
+ switch (cf->proto) {
+ case IPPROTO_TCP:
+ {
+ struct fd_endpoint * ep = (struct fd_endpoint *)(eps.next);
+ cnx = fd_cnx_cli_connect_tcp( &ep->sa, sSAlen(&ep->ss) );
+ CHECK( 1, (cnx ? 1 : 0) ^ cf->expect_failure );
+ }
+ break;
+#ifndef DISABLE_SCTP
+ case IPPROTO_SCTP:
+ {
+ cnx = fd_cnx_cli_connect_sctp(0, TEST_PORT, &eps);
+ CHECK( 1, (cnx ? 1 : 0) ^ cf->expect_failure );
+ }
+ break;
+#endif /* DISABLE_SCTP */
+ default:
+ CHECK( 0, 1 );
+ }
+
+ /* exit */
+ return cnx;
+}
+
+/* Parameters to the handshake thread */
+struct handshake_flags {
+ struct cnxctx * cnx;
+ gnutls_certificate_credentials_t creds;
+ int algo;
+ int ret;
+};
+
+/* Handshake the client's side */
+static void * handshake_thr(void * arg)
+{
+ struct handshake_flags * hf = arg;
+ fd_log_threadname ( "testcnx:handshake" );
+ hf->ret = fd_cnx_handshake(hf->cnx, GNUTLS_CLIENT, hf->algo, NULL, hf->creds);
+ return NULL;
+}
+
+/* Terminate the client's connection side */
+static void * destroy_thr(void * arg)
+{
+ struct cnxctx * cnx = arg;
+ fd_log_threadname ( "testcnx:destroy" );
+ fd_cnx_destroy(cnx);
+ return NULL;
+}
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+ gnutls_datum_t ca = { (uint8_t *)ca_data, sizeof(ca_data) };
+ gnutls_datum_t server_cert = { (uint8_t *)server_cert_data, sizeof(server_cert_data) };
+ gnutls_datum_t server_priv = { (uint8_t *)server_priv_data, sizeof(server_priv_data) };
+ gnutls_datum_t client_cert = { (uint8_t *)client_cert_data, sizeof(client_cert_data) };
+ gnutls_datum_t client_priv = { (uint8_t *)client_priv_data, sizeof(client_priv_data) };
+ gnutls_datum_t expired_cert = { (uint8_t *)expired_cert_data, sizeof(expired_cert_data) };
+ gnutls_datum_t expired_priv = { (uint8_t *)expired_priv_data, sizeof(expired_priv_data) };
+ gnutls_datum_t notrust_ca = { (uint8_t *)notrust_ca_data, sizeof(notrust_ca_data) };
+ gnutls_datum_t notrust_cert = { (uint8_t *)notrust_cert_data, sizeof(notrust_cert_data) };
+ gnutls_datum_t notrust_priv = { (uint8_t *)notrust_priv_data, sizeof(notrust_priv_data) };
+ gnutls_datum_t dh_params = { (uint8_t *)dh_params_data, sizeof(dh_params_data) };
+
+ /* Listening socket, server side */
+ struct cnxctx * listener;
+#ifndef DISABLE_SCTP
+ struct cnxctx * listener_sctp;
+#endif /* DISABLE_SCTP */
+
+ /* Server & client connected sockets */
+ struct cnxctx * server_side;
+ struct cnxctx * client_side;
+
+ pthread_t thr;
+ int ret, i;
+ uint8_t * cer_buf;
+ size_t cer_sz;
+ uint8_t * rcv_buf;
+ size_t rcv_sz;
+
+ /* First, initialize the daemon modules */
+ INIT_FD();
+
+ /* Restrain the # of streams */
+ fd_g_config->cnf_sctp_str = NB_STREAMS;
+
+ /* Set the CA parameter in the config */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( fd_g_config->cnf_sec_data.credentials,
+ &ca,
+ GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+
+ #ifdef GNUTLS_VERSION_300
+ {
+ /* We import these CA in the trust list */
+ gnutls_x509_crt_t * calist;
+ unsigned int cacount = 0;
+
+ CHECK_GNUTLS_DO( ret = gnutls_x509_crt_list_import2(&calist, &cacount, &ca, GNUTLS_X509_FMT_PEM,
+ GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED), );
+ CHECK( 1, cacount );
+
+ CHECK_GNUTLS_DO( ret = gnutls_x509_trust_list_add_cas (fd_g_config->cnf_sec_data.trustlist, calist, cacount, 0), );
+ CHECK( 1, ret );
+ }
+
+ /* Use certificate verification during the handshake */
+ gnutls_certificate_set_verify_function (fd_g_config->cnf_sec_data.credentials, fd_tls_verify_credentials_2);
+
+ #endif /* GNUTLS_VERSION_300 */
+
+
+ /* Set the server credentials (in config) */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( fd_g_config->cnf_sec_data.credentials,
+ &server_cert,
+ &server_priv,
+ GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Set the default priority */
+ CHECK_GNUTLS_DO( ret = gnutls_priority_init( &fd_g_config->cnf_sec_data.prio_cache, GNUTLS_DEFAULT_PRIORITY, NULL), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Set default DH params */
+ CHECK_GNUTLS_DO( ret = gnutls_dh_params_import_pkcs3( fd_g_config->cnf_sec_data.dh_cache, &dh_params, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+
+ /* Initialize the server address (this should give a safe loopback address + port, even on non-standard configs) */
+ {
+ struct addrinfo hints, *ai, *aip;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_NUMERICSERV;
+ hints.ai_family = AF_INET;
+ CHECK( 0, getaddrinfo("localhost", _stringize(TEST_PORT), &hints, &ai) );
+ aip = ai;
+ while (aip) {
+ CHECK( 0, fd_ep_add_merge( &eps, aip->ai_addr, aip->ai_addrlen, EP_FL_DISC | EP_ACCEPTALL ));
+ aip = aip->ai_next;
+ };
+ freeaddrinfo(ai);
+
+ CHECK( 0, FD_IS_LIST_EMPTY(&eps) ? 1 : 0 );
+ }
+
+ /* Start the server(s) */
+ {
+ /* TCP server */
+ listener = fd_cnx_serv_tcp(TEST_PORT, 0, (struct fd_endpoint *)(eps.next));
+ CHECK( 1, listener ? 1 : 0 );
+
+ /* Accept incoming clients */
+ CHECK( 0, fd_cnx_serv_listen(listener));
+
+#ifndef DISABLE_SCTP
+ /* SCTP server */
+ listener_sctp = fd_cnx_serv_sctp(TEST_PORT, &eps);
+ CHECK( 1, listener_sctp ? 1 : 0 );
+
+ /* Accept incoming clients */
+ CHECK( 0, fd_cnx_serv_listen(listener_sctp));
+#endif /* DISABLE_SCTP */
+
+ }
+
+ /* Initialize the CER message */
+ {
+ struct msg * cer;
+ struct dict_object * model = NULL;
+ struct avp * oh;
+ union avp_value value;
+
+ /* Find the CER dictionary object */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &model, ENOENT ) );
+
+ /* Create the instance */
+ CHECK( 0, fd_msg_new ( model, 0, &cer ) );
+
+ /* Now find the Origin-Host dictionary object */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &model, ENOENT ) );
+
+ /* Create the instance */
+ CHECK( 0, fd_msg_avp_new ( model, 0, &oh ) );
+ value.os.data = (uint8_t *)"Client.side";
+ value.os.len = strlen((char *)value.os.data);
+ CHECK( 0, fd_msg_avp_setvalue ( oh, &value ) );
+
+ /* Add the AVP */
+ CHECK( 0, fd_msg_avp_add( cer, MSG_BRW_LAST_CHILD, oh) );
+
+ #if 0
+ /* For debug: dump the object */
+ fd_log_debug("Dumping CER");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, cer, fd_g_config->cnf_dict, 0, 1));
+ #endif
+
+ CHECK( 0, fd_msg_bufferize( cer, &cer_buf, &cer_sz ) );
+ CHECK( 0, fd_msg_free(cer) );
+ }
+
+ /* Simple TCP client / server test (no TLS) */
+ {
+ struct connect_flags cf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_TCP;
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener);
+ CHECK( 1, server_side ? 1 : 0 );
+ CHECK( 0, fd_cnx_start_clear(server_side, 0) );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ CHECK( 0, fd_cnx_start_clear(client_side, 0) );
+
+ /* Send a message and receive it */
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ /* Do it in the other direction */
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ /* Now close the connections */
+ fd_cnx_destroy(client_side);
+ fd_cnx_destroy(server_side);
+ }
+
+#ifndef DISABLE_SCTP
+ /* Simple SCTP client / server test (no TLS) */
+ {
+ struct connect_flags cf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_SCTP;
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener_sctp);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+
+ CHECK( 0, fd_cnx_start_clear(server_side, 1) );
+
+ /* Send a message and receive it */
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( EINVAL, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( 0, fd_cnx_start_clear(client_side, 0) );
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ /* Do it in the other direction */
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ /* Do it one more time to use another stream */
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ /* Now close the connection */
+ fd_cnx_destroy(client_side);
+ fd_cnx_destroy(server_side);
+ }
+#endif /* DISABLE_SCTP */
+
+ /* TCP Client / server emulating old Diameter behavior (handshake after 1 message exchange) */
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_TCP;
+
+ memset(&hf, 0, sizeof(hf));
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* In legacy Diameter, we exchange first one message (CER / CEA) */
+
+ CHECK( 0, fd_cnx_start_clear(server_side, 0) );
+ CHECK( 0, fd_cnx_start_clear(client_side, 0) );
+
+ /* Send a message and receive it */
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ /* And the supposed reply */
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ /* At this point in legacy Diameter we start the handshake */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT /* No impact on TCP */, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Send a few TLS protected message, and replies */
+ for (i = 0; i < 2 * NB_STREAMS; i++) {
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+ }
+
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+
+#ifndef DISABLE_SCTP
+ /* SCTP Client / server emulating old Diameter behavior (handshake after 1 message exchange) */
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_SCTP;
+
+ memset(&hf, 0, sizeof(hf));
+ hf.algo = ALGO_HANDSHAKE_3436; /* this is mandatory for old TLS mechanism */
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener_sctp);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* In legacy Diameter, we exchange first one message (CER / CEA) */
+
+ CHECK( 0, fd_cnx_start_clear(server_side, 0) );
+ CHECK( 0, fd_cnx_start_clear(client_side, 0) );
+
+ /* Send a message and receive it */
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ /* And the supposed reply */
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ /* At this point in legacy Diameter we start the handshake */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Send a few TLS protected message, and replies */
+ for (i = 0; i < 2 * NB_STREAMS; i++) {
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+ }
+
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+#endif /* DISABLE_SCTP */
+
+ /* TCP Client / server emulating new Diameter behavior (handshake at connection directly) */
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_TCP;
+
+ memset(&hf, 0, sizeof(hf));
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake directly */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Send a few TLS protected message, and replies */
+ for (i = 0; i < 2 * NB_STREAMS; i++) {
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+ }
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+
+#ifndef DISABLE_SCTP
+
+
+ /* SCTP Client / server emulating new Diameter behavior (DTLS handshake at connection directly) */
+ TODO("Enabled after DTLS implementation");
+ if (0)
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_SCTP;
+
+ memset(&hf, 0, sizeof(hf));
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener_sctp);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake directly */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Send a few TLS protected messages, and replies */
+ for (i = 0; i < 2 * NB_STREAMS; i++) {
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+ }
+
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+
+ /* SCTP Client / server emulating old intermediary Diameter behavior (TLS handshake at connection directly) */
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_SCTP;
+
+ memset(&hf, 0, sizeof(hf));
+ hf.algo = ALGO_HANDSHAKE_3436; /* this is mandatory for old TLS mechanism */
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener_sctp);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake directly */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Send a few TLS protected messages, and replies */
+ for (i = 0; i < 2 * NB_STREAMS; i++) {
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+ }
+
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+#endif /* DISABLE_SCTP */
+
+ /* Test with different number of streams between server and client */
+#ifndef DISABLE_SCTP
+ /* DTLS / SCTP style */
+ TODO("Enabled after DTLS implementation");
+ if (0)
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_SCTP;
+
+ memset(&hf, 0, sizeof(hf));
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread with more streams than the server */
+ fd_g_config->cnf_sctp_str = 2 * NB_STREAMS;
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener_sctp);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake directly */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Send a few TLS protected message, and replies */
+ for (i = 0; i < 4 * NB_STREAMS; i++) {
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+ }
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Do the same test but with more streams on the server this time */
+ fd_g_config->cnf_sctp_str = NB_STREAMS / 2;
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener_sctp);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake directly */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Send a few TLS protected message, and replies */
+ for (i = 0; i < 2 * NB_STREAMS; i++) {
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+ }
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+
+ /* TLS / SCTP style */
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_SCTP;
+
+ memset(&hf, 0, sizeof(hf));
+ hf.algo = ALGO_HANDSHAKE_3436;
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread with more streams than the server */
+ fd_g_config->cnf_sctp_str = 2 * NB_STREAMS;
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener_sctp);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake directly */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Send a few TLS protected message, and replies */
+ for (i = 0; i < 4 * NB_STREAMS; i++) {
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+ }
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Do the same test but with more streams on the server this time */
+ fd_g_config->cnf_sctp_str = NB_STREAMS / 2;
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener_sctp);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake directly */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Send a few TLS protected message, and replies */
+ for (i = 0; i < 2 * NB_STREAMS; i++) {
+ CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+ CHECK( cer_sz, rcv_sz );
+ CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+ free(rcv_buf);
+ }
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+
+#endif /* DISABLE_SCTP */
+
+
+ /* Basic operation tested successfully, now test we detect error conditions */
+
+ /* Untrusted certificate, TCP */
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_TCP;
+
+ memset(&hf, 0, sizeof(hf));
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, ¬rust_ca, GNUTLS_X509_FMT_PEM), );
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, ¬rust_cert, ¬rust_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake directly */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+ fd_cnx_destroy(server_side);
+
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+
+ /* Same in SCTP */
+#ifndef DISABLE_SCTP
+ /* DTLS */
+ TODO("Enabled after DTLS implementation");
+ if (0)
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_SCTP;
+
+ memset(&hf, 0, sizeof(hf));
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, ¬rust_ca, GNUTLS_X509_FMT_PEM), );
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, ¬rust_cert, ¬rust_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener_sctp);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake directly */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+
+ /* TLS */
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_SCTP;
+
+ memset(&hf, 0, sizeof(hf));
+ hf.algo = ALGO_HANDSHAKE_3436;
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, ¬rust_ca, GNUTLS_X509_FMT_PEM), );
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, ¬rust_cert, ¬rust_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener_sctp);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake directly */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+#endif /* DISABLE_SCTP */
+
+ /* Expired certificate */
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_TCP;
+
+ memset(&hf, 0, sizeof(hf));
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &expired_cert, &expired_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake directly */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+
+ /* Non matching hostname */
+
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_TCP;
+
+ memset(&hf, 0, sizeof(hf));
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Set the correct hostname we expect from the client (in the server) */
+ fd_cnx_sethostname(server_side, "client.test");
+
+ /* Start the handshake, check it is successful */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Do it again with an invalid hostname */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Set the correct hostname we expect from the client (in the server) */
+ fd_cnx_sethostname(server_side, "nomatch.test");
+
+ /* Start the handshake, check it is successful */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+
+ /* Test the other functions of the module */
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+ char * str;
+ const gnutls_datum_t *cert_list;
+ unsigned int cert_list_size;
+ struct fifo * myfifo = NULL;
+ struct timespec now;
+ int ev_code;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_TCP;
+
+ memset(&hf, 0, sizeof(hf));
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Test some simple functions */
+
+ /* fd_cnx_getid */
+ str = fd_cnx_getid(server_side);
+ CHECK( 1, str ? 1 : 0 );
+ CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+
+ /* fd_cnx_getproto */
+ i = fd_cnx_getproto(server_side);
+ CHECK( IPPROTO_TCP, i);
+
+ /* fd_cnx_getTLS */
+ i = fd_cnx_getTLS(server_side);
+ CHECK( 1, i ? 1 : 0 );
+
+ /* fd_cnx_getcred */
+ CHECK( 0, fd_cnx_getcred(server_side, &cert_list, &cert_list_size) );
+ CHECK( 1, (cert_list_size > 0) ? 1 : 0 );
+ /* We could also verify that the cert_list really contains the client_cert and ca certificates */
+
+ /* fd_cnx_getremoteid */
+ str = fd_cnx_getremoteid(server_side);
+ CHECK( 1, str ? 1 : 0 );
+ CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+
+ /* fd_cnx_recv_setaltfifo */
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_fifo_new(&myfifo, 0) );
+ CHECK( 0, fd_cnx_recv_setaltfifo(server_side, myfifo) );
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &now) );
+ do {
+ CHECK( 0, fd_event_timedget(myfifo, &now, ETIMEDOUT, &ev_code, NULL, (void *)&rcv_buf) );
+ free(rcv_buf);
+ } while (ev_code != FDEVP_CNX_MSG_RECV);
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ fd_event_destroy(&myfifo, free);
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+
+#ifndef DISABLE_SCTP
+ /* And re-test with a SCTP connection */
+ TODO("Enabled after DTLS implementation");
+ if (0)
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+ char * str;
+ const gnutls_datum_t *cert_list;
+ unsigned int cert_list_size;
+ struct fifo * myfifo = NULL;
+ struct timespec now;
+ int ev_code;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_SCTP;
+
+ memset(&hf, 0, sizeof(hf));
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener_sctp);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Test some simple functions */
+
+ /* fd_cnx_getid */
+ str = fd_cnx_getid(server_side);
+ CHECK( 1, str ? 1 : 0 );
+ CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+
+ /* fd_cnx_getproto */
+ i = fd_cnx_getproto(server_side);
+ CHECK( IPPROTO_SCTP, i);
+
+ /* fd_cnx_getTLS */
+ i = fd_cnx_getTLS(server_side);
+ CHECK( 1, i ? 1 : 0 );
+
+ /* fd_cnx_getcred */
+ CHECK( 0, fd_cnx_getcred(server_side, &cert_list, &cert_list_size) );
+ CHECK( 1, (cert_list_size > 0) ? 1 : 0 );
+ /* We could also verify that the cert_list really contains the client_cert and ca certificates */
+
+ /* fd_cnx_getremoteid */
+ str = fd_cnx_getremoteid(server_side);
+ CHECK( 1, str ? 1 : 0 );
+ CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+
+ /* fd_cnx_recv_setaltfifo */
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_fifo_new(&myfifo, 0) );
+ CHECK( 0, fd_cnx_recv_setaltfifo(server_side, myfifo) );
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &now) );
+ do {
+ CHECK( 0, fd_event_timedget(myfifo, &now, ETIMEDOUT, &ev_code, NULL, (void *)&rcv_buf) );
+ free(rcv_buf);
+ } while (ev_code != FDEVP_CNX_MSG_RECV);
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ fd_event_destroy(&myfifo, free);
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+
+ /* TLS */
+ {
+ struct connect_flags cf;
+ struct handshake_flags hf;
+ char * str;
+ const gnutls_datum_t *cert_list;
+ unsigned int cert_list_size;
+ struct fifo * myfifo = NULL;
+ struct timespec now;
+ int ev_code;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_SCTP;
+
+ memset(&hf, 0, sizeof(hf));
+ hf.algo = ALGO_HANDSHAKE_3436;
+
+ /* Initialize remote certificate */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+ /* Set the CA */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+ CHECK( 1, ret );
+ /* Set the key */
+ CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+ CHECK( GNUTLS_E_SUCCESS, ret );
+
+ /* Start the client thread */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+ /* Accept the connection of the client */
+ server_side = fd_cnx_serv_accept(listener_sctp);
+ CHECK( 1, server_side ? 1 : 0 );
+
+ /* Retrieve the client connection object */
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 1, client_side ? 1 : 0 );
+ hf.cnx = client_side;
+
+ /* Start the handshake */
+ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+ CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+ CHECK( 0, pthread_join(thr, NULL) );
+ CHECK( 0, hf.ret );
+
+ /* Test some simple functions */
+
+ /* fd_cnx_getid */
+ str = fd_cnx_getid(server_side);
+ CHECK( 1, str ? 1 : 0 );
+ CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+
+ /* fd_cnx_getproto */
+ i = fd_cnx_getproto(server_side);
+ CHECK( IPPROTO_SCTP, i);
+
+ /* fd_cnx_getTLS */
+ i = fd_cnx_getTLS(server_side);
+ CHECK( 1, i ? 1 : 0 );
+
+ /* fd_cnx_getcred */
+ CHECK( 0, fd_cnx_getcred(server_side, &cert_list, &cert_list_size) );
+ CHECK( 1, (cert_list_size > 0) ? 1 : 0 );
+ /* We could also verify that the cert_list really contains the client_cert and ca certificates */
+
+ /* fd_cnx_getremoteid */
+ str = fd_cnx_getremoteid(server_side);
+ CHECK( 1, str ? 1 : 0 );
+ CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+
+ /* fd_cnx_recv_setaltfifo */
+ CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+ CHECK( 0, fd_fifo_new(&myfifo, 0) );
+ CHECK( 0, fd_cnx_recv_setaltfifo(server_side, myfifo) );
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &now) );
+ do {
+ CHECK( 0, fd_event_timedget(myfifo, &now, ETIMEDOUT, &ev_code, NULL, (void *)&rcv_buf) );
+ free(rcv_buf);
+ } while (ev_code != FDEVP_CNX_MSG_RECV);
+
+ /* Now close the connection */
+ CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+ fd_cnx_destroy(server_side);
+ CHECK( 0, pthread_join(thr, NULL) );
+
+ fd_event_destroy(&myfifo, free);
+
+ /* Free the credentials */
+ gnutls_certificate_free_keys(hf.creds);
+ gnutls_certificate_free_cas(hf.creds);
+ gnutls_certificate_free_credentials(hf.creds);
+ }
+#endif /* DISABLE_SCTP */
+
+
+ /* Destroy the servers */
+ {
+ fd_cnx_destroy(listener);
+#ifndef DISABLE_SCTP
+ fd_cnx_destroy(listener_sctp);
+#endif /* DISABLE_SCTP */
+ }
+
+ /* Check that connection attempt fails then */
+ {
+ struct connect_flags cf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_TCP;
+ cf.expect_failure = 1;
+
+ /* Start the client thread, that should fail */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 0, client_side ? 1 : 0 );
+ }
+
+#ifndef DISABLE_SCTP
+ {
+ struct connect_flags cf;
+
+ memset(&cf, 0, sizeof(cf));
+ cf.proto = IPPROTO_SCTP;
+ cf.expect_failure = 1;
+
+ /* Start the client thread, that should fail */
+ CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+ CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+ CHECK( 0, client_side ? 1 : 0 );
+ }
+#endif /* DISABLE_SCTP */
+
+
+ /* That's all for the tests yet */
+ PASSTEST();
+}
+
diff --git a/tests/testdict.c b/tests/testdict.c
new file mode 100644
index 0000000..d45d52e
--- /dev/null
+++ b/tests/testdict.c
@@ -0,0 +1,211 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 "tests.h"
+
+/* Test for the dict_iterate_rules function */
+int iter_test(void * data, struct dict_rule_data * rule)
+{
+ struct dict_avp_data avpdata;
+ (*(int *)data)++;
+
+ CHECK( 0, fd_dict_getval ( rule->rule_avp, &avpdata ) );
+ TRACE_DEBUG(FULL, "rule #%d: avp '%s'", *(int *)data, avpdata.avp_name);
+ return 0;
+}
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+ /* First, initialize the daemon modules */
+ INIT_FD();
+
+ /* Test creating and searching all types of objects */
+ {
+ struct dict_object * obj1 = NULL;
+ struct dict_object * obj2 = NULL;
+ struct dict_object * obj3 = NULL;
+
+ vendor_id_t vendor_id = 735671;
+ struct dict_vendor_data vendor1_data = { 735671, "Vendor test 1" };
+ struct dict_vendor_data vendor2_data = { 735672, "Vendor test 2" };
+ struct dict_application_data app1_data = { 735674, "Application test 1" };
+
+
+ /* Create two vendors */
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_VENDOR, &vendor1_data , NULL, &obj1 ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_VENDOR, &vendor2_data , NULL, NULL ) );
+
+ /* Check we always retrieve the correct vendor object */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, &vendor_id, &obj2, ENOENT ) );
+ CHECK( obj1, obj2);
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_NAME, "Vendor test 1", &obj2, ENOENT ) );
+ CHECK( obj1, obj2);
+
+ /* Check the error conditions */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, &vendor_id, NULL, ENOENT ) );
+
+ vendor_id = 735673; /* Not defined */
+ CHECK( ENOENT, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, &vendor_id, NULL, ENOENT ) );
+ CHECK( ENOENT, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_NAME, "Vendor test 3", NULL, ENOENT ) );
+ CHECK( ENOENT, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, &vendor_id, &obj2, ENOENT ) );
+ CHECK( ENOENT, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_NAME, "Vendor test 3", &obj2, ENOENT ) );
+ CHECK( ENOTSUP, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_NAME, "Vendor test 3", &obj2, ENOTSUP ) );
+
+ /* Check the get_* functions */
+ CHECK( 0, fd_dict_getval ( obj1, &vendor1_data ) );
+ CHECK( 735671, vendor1_data.vendor_id );
+ CHECK( 0, strcmp(vendor1_data.vendor_name, "Vendor test 1") );
+ /* error conditions */
+ CHECK( EINVAL, fd_dict_getval ( (struct dict_object *)"not an object", &vendor1_data ) );
+
+ /* Create the application with vendor1 as parent */
+ CHECK( EINVAL, fd_dict_new ( fd_g_config->cnf_dict, DICT_APPLICATION, &app1_data , (struct dict_object *)"bad object", &obj2 ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_APPLICATION, &app1_data , obj1, &obj2 ) );
+
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_OF_APPLICATION, obj2, &obj3, ENOENT ) );
+ CHECK( obj1, obj3);
+
+ /* Creating and searching the other objects is already done in dictionary initialization */
+ }
+
+ /* Test creation of the "Example-AVP" grouped AVP from the RFC */
+ {
+ int nbr = 0;
+ struct dict_object * origin_host_avp = NULL;
+ struct dict_object * session_id_avp = NULL;
+ struct dict_object * example_avp_avp = NULL;
+ struct dict_rule_data rule_data = { NULL, RULE_REQUIRED, -1, -1 };
+ struct dict_avp_data example_avp_data = { 999999, 0, "Example-AVP", AVP_FLAG_VENDOR , 0, AVP_TYPE_GROUPED };
+
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &origin_host_avp, ENOENT ) );
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &session_id_avp, ENOENT ) );
+
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &example_avp_data , NULL, &example_avp_avp ) );
+
+ rule_data.rule_avp = origin_host_avp;
+ rule_data.rule_min = 1;
+ rule_data.rule_max = 1;
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_RULE, &rule_data, example_avp_avp, NULL ) );
+
+ rule_data.rule_avp = session_id_avp;
+ rule_data.rule_min = 1;
+ rule_data.rule_max = -1;
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_RULE, &rule_data, example_avp_avp, NULL ) );
+
+ CHECK( 0, fd_dict_iterate_rules ( example_avp_avp, &nbr, iter_test) );
+ CHECK( 2, nbr );
+ }
+
+ /* Test list function */
+ {
+ struct fd_list * li = NULL;
+ struct fd_list * sentinel = NULL;
+ enum dict_object_type type;
+ struct dict_object * defvnd=NULL;
+ vendor_id_t vid = 0;
+ int first = 1;
+
+ CHECK( 0, fd_dict_getlistof(VENDOR_BY_ID, fd_g_config->cnf_dict, &sentinel));
+
+ for (li = sentinel; (li != sentinel) || (first != 0); li = li->next) {
+ first = 0;
+ CHECK(0, fd_dict_gettype(li->o, &type));
+ CHECK(DICT_VENDOR, type);
+#if 0
+ struct dict_vendor_data data;
+ CHECK( 0, fd_dict_getval(li->o, &data) );
+ printf("%d : %s\n", data.vendor_id, data.vendor_name);
+#endif
+ }
+
+ CHECK( 0, fd_dict_search(fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, &vid, &defvnd, ENOENT) );
+
+ CHECK( 0, fd_dict_getlistof(AVP_BY_NAME, defvnd, &sentinel));
+ for (li = sentinel->next; li != sentinel; li = li->next) {
+ CHECK(0, fd_dict_gettype(li->o, &type));
+ CHECK(DICT_AVP, type);
+#if 0
+ struct dict_avp_data data;
+ CHECK( 0, fd_dict_getval(li->o, &data) );
+ printf("%d : %s\n", data.avp_code, data.avp_name);
+#endif
+ }
+ }
+
+ /* Test delete function */
+ {
+ struct fd_list * li = NULL;
+ struct fd_list * sentinel = NULL;
+ struct dict_object * obj=NULL;
+ vendor_id_t vid = 0;
+ int count = 0, cntbkp;
+
+ CHECK( 0, fd_dict_search(fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, &vid, &obj, ENOENT) );
+
+ CHECK( EINVAL, fd_dict_delete(obj) );
+
+
+ CHECK( 0, fd_dict_getlistof(AVP_BY_NAME, obj, &sentinel));
+ obj = NULL;
+
+ for (li = sentinel->next; li != sentinel; li = li->next) {
+ struct dict_avp_data data;
+ CHECK( 0, fd_dict_getval(li->o, &data) );
+ count++;
+ if (data.avp_basetype != AVP_TYPE_GROUPED)
+ obj = li->o;
+ }
+
+ CHECK(1, obj ? 1 : 0 );
+#if 1
+ fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, obj));
+#endif
+ CHECK( 0, fd_dict_delete(obj) );
+ cntbkp = count;
+ count = 0;
+ for (li = sentinel->next; li != sentinel; li = li->next) {
+ count++;
+ }
+ CHECK( 1, cntbkp - count );
+
+ }
+
+ LOG_D( "Dictionary at the end of %s: %s", __FILE__, fd_dict_dump(FD_DUMP_TEST_PARAMS, fd_g_config->cnf_dict) ?: "error");
+
+ /* That's all for the tests yet */
+ PASSTEST();
+}
+
diff --git a/tests/testdisp.c b/tests/testdisp.c
new file mode 100644
index 0000000..7625a69
--- /dev/null
+++ b/tests/testdisp.c
@@ -0,0 +1,729 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 "tests.h"
+
+#define Define_cb( __int, __extra ) \
+int cb_##__int( struct msg ** msg, struct avp * avp, struct session * session, void * opaque, enum disp_action * action ) \
+{ \
+ CHECK( 1, msg ? 1 : 0 ); \
+ CHECK( 1, action ? 1 : 0 ); \
+ CHECK( sess, session ); \
+ if (opaque) { \
+ CHECK( 1, opaque == g_opaque ? 1 : 0 ); \
+ } \
+ *action = DISP_ACT_CONT; \
+ cbcalled[__int] += 1; \
+ do { \
+ __extra ; \
+ } while (0); \
+ return 0; \
+}
+
+#define NB_CB 10
+char cbcalled[NB_CB];
+struct session * sess;
+void * g_opaque = (void *)"test";
+
+/* cb_0 */ Define_cb( 0, );
+/* cb_1 */ Define_cb( 1, );
+/* cb_2 */ Define_cb( 2, );
+/* cb_3 */ Define_cb( 3, );
+/* cb_4 */ Define_cb( 4, );
+/* cb_5 */ Define_cb( 5, );
+/* cb_6 */ Define_cb( 6, return 12345 );
+/* cb_7 */ Define_cb( 7, { CHECK( 1, avp ? 1 : 0 ); } );
+/* cb_8 */ Define_cb( 8, { CHECK( 0, fd_msg_free( *msg ) ); *msg = NULL; } );
+/* cb_9 */ Define_cb( 9, *action = DISP_ACT_SEND );
+/* max: cb_<NB_CB - 1> */
+
+/* Create a new message containing what we want */
+struct msg * new_msg(int appid, struct dict_object * cmd, struct dict_object * avp1, struct dict_object * avp2, int val)
+{
+ struct msg *new;
+ struct avp *avp;
+ union avp_value value;
+ struct msg_hdr * msg_hdr = NULL;
+
+ CHECK( 0, fd_msg_new ( cmd, 0, &new ) );
+ CHECK( 0, fd_msg_hdr ( new, &msg_hdr ) );
+ msg_hdr->msg_appl = appid;
+
+ if (avp1) {
+ CHECK( 0, fd_msg_avp_new ( avp1, 0, &avp ) );
+ value.u32 = 0;
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+ CHECK( 0, fd_msg_avp_add ( new, MSG_BRW_LAST_CHILD, avp ) );
+ }
+
+ if (avp2) {
+ CHECK( 0, fd_msg_avp_new ( avp2, 0, &avp ) );
+ value.u32 = val;
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+ CHECK( 0, fd_msg_avp_add ( new, MSG_BRW_LAST_CHILD, avp ) );
+ }
+
+ return new;
+}
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+ struct dict_object * app1, * app2;
+ struct dict_object * cmd1, * cmd2;
+ struct dict_object * avp1, * avp2; /* avp2 is enumerated; they are both unsigned32 types */
+ struct dict_object * enu1, * enu2;
+ struct msg * msg = NULL, *error;
+ enum disp_action action;
+ struct disp_hdl * hdl[NB_CB];
+ struct disp_when when;
+ char * ec, *em;
+
+ /* First, initialize the daemon modules */
+ INIT_FD();
+
+ /* Create a dummy session, we don't use it anyway */
+ #define DUMMY_SID "test.disp"
+ CHECK( 0, fd_sess_new( &sess, DUMMY_SID, CONSTSTRLEN(DUMMY_SID), NULL, 0 ) );
+
+ memset(&when, 0xff, sizeof(when)); /* check that we don't use un-initialized parts */
+
+ /* Initialize dictionary objects */
+ {
+ struct dict_object * enutype;
+ struct dict_application_data app1_data = { 1, "Application test 1" };
+ struct dict_application_data app2_data = { 2, "Application test 2" };
+ struct dict_cmd_data cmd1_data = { 1, "Command test 1 (req)", CMD_FLAG_REQUEST, CMD_FLAG_REQUEST };
+ struct dict_cmd_data cmd2_data = { 1, "Command test 2 (ans)", CMD_FLAG_REQUEST, 0 };
+ struct dict_type_data type_data = { AVP_TYPE_UNSIGNED32, "Type test", NULL, NULL };
+ struct dict_avp_data avp1_data = { 10001, 0, "AVP test 1", 0, 0, AVP_TYPE_UNSIGNED32 };
+ struct dict_avp_data avp2_data = { 10002, 0, "AVP test 2", 0, 0, AVP_TYPE_UNSIGNED32 };
+ struct dict_enumval_data enu1_data = { "ENU test 1", { .u32 = 1 }};
+ struct dict_enumval_data enu2_data = { "ENU test 2", { .u32 = 2 }};
+
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_APPLICATION, &app1_data, NULL, &app1 ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_APPLICATION, &app2_data, NULL, &app2 ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd1_data, NULL, &cmd1 ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd2_data, NULL, &cmd2 ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data, NULL, &enutype ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp1_data, NULL, &avp1 ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp2_data, enutype, &avp2 ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &enu1_data, enutype, &enu1 ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &enu2_data, enutype, &enu2 ) );
+ }
+
+ /* Register first handler, very simple test */
+ {
+ CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, NULL, NULL, &hdl[0] ) );
+ CHECK( 1, hdl[0] ? 1 : 0 );
+ CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+ CHECK( NULL, hdl[0] );
+ CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, NULL, NULL, &hdl[0] ) );
+
+ /* Check this handler is called for a message */
+ msg = new_msg( 0, cmd1, avp1, NULL, 0 );
+ memset(cbcalled, 0, sizeof(cbcalled));
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( DISP_ACT_CONT, action );
+
+ /* Delete the message */
+ CHECK( 0, fd_msg_free( msg ) );
+ CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+ }
+
+ /* Handlers for applications */
+ {
+ CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) );
+ when.app = app1;
+ CHECK( 0, fd_disp_register( cb_1, DISP_HOW_APPID, &when, NULL, &hdl[1] ) );
+ when.app = app2;
+ CHECK( 0, fd_disp_register( cb_2, DISP_HOW_APPID, &when, NULL, &hdl[2] ) );
+ when.avp = avp2;
+ CHECK( 0, fd_disp_register( cb_3, DISP_HOW_APPID, &when, NULL, &hdl[3] ) );
+ when.avp = avp1;
+ CHECK( 0, fd_disp_register( cb_4, DISP_HOW_APPID, &when, NULL, &hdl[4] ) );
+
+ /* Check the callbacks are called as appropriate */
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 0, cmd1, avp1, NULL, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 0, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( 0, cbcalled[4] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd1, avp1, NULL, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( 0, cbcalled[4] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 2, cmd1, avp1, NULL, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 0, cbcalled[1] );
+ CHECK( 1, cbcalled[2] );
+ CHECK( 1, cbcalled[3] );
+ CHECK( 1, cbcalled[4] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[4], NULL ) );
+ }
+
+ /* Handlers for commands */
+ {
+ when.app = NULL;
+ when.command = NULL;
+ CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) );
+ CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_CC, &when, NULL, &hdl[1] ) );
+ when.command = cmd1;
+ CHECK( 0, fd_disp_register( cb_1, DISP_HOW_CC, &when, NULL, &hdl[1] ) ); /* cmd1 */
+ when.app = app2;
+ CHECK( 0, fd_disp_register( cb_2, DISP_HOW_CC, &when, NULL, &hdl[2] ) ); /* app2 + cmd1 */
+ when.command = cmd2;
+ when.app = NULL;
+ when.avp = avp1;
+ CHECK( 0, fd_disp_register( cb_3, DISP_HOW_CC, &when, NULL, &hdl[3] ) ); /* cmd2 (avp1 ignored) */
+
+ /* Check the callbacks are called as appropriate */
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 0, cmd1, avp1, NULL, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 2, cmd1, avp1, NULL, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 2, cmd2, avp1, NULL, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 0, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 1, cbcalled[3] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd2, NULL, avp2, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 0, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 1, cbcalled[3] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+ }
+
+ /* Handlers for AVPs */
+ {
+ when.app = NULL;
+ when.command = NULL;
+ when.avp = NULL;
+
+ CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) ); /* all */
+ CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_AVP, &when, NULL, &hdl[1] ) );
+
+ when.avp = avp1;
+ CHECK( 0, fd_disp_register( cb_1, DISP_HOW_AVP, &when, NULL, &hdl[1] ) ); /* avp1 */
+
+ when.command = cmd1;
+ CHECK( 0, fd_disp_register( cb_2, DISP_HOW_AVP, &when, NULL, &hdl[2] ) ); /* avp1 + cmd1 */
+
+ when.command = NULL;
+ when.app = app1;
+ CHECK( 0, fd_disp_register( cb_3, DISP_HOW_AVP, &when, NULL, &hdl[3] ) ); /* avp1 + app1 */
+
+ when.command = cmd1;
+ CHECK( 0, fd_disp_register( cb_4, DISP_HOW_AVP, &when, NULL, &hdl[4] ) ); /* avp1 + cmd1 + app1 */
+
+ when.app = NULL;
+ when.command = NULL;
+ when.avp = avp2;
+ when.value = enu1;
+ CHECK( 0, fd_disp_register( cb_5, DISP_HOW_AVP, &when, NULL, &hdl[5] ) ); /* avp2 */
+
+ when.value = enu2;
+ CHECK( 0, fd_disp_register( cb_7, DISP_HOW_AVP, &when, NULL, &hdl[6] ) ); /* avp2 */
+
+
+
+ /* Check the callbacks are called as appropriate */
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 0, cmd1, NULL, NULL, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 0, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( 0, cbcalled[4] );
+ CHECK( 0, cbcalled[5] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 0, cmd1, avp1, NULL, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( 0, cbcalled[4] );
+ CHECK( 0, cbcalled[5] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd2, avp1, NULL, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 1, cbcalled[3] );
+ CHECK( 0, cbcalled[4] );
+ CHECK( 0, cbcalled[5] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd1, avp1, NULL, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[2] );
+ CHECK( 1, cbcalled[3] );
+ CHECK( 1, cbcalled[4] );
+ CHECK( 0, cbcalled[5] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd1, avp1, avp2, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[2] );
+ CHECK( 1, cbcalled[3] );
+ CHECK( 1, cbcalled[4] );
+ CHECK( 1, cbcalled[5] );
+ CHECK( 1, cbcalled[7] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd1, NULL, avp2, 1 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 0, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( 0, cbcalled[4] );
+ CHECK( 1, cbcalled[5] );
+ CHECK( 1, cbcalled[7] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd1, NULL, avp2, 2 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 0, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( 0, cbcalled[4] );
+ CHECK( 1, cbcalled[5] );
+ CHECK( 1, cbcalled[7] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[4], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[5], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[6], NULL ) );
+ }
+
+ /* Handlers for enum values */
+ {
+ when.app = NULL;
+ when.command = NULL;
+ when.avp = NULL;
+ when.value = NULL;
+
+ CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) ); /* all */
+ CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[1] ) );
+ when.value = enu1;
+ CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[1] ) );
+ when.avp = avp1;
+ CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[1] ) );
+ when.avp = avp2;
+ CHECK( 0, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[1] ) ); /* avp2, enu1 */
+
+ when.command = cmd1;
+ CHECK( 0, fd_disp_register( cb_2, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[2] ) ); /* avp2, enu1 + cmd1 */
+
+ when.command = NULL;
+ when.app = app1;
+ when.value = enu2;
+ CHECK( 0, fd_disp_register( cb_3, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[3] ) ); /* avp2, enu2 + app1 */
+
+ /* Check the callbacks are called as appropriate */
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 0, cmd1, avp1, NULL, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 0, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd2, avp1, avp2, 0 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 0, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd2, avp1, avp2, 1 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd2, avp1, avp2, 2 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 0, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 1, cbcalled[3] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd1, avp1, avp2, 1 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd2, avp1, avp2, 1 );
+ {
+ struct avp *avp;
+ union avp_value value;
+ CHECK( 0, fd_msg_avp_new ( avp2, 0, &avp ) );
+ value.u32 = 2;
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+ CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp ) );
+ }
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 1, cbcalled[3] );
+ CHECK( DISP_ACT_CONT, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+ }
+
+ /* Test behavior of handlers */
+ {
+ CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) );
+ CHECK( 0, fd_disp_register( cb_1, DISP_HOW_ANY, &when, NULL, &hdl[1] ) );
+ CHECK( 0, fd_disp_register( cb_6, DISP_HOW_ANY, &when, NULL, &hdl[2] ) );
+ CHECK( 0, fd_disp_register( cb_2, DISP_HOW_ANY, &when, NULL, &hdl[3] ) );
+ CHECK( 0, fd_disp_register( cb_3, DISP_HOW_ANY, &when, NULL, &hdl[4] ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd1, avp1, avp2, 1 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[6] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( 0, msg ? 1 : 0);
+ CHECK( 1, em ? 1 : 0);
+ CHECK( 0, fd_msg_free( error ) );
+
+ CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[4], NULL ) );
+
+ CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) );
+ CHECK( 0, fd_disp_register( cb_1, DISP_HOW_ANY, &when, NULL, &hdl[1] ) );
+ CHECK( 0, fd_disp_register( cb_8, DISP_HOW_ANY, &when, NULL, &hdl[2] ) );
+ CHECK( 0, fd_disp_register( cb_2, DISP_HOW_ANY, &when, NULL, &hdl[3] ) );
+ CHECK( 0, fd_disp_register( cb_3, DISP_HOW_ANY, &when, NULL, &hdl[4] ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd1, avp1, avp2, 1 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[8] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( NULL, msg );
+ CHECK( NULL, em );
+
+ CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[4], NULL ) );
+
+ CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) );
+ CHECK( 0, fd_disp_register( cb_1, DISP_HOW_ANY, &when, NULL, &hdl[1] ) );
+ CHECK( 0, fd_disp_register( cb_9, DISP_HOW_ANY, &when, NULL, &hdl[2] ) );
+ CHECK( 0, fd_disp_register( cb_2, DISP_HOW_ANY, &when, NULL, &hdl[3] ) );
+ CHECK( 0, fd_disp_register( cb_3, DISP_HOW_ANY, &when, NULL, &hdl[4] ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 1, cmd1, avp1, avp2, 1 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[9] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( DISP_ACT_SEND, action );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[4], NULL ) );
+ }
+
+ /* Test order of handlers */
+ {
+ when.app = app2;
+ when.command = cmd2;
+ when.avp = avp2;
+ when.value = enu2;
+
+ CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) );
+ CHECK( 0, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[1] ) );
+ CHECK( 0, fd_disp_register( cb_2, DISP_HOW_AVP, &when, NULL, &hdl[2] ) );
+ CHECK( 0, fd_disp_register( cb_3, DISP_HOW_CC, &when, NULL, &hdl[3] ) );
+ CHECK( 0, fd_disp_register( cb_4, DISP_HOW_APPID, &when, NULL, &hdl[4] ) );
+
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 2, cmd2, avp1, avp2, 2 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[2] );
+ CHECK( 1, cbcalled[3] );
+ CHECK( 1, cbcalled[4] );
+ CHECK( 0, cbcalled[9] );
+ CHECK( 0, fd_msg_free( msg ) );
+
+ CHECK( 0, fd_disp_register( cb_9, DISP_HOW_ANY, &when, NULL, &hdl[5] ) );
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 2, cmd2, avp1, avp2, 2 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 0, cbcalled[1] );
+ CHECK( 0, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( 0, cbcalled[4] );
+ CHECK( 1, cbcalled[9] );
+ CHECK( 0, fd_msg_free( msg ) );
+ CHECK( 0, fd_disp_unregister( &hdl[5], NULL ) );
+
+ CHECK( 0, fd_disp_register( cb_9, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[5] ) );
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 2, cmd2, avp1, avp2, 2 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( 0, cbcalled[4] );
+ CHECK( 1, cbcalled[9] );
+ CHECK( 0, fd_msg_free( msg ) );
+ CHECK( 0, fd_disp_unregister( &hdl[5], NULL ) );
+
+ CHECK( 0, fd_disp_register( cb_9, DISP_HOW_AVP, &when, NULL, &hdl[5] ) );
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 2, cmd2, avp1, avp2, 2 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[2] );
+ CHECK( 0, cbcalled[3] );
+ CHECK( 0, cbcalled[4] );
+ CHECK( 1, cbcalled[9] );
+ CHECK( 0, fd_msg_free( msg ) );
+ CHECK( 0, fd_disp_unregister( &hdl[5], NULL ) );
+
+ CHECK( 0, fd_disp_register( cb_9, DISP_HOW_CC, &when, NULL, &hdl[5] ) );
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 2, cmd2, avp1, avp2, 2 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[2] );
+ CHECK( 1, cbcalled[3] );
+ CHECK( 0, cbcalled[4] );
+ CHECK( 1, cbcalled[9] );
+ CHECK( 0, fd_msg_free( msg ) );
+ CHECK( 0, fd_disp_unregister( &hdl[5], NULL ) );
+
+ CHECK( 0, fd_disp_register( cb_9, DISP_HOW_APPID, &when, NULL, &hdl[5] ) );
+ memset(cbcalled, 0, sizeof(cbcalled));
+ msg = new_msg( 2, cmd2, avp1, avp2, 2 );
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( 1, cbcalled[1] );
+ CHECK( 1, cbcalled[2] );
+ CHECK( 1, cbcalled[3] );
+ CHECK( 1, cbcalled[4] );
+ CHECK( 1, cbcalled[9] );
+ CHECK( 0, fd_msg_free( msg ) );
+ CHECK( 0, fd_disp_unregister( &hdl[5], NULL ) );
+
+ CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+ CHECK( 0, fd_disp_unregister( &hdl[4], NULL ) );
+ }
+
+ /* Test application support advertisement */
+ {
+ struct dict_object * vnd;
+ struct dict_vendor_data vnd_data = { 1, "Vendor test" };
+ struct fd_app * app;
+
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_VENDOR, &vnd_data, NULL, &vnd ) );
+
+ CHECK( EINVAL, fd_disp_app_support ( vnd, NULL, 1, 0 ) );
+ CHECK( EINVAL, fd_disp_app_support ( app1, NULL, 0, 0 ) );
+ CHECK( 0, fd_disp_app_support ( app1, NULL, 1, 0 ) );
+ CHECK( 0, fd_disp_app_support ( app1, NULL, 0, 1 ) );
+ CHECK( 0, fd_disp_app_support ( app2, vnd, 1, 0 ) );
+
+ app = (struct fd_app *)(fd_g_config->cnf_apps.next);
+ CHECK( 1, app->appid );
+ CHECK( 1, app->flags.auth );
+ CHECK( 1, app->flags.acct );
+ app = (struct fd_app *)(fd_g_config->cnf_apps.prev);
+ CHECK( 2, app->appid );
+ CHECK( 1, app->flags.auth );
+ CHECK( 0, app->flags.acct );
+
+ #if 0
+ fd_log_debug("%s", fd_conf_dump(FD_DUMP_TEST_PARAMS));
+ #endif
+ }
+
+ /* Test opaque pointer management */
+ {
+ void * ptr;
+ CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, NULL, g_opaque, &hdl[0] ) );
+
+ /* Check this handler is called for a message */
+ msg = new_msg( 0, cmd1, avp1, NULL, 0 );
+ memset(cbcalled, 0, sizeof(cbcalled));
+ CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+ CHECK( 1, cbcalled[0] );
+ CHECK( DISP_ACT_CONT, action );
+
+ /* Delete the message */
+ CHECK( 0, fd_msg_free( msg ) );
+ CHECK( 0, fd_disp_unregister( &hdl[0], &ptr ) );
+ CHECK( 1, ptr == g_opaque ? 1 : 0 );
+ }
+
+ /* That's all for the tests yet */
+ PASSTEST();
+}
+
diff --git a/tests/testfifo.c b/tests/testfifo.c
new file mode 100644
index 0000000..c1c7cf1
--- /dev/null
+++ b/tests/testfifo.c
@@ -0,0 +1,579 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 "tests.h"
+#include <unistd.h>
+#include <limits.h>
+
+/* Wrapper for pthread_barrier stuff on Mac OS X */
+#ifndef HAVE_PTHREAD_BAR
+
+#define PTHREAD_BARRIER_SERIAL_THREAD 1
+typedef struct {
+ int count;
+ int entered;
+ int serial;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+} pthread_barrier_t;
+
+int pthread_barrier_init(pthread_barrier_t * barrier, int * barrier_attr, int count)
+{
+ memset(barrier, 0, sizeof(pthread_barrier_t));
+ barrier->count = count;
+ pthread_mutex_init(&barrier->mutex, NULL);
+ pthread_cond_init(&barrier->cond, NULL);
+ return 0;
+}
+
+int pthread_barrier_destroy(pthread_barrier_t * barrier)
+{
+ pthread_mutex_destroy(&barrier->mutex);
+ pthread_cond_destroy(&barrier->cond);
+ return 0;
+}
+
+int pthread_barrier_wait(pthread_barrier_t * barrier)
+{
+ int ret = 0;
+ int serial;
+ pthread_mutex_lock(&barrier->mutex);
+ serial = barrier->serial;
+
+ /* first thread gets the special value */
+ if (barrier->entered++ == 0)
+ ret = PTHREAD_BARRIER_SERIAL_THREAD;
+
+ /* Count was achieved? */
+ if (barrier->entered == barrier->count) {
+ /* Ok, increase serial, reset number of threads, and signal everyone */
+ barrier->entered = 0;
+ barrier->serial++;
+ pthread_cond_broadcast(&barrier->cond);
+ } else {
+ do {
+ pthread_cond_wait(&barrier->cond, &barrier->mutex);
+ } while (barrier->serial == serial);
+ /* this protects against spurious wakes */
+ }
+ pthread_mutex_unlock(&barrier->mutex);
+ return 0;
+}
+
+#endif /* HAVE_PTHREAD_BAR */
+
+/* Structure for testing threshold function */
+static struct thrh_test {
+ struct fifo * queue; /* pointer to the queue */
+ int h_calls; /* number of calls of h_cb */
+ int l_calls; /* number of calls of l_cb */
+} thrh_td;
+
+/* Callbacks for threasholds test */
+void thrh_cb_h(struct fifo *queue, void **data)
+{
+ if (thrh_td.h_calls == thrh_td.l_calls) {
+ CHECK( NULL, *data );
+ *data = &thrh_td;
+ } else {
+ CHECK( *data, &thrh_td );
+ }
+ CHECK( queue, thrh_td.queue );
+
+ /* Update the count */
+ thrh_td.h_calls ++;
+}
+void thrh_cb_l(struct fifo *queue, void **data)
+{
+ CHECK( 1, data ? 1 : 0 );
+ CHECK( *data, &thrh_td );
+
+ /* Check the queue parameter is correct */
+ CHECK( queue, thrh_td.queue );
+
+ /* Update the count */
+ thrh_td.l_calls ++;
+ /* Cleanup the data ptr if needed */
+ if (thrh_td.l_calls == thrh_td.h_calls)
+ *data = NULL;
+ /* done */
+}
+
+
+/* Structure that is passed to the test function */
+struct test_data {
+ struct fifo * queue; /* pointer to the queue */
+ pthread_barrier_t * bar; /* if not NULL, barrier to synchronize before getting messages */
+ struct timespec * ts; /* if not NULL, use a timedget instead of a get */
+ int nbr; /* number of messages to retrieve from the queue */
+};
+
+/* The test function, to be threaded */
+static void * test_fct(void * data)
+{
+ int ret = 0, i;
+ struct msg * msg = NULL;
+ struct test_data * td = (struct test_data *) data;
+
+ if (td->bar != NULL) {
+ ret = pthread_barrier_wait(td->bar);
+ if (ret != PTHREAD_BARRIER_SERIAL_THREAD) {
+ CHECK( 0, ret);
+ } else {
+ CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret); /* just for the traces */
+ }
+ }
+
+ for (i=0; i< td->nbr; i++) {
+ if (td->ts != NULL) {
+ CHECK( 0, fd_fifo_timedget(td->queue, &msg, td->ts) );
+ } else {
+ CHECK( 0, fd_fifo_get(td->queue, &msg) );
+ }
+ }
+
+ return NULL;
+}
+
+/* The test function, to be threaded */
+static int iter = 0;
+static void * test_fct2(void * data)
+{
+ int i;
+ int * item;
+ struct test_data * td = (struct test_data *) data;
+
+ for (i=0; i< td->nbr; i++) {
+ item = malloc(sizeof(int));
+ CHECK( 1, item ? 1 : 0 );
+ *item = i;
+ CHECK( 0, fd_fifo_post(td->queue, &item) );
+ iter++;
+ }
+
+ return NULL;
+}
+
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+ struct timespec ts;
+
+ struct msg * msg1 = NULL;
+ struct msg * msg2 = NULL;
+ struct msg * msg3 = NULL;
+
+ /* First, initialize the daemon modules */
+ INIT_FD();
+
+ /* Prolog: create the messages */
+ {
+ struct dict_object * acr_model = NULL;
+ struct dict_object * cer_model = NULL;
+ struct dict_object * dwr_model = NULL;
+
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &acr_model, ENOENT ) );
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer_model, ENOENT ) );
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &dwr_model, ENOENT ) );
+ CHECK( 0, fd_msg_new ( acr_model, 0, &msg1 ) );
+ CHECK( 0, fd_msg_new ( cer_model, 0, &msg2 ) );
+ CHECK( 0, fd_msg_new ( dwr_model, 0, &msg3 ) );
+ }
+
+ /* Basic operation */
+ {
+ struct fifo * queue = NULL;
+ struct msg * msg = NULL;
+ int max;
+ long long count;
+
+ /* Create the queue */
+ CHECK( 0, fd_fifo_new(&queue, 0) );
+
+ /* Check the count is 0 */
+ CHECK( 0, fd_fifo_length(queue) );
+
+ /* Now enqueue */
+ msg = msg1;
+ CHECK( 0, fd_fifo_post(queue, &msg) );
+ msg = msg2;
+ CHECK( 0, fd_fifo_post(queue, &msg) );
+ msg = msg3;
+ CHECK( 0, fd_fifo_post(queue, &msg) );
+
+ /* Check the count is 3 */
+ CHECK( 3, fd_fifo_length(queue) );
+
+ /* Retrieve the first message using fd_fifo_get */
+ CHECK( 0, fd_fifo_get(queue, &msg) );
+ CHECK( msg1, msg);
+ CHECK( 2, fd_fifo_length(queue) );
+
+ /* Retrieve the second message using fd_fifo_timedget */
+ CHECK(0, clock_gettime(CLOCK_REALTIME, &ts));
+ ts.tv_sec += 1; /* Set the timeout to 1 second */
+ CHECK( 0, fd_fifo_timedget(queue, &msg, &ts) );
+ CHECK( msg2, msg);
+ CHECK( 1, fd_fifo_length(queue) );
+
+ /* Retrieve the third message using meq_tryget */
+ CHECK( 0, fd_fifo_tryget(queue, &msg) );
+ CHECK( msg3, msg);
+ CHECK( 0, fd_fifo_length(queue) );
+
+ /* Check that another meq_tryget does not block */
+ CHECK( EWOULDBLOCK, fd_fifo_tryget(queue, &msg) );
+ CHECK( 0, fd_fifo_length(queue) );
+
+ /* Check the timedget actually timesout */
+ CHECK(0, clock_gettime(CLOCK_REALTIME, &ts));
+ ts.tv_nsec += 1000000; /* 1 millisecond */
+ if (ts.tv_nsec >= 1000000000L) {
+ ts.tv_nsec -= 1000000000L;
+ ts.tv_sec += 1;
+ }
+ CHECK( ETIMEDOUT, fd_fifo_timedget(queue, &msg, &ts) );
+ CHECK( 0, fd_fifo_length(queue) );
+
+ /* Post & get another message */
+ msg = msg1;
+ CHECK( 0, fd_fifo_post(queue, &msg) );
+ CHECK( 0, fd_fifo_timedget(queue, &msg, &ts) );
+ CHECK( msg1, msg);
+
+ /* Check some statistics */
+ CHECK( 0, fd_fifo_getstats(queue, NULL, NULL, &max, &count, NULL, NULL, NULL) );
+ CHECK( 3, max );
+ CHECK( 4, count );
+
+ /* We're done for basic tests */
+ CHECK( 0, fd_fifo_del(&queue) );
+ }
+
+ /* Test robustness, ensure no messages are lost */
+ {
+#define NBR_MSG 200
+#define NBR_THREADS 60
+ struct fifo *queue = NULL;
+ pthread_barrier_t bar;
+ struct test_data td_1;
+ struct test_data td_2;
+ struct msg *msgs[NBR_MSG * NBR_THREADS * 2], *msg;
+ pthread_t thr [NBR_THREADS * 2];
+ struct dict_object *dwr_model = NULL;
+ int i;
+ int nbr_threads;
+#ifdef _POSIX_THREAD_THREADS_MAX
+ nbr_threads = _POSIX_THREAD_THREADS_MAX;
+#else /* _POSIX_THREAD_THREADS_MAX */
+ nbr_threads = sysconf(_SC_THREAD_THREADS_MAX);
+#endif /* _POSIX_THREAD_THREADS_MAX */
+ if ((nbr_threads <= 0) || (nbr_threads > NBR_THREADS * 2)) {
+ nbr_threads = NBR_THREADS;
+ } else {
+ TRACE_DEBUG(INFO, "Local limit on number of threads: %d", nbr_threads);
+ /* The local limit is below NBR_THREADS */
+ nbr_threads = (nbr_threads / 2) - 1;
+ /* Ensure we create at least a few threads! */
+ CHECK( 1, nbr_threads >= 10 ? 1 : 0 );
+ }
+
+ /* Create the queue */
+ CHECK( 0, fd_fifo_new(&queue, 0) );
+
+ /* Create the barrier */
+ CHECK( 0, pthread_barrier_init(&bar, NULL, nbr_threads * 2 + 1) );
+
+ /* Initialize the ts */
+ CHECK(0, clock_gettime(CLOCK_REALTIME, &ts));
+ ts.tv_sec += 20; /* Set the timeout to 20 second */
+
+ /* Create the messages */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &dwr_model, ENOENT ) );
+ for (i = 0; i < NBR_MSG * nbr_threads * 2; i++) {
+ CHECK( 0, fd_msg_new ( dwr_model, 0, &msgs[i] ) );
+ }
+
+ /* Initialize the test data structures */
+ td_1.queue = queue;
+ td_1.bar = &bar;
+ td_1.ts = &ts;
+ td_1.nbr = NBR_MSG;
+ td_2.queue = queue;
+ td_2.bar = &bar;
+ td_2.ts = NULL;
+ td_2.nbr = NBR_MSG;
+
+ /* Create the threads */
+ for (i=0; i < nbr_threads * 2; i++) {
+ CHECK( 0, pthread_create( &thr[i], NULL, test_fct, (i & 1) ? &td_1 : &td_2 ) );
+ }
+
+ /* Synchronize everyone */
+ {
+ int ret = pthread_barrier_wait(&bar);
+ if (ret != PTHREAD_BARRIER_SERIAL_THREAD) {
+ CHECK( 0, ret);
+ } else {
+ CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret); /* for trace only */
+ }
+ }
+
+ /* Now post all the messages */
+ for (i=0; i < NBR_MSG * nbr_threads * 2; i++) {
+ msg = msgs[i];
+ CHECK( 0, fd_fifo_post(queue, &msg) );
+ }
+
+ /* Join all threads. This blocks if messages are lost... */
+ for (i=0; i < nbr_threads * 2; i++) {
+ CHECK( 0, pthread_join( thr[i], NULL ) );
+ }
+
+ /* Check the count of the queue is back to 0 */
+ CHECK( 0, fd_fifo_length(queue) );
+
+ /* Destroy this queue and the messages */
+ CHECK( 0, fd_fifo_del(&queue) );
+ for (i=0; i < NBR_MSG * nbr_threads * 2; i++) {
+ CHECK( 0, fd_msg_free( msgs[i] ) );
+ }
+ }
+
+ /* Test thread cancelation */
+ {
+ struct fifo *queue = NULL;
+ pthread_barrier_t bar;
+ struct test_data td;
+ pthread_t th;
+
+ /* Create the queue */
+ CHECK( 0, fd_fifo_new(&queue, 0) );
+
+ /* Create the barrier */
+ CHECK( 0, pthread_barrier_init(&bar, NULL, 2) );
+
+ /* Initialize the ts */
+ CHECK(0, clock_gettime(CLOCK_REALTIME, &ts));
+ ts.tv_sec += 10; /* Set the timeout to 10 second */
+
+ /* Initialize the test data structures */
+ td.queue = queue;
+ td.bar = &bar;
+ td.ts = &ts;
+ td.nbr = 1;
+
+ /* Create the thread */
+ CHECK( 0, pthread_create( &th, NULL, test_fct, &td ) );
+
+ /* Wait for the thread to be running */
+ {
+ int ret = pthread_barrier_wait(&bar);
+ if (ret != PTHREAD_BARRIER_SERIAL_THREAD) {
+ CHECK( 0, ret);
+ } else {
+ CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret );
+ }
+ }
+
+ /* Now cancel the thread */
+ CHECK( 0, pthread_cancel( th ) );
+
+ /* Join it */
+ CHECK( 0, pthread_join( th, NULL ) );
+
+ /* Do the same with the other function */
+ td.ts = NULL;
+
+ /* Create the thread */
+ CHECK( 0, pthread_create( &th, NULL, test_fct, &td ) );
+
+ /* Wait for the thread to be running */
+ {
+ int ret = pthread_barrier_wait(&bar);
+ if (ret != PTHREAD_BARRIER_SERIAL_THREAD) {
+ CHECK( 0, ret);
+ } else {
+ CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret );
+ }
+ }
+
+ /* Now cancel the thread */
+ CHECK( 0, pthread_cancel( th ) );
+
+ /* Join it */
+ CHECK( 0, pthread_join( th, NULL ) );
+
+ /* Destroy the queue */
+ CHECK( 0, fd_fifo_del(&queue) );
+ }
+
+ /* Test the threashold function */
+ {
+ struct fifo * queue = NULL;
+ int i;
+ struct msg * msg = NULL;
+
+ /* Create the queue */
+ CHECK( 0, fd_fifo_new(&queue, 0) );
+
+ /* Prepare the test data */
+ memset(&thrh_td, 0, sizeof(thrh_td));
+ thrh_td.queue = queue;
+
+ /* Set the thresholds for the queue */
+ CHECK( 0, fd_fifo_setthrhd ( queue, NULL, 6, thrh_cb_h, 4, thrh_cb_l ) );
+
+ /* Post 5 messages, no cb must be called. */
+ for (i=0; i<5; i++) {
+ msg = msg1;
+ CHECK( 0, fd_fifo_post(queue, &msg) );
+ } /* 5 msg in queue */
+ CHECK( 0, thrh_td.h_calls );
+ CHECK( 0, thrh_td.l_calls );
+
+ /* Get all these messages, and check again */
+ for (i=0; i<5; i++) {
+ CHECK( 0, fd_fifo_get(queue, &msg) );
+ } /* 0 msg in queue */
+ CHECK( 0, thrh_td.h_calls );
+ CHECK( 0, thrh_td.l_calls );
+
+ /* Now, post 6 messages, the high threashold */
+ for (i=0; i<6; i++) {
+ msg = msg1;
+ CHECK( 0, fd_fifo_post(queue, &msg) );
+ } /* 6 msg in queue */
+ CHECK( 1, thrh_td.h_calls );
+ CHECK( 0, thrh_td.l_calls );
+
+ /* Remove 2 messages, to reach the low threshold */
+ for (i=0; i<2; i++) {
+ CHECK( 0, fd_fifo_get(queue, &msg) );
+ } /* 4 msg in queue */
+ CHECK( 1, thrh_td.h_calls );
+ CHECK( 1, thrh_td.l_calls );
+
+ /* Come again at the high threshold */
+ for (i=0; i<2; i++) {
+ msg = msg1;
+ CHECK( 0, fd_fifo_post(queue, &msg) );
+ } /* 6 msg in queue */
+ CHECK( 2, thrh_td.h_calls );
+ CHECK( 1, thrh_td.l_calls );
+
+ /* Suppose the queue continues to grow */
+ for (i=0; i<6; i++) {
+ msg = msg1;
+ CHECK( 0, fd_fifo_post(queue, &msg) );
+ } /* 12 msg in queue */
+ CHECK( 3, thrh_td.h_calls );
+ CHECK( 1, thrh_td.l_calls );
+ for (i=0; i<5; i++) {
+ msg = msg1;
+ CHECK( 0, fd_fifo_post(queue, &msg) );
+ } /* 17 msg in queue */
+ CHECK( 3, thrh_td.h_calls );
+ CHECK( 1, thrh_td.l_calls );
+
+ /* Now the queue goes back to 0 messages */
+ for (i=0; i<17; i++) {
+ CHECK( 0, fd_fifo_get(queue, &msg) );
+ } /* 0 msg in queue */
+ CHECK( 3, thrh_td.h_calls );
+ CHECK( 3, thrh_td.l_calls );
+
+ /* We're done for this test */
+ CHECK( 0, fd_fifo_del(&queue) );
+ }
+
+ /* Test max queue limit */
+ {
+ struct fifo *queue = NULL;
+ struct test_data td;
+ pthread_t th;
+ int * item, i;
+
+ /* Create the queue */
+ CHECK( 0, fd_fifo_new(&queue, 10) );
+
+ /* Initialize the test data structures */
+ td.queue = queue;
+ td.nbr = 15;
+
+ CHECK( 0, pthread_create( &th, NULL, test_fct2, &td ) );
+
+ usleep(100000); /* 100 millisec */
+
+ CHECK( 10, iter );
+
+ CHECK( 0, fd_fifo_tryget(queue, &item) );
+ CHECK( 0, *item);
+ free(item);
+
+ usleep(100000); /* 100 millisec */
+
+ CHECK( 11, iter );
+
+ for (i=1; i<4; i++) {
+ CHECK( 0, fd_fifo_get(queue, &item) );
+ CHECK( i, *item);
+ free(item);
+ }
+
+ usleep(100000); /* 100 millisec */
+
+ CHECK( 14, iter );
+
+ for (; i < td.nbr; i++) {
+ CHECK( 0, fd_fifo_tryget(queue, &item) );
+ CHECK( i, *item);
+ free(item);
+ }
+
+ CHECK( 0, pthread_join( th, NULL ) );
+ CHECK( 15, iter );
+
+ }
+
+ /* Delete the messages */
+ CHECK( 0, fd_msg_free( msg1 ) );
+ CHECK( 0, fd_msg_free( msg2 ) );
+ CHECK( 0, fd_msg_free( msg3 ) );
+
+ /* That's all for the tests yet */
+ PASSTEST();
+}
diff --git a/tests/testloadext.c b/tests/testloadext.c
new file mode 100644
index 0000000..452737f
--- /dev/null
+++ b/tests/testloadext.c
@@ -0,0 +1,100 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* Copyright (c) 2011, 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 "tests.h"
+
+#ifndef BUILD_DIR
+#error "Missing BUILD_DIR information"
+#endif /* BUILD_DIR */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <dlfcn.h>
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+ DIR *dir;
+ struct dirent *dp;
+ char fullname[512];
+ int pathlen;
+
+ /* First, initialize the daemon modules */
+ INIT_FD();
+ CHECK( 0, fd_queues_init() );
+ CHECK( 0, fd_msg_init() );
+ CHECK( 0, fd_rtdisp_init() );
+
+ /* Find all extensions which have been compiled along the test */
+ TRACE_DEBUG(INFO, "Loading from: '%s'", BUILD_DIR "/extensions");
+ CHECK( 0, (dir = opendir (BUILD_DIR "/extensions")) == NULL ? 1 : 0 );
+ pathlen = snprintf(fullname, sizeof(fullname), BUILD_DIR "/extensions/");
+
+ while ((dp = readdir (dir)) != NULL) {
+ char * dot = strrchr(dp->d_name, '.');
+ if (dot && !(strcmp(dot, ".fdx"))) {
+ /* We found a file with name *.fdx, attempt to load it */
+ void *hdl, * ep;
+ snprintf(fullname + pathlen, sizeof(fullname) - pathlen, "%s", dp->d_name);
+
+ TRACE_DEBUG(INFO, "Extension: '%s'", dp->d_name);
+
+ /* load */
+ hdl = dlopen(fullname, RTLD_NOW | RTLD_GLOBAL);
+ if (!hdl) {
+ TRACE_DEBUG(INFO, "Unable to load '%s': %s.", fullname, dlerror());
+ }
+ CHECK( 0, hdl == NULL ? 1 : 0 );
+
+ /* resolve entry */
+ ep = dlsym( hdl, "fd_ext_init" );
+ if (!ep) {
+ TRACE_DEBUG(INFO, "No 'fd_ext_init' entry point in '%s': %s.", fullname, dlerror());
+ }
+ CHECK( 0, ep == NULL ? 1 : 0 );
+
+ /* Done, now unload */
+#ifndef SKIP_DLCLOSE
+ CHECK( 0, dlclose(hdl) );
+#endif /* SKIP_DLCLOSE */
+ }
+ }
+
+ CHECK( 0, closedir(dir) );
+
+ PASSTEST();
+}
+
diff --git a/tests/testmesg.c b/tests/testmesg.c
new file mode 100644
index 0000000..5525ff4
--- /dev/null
+++ b/tests/testmesg.c
@@ -0,0 +1,1455 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 "tests.h"
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+ struct msg * acr = NULL;
+ struct avp * pi = NULL, *avp1, *avp2;
+ unsigned char * buf = NULL;
+
+ /* First, initialize the daemon modules */
+ INIT_FD();
+
+ /* Create the message object from model */
+ {
+ struct dict_object * acr_model = NULL;
+
+ /* Now find the ACR dictionary object */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &acr_model, ENOENT ) );
+
+ /* Create the instance, using the templates */
+ CHECK( 0, fd_msg_new ( acr_model, 0, &acr ) );
+
+ /* Check there is no child */
+ CHECK( ENOENT, fd_msg_browse ( acr, MSG_BRW_FIRST_CHILD, NULL, NULL) );
+
+ #if 0
+ /* For debug: dump the object */
+ fd_log_debug("Dumping Accounting-Request empty message:");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, acr, fd_g_config->cnf_dict, 0, 1));
+ #endif
+ }
+
+ /* Create the Proxy-Info AVP from model */
+ {
+ struct dict_object * pi_model = NULL;
+
+ /* Now find the ACR dictionary object */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Proxy-Info", &pi_model, ENOENT ) );
+
+ /* Create the instance, using the templates */
+ CHECK( 0, fd_msg_avp_new ( pi_model, 0, &pi ) );
+
+ #if 0
+ /* For debug: dump the object */
+ fd_log_debug("Dumping Proxy-Info AVP");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, pi, fd_g_config->cnf_dict, 0, 1));
+ fd_log_debug("Dumping dictionary model");
+ fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, pi_model));
+ #endif
+
+ }
+
+ /* Get a reference to the current last AVP in the message */
+ {
+ int diff = 0;
+
+ CHECK( 0, fd_msg_avp_new ( NULL, 0, &avp1 ) );
+ CHECK( 0, fd_msg_avp_add ( acr, MSG_BRW_LAST_CHILD, avp1) );
+
+ CHECK( 0, fd_msg_browse ( acr, MSG_BRW_LAST_CHILD, &avp2, &diff) );
+ CHECK( 1, diff );
+ CHECK( avp1, avp2 );
+
+ /* Check that we cannot add this AVP to another object since it is already linked */
+ CHECK( EINVAL, fd_msg_avp_add( pi, MSG_BRW_LAST_CHILD, avp1) );
+ }
+
+ /* Now add the Proxy-Info AVP at the end of the message */
+ {
+ CHECK( 0, fd_msg_avp_add( acr, MSG_BRW_LAST_CHILD, pi) );
+ #if 0
+ /* For debug: dump the object */
+ fd_log_debug("Dumping Accounting-Request with Proxy-Info AVP at the end");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, acr, fd_g_config->cnf_dict, 0, 1));
+ #endif
+ }
+
+ /* Check the last child is now the proxy-Info */
+ {
+ CHECK( 0, fd_msg_browse ( acr, MSG_BRW_LAST_CHILD, &avp2, NULL) );
+ CHECK( pi, avp2 );
+ }
+
+ /* Check that the avp before the proxy-info is the previous last one */
+ {
+ int diff = 0;
+ CHECK( 0, fd_msg_browse ( pi, MSG_BRW_PREV, &avp2, &diff) );
+ CHECK( avp1, avp2 );
+ CHECK( 0, diff);
+ }
+
+ /* Check that there are no AVP after the proxy-info */
+ CHECK( ENOENT, fd_msg_browse ( pi, MSG_BRW_NEXT, NULL, NULL) );
+
+ /* Test the fd_msg_free function unlinks the object properly */
+ {
+ struct dict_object * rr_model = NULL;
+
+ /* Now find the dictionary object */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &rr_model, ENOENT ) );
+
+ /* Create the instance, using the templates */
+ CHECK( 0, fd_msg_avp_new ( rr_model, 0, &avp1 ) );
+
+ /* Add the AVP at the end of the message */
+ CHECK( 0, fd_msg_avp_add( pi, MSG_BRW_NEXT, avp1) );
+
+ /* Check the last AVP of the message is now this one */
+ CHECK( 0, fd_msg_browse ( acr, MSG_BRW_LAST_CHILD, &avp2, NULL) );
+ CHECK( avp1, avp2 );
+
+ /* Now delete it */
+ CHECK( 0, fd_msg_free( avp1 ) );
+
+ /* Check the last AVP of the message is back to pi */
+ CHECK( 0, fd_msg_browse ( acr, MSG_BRW_LAST_CHILD, &avp2, NULL) );
+ CHECK( pi, avp2 );
+
+ /* Delete the whole message */
+ CHECK( 0, fd_msg_free( acr ) );
+ }
+
+ /* Recreate the message object */
+ {
+ struct dict_object * acr_model = NULL;
+
+ /* Now find the ACR dictionary object */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &acr_model, ENOENT ) );
+
+ /* Create the instance, using the templates */
+ CHECK( 0, fd_msg_new ( acr_model, 0, &acr ) );
+ }
+
+ /* Now let's create some additional Dictionary objects for the test */
+ {
+ /* The constant values used here are totally arbitrary chosen */
+ struct dict_object * vendor;
+ {
+ struct dict_vendor_data vendor_data = { 73565, "Vendor test" };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_VENDOR, &vendor_data , NULL, &vendor ) );
+ }
+
+ {
+ struct dict_application_data app_data = { 73566, "Application test" };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_APPLICATION, &app_data , vendor, NULL ) );
+ }
+
+ {
+ struct dict_avp_data avp_data = { 73567, 0, "AVP Test - no vendor - f32", 0, 0, AVP_TYPE_FLOAT32 };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+ }
+
+ {
+ struct dict_avp_data avp_data = { 139103, 0, "AVP Test - no vendor - f64", 0, 0, AVP_TYPE_FLOAT64 };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+ }
+
+ {
+ struct dict_object * type = NULL;
+ struct dict_type_data type_data = { AVP_TYPE_INTEGER64, "Int64 test" };
+ struct dict_avp_data avp_data = { 73568, 73565, "AVP Test - i64", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_INTEGER64 };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+ }
+
+ {
+ struct dict_object * type = NULL;
+ struct dict_type_data type_data = { AVP_TYPE_INTEGER32, "Enum32 test" };
+ struct dict_enumval_data val1 = { "i32 const test (val 1)", { .i32 = 1 } };
+ struct dict_enumval_data val2 = { "i32 const test (val 2)", { .i32 = 2 } };
+ struct dict_enumval_data val3 = { "i32 const test (val -5)",{ .i32 = -5 } };
+ struct dict_avp_data avp_data = { 73569, 73565, "AVP Test - enumi32", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_INTEGER32 };
+
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val1 , type, NULL ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val2 , type, NULL ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val3 , type, NULL ) );
+ }
+
+ {
+ struct dict_object * type = NULL;
+ struct dict_type_data type_data = { AVP_TYPE_OCTETSTRING, "OS test" };
+ struct dict_avp_data avp_data = { 73570, 73565, "AVP Test - os", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+ }
+
+ {
+ struct dict_object * type = NULL;
+ struct dict_type_data type_data = { AVP_TYPE_OCTETSTRING, "OS enum test" };
+ struct dict_enumval_data val1 = { "os const test (Test)", { .os = { (unsigned char *)"Test", 4 } } };
+ struct dict_enumval_data val2 = { "os const test (waaad)", { .os = { (unsigned char *)"waaad", 5 } } };
+ struct dict_enumval_data val3 = { "os const test (waa)", { .os = { (unsigned char *)"waaad", 3 } } };
+ struct dict_avp_data avp_data = { 73571, 73565, "AVP Test - enumos", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING };
+
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val1 , type, NULL ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val2 , type, NULL ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val3 , type, NULL ) );
+ }
+
+ {
+ struct dict_object * gavp = NULL;
+ struct dict_avp_data avp_data = { 73572, 73565, "AVP Test - grouped", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_GROUPED };
+
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, &gavp ) );
+
+ /* Macro to search AVP and create a rule */
+ #define ADD_RULE( _parent, _vendor, _avpname, _pos, _min, _max, _ord ) { \
+ struct dict_object * _avp = NULL; \
+ struct dict_avp_request _req = { (_vendor), 0, (_avpname) }; \
+ struct dict_rule_data _data; \
+ CHECK( 0, fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT));\
+ _data.rule_avp = _avp; \
+ _data.rule_position = (_pos); \
+ _data.rule_order = (_ord); \
+ _data.rule_min = (_min); \
+ _data.rule_max = (_max); \
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_RULE, &_data , (_parent), NULL ) ); \
+ }
+
+ ADD_RULE(gavp, 73565, "AVP Test - os", RULE_OPTIONAL, -1, -1, 0);
+
+ }
+
+ {
+ struct dict_object * application = NULL;
+ struct dict_object * command = NULL;
+ struct dict_cmd_data cmd_data = { 73573, "Test-Command-Request", CMD_FLAG_REQUEST, CMD_FLAG_REQUEST };
+
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Application test", &application, ENOENT ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd_data , application, &command ) );
+ ADD_RULE(command, 0, "AVP Test - no vendor - f32", RULE_FIXED_HEAD, -1, 1, 1);
+ ADD_RULE(command, 73565, "AVP Test - i64", RULE_REQUIRED, -1, -1, 0);
+ ADD_RULE(command, 73565, "AVP Test - enumi32", RULE_OPTIONAL, -1, -1, 0);
+ ADD_RULE(command, 73565, "AVP Test - os", RULE_OPTIONAL, -1, -1, 0);
+ ADD_RULE(command, 73565, "AVP Test - enumos", RULE_OPTIONAL, -1, -1, 0);
+ ADD_RULE(command, 73565, "AVP Test - grouped", RULE_OPTIONAL, -1, -1, 0);
+ }
+
+ {
+ struct dict_object * application = NULL;
+ struct dict_object * command = NULL;
+ struct dict_cmd_data cmd_data = { 73573, "Test-Command-Answer", CMD_FLAG_REQUEST, 0 };
+
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Application test", &application, ENOENT ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd_data , application, &command ) );
+ }
+
+ {
+ struct dict_object * gavp = NULL;
+ struct dict_avp_data avp_data = { 73574, 73565, "AVP Test - rules", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_GROUPED };
+
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, &gavp ) );
+
+ ADD_RULE(gavp, 0, "AVP Test - no vendor - f32", RULE_FIXED_HEAD, 0, 1, 1);
+ ADD_RULE(gavp, 73565, "AVP Test - i64", RULE_FIXED_HEAD, -1, 1, 2);
+ ADD_RULE(gavp, 73565, "AVP Test - enumi32", RULE_FIXED_HEAD, -1, 1, 3);
+ ADD_RULE(gavp, 73565, "AVP Test - os", RULE_REQUIRED, 2, 3, 0);
+ ADD_RULE(gavp, 73565, "AVP Test - enumos", RULE_OPTIONAL, 0, 1, 0);
+ ADD_RULE(gavp, 73565, "AVP Test - grouped", RULE_FIXED_TAIL, -1, 1, 1);
+ /* ABNF :
+ < no vendor - f32 >
+ < i64 >
+ < enumi32 >
+ 2*3 { os }
+ *1 [ enumos ]
+ < grouped >
+ */
+ #if 0
+ fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, gavp));
+ #endif
+ }
+
+ {
+ struct dict_object * type = NULL;
+ struct dict_type_data type_data = { AVP_TYPE_OCTETSTRING, "OS test2", NULL, NULL, NULL, fd_dictfct_CharInOS_check, "@." };
+ struct dict_avp_data avp_data = { 73575, 73565, "AVP Test - os2", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+ }
+
+ #if 0
+ {
+ fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, vendor));
+ }
+ #endif
+ }
+
+ /* Now create some values and check the length is correctly handled */
+ {
+ struct dict_object * cmd_model = NULL;
+ struct msg * msg = NULL;
+ struct dict_object * avp_model = NULL;
+ struct avp * avp = NULL;
+ union avp_value value;
+
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Test-Command-Request", &cmd_model, ENOENT ) );
+
+ /* Check an error is trigged if the AVP has no value set */
+ {
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "AVP Test - no vendor - f32", &avp_model, ENOENT ) );
+
+ CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) );
+ CHECK( 0, fd_msg_avp_new ( avp_model, 0, &avp ) );
+
+ CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_FIRST_CHILD, avp ) );
+
+ CHECK( EINVAL, fd_msg_update_length ( avp ) );
+ CHECK( EINVAL, fd_msg_update_length ( msg ) );
+
+ CHECK( 0, fd_msg_free( msg ) );
+ }
+
+ /* Check the sizes are handled properly */
+ {
+ struct avp * avpi = NULL;
+ struct avp * avpch = NULL;
+ struct avp_hdr * avpdata = NULL;
+ struct msg_hdr * msgdata = NULL;
+ #define ADD_AVP( _parent, _position, _avpi, _avpvendor, _avpname) { \
+ struct dict_object * _avp = NULL; \
+ struct dict_avp_request _req = { (_avpvendor), 0, (_avpname) }; \
+ CHECK( 0, fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT));\
+ CHECK( 0, fd_msg_avp_new ( _avp, 0, &_avpi ) ); \
+ CHECK( 0, fd_msg_avp_add ( (_parent), (_position), _avpi ) ); \
+ }
+ /* Create a message with many AVP inside */
+ CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) );
+ CHECK( 0, fd_msg_hdr ( msg, &msgdata ) );
+
+ /* Avp no vendor, float32 => size = 12 */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test - no vendor - f32" );
+ value.f32 = 3.1415;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+ CHECK( 0, fd_msg_update_length ( avpi ) );
+ #if 0
+ fd_log_debug("AVP no vendor, value 3.1415:");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+ #endif
+ CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+ CHECK( 12, avpdata->avp_len );
+
+ /* Check what happens when we delete the value */
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, NULL ) );
+ CHECK( EINVAL, fd_msg_update_length ( avpi ) );
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ /* Add a vendor AVP, integer64 => size = 20 */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - i64" );
+ value.i64 = 0x123456789abcdeLL;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+ CHECK( 0, fd_msg_update_length ( avpi ) );
+ #if 0
+ fd_log_debug("AVP vendor, value 0x123456789abcdeL:");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+ #endif
+ CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+ CHECK( 20, avpdata->avp_len );
+
+ /* Check the size of the message is 20 (header) + 12 + 20 = 52 */
+ CHECK( 0, fd_msg_update_length ( msg ) );
+ CHECK( 52, msgdata->msg_length );
+
+ /* Add an AVP with an enum value */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" );
+ {
+ struct dict_object * type_model = NULL;
+ struct dict_object * value_model = NULL;
+ struct dict_enumval_request request;
+
+ CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+ memset(&request, 0, sizeof(request));
+ request.type_obj = type_model;
+ request.search.enum_name = "i32 const test (val 2)";
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+ CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+ #if 0
+ fd_log_debug("AVP enum i32, value 2 (from const):");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+ #endif
+ }
+
+ /* Add an AVP with an enum value, negative */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" );
+ {
+ struct dict_object * type_model = NULL;
+ struct dict_object * value_model = NULL;
+ struct dict_enumval_request request;
+
+ CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+ memset(&request, 0, sizeof(request));
+ request.type_obj = type_model;
+ request.search.enum_name = "i32 const test (val -5)";
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+ CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+ #if 0
+ fd_log_debug("AVP enum i32, value -5 (from const):");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+ #endif
+ /* Check the size is correct ( 12 for header + 4 for value ) */
+ CHECK( 0, fd_msg_update_length ( avpi ) );
+ CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+ CHECK( 16, avpdata->avp_len );
+ }
+
+ /* Now add a value which is not a constant into an enumerated AVP */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" );
+ value.i32 = -10;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+ CHECK( 0, fd_msg_update_length ( avpi ) );
+ #if 0
+ fd_log_debug("AVP vendor enum i32, value -10 (not const):");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+ #endif
+
+ /* Add an octetstring AVP */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - os" );
+ {
+ unsigned char buf[90];
+ memcpy(&buf, "This\0 is a buffer of dat\a. It is not a string so we can have any c\0ntr\0l character here...\0\0", 89);
+ value.os.data = buf;
+ value.os.len = 89;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+ memset(&buf, 0, sizeof(buf)); /* Test that the OS value is really copied */
+ CHECK( 0, fd_msg_update_length ( avpi ) );
+ #if 0
+ fd_log_debug("AVP octet string, 'This\\0 is a b...'");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+ #endif
+ CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+ CHECK( 101, avpdata->avp_len );
+ CHECK( 'T', avpdata->avp_value->os.data[0] );
+ CHECK( 'i', avpdata->avp_value->os.data[6] );
+ }
+
+ /* Check the size of the message is 20 (header) + 12 + 20 + 16 * 3 + 101 + 3 (padding) = 204 */
+ CHECK( 0, fd_msg_update_length ( msg ) );
+ CHECK( 204, msgdata->msg_length );
+
+ /* Add an octetstring from an enumerated constant */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumos" );
+ {
+ struct dict_object * type_model = NULL;
+ struct dict_object * value_model = NULL;
+ struct dict_enumval_request request;
+
+ CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+ memset(&request, 0, sizeof(request));
+ request.type_obj = type_model;
+ request.search.enum_name = "os const test (waaad)";
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+ CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+ #if 0
+ fd_log_debug("AVP Enumuerated OctetString (from const):");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+ #endif
+ /* Check the size is correct ( 12 for header + 5 for value ) */
+ CHECK( 0, fd_msg_update_length ( avpi ) );
+ CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+ CHECK( 17, avpdata->avp_len );
+ }
+
+ /* Add an octetstring from an enumerated constant */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumos" );
+ {
+ struct dict_object * type_model = NULL;
+ struct dict_object * value_model = NULL;
+ struct dict_enumval_request request;
+
+ CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+ memset(&request, 0, sizeof(request));
+ request.type_obj = type_model;
+ request.search.enum_name = "os const test (waa)";
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+ CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+ #if 0
+ fd_log_debug("AVP Enumuerated OctetString (from const):");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+ #endif
+ /* Check the size is correct ( 12 for header + 3 for value ) */
+ CHECK( 0, fd_msg_update_length ( avpi ) );
+ CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+ CHECK( 15, avpdata->avp_len );
+ }
+
+
+ /* Check the size of the message is 20 (header) + 12 + 20 + 16 * 3 + (101 + 3) + (17 + 3) + (15 + 1) = 240 */
+ CHECK( 0, fd_msg_update_length ( msg ) );
+ CHECK( 240, msgdata->msg_length );
+
+ /* Now test the grouped AVPs */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - grouped" );
+ ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+ {
+ value.os.data = (unsigned char *)"12345678";
+ value.os.len = 8;
+ CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+ #if 0
+ fd_log_debug("AVP octet string, '1234678'");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpch, fd_g_config->cnf_dict, 0, 0));
+ #endif
+ CHECK( 0, fd_msg_update_length ( avpch ) );
+ CHECK( 0, fd_msg_avp_hdr ( avpch, &avpdata ) );
+ CHECK( 20, avpdata->avp_len );
+ }
+ ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+ {
+ value.os.data = (unsigned char *)"123456789";
+ value.os.len = 9;
+ CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+ #if 0
+ fd_log_debug("AVP octet string, '12346789'");
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpch, fd_g_config->cnf_dict, 0, 0));
+ #endif
+ }
+
+ /* Check the size is updated recursively: (gavp hdr: 12) + (avp1: 20) + (avp2: 21 + 3) = 56 */
+ CHECK( 0, fd_msg_update_length ( avpi ) );
+ CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+ CHECK( 56, avpdata->avp_len );
+
+ /* Add another similar grouped AVP, to have lot of padding */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - grouped" );
+ ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+ {
+ value.os.data = (unsigned char *)"1";
+ value.os.len = 1;
+ CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+ }
+ ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+ {
+ value.os.data = (unsigned char *)"1234567";
+ value.os.len = 7;
+ CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+ }
+
+ /* Now check the global size of the message, if padding is correctly handled */
+ /* size = 20 (header) + 12 + 20 + 16 * 3 + (101 + 3) + (17 + 3) + (15 + 1)
+ * + ( 12 + ( 20 + 21) + 3 ) # padding for the grouped AVP = 3
+ * + ( 12 + ( (13 + 3) + 19 ) + 1 ) # and 1 for this one
+ * size = 240 + 56 + 48 = 344
+ */
+ CHECK( 0, fd_msg_update_length ( msg ) );
+ #if 0
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+ #endif
+ CHECK( 344, msgdata->msg_length );
+
+ /* Set the application to the test application: 73566 */
+ msgdata->msg_appl = 73566;
+
+ /* Set the hop-by-hop ID to a random value: 0x4b44b41d */
+ msgdata->msg_hbhid = 0x4b44b41d;
+ /* Set the end-to-end ID to a random value: 0xe2ee2e1d */
+ msgdata->msg_eteid = 0xe2ee2e1d;
+ }
+
+ /* Test the msg_bufferize function */
+ {
+
+ CHECK( 0, fd_msg_bufferize( msg, &buf, NULL ) );
+
+ /* Test the first bytes */
+ CHECK( 0x01, buf[0] ); /* Version */
+ CHECK( 0x00, buf[1] ); /* Length: 344 = 0x000158 */
+ CHECK( 0x01, buf[2] );
+ CHECK( 0x58, buf[3] );
+ CHECK( 0x80, buf[4] ); /* flags: only "R" is set. */
+ CHECK( 0x01, buf[5] ); /* Command code: 73573 = 0x011F65 */
+ CHECK( 0x1F, buf[6] );
+ CHECK( 0x65, buf[7] );
+ CHECK( 0x00, buf[8] ); /* App ID: 73566 = 0x00011F5E */
+ CHECK( 0x01, buf[9] );
+ CHECK( 0x1F, buf[10] );
+ CHECK( 0x5E, buf[11] );
+ CHECK( 0x4b, buf[12] ); /* hop-by-hop id: 0x4b44b41d */
+ CHECK( 0x44, buf[13] );
+ CHECK( 0xb4, buf[14] );
+ CHECK( 0x1d, buf[15] );
+ CHECK( 0xe2, buf[16] ); /* end-to-end id: 0xe2ee2e1d */
+ CHECK( 0xee, buf[17] );
+ CHECK( 0x2e, buf[18] );
+ CHECK( 0x1d, buf[19] );
+
+ CHECK( 0x00, buf[20] ); /* First AVP (AVP Test - no vendor - f32) begin: code 73567 = 0x00011F5F */
+ CHECK( 0x01, buf[21] );
+ CHECK( 0x1F, buf[22] );
+ CHECK( 0x5F, buf[23] );
+ CHECK( 0x00, buf[24] ); /* flags: 0 */
+ CHECK( 0x00, buf[25] ); /* length: 12 = 0x00000c */
+ CHECK( 0x00, buf[26] );
+ CHECK( 0x0C, buf[27] );
+ CHECK( 0x40, buf[28] ); /* Value: 3.1415: sign = '+' => most significant bit = 0 */
+ CHECK( 0x49, buf[29] ); /* 2 <= 3.1415 < 4 => exponent = 1 => biaised (on 8 bits) = (decimal) 128 = (binary) 100 0000 0 */
+ CHECK( 0x0e, buf[30] ); /* significand = (decimal) 1.57075 = (binary) 1.100 1001 0000 1110 0101 0110 */
+ CHECK( 0x56, buf[31] ); /* total => 0100 0000 0100 1001 0000 1110 0101 0110 = (hexa) 40 49 0e 56*/
+
+ /* The other AVPs will be tested by successful parsing... */
+ }
+
+ /* Now free the message, we keep only the buffer. */
+ CHECK( 0, fd_msg_free( msg ) );
+
+ }
+
+ /* Test the parsing of buffers and messages */
+ {
+ unsigned char * buf_cpy = NULL;
+ struct msg * msg;
+
+ #define CPYBUF() { \
+ buf_cpy = malloc(344); \
+ CHECK( buf_cpy ? 1 : 0, 1); \
+ memcpy(buf_cpy, buf, 344); \
+ }
+
+ /* Test the msg_parse_buffer function */
+ {
+ CPYBUF();
+ CHECK( EBADMSG, fd_msg_parse_buffer( &buf_cpy, 340, &msg) );
+
+ CPYBUF();
+ CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+ #if 0
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+ #endif
+
+ /* reinit the msg */
+ CHECK( 0, fd_msg_free ( msg ) );
+
+ }
+
+ /* Test the fd_msg_search_avp function */
+ {
+ struct dict_object * avp_model;
+ struct avp * found;
+ struct avp_hdr * avpdata = NULL;
+
+ /* Now find the ACR dictionary object */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "AVP Test - no vendor - f32", &avp_model, ENOENT ) );
+
+ CPYBUF();
+ CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+
+ /* Search this AVP instance in the msg */
+ CHECK( 0, fd_msg_search_avp( msg, avp_model, &found ) );
+
+ /* Check the AVP value is 3.1415 */
+ CHECK( 0, fd_msg_avp_hdr ( found, &avpdata ) );
+ CHECK( 3.1415F, avpdata->avp_value->f32 );
+
+ /* reinit the msg */
+ CHECK( 0, fd_msg_free ( msg ) );
+
+ }
+
+ /* Test the msg_parse_dict function */
+ {
+ /* Test with an unknown command code */
+ {
+ CPYBUF();
+
+ /* Change the command-code */
+ buf_cpy[5] = 0x11;
+ CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+ CHECK( ENOTSUP, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+
+ /* reset */
+ CHECK( 0, fd_msg_free ( msg ) );
+ }
+
+ /* Test with an unknown Mandatory AVP */
+ {
+ CPYBUF();
+
+ buf_cpy[20] = 0x11; /* New AVP code = 0x11011F5F, undefined */
+ buf_cpy[24] = 0x40; /* Add the 'M' flag */
+
+ /* Check that we cannot support this message now */
+ CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+ CHECK( ENOTSUP, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+
+ /* reset */
+ CHECK( 0, fd_msg_free ( msg ) );
+ }
+
+ /* Test with an unknown optional AVP */
+ {
+ CPYBUF();
+
+ buf_cpy[20] = 0x11; /* New AVP code = 0x11011F5F, undefined */
+
+ /* Check that we can support this message now */
+ CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+ CHECK( 0, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+
+ #if 0
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+ #endif
+
+ /* reset */
+ CHECK( 0, fd_msg_free ( msg ) );
+ }
+
+ /* Test with an invalid AVP (definition mismatch with the dictionary) */
+ {
+ CPYBUF();
+
+ buf_cpy[21] = 0x02; /* New AVP code = 0x00021F5F, f64 type in the dictionary */
+
+
+ /* Check that we cannot support this message now */
+ CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+ CHECK( EBADMSG, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+
+ /* reset */
+ CHECK( 0, fd_msg_free ( msg ) );
+ }
+
+ /* Test with a type verifier */
+ {
+ struct fd_pei error_info;
+ CPYBUF();
+ buf_cpy[103] = 0x67; /* Replaced AVP code = 0x00011F67, OS test2 type in the dictionary */
+
+ /* Check that we cannot support this message now */
+ CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+ CHECK( EBADMSG, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+
+ /* reset */
+ CHECK( 0, fd_msg_free ( msg ) );
+
+ CPYBUF();
+ buf_cpy[103] = 0x67; /* Replaced AVP code = 0x00011F67, OS test2 type in the dictionary */
+
+ /* Check error reporting works */
+ CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+ CHECK( EBADMSG, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, &error_info ) );
+
+ #if 1
+ fd_log_debug("Error reported: %s\n in AVP: %s", error_info.pei_message, fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, error_info.pei_avp, fd_g_config->cnf_dict, 0, 1));
+ #endif
+
+ /* reset */
+ CHECK( 0, fd_msg_free ( msg ) );
+
+ CPYBUF();
+ buf_cpy[103] = 0x67; /* Replaced AVP code = 0x00011F67, OS test2 type in the dictionary */
+ buf_cpy[130] = '@';
+ buf_cpy[140] = '.'; /* now we comply to the constraints */
+
+ /* Check that we cannot support this message now */
+ CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+ CHECK( 0, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+
+ /* reset */
+ CHECK( 0, fd_msg_free ( msg ) );
+
+
+ }
+
+ {
+ unsigned char * buftmp = NULL;
+ struct msg * error;
+ /* Check the parse or error works as expected */
+ CPYBUF();
+
+ buf_cpy[21] = 0x02; /* New AVP code = 0x00021F5F, f64 type in the dictionary */
+
+ /* Check that we cannot support this message now */
+ CHECK( 0, fd_msg_init() );
+ CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+ CHECK( EBADMSG, fd_msg_parse_or_error( &msg, &error ) );
+ CHECK( NULL, msg );
+ msg = error;
+
+ CHECK( 0, fd_msg_bufferize( msg, &buftmp, NULL ) );
+
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+
+ TODO("Check the Failed-AVP is as expected");
+
+
+ /* reset */
+ CHECK( 0, fd_msg_free ( msg ) );
+ free(buftmp);
+ }
+
+
+ CHECK( 0, fd_msg_parse_buffer( &buf, 344, &msg) );
+ CHECK( 0, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+ #if 0
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+ #endif
+ }
+
+ /* Now test the msg_parse_rule function */
+ {
+ struct fd_pei pei;
+
+ CHECK( 0, fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ) );
+
+ /* Use the "AVP Test - rules" AVP to test the rules */
+ {
+ struct avp * tavp = NULL;
+ struct avp * tempavp = NULL;
+ struct avp * childavp = NULL;
+
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, tavp, 73565, "AVP Test - rules" );
+
+ /* Create a conforming message first */
+ ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 0, "AVP Test - no vendor - f32" );
+ ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - i64" );
+ ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - enumi32" );
+ ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - os" );
+ ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - os" );
+ ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - grouped" );
+
+ /* Check the message is still conform */
+ CHECK( 0, fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ) );
+
+ /* The first avp is optional in fixed position, so remove it and check the message is still OK */
+ CHECK( 0, fd_msg_browse ( tavp, MSG_BRW_FIRST_CHILD, &childavp, NULL) );
+ CHECK( 0, fd_msg_free ( childavp ) );
+ CHECK( 0, fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ) );
+ ADD_AVP( tavp, MSG_BRW_FIRST_CHILD, childavp, 0, "AVP Test - no vendor - f32" );
+
+
+ /* Now break some rules and check it is detected */
+ #define CHECK_CONFLICT( _msg, _error, _conflictavp_name, _conflictavp_vnd ) { \
+ struct fd_pei _pei; \
+ CHECK( EBADMSG, fd_msg_parse_rules( _msg, fd_g_config->cnf_dict, &_pei ) ); \
+ if (_error) { \
+ CHECK( 0, strcmp( _error, _pei.pei_errcode ) ); \
+ } \
+ if ((_conflictavp_name) == NULL) { \
+ CHECK( NULL, _pei.pei_avp); \
+ } else { \
+ struct dict_avp_request _req = { (_conflictavp_vnd), 0, (_conflictavp_name) }; \
+ struct dict_object * _avp; \
+ struct dict_object * _conflict; \
+ CHECK( 1, (_pei.pei_avp) ? 1 : 0 ); \
+ CHECK( 0, fd_msg_model( _pei.pei_avp, &_conflict ) ); \
+ CHECK( 0, fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT)); \
+ CHECK( _avp, _conflict ); \
+ } \
+ }
+
+ /* ABNF :
+ < no vendor - f32 >
+ < i64 >
+ < enumi32 >
+ 2*3 { os }
+ *1 [ enumos ]
+ < grouped >
+ */
+ {
+ /* Test the FIXED_HEAD rules positions: add another AVP before the third */
+ CHECK( 0, fd_msg_browse ( tavp, MSG_BRW_FIRST_CHILD, &tempavp, NULL) ); /* tempavp is the novendor avp */
+ CHECK( 0, fd_msg_browse ( tempavp, MSG_BRW_NEXT, &tempavp, NULL) ); /* tempavp is the i64 avp */
+ ADD_AVP( tempavp, MSG_BRW_NEXT, childavp, 73565, "AVP Test - os" );
+
+ CHECK_CONFLICT( msg, "DIAMETER_MISSING_AVP", "AVP Test - enumi32", 73565 );
+
+ /* Now remove this AVP */
+ CHECK( 0, fd_msg_free ( childavp ) );
+ }
+ {
+ /* Remove the third AVP, same rule must conflict */
+ CHECK( 0, fd_msg_browse ( tempavp, MSG_BRW_NEXT, &childavp, NULL) ); /* childavp is the enumi32 avp */
+ CHECK( 0, fd_msg_free ( childavp ) );
+
+ CHECK_CONFLICT( msg, "DIAMETER_MISSING_AVP", "AVP Test - enumi32", 73565 );
+
+ /* Add the AVP back */
+ ADD_AVP( tempavp, MSG_BRW_NEXT, childavp, 73565, "AVP Test - enumi32" );
+ }
+
+ {
+ /* Test the minimum value in the REQUIRED rule: delete one of the os AVPs */
+ CHECK( 0, fd_msg_browse ( childavp, MSG_BRW_NEXT, &tempavp, NULL) ); /* tempavp is the os avp */
+ CHECK( 0, fd_msg_free ( tempavp ) );
+
+ CHECK_CONFLICT( msg, "DIAMETER_MISSING_AVP", "AVP Test - os", 73565 ); /* The rule requires at least 2 AVP, we have only 1 */
+
+ /* Now add this AVP */
+ ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - os" );
+ }
+ {
+ /* Test the maximum value in the REQUIRED rule: add more of the os AVPs */
+ ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - os" );
+ ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - os" );
+
+ CHECK_CONFLICT( msg, "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES", "AVP Test - os", 73565 ); /* The rule requires at most 3 AVP, we have 4 */
+
+ /* Now delete these AVP */
+ CHECK( 0, fd_msg_free ( tempavp ) );
+ CHECK( 0, fd_msg_browse ( childavp, MSG_BRW_NEXT, &tempavp, NULL) );
+ CHECK( 0, fd_msg_free ( tempavp ) );
+ }
+
+ {
+ /* Test the maximum value in the OPTIONAL rule: add 2 enumos AVPs */
+ ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - enumos" );
+
+ /* The message is still conform */
+ CHECK( 0, fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ) );
+
+ /* Now break the rule */
+ ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - enumos" );
+
+ CHECK_CONFLICT( msg, "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES", "AVP Test - enumos", 73565 );
+
+ /* Now delete this AVP */
+ CHECK( 0, fd_msg_free ( tempavp ) );
+ }
+
+ {
+ /* Test the RULE_FIXED_TAIL rules positions: add another AVP at the end */
+ ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - os" );
+
+ CHECK_CONFLICT( msg, "DIAMETER_MISSING_AVP", "AVP Test - grouped", 73565 );
+
+ /* Now remove this AVP */
+ CHECK( 0, fd_msg_free ( childavp ) );
+ }
+ }
+ }
+
+ /* Test the fd_msg_new_answer_from_req function */
+ {
+ struct dict_object * cmd_model = NULL;
+ struct msg * msg = NULL;
+ struct avp * pi1, *pi2, *avp;
+ char * host1="host1", * host2="host2";
+ union avp_value value;
+ struct msg_hdr * msgdata = NULL;
+
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Test-Command-Request", &cmd_model, ENOENT ) );
+
+ /* Test default behavior without flags */
+ {
+ /* Create a message with some AVPs inside */
+ CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) );
+ CHECK( 0, fd_msg_hdr ( msg, &msgdata ) );
+
+ /* Add a session id */
+ CHECK( 0, fd_msg_new_session( msg, (os0_t)"testmsg", strlen("testmsg") ) );
+
+ /* Create two instances of Proxy-Info */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, pi1, 0, "Proxy-Info");
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, pi2, 0, "Proxy-Info");
+
+ ADD_AVP( pi1, MSG_BRW_LAST_CHILD, avp, 0, "Proxy-State");
+ value.os.data = (os0_t)"ps_pi1";
+ value.os.len = strlen((char *)value.os.data);
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+
+ ADD_AVP( pi2, MSG_BRW_LAST_CHILD, avp, 0, "Proxy-State");
+ value.os.data = (os0_t)"pi2_state";
+ value.os.len = strlen((char *)value.os.data);
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+
+ ADD_AVP( pi1, MSG_BRW_FIRST_CHILD, avp, 0, "Proxy-Host");
+ value.os.data = (os0_t)host1;
+ value.os.len = strlen(host1);
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+
+ ADD_AVP( pi2, MSG_BRW_LAST_CHILD, avp, 0, "Proxy-Host");
+ value.os.data = (os0_t)host2;
+ value.os.len = strlen(host2);
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+
+ ADD_AVP( pi2, MSG_BRW_LAST_CHILD, avp, 73565, "AVP Test - i64");
+ value.i64 = 0x123456789abcdeLL;
+ CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+
+
+ /* Now call the fd_msg_new_answer_from_req function */
+ CHECK( 0, fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, 0 ) );
+
+ /* Check there is a Session-Id AVP */
+ {
+ struct session * sess;
+ int new;
+ CHECK( 0, fd_msg_sess_get(fd_g_config->cnf_dict, msg, &sess, &new) );
+ CHECK( 1, sess == NULL ? 0 : 1 );
+ CHECK( 0, new ? 1 : 0 );
+ }
+
+ /* Check there are two Proxy-Info with the two hosts */
+ {
+ int got_h1 = 0, got_h2=0;
+ CHECK( 0, fd_msg_browse ( msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
+ while(avp) {
+ struct avp_hdr * avpdata = NULL;
+ CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+ if (avpdata->avp_code == AC_PROXY_INFO) {
+ struct avp * iavp;
+ CHECK( 0, fd_msg_browse ( avp, MSG_BRW_FIRST_CHILD, &iavp, NULL) );
+ while(iavp) {
+ struct avp_hdr * iavpdata = NULL;
+ CHECK( 0, fd_msg_avp_hdr ( iavp, &iavpdata ) );
+ if (iavpdata->avp_code == AC_PROXY_HOST) {
+ if (!memcmp(host1, iavpdata->avp_value->os.data, strlen(host1)))
+ got_h1++;
+ if (!memcmp(host2, iavpdata->avp_value->os.data, strlen(host2)))
+ got_h2++;
+ }
+ CHECK( 0, fd_msg_browse ( iavp, MSG_BRW_NEXT, &iavp, NULL) );
+ }
+ }
+
+ CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+ }
+
+ CHECK(1, got_h1);
+ CHECK(1, got_h2);
+ }
+
+ /* Now test the behavior of fd_msg_rescode_set with a grouped AVP */
+ CHECK( 0, fd_msg_rescode_set(msg, "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES", NULL, pi1, 1) );
+
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+
+ TODO("Check the Failed-AVP is as expected");
+ }
+
+ }
+ }
+
+ /* Test the msg_avp_value_interpret and msg_avp_value_encode functions. use the Address type and Host-IP-Address AVPs */
+ {
+ struct dict_object * cer_model = NULL;
+ struct msg * cer = NULL;
+
+ struct dict_object * hia_model = NULL;
+ struct avp *avp4, *avp6;
+ #define TEST_IP4 "192.168.100.101"
+ char buf4[INET_ADDRSTRLEN];
+ #define TEST_IP6 "1111:2222:3333:4444:1234:5678:9abc:def0"
+ char buf6[INET6_ADDRSTRLEN];
+
+ struct sockaddr_storage ss;
+ struct sockaddr_in sin, *psin;
+ struct sockaddr_in6 sin6, *psin6;
+
+ /* Find the CER dictionary object */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer_model, ENOENT ) );
+
+ /* Now find the Host-IP-Address dictionary object */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Host-IP-Address", &hia_model, ENOENT ) );
+
+ /* Create the msg instance */
+ CHECK( 0, fd_msg_new ( cer_model, 0, &cer ) );
+
+ /* Create the avp instances */
+ CHECK( 0, fd_msg_avp_new ( hia_model, 0, &avp4 ) );
+ CHECK( 0, fd_msg_avp_new ( hia_model, 0, &avp6 ) );
+
+ /* Set the value of the IP avp */
+ sin.sin_family = AF_INET;
+ CHECK( 1, inet_pton( AF_INET, TEST_IP4, &sin.sin_addr.s_addr ) );
+ CHECK( 0, fd_msg_avp_value_encode ( &sin, avp4 ) );
+
+ /* Set the value of the IP6 avp */
+ sin6.sin6_family = AF_INET6;
+ CHECK( 1, inet_pton( AF_INET6, TEST_IP6, &sin6.sin6_addr.s6_addr ) );
+ CHECK( 0, fd_msg_avp_value_encode ( &sin6, avp6 ) );
+
+ /* Add these AVPs in the message */
+ CHECK( 0, fd_msg_avp_add( cer, MSG_BRW_LAST_CHILD, avp4) );
+ CHECK( 0, fd_msg_avp_add( cer, MSG_BRW_LAST_CHILD, avp6) );
+
+ /* Create the buffer for this message */
+ CHECK( 0, fd_msg_bufferize( cer, &buf, NULL ) );
+
+ /* Now free the message, we keep only the buffer. */
+ CHECK( 0, fd_msg_free( cer ) );
+
+ /* Check the content of the buffer is correct (skip command header) */
+ CHECK( 0x00, buf[20] ); /* First AVP (IP4) begins: code 257 = 0x00000101 */
+ CHECK( 0x00, buf[21] );
+ CHECK( 0x01, buf[22] );
+ CHECK( 0x01, buf[23] );
+ CHECK( 0x40, buf[24] ); /* flags: M */
+ CHECK( 0x00, buf[25] ); /* length: 8+6 = 0x00000e */
+ CHECK( 0x00, buf[26] );
+ CHECK( 0x0E, buf[27] );
+ CHECK( 0x00, buf[28] ); /* Value: AddressType 1 */
+ CHECK( 0x01, buf[29] );
+ CHECK( 192, buf[30] ); /* 192.168.100.101 */
+ CHECK( 168, buf[31] );
+ CHECK( 100, buf[32] );
+ CHECK( 101, buf[33] );
+
+ CHECK( 0x00, buf[34] ); /* Padding */
+ CHECK( 0x00, buf[35] );
+
+ CHECK( 0x00, buf[36] ); /* Second AVP (IP6) begins: code 257 = 0x00000101 */
+ CHECK( 0x00, buf[37] );
+ CHECK( 0x01, buf[38] );
+ CHECK( 0x01, buf[39] );
+ CHECK( 0x40, buf[40] ); /* flags: M */
+ CHECK( 0x00, buf[41] ); /* length: 8+18 = 0x00001a */
+ CHECK( 0x00, buf[42] );
+ CHECK( 0x1A, buf[43] );
+ CHECK( 0x00, buf[44] ); /* Value: AddressType 2 */
+ CHECK( 0x02, buf[45] );
+ CHECK( 0x11, buf[46] ); /* 1111:2222:3333:4444:1234:5678:9abc:def0 */
+ CHECK( 0x11, buf[47] );
+ CHECK( 0x22, buf[48] );
+ CHECK( 0x22, buf[49] );
+ CHECK( 0x33, buf[50] );
+ CHECK( 0x33, buf[51] );
+ CHECK( 0x44, buf[52] );
+ CHECK( 0x44, buf[53] );
+ CHECK( 0x12, buf[54] );
+ CHECK( 0x34, buf[55] );
+ CHECK( 0x56, buf[56] );
+ CHECK( 0x78, buf[57] );
+ CHECK( 0x9a, buf[58] );
+ CHECK( 0xbc, buf[59] );
+ CHECK( 0xde, buf[60] );
+ CHECK( 0xf0, buf[61] );
+
+ /* Ok, now let's recreate the message */
+ CHECK( 0, fd_msg_parse_buffer( &buf, 64, &cer) );
+ CHECK( 0, fd_msg_parse_dict( cer, fd_g_config->cnf_dict, NULL ) );
+
+ /* Get the pointers to the first and last AVP */
+ CHECK( 0, fd_msg_browse( cer, MSG_BRW_FIRST_CHILD, &avp4, NULL) );
+ CHECK( 0, fd_msg_browse( cer, MSG_BRW_LAST_CHILD, &avp6, NULL) );
+
+ /* Try and interpret the data in the AVPs */
+ CHECK( 0, fd_msg_avp_value_interpret ( avp4, &ss ) );
+ psin = (struct sockaddr_in *)&ss;
+ CHECK( AF_INET, psin->sin_family );
+ CHECK( 0, (inet_ntop( AF_INET, &psin->sin_addr.s_addr, buf4, sizeof(buf4) ) == NULL) ? errno : 0 );
+ CHECK( 0, strcmp( buf4, TEST_IP4 ) );
+
+ CHECK( 0, fd_msg_avp_value_interpret ( avp6, &ss ) );
+ psin6 = (struct sockaddr_in6 *)&ss;
+ CHECK( AF_INET6, psin6->sin6_family );
+ CHECK( 0, (inet_ntop( AF_INET6, &psin6->sin6_addr.s6_addr, buf6, sizeof(buf6) ) == NULL) ? errno : 0 );
+ CHECK( 0, strcasecmp( buf6, TEST_IP6 ) );
+
+ /* Ok, it's done */
+ CHECK( 0, fd_msg_free( cer ) );
+ }
+
+ /* Check proper encoding / decoding for all basic types of AVP */
+ {
+ {
+ struct dict_avp_data avp_data = { 91001, 0, "AVP Test 2 - os", 0, 0, AVP_TYPE_OCTETSTRING };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+ }
+ {
+ struct dict_avp_data avp_data = { 91002, 0, "AVP Test 2 - i32", 0, 0, AVP_TYPE_INTEGER32 };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+ }
+ {
+ struct dict_avp_data avp_data = { 91003, 0, "AVP Test 2 - i64", 0, 0, AVP_TYPE_INTEGER64 };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+ }
+ {
+ struct dict_avp_data avp_data = { 91004, 0, "AVP Test 2 - u32", 0, 0, AVP_TYPE_UNSIGNED32 };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+ }
+ {
+ struct dict_avp_data avp_data = { 91005, 0, "AVP Test 2 - u64", 0, 0, AVP_TYPE_UNSIGNED64 };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+ }
+ {
+ struct dict_avp_data avp_data = { 91006, 0, "AVP Test 2 - f32", 0, 0, AVP_TYPE_FLOAT32 };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+ }
+ {
+ struct dict_avp_data avp_data = { 91007, 0, "AVP Test 2 - f64", 0, 0, AVP_TYPE_FLOAT64 };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+ }
+
+ {
+ struct dict_object * cmd_model = NULL;
+ struct msg * msg = NULL;
+ struct avp * avp = NULL;
+ union avp_value value;
+ struct avp * avpi = NULL;
+ struct avp_hdr * avpdata = NULL;
+ struct msg_hdr * msgdata = NULL;
+
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Test-Command-Request", &cmd_model, ENOENT ) );
+
+ /* Create a message */
+ CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) );
+ CHECK( 0, fd_msg_hdr ( msg, &msgdata ) );
+
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - os" );
+ value.os.data = (unsigned char *) "waaad";
+ value.os.len = 6;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - i32" );
+ value.i32 = 0x123456;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - i32" );
+ value.i32 = -0x123456;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - i64" );
+ value.i64 = 0x11223344556677LL;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - i64" );
+ value.i64 = -0x11223344556677LL;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - u32" );
+ value.u32 = 0xFEDCBA98;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - u64" );
+ value.u64 = 0x123456789abcdef0LL;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - f32" );
+ value.f32 = 2097153.0F;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - f64" );
+ value.f64 = -1099511627777LL;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ /* Ok now bufferize */
+ CHECK( 0, fd_msg_bufferize( msg, &buf, NULL ) );
+
+ /* Test the first bytes */
+ CHECK( 0x01, buf[0] ); /* Version */
+ CHECK( 0x00, buf[1] ); /* Length: 148 = 0x000094 */
+ CHECK( 0x00, buf[2] );
+ CHECK( 0x94, buf[3] );
+ CHECK( 0x80, buf[4] ); /* flags: only "R" is set. */
+ CHECK( 0x01, buf[5] ); /* Command code: 73573 = 0x011F65 */
+ CHECK( 0x1F, buf[6] );
+ CHECK( 0x65, buf[7] );
+ CHECK( 0x00, buf[8] ); /* App ID */
+ CHECK( 0x01, buf[9] );
+ CHECK( 0x1F, buf[10] );
+ CHECK( 0x5E, buf[11] );
+ CHECK( 0x00, buf[12] ); /* hop-by-hop id */
+ CHECK( 0x00, buf[13] );
+ CHECK( 0x00, buf[14] );
+ CHECK( 0x00, buf[15] );
+ CHECK( 0x00, buf[16] ); /* end-to-end id */
+ CHECK( 0x00, buf[17] );
+ CHECK( 0x00, buf[18] );
+ CHECK( 0x00, buf[19] );
+
+ CHECK( 0x00, buf[20] ); /* First AVP (AVP Test 2 - os) begin: code 91001 = 0x00016379 */
+ CHECK( 0x01, buf[21] );
+ CHECK( 0x63, buf[22] );
+ CHECK( 0x79, buf[23] );
+ CHECK( 0x00, buf[24] ); /* flags: 0 */
+ CHECK( 0x00, buf[25] ); /* length: 14 = 0x00000e */
+ CHECK( 0x00, buf[26] );
+ CHECK( 0x0e, buf[27] );
+
+ CHECK( 0x77, buf[28] ); /* "waaad\0" + padding */
+ CHECK( 0x61, buf[29] );
+ CHECK( 0x61, buf[30] );
+ CHECK( 0x61, buf[31] );
+ CHECK( 0x64, buf[32] );
+ CHECK( 0x00, buf[33] );
+ CHECK( 0x00, buf[34] );
+ CHECK( 0x00, buf[35] );
+
+ /* 36 ~ 43 : 2nd AVP header (size at last octet) */
+ CHECK( 0x0c, buf[43] );
+ CHECK( 0x00, buf[44] ); /* 0x123456 stored in integer32 in network byte order */
+ CHECK( 0x12, buf[45] );
+ CHECK( 0x34, buf[46] );
+ CHECK( 0x56, buf[47] );
+
+ /* 48 ~ 55 : next AVP header */
+ CHECK( 0xff, buf[56] ); /* -0x123456 stored in integer32 in network byte order. */
+ CHECK( 0xed, buf[57] ); /* We assume that two's complement is the correct representation, although it's not clearly specified. */
+ CHECK( 0xcb, buf[58] ); /* 00 12 34 56 inversed => FF ED CB A9 */
+ CHECK( 0xaa, buf[59] ); /* then "+1" => FF ED CB AA */
+
+ /* 60 ~ 67 : next header */
+ CHECK( 0x10, buf[67] ); /* (the size) */
+ CHECK( 0x00, buf[68] ); /* 0x11223344556677 in network byte order */
+ CHECK( 0x11, buf[69] );
+ CHECK( 0x22, buf[70] );
+ CHECK( 0x33, buf[71] );
+ CHECK( 0x44, buf[72] );
+ CHECK( 0x55, buf[73] );
+ CHECK( 0x66, buf[74] );
+ CHECK( 0x77, buf[75] );
+
+ /* 76 ~ 83 : next header */
+ CHECK( 0xFF, buf[84] ); /* - 0x11223344556677 (in two's complement) */
+ CHECK( 0xEE, buf[85] ); /* gives FF EE DD CC BB AA 99 89 */
+ CHECK( 0xDD, buf[86] );
+ CHECK( 0xCC, buf[87] );
+ CHECK( 0xBB, buf[88] );
+ CHECK( 0xAA, buf[89] );
+ CHECK( 0x99, buf[90] );
+ CHECK( 0x89, buf[91] );
+
+ /* 92 ~ 99 : next header */
+ CHECK( 0x0c, buf[99] ); /* (the size) */
+ CHECK( 0xFE, buf[100]); /* 0xFEDCBA98 in network byte order */
+ CHECK( 0xDC, buf[101]);
+ CHECK( 0xBA, buf[102]);
+ CHECK( 0x98, buf[103]);
+
+ /* 104 ~ 111 : next header */
+ CHECK( 0x10, buf[111] ); /* (the size) */
+ CHECK( 0x12, buf[112]); /* 0x123456789abcdef0LL in network byte order */
+ CHECK( 0x34, buf[113]);
+ CHECK( 0x56, buf[114]);
+ CHECK( 0x78, buf[115]);
+ CHECK( 0x9a, buf[116]);
+ CHECK( 0xbc, buf[117]);
+ CHECK( 0xde, buf[118]);
+ CHECK( 0xf0, buf[119]);
+
+ /* 120 ~ 127 : next header */
+ CHECK( 0x0c, buf[127] ); /* (the size) */
+ CHECK( 0x4a, buf[128]); /* http://en.wikipedia.org/wiki/IEEE_754-1985 to get descvription of the format */
+ CHECK( 0x00, buf[129]); /* v = 2097153 = 2^21 + 2 ^ 0; sign : "+", 2^21 <= v < 2^22 => exponent = 21; biaised on 8 bits => 21 + 127 => 100 1010 0 */
+ CHECK( 0x00, buf[130]); /* v = (+1) * (1 ^ 21) * ( 1 + 2^-21 ) => significand 000 0000 0000 0000 0000 0100 */
+ CHECK( 0x04, buf[131]); /* result: 4a 00 00 04 */
+
+ /* 132 ~ 139 : next header */
+ CHECK( 0x10, buf[139] ); /* (the size) */
+ CHECK( 0xc2, buf[140]); /* -1099511627777L ( 2^40 + 1 ) in network byte order */
+ CHECK( 0x70, buf[141]); /* sign: - => most significant bit = 1 */
+ CHECK( 0x00, buf[142]); /* 2^40 <= v < 2^41 => biaised exponent on 11 bits: 1023 + 40: 100 0010 0111 */
+ CHECK( 0x00, buf[143]); /* significand: 1 + 2^-40 => 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 */
+ CHECK( 0x00, buf[144]); /* result: c2 70 00 00 00 00 10 00 */
+ CHECK( 0x00, buf[145]);
+ CHECK( 0x10, buf[146]);
+ CHECK( 0x00, buf[147]);
+
+
+
+ /* Okay, now delete the message and parse the buffer, then check we obtain the same values back */
+ #if 0
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+ #endif
+ CHECK( 0, fd_msg_free( msg ) );
+
+ CHECK( 0, fd_msg_parse_buffer( &buf, 148, &msg) );
+ CHECK( 0, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+ #if 0
+ fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+ #endif
+
+ CHECK( 0, fd_msg_browse ( msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
+ CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+ CHECK( 6, avpdata->avp_value->os.len );
+ CHECK( 'w', (char)(avpdata->avp_value->os.data[0]) );
+ CHECK( 'a', (char)(avpdata->avp_value->os.data[1]) );
+ CHECK( 'd', (char)(avpdata->avp_value->os.data[4]) );
+ CHECK( '\0', (char)(avpdata->avp_value->os.data[5]) );
+
+ CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+ CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+ CHECK( 0x123456, avpdata->avp_value->i32 );
+
+ CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+ CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+ CHECK( -0x123456, avpdata->avp_value->i32 );
+
+ CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+ CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+ CHECK( 0x11223344556677LL, avpdata->avp_value->i64 );
+
+ CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+ CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+ CHECK( -0x11223344556677LL, avpdata->avp_value->i64 );
+
+ CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+ CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+ CHECK( 0xFEDCBA98, avpdata->avp_value->u32 );
+
+ CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+ CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+ CHECK( 0x123456789abcdef0LL, avpdata->avp_value->u64 );
+
+ CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+ CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+ CHECK( 2097153.0F, avpdata->avp_value->f32 );
+
+ CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+ CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+ CHECK( -1099511627777LL, avpdata->avp_value->f64 );
+
+ CHECK( 0, fd_msg_free( msg ) );
+ }
+ }
+
+ /* That's all for the tests yet */
+ PASSTEST();
+}
+
diff --git a/tests/testmesg_stress.c b/tests/testmesg_stress.c
new file mode 100644
index 0000000..310a9d2
--- /dev/null
+++ b/tests/testmesg_stress.c
@@ -0,0 +1,689 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 "tests.h"
+#include <dirent.h>
+#include <libgen.h>
+#include <dlfcn.h>
+
+#ifndef BUILD_DIR
+#error "Missing BUILD_DIR information"
+#endif /* BUILD_DIR */
+
+
+/* The number of times each operation is repeated to measure the average operation time */
+#define DEFAULT_NUMBER_OF_SAMPLES 100000
+
+static void display_result(int nr, struct timespec * start, struct timespec * end, char * fct, char * type, char *op)
+{
+ long double dur = (long double)end->tv_sec + (long double)end->tv_nsec/1000000000;
+ dur -= (long double)start->tv_sec + (long double)start->tv_nsec/1000000000;
+ long double thrp = (long double)nr / dur;
+ printf("%-19s: %d %-8s %-7s in %.6LFs (%.1LFmsg/s)\n", fct, nr, type, op, dur, thrp);
+}
+
+struct ext_info {
+ struct fd_list chain; /* link in the list */
+ void *handler; /* object returned by dlopen() */
+ int (*init_cb)(int, int, char *);
+ char *ext_name; /* points to the extension name, either inside depends, or basename(filename) */
+ int free_ext_name; /* must be freed if it was malloc'd */
+ const char **depends; /* names of the other extensions this one depends on (if provided) */
+};
+
+static void load_all_extensions(char * prefix)
+{
+ DIR *dir;
+ struct dirent *dp;
+ char fullname[512];
+ int pathlen;
+ struct fd_list all_extensions = FD_LIST_INITIALIZER(all_extensions);
+ struct fd_list ext_with_depends = FD_LIST_INITIALIZER(ext_with_depends);
+
+ /* Find all extensions which have been compiled along the test */
+ LOG_D("Loading %s*.fdx from: '%s'", BUILD_DIR "/extensions", prefix ?: "");
+ CHECK( 0, (dir = opendir (BUILD_DIR "/extensions")) == NULL ? 1 : 0 );
+ pathlen = snprintf(fullname, sizeof(fullname), BUILD_DIR "/extensions/");
+
+ while ((dp = readdir (dir)) != NULL) {
+ char * dot = strrchr(dp->d_name, '.');
+ if (dot && ((!prefix) || !(strncmp(dp->d_name, prefix, strlen(prefix)))) && (!(strcmp(dot, ".fdx")))) {
+ /* We found a file with name dict_*.fdx, attempt to load it */
+ struct ext_info * new = malloc(sizeof(struct ext_info));
+ CHECK( 1, new ? 1:0);
+ fd_list_init(&new->chain, new);
+
+ snprintf(fullname + pathlen, sizeof(fullname) - pathlen, "%s", dp->d_name);
+
+ LOG_D("Extension: '%s'", dp->d_name);
+
+ /* load */
+ new->handler = dlopen(fullname, RTLD_NOW | RTLD_GLOBAL);
+ if (!new->handler) {
+ TRACE_DEBUG(INFO, "Unable to load '%s': %s.", fullname, dlerror());
+ }
+ CHECK( 0, new->handler == NULL ? 1 : 0 );
+
+ /* resolve entry */
+ new->init_cb = dlsym( new->handler, "fd_ext_init" );
+ if (!new->init_cb) {
+ TRACE_DEBUG(INFO, "No 'fd_ext_init' entry point in '%s': %s.", fullname, dlerror());
+ }
+ CHECK( 0, new->init_cb == NULL ? 1 : 0 );
+
+ new->depends = dlsym( new->handler, "fd_ext_depends" );
+ if (new->depends) {
+ new->ext_name = (char *)new->depends[0];
+ new->free_ext_name = 0;
+ if ( new->depends[1] ) {
+ fd_list_insert_before(&ext_with_depends, &new->chain);
+ } else {
+ fd_list_insert_before(&all_extensions, &new->chain);
+ }
+ } else {
+ new->ext_name = strdup(basename(dp->d_name));
+ new->free_ext_name = 1;
+ fd_list_insert_before(&all_extensions, &new->chain);
+ }
+
+ }
+ }
+
+ /* Now, reorder the list by dependencies */
+ {
+ int count, prevcount = 0;
+ struct fd_list * li;
+ do {
+ count = 0;
+ for (li=ext_with_depends.next; li != &ext_with_depends; li=li->next) {
+ struct ext_info * e = li->o;
+ int d;
+ int satisfied=0;
+
+ /* Can we satisfy all dependencies? */
+ for (d=1; ;d++) {
+ struct fd_list * eli;
+ if (!e->depends[d]) {
+ satisfied = 1;
+ break;
+ }
+
+ /* can we find this dependency in the list? */
+ for (eli=all_extensions.next; eli != &all_extensions; eli = eli->next) {
+ struct ext_info * de = eli->o;
+ if (!strcasecmp(de->ext_name, e->depends[d]))
+ break; /* this dependency is satisfied */
+ }
+
+ if (eli == &all_extensions) {
+ satisfied = 0;
+ break;
+ }
+ }
+
+ if (satisfied) {
+ /* OK, we have all our dependencies in the list */
+ li=li->prev;
+ fd_list_unlink(&e->chain);
+ fd_list_insert_before(&all_extensions, &e->chain);
+ } else {
+ count++;
+ }
+ }
+
+ if (prevcount && (prevcount == count)) {
+ LOG_E("Some extensions cannot have their dependencies satisfied, e.g.: %s", ((struct ext_info *)ext_with_depends.next->o)->ext_name);
+ CHECK(0, 1);
+ }
+ prevcount = count;
+
+ if (FD_IS_LIST_EMPTY(&ext_with_depends))
+ break;
+ } while (1);
+ }
+
+ /* Now, load all the extensions */
+ {
+ struct fd_list * li;
+ for (li=all_extensions.next; li != &all_extensions; li=li->next) {
+ struct ext_info * e = li->o;
+ int ret = (*e->init_cb)( FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, NULL );
+ LOG_N("Initializing extension '%s': %s", e->ext_name, ret ? strerror(ret) : "Success");
+ }
+ }
+
+ /* We should probably clean the list here ? */
+}
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+ struct msg * acr = NULL;
+ unsigned char * buf = NULL;
+
+ int dictionaries_loaded = 0;
+
+ test_parameter = DEFAULT_NUMBER_OF_SAMPLES;
+
+ /* First, initialize the daemon modules */
+ INIT_FD();
+ CHECK( 0, fd_queues_init() );
+ CHECK( 0, fd_msg_init() );
+ CHECK( 0, fd_rtdisp_init() );
+
+
+ {
+ struct dict_object * acr_model = NULL;
+
+ /* Now find the ACR dictionary object */
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &acr_model, ENOENT ) );
+
+ /* Create the instance, using the templates */
+ CHECK( 0, fd_msg_new ( acr_model, 0, &acr ) );
+ }
+
+ /* Now let's create some additional Dictionary objects for the test */
+ {
+ /* The constant values used here are totally arbitrary chosen */
+ struct dict_object * vendor;
+ {
+ struct dict_vendor_data vendor_data = { 73565, "Vendor test" };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_VENDOR, &vendor_data , NULL, &vendor ) );
+ }
+
+ {
+ struct dict_application_data app_data = { 73566, "Application test" };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_APPLICATION, &app_data , vendor, NULL ) );
+ }
+
+ {
+ struct dict_avp_data avp_data = { 73567, 0, "AVP Test - no vendor - f32", 0, 0, AVP_TYPE_FLOAT32 };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+ }
+
+ {
+ struct dict_avp_data avp_data = { 139103, 0, "AVP Test - no vendor - f64", 0, 0, AVP_TYPE_FLOAT64 };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+ }
+
+ {
+ struct dict_object * type = NULL;
+ struct dict_type_data type_data = { AVP_TYPE_INTEGER64, "Int64 test" };
+ struct dict_avp_data avp_data = { 73568, 73565, "AVP Test - i64", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_INTEGER64 };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+ }
+
+ {
+ struct dict_object * type = NULL;
+ struct dict_type_data type_data = { AVP_TYPE_INTEGER32, "Enum32 test" };
+ struct dict_enumval_data val1 = { "i32 const test (val 1)", { .i32 = 1 } };
+ struct dict_enumval_data val2 = { "i32 const test (val 2)", { .i32 = 2 } };
+ struct dict_enumval_data val3 = { "i32 const test (val -5)",{ .i32 = -5 } };
+ struct dict_avp_data avp_data = { 73569, 73565, "AVP Test - enumi32", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_INTEGER32 };
+
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val1 , type, NULL ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val2 , type, NULL ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val3 , type, NULL ) );
+ }
+
+ {
+ struct dict_object * type = NULL;
+ struct dict_type_data type_data = { AVP_TYPE_OCTETSTRING, "OS test" };
+ struct dict_avp_data avp_data = { 73570, 73565, "AVP Test - os", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING };
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+ }
+
+ {
+ struct dict_object * type = NULL;
+ struct dict_type_data type_data = { AVP_TYPE_OCTETSTRING, "OS enum test" };
+ struct dict_enumval_data val1 = { "os const test (Test)", { .os = { (unsigned char *)"Test", 4 } } };
+ struct dict_enumval_data val2 = { "os const test (waaad)", { .os = { (unsigned char *)"waaad", 5 } } };
+ struct dict_enumval_data val3 = { "os const test (waa)", { .os = { (unsigned char *)"waaad", 3 } } };
+ struct dict_avp_data avp_data = { 73571, 73565, "AVP Test - enumos", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING };
+
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val1 , type, NULL ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val2 , type, NULL ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val3 , type, NULL ) );
+ }
+
+ {
+ struct dict_object * gavp = NULL;
+ struct dict_avp_data avp_data = { 73572, 73565, "AVP Test - grouped", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_GROUPED };
+
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, &gavp ) );
+
+ /* Macro to search AVP and create a rule */
+ #define ADD_RULE( _parent, _vendor, _avpname, _pos, _min, _max, _ord ) { \
+ struct dict_object * _avp = NULL; \
+ struct dict_avp_request _req = { (_vendor), 0, (_avpname) }; \
+ struct dict_rule_data _data; \
+ CHECK( 0, fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT));\
+ _data.rule_avp = _avp; \
+ _data.rule_position = (_pos); \
+ _data.rule_order = (_ord); \
+ _data.rule_min = (_min); \
+ _data.rule_max = (_max); \
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_RULE, &_data , (_parent), NULL ) ); \
+ }
+
+ ADD_RULE(gavp, 73565, "AVP Test - os", RULE_OPTIONAL, -1, -1, 0);
+
+ }
+
+ {
+ struct dict_object * application = NULL;
+ struct dict_object * command = NULL;
+ struct dict_cmd_data cmd_data = { 73573, "Test-Command-Request", CMD_FLAG_REQUEST, CMD_FLAG_REQUEST };
+
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Application test", &application, ENOENT ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd_data , application, &command ) );
+ ADD_RULE(command, 0, "AVP Test - no vendor - f32", RULE_FIXED_HEAD, -1, 1, 1);
+ ADD_RULE(command, 73565, "AVP Test - i64", RULE_REQUIRED, -1, -1, 0);
+ ADD_RULE(command, 73565, "AVP Test - enumi32", RULE_OPTIONAL, -1, -1, 0);
+ ADD_RULE(command, 73565, "AVP Test - os", RULE_OPTIONAL, -1, -1, 0);
+ ADD_RULE(command, 73565, "AVP Test - enumos", RULE_OPTIONAL, -1, -1, 0);
+ ADD_RULE(command, 73565, "AVP Test - grouped", RULE_OPTIONAL, -1, -1, 0);
+ }
+
+ {
+ struct dict_object * application = NULL;
+ struct dict_object * command = NULL;
+ struct dict_cmd_data cmd_data = { 73573, "Test-Command-Answer", CMD_FLAG_REQUEST, 0 };
+
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Application test", &application, ENOENT ) );
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd_data , application, &command ) );
+ }
+
+ {
+ struct dict_object * gavp = NULL;
+ struct dict_avp_data avp_data = { 73574, 73565, "AVP Test - rules", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_GROUPED };
+
+ CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, &gavp ) );
+
+ ADD_RULE(gavp, 0, "AVP Test - no vendor - f32", RULE_FIXED_HEAD, 0, 1, 1);
+ ADD_RULE(gavp, 73565, "AVP Test - i64", RULE_FIXED_HEAD, -1, 1, 2);
+ ADD_RULE(gavp, 73565, "AVP Test - enumi32", RULE_FIXED_HEAD, -1, 1, 3);
+ ADD_RULE(gavp, 73565, "AVP Test - os", RULE_REQUIRED, 2, 3, 0);
+ ADD_RULE(gavp, 73565, "AVP Test - enumos", RULE_OPTIONAL, 0, 1, 0);
+ ADD_RULE(gavp, 73565, "AVP Test - grouped", RULE_FIXED_TAIL, -1, 1, 1);
+ /* ABNF :
+ < no vendor - f32 >
+ < i64 >
+ < enumi32 >
+ 2*3 { os }
+ *1 [ enumos ]
+ < grouped >
+ */
+ }
+ }
+
+ /* Now create some values and check the length is correctly handled */
+ {
+ struct dict_object * cmd_model = NULL;
+ struct msg * msg = NULL;
+ struct dict_object * avp_model = NULL;
+ union avp_value value;
+
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Test-Command-Request", &cmd_model, ENOENT ) );
+
+ /* Check the sizes are handled properly */
+ {
+ struct avp * avpi = NULL;
+ struct avp * avpch = NULL;
+ struct msg_hdr * msgdata = NULL;
+ #define ADD_AVP( _parent, _position, _avpi, _avpvendor, _avpname) { \
+ struct dict_object * _avp = NULL; \
+ struct dict_avp_request _req = { (_avpvendor), 0, (_avpname) }; \
+ CHECK( 0, fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT));\
+ CHECK( 0, fd_msg_avp_new ( _avp, 0, &_avpi ) ); \
+ CHECK( 0, fd_msg_avp_add ( (_parent), (_position), _avpi ) ); \
+ }
+ /* Create a message with many AVP inside */
+ CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) );
+ CHECK( 0, fd_msg_hdr ( msg, &msgdata ) );
+
+ /* Avp no vendor, float32 => size = 12 */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test - no vendor - f32" );
+ value.f32 = 3.1415;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ /* Add a vendor AVP, integer64 => size = 20 */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - i64" );
+ value.i64 = 0x123456789abcdeLL;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ /* Add an AVP with an enum value */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" );
+ {
+ struct dict_object * type_model = NULL;
+ struct dict_object * value_model = NULL;
+ struct dict_enumval_request request;
+
+ CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+ memset(&request, 0, sizeof(request));
+ request.type_obj = type_model;
+ request.search.enum_name = "i32 const test (val 2)";
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+ CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+ }
+
+ /* Add an AVP with an enum value, negative */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" );
+ {
+ struct dict_object * type_model = NULL;
+ struct dict_object * value_model = NULL;
+ struct dict_enumval_request request;
+
+ CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+ memset(&request, 0, sizeof(request));
+ request.type_obj = type_model;
+ request.search.enum_name = "i32 const test (val -5)";
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+ CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+ }
+
+ /* Now add a value which is not a constant into an enumerated AVP */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" );
+ value.i32 = -10;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+ /* Add an octetstring AVP */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - os" );
+ {
+ unsigned char buf[90];
+ memcpy(&buf, "This\0 is a buffer of dat\a. It is not a string so we can have any c\0ntr\0l character here...\0\0", 89);
+ value.os.data = buf;
+ value.os.len = 89;
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+ memset(&buf, 0, sizeof(buf)); /* Test that the OS value is really copied */
+ }
+
+ /* Add an octetstring from an enumerated constant */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumos" );
+ {
+ struct dict_object * type_model = NULL;
+ struct dict_object * value_model = NULL;
+ struct dict_enumval_request request;
+
+ CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+ memset(&request, 0, sizeof(request));
+ request.type_obj = type_model;
+ request.search.enum_name = "os const test (waaad)";
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+ CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+ }
+
+ /* Add an octetstring from an enumerated constant */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumos" );
+ {
+ struct dict_object * type_model = NULL;
+ struct dict_object * value_model = NULL;
+ struct dict_enumval_request request;
+
+ CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+ memset(&request, 0, sizeof(request));
+ request.type_obj = type_model;
+ request.search.enum_name = "os const test (waa)";
+ CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+ CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+ CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+ }
+
+ /* Now test the grouped AVPs */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - grouped" );
+ ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+ {
+ value.os.data = (unsigned char *)"12345678";
+ value.os.len = 8;
+ CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+ }
+ ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+ {
+ value.os.data = (unsigned char *)"123456789";
+ value.os.len = 9;
+ CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+ }
+
+ /* Add another similar grouped AVP, to have lot of padding */
+ ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - grouped" );
+ ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+ {
+ value.os.data = (unsigned char *)"1";
+ value.os.len = 1;
+ CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+ }
+ ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+ {
+ value.os.data = (unsigned char *)"1234567";
+ value.os.len = 7;
+ CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+ }
+
+ /* Set the application to the test application: 73566 */
+ msgdata->msg_appl = 73566;
+
+ /* Set the hop-by-hop ID to a random value: 0x4b44b41d */
+ msgdata->msg_hbhid = 0x4b44b41d;
+ /* Set the end-to-end ID to a random value: 0xe2ee2e1d */
+ msgdata->msg_eteid = 0xe2ee2e1d;
+ }
+
+ CHECK( 0, fd_msg_bufferize( msg, &buf, NULL ) );
+
+ LOG_D( "Test message: %s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, NULL, 0, 1));
+
+ /* Now free the message, we keep only the buffer. */
+ CHECK( 0, fd_msg_free( msg ) );
+
+ }
+
+ /* We have our "buf" now, length is 344 -- cf. testmesg.c. */
+redo:
+ /* Test the throughput of the different functions function */
+ {
+ struct stress_struct {
+ struct msg * m;
+ uint8_t * b;
+ } * stress_array;
+ int i;
+ struct timespec start, end;
+
+ /* Create the copies of the message buffer */
+ stress_array = calloc(test_parameter, sizeof(struct stress_struct));
+ CHECK( stress_array ? 1 : 0, 1);
+
+ for (i=0; i < test_parameter; i++) {
+ stress_array[i].b = malloc(344);
+ if (!stress_array[i].b)
+ break;
+ memcpy(stress_array[i].b, buf, 344);
+ }
+ CHECK( test_parameter, i ); /* if false, a malloc failed */
+
+ /* fd_msg_parse_buffer */
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+
+ /* Test the msg_parse_buffer function */
+ for (i=0; i < test_parameter; i++) {
+ if (0 != fd_msg_parse_buffer( &stress_array[i].b, 344, &stress_array[i].m) )
+ break;
+ }
+ CHECK( test_parameter, i ); /* if false, a call failed */
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+ display_result(test_parameter, &start, &end, "fd_msg_parse_buffer", "buffers", "parsed");
+
+ /* fd_msg_parse_dict */
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+
+ /* Test the fd_msg_parse_dict function */
+ for (i=0; i < test_parameter; i++) {
+ if (0 != fd_msg_parse_dict( stress_array[i].m, fd_g_config->cnf_dict, NULL ) )
+ break;
+ }
+ CHECK( test_parameter, i ); /* if false, a call failed */
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+ display_result(test_parameter, &start, &end, "fd_msg_parse_dict", "messages", "parsed");
+
+
+ /* fd_msg_parse_rules */
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+
+ /* Test the fd_msg_parse_rules function */
+ for (i=0; i < test_parameter; i++) {
+ if (0 != fd_msg_parse_rules( stress_array[i].m, fd_g_config->cnf_dict, NULL ) )
+ break;
+ }
+ CHECK( test_parameter, i ); /* if false, a call failed */
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+ display_result(test_parameter, &start, &end, "fd_msg_parse_rules", "messages", "parsed");
+
+
+ /* fd_msg_new_answer_from_req (0) */
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+
+ /* Test the fd_msg_new_answer_from_req function */
+ for (i=0; i < test_parameter; i++) {
+ if (0 != fd_msg_new_answer_from_req( fd_g_config->cnf_dict, &stress_array[i].m, 0 ) )
+ break;
+ }
+ CHECK( test_parameter, i ); /* if false, a call failed */
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+ display_result(test_parameter, &start, &end, "new_answer(normal)", "messages", "created");
+
+ /* unlink answers and go back to request messages */
+ for (i=0; i < test_parameter; i++) {
+ struct msg * ans = stress_array[i].m;
+ if (0 != fd_msg_answ_getq( ans, &stress_array[i].m ) )
+ break;
+ if (0 != fd_msg_answ_detach( ans ) )
+ break;
+ fd_msg_free( ans );
+ }
+ CHECK( test_parameter, i ); /* if false, a call failed */
+
+
+ /* fd_msg_new_answer_from_req (MSGFL_ANSW_ERROR) */
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+
+ /* Test the fd_msg_new_answer_from_req function */
+ for (i=0; i < test_parameter; i++) {
+ if ( 0 != fd_msg_new_answer_from_req( fd_g_config->cnf_dict, &stress_array[i].m, MSGFL_ANSW_ERROR ) )
+ break;
+ }
+ CHECK( test_parameter, i ); /* if false, a call failed */
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+ display_result(test_parameter, &start, &end, "new_answer(error)", "messages", "created");
+
+ /* unlink answers and go back to request messages */
+ for (i=0; i < test_parameter; i++) {
+ struct msg * ans = stress_array[i].m;
+ if (0 != fd_msg_answ_getq( ans, &stress_array[i].m ) )
+ break;
+ if (0 != fd_msg_answ_detach( ans ) )
+ break;
+ fd_msg_free( ans );
+ }
+
+
+ /* fd_msg_bufferize */
+
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+
+ /* Test the fd_msg_bufferize function */
+ for (i=0; i < test_parameter; i++) {
+ size_t len = 0;
+ if (0 != fd_msg_bufferize( stress_array[i].m, &stress_array[i].b, &len ) )
+ break;
+ }
+ CHECK( test_parameter, i ); /* if false, a call failed */
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+ display_result(test_parameter, &start, &end, "fd_msg_bufferize", "buffers", "created");
+
+
+ /* fd_msg_free */
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+
+ /* Free those messages */
+ for (i=0; i < test_parameter; i++) {
+ fd_msg_free( stress_array[i].m );
+ }
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+ display_result(test_parameter, &start, &end, "fd_msg_free", "messages", "freed");
+
+
+ for (i=0; i < test_parameter; i++) {
+ free(stress_array[i].b);
+ }
+ free(stress_array);
+ }
+
+ if (!dictionaries_loaded) {
+ load_all_extensions("dict_");
+ dictionaries_loaded = 1;
+ printf("Loaded all dictionary extensions, restarting...\n");
+ goto redo;
+ }
+
+ /* That's all for the tests yet */
+ PASSTEST();
+}
+
diff --git a/tests/testostr.c b/tests/testostr.c
new file mode 100644
index 0000000..14a64c5
--- /dev/null
+++ b/tests/testostr.c
@@ -0,0 +1,202 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* Copyright (c) 2011, 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 "tests.h"
+
+#define TEST_STR (os0_t)"This is my test string (with extra unused data)"
+
+/* The following string contains UTF-8 encoded characters (Chinese characters) */
+#define TEST_IDN_UTF8 "freeDiameter.ä¸å›½"
+#define TEST_IDN_CONV "freeDiameter.xn--fiqs8s"
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+ /* First, initialize the daemon modules */
+ INIT_FD();
+
+ /* Check the hash function */
+ {
+ uint8_t buf[30];
+
+ uint32_t hash = fd_os_hash(TEST_STR, CONSTSTRLEN(TEST_STR)); /* reference value */
+
+ /* Check that a hash of a substring / surstring is different */
+ CHECK( 1, hash != fd_os_hash(TEST_STR, CONSTSTRLEN(TEST_STR) - 1) ? 1 : 0 );
+ CHECK( 1, hash != fd_os_hash(TEST_STR, CONSTSTRLEN(TEST_STR) + 1) ? 1 : 0 );
+
+ /* Check alignment of the string is not important */
+ memcpy(buf + 4, TEST_STR, CONSTSTRLEN(TEST_STR));
+ CHECK( hash, fd_os_hash(buf + 4, CONSTSTRLEN(TEST_STR)) );
+
+ memcpy(buf + 3, TEST_STR, CONSTSTRLEN(TEST_STR));
+ CHECK( hash, fd_os_hash(buf + 3, CONSTSTRLEN(TEST_STR)) );
+
+ memcpy(buf + 2, TEST_STR, CONSTSTRLEN(TEST_STR));
+ CHECK( hash, fd_os_hash(buf + 2, CONSTSTRLEN(TEST_STR)) );
+
+ memcpy(buf + 1, TEST_STR, CONSTSTRLEN(TEST_STR));
+ CHECK( hash, fd_os_hash(buf + 1, CONSTSTRLEN(TEST_STR)) );
+ }
+
+ /* Check the Diameter Identity functions */
+ {
+ char * res;
+ size_t len=0;
+
+ /* A valid ASCII domain name */
+ res = TEST_IDN_CONV;
+ CHECK( 0, fd_os_validate_DiameterIdentity(&res, &len, 1) );
+ CHECK( 0, strcasecmp(res, TEST_IDN_CONV) ); /* the function does not change a valid DN */
+ CHECK( 0, fd_os_validate_DiameterIdentity(&res, &len, 0) );
+ CHECK( 0, strcasecmp(res, TEST_IDN_CONV) );
+ CHECK( CONSTSTRLEN(TEST_IDN_CONV), len );
+ free(res);
+
+ /* Now, an invalid string */
+ res = TEST_IDN_UTF8;
+ len = 0;
+
+ #ifdef DIAMID_IDNA_IGNORE
+
+ /* The UTF-8 chars are considered valid */
+ CHECK( 1, fd_os_is_valid_DiameterIdentity((os0_t)TEST_IDN_UTF8, CONSTSTRLEN(TEST_IDN_UTF8) ) );
+
+ /* The string should be passed unmodified */
+ CHECK( 0, fd_os_validate_DiameterIdentity(&res, &len, 1) );
+ CHECK( 0, strcasecmp(res, TEST_IDN_UTF8) );
+ CHECK( 0, fd_os_cmp(res, len, TEST_IDN_UTF8, CONSTSTRLEN(TEST_IDN_UTF8)) );
+ CHECK( 0, fd_os_almostcasesrch(res, len, TEST_IDN_UTF8, CONSTSTRLEN(TEST_IDN_UTF8), NULL) );
+ CHECK( 0, fd_os_validate_DiameterIdentity(&res, &len, 0) );
+ CHECK( 0, strcasecmp(res, TEST_IDN_UTF8) );
+ CHECK( CONSTSTRLEN(TEST_IDN_UTF8), len );
+ free(res);
+
+ #else /* DIAMID_IDNA_IGNORE */
+
+ /* The UTF-8 chars are recognized as invalid DiameterIdentity */
+ CHECK( 0, fd_os_is_valid_DiameterIdentity((os0_t)TEST_IDN_UTF8, CONSTSTRLEN(TEST_IDN_UTF8) ));
+
+ # ifdef DIAMID_IDNA_REJECT
+
+ /* The string must be rejected */
+ CHECK( EINVAL, fd_os_validate_DiameterIdentity(&res, &len, 1) );
+
+ # else /* DIAMID_IDNA_REJECT */
+
+ /* The string should be transformed into TEST_IDN_CONV */
+ CHECK( 0, fd_os_validate_DiameterIdentity(&res, &len, 1) );
+ CHECK( 0, strcasecmp(res, TEST_IDN_CONV) );
+ CHECK( CONSTSTRLEN(TEST_IDN_CONV), len );
+ free(res);
+
+ # endif /* DIAMID_IDNA_REJECT */
+ #endif /* DIAMID_IDNA_IGNORE */
+
+ }
+
+ {
+ /* test fd_os_cmp and fd_os_almostcasesrch and that they are compatible */
+ char *t1 = "a";
+ char *t2 = "b";
+ char *t3 = "C";
+ char *t4 = "d";
+ char *t5 = "aa";
+ char *t6 = "aB";
+ char *t7 = "Ac";
+ char *t8 = "aD";
+ char *t9 = "AAA";
+
+ char *t5b = "Aa";
+ char *t6b = "ab";
+
+ /* First, create a list with all the elements in order given by fd_os_cmp */
+ char *t[] = { t1, t2, t3, t4, t5, t6,t7, t8, t9 };
+ int i;
+ struct fd_list *li, l = FD_LIST_INITIALIZER(l);
+ for (i = 0; i < sizeof(t) / sizeof(t[0]); i++) {
+ /* insert t[i] */
+ struct fd_list *n = malloc(sizeof(struct fd_list));
+ CHECK( 1, n ? 1 : 0 );
+ fd_list_init(n, t[i]);
+ for (li = l.next; li != &l; li = li->next) {
+ if ( fd_os_cmp(t[i], strlen(t[i]), li->o, strlen(li->o)) < 0 )
+ break;
+ }
+ fd_list_insert_before(li, n);
+ }
+ /* in principle the result is: [ "C", "a", "b", "d", "Ac", "aB", "aD", "aa", "AAA" ] */
+
+ /* Since there is no equal value in the list (even case-insensitive), check that the order is valid also for the caseinsensitive variant */
+ for (li = l.next; li != l.prev; li = li->next) {
+ CHECK( 1, fd_os_almostcasesrch(li->o, strlen(li->o), li->next->o, strlen(li->next->o), NULL) < 0 ? 1 : 0 );
+ }
+
+ /* Now check that we can case-insentively find t5b and t6b to be equal to t5 and t6 resp. (this is how we use it in the daemon) */
+ for (li = l.next; li != &l; li = li->next) {
+ int cont, cmp;
+ cmp = fd_os_almostcasesrch(t5b, strlen(t5b), li->o, strlen(li->o), &cont);
+ TRACE_DEBUG(FULL, "Comp '%s' : %d, %d", (char *)li->o, cmp, cont);
+ if (cmp == 0)
+ break;
+ if (!cont)
+ break;
+ }
+ CHECK( li->o, t5 );
+
+ for (li = l.next; li != &l; li = li->next) {
+ int cont, cmp;
+ cmp = fd_os_almostcasesrch(t6b, strlen(t6b), li->o, strlen(li->o), &cont);
+ TRACE_DEBUG(FULL, "Comp '%s' : %d, %d", (char *)li->o, cmp, cont);
+ if (cmp == 0)
+ break;
+ if (!cont)
+ break;
+ }
+ CHECK( li->o, t6 );
+
+
+ /* done */
+ while (!FD_IS_LIST_EMPTY(&l)) {
+ li = l.next;
+ fd_list_unlink(li);
+ free(li);
+ }
+ }
+
+ /* That's all for the tests yet */
+ PASSTEST();
+}
+
diff --git a/tests/testpeers.c b/tests/testpeers.c
new file mode 100644
index 0000000..50d252a
--- /dev/null
+++ b/tests/testpeers.c
@@ -0,0 +1,78 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 "tests.h"
+
+const char * ids[] = { "b11", "b14", "b1", "b4" };
+#define DomainName "localdomain"
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+ /* First, initialize the daemon modules */
+ INIT_FD();
+
+ /* Create 4 peers with these ids */
+ {
+ int i;
+ struct peer_info inf;
+ char locid[255];
+ memset(&inf, 0, sizeof(inf));
+ inf.pi_diamid = (char *)locid;
+ for (i=0; i < sizeof(ids) / sizeof(ids[0]); i++) {
+ snprintf(locid, sizeof(locid), "%s." DomainName, ids[i]);
+ CHECK( 0, fd_peer_add(&inf, __FILE__, NULL, NULL));
+ }
+ }
+ fd_log_debug("%s", fd_peer_dump_list(FD_DUMP_TEST_PARAMS, 0));
+ /* Check we are able to find again any of these */
+ {
+ int i;
+ char locid[255];
+ struct peer_hdr *p;
+ for (i=0; i < sizeof(ids) / sizeof(ids[0]); i++) {
+ snprintf(locid, sizeof(locid), "%s." DomainName, ids[i]);
+ CHECK( 0, fd_peer_getbyid((DiamId_t)locid, strlen((char *)locid), 0, &p));
+ CHECK( 0, strcmp((char *)locid, p->info.pi_diamid));
+ CHECK( 0, fd_peer_getbyid((DiamId_t)locid, strlen((char *)locid), 1, &p));
+ CHECK( 0, strcmp((char *)locid, p->info.pi_diamid));
+ }
+ }
+
+
+ /* That's all for the tests yet */
+ PASSTEST();
+}
+
diff --git a/tests/tests.h b/tests/tests.h
new file mode 100644
index 0000000..85e6d9b
--- /dev/null
+++ b/tests/tests.h
@@ -0,0 +1,239 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 definition of our test harness.
+ * The harness is very simple yet.
+ * It may be interessant to go to dejagnu later...
+ *
+ */
+#ifndef _TESTS_H
+#define _TESTS_H
+
+#include "fdproto-internal.h"
+#include "fdcore-internal.h"
+
+#include <pthread.h>
+#include <errno.h>
+GCC_DIAG_OFF("-Wdeprecated-declarations")
+#include <gcrypt.h>
+GCC_DIAG_ON("-Wdeprecated-declarations")
+
+/* Test timeout duration, unless -n is passed on the command line */
+#ifndef TEST_TIMEOUT
+#define TEST_TIMEOUT 120 /* in seconds */
+#endif /* TEST_TIMEOUT */
+
+/* Standard includes */
+#include <getopt.h>
+#include <time.h>
+#include <libgen.h>
+#include <signal.h>
+
+/* Define the return code values */
+#define PASS 0
+#define FAIL 1
+
+/* Define the macro to fail a test with a message */
+#define FAILTEST( message... ){ \
+ LOG_F(message); \
+ LOG_F("FAILED: %s ", __STRIPPED_FILE__); \
+ free(tbuf); \
+ exit(FAIL); \
+}
+
+/* Define the macro to pass a test */
+#define PASSTEST( ){ \
+ LOG_N("PASS: %s", __STRIPPED_FILE__); \
+ (void)fd_core_shutdown(); \
+ (void)fd_core_wait_shutdown_complete(); \
+ (void)fd_thr_term(&signal_thr); \
+ free(tbuf); \
+ exit(PASS); \
+}
+
+static struct fd_config conf;
+extern struct fd_config * fd_g_config;
+
+/* for dumps */
+static char * tbuf = NULL; size_t tbuflen = 0;
+#define FD_DUMP_TEST_PARAMS &tbuf, &tbuflen, NULL
+
+
+/* Define the standard check routines */
+#define CHECK( _val, _assert ){ \
+ LOG_D("CHECK( %s == %s )", \
+ #_assert, \
+ #_val); \
+ { \
+ __typeof__ (_val) __ret = (_assert); \
+ if (__ret != (_val)) { \
+ FAILTEST( "%s:%d: CHECK FAILED : %s == %lx != %lx", \
+ __STRIPPED_FILE__, \
+ __LINE__, \
+ #_assert, \
+ (unsigned long)__ret, \
+ (unsigned long)(_val)); \
+ }} \
+}
+
+static pthread_t signal_thr;
+static void * signal_catch(void * arg)
+{
+ int sig;
+ sigset_t ss;
+ fd_log_threadname ( "Signal catcher" );
+
+ sigemptyset(&ss);
+
+ /* We use SIGALRM */
+ sigaddset(&ss, SIGALRM);
+
+ /* Unblock any other signal for this thread, so that default handler is enabled */
+ CHECK_SYS_DO( pthread_sigmask( SIG_SETMASK, &ss, NULL ), );
+
+ /* Now wait for sigwait or cancelation */
+ CHECK_POSIX_DO( sigwait(&ss, &sig), );
+ FAILTEST("The timeout (" _stringize(TEST_TIMEOUT) " sec) was reached. Use -n or change TEST_TIMEOUT if the test needs more time to execute.");
+
+ return NULL;
+}
+
+
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+
+/* gnutls debug. */
+static void fd_gnutls_debug(int level, const char * str) {
+ const char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");
+ fd_log_debug("tid:%-20s[gnutls:%d] %s", __thn, level, str);
+}
+static int gnutls_debug = 0;
+
+static int test_parameter = 0;
+
+static inline void parse_cmdline(int argc, char * argv[]) {
+ int c;
+ int no_timeout = 0;
+ while ((c = getopt (argc, argv, "dqnf:F:g:p:")) != -1) {
+ switch (c) {
+ case 'd': /* Increase verbosity of debug messages. */
+ fd_g_debug_lvl--;
+ break;
+
+ case 'q': /* Decrease verbosity. */
+ fd_g_debug_lvl++;
+ break;
+
+ case 'n': /* Disable the timeout of the test. */
+ no_timeout = 1;
+ break;
+
+ case 'f': /* Full debug for the function with this name. */
+ #ifdef DEBUG
+ fd_debug_one_function = optarg;
+ #else /* DEBUG */
+ TRACE_DEBUG(INFO, "Error: must compile with DEBUG support to use this feature");
+ #endif /* DEBUG */
+ break;
+
+ case 'F': /* Full debug for the functions in file with this name. */
+ #ifdef DEBUG
+ fd_debug_one_file = optarg;
+ #else /* DEBUG */
+ TRACE_DEBUG(INFO, "Error: must compile with DEBUG support to use this feature");
+ #endif /* DEBUG */
+ break;
+
+ case 'g': /* Set a debug level and function for GNU TLS calls. */
+ gnutls_debug = (int)atoi(optarg);
+ break;
+
+ case 'p': /* Set a debug level and function for GNU TLS calls. */
+ test_parameter = (int)atoi(optarg);
+ break;
+
+ default: /* bug: option not considered. */
+ return;
+ }
+ }
+ if (!no_timeout) {
+ alarm(TEST_TIMEOUT);
+ }
+ CHECK( 0, pthread_create(&signal_thr, NULL, signal_catch, NULL) );
+}
+
+static inline void test_init(int argc, char * argv[], char *fname)
+{
+ sigset_t sig_all;
+ sigfillset(&sig_all);
+
+ CHECK( 0, pthread_sigmask(SIG_BLOCK, &sig_all, NULL));
+
+ fd_g_config = &conf;
+ memset(fd_g_config, 0, sizeof(struct fd_config));
+
+ CHECK( 0, fd_libproto_init() );
+
+ CHECK( 0, fd_hooks_init() );
+
+ fd_log_threadname(fname);
+
+ /* Parse the command line */
+ parse_cmdline(argc, argv);
+
+ /* Initialize gcrypt and gnutls */
+ (void) gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+ (void) gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+ CHECK( 0, gnutls_global_init());
+ /* Set gnutls debug level ? */
+ if (gnutls_debug) {
+ gnutls_global_set_log_function((gnutls_log_func)fd_gnutls_debug);
+ gnutls_global_set_log_level (gnutls_debug);
+ TRACE_DEBUG(INFO, "Enabled GNUTLS debug at level %d", gnutls_debug);
+ }
+
+ /* Initialize the config */
+ CHECK( 0, fd_conf_init() );
+
+ /* Add definitions of the base protocol */
+ CHECK( 0, fd_dict_base_protocol(fd_g_config->cnf_dict) );
+
+ /* Initialize only the sessions */
+ CHECK( 0, fd_sess_start() );
+
+ return;
+}
+#define INIT_FD() test_init(argc, argv, __STRIPPED_FILE__)
+
+#endif /* _TESTS_H */
diff --git a/tests/testsctp.c b/tests/testsctp.c
new file mode 100644
index 0000000..46453c8
--- /dev/null
+++ b/tests/testsctp.c
@@ -0,0 +1,144 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 "tests.h"
+
+#include <cnxctx.h>
+
+#ifndef TEST_PORT
+#define TEST_PORT 3868
+#endif /* TEST_PORT */
+
+#ifndef NB_STREAMS
+#define NB_STREAMS 10
+#endif /* NB_STREAMS */
+
+
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+#ifdef DISABLE_SCTP
+ INIT_FD();
+ /* In this case, we don't perform this simple test */
+ PASSTEST();
+#else /* DISABLE_SCTP */
+ struct cnxctx cli, srv; /* we use only their cc_socket & cc_state */
+ int sock;
+ char buf1[]="abcdef";
+ char *buf2;
+ size_t sz;
+ struct iovec iov;
+ struct fd_list eps = FD_LIST_INITIALIZER(eps);
+ uint16_t str;
+ int ev;
+
+ /* Initialize the server addresses */
+ {
+ struct addrinfo hints, *ai, *aip;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_NUMERICSERV;
+ hints.ai_family = AF_INET;
+ CHECK( 0, getaddrinfo("localhost", _stringize(TEST_PORT), &hints, &ai) );
+ aip = ai;
+ while (aip) {
+ CHECK( 0, fd_ep_add_merge( &eps, aip->ai_addr, aip->ai_addrlen, EP_FL_DISC | EP_ACCEPTALL ));
+ aip = aip->ai_next;
+ };
+ freeaddrinfo(ai);
+
+ CHECK( 0, FD_IS_LIST_EMPTY(&eps) ? 1 : 0 );
+ }
+
+ memset(&cli, 0, sizeof(cli));
+ memset(&srv, 0, sizeof(srv));
+
+ /* First, initialize the daemon modules */
+ INIT_FD();
+
+ /* Restrain the # of streams */
+ fd_g_config->cnf_sctp_str = NB_STREAMS;
+
+ /* Create the server socket */
+ CHECK( 0, fd_sctp_create_bind_server( &sock, AF_INET6, &eps, TEST_PORT ));
+
+ /* Accept incoming clients */
+ CHECK( 0, fd_sctp_listen( sock ));
+
+ /* Now, create the client socket */
+ CHECK( 0, fd_sctp_client( &cli.cc_socket, 0, TEST_PORT, &eps ));
+
+ /* Accept this connection */
+ srv.cc_socket = accept(sock, NULL, NULL);
+
+ /* Send a first message */
+ iov.iov_base = buf1;
+ iov.iov_len = sizeof(buf1);
+ CHECK( sizeof(buf1), fd_sctp_sendstrv(&srv, 1, &iov, 1 ) );
+ CHECK( 0, srv.cc_state);
+
+ /* Receive this message */
+redo1:
+ CHECK( 0, fd_sctp_recvmeta(&cli, &str, (uint8_t **)&buf2, &sz, &ev) );
+ if (ev == FDEVP_CNX_EP_CHANGE)
+ goto redo1;
+ CHECK( FDEVP_CNX_MSG_RECV, ev);
+ CHECK( 0, cli.cc_state);
+ CHECK( 1, str);
+ CHECK( sizeof(buf1), sz );
+ CHECK( 0, memcmp(buf1, buf2, sz) );
+ free(buf2); buf2 = NULL;
+
+ /* Send in the other direction */
+ CHECK( sizeof(buf1), fd_sctp_sendstrv(&cli, 2, &iov, 1) );
+ CHECK( 0, cli.cc_state);
+
+ /* Receive this message */
+redo2:
+ CHECK( 0, fd_sctp_recvmeta(&srv, &str, (uint8_t **)&buf2, &sz, &ev) );
+ if (ev == FDEVP_CNX_EP_CHANGE)
+ goto redo2;
+ CHECK( FDEVP_CNX_MSG_RECV, ev);
+ CHECK( 0, srv.cc_state);
+ CHECK( 2, str);
+ CHECK( sizeof(buf1), sz );
+ CHECK( 0, memcmp(buf1, buf2, sz) );
+ free(buf2); buf2 = NULL;
+
+ /* That's all for the tests yet */
+ PASSTEST();
+#endif /* DISABLE_SCTP */
+}
+
diff --git a/tests/testsess.c b/tests/testsess.c
new file mode 100644
index 0000000..5acc336
--- /dev/null
+++ b/tests/testsess.c
@@ -0,0 +1,400 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 "tests.h"
+
+#define TEST_DIAM_ID "testsess.myid"
+#define TEST_OPT_IN "suffix"
+#define TEST_OPT (os0_t)TEST_OPT_IN
+#define TEST_SID_IN TEST_DIAM_ID ";1234;5678;" TEST_OPT_IN
+#define TEST_SID (os0_t)TEST_SID_IN
+
+#define TEST_EYEC 0x7e57e1ec
+struct sess_state {
+ int eyec; /* TEST_EYEC */
+ os0_t sid; /* the session with which the data was registered */
+ int * freed; /* location where to write the freed status */
+ void * opaque; /* if opaque was provided, this is the value we expect */
+};
+
+static void mycleanup( struct sess_state * data, os0_t sid, void * opaque )
+{
+ /* sanity */
+ CHECK( 1, sid ? 1 : 0 );
+ CHECK( 1, data? 1 : 0 );
+ CHECK( TEST_EYEC, data->eyec );
+ CHECK( 0, strcmp((char *)sid, (char *)data->sid) );
+ if (data->freed)
+ *(data->freed) += 1;
+ if (data->opaque) {
+ CHECK( 1, opaque == data->opaque ? 1 : 0 );
+ }
+ /* Now, free the data */
+ free(data->sid);
+ free(data);
+}
+
+static __inline__ struct sess_state * new_state(os0_t sid, int *freed)
+{
+ struct sess_state *new;
+ new = malloc(sizeof(struct sess_state));
+ CHECK( 1, new ? 1 : 0 );
+ memset(new, 0, sizeof(struct sess_state));
+ new->eyec = TEST_EYEC;
+ new->sid = os0dup(sid, strlen((char *)sid));
+ CHECK( 1, new->sid ? 1 : 0 );
+ new->freed = freed;
+ return new;
+}
+
+void * g_opaque = (void *)"test";
+
+/* Avoid a lot of casts */
+#undef strlen
+#define strlen(s) strlen((char *)s)
+#undef strncmp
+#define strncmp(s1,s2,l) strncmp((char *)s1, (char *)s2, l)
+#undef strcmp
+#define strcmp(s1,s2) strcmp((char *)s1, (char *)s2)
+
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+ struct session_handler * hdl1, *hdl2;
+ struct session *sess1, *sess2, *sess3;
+ os0_t str1, str2;
+ size_t str1len, str2len;
+ int new;
+
+ /* First, initialize the daemon modules */
+ INIT_FD();
+
+ /* Test functions related to handlers (simple situation) */
+ {
+ void * testptr = NULL;
+ CHECK( 0, fd_sess_handler_create ( &hdl1, mycleanup, NULL, NULL ) );
+ CHECK( 0, fd_sess_handler_create ( &hdl2, mycleanup, NULL, NULL ) );
+ CHECK( 0, fd_sess_handler_destroy( &hdl2, &testptr ) );
+ CHECK( 1, testptr == NULL ? 1 : 0 );
+ CHECK( 0, fd_sess_handler_create ( &hdl2, mycleanup, NULL, g_opaque ) );
+ #if 0
+ fd_log_debug("%s", fd_sess_dump_hdl(FD_DUMP_TEST_PARAMS, hdl1));
+ fd_log_debug("%s", fd_sess_dump_hdl(FD_DUMP_TEST_PARAMS, hdl2));
+ #endif
+ }
+
+ /* Test Session Id generation (fd_sess_new) */
+ {
+ /* DiamId is provided, not opt */
+ CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), NULL, 0 ) );
+ CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), NULL, 0 ) );
+ #if 0
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess2, 1));
+ #endif
+
+ /* Check both string start with the diameter Id, but are different */
+ CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+ CHECK( 1, (strlen(str1) == str1len) ? 1 : 0 );
+ CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) );
+ CHECK( 0, fd_sess_getsid(sess2, &str2, &str2len) );
+ CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) );
+ CHECK( 1, strcmp(str1, str2) ? 1 : 0 );
+ CHECK( 0, fd_sess_destroy( &sess1 ) );
+ CHECK( 0, fd_sess_destroy( &sess2 ) );
+
+ /* diamId and opt */
+ CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, 0, TEST_OPT, 0 ) );
+ CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), TEST_OPT, CONSTSTRLEN(TEST_OPT_IN) - 1 ) );
+ #if 0
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess2, 1));
+ #endif
+
+ CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+ CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) );
+ CHECK( 0, strcmp(str1 + str1len - CONSTSTRLEN(TEST_OPT_IN) - 1, ";" TEST_OPT_IN) );
+
+ CHECK( 0, fd_sess_getsid(sess2, &str2, &str2len) );
+ CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) );
+ CHECK( 0, strncmp(str2 + str2len - CONSTSTRLEN(TEST_OPT_IN), ";" TEST_OPT_IN, CONSTSTRLEN(TEST_OPT_IN)) );
+
+ CHECK( 1, strcmp(str1, str2) ? 1 : 0 );
+ CHECK( 0, fd_sess_destroy( &sess1 ) );
+ CHECK( 0, fd_sess_destroy( &sess2 ) );
+
+ /* Now, only opt is provided */
+ CHECK( 0, fd_sess_new( &sess1, NULL, 0, TEST_SID, 0 ) );
+ CHECK( EALREADY, fd_sess_new( &sess2, NULL, 0, TEST_SID, 0 ) );
+ CHECK( sess2, sess1 );
+ CHECK( EALREADY, fd_sess_new( &sess3, NULL, 0, TEST_SID, CONSTSTRLEN(TEST_SID_IN) ) );
+ CHECK( sess3, sess1 );
+ CHECK( 0, fd_sess_new( &sess2, NULL, 0, TEST_SID, CONSTSTRLEN(TEST_SID_IN) - 1 ) );
+ #if 0
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess2, 1));
+ #endif
+ CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+ CHECK( 0, fd_sess_getsid(sess2, &str2, &str2len) );
+ CHECK( 0, strncmp( str1, str2, CONSTSTRLEN(TEST_SID_IN) - 1 ) );
+ CHECK( 0, strcmp( str1, TEST_SID ) );
+ CHECK( 1, strcmp( str1, str2 ) ? 1 : 0 );
+
+ CHECK( 0, fd_sess_destroy( &sess2 ) );
+ CHECK( 0, fd_sess_destroy( &sess1 ) );
+ }
+
+ /* Test fd_sess_getcount */
+ {
+ uint32_t cnt;
+ CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), NULL, 0 ) );
+ CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), NULL, 0 ) );
+ CHECK( 0, fd_sess_getcount(&cnt));
+ CHECK( 2, cnt);
+ CHECK( 0, fd_sess_destroy( &sess2 ) );
+ CHECK( 0, fd_sess_getcount(&cnt));
+ CHECK( 1, cnt);
+ CHECK( 0, fd_sess_destroy( &sess1 ) );
+ CHECK( 0, fd_sess_getcount(&cnt));
+ CHECK( 0, cnt);
+
+ }
+
+ /* Test fd_sess_fromsid */
+ {
+ CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+ CHECK( 1, new ? 1 : 0 );
+
+ CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess2, &new ) );
+ CHECK( 0, new );
+ CHECK( sess1, sess2 );
+
+ CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess3, NULL ) );
+ CHECK( sess1, sess3 );
+
+ CHECK( 0, fd_sess_destroy( &sess1 ) );
+ }
+
+ /* Test fd_sess_reclaim */
+ {
+ struct sess_state *tms;
+
+ CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+ CHECK( 1, new ? 1 : 0 );
+
+ CHECK( 0, fd_sess_reclaim( &sess1 ) );
+ CHECK( NULL, sess1 );
+
+ CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+ CHECK( 1, new ? 1 : 0 );
+
+ tms = new_state(TEST_SID, NULL);
+ CHECK( 0, fd_sess_state_store ( hdl1, sess1, &tms ) );
+
+ CHECK( 0, fd_sess_reclaim( &sess1 ) );
+ CHECK( NULL, sess1 );
+
+ CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+ CHECK( 0, new );
+
+ CHECK( 0, fd_sess_destroy( &sess1 ) );
+
+ CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+ CHECK( 1, new ? 1 : 0 );
+
+ CHECK( 0, fd_sess_destroy( &sess1 ) );
+ }
+
+ /* Test timeout function */
+ {
+ struct timespec timeout;
+
+ CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+ CHECK( 1, new ? 1 : 0 );
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &timeout) );
+ CHECK( 0, fd_sess_settimeout( sess1, &timeout) ); /* expire now */
+ timeout.tv_sec = 0;
+ timeout.tv_nsec= 50000000; /* 50 ms */
+ CHECK( 0, nanosleep(&timeout, NULL) );
+
+ CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+ CHECK( 1, new ? 1 : 0 );
+
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &timeout) );
+ timeout.tv_sec += 2678500; /* longer that SESS_DEFAULT_LIFETIME */
+ CHECK( 0, fd_sess_settimeout( sess1, &timeout) );
+
+ /* Create a second session */
+ CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, 0, NULL, 0 ) );
+
+ /* We don't really have a way to verify the expiry list is in proper order automatically here... */
+
+ CHECK( 0, fd_sess_destroy( &sess2 ) );
+ CHECK( 0, fd_sess_destroy( &sess1 ) );
+ }
+
+
+ /* Test states operations */
+ {
+ struct sess_state * ms[6], *tms;
+ int freed[6];
+ struct timespec timeout;
+ void * testptr = NULL;
+
+ /* Create three sessions */
+ CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, 0, NULL, 0 ) );
+ CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, 0, NULL, 0 ) );
+ CHECK( 0, fd_sess_new( &sess3, TEST_DIAM_ID, 0, NULL, 0 ) );
+
+ /* Create 2 states */
+ CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+ freed[0] = 0;
+ ms[0] = new_state(str1, &freed[0]);
+ ms[1] = new_state(str1, NULL);
+
+ tms = ms[0]; /* save a copy */
+ CHECK( 0, fd_sess_state_store ( hdl1, sess1, &ms[0] ) );
+ CHECK( NULL, ms[0] );
+ CHECK( EINVAL, fd_sess_state_store ( hdl1, sess1, NULL ) );
+ CHECK( EALREADY, fd_sess_state_store ( hdl1, sess1, &ms[1] ) );
+ CHECK( 1, ms[1] ? 1 : 0 );
+
+ #if 0
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+ #endif
+
+ CHECK( 0, fd_sess_state_retrieve( hdl1, sess1, &ms[0] ) );
+ CHECK( tms, ms[0] );
+ CHECK( 0, freed[0] );
+
+ CHECK( 0, fd_sess_state_retrieve( hdl1, sess2, &tms ) );
+ CHECK( NULL, tms );
+
+ mycleanup(ms[0], str1, NULL);
+ mycleanup(ms[1], str1, NULL);
+
+ /* Now create 6 states */
+ memset(&freed[0], 0, sizeof(freed));
+ CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+ ms[0] = new_state(str1, &freed[0]);
+ ms[1] = new_state(str1, &freed[1]);
+ CHECK( 0, fd_sess_getsid(sess2, &str1, &str1len) );
+ ms[2] = new_state(str1, &freed[2]);
+ ms[3] = new_state(str1, &freed[3]);
+ CHECK( 0, fd_sess_getsid(sess3, &str1, &str1len) );
+ ms[4] = new_state(str1, &freed[4]);
+ ms[5] = new_state(str1, &freed[5]);
+ ms[5]->opaque = g_opaque;
+ str2 = os0dup(str1, str1len);
+ CHECK( 1, str2 ? 1 : 0 );
+
+ /* Store the six states */
+ CHECK( 0, fd_sess_state_store ( hdl1, sess1, &ms[0] ) );
+ CHECK( 0, fd_sess_state_store ( hdl2, sess1, &ms[1] ) );
+ CHECK( 0, fd_sess_state_store ( hdl1, sess2, &ms[2] ) );
+ CHECK( 0, fd_sess_state_store ( hdl2, sess2, &ms[3] ) );
+ CHECK( 0, fd_sess_state_store ( hdl1, sess3, &ms[4] ) );
+ CHECK( 0, fd_sess_state_store ( hdl2, sess3, &ms[5] ) );
+
+ #if 0
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess2, 1));
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess3, 1));
+ #endif
+
+ /* Destroy session 3 */
+ CHECK( 0, fd_sess_destroy( &sess3 ) );
+ CHECK( 0, freed[0] );
+ CHECK( 0, freed[1] );
+ CHECK( 0, freed[2] );
+ CHECK( 0, freed[3] );
+ CHECK( 1, freed[4] );
+ CHECK( 1, freed[5] );
+
+ /* Destroy handler 2 */
+ CHECK( 0, fd_sess_handler_destroy( &hdl2, &testptr ) );
+ CHECK( 0, freed[0] );
+ CHECK( 1, freed[1] );
+ CHECK( 0, freed[2] );
+ CHECK( 1, freed[3] );
+ CHECK( 1, freed[4] );
+ CHECK( 1, freed[5] );
+ CHECK( 1, testptr == g_opaque ? 1 : 0 );
+
+ #if 1
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess2, 1));
+ #endif
+
+ /* Create again session 3, check that no data is associated to it */
+ CHECK( 0, fd_sess_fromsid( str2, str1len, &sess3, &new ) );
+ CHECK( 1, new ? 1 : 0 );
+ CHECK( 0, fd_sess_state_retrieve( hdl1, sess3, &tms ) );
+ CHECK( NULL, tms );
+ CHECK( 0, fd_sess_destroy( &sess3 ) );
+ free(str2);
+
+ /* Timeout does call cleanups */
+ CHECK( 0, clock_gettime(CLOCK_REALTIME, &timeout) );
+ CHECK( 0, fd_sess_settimeout( sess2, &timeout) );
+ #if 1
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+ fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess2, 1));
+ #endif
+ timeout.tv_sec = 0;
+ timeout.tv_nsec= 50000000; /* 50 ms */
+ CHECK( 0, nanosleep(&timeout, NULL) );
+ CHECK( 0, freed[0] );
+ CHECK( 1, freed[1] );
+ CHECK( 1, freed[2] );
+ CHECK( 1, freed[3] );
+ CHECK( 1, freed[4] );
+ CHECK( 1, freed[5] );
+
+ /* Check the last data can still be retrieved */
+ CHECK( 0, fd_sess_state_retrieve( hdl1, sess1, &tms ) );
+ CHECK( 1, tms ? 1 : 0);
+ CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+ mycleanup(tms, str1, NULL);
+ }
+
+ /* TODO: add tests on messages referencing sessions */
+
+ /* That's all for the tests yet */
+ PASSTEST();
+}
+