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

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

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

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

Karl Heyes's avatar
Karl Heyes committed
26 27 28
#include "thread/thread.h"
#include "avl/avl.h"
#include "httpp/httpp.h"
Jack Moffitt's avatar
Jack Moffitt committed
29

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

#include "client.h"
#include "logging.h"
39

40 41
#include "util.h"

42 43 44
#ifdef _WIN32
#define snprintf _snprintf
#endif
Jack Moffitt's avatar
Jack Moffitt committed
45

46 47 48
#undef CATMODULE
#define CATMODULE "client"

49 50 51 52 53
/* 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.
 */
54
int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser)
Jack Moffitt's avatar
Jack Moffitt committed
55
{
56
    ice_config_t *config;
57
    client_t *client = (client_t *)calloc(1, sizeof(client_t));
58 59 60
    int ret = -1;

    if (client == NULL)
61
        abort();
62 63

    config = config_get_config ();
64 65

    global.clients++;
66 67 68 69
    if (config->client_limit < global.clients)
        WARN2 ("server client limit reached (%d/%d)", config->client_limit, global.clients);
    else
        ret = 0;
Jack Moffitt's avatar
Jack Moffitt committed
70

71 72 73
    config_release_config ();

    stats_event_args (NULL, "clients", "%d", global.clients);
74 75
    client->con = con;
    client->parser = parser;
76 77
    client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
    client->refbuf->len = 0; /* force reader code to ignore buffer contents */
78
    client->pos = 0;
79
    client->write_to_client = format_generic_write_to_client;
80
    *c_ptr = client;
Jack Moffitt's avatar
Jack Moffitt committed
81

82
    return ret;
Jack Moffitt's avatar
Jack Moffitt committed
83 84 85 86
}

void client_destroy(client_t *client)
{
Karl Heyes's avatar
Karl Heyes committed
87 88
    if (client == NULL)
        return;
89

90 91 92 93 94 95 96 97
    /* release the buffer now, as the buffer could be on the source queue
     * and may of disappeared after auth completes */
    if (client->refbuf)
    {
        refbuf_release (client->refbuf);
        client->refbuf = NULL;
    }

98
    if (auth_release_listener (client))
99 100
        return;

101
    /* write log entry if ip is set (some things don't set it, like outgoing 
102 103
     * slave requests
     */
Karl Heyes's avatar
Karl Heyes committed
104
    if (client->respcode && client->parser)
105
        logging_access(client);
106

107 108
    if (client->con)
        connection_close(client->con);
Karl Heyes's avatar
Karl Heyes committed
109 110
    if (client->parser)
        httpp_destroy(client->parser);
Jack Moffitt's avatar
Jack Moffitt committed
111

112 113 114 115 116
    global_lock ();
    global.clients--;
    stats_event_args (NULL, "clients", "%d", global.clients);
    global_unlock ();

117 118 119 120
    /* we need to free client specific format data (if any) */
    if (client->free_client_data)
        client->free_client_data (client);

Michael Smith's avatar
Michael Smith committed
121
    free(client->username);
Karl Heyes's avatar
Karl Heyes committed
122
    free(client->password);
Michael Smith's avatar
Michael Smith committed
123

124
    free(client);
Jack Moffitt's avatar
Jack Moffitt committed
125
}
126

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
/* return -1 for failed, 0 for authenticated, 1 for pending
 */
int client_check_source_auth (client_t *client, const char *mount)
{
    ice_config_t *config = config_get_config();
    char *pass = config->source_password;
    char *user = "source";
    int ret = -1;
    mount_proxy *mountinfo = config_find_mount (config, mount);

    do
    {
        if (mountinfo)
        {
            ret = 1;
            if (auth_stream_authenticate (client, mount, mountinfo) > 0)
                break;
            ret = -1;
            if (mountinfo->password)
                pass = mountinfo->password;
            if (mountinfo->username)
                user = mountinfo->username;
        }
        if (connection_check_pass (client->parser, user, pass) > 0)
            ret = 0;
    } while (0);
    config_release_config();
    return ret;
}

157 158 159 160

/* helper function for reading data from a client */
int client_read_bytes (client_t *client, void *buf, unsigned len)
{
161
    int bytes;
162

163 164 165 166 167 168
    if (client->refbuf && client->refbuf->len)
    {
        /* we have data to read from a refbuf first */
        if (client->refbuf->len < len)
            len = client->refbuf->len;
        memcpy (buf, client->refbuf->data, len);
169
        if (len < client->refbuf->len)
170 171 172 173 174 175 176
        {
            char *ptr = client->refbuf->data;
            memmove (ptr, ptr+len, client->refbuf->len - len);
        }
        client->refbuf->len -= len;
        return len;
    }
177
    bytes = client->con->read (client->con, buf, len);
178

179 180 181 182
    if (bytes == -1 && client->con->error)
        DEBUG0 ("reading from connection has failed");

    return bytes;
183 184
}

185
static void client_send_error(client_t *client, int status, int plain, const char *message)
186
{
187 188 189
    ssize_t ret;

    ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
190 191 192
                                 0, status, NULL,
                                 plain ? "text/plain" : "text/html", NULL,
                                 plain ? message : "");
193

194 195 196 197
    if (!plain)
        snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret,
                 "<html><head><title>Error %i</title></head><body><b>%i - %s</b></body></html>\r\n",
                 status, status, message);
198

199
    client->respcode = status;
200 201
    client->refbuf->len = strlen (client->refbuf->data);
    fserve_add_client (client, NULL);
202 203
}

204
void client_send_400(client_t *client, const char *message)
205
{
206
    client_send_error(client, 400, 0, message);
207 208
}

209
void client_send_404(client_t *client, const char *message)
210
{
211
    client_send_error(client, 404, 0, message);
212
}
Michael Smith's avatar
Michael Smith committed
213

214 215
void client_send_401(client_t *client)
{
216
    client_send_error(client, 401, 1, "You need to authenticate\r\n");
217
}
218

219
void client_send_403(client_t *client, const char *message)
220
{
221
    client_send_error(client, 403, 1, message);
222
}
223 224 225 226 227


/* helper function for sending the data to a client */
int client_send_bytes (client_t *client, const void *buf, unsigned len)
{
228 229 230
    int ret = client->con->send (client->con, buf, len);

    if (client->con->error)
231
        DEBUG0 ("Client connection died");
232

233 234 235
    return ret;
}

Karl Heyes's avatar
Karl Heyes committed
236 237 238 239 240
void client_set_queue (client_t *client, refbuf_t *refbuf)
{
    refbuf_t *to_release = client->refbuf;

    client->refbuf = refbuf;
Karl Heyes's avatar
Karl Heyes committed
241 242
    if (refbuf)
        refbuf_addref (client->refbuf);
Karl Heyes's avatar
Karl Heyes committed
243 244 245 246 247
    client->pos = 0;
    if (to_release)
        refbuf_release (to_release);
}