(libT) experimental code to serve clutch/rpc via evhttpd

This commit is contained in:
Charles Kerr 2008-09-25 05:03:39 +00:00
parent 15ae0d49ad
commit bd5a2ae6b8
38 changed files with 347 additions and 6274 deletions

View File

@ -9,10 +9,9 @@ transmissioncli_SOURCES = cli.c
transmissioncli_LDADD = \
$(top_builddir)/libtransmission/libtransmission.a \
$(top_builddir)/third-party/libevent/libevent_core.la \
$(top_builddir)/third-party/libevent/libevent.la \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(top_builddir)/third-party/shttpd/libshttpd.a \
$(INTLLIBS) \
$(OPENSSL_LIBS) \
$(LIBCURL_LIBS) \

View File

@ -325,7 +325,6 @@ AC_CONFIG_FILES([Makefile
third-party/Makefile
third-party/miniupnp/Makefile
third-party/libnatpmp/Makefile
third-party/shttpd/Makefile
macosx/Makefile
wx/Makefile
wx/images/Makefile

View File

@ -11,10 +11,9 @@ bin_PROGRAMS = \
LDADD = \
$(top_builddir)/libtransmission/libtransmission.a \
$(top_builddir)/third-party/shttpd/libshttpd.a \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/libevent/libevent_core.la \
$(top_builddir)/third-party/libevent/libevent.la \
$(INTLLIBS) \
$(OPENSSL_LIBS) \
$(LIBCURL_LIBS) \

View File

@ -82,10 +82,9 @@ dist_man_MANS = transmission.1
transmission_LDADD = \
$(top_builddir)/libtransmission/libtransmission.a \
$(top_builddir)/third-party/libevent/libevent_core.la \
$(top_builddir)/third-party/libevent/libevent.la \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/shttpd/libshttpd.a \
$(GTK_LIBS) \
$(GIO_LIBS) \
$(LIBNOTIFY_LIBS) \

View File

@ -109,7 +109,6 @@ noinst_PROGRAMS = $(TESTS)
APPS_LDADD = \
./libtransmission.a \
$(top_builddir)/third-party/shttpd/libshttpd.a \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/libevent/libevent.la \

View File

@ -15,12 +15,16 @@
#include <errno.h>
#include <stdlib.h> /* strtol */
#include <string.h>
#include <limits.h> /* INT_MAX */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> /* open */
#include <unistd.h> /* unlink */
#include <libevent/event.h>
#include <shttpd/defs.h> /* edit_passwords */
#include <shttpd/shttpd.h>
#include <libevent/evhttp.h>
#include "transmission.h"
#include "bencode.h"
@ -36,19 +40,26 @@
#define ACTIVE_INTERVAL_MSEC 40
#define INACTIVE_INTERVAL_MSEC 200
#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
struct acl_addr
{
char flag;
char parts[4][20];
};
struct tr_rpc_server
{
unsigned int isEnabled : 1;
unsigned int isPasswordEnabled : 1;
int port;
time_t lastRequestTime;
struct shttpd_ctx * ctx;
struct evhttp * httpd;
tr_handle * session;
struct event timer;
char * username;
char * password;
char * acl;
tr_list * connections;
char * acl_str;
tr_list * acl_list; /* struct acl_addr */
};
#define dbgmsg( fmt... ) tr_deepLog( __FILE__, __LINE__, MY_NAME, ## fmt )
@ -71,66 +82,106 @@ tr_memmem( const char * s1,
return NULL;
}
/**
***
**/
struct ConnBuf
{
char * key;
time_t lastActivity;
struct evbuffer * in;
struct evbuffer * out;
};
/****
***** ACL UTILITIES
****/
static char*
buildKey( struct shttpd_arg * arg )
static int
parseAddress( const char * addr, struct acl_addr * setme, const char ** end )
{
return tr_strdup_printf( "%s %s",
shttpd_get_env( arg, "REMOTE_ADDR" ),
shttpd_get_env( arg, "REQUEST_URI" ) );
const char * pch;
memset( setme, 0, sizeof( struct acl_addr ) );
if( !addr ) return 0;
if(!((pch = strchr( addr, '.' )))) return 0;
if( pch-addr > 20 ) return 0;
memcpy( setme->parts[0], addr, pch-addr );
addr = pch + 1;
if(!((pch = strchr( addr, '.' )))) return 0;
if( pch-addr > 20 ) return 0;
memcpy( setme->parts[1], addr, pch-addr );
addr = pch + 1;
if(!((pch = strchr( addr, '.' )))) return 0;
if( pch-addr > 20 ) return 0;
memcpy( setme->parts[2], addr, pch-addr );
addr = pch + 1;
while( *pch && *pch!=',' ) ++pch;
if( pch-addr > 20 ) return 0;
memcpy( setme->parts[3], addr, pch-addr );
*end = pch;
return 1;
}
static struct ConnBuf*
getBuffer(
tr_rpc_server * server,
struct shttpd_arg * arg )
static int
testAddress( const struct acl_addr * ref,
const struct acl_addr * testme )
{
tr_list * l;
char * key = buildKey( arg );
struct ConnBuf * found = NULL;
int i;
for( l = server->connections; l && !found; l = l->next )
for( i=0; i<4; ++i )
{
struct ConnBuf * buf = l->data;
if( !strcmp( key, buf->key ) )
found = buf;
const char * a = ref->parts[i];
const char * b = testme->parts[i];
if( strcmp( a, "*" ) && strcmp( a, b ) ) return 0;
}
if( found == NULL )
{
found = tr_new0( struct ConnBuf, 1 );
found->lastActivity = time( NULL );
found->key = tr_strdup( key );
found->in = evbuffer_new( );
found->out = evbuffer_new( );
tr_list_append( &server->connections, found );
}
tr_free( key );
return found;
return 1;
}
static void
pruneBuf( tr_rpc_server * server,
struct ConnBuf * buf )
static int
isAddressAllowed( const tr_rpc_server * server,
const char * address )
{
tr_list_remove_data( &server->connections, buf );
struct acl_addr tmp;
const char * end;
const int parsed = parseAddress( address, &tmp, &end );
tr_list * l;
evbuffer_free( buf->in );
evbuffer_free( buf->out );
tr_free( buf->key );
tr_free( buf );
if( !parsed )
return 0;
for( l=server->acl_list; l; l=l->next ) {
const struct acl_addr * a = l->data;
if( testAddress( a, &tmp ) ) {
return a->flag == '+';
}
}
return 0;
}
static tr_list*
parseACL( const char * acl, int * err )
{
tr_list * list = NULL;
*err = 0;
for( ;; )
{
const char flag = *acl++;
struct acl_addr tmp;
if( ( flag!='+' && flag!='-' ) || !parseAddress( acl, &tmp, &acl ) )
{
*err = 1;
tr_list_free( &list, tr_free );
return NULL;
}
tmp.flag = flag;
tr_list_append( &list, tr_memdup( &tmp, sizeof( struct acl_addr ) ) );
if( !*acl )
return list;
++acl;
}
}
/**
@ -138,50 +189,30 @@ pruneBuf( tr_rpc_server * server,
**/
static void
handle_upload( struct shttpd_arg * arg )
handle_upload( struct evhttp_request * req, struct tr_rpc_server * server )
{
struct tr_rpc_server * s;
struct ConnBuf * cbuf;
s = arg->user_data;
s->lastRequestTime = time( NULL );
cbuf = getBuffer( s, arg );
/* if we haven't parsed the POST, do that now */
if( !EVBUFFER_LENGTH( cbuf->out ) )
if( req->type != EVHTTP_REQ_POST )
{
evhttp_send_reply( req, HTTP_BADREQUEST, "Bad Request", NULL );
}
else
{
const char * query_string;
const char * content_type;
const char * delim;
const char * in;
size_t inlen;
char * boundary;
size_t boundary_len;
char buf[64];
int paused;
/* if we haven't finished reading the POST, read more now */
evbuffer_add( cbuf->in, arg->in.buf, arg->in.len );
arg->in.num_bytes = arg->in.len;
if( arg->flags & SHTTPD_MORE_POST_DATA )
return;
const char * content_type = evhttp_find_header( req->input_headers, "Content-Type" );
query_string = shttpd_get_env( arg, "QUERY_STRING" );
content_type = shttpd_get_header( arg, "Content-Type" );
in = (const char *) EVBUFFER_DATA( cbuf->in );
inlen = EVBUFFER_LENGTH( cbuf->in );
boundary =
const char * query = strchr( req->uri, '?' );
const int paused = query && strstr( query+1, "paused=true" );
const char * in = (const char *) EVBUFFER_DATA( req->input_buffer );
size_t inlen = EVBUFFER_LENGTH( req->input_buffer );
char * boundary =
tr_strdup_printf( "--%s", strstr( content_type,
"boundary=" ) +
strlen( "boundary=" ) );
boundary_len = strlen( boundary );
paused = ( query_string != NULL )
&& ( shttpd_get_var( "paused", query_string,
strlen( query_string ), buf,
sizeof( buf ) ) == 4 )
&& ( !strcmp( buf, "true" ) );
const size_t boundary_len = strlen( boundary );
delim = tr_memmem( in, inlen, boundary, boundary_len );
const char * delim = tr_memmem( in, inlen, boundary, boundary_len );
if( delim ) do
{
size_t part_len;
@ -219,8 +250,8 @@ handle_upload( struct shttpd_arg * arg )
tr_bencDictAddInt( args, "paused", paused );
json = tr_bencSaveAsJSON( &top, &json_len );
freeme =
tr_rpc_request_exec_json( s->session, json,
json_len,
tr_rpc_request_exec_json( server->session,
json, json_len,
NULL );
tr_free( freeme );
@ -234,249 +265,223 @@ handle_upload( struct shttpd_arg * arg )
}
while( delim );
evbuffer_drain( cbuf->in, EVBUFFER_LENGTH( cbuf->in ) );
tr_free( boundary );
{
/* use xml here because json responses to file uploads is trouble.
* see http://www.malsup.com/jquery/form/#sample7 for details */
const char * response = "<result>success</result>";
const int len = strlen( response );
evbuffer_add_printf(
cbuf->out, "HTTP/1.1 200 OK\r\n"
"Content-Type: text/xml; charset=UTF-8\r\n"
"Content-Length: %d\r\n"
"\r\n"
"%s\r\n", len, response );
}
}
if( EVBUFFER_LENGTH( cbuf->out ) )
{
const int n = MIN( ( int )EVBUFFER_LENGTH(
cbuf->out ), arg->out.len );
memcpy( arg->out.buf, EVBUFFER_DATA( cbuf->out ), n );
evbuffer_drain( cbuf->out, n );
arg->out.num_bytes = n;
}
if( !EVBUFFER_LENGTH( cbuf->out ) )
{
arg->flags |= SHTTPD_END_OF_OUTPUT;
pruneBuf( s, cbuf );
/* use xml here because json responses to file uploads is trouble.
* see http://www.malsup.com/jquery/form/#sample7 for details */
evhttp_add_header(req->output_headers, "Content-Type", "text/xml; charset=UTF-8" );
evhttp_send_reply( req, HTTP_OK, "Success", NULL );
}
}
static void
handle_root( struct shttpd_arg * arg )
static const char*
mimetype_guess( const char * path )
{
const char * redirect = "HTTP/1.1 200 OK" "\r\n"
"Content-Type: text/html; charset=UTF-8"
"\r\n"
"\r\n"
"<html><head>"
"\r\n"
" <meta http-equiv=\"Refresh\" content=\"2; url=/transmission/web/\">"
"\r\n"
"</head><body>"
"\r\n"
" <p>redirecting to <a href=\"/transmission/web\">/transmission/web/</a></p>"
"\r\n"
"</body></html>"
"\r\n";
const size_t n = strlen( redirect );
unsigned int i;
const struct {
const char * suffix;
const char * mime_type;
} types[] = {
/* these are just the ones we need for serving clutch... */
{ "css", "text/css" },
{ "gif", "image/gif" },
{ "html", "text/html" },
{ "ico", "image/vnd.microsoft.icon" },
{ "js", "application/javascript" },
{ "png", "image/png" }
};
const char * dot = strrchr( path, '.' );
memcpy( arg->out.buf, redirect, n );
arg->in.num_bytes = arg->in.len;
arg->out.num_bytes = n;
arg->flags |= SHTTPD_END_OF_OUTPUT;
for( i=0; dot && i<TR_N_ELEMENTS(types); ++i )
if( !strcmp( dot+1, types[i].suffix ) )
return types[i].mime_type;
return "application/octet-stream";
}
static void
handle_rpc( struct shttpd_arg * arg )
serve_file( struct evhttp_request * req, const char * path )
{
struct tr_rpc_server * s;
struct ConnBuf * cbuf;
s = arg->user_data;
s->lastRequestTime = time( NULL );
cbuf = getBuffer( s, arg );
if( !EVBUFFER_LENGTH( cbuf->out ) )
if( req->type != EVHTTP_REQ_GET )
{
int len = 0;
char * response = NULL;
const char * request_method = shttpd_get_env( arg, "REQUEST_METHOD" );
const char * query_string = shttpd_get_env( arg, "QUERY_STRING" );
if( query_string && *query_string )
response = tr_rpc_request_exec_uri( s->session,
query_string,
strlen( query_string ),
&len );
else if( !strcmp( request_method, "POST" ) )
{
evbuffer_add( cbuf->in, arg->in.buf, arg->in.len );
arg->in.num_bytes = arg->in.len;
if( arg->flags & SHTTPD_MORE_POST_DATA )
return;
response = tr_rpc_request_exec_json( s->session,
EVBUFFER_DATA( cbuf->in ),
EVBUFFER_LENGTH( cbuf->in ),
&len );
evbuffer_drain( cbuf->in, EVBUFFER_LENGTH( cbuf->in ) );
}
evbuffer_add_printf(
cbuf->out, "HTTP/1.1 200 OK\r\n"
"Content-Type: application/json; charset=UTF-8\r\n"
"Content-Length: %d\r\n"
"\r\n"
"%*.*s", len, len, len, response );
tr_free( response );
evhttp_add_header(req->output_headers, "Allow", "GET");
evhttp_send_reply(req, 405, "Method Not Allowed", NULL);
}
if( EVBUFFER_LENGTH( cbuf->out ) )
{
const int n = MIN( ( int )EVBUFFER_LENGTH(
cbuf->out ), arg->out.len );
memcpy( arg->out.buf, EVBUFFER_DATA( cbuf->out ), n );
evbuffer_drain( cbuf->out, n );
arg->out.num_bytes = n;
}
if( !EVBUFFER_LENGTH( cbuf->out ) )
{
arg->flags |= SHTTPD_END_OF_OUTPUT;
pruneBuf( s, cbuf );
}
}
static void
rpcPulse( int socket UNUSED,
short action UNUSED,
void * vserver )
{
int interval;
struct timeval tv;
tr_rpc_server * server = vserver;
const time_t now = time( NULL );
assert( server );
if( server->ctx )
shttpd_poll( server->ctx, 1 );
/* set a timer for the next pulse */
if( now - server->lastRequestTime < 300 )
interval = ACTIVE_INTERVAL_MSEC;
else
interval = INACTIVE_INTERVAL_MSEC;
tv = tr_timevalMsec( interval );
evtimer_add( &server->timer, &tv );
{
const int fd = open( path, O_RDONLY, 0 );
if( fd != -1 )
{
char size[12];
struct evbuffer * buf = evbuffer_new();
evbuffer_read(buf, fd, INT_MAX );
evhttp_add_header(req->output_headers, "Content-Type", mimetype_guess( path ) );
snprintf(size, sizeof(size), "%zu", EVBUFFER_LENGTH( buf ) );
evhttp_add_header(req->output_headers, "Content-Length", size );
evhttp_send_reply(req, HTTP_OK, "OK", buf);
evbuffer_free(buf);
close( fd );
}
else
{
struct evbuffer * buf = evbuffer_new();
evbuffer_add_printf(buf, "<h1>Not Found</h1>");
evhttp_send_reply(req, HTTP_NOTFOUND, "Not Found", buf);
evbuffer_free(buf);
}
}
}
static void
getPasswordFile( tr_rpc_server * server,
char * buf,
int buflen )
handle_clutch( struct evhttp_request * req, struct tr_rpc_server * server )
{
tr_buildPath( buf, buflen, tr_sessionGetConfigDir( server->session ),
"htpasswd",
NULL );
const char * uri;
struct evbuffer * buf = evbuffer_new( );
assert( !strncmp( req->uri, "/transmission/web/", 18 ) );
evbuffer_add_printf( buf, "%s%s", tr_getClutchDir( server->session ), TR_PATH_DELIMITER_STR );
uri = req->uri + 18;
if( !*uri || (*uri=='?') )
evbuffer_add_printf( buf, "index.html" );
else {
const char * pch;
if(( pch = strchr( uri, '?' )))
evbuffer_add_printf( buf, "%*.*s", (int)(pch-uri), (int)(pch-uri), uri );
else
evbuffer_add_printf( buf, "%s", uri );
}
if( strstr( (const char *)EVBUFFER_DATA( buf ), ".." ) )
serve_file( req, "/dev/null" );
else
serve_file( req, (const char *)EVBUFFER_DATA( buf ) );
evbuffer_free( buf );
}
static void
handle_rpc( struct evhttp_request * req, struct tr_rpc_server * server )
{
int len = 0;
char * response;
struct evbuffer * buf;
if( req->type == EVHTTP_REQ_GET )
{
const char * q;
if(( q = strchr( req->uri, '?' )))
response = tr_rpc_request_exec_uri( server->session,
q + 1,
strlen( q + 1 ),
&len );
}
else if( req->type == EVHTTP_REQ_POST )
{
response = tr_rpc_request_exec_json( server->session,
EVBUFFER_DATA( req->input_buffer ),
EVBUFFER_LENGTH( req->input_buffer ),
&len );
}
buf = evbuffer_new( );
evbuffer_add( buf, response, len );
evhttp_add_header( req->output_headers, "Content-Type", "application/json; charset=UTF-8" );
evhttp_send_reply( req, HTTP_OK, "OK", buf );
evbuffer_free( buf );
}
static void
handle_request( struct evhttp_request * req, void * arg )
{
struct tr_rpc_server * server = arg;
if (req && req->evcon )
{
const char * auth;
char * user = NULL;
char * pass = NULL;
evhttp_add_header( req->output_headers, "Server", "Transmission" );
auth = evhttp_find_header( req->input_headers, "Authorization" );
if( auth && !strncasecmp( auth, "basic ", 6 ) )
{
int plen;
char * p = tr_base64_decode( auth + 6, 0, &plen );
if( p && plen && (( pass = strchr( p, ':' )))) {
user = p;
*pass++ = '\0';
}
}
if( server->acl_list && !isAddressAllowed( server, req->remote_host ) )
{
struct evbuffer * buf = evbuffer_new();
evbuffer_add_printf(buf, "<h1>Unauthorized IP Address</h1>");
evhttp_send_reply(req, 401, "Unauthorized", buf );
}
else if( server->isPasswordEnabled && ( !pass
|| !user
|| strcmp( server->username, user )
|| strcmp( server->password, pass ) ) )
{
struct evbuffer * buf = evbuffer_new();
evhttp_add_header( req->output_headers,
"WWW-Authenticate", "Basic realm=\"Transmission\"");
evbuffer_add_printf(buf, "<h1>Unauthorized User</h1>");
evhttp_send_reply(req, 401, "Unauthorized", buf);
}
else if( !strcmp( req->uri, "/transmission/web" ) ||
!strcmp( req->uri, "/transmission/clutch" ) ||
!strcmp( req->uri, "/" ) )
{
evhttp_add_header( req->output_headers, "Location", "/transmission/web/" );
evhttp_send_reply(req, HTTP_MOVEPERM, "Moved Permanently", NULL );
}
else if( !strncmp( req->uri, "/transmission/web/", 18 ) )
{
handle_clutch( req, server );
}
else if( !strncmp( req->uri, "/transmission/rpc", 17 ) )
{
handle_rpc( req, server );
}
else if( !strncmp( req->uri, "/transmission/upload", 20 ) )
{
handle_upload( req, server );
}
else
{
struct evbuffer *buf = evbuffer_new( );
evbuffer_add_printf(buf, "<h1>Not Found</h1>");
evhttp_send_reply(req, HTTP_NOTFOUND, "Not Found", buf);
}
tr_free( user );
}
}
static void
startServer( tr_rpc_server * server )
{
dbgmsg( "in startServer; current context is %p", server->ctx );
dbgmsg( "in startServer; current context is %p", server->httpd );
if( !server->ctx )
{
int i;
int argc = 0;
char * argv[100];
char passwd[MAX_PATH_LENGTH];
const char * clutchDir = tr_getClutchDir( server->session );
struct timeval tv = tr_timevalMsec( INACTIVE_INTERVAL_MSEC );
getPasswordFile( server, passwd, sizeof( passwd ) );
if( !server->isPasswordEnabled )
unlink( passwd );
else
shttpd_edit_passwords( passwd, MY_REALM, server->username,
server->password );
argv[argc++] = tr_strdup( "appname-unused" );
argv[argc++] = tr_strdup( "-ports" );
argv[argc++] = tr_strdup_printf( "%d", server->port );
argv[argc++] = tr_strdup( "-dir_list" );
argv[argc++] = tr_strdup( "0" );
argv[argc++] = tr_strdup( "-auth_realm" );
argv[argc++] = tr_strdup( MY_REALM );
argv[argc++] = tr_strdup( "-root" );
argv[argc++] = tr_strdup( "/dev/null" );
if( server->acl )
{
argv[argc++] = tr_strdup( "-acl" );
argv[argc++] = tr_strdup( server->acl );
}
if( server->isPasswordEnabled )
{
argv[argc++] = tr_strdup( "-protect" );
argv[argc++] = tr_strdup_printf( "/transmission=%s", passwd );
}
if( clutchDir && *clutchDir )
{
tr_inf( _(
"Serving the web interface files from \"%s\"" ),
clutchDir );
argv[argc++] = tr_strdup( "-aliases" );
argv[argc++] = tr_strdup_printf( "%s=%s,%s=%s",
"/transmission/clutch",
clutchDir,
"/transmission/web",
clutchDir );
}
argv[argc] = NULL; /* shttpd_init() wants it null-terminated */
if( ( server->ctx = shttpd_init( argc, argv ) ) )
{
shttpd_register_uri( server->ctx, "/transmission/rpc",
handle_rpc,
server );
shttpd_register_uri( server->ctx, "/transmission/upload",
handle_upload,
server );
shttpd_register_uri( server->ctx, "/", handle_root, server );
evtimer_set( &server->timer, rpcPulse, server );
evtimer_add( &server->timer, &tv );
}
for( i = 0; i < argc; ++i )
tr_free( argv[i] );
}
if( !server->httpd )
if( ( server->httpd = evhttp_start( "0.0.0.0", server->port ) ) )
evhttp_set_gencb( server->httpd, handle_request, server );
}
static void
stopServer( tr_rpc_server * server )
{
if( server->ctx )
if( server->httpd )
{
char passwd[MAX_PATH_LENGTH];
getPasswordFile( server, passwd, sizeof( passwd ) );
unlink( passwd );
evtimer_del( &server->timer );
shttpd_fini( server->ctx );
server->ctx = NULL;
evhttp_free( server->httpd );
server->httpd = NULL;
}
}
@ -495,7 +500,7 @@ tr_rpcSetEnabled( tr_rpc_server * server,
int
tr_rpcIsEnabled( const tr_rpc_server * server )
{
return server->ctx != NULL;
return server->httpd != NULL;
}
void
@ -520,146 +525,38 @@ tr_rpcGetPort( const tr_rpc_server * server )
return server->port;
}
/****
***** ACL
****/
/*
* FOR_EACH_WORD_IN_LIST, isbyte, and testACL are from, or modified from,
* shttpd, written by Sergey Lyubka under this license:
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#define FOR_EACH_WORD_IN_LIST( s, len ) \
for( ; s != NULL && ( len = strcspn( s, DELIM_CHARS ) ) != 0; \
s += len, s += strspn( s, DELIM_CHARS ) )
static int
isbyte( int n ) { return n >= 0 && n <= 255; }
static char*
testACL( const char * s )
{
int len;
FOR_EACH_WORD_IN_LIST( s, len )
{
char flag;
int a, b, c, d, n, mask;
if( sscanf( s, "%c%d.%d.%d.%d%n", &flag, &a, &b, &c, &d, &n ) != 5 )
return tr_strdup_printf( _(
"[%s]: subnet must be [+|-]x.x.x.x[/x]" ),
s );
if( flag != '+' && flag != '-' )
return tr_strdup_printf( _( "[%s]: flag must be + or -" ), s );
if( !isbyte( a ) || !isbyte( b ) || !isbyte( c ) || !isbyte( d ) )
return tr_strdup_printf( _( "[%s]: bad ip address" ), s );
if( sscanf( s + n, "/%d", &mask ) == 1 && ( mask < 0 || mask > 32 ) )
return tr_strdup_printf( _( "[%s]: bad subnet mask %d" ), s, n );
}
return NULL;
}
/* 192.*.*.* --> 192.0.0.0/8
192.64.*.* --> 192.64.0.0/16
192.64.1.* --> 192.64.1.0/24
192.64.1.2 --> 192.64.1.2/32 */
static void
cidrizeOne( const char * in,
int len,
struct evbuffer * out )
{
int stars = 0;
const char * pch;
const char * end;
char zero = '0';
char huh = '?';
for( pch = in, end = pch + len; pch != end; ++pch )
{
if( stars && isdigit( *pch ) )
evbuffer_add( out, &huh, 1 );
else if( *pch != '*' )
evbuffer_add( out, pch, 1 );
else
{
evbuffer_add( out, &zero, 1 );
++stars;
}
}
evbuffer_add_printf( out, "/%d", ( 32 - ( stars * 8 ) ) );
}
char*
cidrize( const char * acl )
{
int len;
const char * walk = acl;
char * ret;
struct evbuffer * out = evbuffer_new( );
FOR_EACH_WORD_IN_LIST( walk, len )
{
cidrizeOne( walk, len, out );
evbuffer_add_printf( out, "," );
}
/* the -1 is to eat the final ", " */
ret = tr_strndup( EVBUFFER_DATA( out ), EVBUFFER_LENGTH( out ) - 1 );
evbuffer_free( out );
return ret;
}
int
tr_rpcTestACL( const tr_rpc_server * server UNUSED,
const char * acl,
char ** setme_errmsg )
{
int err = 0;
char * cidr = cidrize( acl );
char * errmsg = testACL( cidr );
if( errmsg )
{
if( setme_errmsg )
*setme_errmsg = errmsg;
else
tr_free( errmsg );
err = -1;
}
tr_free( cidr );
int err = 0;
tr_list * list = parseACL( acl, &err );
if( err )
*setme_errmsg = tr_strdup( "invalid ACL" );
tr_list_free( &list, tr_free );
return err;
}
int
tr_rpcSetACL( tr_rpc_server * server,
const char * acl,
const char * acl_str,
char ** setme_errmsg )
{
char * cidr = cidrize( acl );
const int err = tr_rpcTestACL( server, cidr, setme_errmsg );
int err = 0;
tr_list * list = parseACL( acl_str, &err );
if( !err )
if( err )
{
const int isEnabled = server->isEnabled;
if( isEnabled )
stopServer( server );
tr_free( server->acl );
server->acl = tr_strdup( cidr );
dbgmsg( "setting our ACL to [%s]", server->acl );
if( isEnabled )
startServer( server );
*setme_errmsg = tr_strdup( "invalid ACL" );
}
else
{
tr_free( server->acl_str );
tr_list_free( &server->acl_list, tr_free );
server->acl_str = tr_strdup( acl_str );
server->acl_list = list;
}
tr_free( cidr );
return err;
}
@ -667,7 +564,7 @@ tr_rpcSetACL( tr_rpc_server * server,
char*
tr_rpcGetACL( const tr_rpc_server * server )
{
return tr_strdup( server->acl ? server->acl : "" );
return tr_strdup( server->acl_str ? server->acl_str : "" );
}
/****
@ -678,17 +575,9 @@ void
tr_rpcSetUsername( tr_rpc_server * server,
const char * username )
{
const int isEnabled = server->isEnabled;
if( isEnabled )
stopServer( server );
tr_free( server->username );
server->username = tr_strdup( username );
dbgmsg( "setting our Username to [%s]", server->username );
if( isEnabled )
startServer( server );
}
char*
@ -701,17 +590,9 @@ void
tr_rpcSetPassword( tr_rpc_server * server,
const char * password )
{
const int isEnabled = server->isEnabled;
if( isEnabled )
stopServer( server );
tr_free( server->password );
server->password = tr_strdup( password );
dbgmsg( "setting our Password to [%s]", server->password );
if( isEnabled )
startServer( server );
}
char*
@ -724,16 +605,8 @@ void
tr_rpcSetPasswordEnabled( tr_rpc_server * server,
int isEnabled )
{
const int wasEnabled = server->isEnabled;
if( wasEnabled )
stopServer( server );
server->isPasswordEnabled = isEnabled;
dbgmsg( "setting 'password enabled' to %d", isEnabled );
if( isEnabled )
startServer( server );
}
int
@ -754,9 +627,10 @@ tr_rpcClose( tr_rpc_server ** ps )
*ps = NULL;
stopServer( s );
tr_list_free( &s->acl_list, tr_free );
tr_free( s->acl_str );
tr_free( s->username );
tr_free( s->password );
tr_free( s->acl );
tr_free( s );
}
@ -764,26 +638,26 @@ tr_rpc_server *
tr_rpcInit( tr_handle * session,
int isEnabled,
int port,
const char * acl,
const char * acl_str,
int isPasswordEnabled,
const char * username,
const char * password )
{
char * errmsg;
tr_rpc_server * s;
int err = 0;
tr_list * list = parseACL( acl_str, &err );
if( ( errmsg = testACL ( acl ) ) )
{
tr_nerr( MY_NAME, errmsg );
tr_free( errmsg );
acl = TR_DEFAULT_RPC_ACL;
tr_nerr( MY_NAME, "using fallback ACL \"%s\"", acl );
if( err ) {
acl_str = TR_DEFAULT_RPC_ACL;
tr_nerr( MY_NAME, "using fallback ACL \"%s\"", acl_str );
list = parseACL( acl_str, &err );
}
s = tr_new0( tr_rpc_server, 1 );
s->session = session;
s->port = port;
s->acl = tr_strdup( acl );
s->acl_str = tr_strdup( acl_str );
s->acl_list = list;
s->username = tr_strdup( username );
s->password = tr_strdup( password );
s->isPasswordEnabled = isPasswordEnabled != 0;
@ -793,4 +667,3 @@ tr_rpcInit( tr_handle * session,
startServer( s );
return s;
}

View File

@ -24,6 +24,7 @@ static int test = 0;
} \
}
#if 0
extern char* cidrize( const char * in );
extern int tr_rpcTestACL( const void * unused,
@ -42,10 +43,12 @@ testWildcard( const char * in,
tr_free( str );
return ok;
}
#endif
static int
test_acl( void )
{
#if 0
int err;
char * errmsg = NULL;
@ -67,6 +70,7 @@ test_acl( void )
err = tr_rpcTestACL( NULL, "+192.*.*.*,-192.168.*.*", &errmsg );
check( !err );
check( !errmsg );
#endif
return 0;
}

View File

@ -1,8 +1,7 @@
SUBDIRS = \
libevent \
libnatpmp \
miniupnp \
shttpd
miniupnp
EXTRA_DIST = \
macosx-libevent-config.h

View File

@ -1,5 +0,0 @@
"THE BEER-WARE LICENSE" (Revision 42):
Sergey Lyubka wrote this software. As long as you retain this notice you
can do whatever you want with this stuff. If we meet some day, and you think
this stuff is worth it, you can buy me a beer in return.

View File

@ -1,28 +0,0 @@
noinst_LIBRARIES = libshttpd.a
AM_CPPFLAGS = -DEMBEDDED -DNDEBUG -DNO_CGI -DNO_SSI
libshttpd_a_SOURCES = \
string.c shttpd.c log.c auth.c md5.c \
io_file.c io_socket.c io_ssl.c io_emb.c io_dir.c \
compat_unix.c compat_win32.c
noinst_HEADERS = \
defs.h llist.h shttpd.h std_includes.h io.h md5.h ssl.h \
compat_unix.h compat_rtems.h config.h
extra_DIST = \
README \
LICENSE
# Possible flags: (in brackets are rough numbers for 'gcc -O2' on i386)
# -DHAVE_MD5 - use system md5 library (-2kb)
# -DNDEBUG - strip off all debug code (-5kb)
# -D_DEBUG - build debug version (very noisy) (+6kb)
# -DNO_CGI - disable CGI support (-5kb)
# -DNO_SSL - disable SSL functionality (-2kb)
# -DNO_AUTH - disable authorization support (-4kb)
# -DCONFIG=\"file\" - use `file' as the default config file
# -DNO_SSI - disable SSI support (-4kb)

View File

@ -1,3 +0,0 @@
http://shttpd.sourceforge.net/
Sergey Lyubka wrote this software.
This snapshot is from shttpd-1.42

View File

@ -1,419 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
#if !defined(NO_AUTH)
/*
* Stringify binary data. Output buffer must be twice as big as input,
* because each byte takes 2 bytes in string representation
*/
static void
bin2str(char *to, const unsigned char *p, size_t len)
{
const char *hex = "0123456789abcdef";
for (;len--; p++) {
*to++ = hex[p[0] >> 4];
*to++ = hex[p[0] & 0x0f];
}
}
/*
* Return stringified MD5 hash for list of vectors.
* buf must point to at least 32-bytes long buffer
*/
static void
md5(char *buf, ...)
{
unsigned char hash[16];
const struct vec *v;
va_list ap;
MD5_CTX ctx;
int i;
MD5Init(&ctx);
va_start(ap, buf);
for (i = 0; (v = va_arg(ap, const struct vec *)) != NULL; i++) {
assert(v->len >= 0);
if (v->len == 0)
continue;
if (i > 0)
MD5Update(&ctx, (unsigned char *) ":", 1);
MD5Update(&ctx,(unsigned char *)v->ptr,(unsigned int)v->len);
}
va_end(ap);
MD5Final(hash, &ctx);
bin2str(buf, hash, sizeof(hash));
}
/*
* Compare to vectors. Return 1 if they are equal
*/
static int
vcmp(const struct vec *v1, const struct vec *v2)
{
return (v1->len == v2->len && !memcmp(v1->ptr, v2->ptr, v1->len));
}
struct digest {
struct vec user;
struct vec uri;
struct vec nonce;
struct vec cnonce;
struct vec resp;
struct vec qop;
struct vec nc;
};
static const struct auth_keyword {
size_t offset;
struct vec vec;
} known_auth_keywords[] = {
{offsetof(struct digest, user), {"username=", 9}},
{offsetof(struct digest, cnonce), {"cnonce=", 7}},
{offsetof(struct digest, resp), {"response=", 9}},
{offsetof(struct digest, uri), {"uri=", 4}},
{offsetof(struct digest, qop), {"qop=", 4}},
{offsetof(struct digest, nc), {"nc=", 3}},
{offsetof(struct digest, nonce), {"nonce=", 6}},
{0, {NULL, 0}}
};
static void
parse_authorization_header(const struct vec *h, struct digest *dig)
{
const unsigned char *p, *e, *s;
struct vec *v, vec;
const struct auth_keyword *kw;
(void) memset(dig, 0, sizeof(*dig));
p = (unsigned char *) h->ptr + 7;
e = (unsigned char *) h->ptr + h->len;
while (p < e) {
/* Skip spaces */
while (p < e && (*p == ' ' || *p == ','))
p++;
/* Skip to "=" */
for (s = p; s < e && *s != '='; )
s++;
s++;
/* Is it known keyword ? */
for (kw = known_auth_keywords; kw->vec.len > 0; kw++)
if (kw->vec.len <= s - p &&
!memcmp(p, kw->vec.ptr, kw->vec.len))
break;
if (kw->vec.len == 0)
v = &vec; /* Dummy placeholder */
else
v = (struct vec *) ((char *) dig + kw->offset);
if (*s == '"') {
p = ++s;
while (p < e && *p != '"')
p++;
} else {
p = s;
while (p < e && *p != ' ' && *p != ',')
p++;
}
v->ptr = (char *) s;
v->len = p - s;
if (*p == '"')
p++;
DBG(("auth field [%.*s]", v->len, v->ptr));
}
}
/*
* Check the user's password, return 1 if OK
*/
static int
check_password(int method, const struct vec *ha1, const struct digest *digest)
{
char a2[32], resp[32];
struct vec vec_a2;
/* XXX Due to a bug in MSIE, we do not compare the URI */
/* Also, we do not check for authentication timeout */
if (/*strcmp(dig->uri, c->ouri) != 0 || */
digest->resp.len != 32 /*||
now - strtoul(dig->nonce, NULL, 10) > 3600 */)
return (0);
md5(a2, &_shttpd_known_http_methods[method], &digest->uri, NULL);
vec_a2.ptr = a2;
vec_a2.len = sizeof(a2);
md5(resp, ha1, &digest->nonce, &digest->nc,
&digest->cnonce, &digest->qop, &vec_a2, NULL);
DBG(("%s: uri [%.*s] expected_resp [%.*s] resp [%.*s]",
"check_password", digest->uri.len, digest->uri.ptr,
32, resp, digest->resp.len, digest->resp.ptr));
return (!memcmp(resp, digest->resp.ptr, 32));
}
static FILE *
open_auth_file(struct shttpd_ctx *ctx, const char *path)
{
char name[FILENAME_MAX];
const char *p, *e;
FILE *fp = NULL;
int fd;
if (ctx->options[OPT_AUTH_GPASSWD] != NULL) {
/* Use global passwords file */
_shttpd_snprintf(name, sizeof(name), "%s",
ctx->options[OPT_AUTH_GPASSWD]);
} else {
/*
* Try to find .htpasswd in requested directory.
* Given the path, create the path to .htpasswd file
* in the same directory. Find the right-most
* directory separator character first. That would be the
* directory name. If directory separator character is not
* found, 'e' will point to 'p'.
*/
for (p = path, e = p + strlen(p) - 1; e > p; e--)
if (IS_DIRSEP_CHAR(*e))
break;
/*
* Make up the path by concatenating directory name and
* .htpasswd file name.
*/
(void) _shttpd_snprintf(name, sizeof(name), "%.*s/%s",
(int) (e - p), p, HTPASSWD);
}
if ((fd = _shttpd_open(name, O_RDONLY, 0)) == -1) {
DBG(("open_auth_file: open(%s)", name));
} else if ((fp = fdopen(fd, "r")) == NULL) {
DBG(("open_auth_file: fdopen(%s)", name));
(void) close(fd);
}
return (fp);
}
/*
* Parse the line from htpasswd file. Line should be in form of
* "user:domain:ha1". Fill in the vector values. Return 1 if successful.
*/
static int
parse_htpasswd_line(const char *s, struct vec *user,
struct vec *domain, struct vec *ha1)
{
user->len = domain->len = ha1->len = 0;
for (user->ptr = s; *s != '\0' && *s != ':'; s++, user->len++);
if (*s++ != ':')
return (0);
for (domain->ptr = s; *s != '\0' && *s != ':'; s++, domain->len++);
if (*s++ != ':')
return (0);
for (ha1->ptr = s; *s != '\0' && !isspace(* (unsigned char *) s);
s++, ha1->len++);
DBG(("parse_htpasswd_line: [%.*s] [%.*s] [%.*s]", user->len, user->ptr,
domain->len, domain->ptr, ha1->len, ha1->ptr));
return (user->len > 0 && domain->len > 0 && ha1->len > 0);
}
/*
* Authorize against the opened passwords file. Return 1 if authorized.
*/
static int
authorize(struct conn *c, FILE *fp)
{
struct vec *auth_vec = &c->ch.auth.v_vec;
struct vec *user_vec = &c->ch.user.v_vec;
struct vec user, domain, ha1;
struct digest digest;
int ok = 0;
char line[256];
if (auth_vec->len > 20 &&
!_shttpd_strncasecmp(auth_vec->ptr, "Digest ", 7)) {
parse_authorization_header(auth_vec, &digest);
*user_vec = digest.user;
while (fgets(line, sizeof(line), fp) != NULL) {
if (!parse_htpasswd_line(line, &user, &domain, &ha1))
continue;
DBG(("[%.*s] [%.*s] [%.*s]", user.len, user.ptr,
domain.len, domain.ptr, ha1.len, ha1.ptr));
if (vcmp(user_vec, &user) &&
!memcmp(c->ctx->options[OPT_AUTH_REALM],
domain.ptr, domain.len)) {
ok = check_password(c->method, &ha1, &digest);
break;
}
}
}
return (ok);
}
int
_shttpd_check_authorization(struct conn *c, const char *path)
{
FILE *fp = NULL;
int len, n, authorized = 1;
const char *p, *s = c->ctx->options[OPT_PROTECT];
char protected_path[FILENAME_MAX];
FOR_EACH_WORD_IN_LIST(s, len) {
if ((p = memchr(s, '=', len)) == NULL || p >= s + len || p == s)
continue;
if (!memcmp(c->uri, s, p - s)) {
n = s + len - p;
if (n > (int) sizeof(protected_path) - 1)
n = sizeof(protected_path) - 1;
_shttpd_strlcpy(protected_path, p + 1, n);
if ((fp = fopen(protected_path, "r")) == NULL)
_shttpd_elog(E_LOG, c,
"check_auth: cannot open %s: %s",
protected_path, strerror(errno));
break;
}
}
if (fp == NULL)
fp = open_auth_file(c->ctx, path);
if (fp != NULL) {
authorized = authorize(c, fp);
(void) fclose(fp);
}
return (authorized);
}
int
_shttpd_is_authorized_for_put(struct conn *c)
{
FILE *fp;
int ret = 0;
if ((fp = fopen(c->ctx->options[OPT_AUTH_PUT], "r")) != NULL) {
ret = authorize(c, fp);
(void) fclose(fp);
}
return (ret);
}
void
_shttpd_send_authorization_request(struct conn *c)
{
char buf[512];
(void) _shttpd_snprintf(buf, sizeof(buf), "Unauthorized\r\n"
"WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
"nonce=\"%lu\"", c->ctx->options[OPT_AUTH_REALM],
(unsigned long) _shttpd_current_time);
_shttpd_send_server_error(c, 401, buf);
}
/*
* Edit the passwords file.
*/
int
shttpd_edit_passwords(const char *fname, const char *domain,
const char *user, const char *pass)
{
int ret = EXIT_SUCCESS, found = 0;
struct vec u, d, p;
char line[512], tmp[FILENAME_MAX], ha1[32];
FILE *fp = NULL, *fp2 = NULL;
(void) _shttpd_snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
/* Create the file if does not exist */
if ((fp = fopen(fname, "a+")))
(void) fclose(fp);
/* Open the given file and temporary file */
if ((fp = fopen(fname, "r")) == NULL)
_shttpd_elog(E_FATAL, NULL,
"Cannot open %s: %s", fname, strerror(errno));
else if ((fp2 = fopen(tmp, "w+")) == NULL)
_shttpd_elog(E_FATAL, NULL,
"Cannot open %s: %s", tmp, strerror(errno));
p.ptr = pass;
p.len = strlen(pass);
/* Copy the stuff to temporary file */
while (fgets(line, sizeof(line), fp) != NULL) {
u.ptr = line;
if ((d.ptr = strchr(line, ':')) == NULL)
continue;
u.len = d.ptr - u.ptr;
d.ptr++;
if (strchr(d.ptr, ':') == NULL)
continue;
d.len = strchr(d.ptr, ':') - d.ptr;
if ((int) strlen(user) == u.len &&
!memcmp(user, u.ptr, u.len) &&
(int) strlen(domain) == d.len &&
!memcmp(domain, d.ptr, d.len)) {
found++;
md5(ha1, &u, &d, &p, NULL);
(void) fprintf(fp2, "%s:%s:%.32s\n", user, domain, ha1);
} else {
(void) fprintf(fp2, "%s", line);
}
}
/* If new user, just add it */
if (found == 0) {
u.ptr = user; u.len = strlen(user);
d.ptr = domain; d.len = strlen(domain);
md5(ha1, &u, &d, &p, NULL);
(void) fprintf(fp2, "%s:%s:%.32s\n", user, domain, ha1);
}
/* Close files */
(void) fclose(fp);
(void) fclose(fp2);
/* Put the temp file in place of real file */
(void) _shttpd_remove(fname);
(void) _shttpd_rename(tmp, fname);
return (ret);
}
#endif /* NO_AUTH */

View File

@ -1,198 +0,0 @@
/**********************************************************************
*
* rtems shttpd management
*
* FILE NAME : rtems_shttpd.c
*
* AUTHOR : Steven Johnson
*
* DESCRIPTION : Defines the interface functions to the shttp daemon
*
* REVISION : $Id: compat_rtems.c,v 1.2 2006/11/12 03:29:17 infidel Exp $
*
* COMMENTS :
*
**********************************************************************/
/**********************************************************************
* INCLUDED MODULES
**********************************************************************/
#include <rtems.h>
#include "defs.h"
#define MAX_WEB_BASE_PATH_LENGTH 256
#define MIN_SHTTPD_STACK (8*1024)
typedef struct RTEMS_HTTPD_ARGS {
rtems_shttpd_init init_callback;
rtems_shttpd_addpages addpages_callback;
char webroot[MAX_WEB_BASE_PATH_LENGTH];
} RTEMS_HTTPD_ARGS;
static int rtems_webserver_running = FALSE; //not running.
static rtems_task rtems_httpd_daemon(rtems_task_argument args )
{
RTEMS_HTTPD_ARGS *httpd_args = (RTEMS_HTTPD_ARGS*)args;
struct shttpd_ctx *ctx;
if (httpd_args != NULL)
if (httpd_args->init_callback != NULL)
httpd_args->init_callback();
/**************************************
* Initialize the web server
*/
/*
* Initialize SHTTPD context.
* Set WWW root to current WEB_ROOT_PATH.
*/
ctx = shttpd_init(NULL, "document_root", httpd_args->webroot, NULL);
if (httpd_args != NULL)
if (httpd_args->addpages_callback != NULL)
httpd_args->addpages_callback(ctx);
/* Finished with args, so free them */
if (httpd_args != NULL)
free(httpd_args);
/* Open listening socket */
shttpd_listen(ctx, 9000);
rtems_webserver_running = TRUE;
/* Serve connections infinitely until someone kills us */
while (rtems_webserver_running)
shttpd_poll(ctx, 1000);
/* Unreached, because we will be killed by a signal */
shttpd_fini(ctx);
rtems_task_delete( RTEMS_SELF );
}
rtems_status_code rtems_initialize_webserver(rtems_task_priority initial_priority,
rtems_unsigned32 stack_size,
rtems_mode initial_modes,
rtems_attribute attribute_set,
rtems_shttpd_init init_callback,
rtems_shttpd_addpages addpages_callback,
char *webroot
)
{
rtems_status_code sc;
rtems_id tid;
RTEMS_HTTPD_ARGS *args;
if (stack_size < MIN_SHTTPD_STACK)
stack_size = MIN_SHTTPD_STACK;
args = malloc(sizeof(RTEMS_HTTPD_ARGS));
if (args != NULL)
{
args->init_callback = init_callback;
args->addpages_callback = addpages_callback;
strncpy(args->webroot,webroot,MAX_WEB_BASE_PATH_LENGTH);
sc = rtems_task_create(rtems_build_name('H', 'T', 'P', 'D'),
initial_priority,
stack_size,
initial_modes,
attribute_set,
&tid);
if (sc == RTEMS_SUCCESSFUL)
{
sc = rtems_task_start(tid, rtems_httpd_daemon, (rtems_task_argument)args);
}
}
else
{
sc = RTEMS_NO_MEMORY;
}
return sc;
}
void rtems_terminate_webserver(void)
{
rtems_webserver_running = FALSE; //not running, so terminate
}
int rtems_webserver_ok(void)
{
return rtems_webserver_running;
}
void
set_close_on_exec(int fd)
{
// RTEMS Does not have a functional "execve"
// so technically this call does not do anything,
// but it doesnt hurt either.
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
}
int
my_stat(const char *path, struct stat *stp)
{
return (stat(path, stp));
}
int
my_open(const char *path, int flags, int mode)
{
return (open(path, flags, mode));
}
int
my_remove(const char *path)
{
return (remove(path));
}
int
my_rename(const char *path1, const char *path2)
{
return (rename(path1, path2));
}
int
my_mkdir(const char *path, int mode)
{
return (mkdir(path, mode));
}
char *
my_getcwd(char *buffer, int maxlen)
{
return (getcwd(buffer, maxlen));
}
int
set_non_blocking_mode(int fd)
{
int ret = -1;
int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) == -1) {
DBG(("nonblock: fcntl(F_GETFL): %d", ERRNO));
} else if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
DBG(("nonblock: fcntl(F_SETFL): %d", ERRNO));
} else {
ret = 0; /* Success */
}
return (ret);
}
#if !defined(NO_CGI)
int
spawn_process(struct conn *c, const char *prog, char *envblk, char **envp)
{
return (-1); // RTEMS does not have subprocess support as standard.
}
#endif

View File

@ -1,60 +0,0 @@
/**
* @file rtems/rtems-shttpd.h
*/
#ifndef _rtems_rtems_webserver_h
#define _rtems_rtems_webserver_h
#include "shttpd.h"
#include <rtems.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <dirent.h>
/* RTEMS is an Real Time Embedded operating system, for operation in hardware.
It does not have SSL or CGI support, as it does not have dynamic library
loading or sub-processes. */
#define EMBEDDED
#define NO_SSL
#define NO_CGI
#define DIRSEP '/'
#define O_BINARY 0
#define ERRNO errno
/* RTEMS version is Thread Safe */
#define InitializeCriticalSection(x) rtems_semaphore_create( \
rtems_build_name('H','T','P','X'), \
1, /* Not Held Yet.*/ \
RTEMS_FIFO | \
RTEMS_BINARY_SEMAPHORE, \
0, \
x);
#define EnterCriticalSection(x) rtems_semaphore_obtain(*(x),RTEMS_WAIT,RTEMS_NO_TIMEOUT)
#define LeaveCriticalSection(x) rtems_semaphore_release(*(x))
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*rtems_shttpd_addpages)(struct shttpd_ctx *ctx);
typedef void (*rtems_shttpd_init)(void);
rtems_status_code rtems_initialize_webserver(rtems_task_priority initial_priority,
rtems_unsigned32 stack_size,
rtems_mode initial_modes,
rtems_attribute attribute_set,
rtems_shttpd_init init_callback,
rtems_shttpd_addpages addpages_callback,
char *webroot
);
void rtems_terminate_webserver(void);
int rtems_webserver_ok(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,133 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef _WIN32
#include "defs.h"
void
_shttpd_set_close_on_exec(int fd)
{
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
}
int
_shttpd_stat(const char *path, struct stat *stp)
{
return (stat(path, stp));
}
int
_shttpd_open(const char *path, int flags, int mode)
{
return (open(path, flags, mode));
}
int
_shttpd_remove(const char *path)
{
return (remove(path));
}
int
_shttpd_rename(const char *path1, const char *path2)
{
return (rename(path1, path2));
}
int
_shttpd_mkdir(const char *path, int mode)
{
return (mkdir(path, mode));
}
char *
_shttpd_getcwd(char *buffer, int maxlen)
{
return (getcwd(buffer, maxlen));
}
int
_shttpd_set_non_blocking_mode(int fd)
{
int ret = -1;
int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) == -1) {
DBG(("nonblock: fcntl(F_GETFL): %d", ERRNO));
} else if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
DBG(("nonblock: fcntl(F_SETFL): %d", ERRNO));
} else {
ret = 0; /* Success */
}
return (ret);
}
#ifndef NO_CGI
int
_shttpd_spawn_process(struct conn *c, const char *prog, char *envblk,
char *envp[], int sock, const char *dir)
{
int ret;
pid_t pid;
const char *p, *interp = c->ctx->options[OPT_CGI_INTERPRETER];
envblk = NULL; /* unused */
if ((pid = vfork()) == -1) {
ret = -1;
_shttpd_elog(E_LOG, c, "redirect: fork: %s", strerror(errno));
} else if (pid == 0) {
/* Child */
(void) chdir(dir);
(void) dup2(sock, 0);
(void) dup2(sock, 1);
(void) closesocket(sock);
/* If error file is specified, send errors there */
if (c->ctx->error_log)
(void) dup2(fileno(c->ctx->error_log), 2);
if ((p = strrchr(prog, '/')) != NULL)
p++;
else
p = prog;
/* Execute CGI program */
if (interp == NULL) {
(void) execle(p, p, NULL, envp);
_shttpd_elog(E_FATAL, c, "redirect: exec(%s)", prog);
} else {
(void) execle(interp, interp, p, NULL, envp);
_shttpd_elog(E_FATAL, c, "redirect: exec(%s %s)",
interp, prog);
}
/* UNREACHED */
exit(EXIT_FAILURE);
} else {
/* Parent */
ret = 0;
(void) closesocket(sock);
}
return (ret);
}
#endif /* !NO_CGI */
#endif /* !_WIN32 */

