connection.c 28.8 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>
Jack Moffitt's avatar
Jack Moffitt committed
22
#include <sys/types.h>
23
#include <sys/stat.h>
24 25 26
#ifdef HAVE_POLL
#include <sys/poll.h>
#endif
27 28 29

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

#include "os.h"

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

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

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

#define CATMODULE "connection"

typedef struct con_queue_tag {
69 70
    connection_t *con;
    struct con_queue_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
71 72 73
} con_queue_t;

typedef struct _thread_queue_tag {
74 75
    thread_type *thread_id;
    struct _thread_queue_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
76 77 78 79 80 81 82 83 84 85 86 87
} thread_queue_t;

static mutex_t _connection_mutex;
static unsigned long _current_id = 0;
static int _initialized = 0;
static cond_t _pool_cond;

static con_queue_t *_queue = NULL;
static mutex_t _queue_mutex;

static thread_queue_t *_conhands = NULL;

88
rwlock_t _source_shutdown_rwlock;
Jack Moffitt's avatar
Jack Moffitt committed
89 90 91 92 93

static void *_handle_connection(void *arg);

void connection_initialize(void)
{
94 95 96 97
    if (_initialized) return;
    
    thread_mutex_create(&_connection_mutex);
    thread_mutex_create(&_queue_mutex);
98
    thread_mutex_create(&move_clients_mutex);
99 100
    thread_rwlock_create(&_source_shutdown_rwlock);
    thread_cond_create(&_pool_cond);
101
    thread_cond_create(&global.shutdown_cond);
Jack Moffitt's avatar
Jack Moffitt committed
102

103
    _initialized = 1;
Jack Moffitt's avatar
Jack Moffitt committed
104 105 106 107
}

