client.c 9.37 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-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
12 13
 */

Jack Moffitt's avatar
Jack Moffitt committed
14
/* client.c
15 16 17 18
 **
 ** client interface implementation
 **
 */
Jack Moffitt's avatar
Jack Moffitt committed
19

20 21 22 23
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Jack Moffitt's avatar
Jack Moffitt committed
24 25 26
#include <stdlib.h>
#include <string.h>

Marvin Scholz's avatar
Marvin Scholz committed
27 28 29
#include "common/thread/thread.h"
#include "common/avl/avl.h"
#include "common/httpp/httpp.h"
Jack Moffitt's avatar
Jack Moffitt committed
30

31
#include "cfgfile.h"
Jack Moffitt's avatar
Jack Moffitt committed
32 33
#include "connection.h"
#include "refbuf.h"
Karl Heyes's avatar
Karl Heyes committed
34
#include "format.h"
35
#include "stats.h"
36
#include "fserve.h"
Jack Moffitt's avatar
Jack Moffitt committed
37 38

#include "client.h"
Philipp Schafft's avatar
Philipp Schafft committed
39
#include "auth.h"
Jack Moffitt's avatar
Jack Moffitt committed
40
#include "logging.h"
41

42 43
#include "util.h"

Philipp Schafft's avatar
Philipp Schafft committed
44 45 46
/* for ADMIN_COMMAND_ERROR */
#include "admin.h"

47 48 49
#ifdef _WIN32
#define snprintf _snprintf
#endif
Jack Moffitt's avatar
Jack Moffitt committed
50

51 52 53
#undef CATMODULE
#define CATMODULE "client"

54 55
static inline void client_send_500(client_t *client, const char *message);

56 57 58 59 60
/* create a client_t with the provided connection and parser details. Return
 * 0 on success, -1 if server limit has been reached.  In either case a
 * client_t is returned just in case a message needs to be returned. Should
 * be called with global lock held.
 */
61
int client_create(client_t **c_ptr, connection_t *con, http_parser_t *parser)
Jack Moffitt's avatar
Jack Moffitt committed
62
{
Marvin Scholz's avatar
Marvin Scholz committed
63 64 65
    ice_config_t    *config;
    client_t        *client = (client_t *) calloc(1, sizeof(client_t));
    int              ret    = -1;
66 67

    if (client == NULL)
68
        abort();
69

70
    config = config_get_config();
71 72

    global.clients++;
Marvin Scholz's avatar
Marvin Scholz committed
73
    if (config->client_limit < global.clients) {
74
        ICECAST_LOG_WARN("server client limit reached (%d/%d)", config->client_limit, global.clients);
Marvin Scholz's avatar
Marvin Scholz committed
75
    } else {
76
        ret = 0;
Marvin Scholz's avatar
Marvin Scholz committed
77
    }
Jack Moffitt's avatar
Jack Moffitt committed
78

79 80 81
    config_release_config ();

    stats_event_args (NULL, "clients", "%d", global.clients);
82 83
    client->con = con;
    client->parser = parser;
Philipp Schafft's avatar
Philipp Schafft committed
84 85
    client->protocol = ICECAST_PROTOCOL_HTTP;
    client->admin_command = ADMIN_COMMAND_ERROR;
86 87
    client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
    client->refbuf->len = 0; /* force reader code to ignore buffer contents */
88
    client->pos = 0;
89
    client->write_to_client = format_generic_write_to_client;
90
    *c_ptr = client;
Jack Moffitt's avatar
Jack Moffitt committed
91

92
    return ret;
Jack Moffitt's avatar
Jack Moffitt committed
93 94
}

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
static inline void client_reuseconnection(client_t *client) {
    connection_t *con;
    reuse_t reuse;

    if (!client)
        return;

    con = client->con;
    con = connection_create(con->sock, con->serversock, strdup(con->ip));
    reuse = client->reuse;
    client->con->sock = -1; /* TODO: do not use magic */
    client->reuse = ICECAST_REUSE_CLOSE;

    client_destroy(client);

110 111
    if (reuse == ICECAST_REUSE_UPGRADETLS)
        connection_uses_ssl(con);
112 113 114
    connection_queue(con);
}

