Refactor TR_FLINE using GPtrArray
[trust_router.git] / common / tr_filter.c
1 /*
2  * Copyright (c) 2012, 2013, 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 <string.h>
38 #include <talloc.h>
39 #include <assert.h>
40
41 #include <tr_filter.h>
42 #include <trp_internal.h>
43 #include <tid_internal.h>
44 #include <tr_debug.h>
45
46 /* Function types for handling filter fields generally. All target values
47  * are represented as strings in a TR_NAME.
48  */
49
50 /* CMP functions return values like strcmp: 0 on match, <0 on target<val, >0 on target>val */
51 typedef int (*TR_FILTER_FIELD_CMP)(TR_FILTER_TARGET *target, TR_NAME *val);
52 /* get functions return TR_NAME format of the field value. Caller must free it. */
53 typedef TR_NAME *(*TR_FILTER_FIELD_GET)(TR_FILTER_TARGET *target);
54
55 static TR_FILTER_TARGET *tr_filter_target_new(TALLOC_CTX *mem_ctx)
56 {
57   TR_FILTER_TARGET *target=talloc(mem_ctx, TR_FILTER_TARGET);
58   if (target) {
59     target->trp_inforec=NULL;
60     target->trp_upd=NULL;
61     target->tid_req=NULL;
62   }
63   return target;
64 }
65 void tr_filter_target_free(TR_FILTER_TARGET *target)
66 {
67   talloc_free(target);
68 }
69
70 /**
71  * Create a filter target for a TID request. Does not change the context of the request,
72  * so this is only valid until that is freed.
73  *
74  * @param mem_ctx talloc context for the object
75  * @param req TID request object
76  * @return pointer to a TR_FILTER_TARGET structure, or null on allocation failure
77  */
78 TR_FILTER_TARGET *tr_filter_target_tid_req(TALLOC_CTX *mem_ctx, TID_REQ *req)
79 {
80   TR_FILTER_TARGET *target=tr_filter_target_new(mem_ctx);
81   if (target)
82     target->tid_req=req; /* borrowed, not adding to our context */
83   return target;
84 }
85
86 /**
87  * Create a filter target for a TRP inforec. Does not change the context of the inforec or duplicate TR_NAMEs,
88  * so this is only valid until those are freed.
89  *
90  * @param mem_ctx talloc context for the object
91  * @param upd Update containing the TRP inforec
92  * @param inforec TRP inforec
93  * @return pointer to a TR_FILTER_TARGET structure, or null on allocation failure
94  */
95 TR_FILTER_TARGET *tr_filter_target_trp_inforec(TALLOC_CTX *mem_ctx, TRP_UPD *upd, TRP_INFOREC *inforec)
96 {
97   TR_FILTER_TARGET *target=tr_filter_target_new(mem_ctx);
98   if (target) {
99     target->trp_inforec = inforec; /* borrowed, not adding to our context */
100     target->trp_upd=upd;
101   }
102   return target;
103 }
104
105 /** Handler functions for TID RP_REALM field */
106 static int tr_ff_cmp_tid_rp_realm(TR_FILTER_TARGET *target, TR_NAME *val)
107 {
108   return tr_name_cmp(tid_req_get_rp_realm(target->tid_req), val);
109 }
110
111 static TR_NAME *tr_ff_get_tid_rp_realm(TR_FILTER_TARGET *target)
112 {
113   return tr_dup_name(tid_req_get_rp_realm(target->tid_req));
114 }
115
116 /** Handler functions for TRP info_type field */
117 static int tr_ff_cmp_trp_info_type(TR_FILTER_TARGET *target, TR_NAME *val)
118 {
119   TRP_INFOREC *inforec=target->trp_inforec;
120   char *valstr=NULL;
121   int val_type=0;
122
123   assert(val);
124   assert(inforec);
125
126   /* nothing matches unknown */
127   if (inforec->type==TRP_INFOREC_TYPE_UNKNOWN)
128     return 0;
129
130   valstr = tr_name_strdup(val); /* get this as an official null-terminated string */
131   val_type = trp_inforec_type_from_string(valstr);
132   free(valstr);
133
134   /* we do not define an ordering of info types */
135   return (val_type==inforec->type);
136 }
137
138 static TR_NAME *tr_ff_get_trp_info_type(TR_FILTER_TARGET *target)
139 {
140   TRP_INFOREC *inforec=target->trp_inforec;
141   return tr_new_name(trp_inforec_type_to_string(inforec->type));
142 }
143
144 /** Handlers for TRP realm field */
145 static int tr_ff_cmp_trp_realm(TR_FILTER_TARGET *target, TR_NAME *val)
146 {
147   return tr_name_cmp(trp_upd_get_realm(target->trp_upd), val);
148 }
149
150 static TR_NAME *tr_ff_get_trp_realm(TR_FILTER_TARGET *target)
151 {
152   return tr_dup_name(trp_upd_get_realm(target->trp_upd));
153 }
154
155 /** Handlers for TID realm field */
156 static int tr_ff_cmp_tid_realm(TR_FILTER_TARGET *target, TR_NAME *val)
157 {
158   return tr_name_cmp(tid_req_get_realm(target->tid_req), val);
159 }
160
161 static TR_NAME *tr_ff_get_tid_realm(TR_FILTER_TARGET *target)
162 {
163   return tr_dup_name(tid_req_get_realm(target->tid_req));
164 }
165
166 /** Handlers for TRP community field */
167 static int tr_ff_cmp_trp_comm(TR_FILTER_TARGET *target, TR_NAME *val)
168 {
169   return tr_name_cmp(trp_upd_get_comm(target->trp_upd), val);
170 }
171
172 static TR_NAME *tr_ff_get_trp_comm(TR_FILTER_TARGET *target)
173 {
174   return tr_dup_name(trp_upd_get_comm(target->trp_upd));
175 }
176
177 /** Handlers for TID community field */
178 static int tr_ff_cmp_tid_comm(TR_FILTER_TARGET *target, TR_NAME *val)
179 {
180   return tr_name_cmp(tid_req_get_comm(target->tid_req), val);
181 }
182
183 static TR_NAME *tr_ff_get_tid_comm(TR_FILTER_TARGET *target)
184 {
185   return tr_dup_name(tid_req_get_comm(target->tid_req));
186 }
187
188 /** Handlers for TRP community_type field */
189 static TR_NAME *tr_ff_get_trp_comm_type(TR_FILTER_TARGET *target)
190 {
191   TR_NAME *type=NULL;
192
193   switch(trp_inforec_get_comm_type(target->trp_inforec)) {
194     case TR_COMM_APC:
195       type=tr_new_name("apc");
196       break;
197     case TR_COMM_COI:
198       type=tr_new_name("coi");
199       break;
200     default:
201       type=NULL;
202       break; /* unknown types always fail */
203   }
204
205   return type;
206 }
207
208 static int tr_ff_cmp_trp_comm_type(TR_FILTER_TARGET *target, TR_NAME *val)
209 {
210   TR_NAME *type=tr_ff_get_trp_comm_type(target);
211   int retval=0;
212
213   if (type==NULL)
214     retval=1;
215   else {
216     retval = tr_name_cmp(val, type);
217     tr_free_name(type);
218   }
219   return retval;
220 }
221
222 /** Handlers for TRP realm_role field */
223 static TR_NAME *tr_ff_get_trp_realm_role(TR_FILTER_TARGET *target)
224 {
225   TR_NAME *type=NULL;
226
227   switch(trp_inforec_get_role(target->trp_inforec)) {
228     case TR_ROLE_IDP:
229       type=tr_new_name("idp");
230       break;
231     case TR_ROLE_RP:
232       type=tr_new_name("rp");
233       break;
234     default:
235       type=NULL;
236       break; /* unknown types always fail */
237   }
238
239   return type;
240 }
241
242 static int tr_ff_cmp_trp_realm_role(TR_FILTER_TARGET *target, TR_NAME *val)
243 {
244   TR_NAME *type=tr_ff_get_trp_realm_role(target);
245   int retval=0;
246
247   if (type==NULL)
248     retval=1;
249   else {
250     retval = tr_name_cmp(val, type);
251     tr_free_name(type);
252   }
253   return retval;
254 }
255
256 /** Handlers for TRP apc field */
257 /* TODO: Handle multiple APCs, not just the first */
258 static int tr_ff_cmp_trp_apc(TR_FILTER_TARGET *target, TR_NAME *val)
259 {
260   return tr_name_cmp(tr_apc_get_id(trp_inforec_get_apcs(target->trp_inforec)), val);
261 }
262
263 static TR_NAME *tr_ff_get_trp_apc(TR_FILTER_TARGET *target)
264 {
265   TR_APC *apc=trp_inforec_get_apcs(target->trp_inforec);
266   if (apc==NULL)
267     return NULL;
268
269   return tr_dup_name(tr_apc_get_id(apc));
270 }
271
272 /** Handlers for TRP owner_realm field */
273 static int tr_ff_cmp_trp_owner_realm(TR_FILTER_TARGET *target, TR_NAME *val)
274 {
275   return tr_name_cmp(trp_inforec_get_owner_realm(target->trp_inforec), val);
276 }
277
278 static TR_NAME *tr_ff_get_trp_owner_realm(TR_FILTER_TARGET *target)
279 {
280   return tr_dup_name(trp_inforec_get_owner_realm(target->trp_inforec));
281 }
282
283 /** Handlers for TRP trust_router field */
284 static int tr_ff_cmp_trp_trust_router(TR_FILTER_TARGET *target, TR_NAME *val)
285 {
286   return tr_name_cmp(trp_inforec_get_trust_router(target->trp_inforec), val);
287 }
288
289 static TR_NAME *tr_ff_get_trp_trust_router(TR_FILTER_TARGET *target)
290 {
291   return tr_dup_name(trp_inforec_get_trust_router(target->trp_inforec));
292 }
293
294 /** Handlers for TRP owner_contact field */
295 static int tr_ff_cmp_trp_owner_contact(TR_FILTER_TARGET *target, TR_NAME *val)
296 {
297   return tr_name_cmp(trp_inforec_get_owner_contact(target->trp_inforec), val);
298 }
299
300 static TR_NAME *tr_ff_get_trp_owner_contact(TR_FILTER_TARGET *target)
301 {
302   return tr_dup_name(trp_inforec_get_owner_contact(target->trp_inforec));
303 }
304
305 /** Handlers for TID req original_coi field */
306 static int tr_ff_cmp_tid_orig_coi(TR_FILTER_TARGET *target, TR_NAME *val)
307 {
308   return tr_name_cmp(tid_req_get_orig_coi(target->tid_req), val);
309 }
310
311 static TR_NAME *tr_ff_get_tid_orig_coi(TR_FILTER_TARGET *target)
312 {
313   return tr_dup_name(tid_req_get_orig_coi(target->tid_req));
314 }
315
316 /**
317  * Filter field handler table
318  */
319 struct tr_filter_field_entry {
320   TR_FILTER_TYPE filter_type;
321   const char *name;
322   TR_FILTER_FIELD_CMP cmp;
323   TR_FILTER_FIELD_GET get;
324 };
325 static struct tr_filter_field_entry tr_filter_field_table[] = {
326     /* realm */
327     {TR_FILTER_TYPE_TID_INBOUND, "realm", tr_ff_cmp_tid_realm, tr_ff_get_tid_realm},
328     {TR_FILTER_TYPE_TRP_INBOUND, "realm", tr_ff_cmp_trp_realm, tr_ff_get_trp_realm},
329     {TR_FILTER_TYPE_TRP_OUTBOUND, "realm", tr_ff_cmp_trp_realm, tr_ff_get_trp_realm},
330
331     /* community */
332     {TR_FILTER_TYPE_TID_INBOUND, "comm", tr_ff_cmp_tid_comm, tr_ff_get_tid_comm},
333     {TR_FILTER_TYPE_TRP_INBOUND, "comm", tr_ff_cmp_trp_comm, tr_ff_get_trp_comm},
334     {TR_FILTER_TYPE_TRP_OUTBOUND, "comm", tr_ff_cmp_trp_comm, tr_ff_get_trp_comm},
335
336     /* community type */
337     {TR_FILTER_TYPE_TRP_INBOUND, "comm_type", tr_ff_cmp_trp_comm_type, tr_ff_get_trp_comm_type},
338     {TR_FILTER_TYPE_TRP_OUTBOUND, "comm_type", tr_ff_cmp_trp_comm_type, tr_ff_get_trp_comm_type},
339
340     /* realm role */
341     {TR_FILTER_TYPE_TRP_INBOUND, "realm_role", tr_ff_cmp_trp_realm_role, tr_ff_get_trp_realm_role},
342     {TR_FILTER_TYPE_TRP_OUTBOUND, "realm_role", tr_ff_cmp_trp_realm_role, tr_ff_get_trp_realm_role},
343
344     /* apc */
345     {TR_FILTER_TYPE_TRP_INBOUND, "apc", tr_ff_cmp_trp_apc, tr_ff_get_trp_apc},
346     {TR_FILTER_TYPE_TRP_OUTBOUND, "apc", tr_ff_cmp_trp_apc, tr_ff_get_trp_apc},
347
348     /* trust_router */
349     {TR_FILTER_TYPE_TRP_INBOUND, "trust_router", tr_ff_cmp_trp_trust_router, tr_ff_get_trp_trust_router},
350     {TR_FILTER_TYPE_TRP_OUTBOUND, "trust_router", tr_ff_cmp_trp_trust_router, tr_ff_get_trp_trust_router},
351
352     /* owner_realm */
353     {TR_FILTER_TYPE_TRP_INBOUND, "owner_realm", tr_ff_cmp_trp_owner_realm, tr_ff_get_trp_owner_realm},
354     {TR_FILTER_TYPE_TRP_OUTBOUND, "owner_realm", tr_ff_cmp_trp_owner_realm, tr_ff_get_trp_owner_realm},
355
356     /* owner_contact */
357     {TR_FILTER_TYPE_TRP_INBOUND, "owner_contact", tr_ff_cmp_trp_owner_contact, tr_ff_get_trp_owner_contact},
358     {TR_FILTER_TYPE_TRP_OUTBOUND, "owner_contact", tr_ff_cmp_trp_owner_contact, tr_ff_get_trp_owner_contact},
359
360     /* rp_realm */
361     {TR_FILTER_TYPE_TID_INBOUND, "rp_realm", tr_ff_cmp_tid_rp_realm, tr_ff_get_tid_rp_realm},
362
363     /* original coi */
364     {TR_FILTER_TYPE_TID_INBOUND, "original_coi", tr_ff_cmp_tid_orig_coi, tr_ff_get_tid_orig_coi},
365
366     /* info_type */
367     {TR_FILTER_TYPE_TRP_INBOUND, "info_type", tr_ff_cmp_trp_info_type, tr_ff_get_trp_info_type},
368     {TR_FILTER_TYPE_TRP_OUTBOUND, "info_type", tr_ff_cmp_trp_info_type, tr_ff_get_trp_info_type},
369
370     /* Unknown */
371     {TR_FILTER_TYPE_UNKNOWN, NULL } /* This must be the final entry */
372 };
373
374 /* TODO: support TRP metric field (requires > < comparison instead of wildcard match) */
375
376 static struct tr_filter_field_entry *tr_filter_field_entry(TR_FILTER_TYPE filter_type, TR_NAME *field_name)
377 {
378   unsigned int ii;
379
380   for (ii=0; tr_filter_field_table[ii].filter_type!=TR_FILTER_TYPE_UNKNOWN; ii++) {
381     if ((tr_filter_field_table[ii].filter_type==filter_type)
382         && (tr_name_cmp_str(field_name, tr_filter_field_table[ii].name)==0)) {
383       return tr_filter_field_table+ii;
384     }
385   }
386   return NULL;
387 }
388
389 /**
390  * Apply a filter to a target record or TID request.
391  *
392  * If one of the filter lines matches, out_action is set to the applicable action. If constraints
393  * is not NULL, the constraints from the matching filter line will be added to the constraint set
394  * *constraints, or to a new one if *constraints is NULL. In this case, TR_FILTER_MATCH will be
395  * returned.
396  *
397  * If there is no match, returns TR_FILTER_NO_MATCH, out_action is undefined, and constraints
398  * will not be changed.
399  *
400  * @param target Record or request to which the filter is applied
401  * @param filt Filter to apply
402  * @param constraints Pointer to existing set of constraints (NULL if not tracking constraints)
403  * @param out_action Action to be carried out (output)
404  * @return TR_FILTER_MATCH or TR_FILTER_NO_MATCH
405  */
406 int tr_filter_apply(TR_FILTER_TARGET *target,
407                     TR_FILTER *filt,
408                     TR_CONSTRAINT_SET **constraints,
409                     TR_FILTER_ACTION *out_action)
410 {
411   TALLOC_CTX *tmp_ctx = talloc_new(NULL);
412   TR_FILTER_ITER *filt_iter = tr_filter_iter_new(tmp_ctx);
413   TR_FLINE *this_fline = NULL;
414   TR_FLINE_ITER *fline_iter = tr_fline_iter_new(tmp_ctx);
415   TR_FSPEC *this_fspec = NULL;
416   int retval=TR_FILTER_NO_MATCH;
417
418   /* Default action is reject */
419   *out_action = TR_FILTER_ACTION_REJECT;
420
421   /* Validate filter */
422   if ((filt_iter == NULL) || (fline_iter == NULL) || (filt==NULL) || (filt->type==TR_FILTER_TYPE_UNKNOWN)) {
423     talloc_free(tmp_ctx);
424     return TR_FILTER_NO_MATCH;
425   }
426
427   /* Step through filter lines looking for a match. If a line matches, retval
428    * will be set to TR_FILTER_MATCH, so stop then. */
429   this_fline = tr_filter_iter_first(filt_iter, filt);
430   while(this_fline) {
431     /* Assume we are going to succeed. If any specs fail to match, we'll set
432      * this to TR_FILTER_NO_MATCH. */
433     retval=TR_FILTER_MATCH;
434     this_fspec = tr_fline_iter_first(fline_iter, this_fline);
435     while(this_fspec) {
436       if (!tr_fspec_matches(this_fspec, filt->type, target)) {
437         retval=TR_FILTER_NO_MATCH; /* set this in case this is the last filter line */
438         break; /* give up on this filter line */
439       }
440     this_fspec = tr_fline_iter_next(fline_iter);
441     }
442
443     if (retval==TR_FILTER_MATCH)
444       break;
445
446     this_fline = tr_filter_iter_next(filt_iter);
447   }
448
449   if (retval==TR_FILTER_MATCH) {
450     /* Matched line ii. Grab its action and constraints. */
451     *out_action = this_fline->action;
452     if (constraints!=NULL) {
453       /* if either constraint is missing, these are no-ops */
454       tr_constraint_add_to_set(constraints, this_fline->realm_cons);
455       tr_constraint_add_to_set(constraints, this_fline->domain_cons);
456     }
457   }
458
459   return retval;
460 }
461
462 void tr_fspec_free(TR_FSPEC *fspec)
463 {
464   talloc_free(fspec);
465 }
466
467 static int tr_fspec_destructor(void *obj)
468 {
469   TR_FSPEC *fspec = talloc_get_type_abort(obj, TR_FSPEC);
470
471   if (fspec->field != NULL)
472     tr_free_name(fspec->field);
473
474   if (fspec->match)
475     g_ptr_array_unref(fspec->match);
476
477   return 0;
478 }
479
480 TR_FSPEC *tr_fspec_new(TALLOC_CTX *mem_ctx)
481 {
482   TR_FSPEC *fspec = talloc(mem_ctx, TR_FSPEC);
483
484   if (fspec != NULL) {
485     fspec->field = NULL;
486     fspec->match = g_ptr_array_new_with_free_func((GDestroyNotify) tr_free_name);
487     if (fspec->match == NULL) {
488       talloc_free(fspec);
489       return NULL;
490     }
491     talloc_set_destructor((void *)fspec, tr_fspec_destructor);
492   }
493   return fspec;
494 }
495
496 TR_NAME *tr_fspec_add_match(TR_FSPEC *fspec, TR_NAME *match)
497 {
498   guint old_len = fspec->match->len;
499   g_ptr_array_add(fspec->match, match);
500
501   if (fspec->match->len == old_len)
502     return NULL; /* failed to add */
503
504   return match;
505 }
506
507 /* returns 1 if the spec matches */
508 int tr_fspec_matches(TR_FSPEC *fspec, TR_FILTER_TYPE ftype, TR_FILTER_TARGET *target)
509 {
510   struct tr_filter_field_entry *field=NULL;
511   TR_NAME *name=NULL;
512   int retval=0;
513
514   guint ii=0;
515
516   if (fspec==NULL)
517     return 0;
518
519   /* Look up how to handle the requested field */
520   field = tr_filter_field_entry(ftype, fspec->field);
521   if (field==NULL) {
522     tr_err("tr_fspec_matches: No entry to handle field %.*s for %*s filter.",
523            fspec->field->len, fspec->field->buf,
524            tr_filter_type_to_string(ftype));
525     return 0;
526   }
527
528   name=field->get(target);
529   if (name==NULL)
530     return 0; /* if there's no value, there's no match */
531
532   if (g_ptr_array_find_with_equal_func(fspec->match,
533                                        name,
534                                        (GEqualFunc) tr_name_prefix_wildcard_match,
535                                        &ii)) {
536     retval=1;
537     tr_debug("tr_fspec_matches: Field %.*s value \"%.*s\" matches \"%.*s\" for %s filter.",
538              fspec->field->len, fspec->field->buf,
539              name->len, name->buf,
540              ((TR_NAME *)g_ptr_array_index(fspec->match,ii))->len,
541              ((TR_NAME *)g_ptr_array_index(fspec->match,ii))->buf,
542              tr_filter_type_to_string(ftype));
543   }
544
545   if (!retval) {
546         tr_debug("tr_fspec_matches: Field %.*s value \"%.*s\" does not match for %s filter.",
547                  fspec->field->len, fspec->field->buf,
548                  name->len, name->buf,
549                  tr_filter_type_to_string(ftype));
550   }
551   tr_free_name(name);
552   return retval;
553 }
554
555 void tr_fline_free(TR_FLINE *fline)
556 {
557   talloc_free(fline);
558 }
559
560 TR_FSPEC *tr_fline_add_spec(TR_FLINE *fline, TR_FSPEC *spec)
561 {
562   guint old_len = fline->specs->len;
563   g_ptr_array_add(fline->specs, spec);
564
565   if (old_len == fline->specs->len)
566     return NULL; /* failed to add */
567
568   talloc_steal(fline, spec);
569   return spec;
570 }
571
572 static int tr_fline_destructor(void *object)
573 {
574   TR_FLINE *fline = talloc_get_type_abort(object, TR_FLINE);
575   if (fline->specs)
576     g_ptr_array_unref(fline->specs);
577   return 0;
578 }
579
580 TR_FLINE *tr_fline_new(TALLOC_CTX *mem_ctx)
581 {
582   TR_FLINE *fl = talloc(mem_ctx, TR_FLINE);
583
584   if (fl != NULL) {
585     fl->action = TR_FILTER_ACTION_UNKNOWN;
586     fl->realm_cons = NULL;
587     fl->domain_cons = NULL;
588     fl->specs = g_ptr_array_new();
589     if (fl->specs == NULL) {
590       talloc_free(fl);
591       return NULL;
592     }
593     talloc_set_destructor((void *)fl, tr_fline_destructor);
594   }
595   return fl;
596 }
597
598 static int tr_filter_destructor(void *object)
599 {
600   TR_FILTER *filt = talloc_get_type_abort(object, TR_FILTER);
601   if (filt->lines)
602     g_ptr_array_unref(filt->lines);
603   return 0;
604 }
605 TR_FILTER *tr_filter_new(TALLOC_CTX *mem_ctx)
606 {
607   TR_FILTER *f = talloc(mem_ctx, TR_FILTER);
608
609   if (f != NULL) {
610     f->type = TR_FILTER_TYPE_UNKNOWN;
611     f->lines = g_ptr_array_new();
612     if (f->lines == NULL) {
613       talloc_free(f);
614       return NULL;
615     }
616     talloc_set_destructor((void *)f, tr_filter_destructor);
617   }
618   return f;
619 }
620
621 void tr_filter_free(TR_FILTER *filt)
622 {
623   talloc_free(filt);
624 }
625
626 void tr_filter_set_type(TR_FILTER *filt, TR_FILTER_TYPE type)
627 {
628   filt->type = type;
629 }
630
631 TR_FILTER_TYPE tr_filter_get_type(TR_FILTER *filt)
632 {
633   return filt->type;
634 }
635
636 /**
637  * Add a TR_FLINE to a filter
638  *
639  * Steals the line into its context on success
640  *
641  * @param filt
642  * @param line
643  * @return line, or null on failure
644  */
645 TR_FLINE *tr_filter_add_line(TR_FILTER *filt, TR_FLINE *line)
646 {
647   guint old_len = filt->lines->len;
648   g_ptr_array_add(filt->lines, line);
649
650   if (old_len == filt->lines->len)
651     return NULL; /* failed to add */
652
653   talloc_steal(filt, line);
654   return line;
655 }
656
657 /**
658  * Iterator for TR_FLINES in a TR_FILTER
659  *
660  * @param mem_ctx
661  * @return
662  */
663 TR_FILTER_ITER *tr_filter_iter_new(TALLOC_CTX *mem_ctx)
664 {
665   TR_FILTER_ITER *iter = talloc(mem_ctx, TR_FILTER_ITER);
666   if (iter) {
667     iter->filter = NULL;
668   }
669   return iter;
670 }
671
672 void tr_filter_iter_free(TR_FILTER_ITER *iter)
673 {
674   talloc_free(iter);
675 }
676
677 TR_FLINE *tr_filter_iter_first(TR_FILTER_ITER *iter, TR_FILTER *filter)
678 {
679   if (!iter || !filter)
680     return NULL;
681
682   iter->filter = filter;
683   iter->ii = 0;
684   return tr_filter_iter_next(iter);
685 }
686
687 TR_FLINE *tr_filter_iter_next(TR_FILTER_ITER *iter)
688 {
689   if (!iter)
690     return NULL;
691
692   if (iter->ii < iter->filter->lines->len)
693     return g_ptr_array_index(iter->filter->lines, iter->ii++);
694   return NULL;
695 }
696
697 TR_FLINE_ITER *tr_fline_iter_new(TALLOC_CTX *mem_ctx)
698 {
699   TR_FLINE_ITER *iter = talloc(mem_ctx, TR_FLINE_ITER);
700   if (iter) {
701     iter->fline = NULL;
702   }
703   return iter;
704 }
705
706 void tr_fline_iter_free(TR_FLINE_ITER *iter)
707 {
708   talloc_free(iter);
709 }
710
711 TR_FSPEC * tr_fline_iter_first(TR_FLINE_ITER *iter, TR_FLINE *fline)
712 {
713   if (!iter || !fline)
714     return NULL;
715
716   iter->fline = fline;
717   iter->ii = 0;
718   return tr_fline_iter_next(iter);
719 }
720
721 TR_FSPEC * tr_fline_iter_next(TR_FLINE_ITER *iter)
722 {
723   if (!iter)
724     return NULL;
725
726   if (iter->ii < iter->fline->specs->len)
727     return g_ptr_array_index(iter->fline->specs, iter->ii++);
728   return NULL;
729 }
730
731 TR_FSPEC_ITER *tr_fspec_iter_new(TALLOC_CTX *mem_ctx)
732 {
733   TR_FSPEC_ITER *iter = talloc(mem_ctx, TR_FSPEC_ITER);
734   if (iter) {
735     iter->fspec = NULL;
736   }
737   return iter;
738 }
739
740 void tr_fspec_iter_free(TR_FSPEC_ITER *iter)
741 {
742   talloc_free(iter);
743 }
744
745 TR_NAME *tr_fspec_iter_first(TR_FSPEC_ITER *iter, TR_FSPEC *fspec)
746 {
747   if (!iter || !fspec)
748     return NULL;
749
750   iter->fspec = fspec;
751   iter->ii = 0;
752   return tr_fspec_iter_next(iter);
753 }
754
755 TR_NAME *tr_fspec_iter_next(TR_FSPEC_ITER *iter)
756 {
757   if (!iter)
758     return NULL;
759
760   if (iter->ii < iter->fspec->match->len)
761     return g_ptr_array_index(iter->fspec->match, iter->ii++);
762   return NULL;
763 }
764
765 /**
766  * Check that a filter is valid, i.e., can be processed.
767  *
768  * @param filt Filter to verify
769  * @return 1 if the filter is valid, 0 otherwise
770  */
771 int tr_filter_validate(TR_FILTER *filt)
772 {
773   TALLOC_CTX *tmp_ctx = talloc_new(NULL);
774   TR_FILTER_ITER *filt_iter = tr_filter_iter_new(tmp_ctx);
775   TR_FLINE *this_fline = NULL;
776   TR_FLINE_ITER *fline_iter = tr_fline_iter_new(tmp_ctx);
777   TR_FSPEC *this_fspec = NULL;
778   
779   if ((!filt) || (!filt_iter) || (!fline_iter)) {
780     talloc_free(tmp_ctx);
781     return 0;
782   }
783
784   /* check that we recognize the type */
785   switch(filt->type) {
786     case TR_FILTER_TYPE_TID_INBOUND:
787     case TR_FILTER_TYPE_TRP_INBOUND:
788     case TR_FILTER_TYPE_TRP_OUTBOUND:
789       break;
790
791     default:
792       talloc_free(tmp_ctx);
793       return 0; /* if we get here, either TR_FILTER_TYPE_UNKNOWN or an invalid value was found */
794   }
795   
796   this_fline = tr_filter_iter_first(filt_iter, filt);
797   while(this_fline) {
798     /* check that we recognize the action */
799     switch(this_fline->action) {
800       case TR_FILTER_ACTION_ACCEPT:
801       case TR_FILTER_ACTION_REJECT:
802         break;
803
804       default:
805         /* if we get here, either TR_FILTER_ACTION_UNKNOWN or an invalid value was found */
806         talloc_free(tmp_ctx);
807         return 0;
808     }
809
810     this_fspec = tr_fline_iter_first(fline_iter, this_fline);
811     while(this_fspec) {
812       if (!tr_filter_validate_spec_field(filt->type, this_fspec)) {
813         talloc_free(tmp_ctx);
814         return 0;
815       }
816
817       /* check that at least one match is defined*/
818       if (this_fspec->match->len == 0) {
819         talloc_free(tmp_ctx);
820         return 0;
821       }
822       this_fspec = tr_fline_iter_next(fline_iter);
823     }
824     this_fline = tr_filter_iter_next(filt_iter);
825   }
826
827   /* We ran the gauntlet. Success! */
828   talloc_free(tmp_ctx);
829   return 1;
830 }
831
832 int tr_filter_validate_spec_field(TR_FILTER_TYPE ftype, TR_FSPEC *fspec)
833 {
834   if ((fspec==NULL) || (tr_filter_field_entry(ftype, fspec->field)==NULL))
835     return 0; /* unknown field */
836
837   return 1;
838 }
839
840 /**
841  * Allocate a new filter set.
842  *
843  * @param mem_ctx Talloc context for the new set
844  * @return Pointer to new set, or null on error
845  */
846 TR_FILTER_SET *tr_filter_set_new(TALLOC_CTX *mem_ctx)
847 {
848   TR_FILTER_SET *set=talloc(mem_ctx, TR_FILTER_SET);
849   if (set!=NULL) {
850     set->next=NULL;
851     set->this=NULL;
852   }
853   return set;
854 }
855
856 /**
857  * Free a filter set
858  *
859  * @param fs Filter set to free
860  */
861 void tr_filter_set_free(TR_FILTER_SET *fs)
862 {
863   talloc_free(fs);
864 }
865
866 /**
867  * Find the tail of the filter set linked list.
868  *
869  * @param set Set to find tail of
870  * @return Last element in the list
871  */
872 static TR_FILTER_SET *tr_filter_set_tail(TR_FILTER_SET *set)
873 {
874   while (set->next)
875     set=set->next;
876   return set;
877 }
878
879 /**
880  * Add new filter to filter set.
881  *
882  * @param set Filter set
883  * @param new New filter to add
884  * @return 0 on success, nonzero on error
885  */
886 int tr_filter_set_add(TR_FILTER_SET *set, TR_FILTER *new)
887 {
888   TR_FILTER_SET *tail=NULL;
889
890   if (set->this==NULL)
891     tail=set;
892   else {
893     tail=tr_filter_set_tail(set);
894     tail->next=tr_filter_set_new(set);
895     if (tail->next==NULL)
896       return 1;
897     tail=tail->next;
898   }
899   tail->this=new;
900   talloc_steal(tail, new);
901   return 0;
902 }
903
904 /**
905  * Find a filter of a given type in the filter set. If there are multiple, returns the first one.
906  *
907  * @param set Filter set to search
908  * @param type Type of filter to find
909  * @return Borrowed pointer to the filter, or null if no filter of that type is found
910  */
911 TR_FILTER *tr_filter_set_get(TR_FILTER_SET *set, TR_FILTER_TYPE type)
912 {
913   TR_FILTER_SET *cur=set;
914   while(cur!=NULL) {
915     if ((cur->this != NULL) && (cur->this->type == type))
916       return cur->this;
917     cur=cur->next;
918   }
919   return NULL;
920 }
921
922 TR_FILTER_TYPE filter_type[]={TR_FILTER_TYPE_TID_INBOUND,
923                               TR_FILTER_TYPE_TRP_INBOUND,
924                               TR_FILTER_TYPE_TRP_OUTBOUND};
925 const char *filter_label[]={"tid_inbound",
926                             "trp_inbound",
927                             "trp_outbound"};
928 size_t num_filter_types=sizeof(filter_type)/sizeof(filter_type[0]);
929
930 const char *tr_filter_type_to_string(TR_FILTER_TYPE ftype)
931 {
932   size_t ii=0;
933
934   for (ii=0; ii<num_filter_types; ii++) {
935     if (ftype==filter_type[ii])
936       return filter_label[ii];
937   }
938   return "unknown";
939 }
940
941 TR_FILTER_TYPE tr_filter_type_from_string(const char *s)
942 {
943   size_t ii=0;
944
945   for(ii=0; ii<num_filter_types; ii++) {
946     if (0==strcmp(s, filter_label[ii]))
947       return filter_type[ii];
948   }
949   return TR_FILTER_TYPE_UNKNOWN;
950 }
951