void connection_shutdown(void)
{
108 109
    if (!_initialized) return;
    
110
    thread_cond_destroy(&global.shutdown_cond);
111 112 113 114
    thread_cond_destroy(&_pool_cond);
    thread_rwlock_destroy(&_source_shutdown_rwlock);
    thread_mutex_destroy(&_queue_mutex);
    thread_mutex_destroy(&_connection_mutex);
115
    thread_mutex_destroy(&move_clients_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
116

117
    _initialized = 0;
Jack Moffitt's avatar
Jack Moffitt committed
118 119 120 121
}

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

124 125 126
    thread_mutex_lock(&_connection_mutex);
    id = _current_id++;
    thread_mutex_unlock(&_connection_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
127

128
    return id;
Jack Moffitt's avatar
Jack Moffitt committed
129 130
}

131
connection_t *create_connection(sock_t sock, sock_t serversock, char *ip) {
132 133 134 135
    connection_t *con;
    con = (connection_t *)malloc(sizeof(connection_t));
    memset(con, 0, sizeof(connection_t));
    con->sock = sock;
136
    con->serversock = serversock;
137 138 139
    con->con_time = time(NULL);
    con->id = _next_connection_id();
    con->ip = ip;
Michael Smith's avatar
Michael Smith committed
140 141 142 143

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

144
    return con;
145 146
}

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
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 {
167
        int dst;
168
        for(i=0; i < global.server_sockets; i++) {
169
            if(ufds[i].revents & POLLIN)
170
                return ufds[i].fd;
171 172 173 174 175 176 177 178 179
            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;
            }
180
        }
181 182 183 184 185 186 187 188 189 190 191
        /* 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;
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
    }
#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;
209
        tv.tv_usec = (timeout % 1000) * 1000;
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
        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
230 231
static connection_t *_accept_connection(void)
{
232 233 234
    int sock;
    connection_t *con;
    char *ip;
235
    int serversock; 
Jack Moffitt's avatar
Jack Moffitt committed
236

237 238 239
    serversock = wait_for_serversock(100);
    if(serversock < 0)
        return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
240

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

244 245
    sock = sock_accept(serversock, ip, MAX_ADDR_LEN);
    if (sock >= 0) {
246
        con = create_connection(sock, serversock, ip);
Jack Moffitt's avatar
Jack Moffitt committed
247

248 249
        return con;
    }
Jack Moffitt's avatar
Jack Moffitt committed
250

251 252 253 254
    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
255

256
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
257 258 259 260
}

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

263 264 265 266 267 268 269
    node = (con_queue_t *)malloc(sizeof(con_queue_t));
    
    thread_mutex_lock(&_queue_mutex);
    node->con = con;
    node->next = _queue;
    _queue = node;
    thread_mutex_unlock(&_queue_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
270 271 272 273 274

}

static void _signal_pool(void)
{
275
    thread_cond_signal(&_pool_cond);
Jack Moffitt's avatar
Jack Moffitt committed
276 277
}

278
static void _push_thread(thread_queue_t **queue, thread_type *thread_id)
Jack Moffitt's avatar
Jack Moffitt committed
279
{
280 281 282 283 284 285 286 287 288 289 290 291 292 293
    /* 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
294 295
}

296
static thread_type *_pop_thread(thread_queue_t **queue)
Jack Moffitt's avatar
Jack Moffitt committed
297
{
298 299
    thread_type *id;
    thread_queue_t *item;
Jack Moffitt's avatar
Jack Moffitt committed
300

301
    thread_mutex_lock(&_queue_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
302

303 304 305 306 307
    item = *queue;
    if (item == NULL) {
        thread_mutex_unlock(&_queue_mutex);
        return NULL;
    }
Jack Moffitt's avatar
Jack Moffitt committed
308

309 310 311 312
    *queue = item->next;
    item->next = NULL;
    id = item->thread_id;
    free(item);
Jack Moffitt's avatar
Jack Moffitt committed
313

314
    thread_mutex_unlock(&_queue_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
315

316
    return id;
Jack Moffitt's avatar
Jack Moffitt committed
317 318 319 320
}

static void _build_pool(void)
{
321 322
    ice_config_t *config;
    int i;
323
    thread_type *tid;
324
    char buff[64];
Michael Smith's avatar
Michael Smith committed
325
    int threadpool_size;
Jack Moffitt's avatar
Jack Moffitt committed
326

327
    config = config_get_config();
Michael Smith's avatar
Michael Smith committed
328 329
    threadpool_size = config->threadpool_size;
    config_release_config();
Jack Moffitt's avatar
Jack Moffitt committed
330

331 332 333 334 335
    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
336 337 338 339
}

static void _destroy_pool(void)
{
340 341 342 343 344 345 346 347 348 349 350 351
    thread_type *id;
    int i;

    i = 0;

    thread_cond_broadcast(&_pool_cond);
    id = _pop_thread(&_conhands);
    while (id != NULL) {
        thread_join(id);
        _signal_pool();
        id = _pop_thread(&_conhands);
    }
Jack Moffitt's avatar
Jack Moffitt committed
352 353 354 355
}

void connection_accept_loop(void)
{
356
    connection_t *con;
Jack Moffitt's avatar
Jack Moffitt committed
357

358
    _build_pool();
Jack Moffitt's avatar
Jack Moffitt committed
359

360 361
    while (global.running == ICE_RUNNING)
    {
362
        if (global . schedule_config_reread)
363 364 365 366 367
        {
            /* reread config file */
            INFO0("Scheduling config reread ...");

            connection_inject_event(EVENT_CONFIG_READ, NULL);
368
            global . schedule_config_reread = 0;
369 370
        }

371
        con = _accept_connection();
Jack Moffitt's avatar
Jack Moffitt committed
372

373 374 375 376 377
        if (con) {
            _add_connection(con);
            _signal_pool();
        }
    }
Jack Moffitt's avatar
Jack Moffitt committed
378

379 380 381
    /* Give all the other threads notification to shut down */
    thread_cond_broadcast(&global.shutdown_cond);

382
    _destroy_pool();
Jack Moffitt's avatar
Jack Moffitt committed
383

384 385 386
    /* 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
387 388 389 390
}

static connection_t *_get_connection(void)
{
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
    con_queue_t *node = NULL;
    con_queue_t *oldnode = NULL;
    connection_t *con = NULL;

    thread_mutex_lock(&_queue_mutex);
    if (_queue) {
        node = _queue;
        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
417 418
}

Michael Smith's avatar
Michael Smith committed
419 420 421 422 423 424 425 426 427 428
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);
    _signal_pool();
}

429 430 431
/* TODO: Make this return an appropriate error code so that we can use HTTP
 * codes where appropriate
 */
