Implement all TRP and TID fields for filtering
[trust_router.git] / common / tests / filt_test.c
1 /*
2  * Copyright (c) 2017, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <assert.h>
38 #include <string.h>
39 #include <jansson.h>
40
41 #include <trp_internal.h>
42 #include <tid_internal.h>
43 #include <tr_filter.h>
44 #include <tr_config.h>
45
46 #define FILTER_PATH "./test-filters/"
47
48 /**
49  * Load a JSON file containing filters and return the filters from the first rp_client.
50  *
51  * @param fname File to read
52  * @param filt_out Will point to the loaded filter on success
53  * @return Return value from tr_cfg_parse_one_config_file()
54  */
55 int load_filter(const char *fname, TR_FILTER_SET **filts_out)
56 {
57   TR_CFG *cfg=tr_cfg_new(NULL);
58   TR_CFG_RC rc=TR_CFG_ERROR;
59
60   assert(fname);
61   assert(filts_out);
62
63   rc=tr_cfg_parse_one_config_file(cfg, fname);
64   if (rc!=TR_CFG_SUCCESS)
65     goto cleanup;
66
67   /* Steal the filter from the first rp_client */
68   assert(cfg);
69   assert(cfg->rp_clients);
70   assert(cfg->rp_clients->filters);
71   *filts_out=cfg->rp_clients->filters;
72   cfg->rp_clients->filters=NULL; /* can't use the _set_filter() because that will free the filter */
73   talloc_steal(NULL, *filts_out);
74
75 cleanup:
76   tr_cfg_free(cfg);
77   return rc;
78 }
79
80 /**
81  * Test that filters load / fail to load as expected.
82  *
83  * @return 1 if all tests pass
84  */
85 int test_load_filter(void)
86 {
87   TR_FILTER_SET *filts=NULL;
88
89   assert(TR_CFG_SUCCESS==load_filter(FILTER_PATH "valid-filt.json", &filts));
90   if (filts) tr_filter_set_free(filts);
91   filts=NULL;
92   assert(TR_CFG_NOPARSE==load_filter(FILTER_PATH "invalid-filt-repeated-key.json", &filts));
93   if (filts) tr_filter_set_free(filts);
94   filts=NULL;
95   assert(TR_CFG_ERROR==load_filter(FILTER_PATH "invalid-filt-unknown-field.json", &filts));
96   if (filts) tr_filter_set_free(filts);
97   filts=NULL;
98   return 1;
99 }
100
101 /**
102  * Read the first inforec from the TR_MSG encoded in JSON file named fname.
103  *
104  * @param fname Filename with path for TR_MSG JSON
105  * @return Pointer to the decoded inforec, or NULL on failure
106  */
107 TRP_INFOREC *load_inforec(const char *fname)
108 {
109   TR_MSG *msg=NULL;
110   TRP_UPD *upd=NULL;
111   TRP_INFOREC *inforec=NULL;
112   json_t *decoded=json_load_file(fname, JSON_REJECT_DUPLICATES|JSON_DISABLE_EOF_CHECK, NULL);
113   char *encoded=json_dumps(decoded, 0); /* silly way to read the file without mucking around */
114
115   assert(decoded);
116   json_decref(decoded);
117
118   assert(encoded);
119   assert(msg=tr_msg_decode(encoded, strlen(encoded)));
120   assert(upd=tr_msg_get_trp_upd(msg));
121   assert(inforec=trp_upd_get_inforec(upd));
122   /* now remove the inforec from the update context */
123   talloc_steal(NULL, inforec);
124   tr_msg_free_decoded(msg);
125   tr_msg_free_encoded(encoded);
126   return inforec;
127 }
128
129 /* make this bigger than your message file */
130 #define MAX_FILE_SIZE 20000
131 TID_REQ *load_tid_req(const char *fname)
132 {
133   TID_REQ *out=NULL;
134   TR_MSG *msg=NULL;
135   FILE *f=NULL;
136   char *msgbuf=NULL;
137   size_t msglen=0;
138
139   msgbuf=malloc(MAX_FILE_SIZE);
140   assert(msgbuf);
141   f=fopen(fname, "r");
142   assert(f);
143   msglen=fread(msgbuf, 1, MAX_FILE_SIZE, f);
144   assert(msglen);
145   assert(feof(f));
146   msg=tr_msg_decode(msgbuf, msglen);
147   free(msgbuf);
148   msgbuf=NULL;
149
150   assert(msg);
151   assert(tr_msg_get_msg_type(msg)==TID_REQUEST);
152
153   /* take the tid req out of the msg */
154   out=tr_msg_get_req(msg);
155   tr_msg_set_req(msg, NULL);
156   assert(out);
157
158   tr_msg_free_decoded(msg);
159   return out;
160 }
161
162 /**
163  * Read a set of filters from a config JSON in filt_fname and test against the tid_req or inforec in target_fname.
164  * If expect==1, succeed if the target is accepted by the filter, otherwise succeed if it is rejected.
165  * Takes filters from the first rp_realm defined in the filter file and the first inforec or tid req from
166  * the target file.
167  *
168  * @param filt_fname Name of JSON file containing filters
169  * @param ftype Which type of filter to test
170  * @param target_fname  Name of JSON file containing inforec
171  * @param expected_match 1 if we expect a match, 0 otherwise
172  * @param expected_action Expected action if the filter matches
173  * @return 1 if expected result is obtained, 0 or does not return otherwise
174  */
175 int test_one_filter(const char *filt_fname,
176                     TR_FILTER_TYPE ftype,
177                     const char *target_fname,
178                     int expected_match,
179                     TR_FILTER_ACTION expected_action)
180 {
181   TR_FILTER_TARGET *target=NULL;
182   TR_FILTER_SET *filts=NULL;
183   TR_FILTER_ACTION action=TR_FILTER_ACTION_UNKNOWN;
184
185   /* load filter for first test */
186   assert(TR_CFG_SUCCESS==load_filter(filt_fname, &filts));
187
188   /* load the target req or inforec */
189   switch(ftype) {
190     case TR_FILTER_TYPE_TID_INBOUND:
191       target=tr_filter_target_tid_req(NULL, load_tid_req(target_fname));
192       break;
193
194     case TR_FILTER_TYPE_TRP_INBOUND:
195     case TR_FILTER_TYPE_TRP_OUTBOUND:
196       /* TODO: read realm and community */
197       target= tr_filter_target_trp_inforec(NULL, NULL, load_inforec(target_fname));
198       break;
199
200     default:
201       printf("Unknown filter type.\n");
202   }
203   assert(target);
204
205   assert(expected_match==tr_filter_apply(target, tr_filter_set_get(filts, ftype), NULL, &action));
206   if (expected_match==TR_FILTER_MATCH)
207     assert(action==expected_action);
208
209   tr_filter_set_free(filts);
210   switch(ftype) {
211     case TR_FILTER_TYPE_TID_INBOUND:
212       tid_req_free(target->tid_req);
213       break;
214
215     case TR_FILTER_TYPE_TRP_INBOUND:
216     case TR_FILTER_TYPE_TRP_OUTBOUND:
217       trp_inforec_free(target->trp_inforec);
218       if (target->trp_upd!=NULL)
219         trp_upd_free(target->trp_upd);
220       break;
221
222     default:
223       printf("Unknown filter type.\n");
224   }
225   tr_filter_target_free(target);
226   return 1;
227 }
228
229 int test_filter(void)
230 {
231   json_t *test_list=json_load_file(FILTER_PATH "filter-tests.json", JSON_DISABLE_EOF_CHECK, NULL);
232   json_t *this;
233   size_t ii;
234   const char *filt_file, *target_file;
235   TR_FILTER_TYPE ftype;
236   int expect_match;
237   TR_FILTER_ACTION action;
238
239   json_array_foreach(test_list, ii, this) {
240     printf("Running filter test case: %s\n", json_string_value(json_object_get(this, "test label")));
241     fflush(stdout);
242
243     filt_file=json_string_value(json_object_get(this, "filter file"));
244     ftype=tr_filter_type_from_string(json_string_value(json_object_get(this, "filter type")));
245     target_file=json_string_value(json_object_get(this, "target file"));
246     if (0==strcmp("yes", json_string_value(json_object_get(this, "expect match"))))
247       expect_match=TR_FILTER_MATCH;
248     else
249       expect_match=TR_FILTER_NO_MATCH;
250
251     if (0==strcmp("accept", json_string_value(json_object_get(this, "action"))))
252       action=TR_FILTER_ACTION_ACCEPT;
253     else
254       action=TR_FILTER_ACTION_REJECT;
255
256     assert(test_one_filter(filt_file, ftype, target_file, expect_match, action));
257   }
258
259   return 1;
260 }
261
262
263
264 int main(void)
265 {
266   assert(test_load_filter());
267   assert(test_filter());
268   printf("Success\n");
269   return 0;
270 }