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
*
2012-09-21 17:14:59 +00:00
* GMime http://spruce.sourceforge.net/gmime/
2009-11-02 20:43:52 +00:00
*
2006-04-03 18:38:28 +00:00
* \ref AstHTTP - AMI over the http protocol
2006-03-26 16:48:47 +00:00
*/
2012-10-18 14:17:40 +00:00
/*! \li \ref http.c uses the configuration file \ref http.conf
2012-10-01 23:39:45 +00:00
* \addtogroup configuration_file
*/
/*! \page http.conf http.conf
* \verbinclude http.conf.sample
*/
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"
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>
2016-06-03 08:57:02 +03:00
# include <signal.h>
2006-03-25 23:50:09 +00:00
# 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"
2018-02-16 22:11:42 -05:00
# include "asterisk/module.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"
2013-11-27 15:48:39 +00:00
# include "asterisk/json.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-07-03 17:16:55 +00:00
/*! (ms) Idle time waiting for data. */
# define DEFAULT_SESSION_INACTIVITY 30000
/*! (ms) Min timeout for initial HTTP request to start coming in. */
# define MIN_INITIAL_REQUEST_TIMEOUT 10000
/*! (ms) Idle time between HTTP requests */
# define DEFAULT_SESSION_KEEP_ALIVE 15000
2015-01-30 17:21:50 +00:00
/*! Max size for the http server name */
# define MAX_SERVER_NAME_LENGTH 128
/*! Max size for the http response header */
# define DEFAULT_RESPONSE_HEADER_LENGTH 512
2014-07-03 17:16:55 +00:00
/*! Maximum application/json or application/x-www-form-urlencoded body content length. */
# if !defined(LOW_MEMORY)
2019-01-09 10:27:03 +00:00
# define MAX_CONTENT_LENGTH 40960
2014-07-03 17:16:55 +00:00
# else
# define MAX_CONTENT_LENGTH 1024
# endif /* !defined(LOW_MEMORY) */
2019-01-09 10:27:03 +00:00
/*! Initial response body length. */
# if !defined(LOW_MEMORY)
# define INITIAL_RESPONSE_BODY_BUFFER 1024
# else
# define INITIAL_RESPONSE_BODY_BUFFER 512
# endif /* !defined(LOW_MEMORY) */
2014-07-03 17:16:55 +00:00
/*! Maximum line length for HTTP requests. */
# if !defined(LOW_MEMORY)
# define MAX_HTTP_LINE_LENGTH 4096
# else
# define MAX_HTTP_LINE_LENGTH 1024
# endif /* !defined(LOW_MEMORY) */
2006-03-25 23:50:09 +00:00
2015-01-30 17:21:50 +00:00
static char http_server_name [ MAX_SERVER_NAME_LENGTH ] ;
2011-04-21 18:32:50 +00:00
static int session_limit = DEFAULT_SESSION_LIMIT ;
2014-06-12 17:00:08 +00:00
static int session_inactivity = DEFAULT_SESSION_INACTIVITY ;
2014-07-03 17:16:55 +00:00
static int session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE ;
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 " } ,
2013-04-22 14:58:53 +00:00
{ AST_HTTP_DELETE , " DELETE " } ,
{ AST_HTTP_OPTIONS , " OPTIONS " } ,
2009-04-23 20:36:35 +00:00
} ;
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:21:01 +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 " ) ;
2014-07-03 17:16:55 +00:00
return 0 ;
2009-04-23 20:36:35 +00:00
}
/* 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:52:16 +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:52:16 +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:52:16 +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:52:16 +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
2014-07-03 17:16:55 +00:00
http_header = ast_str_create ( 255 ) ;
if ( ! http_header ) {
ast_http_request_close_on_completion ( ser ) ;
ast_http_error ( ser , 500 , " Server Error " , " Out of memory " ) ;
2013-03-27 18:52:16 +00:00
close ( fd ) ;
2014-07-03 17:16:55 +00:00
return 0 ;
2009-04-23 20:36:35 +00:00
}
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. " ) ;
2014-07-03 17:16:55 +00:00
return 0 ;
2006-04-01 08:49:54 +00:00
out403 :
2014-07-03 17:16:55 +00:00
ast_http_request_close_on_completion ( ser ) ;
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. " ) ;
2014-07-03 17:16:55 +00:00
return 0 ;
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 " ) ;
2014-07-03 17:16:55 +00:00
return 0 ;
2009-04-23 20:36:35 +00:00
}
2014-07-03 17:16:55 +00:00
out = ast_str_create ( 512 ) ;
if ( ! out ) {
ast_http_request_close_on_completion ( ser ) ;
ast_http_error ( ser , 500 , " Server Error " , " Out of memory " ) ;
return 0 ;
2008-03-19 15:41:54 +00:00
}
2006-12-16 09:33:31 +00:00
ast_str_append ( & out , 0 ,
2015-02-21 14:06:20 +00:00
" <html><title>Asterisk HTTP Status</title> \r \n "
2009-04-23 20:36:35 +00:00
" <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 " ) ;
2015-01-30 17:21:50 +00:00
ast_str_append ( & out , 0 , " <tr><td><i>Server</i></td><td><b>%s</b></td></tr> \r \n " , http_server_name ) ;
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
2015-02-21 14:06:20 +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></html> \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
2014-07-03 17:16:55 +00:00
enum http_private_flags {
/*! TRUE if the HTTP request has a body. */
HTTP_FLAG_HAS_BODY = ( 1 < < 0 ) ,
/*! TRUE if the HTTP request body has been read. */
HTTP_FLAG_BODY_READ = ( 1 < < 1 ) ,
/*! TRUE if the HTTP request must close when completed. */
HTTP_FLAG_CLOSE_ON_COMPLETION = ( 1 < < 2 ) ,
} ;
/*! HTTP tcptls worker_fn private data. */
struct http_worker_private_data {
/*! Body length or -1 if chunked. Valid if HTTP_FLAG_HAS_BODY is TRUE. */
int body_length ;
/*! HTTP body tracking flags */
struct ast_flags flags ;
} ;
2009-04-23 20:36:35 +00:00
void ast_http_send ( struct ast_tcptls_session_instance * ser ,
enum ast_http_method method , int status_code , const char * status_title ,
2014-07-03 17:16:55 +00:00
struct ast_str * http_header , struct ast_str * out , int fd ,
2009-04-23 20:36:35 +00:00
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 ] ;
2016-06-02 22:10:06 +03:00
char buf [ 256 ] ;
int len ;
2009-04-23 20:36:35 +00:00
int content_length = 0 ;
2014-07-03 17:16:55 +00:00
int close_connection ;
2015-01-30 17:21:50 +00:00
struct ast_str * server_header_field = ast_str_create ( MAX_SERVER_NAME_LENGTH ) ;
2016-12-01 16:49:03 -06:00
int send_content ;
2009-04-23 20:36:35 +00:00
2016-06-02 22:10:06 +03:00
if ( ! ser | | ! server_header_field ) {
2014-07-03 17:16:55 +00:00
/* The connection is not open. */
ast_free ( http_header ) ;
ast_free ( out ) ;
2015-01-30 17:21:50 +00:00
ast_free ( server_header_field ) ;
2009-04-23 20:36:35 +00:00
return ;
}
2015-01-30 17:21:50 +00:00
if ( ! ast_strlen_zero ( http_server_name ) ) {
ast_str_set ( & server_header_field ,
0 ,
" Server: %s \r \n " ,
http_server_name ) ;
}
2014-07-03 17:16:55 +00:00
/*
* We shouldn't be sending non-final status codes to this
* function because we may close the connection before
* returning.
*/
ast_assert ( 200 < = status_code ) ;
if ( session_keep_alive < = 0 ) {
close_connection = 1 ;
} else {
struct http_worker_private_data * request ;
request = ser - > private_data ;
if ( ! request
| | ast_test_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION )
| | ast_http_body_discard ( ser ) ) {
close_connection = 1 ;
} else {
close_connection = 0 ;
}
}
2009-04-23 20:36:35 +00:00
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-04 15:13:55 +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 ) ;
}
2016-12-01 16:49:03 -06:00
send_content = method ! = AST_HTTP_HEAD | | status_code > = 400 ;
2009-04-23 20:36:35 +00:00
/* send http header */
2017-07-05 14:31:43 -06:00
if ( ast_iostream_printf ( ser - > stream ,
2014-07-03 17:16:55 +00:00
" HTTP/1.1 %d %s \r \n "
2015-01-30 17:21:50 +00:00
" %s "
2009-04-23 20:36:35 +00:00
" Date: %s \r \n "
" %s "
2011-03-01 22:26:37 +00:00
" %s "
2014-07-03 17:16:55 +00:00
" %s "
" Content-Length: %d \r \n "
2016-12-01 16:49:03 -06:00
" \r \n "
" %s " ,
2009-04-23 20:36:35 +00:00
status_code , status_title ? status_title : " OK " ,
2015-01-30 17:21:50 +00:00
ast_str_buffer ( server_header_field ) ,
2009-04-23 20:36:35 +00:00
timebuf ,
2014-07-03 17:16:55 +00:00
close_connection ? " Connection: close \r \n " : " " ,
2009-04-23 20:36:35 +00:00
static_content ? " " : " Cache-Control: no-cache, no-store \r \n " ,
2014-07-03 17:16:55 +00:00
http_header ? ast_str_buffer ( http_header ) : " " ,
2016-12-01 16:49:03 -06:00
content_length ,
send_content & & out & & ast_str_strlen ( out ) ? ast_str_buffer ( out ) : " "
2017-07-05 14:31:43 -06:00
) < = 0 ) {
ast_debug ( 1 , " ast_iostream_printf() failed: %s \n " , strerror ( errno ) ) ;
close_connection = 1 ;
2017-10-25 09:23:55 -05:00
} else if ( send_content & & fd ) {
/* send file content */
2016-12-01 16:49:03 -06:00
while ( ( len = read ( fd , buf , sizeof ( buf ) ) ) > 0 ) {
if ( ast_iostream_write ( ser - > stream , buf , len ) ! = len ) {
2017-07-05 14:31:43 -06:00
ast_debug ( 1 , " ast_iostream_write() failed: %s \n " , strerror ( errno ) ) ;
2014-07-03 17:16:55 +00:00
close_connection = 1 ;
2016-12-01 16:49:03 -06:00
break ;
2009-04-23 20:36:35 +00:00
}
}
}
2014-07-03 17:16:55 +00:00
ast_free ( http_header ) ;
ast_free ( out ) ;
2015-01-30 17:21:50 +00:00
ast_free ( server_header_field ) ;
2009-04-23 20:36:35 +00:00
2014-07-03 17:16:55 +00:00
if ( close_connection ) {
ast_debug ( 1 , " HTTP closing session. status_code:%d \n " , status_code ) ;
ast_tcptls_close_session_file ( ser ) ;
} else {
ast_debug ( 1 , " HTTP keeping session open. status_code:%d \n " , status_code ) ;
}
2009-04-23 20:36:35 +00:00
}
2015-01-30 17:21:50 +00:00
void ast_http_create_response ( struct ast_tcptls_session_instance * ser , int status_code ,
const char * status_title , struct ast_str * http_header_data , const char * text )
2009-04-23 20:36:35 +00:00
{
2015-01-30 17:21:50 +00:00
char server_name [ MAX_SERVER_NAME_LENGTH ] ;
struct ast_str * server_address = ast_str_create ( MAX_SERVER_NAME_LENGTH ) ;
2019-01-09 10:27:03 +00:00
struct ast_str * out = ast_str_create ( INITIAL_RESPONSE_BODY_BUFFER ) ;
2008-03-19 15:41:54 +00:00
2015-01-30 17:21:50 +00:00
if ( ! http_header_data | | ! server_address | | ! out ) {
ast_free ( http_header_data ) ;
ast_free ( server_address ) ;
2009-04-23 20:36:35 +00:00
ast_free ( out ) ;
2016-06-02 22:10:06 +03:00
if ( ser ) {
2015-01-30 17:21:50 +00:00
ast_debug ( 1 , " HTTP closing session. OOM. \n " ) ;
2014-07-03 17:16:55 +00:00
ast_tcptls_close_session_file ( ser ) ;
}
2009-04-23 20:36:35 +00:00
return ;
2008-03-19 15:41:54 +00:00
}
2015-01-30 17:21:50 +00:00
if ( ! ast_strlen_zero ( http_server_name ) ) {
ast_xml_escape ( http_server_name , server_name , sizeof ( server_name ) ) ;
ast_str_set ( & server_address ,
0 ,
" <address>%s</address> \r \n " ,
server_name ) ;
}
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 "
" %s "
" </body></html> \r \n " ,
status_code ,
status_title ,
status_title ,
text ? text : " " ,
ast_str_buffer ( server_address ) ) ;
ast_free ( server_address ) ;
ast_http_send ( ser ,
AST_HTTP_UNKNOWN ,
status_code ,
status_title ,
http_header_data ,
out ,
0 ,
0 ) ;
2006-03-25 23:50:09 +00:00
}
2015-01-30 17:21:50 +00:00
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 )
2009-04-23 20:36:35 +00:00
{
2015-01-30 17:21:50 +00:00
int status_code = 401 ;
char * status_title = " Unauthorized " ;
struct ast_str * http_header_data = ast_str_create ( DEFAULT_RESPONSE_HEADER_LENGTH ) ;
if ( http_header_data ) {
ast_str_set ( & http_header_data ,
0 ,
" WWW-authenticate: Digest algorithm=MD5, realm= \" %s \" , nonce= \" %08lx \" , qop= \" auth \" , opaque= \" %08lx \" %s \r \n "
" Content-type: text/html \r \n " ,
realm ? realm : " Asterisk " ,
nonce ,
opaque ,
stale ? " , stale=true " : " " ) ;
}
ast_http_create_response ( ser ,
status_code ,
status_title ,
http_header_data ,
text ) ;
}
2009-04-23 20:36:35 +00:00
2015-01-30 17:21:50 +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_header_data = ast_str_create ( DEFAULT_RESPONSE_HEADER_LENGTH ) ;
2009-04-23 20:36:35 +00:00
2015-01-30 17:21:50 +00:00
if ( http_header_data ) {
ast_str_set ( & http_header_data , 0 , " Content-type: text/html \r \n " ) ;
}
2009-04-23 20:36:35 +00:00
2015-01-30 17:21:50 +00:00
ast_http_create_response ( ser ,
status_code ,
status_title ,
http_header_data ,
text ) ;
2009-04-23 20:36:35 +00:00
}
2014-07-03 17:16:55 +00:00
/*!
* \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
2016-08-17 15:10:54 +02:00
urih - > prefix = prefix ;
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-12-24 16:50:48 +00:00
/*!
* \brief Retrieves the header with the given field name.
*
* \param headers Headers to search.
* \param field_name Name of the header to find.
* \return Associated header value.
* \return \c NULL if header is not present.
*/
2014-07-03 17:16:55 +00:00
static const char * get_header ( struct ast_variable * headers , const char * field_name )
2013-12-24 16:50:48 +00:00
{
struct ast_variable * v ;
for ( v = headers ; v ; v = v - > next ) {
if ( ! strcasecmp ( v - > name , field_name ) ) {
return v - > value ;
}
}
return NULL ;
}
2013-12-13 17:19:23 +00:00
/*!
* \brief Retrieves the content type specified in the "Content-Type" header.
*
* This function only returns the "type/subtype" and any trailing parameter is
* not included.
*
* \note the return value is an allocated string that needs to be freed.
*
* \retval the content type/subtype or NULL if the header is not found.
*/
static char * get_content_type ( struct ast_variable * headers )
2013-11-27 15:48:39 +00:00
{
2013-12-24 16:50:48 +00:00
const char * content_type = get_header ( headers , " Content-Type " ) ;
const char * param ;
size_t size ;
2013-11-27 15:48:39 +00:00
2013-12-24 16:50:48 +00:00
if ( ! content_type ) {
return NULL ;
2013-11-27 15:48:39 +00:00
}
2013-12-24 16:50:48 +00:00
param = strchr ( content_type , ' ; ' ) ;
size = param ? param - content_type : strlen ( content_type ) ;
return ast_strndup ( content_type , size ) ;
2013-11-27 15:48:39 +00:00
}
2013-12-24 16:50:48 +00:00
/*!
* \brief Returns the value of the Content-Length header.
*
* \param headers HTTP headers.
2014-07-03 17:16:55 +00:00
*
* \retval length Value of the Content-Length header.
* \retval 0 if header is not present.
* \retval -1 if header is invalid.
2013-12-24 16:50:48 +00:00
*/
2013-11-27 15:48:39 +00:00
static int get_content_length ( struct ast_variable * headers )
{
2013-12-24 16:50:48 +00:00
const char * content_length = get_header ( headers , " Content-Length " ) ;
2014-07-03 17:16:55 +00:00
int length ;
2013-11-27 15:48:39 +00:00
2013-12-24 16:50:48 +00:00
if ( ! content_length ) {
/* Missing content length; assume zero */
return 0 ;
2013-11-27 15:48:39 +00:00
}
2014-07-03 17:16:55 +00:00
length = 0 ;
if ( sscanf ( content_length , " %30d " , & length ) ! = 1 ) {
/* Invalid Content-Length value */
length = - 1 ;
}
return length ;
2013-12-24 16:50:48 +00:00
}
/*!
* \brief Returns the value of the Transfer-Encoding header.
*
* \param headers HTTP headers.
2014-07-03 17:16:55 +00:00
* \retval string Value of the Transfer-Encoding header.
* \retval NULL if header is not present.
2013-12-24 16:50:48 +00:00
*/
static const char * get_transfer_encoding ( struct ast_variable * headers )
{
return get_header ( headers , " Transfer-Encoding " ) ;
2013-11-27 15:48:39 +00:00
}
2014-01-17 20:51:19 +00:00
/*!
2014-07-03 17:16:55 +00:00
* \internal
* \brief Determine if the HTTP peer wants the connection closed.
*
* \param headers List of HTTP headers
*
* \retval 0 keep connection open.
* \retval -1 close connection.
*/
static int http_check_connection_close ( struct ast_variable * headers )
{
const char * connection = get_header ( headers , " Connection " ) ;
int close_connection = 0 ;
if ( connection & & ! strcasecmp ( connection , " close " ) ) {
close_connection = - 1 ;
}
return close_connection ;
}
void ast_http_request_close_on_completion ( struct ast_tcptls_session_instance * ser )
{
struct http_worker_private_data * request = ser - > private_data ;
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
}
/*!
* \internal
* \brief Initialize the request tracking information in case of early failure.
* \since 12.4.0
*
* \param request Request tracking information.
*
* \return Nothing
*/
static void http_request_tracking_init ( struct http_worker_private_data * request )
{
ast_set_flags_to ( & request - > flags ,
HTTP_FLAG_HAS_BODY | HTTP_FLAG_BODY_READ | HTTP_FLAG_CLOSE_ON_COMPLETION ,
/* Assume close in case request fails early */
HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
}
/*!
* \internal
* \brief Setup the HTTP request tracking information.
* \since 12.4.0
*
* \param ser HTTP TCP/TLS session object.
* \param headers List of HTTP headers.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int http_request_tracking_setup ( struct ast_tcptls_session_instance * ser , struct ast_variable * headers )
{
struct http_worker_private_data * request = ser - > private_data ;
const char * transfer_encoding ;
ast_set_flags_to ( & request - > flags ,
HTTP_FLAG_HAS_BODY | HTTP_FLAG_BODY_READ | HTTP_FLAG_CLOSE_ON_COMPLETION ,
http_check_connection_close ( headers ) ? HTTP_FLAG_CLOSE_ON_COMPLETION : 0 ) ;
transfer_encoding = get_transfer_encoding ( headers ) ;
if ( transfer_encoding & & ! strcasecmp ( transfer_encoding , " chunked " ) ) {
request - > body_length = - 1 ;
ast_set_flag ( & request - > flags , HTTP_FLAG_HAS_BODY ) ;
return 0 ;
}
request - > body_length = get_content_length ( headers ) ;
if ( 0 < request - > body_length ) {
ast_set_flag ( & request - > flags , HTTP_FLAG_HAS_BODY ) ;
} else if ( request - > body_length < 0 ) {
/* Invalid Content-Length */
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
ast_http_error ( ser , 400 , " Bad Request " , " Invalid Content-Length in request! " ) ;
return - 1 ;
}
return 0 ;
}
void ast_http_body_read_status ( struct ast_tcptls_session_instance * ser , int read_success )
{
struct http_worker_private_data * request ;
request = ser - > private_data ;
if ( ! ast_test_flag ( & request - > flags , HTTP_FLAG_HAS_BODY )
| | ast_test_flag ( & request - > flags , HTTP_FLAG_BODY_READ ) ) {
/* No body to read. */
return ;
}
ast_set_flag ( & request - > flags , HTTP_FLAG_BODY_READ ) ;
if ( ! read_success ) {
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
}
}
/*!
* \internal
* \brief Read the next length bytes from the HTTP body.
* \since 12.4.0
*
* \param ser HTTP TCP/TLS session object.
* \param buf Where to put the contents reading.
* \param length How much contents to read.
* \param what_getting Name of the contents reading.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int http_body_read_contents ( struct ast_tcptls_session_instance * ser , char * buf , int length , const char * what_getting )
{
int res ;
2019-01-09 10:27:03 +00:00
int total = 0 ;
2014-07-03 17:16:55 +00:00
2016-06-02 22:10:06 +03:00
/* Stream is in exclusive mode so we get it all if possible. */
2019-01-09 10:27:03 +00:00
while ( total ! = length ) {
res = ast_iostream_read ( ser - > stream , buf + total , length - total ) ;
if ( res < = 0 ) {
break ;
}
total + = res ;
}
if ( total ! = length ) {
ast_log ( LOG_WARNING , " Wrong HTTP content read. Request %s (Wanted %d, Read %d) \n " ,
what_getting , length , res ) ;
2014-07-03 17:16:55 +00:00
return - 1 ;
}
2019-01-09 10:27:03 +00:00
2014-07-03 17:16:55 +00:00
return 0 ;
}
/*!
* \internal
* \brief Read and discard the next length bytes from the HTTP body.
* \since 12.4.0
*
* \param ser HTTP TCP/TLS session object.
* \param length How much contents to discard
* \param what_getting Name of the contents discarding.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int http_body_discard_contents ( struct ast_tcptls_session_instance * ser , int length , const char * what_getting )
{
2016-06-02 22:10:06 +03:00
ssize_t res ;
res = ast_iostream_discard ( ser - > stream , length ) ;
if ( res < length ) {
ast_log ( LOG_WARNING , " Short HTTP request %s (Wanted %d but got %zd) \n " ,
what_getting , length , res ) ;
2014-07-03 17:16:55 +00:00
return - 1 ;
}
return 0 ;
}
/*!
* \internal
2014-01-17 20:51:19 +00:00
* \brief decode chunked mode hexadecimal value
*
* \param s string to decode
* \param len length of string
2014-07-03 17:16:55 +00:00
*
* \retval length on success.
* \retval -1 on error.
2014-01-17 20:51:19 +00:00
*/
static int chunked_atoh ( const char * s , int len )
{
int value = 0 ;
char c ;
if ( * s < ' 0 ' ) {
/* zero value must be 0\n not just \n */
return - 1 ;
}
2014-07-03 17:16:55 +00:00
while ( len - - ) {
c = * s + + ;
if ( c = = ' \x0D ' ) {
2014-01-17 20:51:19 +00:00
return value ;
}
2014-07-03 17:16:55 +00:00
if ( c = = ' ; ' ) {
/* We have a chunk-extension that we don't care about. */
while ( len - - ) {
if ( * s + + = = ' \x0D ' ) {
return value ;
}
}
break ;
}
2014-01-17 20:51:19 +00:00
value < < = 4 ;
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
value + = c - ' 0 ' ;
continue ;
}
if ( c > = ' a ' & & c < = ' f ' ) {
value + = 10 + c - ' a ' ;
continue ;
}
if ( c > = ' A ' & & c < = ' F ' ) {
value + = 10 + c - ' A ' ;
continue ;
}
/* invalid character */
return - 1 ;
}
/* end of string */
return - 1 ;
}
2014-07-03 17:16:55 +00:00
/*!
* \internal
* \brief Read and convert the chunked body header length.
* \since 12.4.0
*
* \param ser HTTP TCP/TLS session object.
*
* \retval length Size of chunk to expect.
* \retval -1 on error.
*/
static int http_body_get_chunk_length ( struct ast_tcptls_session_instance * ser )
{
int length ;
char header_line [ MAX_HTTP_LINE_LENGTH ] ;
/* get the line of hexadecimal giving chunk-size w/ optional chunk-extension */
2016-06-02 22:10:06 +03:00
if ( ast_iostream_gets ( ser - > stream , header_line , sizeof ( header_line ) ) < = 0 ) {
2014-07-03 17:16:55 +00:00
ast_log ( LOG_WARNING , " Short HTTP read of chunked header \n " ) ;
return - 1 ;
}
length = chunked_atoh ( header_line , strlen ( header_line ) ) ;
if ( length < 0 ) {
ast_log ( LOG_WARNING , " Invalid HTTP chunk size \n " ) ;
return - 1 ;
}
return length ;
}
/*!
* \internal
* \brief Read and check the chunk contents line termination.
* \since 12.4.0
*
* \param ser HTTP TCP/TLS session object.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int http_body_check_chunk_sync ( struct ast_tcptls_session_instance * ser )
{
int res ;
char chunk_sync [ 2 ] ;
/* Stay in fread until get the expected CRLF or timeout. */
2016-06-02 22:10:06 +03:00
res = ast_iostream_read ( ser - > stream , chunk_sync , sizeof ( chunk_sync ) ) ;
if ( res < sizeof ( chunk_sync ) ) {
2014-07-03 17:16:55 +00:00
ast_log ( LOG_WARNING , " Short HTTP chunk sync read (Wanted %zu) \n " ,
sizeof ( chunk_sync ) ) ;
return - 1 ;
}
if ( chunk_sync [ 0 ] ! = 0x0D | | chunk_sync [ 1 ] ! = 0x0A ) {
2014-12-17 10:23:32 +00:00
ast_log ( LOG_WARNING , " HTTP chunk sync bytes wrong (0x%02hhX, 0x%02hhX) \n " ,
( unsigned char ) chunk_sync [ 0 ] , ( unsigned char ) chunk_sync [ 1 ] ) ;
2014-07-03 17:16:55 +00:00
return - 1 ;
}
return 0 ;
}
/*!
* \internal
* \brief Read and discard any chunked trailer entity-header lines.
* \since 12.4.0
*
* \param ser HTTP TCP/TLS session object.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int http_body_discard_chunk_trailer_headers ( struct ast_tcptls_session_instance * ser )
{
char header_line [ MAX_HTTP_LINE_LENGTH ] ;
for ( ; ; ) {
2016-06-02 22:10:06 +03:00
if ( ast_iostream_gets ( ser - > stream , header_line , sizeof ( header_line ) ) < = 0 ) {
2014-07-03 17:16:55 +00:00
ast_log ( LOG_WARNING , " Short HTTP read of chunked trailer header \n " ) ;
return - 1 ;
}
/* Trim trailing whitespace */
ast_trim_blanks ( header_line ) ;
if ( ast_strlen_zero ( header_line ) ) {
/* A blank line ends the chunked-body */
break ;
}
}
return 0 ;
}
int ast_http_body_discard ( struct ast_tcptls_session_instance * ser )
{
struct http_worker_private_data * request ;
request = ser - > private_data ;
if ( ! ast_test_flag ( & request - > flags , HTTP_FLAG_HAS_BODY )
| | ast_test_flag ( & request - > flags , HTTP_FLAG_BODY_READ ) ) {
/* No body to read or it has already been read. */
return 0 ;
}
ast_set_flag ( & request - > flags , HTTP_FLAG_BODY_READ ) ;
ast_debug ( 1 , " HTTP discarding unused request body \n " ) ;
ast_assert ( request - > body_length ! = 0 ) ;
if ( 0 < request - > body_length ) {
if ( http_body_discard_contents ( ser , request - > body_length , " body " ) ) {
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
return - 1 ;
}
return 0 ;
}
/* parse chunked-body */
for ( ; ; ) {
int length ;
length = http_body_get_chunk_length ( ser ) ;
if ( length < 0 ) {
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
return - 1 ;
}
if ( length = = 0 ) {
/* parsed last-chunk */
break ;
}
if ( http_body_discard_contents ( ser , length , " chunk-data " )
| | http_body_check_chunk_sync ( ser ) ) {
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
return - 1 ;
}
}
/* Read and discard any trailer entity-header lines. */
if ( http_body_discard_chunk_trailer_headers ( ser ) ) {
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
return - 1 ;
}
return 0 ;
}
2014-01-17 20:51:19 +00:00
/*!
* \brief Returns the contents (body) of the HTTP request
*
* \param return_length ptr to int that returns content length
2014-07-03 17:16:55 +00:00
* \param ser HTTP TCP/TLS session object
2014-01-17 20:51:19 +00:00
* \param headers List of HTTP headers
* \return ptr to content (zero terminated) or NULL on failure
* \note Since returned ptr is malloc'd, it should be free'd by caller
*/
static char * ast_http_get_contents ( int * return_length ,
struct ast_tcptls_session_instance * ser , struct ast_variable * headers )
{
2014-07-03 17:16:55 +00:00
struct http_worker_private_data * request ;
int content_length ;
int bufsize ;
2014-01-17 20:51:19 +00:00
char * buf ;
2014-07-03 17:16:55 +00:00
request = ser - > private_data ;
if ( ! ast_test_flag ( & request - > flags , HTTP_FLAG_HAS_BODY ) ) {
/* no content - not an error */
return NULL ;
}
if ( ast_test_flag ( & request - > flags , HTTP_FLAG_BODY_READ ) ) {
/* Already read the body. Cannot read again. Assume no content. */
ast_assert ( 0 ) ;
return NULL ;
}
ast_set_flag ( & request - > flags , HTTP_FLAG_BODY_READ ) ;
2014-01-17 20:51:19 +00:00
2014-07-03 17:16:55 +00:00
ast_debug ( 2 , " HTTP consuming request body \n " ) ;
ast_assert ( request - > body_length ! = 0 ) ;
if ( 0 < request - > body_length ) {
2014-01-17 20:51:19 +00:00
/* handle regular non-chunked content */
2014-07-03 17:16:55 +00:00
content_length = request - > body_length ;
if ( content_length > MAX_CONTENT_LENGTH ) {
ast_log ( LOG_WARNING , " Excessively long HTTP content. (%d > %d) \n " ,
content_length , MAX_CONTENT_LENGTH ) ;
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
2014-01-17 20:51:19 +00:00
errno = EFBIG ;
return NULL ;
}
buf = ast_malloc ( content_length + 1 ) ;
if ( ! buf ) {
/* Malloc sets ENOMEM */
2014-07-03 17:16:55 +00:00
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
2014-01-17 20:51:19 +00:00
return NULL ;
}
2014-07-03 17:16:55 +00:00
if ( http_body_read_contents ( ser , buf , content_length , " body " ) ) {
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
2014-01-17 20:51:19 +00:00
errno = EIO ;
ast_free ( buf ) ;
return NULL ;
}
2014-07-03 17:16:55 +00:00
2014-01-17 20:51:19 +00:00
buf [ content_length ] = 0 ;
* return_length = content_length ;
return buf ;
}
/* pre-allocate buffer */
2014-07-03 17:16:55 +00:00
bufsize = 250 ;
2014-01-17 20:51:19 +00:00
buf = ast_malloc ( bufsize ) ;
if ( ! buf ) {
2014-07-03 17:16:55 +00:00
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
2014-01-17 20:51:19 +00:00
return NULL ;
}
2014-07-03 17:16:55 +00:00
/* parse chunked-body */
content_length = 0 ;
for ( ; ; ) {
int chunk_length ;
chunk_length = http_body_get_chunk_length ( ser ) ;
2014-01-17 20:51:19 +00:00
if ( chunk_length < 0 ) {
2014-07-03 17:16:55 +00:00
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
2014-01-17 20:51:19 +00:00
errno = EIO ;
ast_free ( buf ) ;
return NULL ;
}
2014-07-03 17:16:55 +00:00
if ( chunk_length = = 0 ) {
/* parsed last-chunk */
break ;
}
if ( content_length + chunk_length > MAX_CONTENT_LENGTH ) {
2014-01-17 20:51:19 +00:00
ast_log ( LOG_WARNING ,
2014-07-03 17:16:55 +00:00
" Excessively long HTTP accumulated chunked body. (%d + %d > %d) \n " ,
content_length , chunk_length , MAX_CONTENT_LENGTH ) ;
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
2014-01-17 20:51:19 +00:00
errno = EFBIG ;
ast_free ( buf ) ;
return NULL ;
}
/* insure buffer is large enough +1 */
2014-07-03 17:16:55 +00:00
if ( content_length + chunk_length > = bufsize ) {
char * new_buf ;
/* Increase bufsize until it can handle the expected data. */
do {
bufsize * = 2 ;
} while ( content_length + chunk_length > = bufsize ) ;
new_buf = ast_realloc ( buf , bufsize ) ;
if ( ! new_buf ) {
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
ast_free ( buf ) ;
2014-01-17 20:51:19 +00:00
return NULL ;
}
2014-07-03 17:16:55 +00:00
buf = new_buf ;
2014-01-17 20:51:19 +00:00
}
2014-07-03 17:16:55 +00:00
if ( http_body_read_contents ( ser , buf + content_length , chunk_length , " chunk-data " )
| | http_body_check_chunk_sync ( ser ) ) {
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
2014-01-17 20:51:19 +00:00
errno = EIO ;
ast_free ( buf ) ;
return NULL ;
}
content_length + = chunk_length ;
2014-07-03 17:16:55 +00:00
}
2014-01-17 20:51:19 +00:00
2014-07-03 17:16:55 +00:00
/*
* Read and discard any trailer entity-header lines
* which we don't care about.
*
* XXX In the future we may need to add the trailer headers
* to the passed in headers list rather than discarding them.
*/
if ( http_body_discard_chunk_trailer_headers ( ser ) ) {
ast_set_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ;
errno = EIO ;
ast_free ( buf ) ;
return NULL ;
}
2014-01-17 20:51:19 +00:00
buf [ content_length ] = 0 ;
* return_length = content_length ;
return buf ;
}
2013-11-27 15:48:39 +00:00
struct ast_json * ast_http_get_json (
struct ast_tcptls_session_instance * ser , struct ast_variable * headers )
{
int content_length = 0 ;
struct ast_json * body ;
RAII_VAR ( char * , buf , NULL , ast_free ) ;
2013-12-13 17:19:23 +00:00
RAII_VAR ( char * , type , get_content_type ( headers ) , ast_free ) ;
2013-11-27 15:48:39 +00:00
/* Use errno to distinguish errors from no body */
errno = 0 ;
2013-12-13 17:19:23 +00:00
if ( ast_strlen_zero ( type ) | | strcasecmp ( type , " application/json " ) ) {
2014-07-03 17:16:55 +00:00
/* Content type is not JSON. Don't read the body. */
2013-11-27 15:48:39 +00:00
return NULL ;
}
2014-01-17 20:51:19 +00:00
buf = ast_http_get_contents ( & content_length , ser , headers ) ;
2014-07-03 17:16:55 +00:00
if ( ! buf | | ! content_length ) {
/*
* errno already set
* or it is not an error to have zero content
*/
2014-03-18 15:45:04 +00:00
return NULL ;
}
2013-11-27 15:48:39 +00:00
body = ast_json_load_buf ( buf , content_length , NULL ) ;
2014-07-03 17:16:55 +00:00
if ( ! body ) {
2013-11-27 15:48:39 +00:00
/* Failed to parse JSON; treat as an I/O error */
errno = EIO ;
return NULL ;
}
return body ;
}
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 ;
2013-11-08 17:29:53 +00:00
char * var , * val ;
2014-07-03 17:16:55 +00:00
RAII_VAR ( char * , buf , NULL , ast_free ) ;
2013-12-13 17:19:23 +00:00
RAII_VAR ( char * , type , get_content_type ( headers ) , ast_free ) ;
2009-04-23 20:36:35 +00:00
2013-11-08 17:29:53 +00:00
/* Use errno to distinguish errors from no params */
errno = 0 ;
2013-12-13 17:19:23 +00:00
if ( ast_strlen_zero ( type ) | |
strcasecmp ( type , " application/x-www-form-urlencoded " ) ) {
2014-07-03 17:16:55 +00:00
/* Content type is not form data. Don't read the body. */
2013-11-27 15:48:39 +00:00
return NULL ;
2009-04-23 20:36:35 +00:00
}
2014-01-17 20:51:19 +00:00
buf = ast_http_get_contents ( & content_length , ser , headers ) ;
2014-07-03 17:16:55 +00:00
if ( ! buf | | ! content_length ) {
/*
* errno already set
* or it is not an error to have zero content
*/
2013-11-08 17:29:53 +00:00
return NULL ;
2013-01-02 15:39:42 +00:00
}
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-18 14:50:56 +00:00
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 ;
2014-07-03 17:16:55 +00:00
int res = 0 ;
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
}
}
}
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 ) ;
2014-07-03 17:16:55 +00:00
if ( ! http_header ) {
ast_http_request_close_on_completion ( ser ) ;
ast_http_error ( ser , 500 , " Server Error " , " Out of memory " ) ;
break ;
}
2009-04-23 20:36:35 +00:00
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 ) ;
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 ) {
2006-10-18 04:05:56 +00:00
l = strlen ( urih - > uri ) ;
c = uri + l ; /* candidate */
2013-07-18 14:50:56 +00:00
ast_debug ( 2 , " match request [%s] with handler [%s] len %d \n " , uri , urih - > uri , l ) ;
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 ) {
2013-07-18 14:50:56 +00:00
ast_debug ( 1 , " Match made with [%s] \n " , urih - > uri ) ;
2013-06-24 13:49:20 +00:00
if ( ! urih - > no_decode_uri ) {
ast_uri_decode ( uri , ast_uri_http_legacy ) ;
}
2009-04-23 20:36:35 +00:00
res = urih - > callback ( ser , urih , uri , method , get_vars , headers ) ;
2006-03-25 23:50:09 +00:00
} else {
2013-07-18 14:50:56 +00:00
ast_debug ( 1 , " Requested URI [%s] has no handler \n " , uri ) ;
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
}
2014-03-10 17:21:01 +00:00
static struct ast_variable * parse_cookies ( const char * cookies )
2008-04-23 22:53:20 +00:00
{
2014-03-10 17:21:01 +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:21:01 +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:21:01 +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:10:41 +00:00
if ( ! strcasecmp ( v - > name , " Cookie " ) ) {
2014-03-10 17:21:01 +00:00
ast_variables_destroy ( cookies ) ;
cookies = parse_cookies ( v - > value ) ;
2009-04-23 20:36:35 +00:00
}
}
return cookies ;
}
2014-07-03 17:16:55 +00:00
static struct ast_http_auth * auth_create ( const char * userid , const char * password )
2013-07-03 16:33:13 +00:00
{
2014-07-03 17:16:55 +00:00
struct ast_http_auth * auth ;
2013-07-03 16:33:13 +00:00
size_t userid_len ;
size_t password_len ;
if ( ! userid | | ! password ) {
ast_log ( LOG_ERROR , " Invalid userid/password \n " ) ;
return NULL ;
}
userid_len = strlen ( userid ) + 1 ;
password_len = strlen ( password ) + 1 ;
/* Allocate enough room to store everything in one memory block */
auth = ao2_alloc ( sizeof ( * auth ) + userid_len + password_len , NULL ) ;
if ( ! auth ) {
return NULL ;
}
/* Put the userid right after the struct */
auth - > userid = ( char * ) ( auth + 1 ) ;
strcpy ( auth - > userid , userid ) ;
/* Put the password right after the userid */
auth - > password = auth - > userid + userid_len ;
strcpy ( auth - > password , password ) ;
return auth ;
}
# define BASIC_PREFIX "Basic "
# define BASIC_LEN 6 /*!< strlen(BASIC_PREFIX) */
struct ast_http_auth * ast_http_get_auth ( struct ast_variable * headers )
{
struct ast_variable * v ;
for ( v = headers ; v ; v = v - > next ) {
const char * base64 ;
char decoded [ 256 ] = { } ;
char * username ;
char * password ;
2013-09-06 18:53:32 +00:00
# ifdef AST_DEVMODE
2013-07-03 16:33:13 +00:00
int cnt ;
2013-09-06 18:53:32 +00:00
# endif /* AST_DEVMODE */
2013-07-03 16:33:13 +00:00
if ( strcasecmp ( " Authorization " , v - > name ) ! = 0 ) {
continue ;
}
if ( ! ast_begins_with ( v - > value , BASIC_PREFIX ) ) {
ast_log ( LOG_DEBUG ,
" Unsupported Authorization scheme \n " ) ;
continue ;
}
/* Basic auth header parsing. RFC 2617, section 2.
* credentials = "Basic" basic-credentials
* basic-credentials = base64-user-pass
* base64-user-pass = <base64 encoding of user-pass,
* except not limited to 76 char/line>
* user-pass = userid ":" password
*/
base64 = v - > value + BASIC_LEN ;
/* This will truncate "userid:password" lines to
* sizeof(decoded). The array is long enough that this shouldn't
* be a problem */
2013-09-06 18:53:32 +00:00
# ifdef AST_DEVMODE
cnt =
# endif /* AST_DEVMODE */
ast_base64decode ( ( unsigned char * ) decoded , base64 ,
2013-07-03 16:33:13 +00:00
sizeof ( decoded ) - 1 ) ;
ast_assert ( cnt < sizeof ( decoded ) ) ;
/* Split the string at the colon */
password = decoded ;
username = strsep ( & password , " : " ) ;
if ( ! password ) {
ast_log ( LOG_WARNING , " Invalid Authorization header \n " ) ;
return NULL ;
}
return auth_create ( username , password ) ;
}
return NULL ;
}
2009-04-23 20:36:35 +00:00
2014-06-05 17:22:35 +00:00
int ast_http_response_status_line ( const char * buf , const char * version , int code )
{
int status_code ;
size_t size = strlen ( version ) ;
if ( strncmp ( buf , version , size ) | | buf [ size ] ! = ' ' ) {
ast_log ( LOG_ERROR , " HTTP version not supported - "
" expected %s \n " , version ) ;
return - 1 ;
}
/* skip to status code (version + space) */
buf + = size + 1 ;
if ( sscanf ( buf , " %d " , & status_code ) ! = 1 ) {
ast_log ( LOG_ERROR , " Could not read HTTP status code - "
" %s \n " , buf ) ;
return - 1 ;
}
return status_code ;
}
static void remove_excess_lws ( char * s )
{
char * p , * res = s ;
char * buf = ast_malloc ( strlen ( s ) + 1 ) ;
char * buf_end ;
if ( ! buf ) {
return ;
}
buf_end = buf ;
while ( * s & & * ( s = ast_skip_blanks ( s ) ) ) {
p = s ;
s = ast_skip_nonblanks ( s ) ;
if ( buf_end ! = buf ) {
* buf_end + + = ' ' ;
}
memcpy ( buf_end , p , s - p ) ;
buf_end + = s - p ;
}
* buf_end = ' \0 ' ;
/* safe since buf will always be less than or equal to res */
strcpy ( res , buf ) ;
ast_free ( buf ) ;
}
int ast_http_header_parse ( char * buf , char * * name , char * * value )
{
ast_trim_blanks ( buf ) ;
if ( ast_strlen_zero ( buf ) ) {
return - 1 ;
}
* value = buf ;
* name = strsep ( value , " : " ) ;
if ( ! * value ) {
return 1 ;
}
* value = ast_skip_blanks ( * value ) ;
if ( ast_strlen_zero ( * value ) | | ast_strlen_zero ( * name ) ) {
return 1 ;
}
remove_excess_lws ( * value ) ;
return 0 ;
}
int ast_http_header_match ( const char * name , const char * expected_name ,
const char * value , const char * expected_value )
{
if ( strcasecmp ( name , expected_name ) ) {
/* no value to validate if names don't match */
return 0 ;
}
if ( strcasecmp ( value , expected_value ) ) {
ast_log ( LOG_ERROR , " Invalid header value - expected %s "
" received %s " , value , expected_value ) ;
return - 1 ;
}
return 1 ;
}
int ast_http_header_match_in ( const char * name , const char * expected_name ,
const char * value , const char * expected_value )
{
if ( strcasecmp ( name , expected_name ) ) {
/* no value to validate if names don't match */
return 0 ;
}
if ( ! strcasestr ( expected_value , value ) ) {
ast_log ( LOG_ERROR , " Header '%s' - could not locate '%s' "
" in '%s' \n " , name , value , expected_value ) ;
return - 1 ;
}
return 1 ;
}
2014-03-10 17:21:01 +00:00
/*! Limit the number of request headers in case the sender is being ridiculous. */
# define MAX_HTTP_REQUEST_HEADERS 100
2014-07-03 17:16:55 +00:00
/*!
* \internal
* \brief Read the request headers.
* \since 12.4.0
*
* \param ser HTTP TCP/TLS session object.
* \param headers Where to put the request headers list pointer.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int http_request_headers_get ( struct ast_tcptls_session_instance * ser , struct ast_variable * * headers )
2006-11-27 19:19:48 +00:00
{
2014-07-03 17:16:55 +00:00
struct ast_variable * tail = * headers ;
2014-03-10 17:21:01 +00:00
int remaining_headers ;
2014-07-03 17:16:55 +00:00
char header_line [ MAX_HTTP_LINE_LENGTH ] ;
2014-06-12 17:00:08 +00:00
2014-03-10 17:21:01 +00:00
remaining_headers = MAX_HTTP_REQUEST_HEADERS ;
2014-06-12 17:00:08 +00:00
for ( ; ; ) {
2018-08-30 14:42:06 -05:00
ssize_t len ;
2014-06-12 17:00:08 +00:00
char * name ;
char * value ;
2018-08-30 14:42:06 -05:00
len = ast_iostream_gets ( ser - > stream , header_line , sizeof ( header_line ) ) ;
if ( len < = 0 ) {
2014-06-12 17:00:08 +00:00
ast_http_error ( ser , 400 , " Bad Request " , " Timeout " ) ;
2014-07-03 17:16:55 +00:00
return - 1 ;
2014-06-12 17:00:08 +00:00
}
2018-08-30 14:42:06 -05:00
if ( header_line [ len - 1 ] ! = ' \n ' ) {
/* We didn't get a full line */
ast_http_error ( ser , 400 , " Bad Request " ,
( len = = sizeof ( header_line ) - 1 ) ? " Header line too long " : " Timeout " ) ;
return - 1 ;
}
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 17:00:08 +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:21:01 +00:00
if ( ! remaining_headers - - ) {
/* Too many headers. */
ast_http_error ( ser , 413 , " Request Entity Too Large " , " Too many headers " ) ;
2014-07-03 17:16:55 +00:00
return - 1 ;
2014-03-10 17:21:01 +00:00
}
2014-07-03 17:16:55 +00:00
if ( ! * headers ) {
* headers = ast_variable_new ( name , value , __FILE__ ) ;
tail = * headers ;
2009-04-23 20:36:35 +00:00
} else {
tail - > next = ast_variable_new ( name , value , __FILE__ ) ;
tail = tail - > next ;
2006-03-25 23:50:09 +00:00
}
2014-03-10 17:21:01 +00:00
if ( ! tail ) {
/*
* Variable allocation failure.
* Try to make some room.
*/
2014-07-03 17:16:55 +00:00
ast_variables_destroy ( * headers ) ;
* headers = NULL ;
2014-03-10 17:21:01 +00:00
ast_http_error ( ser , 500 , " Server Error " , " Out of memory " ) ;
2014-07-03 17:16:55 +00:00
return - 1 ;
2014-03-10 17:21:01 +00:00
}
2006-03-25 23:50:09 +00:00
}
2006-10-18 04:05:56 +00:00
2014-07-03 17:16:55 +00:00
return 0 ;
}
/*!
* \internal
* \brief Process a HTTP request.
* \since 12.4.0
*
* \param ser HTTP TCP/TLS session object.
*
* \retval 0 Continue and process the next HTTP request.
* \retval -1 Fatal HTTP connection error. Force the HTTP connection closed.
*/
static int httpd_process_request ( struct ast_tcptls_session_instance * ser )
{
RAII_VAR ( struct ast_variable * , headers , NULL , ast_variables_destroy ) ;
char * uri ;
char * method ;
const char * transfer_encoding ;
struct http_worker_private_data * request ;
enum ast_http_method http_method = AST_HTTP_UNKNOWN ;
int res ;
2018-08-30 14:42:06 -05:00
ssize_t len ;
2014-07-03 17:16:55 +00:00
char request_line [ MAX_HTTP_LINE_LENGTH ] ;
2018-08-30 14:42:06 -05:00
len = ast_iostream_gets ( ser - > stream , request_line , sizeof ( request_line ) ) ;
if ( len < = 0 ) {
2014-07-03 17:16:55 +00:00
return - 1 ;
}
/* Re-initialize the request body tracking data. */
request = ser - > private_data ;
http_request_tracking_init ( request ) ;
2018-08-30 14:42:06 -05:00
if ( request_line [ len - 1 ] ! = ' \n ' ) {
/* We didn't get a full line */
ast_http_error ( ser , 400 , " Bad Request " ,
( len = = sizeof ( request_line ) - 1 ) ? " Request line too long " : " Timeout " ) ;
return - 1 ;
}
2014-07-03 17:16:55 +00:00
/* Get method */
method = ast_skip_blanks ( request_line ) ;
uri = ast_skip_nonblanks ( method ) ;
if ( * uri ) {
* uri + + = ' \0 ' ;
}
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 ;
} else if ( ! strcasecmp ( method , " DELETE " ) ) {
http_method = AST_HTTP_DELETE ;
} else if ( ! strcasecmp ( method , " OPTIONS " ) ) {
http_method = AST_HTTP_OPTIONS ;
}
uri = ast_skip_blanks ( uri ) ; /* Skip white space */
if ( * uri ) { /* terminate at the first blank */
char * c = ast_skip_nonblanks ( uri ) ;
if ( * c ) {
* c = ' \0 ' ;
}
} else {
ast_http_error ( ser , 400 , " Bad Request " , " Invalid Request " ) ;
return - 1 ;
}
2015-02-11 17:39:13 +00:00
if ( ast_shutdown_final ( ) ) {
ast_http_error ( ser , 503 , " Service Unavailable " , " Shutdown in progress " ) ;
return - 1 ;
}
2014-07-03 17:16:55 +00:00
/* process "Request Headers" lines */
if ( http_request_headers_get ( ser , & headers ) ) {
return - 1 ;
}
2013-12-24 16:50:48 +00:00
transfer_encoding = get_transfer_encoding ( headers ) ;
/* Transfer encoding defaults to identity */
if ( ! transfer_encoding ) {
transfer_encoding = " identity " ;
}
/*
* RFC 2616, section 3.6, we should respond with a 501 for any transfer-
* codings we don't understand.
*/
2014-01-17 20:51:19 +00:00
if ( strcasecmp ( transfer_encoding , " identity " ) ! = 0 & &
strcasecmp ( transfer_encoding , " chunked " ) ! = 0 ) {
2013-12-24 16:50:48 +00:00
/* Transfer encodings not supported */
ast_http_error ( ser , 501 , " Unimplemented " , " Unsupported Transfer-Encoding. " ) ;
2014-07-03 17:16:55 +00:00
return - 1 ;
}
if ( http_request_tracking_setup ( ser , headers )
| | handle_uri ( ser , uri , http_method , headers )
| | ast_test_flag ( & request - > flags , HTTP_FLAG_CLOSE_ON_COMPLETION ) ) {
res = - 1 ;
} else {
res = 0 ;
}
return res ;
}
static void * httpd_helper_thread ( void * data )
{
struct ast_tcptls_session_instance * ser = data ;
int timeout ;
2016-06-02 22:10:06 +03:00
int arg = 1 ;
2014-07-03 17:16:55 +00:00
2016-06-02 22:10:06 +03:00
if ( ! ser ) {
2014-07-03 17:16:55 +00:00
ao2_cleanup ( ser ) ;
return NULL ;
}
if ( ast_atomic_fetchadd_int ( & session_count , + 1 ) > = session_limit ) {
ast_log ( LOG_WARNING , " HTTP session count exceeded %d sessions. \n " ,
session_limit ) ;
2013-12-24 16:50:48 +00:00
goto done ;
}
2014-07-03 17:16:55 +00:00
ast_debug ( 1 , " HTTP opening session. Top level \n " ) ;
2013-12-24 16:50:48 +00:00
2014-07-03 17:16:55 +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.
*/
2017-03-18 13:30:32 -04:00
if ( setsockopt ( ast_iostream_get_fd ( ser - > stream ) , IPPROTO_TCP , TCP_NODELAY , ( char * ) & arg , sizeof ( arg ) ) < 0 ) {
2016-06-02 22:10:06 +03:00
ast_log ( LOG_WARNING , " Failed to set TCP_NODELAY on HTTP connection: %s \n " , strerror ( errno ) ) ;
2014-07-03 17:16:55 +00:00
}
2016-06-02 22:10:06 +03:00
ast_iostream_nonblock ( ser - > stream ) ;
2014-07-03 17:16:55 +00:00
/* Setup HTTP worker private data to keep track of request body reading. */
ao2_cleanup ( ser - > private_data ) ;
ser - > private_data = ao2_alloc_options ( sizeof ( struct http_worker_private_data ) , NULL ,
AO2_ALLOC_OPT_LOCK_NOLOCK ) ;
if ( ! ser - > private_data ) {
ast_http_error ( ser , 500 , " Server Error " , " Out of memory " ) ;
goto done ;
}
http_request_tracking_init ( ser - > private_data ) ;
/* Determine initial HTTP request wait timeout. */
timeout = session_keep_alive ;
if ( timeout < = 0 ) {
/* Persistent connections not enabled. */
timeout = session_inactivity ;
}
if ( timeout < MIN_INITIAL_REQUEST_TIMEOUT ) {
timeout = MIN_INITIAL_REQUEST_TIMEOUT ;
}
/* We can let the stream wait for data to arrive. */
2016-06-02 22:10:06 +03:00
ast_iostream_set_exclusive_input ( ser - > stream , 1 ) ;
2014-07-03 17:16:55 +00:00
for ( ; ; ) {
/* Wait for next potential HTTP request message. */
2016-06-02 22:10:06 +03:00
ast_iostream_set_timeout_idle_inactivity ( ser - > stream , timeout , session_inactivity ) ;
if ( httpd_process_request ( ser ) ) {
/* Break the connection or the connection closed */
2014-07-03 17:16:55 +00:00
break ;
}
2016-06-02 22:10:06 +03:00
if ( ! ser - > stream ) {
/* Web-socket or similar that took the connection */
2014-07-03 17:16:55 +00:00
break ;
}
timeout = session_keep_alive ;
if ( timeout < = 0 ) {
/* Persistent connections not enabled. */
break ;
}
}
2009-04-23 20:36:35 +00:00
2011-04-21 18:32:50 +00:00
done :
ast_atomic_fetchadd_int ( & session_count , - 1 ) ;
2016-06-02 22:10:06 +03:00
ast_debug ( 1 , " HTTP closing session. Top level \n " ) ;
ast_tcptls_close_session_file ( ser ) ;
2008-06-17 21:46:57 +00:00
ao2_ref ( ser , - 1 ) ;
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 ] = " " ;
2015-01-30 17:21:50 +00:00
char server_name [ MAX_SERVER_NAME_LENGTH ] ;
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:12:58 +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 ) ;
2018-10-03 08:56:34 -04:00
if ( ! cfg | | cfg = = CONFIG_STATUS_FILEINVALID ) {
return 0 ;
}
/* Even if the http.conf hasn't been updated, the TLS certs/keys may have been */
if ( cfg = = CONFIG_STATUS_FILEUNCHANGED ) {
if ( http_tls_cfg . enabled & & ast_ssl_setup ( https_desc . tls_cfg ) ) {
ast_tcptls_server_start ( & https_desc ) ;
}
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 ;
2017-02-15 13:44:32 -07:00
ast_free ( http_tls_cfg . certfile ) ;
2006-12-07 16:42:29 +00:00
http_tls_cfg . certfile = ast_strdup ( AST_CERTFILE ) ;
2009-04-24 21:22:31 +00:00
2017-02-15 13:44:32 -07:00
ast_free ( http_tls_cfg . capath ) ;
http_tls_cfg . capath = ast_strdup ( " " ) ;
ast_free ( http_tls_cfg . pvtfile ) ;
2009-04-24 21:22:31 +00:00
http_tls_cfg . pvtfile = ast_strdup ( " " ) ;
2016-02-03 14:05:20 -04:00
/* Apply modern intermediate settings according to the Mozilla OpSec team as of July 30th, 2015 but disable TLSv1 */
ast_set_flag ( & http_tls_cfg . flags , AST_SSL_DISABLE_TLSV1 | AST_SSL_SERVER_CIPHER_ORDER ) ;
2017-02-15 13:44:32 -07:00
ast_free ( http_tls_cfg . cipher ) ;
2016-02-03 14:05:20 -04:00
http_tls_cfg . cipher = ast_strdup ( " ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA " ) ;
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 17:00:08 +00:00
session_limit = DEFAULT_SESSION_LIMIT ;
session_inactivity = DEFAULT_SESSION_INACTIVITY ;
2014-07-03 17:16:55 +00:00
session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE ;
2015-01-30 17:21:50 +00:00
snprintf ( server_name , sizeof ( server_name ) , " Asterisk/%s " , ast_get_version ( ) ) ;
2014-07-03 17:16:55 +00:00
v = ast_variable_browse ( cfg , " general " ) ;
for ( ; v ; v = v - > next ) {
/* 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 " )
& & ! ast_tls_read_conf ( & http_tls_cfg , & https_desc , v - > name , v - > value ) ) {
continue ;
}
2014-06-12 17:00:08 +00:00
2015-01-30 17:21:50 +00:00
if ( ! strcasecmp ( v - > name , " servername " ) ) {
if ( ! ast_strlen_zero ( v - > value ) ) {
ast_copy_string ( server_name , v - > value , sizeof ( server_name ) ) ;
} else {
server_name [ 0 ] = ' \0 ' ;
}
} else if ( ! strcasecmp ( v - > name , " enabled " ) ) {
2014-07-03 17:16:55 +00:00
enabled = ast_true ( v - > value ) ;
} else if ( ! strcasecmp ( v - > name , " enablestatic " ) ) {
newenablestatic = ast_true ( v - > value ) ;
} else if ( ! strcasecmp ( v - > name , " bindport " ) ) {
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 " \n " ,
v - > value , DEFAULT_PORT ) ;
2009-04-29 14:39:48 +00:00
}
2014-07-03 17:16:55 +00:00
} else if ( ! strcasecmp ( v - > name , " bindaddr " ) ) {
if ( ! ( num_addrs = ast_sockaddr_resolve ( & addrs , v - > value , 0 , AST_AF_UNSPEC ) ) ) {
ast_log ( LOG_WARNING , " Invalid bind address %s \n " , v - > value ) ;
}
} else if ( ! strcasecmp ( v - > name , " prefix " ) ) {
if ( ! ast_strlen_zero ( v - > value ) ) {
newprefix [ 0 ] = ' / ' ;
ast_copy_string ( newprefix + 1 , v - > value , sizeof ( newprefix ) - 1 ) ;
2006-12-23 20:13:14 +00:00
} else {
2014-07-03 17:16:55 +00:00
newprefix [ 0 ] = ' \0 ' ;
2006-04-02 09:10:43 +00:00
}
2014-07-03 17:16:55 +00:00
} else if ( ! strcasecmp ( v - > name , " redirect " ) ) {
add_redirect ( v - > value ) ;
} 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 ) ;
}
} 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 ) ;
}
} else if ( ! strcasecmp ( v - > name , " session_keep_alive " ) ) {
if ( sscanf ( v - > value , " %30d " , & session_keep_alive ) ! = 1
| | session_keep_alive < 0 ) {
session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE ;
ast_log ( LOG_WARNING , " Invalid %s '%s' at line %d of http.conf \n " ,
v - > name , v - > value , v - > lineno ) ;
}
} else {
ast_log ( LOG_WARNING , " Ignoring unknown option '%s' in http.conf \n " , v - > name ) ;
2006-03-25 23:50:09 +00:00
}
}
2010-10-29 20:46:06 +00:00
2014-07-03 17:16:55 +00:00
ast_config_destroy ( cfg ) ;
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
}
2015-01-30 17:21:50 +00:00
ast_copy_string ( http_server_name , server_name , sizeof ( http_server_name ) ) ;
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 ) {
2016-06-20 15:05:09 +02:00
ast_sockaddr_copy ( & https_desc . local_address , & http_desc . local_address ) ;
2010-10-29 20:46:06 +00:00
/* 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 ) ;
2015-01-30 17:21:50 +00:00
ast_cli ( a - > fd , " Server: %s \n " , http_server_name ) ;
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
}
2018-02-16 22:11:42 -05:00
static int reload_module ( void )
2006-03-25 23:50:09 +00:00
{
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
} ;
2018-02-16 22:11:42 -05:00
static int unload_module ( void )
2012-12-11 22:03:23 +00:00
{
2013-08-21 17:12:30 +00:00
struct http_uri_redirect * redirect ;
2012-12-11 22:03:23 +00:00
ast_cli_unregister_multiple ( cli_http , ARRAY_LEN ( cli_http ) ) ;
2013-08-21 17:12:30 +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 ) ;
2017-02-15 13:44:32 -07:00
ast_free ( http_tls_cfg . capath ) ;
2013-08-21 17:12:30 +00:00
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 ) ;
2018-02-16 22:11:42 -05:00
return 0 ;
2012-12-11 22:03:23 +00:00
}
2018-02-16 22:11:42 -05:00
static int load_module ( void )
2006-03-25 23:50:09 +00:00
{
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 ) ) ;
2007-04-06 21:16:38 +00:00
2018-02-16 22:11:42 -05:00
return __ast_http_load ( 0 ) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS ;
2006-03-25 23:50:09 +00:00
}
2018-02-16 22:11:42 -05:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , " Built-in HTTP Server " ,
. support_level = AST_MODULE_SUPPORT_CORE ,
. load = load_module ,
. unload = unload_module ,
. reload = reload_module ,
. load_pri = AST_MODPRI_CORE ,
2018-07-26 13:52:46 -04:00
. requires = " extconfig " ,
2018-02-16 22:11:42 -05:00
) ;