util.c 16.3 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).
 */

13
14
15
16
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Jack Moffitt's avatar
Jack Moffitt committed
17
#include <sys/types.h>
18
19
#include <stdio.h>
#include <string.h>
20
#include <stdlib.h>
Jack Moffitt's avatar
Jack Moffitt committed
21
22

#ifndef _WIN32
23
24
#include <sys/time.h>
#include <sys/socket.h>
Jack Moffitt's avatar
Jack Moffitt committed
25
#include <unistd.h>
26
27
28
#ifdef HAVE_POLL
#include <sys/poll.h>
#endif
29
#else
30
#include <winsock2.h>
31
#include <windows.h>
32
33
34
35
#include <stdio.h>
#define snprintf _snprintf
#define strcasecmp stricmp
#define strncasecmp strnicmp
Jack Moffitt's avatar
Jack Moffitt committed
36
37
#endif

Karl Heyes's avatar
Karl Heyes committed
38
#include "net/sock.h"
39
#include "thread/thread.h"
Jack Moffitt's avatar
Jack Moffitt committed
40

41
#include "cfgfile.h"
Jack Moffitt's avatar
Jack Moffitt committed
42
#include "util.h"
43
#include "compat.h"
44
45
46
47
48
49
50
#include "refbuf.h"
#include "connection.h"
#include "client.h"

#define CATMODULE "util"

#include "logging.h"
Jack Moffitt's avatar
Jack Moffitt committed
51

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/* Abstract out an interface to use either poll or select depending on which
 * is available (poll is preferred) to watch a single fd.
 *
 * timeout is in milliseconds.
 *
 * returns > 0 if activity on the fd occurs before the timeout.
 *           0 if no activity occurs
 *         < 0 for error.
 */
int util_timed_wait_for_fd(int fd, int timeout)
{
#ifdef HAVE_POLL
    struct pollfd ufds;

    ufds.fd = fd;
    ufds.events = POLLIN;
    ufds.revents = 0;

    return poll(&ufds, 1, timeout);
#else
    fd_set rfds;
73
    struct timeval tv, *p=NULL;
74
75
76
77

    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);

78
79
80
81
82
83
    if(timeout >= 0) {
        tv.tv_sec = timeout/1000;
        tv.tv_usec = (timeout % 1000)*1000;
        p = &tv;
    }
    return select(fd+1, &rfds, NULL, NULL, p);
84
85
86
#endif
}

87
int util_read_header(int sock, char *buff, unsigned long len, int entire)
Jack Moffitt's avatar
Jack Moffitt committed
88
{
89
90
91
92
    int read_bytes, ret;
    unsigned long pos;
    char c;
    ice_config_t *config;
Michael Smith's avatar
Michael Smith committed
93
    int header_timeout;
Jack Moffitt's avatar
Jack Moffitt committed
94

95
    config = config_get_config();
Michael Smith's avatar
Michael Smith committed
96
97
    header_timeout = config->header_timeout;
    config_release_config();
Jack Moffitt's avatar
Jack Moffitt committed
98

99
100
101
    read_bytes = 1;
    pos = 0;
    ret = 0;
Jack Moffitt's avatar
Jack Moffitt committed
102

103
104
    while ((read_bytes == 1) && (pos < (len - 1))) {
        read_bytes = 0;
Jack Moffitt's avatar
Jack Moffitt committed
105

Michael Smith's avatar
Michael Smith committed
106
        if (util_timed_wait_for_fd(sock, header_timeout*1000) > 0) {
Jack Moffitt's avatar
Jack Moffitt committed
107

108
109
            if ((read_bytes = recv(sock, &c, 1, 0))) {
                if (c != '\r') buff[pos++] = c;
110
111
112
113
114
115
116
117
118
119
120
121
                if (entire) {
                    if ((pos > 1) && (buff[pos - 1] == '\n' && 
                                      buff[pos - 2] == '\n')) {
                        ret = 1;
                        break;
                    }
                }
                else {
                    if ((pos > 1) && (buff[pos - 1] == '\n')) {
                        ret = 1;
                        break;
                    }
122
123
124
125
126
127
128
129
130
131
                }
            }
        } else {
            break;
        }
    }

    if (ret) buff[pos] = '\0';
    
    return ret;
Jack Moffitt's avatar
Jack Moffitt committed
132
133
}

