connection.c 49 KB
Newer Older
1
2
3
4
5
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
6
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7
8
9
10
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
Philipp Schafft's avatar
Philipp Schafft committed
11
12
 * Copyright 2011,      Dave 'justdave' Miller <justdave@mozilla.com>,
 * Copyright 2011-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
13
14
 */

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

Jack Moffitt's avatar
Jack Moffitt committed
20
21
#include <stdio.h>
#include <stdlib.h>
22
#include <errno.h>
Jack Moffitt's avatar
Jack Moffitt committed
23
#include <string.h>
24
25
26
#ifdef HAVE_POLL
#include <sys/poll.h>
#endif
27
28
#include <sys/types.h>
#include <sys/stat.h>
29
30

#ifndef _WIN32
Jack Moffitt's avatar
Jack Moffitt committed
31
32
#include <sys/socket.h>
#include <netinet/in.h>
33
#else
34
#include <winsock2.h>
35
#endif
Jack Moffitt's avatar
Jack Moffitt committed
36

37
#include "compat.h"
Jack Moffitt's avatar
Jack Moffitt committed
38

Marvin Scholz's avatar
Marvin Scholz committed
39
40
41
42
#include "common/thread/thread.h"
#include "common/avl/avl.h"
#include "common/net/sock.h"
#include "common/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"
60
#include "admin.h"
Michael Smith's avatar
Michael Smith committed
61
#include "auth.h"
Jack Moffitt's avatar
Jack Moffitt committed
62
63
64

#define CATMODULE "connection"

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/* 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.
*/

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

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

94
95
96
97
98
99
100
101
typedef struct
{
    char *filename;
    time_t file_recheck;
    time_t file_mtime;
    avl_tree *contents;
} cache_file_contents;

102
static spin_t _connection_lock; // protects _current_id, _con_queue, _con_queue_tail
103
static volatile unsigned long _current_id = 0;
Jack Moffitt's avatar
Jack Moffitt committed
104
105
static int _initialized = 0;

106
107
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;
108
109
110
111
112
static int ssl_ok;
#ifdef HAVE_OPENSSL
static SSL_CTX *ssl_ctx;
#endif

113
/* filtering client connection based on IP */
114
static cache_file_contents banned_ip, allowed_ip;
115

116
rwlock_t _source_shutdown_rwlock;
Jack Moffitt's avatar
Jack Moffitt committed
117

118
static void _handle_connection(void);
Jack Moffitt's avatar
Jack Moffitt committed
119

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
static int compare_ip (void *arg, void *a, void *b)
{
    const char *ip = (const char *)a;
    const char *pattern = (const char *)b;

    return strcmp (pattern, ip);
}


static int free_filtered_ip (void*x)
{
    free (x);
    return 1;
}


Jack Moffitt's avatar
Jack Moffitt committed
136
137
void connection_initialize(void)
{
Marvin Scholz's avatar
Marvin Scholz committed
138
139
    if (_initialized)
        return;
140

141
    thread_spin_create (&_connection_lock);
142
    thread_mutex_create(&move_clients_mutex);
143
    thread_rwlock_create(&_source_shutdown_rwlock);
144
    thread_cond_create(&global.shutdown_cond);
145
146
147
148
    _req_queue = NULL;
    _req_queue_tail = &_req_queue;
    _con_queue = NULL;
    _con_queue_tail = &_con_queue;
Jack Moffitt's avatar
Jack Moffitt committed
149

150
151
152
153
154
155
    banned_ip.contents = NULL;
    banned_ip.file_mtime = 0;

    allowed_ip.contents = NULL;
    allowed_ip.file_mtime = 0;

156
    _initialized = 1;
Jack Moffitt's avatar
Jack Moffitt committed
157
158
159
160
}

void connection_shutdown(void)
{
Marvin Scholz's avatar
Marvin Scholz committed
161
162
    if (!_initialized)
        return;
163

164
165
166
#ifdef HAVE_OPENSSL
    SSL_CTX_free (ssl_ctx);
#endif
167
168
    if (banned_ip.contents)  avl_tree_free (banned_ip.contents, free_filtered_ip);
    if (allowed_ip.contents) avl_tree_free (allowed_ip.contents, free_filtered_ip);
169

170
    thread_cond_destroy(&global.shutdown_cond);
171
    thread_rwlock_destroy(&_source_shutdown_rwlock);
172
    thread_spin_destroy (&_connection_lock);
173
    thread_mutex_destroy(&move_clients_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
174

175
    _initialized = 0;
Jack Moffitt's avatar
Jack Moffitt committed
176
177
178
179
}

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

182
    thread_spin_lock(&_connection_lock);
183
    id = _current_id++;
184
    thread_spin_unlock(&_connection_lock);
Jack Moffitt's avatar
Jack Moffitt committed
185

186
    return id;
Jack Moffitt's avatar
Jack Moffitt committed
187
188
}

