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