134
char *util_get_extension(const char *path) {
135
136
137
138
139
140
141
142
    char *ext = strrchr(path, '.');

    if(ext == NULL)
        return "";
    else
        return ext+1;
}

143
int util_check_valid_extension(const char *uri) {
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
    int    ret = 0;
    char    *p2;

    if (uri) {
        p2 = strrchr(uri, '.');
        if (p2) {
            p2++;
            if (strncmp(p2, "xsl", strlen("xsl")) == 0) {
                /* Build the full path for the request, concatenating the webroot from the config.
                ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
                */
                ret = XSLT_CONTENT;
            }
            if (strncmp(p2, "htm", strlen("htm")) == 0) {
                /* Build the full path for the request, concatenating the webroot from the config.
                ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
                */
                ret = HTML_CONTENT;
            }
            if (strncmp(p2, "html", strlen("html")) == 0) {
                /* Build the full path for the request, concatenating the webroot from the config.
                ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
                */
                ret = HTML_CONTENT;
            }

        }
    }
    return ret;
173
}
Jack Moffitt's avatar
Jack Moffitt committed
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
static int hex(char c)
{
    if(c >= '0' && c <= '9')
        return c - '0';
    else if(c >= 'A' && c <= 'F')
        return c - 'A' + 10;
    else if(c >= 'a' && c <= 'f')
        return c - 'a' + 10;
    else
        return -1;
}

static int verify_path(char *path) {
    int dir = 0, indotseq = 0;

    while(*path) {
        if(*path == '/' || *path == '\\') {
            if(indotseq)
                return 0;
            if(dir)
                return 0;
            dir = 1;
            path++;
            continue;
        }

        if(dir || indotseq) {
            if(*path == '.')
                indotseq = 1;
            else
                indotseq = 0;
        }
        
        dir = 0;
        path++;
    }

    return 1;
}

215
216
217
218
219
220
221
222
223
224
225
226
227
char *util_get_path_from_uri(char *uri) {
    char *path = util_normalise_uri(uri);
    char *fullpath;

    if(!path)
        return NULL;
    else {
        fullpath = util_get_path_from_normalised_uri(path);
        free(path);
        return fullpath;
    }
}

228
char *util_get_path_from_normalised_uri(const char *uri) {
229
    char *fullpath;
Michael Smith's avatar
Michael Smith committed
230
231
    char *webroot;
    ice_config_t *config = config_get_config();
232

Michael Smith's avatar
Michael Smith committed
233
234
235
    webroot = config->webroot_dir;

    fullpath = malloc(strlen(uri) + strlen(webroot) + 1);
236
237
238
    if (fullpath)
        sprintf (fullpath, "%s%s", webroot, uri);
    config_release_config();
239
240
241
242

    return fullpath;
}

243
static char hexchars[16] = {
Michael Smith's avatar
Michael Smith committed
244
    '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
};

static char safechars[256] = {
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0,
      0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,
      0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
};

266
char *util_url_escape (const char *src)
267
268
269
270
{
    int len = strlen(src);
    /* Efficiency not a big concern here, keep the code simple/conservative */
    char *dst = calloc(1, len*3 + 1); 
271
    unsigned char *source = (unsigned char *)src;
272
273
274
275
276
277
278
279
    int i,j=0;

    for(i=0; i < len; i++) {
        if(safechars[source[i]]) {
            dst[j++] = source[i];
        }
        else {
            dst[j] = '%';
280
281
            dst[j+1] = hexchars[ (source[i] >> 4) & 0xf ];
            dst[j+2] = hexchars[ source[i] & 0xf ];
282
283
284
285
286
287
288
289
            j+= 3;
        }
    }

    dst[j] = 0;
    return dst;
}

290
char *util_url_unescape (const char *src)
291
292
{
    int len = strlen(src);
293
    char *decoded;
294
    int i;
295
    char *dst;
296
297
    int done = 0;

298
    decoded = calloc(1, len + 1);
299

300
    dst = decoded;
301

302
303
    for(i=0; i < len; i++) {
        switch(src[i]) {
304
            case '%':
305
306
                if(i+2 >= len) {
                    free(decoded);
307
308
                    return NULL;
                }
309
310
                if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) {
                    free(decoded);
311
312
313
                    return NULL;
                }

314
                *dst++ = hex(src[i+1]) * 16  + hex(src[i+2]);
315
316
317
318
319
320
                i+= 2;
                break;
            case '#':
                done = 1;
                break;
            case 0:
321
                ERROR0("Fatal internal logic error in util_url_unescape()");
322
                free(decoded);
323
324
325
                return NULL;
                break;
            default:
326
                *dst++ = src[i];
327
328
329
330
331
332
333
334
                break;
        }
        if(done)
            break;
    }

    *dst = 0; /* null terminator */

