connection.c 33.4 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

#define CATMODULE "connection"

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
/* 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
85
typedef struct con_queue_tag {
86 87
    connection_t *con;
    struct con_queue_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
88 89 90
} con_queue_t;

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

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

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

static thread_queue_t *_conhands = NULL;

104
rwlock_t _source_shutdown_rwlock;
Jack Moffitt's avatar
Jack Moffitt committed
105 106 107 108 109

static void *_handle_connection(void *arg);

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

118
    _initialized = 1;
Jack Moffitt's avatar
Jack Moffitt committed
119 120 121 122
}

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

131
    _initialized = 0;
Jack Moffitt's avatar
Jack Moffitt committed
132 133 134 135
}

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

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

142
    return id;
Jack Moffitt's avatar
Jack Moffitt committed
143 144
}

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

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

158
    return con;
159 160
}

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

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

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

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

262 263
        return con;
    }
Jack Moffitt's avatar
Jack Moffitt committed
264

265 266 267 268
    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
269

270
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
271 272 273 274
}

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

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

286
static void _push_thread(thread_queue_t **queue, thread_type *thread_id)
Jack Moffitt's avatar
Jack Moffitt committed
287
{
288 289 290 291 292 293 294 295 296 297 298 299 300 301
    /* 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
302 303
}

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

309
    thread_mutex_lock(&_queue_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
310

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

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

322
    thread_mutex_unlock(&_queue_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
323

324
    return id;
Jack Moffitt's avatar
Jack Moffitt committed
325 326 327 328
}

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

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

339 340 341 342 343
    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
344 345 346 347
}

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

    i = 0;

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

void connection_accept_loop(void)
{
363
    connection_t *con;
Jack Moffitt's avatar
Jack Moffitt committed
364

365
    _build_pool();
Jack Moffitt's avatar
Jack Moffitt committed
366

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

            connection_inject_event(EVENT_CONFIG_READ, NULL);
375
            global . schedule_config_reread = 0;
376 377
        }

378
        con = _accept_connection();
Jack Moffitt's avatar
Jack Moffitt committed
379

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

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

388
    _destroy_pool();
Jack Moffitt's avatar
Jack Moffitt committed
389

390 391 392
    /* 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
393 394 395 396
}

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

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

405 406
    thread_mutex_lock(&_queue_mutex);
    if (_queue) {
407
        node = (con_queue_t *)_queue;
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
        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
427 428
}

Michael Smith's avatar
Michael Smith committed
429 430 431 432 433 434 435 436 437
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);
}

438 439 440

/* Called when activating a source. Verifies that the source count is not
 * exceeded and applies any initial parameters.
441
 */
442 443 444 445 446 447 448 449 450 451
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;
452
        mount_proxy *mountproxy;
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
        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
        {
473
            WARN0("No content-type header, falling back to backwards compatibility mode "
474
                    "for icecast 1.x relays. Assuming content is mp3.");
475
            format_type = FORMAT_TYPE_GENERIC;
476 477
        }

Karl Heyes's avatar
Karl Heyes committed
478
        if (format_get_plugin (format_type, source) < 0)
479 480 481 482 483 484
        {
            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);
485
            source->client = NULL;
486 487 488 489 490 491
            return -1;
        }

        global.sources++;
        global_unlock();

492 493 494
        /* set global settings first */
        source->queue_size_limit = config->queue_size_limit;
        source->timeout = config->source_timeout;
Karl Heyes's avatar
Karl Heyes committed
495
        source->burst_size = config->burst_size;
496

