micro_httpd/0040755000301000001200000000000012373140663011532 5ustar jefwwwmicro_httpd/micro_httpd.80100644000301000001200000000671110243433037014133 0ustar jefwww.TH micro_httpd 8 "15 March 1999" .SH NAME micro_httpd - really small HTTP server .SH SYNOPSIS .B micro_httpd .I directory .SH DESCRIPTION .PP .I micro_httpd is a very small HTTP server. It runs from inetd, which means its performance is poor. But for low-traffic sites, it's quite adequate. It implements all the basic features of an HTTP server, including: .TP 3 * Security against ".." filename snooping. .TP 3 * The common MIME types. .TP 3 * Trailing-slash redirection. .TP 3 * index.html .TP 3 * Directory listings. .PP All in 150 lines of code. .PP To install it, add a line like this to /etc/inetd.conf: .nf micro_http stream tcp nowait nobody /usr/local/sbin/micro_httpd micro_httpd dir .fi Make sure the path to the executable is correct, and change "dir" to be the directory you want to serve. Then add a line like this to /etc/services: .nf micro_http port/tcp #Micro HTTP server .fi Change "port" to the port number you want to use - 80, 8000, whatever. Then restart inetd by sending it a "HUP" signal, or rebooting. .PP On some systems, inetd has a maximum spawn rate - if you try to run inetd services faster than a certain number of times per minute, it assumed there's either a bug of an attack going on and it shuts down for a few minutes. If you run into this problem - look for syslog messages about too-rapid looping - you'll need to find out how to increase the limit. Unfortunately this varies from OS to OS. On FreeBSD, you add a "-R 10000" flag to inetd's initial command line. On some Linux systems, you can set the limit on a per-service basis in inetd.conf, by changing "nowait" to "nowait.10000". .PP Note that you can use micro_httpd to serve HTTPS, if you like, by running it from stunnel. First fetch and install stunnel - FreeBSD users can just go to /usr/ports/security/stunnel and do a "make cert ; make install". Then as root run: .nf stunnel -p /usr/local/certs/stunnel.pem -d 443 \ -l /usr/local/sbin/micro_httpd -- \ micro_httpd dir .fi Make sure the paths to the certificate and executable are correct, and again don't forget to change "dir" to the directory you want to serve. .SH AUTHOR Copyright © 1999 by Jef Poskanzer . All rights reserved. .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. micro_httpd/Makefile0100644000301000001200000000124412372212746013171 0ustar jefwwwBINDIR = /usr/local/sbin MANDIR = /usr/local/man/man8 CC = cc CFLAGS = -O -ansi -pedantic -U__STRICT_ANSI__ -Wall -Wpointer-arith -Wshadow -Wcast-qual -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wno-long-long #SYSVLIBS = -lnsl -lsocket LDFLAGS = -s $(SYSVLIBS) all: micro_httpd micro_httpd: micro_httpd.o $(CC) micro_httpd.o $(LDFLAGS) -o micro_httpd micro_httpd.o: micro_httpd.c $(CC) $(CFLAGS) -c micro_httpd.c install: all rm -f $(BINDIR)/micro_httpd cp micro_httpd $(BINDIR)/micro_httpd rm -f $(MANDIR)/micro_httpd.8 cp micro_httpd.8 $(MANDIR)/micro_httpd.8 clean: rm -f micro_httpd *.o core core.* *.core micro_httpd/README0100644000301000001200000000170310347427122012405 0ustar jefwww micro_httpd - really small HTTP server micro_httpd is a very small HTTP server. It runs from inetd, which means its performance is poor. But for low-traffic sites, it's quite adequate. It implements all the basic features of an HTTP server, including: * Security against ".." filename snooping. * The common MIME types. * Trailing-slash redirection. * index.html * Directory listings. All in about 200 lines of code. See the manual entry for more details. Files in this distribution: README this Makefile guess micro_httpd.c source file micro_httpd.8 manual entry To build: If you're on a SysV-like machine (which includes old Linux systems but not new Linux systems), edit the Makefile and uncomment the SYSVLIBS line. Otherwise, just do a make. Feedback is welcome - send bug reports, enhancements, checks, money orders, etc. to the addresses below. Jef Poskanzer jef@mail.acme.com http://www.acme.com/jef/ micro_httpd/micro_httpd.c0100644000301000001200000002362012357547505014222 0ustar jefwww/* micro_httpd - really small HTTP server ** ** Copyright © 1999,2005 by Jef Poskanzer . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #define SERVER_NAME "micro_httpd" #define SERVER_URL "http://www.acme.com/software/micro_httpd/" #define PROTOCOL "HTTP/1.0" #define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT" /* Forwards. */ static void file_details( char* dir, char* name ); static void send_error( int status, char* title, char* extra_header, char* text ); static void send_headers( int status, char* title, char* extra_header, char* mime_type, off_t length, time_t mod ); static char* get_mime_type( char* name ); static void strdecode( char* to, char* from ); static int hexit( char c ); static void strencode( char* to, size_t tosize, const char* from ); int main( int argc, char** argv ) { char line[10000], method[10000], path[10000], protocol[10000], idx[20000], location[20000]; char* file; size_t len; int ich; struct stat sb; FILE* fp; struct dirent **dl; int i, n; if ( argc != 2 ) send_error( 500, "Internal Error", (char*) 0, "Config error - no dir specified." ); if ( chdir( argv[1] ) < 0 ) send_error( 500, "Internal Error", (char*) 0, "Config error - couldn't chdir()." ); if ( fgets( line, sizeof(line), stdin ) == (char*) 0 ) send_error( 400, "Bad Request", (char*) 0, "No request found." ); if ( sscanf( line, "%[^ ] %[^ ] %[^ ]", method, path, protocol ) != 3 ) send_error( 400, "Bad Request", (char*) 0, "Can't parse request." ); while ( fgets( line, sizeof(line), stdin ) != (char*) 0 ) { if ( strcmp( line, "\n" ) == 0 || strcmp( line, "\r\n" ) == 0 ) break; } if ( strcasecmp( method, "get" ) != 0 ) send_error( 501, "Not Implemented", (char*) 0, "That method is not implemented." ); if ( path[0] != '/' ) send_error( 400, "Bad Request", (char*) 0, "Bad filename." ); file = &(path[1]); strdecode( file, file ); if ( file[0] == '\0' ) file = "./"; len = strlen( file ); if ( file[0] == '/' || strcmp( file, ".." ) == 0 || strncmp( file, "../", 3 ) == 0 || strstr( file, "/../" ) != (char*) 0 || strcmp( &(file[len-3]), "/.." ) == 0 ) send_error( 400, "Bad Request", (char*) 0, "Illegal filename." ); if ( stat( file, &sb ) < 0 ) send_error( 404, "Not Found", (char*) 0, "File not found." ); if ( S_ISDIR( sb.st_mode ) ) { if ( file[len-1] != '/' ) { (void) snprintf( location, sizeof(location), "Location: %s/", path ); send_error( 302, "Found", location, "Directories must end with a slash." ); } (void) snprintf( idx, sizeof(idx), "%sindex.html", file ); if ( stat( idx, &sb ) >= 0 ) { file = idx; goto do_file; } send_headers( 200, "Ok", (char*) 0, "text/html", -1, sb.st_mtime ); (void) printf( "\ \n\ \n\ \n\ \n\ Index of %s\n\ \n\ \n\