335
336
337
338
339
340
341
342
    return decoded;
}

/* Get an absolute path (from the webroot dir) from a URI. Return NULL if the
 * path contains 'disallowed' sequences like foo/../ (which could be used to
 * escape from the webroot) or if it cannot be URI-decoded.
 * Caller should free the path.
 */
343
char *util_normalise_uri(const char *uri) {
344
345
346
347
348
    char *path;

    if(uri[0] != '/')
        return NULL;

349
    path = util_url_unescape(uri);
350
351
352
353
354
355

    if(path == NULL) {
        WARN1("Error decoding URI: %s\n", uri);
        return NULL;
    }

356
357
    /* We now have a full URI-decoded path. Check it for allowability */
    if(verify_path(path))
358
        return path;
359
360
361
362
363
364
    else {
        WARN1("Rejecting invalid path \"%s\"", path);
        free(path);
        return NULL;
    }
}
Jack Moffitt's avatar
Jack Moffitt committed
365

366
367
368
369
370
371
372
static char base64table[64] = {
    'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
    'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
    'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
    'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
};

373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
static signed char base64decode[256] = {
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -1, -2, -2,
     -2,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
     -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
};

Michael Smith's avatar
Michael Smith committed
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
char *util_bin_to_hex(unsigned char *data, int len)
{
    char *hex = malloc(len*2 + 1);
    int i;

    for(i = 0; i < len; i++) {
        hex[i*2] = hexchars[(data[i]&0xf0) >> 4];
        hex[i*2+1] = hexchars[data[i]&0x0f];
    }

    hex[len*2] = 0;

    return hex;
}

407
/* This isn't efficient, but it doesn't need to be */
408
char *util_base64_encode(const char *data)
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
{
    int len = strlen(data);
    char *out = malloc(len*4/3 + 4);
    char *result = out;
    int chunk;

    while(len > 0) {
        chunk = (len >3)?3:len;
        *out++ = base64table[(*data & 0xFC)>>2];
        *out++ = base64table[((*data & 0x03)<<4) | ((*(data+1) & 0xF0) >> 4)];
        switch(chunk) {
            case 3:
                *out++ = base64table[((*(data+1) & 0x0F)<<2) | ((*(data+2) & 0xC0)>>6)];
                *out++ = base64table[(*(data+2)) & 0x3F];
                break;
            case 2:
                *out++ = base64table[((*(data+1) & 0x0F)<<2)];
                *out++ = '=';
                break;
            case 1:
                *out++ = '=';
                *out++ = '=';
                break;
        }
        data += chunk;
        len -= chunk;
    }
436
    *out = 0;
437
438
439
440

    return result;
}

441
char *util_base64_decode(const char *data)
442
{
443
444
    const unsigned char *input = (const unsigned char *)data;
    int len = strlen (data);
445
446
447
448
449
450
451
452
453
454
455
    char *out = malloc(len*3/4 + 5);
    char *result = out;
    signed char vals[4];

    while(len > 0) {
        if(len < 4)
        {
            free(result);
            return NULL; /* Invalid Base64 data */
        }

456
457
458
459
        vals[0] = base64decode[*input++];
        vals[1] = base64decode[*input++];
        vals[2] = base64decode[*input++];
        vals[3] = base64decode[*input++];
460
461

        if(vals[0] < 0 || vals[1] < 0 || vals[2] < -1 || vals[3] < -1) {
462
            len -= 4;
463
464
465
466
            continue;
        }

        *out++ = vals[0]<<2 | vals[1]>>4;
467
468
469
470
        /* vals[3] and (if that is) vals[2] can be '=' as padding, which is
           looked up in the base64decode table as '-1'. Check for this case,
           and output zero-terminators instead of characters if we've got
           padding. */
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
        if(vals[2] >= 0)
            *out++ = ((vals[1]&0x0F)<<4) | (vals[2]>>2);
        else
            *out++ = 0;

        if(vals[3] >= 0)
            *out++ = ((vals[2]&0x03)<<6) | (vals[3]);
        else
            *out++ = 0;

        len -= 4;
    }
    *out = 0;

    return result;
}

