connection.c 28.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org, 
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
 */

13
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
14 15 16 17
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Jack Moffitt's avatar
Jack Moffitt committed
18 19 20
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
21
#include <time.h>
22 23 24
#ifdef HAVE_POLL
#include <sys/poll.h>
#endif
25 26 27

#ifndef _WIN32
#include <sys/time.h>
Jack Moffitt's avatar
Jack Moffitt committed
28 29
#include <sys/socket.h>
#include <netinet/in.h>
30
#else
31
#include <winsock2.h>
32 33
#define snprintf _snprintf
#define strcasecmp stricmp
34
#define strncasecmp strnicmp
35
#endif
Jack Moffitt's avatar
Jack Moffitt committed
36 37 38

#include "os.h"

Karl Heyes's avatar
Karl Heyes committed
39 40 41 42
#include "thread/thread.h"
#include "avl/avl.h"
#include "net/sock.h"
#include "httpp/httpp.h"
Jack Moffitt's avatar
Jack Moffitt committed
43

44
#include "cfgfile.h"
Jack Moffitt's avatar
Jack Moffitt committed
45 46 47 48 49 50 51
#include "global.h"
#include "util.h"
#include "connection.h"
#include "refbuf.h"
#include "client.h"
#include "stats.h"
#include "logging.h"
52
#include "xslt.h"
53
#include "fserve.h"
54
#include "sighandler.h"
55 56

#include "yp.h"
Jack Moffitt's avatar
Jack Moffitt committed
57
#include "source.h"
Michael Smith's avatar
Michael Smith committed
58
#include "format.h"
59
#include "format_mp3.h"
Michael Smith's avatar
Michael Smith committed
60
#include "event.h"
61
#include "admin.h"
Michael Smith's avatar
Michael Smith committed
62
#include "auth.h"
Jack Moffitt's avatar
Jack Moffitt committed
63 64 65

#define CATMODULE "connection"

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
/* Two different major types of source authentication.
   Shoutcast style is used only by the Shoutcast DSP
   and is a crazy version of HTTP.  It looks like :
     Source Client -> Connects to port + 1
     Source Client -> sends encoder password (plaintext)\r\n
     Icecast -> reads encoder password, if ok, sends OK2\r\n, else disconnects
     Source Client -> reads OK2\r\n, then sends http-type request headers
                      that contain the stream details (icy-name, etc..)
     Icecast -> reads headers, stores them
     Source Client -> starts sending MP3 data
     Source Client -> periodically updates metadata via admin.cgi call

   Icecast auth style uses HTTP and Basic Authorization.
*/
#define SHOUTCAST_SOURCE_AUTH 1
#define ICECAST_SOURCE_AUTH 0

Jack Moffitt's avatar
Jack Moffitt committed
83
typedef struct con_queue_tag {
84 85
    connection_t *con;
    struct con_queue_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
86 87 88
} con_queue_t;

typedef struct _thread_queue_tag {
89 90
    thread_type *thread_id;
    struct _thread_queue_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
91 92 93
} thread_queue_t;

static mutex_t _connection_mutex;
94
static volatile unsigned long _current_id = 0;
Jack Moffitt's avatar
Jack Moffitt committed
95 96
static int _initialized = 0;

97
volatile static con_queue_t *_queue = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
98 99 100 101
static mutex_t _queue_mutex;

static thread_queue_t *_conhands = NULL;

102
rwlock_t _source_shutdown_rwlock;
Jack Moffitt's avatar
Jack Moffitt committed
103 104 105 106 107

static void *_handle_connection(void *arg);

void connection_initialize(void)
{
108 109 110 111
    if (_initialized) return;
    
    thread_mutex_create(&_connection_mutex);
    thread_mutex_create(&_queue_mutex);
112
    thread_mutex_create(&move_clients_mutex);
113
    thread_rwlock_create(&_source_shutdown_rwlock);
114
    thread_cond_create(&global.shutdown_cond);
Jack Moffitt's avatar
Jack Moffitt committed
115

116
    _initialized = 1;
Jack Moffitt's avatar
Jack Moffitt committed
117 118 119 120
}

