![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
http.c
Go to the documentation of this file.
00001 /* 00002 * Asterisk -- An open source telephony toolkit. 00003 * 00004 * Copyright (C) 1999 - 2006, Digium, Inc. 00005 * 00006 * Mark Spencer <markster@digium.com> 00007 * 00008 * See http://www.asterisk.org for more information about 00009 * the Asterisk project. Please do not directly contact 00010 * any of the maintainers of this project for assistance; 00011 * the project provides a web site, mailing lists and IRC 00012 * channels for your use. 00013 * 00014 * This program is free software, distributed under the terms of 00015 * the GNU General Public License Version 2. See the LICENSE file 00016 * at the top of the source tree. 00017 */ 00018 00019 /*! 00020 * \file 00021 * \brief http server for AMI access 00022 * 00023 * \author Mark Spencer <markster@digium.com> 00024 * This program implements a tiny http server supporting the "get" method 00025 * only and was inspired by micro-httpd by Jef Poskanzer 00026 * 00027 * \ref AstHTTP - AMI over the http protocol 00028 */ 00029 00030 #include "asterisk.h" 00031 00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 53128 $") 00033 00034 #include <sys/types.h> 00035 #include <stdio.h> 00036 #include <unistd.h> 00037 #include <stdlib.h> 00038 #include <time.h> 00039 #include <string.h> 00040 #include <netinet/in.h> 00041 #include <sys/time.h> 00042 #include <sys/socket.h> 00043 #include <sys/stat.h> 00044 #include <sys/signal.h> 00045 #include <arpa/inet.h> 00046 #include <errno.h> 00047 #include <fcntl.h> 00048 #include <pthread.h> 00049 00050 #include "asterisk/cli.h" 00051 #include "asterisk/http.h" 00052 #include "asterisk/utils.h" 00053 #include "asterisk/strings.h" 00054 #include "asterisk/options.h" 00055 #include "asterisk/config.h" 00056 #include "asterisk/stringfields.h" 00057 00058 #define MAX_PREFIX 80 00059 #define DEFAULT_PREFIX "/asterisk" 00060 00061 /* See http.h for more information about the SSL implementation */ 00062 #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE)) 00063 #define DO_SSL /* comment in/out if you want to support ssl */ 00064 #endif 00065 00066 static struct tls_config http_tls_cfg; 00067 00068 static void *httpd_helper_thread(void *arg); 00069 00070 /*! 00071 * we have up to two accepting threads, one for http, one for https 00072 */ 00073 static struct server_args http_desc = { 00074 .accept_fd = -1, 00075 .master = AST_PTHREADT_NULL, 00076 .tls_cfg = NULL, 00077 .poll_timeout = -1, 00078 .name = "http server", 00079 .accept_fn = server_root, 00080 .worker_fn = httpd_helper_thread, 00081 }; 00082 00083 static struct server_args https_desc = { 00084 .accept_fd = -1, 00085 .master = AST_PTHREADT_NULL, 00086 .tls_cfg = &http_tls_cfg, 00087 .poll_timeout = -1, 00088 .name = "https server", 00089 .accept_fn = server_root, 00090 .worker_fn = httpd_helper_thread, 00091 }; 00092 00093 static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri); /*!< list of supported handlers */ 00094 00095 /* all valid URIs must be prepended by the string in prefix. */ 00096 static char prefix[MAX_PREFIX]; 00097 static int enablestatic; 00098 00099 /*! \brief Limit the kinds of files we're willing to serve up */ 00100 static struct { 00101 char *ext; 00102 char *mtype; 00103 } mimetypes[] = { 00104 { "png", "image/png" }, 00105 { "jpg", "image/jpeg" }, 00106 { "js", "application/x-javascript" }, 00107 { "wav", "audio/x-wav" }, 00108 { "mp3", "audio/mpeg" }, 00109 }; 00110 00111 struct http_uri_redirect { 00112 AST_LIST_ENTRY(http_uri_redirect) entry; 00113 char *dest; 00114 char target[0]; 00115 }; 00116 00117 static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect); 00118 00119 static char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen) 00120 { 00121 int x; 00122 if (ftype) { 00123 for (x=0;x<sizeof(mimetypes) / sizeof(mimetypes[0]); x++) { 00124 if (!strcasecmp(ftype, mimetypes[x].ext)) 00125 return mimetypes[x].mtype; 00126 } 00127 } 00128 snprintf(wkspace, wkspacelen, "text/%s", ftype ? ftype : "plain"); 00129 return wkspace; 00130 } 00131 00132 /* like ast_uri_decode, but replace '+' with ' ' */ 00133 static char *uri_decode(char *buf) 00134 { 00135 char *c; 00136 ast_uri_decode(buf); 00137 for (c = buf; *c; c++) { 00138 if (*c == '+') 00139 *c = ' '; 00140 } 00141 return buf; 00142 } 00143 00144 static struct ast_str *static_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength) 00145 { 00146 struct ast_str *result; 00147 char *path; 00148 char *ftype, *mtype; 00149 char wkspace[80]; 00150 struct stat st; 00151 int len; 00152 int fd; 00153 00154 /* Yuck. I'm not really sold on this, but if you don't deliver static content it makes your configuration 00155 substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */ 00156 if (!enablestatic || ast_strlen_zero(uri)) 00157 goto out403; 00158 /* Disallow any funny filenames at all */ 00159 if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0])) 00160 goto out403; 00161 if (strstr(uri, "/..")) 00162 goto out403; 00163 00164 if ((ftype = strrchr(uri, '.'))) 00165 ftype++; 00166 mtype=ftype2mtype(ftype, wkspace, sizeof(wkspace)); 00167 00168 /* Cap maximum length */ 00169 len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5; 00170 if (len > 1024) 00171 goto out403; 00172 00173 path = alloca(len); 00174 sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri); 00175 if (stat(path, &st)) 00176 goto out404; 00177 if (S_ISDIR(st.st_mode)) 00178 goto out404; 00179 fd = open(path, O_RDONLY); 00180 if (fd < 0) 00181 goto out403; 00182 00183 len = st.st_size + strlen(mtype) + 40; 00184 result = ast_str_create(len); 00185 if (result == NULL) /* XXX not really but... */ 00186 goto out403; 00187 00188 ast_str_append(&result, 0, "Content-type: %s\r\n\r\n", mtype); 00189 *contentlength = read(fd, result->str + result->used, st.st_size); 00190 if (*contentlength < 0) { 00191 close(fd); 00192 free(result); 00193 goto out403; 00194 } 00195 result->used += *contentlength; 00196 close(fd); 00197 return result; 00198 00199 out404: 00200 *status = 404; 00201 *title = strdup("Not Found"); 00202 return ast_http_error(404, "Not Found", NULL, "Nothing to see here. Move along."); 00203 00204 out403: 00205 *status = 403; 00206 *title = strdup("Access Denied"); 00207 return ast_http_error(403, "Access Denied", NULL, "Sorry, I cannot let you do that, Dave."); 00208 } 00209 00210 00211 static struct ast_str *httpstatus_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength) 00212 { 00213 struct ast_str *out = ast_str_create(512); 00214 struct ast_variable *v; 00215 00216 if (out == NULL) 00217 return out; 00218 00219 ast_str_append(&out, 0, 00220 "\r\n" 00221 "<title>Asterisk HTTP Status</title>\r\n" 00222 "<body bgcolor=\"#ffffff\">\r\n" 00223 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n" 00224 "<h2> Asterisk™ HTTP Status</h2></td></tr>\r\n"); 00225 00226 ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix); 00227 ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n", 00228 ast_inet_ntoa(http_desc.oldsin.sin_addr)); 00229 ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n", 00230 ntohs(http_desc.oldsin.sin_port)); 00231 if (http_tls_cfg.enabled) 00232 ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n", 00233 ntohs(https_desc.oldsin.sin_port)); 00234 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n"); 00235 for (v = vars; v; v = v->next) { 00236 if (strncasecmp(v->name, "cookie_", 7)) 00237 ast_str_append(&out, 0, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value); 00238 } 00239 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n"); 00240 for (v = vars; v; v = v->next) { 00241 if (!strncasecmp(v->name, "cookie_", 7)) 00242 ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value); 00243 } 00244 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"); 00245 return out; 00246 } 00247 00248 static struct ast_http_uri statusuri = { 00249 .callback = httpstatus_callback, 00250 .description = "Asterisk HTTP General Status", 00251 .uri = "httpstatus", 00252 .has_subtree = 0, 00253 }; 00254 00255 static struct ast_http_uri staticuri = { 00256 .callback = static_callback, 00257 .description = "Asterisk HTTP Static Delivery", 00258 .uri = "static", 00259 .has_subtree = 1, 00260 }; 00261 00262 struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text) 00263 { 00264 struct ast_str *out = ast_str_create(512); 00265 if (out == NULL) 00266 return out; 00267 ast_str_set(&out, 0, 00268 "Content-type: text/html\r\n" 00269 "%s" 00270 "\r\n" 00271 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n" 00272 "<html><head>\r\n" 00273 "<title>%d %s</title>\r\n" 00274 "</head><body>\r\n" 00275 "<h1>%s</h1>\r\n" 00276 "<p>%s</p>\r\n" 00277 "<hr />\r\n" 00278 "<address>Asterisk Server</address>\r\n" 00279 "</body></html>\r\n", 00280 (extra_header ? extra_header : ""), status, title, title, text); 00281 return out; 00282 } 00283 00284 /*! \brief 00285 * Link the new uri into the list. 00286 * 00287 * They are sorted by length of 00288 * the string, not alphabetically. Duplicate entries are not replaced, 00289 * but the insertion order (using <= and not just <) makes sure that 00290 * more recent insertions hide older ones. 00291 * On a lookup, we just scan the list and stop at the first matching entry. 00292 */ 00293 int ast_http_uri_link(struct ast_http_uri *urih) 00294 { 00295 struct ast_http_uri *uri; 00296 int len = strlen(urih->uri); 00297 00298 AST_RWLIST_WRLOCK(&uris); 00299 00300 if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) { 00301 AST_RWLIST_INSERT_HEAD(&uris, urih, entry); 00302 AST_RWLIST_UNLOCK(&uris); 00303 return 0; 00304 } 00305 00306 AST_RWLIST_TRAVERSE(&uris, uri, entry) { 00307 if ( AST_RWLIST_NEXT(uri, entry) 00308 && strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len ) { 00309 AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry); 00310 AST_RWLIST_UNLOCK(&uris); 00311 return 0; 00312 } 00313 } 00314 00315 AST_RWLIST_INSERT_TAIL(&uris, urih, entry); 00316 00317 AST_RWLIST_UNLOCK(&uris); 00318 00319 return 0; 00320 } 00321 00322 void ast_http_uri_unlink(struct ast_http_uri *urih) 00323 { 00324 AST_RWLIST_WRLOCK(&uris); 00325 AST_RWLIST_REMOVE(&uris, urih, entry); 00326 AST_RWLIST_UNLOCK(&uris); 00327 } 00328 00329 static struct ast_str *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength, struct ast_variable **cookies) 00330 { 00331 char *c; 00332 struct ast_str *out = NULL; 00333 char *params = uri; 00334 struct ast_http_uri *urih=NULL; 00335 int l; 00336 struct ast_variable *vars=NULL, *v, *prev = NULL; 00337 struct http_uri_redirect *redirect; 00338 00339 strsep(¶ms, "?"); 00340 /* Extract arguments from the request and store them in variables. */ 00341 if (params) { 00342 char *var, *val; 00343 00344 while ((val = strsep(¶ms, "&"))) { 00345 var = strsep(&val, "="); 00346 if (val) 00347 uri_decode(val); 00348 else 00349 val = ""; 00350 ast_uri_decode(var); 00351 if ((v = ast_variable_new(var, val))) { 00352 if (vars) 00353 prev->next = v; 00354 else 00355 vars = v; 00356 prev = v; 00357 } 00358 } 00359 } 00360 /* 00361 * Append the cookies to the variables (the only reason to have them 00362 * at the end is to avoid another pass of the cookies list to find 00363 * the tail). 00364 */ 00365 if (prev) 00366 prev->next = *cookies; 00367 else 00368 vars = *cookies; 00369 *cookies = NULL; 00370 ast_uri_decode(uri); 00371 00372 AST_RWLIST_RDLOCK(&uri_redirects); 00373 AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) { 00374 if (!strcasecmp(uri, redirect->target)) { 00375 char buf[512]; 00376 snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest); 00377 out = ast_http_error(302, "Moved Temporarily", buf, 00378 "There is no spoon..."); 00379 *status = 302; 00380 *title = strdup("Moved Temporarily"); 00381 break; 00382 } 00383 } 00384 AST_RWLIST_UNLOCK(&uri_redirects); 00385 if (redirect) 00386 goto cleanup; 00387 00388 /* We want requests to start with the prefix and '/' */ 00389 l = strlen(prefix); 00390 if (l && !strncasecmp(uri, prefix, l) && uri[l] == '/') { 00391 uri += l + 1; 00392 /* scan registered uris to see if we match one. */ 00393 AST_RWLIST_RDLOCK(&uris); 00394 AST_RWLIST_TRAVERSE(&uris, urih, entry) { 00395 l = strlen(urih->uri); 00396 c = uri + l; /* candidate */ 00397 if (strncasecmp(urih->uri, uri, l) /* no match */ 00398 || (*c && *c != '/')) /* substring */ 00399 continue; 00400 if (*c == '/') 00401 c++; 00402 if (!*c || urih->has_subtree) { 00403 uri = c; 00404 break; 00405 } 00406 } 00407 if (!urih) 00408 AST_RWLIST_UNLOCK(&uris); 00409 } 00410 if (urih) { 00411 out = urih->callback(sin, uri, vars, status, title, contentlength); 00412 AST_RWLIST_UNLOCK(&uris); 00413 } else { 00414 out = ast_http_error(404, "Not Found", NULL, 00415 "The requested URL was not found on this server."); 00416 *status = 404; 00417 *title = strdup("Not Found"); 00418 } 00419 00420 cleanup: 00421 ast_variables_destroy(vars); 00422 return out; 00423 } 00424 00425 #ifdef DO_SSL 00426 #if defined(HAVE_FUNOPEN) 00427 #define HOOK_T int 00428 #define LEN_T int 00429 #else 00430 #define HOOK_T ssize_t 00431 #define LEN_T size_t 00432 #endif 00433 /*! 00434 * replacement read/write functions for SSL support. 00435 * We use wrappers rather than SSL_read/SSL_write directly so 00436 * we can put in some debugging. 00437 */ 00438 static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len) 00439 { 00440 int i = SSL_read(cookie, buf, len-1); 00441 #if 0 00442 if (i >= 0) 00443 buf[i] = '\0'; 00444 ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf); 00445 #endif 00446 return i; 00447 } 00448 00449 static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len) 00450 { 00451 #if 0 00452 char *s = alloca(len+1); 00453 strncpy(s, buf, len); 00454 s[len] = '\0'; 00455 ast_verbose("ssl write size %d <%s>\n", (int)len, s); 00456 #endif 00457 return SSL_write(cookie, buf, len); 00458 } 00459 00460 static int ssl_close(void *cookie) 00461 { 00462 close(SSL_get_fd(cookie)); 00463 SSL_shutdown(cookie); 00464 SSL_free(cookie); 00465 return 0; 00466 } 00467 #endif /* DO_SSL */ 00468 00469 /*! 00470 * creates a FILE * from the fd passed by the accept thread. 00471 * This operation is potentially expensive (certificate verification), 00472 * so we do it in the child thread context. 00473 */ 00474 static void *make_file_from_fd(void *data) 00475 { 00476 struct server_instance *ser = data; 00477 00478 /* 00479 * open a FILE * as appropriate. 00480 */ 00481 if (!ser->parent->tls_cfg) 00482 ser->f = fdopen(ser->fd, "w+"); 00483 #ifdef DO_SSL 00484 else if ( (ser->ssl = SSL_new(ser->parent->tls_cfg->ssl_ctx)) ) { 00485 SSL_set_fd(ser->ssl, ser->fd); 00486 if (SSL_accept(ser->ssl) == 0) 00487 ast_verbose(" error setting up ssl connection"); 00488 else { 00489 #if defined(HAVE_FUNOPEN) /* the BSD interface */ 00490 ser->f = funopen(ser->ssl, ssl_read, ssl_write, NULL, ssl_close); 00491 00492 #elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */ 00493 static const cookie_io_functions_t cookie_funcs = { 00494 ssl_read, ssl_write, NULL, ssl_close 00495 }; 00496 ser->f = fopencookie(ser->ssl, "w+", cookie_funcs); 00497 #else 00498 /* could add other methods here */ 00499 #endif 00500 } 00501 if (!ser->f) /* no success opening descriptor stacking */ 00502 SSL_free(ser->ssl); 00503 } 00504 #endif /* DO_SSL */ 00505 00506 if (!ser->f) { 00507 close(ser->fd); 00508 ast_log(LOG_WARNING, "FILE * open failed!\n"); 00509 free(ser); 00510 return NULL; 00511 } 00512 return ser->parent->worker_fn(ser); 00513 } 00514 00515 static void *httpd_helper_thread(void *data) 00516 { 00517 char buf[4096]; 00518 char cookie[4096]; 00519 struct server_instance *ser = data; 00520 struct ast_variable *var, *prev=NULL, *vars=NULL; 00521 char *uri, *title=NULL; 00522 int status = 200, contentlength = 0; 00523 struct ast_str *out = NULL; 00524 00525 if (!fgets(buf, sizeof(buf), ser->f)) 00526 goto done; 00527 00528 uri = ast_skip_nonblanks(buf); /* Skip method */ 00529 if (*uri) 00530 *uri++ = '\0'; 00531 00532 uri = ast_skip_blanks(uri); /* Skip white space */ 00533 00534 if (*uri) { /* terminate at the first blank */ 00535 char *c = ast_skip_nonblanks(uri); 00536 if (*c) 00537 *c = '\0'; 00538 } 00539 00540 /* process "Cookie: " lines */ 00541 while (fgets(cookie, sizeof(cookie), ser->f)) { 00542 char *vname, *vval; 00543 int l; 00544 00545 /* Trim trailing characters */ 00546 ast_trim_blanks(cookie); 00547 if (ast_strlen_zero(cookie)) 00548 break; 00549 if (strncasecmp(cookie, "Cookie: ", 8)) 00550 continue; 00551 00552 /* TODO - The cookie parsing code below seems to work 00553 in IE6 and FireFox 1.5. However, it is not entirely 00554 correct, and therefore may not work in all 00555 circumstances. 00556 For more details see RFC 2109 and RFC 2965 */ 00557 00558 /* FireFox cookie strings look like: 00559 Cookie: mansession_id="********" 00560 InternetExplorer's look like: 00561 Cookie: $Version="1"; mansession_id="********" */ 00562 00563 /* If we got a FireFox cookie string, the name's right 00564 after "Cookie: " */ 00565 vname = ast_skip_blanks(cookie + 8); 00566 00567 /* If we got an IE cookie string, we need to skip to 00568 past the version to get to the name */ 00569 if (*vname == '$') { 00570 strsep(&vname, ";"); 00571 if (!vname) /* no name ? */ 00572 continue; 00573 vname = ast_skip_blanks(vname); 00574 } 00575 vval = strchr(vname, '='); 00576 if (!vval) 00577 continue; 00578 /* Ditch the = and the quotes */ 00579 *vval++ = '\0'; 00580 if (*vval) 00581 vval++; 00582 if ( (l = strlen(vval)) ) 00583 vval[l - 1] = '\0'; /* trim trailing quote */ 00584 var = ast_variable_new(vname, vval); 00585 if (var) { 00586 if (prev) 00587 prev->next = var; 00588 else 00589 vars = var; 00590 prev = var; 00591 } 00592 } 00593 00594 if (!*uri) 00595 out = ast_http_error(400, "Bad Request", NULL, "Invalid Request"); 00596 else if (strcasecmp(buf, "get")) 00597 out = ast_http_error(501, "Not Implemented", NULL, 00598 "Attempt to use unimplemented / unsupported method"); 00599 else /* try to serve it */ 00600 out = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars); 00601 00602 /* If they aren't mopped up already, clean up the cookies */ 00603 if (vars) 00604 ast_variables_destroy(vars); 00605 00606 if (out == NULL) 00607 out = ast_http_error(500, "Internal Error", NULL, "Internal Server Error"); 00608 if (out) { 00609 time_t t = time(NULL); 00610 char timebuf[256]; 00611 00612 strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t)); 00613 fprintf(ser->f, "HTTP/1.1 %d %s\r\n" 00614 "Server: Asterisk\r\n" 00615 "Date: %s\r\n" 00616 "Connection: close\r\n", 00617 status, title ? title : "OK", timebuf); 00618 if (!contentlength) { /* opaque body ? just dump it hoping it is properly formatted */ 00619 fprintf(ser->f, "%s", out->str); 00620 } else { 00621 char *tmp = strstr(out->str, "\r\n\r\n"); 00622 00623 if (tmp) { 00624 fprintf(ser->f, "Content-length: %d\r\n", contentlength); 00625 /* first write the header, then the body */ 00626 fwrite(out->str, 1, (tmp + 4 - out->str), ser->f); 00627 fwrite(tmp + 4, 1, contentlength, ser->f); 00628 } 00629 } 00630 free(out); 00631 } 00632 if (title) 00633 free(title); 00634 00635 done: 00636 if (ser->f) 00637 fclose(ser->f); 00638 free(ser); 00639 return NULL; 00640 } 00641 00642 void *server_root(void *data) 00643 { 00644 struct server_args *desc = data; 00645 int fd; 00646 struct sockaddr_in sin; 00647 socklen_t sinlen; 00648 struct server_instance *ser; 00649 pthread_t launched; 00650 pthread_attr_t attr; 00651 00652 for (;;) { 00653 int i, flags; 00654 00655 if (desc->periodic_fn) 00656 desc->periodic_fn(desc); 00657 i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout); 00658 if (i <= 0) 00659 continue; 00660 sinlen = sizeof(sin); 00661 fd = accept(desc->accept_fd, (struct sockaddr *)&sin, &sinlen); 00662 if (fd < 0) { 00663 if ((errno != EAGAIN) && (errno != EINTR)) 00664 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno)); 00665 continue; 00666 } 00667 ser = ast_calloc(1, sizeof(*ser)); 00668 if (!ser) { 00669 ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno)); 00670 close(fd); 00671 continue; 00672 } 00673 flags = fcntl(fd, F_GETFL); 00674 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); 00675 ser->fd = fd; 00676 ser->parent = desc; 00677 memcpy(&ser->requestor, &sin, sizeof(ser->requestor)); 00678 00679 pthread_attr_init(&attr); 00680 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 00681 00682 if (ast_pthread_create_background(&launched, &attr, make_file_from_fd, ser)) { 00683 ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno)); 00684 close(ser->fd); 00685 free(ser); 00686 } 00687 00688 pthread_attr_destroy(&attr); 00689 } 00690 return NULL; 00691 } 00692 00693 int ssl_setup(struct tls_config *cfg) 00694 { 00695 #ifndef DO_SSL 00696 cfg->enabled = 0; 00697 return 0; 00698 #else 00699 if (!cfg->enabled) 00700 return 0; 00701 SSL_load_error_strings(); 00702 SSLeay_add_ssl_algorithms(); 00703 cfg->ssl_ctx = SSL_CTX_new( SSLv23_server_method() ); 00704 if (!ast_strlen_zero(cfg->certfile)) { 00705 if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 || 00706 SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 || 00707 SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 ) { 00708 ast_verbose("ssl cert error <%s>", cfg->certfile); 00709 sleep(2); 00710 cfg->enabled = 0; 00711 return 0; 00712 } 00713 } 00714 if (!ast_strlen_zero(cfg->cipher)) { 00715 if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) { 00716 ast_verbose("ssl cipher error <%s>", cfg->cipher); 00717 sleep(2); 00718 cfg->enabled = 0; 00719 return 0; 00720 } 00721 } 00722 ast_verbose("ssl cert ok"); 00723 return 1; 00724 #endif 00725 } 00726 00727 /*! 00728 * This is a generic (re)start routine for a TCP server, 00729 * which does the socket/bind/listen and starts a thread for handling 00730 * accept(). 00731 */ 00732 void server_start(struct server_args *desc) 00733 { 00734 int flags; 00735 int x = 1; 00736 00737 /* Do nothing if nothing has changed */ 00738 if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) { 00739 if (option_debug) 00740 ast_log(LOG_DEBUG, "Nothing changed in %s\n", desc->name); 00741 return; 00742 } 00743 00744 desc->oldsin = desc->sin; 00745 00746 /* Shutdown a running server if there is one */ 00747 if (desc->master != AST_PTHREADT_NULL) { 00748 pthread_cancel(desc->master); 00749 pthread_kill(desc->master, SIGURG); 00750 pthread_join(desc->master, NULL); 00751 } 00752 00753 if (desc->accept_fd != -1) 00754 close(desc->accept_fd); 00755 00756 /* If there's no new server, stop here */ 00757 if (desc->sin.sin_family == 0) 00758 return; 00759 00760 desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0); 00761 if (desc->accept_fd < 0) { 00762 ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n", 00763 desc->name, strerror(errno)); 00764 return; 00765 } 00766 00767 setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); 00768 if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) { 00769 ast_log(LOG_NOTICE, "Unable to bind %s to %s:%d: %s\n", 00770 desc->name, 00771 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port), 00772 strerror(errno)); 00773 goto error; 00774 } 00775 if (listen(desc->accept_fd, 10)) { 00776 ast_log(LOG_NOTICE, "Unable to listen for %s!\n", desc->name); 00777 goto error; 00778 } 00779 flags = fcntl(desc->accept_fd, F_GETFL); 00780 fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK); 00781 if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) { 00782 ast_log(LOG_NOTICE, "Unable to launch %s on %s:%d: %s\n", 00783 desc->name, 00784 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port), 00785 strerror(errno)); 00786 goto error; 00787 } 00788 return; 00789 00790 error: 00791 close(desc->accept_fd); 00792 desc->accept_fd = -1; 00793 } 00794 00795 /*! 00796 * \brief Add a new URI redirect 00797 * The entries in the redirect list are sorted by length, just like the list 00798 * of URI handlers. 00799 */ 00800 static void add_redirect(const char *value) 00801 { 00802 char *target, *dest; 00803 struct http_uri_redirect *redirect, *cur; 00804 unsigned int target_len; 00805 unsigned int total_len; 00806 00807 dest = ast_strdupa(value); 00808 dest = ast_skip_blanks(dest); 00809 target = strsep(&dest, " "); 00810 target = ast_skip_blanks(target); 00811 target = strsep(&target, " "); /* trim trailing whitespace */ 00812 00813 if (!dest) { 00814 ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value); 00815 return; 00816 } 00817 00818 target_len = strlen(target) + 1; 00819 total_len = sizeof(*redirect) + target_len + strlen(dest) + 1; 00820 00821 if (!(redirect = ast_calloc(1, total_len))) 00822 return; 00823 00824 redirect->dest = redirect->target + target_len; 00825 strcpy(redirect->target, target); 00826 strcpy(redirect->dest, dest); 00827 00828 AST_RWLIST_WRLOCK(&uri_redirects); 00829 00830 target_len--; /* So we can compare directly with strlen() */ 00831 if ( AST_RWLIST_EMPTY(&uri_redirects) 00832 || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) { 00833 AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry); 00834 AST_RWLIST_UNLOCK(&uri_redirects); 00835 return; 00836 } 00837 00838 AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) { 00839 if ( AST_RWLIST_NEXT(cur, entry) 00840 && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) { 00841 AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry); 00842 AST_RWLIST_UNLOCK(&uri_redirects); 00843 return; 00844 } 00845 } 00846 00847 AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry); 00848 00849 AST_RWLIST_UNLOCK(&uri_redirects); 00850 } 00851 00852 static int __ast_http_load(int reload) 00853 { 00854 struct ast_config *cfg; 00855 struct ast_variable *v; 00856 int enabled=0; 00857 int newenablestatic=0; 00858 struct hostent *hp; 00859 struct ast_hostent ahp; 00860 char newprefix[MAX_PREFIX]; 00861 int have_sslbindaddr = 0; 00862 struct http_uri_redirect *redirect; 00863 00864 /* default values */ 00865 memset(&http_desc.sin, 0, sizeof(http_desc.sin)); 00866 http_desc.sin.sin_port = htons(8088); 00867 00868 memset(&https_desc.sin, 0, sizeof(https_desc.sin)); 00869 https_desc.sin.sin_port = htons(8089); 00870 strcpy(newprefix, DEFAULT_PREFIX); 00871 00872 http_tls_cfg.enabled = 0; 00873 if (http_tls_cfg.certfile) 00874 free(http_tls_cfg.certfile); 00875 http_tls_cfg.certfile = ast_strdup(AST_CERTFILE); 00876 if (http_tls_cfg.cipher) 00877 free(http_tls_cfg.cipher); 00878 http_tls_cfg.cipher = ast_strdup(""); 00879 00880 AST_RWLIST_WRLOCK(&uri_redirects); 00881 while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) 00882 free(redirect); 00883 AST_RWLIST_UNLOCK(&uri_redirects); 00884 00885 cfg = ast_config_load("http.conf"); 00886 if (cfg) { 00887 v = ast_variable_browse(cfg, "general"); 00888 for (; v; v = v->next) { 00889 if (!strcasecmp(v->name, "enabled")) 00890 enabled = ast_true(v->value); 00891 else if (!strcasecmp(v->name, "sslenable")) 00892 http_tls_cfg.enabled = ast_true(v->value); 00893 else if (!strcasecmp(v->name, "sslbindport")) 00894 https_desc.sin.sin_port = htons(atoi(v->value)); 00895 else if (!strcasecmp(v->name, "sslcert")) { 00896 free(http_tls_cfg.certfile); 00897 http_tls_cfg.certfile = ast_strdup(v->value); 00898 } else if (!strcasecmp(v->name, "sslcipher")) { 00899 free(http_tls_cfg.cipher); 00900 http_tls_cfg.cipher = ast_strdup(v->value); 00901 } 00902 else if (!strcasecmp(v->name, "enablestatic")) 00903 newenablestatic = ast_true(v->value); 00904 else if (!strcasecmp(v->name, "bindport")) 00905 http_desc.sin.sin_port = htons(atoi(v->value)); 00906 else if (!strcasecmp(v->name, "sslbindaddr")) { 00907 if ((hp = ast_gethostbyname(v->value, &ahp))) { 00908 memcpy(&https_desc.sin.sin_addr, hp->h_addr, sizeof(https_desc.sin.sin_addr)); 00909 have_sslbindaddr = 1; 00910 } else { 00911 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value); 00912 } 00913 } else if (!strcasecmp(v->name, "bindaddr")) { 00914 if ((hp = ast_gethostbyname(v->value, &ahp))) { 00915 memcpy(&http_desc.sin.sin_addr, hp->h_addr, sizeof(http_desc.sin.sin_addr)); 00916 } else { 00917 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value); 00918 } 00919 } else if (!strcasecmp(v->name, "prefix")) { 00920 if (!ast_strlen_zero(v->value)) { 00921 newprefix[0] = '/'; 00922 ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1); 00923 } else { 00924 newprefix[0] = '\0'; 00925 } 00926 } else if (!strcasecmp(v->name, "redirect")) { 00927 add_redirect(v->value); 00928 } else { 00929 ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name); 00930 } 00931 } 00932 ast_config_destroy(cfg); 00933 } 00934 if (!have_sslbindaddr) 00935 https_desc.sin.sin_addr = http_desc.sin.sin_addr; 00936 if (enabled) 00937 http_desc.sin.sin_family = https_desc.sin.sin_family = AF_INET; 00938 if (strcmp(prefix, newprefix)) 00939 ast_copy_string(prefix, newprefix, sizeof(prefix)); 00940 enablestatic = newenablestatic; 00941 server_start(&http_desc); 00942 if (ssl_setup(https_desc.tls_cfg)) 00943 server_start(&https_desc); 00944 return 0; 00945 } 00946 00947 static int handle_show_http(int fd, int argc, char *argv[]) 00948 { 00949 struct ast_http_uri *urih; 00950 struct http_uri_redirect *redirect; 00951 00952 if (argc != 3) 00953 return RESULT_SHOWUSAGE; 00954 00955 ast_cli(fd, "HTTP Server Status:\n"); 00956 ast_cli(fd, "Prefix: %s\n", prefix); 00957 if (!http_desc.oldsin.sin_family) 00958 ast_cli(fd, "Server Disabled\n\n"); 00959 else { 00960 ast_cli(fd, "Server Enabled and Bound to %s:%d\n\n", 00961 ast_inet_ntoa(http_desc.oldsin.sin_addr), 00962 ntohs(http_desc.oldsin.sin_port)); 00963 if (http_tls_cfg.enabled) 00964 ast_cli(fd, "HTTPS Server Enabled and Bound to %s:%d\n\n", 00965 ast_inet_ntoa(https_desc.oldsin.sin_addr), 00966 ntohs(https_desc.oldsin.sin_port)); 00967 } 00968 00969 ast_cli(fd, "Enabled URI's:\n"); 00970 AST_RWLIST_RDLOCK(&uris); 00971 if (AST_RWLIST_EMPTY(&uris)) { 00972 ast_cli(fd, "None.\n"); 00973 } else { 00974 AST_RWLIST_TRAVERSE(&uris, urih, entry) 00975 ast_cli(fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description); 00976 } 00977 AST_RWLIST_UNLOCK(&uris); 00978 00979 ast_cli(fd, "\nEnabled Redirects:\n"); 00980 AST_RWLIST_RDLOCK(&uri_redirects); 00981 AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) 00982 ast_cli(fd, " %s => %s\n", redirect->target, redirect->dest); 00983 if (AST_RWLIST_EMPTY(&uri_redirects)) 00984 ast_cli(fd, " None.\n"); 00985 AST_RWLIST_UNLOCK(&uri_redirects); 00986 00987 return RESULT_SUCCESS; 00988 } 00989 00990 int ast_http_reload(void) 00991 { 00992 return __ast_http_load(1); 00993 } 00994 00995 static char show_http_help[] = 00996 "Usage: http show status\n" 00997 " Lists status of internal HTTP engine\n"; 00998 00999 static struct ast_cli_entry cli_http[] = { 01000 { { "http", "show", "status", NULL }, 01001 handle_show_http, "Display HTTP server status", 01002 show_http_help }, 01003 }; 01004 01005 int ast_http_init(void) 01006 { 01007 ast_http_uri_link(&statusuri); 01008 ast_http_uri_link(&staticuri); 01009 ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry)); 01010 return __ast_http_load(0); 01011 }