497 498 499 500 501 502
        /* 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)
503
        {
504
            source->client = client_create (source->con, source->parser);
505 506 507 508 509 510 511 512 513 514 515 516 517
            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;
            }
        }
518

519 520 521 522
        mountproxy = config_find_mount (config, source->mount);
        if (mountproxy)
            source_apply_mount (source, mountproxy);

523 524 525 526 527 528 529
        config_release_config();

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

        return 0;
    }
530
    WARN1("Request to add source when maximum source limit "
531 532 533 534 535 536 537 538 539 540 541 542
            "reached %d", global.sources);

    global_unlock();
    config_release_config();

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

    return -1;
}


543 544
static int _check_pass_http(http_parser_t *parser, 
        char *correctuser, char *correctpass)
545 546 547 548 549 550 551 552 553 554 555 556 557
{
    /* 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);
558 559 560
    if(userpass == NULL) {
        WARN1("Base64 decode of Authorization header \"%s\" failed",
                header+6);
561
        return 0;
562
    }
563 564 565 566 567 568 569 570 571 572

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

573
    if(strcmp(username, correctuser) || strcmp(password, correctpass)) {
574 575 576
        free(userpass);
        return 0;
    }
577
    free(userpass);
578 579 580 581

    return 1;
}

582 583 584 585 586 587 588 589
static int _check_pass_icy(http_parser_t *parser, char *correctpass)
{
    char *password;

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

590
    if (strcmp(password, correctpass))
591 592 593 594 595
        return 0;
    else
        return 1;
}

596
static int _check_pass_ice(http_parser_t *parser, char *correctpass)
597
{
598
    char *password;
599 600 601 602 603

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

604
    if (strcmp(password, correctpass))
605 606 607 608 609
        return 0;
    else
        return 1;
}

610
int connection_check_admin_pass(http_parser_t *parser)
611
{
612
    int ret;
Michael Smith's avatar
Michael Smith committed
613 614 615
    ice_config_t *config = config_get_config();
    char *pass = config->admin_password;
    char *user = config->admin_username;
616
    char *protocol;
617 618 619 620 621 622

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

623 624 625 626 627
    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
628
    config_release_config();
629 630
    return ret;
}
Michael Smith's avatar
Michael Smith committed
631

632 633 634 635 636
int connection_check_relay_pass(http_parser_t *parser)
{
    int ret;
    ice_config_t *config = config_get_config();
    char *pass = config->relay_password;
637
    char *user = config->relay_username;
Michael Smith's avatar
Michael Smith committed
638

639 640
    if(!pass || !user) {
        config_release_config();
641
        return 0;
642
    }
643

644 645 646
    ret = _check_pass_http(parser, user, pass);
    config_release_config();
    return ret;
647 648
}

649
int connection_check_source_pass(http_parser_t *parser, const char *mount)
650
{
Michael Smith's avatar
Michael Smith committed
651 652
    ice_config_t *config = config_get_config();
    char *pass = config->source_password;
653
    char *user = "source";
654
    int ret;
Michael Smith's avatar
Michael Smith committed
655
    int ice_login = config->ice_login;
656
    char *protocol;
Michael Smith's avatar
Michael Smith committed
657

658
    mount_proxy *mountinfo = config_find_mount (config, mount);
659

660 661 662 663 664 665
    if (mountinfo)
    {
        if (mountinfo->password)
            pass = mountinfo->password;
        if (mountinfo->username)
            user = mountinfo->username;
666 667 668 669
    }

    if(!pass) {
        WARN0("No source password set, rejecting source");
670
        config_release_config();
671 672 673
        return 0;
    }

674
    protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
675 676 677 678 679 680 681 682 683 684 685
    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");
        }
686
    }
687
    config_release_config();
688
    return ret;
689 690
}

691

692
static void _handle_source_request (client_t *client, char *uri, int auth_style)
693
{
694
    source_t *source;
695

696
    INFO1("Source logging in at mountpoint \"%s\"", uri);
697

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

741

742
static void _handle_stats_request (client_t *client, char *uri)
Jack Moffitt's avatar
Jack Moffitt committed
743
{
744
    stats_event_inc(NULL, "stats_connections");
745 746 747 748

    if (connection_check_admin_pass (client->parser) == 0)
    {
        client_send_401 (client);
749 750
        ERROR0("Bad password for stats connection");
        return;
751
    }
752

753
    stats_event_inc(NULL, "stats");
754 755

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

758
static void _handle_get_request (client_t *client, char *passed_uri)
759 760 761
{
    char *fullpath;
    int bytes;
762 763
    struct stat statbuf;
    source_t *source;
Michael Smith's avatar
Michael Smith committed
764
    int fileserve;
765
    char *host = NULL;
Michael Smith's avatar
Michael Smith committed
766
    int port;
767
    int i;
Karl Heyes's avatar
Karl Heyes committed
768 769
    char *serverhost = NULL;
    int serverport = 0;
770
    aliases *alias;
Michael Smith's avatar
Michael Smith committed
771
    ice_config_t *config;
772
    int ret;
773
    char *uri = passed_uri;
Michael Smith's avatar
Michael Smith committed
774 775 776

    config = config_get_config();
    fileserve = config->fileserve;
777 778
    if (config->hostname)
        host = strdup (config->hostname);
Michael Smith's avatar
Michael Smith committed
779
    port = config->port;
780
    for(i = 0; i < global.server_sockets; i++) {
781
        if(global.serversock[i] == client->con->serversock) {
782 783 784 785 786 787
            serverhost = config->listeners[i].bind_address;
            serverport = config->listeners[i].port;
            break;
        }
    }
    alias = config->aliases;
788

789 790 791
    /* 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
792
    ** and directory server authorizers, which are looking for /GUID-xxxxxxxx 
793
    ** (where xxxxxx is the GUID in question) - this isn't implemented yet.
794 795 796 797
    ** we need to handle the latter two before the former, as the latter two
    ** aren't subject to the limits.
    */
    /* TODO: add GUID-xxxxxx */
798

799 800 801
    /* 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))) {
802 803
            uri = strdup (alias->destination);
            DEBUG2 ("alias has made %s into %s", passed_uri, uri);
804 805 806 807
            break;
        }
        alias = alias->next;
    }
808 809 810
    config_release_config();

    stats_event_inc(NULL, "client_connections");
811

812
    /* Dispatch all admin requests */
813 814
    if ((strcmp(uri, "/admin.cgi") == 0) ||
        (strncmp(uri, "/admin/", 7) == 0)) {
815
        admin_handle_request(client, uri);
816
        if (uri != passed_uri) free (uri);
817
        free (host);
Michael Smith's avatar
Michael Smith committed
818 819 820
        return;
    }

