/* * * (c) Copyright 1989 OPEN SOFTWARE FOUNDATION, INC. * (c) Copyright 1989 HEWLETT-PACKARD COMPANY * (c) Copyright 1989 DIGITAL EQUIPMENT CORPORATION * To anyone who acknowledges that this file is provided "AS IS" * without any express or implied warranty: * permission to use, copy, modify, and distribute this * file for any purpose is hereby granted without fee, provided that * the above copyright notices and this notice appears in all source * code copies, and that none of the names of Open Software * Foundation, Inc., Hewlett-Packard Company, or Digital Equipment * Corporation be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Neither Open Software Foundation, Inc., Hewlett- * Packard Company, nor Digital Equipment Corporation makes any * representations about the suitability of this software for any * purpose. * */ /* */ /* ** ** NAME: ** ** rpcdepdb.c ** ** FACILITY: ** ** RPC Daemon ** ** ABSTRACT: ** ** Generic Endpoint Database Management, Forwarding, ** and Lookup Routines ** ** */ #include #include #include #include /* derived from ep.idl */ #include /* derived from mgmt.idl */ #include /* derived from dsm.idl */ #include #include #include #include #include #include /* * The server supports a single endpoint location database. */ INTERNAL epdb_handle_t epdb_handle = NULL; /* * The current version of the persistent database file (the value in a file_hdr). */ #define epdb_c_file_version 8 INTERNAL void epdb_recreate_lists _DCE_PROTOTYPE_(( struct db *h, error_status_t *status )); INTERNAL void epdb_chk_entry _DCE_PROTOTYPE_(( ept_entry_p_t xentry, twr_fields_p_t tfp, rpc_addr_p_t addr, error_status_t *status )); INTERNAL void epdb_chk_map_entry _DCE_PROTOTYPE_(( twr_fields_p_t tfp, error_status_t *status )); INTERNAL void epdb_to_ept _DCE_PROTOTYPE_(( db_entry_p_t entp, ept_entry_t *xentry, error_status_t *status )); INTERNAL void epdb_insert_entry _DCE_PROTOTYPE_(( struct db *h, ept_entry_p_t xentry, twr_fields_p_t tfp, rpc_addr_p_t addr, db_entry_p_t *entp, error_status_t *status )); INTERNAL void epdb_replace_entry _DCE_PROTOTYPE_(( struct db *h, ept_entry_p_t xentry, db_entry_p_t entp, error_status_t *status )); INTERNAL boolean32 epdb_is_replace_candidate _DCE_PROTOTYPE_(( db_entry_t *entp, uuid_p_t object, twr_fields_p_t tfp, rpc_addr_p_t addr )); INTERNAL void epdb_delete_replaceable_entries _DCE_PROTOTYPE_(( struct db *h, uuid_p_t object, twr_fields_p_t tfp, rpc_addr_p_t addr, error_status_t *status )); INTERNAL void epdb_delete_entries_by_obj_if_addr _DCE_PROTOTYPE_(( struct db *h, boolean32 object_speced, uuid_p_t object, rpc_if_id_p_t interface, rpc_addr_p_t addr, error_status_t *status )); INTERNAL db_entry_t *epdb_lookup_entry _DCE_PROTOTYPE_(( struct db *h, ept_entry_p_t xentry )); INTERNAL void lookup _DCE_PROTOTYPE_(( struct db *h, unsigned32 inquiry_type, uuid_p_t object, rpc_if_id_p_t interface, unsigned32 vers_option, ept_lookup_handle_t *entry_handle, unsigned32 max_ents, unsigned32 *num_ents, ept_entry_t entries[], error_status_t *status )); INTERNAL void lookup_match _DCE_PROTOTYPE_(( unsigned32 inquiry_type, uuid_p_t object, rpc_if_id_p_t interface, unsigned32 vers_option, unsigned32 max_ents, unsigned32 *num_ents, ept_entry_t entries[], unsigned32 list_type, db_lists_t **lpp, error_status_t *status )); INTERNAL void map _DCE_PROTOTYPE_(( struct db *h, uuid_p_t object, rpc_if_id_p_t interface, rpc_syntax_id_p_t data_rep, rpc_protocol_id_t rpc_protocol, unsigned32 rpc_protocol_vers_major, unsigned32 rpc_protocol_vers_minor, rpc_protseq_id_t protseq, ept_lookup_handle_t *map_handle, unsigned32 max_ents, unsigned32 *n_ents, db_entry_t *db_entries[], unsigned32 *status )); INTERNAL void map_match _DCE_PROTOTYPE_(( uuid_p_t object, rpc_if_id_p_t interface, rpc_syntax_id_p_t data_rep, rpc_protocol_id_t rpc_protocol, unsigned32 rpc_protocol_vers_major, unsigned32 rpc_protocol_vers_minor, rpc_protseq_id_t protseq, unsigned32 max_ents, unsigned32 *n_ents, db_entry_t *entries[], ept_lookup_handle_t *map_handle, unsigned32 pass, db_list_type_t list, db_lists_t **lpp, unsigned32 *status )); INTERNAL void map_mgmt _DCE_PROTOTYPE_(( struct db *h, uuid_p_t object, rpc_syntax_id_p_t data_rep, rpc_protocol_id_t rpc_protocol, unsigned32 rpc_protocol_vers_major, unsigned32 rpc_protocol_vers_minor, rpc_protseq_id_t protseq, ept_lookup_handle_t *map_handle, unsigned32 max_ents, unsigned32 *n_ents, db_entry_t *db_entries[], unsigned32 *status )); INTERNAL void map_mgmt_match _DCE_PROTOTYPE_(( uuid_p_t object, rpc_syntax_id_p_t data_rep, rpc_protocol_id_t rpc_protocol, unsigned32 rpc_protocol_vers_major, unsigned32 rpc_protocol_vers_minor, rpc_protseq_id_t protseq, unsigned32 max_ents, unsigned32 *n_ents, db_entry_t *entries[], ept_lookup_handle_t *map_handle, unsigned32 pass, db_list_type_t list, db_lists_t **lpp, unsigned32 *status )); INTERNAL boolean32 map_mgmt_endpt_unique _DCE_PROTOTYPE_(( rpc_addr_p_t addr, unsigned32 n_ents, db_entry_t *entries[] )); /* * Get an endpoint database handle from a handle_t. * * If the handle is not bound to an object (i.e. the object * is nil) treat this as a request to just use "the" endpoint * database. If a specific object is specified, make sure that * the object is the same as the endpoint database object. * (accept a null handle to allow for "local" callers). */ PRIVATE void epdb_handle_from_ohandle(h, epdb_h, status) handle_t h; epdb_handle_t *epdb_h; error_status_t *status; { uuid_t obj; uuid_t epdb_obj; SET_STATUS(status, rpc_s_ok); if (h == NULL) { *epdb_h = epdb_handle; return; } rpc_binding_inq_object(h, &obj, status); if (uuid_is_nil(&obj, status)) *epdb_h = epdb_handle; else { epdb_inq_object(epdb_handle, &epdb_obj, status); if (uuid_equal(&obj, &epdb_obj, status)) *epdb_h = epdb_handle; else SET_STATUS(status, ept_s_cant_perform_op); /* !!! ep_wrong_object? */ } return; } /* Return the handle to the ep database * Since there's only one database just return what's in * epdb_handle */ PRIVATE epdb_handle_t epdb_inq_handle() { return(epdb_handle); } /* * Open/create a database and return a handle to it. * Init the database lists and lock. Start the task * to check server liveness and to purge entries that * are marked as deleted. */ PRIVATE epdb_handle_t epdb_init(pathname, status) unsigned char *pathname; error_status_t *status; { struct db *h; SET_STATUS_OK(status); h = (struct db *) malloc(sizeof(struct db)); if (h == NULL) { SET_STATUS(status, ept_s_cant_perform_op); return(NULL); } h->dsh = NULL; db_init_lists(h); db_open(h, pathname, epdb_c_file_version, status); if (! STATUS_OK(status)) { /* Try deleting and recreating the database */ unlink(pathname); db_open(h, pathname, epdb_c_file_version, status); if (! STATUS_OK(status)) { return NULL; } } /* Database now exists, recreate its lists */ epdb_recreate_lists(h, status); if (! STATUS_OK(status)) return(NULL); db_init_lock(h); sliv_init(h, status); if (! STATUS_OK(status)) return(NULL); epdb_handle = (epdb_handle_t) h; return((epdb_handle_t) h); } INTERNAL void epdb_recreate_lists(h, status) struct db *h; error_status_t *status; { db_entry_t *entp = NULL; dsm_marker_t marker; /* DSM datastore traversal marker */ dsm_marker_reset(&marker); while(true) { void* _entp = NULL; dsm_read(h->dsh,&marker, &_entp,status); /* get next record */ entp = (db_entry_t*) _entp; if (! STATUS_OK(status)) { if (*status == dsm_err_no_more_entries) { SET_STATUS(status, error_status_ok); } else { if (dflag) show_st("Error reading ept database", status); SET_STATUS(status, ept_s_cant_access); } return; } tower_to_addr(&entp->tower, &entp->addr, status); if (! STATUS_OK(status)) { if (dflag) show_st("tower_to_addr error for ept entry", status); SET_STATUS(status, ept_s_invalid_entry); return; } entp->read_nrefs = 0; entp->ncomm_fails = 0; db_lists_add(h, entp); } } /* * epdb_chk_entry - check an entry that is about to be inserted * into the ep database * All tower floors in an entry must be valid and non-nil * (eg. nil interface uuid is prohibited). Also, check tower * via rpc_tower_to_binding - sliv tasks will need to get binding * handle */ INTERNAL void epdb_chk_entry(xentry, tfp, addr, status) ept_entry_p_t xentry; twr_fields_p_t tfp; rpc_addr_p_t addr; error_status_t *status; { rpc_binding_handle_t binding_h; error_status_t tmp_st; if (uuid_is_nil(&tfp->interface.uuid, &tmp_st) || uuid_is_nil(&tfp->data_rep.id, &tmp_st) || (addr == NULL) || (addr->len == 0)) { SET_STATUS(status, ept_s_invalid_entry); return; } rpc_tower_to_binding(xentry->tower->tower_octet_string, &binding_h, status); if (! STATUS_OK(status)) return; rpc_binding_free(&binding_h, &tmp_st); } INTERNAL void epdb_chk_map_entry(tfp, status) twr_fields_p_t tfp; error_status_t *status; { error_status_t tmp_st; if (uuid_is_nil(&tfp->interface.uuid, &tmp_st) || uuid_is_nil(&tfp->data_rep.id, &tmp_st) || (! RPC_PROTSEQ_INQ_SUPPORTED(tfp->protseq)) ) SET_STATUS(status, ept_s_invalid_entry); else SET_STATUS_OK(status); } INTERNAL void epdb_to_ept(entp, xentry, status) db_entry_p_t entp; ept_entry_t *xentry; error_status_t *status; { xentry->object = entp->object; memcpy((char *)xentry->annotation, (char *)entp->annotation, sizeof(xentry->annotation)); tower_ss_copy(&entp->tower, &xentry->tower, status); } /* epdb_insert_entry * Allocate dsm storage for an entry in the endpoint database * Fill the entry from xentry, tfp, and addr * Write it to disk * Add entry to various lists * Return db pointer to entry in entp */ INTERNAL void epdb_insert_entry(h, xentry, tfp, addr, entp, status) struct db *h; ept_entry_p_t xentry; twr_fields_p_t tfp; rpc_addr_p_t addr; db_entry_p_t *entp; error_status_t *status; { db_entry_t *db_entp; error_status_t tmp_st; dsm_allocate(h->dsh, sizeof(db_entry_t) + xentry->tower->tower_length, (void **) entp, status); if (! STATUS_OK(status)) return; db_entp = *entp; db_entp->read_nrefs = 0; db_entp->ncomm_fails = 0; db_entp->delete_flag = false; db_entp->object = xentry->object; db_entp->interface = tfp->interface; db_entp->object_nil = uuid_is_nil(&db_entp->object, &tmp_st); db_entp->if_nil = uuid_is_nil(&db_entp->interface.uuid, &tmp_st); db_entp->data_rep_id = tfp->data_rep.id; db_entp->data_rep_vers_major = RPC_IF_VERS_MAJOR(tfp->data_rep.version); db_entp->data_rep_vers_minor = RPC_IF_VERS_MINOR(tfp->data_rep.version); db_entp->rpc_protocol = tfp->rpc_protocol; db_entp->rpc_protocol_vers_major = tfp->rpc_protocol_vers_major; db_entp->rpc_protocol_vers_minor = tfp->rpc_protocol_vers_minor; db_entp->addr = addr; memcpy((char *)db_entp->annotation, (char *)xentry->annotation, sizeof(db_entp->annotation)); db_entp->tower.tower_length = xentry->tower->tower_length; memcpy((char *) db_entp->tower.tower_octet_string, (char *) xentry->tower->tower_octet_string, xentry->tower->tower_length); dsm_write(h->dsh, (void *) db_entp, status); if (! STATUS_OK(status)) return; db_lists_add(h, db_entp); } /* epdb_replace_entry * Replace an existing entry - just change the annotation * because all other fields are the same * Also do some mgmt stuff - clear delete flag and ncomm_fails field */ INTERNAL void epdb_replace_entry(h, xentry, entp, status) struct db *h; ept_entry_p_t xentry; db_entry_p_t entp; error_status_t *status; { entp->ncomm_fails = 0; entp->delete_flag = false; memcpy((char *)entp->annotation, (char *)xentry->annotation, sizeof(entp->annotation)); db_update_entry(h, entp, status); } /* epdb_delete_entry * Delete an entry from the database. * * If any readers have unlocked the db but * are pointing to this entry, mark the entry * as deleted and write it to disk. * Otherwise, remove the entry from all * lists and delete it from the database */ PRIVATE void epdb_delete_entry(h, entp, status) struct db *h; db_entry_p_t entp; error_status_t *status; { if (entp->read_nrefs == 0) { db_lists_remove(h, entp); rpc__naf_addr_free(&entp->addr, status); dsm_free(h->dsh, (void *) entp, status); } else { entp->delete_flag = true; db_update_entry(h, entp, status); } } /* * epdb_is_replace_candidate * * Return true iff the specified entry looks like a candidate for * replacement. Requirements for candidacy are: matching object, matching * i/f UUID and major version, matching RPC protseq, matching data rep, * matching RPC protocol ID and RPC protocol major and minor version. * Candidacy is not sufficient to determine actual replaceability; i/f * minor version and network address must be considered as well. See * below. */ INTERNAL boolean32 epdb_is_replace_candidate(entp, object, tfp, addr) twr_fields_p_t tfp; uuid_p_t object; db_entry_t *entp; rpc_addr_p_t addr; { error_status_t tmp_st; unsigned32 data_rep_vers_major, data_rep_vers_minor; data_rep_vers_major = RPC_IF_VERS_MAJOR(tfp->data_rep.version); data_rep_vers_minor = RPC_IF_VERS_MINOR(tfp->data_rep.version); return (uuid_equal(object, &entp->object, &tmp_st) && uuid_equal(&tfp->interface.uuid, &entp->interface.uuid, &tmp_st) && (tfp->interface.vers_major == entp->interface.vers_major) && (addr->rpc_protseq_id == entp->addr->rpc_protseq_id) && uuid_equal(&tfp->data_rep.id, &entp->data_rep_id, &tmp_st) && (data_rep_vers_major == entp->data_rep_vers_major) && (data_rep_vers_minor == entp->data_rep_vers_minor) && (tfp->rpc_protocol == entp->rpc_protocol) && (tfp->rpc_protocol_vers_major == entp->rpc_protocol_vers_major) && (tfp->rpc_protocol_vers_minor == entp->rpc_protocol_vers_minor)); } /* * epdb_delete_replaceable_entries * * Delete entries which are "replaceable" by the new entry implied by * the object, tfp, and addr parameters. */ INTERNAL void epdb_delete_replaceable_entries(h, object, tfp, addr, status) struct db *h; uuid_p_t object; twr_fields_p_t tfp; rpc_addr_p_t addr; error_status_t *status; { unsigned_char_p_t netaddr, netaddr2; db_lists_t *lp, *lp_first, *lp_next; db_list_type_t list_type; error_status_t tmp_st; rpc__naf_addr_inq_netaddr(addr, &netaddr, status); if (! STATUS_OK(status)) { SET_STATUS(status, ept_s_invalid_entry); return; } if (! uuid_is_nil(object, &tmp_st)) { list_type = db_c_object_list; lp_first = db_list_first(&h->lists_mgmt, db_c_object_list, object); } else { list_type = db_c_interface_list; lp_first = db_list_first(&h->lists_mgmt, db_c_interface_list, &tfp->interface.uuid); } /* * Scan the list and see if there are any replace candidates with an * i/f minor version that's greater than the proposed new entry. If * there are, the replace must fail. */ for (lp = lp_first; lp != NULL; lp = db_list_next(list_type, lp)) { db_entry_t *entp = (db_entry_t *) lp; if (entp->delete_flag) continue; if (epdb_is_replace_candidate(entp, object, tfp, addr) && entp->interface.vers_minor > tfp->interface.vers_minor) { SET_STATUS(status, ept_s_invalid_entry); goto DONE; } } /* * Scan the list again and delete all replace candidates that have * network address that matches the network entry of the new entry. */ for (lp = lp_first; lp != NULL; lp = lp_next) { db_entry_t *entp = (db_entry_t *) lp; /* * Point to next entry - may remove this one from the list * and delete it */ lp_next = db_list_next(list_type, lp); if (entp->delete_flag) continue; if (epdb_is_replace_candidate(entp, object, tfp, addr)) { rpc__naf_addr_inq_netaddr(entp->addr, &netaddr2, &tmp_st); if (! STATUS_OK(&tmp_st)) continue; if (strcmp((char *) netaddr, (char *) netaddr2) == 0) { /* Entry matches target in all fields except * endpoint so delete it */ epdb_delete_entry(h, entp, &tmp_st); } rpc_string_free(&netaddr2, &tmp_st); } } SET_STATUS_OK(status); DONE: rpc_string_free(&netaddr, &tmp_st); } /* * epdb_delete_entries_by_obj_if_addr * * Delete entries which match target object (if speced), interface, and addr */ INTERNAL void epdb_delete_entries_by_obj_if_addr(h, object_speced, object, interface, addr, status) struct db *h; boolean32 object_speced; uuid_p_t object; rpc_if_id_p_t interface; rpc_addr_p_t addr; error_status_t *status; { unsigned32 ndelete; db_lists_t *lp, *lp_next; db_entry_t *entp; error_status_t tmp_st; ndelete = 0; for (lp = db_list_first(&h->lists_mgmt, db_c_interface_list, &interface->uuid); lp != NULL; lp = lp_next) { /* * Point to next entry - may remove this one from the list * and delete it */ lp_next = db_list_next(db_c_interface_list, lp); entp = (db_entry_t *) lp; if (entp->delete_flag) continue; if (((! object_speced) || uuid_equal(object, &entp->object, &tmp_st)) && (uuid_equal(&interface->uuid, &entp->interface.uuid, &tmp_st)) && (interface->vers_major == entp->interface.vers_major) && (interface->vers_minor == entp->interface.vers_minor) && (rpc__naf_addr_compare(addr, entp->addr, &tmp_st))) { /* Entry matches target object (if speced), * interface, and addr so delete it */ epdb_delete_entry(h, entp, status); if (! STATUS_OK(status)) return; ndelete++; } } if (ndelete > 0) SET_STATUS_OK(status); else SET_STATUS(status, ept_s_not_registered); } /* epdb_lookup_entry * Return pointer to entry which exactly matches * xentry * Return all entries, even ones marked as deleted */ INTERNAL db_entry_t *epdb_lookup_entry(h, xentry) struct db *h; ept_entry_p_t xentry; { db_entry_t *entp; db_lists_t *lp; db_list_type_t list_type; rpc_if_id_t interface; error_status_t tmp_st; if (! uuid_is_nil(&xentry->object, &tmp_st)) { list_type = db_c_object_list; lp = db_list_first(&h->lists_mgmt, db_c_object_list, &xentry->object); } else { tower_to_if_id(xentry->tower, &interface, &tmp_st); list_type = db_c_interface_list; lp = db_list_first(&h->lists_mgmt, db_c_interface_list, &interface.uuid); } for ( ; lp != NULL; lp = db_list_next(list_type, lp)) { entp = (db_entry_t *) lp; if (uuid_equal(&xentry->object, &entp->object, &tmp_st) && (xentry->tower->tower_length == entp->tower.tower_length) && (memcmp((char *) xentry->tower->tower_octet_string, (char *) entp->tower.tower_octet_string, xentry->tower->tower_length) == 0)) { return(entp); } } return(NULL); } /* * Add a new entry to the database. If the entry already exists simply * modify its annotation field (it's the only field that might be * different), clear some of its mgmt fields and update the entry * on disk. * * Note: All tower floors in an entry must be valid and non-nil * (eg. nil interface uuid is prohibited). */ PRIVATE void epdb_insert(h_, xentry, replace, status) epdb_handle_t h_; ept_entry_p_t xentry; boolean32 replace; error_status_t *status; { struct db *h = (struct db *) h_; db_entry_t *entp; twr_fields_t twr_fields; rpc_addr_p_t addr; error_status_t tmp_st; addr = NULL; /* Parse the new entry's tower and check * whether the new entry is ok. * Prefill status to bad entry for quick return. */ SET_STATUS(status, ept_s_invalid_entry); /* * Parse xentry's tower into twr_fields */ tower_to_fields(xentry->tower, &twr_fields, &tmp_st); if (!STATUS_OK(&tmp_st)) return; tower_to_addr(xentry->tower, &addr, &tmp_st); if (!STATUS_OK(&tmp_st)) return; epdb_chk_entry(xentry, &twr_fields, addr, &tmp_st); if (!STATUS_OK(&tmp_st)) { if (addr != NULL) rpc__naf_addr_free(&addr, &tmp_st); return; } db_lock(h); if (replace) { /* Delete entries that match * in all fields except endpoint */ epdb_delete_replaceable_entries(h, &xentry->object, &twr_fields, addr, status); if (! STATUS_OK(status)) { rpc__naf_addr_free(&addr, &tmp_st); db_unlock(h); return; } } entp = epdb_lookup_entry(h, xentry); if (entp == NULL) { /* New entry * insert it in dbase and add it to lists */ epdb_insert_entry(h, xentry, &twr_fields, addr, &entp, status); } else { /* Existing entry - just replace annotation and clear some mgmt fields * Free unused addr (already have it for this entry) */ rpc__naf_addr_free(&addr, &tmp_st); epdb_replace_entry(h, xentry, entp, status); } db_unlock(h); if (! STATUS_OK(status)) { if (addr != NULL) rpc__naf_addr_free(&addr, &tmp_st); db_to_ept_ecode(status); } } /* * Remove an entry from the database. * Only a database entry which exactly matches * xentry is deleted. */ PRIVATE void epdb_delete(h_, xentry, status) epdb_handle_t h_; ept_entry_p_t xentry; error_status_t *status; { struct db *h = (struct db *) h_; db_entry_t *entp; SET_STATUS_OK(status); db_lock(h); entp = epdb_lookup_entry(h, xentry); if (entp != NULL) { if (entp->delete_flag) SET_STATUS(status, ept_s_not_registered); else { /* * Matching entry found so delete it */ epdb_delete_entry(h, entp, status); if (! STATUS_OK(status)) db_to_ept_ecode(status); } } else SET_STATUS(status, ept_s_not_registered); db_unlock(h); } PRIVATE void epdb_mgmt_delete(h_, object_speced, object, tower, status) epdb_handle_t h_; boolean32 object_speced; uuid_p_t object; twr_p_t tower; error_status_t *status; { struct db *h = (struct db *) h_; rpc_if_id_t interface; rpc_addr_p_t addr; error_status_t tmp_st; SET_STATUS(status, ept_s_invalid_entry); tower_to_if_id(tower, &interface, status); if (! STATUS_OK(status)) return; tower_to_addr(tower, &addr, status); if (! STATUS_OK(status)) return; db_lock(h); epdb_delete_entries_by_obj_if_addr(h, object_speced, object, &interface, addr, status); if (! STATUS_OK(status)) db_to_ept_ecode(status); db_unlock(h); rpc__naf_addr_free(&addr, &tmp_st); } /* * epdb_lookup * Return entries that match filter speced by inquiry_type, vers_option, * object, and interface. * */ PRIVATE void epdb_lookup(h_, inquiry_type, object, interface, vers_option, entry_handle, max_ents, num_ents, entries, status) epdb_handle_t h_; unsigned32 inquiry_type; uuid_p_t object; rpc_if_id_p_t interface; unsigned32 vers_option; ept_lookup_handle_t *entry_handle; unsigned32 max_ents; unsigned32 *num_ents; ept_entry_t entries[]; error_status_t *status; { struct db *h = (struct db *) h_; *num_ents = 0; /* lock database before delete_context or lookup */ db_lock(h); if (entries == NULL) { db_delete_context(h, entry_handle); SET_STATUS(status, ept_s_invalid_entry); db_unlock(h); return; } lookup(h, inquiry_type, object, interface, vers_option, entry_handle, max_ents, num_ents, entries, status); db_unlock(h); } INTERNAL void lookup(h, inquiry_type, object, interface, vers_option, entry_handle, max_ents, num_ents, entries, status) struct db *h; unsigned32 inquiry_type; uuid_p_t object; rpc_if_id_p_t interface; unsigned32 vers_option; ept_lookup_handle_t *entry_handle; unsigned32 max_ents; unsigned32 *num_ents; ept_entry_t entries[]; error_status_t *status; { unsigned32 pass; db_list_type_t list_type; db_lists_t *lp; unsigned32 i; SET_STATUS_OK(status); if ((entry_handle == NULL) || (*entry_handle == NULL)) { /* no context saved so init context */ switch ((int)inquiry_type) { case rpc_c_ep_all_elts: pass = 1; list_type = db_c_entry_list; lp = db_list_first(&h->lists_mgmt, list_type, NULL); break; case rpc_c_ep_match_by_if: case rpc_c_ep_match_by_both: pass = 1; list_type = db_c_interface_list; lp = db_list_first(&h->lists_mgmt, list_type, &interface->uuid); break; case rpc_c_ep_match_by_obj: pass = 1; list_type = db_c_object_list; lp = db_list_first(&h->lists_mgmt, list_type, object); break; default: *status = rpc_s_invalid_inquiry_type; return; } } else { /* context has been saved. * restore context */ db_get_context(h, entry_handle, &list_type, &lp, &pass, status); if (! STATUS_OK(status)) { *entry_handle = NULL; return; } /* do some sanity checking on inquiry_type/context info */ switch ((int)inquiry_type) { case rpc_c_ep_all_elts: if (list_type != db_c_entry_list) *status = ept_s_invalid_context; break; case rpc_c_ep_match_by_if: case rpc_c_ep_match_by_both: if (list_type != db_c_interface_list) *status = ept_s_invalid_context; break; case rpc_c_ep_match_by_obj: if (list_type != db_c_object_list) *status = ept_s_invalid_context; break; default: *status = rpc_s_invalid_inquiry_type; break; } if (! STATUS_OK(status)) { db_delete_context(h, entry_handle); return; } } lookup_match(inquiry_type, object, interface, vers_option, max_ents, num_ents, entries, list_type, &lp, status); if (! STATUS_OK(status)) { /* Lookup failed so delete * the context handle and free * the towers that have been allocated */ db_delete_context(h, entry_handle); for (i = 0; i < *num_ents; i++) rpc_ss_free(entries[i].tower); *num_ents = 0; return; } db_save_context(h, entry_handle, list_type, lp, pass); if (*num_ents == 0) { SET_STATUS(status, ept_s_not_registered); } } INTERNAL void lookup_match(inquiry_type, object, interface, vers_option, max_ents, num_ents, entries, list_type, lpp, status) unsigned32 inquiry_type; uuid_p_t object; rpc_if_id_p_t interface; unsigned32 vers_option; unsigned32 max_ents; unsigned32 *num_ents; ept_entry_t entries[]; unsigned32 list_type; db_lists_t **lpp; error_status_t *status; { boolean32 match; db_lists_t *lp; db_entry_t *entp; error_status_t tmp_st; for (lp = *lpp; lp != NULL; lp = db_list_next(list_type, lp)) { entp = (db_entry_t *) lp; if (entp->delete_flag) continue; match = false; switch ((int)inquiry_type) { case rpc_c_ep_all_elts: match = true; break; case rpc_c_ep_match_by_if: if (uuid_equal(&interface->uuid, &entp->interface.uuid, &tmp_st)) match = true; break; case rpc_c_ep_match_by_obj: if (uuid_equal(object, &entp->object, &tmp_st)) match = true; break; case rpc_c_ep_match_by_both: if (uuid_equal(&interface->uuid, &entp->interface.uuid, &tmp_st) && uuid_equal(object, &entp->object, &tmp_st)) match = true; break; default: *status = rpc_s_invalid_inquiry_type; return; } if (match) { if ((inquiry_type == rpc_c_ep_match_by_if) || (inquiry_type == rpc_c_ep_match_by_both)) { /* check interface version */ match = false; switch ((int)vers_option) { case rpc_c_vers_all: match = true; break; case rpc_c_vers_compatible: if ((interface->vers_major == entp->interface.vers_major) && (interface->vers_minor <= entp->interface.vers_minor)) match = true; break; case rpc_c_vers_exact: if ((interface->vers_major == entp->interface.vers_major) && (interface->vers_minor == entp->interface.vers_minor)) match = true; break; case rpc_c_vers_major_only: if (interface->vers_major == entp->interface.vers_major) match = true; break; case rpc_c_vers_upto: if (interface->vers_major > entp->interface.vers_major) match = true; else if ((interface->vers_major == entp->interface.vers_major) && (interface->vers_minor >= entp->interface.vers_minor)) match = true; break; default: *status = rpc_s_invalid_vers_option; return; } } } if (match) { if (*num_ents >= max_ents) { break; } epdb_to_ept(entp, &entries[*num_ents], status); if (! STATUS_OK(status)) return; (*num_ents)++; } } *lpp = lp; SET_STATUS_OK(status); } /* * epdb_fwd * * Invoke map or map_mgmt to do a sequence of searches through the * endpoint map for appropriate entries to forward to and return pointers * to these entries. Copy the entries' addr info into fwd_addrs. * * map_mgmt is used for the search if the interface is the mgmt interface, * otherwise map is used. * * If bad status is returned delete the search context, otherwise map * will save it. * * NB: caller should set *num_ents to 0 or appropriate offset into fwd_addrs. * * Note: we do not forward to an entry with a nil interface id * AND a nil object id. If this ever becomes desireable, it will * likely require visiting (fixing) the protocol service implementations * that use DG style forwarding. The implementations may try to forward * first through the map and only if the forward map fails, then attempt * to perform normal pkt handling. The problem is that allowing a tuple * with a nil interface AND object id to be registered will result in * an inexact lookup match (due to the nil interface id) for operations * that are really for the server implementing the endpoint map operations. * * Also note that rpcd.c/fwd_map invokes epdb_fwd with max_ents = 1 and * map_handle = NULL. We could remove the context handle mgmt code from * epdb_fwd and gain some in performance - but we'd still need to leave * the context handle code in map and map_match for epdb_map. */ PRIVATE void epdb_fwd(h_, object, interface, data_rep, rpc_protocol, rpc_protocol_vers_major, rpc_protocol_vers_minor, addr, map_handle, max_ents, num_ents, fwd_addrs, status) epdb_handle_t h_; uuid_p_t object; rpc_if_id_p_t interface; rpc_syntax_id_p_t data_rep; rpc_protocol_id_t rpc_protocol; unsigned32 rpc_protocol_vers_major; unsigned32 rpc_protocol_vers_minor; rpc_addr_p_t addr; ept_lookup_handle_t *map_handle; unsigned32 max_ents; unsigned32 *num_ents; rpc_addr_p_t fwd_addrs[]; unsigned32 *status; { struct db *h = (struct db *) h_; rpc_if_rep_p_t mgmt_if_rep; db_entry_t **db_entries; unsigned32 start_ent; unsigned32 i; error_status_t tmp_st; mgmt_if_rep = (rpc_if_rep_p_t) mgmt_v1_0_s_ifspec; if (db_different_context(h, map_handle, status)) return; db_entries = (db_entry_t **) malloc(max_ents * sizeof(db_entry_p_t)); /* lock database before delete_context or lookup */ db_lock(h); if ((fwd_addrs == NULL) || (db_entries == NULL) || (max_ents == 0) || (*num_ents > max_ents)) { if (db_entries != NULL) free(db_entries); db_delete_context(h, map_handle); SET_STATUS(status, ept_s_cant_perform_op); db_unlock(h); return; } start_ent = *num_ents; if (uuid_equal(&interface->uuid, &mgmt_if_rep->id, &tmp_st)) map_mgmt(h, object, data_rep, rpc_protocol, rpc_protocol_vers_major, rpc_protocol_vers_minor, addr->rpc_protseq_id, map_handle, max_ents, num_ents, db_entries, status); else map(h, object, interface, data_rep, rpc_protocol, rpc_protocol_vers_major, rpc_protocol_vers_minor, addr->rpc_protseq_id, map_handle, max_ents, num_ents, db_entries, status); if (! STATUS_OK(status)) { free(db_entries); db_unlock(h); return; } for (i = start_ent; i < *num_ents; i++) { rpc__naf_addr_copy(db_entries[i]->addr, &fwd_addrs[i], status); if (! STATUS_OK(status)) { /* don't remember context but return * addrs that already copied to oput array. */ db_delete_context(h, map_handle); *num_ents = i; if (*num_ents > 0) { SET_STATUS_OK(status); } else { SET_STATUS(status, ept_s_cant_perform_op); } break; } } free(db_entries); db_unlock(h); } /* epdb_map * Invoke map or map_mgmt to do a sequence of searches through the * endpoint map for appropriate entries to forward to and return pointers * to these entries. Copy the entries' tower info into fwd_towers. * * map_mgmt is used for the search if the interface is the mgmt interface, * otherwise map is used. * * If bad status is returned delete the search context, otherwise map * will save it. * * NB: caller should set *num_ents to 0 or appropriate offset into fwd_towers. */ PRIVATE void epdb_map(h_, object, map_tower, map_handle, max_ents, num_ents, fwd_towers, status) epdb_handle_t h_; uuid_p_t object; twr_p_t map_tower; ept_lookup_handle_t *map_handle; unsigned32 max_ents; unsigned32 *num_ents; twr_t *fwd_towers[]; unsigned32 *status; { struct db *h = (struct db *) h_; twr_fields_t twr_fields, *tfp; rpc_if_rep_p_t mgmt_if_rep; db_entry_t **db_entries; unsigned32 start_ent; unsigned32 i; error_status_t tmp_st; tfp = &twr_fields; mgmt_if_rep = (rpc_if_rep_p_t) mgmt_v1_0_s_ifspec; if (db_different_context(h, map_handle, status)) return; /* lock database before delete_context or map */ db_lock(h); tower_to_fields(map_tower, &twr_fields, status); if (! STATUS_OK(status)) { db_delete_context(h, map_handle); SET_STATUS(status, ept_s_invalid_entry); db_unlock(h); return; } epdb_chk_map_entry(&twr_fields, status); if (! STATUS_OK(status)) { db_delete_context(h, map_handle); SET_STATUS(status, ept_s_invalid_entry); db_unlock(h); return; } db_entries = (db_entry_t **) malloc(max_ents * sizeof(db_entry_p_t)); if ((db_entries == NULL) || (fwd_towers == NULL) || (max_ents == 0) || (*num_ents > max_ents)) { if (db_entries != NULL) free(db_entries); db_delete_context(h, map_handle); SET_STATUS(status, ept_s_cant_perform_op); db_unlock(h); return; } start_ent = *num_ents; if (uuid_equal(&tfp->interface.uuid, &mgmt_if_rep->id, &tmp_st)) map_mgmt(h, object, &tfp->data_rep, tfp->rpc_protocol, tfp->rpc_protocol_vers_major, tfp->rpc_protocol_vers_minor, tfp->protseq, map_handle, max_ents, num_ents, db_entries, status); else map(h, object, &tfp->interface, &tfp->data_rep, tfp->rpc_protocol, tfp->rpc_protocol_vers_major, tfp->rpc_protocol_vers_minor, tfp->protseq, map_handle, max_ents, num_ents, db_entries, status); if (! STATUS_OK(status)) { free(db_entries); db_unlock(h); return; } for (i = start_ent; i < *num_ents; i++) { tower_ss_copy(&(db_entries[i]->tower), &fwd_towers[i], status); if (! STATUS_OK(status)) { /* don't remember context but return * towers that already copied to oput array. */ db_delete_context(h, map_handle); *num_ents = i; if (*num_ents > 0) { SET_STATUS_OK(status); } else { SET_STATUS(status, ept_s_cant_perform_op); } break; } } free(db_entries); db_unlock(h); } /* * Search the endpoint database looking for entries which match the target object, * interface, etc. * * If the target object is non-nil, do a 2 pass search * First, search the objectq looking for exact object/interface matches. * Second, search the interfaceq for interface match/object nil * * If the target object is nil * Search the interfaceq for interface match/object nil (same as pass 2 above) * * The following table gives some examples of applying the above algorithm * to various map entrys (the table assumes that the entrys already exactly * match the data rep, protseq, versions, etc. specified in the key): * * Keys and entrys of the form: * Matches (in order of specificity): * Moi - Match on Object and Interface * Mi - Match on Interface * No Match: ' - ' * * * \ Map | * \ entry | * key \ | * -----------|------------------------------------------- * | Moi - - - - * | Mi - Moi - - * */ INTERNAL void map(h, object, interface, data_rep, rpc_protocol, rpc_protocol_vers_major, rpc_protocol_vers_minor, protseq, map_handle, max_ents, n_ents, db_entries, status) struct db *h; uuid_p_t object; rpc_if_id_p_t interface; rpc_syntax_id_p_t data_rep; rpc_protocol_id_t rpc_protocol; unsigned32 rpc_protocol_vers_major; unsigned32 rpc_protocol_vers_minor; rpc_protseq_id_t protseq; ept_lookup_handle_t *map_handle; unsigned32 max_ents; unsigned32 *n_ents; db_entry_t *db_entries[]; unsigned32 *status; { unsigned32 pass; db_list_type_t list_type; db_lists_t *lp; error_status_t tmp_st; SET_STATUS_OK(status); if ((map_handle == NULL) || (*map_handle == NULL)) { if (uuid_is_nil(object, &tmp_st)) { pass = 2; list_type = db_c_interface_list; lp = db_list_first(&h->lists_mgmt, list_type, &interface->uuid); } else { pass = 1; list_type = db_c_object_list; lp = db_list_first(&h->lists_mgmt, list_type, object); } } else { db_get_context(h, map_handle, &list_type, &lp, &pass, status); if (! STATUS_OK(status)) return; } /* search objectq for object/interface match */ if (pass == 1) { map_match( object, interface, data_rep, rpc_protocol, rpc_protocol_vers_major, rpc_protocol_vers_minor, protseq, max_ents, n_ents, db_entries, map_handle, pass, list_type, &lp, status); if (! STATUS_OK(status)) { db_delete_context(h, map_handle); return; } if ((*n_ents >= max_ents) && ((lp != NULL) || (map_handle == NULL))) { /* If entry buffer is full and * have found next match or are not saving context * save context and return * (save_context just returns if map_handle == NULL) */ db_save_context(h, map_handle, list_type, lp, pass); return; } /* get organized for next pass */ pass = 2; list_type = db_c_interface_list; lp = db_list_first(&h->lists_mgmt, list_type, &interface->uuid); } /* end of pass 1 */ /* search interfaceq for interface match/object nil */ if (pass == 2) { map_match( &nil_uuid, interface, data_rep, rpc_protocol, rpc_protocol_vers_major, rpc_protocol_vers_minor, protseq, max_ents, n_ents, db_entries, map_handle, pass, list_type, &lp, status); if (! STATUS_OK(status)) { db_delete_context(h, map_handle); return; } } /* If there's context to be saved, save it. * If there's no context to save, free entry_handle's * context and set entry_handle to NULL */ db_save_context(h, map_handle, list_type, lp, pass); if (*n_ents == 0) { SET_STATUS(status, ept_s_not_registered); } } INTERNAL void map_match( object, interface, data_rep, rpc_protocol, rpc_protocol_vers_major, rpc_protocol_vers_minor, protseq, max_ents, n_ents, entries, map_handle, pass, list_type, lpp, status) uuid_p_t object; rpc_if_id_p_t interface; rpc_syntax_id_p_t data_rep; rpc_protocol_id_t rpc_protocol; unsigned32 rpc_protocol_vers_major; unsigned32 rpc_protocol_vers_minor ATTRIBUTE_UNUSED; rpc_protseq_id_t protseq; unsigned32 max_ents; unsigned32 *n_ents; db_entry_t *entries[]; ept_lookup_handle_t *map_handle; unsigned32 pass ATTRIBUTE_UNUSED; db_list_type_t list_type; db_lists_t **lpp; unsigned32 *status; { boolean32 object_nil; unsigned32 data_rep_vers_major, data_rep_vers_minor; unsigned32 tmp_st; db_lists_t *lp; db_entry_t *entp; SET_STATUS_OK(status); object_nil = uuid_is_nil(object, &tmp_st); data_rep_vers_major = RPC_IF_VERS_MAJOR(data_rep->version); data_rep_vers_minor = RPC_IF_VERS_MINOR(data_rep->version); for (lp = *lpp; lp != NULL; lp = db_list_next(list_type, lp)) { entp = (db_entry_t *) lp; if (entp->delete_flag) continue; if (((object_nil && entp->object_nil) || uuid_equal(object, &entp->object, &tmp_st)) && uuid_equal(&interface->uuid, &entp->interface.uuid, &tmp_st) && (interface->vers_major == entp->interface.vers_major) && (interface->vers_minor <= entp->interface.vers_minor) && (protseq == entp->addr->rpc_protseq_id) && uuid_equal(&data_rep->id, &entp->data_rep_id, &tmp_st) && (data_rep_vers_major == entp->data_rep_vers_major) && (data_rep_vers_minor <= entp->data_rep_vers_minor) && (rpc_protocol == entp->rpc_protocol) && (rpc_protocol_vers_major == entp->rpc_protocol_vers_major)) /* * We dont do this so we can rev the minor protocol number * && (rpc_protocol_vers_minor <= entp->rpc_protocol_vers_minor) ) */ { /* * found the next match and have filled the * entries vector so quit search */ if (*n_ents >= max_ents) { *lpp = lp; return; } entries[*n_ents] = entp; (*n_ents)++; /* if not saving search context don't look for next matching * entry */ if ((map_handle == NULL) && (*n_ents >= max_ents)) { *lpp = lp; return; } } /* entry == target */ } /* for lp */ *lpp = lp; } /* map_mgmt * Packet is for the mgmt interface which all processes export. * Look for entries with matching object, protseq, data_rep, * and rpc_protocol. Only return one entry per endpoint. */ INTERNAL void map_mgmt(h, object, data_rep, rpc_protocol, rpc_protocol_vers_major, rpc_protocol_vers_minor, protseq, map_handle, max_ents, n_ents, db_entries, status) struct db *h; uuid_p_t object; rpc_syntax_id_p_t data_rep; rpc_protocol_id_t rpc_protocol; unsigned32 rpc_protocol_vers_major; unsigned32 rpc_protocol_vers_minor; rpc_protseq_id_t protseq; ept_lookup_handle_t *map_handle; unsigned32 max_ents; unsigned32 *n_ents; db_entry_t *db_entries[]; unsigned32 *status; { unsigned32 pass; db_list_type_t list_type; db_lists_t *lp; error_status_t tmp_st; if (uuid_is_nil(object, &tmp_st)) { db_delete_context(h, map_handle); SET_STATUS(status, ept_s_invalid_entry); return; } if ((map_handle == NULL) || (*map_handle == NULL)) { pass = 1; list_type = db_c_object_list; lp = db_list_first(&h->lists_mgmt, list_type, object); } else { db_get_context(h, map_handle, &list_type, &lp, &pass, status); if (! STATUS_OK(status)) return; } map_mgmt_match( object, data_rep, rpc_protocol, rpc_protocol_vers_major, rpc_protocol_vers_minor, protseq, max_ents, n_ents, db_entries, map_handle, pass, list_type, &lp, status); if (! STATUS_OK(status)) { db_delete_context(h, map_handle); return; } /* If there's context to be saved, save it. * If there's no context to save, free map_handle's * context and set map_handle to NULL */ db_save_context(h, map_handle, list_type, lp, pass); if (*n_ents == 0) { SET_STATUS(status, ept_s_not_registered); } } INTERNAL void map_mgmt_match( object, data_rep, rpc_protocol, rpc_protocol_vers_major, rpc_protocol_vers_minor, protseq, max_ents, n_ents, entries, map_handle, pass, list_type, lpp, status) uuid_p_t object; rpc_syntax_id_p_t data_rep; rpc_protocol_id_t rpc_protocol; unsigned32 rpc_protocol_vers_major; unsigned32 rpc_protocol_vers_minor ATTRIBUTE_UNUSED; rpc_protseq_id_t protseq; unsigned32 max_ents; unsigned32 *n_ents; db_entry_t *entries[]; ept_lookup_handle_t *map_handle; unsigned32 pass ATTRIBUTE_UNUSED; db_list_type_t list_type; db_lists_t **lpp; unsigned32 *status; { unsigned32 data_rep_vers_major, data_rep_vers_minor; unsigned32 tmp_st; db_lists_t *lp; db_entry_t *entp; SET_STATUS_OK(status); data_rep_vers_major = RPC_IF_VERS_MAJOR(data_rep->version); data_rep_vers_minor = RPC_IF_VERS_MINOR(data_rep->version); for (lp = *lpp; lp != NULL; lp = db_list_next(list_type, lp)) { entp = (db_entry_t *) lp; if (entp->delete_flag) continue; if (uuid_equal(object, &entp->object, &tmp_st) && (protseq == entp->addr->rpc_protseq_id) && uuid_equal(&data_rep->id, &entp->data_rep_id, &tmp_st) && (data_rep_vers_major == entp->data_rep_vers_major) && (data_rep_vers_minor <= entp->data_rep_vers_minor) && (rpc_protocol == entp->rpc_protocol) && (rpc_protocol_vers_major == entp->rpc_protocol_vers_major) && /* * We dont do this so we can rev the minor protocol number * (rpc_protocol_vers_minor <= entp->rpc_protocol_vers_minor) && */ /* match - see whether already returning * this endpoint in entries */ map_mgmt_endpt_unique(entp->addr, *n_ents, entries) ) { /* * found the next match with unique endpoint */ /* * have filled the entries vector so quit search */ if (*n_ents >= max_ents) { *lpp = lp; return; } entries[*n_ents] = entp; (*n_ents)++; /* if not saving search context don't look for next matching * entry */ if ((map_handle == NULL) && (*n_ents >= max_ents)) { *lpp = lp; return; } } /* entry == target */ } /* for lp */ *lpp = lp; } /* map_mgmt_endpt_unique * return true if addr does not match addr of any entry in entries * otherwise return false */ INTERNAL boolean32 map_mgmt_endpt_unique(addr, n_ents, entries) rpc_addr_p_t addr; unsigned32 n_ents; db_entry_t *entries[]; { unsigned32 i; error_status_t tmp_st; for (i = 0; i < n_ents; i++) { /* matching endpoint */ if (rpc__naf_addr_compare(addr, entries[i]->addr, &tmp_st)) return(false); } /* no matching endpoint found */ return(true); } /* * Return the database object's id. */ PRIVATE void epdb_inq_object(h_, object, status) epdb_handle_t h_; uuid_t *object; error_status_t *status; { struct db *h = (struct db *) h_; SET_STATUS_OK(status); *object = h->object; } PRIVATE void epdb_delete_lookup_handle(h_, entry_handle) epdb_handle_t h_; ept_lookup_handle_t *entry_handle; { struct db *h = (struct db *) h_; db_lock(h); db_delete_context(h, entry_handle); db_unlock(h); }