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