821 822 823 824
    /* Here we are parsing the URI request to see
    ** if the extension is .xsl, if so, then process
    ** this request as an XSLT request
    */
825 826
    fullpath = util_get_path_from_normalised_uri(uri);
    if (util_check_valid_extension(fullpath) == XSLT_CONTENT) {
827 828
        /* If the file exists, then transform it, otherwise, write a 404 */
        if (stat(fullpath, &statbuf) == 0) {
829
            DEBUG0("Stats request, sending XSL transformed stats");
830
            client->respcode = 200;
831
            bytes = sock_write(client->con->sock, 
832 833 834
                    "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);
835 836 837
            client_destroy(client);
        }
        else {
838
            client_send_404(client, "The file you requested could not be found");
839
        }
840
        free(fullpath);
841
        if (uri != passed_uri) free (uri);
842
        free (host);
843
        return;
844
    }
845
    else if(fileserve && stat(fullpath, &statbuf) == 0 && 
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
846
#ifdef _WIN32
847
            ((statbuf.st_mode) & _S_IFREG))
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
848
#else
849
            S_ISREG(statbuf.st_mode)) 
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
850
#endif
Michael Smith's avatar
Michael Smith committed
851
    {
852
        fserve_client_create(client, fullpath);
853
        free(fullpath);
854
        if (uri != passed_uri) free (uri);
855
        free (host);
856 857
        return;
    }
858 859 860 861 862 863
    free(fullpath);

    if(strcmp(util_get_extension(uri), "m3u") == 0) {
        char *sourceuri = strdup(uri);
        char *dot = strrchr(sourceuri, '.');
        *dot = 0;
864 865
        client->respcode = 200;
        bytes = sock_write(client->con->sock,
866 867
                    "HTTP/1.0 200 OK\r\n"
                    "Content-Type: audio/x-mpegurl\r\n\r\n"
868
                    "http://%s:%d%s\r\n", 
Michael Smith's avatar
Michael Smith committed
869 870
                    host, 
                    port,
871 872
                    sourceuri
                    );
873 874
        if(bytes > 0) client->con->sent_bytes = bytes;
        client_destroy(client);
875
        free(sourceuri);
876
        if (uri != passed_uri) free (uri);
877
        free (host);
878 879
        return;
    }
880
    free (host);
881

882 883 884
    avl_tree_rlock(global.source_tree);
    source = source_find_mount(uri);
    if (source) {
885
        DEBUG0("Source found for client");
Michael Smith's avatar
Michael Smith committed
886 887 888 889 890 891 892

        /* 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) {
            avl_tree_unlock(global.source_tree);
893
            client_send_404(client, "This mount is unavailable.");
894
            if (uri != passed_uri) free (uri);
Michael Smith's avatar
Michael Smith committed
895
            return;
896 897 898 899 900 901
        }
        if (source->running == 0)
        {
            avl_tree_unlock(global.source_tree);
            DEBUG0("inactive source, client dropped");
            client_send_404(client, "This mount is unavailable.");
902
            if (uri != passed_uri) free (uri);
903
            return;
Michael Smith's avatar
Michael Smith committed
904 905 906 907
        }

        /* Check for any required authentication first */
        if(source->authenticator != NULL) {
908 909
            ret = auth_check_client(source, client);
            if(ret != AUTH_OK) {
910
                avl_tree_unlock(global.source_tree);
911 912 913 914 915 916 917 918
                if (ret == AUTH_FORBIDDEN) {
                    INFO1("Client attempted to log multiple times to source "
                        "(\"%s\")", uri);
                    client_send_403(client);
                }
                else {
                /* If not FORBIDDEN, default to 401 */
                    INFO1("Client attempted to log in to source (\"%s\")with "
Michael Smith's avatar
Michael Smith committed
919
                        "incorrect or missing password", uri);
920 921
                    client_send_401(client);
                }
922
                if (uri != passed_uri) free (uri);
Michael Smith's avatar
Michael Smith committed
923 924 925 926
                return;
            }
        }

927
        global_lock();
Michael Smith's avatar
Michael Smith committed
928 929 930 931
        /* 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.
         */
932
        if (source->max_listeners != -1 && 
Michael Smith's avatar
Michael Smith committed
933 934
                source->listeners >= source->max_listeners) 
        {
935
            global_unlock();
Michael Smith's avatar
Michael Smith committed
936
            avl_tree_unlock(global.source_tree);
937 938
            client_send_404(client, 
                    "Too many clients on this mountpoint. Try again later.");
939
            if (uri != passed_uri) free (uri);
Michael Smith's avatar
Michael Smith committed
940 941
            return;
        }
942 943
        global_unlock();
                        
Karl Heyes's avatar
Karl Heyes committed
944
        source->format->create_client_data (source, client);
Michael Smith's avatar
Michael Smith committed
945 946

        source->format->client_send_headers(source->format, source, client);
947 948
                        
        bytes = sock_write(client->con->sock, "\r\n");
949
        if(bytes > 0) client->con->sent_bytes += bytes;
950 951
                            
        sock_set_blocking(client->con->sock, SOCK_NONBLOCK);