189
190

#ifdef HAVE_OPENSSL
Marvin Scholz's avatar
Marvin Scholz committed
191
static void get_ssl_certificate(ice_config_t *config)
192
{
193
    SSL_METHOD *method;
194
    long ssl_opts;
195
196
    ssl_ok = 0;

197
198
    SSL_load_error_strings(); /* readable error messages */
    SSL_library_init(); /* initialize library */
199
200

    method = SSLv23_server_method();
Marvin Scholz's avatar
Marvin Scholz committed
201
202
    ssl_ctx = SSL_CTX_new(method);
    ssl_opts = SSL_CTX_get_options(ssl_ctx);
203
#ifdef SSL_OP_NO_COMPRESSION
Marvin Scholz's avatar
Marvin Scholz committed
204
    SSL_CTX_set_options(ssl_ctx, ssl_opts|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION);
205
#else
Marvin Scholz's avatar
Marvin Scholz committed
206
    SSL_CTX_set_options(ssl_ctx, ssl_opts|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);
207
#endif
208

Marvin Scholz's avatar
Marvin Scholz committed
209
    do {
210
211
        if (config->cert_file == NULL)
            break;
Marvin Scholz's avatar
Marvin Scholz committed
212
        if (SSL_CTX_use_certificate_chain_file (ssl_ctx, config->cert_file) <= 0) {
213
            ICECAST_LOG_WARN("Invalid cert file %s", config->cert_file);
214
215
            break;
        }
Marvin Scholz's avatar
Marvin Scholz committed
216
        if (SSL_CTX_use_PrivateKey_file (ssl_ctx, config->cert_file, SSL_FILETYPE_PEM) <= 0) {
217
            ICECAST_LOG_WARN("Invalid private key file %s", config->cert_file);
218
219
            break;
        }
Marvin Scholz's avatar
Marvin Scholz committed
220
        if (!SSL_CTX_check_private_key (ssl_ctx)) {
221
            ICECAST_LOG_ERROR("Invalid %s - Private key does not match cert public key", config->cert_file);
222
223
            break;
        }
Marvin Scholz's avatar
Marvin Scholz committed
224
        if (SSL_CTX_set_cipher_list(ssl_ctx, config->cipher_list) <= 0) {
225
226
            ICECAST_LOG_WARN("Invalid cipher list: %s", config->cipher_list);
        }
227
        ssl_ok = 1;
228
        ICECAST_LOG_INFO("SSL certificate found at %s", config->cert_file);
229
        ICECAST_LOG_INFO("SSL using ciphers %s", config->cipher_list);
230
        return;
231
    } while (0);
232
    ICECAST_LOG_INFO("No SSL capability on any configured ports");
233
234
235
236
237
238
}


/* handlers for reading and writing a connection_t when there is ssl
 * configured on the listening port
 */
Marvin Scholz's avatar
Marvin Scholz committed
239
static int connection_read_ssl(connection_t *con, void *buf, size_t len)
240
{
Marvin Scholz's avatar
Marvin Scholz committed
241
    int bytes = SSL_read(con->ssl, buf, len);
242

Marvin Scholz's avatar
Marvin Scholz committed
243
244
    if (bytes < 0) {
        switch (SSL_get_error(con->ssl, bytes)) {
245
246
            case SSL_ERROR_WANT_READ:
            case SSL_ERROR_WANT_WRITE:
247
            return -1;
248
249
250
251
252
253
        }
        con->error = 1;
    }
    return bytes;
}

Marvin Scholz's avatar
Marvin Scholz committed
254
static int connection_send_ssl(connection_t *con, const void *buf, size_t len)
255
256
257
{
    int bytes = SSL_write (con->ssl, buf, len);

Marvin Scholz's avatar
Marvin Scholz committed
258
259
    if (bytes < 0) {
        switch (SSL_get_error(con->ssl, bytes)){
260
261
262
263
264
            case SSL_ERROR_WANT_READ:
            case SSL_ERROR_WANT_WRITE:
                return -1;
        }
        con->error = 1;
Marvin Scholz's avatar
Marvin Scholz committed
265
    } else {
266
        con->sent_bytes += bytes;
Marvin Scholz's avatar
Marvin Scholz committed
267
    }
268
269
270
271
272
    return bytes;
}
#else

