diff --git a/src/guacd/Makefile.am b/src/guacd/Makefile.am index b722ba05..2c2078e4 100644 --- a/src/guacd/Makefile.am +++ b/src/guacd/Makefile.am @@ -22,14 +22,14 @@ AUTOMAKE_OPTIONS = foreign -AM_CFLAGS = -Werror -Wall -pedantic @LIBGUAC_INCLUDE@ +AM_CFLAGS = -Werror -Wall -pedantic @LIBGUAC_INCLUDE@ @COMMON_INCLUDE@ sbin_PROGRAMS = guacd man_MANS = man/guacd.8 -noinst_HEADERS = client.h log.h -guacd_SOURCES = daemon.c client.c log.c -guacd_LDADD = @LIBGUAC_LTLIB@ +noinst_HEADERS = client.h client-map.h log.h +guacd_SOURCES = daemon.c client.c client-map.c log.c +guacd_LDADD = @LIBGUAC_LTLIB@ @COMMON_LTLIB@ guacd_LDFLAGS = @PTHREAD_LIBS@ @SSL_LIBS@ EXTRA_DIST = init.d/guacd.in man/guacd.8 diff --git a/src/guacd/client-map.c b/src/guacd/client-map.c new file mode 100644 index 00000000..5fdaff8b --- /dev/null +++ b/src/guacd/client-map.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2014 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" +#include "client.h" +#include "client-map.h" +#include "guac_list.h" + +#include + +#include +#include + +/** + * Returns a hash code based on the connection ID of the given client. + */ +static unsigned int __guacd_client_hash(const char* str) { + + unsigned int hash_value = 0; + int c; + + /* Apply each character in string to the hash code */ + while ((c = *(str++))) + hash_value = hash_value * 65599 + c; + + return hash_value; + +} + +static guac_common_list* __guacd_client_find_bucket(guacd_client_map* map, const char* id) { + + const int index = __guacd_client_hash(id) % GUACD_CLIENT_MAP_BUCKETS; + return map->__buckets[index]; + +} + +static guac_common_list_element* __guacd_client_find(guac_common_list* bucket, const char* id) { + + guac_common_list_element* current = bucket->head; + + /* Search for matching element within bucket */ + while (current != NULL) { + + /* Check connection ID */ + guac_client* client = (guac_client*) current->data; + if (strcmp(client->connection_id, id) == 0) + break; + + current = current->next; + } + + return current; + +} + +guacd_client_map* guacd_client_map_alloc() { + + guacd_client_map* map = malloc(sizeof(guacd_client_map)); + guac_common_list** current; + + int i; + + /* Init all buckets */ + current = map->__buckets; + + for (i=0; iconnection_id); + guac_common_list_element* found; + + /* Retrieve corresponding element, if any */ + guac_common_list_lock(bucket); + found = __guacd_client_find(bucket, client->connection_id); + + /* If no such element, we can add the new client successfully */ + if (found == NULL) { + guac_common_list_add(bucket, client); + guac_common_list_unlock(bucket); + return 0; + } + + /* Otherwise, fail - already exists */ + guac_common_list_unlock(bucket); + return 1; + +} + +guac_client* guacd_client_map_retrieve(guacd_client_map* map, const char* id) { + + guac_client* client; + + guac_common_list* bucket = __guacd_client_find_bucket(map, id); + guac_common_list_element* found; + + /* Retrieve corresponding element, if any */ + guac_common_list_lock(bucket); + found = __guacd_client_find(bucket, id); + + /* If no such element, fail */ + if (found == NULL) { + guac_common_list_unlock(bucket); + return NULL; + } + + client = (guac_client*) found->data; + + guac_common_list_unlock(bucket); + return client; + +} + +guac_client* guacd_client_map_remove(guacd_client_map* map, const char* id) { + + guac_client* client; + + guac_common_list* bucket = __guacd_client_find_bucket(map, id); + guac_common_list_element* found; + + /* Retrieve corresponding element, if any */ + guac_common_list_lock(bucket); + found = __guacd_client_find(bucket, id); + + /* If no such element, fail */ + if (found == NULL) { + guac_common_list_unlock(bucket); + return NULL; + } + + client = (guac_client*) found->data; + guac_common_list_remove(bucket, found); + + guac_common_list_unlock(bucket); + return client; + +} + diff --git a/src/guacd/client-map.h b/src/guacd/client-map.h new file mode 100644 index 00000000..8544bc4e --- /dev/null +++ b/src/guacd/client-map.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef _GUACD_CLIENT_MAP_H +#define _GUACD_CLIENT_MAP_H + +#include "config.h" +#include "client.h" +#include "guac_list.h" + +#include + +#define GUACD_CLIENT_MAP_BUCKETS GUACD_CLIENT_MAX_CONNECTIONS*2 + +/** + * Set of all active connections to guacd, indexed by connection ID. + */ +typedef struct guacd_client_map { + + /** + * Internal hash buckets. Each bucket is a linked list containing all + * guac_client instances which hash to this bucket location. + */ + guac_common_list* __buckets[GUACD_CLIENT_MAP_BUCKETS]; + +} guacd_client_map; + +/** + * Allocates a new client map, which persists for the life of guacd. + */ +guacd_client_map* guacd_client_map_alloc(); + +/** + * Adds the given client to the given client map. On success, zero is returned. + * If adding the client fails (due to lack of space, or duplicate ID), a + * non-zero value is returned instead. + */ +int guacd_client_map_add(guacd_client_map* map, guac_client* client); + +/** + * Retrieves the client having the given ID, or NULL if no such client + * is stored. + */ +guac_client* guacd_client_map_retrieve(guacd_client_map* map, const char* id); + +/** + * Removes the client having the given ID, returning the corresponding client. + * If no such client exists, NULL is returned. + */ +guac_client* guacd_client_map_remove(guacd_client_map* map, const char* id); + +#endif + diff --git a/src/guacd/client.h b/src/guacd/client.h index cca21d88..214af2a0 100644 --- a/src/guacd/client.h +++ b/src/guacd/client.h @@ -67,6 +67,13 @@ */ #define GUACD_USEC_TIMEOUT (GUACD_TIMEOUT*1000) +/** + * The maximum number of concurrent connections to a single instance + * of guacd. + */ +#define GUACD_CLIENT_MAX_CONNECTIONS 65536 + int guacd_client_start(guac_client* client); #endif +