432
int connection_create_source(client_t *client, connection_t *con, http_parser_t *parser, char *mount) {
433 434
    source_t *source;
    char *contenttype;
Michael Smith's avatar
Michael Smith committed
435
    mount_proxy *mountproxy, *mountinfo = NULL;
Michael Smith's avatar
Michael Smith committed
436 437 438 439 440 441
    int source_limit;
    ice_config_t *config;

    config = config_get_config();
    source_limit = config->source_limit;
    config_release_config();
442

443 444 445 446 447 448 449 450 451 452 453 454 455
    /* check to make sure this source wouldn't
    ** be over the limit
    */
    global_lock();
    if (global.sources >= source_limit) {
        INFO1("Source (%s) logged in, but there are too many sources", mount);
        global_unlock();
        return 0;
    }
    global.sources++;
    global_unlock();

    stats_event_inc(NULL, "sources");
Michael Smith's avatar
Michael Smith committed
456
    
Michael Smith's avatar
Michael Smith committed
457 458 459 460
    config = config_get_config();
    mountproxy = config->mounts;
    thread_mutex_lock(&(config_locks()->mounts_lock));

Michael Smith's avatar
Michael Smith committed
461 462 463 464 465 466 467
    while(mountproxy) {
        if(!strcmp(mountproxy->mountname, mount)) {
            mountinfo = mountproxy;
            break;
        }
        mountproxy = mountproxy->next;
    }
468

469
    contenttype = httpp_getvar(parser, "content-type");
470

471 472 473 474
    if (contenttype != NULL) {
        format_type_t format = format_get_type(contenttype);
        if (format == FORMAT_ERROR) {
            WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
Michael Smith's avatar
Michael Smith committed
475
            thread_mutex_unlock(&(config_locks()->mounts_lock));
476
            config_release_config();
477
            goto fail;
478 479
        } else {
            source = source_create(client, con, parser, mount, 
Michael Smith's avatar
Michael Smith committed
480
                    format, mountinfo);
Michael Smith's avatar
Michael Smith committed
481
            thread_mutex_unlock(&(config_locks()->mounts_lock));
482 483
        }
    } else {
484
        format_type_t format = FORMAT_TYPE_MP3;
485
        ERROR0("No content-type header, falling back to backwards compatibility mode for icecast 1.x relays. Assuming content is mp3.");
Michael Smith's avatar
Michael Smith committed
486
        source = source_create(client, con, parser, mount, format, mountinfo);
Michael Smith's avatar
Michael Smith committed
487
        thread_mutex_unlock(&(config_locks()->mounts_lock));
488
    }
489
    config_release_config();
490

491
    source->send_return = 1;
492 493 494 495
    source->shutdown_rwlock = &_source_shutdown_rwlock;
    sock_set_blocking(con->sock, SOCK_NONBLOCK);
    thread_create("Source Thread", source_main, (void *)source, THREAD_DETACHED);
    return 1;
496 497 498 499 500 501 502 503

fail:
    global_lock();
    global.sources--;
    global_unlock();

    stats_event_dec(NULL, "sources");
    return 0;
504 505
}

506 507
static int _check_pass_http(http_parser_t *parser, 
        char *correctuser, char *correctpass)
