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