Asterisk - The Open Source Telephony Project GIT-master-80b953f
Loading...
Searching...
No Matches
http.c
Go to the documentation of this file.
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#include <time.h>
48#include <sys/time.h>
49#include <sys/stat.h>
50#include <signal.h>
51#include <fcntl.h>
52
53#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
54#include "asterisk/acl.h"
55#include "asterisk/cli.h"
56#include "asterisk/tcptls.h"
57#include "asterisk/http.h"
58#include "asterisk/utils.h"
59#include "asterisk/strings.h"
60#include "asterisk/config.h"
63#include "asterisk/manager.h"
64#include "asterisk/module.h"
65#include "asterisk/astobj2.h"
66#include "asterisk/netsock2.h"
67#include "asterisk/json.h"
68
69#define MAX_PREFIX 80
70#define DEFAULT_PORT 8088
71#define DEFAULT_TLS_PORT 8089
72#define DEFAULT_SESSION_LIMIT 100
73/*! (ms) Idle time waiting for data. */
74#define DEFAULT_SESSION_INACTIVITY 30000
75/*! (ms) Min timeout for initial HTTP request to start coming in. */
76#define MIN_INITIAL_REQUEST_TIMEOUT 10000
77/*! (ms) Idle time between HTTP requests */
78#define DEFAULT_SESSION_KEEP_ALIVE 15000
79/*! Max size for the http server name */
80#define MAX_SERVER_NAME_LENGTH 128
81/*! Max size for the http response header */
82#define DEFAULT_RESPONSE_HEADER_LENGTH 512
83
84/*! Maximum application/json or application/x-www-form-urlencoded body content length. */
85#if !defined(LOW_MEMORY)
86#define MAX_CONTENT_LENGTH 40960
87#else
88#define MAX_CONTENT_LENGTH 1024
89#endif /* !defined(LOW_MEMORY) */
90
91/*! Initial response body length. */
92#if !defined(LOW_MEMORY)
93#define INITIAL_RESPONSE_BODY_BUFFER 1024
94#else
95#define INITIAL_RESPONSE_BODY_BUFFER 512
96#endif /* !defined(LOW_MEMORY) */
97
98/*! Maximum line length for HTTP requests. */
99#if !defined(LOW_MEMORY)
100#define MAX_HTTP_LINE_LENGTH 4096
101#else
102#define MAX_HTTP_LINE_LENGTH 1024
103#endif /* !defined(LOW_MEMORY) */
104
106
110static int session_count = 0;
111
113
114static void *httpd_helper_thread(void *arg);
115
116/*!
117 * For standard configuration we have up to two accepting threads,
118 * one for http, one for https. If TEST_FRAMEWORK is enabled it's
119 * possible to have more than one running http server.
120 */
126
127/*!
128 * The default configured HTTP server
129 */
131
133 .accept_fd = -1,
134 .master = AST_PTHREADT_NULL,
135 .tls_cfg = &http_tls_cfg,
136 .poll_timeout = -1,
137 .name = "https server",
138 .accept_fn = ast_tcptls_server_root,
139 .worker_fn = httpd_helper_thread,
140};
141
142static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri); /*!< list of supported handlers */
143
144/* all valid URIs must be prepended by the string in prefix. */
145static char prefix[MAX_PREFIX];
148
149/*! \brief Limit the kinds of files we're willing to serve up */
150static struct {
151 const char *ext;
152 const char *mtype;
153} mimetypes[] = {
154 { "png", "image/png" },
155 { "xml", "text/xml" },
156 { "jpg", "image/jpeg" },
157 { "js", "application/x-javascript" },
158 { "wav", "audio/x-wav" },
159 { "mp3", "audio/mpeg" },
160 { "svg", "image/svg+xml" },
161 { "svgz", "image/svg+xml" },
162 { "gif", "image/gif" },
163 { "html", "text/html" },
164 { "htm", "text/html" },
165 { "css", "text/css" },
166 { "cnf", "text/plain" },
167 { "cfg", "text/plain" },
168 { "bin", "application/octet-stream" },
169 { "sbn", "application/octet-stream" },
170 { "ld", "application/octet-stream" },
172
178
180
181/*! \brief Per-path ACL restriction */
187
189
191
192static int check_restriction_acl(struct ast_tcptls_session_instance *ser, const char *uri);
193
194static const struct ast_cfhttp_methods_text {
196 const char *text;
198 { AST_HTTP_UNKNOWN, "UNKNOWN" },
199 { AST_HTTP_GET, "GET" },
200 { AST_HTTP_POST, "POST" },
201 { AST_HTTP_HEAD, "HEAD" },
202 { AST_HTTP_PUT, "PUT" },
203 { AST_HTTP_DELETE, "DELETE" },
204 { AST_HTTP_OPTIONS, "OPTIONS" },
206
208{
209 int x;
210
211 for (x = 0; x < ARRAY_LEN(ast_http_methods_text); x++) {
213 return ast_http_methods_text[x].text;
214 }
215 }
216
217 return NULL;
218}
219
221{
222 int x;
223
224 for (x = 0; x < ARRAY_LEN(ast_http_methods_text); x++) {
227 }
228 }
229
230 return AST_HTTP_UNKNOWN;
231}
232
233const char *ast_http_ftype2mtype(const char *ftype)
234{
235 int x;
236
237 if (ftype) {
238 for (x = 0; x < ARRAY_LEN(mimetypes); x++) {
239 if (!strcasecmp(ftype, mimetypes[x].ext)) {
240 return mimetypes[x].mtype;
241 }
242 }
243 }
244 return NULL;
245}
246
247uint32_t ast_http_manid_from_vars(struct ast_variable *headers)
248{
249 uint32_t mngid = 0;
250 struct ast_variable *v, *cookies;
251
252 cookies = ast_http_get_cookies(headers);
253 for (v = cookies; v; v = v->next) {
254 if (!strcasecmp(v->name, "mansession_id")) {
255 sscanf(v->value, "%30x", &mngid);
256 break;
257 }
258 }
259 ast_variables_destroy(cookies);
260 return mngid;
261}
262
263void ast_http_prefix(char *buf, int len)
264{
265 if (buf) {
267 }
268}
269
271 const struct ast_http_uri *urih, const char *uri,
272 enum ast_http_method method, struct ast_variable *get_vars,
273 struct ast_variable *headers)
274{
275 char *path;
276 const char *ftype;
277 const char *mtype;
278 char wkspace[80];
279 struct stat st;
280 int len;
281 int fd;
282 struct ast_str *http_header;
283 struct timeval tv;
284 struct ast_tm tm;
285 char timebuf[80], etag[23];
286 struct ast_variable *v;
287 int not_modified = 0;
288
290 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
291 return 0;
292 }
293
294 /* Yuck. I'm not really sold on this, but if you don't deliver static content it
295 * makes your configuration substantially more challenging, but this seems like a
296 * rather irritating feature creep on Asterisk.
297 *
298 * XXX: It is not clear to me what this comment means or if it is any longer
299 * relevant. */
300 if (ast_strlen_zero(uri)) {
301 goto out403;
302 }
303
304 /* Disallow any funny filenames at all (checking first character only??) */
305 if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0])) {
306 goto out403;
307 }
308
309 if (strstr(uri, "/..")) {
310 goto out403;
311 }
312
313 if ((ftype = strrchr(uri, '.'))) {
314 ftype++;
315 }
316
317 if (!(mtype = ast_http_ftype2mtype(ftype))) {
318 snprintf(wkspace, sizeof(wkspace), "text/%s", S_OR(ftype, "plain"));
319 mtype = wkspace;
320 }
321
322 /* Cap maximum length */
323 if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
324 goto out403;
325 }
326
327 path = ast_alloca(len);
328 sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
329 if (stat(path, &st)) {
330 goto out404;
331 }
332
333 if (S_ISDIR(st.st_mode)) {
334 goto out404;
335 }
336
337 if (strstr(path, "/private/") && !astman_is_authed(ast_http_manid_from_vars(headers))) {
338 goto out403;
339 }
340
341 fd = open(path, O_RDONLY);
342 if (fd < 0) {
343 goto out403;
344 }
345
346 /* make "Etag:" http header value */
347 snprintf(etag, sizeof(etag), "\"%ld\"", (long)st.st_mtime);
348
349 /* make "Last-Modified:" http header value */
350 tv.tv_sec = st.st_mtime;
351 tv.tv_usec = 0;
352 ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&tv, &tm, "GMT"));
353
354 /* check received "If-None-Match" request header and Etag value for file */
355 for (v = headers; v; v = v->next) {
356 if (!strcasecmp(v->name, "If-None-Match")) {
357 if (!strcasecmp(v->value, etag)) {
358 not_modified = 1;
359 }
360 break;
361 }
362 }
363
364 http_header = ast_str_create(255);
365 if (!http_header) {
367 ast_http_error(ser, 500, "Server Error", "Out of memory");
368 close(fd);
369 return 0;
370 }
371
372 ast_str_set(&http_header, 0, "Content-type: %s\r\n"
373 "ETag: %s\r\n"
374 "Last-Modified: %s\r\n",
375 mtype,
376 etag,
377 timebuf);
378
379 /* ast_http_send() frees http_header, so we don't need to do it before returning */
380 if (not_modified) {
381 ast_http_send(ser, method, 304, "Not Modified", http_header, NULL, 0, 1);
382 } else {
383 ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 1); /* static content flag is set */
384 }
385 close(fd);
386 return 0;
387
388out404:
389 ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
390 return 0;
391
392out403:
394 ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL.");
395 return 0;
396}
397
398static void str_append_escaped(struct ast_str **str, const char *in)
399{
400 const char *cur = in;
401
402 while(*cur) {
403 switch (*cur) {
404 case '<':
405 ast_str_append(str, 0, "&lt;");
406 break;
407 case '>':
408 ast_str_append(str, 0, "&gt;");
409 break;
410 case '&':
411 ast_str_append(str, 0, "&amp;");
412 break;
413 case '"':
414 ast_str_append(str, 0, "&quot;");
415 break;
416 default:
417 ast_str_append(str, 0, "%c", *cur);
418 break;
419 }
420 cur++;
421 }
422
423 return;
424}
425
427 const struct ast_http_uri *urih, const char *uri,
428 enum ast_http_method method, struct ast_variable *get_vars,
429 struct ast_variable *headers)
430{
431 struct ast_str *out;
432 struct ast_variable *v, *cookies = NULL;
433
435 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
436 return 0;
437 }
438
439 out = ast_str_create(512);
440 if (!out) {
442 ast_http_error(ser, 500, "Server Error", "Out of memory");
443 return 0;
444 }
445
447 "<html><title>Asterisk HTTP Status</title>\r\n"
448 "<body bgcolor=\"#ffffff\">\r\n"
449 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
450 "<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
451
452 ast_str_append(&out, 0, "<tr><td><i>Server</i></td><td><b>%s</b></td></tr>\r\n", http_server_name);
453 ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
454 if (global_http_server) {
455 ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
457 }
458 if (http_tls_cfg.enabled) {
459 ast_str_append(&out, 0, "<tr><td><i>TLS Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
461 }
462 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
463 for (v = get_vars; v; v = v->next) {
464 ast_str_append(&out, 0, "<tr><td><i>Submitted GET Variable '");
466 ast_str_append(&out, 0, "'</i></td><td>");
468 ast_str_append(&out, 0, "</td></tr>\r\n");
469 }
470 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
471
472 cookies = ast_http_get_cookies(headers);
473 for (v = cookies; v; v = v->next) {
474 ast_str_append(&out, 0, "<tr><td><i>Cookie '");
476 ast_str_append(&out, 0, "'</i></td><td>");
478 ast_str_append(&out, 0, "</td></tr>\r\n");
479 }
480 ast_variables_destroy(cookies);
481
482 ast_str_append(&out, 0, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body></html>\r\n");
483 ast_http_send(ser, method, 200, NULL, NULL, out, 0, 0);
484 return 0;
485}
486
487static struct ast_http_uri status_uri = {
489 .description = "Asterisk HTTP General Status",
490 .uri = "httpstatus",
491 .has_subtree = 0,
492 .data = NULL,
493 .key = __FILE__,
494};
495
496static struct ast_http_uri static_uri = {
498 .description = "Asterisk HTTP Static Delivery",
499 .uri = "static",
500 .has_subtree = 1,
501 .data = NULL,
502 .key= __FILE__,
503};
504
506 /*! TRUE if the HTTP request has a body. */
508 /*! TRUE if the HTTP request body has been read. */
510 /*! TRUE if the HTTP request must close when completed. */
512};
513
514/*! HTTP tcptls worker_fn private data. */
516 /*! Body length or -1 if chunked. Valid if HTTP_FLAG_HAS_BODY is TRUE. */
518 /*! HTTP body tracking flags */
520};
521
523 enum ast_http_method method, int status_code, const char *status_title,
524 struct ast_str *http_header, struct ast_str *out, int fd,
525 unsigned int static_content)
526{
527 struct timeval now = ast_tvnow();
528 struct ast_tm tm;
529 char timebuf[80];
530 char buf[256];
531 int len;
532 int content_length = 0;
533 int close_connection;
534 struct ast_str *server_header_field = ast_str_create(MAX_SERVER_NAME_LENGTH);
535 int send_content;
536
537 if (!ser || !server_header_field) {
538 /* The connection is not open. */
539 ast_free(http_header);
540 ast_free(out);
541 ast_free(server_header_field);
542 return;
543 }
544
546 ast_str_set(&server_header_field,
547 0,
548 "Server: %s\r\n",
550 }
551
552 /*
553 * We shouldn't be sending non-final status codes to this
554 * function because we may close the connection before
555 * returning.
556 */
557 ast_assert(200 <= status_code);
558
559 if (session_keep_alive <= 0) {
560 close_connection = 1;
561 } else {
563
564 request = ser->private_data;
565 if (!request
567 || ast_http_body_discard(ser)) {
568 close_connection = 1;
569 } else {
570 close_connection = 0;
571 }
572 }
573
574 ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&now, &tm, "GMT"));
575
576 /* calc content length */
577 if (out) {
578 content_length += ast_str_strlen(out);
579 }
580
581 if (fd) {
582 content_length += lseek(fd, 0, SEEK_END);
583 lseek(fd, 0, SEEK_SET);
584 }
585
586 send_content = method != AST_HTTP_HEAD || status_code >= 400;
587
588 /* send http header */
590 "HTTP/1.1 %d %s\r\n"
591 "%s"
592 "Date: %s\r\n"
593 "%s"
594 "%s"
595 "%s"
596 "Content-Length: %d\r\n"
597 "\r\n"
598 "%s",
599 status_code, status_title ? status_title : "OK",
600 ast_str_buffer(server_header_field),
601 timebuf,
602 close_connection ? "Connection: close\r\n" : "",
603 static_content ? "" : "Cache-Control: no-cache, no-store\r\n",
604 http_header ? ast_str_buffer(http_header) : "",
605 content_length,
606 send_content && out && ast_str_strlen(out) ? ast_str_buffer(out) : ""
607 ) <= 0) {
608 ast_debug(1, "ast_iostream_printf() failed: %s\n", strerror(errno));
609 close_connection = 1;
610 } else if (send_content && fd) {
611 /* send file content */
612 while ((len = read(fd, buf, sizeof(buf))) > 0) {
613 if (ast_iostream_write(ser->stream, buf, len) != len) {
614 ast_debug(1, "ast_iostream_write() failed: %s\n", strerror(errno));
615 close_connection = 1;
616 break;
617 }
618 }
619 }
620
621 ast_free(http_header);
622 ast_free(out);
623 ast_free(server_header_field);
624
625 if (close_connection) {
626 ast_debug(1, "HTTP closing session. status_code:%d\n", status_code);
628 } else {
629 ast_debug(1, "HTTP keeping session open. status_code:%d\n", status_code);
630 }
631}
632
634 const char *status_title, struct ast_str *http_header_data, const char *text)
635{
636 char server_name[MAX_SERVER_NAME_LENGTH];
637 struct ast_str *server_address = ast_str_create(MAX_SERVER_NAME_LENGTH);
639
640 if (!http_header_data || !server_address || !out) {
641 ast_free(http_header_data);
642 ast_free(server_address);
643 ast_free(out);
644 if (ser) {
645 ast_debug(1, "HTTP closing session. OOM.\n");
647 }
648 return;
649 }
650
652 ast_xml_escape(http_server_name, server_name, sizeof(server_name));
653 ast_str_set(&server_address,
654 0,
655 "<address>%s</address>\r\n",
656 server_name);
657 }
658
660 0,
661 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
662 "<html><head>\r\n"
663 "<title>%d %s</title>\r\n"
664 "</head><body>\r\n"
665 "<h1>%s</h1>\r\n"
666 "<p>%s</p>\r\n"
667 "<hr />\r\n"
668 "%s"
669 "</body></html>\r\n",
670 status_code,
671 status_title,
672 status_title,
673 text ? text : "",
674 ast_str_buffer(server_address));
675
676 ast_free(server_address);
677
678 ast_http_send(ser,
680 status_code,
681 status_title,
682 http_header_data,
683 out,
684 0,
685 0);
686}
687
688void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm,
689 const unsigned long nonce, const unsigned long opaque, int stale,
690 const char *text)
691{
692 int status_code = 401;
693 char *status_title = "Unauthorized";
694 struct ast_str *http_header_data = ast_str_create(DEFAULT_RESPONSE_HEADER_LENGTH);
695
696 if (http_header_data) {
697 ast_str_set(&http_header_data,
698 0,
699 "WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n"
700 "Content-type: text/html\r\n",
701 realm ? realm : "Asterisk",
702 nonce,
703 opaque,
704 stale ? ", stale=true" : "");
705 }
706
708 status_code,
709 status_title,
710 http_header_data,
711 text);
712}
713
714void ast_http_error(struct ast_tcptls_session_instance *ser, int status_code,
715 const char *status_title, const char *text)
716{
717 struct ast_str *http_header_data = ast_str_create(DEFAULT_RESPONSE_HEADER_LENGTH);
718
719 if (http_header_data) {
720 ast_str_set(&http_header_data, 0, "Content-type: text/html\r\n");
721 }
722
724 status_code,
725 status_title,
726 http_header_data,
727 text);
728}
729
730/*!
731 * \brief Link the new uri into the list.
732 *
733 * They are sorted by length of
734 * the string, not alphabetically. Duplicate entries are not replaced,
735 * but the insertion order (using <= and not just <) makes sure that
736 * more recent insertions hide older ones.
737 * On a lookup, we just scan the list and stop at the first matching entry.
738 */
740{
741 struct ast_http_uri *uri;
742 int len = strlen(urih->uri);
743
745
746 urih->prefix = prefix;
747
748 if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) {
751 return 0;
752 }
753
755 if (AST_RWLIST_NEXT(uri, entry) &&
756 strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len) {
759
760 return 0;
761 }
762 }
763
765
767
768 return 0;
769}
770
777
779{
780 struct ast_http_uri *urih;
783 if (!strcmp(urih->key, key)) {
785 if (urih->dmallocd) {
786 ast_free(urih->data);
787 }
788 if (urih->mallocd) {
789 ast_free(urih);
790 }
791 }
792 }
795}
796
797/*!
798 * \brief Retrieves the header with the given field name.
799 *
800 * \param headers Headers to search.
801 * \param field_name Name of the header to find.
802 * \return Associated header value.
803 * \retval NULL if header is not present.
804 */
805static const char *get_header(struct ast_variable *headers, const char *field_name)
806{
807 struct ast_variable *v;
808
809 for (v = headers; v; v = v->next) {
810 if (!strcasecmp(v->name, field_name)) {
811 return v->value;
812 }
813 }
814 return NULL;
815}
816
817/*!
818 * \brief Retrieves the content type specified in the "Content-Type" header.
819 *
820 * This function only returns the "type/subtype" and any trailing parameter is
821 * not included.
822 *
823 * \note the return value is an allocated string that needs to be freed.
824 *
825 * \return the content type/subtype
826 * \retval NULL if the header is not found.
827 */
828static char *get_content_type(struct ast_variable *headers)
829{
830 const char *content_type = get_header(headers, "Content-Type");
831 const char *param;
832 size_t size;
833
834 if (!content_type) {
835 return NULL;
836 }
837
838 param = strchr(content_type, ';');
839 size = param ? param - content_type : strlen(content_type);
840
841 return ast_strndup(content_type, size);
842}
843
844/*!
845 * \brief Returns the value of the Content-Length header.
846 *
847 * \param headers HTTP headers.
848 *
849 * \return length Value of the Content-Length header.
850 * \retval 0 if header is not present.
851 * \retval -1 if header is invalid.
852 */
853static int get_content_length(struct ast_variable *headers)
854{
855 const char *content_length = get_header(headers, "Content-Length");
856 int length;
857
858 if (!content_length) {
859 /* Missing content length; assume zero */
860 return 0;
861 }
862
863 length = 0;
864 if (sscanf(content_length, "%30d", &length) != 1) {
865 /* Invalid Content-Length value */
866 length = -1;
867 }
868 return length;
869}
870
871/*!
872 * \brief Returns the value of the Transfer-Encoding header.
873 *
874 * \param headers HTTP headers.
875 * \return string Value of the Transfer-Encoding header.
876 * \retval NULL if header is not present.
877 */
878static const char *get_transfer_encoding(struct ast_variable *headers)
879{
880 return get_header(headers, "Transfer-Encoding");
881}
882
883/*!
884 * \internal
885 * \brief Determine if the HTTP peer wants the connection closed.
886 *
887 * \param headers List of HTTP headers
888 *
889 * \retval 0 keep connection open.
890 * \retval -1 close connection.
891 */
892static int http_check_connection_close(struct ast_variable *headers)
893{
894 const char *connection = get_header(headers, "Connection");
895 int close_connection = 0;
896
897 if (connection && !strcasecmp(connection, "close")) {
898 close_connection = -1;
899 }
900 return close_connection;
901}
902
909
910/*!
911 * \internal
912 * \brief Initialize the request tracking information in case of early failure.
913 * \since 12.4.0
914 *
915 * \param request Request tracking information.
916 */
924
925/*!
926 * \internal
927 * \brief Setup the HTTP request tracking information.
928 * \since 12.4.0
929 *
930 * \param ser HTTP TCP/TLS session object.
931 * \param headers List of HTTP headers.
932 *
933 * \retval 0 on success.
934 * \retval -1 on error.
935 */
937{
939 const char *transfer_encoding;
940
944
945 transfer_encoding = get_transfer_encoding(headers);
946 if (transfer_encoding && !strcasecmp(transfer_encoding, "chunked")) {
947 request->body_length = -1;
949 return 0;
950 }
951
952 request->body_length = get_content_length(headers);
953 if (0 < request->body_length) {
955 } else if (request->body_length < 0) {
956 /* Invalid Content-Length */
958 ast_http_error(ser, 400, "Bad Request", "Invalid Content-Length in request!");
959 return -1;
960 }
961 return 0;
962}
963
965{
967
968 request = ser->private_data;
971 /* No body to read. */
972 return;
973 }
975 if (!read_success) {
977 }
978}
979
980/*!
981 * \internal
982 * \brief Read the next length bytes from the HTTP body.
983 * \since 12.4.0
984 *
985 * \param ser HTTP TCP/TLS session object.
986 * \param buf Where to put the contents reading.
987 * \param length How much contents to read.
988 * \param what_getting Name of the contents reading.
989 *
990 * \retval 0 on success.
991 * \retval -1 on error.
992 */
993static int http_body_read_contents(struct ast_tcptls_session_instance *ser, char *buf, int length, const char *what_getting)
994{
995 int res;
996 int total = 0;
997
998 /* Stream is in exclusive mode so we get it all if possible. */
999 while (total != length) {
1000 res = ast_iostream_read(ser->stream, buf + total, length - total);
1001 if (res <= 0) {
1002 break;
1003 }
1004
1005 total += res;
1006 }
1007
1008 if (total != length) {
1009 ast_log(LOG_WARNING, "Wrong HTTP content read. Request %s (Wanted %d, Read %d)\n",
1010 what_getting, length, res);
1011 return -1;
1012 }
1013
1014 return 0;
1015}
1016
1017/*!
1018 * \internal
1019 * \brief Read and discard the next length bytes from the HTTP body.
1020 * \since 12.4.0
1021 *
1022 * \param ser HTTP TCP/TLS session object.
1023 * \param length How much contents to discard
1024 * \param what_getting Name of the contents discarding.
1025 *
1026 * \retval 0 on success.
1027 * \retval -1 on error.
1028 */
1029static int http_body_discard_contents(struct ast_tcptls_session_instance *ser, int length, const char *what_getting)
1030{
1031 ssize_t res;
1032
1033 res = ast_iostream_discard(ser->stream, length);
1034 if (res < length) {
1035 ast_log(LOG_WARNING, "Short HTTP request %s (Wanted %d but got %zd)\n",
1036 what_getting, length, res);
1037 return -1;
1038 }
1039 return 0;
1040}
1041
1042/*!
1043 * \internal
1044 * \brief decode chunked mode hexadecimal value
1045 *
1046 * \param s string to decode
1047 * \param len length of string
1048 *
1049 * \return length on success.
1050 * \retval -1 on error.
1051 */
1052static int chunked_atoh(const char *s, int len)
1053{
1054 int value = 0;
1055 char c;
1056
1057 if (*s < '0') {
1058 /* zero value must be 0\n not just \n */
1059 return -1;
1060 }
1061
1062 while (len--) {
1063 c = *s++;
1064 if (c == '\x0D') {
1065 return value;
1066 }
1067 if (c == ';') {
1068 /* We have a chunk-extension that we don't care about. */
1069 while (len--) {
1070 if (*s++ == '\x0D') {
1071 return value;
1072 }
1073 }
1074 break;
1075 }
1076 value <<= 4;
1077 if (c >= '0' && c <= '9') {
1078 value += c - '0';
1079 continue;
1080 }
1081 if (c >= 'a' && c <= 'f') {
1082 value += 10 + c - 'a';
1083 continue;
1084 }
1085 if (c >= 'A' && c <= 'F') {
1086 value += 10 + c - 'A';
1087 continue;
1088 }
1089 /* invalid character */
1090 return -1;
1091 }
1092 /* end of string */
1093 return -1;
1094}
1095
1096/*!
1097 * \internal
1098 * \brief Read and convert the chunked body header length.
1099 * \since 12.4.0
1100 *
1101 * \param ser HTTP TCP/TLS session object.
1102 *
1103 * \return length Size of chunk to expect.
1104 * \retval -1 on error.
1105 */
1107{
1108 int length;
1109 char header_line[MAX_HTTP_LINE_LENGTH];
1110
1111 /* get the line of hexadecimal giving chunk-size w/ optional chunk-extension */
1112 if (ast_iostream_gets(ser->stream, header_line, sizeof(header_line)) <= 0) {
1113 ast_log(LOG_WARNING, "Short HTTP read of chunked header\n");
1114 return -1;
1115 }
1116 length = chunked_atoh(header_line, strlen(header_line));
1117 if (length < 0) {
1118 ast_log(LOG_WARNING, "Invalid HTTP chunk size\n");
1119 return -1;
1120 }
1121 return length;
1122}
1123
1124/*!
1125 * \internal
1126 * \brief Read and check the chunk contents line termination.
1127 * \since 12.4.0
1128 *
1129 * \param ser HTTP TCP/TLS session object.
1130 *
1131 * \retval 0 on success.
1132 * \retval -1 on error.
1133 */
1135{
1136 int res;
1137 char chunk_sync[2];
1138
1139 /* Stay in fread until get the expected CRLF or timeout. */
1140 res = ast_iostream_read(ser->stream, chunk_sync, sizeof(chunk_sync));
1141 if (res < sizeof(chunk_sync)) {
1142 ast_log(LOG_WARNING, "Short HTTP chunk sync read (Wanted %zu)\n",
1143 sizeof(chunk_sync));
1144 return -1;
1145 }
1146 if (chunk_sync[0] != 0x0D || chunk_sync[1] != 0x0A) {
1147 ast_log(LOG_WARNING, "HTTP chunk sync bytes wrong (0x%02hhX, 0x%02hhX)\n",
1148 (unsigned char) chunk_sync[0], (unsigned char) chunk_sync[1]);
1149 return -1;
1150 }
1151
1152 return 0;
1153}
1154
1155/*!
1156 * \internal
1157 * \brief Read and discard any chunked trailer entity-header lines.
1158 * \since 12.4.0
1159 *
1160 * \param ser HTTP TCP/TLS session object.
1161 *
1162 * \retval 0 on success.
1163 * \retval -1 on error.
1164 */
1166{
1167 char header_line[MAX_HTTP_LINE_LENGTH];
1168
1169 for (;;) {
1170 if (ast_iostream_gets(ser->stream, header_line, sizeof(header_line)) <= 0) {
1171 ast_log(LOG_WARNING, "Short HTTP read of chunked trailer header\n");
1172 return -1;
1173 }
1174
1175 /* Trim trailing whitespace */
1176 ast_trim_blanks(header_line);
1177 if (ast_strlen_zero(header_line)) {
1178 /* A blank line ends the chunked-body */
1179 break;
1180 }
1181 }
1182 return 0;
1183}
1184
1186{
1188
1189 request = ser->private_data;
1192 /* No body to read or it has already been read. */
1193 return 0;
1194 }
1196
1197 ast_debug(1, "HTTP discarding unused request body\n");
1198
1199 ast_assert(request->body_length != 0);
1200 if (0 < request->body_length) {
1201 if (http_body_discard_contents(ser, request->body_length, "body")) {
1203 return -1;
1204 }
1205 return 0;
1206 }
1207
1208 /* parse chunked-body */
1209 for (;;) {
1210 int length;
1211
1212 length = http_body_get_chunk_length(ser);
1213 if (length < 0) {
1215 return -1;
1216 }
1217 if (length == 0) {
1218 /* parsed last-chunk */
1219 break;
1220 }
1221
1222 if (http_body_discard_contents(ser, length, "chunk-data")
1225 return -1;
1226 }
1227 }
1228
1229 /* Read and discard any trailer entity-header lines. */
1232 return -1;
1233 }
1234 return 0;
1235}
1236
1237/*!
1238 * \brief Returns the contents (body) of the HTTP request
1239 *
1240 * \param return_length ptr to int that returns content length
1241 * \param ser HTTP TCP/TLS session object
1242 * \param headers List of HTTP headers
1243 * \return ptr to content (zero terminated)
1244 * \retval NULL on failure
1245 * \note Since returned ptr is malloc'd, it should be free'd by caller
1246 */
1247static char *ast_http_get_contents(int *return_length,
1248 struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
1249{
1251 int content_length;
1252 int bufsize;
1253 char *buf;
1254
1255 request = ser->private_data;
1256 if (!ast_test_flag(&request->flags, HTTP_FLAG_HAS_BODY)) {
1257 /* no content - not an error */
1258 return NULL;
1259 }
1261 /* Already read the body. Cannot read again. Assume no content. */
1262 ast_assert(0);
1263 return NULL;
1264 }
1266
1267 ast_debug(2, "HTTP consuming request body\n");
1268
1269 ast_assert(request->body_length != 0);
1270 if (0 < request->body_length) {
1271 /* handle regular non-chunked content */
1272 content_length = request->body_length;
1273 if (content_length > MAX_CONTENT_LENGTH) {
1274 ast_log(LOG_WARNING, "Excessively long HTTP content. (%d > %d)\n",
1275 content_length, MAX_CONTENT_LENGTH);
1277 errno = EFBIG;
1278 return NULL;
1279 }
1280 buf = ast_malloc(content_length + 1);
1281 if (!buf) {
1282 /* Malloc sets ENOMEM */
1284 return NULL;
1285 }
1286
1287 if (http_body_read_contents(ser, buf, content_length, "body")) {
1289 errno = EIO;
1290 ast_free(buf);
1291 return NULL;
1292 }
1293
1294 buf[content_length] = 0;
1295 *return_length = content_length;
1296 return buf;
1297 }
1298
1299 /* pre-allocate buffer */
1300 bufsize = 250;
1301 buf = ast_malloc(bufsize);
1302 if (!buf) {
1304 return NULL;
1305 }
1306
1307 /* parse chunked-body */
1308 content_length = 0;
1309 for (;;) {
1310 int chunk_length;
1311
1312 chunk_length = http_body_get_chunk_length(ser);
1313 if (chunk_length < 0) {
1315 errno = EIO;
1316 ast_free(buf);
1317 return NULL;
1318 }
1319 if (chunk_length == 0) {
1320 /* parsed last-chunk */
1321 break;
1322 }
1323 if (content_length + chunk_length > MAX_CONTENT_LENGTH) {
1325 "Excessively long HTTP accumulated chunked body. (%d + %d > %d)\n",
1326 content_length, chunk_length, MAX_CONTENT_LENGTH);
1328 errno = EFBIG;
1329 ast_free(buf);
1330 return NULL;
1331 }
1332
1333 /* insure buffer is large enough +1 */
1334 if (content_length + chunk_length >= bufsize) {
1335 char *new_buf;
1336
1337 /* Increase bufsize until it can handle the expected data. */
1338 do {
1339 bufsize *= 2;
1340 } while (content_length + chunk_length >= bufsize);
1341
1342 new_buf = ast_realloc(buf, bufsize);
1343 if (!new_buf) {
1345 ast_free(buf);
1346 return NULL;
1347 }
1348 buf = new_buf;
1349 }
1350
1351 if (http_body_read_contents(ser, buf + content_length, chunk_length, "chunk-data")
1354 errno = EIO;
1355 ast_free(buf);
1356 return NULL;
1357 }
1358 content_length += chunk_length;
1359 }
1360
1361 /*
1362 * Read and discard any trailer entity-header lines
1363 * which we don't care about.
1364 *
1365 * XXX In the future we may need to add the trailer headers
1366 * to the passed in headers list rather than discarding them.
1367 */
1370 errno = EIO;
1371 ast_free(buf);
1372 return NULL;
1373 }
1374
1375 buf[content_length] = 0;
1376 *return_length = content_length;
1377 return buf;
1378}
1379
1381 struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
1382{
1383 int content_length = 0;
1384 struct ast_json *body;
1385 RAII_VAR(char *, buf, NULL, ast_free);
1386 RAII_VAR(char *, type, get_content_type(headers), ast_free);
1387
1388 /* Use errno to distinguish errors from no body */
1389 errno = 0;
1390
1391 if (ast_strlen_zero(type) || strcasecmp(type, "application/json")) {
1392 /* Content type is not JSON. Don't read the body. */
1393 return NULL;
1394 }
1395
1396 buf = ast_http_get_contents(&content_length, ser, headers);
1397 if (!buf || !content_length) {
1398 /*
1399 * errno already set
1400 * or it is not an error to have zero content
1401 */
1402 return NULL;
1403 }
1404
1405 body = ast_json_load_buf(buf, content_length, NULL);
1406 if (!body) {
1407 /* Failed to parse JSON; treat as an I/O error */
1408 errno = EIO;
1409 return NULL;
1410 }
1411
1412 return body;
1413}
1414
1415/*
1416 * get post variables from client Request Entity-Body, if content type is
1417 * application/x-www-form-urlencoded
1418 */
1419struct ast_variable *ast_http_parse_post_form(char *buf, int content_length,
1420 const char *content_type)
1421{
1422 struct ast_variable *v, *post_vars=NULL, *prev = NULL;
1423 char *var, *val;
1424
1425 /* Use errno to distinguish errors from no params */
1426 errno = 0;
1427
1428 if (ast_strlen_zero(content_type) ||
1429 strcasecmp(content_type, "application/x-www-form-urlencoded") != 0) {
1430 /* Content type is not form data. Don't read the body. */
1431 return NULL;
1432 }
1433
1434 while ((val = strsep(&buf, "&"))) {
1435 var = strsep(&val, "=");
1436 if (val) {
1438 } else {
1439 val = "";
1440 }
1442 if ((v = ast_variable_new(var, val, ""))) {
1443 if (post_vars) {
1444 prev->next = v;
1445 } else {
1446 post_vars = v;
1447 }
1448 prev = v;
1449 }
1450 }
1451
1452 return post_vars;
1453}
1454
1456 struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
1457{
1458 int content_length = 0;
1459 RAII_VAR(char *, buf, NULL, ast_free);
1460 RAII_VAR(char *, type, get_content_type(headers), ast_free);
1461
1462 /* Use errno to distinguish errors from no params */
1463 errno = 0;
1464
1465 if (ast_strlen_zero(type) ||
1466 strcasecmp(type, "application/x-www-form-urlencoded")) {
1467 /* Content type is not form data. Don't read the body. */
1468 return NULL;
1469 }
1470
1471 buf = ast_http_get_contents(&content_length, ser, headers);
1472 if (!buf || !content_length) {
1473 /*
1474 * errno already set
1475 * or it is not an error to have zero content
1476 */
1477 return NULL;
1478 }
1479
1480 return ast_http_parse_post_form(buf, content_length, type);
1481}
1482
1483static int handle_uri(struct ast_tcptls_session_instance *ser, char *uri,
1484 enum ast_http_method method, struct ast_variable *headers)
1485{
1486 char *c;
1487 int res = 0;
1488 char *params = uri;
1489 struct ast_http_uri *urih = NULL;
1490 int l;
1491 struct ast_variable *get_vars = NULL, *v, *prev = NULL;
1492 struct http_uri_redirect *redirect;
1493
1494 ast_debug(2, "HTTP Request URI is %s \n", uri);
1495
1496 strsep(&params, "?");
1497 /* Extract arguments from the request and store them in variables. */
1498 if (params) {
1499 char *var, *val;
1500
1501 while ((val = strsep(&params, "&"))) {
1502 var = strsep(&val, "=");
1503 if (val) {
1505 } else {
1506 val = "";
1507 }
1509 if ((v = ast_variable_new(var, val, ""))) {
1510 if (get_vars) {
1511 prev->next = v;
1512 } else {
1513 get_vars = v;
1514 }
1515 prev = v;
1516 }
1517 }
1518 }
1519
1520 /* Check path-based ACL restrictions */
1521 if (check_restriction_acl(ser, uri) != 0) {
1523 ast_http_error(ser, 403, "Forbidden", "Access denied by ACL");
1524 goto cleanup;
1525 }
1526
1529 if (!strcasecmp(uri, redirect->target)) {
1530 struct ast_str *http_header = ast_str_create(128);
1531
1532 if (!http_header) {
1534 ast_http_error(ser, 500, "Server Error", "Out of memory");
1535 break;
1536 }
1537 ast_str_set(&http_header, 0, "Location: %s\r\n", redirect->dest);
1538 ast_http_send(ser, method, 302, "Moved Temporarily", http_header, NULL, 0, 0);
1539 break;
1540 }
1541 }
1543 if (redirect) {
1544 goto cleanup;
1545 }
1546
1547 /* We want requests to start with the (optional) prefix and '/' */
1548 l = strlen(prefix);
1549 if (!strncasecmp(uri, prefix, l) && uri[l] == '/') {
1550 uri += l + 1;
1551 /* scan registered uris to see if we match one. */
1553 AST_RWLIST_TRAVERSE(&uris, urih, entry) {
1554 l = strlen(urih->uri);
1555 c = uri + l; /* candidate */
1556 ast_debug(2, "match request [%s] with handler [%s] len %d\n", uri, urih->uri, l);
1557 if (strncasecmp(urih->uri, uri, l) /* no match */
1558 || (*c && *c != '/')) { /* substring */
1559 continue;
1560 }
1561 if (*c == '/') {
1562 c++;
1563 }
1564 if (!*c || urih->has_subtree) {
1565 uri = c;
1566 break;
1567 }
1568 }
1570 }
1571 if (urih) {
1572 ast_debug(1, "Match made with [%s]\n", urih->uri);
1573 if (!urih->no_decode_uri) {
1575 }
1576 res = urih->callback(ser, urih, uri, method, get_vars, headers);
1577 } else {
1578 ast_debug(1, "Request from %s for URI [%s] has no registered handler\n",
1580 ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
1581 }
1582
1583cleanup:
1584 ast_variables_destroy(get_vars);
1585 return res;
1586}
1587
1588static struct ast_variable *parse_cookies(const char *cookies)
1589{
1590 char *parse = ast_strdupa(cookies);
1591 char *cur;
1592 struct ast_variable *vars = NULL, *var;
1593
1594 while ((cur = strsep(&parse, ";"))) {
1595 char *name, *val;
1596
1597 name = val = cur;
1598 strsep(&val, "=");
1599
1601 continue;
1602 }
1603
1604 name = ast_strip(name);
1605 val = ast_strip_quoted(val, "\"", "\"");
1606
1608 continue;
1609 }
1610
1611 ast_debug(1, "HTTP Cookie, Name: '%s' Value: '%s'\n", name, val);
1612
1613 var = ast_variable_new(name, val, __FILE__);
1614 var->next = vars;
1615 vars = var;
1616 }
1617
1618 return vars;
1619}
1620
1621/* get cookie from Request headers */
1623{
1624 struct ast_variable *v, *cookies = NULL;
1625
1626 for (v = headers; v; v = v->next) {
1627 if (!strcasecmp(v->name, "Cookie")) {
1628 ast_variables_destroy(cookies);
1629 cookies = parse_cookies(v->value);
1630 }
1631 }
1632 return cookies;
1633}
1634
1635static struct ast_http_auth *auth_create(const char *userid, const char *password)
1636{
1637 struct ast_http_auth *auth;
1638 size_t userid_len;
1639 size_t password_len;
1640
1641 if (!userid || !password) {
1642 ast_log(LOG_ERROR, "Invalid userid/password\n");
1643 return NULL;
1644 }
1645
1646 userid_len = strlen(userid) + 1;
1647 password_len = strlen(password) + 1;
1648
1649 /* Allocate enough room to store everything in one memory block */
1650 auth = ao2_alloc(sizeof(*auth) + userid_len + password_len, NULL);
1651 if (!auth) {
1652 return NULL;
1653 }
1654
1655 /* Put the userid right after the struct */
1656 auth->userid = (char *)(auth + 1);
1657 strcpy(auth->userid, userid);
1658
1659 /* Put the password right after the userid */
1660 auth->password = auth->userid + userid_len;
1661 strcpy(auth->password, password);
1662
1663 return auth;
1664}
1665
1666#define BASIC_PREFIX "Basic "
1667#define BASIC_LEN 6 /*!< strlen(BASIC_PREFIX) */
1668
1670{
1671 struct ast_variable *v;
1672
1673 for (v = headers; v; v = v->next) {
1674 const char *base64;
1675 char decoded[256] = {};
1676 char *username;
1677 char *password;
1678#ifdef AST_DEVMODE
1679 int cnt;
1680#endif /* AST_DEVMODE */
1681
1682 if (strcasecmp("Authorization", v->name) != 0) {
1683 continue;
1684 }
1685
1688 "Unsupported Authorization scheme\n");
1689 continue;
1690 }
1691
1692 /* Basic auth header parsing. RFC 2617, section 2.
1693 * credentials = "Basic" basic-credentials
1694 * basic-credentials = base64-user-pass
1695 * base64-user-pass = <base64 encoding of user-pass,
1696 * except not limited to 76 char/line>
1697 * user-pass = userid ":" password
1698 */
1699
1700 base64 = v->value + BASIC_LEN;
1701
1702 /* This will truncate "userid:password" lines to
1703 * sizeof(decoded). The array is long enough that this shouldn't
1704 * be a problem */
1705#ifdef AST_DEVMODE
1706 cnt =
1707#endif /* AST_DEVMODE */
1708 ast_base64decode((unsigned char*)decoded, base64,
1709 sizeof(decoded) - 1);
1710 ast_assert(cnt < sizeof(decoded));
1711
1712 /* Split the string at the colon */
1713 password = decoded;
1714 username = strsep(&password, ":");
1715 if (!password) {
1716 ast_log(LOG_WARNING, "Invalid Authorization header\n");
1717 return NULL;
1718 }
1719
1720 return auth_create(username, password);
1721 }
1722
1723 return NULL;
1724}
1725
1727 const char *password)
1728{
1729 int encoded_size = 0;
1730 int userinfo_len = 0;
1731 RAII_VAR(char *, userinfo, NULL, ast_free);
1732 char *encoded_userinfo = NULL;
1733 struct ast_variable *auth_header = NULL;
1734
1735 if (ast_strlen_zero(userid)) {
1736 return NULL;
1737 }
1738
1739 if (strchr(userid, ':')) {
1740 userinfo = ast_strdup(userid);
1741 userinfo_len = strlen(userinfo);
1742 } else {
1743 if (ast_strlen_zero(password)) {
1744 return NULL;
1745 }
1746 userinfo_len = ast_asprintf(&userinfo, "%s:%s", userid, password);
1747 }
1748 if (!userinfo) {
1749 return NULL;
1750 }
1751
1752 /*
1753 * The header value is "Basic " + base64(userinfo).
1754 * Doubling the userinfo length then adding the length
1755 * of the "Basic " prefix is a conservative estimate of the
1756 * final encoded size.
1757 */
1758 encoded_size = userinfo_len * 2 * sizeof(char) + 1 + BASIC_LEN;
1759 encoded_userinfo = ast_alloca(encoded_size);
1760 strcpy(encoded_userinfo, BASIC_PREFIX); /* Safe */
1761 ast_base64encode(encoded_userinfo + BASIC_LEN, (unsigned char *)userinfo,
1762 userinfo_len, encoded_size - BASIC_LEN);
1763
1764 auth_header = ast_variable_new("Authorization",
1765 encoded_userinfo, "");
1766
1767 return auth_header;
1768}
1769
1770int ast_http_response_status_line(const char *buf, const char *version, int code)
1771{
1772 int status_code;
1773 size_t size = strlen(version);
1774
1775 if (strncmp(buf, version, size) || buf[size] != ' ') {
1776 ast_log(LOG_ERROR, "HTTP version not supported - "
1777 "expected %s\n", version);
1778 return -1;
1779 }
1780
1781 /* skip to status code (version + space) */
1782 buf += size + 1;
1783
1784 if (sscanf(buf, "%d", &status_code) != 1) {
1785 ast_log(LOG_ERROR, "Could not read HTTP status code - "
1786 "%s\n", buf);
1787 return -1;
1788 }
1789
1790 return status_code;
1791}
1792
1793static void remove_excess_lws(char *s)
1794{
1795 char *p, *res = s;
1796 char *buf = ast_malloc(strlen(s) + 1);
1797 char *buf_end;
1798
1799 if (!buf) {
1800 return;
1801 }
1802
1803 buf_end = buf;
1804
1805 while (*s && *(s = ast_skip_blanks(s))) {
1806 p = s;
1807 s = ast_skip_nonblanks(s);
1808
1809 if (buf_end != buf) {
1810 *buf_end++ = ' ';
1811 }
1812
1813 memcpy(buf_end, p, s - p);
1814 buf_end += s - p;
1815 }
1816 *buf_end = '\0';
1817 /* safe since buf will always be less than or equal to res */
1818 strcpy(res, buf);
1819 ast_free(buf);
1820}
1821
1822int ast_http_header_parse(char *buf, char **name, char **value)
1823{
1825 if (ast_strlen_zero(buf)) {
1826 return -1;
1827 }
1828
1829 *value = buf;
1830 *name = strsep(value, ":");
1831 if (!*value) {
1832 return 1;
1833 }
1834
1837 return 1;
1838 }
1839
1841 return 0;
1842}
1843
1844int ast_http_header_match(const char *name, const char *expected_name,
1845 const char *value, const char *expected_value)
1846{
1847 if (strcasecmp(name, expected_name)) {
1848 /* no value to validate if names don't match */
1849 return 0;
1850 }
1851
1852 if (strcasecmp(value, expected_value)) {
1853 ast_log(LOG_ERROR, "Invalid header value - expected %s "
1854 "received %s", value, expected_value);
1855 return -1;
1856 }
1857 return 1;
1858}
1859
1860int ast_http_header_match_in(const char *name, const char *expected_name,
1861 const char *value, const char *expected_value)
1862{
1863 if (strcasecmp(name, expected_name)) {
1864 /* no value to validate if names don't match */
1865 return 0;
1866 }
1867
1868 if (!strcasestr(expected_value, value)) {
1869 ast_log(LOG_ERROR, "Header '%s' - could not locate '%s' "
1870 "in '%s'\n", name, value, expected_value);
1871 return -1;
1872
1873 }
1874 return 1;
1875}
1876
1877/*! Limit the number of request headers in case the sender is being ridiculous. */
1878#define MAX_HTTP_REQUEST_HEADERS 100
1879
1880/*!
1881 * \internal
1882 * \brief Read the request headers.
1883 * \since 12.4.0
1884 *
1885 * \param ser HTTP TCP/TLS session object.
1886 * \param headers Where to put the request headers list pointer.
1887 *
1888 * \retval 0 on success.
1889 * \retval -1 on error.
1890 */
1892{
1893 struct ast_variable *tail = *headers;
1894 int remaining_headers;
1895 char header_line[MAX_HTTP_LINE_LENGTH];
1896
1897 remaining_headers = MAX_HTTP_REQUEST_HEADERS;
1898 for (;;) {
1899 ssize_t len;
1900 char *name;
1901 char *value;
1902
1903 len = ast_iostream_gets(ser->stream, header_line, sizeof(header_line));
1904 if (len <= 0) {
1905 ast_http_error(ser, 400, "Bad Request", "Timeout");
1906 return -1;
1907 }
1908 if (header_line[len - 1] != '\n') {
1909 /* We didn't get a full line */
1910 ast_http_error(ser, 400, "Bad Request",
1911 (len == sizeof(header_line) - 1) ? "Header line too long" : "Timeout");
1912 return -1;
1913 }
1914
1915 /* Trim trailing characters */
1916 ast_trim_blanks(header_line);
1917 if (ast_strlen_zero(header_line)) {
1918 /* A blank line ends the request header section. */
1919 break;
1920 }
1921
1922 value = header_line;
1923 name = strsep(&value, ":");
1924 if (!value) {
1925 continue;
1926 }
1927
1930 continue;
1931 }
1932
1934
1935 if (!remaining_headers--) {
1936 /* Too many headers. */
1937 ast_http_error(ser, 413, "Request Entity Too Large", "Too many headers");
1938 return -1;
1939 }
1940 if (!*headers) {
1941 *headers = ast_variable_new(name, value, __FILE__);
1942 tail = *headers;
1943 } else {
1944 tail->next = ast_variable_new(name, value, __FILE__);
1945 tail = tail->next;
1946 }
1947 if (!tail) {
1948 /*
1949 * Variable allocation failure.
1950 * Try to make some room.
1951 */
1952 ast_variables_destroy(*headers);
1953 *headers = NULL;
1954
1955 ast_http_error(ser, 500, "Server Error", "Out of memory");
1956 return -1;
1957 }
1958 }
1959
1960 return 0;
1961}
1962
1963/*!
1964 * \internal
1965 * \brief Process a HTTP request.
1966 * \since 12.4.0
1967 *
1968 * \param ser HTTP TCP/TLS session object.
1969 *
1970 * \retval 0 Continue and process the next HTTP request.
1971 * \retval -1 Fatal HTTP connection error. Force the HTTP connection closed.
1972 */
1974{
1975 RAII_VAR(struct ast_variable *, headers, NULL, ast_variables_destroy);
1976 char *uri;
1977 char *method;
1978 const char *transfer_encoding;
1980 enum ast_http_method http_method = AST_HTTP_UNKNOWN;
1981 int res;
1982 ssize_t len;
1983 char request_line[MAX_HTTP_LINE_LENGTH];
1984
1985 len = ast_iostream_gets(ser->stream, request_line, sizeof(request_line));
1986 if (len <= 0) {
1987 return -1;
1988 }
1989
1990 /* Re-initialize the request body tracking data. */
1991 request = ser->private_data;
1993
1994 if (request_line[len - 1] != '\n') {
1995 /* We didn't get a full line */
1996 ast_http_error(ser, 400, "Bad Request",
1997 (len == sizeof(request_line) - 1) ? "Request line too long" : "Timeout");
1998 return -1;
1999 }
2000
2001 /* Get method */
2002 method = ast_skip_blanks(request_line);
2004 if (*uri) {
2005 *uri++ = '\0';
2006 }
2007
2008 if (!strcasecmp(method,"GET")) {
2009 http_method = AST_HTTP_GET;
2010 } else if (!strcasecmp(method,"POST")) {
2011 http_method = AST_HTTP_POST;
2012 } else if (!strcasecmp(method,"HEAD")) {
2013 http_method = AST_HTTP_HEAD;
2014 } else if (!strcasecmp(method,"PUT")) {
2015 http_method = AST_HTTP_PUT;
2016 } else if (!strcasecmp(method,"DELETE")) {
2017 http_method = AST_HTTP_DELETE;
2018 } else if (!strcasecmp(method,"OPTIONS")) {
2019 http_method = AST_HTTP_OPTIONS;
2020 }
2021
2022 uri = ast_skip_blanks(uri); /* Skip white space */
2023 if (*uri) { /* terminate at the first blank */
2024 char *c = ast_skip_nonblanks(uri);
2025
2026 if (*c) {
2027 *c = '\0';
2028 }
2029 } else {
2030 ast_http_error(ser, 400, "Bad Request", "Invalid Request");
2031 return -1;
2032 }
2033
2034 if (ast_shutdown_final()) {
2035 ast_http_error(ser, 503, "Service Unavailable", "Shutdown in progress");
2036 return -1;
2037 }
2038
2039 /* process "Request Headers" lines */
2040 if (http_request_headers_get(ser, &headers)) {
2041 return -1;
2042 }
2043
2044 transfer_encoding = get_transfer_encoding(headers);
2045 /* Transfer encoding defaults to identity */
2046 if (!transfer_encoding) {
2047 transfer_encoding = "identity";
2048 }
2049
2050 /*
2051 * RFC 2616, section 3.6, we should respond with a 501 for any transfer-
2052 * codings we don't understand.
2053 */
2054 if (strcasecmp(transfer_encoding, "identity") != 0 &&
2055 strcasecmp(transfer_encoding, "chunked") != 0) {
2056 /* Transfer encodings not supported */
2057 ast_http_error(ser, 501, "Unimplemented", "Unsupported Transfer-Encoding.");
2058 return -1;
2059 }
2060
2061 if (http_request_tracking_setup(ser, headers)
2062 || handle_uri(ser, uri, http_method, headers)
2064 res = -1;
2065 } else {
2066 res = 0;
2067 }
2068 return res;
2069}
2070
2071static void *httpd_helper_thread(void *data)
2072{
2073 struct ast_tcptls_session_instance *ser = data;
2074 int timeout;
2075 int arg = 1;
2076
2077 if (!ser) {
2078 ao2_cleanup(ser);
2079 return NULL;
2080 }
2081
2083 ast_log(LOG_WARNING, "HTTP session count exceeded %d sessions.\n",
2085 goto done;
2086 }
2087 ast_debug(1, "HTTP opening session. Top level\n");
2088
2089 /*
2090 * Here we set TCP_NODELAY on the socket to disable Nagle's algorithm.
2091 * This is necessary to prevent delays (caused by buffering) as we
2092 * write to the socket in bits and pieces.
2093 */
2094 if (setsockopt(ast_iostream_get_fd(ser->stream), IPPROTO_TCP, TCP_NODELAY, (char *) &arg, sizeof(arg)) < 0) {
2095 ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on HTTP connection: %s\n", strerror(errno));
2096 }
2098
2099 /* Setup HTTP worker private data to keep track of request body reading. */
2103 if (!ser->private_data) {
2104 ast_http_error(ser, 500, "Server Error", "Out of memory");
2105 goto done;
2106 }
2108
2109 /* Determine initial HTTP request wait timeout. */
2110 timeout = session_keep_alive;
2111 if (timeout <= 0) {
2112 /* Persistent connections not enabled. */
2113 timeout = session_inactivity;
2114 }
2115 if (timeout < MIN_INITIAL_REQUEST_TIMEOUT) {
2117 }
2118
2119 /* We can let the stream wait for data to arrive. */
2121
2122 for (;;) {
2123 /* Wait for next potential HTTP request message. */
2125 if (httpd_process_request(ser)) {
2126 /* Break the connection or the connection closed */
2127 break;
2128 }
2129 if (!ser->stream) {
2130 /* Web-socket or similar that took the connection */
2131 break;
2132 }
2133
2134 timeout = session_keep_alive;
2135 if (timeout <= 0) {
2136 /* Persistent connections not enabled. */
2137 break;
2138 }
2139 }
2140
2141done:
2143
2144 ast_debug(1, "HTTP closing session. Top level\n");
2146
2147 ao2_ref(ser, -1);
2148 return NULL;
2149}
2150
2151/*!
2152 * \brief Check if a URI path is allowed or denied by acl
2153 * \param ser TCP/TLS session instance
2154 * \param uri The URI path to check
2155 * \return 0 if allowed, -1 if denied
2156 */
2157static int check_restriction_acl(struct ast_tcptls_session_instance *ser, const char *uri)
2158{
2159 struct http_restriction *restriction;
2160 int denied = 0;
2161
2163 AST_RWLIST_TRAVERSE(&restrictions, restriction, entry) {
2164 if (ast_begins_with(uri, restriction->path)) {
2165 if (restriction->acl && !ast_acl_list_is_empty(restriction->acl)) {
2166 if (ast_apply_acl(restriction->acl, &ser->remote_address,
2167 "HTTP Path ACL") == AST_SENSE_DENY) {
2168 ast_debug(2, "HTTP request for uri '%s' from %s denied by acl by restriction on '%s'\n",
2169 uri, ast_sockaddr_stringify(&ser->remote_address), restriction->path);
2170 denied = -1;
2171 break;
2172 }
2173 }
2174 }
2175 }
2177
2178 return denied;
2179}
2180
2181/*!
2182 * \brief Add a new URI redirect
2183 * The entries in the redirect list are sorted by length, just like the list
2184 * of URI handlers.
2185 */
2186static void add_redirect(const char *value)
2187{
2188 char *target, *dest;
2189 struct http_uri_redirect *redirect, *cur;
2190 unsigned int target_len;
2191 unsigned int total_len;
2192 size_t dest_len;
2193
2196 target = strsep(&dest, " ");
2198 target = strsep(&target, " "); /* trim trailing whitespace */
2199
2200 if (!dest) {
2201 ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
2202 return;
2203 }
2204
2205 target_len = strlen(target) + 1;
2206 dest_len = strlen(dest) + 1;
2207 total_len = sizeof(*redirect) + target_len + dest_len;
2208
2209 if (!(redirect = ast_calloc(1, total_len))) {
2210 return;
2211 }
2212 redirect->dest = redirect->target + target_len;
2213 strcpy(redirect->target, target);
2214 ast_copy_string(redirect->dest, dest, dest_len);
2215
2217
2218 target_len--; /* So we can compare directly with strlen() */
2220 || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
2223
2224 return;
2225 }
2226
2228 if (AST_RWLIST_NEXT(cur, entry)
2229 && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
2232 return;
2233 }
2234 }
2235
2237
2239}
2240
2241/*! \brief Number of HTTP server buckets */
2242#define HTTP_SERVER_BUCKETS 5
2243
2245
2248
2249static void http_server_destroy(void *obj)
2250{
2251 struct ast_http_server *server = obj;
2252
2253 ast_tcptls_server_stop(&server->args);
2254
2255 ast_verb(1, "Stopped http server '%s' listening at '%s'\n", server->name, server->address);
2256
2257 ast_free(server->name);
2258 ast_free(server->address);
2259}
2260
2261static struct ast_http_server *http_server_create(const char *name, const char *address,
2262 const struct ast_sockaddr *addr)
2263{
2264 struct ast_http_server *server;
2265
2266 server = ao2_alloc(sizeof(*server), http_server_destroy);
2267 if (!server) {
2268 ast_log(LOG_ERROR, "Unable to allocate HTTP server '%s' at address '%s'\n",
2269 name, address);
2270 return NULL;
2271 }
2272
2273 if (!(server->address = ast_strdup(address)) || !(server->name = ast_strdup(name))) {
2274 ast_log(LOG_ERROR, "Unable to complete setup for HTTP server '%s' at address '%s'\n",
2275 name, address);
2276 ao2_ref(server, -1);
2277 return NULL;
2278 }
2279
2280 server->args.accept_fd = -1;
2281 server->args.master = AST_PTHREADT_NULL;
2282 server->args.tls_cfg = NULL;
2283 server->args.poll_timeout = -1;
2284 server->args.name = server->name;
2287
2288 ast_sockaddr_copy(&server->args.local_address, addr);
2289
2290 return server;
2291}
2292
2293static int http_server_start(struct ast_http_server *server)
2294{
2295 if (server->args.accept_fd != -1) {
2296 /* Already running */
2297 return 0;
2298 }
2299
2300 ast_tcptls_server_start(&server->args);
2301 if (server->args.accept_fd == -1) {
2302 ast_log(LOG_WARNING, "Failed to start HTTP server '%s' at address '%s'\n",
2303 server->name, server->address);
2304 return -1;
2305 }
2306
2307 if (!ao2_link_flags(http_servers, server, OBJ_NOLOCK)) {
2308 ast_log(LOG_WARNING, "Failed to link HTTP server '%s' at address '%s'\n",
2309 server->name, server->address);
2310 return -1;
2311 }
2312
2313 ast_verb(1, "Bound HTTP server '%s' to address %s\n", server->name, server->address);
2314
2315 return 0;
2316}
2317
2318/*!
2319 * \brief Discard/Drop a HTTP server
2320 *
2321 * Decrements the reference to the given object, and unlinks it from the servers
2322 * container if it's the last reference.
2323 *
2324 * After a server object has been added to the container this method should always
2325 * be called to decrement the object's reference instead of the regular ao2 methods.
2326 *
2327 * \note NULL tolerant
2328 *
2329 * \param server The server object
2330 */
2331static void http_server_discard(struct ast_http_server *server)
2332{
2333 if (!server) {
2334 return;
2335 }
2336
2337 /*
2338 * If only two references were on the object then the last one is from
2339 * the servers container, so remove from container now.
2340 */
2341 if (ao2_ref(server, -1) == 2) {
2342 ao2_unlink(http_servers, server);
2343 }
2344}
2345
2346/*!
2347 * \brief Retrieve, or create a HTTP server object by sock address
2348 *
2349 * Look for, and return a matching server object by addr. If an object is not found
2350 * then create a new one.
2351 *
2352 * \note This method should be called with the http_servers container already locked.
2353 *
2354 * \param name The name of the server
2355 * \param addr The address to match on, or create a new object with
2356 *
2357 * \return a HTTP server object, or NULL on error
2358 */
2360 const char *name, const struct ast_sockaddr *addr)
2361{
2362 struct ast_http_server *server;
2363 const char *address;
2364
2366 if (ast_strlen_zero(address)) {
2367 return NULL;
2368 }
2369
2371
2372 return server ?: http_server_create(name, address, addr);
2373}
2374
2375/*!
2376 * \brief Retrieve, or create a HTTP server object by host
2377 *
2378 * Resolve the given host, and then look for, and return a matching server object.
2379 * If an object is not found then create a new one.
2380 *
2381 * \note This method should be called with the http_servers container already locked.
2382 *
2383 * \param name The name of the server
2384 * \param host The host to resolve, and match on or create a new object with
2385 * \param port Optional port used if one is not specified with the host (default 8088)
2386 *
2387 * \return a HTTP server object, or NULL on error
2388 */
2389static struct ast_http_server *http_server_get_by_host(const char *name, const char *host,
2390 uint32_t port)
2391{
2392 struct ast_sockaddr *addrs = NULL;
2393 int num_addrs;
2394 int i;
2395
2396 if (!(num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC))) {
2397 ast_log(LOG_WARNING, "Unable to resolve host '%s'\n", host);
2398 return NULL;
2399 }
2400
2401 if (port == 0) {
2402 port = DEFAULT_PORT;
2403 }
2404
2405 for (i = 0; i < num_addrs; ++i) {
2406 struct ast_http_server *server;
2407
2408 /* Use the given port if one was not specified already */
2409 if (!ast_sockaddr_port(&addrs[i])) {
2410 ast_sockaddr_set_port(&addrs[i], port);
2411 }
2412
2413 server = http_server_get_by_addr(name, &addrs[i]);
2414 if (server) {
2415 ast_free(addrs);
2416 return server;
2417 }
2418 }
2419
2420 ast_free(addrs);
2421 return NULL;
2422}
2423
2424/*!
2425 * \brief Retrieve, or create and start a HTTP server
2426 *
2427 * Resolve the given host, and retrieve a listening server object. If the server is
2428 * not already listening then start it. If a replace_me parameter is given, and it
2429 * points to a non-NULL value then that server is discarded and replaced.
2430 *
2431 * \param name The name of the server
2432 * \param host The host to resolve, and match on or create a new object with
2433 * \param port Optional port used if one is not specified with the host (default 8088)
2434 * \param[out] replace_me Optional server to be replaced
2435 *
2436 * \note If replace_me is specified the returned value is always the same as what's
2437 * passed back out in the variable.
2438 *
2439 * \return a HTTP server object, or NULL on error
2440 */
2441static struct ast_http_server *http_server_get(const char *name, const char *host,
2442 uint32_t port, struct ast_http_server **replace_me)
2443{
2444 struct ast_http_server *server;
2445
2447
2448 server = http_server_get_by_host(name, host, port);
2449
2450 if (replace_me) {
2451 /* Only replace if different */
2452 if (*replace_me == server) {
2453 ao2_cleanup(server);
2455 return *replace_me;
2456 }
2457
2458 if (*replace_me) {
2459 http_server_discard(*replace_me);
2460 }
2461
2462 *replace_me = server;
2463 }
2464
2465 if (server && http_server_start(server)) {
2466 if (replace_me) {
2467 *replace_me = NULL;
2468 }
2469
2470 ao2_ref(server, -1);
2471 server = NULL;
2472 }
2473
2475 return server;
2476}
2477
2478#ifdef TEST_FRAMEWORK
2479
2480struct ast_http_server *ast_http_test_server_get(const char *name, const char *host)
2481{
2482 struct ast_http_server *server;
2483
2484 /*
2485 * Currently multiple HTTP servers are only allowed when the TEST_FRAMEWORK
2486 * is enabled, leaving the follow 'todos' if they become a problem or if this
2487 * ability moves outside the TEST_FRAMEWORK.
2488 *
2489 * TODO: Add locking around global_http_server use. If this module is reloading
2490 * it's possible for the global_http_server to exist here, and then become
2491 * NULL between the check and return.
2492 *
2493 * TODO: Make it so 'localhost' and 'any' addresses equate.
2494 */
2495
2496 if (ast_strlen_zero(host)) {
2497 /* Use configured server if one available */
2498 if (global_http_server) {
2501 }
2502
2503 host = "localhost:8088";
2504 }
2505
2506 if (!name) {
2507 name = "http test server";
2508 }
2509
2510 server = http_server_get(name, host, 0, NULL);
2511 if (server) {
2513 }
2514
2515 return server;
2516}
2517
2518void ast_http_test_server_discard(struct ast_http_server *server)
2519{
2520 if (server) {
2521 http_server_discard(server);
2523 }
2524}
2525
2526#endif
2527
2529{
2530 struct ast_config *cfg;
2531 struct ast_variable *v;
2532 int enabled = 0;
2533 int new_static_uri_enabled = 0;
2534 int new_status_uri_enabled = 0;
2535 char newprefix[MAX_PREFIX] = "";
2536 char server_name[MAX_SERVER_NAME_LENGTH];
2537 struct http_uri_redirect *redirect;
2538 struct http_restriction *restriction;
2541 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
2542 uint32_t bindport = DEFAULT_PORT;
2543 int http_tls_was_enabled = 0;
2544 const char *bindaddr = NULL;
2545 const char *cat = NULL;
2546
2547 cfg = ast_config_load2("http.conf", "http", config_flags);
2548 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
2549 return 0;
2550 }
2551
2552 /* Even if the http.conf hasn't been updated, the TLS certs/keys may have been */
2553 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
2556 }
2557 return 0;
2558 }
2559
2560 http_tls_was_enabled = (reload && http_tls_cfg.enabled);
2561
2563
2566
2569
2572
2573 /* Apply modern intermediate settings according to the Mozilla OpSec team as of July 30th, 2015 but disable TLSv1 */
2575
2577 http_tls_cfg.cipher = ast_strdup("ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA");
2578
2580 while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
2581 ast_free(redirect);
2582 }
2584
2586
2590
2591 snprintf(server_name, sizeof(server_name), "Asterisk/%s", ast_get_version());
2592
2593 v = ast_variable_browse(cfg, "general");
2594 for (; v; v = v->next) {
2595 /* read tls config options while preventing unsupported options from being set */
2596 if (strcasecmp(v->name, "tlscafile")
2597 && strcasecmp(v->name, "tlscapath")
2598 && strcasecmp(v->name, "tlscadir")
2599 && strcasecmp(v->name, "tlsverifyclient")
2600 && strcasecmp(v->name, "tlsdontverifyserver")
2601 && strcasecmp(v->name, "tlsclientmethod")
2602 && strcasecmp(v->name, "sslclientmethod")
2604 continue;
2605 }
2606
2607 if (!strcasecmp(v->name, "servername")) {
2608 if (!ast_strlen_zero(v->value)) {
2609 ast_copy_string(server_name, v->value, sizeof(server_name));
2610 } else {
2611 server_name[0] = '\0';
2612 }
2613 } else if (!strcasecmp(v->name, "enabled")) {
2614 enabled = ast_true(v->value);
2615 } else if (!strcasecmp(v->name, "enablestatic") || !strcasecmp(v->name, "enable_static")) {
2616 new_static_uri_enabled = ast_true(v->value);
2617 } else if (!strcasecmp(v->name, "enable_status")) {
2618 new_status_uri_enabled = ast_true(v->value);
2619 } else if (!strcasecmp(v->name, "bindport")) {
2621 &bindport, DEFAULT_PORT, 0, 65535)) {
2622 ast_log(LOG_WARNING, "Invalid port %s specified. Using default port %" PRId32 "\n",
2623 v->value, DEFAULT_PORT);
2624 }
2625 } else if (!strcasecmp(v->name, "bindaddr")) {
2627 } else if (!strcasecmp(v->name, "prefix")) {
2628 if (!ast_strlen_zero(v->value)) {
2629 newprefix[0] = '/';
2630 ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
2631 } else {
2632 newprefix[0] = '\0';
2633 }
2634 } else if (!strcasecmp(v->name, "redirect")) {
2635 add_redirect(v->value);
2636 } else if (!strcasecmp(v->name, "sessionlimit")) {
2638 &session_limit, DEFAULT_SESSION_LIMIT, 1, INT_MAX)) {
2639 ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
2640 v->name, v->value, v->lineno);
2641 }
2642 } else if (!strcasecmp(v->name, "session_inactivity")) {
2645 ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
2646 v->name, v->value, v->lineno);
2647 }
2648 } else if (!strcasecmp(v->name, "session_keep_alive")) {
2649 if (sscanf(v->value, "%30d", &session_keep_alive) != 1
2650 || session_keep_alive < 0) {
2652 ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
2653 v->name, v->value, v->lineno);
2654 }
2655 } else {
2656 ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
2657 }
2658 }
2659
2660 while ((cat = ast_category_browse(cfg, cat))) {
2661 const char *type;
2662 struct http_restriction *new_restriction;
2663 struct ast_acl_list *acl = NULL;
2664 int acl_error = 0;
2665 int acl_subscription_flag = 0;
2666
2667 if (strcasecmp(cat, "general") == 0) {
2668 continue;
2669 }
2670
2671 type = ast_variable_retrieve(cfg, cat, "type");
2672 if (!type || strcasecmp(type, "restriction") != 0) {
2673 continue;
2674 }
2675
2676 new_restriction = ast_calloc(1, sizeof(*new_restriction) + strlen(cat) + 1);
2677 if (!new_restriction) {
2678 continue;
2679 }
2680
2681 /* Safe */
2682 strcpy(new_restriction->path, cat);
2683
2684 /* Parse ACL options for this restriction */
2685 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
2686 if (!strcasecmp(v->name, "permit") ||
2687 !strcasecmp(v->name, "deny") ||
2688 !strcasecmp(v->name, "acl")) {
2689 ast_append_acl(v->name, v->value, &acl, &acl_error, &acl_subscription_flag);
2690 if (acl_error) {
2691 ast_log(LOG_ERROR, "Bad ACL '%s' at line '%d' of http.conf for restriction '%s'\n",
2692 v->value, v->lineno, cat);
2693 }
2694 }
2695 }
2696
2697 new_restriction->acl = acl;
2698
2699 AST_LIST_INSERT_TAIL(&new_restrictions, new_restriction, entry);
2700 ast_debug(2, "HTTP: Added restriction for path '%s'\n", cat);
2701 }
2702
2704 AST_RWLIST_APPEND_LIST(&old_restrictions, &restrictions, entry);
2705 AST_RWLIST_APPEND_LIST(&restrictions, &new_restrictions, entry);
2707
2708 while ((restriction = AST_LIST_REMOVE_HEAD(&old_restrictions, entry))) {
2709 if (restriction->acl) {
2710 ast_free_acl_list(restriction->acl);
2711 }
2712 ast_free(restriction);
2713 }
2714
2715 ast_config_destroy(cfg);
2716
2717 if (strcmp(prefix, newprefix)) {
2718 ast_copy_string(prefix, newprefix, sizeof(prefix));
2719 }
2720
2721 ast_copy_string(http_server_name, server_name, sizeof(http_server_name));
2722
2723 if (enabled) {
2724 http_server_get("http server", bindaddr, bindport, &global_http_server);
2725 } else if (global_http_server) {
2728 }
2729
2730 /* When no specific TLS bindaddr is specified, we just use
2731 * the non-TLS bindaddress here.
2732 */
2735
2737 /* Of course, we can't use the same port though.
2738 * Since no bind address was specified, we just use the
2739 * default TLS port
2740 */
2742 }
2743
2744 if (http_tls_was_enabled && !http_tls_cfg.enabled) {
2747 /* We can get here either because a TLS-specific address was specified
2748 * or because we copied the non-TLS address here. In the case where
2749 * we read an explicit address from the config, there may have been
2750 * no port specified, so we'll just use the default TLS port.
2751 */
2754 }
2757 }
2758 }
2759
2760 if (static_uri_enabled && !new_static_uri_enabled) {
2762 } else if (!static_uri_enabled && new_static_uri_enabled) {
2764 }
2765
2766 static_uri_enabled = new_static_uri_enabled;
2767
2768 if (status_uri_enabled && !new_status_uri_enabled) {
2770 } else if (!status_uri_enabled && new_status_uri_enabled) {
2772 }
2773
2774 status_uri_enabled = new_status_uri_enabled;
2775
2776 return 0;
2777}
2778
2779static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2780{
2781 struct ast_http_uri *urih;
2782 struct http_uri_redirect *redirect;
2783
2784 switch (cmd) {
2785 case CLI_INIT:
2786 e->command = "http show status";
2787 e->usage =
2788 "Usage: http show status\n"
2789 " Lists status of internal HTTP engine\n";
2790 return NULL;
2791 case CLI_GENERATE:
2792 return NULL;
2793 }
2794
2795 if (a->argc != 3) {
2796 return CLI_SHOWUSAGE;
2797 }
2798 ast_cli(a->fd, "HTTP Server Status:\n");
2799 ast_cli(a->fd, "Prefix: %s\n", prefix);
2800 ast_cli(a->fd, "Server: %s\n", http_server_name);
2801 if (!global_http_server) {
2802 ast_cli(a->fd, "Server Disabled\n\n");
2803 } else {
2804 ast_cli(a->fd, "Server Enabled and Bound to %s\n\n",
2806 if (http_tls_cfg.enabled) {
2807 ast_cli(a->fd, "HTTPS Server Enabled and Bound to %s\n\n",
2809 }
2810 }
2811
2812 ast_cli(a->fd, "Enabled URI's:\n");
2814 if (AST_RWLIST_EMPTY(&uris)) {
2815 ast_cli(a->fd, "None.\n");
2816 } else {
2818 ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
2819 }
2821
2822 ast_cli(a->fd, "\nEnabled Redirects:\n");
2825 ast_cli(a->fd, " None.\n");
2826 } else {
2828 ast_cli(a->fd, " %s => %s\n", redirect->target, redirect->dest);
2829 }
2831
2832 ast_cli(a->fd, "\nPath Restrictions:\n");
2835 ast_cli(a->fd, " None.\n");
2836 } else {
2837 struct http_restriction *restriction;
2838 AST_RWLIST_TRAVERSE(&restrictions, restriction, entry) {
2839 ast_cli(a->fd, " Path: %s\n", restriction->path);
2840 if (restriction->acl && !ast_acl_list_is_empty(restriction->acl)) {
2841 ast_acl_output(a->fd, restriction->acl, " ");
2842 } else {
2843 ast_cli(a->fd, " No ACL configured\n");
2844 }
2845 }
2846 }
2848
2849 return CLI_SUCCESS;
2850}
2851
2852static int reload_module(void)
2853{
2854 return __ast_http_load(1);
2855}
2856
2857static struct ast_cli_entry cli_http[] = {
2858 AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"),
2859};
2860
2861static int unload_module(void)
2862{
2863 struct http_uri_redirect *redirect;
2864 struct http_restriction *restriction;
2866
2869
2870 if (http_tls_cfg.enabled) {
2872 }
2877
2878 if (status_uri_enabled) {
2880 }
2881
2882 if (static_uri_enabled) {
2884 }
2885
2887 while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
2888 ast_free(redirect);
2889 }
2891
2893 while ((restriction = AST_RWLIST_REMOVE_HEAD(&restrictions, entry))) {
2894 if (restriction->acl) {
2895 ast_free_acl_list(restriction->acl);
2896 }
2897 ast_free(restriction);
2898 }
2900
2901 return 0;
2902}
2903
2904static int load_module(void)
2905{
2907
2909 HTTP_SERVER_BUCKETS, ast_http_server_hash_fn, NULL, ast_http_server_cmp_fn);
2910 if (!http_servers) {
2912 }
2913
2914 if (__ast_http_load(0)) {
2918 }
2919
2921}
2922
2924 .support_level = AST_MODULE_SUPPORT_CORE,
2925 .load = load_module,
2926 .unload = unload_module,
2928 .load_pri = AST_MODPRI_CORE,
2929 .requires = "extconfig",
Access Control of various sorts.
enum ast_acl_sense ast_apply_acl(struct ast_acl_list *acl_list, const struct ast_sockaddr *addr, const char *purpose)
Apply a set of rules to a given IP address.
Definition acl.c:799
void ast_acl_output(int fd, struct ast_acl_list *acl, const char *prefix)
output an ACL to the provided fd
Definition acl.c:1115
void ast_append_acl(const char *sense, const char *stuff, struct ast_acl_list **path, int *error, int *named_acl_flag)
Add a rule to an ACL struct.
Definition acl.c:429
@ AST_SENSE_DENY
Definition acl.h:37
int ast_acl_list_is_empty(struct ast_acl_list *acl_list)
Determines if an ACL is empty or if it contains entries.
Definition acl.c:540
struct ast_acl_list * ast_free_acl_list(struct ast_acl_list *acl)
Free a list of ACLs.
Definition acl.c:233
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
const char * str
Definition app_jack.c:150
char * text
Definition app_queue.c:1791
#define var
Definition ast_expr2f.c:605
Asterisk version information.
const char * ast_get_version(void)
Retrieve the Asterisk version string.
char * strsep(char **str, const char *delims)
char * strcasestr(const char *, const char *)
Asterisk main include file. File version handling, generic pbx functions.
int ast_shutdown_final(void)
Definition asterisk.c:1884
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition astmm.h:288
#define ast_free(a)
Definition astmm.h:180
#define ast_strndup(str, len)
A wrapper for strndup()
Definition astmm.h:256
#define ast_realloc(p, len)
A wrapper for realloc()
Definition astmm.h:226
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition astmm.h:191
#define ast_log
Definition astobj2.c:42
#define OBJ_KEY
Definition astobj2.h:1151
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition astobj2.h:367
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition astobj2.h:363
#define AO2_STRING_FIELD_CMP_FN(stype, field)
Creates a compare function for a structure string field.
Definition astobj2.h:2048
#define ao2_cleanup(obj)
Definition astobj2.h:1934
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition astobj2.h:1578
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
Definition astobj2.h:1554
#define ao2_find(container, arg, flags)
Definition astobj2.h:1736
#define ao2_unlock(a)
Definition astobj2.h:729
#define ao2_lock(a)
Definition astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition astobj2.h:459
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition astobj2.h:404
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition astobj2.h:480
#define AO2_STRING_FIELD_HASH_FN(stype, field)
Creates a hash function for a structure string field.
Definition astobj2.h:2032
@ OBJ_NOLOCK
Assume that the ao2_container is already locked.
Definition astobj2.h:1063
#define ao2_alloc(data_size, destructor_fn)
Definition astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition astobj2.h:1303
static const char type[]
struct ast_sockaddr bindaddr
static char version[AST_MAX_EXTENSION]
static int request(void *obj)
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition cli.h:45
#define CLI_SUCCESS
Definition cli.h:44
#define AST_CLI_DEFINE(fn, txt,...)
Definition cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition clicompat.c:6
@ CLI_INIT
Definition cli.h:152
@ CLI_GENERATE
Definition cli.h:153
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition cli.h:265
static int enabled
Definition dnsmgr.c:91
char buf[BUFSIZE]
Definition eagi_proxy.c:66
char * address
Definition f2c.h:59
static const char name[]
Definition format_mp3.c:68
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void ast_http_prefix(char *buf, int len)
Return the current prefix.
Definition http.c:263
static int http_body_discard_chunk_trailer_headers(struct ast_tcptls_session_instance *ser)
Definition http.c:1165
void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, int fd, unsigned int static_content)
Generic function for sending HTTP/1.1 response.
Definition http.c:522
static void * httpd_helper_thread(void *arg)
Definition http.c:2071
struct ast_variable * ast_http_get_post_vars(struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
Get post variables from client Request Entity-Body, if content type is application/x-www-form-urlenco...
Definition http.c:1455
static int http_check_connection_close(struct ast_variable *headers)
Definition http.c:892
struct ast_json * ast_http_get_json(struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
Get JSON from client Request Entity-Body, if content type is application/json.
Definition http.c:1380
#define DEFAULT_PORT
Definition http.c:70
static struct @382 mimetypes[]
Limit the kinds of files we're willing to serve up.
static int httpstatus_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
Definition http.c:426
int ast_http_header_parse(char *buf, char **name, char **value)
Parse a header into the given name/value strings.
Definition http.c:1822
static struct ast_tls_config http_tls_cfg
Definition http.c:112
const char * ast_get_http_method(enum ast_http_method method)
Return http method name string.
Definition http.c:207
struct ast_variable * ast_http_get_cookies(struct ast_variable *headers)
Get cookie from Request headers.
Definition http.c:1622
int ast_http_header_match_in(const char *name, const char *expected_name, const char *value, const char *expected_value)
Check if the header name matches the expected header name. If so, then check to see if the value can ...
Definition http.c:1860
#define MIN_INITIAL_REQUEST_TIMEOUT
Definition http.c:76
const char * mtype
Definition http.c:152
#define INITIAL_RESPONSE_BODY_BUFFER
Definition http.c:93
static int __ast_http_load(int reload)
Definition http.c:2528
static int http_request_headers_get(struct ast_tcptls_session_instance *ser, struct ast_variable **headers)
Definition http.c:1891
static void http_server_destroy(void *obj)
Definition http.c:2249
struct ao2_container * http_servers
Definition http.c:2244
static int http_request_tracking_setup(struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
Definition http.c:936
static void http_request_tracking_init(struct http_worker_private_data *request)
Definition http.c:917
#define BASIC_PREFIX
Definition http.c:1666
static int static_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
Definition http.c:270
static char http_server_name[MAX_SERVER_NAME_LENGTH]
Definition http.c:105
static void add_redirect(const char *value)
Add a new URI redirect The entries in the redirect list are sorted by length, just like the list of U...
Definition http.c:2186
static const char * get_header(struct ast_variable *headers, const char *field_name)
Retrieves the header with the given field name.
Definition http.c:805
#define MAX_HTTP_REQUEST_HEADERS
Definition http.c:1878
int ast_http_uri_link(struct ast_http_uri *urih)
Link the new uri into the list.
Definition http.c:739
static int status_uri_enabled
Definition http.c:147
int ast_http_body_discard(struct ast_tcptls_session_instance *ser)
Read and discard any unread HTTP request body.
Definition http.c:1185
static struct ast_cli_entry cli_http[]
Definition http.c:2857
enum ast_http_method ast_get_http_method_from_string(const char *method)
Return http method from string.
Definition http.c:220
static int http_server_start(struct ast_http_server *server)
Definition http.c:2293
http_private_flags
Definition http.c:505
@ HTTP_FLAG_BODY_READ
Definition http.c:509
@ HTTP_FLAG_CLOSE_ON_COMPLETION
Definition http.c:511
@ HTTP_FLAG_HAS_BODY
Definition http.c:507
void ast_http_error(struct ast_tcptls_session_instance *ser, int status_code, const char *status_title, const char *text)
Send HTTP error message and close socket.
Definition http.c:714
static char prefix[MAX_PREFIX]
Definition http.c:145
static int http_body_discard_contents(struct ast_tcptls_session_instance *ser, int length, const char *what_getting)
Definition http.c:1029
struct ast_variable * ast_http_parse_post_form(char *buf, int content_length, const char *content_type)
Get post variables from an application/x-www-form-urlencoded buffer.
Definition http.c:1419
static void str_append_escaped(struct ast_str **str, const char *in)
Definition http.c:398
static char * get_content_type(struct ast_variable *headers)
Retrieves the content type specified in the "Content-Type" header.
Definition http.c:828
static int session_count
Definition http.c:110
static int reload_module(void)
Definition http.c:2852
static int session_inactivity
Definition http.c:108
struct ast_variable * ast_http_create_basic_auth_header(const char *userid, const char *password)
Create an HTTP authorization header.
Definition http.c:1726
#define DEFAULT_SESSION_INACTIVITY
Definition http.c:74
static struct ast_http_server * http_server_create(const char *name, const char *address, const struct ast_sockaddr *addr)
Definition http.c:2261
static struct ast_http_uri status_uri
Definition http.c:487
static int http_body_check_chunk_sync(struct ast_tcptls_session_instance *ser)
Definition http.c:1134
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
Definition http.c:1669
int ast_http_header_match(const char *name, const char *expected_name, const char *value, const char *expected_value)
Check if the header and value match (case insensitive) their associated expected values.
Definition http.c:1844
const char * ast_http_ftype2mtype(const char *ftype)
Return mime type based on extension.
Definition http.c:233
static void remove_excess_lws(char *s)
Definition http.c:1793
static void http_server_discard(struct ast_http_server *server)
Discard/Drop a HTTP server.
Definition http.c:2331
static struct ast_http_server * http_server_get_by_host(const char *name, const char *host, uint32_t port)
Retrieve, or create a HTTP server object by host.
Definition http.c:2389
#define DEFAULT_SESSION_KEEP_ALIVE
Definition http.c:78
static char * ast_http_get_contents(int *return_length, struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
Returns the contents (body) of the HTTP request.
Definition http.c:1247
int ast_http_response_status_line(const char *buf, const char *version, int code)
Parse the http response status line.
Definition http.c:1770
#define MAX_CONTENT_LENGTH
Definition http.c:86
static int chunked_atoh(const char *s, int len)
Definition http.c:1052
void ast_http_request_close_on_completion(struct ast_tcptls_session_instance *ser)
Request the HTTP connection be closed after this HTTP request.
Definition http.c:903
static int http_body_read_contents(struct ast_tcptls_session_instance *ser, char *buf, int length, const char *what_getting)
Definition http.c:993
#define DEFAULT_TLS_PORT
Definition http.c:71
void ast_http_uri_unlink(struct ast_http_uri *urih)
Unregister a URI handler.
Definition http.c:771
static int load_module(void)
Definition http.c:2904
static const char * get_transfer_encoding(struct ast_variable *headers)
Returns the value of the Transfer-Encoding header.
Definition http.c:878
static struct ast_http_server * http_server_get_by_addr(const char *name, const struct ast_sockaddr *addr)
Retrieve, or create a HTTP server object by sock address.
Definition http.c:2359
static struct ast_http_uri static_uri
Definition http.c:496
static struct ast_http_server * http_server_get(const char *name, const char *host, uint32_t port, struct ast_http_server **replace_me)
Retrieve, or create and start a HTTP server.
Definition http.c:2441
void ast_http_create_response(struct ast_tcptls_session_instance *ser, int status_code, const char *status_title, struct ast_str *http_header_data, const char *text)
Creates and sends a formatted http response message.
Definition http.c:633
static struct ast_tcptls_session_args https_desc
Definition http.c:132
static int unload_module(void)
Definition http.c:2861
static int static_uri_enabled
Definition http.c:146
static int session_keep_alive
Definition http.c:109
#define BASIC_LEN
Definition http.c:1667
static int httpd_process_request(struct ast_tcptls_session_instance *ser)
Definition http.c:1973
static int check_restriction_acl(struct ast_tcptls_session_instance *ser, const char *uri)
Check if a URI path is allowed or denied by acl.
Definition http.c:2157
static int handle_uri(struct ast_tcptls_session_instance *ser, char *uri, enum ast_http_method method, struct ast_variable *headers)
Definition http.c:1483
uint32_t ast_http_manid_from_vars(struct ast_variable *headers)
Return manager id, if exist, from request headers.
Definition http.c:247
static struct ast_http_auth * auth_create(const char *userid, const char *password)
Definition http.c:1635
void ast_http_uri_unlink_all_with_key(const char *key)
Unregister all handlers with matching key.
Definition http.c:778
static const struct ast_cfhttp_methods_text ast_http_methods_text[]
static struct ast_variable * parse_cookies(const char *cookies)
Definition http.c:1588
static char * handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition http.c:2779
#define MAX_PREFIX
Definition http.c:69
#define MAX_HTTP_LINE_LENGTH
Definition http.c:100
void ast_http_body_read_status(struct ast_tcptls_session_instance *ser, int read_success)
Update the body read success status.
Definition http.c:964
struct ast_http_server * global_http_server
Definition http.c:130
#define DEFAULT_RESPONSE_HEADER_LENGTH
Definition http.c:82
#define MAX_SERVER_NAME_LENGTH
Definition http.c:80
static int http_body_get_chunk_length(struct ast_tcptls_session_instance *ser)
Definition http.c:1106
#define HTTP_SERVER_BUCKETS
Number of HTTP server buckets.
Definition http.c:2242
const char * ext
Definition http.c:151
static int session_limit
Definition http.c:107
static int get_content_length(struct ast_variable *headers)
Returns the value of the Content-Length header.
Definition http.c:853
#define DEFAULT_SESSION_LIMIT
Definition http.c:72
Support for Private Asterisk HTTP Servers.
ast_http_method
HTTP Request methods known by Asterisk.
Definition http.h:58
@ AST_HTTP_PUT
Definition http.h:63
@ AST_HTTP_DELETE
Definition http.h:64
@ AST_HTTP_POST
Definition http.h:61
@ AST_HTTP_GET
Definition http.h:60
@ AST_HTTP_UNKNOWN
Definition http.h:59
@ AST_HTTP_OPTIONS
Definition http.h:65
@ AST_HTTP_HEAD
Definition http.h:62
Configuration File Parser.
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition extconf.c:3324
#define ast_variable_new(name, value, filename)
#define CONFIG_STATUS_FILEUNCHANGED
@ CONFIG_FLAG_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
int ast_parse_arg(const char *arg, enum ast_parse_flags flags, void *p_result,...)
The argument parsing routine.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition extconf.c:1287
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition extconf.c:1213
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_DEBUG
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_WARNING
ssize_t ast_iostream_printf(struct ast_iostream *stream, const char *format,...)
Write a formatted string to an iostream.
Definition iostream.c:502
ssize_t ast_iostream_gets(struct ast_iostream *stream, char *buffer, size_t size)
Read a LF-terminated string from an iostream.
Definition iostream.c:311
void ast_iostream_set_timeout_idle_inactivity(struct ast_iostream *stream, int timeout, int timeout_reset)
Set the iostream inactivity & idle timeout timers.
Definition iostream.c:131
ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t count)
Write data to an iostream.
Definition iostream.c:385
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream's file descriptor.
Definition iostream.c:85
void ast_iostream_set_exclusive_input(struct ast_iostream *stream, int exclusive_input)
Set the iostream if it can exclusively depend upon the set timeouts.
Definition iostream.c:149
ssize_t ast_iostream_read(struct ast_iostream *stream, void *buffer, size_t count)
Read data from an iostream.
Definition iostream.c:284
void ast_iostream_nonblock(struct ast_iostream *stream)
Make an iostream non-blocking.
Definition iostream.c:104
ssize_t ast_iostream_discard(struct ast_iostream *stream, size_t count)
Discard the specified number of bytes from an iostream.
Definition iostream.c:368
Asterisk JSON abstraction layer.
struct ast_json * ast_json_load_buf(const char *buffer, size_t buflen, struct ast_json_error *error)
Parse buffer with known length into a JSON object or array.
Definition json.c:585
#define AST_RWLIST_EMPTY
#define AST_RWLIST_REMOVE_CURRENT
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition linkedlists.h:78
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition linkedlists.h:52
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized.
#define AST_RWLIST_REMOVE_HEAD
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define AST_RWLIST_INSERT_AFTER
#define AST_LIST_HEAD_NOLOCK_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK.
#define AST_RWLIST_NEXT
#define AST_RWLIST_REMOVE
#define AST_RWLIST_FIRST
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_RWLIST_TRAVERSE_SAFE_END
#define AST_RWLIST_APPEND_LIST
#define AST_RWLIST_TRAVERSE
#define AST_RWLIST_INSERT_HEAD
#define AST_RWLIST_INSERT_TAIL
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition localtime.c:1739
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition localtime.c:2524
#define AST_PTHREADT_NULL
Definition lock.h:73
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition lock.h:764
int errno
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
int astman_is_authed(uint32_t ident)
Determine if a manager session ident is authenticated.
Definition manager.c:8014
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition module.h:330
#define ast_module_unref(mod)
Release a reference to the module.
Definition module.h:483
#define ast_module_ref(mod)
Hold a reference to the module.
Definition module.h:457
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODPRI_CORE
Definition module.h:338
@ AST_MODULE_SUPPORT_CORE
Definition module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
@ AST_MODULE_LOAD_FAILURE
Module could not be loaded properly.
Definition module.h:102
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
Network socket handling.
@ AST_AF_UNSPEC
Definition netsock2.h:54
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition netsock2.h:256
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition netsock2.h:517
static void ast_sockaddr_copy(struct ast_sockaddr *dst, const struct ast_sockaddr *src)
Copies the data from one ast_sockaddr to another.
Definition netsock2.h:167
int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str, int flags, int family)
Parses a string with an IPv4 or IPv6 address and place results into an array.
Definition netsock2.c:280
static int ast_sockaddr_isnull(const struct ast_sockaddr *addr)
Checks if the ast_sockaddr is null. "null" in this sense essentially means uninitialized,...
Definition netsock2.h:127
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition netsock2.h:532
static char * ast_sockaddr_stringify_addr(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only.
Definition netsock2.h:286
static void ast_sockaddr_setnull(struct ast_sockaddr *addr)
Sets address addr to null.
Definition netsock2.h:138
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_DATA_DIR
Definition options.c:159
static int total
Definition res_adsi.c:970
static int reload(void)
const char * method
Definition res_pjsip.c:1273
static void cleanup(void)
Clean up any old apps that we don't need any more.
Definition res_stasis.c:327
#define NULL
Definition resample.c:96
String manipulation functions.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition strings.h:1139
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition strings.c:238
size_t attribute_pure ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition strings.h:730
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition strings.h:80
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition utils.c:2233
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
char *attribute_pure ast_skip_nonblanks(const char *str)
Gets a pointer to first whitespace character in a string.
Definition strings.h:204
char * ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
Strip leading/trailing whitespace and quotes from a string.
Definition utils.c:1852
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition strings.h:1113
char * ast_trim_blanks(char *str)
Trims trailing whitespace characters from a string.
Definition strings.h:186
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition strings.h:97
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition strings.h:223
char *attribute_pure ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition strings.h:161
Generic container type.
Wrapper for an ast_acl linked list.
Definition acl.h:76
const char * text
Definition http.c:196
enum ast_http_method method
Definition http.c:195
descriptor for a cli entry.
Definition cli.h:171
char * command
Definition cli.h:186
const char * usage
Definition cli.h:177
Structure used to handle boolean flags.
Definition utils.h:220
HTTP authentication information.
Definition http.h:125
char * password
Definition http.h:129
char * userid
Definition http.h:127
struct ast_tcptls_session_args args
Definition http.c:122
char * name
Definition http.c:123
char * address
Definition http.c:124
Definition of a URI handler.
Definition http.h:102
struct ast_http_uri::@237 entry
unsigned int has_subtree
Definition http.h:108
unsigned int no_decode_uri
Definition http.h:114
ast_http_callback callback
Definition http.h:107
unsigned int dmallocd
Definition http.h:112
const char * prefix
Definition http.h:106
const char * description
Definition http.h:104
const char * uri
Definition http.h:105
void * data
Definition http.h:116
const char * key
Definition http.h:118
unsigned int mallocd
Definition http.h:110
Abstract JSON element (object, array, string, int, ...).
struct ast_module * self
Definition module.h:356
Socket address structure.
Definition netsock2.h:97
Support for dynamic strings.
Definition strings.h:623
arguments for the accepting thread
Definition tcptls.h:130
void *(* accept_fn)(void *)
Definition tcptls.h:140
struct ast_sockaddr local_address
Definition tcptls.h:131
const char * name
Definition tcptls.h:143
void *(* worker_fn)(void *)
Definition tcptls.h:142
struct ast_sockaddr old_address
Definition tcptls.h:132
struct ast_tls_config * tls_cfg
Definition tcptls.h:135
describes a server instance
Definition tcptls.h:151
struct ast_iostream * stream
Definition tcptls.h:162
struct ast_sockaddr remote_address
Definition tcptls.h:153
char * certfile
Definition tcptls.h:90
char * cipher
Definition tcptls.h:92
char * pvtfile
Definition tcptls.h:91
char * capath
Definition tcptls.h:94
struct ast_flags flags
Definition tcptls.h:95
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Per-path ACL restriction.
Definition http.c:182
struct http_restriction::@384 entry
char path[]
Definition http.c:185
struct ast_acl_list * acl
Definition http.c:184
char * dest
Definition http.c:175
char target[0]
Definition http.c:176
struct http_uri_redirect::@383 entry
struct ast_flags flags
Definition http.c:519
Definition http.c:142
int value
Definition syslog.c:37
Generic support for tcp/tls servers in Asterisk.
void * ast_tcptls_server_root(void *)
Definition tcptls.c:280
#define AST_CERTFILE
Definition tcptls.h:63
void ast_tcptls_server_stop(struct ast_tcptls_session_args *desc)
Shutdown a running server if there is one.
Definition tcptls.c:933
int ast_ssl_setup(struct ast_tls_config *cfg)
Set up an SSL server.
Definition tcptls.c:577
void ast_tcptls_server_start(struct ast_tcptls_session_args *desc)
This is a generic (re)start routine for a TCP server, which does the socket/bind/listen and starts a ...
Definition tcptls.c:768
@ AST_SSL_SERVER_CIPHER_ORDER
Definition tcptls.h:79
@ AST_SSL_DISABLE_TLSV1
Definition tcptls.h:81
int ast_tls_read_conf(struct ast_tls_config *tls_cfg, struct ast_tcptls_session_args *tls_desc, const char *varname, const char *value)
Used to parse conf files containing tls/ssl options.
Definition tcptls.c:959
void ast_tcptls_close_session_file(struct ast_tcptls_session_instance *tcptls_session)
Closes a tcptls session instance's file and/or file descriptor. The tcptls_session will be set to NUL...
Definition tcptls.c:923
int done
static struct test_val a
static struct test_val c
Time-related functions and macros.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition time.h:159
FILE * out
Definition utils/frame.c:33
FILE * in
Definition utils/frame.c:33
static char base64[64]
Definition utils.c:80
Utility functions.
#define ast_test_flag(p, flag)
Definition utils.h:64
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:981
int ast_base64decode(unsigned char *dst, const char *src, int max)
Decode data from base64.
Definition utils.c:296
#define ast_assert(a)
Definition utils.h:779
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
Definition utils.c:404
int ast_xml_escape(const char *string, char *outbuf, size_t buflen)
Escape reserved characters for use in XML.
Definition utils.c:898
#define ast_set_flag(p, flag)
Definition utils.h:71
#define ARRAY_LEN(a)
Definition utils.h:706
void ast_uri_decode(char *s, struct ast_flags spec)
Decode URI, URN, URL (overwrite string)
Definition utils.c:760
#define ast_set_flags_to(p, flag, value)
Definition utils.h:105
const struct ast_flags ast_uri_http_legacy
Definition utils.c:718