/* 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 event_config_read(void *arg) { int ret; ice_config_t *config; ice_config_t new_config; /* reread config file */ config = config_grab_config(); /* Both to get the lock, and to be able to find out the config filename */ xmlSetGenericErrorFunc ("config", log_parse_failure); ret = config_parse_file(config->config_filename, &new_config); if(ret < 0) { ICECAST_LOG_ERROR("Error parsing config, not replacing existing config"); switch(ret) { case CONFIG_EINSANE: ICECAST_LOG_ERROR("Config filename null or blank"); break; case CONFIG_ENOROOT: ICECAST_LOG_ERROR("Root element not found in %s", config->config_filename); break; case CONFIG_EBADROOT: ICECAST_LOG_ERROR("Not an icecast2 config file: %s", config->config_filename); break; default: ICECAST_LOG_ERROR("Parse error in reading %s", config->config_filename); break; } config_release_config(); } else { config_clear(config); config_set_config(&new_config); config = config_get_config_unlocked(); restart_logging (config); yp_recheck_config (config); fserve_recheck_mime_types (config); stats_global (config); config_release_config(); slave_update_all_mounts(); } }
static int ebml_wrote(ebml_t *ebml, int len) { int b; if (ebml->header_size == 0) { if ((ebml->header_position + len) > EBML_HEADER_MAX_SIZE) { ICECAST_LOG_ERROR("EBML Header too large, failing"); return -1; } /* ICECAST_LOG_DEBUG("EBML: Adding to header, ofset is %d size is %d adding %d", ebml->header_size, ebml->header_position, len); */ memcpy(ebml->header + ebml->header_position, ebml->input_buffer, len); ebml->header_position += len; } else { memcpy(ebml->buffer + ebml->position, ebml->input_buffer, len); } for (b = 0; b < len - 4; b++) { if (!memcmp(ebml->input_buffer + b, ebml->cluster_id, 4)) { /* ICECAST_LOG_DEBUG("EBML: found cluster"); */ if (ebml->header_size == 0) { ebml->header_size = ebml->header_position - len + b; memcpy(ebml->buffer, ebml->input_buffer + b, len - b); ebml->position = len - b; ebml->cluster_start = -1; return len; } else { ebml->cluster_start = ebml->position + b; } } } ebml->position += len; return len; }
static int get_authenticator (auth_t *auth, config_options_t *options) { if (auth->type == NULL) { ICECAST_LOG_WARN("no authentication type defined"); return -1; } do { ICECAST_LOG_DEBUG("type is %s", auth->type); if (strcmp (auth->type, "url") == 0) { #ifdef HAVE_AUTH_URL if (auth_get_url_auth (auth, options) < 0) return -1; break; #else ICECAST_LOG_ERROR("Auth URL disabled"); return -1; #endif } if (strcmp (auth->type, "htpasswd") == 0) { if (auth_get_htpasswd_auth (auth, options) < 0) return -1; break; } ICECAST_LOG_ERROR("Unrecognised authenticator type: \"%s\"", auth->type); return -1; } while (0); while (options) { if (strcmp (options->name, "allow_duplicate_users") == 0) auth->allow_duplicate_users = atoi ((char*)options->value); options = options->next; } return 0; }
static refbuf_t *ebml_get_buffer(source_t *source) { ebml_source_state_t *ebml_source_state = source->format->_state; format_plugin_t *format = source->format; char *data = NULL; int bytes = 0; refbuf_t *refbuf; int ret; while (1) { if ((bytes = ebml_read_space(ebml_source_state->ebml)) > 0) { refbuf = refbuf_new(bytes); ebml_read(ebml_source_state->ebml, refbuf->data, bytes); if (ebml_source_state->header == NULL) { ebml_source_state->header = refbuf; continue; } if (ebml_last_was_sync(ebml_source_state->ebml)) { refbuf->sync_point = 1; } return refbuf; } else { data = ebml_write_buffer(ebml_source_state->ebml, EBML_SLICE_SIZE); bytes = client_read_bytes (source->client, data, EBML_SLICE_SIZE); if (bytes <= 0) { ebml_wrote (ebml_source_state->ebml, 0); return NULL; } format->read_bytes += bytes; ret = ebml_wrote (ebml_source_state->ebml, bytes); if (ret != bytes) { ICECAST_LOG_ERROR("Problem processing stream"); source->running = 0; return NULL; } } } }
void refbuf_release(refbuf_t *self) { if (self == NULL) return; self->_count--; if (self->_count == 0) { refbuf_release_associated (self->associated); if (self->next) ICECAST_LOG_ERROR("next not null"); free(self->data); free(self); } }
/* 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_listener_to_source (source_t *source, client_t *client) { int loop = 10; do { ICECAST_LOG_DEBUG("max on %s is %ld (cur %lu)", source->mount, source->max_listeners, source->listeners); if (source->max_listeners == -1) break; if (source->listeners < (unsigned long)source->max_listeners) break; if (loop && source->fallback_when_full && source->fallback_mount) { source_t *next = source_find_mount (source->fallback_mount); if (!next) { ICECAST_LOG_ERROR("Fallback '%s' for full source '%s' not found", source->mount, source->fallback_mount); return -1; } ICECAST_LOG_INFO("stream full trying %s", next->mount); source = next; loop--; continue; } /* now we fail the client */ return -1; } while (1); client->write_to_client = format_generic_write_to_client; client->check_buffer = format_check_http_buffer; client->refbuf->len = PER_CLIENT_REFBUF_SIZE; memset (client->refbuf->data, 0, PER_CLIENT_REFBUF_SIZE); /* lets add the client to the active list */ avl_tree_wlock (source->pending_tree); avl_insert (source->pending_tree, client); avl_tree_unlock (source->pending_tree); if (source->running == 0 && source->on_demand) { /* enable on-demand relay to start, wake up the slave thread */ ICECAST_LOG_DEBUG("kicking off on-demand relay"); source->on_demand_req = 1; } ICECAST_LOG_DEBUG("Added client to %s", source->mount); return 0; }
/* The auth thread main loop. */ static void *auth_run_thread (void *arg) { auth_t *auth = arg; ICECAST_LOG_INFO("Authentication thread started"); while (auth->running) { /* usually no clients are waiting, so don't bother taking locks */ if (auth->head) { auth_client *auth_user; /* 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; } ICECAST_LOG_DEBUG("%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) auth_user->process (auth, auth_user); else ICECAST_LOG_ERROR("client auth process not set"); auth_client_free (auth_user); continue; } thread_sleep (150000); } ICECAST_LOG_INFO("Authenication thread shutting down"); return NULL; }
int event_get_log(event_registration_t *er, config_options_t *options) { event_log_t *self = calloc(1, sizeof(event_log_t)); if (!self) return -1; self->prefix = strdup("Event"); self->level = ICECAST_LOGLEVEL_INFO; if (options) { do { if (options->type) continue; if (!options->name) continue; /* BEFORE RELEASE 2.5.0 DOCUMENT: Document supported options: * <option name="prefix" value="..." /> * <option name="level" value="..." /> */ if (strcmp(options->name, "prefix") == 0) { free(self->prefix); self->prefix = NULL; if (options->value) self->prefix = strdup(options->value); } else if (strcmp(options->name, "level") == 0) { self->level = ICECAST_LOGLEVEL_INFO; if (options->value) self->level = util_str_to_loglevel(options->value); } else { ICECAST_LOG_ERROR("Unknown <option> tag with name %s.", options->name); } } while ((options = options->next)); } er->state = self; er->emit = event_log_emit; er->free = event_log_free; return 0; }
static void client_send_error(client_t *client, int status, int plain, const char *message) { ssize_t ret; ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0, 0, status, NULL, plain ? "text/plain" : "text/html", "utf-8", plain ? message : "", NULL); if (ret == -1 || ret >= PER_CLIENT_REFBUF_SIZE) { ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); client_send_500(client, "Header generation failed."); return; } if (!plain) snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret, "<html><head><title>Error %i</title></head><body><b>%i - %s</b></body></html>\r\n", status, status, message); client->respcode = status; client->refbuf->len = strlen (client->refbuf->data); fserve_add_client (client, NULL); }
void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client) { xmlDocPtr res; xsltStylesheetPtr cur; xmlChar *string; int len, problem = 0; const char *mediatype = NULL; const char *charset = NULL; xmlSetGenericErrorFunc ("", log_parse_failure); xsltSetGenericErrorFunc ("", log_parse_failure); thread_mutex_lock(&xsltlock); cur = xslt_get_stylesheet(xslfilename); if (cur == NULL) { thread_mutex_unlock(&xsltlock); ICECAST_LOG_ERROR("problem reading stylesheet \"%s\"", xslfilename); client_send_404 (client, "Could not parse XSLT file"); return; } res = xsltApplyStylesheet(cur, doc, NULL); if (xsltSaveResultToString (&string, &len, res, cur) < 0) problem = 1; /* lets find out the content type and character encoding to use */ if (cur->encoding) charset = (char *)cur->encoding; if (cur->mediaType) mediatype = (char *)cur->mediaType; else { /* check method for the default, a missing method assumes xml */ if (cur->method && xmlStrcmp (cur->method, XMLSTR("html")) == 0) mediatype = "text/html"; else if (cur->method && xmlStrcmp (cur->method, XMLSTR("text")) == 0) mediatype = "text/plain"; else mediatype = "text/xml"; } if (problem == 0) { ssize_t ret; int failed = 0; refbuf_t *refbuf; size_t full_len = strlen (mediatype) + len + 1024; if (full_len < 4096) full_len = 4096; refbuf = refbuf_new (full_len); if (string == NULL) string = xmlCharStrdup (""); ret = util_http_build_header(refbuf->data, full_len, 0, 0, 200, NULL, mediatype, charset, NULL, NULL); if (ret == -1) { ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); client_send_500(client, "Header generation failed."); } else { if ( full_len < (ret + len + 64) ) { void *new_data; full_len = ret + len + 64; new_data = realloc(refbuf->data, full_len); if (new_data) { ICECAST_LOG_DEBUG("Client buffer reallocation succeeded."); refbuf->data = new_data; refbuf->len = full_len; ret = util_http_build_header(refbuf->data, full_len, 0, 0, 200, NULL, mediatype, charset, NULL, NULL); if (ret == -1) { ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); client_send_500(client, "Header generation failed."); failed = 1; } } else { ICECAST_LOG_ERROR("Client buffer reallocation failed. Dropping client."); client_send_500(client, "Buffer reallocation failed."); failed = 1; } } if (!failed) { snprintf(refbuf->data + ret, full_len - ret, "Content-Length: %d\r\n\r\n%s", len, string); client->respcode = 200; client_set_queue (client, NULL); client->refbuf = refbuf; refbuf->len = strlen (refbuf->data); fserve_add_client (client, NULL); } } xmlFree (string); } else { ICECAST_LOG_WARN("problem applying stylesheet \"%s\"", xslfilename); client_send_404 (client, "XSLT problem"); } thread_mutex_unlock (&xsltlock); xmlFreeDoc(res); }
int auth_get_url_auth(auth_t *authenticator, config_options_t *options) { auth_url *url_info; const char *addaction = "listener_add"; const char *removeaction = "listener_remove"; authenticator->free = auth_url_clear; authenticator->adduser = auth_url_adduser; authenticator->deleteuser = auth_url_deleteuser; authenticator->listuser = auth_url_listuser; url_info = calloc(1, sizeof(auth_url)); authenticator->state = url_info; /* default headers */ url_info->auth_header = strdup("icecast-auth-user: 1\r\n"); url_info->timelimit_header = strdup("icecast-auth-timelimit:"); /* force auth thread to call function. this makes sure the auth_t is attached to client */ authenticator->authenticate_client = url_add_client; while(options) { if(strcmp(options->name, "username") == 0) { free(url_info->username); url_info->username = strdup(options->value); } else if(strcmp(options->name, "password") == 0) { free(url_info->password); url_info->password = strdup(options->value); } else if(strcmp(options->name, "headers") == 0) { free(url_info->pass_headers); url_info->pass_headers = strdup(options->value); } else if(strcmp(options->name, "header_prefix") == 0) { free(url_info->prefix_headers); url_info->prefix_headers = strdup(options->value); } else if(strcmp(options->name, "client_add") == 0) { free(url_info->addurl); url_info->addurl = strdup(options->value); } else if(strcmp(options->name, "client_remove") == 0) { authenticator->release_client = url_remove_client; free(url_info->removeurl); url_info->removeurl = strdup(options->value); } else if(strcmp(options->name, "action_add") == 0) { addaction = options->value; } else if(strcmp(options->name, "action_remove") == 0) { removeaction = options->value; } else if(strcmp(options->name, "auth_header") == 0) { free(url_info->auth_header); url_info->auth_header = strdup(options->value); } else if (strcmp(options->name, "timelimit_header") == 0) { free(url_info->timelimit_header); url_info->timelimit_header = strdup(options->value); } else { ICECAST_LOG_ERROR("Unknown option: %s", options->name); } options = options->next; } url_info->addaction = util_url_escape(addaction); url_info->removeaction = util_url_escape(removeaction); url_info->handle = icecast_curl_new(NULL, &url_info->errormsg[0]); if (url_info->handle == NULL) { auth_url_clear(authenticator); return -1; } if (url_info->auth_header) url_info->auth_header_len = strlen (url_info->auth_header); if (url_info->timelimit_header) url_info->timelimit_header_len = strlen (url_info->timelimit_header); curl_easy_setopt(url_info->handle, CURLOPT_HEADERFUNCTION, handle_returned_header); if (url_info->username && url_info->password) { int len = strlen(url_info->username) + strlen(url_info->password) + 2; url_info->userpwd = malloc(len); snprintf(url_info->userpwd, len, "%s:%s", url_info->username, url_info->password); } ICECAST_LOG_INFO("URL based authentication setup"); return 0; }
static int format_prepare_headers (source_t *source, client_t *client) { unsigned remaining; char *ptr; int bytes; int bitrate_filtered = 0; avl_node *node; remaining = client->refbuf->len; ptr = client->refbuf->data; client->respcode = 200; bytes = util_http_build_header(ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL, source, client); if (bytes == -1) { ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); client_send_error(client, 500, 0, "Header generation failed."); return -1; } else if ((bytes + 1024) >= remaining) { /* we don't know yet how much to follow but want at least 1kB free space */ void *new_ptr = realloc(ptr, bytes + 1024); if (new_ptr) { ICECAST_LOG_DEBUG("Client buffer reallocation succeeded."); client->refbuf->data = ptr = new_ptr; client->refbuf->len = remaining = bytes + 1024; bytes = util_http_build_header(ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL, source, client); if (bytes == -1 ) { ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); client_send_error(client, 500, 0, "Header generation failed."); return -1; } } else { ICECAST_LOG_ERROR("Client buffer reallocation failed. Dropping client."); client_send_error(client, 500, 0, "Buffer reallocation failed."); return -1; } } remaining -= bytes; ptr += bytes; /* iterate through source http headers and send to client */ avl_tree_rlock(source->parser->vars); node = avl_get_first(source->parser->vars); while (node) { int next = 1; http_var_t *var = (http_var_t *) node->key; bytes = 0; if (!strcasecmp(var->name, "ice-audio-info")) { /* convert ice-audio-info to icy-br */ char *brfield = NULL; unsigned int bitrate; if (bitrate_filtered == 0) brfield = strstr(var->value, "bitrate="); if (brfield && sscanf (brfield, "bitrate=%u", &bitrate)) { bytes = snprintf (ptr, remaining, "icy-br:%u\r\n", bitrate); next = 0; bitrate_filtered = 1; } else /* show ice-audio_info header as well because of relays */ bytes = snprintf (ptr, remaining, "%s: %s\r\n", var->name, var->value); } else { if (strcasecmp(var->name, "ice-password") && strcasecmp(var->name, "icy-metaint")) { if (!strcasecmp(var->name, "ice-name")) { ice_config_t *config; mount_proxy *mountinfo; config = config_get_config(); mountinfo = config_find_mount (config, source->mount, MOUNT_TYPE_NORMAL); if (mountinfo && mountinfo->stream_name) bytes = snprintf (ptr, remaining, "icy-name:%s\r\n", mountinfo->stream_name); else bytes = snprintf (ptr, remaining, "icy-name:%s\r\n", var->value); config_release_config(); } else if (!strncasecmp("ice-", var->name, 4)) { if (!strcasecmp("ice-public", var->name)) bytes = snprintf (ptr, remaining, "icy-pub:%s\r\n", var->value); else if (!strcasecmp ("ice-bitrate", var->name)) bytes = snprintf (ptr, remaining, "icy-br:%s\r\n", var->value); else bytes = snprintf (ptr, remaining, "icy%s:%s\r\n", var->name + 3, var->value); } else if (!strncasecmp("icy-", var->name, 4)) { bytes = snprintf (ptr, remaining, "icy%s:%s\r\n", var->name + 3, var->value); } } } remaining -= bytes; ptr += bytes; if (next) node = avl_get_next(node); } avl_tree_unlock(source->parser->vars); bytes = snprintf(ptr, remaining, "\r\n"); remaining -= bytes; ptr += bytes; client->refbuf->len -= remaining; if (source->format->create_client_data) if (source->format->create_client_data (source, client) < 0) return -1; return 0; }
/* read mp3 data with inlined metadata from the source. Filter out the * metadata so that the mp3 data itself is store on the queue and the * metadata is is associated with it */ static refbuf_t *mp3_get_filter_meta (source_t *source) { refbuf_t *refbuf; format_plugin_t *plugin = source->format; mp3_state *source_mp3 = plugin->_state; unsigned char *src; unsigned int bytes, mp3_block; if (complete_read (source) == 0) return NULL; refbuf = source_mp3->read_data; source_mp3->read_data = NULL; src = (unsigned char *)refbuf->data; if (source_mp3->update_metadata) { mp3_set_title (source); source_mp3->update_metadata = 0; } /* fill the buffer with the read data */ bytes = source_mp3->read_count; refbuf->len = 0; while (bytes > 0) { unsigned int metadata_remaining; mp3_block = source_mp3->inline_metadata_interval - source_mp3->offset; /* is there only enough to account for mp3 data */ if (bytes <= mp3_block) { refbuf->len += bytes; source_mp3->offset += bytes; break; } /* we have enough data to get to the metadata * block, but only transfer upto it */ if (mp3_block) { src += mp3_block; bytes -= mp3_block; refbuf->len += mp3_block; source_mp3->offset += mp3_block; continue; } /* process the inline metadata, len == 0 indicates not seen any yet */ if (source_mp3->build_metadata_len == 0) { memset (source_mp3->build_metadata, 0, sizeof (source_mp3->build_metadata)); source_mp3->build_metadata_offset = 0; source_mp3->build_metadata_len = 1 + (*src * 16); } /* do we have all of the metatdata block */ metadata_remaining = source_mp3->build_metadata_len - source_mp3->build_metadata_offset; if (bytes < metadata_remaining) { memcpy (source_mp3->build_metadata + source_mp3->build_metadata_offset, src, bytes); source_mp3->build_metadata_offset += bytes; break; } /* copy all bytes except the last one, that way we * know a null byte terminates the message */ memcpy (source_mp3->build_metadata + source_mp3->build_metadata_offset, src, metadata_remaining-1); /* overwrite metadata in the buffer */ bytes -= metadata_remaining; memmove (src, src+metadata_remaining, bytes); /* assign metadata if it's greater than 1 byte, and the text has changed */ if (source_mp3->build_metadata_len > 1 && strcmp (source_mp3->build_metadata+1, source_mp3->metadata->data+1) != 0) { refbuf_t *meta = refbuf_new (source_mp3->build_metadata_len); memcpy (meta->data, source_mp3->build_metadata, source_mp3->build_metadata_len); ICECAST_LOG_DEBUG("shoutcast metadata %.*s", 4080, meta->data+1); if (strncmp (meta->data+1, "StreamTitle=", 12) == 0) { filter_shoutcast_metadata (source, source_mp3->build_metadata, source_mp3->build_metadata_len); refbuf_release (source_mp3->metadata); source_mp3->metadata = meta; source_mp3->inline_url = strstr (meta->data+1, "StreamUrl='"); } else { ICECAST_LOG_ERROR("Incorrect metadata format, ending stream"); source->running = 0; refbuf_release (refbuf); refbuf_release (meta); return NULL; } } source_mp3->offset = 0; source_mp3->build_metadata_len = 0; } /* the data we have just read may of just been metadata */ if (refbuf->len == 0) { refbuf_release (refbuf); return NULL; } refbuf->associated = source_mp3->metadata; refbuf_addref (source_mp3->metadata); refbuf->sync_point = 1; return refbuf; }
/* a new BOS page has been seen so check which codec it is */ static int process_initial_page (format_plugin_t *plugin, ogg_page *page) { ogg_state_t *ogg_info = plugin->_state; ogg_codec_t *codec; if (ogg_info->bos_completed) { ogg_info->bitrate = 0; ogg_info->codec_sync = NULL; /* need to zap old list of codecs when next group of BOS pages appear */ free_ogg_codecs (ogg_info); } do { if (ogg_info->codec_count > 10) { ICECAST_LOG_ERROR("many codecs in stream, playing safe, dropping source"); ogg_info->error = 1; return -1; } codec = initial_vorbis_page (plugin, page); if (codec) break; #ifdef HAVE_THEORA codec = initial_theora_page (plugin, page); if (codec) break; #endif codec = initial_midi_page (plugin, page); if (codec) break; codec = initial_flac_page (plugin, page); if (codec) break; #ifdef HAVE_SPEEX codec = initial_speex_page (plugin, page); if (codec) break; #endif codec = initial_kate_page (plugin, page); if (codec) break; codec = initial_skeleton_page (plugin, page); if (codec) break; codec = initial_opus_page (plugin, page); if (codec) break; /* any others */ ICECAST_LOG_ERROR("Seen BOS page with unknown type"); ogg_info->error = 1; return -1; } while (0); if (codec) { /* add codec to list */ codec->next = ogg_info->codecs; ogg_info->codecs = codec; ogg_info->codec_count++; } return 0; }
/* main plugin handler for getting a buffer for the queue. In here we * just add an incoming page to the codecs and process it until either * more data is needed or we prodice a buffer for the queue. */ static refbuf_t *ogg_get_buffer (source_t *source) { ogg_state_t *ogg_info = source->format->_state; format_plugin_t *format = source->format; char *data = NULL; int bytes = 0; while (1) { while (1) { ogg_page page; refbuf_t *refbuf = NULL; ogg_codec_t *codec = ogg_info->current; /* if a codec has just been given a page then process it */ if (codec && codec->process) { refbuf = codec->process (ogg_info, codec); if (refbuf) return complete_buffer (source, refbuf); ogg_info->current = NULL; } if (ogg_sync_pageout (&ogg_info->oy, &page) > 0) { if (ogg_page_bos (&page)) { process_initial_page (source->format, &page); } else { ogg_info->bos_completed = 1; refbuf = process_ogg_page (ogg_info, &page); } if (ogg_info->error) { ICECAST_LOG_ERROR("Problem processing stream"); source->running = 0; return NULL; } if (refbuf) return complete_buffer (source, refbuf); continue; } /* need more stream data */ break; } /* we need more data to continue getting pages */ data = ogg_sync_buffer (&ogg_info->oy, 4096); bytes = client_read_bytes (source->client, data, 4096); if (bytes <= 0) { ogg_sync_wrote (&ogg_info->oy, 0); return NULL; } format->read_bytes += bytes; ogg_sync_wrote (&ogg_info->oy, bytes); } }
/* 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; }
/* Actually open the connection and do some http parsing, handle any 302 * responses within here. */ static client_t *open_relay_connection (relay_server *relay) { int redirects = 0; char *server_id = NULL; ice_config_t *config; http_parser_t *parser = NULL; connection_t *con=NULL; char *server = strdup (relay->server); char *mount = strdup (relay->mount); int port = relay->port; char *auth_header; char header[4096]; config = config_get_config (); server_id = strdup (config->server_id); config_release_config (); /* build any authentication header before connecting */ if (relay->username && relay->password) { char *esc_authorisation; unsigned len = strlen(relay->username) + strlen(relay->password) + 2; auth_header = malloc (len); snprintf (auth_header, len, "%s:%s", relay->username, relay->password); esc_authorisation = util_base64_encode(auth_header, len); free(auth_header); len = strlen (esc_authorisation) + 24; auth_header = malloc (len); snprintf (auth_header, len, "Authorization: Basic %s\r\n", esc_authorisation); free(esc_authorisation); } else auth_header = strdup (""); while (redirects < 10) { sock_t streamsock; ICECAST_LOG_INFO("connecting to %s:%d", server, port); streamsock = sock_connect_wto_bind (server, port, relay->bind, 10); if (streamsock == SOCK_ERROR) { ICECAST_LOG_WARN("Failed to connect to %s:%d", server, port); break; } con = connection_create (streamsock, -1, strdup (server)); /* At this point we may not know if we are relaying an mp3 or vorbis * stream, but only send the icy-metadata header if the relay details * state so (the typical case). It's harmless in the vorbis case. If * we don't send in this header then relay will not have mp3 metadata. */ sock_write(streamsock, "GET %s HTTP/1.0\r\n" "User-Agent: %s\r\n" "Host: %s\r\n" "%s" "%s" "\r\n", mount, server_id, server, relay->mp3metadata?"Icy-MetaData: 1\r\n":"", auth_header); memset (header, 0, sizeof(header)); if (util_read_header (con->sock, header, 4096, READ_ENTIRE_HEADER) == 0) { ICECAST_LOG_ERROR("Header read failed for %s (%s:%d%s)", relay->localmount, server, port, mount); break; } parser = httpp_create_parser(); httpp_initialize (parser, NULL); if (! httpp_parse_response (parser, header, strlen(header), relay->localmount)) { ICECAST_LOG_ERROR("Error parsing relay request for %s (%s:%d%s)", relay->localmount, server, port, mount); break; } if (strcmp (httpp_getvar (parser, HTTPP_VAR_ERROR_CODE), "302") == 0) { /* better retry the connection again but with different details */ const char *uri, *mountpoint; int len; uri = httpp_getvar (parser, "location"); ICECAST_LOG_INFO("redirect received %s", uri); if (strncmp (uri, "http://", 7) != 0) break; uri += 7; mountpoint = strchr (uri, '/'); free (mount); if (mountpoint) mount = strdup (mountpoint); else mount = strdup ("/"); len = strcspn (uri, ":/"); port = 80; if (uri [len] == ':') port = atoi (uri+len+1); free (server); server = calloc (1, len+1); strncpy (server, uri, len); connection_close (con); httpp_destroy (parser); con = NULL; parser = NULL; } else { client_t *client = NULL; if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE)) { ICECAST_LOG_ERROR("Error from relay request: %s (%s)", relay->localmount, httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE)); break; } global_lock (); if (client_create (&client, con, parser) < 0) { global_unlock (); /* make sure only the client_destory frees these */ con = NULL; parser = NULL; client_destroy (client); break; } global_unlock (); sock_set_blocking (streamsock, 0); client_set_queue (client, NULL); free (server); free (mount); free (server_id); free (auth_header); return client; } redirects++; } /* failed, better clean up */ free (server); free (mount); free (server_id); free (auth_header); if (con) connection_close (con); if (parser) httpp_destroy (parser); return NULL; }