2005-05-26 05:06:32 +04:00
/*
Unix SMB / CIFS implementation .
web server startup
Copyright ( C ) Andrew Tridgell 2005
2008-09-21 18:53:29 +04:00
Copyright ( C ) Jelmer Vernooij 2008
2005-05-26 05:06:32 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2005-05-26 05:06:32 +04:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2005-05-26 05:06:32 +04:00
*/
# include "includes.h"
# include "smbd/service_task.h"
# include "smbd/service_stream.h"
2006-03-07 17:13:38 +03:00
# include "smbd/service.h"
2005-05-26 05:06:32 +04:00
# include "web_server/web_server.h"
# include "lib/events/events.h"
# include "system/filesys.h"
2006-03-07 14:07:23 +03:00
# include "system/network.h"
2006-08-17 17:37:04 +04:00
# include "lib/socket/netif.h"
2005-06-19 08:21:45 +04:00
# include "lib/tls/tls.h"
2008-10-11 23:31:42 +04:00
# include "../lib/util/dlinklist.h"
2007-09-08 16:42:09 +04:00
# include "param/param.h"
2005-05-26 05:06:32 +04:00
/* don't allow connections to hang around forever */
2006-10-10 12:55:44 +04:00
# define HTTP_TIMEOUT 120
2005-05-26 05:06:32 +04:00
/*
destroy a web connection
*/
2006-05-24 11:35:06 +04:00
static int websrv_destructor ( struct websrv_context * web )
2005-05-26 05:06:32 +04:00
{
return 0 ;
}
/*
called when a connection times out . This prevents a stuck connection
from hanging around forever
*/
2008-12-29 22:24:57 +03:00
static void websrv_timeout ( struct tevent_context * event_context ,
struct tevent_timer * te ,
2009-02-02 10:42:38 +03:00
struct timeval t , void * private_data )
2005-05-26 05:06:32 +04:00
{
2009-02-02 10:42:38 +03:00
struct websrv_context * web = talloc_get_type ( private_data , struct websrv_context ) ;
2005-08-20 08:38:35 +04:00
struct stream_connection * conn = web - > conn ;
web - > conn = NULL ;
/* TODO: send a message to any running esp context on this connection
to stop running */
stream_terminate_connection ( conn , " websrv_timeout: timed out " ) ;
2005-05-26 05:06:32 +04:00
}
2008-09-21 18:53:29 +04:00
/*
setup for a raw http level error
*/
2008-09-21 21:03:12 +04:00
void http_error ( struct websrv_context * web , const char * status , const char * info )
2008-09-21 18:53:29 +04:00
{
char * s ;
2008-09-21 21:03:12 +04:00
s = talloc_asprintf ( web , " <HTML><HEAD><TITLE>Error %s</TITLE></HEAD><BODY><H1>Error %s</H1><pre>%s</pre><p></BODY></HTML> \r \n \r \n " ,
status , status , info ) ;
2008-09-21 18:53:29 +04:00
if ( s = = NULL ) {
stream_terminate_connection ( web - > conn , " http_error: out of memory " ) ;
return ;
}
2008-09-21 21:03:12 +04:00
websrv_output_headers ( web , status , NULL ) ;
websrv_output ( web , s , strlen ( s ) ) ;
2008-09-21 18:53:29 +04:00
}
2008-09-21 20:45:09 +04:00
void websrv_output_headers ( struct websrv_context * web , const char * status , struct http_header * headers )
{
2008-09-21 21:03:12 +04:00
char * s ;
DATA_BLOB b ;
struct http_header * hdr ;
s = talloc_asprintf ( web , " HTTP/1.0 %s \r \n " , status ) ;
if ( s = = NULL ) return ;
for ( hdr = headers ; hdr ; hdr = hdr - > next ) {
s = talloc_asprintf_append_buffer ( s , " %s: %s \r \n " , hdr - > name , hdr - > value ) ;
}
s = talloc_asprintf_append_buffer ( s , " \r \n " ) ;
b = web - > output . content ;
web - > output . content = data_blob_string_const ( s ) ;
websrv_output ( web , b . data , b . length ) ;
data_blob_free ( & b ) ;
}
void websrv_output ( struct websrv_context * web , void * data , size_t length )
{
data_blob_append ( web , & web - > output . content , data , length ) ;
EVENT_FD_NOT_READABLE ( web - > conn - > event . fde ) ;
EVENT_FD_WRITEABLE ( web - > conn - > event . fde ) ;
web - > output . output_pending = true ;
2008-09-21 20:45:09 +04:00
}
2008-09-21 18:53:29 +04:00
/*
parse one line of header input
*/
NTSTATUS http_parse_header ( struct websrv_context * web , const char * line )
{
if ( line [ 0 ] = = 0 ) {
web - > input . end_of_headers = true ;
} else if ( strncasecmp ( line , " GET " , 4 ) = = 0 ) {
web - > input . url = talloc_strndup ( web , & line [ 4 ] , strcspn ( & line [ 4 ] , " \t " ) ) ;
} else if ( strncasecmp ( line , " POST " , 5 ) = = 0 ) {
web - > input . post_request = true ;
web - > input . url = talloc_strndup ( web , & line [ 5 ] , strcspn ( & line [ 5 ] , " \t " ) ) ;
} else if ( strchr ( line , ' : ' ) = = NULL ) {
2008-09-21 21:03:12 +04:00
http_error ( web , " 400 Bad request " , " This server only accepts GET and POST requests " ) ;
2008-09-21 18:53:29 +04:00
return NT_STATUS_INVALID_PARAMETER ;
} else if ( strncasecmp ( line , " Content-Length: " , 16 ) = = 0 ) {
web - > input . content_length = strtoul ( & line [ 16 ] , NULL , 10 ) ;
} else {
struct http_header * hdr = talloc_zero ( web , struct http_header ) ;
char * colon = strchr ( line , ' : ' ) ;
if ( colon = = NULL ) {
2008-09-21 21:03:12 +04:00
http_error ( web , " 500 Internal Server Error " , " invalidly formatted header " ) ;
2008-09-21 18:53:29 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
hdr - > name = talloc_strndup ( hdr , line , colon - line ) ;
hdr - > value = talloc_strdup ( hdr , colon + 1 ) ;
DLIST_ADD ( web - > input . headers , hdr ) ;
}
/* ignore all other headers for now */
return NT_STATUS_OK ;
}
2005-05-26 05:06:32 +04:00
/*
called when a web connection becomes readable
*/
static void websrv_recv ( struct stream_connection * conn , uint16_t flags )
{
2008-09-21 20:45:09 +04:00
struct web_server_data * wdata ;
2009-02-02 12:30:03 +03:00
struct websrv_context * web = talloc_get_type ( conn - > private_data ,
2005-05-26 05:06:32 +04:00
struct websrv_context ) ;
NTSTATUS status ;
uint8_t buf [ 1024 ] ;
size_t nread ;
uint8_t * p ;
DATA_BLOB b ;
/* not the most efficient http parser ever, but good enough for us */
2006-05-03 00:15:47 +04:00
status = socket_recv ( conn - > socket , buf , sizeof ( buf ) , & nread ) ;
2005-05-26 05:06:32 +04:00
if ( NT_STATUS_IS_ERR ( status ) ) goto failed ;
if ( ! NT_STATUS_IS_OK ( status ) ) return ;
2007-08-29 17:07:03 +04:00
if ( ! data_blob_append ( web , & web - > input . partial , buf , nread ) )
goto failed ;
2005-05-26 05:06:32 +04:00
/* parse any lines that are available */
b = web - > input . partial ;
while ( ! web - > input . end_of_headers & &
2007-09-08 20:46:30 +04:00
( p = ( uint8_t * ) memchr ( b . data , ' \n ' , b . length ) ) ) {
2005-10-12 10:10:23 +04:00
const char * line = ( const char * ) b . data ;
2005-05-26 05:06:32 +04:00
* p = 0 ;
if ( p ! = b . data & & p [ - 1 ] = = ' \r ' ) {
p [ - 1 ] = 0 ;
}
status = http_parse_header ( web , line ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) return ;
b . length - = ( p - b . data ) + 1 ;
b . data = p + 1 ;
}
/* keep any remaining bytes in web->input.partial */
if ( b . length = = 0 ) {
b . data = NULL ;
}
b = data_blob_talloc ( web , b . data , b . length ) ;
data_blob_free ( & web - > input . partial ) ;
web - > input . partial = b ;
/* we finish when we have both the full headers (terminated by
a blank line ) and any post data , as indicated by the
content_length */
if ( web - > input . end_of_headers & &
2005-08-05 19:34:31 +04:00
web - > input . partial . length > = web - > input . content_length ) {
if ( web - > input . partial . length > web - > input . content_length ) {
web - > input . partial . data [ web - > input . content_length ] = 0 ;
}
2005-05-26 05:06:32 +04:00
EVENT_FD_NOT_READABLE ( web - > conn - > event . fde ) ;
2005-08-20 08:38:35 +04:00
/* the reference/unlink code here is quite subtle. It
is needed because the rendering of the web - pages , and
in particular the esp / ejs backend , is semi - async . So
we could well end up in the connection timeout code
while inside http_process_input ( ) , but we must not
destroy the stack variables being used by that
rendering process when we handle the timeout . */
2006-09-27 10:39:56 +04:00
if ( ! talloc_reference ( web - > task , web ) ) goto failed ;
2009-02-02 12:21:51 +03:00
wdata = talloc_get_type ( web - > task - > private_data , struct web_server_data ) ;
2008-09-21 20:45:09 +04:00
if ( wdata = = NULL ) goto failed ;
wdata - > http_process_input ( wdata , web ) ;
2005-08-20 08:38:35 +04:00
talloc_unlink ( web - > task , web ) ;
2005-05-26 05:06:32 +04:00
}
return ;
failed :
2005-07-12 10:17:19 +04:00
stream_terminate_connection ( conn , " websrv_recv: failed " ) ;
2005-05-26 05:06:32 +04:00
}
2008-09-21 20:45:09 +04:00
2005-05-26 05:06:32 +04:00
/*
called when a web connection becomes writable
*/
static void websrv_send ( struct stream_connection * conn , uint16_t flags )
{
2009-02-02 12:30:03 +03:00
struct websrv_context * web = talloc_get_type ( conn - > private_data ,
2005-05-26 05:06:32 +04:00
struct websrv_context ) ;
NTSTATUS status ;
size_t nsent ;
DATA_BLOB b ;
b = web - > output . content ;
b . data + = web - > output . nsent ;
b . length - = web - > output . nsent ;
2006-05-03 00:15:47 +04:00
status = socket_send ( conn - > socket , & b , & nsent ) ;
2005-05-26 05:06:32 +04:00
if ( NT_STATUS_IS_ERR ( status ) ) {
stream_terminate_connection ( web - > conn , " socket_send: failed " ) ;
return ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
return ;
}
web - > output . nsent + = nsent ;
2008-09-21 20:45:09 +04:00
if ( web - > output . content . length = = web - > output . nsent ) {
2005-07-12 10:17:19 +04:00
stream_terminate_connection ( web - > conn , " websrv_send: finished sending " ) ;
2005-05-26 05:06:32 +04:00
}
}
/*
establish a new connection to the web server
*/
static void websrv_accept ( struct stream_connection * conn )
{
2009-02-02 12:30:03 +03:00
struct task_server * task = talloc_get_type ( conn - > private_data , struct task_server ) ;
2009-02-02 12:21:51 +03:00
struct web_server_data * wdata = talloc_get_type ( task - > private_data , struct web_server_data ) ;
2005-05-26 05:06:32 +04:00
struct websrv_context * web ;
2006-07-28 07:51:20 +04:00
struct socket_context * tls_socket ;
2005-05-26 05:06:32 +04:00
web = talloc_zero ( conn , struct websrv_context ) ;
if ( web = = NULL ) goto failed ;
2005-05-27 04:29:58 +04:00
web - > task = task ;
2005-05-26 05:06:32 +04:00
web - > conn = conn ;
2009-02-02 12:30:03 +03:00
conn - > private_data = web ;
2005-05-26 05:06:32 +04:00
talloc_set_destructor ( web , websrv_destructor ) ;
event_add_timed ( conn - > event . ctx , web ,
timeval_current_ofs ( HTTP_TIMEOUT , 0 ) ,
websrv_timeout , web ) ;
2005-05-27 15:57:14 +04:00
2006-05-03 00:15:47 +04:00
/* Overwrite the socket with a (possibly) TLS socket */
2008-05-24 20:13:30 +04:00
tls_socket = tls_init_server ( wdata - > tls_params , conn - > socket ,
2006-07-28 07:51:20 +04:00
conn - > event . fde , " GPHO " ) ;
/* We might not have TLS, or it might not have initilised */
if ( tls_socket ) {
talloc_unlink ( conn , conn - > socket ) ;
talloc_steal ( conn , tls_socket ) ;
conn - > socket = tls_socket ;
} else {
DEBUG ( 3 , ( " TLS not available for web_server connections \n " ) ) ;
}
2005-05-27 15:57:14 +04:00
2005-05-26 05:06:32 +04:00
return ;
failed :
talloc_free ( conn ) ;
}
static const struct stream_server_ops web_stream_ops = {
. name = " web " ,
. accept_connection = websrv_accept ,
. recv_handler = websrv_recv ,
. send_handler = websrv_send ,
} ;
/*
startup the web server task
*/
static void websrv_task_init ( struct task_server * task )
{
NTSTATUS status ;
2007-12-02 19:09:52 +03:00
uint16_t port = lp_web_port ( task - > lp_ctx ) ;
2005-05-26 05:06:32 +04:00
const struct model_ops * model_ops ;
2008-05-24 20:13:30 +04:00
struct web_server_data * wdata ;
2005-05-26 05:06:32 +04:00
2006-03-09 20:48:41 +03:00
task_server_set_title ( task , " task[websrv] " ) ;
2005-05-26 05:06:32 +04:00
/* run the web server as a single process */
2008-09-22 20:15:24 +04:00
model_ops = process_model_startup ( task - > event_ctx , " single " ) ;
2005-05-26 05:06:32 +04:00
if ( ! model_ops ) goto failed ;
2007-12-02 19:09:52 +03:00
if ( lp_interfaces ( task - > lp_ctx ) & & lp_bind_interfaces_only ( task - > lp_ctx ) ) {
2007-12-12 00:23:14 +03:00
int num_interfaces ;
2005-05-26 05:06:32 +04:00
int i ;
2007-12-12 00:23:14 +03:00
struct interface * ifaces ;
2007-12-12 00:23:20 +03:00
load_interfaces ( NULL , lp_interfaces ( task - > lp_ctx ) , & ifaces ) ;
2007-12-12 00:23:14 +03:00
num_interfaces = iface_count ( ifaces ) ;
2005-05-26 05:06:32 +04:00
for ( i = 0 ; i < num_interfaces ; i + + ) {
2007-12-12 00:23:14 +03:00
const char * address = iface_n_ip ( ifaces , i ) ;
2008-01-06 04:03:43 +03:00
status = stream_setup_socket ( task - > event_ctx ,
task - > lp_ctx , model_ops ,
2005-05-26 05:06:32 +04:00
& web_stream_ops ,
" ipv4 " , address ,
2007-12-06 18:54:34 +03:00
& port , lp_socket_options ( task - > lp_ctx ) ,
task ) ;
2005-05-26 05:06:32 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
}
2007-12-12 00:23:20 +03:00
talloc_free ( ifaces ) ;
2005-05-26 05:06:32 +04:00
} else {
2008-01-06 04:03:43 +03:00
status = stream_setup_socket ( task - > event_ctx , task - > lp_ctx ,
model_ops , & web_stream_ops ,
2007-12-02 19:09:52 +03:00
" ipv4 " , lp_socket_address ( task - > lp_ctx ) ,
2007-12-06 18:54:34 +03:00
& port , lp_socket_options ( task - > lp_ctx ) , task ) ;
2005-05-26 05:06:32 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
}
2005-05-27 04:29:58 +04:00
/* startup the esp processor - unfortunately we can't do this
per connection as that wouldn ' t allow for session variables */
2008-05-24 20:13:30 +04:00
wdata = talloc_zero ( task , struct web_server_data ) ;
if ( wdata = = NULL ) goto failed ;
2009-02-02 12:21:51 +03:00
task - > private_data = wdata ;
2008-05-24 20:13:30 +04:00
wdata - > tls_params = tls_initialise ( wdata , task - > lp_ctx ) ;
if ( wdata - > tls_params = = NULL ) goto failed ;
2008-09-21 20:45:09 +04:00
if ( ! wsgi_initialize ( wdata ) ) goto failed ;
2005-05-26 05:06:32 +04:00
return ;
failed :
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " websrv_task_init: failed to startup web server task " , true ) ;
2005-05-26 05:06:32 +04:00
}
/* called at smbd startup - register ourselves as a server service */
NTSTATUS server_service_web_init ( void )
{
2008-02-04 13:58:29 +03:00
return register_server_service ( " web " , websrv_task_init ) ;
2005-05-26 05:06:32 +04:00
}