View File

@ -1,35 +0,0 @@
/*
* Copyright (c) 2004-2007 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/mman.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <pwd.h>
#include <unistd.h>
#include <dirent.h>
#include <dlfcn.h>
#if !defined(NO_THREADS)
#include "pthread.h"
#define _beginthread(a, b, c) do { pthread_t tid; \
pthread_create(&tid, NULL, (void *(*)(void *))a, c); } while (0)
#endif /* !NO_THREADS */
#define SSL_LIB "libssl.so"
#define DIRSEP '/'
#define IS_DIRSEP_CHAR(c) ((c) == '/')
#define O_BINARY 0
#define closesocket(a) close(a)
#define ERRNO errno

View File

@ -1,692 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifdef _WIN32
#include "defs.h"
static SERVICE_STATUS ss;
static SERVICE_STATUS_HANDLE hStatus;
static SERVICE_DESCRIPTION service_descr = {"Web server"};
static void
fix_directory_separators(char *path)
{
for (; *path != '\0'; path++) {
if (*path == '/')
*path = '\\';
if (*path == '\\')
while (path[1] == '\\' || path[1] == '/')
(void) memmove(path + 1,
path + 2, strlen(path + 2) + 1);
}
}
static int
protect_against_code_disclosure(const wchar_t *path)
{
WIN32_FIND_DATAW data;
HANDLE handle;
const wchar_t *p;
/*
* Protect against CGI code disclosure under Windows.
* This is very nasty hole. Windows happily opens files with
* some garbage in the end of file name. So fopen("a.cgi ", "r")
* actually opens "a.cgi", and does not return an error! And since
* "a.cgi " does not have valid CGI extension, this leads to
* the CGI code disclosure.
* To protect, here we delete all fishy characters from the
* end of file name.
*/
if ((handle = FindFirstFileW(path, &data)) == INVALID_HANDLE_VALUE)
return (FALSE);
FindClose(handle);
for (p = path + wcslen(path); p > path && p[-1] != L'\\';)
p--;
if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
wcscmp(data.cFileName, p) != 0)
return (FALSE);
return (TRUE);
}
int
_shttpd_open(const char *path, int flags, int mode)
{
char buf[FILENAME_MAX];
wchar_t wbuf[FILENAME_MAX];
_shttpd_strlcpy(buf, path, sizeof(buf));
fix_directory_separators(buf);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
if (protect_against_code_disclosure(wbuf) == FALSE)
return (-1);
return (_wopen(wbuf, flags));
}
int
_shttpd_stat(const char *path, struct stat *stp)
{
char buf[FILENAME_MAX], *p;
wchar_t wbuf[FILENAME_MAX];
_shttpd_strlcpy(buf, path, sizeof(buf));
fix_directory_separators(buf);
p = buf + strlen(buf) - 1;
while (p > buf && *p == '\\' && p[-1] != ':')
*p-- = '\0';
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
return (_wstat(wbuf, (struct _stat *) stp));
}
int
_shttpd_remove(const char *path)
{
char buf[FILENAME_MAX];
wchar_t wbuf[FILENAME_MAX];
_shttpd_strlcpy(buf, path, sizeof(buf));
fix_directory_separators(buf);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
return (_wremove(wbuf));
}
int
_shttpd_rename(const char *path1, const char *path2)
{
char buf1[FILENAME_MAX];
char buf2[FILENAME_MAX];
wchar_t wbuf1[FILENAME_MAX];
wchar_t wbuf2[FILENAME_MAX];
_shttpd_strlcpy(buf1, path1, sizeof(buf1));
_shttpd_strlcpy(buf2, path2, sizeof(buf2));
fix_directory_separators(buf1);
fix_directory_separators(buf2);
MultiByteToWideChar(CP_UTF8, 0, buf1, -1, wbuf1, sizeof(wbuf1));
MultiByteToWideChar(CP_UTF8, 0, buf2, -1, wbuf2, sizeof(wbuf2));
return (_wrename(wbuf1, wbuf2));
}
int
_shttpd_mkdir(const char *path, int mode)
{
char buf[FILENAME_MAX];
wchar_t wbuf[FILENAME_MAX];
_shttpd_strlcpy(buf, path, sizeof(buf));
fix_directory_separators(buf);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
return (_wmkdir(wbuf));
}
static char *
wide_to_utf8(const wchar_t *str)
{
char *buf = NULL;
if (str) {
int nchar = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
if (nchar > 0) {
buf = malloc(nchar);
if (!buf)
errno = ENOMEM;
else if (!WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, nchar, NULL, NULL)) {
free(buf);
buf = NULL;
errno = EINVAL;
}
} else
errno = EINVAL;
} else
errno = EINVAL;
return buf;
}
char *
_shttpd_getcwd(char *buffer, int maxlen)
{
char *result = NULL;
wchar_t *wbuffer, *wresult;
if (buffer) {
/* User-supplied buffer */
wbuffer = malloc(maxlen * sizeof(wchar_t));
if (wbuffer == NULL)
return NULL;
} else
/* Dynamically allocated buffer */
wbuffer = NULL;
wresult = _wgetcwd(wbuffer, maxlen);
if (wresult) {
int err = errno;
if (buffer) {
/* User-supplied buffer */
int n = WideCharToMultiByte(CP_UTF8, 0, wresult, -1, buffer, maxlen, NULL, NULL);
if (n == 0)
err = ERANGE;
free(wbuffer);
result = buffer;
} else {
/* Buffer allocated by _wgetcwd() */
result = wide_to_utf8(wresult);
err = errno;
free(wresult);
}
errno = err;
}
return result;
}
DIR *
opendir(const char *name)
{
DIR *dir = NULL;
char path[FILENAME_MAX];
wchar_t wpath[FILENAME_MAX];
if (name == NULL || name[0] == '\0') {
errno = EINVAL;
} else if ((dir = malloc(sizeof(*dir))) == NULL) {
errno = ENOMEM;
} else {
_shttpd_snprintf(path, sizeof(path), "%s/*", name);
fix_directory_separators(path);
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, sizeof(wpath));
dir->handle = FindFirstFileW(wpath, &dir->info);
if (dir->handle != INVALID_HANDLE_VALUE) {
dir->result.d_name[0] = '\0';
} else {
free(dir);
dir = NULL;
}
}
return (dir);
}
int
closedir(DIR *dir)
{
int result = -1;
if (dir != NULL) {
if (dir->handle != INVALID_HANDLE_VALUE)
result = FindClose(dir->handle) ? 0 : -1;
free(dir);
}
if (result == -1)
errno = EBADF;
return (result);
}
struct dirent *
readdir(DIR *dir)
{
struct dirent *result = 0;
if (dir && dir->handle != INVALID_HANDLE_VALUE) {
if(!dir->result.d_name ||
FindNextFileW(dir->handle, &dir->info)) {
result = &dir->result;
WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName,
-1, result->d_name,
sizeof(result->d_name), NULL, NULL);
}
} else {
errno = EBADF;
}
return (result);
}
int
_shttpd_set_non_blocking_mode(int fd)
{
unsigned long on = 1;
return (ioctlsocket(fd, FIONBIO, &on));
}
void
_shttpd_set_close_on_exec(int fd)
{
fd = 0; /* Do nothing. There is no FD_CLOEXEC on Windows */
}
#if !defined(NO_CGI)
struct threadparam {
SOCKET s;
HANDLE hPipe;
big_int_t content_len;
};
enum ready_mode_t {IS_READY_FOR_READ, IS_READY_FOR_WRITE};
/*
* Wait until given socket is in ready state. Always return TRUE.
*/
static int
is_socket_ready(int sock, enum ready_mode_t mode)
{
fd_set read_set, write_set;
FD_ZERO(&read_set);
FD_ZERO(&write_set);
if (mode == IS_READY_FOR_READ)
FD_SET(sock, &read_set);
else
FD_SET(sock, &write_set);
select(sock + 1, &read_set, &write_set, NULL, NULL);
return (TRUE);
}
/*
* Thread function that reads POST data from the socket pair
* and writes it to the CGI process.
*/
static void//DWORD WINAPI
stdoutput(void *arg)
{
struct threadparam *tp = arg;
int n, sent, stop = 0;
big_int_t total = 0;
DWORD k;
char buf[BUFSIZ];
size_t max_recv;
max_recv = min(sizeof(buf), tp->content_len - total);
while (!stop &&
max_recv > 0 &&
is_socket_ready(tp->s, IS_READY_FOR_READ) &&
(n = recv(tp->s, buf, max_recv, 0)) > 0) {
if (n == -1 && ERRNO == EWOULDBLOCK)
continue;
for (sent = 0; !stop && sent < n; sent += k)
if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0))
stop++;
total += n;
max_recv = min(sizeof(buf), tp->content_len - total);
}
CloseHandle(tp->hPipe); /* Suppose we have POSTed everything */
free(tp);
}
/*
* Thread function that reads CGI output and pushes it to the socket pair.
*/
static void
stdinput(void *arg)
{
struct threadparam *tp = arg;
static int ntotal;
int k, stop = 0;
DWORD n, sent;
char buf[BUFSIZ];
while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) {
ntotal += n;
for (sent = 0; !stop && sent < n; sent += k) {
if (is_socket_ready(tp->s, IS_READY_FOR_WRITE) &&
(k = send(tp->s, buf + sent, n - sent, 0)) <= 0) {
if (k == -1 && ERRNO == EWOULDBLOCK) {
k = 0;
continue;
}
stop++;
}
}
}
CloseHandle(tp->hPipe);
/*
* Windows is a piece of crap. When this thread closes its end
* of the socket pair, the other end (get_cgi() function) may loose
* some data. I presume, this happens if get_cgi() is not fast enough,
* and the data written by this end does not "push-ed" to the other
* end socket buffer. So after closesocket() the remaining data is
* gone. If I put shutdown() before closesocket(), that seems to
* fix the problem, but I am not sure this is the right fix.
* XXX (submitted by James Marshall) we do not do shutdown() on UNIX.
* If fork() is called from user callback, shutdown() messes up things.
*/
shutdown(tp->s, 2);
closesocket(tp->s);
free(tp);
_endthread();
}
static void
spawn_stdio_thread(int sock, HANDLE hPipe, void (*func)(void *),
big_int_t content_len)
{
struct threadparam *tp;
DWORD tid;
tp = malloc(sizeof(*tp));
assert(tp != NULL);
tp->s = sock;
tp->hPipe = hPipe;
tp->content_len = content_len;
_beginthread(func, 0, tp);
}
int
_shttpd_spawn_process(struct conn *c, const char *prog, char *envblk,
char *envp[], int sock, const char *dir)
{
HANDLE a[2], b[2], h[2], me;
DWORD flags;
char *p, *interp, cmdline[FILENAME_MAX], line[FILENAME_MAX];
FILE *fp;
STARTUPINFOA si;
PROCESS_INFORMATION pi;
me = GetCurrentProcess();
flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS;
/* FIXME add error checking code here */
CreatePipe(&a[0], &a[1], NULL, 0);
CreatePipe(&b[0], &b[1], NULL, 0);
DuplicateHandle(me, a[0], me, &h[0], 0, TRUE, flags);
DuplicateHandle(me, b[1], me, &h[1], 0, TRUE, flags);
(void) memset(&si, 0, sizeof(si));
(void) memset(&pi, 0, sizeof(pi));
/* XXX redirect CGI errors to the error log file */
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = h[1];
si.hStdInput = h[0];
/* If CGI file is a script, try to read the interpreter line */
interp = c->ctx->options[OPT_CGI_INTERPRETER];
if (interp == NULL) {
if ((fp = fopen(prog, "r")) != NULL) {
(void) fgets(line, sizeof(line), fp);
if (memcmp(line, "#!", 2) != 0)
line[2] = '\0';
/* Trim whitespaces from interpreter name */
for (p = &line[strlen(line) - 1]; p > line &&
isspace(*p); p--)
*p = '\0';
(void) fclose(fp);
}
interp = line + 2;
(void) _shttpd_snprintf(cmdline, sizeof(cmdline), "%s%s%s",
line + 2, line[2] == '\0' ? "" : " ", prog);
}
if ((p = strrchr(prog, '/')) != NULL)
prog = p + 1;
(void) _shttpd_snprintf(cmdline, sizeof(cmdline), "%s %s", interp, prog);
(void) _shttpd_snprintf(line, sizeof(line), "%s", dir);
fix_directory_separators(line);
fix_directory_separators(cmdline);
/*
* Spawn reader & writer threads before we create CGI process.
* Otherwise CGI process may die too quickly, loosing the data
*/
spawn_stdio_thread(sock, b[0], stdinput, 0);
spawn_stdio_thread(sock, a[1], stdoutput, c->rem.content_len);
if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) {
_shttpd_elog(E_LOG, c,
"redirect: CreateProcess(%s): %d", cmdline, ERRNO);
return (-1);
} else {
CloseHandle(h[0]);
CloseHandle(h[1]);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
return (0);
}
#endif /* !NO_CGI */
#define ID_TRAYICON 100
#define ID_QUIT 101
static NOTIFYICONDATA ni;
static LRESULT CALLBACK
WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
POINT pt;
HMENU hMenu;
switch (msg) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_QUIT:
exit(EXIT_SUCCESS);
break;
}
break;
case WM_USER:
switch (lParam) {
case WM_RBUTTONUP:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
hMenu = CreatePopupMenu();
AppendMenu(hMenu, 0, ID_QUIT, "Exit SHTTPD");
GetCursorPos(&pt);
TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
DestroyMenu(hMenu);
break;
}
break;
}
return (DefWindowProc(hWnd, msg, wParam, lParam));
}
static void
systray(void *arg)
{
WNDCLASS cls;
HWND hWnd;
MSG msg;
(void) memset(&cls, 0, sizeof(cls));
cls.lpfnWndProc = (WNDPROC) WindowProc;
cls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
cls.lpszClassName = "shttpd v." SHTTPD_VERSION;
if (!RegisterClass(&cls))
_shttpd_elog(E_FATAL, NULL, "RegisterClass: %d", ERRNO);
else if ((hWnd = CreateWindow(cls.lpszClassName, "",
WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, NULL, arg)) == NULL)
_shttpd_elog(E_FATAL, NULL, "CreateWindow: %d", ERRNO);
ShowWindow(hWnd, SW_HIDE);
ni.cbSize = sizeof(ni);
ni.uID = ID_TRAYICON;
ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
ni.hIcon = LoadIcon(NULL, IDI_APPLICATION);
ni.hWnd = hWnd;
_shttpd_snprintf(ni.szTip, sizeof(ni.szTip), "SHTTPD web server");
ni.uCallbackMessage = WM_USER;
Shell_NotifyIcon(NIM_ADD, &ni);
while (GetMessage(&msg, hWnd, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
int
_shttpd_set_systray(struct shttpd_ctx *ctx, const char *opt)
{
HWND hWnd;
char title[512];
static WNDPROC oldproc;
if (!_shttpd_is_true(opt))
return (TRUE);
FreeConsole();
GetConsoleTitle(title, sizeof(title));
hWnd = FindWindow(NULL, title);
ShowWindow(hWnd, SW_HIDE);
_beginthread(systray, 0, hWnd);
return (TRUE);
}
int
_shttpd_set_nt_service(struct shttpd_ctx *ctx, const char *action)
{
SC_HANDLE hSCM, hService;
char path[FILENAME_MAX], key[128];
HKEY hKey;
DWORD dwData;
if (!strcmp(action, "install")) {
if ((hSCM = OpenSCManager(NULL, NULL,
SC_MANAGER_ALL_ACCESS)) == NULL)
_shttpd_elog(E_FATAL, NULL, "Error opening SCM (%d)", ERRNO);
GetModuleFileName(NULL, path, sizeof(path));
hService = CreateService(hSCM, SERVICE_NAME, SERVICE_NAME,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
NULL, NULL, NULL, NULL, NULL);
if (!hService)
_shttpd_elog(E_FATAL, NULL,
"Error installing service (%d)", ERRNO);
ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION,
&service_descr);
_shttpd_elog(E_FATAL, NULL, "Service successfully installed");
} else if (!strcmp(action, "uninstall")) {
if ((hSCM = OpenSCManager(NULL, NULL,
SC_MANAGER_ALL_ACCESS)) == NULL) {
_shttpd_elog(E_FATAL, NULL, "Error opening SCM (%d)", ERRNO);
} else if ((hService = OpenService(hSCM,
SERVICE_NAME, DELETE)) == NULL) {
_shttpd_elog(E_FATAL, NULL,
"Error opening service (%d)", ERRNO);
} else if (!DeleteService(hService)) {
_shttpd_elog(E_FATAL, NULL,
"Error deleting service (%d)", ERRNO);
} else {
_shttpd_elog(E_FATAL, NULL, "Service deleted");
}
} else {
_shttpd_elog(E_FATAL, NULL, "Use -service <install|uninstall>");
}
/* NOTREACHED */
return (TRUE);
}
static void WINAPI
ControlHandler(DWORD code)
{
if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) {
ss.dwWin32ExitCode = 0;
ss.dwCurrentState = SERVICE_STOPPED;
}
SetServiceStatus(hStatus, &ss);
}
static void WINAPI
ServiceMain(int argc, char *argv[])
{
char path[MAX_PATH], *p, *av[] = {"shttpd_service", path, NULL};
struct shttpd_ctx *ctx;
ss.dwServiceType = SERVICE_WIN32;
ss.dwCurrentState = SERVICE_RUNNING;
ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
hStatus = RegisterServiceCtrlHandler(SERVICE_NAME, ControlHandler);
SetServiceStatus(hStatus, &ss);
GetModuleFileName(NULL, path, sizeof(path));
if ((p = strrchr(path, DIRSEP)) != NULL)
*++p = '\0';
strcat(path, CONFIG_FILE); /* woo ! */
ctx = shttpd_init(NELEMS(av) - 1, av);
if ((ctx = shttpd_init(NELEMS(av) - 1, av)) == NULL)
_shttpd_elog(E_FATAL, NULL, "Cannot initialize SHTTP context");
while (ss.dwCurrentState == SERVICE_RUNNING)
shttpd_poll(ctx, INT_MAX);
shttpd_fini(ctx);
ss.dwCurrentState = SERVICE_STOPPED;
ss.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ss);
}
void
try_to_run_as_nt_service(void)
{
static SERVICE_TABLE_ENTRY service_table[] = {
{SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{NULL, NULL}
};
if (StartServiceCtrlDispatcher(service_table))
exit(EXIT_SUCCESS);
}
#endif /* _WIN32 */

View File

@ -1,83 +0,0 @@
/*
* Copyright (c) 2004-2007 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
/* Tip from Justin Maximilian, suppress errors from winsock2.h */
#define _WINSOCKAPI_
#include <windows.h>
#include <winsock2.h>
#include <commctrl.h>
#include <winnls.h>
#include <shlobj.h>
#include <shellapi.h>
#ifndef _WIN32_WCE
#include <process.h>
#include <direct.h>
#include <io.h>
#else /* _WIN32_WCE */
/* Windows CE-specific definitions */
#define NO_CGI /* WinCE has no pipes */
#define NO_GUI /* temporarily until it is fixed */
#pragma comment(lib,"ws2")
/* WinCE has both Unicode and ANSI versions of GetProcAddress */
#undef GetProcAddress
#define GetProcAddress GetProcAddressA
#include "compat_wince.h"
#endif /* _WIN32_WCE */
#define ERRNO GetLastError()
#define NO_SOCKLEN_T
#define SSL_LIB L"ssleay32.dll"
#define DIRSEP '\\'
#define IS_DIRSEP_CHAR(c) ((c) == '/' || (c) == '\\')
#define O_NONBLOCK 0
#define EWOULDBLOCK WSAEWOULDBLOCK
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define mkdir(x,y) _mkdir(x)
#define popen(x,y) _popen(x, y)
#define pclose(x) _pclose(x)
#define dlopen(x,y) LoadLibraryW(x)
#define dlsym(x,y) (void *) GetProcAddress(x,y)
#define _POSIX_
#ifdef __LCC__
#include <stdint.h>
#endif /* __LCC__ */
#ifdef _MSC_VER /* MinGW already has these */
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef __int64 uint64_t;
#define S_ISDIR(x) ((x) & _S_IFDIR)
#endif /* _MSC_VER */
/*
* POSIX dirent interface
*/
struct dirent {
char d_name[FILENAME_MAX];
};
typedef struct DIR {
HANDLE handle;
WIN32_FIND_DATAW info;
struct dirent result;
char *name;
} DIR;
extern DIR *opendir(const char *name);
extern int closedir(DIR *dir);
extern struct dirent *readdir(DIR *dir);

View File

@ -1,30 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef CONFIG_HEADER_DEFINED
#define CONFIG_HEADER_DEFINED
#define SHTTPD_VERSION "1.42" /* Version */
#define CONFIG_FILE "shttpd.conf" /* Configuration file */
#define HTPASSWD ".htpasswd" /* Passwords file name */
#define URI_MAX 16384 /* Default max request size */
#define IO_BUFSIZE 65536 /* IO buffer size */
#define LISTENING_PORTS "80" /* Default listening ports */
#define INDEX_FILES "index.html,index.htm,index.php,index.cgi"
#define CGI_EXT "cgi,pl,php" /* Default CGI extensions */
#define SSI_EXT "shtml,shtm" /* Default SSI extensions */
#define REALM "mydomain.com" /* Default authentication realm */
#define DELIM_CHARS "," /* Separators for lists */
#define EXPIRE_TIME 3600 /* Expiration time, seconds */
#define ENV_MAX 4096 /* Size of environment block */
#define CGI_ENV_VARS 64 /* Maximum vars passed to CGI */
#define SERVICE_NAME "SHTTPD " SHTTPD_VERSION /* NT service name */
#endif /* CONFIG_HEADER_DEFINED */

View File

@ -1,395 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef DEFS_HEADER_DEFINED
#define DEFS_HEADER_DEFINED
#include "std_includes.h"
#include "llist.h"
#include "io.h"
#include "md5.h"
#include "config.h"
#include "shttpd.h"
#define NELEMS(ar) (sizeof(ar) / sizeof(ar[0]))
#ifdef _DEBUG
#define DBG(x) do { printf x ; putchar('\n'); fflush(stdout); } while (0)
#else
#define DBG(x)
#endif /* DEBUG */
/*
* Darwin prior to 7.0 and Win32 do not have socklen_t
*/
#ifdef NO_SOCKLEN_T
typedef int socklen_t;
#endif /* NO_SOCKLEN_T */
/*
* For parsing. This guy represents a substring.
*/
struct vec {
const char *ptr;
int len;
};
#if !defined(FALSE)
enum {FALSE, TRUE};
#endif /* !FALSE */
enum {METHOD_GET, METHOD_POST, METHOD_PUT, METHOD_DELETE, METHOD_HEAD};
enum {HDR_DATE, HDR_INT, HDR_STRING}; /* HTTP header types */
enum {E_FATAL = 1, E_LOG = 2}; /* Flags for elog() function */
typedef unsigned long big_int_t; /* Type for Content-Length */
/*
* Unified socket address
*/
struct usa {
socklen_t len;
union {
struct sockaddr sa;
struct sockaddr_in sin;
} u;
};
/*
* This thing is aimed to hold values of any type.
* Used to store parsed headers' values.
*/
union variant {
char *v_str;
int v_int;
big_int_t v_big_int;
time_t v_time;
void (*v_func)(void);
void *v_void;
struct vec v_vec;
};
/*
* This is used only in embedded configuration. This structure holds a
* registered URI, associated callback function with callback data.
* For non-embedded compilation shttpd_callback_t is not defined, so
* we use union variant to keep the compiler silent.
*/
struct registered_uri {
struct llhead link;
const char *uri;
union variant callback;
void *callback_data;
};
/*
* User may want to handle certain errors. This structure holds the
* handlers for corresponding error codes.
*/
struct error_handler {
struct llhead link;
int code;
union variant callback;
void *callback_data;
};
struct http_header {
int len; /* Header name length */
int type; /* Header type */
size_t offset; /* Value placeholder */
const char *name; /* Header name */
};
/*
* This guy holds parsed HTTP headers
*/
struct headers {
union variant cl; /* Content-Length: */
union variant ct; /* Content-Type: */
union variant connection; /* Connection: */
union variant ims; /* If-Modified-Since: */
union variant user; /* Remote user name */
union variant auth; /* Authorization */
union variant useragent; /* User-Agent: */
union variant referer; /* Referer: */
union variant cookie; /* Cookie: */
union variant location; /* Location: */
union variant range; /* Range: */
union variant status; /* Status: */
union variant transenc; /* Transfer-Encoding: */
};
/* Must go after union variant definition */
#include "ssl.h"
/*
* The communication channel
*/
union channel {
int fd; /* Regular static file */
int sock; /* Connected socket */
struct {
int sock; /* XXX important. must be first */
SSL *ssl; /* shttpd_poll() assumes that */
} ssl; /* SSL-ed socket */
struct {
DIR *dirp;
char *path;
} dir; /* Opened directory */
struct {
void *state; /* For keeping state */
union variant func; /* User callback function */
void *data; /* User defined parameters */
} emb; /* Embedded, user callback */
};
struct stream;
/*
* IO class descriptor (file, directory, socket, SSL, CGI, etc)
* These classes are defined in io_*.c files.
*/
struct io_class {
const char *name;
int (*read)(struct stream *, void *buf, size_t len);
int (*write)(struct stream *, const void *buf, size_t len);
void (*close)(struct stream *);
};
/*
* Data exchange stream. It is backed by some communication channel:
* opened file, socket, etc. The 'read' and 'write' methods are
* determined by a communication channel.
*/
struct stream {
struct conn *conn;
union channel chan; /* Descriptor */
struct io io; /* IO buffer */
const struct io_class *io_class; /* IO class */
int headers_len;
big_int_t content_len;
unsigned int flags;
#define FLAG_HEADERS_PARSED 1
#define FLAG_SSL_ACCEPTED 2
#define FLAG_R 4 /* Can read in general */
#define FLAG_W 8 /* Can write in general */
#define FLAG_CLOSED 16
#define FLAG_DONT_CLOSE 32
#define FLAG_ALWAYS_READY 64 /* File, dir, user_func */
#define FLAG_SUSPEND 128
};
struct worker {
struct llhead link;
int num_conns; /* Num of active connections */
int exit_flag; /* Ditto - exit flag */
int ctl[2]; /* Control socket pair */
struct shttpd_ctx *ctx; /* Context reference */
struct llhead connections; /* List of connections */
};
struct conn {
struct llhead link; /* Connections chain */
struct worker *worker; /* Worker this conn belongs to */
struct shttpd_ctx *ctx; /* Context this conn belongs to */
struct usa sa; /* Remote socket address */
time_t birth_time; /* Creation time */
time_t expire_time; /* Expiration time */
int loc_port; /* Local port */
int status; /* Reply status code */
int method; /* Request method */
char *uri; /* Decoded URI */
unsigned long major_version; /* Major HTTP version number */
unsigned long minor_version; /* Minor HTTP version number */
char *request; /* Request line */
char *headers; /* Request headers */
char *query; /* QUERY_STRING part of the URI */
char *path_info; /* PATH_INFO thing */
struct vec mime_type; /* Mime type */
struct headers ch; /* Parsed client headers */
struct stream loc; /* Local stream */
struct stream rem; /* Remote stream */
#if !defined(NO_SSI)
void *ssi; /* SSI descriptor */
#endif /* NO_SSI */
};
enum {
OPT_ROOT, OPT_INDEX_FILES, OPT_PORTS, OPT_DIR_LIST,
OPT_CGI_EXTENSIONS, OPT_CGI_INTERPRETER, OPT_CGI_ENVIRONMENT,
OPT_SSI_EXTENSIONS, OPT_AUTH_REALM, OPT_AUTH_GPASSWD,
OPT_AUTH_PUT, OPT_ACCESS_LOG, OPT_ERROR_LOG, OPT_MIME_TYPES,
OPT_SSL_CERTIFICATE, OPT_ALIASES, OPT_ACL, OPT_INETD, OPT_UID,
OPT_CFG_URI, OPT_PROTECT, OPT_SERVICE, OPT_HIDE, OPT_THREADS,
NUM_OPTIONS
};
/*
* SHTTPD context
*/
struct shttpd_ctx {
SSL_CTX *ssl_ctx; /* SSL context */
struct llhead registered_uris;/* User urls */
struct llhead error_handlers; /* Embedded error handlers */
struct llhead acl; /* Access control list */
struct llhead ssi_funcs; /* SSI callback functions */
struct llhead listeners; /* Listening sockets */
struct llhead workers; /* Worker workers */
FILE *access_log; /* Access log stream */
FILE *error_log; /* Error log stream */
char *options[NUM_OPTIONS]; /* Configurable options */
#if defined(__rtems__)
rtems_id mutex;
#endif /* _WIN32 */
};
struct listener {
struct llhead link;
struct shttpd_ctx *ctx; /* Context that socket belongs */
int sock; /* Listening socket */
int is_ssl; /* Should be SSL-ed */
};
/* Types of messages that could be sent over the control socket */
enum {CTL_PASS_SOCKET, CTL_WAKEUP};
/*
* In SHTTPD, list of values are represented as comma or space separated
* string. For example, list of CGI extensions can be represented as
* ".cgi,.php,.pl", or ".cgi .php .pl". The macro that follows allows to
* loop through the individual values in that list.
*
* A "const char *" pointer and size_t variable must be passed to the macro.
* Spaces or commas can be used as delimiters (macro DELIM_CHARS).
*
* In every iteration of the loop, "s" points to the current value, and
* "len" specifies its length. The code inside loop must not change
* "s" and "len" parameters.
*/
#define FOR_EACH_WORD_IN_LIST(s,len) \
for (; s != NULL && (len = strcspn(s, DELIM_CHARS)) != 0; \
s += len, s+= strspn(s, DELIM_CHARS))
/*
* IPv4 ACL entry. Specifies subnet with deny/allow flag
*/
struct acl {
struct llhead link;
uint32_t ip; /* IP, in network byte order */
uint32_t mask; /* Also in network byte order */
int flag; /* Either '+' or '-' */
};
/*
* shttpd.c
*/
extern time_t _shttpd_current_time; /* Current UTC time */
extern int _shttpd_tz_offset; /* Offset from GMT time zone */
extern const struct vec _shttpd_known_http_methods[];
extern void _shttpd_stop_stream(struct stream *stream);
extern int _shttpd_url_decode(const char *, int, char *dst, int);
extern void _shttpd_send_server_error(struct conn *, int, const char *);
extern int _shttpd_get_headers_len(const char *buf, size_t buflen);
extern void _shttpd_parse_headers(const char *s, int, struct headers *);
extern int _shttpd_is_true(const char *str);
extern int _shttpd_socketpair(int pair[2]);
extern void _shttpd_get_mime_type(struct shttpd_ctx *,
const char *, int, struct vec *);
#define IS_TRUE(ctx, opt) _shttpd_is_true((ctx)->options[opt])
/*
* config.c
*/
extern void _shttpd_usage(const char *prog);
/*
* log.c
*/
extern void _shttpd_elog(int flags, struct conn *c, const char *fmt, ...);
extern void _shttpd_log_access(FILE *fp, const struct conn *c);
/*
* string.c
*/
extern void _shttpd_strlcpy(register char *, register const char *, size_t);
extern int _shttpd_strncasecmp(register const char *,
register const char *, size_t);
extern char *_shttpd_strndup(const char *ptr, size_t len);
extern char *_shttpd_strdup(const char *str);
extern int _shttpd_snprintf(char *buf, size_t len, const char *fmt, ...);
extern int _shttpd_match_extension(const char *path, const char *ext_list);
/*
* compat_*.c
*/
extern void _shttpd_set_close_on_exec(int fd);
extern int _shttpd_set_non_blocking_mode(int fd);
extern int _shttpd_stat(const char *, struct stat *stp);
extern int _shttpd_open(const char *, int flags, int mode);
extern int _shttpd_remove(const char *);
extern int _shttpd_rename(const char *, const char *);
extern int _shttpd_mkdir(const char *, int);
extern char * _shttpd_getcwd(char *, int);
extern int _shttpd_spawn_process(struct conn *c, const char *prog,
char *envblk, char *envp[], int sock, const char *dir);
extern int _shttpd_set_nt_service(struct shttpd_ctx *, const char *);
extern int _shttpd_set_systray(struct shttpd_ctx *, const char *);
extern void _shttpd_try_to_run_as_nt_service(void);
/*
* io_*.c
*/
extern const struct io_class _shttpd_io_file;
extern const struct io_class _shttpd_io_socket;
extern const struct io_class _shttpd_io_ssl;
extern const struct io_class _shttpd_io_cgi;
extern const struct io_class _shttpd_io_dir;
extern const struct io_class _shttpd_io_embedded;
extern const struct io_class _shttpd_io_ssi;
extern int _shttpd_put_dir(const char *path);
extern void _shttpd_get_dir(struct conn *c);
extern void _shttpd_get_file(struct conn *c, struct stat *stp);
extern void _shttpd_ssl_handshake(struct stream *stream);
extern void _shttpd_setup_embedded_stream(struct conn *,
union variant, void *);
extern struct registered_uri *_shttpd_is_registered_uri(struct shttpd_ctx *,
const char *uri);
extern void _shttpd_do_ssi(struct conn *);
extern void _shttpd_ssi_func_destructor(struct llhead *lp);
/*
* auth.c
*/
extern int _shttpd_check_authorization(struct conn *c, const char *path);
extern int _shttpd_is_authorized_for_put(struct conn *c);
extern void _shttpd_send_authorization_request(struct conn *c);
extern int shttpd_edit_passwords(const char *fname, const char *domain,
const char *user, const char *pass);
/*
* cgi.c
*/
extern int _shttpd_run_cgi(struct conn *c, const char *prog);
extern void _shttpd_do_cgi(struct conn *c);
#define CGI_REPLY "HTTP/1.1 OK\r\n"
#define CGI_REPLY_LEN (sizeof(CGI_REPLY) - 1)
#endif /* DEFS_HEADER_DEFINED */

View File

@ -1,97 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef IO_HEADER_INCLUDED
#define IO_HEADER_INCLUDED
#include <assert.h>
#include <stddef.h>
/*
* I/O buffer descriptor
*/
struct io {
char *buf; /* IO Buffer */
size_t size; /* IO buffer size */
size_t head; /* Bytes read */
size_t tail; /* Bytes written */
size_t total; /* Total bytes read */
};
static __inline void
io_clear(struct io *io)
{
assert(io->buf != NULL);
assert(io->size > 0);
io->total = io->tail = io->head = 0;
}
static __inline char *
io_space(struct io *io)
{
assert(io->buf != NULL);
assert(io->size > 0);
assert(io->head <= io->size);
return (io->buf + io->head);
}
static __inline char *
io_data(struct io *io)
{
assert(io->buf != NULL);
assert(io->size > 0);
assert(io->tail <= io->size);
return (io->buf + io->tail);
}
static __inline size_t
io_space_len(const struct io *io)
{
assert(io->buf != NULL);
assert(io->size > 0);
assert(io->head <= io->size);
return (io->size - io->head);
}
static __inline size_t
io_data_len(const struct io *io)
{
assert(io->buf != NULL);
assert(io->size > 0);
assert(io->head <= io->size);
assert(io->tail <= io->head);
return (io->head - io->tail);
}
static __inline void
io_inc_tail(struct io *io, size_t n)
{
assert(io->buf != NULL);
assert(io->size > 0);
assert(io->tail <= io->head);
assert(io->head <= io->size);
io->tail += n;
assert(io->tail <= io->head);
if (io->tail == io->head)
io->head = io->tail = 0;
}
static __inline void
io_inc_head(struct io *io, size_t n)
{
assert(io->buf != NULL);
assert(io->size > 0);
assert(io->tail <= io->head);
io->head += n;
io->total += n;
assert(io->head <= io->size);
}
#endif /* IO_HEADER_INCLUDED */

View File

@ -1,153 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
/*
* For a given PUT path, create all intermediate subdirectories
* for given path. Return 0 if the path itself is a directory,
* or -1 on error, 1 if OK.
*/
int
_shttpd_put_dir(const char *path)
{
char buf[FILENAME_MAX];
const char *s, *p;
struct stat st;
size_t len;
for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
len = p - path;
assert(len < sizeof(buf));
(void) memcpy(buf, path, len);
buf[len] = '\0';
/* Try to create intermediate directory */
if (_shttpd_stat(buf, &st) == -1 &&
_shttpd_mkdir(buf, 0755) != 0)
return (-1);
/* Is path itself a directory ? */
if (p[1] == '\0')
return (0);
}
return (1);
}
static int
read_dir(struct stream *stream, void *buf, size_t len)
{
static const char footer[] = "</table></body></html>\n";
struct dirent *dp = NULL;
char file[FILENAME_MAX], line[FILENAME_MAX + 512],
size[64], mod[64];
struct stat st;
struct conn *c = stream->conn;
int n, nwritten = 0;
const char *slash = "";
assert(stream->chan.dir.dirp != NULL);
assert(stream->conn->uri[0] != '\0');
do {
if (len < sizeof(line))
break;
if ((dp = readdir(stream->chan.dir.dirp)) == NULL)
break;
DBG(("read_dir: %s", dp->d_name));
/* Do not show current dir and passwords file */
if (strcmp(dp->d_name, ".") == 0 ||
strcmp(dp->d_name, HTPASSWD) == 0)
continue;
(void) _shttpd_snprintf(file, sizeof(file),
"%s%s%s", stream->chan.dir.path, slash, dp->d_name);
(void) _shttpd_stat(file, &st);
if (S_ISDIR(st.st_mode)) {
_shttpd_snprintf(size,sizeof(size),"%s","&lt;DIR&gt;");
} else {
if (st.st_size < 1024)
(void) _shttpd_snprintf(size, sizeof(size),
"%lu", (unsigned long) st.st_size);
else if (st.st_size < 1024 * 1024)
(void) _shttpd_snprintf(size,
sizeof(size), "%luk",
(unsigned long) (st.st_size >> 10) + 1);
else
(void) _shttpd_snprintf(size, sizeof(size),
"%.1fM", (float) st.st_size / 1048576);
}
(void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
localtime(&st.st_mtime));
n = _shttpd_snprintf(line, sizeof(line),
"<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
"<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
c->uri, slash, dp->d_name, dp->d_name,
S_ISDIR(st.st_mode) ? "/" : "", mod, size);
(void) memcpy(buf, line, n);
buf = (char *) buf + n;
nwritten += n;
len -= n;
} while (dp != NULL);
/* Append proper HTML footer for the page */
if (dp == NULL && len >= sizeof(footer)) {
(void) memcpy(buf, footer, sizeof(footer));
nwritten += sizeof(footer);
stream->flags |= FLAG_CLOSED;
}
return (nwritten);
}
static void
close_dir(struct stream *stream)
{
assert(stream->chan.dir.dirp != NULL);
assert(stream->chan.dir.path != NULL);
(void) closedir(stream->chan.dir.dirp);
free(stream->chan.dir.path);
}
void
_shttpd_get_dir(struct conn *c)
{
if ((c->loc.chan.dir.dirp = opendir(c->loc.chan.dir.path)) == NULL) {
(void) free(c->loc.chan.dir.path);
_shttpd_send_server_error(c, 500, "Cannot open directory");
} else {
c->loc.io.head = _shttpd_snprintf(c->loc.io.buf, c->loc.io.size,
"HTTP/1.1 200 OK\r\n"
"Connection: close\r\n"
"Content-Type: text/html; charset=utf-8\r\n\r\n"
"<html><head><title>Index of %s</title>"
"<style>th {text-align: left;}</style></head>"
"<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
"<tr><th>Name</th><th>Modified</th><th>Size</th></tr>"
"<tr><td colspan=\"3\"><hr></td></tr>",
c->uri, c->uri);
io_clear(&c->rem.io);
c->status = 200;
c->loc.io_class = &_shttpd_io_dir;
c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
}
}
const struct io_class _shttpd_io_dir = {
"dir",
read_dir,
NULL,
close_dir
};

