connection.c 30.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
#include "compat.h"
Jack Moffitt's avatar
Jack Moffitt committed
38

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

83 84 85 86 87 88 89
typedef struct client_queue_tag {
    client_t *client;
    int offset;
    int stream_offset;
    int shoutcast;
    struct client_queue_tag *next;
} client_queue_t;
Jack Moffitt's avatar
Jack Moffitt committed
90 91

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

static mutex_t _connection_mutex;
97
static volatile unsigned long _current_id = 0;
Jack Moffitt's avatar
Jack Moffitt committed
98
static int _initialized = 0;
99
static thread_type *tid;
Jack Moffitt's avatar
Jack Moffitt committed
100

101 102 103 104
static volatile client_queue_t *_req_queue = NULL, **_req_queue_tail = &_req_queue;
static volatile client_queue_t *_con_queue = NULL, **_con_queue_tail = &_con_queue;
static mutex_t _con_queue_mutex;
static mutex_t _req_queue_mutex;
Jack Moffitt's avatar
Jack Moffitt committed
105

106
rwlock_t _source_shutdown_rwlock;
Jack Moffitt's avatar
Jack Moffitt committed
107 108 109 110 111

static void *_handle_connection(void *arg);

void connection_initialize(void)
{
112 113 114
    if (_initialized) return;
    
    thread_mutex_create(&_connection_mutex);
115 116
    thread_mutex_create(&_con_queue_mutex);
    thread_mutex_create(&_req_queue_mutex);
117
    thread_mutex_create(&move_clients_mutex);
118
    thread_rwlock_create(&_source_shutdown_rwlock);
119
    thread_cond_create(&global.shutdown_cond);
120 121 122 123
    _req_queue = NULL;
    _req_queue_tail = &_req_queue;
    _con_queue = NULL;
    _con_queue_tail = &_con_queue;
Jack Moffitt's avatar
Jack Moffitt committed
124

125
    _initialized = 1;
Jack Moffitt's avatar
Jack Moffitt committed
126 127 128 129
}

void connection_shutdown(void)
{
130 131
    if (!_initialized) return;
    
132
    thread_cond_destroy(&global.shutdown_cond);
133
    thread_rwlock_destroy(&_source_shutdown_rwlock);
134 135
    thread_mutex_destroy(&_con_queue_mutex);
    thread_mutex_destroy(&_req_queue_mutex);
136
    thread_mutex_destroy(&_connection_mutex);
137
    thread_mutex_destroy(&move_clients_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
138

139
    _initialized = 0;
Jack Moffitt's avatar
Jack Moffitt committed
140 141 142 143
}

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

146 147 148
    thread_mutex_lock(&_connection_mutex);
    id = _current_id++;
    thread_mutex_unlock(&_connection_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
149

150
    return id;
Jack Moffitt's avatar
Jack Moffitt committed
151 152
}

153 154
connection_t *connection_create (sock_t sock, sock_t serversock, char *ip)
{
155
    connection_t *con;
156 157 158 159 160 161 162 163 164
    con = (connection_t *)calloc(1, sizeof(connection_t));
    if (con)
    {
        con->sock = sock;
        con->serversock = serversock;
        con->con_time = time(NULL);
        con->id = _next_connection_id();
        con->ip = ip;
    }
Michael Smith's avatar
Michael Smith committed
165

166
    return con;
167 168
}

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

259 260 261
    serversock = wait_for_serversock(100);
    if(serversock < 0)
        return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
262

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

266
    sock = sock_accept(serversock, ip, MAX_ADDR_LEN);
267 268
    if (sock >= 0)
    {
269 270 271
        /* Make any IPv4 mapped IPv6 address look like a normal IPv4 address */
        if (strncmp (ip, "::ffff:", 7) == 0)
            memmove (ip, ip+7, strlen (ip+7)+1);
Jack Moffitt's avatar
Jack Moffitt committed
272

273 274 275 276 277 278 279 280 281 282 283 284
        con = connection_create (sock, serversock, ip);
        if (con)
            return con;
        sock_close (sock);
    }
    else
    {
        if (!sock_recoverable(sock_error()))
        {
            WARN2("accept() failed with error %d: %s", sock_error(), strerror(sock_error()));
            thread_sleep (500000);
        }
285 286 287
    }
    free(ip);
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
288 289 290
}


