1 /*
2 * tvheadend, HTTP interface
3 * Copyright (C) 2007 Andreas Öman
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <pthread.h>
20 #include <assert.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <ctype.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <signal.h>
30 #include <netinet/in.h>
31 #include <netinet/tcp.h>
32 #include <arpa/inet.h>
33 #include <openssl/md5.h>
34
35 #include "tvheadend.h"
36 #include "tcp.h"
37 #include "http.h"
38 #include "access.h"
39 #include "notify.h"
40 #include "channels.h"
41 #include "config.h"
42 #include "compat.h"
43
44 #if ENABLE_ANDROID
45 #include <sys/socket.h>
46 #endif
47
48 void *http_server;
49 static int http_server_running;
50
51 static pthread_mutex_t http_paths_mutex = PTHREAD_MUTEX_INITIALIZER;
52 static http_path_list_t http_paths;
53
54 static struct strtab HTTP_cmdtab[] = {
55 { "NONE", HTTP_CMD_NONE },
56 { "GET", HTTP_CMD_GET },
57 { "HEAD", HTTP_CMD_HEAD },
58 { "POST", HTTP_CMD_POST },
59 { "DESCRIBE", RTSP_CMD_DESCRIBE },
60 { "OPTIONS", RTSP_CMD_OPTIONS },
61 { "SETUP", RTSP_CMD_SETUP },
62 { "PLAY", RTSP_CMD_PLAY },
63 { "TEARDOWN", RTSP_CMD_TEARDOWN },
64 { "PAUSE", RTSP_CMD_PAUSE },
65 { "GET_PARAMETER", RTSP_CMD_GET_PARAMETER },
66 };
67
68
69
70 static struct strtab HTTP_versiontab[] = {
71 { "HTTP/1.0", HTTP_VERSION_1_0 },
72 { "HTTP/1.1", HTTP_VERSION_1_1 },
73 { "RTSP/1.0", RTSP_VERSION_1_0 },
74 };
75
76 /**
77 *
78 */
79 const char *
http_cmd2str(int val)80 http_cmd2str(int val)
81 {
82 return val2str(val, HTTP_cmdtab);
83 }
84
http_str2cmd(const char * str)85 int http_str2cmd(const char *str)
86 {
87 return str2val(str, HTTP_cmdtab);
88 }
89
90 const char *
http_ver2str(int val)91 http_ver2str(int val)
92 {
93 return val2str(val, HTTP_versiontab);
94 }
95
http_str2ver(const char * str)96 int http_str2ver(const char *str)
97 {
98 return str2val(str, HTTP_versiontab);
99 }
100
101 /**
102 *
103 */
104 static int
http_resolve(http_connection_t * hc,http_path_t * _hp,char ** remainp,char ** argsp)105 http_resolve(http_connection_t *hc, http_path_t *_hp,
106 char **remainp, char **argsp)
107 {
108 http_path_t *hp;
109 int n = 0, cut = 0;
110 char *v, *path = tvh_strdupa(hc->hc_url);
111 char *npath;
112
113 /* Canocalize path (or at least remove excess slashes) */
114 v = path;
115 while (*v && *v != '?') {
116 if (*v == '/' && v[1] == '/') {
117 int l = strlen(v+1);
118 memmove(v, v+1, l);
119 v[l] = 0;
120 n++;
121 } else
122 v++;
123 }
124
125 while (1) {
126
127 pthread_mutex_lock(hc->hc_paths_mutex);
128 LIST_FOREACH(hp, hc->hc_paths, hp_link) {
129 if(!strncmp(path, hp->hp_path, hp->hp_len)) {
130 if(path[hp->hp_len] == 0 ||
131 path[hp->hp_len] == '/' ||
132 path[hp->hp_len] == '?')
133 break;
134 }
135 }
136 if (hp) {
137 *_hp = *hp;
138 hp = _hp;
139 }
140 pthread_mutex_unlock(hc->hc_paths_mutex);
141
142 if(hp == NULL)
143 return 0;
144
145 cut += hp->hp_len;
146
147 if(hp->hp_path_modify == NULL)
148 break;
149
150 npath = hp->hp_path_modify(hc, path, &cut);
151 if(npath == NULL)
152 break;
153
154 path = tvh_strdupa(npath);
155 free(npath);
156
157 }
158
159 v = hc->hc_url + n + cut;
160
161 *remainp = NULL;
162 *argsp = NULL;
163
164 switch(*v) {
165 case 0:
166 break;
167
168 case '/':
169 if(v[1] == '?') {
170 *argsp = v + 1;
171 break;
172 }
173
174 *remainp = v + 1;
175 v = strchr(v + 1, '?');
176 if(v != NULL) {
177 *v = 0; /* terminate remaining url */
178 *argsp = v + 1;
179 }
180 break;
181
182 case '?':
183 *argsp = v + 1;
184 break;
185
186 default:
187 return 0;
188 }
189
190 return 1;
191 }
192
193
194
195 /*
196 * HTTP status code to string
197 */
198
199 static const char *
http_rc2str(int code)200 http_rc2str(int code)
201 {
202 switch(code) {
203 case HTTP_STATUS_OK: /* 200 */ return "OK";
204 case HTTP_STATUS_PARTIAL_CONTENT: /* 206 */ return "Partial Content";
205 case HTTP_STATUS_FOUND: /* 302 */ return "Found";
206 case HTTP_STATUS_BAD_REQUEST: /* 400 */ return "Bad Request";
207 case HTTP_STATUS_UNAUTHORIZED: /* 401 */ return "Unauthorized";
208 case HTTP_STATUS_FORBIDDEN: /* 403 */ return "Forbidden";
209 case HTTP_STATUS_NOT_FOUND: /* 404 */ return "Not Found";
210 case HTTP_STATUS_NOT_ALLOWED: /* 405 */ return "Method Not Allowed";
211 case HTTP_STATUS_UNSUPPORTED: /* 415 */ return "Unsupported Media Type";
212 case HTTP_STATUS_BANDWIDTH: /* 453 */ return "Not Enough Bandwidth";
213 case HTTP_STATUS_BAD_SESSION: /* 454 */ return "Session Not Found";
214 case HTTP_STATUS_INTERNAL: /* 500 */ return "Internal Server Error";
215 case HTTP_STATUS_HTTP_VERSION: /* 505 */ return "HTTP/RTSP Version Not Supported";
216 default:
217 return "Unknown Code";
218 break;
219 }
220 }
221
222 static const char *cachedays[7] = {
223 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
224 };
225
226 static const char *cachemonths[12] = {
227 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov",
228 "Dec"
229 };
230
231 /**
232 * Digest authentication helpers
233 */
234 typedef struct http_nonce {
235 RB_ENTRY(http_nonce) link;
236 mtimer_t expire;
237 char nonce[MD5_DIGEST_LENGTH*2+1];
238 } http_nonce_t;
239
240 static RB_HEAD(, http_nonce) http_nonces;
241
242 static int
http_nonce_cmp(const void * a,const void * b)243 http_nonce_cmp(const void *a, const void *b)
244 {
245 return strcmp(((http_nonce_t *)a)->nonce, ((http_nonce_t *)b)->nonce);
246 }
247
248 static void
http_nonce_timeout(void * aux)249 http_nonce_timeout(void *aux)
250 {
251 struct http_nonce *n = aux;
252 RB_REMOVE(&http_nonces, n, link);
253 free(n);
254 }
255
256 static char *
http_get_nonce(void)257 http_get_nonce(void)
258 {
259 struct http_nonce *n = calloc(1, sizeof(*n));
260 char stamp[33], *m;
261 int64_t mono;
262
263 while (1) {
264 mono = getmonoclock();
265 mono ^= 0xa1687211885fcd30LL;
266 snprintf(stamp, sizeof(stamp), "%"PRId64, mono);
267 m = md5sum(stamp, 1);
268 strlcpy(n->nonce, m, sizeof(stamp));
269 pthread_mutex_lock(&global_lock);
270 if (RB_INSERT_SORTED(&http_nonces, n, link, http_nonce_cmp)) {
271 pthread_mutex_unlock(&global_lock);
272 free(m);
273 continue; /* get unique md5 */
274 }
275 mtimer_arm_rel(&n->expire, http_nonce_timeout, n, sec2mono(30));
276 pthread_mutex_unlock(&global_lock);
277 break;
278 }
279 return m;
280 }
281
282 static int
http_nonce_exists(const char * nonce)283 http_nonce_exists(const char *nonce)
284 {
285 struct http_nonce *n, tmp;
286
287 if (nonce == NULL)
288 return 0;
289 strlcpy(tmp.nonce, nonce, sizeof(tmp.nonce));
290 pthread_mutex_lock(&global_lock);
291 n = RB_FIND(&http_nonces, &tmp, link, http_nonce_cmp);
292 if (n) {
293 mtimer_arm_rel(&n->expire, http_nonce_timeout, n, sec2mono(2*60));
294 pthread_mutex_unlock(&global_lock);
295 return 1;
296 }
297 pthread_mutex_unlock(&global_lock);
298 return 0;
299 }
300
301 static char *
http_get_opaque(const char * realm,const char * nonce)302 http_get_opaque(const char *realm, const char *nonce)
303 {
304 char *a = alloca(strlen(realm) + strlen(nonce) + 1);
305 strcpy(a, realm);
306 strcat(a, nonce);
307 return md5sum(a, 1);
308 }
309
310 /**
311 * Transmit a HTTP reply
312 */
313 void
http_send_header(http_connection_t * hc,int rc,const char * content,int64_t contentlen,const char * encoding,const char * location,int maxage,const char * range,const char * disposition,http_arg_list_t * args)314 http_send_header(http_connection_t *hc, int rc, const char *content,
315 int64_t contentlen,
316 const char *encoding, const char *location,
317 int maxage, const char *range,
318 const char *disposition,
319 http_arg_list_t *args)
320 {
321 struct tm tm0, *tm;
322 htsbuf_queue_t hdrs;
323 http_arg_t *ra;
324 time_t t;
325 int sess = 0;
326
327 assert(atomic_get(&hc->hc_extra_insend) > 0);
328
329 htsbuf_queue_init(&hdrs, 0);
330
331 htsbuf_qprintf(&hdrs, "%s %d %s\r\n",
332 http_ver2str(hc->hc_version), rc, http_rc2str(rc));
333
334 if (hc->hc_version != RTSP_VERSION_1_0){
335 htsbuf_append_str(&hdrs, "Server: HTS/tvheadend\r\n");
336 if (config.cors_origin && config.cors_origin[0]) {
337 htsbuf_qprintf(&hdrs, "Access-Control-Allow-Origin: %s\r\n", config.cors_origin);
338 htsbuf_append_str(&hdrs, "Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n");
339 htsbuf_append_str(&hdrs, "Access-Control-Allow-Headers: x-requested-with,authorization\r\n");
340 }
341 }
342
343 if(maxage == 0) {
344 if (hc->hc_version != RTSP_VERSION_1_0)
345 htsbuf_append_str(&hdrs, "Cache-Control: no-cache\r\n");
346 } else if (maxage > 0) {
347 time(&t);
348
349 tm = gmtime_r(&t, &tm0);
350 htsbuf_qprintf(&hdrs,
351 "Last-Modified: %s, %d %s %02d %02d:%02d:%02d GMT\r\n",
352 cachedays[tm->tm_wday], tm->tm_mday,
353 cachemonths[tm->tm_mon], tm->tm_year + 1900,
354 tm->tm_hour, tm->tm_min, tm->tm_sec);
355
356 t += maxage;
357
358 tm = gmtime_r(&t, &tm0);
359 htsbuf_qprintf(&hdrs,
360 "Expires: %s, %d %s %02d %02d:%02d:%02d GMT\r\n",
361 cachedays[tm->tm_wday], tm->tm_mday,
362 cachemonths[tm->tm_mon], tm->tm_year + 1900,
363 tm->tm_hour, tm->tm_min, tm->tm_sec);
364
365 htsbuf_qprintf(&hdrs, "Cache-Control: max-age=%d\r\n", maxage);
366 }
367
368 if(rc == HTTP_STATUS_UNAUTHORIZED) {
369 const char *realm = tvh_str_default(config.realm, "tvheadend");
370 if (config.digest) {
371 if (hc->hc_nonce == NULL)
372 hc->hc_nonce = http_get_nonce();
373 char *opaque = http_get_opaque(realm, hc->hc_nonce);
374 htsbuf_append_str(&hdrs, "WWW-Authenticate: Digest realm=\"");
375 htsbuf_append_str(&hdrs, realm);
376 htsbuf_append_str(&hdrs, "\", qop=\"auth\", nonce=\"");
377 htsbuf_append_str(&hdrs, hc->hc_nonce);
378 htsbuf_append_str(&hdrs, "\", opaque=\"");
379 htsbuf_append_str(&hdrs, opaque);
380 htsbuf_append_str(&hdrs, "\"\r\n");
381 free(opaque);
382 } else {
383 htsbuf_append_str(&hdrs, "WWW-Authenticate: Basic realm=\"");
384 htsbuf_append_str(&hdrs, realm);
385 htsbuf_append_str(&hdrs, "\"\r\n");
386 }
387 }
388
389 if (hc->hc_version != RTSP_VERSION_1_0)
390 htsbuf_qprintf(&hdrs, "Connection: %s\r\n",
391 hc->hc_keep_alive ? "Keep-Alive" : "Close");
392
393 if(encoding != NULL)
394 htsbuf_qprintf(&hdrs, "Content-Encoding: %s\r\n", encoding);
395
396 if(location != NULL)
397 htsbuf_qprintf(&hdrs, "Location: %s\r\n", location);
398
399 if(content != NULL)
400 htsbuf_qprintf(&hdrs, "Content-Type: %s\r\n", content);
401
402 if(contentlen > 0)
403 htsbuf_qprintf(&hdrs, "Content-Length: %"PRId64"\r\n", contentlen);
404 else if(contentlen == INT64_MIN)
405 htsbuf_append_str(&hdrs, "Content-Length: 0\r\n");
406
407 if(range) {
408 htsbuf_qprintf(&hdrs, "Accept-Ranges: %s\r\n", "bytes");
409 htsbuf_qprintf(&hdrs, "Content-Range: %s\r\n", range);
410 }
411
412 if(disposition != NULL)
413 htsbuf_qprintf(&hdrs, "Content-Disposition: %s\r\n", disposition);
414
415 if(hc->hc_cseq) {
416 htsbuf_qprintf(&hdrs, "CSeq: %"PRIu64"\r\n", hc->hc_cseq);
417 if (++hc->hc_cseq == 0)
418 hc->hc_cseq = 1;
419 }
420
421 if (args) {
422 TAILQ_FOREACH(ra, args, link) {
423 if (strcmp(ra->key, "Session") == 0)
424 sess = 1;
425 htsbuf_qprintf(&hdrs, "%s: %s\r\n", ra->key, ra->val ?: "");
426 }
427 }
428 if(hc->hc_session && !sess)
429 htsbuf_qprintf(&hdrs, "Session: %s\r\n", hc->hc_session);
430
431 htsbuf_append_str(&hdrs, "\r\n");
432
433 tcp_write_queue(hc->hc_fd, &hdrs);
434 }
435
436 /*
437 *
438 */
439 int
http_encoding_valid(http_connection_t * hc,const char * encoding)440 http_encoding_valid(http_connection_t *hc, const char *encoding)
441 {
442 const char *accept;
443 char *tokbuf, *tok, *saveptr = NULL, *q, *s;
444
445 accept = http_arg_get(&hc->hc_args, "accept-encoding");
446 if (!accept)
447 return 0;
448
449 tokbuf = tvh_strdupa(accept);
450 tok = strtok_r(tokbuf, ",", &saveptr);
451 while (tok) {
452 while (*tok == ' ')
453 tok++;
454 // check for semicolon
455 if ((q = strchr(tok, ';')) != NULL) {
456 *q = '\0';
457 q++;
458 while (*q == ' ')
459 q++;
460 }
461 if ((s = strchr(tok, ' ')) != NULL)
462 *s = '\0';
463 // check for matching encoding with q > 0
464 if ((!strcasecmp(tok, encoding) || !strcmp(tok, "*")) && (q == NULL || strncmp(q, "q=0.000", strlen(q))))
465 return 1;
466 tok = strtok_r(NULL, ",", &saveptr);
467 }
468 return 0;
469 }
470
471 /**
472 *
473 */
474 static char *
http_get_header_value(const char * hdr,const char * name)475 http_get_header_value(const char *hdr, const char *name)
476 {
477 char *s, *start, *val;
478 s = tvh_strdupa(hdr);
479 while (*s) {
480 while (*s && *s <= ' ') s++;
481 start = s;
482 while (*s && *s != '=') s++;
483 if (*s == '=') {
484 *s = '\0';
485 s++;
486 }
487 while (*s && *s <= ' ') s++;
488 if (*s == '"') {
489 val = ++s;
490 while (*s && *s != '"') s++;
491 if (*s == '"') {
492 *s = '\0';
493 s++;
494 }
495 while (*s && (*s <= ' ' || *s == ',')) s++;
496 } else {
497 val = s;
498 while (*s && *s != ',') s++;
499 *s = '\0';
500 s++;
501 }
502 if (*start && strcmp(name, start) == 0)
503 return strdup(val);
504 }
505 return NULL;
506 }
507
508 /**
509 * Transmit a HTTP reply
510 */
511 static void
http_send_reply(http_connection_t * hc,int rc,const char * content,const char * encoding,const char * location,int maxage)512 http_send_reply(http_connection_t *hc, int rc, const char *content,
513 const char *encoding, const char *location, int maxage)
514 {
515 size_t size = hc->hc_reply.hq_size;
516 uint8_t *data = NULL;
517
518 #if ENABLE_ZLIB
519 if (http_encoding_valid(hc, "gzip") && encoding == NULL && size > 256) {
520 uint8_t *data2 = (uint8_t *)htsbuf_to_string(&hc->hc_reply);
521 data = tvh_gzip_deflate(data2, size, &size);
522 free(data2);
523 encoding = "gzip";
524 }
525 #endif
526
527 http_send_begin(hc);
528 http_send_header(hc, rc, content, size,
529 encoding, location, maxage, 0, NULL, NULL);
530
531 if(!hc->hc_no_output) {
532 if (data == NULL)
533 tcp_write_queue(hc->hc_fd, &hc->hc_reply);
534 else
535 tvh_write(hc->hc_fd, data, size);
536 }
537 http_send_end(hc);
538
539 free(data);
540 }
541
542
543 /**
544 * Send HTTP error back
545 */
546 void
http_error(http_connection_t * hc,int error)547 http_error(http_connection_t *hc, int error)
548 {
549 const char *errtxt = http_rc2str(error);
550 const char *lang;
551 int level;
552
553 if (!atomic_get(&http_server_running)) return;
554
555 if (error != HTTP_STATUS_FOUND && error != HTTP_STATUS_MOVED) {
556 level = LOG_INFO;
557 if (error == HTTP_STATUS_UNAUTHORIZED)
558 level = LOG_DEBUG;
559 else if (error == HTTP_STATUS_BAD_REQUEST || error > HTTP_STATUS_UNAUTHORIZED)
560 level = LOG_ERR;
561 tvhlog(level, LS_HTTP, "%s: %s %s %s -- %d",
562 hc->hc_peer_ipstr, http_ver2str(hc->hc_version),
563 http_cmd2str(hc->hc_cmd), hc->hc_url, error);
564 }
565
566 if (hc->hc_version != RTSP_VERSION_1_0) {
567 htsbuf_queue_flush(&hc->hc_reply);
568
569 htsbuf_qprintf(&hc->hc_reply,
570 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
571 "<HTML><HEAD>\r\n"
572 "<TITLE>%d %s</TITLE>\r\n"
573 "</HEAD><BODY>\r\n"
574 "<H1>%d %s</H1>\r\n",
575 error, errtxt, error, errtxt);
576
577 if (error == HTTP_STATUS_UNAUTHORIZED) {
578 lang = tvh_gettext_get_lang(hc->hc_access ? hc->hc_access->aa_lang_ui : NULL);
579 htsbuf_qprintf(&hc->hc_reply, "<P STYLE=\"text-align: center; margin: 2em\"><A HREF=\"%s/\" STYLE=\"border: 1px solid; border-radius: 4px; padding: .6em\">%s</A></P>",
580 tvheadend_webroot ? tvheadend_webroot : "",
581 tvh_gettext_lang(lang, N_("Default login")));
582 htsbuf_qprintf(&hc->hc_reply, "<P STYLE=\"text-align: center; margin: 2em\"><A HREF=\"%s/login\" STYLE=\"border: 1px solid; border-radius: 4px; padding: .6em\">%s</A></P>",
583 tvheadend_webroot ? tvheadend_webroot : "",
584 tvh_gettext_lang(lang, N_("New login")));
585 }
586
587 htsbuf_append_str(&hc->hc_reply, "</BODY></HTML>\r\n");
588
589 http_send_reply(hc, error, "text/html", NULL, NULL, 0);
590 } else {
591 http_send_reply(hc, error, NULL, NULL, NULL, 0);
592 }
593 }
594
595
596 /**
597 * Send an HTTP OK, simple version for text/html
598 */
599 void
http_output_html(http_connection_t * hc)600 http_output_html(http_connection_t *hc)
601 {
602 return http_send_reply(hc, HTTP_STATUS_OK, "text/html; charset=UTF-8",
603 NULL, NULL, 0);
604 }
605
606 /**
607 * Send an HTTP OK, simple version for text/html
608 */
609 void
http_output_content(http_connection_t * hc,const char * content)610 http_output_content(http_connection_t *hc, const char *content)
611 {
612 return http_send_reply(hc, HTTP_STATUS_OK, content, NULL, NULL, 0);
613 }
614
615
616
617 /**
618 * Send an HTTP REDIRECT
619 */
620 void
http_redirect(http_connection_t * hc,const char * location,http_arg_list_t * req_args,int external)621 http_redirect(http_connection_t *hc, const char *location,
622 http_arg_list_t *req_args, int external)
623 {
624 const char *loc = location;
625 htsbuf_queue_flush(&hc->hc_reply);
626
627 if (req_args) {
628 http_arg_t *ra;
629 htsbuf_queue_t hq;
630 int first = 1;
631 htsbuf_queue_init(&hq, 0);
632 if (!external && tvheadend_webroot && location[0] == '/')
633 htsbuf_append_str(&hq, tvheadend_webroot);
634 htsbuf_append_str(&hq, location);
635 TAILQ_FOREACH(ra, req_args, link) {
636 if (!first)
637 htsbuf_append(&hq, "&", 1);
638 else
639 htsbuf_append(&hq, "?", 1);
640 first = 0;
641 htsbuf_append_and_escape_url(&hq, ra->key);
642 if (ra->val) {
643 htsbuf_append(&hq, "=", 1);
644 htsbuf_append_and_escape_url(&hq, ra->val);
645 }
646 }
647 loc = htsbuf_to_string(&hq);
648 htsbuf_queue_flush(&hq);
649 } else if (!external && tvheadend_webroot && location[0] == '/') {
650 loc = malloc(strlen(location) + strlen(tvheadend_webroot) + 1);
651 strcpy((char *)loc, tvheadend_webroot);
652 strcat((char *)loc, location);
653 }
654
655 htsbuf_qprintf(&hc->hc_reply,
656 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
657 "<HTML><HEAD>\r\n"
658 "<TITLE>Redirect</TITLE>\r\n"
659 "</HEAD><BODY>\r\n"
660 "Please follow <a href=\"%s\">%s</a>\r\n"
661 "</BODY></HTML>\r\n",
662 loc, loc);
663
664 http_send_reply(hc, HTTP_STATUS_FOUND, "text/html", NULL, loc, 0);
665
666 if (loc != location)
667 free((char *)loc);
668 }
669
670
671 /**
672 * Send an CSS @import
673 */
674 void
http_css_import(http_connection_t * hc,const char * location)675 http_css_import(http_connection_t *hc, const char *location)
676 {
677 const char *loc = location;
678
679 htsbuf_queue_flush(&hc->hc_reply);
680
681 if (tvheadend_webroot && location[0] == '/') {
682 loc = alloca(strlen(location) + strlen(tvheadend_webroot) + 1);
683 strcpy((char *)loc, tvheadend_webroot);
684 strcat((char *)loc, location);
685 }
686
687 htsbuf_qprintf(&hc->hc_reply, "@import url('%s');\r\n", loc);
688
689 http_send_reply(hc, HTTP_STATUS_OK, "text/css", NULL, loc, 0);
690 }
691
692 /**
693 *
694 */
695 void
http_extra_destroy(http_connection_t * hc)696 http_extra_destroy(http_connection_t *hc)
697 {
698 htsbuf_data_t *hd, *hd_next;
699
700 pthread_mutex_lock(&hc->hc_extra_lock);
701 for (hd = TAILQ_FIRST(&hc->hc_extra.hq_q); hd; hd = hd_next) {
702 hd_next = TAILQ_NEXT(hd, hd_link);
703 if (hd->hd_data_off <= 0) {
704 htsbuf_data_free(&hc->hc_extra, hd);
705 atomic_dec(&hc->hc_extra_chunks, 1);
706 }
707 }
708 pthread_mutex_unlock(&hc->hc_extra_lock);
709 }
710
711 /**
712 *
713 */
714 int
http_extra_flush(http_connection_t * hc)715 http_extra_flush(http_connection_t *hc)
716 {
717 htsbuf_data_t *hd;
718 int r, serr = 0;
719
720 if (atomic_add(&hc->hc_extra_insend, 1) != 0)
721 goto fin;
722
723 while (1) {
724 r = -1;
725 serr = 0;
726 pthread_mutex_lock(&hc->hc_extra_lock);
727 if (atomic_add(&hc->hc_extra_insend, 0) != 1)
728 goto unlock;
729 hd = TAILQ_FIRST(&hc->hc_extra.hq_q);
730 if (hd == NULL)
731 goto unlock;
732 do {
733 r = send(hc->hc_fd, hd->hd_data + hd->hd_data_off,
734 hd->hd_data_size - hd->hd_data_off,
735 MSG_DONTWAIT | (TAILQ_NEXT(hd, hd_link) ? MSG_MORE : 0));
736 serr = errno;
737 } while (r < 0 && serr == EINTR);
738 if (r > 0 && r + hd->hd_data_off >= hd->hd_data_size) {
739 atomic_dec(&hc->hc_extra_chunks, 1);
740 htsbuf_data_free(&hc->hc_extra, hd);
741 } else if (r > 0) {
742 hd->hd_data_off += r;
743 hc->hc_extra.hq_size -= r;
744 }
745 unlock:
746 pthread_mutex_unlock(&hc->hc_extra_lock);
747
748 if (r < 0) {
749 if (ERRNO_AGAIN(serr))
750 serr = 0;
751 break;
752 }
753 }
754
755 fin:
756 atomic_dec(&hc->hc_extra_insend, 1);
757 return serr;
758 }
759
760 /**
761 *
762 */
763 int
http_extra_flush_partial(http_connection_t * hc)764 http_extra_flush_partial(http_connection_t *hc)
765 {
766 htsbuf_data_t *hd;
767 int r = 0;
768 unsigned int off, size;
769 void *data = NULL;
770
771 atomic_add(&hc->hc_extra_insend, 1);
772
773 pthread_mutex_lock(&hc->hc_extra_lock);
774 hd = TAILQ_FIRST(&hc->hc_extra.hq_q);
775 if (hd && hd->hd_data_off > 0) {
776 data = hd->hd_data;
777 hd->hd_data = NULL;
778 off = hd->hd_data_off;
779 size = hd->hd_data_size;
780 atomic_dec(&hc->hc_extra_chunks, 1);
781 htsbuf_data_free(&hc->hc_extra, hd);
782 }
783 pthread_mutex_unlock(&hc->hc_extra_lock);
784 if (data) {
785 r = tvh_write(hc->hc_fd, data + off, size - off);
786 free(data);
787 }
788 atomic_dec(&hc->hc_extra_insend, 1);
789 return r;
790 }
791
792 /**
793 *
794 */
795 int
http_extra_send(http_connection_t * hc,const void * data,size_t data_len,int may_discard)796 http_extra_send(http_connection_t *hc, const void *data,
797 size_t data_len, int may_discard)
798 {
799 uint8_t *b = malloc(data_len);
800 memcpy(b, data, data_len);
801 return http_extra_send_prealloc(hc, b, data_len, may_discard);
802 }
803
804 /**
805 *
806 */
807 int
http_extra_send_prealloc(http_connection_t * hc,const void * data,size_t data_len,int may_discard)808 http_extra_send_prealloc(http_connection_t *hc, const void *data,
809 size_t data_len, int may_discard)
810 {
811 if (data == NULL) return 0;
812 pthread_mutex_lock(&hc->hc_extra_lock);
813 if (!may_discard || hc->hc_extra.hq_size <= 1024*1024) {
814 atomic_add(&hc->hc_extra_chunks, 1);
815 htsbuf_append_prealloc(&hc->hc_extra, data, data_len);
816 } else {
817 free((void *)data);
818 }
819 pthread_mutex_unlock(&hc->hc_extra_lock);
820 return http_extra_flush(hc);
821 }
822
823 /**
824 *
825 */
826 char *
http_get_hostpath(http_connection_t * hc)827 http_get_hostpath(http_connection_t *hc)
828 {
829 char buf[256];
830 const char *host, *proto;
831
832 host = http_arg_get(&hc->hc_args, "Host") ?:
833 http_arg_get(&hc->hc_args, "X-Forwarded-Host");
834 proto = http_arg_get(&hc->hc_args, "X-Forwarded-Proto");
835
836 snprintf(buf, sizeof(buf), "%s://%s%s",
837 proto ?: "http", host ?: "localhost", tvheadend_webroot ?: "");
838
839 return strdup(buf);
840 }
841
842 /**
843 *
844 */
845 static void
http_access_verify_ticket(http_connection_t * hc)846 http_access_verify_ticket(http_connection_t *hc)
847 {
848 const char *ticket_id;
849
850 if (hc->hc_access)
851 return;
852 ticket_id = http_arg_get(&hc->hc_req_args, "ticket");
853 hc->hc_access = access_ticket_verify2(ticket_id, hc->hc_url);
854 if (hc->hc_access == NULL)
855 return;
856 tvhinfo(LS_HTTP, "%s: using ticket %s for %s",
857 hc->hc_peer_ipstr, ticket_id, hc->hc_url);
858 }
859
860 /**
861 *
862 */
863 struct http_verify_structure {
864 char *password;
865 char *d_ha1;
866 char *d_all;
867 char *d_response;
868 http_connection_t *hc;
869 };
870
871 static int
http_verify_callback(void * aux,const char * passwd)872 http_verify_callback(void *aux, const char *passwd)
873 {
874 struct http_verify_structure *v = aux;
875 char ha1[512];
876 char all[1024];
877 char *m;
878 int res;
879
880 if (v->d_ha1) {
881 snprintf(ha1, sizeof(ha1), "%s:%s", v->d_ha1, passwd);
882 m = md5sum(ha1, 1);
883 snprintf(all, sizeof(all), "%s:%s", m, v->d_all);
884 free(m);
885 m = md5sum(all, 1);
886 res = strcmp(m, v->d_response) == 0;
887 free(m);
888 return res;
889 }
890 if (v->password == NULL) return 0;
891 return strcmp(v->password, passwd) == 0;
892 }
893
894 /**
895 *
896 */
897 static int
http_verify_prepare(http_connection_t * hc,struct http_verify_structure * v)898 http_verify_prepare(http_connection_t *hc, struct http_verify_structure *v)
899 {
900 memset(v, 0, sizeof(*v));
901 if (hc->hc_authhdr) {
902
903 if (hc->hc_nonce == NULL)
904 return -1;
905
906 const char *method = http_cmd2str(hc->hc_cmd);
907 char *response = http_get_header_value(hc->hc_authhdr, "response");
908 char *qop = http_get_header_value(hc->hc_authhdr, "qop");
909 char *uri = http_get_header_value(hc->hc_authhdr, "uri");
910 char *realm = NULL, *nonce_count = NULL, *cnonce = NULL, *m = NULL;
911 char all[1024];
912 int res = -1;
913
914 if (qop == NULL || uri == NULL)
915 goto end;
916
917 if (strcasecmp(qop, "auth-int") == 0) {
918 m = md5sum(hc->hc_post_data ?: "", 1);
919 snprintf(all, sizeof(all), "%s:%s:%s", method, uri, m);
920 free(m);
921 m = NULL;
922 } else if (strcasecmp(qop, "auth") == 0) {
923 snprintf(all, sizeof(all), "%s:%s", method, uri);
924 } else {
925 goto end;
926 }
927
928 m = md5sum(all, 1);
929 if (tvh_str_default(qop, NULL) == NULL) {
930 snprintf(all, sizeof(all), "%s:%s", hc->hc_nonce, m);
931 goto set;
932 } else {
933 realm = http_get_header_value(hc->hc_authhdr, "realm");
934 nonce_count = http_get_header_value(hc->hc_authhdr, "nc");
935 cnonce = http_get_header_value(hc->hc_authhdr, "cnonce");
936 if (realm == NULL || nonce_count == NULL || cnonce == NULL) {
937 goto end;
938 } else {
939 snprintf(all, sizeof(all), "%s:%s:%s:%s:%s",
940 hc->hc_nonce, nonce_count, cnonce, qop, m);
941 set:
942 v->d_all = strdup(all);
943 snprintf(all, sizeof(all), "%s:%s", hc->hc_username, realm);
944 v->d_ha1 = strdup(all);
945 v->d_response = response;
946 v->hc = hc;
947 response = NULL;
948 }
949 }
950 res = 0;
951 end:
952 free(response);
953 free(qop);
954 free(uri);
955 free(realm);
956 free(nonce_count);
957 free(cnonce);
958 free(m);
959 return res;
960 } else {
961 v->password = hc->hc_password;
962 }
963 return 0;
964 }
965
966 /*
967 *
968 */
969 static void
http_verify_free(struct http_verify_structure * v)970 http_verify_free(struct http_verify_structure *v)
971 {
972 free(v->d_ha1);
973 free(v->d_all);
974 free(v->d_response);
975 }
976
977 /**
978 * Return non-zero if no access
979 */
980 int
http_access_verify(http_connection_t * hc,int mask)981 http_access_verify(http_connection_t *hc, int mask)
982 {
983 struct http_verify_structure v;
984
985 if (hc->hc_access)
986 return access_verify2(hc->hc_access, mask);
987
988 http_access_verify_ticket(hc);
989 if (hc->hc_access && !access_verify2(hc->hc_access, mask))
990 return 0;
991
992 access_destroy(hc->hc_access);
993 if (http_verify_prepare(hc, &v)) {
994 hc->hc_access = NULL;
995 return -1;
996 }
997 hc->hc_access = access_get(hc->hc_peer, hc->hc_username,
998 http_verify_callback, &v);
999 http_verify_free(&v);
1000 if (hc->hc_access)
1001 return access_verify2(hc->hc_access, mask);
1002
1003 return -1;
1004 }
1005
1006 /**
1007 * Return non-zero if no access
1008 */
1009 int
http_access_verify_channel(http_connection_t * hc,int mask,struct channel * ch)1010 http_access_verify_channel(http_connection_t *hc, int mask,
1011 struct channel *ch)
1012 {
1013 assert(ch);
1014
1015 if (http_access_verify(hc, mask))
1016 return -1;
1017
1018 return channel_access(ch, hc->hc_access, 0) ? 0 : -1;
1019 }
1020
1021 /**
1022 * Execute url callback
1023 *
1024 * Returns 1 if we should disconnect
1025 *
1026 */
1027 static int
http_exec(http_connection_t * hc,http_path_t * hp,char * remain)1028 http_exec(http_connection_t *hc, http_path_t *hp, char *remain)
1029 {
1030 int err;
1031
1032 if ((hc->hc_username && hc->hc_username[0] == '\0') ||
1033 http_access_verify(hc, hp->hp_accessmask)) {
1034 if (!hp->hp_no_verification) {
1035 err = HTTP_STATUS_UNAUTHORIZED;
1036 goto destroy;
1037 }
1038 }
1039 err = hp->hp_callback(hc, remain, hp->hp_opaque);
1040 destroy:
1041 access_destroy(hc->hc_access);
1042 hc->hc_access = NULL;
1043
1044 if(err == -1)
1045 return 1;
1046
1047 if(err)
1048 http_error(hc, err);
1049 return 0;
1050 }
1051
1052 /*
1053 * Dump request
1054 */
1055 static void
dump_request(http_connection_t * hc)1056 dump_request(http_connection_t *hc)
1057 {
1058 char buf[2048] = "";
1059 http_arg_t *ra;
1060 int first, ptr = 0;
1061
1062 first = 1;
1063 TAILQ_FOREACH(ra, &hc->hc_req_args, link) {
1064 tvh_strlcatf(buf, sizeof(buf), ptr, first ? "?%s=%s" : "&%s=%s", ra->key, ra->val);
1065 first = 0;
1066 }
1067
1068 first = 1;
1069 TAILQ_FOREACH(ra, &hc->hc_args, link) {
1070 tvh_strlcatf(buf, sizeof(buf), ptr, first ? "{{%s=%s" : ",%s=%s", ra->key, ra->val);
1071 first = 0;
1072 }
1073 if (!first)
1074 tvh_strlcatf(buf, sizeof(buf), ptr, "}}");
1075
1076 tvhtrace(LS_HTTP, "%s %s %s%s", http_ver2str(hc->hc_version),
1077 http_cmd2str(hc->hc_cmd), hc->hc_url, buf);
1078 }
1079
1080 /**
1081 * HTTP GET
1082 */
1083 static int
http_cmd_options(http_connection_t * hc)1084 http_cmd_options(http_connection_t *hc)
1085 {
1086 http_send_begin(hc);
1087 http_send_header(hc, HTTP_STATUS_OK, NULL, INT64_MIN,
1088 NULL, NULL, -1, 0, NULL, NULL);
1089 http_send_end(hc);
1090 return 0;
1091 }
1092
1093 /**
1094 * HTTP GET
1095 */
1096 static int
http_cmd_get(http_connection_t * hc)1097 http_cmd_get(http_connection_t *hc)
1098 {
1099 http_path_t hp;
1100 char *remain;
1101 char *args;
1102
1103 if (tvhtrace_enabled())
1104 dump_request(hc);
1105
1106 if (!http_resolve(hc, &hp, &remain, &args)) {
1107 http_error(hc, HTTP_STATUS_NOT_FOUND);
1108 return 0;
1109 }
1110
1111 if(args != NULL)
1112 http_parse_args(&hc->hc_req_args, args);
1113
1114 return http_exec(hc, &hp, remain);
1115 }
1116
1117
1118
1119
1120
1121 /**
1122 * Initial processing of HTTP POST
1123 *
1124 * Return non-zero if we should disconnect
1125 */
1126 static int
http_cmd_post(http_connection_t * hc,htsbuf_queue_t * spill)1127 http_cmd_post(http_connection_t *hc, htsbuf_queue_t *spill)
1128 {
1129 http_path_t hp;
1130 char *remain, *args, *v;
1131
1132 /* Set keep-alive status */
1133 v = http_arg_get(&hc->hc_args, "Content-Length");
1134 if(v == NULL) {
1135 /* No content length in POST, make us disconnect */
1136 return -1;
1137 }
1138
1139 hc->hc_post_len = atoi(v);
1140 if(hc->hc_post_len > 16 * 1024 * 1024) {
1141 /* Bail out if POST data > 16 Mb */
1142 hc->hc_keep_alive = 0;
1143 return -1;
1144 }
1145
1146 /* Allocate space for data, we add a terminating null char to ease
1147 string processing on the content */
1148
1149 hc->hc_post_data = malloc(hc->hc_post_len + 1);
1150 hc->hc_post_data[hc->hc_post_len] = 0;
1151
1152 if(tcp_read_data(hc->hc_fd, hc->hc_post_data, hc->hc_post_len, spill) < 0)
1153 return -1;
1154
1155 /* Parse content-type */
1156 v = http_arg_get(&hc->hc_args, "Content-Type");
1157 if(v != NULL) {
1158 char *argv[2];
1159 int n = http_tokenize(v, argv, 2, ';');
1160 if(n == 0) {
1161 http_error(hc, HTTP_STATUS_BAD_REQUEST);
1162 return 0;
1163 }
1164
1165 if(!strcmp(argv[0], "application/x-www-form-urlencoded"))
1166 http_parse_args(&hc->hc_req_args, hc->hc_post_data);
1167 }
1168
1169 if (tvhtrace_enabled())
1170 dump_request(hc);
1171
1172 if (!http_resolve(hc, &hp, &remain, &args)) {
1173 http_error(hc, HTTP_STATUS_NOT_FOUND);
1174 return 0;
1175 }
1176 return http_exec(hc, &hp, remain);
1177 }
1178
1179
1180 /**
1181 * Process a HTTP request
1182 */
1183 static int
http_process_request(http_connection_t * hc,htsbuf_queue_t * spill)1184 http_process_request(http_connection_t *hc, htsbuf_queue_t *spill)
1185 {
1186 switch(hc->hc_cmd) {
1187 default:
1188 http_error(hc, HTTP_STATUS_BAD_REQUEST);
1189 return 0;
1190 case HTTP_CMD_OPTIONS:
1191 return http_cmd_options(hc);
1192 case HTTP_CMD_GET:
1193 return http_cmd_get(hc);
1194 case HTTP_CMD_HEAD:
1195 hc->hc_no_output = 1;
1196 return http_cmd_get(hc);
1197 case HTTP_CMD_POST:
1198 return http_cmd_post(hc, spill);
1199 }
1200 }
1201
1202 /**
1203 * Process a request, extract info from headers, dispatch command and
1204 * clean up
1205 */
1206 static int
process_request(http_connection_t * hc,htsbuf_queue_t * spill)1207 process_request(http_connection_t *hc, htsbuf_queue_t *spill)
1208 {
1209 char *v, *argv[2];
1210 int n, rval = -1;
1211 char authbuf[150];
1212
1213 hc->hc_url_orig = tvh_strdupa(hc->hc_url);
1214
1215 v = (config.proxy) ? http_arg_get(&hc->hc_args, "X-Forwarded-For") : NULL;
1216 if (v) {
1217 if (tcp_get_ip_from_str(v, hc->hc_peer) == NULL) {
1218 http_error(hc, HTTP_STATUS_BAD_REQUEST);
1219 return -1;
1220 }
1221 }
1222
1223 tcp_get_str_from_ip(hc->hc_peer, authbuf, sizeof(authbuf));
1224
1225 hc->hc_peer_ipstr = tvh_strdupa(authbuf);
1226 hc->hc_representative = hc->hc_peer_ipstr;
1227 hc->hc_username = NULL;
1228 hc->hc_password = NULL;
1229 hc->hc_authhdr = NULL;
1230 hc->hc_session = NULL;
1231
1232 /* Set keep-alive status */
1233 v = http_arg_get(&hc->hc_args, "connection");
1234
1235 switch(hc->hc_version) {
1236 case RTSP_VERSION_1_0:
1237 hc->hc_keep_alive = 1;
1238 /* Extract CSeq */
1239 if((v = http_arg_get(&hc->hc_args, "CSeq")) != NULL)
1240 hc->hc_cseq = strtoll(v, NULL, 10);
1241 else
1242 hc->hc_cseq = 0;
1243 free(hc->hc_session);
1244 if ((v = http_arg_get(&hc->hc_args, "Session")) != NULL)
1245 hc->hc_session = tvh_strdupa(v);
1246 else
1247 hc->hc_session = NULL;
1248 if(hc->hc_cseq == 0) {
1249 http_error(hc, HTTP_STATUS_BAD_REQUEST);
1250 return -1;
1251 }
1252 break;
1253
1254 case HTTP_VERSION_1_0:
1255 /* Keep-alive is default off, but can be enabled */
1256 hc->hc_keep_alive = v != NULL && !strcasecmp(v, "keep-alive");
1257 break;
1258
1259 case HTTP_VERSION_1_1:
1260 /* Keep-alive is default on, but can be disabled */
1261 hc->hc_keep_alive = !(v != NULL && !strcasecmp(v, "close"));
1262 break;
1263 }
1264
1265 /* Extract authorization */
1266 if((v = http_arg_get(&hc->hc_args, "Authorization")) != NULL) {
1267 if((n = http_tokenize(v, argv, 2, -1)) == 2) {
1268 if (strcasecmp(argv[0], "basic") == 0) {
1269 n = base64_decode((uint8_t *)authbuf, argv[1], sizeof(authbuf) - 1);
1270 if (n < 0)
1271 n = 0;
1272 authbuf[n] = 0;
1273 if((n = http_tokenize(authbuf, argv, 2, ':')) == 2) {
1274 hc->hc_username = tvh_strdupa(argv[0]);
1275 hc->hc_password = tvh_strdupa(argv[1]);
1276 http_deescape(hc->hc_username);
1277 http_deescape(hc->hc_password);
1278 // No way to actually track this
1279 } else {
1280 http_error(hc, HTTP_STATUS_UNAUTHORIZED);
1281 return -1;
1282 }
1283 } else if (strcasecmp(argv[0], "digest") == 0) {
1284 v = http_get_header_value(argv[1], "nonce");
1285 if (v == NULL || !http_nonce_exists(v)) {
1286 free(v);
1287 http_error(hc, HTTP_STATUS_UNAUTHORIZED);
1288 return -1;
1289 }
1290 free(hc->hc_nonce);
1291 hc->hc_nonce = v;
1292 v = http_get_header_value(argv[1], "username");
1293 hc->hc_authhdr = tvh_strdupa(argv[1]);
1294 hc->hc_username = tvh_strdupa(v);
1295 http_deescape(hc->hc_username);
1296 free(v);
1297 } else {
1298 http_error(hc, HTTP_STATUS_BAD_REQUEST);
1299 return -1;
1300 }
1301 }
1302 }
1303
1304 if (hc->hc_username)
1305 hc->hc_representative = hc->hc_username;
1306
1307 switch(hc->hc_version) {
1308 case RTSP_VERSION_1_0:
1309 if (tvhtrace_enabled())
1310 dump_request(hc);
1311 if (hc->hc_cseq)
1312 rval = hc->hc_process(hc, spill);
1313 else
1314 http_error(hc, HTTP_STATUS_HTTP_VERSION);
1315 break;
1316
1317 case HTTP_VERSION_1_0:
1318 case HTTP_VERSION_1_1:
1319 if (!hc->hc_cseq)
1320 rval = hc->hc_process(hc, spill);
1321 else
1322 http_error(hc, HTTP_STATUS_HTTP_VERSION);
1323 break;
1324 }
1325 return rval;
1326 }
1327
1328
1329
1330 /*
1331 * Delete one argument
1332 */
1333 void
http_arg_remove(struct http_arg_list * list,struct http_arg * arg)1334 http_arg_remove(struct http_arg_list *list, struct http_arg *arg)
1335 {
1336 TAILQ_REMOVE(list, arg, link);
1337 free(arg->key);
1338 free(arg->val);
1339 free(arg);
1340 }
1341
1342
1343 /*
1344 * Delete all arguments associated with a connection
1345 */
1346 void
http_arg_flush(struct http_arg_list * list)1347 http_arg_flush(struct http_arg_list *list)
1348 {
1349 http_arg_t *ra;
1350 while((ra = TAILQ_FIRST(list)) != NULL)
1351 http_arg_remove(list, ra);
1352 }
1353
1354
1355 /**
1356 * Find an argument associated with a connection
1357 */
1358 char *
http_arg_get(struct http_arg_list * list,const char * name)1359 http_arg_get(struct http_arg_list *list, const char *name)
1360 {
1361 http_arg_t *ra;
1362 TAILQ_FOREACH(ra, list, link)
1363 if(!strcasecmp(ra->key, name))
1364 return ra->val;
1365 return NULL;
1366 }
1367
1368 /**
1369 * Find an argument associated with a connection and remove it
1370 */
1371 char *
http_arg_get_remove(struct http_arg_list * list,const char * name)1372 http_arg_get_remove(struct http_arg_list *list, const char *name)
1373 {
1374 static __thread char buf[128];
1375 int empty;
1376 http_arg_t *ra;
1377 TAILQ_FOREACH(ra, list, link)
1378 if(!strcasecmp(ra->key, name)) {
1379 TAILQ_REMOVE(list, ra, link);
1380 empty = ra->val == NULL;
1381 if (!empty)
1382 strlcpy(buf, ra->val, sizeof(buf));
1383 free(ra->key);
1384 free(ra->val);
1385 free(ra);
1386 return !empty ? buf : NULL;
1387 }
1388 buf[0] = '\0';
1389 return buf;
1390 }
1391
1392
1393 /**
1394 * Set an argument associated with a connection
1395 */
1396 void
http_arg_set(struct http_arg_list * list,const char * key,const char * val)1397 http_arg_set(struct http_arg_list *list, const char *key, const char *val)
1398 {
1399 http_arg_t *ra;
1400
1401 ra = malloc(sizeof(http_arg_t));
1402 TAILQ_INSERT_TAIL(list, ra, link);
1403 ra->key = strdup(key);
1404 ra->val = val ? strdup(val) : NULL;
1405 }
1406
1407 /*
1408 * Split a string in components delimited by 'delimiter'
1409 */
1410 int
http_tokenize(char * buf,char ** vec,int vecsize,int delimiter)1411 http_tokenize(char *buf, char **vec, int vecsize, int delimiter)
1412 {
1413 int n = 0;
1414
1415 while(1) {
1416 while((*buf > 0 && *buf < 33) || *buf == delimiter)
1417 buf++;
1418 if(*buf == 0)
1419 break;
1420 vec[n++] = buf;
1421 if(n == vecsize)
1422 break;
1423 while(*buf > 32 && *buf != delimiter)
1424 buf++;
1425 if(*buf == 0)
1426 break;
1427 *buf = 0;
1428 buf++;
1429 }
1430 return n;
1431 }
1432
1433
1434 /**
1435 * Add a callback for a given "virtual path" on our HTTP server
1436 */
1437 http_path_t *
http_path_add_modify(const char * path,void * opaque,http_callback_t * callback,uint32_t accessmask,http_path_modify_t * path_modify)1438 http_path_add_modify(const char *path, void *opaque, http_callback_t *callback,
1439 uint32_t accessmask, http_path_modify_t *path_modify)
1440 {
1441 http_path_t *hp = malloc(sizeof(http_path_t));
1442 char *tmp;
1443
1444 if (tvheadend_webroot) {
1445 size_t len = strlen(tvheadend_webroot) + strlen(path) + 1;
1446 hp->hp_path = tmp = malloc(len);
1447 sprintf(tmp, "%s%s", tvheadend_webroot, path);
1448 } else
1449 hp->hp_path = strdup(path);
1450 hp->hp_len = strlen(hp->hp_path);
1451 hp->hp_opaque = opaque;
1452 hp->hp_callback = callback;
1453 hp->hp_accessmask = accessmask;
1454 hp->hp_path_modify = path_modify;
1455 hp->hp_no_verification = 0;
1456 pthread_mutex_lock(&http_paths_mutex);
1457 LIST_INSERT_HEAD(&http_paths, hp, hp_link);
1458 pthread_mutex_unlock(&http_paths_mutex);
1459 return hp;
1460 }
1461
1462 /**
1463 * Add a callback for a given "virtual path" on our HTTP server
1464 */
1465 http_path_t *
http_path_add(const char * path,void * opaque,http_callback_t * callback,uint32_t accessmask)1466 http_path_add(const char *path, void *opaque, http_callback_t *callback,
1467 uint32_t accessmask)
1468 {
1469 return http_path_add_modify(path, opaque, callback, accessmask, NULL);
1470 }
1471
1472 /**
1473 * Parse arguments of a HTTP GET url, not perfect, but works for us
1474 */
1475 void
http_parse_args(http_arg_list_t * list,char * args)1476 http_parse_args(http_arg_list_t *list, char *args)
1477 {
1478 char *k, *v;
1479
1480 if (args && *args == '&')
1481 args++;
1482 while(args) {
1483 k = args;
1484 if((args = strchr(args, '=')) != NULL) {
1485 *args++ = 0;
1486 } else {
1487 args = k;
1488 }
1489
1490 v = args;
1491 args = strchr(args, '&');
1492 if(args != NULL)
1493 *args++ = 0;
1494 else if(v == k) {
1495 if (*k == '\0')
1496 break;
1497 v = NULL;
1498 }
1499
1500 http_deescape(k);
1501 if (v)
1502 http_deescape(v);
1503 // printf("%s = %s\n", k, v);
1504 http_arg_set(list, k, v);
1505 }
1506 }
1507
1508 /**
1509 *
1510 */
1511 void
http_serve_requests(http_connection_t * hc)1512 http_serve_requests(http_connection_t *hc)
1513 {
1514 htsbuf_queue_t spill;
1515 char *argv[3], *c, *s, *cmdline = NULL, *hdrline = NULL;
1516 int n, r, delim;
1517
1518 pthread_mutex_init(&hc->hc_extra_lock, NULL);
1519 http_arg_init(&hc->hc_args);
1520 http_arg_init(&hc->hc_req_args);
1521 htsbuf_queue_init(&spill, 0);
1522 htsbuf_queue_init(&hc->hc_reply, 0);
1523 htsbuf_queue_init(&hc->hc_extra, 0);
1524 atomic_set(&hc->hc_extra_insend, 0);
1525 atomic_set(&hc->hc_extra_chunks, 0);
1526
1527 do {
1528 hc->hc_no_output = 0;
1529
1530 if (cmdline) free(cmdline);
1531
1532 if ((cmdline = tcp_read_line(hc->hc_fd, &spill)) == NULL)
1533 goto error;
1534
1535 /* PROXY Protocol v1 support
1536 * Format: 'PROXY TCP4 192.168.0.1 192.168.0.11 56324 9981\r\n'
1537 * SRC-ADDRESS DST-ADDRESS SPORT DPORT */
1538 if (config.proxy && strncmp(cmdline, "PROXY ", 6) == 0) {
1539 tvhtrace(LS_HTTP, "[PROXY] PROXY protocol detected! cmdline='%s'", cmdline);
1540
1541 argv[0] = cmdline;
1542 s = cmdline + 6;
1543
1544 if ((cmdline = tcp_read_line(hc->hc_fd, &spill)) == NULL)
1545 goto error; /* No more data after the PROXY protocol */
1546
1547 delim = '.';
1548 if (strncmp(s, "TCP6 ", 5) == 0) {
1549 delim = ':';
1550 } else if (strncmp(s, "TCP4 ", 5) != 0) {
1551 goto error;
1552 }
1553
1554 s += 5;
1555
1556 /* Check the SRC-ADDRESS */
1557 for (c = s; *c != ' '; c++) {
1558 if (*c == '\0') goto error; /* Incomplete PROXY format */
1559 if (*c != delim && (*c < '0' || *c > '9')) {
1560 if (delim == ':') {
1561 if (*c >= 'a' && *c <= 'f') continue;
1562 if (*c >= 'A' && *c <= 'F') continue;
1563 }
1564 goto error; /* Not valid IP address */
1565 }
1566 }
1567 if (*c != ' ') goto error;
1568 /* Check length */
1569 if ((c-s) < 7) goto error;
1570 if ((c-s) > (delim == ':' ? 45 : 15)) goto error;
1571
1572 /* Add null terminator */
1573 *c = '\0';
1574
1575 /* Don't care about DST-ADDRESS, SRC-PORT & DST-PORT
1576 All it's OK, push the original client IP */
1577 tvhtrace(LS_HTTP, "[PROXY] Original source='%s'", s);
1578 http_arg_set(&hc->hc_args, "X-Forwarded-For", s);
1579 free(argv[0]);
1580 }
1581
1582 if((n = http_tokenize(cmdline, argv, 3, -1)) != 3)
1583 goto error;
1584
1585 if((hc->hc_cmd = str2val(argv[0], HTTP_cmdtab)) == -1)
1586 goto error;
1587
1588 hc->hc_url = argv[1];
1589 if((hc->hc_version = str2val(argv[2], HTTP_versiontab)) == -1)
1590 goto error;
1591
1592 /* parse header */
1593 while(1) {
1594 if (hdrline) free(hdrline);
1595
1596 if ((hdrline = tcp_read_line(hc->hc_fd, &spill)) == NULL)
1597 goto error;
1598
1599 if(!*hdrline)
1600 break; /* header complete */
1601
1602 if((n = http_tokenize(hdrline, argv, 2, -1)) < 2) {
1603 if ((c = strchr(hdrline, ':')) != NULL) {
1604 *c = '\0';
1605 argv[0] = hdrline;
1606 argv[1] = c + 1;
1607 } else {
1608 continue;
1609 }
1610 } else if((c = strrchr(argv[0], ':')) == NULL)
1611 goto error;
1612
1613 *c = 0;
1614 http_arg_set(&hc->hc_args, argv[0], argv[1]);
1615 }
1616
1617 r = process_request(hc, &spill);
1618
1619 free(hc->hc_post_data);
1620 hc->hc_post_data = NULL;
1621
1622 http_arg_flush(&hc->hc_args);
1623 http_arg_flush(&hc->hc_req_args);
1624
1625 htsbuf_queue_flush(&hc->hc_reply);
1626
1627 if (r)
1628 break;
1629
1630 } while(hc->hc_keep_alive && atomic_get(&http_server_running));
1631
1632 error:
1633 free(hdrline);
1634 free(cmdline);
1635 htsbuf_queue_flush(&spill);
1636 htsbuf_queue_flush(&hc->hc_extra);
1637
1638 free(hc->hc_nonce);
1639 hc->hc_nonce = NULL;
1640
1641 }
1642
1643
1644 /**
1645 *
1646 */
1647 static void
http_serve(int fd,void ** opaque,struct sockaddr_storage * peer,struct sockaddr_storage * self)1648 http_serve(int fd, void **opaque, struct sockaddr_storage *peer,
1649 struct sockaddr_storage *self)
1650 {
1651 http_connection_t hc;
1652
1653 /* Note: global_lock held on entry */
1654 pthread_mutex_unlock(&global_lock);
1655 memset(&hc, 0, sizeof(http_connection_t));
1656 *opaque = &hc;
1657
1658 hc.hc_fd = fd;
1659 hc.hc_peer = peer;
1660 hc.hc_self = self;
1661 hc.hc_paths = &http_paths;
1662 hc.hc_paths_mutex = &http_paths_mutex;
1663 hc.hc_process = http_process_request;
1664
1665 http_serve_requests(&hc);
1666
1667 close(fd);
1668
1669 // Note: leave global_lock held for parent
1670 pthread_mutex_lock(&global_lock);
1671 *opaque = NULL;
1672 }
1673
1674 void
http_cancel(void * opaque)1675 http_cancel( void *opaque )
1676 {
1677 http_connection_t *hc = opaque;
1678
1679 if (hc) {
1680 shutdown(hc->hc_fd, SHUT_RDWR);
1681 hc->hc_shutdown = 1;
1682 }
1683 }
1684
1685 /**
1686 * Fire up HTTP server
1687 */
1688 void
http_server_init(const char * bindaddr)1689 http_server_init(const char *bindaddr)
1690 {
1691 static tcp_server_ops_t ops = {
1692 .start = http_serve,
1693 .stop = NULL,
1694 .cancel = http_cancel
1695 };
1696 RB_INIT(&http_nonces);
1697 http_server = tcp_server_create(LS_HTTP, "HTTP", bindaddr, tvheadend_webui_port, &ops, NULL);
1698 atomic_set(&http_server_running, 1);
1699 }
1700
1701 void
http_server_register(void)1702 http_server_register(void)
1703 {
1704 tcp_server_register(http_server);
1705 }
1706
1707 void
http_server_done(void)1708 http_server_done(void)
1709 {
1710 http_path_t *hp;
1711 http_nonce_t *n;
1712
1713 pthread_mutex_lock(&global_lock);
1714 atomic_set(&http_server_running, 0);
1715 if (http_server)
1716 tcp_server_delete(http_server);
1717 http_server = NULL;
1718 pthread_mutex_lock(&http_paths_mutex);
1719 while ((hp = LIST_FIRST(&http_paths)) != NULL) {
1720 LIST_REMOVE(hp, hp_link);
1721 free((void *)hp->hp_path);
1722 free(hp);
1723 }
1724 pthread_mutex_unlock(&http_paths_mutex);
1725 while ((n = RB_FIRST(&http_nonces)) != NULL) {
1726 mtimer_disarm(&n->expire);
1727 RB_REMOVE(&http_nonces, n, link);
1728 free(n);
1729 }
1730 pthread_mutex_unlock(&global_lock);
1731 }
1732