508 509 510 511 512 513 514 515 516 517 518 519 520
{
    /* 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);
521 522 523
    if(userpass == NULL) {
        WARN1("Base64 decode of Authorization header \"%s\" failed",
                header+6);
524
        return 0;
525
    }
526 527 528 529 530 531 532 533 534 535

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

536
    if(strcmp(username, correctuser) || strcmp(password, correctpass)) {
537 538 539
        free(userpass);
        return 0;
    }
540
    free(userpass);
541 542 543 544

    return 1;
}

545 546 547 548 549 550 551 552
static int _check_pass_icy(http_parser_t *parser, char *correctpass)
{
    char *password;

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

553
    if (strcmp(password, correctpass))
554 555 556 557 558
        return 0;
    else
        return 1;
}

559
static int _check_pass_ice(http_parser_t *parser, char *correctpass)
560
{
561
    char *password;
562 563 564 565 566

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

567
    if (strcmp(password, correctpass))
568 569 570 571 572
        return 0;
    else
        return 1;
}

573
int connection_check_admin_pass(http_parser_t *parser)
574
{
575
    int ret;
Michael Smith's avatar
Michael Smith committed
576 577 578
    ice_config_t *config = config_get_config();
    char *pass = config->admin_password;
    char *user = config->admin_username;
579 580 581 582 583 584 585

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

    ret = _check_pass_http(parser, user, pass);
Michael Smith's avatar
Michael Smith committed
586
    config_release_config();
587 588
    return ret;
}
Michael Smith's avatar
Michael Smith committed
589

590 591 592 593 594 595
int connection_check_relay_pass(http_parser_t *parser)
{
    int ret;
    ice_config_t *config = config_get_config();
    char *pass = config->relay_password;
    char *user = "relay";
Michael Smith's avatar
Michael Smith committed
596

597 598
    if(!pass || !user) {
        config_release_config();
599
        return 0;
600
    }
601

602 603 604
    ret = _check_pass_http(parser, user, pass);
    config_release_config();
    return ret;
605 606
}

607
int connection_check_source_pass(http_parser_t *parser, char *mount)
608
{
Michael Smith's avatar
Michael Smith committed
609 610
    ice_config_t *config = config_get_config();
    char *pass = config->source_password;
611
    char *user = "source";
612
    int ret;
Michael Smith's avatar
Michael Smith committed
613
    int ice_login = config->ice_login;
614
    char *protocol;
Michael Smith's avatar
Michael Smith committed
615 616 617

    mount_proxy *mountinfo = config->mounts;
    thread_mutex_lock(&(config_locks()->mounts_lock));
618

619 620
    while(mountinfo) {
        if(!strcmp(mountinfo->mountname, mount)) {
621 622 623 624
            if(mountinfo->password)
                pass = mountinfo->password;
            if(mountinfo->username)
                user = mountinfo->username;
625 626
            break;
        }
627
        mountinfo = mountinfo->next;
628 629
    }

Michael Smith's avatar
Michael Smith committed
630 631
    thread_mutex_unlock(&(config_locks()->mounts_lock));

632 633
    if(!pass) {
        WARN0("No source password set, rejecting source");
634
        config_release_config();
635 636 637
        return 0;
    }

638
    protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
639 640 641 642 643 644 645 646 647 648 649
    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");
        }
650
    }
651
    config_release_config();
652
    return ret;
653 654
}

655 656 657
static void _handle_source_request(connection_t *con, 
        http_parser_t *parser, char *uri)
{
658 659
    client_t *client;

660
    client = client_create(con, parser);
661

662 663
    INFO1("Source logging in at mountpoint \"%s\"", uri);
    stats_event_inc(NULL, "source_connections");
664 665
                
    if (!connection_check_source_pass(parser, uri)) {
Michael Smith's avatar
Michael Smith committed
666 667 668 669
        /* 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 */
670
        INFO1("Source (%s) attempted to login with invalid or missing password", uri);
671
        client_send_401(client);
672
        return;
673
    }
674

675 676 677
    /* check to make sure this source has
    ** a unique mountpoint
    */
678

679
    avl_tree_rlock(global.source_tree);
Michael Smith's avatar
Michael Smith committed
680
    if (source_find_mount_raw(uri) != NULL) {
681 682
        avl_tree_unlock(global.source_tree);
        INFO1("Source tried to log in as %s, but mountpoint is already used", uri);
683
        client_send_404(client, "Mountpoint in use");
684 685 686
        return;
    }
    avl_tree_unlock(global.source_tree);
687

688
    if (!connection_create_source(client, con, parser, uri)) {
689
        client_send_404(client, "Mountpoint in use");
690
    }
691 692 693 694
}

static void _handle_stats_request(connection_t *con, 
        http_parser_t *parser, char *uri)
