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
}