291 292 293 294 295
/* add client to connection queue. At this point some header information
 * has been collected, so we now pass it onto the connection thread for
 * further processing
 */
static void _add_connection (client_queue_t *node)
Jack Moffitt's avatar
Jack Moffitt committed
296
{
297 298 299 300
    thread_mutex_lock (&_con_queue_mutex);
    *_con_queue_tail = node;
    _con_queue_tail = (volatile client_queue_t **)&node->next;
    thread_mutex_unlock (&_con_queue_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
301 302 303
}


304 305 306 307 308 309
/* this returns queued clients for the connection thread. headers are
 * already provided, but need to be parsed.
 */
static client_queue_t *_get_connection(void)
{
    client_queue_t *node = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
310

311 312 313 314 315 316 317 318 319
    /* common case, no new connections so don't bother taking locks */
    if (_con_queue)
    {
        thread_mutex_lock (&_con_queue_mutex);
        node = (client_queue_t *)_con_queue;
        _con_queue = node->next;
        if (_con_queue == NULL)
            _con_queue_tail = &_con_queue;
        thread_mutex_unlock (&_con_queue_mutex);
320
        node->next = NULL;
321
    }
322 323
    return node;
}
Jack Moffitt's avatar
Jack Moffitt committed
324 325


326
/* run along queue checking for any data that has come in or a timeout */
327
static void process_request_queue (void)
328 329 330 331 332
{
    client_queue_t **node_ref = (client_queue_t **)&_req_queue;
    ice_config_t *config = config_get_config ();
    int timeout = config->header_timeout;
    config_release_config();
Jack Moffitt's avatar
Jack Moffitt committed
333

334 335 336 337 338 339
    while (*node_ref)
    {
        client_queue_t *node = *node_ref;
        client_t *client = node->client;
        int len = PER_CLIENT_REFBUF_SIZE - 1 - node->offset;
        char *buf = client->refbuf->data + node->offset;
Jack Moffitt's avatar
Jack Moffitt committed
340

341 342 343 344 345 346 347
        if (len > 0)
        {
            if (client->con->con_time + timeout <= time(NULL))
                len = 0;
            else
                len = client_read_bytes (client, buf, len);
        }
Jack Moffitt's avatar
Jack Moffitt committed
348

349 350 351 352 353
        if (len > 0)
        {
            int pass_it = 1;
            char *ptr;

354 355
            /* handle \n, \r\n and nsvcap which for some strange reason has
             * EOL as \r\r\n */
356 357 358 359 360 361 362
            node->offset += len;
            client->refbuf->data [node->offset] = '\000';
            do
            {
                if (node->shoutcast == 1)
                {
                    /* password line */
363 364
                    if (strstr (client->refbuf->data, "\r\r\n") != NULL)
                        break;
365 366 367 368 369 370 371
                    if (strstr (client->refbuf->data, "\r\n") != NULL)
                        break;
                    if (strstr (client->refbuf->data, "\n") != NULL)
                        break;
                }
                /* stream_offset refers to the start of any data sent after the
                 * http style headers, we don't want to lose those */
372 373 374 375 376 377
                ptr = strstr (client->refbuf->data, "\r\r\n\r\r\n");
                if (ptr)
                {
                    node->stream_offset = (ptr+6) - client->refbuf->data;
                    break;
                }
378 379 380 381 382 383 384 385 386 387 388 389 390 391
                ptr = strstr (client->refbuf->data, "\r\n\r\n");
                if (ptr)
                {
                    node->stream_offset = (ptr+4) - client->refbuf->data;
                    break;
                }
                ptr = strstr (client->refbuf->data, "\n\n");
                if (ptr)
                {
                    node->stream_offset = (ptr+2) - client->refbuf->data;
                    break;
                }
                pass_it = 0;
            } while (0);
Jack Moffitt's avatar
Jack Moffitt committed
392

393 394
            if (pass_it)
            {
395
                thread_mutex_lock (&_req_queue_mutex);
396 397 398 399
                if ((client_queue_t **)_req_queue_tail == &(node->next))
                    _req_queue_tail = (volatile client_queue_t **)node_ref;
                *node_ref = node->next;
                node->next = NULL;
400
                thread_mutex_unlock (&_req_queue_mutex);
401
                _add_connection (node);
402
                continue;
403 404 405 406 407 408
            }
        }
        else
        {
            if (len == 0 || client->con->error)
            {
409
                thread_mutex_lock (&_req_queue_mutex);
410 411 412
                if ((client_queue_t **)_req_queue_tail == &node->next)
                    _req_queue_tail = (volatile client_queue_t **)node_ref;
                *node_ref = node->next;
413
                thread_mutex_unlock (&_req_queue_mutex);
414 415 416 417 418 419
                client_destroy (client);
                free (node);
                continue;
            }
        }
        node_ref = &node->next;
420
    }
Jack Moffitt's avatar
Jack Moffitt committed
421 422
}

423

424 425 426 427 428 429 430 431 432
/* add node to the queue of requests. This is where the clients are when
 * initial http details are read.
 */
static void _add_request_queue (client_queue_t *node)
{
    thread_mutex_lock (&_req_queue_mutex);
    *_req_queue_tail = node;
    _req_queue_tail = (volatile client_queue_t **)&node->next;
    thread_mutex_unlock (&_req_queue_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
433 434
}

435

Jack Moffitt's avatar
Jack Moffitt committed
436 437
void connection_accept_loop(void)
{
438
    connection_t *con;
Jack Moffitt's avatar
Jack Moffitt committed
439

440
    tid = thread_create ("connection thread", _handle_connection, NULL, THREAD_ATTACHED);
Jack Moffitt's avatar
Jack Moffitt committed
441

442 443
    while (global.running == ICE_RUNNING)
    {
444 445 446
        con = _accept_connection();

        if (con)
447
        {
448 449 450 451
            client_queue_t *node;
            ice_config_t *config;
            int i;
            client_t *client = NULL;
452

453 454 455 456
            global_lock();
            if (client_create (&client, con, NULL) < 0)
            {
                global_unlock();
457
                client_send_403 (client, "Icecast connection limit reached");
458 459 460
                continue;
            }
            global_unlock();
461

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
            /* setup client for reading incoming http */
            client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] = '\000';

            node = calloc (1, sizeof (client_queue_t));
            if (node == NULL)
            {
                client_destroy (client);
                continue;
            }
            node->client = client;

            /* Check for special shoutcast compatability processing */
            config = config_get_config();
            for (i = 0; i < global.server_sockets; i++)
            {
                if (global.serversock[i] == con->serversock)
                {
                    if (config->listeners[i].shoutcast_compat)
                        node->shoutcast = 1;
                }
            }
            config_release_config(); 

            sock_set_blocking (client->con->sock, SOCK_NONBLOCK);
            sock_set_nodelay (client->con->sock);
Jack Moffitt's avatar
Jack Moffitt committed
487

488 489
            _add_request_queue (node);
            stats_event_inc (NULL, "connections");
490
        }
491
        process_request_queue ();
492
    }
Jack Moffitt's avatar
Jack Moffitt committed
493

494 495 496
    /* Give all the other threads notification to shut down */
    thread_cond_broadcast(&global.shutdown_cond);

497 498
    if (tid)
        thread_join (tid);
Jack Moffitt's avatar
Jack Moffitt committed
499

500 501 502
    /* 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
503 504
}

505 506 507

/* Called when activating a source. Verifies that the source count is not
 * exceeded and applies any initial parameters.
508
 */
509
int connection_complete_source (source_t *source, int response)
510 511 512 513 514 515 516 517 518
{
    ice_config_t *config = config_get_config();

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

    if (global.sources < config->source_limit)
    {
        char *contenttype;
519
        mount_proxy *mountinfo;
520 521 522 523 524 525 526 527 528 529 530 531
        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();
532 533
                if (response)
                {
534
                    client_send_403 (source->client, "Content-type not supported");
535 536
                    source->client = NULL;
                }
537 538 539 540 541 542
                WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
                return -1;
            }
        }
        else
        {
543
            WARN0("No content-type header, falling back to backwards compatibility mode "
544
                    "for icecast 1.x relays. Assuming content is mp3.");
545
            format_type = FORMAT_TYPE_GENERIC;
546 547
        }

Karl Heyes's avatar
Karl Heyes committed
548
        if (format_get_plugin (format_type, source) < 0)
549 550 551
        {
            global_unlock();
            config_release_config();
552 553
            if (response)
            {
554
                client_send_403 (source->client, "internal format allocation problem");
555 556
                source->client = NULL;
            }
557 558 559 560
            WARN1 ("plugin format failed for \"%s\"", source->mount);
            return -1;
        }

561 562 563
        global.sources++;
        stats_event_args (NULL, "sources", "%d", global.sources);
        global_unlock();
564

565 566 567 568 569
        source->running = 1;
        mountinfo = config_find_mount (config, source->mount);
        if (mountinfo == NULL)
            source_update_settings (config, source, mountinfo);
        source_recheck_mounts ();
570 571 572 573 574 575 576
        config_release_config();

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

        return 0;
    }
577
    WARN1("Request to add source when maximum source limit "
578 579 580 581 582
            "reached %d", global.sources);

    global_unlock();
    config_release_config();

583 584
    if (response)
    {
585
        client_send_403 (source->client, "too many sources connected");
586 587
        source->client = NULL;
    }
588 589 590 591 592

    return -1;
}


593 594
static int _check_pass_http(http_parser_t *parser, 
        char *correctuser, char *correctpass)
595 596 597 598 599 600 601 602 603 604 605 606 607
{
    /* 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);
608 609 610
    if(userpass == NULL) {
        WARN1("Base64 decode of Authorization header \"%s\" failed",
                header+6);
611
        return 0;
612
    }
613 614 615 616 617 618 619 620 621 622

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

623
    if(strcmp(username, correctuser) || strcmp(password, correctpass)) {
624 625 626
        free(userpass);
        return 0;
    }
627
    free(userpass);
628 629 630 631

    return 1;
}

632 633 634 635 636 637 638 639
static int _check_pass_icy(http_parser_t *parser, char *correctpass)
{
    char *password;

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

640
    if (strcmp(password, correctpass))
641 642 643 644 645
        return 0;
    else
        return 1;
}

646
static int _check_pass_ice(http_parser_t *parser, char *correctpass)
647
{
648
    char *password;
649 650 651 652 653

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

654
    if (strcmp(password, correctpass))
655 656 657 658 659
        return 0;
    else
        return 1;
}

660
int connection_check_admin_pass(http_parser_t *parser)
661
{
662
    int ret;
Michael Smith's avatar
Michael Smith committed
663 664 665
    ice_config_t *config = config_get_config();
    char *pass = config->admin_password;
    char *user = config->admin_username;
666
    char *protocol;
667 668 669 670 671 672

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

673 674 675 676 677
    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
678
    config_release_config();
679 680
    return ret;
}
Michael Smith's avatar
Michael Smith committed
681

682 683 684 685 686
int connection_check_relay_pass(http_parser_t *parser)
{
    int ret;
    ice_config_t *config = config_get_config();
    char *pass = config->relay_password;
687
    char *user = config->relay_username;
Michael Smith's avatar
Michael Smith committed
688

689 690
    if(!pass || !user) {
        config_release_config();
691
        return 0;
692
    }
693

694 695 696
    ret = _check_pass_http(parser, user, pass);
    config_release_config();
    return ret;
697 698
}

699
int connection_check_source_pass(http_parser_t *parser, const char *mount)
700
{
Michael Smith's avatar
Michael Smith committed
701 702
    ice_config_t *config = config_get_config();
    char *pass = config->source_password;
703
    char *user = "source";
704
    int ret;
Michael Smith's avatar
Michael Smith committed
705
    int ice_login = config->ice_login;
706
    char *protocol;
Michael Smith's avatar
Michael Smith committed
707

708
    mount_proxy *mountinfo = config_find_mount (config, mount);
709

710 711 712 713 714 715
    if (mountinfo)
    {
        if (mountinfo->password)
            pass = mountinfo->password;
        if (mountinfo->username)
            user = mountinfo->username;
716 717 718 719
    }

    if(!pass) {
        WARN0("No source password set, rejecting source");
720
        config_release_config();
721 722 723
        return 0;
    }

724
    protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
725 726 727 728 729 730 731 732 733 734 735
    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");
        }
736
    }
737
    config_release_config();
738
    return ret;
739 740
}

741

742
static void _handle_source_request (client_t *client, char *uri, int auth_style)
743
{
744
    source_t *source;
745

746
    INFO1("Source logging in at mountpoint \"%s\"", uri);
747

748 749 750 751 752 753
    if (uri[0] != '/')
    {
        WARN0 ("source mountpoint not starting with /");
        client_send_401 (client);
        return;
    }
754
    if (auth_style == ICECAST_SOURCE_AUTH) {
755 756
        if (connection_check_source_pass (client->parser, uri) == 0)
        {
757 758 759 760
            /* 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 */
761
            INFO1("Source (%s) attempted to login with invalid or missing password", uri);
762 763 764
            client_send_401(client);
            return;
        }