Jack Moffitt's avatar
Jack Moffitt committed
115 116
void client_destroy(client_t *client)
{
Philipp Schafft's avatar
Philipp Schafft committed
117
    ICECAST_LOG_DEBUG("Called to destory client %p", client);
Karl Heyes's avatar
Karl Heyes committed
118 119
    if (client == NULL)
        return;
120

121 122 123 124 125
    if (client->reuse != ICECAST_REUSE_CLOSE) {
        client_reuseconnection(client);
        return;
    }

126 127
    /* release the buffer now, as the buffer could be on the source queue
     * and may of disappeared after auth completes */
Marvin Scholz's avatar
Marvin Scholz committed
128
    if (client->refbuf) {
129 130 131 132
        refbuf_release (client->refbuf);
        client->refbuf = NULL;
    }

Philipp Schafft's avatar
Philipp Schafft committed
133
    if (auth_release_client(client))
134 135
        return;

136
    /* write log entry if ip is set (some things don't set it, like outgoing
137 138
     * slave requests
     */
Karl Heyes's avatar
Karl Heyes committed
139
    if (client->respcode && client->parser)
140
        logging_access(client);
141 142
    if (client->con)
        connection_close(client->con);
Karl Heyes's avatar
Karl Heyes committed
143 144
    if (client->parser)
        httpp_destroy(client->parser);
Jack Moffitt's avatar
Jack Moffitt committed
145

146
    global_lock();
147
    global.clients--;
148 149
    stats_event_args(NULL, "clients", "%d", global.clients);
    global_unlock();
150

151 152
    /* we need to free client specific format data (if any) */
    if (client->free_client_data)
153
        client->free_client_data(client);
154

Michael Smith's avatar
Michael Smith committed
155
    free(client->username);
Karl Heyes's avatar
Karl Heyes committed
156
    free(client->password);
Philipp Schafft's avatar
Philipp Schafft committed
157 158
    free(client->role);
    acl_release(client->acl);
Michael Smith's avatar
Michael Smith committed
159

160
    free(client);
Jack Moffitt's avatar
Jack Moffitt committed
161
}
162

163
/* helper function for reading data from a client */
164
int client_read_bytes(client_t *client, void *buf, unsigned len)
165
{
166
    int bytes;
167

Marvin Scholz's avatar
Marvin Scholz committed
168
    if (client->refbuf && client->refbuf->len) {
169 170 171 172
        /* we have data to read from a refbuf first */
        if (client->refbuf->len < len)
            len = client->refbuf->len;
        memcpy (buf, client->refbuf->data, len);
Marvin Scholz's avatar
Marvin Scholz committed
173
        if (len < client->refbuf->len) {
174 175 176 177 178 179
            char *ptr = client->refbuf->data;
            memmove (ptr, ptr+len, client->refbuf->len - len);
        }
        client->refbuf->len -= len;
        return len;
    }
180
    bytes = client->con->read (client->con, buf, len);
181

182
    if (bytes == -1 && client->con->error)
183
        ICECAST_LOG_DEBUG("reading from connection has failed");
184 185

    return bytes;
186 187
}

188
void client_send_error(client_t *client, int status, int plain, const char *message)
189
{
190
    ssize_t ret;
191
    refbuf_t *data;
192

193 194 195 196 197
    if (status == 500) {
         client_send_500(client, message);
         return;
    }

198 199 200 201 202 203
    data = refbuf_new(PER_CLIENT_REFBUF_SIZE);
    if (!data) {
         client_send_500(client, message);
         return;
    }

204
    ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
205
                                 0, status, NULL,
206
                                 plain ? "text/plain" : "text/html", "utf-8",
207
                                 NULL, NULL, client);
208

209 210 211 212 213 214
    if (ret == -1 || ret >= PER_CLIENT_REFBUF_SIZE) {
        ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
        client_send_500(client, "Header generation failed.");
        return;
    }