Index of %s

\n\
\n", file, file );
	n = scandir( file, &dl, NULL, alphasort );
	if ( n < 0 )
	    perror( "scandir" );
	else
	    for ( i = 0; i < n; ++i )
		file_details( file, dl[i]->d_name );
	(void) printf( "\
    
\n\
\n\
%s
\n\ \n\ \n", SERVER_URL, SERVER_NAME ); } else { do_file: fp = fopen( file, "r" ); if ( fp == (FILE*) 0 ) send_error( 403, "Forbidden", (char*) 0, "File is protected." ); send_headers( 200, "Ok", (char*) 0, get_mime_type( file ), sb.st_size, sb.st_mtime ); while ( ( ich = getc( fp ) ) != EOF ) putchar( ich ); } (void) fflush( stdout ); exit( 0 ); } static void file_details( char* dir, char* name ) { static char encoded_name[1000]; static char path[2000]; struct stat sb; char timestr[16]; strencode( encoded_name, sizeof(encoded_name), name ); (void) snprintf( path, sizeof(path), "%s/%s", dir, name ); if ( lstat( path, &sb ) < 0 ) (void) printf( "%-32.32s ???\n", encoded_name, name ); else { (void) strftime( timestr, sizeof(timestr), "%d%b%Y %H:%M", localtime( &sb.st_mtime ) ); (void) printf( "%-32.32s %15s %14lld\n", encoded_name, name, timestr, (long long) sb.st_size ); } } static void send_error( int status, char* title, char* extra_header, char* text ) { send_headers( status, title, extra_header, "text/html", -1, -1 ); (void) printf( "\ \n\ \n\ \n\ \n\ %d %s\n\ \n\ \n\

%d %s

\n", status, title, status, title ); (void) printf( "%s\n", text ); (void) printf( "\
\n\
%s
\n\ \n\ \n", SERVER_URL, SERVER_NAME ); (void) fflush( stdout ); exit( 1 ); } static void send_headers( int status, char* title, char* extra_header, char* mime_type, off_t length, time_t mod ) { time_t now; char timebuf[100]; (void) printf( "%s %d %s\015\012", PROTOCOL, status, title ); (void) printf( "Server: %s\015\012", SERVER_NAME ); now = time( (time_t*) 0 ); (void) strftime( timebuf, sizeof(timebuf), RFC1123FMT, gmtime( &now ) ); (void) printf( "Date: %s\015\012", timebuf ); if ( extra_header != (char*) 0 ) (void) printf( "%s\015\012", extra_header ); if ( mime_type != (char*) 0 ) (void) printf( "Content-Type: %s\015\012", mime_type ); if ( length >= 0 ) (void) printf( "Content-Length: %lld\015\012", (long long) length ); if ( mod != (time_t) -1 ) { (void) strftime( timebuf, sizeof(timebuf), RFC1123FMT, gmtime( &mod ) ); (void) printf( "Last-Modified: %s\015\012", timebuf ); } (void) printf( "Connection: close\015\012" ); (void) printf( "\015\012" ); } static char* get_mime_type( char* name ) { char* dot; dot = strrchr( name, '.' ); if ( dot == (char*) 0 ) return "text/plain; charset=UTF-8"; if ( strcmp( dot, ".html" ) == 0 || strcmp( dot, ".htm" ) == 0 ) return "text/html; charset=UTF-8"; if ( strcmp( dot, ".xhtml" ) == 0 || strcmp( dot, ".xht" ) == 0 ) return "application/xhtml+xml; charset=UTF-8"; if ( strcmp( dot, ".jpg" ) == 0 || strcmp( dot, ".jpeg" ) == 0 ) return "image/jpeg"; if ( strcmp( dot, ".gif" ) == 0 ) return "image/gif"; if ( strcmp( dot, ".png" ) == 0 ) return "image/png"; if ( strcmp( dot, ".css" ) == 0 ) return "text/css"; if ( strcmp( dot, ".xml" ) == 0 || strcmp( dot, ".xsl" ) == 0 ) return "text/xml; charset=UTF-8"; if ( strcmp( dot, ".au" ) == 0 ) return "audio/basic"; if ( strcmp( dot, ".wav" ) == 0 ) return "audio/wav"; if ( strcmp( dot, ".avi" ) == 0 ) return "video/x-msvideo"; if ( strcmp( dot, ".mov" ) == 0 || strcmp( dot, ".qt" ) == 0 ) return "video/quicktime"; if ( strcmp( dot, ".mpeg" ) == 0 || strcmp( dot, ".mpe" ) == 0 ) return "video/mpeg"; if ( strcmp( dot, ".vrml" ) == 0 || strcmp( dot, ".wrl" ) == 0 ) return "model/vrml"; if ( strcmp( dot, ".midi" ) == 0 || strcmp( dot, ".mid" ) == 0 ) return "audio/midi"; if ( strcmp( dot, ".mp3" ) == 0 ) return "audio/mpeg"; if ( strcmp( dot, ".ogg" ) == 0 ) return "application/ogg"; if ( strcmp( dot, ".pac" ) == 0 ) return "application/x-ns-proxy-autoconfig"; return "text/plain; charset=UTF-8"; } static void strdecode( char* to, char* from ) { for ( ; *from != '\0'; ++to, ++from ) { if ( from[0] == '%' && isxdigit( from[1] ) && isxdigit( from[2] ) ) { *to = hexit( from[1] ) * 16 + hexit( from[2] ); from += 2; } else *to = *from; } *to = '\0'; } static int hexit( char c ) { if ( c >= '0' && c <= '9' ) return c - '0'; if ( c >= 'a' && c <= 'f' ) return c - 'a' + 10; if ( c >= 'A' && c <= 'F' ) return c - 'A' + 10; return 0; /* shouldn't happen, we're guarded by isxdigit() */ } static void strencode( char* to, size_t tosize, const char* from ) { int tolen; for ( tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from ) { if ( isalnum(*from) || strchr( "/_.-~", *from ) != (char*) 0 ) { *to = *from; ++to; ++tolen; } else { (void) sprintf( to, "%%%02x", (int) *from & 0xff ); to += 3; tolen += 3; } } *to = '\0'; }