/* SSL not compiled in, so at least log it */
273
static void get_ssl_certificate(ice_config_t *config)
274
275
{
    ssl_ok = 0;
276
    ICECAST_LOG_INFO("No SSL capability");
277
278
279
280
281
282
283
}
#endif /* HAVE_OPENSSL */


/* handlers (default) for reading and writing a connection_t, no encrpytion
 * used just straight access to the socket
 */
Marvin Scholz's avatar
Marvin Scholz committed
284
static int connection_read(connection_t *con, void *buf, size_t len)
285
{
Marvin Scholz's avatar
Marvin Scholz committed
286
    int bytes = sock_read_bytes(con->sock, buf, len);
287
288
    if (bytes == 0)
        con->error = 1;
Marvin Scholz's avatar
Marvin Scholz committed
289
    if (bytes == -1 && !sock_recoverable(sock_error()))
290
291
292
293
        con->error = 1;
    return bytes;
}

Marvin Scholz's avatar
Marvin Scholz committed
294
static int connection_send(connection_t *con, const void *buf, size_t len)
295
{
Marvin Scholz's avatar
Marvin Scholz committed
296
297
298
    int bytes = sock_write_bytes(con->sock, buf, len);
    if (bytes < 0) {
        if (!sock_recoverable(sock_error()))
299
            con->error = 1;
Marvin Scholz's avatar
Marvin Scholz committed
300
    } else {
301
        con->sent_bytes += bytes;
Marvin Scholz's avatar
Marvin Scholz committed
302
    }
303
304
305
306
    return bytes;
}


307
308
309
/* function to handle the re-populating of the avl tree containing IP addresses
 * for deciding whether a connection of an incoming request is to be dropped.
 */
310
static void recheck_ip_file(cache_file_contents *cache)
311
312
{
    time_t now = time(NULL);
Marvin Scholz's avatar
Marvin Scholz committed
313
314
315
316
317
318
    if (now >= cache->file_recheck) {
        struct      stat file_stat;
        FILE       *file             = NULL;
        int         count            = 0;
        avl_tree   *new_ips;
        char        line[MAX_LINE_LEN];
319
320

        cache->file_recheck = now + 10;
Marvin Scholz's avatar
Marvin Scholz committed
321
322
        if (cache->filename == NULL) {
            if (cache->contents) {
323
324
325
326
327
                avl_tree_free (cache->contents, free_filtered_ip);
                cache->contents = NULL;
            }
            return;
        }
Marvin Scholz's avatar
Marvin Scholz committed
328
        if (stat(cache->filename, &file_stat) < 0) {
329
            ICECAST_LOG_WARN("failed to check status of \"%s\": %s", cache->filename, strerror(errno));
330
331
332
333
334
335
336
337
            return;
        }
        if (file_stat.st_mtime == cache->file_mtime)
            return; /* common case, no update to file */

        cache->file_mtime = file_stat.st_mtime;

        file = fopen (cache->filename, "r");
Marvin Scholz's avatar
Marvin Scholz committed
338
        if (file == NULL) {
339
            ICECAST_LOG_WARN("Failed to open file \"%s\": %s", cache->filename, strerror (errno));
340
341
342
            return;
        }

Marvin Scholz's avatar
Marvin Scholz committed
343
        new_ips = avl_tree_new(compare_ip, NULL);
344

Marvin Scholz's avatar
Marvin Scholz committed
345
        while (get_line(file, line, MAX_LINE_LEN)) {
346
347
348
349
350
351
            char *str;
            if(!line[0] || line[0] == '#')
                continue;
            count++;
            str = strdup (line);
            if (str)
Marvin Scholz's avatar
Marvin Scholz committed
352
                avl_insert(new_ips, str);
353
354
        }
        fclose (file);
355
        ICECAST_LOG_INFO("%d entries read from file \"%s\"", count, cache->filename);
356

Marvin Scholz's avatar
Marvin Scholz committed
357
358
        if (cache->contents)
            avl_tree_free(cache->contents, free_filtered_ip);
359
360
361
362
363
364
        cache->contents = new_ips;
    }
}