View File

@ -1,291 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
const char *
shttpd_version(void)
{
return (SHTTPD_VERSION);
}
static void
call_user(struct conn *c, struct shttpd_arg *arg, shttpd_callback_t func)
{
arg->priv = c;
arg->state = c->loc.chan.emb.state;
arg->out.buf = io_space(&c->loc.io);
arg->out.len = io_space_len(&c->loc.io);
arg->out.num_bytes = 0;
arg->in.buf = io_data(&c->rem.io);;
arg->in.len = io_data_len(&c->rem.io);
arg->in.num_bytes = 0;
if (io_data_len(&c->rem.io) >= c->rem.io.size)
arg->flags |= SHTTPD_POST_BUFFER_FULL;
if (c->rem.content_len > 0 && c->rem.io.total < c->rem.content_len)
arg->flags |= SHTTPD_MORE_POST_DATA;
func(arg);
io_inc_head(&c->loc.io, arg->out.num_bytes);
io_inc_tail(&c->rem.io, arg->in.num_bytes);
c->loc.chan.emb.state = arg->state; /* Save state */
/*
* If callback finished output, that means it did all cleanup.
* If the connection is terminated unexpectedly, we canna call
* the callback via the stream close() method from disconnect.
* However, if cleanup is already done, we set close() method to
* NULL, to prevent the call from disconnect().
*/
if (arg->flags & SHTTPD_END_OF_OUTPUT)
c->loc.flags &= ~FLAG_DONT_CLOSE;
else
c->loc.flags |= FLAG_DONT_CLOSE;
if (arg->flags & SHTTPD_SUSPEND)
c->loc.flags |= FLAG_SUSPEND;
}
static int
do_embedded(struct stream *stream, void *buf, size_t len)
{
struct shttpd_arg arg;
buf = NULL; len = 0; /* Squash warnings */
arg.user_data = stream->conn->loc.chan.emb.data;
arg.flags = 0;
call_user(stream->conn, &arg, (shttpd_callback_t)
stream->conn->loc.chan.emb.func.v_func);
return (0);
}
static void
close_embedded(struct stream *stream)
{
struct shttpd_arg arg;
struct conn *c = stream->conn;
arg.flags = SHTTPD_CONNECTION_ERROR;
arg.user_data = c->loc.chan.emb.data;
/*
* Do not call the user function if SHTTPD_END_OF_OUTPUT was set,
* i.e. the callback already terminated correctly
*/
if (stream->flags & FLAG_DONT_CLOSE)
call_user(stream->conn, &arg, (shttpd_callback_t)
c->loc.chan.emb.func.v_func);
}
size_t
shttpd_printf(struct shttpd_arg *arg, const char *fmt, ...)
{
char *buf = arg->out.buf + arg->out.num_bytes;
int buflen = arg->out.len - arg->out.num_bytes, len = 0;
va_list ap;
if (buflen > 0) {
va_start(ap, fmt);
len = vsnprintf(buf, buflen, fmt, ap);
va_end(ap);
if (len < 0 || len > buflen)
len = buflen;
arg->out.num_bytes += len;
}
return (len);
}
const char *
shttpd_get_header(struct shttpd_arg *arg, const char *header_name)
{
struct conn *c = arg->priv;
char *p, *s, *e;
size_t len;
p = c->headers;
e = c->request + c->rem.headers_len;
len = strlen(header_name);
while (p < e) {
if ((s = strchr(p, '\n')) != NULL)
s[s[-1] == '\r' ? -1 : 0] = '\0';
if (_shttpd_strncasecmp(header_name, p, len) == 0)
return (p + len + 2);
p += strlen(p) + 1;
}
return (NULL);
}
const char *
shttpd_get_env(struct shttpd_arg *arg, const char *env_name)
{
struct conn *c = arg->priv;
struct vec *vec;
if (strcmp(env_name, "REQUEST_METHOD") == 0) {
return (_shttpd_known_http_methods[c->method].ptr);
} else if (strcmp(env_name, "REQUEST_URI") == 0) {
return (c->uri);
} else if (strcmp(env_name, "QUERY_STRING") == 0) {
return (c->query);
} else if (strcmp(env_name, "REMOTE_USER") == 0) {
vec = &c->ch.user.v_vec;
if (vec->len > 0) {
((char *) vec->ptr)[vec->len] = '\0';
return (vec->ptr);
}
} else if (strcmp(env_name, "REMOTE_ADDR") == 0) {
return (inet_ntoa(c->sa.u.sin.sin_addr));/* FIXME NOT MT safe */
}
return (NULL);
}
void
shttpd_get_http_version(struct shttpd_arg *arg,
unsigned long *major, unsigned long *minor)
{
struct conn *c = arg->priv;
*major = c->major_version;
*minor = c->minor_version;
}
void
shttpd_register_uri(struct shttpd_ctx *ctx,
const char *uri, shttpd_callback_t callback, void *data)
{
struct registered_uri *e;
if ((e = malloc(sizeof(*e))) != NULL) {
e->uri = _shttpd_strdup(uri);
e->callback.v_func = (void (*)(void)) callback;
e->callback_data = data;
LL_TAIL(&ctx->registered_uris, &e->link);
}
}
int
shttpd_get_var(const char *var, const char *buf, int buf_len,
char *value, int value_len)
{
const char *p, *e, *s;
size_t var_len;
var_len = strlen(var);
e = buf + buf_len; /* End of QUERY_STRING buffer */
/* buf is "var1=val1&var2=val2...". Find variable first */
for (p = buf; p + var_len < e; p++)
if ((p == buf || p[-1] == '&') &&
p[var_len] == '=' &&
!_shttpd_strncasecmp(var, p, var_len)) {
/* Point 'p' to var value, 's' to the end of value */
p += var_len + 1;
if ((s = memchr(p, '&', e - p)) == NULL)
s = e;
/* URL-decode value. Return result length */
return (_shttpd_url_decode(p, s - p, value, value_len));
}
return (-1);
}
static int
match_regexp(const char *regexp, const char *text)
{
if (*regexp == '\0')
return (*text == '\0');
if (*regexp == '*')
do {
if (match_regexp(regexp + 1, text))
return (1);
} while (*text++ != '\0');
if (*text != '\0' && *regexp == *text)
return (match_regexp(regexp + 1, text + 1));
return (0);
}
struct registered_uri *
_shttpd_is_registered_uri(struct shttpd_ctx *ctx, const char *uri)
{
struct llhead *lp;
struct registered_uri *reg_uri;
LL_FOREACH(&ctx->registered_uris, lp) {
reg_uri = LL_ENTRY(lp, struct registered_uri, link);
if (match_regexp(reg_uri->uri, uri))
return (reg_uri);
}
return (NULL);
}
void
_shttpd_setup_embedded_stream(struct conn *c, union variant func, void *data)
{
c->loc.chan.emb.state = NULL;
c->loc.chan.emb.func = func;
c->loc.chan.emb.data = data;
c->loc.io_class = &_shttpd_io_embedded;
c->loc.flags |= FLAG_R | FLAG_W |FLAG_ALWAYS_READY;
}
void
shttpd_handle_error(struct shttpd_ctx *ctx, int code,
shttpd_callback_t func, void *data)
{
struct error_handler *e;
if ((e = malloc(sizeof(*e))) != NULL) {
e->code = code;
e->callback.v_func = (void (*)(void)) func;
e->callback_data = data;
LL_TAIL(&ctx->error_handlers, &e->link);
}
}
void
shttpd_wakeup(const void *priv)
{
const struct conn *conn = priv;
char buf[sizeof(int) + sizeof(void *)];
int cmd = CTL_WAKEUP;
#if 0
conn->flags &= ~SHTTPD_SUSPEND;
#endif
(void) memcpy(buf, &cmd, sizeof(cmd));
(void) memcpy(buf + sizeof(cmd), conn, sizeof(conn));
(void) send(conn->worker->ctl[1], buf, sizeof(buf), 0);
}
const struct io_class _shttpd_io_embedded = {
"embedded",
do_embedded,
(int (*)(struct stream *, const void *, size_t)) do_embedded,
close_embedded
};

