📄 libhttpd.c
字号:
/* libhttpd.c - HTTP protocol library**** Copyright (C)1995,1998 by Jef Poskanzer <jef@acme.com>. 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 "config.h"#include "version.h"#include <sys/types.h>#include <sys/param.h>#include <sys/wait.h>#include <sys/stat.h>#include <sys/time.h>#include <sys/resource.h>#include <ctype.h>#include <errno.h>#include <fcntl.h>#ifdef HAVE_MEMORY_H#include <memory.h>#endif#include <pwd.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <syslog.h>#include <unistd.h>#ifdef HAVE_DIRENT_H# include <dirent.h># define NAMLEN(dirent) strlen((dirent)->d_name)#else# define dirent direct# define NAMLEN(dirent) (dirent)->d_namlen# ifdef HAVE_SYS_NDIR_H# include <sys/ndir.h># endif# ifdef HAVE_SYS_DIR_H# include <sys/dir.h># endif# ifdef HAVE_NDIR_H# include <ndir.h># endif#endifextern char* crypt( const char* key, const char* setting );#include "libhttpd.h"#include "mmc.h"#include "timers.h"#include "match.h"#include "tdate_parse.h"#ifndef STDIN_FILENO#define STDIN_FILENO 0#endif#ifndef STDOUT_FILENO#define STDOUT_FILENO 1#endif#ifndef STDERR_FILENO#define STDERR_FILENO 2#endif#ifndef max#define max(a,b) ((a) > (b) ? (a) : (b))#endif#ifndef min#define min(a,b) ((a) < (b) ? (a) : (b))#endifstatic const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";static const char* cernfmt = "%d/%b/%Y:%H:%M:%S %Z";/* Forwards. */static void child_reaper( ClientData client_data, struct timeval* nowP );static int do_reap( void );static void check_options( void );static void free_httpd_server( httpd_server* hs );static void add_response( httpd_conn* hc, char* str );static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod );static void realloc_str( char** strP, int* maxsizeP, int size );static void send_response( httpd_conn* hc, int status, char* title, char* extrahead, char* form, char* arg );#ifdef AUTH_FILEstatic void send_authenticate( httpd_conn* hc, char* realm );static int b64_decode( const char* str, unsigned char* space, int size );static int auth_check( httpd_conn* hc, char* dirname );#endif /* AUTH_FILE */static void send_dirredirect( httpd_conn* hc );static int is_hexit( char c );static int hexit( char c );static void strdecode( char* to, char* from );static int tilde_map( httpd_conn* hc );static char* expand_symlinks( char* path, char** restP, int chrooted );static char* bufgets( httpd_conn* hc );static void figure_mime( httpd_conn* hc );static void cgi_kill2( ClientData client_data, struct timeval* nowP );static void cgi_kill( ClientData client_data, struct timeval* nowP );#ifdef GENERATE_INDEXESstatic off_t ls( httpd_conn* hc );#endif /* GENERATE_INDEXES */static char* build_env( char* fmt, char* arg );#ifdef SERVER_NAME_LISTstatic char* hostname_map( char* hostname );#endif /* SERVER_NAME_LIST */static char** make_envp( httpd_conn* hc );static char** make_argp( httpd_conn* hc );static void cgi_interpose( httpd_conn* hc, int wfd );static void cgi_child( httpd_conn* hc );static off_t cgi( httpd_conn* hc );static int really_start_request( httpd_conn* hc );static int reap_time;static voidchild_reaper( ClientData client_data, struct timeval* nowP ) { int child_count; static int prev_child_count = 0; child_count = do_reap(); /* Reschedule reaping, with adaptively changed time. */ if ( child_count > prev_child_count * 3 / 2 ) reap_time = max( reap_time / 2, MIN_REAP_TIME ); else if ( child_count < prev_child_count * 2 / 3 ) reap_time = min( reap_time * 5 / 4, MAX_REAP_TIME ); (void) tmr_create( nowP, child_reaper, (ClientData) 0, reap_time * 1000L, 0 ); }static intdo_reap( void ) { int child_count; pid_t pid; int status; /* Reap defunct children until there aren't any more. */ for ( child_count = 0; ; ++child_count ) {#ifdef HAVE_WAITPID pid = waitpid( (pid_t) -1, &status, WNOHANG );#else /* HAVE_WAITPID */ pid = wait3( &status, WNOHANG, (struct rusage*) 0 );#endif /* HAVE_WAITPID */ if ( (int) pid == 0 ) /* none left */ break; if ( (int) pid < 0 ) { if ( errno == EINTR ) /* because of ptrace */ continue; /* ECHILD shouldn't happen with the WNOHANG option, but with ** some kernels it does anyway. Ignore it. */ if ( errno != ECHILD ) syslog( LOG_ERR, "waitpid - %m" ); break; } } return child_count; }static voidcheck_options( void ) {#if defined(TILDE_MAP_1) && defined(TILDE_MAP_2) syslog( LOG_CRIT, "both TILDE_MAP_1 and TILDE_MAP_2 are defined" ); exit( 1 );#endif /* both */ }static voidfree_httpd_server( httpd_server* hs ) { if ( hs->cwd != (char*) 0 ) free( (void*) hs->cwd ); if ( hs->cgi_pattern != (char*) 0 ) free( (void*) hs->cgi_pattern ); free( (void*) hs ); }httpd_server*httpd_initialize( char* hostname, u_int addr, int port, char* cgi_pattern, char* cwd, FILE* logfp, int chrooted ) { httpd_server* hs; int on; struct sockaddr_in sa; char* cp; check_options(); /* Set up child-process reaper. */ reap_time = min( MIN_REAP_TIME * 4, MAX_REAP_TIME ); (void) tmr_create( (struct timeval*) 0, child_reaper, (ClientData) 0, reap_time * 1000L, 0 ); hs = NEW( httpd_server, 1 ); if ( hs == (httpd_server*) 0 ) { syslog( LOG_CRIT, "out of memory" ); return (httpd_server*) 0; } if ( hostname == (char*) 0 ) hs->hostname = (char*) 0; else hs->hostname = strdup( hostname ); hs->port = port; if ( cgi_pattern == (char*) 0 ) hs->cgi_pattern = (char*) 0; else { /* Nuke any leading slashes. */ if ( cgi_pattern[0] == '/' ) ++cgi_pattern; hs->cgi_pattern = strdup( cgi_pattern ); if ( hs->cgi_pattern == (char*) 0 ) { syslog( LOG_CRIT, "out of memory" ); return (httpd_server*) 0; } /* Nuke any leading slashes in the cgi pattern. */ while ( ( cp = strstr( hs->cgi_pattern, "|/" ) ) != (char*) 0 ) (void) strcpy( cp + 1, cp + 2 ); } hs->cwd = strdup( cwd ); if ( hs->cwd == (char*) 0 ) { syslog( LOG_CRIT, "out of memory" ); return (httpd_server*) 0; } hs->logfp = logfp; hs->chrooted = chrooted; /* Create socket. */ hs->listen_fd = socket( AF_INET, SOCK_STREAM, 0 ); if ( hs->listen_fd < 0 ) { syslog( LOG_CRIT, "socket - %m" ); free_httpd_server( hs ); return (httpd_server*) 0; } (void) fcntl( hs->listen_fd, F_SETFD, 1 ); /* Allow reuse of local addresses. */ on = 1; if ( setsockopt( hs->listen_fd, SOL_SOCKET, SO_REUSEADDR, (char*) &on, sizeof(on) ) < 0 ) syslog( LOG_CRIT, "setsockopt SO_REUSEADDR - %m" ); /* Bind to it. */ memset( (char*) &sa, 0, sizeof(sa) ); sa.sin_family = AF_INET; sa.sin_addr.s_addr = addr; sa.sin_port = htons( hs->port ); hs->host_addr = sa.sin_addr; if ( bind( hs->listen_fd, (struct sockaddr*) &sa, sizeof(sa) ) < 0 ) { syslog( LOG_CRIT, "bind %.80s - %m", inet_ntoa( sa.sin_addr ) ); (void) close( hs->listen_fd ); free_httpd_server( hs ); return (httpd_server*) 0; } /* Set the listen file descriptor to no-delay mode. */ if ( fcntl( hs->listen_fd, F_SETFL, O_NDELAY ) < 0 ) { syslog( LOG_CRIT, "fcntl O_NDELAY - %m" ); (void) close( hs->listen_fd ); free_httpd_server( hs ); return (httpd_server*) 0; } /* Start a listen going. */ if ( listen( hs->listen_fd, LISTEN_BACKLOG ) < 0 ) { syslog( LOG_CRIT, "listen - %m" ); (void) close( hs->listen_fd ); free_httpd_server( hs ); return (httpd_server*) 0; } /* Done initializing. */ if ( hs->hostname == (char*) 0 ) syslog( LOG_INFO, "%s starting on port %d", SERVER_SOFTWARE, hs->port ); else syslog( LOG_INFO, "%s starting on %.80s, port %d", SERVER_SOFTWARE, inet_ntoa( hs->host_addr ), hs->port ); return hs; }voidhttpd_terminate( httpd_server* hs ) { (void) close( hs->listen_fd ); if ( hs->logfp != (FILE*) 0 ) (void) fclose( hs->logfp ); free_httpd_server( hs ); }static char* ok200title = "OK";static char* ok206title = "Partial Content";static char* err302title = "Found";static char* err302form = "The actual URL is '%.80s'.\n";static char* err304title = "Not Modified";char* httpd_err400title = "Bad Request";char* httpd_err400form = "Your request has bad syntax or is inherently impossible to satisfy.\n";#ifdef AUTH_FILEstatic char* err401title = "Unauthorized";static char* err401form = "Authorization required for the URL '%.80s'.\n";#endif /* AUTH_FILE */static char* err403title = "Forbidden";static char* err403form = "You do not have permission to get URL '%.80s' from this server.\n";static char* err404title = "Not Found";static char* err404form = "The requested URL '%.80s' was not found on this server.\n";char* httpd_err408title = "Request Timeout";char* httpd_err408form = "No request appeared within a reasonable time period.\n";static char* err500title = "Internal Error";static char* err500form = "There was an unusual problem serving the requested URL '%.80s'.\n";static char* err501title = "Not Implemented";static char* err501form = "The requested method '%.80s' is not implemented by this server.\n";char* httpd_err503title = "Service Temporarily Overloaded";char* httpd_err503form = "The requested URL '%.80s' is temporarily overloaded. Please try again later.\n";/* Append a string to the buffer waiting to be sent as response. */static voidadd_response( httpd_conn* hc, char* str ) { int len; len = strlen( str ); realloc_str( &hc->response, &hc->maxresponse, hc->responselen + len ); (void) memcpy( &(hc->response[hc->responselen]), str, len ); hc->responselen += len; }/* Send the buffered response. */voidhttpd_write_response( httpd_conn* hc ) { if ( hc->responselen > 0 ) { (void) write( hc->conn_fd, hc->response, hc->responselen ); hc->responselen = 0; } }static voidsend_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod ) { time_t now; char nowbuf[100]; char modbuf[100]; char buf[1000]; int partial_content; hc->status = status; hc->bytes = length; if ( hc->mime_flag ) { if ( status == 200 && hc->got_range && ( hc->end_byte_loc >= hc->init_byte_loc ) && ( ( hc->end_byte_loc != length - 1 ) || ( hc->init_byte_loc != 0 ) ) && ( hc->range_if == (time_t) -1 || hc->range_if == hc->sb.st_mtime ) ) { partial_content = 1; hc->status = status = 206; title = ok206title; } else partial_content = 0; now = time( (time_t*) 0 ); if ( mod == (time_t) 0 ) mod = now;#ifdef EMBED modbuf[0] = nowbuf[0] = 0;#else (void) strftime( nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime( &now ) ); (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) );#endif (void) sprintf( buf, "%.20s %d %s\r\nServer: %s\r\nContent-type: %s\r\nDate: %s\r\nLast-modified: %s\r\nAccept-Ranges: bytes\r\nConnection: close\r\n", hc->protocol, status, title, SERVER_SOFTWARE, type, nowbuf, modbuf ); add_response( hc, buf ); if ( encodings[0] != '\0' ) { (void) sprintf( buf, "Content-encoding: %s\r\n", encodings ); add_response( hc, buf ); } if ( partial_content ) { (void) sprintf( buf, "Content-range: bytes %ld-%ld/%d\r\nContent-length: %ld\r\n", (long) hc->init_byte_loc, (long) hc->end_byte_loc, length, (long) ( hc->end_byte_loc - hc->init_byte_loc + 1 ) ); add_response( hc, buf ); } else if ( length >= 0 ) { (void) sprintf( buf, "Content-length: %d\r\n", length ); add_response( hc, buf ); } if ( extraheads[0] != '\0' ) add_response( hc, extraheads ); add_response( hc, "\r\n" ); } }static voidrealloc_str( char** strP, int* maxsizeP, int size ) { if ( *maxsizeP == 0 ) { *maxsizeP = MAX( 200, size ); /* arbitrary */ *strP = NEW( char, *maxsizeP + 1 ); } else if ( size > *maxsizeP ) { *maxsizeP = MAX( *maxsizeP * 2, size * 5 / 4 ); *strP = RENEW( *strP, char, *maxsizeP + 1 ); } else return; if ( *strP == (char*) 0 ) { syslog( LOG_ERR, "out of memory" ); exit( 1 ); } }static voidsend_response( httpd_conn* hc, int status, char* title, char* extrahead, char* form, char* arg )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -