1 /* Icecast
2 *
3 * This program is distributed under the GNU General Public License, version 2.
4 * A copy of this license is included with this source.
5 *
6 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7 * Michael Smith <msmith@xiph.org>,
8 * oddsock <oddsock@xiph.org>,
9 * Karl Heyes <karl@xiph.org>
10 * and others (see AUTHORS for details).
11 * Copyright 2011, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>.
12 */
13
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <errno.h>
24
25 #ifdef HAVE_POLL
26 #include <sys/poll.h>
27 #endif
28
29 #ifndef _WIN32
30 #include <unistd.h>
31 #include <sys/time.h>
32 #include <sys/socket.h>
33 #define SCN_OFF_T SCNdMAX
34 #define PRI_OFF_T PRIdMAX
35 #else
36 #include <winsock2.h>
37 #include <windows.h>
38 #define fseeko fseek
39 #define SCN_OFF_T "ld"
40 #define PRI_OFF_T "ld"
41 #define snprintf _snprintf
42 #define strncasecmp _strnicmp
43 #ifndef S_ISREG
44 #define S_ISREG(mode) ((mode) & _S_IFREG)
45 #endif
46 #endif
47
48 #include "thread/thread.h"
49 #include "avl/avl.h"
50 #include "httpp/httpp.h"
51 #include "net/sock.h"
52
53 #include "connection.h"
54 #include "global.h"
55 #include "refbuf.h"
56 #include "client.h"
57 #include "stats.h"
58 #include "format.h"
59 #include "logging.h"
60 #include "cfgfile.h"
61 #include "util.h"
62 #include "admin.h"
63 #include "compat.h"
64
65 #include "fserve.h"
66
67 #undef CATMODULE
68 #define CATMODULE "fserve"
69
70 #define BUFSIZE 4096
71
72 static volatile int __inited = 0;
73
74 static fserve_t *active_list = NULL;
75 static fserve_t *pending_list = NULL;
76
77 static spin_t pending_lock;
78 static avl_tree *mimetypes = NULL;
79
80 static volatile int run_fserv = 0;
81 static unsigned int fserve_clients;
82 static int client_tree_changed=0;
83
84 #ifdef HAVE_POLL
85 static struct pollfd *ufds = NULL;
86 #else
87 static fd_set fds;
88 static sock_t fd_max = SOCK_ERROR;
89 #endif
90
91 typedef struct {
92 char *ext;
93 char *type;
94 } mime_type;
95
96 static void fserve_client_destroy(fserve_t *fclient);
97 static int _delete_mapping(void *mapping);
98 static void *fserv_thread_function(void *arg);
99
fserve_initialize(void)100 void fserve_initialize(void)
101 {
102 ice_config_t *config = config_get_config();
103
104 mimetypes = NULL;
105 active_list = NULL;
106 pending_list = NULL;
107 thread_spin_create (&pending_lock);
108
109 fserve_recheck_mime_types (config);
110 config_release_config();
111
112 __inited = 1;
113
114 stats_event (NULL, "file_connections", "0");
115 ICECAST_LOG_INFO("file serving started");
116 }
117
fserve_shutdown(void)118 void fserve_shutdown(void)
119 {
120 if (!__inited)
121 return;
122
123 thread_spin_lock (&pending_lock);
124 run_fserv = 0;
125 while (pending_list)
126 {
127 fserve_t *to_go = (fserve_t *)pending_list;
128 pending_list = to_go->next;
129
130 fserve_client_destroy (to_go);
131 }
132 while (active_list)
133 {
134 fserve_t *to_go = active_list;
135 active_list = to_go->next;
136 fserve_client_destroy (to_go);
137 }
138
139 if (mimetypes)
140 avl_tree_free (mimetypes, _delete_mapping);
141
142 thread_spin_unlock (&pending_lock);
143 thread_spin_destroy (&pending_lock);
144 ICECAST_LOG_INFO("file serving stopped");
145 }
146
147 #ifdef HAVE_POLL
fserve_client_waiting(void)148 int fserve_client_waiting (void)
149 {
150 fserve_t *fclient;
151 unsigned int i = 0;
152
153 /* only rebuild ufds if there are clients added/removed */
154 if (client_tree_changed)
155 {
156 client_tree_changed = 0;
157 ufds = realloc(ufds, fserve_clients * sizeof(struct pollfd));
158 fclient = active_list;
159 while (fclient)
160 {
161 ufds[i].fd = fclient->client->con->sock;
162 ufds[i].events = POLLOUT;
163 ufds[i].revents = 0;
164 fclient = fclient->next;
165 i++;
166 }
167 }
168 if (!ufds)
169 {
170 thread_spin_lock (&pending_lock);
171 run_fserv = 0;
172 thread_spin_unlock (&pending_lock);
173 return -1;
174 }
175 else if (poll(ufds, fserve_clients, 200) > 0)
176 {
177 /* mark any clients that are ready */
178 fclient = active_list;
179 for (i=0; i<fserve_clients; i++)
180 {
181 if (ufds[i].revents & (POLLOUT|POLLHUP|POLLERR))
182 fclient->ready = 1;
183 fclient = fclient->next;
184 }
185 return 1;
186 }
187 return 0;
188 }
189 #else
fserve_client_waiting(void)190 int fserve_client_waiting (void)
191 {
192 fserve_t *fclient;
193 fd_set realfds;
194
195 /* only rebuild fds if there are clients added/removed */
196 if(client_tree_changed) {
197 client_tree_changed = 0;
198 FD_ZERO(&fds);
199 fd_max = SOCK_ERROR;
200 fclient = active_list;
201 while (fclient) {
202 FD_SET (fclient->client->con->sock, &fds);
203 if (fclient->client->con->sock > fd_max || fd_max == SOCK_ERROR)
204 fd_max = fclient->client->con->sock;
205 fclient = fclient->next;
206 }
207 }
208 /* hack for windows, select needs at least 1 descriptor */
209 if (fd_max == SOCK_ERROR)
210 {
211 thread_spin_lock (&pending_lock);
212 run_fserv = 0;
213 thread_spin_unlock (&pending_lock);
214 return -1;
215 }
216 else
217 {
218 struct timeval tv;
219 tv.tv_sec = 0;
220 tv.tv_usec = 200000;
221 /* make a duplicate of the set so we do not have to rebuild it
222 * each time around */
223 memcpy(&realfds, &fds, sizeof(fd_set));
224 if(select(fd_max+1, NULL, &realfds, NULL, &tv) > 0)
225 {
226 /* mark any clients that are ready */
227 fclient = active_list;
228 while (fclient)
229 {
230 if (FD_ISSET (fclient->client->con->sock, &realfds))
231 fclient->ready = 1;
232 fclient = fclient->next;
233 }
234 return 1;
235 }
236 }
237 return 0;
238 }
239 #endif
240
wait_for_fds(void)241 static int wait_for_fds(void)
242 {
243 fserve_t *fclient;
244 int ret;
245
246 while (run_fserv)
247 {
248 /* add any new clients here */
249 if (pending_list)
250 {
251 thread_spin_lock (&pending_lock);
252
253 fclient = (fserve_t*)pending_list;
254 while (fclient)
255 {
256 fserve_t *to_move = fclient;
257 fclient = fclient->next;
258 to_move->next = active_list;
259 active_list = to_move;
260 client_tree_changed = 1;
261 fserve_clients++;
262 }
263 pending_list = NULL;
264 thread_spin_unlock (&pending_lock);
265 }
266 /* drop out of here if someone is ready */
267 ret = fserve_client_waiting();
268 if (ret)
269 return ret;
270 }
271 return -1;
272 }
273
fserv_thread_function(void * arg)274 static void *fserv_thread_function(void *arg)
275 {
276 fserve_t *fclient, **trail;
277 size_t bytes;
278
279 while (1)
280 {
281 if (wait_for_fds() < 0)
282 break;
283
284 fclient = active_list;
285 trail = &active_list;
286
287 while (fclient)
288 {
289 /* process this client, if it is ready */
290 if (fclient->ready)
291 {
292 client_t *client = fclient->client;
293 refbuf_t *refbuf = client->refbuf;
294 fclient->ready = 0;
295 if (client->pos == refbuf->len)
296 {
297 /* Grab a new chunk */
298 if (fclient->file)
299 bytes = fread (refbuf->data, 1, BUFSIZE, fclient->file);
300 else
301 bytes = 0;
302 if (bytes == 0)
303 {
304 if (refbuf->next == NULL)
305 {
306 fserve_t *to_go = fclient;
307 fclient = fclient->next;
308 *trail = fclient;
309 fserve_client_destroy (to_go);
310 fserve_clients--;
311 client_tree_changed = 1;
312 continue;
313 }
314 refbuf = refbuf->next;
315 client->refbuf->next = NULL;
316 refbuf_release (client->refbuf);
317 client->refbuf = refbuf;
318 bytes = refbuf->len;
319 }
320 refbuf->len = (unsigned int)bytes;
321 client->pos = 0;
322 }
323
324 /* Now try and send current chunk. */
325 format_generic_write_to_client (client);
326
327 if (client->con->error)
328 {
329 fserve_t *to_go = fclient;
330 fclient = fclient->next;
331 *trail = fclient;
332 fserve_clients--;
333 fserve_client_destroy (to_go);
334 client_tree_changed = 1;
335 continue;
336 }
337 }
338 trail = &fclient->next;
339 fclient = fclient->next;
340 }
341 }
342 ICECAST_LOG_DEBUG("fserve handler exit");
343 return NULL;
344 }
345
346 /* string returned needs to be free'd */
fserve_content_type(const char * path)347 char *fserve_content_type (const char *path)
348 {
349 char *ext = util_get_extension(path);
350 mime_type exttype = {ext, NULL};
351 void *result;
352 char *type;
353
354 thread_spin_lock (&pending_lock);
355 if (mimetypes && !avl_get_by_key (mimetypes, &exttype, &result))
356 {
357 mime_type *mime = result;
358 type = strdup (mime->type);
359 }
360 else {
361 /* Fallbacks for a few basic ones */
362 if(!strcmp(ext, "ogg"))
363 type = strdup ("application/ogg");
364 else if(!strcmp(ext, "mp3"))
365 type = strdup ("audio/mpeg");
366 else if(!strcmp(ext, "html"))
367 type = strdup ("text/html");
368 else if(!strcmp(ext, "css"))
369 type = strdup ("text/css");
370 else if(!strcmp(ext, "txt"))
371 type = strdup ("text/plain");
372 else if(!strcmp(ext, "jpg"))
373 type = strdup ("image/jpeg");
374 else if(!strcmp(ext, "png"))
375 type = strdup ("image/png");
376 else if(!strcmp(ext, "m3u"))
377 type = strdup ("audio/x-mpegurl");
378 else if(!strcmp(ext, "aac"))
379 type = strdup ("audio/aac");
380 else
381 type = strdup ("application/octet-stream");
382 }
383 thread_spin_unlock (&pending_lock);
384 return type;
385 }
386
fserve_client_destroy(fserve_t * fclient)387 static void fserve_client_destroy(fserve_t *fclient)
388 {
389 if (fclient)
390 {
391 if (fclient->file)
392 fclose (fclient->file);
393
394 if (fclient->callback)
395 fclient->callback (fclient->client, fclient->arg);
396 else
397 if (fclient->client)
398 client_destroy (fclient->client);
399 free (fclient);
400 }
401 }
402
403
404 /* client has requested a file, so check for it and send the file. Do not
405 * refer to the client_t afterwards. return 0 for success, -1 on error.
406 */
fserve_client_create(client_t * httpclient,const char * path)407 int fserve_client_create (client_t *httpclient, const char *path)
408 {
409 int bytes;
410 struct stat file_buf;
411 const char *range = NULL;
412 off_t new_content_len = 0;
413 off_t rangenumber = 0, content_length;
414 int rangeproblem = 0;
415 int ret = 0;
416 char *fullpath;
417 int m3u_requested = 0, m3u_file_available = 1;
418 const char * xslt_playlist_requested = NULL;
419 int xslt_playlist_file_available = 1;
420 ice_config_t *config;
421 FILE *file;
422
423 fullpath = util_get_path_from_normalised_uri (path);
424 ICECAST_LOG_INFO("checking for file %H (%H)", path, fullpath);
425
426 if (strcmp (util_get_extension (fullpath), "m3u") == 0)
427 m3u_requested = 1;
428
429 if (strcmp (util_get_extension (fullpath), "xspf") == 0)
430 xslt_playlist_requested = "xspf.xsl";
431
432 if (strcmp (util_get_extension (fullpath), "vclt") == 0)
433 xslt_playlist_requested = "vclt.xsl";
434
435 /* check for the actual file */
436 if (stat (fullpath, &file_buf) != 0)
437 {
438 /* the m3u can be generated, but send an m3u file if available */
439 if (m3u_requested == 0 && xslt_playlist_requested == NULL)
440 {
441 ICECAST_LOG_WARN("req for file \"%H\" %s", fullpath, strerror (errno));
442 client_send_404 (httpclient, "The file you requested could not be found");
443 free (fullpath);
444 return -1;
445 }
446 m3u_file_available = 0;
447 xslt_playlist_file_available = 0;
448 }
449
450 httpclient->refbuf->len = PER_CLIENT_REFBUF_SIZE;
451
452 if (m3u_requested && m3u_file_available == 0)
453 {
454 const char *host = httpp_getvar (httpclient->parser, "host");
455 char *sourceuri = strdup (path);
456 char *dot = strrchr(sourceuri, '.');
457
458 /* at least a couple of players (fb2k/winamp) are reported to send a
459 * host header but without the port number. So if we are missing the
460 * port then lets treat it as if no host line was sent */
461 if (host && strchr (host, ':') == NULL)
462 host = NULL;
463
464 *dot = 0;
465 httpclient->respcode = 200;
466 ret = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
467 0, 200, NULL,
468 "audio/x-mpegurl", NULL, "", NULL);
469 if (ret == -1 || ret >= (BUFSIZE - 512)) { /* we want at least 512 bytes left for the content of the playlist */
470 ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
471 client_send_500(httpclient, "Header generation failed.");
472 return -1;
473 }
474 if (host == NULL)
475 {
476 config = config_get_config();
477 snprintf (httpclient->refbuf->data + ret, BUFSIZE - ret,
478 "http://%s:%d%s\r\n",
479 config->hostname, config->port,
480 sourceuri
481 );
482 config_release_config();
483 }
484 else
485 {
486 snprintf (httpclient->refbuf->data + ret, BUFSIZE - ret,
487 "http://%s%s\r\n",
488 host,
489 sourceuri
490 );
491 }
492 httpclient->refbuf->len = strlen (httpclient->refbuf->data);
493 fserve_add_client (httpclient, NULL);
494 free (sourceuri);
495 free (fullpath);
496 return 0;
497 }
498 if (xslt_playlist_requested && xslt_playlist_file_available == 0)
499 {
500 xmlDocPtr doc;
501 char *reference = strdup (path);
502 char *eol = strrchr (reference, '.');
503 if (eol)
504 *eol = '\0';
505 doc = stats_get_xml (0, reference);
506 free (reference);
507 admin_send_response (doc, httpclient, TRANSFORMED, xslt_playlist_requested);
508 xmlFreeDoc(doc);
509 return 0;
510 }
511
512 /* on demand file serving check */
513 config = config_get_config();
514 if (config->fileserve == 0)
515 {
516 ICECAST_LOG_DEBUG("on demand file \"%H\" refused. Serving static files has been disabled in the config", fullpath);
517 client_send_404 (httpclient, "The file you requested could not be found");
518 config_release_config();
519 free (fullpath);
520 return -1;
521 }
522 config_release_config();
523
524 if (S_ISREG (file_buf.st_mode) == 0)
525 {
526 client_send_404 (httpclient, "The file you requested could not be found");
527 ICECAST_LOG_WARN("found requested file but there is no handler for it: %H", fullpath);
528 free (fullpath);
529 return -1;
530 }
531
532 file = fopen (fullpath, "rb");
533 if (file == NULL)
534 {
535 ICECAST_LOG_WARN("Problem accessing file \"%H\"", fullpath);
536 client_send_404 (httpclient, "File not readable");
537 free (fullpath);
538 return -1;
539 }
540 free (fullpath);
541
542 content_length = file_buf.st_size;
543 range = httpp_getvar (httpclient->parser, "range");
544
545 /* full http range handling is currently not done but we deal with the common case */
546 if (range != NULL) {
547 ret = 0;
548 if (strncasecmp (range, "bytes=", 6) == 0)
549 ret = sscanf (range+6, "%" SCN_OFF_T "-", &rangenumber);
550
551 if (ret != 1) {
552 /* format not correct, so lets just assume
553 we start from the beginning */
554 rangeproblem = 1;
555 }
556 if (rangenumber < 0) {
557 rangeproblem = 1;
558 }
559 if (!rangeproblem) {
560 ret = fseeko (file, rangenumber, SEEK_SET);
561 if (ret != -1) {
562 new_content_len = content_length - rangenumber;
563 if (new_content_len < 0) {
564 rangeproblem = 1;
565 }
566 }
567 else {
568 rangeproblem = 1;
569 }
570 if (!rangeproblem) {
571 off_t endpos = rangenumber+new_content_len-1;
572 char *type;
573
574 if (endpos < 0) {
575 endpos = 0;
576 }
577 httpclient->respcode = 206;
578 type = fserve_content_type (path);
579 bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
580 0, 206, NULL,
581 type, NULL,
582 NULL, NULL);
583 if (bytes == -1 || bytes >= (BUFSIZE - 512)) { /* we want at least 512 bytes left */
584 ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
585 client_send_500(httpclient, "Header generation failed.");
586 return -1;
587 }
588 bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes,
589 "Accept-Ranges: bytes\r\n"
590 "Content-Length: %" PRI_OFF_T "\r\n"
591 "Content-Range: bytes %" PRI_OFF_T \
592 "-%" PRI_OFF_T "/%" PRI_OFF_T "\r\n\r\n",
593 new_content_len,
594 rangenumber,
595 endpos,
596 content_length);
597 free (type);
598 }
599 else {
600 goto fail;
601 }
602 }
603 else {
604 goto fail;
605 }
606 }
607 else {
608 char *type = fserve_content_type(path);
609 httpclient->respcode = 200;
610 bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
611 0, 200, NULL,
612 type, NULL,
613 NULL, NULL);
614 if (bytes == -1 || bytes >= (BUFSIZE - 512)) { /* we want at least 512 bytes left */
615 ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
616 client_send_500(httpclient, "Header generation failed.");
617 return -1;
618 }
619 bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes,
620 "Accept-Ranges: bytes\r\n"
621 "Content-Length: %" PRI_OFF_T "\r\n\r\n",
622 content_length);
623 free (type);
624 }
625 httpclient->refbuf->len = bytes;
626 httpclient->pos = 0;
627
628 stats_event_inc (NULL, "file_connections");
629 fserve_add_client (httpclient, file);
630
631 return 0;
632
633 fail:
634 fclose (file);
635 client_send_error(httpclient, 416, 1, "Request Range Not Satisfiable\r\n");
636 return -1;
637 }
638
639
640 /* Routine to actually add pre-configured client structure to pending list and
641 * then to start off the file serving thread if it is not already running
642 */
fserve_add_pending(fserve_t * fclient)643 static void fserve_add_pending (fserve_t *fclient)
644 {
645 thread_spin_lock (&pending_lock);
646 fclient->next = (fserve_t *)pending_list;
647 pending_list = fclient;
648 if (run_fserv == 0)
649 {
650 run_fserv = 1;
651 ICECAST_LOG_DEBUG("fserve handler waking up");
652 thread_create("File Serving Thread", fserv_thread_function, NULL, THREAD_DETACHED);
653 }
654 thread_spin_unlock (&pending_lock);
655 }
656
657
658 /* Add client to fserve thread, client needs to have refbuf set and filled
659 * but may provide a NULL file if no data needs to be read
660 */
fserve_add_client(client_t * client,FILE * file)661 int fserve_add_client (client_t *client, FILE *file)
662 {
663 fserve_t *fclient = calloc (1, sizeof(fserve_t));
664
665 ICECAST_LOG_DEBUG("Adding client to file serving engine");
666 if (fclient == NULL)
667 {
668 client_send_404 (client, "memory exhausted");
669 return -1;
670 }
671 fclient->file = file;
672 fclient->client = client;
673 fclient->ready = 0;
674 fserve_add_pending (fclient);
675
676 return 0;
677 }
678
679
680 /* add client to file serving engine, but just write out the buffer contents,
681 * then pass the client to the callback with the provided arg
682 */
fserve_add_client_callback(client_t * client,fserve_callback_t callback,void * arg)683 void fserve_add_client_callback (client_t *client, fserve_callback_t callback, void *arg)
684 {
685 fserve_t *fclient = calloc (1, sizeof(fserve_t));
686
687 ICECAST_LOG_DEBUG("Adding client to file serving engine");
688 if (fclient == NULL)
689 {
690 client_send_404 (client, "memory exhausted");
691 return;
692 }
693 fclient->file = NULL;
694 fclient->client = client;
695 fclient->ready = 0;
696 fclient->callback = callback;
697 fclient->arg = arg;
698
699 fserve_add_pending (fclient);
700 }
701
702
_delete_mapping(void * mapping)703 static int _delete_mapping(void *mapping) {
704 mime_type *map = mapping;
705 free(map->ext);
706 free(map->type);
707 free(map);
708
709 return 1;
710 }
711
_compare_mappings(void * arg,void * a,void * b)712 static int _compare_mappings(void *arg, void *a, void *b)
713 {
714 return strcmp(
715 ((mime_type *)a)->ext,
716 ((mime_type *)b)->ext);
717 }
718
fserve_recheck_mime_types(ice_config_t * config)719 void fserve_recheck_mime_types (ice_config_t *config)
720 {
721 FILE *mimefile;
722 char line[4096];
723 char *type, *ext, *cur;
724 mime_type *mapping;
725 avl_tree *new_mimetypes;
726
727 if (config->mimetypes_fn == NULL)
728 return;
729 mimefile = fopen (config->mimetypes_fn, "r");
730 if (mimefile == NULL)
731 {
732 ICECAST_LOG_WARN("Cannot open mime types file %s", config->mimetypes_fn);
733 return;
734 }
735
736 new_mimetypes = avl_tree_new(_compare_mappings, NULL);
737
738 while(fgets(line, 4096, mimefile))
739 {
740 line[4095] = 0;
741
742 if(*line == 0 || *line == '#')
743 continue;
744
745 type = line;
746
747 cur = line;
748
749 while(*cur != ' ' && *cur != '\t' && *cur)
750 cur++;
751 if(*cur == 0)
752 continue;
753
754 *cur++ = 0;
755
756 while(1) {
757 while(*cur == ' ' || *cur == '\t')
758 cur++;
759 if(*cur == 0)
760 break;
761
762 ext = cur;
763 while(*cur != ' ' && *cur != '\t' && *cur != '\n' && *cur)
764 cur++;
765 *cur++ = 0;
766 if(*ext)
767 {
768 void *tmp;
769 /* Add a new extension->type mapping */
770 mapping = malloc(sizeof(mime_type));
771 mapping->ext = strdup(ext);
772 mapping->type = strdup(type);
773 if (!avl_get_by_key (new_mimetypes, mapping, &tmp))
774 avl_delete (new_mimetypes, mapping, _delete_mapping);
775 avl_insert (new_mimetypes, mapping);
776 }
777 }
778 }
779 fclose(mimefile);
780
781 thread_spin_lock (&pending_lock);
782 if (mimetypes)
783 avl_tree_free (mimetypes, _delete_mapping);
784 mimetypes = new_mimetypes;
785 thread_spin_unlock (&pending_lock);
786 }
787
788