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
 * Copyright 2011,      Dave 'justdave' Miller <justdave@mozilla.com>,
12
 * Copyright 2011-2018, 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
#include <sys/types.h>
28 29

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

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

Marvin Scholz's avatar
Marvin Scholz committed
38 39 40 41
#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
42

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

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

#define CATMODULE "connection"

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
/* 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.
*/

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

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

98
static spin_t _connection_lock; // protects _current_id, _con_queue, _con_queue_tail
99
static volatile unsigned long _current_id = 0;
Jack Moffitt's avatar
Jack Moffitt committed
100 101
static int _initialized = 0;

102 103
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;
104
static int tls_ok;
105
static tls_ctx_t *tls_ctx;
106

107
/* filtering client connection based on IP */
108
static matchfile_t *banned_ip, *allowed_ip;
109

110
rwlock_t _source_shutdown_rwlock;
Jack Moffitt's avatar
Jack Moffitt committed
111

112
static void _handle_connection(void);
113
static void get_tls_certificate(ice_config_t *config);
Jack Moffitt's avatar
Jack Moffitt committed
114 115 116

void connection_initialize(void)
{
Marvin Scholz's avatar
Marvin Scholz committed
117 118
    if (_initialized)
        return;
119

120
    thread_spin_create (&_connection_lock);
121
    thread_mutex_create(&move_clients_mutex);
122
    thread_rwlock_create(&_source_shutdown_rwlock);
123
    thread_cond_create(&global.shutdown_cond);
124 125 126 127
    _req_queue = NULL;
    _req_queue_tail = &_req_queue;
    _con_queue = NULL;
    _con_queue_tail = &_con_queue;
Jack Moffitt's avatar
Jack Moffitt committed
128

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

void connection_shutdown(void)
{
Marvin Scholz's avatar
Marvin Scholz committed
134 135
    if (!_initialized)
        return;
136

137
    tls_ctx_unref(tls_ctx);
138 139 140
    matchfile_release(banned_ip);
    matchfile_release(allowed_ip);
 
141
    thread_cond_destroy(&global.shutdown_cond);
142
    thread_rwlock_destroy(&_source_shutdown_rwlock);
143
    thread_spin_destroy (&_connection_lock);
144
    thread_mutex_destroy(&move_clients_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
145

146
    _initialized = 0;
Jack Moffitt's avatar
Jack Moffitt committed
147 148
}

149
void connection_reread_config(ice_config_t *config)
150
{
151
    get_tls_certificate(config);
152 153
}

Jack Moffitt's avatar
Jack Moffitt committed
154 155
static unsigned long _next_connection_id(void)
{
156
    unsigned long id;
Jack Moffitt's avatar
Jack Moffitt committed
157

158
    thread_spin_lock(&_connection_lock);
159
    id = _current_id++;
160
    thread_spin_unlock(&_connection_lock);
Jack Moffitt's avatar
Jack Moffitt committed
161

162
    return id;
Jack Moffitt's avatar
Jack Moffitt committed
163 164
}

165

166
#ifdef ICECAST_CAP_TLS
167
static void get_tls_certificate(ice_config_t *config)
168
{
169 170
    const char *keyfile;

171
    config->tls_ok = tls_ok = 0;
172

173 174 175 176
    keyfile = config->tls_context.key_file;
    if (!keyfile)
        keyfile = config->tls_context.cert_file;

177
    tls_ctx_unref(tls_ctx);
178
    tls_ctx = tls_ctx_new(config->tls_context.cert_file, keyfile, config->tls_context.cipher_list);
179 180
    if (!tls_ctx) {
        ICECAST_LOG_INFO("No TLS capability on any configured ports");
181
        return;
182 183
    }

184
    config->tls_ok = tls_ok = 1;
185 186 187
}


188
/* handlers for reading and writing a connection_t when there is TLS
189 190
 * configured on the listening port
 */
191
static int connection_read_tls(connection_t *con, void *buf, size_t len)
192
{
193
    ssize_t bytes = tls_read(con->tls, buf, len);
194

Marvin Scholz's avatar
Marvin Scholz committed
195
    if (bytes < 0) {
196
        if (tls_want_io(con->tls) > 0)
197
            return -1;
198 199 200 201 202
        con->error = 1;
    }
    return bytes;
}

203
static int connection_send_tls(connection_t *con, const void *buf, size_t len)
204
{
205
    ssize_t bytes = tls_write(con->tls, buf, len);
206

Marvin Scholz's avatar
Marvin Scholz committed
207
    if (bytes < 0) {
208 209
        if (tls_want_io(con->tls) > 0)
            return -1;
210
        con->error = 1;
Marvin Scholz's avatar
Marvin Scholz committed
211
    } else {
212
        con->sent_bytes += bytes;
Marvin Scholz's avatar
Marvin Scholz committed
213
    }
214 215 216 217
    return bytes;
}
#else

218 219
/* TLS not compiled in, so at least log it */
static void get_tls_certificate(ice_config_t *config)
220
{
221
    tls_ok = 0;
222
    ICECAST_LOG_INFO("No TLS capability. "
Philipp Schafft's avatar
Philipp Schafft committed
223
                     "Rebuild Icecast with OpenSSL support to enable this.");
224
}
225
#endif /* ICECAST_CAP_TLS */
226 227 228 229 230


/* 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
231
static int connection_read(connection_t *con, void *buf, size_t len)
232
{
Marvin Scholz's avatar
Marvin Scholz committed
233
    int bytes = sock_read_bytes(con->sock, buf, len);
234 235
    if (bytes == 0)
        con->error = 1;
Marvin Scholz's avatar
Marvin Scholz committed
236
    if (bytes == -1 && !sock_recoverable(sock_error()))
237 238 239 240
        con->error = 1;
    return bytes;
}

Marvin Scholz's avatar
Marvin Scholz committed
241
static int connection_send(connection_t *con, const void *buf, size_t len)
242
{
Marvin Scholz's avatar
Marvin Scholz committed
243 244 245
    int bytes = sock_write_bytes(con->sock, buf, len);
    if (bytes < 0) {
        if (!sock_recoverable(sock_error()))
246
            con->error = 1;
Marvin Scholz's avatar
Marvin Scholz committed
247
    } else {
248
        con->sent_bytes += bytes;
Marvin Scholz's avatar
Marvin Scholz committed
249
    }
250 251 252
    return bytes;
}

253 254
connection_t *connection_create (sock_t sock, sock_t serversock, char *ip)
{
255
    connection_t *con;
256
    con = (connection_t *)calloc(1, sizeof(connection_t));
Marvin Scholz's avatar
Marvin Scholz committed
257 258
    if (con) {
        con->sock       = sock;
259
        con->serversock = serversock;
Marvin Scholz's avatar
Marvin Scholz committed
260 261 262
        con->con_time   = time(NULL);
        con->id         = _next_connection_id();
        con->ip         = ip;
263
        con->tlsmode    = ICECAST_TLSMODE_AUTO;
Marvin Scholz's avatar
Marvin Scholz committed
264 265
        con->read       = connection_read;
        con->send       = connection_send;
266
    }
267

268
    return con;
269 270
}

271
/* prepare connection for interacting over a TLS connection
272
 */
273
void connection_uses_tls(connection_t *con)
274
{
275
#ifdef ICECAST_CAP_TLS
276
    if (con->tls)
277 278
        return;

279
    con->tlsmode = ICECAST_TLSMODE_RFC2818;
280 281
    con->read = connection_read_tls;
    con->send = connection_send_tls;
282 283 284
    con->tls = tls_new(tls_ctx);
    tls_set_incoming(con->tls);
    tls_set_socket(con->tls, con->sock);
285 286 287
#endif
}

288 289 290 291 292
ssize_t connection_read_bytes(connection_t *con, void *buf, size_t len)
{
    return con->read(con, buf, len);
}

293
static sock_t wait_for_serversock(int timeout)
294 295
{
#ifdef HAVE_POLL
296
    struct pollfd ufds [global.server_sockets];
297 298 299 300 301 302 303 304 305 306
    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) {
307
        return SOCK_ERROR;
Marvin Scholz's avatar
Marvin Scholz committed
308
    } else if(ret == 0) {
309
        return SOCK_ERROR;
Marvin Scholz's avatar
Marvin Scholz committed
310
    } else {
311
        int dst;
312
        for(i=0; i < global.server_sockets; i++) {
313
            if(ufds[i].revents & POLLIN)
314
                return ufds[i].fd;
Marvin Scholz's avatar
Marvin Scholz committed
315 316
            if(ufds[i].revents & (POLLHUP|POLLERR|POLLNVAL)) {
                if (ufds[i].revents & (POLLHUP|POLLERR)) {
317
                    sock_close (global.serversock[i]);
318
                    ICECAST_LOG_WARN("Had to close a listening socket");
319
                }
320
                global.serversock[i] = SOCK_ERROR;
321
            }
322
        }
323
        /* remove any closed sockets */
Marvin Scholz's avatar
Marvin Scholz committed
324
        for(i=0, dst=0; i < global.server_sockets; i++) {
325
            if (global.serversock[i] == SOCK_ERROR)
326
            continue;
327
            if (i!=dst)
328
            global.serversock[dst] = global.serversock[i];
329 330 331
            dst++;
        }
        global.server_sockets = dst;
332
        return SOCK_ERROR;
333 334 335 336 337
    }
#else
    fd_set rfds;
    struct timeval tv, *p=NULL;
    int i, ret;
338
    sock_t max = SOCK_ERROR;
339 340 341 342 343

    FD_ZERO(&rfds);

    for(i=0; i < global.server_sockets; i++) {
        FD_SET(global.serversock[i], &rfds);
344
        if (max == SOCK_ERROR || global.serversock[i] > max)
345 346 347 348 349
            max = global.serversock[i];
    }

    if(timeout >= 0) {
        tv.tv_sec = timeout/1000;
350
        tv.tv_usec = (timeout % 1000) * 1000;
351 352 353 354 355
        p = &tv;
    }

    ret = select(max+1, &rfds, NULL, NULL, p);
    if(ret < 0) {
356
        return SOCK_ERROR;
Marvin Scholz's avatar
Marvin Scholz committed
357
    } else if(ret == 0) {
358
        return SOCK_ERROR;
Marvin Scholz's avatar
Marvin Scholz committed
359
    } else {
360 361 362 363
        for(i=0; i < global.server_sockets; i++) {
            if(FD_ISSET(global.serversock[i], &rfds))
                return global.serversock[i];
        }
364
        return SOCK_ERROR; /* Should be impossible, stop compiler warnings */
365 366 367 368
    }
#endif
}

369
static connection_t *_accept_connection(int duration)
Jack Moffitt's avatar
Jack Moffitt committed
370
{
371
    sock_t sock, serversock;
372
    char *ip;
Jack Moffitt's avatar
Jack Moffitt committed
373

374
    serversock = wait_for_serversock (duration);
375
    if (serversock == SOCK_ERROR)
376
        return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
377

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

381
    sock = sock_accept(serversock, ip, MAX_ADDR_LEN);
Marvin Scholz's avatar
Marvin Scholz committed
382
    if (sock != SOCK_ERROR) {
383
        connection_t *con = NULL;
384
        /* Make any IPv4 mapped IPv6 address look like a normal IPv4 address */
Marvin Scholz's avatar
Marvin Scholz committed
385 386
        if (strncmp(ip, "::ffff:", 7) == 0)
            memmove(ip, ip+7, strlen (ip+7)+1);
Jack Moffitt's avatar
Jack Moffitt committed
387

388 389
        if (matchfile_match_allow_deny(allowed_ip, banned_ip, ip))
            con = connection_create (sock, serversock, ip);
390 391
        if (con)
            return con;
Marvin Scholz's avatar
Marvin Scholz committed
392 393 394
        sock_close(sock);
    } else {
        if (!sock_recoverable(sock_error())) {
395
            ICECAST_LOG_WARN("accept() failed with error %d: %s", sock_error(), strerror(sock_error()));
Marvin Scholz's avatar
Marvin Scholz committed
396
            thread_sleep(500000);
397
        }
398 399 400
    }
    free(ip);
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
401 402 403
}


404 405 406 407
/* 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
 */
408
static void _add_connection(client_queue_t *node)
Jack Moffitt's avatar
Jack Moffitt committed
409
{
410
    thread_spin_lock(&_connection_lock);
411
    *_con_queue_tail = node;
412 413
    _con_queue_tail = (volatile client_queue_t **) &node->next;
    thread_spin_unlock(&_connection_lock);
Jack Moffitt's avatar
Jack Moffitt committed
414 415 416
}


417 418 419 420 421 422
/* 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
423

Marvin Scholz's avatar
Marvin Scholz committed
424
    thread_spin_lock(&_connection_lock);
425

Marvin Scholz's avatar
Marvin Scholz committed
426
    if (_con_queue){
427 428 429 430
        node = (client_queue_t *)_con_queue;
        _con_queue = node->next;
        if (_con_queue == NULL)
            _con_queue_tail = &_con_queue;
431
        node->next = NULL;
432
    }
433

Marvin Scholz's avatar
Marvin Scholz committed
434
    thread_spin_unlock(&_connection_lock);
435 436
    return node;
}
Jack Moffitt's avatar
Jack Moffitt committed
437 438


439
/* run along queue checking for any data that has come in or a timeout */
440
static void process_request_queue (void)
441 442
{
    client_queue_t **node_ref = (client_queue_t **)&_req_queue;
443 444 445 446 447 448
    ice_config_t *config;
    int timeout;
    char peak;

    config = config_get_config();
    timeout = config->header_timeout;
449
    config_release_config();
Jack Moffitt's avatar
Jack Moffitt committed
450

Marvin Scholz's avatar
Marvin Scholz committed
451
    while (*node_ref) {
452 453 454 455
        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
456

457
        if (client->con->tlsmode == ICECAST_TLSMODE_AUTO || client->con->tlsmode == ICECAST_TLSMODE_AUTO_NO_PLAIN) {
458 459
            if (recv(client->con->sock, &peak, 1, MSG_PEEK) == 1) {
                if (peak == 0x16) { /* TLS Record Protocol Content type 0x16 == Handshake */
460
                    connection_uses_tls(client->con);
461 462 463 464
                }
            }
        }

Marvin Scholz's avatar
Marvin Scholz committed
465 466
        if (len > 0) {
            if (client->con->con_time + timeout <= time(NULL)) {
467
                len = 0;
Marvin Scholz's avatar
Marvin Scholz committed
468 469 470
            } else {
                len = client_read_bytes(client, buf, len);
            }
471
        }
Jack Moffitt's avatar
Jack Moffitt committed
472

Marvin Scholz's avatar
Marvin Scholz committed
473
        if (len > 0) {
474 475 476
            int pass_it = 1;
            char *ptr;

477 478
            /* handle \n, \r\n and nsvcap which for some strange reason has
             * EOL as \r\r\n */
479
            node->offset += len;
Marvin Scholz's avatar
Marvin Scholz committed
480 481 482
            client->refbuf->data[node->offset] = '\000';
            do {
                if (node->shoutcast == 1) {
483
                    /* password line */
484 485
                    if (strstr (client->refbuf->data, "\r\r\n") != NULL)
                        break;
486 487 488 489 490 491 492
                    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
493 494
                ptr = strstr(client->refbuf->data, "\r\r\n\r\r\n");
                if (ptr) {
495 496 497
                    node->stream_offset = (ptr+6) - client->refbuf->data;
                    break;
                }
Marvin Scholz's avatar
Marvin Scholz committed
498 499
                ptr = strstr(client->refbuf->data, "\r\n\r\n");
                if (ptr) {
500 501 502
                    node->stream_offset = (ptr+4) - client->refbuf->data;
                    break;
                }
Marvin Scholz's avatar
Marvin Scholz committed
503 504
                ptr = strstr(client->refbuf->data, "\n\n");
                if (ptr) {
505 506 507 508 509
                    node->stream_offset = (ptr+2) - client->refbuf->data;
                    break;
                }
                pass_it = 0;
            } while (0);
Jack Moffitt's avatar
Jack Moffitt committed
510

Marvin Scholz's avatar
Marvin Scholz committed
511
            if (pass_it) {
512 513 514 515
                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
516
                _add_connection(node);
517
                continue;
518
            }
Marvin Scholz's avatar
Marvin Scholz committed
519 520
        } else {
            if (len == 0 || client->con->error) {
521 522 523
                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
524 525
                client_destroy(client);
                free(node);
526 527 528 529
                continue;
            }
        }
        node_ref = &node->next;
530
    }
531
    _handle_connection();
Jack Moffitt's avatar
Jack Moffitt committed
532 533
}

534

535 536 537
/* 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
538
static void _add_request_queue(client_queue_t *node)
539 540 541
{
    *_req_queue_tail = node;
    _req_queue_tail = (volatile client_queue_t **)&node->next;
Jack Moffitt's avatar
Jack Moffitt committed
542 543
}

544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
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;
561 562
        client->con->tlsmode = listener->tls;
        if (listener->tls == ICECAST_TLSMODE_RFC2818 && tls_ok)
563
            connection_uses_tls(client->con);
564 565 566 567 568 569 570 571
        if (listener->shoutcast_mount)
            node->shoutcast_mount = strdup(listener->shoutcast_mount);
    }

    config_release_config();

    return node;
}
572

573 574 575 576 577 578 579 580
void connection_queue(connection_t *con)
{
    client_queue_t *node;
    client_t *client = NULL;

    global_lock();
    if (client_create(&client, con, NULL) < 0) {
        global_unlock();
581
        client_send_error_by_id(client, ICECAST_ERROR_GEN_CLIENT_LIMIT);
582 583 584 585 586 587 588 589 590 591
        /* don't be too eager as this is an imposed hard limit */
        thread_sleep(400000);
        return;
    }

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

    if (sock_set_blocking(client->con->sock, 0) || sock_set_nodelay(client->con->sock)) {
        global_unlock();
592
        ICECAST_LOG_WARN("Failed to set tcp options on client connection, dropping");
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
        client_destroy(client);
        return;
    }
    node = create_client_node(client);
    global_unlock();

    if (node == NULL) {
        client_destroy(client);
        return;
    }

    _add_request_queue(node);
    stats_event_inc(NULL, "connections");
}

