/* call this to verify that the HTTP data has been sent and if so setup * callbacks to the appropriate format functions */ int format_check_http_buffer(source_t *source, client_t *client) { refbuf_t *refbuf = client->refbuf; if (refbuf == NULL) return -1; if (client->respcode == 0) { ICECAST_LOG_DEBUG("processing pending client headers"); if (format_prepare_headers (source, client) < 0) { ICECAST_LOG_ERROR("internal problem, dropping client"); client->con->error = 1; return -1; } client->respcode = 200; stats_event_inc(NULL, "listeners"); stats_event_inc(NULL, "listener_connections"); stats_event_inc(source->mount, "listener_connections"); } if (client->pos == refbuf->len) { client->write_to_client = source->format->write_buf_to_client; client->check_buffer = format_check_file_buffer; client->intro_offset = 0; client->pos = refbuf->len = 4096; return -1; } return 0; }
void *source_client_thread (void *arg) { source_t *source = arg; const char ok_msg[] = "HTTP/1.0 200 OK\r\n\r\n"; int bytes; source->client->respcode = 200; bytes = sock_write_bytes (source->client->con->sock, ok_msg, sizeof (ok_msg)-1); if (bytes < sizeof (ok_msg)-1) { global_lock(); global.sources--; global_unlock(); WARN0 ("Error writing 200 OK message to source client"); } else { source->client->con->sent_bytes += bytes; stats_event_inc(NULL, "source_client_connections"); source_main (source); } source_free_source (source); return NULL; }
static void wait_for_fds() { fserve_t *fclient; while (run_fserv) { /* add any new clients here */ if (pending_list) { thread_mutex_lock (&pending_lock); fclient = (fserve_t*)pending_list; while (fclient) { fserve_t *to_move = fclient; fclient = fclient->next; to_move->next = active_list; active_list = to_move; client_tree_changed = 1; fserve_clients++; stats_event_inc(NULL, "clients"); } pending_list = NULL; thread_mutex_unlock (&pending_lock); } /* drop out of here is someone is ready */ if (fserve_client_waiting()) break; } }
static void _handle_get_request (client_t *client, char *passed_uri) { int port; char *serverhost = NULL; int serverport = 0; aliases *alias; ice_config_t *config; char *uri = passed_uri; listener_t *listen_sock; config = config_get_config(); port = config->port; listen_sock = config_get_listen_sock (config, client->con); if (listen_sock) { serverhost = listen_sock->bind_address; serverport = listen_sock->port; } alias = config->aliases; /* 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(); stats_event_inc(NULL, "client_connections"); /* Dispatch all admin requests */ if ((strcmp(uri, "/admin.cgi") == 0) || (strncmp(uri, "/admin/", 7) == 0)) { admin_handle_request(client, uri); if (uri != passed_uri) free (uri); return; } auth_add_listener (uri, client); if (uri != passed_uri) free (uri); }
/* general send routine per listener. The deletion_expected tells us whether * the last in the queue is about to disappear, so if this client is still * referring to it after writing then drop the client as it's fallen too far * behind */ static void send_to_listener (source_t *source, client_t *client, int deletion_expected) { int bytes; int loop = 10; /* max number of iterations in one go */ int total_written = 0; while (1) { /* check for limited listener time */ if (client->con->discon_time) if (time(NULL) >= client->con->discon_time) { INFO1 ("time limit reached for client #%lu", client->con->id); client->con->error = 1; } /* jump out if client connection has died */ if (client->con->error) break; /* lets not send too much to one client in one go, but don't sleep for too long if more data can be sent */ if (total_written > 20000 || loop == 0) { if (client->check_buffer != format_check_file_buffer) source->short_delay = 1; break; } loop--; if (client->check_buffer (source, client) < 0) break; bytes = client->write_to_client (client); if (bytes <= 0) break; /* can't write any more */ total_written += bytes; } source->format->sent_bytes += total_written; /* the refbuf referenced at head (last in queue) may be marked for deletion * if so, check to see if this client is still referring to it */ if (deletion_expected && client->refbuf && client->refbuf == source->stream_data) { INFO2 ("Client %lu (%s) has fallen too far behind, removing", client->con->id, client->con->ip); stats_event_inc (source->mount, "slow_listeners"); client->con->error = 1; } }
static void _handle_stats_request(connection_t *con, http_parser_t *parser, char *uri) { stats_connection_t *stats; stats_event_inc(NULL, "stats_connections"); if (!connection_check_admin_pass(parser)) { ERROR0("Bad password for stats connection"); connection_close(con); httpp_destroy(parser); return; } stats_event_inc(NULL, "stats"); /* create stats connection and create stats handler thread */ stats = (stats_connection_t *)malloc(sizeof(stats_connection_t)); stats->parser = parser; stats->con = con; thread_create("Stats Connection", stats_connection, (void *)stats, THREAD_DETACHED); }
void *source_client_thread (void *arg) { source_t *source = arg; stats_event_inc(NULL, "source_client_connections"); stats_event (source->mount, "listeners", "0"); source_main (source); source_free_source (source); slave_update_all_mounts(); return NULL; }
static void _handle_stats_request (client_t *client, char *uri) { stats_event_inc(NULL, "stats_connections"); if (connection_check_admin_pass (client->parser) == 0) { client_send_401 (client); ERROR0("Bad password for stats connection"); return; } client->respcode = 200; snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE, "HTTP/1.0 200 OK\r\n\r\n"); client->refbuf->len = strlen (client->refbuf->data); fserve_add_client_callback (client, stats_callback, NULL); }
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); }
static void *_handle_connection(void *arg) { char header[4096]; connection_t *con; http_parser_t *parser; char *rawuri, *uri; client_t *client; while (global.running == ICE_RUNNING) { /* grab a connection and set the socket to blocking */ while ((con = _get_connection())) { /* Handle meta-connections */ if(con->event_number > 0) { switch(con->event_number) { case EVENT_CONFIG_READ: event_config_read(con->event); break; default: ERROR1("Unknown event number: %d", con->event_number); break; } free(con); continue; } stats_event_inc(NULL, "connections"); sock_set_blocking(con->sock, SOCK_BLOCK); /* fill header with the http header */ memset(header, 0, sizeof (header)); if (util_read_header(con->sock, header, sizeof (header)) == 0) { /* either we didn't get a complete header, or we timed out */ connection_close(con); continue; } parser = httpp_create_parser(); httpp_initialize(parser, NULL); if (httpp_parse(parser, header, strlen(header))) { /* handle the connection or something */ if (strcmp("ICE", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) && strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) { ERROR0("Bad HTTP protocol detected"); connection_close(con); httpp_destroy(parser); continue; } rawuri = httpp_getvar(parser, HTTPP_VAR_URI); uri = util_normalise_uri(rawuri); if(!uri) { client = client_create(con, parser); client_send_404(client, "The path you requested was invalid"); continue; } if (parser->req_type == httpp_req_source) { _handle_source_request(con, parser, uri); } else if (parser->req_type == httpp_req_stats) { _handle_stats_request(con, parser, uri); } else if (parser->req_type == httpp_req_get) { _handle_get_request(con, parser, uri); } else { ERROR0("Wrong request type from client"); connection_close(con); httpp_destroy(parser); } free(uri); continue; } else if(httpp_parse_icy(parser, header, strlen(header))) { /* TODO: Map incoming icy connections to /icy_0, etc. */ char mount[20]; unsigned i = 0; strcpy(mount, "/"); avl_tree_rlock(global.source_tree); while (source_find_mount (mount) != NULL) { snprintf (mount, sizeof (mount), "/icy_%u", i++); } avl_tree_unlock(global.source_tree); _handle_source_request(con, parser, mount); continue; } else { ERROR0("HTTP request parsing failed"); connection_close(con); httpp_destroy(parser); continue; } } thread_sleep (100000); } DEBUG0 ("Connection thread done"); return NULL; }
static void _handle_get_request (client_t *client, char *passed_uri) { int fileserve; int port; int i; char *serverhost = NULL; int serverport = 0; aliases *alias; ice_config_t *config; char *uri = passed_uri; config = config_get_config(); fileserve = config->fileserve; port = config->port; for(i = 0; i < global.server_sockets; i++) { if(global.serversock[i] == client->con->serversock) { serverhost = config->listeners[i].bind_address; serverport = config->listeners[i].port; break; } } alias = config->aliases; /* 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(); stats_event_inc(NULL, "client_connections"); /* Dispatch all admin requests */ if ((strcmp(uri, "/admin.cgi") == 0) || (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 */ if (util_check_valid_extension (uri) == XSLT_CONTENT) { /* If the file exists, then transform it, otherwise, write a 404 */ DEBUG0("Stats request, sending XSL transformed stats"); stats_transform_xslt (client, uri); if (uri != passed_uri) free (uri); return; } add_client (uri, client); if (uri != passed_uri) free (uri); }
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_404 (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); }
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); }
/* client has requested a file, so check for it and send the file. Do not * refer to the client_t afterwards. return 0 for success, -1 on error. */ int fserve_client_create (client_t *httpclient) { int bytes; struct stat file_buf; const char *range = NULL; off_t new_content_len = 0; off_t rangenumber = 0, content_length; int rangeproblem = 0; int ret = 0; char *fullpath; int m3u_requested = 0, m3u_file_available = 1; const char * xslt_playlist_requested = NULL; int xslt_playlist_file_available = 1; ice_config_t *config; FILE *file; fullpath = util_get_path_from_normalised_uri(httpclient->uri); ICECAST_LOG_INFO("checking for file %H (%H)", httpclient->uri, fullpath); if (strcmp (util_get_extension (fullpath), "m3u") == 0) m3u_requested = 1; if (strcmp (util_get_extension (fullpath), "xspf") == 0) xslt_playlist_requested = "xspf.xsl"; if (strcmp (util_get_extension (fullpath), "vclt") == 0) xslt_playlist_requested = "vclt.xsl"; /* check for the actual file */ if (stat (fullpath, &file_buf) != 0) { /* the m3u can be generated, but send an m3u file if available */ if (m3u_requested == 0 && xslt_playlist_requested == NULL) { ICECAST_LOG_WARN("req for file \"%H\" %s", fullpath, strerror (errno)); client_send_error_by_id(httpclient, ICECAST_ERROR_FSERV_FILE_NOT_FOUND); free (fullpath); return -1; } m3u_file_available = 0; xslt_playlist_file_available = 0; } httpclient->refbuf->len = PER_CLIENT_REFBUF_SIZE; if (m3u_requested && m3u_file_available == 0) { char *sourceuri = strdup(httpclient->uri); char *dot = strrchr(sourceuri, '.'); *dot = 0; httpclient->respcode = 200; ret = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0, 0, 200, NULL, "audio/x-mpegurl", NULL, "", NULL, httpclient); if (ret == -1 || ret >= (BUFSIZE - 512)) { /* we want at least 512 bytes left for the content of the playlist */ ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); client_send_error_by_id(httpclient, ICECAST_ERROR_GEN_HEADER_GEN_FAILED); free(sourceuri); return -1; } client_get_baseurl(httpclient, NULL, httpclient->refbuf->data + ret, BUFSIZE - ret, NULL, NULL, NULL, sourceuri, "\r\n"); httpclient->refbuf->len = strlen (httpclient->refbuf->data); fserve_add_client (httpclient, NULL); free (sourceuri); free (fullpath); return 0; } if (xslt_playlist_requested && xslt_playlist_file_available == 0) { xmlDocPtr doc; char *reference = strdup(httpclient->uri); char *eol = strrchr (reference, '.'); if (eol) *eol = '\0'; doc = stats_get_xml (0, reference, httpclient); free (reference); admin_send_response (doc, httpclient, ADMIN_FORMAT_HTML, xslt_playlist_requested); xmlFreeDoc(doc); free (fullpath); return 0; } /* on demand file serving check */ config = config_get_config(); if (config->fileserve == 0) { ICECAST_LOG_DEBUG("on demand file \"%H\" refused. Serving static files has been disabled in the config", fullpath); client_send_error_by_id(httpclient, ICECAST_ERROR_FSERV_FILE_NOT_FOUND); config_release_config(); free(fullpath); return -1; } config_release_config(); if (S_ISREG (file_buf.st_mode) == 0) { client_send_error_by_id(httpclient, ICECAST_ERROR_FSERV_FILE_NOT_FOUND); ICECAST_LOG_WARN("found requested file but there is no handler for it: %H", fullpath); free (fullpath); return -1; } file = fopen (fullpath, "rb"); if (file == NULL) { ICECAST_LOG_WARN("Problem accessing file \"%H\"", fullpath); client_send_error_by_id(httpclient, ICECAST_ERROR_FSERV_FILE_NOT_READABLE); free (fullpath); return -1; } free (fullpath); content_length = file_buf.st_size; range = httpp_getvar (httpclient->parser, "range"); /* full http range handling is currently not done but we deal with the common case */ if (range != NULL) { ret = 0; if (strncasecmp (range, "bytes=", 6) == 0) ret = sscanf (range+6, "%" SCN_OFF_T "-", &rangenumber); if (ret != 1) { /* format not correct, so lets just assume we start from the beginning */ rangeproblem = 1; } if (rangenumber < 0) { rangeproblem = 1; } if (!rangeproblem) { ret = fseeko (file, rangenumber, SEEK_SET); if (ret != -1) { new_content_len = content_length - rangenumber; if (new_content_len < 0) { rangeproblem = 1; } } else { rangeproblem = 1; } if (!rangeproblem) { off_t endpos = rangenumber+new_content_len-1; char *type; if (endpos < 0) { endpos = 0; } httpclient->respcode = 206; type = fserve_content_type(httpclient->uri); bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0, 0, 206, NULL, type, NULL, NULL, NULL, httpclient); if (bytes == -1 || bytes >= (BUFSIZE - 512)) { /* we want at least 512 bytes left */ ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); client_send_error_by_id(httpclient, ICECAST_ERROR_GEN_HEADER_GEN_FAILED); return -1; } bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes, "Accept-Ranges: bytes\r\n" "Content-Length: %" PRI_OFF_T "\r\n" "Content-Range: bytes %" PRI_OFF_T \ "-%" PRI_OFF_T "/%" PRI_OFF_T "\r\n\r\n", new_content_len, rangenumber, endpos, content_length); free (type); } else { goto fail; } } else { goto fail; } } else { char *type = fserve_content_type(httpclient->uri); httpclient->respcode = 200; bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0, 0, 200, NULL, type, NULL, NULL, NULL, httpclient); if (bytes == -1 || bytes >= (BUFSIZE - 512)) { /* we want at least 512 bytes left */ ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); client_send_error_by_id(httpclient, ICECAST_ERROR_GEN_HEADER_GEN_FAILED); fclose(file); return -1; } bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes, "Accept-Ranges: bytes\r\n" "Content-Length: %" PRI_OFF_T "\r\n\r\n", content_length); free (type); } httpclient->refbuf->len = bytes; httpclient->pos = 0; stats_event_inc (NULL, "file_connections"); fserve_add_client (httpclient, file); return 0; fail: fclose (file); client_send_error_by_id(httpclient, ICECAST_ERROR_FSERV_REQUEST_RANGE_NOT_SATISFIABLE); return -1; }
static void source_init (source_t *source) { ice_config_t *config = config_get_config(); char *listenurl, *str; int listen_url_size; char *s; /* 6 for max size of port */ listen_url_size = strlen("http://") + strlen(config->hostname) + strlen(":") + 6 + strlen(source->mount) + 1; listenurl = malloc (listen_url_size); memset (listenurl, '\000', listen_url_size); snprintf (listenurl, listen_url_size, "http://%s:%d%s", config->hostname, config->port, source->mount); config_release_config(); do { str = "0"; if (source->yp_prevent) break; if ((str = httpp_getvar(source->parser, "ice-public"))) break; if ((str = httpp_getvar(source->parser, "icy-pub"))) break; /* handle header from icecast v2 release */ if ((str = httpp_getvar(source->parser, "icy-public"))) break; str = "0"; } while (0); source->yp_public = atoi (str); stats_event (source->mount, "public", str); str = httpp_getvar(source->parser, "ice-audio-info"); source->audio_info = util_dict_new(); if (str) { _parse_audio_info (source, str); stats_event (source->mount, "audio_info", str); } stats_event (source->mount, "listenurl", listenurl); if (listenurl) { free(listenurl); } if (source->dumpfilename != NULL) { source->dumpfile = fopen (source->dumpfilename, "ab"); if (source->dumpfile == NULL) { WARN2("Cannot open dump file \"%s\" for appending: %s, disabling.", source->dumpfilename, strerror(errno)); } } /* grab a read lock, to make sure we get a chance to cleanup */ thread_rwlock_rlock (source->shutdown_rwlock); /* start off the statistics */ source->listeners = 0; stats_event_inc (NULL, "sources"); stats_event_inc (NULL, "source_total_connections"); stats_event (source->mount, "listeners", "0"); sock_set_blocking (source->con->sock, SOCK_NONBLOCK); DEBUG0("Source creation complete"); source->last_read = time (NULL); source->running = 1; /* ** Now, if we have a fallback source and override is on, we want ** to steal its clients, because it means we've come back online ** after a failure and they should be gotten back from the waiting ** loop or jingle track or whatever the fallback is used for */ if (source->fallback_override && source->fallback_mount) { source_t *fallback_source; avl_tree_rlock(global.source_tree); fallback_source = source_find_mount(source->fallback_mount); if (fallback_source) source_move_clients (fallback_source, source); avl_tree_unlock(global.source_tree); } if (source->yp_public) { yp_add (source); } else { /* If we are a private server, see if ic*-name and description is provided, and if so, add them to the stats */ if ((s = httpp_getvar(source->parser, "ice-name"))) { stats_event (source->mount, "server_name", s); } if ((s = httpp_getvar(source->parser, "icy-name"))) { stats_event (source->mount, "server_name", s); } if ((s = httpp_getvar(source->parser, "ice-description"))) { stats_event (source->mount, "server_description", s); } } }
static void *start_relay_stream (void *arg) { client_t *client = arg; relay_server *relay; source_t *src; int failed = 1, sources; global_lock(); sources = ++global.sources; stats_event_args (NULL, "sources", "%d", global.sources); global_unlock(); /* set the start time, because we want to decrease the sources on all failures */ client->connection.con_time = time (NULL); do { ice_config_t *config = config_get_config(); mount_proxy *mountinfo; relay = client->shared_data; src = relay->source; thread_rwlock_wlock (&src->lock); src->flags |= SOURCE_PAUSE_LISTENERS; if (sources > config->source_limit) { config_release_config(); WARN1 ("starting relayed mountpoint \"%s\" requires a higher sources limit", relay->localmount); break; } config_release_config(); INFO1("Starting relayed source at mountpoint \"%s\"", relay->localmount); if (open_relay (relay) < 0) break; if (connection_complete_source (src) < 0) { WARN1 ("Failed to complete initialisation on %s", relay->localmount); break; } stats_event_inc (NULL, "source_relay_connections"); source_init (src); config = config_get_config(); mountinfo = config_find_mount (config, src->mount); source_update_settings (config, src, mountinfo); INFO1 ("source %s is ready to start", src->mount); config_release_config(); failed = 0; } while (0); client->ops = &relay_client_ops; client->schedule_ms = timing_get_time(); if (failed) { /* failed to start any connection, better clean up and reset */ if (relay->on_demand == 0) { yp_remove (relay->localmount); src->yp_public = -1; } relay->in_use = NULL; INFO2 ("listener count remaining on %s is %d", src->mount, src->listeners); src->flags &= ~(SOURCE_PAUSE_LISTENERS|SOURCE_RUNNING); } thread_rwlock_unlock (&src->lock); thread_spin_lock (&relay_start_lock); relays_connecting--; thread_spin_unlock (&relay_start_lock); client->flags |= CLIENT_ACTIVE; worker_wakeup (client->worker); return NULL; }
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); }
/* client has requested a file, so check for it and send the file. Do not * refer to the client_t afterwards. return 0 for success, -1 on error. */ int fserve_client_create (client_t *httpclient, const char *path) { int bytes; struct stat file_buf; const char *range = NULL; off_t new_content_len = 0; off_t rangenumber = 0, content_length; int rangeproblem = 0; int ret = 0; char *fullpath; int m3u_requested = 0, m3u_file_available = 1; const char * xslt_playlist_requested = NULL; int xslt_playlist_file_available = 1; ice_config_t *config; FILE *file; fullpath = util_get_path_from_normalised_uri (path); INFO2 ("checking for file %H (%H)", path, fullpath); if (strcmp (util_get_extension (fullpath), "m3u") == 0) m3u_requested = 1; if (strcmp (util_get_extension (fullpath), "xspf") == 0) xslt_playlist_requested = "xspf.xsl"; if (strcmp (util_get_extension (fullpath), "vclt") == 0) xslt_playlist_requested = "vclt.xsl"; /* check for the actual file */ if (stat (fullpath, &file_buf) != 0) { /* the m3u can be generated, but send an m3u file if available */ if (m3u_requested == 0 && xslt_playlist_requested == NULL) { WARN2 ("req for file \"%H\" %s", fullpath, strerror (errno)); client_send_404 (httpclient, "The file you requested could not be found"); free (fullpath); return -1; } m3u_file_available = 0; xslt_playlist_file_available = 0; } httpclient->refbuf->len = PER_CLIENT_REFBUF_SIZE; if (m3u_requested && m3u_file_available == 0) { const char *host = httpp_getvar (httpclient->parser, "host"); char *sourceuri = strdup (path); char *dot = strrchr(sourceuri, '.'); /* at least a couple of players (fb2k/winamp) are reported to send a * host header but without the port number. So if we are missing the * port then lets treat it as if no host line was sent */ if (host && strchr (host, ':') == NULL) host = NULL; *dot = 0; httpclient->respcode = 200; if (host == NULL) { config = config_get_config(); snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.0 200 OK\r\n" "Content-Type: audio/x-mpegurl\r\n\r\n" "http://%s:%d%s\r\n", config->hostname, config->port, sourceuri ); config_release_config(); } else { snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.0 200 OK\r\n" "Content-Type: audio/x-mpegurl\r\n\r\n" "http://%s%s\r\n", host, sourceuri ); } httpclient->refbuf->len = strlen (httpclient->refbuf->data); fserve_add_client (httpclient, NULL); free (sourceuri); free (fullpath); return 0; } if (xslt_playlist_requested && xslt_playlist_file_available == 0) { xmlDocPtr doc; char *reference = strdup (path); char *eol = strrchr (reference, '.'); if (eol) *eol = '\0'; doc = stats_get_xml (0, reference); free (reference); admin_send_response (doc, httpclient, TRANSFORMED, xslt_playlist_requested); xmlFreeDoc(doc); return 0; } /* on demand file serving check */ config = config_get_config(); if (config->fileserve == 0) { DEBUG1 ("on demand file \"%H\" refused", fullpath); client_send_404 (httpclient, "The file you requested could not be found"); config_release_config(); free (fullpath); return -1; } config_release_config(); if (S_ISREG (file_buf.st_mode) == 0) { client_send_404 (httpclient, "The file you requested could not be found"); WARN1 ("found requested file but there is no handler for it: %H", fullpath); free (fullpath); return -1; } file = fopen (fullpath, "rb"); if (file == NULL) { WARN1 ("Problem accessing file \"%H\"", fullpath); client_send_404 (httpclient, "File not readable"); free (fullpath); return -1; } free (fullpath); content_length = file_buf.st_size; range = httpp_getvar (httpclient->parser, "range"); /* full http range handling is currently not done but we deal with the common case */ if (range != NULL) { ret = 0; if (strncasecmp (range, "bytes=", 6) == 0) ret = sscanf (range+6, "%" SCN_OFF_T "-", &rangenumber); if (ret != 1) { /* format not correct, so lets just assume we start from the beginning */ rangeproblem = 1; } if (rangenumber < 0) { rangeproblem = 1; } if (!rangeproblem) { ret = fseeko (file, rangenumber, SEEK_SET); if (ret != -1) { new_content_len = content_length - rangenumber; if (new_content_len < 0) { rangeproblem = 1; } } else { rangeproblem = 1; } if (!rangeproblem) { /* Date: is required on all HTTP1.1 responses */ char currenttime[50]; time_t now; int strflen; struct tm result; off_t endpos = rangenumber+new_content_len-1; char *type; if (endpos < 0) { endpos = 0; } time(&now); strflen = strftime(currenttime, 50, "%a, %d-%b-%Y %X GMT", gmtime_r(&now, &result)); httpclient->respcode = 206; type = fserve_content_type (path); bytes = snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.1 206 Partial Content\r\n" "Date: %s\r\n" "Accept-Ranges: bytes\r\n" "Content-Length: %" PRI_OFF_T "\r\n" "Content-Range: bytes %" PRI_OFF_T \ "-%" PRI_OFF_T "/%" PRI_OFF_T "\r\n" "Content-Type: %s\r\n\r\n", currenttime, new_content_len, rangenumber, endpos, content_length, type); free (type); } else { goto fail; } } else { goto fail; } } else { char *type = fserve_content_type(path); httpclient->respcode = 200; bytes = snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.0 200 OK\r\n" "Accept-Ranges: bytes\r\n" "Content-Length: %" PRI_OFF_T "\r\n" "Content-Type: %s\r\n\r\n", content_length, type); free (type); } httpclient->refbuf->len = bytes; httpclient->pos = 0; stats_event_inc (NULL, "file_connections"); fserve_add_client (httpclient, file); return 0; fail: fclose (file); httpclient->respcode = 416; sock_write (httpclient->con->sock, "HTTP/1.0 416 Request Range Not Satisfiable\r\n\r\n"); client_destroy (httpclient); return -1; }
/* Perform any initialisation just before the stream data is processed, the header * info is processed by now and the format details are setup */ static void source_init (source_t *source) { ice_config_t *config = config_get_config(); char *listenurl; const char *str; int listen_url_size; mount_proxy *mountinfo; /* 6 for max size of port */ listen_url_size = strlen("http://") + strlen(config->hostname) + strlen(":") + 6 + strlen(source->mount) + 1; listenurl = malloc (listen_url_size); memset (listenurl, '\000', listen_url_size); snprintf (listenurl, listen_url_size, "http://%s%s", config->hostname, source->mount); config_release_config(); str = httpp_getvar(source->parser, "ice-audio-info"); source->audio_info = util_dict_new(); if (str) { _parse_audio_info (source, str); stats_event (source->mount, "audio_info", str); } stats_event (source->mount, "listenurl", listenurl); free(listenurl); if (source->dumpfilename != NULL) { source->dumpfile = fopen (source->dumpfilename, "ab"); if (source->dumpfile == NULL) { WARN2("Cannot open dump file \"%s\" for appending: %s, disabling.", source->dumpfilename, strerror(errno)); } } /* grab a read lock, to make sure we get a chance to cleanup */ thread_rwlock_rlock (source->shutdown_rwlock); /* start off the statistics */ source->listeners = 0; stats_event_inc (NULL, "source_total_connections"); stats_event (source->mount, "slow_listeners", "0"); stats_event_args (source->mount, "listeners", "%lu", source->listeners); stats_event_args (source->mount, "listener_peak", "%lu", source->peak_listeners); stats_event_time (source->mount, "stream_start"); DEBUG0("Source creation complete"); source->last_read = time (NULL); source->prev_listeners = -1; source->running = 1; mountinfo = config_find_mount (config_get_config(), source->mount); if (mountinfo) { if (mountinfo->on_connect) source_run_script (mountinfo->on_connect, source->mount); auth_stream_start (mountinfo, source->mount); } config_release_config(); /* ** Now, if we have a fallback source and override is on, we want ** to steal its clients, because it means we've come back online ** after a failure and they should be gotten back from the waiting ** loop or jingle track or whatever the fallback is used for */ if (source->fallback_override && source->fallback_mount) { source_t *fallback_source; avl_tree_rlock(global.source_tree); fallback_source = source_find_mount(source->fallback_mount); if (fallback_source) source_move_clients (fallback_source, source); avl_tree_unlock(global.source_tree); } }
/* This does the actual connection for a relay. A thread is * started off if a connection can be acquired */ static void *start_relay_stream (void *arg) { relay_server *relay = arg; source_t *src = relay->source; client_t *client; ICECAST_LOG_INFO("Starting relayed source at mountpoint \"%s\"", relay->localmount); do { client = open_relay_connection (relay); if (client == NULL) continue; src->client = client; src->parser = client->parser; src->con = client->con; if (connection_complete_source (src, 0) < 0) { ICECAST_LOG_INFO("Failed to complete source initialisation"); client_destroy (client); src->client = NULL; continue; } stats_event_inc(NULL, "source_relay_connections"); stats_event (relay->localmount, "source_ip", client->con->ip); source_main (relay->source); if (relay->on_demand == 0) { /* only keep refreshing YP entries for inactive on-demand relays */ yp_remove (relay->localmount); relay->source->yp_public = -1; relay->start = time(NULL) + 10; /* prevent busy looping if failing */ slave_update_all_mounts(); } /* we've finished, now get cleaned up */ relay->cleanup = 1; slave_rebuild_mounts(); return NULL; } while (0); /* TODO allow looping through multiple servers */ if (relay->source->fallback_mount) { source_t *fallback_source; ICECAST_LOG_DEBUG("failed relay, fallback to %s", relay->source->fallback_mount); avl_tree_rlock(global.source_tree); fallback_source = source_find_mount(relay->source->fallback_mount); if (fallback_source != NULL) source_move_clients(relay->source, fallback_source); avl_tree_unlock(global.source_tree); } source_clear_source(relay->source); /* cleanup relay, but prevent this relay from starting up again too soon */ thread_mutex_lock(&_slave_mutex); thread_mutex_lock(&(config_locks()->relay_lock)); relay->source->on_demand = 0; relay->start = time(NULL) + max_interval; relay->cleanup = 1; thread_mutex_unlock(&(config_locks()->relay_lock)); thread_mutex_unlock(&_slave_mutex); return NULL; }
void source_main (source_t *source) { refbuf_t *refbuf; client_t *client; avl_node *client_node; source_init (source); while (global.running == ICE_RUNNING && source->running) { int remove_from_q; refbuf = get_next_buffer (source); remove_from_q = 0; source->short_delay = 0; if (refbuf) { /* append buffer to the in-flight data queue, */ if (source->stream_data == NULL) { source->stream_data = refbuf; source->burst_point = refbuf; } if (source->stream_data_tail) source->stream_data_tail->next = refbuf; source->stream_data_tail = refbuf; source->queue_size += refbuf->len; /* new buffer is referenced for burst */ refbuf_addref (refbuf); /* new data on queue, so check the burst point */ source->burst_offset += refbuf->len; while (source->burst_offset > source->burst_size) { refbuf_t *to_release = source->burst_point; if (to_release->next) { source->burst_point = to_release->next; source->burst_offset -= to_release->len; refbuf_release (to_release); continue; } break; } /* save stream to file */ if (source->dumpfile && source->format->write_buf_to_file) source->format->write_buf_to_file (source, refbuf); } /* lets see if we have too much data in the queue, but don't remove it until later */ if (source->queue_size > source->queue_size_limit) remove_from_q = 1; /* acquire write lock on pending_tree */ avl_tree_wlock(source->pending_tree); /* acquire write lock on client_tree */ avl_tree_wlock(source->client_tree); client_node = avl_get_first(source->client_tree); while (client_node) { client = (client_t *)client_node->key; send_to_listener (source, client, remove_from_q); if (client->con->error) { client_node = avl_get_next(client_node); if (client->respcode == 200) stats_event_dec (NULL, "listeners"); avl_delete(source->client_tree, (void *)client, _free_client); source->listeners--; DEBUG0("Client removed"); continue; } client_node = avl_get_next(client_node); } /** add pending clients **/ client_node = avl_get_first(source->pending_tree); while (client_node) { if(source->max_listeners != -1 && source->listeners >= (unsigned long)source->max_listeners) { /* The common case is caught in the main connection handler, * this deals with rarer cases (mostly concerning fallbacks) * and doesn't give the listening client any information about * why they were disconnected */ client = (client_t *)client_node->key; client_node = avl_get_next(client_node); avl_delete(source->pending_tree, (void *)client, _free_client); INFO0("Client deleted, exceeding maximum listeners for this " "mountpoint."); continue; } /* Otherwise, the client is accepted, add it */ avl_insert(source->client_tree, client_node->key); source->listeners++; DEBUG0("Client added"); stats_event_inc(source->mount, "connections"); client_node = avl_get_next(client_node); } /** clear pending tree **/ while (avl_get_first(source->pending_tree)) { avl_delete(source->pending_tree, avl_get_first(source->pending_tree)->key, source_remove_client); } /* release write lock on pending_tree */ avl_tree_unlock(source->pending_tree); /* update the stats if need be */ if (source->listeners != source->prev_listeners) { source->prev_listeners = source->listeners; INFO2("listener count on %s now %lu", source->mount, source->listeners); if (source->listeners > source->peak_listeners) { source->peak_listeners = source->listeners; stats_event_args (source->mount, "listener_peak", "%lu", source->peak_listeners); } stats_event_args (source->mount, "listeners", "%lu", source->listeners); if (source->listeners == 0 && source->on_demand) source->running = 0; } /* lets reduce the queue, any lagging clients should of been * terminated by now */ if (source->stream_data) { /* normal unreferenced queue data will have a refcount 1, but * burst queue data will be at least 2, active clients will also * increase refcount */ while (source->stream_data->_count == 1) { refbuf_t *to_go = source->stream_data; if (to_go->next == NULL || source->burst_point == to_go) { /* this should not happen */ ERROR0 ("queue state is unexpected"); source->running = 0; break; } source->stream_data = to_go->next; source->queue_size -= to_go->len; to_go->next = NULL; refbuf_release (to_go); } } /* release write lock on client_tree */ avl_tree_unlock(source->client_tree); } source_shutdown (source); }
/* client has requested a file, so check for it and send the file. Do not * refer to the client_t afterwards. return 0 for success, -1 on error. */ int fserve_client_create (client_t *httpclient, const char *path) { struct stat file_buf; char *fullpath; int m3u_requested = 0, m3u_file_available = 1; int xspf_requested = 0, xspf_file_available = 1; int ret = -1; ice_config_t *config; fbinfo finfo; char fsize[20]; fullpath = util_get_path_from_normalised_uri (path, 0); DEBUG2 ("checking for file %s (%s)", path, fullpath); if (strcmp (util_get_extension (fullpath), "m3u") == 0) m3u_requested = 1; if (strcmp (util_get_extension (fullpath), "xspf") == 0) xspf_requested = 1; /* check for the actual file */ if (stat (fullpath, &file_buf) != 0) { /* the m3u can be generated, but send an m3u file if available */ if (m3u_requested == 0 && xspf_requested == 0) { if (redirect_client (path, httpclient) == 0) { if ((httpclient->flags & CLIENT_SKIP_ACCESSLOG) == 0) WARN2 ("req for file \"%s\" %s", fullpath, strerror (errno)); ret = client_send_404 (httpclient, "The file you requested could not be found"); } free (fullpath); return ret; } m3u_file_available = 0; xspf_file_available = 0; } client_set_queue (httpclient, NULL); httpclient->refbuf = refbuf_new (4096); if (m3u_requested && m3u_file_available == 0) { const char *host = httpp_getvar (httpclient->parser, "host"), *args = httpp_getvar (httpclient->parser, HTTPP_VAR_QUERYARGS), *at = "", *user = "", *pass =""; char *sourceuri = strdup (path); char *dot = strrchr (sourceuri, '.'); char *protocol = "http"; const char *agent = httpp_getvar (httpclient->parser, "user-agent"); int x; char scratch[1000]; if (agent) { if (strstr (agent, "QTS") || strstr (agent, "QuickTime")) protocol = "icy"; } /* at least a couple of players (fb2k/winamp) are reported to send a * host header but without the port number. So if we are missing the * port then lets treat it as if no host line was sent */ if (host && strchr (host, ':') == NULL) host = NULL; *dot = 0; if (httpclient->username && httpclient->password) { at = "@"; user = httpclient->username; pass = httpclient->password; } httpclient->respcode = 200; if (host == NULL) { config = config_get_config(); x = snprintf (scratch, sizeof scratch, "%s://%s%s%s%s%s:%d%s%s\r\n", protocol, user, at[0]?":":"", pass, at, config->hostname, config->port, sourceuri, args?args:""); config_release_config(); } else { x = snprintf (scratch, sizeof scratch, "%s://%s%s%s%s%s%s%s\r\n", protocol, user, at[0]?":":"", pass, at, host, sourceuri, args?args:""); } snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.0 200 OK\r\n" "Content-Length: %d\r\n" "%s\r\n" "Content-Type: audio/x-mpegurl\r\n\r\n%s", x, client_keepalive_header (httpclient), scratch); httpclient->refbuf->len = strlen (httpclient->refbuf->data); free (sourceuri); free (fullpath); return fserve_setup_client_fb (httpclient, NULL); } if (xspf_requested && xspf_file_available == 0) { xmlDocPtr doc; char *reference = strdup (path); char *eol = strrchr (reference, '.'); if (eol) *eol = '\0'; doc = stats_get_xml (0, reference); free (reference); free (fullpath); return admin_send_response (doc, httpclient, XSLT, "xspf.xsl"); } /* on demand file serving check */ config = config_get_config(); if (config->fileserve == 0) { config_release_config(); DEBUG1 ("on demand file \"%s\" refused", fullpath); free (fullpath); return client_send_404 (httpclient, "The file you requested could not be found"); } config_release_config(); if (S_ISREG (file_buf.st_mode) == 0) { WARN1 ("found requested file but there is no handler for it: %s", fullpath); free (fullpath); return client_send_404 (httpclient, "The file you requested could not be found"); } free (fullpath); finfo.flags = 0; finfo.mount = (char *)path; finfo.fallback = NULL; finfo.limit = 0; finfo.type = FORMAT_TYPE_UNDEFINED; snprintf (fsize, 20, "%" PRId64, (int64_t)file_buf.st_size); httpp_setvar (httpclient->parser, "__FILESIZE", fsize); stats_event_inc (NULL, "file_connections"); return fserve_setup_client_fb (httpclient, &finfo); }
/* we don't need to clean up on err, as we'll go through the node struct and clean all we have inside */ static int _connection_process (connection_queue_t *node) { refbuf_t *header; http_parser_t *parser = NULL; int hdrsize = 0; int shoutcast = 0; int err; char *shoutcast_mount = NULL; mount_proxy *mountinfo; ice_config_t *config; listener_t *listener; if (!node->refbuf) node->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE); header = node->refbuf; { /* this code tests for shoutcastness */ config = config_get_config(); listener = config_get_listen_sock (config, node->con); if (listener) { WARN("listner"); if (listener->shoutcast_compat) shoutcast = 1; if (listener->ssl && ssl_ok) connection_uses_ssl (node->con); if (listener->shoutcast_mount) { shoutcast_mount = strdup (listener->shoutcast_mount); } else { shoutcast_mount = config->shoutcast_mount; } } WARN("shoutcast %d, mount %s", shoutcast, shoutcast_mount); mountinfo = config_find_mount (config, shoutcast_mount); config_release_config(); } if (shoutcast && !header->sync_point) { /* stage2 is actually handled by generic code */ err = _handle_shoutcast_stage1 (node, shoutcast_mount, mountinfo); if (err < 0) return err; } hdrsize = util_read_header (node->con, header, HEADER_READ_ENTIRE); if (hdrsize < 0) { ERROR ("Header read failed"); return hdrsize; } /* process normal HTTP headers */ if (node->parser) { parser = node->parser; } else { parser = node->parser = httpp_create_parser(); httpp_initialize(parser, NULL); } err = httpp_parse (parser, header->data, hdrsize); if (err == 0) { ERROR0("HTTP request parsing failed"); return -EINVAL; } /* XXX what happens when error in http ??? is err set ? */ if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE)) { ERROR("Error(%s)", httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE)); return err; } if (header->sync_point && (parser->req_type == httpp_req_source || parser->req_type == httpp_req_post)) { hdrsize = util_read_header (node->con, header, HEADER_READ_ENTIRE); if (hdrsize < 0) { INFO ("Header read failed"); return hdrsize; } } if (! node->client) { err = connection_client_setup (node); if (err < 0) return err; header->len -= hdrsize; if (header->len) { memmove(header->data, header->data + hdrsize, header->len); client_set_queue (node->client, header); } refbuf_release(header); } stats_event_inc (NULL, "connections"); WARN("shoutcast = %d", shoutcast); return _handle_client (node->client); }