Пример #1
0
/* Add listener to the pending lists of either the source or fserve thread. This can be run
 * from the connection or auth thread context. return -1 to indicate that client has been
 * terminated, 0 for receiving content.
 */
static int add_authenticated_listener (const char *mount, mount_proxy *mountinfo, client_t *client)
{
    int ret = 0;

    if (client->parser->req_type != httpp_req_head)
        client->flags |= CLIENT_AUTHENTICATED;

    /* some win32 setups do not do TCP win scaling well, so allow an override */
    if (mountinfo && mountinfo->so_sndbuf > 0)
        sock_set_send_buffer (client->connection.sock, mountinfo->so_sndbuf);

    /* check whether we are processing a streamlist request for slaves */
    if (strcmp (mount, "/admin/streams") == 0)
    {
        client->flags |= CLIENT_IS_SLAVE;
        if (client->parser->req_type == httpp_req_stats)
        {
            stats_add_listener (client, STATS_SLAVE|STATS_GENERAL);
            return 0;
        }
        mount = httpp_get_query_param (client->parser, "mount");
        if (mount == NULL)
        {
            command_list_mounts (client, TEXT);
            return 0;
        }
        mountinfo = config_find_mount (config_get_config_unlocked(), mount);
    }

    /* Here we are parsing the URI request to see if the extension is .xsl, if
     * so, then process this request as an XSLT request
     */
    if (util_check_valid_extension (mount) == XSLT_CONTENT)
    {
        /* If the file exists, then transform it, otherwise, write a 404 */
        DEBUG0("Stats request, sending XSL transformed stats");
        return stats_transform_xslt (client, mount);
    }

    ret = source_add_listener (mount, mountinfo, client);

    if (ret == -2)
    {
        if (mountinfo && mountinfo->file_seekable == 0)
        {
            DEBUG1 ("disable seek on file matching %s", mountinfo->mountname);
            httpp_deletevar (client->parser, "range");
            client->flags |= CLIENT_NO_CONTENT_LENGTH;
        }
        ret = fserve_client_create (client, mount);
    }
    return ret;
}
Пример #2
0
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, MOUNT_TYPE_NORMAL);
    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))
        ICECAST_LOG_WARN("auth to server %s failed with %s", stream_end_url, url->errormsg);

    auth_release (auth);
    free (stream_end_url);
    return;
}
Пример #3
0
static void file_release (client_t *client)
{
    fh_node *fh = client->shared_data;
    int ret = -1;

    if (fh->finfo.limit && (client->flags & CLIENT_AUTHENTICATED))
        stats_event_dec (NULL, "listeners");

    client_set_queue (client, NULL);

    if (client->flags & CLIENT_AUTHENTICATED && client->parser->req_type == httpp_req_get)
    {
        const char *m = NULL;

        if (fh->finfo.flags & FS_FALLBACK)
            m = httpp_getvar (client->parser, HTTPP_VAR_URI);
        else if (client->mount)
            m = client->mount;
        else
            m = fh->finfo.mount;
        if (m)
        {
            ice_config_t *config;
            char *mount = strdup (m);
            mount_proxy *mountinfo;

            remove_from_fh (fh, client);
            client->shared_data = NULL;
            config = config_get_config ();
            mountinfo = config_find_mount (config, mount);
            if (mountinfo && mountinfo->access_log.name)
                logging_access_id (&mountinfo->access_log, client);
            ret = auth_release_listener (client, mount, mountinfo);
            config_release_config();
            free (mount);
        }
        else
            remove_from_fh (fh, client);
    }
    else
        remove_from_fh (fh, client);
    if (ret < 0)
    {
        client->shared_data = NULL;
        client->flags &= ~CLIENT_AUTHENTICATED;
        client_destroy (client);
    }
    global_reduce_bitrate_sampling (global.out_bitrate);
}
Пример #4
0
static void source_shutdown (source_t *source)
{
    mount_proxy *mountinfo;

    source->running = 0;
    INFO1("Source \"%s\" exiting", source->mount);

    mountinfo = config_find_mount (config_get_config(), source->mount);
    if (mountinfo)
    {
        if (mountinfo->on_disconnect)
            source_run_script (mountinfo->on_disconnect, source->mount);
        auth_stream_end (mountinfo, source->mount);
    }
    config_release_config();

    /* we have de-activated the source now, so no more clients will be
     * added, now move the listeners we have to the fallback (if any)
     */
    if (source->fallback_mount)
    {
        source_t *fallback_source;

        avl_tree_rlock(global.source_tree);
        fallback_source = source_find_mount (source->fallback_mount);

        if (fallback_source != NULL)
            source_move_clients (source, fallback_source);

        avl_tree_unlock (global.source_tree);
    }

    /* delete this sources stats */
    stats_event(source->mount, NULL, NULL);

    /* we don't remove the source from the tree here, it may be a relay and
       therefore reserved */
    source_clear_source (source);

    global_lock();
    global.sources--;
    stats_event_args (NULL, "sources", "%d", global.sources);
    global_unlock();

    /* release our hold on the lock so the main thread can continue cleaning up */
    thread_rwlock_unlock(source->shutdown_rwlock);
}
Пример #5
0
int auth_postprocess_listener (auth_client *auth_user)
{
    int ret;
    client_t *client = auth_user->client;
    ice_config_t *config = config_get_config();

    mount_proxy *mountinfo = config_find_mount (config, auth_user->mount, MOUNT_TYPE_NORMAL);

    ret = add_authenticated_listener (auth_user->mount, mountinfo, client);
    config_release_config();

    if (ret < 0)
        client_send_401 (auth_user->client);
    auth_user->client = NULL;

    return ret;
}
Пример #6
0
int connection_check_source_pass(http_parser_t *parser, const 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_find_mount (config, mount);

    if (mountinfo)
    {
        if (mountinfo->password)
            pass = mountinfo->password;
        if (mountinfo->username)
            user = mountinfo->username;
    }

    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;
}
Пример #7
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, MOUNT_TYPE_NORMAL);
    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 ();
            ICECAST_LOG_WARN("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;
        ICECAST_LOG_INFO("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");
    }
}
Пример #8
0
static void _handle_shoutcast_compatible (client_queue_t *node)
{
    char *http_compliant;
    int http_compliant_len = 0;
    http_parser_t *parser;
    ice_config_t *config = config_get_config ();
    char *shoutcast_mount;
    client_t *client = node->client;

    if (node->shoutcast_mount)
        shoutcast_mount = node->shoutcast_mount;
    else
        shoutcast_mount = config->shoutcast_mount;

    if (node->shoutcast == 1)
    {
        char *source_password, *ptr, *headers;
        mount_proxy *mountinfo = config_find_mount (config, shoutcast_mount, MOUNT_TYPE_NORMAL);

        if (mountinfo && mountinfo->password)
            source_password = strdup (mountinfo->password);
        else
        {
            if (config->source_password) 
                source_password = strdup (config->source_password);
            else
                source_password = NULL;
        }
        config_release_config();

        /* Get rid of trailing \r\n or \n after password */
        ptr = strstr (client->refbuf->data, "\r\r\n");
        if (ptr)
            headers = ptr+3;
        else
        {
            ptr = strstr (client->refbuf->data, "\r\n");
            if (ptr)
                headers = ptr+2;
            else
            {
                ptr = strstr (client->refbuf->data, "\n");
                if (ptr)
                    headers = ptr+1;
            }
        }

        if (ptr == NULL)
        {
            client_destroy (client);
            free (source_password);
            free (node->shoutcast_mount);
            free (node);
            return;
        }
        *ptr = '\0';

        if (source_password && strcmp (client->refbuf->data, source_password) == 0)
        {
            client->respcode = 200;
            /* send this non-blocking but if there is only a partial write
             * then leave to header timeout */
            sock_write (client->con->sock, "OK2\r\nicy-caps:11\r\n\r\n");
            node->offset -= (headers - client->refbuf->data);
            memmove (client->refbuf->data, headers, node->offset+1);
            node->shoutcast = 2;
            /* we've checked the password, now send it back for reading headers */
            _add_request_queue (node);
            free (source_password);
            return;
        }
        else
            INFO1 ("password does not match \"%s\"", client->refbuf->data);
        client_destroy (client);
        free (source_password);
        free (node->shoutcast_mount);
        free (node);
        return;
    }
    /* actually make a copy as we are dropping the config lock */
    shoutcast_mount = strdup (shoutcast_mount);
    config_release_config();
    /* Here we create a valid HTTP request based of the information
       that was passed in via the non-HTTP style protocol above. This
       means we can use some of our existing code to handle this case */
    http_compliant_len = 20 + strlen (shoutcast_mount) + node->offset;
    http_compliant = (char *)calloc(1, http_compliant_len);
    snprintf (http_compliant, http_compliant_len,
            "SOURCE %s HTTP/1.0\r\n%s", shoutcast_mount, client->refbuf->data);
    parser = httpp_create_parser();
    httpp_initialize(parser, NULL);
    if (httpp_parse (parser, http_compliant, strlen(http_compliant)))
    {
        /* we may have more than just headers, so prepare for it */
        if (node->stream_offset == node->offset)
            client->refbuf->len = 0;
        else
        {
            char *ptr = client->refbuf->data;
            client->refbuf->len = node->offset - node->stream_offset;
            memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
        }
        client->parser = parser;
        source_startup (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH);
    }
    else {
        httpp_destroy (parser);
        client_destroy (client);
    }
    free (http_compliant);
    free (shoutcast_mount);
    free (node->shoutcast_mount);
    free (node);
    return;
}
Пример #9
0
/* Perform any initialisation just before the stream data is processed, the header
 * info is processed by now and the format details are setup
 */
