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"
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
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 ;
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 ;
}
}
if ( cookies ) {
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: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
2009-04-23 20:36:35 +00:00
if ( ( http_header = ast_str_create ( 255 )) == NULL ) {
2013-03-27 18:52:16 +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 ) {
content_length += strlen ( ast_str_buffer ( out ));
}
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 ) {
if ( out ) {
fprintf ( ser -> f , "%s" , ast_str_buffer ( out ));
}
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 );
}
fclose ( ser -> f );
ser -> f = 0 ;
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:39:11 +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 ;
2013-11-08 17:28:40 +00:00
char * var , * val ;
RAII_VAR ( char * , buf , NULL , ast_free_ptr );
2013-01-02 15:39:42 +00:00
int res ;
2009-04-23 20:36:35 +00:00
2013-11-08 17:28:40 +00:00
/* Use errno to distinguish errors from no params */
errno = 0 ;
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:39:42 +00:00
content_length = atoi ( v -> value );
2009-04-23 20:36:35 +00:00
break ;
}
}
2013-01-02 15:39:42 +00:00
if ( content_length <= 0 ) {
2009-04-23 20:36:35 +00:00
return NULL ;
}
2013-03-27 14:39:11 +00:00
if ( content_length > MAX_POST_CONTENT - 1 ) {
2013-11-08 17:28:40 +00:00
ast_log ( LOG_WARNING ,
"Excessively long HTTP content. (%d > %d) \n " ,
content_length , MAX_POST_CONTENT );
errno = EFBIG ;
2013-03-27 14:39:11 +00:00
return NULL ;
}
2013-01-02 15:39:42 +00:00
buf = ast_malloc ( content_length + 1 );
if ( ! buf ) {
2013-11-08 17:28:40 +00:00
/* malloc sets errno to ENOMEM */
2009-04-23 20:36:35 +00:00
return NULL ;
}
2013-01-02 15:39:42 +00:00
res = fread ( buf , 1 , content_length , ser -> f );
if ( res < content_length ) {
/* Error, distinguishable by ferror() or feof(), but neither
2013-11-08 17:28:40 +00:00
* is good. Treat either one as I/O error */
errno = EIO ;
return NULL ;
2013-01-02 15:39:42 +00:00
}
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-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 ;
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
}
}
}
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 ) {
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
}
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
2008-04-23 22:53:20 +00:00
static struct ast_variable * parse_cookies ( char * cookies )
{
char * cur ;
struct ast_variable * vars = NULL , * var ;
while (( cur = strsep ( & cookies , ";" ))) {
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 )
{
struct ast_variable * v , * cookies = NULL ;
for ( v = headers ; v ; v = v -> next ) {
2013-07-21 03:10:41 +00:00
if ( ! strcasecmp ( v -> name , "Cookie" )) {
2009-08-31 22:02:24 +00:00
char * tmp = ast_strdupa ( v -> value );
2009-04-23 20:36:35 +00:00
if ( cookies ) {
ast_variables_destroy ( cookies );
}
2009-08-31 22:02:24 +00:00
cookies = parse_cookies ( tmp );
2009-04-23 20:36:35 +00:00
}
}
return cookies ;
}
2013-07-03 16:33:13 +00:00
static struct ast_http_auth * auth_create ( const char * userid ,
const char * password )
{
RAII_VAR ( struct ast_http_auth * , auth , NULL , ao2_cleanup );
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 );
ao2_ref ( auth , + 1 );
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:49:09 +00:00
#ifdef AST_DEVMODE
2013-07-03 16:33:13 +00:00
int cnt ;
2013-09-06 18:49:09 +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:49:09 +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
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 ;
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 ;
}
2008-03-19 15:41:54 +00:00
if ( ! fgets ( buf , sizeof ( buf ), 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 ;
2013-04-22 14:58:53 +00:00
} else if ( ! strcasecmp ( method , "DELETE" )) {
http_method = AST_HTTP_DELETE ;
} else if ( ! strcasecmp ( method , "OPTIONS" )) {
http_method = AST_HTTP_OPTIONS ;
2009-04-23 20:36:35 +00:00
}
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
}
2006-10-18 04:05:56 +00:00
}
2009-04-23 20:36:35 +00:00
/* process "Request Headers" lines */
while ( fgets ( header_line , sizeof ( header_line ), ser -> f )) {
char * name , * value ;
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 )) {
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 );
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
}
}
2006-10-18 04:05:56 +00:00
2008-03-17 22:10:06 +00:00
if ( !* uri ) {
2009-04-23 20:36:35 +00:00
ast_http_error ( ser , 400 , "Bad Request" , "Invalid Request" );
2011-04-21 18:32:50 +00:00
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 */
2008-03-19 15:41:54 +00:00
if ( headers ) {
2007-09-17 14:58:19 +00:00
ast_variables_destroy ( headers );
2008-03-19 15:41:54 +00:00
}
2006-10-18 04:05:56 +00:00
2009-04-23 20:36:35 +00:00
if ( ser -> f ) {
fclose ( ser -> f );
}
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: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 );
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 );
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:53:03 +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 );
}
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:03:23 +00:00
static void http_shutdown ( void )
{
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 );
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:03:23 +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:03:23 +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 );
}