View File

@ -1,157 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
static int
write_file(struct stream *stream, const void *buf, size_t len)
{
struct stat st;
struct stream *rem = &stream->conn->rem;
int n, fd = stream->chan.fd;
assert(fd != -1);
n = write(fd, buf, len);
DBG(("put_file(%p, %d): %d bytes", (void *) stream, (int) len, n));
if (n <= 0 || (rem->io.total >= (big_int_t) rem->content_len)) {
(void) fstat(fd, &st);
stream->io.head = stream->headers_len =
_shttpd_snprintf(stream->io.buf,
stream->io.size, "HTTP/1.1 %d OK\r\n"
"Content-Length: %lu\r\nConnection: close\r\n\r\n",
stream->conn->status, st.st_size);
_shttpd_stop_stream(stream);
}
return (n);
}
static int
read_file(struct stream *stream, void *buf, size_t len)
{
#ifdef USE_SENDFILE
struct iovec vec;
struct sf_hdtr hd = {&vec, 1, NULL, 0}, *hdp = &hd;
int sock, fd, n;
size_t nbytes;
off_t sent;
sock = stream->conn->rem.chan.sock;
fd = stream->chan.fd;
/* If this is the first call for this file, send the headers */
vec.iov_base = stream->io.buf;
vec.iov_len = stream->headers_len;
if (stream->io.total > 0)
hdp = NULL;
nbytes = stream->content_len - stream->io.total;
n = sendfile(fd, sock, lseek(fd, 0, SEEK_CUR), nbytes, hdp, &sent, 0);
if (n == -1 && ERRNO != EAGAIN) {
stream->flags &= ~FLAG_DONT_CLOSE;
return (n);
}
stream->conn->ctx->out += sent;
/* If we have sent the HTTP headers in this turn, clear them off */
if (stream->io.total == 0) {
assert(sent >= stream->headers_len);
sent -= stream->headers_len;
io_clear(&stream->io);
}
(void) lseek(fd, sent, SEEK_CUR);
stream->io.total += sent;
stream->flags |= FLAG_DONT_CLOSE;
return (0);
#endif /* USE_SENDFILE */
assert(stream->chan.fd != -1);
return (read(stream->chan.fd, buf, len));
}
static void
close_file(struct stream *stream)
{
assert(stream->chan.fd != -1);
(void) close(stream->chan.fd);
}
void
_shttpd_get_file(struct conn *c, struct stat *stp)
{
char date[64], lm[64], etag[64], range[64] = "";
size_t n, status = 200;
unsigned long r1, r2;
const char *fmt = "%a, %d %b %Y %H:%M:%S GMT", *msg = "OK";
big_int_t cl; /* Content-Length */
if (c->mime_type.len == 0)
_shttpd_get_mime_type(c->ctx, c->uri,
strlen(c->uri), &c->mime_type);
cl = (big_int_t) stp->st_size;
/* If Range: header specified, act accordingly */
if (c->ch.range.v_vec.len > 0 &&
(n = sscanf(c->ch.range.v_vec.ptr,"bytes=%lu-%lu",&r1, &r2)) > 0) {
status = 206;
(void) lseek(c->loc.chan.fd, r1, SEEK_SET);
cl = n == 2 ? r2 - r1 + 1: cl - r1;
(void) _shttpd_snprintf(range, sizeof(range),
"Content-Range: bytes %lu-%lu/%lu\r\n",
r1, r1 + cl - 1, (unsigned long) stp->st_size);
msg = "Partial Content";
}
/* Prepare Etag, Date, Last-Modified headers */
(void) strftime(date, sizeof(date),
fmt, localtime(&_shttpd_current_time));
(void) strftime(lm, sizeof(lm), fmt, localtime(&stp->st_mtime));
(void) _shttpd_snprintf(etag, sizeof(etag), "%lx.%lx",
(unsigned long) stp->st_mtime, (unsigned long) stp->st_size);
/*
* We do not do io_inc_head here, because it will increase 'total'
* member in io. We want 'total' to be equal to the content size,
* and exclude the headers length from it.
*/
c->loc.io.head = c->loc.headers_len = _shttpd_snprintf(c->loc.io.buf,
c->loc.io.size,
"HTTP/1.1 %d %s\r\n"
"Date: %s\r\n"
"Last-Modified: %s\r\n"
"Etag: \"%s\"\r\n"
"Content-Type: %.*s\r\n"
"Content-Length: %lu\r\n"
"Accept-Ranges: bytes\r\n"
"%s\r\n",
status, msg, date, lm, etag,
c->mime_type.len, c->mime_type.ptr, cl, range);
c->status = status;
c->loc.content_len = cl;
c->loc.io_class = &_shttpd_io_file;
c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
if (c->method == METHOD_HEAD)
_shttpd_stop_stream(&c->loc);
}
const struct io_class _shttpd_io_file = {
"file",
read_file,
write_file,
close_file
};

