HTTP: Add persistent connection support.
[asterisk/asterisk.git] / main / http.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief http server for AMI access
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * This program implements a tiny http server
26  * and was inspired by micro-httpd by Jef Poskanzer
27  *
28  * GMime http://spruce.sourceforge.net/gmime/
29  *
30  * \ref AstHTTP - AMI over the http protocol
31  */
32
33 /*! \li \ref http.c uses the configuration file \ref http.conf
34  * \addtogroup configuration_file
35  */
36
37 /*! \page http.conf http.conf
38  * \verbinclude http.conf.sample
39  */
40
41 /*** MODULEINFO
42         <support_level>core</support_level>
43  ***/
44
45 #include "asterisk.h"
46
47 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
48
49 #include <time.h>
50 #include <sys/time.h>
51 #include <sys/stat.h>
52 #include <sys/signal.h>
53 #include <fcntl.h>
54
55 #include "asterisk/paths.h"     /* use ast_config_AST_DATA_DIR */
56 #include "asterisk/cli.h"
57 #include "asterisk/tcptls.h"
58 #include "asterisk/http.h"
59 #include "asterisk/utils.h"
60 #include "asterisk/strings.h"
61 #include "asterisk/config.h"
62 #include "asterisk/stringfields.h"
63 #include "asterisk/ast_version.h"
64 #include "asterisk/manager.h"
65 #include "asterisk/_private.h"
66 #include "asterisk/astobj2.h"
67 #include "asterisk/netsock2.h"
68 #include "asterisk/json.h"
69
70 #define MAX_PREFIX 80
71 #define DEFAULT_PORT 8088
72 #define DEFAULT_TLS_PORT 8089
73 #define DEFAULT_SESSION_LIMIT 100
74 /*! (ms) Idle time waiting for data. */
75 #define DEFAULT_SESSION_INACTIVITY 30000
76 /*! (ms) Min timeout for initial HTTP request to start coming in. */
77 #define MIN_INITIAL_REQUEST_TIMEOUT     10000
78 /*! (ms) Idle time between HTTP requests */
79 #define DEFAULT_SESSION_KEEP_ALIVE 15000
80
81 /*! Maximum application/json or application/x-www-form-urlencoded body content length. */
82 #if !defined(LOW_MEMORY)
83 #define MAX_CONTENT_LENGTH 4096
84 #else
85 #define MAX_CONTENT_LENGTH 1024
86 #endif  /* !defined(LOW_MEMORY) */
87
88 /*! Maximum line length for HTTP requests. */
89 #if !defined(LOW_MEMORY)
90 #define MAX_HTTP_LINE_LENGTH 4096
91 #else
92 #define MAX_HTTP_LINE_LENGTH 1024
93 #endif  /* !defined(LOW_MEMORY) */
94
95 static int session_limit = DEFAULT_SESSION_LIMIT;
96 static int session_inactivity = DEFAULT_SESSION_INACTIVITY;
97 static int session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE;
98 static int session_count = 0;
99
100 static struct ast_tls_config http_tls_cfg;
101
102 static void *httpd_helper_thread(void *arg);
103
104 /*!
105  * we have up to two accepting threads, one for http, one for https
106  */
107 static struct ast_tcptls_session_args http_desc = {
108         .accept_fd = -1,
109         .master = AST_PTHREADT_NULL,
110         .tls_cfg = NULL,
111         .poll_timeout = -1,
112         .name = "http server",
113         .accept_fn = ast_tcptls_server_root,
114         .worker_fn = httpd_helper_thread,
115 };
116
117 static struct ast_tcptls_session_args https_desc = {
118         .accept_fd = -1,
119         .master = AST_PTHREADT_NULL,
120         .tls_cfg = &http_tls_cfg,
121         .poll_timeout = -1,
122         .name = "https server",
123         .accept_fn = ast_tcptls_server_root,
124         .worker_fn = httpd_helper_thread,
125 };
126
127 static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri);      /*!< list of supported handlers */
128
129 /* all valid URIs must be prepended by the string in prefix. */
130 static char prefix[MAX_PREFIX];
131 static int enablestatic;
132
133 /*! \brief Limit the kinds of files we're willing to serve up */
134 static struct {
135         const char *ext;
136         const char *mtype;
137 } mimetypes[] = {
138         { "png", "image/png" },
139         { "xml", "text/xml" },
140         { "jpg", "image/jpeg" },
141         { "js", "application/x-javascript" },
142         { "wav", "audio/x-wav" },
143         { "mp3", "audio/mpeg" },
144         { "svg", "image/svg+xml" },
145         { "svgz", "image/svg+xml" },
146         { "gif", "image/gif" },
147         { "html", "text/html" },
148         { "htm", "text/html" },
149         { "css", "text/css" },
150         { "cnf", "text/plain" },
151         { "cfg", "text/plain" },
152         { "bin", "application/octet-stream" },
153         { "sbn", "application/octet-stream" },
154         { "ld", "application/octet-stream" },
155 };
156
157 struct http_uri_redirect {
158         AST_LIST_ENTRY(http_uri_redirect) entry;
159         char *dest;
160         char target[0];
161 };
162
163 static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
164
165 static const struct ast_cfhttp_methods_text {
166         enum ast_http_method method;
167         const char *text;
168 } ast_http_methods_text[] = {
169         { AST_HTTP_UNKNOWN,     "UNKNOWN" },
170         { AST_HTTP_GET,         "GET" },
171         { AST_HTTP_POST,        "POST" },
172         { AST_HTTP_HEAD,        "HEAD" },
173         { AST_HTTP_PUT,         "PUT" },
174         { AST_HTTP_DELETE,      "DELETE" },
175         { AST_HTTP_OPTIONS,     "OPTIONS" },
176 };
177
178 const char *ast_get_http_method(enum ast_http_method method)
179 {
180         int x;
181
182         for (x = 0; x < ARRAY_LEN(ast_http_methods_text); x++) {
183                 if (ast_http_methods_text[x].method == method) {
184                         return ast_http_methods_text[x].text;
185                 }
186         }
187
188         return NULL;
189 }
190
191 const char *ast_http_ftype2mtype(const char *ftype)
192 {
193         int x;
194
195         if (ftype) {
196                 for (x = 0; x < ARRAY_LEN(mimetypes); x++) {
197                         if (!strcasecmp(ftype, mimetypes[x].ext)) {
198                                 return mimetypes[x].mtype;
199                         }
200                 }
201         }
202         return NULL;
203 }
204
205 uint32_t ast_http_manid_from_vars(struct ast_variable *headers)
206 {
207         uint32_t mngid = 0;
208         struct ast_variable *v, *cookies;
209
210         cookies = ast_http_get_cookies(headers);
211         for (v = cookies; v; v = v->next) {
212                 if (!strcasecmp(v->name, "mansession_id")) {
213                         sscanf(v->value, "%30x", &mngid);
214                         break;
215                 }
216         }
217         ast_variables_destroy(cookies);
218         return mngid;
219 }
220
221 void ast_http_prefix(char *buf, int len)
222 {
223         if (buf) {
224                 ast_copy_string(buf, prefix, len);
225         }
226 }
227
228 static int static_callback(struct ast_tcptls_session_instance *ser,
229         const struct ast_http_uri *urih, const char *uri,
230         enum ast_http_method method, struct ast_variable *get_vars,
231         struct ast_variable *headers)
232 {
233         char *path;
234         const char *ftype;
235         const char *mtype;
236         char wkspace[80];
237         struct stat st;
238         int len;
239         int fd;
240         struct ast_str *http_header;
241         struct timeval tv;
242         struct ast_tm tm;
243         char timebuf[80], etag[23];
244         struct ast_variable *v;
245         int not_modified = 0;
246
247         if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
248                 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
249                 return 0;
250         }
251
252         /* Yuck.  I'm not really sold on this, but if you don't deliver static content it makes your configuration
253            substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
254         if (!enablestatic || ast_strlen_zero(uri)) {
255                 goto out403;
256         }
257
258         /* Disallow any funny filenames at all (checking first character only??) */
259         if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0])) {
260                 goto out403;
261         }
262
263         if (strstr(uri, "/..")) {
264                 goto out403;
265         }
266
267         if ((ftype = strrchr(uri, '.'))) {
268                 ftype++;
269         }
270
271         if (!(mtype = ast_http_ftype2mtype(ftype))) {
272                 snprintf(wkspace, sizeof(wkspace), "text/%s", S_OR(ftype, "plain"));
273                 mtype = wkspace;
274         }
275
276         /* Cap maximum length */
277         if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
278                 goto out403;
279         }
280
281         path = ast_alloca(len);
282         sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
283         if (stat(path, &st)) {
284                 goto out404;
285         }
286
287         if (S_ISDIR(st.st_mode)) {
288                 goto out404;
289         }
290
291         if (strstr(path, "/private/") && !astman_is_authed(ast_http_manid_from_vars(headers))) {
292                 goto out403;
293         }
294
295         fd = open(path, O_RDONLY);
296         if (fd < 0) {
297                 goto out403;
298         }
299
300         /* make "Etag:" http header value */
301         snprintf(etag, sizeof(etag), "\"%ld\"", (long)st.st_mtime);
302
303         /* make "Last-Modified:" http header value */
304         tv.tv_sec = st.st_mtime;
305         tv.tv_usec = 0;
306         ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&tv, &tm, "GMT"));
307
308         /* check received "If-None-Match" request header and Etag value for file */
309         for (v = headers; v; v = v->next) {
310                 if (!strcasecmp(v->name, "If-None-Match")) {
311                         if (!strcasecmp(v->value, etag)) {
312                                 not_modified = 1;
313                         }
314                         break;
315                 }
316         }
317
318         http_header = ast_str_create(255);
319         if (!http_header) {
320                 ast_http_request_close_on_completion(ser);
321                 ast_http_error(ser, 500, "Server Error", "Out of memory");
322                 close(fd);
323                 return 0;
324         }
325
326         ast_str_set(&http_header, 0, "Content-type: %s\r\n"
327                 "ETag: %s\r\n"
328                 "Last-Modified: %s\r\n",
329                 mtype,
330                 etag,
331                 timebuf);
332
333         /* ast_http_send() frees http_header, so we don't need to do it before returning */
334         if (not_modified) {
335                 ast_http_send(ser, method, 304, "Not Modified", http_header, NULL, 0, 1);
336         } else {
337                 ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 1); /* static content flag is set */
338         }
339         close(fd);
340         return 0;
341
342 out404:
343         ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
344         return 0;
345
346 out403:
347         ast_http_request_close_on_completion(ser);
348         ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL.");
349         return 0;
350 }
351
352 static int httpstatus_callback(struct ast_tcptls_session_instance *ser,
353         const struct ast_http_uri *urih, const char *uri,
354         enum ast_http_method method, struct ast_variable *get_vars,
355         struct ast_variable *headers)
356 {
357         struct ast_str *out;
358         struct ast_variable *v, *cookies = NULL;
359
360         if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
361                 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
362                 return 0;
363         }
364
365         out = ast_str_create(512);
366         if (!out) {
367                 ast_http_request_close_on_completion(ser);
368                 ast_http_error(ser, 500, "Server Error", "Out of memory");
369                 return 0;
370         }
371
372         ast_str_append(&out, 0,
373                 "<title>Asterisk HTTP Status</title>\r\n"
374                 "<body bgcolor=\"#ffffff\">\r\n"
375                 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
376                 "<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
377
378         ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
379         ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
380                        ast_sockaddr_stringify_addr(&http_desc.old_address));
381         ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%s</b></td></tr>\r\n",
382                        ast_sockaddr_stringify_port(&http_desc.old_address));
383         if (http_tls_cfg.enabled) {
384                 ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%s</b></td></tr>\r\n",
385                                ast_sockaddr_stringify_port(&https_desc.old_address));
386         }
387         ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
388         for (v = get_vars; v; v = v->next) {
389                 ast_str_append(&out, 0, "<tr><td><i>Submitted GET Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
390         }
391         ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
392
393         cookies = ast_http_get_cookies(headers);
394         for (v = cookies; v; v = v->next) {
395                 ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
396         }
397         ast_variables_destroy(cookies);
398
399         ast_str_append(&out, 0, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
400         ast_http_send(ser, method, 200, NULL, NULL, out, 0, 0);
401         return 0;
402 }
403
404 static struct ast_http_uri statusuri = {
405         .callback = httpstatus_callback,
406         .description = "Asterisk HTTP General Status",
407         .uri = "httpstatus",
408         .has_subtree = 0,
409         .data = NULL,
410         .key = __FILE__,
411 };
412
413 static struct ast_http_uri staticuri = {
414         .callback = static_callback,
415         .description = "Asterisk HTTP Static Delivery",
416         .uri = "static",
417         .has_subtree = 1,
418         .data = NULL,
419         .key= __FILE__,
420 };
421
422 enum http_private_flags {
423         /*! TRUE if the HTTP request has a body. */
424         HTTP_FLAG_HAS_BODY = (1 << 0),
425         /*! TRUE if the HTTP request body has been read. */
426         HTTP_FLAG_BODY_READ = (1 << 1),
427         /*! TRUE if the HTTP request must close when completed. */
428         HTTP_FLAG_CLOSE_ON_COMPLETION = (1 << 2),
429 };
430
431 /*! HTTP tcptls worker_fn private data. */
432 struct http_worker_private_data {
433         /*! Body length or -1 if chunked.  Valid if HTTP_FLAG_HAS_BODY is TRUE. */
434         int body_length;
435         /*! HTTP body tracking flags */
436         struct ast_flags flags;
437 };
438
439 void ast_http_send(struct ast_tcptls_session_instance *ser,
440         enum ast_http_method method, int status_code, const char *status_title,
441         struct ast_str *http_header, struct ast_str *out, int fd,
442         unsigned int static_content)
443 {
444         struct timeval now = ast_tvnow();
445         struct ast_tm tm;
446         char timebuf[80];
447         int content_length = 0;
448         int close_connection;
449
450         if (!ser || !ser->f) {
451                 /* The connection is not open. */
452                 ast_free(http_header);
453                 ast_free(out);
454                 return;
455         }
456
457         /*
458          * We shouldn't be sending non-final status codes to this
459          * function because we may close the connection before
460          * returning.
461          */
462         ast_assert(200 <= status_code);
463
464         if (session_keep_alive <= 0) {
465                 close_connection = 1;
466         } else {
467                 struct http_worker_private_data *request;
468
469                 request = ser->private_data;
470                 if (!request
471                         || ast_test_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION)
472                         || ast_http_body_discard(ser)) {
473                         close_connection = 1;
474                 } else {
475                         close_connection = 0;
476                 }
477         }
478
479         ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&now, &tm, "GMT"));
480
481         /* calc content length */
482         if (out) {
483                 content_length += ast_str_strlen(out);
484         }
485
486         if (fd) {
487                 content_length += lseek(fd, 0, SEEK_END);
488                 lseek(fd, 0, SEEK_SET);
489         }
490
491         /* send http header */
492         fprintf(ser->f,
493                 "HTTP/1.1 %d %s\r\n"
494                 "Server: Asterisk/%s\r\n"
495                 "Date: %s\r\n"
496                 "%s"
497                 "%s"
498                 "%s"
499                 "Content-Length: %d\r\n"
500                 "\r\n",
501                 status_code, status_title ? status_title : "OK",
502                 ast_get_version(),
503                 timebuf,
504                 close_connection ? "Connection: close\r\n" : "",
505                 static_content ? "" : "Cache-Control: no-cache, no-store\r\n",
506                 http_header ? ast_str_buffer(http_header) : "",
507                 content_length
508                 );
509
510         /* send content */
511         if (method != AST_HTTP_HEAD || status_code >= 400) {
512                 if (out && ast_str_strlen(out)) {
513                         if (fwrite(ast_str_buffer(out), ast_str_strlen(out), 1, ser->f) != 1) {
514                                 ast_log(LOG_ERROR, "fwrite() failed: %s\n", strerror(errno));
515                                 close_connection = 1;
516                         }
517                 }
518
519                 if (fd) {
520                         char buf[256];
521                         int len;
522
523                         while ((len = read(fd, buf, sizeof(buf))) > 0) {
524                                 if (fwrite(buf, len, 1, ser->f) != 1) {
525                                         ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
526                                         close_connection = 1;
527                                         break;
528                                 }
529                         }
530                 }
531         }
532
533         ast_free(http_header);
534         ast_free(out);
535
536         if (close_connection) {
537                 ast_debug(1, "HTTP closing session.  status_code:%d\n", status_code);
538                 ast_tcptls_close_session_file(ser);
539         } else {
540                 ast_debug(1, "HTTP keeping session open.  status_code:%d\n", status_code);
541         }
542 }
543
544 void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm,
545         const unsigned long nonce, const unsigned long opaque, int stale,
546         const char *text)
547 {
548         struct ast_str *http_headers = ast_str_create(128);
549         struct ast_str *out = ast_str_create(512);
550
551         if (!http_headers || !out) {
552                 ast_free(http_headers);
553                 ast_free(out);
554                 if (ser && ser->f) {
555                         ast_debug(1, "HTTP closing session.  Auth OOM\n");
556                         ast_tcptls_close_session_file(ser);
557                 }
558                 return;
559         }
560
561         ast_str_set(&http_headers, 0,
562                 "WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n"
563                 "Content-type: text/html\r\n",
564                 realm ? realm : "Asterisk",
565                 nonce,
566                 opaque,
567                 stale ? ", stale=true" : "");
568
569         ast_str_set(&out, 0,
570                 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
571                 "<html><head>\r\n"
572                 "<title>401 Unauthorized</title>\r\n"
573                 "</head><body>\r\n"
574                 "<h1>401 Unauthorized</h1>\r\n"
575                 "<p>%s</p>\r\n"
576                 "<hr />\r\n"
577                 "<address>Asterisk Server</address>\r\n"
578                 "</body></html>\r\n",
579                 text ? text : "");
580
581         ast_http_send(ser, AST_HTTP_UNKNOWN, 401, "Unauthorized", http_headers, out, 0, 0);
582 }
583
584 void ast_http_error(struct ast_tcptls_session_instance *ser, int status_code, const char *status_title, const char *text)
585 {
586         struct ast_str *http_headers = ast_str_create(40);
587         struct ast_str *out = ast_str_create(256);
588
589         if (!http_headers || !out) {
590                 ast_free(http_headers);
591                 ast_free(out);
592                 if (ser && ser->f) {
593                         ast_debug(1, "HTTP closing session.  error OOM\n");
594                         ast_tcptls_close_session_file(ser);
595                 }
596                 return;
597         }
598
599         ast_str_set(&http_headers, 0, "Content-type: text/html\r\n");
600
601         ast_str_set(&out, 0,
602                 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
603                 "<html><head>\r\n"
604                 "<title>%d %s</title>\r\n"
605                 "</head><body>\r\n"
606                 "<h1>%s</h1>\r\n"
607                 "<p>%s</p>\r\n"
608                 "<hr />\r\n"
609                 "<address>Asterisk Server</address>\r\n"
610                 "</body></html>\r\n",
611                 status_code, status_title, status_title, text);
612
613         ast_http_send(ser, AST_HTTP_UNKNOWN, status_code, status_title, http_headers, out, 0, 0);
614 }
615
616 /*!
617  * \brief Link the new uri into the list.
618  *
619  * They are sorted by length of
620  * the string, not alphabetically. Duplicate entries are not replaced,
621  * but the insertion order (using <= and not just <) makes sure that
622  * more recent insertions hide older ones.
623  * On a lookup, we just scan the list and stop at the first matching entry.
624  */
625 int ast_http_uri_link(struct ast_http_uri *urih)
626 {
627         struct ast_http_uri *uri;
628         int len = strlen(urih->uri);
629
630         AST_RWLIST_WRLOCK(&uris);
631
632         if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) {
633                 AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
634                 AST_RWLIST_UNLOCK(&uris);
635                 return 0;
636         }
637
638         AST_RWLIST_TRAVERSE(&uris, uri, entry) {
639                 if (AST_RWLIST_NEXT(uri, entry) &&
640                         strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len) {
641                         AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
642                         AST_RWLIST_UNLOCK(&uris);
643
644                         return 0;
645                 }
646         }
647
648         AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
649
650         AST_RWLIST_UNLOCK(&uris);
651
652         return 0;
653 }
654
655 void ast_http_uri_unlink(struct ast_http_uri *urih)
656 {
657         AST_RWLIST_WRLOCK(&uris);
658         AST_RWLIST_REMOVE(&uris, urih, entry);
659         AST_RWLIST_UNLOCK(&uris);
660 }
661
662 void ast_http_uri_unlink_all_with_key(const char *key)
663 {
664         struct ast_http_uri *urih;
665         AST_RWLIST_WRLOCK(&uris);
666         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&uris, urih, entry) {
667                 if (!strcmp(urih->key, key)) {
668                         AST_RWLIST_REMOVE_CURRENT(entry);
669                         if (urih->dmallocd) {
670                                 ast_free(urih->data);
671                         }
672                         if (urih->mallocd) {
673                                 ast_free(urih);
674                         }
675                 }
676         }
677         AST_RWLIST_TRAVERSE_SAFE_END;
678         AST_RWLIST_UNLOCK(&uris);
679 }
680
681 /*!
682  * \brief Retrieves the header with the given field name.
683  *
684  * \param headers Headers to search.
685  * \param field_name Name of the header to find.
686  * \return Associated header value.
687  * \return \c NULL if header is not present.
688  */
689 static const char *get_header(struct ast_variable *headers, const char *field_name)
690 {
691         struct ast_variable *v;
692
693         for (v = headers; v; v = v->next) {
694                 if (!strcasecmp(v->name, field_name)) {
695                         return v->value;
696                 }
697         }
698         return NULL;
699 }
700
701 /*!
702  * \brief Retrieves the content type specified in the "Content-Type" header.
703  *
704  * This function only returns the "type/subtype" and any trailing parameter is
705  * not included.
706  *
707  * \note the return value is an allocated string that needs to be freed.
708  *
709  * \retval the content type/subtype or NULL if the header is not found.
710  */
711 static char *get_content_type(struct ast_variable *headers)
712 {
713         const char *content_type = get_header(headers, "Content-Type");
714         const char *param;
715         size_t size;
716
717         if (!content_type) {
718                 return NULL;
719         }
720
721         param = strchr(content_type, ';');
722         size = param ? param - content_type : strlen(content_type);
723
724         return ast_strndup(content_type, size);
725 }
726
727 /*!
728  * \brief Returns the value of the Content-Length header.
729  *
730  * \param headers HTTP headers.
731  *
732  * \retval length Value of the Content-Length header.
733  * \retval 0 if header is not present.
734  * \retval -1 if header is invalid.
735  */
736 static int get_content_length(struct ast_variable *headers)
737 {
738         const char *content_length = get_header(headers, "Content-Length");
739         int length;
740
741         if (!content_length) {
742                 /* Missing content length; assume zero */
743                 return 0;
744         }
745
746         length = 0;
747         if (sscanf(content_length, "%30d", &length) != 1) {
748                 /* Invalid Content-Length value */
749                 length = -1;
750         }
751         return length;
752 }
753
754 /*!
755  * \brief Returns the value of the Transfer-Encoding header.
756  *
757  * \param headers HTTP headers.
758  * \retval string Value of the Transfer-Encoding header.
759  * \retval NULL if header is not present.
760  */
761 static const char *get_transfer_encoding(struct ast_variable *headers)
762 {
763         return get_header(headers, "Transfer-Encoding");
764 }
765
766 /*!
767  * \internal
768  * \brief Determine if the HTTP peer wants the connection closed.
769  *
770  * \param headers List of HTTP headers
771  *
772  * \retval 0 keep connection open.
773  * \retval -1 close connection.
774  */
775 static int http_check_connection_close(struct ast_variable *headers)
776 {
777         const char *connection = get_header(headers, "Connection");
778         int close_connection = 0;
779
780         if (connection && !strcasecmp(connection, "close")) {
781                 close_connection = -1;
782         }
783         return close_connection;
784 }
785
786 void ast_http_request_close_on_completion(struct ast_tcptls_session_instance *ser)
787 {
788         struct http_worker_private_data *request = ser->private_data;
789
790         ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
791 }
792
793 /*!
794  * \internal
795  * \brief Initialize the request tracking information in case of early failure.
796  * \since 12.4.0
797  *
798  * \param request Request tracking information.
799  *
800  * \return Nothing
801  */
802 static void http_request_tracking_init(struct http_worker_private_data *request)
803 {
804         ast_set_flags_to(&request->flags,
805                 HTTP_FLAG_HAS_BODY | HTTP_FLAG_BODY_READ | HTTP_FLAG_CLOSE_ON_COMPLETION,
806                 /* Assume close in case request fails early */
807                 HTTP_FLAG_CLOSE_ON_COMPLETION);
808 }
809
810 /*!
811  * \internal
812  * \brief Setup the HTTP request tracking information.
813  * \since 12.4.0
814  *
815  * \param ser HTTP TCP/TLS session object.
816  * \param headers List of HTTP headers.
817  *
818  * \retval 0 on success.
819  * \retval -1 on error.
820  */
821 static int http_request_tracking_setup(struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
822 {
823         struct http_worker_private_data *request = ser->private_data;
824         const char *transfer_encoding;
825
826         ast_set_flags_to(&request->flags,
827                 HTTP_FLAG_HAS_BODY | HTTP_FLAG_BODY_READ | HTTP_FLAG_CLOSE_ON_COMPLETION,
828                 http_check_connection_close(headers) ? HTTP_FLAG_CLOSE_ON_COMPLETION : 0);
829
830         transfer_encoding = get_transfer_encoding(headers);
831         if (transfer_encoding && !strcasecmp(transfer_encoding, "chunked")) {
832                 request->body_length = -1;
833                 ast_set_flag(&request->flags, HTTP_FLAG_HAS_BODY);
834                 return 0;
835         }
836
837         request->body_length = get_content_length(headers);
838         if (0 < request->body_length) {
839                 ast_set_flag(&request->flags, HTTP_FLAG_HAS_BODY);
840         } else if (request->body_length < 0) {
841                 /* Invalid Content-Length */
842                 ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
843                 ast_http_error(ser, 400, "Bad Request", "Invalid Content-Length in request!");
844                 return -1;
845         }
846         return 0;
847 }
848
849 void ast_http_body_read_status(struct ast_tcptls_session_instance *ser, int read_success)
850 {
851         struct http_worker_private_data *request;
852
853         request = ser->private_data;
854         if (!ast_test_flag(&request->flags, HTTP_FLAG_HAS_BODY)
855                 || ast_test_flag(&request->flags, HTTP_FLAG_BODY_READ)) {
856                 /* No body to read. */
857                 return;
858         }
859         ast_set_flag(&request->flags, HTTP_FLAG_BODY_READ);
860         if (!read_success) {
861                 ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
862         }
863 }
864
865 /*!
866  * \internal
867  * \brief Read the next length bytes from the HTTP body.
868  * \since 12.4.0
869  *
870  * \param ser HTTP TCP/TLS session object.
871  * \param buf Where to put the contents reading.
872  * \param length How much contents to read.
873  * \param what_getting Name of the contents reading.
874  *
875  * \retval 0 on success.
876  * \retval -1 on error.
877  */
878 static int http_body_read_contents(struct ast_tcptls_session_instance *ser, char *buf, int length, const char *what_getting)
879 {
880         int res;
881
882         /* Stay in fread until get all the expected data or timeout. */
883         res = fread(buf, length, 1, ser->f);
884         if (res < 1) {
885                 ast_log(LOG_WARNING, "Short HTTP request %s (Wanted %d)\n",
886                         what_getting, length);
887                 return -1;
888         }
889         return 0;
890 }
891
892 /*!
893  * \internal
894  * \brief Read and discard the next length bytes from the HTTP body.
895  * \since 12.4.0
896  *
897  * \param ser HTTP TCP/TLS session object.
898  * \param length How much contents to discard
899  * \param what_getting Name of the contents discarding.
900  *
901  * \retval 0 on success.
902  * \retval -1 on error.
903  */
904 static int http_body_discard_contents(struct ast_tcptls_session_instance *ser, int length, const char *what_getting)
905 {
906         int res;
907         char buf[MAX_HTTP_LINE_LENGTH];/* Discard buffer */
908
909         /* Stay in fread until get all the expected data or timeout. */
910         while (sizeof(buf) < length) {
911                 res = fread(buf, sizeof(buf), 1, ser->f);
912                 if (res < 1) {
913                         ast_log(LOG_WARNING, "Short HTTP request %s (Wanted %zu of remaining %d)\n",
914                                 what_getting, sizeof(buf), length);
915                         return -1;
916                 }
917                 length -= sizeof(buf);
918         }
919         res = fread(buf, length, 1, ser->f);
920         if (res < 1) {
921                 ast_log(LOG_WARNING, "Short HTTP request %s (Wanted %d of remaining %d)\n",
922                         what_getting, length, length);
923                 return -1;
924         }
925         return 0;
926 }
927
928 /*!
929  * \internal
930  * \brief decode chunked mode hexadecimal value
931  *
932  * \param s string to decode
933  * \param len length of string
934  *
935  * \retval length on success.
936  * \retval -1 on error.
937  */
938 static int chunked_atoh(const char *s, int len)
939 {
940         int value = 0;
941         char c;
942
943         if (*s < '0') {
944                 /* zero value must be 0\n not just \n */
945                 return -1;
946         }
947
948         while (len--) {
949                 c = *s++;
950                 if (c == '\x0D') {
951                         return value;
952                 }
953                 if (c == ';') {
954                         /* We have a chunk-extension that we don't care about. */
955                         while (len--) {
956                                 if (*s++ == '\x0D') {
957                                         return value;
958                                 }
959                         }
960                         break;
961                 }
962                 value <<= 4;
963                 if (c >= '0' && c <= '9') {
964                         value += c - '0';
965                         continue;
966                 }
967                 if (c >= 'a' && c <= 'f') {
968                         value += 10 + c - 'a';
969                         continue;
970                 }
971                 if (c >= 'A' && c <= 'F') {
972                         value += 10 + c - 'A';
973                         continue;
974                 }
975                 /* invalid character */
976                 return -1;
977         }
978         /* end of string */
979         return -1;
980 }
981
982 /*!
983  * \internal
984  * \brief Read and convert the chunked body header length.
985  * \since 12.4.0
986  *
987  * \param ser HTTP TCP/TLS session object.
988  *
989  * \retval length Size of chunk to expect.
990  * \retval -1 on error.
991  */
992 static int http_body_get_chunk_length(struct ast_tcptls_session_instance *ser)
993 {
994         int length;
995         char header_line[MAX_HTTP_LINE_LENGTH];
996
997         /* get the line of hexadecimal giving chunk-size w/ optional chunk-extension */
998         if (!fgets(header_line, sizeof(header_line), ser->f)) {
999                 ast_log(LOG_WARNING, "Short HTTP read of chunked header\n");
1000                 return -1;
1001         }
1002         length = chunked_atoh(header_line, strlen(header_line));
1003         if (length < 0) {
1004                 ast_log(LOG_WARNING, "Invalid HTTP chunk size\n");
1005                 return -1;
1006         }
1007         return length;
1008 }
1009
1010 /*!
1011  * \internal
1012  * \brief Read and check the chunk contents line termination.
1013  * \since 12.4.0
1014  *
1015  * \param ser HTTP TCP/TLS session object.
1016  *
1017  * \retval 0 on success.
1018  * \retval -1 on error.
1019  */
1020 static int http_body_check_chunk_sync(struct ast_tcptls_session_instance *ser)
1021 {
1022         int res;
1023         char chunk_sync[2];
1024
1025         /* Stay in fread until get the expected CRLF or timeout. */
1026         res = fread(chunk_sync, sizeof(chunk_sync), 1, ser->f);
1027         if (res < 1) {
1028                 ast_log(LOG_WARNING, "Short HTTP chunk sync read (Wanted %zu)\n",
1029                         sizeof(chunk_sync));
1030                 return -1;
1031         }
1032         if (chunk_sync[0] != 0x0D || chunk_sync[1] != 0x0A) {
1033                 ast_log(LOG_WARNING, "HTTP chunk sync bytes wrong (0x%02X, 0x%02X)\n",
1034                         chunk_sync[0], chunk_sync[1]);
1035                 return -1;
1036         }
1037
1038         return 0;
1039 }
1040
1041 /*!
1042  * \internal
1043  * \brief Read and discard any chunked trailer entity-header lines.
1044  * \since 12.4.0
1045  *
1046  * \param ser HTTP TCP/TLS session object.
1047  *
1048  * \retval 0 on success.
1049  * \retval -1 on error.
1050  */
1051 static int http_body_discard_chunk_trailer_headers(struct ast_tcptls_session_instance *ser)
1052 {
1053         char header_line[MAX_HTTP_LINE_LENGTH];
1054
1055         for (;;) {
1056                 if (!fgets(header_line, sizeof(header_line), ser->f)) {
1057                         ast_log(LOG_WARNING, "Short HTTP read of chunked trailer header\n");
1058                         return -1;
1059                 }
1060
1061                 /* Trim trailing whitespace */
1062                 ast_trim_blanks(header_line);
1063                 if (ast_strlen_zero(header_line)) {
1064                         /* A blank line ends the chunked-body */
1065                         break;
1066                 }
1067         }
1068         return 0;
1069 }
1070
1071 int ast_http_body_discard(struct ast_tcptls_session_instance *ser)
1072 {
1073         struct http_worker_private_data *request;
1074
1075         request = ser->private_data;
1076         if (!ast_test_flag(&request->flags, HTTP_FLAG_HAS_BODY)
1077                 || ast_test_flag(&request->flags, HTTP_FLAG_BODY_READ)) {
1078                 /* No body to read or it has already been read. */
1079                 return 0;
1080         }
1081         ast_set_flag(&request->flags, HTTP_FLAG_BODY_READ);
1082
1083         ast_debug(1, "HTTP discarding unused request body\n");
1084
1085         ast_assert(request->body_length != 0);
1086         if (0 < request->body_length) {
1087                 if (http_body_discard_contents(ser, request->body_length, "body")) {
1088                         ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1089                         return -1;
1090                 }
1091                 return 0;
1092         }
1093
1094         /* parse chunked-body */
1095         for (;;) {
1096                 int length;
1097
1098                 length = http_body_get_chunk_length(ser);
1099                 if (length < 0) {
1100                         ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1101                         return -1;
1102                 }
1103                 if (length == 0) {
1104                         /* parsed last-chunk */
1105                         break;
1106                 }
1107
1108                 if (http_body_discard_contents(ser, length, "chunk-data")
1109                         || http_body_check_chunk_sync(ser)) {
1110                         ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1111                         return -1;
1112                 }
1113         }
1114
1115         /* Read and discard any trailer entity-header lines. */
1116         if (http_body_discard_chunk_trailer_headers(ser)) {
1117                 ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1118                 return -1;
1119         }
1120         return 0;
1121 }
1122
1123 /*!
1124  * \brief Returns the contents (body) of the HTTP request
1125  *
1126  * \param return_length ptr to int that returns content length
1127  * \param ser HTTP TCP/TLS session object
1128  * \param headers List of HTTP headers
1129  * \return ptr to content (zero terminated) or NULL on failure
1130  * \note Since returned ptr is malloc'd, it should be free'd by caller
1131  */
1132 static char *ast_http_get_contents(int *return_length,
1133         struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
1134 {
1135         struct http_worker_private_data *request;
1136         int content_length;
1137         int bufsize;
1138         char *buf;
1139
1140         request = ser->private_data;
1141         if (!ast_test_flag(&request->flags, HTTP_FLAG_HAS_BODY)) {
1142                 /* no content - not an error */
1143                 return NULL;
1144         }
1145         if (ast_test_flag(&request->flags, HTTP_FLAG_BODY_READ)) {
1146                 /* Already read the body.  Cannot read again.  Assume no content. */
1147                 ast_assert(0);
1148                 return NULL;
1149         }
1150         ast_set_flag(&request->flags, HTTP_FLAG_BODY_READ);
1151
1152         ast_debug(2, "HTTP consuming request body\n");
1153
1154         ast_assert(request->body_length != 0);
1155         if (0 < request->body_length) {
1156                 /* handle regular non-chunked content */
1157                 content_length = request->body_length;
1158                 if (content_length > MAX_CONTENT_LENGTH) {
1159                         ast_log(LOG_WARNING, "Excessively long HTTP content. (%d > %d)\n",
1160                                 content_length, MAX_CONTENT_LENGTH);
1161                         ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1162                         errno = EFBIG;
1163                         return NULL;
1164                 }
1165                 buf = ast_malloc(content_length + 1);
1166                 if (!buf) {
1167                         /* Malloc sets ENOMEM */
1168                         ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1169                         return NULL;
1170                 }
1171
1172                 if (http_body_read_contents(ser, buf, content_length, "body")) {
1173                         ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1174                         errno = EIO;
1175                         ast_free(buf);
1176                         return NULL;
1177                 }
1178
1179                 buf[content_length] = 0;
1180                 *return_length = content_length;
1181                 return buf;
1182         }
1183
1184         /* pre-allocate buffer */
1185         bufsize = 250;
1186         buf = ast_malloc(bufsize);
1187         if (!buf) {
1188                 ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1189                 return NULL;
1190         }
1191
1192         /* parse chunked-body */
1193         content_length = 0;
1194         for (;;) {
1195                 int chunk_length;
1196
1197                 chunk_length = http_body_get_chunk_length(ser);
1198                 if (chunk_length < 0) {
1199                         ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1200                         errno = EIO;
1201                         ast_free(buf);
1202                         return NULL;
1203                 }
1204                 if (chunk_length == 0) {
1205                         /* parsed last-chunk */
1206                         break;
1207                 }
1208                 if (content_length + chunk_length > MAX_CONTENT_LENGTH) {
1209                         ast_log(LOG_WARNING,
1210                                 "Excessively long HTTP accumulated chunked body. (%d + %d > %d)\n",
1211                                 content_length, chunk_length, MAX_CONTENT_LENGTH);
1212                         ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1213                         errno = EFBIG;
1214                         ast_free(buf);
1215                         return NULL;
1216                 }
1217
1218                 /* insure buffer is large enough +1 */
1219                 if (content_length + chunk_length >= bufsize) {
1220                         char *new_buf;
1221
1222                         /* Increase bufsize until it can handle the expected data. */
1223                         do {
1224                                 bufsize *= 2;
1225                         } while (content_length + chunk_length >= bufsize);
1226
1227                         new_buf = ast_realloc(buf, bufsize);
1228                         if (!new_buf) {
1229                                 ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1230                                 ast_free(buf);
1231                                 return NULL;
1232                         }
1233                         buf = new_buf;
1234                 }
1235
1236                 if (http_body_read_contents(ser, buf + content_length, chunk_length, "chunk-data")
1237                         || http_body_check_chunk_sync(ser)) {
1238                         ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1239                         errno = EIO;
1240                         ast_free(buf);
1241                         return NULL;
1242                 }
1243                 content_length += chunk_length;
1244         }
1245
1246         /*
1247          * Read and discard any trailer entity-header lines
1248          * which we don't care about.
1249          *
1250          * XXX In the future we may need to add the trailer headers
1251          * to the passed in headers list rather than discarding them.
1252          */
1253         if (http_body_discard_chunk_trailer_headers(ser)) {
1254                 ast_set_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION);
1255                 errno = EIO;
1256                 ast_free(buf);
1257                 return NULL;
1258         }
1259
1260         buf[content_length] = 0;
1261         *return_length = content_length;
1262         return buf;
1263 }
1264
1265 struct ast_json *ast_http_get_json(
1266         struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
1267 {
1268         int content_length = 0;
1269         struct ast_json *body;
1270         RAII_VAR(char *, buf, NULL, ast_free);
1271         RAII_VAR(char *, type, get_content_type(headers), ast_free);
1272
1273         /* Use errno to distinguish errors from no body */
1274         errno = 0;
1275
1276         if (ast_strlen_zero(type) || strcasecmp(type, "application/json")) {
1277                 /* Content type is not JSON.  Don't read the body. */
1278                 return NULL;
1279         }
1280
1281         buf = ast_http_get_contents(&content_length, ser, headers);
1282         if (!buf || !content_length) {
1283                 /*
1284                  * errno already set
1285                  * or it is not an error to have zero content
1286                  */
1287                 return NULL;
1288         }
1289
1290         body = ast_json_load_buf(buf, content_length, NULL);
1291         if (!body) {
1292                 /* Failed to parse JSON; treat as an I/O error */
1293                 errno = EIO;
1294                 return NULL;
1295         }
1296
1297         return body;
1298 }
1299
1300 /*
1301  * get post variables from client Request Entity-Body, if content type is
1302  * application/x-www-form-urlencoded
1303  */
1304 struct ast_variable *ast_http_get_post_vars(
1305         struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
1306 {
1307         int content_length = 0;
1308         struct ast_variable *v, *post_vars=NULL, *prev = NULL;
1309         char *var, *val;
1310         RAII_VAR(char *, buf, NULL, ast_free);
1311         RAII_VAR(char *, type, get_content_type(headers), ast_free);
1312
1313         /* Use errno to distinguish errors from no params */
1314         errno = 0;
1315
1316         if (ast_strlen_zero(type) ||
1317             strcasecmp(type, "application/x-www-form-urlencoded")) {
1318                 /* Content type is not form data.  Don't read the body. */
1319                 return NULL;
1320         }
1321
1322         buf = ast_http_get_contents(&content_length, ser, headers);
1323         if (!buf || !content_length) {
1324                 /*
1325                  * errno already set
1326                  * or it is not an error to have zero content
1327                  */
1328                 return NULL;
1329         }
1330
1331         while ((val = strsep(&buf, "&"))) {
1332                 var = strsep(&val, "=");
1333                 if (val) {
1334                         ast_uri_decode(val, ast_uri_http_legacy);
1335                 } else  {
1336                         val = "";
1337                 }
1338                 ast_uri_decode(var, ast_uri_http_legacy);
1339                 if ((v = ast_variable_new(var, val, ""))) {
1340                         if (post_vars) {
1341                                 prev->next = v;
1342                         } else {
1343                                 post_vars = v;
1344                         }
1345                         prev = v;
1346                 }
1347         }
1348
1349         return post_vars;
1350 }
1351
1352 static int handle_uri(struct ast_tcptls_session_instance *ser, char *uri,
1353         enum ast_http_method method, struct ast_variable *headers)
1354 {
1355         char *c;
1356         int res = 0;
1357         char *params = uri;
1358         struct ast_http_uri *urih = NULL;
1359         int l;
1360         struct ast_variable *get_vars = NULL, *v, *prev = NULL;
1361         struct http_uri_redirect *redirect;
1362
1363         ast_debug(2, "HTTP Request URI is %s \n", uri);
1364
1365         strsep(&params, "?");
1366         /* Extract arguments from the request and store them in variables. */
1367         if (params) {
1368                 char *var, *val;
1369
1370                 while ((val = strsep(&params, "&"))) {
1371                         var = strsep(&val, "=");
1372                         if (val) {
1373                                 ast_uri_decode(val, ast_uri_http_legacy);
1374                         } else  {
1375                                 val = "";
1376                         }
1377                         ast_uri_decode(var, ast_uri_http_legacy);
1378                         if ((v = ast_variable_new(var, val, ""))) {
1379                                 if (get_vars) {
1380                                         prev->next = v;
1381                                 } else {
1382                                         get_vars = v;
1383                                 }
1384                                 prev = v;
1385                         }
1386                 }
1387         }
1388
1389         AST_RWLIST_RDLOCK(&uri_redirects);
1390         AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
1391                 if (!strcasecmp(uri, redirect->target)) {
1392                         struct ast_str *http_header = ast_str_create(128);
1393
1394                         if (!http_header) {
1395                                 ast_http_request_close_on_completion(ser);
1396                                 ast_http_error(ser, 500, "Server Error", "Out of memory");
1397                                 break;
1398                         }
1399                         ast_str_set(&http_header, 0, "Location: %s\r\n", redirect->dest);
1400                         ast_http_send(ser, method, 302, "Moved Temporarily", http_header, NULL, 0, 0);
1401                         break;
1402                 }
1403         }
1404         AST_RWLIST_UNLOCK(&uri_redirects);
1405         if (redirect) {
1406                 goto cleanup;
1407         }
1408
1409         /* We want requests to start with the (optional) prefix and '/' */
1410         l = strlen(prefix);
1411         if (!strncasecmp(uri, prefix, l) && uri[l] == '/') {
1412                 uri += l + 1;
1413                 /* scan registered uris to see if we match one. */
1414                 AST_RWLIST_RDLOCK(&uris);
1415                 AST_RWLIST_TRAVERSE(&uris, urih, entry) {
1416                         l = strlen(urih->uri);
1417                         c = uri + l;    /* candidate */
1418                         ast_debug(2, "match request [%s] with handler [%s] len %d\n", uri, urih->uri, l);
1419                         if (strncasecmp(urih->uri, uri, l) /* no match */
1420                             || (*c && *c != '/')) { /* substring */
1421                                 continue;
1422                         }
1423                         if (*c == '/') {
1424                                 c++;
1425                         }
1426                         if (!*c || urih->has_subtree) {
1427                                 uri = c;
1428                                 break;
1429                         }
1430                 }
1431                 AST_RWLIST_UNLOCK(&uris);
1432         }
1433         if (urih) {
1434                 ast_debug(1, "Match made with [%s]\n", urih->uri);
1435                 if (!urih->no_decode_uri) {
1436                         ast_uri_decode(uri, ast_uri_http_legacy);
1437                 }
1438                 res = urih->callback(ser, urih, uri, method, get_vars, headers);
1439         } else {
1440                 ast_debug(1, "Requested URI [%s] has no handler\n", uri);
1441                 ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
1442         }
1443
1444 cleanup:
1445         ast_variables_destroy(get_vars);
1446         return res;
1447 }
1448
1449 static struct ast_variable *parse_cookies(const char *cookies)
1450 {
1451         char *parse = ast_strdupa(cookies);
1452         char *cur;
1453         struct ast_variable *vars = NULL, *var;
1454
1455         while ((cur = strsep(&parse, ";"))) {
1456                 char *name, *val;
1457
1458                 name = val = cur;
1459                 strsep(&val, "=");
1460
1461                 if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
1462                         continue;
1463                 }
1464
1465                 name = ast_strip(name);
1466                 val = ast_strip_quoted(val, "\"", "\"");
1467
1468                 if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
1469                         continue;
1470                 }
1471
1472                 ast_debug(1, "HTTP Cookie, Name: '%s'  Value: '%s'\n", name, val);
1473
1474                 var = ast_variable_new(name, val, __FILE__);
1475                 var->next = vars;
1476                 vars = var;
1477         }
1478
1479         return vars;
1480 }
1481
1482 /* get cookie from Request headers */
1483 struct ast_variable *ast_http_get_cookies(struct ast_variable *headers)
1484 {
1485         struct ast_variable *v, *cookies = NULL;
1486
1487         for (v = headers; v; v = v->next) {
1488                 if (!strcasecmp(v->name, "Cookie")) {
1489                         ast_variables_destroy(cookies);
1490                         cookies = parse_cookies(v->value);
1491                 }
1492         }
1493         return cookies;
1494 }
1495
1496 static struct ast_http_auth *auth_create(const char *userid, const char *password)
1497 {
1498         struct ast_http_auth *auth;
1499         size_t userid_len;
1500         size_t password_len;
1501
1502         if (!userid || !password) {
1503                 ast_log(LOG_ERROR, "Invalid userid/password\n");
1504                 return NULL;
1505         }
1506
1507         userid_len = strlen(userid) + 1;
1508         password_len = strlen(password) + 1;
1509
1510         /* Allocate enough room to store everything in one memory block */
1511         auth = ao2_alloc(sizeof(*auth) + userid_len + password_len, NULL);
1512         if (!auth) {
1513                 return NULL;
1514         }
1515
1516         /* Put the userid right after the struct */
1517         auth->userid = (char *)(auth + 1);
1518         strcpy(auth->userid, userid);
1519
1520         /* Put the password right after the userid */
1521         auth->password = auth->userid + userid_len;
1522         strcpy(auth->password, password);
1523
1524         return auth;
1525 }
1526
1527 #define BASIC_PREFIX "Basic "
1528 #define BASIC_LEN 6 /*!< strlen(BASIC_PREFIX) */
1529
1530 struct ast_http_auth *ast_http_get_auth(struct ast_variable *headers)
1531 {
1532         struct ast_variable *v;
1533
1534         for (v = headers; v; v = v->next) {
1535                 const char *base64;
1536                 char decoded[256] = {};
1537                 char *username;
1538                 char *password;
1539 #ifdef AST_DEVMODE
1540                 int cnt;
1541 #endif /* AST_DEVMODE */
1542
1543                 if (strcasecmp("Authorization", v->name) != 0) {
1544                         continue;
1545                 }
1546
1547                 if (!ast_begins_with(v->value, BASIC_PREFIX)) {
1548                         ast_log(LOG_DEBUG,
1549                                 "Unsupported Authorization scheme\n");
1550                         continue;
1551                 }
1552
1553                 /* Basic auth header parsing. RFC 2617, section 2.
1554                  *   credentials = "Basic" basic-credentials
1555                  *   basic-credentials = base64-user-pass
1556                  *   base64-user-pass  = <base64 encoding of user-pass,
1557                  *                        except not limited to 76 char/line>
1558                  *   user-pass   = userid ":" password
1559                  */
1560
1561                 base64 = v->value + BASIC_LEN;
1562
1563                 /* This will truncate "userid:password" lines to
1564                  * sizeof(decoded). The array is long enough that this shouldn't
1565                  * be a problem */
1566 #ifdef AST_DEVMODE
1567                 cnt =
1568 #endif /* AST_DEVMODE */
1569                 ast_base64decode((unsigned char*)decoded, base64,
1570                         sizeof(decoded) - 1);
1571                 ast_assert(cnt < sizeof(decoded));
1572
1573                 /* Split the string at the colon */
1574                 password = decoded;
1575                 username = strsep(&password, ":");
1576                 if (!password) {
1577                         ast_log(LOG_WARNING, "Invalid Authorization header\n");
1578                         return NULL;
1579                 }
1580
1581                 return auth_create(username, password);
1582         }
1583
1584         return NULL;
1585 }
1586
1587 int ast_http_response_status_line(const char *buf, const char *version, int code)
1588 {
1589         int status_code;
1590         size_t size = strlen(version);
1591
1592         if (strncmp(buf, version, size) || buf[size] != ' ') {
1593                 ast_log(LOG_ERROR, "HTTP version not supported - "
1594                         "expected %s\n", version);
1595                 return -1;
1596         }
1597
1598         /* skip to status code (version + space) */
1599         buf += size + 1;
1600
1601         if (sscanf(buf, "%d", &status_code) != 1) {
1602                 ast_log(LOG_ERROR, "Could not read HTTP status code - "
1603                         "%s\n", buf);
1604                 return -1;
1605         }
1606
1607         return status_code;
1608 }
1609
1610 static void remove_excess_lws(char *s)
1611 {
1612         char *p, *res = s;
1613         char *buf = ast_malloc(strlen(s) + 1);
1614         char *buf_end;
1615
1616         if (!buf) {
1617                 return;
1618         }
1619
1620         buf_end = buf;
1621
1622         while (*s && *(s = ast_skip_blanks(s))) {
1623                 p = s;
1624                 s = ast_skip_nonblanks(s);
1625
1626                 if (buf_end != buf) {
1627                         *buf_end++ = ' ';
1628                 }
1629
1630                 memcpy(buf_end, p, s - p);
1631                 buf_end += s - p;
1632         }
1633         *buf_end = '\0';
1634         /* safe since buf will always be less than or equal to res */
1635         strcpy(res, buf);
1636         ast_free(buf);
1637 }
1638
1639 int ast_http_header_parse(char *buf, char **name, char **value)
1640 {
1641         ast_trim_blanks(buf);
1642         if (ast_strlen_zero(buf)) {
1643                 return -1;
1644         }
1645
1646         *value = buf;
1647         *name = strsep(value, ":");
1648         if (!*value) {
1649                 return 1;
1650         }
1651
1652         *value = ast_skip_blanks(*value);
1653         if (ast_strlen_zero(*value) || ast_strlen_zero(*name)) {
1654                 return 1;
1655         }
1656
1657         remove_excess_lws(*value);
1658         return 0;
1659 }
1660
1661 int ast_http_header_match(const char *name, const char *expected_name,
1662                           const char *value, const char *expected_value)
1663 {
1664         if (strcasecmp(name, expected_name)) {
1665                 /* no value to validate if names don't match */
1666                 return 0;
1667         }
1668
1669         if (strcasecmp(value, expected_value)) {
1670                 ast_log(LOG_ERROR, "Invalid header value - expected %s "
1671                         "received %s", value, expected_value);
1672                 return -1;
1673         }
1674         return 1;
1675 }
1676
1677 int ast_http_header_match_in(const char *name, const char *expected_name,
1678                              const char *value, const char *expected_value)
1679 {
1680         if (strcasecmp(name, expected_name)) {
1681                 /* no value to validate if names don't match */
1682                 return 0;
1683         }
1684
1685         if (!strcasestr(expected_value, value)) {
1686                 ast_log(LOG_ERROR, "Header '%s' - could not locate '%s' "
1687                         "in '%s'\n", name, value, expected_value);
1688                 return -1;
1689
1690         }
1691         return 1;
1692 }
1693
1694 /*! Limit the number of request headers in case the sender is being ridiculous. */
1695 #define MAX_HTTP_REQUEST_HEADERS        100
1696
1697 /*!
1698  * \internal
1699  * \brief Read the request headers.
1700  * \since 12.4.0
1701  *
1702  * \param ser HTTP TCP/TLS session object.
1703  * \param headers Where to put the request headers list pointer.
1704  *
1705  * \retval 0 on success.
1706  * \retval -1 on error.
1707  */
1708 static int http_request_headers_get(struct ast_tcptls_session_instance *ser, struct ast_variable **headers)
1709 {
1710         struct ast_variable *tail = *headers;
1711         int remaining_headers;
1712         char header_line[MAX_HTTP_LINE_LENGTH];
1713
1714         remaining_headers = MAX_HTTP_REQUEST_HEADERS;
1715         for (;;) {
1716                 char *name;
1717                 char *value;
1718
1719                 if (!fgets(header_line, sizeof(header_line), ser->f)) {
1720                         ast_http_error(ser, 400, "Bad Request", "Timeout");
1721                         return -1;
1722                 }
1723
1724                 /* Trim trailing characters */
1725                 ast_trim_blanks(header_line);
1726                 if (ast_strlen_zero(header_line)) {
1727                         /* A blank line ends the request header section. */
1728                         break;
1729                 }
1730
1731                 value = header_line;
1732                 name = strsep(&value, ":");
1733                 if (!value) {
1734                         continue;
1735                 }
1736
1737                 value = ast_skip_blanks(value);
1738                 if (ast_strlen_zero(value) || ast_strlen_zero(name)) {
1739                         continue;
1740                 }
1741
1742                 ast_trim_blanks(name);
1743
1744                 if (!remaining_headers--) {
1745                         /* Too many headers. */
1746                         ast_http_error(ser, 413, "Request Entity Too Large", "Too many headers");
1747                         return -1;
1748                 }
1749                 if (!*headers) {
1750                         *headers = ast_variable_new(name, value, __FILE__);
1751                         tail = *headers;
1752                 } else {
1753                         tail->next = ast_variable_new(name, value, __FILE__);
1754                         tail = tail->next;
1755                 }
1756                 if (!tail) {
1757                         /*
1758                          * Variable allocation failure.
1759                          * Try to make some room.
1760                          */
1761                         ast_variables_destroy(*headers);
1762                         *headers = NULL;
1763
1764                         ast_http_error(ser, 500, "Server Error", "Out of memory");
1765                         return -1;
1766                 }
1767         }
1768
1769         return 0;
1770 }
1771
1772 /*!
1773  * \internal
1774  * \brief Process a HTTP request.
1775  * \since 12.4.0
1776  *
1777  * \param ser HTTP TCP/TLS session object.
1778  *
1779  * \retval 0 Continue and process the next HTTP request.
1780  * \retval -1 Fatal HTTP connection error.  Force the HTTP connection closed.
1781  */
1782 static int httpd_process_request(struct ast_tcptls_session_instance *ser)
1783 {
1784         RAII_VAR(struct ast_variable *, headers, NULL, ast_variables_destroy);
1785         char *uri;
1786         char *method;
1787         const char *transfer_encoding;
1788         struct http_worker_private_data *request;
1789         enum ast_http_method http_method = AST_HTTP_UNKNOWN;
1790         int res;
1791         char request_line[MAX_HTTP_LINE_LENGTH];
1792
1793         if (!fgets(request_line, sizeof(request_line), ser->f)) {
1794                 return -1;
1795         }
1796
1797         /* Re-initialize the request body tracking data. */
1798         request = ser->private_data;
1799         http_request_tracking_init(request);
1800
1801         /* Get method */
1802         method = ast_skip_blanks(request_line);
1803         uri = ast_skip_nonblanks(method);
1804         if (*uri) {
1805                 *uri++ = '\0';
1806         }
1807
1808         if (!strcasecmp(method,"GET")) {
1809                 http_method = AST_HTTP_GET;
1810         } else if (!strcasecmp(method,"POST")) {
1811                 http_method = AST_HTTP_POST;
1812         } else if (!strcasecmp(method,"HEAD")) {
1813                 http_method = AST_HTTP_HEAD;
1814         } else if (!strcasecmp(method,"PUT")) {
1815                 http_method = AST_HTTP_PUT;
1816         } else if (!strcasecmp(method,"DELETE")) {
1817                 http_method = AST_HTTP_DELETE;
1818         } else if (!strcasecmp(method,"OPTIONS")) {
1819                 http_method = AST_HTTP_OPTIONS;
1820         }
1821
1822         uri = ast_skip_blanks(uri);     /* Skip white space */
1823         if (*uri) {                     /* terminate at the first blank */
1824                 char *c = ast_skip_nonblanks(uri);
1825
1826                 if (*c) {
1827                         *c = '\0';
1828                 }
1829         } else {
1830                 ast_http_error(ser, 400, "Bad Request", "Invalid Request");
1831                 return -1;
1832         }
1833
1834         /* process "Request Headers" lines */
1835         if (http_request_headers_get(ser, &headers)) {
1836                 return -1;
1837         }
1838
1839         transfer_encoding = get_transfer_encoding(headers);
1840         /* Transfer encoding defaults to identity */
1841         if (!transfer_encoding) {
1842                 transfer_encoding = "identity";
1843         }
1844
1845         /*
1846          * RFC 2616, section 3.6, we should respond with a 501 for any transfer-
1847          * codings we don't understand.
1848          */
1849         if (strcasecmp(transfer_encoding, "identity") != 0 &&
1850                 strcasecmp(transfer_encoding, "chunked") != 0) {
1851                 /* Transfer encodings not supported */
1852                 ast_http_error(ser, 501, "Unimplemented", "Unsupported Transfer-Encoding.");
1853                 return -1;
1854         }
1855
1856         if (http_request_tracking_setup(ser, headers)
1857                 || handle_uri(ser, uri, http_method, headers)
1858                 || ast_test_flag(&request->flags, HTTP_FLAG_CLOSE_ON_COMPLETION)) {
1859                 res = -1;
1860         } else {
1861                 res = 0;
1862         }
1863         return res;
1864 }
1865
1866 static void *httpd_helper_thread(void *data)
1867 {
1868         struct ast_tcptls_session_instance *ser = data;
1869         struct protoent *p;
1870         int flags;
1871         int timeout;
1872
1873         if (!ser || !ser->f) {
1874                 ao2_cleanup(ser);
1875                 return NULL;
1876         }
1877
1878         if (ast_atomic_fetchadd_int(&session_count, +1) >= session_limit) {
1879                 ast_log(LOG_WARNING, "HTTP session count exceeded %d sessions.\n",
1880                         session_limit);
1881                 goto done;
1882         }
1883         ast_debug(1, "HTTP opening session.  Top level\n");
1884
1885         /*
1886          * Here we set TCP_NODELAY on the socket to disable Nagle's algorithm.
1887          * This is necessary to prevent delays (caused by buffering) as we
1888          * write to the socket in bits and pieces.
1889          */
1890         p = getprotobyname("tcp");
1891         if (p) {
1892                 int arg = 1;
1893
1894                 if (setsockopt(ser->fd, p->p_proto, TCP_NODELAY, (char *) &arg, sizeof(arg) ) < 0) {
1895                         ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on HTTP connection: %s\n", strerror(errno));
1896                         ast_log(LOG_WARNING, "Some HTTP requests may be slow to respond.\n");
1897                 }
1898         } else {
1899                 ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on HTTP connection, getprotobyname(\"tcp\") failed\n");
1900                 ast_log(LOG_WARNING, "Some HTTP requests may be slow to respond.\n");
1901         }
1902
1903         /* make sure socket is non-blocking */
1904         flags = fcntl(ser->fd, F_GETFL);
1905         flags |= O_NONBLOCK;
1906         fcntl(ser->fd, F_SETFL, flags);
1907
1908         /* Setup HTTP worker private data to keep track of request body reading. */
1909         ao2_cleanup(ser->private_data);
1910         ser->private_data = ao2_alloc_options(sizeof(struct http_worker_private_data), NULL,
1911                 AO2_ALLOC_OPT_LOCK_NOLOCK);
1912         if (!ser->private_data) {
1913                 ast_http_error(ser, 500, "Server Error", "Out of memory");
1914                 goto done;
1915         }
1916         http_request_tracking_init(ser->private_data);
1917
1918         /* Determine initial HTTP request wait timeout. */
1919         timeout = session_keep_alive;
1920         if (timeout <= 0) {
1921                 /* Persistent connections not enabled. */
1922                 timeout = session_inactivity;
1923         }
1924         if (timeout < MIN_INITIAL_REQUEST_TIMEOUT) {
1925                 timeout = MIN_INITIAL_REQUEST_TIMEOUT;
1926         }
1927
1928         /* We can let the stream wait for data to arrive. */
1929         ast_tcptls_stream_set_exclusive_input(ser->stream_cookie, 1);
1930
1931         for (;;) {
1932                 int ch;
1933
1934                 /* Wait for next potential HTTP request message. */
1935                 ast_tcptls_stream_set_timeout_inactivity(ser->stream_cookie, timeout);
1936                 ch = fgetc(ser->f);
1937                 if (ch == EOF || ungetc(ch, ser->f) == EOF) {
1938                         /* Between request idle timeout */
1939                         ast_debug(1, "HTTP idle timeout or peer closed connection.\n");
1940                         break;
1941                 }
1942
1943                 ast_tcptls_stream_set_timeout_inactivity(ser->stream_cookie, session_inactivity);
1944                 if (httpd_process_request(ser) || !ser->f || feof(ser->f)) {
1945                         /* Break the connection or the connection closed */
1946                         break;
1947                 }
1948
1949                 timeout = session_keep_alive;
1950                 if (timeout <= 0) {
1951                         /* Persistent connections not enabled. */
1952                         break;
1953                 }
1954         }
1955
1956 done:
1957         ast_atomic_fetchadd_int(&session_count, -1);
1958
1959         if (ser->f) {
1960                 ast_debug(1, "HTTP closing session.  Top level\n");
1961                 ast_tcptls_close_session_file(ser);
1962         }
1963         ao2_ref(ser, -1);
1964         return NULL;
1965 }
1966
1967 /*!
1968  * \brief Add a new URI redirect
1969  * The entries in the redirect list are sorted by length, just like the list
1970  * of URI handlers.
1971  */
1972 static void add_redirect(const char *value)
1973 {
1974         char *target, *dest;
1975         struct http_uri_redirect *redirect, *cur;
1976         unsigned int target_len;
1977         unsigned int total_len;
1978
1979         dest = ast_strdupa(value);
1980         dest = ast_skip_blanks(dest);
1981         target = strsep(&dest, " ");
1982         target = ast_skip_blanks(target);
1983         target = strsep(&target, " "); /* trim trailing whitespace */
1984
1985         if (!dest) {
1986                 ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
1987                 return;
1988         }
1989
1990         target_len = strlen(target) + 1;
1991         total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
1992
1993         if (!(redirect = ast_calloc(1, total_len))) {
1994                 return;
1995         }
1996         redirect->dest = redirect->target + target_len;
1997         strcpy(redirect->target, target);
1998         strcpy(redirect->dest, dest);
1999
2000         AST_RWLIST_WRLOCK(&uri_redirects);
2001
2002         target_len--; /* So we can compare directly with strlen() */
2003         if (AST_RWLIST_EMPTY(&uri_redirects)
2004                 || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
2005                 AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
2006                 AST_RWLIST_UNLOCK(&uri_redirects);
2007
2008                 return;
2009         }
2010
2011         AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
2012                 if (AST_RWLIST_NEXT(cur, entry)
2013                         && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
2014                         AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
2015                         AST_RWLIST_UNLOCK(&uri_redirects);
2016                         return;
2017                 }
2018         }
2019
2020         AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
2021
2022         AST_RWLIST_UNLOCK(&uri_redirects);
2023 }
2024
2025 static int __ast_http_load(int reload)
2026 {
2027         struct ast_config *cfg;
2028         struct ast_variable *v;
2029         int enabled=0;
2030         int newenablestatic=0;
2031         char newprefix[MAX_PREFIX] = "";
2032         struct http_uri_redirect *redirect;
2033         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
2034         uint32_t bindport = DEFAULT_PORT;
2035         RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);
2036         int num_addrs = 0;
2037         int http_tls_was_enabled = 0;
2038
2039         cfg = ast_config_load2("http.conf", "http", config_flags);
2040         if (!cfg || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
2041                 return 0;
2042         }
2043
2044         http_tls_was_enabled = (reload && http_tls_cfg.enabled);
2045
2046         http_tls_cfg.enabled = 0;
2047         if (http_tls_cfg.certfile) {
2048                 ast_free(http_tls_cfg.certfile);
2049         }
2050         http_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
2051
2052         if (http_tls_cfg.pvtfile) {
2053                 ast_free(http_tls_cfg.pvtfile);
2054         }
2055         http_tls_cfg.pvtfile = ast_strdup("");
2056
2057         if (http_tls_cfg.cipher) {
2058                 ast_free(http_tls_cfg.cipher);
2059         }
2060         http_tls_cfg.cipher = ast_strdup("");
2061
2062         AST_RWLIST_WRLOCK(&uri_redirects);
2063         while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
2064                 ast_free(redirect);
2065         }
2066         AST_RWLIST_UNLOCK(&uri_redirects);
2067
2068         ast_sockaddr_setnull(&https_desc.local_address);
2069
2070         session_limit = DEFAULT_SESSION_LIMIT;
2071         session_inactivity = DEFAULT_SESSION_INACTIVITY;
2072         session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE;
2073
2074         v = ast_variable_browse(cfg, "general");
2075         for (; v; v = v->next) {
2076                 /* read tls config options while preventing unsupported options from being set */
2077                 if (strcasecmp(v->name, "tlscafile")
2078                         && strcasecmp(v->name, "tlscapath")
2079                         && strcasecmp(v->name, "tlscadir")
2080                         && strcasecmp(v->name, "tlsverifyclient")
2081                         && strcasecmp(v->name, "tlsdontverifyserver")
2082                         && strcasecmp(v->name, "tlsclientmethod")
2083                         && strcasecmp(v->name, "sslclientmethod")
2084                         && strcasecmp(v->name, "tlscipher")
2085                         && strcasecmp(v->name, "sslcipher")
2086                         && !ast_tls_read_conf(&http_tls_cfg, &https_desc, v->name, v->value)) {
2087                         continue;
2088                 }
2089
2090                 if (!strcasecmp(v->name, "enabled")) {
2091                         enabled = ast_true(v->value);
2092                 } else if (!strcasecmp(v->name, "enablestatic")) {
2093                         newenablestatic = ast_true(v->value);
2094                 } else if (!strcasecmp(v->name, "bindport")) {
2095                         if (ast_parse_arg(v->value, PARSE_UINT32 | PARSE_IN_RANGE | PARSE_DEFAULT,
2096                                 &bindport, DEFAULT_PORT, 0, 65535)) {
2097                                 ast_log(LOG_WARNING, "Invalid port %s specified. Using default port %" PRId32 "\n",
2098                                         v->value, DEFAULT_PORT);
2099                         }
2100                 } else if (!strcasecmp(v->name, "bindaddr")) {
2101                         if (!(num_addrs = ast_sockaddr_resolve(&addrs, v->value, 0, AST_AF_UNSPEC))) {
2102                                 ast_log(LOG_WARNING, "Invalid bind address %s\n", v->value);
2103                         }
2104                 } else if (!strcasecmp(v->name, "prefix")) {
2105                         if (!ast_strlen_zero(v->value)) {
2106                                 newprefix[0] = '/';
2107                                 ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
2108                         } else {
2109                                 newprefix[0] = '\0';
2110                         }
2111                 } else if (!strcasecmp(v->name, "redirect")) {
2112                         add_redirect(v->value);
2113                 } else if (!strcasecmp(v->name, "sessionlimit")) {
2114                         if (ast_parse_arg(v->value, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE,
2115                                 &session_limit, DEFAULT_SESSION_LIMIT, 1, INT_MAX)) {
2116                                 ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
2117                                         v->name, v->value, v->lineno);
2118                         }
2119                 } else if (!strcasecmp(v->name, "session_inactivity")) {
2120                         if (ast_parse_arg(v->value, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE,
2121                                 &session_inactivity, DEFAULT_SESSION_INACTIVITY, 1, INT_MAX)) {
2122                                 ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
2123                                         v->name, v->value, v->lineno);
2124                         }
2125                 } else if (!strcasecmp(v->name, "session_keep_alive")) {
2126                         if (sscanf(v->value, "%30d", &session_keep_alive) != 1
2127                                 || session_keep_alive < 0) {
2128                                 session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE;
2129                                 ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
2130                                         v->name, v->value, v->lineno);
2131                         }
2132                 } else {
2133                         ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
2134                 }
2135         }
2136
2137         ast_config_destroy(cfg);
2138
2139         if (strcmp(prefix, newprefix)) {
2140                 ast_copy_string(prefix, newprefix, sizeof(prefix));
2141         }
2142         enablestatic = newenablestatic;
2143
2144         if (num_addrs && enabled) {
2145                 int i;
2146                 for (i = 0; i < num_addrs; ++i) {
2147                         ast_sockaddr_copy(&http_desc.local_address, &addrs[i]);
2148                         if (!ast_sockaddr_port(&http_desc.local_address)) {
2149                                 ast_sockaddr_set_port(&http_desc.local_address, bindport);
2150                         }
2151                         ast_tcptls_server_start(&http_desc);
2152                         if (http_desc.accept_fd == -1) {
2153                                 ast_log(LOG_WARNING, "Failed to start HTTP server for address %s\n", ast_sockaddr_stringify(&addrs[i]));
2154                                 ast_sockaddr_setnull(&http_desc.local_address);
2155                         } else {
2156                                 ast_verb(1, "Bound HTTP server to address %s\n", ast_sockaddr_stringify(&addrs[i]));
2157                                 break;
2158                         }
2159                 }
2160                 /* When no specific TLS bindaddr is specified, we just use
2161                  * the non-TLS bindaddress here.
2162                  */
2163                 if (ast_sockaddr_isnull(&https_desc.local_address) && http_desc.accept_fd != -1) {
2164                         ast_sockaddr_copy(&https_desc.local_address, &https_desc.local_address);
2165                         /* Of course, we can't use the same port though.
2166                          * Since no bind address was specified, we just use the
2167                          * default TLS port
2168                          */
2169                         ast_sockaddr_set_port(&https_desc.local_address, DEFAULT_TLS_PORT);
2170                 }
2171         }
2172         if (http_tls_was_enabled && !http_tls_cfg.enabled) {
2173                 ast_tcptls_server_stop(&https_desc);
2174         } else if (http_tls_cfg.enabled && !ast_sockaddr_isnull(&https_desc.local_address)) {
2175                 /* We can get here either because a TLS-specific address was specified
2176                  * or because we copied the non-TLS address here. In the case where
2177                  * we read an explicit address from the config, there may have been
2178                  * no port specified, so we'll just use the default TLS port.
2179                  */
2180                 if (!ast_sockaddr_port(&https_desc.local_address)) {
2181                         ast_sockaddr_set_port(&https_desc.local_address, DEFAULT_TLS_PORT);
2182                 }
2183                 if (ast_ssl_setup(https_desc.tls_cfg)) {
2184                         ast_tcptls_server_start(&https_desc);
2185                 }
2186         }
2187
2188         return 0;
2189 }
2190
2191 static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2192 {
2193         struct ast_http_uri *urih;
2194         struct http_uri_redirect *redirect;
2195
2196         switch (cmd) {
2197         case CLI_INIT:
2198                 e->command = "http show status";
2199                 e->usage =
2200                         "Usage: http show status\n"
2201                         "       Lists status of internal HTTP engine\n";
2202                 return NULL;
2203         case CLI_GENERATE:
2204                 return NULL;
2205         }
2206
2207         if (a->argc != 3) {
2208                 return CLI_SHOWUSAGE;
2209         }
2210         ast_cli(a->fd, "HTTP Server Status:\n");
2211         ast_cli(a->fd, "Prefix: %s\n", prefix);
2212         if (ast_sockaddr_isnull(&http_desc.old_address)) {
2213                 ast_cli(a->fd, "Server Disabled\n\n");
2214         } else {
2215                 ast_cli(a->fd, "Server Enabled and Bound to %s\n\n",
2216                         ast_sockaddr_stringify(&http_desc.old_address));
2217                 if (http_tls_cfg.enabled) {
2218                         ast_cli(a->fd, "HTTPS Server Enabled and Bound to %s\n\n",
2219                                 ast_sockaddr_stringify(&https_desc.old_address));
2220                 }
2221         }
2222
2223         ast_cli(a->fd, "Enabled URI's:\n");
2224         AST_RWLIST_RDLOCK(&uris);
2225         if (AST_RWLIST_EMPTY(&uris)) {
2226                 ast_cli(a->fd, "None.\n");
2227         } else {
2228                 AST_RWLIST_TRAVERSE(&uris, urih, entry)
2229                         ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
2230         }
2231         AST_RWLIST_UNLOCK(&uris);
2232
2233         ast_cli(a->fd, "\nEnabled Redirects:\n");
2234         AST_RWLIST_RDLOCK(&uri_redirects);
2235         AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
2236                 ast_cli(a->fd, "  %s => %s\n", redirect->target, redirect->dest);
2237         if (AST_RWLIST_EMPTY(&uri_redirects)) {
2238                 ast_cli(a->fd, "  None.\n");
2239         }
2240         AST_RWLIST_UNLOCK(&uri_redirects);
2241
2242         return CLI_SUCCESS;
2243 }
2244
2245 int ast_http_reload(void)
2246 {
2247         return __ast_http_load(1);
2248 }
2249
2250 static struct ast_cli_entry cli_http[] = {
2251         AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"),
2252 };
2253
2254 static void http_shutdown(void)
2255 {
2256         struct http_uri_redirect *redirect;
2257         ast_cli_unregister_multiple(cli_http, ARRAY_LEN(cli_http));
2258
2259         ast_tcptls_server_stop(&http_desc);
2260         if (http_tls_cfg.enabled) {
2261                 ast_tcptls_server_stop(&https_desc);
2262         }
2263         ast_free(http_tls_cfg.certfile);
2264         ast_free(http_tls_cfg.pvtfile);
2265         ast_free(http_tls_cfg.cipher);
2266
2267         ast_http_uri_unlink(&statusuri);
2268         ast_http_uri_unlink(&staticuri);
2269
2270         AST_RWLIST_WRLOCK(&uri_redirects);
2271         while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
2272                 ast_free(redirect);
2273         }
2274         AST_RWLIST_UNLOCK(&uri_redirects);
2275 }
2276
2277 int ast_http_init(void)
2278 {
2279         ast_http_uri_link(&statusuri);
2280         ast_http_uri_link(&staticuri);
2281         ast_cli_register_multiple(cli_http, ARRAY_LEN(cli_http));
2282         ast_register_atexit(http_shutdown);
2283
2284         return __ast_http_load(0);
2285 }