00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #include "asterisk.h"
00032
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 315201 $")
00034
00035 #include <time.h>
00036 #include <sys/time.h>
00037 #include <sys/stat.h>
00038 #include <sys/signal.h>
00039 #include <fcntl.h>
00040
00041 #include "asterisk/paths.h"
00042 #include "asterisk/network.h"
00043 #include "asterisk/cli.h"
00044 #include "asterisk/tcptls.h"
00045 #include "asterisk/http.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/strings.h"
00048 #include "asterisk/config.h"
00049 #include "asterisk/stringfields.h"
00050 #include "asterisk/ast_version.h"
00051 #include "asterisk/manager.h"
00052 #include "asterisk/_private.h"
00053 #include "asterisk/astobj2.h"
00054
00055 #define MAX_PREFIX 80
00056 #define DEFAULT_SESSION_LIMIT 100
00057
00058
00059 #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
00060 #define DO_SSL
00061 #endif
00062
00063 static int session_limit = DEFAULT_SESSION_LIMIT;
00064 static int session_count = 0;
00065
00066 static struct ast_tls_config http_tls_cfg;
00067
00068 static void *httpd_helper_thread(void *arg);
00069
00070
00071
00072
00073 static struct ast_tcptls_session_args http_desc = {
00074 .accept_fd = -1,
00075 .master = AST_PTHREADT_NULL,
00076 .tls_cfg = NULL,
00077 .poll_timeout = -1,
00078 .name = "http server",
00079 .accept_fn = ast_tcptls_server_root,
00080 .worker_fn = httpd_helper_thread,
00081 };
00082
00083 static struct ast_tcptls_session_args https_desc = {
00084 .accept_fd = -1,
00085 .master = AST_PTHREADT_NULL,
00086 .tls_cfg = &http_tls_cfg,
00087 .poll_timeout = -1,
00088 .name = "https server",
00089 .accept_fn = ast_tcptls_server_root,
00090 .worker_fn = httpd_helper_thread,
00091 };
00092
00093 static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri);
00094
00095
00096 static char prefix[MAX_PREFIX];
00097 static int enablestatic;
00098
00099
00100 static struct {
00101 const char *ext;
00102 const char *mtype;
00103 } mimetypes[] = {
00104 { "png", "image/png" },
00105 { "xml", "text/xml" },
00106 { "jpg", "image/jpeg" },
00107 { "js", "application/x-javascript" },
00108 { "wav", "audio/x-wav" },
00109 { "mp3", "audio/mpeg" },
00110 { "svg", "image/svg+xml" },
00111 { "svgz", "image/svg+xml" },
00112 { "gif", "image/gif" },
00113 { "html", "text/html" },
00114 { "htm", "text/html" },
00115 { "css", "text/css" },
00116 { "cnf", "text/plain" },
00117 { "cfg", "text/plain" },
00118 { "bin", "application/octet-stream" },
00119 { "sbn", "application/octet-stream" },
00120 { "ld", "application/octet-stream" },
00121 };
00122
00123 struct http_uri_redirect {
00124 AST_LIST_ENTRY(http_uri_redirect) entry;
00125 char *dest;
00126 char target[0];
00127 };
00128
00129 static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
00130
00131 static const char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
00132 {
00133 int x;
00134
00135 if (ftype) {
00136 for (x = 0; x < ARRAY_LEN(mimetypes); x++) {
00137 if (!strcasecmp(ftype, mimetypes[x].ext)) {
00138 return mimetypes[x].mtype;
00139 }
00140 }
00141 }
00142
00143 snprintf(wkspace, wkspacelen, "text/%s", S_OR(ftype, "plain"));
00144
00145 return wkspace;
00146 }
00147
00148 static uint32_t manid_from_vars(struct ast_variable *sid) {
00149 uint32_t mngid;
00150
00151 while (sid && strcmp(sid->name, "mansession_id"))
00152 sid = sid->next;
00153
00154 if (!sid || sscanf(sid->value, "%30x", &mngid) != 1)
00155 return 0;
00156
00157 return mngid;
00158 }
00159
00160 void ast_http_prefix(char *buf, int len)
00161 {
00162 if (buf) {
00163 ast_copy_string(buf, prefix, len);
00164 }
00165 }
00166
00167 static struct ast_str *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 *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
00168 {
00169 char *path;
00170 char *ftype;
00171 const char *mtype;
00172 char wkspace[80];
00173 struct stat st;
00174 int len;
00175 int fd;
00176 struct timeval now = ast_tvnow();
00177 char buf[256];
00178 struct ast_tm tm;
00179
00180
00181
00182 if (!enablestatic || ast_strlen_zero(uri)) {
00183 goto out403;
00184 }
00185
00186
00187 if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0])) {
00188 goto out403;
00189 }
00190
00191 if (strstr(uri, "/..")) {
00192 goto out403;
00193 }
00194
00195 if ((ftype = strrchr(uri, '.'))) {
00196 ftype++;
00197 }
00198
00199 mtype = ftype2mtype(ftype, wkspace, sizeof(wkspace));
00200
00201
00202 if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
00203 goto out403;
00204 }
00205
00206 path = alloca(len);
00207 sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
00208 if (stat(path, &st)) {
00209 goto out404;
00210 }
00211
00212 if (S_ISDIR(st.st_mode)) {
00213 goto out404;
00214 }
00215
00216 if ((fd = open(path, O_RDONLY)) < 0) {
00217 goto out403;
00218 }
00219
00220 if (strstr(path, "/private/") && !astman_is_authed(manid_from_vars(vars))) {
00221 goto out403;
00222 }
00223
00224 ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
00225 fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
00226 "Server: Asterisk/%s\r\n"
00227 "Date: %s\r\n"
00228 "Connection: close\r\n"
00229 "Cache-Control: private\r\n"
00230 "Content-Length: %d\r\n"
00231 "Content-type: %s\r\n\r\n",
00232 ast_get_version(), buf, (int) st.st_size, mtype);
00233
00234 while ((len = read(fd, buf, sizeof(buf))) > 0) {
00235 if (fwrite(buf, 1, len, ser->f) != len) {
00236 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00237 }
00238 }
00239
00240 close(fd);
00241
00242 return NULL;
00243
00244 out404:
00245 return ast_http_error((*status = 404),
00246 (*title = ast_strdup("Not Found")),
00247 NULL, "The requested URL was not found on this server.");
00248
00249 out403:
00250 return ast_http_error((*status = 403),
00251 (*title = ast_strdup("Access Denied")),
00252 NULL, "You do not have permission to access the requested URL.");
00253 }
00254
00255
00256 static struct ast_str *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 *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
00257 {
00258 struct ast_str *out = ast_str_create(512);
00259 struct ast_variable *v;
00260
00261 if (out == NULL) {
00262 return out;
00263 }
00264
00265 ast_str_append(&out, 0,
00266 "\r\n"
00267 "<title>Asterisk HTTP Status</title>\r\n"
00268 "<body bgcolor=\"#ffffff\">\r\n"
00269 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
00270 "<h2> Asterisk™ HTTP Status</h2></td></tr>\r\n");
00271 ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
00272 ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
00273 ast_inet_ntoa(http_desc.old_address.sin_addr));
00274 ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
00275 ntohs(http_desc.old_address.sin_port));
00276
00277 if (http_tls_cfg.enabled) {
00278 ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
00279 ntohs(https_desc.old_address.sin_port));
00280 }
00281
00282 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00283
00284 for (v = vars; v; v = v->next) {
00285 if (strncasecmp(v->name, "cookie_", 7)) {
00286 ast_str_append(&out, 0, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
00287 }
00288 }
00289
00290 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00291
00292 for (v = vars; v; v = v->next) {
00293 if (!strncasecmp(v->name, "cookie_", 7)) {
00294 ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
00295 }
00296 }
00297
00298 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");
00299 return out;
00300 }
00301
00302 static struct ast_http_uri statusuri = {
00303 .callback = httpstatus_callback,
00304 .description = "Asterisk HTTP General Status",
00305 .uri = "httpstatus",
00306 .supports_get = 1,
00307 .data = NULL,
00308 .key = __FILE__,
00309 };
00310
00311 static struct ast_http_uri staticuri = {
00312 .callback = static_callback,
00313 .description = "Asterisk HTTP Static Delivery",
00314 .uri = "static",
00315 .has_subtree = 1,
00316 .static_content = 1,
00317 .supports_get = 1,
00318 .data = NULL,
00319 .key= __FILE__,
00320 };
00321
00322 struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
00323 {
00324 struct ast_str *out = ast_str_create(512);
00325
00326 if (out == NULL) {
00327 return out;
00328 }
00329
00330 ast_str_set(&out, 0,
00331 "Content-type: text/html\r\n"
00332 "%s"
00333 "\r\n"
00334 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
00335 "<html><head>\r\n"
00336 "<title>%d %s</title>\r\n"
00337 "</head><body>\r\n"
00338 "<h1>%s</h1>\r\n"
00339 "<p>%s</p>\r\n"
00340 "<hr />\r\n"
00341 "<address>Asterisk Server</address>\r\n"
00342 "</body></html>\r\n",
00343 (extra_header ? extra_header : ""), status, title, title, text);
00344
00345 return out;
00346 }
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357 int ast_http_uri_link(struct ast_http_uri *urih)
00358 {
00359 struct ast_http_uri *uri;
00360 int len = strlen(urih->uri);
00361
00362 if (!(urih->supports_get || urih->supports_post)) {
00363 ast_log(LOG_WARNING, "URI handler does not provide either GET or POST method: %s (%s)\n", urih->uri, urih->description);
00364 return -1;
00365 }
00366
00367 AST_RWLIST_WRLOCK(&uris);
00368
00369 if (AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len) {
00370 AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
00371 AST_RWLIST_UNLOCK(&uris);
00372
00373 return 0;
00374 }
00375
00376 AST_RWLIST_TRAVERSE(&uris, uri, entry) {
00377 if (AST_RWLIST_NEXT(uri, entry) &&
00378 strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len) {
00379 AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
00380 AST_RWLIST_UNLOCK(&uris);
00381
00382 return 0;
00383 }
00384 }
00385
00386 AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
00387
00388 AST_RWLIST_UNLOCK(&uris);
00389
00390 return 0;
00391 }
00392
00393 void ast_http_uri_unlink(struct ast_http_uri *urih)
00394 {
00395 AST_RWLIST_WRLOCK(&uris);
00396 AST_RWLIST_REMOVE(&uris, urih, entry);
00397 AST_RWLIST_UNLOCK(&uris);
00398 }
00399
00400 void ast_http_uri_unlink_all_with_key(const char *key)
00401 {
00402 struct ast_http_uri *urih;
00403 AST_RWLIST_WRLOCK(&uris);
00404 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&uris, urih, entry) {
00405 if (!strcmp(urih->key, key)) {
00406 AST_RWLIST_REMOVE_CURRENT(entry);
00407 }
00408 if (urih->dmallocd) {
00409 ast_free(urih->data);
00410 }
00411 if (urih->mallocd) {
00412 ast_free(urih);
00413 }
00414 }
00415 AST_RWLIST_TRAVERSE_SAFE_END;
00416 AST_RWLIST_UNLOCK(&uris);
00417 }
00418
00419
00420
00421
00422
00423
00424 static void http_decode(char *s)
00425 {
00426 char *t;
00427
00428 for (t = s; *t; t++) {
00429 if (*t == '+')
00430 *t = ' ';
00431 }
00432
00433 ast_uri_decode(s);
00434 }
00435
00436 static struct ast_str *handle_uri(struct ast_tcptls_session_instance *ser, char *uri, enum ast_http_method method,
00437 int *status, char **title, int *contentlength, struct ast_variable **cookies, struct ast_variable *headers,
00438 unsigned int *static_content)
00439 {
00440 char *c;
00441 struct ast_str *out = NULL;
00442 char *params = uri;
00443 struct ast_http_uri *urih = NULL;
00444 int l;
00445 struct ast_variable *vars = NULL, *v, *prev = NULL;
00446 struct http_uri_redirect *redirect;
00447 int saw_method = 0;
00448
00449 ast_debug(2, "HTTP Request URI is %s \n", uri);
00450
00451
00452 if (method == AST_HTTP_GET) {
00453 strsep(¶ms, "?");
00454
00455
00456
00457
00458
00459
00460 if (params) {
00461 char *var, *val;
00462
00463 while ((val = strsep(¶ms, "&"))) {
00464 var = strsep(&val, "=");
00465 if (val) {
00466 http_decode(val);
00467 } else {
00468 val = "";
00469 }
00470 http_decode(var);
00471 if ((v = ast_variable_new(var, val, ""))) {
00472 if (vars) {
00473 prev->next = v;
00474 } else {
00475 vars = v;
00476 }
00477 prev = v;
00478 }
00479 }
00480 }
00481 }
00482
00483
00484
00485
00486
00487
00488
00489
00490 if (prev) {
00491 prev->next = *cookies;
00492 } else {
00493 vars = *cookies;
00494 }
00495 *cookies = NULL;
00496
00497 http_decode(uri);
00498
00499 AST_RWLIST_RDLOCK(&uri_redirects);
00500 AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
00501 if (!strcasecmp(uri, redirect->target)) {
00502 char buf[512];
00503
00504 snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest);
00505 out = ast_http_error((*status = 302),
00506 (*title = ast_strdup("Moved Temporarily")),
00507 buf, "Redirecting...");
00508
00509 break;
00510 }
00511 }
00512 AST_RWLIST_UNLOCK(&uri_redirects);
00513
00514 if (redirect) {
00515 goto cleanup;
00516 }
00517
00518
00519 l = strlen(prefix);
00520 if (!strncasecmp(uri, prefix, l) && uri[l] == '/') {
00521 uri += l + 1;
00522
00523 AST_RWLIST_RDLOCK(&uris);
00524 AST_RWLIST_TRAVERSE(&uris, urih, entry) {
00525 ast_debug(2, "match request [%s] with handler [%s] len %d\n", uri, urih->uri, l);
00526 if (!saw_method) {
00527 switch (method) {
00528 case AST_HTTP_GET:
00529 if (urih->supports_get) {
00530 saw_method = 1;
00531 }
00532 break;
00533 case AST_HTTP_POST:
00534 if (urih->supports_post) {
00535 saw_method = 1;
00536 }
00537 break;
00538 }
00539 }
00540
00541 l = strlen(urih->uri);
00542 c = uri + l;
00543
00544 if (strncasecmp(urih->uri, uri, l) ||
00545 (*c && *c != '/')) {
00546 continue;
00547 }
00548
00549 if (*c == '/') {
00550 c++;
00551 }
00552
00553 if (!*c || urih->has_subtree) {
00554 if (((method == AST_HTTP_GET) && urih->supports_get) ||
00555 ((method == AST_HTTP_POST) && urih->supports_post)) {
00556 uri = c;
00557
00558 break;
00559 }
00560 }
00561 }
00562
00563 if (!urih) {
00564 AST_RWLIST_UNLOCK(&uris);
00565 }
00566 }
00567
00568 if (method == AST_HTTP_POST && !astman_is_authed(manid_from_vars(vars))) {
00569 out = ast_http_error((*status = 403),
00570 (*title = ast_strdup("Access Denied")),
00571 NULL, "You do not have permission to access the requested URL.");
00572 } else if (urih) {
00573 *static_content = urih->static_content;
00574 out = urih->callback(ser, urih, uri, method, vars, headers, status, title, contentlength);
00575 AST_RWLIST_UNLOCK(&uris);
00576 } else if (saw_method) {
00577 out = ast_http_error((*status = 404),
00578 (*title = ast_strdup("Not Found")), NULL,
00579 "The requested URL was not found on this server.");
00580 } else {
00581 out = ast_http_error((*status = 501),
00582 (*title = ast_strdup("Not Implemented")), NULL,
00583 "Attempt to use unimplemented / unsupported method");
00584 }
00585
00586 cleanup:
00587 ast_variables_destroy(vars);
00588
00589 return out;
00590 }
00591
00592 #ifdef DO_SSL
00593 #if defined(HAVE_FUNOPEN)
00594 #define HOOK_T int
00595 #define LEN_T int
00596 #else
00597 #define HOOK_T ssize_t
00598 #define LEN_T size_t
00599 #endif
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635 #endif
00636
00637 static struct ast_variable *parse_cookies(char *cookies)
00638 {
00639 char *cur;
00640 struct ast_variable *vars = NULL, *var;
00641
00642
00643 cookies += 8;
00644
00645 while ((cur = strsep(&cookies, ";"))) {
00646 char *name, *val;
00647
00648 name = val = cur;
00649 strsep(&val, "=");
00650
00651 if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
00652 continue;
00653 }
00654
00655 name = ast_strip(name);
00656 val = ast_strip_quoted(val, "\"", "\"");
00657
00658 if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
00659 continue;
00660 }
00661
00662 ast_debug(1, "HTTP Cookie, Name: '%s' Value: '%s'\n", name, val);
00663
00664 var = ast_variable_new(name, val, __FILE__);
00665 var->next = vars;
00666 vars = var;
00667 }
00668
00669 return vars;
00670 }
00671
00672 static void *httpd_helper_thread(void *data)
00673 {
00674 char buf[4096];
00675 char cookie[4096];
00676 struct ast_tcptls_session_instance *ser = data;
00677 struct ast_variable *vars=NULL, *headers = NULL;
00678 char *uri, *title=NULL;
00679 int status = 200, contentlength = 0;
00680 struct ast_str *out = NULL;
00681 unsigned int static_content = 0;
00682 struct ast_variable *tail = headers;
00683
00684 if (ast_atomic_fetchadd_int(&session_count, +1) >= session_limit) {
00685 goto done;
00686 }
00687
00688 if (!fgets(buf, sizeof(buf), ser->f)) {
00689 goto done;
00690 }
00691
00692 uri = ast_skip_nonblanks(buf);
00693 if (*uri) {
00694 *uri++ = '\0';
00695 }
00696
00697 uri = ast_skip_blanks(uri);
00698
00699 if (*uri) {
00700 char *c = ast_skip_nonblanks(uri);
00701
00702 if (*c) {
00703 *c = '\0';
00704 }
00705 }
00706
00707
00708 while (fgets(cookie, sizeof(cookie), ser->f)) {
00709
00710 ast_trim_blanks(cookie);
00711 if (ast_strlen_zero(cookie)) {
00712 break;
00713 }
00714 if (!strncasecmp(cookie, "Cookie: ", 8)) {
00715 vars = parse_cookies(cookie);
00716 } else {
00717 char *name, *val;
00718
00719 val = cookie;
00720 name = strsep(&val, ":");
00721 if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
00722 continue;
00723 }
00724 ast_trim_blanks(name);
00725 val = ast_skip_blanks(val);
00726
00727 if (!headers) {
00728 headers = ast_variable_new(name, val, __FILE__);
00729 tail = headers;
00730 } else {
00731 tail->next = ast_variable_new(name, val, __FILE__);
00732 tail = tail->next;
00733 }
00734 }
00735 }
00736
00737 if (!*uri) {
00738 out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
00739 } else if (strcasecmp(buf, "post") && strcasecmp(buf, "get")) {
00740 out = ast_http_error(501, "Not Implemented", NULL,
00741 "Attempt to use unimplemented / unsupported method");
00742 } else {
00743 out = handle_uri(ser, uri, (!strcasecmp(buf, "get")) ? AST_HTTP_GET : AST_HTTP_POST,
00744 &status, &title, &contentlength, &vars, headers, &static_content);
00745 }
00746
00747
00748 if (vars) {
00749 ast_variables_destroy(vars);
00750 }
00751
00752 if (headers) {
00753 ast_variables_destroy(headers);
00754 }
00755
00756 if (out) {
00757 struct timeval now = ast_tvnow();
00758 char timebuf[256];
00759 struct ast_tm tm;
00760
00761 ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
00762 fprintf(ser->f,
00763 "HTTP/1.1 %d %s\r\n"
00764 "Server: Asterisk/%s\r\n"
00765 "Date: %s\r\n"
00766 "Connection: close\r\n"
00767 "%s",
00768 status, title ? title : "OK", ast_get_version(), timebuf,
00769 static_content ? "" : "Cache-Control: no-cache, no-store\r\n");
00770
00771
00772
00773
00774 if (!contentlength) {
00775 fprintf(ser->f, "%s", ast_str_buffer(out));
00776 } else {
00777 char *tmp = strstr(ast_str_buffer(out), "\r\n\r\n");
00778
00779 if (tmp) {
00780 fprintf(ser->f, "Content-length: %d\r\n", contentlength);
00781
00782 if (fwrite(ast_str_buffer(out), 1, (tmp + 4 - ast_str_buffer(out)), ser->f) != tmp + 4 - ast_str_buffer(out)) {
00783 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00784 }
00785 if (fwrite(tmp + 4, 1, contentlength, ser->f) != contentlength ) {
00786 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00787 }
00788 }
00789 }
00790 ast_free(out);
00791 }
00792
00793 if (title) {
00794 ast_free(title);
00795 }
00796
00797 done:
00798 ast_atomic_fetchadd_int(&session_count, -1);
00799 fclose(ser->f);
00800 ao2_ref(ser, -1);
00801 ser = NULL;
00802
00803 return NULL;
00804 }
00805
00806
00807
00808
00809
00810
00811 static void add_redirect(const char *value)
00812 {
00813 char *target, *dest;
00814 struct http_uri_redirect *redirect, *cur;
00815 unsigned int target_len;
00816 unsigned int total_len;
00817
00818 dest = ast_strdupa(value);
00819 dest = ast_skip_blanks(dest);
00820 target = strsep(&dest, " ");
00821 target = ast_skip_blanks(target);
00822 target = strsep(&target, " ");
00823
00824 if (!dest) {
00825 ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
00826 return;
00827 }
00828
00829 target_len = strlen(target) + 1;
00830 total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
00831
00832 if (!(redirect = ast_calloc(1, total_len))) {
00833 return;
00834 }
00835
00836 redirect->dest = redirect->target + target_len;
00837 strcpy(redirect->target, target);
00838 strcpy(redirect->dest, dest);
00839
00840 AST_RWLIST_WRLOCK(&uri_redirects);
00841
00842 target_len--;
00843 if (AST_RWLIST_EMPTY(&uri_redirects)
00844 || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len) {
00845 AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
00846 AST_RWLIST_UNLOCK(&uri_redirects);
00847
00848 return;
00849 }
00850
00851 AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
00852 if (AST_RWLIST_NEXT(cur, entry)
00853 && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len) {
00854 AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
00855 AST_RWLIST_UNLOCK(&uri_redirects);
00856
00857 return;
00858 }
00859 }
00860
00861 AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
00862
00863 AST_RWLIST_UNLOCK(&uri_redirects);
00864 }
00865
00866 static int __ast_http_load(int reload)
00867 {
00868 struct ast_config *cfg;
00869 struct ast_variable *v;
00870 int enabled=0;
00871 int newenablestatic=0;
00872 struct hostent *hp;
00873 struct ast_hostent ahp;
00874 char newprefix[MAX_PREFIX] = "";
00875 int have_sslbindaddr = 0;
00876 struct http_uri_redirect *redirect;
00877 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00878
00879 cfg = ast_config_load2("http.conf", "http", config_flags);
00880 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
00881 return 0;
00882 }
00883
00884
00885 memset(&http_desc.local_address, 0, sizeof(http_desc.local_address));
00886 http_desc.local_address.sin_port = htons(8088);
00887
00888 memset(&https_desc.local_address, 0, sizeof(https_desc.local_address));
00889 https_desc.local_address.sin_port = htons(8089);
00890
00891 http_tls_cfg.enabled = 0;
00892 if (http_tls_cfg.certfile) {
00893 ast_free(http_tls_cfg.certfile);
00894 }
00895 http_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
00896 if (http_tls_cfg.cipher) {
00897 ast_free(http_tls_cfg.cipher);
00898 }
00899 http_tls_cfg.cipher = ast_strdup("");
00900
00901 AST_RWLIST_WRLOCK(&uri_redirects);
00902 while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
00903 ast_free(redirect);
00904 }
00905 AST_RWLIST_UNLOCK(&uri_redirects);
00906
00907 if (cfg) {
00908 v = ast_variable_browse(cfg, "general");
00909 for (; v; v = v->next) {
00910 if (!strcasecmp(v->name, "enabled")) {
00911 enabled = ast_true(v->value);
00912 } else if (!strcasecmp(v->name, "sslenable")) {
00913 http_tls_cfg.enabled = ast_true(v->value);
00914 } else if (!strcasecmp(v->name, "sslbindport")) {
00915 https_desc.local_address.sin_port = htons(atoi(v->value));
00916 } else if (!strcasecmp(v->name, "sslcert")) {
00917 ast_free(http_tls_cfg.certfile);
00918 http_tls_cfg.certfile = ast_strdup(v->value);
00919 } else if (!strcasecmp(v->name, "sslcipher")) {
00920 ast_free(http_tls_cfg.cipher);
00921 http_tls_cfg.cipher = ast_strdup(v->value);
00922 } else if (!strcasecmp(v->name, "enablestatic")) {
00923 newenablestatic = ast_true(v->value);
00924 } else if (!strcasecmp(v->name, "bindport")) {
00925 http_desc.local_address.sin_port = htons(atoi(v->value));
00926 } else if (!strcasecmp(v->name, "sslbindaddr")) {
00927 if ((hp = ast_gethostbyname(v->value, &ahp))) {
00928 memcpy(&https_desc.local_address.sin_addr, hp->h_addr, sizeof(https_desc.local_address.sin_addr));
00929 have_sslbindaddr = 1;
00930 } else {
00931 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
00932 }
00933 } else if (!strcasecmp(v->name, "bindaddr")) {
00934 if ((hp = ast_gethostbyname(v->value, &ahp))) {
00935 memcpy(&http_desc.local_address.sin_addr, hp->h_addr, sizeof(http_desc.local_address.sin_addr));
00936 } else {
00937 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
00938 }
00939 } else if (!strcasecmp(v->name, "prefix")) {
00940 if (!ast_strlen_zero(v->value)) {
00941 newprefix[0] = '/';
00942 ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
00943 } else {
00944 newprefix[0] = '\0';
00945 }
00946 } else if (!strcasecmp(v->name, "redirect")) {
00947 add_redirect(v->value);
00948 } else if (!strcasecmp(v->name, "sessionlimit")) {
00949 if (ast_parse_arg(v->value, PARSE_INT32|PARSE_DEFAULT|PARSE_IN_RANGE,
00950 &session_limit, DEFAULT_SESSION_LIMIT, 1, INT_MAX)) {
00951 ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
00952 v->name, v->value, v->lineno);
00953 }
00954 } else {
00955 ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
00956 }
00957 }
00958
00959 ast_config_destroy(cfg);
00960 }
00961
00962 if (!have_sslbindaddr) {
00963 https_desc.local_address.sin_addr = http_desc.local_address.sin_addr;
00964 }
00965 if (enabled) {
00966 http_desc.local_address.sin_family = https_desc.local_address.sin_family = AF_INET;
00967 }
00968 if (strcmp(prefix, newprefix)) {
00969 ast_copy_string(prefix, newprefix, sizeof(prefix));
00970 }
00971 enablestatic = newenablestatic;
00972 ast_tcptls_server_start(&http_desc);
00973 if (ast_ssl_setup(https_desc.tls_cfg)) {
00974 ast_tcptls_server_start(&https_desc);
00975 }
00976
00977 return 0;
00978 }
00979
00980 static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00981 {
00982 struct ast_http_uri *urih;
00983 struct http_uri_redirect *redirect;
00984
00985 switch (cmd) {
00986 case CLI_INIT:
00987 e->command = "http show status";
00988 e->usage =
00989 "Usage: http show status\n"
00990 " Lists status of internal HTTP engine\n";
00991 return NULL;
00992 case CLI_GENERATE:
00993 return NULL;
00994 }
00995
00996 if (a->argc != 3) {
00997 return CLI_SHOWUSAGE;
00998 }
00999 ast_cli(a->fd, "HTTP Server Status:\n");
01000 ast_cli(a->fd, "Prefix: %s\n", prefix);
01001 if (!http_desc.old_address.sin_family) {
01002 ast_cli(a->fd, "Server Disabled\n\n");
01003 } else {
01004 ast_cli(a->fd, "Server Enabled and Bound to %s:%d\n\n",
01005 ast_inet_ntoa(http_desc.old_address.sin_addr),
01006 ntohs(http_desc.old_address.sin_port));
01007 if (http_tls_cfg.enabled) {
01008 ast_cli(a->fd, "HTTPS Server Enabled and Bound to %s:%d\n\n",
01009 ast_inet_ntoa(https_desc.old_address.sin_addr),
01010 ntohs(https_desc.old_address.sin_port));
01011 }
01012 }
01013
01014 ast_cli(a->fd, "Enabled URI's:\n");
01015 AST_RWLIST_RDLOCK(&uris);
01016 if (AST_RWLIST_EMPTY(&uris)) {
01017 ast_cli(a->fd, "None.\n");
01018 } else {
01019 AST_RWLIST_TRAVERSE(&uris, urih, entry) {
01020 ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : ""), urih->description);
01021 }
01022 }
01023 AST_RWLIST_UNLOCK(&uris);
01024
01025 ast_cli(a->fd, "\nEnabled Redirects:\n");
01026 AST_RWLIST_RDLOCK(&uri_redirects);
01027 AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
01028 ast_cli(a->fd, " %s => %s\n", redirect->target, redirect->dest);
01029 }
01030 if (AST_RWLIST_EMPTY(&uri_redirects)) {
01031 ast_cli(a->fd, " None.\n");
01032 }
01033 AST_RWLIST_UNLOCK(&uri_redirects);
01034
01035 return CLI_SUCCESS;
01036 }
01037
01038 int ast_http_reload(void)
01039 {
01040 return __ast_http_load(1);
01041 }
01042
01043 static struct ast_cli_entry cli_http[] = {
01044 AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"),
01045 };
01046
01047 int ast_http_init(void)
01048 {
01049 ast_http_uri_link(&statusuri);
01050 ast_http_uri_link(&staticuri);
01051 ast_cli_register_multiple(cli_http, ARRAY_LEN(cli_http));
01052
01053 return __ast_http_load(0);
01054 }