/* find/create handle and return it with the structure in a locked state */ static fh_node *open_fh (fbinfo *finfo) { fh_node *fh, *result; if (finfo->mount == NULL) finfo->mount = ""; fh = calloc (1, sizeof (fh_node)); memcpy (&fh->finfo, finfo, sizeof (fbinfo)); if (avl_get_by_key (fh_cache, fh, (void**)&result) == 0) { free (fh); thread_mutex_lock (&result->lock); avl_tree_unlock (fh_cache); if (finfo->flags & FS_FALLBACK) { if (result->finfo.type != finfo->type && finfo->type != FORMAT_TYPE_UNDEFINED) { WARN1 ("format mismatched for %s", finfo->mount); thread_mutex_unlock (&result->lock); return NULL; } result->expire = (time_t)-1; } return result; } // insert new one if (fh->finfo.mount[0]) { char *fullpath= util_get_path_from_normalised_uri (fh->finfo.mount, fh->finfo.flags&FS_USE_ADMIN); char *contenttype = fserve_content_type (fullpath); format_type_t type = format_get_type (contenttype); if (fh->finfo.type == FORMAT_TYPE_UNDEFINED) fh->finfo.type = type; if (finfo->flags & FS_FALLBACK) { if (fh->finfo.type != type && type != FORMAT_TYPE_UNDEFINED && fh->finfo.type != FORMAT_TYPE_UNDEFINED) { avl_tree_unlock (fh_cache); free (contenttype); free (fullpath); free (fh); WARN1 ("format mismatched for %s", finfo->mount); return NULL; } fh->expire = (time_t)-1; INFO2 ("lookup of fallback file \"%s\" (%d)", finfo->mount, finfo->limit); } else INFO1 ("lookup of \"%s\"", finfo->mount); if (file_open (&fh->f, fullpath) < 0) { INFO1 ("Failed to open \"%s\"", fullpath); avl_tree_unlock (fh_cache); free (contenttype); free (fullpath); free (fh); return NULL; } free (fullpath); fh->format = calloc (1, sizeof (format_plugin_t)); fh->format->type = fh->finfo.type; fh->format->contenttype = strdup (contenttype); free (contenttype); if (fh->finfo.type != FORMAT_TYPE_UNDEFINED) { fh->format->mount = strdup (fh->finfo.mount); if (format_get_plugin (fh->format) < 0) { avl_tree_unlock (fh_cache); file_close (&fh->f); free (fh->format); free (fh); return NULL; } } if (fh->finfo.limit) fh->out_bitrate = rate_setup (10000, 1000); } fh->clients = avl_tree_new (client_compare, NULL); thread_mutex_create (&fh->lock); thread_mutex_lock (&fh->lock); avl_insert (fh_cache, fh); avl_tree_unlock (fh_cache); fh->refcount = 0; fh->peak = 0; fh->finfo.mount = strdup (finfo->mount); fh->finfo.fallback = NULL; return fh; }
/* 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; }
/* 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_404 (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_404 (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_404 (source->client, "too many sources connected"); source->client = NULL; } return -1; }
static size_t handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream) { auth_client *auth_user = stream; unsigned bytes = size * nmemb; client_t *client = auth_user->client; auth_thread_data *atd = auth_user->thread_data; char *header = (char *)ptr, *header_data; if (bytes <= 1 || client == NULL) return bytes; do { auth_t *auth = auth_user->auth; auth_url *url = auth->state; int retcode = 0, header_datalen; /* replace the EOL with a nul char, libcurl may not provide a nul */ header [bytes-2] = '\0'; if (sscanf (ptr, "HTTP%*c%*u.%*u %3d %*c", &retcode) == 1) { if (retcode == 403) { char *p = strchr (ptr, ' ') + 1; snprintf (atd->errormsg, sizeof(atd->errormsg), "%s", p); p = strchr (atd->errormsg, '\r'); if (p) *p='\0'; } else if ((auth->flags & AUTH_SKIP_IF_SLOW) && retcode >= 400 && retcode < 600) { snprintf (atd->errormsg, sizeof(atd->errormsg), "auth on %s disabled, response was \'%.200s...\'", auth->mount, header); url->stop_req_until = time (NULL) + url->stop_req_duration; /* prevent further attempts for a while */ client->flags |= CLIENT_AUTHENTICATED; return bytes; } } header_data = strchr (header, ':'); if (header_data == NULL) return bytes; header_data++; header_data += strspn (header_data, " \t"); // find non-space start header_datalen = strcspn (header_data, "\r\n"); // find length if (strncasecmp (header, url->auth_header, url->auth_header_len) == 0) { client->flags |= CLIENT_AUTHENTICATED; if (header_data) { if (strstr (header_data, "withintro")) client->flags |= CLIENT_HAS_INTRO_CONTENT; if (strstr (header_data, "hijack")) client->flags |= CLIENT_HIJACKER; if (strstr (header_data, "0")) { WARN0 ("auth header returned with 0 value"); client->flags &= ~CLIENT_AUTHENTICATED; } } break; } if (strncasecmp (header, url->timelimit_header, url->timelimit_header_len) == 0) { unsigned int limit = 60; sscanf (header_data, "%u\r\n", &limit); client->connection.discon.time = time(NULL) + limit; break; } if (strncasecmp (header, "icecast-slave:", 14) == 0) { client->flags |= CLIENT_IS_SLAVE; break; } if (strncasecmp (header, "icecast-auth-message:", 21) == 0) { snprintf (atd->errormsg, sizeof (atd->errormsg), "%.*s", header_datalen, header_data); break; } if (strncasecmp (header, "ice-username:"******"%s", header_data); free (client->username); client->username = name; } break; } if (strncasecmp (header, "Location:", 9) == 0) { free (atd->location); atd->location = malloc (header_datalen+1); if (atd->location) snprintf (atd->location, header_datalen+1, "%s", header_data); break; } if (strncasecmp (header, "Mountpoint:", 11) == 0) { char *mount = malloc (header_datalen+1); if (mount) { snprintf (mount, header_datalen+1, "%s", header_data); free (auth_user->mount); auth_user->mount = mount; } break; } if (strncasecmp (header, "content-type:", 13) == 0) { format_type_t type = format_get_type (header_data); if (client->refbuf && (type == FORMAT_TYPE_AAC || type == FORMAT_TYPE_MPEG)) { struct build_intro_contents *x = (void*)client->refbuf->data; x->type = type; mpeg_setup (&x->sync, client->connection.ip); } break; } } while (0); return (int)bytes; }
static size_t handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream) { auth_client *auth_user = stream; unsigned bytes = size * nmemb; client_t *client = auth_user->client; auth_thread_data *atd = auth_user->thread_data; if (bytes <= 1) // we should have the EOL at least return bytes; if (client) { auth_t *auth = auth_user->auth; auth_url *url = auth->state; int retcode = 0; char *p = ptr; /* replace the EOL with a nul char, libcurl may not provide a nul */ p[bytes-2] = '\0'; if (sscanf (ptr, "HTTP%*c%*u.%*u %3d %*c", &retcode) == 1) { if (retcode == 403) { char *p = strchr (ptr, ' ') + 1; snprintf (atd->errormsg, sizeof(atd->errormsg), "%s", p); p = strchr (atd->errormsg, '\r'); if (p) *p='\0'; } } if (strncasecmp (ptr, url->auth_header, url->auth_header_len) == 0) { client->flags |= CLIENT_AUTHENTICATED; p = strchr (ptr, ':'); if (p) { ++p; if (strstr (p, "withintro")) client->flags |= CLIENT_HAS_INTRO_CONTENT; if (strstr (p, "hijack")) client->flags |= CLIENT_HIJACKER; if (strstr (p, "0")) { WARN0 ("auth header returned with 0 value"); client->flags &= ~CLIENT_AUTHENTICATED; } } } if (strncasecmp (ptr, url->timelimit_header, url->timelimit_header_len) == 0) { unsigned int limit = 0; sscanf ((char *)ptr+url->timelimit_header_len, "%u\r\n", &limit); client->connection.discon_time = time(NULL) + limit; } if (strncasecmp (ptr, "icecast-slave: 1", 16) == 0) client->flags |= CLIENT_IS_SLAVE; if (strncasecmp (ptr, "icecast-auth-message: ", 22) == 0) { char *eol; snprintf (atd->errormsg, sizeof (atd->errormsg), "%s", (char*)ptr+22); eol = strchr (atd->errormsg, '\r'); if (eol == NULL) eol = strchr (atd->errormsg, '\n'); if (eol) *eol = '\0'; } if (strncasecmp (ptr, "ice-username: "******"\r\n"); char *name = malloc (len+1); if (name) { snprintf (name, len+1, "%s", (char *)ptr+14); free (client->username); client->username = name; } } if (strncasecmp (ptr, "Location: ", 10) == 0) { int len = strcspn ((char*)ptr+10, "\r\n"); free (atd->location); atd->location = malloc (len+1); snprintf (atd->location, len+1, "%s", (char *)ptr+10); } if (strncasecmp (ptr, "Mountpoint: ", 12) == 0) { int len = strcspn ((char*)ptr+12, "\r\n"); char *mount = malloc (len+1); if (mount) { snprintf (mount, len+1, "%s", (char *)ptr+12); free (auth_user->mount); auth_user->mount = mount; } } if (strncasecmp (ptr, "content-type: ", 14) == 0) { format_type_t type = format_get_type ((char*)ptr+14); if (client->refbuf && (type == FORMAT_TYPE_AAC || type == FORMAT_TYPE_MPEG)) { struct build_intro_contents *x = (void*)client->refbuf->data; x->type = type; mpeg_setup (&x->sync, client->connection.ip); } } } return (int)bytes; }
/* 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) { 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 *mountproxy = config->mounts; 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 (source->client) client_send_404 (source->client, "Content-type not supported"); 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_MP3; } if (format_get_plugin (format_type, source) < 0) { global_unlock(); config_release_config(); if (source->client) client_send_404 (source->client, "internal format allocation problem"); WARN1 ("plugin format failed for \"%s\"", source->mount); return -1; } global.sources++; global_unlock(); /* set global settings first */ source->queue_size_limit = config->queue_size_limit; source->timeout = config->source_timeout; source->burst_size = config->burst_size; /* for relays, we don't yet have a client, however we do require one * to retrieve the stream from. This is created here, quite late, * because we can't use this client to return an error code/message, * so we only do this once we know we're going to accept the source. */ if (source->client == NULL) source->client = client_create (source->con, source->parser); while (mountproxy) { if (strcmp (mountproxy->mountname, source->mount) == 0) { source_apply_mount (source, mountproxy); break; } mountproxy = mountproxy->next; } 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 (source->client) client_send_404 (source->client, "too many sources connected"); return -1; }