Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:48 2007

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>&nbsp;&nbsp;Asterisk&trade; 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(&params, "?");
00340    /* Extract arguments from the request and store them in variables. */
00341    if (params) {
00342       char *var, *val;
00343 
00344       while ((val = strsep(&params, "&"))) {
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 }

Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen