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

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

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

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

#include "os.h"

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

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

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

#define CATMODULE "connection"

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

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

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

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

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

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

static thread_queue_t *_conhands = NULL;

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

static void *_handle_connection(void *arg);

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

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

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

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

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

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

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

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

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

156
    return con;
157
158
}

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
static int wait_for_serversock(int timeout)
{
#ifdef HAVE_POLL
    struct pollfd ufds[MAX_LISTEN_SOCKETS];
    int i, ret;

    for(i=0; i < global.server_sockets; i++) {
        ufds[i].fd = global.serversock[i];
        ufds[i].events = POLLIN;
        ufds[i].revents = 0;
    }

    ret = poll(ufds, global.server_sockets, timeout);
    if(ret < 0) {
        return -2;
    }
    else if(ret == 0) {
        return -1;
    }
    else {
179
        int dst;
180
        for(i=0; i < global.server_sockets; i++) {
181
            if(ufds[i].revents & POLLIN)
182
                return ufds[i].fd;
183
184
185
186
187
188
189
190
191
            if(ufds[i].revents & (POLLHUP|POLLERR|POLLNVAL))
            {
                if (ufds[i].revents & (POLLHUP|POLLERR))
                {
                    close (global.serversock[i]);
                    WARN0("Had to close a listening socket");
                }
                global.serversock[i] = -1;
            }
192
        }
193
194
195
196
197
198
199
200
201
202
203
        /* remove any closed sockets */
        for(i=0, dst=0; i < global.server_sockets; i++)
        {
            if (global.serversock[i] == -1)
                continue;
            if (i!=dst)
                global.serversock[dst] = global.serversock[i];
            dst++;
        }
        global.server_sockets = dst;
        return -1;
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
    }
#else
    fd_set rfds;
    struct timeval tv, *p=NULL;
    int i, ret;
    int max = -1;

    FD_ZERO(&rfds);

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

    i = 0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    _add_connection(con);
}

436
437
438

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

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

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

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

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

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

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

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

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

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

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

    global_unlock();
    config_release_config();

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

    return -1;
}


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

    if(header == NULL)
        return 0;

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

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

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

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

    return 1;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

669
    protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
670
671
672
673
674
675
676
677
678
679
680
    if(protocol != NULL && !strcmp(protocol, "ICY")) {
        ret = _check_pass_icy(parser, pass);
    }
    else {
        ret = _check_pass_http(parser, user, pass);
        if(!ret && ice_login)
        {
            ret = _check_pass_ice(parser, pass);
            if(ret)
                WARN0("Source is using deprecated icecast login");
        }
681
    }
682
    config_release_config();
683
    return ret;
684
685
}

686

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

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

693
694
695
696
697
698
    if (uri[0] != '/')
    {
        WARN0 ("source mountpoint not starting with /");
        client_send_401 (client);
        return;
    }
699
    if (auth_style == ICECAST_SOURCE_AUTH) {
700
701
        if (connection_check_source_pass (client->parser, uri) == 0)
        {
702
703
704
705
            /* We commonly get this if the source client is using the wrong
             * protocol: attempt to diagnose this and return an error
             */
            /* TODO: Do what the above comment says */
706
            INFO1("Source (%s) attempted to login with invalid or missing password", uri);
707
708
709
            client_send_401(client);
            return;
        }
710
    }
711
712
713
    source = source_reserve (uri);
    if (source)
    {
714
715
716
        if (auth_style == SHOUTCAST_SOURCE_AUTH) {
            source->shoutcast_compat = 1;
        }
717
        source->client = client;
718
719
        source->parser = client->parser;
        source->con = client->con;
720
721
722
723
724
725
726
727
728
729
730
731
        if (connection_complete_source (source) < 0)
        {
            source->client = NULL;
            source_free_source (source);
        }
        else
            thread_create ("Source Thread", source_client_thread,
                    source, THREAD_DETACHED);
    }
    else
    {
        client_send_404 (client, "Mountpoint in use");
732
        WARN1 ("Mountpoint %s in use", uri);
733
    }
734
735
}

736

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

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

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

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

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

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

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

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

    stats_event_inc(NULL, "client_connections");
804

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

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

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

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

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

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

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

    memset(header, 0, sizeof (header));
    /* Step three of shoutcast auth protocol, read HTTP-style
       request headers and process them.*/
    if (util_read_header(con->sock, header, sizeof (header), 
                         READ_ENTIRE_HEADER) == 0) {
        /* either we didn't get a complete header, or we timed out */
        connection_close(con);
        return;
    }
    /* Here we create a valid HTTP request based of the information
       that was passed in via the non-HTTP style protocol above. This
       means we can use some of our existing code to handle this case */
879
    http_compliant_len = strlen(header) + strlen(mount) + 20;
880
    http_compliant = (char *)calloc(1, http_compliant_len);
881
882
    snprintf (http_compliant, http_compliant_len,
            "SOURCE %s HTTP/1.0\r\n%s", mount, header);
883
884
    parser = httpp_create_parser();
    httpp_initialize(parser, NULL);
885
886
887
888
889
890
891
892
893
    if (httpp_parse (parser, http_compliant, strlen(http_compliant)))
    {
        client_t *client = client_create (con, parser);
        if (client)
        {
            _handle_source_request (client, mount, SHOUTCAST_SOURCE_AUTH);
            free (http_compliant);
            return;
        }
894
    }
895
896
897
    connection_close (con);
    httpp_destroy (parser);
    free (http_compliant);
898
899
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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