Jack Moffitt's avatar
Jack Moffitt committed
695
{
696
    stats_connection_t *stats;
697

698 699 700
    stats_event_inc(NULL, "stats_connections");
                
    if (!connection_check_admin_pass(parser)) {
701
        ERROR0("Bad password for stats connection");
702 703
        connection_close(con);
        httpp_destroy(parser);
704
        return;
705 706 707 708 709 710 711 712 713 714
    }
                    
    stats_event_inc(NULL, "stats");
                    
    /* create stats connection and create stats handler thread */
    stats = (stats_connection_t *)malloc(sizeof(stats_connection_t));
    stats->parser = parser;
    stats->con = con;
                    
    thread_create("Stats Connection", stats_connection, (void *)stats, THREAD_DETACHED);
715 716 717 718 719 720
}

static void _handle_get_request(connection_t *con,
        http_parser_t *parser, char *uri)
{
    char *fullpath;
721
    client_t *client;
722
    int bytes;
723 724
    struct stat statbuf;
    source_t *source;
Michael Smith's avatar
Michael Smith committed
725 726 727
    int fileserve;
    char *host;
    int port;
728
    int i;
Karl Heyes's avatar
Karl Heyes committed
729 730
    char *serverhost = NULL;
    int serverport = 0;
731
    aliases *alias;
Michael Smith's avatar
Michael Smith committed
732 733 734 735 736 737 738
    ice_config_t *config;
    int client_limit;

    config = config_get_config();
    fileserve = config->fileserve;
    host = config->hostname;
    port = config->port;
739 740 741 742 743 744 745 746
    for(i = 0; i < MAX_LISTEN_SOCKETS; i++) {
        if(global.serversock[i] == con->serversock) {
            serverhost = config->listeners[i].bind_address;
            serverport = config->listeners[i].port;
            break;
        }
    }
    alias = config->aliases;
Michael Smith's avatar
Michael Smith committed
747 748 749
    client_limit = config->client_limit;
    config_release_config();

750 751 752

    DEBUG0("Client connected");

753 754 755 756 757 758 759
    /* make a client */
    client = client_create(con, parser);
    stats_event_inc(NULL, "client_connections");
                    
    /* 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
760
    ** and directory server authorizers, which are looking for /GUID-xxxxxxxx 
761
    ** (where xxxxxx is the GUID in question) - this isn't implemented yet.
762 763 764 765
    ** we need to handle the latter two before the former, as the latter two
    ** aren't subject to the limits.
    */
    /* TODO: add GUID-xxxxxx */
766

767 768 769 770 771 772 773 774 775
    /* 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))) {
            uri = alias->destination;
            break;
        }
        alias = alias->next;
    }

776
    /* Dispatch all admin requests */
777
    if (strncmp(uri, "/admin/", 7) == 0) {
778
        admin_handle_request(client, uri);
Michael Smith's avatar
Michael Smith committed
779 780 781
        return;
    }

782 783 784 785
    /* Here we are parsing the URI request to see
    ** if the extension is .xsl, if so, then process
    ** this request as an XSLT request
    */
786 787
    fullpath = util_get_path_from_normalised_uri(uri);
    if (util_check_valid_extension(fullpath) == XSLT_CONTENT) {
788 789
        /* If the file exists, then transform it, otherwise, write a 404 */
        if (stat(fullpath, &statbuf) == 0) {
790
            DEBUG0("Stats request, sending XSL transformed stats");
791
            client->respcode = 200;
792
            bytes = sock_write(client->con->sock, 
793 794 795
                    "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
            if(bytes > 0) client->con->sent_bytes = bytes;
            stats_transform_xslt(client, fullpath);
796 797 798
            client_destroy(client);
        }
        else {
799
            client_send_404(client, "The file you requested could not be found");
800
        }
801 802
        free(fullpath);
        return;
803
    }
804
    else if(fileserve && stat(fullpath, &statbuf) == 0 && 
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
805
#ifdef _WIN32
806
            ((statbuf.st_mode) & _S_IFREG))
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
807
#else
808
            S_ISREG(statbuf.st_mode)) 
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
809
#endif
Michael Smith's avatar
Michael Smith committed
810
    {
811
        fserve_client_create(client, fullpath);
812 813 814
        free(fullpath);
        return;
    }
815 816 817 818 819 820
    free(fullpath);

    if(strcmp(util_get_extension(uri), "m3u") == 0) {
        char *sourceuri = strdup(uri);
        char *dot = strrchr(sourceuri, '.');
        *dot = 0;
821 822 823
        avl_tree_rlock(global.source_tree);
        source = source_find_mount(sourceuri);
           if (source) {
824 825 826 827
            client->respcode = 200;
            bytes = sock_write(client->con->sock,
                    "HTTP/1.0 200 OK\r\n"
                    "Content-Type: audio/x-mpegurl\r\n\r\n"
828
                    "http://%s:%d%s\r\n", 
Michael Smith's avatar
Michael Smith committed
829 830
                    host, 
                    port,
831 832 833
                    sourceuri
                    );
            if(bytes > 0) client->con->sent_bytes = bytes;
834
            client_destroy(client);
835
        }
Michael Smith's avatar
Michael Smith committed
836
        else if(fileserve) {
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
            fullpath = util_get_path_from_normalised_uri(sourceuri);
            if(stat(fullpath, &statbuf) == 0) {
                fserve_client_create(client, fullpath);
                free(fullpath);
            }
            else {
                free(fullpath);
                fullpath = util_get_path_from_normalised_uri(uri);
                if(stat(fullpath, &statbuf) == 0) {
                    fserve_client_create(client, fullpath);
                    free(fullpath);
                }
                else {
                    free(fullpath);
                    client_send_404(client, 
                            "The file you requested could not be found");
                }
            }
        }
856 857 858
        else {
            client_send_404(client, "The file you requested could not be found");
        }
859
        avl_tree_unlock(global.source_tree);
860 861 862 863
        free(sourceuri);
        return;
    }

864 865
    global_lock();
    if (global.clients >= client_limit) {
Michael Smith's avatar
Michael Smith committed
866 867
        client_send_504(client,
                "The server is already full. Try again later.");
868
        global_unlock();
869
        return;
870 871 872 873 874 875
    }
    global_unlock();
                    
    avl_tree_rlock(global.source_tree);
    source = source_find_mount(uri);
    if (source) {
876
        DEBUG0("Source found for client");
Michael Smith's avatar
Michael Smith committed
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899

        /* The source may not be the requested source - it might have gone
         * via one or more fallbacks. We only reject it for no-mount if it's
         * the originally requested source
         */
        if(strcmp(uri, source->mount) == 0 && source->no_mount) {
            client_send_404(client, "This mount is unavailable.");
            avl_tree_unlock(global.source_tree);
            return;
        }

        /* Check for any required authentication first */
        if(source->authenticator != NULL) {
            if(auth_check_client(source, client) != AUTH_OK) {
                INFO1("Client attempted to log in to source (\"%s\")with "
                        "incorrect or missing password", uri);
                client_send_401(client);
                avl_tree_unlock(global.source_tree);
                return;
            }
        }

        /* And then check that there's actually room in the server... */
900 901
        global_lock();
        if (global.clients >= client_limit) {
Michael Smith's avatar
Michael Smith committed
902 903
            client_send_504(client, 
                    "The server is already full. Try again later.");
904
            global_unlock();
905 906
            avl_tree_unlock(global.source_tree);
            return;
907
        }
Michael Smith's avatar
Michael Smith committed
908 909 910 911
        /* Early-out for per-source max listeners. This gets checked again
         * by the source itself, later. This route gives a useful message to
         * the client, also.
         */
Michael Smith's avatar
Michael Smith committed
912 913 914 915 916
        else if(source->max_listeners != -1 && 
                source->listeners >= source->max_listeners) 
        {
            client_send_504(client, 
                    "Too many clients on this mountpoint. Try again later.");
917
            global_unlock();
Michael Smith's avatar
Michael Smith committed
918 919 920
            avl_tree_unlock(global.source_tree);
            return;
        }
921 922 923
        global.clients++;
        global_unlock();
                        
Michael Smith's avatar
Michael Smith committed
924 925 926 927
        client->format_data = source->format->create_client_data(
                source->format, source, client);

        source->format->client_send_headers(source->format, source, client);
928 929
                        
        bytes = sock_write(client->con->sock, "\r\n");
930
        if(bytes > 0) client->con->sent_bytes += bytes;
931 932
                            
        sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
933
        sock_set_nodelay(client->con->sock);
934 935 936 937 938 939 940 941 942
                        
        avl_tree_wlock(source->pending_tree);
        avl_insert(source->pending_tree, (void *)client);
        avl_tree_unlock(source->pending_tree);
    }
                    
    avl_tree_unlock(global.source_tree);
                    
    if (!source) {
943 944
        DEBUG0("Source not found for client");
        client_send_404(client, "The source you requested could not be found.");
945
    }
946 947 948 949
}

