2 * Copyright (c) 2012, 2013, JANET(UK)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
41 #include <tr_filter.h>
42 #include <trp_internal.h>
43 #include <tid_internal.h>
45 /* Function types for handling filter fields generally. All target values
46 * are represented as strings in a TR_NAME.
49 /* CMP functions return values like strcmp: 0 on match, <0 on target<val, >0 on target>val */
50 typedef int (*TR_FILTER_FIELD_CMP)(TR_FILTER_TARGET *target, TR_NAME *val);
51 /* get functions return TR_NAME format of the field value. Caller must free it. */
52 typedef TR_NAME *(*TR_FILTER_FIELD_GET)(TR_FILTER_TARGET *target);
54 static TR_FILTER_TARGET *tr_filter_target_new(TALLOC_CTX *mem_ctx)
56 TR_FILTER_TARGET *target=talloc(mem_ctx, TR_FILTER_TARGET);
58 target->trp_inforec=NULL;
64 void tr_filter_target_free(TR_FILTER_TARGET *target)
70 * Create a filter target for a TID request. Does not change the context of the request,
71 * so this is only valid until that is freed.
73 * @param mem_ctx talloc context for the object
74 * @param req TID request object
75 * @return pointer to a TR_FILTER_TARGET structure, or null on allocation failure
77 TR_FILTER_TARGET *tr_filter_target_tid_req(TALLOC_CTX *mem_ctx, TID_REQ *req)
79 TR_FILTER_TARGET *target=tr_filter_target_new(mem_ctx);
81 target->tid_req=req; /* borrowed, not adding to our context */
86 * Create a filter target for a TRP inforec. Does not change the context of the inforec or duplicate TR_NAMEs,
87 * so this is only valid until those are freed.
89 * @param mem_ctx talloc context for the object
90 * @param upd Update containing the TRP inforec
91 * @param inforec TRP inforec
92 * @return pointer to a TR_FILTER_TARGET structure, or null on allocation failure
94 TR_FILTER_TARGET *tr_filter_target_trp_inforec(TALLOC_CTX *mem_ctx, TRP_UPD *upd, TRP_INFOREC *inforec)
96 TR_FILTER_TARGET *target=tr_filter_target_new(mem_ctx);
98 target->trp_inforec = inforec; /* borrowed, not adding to our context */
104 /** Handler functions for TID RP_REALM field */
105 static int tr_ff_cmp_tid_rp_realm(TR_FILTER_TARGET *target, TR_NAME *val)
107 return tr_name_cmp(tid_req_get_rp_realm(target->tid_req), val);
110 static TR_NAME *tr_ff_get_tid_rp_realm(TR_FILTER_TARGET *target)
112 return tr_dup_name(tid_req_get_rp_realm(target->tid_req));
115 /** Handler functions for TRP info_type field */
116 static int tr_ff_cmp_trp_info_type(TR_FILTER_TARGET *target, TR_NAME *val)
118 TRP_INFOREC *inforec=target->trp_inforec;
125 /* nothing matches unknown */
126 if (inforec->type==TRP_INFOREC_TYPE_UNKNOWN)
129 valstr = tr_name_strdup(val); /* get this as an official null-terminated string */
130 val_type = trp_inforec_type_from_string(valstr);
133 /* we do not define an ordering of info types */
134 return (val_type==inforec->type);
137 static TR_NAME *tr_ff_get_trp_info_type(TR_FILTER_TARGET *target)
139 TRP_INFOREC *inforec=target->trp_inforec;
140 return tr_new_name(trp_inforec_type_to_string(inforec->type));
143 /** Handlers for TRP realm field */
144 static int tr_ff_cmp_trp_realm(TR_FILTER_TARGET *target, TR_NAME *val)
146 return tr_name_cmp(trp_upd_get_realm(target->trp_upd), val);
149 static TR_NAME *tr_ff_get_trp_realm(TR_FILTER_TARGET *target)
151 return tr_dup_name(trp_upd_get_realm(target->trp_upd));
154 /** Handlers for TID realm field */
155 static int tr_ff_cmp_tid_realm(TR_FILTER_TARGET *target, TR_NAME *val)
157 return tr_name_cmp(tid_req_get_realm(target->tid_req), val);
160 static TR_NAME *tr_ff_get_tid_realm(TR_FILTER_TARGET *target)
162 return tr_dup_name(tid_req_get_realm(target->tid_req));
165 /** Handlers for TRP community field */
166 static int tr_ff_cmp_trp_comm(TR_FILTER_TARGET *target, TR_NAME *val)
168 return tr_name_cmp(trp_upd_get_comm(target->trp_upd), val);
171 static TR_NAME *tr_ff_get_trp_comm(TR_FILTER_TARGET *target)
173 return tr_dup_name(trp_upd_get_comm(target->trp_upd));
176 /** Handlers for TID community field */
177 static int tr_ff_cmp_tid_comm(TR_FILTER_TARGET *target, TR_NAME *val)
179 return tr_name_cmp(tid_req_get_comm(target->tid_req), val);
182 static TR_NAME *tr_ff_get_tid_comm(TR_FILTER_TARGET *target)
184 return tr_dup_name(tid_req_get_comm(target->tid_req));
187 /** Handlers for TRP community_type field */
188 static TR_NAME *tr_ff_get_trp_comm_type(TR_FILTER_TARGET *target)
192 switch(trp_inforec_get_comm_type(target->trp_inforec)) {
194 type=tr_new_name("apc");
197 type=tr_new_name("coi");
201 break; /* unknown types always fail */
207 static int tr_ff_cmp_trp_comm_type(TR_FILTER_TARGET *target, TR_NAME *val)
209 TR_NAME *type=tr_ff_get_trp_comm_type(target);
215 retval = tr_name_cmp(val, type);
221 /** Handlers for TRP realm_role field */
222 static TR_NAME *tr_ff_get_trp_realm_role(TR_FILTER_TARGET *target)
226 switch(trp_inforec_get_role(target->trp_inforec)) {
228 type=tr_new_name("idp");
231 type=tr_new_name("rp");
235 break; /* unknown types always fail */
241 static int tr_ff_cmp_trp_realm_role(TR_FILTER_TARGET *target, TR_NAME *val)
243 TR_NAME *type=tr_ff_get_trp_realm_role(target);
249 retval = tr_name_cmp(val, type);
255 /** Handlers for TRP apc field */
256 static int tr_ff_cmp_trp_apc(TR_FILTER_TARGET *target, TR_NAME *val)
258 /* TODO: Handle multiple APCs, not just the first */
259 return tr_name_cmp(tr_apc_get_id(trp_inforec_get_apcs(target->trp_inforec)), val);
262 static TR_NAME *tr_ff_get_trp_apc(TR_FILTER_TARGET *target)
264 /* TODO: Handle multiple APCs, not just the first */
265 return tr_dup_name(tr_apc_get_id(trp_inforec_get_apcs(target->trp_inforec)));
268 /** Handlers for TRP owner_realm field */
269 static int tr_ff_cmp_trp_owner_realm(TR_FILTER_TARGET *target, TR_NAME *val)
271 return tr_name_cmp(trp_inforec_get_owner_realm(target->trp_inforec), val);
274 static TR_NAME *tr_ff_get_trp_owner_realm(TR_FILTER_TARGET *target)
276 return tr_dup_name(trp_inforec_get_owner_realm(target->trp_inforec));
279 /** Handlers for TRP trust_router field */
280 static int tr_ff_cmp_trp_trust_router(TR_FILTER_TARGET *target, TR_NAME *val)
282 return tr_name_cmp(trp_inforec_get_trust_router(target->trp_inforec), val);
285 static TR_NAME *tr_ff_get_trp_trust_router(TR_FILTER_TARGET *target)
287 return tr_dup_name(trp_inforec_get_trust_router(target->trp_inforec));
290 /** Handlers for TRP owner_contact field */
291 static int tr_ff_cmp_trp_owner_contact(TR_FILTER_TARGET *target, TR_NAME *val)
293 return tr_name_cmp(trp_inforec_get_owner_contact(target->trp_inforec), val);
296 static TR_NAME *tr_ff_get_trp_owner_contact(TR_FILTER_TARGET *target)
298 return tr_dup_name(trp_inforec_get_owner_contact(target->trp_inforec));
301 /** Handlers for TID req original_coi field */
302 static int tr_ff_cmp_tid_orig_coi(TR_FILTER_TARGET *target, TR_NAME *val)
304 return tr_name_cmp(tid_req_get_orig_coi(target->tid_req), val);
307 static TR_NAME *tr_ff_get_tid_orig_coi(TR_FILTER_TARGET *target)
309 return tr_dup_name(tid_req_get_orig_coi(target->tid_req));
313 * Filter field handler table
315 struct tr_filter_field_entry {
316 TR_FILTER_TYPE filter_type;
318 TR_FILTER_FIELD_CMP cmp;
319 TR_FILTER_FIELD_GET get;
321 static struct tr_filter_field_entry tr_filter_field_table[] = {
323 {TR_FILTER_TYPE_TID_INBOUND, "realm", tr_ff_cmp_tid_realm, tr_ff_get_tid_realm},
324 {TR_FILTER_TYPE_TRP_INBOUND, "realm", tr_ff_cmp_trp_realm, tr_ff_get_trp_realm},
325 {TR_FILTER_TYPE_TRP_OUTBOUND, "realm", tr_ff_cmp_trp_realm, tr_ff_get_trp_realm},
328 {TR_FILTER_TYPE_TID_INBOUND, "community", tr_ff_cmp_tid_comm, tr_ff_get_tid_comm},
329 {TR_FILTER_TYPE_TRP_INBOUND, "community", tr_ff_cmp_trp_comm, tr_ff_get_trp_comm},
330 {TR_FILTER_TYPE_TRP_OUTBOUND, "community", tr_ff_cmp_trp_comm, tr_ff_get_trp_comm},
333 {TR_FILTER_TYPE_TRP_INBOUND, "community_type", tr_ff_cmp_trp_comm_type, tr_ff_get_trp_comm_type},
334 {TR_FILTER_TYPE_TRP_OUTBOUND, "community_type", tr_ff_cmp_trp_comm_type, tr_ff_get_trp_comm_type},
337 {TR_FILTER_TYPE_TRP_INBOUND, "realm_role", tr_ff_cmp_trp_realm_role, tr_ff_get_trp_realm_role},
338 {TR_FILTER_TYPE_TRP_OUTBOUND, "realm_role", tr_ff_cmp_trp_realm_role, tr_ff_get_trp_realm_role},
341 {TR_FILTER_TYPE_TRP_INBOUND, "apc", tr_ff_cmp_trp_apc, tr_ff_get_trp_apc},
342 {TR_FILTER_TYPE_TRP_OUTBOUND, "apc", tr_ff_cmp_trp_apc, tr_ff_get_trp_apc},
345 {TR_FILTER_TYPE_TRP_INBOUND, "trust_router", tr_ff_cmp_trp_trust_router, tr_ff_get_trp_trust_router},
346 {TR_FILTER_TYPE_TRP_OUTBOUND, "trust_router", tr_ff_cmp_trp_trust_router, tr_ff_get_trp_trust_router},
349 {TR_FILTER_TYPE_TRP_INBOUND, "owner_realm", tr_ff_cmp_trp_owner_realm, tr_ff_get_trp_owner_realm},
350 {TR_FILTER_TYPE_TRP_OUTBOUND, "owner_realm", tr_ff_cmp_trp_owner_realm, tr_ff_get_trp_owner_realm},
353 {TR_FILTER_TYPE_TRP_INBOUND, "owner_contact", tr_ff_cmp_trp_owner_contact, tr_ff_get_trp_owner_contact},
354 {TR_FILTER_TYPE_TRP_OUTBOUND, "owner_contact", tr_ff_cmp_trp_owner_contact, tr_ff_get_trp_owner_contact},
357 {TR_FILTER_TYPE_TID_INBOUND, "rp_realm", tr_ff_cmp_tid_rp_realm, tr_ff_get_tid_rp_realm},
360 {TR_FILTER_TYPE_TID_INBOUND, "original_coi", tr_ff_cmp_tid_orig_coi, tr_ff_get_tid_orig_coi},
363 {TR_FILTER_TYPE_TRP_INBOUND, "info_type", tr_ff_cmp_trp_info_type, tr_ff_get_trp_info_type},
364 {TR_FILTER_TYPE_TRP_OUTBOUND, "info_type", tr_ff_cmp_trp_info_type, tr_ff_get_trp_info_type},
367 {TR_FILTER_TYPE_UNKNOWN, NULL } /* This must be the final entry */
370 /* TODO: support TRP metric field (requires > < comparison instead of wildcard match) */
372 static struct tr_filter_field_entry *tr_filter_field_entry(TR_FILTER_TYPE filter_type, TR_NAME *field_name)
376 for (ii=0; tr_filter_field_table[ii].filter_type!=TR_FILTER_TYPE_UNKNOWN; ii++) {
377 if ((tr_filter_field_table[ii].filter_type==filter_type)
378 && (tr_name_cmp_str(field_name, tr_filter_field_table[ii].name)==0)) {
379 return tr_filter_field_table+ii;
386 * Apply a filter to a target record or TID request.
388 * If one of the filter lines matches, out_action is set to the applicable action. If constraints
389 * is not NULL, the constraints from the matching filter line will be added to the constraint set
390 * *constraints, or to a new one if *constraints is NULL. In this case, TR_FILTER_MATCH will be
393 * If there is no match, returns TR_FILTER_NO_MATCH, out_action is undefined, and constraints
394 * will not be changed.
396 * @param target Record or request to which the filter is applied
397 * @param filt Filter to apply
398 * @param constraints Pointer to existing set of constraints (NULL if not tracking constraints)
399 * @param out_action Action to be carried out (output)
400 * @return TR_FILTER_MATCH or TR_FILTER_NO_MATCH
402 int tr_filter_apply(TR_FILTER_TARGET *target,
404 TR_CONSTRAINT_SET **constraints,
405 TR_FILTER_ACTION *out_action)
407 unsigned int ii=0, jj=0;
408 int retval=TR_FILTER_NO_MATCH;
410 /* Default action is reject */
411 *out_action = TR_FILTER_ACTION_REJECT;
413 /* Validate filter */
414 if ((filt==NULL) || (filt->type==TR_FILTER_TYPE_UNKNOWN))
415 return TR_FILTER_NO_MATCH;
417 /* Step through filter lines looking for a match. If a line matches, retval
418 * will be set to TR_FILTER_MATCH, so stop then. */
419 for (ii=0, retval=TR_FILTER_NO_MATCH;
420 ii<TR_MAX_FILTER_LINES;
422 /* skip empty lines (these shouldn't really happen) */
423 if (filt->lines[ii]==NULL)
426 /* Assume we are going to succeed. If any specs fail to match, we'll set
427 * this to TR_FILTER_NO_MATCH. */
428 retval=TR_FILTER_MATCH;
429 for (jj=0; jj<TR_MAX_FILTER_SPECS; jj++) {
430 /* skip empty specs (these shouldn't really happen either) */
431 if (filt->lines[ii]->specs[jj]==NULL)
434 if (!tr_fspec_matches(filt->lines[ii]->specs[jj], filt->type, target)) {
435 retval=TR_FILTER_NO_MATCH; /* set this in case this is the last filter line */
436 break; /* give up on this filter line */
440 if (retval==TR_FILTER_MATCH)
444 if (retval==TR_FILTER_MATCH) {
445 /* Matched line ii. Grab its action and constraints. */
446 *out_action = filt->lines[ii]->action;
447 if (constraints!=NULL) {
448 /* if either constraint is missing, these are no-ops */
449 tr_constraint_add_to_set(constraints, filt->lines[ii]->realm_cons);
450 tr_constraint_add_to_set(constraints, filt->lines[ii]->domain_cons);
457 void tr_fspec_free(TR_FSPEC *fspec)
462 static int tr_fspec_destructor(void *obj)
464 TR_FSPEC *fspec = talloc_get_type_abort(obj, TR_FSPEC);
467 if (fspec->field != NULL)
468 tr_free_name(fspec->field);
469 for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++) {
470 if (fspec->match[ii] != NULL)
471 tr_free_name(fspec->match[ii]);
476 TR_FSPEC *tr_fspec_new(TALLOC_CTX *mem_ctx)
478 TR_FSPEC *fspec = talloc(mem_ctx, TR_FSPEC);
483 for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++)
484 fspec->match[ii] = NULL;
486 talloc_set_destructor((void *)fspec, tr_fspec_destructor);
491 void tr_fspec_add_match(TR_FSPEC *fspec, TR_NAME *match)
494 for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++) {
495 if (fspec->match[ii]==NULL) {
496 fspec->match[ii]=match;
500 /* TODO: handle case that adding the match failed */
503 /* returns 1 if the spec matches */
504 int tr_fspec_matches(TR_FSPEC *fspec, TR_FILTER_TYPE ftype, TR_FILTER_TARGET *target)
506 struct tr_filter_field_entry *field=NULL;
515 /* Look up how to handle the requested field */
516 field = tr_filter_field_entry(ftype, fspec->field);
520 name=field->get(target);
521 for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++) {
522 if (fspec->match[ii]!=NULL) {
523 if (tr_name_prefix_wildcard_match(name, fspec->match[ii]))
531 void tr_fline_free(TR_FLINE *fline)
536 TR_FLINE *tr_fline_new(TALLOC_CTX *mem_ctx)
538 TR_FLINE *fl = talloc(mem_ctx, TR_FLINE);
542 fl->action = TR_FILTER_ACTION_UNKNOWN;
543 fl->realm_cons = NULL;
544 fl->domain_cons = NULL;
545 for (ii = 0; ii < TR_MAX_FILTER_SPECS; ii++)
546 fl->specs[ii] = NULL;
551 TR_FILTER *tr_filter_new(TALLOC_CTX *mem_ctx)
553 TR_FILTER *f = talloc(mem_ctx, TR_FILTER);
557 f->type = TR_FILTER_TYPE_UNKNOWN;
558 for (ii = 0; ii < TR_MAX_FILTER_LINES; ii++)
564 void tr_filter_free(TR_FILTER *filt)
569 void tr_filter_set_type(TR_FILTER *filt, TR_FILTER_TYPE type)
574 TR_FILTER_TYPE tr_filter_get_type(TR_FILTER *filt)
580 * Check that a filter is valid, i.e., can be processed.
582 * @param filt Filter to verify
583 * @return 1 if the filter is valid, 0 otherwise
585 int tr_filter_validate(TR_FILTER *filt)
587 size_t ii=0, jj=0, kk=0;
592 /* check that we recognize the type */
594 case TR_FILTER_TYPE_TID_INBOUND:
595 case TR_FILTER_TYPE_TRP_INBOUND:
596 case TR_FILTER_TYPE_TRP_OUTBOUND:
600 return 0; /* if we get here, either TR_FILTER_TYPE_UNKNOWN or an invalid value was found */
602 for (ii=0; ii<TR_MAX_FILTER_LINES; ii++) {
603 if (filt->lines[ii]==NULL)
604 continue; /* an empty filter line is valid */
606 /* check that we recognize the action */
607 switch(filt->lines[ii]->action) {
608 case TR_FILTER_ACTION_ACCEPT:
609 case TR_FILTER_ACTION_REJECT:
613 /* if we get here, either TR_FILTER_ACTION_UNKNOWN or an invalid value was found */
617 for (jj=0; jj<TR_MAX_FILTER_SPECS; jj++) {
618 if (filt->lines[ii]->specs[jj]==NULL)
619 continue; /* an empty filter spec is valid */
621 if (!tr_filter_validate_spec_field(filt->type, filt->lines[ii]->specs[jj]))
624 /* check that at least one match is non-null */
625 for (kk=0; kk<TR_MAX_FILTER_SPEC_MATCHES; kk++) {
626 if (filt->lines[ii]->specs[jj]->match[kk]!=NULL)
629 if (kk==TR_MAX_FILTER_SPEC_MATCHES)
634 /* We ran the gauntlet. Success! */
638 int tr_filter_validate_spec_field(TR_FILTER_TYPE ftype, TR_FSPEC *fspec)
640 if ((fspec==NULL) || (tr_filter_field_entry(ftype, fspec->field)==NULL))
641 return 0; /* unknown field */
647 * Allocate a new filter set.
649 * @param mem_ctx Talloc context for the new set
650 * @return Pointer to new set, or null on error
652 TR_FILTER_SET *tr_filter_set_new(TALLOC_CTX *mem_ctx)
654 TR_FILTER_SET *set=talloc(mem_ctx, TR_FILTER_SET);
665 * @param fs Filter set to free
667 void tr_filter_set_free(TR_FILTER_SET *fs)
673 * Find the tail of the filter set linked list.
675 * @param set Set to find tail of
676 * @return Last element in the list
678 static TR_FILTER_SET *tr_filter_set_tail(TR_FILTER_SET *set)
686 * Add new filter to filter set.
688 * @param set Filter set
689 * @param new New filter to add
690 * @return 0 on success, nonzero on error
692 int tr_filter_set_add(TR_FILTER_SET *set, TR_FILTER *new)
694 TR_FILTER_SET *tail=NULL;
699 tail=tr_filter_set_tail(set);
700 tail->next=tr_filter_set_new(set);
701 if (tail->next==NULL)
706 talloc_steal(tail, new);
711 * Find a filter of a given type in the filter set. If there are multiple, returns the first one.
713 * @param set Filter set to search
714 * @param type Type of filter to find
715 * @return Borrowed pointer to the filter, or null if no filter of that type is found
717 TR_FILTER *tr_filter_set_get(TR_FILTER_SET *set, TR_FILTER_TYPE type)
719 TR_FILTER_SET *cur=set;
721 if ((cur->this != NULL) && (cur->this->type == type))
728 TR_FILTER_TYPE filter_type[]={TR_FILTER_TYPE_TID_INBOUND,
729 TR_FILTER_TYPE_TRP_INBOUND,
730 TR_FILTER_TYPE_TRP_OUTBOUND};
731 const char *filter_label[]={"tid_inbound",
734 size_t num_filter_types=sizeof(filter_type)/sizeof(filter_type[0]);
736 const char *tr_filter_type_to_string(TR_FILTER_TYPE ftype)
740 for (ii=0; ii<num_filter_types; ii++) {
741 if (ftype==filter_type[ii])
742 return filter_label[ii];
747 TR_FILTER_TYPE tr_filter_type_from_string(const char *s)
751 for(ii=0; ii<num_filter_types; ii++) {
752 if (0==strcmp(s, filter_label[ii]))
753 return filter_type[ii];
755 return TR_FILTER_TYPE_UNKNOWN;