Commit 176b9f7e authored by Karl Heyes's avatar Karl Heyes

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
parent 620c51f6
......@@ -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");
}
......@@ -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
......
......@@ -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;
......
......@@ -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;
......
......@@ -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
......
......@@ -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);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment