2006-09-19 19:58:09 +00:00
/*
2006-03-25 23:50:09 +00:00
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
2006-03-26 16:48:47 +00:00
/*!
2009-04-23 20:36:35 +00:00
* \file
2006-04-03 18:38:28 +00:00
* \brief http server for AMI access
2006-03-26 16:48:47 +00:00
*
2006-04-03 18:38:28 +00:00
* \author Mark Spencer <markster@digium.com>
2007-04-06 21:16:38 +00:00
*
* This program implements a tiny http server
2009-04-23 20:36:35 +00:00
* and was inspired by micro-httpd by Jef Poskanzer
*
2009-11-02 20:43:52 +00:00
* \extref GMime http://spruce.sourceforge.net/gmime/
*
2006-04-03 18:38:28 +00:00
* \ref AstHTTP - AMI over the http protocol
2006-03-26 16:48:47 +00:00
*/
2011-07-14 20:28:54 +00:00
/*** MODULEINFO
<support_level>core</support_level>
***/
2006-06-07 18:54:56 +00:00
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
2006-03-25 23:50:09 +00:00
# include <time.h>
# include <sys/time.h>
2006-04-01 08:49:54 +00:00
# include <sys/stat.h>
2006-03-25 23:50:09 +00:00
# include <sys/signal.h>
# include <fcntl.h>
2006-03-26 16:48:47 +00:00
2008-03-18 22:32:26 +00:00
# include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
2006-03-26 16:48:47 +00:00
# include "asterisk/cli.h"
2008-01-18 22:04:33 +00:00
# include "asterisk/tcptls.h"
2006-03-26 16:48:47 +00:00
# include "asterisk/http.h"
# include "asterisk/utils.h"
# include "asterisk/strings.h"
2006-04-01 08:49:54 +00:00
# include "asterisk/config.h"
2006-12-23 20:13:14 +00:00
# include "asterisk/stringfields.h"
2008-02-26 20:02:14 +00:00
# include "asterisk/ast_version.h"
2007-04-06 21:16:38 +00:00
# include "asterisk/manager.h"
2008-03-18 22:32:26 +00:00
# include "asterisk/_private.h"
2008-10-19 19:11:28 +00:00
# include "asterisk/astobj2.h"
2010-10-29 20:46:06 +00:00
# include "asterisk/netsock2.h"
2006-03-25 23:50:09 +00:00
# define MAX_PREFIX 80
2010-10-29 20:46:06 +00:00
# define DEFAULT_PORT 8088
# define DEFAULT_TLS_PORT 8089
2011-04-21 18:32:50 +00:00
# define DEFAULT_SESSION_LIMIT 100
2014-06-12 16:22:19 +00:00
# define DEFAULT_SESSION_INACTIVITY 30000 /* (ms) Idle time waiting for data. */
2006-03-25 23:50:09 +00:00
2007-01-24 09:31:26 +00:00
/* See http.h for more information about the SSL implementation */
2006-10-22 19:09:25 +00:00
# if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
# define DO_SSL /* comment in/out if you want to support ssl */
2006-10-22 12:02:35 +00:00
# endif
2011-04-21 18:32:50 +00:00
static int session_limit = DEFAULT_SESSION_LIMIT ;
2014-06-12 16:22:19 +00:00
static int session_inactivity = DEFAULT_SESSION_INACTIVITY ;
2011-04-21 18:32:50 +00:00
static int session_count = 0 ;
2008-01-18 22:04:33 +00:00
static struct ast_tls_config http_tls_cfg ;
2006-10-22 12:02:35 +00:00
2006-11-25 17:37:04 +00:00
static void * httpd_helper_thread ( void * arg ) ;
2006-10-22 12:02:35 +00:00
/*!
* we have up to two accepting threads, one for http, one for https
*/
2008-10-19 19:11:28 +00:00
static struct ast_tcptls_session_args http_desc = {
2006-10-22 12:02:35 +00:00
. accept_fd = - 1 ,
. master = AST_PTHREADT_NULL ,
2006-12-07 16:42:29 +00:00
. tls_cfg = NULL ,
2006-11-28 00:02:42 +00:00
. poll_timeout = - 1 ,
2006-11-25 17:37:04 +00:00
. name = " http server " ,
2008-03-04 22:23:21 +00:00
. accept_fn = ast_tcptls_server_root ,
2006-11-25 17:37:04 +00:00
. worker_fn = httpd_helper_thread ,
2006-10-22 12:02:35 +00:00
} ;
2008-10-19 19:11:28 +00:00
static struct ast_tcptls_session_args https_desc = {
2006-10-22 12:02:35 +00:00
. accept_fd = - 1 ,
. master = AST_PTHREADT_NULL ,
2006-12-07 16:42:29 +00:00
. tls_cfg = & http_tls_cfg ,
2006-11-28 00:02:42 +00:00
. poll_timeout = - 1 ,
2006-11-25 17:37:04 +00:00
. name = " https server " ,
2008-03-04 22:23:21 +00:00
. accept_fn = ast_tcptls_server_root ,
2006-11-25 17:37:04 +00:00
. worker_fn = httpd_helper_thread ,
2006-10-22 12:02:35 +00:00
} ;
2006-03-25 23:50:09 +00:00
2006-12-28 20:02:41 +00:00
static AST_RWLIST_HEAD_STATIC ( uris , ast_http_uri ) ; /*!< list of supported handlers */
2006-10-16 08:38:35 +00:00
/* all valid URIs must be prepended by the string in prefix. */
2006-03-25 23:50:09 +00:00
static char prefix [ MAX_PREFIX ] ;
2006-12-27 22:14:33 +00:00
static int enablestatic ;
2006-04-01 08:49:54 +00:00
2006-04-03 18:38:28 +00:00
/*! \brief Limit the kinds of files we're willing to serve up */
2006-04-01 08:49:54 +00:00
static struct {
2007-04-11 14:49:07 +00:00
const char * ext ;
const char * mtype ;
2006-04-01 08:49:54 +00:00
} mimetypes [ ] = {
{ " png " , " image/png " } ,
2009-04-23 20:36:35 +00:00
{ " xml " , " text/xml " } ,
2006-04-01 08:49:54 +00:00
{ " jpg " , " image/jpeg " } ,
{ " js " , " application/x-javascript " } ,
{ " wav " , " audio/x-wav " } ,
{ " mp3 " , " audio/mpeg " } ,
2007-03-21 18:10:01 +00:00
{ " svg " , " image/svg+xml " } ,
2007-04-11 14:49:07 +00:00
{ " svgz " , " image/svg+xml " } ,
2007-04-05 15:48:17 +00:00
{ " gif " , " image/gif " } ,
2011-01-31 13:57:53 +00:00
{ " html " , " text/html " } ,
{ " htm " , " text/html " } ,
2011-02-19 14:07:38 +00:00
{ " css " , " text/css " } ,
2011-01-31 13:57:53 +00:00
{ " cnf " , " text/plain " } ,
{ " cfg " , " text/plain " } ,
{ " bin " , " application/octet-stream " } ,
{ " sbn " , " application/octet-stream " } ,
{ " ld " , " application/octet-stream " } ,
2006-04-01 08:49:54 +00:00
} ;
2006-12-23 20:13:14 +00:00
struct http_uri_redirect {
AST_LIST_ENTRY ( http_uri_redirect ) entry ;
2006-12-24 03:29:42 +00:00
char * dest ;
char target [ 0 ] ;
2006-12-23 20:13:14 +00:00
} ;
2006-12-28 20:05:00 +00:00
static AST_RWLIST_HEAD_STATIC ( uri_redirects , http_uri_redirect ) ;
2006-12-23 20:13:14 +00:00
2009-06-15 17:34:30 +00:00
static const struct ast_cfhttp_methods_text {
2009-04-23 20:36:35 +00:00
enum ast_http_method method ;
2011-05-05 02:34:29 +00:00
const char * text ;
2009-04-23 20:36:35 +00:00
} ast_http_methods_text [ ] = {
{ AST_HTTP_UNKNOWN , " UNKNOWN " } ,
{ AST_HTTP_GET , " GET " } ,
{ AST_HTTP_POST , " POST " } ,
{ AST_HTTP_HEAD , " HEAD " } ,
{ AST_HTTP_PUT , " PUT " } ,
} ;
const char * ast_get_http_method ( enum ast_http_method method )
{
2011-05-05 02:34:29 +00:00
int x ;
for ( x = 0 ; x < ARRAY_LEN ( ast_http_methods_text ) ; x + + ) {
if ( ast_http_methods_text [ x ] . method = = method ) {
return ast_http_methods_text [ x ] . text ;
}
}
return NULL ;
2009-04-23 20:36:35 +00:00
}
const char * ast_http_ftype2mtype ( const char * ftype )
2006-04-01 08:49:54 +00:00
{
int x ;
2008-03-19 04:09:55 +00:00
2006-04-01 08:49:54 +00:00
if ( ftype ) {
2008-03-19 04:09:55 +00:00
for ( x = 0 ; x < ARRAY_LEN ( mimetypes ) ; x + + ) {
2008-03-19 15:41:54 +00:00
if ( ! strcasecmp ( ftype , mimetypes [ x ] . ext ) ) {
2006-04-01 08:49:54 +00:00
return mimetypes [ x ] . mtype ;
2008-03-19 15:41:54 +00:00
}
2006-04-01 08:49:54 +00:00
}
}
2009-04-23 20:36:35 +00:00
return NULL ;
2006-04-01 08:49:54 +00:00
}
2009-04-23 20:36:35 +00:00
uint32_t ast_http_manid_from_vars ( struct ast_variable * headers )
{
uint32_t mngid = 0 ;
struct ast_variable * v , * cookies ;
2008-05-23 21:19:42 +00:00
2009-04-23 20:36:35 +00:00
cookies = ast_http_get_cookies ( headers ) ;
for ( v = cookies ; v ; v = v - > next ) {
if ( ! strcasecmp ( v - > name , " mansession_id " ) ) {
2009-08-10 19:20:57 +00:00
sscanf ( v - > value , " %30x " , & mngid ) ;
2009-04-23 20:36:35 +00:00
break ;
}
}
2014-03-10 17:09:42 +00:00
ast_variables_destroy ( cookies ) ;
2008-05-23 21:19:42 +00:00
return mngid ;
}
2008-07-02 20:28:17 +00:00
void ast_http_prefix ( char * buf , int len )
{
if ( buf ) {
ast_copy_string ( buf , prefix , len ) ;
}
}
2009-04-23 20:36:35 +00:00
static int static_callback ( struct ast_tcptls_session_instance * ser ,
const struct ast_http_uri * urih , const char * uri ,
enum ast_http_method method , struct ast_variable * get_vars ,
struct ast_variable * headers )
2006-04-01 08:49:54 +00:00
{
char * path ;
2009-04-23 20:36:35 +00:00
const char * ftype ;
2007-04-11 14:49:07 +00:00
const char * mtype ;
2006-04-01 08:49:54 +00:00
char wkspace [ 80 ] ;
struct stat st ;
int len ;
int fd ;
2009-04-23 20:36:35 +00:00
struct ast_str * http_header ;
struct timeval tv ;
2007-07-18 19:47:20 +00:00
struct ast_tm tm ;
2009-04-23 20:36:35 +00:00
char timebuf [ 80 ] , etag [ 23 ] ;
struct ast_variable * v ;
int not_modified = 0 ;
2006-04-01 08:49:54 +00:00
2009-04-23 20:36:35 +00:00
if ( method ! = AST_HTTP_GET & & method ! = AST_HTTP_HEAD ) {
ast_http_error ( ser , 501 , " Not Implemented " , " Attempt to use unimplemented / unsupported method " ) ;
return - 1 ;
}
/* Yuck. I'm not really sold on this, but if you don't deliver static content it makes your configuration
2006-04-01 08:49:54 +00:00
substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
2008-03-19 15:41:54 +00:00
if ( ! enablestatic | | ast_strlen_zero ( uri ) ) {
2006-04-01 08:49:54 +00:00
goto out403 ;
2008-03-19 15:41:54 +00:00
}
2013-03-27 18:51:11 +00:00
/* Disallow any funny filenames at all (checking first character only??) */
2008-03-19 15:41:54 +00:00
if ( ( uri [ 0 ] < 33 ) | | strchr ( " ./|~@#$%^&*() \t " , uri [ 0 ] ) ) {
2006-04-01 08:49:54 +00:00
goto out403 ;
2008-03-19 15:41:54 +00:00
}
if ( strstr ( uri , " /.. " ) ) {
2006-04-01 08:49:54 +00:00
goto out403 ;
2008-03-19 15:41:54 +00:00
}
2009-04-23 20:36:35 +00:00
2008-03-19 15:41:54 +00:00
if ( ( ftype = strrchr ( uri , ' . ' ) ) ) {
2006-04-01 08:49:54 +00:00
ftype + + ;
2008-03-19 15:41:54 +00:00
}
2009-04-23 20:36:35 +00:00
if ( ! ( mtype = ast_http_ftype2mtype ( ftype ) ) ) {
snprintf ( wkspace , sizeof ( wkspace ) , " text/%s " , S_OR ( ftype , " plain " ) ) ;
2013-03-27 18:51:11 +00:00
mtype = wkspace ;
2009-04-23 20:36:35 +00:00
}
2006-04-01 08:49:54 +00:00
/* Cap maximum length */
2008-03-27 22:10:25 +00:00
if ( ( len = strlen ( uri ) + strlen ( ast_config_AST_DATA_DIR ) + strlen ( " /static-http/ " ) + 5 ) > 1024 ) {
2006-04-01 08:49:54 +00:00
goto out403 ;
2008-03-19 15:41:54 +00:00
}
2009-04-23 20:36:35 +00:00
2012-07-31 20:21:43 +00:00
path = ast_alloca ( len ) ;
2006-04-15 22:53:53 +00:00
sprintf ( path , " %s/static-http/%s " , ast_config_AST_DATA_DIR , uri ) ;
2008-03-19 15:41:54 +00:00
if ( stat ( path , & st ) ) {
2006-04-01 08:49:54 +00:00
goto out404 ;
2008-03-19 15:41:54 +00:00
}
if ( S_ISDIR ( st . st_mode ) ) {
2006-04-01 08:49:54 +00:00
goto out404 ;
2009-04-23 20:36:35 +00:00
}
2008-03-19 15:41:54 +00:00
2013-03-27 18:51:11 +00:00
if ( strstr ( path , " /private/ " ) & & ! astman_is_authed ( ast_http_manid_from_vars ( headers ) ) ) {
2006-04-01 08:49:54 +00:00
goto out403 ;
2008-03-19 15:41:54 +00:00
}
2006-12-16 09:33:31 +00:00
2013-03-27 18:51:11 +00:00
fd = open ( path , O_RDONLY ) ;
if ( fd < 0 ) {
2008-05-23 21:19:42 +00:00
goto out403 ;
}
2009-04-23 20:36:35 +00:00
/* make "Etag:" http header value */
snprintf ( etag , sizeof ( etag ) , " \" %ld \" " , ( long ) st . st_mtime ) ;
/* make "Last-Modified:" http header value */
tv . tv_sec = st . st_mtime ;
tv . tv_usec = 0 ;
ast_strftime ( timebuf , sizeof ( timebuf ) , " %a, %d %b %Y %H:%M:%S GMT " , ast_localtime ( & tv , & tm , " GMT " ) ) ;
2007-06-29 20:35:09 +00:00
2009-04-23 20:36:35 +00:00
/* check received "If-None-Match" request header and Etag value for file */
for ( v = headers ; v ; v = v - > next ) {
if ( ! strcasecmp ( v - > name , " If-None-Match " ) ) {
if ( ! strcasecmp ( v - > value , etag ) ) {
not_modified = 1 ;
}
break ;
2008-11-02 18:52:13 +00:00
}
2008-03-19 15:41:54 +00:00
}
2006-12-16 09:33:31 +00:00
2009-04-23 20:36:35 +00:00
if ( ( http_header = ast_str_create ( 255 ) ) = = NULL ) {
2013-03-27 18:51:11 +00:00
close ( fd ) ;
2009-04-23 20:36:35 +00:00
return - 1 ;
}
2008-03-19 15:41:54 +00:00
2009-04-23 20:36:35 +00:00
ast_str_set ( & http_header , 0 , " Content-type: %s \r \n "
" ETag: %s \r \n "
2011-03-01 22:26:37 +00:00
" Last-Modified: %s \r \n " ,
2009-04-23 20:36:35 +00:00
mtype ,
etag ,
timebuf ) ;
/* ast_http_send() frees http_header, so we don't need to do it before returning */
if ( not_modified ) {
ast_http_send ( ser , method , 304 , " Not Modified " , http_header , NULL , 0 , 1 ) ;
} else {
ast_http_send ( ser , method , 200 , NULL , http_header , NULL , fd , 1 ) ; /* static content flag is set */
}
close ( fd ) ;
return 0 ;
2006-04-01 08:49:54 +00:00
out404 :
2009-04-23 20:36:35 +00:00
ast_http_error ( ser , 404 , " Not Found " , " The requested URL was not found on this server. " ) ;
return - 1 ;
2006-04-01 08:49:54 +00:00
out403 :
2009-04-23 20:36:35 +00:00
ast_http_error ( ser , 403 , " Access Denied " , " You do not have permission to access the requested URL. " ) ;
return - 1 ;
2006-04-01 08:49:54 +00:00
}
2006-03-25 23:50:09 +00:00
2009-04-23 20:36:35 +00:00
static int httpstatus_callback ( struct ast_tcptls_session_instance * ser ,
const struct ast_http_uri * urih , const char * uri ,
enum ast_http_method method , struct ast_variable * get_vars ,
struct ast_variable * headers )
2006-03-25 23:50:09 +00:00
{
2009-04-23 20:36:35 +00:00
struct ast_str * out ;
struct ast_variable * v , * cookies = NULL ;
2006-03-25 23:50:09 +00:00
2009-04-23 20:36:35 +00:00
if ( method ! = AST_HTTP_GET & & method ! = AST_HTTP_HEAD ) {
ast_http_error ( ser , 501 , " Not Implemented " , " Attempt to use unimplemented / unsupported method " ) ;
return - 1 ;
}
if ( ( out = ast_str_create ( 512 ) ) = = NULL ) {
return - 1 ;
2008-03-19 15:41:54 +00:00
}
2006-12-16 09:33:31 +00:00
ast_str_append ( & out , 0 ,
2009-04-23 20:36:35 +00:00
" <title>Asterisk HTTP Status</title> \r \n "
" <body bgcolor= \" #ffffff \" > \r \n "
" <table bgcolor= \" #f1f1f1 \" align= \" center \" ><tr><td bgcolor= \" #e0e0ff \" colspan= \" 2 \" width= \" 500 \" > \r \n "
" <h2> Asterisk™ HTTP Status</h2></td></tr> \r \n " ) ;
2006-12-16 09:33:31 +00:00
ast_str_append ( & out , 0 , " <tr><td><i>Prefix</i></td><td><b>%s</b></td></tr> \r \n " , prefix ) ;
ast_str_append ( & out , 0 , " <tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr> \r \n " ,
2010-07-08 22:08:07 +00:00
ast_sockaddr_stringify_addr ( & http_desc . old_address ) ) ;
ast_str_append ( & out , 0 , " <tr><td><i>Bind Port</i></td><td><b>%s</b></td></tr> \r \n " ,
ast_sockaddr_stringify_port ( & http_desc . old_address ) ) ;
2008-03-19 15:41:54 +00:00
if ( http_tls_cfg . enabled ) {
2010-07-08 22:08:07 +00:00
ast_str_append ( & out , 0 , " <tr><td><i>SSL Bind Port</i></td><td><b>%s</b></td></tr> \r \n " ,
ast_sockaddr_stringify_port ( & https_desc . old_address ) ) ;
2008-03-19 15:41:54 +00:00
}
2006-12-16 09:33:31 +00:00
ast_str_append ( & out , 0 , " <tr><td colspan= \" 2 \" ><hr></td></tr> \r \n " ) ;
2009-04-23 20:36:35 +00:00
for ( v = get_vars ; v ; v = v - > next ) {
ast_str_append ( & out , 0 , " <tr><td><i>Submitted GET Variable '%s'</i></td><td>%s</td></tr> \r \n " , v - > name , v - > value ) ;
2006-04-01 08:49:54 +00:00
}
2006-12-16 09:33:31 +00:00
ast_str_append ( & out , 0 , " <tr><td colspan= \" 2 \" ><hr></td></tr> \r \n " ) ;
2008-03-19 15:41:54 +00:00
2009-04-23 20:36:35 +00:00
cookies = ast_http_get_cookies ( headers ) ;
for ( v = cookies ; v ; v = v - > next ) {
ast_str_append ( & out , 0 , " <tr><td><i>Cookie '%s'</i></td><td>%s</td></tr> \r \n " , v - > name , v - > value ) ;
2006-03-25 23:50:09 +00:00
}
2009-04-23 20:36:35 +00:00
ast_variables_destroy ( cookies ) ;
2008-03-19 15:41:54 +00:00
2006-12-16 09:33:31 +00:00
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 " ) ;
2009-04-23 20:36:35 +00:00
ast_http_send ( ser , method , 200 , NULL , NULL , out , 0 , 0 ) ;
return 0 ;
2006-03-25 23:50:09 +00:00
}
static struct ast_http_uri statusuri = {
. callback = httpstatus_callback ,
. description = " Asterisk HTTP General Status " ,
. uri = " httpstatus " ,
2009-04-23 20:36:35 +00:00
. has_subtree = 0 ,
2008-04-02 15:25:48 +00:00
. data = NULL ,
. key = __FILE__ ,
2006-03-25 23:50:09 +00:00
} ;
2009-04-23 20:36:35 +00:00
2006-04-01 08:49:54 +00:00
static struct ast_http_uri staticuri = {
. callback = static_callback ,
. description = " Asterisk HTTP Static Delivery " ,
. uri = " static " ,
. has_subtree = 1 ,
2008-04-02 15:25:48 +00:00
. data = NULL ,
. key = __FILE__ ,
2006-04-01 08:49:54 +00:00
} ;
2009-04-23 20:36:35 +00:00
2011-03-01 22:26:37 +00:00
/* send http/1.1 response */
2009-04-23 20:36:35 +00:00
/* free content variable and close socket*/
void ast_http_send ( struct ast_tcptls_session_instance * ser ,
enum ast_http_method method , int status_code , const char * status_title ,
struct ast_str * http_header , struct ast_str * out , const int fd ,
unsigned int static_content )
2006-03-25 23:50:09 +00:00
{
2009-04-23 20:36:35 +00:00
struct timeval now = ast_tvnow ( ) ;
struct ast_tm tm ;
char timebuf [ 80 ] ;
int content_length = 0 ;
if ( ! ser | | 0 = = ser - > f ) {
return ;
}
ast_strftime ( timebuf , sizeof ( timebuf ) , " %a, %d %b %Y %H:%M:%S GMT " , ast_localtime ( & now , & tm , " GMT " ) ) ;
2011-03-01 22:26:37 +00:00
/* calc content length */
2009-04-23 20:36:35 +00:00
if ( out ) {
2014-04-23 17:51:19 +00:00
content_length + = ast_str_strlen ( out ) ;
2009-04-23 20:36:35 +00:00
}
if ( fd ) {
content_length + = lseek ( fd , 0 , SEEK_END ) ;
lseek ( fd , 0 , SEEK_SET ) ;
}
/* send http header */
fprintf ( ser - > f , " HTTP/1.1 %d %s \r \n "
" Server: Asterisk/%s \r \n "
" Date: %s \r \n "
" Connection: close \r \n "
" %s "
" Content-Length: %d \r \n "
2011-03-01 22:26:37 +00:00
" %s "
" \r \n " ,
2009-04-23 20:36:35 +00:00
status_code , status_title ? status_title : " OK " ,
ast_get_version ( ) ,
timebuf ,
static_content ? " " : " Cache-Control: no-cache, no-store \r \n " ,
content_length ,
http_header ? ast_str_buffer ( http_header ) : " "
) ;
/* send content */
if ( method ! = AST_HTTP_HEAD | | status_code > = 400 ) {
2014-04-23 17:51:19 +00:00
if ( out & & ast_str_strlen ( out ) ) {
if ( fwrite ( ast_str_buffer ( out ) , ast_str_strlen ( out ) , 1 , ser - > f ) ! = 1 ) {
2014-03-28 16:16:53 +00:00
ast_log ( LOG_ERROR , " fwrite() failed: %s \n " , strerror ( errno ) ) ;
}
2009-04-23 20:36:35 +00:00
}
if ( fd ) {
char buf [ 256 ] ;
int len ;
while ( ( len = read ( fd , buf , sizeof ( buf ) ) ) > 0 ) {
2010-03-23 16:52:53 +00:00
if ( fwrite ( buf , len , 1 , ser - > f ) ! = 1 ) {
2009-04-23 20:36:35 +00:00
ast_log ( LOG_WARNING , " fwrite() failed: %s \n " , strerror ( errno ) ) ;
2010-06-15 21:42:33 +00:00
break ;
2009-04-23 20:36:35 +00:00
}
}
}
}
if ( http_header ) {
ast_free ( http_header ) ;
}
if ( out ) {
ast_free ( out ) ;
}
2014-03-28 16:16:53 +00:00
ast_tcptls_close_session_file ( ser ) ;
2009-04-23 20:36:35 +00:00
return ;
}
/* Send http "401 Unauthorized" responce and close socket*/
void ast_http_auth ( struct ast_tcptls_session_instance * ser , const char * realm ,
const unsigned long nonce , const unsigned long opaque , int stale ,
const char * text )
{
struct ast_str * http_headers = ast_str_create ( 128 ) ;
2006-12-16 09:33:31 +00:00
struct ast_str * out = ast_str_create ( 512 ) ;
2008-03-19 15:41:54 +00:00
2009-04-23 20:36:35 +00:00
if ( ! http_headers | | ! out ) {
ast_free ( http_headers ) ;
ast_free ( out ) ;
return ;
2008-03-19 15:41:54 +00:00
}
2009-04-23 20:36:35 +00:00
ast_str_set ( & http_headers , 0 ,
" WWW-authenticate: Digest algorithm=MD5, realm= \" %s \" , nonce= \" %08lx \" , qop= \" auth \" , opaque= \" %08lx \" %s \r \n "
2011-03-01 22:26:37 +00:00
" Content-type: text/html \r \n " ,
2009-04-23 20:36:35 +00:00
realm ? realm : " Asterisk " ,
nonce ,
opaque ,
stale ? " , stale=true " : " " ) ;
2006-12-16 09:33:31 +00:00
ast_str_set ( & out , 0 ,
2009-04-23 20:36:35 +00:00
" <!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN \" > \r \n "
" <html><head> \r \n "
" <title>401 Unauthorized</title> \r \n "
" </head><body> \r \n "
" <h1>401 Unauthorized</h1> \r \n "
" <p>%s</p> \r \n "
" <hr /> \r \n "
" <address>Asterisk Server</address> \r \n "
" </body></html> \r \n " ,
text ? text : " " ) ;
ast_http_send ( ser , AST_HTTP_UNKNOWN , 401 , " Unauthorized " , http_headers , out , 0 , 0 ) ;
return ;
2006-03-25 23:50:09 +00:00
}
2011-03-01 22:26:37 +00:00
/* send http error response and close socket*/
2009-04-23 20:36:35 +00:00
void ast_http_error ( struct ast_tcptls_session_instance * ser , int status_code , const char * status_title , const char * text )
{
struct ast_str * http_headers = ast_str_create ( 40 ) ;
struct ast_str * out = ast_str_create ( 256 ) ;
if ( ! http_headers | | ! out ) {
ast_free ( http_headers ) ;
ast_free ( out ) ;
return ;
}
2011-03-01 22:26:37 +00:00
ast_str_set ( & http_headers , 0 , " Content-type: text/html \r \n " ) ;
2009-04-23 20:36:35 +00:00
ast_str_set ( & out , 0 ,
" <!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN \" > \r \n "
" <html><head> \r \n "
" <title>%d %s</title> \r \n "
" </head><body> \r \n "
" <h1>%s</h1> \r \n "
" <p>%s</p> \r \n "
" <hr /> \r \n "
" <address>Asterisk Server</address> \r \n "
" </body></html> \r \n " ,
status_code , status_title , status_title , text ) ;
ast_http_send ( ser , AST_HTTP_UNKNOWN , status_code , status_title , http_headers , out , 0 , 0 ) ;
return ;
}
/*! \brief
* Link the new uri into the list.
2007-01-24 09:31:26 +00:00
*
* They are sorted by length of
2006-10-16 08:38:35 +00:00
* the string, not alphabetically. Duplicate entries are not replaced,
* but the insertion order (using <= and not just <) makes sure that
* more recent insertions hide older ones.
* On a lookup, we just scan the list and stop at the first matching entry.
*/
2006-03-25 23:50:09 +00:00
int ast_http_uri_link ( struct ast_http_uri * urih )
{
2006-12-23 20:13:14 +00:00
struct ast_http_uri * uri ;
2006-10-16 08:38:35 +00:00
int len = strlen ( urih - > uri ) ;
2006-12-28 20:02:41 +00:00
AST_RWLIST_WRLOCK ( & uris ) ;
2006-12-23 20:13:14 +00:00
2009-04-23 20:36:35 +00:00
if ( AST_RWLIST_EMPTY ( & uris ) | | strlen ( AST_RWLIST_FIRST ( & uris ) - > uri ) < = len ) {
2006-12-28 20:02:41 +00:00
AST_RWLIST_INSERT_HEAD ( & uris , urih , entry ) ;
AST_RWLIST_UNLOCK ( & uris ) ;
2006-12-23 20:13:14 +00:00
return 0 ;
2006-03-25 23:50:09 +00:00
}
2006-12-23 20:13:14 +00:00
2006-12-28 20:02:41 +00:00
AST_RWLIST_TRAVERSE ( & uris , uri , entry ) {
2008-03-19 16:18:29 +00:00
if ( AST_RWLIST_NEXT ( uri , entry ) & &
2009-04-23 20:36:35 +00:00
strlen ( AST_RWLIST_NEXT ( uri , entry ) - > uri ) < = len ) {
2006-12-28 20:02:41 +00:00
AST_RWLIST_INSERT_AFTER ( & uris , uri , urih , entry ) ;
2009-04-23 20:36:35 +00:00
AST_RWLIST_UNLOCK ( & uris ) ;
2008-03-19 15:41:54 +00:00
2006-12-23 20:13:14 +00:00
return 0 ;
}
}
2006-12-28 20:02:41 +00:00
AST_RWLIST_INSERT_TAIL ( & uris , urih , entry ) ;
2006-12-23 20:13:14 +00:00
2006-12-28 20:02:41 +00:00
AST_RWLIST_UNLOCK ( & uris ) ;
2009-04-23 20:36:35 +00:00
2006-03-25 23:50:09 +00:00
return 0 ;
2009-04-23 20:36:35 +00:00
}
2006-03-25 23:50:09 +00:00
void ast_http_uri_unlink ( struct ast_http_uri * urih )
{
2006-12-28 20:02:41 +00:00
AST_RWLIST_WRLOCK ( & uris ) ;
AST_RWLIST_REMOVE ( & uris , urih , entry ) ;
AST_RWLIST_UNLOCK ( & uris ) ;
2006-03-25 23:50:09 +00:00
}
2008-04-02 15:25:48 +00:00
void ast_http_uri_unlink_all_with_key ( const char * key )
2007-04-06 21:16:38 +00:00
{
2008-04-02 15:25:48 +00:00
struct ast_http_uri * urih ;
AST_RWLIST_WRLOCK ( & uris ) ;
AST_RWLIST_TRAVERSE_SAFE_BEGIN ( & uris , urih , entry ) {
if ( ! strcmp ( urih - > key , key ) ) {
AST_RWLIST_REMOVE_CURRENT ( entry ) ;
2012-04-10 19:58:04 +00:00
if ( urih - > dmallocd ) {
ast_free ( urih - > data ) ;
}
if ( urih - > mallocd ) {
ast_free ( urih ) ;
}
2008-08-04 16:34:04 +00:00
}
2007-04-06 21:16:38 +00:00
}
2009-04-10 03:55:27 +00:00
AST_RWLIST_TRAVERSE_SAFE_END ;
2008-07-29 21:23:43 +00:00
AST_RWLIST_UNLOCK ( & uris ) ;
2007-04-06 21:16:38 +00:00
}
2013-03-27 14:38:02 +00:00
# define MAX_POST_CONTENT 1025
2009-04-23 20:36:35 +00:00
/*
* get post variables from client Request Entity-Body, if content type is
* application/x-www-form-urlencoded
*/
struct ast_variable * ast_http_get_post_vars (
struct ast_tcptls_session_instance * ser , struct ast_variable * headers )
{
int content_length = 0 ;
struct ast_variable * v , * post_vars = NULL , * prev = NULL ;
char * buf , * var , * val ;
2013-01-02 15:31:41 +00:00
int res ;
2009-04-23 20:36:35 +00:00
for ( v = headers ; v ; v = v - > next ) {
if ( ! strcasecmp ( v - > name , " Content-Type " ) ) {
if ( strcasecmp ( v - > value , " application/x-www-form-urlencoded " ) ) {
return NULL ;
}
break ;
}
}
for ( v = headers ; v ; v = v - > next ) {
if ( ! strcasecmp ( v - > name , " Content-Length " ) ) {
2013-01-02 15:31:41 +00:00
content_length = atoi ( v - > value ) ;
2009-04-23 20:36:35 +00:00
break ;
}
}
2013-01-02 15:31:41 +00:00
if ( content_length < = 0 ) {
2009-04-23 20:36:35 +00:00
return NULL ;
}
2013-03-27 14:38:02 +00:00
if ( content_length > MAX_POST_CONTENT - 1 ) {
ast_log ( LOG_WARNING , " Excessively long HTTP content. %d is greater than our max of %d \n " ,
content_length , MAX_POST_CONTENT ) ;
ast_http_send ( ser , AST_HTTP_POST , 413 , " Request Entity Too Large " , NULL , NULL , 0 , 0 ) ;
return NULL ;
}
2013-01-02 15:31:41 +00:00
buf = ast_malloc ( content_length + 1 ) ;
if ( ! buf ) {
2009-04-23 20:36:35 +00:00
return NULL ;
}
2013-01-02 15:31:41 +00:00
res = fread ( buf , 1 , content_length , ser - > f ) ;
if ( res < content_length ) {
/* Error, distinguishable by ferror() or feof(), but neither
* is good. */
goto done ;
}
buf [ content_length ] = ' \0 ' ;
2009-04-23 20:36:35 +00:00
while ( ( val = strsep ( & buf , " & " ) ) ) {
var = strsep ( & val , " = " ) ;
if ( val ) {
2011-01-24 18:59:22 +00:00
ast_uri_decode ( val , ast_uri_http_legacy ) ;
2009-04-23 20:36:35 +00:00
} else {
val = " " ;
}
2011-01-24 18:59:22 +00:00
ast_uri_decode ( var , ast_uri_http_legacy ) ;
2009-04-23 20:36:35 +00:00
if ( ( v = ast_variable_new ( var , val , " " ) ) ) {
if ( post_vars ) {
prev - > next = v ;
} else {
post_vars = v ;
}
prev = v ;
}
}
2013-07-21 03:09:59 +00:00
2013-01-02 15:31:41 +00:00
done :
ast_free ( buf ) ;
2009-04-23 20:36:35 +00:00
return post_vars ;
}
static int handle_uri ( struct ast_tcptls_session_instance * ser , char * uri ,
enum ast_http_method method , struct ast_variable * headers )
2006-03-25 23:50:09 +00:00
{
char * c ;
2009-04-23 20:36:35 +00:00
int res = - 1 ;
2006-10-18 04:05:56 +00:00
char * params = uri ;
2008-03-19 16:18:29 +00:00
struct ast_http_uri * urih = NULL ;
2006-10-18 04:05:56 +00:00
int l ;
2009-04-23 20:36:35 +00:00
struct ast_variable * get_vars = NULL , * v , * prev = NULL ;
2006-12-23 20:13:14 +00:00
struct http_uri_redirect * redirect ;
2009-04-23 20:36:35 +00:00
2011-02-22 15:33:56 +00:00
ast_debug ( 2 , " HTTP Request URI is %s \n " , uri ) ;
2011-02-21 14:14:41 +00:00
2009-04-23 20:36:35 +00:00
strsep ( & params , " ? " ) ;
/* Extract arguments from the request and store them in variables. */
if ( params ) {
char * var , * val ;
while ( ( val = strsep ( & params , " & " ) ) ) {
var = strsep ( & val , " = " ) ;
if ( val ) {
2011-01-24 18:59:22 +00:00
ast_uri_decode ( val , ast_uri_http_legacy ) ;
2009-04-23 20:36:35 +00:00
} else {
val = " " ;
}
2011-01-24 18:59:22 +00:00
ast_uri_decode ( var , ast_uri_http_legacy ) ;
2009-04-23 20:36:35 +00:00
if ( ( v = ast_variable_new ( var , val , " " ) ) ) {
if ( get_vars ) {
prev - > next = v ;
2008-03-19 15:41:54 +00:00
} else {
2009-04-23 20:36:35 +00:00
get_vars = v ;
2008-03-19 15:41:54 +00:00
}
2009-04-23 20:36:35 +00:00
prev = v ;
2006-03-25 23:50:09 +00:00
}
}
}
2011-01-24 18:59:22 +00:00
ast_uri_decode ( uri , ast_uri_http_legacy ) ;
2006-10-16 08:38:35 +00:00
2006-12-28 20:05:00 +00:00
AST_RWLIST_RDLOCK ( & uri_redirects ) ;
AST_RWLIST_TRAVERSE ( & uri_redirects , redirect , entry ) {
2006-12-23 20:13:14 +00:00
if ( ! strcasecmp ( uri , redirect - > target ) ) {
2009-04-23 20:36:35 +00:00
struct ast_str * http_header = ast_str_create ( 128 ) ;
ast_str_set ( & http_header , 0 , " Location: %s \r \n " , redirect - > dest ) ;
ast_http_send ( ser , method , 302 , " Moved Temporarily " , http_header , NULL , 0 , 0 ) ;
2008-03-19 15:41:54 +00:00
2006-12-23 20:13:14 +00:00
break ;
}
}
2006-12-28 20:05:00 +00:00
AST_RWLIST_UNLOCK ( & uri_redirects ) ;
2008-03-19 15:41:54 +00:00
if ( redirect ) {
2006-12-23 20:13:14 +00:00
goto cleanup ;
2008-03-19 15:41:54 +00:00
}
2006-12-23 20:13:14 +00:00
2008-03-31 20:45:05 +00:00
/* We want requests to start with the (optional) prefix and '/' */
2008-03-31 21:01:59 +00:00
l = strlen ( prefix ) ;
if ( ! strncasecmp ( uri , prefix , l ) & & uri [ l ] = = ' / ' ) {
2006-10-18 04:05:56 +00:00
uri + = l + 1 ;
2006-10-16 08:38:35 +00:00
/* scan registered uris to see if we match one. */
2006-12-28 20:02:41 +00:00
AST_RWLIST_RDLOCK ( & uris ) ;
AST_RWLIST_TRAVERSE ( & uris , urih , entry ) {
2008-05-20 16:39:39 +00:00
ast_debug ( 2 , " match request [%s] with handler [%s] len %d \n " , uri , urih - > uri , l ) ;
2006-10-18 04:05:56 +00:00
l = strlen ( urih - > uri ) ;
c = uri + l ; /* candidate */
2009-04-23 20:36:35 +00:00
if ( strncasecmp ( urih - > uri , uri , l ) /* no match */
| | ( * c & & * c ! = ' / ' ) ) { /* substring */
2006-10-18 04:05:56 +00:00
continue ;
2008-03-19 15:41:54 +00:00
}
if ( * c = = ' / ' ) {
2006-10-18 04:05:56 +00:00
c + + ;
2008-03-19 15:41:54 +00:00
}
2006-10-18 04:05:56 +00:00
if ( ! * c | | urih - > has_subtree ) {
2009-04-23 20:36:35 +00:00
uri = c ;
break ;
2006-03-25 23:50:09 +00:00
}
}
2006-12-28 20:02:41 +00:00
AST_RWLIST_UNLOCK ( & uris ) ;
2009-04-23 20:36:35 +00:00
}
if ( urih ) {
res = urih - > callback ( ser , urih , uri , method , get_vars , headers ) ;
2006-03-25 23:50:09 +00:00
} else {
2009-04-23 20:36:35 +00:00
ast_http_error ( ser , 404 , " Not Found " , " The requested URL was not found on this server. " ) ;
2006-03-25 23:50:09 +00:00
}
2006-12-23 20:13:14 +00:00
cleanup :
2009-04-23 20:36:35 +00:00
ast_variables_destroy ( get_vars ) ;
return res ;
2006-03-25 23:50:09 +00:00
}
2006-10-22 12:02:35 +00:00
# ifdef DO_SSL
2006-10-22 19:09:25 +00:00
# if defined(HAVE_FUNOPEN)
# define HOOK_T int
# define LEN_T int
# else
# define HOOK_T ssize_t
# define LEN_T size_t
# endif
2008-03-19 15:41:54 +00:00
2006-10-22 12:02:35 +00:00
/*!
* replacement read/write functions for SSL support.
* We use wrappers rather than SSL_read/SSL_write directly so
* we can put in some debugging.
*/
2008-01-18 22:04:33 +00:00
/*static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
2006-10-22 12:02:35 +00:00
{
2006-10-22 19:09:25 +00:00
int i = SSL_read(cookie, buf, len-1);
2006-10-22 12:02:35 +00:00
#if 0
if (i >= 0)
buf[i] = '\0';
2006-10-22 19:09:25 +00:00
ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
2006-10-22 12:02:35 +00:00
#endif
return i;
}
2006-10-22 19:09:25 +00:00
static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
2006-10-22 12:02:35 +00:00
{
#if 0
2012-07-31 20:21:43 +00:00
char *s = ast_alloca(len+1);
2006-10-22 12:02:35 +00:00
strncpy(s, buf, len);
s[len] = '\0';
2006-10-22 19:09:25 +00:00
ast_verbose("ssl write size %d <%s>\n", (int)len, s);
2006-10-22 12:02:35 +00:00
#endif
return SSL_write(cookie, buf, len);
}
static int ssl_close(void *cookie)
{
close(SSL_get_fd(cookie));
SSL_shutdown(cookie);
SSL_free(cookie);
return 0;
2008-01-18 22:04:33 +00:00
}*/
2006-10-22 19:09:25 +00:00
# endif /* DO_SSL */
2006-10-22 12:02:35 +00:00
2014-03-10 17:09:42 +00:00
static struct ast_variable * parse_cookies ( const char * cookies )
2008-04-23 22:53:20 +00:00
{
2014-03-10 17:09:42 +00:00
char * parse = ast_strdupa ( cookies ) ;
2008-04-23 22:53:20 +00:00
char * cur ;
struct ast_variable * vars = NULL , * var ;
2014-03-10 17:09:42 +00:00
while ( ( cur = strsep ( & parse , " ; " ) ) ) {
2008-04-23 22:53:20 +00:00
char * name , * val ;
2009-04-23 20:36:35 +00:00
2008-04-23 22:53:20 +00:00
name = val = cur ;
strsep ( & val , " = " ) ;
if ( ast_strlen_zero ( name ) | | ast_strlen_zero ( val ) ) {
continue ;
}
name = ast_strip ( name ) ;
val = ast_strip_quoted ( val , " \" " , " \" " ) ;
if ( ast_strlen_zero ( name ) | | ast_strlen_zero ( val ) ) {
continue ;
}
2011-02-22 15:33:56 +00:00
ast_debug ( 1 , " HTTP Cookie, Name: '%s' Value: '%s' \n " , name , val ) ;
2008-04-23 22:53:20 +00:00
var = ast_variable_new ( name , val , __FILE__ ) ;
var - > next = vars ;
vars = var ;
}
return vars ;
}
2009-04-23 20:36:35 +00:00
/* get cookie from Request headers */
struct ast_variable * ast_http_get_cookies ( struct ast_variable * headers )
{
2014-03-10 17:09:42 +00:00
struct ast_variable * v , * cookies = NULL ;
2009-04-23 20:36:35 +00:00
for ( v = headers ; v ; v = v - > next ) {
2013-07-21 03:09:59 +00:00
if ( ! strcasecmp ( v - > name , " Cookie " ) ) {
2014-03-10 17:09:42 +00:00
ast_variables_destroy ( cookies ) ;
cookies = parse_cookies ( v - > value ) ;
2009-04-23 20:36:35 +00:00
}
}
return cookies ;
}
2014-03-10 17:09:42 +00:00
/*! Limit the number of request headers in case the sender is being ridiculous. */
# define MAX_HTTP_REQUEST_HEADERS 100
2009-04-23 20:36:35 +00:00
2006-11-27 19:19:48 +00:00
static void * httpd_helper_thread ( void * data )
{
char buf [ 4096 ] ;
2009-04-23 20:36:35 +00:00
char header_line [ 4096 ] ;
2008-03-12 22:13:18 +00:00
struct ast_tcptls_session_instance * ser = data ;
2009-04-23 20:36:35 +00:00
struct ast_variable * headers = NULL ;
2008-08-01 21:56:07 +00:00
struct ast_variable * tail = headers ;
2009-04-23 20:36:35 +00:00
char * uri , * method ;
enum ast_http_method http_method = AST_HTTP_UNKNOWN ;
2014-03-10 17:09:42 +00:00
int remaining_headers ;
2014-06-12 16:22:19 +00:00
int flags ;
2014-04-21 16:13:36 +00:00
struct protoent * p ;
2006-10-22 12:02:35 +00:00
2011-04-21 18:32:50 +00:00
if ( ast_atomic_fetchadd_int ( & session_count , + 1 ) > = session_limit ) {
goto done ;
}
2014-04-21 16:13:36 +00:00
/* here we set TCP_NODELAY on the socket to disable Nagle's algorithm.
* This is necessary to prevent delays (caused by buffering) as we
* write to the socket in bits and pieces. */
p = getprotobyname ( " tcp " ) ;
if ( p ) {
int arg = 1 ;
if ( setsockopt ( ser - > fd , p - > p_proto , TCP_NODELAY , ( char * ) & arg , sizeof ( arg ) ) < 0 ) {
ast_log ( LOG_WARNING , " Failed to set TCP_NODELAY on HTTP connection: %s \n " , strerror ( errno ) ) ;
ast_log ( LOG_WARNING , " Some HTTP requests may be slow to respond. \n " ) ;
}
} else {
ast_log ( LOG_WARNING , " Failed to set TCP_NODELAY on HTTP connection, getprotobyname( \" tcp \" ) failed \n " ) ;
ast_log ( LOG_WARNING , " Some HTTP requests may be slow to respond. \n " ) ;
}
2014-06-12 16:22:19 +00:00
/* make sure socket is non-blocking */
flags = fcntl ( ser - > fd , F_GETFL ) ;
flags | = O_NONBLOCK ;
fcntl ( ser - > fd , F_SETFL , flags ) ;
2014-06-13 05:06:02 +00:00
/* We can let the stream wait for data to arrive. */
ast_tcptls_stream_set_exclusive_input ( ser - > stream_cookie , 1 ) ;
2014-06-12 16:22:19 +00:00
ast_tcptls_stream_set_timeout_inactivity ( ser - > stream_cookie , session_inactivity ) ;
if ( ! fgets ( buf , sizeof ( buf ) , ser - > f ) | | feof ( ser - > f ) ) {
2006-10-18 04:05:56 +00:00
goto done ;
2008-03-19 15:41:54 +00:00
}
2006-03-25 23:50:09 +00:00
2009-04-23 20:36:35 +00:00
/* Get method */
method = ast_skip_blanks ( buf ) ;
uri = ast_skip_nonblanks ( method ) ;
2008-03-19 15:41:54 +00:00
if ( * uri ) {
2006-10-18 04:05:56 +00:00
* uri + + = ' \0 ' ;
2008-03-19 15:41:54 +00:00
}
2006-03-25 23:50:09 +00:00
2009-04-23 20:36:35 +00:00
if ( ! strcasecmp ( method , " GET " ) ) {
http_method = AST_HTTP_GET ;
} else if ( ! strcasecmp ( method , " POST " ) ) {
http_method = AST_HTTP_POST ;
} else if ( ! strcasecmp ( method , " HEAD " ) ) {
http_method = AST_HTTP_HEAD ;
} else if ( ! strcasecmp ( method , " PUT " ) ) {
http_method = AST_HTTP_PUT ;
}
2006-10-18 04:05:56 +00:00
uri = ast_skip_blanks ( uri ) ; /* Skip white space */
2006-04-01 08:49:54 +00:00
2006-10-18 04:05:56 +00:00
if ( * uri ) { /* terminate at the first blank */
2006-12-16 09:33:31 +00:00
char * c = ast_skip_nonblanks ( uri ) ;
2008-03-19 15:41:54 +00:00
if ( * c ) {
2006-10-18 04:05:56 +00:00
* c = ' \0 ' ;
2008-03-19 15:41:54 +00:00
}
2014-03-10 17:09:42 +00:00
} else {
ast_http_error ( ser , 400 , " Bad Request " , " Invalid Request " ) ;
goto done ;
2006-10-18 04:05:56 +00:00
}
2009-04-23 20:36:35 +00:00
/* process "Request Headers" lines */
2014-03-10 17:09:42 +00:00
remaining_headers = MAX_HTTP_REQUEST_HEADERS ;
2014-06-12 16:22:19 +00:00
for ( ; ; ) {
char * name ;
char * value ;
if ( ! fgets ( header_line , sizeof ( header_line ) , ser - > f ) | | feof ( ser - > f ) ) {
ast_http_error ( ser , 400 , " Bad Request " , " Timeout " ) ;
goto done ;
}
2009-04-23 20:36:35 +00:00
2006-10-18 04:05:56 +00:00
/* Trim trailing characters */
2009-04-23 20:36:35 +00:00
ast_trim_blanks ( header_line ) ;
if ( ast_strlen_zero ( header_line ) ) {
2014-06-12 16:22:19 +00:00
/* A blank line ends the request header section. */
2006-10-18 04:05:56 +00:00
break ;
2008-03-19 15:41:54 +00:00
}
2008-08-01 21:56:07 +00:00
2009-04-23 20:36:35 +00:00
value = header_line ;
name = strsep ( & value , " : " ) ;
if ( ! value ) {
continue ;
}
2008-08-01 21:56:07 +00:00
2009-04-23 20:36:35 +00:00
value = ast_skip_blanks ( value ) ;
if ( ast_strlen_zero ( value ) | | ast_strlen_zero ( name ) ) {
continue ;
}
ast_trim_blanks ( name ) ;
2014-03-10 17:09:42 +00:00
if ( ! remaining_headers - - ) {
/* Too many headers. */
ast_http_error ( ser , 413 , " Request Entity Too Large " , " Too many headers " ) ;
goto done ;
}
2009-04-23 20:36:35 +00:00
if ( ! headers ) {
headers = ast_variable_new ( name , value , __FILE__ ) ;
tail = headers ;
} else {
tail - > next = ast_variable_new ( name , value , __FILE__ ) ;
tail = tail - > next ;
2006-03-25 23:50:09 +00:00
}
2014-03-10 17:09:42 +00:00
if ( ! tail ) {
/*
* Variable allocation failure.
* Try to make some room.
*/
ast_variables_destroy ( headers ) ;
headers = NULL ;
ast_http_error ( ser , 500 , " Server Error " , " Out of memory " ) ;
goto done ;
}
2008-03-17 22:10:06 +00:00
}
2006-10-18 04:05:56 +00:00
2009-04-23 20:36:35 +00:00
handle_uri ( ser , uri , http_method , headers ) ;
2011-04-21 18:32:50 +00:00
done :
ast_atomic_fetchadd_int ( & session_count , - 1 ) ;
/* clean up all the header information */
2014-03-10 17:09:42 +00:00
ast_variables_destroy ( headers ) ;
2006-10-18 04:05:56 +00:00
2009-04-23 20:36:35 +00:00
if ( ser - > f ) {
2014-06-12 16:22:19 +00:00
ast_tcptls_close_session_file ( ser ) ;
2009-04-23 20:36:35 +00:00
}
2008-06-17 21:46:57 +00:00
ao2_ref ( ser , - 1 ) ;
ser = NULL ;
2006-03-25 23:50:09 +00:00
return NULL ;
}
2006-12-23 20:13:14 +00:00
/*!
* \brief Add a new URI redirect
* The entries in the redirect list are sorted by length, just like the list
* of URI handlers.
*/
static void add_redirect ( const char * value )
{
char * target , * dest ;
struct http_uri_redirect * redirect , * cur ;
2006-12-24 03:29:42 +00:00
unsigned int target_len ;
unsigned int total_len ;
2006-12-23 20:13:14 +00:00
dest = ast_strdupa ( value ) ;
2006-12-24 21:01:02 +00:00
dest = ast_skip_blanks ( dest ) ;
target = strsep ( & dest , " " ) ;
target = ast_skip_blanks ( target ) ;
target = strsep ( & target , " " ) ; /* trim trailing whitespace */
2006-12-23 20:13:14 +00:00
if ( ! dest ) {
ast_log ( LOG_WARNING , " Invalid redirect '%s' \n " , value ) ;
return ;
}
2006-12-24 03:29:42 +00:00
target_len = strlen ( target ) + 1 ;
total_len = sizeof ( * redirect ) + target_len + strlen ( dest ) + 1 ;
2006-12-23 20:13:14 +00:00
2008-03-19 15:41:54 +00:00
if ( ! ( redirect = ast_calloc ( 1 , total_len ) ) ) {
2006-12-23 20:13:14 +00:00
return ;
2008-03-19 15:41:54 +00:00
}
2006-12-24 03:29:42 +00:00
redirect - > dest = redirect - > target + target_len ;
strcpy ( redirect - > target , target ) ;
strcpy ( redirect - > dest , dest ) ;
2006-12-23 20:13:14 +00:00
2006-12-28 20:05:00 +00:00
AST_RWLIST_WRLOCK ( & uri_redirects ) ;
2006-12-23 20:13:14 +00:00
2006-12-24 03:29:42 +00:00
target_len - - ; /* So we can compare directly with strlen() */
2009-04-23 20:36:35 +00:00
if ( AST_RWLIST_EMPTY ( & uri_redirects )
| | strlen ( AST_RWLIST_FIRST ( & uri_redirects ) - > target ) < = target_len ) {
2006-12-28 20:05:00 +00:00
AST_RWLIST_INSERT_HEAD ( & uri_redirects , redirect , entry ) ;
AST_RWLIST_UNLOCK ( & uri_redirects ) ;
2008-03-19 15:41:54 +00:00
2006-12-23 20:13:14 +00:00
return ;
}
2006-12-28 20:05:00 +00:00
AST_RWLIST_TRAVERSE ( & uri_redirects , cur , entry ) {
2009-04-23 20:36:35 +00:00
if ( AST_RWLIST_NEXT ( cur , entry )
& & strlen ( AST_RWLIST_NEXT ( cur , entry ) - > target ) < = target_len ) {
2006-12-28 20:05:00 +00:00
AST_RWLIST_INSERT_AFTER ( & uri_redirects , cur , redirect , entry ) ;
2009-04-23 20:36:35 +00:00
AST_RWLIST_UNLOCK ( & uri_redirects ) ;
2006-12-23 20:13:14 +00:00
return ;
}
}
2006-12-28 20:05:00 +00:00
AST_RWLIST_INSERT_TAIL ( & uri_redirects , redirect , entry ) ;
2006-12-23 20:13:14 +00:00
2006-12-28 20:05:00 +00:00
AST_RWLIST_UNLOCK ( & uri_redirects ) ;
2006-12-23 20:13:14 +00:00
}
2006-03-25 23:50:09 +00:00
static int __ast_http_load ( int reload )
{
struct ast_config * cfg ;
struct ast_variable * v ;
int enabled = 0 ;
2006-04-01 08:49:54 +00:00
int newenablestatic = 0 ;
2008-03-12 22:49:26 +00:00
char newprefix [ MAX_PREFIX ] = " " ;
2006-12-23 20:13:14 +00:00
struct http_uri_redirect * redirect ;
2007-08-16 21:09:46 +00:00
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 } ;
2010-10-29 20:46:06 +00:00
uint32_t bindport = DEFAULT_PORT ;
2013-06-25 01:07:29 +00:00
RAII_VAR ( struct ast_sockaddr * , addrs , NULL , ast_free ) ;
2010-10-29 20:46:06 +00:00
int num_addrs = 0 ;
2012-02-02 18:55:05 +00:00
int http_tls_was_enabled = 0 ;
2007-08-16 21:09:46 +00:00
2008-09-12 23:30:03 +00:00
cfg = ast_config_load2 ( " http.conf " , " http " , config_flags ) ;
if ( cfg = = CONFIG_STATUS_FILEMISSING | | cfg = = CONFIG_STATUS_FILEUNCHANGED | | cfg = = CONFIG_STATUS_FILEINVALID ) {
2007-08-16 21:09:46 +00:00
return 0 ;
2008-03-19 15:41:54 +00:00
}
2006-04-03 18:38:28 +00:00
2012-02-02 18:55:05 +00:00
http_tls_was_enabled = ( reload & & http_tls_cfg . enabled ) ;
2006-12-07 16:42:29 +00:00
http_tls_cfg . enabled = 0 ;
2008-03-19 15:41:54 +00:00
if ( http_tls_cfg . certfile ) {
2007-06-06 21:20:11 +00:00
ast_free ( http_tls_cfg . certfile ) ;
2008-03-19 15:41:54 +00:00
}
2006-12-07 16:42:29 +00:00
http_tls_cfg . certfile = ast_strdup ( AST_CERTFILE ) ;
2009-04-24 21:22:31 +00:00
if ( http_tls_cfg . pvtfile ) {
ast_free ( http_tls_cfg . pvtfile ) ;
}
http_tls_cfg . pvtfile = ast_strdup ( " " ) ;
2008-03-19 15:41:54 +00:00
if ( http_tls_cfg . cipher ) {
2007-06-06 21:20:11 +00:00
ast_free ( http_tls_cfg . cipher ) ;
2008-03-19 15:41:54 +00:00
}
2006-12-07 16:42:29 +00:00
http_tls_cfg . cipher = ast_strdup ( " " ) ;
2006-10-22 12:02:35 +00:00
2006-12-28 20:05:00 +00:00
AST_RWLIST_WRLOCK ( & uri_redirects ) ;
2008-03-19 15:41:54 +00:00
while ( ( redirect = AST_RWLIST_REMOVE_HEAD ( & uri_redirects , entry ) ) ) {
2007-06-06 21:20:11 +00:00
ast_free ( redirect ) ;
2008-03-19 15:41:54 +00:00
}
2006-12-28 20:05:00 +00:00
AST_RWLIST_UNLOCK ( & uri_redirects ) ;
2006-12-23 20:13:14 +00:00
2012-02-02 18:55:05 +00:00
ast_sockaddr_setnull ( & https_desc . local_address ) ;
2014-06-12 16:22:19 +00:00
session_limit = DEFAULT_SESSION_LIMIT ;
session_inactivity = DEFAULT_SESSION_INACTIVITY ;
2006-03-25 23:50:09 +00:00
if ( cfg ) {
v = ast_variable_browse ( cfg , " general " ) ;
2006-12-23 20:13:14 +00:00
for ( ; v ; v = v - > next ) {
2009-04-29 14:39:48 +00:00
2013-03-15 12:51:34 +00:00
/* read tls config options while preventing unsupported options from being set */
if ( strcasecmp ( v - > name , " tlscafile " )
& & strcasecmp ( v - > name , " tlscapath " )
& & strcasecmp ( v - > name , " tlscadir " )
& & strcasecmp ( v - > name , " tlsverifyclient " )
& & strcasecmp ( v - > name , " tlsdontverifyserver " )
& & strcasecmp ( v - > name , " tlsclientmethod " )
& & strcasecmp ( v - > name , " sslclientmethod " )
& & strcasecmp ( v - > name , " tlscipher " )
& & strcasecmp ( v - > name , " sslcipher " )
& & ! ast_tls_read_conf ( & http_tls_cfg , & https_desc , v - > name , v - > value ) ) {
2009-04-29 14:39:48 +00:00
continue ;
}
2008-03-19 15:41:54 +00:00
if ( ! strcasecmp ( v - > name , " enabled " ) ) {
2006-03-25 23:50:09 +00:00
enabled = ast_true ( v - > value ) ;
2008-03-19 15:41:54 +00:00
} else if ( ! strcasecmp ( v - > name , " enablestatic " ) ) {
2006-04-01 08:49:54 +00:00
newenablestatic = ast_true ( v - > value ) ;
2008-03-19 15:41:54 +00:00
} else if ( ! strcasecmp ( v - > name , " bindport " ) ) {
2010-10-29 20:46:06 +00:00
if ( ast_parse_arg ( v - > value , PARSE_UINT32 | PARSE_IN_RANGE | PARSE_DEFAULT , & bindport , DEFAULT_PORT , 0 , 65535 ) ) {
ast_log ( LOG_WARNING , " Invalid port %s specified. Using default port % " PRId32 , v - > value , DEFAULT_PORT ) ;
}
2006-11-27 20:21:40 +00:00
} else if ( ! strcasecmp ( v - > name , " bindaddr " ) ) {
2010-10-29 20:46:06 +00:00
if ( ! ( num_addrs = ast_sockaddr_resolve ( & addrs , v - > value , 0 , AST_AF_UNSPEC ) ) ) {
ast_log ( LOG_WARNING , " Invalid bind address %s \n " , v - > value ) ;
2006-03-25 23:50:09 +00:00
}
2006-04-02 09:10:43 +00:00
} else if ( ! strcasecmp ( v - > name , " prefix " ) ) {
if ( ! ast_strlen_zero ( v - > value ) ) {
newprefix [ 0 ] = ' / ' ;
ast_copy_string ( newprefix + 1 , v - > value , sizeof ( newprefix ) - 1 ) ;
} else {
newprefix [ 0 ] = ' \0 ' ;
}
2006-12-23 20:13:14 +00:00
} else if ( ! strcasecmp ( v - > name , " redirect " ) ) {
add_redirect ( v - > value ) ;
2011-04-21 18:32:50 +00:00
} else if ( ! strcasecmp ( v - > name , " sessionlimit " ) ) {
if ( ast_parse_arg ( v - > value , PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE ,
& session_limit , DEFAULT_SESSION_LIMIT , 1 , INT_MAX ) ) {
ast_log ( LOG_WARNING , " Invalid %s '%s' at line %d of http.conf \n " ,
v - > name , v - > value , v - > lineno ) ;
}
2014-06-12 16:22:19 +00:00
} else if ( ! strcasecmp ( v - > name , " session_inactivity " ) ) {
if ( ast_parse_arg ( v - > value , PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE ,
& session_inactivity , DEFAULT_SESSION_INACTIVITY , 1 , INT_MAX ) ) {
ast_log ( LOG_WARNING , " Invalid %s '%s' at line %d of http.conf \n " ,
v - > name , v - > value , v - > lineno ) ;
}
2006-12-23 20:13:14 +00:00
} else {
ast_log ( LOG_WARNING , " Ignoring unknown option '%s' in http.conf \n " , v - > name ) ;
2006-04-02 09:10:43 +00:00
}
2006-03-25 23:50:09 +00:00
}
2007-04-06 21:16:38 +00:00
2006-03-25 23:50:09 +00:00
ast_config_destroy ( cfg ) ;
}
2010-10-29 20:46:06 +00:00
2008-03-19 15:41:54 +00:00
if ( strcmp ( prefix , newprefix ) ) {
2006-03-25 23:50:09 +00:00
ast_copy_string ( prefix , newprefix , sizeof ( prefix ) ) ;
2008-03-19 15:41:54 +00:00
}
2006-04-01 08:49:54 +00:00
enablestatic = newenablestatic ;
2010-10-29 20:46:06 +00:00
if ( num_addrs & & enabled ) {
int i ;
for ( i = 0 ; i < num_addrs ; + + i ) {
ast_sockaddr_copy ( & http_desc . local_address , & addrs [ i ] ) ;
if ( ! ast_sockaddr_port ( & http_desc . local_address ) ) {
ast_sockaddr_set_port ( & http_desc . local_address , bindport ) ;
}
ast_tcptls_server_start ( & http_desc ) ;
if ( http_desc . accept_fd = = - 1 ) {
ast_log ( LOG_WARNING , " Failed to start HTTP server for address %s \n " , ast_sockaddr_stringify ( & addrs [ i ] ) ) ;
ast_sockaddr_setnull ( & http_desc . local_address ) ;
} else {
ast_verb ( 1 , " Bound HTTP server to address %s \n " , ast_sockaddr_stringify ( & addrs [ i ] ) ) ;
break ;
}
}
/* When no specific TLS bindaddr is specified, we just use
* the non-TLS bindaddress here.
*/
if ( ast_sockaddr_isnull ( & https_desc . local_address ) & & http_desc . accept_fd ! = - 1 ) {
ast_sockaddr_copy ( & https_desc . local_address , & https_desc . local_address ) ;
/* Of course, we can't use the same port though.
* Since no bind address was specified, we just use the
* default TLS port
*/
ast_sockaddr_set_port ( & https_desc . local_address , DEFAULT_TLS_PORT ) ;
}
}
2012-02-02 18:55:05 +00:00
if ( http_tls_was_enabled & & ! http_tls_cfg . enabled ) {
ast_tcptls_server_stop ( & https_desc ) ;
} else if ( http_tls_cfg . enabled & & ! ast_sockaddr_isnull ( & https_desc . local_address ) ) {
2010-10-29 20:46:06 +00:00
/* We can get here either because a TLS-specific address was specified
* or because we copied the non-TLS address here. In the case where
* we read an explicit address from the config, there may have been
* no port specified, so we'll just use the default TLS port.
*/
if ( ! ast_sockaddr_port ( & https_desc . local_address ) ) {
ast_sockaddr_set_port ( & https_desc . local_address , DEFAULT_TLS_PORT ) ;
}
if ( ast_ssl_setup ( https_desc . tls_cfg ) ) {
ast_tcptls_server_start ( & https_desc ) ;
}
2008-03-19 15:41:54 +00:00
}
2007-04-06 21:16:38 +00:00
2006-03-25 23:50:09 +00:00
return 0 ;
}
2007-09-18 22:43:45 +00:00
static char * handle_show_http ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2006-03-25 23:50:09 +00:00
{
struct ast_http_uri * urih ;
2006-12-23 20:13:14 +00:00
struct http_uri_redirect * redirect ;
2008-03-17 22:10:06 +00:00
2007-09-18 22:43:45 +00:00
switch ( cmd ) {
case CLI_INIT :
e - > command = " http show status " ;
2009-04-23 20:36:35 +00:00
e - > usage =
2007-09-18 22:43:45 +00:00
" Usage: http show status \n "
" Lists status of internal HTTP engine \n " ;
return NULL ;
case CLI_GENERATE :
return NULL ;
}
2009-04-23 20:36:35 +00:00
2008-03-19 15:41:54 +00:00
if ( a - > argc ! = 3 ) {
2007-09-18 22:43:45 +00:00
return CLI_SHOWUSAGE ;
2008-03-19 15:41:54 +00:00
}
2007-09-18 22:43:45 +00:00
ast_cli ( a - > fd , " HTTP Server Status: \n " ) ;
ast_cli ( a - > fd , " Prefix: %s \n " , prefix ) ;
2010-10-29 20:46:06 +00:00
if ( ast_sockaddr_isnull ( & http_desc . old_address ) ) {
2007-09-18 22:43:45 +00:00
ast_cli ( a - > fd , " Server Disabled \n \n " ) ;
2008-03-19 15:41:54 +00:00
} else {
2010-10-29 20:46:06 +00:00
ast_cli ( a - > fd , " Server Enabled and Bound to %s \n \n " ,
ast_sockaddr_stringify ( & http_desc . old_address ) ) ;
2008-03-19 15:41:54 +00:00
if ( http_tls_cfg . enabled ) {
2010-10-29 20:46:06 +00:00
ast_cli ( a - > fd , " HTTPS Server Enabled and Bound to %s \n \n " ,
ast_sockaddr_stringify ( & https_desc . old_address ) ) ;
2008-03-19 15:41:54 +00:00
}
2006-10-22 12:02:35 +00:00
}
2006-12-23 20:13:14 +00:00
2007-09-18 22:43:45 +00:00
ast_cli ( a - > fd , " Enabled URI's: \n " ) ;
2006-12-28 20:02:41 +00:00
AST_RWLIST_RDLOCK ( & uris ) ;
if ( AST_RWLIST_EMPTY ( & uris ) ) {
2007-09-18 22:43:45 +00:00
ast_cli ( a - > fd , " None. \n " ) ;
2006-12-28 20:02:41 +00:00
} else {
2009-04-23 20:36:35 +00:00
AST_RWLIST_TRAVERSE ( & uris , urih , entry )
ast_cli ( a - > fd , " %s/%s%s => %s \n " , prefix , urih - > uri , ( urih - > has_subtree ? " /... " : " " ) , urih - > description ) ;
2006-12-28 20:02:41 +00:00
}
AST_RWLIST_UNLOCK ( & uris ) ;
2006-12-23 20:13:14 +00:00
2007-09-18 22:43:45 +00:00
ast_cli ( a - > fd , " \n Enabled Redirects: \n " ) ;
2006-12-28 20:05:00 +00:00
AST_RWLIST_RDLOCK ( & uri_redirects ) ;
2009-04-23 20:36:35 +00:00
AST_RWLIST_TRAVERSE ( & uri_redirects , redirect , entry )
2007-09-18 22:43:45 +00:00
ast_cli ( a - > fd , " %s => %s \n " , redirect - > target , redirect - > dest ) ;
2008-03-19 15:41:54 +00:00
if ( AST_RWLIST_EMPTY ( & uri_redirects ) ) {
2007-09-18 22:43:45 +00:00
ast_cli ( a - > fd , " None. \n " ) ;
2008-03-19 15:41:54 +00:00
}
2006-12-28 20:05:00 +00:00
AST_RWLIST_UNLOCK ( & uri_redirects ) ;
2006-12-23 20:13:14 +00:00
2007-09-18 22:43:45 +00:00
return CLI_SUCCESS ;
2006-03-25 23:50:09 +00:00
}
int ast_http_reload ( void )
{
return __ast_http_load ( 1 ) ;
}
2006-09-18 19:54:18 +00:00
static struct ast_cli_entry cli_http [ ] = {
2007-10-22 20:05:18 +00:00
AST_CLI_DEFINE ( handle_show_http , " Display HTTP server status " ) ,
2006-03-25 23:50:09 +00:00
} ;
2012-12-11 22:01:13 +00:00
static void http_shutdown ( void )
{
2013-08-21 17:07:06 +00:00
struct http_uri_redirect * redirect ;
2012-12-11 22:01:13 +00:00
ast_cli_unregister_multiple ( cli_http , ARRAY_LEN ( cli_http ) ) ;
2013-08-21 17:07:06 +00:00
ast_tcptls_server_stop ( & http_desc ) ;
if ( http_tls_cfg . enabled ) {
ast_tcptls_server_stop ( & https_desc ) ;
}
ast_free ( http_tls_cfg . certfile ) ;
ast_free ( http_tls_cfg . pvtfile ) ;
ast_free ( http_tls_cfg . cipher ) ;
ast_http_uri_unlink ( & statusuri ) ;
ast_http_uri_unlink ( & staticuri ) ;
AST_RWLIST_WRLOCK ( & uri_redirects ) ;
while ( ( redirect = AST_RWLIST_REMOVE_HEAD ( & uri_redirects , entry ) ) ) {
ast_free ( redirect ) ;
}
AST_RWLIST_UNLOCK ( & uri_redirects ) ;
2012-12-11 22:01:13 +00:00
}
2006-03-25 23:50:09 +00:00
int ast_http_init ( void )
{
ast_http_uri_link ( & statusuri ) ;
2006-04-01 08:49:54 +00:00
ast_http_uri_link ( & staticuri ) ;
2008-12-05 10:31:25 +00:00
ast_cli_register_multiple ( cli_http , ARRAY_LEN ( cli_http ) ) ;
2012-12-11 22:01:13 +00:00
ast_register_atexit ( http_shutdown ) ;
2007-04-06 21:16:38 +00:00
2006-03-25 23:50:09 +00:00
return __ast_http_load ( 0 ) ;
}