765
    }
766 767 768
    source = source_reserve (uri);
    if (source)
    {
769 770 771
        if (auth_style == SHOUTCAST_SOURCE_AUTH) {
            source->shoutcast_compat = 1;
        }
772
        source->client = client;
773 774
        source->parser = client->parser;
        source->con = client->con;
775
        if (connection_complete_source (source, 1) < 0)
776
        {
777
            source_clear_source (source);
778 779 780
            source_free_source (source);
        }
        else
781
        {
782
            refbuf_t *ok = refbuf_new (PER_CLIENT_REFBUF_SIZE);
783
            client->respcode = 200;
784
            snprintf (ok->data, PER_CLIENT_REFBUF_SIZE,
785
                    "HTTP/1.0 200 OK\r\n\r\n");
786 787 788 789
            ok->len = strlen (ok->data);
            /* we may have unprocessed data read in, so don't overwrite it */
            ok->associated = client->refbuf;
            client->refbuf = ok;
790 791
            fserve_add_client_callback (client, source_client_callback, source);
        }
792 793 794
    }
    else
    {
795
        client_send_403 (client, "Mountpoint in use");
796
        WARN1 ("Mountpoint %s in use", uri);
797
    }
798 799
}

800

801
static void _handle_stats_request (client_t *client, char *uri)
Jack Moffitt's avatar
Jack Moffitt committed
802
{
803
    stats_event_inc(NULL, "stats_connections");
804 805 806 807

    if (connection_check_admin_pass (client->parser) == 0)
    {
        client_send_401 (client);
808 809
        ERROR0("Bad password for stats connection");
        return;
810
    }
811

812
    client->respcode = 200;
813 814 815 816
    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
            "HTTP/1.0 200 OK\r\n\r\n");
    client->refbuf->len = strlen (client->refbuf->data);
    fserve_add_client_callback (client, stats_callback, NULL);
