Files
strongswan-ext/src/libstrongswan/credentials/cred_encoding.c
T
Tobias Brunner 3b09c02ec0 Properly hash pointers for hash tables where appropriate
Simply using the pointer is not optimal for our hash table
implementation, which simply masks the key to determine the bucket.
2014-03-31 14:32:44 +02:00

283 lines
6.1 KiB
C

/*
* Copyright (C) 2009 Martin Willi
* 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 <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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 "cred_encoding.h"
#include <stdint.h>
#include <collections/linked_list.h>
#include <collections/hashtable.h>
#include <threading/rwlock.h>
typedef struct private_cred_encoding_t private_cred_encoding_t;
/**
* Private data of an cred_encoding_t object.
*/
struct private_cred_encoding_t {
/**
* Public cred_encoding_t interface.
*/
cred_encoding_t public;
/**
* cached encodings, a table for each encoding_type_t, containing chunk_t*
*/
hashtable_t *cache[CRED_ENCODING_MAX];
/**
* Registered encoding fuctions, cred_encoder_t
*/
linked_list_t *encoders;
/**
* lock to access cache/encoders
*/
rwlock_t *lock;
};
/**
* See header.
*/
bool cred_encoding_args(va_list args, ...)
{
va_list parts, copy;
bool failed = FALSE;
va_start(parts, args);
while (!failed)
{
cred_encoding_part_t current, target;
chunk_t *out, data;
/* get the part we are looking for */
target = va_arg(parts, cred_encoding_part_t);
if (target == CRED_PART_END)
{
break;
}
out = va_arg(parts, chunk_t*);
va_copy(copy, args);
while (!failed)
{
current = va_arg(copy, cred_encoding_part_t);
if (current == CRED_PART_END)
{
failed = TRUE;
break;
}
data = va_arg(copy, chunk_t);
if (current == target)
{
*out = data;
break;
}
}
va_end(copy);
}
va_end(parts);
return !failed;
}
METHOD(cred_encoding_t, get_cache, bool,
private_cred_encoding_t *this, cred_encoding_type_t type, void *cache,
chunk_t *encoding)
{
chunk_t *chunk;
if (type >= CRED_ENCODING_MAX || (int)type < 0)
{
return FALSE;
}
this->lock->read_lock(this->lock);
chunk = this->cache[type]->get(this->cache[type], cache);
if (chunk)
{
*encoding = *chunk;
}
this->lock->unlock(this->lock);
return !!chunk;
}
/**
* Implementation of cred_encoding_t.encode
*/
static bool encode(private_cred_encoding_t *this, cred_encoding_type_t type,
void *cache, chunk_t *encoding, ...)
{
enumerator_t *enumerator;
va_list args, copy;
cred_encoder_t encode;
bool success = FALSE;
chunk_t *chunk;
if (type >= CRED_ENCODING_MAX || (int)type < 0)
{
return FALSE;
}
this->lock->read_lock(this->lock);
if (cache)
{
chunk = this->cache[type]->get(this->cache[type], cache);
if (chunk)
{
*encoding = *chunk;
this->lock->unlock(this->lock);
return TRUE;
}
}
va_start(args, encoding);
enumerator = this->encoders->create_enumerator(this->encoders);
while (enumerator->enumerate(enumerator, &encode))
{
va_copy(copy, args);
success = encode(type, encoding, copy);
va_end(copy);
if (success)
{
break;
}
}
enumerator->destroy(enumerator);
this->lock->unlock(this->lock);
va_end(args);
if (success && cache)
{
chunk = malloc_thing(chunk_t);
*chunk = *encoding;
this->lock->write_lock(this->lock);
chunk = this->cache[type]->put(this->cache[type], cache, chunk);
this->lock->unlock(this->lock);
if (chunk)
{
free(chunk->ptr);
free(chunk);
}
}
return success;
}
METHOD(cred_encoding_t, cache, void,
private_cred_encoding_t *this, cred_encoding_type_t type, void *cache,
chunk_t encoding)
{
chunk_t *chunk;
if (type >= CRED_ENCODING_MAX || (int)type < 0)
{
return free(encoding.ptr);
}
chunk = malloc_thing(chunk_t);
*chunk = encoding;
this->lock->write_lock(this->lock);
chunk = this->cache[type]->put(this->cache[type], cache, chunk);
this->lock->unlock(this->lock);
/* free an encoding already associated to the cache */
if (chunk)
{
free(chunk->ptr);
free(chunk);
}
}
METHOD(cred_encoding_t, clear_cache, void,
private_cred_encoding_t *this, void *cache)
{
cred_encoding_type_t type;
chunk_t *chunk;
this->lock->write_lock(this->lock);
for (type = 0; type < CRED_ENCODING_MAX; type++)
{
chunk = this->cache[type]->remove(this->cache[type], cache);
if (chunk)
{
chunk_free(chunk);
free(chunk);
}
}
this->lock->unlock(this->lock);
}
METHOD(cred_encoding_t, add_encoder, void,
private_cred_encoding_t *this, cred_encoder_t encoder)
{
this->lock->write_lock(this->lock);
this->encoders->insert_last(this->encoders, encoder);
this->lock->unlock(this->lock);
}
METHOD(cred_encoding_t, remove_encoder, void,
private_cred_encoding_t *this, cred_encoder_t encoder)
{
this->lock->write_lock(this->lock);
this->encoders->remove(this->encoders, encoder, NULL);
this->lock->unlock(this->lock);
}
METHOD(cred_encoding_t, destroy, void,
private_cred_encoding_t *this)
{
cred_encoding_type_t type;
for (type = 0; type < CRED_ENCODING_MAX; type++)
{
/* We explicitly do not free remaining encodings. All creds should
* have gone now, and they are responsible for cleaning out their
* cache entries. Not flushing here allows the leak detective to
* complain if a credential did not flush cached encodings. */
this->cache[type]->destroy(this->cache[type]);
}
this->encoders->destroy(this->encoders);
this->lock->destroy(this->lock);
free(this);
}
/**
* See header
*/
cred_encoding_t *cred_encoding_create()
{
private_cred_encoding_t *this;
cred_encoding_type_t type;
INIT(this,
.public = {
.encode = (bool(*)(cred_encoding_t*, cred_encoding_type_t type, void *cache, chunk_t *encoding, ...))encode,
.get_cache = _get_cache,
.cache = _cache,
.clear_cache = _clear_cache,
.add_encoder = _add_encoder,
.remove_encoder = _remove_encoder,
.destroy = _destroy,
},
.encoders = linked_list_create(),
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
);
for (type = 0; type < CRED_ENCODING_MAX; type++)
{
this->cache[type] = hashtable_create(hashtable_hash_ptr,
hashtable_equals_ptr, 8);
}
return &this->public;
}