/* return 0 if the passed ip address is not to be handled by icecast, non-zero otherwise */
365
static int accept_ip_address(char *ip)
366
367
368
{
    void *result;

369
370
    recheck_ip_file(&banned_ip);
    recheck_ip_file(&allowed_ip);
371

Marvin Scholz's avatar
Marvin Scholz committed
372
373
    if (banned_ip.contents) {
        if (avl_get_by_key (banned_ip.contents, ip, &result) == 0) {
374
            ICECAST_LOG_DEBUG("%s is banned", ip);
375
376
377
            return 0;
        }
    }
Marvin Scholz's avatar
Marvin Scholz committed
378
379
    if (allowed_ip.contents) {
        if (avl_get_by_key (allowed_ip.contents, ip, &result) == 0) {
380
            ICECAST_LOG_DEBUG("%s is allowed", ip);
381
            return 1;
Marvin Scholz's avatar
Marvin Scholz committed
382
        } else {
383
            ICECAST_LOG_DEBUG("%s is not allowed", ip);
384
385
386
387
388
389
390
            return 0;
        }
    }
    return 1;
}


391
392
connection_t *connection_create (sock_t sock, sock_t serversock, char *ip)
{
393
    connection_t *con;
394
    con = (connection_t *)calloc(1, sizeof(connection_t));
Marvin Scholz's avatar
Marvin Scholz committed
395
396
    if (con) {
        con->sock       = sock;
397
        con->serversock = serversock;
Marvin Scholz's avatar
Marvin Scholz committed
398
399
400
401
402
        con->con_time   = time(NULL);
        con->id         = _next_connection_id();
        con->ip         = ip;
        con->read       = connection_read;
        con->send       = connection_send;
403
    }
Michael Smith's avatar
Michael Smith committed
404

405
    return con;
406
407
}

408
409
/* prepare connection for interacting over a SSL connection
 */
410
void connection_uses_ssl(connection_t *con)
411
412
413
414
{
#ifdef HAVE_OPENSSL
    con->read = connection_read_ssl;
    con->send = connection_send_ssl;
Marvin Scholz's avatar
Marvin Scholz committed
415
416
417
    con->ssl = SSL_new(ssl_ctx);
    SSL_set_accept_state(con->ssl);
    SSL_set_fd(con->ssl, con->sock);
418
419
420
#endif
}

421
static sock_t wait_for_serversock(int timeout)
422
423
{
#ifdef HAVE_POLL
424
    struct pollfd ufds [global.server_sockets];
425
426
427
428
429
430
431
432
433
434
    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) {
435
        return SOCK_ERROR;
Marvin Scholz's avatar
Marvin Scholz committed
436
    } else if(ret == 0) {
437
        return SOCK_ERROR;
Marvin Scholz's avatar
Marvin Scholz committed
438
    } else {
439
        int dst;
440
        for(i=0; i < global.server_sockets; i++) {
441
            if(ufds[i].revents & POLLIN)
442
                return ufds[i].fd;
Marvin Scholz's avatar
Marvin Scholz committed
443
444
            if(ufds[i].revents & (POLLHUP|POLLERR|POLLNVAL)) {
                if (ufds[i].revents & (POLLHUP|POLLERR)) {
445
                    sock_close (global.serversock[i]);
446
                    ICECAST_LOG_WARN("Had to close a listening socket");
447
                }
448
                global.serversock[i] = SOCK_ERROR;
449
            }
450
        }
451
        /* remove any closed sockets */
Marvin Scholz's avatar
Marvin Scholz committed
452
        for(i=0, dst=0; i < global.server_sockets; i++) {
453
            if (global.serversock[i] == SOCK_ERROR)
454
            continue;
455
            if (i!=dst)
456
            global.serversock[dst] = global.serversock[i];
457
458
459
            dst++;
        }
        global.server_sockets = dst;
460
        return SOCK_ERROR;
461
462
463
464
465
    }
#else
    fd_set rfds;
    struct timeval tv, *p=NULL;
    int i, ret;
466
    sock_t max = SOCK_ERROR;
467
468
469
470
471

    FD_ZERO(&rfds);

    for(i=0; i < global.server_sockets; i++) {
        FD_SET(global.serversock[i], &rfds);
472
        if (max == SOCK_ERROR || global.serversock[i] > max)
473
474
475
476
477
            max = global.serversock[i];
    }

    if(timeout >= 0) {
        tv.tv_sec = timeout/1000;
478
        tv.tv_usec = (timeout % 1000) * 1000;
479
480
481
482
483
        p = &tv;
    }

    ret = select(max+1, &rfds, NULL, NULL, p);
    if(ret < 0) {
484
        return SOCK_ERROR;
Marvin Scholz's avatar
Marvin Scholz committed
485
    } else if(ret == 0) {
486
        return SOCK_ERROR;
Marvin Scholz's avatar
Marvin Scholz committed
487
    } else {
488
489
490
491
        for(i=0; i < global.server_sockets; i++) {
            if(FD_ISSET(global.serversock[i], &rfds))
                return global.serversock[i];
        }
492
        return SOCK_ERROR; /* Should be impossible, stop compiler warnings */
493
494
495
496
    }
