Brian Waters | 13d9601 | 2017-12-08 16:53:31 -0600 | [diff] [blame] | 1 | /********************************************************************************************************* |
| 2 | * Software License Agreement (BSD License) * |
| 3 | * Author: Sebastien Decugis <sdecugis@freediameter.net> * |
| 4 | * * |
| 5 | * Copyright (c) 2013, WIDE Project and NICT * |
| 6 | * All rights reserved. * |
| 7 | * * |
| 8 | * Redistribution and use of this software in source and binary forms, with or without modification, are * |
| 9 | * permitted provided that the following conditions are met: * |
| 10 | * * |
| 11 | * * Redistributions of source code must retain the above * |
| 12 | * copyright notice, this list of conditions and the * |
| 13 | * following disclaimer. * |
| 14 | * * |
| 15 | * * Redistributions in binary form must reproduce the above * |
| 16 | * copyright notice, this list of conditions and the * |
| 17 | * following disclaimer in the documentation and/or other * |
| 18 | * materials provided with the distribution. * |
| 19 | * * |
| 20 | * * Neither the name of the WIDE Project or NICT nor the * |
| 21 | * names of its contributors may be used to endorse or * |
| 22 | * promote products derived from this software without * |
| 23 | * specific prior written permission of WIDE Project and * |
| 24 | * NICT. * |
| 25 | * * |
| 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * |
| 27 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * |
| 28 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * |
| 29 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * |
| 30 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * |
| 31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * |
| 32 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * |
| 33 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * |
| 34 | *********************************************************************************************************/ |
| 35 | |
| 36 | #include "tests.h" |
| 37 | |
| 38 | /* The connection string to the database */ |
| 39 | #ifndef TEST_CONNINFO |
| 40 | #error "Please specify the conninfo information" |
| 41 | #endif /* TEST_CONNINFO */ |
| 42 | |
| 43 | /* The table used for tests. This table will receive the following instructions: |
| 44 | DROP TABLE <table>; |
| 45 | CREATE TABLE <table> |
| 46 | ( |
| 47 | recorded_on timestamp with time zone NOT NULL, |
| 48 | "Accounting-Record-Type" integer, |
| 49 | "Session-Id" bytea, |
| 50 | "Accounting-Record-Number" integer, |
| 51 | "Route-Record1" bytea, |
| 52 | "Route-Record2" bytea, |
| 53 | "Route-Record3" bytea, |
| 54 | "Route-Record4" bytea |
| 55 | ); |
| 56 | */ |
| 57 | #define TABLE "incoming_test" |
| 58 | |
| 59 | #include "app_acct.h" |
| 60 | #include <libpq-fe.h> |
| 61 | |
| 62 | static int add_avp_in_conf(char * avpname, int multi) |
| 63 | { |
| 64 | struct acct_conf_avp *new; |
| 65 | struct dict_object * dict; |
| 66 | struct dict_avp_data dictdata; |
| 67 | |
| 68 | /* Validate the avp name first */ |
| 69 | CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, avpname, &dict, ENOENT) ); |
| 70 | CHECK_FCT( fd_dict_getval( dict, &dictdata )); |
| 71 | |
| 72 | /* Create a new entry */ |
| 73 | CHECK_MALLOC( new = malloc(sizeof(struct acct_conf_avp)) ); |
| 74 | memset(new, 0, sizeof(struct acct_conf_avp)); |
| 75 | fd_list_init(&new->chain, NULL); |
| 76 | new->avpname = avpname; |
| 77 | new->avpobj = dict; |
| 78 | new->avptype = dictdata.avp_basetype; |
| 79 | new->multi = multi; |
| 80 | |
| 81 | /* Add this new entry at the end of the list */ |
| 82 | fd_list_insert_before( &acct_config->avps, &new->chain ); |
| 83 | |
| 84 | return 0; |
| 85 | } |
| 86 | |
| 87 | #define LOCAL_ID "test.app.acct" |
| 88 | #define LOCAL_REALM "app.acct" |
| 89 | |
| 90 | /* Main test routine */ |
| 91 | int main(int argc, char *argv[]) |
| 92 | { |
| 93 | extern pthread_key_t connk; /* in acct_db.c */ |
| 94 | PGconn *conn; |
| 95 | extern int fd_ext_init(int major, int minor, char * conffile); /* defined in include's extension.h */ |
| 96 | extern void fd_ext_fini(void); /* defined in the extension itself */ |
| 97 | struct msg * msg; |
| 98 | os0_t sess_bkp; |
| 99 | size_t sess_bkp_len; |
| 100 | |
| 101 | /* First, initialize the daemon modules */ |
| 102 | INIT_FD(); |
| 103 | fd_g_config->cnf_diamid = strdup(LOCAL_ID); |
| 104 | fd_g_config->cnf_diamid_len = CONSTSTRLEN(LOCAL_ID); |
| 105 | fd_g_config->cnf_diamrlm = strdup(LOCAL_REALM); |
| 106 | fd_g_config->cnf_diamrlm_len = CONSTSTRLEN(LOCAL_REALM); |
| 107 | |
| 108 | CHECK( 0, fd_queues_init() ); |
| 109 | CHECK( 0, fd_msg_init() ); |
| 110 | CHECK( 0, fd_rtdisp_init() ); |
| 111 | |
| 112 | /* Initialize the extension configuration for the test */ |
| 113 | { |
| 114 | CHECK( 0, acct_conf_init() ); |
| 115 | acct_config->conninfo = strdup(TEST_CONNINFO); |
| 116 | acct_config->tablename = strdup(TABLE); |
| 117 | acct_config->tsfield = strdup("recorded_on"); |
| 118 | CHECK( 0, add_avp_in_conf(strdup("Session-Id"), 0) ); |
| 119 | CHECK( 0, add_avp_in_conf(strdup("Accounting-Record-Type"), 0) ); |
| 120 | CHECK( 0, add_avp_in_conf(strdup("Accounting-Record-Number"), 0) ); |
| 121 | CHECK( 0, add_avp_in_conf(strdup("Route-Record"), 4) ); |
| 122 | |
| 123 | /* Now, call the one of the extension */ |
| 124 | CHECK( 0, fd_ext_init(FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR,NULL) ); |
| 125 | conn = pthread_getspecific(connk); |
| 126 | } |
| 127 | |
| 128 | /* Drop and recreate the table for the test */ |
| 129 | { |
| 130 | PGresult * res; |
| 131 | CHECK( CONNECTION_OK, PQstatus(conn) ); |
| 132 | |
| 133 | res = PQexec(conn, "DROP TABLE " TABLE ";"); |
| 134 | CHECK( PGRES_COMMAND_OK, PQresultStatus(res) ); |
| 135 | PQclear(res); |
| 136 | |
| 137 | res = PQexec(conn, "CREATE TABLE " TABLE " ( " |
| 138 | " recorded_on timestamp with time zone NOT NULL, " |
| 139 | " \"Accounting-Record-Type\" integer, " |
| 140 | " \"Session-Id\" bytea, " |
| 141 | " \"Accounting-Record-Number\" integer, " |
| 142 | " \"Route-Record1\" bytea, " |
| 143 | " \"Route-Record2\" bytea, " |
| 144 | " \"Route-Record3\" bytea, " |
| 145 | " \"Route-Record4\" bytea " |
| 146 | ");" |
| 147 | ); |
| 148 | CHECK( PGRES_COMMAND_OK, PQresultStatus(res) ); |
| 149 | PQclear(res); |
| 150 | } |
| 151 | |
| 152 | /* OK, we are ready to test now. Create an ACR message that will pass the ABNF check */ |
| 153 | { |
| 154 | struct dict_object * d = NULL; |
| 155 | struct avp *avp = NULL; |
| 156 | union avp_value avp_val; |
| 157 | |
| 158 | /* Now find the ACR dictionary object */ |
| 159 | CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &d, ENOENT ) ); |
| 160 | |
| 161 | /* Create the instance */ |
| 162 | CHECK( 0, fd_msg_new ( d, MSGFL_ALLOC_ETEID, &msg ) ); |
| 163 | |
| 164 | /* App id */ |
| 165 | { |
| 166 | struct msg_hdr * h; |
| 167 | CHECK( 0, fd_msg_hdr( msg, &h ) ); |
| 168 | h->msg_appl = 3; |
| 169 | } |
| 170 | |
| 171 | /* sid */ |
| 172 | { |
| 173 | struct session * sess = NULL; |
| 174 | os0_t s; |
| 175 | CHECK( 0, fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, NULL, 0) ); |
| 176 | CHECK( 0, fd_sess_getsid(sess, &s, &sess_bkp_len) ); |
| 177 | CHECK( 1, (sess_bkp = os0dup(s, sess_bkp_len)) ? 1 : 0); |
| 178 | |
| 179 | CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &d, ENOENT ) ); |
| 180 | CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) ); |
| 181 | memset(&avp_val, 0, sizeof(avp_val)); |
| 182 | avp_val.os.data = sess_bkp; |
| 183 | avp_val.os.len = sess_bkp_len; |
| 184 | CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) ); |
| 185 | CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_FIRST_CHILD, avp) ); |
| 186 | } |
| 187 | |
| 188 | /* Origin-* */ |
| 189 | CHECK( 0, fd_msg_add_origin(msg, 1) ); |
| 190 | |
| 191 | /* Destination-Realm */ |
| 192 | { |
| 193 | CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &d, ENOENT ) ); |
| 194 | CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) ); |
| 195 | memset(&avp_val, 0, sizeof(avp_val)); |
| 196 | avp_val.os.data = (unsigned char *)fd_g_config->cnf_diamrlm; |
| 197 | avp_val.os.len = fd_g_config->cnf_diamrlm_len; |
| 198 | CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) ); |
| 199 | CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) ); |
| 200 | } |
| 201 | |
| 202 | /* Accounting-Record-Type */ |
| 203 | { |
| 204 | CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Type", &d, ENOENT ) ); |
| 205 | CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) ); |
| 206 | memset(&avp_val, 0, sizeof(avp_val)); |
| 207 | avp_val.u32 = 2; |
| 208 | CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) ); |
| 209 | CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) ); |
| 210 | } |
| 211 | |
| 212 | /* Accounting-Record-Number */ |
| 213 | { |
| 214 | CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Number", &d, ENOENT ) ); |
| 215 | CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) ); |
| 216 | memset(&avp_val, 0, sizeof(avp_val)); |
| 217 | avp_val.u32 = 2; |
| 218 | CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) ); |
| 219 | CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) ); |
| 220 | } |
| 221 | |
| 222 | /* Route-Record */ |
| 223 | { |
| 224 | CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &d, ENOENT ) ); |
| 225 | CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) ); |
| 226 | memset(&avp_val, 0, sizeof(avp_val)); |
| 227 | avp_val.os.data = (unsigned char *)"peer1"; |
| 228 | avp_val.os.len = strlen((char *)avp_val.os.data); |
| 229 | CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) ); |
| 230 | CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) ); |
| 231 | |
| 232 | CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) ); |
| 233 | memset(&avp_val, 0, sizeof(avp_val)); |
| 234 | avp_val.os.data = (unsigned char *)"peer2"; |
| 235 | avp_val.os.len = strlen((char *)avp_val.os.data); |
| 236 | CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) ); |
| 237 | CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) ); |
| 238 | } |
| 239 | |
| 240 | /* Source */ |
| 241 | CHECK( 0, fd_msg_source_set( msg, "peer3", CONSTSTRLEN("peer3") ) ); |
| 242 | CHECK( 0, fd_msg_source_setrr( msg, "peer3", CONSTSTRLEN("peer3"), fd_g_config->cnf_dict ) ); |
| 243 | } |
| 244 | |
| 245 | /* Now, have the daemon handle this */ |
| 246 | CHECK( 0, fd_fifo_post(fd_g_incoming, &msg) ); |
| 247 | |
| 248 | /* It is picked by the dispatch module, the extension handles the query, inserts the records in the DB, send creates the answer. |
| 249 | Once the answer is ready, it is sent to "peer3" which is not available of course; then the message is simply destroyed. |
| 250 | We wait 1 second for this to happen... */ |
| 251 | sleep(1); |
| 252 | |
| 253 | /* Now, check the record was actually registered properly */ |
| 254 | { |
| 255 | PGresult * res; |
| 256 | uint8_t * bs; |
| 257 | char * es; |
| 258 | size_t l; |
| 259 | |
| 260 | res = PQexec(conn, "SELECT \"Session-Id\" from " TABLE ";"); |
| 261 | CHECK( PGRES_TUPLES_OK, PQresultStatus(res) ); |
| 262 | |
| 263 | /* We also check that the Session-Id we retrieve is the same as what we generated earlier (not trashed in the process) */ |
| 264 | es = PQgetvalue(res, 0, 0); |
| 265 | bs = PQunescapeBytea((uint8_t *)es, &l); |
| 266 | |
| 267 | CHECK( 0, fd_os_cmp(bs, l, sess_bkp, sess_bkp_len) ); |
| 268 | |
| 269 | PQclear(res); |
| 270 | PQfreemem(bs); |
| 271 | } |
| 272 | |
| 273 | /* That's all for the tests yet */ |
| 274 | free(sess_bkp); |
| 275 | |
| 276 | PASSTEST(); |
| 277 | } |
| 278 | |