817 818
}

819
static void _handle_get_request (client_t *client, char *passed_uri)
820
{
Michael Smith's avatar
Michael Smith committed
821 822
    int fileserve;
    int port;
823
    int i;
Karl Heyes's avatar
Karl Heyes committed
824 825
    char *serverhost = NULL;
    int serverport = 0;
826
    aliases *alias;
Michael Smith's avatar
Michael Smith committed
827
    ice_config_t *config;
828
    char *uri = passed_uri;
Michael Smith's avatar
Michael Smith committed
829 830 831 832

    config = config_get_config();
    fileserve = config->fileserve;
    port = config->port;
833
    for(i = 0; i < global.server_sockets; i++) {
834
        if(global.serversock[i] == client->con->serversock) {
835 836 837 838 839 840
            serverhost = config->listeners[i].bind_address;
            serverport = config->listeners[i].port;
            break;
        }
    }
    alias = config->aliases;
841

842 843 844
    /* 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
845
    ** and directory server authorizers, which are looking for /GUID-xxxxxxxx 
846
    ** (where xxxxxx is the GUID in question) - this isn't implemented yet.
847 848 849 850
    ** we need to handle the latter two before the former, as the latter two
    ** aren't subject to the limits.
    */
    /* TODO: add GUID-xxxxxx */
851

852 853 854
    /* 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))) {
855 856
            uri = strdup (alias->destination);
            DEBUG2 ("alias has made %s into %s", passed_uri, uri);
857 858 859 860
            break;
        }
        alias = alias->next;
    }
861 862 863
    config_release_config();

    stats_event_inc(NULL, "client_connections");
864

865
    /* Dispatch all admin requests */
866 867
    if ((strcmp(uri, "/admin.cgi") == 0) ||
        (strncmp(uri, "/admin/", 7) == 0)) {
868
        admin_handle_request(client, uri);
869
        if (uri != passed_uri) free (uri);
Michael Smith's avatar
Michael Smith committed
870 871 872
        return;
    }

873 874 875 876
    /* Here we are parsing the URI request to see
    ** if the extension is .xsl, if so, then process
    ** this request as an XSLT request
    */
877 878
    if (util_check_valid_extension (uri) == XSLT_CONTENT)
    {
879
        /* If the file exists, then transform it, otherwise, write a 404 */
880 881
        DEBUG0("Stats request, sending XSL transformed stats");
        stats_transform_xslt (client, uri);
882
        if (uri != passed_uri) free (uri);
883
        return;
884
    }
885

886
    add_client (uri, client);
887
    if (uri != passed_uri) free (uri);
888 889
}

