1 /* util.c: libshout utility/portability functions
2  *
3  *  Copyright 2002-2003 the Icecast team <team@icecast.org>,
4  *  Copyright 2012-2015 Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Library General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public
17  *  License along with this library; if not, write to the Free
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #   include <config.h>
23 #endif
24 
25 #include <sys/types.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 
30 #ifdef HAVE_SYS_SOCKET_H
31 #   include <sys/socket.h>
32 #endif
33 
34 #ifdef HAVE_WINSOCK2_H
35 #   include <winsock2.h>
36 #endif
37 
38 #include <shout/shout.h>
39 
40 #include "util.h"
41 
42 /* first all static tables, then the code */
43 
44 static const char hexchars[16] = {
45     '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
46 };
47 
48 /* For the next to tables see RFC3986 section 2.2 and 2.3. */
49 static const char safechars[256] = {
50     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
54     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
55     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
56     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
57     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
58     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
65     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66 };
67 
68 static const char safechars_plus_gen_delims_minus_3F_and_23[256] = {
69 /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
70     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
71     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
72     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 2x */
73     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 3x */
74     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
75     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, /* 5x */
76     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
77     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, /* 7x */
78     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8x */
79     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9x */
80     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ax */
81     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* bx */
82     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* cx */
83     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* dx */
84     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ex */
85     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* fx */
86 };
87 
88 static const char base64table[64] = {
89     'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
90     'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
91     'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
92     'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
93 };
94 
95 
_shout_util_strdup(const char * s)96 char *_shout_util_strdup(const char *s)
97 {
98 	if (!s)
99         return NULL;
100 
101     return strdup(s);
102 }
103 
_shout_util_read_header(int sock,char * buff,unsigned long len)104 int _shout_util_read_header(int sock, char *buff, unsigned long len)
105 {
106     int read_bytes, ret;
107     unsigned long pos;
108     char c;
109 
110     read_bytes = 1;
111     pos = 0;
112     ret = 0;
113 
114     while ((read_bytes == 1) && (pos < (len - 1))) {
115         read_bytes = 0;
116 
117         if ((read_bytes = recv(sock, &c, 1, 0))) {
118             if (c != '\r')
119                 buff[pos++] = c;
120             if ((pos > 1) && (buff[pos - 1] == '\n' &&
121                               buff[pos - 2] == '\n')) {
122                 ret = 1;
123                 break;
124             }
125         } else {
126             break;
127         }
128     }
129 
130 	if (ret)
131         buff[pos] = '\0';
132 
133     return ret;
134 }
135 
136 /* This isn't efficient, but it doesn't need to be */
_shout_util_base64_encode(char * data)137 char *_shout_util_base64_encode(char *data)
138 {
139     size_t len = strlen(data);
140     char *out = malloc(len * 4 / 3 + 4);
141     char *result = out;
142     size_t chunk;
143 
144     while (len > 0) {
145         chunk = (len > 3) ? 3 : len;
146         *out++ = base64table[(*data & 0xFC) >> 2];
147         *out++ = base64table[((*data & 0x03) << 4) | ((*(data + 1) & 0xF0) >> 4)];
148 
149         switch (chunk) {
150             case 3:
151                 *out++ = base64table[((*(data + 1) & 0x0F) << 2) | ((*(data + 2) & 0xC0) >> 6)];
152                 *out++ = base64table[(*(data + 2)) & 0x3F];
153             break;
154             case 2:
155                 *out++ = base64table[((*(data + 1) & 0x0F) << 2)];
156                 *out++ = '=';
157             break;
158             case 1:
159                 *out++ = '=';
160                 *out++ = '=';
161             break;
162         }
163         data += chunk;
164         len -= chunk;
165     }
166     *out = 0;
167 
168     return result;
169 }
170 
171 /* modified from libshout1, which credits Rick Franchuk <rickf@transpect.net>.
172  * Caller must free result.
173  */
_url_encode_with_table(const char * data,const char table[256])174 static char *_url_encode_with_table(const char *data, const char table[256])
175 {
176     const char *p;
177     char *q, *dest;
178     int digit;
179     size_t n;
180 
181     for (p = data, n = 0; *p; p++) {
182         n++;
183         if (!table[(unsigned char)(*p)])
184             n += 2;
185     }
186 
187     if (!(dest = malloc(n+1))) return NULL;
188 
189     for (p = data, q = dest; *p; p++, q++) {
190         if (table[(unsigned char)(*p)]) {
191             *q = *p;
192         } else {
193             *q++ = '%';
194             digit = (*p >> 4) & 0xF;
195             *q++ = hexchars[digit];
196             digit = *p & 0xf;
197             *q = hexchars[digit];
198             n += 2;
199         }
200     }
201     *q = '\0';
202 
203     return dest;
204 }
205 
_shout_util_url_encode(const char * data)206 char *_shout_util_url_encode(const char *data)
207 {
208     return _url_encode_with_table(data, safechars);
209 }
210 
_shout_util_url_encode_resource(const char * data)211 char *_shout_util_url_encode_resource(const char *data)
212 {
213     return _url_encode_with_table(data, safechars_plus_gen_delims_minus_3F_and_23);
214 }
215 
_shout_util_dict_new(void)216 util_dict *_shout_util_dict_new(void)
217 {
218     return (util_dict*)calloc(1, sizeof(util_dict));
219 }
220 
_shout_util_dict_free(util_dict * dict)221 void _shout_util_dict_free(util_dict *dict)
222 {
223     util_dict *next;
224 
225     while (dict) {
226         next = dict->next;
227 
228 		if (dict->key)
229             free(dict->key);
230 		if (dict->val)
231             free(dict->val);
232         free(dict);
233 
234         dict = next;
235     }
236 }
237 
_shout_util_dict_get(util_dict * dict,const char * key)238 const char *_shout_util_dict_get(util_dict *dict, const char *key)
239 {
240     while (dict) {
241         if (dict->key && !strcmp(key, dict->key))
242             return dict->val;
243         dict = dict->next;
244     }
245 
246     return NULL;
247 }
248 
_shout_util_dict_set(util_dict * dict,const char * key,const char * val)249 int _shout_util_dict_set(util_dict *dict, const char *key, const char *val)
250 {
251     util_dict *prev;
252 
253     if (!dict || !key) {
254         return SHOUTERR_INSANE;
255     }
256 
257     prev = NULL;
258     while (dict) {
259         if (!dict->key || !strcmp(dict->key, key))
260             break;
261         prev = dict;
262         dict = dict->next;
263     }
264 
265     if (!dict) {
266         dict = _shout_util_dict_new();
267         if (!dict)
268             return SHOUTERR_MALLOC;
269         if (prev)
270             prev->next = dict;
271     }
272 
273     if (dict->key) {
274         free(dict->val);
275     } else if (!(dict->key = strdup(key))) {
276         if (prev)
277             prev->next = NULL;
278         _shout_util_dict_free(dict);
279 
280         return SHOUTERR_MALLOC;
281     }
282 
283     dict->val = strdup(val);
284     if (!dict->val) {
285         return SHOUTERR_MALLOC;
286     }
287 
288     return SHOUTERR_SUCCESS;
289 }
290 
291 /* given a dictionary, URL-encode each key and val and stringify them in order as
292  * key=val&key=val... if val is set, or just key&key if val is NULL.
293  *
294  * TODO: Memory management needs overhaul.
295  */
_shout_util_dict_urlencode(util_dict * dict,char delim)296 char *_shout_util_dict_urlencode(util_dict *dict, char delim)
297 {
298     size_t reslen, resoffset;
299     char *res, *tmp;
300     char *enc;
301     int start = 1;
302 
303     for (res = NULL; dict; dict = dict->next) {
304         /* encode key */
305         if (!dict->key)
306             continue;
307         if (!(enc = _shout_util_url_encode(dict->key))) {
308             if (res)
309                 free(res);
310             return NULL;
311         }
312         if (start) {
313             reslen = strlen(enc) + 1;
314             if (!(res = malloc(reslen))) {
315                 free(enc);
316                 return NULL;
317             }
318             snprintf(res, reslen, "%s", enc);
319             free(enc);
320             start = 0;
321         } else {
322             resoffset = strlen(res);
323             reslen = resoffset + strlen(enc) + 2;
324             if (!(tmp = realloc(res, reslen))) {
325                 free(enc);
326                 free(res);
327                 return NULL;
328             } else {
329                 res = tmp;
330             }
331             snprintf(res + resoffset, reslen - resoffset, "%c%s", delim, enc);
332             free(enc);
333         }
334 
335         /* encode value */
336         if (!dict->val)
337             continue;
338         if (!(enc = _shout_util_url_encode(dict->val))) {
339             free(res);
340             return NULL;
341         }
342 
343         resoffset = strlen(res);
344         reslen = resoffset + strlen(enc) + 2;
345         if (!(tmp = realloc(res, reslen))) {
346             free(enc);
347             free(res);
348             return NULL;
349         } else {
350             res = tmp;
351         }
352         snprintf(res + resoffset, reslen - resoffset, "=%s", enc);
353         free(enc);
354     }
355 
356     return res;
357 }
358 
_shout_util_dict_next(util_dict ** dict,const char ** key,const char ** val)359 const char *_shout_util_dict_next(util_dict **dict, const char **key, const char **val)
360 {
361     *key = NULL;
362     *val = NULL;
363 
364     if (!dict)
365         return NULL;
366     *dict = (*dict)->next;
367     while (*dict && !(*dict)->key)
368         *dict = (*dict)->next;
369     if (!*dict)
370         return NULL;
371     *key = (*dict)->key;
372     *val = (*dict)->val;
373     return *key;
374 }
375