+
+ return TRP_SUCCESS;
+}
+
+static int trps_name_in_provenance(TR_NAME *name, json_t *prov)
+{
+ size_t ii=0;
+ TR_NAME *this_name=NULL;
+ const char *s=NULL;
+
+ if (prov==NULL)
+ return 0; /* no provenance list, so it has no names in it */
+
+ /* now check to see if name is in the provenance */
+ for (ii=0; ii<json_array_size(prov); ii++) {
+ s=json_string_value(json_array_get(prov, ii));
+ if (s==NULL) {
+ tr_debug("trps_name_in_provenance: empty entry in provenance list.");
+ continue;
+ }
+
+ this_name=tr_new_name(s);
+ if (this_name==NULL) {
+ tr_debug("trps_name_in_provenance: unable to allocate name.");
+ return -1;
+ }
+ if (0==tr_name_cmp(name, this_name)) {
+ tr_free_name(this_name);
+ return 1;
+ }
+ tr_free_name(this_name);
+ }
+ return 0;
+}
+
+static TR_COMM *trps_create_new_comm(TALLOC_CTX *mem_ctx, TR_NAME *comm_id, TRP_INFOREC *rec)
+{
+ TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+ TR_COMM *comm=tr_comm_new(tmp_ctx);
+
+ if (comm==NULL) {
+ tr_debug("trps_create_new_comm: unable to allocate new community.");
+ goto cleanup;
+ }
+ /* fill in the community with info */
+ tr_comm_set_id(comm, tr_dup_name(comm_id));
+ if (tr_comm_get_id(comm)==NULL) {
+ tr_debug("trps_create_new_comm: unable to allocate community name.");
+ comm=NULL;
+ goto cleanup;
+ }
+ tr_comm_set_type(comm, trp_inforec_get_comm_type(rec));
+ if (trp_inforec_get_apcs(rec)!=NULL) {
+ tr_comm_set_apcs(comm, tr_apc_dup(tmp_ctx, trp_inforec_get_apcs(rec)));
+ if (tr_comm_get_apcs(comm)==NULL) {
+ tr_debug("trps_create_new_comm: unable to allocate APC list.");
+ comm=NULL;
+ goto cleanup;
+ }
+ }
+ if (trp_inforec_get_owner_realm(rec)!=NULL) {
+ tr_comm_set_owner_realm(comm, tr_dup_name(trp_inforec_get_owner_realm(rec)));
+ if (tr_comm_get_owner_realm(comm)==NULL) {
+ tr_debug("trps_create_new_comm: unable to allocate owner realm name.");
+ comm=NULL;
+ goto cleanup;
+ }
+ }
+ if (trp_inforec_get_owner_contact(rec)!=NULL) {
+ tr_comm_set_owner_contact(comm, tr_dup_name(trp_inforec_get_owner_contact(rec)));
+ if (tr_comm_get_owner_contact(comm)==NULL) {
+ tr_debug("trps_create_new_comm: unable to allocate owner contact.");
+ comm=NULL;
+ goto cleanup;
+ }
+ }
+ comm->expiration_interval=trp_inforec_get_exp_interval(rec);
+ talloc_steal(mem_ctx, comm);
+
+cleanup:
+ talloc_free(tmp_ctx);
+ return comm;
+}
+
+static TR_RP_REALM *trps_create_new_rp_realm(TALLOC_CTX *mem_ctx, TR_NAME *comm, TR_NAME *realm_id, TRP_INFOREC *rec)
+{
+ TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+ TR_RP_REALM *rp=tr_rp_realm_new(tmp_ctx);
+
+ if (rp==NULL) {
+ tr_debug("trps_create_new_rp_realm: unable to allocate new realm.");
+ goto cleanup;
+ }
+ /* fill in the realm */
+ tr_rp_realm_set_id(rp, tr_dup_name(realm_id));
+ if (tr_rp_realm_get_id(rp)==NULL) {
+ tr_debug("trps_create_new_rp_realm: unable to allocate realm name.");
+ rp=NULL;
+ goto cleanup;
+ }
+ talloc_steal(mem_ctx, rp);
+
+cleanup:
+ talloc_free(tmp_ctx);
+ return rp;
+}
+
+static TR_IDP_REALM *trps_create_new_idp_realm(TALLOC_CTX *mem_ctx,
+ TR_NAME *comm_id,
+ TR_NAME *realm_id,
+ TRP_INFOREC *rec)
+{
+ TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+ TR_IDP_REALM *idp=tr_idp_realm_new(tmp_ctx);
+ TR_APC *realm_apcs = NULL;
+
+ if (idp==NULL) {
+ tr_debug("trps_create_new_idp_realm: unable to allocate new realm.");
+ goto cleanup;
+ }
+ /* fill in the realm */
+ tr_idp_realm_set_id(idp, tr_dup_name(realm_id));
+ if (tr_idp_realm_get_id(idp)==NULL) {
+ tr_debug("trps_create_new_idp_realm: unable to allocate realm name.");
+ idp=NULL;
+ goto cleanup;
+ }
+
+ /* Set the APCs. If the community is a CoI, copy its APCs. If it is an APC, then
+ * that community itself is the APC for the realm. */
+ if (trp_inforec_get_comm_type(rec) == TR_COMM_APC) {
+ /* the community is an APC for this realm */
+ realm_apcs = tr_apc_new(tmp_ctx);
+ if (realm_apcs == NULL) {
+ tr_debug("trps_create_new_idp_realm: unable to allocate new APC list.");
+ idp = NULL;
+ goto cleanup;
+ }
+
+ tr_apc_set_id(realm_apcs, tr_dup_name(comm_id));
+ if (tr_apc_get_id(realm_apcs) == NULL) {
+ tr_debug("trps_create_new_idp_realm: unable to allocate new APC name.");
+ idp = NULL;
+ goto cleanup;
+ }
+ } else {
+ /* the community is not an APC for this realm */
+ realm_apcs = trp_inforec_get_apcs(rec);
+ if (realm_apcs == NULL) {
+ tr_debug("trps_create_new_idp_realm: no APCs for realm %.*s/%.*s, cannot add.",
+ realm_id->len, realm_id->buf,
+ comm_id->len, comm_id->buf);
+ idp = NULL;
+ goto cleanup;
+ }
+
+ /* we have APCs, make our own copy */
+ realm_apcs = tr_apc_dup(tmp_ctx, realm_apcs);
+ if (realm_apcs == NULL) {
+ tr_debug("trps_create_new_idp_realm: unable to duplicate APC list.");
+ idp = NULL;
+ goto cleanup;
+ }
+ }
+
+ /* Whether the community is an APC or CoI, the APCs for the realm are in realm_apcs */
+ tr_idp_realm_set_apcs(idp, realm_apcs); /* takes realm_apcs out of tmp_ctx on success */
+ if (tr_idp_realm_get_apcs(idp) == NULL) {
+ tr_debug("trps_create_new_idp_realm: unable to set APC list for new realm.");
+ idp=NULL;
+ goto cleanup;
+ }
+
+ idp->origin=TR_REALM_DISCOVERED;
+
+ talloc_steal(mem_ctx, idp);
+
+cleanup:
+ talloc_free(tmp_ctx);
+ return idp;
+}
+
+static TRP_RC trps_handle_inforec_comm(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
+{
+ TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+ TR_NAME *comm_id=trp_upd_get_comm(upd);
+ TR_NAME *realm_id=trp_upd_get_realm(upd);
+ TR_NAME *origin_id=NULL;
+ TR_NAME *our_peer_label=NULL;
+ TR_COMM *comm=NULL;
+ TR_RP_REALM *rp_realm=NULL;
+ TR_IDP_REALM *idp_realm=NULL;
+ struct timespec expiry={0,0};
+ TRP_RC rc=TRP_ERROR;
+
+ if ((comm_id==NULL) || (realm_id==NULL))
+ goto cleanup;
+
+ origin_id=trp_inforec_dup_origin(rec);
+ if (origin_id==NULL)
+ goto cleanup;
+
+ /* see whether we want to add this */
+ our_peer_label=trps_dup_label(trps);
+ if (our_peer_label==NULL) {
+ tr_debug("trps_handle_inforec_comm: unable to allocate peer label.");
+ goto cleanup;
+ }
+
+ if (trps_name_in_provenance(our_peer_label, trp_inforec_get_provenance(rec)))
+ tr_debug("trps_handle_inforec_comm: rejecting community inforec to avoid provenance loop.");
+ else {
+ /* no loop occurring, accept the update */
+ comm=tr_comm_table_find_comm(trps->ctable, comm_id);
+ if (comm==NULL) {
+ tr_debug("trps_handle_inforec_comm: unknown community %.*s in inforec, creating it.",
+ comm_id->len, comm_id->buf);
+ comm=trps_create_new_comm(tmp_ctx, comm_id, rec);
+ if (comm==NULL) {
+ tr_debug("trps_handle_inforec_comm: unable to create new community.");
+ goto cleanup;
+ }
+ if (tr_comm_table_add_comm(trps->ctable, comm) != 0)
+ {
+ tr_debug("trps_handle_inforec_comm: unable to add community to community table.");
+ goto cleanup;
+ }
+ }
+ /* TODO: see if other comm data match the new inforec and update or complain */
+
+ trps_compute_expiry(trps, trp_inforec_get_interval(rec), &expiry);
+ if ((expiry.tv_sec==0)&&(expiry.tv_nsec==0))
+ goto cleanup;
+
+ switch (trp_inforec_get_role(rec)) {
+ case TR_ROLE_RP:
+ rp_realm=tr_rp_realm_lookup(trps->ctable->rp_realms, realm_id);
+ if (rp_realm==NULL) {
+ tr_debug("trps_handle_inforec_comm: unknown RP realm %.*s in inforec, creating it.",
+ realm_id->len, realm_id->buf);
+ rp_realm= trps_create_new_rp_realm(tmp_ctx, tr_comm_get_id(comm), realm_id, rec);
+ if (rp_realm==NULL) {
+ tr_debug("trps_handle_inforec_comm: unable to create new RP realm.");
+ /* we may leave an unused community in the table, but it will only last until
+ * the next table sweep if it does not get any realms before that happens */
+ goto cleanup;
+ }
+ tr_comm_table_add_rp_realm(trps->ctable, rp_realm);
+ }
+ /* TODO: if realm existed, see if data match the new inforec and update or complain */
+ tr_comm_add_rp_realm(trps->ctable, comm, rp_realm, trp_inforec_get_interval(rec), trp_inforec_get_provenance(rec), &expiry);
+ tr_debug("trps_handle_inforec_comm: added RP realm %.*s to comm %.*s (origin %.*s).",
+ realm_id->len, realm_id->buf,
+ comm_id->len, comm_id->buf,
+ origin_id->len, origin_id->buf);
+ break;
+ case TR_ROLE_IDP:
+ idp_realm=tr_idp_realm_lookup(trps->ctable->idp_realms, realm_id);
+ if (idp_realm==NULL) {
+ tr_debug("trps_handle_inforec_comm: unknown IDP realm %.*s in inforec, creating it.",
+ realm_id->len, realm_id->buf);
+ idp_realm= trps_create_new_idp_realm(tmp_ctx, tr_comm_get_id(comm), realm_id, rec);
+ if (idp_realm==NULL) {
+ tr_debug("trps_handle_inforec_comm: unable to create new IDP realm.");
+ /* we may leave an unused community in the table, but it will only last until
+ * the next table sweep if it does not get any realms before that happens */
+ goto cleanup;
+ }
+ tr_comm_table_add_idp_realm(trps->ctable, idp_realm);
+ }
+ /* TODO: if realm existed, see if data match the new inforec and update or complain */
+ tr_comm_add_idp_realm(trps->ctable, comm, idp_realm, trp_inforec_get_interval(rec), trp_inforec_get_provenance(rec), &expiry);
+ tr_debug("trps_handle_inforec_comm: added IDP realm %.*s to comm %.*s (origin %.*s).",
+ realm_id->len, realm_id->buf,
+ comm_id->len, comm_id->buf,
+ origin_id->len, origin_id->buf);
+ break;
+ default:
+ tr_debug("trps_handle_inforec_comm: unable to add realm.");
+ goto cleanup;
+ }
+ }
+
+ rc=TRP_SUCCESS;
+
+cleanup:
+ if (our_peer_label!=NULL)
+ tr_free_name(our_peer_label);
+ if (origin_id!=NULL)
+ tr_free_name(origin_id);
+ talloc_free(tmp_ctx);
+ return rc;
+}
+
+/**
+ * Apply applicable TRP_INBOUND filters to an inforec. Rejects everything if peer has no filters.
+ *
+ * @param trps Active TRPS instance
+ * @param upd TRP_UPD that contains the inforec to filter
+ * @param rec Inforec to filter
+ * @return 1 if accepted by the filter, 0 otherwise
+ */
+static int trps_filter_inbound_inforec(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
+{
+ TRP_PEER *peer=NULL;
+ TR_NAME *peer_name=NULL;
+ TR_FILTER_ACTION action=TR_FILTER_ACTION_REJECT;
+ TR_FILTER_TARGET *target=NULL;
+ int retval=0;
+
+ /* Look up the peer. For inbound messages, the peer is identified by its GSS name */
+ peer_name=trp_upd_get_peer(upd);
+ peer=trps_get_peer_by_gssname(trps, peer_name);
+ if (peer==NULL) {
+ tr_err("trps_filter_inbound_inforec: received inforec from unknown peer (%.*s), rejecting.",
+ peer_name->len,
+ peer_name->buf);
+ return 0;
+ }
+
+ /* tr_filter_apply() and tr_filter_set_get() handle null filter sets/filters by rejecting */
+ target= tr_filter_target_trp_inforec(NULL, upd, rec);
+ if (target==NULL) {
+ /* TODO: signal that filtering failed. Until then, just filter everything and give an error message. */
+ tr_crit("trps_filter_inbound_inforec: Unable to allocate filter target, cannot apply filter!");
+ }
+ if ((target==NULL)
+ || (TR_FILTER_NO_MATCH==tr_filter_apply(target,
+ tr_filter_set_get(peer->filters, TR_FILTER_TYPE_TRP_INBOUND),
+ NULL,
+ &action))
+ || (action!=TR_FILTER_ACTION_ACCEPT)) {
+ /* either the filter did not match or it matched a reject rule or allocating the target failed */
+ retval=0;
+ } else
+ retval=1;
+ if (target!=NULL)
+ tr_filter_target_free(target);
+
+ /* filter matched an accept rule */
+ return retval;
+}
+
+
+static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
+{
+ TRP_INFOREC *rec=NULL;
+
+ if (trps_validate_update(trps, upd) != TRP_SUCCESS) {
+ tr_notice("trps_handle_update: received invalid TRP update.");
+ return TRP_ERROR;
+ }
+
+ for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
+ /* validate/sanity check the record update */
+ if (trps_validate_inforec(trps, rec) != TRP_SUCCESS) {
+ tr_notice("trps_handle_update: invalid inforec in TRP update, discarding entire update.");
+ return TRP_ERROR;
+ }
+ }
+
+ for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
+ if (!trps_filter_inbound_inforec(trps, upd, rec)) {
+ tr_debug("trps_handle_update: inforec rejected by filter.");
+ continue; /* just go on to the next record */
+ }
+
+ switch (trp_inforec_get_type(rec)) {
+ case TRP_INFOREC_TYPE_ROUTE:
+ tr_debug("trps_handle_update: handling route inforec.");
+ if (TRP_SUCCESS!=trps_handle_inforec_route(trps, upd, rec))
+ tr_notice("trps_handle_update: error handling route inforec.");
+ break;
+ case TRP_INFOREC_TYPE_COMMUNITY:
+ tr_debug("trps_handle_update: handling community inforec.");
+ if (TRP_SUCCESS!=trps_handle_inforec_comm(trps, upd, rec))
+ tr_notice("trps_handle_update: error handling community inforec.");
+
+ break;
+ default:
+ tr_notice("trps_handle_update: unsupported inforec in TRP update.");
+ break;
+ }
+ }