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