215 216 217 218 219
    if (plain) {
        strncpy(data->data, message, data->len);
        data->data[data->len-1] = 0;
    } else {
        snprintf(data->data, data->len,
220 221
                 "<html><head><title>Error %i</title></head><body><b>%i - %s</b></body></html>\r\n",
                 status, status, message);
222 223 224 225 226 227
    }
    data->len = strlen(data->data);

    snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret,
             "Content-Length: %llu\r\nConnection: Keep-Alive\r\n\r\n",
             (long long unsigned int)data->len);
228

229
    client->respcode = status;
230
    client->refbuf->len = strlen (client->refbuf->data);
231 232 233 234
    client->refbuf->next = data;

    client->reuse = ICECAST_REUSE_KEEPALIVE;

235
    fserve_add_client (client, NULL);
236 237
}

238 239 240 241 242 243
void client_send_100(client_t *client)
{
    /* On demand inject a HTTP/1.1 100 Continue to make sure clients are happy */
    sock_write (client->con->sock, "HTTP/1.1 100 Continue\r\n\r\n");
}

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
void client_send_101(client_t *client, reuse_t reuse)
{
    ssize_t ret;

    if (!client)
        return;

    if (reuse != ICECAST_REUSE_UPGRADETLS) {
        client_send_500(client, "Bad reuse parameter");
        return;
    }

    ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
                                 0, 101, NULL,
                                 "text/plain", "utf-8",
                                 NULL, NULL, client);

    snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret,
             "Content-Length: 0\r\nUpgrade: TLS/1.0, HTTP/1.0\r\nConnection: Upgrade\r\n\r\n");

    client->respcode = 101;
    client->refbuf->len = strlen(client->refbuf->data);

    client->reuse = reuse;

    fserve_add_client(client, NULL);
}

void client_send_426(client_t *client, reuse_t reuse)
{
    ssize_t ret;

    if (!client)
        return;

    if (reuse != ICECAST_REUSE_UPGRADETLS) {
        client_send_500(client, "Bad reuse parameter");
        return;
    }

    ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
                                 0, 426, NULL,
                                 "text/plain", "utf-8",
                                 NULL, NULL, client);

    snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret,
             "Content-Length: 0\r\nUpgrade: TLS/1.0, HTTP/1.0\r\nConnection: Upgrade\r\n\r\n");

    client->respcode = 426;
    client->refbuf->len = strlen(client->refbuf->data);

    client->reuse = ICECAST_REUSE_KEEPALIVE;

    fserve_add_client(client, NULL);
}

300
/* this function is designed to work even if client is in bad state */
Marvin Scholz's avatar
Marvin Scholz committed
301 302
static inline void client_send_500(client_t *client, const char *message)
{
303 304
    const char header[] = "HTTP/1.0 500 Internal Server Error\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n"
                          "500 - Internal Server Error\n---------------------------\n";
Philipp Schafft's avatar
Philipp Schafft committed
305 306
    const ssize_t header_len = sizeof(header) - 1;
    ssize_t ret;
307 308 309 310 311 312 313 314 315

    ret = client_send_bytes(client, header, header_len);

    /* only send message if we have one AND if header could have transmitted completly */
    if (message && ret == header_len)
        client_send_bytes(client, message, strlen(message));

    client_destroy(client);
}
316 317

/* helper function for sending the data to a client */
318
int client_send_bytes(client_t *client, const void *buf, unsigned len)
319
{
320
    int ret = client->con->send(client->con, buf, len);
321 322

    if (client->con->error)
323
        ICECAST_LOG_DEBUG("Client connection died");
324

325 326 327
    return ret;
}

328
void client_set_queue(client_t *client, refbuf_t *refbuf)
Karl Heyes's avatar
Karl Heyes committed
329 330 331 332
{
    refbuf_t *to_release = client->refbuf;

    client->refbuf = refbuf;
Karl Heyes's avatar
Karl Heyes committed
333
    if (refbuf)
334
        refbuf_addref(client->refbuf);
Karl Heyes's avatar
Karl Heyes committed
335 336
    client->pos = 0;
    if (to_release)
337
        refbuf_release(to_release);
Karl Heyes's avatar
Karl Heyes committed
338
}