+ valstr = tr_name_strdup(val); /* get this as an official null-terminated string */
+ val_type = trp_inforec_type_from_string(valstr);
+ free(valstr);
+
+ /* we do not define an ordering of info types */
+ return (val_type==inforec->type);
+}
+
+static TR_NAME *tr_ff_get_trp_info_type(TR_FILTER_TARGET *target)
+{
+ TRP_INFOREC *inforec=target->trp_inforec;
+ return tr_new_name(trp_inforec_type_to_string(inforec->type));
+}
+
+/** Handlers for TRP realm field */
+static int tr_ff_cmp_trp_realm(TR_FILTER_TARGET *target, TR_NAME *val)
+{
+ return tr_name_cmp(trp_upd_get_realm(target->trp_upd), val);
+}
+
+static TR_NAME *tr_ff_get_trp_realm(TR_FILTER_TARGET *target)
+{
+ return tr_dup_name(trp_upd_get_realm(target->trp_upd));
+}
+
+/** Handlers for TID realm field */
+static int tr_ff_cmp_tid_realm(TR_FILTER_TARGET *target, TR_NAME *val)
+{
+ return tr_name_cmp(tid_req_get_realm(target->tid_req), val);
+}
+
+static TR_NAME *tr_ff_get_tid_realm(TR_FILTER_TARGET *target)
+{
+ return tr_dup_name(tid_req_get_realm(target->tid_req));
+}
+
+/** Handlers for TRP community field */
+static int tr_ff_cmp_trp_comm(TR_FILTER_TARGET *target, TR_NAME *val)
+{
+ return tr_name_cmp(trp_upd_get_comm(target->trp_upd), val);
+}
+
+static TR_NAME *tr_ff_get_trp_comm(TR_FILTER_TARGET *target)
+{
+ return tr_dup_name(trp_upd_get_comm(target->trp_upd));
+}
+
+/** Handlers for TID community field */
+static int tr_ff_cmp_tid_comm(TR_FILTER_TARGET *target, TR_NAME *val)
+{
+ return tr_name_cmp(tid_req_get_comm(target->tid_req), val);
+}
+
+static TR_NAME *tr_ff_get_tid_comm(TR_FILTER_TARGET *target)
+{
+ return tr_dup_name(tid_req_get_comm(target->tid_req));
+}
+
+/** Handlers for TRP community_type field */
+static TR_NAME *tr_ff_get_trp_comm_type(TR_FILTER_TARGET *target)
+{
+ TR_NAME *type=NULL;
+
+ switch(trp_inforec_get_comm_type(target->trp_inforec)) {
+ case TR_COMM_APC:
+ type=tr_new_name("apc");
+ break;
+ case TR_COMM_COI:
+ type=tr_new_name("coi");
+ break;
+ default:
+ type=NULL;
+ break; /* unknown types always fail */
+ }
+
+ return type;
+}
+
+static int tr_ff_cmp_trp_comm_type(TR_FILTER_TARGET *target, TR_NAME *val)
+{
+ TR_NAME *type=tr_ff_get_trp_comm_type(target);
+ int retval=0;
+
+ if (type==NULL)
+ retval=1;
+ else {
+ retval = tr_name_cmp(val, type);
+ tr_free_name(type);
+ }
+ return retval;
+}
+
+/** Handlers for TRP realm_role field */
+static TR_NAME *tr_ff_get_trp_realm_role(TR_FILTER_TARGET *target)
+{
+ TR_NAME *type=NULL;
+
+ switch(trp_inforec_get_role(target->trp_inforec)) {
+ case TR_ROLE_IDP:
+ type=tr_new_name("idp");
+ break;
+ case TR_ROLE_RP:
+ type=tr_new_name("rp");
+ break;
+ default:
+ type=NULL;
+ break; /* unknown types always fail */
+ }
+
+ return type;
+}
+
+static int tr_ff_cmp_trp_realm_role(TR_FILTER_TARGET *target, TR_NAME *val)
+{
+ TR_NAME *type=tr_ff_get_trp_realm_role(target);
+ int retval=0;
+
+ if (type==NULL)
+ retval=1;
+ else {
+ retval = tr_name_cmp(val, type);
+ tr_free_name(type);
+ }
+ return retval;
+}
+
+/** Handlers for TRP apc field */
+/* TODO: Handle multiple APCs, not just the first */
+static int tr_ff_cmp_trp_apc(TR_FILTER_TARGET *target, TR_NAME *val)
+{
+ return tr_name_cmp(tr_apc_get_id(trp_inforec_get_apcs(target->trp_inforec)), val);
+}
+
+static TR_NAME *tr_ff_get_trp_apc(TR_FILTER_TARGET *target)
+{
+ TR_APC *apc=trp_inforec_get_apcs(target->trp_inforec);
+ if (apc==NULL)
+ return NULL;
+
+ return tr_dup_name(tr_apc_get_id(apc));
+}
+
+/** Handlers for TRP owner_realm field */
+static int tr_ff_cmp_trp_owner_realm(TR_FILTER_TARGET *target, TR_NAME *val)
+{
+ return tr_name_cmp(trp_inforec_get_owner_realm(target->trp_inforec), val);
+}
+
+static TR_NAME *tr_ff_get_trp_owner_realm(TR_FILTER_TARGET *target)
+{
+ return tr_dup_name(trp_inforec_get_owner_realm(target->trp_inforec));
+}
+
+/** Handlers for TRP trust_router field */
+static int tr_ff_cmp_trp_trust_router(TR_FILTER_TARGET *target, TR_NAME *val)
+{
+ return tr_name_cmp(trp_inforec_get_trust_router(target->trp_inforec), val);
+}
+
+static TR_NAME *tr_ff_get_trp_trust_router(TR_FILTER_TARGET *target)
+{
+ return tr_dup_name(trp_inforec_get_trust_router(target->trp_inforec));
+}
+
+/** Handlers for TRP owner_contact field */
+static int tr_ff_cmp_trp_owner_contact(TR_FILTER_TARGET *target, TR_NAME *val)
+{
+ return tr_name_cmp(trp_inforec_get_owner_contact(target->trp_inforec), val);
+}
+
+static TR_NAME *tr_ff_get_trp_owner_contact(TR_FILTER_TARGET *target)
+{
+ return tr_dup_name(trp_inforec_get_owner_contact(target->trp_inforec));
+}
+
+/** Handlers for TID req original_coi field */
+static int tr_ff_cmp_tid_orig_coi(TR_FILTER_TARGET *target, TR_NAME *val)
+{
+ return tr_name_cmp(tid_req_get_orig_coi(target->tid_req), val);
+}
+
+static TR_NAME *tr_ff_get_tid_orig_coi(TR_FILTER_TARGET *target)
+{
+ return tr_dup_name(tid_req_get_orig_coi(target->tid_req));
+}
+
+/**
+ * Filter field handler table
+ */
+struct tr_filter_field_entry {
+ TR_FILTER_TYPE filter_type;
+ const char *name;
+ TR_FILTER_FIELD_CMP cmp;
+ TR_FILTER_FIELD_GET get;
+};
+static struct tr_filter_field_entry tr_filter_field_table[] = {
+ /* realm */
+ {TR_FILTER_TYPE_TID_INBOUND, "realm", tr_ff_cmp_tid_realm, tr_ff_get_tid_realm},
+ {TR_FILTER_TYPE_TRP_INBOUND, "realm", tr_ff_cmp_trp_realm, tr_ff_get_trp_realm},
+ {TR_FILTER_TYPE_TRP_OUTBOUND, "realm", tr_ff_cmp_trp_realm, tr_ff_get_trp_realm},
+
+ /* community */
+ {TR_FILTER_TYPE_TID_INBOUND, "comm", tr_ff_cmp_tid_comm, tr_ff_get_tid_comm},
+ {TR_FILTER_TYPE_TRP_INBOUND, "comm", tr_ff_cmp_trp_comm, tr_ff_get_trp_comm},
+ {TR_FILTER_TYPE_TRP_OUTBOUND, "comm", tr_ff_cmp_trp_comm, tr_ff_get_trp_comm},
+
+ /* community type */
+ {TR_FILTER_TYPE_TRP_INBOUND, "comm_type", tr_ff_cmp_trp_comm_type, tr_ff_get_trp_comm_type},
+ {TR_FILTER_TYPE_TRP_OUTBOUND, "comm_type", tr_ff_cmp_trp_comm_type, tr_ff_get_trp_comm_type},
+
+ /* realm role */
+ {TR_FILTER_TYPE_TRP_INBOUND, "realm_role", tr_ff_cmp_trp_realm_role, tr_ff_get_trp_realm_role},
+ {TR_FILTER_TYPE_TRP_OUTBOUND, "realm_role", tr_ff_cmp_trp_realm_role, tr_ff_get_trp_realm_role},
+
+ /* apc */
+ {TR_FILTER_TYPE_TRP_INBOUND, "apc", tr_ff_cmp_trp_apc, tr_ff_get_trp_apc},
+ {TR_FILTER_TYPE_TRP_OUTBOUND, "apc", tr_ff_cmp_trp_apc, tr_ff_get_trp_apc},
+
+ /* trust_router */
+ {TR_FILTER_TYPE_TRP_INBOUND, "trust_router", tr_ff_cmp_trp_trust_router, tr_ff_get_trp_trust_router},
+ {TR_FILTER_TYPE_TRP_OUTBOUND, "trust_router", tr_ff_cmp_trp_trust_router, tr_ff_get_trp_trust_router},
+
+ /* owner_realm */
+ {TR_FILTER_TYPE_TRP_INBOUND, "owner_realm", tr_ff_cmp_trp_owner_realm, tr_ff_get_trp_owner_realm},
+ {TR_FILTER_TYPE_TRP_OUTBOUND, "owner_realm", tr_ff_cmp_trp_owner_realm, tr_ff_get_trp_owner_realm},
+
+ /* owner_contact */
+ {TR_FILTER_TYPE_TRP_INBOUND, "owner_contact", tr_ff_cmp_trp_owner_contact, tr_ff_get_trp_owner_contact},
+ {TR_FILTER_TYPE_TRP_OUTBOUND, "owner_contact", tr_ff_cmp_trp_owner_contact, tr_ff_get_trp_owner_contact},
+
+ /* rp_realm */
+ {TR_FILTER_TYPE_TID_INBOUND, "rp_realm", tr_ff_cmp_tid_rp_realm, tr_ff_get_tid_rp_realm},
+
+ /* original coi */
+ {TR_FILTER_TYPE_TID_INBOUND, "original_coi", tr_ff_cmp_tid_orig_coi, tr_ff_get_tid_orig_coi},
+
+ /* info_type */
+ {TR_FILTER_TYPE_TRP_INBOUND, "info_type", tr_ff_cmp_trp_info_type, tr_ff_get_trp_info_type},
+ {TR_FILTER_TYPE_TRP_OUTBOUND, "info_type", tr_ff_cmp_trp_info_type, tr_ff_get_trp_info_type},
+
+ /* Unknown */
+ {TR_FILTER_TYPE_UNKNOWN, NULL } /* This must be the final entry */
+};
+
+/* TODO: support TRP metric field (requires > < comparison instead of wildcard match) */
+
+static struct tr_filter_field_entry *tr_filter_field_entry(TR_FILTER_TYPE filter_type, TR_NAME *field_name)
+{
+ unsigned int ii;
+
+ for (ii=0; tr_filter_field_table[ii].filter_type!=TR_FILTER_TYPE_UNKNOWN; ii++) {
+ if ((tr_filter_field_table[ii].filter_type==filter_type)
+ && (tr_name_cmp_str(field_name, tr_filter_field_table[ii].name)==0)) {
+ return tr_filter_field_table+ii;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Apply a filter to a target record or TID request.
+ *
+ * If one of the filter lines matches, out_action is set to the applicable action. If constraints
+ * is not NULL, the constraints from the matching filter line will be added to the constraint set
+ * *constraints, or to a new one if *constraints is NULL. In this case, TR_FILTER_MATCH will be
+ * returned.
+ *
+ * If there is no match, returns TR_FILTER_NO_MATCH, out_action is undefined, and constraints
+ * will not be changed.
+ *
+ * @param target Record or request to which the filter is applied
+ * @param filt Filter to apply
+ * @param constraints Pointer to existing set of constraints (NULL if not tracking constraints)
+ * @param out_action Action to be carried out (output)
+ * @return TR_FILTER_MATCH or TR_FILTER_NO_MATCH
+ */
+int tr_filter_apply(TR_FILTER_TARGET *target,
+ TR_FILTER *filt,
+ TR_CONSTRAINT_SET **constraints,
+ TR_FILTER_ACTION *out_action)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ TR_FILTER_ITER *filt_iter = tr_filter_iter_new(tmp_ctx);
+ TR_FLINE *this_fline = NULL;
+ TR_FLINE_ITER *fline_iter = tr_fline_iter_new(tmp_ctx);
+ TR_FSPEC *this_fspec = NULL;
+ int retval=TR_FILTER_NO_MATCH;
+
+ /* Default action is reject */
+ *out_action = TR_FILTER_ACTION_REJECT;
+
+ /* Validate filter */
+ if ((filt_iter == NULL) || (fline_iter == NULL) || (filt==NULL) || (filt->type==TR_FILTER_TYPE_UNKNOWN)) {
+ talloc_free(tmp_ctx);
+ return TR_FILTER_NO_MATCH;
+ }
+
+ /* Step through filter lines looking for a match. If a line matches, retval
+ * will be set to TR_FILTER_MATCH, so stop then. */
+ for (this_fline = tr_filter_iter_first(filt_iter, filt);
+ this_fline != NULL;
+ this_fline = tr_filter_iter_next(filt_iter)) {
+ /* Assume we are going to succeed. If any specs fail to match, we'll set
+ * this to TR_FILTER_NO_MATCH. */
+ retval=TR_FILTER_MATCH;
+ for (this_fspec = tr_fline_iter_first(fline_iter, this_fline);
+ this_fspec != NULL;
+ this_fspec = tr_fline_iter_next(fline_iter)) {
+ if (!tr_fspec_matches(this_fspec, filt->type, target)) {
+ retval=TR_FILTER_NO_MATCH; /* set this in case this is the last filter line */
+ break; /* give up on this filter line */
+ }
+ }
+
+ if (retval==TR_FILTER_MATCH)
+ break;
+
+ }
+
+ if (retval==TR_FILTER_MATCH) {
+ /* Matched line ii. Grab its action and constraints. */
+ *out_action = this_fline->action;
+ if (constraints!=NULL) {
+ /* if either constraint is missing, these are no-ops */
+ tr_constraint_add_to_set(constraints, this_fline->realm_cons);
+ tr_constraint_add_to_set(constraints, this_fline->domain_cons);
+ }
+ }
+
+ return retval;
+}
+
+void tr_fspec_free(TR_FSPEC *fspec)
+{
+ talloc_free(fspec);
+}
+
+/**
+ * Helper for tr_fspec_destructor - calls tr_free_name on its first argument
+ *
+ * @param item void pointer to a TR_NAME
+ * @param cookie ignored
+ */
+static void fspec_destruct_helper(void *item, void *cookie)
+{
+ TR_NAME *name = (TR_NAME *) item;
+ tr_free_name(name);
+}
+static int tr_fspec_destructor(void *obj)
+{
+ TR_FSPEC *fspec = talloc_get_type_abort(obj, TR_FSPEC);
+
+ if (fspec->field != NULL)
+ tr_free_name(fspec->field);
+
+ if (fspec->match)
+ tr_list_foreach(fspec->match, fspec_destruct_helper, NULL);
+
+ return 0;
+}
+
+TR_FSPEC *tr_fspec_new(TALLOC_CTX *mem_ctx)
+{
+ TR_FSPEC *fspec = talloc(mem_ctx, TR_FSPEC);
+
+ if (fspec != NULL) {
+ fspec->field = NULL;
+ fspec->match = tr_list_new(fspec);
+ if (fspec->match == NULL) {
+ talloc_free(fspec);
+ return NULL;
+ }
+ talloc_set_destructor((void *)fspec, tr_fspec_destructor);
+ }
+ return fspec;
+}
+
+/* Helper function and cookie structure for finding a match. The helper is called
+ * for every item in the match list, even after a match is found. If a match is found,
+ * match should be pointed to the matching item. If this is not NULL, do not change it
+ * because a match has already been found. */
+struct fspec_match_cookie { TR_NAME *name; TR_NAME *match;};
+static void fspec_match_helper(void *item, void *data)
+{
+ TR_NAME *this_name = (TR_NAME *) item;
+ struct fspec_match_cookie *cookie = (struct fspec_match_cookie *) data;
+ if (cookie->match == NULL) {
+ if (tr_name_prefix_wildcard_match(cookie->name, this_name))
+ cookie->match = this_name;
+ }
+}
+/* returns 1 if the spec matches */
+int tr_fspec_matches(TR_FSPEC *fspec, TR_FILTER_TYPE ftype, TR_FILTER_TARGET *target)
+{
+ struct tr_filter_field_entry *field=NULL;
+ struct fspec_match_cookie cookie = {0};
+
+ if (fspec==NULL)
+ return 0;
+
+ /* Look up how to handle the requested field */
+ field = tr_filter_field_entry(ftype, fspec->field);
+ if (field==NULL) {
+ tr_err("tr_fspec_matches: No entry to handle field %.*s for %*s filter.",
+ fspec->field->len, fspec->field->buf,
+ tr_filter_type_to_string(ftype));