View File

@ -1,39 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
static int
read_socket(struct stream *stream, void *buf, size_t len)
{
assert(stream->chan.sock != -1);
return (recv(stream->chan.sock, buf, len, 0));
}
static int
write_socket(struct stream *stream, const void *buf, size_t len)
{
assert(stream->chan.sock != -1);
return (send(stream->chan.sock, buf, len, 0));
}
static void
close_socket(struct stream *stream)
{
assert(stream->chan.sock != -1);
(void) closesocket(stream->chan.sock);
}
const struct io_class _shttpd_io_socket = {
"socket",
read_socket,
write_socket,
close_socket
};

View File

@ -1,85 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
#if !defined(NO_SSL)
struct ssl_func ssl_sw[] = {
{"SSL_free", {0}},
{"SSL_accept", {0}},
{"SSL_connect", {0}},
{"SSL_read", {0}},
{"SSL_write", {0}},
{"SSL_get_error", {0}},
{"SSL_set_fd", {0}},
{"SSL_new", {0}},
{"SSL_CTX_new", {0}},
{"SSLv23_server_method", {0}},
{"SSL_library_init", {0}},
{"SSL_CTX_use_PrivateKey_file", {0}},
{"SSL_CTX_use_certificate_file",{0}},
{NULL, {0}}
};
void
_shttpd_ssl_handshake(struct stream *stream)
{
int n;
if ((n = SSL_accept(stream->chan.ssl.ssl)) == 1) {
DBG(("handshake: SSL accepted"));
stream->flags |= FLAG_SSL_ACCEPTED;
} else {
n = SSL_get_error(stream->chan.ssl.ssl, n);
if (n != SSL_ERROR_WANT_READ && n != SSL_ERROR_WANT_WRITE)
stream->flags |= FLAG_CLOSED;
DBG(("SSL_accept error %d", n));
}
}
static int
read_ssl(struct stream *stream, void *buf, size_t len)
{
int nread = -1;
assert(stream->chan.ssl.ssl != NULL);
if (!(stream->flags & FLAG_SSL_ACCEPTED))
_shttpd_ssl_handshake(stream);
if (stream->flags & FLAG_SSL_ACCEPTED)
nread = SSL_read(stream->chan.ssl.ssl, buf, len);
return (nread);
}
static int
write_ssl(struct stream *stream, const void *buf, size_t len)
{
assert(stream->chan.ssl.ssl != NULL);
return (SSL_write(stream->chan.ssl.ssl, buf, len));
}
static void
close_ssl(struct stream *stream)
{
assert(stream->chan.ssl.sock != -1);
assert(stream->chan.ssl.ssl != NULL);
(void) closesocket(stream->chan.ssl.sock);
SSL_free(stream->chan.ssl.ssl);
}
const struct io_class _shttpd_io_ssl = {
"ssl",
read_ssl,
write_ssl,
close_ssl
};
#endif /* !NO_SSL */