void connection_shutdown(void)
{
121 122
    if (!_initialized) return;
    
123
    thread_cond_destroy(&global.shutdown_cond);
124 125 126
    thread_rwlock_destroy(&_source_shutdown_rwlock);
    thread_mutex_destroy(&_queue_mutex);
    thread_mutex_destroy(&_connection_mutex);
127
    thread_mutex_destroy(&move_clients_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
128

129
    _initialized = 0;
Jack Moffitt's avatar
Jack Moffitt committed
130 131 132 133
}

static unsigned long _next_connection_id(void)
{
134
    unsigned long id;
Jack Moffitt's avatar
Jack Moffitt committed
135

136 137 138
    thread_mutex_lock(&_connection_mutex);
    id = _current_id++;
    thread_mutex_unlock(&_connection_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
139

140
    return id;
Jack Moffitt's avatar
Jack Moffitt committed
141 142
}

143
connection_t *create_connection(sock_t sock, sock_t serversock, char *ip) {
144 145 146 147
    connection_t *con;
    con = (connection_t *)malloc(sizeof(connection_t));
    memset(con, 0, sizeof(connection_t));
    con->sock = sock;
148
    con->serversock = serversock;
149 150 151
    con->con_time = time(NULL);
    con->id = _next_connection_id();
    con->ip = ip;
Michael Smith's avatar
Michael Smith committed
152 153 154 155

    con->event_number = EVENT_NO_EVENT;
    con->event = NULL;

156
    return con;
157 158
}

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
static int wait_for_serversock(int timeout)
{
#ifdef HAVE_POLL
    struct pollfd ufds[MAX_LISTEN_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 -2;
    }
    else if(ret == 0) {
        return -1;
    }
    else {
179
        int dst;
180
        for(i=0; i < global.server_sockets; i++) {
181
            if(ufds[i].revents & POLLIN)
182
                return ufds[i].fd;
183 184 185 186 187 188 189 190 191
            if(ufds[i].revents & (POLLHUP|POLLERR|POLLNVAL))
            {
                if (ufds[i].revents & (POLLHUP|POLLERR))
                {
                    close (global.serversock[i]);
                    WARN0("Had to close a listening socket");
                }
                global.serversock[i] = -1;
            }
192
        }
193 194 195 196 197 198 199 200 201 202 203
        /* remove any closed sockets */
        for(i=0, dst=0; i < global.server_sockets; i++)
        {
            if (global.serversock[i] == -1)
                continue;
            if (i!=dst)
                global.serversock[dst] = global.serversock[i];
            dst++;
        }
        global.server_sockets = dst;
        return -1;
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
    }
#else
    fd_set rfds;
    struct timeval tv, *p=NULL;
    int i, ret;
    int max = -1;

    FD_ZERO(&rfds);

    for(i=0; i < global.server_sockets; i++) {
        FD_SET(global.serversock[i], &rfds);
        if(global.serversock[i] > max)
            max = global.serversock[i];
    }

    if(timeout >= 0) {
        tv.tv_sec = timeout/1000;
221
        tv.tv_usec = (timeout % 1000) * 1000;
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
        p = &tv;
    }

    ret = select(max+1, &rfds, NULL, NULL, p);
    if(ret < 0) {
        return -2;
    }
    else if(ret == 0) {
        return -1;
    }
    else {
        for(i=0; i < global.server_sockets; i++) {
            if(FD_ISSET(global.serversock[i], &rfds))
                return global.serversock[i];
        }
        return -1; /* Should be impossible, stop compiler warnings */
    }
#endif
}

Jack Moffitt's avatar
Jack Moffitt committed
242 243
static connection_t *_accept_connection(void)
{
244 245 246
    int sock;
    connection_t *con;
    char *ip;
247
    int serversock; 
Jack Moffitt's avatar
Jack Moffitt committed
248

249 250 251
    serversock = wait_for_serversock(100);
    if(serversock < 0)
        return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
252

253 254
    /* malloc enough room for a full IP address (including ipv6) */
    ip = (char *)malloc(MAX_ADDR_LEN);
Jack Moffitt's avatar
Jack Moffitt committed
255

256 257
    sock = sock_accept(serversock, ip, MAX_ADDR_LEN);
    if (sock >= 0) {
258
        con = create_connection(sock, serversock, ip);
Jack Moffitt's avatar
Jack Moffitt committed
259

260 261
        return con;
    }
Jack Moffitt's avatar
Jack Moffitt committed
262

263 264 265 266
    if (!sock_recoverable(sock_error()))
        WARN2("accept() failed with error %d: %s", sock_error(), strerror(sock_error()));
    
    free(ip);
Jack Moffitt's avatar
Jack Moffitt committed
267

268
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
269 270 271 272
}

static void _add_connection(connection_t *con)
{
273
    con_queue_t *node;
Jack Moffitt's avatar
Jack Moffitt committed
274

275 276 277 278
    node = (con_queue_t *)malloc(sizeof(con_queue_t));
    
    thread_mutex_lock(&_queue_mutex);
    node->con = con;
279
    node->next = (con_queue_t *)_queue;
280 281
    _queue = node;
    thread_mutex_unlock(&_queue_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
282 283
}

284
static void _push_thread(thread_queue_t **queue, thread_type *thread_id)
Jack Moffitt's avatar
Jack Moffitt committed
285
{
286 287 288 289 290 291 292 293 294 295 296 297 298 299
    /* create item */
    thread_queue_t *item = (thread_queue_t *)malloc(sizeof(thread_queue_t));
    item->thread_id = thread_id;
    item->next = NULL;


    thread_mutex_lock(&_queue_mutex);
    if (*queue == NULL) {
        *queue = item;
    } else {
        item->next = *queue;
        *queue = item;
    }
    thread_mutex_unlock(&_queue_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
300 301
}

302
static thread_type *_pop_thread(thread_queue_t **queue)
Jack Moffitt's avatar
Jack Moffitt committed
303
{
304 305
    thread_type *id;
    thread_queue_t *item;
Jack Moffitt's avatar
Jack Moffitt committed
306

307
    thread_mutex_lock(&_queue_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
308

309 310 311 312 313
    item = *queue;
    if (item == NULL) {
        thread_mutex_unlock(&_queue_mutex);
        return NULL;
    }
Jack Moffitt's avatar
Jack Moffitt committed
314

315 316 317 318
    *queue = item->next;
    item->next = NULL;
    id = item->thread_id;
    free(item);
Jack Moffitt's avatar
Jack Moffitt committed
319

320
    thread_mutex_unlock(&_queue_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
321

322
    return id;
Jack Moffitt's avatar
Jack Moffitt committed
323 324 325 326
}

static void _build_pool(void)
{
327 328
    ice_config_t *config;
    int i;
329
    thread_type *tid;
330
    char buff[64];
Michael Smith's avatar
Michael Smith committed
331
    int threadpool_size;
Jack Moffitt's avatar
Jack Moffitt committed
332

333
    config = config_get_config();
Michael Smith's avatar
Michael Smith committed
334 335
    threadpool_size = config->threadpool_size;
    config_release_config();
Jack Moffitt's avatar
Jack Moffitt committed
336

337 338 339 340 341
    for (i = 0; i < threadpool_size; i++) {
        snprintf(buff, 64, "Connection Thread #%d", i);
        tid = thread_create(buff, _handle_connection, NULL, THREAD_ATTACHED);
        _push_thread(&_conhands, tid);
    }
Jack Moffitt's avatar
Jack Moffitt committed
342 343 344 345
}

static void _destroy_pool(void)
{
346 347 348 349 350 351 352 353 354 355
    thread_type *id;
    int i;

    i = 0;

    id = _pop_thread(&_conhands);
    while (id != NULL) {
        thread_join(id);
        id = _pop_thread(&_conhands);
    }
356
    INFO0("All connection threads down");
Jack Moffitt's avatar
Jack Moffitt committed
357 358 359 360
}

void connection_accept_loop(void)
{
361
    connection_t *con;
Jack Moffitt's avatar
Jack Moffitt committed
362

363
    _build_pool();
Jack Moffitt's avatar
Jack Moffitt committed
364

365 366
    while (global.running == ICE_RUNNING)
    {
367
        if (global . schedule_config_reread)
368 369 370 371 372
        {
            /* reread config file */
            INFO0("Scheduling config reread ...");

            connection_inject_event(EVENT_CONFIG_READ, NULL);
373
            global . schedule_config_reread = 0;
374 375
        }

376
        con = _accept_connection();
Jack Moffitt's avatar
Jack Moffitt committed
377

378 379 380 381
        if (con) {
            _add_connection(con);
        }
    }
Jack Moffitt's avatar
Jack Moffitt committed
382

383 384 385
    /* Give all the other threads notification to shut down */
    thread_cond_broadcast(&global.shutdown_cond);

386
    _destroy_pool();
Jack Moffitt's avatar
Jack Moffitt committed
387

388 389 390
    /* wait for all the sources to shutdown */
    thread_rwlock_wlock(&_source_shutdown_rwlock);
    thread_rwlock_unlock(&_source_shutdown_rwlock);
Jack Moffitt's avatar
Jack Moffitt committed
391 392 393 394
}

static connection_t *_get_connection(void)
{
395 396 397 398
    con_queue_t *node = NULL;
    con_queue_t *oldnode = NULL;
    connection_t *con = NULL;

399 400 401 402
    /* common case, no new connections so don't bother taking locks */
    if (_queue == NULL)
        return NULL;

403 404
    thread_mutex_lock(&_queue_mutex);
    if (_queue) {
405
        node = (con_queue_t *)_queue;
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
        while (node->next) {
            oldnode = node;
            node = node->next;
        }
        
        /* node is now the last node
        ** and oldnode is the previous one, or NULL
        */
        if (oldnode) oldnode->next = NULL;
        else (_queue) = NULL;
    }
    thread_mutex_unlock(&_queue_mutex);

    if (node) {
        con = node->con;
        free(node);
    }

    return con;
Jack Moffitt's avatar
Jack Moffitt committed
425 426
}

Michael Smith's avatar
Michael Smith committed
427 428 429 430 431 432 433 434 435
void connection_inject_event(int eventnum, void *event_data) {
    connection_t *con = calloc(1, sizeof(connection_t));

    con->event_number = eventnum;
    con->event = event_data;

    _add_connection(con);
}

436 437 438

/* Called when activating a source. Verifies that the source count is not
 * exceeded and applies any initial parameters.
439
 */
440 441 442 443 444 445 446 447 448 449
int connection_complete_source (source_t *source)
{
    ice_config_t *config = config_get_config();

    global_lock ();
    DEBUG1 ("sources count is %d", global.sources);

    if (global.sources < config->source_limit)
    {
        char *contenttype;
450
        mount_proxy *mountinfo;
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
        format_type_t format_type;

        /* setup format handler */
        contenttype = httpp_getvar (source->parser, "content-type");
        if (contenttype != NULL)
        {
            format_type = format_get_type (contenttype);

            if (format_type == FORMAT_ERROR)
            {
                global_unlock();
                config_release_config();
                if (source->client)
                    client_send_404 (source->client, "Content-type not supported");
                WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
                return -1;
            }
        }
        else
        {
471
            WARN0("No content-type header, falling back to backwards compatibility mode "
472
                    "for icecast 1.x relays. Assuming content is mp3.");
473
            format_type = FORMAT_TYPE_GENERIC;
474 475
        }

Karl Heyes's avatar
Karl Heyes committed
476
        if (format_get_plugin (format_type, source) < 0)
477 478 479 480 481 482
        {
            global_unlock();
            config_release_config();
            if (source->client)
                client_send_404 (source->client, "internal format allocation problem");
            WARN1 ("plugin format failed for \"%s\"", source->mount);
483
            source->client = NULL;
484 485 486 487
            return -1;
        }

        global.sources++;
Karl Heyes's avatar
Karl Heyes committed
488
        stats_event_args (NULL, "sources", "%d", global.sources);
489 490 491 492 493 494 495 496
        global_unlock();

        /* for relays, we don't yet have a client, however we do require one
         * to retrieve the stream from.  This is created here, quite late,
         * because we can't use this client to return an error code/message,
         * so we only do this once we know we're going to accept the source.
         */
        if (source->client == NULL)
497
        {
498
            source->client = client_create (source->con, source->parser);
499 500 501 502 503 504 505 506 507 508 509 510 511
            if (source->client == NULL)
            {
                config_release_config();
                global_lock();
                global.sources--;
                global_unlock();
                connection_close (source->con);
                source->con = NULL;
                httpp_destroy (source->parser);
                source->parser = NULL;
                return -1;
            }
        }
512

513 514 515 516 517
        source->running = 1;
        mountinfo = config_find_mount (config, source->mount);
        if (mountinfo == NULL)
            source_update_settings (config, source, mountinfo);
        source_recheck_mounts ();
518 519 520 521 522 523 524
        config_release_config();

        source->shutdown_rwlock = &_source_shutdown_rwlock;
        DEBUG0 ("source is ready to start");

        return 0;
    }
525
    WARN1("Request to add source when maximum source limit "
526 527 528 529 530 531 532 533 534 535 536 537
            "reached %d", global.sources);

    global_unlock();
    config_release_config();

    if (source->client)
        client_send_404 (source->client, "too many sources connected");

    return -1;
}


538 539
static int _check_pass_http(http_parser_t *parser, 
        char *correctuser, char *correctpass)
540 541 542 543 544 545 546 547 548 549 550 551 552
{
    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
    char *header = httpp_getvar(parser, "authorization");
    char *userpass, *tmp;
    char *username, *password;

    if(header == NULL)
        return 0;

    if(strncmp(header, "Basic ", 6))
        return 0;

    userpass = util_base64_decode(header+6);
553 554 555
    if(userpass == NULL) {
        WARN1("Base64 decode of Authorization header \"%s\" failed",
                header+6);
556
        return 0;
557
    }
558 559 560 561 562 563 564 565 566 567

    tmp = strchr(userpass, ':');
    if(!tmp) {
        free(userpass);
        return 0;
    }
    *tmp = 0;
    username = userpass;
    password = tmp+1;

568
    if(strcmp(username, correctuser) || strcmp(password, correctpass)) {
569 570 571
        free(userpass);
        return 0;
    }
572
    free(userpass);
573 574 575 576

    return 1;
}

577 578 579 580 581 582 583 584
static int _check_pass_icy(http_parser_t *parser, char *correctpass)
{
    char *password;

    password = httpp_getvar(parser, HTTPP_VAR_ICYPASSWORD);
    if(!password)
        return 0;

585
    if (strcmp(password, correctpass))
586 587 588 589 590
        return 0;
    else
        return 1;
}

591
static int _check_pass_ice(http_parser_t *parser, char *correctpass)
592
{
593
    char *password;
594 595 596 597 598

    password = httpp_getvar(parser, "ice-password");
    if(!password)
        password = "";

599
    if (strcmp(password, correctpass))
600 601 602 603 604
        return 0;
    else
        return 1;
}

605
int connection_check_admin_pass(http_parser_t *parser)
606
{
607
    int ret;
Michael Smith's avatar
Michael Smith committed
608 609 610
    ice_config_t *config = config_get_config();
    char *pass = config->admin_password;
    char *user = config->admin_username;
611
    char *protocol;
612 613 614 615 616 617

    if(!pass || !user) {
        config_release_config();
        return 0;
    }

618 619 620 621 622
    protocol = httpp_getvar (parser, HTTPP_VAR_PROTOCOL);
    if (protocol && strcmp (protocol, "ICY") == 0)
        ret = _check_pass_icy (parser, pass);
    else 
        ret = _check_pass_http (parser, user, pass);
Michael Smith's avatar
Michael Smith committed
623
    config_release_config();
624 625
    return ret;
}
Michael Smith's avatar
Michael Smith committed
626

627 628 629 630 631
int connection_check_relay_pass(http_parser_t *parser)
{
    int ret;
    ice_config_t *config = config_get_config();
    char *pass = config->relay_password;
632
    char *user = config->relay_username;
Michael Smith's avatar
Michael Smith committed
633

634 635
    if(!pass || !user) {
        config_release_config();
636
        return 0;
637
    }
638

639 640 641
    ret = _check_pass_http(parser, user, pass);
    config_release_config();
    return ret;
642 643
}

644
int connection_check_source_pass(http_parser_t *parser, const char *mount)
645
{
Michael Smith's avatar
Michael Smith committed
646 647
    ice_config_t *config = config_get_config();
    char *pass = config->source_password;
648
    char *user = "source";
649
    int ret;
Michael Smith's avatar
Michael Smith committed
650
    int ice_login = config->ice_login;
651
    char *protocol;
Michael Smith's avatar
Michael Smith committed
652

653
    mount_proxy *mountinfo = config_find_mount (config, mount);
654

655 656 657 658 659 660
    if (mountinfo)
    {
        if (mountinfo->password)
            pass = mountinfo->password;
        if (mountinfo->username)
            user = mountinfo->username;
661 662 663 664
    }

    if(!pass) {
        WARN0("No source password set, rejecting source");
665
        config_release_config();
666 667 668
        return 0;
    }

669
    protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
670 671 672 673 674 675 676 677 678 679 680
    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");
        }
681
    }
682
    config_release_config();
683
    return ret;
684 685
}

686

687
static void _handle_source_request (client_t *client, char *uri, int auth_style)
688
{
689
    source_t *source;
690

691
    INFO1("Source logging in at mountpoint \"%s\"", uri);
692

693 694 695 696 697 698
    if (uri[0] != '/')
    {
        WARN0 ("source mountpoint not starting with /");
        client_send_401 (client);
        return;
    }
699
    if (auth_style == ICECAST_SOURCE_AUTH) {
700 701
        if (connection_check_source_pass (client->parser, uri) == 0)
        {
702 703 704 705
            /* 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 */
706
            INFO1("Source (%s) attempted to login with invalid or missing password", uri);
707 708 709
            client_send_401(client);
            return;
        }
710
    }
711 712 713
    source = source_reserve (uri);
    if (source)
    {
714 715 716
        if (auth_style == SHOUTCAST_SOURCE_AUTH) {
            source->shoutcast_compat = 1;
        }
717
        source->client = client;
718 719
        source->parser = client->parser;
        source->con = client->con;
720 721 722 723 724 725 726 727 728 729 730 731
        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");
732
        WARN1 ("Mountpoint %s in use", uri);
733
    }
734 735
}

736

737
static void _handle_stats_request (client_t *client, char *uri)
Jack Moffitt's avatar
Jack Moffitt committed
738
{
739
    stats_event_inc(NULL, "stats_connections");
740 741 742 743

    if (connection_check_admin_pass (client->parser) == 0)
    {
        client_send_401 (client);
744 745
        ERROR0("Bad password for stats connection");
        return;
746
    }
747

748 749 750 751 752 753 754
    client->respcode = 200;
    if (sock_write (client->con->sock, "HTTP/1.0 200 OK\r\n\r\n") < 19)
    {
        client_destroy (client);
        ERROR0 ("failed to write header");
        return;
    }
755 756

    thread_create("Stats Connection", stats_connection, (void *)client, THREAD_DETACHED);
757 758
}

759
static void _handle_get_request (client_t *client, char *passed_uri)
760
{
Michael Smith's avatar
Michael Smith committed
761 762
    int fileserve;
    int port;
763
    int i;
Karl Heyes's avatar
Karl Heyes committed
764 765
    char *serverhost = NULL;
    int serverport = 0;
766
    aliases *alias;
Michael Smith's avatar
Michael Smith committed
767
    ice_config_t *config;
768
    char *uri = passed_uri;
Michael Smith's avatar
Michael Smith committed
769 770 771 772

    config = config_get_config();
    fileserve = config->fileserve;
    port = config->port;
773
    for(i = 0; i < global.server_sockets; i++) {
774
        if(global.serversock[i] == client->con->serversock) {
775 776 777 778 779 780
            serverhost = config->listeners[i].bind_address;
            serverport = config->listeners[i].port;
            break;
        }
    }
    alias = config->aliases;
781

782 783 784
    /* there are several types of HTTP GET clients
    ** media clients, which are looking for a source (eg, URI = /stream.ogg)
    ** stats clients, which are looking for /admin/stats.xml
785
    ** and directory server authorizers, which are looking for /GUID-xxxxxxxx 
786
    ** (where xxxxxx is the GUID in question) - this isn't implemented yet.
787 788 789 790
    ** we need to handle the latter two before the former, as the latter two
    ** aren't subject to the limits.
    */
    /* TODO: add GUID-xxxxxx */
791

792 793 794
    /* Handle aliases */
    while(alias) {
        if(strcmp(uri, alias->source) == 0 && (alias->port == -1 || alias->port == serverport) && (alias->bind_address == NULL || (serverhost != NULL && strcmp(alias->bind_address, serverhost) == 0))) {
795 796
            uri = strdup (alias->destination);
            DEBUG2 ("alias has made %s into %s", passed_uri, uri);
797 798 799 800
            break;
        }
        alias = alias->next;
    }
801 802 803
    config_release_config();

    stats_event_inc(NULL, "client_connections");
804

805
    /* Dispatch all admin requests */
806 807
    if ((strcmp(uri, "/admin.cgi") == 0) ||
        (strncmp(uri, "/admin/", 7) == 0)) {
808
        admin_handle_request(client, uri);
809
        if (uri != passed_uri) free (uri);
Michael Smith's avatar
Michael Smith committed
810 811 812
        return;
    }

813 814 815 816
    /* Here we are parsing the URI request to see
    ** if the extension is .xsl, if so, then process
    ** this request as an XSLT request
    */
817 818
    if (util_check_valid_extension (uri) == XSLT_CONTENT)
    {
819
        /* If the file exists, then transform it, otherwise, write a 404 */
820 821
        DEBUG0("Stats request, sending XSL transformed stats");
        stats_transform_xslt (client, uri);
822
        if (uri != passed_uri) free (uri);
823
        return;
824
    }
825

826 827
    sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
    sock_set_nodelay(client->con->sock);
828

829 830 831
    client->write_to_client = format_generic_write_to_client;
    client->check_buffer = format_check_http_buffer;
    client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
832

833
    add_client (uri, client);
834
    if (uri != passed_uri) free (uri);
835 836
}

837
void _handle_shoutcast_compatible(connection_t *con, char *mount, char *source_password) {
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878
    char shoutcast_password[256];
    char *http_compliant;
    int http_compliant_len = 0;
    char header[4096];
    http_parser_t *parser;

    memset(shoutcast_password, 0, sizeof (shoutcast_password));
    /* Step one of shoutcast auth protocol, read encoder password (1 line) */
    if (util_read_header(con->sock, shoutcast_password, 
            sizeof (shoutcast_password), 
            READ_LINE) == 0) {
        /* either we didn't get a complete line, or we timed out */
        connection_close(con);
        return;
    }
    /* Get rid of trailing \n */
    shoutcast_password[strlen(shoutcast_password)-1] = '\000';
    if (strcmp(shoutcast_password, source_password)) {
        ERROR0("Invalid source password");
        connection_close(con);
        return;
    }
    /* Step two of shoutcast auth protocol, send OK2.  For those
       interested, OK2 means it supports metadata updates via admin.cgi,
       and the string "OK" can also be sent, but will indicate to the
       shoutcast source client to not send metadata updates.
       I believe icecast 1.x used to send OK. */
    sock_write(con->sock, "%s\r\n", "OK2");

    memset(header, 0, sizeof (header));
    /* Step three of shoutcast auth protocol, read HTTP-style
       request headers and process them.*/
    if (util_read_header(con->sock, header, sizeof (header), 
                         READ_ENTIRE_HEADER) == 0) {
        /* either we didn't get a complete header, or we timed out */
        connection_close(con);
        return;
    }
    /* 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 */
879
    http_compliant_len = strlen(header) + strlen(mount) + 20;
880
    http_compliant = (char *)calloc(1, http_compliant_len);
881 882
    snprintf (http_compliant, http_compliant_len,
            "SOURCE %s HTTP/1.0\r\n%s", mount, header);
883 884
    parser = httpp_create_parser();
    httpp_initialize(parser, NULL);
885 886 887 888 889 890 891 892 893
    if (httpp_parse (parser, http_compliant, strlen(http_compliant)))
    {
        client_t *client = client_create (con, parser);
        if (client)
        {
            _handle_source_request (client, mount, SHOUTCAST_SOURCE_AUTH);
            free (http_compliant);
            return;
        }
894
    }
895 896 897
    connection_close (con);
    httpp_destroy (parser);
    free (http_compliant);
898 899
}

900 901
static void *_handle_connection(void *arg)
{
902 903 904
    char header[4096];
    connection_t *con;
    http_parser_t *parser;
905
    char *rawuri, *uri;
906
    client_t *client;
907 908 909 910
    int i = 0;
    int continue_flag = 0;
    ice_config_t *config;
    char *source_password;
Jack Moffitt's avatar
Jack Moffitt committed
911

912
    while (global.running == ICE_RUNNING) {
Jack Moffitt's avatar
Jack Moffitt committed
913

914 915
        /* grab a connection and set the socket to blocking */
        while ((con = _get_connection())) {
Michael Smith's avatar
Michael Smith committed
916 917 918 919 920 921 922 923 924 925 926 927 928 929 930

            /* Handle meta-connections */
            if(con->event_number > 0) {
                switch(con->event_number) {
                    case EVENT_CONFIG_READ:
                        event_config_read(con->event);
                        break;
                    default:
                        ERROR1("Unknown event number: %d", con->event_number);
                        break;
                }
                free(con);
                continue;
            }

931
            stats_event_inc(NULL, "connections");
Jack Moffitt's avatar
Jack Moffitt committed
932

933
            sock_set_blocking(con->sock, SOCK_BLOCK);
Jack Moffitt's avatar
Jack Moffitt committed
934

935 936 937 938 939 940
            continue_flag = 0;
            /* Check for special shoutcast compatability processing */
            for(i = 0; i < MAX_LISTEN_SOCKETS; i++) {
                if(global.serversock[i] == con->serversock) {
                    config = config_get_config();
                    if (config->listeners[i].shoutcast_compat) {
941
                        char *shoutcast_mount = strdup (config->shoutcast_mount);
942 943 944 945 946
                        mount_proxy *mountinfo = config_find_mount (config, config->shoutcast_mount);
                        if (mountinfo && mountinfo->password)
                            source_password = strdup (mountinfo->password);
                        else
                            source_password = strdup (config->source_password);
947
                        config_release_config();
948
                        _handle_shoutcast_compatible(con, shoutcast_mount, source_password);
949
                        free(source_password);
950
                        free (shoutcast_mount);
951 952 953 954 955 956 957 958 959 960
                        continue_flag = 1;
                        break;
                    }
                    config_release_config();
                }
            }
            if(continue_flag) {
                continue;
            }

961
            /* fill header with the http header */
962
            memset(header, 0, sizeof (header));
963 964
            if (util_read_header(con->sock, header, sizeof (header), 
                                 READ_ENTIRE_HEADER) == 0) {
965 966 967 968
                /* either we didn't get a complete header, or we timed out */
                connection_close(con);
                continue;
            }
Jack Moffitt's avatar
Jack Moffitt committed
969

970 971 972 973 974 975
            parser = httpp_create_parser();
            httpp_initialize(parser, NULL);
            if (httpp_parse(parser, header, strlen(header))) {
                /* handle the connection or something */
                
                if (strcmp("ICE",  httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) &&
976
                    strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) {
977
                    ERROR0("Bad HTTP protocol detected");
978 979 980 981
                    connection_close(con);
                    httpp_destroy(parser);
                    continue;
                }
Jack Moffitt's avatar
Jack Moffitt committed
982

983 984 985
                rawuri = httpp_getvar(parser, HTTPP_VAR_URI);
                uri = util_normalise_uri(rawuri);

986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
                if (uri == NULL)
                {
                    sock_write(con->sock, "The path you requested was invalid\r\n");
                    connection_close(con);
                    httpp_destroy(parser);
                    continue;
                }
                client = client_create (con, parser);
                if (client == NULL)
                {
                    sock_write (con->sock, "HTTP/1.0 404 File Not Found\r\n"
                            "Content-Type: text/html\r\n\r\n"
                            "<b>Connection limit reached</b>");
                    connection_close(con);
                    httpp_destroy(parser);
1001 1002
                    continue;
                }
1003

1004
                if (parser->req_type == httpp_req_source) {
1005
                    _handle_source_request (client, uri, ICECAST_SOURCE_AUTH);
1006 1007
                }
                else if (parser->req_type == httpp_req_stats) {
1008
                    _handle_stats_request (client, uri);
1009 1010
                }
                else if (parser->req_type == httpp_req_get) {
1011
                    _handle_get_request (client, uri);
1012 1013
                }
                else {
1014
                    ERROR0("Wrong request type from client");
1015
                    client_send_400 (client, "unknown request");
1016 1017 1018
                }

                free(uri);
1019
                continue;
1020
            } 
1021
            else {
1022
                ERROR0("HTTP request parsing failed");
1023 1024 1025 1026 1027
                connection_close(con);
                httpp_destroy(parser);
                continue;
            }
        }
1028
        thread_sleep (100000);
1029
    }
1030
    DEBUG0 ("Connection thread done");
Jack Moffitt's avatar
Jack Moffitt committed
1031

1032
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
1033 1034 1035 1036
}

void connection_close(connection_t *con)
{
1037 1038 1039 1040
    sock_close(con->sock);
    if (con->ip) free(con->ip);
    if (con->host) free(con->host);
    free(con);
Jack Moffitt's avatar
Jack Moffitt committed
1041
}