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
00032
00033
00034
00035
00036
00037 #include "asterisk.h"
00038
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416066 $")
00040
00041 #include <time.h>
00042 #include <sys/time.h>
00043 #include <sys/stat.h>
00044 #include <sys/signal.h>
00045 #include <fcntl.h>
00046
00047 #include "asterisk/paths.h"
00048 #include "asterisk/network.h"
00049 #include "asterisk/cli.h"
00050 #include "asterisk/tcptls.h"
00051 #include "asterisk/http.h"
00052 #include "asterisk/utils.h"
00053 #include "asterisk/strings.h"
00054 #include "asterisk/config.h"
00055 #include "asterisk/stringfields.h"
00056 #include "asterisk/ast_version.h"
00057 #include "asterisk/manager.h"
00058 #include "asterisk/_private.h"
00059 #include "asterisk/astobj2.h"
00060
00061 #define MAX_PREFIX 80
00062 #define DEFAULT_SESSION_LIMIT 100
00063 #define DEFAULT_SESSION_INACTIVITY 30000
00064
00065 #define DEFAULT_HTTP_PORT 8088
00066 #define DEFAULT_HTTPS_PORT 8089
00067
00068
00069 #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
00070 #define DO_SSL
00071 #endif
00072
00073 static int session_limit = DEFAULT_SESSION_LIMIT;
00074 static int session_inactivity = DEFAULT_SESSION_INACTIVITY;
00075 static int session_count = 0;
00076
00077 static struct ast_tls_config http_tls_cfg;
00078
00079 static void *httpd_helper_thread(void *arg);
00080
00081
00082
00083
00084 static struct ast_tcptls_session_args http_desc = {
00085 .accept_fd = -1,
00086 .master = AST_PTHREADT_NULL,
00087 .tls_cfg = NULL,
00088 .poll_timeout = -1,
00089 .name = "http server",
00090 .accept_fn = ast_tcptls_server_root,
00091 .worker_fn = httpd_helper_thread,
00092 };
00093
00094 static struct ast_tcptls_session_args https_desc = {
00095 .accept_fd = -1,
00096 .master = AST_PTHREADT_NULL,
00097 .tls_cfg = &http_tls_cfg,
00098 .poll_timeout = -1,
00099 .name = "https server",
00100 .accept_fn = ast_tcptls_server_root,
00101 .worker_fn = httpd_helper_thread,
00102 };
00103
00104 static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri);
00105
00106
00107 static char prefix[MAX_PREFIX];
00108 static int enablestatic;
00109
00110
00111 static struct {
00112 const char *ext;
00113 const char *mtype;
00114 } mimetypes[] = {
00115 { "png", "image/png" },
00116 { "xml", "text/xml" },
00117 { "jpg", "image/jpeg" },
00118 { "js", "application/x-javascript" },
00119 { "wav", "audio/x-wav" },
00120 { "mp3", "audio/mpeg" },
00121 { "svg", "image/svg+xml" },
00122 { "svgz", "image/svg+xml" },
00123 { "gif", "image/gif" },
00124 { "html", "text/html" },
00125 { "htm", "text/html" },
00126 { "css", "text/css" },
00127 { "cnf", "text/plain" },
00128 { "cfg", "text/plain" },
00129 { "bin", "application/octet-stream" },
00130 { "sbn", "application/octet-stream" },
00131 { "ld", "application/octet-stream" },
00132 };
00133
00134 struct http_uri_redirect {
00135 AST_LIST_ENTRY(http_uri_redirect) entry;
00136 char *dest;
00137 char target[0];
00138 };
00139
00140 static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
00141
00142 static const struct ast_cfhttp_methods_text {
00143 enum ast_http_method method;
00144 const char *text;
00145 } ast_http_methods_text[] = {
00146 { AST_HTTP_UNKNOWN, "UNKNOWN" },
00147 { AST_HTTP_GET, "GET" },
00148 { AST_HTTP_POST, "POST" },
00149 { AST_HTTP_HEAD, "HEAD" },
00150 { AST_HTTP_PUT, "PUT" },
00151 };
00152
00153 const char *ast_get_http_method(enum ast_http_method method)
00154 {
00155 int x;
00156
00157 for (x = 0; x < ARRAY_LEN(ast_http_methods_text); x++) {
00158 if (ast_http_methods_text[x].method == method) {
00159 return ast_http_methods_text[x].text;
00160 }
00161 }
00162
00163 return NULL;
00164 }
00165
00166 const char *ast_http_ftype2mtype(const char *ftype)
00167 {
00168 int x;
00169
00170 if (ftype) {
00171 for (x = 0; x < ARRAY_LEN(mimetypes); x++) {
00172 if (!strcasecmp(ftype, mimetypes[x].ext)) {
00173 return mimetypes[x].mtype;
00174 }
00175 }
00176 }
00177 return NULL;
00178 }
00179
00180 uint32_t ast_http_manid_from_vars(struct ast_variable *headers)
00181 {
00182 uint32_t mngid = 0;
00183 struct ast_variable *v, *cookies;
00184
00185 cookies = ast_http_get_cookies(headers);
00186 for (v = cookies; v; v = v->next) {
00187 if (!strcasecmp(v->name, "mansession_id")) {
00188 sscanf(v->value, "%30x", &mngid);
00189 break;
00190 }
00191 }
00192 ast_variables_destroy(cookies);
00193 return mngid;
00194 }
00195
00196 void ast_http_prefix(char *buf, int len)
00197 {
00198 if (buf) {
00199 ast_copy_string(buf, prefix, len);
00200 }
00201 }
00202
00203 static int static_callback(struct ast_tcptls_session_instance *ser,
00204 const struct ast_http_uri *urih, const char *uri,
00205 enum ast_http_method method, struct ast_variable *get_vars,
00206 struct ast_variable *headers)
00207 {
00208 char *path;
00209 const char *ftype;
00210 const char *mtype;
00211 char wkspace[80];
00212 struct stat st;
00213 int len;
00214 int fd;
00215 struct ast_str *http_header;
00216 struct timeval tv;
00217 struct ast_tm tm;
00218 char timebuf[80], etag[23];
00219 struct ast_variable *v;
00220 int not_modified = 0;
00221
00222 if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
00223 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
00224 return -1;
00225 }
00226
00227
00228
00229 if (!enablestatic || ast_strlen_zero(uri)) {
00230 goto out403;
00231 }
00232
00233
00234 if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0])) {
00235 goto out403;
00236 }
00237
00238 if (strstr(uri, "/..")) {
00239 goto out403;
00240 }
00241
00242 if ((ftype = strrchr(uri, '.'))) {
00243 ftype++;
00244 }
00245
00246 if (!(mtype = ast_http_ftype2mtype(ftype))) {
00247 snprintf(wkspace, sizeof(wkspace), "text/%s", S_OR(ftype, "plain"));
00248 mtype = wkspace;
00249 }
00250
00251
00252 if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
00253 goto out403;
00254 }
00255
00256 path = ast_alloca(len);
00257 sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
00258 if (stat(path, &st)) {
00259 goto out404;
00260 }
00261
00262 if (S_ISDIR(st.st_mode)) {
00263 goto out404;
00264 }
00265
00266 if (strstr(path, "/private/") && !astman_is_authed(ast_http_manid_from_vars(headers))) {
00267 goto out403;
00268 }
00269
00270 fd = open(path, O_RDONLY);
00271 if (fd < 0) {
00272 goto out403;
00273 }
00274
00275
00276 snprintf(etag, sizeof(etag), "\"%ld\"", (long)st.st_mtime);
00277
00278
00279 tv.tv_sec = st.st_mtime;
00280 tv.tv_usec = 0;
00281 ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&tv, &tm, "GMT"));
00282
00283
00284 for (v = headers; v; v = v->next) {
00285 if (!strcasecmp(v->name, "If-None-Match")) {
00286 if (!strcasecmp(v->value, etag)) {
00287 not_modified = 1;
00288 }
00289 break;
00290 }
00291 }
00292
00293 if ( (http_header = ast_str_create(255)) == NULL) {
00294 close(fd);
00295 return -1;
00296 }
00297
00298 ast_str_set(&http_header, 0, "Content-type: %s\r\n"
00299 "ETag: %s\r\n"
00300 "Last-Modified: %s\r\n",
00301 mtype,
00302 etag,
00303 timebuf);
00304
00305
00306 if (not_modified) {
00307 ast_http_send(ser, method, 304, "Not Modified", http_header, NULL, 0, 1);
00308 } else {
00309 ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 1);
00310 }
00311 close(fd);
00312 return 0;
00313
00314 out404:
00315 ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
00316 return -1;
00317
00318 out403:
00319 ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL.");
00320 return -1;
00321 }
00322
00323 static int httpstatus_callback(struct ast_tcptls_session_instance *ser,
00324 const struct ast_http_uri *urih, const char *uri,
00325 enum ast_http_method method, struct ast_variable *get_vars,
00326 struct ast_variable *headers)
00327 {
00328 struct ast_str *out;
00329 struct ast_variable *v, *cookies = NULL;
00330
00331 if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
00332 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
00333 return -1;
00334 }
00335
00336 if ( (out = ast_str_create(512)) == NULL) {
00337 return -1;
00338 }
00339
00340 ast_str_append(&out, 0,
00341 "<title>Asterisk HTTP Status</title>\r\n"
00342 "<body bgcolor=\"#ffffff\">\r\n"
00343 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
00344 "<h2> Asterisk™ HTTP Status</h2></td></tr>\r\n");
00345
00346 ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
00347 ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
00348 ast_sockaddr_stringify_addr(&http_desc.old_address));
00349 ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%s</b></td></tr>\r\n",
00350 ast_sockaddr_stringify_port(&http_desc.old_address));
00351 if (http_tls_cfg.enabled) {
00352 ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%s</b></td></tr>\r\n",
00353 ast_sockaddr_stringify_port(&https_desc.old_address));
00354 }
00355 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00356 for (v = get_vars; v; v = v->next) {
00357 ast_str_append(&out, 0, "<tr><td><i>Submitted GET Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
00358 }
00359 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00360
00361 cookies = ast_http_get_cookies(headers);
00362 for (v = cookies; v; v = v->next) {
00363 ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
00364 }
00365 ast_variables_destroy(cookies);
00366
00367 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");
00368 ast_http_send(ser, method, 200, NULL, NULL, out, 0, 0);
00369 return 0;
00370 }
00371
00372 static struct ast_http_uri statusuri = {
00373 .callback = httpstatus_callback,
00374 .description = "Asterisk HTTP General Status",
00375 .uri = "httpstatus",
00376 .has_subtree = 0,
00377 .data = NULL,
00378 .key = __FILE__,
00379 };
00380
00381 static struct ast_http_uri staticuri = {
00382 .callback = static_callback,
00383 .description = "Asterisk HTTP Static Delivery",
00384 .uri = "static",
00385 .has_subtree = 1,
00386 .data = NULL,
00387 .key= __FILE__,
00388 };
00389
00390
00391
00392
00393 void ast_http_send(struct ast_tcptls_session_instance *ser,
00394 enum ast_http_method method, int status_code, const char *status_title,
00395 struct ast_str *http_header, struct ast_str *out, const int fd,
00396 unsigned int static_content)
00397 {
00398 struct timeval now = ast_tvnow();
00399 struct ast_tm tm;
00400 char timebuf[80];
00401 int content_length = 0;
00402
00403 if (!ser || 0 == ser->f) {
00404 return;
00405 }
00406
00407 ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&now, &tm, "GMT"));
00408
00409
00410 if (out) {
00411 content_length += ast_str_strlen(out);
00412 }
00413
00414 if (fd) {
00415 content_length += lseek(fd, 0, SEEK_END);
00416 lseek(fd, 0, SEEK_SET);
00417 }
00418
00419
00420 fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
00421 "Server: Asterisk/%s\r\n"
00422 "Date: %s\r\n"
00423 "Connection: close\r\n"
00424 "%s"
00425 "Content-Length: %d\r\n"
00426 "%s"
00427 "\r\n",
00428 status_code, status_title ? status_title : "OK",
00429 ast_get_version(),
00430 timebuf,
00431 static_content ? "" : "Cache-Control: no-cache, no-store\r\n",
00432 content_length,
00433 http_header ? ast_str_buffer(http_header) : ""
00434 );
00435
00436
00437 if (method != AST_HTTP_HEAD || status_code >= 400) {
00438 if (out && ast_str_strlen(out)) {
00439 if (fwrite(ast_str_buffer(out), ast_str_strlen(out), 1, ser->f) != 1) {
00440 ast_log(LOG_ERROR, "fwrite() failed: %s\n", strerror(errno));
00441 }
00442 }
00443
00444 if (fd) {
00445 char buf[256];
00446 int len;
00447 while ((len = read(fd, buf, sizeof(buf))) > 0) {
00448 if (fwrite(buf, len, 1, ser->f) != 1) {
00449 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00450 break;
00451 }
00452 }
00453 }
00454 }
00455
00456 if (http_header) {
00457 ast_free(http_header);
00458 }
00459 if (out) {
00460 ast_free(out);
00461 }
00462
00463 ast_tcptls_close_session_file(ser);
00464 return;
00465 }
00466
00467
00468 void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm,
00469 const unsigned long nonce, const unsigned long opaque, int stale,
00470 const char *text)
00471 {
00472 struct ast_str *http_headers = ast_str_create(128);
00473 struct ast_str *out = ast_str_create(512);
00474
00475 if (!http_headers || !out) {
00476 ast_free(http_headers);
00477 ast_free(out);
00478 return;
00479 }
00480
00481 ast_str_set(&http_headers, 0,
00482 "WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n"
00483 "Content-type: text/html\r\n",
00484 realm ? realm : "Asterisk",
00485 nonce,
00486 opaque,
00487 stale ? ", stale=true" : "");
00488
00489 ast_str_set(&out, 0,
00490 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
00491 "<html><head>\r\n"
00492 "<title>401 Unauthorized</title>\r\n"
00493 "</head><body>\r\n"
00494 "<h1>401 Unauthorized</h1>\r\n"
00495 "<p>%s</p>\r\n"
00496 "<hr />\r\n"
00497 "<address>Asterisk Server</address>\r\n"
00498 "</body></html>\r\n",
00499 text ? text : "");
00500
00501 ast_http_send(ser, AST_HTTP_UNKNOWN, 401, "Unauthorized", http_headers, out, 0, 0);
00502 return;
00503 }
00504
00505
00506 void ast_http_error(struct ast_tcptls_session_instance *ser, int status_code, const char *status_title, const char *text)
00507 {
00508 struct ast_str *http_headers = ast_str_create(40);
00509 struct ast_str *out = ast_str_create(256);
00510
00511 if (!http_headers || !out) {
00512 ast_free(http_headers);
00513 ast_free(out);
00514 return;
00515 }
00516
00517 ast_str_set(&http_headers, 0, "Content-type: text/html\r\n");
00518
00519 ast_str_set(&out, 0,
00520 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
00521 "<html><head>\r\n"
00522 "<title>%d %s</title>\r\n"
00523 "</head><body>\r\n"
00524 "<h1>%s</h1>\r\n"
00525 "<p>%s</p>\r\n"
00526 "<hr />\r\n"
00527 "<address>Asterisk Server</address>\r\n"
00528 "</body></html>\r\n",
00529 status_code, status_title, status_title, text);
00530
00531 ast_http_send(ser, AST_HTTP_UNKNOWN, status_code, status_title, http_headers, out, 0, 0);
00532 return;
00533 }
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544 int ast_http_uri_link(struct ast_http_uri *urih)
00545 {
00546 struct ast_http_uri *uri;
00547 int len = strlen(urih->uri);
00548
00549 AST_RWLIST_WRLOCK(&uris);
00550
00551 if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) {
00552 AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
00553 AST_RWLIST_UNLOCK(&uris);
00554 return 0;
00555 }
00556
00557 AST_RWLIST_TRAVERSE(&uris, uri, entry) {
00558 if (AST_RWLIST_NEXT(uri, entry) &&
00559 strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len) {
00560 AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
00561 AST_RWLIST_UNLOCK(&uris);
00562
00563 return 0;
00564 }
00565 }
00566
00567 AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
00568
00569 AST_RWLIST_UNLOCK(&uris);
00570
00571 return 0;
00572 }
00573
00574 void ast_http_uri_unlink(struct ast_http_uri *urih)
00575 {
00576 AST_RWLIST_WRLOCK(&uris);
00577 AST_RWLIST_REMOVE(&uris, urih, entry);
00578 AST_RWLIST_UNLOCK(&uris);
00579 }
00580
00581 void ast_http_uri_unlink_all_with_key(const char *key)
00582 {
00583 struct ast_http_uri *urih;
00584 AST_RWLIST_WRLOCK(&uris);
00585 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&uris, urih, entry) {
00586 if (!strcmp(urih->key, key)) {
00587 AST_RWLIST_REMOVE_CURRENT(entry);
00588 if (urih->dmallocd) {
00589 ast_free(urih->data);
00590 }
00591 if (urih->mallocd) {
00592 ast_free(urih);
00593 }
00594 }
00595 }
00596 AST_RWLIST_TRAVERSE_SAFE_END;
00597 AST_RWLIST_UNLOCK(&uris);
00598 }
00599
00600
00601
00602
00603
00604
00605 static void http_decode(char *s)
00606 {
00607 char *t;
00608
00609 for (t = s; *t; t++) {
00610 if (*t == '+') {
00611 *t = ' ';
00612 }
00613 }
00614
00615 ast_uri_decode(s);
00616 }
00617
00618 #define MAX_POST_CONTENT 1025
00619
00620
00621
00622
00623
00624 struct ast_variable *ast_http_get_post_vars(
00625 struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
00626 {
00627 int content_length = 0;
00628 struct ast_variable *v, *post_vars=NULL, *prev = NULL;
00629 char *buf, *var, *val;
00630 int res;
00631
00632 for (v = headers; v; v = v->next) {
00633 if (!strcasecmp(v->name, "Content-Type")) {
00634 if (strcasecmp(v->value, "application/x-www-form-urlencoded")) {
00635 return NULL;
00636 }
00637 break;
00638 }
00639 }
00640
00641 for (v = headers; v; v = v->next) {
00642 if (!strcasecmp(v->name, "Content-Length")) {
00643 content_length = atoi(v->value);
00644 break;
00645 }
00646 }
00647
00648 if (content_length <= 0) {
00649 return NULL;
00650 }
00651
00652 if (content_length > MAX_POST_CONTENT - 1) {
00653 ast_log(LOG_WARNING, "Excessively long HTTP content. %d is greater than our max of %d\n",
00654 content_length, MAX_POST_CONTENT);
00655 ast_http_send(ser, AST_HTTP_POST, 413, "Request Entity Too Large", NULL, NULL, 0, 0);
00656 return NULL;
00657 }
00658
00659 buf = ast_malloc(content_length + 1);
00660 if (!buf) {
00661 return NULL;
00662 }
00663
00664 res = fread(buf, 1, content_length, ser->f);
00665 if (res < content_length) {
00666
00667
00668 goto done;
00669 }
00670 buf[content_length] = '\0';
00671
00672 while ((val = strsep(&buf, "&"))) {
00673 var = strsep(&val, "=");
00674 if (val) {
00675 http_decode(val);
00676 } else {
00677 val = "";
00678 }
00679 http_decode(var);
00680 if ((v = ast_variable_new(var, val, ""))) {
00681 if (post_vars) {
00682 prev->next = v;
00683 } else {
00684 post_vars = v;
00685 }
00686 prev = v;
00687 }
00688 }
00689
00690 done:
00691 ast_free(buf);
00692 return post_vars;
00693 }
00694
00695 static int handle_uri(struct ast_tcptls_session_instance *ser, char *uri,
00696 enum ast_http_method method, struct ast_variable *headers)
00697 {
00698 char *c;
00699 int res = -1;
00700 char *params = uri;
00701 struct ast_http_uri *urih = NULL;
00702 int l;
00703 struct ast_variable *get_vars = NULL, *v, *prev = NULL;
00704 struct http_uri_redirect *redirect;
00705
00706 ast_debug(2, "HTTP Request URI is %s \n", uri);
00707
00708 strsep(¶ms, "?");
00709
00710 if (params) {
00711 char *var, *val;
00712
00713 while ((val = strsep(¶ms, "&"))) {
00714 var = strsep(&val, "=");
00715 if (val) {
00716 http_decode(val);
00717 } else {
00718 val = "";
00719 }
00720 http_decode(var);
00721 if ((v = ast_variable_new(var, val, ""))) {
00722 if (get_vars) {
00723 prev->next = v;
00724 } else {
00725 get_vars = v;
00726 }
00727 prev = v;
00728 }
00729 }
00730 }
00731 http_decode(uri);
00732
00733 AST_RWLIST_RDLOCK(&uri_redirects);
00734 AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
00735 if (!strcasecmp(uri, redirect->target)) {
00736 struct ast_str *http_header = ast_str_create(128);
00737 ast_str_set(&http_header, 0, "Location: %s\r\n", redirect->dest);
00738 ast_http_send(ser, method, 302, "Moved Temporarily", http_header, NULL, 0, 0);
00739
00740 break;
00741 }
00742 }
00743 AST_RWLIST_UNLOCK(&uri_redirects);
00744 if (redirect) {
00745 goto cleanup;
00746 }
00747
00748
00749 l = strlen(prefix);
00750 if (!strncasecmp(uri, prefix, l) && uri[l] == '/') {
00751 uri += l + 1;
00752
00753 AST_RWLIST_RDLOCK(&uris);
00754 AST_RWLIST_TRAVERSE(&uris, urih, entry) {
00755 ast_debug(2, "match request [%s] with handler [%s] len %d\n", uri, urih->uri, l);
00756 l = strlen(urih->uri);
00757 c = uri + l;
00758 if (strncasecmp(urih->uri, uri, l)
00759 || (*c && *c != '/')) {
00760 continue;
00761 }
00762 if (*c == '/') {
00763 c++;
00764 }
00765 if (!*c || urih->has_subtree) {
00766 uri = c;
00767 break;
00768 }
00769 }
00770 AST_RWLIST_UNLOCK(&uris);
00771 }
00772 if (urih) {
00773 res = urih->callback(ser, urih, uri, method, get_vars, headers);
00774 } else {
00775 ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
00776 }
00777
00778 cleanup:
00779 ast_variables_destroy(get_vars);
00780 return res;
00781 }
00782
00783 #ifdef DO_SSL
00784 #if defined(HAVE_FUNOPEN)
00785 #define HOOK_T int
00786 #define LEN_T int
00787 #else
00788 #define HOOK_T ssize_t
00789 #define LEN_T size_t
00790 #endif
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826 #endif
00827
00828 static struct ast_variable *parse_cookies(const char *cookies)
00829 {
00830 char *parse = ast_strdupa(cookies);
00831 char *cur;
00832 struct ast_variable *vars = NULL, *var;
00833
00834 while ((cur = strsep(&parse, ";"))) {
00835 char *name, *val;
00836
00837 name = val = cur;
00838 strsep(&val, "=");
00839
00840 if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
00841 continue;
00842 }
00843
00844 name = ast_strip(name);
00845 val = ast_strip_quoted(val, "\"", "\"");
00846
00847 if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
00848 continue;
00849 }
00850
00851 ast_debug(1, "HTTP Cookie, Name: '%s' Value: '%s'\n", name, val);
00852
00853 var = ast_variable_new(name, val, __FILE__);
00854 var->next = vars;
00855 vars = var;
00856 }
00857
00858 return vars;
00859 }
00860
00861
00862 struct ast_variable *ast_http_get_cookies(struct ast_variable *headers)
00863 {
00864 struct ast_variable *v, *cookies = NULL;
00865
00866 for (v = headers; v; v = v->next) {
00867 if (!strcasecmp(v->name, "Cookie")) {
00868 ast_variables_destroy(cookies);
00869 cookies = parse_cookies(v->value);
00870 }
00871 }
00872 return cookies;
00873 }
00874
00875
00876 #define MAX_HTTP_REQUEST_HEADERS 100
00877
00878 static void *httpd_helper_thread(void *data)
00879 {
00880 char buf[4096];
00881 char header_line[4096];
00882 struct ast_tcptls_session_instance *ser = data;
00883 struct ast_variable *headers = NULL;
00884 struct ast_variable *tail = headers;
00885 char *uri, *method;
00886 enum ast_http_method http_method = AST_HTTP_UNKNOWN;
00887 int remaining_headers;
00888 int flags;
00889 struct protoent *p;
00890
00891 if (ast_atomic_fetchadd_int(&session_count, +1) >= session_limit) {
00892 goto done;
00893 }
00894
00895
00896
00897
00898 p = getprotobyname("tcp");
00899 if (p) {
00900 int arg = 1;
00901 if( setsockopt(ser->fd, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
00902 ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on HTTP connection: %s\n", strerror(errno));
00903 ast_log(LOG_WARNING, "Some HTTP requests may be slow to respond.\n");
00904 }
00905 } else {
00906 ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on HTTP connection, getprotobyname(\"tcp\") failed\n");
00907 ast_log(LOG_WARNING, "Some HTTP requests may be slow to respond.\n");
00908 }
00909
00910
00911 flags = fcntl(ser->fd, F_GETFL);
00912 flags |= O_NONBLOCK;
00913 fcntl(ser->fd, F_SETFL, flags);
00914
00915
00916 ast_tcptls_stream_set_exclusive_input(ser->stream_cookie, 1);
00917
00918 ast_tcptls_stream_set_timeout_inactivity(ser->stream_cookie, session_inactivity);
00919
00920 if (!fgets(buf, sizeof(buf), ser->f) || feof(ser->f)) {
00921 goto done;
00922 }
00923
00924
00925 method = ast_skip_blanks(buf);
00926 uri = ast_skip_nonblanks(method);
00927 if (*uri) {
00928 *uri++ = '\0';
00929 }
00930
00931 if (!strcasecmp(method,"GET")) {
00932 http_method = AST_HTTP_GET;
00933 } else if (!strcasecmp(method,"POST")) {
00934 http_method = AST_HTTP_POST;
00935 } else if (!strcasecmp(method,"HEAD")) {
00936 http_method = AST_HTTP_HEAD;
00937 } else if (!strcasecmp(method,"PUT")) {
00938 http_method = AST_HTTP_PUT;
00939 }
00940
00941 uri = ast_skip_blanks(uri);
00942
00943 if (*uri) {
00944 char *c = ast_skip_nonblanks(uri);
00945
00946 if (*c) {
00947 *c = '\0';
00948 }
00949 } else {
00950 ast_http_error(ser, 400, "Bad Request", "Invalid Request");
00951 goto done;
00952 }
00953
00954
00955 remaining_headers = MAX_HTTP_REQUEST_HEADERS;
00956 for (;;) {
00957 char *name;
00958 char *value;
00959
00960 if (!fgets(header_line, sizeof(header_line), ser->f) || feof(ser->f)) {
00961 ast_http_error(ser, 400, "Bad Request", "Timeout");
00962 goto done;
00963 }
00964
00965
00966 ast_trim_blanks(header_line);
00967 if (ast_strlen_zero(header_line)) {
00968
00969 break;
00970 }
00971
00972 value = header_line;
00973 name = strsep(&value, ":");
00974 if (!value) {
00975 continue;
00976 }
00977
00978 value = ast_skip_blanks(value);
00979 if (ast_strlen_zero(value) || ast_strlen_zero(name)) {
00980 continue;
00981 }
00982
00983 ast_trim_blanks(name);
00984
00985 if (!remaining_headers--) {
00986
00987 ast_http_error(ser, 413, "Request Entity Too Large", "Too many headers");
00988 goto done;
00989 }
00990 if (!headers) {
00991 headers = ast_variable_new(name, value, __FILE__);
00992 tail = headers;
00993 } else {
00994 tail->next = ast_variable_new(name, value, __FILE__);
00995 tail = tail->next;
00996 }
00997 if (!tail) {
00998
00999
01000
01001
01002 ast_variables_destroy(headers);
01003 headers = NULL;
01004
01005 ast_http_error(ser, 500, "Server Error", "Out of memory");
01006 goto done;
01007 }
01008 }
01009
01010 handle_uri(ser, uri, http_method, headers);
01011
01012 done:
01013 ast_atomic_fetchadd_int(&session_count, -1);
01014
01015
01016 ast_variables_destroy(headers);
01017
01018 if (ser->f) {
01019 ast_tcptls_close_session_file(ser);
01020 }
01021 ao2_ref(ser, -1);
01022 ser = NULL;
01023 return NULL;
01024 }
01025
01026
01027
01028
01029
01030
01031 static void add_redirect(const char *value)
01032 {
01033 char *target, *dest;
01034 struct http_uri_redirect *redirect, *cur;
01035 unsigned int target_len;
01036 unsigned int total_len;
01037
01038 dest = ast_strdupa(value);
01039 dest = ast_skip_blanks(dest);
01040 target = strsep(&dest, " ");
01041 target = ast_skip_blanks(target);
01042 target = strsep(&target, " ");
01043
01044 if (!dest) {
01045 ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
01046 return;
01047 }
01048
01049 target_len = strlen(target) + 1;
01050 total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
01051
01052 if (!(redirect = ast_calloc(1, total_len))) {
01053 return;
01054 }
01055 redirect->dest = redirect->target + target_len;
01056 strcpy(redirect->target, target);
01057 strcpy(redirect->dest, dest);
01058
01059 AST_RWLIST_WRLOCK(&uri_redirects);
01060
01061 target_len--;
01062 if (AST_RWLIST_EMPTY(&uri_redirects)
01063 || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
01064 AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
01065 AST_RWLIST_UNLOCK(&uri_redirects);
01066
01067 return;
01068 }
01069
01070 AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
01071 if (AST_RWLIST_NEXT(cur, entry)
01072 && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
01073 AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
01074 AST_RWLIST_UNLOCK(&uri_redirects);
01075 return;
01076 }
01077 }
01078
01079 AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
01080
01081 AST_RWLIST_UNLOCK(&uri_redirects);
01082 }
01083
01084 static int __ast_http_load(int reload)
01085 {
01086 struct ast_config *cfg;
01087 struct ast_variable *v;
01088 int enabled=0;
01089 int newenablestatic=0;
01090 struct hostent *hp;
01091 struct ast_hostent ahp;
01092 char newprefix[MAX_PREFIX] = "";
01093 struct http_uri_redirect *redirect;
01094 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01095 struct sockaddr_in tmp = {0,};
01096 struct sockaddr_in tmp2 = {0,};
01097 int http_tls_was_enabled = 0;
01098
01099 cfg = ast_config_load2("http.conf", "http", config_flags);
01100 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
01101 return 0;
01102 }
01103
01104 http_tls_was_enabled = (reload && http_tls_cfg.enabled);
01105
01106 tmp.sin_family = AF_INET;
01107 tmp.sin_port = htons(DEFAULT_HTTP_PORT);
01108 ast_sockaddr_from_sin(&http_desc.local_address, &tmp);
01109
01110 http_tls_cfg.enabled = 0;
01111 if (http_tls_cfg.certfile) {
01112 ast_free(http_tls_cfg.certfile);
01113 }
01114 http_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
01115
01116 if (http_tls_cfg.pvtfile) {
01117 ast_free(http_tls_cfg.pvtfile);
01118 }
01119 http_tls_cfg.pvtfile = ast_strdup("");
01120
01121 if (http_tls_cfg.cipher) {
01122 ast_free(http_tls_cfg.cipher);
01123 }
01124 http_tls_cfg.cipher = ast_strdup("");
01125
01126 AST_RWLIST_WRLOCK(&uri_redirects);
01127 while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
01128 ast_free(redirect);
01129 }
01130 AST_RWLIST_UNLOCK(&uri_redirects);
01131
01132 ast_sockaddr_setnull(&https_desc.local_address);
01133
01134 session_limit = DEFAULT_SESSION_LIMIT;
01135 session_inactivity = DEFAULT_SESSION_INACTIVITY;
01136
01137 if (cfg) {
01138 v = ast_variable_browse(cfg, "general");
01139 for (; v; v = v->next) {
01140
01141
01142 if (strcasecmp(v->name, "tlscafile")
01143 && strcasecmp(v->name, "tlscapath")
01144 && strcasecmp(v->name, "tlscadir")
01145 && strcasecmp(v->name, "tlsverifyclient")
01146 && strcasecmp(v->name, "tlsdontverifyserver")
01147 && strcasecmp(v->name, "tlsclientmethod")
01148 && strcasecmp(v->name, "sslclientmethod")
01149 && strcasecmp(v->name, "tlscipher")
01150 && strcasecmp(v->name, "sslcipher")
01151 && !ast_tls_read_conf(&http_tls_cfg, &https_desc, v->name, v->value)) {
01152 continue;
01153 }
01154
01155 if (!strcasecmp(v->name, "enabled")) {
01156 enabled = ast_true(v->value);
01157 } else if (!strcasecmp(v->name, "enablestatic")) {
01158 newenablestatic = ast_true(v->value);
01159 } else if (!strcasecmp(v->name, "bindport")) {
01160 ast_sockaddr_set_port(&http_desc.local_address,
01161 atoi(v->value));
01162 } else if (!strcasecmp(v->name, "bindaddr")) {
01163 if ((hp = ast_gethostbyname(v->value, &ahp))) {
01164 ast_sockaddr_to_sin(&http_desc.local_address,
01165 &tmp);
01166 memcpy(&tmp.sin_addr, hp->h_addr, sizeof(tmp.sin_addr));
01167 ast_sockaddr_from_sin(&http_desc.local_address,
01168 &tmp);
01169 } else {
01170 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
01171 }
01172 } else if (!strcasecmp(v->name, "prefix")) {
01173 if (!ast_strlen_zero(v->value)) {
01174 newprefix[0] = '/';
01175 ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
01176 } else {
01177 newprefix[0] = '\0';
01178 }
01179 } else if (!strcasecmp(v->name, "redirect")) {
01180 add_redirect(v->value);
01181 } else if (!strcasecmp(v->name, "sessionlimit")) {
01182 if (ast_parse_arg(v->value, PARSE_INT32|PARSE_DEFAULT|PARSE_IN_RANGE,
01183 &session_limit, DEFAULT_SESSION_LIMIT, 1, INT_MAX)) {
01184 ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
01185 v->name, v->value, v->lineno);
01186 }
01187 } else if (!strcasecmp(v->name, "session_inactivity")) {
01188 if (ast_parse_arg(v->value, PARSE_INT32 |PARSE_DEFAULT | PARSE_IN_RANGE,
01189 &session_inactivity, DEFAULT_SESSION_INACTIVITY, 1, INT_MAX)) {
01190 ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
01191 v->name, v->value, v->lineno);
01192 }
01193 } else {
01194 ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
01195 }
01196 }
01197
01198 ast_config_destroy(cfg);
01199 }
01200
01201 ast_sockaddr_to_sin(&http_desc.local_address, &tmp);
01202 ast_sockaddr_to_sin(&https_desc.local_address, &tmp2);
01203 if (!tmp2.sin_addr.s_addr) {
01204 tmp2.sin_addr = tmp.sin_addr;
01205 }
01206 if (!tmp2.sin_port) {
01207 tmp2.sin_port = htons(DEFAULT_HTTPS_PORT);
01208 }
01209 ast_sockaddr_from_sin(&https_desc.local_address, &tmp2);
01210 if (!enabled) {
01211 ast_sockaddr_setnull(&http_desc.local_address);
01212 ast_sockaddr_setnull(&https_desc.local_address);
01213 }
01214 if (strcmp(prefix, newprefix)) {
01215 ast_copy_string(prefix, newprefix, sizeof(prefix));
01216 }
01217 enablestatic = newenablestatic;
01218 ast_tcptls_server_start(&http_desc);
01219
01220 if (http_tls_was_enabled && !http_tls_cfg.enabled) {
01221 ast_tcptls_server_stop(&https_desc);
01222 } else if (ast_ssl_setup(https_desc.tls_cfg)) {
01223 ast_tcptls_server_start(&https_desc);
01224 }
01225
01226 return 0;
01227 }
01228
01229 static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01230 {
01231 struct ast_http_uri *urih;
01232 struct http_uri_redirect *redirect;
01233 struct sockaddr_in tmp;
01234
01235 switch (cmd) {
01236 case CLI_INIT:
01237 e->command = "http show status";
01238 e->usage =
01239 "Usage: http show status\n"
01240 " Lists status of internal HTTP engine\n";
01241 return NULL;
01242 case CLI_GENERATE:
01243 return NULL;
01244 }
01245
01246 if (a->argc != 3) {
01247 return CLI_SHOWUSAGE;
01248 }
01249 ast_cli(a->fd, "HTTP Server Status:\n");
01250 ast_cli(a->fd, "Prefix: %s\n", prefix);
01251 ast_sockaddr_to_sin(&http_desc.old_address, &tmp);
01252 if (!tmp.sin_family) {
01253 ast_cli(a->fd, "Server Disabled\n\n");
01254 } else {
01255 ast_cli(a->fd, "Server Enabled and Bound to %s:%d\n\n",
01256 ast_inet_ntoa(tmp.sin_addr), ntohs(tmp.sin_port));
01257 if (http_tls_cfg.enabled) {
01258 ast_sockaddr_to_sin(&https_desc.old_address, &tmp);
01259 ast_cli(a->fd, "HTTPS Server Enabled and Bound to %s:%d\n\n",
01260 ast_inet_ntoa(tmp.sin_addr),
01261 ntohs(tmp.sin_port));
01262 }
01263 }
01264
01265 ast_cli(a->fd, "Enabled URI's:\n");
01266 AST_RWLIST_RDLOCK(&uris);
01267 if (AST_RWLIST_EMPTY(&uris)) {
01268 ast_cli(a->fd, "None.\n");
01269 } else {
01270 AST_RWLIST_TRAVERSE(&uris, urih, entry)
01271 ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
01272 }
01273 AST_RWLIST_UNLOCK(&uris);
01274
01275 ast_cli(a->fd, "\nEnabled Redirects:\n");
01276 AST_RWLIST_RDLOCK(&uri_redirects);
01277 AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
01278 ast_cli(a->fd, " %s => %s\n", redirect->target, redirect->dest);
01279 if (AST_RWLIST_EMPTY(&uri_redirects)) {
01280 ast_cli(a->fd, " None.\n");
01281 }
01282 AST_RWLIST_UNLOCK(&uri_redirects);
01283
01284 return CLI_SUCCESS;
01285 }
01286
01287 int ast_http_reload(void)
01288 {
01289 return __ast_http_load(1);
01290 }
01291
01292 static struct ast_cli_entry cli_http[] = {
01293 AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"),
01294 };
01295
01296 static void http_shutdown(void)
01297 {
01298 struct http_uri_redirect *redirect;
01299 ast_cli_unregister_multiple(cli_http, ARRAY_LEN(cli_http));
01300
01301 ast_tcptls_server_stop(&http_desc);
01302 if (http_tls_cfg.enabled) {
01303 ast_tcptls_server_stop(&https_desc);
01304 }
01305 ast_free(http_tls_cfg.certfile);
01306 ast_free(http_tls_cfg.pvtfile);
01307 ast_free(http_tls_cfg.cipher);
01308
01309 ast_http_uri_unlink(&statusuri);
01310 ast_http_uri_unlink(&staticuri);
01311
01312 AST_RWLIST_WRLOCK(&uri_redirects);
01313 while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
01314 ast_free(redirect);
01315 }
01316 AST_RWLIST_UNLOCK(&uri_redirects);
01317 }
01318
01319 int ast_http_init(void)
01320 {
01321 ast_http_uri_link(&statusuri);
01322 ast_http_uri_link(&staticuri);
01323 ast_cli_register_multiple(cli_http, ARRAY_LEN(cli_http));
01324 ast_register_atexit(http_shutdown);
01325
01326 return __ast_http_load(0);
01327 }