static void *_handle_connection(void *arg)
{
950 951 952
    char header[4096];
    connection_t *con;
    http_parser_t *parser;
953
    char *rawuri, *uri;
954
    client_t *client;
Jack Moffitt's avatar
Jack Moffitt committed
955

956 957
    while (global.running == ICE_RUNNING) {
        memset(header, 0, 4096);
Jack Moffitt's avatar
Jack Moffitt committed
958

959 960
        thread_cond_wait(&_pool_cond);
        if (global.running != ICE_RUNNING) break;
Jack Moffitt's avatar
Jack Moffitt committed
961

962 963
        /* grab a connection and set the socket to blocking */
        while ((con = _get_connection())) {
Michael Smith's avatar
Michael Smith committed
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978

            /* 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;
            }

979
            stats_event_inc(NULL, "connections");
Jack Moffitt's avatar
Jack Moffitt committed
980

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

983 984 985 986 987 988
            /* fill header with the http header */
            if (util_read_header(con->sock, header, 4096) == 0) {
                /* either we didn't get a complete header, or we timed out */
                connection_close(con);
                continue;
            }
Jack Moffitt's avatar
Jack Moffitt committed
989

990 991 992 993 994 995
            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)) &&
996
                    strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) {
997
                    ERROR0("Bad HTTP protocol detected");
998 999 1000 1001
                    connection_close(con);
                    httpp_destroy(parser);
                    continue;
                }