#endif
}

497
static connection_t *_accept_connection(int duration)
Jack Moffitt's avatar
Jack Moffitt committed
498
{
499
    sock_t sock, serversock;
500
    char *ip;
Jack Moffitt's avatar
Jack Moffitt committed
501

502
    serversock = wait_for_serversock (duration);
503
    if (serversock == SOCK_ERROR)
504
        return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
505

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

509
    sock = sock_accept(serversock, ip, MAX_ADDR_LEN);
Marvin Scholz's avatar
Marvin Scholz committed
510
    if (sock != SOCK_ERROR) {
511
        connection_t *con = NULL;
512
        /* Make any IPv4 mapped IPv6 address look like a normal IPv4 address */
Marvin Scholz's avatar
Marvin Scholz committed
513
514
        if (strncmp(ip, "::ffff:", 7) == 0)
            memmove(ip, ip+7, strlen (ip+7)+1);
Jack Moffitt's avatar
Jack Moffitt committed
515

Marvin Scholz's avatar
Marvin Scholz committed
516
517
        if (accept_ip_address(ip))
            con = connection_create(sock, serversock, ip);
518
519
        if (con)
            return con;
Marvin Scholz's avatar
Marvin Scholz committed
520
521
522
        sock_close(sock);
    } else {
        if (!sock_recoverable(sock_error())) {
523
            ICECAST_LOG_WARN("accept() failed with error %d: %s", sock_error(), strerror(sock_error()));
Marvin Scholz's avatar
Marvin Scholz committed
524
            thread_sleep(500000);
525
        }
526
527
528
    }
    free(ip);
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
529
530
531
}


532
533
534
535
/* 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
 */
536
static void _add_connection(client_queue_t *node)
Jack Moffitt's avatar
Jack Moffitt committed
537
{
538
    thread_spin_lock(&_connection_lock);
539
    *_con_queue_tail = node;
540
541
    _con_queue_tail = (volatile client_queue_t **) &node->next;
    thread_spin_unlock(&_connection_lock);
Jack Moffitt's avatar
Jack Moffitt committed
542
543
544
}