Marvin Scholz's avatar
Marvin Scholz committed
608
void connection_accept_loop(void)
Jack Moffitt's avatar
Jack Moffitt committed
609
{
610
    connection_t *con;
611
    ice_config_t *config;
612
    int duration = 300;
613

Marvin Scholz's avatar
Marvin Scholz committed
614
    config = config_get_config();
615
    get_tls_certificate(config);
Marvin Scholz's avatar
Marvin Scholz committed
616
    config_release_config();
Jack Moffitt's avatar
Jack Moffitt committed
617

Marvin Scholz's avatar
Marvin Scholz committed
618
    while (global.running == ICECAST_RUNNING) {
619
        con = _accept_connection (duration);
620

Marvin Scholz's avatar
Marvin Scholz committed
621
        if (con) {
622
            connection_queue(con);
623
            duration = 5;
Marvin Scholz's avatar
Marvin Scholz committed
624
        } else {
625 626
            if (_req_queue == NULL)
                duration = 300; /* use longer timeouts when nothing waiting */
627
        }
Marvin Scholz's avatar
Marvin Scholz committed
628
        process_request_queue();
629
    }
Jack Moffitt's avatar
Jack Moffitt committed
630

631 632 633
    /* Give all the other threads notification to shut down */
    thread_cond_broadcast(&global.shutdown_cond);

634 635 636
    /* 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
637 638
}

639 640 641

/* Called when activating a source. Verifies that the source count is not
 * exceeded and applies any initial parameters.
642
 */
Marvin Scholz's avatar
Marvin Scholz committed
643
int connection_complete_source(source_t *source, int response)
644
{
645
    ice_config_t *config;
646

Marvin Scholz's avatar
Marvin Scholz committed
647
    global_lock();
648
    ICECAST_LOG_DEBUG("sources count is %d", global.sources);
649

650
    config = config_get_config();
Marvin Scholz's avatar
Marvin Scholz committed
651
    if (global.sources < config->source_limit) {
652
        const char *contenttype;
653
        mount_proxy *mountinfo;
654 655 656 657
        format_type_t format_type;

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

Marvin Scholz's avatar
Marvin Scholz committed
661
            if (format_type == FORMAT_ERROR) {
662
                config_release_config();
663
                global_unlock();
664
                if (response) {
665
                    client_send_error_by_id(source->client, ICECAST_ERROR_CON_CONTENT_TYPE_NOSYS);
666 667
                    source->client = NULL;
                }
668
                ICECAST_LOG_WARN("Content-type \"%s\" not supported, dropping source", contenttype);
669 670
                return -1;
            }
671 672 673 674
        } else if (source->parser->req_type == httpp_req_put) {
            config_release_config();
            global_unlock();
            if (response) {
675
                client_send_error_by_id(source->client, ICECAST_ERROR_CON_NO_CONTENT_TYPE_GIVEN);
676 677 678 679 680 681 682 683
                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!");
684
            format_type = FORMAT_TYPE_GENERIC;
685 686
        }

Marvin Scholz's avatar
Marvin Scholz committed
687
        if (format_get_plugin (format_type, source) < 0) {
688 689
            global_unlock();
            config_release_config();
Marvin Scholz's avatar
Marvin Scholz committed
690
            if (response) {
691
                client_send_error_by_id(source->client, ICECAST_ERROR_CON_INTERNAL_FORMAT_ALLOC_ERROR);
692 693
                source->client = NULL;
            }
694
            ICECAST_LOG_WARN("plugin format failed for \"%s\"", source->mount);
695 696 697
            return -1;
        }

698
        global.sources++;
Marvin Scholz's avatar
Marvin Scholz committed
699
        stats_event_args(NULL, "sources", "%d", global.sources);
700
        global_unlock();
701

702
        source->running = 1;
Marvin Scholz's avatar
Marvin Scholz committed
703 704
        mountinfo = config_find_mount(config, source->mount, MOUNT_TYPE_NORMAL);
        source_update_settings(config, source, mountinfo);
705
        config_release_config();
706
        slave_rebuild_mounts();
707 708

        source->shutdown_rwlock = &_source_shutdown_rwlock;
709
        ICECAST_LOG_DEBUG("source is ready to start");
710 711 712

        return 0;
    }
713
    ICECAST_LOG_WARN("Request to add source when maximum source limit "
Marvin Scholz's avatar
Marvin Scholz committed
714
        "reached %d", global.sources);
715 716 717 718

    global_unlock();
    config_release_config();

Marvin Scholz's avatar
Marvin Scholz committed
719
    if (response) {
720
        client_send_error_by_id(source->client, ICECAST_ERROR_CON_SOURCE_CLIENT_LIMIT);
721 722
        source->client = NULL;
    }
723 724 725 726

    return -1;
}

Marvin Scholz's avatar
Marvin Scholz committed
727
static inline void source_startup(client_t *client, const char *uri)
728 729
{
    source_t *source;
Marvin Scholz's avatar
Marvin Scholz committed
730
    source = source_reserve(uri);
731

Marvin Scholz's avatar
Marvin Scholz committed
732
    if (source) {
733
        source->client = client;
734 735
        source->parser = client->parser;
        source->con = client->con;
Marvin Scholz's avatar
Marvin Scholz committed
736 737 738
        if (connection_complete_source(source, 1) < 0) {
            source_clear_source(source);
            source_free_source(source);
739 740 741
            return;
        }
        client->respcode = 200;
Philipp Schafft's avatar
Philipp Schafft committed
742 743 744 745
        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 */
746
            client_send_bytes(client, "OK2\r\nicy-caps:11\r\n\r\n", 20); /* TODO: Replace Magic Number! */
747
            source->shoutcast_compat = 1;
Marvin Scholz's avatar
Marvin Scholz committed
748
            source_client_callback(client, source);
Philipp Schafft's avatar
Philipp Schafft committed
749
        } else {
Marvin Scholz's avatar
Marvin Scholz committed
750
            refbuf_t *ok = refbuf_new(PER_CLIENT_REFBUF_SIZE);
751
            const char *expectcontinue;
752
            const char *transfer_encoding;
753
            int status_to_send = 200;
754
            ssize_t ret;
755

756 757 758 759
            transfer_encoding = httpp_getvar(source->parser, "transfer-encoding");
            if (transfer_encoding && strcasecmp(transfer_encoding, HTTPP_ENCODING_IDENTITY) != 0) {
                client->encoding = httpp_encoding_new(transfer_encoding);
                if (!client->encoding) {
760
                    client_send_error_by_id(client, ICECAST_ERROR_CON_UNIMPLEMENTED);
761 762 763 764
                    return;
                }
            }

765 766 767 768 769 770 771
            /* For PUT support we check for 100-continue and send back a 100 to stay in spec */
            expectcontinue = httpp_getvar (source->parser, "expect");

            if (expectcontinue != NULL) {
#ifdef HAVE_STRCASESTR
                if (strcasestr (expectcontinue, "100-continue") != NULL)
#else
772
                ICECAST_LOG_WARN("OS doesn't support case insensitive substring checks...");
773 774 775 776 777 778 779
                if (strstr (expectcontinue, "100-continue") != NULL)
#endif
                {
                    status_to_send = 100;
                }
            }

780
            client->respcode = 200;
781 782
            ret = util_http_build_header(ok->data, PER_CLIENT_REFBUF_SIZE, 0, 0, status_to_send, NULL, NULL, NULL, NULL, NULL, client);
            snprintf(ok->data + ret, PER_CLIENT_REFBUF_SIZE - ret, "Content-Length: 0\r\n\r\n");
Marvin Scholz's avatar
Marvin Scholz committed
783
            ok->len = strlen(ok->data);
784 785 786
            /* 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
787
            fserve_add_client_callback(client, source_client_callback, source);
788
        }
Marvin Scholz's avatar
Marvin Scholz committed
789
    } else {
790
        client_send_error_by_id(client, ICECAST_ERROR_CON_MOUNT_IN_USE);
791
        ICECAST_LOG_WARN("Mountpoint %s in use", uri);
792
    }
793 794
}

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

Marvin Scholz's avatar
Marvin Scholz committed
801
    if (uri[0] != '/') {
Philipp Schafft's avatar
Philipp Schafft committed
802
        ICECAST_LOG_WARN("source mountpoint not starting with /");
803
        client_send_error_by_id(client, ICECAST_ERROR_CON_MOUNTPOINT_NOT_STARTING_WITH_SLASH);
804
        return;
805
    }
806

Philipp Schafft's avatar
Philipp Schafft committed
807 808 809 810
    source_startup(client, uri);
}


Marvin Scholz's avatar
Marvin Scholz committed
811
static void _handle_stats_request(client_t *client, char *uri)
Philipp Schafft's avatar
Philipp Schafft committed
812 813 814
{
    stats_event_inc(NULL, "stats_connections");

815
    client->respcode = 200;
816
    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
Marvin Scholz's avatar
Marvin Scholz committed
817 818 819
        "HTTP/1.0 200 OK\r\n\r\n");
    client->refbuf->len = strlen(client->refbuf->data);
    fserve_add_client_callback(client, stats_callback, NULL);
820 821
}

Philipp Schafft's avatar
Philipp Schafft committed
822 823 824
/* if 0 is returned then the client should not be touched, however if -1
 * is returned then the caller is responsible for handling the client
 */
Marvin Scholz's avatar
Marvin Scholz committed
825
static int __add_listener_to_source(source_t *source, client_t *client)
826
{
Philipp Schafft's avatar
Philipp Schafft committed
827
    size_t loop = 10;
828

Marvin Scholz's avatar
Marvin Scholz committed
829
    do {
Philipp Schafft's avatar
Philipp Schafft committed
830
        ICECAST_LOG_DEBUG("max on %s is %ld (cur %lu)", source->mount,
Marvin Scholz's avatar
Marvin Scholz committed
831
            source->max_listeners, source->listeners);
Philipp Schafft's avatar
Philipp Schafft committed
832 833 834 835
        if (source->max_listeners == -1)
            break;
        if (source->listeners < (unsigned long)source->max_listeners)
            break;
836

Marvin Scholz's avatar
Marvin Scholz committed
837
        if (loop && source->fallback_when_full && source->fallback_mount) {
Philipp Schafft's avatar
Philipp Schafft committed
838 839 840
            source_t *next = source_find_mount (source->fallback_mount);
            if (!next) {
                ICECAST_LOG_ERROR("Fallback '%s' for full source '%s' not found",
Marvin Scholz's avatar
Marvin Scholz committed
841
                    source->mount, source->fallback_mount);
Philipp Schafft's avatar
Philipp Schafft committed
842 843
                return -1;
            }
844
            ICECAST_LOG_INFO("stream full, trying %s", next->mount);
Philipp Schafft's avatar
Philipp Schafft committed
845 846 847 848 849 850 851 852 853 854 855
            source = next;
            loop--;
            continue;
        }
        /* now we fail the client */
        return -1;
    } while (1);

    client->write_to_client = format_generic_write_to_client;
    client->check_buffer = format_check_http_buffer;
    client->refbuf->len = PER_CLIENT_REFBUF_SIZE;
Marvin Scholz's avatar
Marvin Scholz committed
856
    memset(client->refbuf->data, 0, PER_CLIENT_REFBUF_SIZE);
Philipp Schafft's avatar
Philipp Schafft committed
857 858

    /* lets add the client to the active list */
Marvin Scholz's avatar
Marvin Scholz committed
859 860 861
    avl_tree_wlock(source->pending_tree);
    avl_insert(source->pending_tree, client);
    avl_tree_unlock(source->pending_tree);
Philipp Schafft's avatar
Philipp Schafft committed
862

Marvin Scholz's avatar
Marvin Scholz committed
863
    if (source->running == 0 && source->on_demand) {
Philipp Schafft's avatar
Philipp Schafft committed
864 865 866
        /* enable on-demand relay to start, wake up the slave thread */
        ICECAST_LOG_DEBUG("kicking off on-demand relay");
        source->on_demand_req = 1;
867
    }
Philipp Schafft's avatar
Philipp Schafft committed
868 869 870
    ICECAST_LOG_DEBUG("Added client to %s", source->mount);
    return 0;
}
871

Philipp Schafft's avatar
Philipp Schafft committed
872 873 874 875 876 877 878 879 880 881 882 883
/* count the number of clients on a mount with same username and same role as the given one */
static inline ssize_t __count_user_role_on_mount (source_t *source, client_t *client) {
    ssize_t ret = 0;
    avl_node *node;

    avl_tree_rlock(source->client_tree);
    node = avl_get_first(source->client_tree);
    while (node) {
        client_t *existing_client = (client_t *)node->key;
        if (existing_client->username && client->username &&
            strcmp(existing_client->username, client->username) == 0 &&
            existing_client->role && client->role &&
Marvin Scholz's avatar
Marvin Scholz committed
884
            strcmp(existing_client->role, client->role) == 0) {
Philipp Schafft's avatar
Philipp Schafft committed
885
            ret++;
Marvin Scholz's avatar
Marvin Scholz committed
886
        }
Philipp Schafft's avatar
Philipp Schafft committed
887
        node = avl_get_next(node);
888
    }
Philipp Schafft's avatar
Philipp Schafft committed
889 890 891 892
    avl_tree_unlock(source->client_tree);

    avl_tree_rlock(source->pending_tree);
    node = avl_get_first(source->pending_tree);
Marvin Scholz's avatar
Marvin Scholz committed
893
    while (node) {
Philipp Schafft's avatar
Philipp Schafft committed
894 895 896 897
        client_t *existing_client = (client_t *)node->key;
        if (existing_client->username && client->username &&
            strcmp(existing_client->username, client->username) == 0 &&
            existing_client->role && client->role &&
Marvin Scholz's avatar
Marvin Scholz committed
898
            strcmp(existing_client->role, client->role) == 0){
Philipp Schafft's avatar
Philipp Schafft committed
899
            ret++;
Marvin Scholz's avatar
Marvin Scholz committed
900
        }
Philipp Schafft's avatar
Philipp Schafft committed
901
        node = avl_get_next(node);
902
    }
Philipp Schafft's avatar
Philipp Schafft committed
903 904 905 906
    avl_tree_unlock(source->pending_tree);
    return ret;
}

Marvin Scholz's avatar
Marvin Scholz committed
907
static void _handle_get_request(client_t *client, char *uri) {
Philipp Schafft's avatar
Philipp Schafft committed
908 909 910 911 912 913 914 915 916
    source_t *source = NULL;

    ICECAST_LOG_DEBUG("Got client %p with URI %H", client, uri);

    /* 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 and
     * fserve clients, which are looking for static files.
     */
917 918

    stats_event_inc(NULL, "client_connections");
919

Philipp Schafft's avatar
Philipp Schafft committed
920 921 922 923 924 925
    /* this is a web/ request. let's check if we are allowed to do that. */
    if (acl_test_web(client->acl) != ACL_POLICY_ALLOW) {
        /* doesn't seem so, sad client :( */
        if (client->protocol == ICECAST_PROTOCOL_SHOUTCAST) {
            client_destroy(client);
        } else {
926
            client_send_error_by_id(client, ICECAST_ERROR_GEN_CLIENT_NEEDS_TO_AUTHENTICATE);
Philipp Schafft's avatar
Philipp Schafft committed
927 928 929 930
        }
        return;
    }

931 932 933 934 935
    if (client->parser->req_type == httpp_req_options) {
        client_send_204(client);
        return;
    }

Philipp Schafft's avatar
Philipp Schafft committed
936 937 938 939 940 941 942 943 944