From 176b9f7ecaf20b6ff495580f39b5f4c3994dbf6a Mon Sep 17 00:00:00 2001 From: Karl Heyes Date: Tue, 21 Aug 2007 22:30:30 +0000 Subject: [PATCH] Auth update. Have each auth_t has its own queue of requests and thread to process them. Each listener connection for each request is checked as connected before performing the request (so that time isn't wasted on slow authentication). Various name/comment cleanups as well. svn path=/icecast/trunk/icecast/; revision=13583 --- src/auth.c | 174 +++++++++++++++++++++++++++++++---------------- src/auth.h | 23 +++++-- src/auth_url.c | 8 +-- src/cfgfile.c | 2 + src/client.c | 2 +- src/connection.c | 2 +- 6 files changed, 139 insertions(+), 72 deletions(-) diff --git a/src/auth.c b/src/auth.c index 88830e2c..bddd7cdf 100644 --- a/src/auth.c +++ b/src/auth.c @@ -37,11 +37,7 @@ #define CATMODULE "auth" -static volatile auth_client *clients_to_auth; -static volatile unsigned int auth_pending_count; -static volatile int auth_running; static mutex_t auth_lock; -static thread_type *auth_thread; static void auth_client_setup (mount_proxy *mountinfo, client_t *client) @@ -88,17 +84,25 @@ static void auth_client_setup (mount_proxy *mountinfo, client_t *client) thread_mutex_lock (&mountinfo->auth->lock); client->auth = mountinfo->auth; client->auth->refcount++; + DEBUG2 ("...refcount on auth_t %s is %d", client->auth->mount, client->auth->refcount); thread_mutex_unlock (&mountinfo->auth->lock); } static void queue_auth_client (auth_client *auth_user) { - thread_mutex_lock (&auth_lock); - auth_user->next = (auth_client *)clients_to_auth; - clients_to_auth = auth_user; - auth_pending_count++; - thread_mutex_unlock (&auth_lock); + auth_t *auth; + + if (auth_user == NULL) + return; + auth = auth_user->client->auth; + thread_mutex_lock (&auth->lock); + auth_user->next = NULL; + *auth->tailp = auth_user; + auth->tailp = &auth_user->next; + auth->pending_count++; + INFO2 ("auth on %s has %d pending", auth->mount, auth->pending_count); + thread_mutex_unlock (&auth->lock); } @@ -112,22 +116,28 @@ void auth_release (auth_t *authenticator) thread_mutex_lock (&authenticator->lock); authenticator->refcount--; + DEBUG2 ("...refcount on auth_t %s is now %d", authenticator->mount, authenticator->refcount); if (authenticator->refcount) { thread_mutex_unlock (&authenticator->lock); return; } + /* cleanup auth thread attached to this auth */ + authenticator->running = 0; + thread_join (authenticator->thread); + if (authenticator->free) authenticator->free (authenticator); xmlFree (authenticator->type); thread_mutex_unlock (&authenticator->lock); thread_mutex_destroy (&authenticator->lock); + free (authenticator->mount); free (authenticator); } -void auth_client_free (auth_client *auth_user) +static void auth_client_free (auth_client *auth_user) { if (auth_user == NULL) return; @@ -146,6 +156,19 @@ void auth_client_free (auth_client *auth_user) } +/* verify that the listener is still connected. */ +static int is_listener_connected (client_t *client) +{ + int ret = 1; + if (client) + { + if (sock_active (client->con->sock) == 0) + ret = 0; + } + return ret; +} + + /* wrapper function for auth thread to authenticate new listener * connection details */ @@ -153,50 +176,90 @@ static void auth_new_listener (auth_client *auth_user) { client_t *client = auth_user->client; + /* make sure there is still a client at this point, a slow backend request + * can be avoided if client has disconnected */ + if (is_listener_connected (client) == 0) + { + DEBUG0 ("listener is no longer connected"); + client->respcode = 400; + return; + } if (client->auth->authenticate) { if (client->auth->authenticate (auth_user) != AUTH_OK) - { - auth_release (client->auth); - client->auth = NULL; return; - } } - if (auth_postprocess_client (auth_user) < 0) + if (auth_postprocess_listener (auth_user) < 0) INFO1 ("client %lu failed", client->con->id); } -/* wrapper function are auth thread to authenticate new listener - * connections +/* wrapper function for auth thread to drop listener connections */ static void auth_remove_listener (auth_client *auth_user) { client_t *client = auth_user->client; - if (client->auth->release_client) - client->auth->release_client (auth_user); + if (client->auth->release_listener) + client->auth->release_listener (auth_user); auth_release (client->auth); client->auth = NULL; - return; + /* client is going, so auth is not an issue at this point */ + client->authenticated = 0; +} + + +/* Callback from auth thread to handle a stream start event, this applies + * to both source clients and relays. + */ +static void stream_start_callback (auth_client *auth_user) +{ + auth_t *auth = auth_user->client->auth; + + if (auth->stream_start) + auth->stream_start (auth_user); +} + + +/* Callback from auth thread to handle a stream start event, this applies + * to both source clients and relays. + */ +static void stream_end_callback (auth_client *auth_user) +{ + auth_t *auth = auth_user->client->auth; + + if (auth->stream_end) + auth->stream_end (auth_user); } /* The auth thread main loop. */ static void *auth_run_thread (void *arg) { + auth_t *auth = arg; + INFO0 ("Authentication thread started"); - while (1) + while (auth->running) { - if (clients_to_auth) + /* usually no clients are waiting, so don't bother taking locks */ + if (auth->head) { auth_client *auth_user; - thread_mutex_lock (&auth_lock); - auth_user = (auth_client*)clients_to_auth; - clients_to_auth = auth_user->next; - auth_pending_count--; - thread_mutex_unlock (&auth_lock); + /* may become NULL before lock taken */ + thread_mutex_lock (&auth->lock); + auth_user = (auth_client*)auth->head; + if (auth_user == NULL) + { + thread_mutex_unlock (&auth->lock); + continue; + } + DEBUG2 ("%d client(s) pending on %s", auth->pending_count, auth->mount); + auth->head = auth_user->next; + if (auth->head == NULL) + auth->tailp = &auth->head; + auth->pending_count--; + thread_mutex_unlock (&auth->lock); auth_user->next = NULL; if (auth_user->process) @@ -208,9 +271,6 @@ static void *auth_run_thread (void *arg) continue; } - /* is there a request to shutdown */ - if (auth_running == 0) - break; thread_sleep (150000); } INFO0 ("Authenication thread shutting down"); @@ -271,7 +331,7 @@ static int check_duplicate_logins (source_t *source, client_t *client) /* if 0 is returned then the client should not be touched, however if -1 * is returned then the caller is responsible for handling the client */ -static int add_client_to_source (source_t *source, client_t *client) +static int add_listener_to_source (source_t *source, client_t *client) { int loop = 10; do @@ -326,7 +386,7 @@ static int add_client_to_source (source_t *source, client_t *client) /* Add listener to the pending lists of either the source or fserve thread. * This can be run from the connection or auth thread context */ -static int add_authenticated_client (const char *mount, mount_proxy *mountinfo, client_t *client) +static int add_authenticated_listener (const char *mount, mount_proxy *mountinfo, client_t *client) { int ret = 0; source_t *source = NULL; @@ -348,7 +408,7 @@ static int add_authenticated_client (const char *mount, mount_proxy *mountinfo, client->con->discon_time = time(NULL) + mountinfo->max_listener_duration; } - ret = add_client_to_source (source, client); + ret = add_listener_to_source (source, client); avl_tree_unlock (global.source_tree); if (ret == 0) DEBUG0 ("client authenticated, passed to source"); @@ -362,15 +422,16 @@ static int add_authenticated_client (const char *mount, mount_proxy *mountinfo, } -int auth_postprocess_client (auth_client *auth_user) +int auth_postprocess_listener (auth_client *auth_user) { int ret; + client_t *client = auth_user->client; ice_config_t *config = config_get_config(); mount_proxy *mountinfo = config_find_mount (config, auth_user->mount); - auth_user->client->authenticated = 1; + client->authenticated = 1; - ret = add_authenticated_client (auth_user->mount, mountinfo, auth_user->client); + ret = add_authenticated_listener (auth_user->mount, mountinfo, client); config_release_config(); if (ret < 0) @@ -384,7 +445,7 @@ int auth_postprocess_client (auth_client *auth_user) /* Add a listener. Check for any mount information that states any * authentication to be used. */ -void add_client (const char *mount, client_t *client) +void auth_add_listener (const char *mount, client_t *client) { mount_proxy *mountinfo; ice_config_t *config = config_get_config(); @@ -400,7 +461,7 @@ void add_client (const char *mount, client_t *client) { auth_client *auth_user; - if (auth_pending_count > 30) + if (mountinfo->auth->pending_count > 100) { config_release_config (); WARN0 ("too many clients awaiting authentication"); @@ -410,11 +471,6 @@ void add_client (const char *mount, client_t *client) auth_client_setup (mountinfo, client); config_release_config (); - if (client->auth == NULL) - { - client_send_401 (client); - return; - } auth_user = calloc (1, sizeof (auth_client)); if (auth_user == NULL) { @@ -430,7 +486,7 @@ void add_client (const char *mount, client_t *client) } else { - int ret = add_authenticated_client (mount, mountinfo, client); + int ret = add_authenticated_listener (mount, mountinfo, client); config_release_config (); if (ret < 0) client_send_403 (client, "max listeners reached"); @@ -441,7 +497,7 @@ void add_client (const char *mount, client_t *client) /* determine whether we need to process this client further. This * involves any auth exit, typically for external auth servers. */ -int release_client (client_t *client) +int auth_release_listener (client_t *client) { if (client->auth) { @@ -465,13 +521,16 @@ static void get_authenticator (auth_t *auth, config_options_t *options) do { DEBUG1 ("type is %s", auth->type); -#ifdef HAVE_AUTH_URL + if (strcmp (auth->type, "url") == 0) { +#ifdef HAVE_AUTH_URL auth_get_url_auth (auth, options); +#else + ERROR0 ("Auth URL disabled"); +#endif break; } -#endif if (strcmp (auth->type, "htpasswd") == 0) { auth_get_htpasswd_auth (auth, options); @@ -531,7 +590,12 @@ auth_t *auth_get_authenticator (xmlNodePtr node) } auth->type = xmlGetProp (node, "type"); get_authenticator (auth, options); + auth->tailp = &auth->head; thread_mutex_create (&auth->lock); + + auth->running = 1; + auth->thread = thread_create ("auth thread", auth_run_thread, auth, THREAD_ATTACHED); + while (options) { config_options_t *opt = options; @@ -555,7 +619,7 @@ void auth_stream_start (mount_proxy *mountinfo, const char *mount) if (auth_user) { auth_user->mount = strdup (mount); - auth_user->process = mountinfo->auth->stream_start; + auth_user->process = stream_start_callback; queue_auth_client (auth_user); } @@ -574,7 +638,7 @@ void auth_stream_end (mount_proxy *mountinfo, const char *mount) if (auth_user) { auth_user->mount = strdup (mount); - auth_user->process = mountinfo->auth->stream_end; + auth_user->process = stream_end_callback; queue_auth_client (auth_user); } @@ -586,20 +650,12 @@ void auth_stream_end (mount_proxy *mountinfo, const char *mount) void auth_initialise (void) { - clients_to_auth = NULL; - auth_pending_count = 0; - auth_running = 1; thread_mutex_create (&auth_lock); - auth_thread = thread_create ("auth thread", auth_run_thread, NULL, THREAD_ATTACHED); } void auth_shutdown (void) { - if (auth_thread) - { - auth_running = 0; - thread_join (auth_thread); - INFO0 ("Auth thread has terminated"); - } + thread_mutex_destroy (&auth_lock); + INFO0 ("Auth shutdown"); } diff --git a/src/auth.h b/src/auth.h index c481bb0d..761941eb 100644 --- a/src/auth.h +++ b/src/auth.h @@ -53,29 +53,38 @@ typedef struct auth_tag /* Authenticate using the given username and password */ auth_result (*authenticate)(auth_client *aclient); - auth_result (*release_client)(auth_client *auth_user); + auth_result (*release_listener)(auth_client *auth_user); - /* callbacks to specific auth for notifying auth server on source - * startup or shutdown - */ + /* auth handler for source startup, no client passed as it may disappear */ void (*stream_start)(auth_client *auth_user); + + /* auth handler for source exit, no client passed as it may disappear */ void (*stream_end)(auth_client *auth_user); + /* auth state-specific free call */ void (*free)(struct auth_tag *self); + auth_result (*adduser)(struct auth_tag *auth, const char *username, const char *password); auth_result (*deleteuser)(struct auth_tag *auth, const char *username); auth_result (*listuser)(struct auth_tag *auth, xmlNodePtr srcnode); mutex_t lock; + int running; int refcount; int allow_duplicate_users; + thread_type *thread; + + /* per-auth queue for clients */ + auth_client *head, **tailp; + int pending_count; + void *state; char *type; } auth_t; -void add_client (const char *mount, client_t *client); -int release_client (client_t *client); +void auth_add_listener (const char *mount, client_t *client); +int auth_release_listener (client_t *client); void auth_initialise (void); void auth_shutdown (void); @@ -91,7 +100,7 @@ void auth_stream_end (struct _mount_proxy *mountinfo, const char *mount); /* called from auth thread, after the client has successfully authenticated * and requires adding to source or fserve. */ -int auth_postprocess_client (auth_client *auth_user); +int auth_postprocess_listener (auth_client *auth_user); #endif diff --git a/src/auth_url.c b/src/auth_url.c index 03d7922f..db92dc86 100644 --- a/src/auth_url.c +++ b/src/auth_url.c @@ -162,7 +162,7 @@ static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *str } -static auth_result url_remove_client (auth_client *auth_user) +static auth_result url_remove_listener (auth_client *auth_user) { client_t *client = auth_user->client; auth_t *auth = client->auth; @@ -242,7 +242,7 @@ static auth_result url_remove_client (auth_client *auth_user) } -static auth_result url_add_client (auth_client *auth_user) +static auth_result url_add_listener (auth_client *auth_user) { client_t *client = auth_user->client; auth_t *auth = client->auth; @@ -463,8 +463,8 @@ int auth_get_url_auth (auth_t *authenticator, config_options_t *options) { auth_url *url_info; - authenticator->authenticate = url_add_client; - authenticator->release_client = url_remove_client; + authenticator->authenticate = url_add_listener; + authenticator->release_listener = url_remove_listener; authenticator->free = auth_url_clear; authenticator->adduser = auth_url_adduser; diff --git a/src/cfgfile.c b/src/cfgfile.c index 23112337..c3ad6ee3 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -675,6 +675,8 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, config_clear_mount (mount); return; } + if (mount->auth) + mount->auth->mount = strdup (mount->mountname); while(current) { last = current; current = current->next; diff --git a/src/client.c b/src/client.c index f2da419d..2fb21d97 100644 --- a/src/client.c +++ b/src/client.c @@ -93,7 +93,7 @@ void client_destroy(client_t *client) client->refbuf = NULL; } - if (release_client (client)) + if (auth_release_listener (client)) return; /* write log entry if ip is set (some things don't set it, like outgoing diff --git a/src/connection.c b/src/connection.c index 01853ded..3fa4e96e 100644 --- a/src/connection.c +++ b/src/connection.c @@ -882,7 +882,7 @@ static void _handle_get_request (client_t *client, char *passed_uri) return; } - add_client (uri, client); + auth_add_listener (uri, client); if (uri != passed_uri) free (uri); }