View File

@ -1,59 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef LLIST_HEADER_INCLUDED
#define LLIST_HEADER_INCLUDED
/*
* Linked list macros.
*/
struct llhead {
struct llhead *prev;
struct llhead *next;
};
#define LL_INIT(N) ((N)->next = (N)->prev = (N))
#define LL_HEAD(H) struct llhead H = { &H, &H }
#define LL_ENTRY(P,T,N) ((T *)((char *)(P) - offsetof(T, N)))
#define LL_ADD(H, N) \
do { \
((H)->next)->prev = (N); \
(N)->next = ((H)->next); \
(N)->prev = (H); \
(H)->next = (N); \
} while (0)
#define LL_TAIL(H, N) \
do { \
((H)->prev)->next = (N); \
(N)->prev = ((H)->prev); \
(N)->next = (H); \
(H)->prev = (N); \
} while (0)
#define LL_DEL(N) \
do { \
((N)->next)->prev = ((N)->prev); \
((N)->prev)->next = ((N)->next); \
LL_INIT(N); \
} while (0)
#define LL_EMPTY(N) ((N)->next == (N))
#define LL_FOREACH(H,N) for (N = (H)->next; N != (H); N = (N)->next)
#define LL_FOREACH_SAFE(H,N,T) \
for (N = (H)->next, T = (N)->next; N != (H); \
N = (T), T = (N)->next)
#endif /* LLIST_HEADER_INCLUDED */