890 891
static void _handle_shoutcast_compatible (client_queue_t *node)
{
892 893 894
    char *http_compliant;
    int http_compliant_len = 0;
    http_parser_t *parser;
895 896 897
    ice_config_t *config = config_get_config ();
    char *shoutcast_mount;
    client_t *client = node->client;
898

899 900
    if (node->shoutcast == 1)
    {
901
        char *source_password, *ptr, *headers;
902 903 904 905 906 907 908 909 910
        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);
        config_release_config();

        /* Get rid of trailing \r\n or \n after password */
911
        ptr = strstr (client->refbuf->data, "\r\r\n");
912
        if (ptr)
913
            headers = ptr+3;
914 915
        else
        {
916
            ptr = strstr (client->refbuf->data, "\r\n");
917
            if (ptr)
918 919 920 921 922 923 924
                headers = ptr+2;
            else
            {
                ptr = strstr (client->refbuf->data, "\n");
                if (ptr)
                    headers = ptr+1;
            }
925
        }
926 927 928 929 930 931 932 933 934 935 936 937 938 939 940

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

        if (strcmp (client->refbuf->data, source_password) == 0)
        {
            client->respcode = 200;
            /* send this non-blocking but if there is only a partial write
             * then leave to header timeout */
941
            sock_write (client->con->sock, "OK2\r\nicy-caps:11\r\n\r\n");
942 943
            node->offset -= (headers - client->refbuf->data);
            memmove (client->refbuf->data, headers, node->offset+1);
944 945 946 947 948 949
            node->shoutcast = 2;
            /* we've checked the password, now send it back for reading headers */
            _add_request_queue (node);
            free (source_password);
            return;
        }
950 951
        else
            INFO1 ("password does not match \"%s\"", client->refbuf->data);
952 953
        client_destroy (client);
        free (node);
954 955
        return;
    }
956 957
    shoutcast_mount = strdup (config->shoutcast_mount);
    config_release_config();
958 959 960
    /* 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 */
961
    http_compliant_len = 20 + strlen (shoutcast_mount) + node->offset;
962
    http_compliant = (char *)calloc(1, http_compliant_len);
963
    snprintf (http_compliant, http_compliant_len,
964
            "SOURCE %s HTTP/1.0\r\n%s", shoutcast_mount, client->refbuf->data);
