/* * Copyright (C) 2008 Martin Willi * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include "attribute_manager.h" #include #include #include typedef struct private_attribute_manager_t private_attribute_manager_t; /** * private data of attribute_manager */ struct private_attribute_manager_t { /** * public functions */ attribute_manager_t public; /** * list of registered providers */ linked_list_t *providers; /** * list of registered handlers */ linked_list_t *handlers; /** * rwlock provider list */ rwlock_t *lock; }; /** * Data to pass to enumerator filters */ typedef struct { /** attribute group pools */ linked_list_t *pools; /** associated IKE_SA */ ike_sa_t *ike_sa; /** requesting/assigned virtual IPs */ linked_list_t *vips; } enum_data_t; METHOD(attribute_manager_t, acquire_address, host_t*, private_attribute_manager_t *this, linked_list_t *pools, ike_sa_t *ike_sa, host_t *requested) { enumerator_t *enumerator; attribute_provider_t *current; host_t *host = NULL; this->lock->read_lock(this->lock); enumerator = this->providers->create_enumerator(this->providers); while (enumerator->enumerate(enumerator, ¤t)) { host = current->acquire_address(current, pools, ike_sa, requested); if (host) { break; } } enumerator->destroy(enumerator); this->lock->unlock(this->lock); return host; } METHOD(attribute_manager_t, release_address, bool, private_attribute_manager_t *this, linked_list_t *pools, host_t *address, ike_sa_t *ike_sa) { enumerator_t *enumerator; attribute_provider_t *current; bool found = FALSE; this->lock->read_lock(this->lock); enumerator = this->providers->create_enumerator(this->providers); while (enumerator->enumerate(enumerator, ¤t)) { if (current->release_address(current, pools, address, ike_sa)) { found = TRUE; break; } } enumerator->destroy(enumerator); this->lock->unlock(this->lock); return found; } /** * inner enumerator constructor for responder attributes */ static enumerator_t *responder_enum_create(attribute_provider_t *provider, enum_data_t *data) { return provider->create_attribute_enumerator(provider, data->pools, data->ike_sa, data->vips); } METHOD(attribute_manager_t, create_responder_enumerator, enumerator_t*, private_attribute_manager_t *this, linked_list_t *pools, ike_sa_t *ike_sa, linked_list_t *vips) { enum_data_t *data; INIT(data, .pools = pools, .ike_sa = ike_sa, .vips = vips, ); this->lock->read_lock(this->lock); return enumerator_create_cleaner( enumerator_create_nested( this->providers->create_enumerator(this->providers), (void*)responder_enum_create, data, free), (void*)this->lock->unlock, this->lock); } METHOD(attribute_manager_t, add_provider, void, private_attribute_manager_t *this, attribute_provider_t *provider) { this->lock->write_lock(this->lock); this->providers->insert_last(this->providers, provider); this->lock->unlock(this->lock); } METHOD(attribute_manager_t, remove_provider, void, private_attribute_manager_t *this, attribute_provider_t *provider) { this->lock->write_lock(this->lock); this->providers->remove(this->providers, provider, NULL); this->lock->unlock(this->lock); } METHOD(attribute_manager_t, handle, attribute_handler_t*, private_attribute_manager_t *this, ike_sa_t *ike_sa, attribute_handler_t *handler, configuration_attribute_type_t type, chunk_t data) { enumerator_t *enumerator; attribute_handler_t *current, *handled = NULL; this->lock->read_lock(this->lock); /* try to find the passed handler */ enumerator = this->handlers->create_enumerator(this->handlers); while (enumerator->enumerate(enumerator, ¤t)) { if (current == handler && current->handle(current, ike_sa, type, data)) { handled = current; break; } } enumerator->destroy(enumerator); if (!handled) { /* handler requesting this attribute not found, try any other */ enumerator = this->handlers->create_enumerator(this->handlers); while (enumerator->enumerate(enumerator, ¤t)) { if (current->handle(current, ike_sa, type, data)) { handled = current; break; } } enumerator->destroy(enumerator); } this->lock->unlock(this->lock); if (!handled) { DBG1(DBG_CFG, "handling %N attribute failed", configuration_attribute_type_names, type); } return handled; } METHOD(attribute_manager_t, release, void, private_attribute_manager_t *this, attribute_handler_t *handler, ike_sa_t *ike_sa, configuration_attribute_type_t type, chunk_t data) { enumerator_t *enumerator; attribute_handler_t *current; this->lock->read_lock(this->lock); enumerator = this->handlers->create_enumerator(this->handlers); while (enumerator->enumerate(enumerator, ¤t)) { if (current == handler) { current->release(current, ike_sa, type, data); break; } } enumerator->destroy(enumerator); this->lock->unlock(this->lock); } /** * Enumerator implementation to enumerate nested initiator attributes */ typedef struct { /** implements enumerator_t */ enumerator_t public; /** back ref */ private_attribute_manager_t *this; /** currently processing handler */ attribute_handler_t *handler; /** outer enumerator over handlers */ enumerator_t *outer; /** inner enumerator over current handlers attributes */ enumerator_t *inner; /** IKE_SA to request attributes for */ ike_sa_t *ike_sa; /** virtual IPs we are requesting along with attributes */ linked_list_t *vips; } initiator_enumerator_t; METHOD(enumerator_t, initiator_enumerate, bool, initiator_enumerator_t *this, va_list args) { configuration_attribute_type_t *type; attribute_handler_t **handler; chunk_t *value; VA_ARGS_VGET(args, handler, type, value); /* enumerate inner attributes using outer handler enumerator */ while (!this->inner || !this->inner->enumerate(this->inner, type, value)) { if (!this->outer->enumerate(this->outer, &this->handler)) { return FALSE; } DESTROY_IF(this->inner); this->inner = this->handler->create_attribute_enumerator(this->handler, this->ike_sa, this->vips); } /* inject the handler as additional attribute */ *handler = this->handler; return TRUE; } METHOD(enumerator_t, initiator_destroy, void, initiator_enumerator_t *this) { this->this->lock->unlock(this->this->lock); this->outer->destroy(this->outer); DESTROY_IF(this->inner); free(this); } METHOD(attribute_manager_t, create_initiator_enumerator, enumerator_t*, private_attribute_manager_t *this, ike_sa_t *ike_sa, linked_list_t *vips) { initiator_enumerator_t *enumerator; this->lock->read_lock(this->lock); INIT(enumerator, .public = { .enumerate = enumerator_enumerate_default, .venumerate = _initiator_enumerate, .destroy = _initiator_destroy, }, .this = this, .ike_sa = ike_sa, .vips = vips, .outer = this->handlers->create_enumerator(this->handlers), ); return &enumerator->public; } METHOD(attribute_manager_t, add_handler, void, private_attribute_manager_t *this, attribute_handler_t *handler) { this->lock->write_lock(this->lock); this->handlers->insert_last(this->handlers, handler); this->lock->unlock(this->lock); } METHOD(attribute_manager_t, remove_handler, void, private_attribute_manager_t *this, attribute_handler_t *handler) { this->lock->write_lock(this->lock); this->handlers->remove(this->handlers, handler, NULL); this->lock->unlock(this->lock); } METHOD(attribute_manager_t, destroy, void, private_attribute_manager_t *this) { this->providers->destroy(this->providers); this->handlers->destroy(this->handlers); this->lock->destroy(this->lock); free(this); } /* * see header file */ attribute_manager_t *attribute_manager_create() { private_attribute_manager_t *this; INIT(this, .public = { .acquire_address = _acquire_address, .release_address = _release_address, .create_responder_enumerator = _create_responder_enumerator, .add_provider = _add_provider, .remove_provider = _remove_provider, .handle = _handle, .release = _release, .create_initiator_enumerator = _create_initiator_enumerator, .add_handler = _add_handler, .remove_handler = _remove_handler, .destroy = _destroy, }, .providers = linked_list_create(), .handlers = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), ); return &this->public; }