View File

@ -1,107 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
/*
* Log function
*/
void
_shttpd_elog(int flags, struct conn *c, const char *fmt, ...)
{
FILE *fp = c == NULL ? NULL : c->ctx->error_log;
va_list ap;
/* Print to stderr */
if (c == NULL || !IS_TRUE(c->ctx, OPT_INETD)) {
va_start(ap, fmt);
(void) vfprintf(stderr, fmt, ap);
(void) fputc('\n', stderr);
va_end(ap);
}
if (fp != NULL && (flags & (E_FATAL | E_LOG))) {
char date[64];
char *buf = malloc( URI_MAX );
int len;
strftime(date, sizeof(date), "%a %b %d %H:%M:%S %Y",
localtime(&_shttpd_current_time));
len = _shttpd_snprintf(buf, URI_MAX,
"[%s] [error] [client %s] \"%s\" ",
date, c ? inet_ntoa(c->sa.u.sin.sin_addr) : "-",
c && c->request ? c->request : "-");
va_start(ap, fmt);
(void) vsnprintf(buf + len, URI_MAX - len, fmt, ap);
va_end(ap);
buf[URI_MAX - 1] = '\0';
(void) fprintf(fp, "%s\n", buf);
(void) fflush(fp);
free(buf);
}
if (flags & E_FATAL)
exit(EXIT_FAILURE);
}
void
_shttpd_log_access(FILE *fp, const struct conn *c)
{
static const struct vec dash = {"-", 1};
const struct vec *user;
const struct vec *referer;
const struct vec *user_agent;
char date[64], *buf, *q1, *q2;
if (fp == NULL)
return;
user = &c->ch.user.v_vec;
referer = &c->ch.referer.v_vec;
user_agent = &c->ch.useragent.v_vec;
q1 = q2 = "\"";
if (user->len == 0)
user = &dash;
if (referer->len == 0) {
referer = &dash;
q1 = "";
}
if (user_agent->len == 0) {
user_agent = &dash;
q2 = "";
}
(void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S",
localtime(&c->birth_time));
buf = malloc(URI_MAX);
(void) _shttpd_snprintf(buf, URI_MAX,
"%s - %.*s [%s %+05d] \"%s\" %d %lu %s%.*s%s %s%.*s%s",
inet_ntoa(c->sa.u.sin.sin_addr), user->len, user->ptr,
date, _shttpd_tz_offset, c->request ? c->request : "-",
c->status, (unsigned long) c->loc.io.total,
q1, referer->len, referer->ptr, q1,
q2, user_agent->len, user_agent->ptr, q2);
(void) fprintf(fp, "%s\n", buf);
(void) fflush(fp);
free(buf);
}

View File