Jack Moffitt's avatar
Jack Moffitt committed
1002

1003 1004 1005 1006
                rawuri = httpp_getvar(parser, HTTPP_VAR_URI);
                uri = util_normalise_uri(rawuri);

                if(!uri) {
1007
                    client = client_create(con, parser);
1008
                    client_send_404(client, "The path you requested was invalid");
1009 1010
                    continue;
                }
1011

1012
                if (parser->req_type == httpp_req_source) {
1013 1014 1015 1016 1017 1018 1019 1020 1021
                    _handle_source_request(con, parser, uri);
                }
                else if (parser->req_type == httpp_req_stats) {
                    _handle_stats_request(con, parser, uri);
                }
                else if (parser->req_type == httpp_req_get) {
                    _handle_get_request(con, parser, uri);
                }
                else {
1022
                    ERROR0("Wrong request type from client");
1023 1024
                    connection_close(con);
                    httpp_destroy(parser);
1025 1026 1027
                }

                free(uri);
1028
            } 
1029 1030
            else if(httpp_parse_icy(parser, header, strlen(header))) {
                /* TODO: Map incoming icy connections to /icy_0, etc. */
1031
                char mount[20];
Karl Heyes's avatar
Karl Heyes committed
1032
                unsigned i = 0;
1033 1034 1035 1036

                strcpy(mount, "/");

                avl_tree_rlock(global.source_tree);
Karl Heyes's avatar
Karl Heyes committed
1037 1038
                while (source_find_mount (mount) != NULL) {
                    snprintf (mount, sizeof (mount), "/icy_%u", i++);
1039 1040 1041 1042
                }
                avl_tree_unlock(global.source_tree);

                _handle_source_request(con, parser, mount);
1043 1044
            }
            else {
1045
                ERROR0("HTTP request parsing failed");
1046 1047 1048 1049 1050 1051
                connection_close(con);
                httpp_destroy(parser);
                continue;
            }
        }
    }
Jack Moffitt's avatar
Jack Moffitt committed
1052

1053
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
1054 1055 1056 1057
}

void connection_close(connection_t *con)
{
1058 1059 1060 1061
    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
1062
}