488
489
util_dict *util_dict_new(void)
{
490
    return (util_dict *)calloc(1, sizeof(util_dict));
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
}

void util_dict_free(util_dict *dict)
{
    util_dict *next;

    while (dict) {
        next = dict->next;

        if (dict->key)
            free (dict->key);
        if (dict->val)
            free (dict->val);
        free (dict);

        dict = next;
    }
}

const char *util_dict_get(util_dict *dict, const char *key)
{
512
513
514
515
516
    while (dict) {
        if (!strcmp(key, dict->key))
            return dict->val;
        dict = dict->next;
    }
Michael Smith's avatar
Michael Smith committed
517
    return NULL;
518
519
520
521
}

int util_dict_set(util_dict *dict, const char *key, const char *val)
{
522
    util_dict *prev;
523

524
    if (!dict || !key) {
525
526
527
528
        ERROR0("NULL values passed to util_dict_set()");
        return 0;
    }

529
530
531
532
533
534
535
536
537
538
539
    prev = NULL;
    while (dict) {
        if (!dict->key || !strcmp(dict->key, key))
            break;
        prev = dict;
        dict = dict->next;
    }

    if (!dict) {
        dict = util_dict_new();
        if (!dict) {
540
            ERROR0("unable to allocate new dictionary");
541
            return 0;
542
        }
543
544
545
        if (prev)
            prev->next = dict;
    }
546

547
548
549
550
551
552
    if (dict->key)
        free (dict->val);
    else if (!(dict->key = strdup(key))) {
        if (prev)
            prev->next = NULL;
        util_dict_free (dict);
553
554
555

        ERROR0("unable to allocate new dictionary key");
        return 0;
556
    }
557

558
559
    dict->val = strdup(val);
    if (!dict->val) {
560
561
        ERROR0("unable to allocate new dictionary value");
        return 0;
562
    }
563

564
    return 1;
565
566
}

567
568
/* given a dictionary, URL-encode each val and 
   stringify it in order as key=val&key=val... if val 
569
570
571
572
   is set, or just key&key if val is NULL.
  TODO: Memory management needs overhaul. */
char *util_dict_urlencode(util_dict *dict, char delim)
{
573
574
575
576
577
578
579
580
581
    char *res, *tmp;
    char *enc;
    int start = 1;

    for (res = NULL; dict; dict = dict->next) {
        /* encode key */
        if (!dict->key)
            continue;
        if (start) {
582
            if (!(res = malloc(strlen(dict->key) + 1))) {
583
584
                return NULL;
            }
585
            sprintf(res, "%s", dict->key);
586
587
            start = 0;
        } else {
588
            if (!(tmp = realloc(res, strlen(res) + strlen(dict->key) + 2))) {
589
590
591
592
                free(res);
                return NULL;
            } else
                res = tmp;
593
            sprintf(res + strlen(res), "%c%s", delim, dict->key);
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
        }

        /* encode value */
        if (!dict->val)
            continue;
        if (!(enc = util_url_escape(dict->val))) {
            free(res);
            return NULL;
        }

        if (!(tmp = realloc(res, strlen(res) + strlen(enc) + 2))) {
            free(enc);
            free(res);
            return NULL;
        } else
            res = tmp;
        sprintf(res + strlen(res), "=%s", enc);
        free(enc);
    }

    return res;
615
}
Michael Smith's avatar
Michael Smith committed
616

617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
#ifndef HAVE_LOCALTIME_R
struct tm *localtime_r (const time_t *timep, struct tm *result)
{
     static mutex_t localtime_lock;
     static int initialised = 0;
     struct tm *tm;

     if (initialised == 0)
     {
         thread_mutex_create (&localtime_lock);
         initialised = 1;
     }
     thread_mutex_lock (&localtime_lock);
     tm = localtime (timep);
     memcpy (result, tm, sizeof (*result));
     thread_mutex_unlock (&localtime_lock);
     return result;
}
#endif