545
546
547
548
549
550
/* 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
551

Marvin Scholz's avatar
Marvin Scholz committed
552
    thread_spin_lock(&_connection_lock);
553

Marvin Scholz's avatar
Marvin Scholz committed
554
    if (_con_queue){
555
556
557
558
        node = (client_queue_t *)_con_queue;
        _con_queue = node->next;
        if (_con_queue == NULL)
            _con_queue_tail = &_con_queue;
559
        node->next = NULL;
560
    }
561

Marvin Scholz's avatar
Marvin Scholz committed
562
    thread_spin_unlock(&_connection_lock);
563
564
    return node;
}
Jack Moffitt's avatar
Jack Moffitt committed
565
566


567
/* run along queue checking for any data that has come in or a timeout */
568
static void process_request_queue (void)
569
570
{
    client_queue_t **node_ref = (client_queue_t **)&_req_queue;
Marvin Scholz's avatar
Marvin Scholz committed
571
    ice_config_t *config = config_get_config();
572
573
    int timeout = config->header_timeout;
    config_release_config();
Jack Moffitt's avatar
Jack Moffitt committed
574

Marvin Scholz's avatar
Marvin Scholz committed
575
    while (*node_ref) {
576
577
578
579
        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
580

Marvin Scholz's avatar
Marvin Scholz committed
581
582
        if (len > 0) {
            if (client->con->con_time + timeout <= time(NULL)) {
583
                len = 0;
Marvin Scholz's avatar
Marvin Scholz committed
584
585
586
            } else {
                len = client_read_bytes(client, buf, len);
            }
587
        }
Jack Moffitt's avatar
Jack Moffitt committed
588

Marvin Scholz's avatar
Marvin Scholz committed
589
        if (len > 0) {
590
591
592
            int pass_it = 1;
            char *ptr;

593
594
            /* handle \n, \r\n and nsvcap which for some strange reason has
             * EOL as \r\r\n */
595
            node->offset += len;
Marvin Scholz's avatar
Marvin Scholz committed
596
597
598
            client->refbuf->data[node->offset] = '\000';
            do {
                if (node->shoutcast == 1) {
599
                    /* password line */
600
601
                    if (strstr (client->refbuf->data, "\r\r\n") != NULL)
                        break;
602
603
604
605
606
607
608
                    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 */
Marvin Scholz's avatar
Marvin Scholz committed
609
610
                ptr = strstr(client->refbuf->data, "\r\r\n\r\r\n");
                if (ptr) {
611
612
613
                    node->stream_offset = (ptr+6) - client->refbuf->data;
                    break;
                }
Marvin Scholz's avatar
Marvin Scholz committed
614
615
                ptr = strstr(client->refbuf->data, "\r\n\r\n");
                if (ptr) {
616
617
618
                    node->stream_offset = (ptr+4) - client->refbuf->data;
                    break;
                }
Marvin Scholz's avatar
Marvin Scholz committed
619
620
                ptr = strstr(client->refbuf->data, "\n\n");
                if (ptr) {
621
622
623
624
625
                    node->stream_offset = (ptr+2) - client->refbuf->data;
                    break;
                }
                pass_it = 0;
            } while (0);
Jack Moffitt's avatar
Jack Moffitt committed
626

Marvin Scholz's avatar
Marvin Scholz committed
627
            if (pass_it) {
628
629
630
631
                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;
Marvin Scholz's avatar
Marvin Scholz committed
632
                _add_connection(node);
633
                continue;
634
            }
Marvin Scholz's avatar
Marvin Scholz committed
635
636
        } else {
            if (len == 0 || client->con->error) {
637
638
639
                if ((client_queue_t **)_req_queue_tail == &node->next)
                    _req_queue_tail = (volatile client_queue_t **)node_ref;
                *node_ref = node->next;
Marvin Scholz's avatar
Marvin Scholz committed
640
641
                client_destroy(client);
                free(node);
642
643
644
645
                continue;
            }
        }
        node_ref = &node->next;
646
    }
647
    _handle_connection();
Jack Moffitt's avatar
Jack Moffitt committed
648
649
}

650

651
652
653
/* add node to the queue of requests. This is where the clients are when
 * initial http details are read.
 */
Marvin Scholz's avatar
Marvin Scholz committed
654
static void _add_request_queue(client_queue_t *node)
655
656
657
{
    *_req_queue_tail = node;
    _req_queue_tail = (volatile client_queue_t **)&node->next;
Jack Moffitt's avatar
Jack Moffitt committed
658
659
}

660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
static client_queue_t *create_client_node(client_t *client)
{
    client_queue_t *node = calloc (1, sizeof (client_queue_t));
    ice_config_t *config;
    listener_t *listener;

    if (!node)
        return NULL;

    node->client = client;

    config = config_get_config();
    listener = config_get_listen_sock(config, client->con);

    if (listener) {
        if (listener->shoutcast_compat)
            node->shoutcast = 1;
        if (listener->ssl && ssl_ok)
            connection_uses_ssl(client->con);
        if (listener->shoutcast_mount)
            node->shoutcast_mount = strdup(listener->shoutcast_mount);
    }

    config_release_config();

    return node;
}
687

Marvin Scholz's avatar
Marvin Scholz committed
688
void connection_accept_loop(void)
Jack Moffitt's avatar
Jack Moffitt committed
689
{
690
    connection_t *con;
691
    ice_config_t *config;
692
    int duration = 300;
693

Marvin Scholz's avatar
Marvin Scholz committed
694
695
696
    config = config_get_config();
    get_ssl_certificate(config);
    config_release_config();
Jack Moffitt's avatar
Jack Moffitt committed
697

Marvin Scholz's avatar
Marvin Scholz committed
698
    while (global.running == ICECAST_RUNNING) {
699
        con = _accept_connection (duration);
700

Marvin Scholz's avatar
Marvin Scholz committed
701
        if (con) {
702
703
            client_queue_t *node;
            client_t *client = NULL;
704

705
            global_lock();
Marvin Scholz's avatar
Marvin Scholz committed
706
            if (client_create (&client, con, NULL) < 0) {
707
                global_unlock();
708
                client_send_error(client, 403, 1, "Icecast connection limit reached");
709
710
                /* don't be too eager as this is an imposed hard limit */
                thread_sleep (400000);
711
712
                continue;
            }
713

714
715
716
            /* setup client for reading incoming http */
            client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] = '\000';

Marvin Scholz's avatar
Marvin Scholz committed
717
            if (sock_set_blocking (client->con->sock, 0) || sock_set_nodelay (client->con->sock)) {
718
                global_unlock();
719
                ICECAST_LOG_WARN("failed to set tcp options on client connection, dropping");
Marvin Scholz's avatar
Marvin Scholz committed
720
                client_destroy(client);
721
722
723
                continue;
            }

724
725
726
            node = create_client_node(client);
            global_unlock();

Marvin Scholz's avatar
Marvin Scholz committed
727
728
            if (node == NULL) {
                client_destroy(client);
729
730
731
                continue;
            }

Marvin Scholz's avatar
Marvin Scholz committed
732
733
            _add_request_queue(node);
            stats_event_inc(NULL, "connections");
734
            duration = 5;
Marvin Scholz's avatar
Marvin Scholz committed
735
        } else {
736
737
            if (_req_queue == NULL)
                duration = 300; /* use longer timeouts when nothing waiting */
738
        }
Marvin Scholz's avatar
Marvin Scholz committed
739
        process_request_queue();
740
    }
Jack Moffitt's avatar
Jack Moffitt committed
741

742
743
744
    /* Give all the other threads notification to shut down */
    thread_cond_broadcast(&global.shutdown_cond);

745
746
747
    /* 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
748
749
}

750
751
752

/* Called when activating a source. Verifies that the source count is not
 * exceeded and applies any initial parameters.
753
 */
Marvin Scholz's avatar
Marvin Scholz committed
754
int connection_complete_source(source_t *source, int response)
755
{
756
    ice_config_t *config;
757

Marvin Scholz's avatar
Marvin Scholz committed
758
    global_lock();
759
    ICECAST_LOG_DEBUG("sources count is %d", global.sources);
760

761
    config = config_get_config();
Marvin Scholz's avatar
Marvin Scholz committed
762
    if (global.sources < config->source_limit) {
763
        const char *contenttype;
764
        const char *expectcontinue;
765
        mount_proxy *mountinfo;
766
767
768
769
        format_type_t format_type;

        /* setup format handler */
        contenttype = httpp_getvar (source->parser, "content-type");
Marvin Scholz's avatar
Marvin Scholz committed
770
771
        if (contenttype != NULL) {
            format_type = format_get_type(contenttype);
772

Marvin Scholz's avatar
Marvin Scholz committed
773
            if (format_type == FORMAT_ERROR) {
774
                config_release_config();
775
                global_unlock();
776
                if (response) {
777
                    client_send_error(source->client, 403, 1, "Content-type not supported");
778
779
                    source->client = NULL;
                }
780
                ICECAST_LOG_WARN("Content-type \"%s\" not supported, dropping source", contenttype);
781
782
                return -1;
            }
783
784
785
786
        } else if (source->parser->req_type == httpp_req_put) {
            config_release_config();
            global_unlock();
            if (response) {
787
                client_send_error(source->client, 403, 1, "No Content-type given");
788
789
790
791
792
793
794
795
                source->client = NULL;
            }
            ICECAST_LOG_ERROR("Content-type not given in PUT request, dropping source");
            return -1;
        } else {
            ICECAST_LOG_ERROR("No content-type header, falling back to backwards compatibility mode "
                    "for icecast 1.x relays. Assuming content is mp3. This behaviour is deprecated "
                    "and the source client will NOT work with future Icecast versions!");
796
            format_type = FORMAT_TYPE_GENERIC;
797
798
        }

Marvin Scholz's avatar
Marvin Scholz committed
799
        if (format_get_plugin (format_type, source) < 0) {
800
801
            global_unlock();
            config_release_config();
Marvin Scholz's avatar
Marvin Scholz committed
802
            if (response) {
803
                client_send_error(source->client, 403, 1, "internal format allocation problem");
804
805
                source->client = NULL;
            }
806
            ICECAST_LOG_WARN("plugin format failed for \"%s\"", source->mount);
807
808
809
            return -1;
        }

810
811
        /* For PUT support we check for 100-continue and send back a 100 to stay in spec */
        expectcontinue = httpp_getvar (source->parser, "expect");
Marvin Scholz's avatar
Marvin Scholz committed
812
        if (expectcontinue != NULL) {
813
#ifdef HAVE_STRCASESTR
814
            if (strcasestr (expectcontinue, "100-continue") != NULL)
815
#else
816
817
            ICECAST_LOG_WARN("OS doesn't support case insenestive substring checks...");
            if (strstr (expectcontinue, "100-continue") != NULL)
818
#endif
819
820
821
822
            {
                client_send_100 (source->client);
            }
        }
823

824
        global.sources++;
Marvin Scholz's avatar
Marvin Scholz committed
825
        stats_event_args(NULL, "sources", "%d", global.sources);
826
        global_unlock();
827

828
        source->running = 1;
Marvin Scholz's avatar
Marvin Scholz committed
829
830
        mountinfo = config_find_mount(config, source->mount, MOUNT_TYPE_NORMAL);
        source_update_settings(config, source, mountinfo);
831
        config_release_config();
832
        slave_rebuild_mounts();
833
834

        source->shutdown_rwlock = &_source_shutdown_rwlock;
835
        ICECAST_LOG_DEBUG("source is ready to start");
836
837
838

        return 0;
    }
839
    ICECAST_LOG_WARN("Request to add source when maximum source limit "
Marvin Scholz's avatar
Marvin Scholz committed
840
        "reached %d", global.sources);
841
842
843
844

    global_unlock();
    config_release_config();

Marvin Scholz's avatar
Marvin Scholz committed
845
    if (response) {
846
        client_send_error(source->client, 403, 1, "too many sources connected");
847
848
        source->client = NULL;
    }
849
850
851
852

    return -1;
}

Marvin Scholz's avatar
Marvin Scholz committed
853
static inline void source_startup(client_t *client, const char *uri)
854
855
{
    source_t *source;
Marvin Scholz's avatar
Marvin Scholz committed
856
    source = source_reserve(uri);
857

Marvin Scholz's avatar
Marvin Scholz committed
858
    if (source) {
859
        source->client = client;
860
861
        source->parser = client->parser;
        source->con = client->con;
Marvin Scholz's avatar
Marvin Scholz committed
862
863
864
        if (connection_complete_source(source, 1) < 0) {
            source_clear_source(source);
            source_free_source(source);
865
866
867
            return;
        }
        client->respcode = 200;
Philipp Schafft's avatar
Philipp Schafft committed
868
869
870
871
872
        if (client->protocol == ICECAST_PROTOCOL_SHOUTCAST) {
            client->respcode = 200;
            /* send this non-blocking but if there is only a partial write
             * then leave to header timeout */
            sock_write (client->con->sock, "OK2\r\nicy-caps:11\r\n\r\n");
873
            source->shoutcast_compat = 1;
Marvin Scholz's avatar
Marvin Scholz committed
874
            source_client_callback(client, source);
Philipp Schafft's avatar
Philipp Schafft committed
875
        } else {
Marvin Scholz's avatar
Marvin Scholz committed
876
            refbuf_t *ok = refbuf_new(PER_CLIENT_REFBUF_SIZE);
877
            client->respcode = 200;
Marvin Scholz's avatar
Marvin Scholz committed
878
            snprintf(ok->data, PER_CLIENT_REFBUF_SIZE,
879
                    "HTTP/1.0 200 OK\r\n\r\n");
Marvin Scholz's avatar
Marvin Scholz committed
880
            ok->len = strlen(ok->data);
881
882
883
            /* we may have unprocessed data read in, so don't overwrite it */
            ok->associated = client->refbuf;
            client->refbuf = ok;
Marvin Scholz's avatar
Marvin Scholz committed
884
            fserve_add_client_callback(client, source_client_callback, source);
885
        }
Marvin Scholz's avatar
Marvin Scholz committed
886
    } else {
887
        client_send_error(client, 403, 1, "Mountpoint in use");
888
        ICECAST_LOG_WARN("Mountpoint %s in use", uri);
889
    }
890
891
}

Philipp Schafft's avatar
Philipp Schafft committed
892
/* only called for native icecast source clients */
Marvin Scholz's avatar
Marvin Scholz committed
893
static void _handle_source_request(client_t *client, const char *uri)
Jack Moffitt's avatar
Jack Moffitt committed
894
{
895
896
    ICECAST_LOG_INFO("Source logging in at mountpoint \"%s\" from %s as role %s",
        uri, client->con->ip, client->role);
897

Marvin Scholz's avatar
Marvin Scholz committed
898
    if (uri[0] != '/') {
Philipp Schafft's avatar
Philipp Schafft committed
899
900
        ICECAST_LOG_WARN("source mountpoint not starting with /");
        client_send_error(client, 400, 1, "source mountpoint not starting with /");
901
        return;
902
    }
903