@ -1,249 +0,0 @@
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
#include "defs.h"
#ifndef HAVE_MD5
#if __BYTE_ORDER == 1234
#define byteReverse(buf, len) /* Nothing */
#else
/*
* Note: this code is harmless on little-endian machines.
*/
static void byteReverse(unsigned char *buf, unsigned longs)
{
uint32_t t;
do {
t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
((unsigned) buf[1] << 8 | buf[0]);
*(uint32_t *) buf = t;
buf += 4;
} while (--longs);
}
#endif /* __BYTE_ORDER */
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void MD5Init(MD5_CTX *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
static void MD5Transform(uint32_t buf[4], uint32_t const in[16])
{
register uint32_t a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void
MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len)
{
uint32_t t;
/* Update bitcount */
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
ctx->bits[1]++; /* Carry from low to high */
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
/* Handle any leading odd-sized chunks */
if (t) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += t;
len -= t;
}
/* Process data in 64-byte chunks */
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->in, buf, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void
MD5Final(unsigned char digest[16], MD5_CTX *ctx)
{
unsigned count;
unsigned char *p;
/* Compute number of bytes mod 64 */
count = (ctx->bits[0] >> 3) & 0x3F;
/* Set the first char of padding to 0x80. This is safe since there is
always at least one byte free */
p = ctx->in + count;
*p++ = 0x80;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count;
/* Pad out to 56 mod 64 */
if (count < 8) {
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
} else {
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
((uint32_t *) ctx->in)[14] = ctx->bits[0];
((uint32_t *) ctx->in)[15] = ctx->bits[1];
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */
}
#endif /* !HAVE_MD5 */

View File

@ -1,24 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef MD5_HEADER_INCLUDED
#define MD5_HEADER_INCLUDED
typedef struct MD5Context {
uint32_t buf[4];
uint32_t bits[2];
unsigned char in[64];
} MD5_CTX;
extern void MD5Init(MD5_CTX *ctx);
extern void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len);
extern void MD5Final(unsigned char digest[16], MD5_CTX *ctx);
#endif /*MD5_HEADER_INCLUDED */

View File

@ -1,172 +0,0 @@
.\" Process this file with
.\" groff -man -Tascii shttpd.1
.\" $Id: shttpd.1,v 1.9 2008/02/13 21:57:27 drozd Exp $
.Dd Feb 12, 2008
.Dt SHTTPD 1
.Sh NAME
.Nm shttpd
.Nd lightweight web server
.Sh SYNOPSIS
.Nm
.Op Ar options
.Op Ar config_file
.Nm
.Fl A Ar htpasswd_file domain_name user_name password
.Sh DESCRIPTION
.Nm
is small, fast and easy to use web server with CGI, SSL, Digest Authorization
support. It can be run as stand-alone server, be managed by
.Xr inetd 8
, or be embedded into existing C/C++ application.
.Pp
.Nm
does not detach from terminal, and makes current working directory
be the web root, unless
.Fl root
option is specified.
.Pp
Unlike other web servers,
.Nm
does not expect CGI scirpts to be put in a special directory. They may be
anywhere. CGI files are recognized by the file extension.
.Pp
SSI files are also recognized by extension. Currently, the only SSI directives
supported are `<!--#include "url-encoded-path" -->'
and `<!--#exec "program" -->'. The `url-encoded-path' can be relative to
.Nm
working directory, or absolute system path. In the embedded mode, more
directives are available: #call, #if/#elif/#endif/#else/#endif.
Unsupported SSI directives are silently ignored.
.Pp
It is possible to specify multiple ports to listen on. For example, to
make
.Nm
listen on HTTP port 80 and HTTPS port 443, one should start it as
.Sq shttpd -ssl_cert cert.pem -ports 80,443s
.Pp
.Nm
can use the configuration file. By default, it is "shttpd.conf", and if it
is present in the same directory where
.Nm
lives, the command line options are read from it. Alternatively, the
configuration file may be specified as a last argument. The format of the
configuration file is exactly the same as for the command line options, the
only difference is that the command line options must be specified on
separate lines, and dashes for options must be omitted.
Lines beginning with '#' are regarded as comments and ignored.
.Pp
.Sh OPTIONS
.Bl -tag -width indent
.It Fl A Ar htpasswd_file domain_name user_name password
Add/edit user's password in the passwords file. Deleting users can be done
with any text editor. Functionality similar to Apache's
.Ic htdigest
utility.
.It Fl access_log Ar file
Access log file. Default: not set, no logging is done.
.It Fl acl Ar (+|-)x.x.x.x[/x],...
Specify access control list (ACL). ACL is a comma separated list
of IP subnets, each subnet is prepended by '-' or '+' sign. Plus means allow,
minus means deny. If subnet mask is
omitted, like "-1.2.3.4", then it means single IP address. Mask may vary
from 0 to 32 inclusive. Default: not set, allow all.
.It Fl aliases Ar list
This options gives an ability to serve the directories outside web root
by sort of symbolic linking to certain URI. The
.Ar list
must be comma-separated list of URI=PATH pairs, like this:
"/etc/=/my_etc,/tmp=/my_tmp". Default: not set.
.It Fl auth_PUT Ar file
PUT and DELETE passwords file. This must be specified if PUT or
DELETE methods are used. Default: not set.
.It Fl auth_gpass Ar file
Location of global passwords file. When set, per-directory .htpasswd files are
ignored, and all accessed must be authorised against global passwords file.
Default: not set.
.It Fl auth_realm Ar domain_name
Authorization realm. Default: "mydomain.com".
.It Fl cfg_uri Ar uri
If set,
.Nm
creates special administrative URI where options may be changed at runtime.
This URI probably wants to be password-protected, look at
.Fl protect
option, and in the EXAMPLES section on how to do it. Default: not set.
.It Fl cgi_env Ar list
Pass environment variables to the CGI script in addition to standard ones.
The list must be comma-separated list of X=Y pairs, like this:
"VARIABLE1=VALUE1,VARIABLE2=VALUE2". Default: not set.
.It Fl cgi_ext Ar list
Comma-separated list of CGI extensions. All files having these extensions
are treated as CGI scripts. Default: "cgi,pl,php"
.It Fl cgi_interp Ar file
Force
.Ar file
to be a CGI interpreter for all CGI scripts. By default this option is not
set, and
.Nm
decides which interpreter to use by looking at the first line of CGI script.
.It Fl dir_list Ar 0|1
Enable/disable directory listing. Default: "1" (enabled).
.It Fl error_log Ar file
Error log file. Default: not set, no errors are logged.
.It Fl inetd Ar 0|1
Enable/disable inetd mode. Default: "0" (disabled).
.It Fl mime_types Ar list
Additional to builtin mime types, in form "EXTENSION1=TYPE1,EXTENSION2=TYPE2".
.It Fl ports Ar port_list
Comma-separated list of ports to listen on. If the port is SSL, a letter 's'
must be appeneded, for example, "80,443s" will open port 80 and port 443,
and connections on port 443 will be SSL-ed. Default: 80
.It Fl protect Ar list
Comma separated list of URI=PATH pairs, specifying that given URIs
must the protected with respected password files. Default: not set.
.It Fl root Ar directory
Location of the WWW root directory. Default: working directory from which
.Nm
has been started.
.It Fl ssi_ext Ar list
Comma separated list of SSI extensions. Default: "shtml,shtm".
.It Fl ssl_cert Ar pem_file
Location of SSL certificate file. Default: not set.
.It Fl uid Ar login
Switch to given user after startup. Default: not set.
.El
.Pp
.Sh EMBEDDING
.Nm
can be built as a library to embed web server functionality
into C/C++ application. The API functions are declared in a header
file
.Pa shttpd.h .
Please refer to the source package for a header file and the examples.
.Pp
.Sh EXAMPLES
.Bl -tag -width indent
.It Nm Fl root Ar /var/www Fl ports Ar 8080,8043s Fl ssl_cert Ar /etc/cert.pem Fl aliases Ar /aa=/tmp,/bb=/etc
Start listening on port 8080 for HTTP, and 8043 for HTTPS connections.
Use /etc/cert.pem as SSL certificate file. Web root is /var/www. In addition,
map directory /tmp to URI /aa, directory /etc to URI /bb.
.It Nm Fl acl Ar -0.0.0.0/0,+10.0.0.0/8,+1.2.3.4
Deny connections from everywhere, allow only IP address 1.2.3.4 and
all IP addresses from 10.0.0.0/8 subnet to connect.
.It Nm Fl ports Ar 8080 Fl cfg_uri Ar /ctl Fl protect Ar /ctl=/tmp/passwords.txt
Start listening on port 8080, create an administrative URI "/ctl" where
options may be changed at runtime, and protect that URI with authorization.
.It http stream tcp nowait nobody /bin/shttpd shttpd -inetd 1 -root /var/www
This line in
.Pa /etc/inetd.conf
makes
.Nm
run by
.Xr inetd 8
daemon.
.El
.Pp
.Sh SEE ALSO
.Xr inetd 8 .
.Sh COPYRIGHT
.Nm
is licensed under the terms of beerware license.
.Sh AUTHOR
.An Sergey Lyubka Aq valenok@gmail.com .

File diff suppressed because it is too large Load Diff

View File

@ -1,116 +0,0 @@
/*
* Copyright (c) 2004-2008 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*
* $Id: shttpd.h,v 1.18 2008/08/23 08:34:50 drozd Exp $
*/
#ifndef SHTTPD_HEADER_INCLUDED
#define SHTTPD_HEADER_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct ubuf {
char *buf; /* Buffer pointer */
int len; /* Size of a buffer */
int num_bytes; /* Bytes processed by callback */
};
/*
* This structure is passed to the user callback function
*/
struct shttpd_arg {
void *priv; /* Private! Do not touch! */
void *state; /* User state */
void *user_data; /* Data from register_uri() */
struct ubuf in; /* Input is here, POST data */
struct ubuf out; /* Output goes here */
unsigned int flags;
#define SHTTPD_END_OF_OUTPUT 1 /* No more data do send */
#define SHTTPD_CONNECTION_ERROR 2 /* Server closed the connection */
#define SHTTPD_MORE_POST_DATA 4 /* arg->in has incomplete data */
#define SHTTPD_POST_BUFFER_FULL 8 /* arg->in has max data */
#define SHTTPD_SSI_EVAL_TRUE 16 /* SSI eval callback must set it*/
#define SHTTPD_SUSPEND 32 /* User wants to suspend output */
};
/*
* User callback function. Called when certain registered URLs have been
* requested. These are the requirements to the callback function:
*
* 1. It must copy data into 'out.buf' buffer, not more than 'out.len' bytes,
* and record how many bytes are copied, into 'out.num_bytes'
* 2. It must not call any blocking functions
* 3. It must set SHTTPD_END_OF_OUTPUT flag when there is no more data to send
* 4. For POST requests, it must process the incoming data (in.buf) of length
* 'in.len', and set 'in.num_bytes', which is how many bytes of POST
* data was processed and can be discarded by SHTTPD.
* 5. If callback allocates arg->state, to keep state, it must deallocate it
* at the end of coonection SHTTPD_CONNECTION_ERROR or SHTTPD_END_OF_OUTPUT
* 6. If callback function wants to suspend until some event, it must store
* arg->priv pointer elsewhere, set SHTTPD_SUSPEND flag and return. When
* the event happens, user code should call shttpd_wakeup(priv).
* It is safe to call shttpd_wakeup() from any thread. User code must
* not call shttpd_wakeup once the connection is closed.
*/
typedef void (*shttpd_callback_t)(struct shttpd_arg *);
/*
* shttpd_init Initialize shttpd context
* shttpd_fini Dealocate the context, close all connections
* shttpd_set_option Set new value for option
* shttpd_register_uri Setup the callback function for specified URL
* shttpd_poll Do connections processing
* shttpd_version return string with SHTTPD version
* shttpd_get_var Fetch POST/GET variable value by name. Return value len
* shttpd_get_header return value of the specified HTTP header
* shttpd_get_env return values for the following pseudo-variables:
"REQUEST_METHOD", "REQUEST_URI",
* "REMOTE_USER" and "REMOTE_ADDR"
* shttpd_printf helper function to output data
* shttpd_handle_error register custom HTTP error handler
* shttpd_wakeup clear SHTTPD_SUSPEND state for the connection
*/
struct shttpd_ctx;
struct shttpd_ctx *shttpd_init(int argc, char *argv[]);
int shttpd_set_option(struct shttpd_ctx *, const char *opt, const char *val);
void shttpd_fini(struct shttpd_ctx *);
void shttpd_register_uri(struct shttpd_ctx *ctx, const char *uri,
shttpd_callback_t callback, void *const user_data);
void shttpd_poll(struct shttpd_ctx *, int milliseconds);
const char *shttpd_version(void);
int shttpd_get_var(const char *var, const char *buf, int buf_len,
char *value, int value_len);
const char *shttpd_get_header(struct shttpd_arg *, const char *header_name);
const char *shttpd_get_env(struct shttpd_arg *, const char *name);
void shttpd_get_http_version(struct shttpd_arg *,
unsigned long *major, unsigned long *minor);
size_t shttpd_printf(struct shttpd_arg *, const char *fmt, ...);
void shttpd_handle_error(struct shttpd_ctx *ctx, int status,
shttpd_callback_t func, void *const data);
void shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
shttpd_callback_t func, void *const user_data);
void shttpd_wakeup(const void *priv);
int shttpd_join(struct shttpd_ctx *, fd_set *, fd_set *, int *max_fd);
int shttpd_socketpair(int sp[2]);
extern int shttpd_edit_passwords(const char *fname, const char *domain,
const char *user, const char *pass);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SHTTPD_HEADER_INCLUDED */

View File

@ -1,52 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
/*
* Snatched from OpenSSL includes. I put the prototypes here to be independent
* from the OpenSSL source installation. Having this, shttpd + SSL can be
* built on any system with binary SSL libraries installed.
*/
typedef struct ssl_st SSL;
typedef struct ssl_method_st SSL_METHOD;
typedef struct ssl_ctx_st SSL_CTX;
#define SSL_ERROR_WANT_READ 2
#define SSL_ERROR_WANT_WRITE 3
#define SSL_FILETYPE_PEM 1
/*
* Dynamically loaded SSL functionality
*/
struct ssl_func {
const char *name; /* SSL function name */
union variant ptr; /* Function pointer */
};
extern struct ssl_func ssl_sw[];
#define FUNC(x) ssl_sw[x].ptr.v_func
#define SSL_free(x) (* (void (*)(SSL *)) FUNC(0))(x)
#define SSL_accept(x) (* (int (*)(SSL *)) FUNC(1))(x)
#define SSL_connect(x) (* (int (*)(SSL *)) FUNC(2))(x)
#define SSL_read(x,y,z) (* (int (*)(SSL *, void *, int)) FUNC(3))((x),(y),(z))
#define SSL_write(x,y,z) \
(* (int (*)(SSL *, const void *,int)) FUNC(4))((x), (y), (z))
#define SSL_get_error(x,y)(* (int (*)(SSL *, int)) FUNC(5))((x), (y))
#define SSL_set_fd(x,y) (* (int (*)(SSL *, int)) FUNC(6))((x), (y))
#define SSL_new(x) (* (SSL * (*)(SSL_CTX *)) FUNC(7))(x)
#define SSL_CTX_new(x) (* (SSL_CTX * (*)(SSL_METHOD *)) FUNC(8))(x)
#define SSLv23_server_method() (* (SSL_METHOD * (*)(void)) FUNC(9))()
#define SSL_library_init() (* (int (*)(void)) FUNC(10))()
#define SSL_CTX_use_PrivateKey_file(x,y,z) (* (int (*)(SSL_CTX *, \
const char *, int)) FUNC(11))((x), (y), (z))
#define SSL_CTX_use_certificate_file(x,y,z) (* (int (*)(SSL_CTX *, \
const char *, int)) FUNC(12))((x), (y), (z))

View File

@ -1,72 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
static int exit_flag; /* Program termination flag */
static void
signal_handler(int sig_num)
{
switch (sig_num) {
#ifndef _WIN32
case SIGCHLD:
while (waitpid(-1, &sig_num, WNOHANG) > 0) ;
break;
#endif /* !_WIN32 */
default:
exit_flag = sig_num;
break;
}
}
int
main(int argc, char *argv[])
{
struct shttpd_ctx *ctx;
#if !defined(NO_AUTH)
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') {
if (argc != 6)
_shttpd_usage(argv[0]);
exit(shttpd_edit_passwords(argv[2],argv[3],argv[4],argv[5]));
}
#endif /* NO_AUTH */
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))
_shttpd_usage(argv[0]);
#if defined(_WIN32)
try_to_run_as_nt_service();
#endif /* _WIN32 */
#ifndef _WIN32
(void) signal(SIGCHLD, signal_handler);
(void) signal(SIGPIPE, SIG_IGN);
#endif /* _WIN32 */
(void) signal(SIGTERM, signal_handler);
(void) signal(SIGINT, signal_handler);
if ((ctx = shttpd_init(argc, argv)) == NULL)
_shttpd_elog(E_FATAL, NULL, "%s",
"Cannot initialize SHTTPD context");
_shttpd_elog(E_LOG, NULL, "shttpd %s started on port(s) %s, serving %s",
SHTTPD_VERSION, ctx->options[OPT_PORTS], ctx->options[OPT_ROOT]);
while (exit_flag == 0)
shttpd_poll(ctx, 10 * 1000);
_shttpd_elog(E_LOG, NULL, "Exit on signal %d", exit_flag);
shttpd_fini(ctx);
return (EXIT_SUCCESS);
}

View File

@ -1,40 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef STD_HEADERS_INCLUDED
#define STD_HEADERS_INCLUDED
#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#endif /* _WIN32_WCE */
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#if defined(_WIN32) /* Windows specific */
#include "compat_win32.h"
#elif defined(__rtems__) /* RTEMS specific */
#include "compat_rtems.h"
#else /* UNIX specific */
#include "compat_unix.h"
#endif /* _WIN32 */
#endif /* STD_HEADERS_INCLUDED */

View File

@ -1,95 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
void
_shttpd_strlcpy(register char *dst, register const char *src, size_t n)
{
for (; *src != '\0' && n > 1; n--)
*dst++ = *src++;
*dst = '\0';
}
int
_shttpd_strncasecmp(const char *str1, const char *str2, size_t len)
{
register const unsigned char *s1 = (unsigned char *) str1,
*s2 = (unsigned char *) str2, *e;
int ret;
for (e = s1 + len - 1; s1 < e && *s1 != '\0' && *s2 != '\0' &&
tolower(*s1) == tolower(*s2); s1++, s2++) ;
ret = tolower(*s1) - tolower(*s2);
return (ret);
}
char *
_shttpd_strndup(const char *ptr, size_t len)
{
char *p;
if ((p = malloc(len + 1)) != NULL)
_shttpd_strlcpy(p, ptr, len + 1);
return (p);
}
char *
_shttpd_strdup(const char *str)
{
return (_shttpd_strndup(str, strlen(str)));
}
/*
* Sane snprintf(). Acts like snprintf(), but never return -1 or the
* value bigger than supplied buffer.
* Thanks Adam Zeldis to pointing snprintf()-caused vulnerability
* in his audit report.
*/
int
_shttpd_snprintf(char *buf, size_t buflen, const char *fmt, ...)
{
va_list ap;
int n;
if (buflen == 0)
return (0);
va_start(ap, fmt);
n = vsnprintf(buf, buflen, fmt, ap);
va_end(ap);
if (n < 0 || (size_t) n >= buflen)
n = buflen - 1;
buf[n] = '\0';
return (n);
}
/*
* Verify that given file has certain extension
*/
int
_shttpd_match_extension(const char *path, const char *ext_list)
{
size_t len, path_len;
path_len = strlen(path);
FOR_EACH_WORD_IN_LIST(ext_list, len)
if (len < path_len && path[path_len - len - 1] == '.' &&
!_shttpd_strncasecmp(path + path_len - len, ext_list, len))
return (TRUE);
return (FALSE);
}

View File

@ -22,10 +22,9 @@ xmission_SOURCES = \
xmission_LDADD = \
$(top_builddir)/libtransmission/libtransmission.a \
$(top_builddir)/third-party/libevent/libevent_core.la \
$(top_builddir)/third-party/libevent/libevent.la \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/shttpd/libshttpd.a \
$(WX_LIBS) \
$(OPENSSL_LIBS) \
$(LIBCURL_LIBS) \