static void source_init (source_t *source)
{
    ice_config_t *config = config_get_config();
    char *listenurl;
    const char *str;
    int listen_url_size;
    mount_proxy *mountinfo;

    /* 6 for max size of port */
    listen_url_size = strlen("http://") + strlen(config->hostname) +
        strlen(":") + 6 + strlen(source->mount) + 1;

    listenurl = malloc (listen_url_size);
    memset (listenurl, '\000', listen_url_size);
    snprintf (listenurl, listen_url_size, "http://%s%s",
            config->hostname, source->mount);
    config_release_config();

    str = httpp_getvar(source->parser, "ice-audio-info");
    source->audio_info = util_dict_new();
    if (str)
    {
        _parse_audio_info (source, str);
        stats_event (source->mount, "audio_info", str);
    }

    stats_event (source->mount, "listenurl", listenurl);

    free(listenurl);

    if (source->dumpfilename != NULL)
    {
        source->dumpfile = fopen (source->dumpfilename, "ab");
        if (source->dumpfile == NULL)
        {
            WARN2("Cannot open dump file \"%s\" for appending: %s, disabling.",
                    source->dumpfilename, strerror(errno));
        }
    }

    /* grab a read lock, to make sure we get a chance to cleanup */
    thread_rwlock_rlock (source->shutdown_rwlock);

    /* start off the statistics */
    source->listeners = 0;
    stats_event_inc (NULL, "source_total_connections");
    stats_event (source->mount, "slow_listeners", "0");
    stats_event_args (source->mount, "listeners", "%lu", source->listeners);
    stats_event_args (source->mount, "listener_peak", "%lu", source->peak_listeners);
    stats_event_time (source->mount, "stream_start");

    DEBUG0("Source creation complete");
    source->last_read = time (NULL);
    source->prev_listeners = -1;
    source->running = 1;

    mountinfo = config_find_mount (config_get_config(), source->mount);
    if (mountinfo)
    {
        if (mountinfo->on_connect)
            source_run_script (mountinfo->on_connect, source->mount);
        auth_stream_start (mountinfo, source->mount);
    }
    config_release_config();

    /*
    ** Now, if we have a fallback source and override is on, we want
    ** to steal its clients, because it means we've come back online
    ** after a failure and they should be gotten back from the waiting
    ** loop or jingle track or whatever the fallback is used for
    */

    if (source->fallback_override && source->fallback_mount)
    {
        source_t *fallback_source;

        avl_tree_rlock(global.source_tree);
        fallback_source = source_find_mount(source->fallback_mount);

        if (fallback_source)
            source_move_clients (fallback_source, source);

        avl_tree_unlock(global.source_tree);
    }
}
Пример #10
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;
}
Пример #11
0
/* wrapper for starting the provided relay stream */
static void check_relay_stream (relay_server *relay)
{
    if (relay->source == NULL)
    {
        if (relay->localmount[0] != '/')
        {
            ICECAST_LOG_WARN("relay mountpoint \"%s\" does not start with /, skipping",
                    relay->localmount);
            return;
        }
        /* new relay, reserve the name */
        relay->source = source_reserve (relay->localmount);
        if (relay->source)
        {
            ICECAST_LOG_DEBUG("Adding relay source at mountpoint \"%s\"", relay->localmount);
            if (relay->on_demand)
            {
                ice_config_t *config = config_get_config ();
                mount_proxy *mountinfo = config_find_mount (config, relay->localmount, MOUNT_TYPE_NORMAL);
                relay->source->on_demand = relay->on_demand;
                if (mountinfo == NULL)
                    source_update_settings (config, relay->source, mountinfo);
                config_release_config ();
                stats_event (relay->localmount, "listeners", "0");
                slave_update_all_mounts();
            }
        }
        else
        {
            if (relay->start == 0)
            {
                ICECAST_LOG_WARN("new relay but source \"%s\" already exists", relay->localmount);
                relay->start = 1;
            }
            return;
        }
    }
    do
    {
        source_t *source = relay->source;
        /* skip relay if active, not configured or just not time yet */
        if (relay->source == NULL || relay->running || relay->start > time(NULL))
            break;
        /* check if an inactive on-demand relay has a fallback that has listeners */
        if (relay->on_demand && source->on_demand_req == 0)
        {
            relay->source->on_demand = relay->on_demand;

            if (source->fallback_mount && source->fallback_override)
            {
                source_t *fallback;
                avl_tree_rlock (global.source_tree);
                fallback = source_find_mount (source->fallback_mount);
                if (fallback && fallback->running && fallback->listeners)
                {
                   ICECAST_LOG_DEBUG("fallback running %d with %lu listeners", fallback->running, fallback->listeners);
                   source->on_demand_req = 1;
                }
                avl_tree_unlock (global.source_tree);
            }
            if (source->on_demand_req == 0)
                break;
        }

        relay->start = time(NULL) + 5;
        relay->running = 1;
        relay->thread = thread_create ("Relay Thread", start_relay_stream,
                relay, THREAD_ATTACHED);
        return;

    } while (0);
    /* the relay thread may of shut down itself */
    if (relay->cleanup)
    {
        if (relay->thread)
        {
            ICECAST_LOG_DEBUG("waiting for relay thread for \"%s\"", relay->localmount);
            thread_join (relay->thread);
            relay->thread = NULL;
        }
        relay->cleanup = 0;
        relay->running = 0;

        if (relay->on_demand && relay->source)
        {
            ice_config_t *config = config_get_config ();
            mount_proxy *mountinfo = config_find_mount (config, relay->localmount, MOUNT_TYPE_NORMAL);
            source_update_settings (config, relay->source, mountinfo);
            config_release_config ();
            stats_event (relay->localmount, "listeners", "0");
        }
    }
}
Пример #12
0
int move_listener (client_t *client, struct _fbinfo *finfo)
{
    source_t *source;
    mount_proxy *minfo;
    int rate = finfo->limit, loop = 20, ret = -1;
    ice_config_t *config = config_get_config();
    struct _fbinfo where;
    unsigned int len = 4096;
    char buffer [len];

    memcpy (&where, finfo, sizeof (where));
    if (finfo->fallback)
        where.fallback = strdup (finfo->fallback);
    avl_tree_rlock (global.source_tree);
    do
    {
        len = sizeof buffer;
        util_expand_pattern (where.fallback, where.mount, buffer, &len);
        where.mount = buffer;

        minfo = config_find_mount (config, where.mount);

        if (rate == 0 && minfo && minfo->limit_rate)
            rate = minfo->limit_rate;
        source = source_find_mount_raw (where.mount);

        if (source == NULL && minfo == NULL)
            break;
        if (source)
        {
            thread_rwlock_wlock (&source->lock);
            if (source_available (source))
            {
                // an unused on-demand relay will still have an unitialised type
                if (source->format->type == finfo->type || source->format->type == FORMAT_TYPE_UNDEFINED)
                {
                    config_release_config();
                    avl_tree_unlock (global.source_tree);
                    source_setup_listener (source, client);
                    source->listeners++;
                    client->flags |= CLIENT_HAS_MOVED;
                    thread_rwlock_unlock (&source->lock);
                    free (where.fallback);
                    return 0;
                }
            }
            thread_rwlock_unlock (&source->lock);
        }
        if (minfo && minfo->fallback_mount)
        {
            free (where.fallback);
            where.fallback = strdup (where.mount);
            where.mount = minfo->fallback_mount;
        }
        else
            break;
    } while (loop--);

    avl_tree_unlock (global.source_tree);
    config_release_config();
    if (where.mount && ((client->flags & CLIENT_IS_SLAVE) == 0))
    {
        if (where.limit == 0)
        {
            if (rate == 0)
                if (sscanf (where.mount, "%*[^[][%d]", &rate) == 1)
                    rate = rate * 1000/8;
            where.limit = rate;
        }
        client->intro_offset = 0;
        ret = fserve_setup_client_fb (client, &where);
    }
    free (where.fallback);
    return ret;
}
Пример #13
0
/* build an XML doc containing information about currently running sources.
 * If a mountpoint is passed then that source will not be added to the XML
 * doc even if the source is running */
