/* return 0 for failed, 1 for ok */ int connection_check_pass (http_parser_t *parser, const char *user, const char *pass) { int ret; const char *protocol; if(!pass) { WARN0("No source password set, rejecting source"); return -1; } protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL); if(protocol != NULL && !strcmp(protocol, "ICY")) { ret = _check_pass_icy(parser, pass); } else { ret = _check_pass_http(parser, user, pass); if (!ret) { ice_config_t *config = config_get_config_unlocked(); if (config->ice_login) { ret = _check_pass_ice(parser, pass); if(ret) WARN0("Source is using deprecated icecast login"); } } } return ret; }
static void destroy_yp_server (struct yp_server *server) { if (server == NULL) return; DEBUG1 ("Removing YP server entry for %s", server->url); if (server->curl) curl_easy_cleanup (server->curl); if (server->mounts) WARN0 ("active ypdata not freed up"); if (server->pending_mounts) WARN0 ("pending ypdata not freed up"); free (server->url); free (server); }
/* only called for native icecast source clients */ static void _handle_source_request (client_t *client, const char *uri) { INFO1("Source logging in at mountpoint \"%s\"", uri); if (uri[0] != '/') { WARN0 ("source mountpoint not starting with /"); client_send_401 (client); return; } switch (client_check_source_auth (client, uri)) { case 0: /* authenticated from config file */ source_startup (client, uri, ICECAST_SOURCE_AUTH); break; case 1: /* auth pending */ break; default: /* failed */ INFO1("Source (%s) attempted to login with invalid or missing password", uri); client_send_401(client); break; } }
static void admin_handle_general_request(client_t *client, int command) { switch(command) { case COMMAND_RAW_STATS: command_stats(client, NULL, RAW); break; case COMMAND_RAW_LIST_MOUNTS: command_list_mounts(client, RAW); break; case COMMAND_RAW_LISTSTREAM: command_list_mounts(client, RAW); break; case COMMAND_PLAINTEXT_LISTSTREAM: command_list_mounts(client, PLAINTEXT); break; case COMMAND_TRANSFORMED_STATS: command_stats(client, NULL, TRANSFORMED); break; case COMMAND_TRANSFORMED_LIST_MOUNTS: command_list_mounts(client, TRANSFORMED); break; case COMMAND_TRANSFORMED_LISTSTREAM: command_list_mounts(client, TRANSFORMED); break; case COMMAND_TRANSFORMED_MOVE_CLIENTS: command_list_mounts(client, TRANSFORMED); break; default: WARN0("General admin request not recognised"); client_send_400(client, "Unknown admin request"); return; } }
static int _parse_alias (xmlNodePtr node, void *arg) { ice_config_t *config = arg; aliases **cur, *alias = calloc (1, sizeof (aliases)); xmlChar *temp; alias->source = (char *)xmlGetProp (node, XMLSTR ("source")); alias->destination = (char *)xmlGetProp (node, XMLSTR ("dest")); if (alias->source == NULL || alias->destination == NULL) { if (alias->source) xmlFree (alias->source); if (alias->destination) xmlFree (alias->destination); free (alias); WARN0 ("incomplete alias definition"); return -1; } alias->bind_address = (char *)xmlGetProp (node, XMLSTR("bind-address")); temp = xmlGetProp(node, XMLSTR("port")); alias->port = -1; if (temp) { alias->port = atoi ((char*)temp); xmlFree (temp); } cur = &config->aliases; while (*cur) cur = &((*cur)->next); *cur = alias; return 0; }
/* helper function for reading data from a client */ int client_read_bytes (client_t *client, void *buf, unsigned len) { int bytes; if (client->refbuf && client->refbuf->len) { /* we have data to read from a refbuf first */ if (client->refbuf->len < len) len = client->refbuf->len; memcpy (buf, client->refbuf->data, len); if (len < client->refbuf->len) { char *ptr = client->refbuf->data; memmove (ptr, ptr+len, client->refbuf->len - len); } client->refbuf->len -= len; return len; } bytes = sock_read_bytes (client->con->sock, buf, len); if (bytes > 0) return bytes; if (bytes < 0) { if (sock_recoverable (sock_error())) return -1; WARN0 ("source connection has died"); } client->con->error = 1; return -1; }
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; }
int connection_check_source_pass(http_parser_t *parser, char *mount) { ice_config_t *config = config_get_config(); char *pass = config->source_password; char *user = "******"; int ret; int ice_login = config->ice_login; char *protocol; mount_proxy *mountinfo = config->mounts; thread_mutex_lock(&(config_locks()->mounts_lock)); while(mountinfo) { if(!strcmp(mountinfo->mountname, mount)) { if(mountinfo->password) pass = mountinfo->password; if(mountinfo->username) user = mountinfo->username; break; } mountinfo = mountinfo->next; } thread_mutex_unlock(&(config_locks()->mounts_lock)); if(!pass) { WARN0("No source password set, rejecting source"); config_release_config(); return 0; } protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL); if(protocol != NULL && !strcmp(protocol, "ICY")) { ret = _check_pass_icy(parser, pass); } else { ret = _check_pass_http(parser, user, pass); if(!ret && ice_login) { ret = _check_pass_ice(parser, pass); if(ret) WARN0("Source is using deprecated icecast login"); } } config_release_config(); return ret; }
static void _handle_source_request (client_t *client, char *uri, int auth_style) { source_t *source; INFO1("Source logging in at mountpoint \"%s\"", uri); if (uri[0] != '/') { WARN0 ("source mountpoint not starting with /"); client_send_401 (client); return; } if (auth_style == ICECAST_SOURCE_AUTH) { if (connection_check_source_pass (client->parser, uri) == 0) { /* We commonly get this if the source client is using the wrong * protocol: attempt to diagnose this and return an error */ /* TODO: Do what the above comment says */ INFO1("Source (%s) attempted to login with invalid or missing password", uri); client_send_401(client); return; } } source = source_reserve (uri); if (source) { if (auth_style == SHOUTCAST_SOURCE_AUTH) { source->shoutcast_compat = 1; } source->client = client; source->parser = client->parser; source->con = client->con; if (connection_complete_source (source, 1) < 0) { source_clear_source (source); source_free_source (source); } else { refbuf_t *ok = refbuf_new (PER_CLIENT_REFBUF_SIZE); client->respcode = 200; snprintf (ok->data, PER_CLIENT_REFBUF_SIZE, "HTTP/1.0 200 OK\r\n\r\n"); ok->len = strlen (ok->data); /* we may have unprocessed data read in, so don't overwrite it */ ok->associated = client->refbuf; client->refbuf = ok; fserve_add_client_callback (client, source_client_callback, source); } } else { client_send_404 (client, "Mountpoint in use"); WARN1 ("Mountpoint %s in use", uri); } }
static void write_mp3_to_file (struct source_tag *source, refbuf_t *refbuf) { if (refbuf->len == 0) return; if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) < (size_t)refbuf->len) { WARN0 ("Write to dump file failed, disabling"); fclose (source->dumpfile); source->dumpfile = NULL; } }
/* get some data from the source. The stream data is placed in a refbuf * and sent back, however NULL is also valid as in the case of a short * timeout and there's no data pending. */ static refbuf_t *get_next_buffer (source_t *source) { refbuf_t *refbuf = NULL; int delay = 250; if (source->short_delay) delay = 0; while (global.running == ICE_RUNNING && source->running) { int fds; time_t current = time (NULL); fds = util_timed_wait_for_fd (source->con->sock, delay); if (fds < 0) { if (! sock_recoverable (sock_error())) { WARN0 ("Error while waiting on socket, Disconnecting source"); source->running = 0; } break; } if (fds == 0) { if (source->last_read + (time_t)source->timeout < current) { DEBUG3 ("last %ld, timeout %ld, now %ld", source->last_read, source->timeout, current); WARN0 ("Disconnecting source due to socket timeout"); source->running = 0; } break; } source->last_read = current; refbuf = source->format->get_buffer (source); if (refbuf) break; } return refbuf; }
void client_destroy(client_t *client) { if (client == NULL) return; if (client->worker) { WARN0 ("client still on worker thread"); return; } /* release the buffer now, as the buffer could be on the source queue * and may of disappeared after auth completes */ if (client->refbuf) { refbuf_release (client->refbuf); client->refbuf = NULL; } if (client->flags & CLIENT_AUTHENTICATED) DEBUG1 ("client still in auth \"%s\"", httpp_getvar (client->parser, HTTPP_VAR_URI)); /* write log entry if ip is set (some things don't set it, like outgoing * slave requests */ if (client->respcode > 0 && client->parser) logging_access(client); if (client->flags & CLIENT_IP_BAN_LIFT) { INFO1 ("lifting IP ban on client at %s", client->connection.ip); connection_release_banned_ip (client->connection.ip); client->flags &= ~CLIENT_IP_BAN_LIFT; } connection_close (&client->connection); if (client->parser) httpp_destroy (client->parser); global_lock (); global.clients--; stats_event_args (NULL, "clients", "%d", global.clients); config_clear_listener (client->server_conn); global_unlock (); /* we need to free client specific format data (if any) */ if (client->free_client_data) client->free_client_data (client); free(client->username); free(client->password); free(client); }
static int write_ogg_data (struct source_tag *source, refbuf_t *refbuf) { int ret = 1; if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) != refbuf->len) { WARN0 ("Write to dump file failed, disabling"); fclose (source->dumpfile); source->dumpfile = NULL; ret = 0; } return ret; }
static void _handle_source_request(connection_t *con, http_parser_t *parser, char *uri) { client_t *client; source_t *source; client = client_create(con, parser); INFO1("Source logging in at mountpoint \"%s\"", uri); if (uri[0] != '/') { WARN0 ("source mountpoint not starting with /"); client_send_401 (client); return; } if (!connection_check_source_pass(parser, uri)) { /* We commonly get this if the source client is using the wrong * protocol: attempt to diagnose this and return an error */ /* TODO: Do what the above comment says */ INFO1("Source (%s) attempted to login with invalid or missing password", uri); client_send_401(client); return; } source = source_reserve (uri); if (source) { source->client = client; source->parser = parser; source->con = con; if (connection_complete_source (source) < 0) { source->client = NULL; source_free_source (source); } else thread_create ("Source Thread", source_client_thread, source, THREAD_DETACHED); } else { client_send_404 (client, "Mountpoint in use"); WARN1 ("Mountpoint %s in use", uri); } }
/* return the mount details that match the supplied mountpoint */ mount_proxy *config_find_mount (ice_config_t *config, const char *mount) { mount_proxy *mountinfo = config->mounts, *to_return = NULL; if (mount == NULL) { WARN0 ("no mount name provided"); return NULL; } while (mountinfo) { if (fnmatch (mountinfo->mountname, mount, 0) == 0) to_return = mountinfo; mountinfo = mountinfo->next; } if (mountinfo == NULL) mountinfo = to_return; return mountinfo; }
void Mailbox::erase(unsigned int idx) { mdir.rewind(); const char* dentry = mdir.read(); for(unsigned i = 0; i < idx && (dentry = mdir.read());) if (dentry[0] != '.') ++i; if (!dentry) { WARN0("index out of range"); return; } std::string fname(path); fname += '/'; fname += dentry; if (std::remove(fname.c_str()) < 0) INFO2("could not remove file \"%s\" (std::remove: %s)", fname.c_str(), std::strerror(errno)); }
static int get_authenticator (auth_t *auth, config_options_t *options) { if (auth->type == NULL) { WARN0 ("no authentication type defined"); return -1; } do { DEBUG1 ("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 ERROR0 ("Auth URL disabled"); return -1; #endif } if (strcmp (auth->type, "htpasswd") == 0) { if (auth_get_htpasswd_auth (auth, options) < 0) return -1; break; } ERROR1("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; }
/* Add a listener. Check for any mount information that states any * authentication to be used. */ void auth_add_listener (const char *mount, client_t *client) { mount_proxy *mountinfo; ice_config_t *config = config_get_config(); mountinfo = config_find_mount (config, mount); if (mountinfo && mountinfo->no_mount) { config_release_config (); client_send_403 (client, "mountpoint unavailable"); return; } if (mountinfo && mountinfo->auth) { auth_client *auth_user; if (mountinfo->auth->pending_count > 100) { config_release_config (); WARN0 ("too many clients awaiting authentication"); client_send_403 (client, "busy, please try again later"); return; } auth_user = auth_client_setup (mount, client); auth_user->process = auth_new_listener; INFO0 ("adding client for authentication"); queue_auth_client (auth_user, mountinfo); config_release_config (); } else { int ret = add_authenticated_listener (mount, mountinfo, client); config_release_config (); if (ret < 0) client_send_403 (client, "max listeners reached"); } }
static int get_authenticator (auth_t *auth, config_options_t *options) { if (auth->type == NULL) { WARN0 ("no authentication type defined"); return -1; } do { DEBUG1 ("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 ERROR0 ("Auth URL disabled, no libcurl support"); return -1; #endif } if (strcmp (auth->type, "command") == 0) { #ifdef WIN32 ERROR1("Authenticator type: \"%s\" not supported on win32 platform", auth->type); return -1; #else if (auth_get_cmd_auth (auth, options) < 0) return -1; break; #endif } if (strcmp (auth->type, "htpasswd") == 0) { if (auth_get_htpasswd_auth (auth, options) < 0) return -1; break; } ERROR1("Unrecognised authenticator type: \"%s\"", auth->type); return -1; } while (0); while (options) { if (strcmp (options->name, "allow_duplicate_users") == 0) auth->flags |= atoi (options->value) ? AUTH_ALLOW_LISTENER_DUP : 0; else if (strcmp(options->name, "realm") == 0) auth->realm = (char*)xmlStrdup (XMLSTR(options->value)); else if (strcmp(options->name, "drop_existing_listener") == 0) auth->flags |= atoi (options->value) ? AUTH_DEL_EXISTING_LISTENER : 0; else if (strcmp (options->name, "rejected_mount") == 0) auth->rejected_mount = (char*)xmlStrdup (XMLSTR(options->value)); else if (strcmp(options->name, "handlers") == 0) auth->handlers = atoi (options->value); options = options->next; } if (auth->handlers < 1) auth->handlers = 3; if (auth->handlers > 100) auth->handlers = 100; return 0; }
/* 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 sock_t wait_for_serversock (void) { #ifdef HAVE_POLL int i, ret; struct pollfd ufds [global.server_sockets + 1]; for(i=0; i < global.server_sockets; i++) { ufds[i].fd = global.serversock[i]; ufds[i].events = POLLIN; ufds[i].revents = 0; } #ifdef HAVE_SIGNALFD ufds[i].revents = 0; if (sigfd >= 0) { ufds[i].fd = sigfd; ufds[i].events = POLLIN; ret = poll(ufds, i+1, 4000); } else ret = poll(ufds, i, 4000); #else ret = poll(ufds, global.server_sockets, 333); #endif if (ret <= 0) return SOCK_ERROR; else { int dst; #ifdef HAVE_SIGNALFD if (ufds[i].revents & POLLIN) { struct signalfd_siginfo fdsi; int ret = read (sigfd, &fdsi, sizeof(struct signalfd_siginfo)); if (ret == sizeof(struct signalfd_siginfo)) { switch (fdsi.ssi_signo) { case SIGINT: case SIGTERM: DEBUG0 ("signalfd received a termination"); global.running = ICE_HALTING; connection_running = 0; break; case SIGHUP: INFO0 ("HUP received, reread scheduled"); global.schedule_config_reread = 1; break; default: WARN1 ("unexpected signal (%d)", fdsi.ssi_signo); } } } if (ufds[i].revents & (POLLNVAL|POLLERR)) { ERROR0 ("signalfd descriptor became invalid, doing thread restart"); slave_restart(); // something odd happened } #endif for(i=0; i < global.server_sockets; i++) { if(ufds[i].revents & POLLIN) return ufds[i].fd; if(ufds[i].revents & (POLLHUP|POLLERR|POLLNVAL)) { if (ufds[i].revents & (POLLHUP|POLLERR)) { sock_close (global.serversock[i]); WARN0("Had to close a listening socket"); } global.serversock[i] = SOCK_ERROR; } } /* remove any closed sockets */ for(i=0, dst=0; i < global.server_sockets; i++) { if (global.serversock[i] == SOCK_ERROR) continue; if (i!=dst) global.serversock[dst] = global.serversock[i]; dst++; } global.server_sockets = dst; return SOCK_ERROR; } #else fd_set rfds; struct timeval tv; int i, ret; sock_t max = SOCK_ERROR; FD_ZERO(&rfds); for(i=0; i < global.server_sockets; i++) { FD_SET(global.serversock[i], &rfds); if (max == SOCK_ERROR || global.serversock[i] > max) max = global.serversock[i]; } tv.tv_sec = 0; tv.tv_usec = 333000; ret = select(max+1, &rfds, NULL, NULL, &tv); if(ret < 0) { return SOCK_ERROR; } else if(ret == 0) { return SOCK_ERROR; } else { for(i=0; i < global.server_sockets; i++) { if(FD_ISSET(global.serversock[i], &rfds)) return global.serversock[i]; } return SOCK_ERROR; /* Should be impossible, stop compiler warnings */ } #endif }
static sock_t wait_for_serversock(int timeout) { #ifdef HAVE_POLL struct pollfd ufds [global.server_sockets]; int i, ret; for(i=0; i < global.server_sockets; i++) { ufds[i].fd = global.serversock[i]; ufds[i].events = POLLIN; ufds[i].revents = 0; } ret = poll(ufds, global.server_sockets, timeout); if(ret < 0) { return SOCK_ERROR; } else if(ret == 0) { return SOCK_ERROR; } else { int dst; for(i=0; i < global.server_sockets; i++) { if(ufds[i].revents & POLLIN) return ufds[i].fd; if(ufds[i].revents & (POLLHUP|POLLERR|POLLNVAL)) { if (ufds[i].revents & (POLLHUP|POLLERR)) { sock_close (global.serversock[i]); WARN0("Had to close a listening socket"); } global.serversock[i] = SOCK_ERROR; } } /* remove any closed sockets */ for(i=0, dst=0; i < global.server_sockets; i++) { if (global.serversock[i] == SOCK_ERROR) continue; if (i!=dst) global.serversock[dst] = global.serversock[i]; dst++; } global.server_sockets = dst; return SOCK_ERROR; } #else fd_set rfds; struct timeval tv, *p=NULL; int i, ret; sock_t max = SOCK_ERROR; FD_ZERO(&rfds); for(i=0; i < global.server_sockets; i++) { FD_SET(global.serversock[i], &rfds); if (max == SOCK_ERROR || global.serversock[i] > max) max = global.serversock[i]; } if(timeout >= 0) { tv.tv_sec = timeout/1000; tv.tv_usec = (timeout % 1000) * 1000; p = &tv; } ret = select(max+1, &rfds, NULL, NULL, p); if(ret < 0) { return SOCK_ERROR; } else if(ret == 0) { return SOCK_ERROR; } else { for(i=0; i < global.server_sockets; i++) { if(FD_ISSET(global.serversock[i], &rfds)) return global.serversock[i]; } return SOCK_ERROR; /* Should be impossible, stop compiler warnings */ } #endif }
static int _parse_root (xmlNodePtr node, ice_config_t *config) { char *bindaddress = NULL; struct cfg_tag icecast_tags[] = { { "location", config_get_str, &config->location }, { "admin", config_get_str, &config->admin }, { "server_id", config_get_str, &config->server_id }, { "server-id", config_get_str, &config->server_id }, { "source-password", config_get_str, &config->source_password }, { "hostname", config_get_str, &config->hostname }, { "port", config_get_int, &config->port }, { "bind-address", config_get_str, &bindaddress }, { "fileserve", config_get_bool, &config->fileserve }, { "relays-on-demand", config_get_bool, &config->on_demand }, { "master-server", config_get_str, &config->master_server }, { "master-username", config_get_str, &config->master_username }, { "master-password", config_get_str, &config->master_password }, { "master-bind", config_get_str, &config->master_bind }, { "master-server-port", config_get_int, &config->master_server_port }, { "master-update-interval", config_get_int, &config->master_update_interval }, { "master-relay-auth", config_get_bool, &config->master_relay_auth }, { "master-ssl-port", config_get_int, &config->master_ssl_port }, { "master-redirect", config_get_bool, &config->master_redirect }, { "max-redirect-slaves",config_get_int, &config->max_redirects }, { "redirect", _parse_redirect, config }, { "shoutcast-mount", config_get_str, &config->shoutcast_mount }, { "listen-socket", _parse_listen_sock, config }, { "limits", _parse_limits, config }, { "relay", _parse_relay, config }, { "mount", _parse_mount, config }, { "master", _parse_master, config }, { "directory", _parse_directory, config }, { "paths", _parse_paths, config }, { "logging", _parse_logging, config }, { "security", _parse_security, config }, { "authentication", _parse_authentication, config}, { NULL, NULL, NULL } }; config->master_relay_auth = 1; if (parse_xml_tags (node, icecast_tags)) return -1; if (config->max_redirects == 0 && config->master_redirect) config->max_redirects = 1; if (config->listen_sock_count == 0) { if (config->port) { listener_t *listener = calloc (1, sizeof(listener_t)); listener->refcount = 1; listener->port = config->port; listener->qlen = ICE_LISTEN_QUEUE; listener->bind_address = (char*)xmlStrdup (XMLSTR(bindaddress)); listener->next = config->listen_sock; config->listen_sock = listener; config->listen_sock_count++; } else { WARN0 ("No listen-socket defintions"); return -1; } } return 0; }
static void admin_handle_mount_request(client_t *client, source_t *source, int command) { switch(command) { case COMMAND_RAW_STATS: command_stats(client, source->mount, RAW); break; case COMMAND_RAW_FALLBACK: command_fallback(client, source, RAW); break; case COMMAND_RAW_METADATA_UPDATE: command_metadata(client, source, RAW); break; case COMMAND_TRANSFORMED_METADATA_UPDATE: command_metadata(client, source, TRANSFORMED); break; case COMMAND_SHOUTCAST_METADATA_UPDATE: command_shoutcast_metadata(client, source); break; case COMMAND_RAW_SHOW_LISTENERS: command_show_listeners(client, source, RAW); break; case COMMAND_RAW_MOVE_CLIENTS: command_move_clients(client, source, RAW); break; case COMMAND_RAW_KILL_CLIENT: command_kill_client(client, source, RAW); break; case COMMAND_RAW_KILL_SOURCE: command_kill_source(client, source, RAW); break; case COMMAND_TRANSFORMED_STATS: command_stats(client, source->mount, TRANSFORMED); break; case COMMAND_TRANSFORMED_FALLBACK: command_fallback(client, source, RAW); break; case COMMAND_TRANSFORMED_SHOW_LISTENERS: command_show_listeners(client, source, TRANSFORMED); break; case COMMAND_TRANSFORMED_MOVE_CLIENTS: command_move_clients(client, source, TRANSFORMED); break; case COMMAND_TRANSFORMED_KILL_CLIENT: command_kill_client(client, source, TRANSFORMED); break; case COMMAND_TRANSFORMED_KILL_SOURCE: command_kill_source(client, source, TRANSFORMED); break; case COMMAND_TRANSFORMED_MANAGEAUTH: command_manageauth(client, source, TRANSFORMED); break; case COMMAND_RAW_MANAGEAUTH: command_manageauth(client, source, RAW); break; case COMMAND_TRANSFORMED_UPDATEMETADATA: command_updatemetadata(client, source, TRANSFORMED); break; case COMMAND_RAW_UPDATEMETADATA: command_updatemetadata(client, source, RAW); break; default: WARN0("Mount request not recognised"); client_send_400(client, "Mount request unknown"); break; } }
/* handle incoming page. as the stream is being rebuilt, we need to * add all pages from the stream before processing packets */ static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page) { ogg_packet header; vorbis_codec_t *source_vorbis = codec->specific; char *comment; if (ogg_stream_pagein (&codec->os, page) < 0) { ogg_info->error = 1; return NULL; } if (codec->headers == 3) return NULL; while (codec->headers < 3) { /* now, lets extract the packets */ DEBUG1 ("processing incoming header packet (%d)", codec->headers); if (ogg_stream_packetout (&codec->os, &header) <= 0) { if (ogg_info->codecs->next) format_ogg_attach_header (ogg_info, page); return NULL; } /* change comments here if need be */ if (vorbis_synthesis_headerin (&source_vorbis->vi, &source_vorbis->vc, &header) < 0) { ogg_info->error = 1; WARN0 ("Problem parsing ogg vorbis header"); return NULL; } header.granulepos = 0; source_vorbis->header [codec->headers] = copy_ogg_packet (&header); codec->headers++; } DEBUG0 ("we have the header packets now"); /* if vorbis is the only codec then allow rebuilding of the streams */ if (ogg_info->codecs->next == NULL) { /* set queued vorbis pages to contain about 1/2 of a second worth of samples */ source_vorbis->page_samples_trigger = source_vorbis->vi.rate / 2; source_vorbis->process_packet = process_vorbis_headers; } else { format_ogg_attach_header (ogg_info, &source_vorbis->bos_page); format_ogg_attach_header (ogg_info, page); codec->process_page = process_vorbis_passthru_page; } free (ogg_info->title); comment = vorbis_comment_query (&source_vorbis->vc, "TITLE", 0); if (comment) ogg_info->title = strdup (comment); else ogg_info->title = NULL; free (ogg_info->artist); comment = vorbis_comment_query (&source_vorbis->vc, "ARTIST", 0); if (comment) ogg_info->artist = strdup (comment); else ogg_info->artist = NULL; ogg_info->log_metadata = 1; stats_event_args (ogg_info->mount, "audio_samplerate", "%ld", (long)source_vorbis->vi.rate); stats_event_args (ogg_info->mount, "audio_channels", "%ld", (long)source_vorbis->vi.channels); stats_event_args (ogg_info->mount, "audio_bitrate", "%ld", (long)source_vorbis->vi.bitrate_nominal); stats_event_args (ogg_info->mount, "ice-bitrate", "%ld", (long)source_vorbis->vi.bitrate_nominal/1000); return 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); }
void client_destroy(client_t *client) { if (client == NULL) return; if (client->worker) { WARN0 ("client still on worker thread"); return; } /* release the buffer now, as the buffer could be on the source queue * and may of disappeared after auth completes */ if (client->refbuf) { refbuf_release (client->refbuf); client->refbuf = NULL; } if (client->flags & CLIENT_AUTHENTICATED) DEBUG1 ("client still in auth \"%s\"", httpp_getvar (client->parser, HTTPP_VAR_URI)); /* write log entry if ip is set (some things don't set it, like outgoing * slave requests */ if (client->respcode > 0 && client->parser) logging_access(client); if (client->flags & CLIENT_IP_BAN_LIFT) { INFO1 ("lifting IP ban on client at %s", client->connection.ip); connection_release_banned_ip (client->connection.ip); client->flags &= ~CLIENT_IP_BAN_LIFT; } if (client->parser) httpp_destroy (client->parser); /* we need to free client specific format data (if any) */ if (client->free_client_data) client->free_client_data (client); free(client->username); free(client->password); client->username = NULL; client->password = NULL; client->parser = NULL; client->respcode = 0; client->free_client_data = NULL; global_lock (); if (global.running != ICE_RUNNING || client->connection.error || (client->flags & CLIENT_KEEPALIVE) == 0 || client_connected (client) == 0) { global.clients--; stats_event_args (NULL, "clients", "%d", global.clients); config_clear_listener (client->server_conn); global_unlock (); connection_close (&client->connection); free(client); return; } global_unlock (); DEBUG0 ("keepalive detected, placing back onto worker"); client->counter = client->schedule_ms = timing_get_time(); client->connection.con_time = client->schedule_ms/1000; client->connection.discon.time = client->connection.con_time + 7; client->ops = &http_request_ops; client->flags = CLIENT_ACTIVE; client->shared_data = NULL; client->refbuf = NULL; client->pos = 0; client->intro_offset = client->connection.sent_bytes = 0; client_add_worker (client); }
/* 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; }