/* function to handle the re-populating of the avl tree containing IP addresses * for deciding whether a connection of an incoming request is to be dropped. */ static void recheck_ip_file (cache_file_contents *cache) { time_t now = time(NULL); if (now >= cache->file_recheck) { struct stat file_stat; FILE *file = NULL; int count = 0; avl_tree *new_ips; char line [MAX_LINE_LEN]; cache->file_recheck = now + 10; if (cache->filename == NULL) { if (cache->contents) { avl_tree_free (cache->contents, free_filtered_ip); cache->contents = NULL; } return; } if (stat (cache->filename, &file_stat) < 0) { WARN2 ("failed to check status of \"%s\": %s", cache->filename, strerror(errno)); return; } if (file_stat.st_mtime == cache->file_mtime) return; /* common case, no update to file */ cache->file_mtime = file_stat.st_mtime; file = fopen (cache->filename, "r"); if (file == NULL) { WARN2("Failed to open file \"%s\": %s", cache->filename, strerror (errno)); return; } new_ips = avl_tree_new (compare_ip, NULL); while (get_line (file, line, MAX_LINE_LEN)) { char *str; if(!line[0] || line[0] == '#') continue; count++; str = strdup (line); if (str) avl_insert (new_ips, str); } fclose (file); INFO2 ("%d entries read from file \"%s\"", count, cache->filename); if (cache->contents) avl_tree_free (cache->contents, free_filtered_ip); cache->contents = new_ips; } }
static int _delete_fh (void *mapping) { fh_node *fh = mapping; if (fh == &no_file) { ERROR0 ("no file handle free detected"); return 0; } if (fh->refcount) WARN2 ("handle for %s has refcount %d", fh->finfo.mount, fh->refcount); else thread_mutex_destroy (&fh->lock); file_close (&fh->f); if (fh->format) { free (fh->format->mount); format_plugin_clear (fh->format, NULL); free (fh->format); } if (fh->clients) avl_tree_free (fh->clients, NULL); rate_free (fh->out_bitrate); free (fh->finfo.mount); free (fh->finfo.fallback); free (fh); return 1; }
/* create a client_t with the provided connection and parser details. Return * 0 on success, -1 if server limit has been reached. In either case a * client_t is returned just in case a message needs to be returned. Should * be called with global lock held. */ int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser) { ice_config_t *config; client_t *client = (client_t *)calloc(1, sizeof(client_t)); int ret = -1; if (client == NULL) abort(); config = config_get_config (); global.clients++; if (config->client_limit < global.clients) WARN2 ("server client limit reached (%d/%d)", config->client_limit, global.clients); else ret = 0; config_release_config (); stats_event_args (NULL, "clients", "%d", global.clients); client->con = con; client->parser = parser; client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE); client->refbuf->len = 0; /* force reader code to ignore buffer contents */ client->pos = 0; client->write_to_client = format_generic_write_to_client; *c_ptr = client; return ret; }
static connection_t *_accept_connection(void) { int sock; connection_t *con; char *ip; int serversock; serversock = wait_for_serversock(100); if(serversock < 0) return NULL; /* malloc enough room for a full IP address (including ipv6) */ ip = (char *)malloc(MAX_ADDR_LEN); sock = sock_accept(serversock, ip, MAX_ADDR_LEN); if (sock >= 0) { con = connection_create (sock, serversock, ip); if (con == NULL) free (ip); return con; } if (!sock_recoverable(sock_error())) WARN2("accept() failed with error %d: %s", sock_error(), strerror(sock_error())); free(ip); return NULL; }
static uint64_t worker_check_time_ms (worker_t *worker) { uint64_t tm = timing_get_time(); if (tm - worker->time_ms > 1000 && worker->time_ms) WARN2 ("worker %p has been stuck for %" PRIu64 " ms", worker, (tm - worker->time_ms)); return tm; }
int auth_htpasswd_adduser(auth_t *auth, char *username, char *password) { FILE *passwdfile; char *hashed_password = NULL; htpasswd_auth_state *state; if (auth_htpasswd_existing_user(auth, username) == AUTH_USEREXISTS) { return AUTH_USEREXISTS; } state = auth->state; passwdfile = fopen(state->filename, "ab"); if(passwdfile == NULL) { WARN2("Failed to open authentication database \"%s\": %s", state->filename, strerror(errno)); return AUTH_FAILED; } hashed_password = get_hash(password, strlen(password)); if (hashed_password) { fprintf(passwdfile, "%s:%s\n", username, hashed_password); free(hashed_password); } fclose(passwdfile); return AUTH_USERADDED; }
/* function to handle the re-populating of the avl tree containing IP addresses * for deciding whether a connection of an incoming request is to be dropped. */ static void recheck_cached_file (cache_file_contents *cache, time_t now) { if (now >= cache->file_recheck) { struct stat file_stat; FILE *file = NULL; int count = 0; char line [MAX_LINE_LEN]; cache->file_recheck = now + 10; if (cache->filename == NULL) { cache_prune (cache); return; } if (stat (cache->filename, &file_stat) < 0) { WARN2 ("failed to check status of \"%s\": %s", cache->filename, strerror(errno)); return; } if (file_stat.st_mtime == cache->file_mtime) return; /* common case, no update to file */ cache->file_mtime = file_stat.st_mtime; file = fopen (cache->filename, "r"); if (file == NULL) { WARN2("Failed to open file \"%s\": %s", cache->filename, strerror (errno)); return; } cache_prune (cache); cache->contents = avl_tree_new (cache->compare, &cache->file_recheck); while (get_line (file, line, MAX_LINE_LEN)) { if(!line[0] || line[0] == '#') continue; count++; cache->add_new_entry (cache, line, 0); } fclose (file); INFO2 ("%d entries read from file \"%s\"", count, cache->filename); } }
/* Not efficient; opens and scans the entire file for every request */ static auth_result htpasswd_auth(auth_t *auth, source_t *source, char *username, char *password) { htpasswd_auth_state *state = auth->state; FILE *passwdfile = NULL; char line[MAX_LINE_LEN]; char *sep; thread_rwlock_rlock(&state->file_rwlock); if (!state->allow_duplicate_users) { if (auth_is_listener_connected(source, username)) { thread_rwlock_unlock(&state->file_rwlock); return AUTH_FORBIDDEN; } } passwdfile = fopen(state->filename, "rb"); if(passwdfile == NULL) { WARN2("Failed to open authentication database \"%s\": %s", state->filename, strerror(errno)); thread_rwlock_unlock(&state->file_rwlock); return AUTH_FAILED; } while(get_line(passwdfile, line, MAX_LINE_LEN)) { if(!line[0] || line[0] == '#') continue; sep = strchr(line, ':'); if(sep == NULL) { DEBUG0("No seperator in line"); continue; } *sep = 0; if(!strcmp(username, line)) { /* Found our user, now: does the hash of password match hash? */ char *hash = sep+1; char *hashed_password = get_hash(password, strlen(password)); if(!strcmp(hash, hashed_password)) { fclose(passwdfile); free(hashed_password); thread_rwlock_unlock(&state->file_rwlock); return AUTH_OK; } free(hashed_password); /* We don't keep searching through the file */ break; } } fclose(passwdfile); thread_rwlock_unlock(&state->file_rwlock); return AUTH_FAILED; }
/* simple name=tag stat create/update */ void stats_event(const char *source, const char *name, const char *value) { stats_event_t *event; if (value && xmlCheckUTF8 ((unsigned char *)value) == 0) { WARN2 ("seen non-UTF8 data, probably incorrect metadata (%s, %s)", name, value); return; } event = build_event (source, name, value); if (event) queue_global_event (event); }
static void url_stream_end (auth_client *auth_user) { char *mount, *server; ice_config_t *config = config_get_config (); mount_proxy *mountinfo = config_find_mount (config, auth_user->mount); auth_t *auth = mountinfo->auth; auth_url *url = auth->state; char *stream_end_url; int port; char post [4096]; if (url->stream_end == NULL) { config_release_config (); return; } server = util_url_escape (config->hostname); port = config->port; stream_end_url = strdup (url->stream_end); /* we don't want this auth disappearing from under us while * the connection is in progress */ mountinfo->auth->refcount++; config_release_config (); mount = util_url_escape (auth_user->mount); snprintf (post, sizeof (post), "action=mount_remove&mount=%s&server=%s&port=%d", mount, server, port); free (server); free (mount); if (strchr (url->stream_end, '@') == NULL) { if (url->userpwd) curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd); else curl_easy_setopt (url->handle, CURLOPT_USERPWD, ""); } else curl_easy_setopt (url->handle, CURLOPT_USERPWD, ""); curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_end); curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post); curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user); if (curl_easy_perform (url->handle)) WARN2 ("auth to server %s failed with %s", stream_end_url, url->errormsg); auth_release (auth); free (stream_end_url); return; }
int config_get_port (xmlNodePtr node, void *x) { int val = 0, ret = config_get_int (node, &val); if (ret == 0) { if (val < 0 || val > 65535) { WARN2 ("port out of range \"%s\" at line %ld, assuming 8000", node->name, xmlGetLineNo(node)); val = 8000; } *(int*)x = val; } return ret; }
/* helper to apply specialised changes to a stats node */ static void modify_node_event (stats_node_t *node, stats_event_t *event) { char *str; if (event->action == STATS_EVENT_HIDDEN) { if (event->value) node->hidden = 1; else node->hidden = 0; return; } if (event->action != STATS_EVENT_SET) { int64_t value = 0; switch (event->action) { case STATS_EVENT_INC: value = atoi (node->value)+1; break; case STATS_EVENT_DEC: value = atoi (node->value)-1; break; case STATS_EVENT_ADD: value = atoi (node->value)+atoi (event->value); break; case STATS_EVENT_SUB: value = atoll (node->value) - atoll (event->value); break; default: WARN2 ("unhandled event (%d) for %s", event->action, event->source); break; } str = malloc (16); snprintf (str, 16, "%" PRId64, value); if (event->value == NULL) event->value = strdup (str); } else str = (char *)strdup (event->value); free (node->value); node->value = str; if (event->source) DEBUG3 ("update \"%s\" %s (%s)", event->source, node->name, node->value); else DEBUG2 ("update global %s (%s)", node->name, node->value); }
static void get_ssl_certificate (ice_config_t *config) { ssl_ok = 0; do { long ssl_opts; ssl_ctx = NULL; if (config->cert_file == NULL) break; ssl_ctx = SSL_CTX_new (SSLv23_server_method()); ssl_opts = SSL_CTX_get_options (ssl_ctx); SSL_CTX_set_options (ssl_ctx, ssl_opts|SSL_OP_NO_SSLv2); if (SSL_CTX_use_certificate_chain_file (ssl_ctx, config->cert_file) <= 0) { WARN1 ("Invalid cert file %s", config->cert_file); break; } if (SSL_CTX_use_PrivateKey_file (ssl_ctx, config->cert_file, SSL_FILETYPE_PEM) <= 0) { WARN1 ("Invalid private key file %s", config->cert_file); break; } if (!SSL_CTX_check_private_key (ssl_ctx)) { ERROR1 ("Invalid %s - Private key does not match cert public key", config->cert_file); break; } if (SSL_CTX_set_cipher_list(ssl_ctx, config->cipher_list) <= 0) { WARN1 ("Invalid cipher list: %s", config->cipher_list); } ssl_ok = 1; INFO1 ("SSL certificate found at %s", config->cert_file); INFO1 ("SSL using ciphers %s", config->cipher_list); return; } while (0); if (ssl_ctx) { WARN2 ("failed to load cert %s (%s)", config->cert_file, ERR_reason_error_string (ERR_peek_last_error())); SSL_CTX_free (ssl_ctx); ssl_ctx = NULL; } INFO0 ("No SSL capability on any configured ports"); }
int CloseSocket(Socket *sock, int waitForPeer) { fd_set readFDs; Socket sd = *sock; struct timeval timeout; if (debug > 0) { DDEBUG1("CloseSocket: Closing connection %d\n", *sock); } if(*sock == NO_SOCKET) { return 1; /* Already closed; nothing to do. */ } if(waitForPeer > 0) { FD_ZERO(&readFDs); FD_SET(sd, &readFDs); timeout.tv_sec = waitForPeer; timeout.tv_usec = 0; if(select(FD_SETSIZE, &readFDs, NULL, NULL, &timeout) < 0) { FAIL2("CloseSocket: no response on select %d %d\n", sd, errno); } } if(!FD_ISSET(sd, &connectedPipes)) { if(shutdown(sd, 2) < 0) { /* The other side may have beaten us to the reset. */ if ((errno != ENOTCONN) && (errno != ECONNRESET)) { WARN1("CloseSocket: shutdown error %d\n", errno); } } } if(close(sd) < 0) { WARN2("CloseSocket: close error %d (%s)\n", errno, strerror(errno)); } ClearSocket(sd); DoDisconnectNotification(sd); *sock = NO_SOCKET; return(1); }
static xsltStylesheetPtr xslt_get_stylesheet(const char *fn) { int i; int empty = -1; struct stat file; if(stat(fn, &file)) { WARN2("Error checking for stylesheet file \"%s\": %s", fn, strerror(errno)); return NULL; } for(i=0; i < CACHESIZE; i++) { if(cache[i].filename) { #ifdef _WIN32 if(!stricmp(fn, cache[i].filename)) #else if(!strcmp(fn, cache[i].filename)) #endif { if(file.st_mtime > cache[i].last_modified) { xsltFreeStylesheet(cache[i].stylesheet); cache[i].last_modified = file.st_mtime; cache[i].stylesheet = xsltParseStylesheetFile(XMLSTR(fn)); cache[i].cache_age = time(NULL); } DEBUG1("Using cached sheet %i", i); return cache[i].stylesheet; } } else empty = i; } if(empty>=0) i = empty; else i = evict_cache_entry(); cache[i].last_modified = file.st_mtime; cache[i].filename = strdup(fn); cache[i].stylesheet = xsltParseStylesheetFile(XMLSTR(fn)); cache[i].cache_age = time(NULL); return cache[i].stylesheet; }
static void url_stream_auth (auth_client *auth_user) { ice_config_t *config; int port; client_t *client = auth_user->client; auth_url *url = client->auth->state; char *mount, *host, *user, *pass, *ipaddr, *admin=""; char post [4096]; if (strchr (url->stream_auth, '@') == NULL) { if (url->userpwd) curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd); else curl_easy_setopt (url->handle, CURLOPT_USERPWD, ""); } else curl_easy_setopt (url->handle, CURLOPT_USERPWD, ""); curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_auth); curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post); curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user); if (strcmp (auth_user->mount, httpp_getvar (client->parser, HTTPP_VAR_URI)) != 0) admin = "&admin=1"; mount = util_url_escape (auth_user->mount); config = config_get_config (); host = util_url_escape (config->hostname); port = config->port; config_release_config (); user = util_url_escape (client->username); pass = util_url_escape (client->password); ipaddr = util_url_escape (client->con->ip); snprintf (post, sizeof (post), "action=stream_auth&mount=%s&ip=%s&server=%s&port=%d&user=%s&pass=%s%s", mount, ipaddr, host, port, user, pass, admin); free (ipaddr); free (user); free (pass); free (mount); free (host); client->authenticated = 0; if (curl_easy_perform (url->handle)) WARN2 ("auth to server %s failed with %s", url->stream_auth, url->errormsg); }
static void url_stream_auth (auth_client *auth_user) { client_t *client = auth_user->client; auth_url *url = auth_user->auth->state; auth_thread_data *atd = auth_user->thread_data; char *mount, *host, *user, *pass, *ipaddr, *admin=""; char post [4096]; if (strchr (url->stream_auth, '@') == NULL) { if (url->userpwd) curl_easy_setopt (atd->curl, CURLOPT_USERPWD, url->userpwd); else curl_easy_setopt (atd->curl, CURLOPT_USERPWD, ""); } else curl_easy_setopt (atd->curl, CURLOPT_USERPWD, ""); curl_easy_setopt (atd->curl, CURLOPT_URL, url->stream_auth); curl_easy_setopt (atd->curl, CURLOPT_POSTFIELDS, post); curl_easy_setopt (atd->curl, CURLOPT_WRITEHEADER, auth_user); curl_easy_setopt (atd->curl, CURLOPT_WRITEDATA, auth_user); if (strcmp (auth_user->mount, httpp_getvar (client->parser, HTTPP_VAR_URI)) != 0) admin = "&admin=1"; mount = util_url_escape (auth_user->mount); host = util_url_escape (auth_user->hostname); user = util_url_escape (client->username); pass = util_url_escape (client->password); ipaddr = util_url_escape (client->connection.ip); snprintf (post, sizeof (post), "action=stream_auth&mount=%s&ip=%s&server=%s&port=%d&user=%s&pass=%s%s", mount, ipaddr, host, auth_user->port, user, pass, admin); free (ipaddr); free (user); free (pass); free (mount); free (host); client->flags &= ~CLIENT_AUTHENTICATED; if (curl_easy_perform (atd->curl)) WARN2 ("auth to server %s failed with %s", url->stream_auth, atd->errormsg); }
/* printf style formatting for stat create/update */ void stats_event_args(const char *source, char *name, char *format, ...) { va_list val; int ret; char buf[1024]; if (name == NULL) return; va_start(val, format); ret = vsnprintf(buf, sizeof (buf), format, val); va_end(val); if (ret < 0 || (unsigned int)ret >= sizeof (buf)) { WARN2 ("problem with formatting %s stat %s", source==NULL ? "global" : source, name); return; } stats_event(source, name, buf); }
void stats_set_args (long handle, const char *name, const char *format, ...) { va_list val; int ret; stats_source_t *src_stats = (stats_source_t *)handle; char buf[1024]; if (name == NULL) return; va_start (val, format); ret = vsnprintf (buf, sizeof (buf), format, val); va_end (val); if (ret < 0 || (unsigned int)ret >= sizeof (buf)) { WARN2 ("problem with formatting %s stat %s", src_stats == NULL ? "global" : src_stats->source, name); return; } stats_set (handle, name, buf); }
static void url_stream_end (auth_client *auth_user) { char *mount, *server, *ipaddr; client_t *client = auth_user->client; auth_url *url = auth_user->auth->state; auth_thread_data *atd = auth_user->thread_data; char post [4096]; server = util_url_escape (auth_user->hostname); mount = util_url_escape (auth_user->mount); if (client && client->connection.ip) ipaddr = util_url_escape (client->connection.ip); else ipaddr = strdup(""); snprintf (post, sizeof (post), "action=mount_remove&mount=%s&server=%s&port=%d&ip=%s", mount, server, auth_user->port, ipaddr); free (ipaddr); free (server); free (mount); if (strchr (url->stream_end, '@') == NULL) { if (url->userpwd) curl_easy_setopt (atd->curl, CURLOPT_USERPWD, url->userpwd); else curl_easy_setopt (atd->curl, CURLOPT_USERPWD, ""); } else curl_easy_setopt (atd->curl, CURLOPT_USERPWD, ""); curl_easy_setopt (atd->curl, CURLOPT_URL, url->stream_end); curl_easy_setopt (atd->curl, CURLOPT_POSTFIELDS, post); curl_easy_setopt (atd->curl, CURLOPT_WRITEHEADER, auth_user); curl_easy_setopt (atd->curl, CURLOPT_WRITEDATA, auth_user); DEBUG1 ("handler %d sending request", auth_user->handler); if (curl_easy_perform (atd->curl)) WARN2 ("auth to server %s failed with %s", url->stream_end, atd->errormsg); DEBUG1 ("handler %d request finished", auth_user->handler); }
static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password) { FILE *passwdfile; char *hashed_password = NULL; htpasswd_auth_state *state = auth->state; htpasswd_user entry; void *result; htpasswd_recheckfile (state); thread_rwlock_wlock (&state->file_rwlock); entry.name = (char*)username; if (avl_get_by_key (state->users, &entry, &result) == 0) { thread_rwlock_unlock (&state->file_rwlock); return AUTH_USEREXISTS; } passwdfile = fopen(state->filename, "ab"); if (passwdfile == NULL) { thread_rwlock_unlock (&state->file_rwlock); WARN2("Failed to open authentication database \"%s\": %s", state->filename, strerror(errno)); return AUTH_FAILED; } hashed_password = get_hash(password, strlen(password)); if (hashed_password) { fprintf(passwdfile, "%s:%s\n", username, hashed_password); free(hashed_password); } fclose(passwdfile); thread_rwlock_unlock (&state->file_rwlock); return AUTH_USERADDED; }
int auth_get_htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode) { htpasswd_auth_state *state; FILE *passwdfile; char line[MAX_LINE_LEN]; char *sep; char *passwd; xmlNodePtr newnode; state = auth->state; passwdfile = fopen(state->filename, "rb"); if(passwdfile == NULL) { WARN2("Failed to open authentication database \"%s\": %s", state->filename, strerror(errno)); return AUTH_FAILED; } while(get_line(passwdfile, line, MAX_LINE_LEN)) { if(!line[0] || line[0] == '#') continue; sep = strchr(line, ':'); if(sep == NULL) { DEBUG0("No seperator in line"); continue; } *sep = 0; newnode = xmlNewChild(srcnode, NULL, "User", NULL); xmlNewChild(newnode, NULL, "username", line); passwd = sep+1; xmlNewChild(newnode, NULL, "password", passwd); } fclose(passwdfile); return AUTH_OK; }
static connection_t *_accept_connection(int duration) { sock_t sock, serversock; char *ip; serversock = wait_for_serversock (duration); if (serversock == SOCK_ERROR) return NULL; /* malloc enough room for a full IP address (including ipv6) */ ip = (char *)malloc(MAX_ADDR_LEN); sock = sock_accept(serversock, ip, MAX_ADDR_LEN); if (sock != SOCK_ERROR) { connection_t *con = NULL; /* Make any IPv4 mapped IPv6 address look like a normal IPv4 address */ if (strncmp (ip, "::ffff:", 7) == 0) memmove (ip, ip+7, strlen (ip+7)+1); if (accept_ip_address (ip)) con = connection_create (sock, serversock, ip); if (con) return con; sock_close (sock); } else { if (!sock_recoverable(sock_error())) { WARN2("accept() failed with error %d: %s", sock_error(), strerror(sock_error())); thread_sleep (500000); } } free(ip); return NULL; }
int auth_htpasswd_existing_user(auth_t *auth, char *username) { FILE *passwdfile; htpasswd_auth_state *state; int ret = AUTH_OK; char line[MAX_LINE_LEN]; char *sep; state = auth->state; passwdfile = fopen(state->filename, "rb"); if(passwdfile == NULL) { WARN2("Failed to open authentication database \"%s\": %s", state->filename, strerror(errno)); return AUTH_FAILED; } while(get_line(passwdfile, line, MAX_LINE_LEN)) { if(!line[0] || line[0] == '#') continue; sep = strchr(line, ':'); if(sep == NULL) { DEBUG0("No seperator in line"); continue; } *sep = 0; if (!strcmp(username, line)) { /* We found the user, break out of the loop */ ret = AUTH_USEREXISTS; break; } } fclose(passwdfile); return ret; }
/* 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); }
static void remove_fh_from_cache (fh_node *fh) { if (fh->refcount) WARN2 ("removing %s with %d still on", fh->finfo.mount, fh->refcount); avl_delete (fh_cache, fh, NULL); }
static int xslt_cached (const char *fn, client_t *client) { worker_t *worker = client->worker; time_t now = worker->current_time.tv_sec, oldest = now; int evict = 0, i; struct stat file; for(i=0; i < CACHESIZE; i++) { if(cache[i].filename) { #ifdef _WIN32 if (stricmp(fn, cache[i].filename) == 0) #else if (strcmp(fn, cache[i].filename) == 0) #endif { if (now - cache[i].last_checked > 10) { cache[i].last_checked = now; if (stat (fn, &file)) { WARN2("Error checking for stylesheet file \"%s\": %s", fn, strerror(errno)); return i; } DEBUG1 ("rechecked file time on %s", fn); thread_spin_lock (&update_lock); if (file.st_mtime > cache[i].last_modified) { cache[i].last_modified = file.st_mtime; thread_spin_unlock (&update_lock); break; } } thread_spin_unlock (&update_lock); cache[i].cache_age = now; return i; } if (oldest < cache[i].cache_age) continue; } evict = i; } xsl_req *x = calloc (1, sizeof (xsl_req)); if (i < CACHESIZE) { x->index = i; } else { if (stat (fn, &file)) { WARN2("Error checking for stylesheet file \"%s\": %s", fn, strerror(errno)); free (x); return -2; } x->client = client; x->index = evict; } x->doc = client->shared_data; x->cache.filename = strdup (fn); x->cache.last_modified = file.st_mtime; x->cache.cache_age = now; x->cache.last_checked = now; client->shared_data = x; client->schedule_ms = worker->time_ms; client->ops = &xslt_ops; thread_spin_lock (&update_lock); if (xsl_updating < 3) { xsl_updating++; thread_spin_unlock (&update_lock); if (x->client) client->flags &= ~CLIENT_ACTIVE; thread_create ("update xslt", xslt_update, x, THREAD_DETACHED); if (x->client == NULL) return i; } else { thread_spin_unlock (&update_lock); x->client = client; client->schedule_ms += 10; if ((client->flags & CLIENT_ACTIVE) == 0) { client->flags |= CLIENT_ACTIVE; worker_wakeup (worker); } } return -1; }
/* Add a listener. Check for any mount information that states any * authentication to be used. */ int auth_add_listener (const char *mount, client_t *client) { int ret = 0, need_auth = 1; ice_config_t *config = config_get_config(); mount_proxy *mountinfo = config_find_mount (config, mount); if (client->flags & CLIENT_AUTHENTICATED) need_auth = 0; else { const char *range = httpp_getvar (client->parser, "range"); if (range) { uint64_t pos1 = 0, pos2 = (uint64_t)-1; if (strncmp (range, "bytes=", 6) == 0) { if (sscanf (range+6, "-%" SCNuMAX, &pos2) < 1) if (sscanf (range+6, "%" SCNuMAX "-%" SCNuMAX, &pos1, &pos2) < 1) pos2 = 0; } else pos2 = 0; if (pos2 > 0 && pos1 < pos2) { client->intro_offset = pos1; client->connection.discon.offset = pos2; client->flags |= CLIENT_RANGE_END; if (pos2 - pos1 < 10) need_auth = 0; // avoid auth check if range is very small, player hack } else WARN2 ("client range invalid (%" PRIu64 ", %" PRIu64 "), ignoring", pos1, pos2); } } if (client->parser->req_type == httpp_req_head) { client->flags &= ~CLIENT_AUTHENTICATED; need_auth = 0; } if (need_auth) { if (mountinfo) { auth_t *auth = mountinfo->auth; if (mountinfo->skip_accesslog) client->flags |= CLIENT_SKIP_ACCESSLOG; if (mountinfo->ban_client) { if (mountinfo->ban_client < 0) client->flags |= CLIENT_IP_BAN_LIFT; connection_add_banned_ip (client->connection.ip, mountinfo->ban_client); } if (mountinfo->no_mount) { config_release_config (); return client_send_403 (client, "mountpoint unavailable"); } if (mountinfo->redirect) { char buffer [4096] = ""; unsigned int len = sizeof buffer; if (util_expand_pattern (mount, mountinfo->redirect, buffer, &len) == 0) { config_release_config (); return client_send_302 (client, buffer); } WARN3 ("failed to expand %s on %s for %s", mountinfo->redirect, mountinfo->mountname, mount); return client_send_501 (client); } do { if (auth == NULL) break; if ((auth->flags & AUTH_RUNNING) == 0) break; if (auth->pending_count > 400) { if (auth->flags & AUTH_SKIP_IF_SLOW) break; config_release_config (); WARN0 ("too many clients awaiting authentication"); if (global.new_connections_slowdown < 10) global.new_connections_slowdown++; return client_send_403 (client, "busy, please try again later"); } if (auth->authenticate) { auth_client *auth_user = auth_client_setup (mount, client); auth_user->process = auth_new_listener; client->flags &= ~CLIENT_ACTIVE; DEBUG0 ("adding client for authentication"); queue_auth_client (auth_user, mountinfo); config_release_config (); return 0; } } while (0); } else { if (strcmp (mount, "/admin/streams") == 0) { config_release_config (); return client_send_401 (client, NULL); } } } ret = add_authenticated_listener (mount, mountinfo, client); config_release_config (); return ret; }
static client_t *accept_client (void) { client_t *client = NULL; sock_t sock, serversock = wait_for_serversock (); char addr [200]; if (serversock == SOCK_ERROR) return NULL; sock = sock_accept (serversock, addr, 200); if (sock == SOCK_ERROR) { if (sock_recoverable (sock_error())) return NULL; WARN2 ("accept() failed with error %d: %s", sock_error(), strerror(sock_error())); thread_sleep (500000); return NULL; } do { int i, num; refbuf_t *r; if (sock_set_blocking (sock, 0) || sock_set_nodelay (sock)) { WARN0 ("failed to set tcp options on client connection, dropping"); break; } client = calloc (1, sizeof (client_t)); if (client == NULL || connection_init (&client->connection, sock, addr) < 0) break; client->shared_data = r = refbuf_new (PER_CLIENT_REFBUF_SIZE); r->len = 0; // for building up the request coming in global_lock (); client_register (client); for (i=0; i < global.server_sockets; i++) { if (global.serversock[i] == serversock) { client->server_conn = global.server_conn[i]; client->server_conn->refcount++; if (client->server_conn->ssl && ssl_ok) connection_uses_ssl (&client->connection); if (client->server_conn->shoutcast_compat) client->ops = &shoutcast_source_ops; else client->ops = &http_request_ops; break; } } num = global.clients; global_unlock (); stats_event_args (NULL, "clients", "%d", num); client->flags |= CLIENT_ACTIVE; return client; } while (0); free (client); sock_close (sock); return NULL; }
static auth_result url_add_listener (auth_client *auth_user) { client_t *client = auth_user->client; auth_t *auth = client->auth; auth_url *url = auth->state; int res = 0, port; const char *agent; char *user_agent, *username, *password; const char *mountreq; char *mount, *ipaddr, *server; ice_config_t *config; char *userpwd = NULL, post [4096]; if (url->addurl == NULL) return AUTH_OK; config = config_get_config (); server = util_url_escape (config->hostname); port = config->port; config_release_config (); agent = httpp_getvar (client->parser, "user-agent"); if (agent == NULL) agent = "-"; user_agent = util_url_escape (agent); if (client->username) username = util_url_escape (client->username); else username = strdup (""); if (client->password) password = util_url_escape (client->password); else password = strdup (""); /* get the full uri (with query params if available) */ mountreq = httpp_getvar (client->parser, HTTPP_VAR_RAWURI); if (mountreq == NULL) mountreq = httpp_getvar (client->parser, HTTPP_VAR_URI); mount = util_url_escape (mountreq); ipaddr = util_url_escape (client->con->ip); snprintf (post, sizeof (post), "action=listener_add&server=%s&port=%d&client=%lu&mount=%s" "&user=%s&pass=%s&ip=%s&agent=%s", server, port, client->con->id, mount, username, password, ipaddr, user_agent); free (server); free (mount); free (user_agent); free (username); free (password); free (ipaddr); if (strchr (url->addurl, '@') == NULL) { if (url->userpwd) curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd); else { /* auth'd requests may not have a user/pass, but may use query args */ if (client->username && client->password) { int len = strlen (client->username) + strlen (client->password) + 2; userpwd = amalloc (len); snprintf (userpwd, len, "%s:%s", client->username, client->password); curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd); } else curl_easy_setopt (url->handle, CURLOPT_USERPWD, ""); } } else { /* url has user/pass but libcurl may need to clear any existing settings */ curl_easy_setopt (url->handle, CURLOPT_USERPWD, ""); } curl_easy_setopt (url->handle, CURLOPT_URL, url->addurl); curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post); curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user); url->errormsg[0] = '\0'; res = curl_easy_perform (url->handle); free (userpwd); if (res) { WARN2 ("auth to server %s failed with %s", url->addurl, url->errormsg); return AUTH_FAILED; } /* we received a response, lets see what it is */ if (client->authenticated) return AUTH_OK; INFO2 ("client auth (%s) failed with \"%s\"", url->addurl, url->errormsg); return AUTH_FAILED; }