xmlDocPtr admin_build_sourcelist (const char *mount)
{
    avl_node *node;
    source_t *source;
    xmlNodePtr xmlnode, srcnode;
    xmlDocPtr doc;
    char buf[22];
    time_t now = time(NULL);

    doc = xmlNewDoc (XMLSTR("1.0"));
    xmlnode = xmlNewDocNode (doc, NULL, XMLSTR("icestats"), NULL);
    xmlDocSetRootElement(doc, xmlnode);

    if (mount) {
        xmlNewChild (xmlnode, NULL, XMLSTR("current_source"), XMLSTR(mount));
    }

    node = avl_get_first(global.source_tree);
    while(node) {
        source = (source_t *)node->key;
        if (mount && strcmp (mount, source->mount) == 0)
        {
            node = avl_get_next (node);
            continue;
        }

        if (source->running || source->on_demand)
        {
            ice_config_t *config;
            mount_proxy *mountinfo;

            srcnode = xmlNewChild(xmlnode, NULL, XMLSTR("source"), NULL);
            xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(source->mount));

            xmlNewChild(srcnode, NULL, XMLSTR("fallback"), 
                    (source->fallback_mount != NULL)?
                    XMLSTR(source->fallback_mount):XMLSTR(""));
            snprintf (buf, sizeof(buf), "%lu", source->listeners);
            xmlNewChild(srcnode, NULL, XMLSTR("listeners"), XMLSTR(buf));

            config = config_get_config();
            mountinfo = config_find_mount (config, source->mount);
            if (mountinfo && mountinfo->auth)
            {
                xmlNewChild(srcnode, NULL, XMLSTR("authenticator"),
                        XMLSTR(mountinfo->auth->type));
            }
            config_release_config();

            if (source->running)
            {
                if (source->client) 
                {
                    snprintf (buf, sizeof(buf), "%lu",
                            (unsigned long)(now - source->con->con_time));
                    xmlNewChild (srcnode, NULL, XMLSTR("Connected"), XMLSTR(buf));
                }
                xmlNewChild (srcnode, NULL, XMLSTR("content-type"), 
                        XMLSTR(source->format->contenttype));
            }
        }
        node = avl_get_next(node);
    }
    return(doc);
}
Пример #14
0
static void command_manageauth(client_t *client, source_t *source,
    int response)
{
    xmlDocPtr doc;
    xmlNodePtr node, srcnode, msgnode;
    const char *action = NULL;
    const char *username = NULL;
    char *message = NULL;
    int ret = AUTH_OK;
    ice_config_t *config = config_get_config ();
    mount_proxy *mountinfo = config_find_mount (config, source->mount);

    do
    {
        if (mountinfo == NULL || mountinfo->auth == NULL)
        {
            WARN1 ("manage auth request for %s but no facility available", source->mount);
            break;
        }
        COMMAND_OPTIONAL(client, "action", action);
        COMMAND_OPTIONAL (client, "username", username);

        if (action == NULL)
            action = "list";

        if (!strcmp(action, "add"))
        {
            const char *password = NULL;
            COMMAND_OPTIONAL (client, "password", password);

            if (username == NULL || password == NULL)
            {
                WARN1 ("manage auth request add for %s but no user/pass", source->mount);
                break;
            }
            ret = mountinfo->auth->adduser(mountinfo->auth, username, password);
            if (ret == AUTH_FAILED) {
                message = strdup("User add failed - check the icecast error log");
            }
            if (ret == AUTH_USERADDED) {
                message = strdup("User added");
            }
            if (ret == AUTH_USEREXISTS) {
                message = strdup("User already exists - not added");
            }
        }
        if (!strcmp(action, "delete"))
        {
            if (username == NULL)
            {
                WARN1 ("manage auth request delete for %s but no username", source->mount);
                break;
            }
            ret = mountinfo->auth->deleteuser(mountinfo->auth, username);
            if (ret == AUTH_FAILED) {
                message = strdup("User delete failed - check the icecast error log");
            }
            if (ret == AUTH_USERDELETED) {
                message = strdup("User deleted");
            }
        }

        doc = xmlNewDoc (XMLSTR("1.0"));
        node = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL);
        srcnode = xmlNewChild(node, NULL, XMLSTR("source"), NULL);
        xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(source->mount));

        if (message) {
            msgnode = xmlNewChild(node, NULL, XMLSTR("iceresponse"), NULL);
            xmlNewChild(msgnode, NULL, XMLSTR("message"), XMLSTR(message));
        }

        xmlDocSetRootElement(doc, node);

        if (mountinfo && mountinfo->auth && mountinfo->auth->listuser)
            mountinfo->auth->listuser (mountinfo->auth, srcnode);

        config_release_config ();

        admin_send_response(doc, client, response, 
                MANAGEAUTH_TRANSFORMED_REQUEST);
        free (message);
        xmlFreeDoc(doc);
        return;
    } while (0);

    config_release_config ();
    client_send_400 (client, "missing parameter");
}
Пример #15
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;
    ice_config_t *config = config_get_config();
    mount_proxy *mountinfo = config_find_mount (config, mount);

    if ((client->flags & CLIENT_AUTHENTICATED) == 0)
    {
        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)
            {
                int len = strlen (mountinfo->redirect) + strlen (mount) + 3;
                char *addr = alloca (len);
                snprintf (addr, len, "%s%s", mountinfo->redirect, mount);
                config_release_config ();
                return client_send_302 (client, addr);
            }
            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;
}
Пример #16
0
/* we don't need to clean up on err, as we'll go through the node struct and clean all we have inside */
static int _connection_process (connection_queue_t *node) {
    refbuf_t *header;
    http_parser_t *parser = NULL;
    int hdrsize = 0;
    int shoutcast = 0;
    int err;
    char *shoutcast_mount = NULL;
    mount_proxy *mountinfo;

    ice_config_t *config;
    listener_t *listener;

    if (!node->refbuf)
	    node->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
    header = node->refbuf;

    { /* this code tests for shoutcastness */
        config = config_get_config();
        listener = config_get_listen_sock (config, node->con);

        if (listener) {
            WARN("listner");
            if (listener->shoutcast_compat)
                shoutcast = 1;
            if (listener->ssl && ssl_ok)
                connection_uses_ssl (node->con);
            if (listener->shoutcast_mount) {
                shoutcast_mount = strdup (listener->shoutcast_mount);
            } else {
                shoutcast_mount = config->shoutcast_mount;
            }
        }

        WARN("shoutcast %d, mount %s", shoutcast, shoutcast_mount);

        mountinfo = config_find_mount (config, shoutcast_mount);
        config_release_config();
    }

    if (shoutcast && !header->sync_point) { /* stage2 is actually handled by generic code */
        err = _handle_shoutcast_stage1 (node, shoutcast_mount, mountinfo);
        if (err < 0)
            return err;
    }

    hdrsize = util_read_header (node->con, header, HEADER_READ_ENTIRE);
    if (hdrsize < 0)
    {
        ERROR ("Header read failed");
        return hdrsize;
    }

    /* process normal HTTP headers */
    if (node->parser) {
        parser = node->parser;
    } else {
        parser = node->parser = httpp_create_parser();
        httpp_initialize(parser, NULL);
    }

    err = httpp_parse (parser, header->data, hdrsize);
    if (err == 0) {
        ERROR0("HTTP request parsing failed");
        return -EINVAL;
    }

    /* XXX what happens when error in http ??? is err set ? */
    if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE)) {
        ERROR("Error(%s)", httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE));
        return err;
    }

    if (header->sync_point && (parser->req_type == httpp_req_source ||
                               parser->req_type == httpp_req_post)) {
	    hdrsize = util_read_header (node->con, header, HEADER_READ_ENTIRE);
	    if (hdrsize < 0) {
            INFO ("Header read failed");
            return hdrsize;
        }
    }

    if (! node->client) {
        err = connection_client_setup (node);
        if (err < 0)
            return err;

        header->len -= hdrsize;
        if (header->len) {
            memmove(header->data, header->data + hdrsize, header->len);
            client_set_queue (node->client, header);
        }
        refbuf_release(header);
    }

    stats_event_inc (NULL, "connections");

    WARN("shoutcast = %d", shoutcast);

    return _handle_client (node->client);
}
Пример #17
0
int fserve_setup_client_fb (client_t *client, fbinfo *finfo)
{
    fh_node *fh = &no_file;
    int ret = 0;

    if (finfo)
    {
        mount_proxy *minfo;
        if (finfo->flags & FS_FALLBACK && finfo->limit == 0)
            return -1;
        avl_tree_wlock (fh_cache);
        fh = find_fh (finfo);
        minfo = config_find_mount (config_get_config(), finfo->mount);
        if (fh)
        {
            thread_mutex_lock (&fh->lock);
            avl_tree_unlock (fh_cache);
            client->shared_data = NULL;
            if (minfo)
            {
                if (minfo->max_listeners >= 0 && fh->refcount > minfo->max_listeners)
                {
                    thread_mutex_unlock (&fh->lock);
                    config_release_config();
                    return client_send_403redirect (client, finfo->mount, "max listeners reached");
                }
                if (check_duplicate_logins (finfo->mount, fh->clients, client, minfo->auth) == 0)
                {
                    thread_mutex_unlock (&fh->lock);
                    config_release_config();
                    return client_send_403 (client, "Account already in use");
                }
            }
            config_release_config();
        }
        else
        {
            if (minfo && minfo->max_listeners == 0)
            {
                avl_tree_unlock (fh_cache);
                config_release_config();
                client->shared_data = NULL;
                return client_send_403redirect (client, finfo->mount, "max listeners reached");
            }
            config_release_config();
            fh = open_fh (finfo);
            if (fh == NULL)
                return client_send_404 (client, NULL);
        }
        if (fh->finfo.limit)
        {
            client->timer_start = client->worker->current_time.tv_sec;
            if (client->connection.sent_bytes == 0)
                client->timer_start -= 2;
            client->counter = 0;
            client->intro_offset = 0;
            global_reduce_bitrate_sampling (global.out_bitrate);
        }
    }
    else
        thread_mutex_lock (&fh->lock);
    client->mount = fh->finfo.mount;
    if (fh->finfo.type == FORMAT_TYPE_UNDEFINED)
    {
        if (client->respcode == 0)
        {
            client->refbuf->len = 0;
            ret = format_general_headers (fh->format, client);
        }
    }
    else
    {
        if (fh->format->create_client_data && client->format_data == NULL)
            ret = fh->format->create_client_data (fh->format, client);
        if (fh->format->write_buf_to_client)
            client->check_buffer = fh->format->write_buf_to_client;
    }
    if (ret < 0)
    {
        thread_mutex_unlock (&fh->lock);
        return client_send_416 (client);
    }
    fh_add_client (fh, client);
    thread_mutex_unlock (&fh->lock);
    client->shared_data = fh;

    if (client->check_buffer == NULL)
        client->check_buffer = format_generic_write_to_client;

    client->ops = &buffer_content_ops;
    client->flags &= ~CLIENT_HAS_INTRO_CONTENT;
    client->flags |= CLIENT_IN_FSERVE;
    if (client->flags & CLIENT_ACTIVE)
    {
        client->schedule_ms = client->worker->time_ms;
        if (finfo && finfo->flags & FS_FALLBACK)
            return 0; // prevent a recursive loop 
        return client->ops->process (client);
    }
    else
    {
        worker_t *worker = client->worker;
        client->flags |= CLIENT_ACTIVE;
        worker_wakeup (worker); /* worker may of already processed client but make sure */
    }
    return 0;
}
Пример #18
0
static xmlNodePtr _dump_stats_to_doc (xmlNodePtr root, const char *show_mount, int hidden) {
    avl_node *avlnode;
    xmlNodePtr ret = NULL;
    ice_config_t *config;

    thread_mutex_lock(&_stats_mutex);
    /* general stats first */
    avlnode = avl_get_first(_stats.global_tree);

    while (avlnode) {
        stats_node_t *stat = avlnode->key;
        if (stat->hidden <=  hidden)
            xmlNewTextChild (root, NULL, XMLSTR(stat->name), XMLSTR(stat->value));
        avlnode = avl_get_next (avlnode);
    }
    /* now per mount stats */
    avlnode = avl_get_first(_stats.source_tree);
    config = config_get_config();
    __add_authstack(config->authstack, root);
    config_release_config();

    while (avlnode) {
        stats_source_t *source = (stats_source_t *)avlnode->key;

        if (source->hidden <= hidden &&
                (show_mount == NULL || strcmp (show_mount, source->source) == 0))
        {
            xmlNodePtr metadata, history;
            source_t *source_real;
            mount_proxy *mountproxy;
            int i;

            avl_node *avlnode2 = avl_get_first (source->stats_tree);
            xmlNodePtr xmlnode = xmlNewTextChild (root, NULL, XMLSTR("source"), NULL);

            xmlSetProp (xmlnode, XMLSTR("mount"), XMLSTR(source->source));
            if (ret == NULL)
                ret = xmlnode;
            while (avlnode2)
            {
                stats_node_t *stat = avlnode2->key;
                xmlNewTextChild (xmlnode, NULL, XMLSTR(stat->name), XMLSTR(stat->value));
                avlnode2 = avl_get_next (avlnode2);
            }


            avl_tree_rlock(global.source_tree);
            source_real = source_find_mount_raw(source->source);
            history = playlist_render_xspf(source_real->history);
            if (history)
                xmlAddChild(xmlnode, history);
            metadata = xmlNewTextChild(xmlnode, NULL, XMLSTR("metadata"), NULL);
            if (source_real->format) {
                for (i = 0; i < source_real->format->vc.comments; i++)
                    __add_metadata(metadata, source_real->format->vc.user_comments[i]);
            }
            avl_tree_unlock(global.source_tree);

            config = config_get_config();
            mountproxy = config_find_mount(config, source->source, MOUNT_TYPE_NORMAL);
            __add_authstack(mountproxy->authstack, xmlnode);
            config_release_config();
        }
        avlnode = avl_get_next (avlnode);
    }
    thread_mutex_unlock(&_stats_mutex);
    return ret;
}
Пример #19
0
/* 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;
}
Пример #20
0
/* 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;
}
Пример #21
0
static void *start_relay_stream (void *arg)
{
    client_t *client = arg;
    relay_server *relay;
    source_t *src;
    int failed = 1, sources;

    global_lock();
    sources = ++global.sources;
    stats_event_args (NULL, "sources", "%d", global.sources);
    global_unlock();
    /* set the start time, because we want to decrease the sources on all failures */
    client->connection.con_time = time (NULL);
    do
    {
        ice_config_t *config = config_get_config();
        mount_proxy *mountinfo;

        relay = client->shared_data;
        src = relay->source;

        thread_rwlock_wlock (&src->lock);
        src->flags |= SOURCE_PAUSE_LISTENERS;
        if (sources > config->source_limit)
        {
            config_release_config();
            WARN1 ("starting relayed mountpoint \"%s\" requires a higher sources limit", relay->localmount);
            break;
        }
        config_release_config();
        INFO1("Starting relayed source at mountpoint \"%s\"", relay->localmount);

        if (open_relay (relay) < 0)
            break;
        if (connection_complete_source (src) < 0)
        {
            WARN1 ("Failed to complete initialisation on %s", relay->localmount);
            break;
        }
        stats_event_inc (NULL, "source_relay_connections");
        source_init (src);
        config = config_get_config();
        mountinfo = config_find_mount (config, src->mount);
        source_update_settings (config, src, mountinfo);
        INFO1 ("source %s is ready to start", src->mount);
        config_release_config();
        failed = 0;
    } while (0);

    client->ops = &relay_client_ops;
    client->schedule_ms = timing_get_time();

    if (failed)
    {
        /* failed to start any connection, better clean up and reset */
        if (relay->on_demand == 0)
        {
            yp_remove (relay->localmount);
            src->yp_public = -1;
        }
        relay->in_use = NULL;
        INFO2 ("listener count remaining on %s is %d", src->mount, src->listeners);
        src->flags &= ~(SOURCE_PAUSE_LISTENERS|SOURCE_RUNNING);
    }
    thread_rwlock_unlock (&src->lock);

    thread_spin_lock (&relay_start_lock);
    relays_connecting--;
    thread_spin_unlock (&relay_start_lock);

    client->flags |= CLIENT_ACTIVE;
    worker_wakeup (client->worker);
    return NULL;
}
Пример #22
0
int fserve_setup_client_fb (client_t *client, fbinfo *finfo)
{
    fh_node *fh = &no_file;
    int ret = 0;
    refbuf_t *refbuf;
    ssize_t bytes;

    if (finfo)
    {
        mount_proxy *minfo;
        if (finfo->flags & FS_FALLBACK && finfo->limit == 0)
            return -1;
        avl_tree_wlock (fh_cache);
        fh = find_fh (finfo);
        minfo = config_find_mount (config_get_config(), finfo->mount);
        if (fh)
        {
            thread_mutex_lock (&fh->lock);
            avl_tree_unlock (fh_cache);
            client->shared_data = NULL;
            if (minfo)
            {
                if (minfo->max_listeners >= 0 && fh->refcount > minfo->max_listeners)
                {
                    thread_mutex_unlock (&fh->lock);
                    config_release_config();
                    return client_send_403redirect (client, finfo->mount, "max listeners reached");
                }
                if (check_duplicate_logins (finfo->mount, fh->clients, client, minfo->auth) == 0)
                {
                    thread_mutex_unlock (&fh->lock);
                    config_release_config();
                    return client_send_403 (client, "Account already in use");
                }
            }
            config_release_config();
        }
        else
        {
            if (minfo && minfo->max_listeners == 0)
            {
                avl_tree_unlock (fh_cache);
                config_release_config();
                client->shared_data = NULL;
                return client_send_403redirect (client, finfo->mount, "max listeners reached");
            }
            config_release_config();
            fh = open_fh (finfo);
            if (fh == NULL)
                return client_send_404 (client, NULL);
            if (fh->finfo.limit)
                DEBUG2 ("request for throttled file %s (bitrate %d)", fh->finfo.mount, fh->finfo.limit*8);
        }
        if (fh->finfo.limit)
        {
            client->timer_start = client->worker->current_time.tv_sec;
            if (client->connection.sent_bytes == 0)
                client->timer_start -= 2;
            client->counter = 0;
            client->intro_offset = 0;
            global_reduce_bitrate_sampling (global.out_bitrate);
        }
    }
    else
    {
        if (client->mount && (client->flags & CLIENT_AUTHENTICATED) && (client->respcode >= 300 || client->respcode < 200))
        {
            fh = calloc (1, sizeof (no_file));
            fh->finfo.mount = strdup (client->mount);
            fh->finfo.flags |= FS_DELETE;
            fh->refcount = 1;
            fh->f = SOCK_ERROR;
            thread_mutex_create (&fh->lock);
        }
        thread_mutex_lock (&fh->lock);
    }
    client->mount = fh->finfo.mount;
    if (fh->finfo.type == FORMAT_TYPE_UNDEFINED)
    {
        if (client->respcode == 0)
        {
            client->refbuf->len = 0;
            ret = format_general_headers (fh->format, client);
        }
    }
    else
    {
        if (fh->format->create_client_data && client->format_data == NULL)
            ret = fh->format->create_client_data (fh->format, client);
        if (fh->format->write_buf_to_client)
            client->check_buffer = fh->format->write_buf_to_client;
    }
    if (ret < 0)
    {
        thread_mutex_unlock (&fh->lock);
        return client_send_416 (client);
    }
    fh_add_client (fh, client);
    thread_mutex_unlock (&fh->lock);
    client->shared_data = fh;

    if (client->check_buffer == NULL)
        client->check_buffer = format_generic_write_to_client;

    // workaround for #134: fill the preallocated, but empty, chained buffer in case a range request was made
    if (client->flags & CLIENT_RANGE_END)
    {
        if (client->refbuf && client->refbuf->next)
        {
            refbuf = client->refbuf->next;
            bytes = pread (fh->f, refbuf->data, refbuf->len, client->intro_offset);
            if (bytes < 0)
                return -1;
        }
    }
    
    client->ops = &buffer_content_ops;
    client->flags &= ~CLIENT_HAS_INTRO_CONTENT;
    client->flags |= CLIENT_IN_FSERVE;
    if (client->flags & CLIENT_ACTIVE)
    {
        client->schedule_ms = client->worker->time_ms;
        if (finfo && finfo->flags & FS_FALLBACK)
            return 0; // prevent a recursive loop 
        return client->ops->process (client);
    }
    else
    {
        worker_t *worker = client->worker;
        ret = (fh->finfo.limit) ? 0 : -1;
        client->flags |= CLIENT_ACTIVE;
        worker_wakeup (worker); /* worker may of already processed client but make sure */
    }
    return ret;
}
Пример #23
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;
}