static int connection_client_setup (connection_queue_t *node) { int err; err = -ENOENT; if (node->con->con_timeout <= time(NULL)) return err; global_lock(); err = client_create (&node->client, node->con, node->parser); if (err < 0) goto out_fail; if (sock_set_blocking (node->con->sock, 0) || sock_set_nodelay (node->con->sock)) { if (! sock_recoverable(sock_error())) { node->con->error = 1; err = -EINVAL; goto out_fail; } err = -EINPROGRESS; client_send_403 (node->client, "failed to set tcp options on client connection, dropping"); goto out_fail; } global_unlock(); return 0; out_fail: global_unlock(); return err; }
static void _handle_source_request (client_t *client, char *uri, int auth_style) { source_t *source; INFO1("Source logging in at mountpoint \"%s\"", uri); if (uri[0] != '/') { WARN0 ("source mountpoint not starting with /"); client_send_401 (client); return; } if (auth_style == ICECAST_SOURCE_AUTH) { if (connection_check_source_pass (client->parser, uri) == 0) { /* We commonly get this if the source client is using the wrong * protocol: attempt to diagnose this and return an error */ /* TODO: Do what the above comment says */ INFO1("Source (%s) attempted to login with invalid or missing password", uri); client_send_401(client); return; } } source = source_reserve (uri); if (source) { if (auth_style == SHOUTCAST_SOURCE_AUTH) { source->shoutcast_compat = 1; } source->client = client; source->parser = client->parser; source->con = client->con; if (connection_complete_source (source, 1) < 0) { source_clear_source (source); source_free_source (source); } else { refbuf_t *ok = refbuf_new (PER_CLIENT_REFBUF_SIZE); client->respcode = 200; snprintf (ok->data, PER_CLIENT_REFBUF_SIZE, "HTTP/1.0 200 OK\r\n\r\n"); ok->len = strlen (ok->data); /* we may have unprocessed data read in, so don't overwrite it */ ok->associated = client->refbuf; client->refbuf = ok; fserve_add_client_callback (client, source_client_callback, source); } } else { client_send_403 (client, "Mountpoint in use"); WARN1 ("Mountpoint %s in use", uri); } }
/* Add a listener. Check for any mount information that states any * authentication to be used. */ void auth_add_listener (const char *mount, client_t *client) { mount_proxy *mountinfo; ice_config_t *config = config_get_config(); mountinfo = config_find_mount (config, mount, MOUNT_TYPE_NORMAL); if (mountinfo && mountinfo->no_mount) { config_release_config (); client_send_403 (client, "mountpoint unavailable"); return; } if (mountinfo && mountinfo->auth) { auth_client *auth_user; if (mountinfo->auth->pending_count > 100) { config_release_config (); ICECAST_LOG_WARN("too many clients awaiting authentication"); client_send_403 (client, "busy, please try again later"); return; } auth_user = auth_client_setup (mount, client); auth_user->process = auth_new_listener; ICECAST_LOG_INFO("adding client for authentication"); queue_auth_client (auth_user, mountinfo); config_release_config (); } else { int ret = add_authenticated_listener (mount, mountinfo, client); config_release_config (); if (ret < 0) client_send_403 (client, "max listeners reached"); } }
void source_startup (client_t *client, const char *uri, int auth_style) { source_t *source; refbuf_t *ok; source = source_reserve (uri); if (!source) { client_send_403 (client, "Mountpoint in use"); WARN1 ("Mountpoint %s in use", uri); return; } source->client = client; source->parser = client->parser; source->con = client->con; if (connection_complete_source (source, 1) < 0) { source_clear_source (source); source_free_source (source); return; } client->respcode = 200; switch (auth_style) { case SHOUTCAST_SOURCE_AUTH: source->shoutcast_compat = 1; case NOAUTH_SOURCE_AUTH: break; case ICECAST_SOURCE_AUTH: ok = refbuf_new (PER_CLIENT_REFBUF_SIZE); client->respcode = 200; snprintf (ok->data, PER_CLIENT_REFBUF_SIZE, "HTTP/1.0 200 OK\r\n\r\n"); ok->len = strlen (ok->data); /* we may have unprocessed data read in, so don't overwrite it */ ok->associated = client->refbuf; client->refbuf = ok; break; default: WARN1("Got unkown source auth type: %d", auth_style); return; } fserve_add_client_callback (client, source_client_callback, source); }
int fserve_setup_client_fb (client_t *client, fbinfo *finfo) { fh_node *fh = &no_file; int ret = 0; refbuf_t *refbuf; ssize_t bytes; if (finfo) { mount_proxy *minfo; if (finfo->flags & FS_FALLBACK && finfo->limit == 0) return -1; avl_tree_wlock (fh_cache); fh = find_fh (finfo); minfo = config_find_mount (config_get_config(), finfo->mount); if (fh) { thread_mutex_lock (&fh->lock); avl_tree_unlock (fh_cache); client->shared_data = NULL; if (minfo) { if (minfo->max_listeners >= 0 && fh->refcount > minfo->max_listeners) { thread_mutex_unlock (&fh->lock); config_release_config(); return client_send_403redirect (client, finfo->mount, "max listeners reached"); } if (check_duplicate_logins (finfo->mount, fh->clients, client, minfo->auth) == 0) { thread_mutex_unlock (&fh->lock); config_release_config(); return client_send_403 (client, "Account already in use"); } } config_release_config(); } else { if (minfo && minfo->max_listeners == 0) { avl_tree_unlock (fh_cache); config_release_config(); client->shared_data = NULL; return client_send_403redirect (client, finfo->mount, "max listeners reached"); } config_release_config(); fh = open_fh (finfo); if (fh == NULL) return client_send_404 (client, NULL); if (fh->finfo.limit) DEBUG2 ("request for throttled file %s (bitrate %d)", fh->finfo.mount, fh->finfo.limit*8); } if (fh->finfo.limit) { client->timer_start = client->worker->current_time.tv_sec; if (client->connection.sent_bytes == 0) client->timer_start -= 2; client->counter = 0; client->intro_offset = 0; global_reduce_bitrate_sampling (global.out_bitrate); } } else { if (client->mount && (client->flags & CLIENT_AUTHENTICATED) && (client->respcode >= 300 || client->respcode < 200)) { fh = calloc (1, sizeof (no_file)); fh->finfo.mount = strdup (client->mount); fh->finfo.flags |= FS_DELETE; fh->refcount = 1; fh->f = SOCK_ERROR; thread_mutex_create (&fh->lock); } thread_mutex_lock (&fh->lock); } client->mount = fh->finfo.mount; if (fh->finfo.type == FORMAT_TYPE_UNDEFINED) { if (client->respcode == 0) { client->refbuf->len = 0; ret = format_general_headers (fh->format, client); } } else { if (fh->format->create_client_data && client->format_data == NULL) ret = fh->format->create_client_data (fh->format, client); if (fh->format->write_buf_to_client) client->check_buffer = fh->format->write_buf_to_client; } if (ret < 0) { thread_mutex_unlock (&fh->lock); return client_send_416 (client); } fh_add_client (fh, client); thread_mutex_unlock (&fh->lock); client->shared_data = fh; if (client->check_buffer == NULL) client->check_buffer = format_generic_write_to_client; // workaround for #134: fill the preallocated, but empty, chained buffer in case a range request was made if (client->flags & CLIENT_RANGE_END) { if (client->refbuf && client->refbuf->next) { refbuf = client->refbuf->next; bytes = pread (fh->f, refbuf->data, refbuf->len, client->intro_offset); if (bytes < 0) return -1; } } client->ops = &buffer_content_ops; client->flags &= ~CLIENT_HAS_INTRO_CONTENT; client->flags |= CLIENT_IN_FSERVE; if (client->flags & CLIENT_ACTIVE) { client->schedule_ms = client->worker->time_ms; if (finfo && finfo->flags & FS_FALLBACK) return 0; // prevent a recursive loop return client->ops->process (client); } else { worker_t *worker = client->worker; ret = (fh->finfo.limit) ? 0 : -1; client->flags |= CLIENT_ACTIVE; worker_wakeup (worker); /* worker may of already processed client but make sure */ } return ret; }
int client_send_403redirect (client_t *client, const char *mount, const char *reason) { if (redirect_client (mount, client)) return 0; return client_send_403 (client, reason); }
/* Add a listener. Check for any mount information that states any * authentication to be used. */ int auth_add_listener (const char *mount, client_t *client) { int ret = 0, need_auth = 1; ice_config_t *config = config_get_config(); mount_proxy *mountinfo = config_find_mount (config, mount); if (client->flags & CLIENT_AUTHENTICATED) need_auth = 0; else { const char *range = httpp_getvar (client->parser, "range"); if (range) { uint64_t pos1 = 0, pos2 = (uint64_t)-1; if (strncmp (range, "bytes=", 6) == 0) { if (sscanf (range+6, "-%" SCNuMAX, &pos2) < 1) if (sscanf (range+6, "%" SCNuMAX "-%" SCNuMAX, &pos1, &pos2) < 1) pos2 = 0; } else pos2 = 0; if (pos2 > 0 && pos1 < pos2) { client->intro_offset = pos1; client->connection.discon.offset = pos2; client->flags |= CLIENT_RANGE_END; if (pos2 - pos1 < 10) need_auth = 0; // avoid auth check if range is very small, player hack } else WARN2 ("client range invalid (%" PRIu64 ", %" PRIu64 "), ignoring", pos1, pos2); } } if (client->parser->req_type == httpp_req_head) { client->flags &= ~CLIENT_AUTHENTICATED; need_auth = 0; } if (need_auth) { if (mountinfo) { auth_t *auth = mountinfo->auth; if (mountinfo->skip_accesslog) client->flags |= CLIENT_SKIP_ACCESSLOG; if (mountinfo->ban_client) { if (mountinfo->ban_client < 0) client->flags |= CLIENT_IP_BAN_LIFT; connection_add_banned_ip (client->connection.ip, mountinfo->ban_client); } if (mountinfo->no_mount) { config_release_config (); return client_send_403 (client, "mountpoint unavailable"); } if (mountinfo->redirect) { char buffer [4096] = ""; unsigned int len = sizeof buffer; if (util_expand_pattern (mount, mountinfo->redirect, buffer, &len) == 0) { config_release_config (); return client_send_302 (client, buffer); } WARN3 ("failed to expand %s on %s for %s", mountinfo->redirect, mountinfo->mountname, mount); return client_send_501 (client); } do { if (auth == NULL) break; if ((auth->flags & AUTH_RUNNING) == 0) break; if (auth->pending_count > 400) { if (auth->flags & AUTH_SKIP_IF_SLOW) break; config_release_config (); WARN0 ("too many clients awaiting authentication"); if (global.new_connections_slowdown < 10) global.new_connections_slowdown++; return client_send_403 (client, "busy, please try again later"); } if (auth->authenticate) { auth_client *auth_user = auth_client_setup (mount, client); auth_user->process = auth_new_listener; client->flags &= ~CLIENT_ACTIVE; DEBUG0 ("adding client for authentication"); queue_auth_client (auth_user, mountinfo); config_release_config (); return 0; } } while (0); } else { if (strcmp (mount, "/admin/streams") == 0) { config_release_config (); return client_send_401 (client, NULL); } } } ret = add_authenticated_listener (mount, mountinfo, client); config_release_config (); return ret; }
/* Called when activating a source. Verifies that the source count is not * exceeded and applies any initial parameters. */ int connection_complete_source (source_t *source, int response) { ice_config_t *config; global_lock (); DEBUG1 ("sources count is %d", global.sources); config = config_get_config(); if (global.sources < config->source_limit) { const char *contenttype; const char *expectcontinue; mount_proxy *mountinfo; format_type_t format_type; /* setup format handler */ contenttype = httpp_getvar (source->parser, "content-type"); if (contenttype != NULL) { format_type = format_get_type (contenttype); if (format_type == FORMAT_ERROR) { config_release_config(); global_unlock(); if (response) { client_send_403 (source->client, "Content-type not supported"); source->client = NULL; } WARN1("Content-type \"%s\" not supported, dropping source", contenttype); return -1; } } else { WARN0("No content-type header, falling back to backwards compatibility mode " "for icecast 1.x relays. Assuming content is mp3."); format_type = FORMAT_TYPE_GENERIC; } if (format_get_plugin (format_type, source) < 0) { global_unlock(); config_release_config(); if (response) { client_send_403 (source->client, "internal format allocation problem"); source->client = NULL; } WARN1 ("plugin format failed for \"%s\"", source->mount); return -1; } /* For PUT support we check for 100-continue and send back a 100 to stay in spec */ expectcontinue = httpp_getvar (source->parser, "expect"); if (expectcontinue != NULL) { #ifdef HAVE_STRCASESTR if (strcasestr (expectcontinue, "100-continue") != NULL) #else WARN0("OS doesn't support case insenestive substring checks..."); if (strstr (expectcontinue, "100-continue") != NULL) #endif { client_send_100 (source->client); } } global.sources++; stats_event_args (NULL, "sources", "%d", global.sources); global_unlock(); source->running = 1; mountinfo = config_find_mount (config, source->mount, MOUNT_TYPE_NORMAL); source_update_settings (config, source, mountinfo); config_release_config(); slave_rebuild_mounts(); source->shutdown_rwlock = &_source_shutdown_rwlock; DEBUG0 ("source is ready to start"); return 0; } WARN1("Request to add source when maximum source limit " "reached %d", global.sources); global_unlock(); config_release_config(); if (response) { client_send_403 (source->client, "too many sources connected"); source->client = NULL; } return -1; }
void connection_accept_loop (void) { connection_t *con; ice_config_t *config; int duration = 300; config = config_get_config (); get_ssl_certificate (config); config_release_config (); while (global.running == ICE_RUNNING) { con = _accept_connection (duration); if (con) { client_queue_t *node; ice_config_t *config; client_t *client = NULL; listener_t *listener; global_lock(); if (client_create (&client, con, NULL) < 0) { global_unlock(); client_send_403 (client, "Icecast connection limit reached"); /* don't be too eager as this is an imposed hard limit */ thread_sleep (400000); continue; } /* setup client for reading incoming http */ client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] = '\000'; if (sock_set_blocking (client->con->sock, 0) || sock_set_nodelay (client->con->sock)) { global_unlock(); WARN0 ("failed to set tcp options on client connection, dropping"); client_destroy (client); continue; } node = calloc (1, sizeof (client_queue_t)); if (node == NULL) { global_unlock(); client_destroy (client); continue; } node->client = client; config = config_get_config(); listener = config_get_listen_sock (config, client->con); if (listener) { if (listener->shoutcast_compat) node->shoutcast = 1; if (listener->ssl && ssl_ok) connection_uses_ssl (client->con); if (listener->shoutcast_mount) node->shoutcast_mount = strdup (listener->shoutcast_mount); } global_unlock(); config_release_config(); _add_request_queue (node); stats_event_inc (NULL, "connections"); duration = 5; } else { if (_req_queue == NULL) duration = 300; /* use longer timeouts when nothing waiting */ } process_request_queue (); } /* Give all the other threads notification to shut down */ thread_cond_broadcast(&global.shutdown_cond); /* wait for all the sources to shutdown */ thread_rwlock_wlock(&_source_shutdown_rwlock); thread_rwlock_unlock(&_source_shutdown_rwlock); }
/* Called when activating a source. Verifies that the source count is not * exceeded and applies any initial parameters. */ int connection_complete_source (source_t *source, int response) { ice_config_t *config = config_get_config(); global_lock (); DEBUG1 ("sources count is %d", global.sources); if (global.sources < config->source_limit) { char *contenttype; mount_proxy *mountinfo; format_type_t format_type; /* setup format handler */ contenttype = httpp_getvar (source->parser, "content-type"); if (contenttype != NULL) { format_type = format_get_type (contenttype); if (format_type == FORMAT_ERROR) { global_unlock(); config_release_config(); if (response) { client_send_403 (source->client, "Content-type not supported"); source->client = NULL; } WARN1("Content-type \"%s\" not supported, dropping source", contenttype); return -1; } } else { WARN0("No content-type header, falling back to backwards compatibility mode " "for icecast 1.x relays. Assuming content is mp3."); format_type = FORMAT_TYPE_GENERIC; } if (format_get_plugin (format_type, source) < 0) { global_unlock(); config_release_config(); if (response) { client_send_403 (source->client, "internal format allocation problem"); source->client = NULL; } WARN1 ("plugin format failed for \"%s\"", source->mount); return -1; } global.sources++; stats_event_args (NULL, "sources", "%d", global.sources); global_unlock(); source->running = 1; mountinfo = config_find_mount (config, source->mount); if (mountinfo == NULL) source_update_settings (config, source, mountinfo); source_recheck_mounts (); config_release_config(); source->shutdown_rwlock = &_source_shutdown_rwlock; DEBUG0 ("source is ready to start"); return 0; } WARN1("Request to add source when maximum source limit " "reached %d", global.sources); global_unlock(); config_release_config(); if (response) { client_send_403 (source->client, "too many sources connected"); source->client = NULL; } return -1; }
void connection_accept_loop(void) { connection_t *con; if (!kitsune_is_updating()) /**DSU control */ tid = thread_create("connection thread", _handle_connection, NULL, THREAD_ATTACHED); while (global.running == ICE_RUNNING) { kitsune_update("connection_accept"); /**DSU updatepoint */ con = _accept_connection(); if (con) { client_queue_t *node; ice_config_t *config; int i; client_t *client = NULL; global_lock(); if (client_create (&client, con, NULL) < 0) { global_unlock(); client_send_403 (client, "Icecast connection limit reached"); continue; } global_unlock(); /* setup client for reading incoming http */ client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] = '\000'; node = calloc (1, sizeof (client_queue_t)); if (node == NULL) { client_destroy (client); continue; } node->client = client; /* Check for special shoutcast compatability processing */ config = config_get_config(); for (i = 0; i < global.server_sockets; i++) { if (global.serversock[i] == con->serversock) { if (config->listeners[i].shoutcast_compat) node->shoutcast = 1; } } config_release_config(); sock_set_blocking (client->con->sock, SOCK_NONBLOCK); sock_set_nodelay (client->con->sock); _add_request_queue (node); stats_event_inc (NULL, "connections"); } process_request_queue (); } /* Give all the other threads notification to shut down */ thread_cond_broadcast(&global.shutdown_cond); if (tid) thread_join (tid); /* wait for all the sources to shutdown */ thread_rwlock_wlock(&_source_shutdown_rwlock); thread_rwlock_unlock(&_source_shutdown_rwlock); }
int fserve_setup_client_fb (client_t *client, fbinfo *finfo) { fh_node *fh = &no_file; int ret = 0; if (finfo) { mount_proxy *minfo; if (finfo->flags & FS_FALLBACK && finfo->limit == 0) return -1; avl_tree_wlock (fh_cache); fh = find_fh (finfo); minfo = config_find_mount (config_get_config(), finfo->mount); if (fh) { thread_mutex_lock (&fh->lock); avl_tree_unlock (fh_cache); client->shared_data = NULL; if (minfo) { if (minfo->max_listeners >= 0 && fh->refcount > minfo->max_listeners) { thread_mutex_unlock (&fh->lock); config_release_config(); return client_send_403redirect (client, finfo->mount, "max listeners reached"); } if (check_duplicate_logins (finfo->mount, fh->clients, client, minfo->auth) == 0) { thread_mutex_unlock (&fh->lock); config_release_config(); return client_send_403 (client, "Account already in use"); } } config_release_config(); } else { if (minfo && minfo->max_listeners == 0) { avl_tree_unlock (fh_cache); config_release_config(); client->shared_data = NULL; return client_send_403redirect (client, finfo->mount, "max listeners reached"); } config_release_config(); fh = open_fh (finfo); if (fh == NULL) return client_send_404 (client, NULL); } if (fh->finfo.limit) { client->timer_start = client->worker->current_time.tv_sec; if (client->connection.sent_bytes == 0) client->timer_start -= 2; client->counter = 0; client->intro_offset = 0; global_reduce_bitrate_sampling (global.out_bitrate); } } else thread_mutex_lock (&fh->lock); client->mount = fh->finfo.mount; if (fh->finfo.type == FORMAT_TYPE_UNDEFINED) { if (client->respcode == 0) { client->refbuf->len = 0; ret = format_general_headers (fh->format, client); } } else { if (fh->format->create_client_data && client->format_data == NULL) ret = fh->format->create_client_data (fh->format, client); if (fh->format->write_buf_to_client) client->check_buffer = fh->format->write_buf_to_client; } if (ret < 0) { thread_mutex_unlock (&fh->lock); return client_send_416 (client); } fh_add_client (fh, client); thread_mutex_unlock (&fh->lock); client->shared_data = fh; if (client->check_buffer == NULL) client->check_buffer = format_generic_write_to_client; client->ops = &buffer_content_ops; client->flags &= ~CLIENT_HAS_INTRO_CONTENT; client->flags |= CLIENT_IN_FSERVE; if (client->flags & CLIENT_ACTIVE) { client->schedule_ms = client->worker->time_ms; if (finfo && finfo->flags & FS_FALLBACK) return 0; // prevent a recursive loop return client->ops->process (client); } else { worker_t *worker = client->worker; client->flags |= CLIENT_ACTIVE; worker_wakeup (worker); /* worker may of already processed client but make sure */ } return 0; }
void connection_accept_loop(void) { connection_t *con; ice_config_t *config; config = config_get_config (); get_ssl_certificate (config); config_release_config (); tid = thread_create ("connection thread", _handle_connection, NULL, THREAD_ATTACHED); while (global.running == ICE_RUNNING) { con = _accept_connection(); if (con) { client_queue_t *node; ice_config_t *config; client_t *client = NULL; listener_t *listener; global_lock(); if (client_create (&client, con, NULL) < 0) { global_unlock(); client_send_403 (client, "Icecast connection limit reached"); /* don't be too eager as this is an imposed hard limit */ thread_sleep (400000); continue; } global_unlock(); /* setup client for reading incoming http */ client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] = '\000'; node = calloc (1, sizeof (client_queue_t)); if (node == NULL) { client_destroy (client); continue; } node->client = client; config = config_get_config(); listener = config_get_listen_sock (config, client->con); if (listener) { if (listener->shoutcast_compat) node->shoutcast = 1; if (listener->ssl && ssl_ok) connection_uses_ssl (client->con); if (listener->shoutcast_mount) node->shoutcast_mount = strdup (listener->shoutcast_mount); } config_release_config(); sock_set_blocking (client->con->sock, SOCK_NONBLOCK); sock_set_nodelay (client->con->sock); _add_request_queue (node); stats_event_inc (NULL, "connections"); } process_request_queue (); } /* Give all the other threads notification to shut down */ thread_cond_broadcast(&global.shutdown_cond); if (tid) thread_join (tid); /* wait for all the sources to shutdown */ thread_rwlock_wlock(&_source_shutdown_rwlock); thread_rwlock_unlock(&_source_shutdown_rwlock); }
/* Add a listener. Check for any mount information that states any * authentication to be used. */ int auth_add_listener (const char *mount, client_t *client) { int ret = 0; ice_config_t *config = config_get_config(); mount_proxy *mountinfo = config_find_mount (config, mount); if ((client->flags & CLIENT_AUTHENTICATED) == 0) { if (mountinfo) { auth_t *auth = mountinfo->auth; if (mountinfo->skip_accesslog) client->flags |= CLIENT_SKIP_ACCESSLOG; if (mountinfo->ban_client) { if (mountinfo->ban_client < 0) client->flags |= CLIENT_IP_BAN_LIFT; connection_add_banned_ip (client->connection.ip, mountinfo->ban_client); } if (mountinfo->no_mount) { config_release_config (); return client_send_403 (client, "mountpoint unavailable"); } if (mountinfo->redirect) { int len = strlen (mountinfo->redirect) + strlen (mount) + 3; char *addr = alloca (len); snprintf (addr, len, "%s%s", mountinfo->redirect, mount); config_release_config (); return client_send_302 (client, addr); } do { if (auth == NULL) break; if ((auth->flags & AUTH_RUNNING) == 0) break; if (auth->pending_count > 400) { if (auth->flags & AUTH_SKIP_IF_SLOW) break; config_release_config (); WARN0 ("too many clients awaiting authentication"); if (global.new_connections_slowdown < 10) global.new_connections_slowdown++; return client_send_403 (client, "busy, please try again later"); } if (auth->authenticate) { auth_client *auth_user = auth_client_setup (mount, client); auth_user->process = auth_new_listener; client->flags &= ~CLIENT_ACTIVE; DEBUG0 ("adding client for authentication"); queue_auth_client (auth_user, mountinfo); config_release_config (); return 0; } } while (0); } else { if (strcmp (mount, "/admin/streams") == 0) { config_release_config (); return client_send_401 (client, NULL); } } } ret = add_authenticated_listener (mount, mountinfo, client); config_release_config (); return ret; }
static auth_result url_add_listener (auth_client *auth_user) { client_t *client = auth_user->client; auth_t *auth = auth_user->auth; auth_url *url = auth->state; auth_thread_data *atd = auth_user->thread_data; int res = 0, ret = AUTH_FAILED, poffset = 0; struct build_intro_contents *x; char *userpwd = NULL, post [8192]; if (url->addurl == NULL || client == NULL) return AUTH_OK; if (url->stop_req_until) { time_t now = time(NULL); if (url->stop_req_until <= now) { INFO1 ("restarting url after timeout on %s", auth_user->mount); url->stop_req_until = 0; } else { if (auth->flags & AUTH_SKIP_IF_SLOW) { client->flags |= CLIENT_AUTHENTICATED; return AUTH_OK; } return AUTH_FAILED; } } do { ice_config_t *config = config_get_config (); char *user_agent, *username, *password, *mount, *ipaddr, *referer, *current_listeners, *server = util_url_escape (config->hostname); int port = config->port; config_release_config (); const char *tmp = httpp_getvar (client->parser, "user-agent"); if (tmp == NULL) tmp = "-"; user_agent = util_url_escape (tmp); if (client->username) username = util_url_escape (client->username); else username = strdup (""); if (client->password) password = util_url_escape (client->password); else password = strdup (""); /* get the full uri (with query params if available) */ tmp = httpp_getvar (client->parser, HTTPP_VAR_QUERYARGS); snprintf (post, sizeof post, "%s%s", auth_user->mount, tmp ? tmp : ""); mount = util_url_escape (post); ipaddr = util_url_escape (client->connection.ip); tmp = httpp_getvar (client->parser, "referer"); referer = tmp ? util_url_escape (tmp) : strdup (""); current_listeners = stats_get_value(auth->mount, "listeners"); if (current_listeners == NULL) current_listeners = strdup(""); poffset = snprintf (post, sizeof (post), "action=listener_add&server=%s&port=%d&client=%" PRIu64 "&mount=%s" "&user=%s&pass=%s&ip=%s&agent=%s&referer=%s&listeners=%s", server, port, client->connection.id, mount, username, password, ipaddr, user_agent, referer, current_listeners); free (current_listeners); free (server); free (mount); free (referer); free (user_agent); free (username); free (password); free (ipaddr); if (poffset < 0 || poffset >= sizeof (post)) { WARN2 ("client from %s (on %s), rejected with headers problem", &client->connection.ip[0], auth_user->mount); return AUTH_FAILED; } } while (0); if (url->header_chk_list) { int c = url->header_chk_count, remaining = sizeof(post) - poffset; char *cur_header = url->header_chk_list; const char *prefix = (url->header_chk_prefix && isalnum (url->header_chk_prefix[0])) ? url->header_chk_prefix : "ClientHeader-"; for (; c ; c--) { int len = strlen (cur_header); const char *val = httpp_getvar (client->parser, cur_header); if (val) { char *valesc = util_url_escape (val); int r = remaining > 0 ? snprintf (post+poffset, remaining, "&%s%s=%s", prefix, cur_header, valesc) : -1; free (valesc); if (r < 0 || r > remaining) { WARN2 ("client from %s (on %s), rejected with too much in headers", &client->connection.ip[0], auth_user->mount); return AUTH_FAILED; } poffset += r; remaining -= r; } cur_header += (len + 1); // get past next nul } } if (strchr (url->addurl, '@') == NULL) { if (url->userpwd) curl_easy_setopt (atd->curl, CURLOPT_USERPWD, url->userpwd); else { /* auth'd requests may not have a user/pass, but may use query args */ if (client->username && client->password) { int len = strlen (client->username) + strlen (client->password) + 2; userpwd = malloc (len); snprintf (userpwd, len, "%s:%s", client->username, client->password); curl_easy_setopt (atd->curl, CURLOPT_USERPWD, userpwd); } else curl_easy_setopt (atd->curl, CURLOPT_USERPWD, ""); } } else { /* url has user/pass but libcurl may need to clear any existing settings */ curl_easy_setopt (atd->curl, CURLOPT_USERPWD, ""); } curl_easy_setopt (atd->curl, CURLOPT_URL, url->addurl); curl_easy_setopt (atd->curl, CURLOPT_POSTFIELDS, post); curl_easy_setopt (atd->curl, CURLOPT_WRITEHEADER, auth_user); curl_easy_setopt (atd->curl, CURLOPT_WRITEDATA, auth_user); atd->errormsg[0] = '\0'; free (atd->location); atd->location = NULL; /* setup in case intro data is returned */ x = (void *)client->refbuf->data; x->type = 0; x->head = NULL; x->intro_len = 0; x->tailp = &x->head; DEBUG2 ("handler %d (%s) sending request", auth_user->handler, auth_user->mount); res = curl_easy_perform (atd->curl); DEBUG2 ("handler %d (%s) request finished", auth_user->handler, auth_user->mount); free (userpwd); if (client->flags & CLIENT_AUTHENTICATED) { if (client->flags & CLIENT_HAS_INTRO_CONTENT) { client->refbuf->next = x->head; DEBUG3 ("intro (%d) received %lu for %s", x->type, (unsigned long)x->intro_len, client->connection.ip); } if (x->head == NULL) client->flags &= ~CLIENT_HAS_INTRO_CONTENT; x->head = NULL; ret = AUTH_OK; } if (res) { url->stop_req_until = time (NULL) + url->stop_req_duration; /* prevent further attempts for a while */ WARN3 ("auth to server %s (%s) failed with %s", url->addurl, auth_user->mount, atd->errormsg); INFO1 ("will not auth new listeners for %d seconds", url->stop_req_duration); if (auth->flags & AUTH_SKIP_IF_SLOW) { client->flags |= CLIENT_AUTHENTICATED; ret = AUTH_OK; } } /* better cleanup memory */ while (x->head) { refbuf_t *n = x->head; x->head = n->next; n->next = NULL; refbuf_release (n); } if (x->type) mpeg_cleanup (&x->sync); if (atd->location) { client_send_302 (client, atd->location); auth_user->client = NULL; free (atd->location); atd->location = NULL; } else if (atd->errormsg[0]) { INFO3 ("listener %s (%s) returned \"%s\"", client->connection.ip, url->addurl, atd->errormsg); if (atoi (atd->errormsg) == 403) { auth_user->client = NULL; client_send_403 (client, atd->errormsg+4); } } return ret; }
static void _handle_get_request(connection_t *con, http_parser_t *parser, char *passed_uri) { char *fullpath; client_t *client; int bytes; struct stat statbuf; source_t *source; int fileserve; char *host; int port; int i; char *serverhost = NULL; int serverport = 0; aliases *alias; ice_config_t *config; int client_limit; int ret; char *uri = passed_uri; config = config_get_config(); fileserve = config->fileserve; host = config->hostname; port = config->port; for(i = 0; i < MAX_LISTEN_SOCKETS; i++) { if(global.serversock[i] == con->serversock) { serverhost = config->listeners[i].bind_address; serverport = config->listeners[i].port; break; } } alias = config->aliases; client_limit = config->client_limit; /* there are several types of HTTP GET clients ** media clients, which are looking for a source (eg, URI = /stream.ogg) ** stats clients, which are looking for /admin/stats.xml ** and directory server authorizers, which are looking for /GUID-xxxxxxxx ** (where xxxxxx is the GUID in question) - this isn't implemented yet. ** we need to handle the latter two before the former, as the latter two ** aren't subject to the limits. */ /* TODO: add GUID-xxxxxx */ /* Handle aliases */ while(alias) { if(strcmp(uri, alias->source) == 0 && (alias->port == -1 || alias->port == serverport) && (alias->bind_address == NULL || (serverhost != NULL && strcmp(alias->bind_address, serverhost) == 0))) { uri = strdup (alias->destination); DEBUG2 ("alias has made %s into %s", passed_uri, uri); break; } alias = alias->next; } config_release_config(); /* make a client */ client = client_create(con, parser); stats_event_inc(NULL, "client_connections"); /* Dispatch all admin requests */ if (strncmp(uri, "/admin/", 7) == 0) { admin_handle_request(client, uri); if (uri != passed_uri) free (uri); return; } /* Here we are parsing the URI request to see ** if the extension is .xsl, if so, then process ** this request as an XSLT request */ fullpath = util_get_path_from_normalised_uri(uri); if (util_check_valid_extension(fullpath) == XSLT_CONTENT) { /* If the file exists, then transform it, otherwise, write a 404 */ if (stat(fullpath, &statbuf) == 0) { DEBUG0("Stats request, sending XSL transformed stats"); client->respcode = 200; bytes = sock_write(client->con->sock, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"); if(bytes > 0) client->con->sent_bytes = bytes; stats_transform_xslt(client, fullpath); client_destroy(client); } else { client_send_404(client, "The file you requested could not be found"); } free(fullpath); if (uri != passed_uri) free (uri); return; } else if(fileserve && stat(fullpath, &statbuf) == 0 && #ifdef _WIN32 ((statbuf.st_mode) & _S_IFREG)) #else S_ISREG(statbuf.st_mode)) #endif { fserve_client_create(client, fullpath); free(fullpath); if (uri != passed_uri) free (uri); return; } free(fullpath); if(strcmp(util_get_extension(uri), "m3u") == 0) { char *sourceuri = strdup(uri); char *dot = strrchr(sourceuri, '.'); *dot = 0; client->respcode = 200; bytes = sock_write(client->con->sock, "HTTP/1.0 200 OK\r\n" "Content-Type: audio/x-mpegurl\r\n\r\n" "http://%s:%d%s\r\n", host, port, sourceuri ); if(bytes > 0) client->con->sent_bytes = bytes; client_destroy(client); free(sourceuri); if (uri != passed_uri) free (uri); return; } global_lock(); if (global.clients >= client_limit) { global_unlock(); client_send_404(client, "The server is already full. Try again later."); if (uri != passed_uri) free (uri); return; } global_unlock(); avl_tree_rlock(global.source_tree); source = source_find_mount(uri); if (source) { DEBUG0("Source found for client"); /* The source may not be the requested source - it might have gone * via one or more fallbacks. We only reject it for no-mount if it's * the originally requested source */ if(strcmp(uri, source->mount) == 0 && source->no_mount) { avl_tree_unlock(global.source_tree); client_send_404(client, "This mount is unavailable."); if (uri != passed_uri) free (uri); return; } if (source->running == 0) { avl_tree_unlock(global.source_tree); DEBUG0("inactive source, client dropped"); client_send_404(client, "This mount is unavailable."); if (uri != passed_uri) free (uri); return; } /* Check for any required authentication first */ if(source->authenticator != NULL) { ret = auth_check_client(source, client); if(ret != AUTH_OK) { avl_tree_unlock(global.source_tree); if (ret == AUTH_FORBIDDEN) { INFO1("Client attempted to log multiple times to source " "(\"%s\")", uri); client_send_403(client); } else { /* If not FORBIDDEN, default to 401 */ INFO1("Client attempted to log in to source (\"%s\")with " "incorrect or missing password", uri); client_send_401(client); } if (uri != passed_uri) free (uri); return; } } /* And then check that there's actually room in the server... */ global_lock(); if (global.clients >= client_limit) { global_unlock(); avl_tree_unlock(global.source_tree); client_send_404(client, "The server is already full. Try again later."); if (uri != passed_uri) free (uri); return; } /* Early-out for per-source max listeners. This gets checked again * by the source itself, later. This route gives a useful message to * the client, also. */ else if(source->max_listeners != -1 && source->listeners >= source->max_listeners) { global_unlock(); avl_tree_unlock(global.source_tree); client_send_404(client, "Too many clients on this mountpoint. Try again later."); if (uri != passed_uri) free (uri); return; } global.clients++; global_unlock(); source->format->create_client_data (source, client); source->format->client_send_headers(source->format, source, client); bytes = sock_write(client->con->sock, "\r\n"); if(bytes > 0) client->con->sent_bytes += bytes; sock_set_blocking(client->con->sock, SOCK_NONBLOCK); sock_set_nodelay(client->con->sock); avl_tree_wlock(source->pending_tree); avl_insert(source->pending_tree, (void *)client); avl_tree_unlock(source->pending_tree); } avl_tree_unlock(global.source_tree); if (!source) { DEBUG0("Source not found for client"); client_send_404(client, "The source you requested could not be found."); } if (uri != passed_uri) free (uri); }