2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
21 * \brief http server for AMI access
23 * \author Mark Spencer <markster@digium.com>
25 * This program implements a tiny http server
26 * and was inspired by micro-httpd by Jef Poskanzer
28 * \ref AstHTTP - AMI over the http protocol
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35 #include <sys/types.h>
41 #include <netinet/in.h>
43 #include <sys/socket.h>
45 #include <sys/signal.h>
46 #include <arpa/inet.h>
51 #include "minimime/mm.h"
53 #include "asterisk/cli.h"
54 #include "asterisk/http.h"
55 #include "asterisk/utils.h"
56 #include "asterisk/strings.h"
57 #include "asterisk/options.h"
58 #include "asterisk/config.h"
59 #include "asterisk/stringfields.h"
60 #include "asterisk/version.h"
61 #include "asterisk/manager.h"
64 #define DEFAULT_PREFIX "/asterisk"
66 /* See http.h for more information about the SSL implementation */
67 #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
68 #define DO_SSL /* comment in/out if you want to support ssl */
71 static struct tls_config http_tls_cfg;
73 static void *httpd_helper_thread(void *arg);
76 * we have up to two accepting threads, one for http, one for https
78 static struct server_args http_desc = {
80 .master = AST_PTHREADT_NULL,
83 .name = "http server",
84 .accept_fn = server_root,
85 .worker_fn = httpd_helper_thread,
88 static struct server_args https_desc = {
90 .master = AST_PTHREADT_NULL,
91 .tls_cfg = &http_tls_cfg,
93 .name = "https server",
94 .accept_fn = server_root,
95 .worker_fn = httpd_helper_thread,
98 static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri); /*!< list of supported handlers */
100 struct ast_http_post_mapping {
101 AST_RWLIST_ENTRY(ast_http_post_mapping) entry;
106 static AST_RWLIST_HEAD_STATIC(post_mappings, ast_http_post_mapping);
108 /* all valid URIs must be prepended by the string in prefix. */
109 static char prefix[MAX_PREFIX];
110 static int enablestatic;
112 /*! \brief Limit the kinds of files we're willing to serve up */
117 { "png", "image/png" },
118 { "jpg", "image/jpeg" },
119 { "js", "application/x-javascript" },
120 { "wav", "audio/x-wav" },
121 { "mp3", "audio/mpeg" },
122 { "svg", "image/svg+xml" },
123 { "svgz", "image/svg+xml" },
124 { "gif", "image/gif" },
127 struct http_uri_redirect {
128 AST_LIST_ENTRY(http_uri_redirect) entry;
133 static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
135 static const char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
139 for (x=0;x<sizeof(mimetypes) / sizeof(mimetypes[0]); x++) {
140 if (!strcasecmp(ftype, mimetypes[x].ext))
141 return mimetypes[x].mtype;
144 snprintf(wkspace, wkspacelen, "text/%s", ftype ? ftype : "plain");
148 static struct ast_str *static_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
150 struct ast_str *result;
159 /* Yuck. I'm not really sold on this, but if you don't deliver static content it makes your configuration
160 substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
161 if (!enablestatic || ast_strlen_zero(uri))
163 /* Disallow any funny filenames at all */
164 if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0]))
166 if (strstr(uri, "/.."))
169 if ((ftype = strrchr(uri, '.')))
171 mtype = ftype2mtype(ftype, wkspace, sizeof(wkspace));
173 /* Cap maximum length */
174 len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5;
179 sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
182 if (S_ISDIR(st.st_mode))
184 fd = open(path, O_RDONLY);
188 len = st.st_size + strlen(mtype) + 40;
189 result = ast_str_create(len);
190 if (result == NULL) /* XXX not really but... */
193 ast_str_append(&result, 0, "Content-type: %s\r\n\r\n", mtype);
194 *contentlength = read(fd, result->str + result->used, st.st_size);
195 if (*contentlength < 0) {
200 result->used += *contentlength;
206 *title = ast_strdup("Not Found");
207 return ast_http_error(404, "Not Found", NULL, "Nothing to see here. Move along.");
211 *title = ast_strdup("Access Denied");
212 return ast_http_error(403, "Access Denied", NULL, "Sorry, I cannot let you do that, Dave.");
216 static struct ast_str *httpstatus_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
218 struct ast_str *out = ast_str_create(512);
219 struct ast_variable *v;
224 ast_str_append(&out, 0,
226 "<title>Asterisk HTTP Status</title>\r\n"
227 "<body bgcolor=\"#ffffff\">\r\n"
228 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
229 "<h2> Asterisk™ HTTP Status</h2></td></tr>\r\n");
231 ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
232 ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
233 ast_inet_ntoa(http_desc.oldsin.sin_addr));
234 ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
235 ntohs(http_desc.oldsin.sin_port));
236 if (http_tls_cfg.enabled)
237 ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
238 ntohs(https_desc.oldsin.sin_port));
239 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
240 for (v = vars; v; v = v->next) {
241 if (strncasecmp(v->name, "cookie_", 7))
242 ast_str_append(&out, 0, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
244 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
245 for (v = vars; v; v = v->next) {
246 if (!strncasecmp(v->name, "cookie_", 7))
247 ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
249 ast_str_append(&out, 0, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
253 static struct ast_http_uri statusuri = {
254 .callback = httpstatus_callback,
255 .description = "Asterisk HTTP General Status",
260 static struct ast_http_uri staticuri = {
261 .callback = static_callback,
262 .description = "Asterisk HTTP Static Delivery",
268 struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
270 struct ast_str *out = ast_str_create(512);
274 "Content-type: text/html\r\n"
277 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
279 "<title>%d %s</title>\r\n"
284 "<address>Asterisk Server</address>\r\n"
285 "</body></html>\r\n",
286 (extra_header ? extra_header : ""), status, title, title, text);
291 * Link the new uri into the list.
293 * They are sorted by length of
294 * the string, not alphabetically. Duplicate entries are not replaced,
295 * but the insertion order (using <= and not just <) makes sure that
296 * more recent insertions hide older ones.
297 * On a lookup, we just scan the list and stop at the first matching entry.
299 int ast_http_uri_link(struct ast_http_uri *urih)
301 struct ast_http_uri *uri;
302 int len = strlen(urih->uri);
304 AST_RWLIST_WRLOCK(&uris);
306 if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) {
307 AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
308 AST_RWLIST_UNLOCK(&uris);
312 AST_RWLIST_TRAVERSE(&uris, uri, entry) {
313 if ( AST_RWLIST_NEXT(uri, entry)
314 && strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len ) {
315 AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
316 AST_RWLIST_UNLOCK(&uris);
321 AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
323 AST_RWLIST_UNLOCK(&uris);
328 void ast_http_uri_unlink(struct ast_http_uri *urih)
330 AST_RWLIST_WRLOCK(&uris);
331 AST_RWLIST_REMOVE(&uris, urih, entry);
332 AST_RWLIST_UNLOCK(&uris);
335 /*! \note This assumes that the post_mappings list is locked */
336 static struct ast_http_post_mapping *find_post_mapping(const char *uri)
338 struct ast_http_post_mapping *post_map;
340 if (!ast_strlen_zero(prefix) && strncmp(prefix, uri, strlen(prefix))) {
341 ast_debug(1, "URI %s does not have prefix %s\n", uri, prefix);
345 uri += strlen(prefix);
349 AST_RWLIST_TRAVERSE(&post_mappings, post_map, entry) {
350 if (!strcmp(uri, post_map->from))
357 static int get_filename(struct mm_mimepart *part, char *fn, size_t fn_len)
359 const char *filename;
361 filename = mm_content_getdispositionparambyname(part->type, "filename");
363 if (ast_strlen_zero(filename))
366 ast_copy_string(fn, filename, fn_len);
371 static void post_raw(struct mm_mimepart *part, const char *post_dir, const char *fn)
373 char filename[PATH_MAX];
378 snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn);
380 ast_debug(1, "Posting raw data to %s\n", filename);
382 if (!(f = fopen(filename, "w"))) {
383 ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename);
387 if (!(body = mm_mimepart_getbody(part, 0))) {
388 ast_debug(1, "Couldn't get the mimepart body\n");
392 body_len = mm_mimepart_getlength(part);
394 ast_debug(1, "Body length is %ld\n", (long int)body_len);
396 fwrite(body, 1, body_len, f);
401 static struct ast_str *handle_post(struct server_instance *ser, char *uri,
402 int *status, char **title, int *contentlength, struct ast_variable *headers,
403 struct ast_variable *cookies)
408 struct ast_variable *var;
412 struct ast_http_post_mapping *post_map;
413 const char *post_dir;
414 unsigned long ident = 0;
416 for (var = cookies; var; var = var->next) {
417 if (strcasecmp(var->name, "mansession_id"))
420 if (sscanf(var->value, "%lx", &ident) != 1) {
422 *title = ast_strdup("Bad Request");
423 return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
426 if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
428 *title = ast_strdup("Unauthorized");
429 return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
436 *title = ast_strdup("Unauthorized");
437 return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
440 if (!(f = tmpfile()))
443 for (var = headers; var; var = var->next) {
444 if (!strcasecmp(var->name, "Content-Length")) {
445 if ((sscanf(var->value, "%u", &content_len)) != 1) {
446 ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
450 ast_debug(1, "Got a Content-Length of %d\n", content_len);
451 } else if (!strcasecmp(var->name, "Content-Type"))
452 fprintf(f, "Content-Type: %s\r\n\r\n", var->value);
455 while ((res = fread(&buf, 1, 1, ser->f))) {
456 fwrite(&buf, 1, 1, f);
462 if (fseek(f, SEEK_SET, 0)) {
463 ast_debug(1, "Failed to seek temp file back to beginning.\n");
468 AST_RWLIST_RDLOCK(&post_mappings);
469 if (!(post_map = find_post_mapping(uri))) {
470 ast_debug(1, "%s is not a valid URI for POST\n", uri);
471 AST_RWLIST_UNLOCK(&post_mappings);
474 *title = ast_strdup("Not Found");
475 return ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server.");
477 post_dir = ast_strdupa(post_map->to);
479 AST_RWLIST_UNLOCK(&post_mappings);
481 ast_debug(1, "Going to post files to dir %s\n", post_dir);
483 if (!(ctx = mm_context_new())) {
488 mm_res = mm_parse_fileptr(ctx, f, MM_PARSE_LOOSE, 0);
491 ast_log(LOG_ERROR, "Error parsing MIME data\n");
492 mm_context_free(ctx);
494 *title = ast_strdup("Bad Request");
495 return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
498 mm_res = mm_context_countparts(ctx);
500 ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
501 mm_context_free(ctx);
503 *title = ast_strdup("Bad Request");
504 return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
508 if (mm_context_iscomposite(ctx))
509 ast_debug(1, "Found %d MIME parts\n", mm_res - 1);
511 ast_debug(1, "We have a flat (not multi-part) message\n");
514 for (i = 1; i < mm_res; i++) {
515 struct mm_mimepart *part;
518 if (!(part = mm_context_getpart(ctx, i))) {
519 ast_debug(1, "Failed to get mime part num %d\n", i);
523 if (get_filename(part, fn, sizeof(fn))) {
524 ast_debug(1, "Failed to retrieve a filename for part num %d\n", i);
529 ast_debug(1, "This part has no content struct?\n");
533 /* XXX This assumes the MIME part body is not encoded! */
534 post_raw(part, post_dir, fn);
537 mm_context_free(ctx);
540 *title = ast_strdup("OK");
541 return ast_http_error(200, "OK", NULL, "File successfully uploaded.");
544 static struct ast_str *handle_uri(struct sockaddr_in *sin, char *uri, int *status,
545 char **title, int *contentlength, struct ast_variable **cookies,
546 unsigned int *static_content)
549 struct ast_str *out = NULL;
551 struct ast_http_uri *urih=NULL;
553 struct ast_variable *vars=NULL, *v, *prev = NULL;
554 struct http_uri_redirect *redirect;
556 strsep(¶ms, "?");
557 /* Extract arguments from the request and store them in variables. */
561 while ((val = strsep(¶ms, "&"))) {
562 var = strsep(&val, "=");
568 if ((v = ast_variable_new(var, val))) {
578 * Append the cookies to the variables (the only reason to have them
579 * at the end is to avoid another pass of the cookies list to find
583 prev->next = *cookies;
589 AST_RWLIST_RDLOCK(&uri_redirects);
590 AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
591 if (!strcasecmp(uri, redirect->target)) {
593 snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest);
594 out = ast_http_error(302, "Moved Temporarily", buf,
595 "There is no spoon...");
597 *title = ast_strdup("Moved Temporarily");
601 AST_RWLIST_UNLOCK(&uri_redirects);
605 /* We want requests to start with the prefix and '/' */
607 if (l && !strncasecmp(uri, prefix, l) && uri[l] == '/') {
609 /* scan registered uris to see if we match one. */
610 AST_RWLIST_RDLOCK(&uris);
611 AST_RWLIST_TRAVERSE(&uris, urih, entry) {
612 l = strlen(urih->uri);
613 c = uri + l; /* candidate */
614 if (strncasecmp(urih->uri, uri, l) /* no match */
615 || (*c && *c != '/')) /* substring */
619 if (!*c || urih->has_subtree) {
625 AST_RWLIST_UNLOCK(&uris);
628 if (urih->static_content)
630 out = urih->callback(sin, uri, vars, status, title, contentlength);
631 AST_RWLIST_UNLOCK(&uris);
633 out = ast_http_error(404, "Not Found", NULL,
634 "The requested URL was not found on this server.");
636 *title = ast_strdup("Not Found");
640 ast_variables_destroy(vars);
645 #if defined(HAVE_FUNOPEN)
649 #define HOOK_T ssize_t
653 * replacement read/write functions for SSL support.
654 * We use wrappers rather than SSL_read/SSL_write directly so
655 * we can put in some debugging.
657 static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
659 int i = SSL_read(cookie, buf, len-1);
663 ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
668 static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
671 char *s = alloca(len+1);
672 strncpy(s, buf, len);
674 ast_verbose("ssl write size %d <%s>\n", (int)len, s);
676 return SSL_write(cookie, buf, len);
679 static int ssl_close(void *cookie)
681 close(SSL_get_fd(cookie));
682 SSL_shutdown(cookie);
689 * creates a FILE * from the fd passed by the accept thread.
690 * This operation is potentially expensive (certificate verification),
691 * so we do it in the child thread context.
693 static void *make_file_from_fd(void *data)
695 struct server_instance *ser = data;
698 * open a FILE * as appropriate.
700 if (!ser->parent->tls_cfg)
701 ser->f = fdopen(ser->fd, "w+");
703 else if ( (ser->ssl = SSL_new(ser->parent->tls_cfg->ssl_ctx)) ) {
704 SSL_set_fd(ser->ssl, ser->fd);
705 if (SSL_accept(ser->ssl) == 0)
706 ast_verbose(" error setting up ssl connection");
708 #if defined(HAVE_FUNOPEN) /* the BSD interface */
709 ser->f = funopen(ser->ssl, ssl_read, ssl_write, NULL, ssl_close);
711 #elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */
712 static const cookie_io_functions_t cookie_funcs = {
713 ssl_read, ssl_write, NULL, ssl_close
715 ser->f = fopencookie(ser->ssl, "w+", cookie_funcs);
717 /* could add other methods here */
720 if (!ser->f) /* no success opening descriptor stacking */
727 ast_log(LOG_WARNING, "FILE * open failed!\n");
731 return ser->parent->worker_fn(ser);
734 static void *httpd_helper_thread(void *data)
738 struct server_instance *ser = data;
739 struct ast_variable *var, *prev=NULL, *vars=NULL, *headers = NULL;
740 char *uri, *title=NULL;
741 int status = 200, contentlength = 0;
742 struct ast_str *out = NULL;
743 unsigned int static_content = 0;
745 if (!fgets(buf, sizeof(buf), ser->f))
748 uri = ast_skip_nonblanks(buf); /* Skip method */
752 uri = ast_skip_blanks(uri); /* Skip white space */
754 if (*uri) { /* terminate at the first blank */
755 char *c = ast_skip_nonblanks(uri);
760 /* process "Cookie: " lines */
761 while (fgets(cookie, sizeof(cookie), ser->f)) {
765 /* Trim trailing characters */
766 ast_trim_blanks(cookie);
767 if (ast_strlen_zero(cookie))
769 if (strncasecmp(cookie, "Cookie: ", 8)) {
772 value = ast_strdupa(cookie);
773 name = strsep(&value, ":");
776 value = ast_skip_blanks(value);
777 if (ast_strlen_zero(value))
779 var = ast_variable_new(name, value);
787 /* TODO - The cookie parsing code below seems to work
788 in IE6 and FireFox 1.5. However, it is not entirely
789 correct, and therefore may not work in all
791 For more details see RFC 2109 and RFC 2965 */
793 /* FireFox cookie strings look like:
794 Cookie: mansession_id="********"
795 InternetExplorer's look like:
796 Cookie: $Version="1"; mansession_id="********" */
798 /* If we got a FireFox cookie string, the name's right
800 vname = ast_skip_blanks(cookie + 8);
802 /* If we got an IE cookie string, we need to skip to
803 past the version to get to the name */
806 if (!vname) /* no name ? */
808 vname = ast_skip_blanks(vname);
810 vval = strchr(vname, '=');
813 /* Ditch the = and the quotes */
817 if ( (l = strlen(vval)) )
818 vval[l - 1] = '\0'; /* trim trailing quote */
819 var = ast_variable_new(vname, vval);
830 out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
831 else if (!strcasecmp(buf, "post"))
832 out = handle_post(ser, uri, &status, &title, &contentlength, headers, vars);
833 else if (strcasecmp(buf, "get"))
834 out = ast_http_error(501, "Not Implemented", NULL,
835 "Attempt to use unimplemented / unsupported method");
836 else /* try to serve it */
837 out = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars, &static_content);
839 /* If they aren't mopped up already, clean up the cookies */
841 ast_variables_destroy(vars);
844 out = ast_http_error(500, "Internal Error", NULL, "Internal Server Error");
846 time_t t = time(NULL);
849 strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
850 fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
851 "Server: Asterisk/%s\r\n"
853 "Connection: close\r\n"
855 status, title ? title : "OK", ASTERISK_VERSION, timebuf,
856 static_content ? "" : "Cache-Control: no-cache, no-store\r\n");
857 if (!contentlength) { /* opaque body ? just dump it hoping it is properly formatted */
858 fprintf(ser->f, "%s", out->str);
860 char *tmp = strstr(out->str, "\r\n\r\n");
863 fprintf(ser->f, "Content-length: %d\r\n", contentlength);
864 /* first write the header, then the body */
865 fwrite(out->str, 1, (tmp + 4 - out->str), ser->f);
866 fwrite(tmp + 4, 1, contentlength, ser->f);
880 void *server_root(void *data)
882 struct server_args *desc = data;
884 struct sockaddr_in sin;
886 struct server_instance *ser;
892 if (desc->periodic_fn)
893 desc->periodic_fn(desc);
894 i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
897 sinlen = sizeof(sin);
898 fd = accept(desc->accept_fd, (struct sockaddr *)&sin, &sinlen);
900 if ((errno != EAGAIN) && (errno != EINTR))
901 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
904 ser = ast_calloc(1, sizeof(*ser));
906 ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
910 flags = fcntl(fd, F_GETFL);
911 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
914 memcpy(&ser->requestor, &sin, sizeof(ser->requestor));
916 if (ast_pthread_create_detached_background(&launched, NULL, make_file_from_fd, ser)) {
917 ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
926 int ssl_setup(struct tls_config *cfg)
934 SSL_load_error_strings();
935 SSLeay_add_ssl_algorithms();
936 cfg->ssl_ctx = SSL_CTX_new( SSLv23_server_method() );
937 if (!ast_strlen_zero(cfg->certfile)) {
938 if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
939 SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
940 SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 ) {
941 ast_verbose("ssl cert error <%s>", cfg->certfile);
947 if (!ast_strlen_zero(cfg->cipher)) {
948 if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) {
949 ast_verbose("ssl cipher error <%s>", cfg->cipher);
955 ast_verbose("ssl cert ok");
961 * This is a generic (re)start routine for a TCP server,
962 * which does the socket/bind/listen and starts a thread for handling
965 void server_start(struct server_args *desc)
970 /* Do nothing if nothing has changed */
971 if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
972 ast_debug(1, "Nothing changed in %s\n", desc->name);
976 desc->oldsin = desc->sin;
978 /* Shutdown a running server if there is one */
979 if (desc->master != AST_PTHREADT_NULL) {
980 pthread_cancel(desc->master);
981 pthread_kill(desc->master, SIGURG);
982 pthread_join(desc->master, NULL);
985 if (desc->accept_fd != -1)
986 close(desc->accept_fd);
988 /* If there's no new server, stop here */
989 if (desc->sin.sin_family == 0)
992 desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0);
993 if (desc->accept_fd < 0) {
994 ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
995 desc->name, strerror(errno));
999 setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
1000 if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
1001 ast_log(LOG_NOTICE, "Unable to bind %s to %s:%d: %s\n",
1003 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
1007 if (listen(desc->accept_fd, 10)) {
1008 ast_log(LOG_NOTICE, "Unable to listen for %s!\n", desc->name);
1011 flags = fcntl(desc->accept_fd, F_GETFL);
1012 fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
1013 if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
1014 ast_log(LOG_NOTICE, "Unable to launch %s on %s:%d: %s\n",
1016 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
1023 close(desc->accept_fd);
1024 desc->accept_fd = -1;
1028 * \brief Add a new URI redirect
1029 * The entries in the redirect list are sorted by length, just like the list
1032 static void add_redirect(const char *value)
1034 char *target, *dest;
1035 struct http_uri_redirect *redirect, *cur;
1036 unsigned int target_len;
1037 unsigned int total_len;
1039 dest = ast_strdupa(value);
1040 dest = ast_skip_blanks(dest);
1041 target = strsep(&dest, " ");
1042 target = ast_skip_blanks(target);
1043 target = strsep(&target, " "); /* trim trailing whitespace */
1046 ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
1050 target_len = strlen(target) + 1;
1051 total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
1053 if (!(redirect = ast_calloc(1, total_len)))
1056 redirect->dest = redirect->target + target_len;
1057 strcpy(redirect->target, target);
1058 strcpy(redirect->dest, dest);
1060 AST_RWLIST_WRLOCK(&uri_redirects);
1062 target_len--; /* So we can compare directly with strlen() */
1063 if ( AST_RWLIST_EMPTY(&uri_redirects)
1064 || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
1065 AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
1066 AST_RWLIST_UNLOCK(&uri_redirects);
1070 AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
1071 if ( AST_RWLIST_NEXT(cur, entry)
1072 && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
1073 AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
1074 AST_RWLIST_UNLOCK(&uri_redirects);
1079 AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
1081 AST_RWLIST_UNLOCK(&uri_redirects);
1084 static void destroy_post_mapping(struct ast_http_post_mapping *post_map)
1087 ast_free(post_map->from);
1089 ast_free(post_map->to);
1093 static void destroy_post_mappings(void)
1095 struct ast_http_post_mapping *post_map;
1097 AST_RWLIST_WRLOCK(&post_mappings);
1098 while ((post_map = AST_RWLIST_REMOVE_HEAD(&post_mappings, entry)))
1099 destroy_post_mapping(post_map);
1100 AST_RWLIST_UNLOCK(&post_mappings);
1103 static void add_post_mapping(const char *from, const char *to)
1105 struct ast_http_post_mapping *post_map;
1107 if (!(post_map = ast_calloc(1, sizeof(*post_map))))
1110 if (!(post_map->from = ast_strdup(from))) {
1111 destroy_post_mapping(post_map);
1115 if (!(post_map->to = ast_strdup(to))) {
1116 destroy_post_mapping(post_map);
1120 AST_RWLIST_WRLOCK(&post_mappings);
1121 AST_RWLIST_INSERT_TAIL(&post_mappings, post_map, entry);
1122 AST_RWLIST_UNLOCK(&post_mappings);
1125 static int __ast_http_load(int reload)
1127 struct ast_config *cfg;
1128 struct ast_variable *v;
1130 int newenablestatic=0;
1132 struct ast_hostent ahp;
1133 char newprefix[MAX_PREFIX];
1134 int have_sslbindaddr = 0;
1135 struct http_uri_redirect *redirect;
1137 /* default values */
1138 memset(&http_desc.sin, 0, sizeof(http_desc.sin));
1139 http_desc.sin.sin_port = htons(8088);
1141 memset(&https_desc.sin, 0, sizeof(https_desc.sin));
1142 https_desc.sin.sin_port = htons(8089);
1144 strcpy(newprefix, DEFAULT_PREFIX);
1146 http_tls_cfg.enabled = 0;
1147 if (http_tls_cfg.certfile)
1148 ast_free(http_tls_cfg.certfile);
1149 http_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
1150 if (http_tls_cfg.cipher)
1151 ast_free(http_tls_cfg.cipher);
1152 http_tls_cfg.cipher = ast_strdup("");
1154 AST_RWLIST_WRLOCK(&uri_redirects);
1155 while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry)))
1157 AST_RWLIST_UNLOCK(&uri_redirects);
1159 destroy_post_mappings();
1161 cfg = ast_config_load("http.conf");
1163 v = ast_variable_browse(cfg, "general");
1164 for (; v; v = v->next) {
1165 if (!strcasecmp(v->name, "enabled"))
1166 enabled = ast_true(v->value);
1167 else if (!strcasecmp(v->name, "sslenable"))
1168 http_tls_cfg.enabled = ast_true(v->value);
1169 else if (!strcasecmp(v->name, "sslbindport"))
1170 https_desc.sin.sin_port = htons(atoi(v->value));
1171 else if (!strcasecmp(v->name, "sslcert")) {
1172 ast_free(http_tls_cfg.certfile);
1173 http_tls_cfg.certfile = ast_strdup(v->value);
1174 } else if (!strcasecmp(v->name, "sslcipher")) {
1175 ast_free(http_tls_cfg.cipher);
1176 http_tls_cfg.cipher = ast_strdup(v->value);
1178 else if (!strcasecmp(v->name, "enablestatic"))
1179 newenablestatic = ast_true(v->value);
1180 else if (!strcasecmp(v->name, "bindport"))
1181 http_desc.sin.sin_port = htons(atoi(v->value));
1182 else if (!strcasecmp(v->name, "sslbindaddr")) {
1183 if ((hp = ast_gethostbyname(v->value, &ahp))) {
1184 memcpy(&https_desc.sin.sin_addr, hp->h_addr, sizeof(https_desc.sin.sin_addr));
1185 have_sslbindaddr = 1;
1187 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
1189 } else if (!strcasecmp(v->name, "bindaddr")) {
1190 if ((hp = ast_gethostbyname(v->value, &ahp))) {
1191 memcpy(&http_desc.sin.sin_addr, hp->h_addr, sizeof(http_desc.sin.sin_addr));
1193 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
1195 } else if (!strcasecmp(v->name, "prefix")) {
1196 if (!ast_strlen_zero(v->value)) {
1198 ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
1200 newprefix[0] = '\0';
1202 } else if (!strcasecmp(v->name, "redirect")) {
1203 add_redirect(v->value);
1205 ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
1209 for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next)
1210 add_post_mapping(v->name, v->value);
1212 ast_config_destroy(cfg);
1214 if (!have_sslbindaddr)
1215 https_desc.sin.sin_addr = http_desc.sin.sin_addr;
1217 http_desc.sin.sin_family = https_desc.sin.sin_family = AF_INET;
1218 if (strcmp(prefix, newprefix))
1219 ast_copy_string(prefix, newprefix, sizeof(prefix));
1220 enablestatic = newenablestatic;
1221 server_start(&http_desc);
1222 if (ssl_setup(https_desc.tls_cfg))
1223 server_start(&https_desc);
1228 static int handle_show_http(int fd, int argc, char *argv[])
1230 struct ast_http_uri *urih;
1231 struct http_uri_redirect *redirect;
1232 struct ast_http_post_mapping *post_map;
1235 return RESULT_SHOWUSAGE;
1237 ast_cli(fd, "HTTP Server Status:\n");
1238 ast_cli(fd, "Prefix: %s\n", prefix);
1239 if (!http_desc.oldsin.sin_family)
1240 ast_cli(fd, "Server Disabled\n\n");
1242 ast_cli(fd, "Server Enabled and Bound to %s:%d\n\n",
1243 ast_inet_ntoa(http_desc.oldsin.sin_addr),
1244 ntohs(http_desc.oldsin.sin_port));
1245 if (http_tls_cfg.enabled)
1246 ast_cli(fd, "HTTPS Server Enabled and Bound to %s:%d\n\n",
1247 ast_inet_ntoa(https_desc.oldsin.sin_addr),
1248 ntohs(https_desc.oldsin.sin_port));
1251 ast_cli(fd, "Enabled URI's:\n");
1252 AST_RWLIST_RDLOCK(&uris);
1253 if (AST_RWLIST_EMPTY(&uris)) {
1254 ast_cli(fd, "None.\n");
1256 AST_RWLIST_TRAVERSE(&uris, urih, entry)
1257 ast_cli(fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
1259 AST_RWLIST_UNLOCK(&uris);
1261 ast_cli(fd, "\nEnabled Redirects:\n");
1262 AST_RWLIST_RDLOCK(&uri_redirects);
1263 AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
1264 ast_cli(fd, " %s => %s\n", redirect->target, redirect->dest);
1265 if (AST_RWLIST_EMPTY(&uri_redirects))
1266 ast_cli(fd, " None.\n");
1267 AST_RWLIST_UNLOCK(&uri_redirects);
1270 ast_cli(fd, "\nPOST mappings:\n");
1271 AST_RWLIST_RDLOCK(&post_mappings);
1272 AST_LIST_TRAVERSE(&post_mappings, post_map, entry)
1273 ast_cli(fd, "%s/%s => %s\n", prefix, post_map->from, post_map->to);
1274 ast_cli(fd, "%s\n", AST_LIST_EMPTY(&post_mappings) ? "None.\n" : "");
1275 AST_RWLIST_UNLOCK(&post_mappings);
1277 return RESULT_SUCCESS;
1280 int ast_http_reload(void)
1282 return __ast_http_load(1);
1285 static char show_http_help[] =
1286 "Usage: http show status\n"
1287 " Lists status of internal HTTP engine\n";
1289 static struct ast_cli_entry cli_http[] = {
1290 { { "http", "show", "status", NULL },
1291 handle_show_http, "Display HTTP server status",
1295 int ast_http_init(void)
1298 mm_codec_registerdefaultcodecs();
1300 ast_http_uri_link(&statusuri);
1301 ast_http_uri_link(&staticuri);
1302 ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
1304 return __ast_http_load(0);