965 966
    parser = httpp_create_parser();
    httpp_initialize(parser, NULL);
967 968
    if (httpp_parse (parser, http_compliant, strlen(http_compliant)))
    {
969 970 971 972
        /* we may have more than just headers, so prepare for it */
        if (node->stream_offset == node->offset)
            client->refbuf->len = 0;
        else
973
        {
974 975 976
            char *ptr = client->refbuf->data;
            client->refbuf->len = node->offset - node->stream_offset;
            memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
977
        }
978 979
        client->parser = parser;
        _handle_source_request (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH);
980
    }
981 982
    else {
        httpp_destroy (parser);
983
        client_destroy (client);
984
    }
985
    free (http_compliant);
986 987 988
    free (shoutcast_mount);
    free (node);
    return;
989 990
}

991 992 993 994 995

/* Connection thread. Here we take clients off the connection queue and check
 * the contents provided. We set up the parser then hand off to the specific
 * request handler.
 */
996 997
static void *_handle_connection(void *arg)
{
998
    http_parser_t *parser;
999
    char *rawuri, *uri;
Jack Moffitt's avatar
Jack Moffitt committed
1000

1001
    while (global.running == ICE_RUNNING) {
Jack Moffitt's avatar
Jack Moffitt committed
1002

1003
        client_queue_t *node = _get_connection();
Michael Smith's avatar
Michael Smith committed
1004

1005 1006 1007
        if (node)
        {
            client_t *client = node->client;
Jack Moffitt's avatar
Jack Moffitt committed
1008

1009
            /* Check for special shoutcast compatability processing */
1010 1011 1012
            if (node->shoutcast) 
            {
                _handle_shoutcast_compatible (node);
1013 1014
                continue;
            }
Jack Moffitt's avatar
Jack Moffitt committed
1015

1016
            /* process normal HTTP headers */
1017 1018
            parser = httpp_create_parser();
            httpp_initialize(parser, NULL);
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
            client->parser = parser;
            if (httpp_parse (parser, client->refbuf->data, node->offset))
            {
                /* we may have more than just headers, so prepare for it */
                if (node->stream_offset == node->offset)
                    client->refbuf->len = 0;
                else
                {
                    char *ptr = client->refbuf->data;
                    client->refbuf->len = node->offset - node->stream_offset;
                    memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
                }
                free (node);
1032 1033
                
                if (strcmp("ICE",  httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) &&
1034
                    strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) {
1035
                    ERROR0("Bad HTTP protocol detected");
1036
                    client_destroy (client);
1037 1038
                    continue;
                }
Jack Moffitt's avatar
Jack Moffitt committed
1039

1040 1041 1042
                rawuri = httpp_getvar(parser, HTTPP_VAR_URI);
                uri = util_normalise_uri(rawuri);

1043 1044
                if (uri == NULL)
                {
1045
                    client_destroy (client);
1046 1047
                    continue;
                }
1048

1049
                if (parser->req_type == httpp_req_source) {
1050
                    _handle_source_request (client, uri, ICECAST_SOURCE_AUTH);
1051 1052
                }
                else if (parser->req_type == httpp_req_stats) {
1053
                    _handle_stats_request (client, uri);
1054 1055
                }
                else if (parser->req_type == httpp_req_get) {
1056
                    _handle_get_request (client, uri);
1057 1058
                }
                else {
1059
                    ERROR0("Wrong request type from client");
1060
                    client_send_400 (client, "unknown request");
1061 1062 1063
                }

                free(uri);
1064
            } 
1065 1066 1067
            else
            {
                free (node);
1068
                ERROR0("HTTP request parsing failed");
1069
                client_destroy (client);
1070
            }
1071
            continue;
1072
        }
1073
        thread_sleep (50000);
1074
    }
1075
    DEBUG0 ("Connection thread done");
Jack Moffitt's avatar
Jack Moffitt committed
1076

1077
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
1078 1079 1080 1081
}

void connection_close(connection_t *con)
{
1082 1083 1084 1085
    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
1086
}