ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/thttpd/libhttpd.c
Revision: 1.4
Committed: Sun Jul 8 08:21:57 2001 UTC (25 years, 2 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +10 -4 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 /* libhttpd.c - HTTP protocol library
2     **
3     ** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer <jef@acme.com>.
4     ** All rights reserved.
5     **
6     ** Redistribution and use in source and binary forms, with or without
7     ** modification, are permitted provided that the following conditions
8     ** are met:
9     ** 1. Redistributions of source code must retain the above copyright
10     ** notice, this list of conditions and the following disclaimer.
11     ** 2. Redistributions in binary form must reproduce the above copyright
12     ** notice, this list of conditions and the following disclaimer in the
13     ** documentation and/or other materials provided with the distribution.
14     **
15     ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16     ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17     ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18     ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19     ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20     ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21     ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22     ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23     ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24     ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25     ** SUCH DAMAGE.
26     */
27    
28    
29     #include "config.h"
30     #include "version.h"
31    
32     #ifdef SHOW_SERVER_VERSION
33     #define EXPOSED_SERVER_SOFTWARE SERVER_SOFTWARE
34     #else /* SHOW_SERVER_VERSION */
35     #define EXPOSED_SERVER_SOFTWARE "thttpd"
36     #endif /* SHOW_SERVER_VERSION */
37    
38     #include <sys/types.h>
39     #include <sys/param.h>
40     #include <sys/wait.h>
41     #include <sys/stat.h>
42    
43     #include <ctype.h>
44     #include <errno.h>
45     #include <fcntl.h>
46     #include <time.h>
47     #ifdef HAVE_MEMORY_H
48     #include <memory.h>
49     #endif /* HAVE_MEMORY_H */
50     #include <pwd.h>
51     #include <signal.h>
52     #include <stdio.h>
53     #include <stdlib.h>
54     #include <string.h>
55     #include <syslog.h>
56     #include <unistd.h>
57     #include <stdarg.h>
58    
59     #ifdef HAVE_OSRELDATE_H
60     #include <osreldate.h>
61     #endif /* HAVE_OSRELDATE_H */
62    
63     #ifdef HAVE_DIRENT_H
64     # include <dirent.h>
65     # define NAMLEN(dirent) strlen((dirent)->d_name)
66     #else
67     # define dirent direct
68     # define NAMLEN(dirent) (dirent)->d_namlen
69     # ifdef HAVE_SYS_NDIR_H
70     # include <sys/ndir.h>
71     # endif
72     # ifdef HAVE_SYS_DIR_H
73     # include <sys/dir.h>
74     # endif
75     # ifdef HAVE_NDIR_H
76     # include <ndir.h>
77     # endif
78     #endif
79    
80     extern char* crypt( const char* key, const char* setting );
81    
82     #include "libhttpd.h"
83     #include "mmc.h"
84     #include "timers.h"
85     #include "match.h"
86     #include "tdate_parse.h"
87    
88     #ifndef STDIN_FILENO
89     #define STDIN_FILENO 0
90     #endif
91     #ifndef STDOUT_FILENO
92     #define STDOUT_FILENO 1
93     #endif
94     #ifndef STDERR_FILENO
95     #define STDERR_FILENO 2
96     #endif
97    
98     #ifndef max
99     #define max(a,b) ((a) > (b) ? (a) : (b))
100     #endif
101     #ifndef min
102     #define min(a,b) ((a) < (b) ? (a) : (b))
103     #endif
104    
105    
106     /* Forwards. */
107     static void child_reaper( ClientData client_data, struct timeval* nowP );
108     static int do_reap( void );
109     static void check_options( void );
110     static void free_httpd_server( httpd_server* hs );
111     static int initialize_listen_socket( httpd_sockaddr* saP );
112     static void unlisten( httpd_server* hs );
113     static void add_response( httpd_conn* hc, char* str );
114     static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod );
115     static void send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg );
116     static void send_response_tail( httpd_conn* hc );
117     static void defang( char* str, char* dfstr, int dfsize );
118     #ifdef ERR_DIR
119     static int send_err_file( httpd_conn* hc, int status, char* title, char* extraheads, char* filename );
120     #endif /* ERR_DIR */
121     #ifdef AUTH_FILE
122     static void send_authenticate( httpd_conn* hc, char* realm );
123     static int b64_decode( const char* str, unsigned char* space, int size );
124     static int auth_check( httpd_conn* hc, char* dirname );
125     static int auth_check2( httpd_conn* hc, char* dirname );
126     #endif /* AUTH_FILE */
127     static void send_dirredirect( httpd_conn* hc );
128     static int hexit( char c );
129     static void strdecode( char* to, char* from );
130     #ifdef GENERATE_INDEXES
131     static void strencode( char* to, int tosize, char* from );
132     #endif /* GENERATE_INDEXES */
133     #ifdef TILDE_MAP_1
134     static int tilde_map_1( httpd_conn* hc );
135     #endif /* TILDE_MAP_1 */
136     #ifdef TILDE_MAP_2
137     static int tilde_map_2( httpd_conn* hc );
138     #endif /* TILDE_MAP_2 */
139     static int vhost_map( httpd_conn* hc );
140     static char* expand_symlinks( char* path, char** restP, int no_symlink, int tildemapped );
141     static char* bufgets( httpd_conn* hc );
142     static void de_dotdot( char* file );
143     static void figure_mime( httpd_conn* hc );
144     #ifdef CGI_TIMELIMIT
145     static void cgi_kill2( ClientData client_data, struct timeval* nowP );
146     static void cgi_kill( ClientData client_data, struct timeval* nowP );
147     #endif /* CGI_TIMELIMIT */
148     #ifdef GENERATE_INDEXES
149     static off_t ls( httpd_conn* hc );
150     #endif /* GENERATE_INDEXES */
151     static char* build_env( char* fmt, char* arg );
152     #ifdef SERVER_NAME_LIST
153     static char* hostname_map( char* hostname );
154     #endif /* SERVER_NAME_LIST */
155     static char** make_envp( httpd_conn* hc );
156     static char** make_argp( httpd_conn* hc );
157     static void cgi_interpose_input( httpd_conn* hc, int wfd );
158     static void post_post_garbage_hack( httpd_conn* hc );
159     static void cgi_interpose_output( httpd_conn* hc, int rfd );
160 root 1.2 static void cgi_child( httpd_conn* hc, char* exefilename );
161     static off_t cgi( httpd_conn* hc, char* exefilename );
162 root 1.1 static int really_start_request( httpd_conn* hc, struct timeval* nowP );
163     static void make_log_entry( httpd_conn* hc, struct timeval* nowP );
164     static int check_referer( httpd_conn* hc );
165     static int really_check_referer( httpd_conn* hc );
166     static int sockaddr_check( httpd_sockaddr* saP );
167     static size_t sockaddr_len( httpd_sockaddr* saP );
168     static int my_snprintf( char* str, size_t size, const char* format, ... );
169    
170    
171     static int reap_time;
172    
173     static void
174     child_reaper( ClientData client_data, struct timeval* nowP )
175     {
176     int child_count;
177     static int prev_child_count = 0;
178    
179     child_count = do_reap();
180    
181     /* Reschedule reaping, with adaptively changed time. */
182     if ( child_count > prev_child_count * 3 / 2 )
183     reap_time = max( reap_time / 2, MIN_REAP_TIME );
184     else if ( child_count < prev_child_count * 2 / 3 )
185     reap_time = min( reap_time * 5 / 4, MAX_REAP_TIME );
186     if ( tmr_create( nowP, child_reaper, JunkClientData, reap_time * 1000L, 0 ) == (Timer*) 0 )
187     {
188     syslog( LOG_CRIT, "tmr_create(child_reaper) failed" );
189     exit( 1 );
190     }
191     }
192    
193     static int
194     do_reap( void )
195     {
196     int child_count;
197     pid_t pid;
198     int status;
199    
200     /* Reap defunct children until there aren't any more. */
201     for ( child_count = 0; ; ++child_count )
202     {
203     #ifdef HAVE_WAITPID
204     pid = waitpid( (pid_t) -1, &status, WNOHANG );
205     #else /* HAVE_WAITPID */
206     pid = wait3( &status, WNOHANG, (struct rusage*) 0 );
207     #endif /* HAVE_WAITPID */
208     if ( (int) pid == 0 ) /* none left */
209     break;
210     if ( (int) pid < 0 )
211     {
212     if ( errno == EINTR ) /* because of ptrace */
213     continue;
214     /* ECHILD shouldn't happen with the WNOHANG option, but with
215     ** some kernels it does anyway. Ignore it.
216     */
217     if ( errno != ECHILD )
218     syslog( LOG_ERR, "waitpid - %m" );
219     break;
220     }
221     }
222     return child_count;
223     }
224    
225    
226     static void
227     check_options( void )
228     {
229     #if defined(TILDE_MAP_1) && defined(TILDE_MAP_2)
230     syslog( LOG_CRIT, "both TILDE_MAP_1 and TILDE_MAP_2 are defined" );
231     exit( 1 );
232     #endif /* both */
233     }
234    
235    
236     static void
237     free_httpd_server( httpd_server* hs )
238     {
239     if ( hs->binding_hostname != (char*) 0 )
240     free( (void*) hs->binding_hostname );
241     if ( hs->cwd != (char*) 0 )
242     free( (void*) hs->cwd );
243 root 1.2 if ( hs->autoindex_prog != (char*) 0 )
244     free( (void*) hs->autoindex_prog );
245 root 1.1 if ( hs->cgi_pattern != (char*) 0 )
246     free( (void*) hs->cgi_pattern );
247     if ( hs->charset != (char*) 0 )
248     free( (void*) hs->charset );
249     if ( hs->url_pattern != (char*) 0 )
250     free( (void*) hs->url_pattern );
251     if ( hs->local_pattern != (char*) 0 )
252     free( (void*) hs->local_pattern );
253     free( (void*) hs );
254     }
255    
256    
257     httpd_server*
258     httpd_initialize(
259     char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P, int port,
260     char* cgi_pattern, char* charset, char* cwd, int no_log, FILE* logfp,
261     int no_symlink, int vhost, int global_passwd, char* url_pattern,
262 root 1.2 char* local_pattern, int no_empty_referers,
263     char* autoindex_prog)
264 root 1.1 {
265     httpd_server* hs;
266     static char ghnbuf[256];
267     char* cp;
268    
269     check_options();
270    
271     /* Set up child-process reaper. */
272     reap_time = min( MIN_REAP_TIME * 4, MAX_REAP_TIME );
273     if ( tmr_create( (struct timeval*) 0, child_reaper, JunkClientData, reap_time * 1000L, 0 ) == (Timer*) 0 )
274     {
275     syslog( LOG_CRIT, "tmr_create(child_reaper) failed" );
276     return (httpd_server*) 0;
277     }
278    
279     hs = NEW( httpd_server, 1 );
280     if ( hs == (httpd_server*) 0 )
281     {
282     syslog( LOG_CRIT, "out of memory allocating an httpd_server" );
283     return (httpd_server*) 0;
284     }
285    
286     if ( hostname != (char*) 0 )
287     {
288     hs->binding_hostname = strdup( hostname );
289     if ( hs->binding_hostname == (char*) 0 )
290     {
291     syslog( LOG_CRIT, "out of memory copying hostname" );
292     return (httpd_server*) 0;
293     }
294     hs->server_hostname = hs->binding_hostname;
295     }
296     else
297     {
298     hs->binding_hostname = (char*) 0;
299     hs->server_hostname = (char*) 0;
300     if ( gethostname( ghnbuf, sizeof(ghnbuf) ) < 0 )
301     ghnbuf[0] = '\0';
302     #ifdef SERVER_NAME_LIST
303     if ( ghnbuf[0] != '\0' )
304     hs->server_hostname = hostname_map( ghnbuf );
305     #endif /* SERVER_NAME_LIST */
306     if ( hs->server_hostname == (char*) 0 )
307     {
308     #ifdef SERVER_NAME
309     hs->server_hostname = SERVER_NAME;
310     #else /* SERVER_NAME */
311     if ( ghnbuf[0] != '\0' )
312     hs->server_hostname = ghnbuf;
313     #endif /* SERVER_NAME */
314     }
315     }
316    
317     hs->port = port;
318     if ( cgi_pattern == (char*) 0 )
319     hs->cgi_pattern = (char*) 0;
320     else
321     {
322     /* Nuke any leading slashes. */
323     if ( cgi_pattern[0] == '/' )
324     ++cgi_pattern;
325     hs->cgi_pattern = strdup( cgi_pattern );
326     if ( hs->cgi_pattern == (char*) 0 )
327     {
328     syslog( LOG_CRIT, "out of memory copying cgi_pattern" );
329     return (httpd_server*) 0;
330     }
331     /* Nuke any leading slashes in the cgi pattern. */
332     while ( ( cp = strstr( hs->cgi_pattern, "|/" ) ) != (char*) 0 )
333     (void) strcpy( cp + 1, cp + 2 );
334     }
335 root 1.2 hs->autoindex_prog = autoindex_prog ? strdup( autoindex_prog ) : 0;
336 root 1.1 hs->charset = strdup( charset );
337     hs->cwd = strdup( cwd );
338     if ( hs->cwd == (char*) 0 )
339     {
340     syslog( LOG_CRIT, "out of memory copying cwd" );
341     return (httpd_server*) 0;
342     }
343     if ( url_pattern == (char*) 0 )
344     hs->url_pattern = (char*) 0;
345     else
346     {
347     hs->url_pattern = strdup( url_pattern );
348     if ( hs->url_pattern == (char*) 0 )
349     {
350     syslog( LOG_CRIT, "out of memory copying url_pattern" );
351     return (httpd_server*) 0;
352     }
353     }
354     if ( local_pattern == (char*) 0 )
355     hs->local_pattern = (char*) 0;
356     else
357     {
358     hs->local_pattern = strdup( local_pattern );
359     if ( hs->local_pattern == (char*) 0 )
360     {
361     syslog( LOG_CRIT, "out of memory copying local_pattern" );
362     return (httpd_server*) 0;
363     }
364     }
365     hs->no_log = no_log;
366     hs->logfp = (FILE*) 0;
367     httpd_set_logfp( hs, logfp );
368     hs->no_symlink = no_symlink;
369     hs->vhost = vhost;
370     hs->global_passwd = global_passwd;
371     hs->no_empty_referers = no_empty_referers;
372    
373     /* Initialize listen sockets. Try v6 first because of a Linux peculiarity;
374     ** unlike other systems, it has magical v6 sockets that also listen for v4,
375     ** but if you bind a v4 socket first then the v6 bind fails.
376     */
377     if ( sa6P == (httpd_sockaddr*) 0 )
378     hs->listen6_fd = -1;
379     else
380     hs->listen6_fd = initialize_listen_socket( sa6P );
381     if ( sa4P == (httpd_sockaddr*) 0 )
382     hs->listen4_fd = -1;
383     else
384     hs->listen4_fd = initialize_listen_socket( sa4P );
385     /* If we didn't get any valid sockets, fail. */
386     if ( hs->listen4_fd == -1 && hs->listen6_fd == -1 )
387     {
388     free_httpd_server( hs );
389     return (httpd_server*) 0;
390     }
391    
392     /* Done initializing. */
393     if ( hs->binding_hostname == (char*) 0 )
394     syslog( LOG_INFO, "%.80s starting on port %d", SERVER_SOFTWARE, hs->port );
395     else
396     syslog(
397     LOG_INFO, "%.80s starting on %.80s, port %d", SERVER_SOFTWARE,
398     httpd_ntoa( hs->listen4_fd != -1 ? sa4P : sa6P ), hs->port );
399     return hs;
400     }
401    
402    
403     static int
404     initialize_listen_socket( httpd_sockaddr* saP )
405     {
406     int listen_fd;
407     int on, flags;
408    
409     /* Check sockaddr. */
410     if ( ! sockaddr_check( saP ) )
411     {
412     syslog( LOG_CRIT, "unknown sockaddr family on listen socket" );
413     return -1;
414     }
415    
416     /* Create socket. */
417     listen_fd = socket( saP->sa.sa_family, SOCK_STREAM, 0 );
418     if ( listen_fd < 0 )
419     {
420     syslog( LOG_CRIT, "socket %.80s - %m", httpd_ntoa( saP ) );
421     return -1;
422     }
423     (void) fcntl( listen_fd, F_SETFD, 1 );
424    
425     /* Allow reuse of local addresses. */
426     on = 1;
427     if ( setsockopt(
428     listen_fd, SOL_SOCKET, SO_REUSEADDR, (char*) &on,
429     sizeof(on) ) < 0 )
430     syslog( LOG_CRIT, "setsockopt SO_REUSEADDR - %m" );
431    
432     /* Use accept filtering, if available. */
433     #ifdef SO_ACCEPTFILTER
434     {
435     #if ( __FreeBSD_version >= 411000 )
436     #define ACCEPT_FILTER_NAME "httpready"
437     #else
438     #define ACCEPT_FILTER_NAME "dataready"
439     #endif
440     struct accept_filter_arg af;
441     (void) bzero( &af, sizeof(af) );
442     (void) strcpy( af.af_name, ACCEPT_FILTER_NAME );
443     (void) setsockopt(
444     listen_fd, SOL_SOCKET, SO_ACCEPTFILTER, (char*) &af, sizeof(af) );
445     }
446     #endif /* SO_ACCEPTFILTER */
447    
448     /* Bind to it. */
449     if ( bind( listen_fd, &saP->sa, sockaddr_len( saP ) ) < 0 )
450     {
451     syslog(
452     LOG_CRIT, "bind %.80s - %m", httpd_ntoa( saP ) );
453     (void) close( listen_fd );
454     return -1;
455     }
456    
457     /* Set the listen file descriptor to no-delay mode. */
458     flags = fcntl( listen_fd, F_GETFL, 0 );
459     if ( flags == -1 )
460     {
461     syslog( LOG_CRIT, "fcntl F_GETFL - %m" );
462     (void) close( listen_fd );
463     return -1;
464     }
465     if ( fcntl( listen_fd, F_SETFL, flags | O_NDELAY ) < 0 )
466     {
467     syslog( LOG_CRIT, "fcntl O_NDELAY - %m" );
468     (void) close( listen_fd );
469     return -1;
470     }
471    
472     /* Start a listen going. */
473     if ( listen( listen_fd, LISTEN_BACKLOG ) < 0 )
474     {
475     syslog( LOG_CRIT, "listen - %m" );
476     (void) close( listen_fd );
477     return -1;
478     }
479    
480     return listen_fd;
481     }
482    
483    
484     void
485     httpd_set_logfp( httpd_server* hs, FILE* logfp )
486     {
487     if ( hs->logfp != (FILE*) 0 )
488     (void) fclose( hs->logfp );
489     hs->logfp = logfp;
490     }
491    
492    
493     void
494     httpd_terminate( httpd_server* hs )
495     {
496     unlisten( hs );
497     if ( hs->logfp != (FILE*) 0 )
498     (void) fclose( hs->logfp );
499     free_httpd_server( hs );
500     }
501    
502    
503     static void
504     unlisten( httpd_server* hs )
505     {
506     if ( hs->listen4_fd != -1 )
507     (void) close( hs->listen4_fd );
508     if ( hs->listen6_fd != -1 )
509     (void) close( hs->listen6_fd );
510     }
511    
512    
513     /* Conditional macro to allow two alternate forms for use in the built-in
514     ** error pages. If EXPLICIT_ERROR_PAGES is defined, the second and more
515     ** explicit error form is used; otherwise, the first and more generic
516     ** form is used.
517     */
518     #ifdef EXPLICIT_ERROR_PAGES
519     #define ERROR_FORM(a,b) b
520     #else /* EXPLICIT_ERROR_PAGES */
521     #define ERROR_FORM(a,b) a
522     #endif /* EXPLICIT_ERROR_PAGES */
523    
524    
525     static char* ok200title = "OK";
526     static char* ok206title = "Partial Content";
527    
528     static char* err302title = "Found";
529     static char* err302form = "The actual URL is '%.80s'.\n";
530    
531     static char* err304title = "Not Modified";
532    
533     char* httpd_err400title = "Bad Request";
534     char* httpd_err400form =
535     "Your request has bad syntax or is inherently impossible to satisfy.\n";
536    
537     #ifdef AUTH_FILE
538     static char* err401title = "Unauthorized";
539     static char* err401form =
540     "Authorization required for the URL '%.80s'.\n";
541     #endif /* AUTH_FILE */
542    
543     static char* err403title = "Forbidden";
544     static char* err403form =
545     "You do not have permission to get URL '%.80s' from this server.\n";
546    
547     static char* err404title = "Not Found";
548     static char* err404form =
549     "The requested URL '%.80s' was not found on this server.\n";
550    
551     char* httpd_err408title = "Request Timeout";
552     char* httpd_err408form =
553     "No request appeared within a reasonable time period.\n";
554    
555     static char* err500title = "Internal Error";
556     static char* err500form =
557     "There was an unusual problem serving the requested URL '%.80s'.\n";
558    
559     static char* err501title = "Not Implemented";
560     static char* err501form =
561     "The requested method '%.80s' is not implemented by this server.\n";
562    
563     char* httpd_err503title = "Service Temporarily Overloaded";
564     char* httpd_err503form =
565     "The requested URL '%.80s' is temporarily overloaded. Please try again later.\n";
566    
567    
568     /* Append a string to the buffer waiting to be sent as response. */
569     static void
570     add_response( httpd_conn* hc, char* str )
571     {
572     int len;
573    
574     len = strlen( str );
575     httpd_realloc_str( &hc->response, &hc->maxresponse, hc->responselen + len );
576     (void) memcpy( &(hc->response[hc->responselen]), str, len );
577     hc->responselen += len;
578     }
579    
580     /* Send the buffered response. */
581     void
582     httpd_write_response( httpd_conn* hc )
583     {
584     /* First turn off NDELAY mode. */
585     httpd_clear_ndelay( hc->conn_fd );
586     /* And send it, if necessary. */
587     if ( hc->responselen > 0 )
588     {
589     (void) write( hc->conn_fd, hc->response, hc->responselen );
590     hc->responselen = 0;
591     }
592     }
593    
594    
595     /* Set NDELAY mode on a socket. */
596     void
597     httpd_set_ndelay( int fd )
598     {
599     int flags, newflags;
600    
601     flags = fcntl( fd, F_GETFL, 0 );
602     if ( flags != -1 )
603     {
604     newflags = flags | (int) O_NDELAY;
605     if ( newflags != flags )
606     (void) fcntl( fd, F_SETFL, newflags );
607     }
608     }
609    
610    
611     /* Clear NDELAY mode on a socket. */
612     void
613     httpd_clear_ndelay( int fd )
614     {
615     int flags, newflags;
616    
617     flags = fcntl( fd, F_GETFL, 0 );
618     if ( flags != -1 )
619     {
620     newflags = flags & ~ (int) O_NDELAY;
621     if ( newflags != flags )
622     (void) fcntl( fd, F_SETFL, newflags );
623     }
624     }
625    
626    
627     static void
628     send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod )
629     {
630     time_t now;
631     const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";
632     char nowbuf[100];
633     char modbuf[100];
634     char fixed_type[500];
635     char buf[1000];
636     int partial_content;
637    
638     hc->status = status;
639     hc->bytes_to_send = length;
640     if ( hc->mime_flag )
641     {
642     if ( status == 200 && hc->got_range &&
643     ( hc->end_byte_loc >= hc->init_byte_loc ) &&
644     ( ( hc->end_byte_loc != length - 1 ) ||
645     ( hc->init_byte_loc != 0 ) ) &&
646     ( hc->range_if == (time_t) -1 ||
647     hc->range_if == hc->sb.st_mtime ) )
648     {
649     partial_content = 1;
650     hc->status = status = 206;
651     title = ok206title;
652     }
653     else
654     partial_content = 0;
655    
656     now = time( (time_t*) 0 );
657     if ( mod == (time_t) 0 )
658     mod = now;
659     (void) strftime( nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime( &now ) );
660     (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) );
661     (void) my_snprintf(
662     fixed_type, sizeof(fixed_type), type, hc->hs->charset );
663     (void) my_snprintf( buf, sizeof(buf),
664     "%.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",
665     hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type,
666     nowbuf, modbuf );
667     add_response( hc, buf );
668     if ( encodings[0] != '\0' )
669     {
670     (void) my_snprintf( buf, sizeof(buf),
671     "Content-Encoding: %s\r\n", encodings );
672     add_response( hc, buf );
673     }
674     if ( partial_content )
675     {
676     (void) my_snprintf( buf, sizeof(buf),
677     "Content-Range: bytes %ld-%ld/%d\r\nContent-Length: %ld\r\n",
678     (long) hc->init_byte_loc, (long) hc->end_byte_loc, length,
679     (long) ( hc->end_byte_loc - hc->init_byte_loc + 1 ) );
680     add_response( hc, buf );
681     }
682     else if ( length >= 0 )
683     {
684     (void) my_snprintf( buf, sizeof(buf),
685     "Content-Length: %d\r\n", length );
686     add_response( hc, buf );
687     }
688     if ( extraheads[0] != '\0' )
689     add_response( hc, extraheads );
690     add_response( hc, "\r\n" );
691     }
692     }
693    
694    
695     static int str_alloc_count = 0;
696     static long str_alloc_size = 0;
697    
698     void
699     httpd_realloc_str( char** strP, int* maxsizeP, int size )
700     {
701     if ( *maxsizeP == 0 )
702     {
703     *maxsizeP = MAX( 200, size ); /* arbitrary */
704     *strP = NEW( char, *maxsizeP + 1 );
705     ++str_alloc_count;
706     str_alloc_size += *maxsizeP;
707     }
708     else if ( size > *maxsizeP )
709     {
710     str_alloc_size -= *maxsizeP;
711     *maxsizeP = MAX( *maxsizeP * 2, size * 5 / 4 );
712     *strP = RENEW( *strP, char, *maxsizeP + 1 );
713     str_alloc_size += *maxsizeP;
714     }
715     else
716     return;
717     if ( *strP == (char*) 0 )
718     {
719     syslog(
720     LOG_ERR, "out of memory reallocating a string to %d bytes",
721     *maxsizeP );
722     exit( 1 );
723     }
724     }
725    
726    
727     static void
728     send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg )
729     {
730     char defanged_arg[1000], buf[2000];
731    
732     send_mime( hc, status, title, "", extraheads, "text/html", -1, 0 );
733     (void) my_snprintf( buf, sizeof(buf),
734     "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n<BODY BGCOLOR=\"#cc9999\"><H2>%d %s</H2>\n",
735     status, title, status, title );
736     add_response( hc, buf );
737     defang( arg, defanged_arg, sizeof(defanged_arg) );
738     (void) my_snprintf( buf, sizeof(buf), form, defanged_arg );
739     add_response( hc, buf );
740     if ( match( "**MSIE**", hc->useragent ) )
741     {
742     int n;
743     add_response( hc, "<!--\n" );
744     for ( n = 0; n < 6; ++n )
745     add_response( hc, "Padding so that MSIE deigns to show this error instead of its own canned one.\n");
746     add_response( hc, "-->\n" );
747     }
748     send_response_tail( hc );
749     }
750    
751    
752     static void
753     send_response_tail( httpd_conn* hc )
754     {
755     char buf[1000];
756    
757     (void) my_snprintf( buf, sizeof(buf),
758     "<HR>\n<ADDRESS><A HREF=\"%s\">%s</A></ADDRESS>\n</BODY></HTML>\n",
759     SERVER_ADDRESS, EXPOSED_SERVER_SOFTWARE );
760     add_response( hc, buf );
761     }
762    
763    
764     static void
765     defang( char* str, char* dfstr, int dfsize )
766     {
767     char* cp1;
768     char* cp2;
769    
770     for ( cp1 = str, cp2 = dfstr;
771     *cp1 != '\0' && cp2 - dfstr < dfsize - 1;
772     ++cp1, ++cp2 )
773     {
774     switch ( *cp1 )
775     {
776     case '<':
777     *cp2++ = '&';
778     *cp2++ = 'l';
779     *cp2++ = 't';
780     *cp2 = ';';
781     break;
782     case '>':
783     *cp2++ = '&';
784     *cp2++ = 'g';
785     *cp2++ = 't';
786     *cp2 = ';';
787     break;
788     default:
789     *cp2 = *cp1;
790     break;
791     }
792     }
793     *cp2 = '\0';
794     }
795    
796    
797     void
798     httpd_send_err( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg )
799     {
800     #ifdef ERR_DIR
801    
802     char filename[1000];
803    
804     /* Try virtual host error page. */
805     if ( hc->hs->vhost && hc->hostdir[0] != '\0' )
806     {
807     (void) my_snprintf( filename, sizeof(filename),
808     "%s/%s/err%d.html", hc->hostdir, ERR_DIR, status );
809     if ( send_err_file( hc, status, title, extraheads, filename ) )
810     return;
811     }
812    
813     /* Try server-wide error page. */
814     (void) my_snprintf( filename, sizeof(filename),
815     "%s/err%d.html", ERR_DIR, status );
816     if ( send_err_file( hc, status, title, extraheads, filename ) )
817     return;
818    
819     /* Fall back on built-in error page. */
820     send_response( hc, status, title, extraheads, form, arg );
821    
822     #else /* ERR_DIR */
823    
824     send_response( hc, status, title, extraheads, form, arg );
825    
826     #endif /* ERR_DIR */
827     }
828    
829 root 1.2 void
830     httpd_send_err_blocked( httpd_conn* hc )
831     {
832 root 1.4 char *protocol = hc->protocol;
833    
834 root 1.2 #ifdef ERR_DIR
835 root 1.4 char filename[1000];
836     #endif
837 root 1.2
838 root 1.4 hc->protocol = "HTTP/1.0";
839 root 1.2
840 root 1.4 #ifdef ERR_DIR
841 root 1.2 /* Try virtual host error page. */
842     if ( hc->hs->vhost && hc->hostdir[0] != '\0' )
843     {
844     (void) my_snprintf( filename, sizeof(filename),
845     "%s/%s/err403blocked.html", hc->hostdir, ERR_DIR );
846     if ( send_err_file( hc, 403, err403title, "", filename ) )
847     return;
848     }
849    
850     /* Try server-wide error page. */
851     (void) my_snprintf( filename, sizeof(filename),
852 root 1.3 "%s/err403blocked.html", ERR_DIR );
853 root 1.2 if ( send_err_file( hc, 403, err403title, "", filename ) )
854     return;
855    
856     /* Fall back on built-in error page. */
857     send_response( hc, 403, err403title, "", err403form, "" );
858    
859     #else /* ERR_DIR */
860    
861     send_response( hc, 403, err403title, "", err403form, "" );
862    
863     #endif /* ERR_DIR */
864 root 1.4 hc->protocol = protocol;
865 root 1.2 }
866 root 1.1
867     #ifdef ERR_DIR
868     static int
869     send_err_file( httpd_conn* hc, int status, char* title, char* extraheads, char* filename )
870     {
871     FILE* fp;
872     char buf[1000];
873     int r;
874    
875     fp = fopen( filename, "r" );
876     if ( fp == (FILE*) 0 )
877     return 0;
878     send_mime( hc, status, title, "", extraheads, "text/html", -1, 0 );
879     for (;;)
880     {
881     r = fread( buf, 1, sizeof(buf) - 1, fp );
882     if ( r <= 0 )
883     break;
884     buf[r] = '\0';
885     add_response( hc, buf );
886     }
887     (void) fclose( fp );
888    
889     #ifdef ERR_APPEND_SERVER_INFO
890     send_response_tail( hc );
891     #endif /* ERR_APPEND_SERVER_INFO */
892    
893     return 1;
894     }
895     #endif /* ERR_DIR */
896    
897    
898     #ifdef AUTH_FILE
899    
900     static void
901     send_authenticate( httpd_conn* hc, char* realm )
902     {
903     static char* header;
904     static int maxheader = 0;
905     static char headstr[] = "WWW-Authenticate: Basic realm=\"";
906    
907     httpd_realloc_str(
908     &header, &maxheader, sizeof(headstr) + strlen( realm ) + 3 );
909     (void) my_snprintf( header, maxheader, "%s%s\"\r\n", headstr, realm );
910     httpd_send_err( hc, 401, err401title, header, err401form, hc->encodedurl );
911     /* If the request was a POST then there might still be data to be read,
912     ** so we need to do a lingering close.
913     */
914     if ( hc->method == METHOD_POST )
915     hc->should_linger = 1;
916     }
917    
918    
919     /* Base-64 decoding. This represents binary data as printable ASCII
920     ** characters. Three 8-bit binary bytes are turned into four 6-bit
921     ** values, like so:
922     **
923     ** [11111111] [22222222] [33333333]
924     **
925     ** [111111] [112222] [222233] [333333]
926     **
927     ** Then the 6-bit values are represented using the characters "A-Za-z0-9+/".
928     */
929    
930     static int b64_decode_table[256] = {
931     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
932     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
933     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
934     52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
935     -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
936     15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
937     -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
938     41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
939     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
940     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
941     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
942     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
943     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
944     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
945     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
946     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
947     };
948    
949     /* Do base-64 decoding on a string. Ignore any non-base64 bytes.
950     ** Return the actual number of bytes generated. The decoded size will
951     ** be at most 3/4 the size of the encoded, and may be smaller if there
952     ** are padding characters (blanks, newlines).
953     */
954     static int
955     b64_decode( const char* str, unsigned char* space, int size )
956     {
957     const char* cp;
958     int space_idx, phase;
959     int d, prev_d = 0;
960     unsigned char c;
961    
962     space_idx = 0;
963     phase = 0;
964     for ( cp = str; *cp != '\0'; ++cp )
965     {
966     d = b64_decode_table[(int) *cp];
967     if ( d != -1 )
968     {
969     switch ( phase )
970     {
971     case 0:
972     ++phase;
973     break;
974     case 1:
975     c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) );
976     if ( space_idx < size )
977     space[space_idx++] = c;
978     ++phase;
979     break;
980     case 2:
981     c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) );
982     if ( space_idx < size )
983     space[space_idx++] = c;
984     ++phase;
985     break;
986     case 3:
987     c = ( ( ( prev_d & 0x03 ) << 6 ) | d );
988     if ( space_idx < size )
989     space[space_idx++] = c;
990     phase = 0;
991     break;
992     }
993     prev_d = d;
994     }
995     }
996     return space_idx;
997     }
998    
999    
1000     /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */
1001     static int
1002     auth_check( httpd_conn* hc, char* dirname )
1003     {
1004     if ( hc->hs->global_passwd )
1005     {
1006     char* topdir;
1007     if ( hc->hs->vhost && hc->hostdir[0] != '\0' )
1008     topdir = hc->hostdir;
1009     else
1010     topdir = ".";
1011     switch ( auth_check2( hc, topdir ) )
1012     {
1013     case -1:
1014     return -1;
1015     case 1:
1016     return 1;
1017     }
1018     }
1019     return auth_check2( hc, dirname );
1020     }
1021    
1022    
1023     /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */
1024     static int
1025     auth_check2( httpd_conn* hc, char* dirname )
1026     {
1027     static char* authpath;
1028     static int maxauthpath = 0;
1029     struct stat sb;
1030     char authinfo[500];
1031     char* authpass;
1032     int l;
1033     FILE* fp;
1034     char line[500];
1035     char* cryp;
1036     static char* prevauthpath;
1037     static int maxprevauthpath = 0;
1038     static time_t prevmtime;
1039     static char* prevuser;
1040     static int maxprevuser = 0;
1041     static char* prevcryp;
1042     static int maxprevcryp = 0;
1043    
1044     /* Construct auth filename. */
1045     httpd_realloc_str(
1046     &authpath, &maxauthpath, strlen( dirname ) + 1 + sizeof(AUTH_FILE) );
1047     (void) my_snprintf( authpath, maxauthpath,
1048     "%s/%s", dirname, AUTH_FILE );
1049    
1050     /* Does this directory have an auth file? */
1051     if ( stat( authpath, &sb ) < 0 )
1052     /* Nope, let the request go through. */
1053     return 0;
1054    
1055     /* Does this request contain basic authorization info? */
1056     if ( hc->authorization[0] == '\0' ||
1057     strncmp( hc->authorization, "Basic ", 6 ) != 0 )
1058     {
1059     /* Nope, return a 401 Unauthorized. */
1060     send_authenticate( hc, dirname );
1061     return -1;
1062     }
1063    
1064     /* Decode it. */
1065     l = b64_decode( &(hc->authorization[6]), authinfo, sizeof(authinfo) - 1 );
1066     authinfo[l] = '\0';
1067     /* Split into user and password. */
1068     authpass = strchr( authinfo, ':' );
1069     if ( authpass == (char*) 0 )
1070     {
1071     /* No colon? Bogus auth info. */
1072     send_authenticate( hc, dirname );
1073     return -1;
1074     }
1075     *authpass++ = '\0';
1076    
1077     /* See if we have a cached entry and can use it. */
1078     if ( maxprevauthpath != 0 &&
1079     strcmp( authpath, prevauthpath ) == 0 &&
1080     sb.st_mtime == prevmtime &&
1081     strcmp( authinfo, prevuser ) == 0 )
1082     {
1083     /* Yes. Check against the cached encrypted password. */
1084     if ( strcmp( crypt( authpass, prevcryp ), prevcryp ) == 0 )
1085     {
1086     /* Ok! */
1087     httpd_realloc_str(
1088     &hc->remoteuser, &hc->maxremoteuser, strlen( authinfo ) );
1089     (void) strcpy( hc->remoteuser, authinfo );
1090     return 1;
1091     }
1092     else
1093     {
1094     /* No. */
1095     send_authenticate( hc, dirname );
1096     return -1;
1097     }
1098     }
1099    
1100     /* Open the password file. */
1101     fp = fopen( authpath, "r" );
1102     if ( fp == (FILE*) 0 )
1103     {
1104     /* The file exists but we can't open it? Disallow access. */
1105     syslog(
1106     LOG_ERR, "%.80s auth file %.80s could not be opened - %m",
1107     httpd_ntoa( &hc->client_addr ), authpath );
1108     httpd_send_err(
1109     hc, 403, err403title, "",
1110     ERROR_FORM( err403form, "The requested URL '%.80s' is protected by an authentication file, but the authentication file cannot be opened.\n" ),
1111     hc->encodedurl );
1112     return -1;
1113     }
1114    
1115     /* Read it. */
1116     while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
1117     {
1118     /* Nuke newline. */
1119     l = strlen( line );
1120     if ( line[l - 1] == '\n' )
1121     line[l - 1] = '\0';
1122     /* Split into user and encrypted password. */
1123     cryp = strchr( line, ':' );
1124     if ( cryp == (char*) 0 )
1125     continue;
1126     *cryp++ = '\0';
1127     /* Is this the right user? */
1128     if ( strcmp( line, authinfo ) == 0 )
1129     {
1130     /* Yes. */
1131     (void) fclose( fp );
1132     /* So is the password right? */
1133     if ( strcmp( crypt( authpass, cryp ), cryp ) == 0 )
1134     {
1135     /* Ok! */
1136     httpd_realloc_str(
1137     &hc->remoteuser, &hc->maxremoteuser, strlen( line ) );
1138     (void) strcpy( hc->remoteuser, line );
1139     /* And cache this user's info for next time. */
1140     httpd_realloc_str(
1141     &prevauthpath, &maxprevauthpath, strlen( authpath ) );
1142     (void) strcpy( prevauthpath, authpath );
1143     prevmtime = sb.st_mtime;
1144     httpd_realloc_str(
1145     &prevuser, &maxprevuser, strlen( authinfo ) );
1146     (void) strcpy( prevuser, authinfo );
1147     httpd_realloc_str( &prevcryp, &maxprevcryp, strlen( cryp ) );
1148     (void) strcpy( prevcryp, cryp );
1149     return 1;
1150     }
1151     else
1152     {
1153     /* No. */
1154     send_authenticate( hc, dirname );
1155     return -1;
1156     }
1157     }
1158     }
1159    
1160     /* Didn't find that user. Access denied. */
1161     (void) fclose( fp );
1162     send_authenticate( hc, dirname );
1163     return -1;
1164     }
1165    
1166     #endif /* AUTH_FILE */
1167    
1168    
1169     static void
1170     send_dirredirect( httpd_conn* hc )
1171     {
1172     static char* location;
1173     static char* header;
1174     static int maxlocation = 0, maxheader = 0;
1175     static char headstr[] = "Location: ";
1176    
1177     httpd_realloc_str( &location, &maxlocation, strlen( hc->encodedurl ) + 1 );
1178     (void) my_snprintf( location, maxlocation,
1179     "%s/", hc->encodedurl );
1180     httpd_realloc_str(
1181     &header, &maxheader, sizeof(headstr) + strlen( location ) );
1182     (void) my_snprintf( header, maxheader,
1183     "%s%s\r\n", headstr, location );
1184     send_response( hc, 302, err302title, header, err302form, location );
1185     }
1186    
1187    
1188     char*
1189     httpd_method_str( int method )
1190     {
1191     switch ( method )
1192     {
1193     case METHOD_GET: return "GET";
1194     case METHOD_HEAD: return "HEAD";
1195     case METHOD_POST: return "POST";
1196     default: return "UNKNOWN";
1197     }
1198     }
1199    
1200    
1201     static int
1202     hexit( char c )
1203     {
1204     if ( c >= '0' && c <= '9' )
1205     return c - '0';
1206     if ( c >= 'a' && c <= 'f' )
1207     return c - 'a' + 10;
1208     if ( c >= 'A' && c <= 'F' )
1209     return c - 'A' + 10;
1210     return 0; /* shouldn't happen, we're guarded by isxdigit() */
1211     }
1212    
1213    
1214     /* Copies and decodes a string. It's ok for from and to to be the
1215     ** same string.
1216     */
1217     static void
1218     strdecode( char* to, char* from )
1219     {
1220     for ( ; *from != '\0'; ++to, ++from )
1221     {
1222     if ( from[0] == '%' && isxdigit( from[1] ) && isxdigit( from[2] ) )
1223     {
1224     *to = hexit( from[1] ) * 16 + hexit( from[2] );
1225     from += 2;
1226     }
1227     else
1228     *to = *from;
1229     }
1230     *to = '\0';
1231     }
1232    
1233    
1234     #ifdef GENERATE_INDEXES
1235     /* Copies and encodes a string. */
1236     static void
1237     strencode( char* to, int tosize, char* from )
1238     {
1239     int tolen;
1240    
1241     for ( tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from )
1242     {
1243     if ( isalnum(*from) || strchr( "/_.-~", *from ) != (char*) 0 )
1244     {
1245     *to = *from;
1246     ++to;
1247     ++tolen;
1248     }
1249     else
1250     {
1251     (void) sprintf( to, "%%%02x", (int) *from & 0xff );
1252     to += 3;
1253     tolen += 3;
1254     }
1255     }
1256     *to = '\0';
1257     }
1258     #endif /* GENERATE_INDEXES */
1259    
1260    
1261     #ifdef TILDE_MAP_1
1262     /* Map a ~username/whatever URL into <prefix>/username. */
1263     static int
1264     tilde_map_1( httpd_conn* hc )
1265     {
1266     static char* temp;
1267     static int maxtemp = 0;
1268     int len;
1269     static char* prefix = TILDE_MAP_1;
1270    
1271     len = strlen( hc->expnfilename ) - 1;
1272     httpd_realloc_str( &temp, &maxtemp, len );
1273     (void) strcpy( temp, &hc->expnfilename[1] );
1274     httpd_realloc_str(
1275     &hc->expnfilename, &hc->maxexpnfilename, strlen( prefix ) + 1 + len );
1276     (void) strcpy( hc->expnfilename, prefix );
1277     if ( prefix[0] != '\0' )
1278     (void) strcat( hc->expnfilename, "/" );
1279     (void) strcat( hc->expnfilename, temp );
1280     return 1;
1281     }
1282     #endif /* TILDE_MAP_1 */
1283    
1284     #ifdef TILDE_MAP_2
1285     /* Map a ~username/whatever URL into <user's homedir>/<postfix>. */
1286     static int
1287     tilde_map_2( httpd_conn* hc )
1288     {
1289     static char* temp;
1290     static int maxtemp = 0;
1291     static char* postfix = TILDE_MAP_2;
1292     char* cp;
1293     struct passwd* pw;
1294     char* alt;
1295     char* rest;
1296    
1297     /* Get the username. */
1298     httpd_realloc_str( &temp, &maxtemp, strlen( hc->expnfilename ) - 1 );
1299     (void) strcpy( temp, &hc->expnfilename[1] );
1300     cp = strchr( temp, '/' );
1301     if ( cp != (char*) 0 )
1302     *cp++ = '\0';
1303     else
1304     cp = "";
1305    
1306     /* Get the passwd entry. */
1307     pw = getpwnam( temp );
1308     if ( pw == (struct passwd*) 0 )
1309     return 0;
1310    
1311     /* Set up altdir. */
1312     httpd_realloc_str(
1313     &hc->altdir, &hc->maxaltdir,
1314     strlen( pw->pw_dir ) + 1 + strlen( postfix ) );
1315     (void) strcpy( hc->altdir, pw->pw_dir );
1316     if ( postfix[0] != '\0' )
1317     {
1318     (void) strcat( hc->altdir, "/" );
1319     (void) strcat( hc->altdir, postfix );
1320     }
1321     alt = expand_symlinks( hc->altdir, &rest, 0, 1 );
1322     if ( rest[0] != '\0' )
1323     return 0;
1324     httpd_realloc_str( &hc->altdir, &hc->maxaltdir, strlen( alt ) );
1325     (void) strcpy( hc->altdir, alt );
1326    
1327     /* And the filename becomes altdir plus the post-~ part of the original. */
1328     httpd_realloc_str(
1329     &hc->expnfilename, &hc->maxexpnfilename,
1330     strlen( hc->altdir ) + 1 + strlen( cp ) );
1331     (void) my_snprintf( hc->expnfilename, hc->maxexpnfilename,
1332     "%s/%s", hc->altdir, cp );
1333    
1334     /* For this type of tilde mapping, we want to defeat vhost mapping. */
1335     hc->tildemapped = 1;
1336    
1337     return 1;
1338     }
1339     #endif /* TILDE_MAP_2 */
1340    
1341    
1342     /* Virtual host mapping. */
1343     static int
1344     vhost_map( httpd_conn* hc )
1345     {
1346     httpd_sockaddr sa;
1347     int sz;
1348     static char* tempfilename;
1349     static int maxtempfilename = 0;
1350     char* cp1;
1351     int len;
1352     #ifdef VHOST_DIRLEVELS
1353     int i;
1354     char* cp2;
1355     #endif /* VHOST_DIRLEVELS */
1356    
1357     /* Figure out the virtual hostname. */
1358     if ( hc->reqhost[0] != '\0' )
1359     hc->hostname = hc->reqhost;
1360     else if ( hc->hdrhost[0] != '\0' )
1361     hc->hostname = hc->hdrhost;
1362     else
1363     {
1364     sz = sizeof(sa);
1365     if ( getsockname( hc->conn_fd, &sa.sa, &sz ) < 0 )
1366     {
1367     syslog( LOG_ERR, "getsockname - %m" );
1368     return 0;
1369     }
1370     hc->hostname = httpd_ntoa( &sa );
1371     }
1372     /* Pound it to lower case. */
1373     for ( cp1 = hc->hostname; *cp1 != '\0'; ++cp1 )
1374     if ( isupper( *cp1 ) )
1375     *cp1 = tolower( *cp1 );
1376    
1377     if ( hc->tildemapped )
1378     return 1;
1379    
1380     /* Figure out the host directory. */
1381     #ifdef VHOST_DIRLEVELS
1382     httpd_realloc_str(
1383     &hc->hostdir, &hc->maxhostdir,
1384     strlen( hc->hostname ) + 2 * VHOST_DIRLEVELS );
1385     if ( strncmp( hc->hostname, "www.", 4 ) == 0 )
1386     cp1 = &hc->hostname[4];
1387     else
1388     cp1 = hc->hostname;
1389     for ( cp2 = hc->hostdir, i = 0; i < VHOST_DIRLEVELS; ++i )
1390     {
1391     /* Skip dots in the hostname. If we don't, then we get vhost
1392     ** directories in higher level of filestructure if dot gets
1393     ** involved into path construction. It's `while' used here instead
1394     ** of `if' for it's possible to have a hostname formed with two
1395     ** dots at the end of it.
1396     */
1397     while ( *cp1 == '.' )
1398     ++cp1;
1399     /* Copy a character from the hostname, or '_' if we ran out. */
1400     if ( *cp1 != '\0' )
1401     *cp2++ = *cp1++;
1402     else
1403     *cp2++ = '_';
1404     /* Copy a slash. */
1405     *cp2++ = '/';
1406     }
1407     (void) strcpy( cp2, hc->hostname );
1408     #else /* VHOST_DIRLEVELS */
1409     httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, strlen( hc->hostname ) );
1410     (void) strcpy( hc->hostdir, hc->hostname );
1411     #endif /* VHOST_DIRLEVELS */
1412    
1413     /* Prepend hostdir to the filename. */
1414     len = strlen( hc->expnfilename );
1415     httpd_realloc_str( &tempfilename, &maxtempfilename, len );
1416     (void) strcpy( tempfilename, hc->expnfilename );
1417     httpd_realloc_str(
1418     &hc->expnfilename, &hc->maxexpnfilename,
1419     strlen( hc->hostdir ) + 1 + len );
1420     (void) strcpy( hc->expnfilename, hc->hostdir );
1421     (void) strcat( hc->expnfilename, "/" );
1422     (void) strcat( hc->expnfilename, tempfilename );
1423     return 1;
1424     }
1425    
1426    
1427     /* Expands all symlinks in the given filename, eliding ..'s and leading /'s.
1428     ** Returns the expanded path (pointer to static string), or (char*) 0 on
1429     ** errors. Also returns, in the string pointed to by restP, any trailing
1430     ** parts of the path that don't exist.
1431     **
1432     ** This is a fairly nice little routine. It handles any size filenames
1433     ** without excessive mallocs.
1434     */
1435     static char*
1436     expand_symlinks( char* path, char** restP, int no_symlink, int tildemapped )
1437     {
1438     static char* checked;
1439     static char* rest;
1440     char link[5000];
1441     static int maxchecked = 0, maxrest = 0;
1442     int checkedlen, restlen, linklen, prevcheckedlen, prevrestlen, nlinks, i;
1443     char* r;
1444     char* cp1;
1445     char* cp2;
1446    
1447     if ( no_symlink )
1448     {
1449     /* If we are chrooted, we can actually skip the symlink-expansion,
1450     ** since it's impossible to get out of the tree. However, we still
1451     ** need to do the pathinfo check, and the existing symlink expansion
1452     ** code is a pretty reasonable way to do this. So, what we do is
1453     ** a single stat() of the whole filename - if it exists, then we
1454     ** return it as is with nothing in restP. If it doesn't exist, we
1455     ** fall through to the existing code.
1456     **
1457     ** One side-effect of this is that users can't symlink to central
1458     ** approved CGIs any more. The workaround is to use the central
1459     ** URL for the CGI instead of a local symlinked one.
1460     */
1461     struct stat sb;
1462     if ( stat( path, &sb ) != -1 )
1463     {
1464     httpd_realloc_str( &checked, &maxchecked, strlen( path ) );
1465     (void) strcpy( checked, path );
1466     httpd_realloc_str( &rest, &maxrest, 0 );
1467     rest[0] = '\0';
1468     *restP = rest;
1469     return checked;
1470     }
1471     }
1472    
1473     /* Start out with nothing in checked and the whole filename in rest. */
1474     httpd_realloc_str( &checked, &maxchecked, 1 );
1475     checked[0] = '\0';
1476     checkedlen = 0;
1477     restlen = strlen( path );
1478     httpd_realloc_str( &rest, &maxrest, restlen );
1479     (void) strcpy( rest, path );
1480     if ( ! tildemapped )
1481     /* Remove any leading slashes. */
1482     while ( rest[0] == '/' )
1483     {
1484     (void) strcpy( rest, &(rest[1]) );
1485     --restlen;
1486     }
1487     r = rest;
1488     nlinks = 0;
1489    
1490     /* While there are still components to check... */
1491     while ( restlen > 0 )
1492     {
1493     /* Save current checkedlen in case we get a symlink. Save current
1494     ** restlen in case we get a non-existant component.
1495     */
1496     prevcheckedlen = checkedlen;
1497     prevrestlen = restlen;
1498    
1499     /* Grab one component from r and transfer it to checked. */
1500     cp1 = strchr( r, '/' );
1501     if ( cp1 != (char*) 0 )
1502     {
1503     i = cp1 - r;
1504     if ( i == 0 )
1505     {
1506     /* Special case for absolute paths. */
1507     httpd_realloc_str( &checked, &maxchecked, checkedlen + 1 );
1508     (void) strncpy( &checked[checkedlen], r, 1 );
1509     checkedlen += 1;
1510     }
1511     else if ( strncmp( r, "..", MAX( i, 2 ) ) == 0 )
1512     {
1513     /* Ignore ..'s that go above the start of the path. */
1514     if ( checkedlen != 0 )
1515     {
1516     cp2 = strrchr( checked, '/' );
1517     if ( cp2 == (char*) 0 )
1518     checkedlen = 0;
1519     else if ( cp2 == checked )
1520     checkedlen = 1;
1521     else
1522     checkedlen = cp2 - checked;
1523     }
1524     }
1525     else
1526     {
1527     httpd_realloc_str( &checked, &maxchecked, checkedlen + 1 + i );
1528     if ( checkedlen > 0 && checked[checkedlen-1] != '/' )
1529     checked[checkedlen++] = '/';
1530     (void) strncpy( &checked[checkedlen], r, i );
1531     checkedlen += i;
1532     }
1533     checked[checkedlen] = '\0';
1534     r += i + 1;
1535     restlen -= i + 1;
1536     }
1537     else
1538     {
1539     /* No slashes remaining, r is all one component. */
1540     if ( strcmp( r, ".." ) == 0 )
1541     {
1542     /* Ignore ..'s that go above the start of the path. */
1543     if ( checkedlen != 0 )
1544     {
1545     cp2 = strrchr( checked, '/' );
1546     if ( cp2 == (char*) 0 )
1547     checkedlen = 0;
1548     else if ( cp2 == checked )
1549     checkedlen = 1;
1550     else
1551     checkedlen = cp2 - checked;
1552     checked[checkedlen] = '\0';
1553     }
1554     }
1555     else
1556     {
1557     httpd_realloc_str(
1558     &checked, &maxchecked, checkedlen + 1 + restlen );
1559     if ( checkedlen > 0 && checked[checkedlen-1] != '/' )
1560     checked[checkedlen++] = '/';
1561     (void) strcpy( &checked[checkedlen], r );
1562     checkedlen += restlen;
1563     }
1564     r += restlen;
1565     restlen = 0;
1566     }
1567    
1568     /* Try reading the current filename as a symlink */
1569     if ( checked[0] == '\0' )
1570     continue;
1571     linklen = readlink( checked, link, sizeof(link) );
1572     if ( linklen == -1 )
1573     {
1574     if ( errno == EINVAL )
1575     continue; /* not a symlink */
1576     if ( errno == EACCES || errno == ENOENT || errno == ENOTDIR )
1577     {
1578     /* That last component was bogus. Restore and return. */
1579     *restP = r - ( prevrestlen - restlen );
1580     if ( prevcheckedlen == 0 )
1581     (void) strcpy( checked, "." );
1582     else
1583     checked[prevcheckedlen] = '\0';
1584     return checked;
1585     }
1586     syslog( LOG_ERR, "readlink %.80s - %m", checked );
1587     return (char*) 0;
1588     }
1589     ++nlinks;
1590     if ( nlinks > MAX_LINKS )
1591     {
1592     syslog( LOG_ERR, "too many symlinks in %.80s", path );
1593     return (char*) 0;
1594     }
1595     link[linklen] = '\0';
1596     if ( link[linklen - 1] == '/' )
1597     link[--linklen] = '\0'; /* trim trailing slash */
1598    
1599     /* Insert the link contents in front of the rest of the filename. */
1600     if ( restlen != 0 )
1601     {
1602     (void) strcpy( rest, r );
1603     httpd_realloc_str( &rest, &maxrest, restlen + linklen + 1 );
1604     for ( i = restlen; i >= 0; --i )
1605     rest[i + linklen + 1] = rest[i];
1606     (void) strcpy( rest, link );
1607     rest[linklen] = '/';
1608     restlen += linklen + 1;
1609     r = rest;
1610     }
1611     else
1612     {
1613     /* There's nothing left in the filename, so the link contents
1614     ** becomes the rest.
1615     */
1616     httpd_realloc_str( &rest, &maxrest, linklen );
1617     (void) strcpy( rest, link );
1618     restlen = linklen;
1619     r = rest;
1620     }
1621    
1622     if ( rest[0] == '/' )
1623     {
1624     /* There must have been an absolute symlink - zero out checked. */
1625     checked[0] = '\0';
1626     checkedlen = 0;
1627     }
1628     else
1629     {
1630     /* Re-check this component. */
1631     checkedlen = prevcheckedlen;
1632     checked[checkedlen] = '\0';
1633     }
1634     }
1635    
1636     /* Ok. */
1637     *restP = r;
1638     if ( checked[0] == '\0' )
1639     (void) strcpy( checked, "." );
1640     return checked;
1641     }
1642    
1643    
1644     int
1645     httpd_get_conn( httpd_server* hs, int listen_fd, httpd_conn* hc )
1646     {
1647     httpd_sockaddr sa;
1648     int sz;
1649    
1650     if ( ! hc->initialized )
1651     {
1652     hc->read_size = 0;
1653     httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 );
1654     hc->maxdecodedurl =
1655     hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings =
1656     hc->maxpathinfo = hc->maxquery = hc->maxaccept =
1657     hc->maxaccepte = hc->maxreqhost = hc->maxhostdir =
1658     hc->maxremoteuser = hc->maxresponse = 0;
1659     #ifdef TILDE_MAP_2
1660     hc->maxaltdir = 0;
1661     #endif /* TILDE_MAP_2 */
1662     httpd_realloc_str( &hc->decodedurl, &hc->maxdecodedurl, 1 );
1663     httpd_realloc_str( &hc->origfilename, &hc->maxorigfilename, 1 );
1664     httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, 0 );
1665     httpd_realloc_str( &hc->encodings, &hc->maxencodings, 0 );
1666     httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, 0 );
1667     httpd_realloc_str( &hc->query, &hc->maxquery, 0 );
1668     httpd_realloc_str( &hc->accept, &hc->maxaccept, 0 );
1669     httpd_realloc_str( &hc->accepte, &hc->maxaccepte, 0 );
1670     httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, 0 );
1671     httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, 0 );
1672     httpd_realloc_str( &hc->remoteuser, &hc->maxremoteuser, 0 );
1673     httpd_realloc_str( &hc->response, &hc->maxresponse, 0 );
1674     #ifdef TILDE_MAP_2
1675     httpd_realloc_str( &hc->altdir, &hc->maxaltdir, 0 );
1676     #endif /* TILDE_MAP_2 */
1677     hc->initialized = 1;
1678     }
1679    
1680     /* Accept the new connection. */
1681     sz = sizeof(sa);
1682     hc->conn_fd = accept( listen_fd, &sa.sa, &sz );
1683     if ( hc->conn_fd < 0 )
1684     {
1685     if ( errno == EWOULDBLOCK )
1686     return GC_NO_MORE;
1687     syslog( LOG_ERR, "accept - %m" );
1688     return GC_FAIL;
1689     }
1690     if ( ! sockaddr_check( &sa ) )
1691     {
1692     syslog( LOG_ERR, "unknown sockaddr family" );
1693     return GC_FAIL;
1694     }
1695     (void) fcntl( hc->conn_fd, F_SETFD, 1 );
1696     hc->hs = hs;
1697     memset( &hc->client_addr, 0, sizeof(hc->client_addr) );
1698     memcpy( &hc->client_addr, &sa, sockaddr_len( &sa ) );
1699     hc->read_idx = 0;
1700     hc->checked_idx = 0;
1701     hc->checked_state = CHST_FIRSTWORD;
1702     hc->method = METHOD_UNKNOWN;
1703     hc->status = 0;
1704     hc->bytes_to_send = 0;
1705     hc->bytes_sent = 0;
1706     hc->encodedurl = "";
1707     hc->decodedurl[0] = '\0';
1708     hc->protocol = "UNKNOWN";
1709     hc->origfilename[0] = '\0';
1710     hc->expnfilename[0] = '\0';
1711     hc->encodings[0] = '\0';
1712     hc->pathinfo[0] = '\0';
1713     hc->query[0] = '\0';
1714     hc->referer = "";
1715     hc->useragent = "";
1716     hc->accept[0] = '\0';
1717     hc->accepte[0] = '\0';
1718     hc->acceptl = "";
1719     hc->cookie = "";
1720     hc->contenttype = "";
1721     hc->reqhost[0] = '\0';
1722     hc->hdrhost = "";
1723     hc->hostdir[0] = '\0';
1724     hc->authorization = "";
1725     hc->remoteuser[0] = '\0';
1726     hc->response[0] = '\0';
1727     #ifdef TILDE_MAP_2
1728     hc->altdir[0] = '\0';
1729     #endif /* TILDE_MAP_2 */
1730     hc->responselen = 0;
1731     hc->if_modified_since = (time_t) -1;
1732     hc->range_if = (time_t) -1;
1733     hc->contentlength = -1;
1734     hc->type = "";
1735     hc->hostname = (char*) 0;
1736     hc->mime_flag = 1;
1737     hc->one_one = 0;
1738     hc->got_range = 0;
1739     hc->tildemapped = 0;
1740     hc->init_byte_loc = 0;
1741     hc->end_byte_loc = -1;
1742     hc->keep_alive = 0;
1743     hc->should_linger = 0;
1744     hc->file_address = (char*) 0;
1745 root 1.2 #ifdef MMAP_MAX
1746     hc->file_fd = -1;
1747     hc->write_buf = (char*) 0;
1748     #endif
1749 root 1.1 return GC_OK;
1750     }
1751    
1752    
1753     /* Checks hc->read_buf to see whether a complete request has been read so far;
1754     ** either the first line has two words (an HTTP/0.9 request), or the first
1755     ** line has three words and there's a blank line present.
1756     **
1757     ** hc->read_idx is how much has been read in; hc->checked_idx is how much we
1758     ** have checked so far; and hc->checked_state is the current state of the
1759     ** finite state machine.
1760     */
1761     int
1762     httpd_got_request( httpd_conn* hc )
1763     {
1764     char c;
1765    
1766     for ( ; hc->checked_idx < hc->read_idx; ++hc->checked_idx )
1767     {
1768     c = hc->read_buf[hc->checked_idx];
1769     switch ( hc->checked_state )
1770     {
1771     case CHST_FIRSTWORD:
1772     switch ( c )
1773     {
1774     case ' ': case '\t':
1775     hc->checked_state = CHST_FIRSTWS;
1776     break;
1777     case '\n': case '\r':
1778     hc->checked_state = CHST_BOGUS;
1779     return GR_BAD_REQUEST;
1780     }
1781     break;
1782     case CHST_FIRSTWS:
1783     switch ( c )
1784     {
1785     case ' ': case '\t':
1786     break;
1787     case '\n': case '\r':
1788     hc->checked_state = CHST_BOGUS;
1789     return GR_BAD_REQUEST;
1790     default:
1791     hc->checked_state = CHST_SECONDWORD;
1792     break;
1793     }
1794     break;
1795     case CHST_SECONDWORD:
1796     switch ( c )
1797     {
1798     case ' ': case '\t':
1799     hc->checked_state = CHST_SECONDWS;
1800     break;
1801     case '\n': case '\r':
1802     /* The first line has only two words - an HTTP/0.9 request. */
1803     return GR_GOT_REQUEST;
1804     }
1805     break;
1806     case CHST_SECONDWS:
1807     switch ( c )
1808     {
1809     case ' ': case '\t':
1810     break;
1811     case '\n': case '\r':
1812     hc->checked_state = CHST_BOGUS;
1813     return GR_BAD_REQUEST;
1814     default:
1815     hc->checked_state = CHST_THIRDWORD;
1816     break;
1817     }
1818     break;
1819     case CHST_THIRDWORD:
1820     switch ( c )
1821     {
1822     case ' ': case '\t':
1823     hc->checked_state = CHST_THIRDWS;
1824     break;
1825     case '\n':
1826     hc->checked_state = CHST_LF;
1827     break;
1828     case '\r':
1829     hc->checked_state = CHST_CR;
1830     break;
1831     }
1832     break;
1833     case CHST_THIRDWS:
1834     switch ( c )
1835     {
1836     case ' ': case '\t':
1837     break;
1838     case '\n':
1839     hc->checked_state = CHST_LF;
1840     break;
1841     case '\r':
1842     hc->checked_state = CHST_CR;
1843     break;
1844     default:
1845     hc->checked_state = CHST_BOGUS;
1846     return GR_BAD_REQUEST;
1847     }
1848     break;
1849     case CHST_LINE:
1850     switch ( c )
1851     {
1852     case '\n':
1853     hc->checked_state = CHST_LF;
1854     break;
1855     case '\r':
1856     hc->checked_state = CHST_CR;
1857     break;
1858     }
1859     break;
1860     case CHST_LF:
1861     switch ( c )
1862     {
1863     case '\n':
1864     /* Two newlines in a row - a blank line - end of request. */
1865     return GR_GOT_REQUEST;
1866     case '\r':
1867     hc->checked_state = CHST_CR;
1868     break;
1869     default:
1870     hc->checked_state = CHST_LINE;
1871     break;
1872     }
1873     break;
1874     case CHST_CR:
1875     switch ( c )
1876     {
1877     case '\n':
1878     hc->checked_state = CHST_CRLF;
1879     break;
1880     case '\r':
1881     /* Two returns in a row - end of request. */
1882     return GR_GOT_REQUEST;
1883     default:
1884     hc->checked_state = CHST_LINE;
1885     break;
1886     }
1887     break;
1888     case CHST_CRLF:
1889     switch ( c )
1890     {
1891     case '\n':
1892     /* Two newlines in a row - end of request. */
1893     return GR_GOT_REQUEST;
1894     case '\r':
1895     hc->checked_state = CHST_CRLFCR;
1896     break;
1897     default:
1898     hc->checked_state = CHST_LINE;
1899     break;
1900     }
1901     break;
1902     case CHST_CRLFCR:
1903     switch ( c )
1904     {
1905     case '\n': case '\r':
1906     /* Two CRLFs or two CRs in a row - end of request. */
1907     return GR_GOT_REQUEST;
1908     default:
1909     hc->checked_state = CHST_LINE;
1910     break;
1911     }
1912     break;
1913     case CHST_BOGUS:
1914     return GR_BAD_REQUEST;
1915     }
1916     }
1917     return GR_NO_REQUEST;
1918     }
1919    
1920    
1921     int
1922     httpd_parse_request( httpd_conn* hc )
1923     {
1924     char* buf;
1925     char* method_str;
1926     char* url;
1927     char* protocol;
1928     char* reqhost;
1929     char* eol;
1930     char* cp;
1931     char* pi;
1932    
1933     hc->checked_idx = 0; /* reset */
1934     method_str = bufgets( hc );
1935     url = strpbrk( method_str, " \t\n\r" );
1936     if ( url == (char*) 0 )
1937     {
1938     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1939     return -1;
1940     }
1941     *url++ = '\0';
1942     url += strspn( url, " \t\n\r" );
1943     protocol = strpbrk( url, " \t\n\r" );
1944     if ( protocol == (char*) 0 )
1945     {
1946     protocol = "HTTP/0.9";
1947     hc->mime_flag = 0;
1948     }
1949     else
1950     {
1951     *protocol++ = '\0';
1952     protocol += strspn( protocol, " \t\n\r" );
1953     if ( *protocol != '\0' )
1954     {
1955     eol = strpbrk( protocol, " \t\n\r" );
1956     if ( eol != (char*) 0 )
1957     *eol = '\0';
1958     if ( strcasecmp( protocol, "HTTP/1.0" ) != 0 )
1959     hc->one_one = 1;
1960     }
1961     }
1962     /* Check for HTTP/1.1 absolute URL. */
1963     if ( strncasecmp( url, "http://", 7 ) == 0 )
1964     {
1965     if ( ! hc->one_one )
1966     {
1967     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1968     return -1;
1969     }
1970     reqhost = url + 7;
1971     url = strchr( reqhost, '/' );
1972     if ( url == (char*) 0 )
1973     {
1974     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1975     return -1;
1976     }
1977     *url = '\0';
1978     httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, strlen( reqhost ) );
1979     (void) strcpy( hc->reqhost, reqhost );
1980     *url = '/';
1981     }
1982    
1983     if ( strcasecmp( method_str, httpd_method_str( METHOD_GET ) ) == 0 )
1984     hc->method = METHOD_GET;
1985     else if ( strcasecmp( method_str, httpd_method_str( METHOD_HEAD ) ) == 0 )
1986     hc->method = METHOD_HEAD;
1987     else if ( strcasecmp( method_str, httpd_method_str( METHOD_POST ) ) == 0 )
1988     hc->method = METHOD_POST;
1989     else
1990     {
1991     httpd_send_err( hc, 501, err501title, "", err501form, method_str );
1992     return -1;
1993     }
1994    
1995     hc->encodedurl = url;
1996     httpd_realloc_str(
1997     &hc->decodedurl, &hc->maxdecodedurl, strlen( hc->encodedurl ) );
1998     strdecode( hc->decodedurl, hc->encodedurl );
1999    
2000     de_dotdot( hc->decodedurl );
2001     if ( hc->decodedurl[0] != '/' || hc->decodedurl[1] == '/' ||
2002     ( hc->decodedurl[1] == '.' && hc->decodedurl[2] == '.' &&
2003     ( hc->decodedurl[3] == '\0' || hc->decodedurl[3] == '/' ) ) )
2004     {
2005     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
2006     return -1;
2007     }
2008    
2009     hc->protocol = protocol;
2010    
2011     httpd_realloc_str(
2012     &hc->origfilename, &hc->maxorigfilename, strlen( hc->decodedurl ) );
2013     (void) strcpy( hc->origfilename, &hc->decodedurl[1] );
2014     /* Special case for top-level URL. */
2015     if ( hc->origfilename[0] == '\0' )
2016     (void) strcpy( hc->origfilename, "." );
2017    
2018     /* Extract query string from encoded URL. */
2019     cp = strchr( hc->encodedurl, '?' );
2020     if ( cp != (char*) 0 )
2021     {
2022     ++cp;
2023     httpd_realloc_str( &hc->query, &hc->maxquery, strlen( cp ) );
2024     (void) strcpy( hc->query, cp );
2025     }
2026     /* And remove query from filename. */
2027     cp = strchr( hc->origfilename, '?' );
2028     if ( cp != (char*) 0 )
2029     *cp = '\0';
2030    
2031     if ( hc->mime_flag )
2032     {
2033     /* Read the MIME headers. */
2034     while ( ( buf = bufgets( hc ) ) != (char*) 0 )
2035     {
2036     if ( buf[0] == '\0' )
2037     break;
2038     if ( strncasecmp( buf, "Referer:", 8 ) == 0 )
2039     {
2040     cp = &buf[8];
2041     cp += strspn( cp, " \t" );
2042     hc->referer = cp;
2043     }
2044     else if ( strncasecmp( buf, "User-Agent:", 11 ) == 0 )
2045     {
2046     cp = &buf[11];
2047     cp += strspn( cp, " \t" );
2048     hc->useragent = cp;
2049     }
2050     else if ( strncasecmp( buf, "Host:", 5 ) == 0 )
2051     {
2052     cp = &buf[5];
2053     cp += strspn( cp, " \t" );
2054     hc->hdrhost = cp;
2055     cp = strchr( hc->hdrhost, ':' );
2056     if ( cp != (char*) 0 )
2057     *cp = '\0';
2058     }
2059     else if ( strncasecmp( buf, "Accept:", 7 ) == 0 )
2060     {
2061     cp = &buf[7];
2062     cp += strspn( cp, " \t" );
2063     if ( hc->accept[0] != '\0' )
2064     {
2065     if ( strlen( hc->accept ) > 5000 )
2066     {
2067     syslog(
2068     LOG_ERR, "%.80s way too much Accept: data",
2069     httpd_ntoa( &hc->client_addr ) );
2070     continue;
2071     }
2072     httpd_realloc_str(
2073     &hc->accept, &hc->maxaccept,
2074     strlen( hc->accept ) + 2 + strlen( cp ) );
2075     (void) strcat( hc->accept, ", " );
2076     }
2077     else
2078     httpd_realloc_str(
2079     &hc->accept, &hc->maxaccept, strlen( cp ) );
2080     (void) strcat( hc->accept, cp );
2081     }
2082     else if ( strncasecmp( buf, "Accept-Encoding:", 16 ) == 0 )
2083     {
2084     cp = &buf[16];
2085     cp += strspn( cp, " \t" );
2086     if ( hc->accepte[0] != '\0' )
2087     {
2088     if ( strlen( hc->accepte ) > 5000 )
2089     {
2090     syslog(
2091     LOG_ERR, "%.80s way too much Accept-Encoding: data",
2092     httpd_ntoa( &hc->client_addr ) );
2093     continue;
2094     }
2095     httpd_realloc_str(
2096     &hc->accepte, &hc->maxaccepte,
2097     strlen( hc->accepte ) + 2 + strlen( cp ) );
2098     (void) strcat( hc->accepte, ", " );
2099     }
2100     else
2101     httpd_realloc_str(
2102     &hc->accepte, &hc->maxaccepte, strlen( cp ) );
2103     (void) strcpy( hc->accepte, cp );
2104     }
2105     else if ( strncasecmp( buf, "Accept-Language:", 16 ) == 0 )
2106     {
2107     cp = &buf[16];
2108     cp += strspn( cp, " \t" );
2109     hc->acceptl = cp;
2110     }
2111     else if ( strncasecmp( buf, "If-Modified-Since:", 18 ) == 0 )
2112     {
2113     cp = &buf[18];
2114     hc->if_modified_since = tdate_parse( cp );
2115     if ( hc->if_modified_since == (time_t) -1 )
2116     syslog( LOG_DEBUG, "unparsable time: %.80s", cp );
2117     }
2118     else if ( strncasecmp( buf, "Cookie:", 7 ) == 0 )
2119     {
2120     cp = &buf[7];
2121     cp += strspn( cp, " \t" );
2122     hc->cookie = cp;
2123     }
2124     else if ( strncasecmp( buf, "Range:", 6 ) == 0 )
2125     {
2126     /* Only support %d- and %d-%d, not %d-%d,%d-%d or -%d. */
2127     if ( strchr( buf, ',' ) == (char*) 0 )
2128     {
2129     char* cp_dash;
2130     cp = strpbrk( buf, "=" );
2131     if ( cp != (char*) 0 )
2132     {
2133     cp_dash = strchr( cp + 1, '-' );
2134     if ( cp_dash != (char*) 0 && cp_dash != cp + 1 )
2135     {
2136     *cp_dash = '\0';
2137     hc->got_range = 1;
2138     hc->init_byte_loc = atol( cp + 1 );
2139     if ( isdigit( (int) cp_dash[1] ) )
2140     hc->end_byte_loc = atol( cp_dash + 1 );
2141     }
2142     }
2143     }
2144     }
2145     else if ( strncasecmp( buf, "Range-If:", 9 ) == 0 ||
2146     strncasecmp( buf, "If-Range:", 9 ) == 0 )
2147     {
2148     cp = &buf[9];
2149     hc->range_if = tdate_parse( cp );
2150     if ( hc->range_if == (time_t) -1 )
2151     syslog( LOG_DEBUG, "unparsable time: %.80s", cp );
2152     }
2153     else if ( strncasecmp( buf, "Content-Type:", 13 ) == 0 )
2154     {
2155     cp = &buf[13];
2156     cp += strspn( cp, " \t" );
2157     hc->contenttype = cp;
2158     }
2159     else if ( strncasecmp( buf, "Content-Length:", 15 ) == 0 )
2160     {
2161     cp = &buf[15];
2162     hc->contentlength = atol( cp );
2163     }
2164     else if ( strncasecmp( buf, "Authorization:", 14 ) == 0 )
2165     {
2166     cp = &buf[14];
2167     cp += strspn( cp, " \t" );
2168     hc->authorization = cp;
2169     }
2170     else if ( strncasecmp( buf, "Connection:", 11 ) == 0 )
2171     {
2172     cp = &buf[11];
2173     cp += strspn( cp, " \t" );
2174     if ( strcasecmp( cp, "keep-alive" ) == 0 )
2175     hc->keep_alive = 1;
2176     }
2177     #ifdef LOG_UNKNOWN_HEADERS
2178     else if ( strncasecmp( buf, "Accept-Charset:", 15 ) == 0 ||
2179     strncasecmp( buf, "Accept-Language:", 16 ) == 0 ||
2180     strncasecmp( buf, "Agent:", 6 ) == 0 ||
2181     strncasecmp( buf, "Cache-Control:", 14 ) == 0 ||
2182     strncasecmp( buf, "Cache-Info:", 11 ) == 0 ||
2183     strncasecmp( buf, "Charge-To:", 10 ) == 0 ||
2184     strncasecmp( buf, "Client-IP:", 10 ) == 0 ||
2185     strncasecmp( buf, "Date:", 5 ) == 0 ||
2186     strncasecmp( buf, "Extension:", 10 ) == 0 ||
2187     strncasecmp( buf, "Forwarded:", 10 ) == 0 ||
2188     strncasecmp( buf, "From:", 5 ) == 0 ||
2189     strncasecmp( buf, "HTTP-Version:", 13 ) == 0 ||
2190     strncasecmp( buf, "Max-Forwards:", 13 ) == 0 ||
2191     strncasecmp( buf, "Message-Id:", 11 ) == 0 ||
2192     strncasecmp( buf, "MIME-Version:", 13 ) == 0 ||
2193     strncasecmp( buf, "Negotiate:", 10 ) == 0 ||
2194     strncasecmp( buf, "Pragma:", 7 ) == 0 ||
2195     strncasecmp( buf, "Proxy-Agent:", 12 ) == 0 ||
2196     strncasecmp( buf, "Proxy-Connection:", 17 ) == 0 ||
2197     strncasecmp( buf, "Security-Scheme:", 16 ) == 0 ||
2198     strncasecmp( buf, "Session-Id:", 11 ) == 0 ||
2199     strncasecmp( buf, "UA-Color:", 9 ) == 0 ||
2200     strncasecmp( buf, "UA-CPU:", 7 ) == 0 ||
2201     strncasecmp( buf, "UA-Disp:", 8 ) == 0 ||
2202     strncasecmp( buf, "UA-OS:", 6 ) == 0 ||
2203     strncasecmp( buf, "UA-Pixels:", 10 ) == 0 ||
2204     strncasecmp( buf, "User:", 5 ) == 0 ||
2205     strncasecmp( buf, "Via:", 4 ) == 0 ||
2206     strncasecmp( buf, "X-", 2 ) == 0 )
2207     ; /* ignore */
2208     else
2209     syslog( LOG_DEBUG, "unknown request header: %.80s", buf );
2210     #endif /* LOG_UNKNOWN_HEADERS */
2211     }
2212     }
2213    
2214     if ( hc->one_one )
2215     {
2216     /* Check that HTTP/1.1 requests specify a host, as required. */
2217     if ( hc->reqhost[0] == '\0' && hc->hdrhost[0] == '\0' )
2218     {
2219     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
2220     return -1;
2221     }
2222    
2223     /* If the client wants to do keep-alives, it might also be doing
2224     ** pipelining. There's no way for us to tell. Since we don't
2225     ** implement keep-alives yet, if we close such a connection there
2226     ** might be unread pipelined requests waiting. So, we have to
2227     ** do a lingering close.
2228     */
2229     if ( hc->keep_alive )
2230     hc->should_linger = 1;
2231     }
2232    
2233     /* Ok, the request has been parsed. Now we resolve stuff that
2234     ** may require the entire request.
2235     */
2236    
2237     /* Copy original filename to expanded filename. */
2238     httpd_realloc_str(
2239     &hc->expnfilename, &hc->maxexpnfilename, strlen( hc->origfilename ) );
2240     (void) strcpy( hc->expnfilename, hc->origfilename );
2241    
2242     /* Tilde mapping. */
2243     if ( hc->expnfilename[0] == '~' )
2244     {
2245     #ifdef TILDE_MAP_1
2246     if ( ! tilde_map_1( hc ) )
2247     {
2248     httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
2249     return -1;
2250     }
2251     #endif /* TILDE_MAP_1 */
2252     #ifdef TILDE_MAP_2
2253     if ( ! tilde_map_2( hc ) )
2254     {
2255     httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
2256     return -1;
2257     }
2258     #endif /* TILDE_MAP_2 */
2259     }
2260    
2261     /* Virtual host mapping. */
2262     if ( hc->hs->vhost )
2263     if ( ! vhost_map( hc ) )
2264     {
2265     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
2266     return -1;
2267     }
2268    
2269     /* Expand all symbolic links in the filename. This also gives us
2270     ** any trailing non-existing components, for pathinfo.
2271     */
2272     cp = expand_symlinks( hc->expnfilename, &pi, hc->hs->no_symlink, hc->tildemapped );
2273     if ( cp == (char*) 0 )
2274     {
2275     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
2276     return -1;
2277     }
2278     httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, strlen( cp ) );
2279     (void) strcpy( hc->expnfilename, cp );
2280     httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, strlen( pi ) );
2281     (void) strcpy( hc->pathinfo, pi );
2282    
2283     /* Remove pathinfo stuff from the original filename too. */
2284     if ( hc->pathinfo[0] != '\0' )
2285     {
2286     int i;
2287     i = strlen( hc->origfilename ) - strlen( hc->pathinfo );
2288     if ( i > 0 && strcmp( &hc->origfilename[i], hc->pathinfo ) == 0 )
2289     hc->origfilename[i - 1] = '\0';
2290     }
2291    
2292     /* If the expanded filename is an absolute path, check that it's still
2293     ** within the current directory or the alternate directory.
2294     */
2295     if ( hc->expnfilename[0] == '/' )
2296     {
2297     if ( strncmp(
2298     hc->expnfilename, hc->hs->cwd, strlen( hc->hs->cwd ) ) == 0 )
2299     {
2300     /* Elide the current directory. */
2301     (void) strcpy(
2302     hc->expnfilename, &hc->expnfilename[strlen( hc->hs->cwd )] );
2303     }
2304     #ifdef TILDE_MAP_2
2305     else if ( hc->altdir[0] != '\0' &&
2306     ( strncmp(
2307     hc->expnfilename, hc->altdir,
2308     strlen( hc->altdir ) ) == 0 &&
2309     ( hc->expnfilename[strlen( hc->altdir )] == '\0' ||
2310     hc->expnfilename[strlen( hc->altdir )] == '/' ) ) )
2311     {}
2312     #endif /* TILDE_MAP_2 */
2313     else
2314     {
2315     syslog(
2316     LOG_NOTICE, "%.80s URL \"%.80s\" goes outside the web tree",
2317     httpd_ntoa( &hc->client_addr ), hc->encodedurl );
2318     httpd_send_err(
2319     hc, 403, err403title, "",
2320     ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file outside the permitted web server directory tree.\n" ),
2321     hc->encodedurl );
2322     return -1;
2323     }
2324     }
2325    
2326     return 0;
2327     }
2328    
2329    
2330     static char*
2331     bufgets( httpd_conn* hc )
2332     {
2333     int i;
2334     char c;
2335    
2336     for ( i = hc->checked_idx; hc->checked_idx < hc->read_idx; ++hc->checked_idx )
2337     {
2338     c = hc->read_buf[hc->checked_idx];
2339     if ( c == '\n' || c == '\r' )
2340     {
2341     hc->read_buf[hc->checked_idx] = '\0';
2342     ++hc->checked_idx;
2343     if ( c == '\r' && hc->checked_idx < hc->read_idx &&
2344     hc->read_buf[hc->checked_idx] == '\n' )
2345     {
2346     hc->read_buf[hc->checked_idx] = '\0';
2347     ++hc->checked_idx;
2348     }
2349     return &(hc->read_buf[i]);
2350     }
2351     }
2352     return (char*) 0;
2353     }
2354    
2355    
2356     static void
2357     de_dotdot( char* file )
2358     {
2359     char* cp;
2360     char* cp2;
2361     int l;
2362    
2363     /* Collapse any multiple / sequences. */
2364     while ( ( cp = strstr( file, "//") ) != (char*) 0 )
2365     {
2366     for ( cp2 = cp + 2; *cp2 == '/'; ++cp2 )
2367     continue;
2368     (void) strcpy( cp + 1, cp2 );
2369     }
2370    
2371     /* Elide any xxx/../ sequences. */
2372     while ( ( cp = strstr( file, "/../" ) ) != (char*) 0 )
2373     {
2374     for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 )
2375     continue;
2376     if ( cp2 < file )
2377     break;
2378     (void) strcpy( cp2, cp + 3 );
2379     }
2380    
2381     /* Also elide any xxx/.. at the end. */
2382     while ( ( l = strlen( file ) ) > 3 &&
2383     strcmp( ( cp = file + l - 3 ), "/.." ) == 0 )
2384     {
2385     for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 )
2386     continue;
2387     if ( cp2 < file )
2388     break;
2389     *cp2 = '\0';
2390     }
2391     }
2392    
2393    
2394     void
2395     httpd_close_conn( httpd_conn* hc, struct timeval* nowP )
2396     {
2397     make_log_entry( hc, nowP );
2398    
2399 root 1.2 #ifdef MMAP_MAX
2400     if ( hc->file_fd >= 0)
2401     {
2402     (void) close( hc->file_fd );
2403     hc->file_fd = -1;
2404     }
2405     if ( hc->write_buf )
2406 root 1.3 {
2407 root 1.2 (void) free (hc->write_buf);
2408 root 1.3 hc->write_buf = 0;
2409     }
2410 root 1.2 #endif
2411 root 1.1 if ( hc->file_address != (char*) 0 )
2412     {
2413     mmc_unmap( hc->file_address, &(hc->sb), nowP );
2414     hc->file_address = (char*) 0;
2415     }
2416     if ( hc->conn_fd >= 0 )
2417     {
2418     (void) close( hc->conn_fd );
2419     hc->conn_fd = -1;
2420     }
2421     }
2422    
2423     void
2424     httpd_destroy_conn( httpd_conn* hc )
2425     {
2426     if ( hc->initialized )
2427     {
2428     free( (void*) hc->read_buf );
2429     free( (void*) hc->decodedurl );
2430     free( (void*) hc->origfilename );
2431     free( (void*) hc->expnfilename );
2432     free( (void*) hc->encodings );
2433     free( (void*) hc->pathinfo );
2434     free( (void*) hc->query );
2435     free( (void*) hc->accept );
2436     free( (void*) hc->accepte );
2437     free( (void*) hc->reqhost );
2438     free( (void*) hc->hostdir );
2439     free( (void*) hc->remoteuser );
2440     free( (void*) hc->response );
2441     #ifdef TILDE_MAP_2
2442     free( (void*) hc->altdir );
2443     #endif /* TILDE_MAP_2 */
2444     hc->initialized = 0;
2445     }
2446     }
2447    
2448    
2449     /* Figures out MIME encodings and type based on the filename. Multiple
2450     ** encodings are separated by semicolons.
2451     */
2452     static void
2453     figure_mime( httpd_conn* hc )
2454     {
2455     int i, j, k, l;
2456     int got_enc;
2457     struct table {
2458     char* ext;
2459     char* val;
2460     };
2461     static struct table enc_tab[] = {
2462     #include "mime_encodings.h"
2463     };
2464     static struct table typ_tab[] = {
2465     #include "mime_types.h"
2466     };
2467    
2468     /* Look at the extensions on hc->expnfilename from the back forwards. */
2469     i = strlen( hc->expnfilename );
2470     for (;;)
2471     {
2472     j = i;
2473     for (;;)
2474     {
2475     --i;
2476     if ( i <= 0 )
2477     {
2478     /* No extensions left. */
2479     hc->type = "text/plain; charset=%s";
2480     return;
2481     }
2482     if ( hc->expnfilename[i] == '.' )
2483     break;
2484     }
2485     /* Found an extension. */
2486     got_enc = 0;
2487     for ( k = 0; k < sizeof(enc_tab)/sizeof(*enc_tab); ++k )
2488     {
2489     l = strlen( enc_tab[k].ext );
2490     if ( l == j - i - 1 &&
2491     strncasecmp( &hc->expnfilename[i+1], enc_tab[k].ext, l ) == 0 )
2492     {
2493     httpd_realloc_str(
2494     &hc->encodings, &hc->maxencodings,
2495     strlen( enc_tab[k].val ) + 1 );
2496     if ( hc->encodings[0] != '\0' )
2497     (void) strcat( hc->encodings, ";" );
2498     (void) strcat( hc->encodings, enc_tab[k].val );
2499     got_enc = 1;
2500     }
2501     }
2502     if ( ! got_enc )
2503     {
2504     /* No encoding extension found - time to try type extensions. */
2505     for ( k = 0; k < sizeof(typ_tab)/sizeof(*typ_tab); ++k )
2506     {
2507     l = strlen( typ_tab[k].ext );
2508     if ( l == j - i - 1 &&
2509     strncasecmp(
2510     &hc->expnfilename[i+1], typ_tab[k].ext, l ) == 0 )
2511     {
2512     hc->type = typ_tab[k].val;
2513     return;
2514     }
2515     }
2516     /* No recognized type extension found - return default. */
2517     hc->type = "text/plain; charset=%s";
2518     return;
2519     }
2520     }
2521     }
2522    
2523    
2524     #ifdef CGI_TIMELIMIT
2525     static void
2526     cgi_kill2( ClientData client_data, struct timeval* nowP )
2527     {
2528     pid_t pid;
2529    
2530     /* Before trying to kill the CGI process, reap any zombie processes.
2531     ** That may get rid of the CGI process.
2532     */
2533     (void) do_reap();
2534    
2535     pid = (pid_t) client_data.i;
2536     if ( kill( pid, SIGKILL ) == 0 )
2537     syslog( LOG_ERR, "hard-killed CGI process %d", pid );
2538     }
2539    
2540     static void
2541     cgi_kill( ClientData client_data, struct timeval* nowP )
2542     {
2543     pid_t pid;
2544    
2545     /* Before trying to kill the CGI process, reap any zombie processes.
2546     ** That may get rid of the CGI process.
2547     */
2548     (void) do_reap();
2549    
2550     pid = (pid_t) client_data.i;
2551     if ( kill( pid, SIGINT ) == 0 )
2552     {
2553     syslog( LOG_ERR, "killed CGI process %d", pid );
2554     /* In case this isn't enough, schedule an uncatchable kill. */
2555     if ( tmr_create( nowP, cgi_kill2, client_data, 5 * 1000L, 0 ) == (Timer*) 0 )
2556     {
2557     syslog( LOG_CRIT, "tmr_create(cgi_kill2) failed" );
2558     exit( 1 );
2559     }
2560     }
2561     }
2562     #endif /* CGI_TIMELIMIT */
2563    
2564    
2565     #ifdef GENERATE_INDEXES
2566    
2567     /* qsort comparison routine - declared old-style on purpose, for portability. */
2568     static int
2569     name_compare( a, b )
2570     char** a;
2571     char** b;
2572     {
2573     return strcmp( *a, *b );
2574     }
2575    
2576    
2577     static off_t
2578     ls( httpd_conn* hc )
2579     {
2580     DIR* dirp;
2581     struct dirent* de;
2582     int namlen;
2583     static int maxnames = 0;
2584     int nnames;
2585     static char* names;
2586     static char** nameptrs;
2587     static char* name;
2588     static int maxname = 0;
2589     static char* rname;
2590     static int maxrname = 0;
2591     static char* encrname;
2592     static int maxencrname = 0;
2593     FILE* fp;
2594     int i, r;
2595     struct stat sb;
2596     struct stat lsb;
2597     char modestr[20];
2598     char* linkprefix;
2599     char link[MAXPATHLEN];
2600     int linklen;
2601     char* fileclass;
2602     time_t now;
2603     char* timestr;
2604     ClientData client_data;
2605    
2606     dirp = opendir( hc->expnfilename );
2607     if ( dirp == (DIR*) 0 )
2608     {
2609     syslog( LOG_ERR, "opendir %.80s - %m", hc->expnfilename );
2610     httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
2611     return -1;
2612     }
2613    
2614     send_mime( hc, 200, ok200title, "", "", "text/html", -1, hc->sb.st_mtime );
2615     if ( hc->method == METHOD_HEAD )
2616     closedir( dirp );
2617     else if ( hc->method == METHOD_GET )
2618     {
2619     httpd_write_response( hc );
2620     r = fork( );
2621     if ( r < 0 )
2622     {
2623     syslog( LOG_ERR, "fork - %m" );
2624     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
2625     return -1;
2626     }
2627     if ( r == 0 )
2628     {
2629     /* Child process. */
2630     unlisten( hc->hs );
2631    
2632     #ifdef CGI_NICE
2633     /* Set priority. */
2634     (void) nice( CGI_NICE );
2635     #endif /* CGI_NICE */
2636    
2637     /* Open a stdio stream so that we can use fprintf, which is more
2638     ** efficient than a bunch of separate write()s. We don't have
2639     ** to worry about double closes or file descriptor leaks cause
2640     ** we're in a subprocess.
2641     */
2642     fp = fdopen( hc->conn_fd, "w" );
2643     if ( fp == (FILE*) 0 )
2644     {
2645     syslog( LOG_ERR, "fdopen - %m" );
2646     httpd_send_err(
2647     hc, 500, err500title, "", err500form, hc->encodedurl );
2648     closedir( dirp );
2649     exit( 1 );
2650     }
2651    
2652     (void) fprintf( fp, "\
2653     <HTML><HEAD><TITLE>Index of %.80s</TITLE></HEAD>\n\
2654     <BODY BGCOLOR=\"#99cc99\">\n\
2655     <H2>Index of %.80s</H2>\n\
2656     <PRE>\n\
2657     mode links bytes last-changed name\n\
2658     <HR>",
2659     hc->encodedurl, hc->encodedurl );
2660    
2661     /* Read in names. */
2662     nnames = 0;
2663     while ( ( de = readdir( dirp ) ) != 0 ) /* dirent or direct */
2664     {
2665     if ( nnames >= maxnames )
2666     {
2667     if ( maxnames == 0 )
2668     {
2669     maxnames = 100;
2670     names = NEW( char, maxnames * MAXPATHLEN );
2671     nameptrs = NEW( char*, maxnames );
2672     }
2673     else
2674     {
2675     maxnames *= 2;
2676     names = RENEW( names, char, maxnames * MAXPATHLEN );
2677     nameptrs = RENEW( nameptrs, char*, maxnames );
2678     }
2679     if ( names == (char*) 0 || nameptrs == (char**) 0 )
2680     {
2681     syslog( LOG_ERR, "out of memory reallocating directory names" );
2682     exit( 1 );
2683     }
2684     for ( i = 0; i < maxnames; ++i )
2685     nameptrs[i] = &names[i * MAXPATHLEN];
2686     }
2687     namlen = NAMLEN(de);
2688     (void) strncpy( nameptrs[nnames], de->d_name, namlen );
2689     nameptrs[nnames][namlen] = '\0';
2690     ++nnames;
2691     }
2692     closedir( dirp );
2693    
2694     /* Sort the names. */
2695     qsort( nameptrs, nnames, sizeof(*nameptrs), name_compare );
2696    
2697     /* Generate output. */
2698     for ( i = 0; i < nnames; ++i )
2699     {
2700     httpd_realloc_str(
2701     &name, &maxname,
2702     strlen( hc->expnfilename ) + 1 + strlen( nameptrs[i] ) );
2703     httpd_realloc_str(
2704     &rname, &maxrname,
2705     strlen( hc->origfilename ) + 1 + strlen( nameptrs[i] ) );
2706     if ( hc->expnfilename[0] == '\0' ||
2707     strcmp( hc->expnfilename, "." ) == 0 )
2708     {
2709     (void) strcpy( name, nameptrs[i] );
2710     (void) strcpy( rname, nameptrs[i] );
2711     }
2712     else
2713     {
2714     (void) my_snprintf( name, maxname,
2715     "%s/%s", hc->expnfilename, nameptrs[i] );
2716     if ( strcmp( hc->origfilename, "." ) == 0 )
2717     (void) my_snprintf( rname, maxrname,
2718     "%s", nameptrs[i] );
2719     else
2720     (void) my_snprintf( rname, maxrname,
2721     "%s%s", hc->origfilename, nameptrs[i] );
2722     }
2723     httpd_realloc_str(
2724     &encrname, &maxencrname, 3 * strlen( rname ) + 1 );
2725     strencode( encrname, maxencrname, rname );
2726    
2727     if ( stat( name, &sb ) < 0 || lstat( name, &lsb ) < 0 )
2728     continue;
2729    
2730     linkprefix = "";
2731     link[0] = '\0';
2732     /* Break down mode word. First the file type. */
2733     switch ( lsb.st_mode & S_IFMT )
2734     {
2735     case S_IFIFO: modestr[0] = 'p'; break;
2736     case S_IFCHR: modestr[0] = 'c'; break;
2737     case S_IFDIR: modestr[0] = 'd'; break;
2738     case S_IFBLK: modestr[0] = 'b'; break;
2739     case S_IFREG: modestr[0] = '-'; break;
2740     case S_IFSOCK: modestr[0] = 's'; break;
2741     case S_IFLNK: modestr[0] = 'l';
2742     linklen = readlink( name, link, sizeof(link) );
2743     if ( linklen != -1 )
2744     {
2745     link[linklen] = '\0';
2746     linkprefix = " -> ";
2747     }
2748     break;
2749     default: modestr[0] = '?'; break;
2750     }
2751     /* Now the world permissions. Owner and group permissions
2752     ** are not of interest to web clients.
2753     */
2754     modestr[1] = ( lsb.st_mode & S_IROTH ) ? 'r' : '-';
2755     modestr[2] = ( lsb.st_mode & S_IWOTH ) ? 'w' : '-';
2756     modestr[3] = ( lsb.st_mode & S_IXOTH ) ? 'x' : '-';
2757     modestr[4] = '\0';
2758    
2759     /* We also leave out the owner and group name, they are
2760     ** also not of interest to web clients. Plus if we're
2761     ** running under chroot(), they would require a copy
2762     ** of /etc/passwd and /etc/group, which we want to avoid.
2763     */
2764    
2765     /* Get time string. */
2766     now = time( (time_t*) 0 );
2767     timestr = ctime( &lsb.st_mtime );
2768     timestr[ 0] = timestr[ 4];
2769     timestr[ 1] = timestr[ 5];
2770     timestr[ 2] = timestr[ 6];
2771     timestr[ 3] = ' ';
2772     timestr[ 4] = timestr[ 8];
2773     timestr[ 5] = timestr[ 9];
2774     timestr[ 6] = ' ';
2775     if ( now - lsb.st_mtime > 60*60*24*182 ) /* 1/2 year */
2776     {
2777     timestr[ 7] = ' ';
2778     timestr[ 8] = timestr[20];
2779     timestr[ 9] = timestr[21];
2780     timestr[10] = timestr[22];
2781     timestr[11] = timestr[23];
2782     }
2783     else
2784     {
2785     timestr[ 7] = timestr[11];
2786     timestr[ 8] = timestr[12];
2787     timestr[ 9] = ':';
2788     timestr[10] = timestr[14];
2789     timestr[11] = timestr[15];
2790     }
2791     timestr[12] = '\0';
2792    
2793     /* The ls -F file class. */
2794     switch ( sb.st_mode & S_IFMT )
2795     {
2796     case S_IFDIR: fileclass = "/"; break;
2797     case S_IFSOCK: fileclass = "="; break;
2798     case S_IFLNK: fileclass = "@"; break;
2799     default:
2800     fileclass = ( sb.st_mode & S_IXOTH ) ? "*" : "";
2801     break;
2802     }
2803    
2804     /* And print. */
2805     (void) fprintf( fp,
2806     "%s %3ld %8ld %s <A HREF=\"/%.500s%s\">%.80s</A>%s%s%s\n",
2807     modestr, (long) lsb.st_nlink, (long) lsb.st_size, timestr,
2808     encrname, S_ISDIR(sb.st_mode) ? "/" : "",
2809     nameptrs[i], linkprefix, link, fileclass );
2810     }
2811    
2812     (void) fprintf( fp, "</PRE></BODY></HTML>\n" );
2813     (void) fclose( fp );
2814     exit( 0 );
2815     }
2816    
2817     /* Parent process. */
2818     closedir( dirp );
2819 root 1.4 /*syslog( LOG_INFO, "spawned indexing process %d for directory '%.200s'", r, hc->expnfilename );*/
2820 root 1.1 #ifdef CGI_TIMELIMIT
2821     /* Schedule a kill for the child process, in case it runs too long */
2822     client_data.i = r;
2823     if ( tmr_create( (struct timeval*) 0, cgi_kill, client_data, CGI_TIMELIMIT * 1000L, 0 ) == (Timer*) 0 )
2824     {
2825     syslog( LOG_CRIT, "tmr_create(cgi_kill) failed" );
2826     exit( 1 );
2827     }
2828     #endif /* CGI_TIMELIMIT */
2829     hc->status = 200;
2830     hc->bytes_sent = CGI_BYTECOUNT;
2831     hc->should_linger = 0;
2832     }
2833     else
2834     {
2835     httpd_send_err(
2836     hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) );
2837     return -1;
2838     }
2839    
2840     return 0;
2841     }
2842    
2843     #endif /* GENERATE_INDEXES */
2844    
2845    
2846     static char*
2847     build_env( char* fmt, char* arg )
2848     {
2849     char* cp;
2850     int size;
2851     static char* buf;
2852     static int maxbuf = 0;
2853    
2854     size = strlen( fmt ) + strlen( arg );
2855     if ( size > maxbuf )
2856     httpd_realloc_str( &buf, &maxbuf, size );
2857     (void) my_snprintf( buf, maxbuf,
2858     fmt, arg );
2859     cp = strdup( buf );
2860     if ( cp == (char*) 0 )
2861     {
2862     syslog( LOG_ERR, "out of memory copying environment variable" );
2863     exit( 1 );
2864     }
2865     return cp;
2866     }
2867    
2868    
2869     #ifdef SERVER_NAME_LIST
2870     static char*
2871     hostname_map( char* hostname )
2872     {
2873     int len, n;
2874     static char* list[] = { SERVER_NAME_LIST };
2875    
2876     len = strlen( hostname );
2877     for ( n = sizeof(list) / sizeof(*list) - 1; n >= 0; --n )
2878     if ( strncasecmp( hostname, list[n], len ) == 0 )
2879     if ( list[n][len] == '/' ) /* check in case of a substring match */
2880     return &list[n][len + 1];
2881     return (char*) 0;
2882     }
2883     #endif /* SERVER_NAME_LIST */
2884    
2885    
2886     /* Set up environment variables. Be real careful here to avoid
2887     ** letting malicious clients overrun a buffer. We don't have
2888     ** to worry about freeing stuff since we're a sub-process.
2889     */
2890     static char**
2891     make_envp( httpd_conn* hc )
2892     {
2893     static char* envp[50];
2894     int envn;
2895     char* cp;
2896     char buf[256];
2897    
2898     envn = 0;
2899     envp[envn++] = build_env( "PATH=%s", CGI_PATH );
2900     #ifdef CGI_LD_LIBRARY_PATH
2901     envp[envn++] = build_env( "LD_LIBRARY_PATH=%s", CGI_LD_LIBRARY_PATH );
2902     #endif /* CGI_LD_LIBRARY_PATH */
2903     envp[envn++] = build_env( "SERVER_SOFTWARE=%s", SERVER_SOFTWARE );
2904     /* If vhosting, use that server-name here. */
2905     if ( hc->hs->vhost && hc->hostname != (char*) 0 )
2906     cp = hc->hostname;
2907     else
2908     cp = hc->hs->server_hostname;
2909     if ( cp != (char*) 0 )
2910     envp[envn++] = build_env( "SERVER_NAME=%s", cp );
2911     envp[envn++] = "GATEWAY_INTERFACE=CGI/1.1";
2912     envp[envn++] = build_env("SERVER_PROTOCOL=%s", hc->protocol);
2913     (void) my_snprintf( buf, sizeof(buf),
2914     "%d", hc->hs->port );
2915     envp[envn++] = build_env( "SERVER_PORT=%s", buf );
2916     envp[envn++] = build_env(
2917     "REQUEST_METHOD=%s", httpd_method_str( hc->method ) );
2918     if ( hc->pathinfo[0] != '\0' )
2919     {
2920     char* cp2;
2921     int l;
2922     envp[envn++] = build_env( "PATH_INFO=/%s", hc->pathinfo );
2923     l = strlen( hc->hs->cwd ) + strlen( hc->pathinfo ) + 1;
2924     cp2 = NEW( char, l );
2925     if ( cp2 != (char*) 0 )
2926     {
2927     (void) my_snprintf( cp2, l,
2928     "%s%s", hc->hs->cwd, hc->pathinfo );
2929     envp[envn++] = build_env( "PATH_TRANSLATED=%s", cp2 );
2930     }
2931     }
2932     envp[envn++] = build_env(
2933     "SCRIPT_NAME=/%s", strcmp( hc->origfilename, "." ) == 0 ?
2934     "" : hc->origfilename );
2935     if ( hc->query[0] != '\0')
2936     envp[envn++] = build_env( "QUERY_STRING=%s", hc->query );
2937     envp[envn++] = build_env(
2938     "REMOTE_ADDR=%s", httpd_ntoa( &hc->client_addr ) );
2939     if ( hc->referer[0] != '\0' )
2940     envp[envn++] = build_env( "HTTP_REFERER=%s", hc->referer );
2941     if ( hc->useragent[0] != '\0' )
2942     envp[envn++] = build_env( "HTTP_USER_AGENT=%s", hc->useragent );
2943     if ( hc->accept[0] != '\0' )
2944     envp[envn++] = build_env( "HTTP_ACCEPT=%s", hc->accept );
2945     if ( hc->accepte[0] != '\0' )
2946     envp[envn++] = build_env( "HTTP_ACCEPT_ENCODING=%s", hc->accepte );
2947     if ( hc->acceptl[0] != '\0' )
2948     envp[envn++] = build_env( "HTTP_ACCEPT_LANGUAGE=%s", hc->acceptl );
2949     if ( hc->cookie[0] != '\0' )
2950     envp[envn++] = build_env( "HTTP_COOKIE=%s", hc->cookie );
2951     if ( hc->contenttype[0] != '\0' )
2952     envp[envn++] = build_env( "CONTENT_TYPE=%s", hc->contenttype );
2953     if ( hc->hdrhost[0] != '\0' )
2954     envp[envn++] = build_env( "HTTP_HOST=%s", hc->hdrhost );
2955     if ( hc->contentlength != -1 )
2956     {
2957     (void) my_snprintf( buf, sizeof(buf),
2958     "%ld", (long) hc->contentlength );
2959     envp[envn++] = build_env( "CONTENT_LENGTH=%s", buf );
2960     }
2961     if ( hc->remoteuser[0] != '\0' )
2962     envp[envn++] = build_env( "REMOTE_USER=%s", hc->remoteuser );
2963     if ( hc->authorization[0] == '\0' )
2964     envp[envn++] = build_env( "AUTH_TYPE=%s", "Basic" );
2965     /* We only support Basic auth at the moment. */
2966     if ( getenv( "TZ" ) != (char*) 0 )
2967     envp[envn++] = build_env( "TZ=%s", getenv( "TZ" ) );
2968     envp[envn++] = build_env( "CGI_PATTERN=%s", hc->hs->cgi_pattern );
2969    
2970     envp[envn] = (char*) 0;
2971     return envp;
2972     }
2973    
2974    
2975     /* Set up argument vector. Again, we don't have to worry about freeing stuff
2976     ** since we're a sub-process. This gets done after make_envp() because we
2977     ** scribble on hc->query.
2978     */
2979     static char**
2980     make_argp( httpd_conn* hc )
2981     {
2982     char** argp;
2983     int argn;
2984     char* cp1;
2985     char* cp2;
2986    
2987     /* By allocating an arg slot for every character in the query, plus
2988     ** one for the filename and one for the NULL, we are guaranteed to
2989     ** have enough. We could actually use strlen/2.
2990     */
2991     argp = NEW( char*, strlen( hc->query ) + 2 );
2992     if ( argp == (char**) 0 )
2993     return (char**) 0;
2994    
2995     argp[0] = strrchr( hc->expnfilename, '/' );
2996     if ( argp[0] != (char*) 0 )
2997     ++argp[0];
2998     else
2999     argp[0] = hc->expnfilename;
3000    
3001     argn = 1;
3002     /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html,
3003     ** "The server should search the query information for a non-encoded =
3004     ** character to determine if the command line is to be used, if it finds
3005     ** one, the command line is not to be used."
3006     */
3007     if ( strchr( hc->query, '=' ) == (char*) 0 )
3008     {
3009     for ( cp1 = cp2 = hc->query; *cp2 != '\0'; ++cp2 )
3010     {
3011     if ( *cp2 == '+' )
3012     {
3013     *cp2 = '\0';
3014     strdecode( cp1, cp1 );
3015     argp[argn++] = cp1;
3016     cp1 = cp2 + 1;
3017     }
3018     }
3019     if ( cp2 != cp1 )
3020     {
3021     strdecode( cp1, cp1 );
3022     argp[argn++] = cp1;
3023     }
3024     }
3025    
3026     argp[argn] = (char*) 0;
3027     return argp;
3028     }
3029    
3030    
3031     /* This routine is used only for POST requests. It reads the data
3032     ** from the request and sends it to the child process. The only reason
3033     ** we need to do it this way instead of just letting the child read
3034     ** directly is that we have already read part of the data into our
3035     ** buffer.
3036     */
3037     static void
3038     cgi_interpose_input( httpd_conn* hc, int wfd )
3039     {
3040     int c, r;
3041     char buf[1024];
3042    
3043     c = hc->read_idx - hc->checked_idx;
3044     if ( c > 0 )
3045     {
3046     if ( write( wfd, &(hc->read_buf[hc->checked_idx]), c ) != c )
3047     return;
3048     }
3049     while ( c < hc->contentlength )
3050     {
3051     r = read( hc->conn_fd, buf, MIN( sizeof(buf), hc->contentlength - c ) );
3052     if ( r == 0 )
3053     sleep( 1 );
3054     else if ( r < 0 )
3055     {
3056     if ( errno == EAGAIN )
3057     sleep( 1 );
3058     else
3059     return;
3060     }
3061     else
3062     {
3063     if ( write( wfd, buf, r ) != r )
3064     return;
3065     c += r;
3066     }
3067     }
3068     post_post_garbage_hack( hc );
3069     }
3070    
3071    
3072     /* Special hack to deal with broken browsers that send a LF or CRLF
3073     ** after POST data, causing TCP resets - we just read and discard up
3074     ** to 2 bytes. Unfortunately this doesn't fix the problem for CGIs
3075     ** which avoid the interposer process due to their POST data being
3076     ** short. Creating an interposer process for all POST CGIs is
3077     ** unacceptably expensive. The eventual fix will come when interposing
3078     ** gets integrated into the main loop as a tasklet instead of a process.
3079     */
3080     static void
3081     post_post_garbage_hack( httpd_conn* hc )
3082     {
3083     char buf[2];
3084     int r;
3085    
3086     r = recv( hc->conn_fd, buf, sizeof(buf), MSG_PEEK );
3087     if ( r > 0 )
3088     (void) read( hc->conn_fd, buf, r );
3089     }
3090    
3091    
3092     /* This routine is used for parsed-header CGIs. The idea here is that the
3093     ** CGI can return special headers such as "Status:" and "Location:" which
3094     ** change the return status of the response. Since the return status has to
3095     ** be the very first line written out, we have to accumulate all the headers
3096     ** and check for the special ones before writing the status. Then we write
3097     ** out the saved headers and proceed to echo the rest of the response.
3098     */
3099     static void
3100     cgi_interpose_output( httpd_conn* hc, int rfd )
3101     {
3102     int r;
3103     char buf[1024];
3104     int headers_size, headers_len;
3105     char* headers;
3106     char* br;
3107     int status;
3108     char* title;
3109     char* cp;
3110    
3111     /* Slurp in all headers. */
3112     headers_size = 0;
3113     httpd_realloc_str( &headers, &headers_size, 500 );
3114     headers_len = 0;
3115     for (;;)
3116     {
3117     r = read( rfd, buf, sizeof(buf) );
3118     if ( r <= 0 )
3119     {
3120     br = &(headers[headers_len]);
3121     break;
3122     }
3123     httpd_realloc_str( &headers, &headers_size, headers_len + r );
3124     (void) memcpy( &(headers[headers_len]), buf, r );
3125     headers_len += r;
3126     headers[headers_len] = '\0';
3127     if ( ( br = strstr( headers, "\r\n\r\n" ) ) != (char*) 0 ||
3128     ( br = strstr( headers, "\n\n" ) ) != (char*) 0 )
3129     break;
3130     }
3131    
3132     /* Figure out the status. */
3133     status = 200;
3134     if ( ( cp = strstr( headers, "Status:" ) ) != (char*) 0 &&
3135     cp < br &&
3136     ( cp == headers || *(cp-1) == '\n' ) )
3137     {
3138     cp += 7;
3139     cp += strspn( cp, " \t" );
3140     status = atoi( cp );
3141     }
3142     if ( ( cp = strstr( headers, "Location:" ) ) != (char*) 0 &&
3143     cp < br &&
3144     ( cp == headers || *(cp-1) == '\n' ) )
3145     status = 302;
3146    
3147     /* Write the status line. */
3148     switch ( status )
3149     {
3150     case 200: title = ok200title; break;
3151     case 302: title = err302title; break;
3152     case 304: title = err304title; break;
3153     case 400: title = httpd_err400title; break;
3154     #ifdef AUTH_FILE
3155     case 401: title = err401title; break;
3156     #endif /* AUTH_FILE */
3157     case 403: title = err403title; break;
3158     case 404: title = err404title; break;
3159     case 408: title = httpd_err408title; break;
3160     case 500: title = err500title; break;
3161     case 501: title = err501title; break;
3162     case 503: title = httpd_err503title; break;
3163     default: title = "Something"; break;
3164     }
3165     (void) my_snprintf( buf, sizeof(buf), "HTTP/1.0 %d %s\r\n", status, title );
3166     (void) write( hc->conn_fd, buf, strlen( buf ) );
3167    
3168     /* Write the saved headers. */
3169     (void) write( hc->conn_fd, headers, headers_len );
3170    
3171     /* Echo the rest of the output. */
3172     for (;;)
3173     {
3174     r = read( rfd, buf, sizeof(buf) );
3175     if ( r <= 0 )
3176     return;
3177     if ( write( hc->conn_fd, buf, r ) != r )
3178     return;
3179     }
3180     }
3181    
3182    
3183     /* CGI child process. */
3184     static void
3185 root 1.2 cgi_child( httpd_conn* hc, char* exefilename )
3186 root 1.1 {
3187     int r;
3188     char** argp;
3189     char** envp;
3190     char* binary;
3191     char* directory;
3192    
3193     /* Unset close-on-exec flag for this socket. This actually shouldn't
3194     ** be necessary, according to POSIX a dup()'d file descriptor does
3195     ** *not* inherit the close-on-exec flag, its flag is always clear.
3196     ** However, Linux messes this up and does copy the flag to the
3197     ** dup()'d descriptor, so we have to clear it. This could be
3198     ** ifdeffed for Linux only.
3199     */
3200     (void) fcntl( hc->conn_fd, F_SETFD, 0 );
3201    
3202     /* Close the syslog descriptor so that the CGI program can't
3203     ** mess with it. All other open descriptors should be either
3204     ** the listen socket(s), sockets from accept(), or the file-logging
3205     ** fd, and all of those are set to close-on-exec, so we don't
3206     ** have to close anything else.
3207     */
3208     closelog();
3209    
3210     /* If the socket happens to be using one of the stdin/stdout/stderr
3211     ** descriptors, move it to another descriptor so that the dup2 calls
3212     ** below don't screw things up.
3213     */
3214     if ( hc->conn_fd == STDIN_FILENO || hc->conn_fd == STDOUT_FILENO || hc->conn_fd == STDERR_FILENO )
3215     {
3216     int newfd = dup( hc->conn_fd );
3217     if ( newfd >= 0 )
3218     hc->conn_fd = newfd;
3219     /* If the dup fails, shrug. We'll just take our chances.
3220     ** Shouldn't happen though.
3221     **
3222     ** If the dup happens to produce an fd that is still one of
3223     ** the standard ones, we should be ok - I think it can be
3224     ** fd 2, stderr, but can never show up as 0 or 1 since at
3225     ** least two file descriptors are always in use. Because
3226     ** of the order in which we dup2 things below - stderr is
3227     ** always done last - it's actually ok for the socket to
3228     ** be fd 2. It'll just get dup2'd onto itself.
3229     */
3230     }
3231    
3232     /* Make the environment vector. */
3233     envp = make_envp( hc );
3234    
3235     /* Make the argument vector. */
3236     argp = make_argp( hc );
3237    
3238     /* Set up stdin. For POSTs we may have to set up a pipe from an
3239     ** interposer process, depending on if we've read some of the data
3240     ** into our buffer.
3241     */
3242     if ( hc->method == METHOD_POST && hc->read_idx > hc->checked_idx )
3243     {
3244     int p[2];
3245    
3246     if ( pipe( p ) < 0 )
3247     {
3248     syslog( LOG_ERR, "pipe - %m" );
3249     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3250     exit( 1 );
3251     }
3252     r = fork( );
3253     if ( r < 0 )
3254     {
3255     syslog( LOG_ERR, "fork - %m" );
3256     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3257     exit( 1 );
3258     }
3259     if ( r == 0 )
3260     {
3261     /* Interposer process. */
3262     (void) close( p[0] );
3263     cgi_interpose_input( hc, p[1] );
3264     exit( 0 );
3265     }
3266     (void) close( p[1] );
3267     (void) dup2( p[0], STDIN_FILENO );
3268     (void) close( p[0] );
3269     }
3270     else
3271     {
3272     /* Otherwise, the request socket is stdin. */
3273     (void) dup2( hc->conn_fd, STDIN_FILENO );
3274     }
3275    
3276     /* Set up stdout/stderr. If we're doing CGI header parsing,
3277     ** we need an output interposer too.
3278     */
3279     if ( strncmp( argp[0], "nph-", 4 ) != 0 && hc->mime_flag )
3280     {
3281     int p[2];
3282    
3283     if ( pipe( p ) < 0 )
3284     {
3285     syslog( LOG_ERR, "pipe - %m" );
3286     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3287     exit( 1 );
3288     }
3289     r = fork( );
3290     if ( r < 0 )
3291     {
3292     syslog( LOG_ERR, "fork - %m" );
3293     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3294     exit( 1 );
3295     }
3296     if ( r == 0 )
3297     {
3298     /* Interposer process. */
3299     (void) close( p[1] );
3300     cgi_interpose_output( hc, p[0] );
3301     exit( 0 );
3302     }
3303     (void) close( p[0] );
3304     (void) dup2( p[1], STDOUT_FILENO );
3305     (void) dup2( p[1], STDERR_FILENO );
3306     (void) close( p[1] );
3307     }
3308     else
3309     {
3310     /* Otherwise, the request socket is stdout/stderr. */
3311     (void) dup2( hc->conn_fd, STDOUT_FILENO );
3312     (void) dup2( hc->conn_fd, STDERR_FILENO );
3313     }
3314    
3315     /* At this point we would like to set close-on-exec again for hc->conn_fd
3316     ** (see previous comments on Linux's broken behavior re: close-on-exec
3317     ** and dup.) Unfortunately there seems to be another Linux problem, or
3318     ** perhaps a different aspect of the same problem - if we do this
3319     ** close-on-exec in Linux, the socket stays open but stderr gets
3320     ** closed - the last fd duped from the socket. What a mess. So we'll
3321     ** just leave the socket as is, which under other OSs means an extra
3322     ** file descriptor gets passed to the child process. Since the child
3323     ** probably already has that file open via stdin stdout and/or stderr,
3324     ** this is not a problem.
3325     */
3326     /* (void) fcntl( hc->conn_fd, F_SETFD, 1 ); */
3327    
3328     #ifdef CGI_NICE
3329     /* Set priority. */
3330     (void) nice( CGI_NICE );
3331     #endif /* CGI_NICE */
3332    
3333     /* Split the program into directory and binary, so we can chdir()
3334     ** to the program's own directory. This isn't in the CGI 1.1
3335     ** spec, but it's what other HTTP servers do.
3336     */
3337 root 1.2 if (exefilename)
3338     binary = exefilename;
3339 root 1.1 else
3340 root 1.2 {
3341     directory = strdup( exefilename ? exefilename : hc->expnfilename );
3342     if ( directory == (char*) 0 )
3343     binary = exefilename; /* ignore errors */
3344     else
3345     {
3346     binary = strrchr( directory, '/' );
3347     if ( binary == (char*) 0 )
3348     binary = exefilename;
3349     else
3350     {
3351     *binary++ = '\0';
3352     (void) chdir( directory ); /* ignore errors */
3353     }
3354     }
3355     }
3356 root 1.1
3357     /* Default behavior for SIGPIPE. */
3358     (void) signal( SIGPIPE, SIG_DFL );
3359    
3360     /* Run the program. */
3361     (void) execve( binary, argp, envp );
3362    
3363     /* Something went wrong. */
3364     syslog( LOG_ERR, "execve %.80s - %m", hc->expnfilename );
3365     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3366     exit( 1 );
3367     }
3368    
3369    
3370     static off_t
3371 root 1.2 cgi( httpd_conn* hc, char* exefilename )
3372 root 1.1 {
3373     int r;
3374     ClientData client_data;
3375    
3376     if ( hc->method == METHOD_GET || hc->method == METHOD_POST )
3377     {
3378     httpd_clear_ndelay( hc->conn_fd );
3379     r = fork( );
3380     if ( r < 0 )
3381     {
3382     syslog( LOG_ERR, "fork - %m" );
3383     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3384     return -1;
3385     }
3386     if ( r == 0 )
3387     {
3388     unlisten( hc->hs );
3389 root 1.2 cgi_child( hc, exefilename );
3390 root 1.1 }
3391    
3392     /* Parent process. */
3393 root 1.4 /*syslog( LOG_INFO, "spawned CGI process %d for file '%.200s'", r, hc->expnfilename );*/
3394 root 1.1 #ifdef CGI_TIMELIMIT
3395     /* Schedule a kill for the child process, in case it runs too long */
3396     client_data.i = r;
3397     if ( tmr_create( (struct timeval*) 0, cgi_kill, client_data, CGI_TIMELIMIT * 1000L, 0 ) == (Timer*) 0 )
3398     {
3399     syslog( LOG_CRIT, "tmr_create(cgi_kill) failed" );
3400     exit( 1 );
3401     }
3402     #endif /* CGI_TIMELIMIT */
3403     hc->status = 200;
3404     hc->bytes_sent = CGI_BYTECOUNT;
3405     hc->should_linger = 0;
3406     }
3407     else
3408     {
3409     httpd_send_err(
3410     hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) );
3411     return -1;
3412     }
3413    
3414     return 0;
3415     }
3416    
3417    
3418     static int
3419     really_start_request( httpd_conn* hc, struct timeval* nowP )
3420     {
3421     static char* indexname;
3422     static int maxindexname = 0;
3423     static const char* index_names[] = { INDEX_NAMES };
3424     int i;
3425     #ifdef AUTH_FILE
3426     static char* dirname;
3427     static int maxdirname = 0;
3428     #endif /* AUTH_FILE */
3429     int expnlen, indxlen;
3430     char* cp;
3431     char* pi;
3432    
3433     expnlen = strlen( hc->expnfilename );
3434    
3435     if ( hc->method != METHOD_GET && hc->method != METHOD_HEAD &&
3436     hc->method != METHOD_POST )
3437     {
3438     httpd_send_err(
3439     hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) );
3440     return -1;
3441     }
3442    
3443     /* Stat the file. */
3444     if ( stat( hc->expnfilename, &hc->sb ) < 0 )
3445     {
3446     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3447     return -1;
3448     }
3449    
3450     /* Is it world-readable or world-executable? We check explicitly instead
3451     ** of just trying to open it, so that no one ever gets surprised by
3452     ** a file that's not set world-readable and yet somehow is
3453     ** readable by the HTTP server and therefore the *whole* world.
3454     */
3455     if ( ! ( hc->sb.st_mode & ( S_IROTH | S_IXOTH ) ) )
3456     {
3457     syslog(
3458     LOG_INFO,
3459     "%.80s URL \"%.80s\" resolves to a non world-readable file",
3460     httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3461     httpd_send_err(
3462     hc, 403, err403title, "",
3463     ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file that is not world-readable.\n" ),
3464     hc->encodedurl );
3465     return -1;
3466     }
3467    
3468     /* Is it a directory? */
3469     if ( S_ISDIR(hc->sb.st_mode) )
3470     {
3471     /* If there's pathinfo, it's just a non-existent file. */
3472     if ( hc->pathinfo[0] != '\0' )
3473     {
3474     httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
3475     return -1;
3476     }
3477    
3478     /* Special handling for directory URLs that don't end in a slash.
3479     ** We send back an explicit redirect with the slash, because
3480     ** otherwise many clients can't build relative URLs properly.
3481     */
3482     if ( hc->decodedurl[strlen( hc->decodedurl ) - 1] != '/' )
3483     {
3484     send_dirredirect( hc );
3485     return -1;
3486     }
3487    
3488     /* Check for an index file. */
3489     for ( i = 0; i < sizeof(index_names) / sizeof(char*); ++i )
3490     {
3491     httpd_realloc_str(
3492     &indexname, &maxindexname,
3493     expnlen + 1 + strlen( index_names[i] ) );
3494     (void) strcpy( indexname, hc->expnfilename );
3495     indxlen = strlen( indexname );
3496     if ( indxlen == 0 || indexname[indxlen - 1] != '/' )
3497     (void) strcat( indexname, "/" );
3498     if ( strcmp( indexname, "./" ) == 0 )
3499     indexname[0] = '\0';
3500     (void) strcat( indexname, index_names[i] );
3501     if ( stat( indexname, &hc->sb ) >= 0 )
3502     goto got_one;
3503     }
3504    
3505     /* Nope, no index file, so it's an actual directory request. */
3506     #ifdef GENERATE_INDEXES
3507     /* Directories must be readable for indexing. */
3508     if ( ! ( hc->sb.st_mode & S_IROTH ) )
3509     {
3510     syslog(
3511     LOG_INFO,
3512     "%.80s URL \"%.80s\" tried to index a directory with indexing disabled",
3513     httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3514     httpd_send_err(
3515     hc, 403, err403title, "",
3516     ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a directory that has indexing disabled.\n" ),
3517     hc->encodedurl );
3518     return -1;
3519     }
3520     #ifdef AUTH_FILE
3521     /* Check authorization for this directory. */
3522     if ( auth_check( hc, hc->expnfilename ) == -1 )
3523     return -1;
3524     #endif /* AUTH_FILE */
3525     /* Referer check. */
3526     if ( ! check_referer( hc ) )
3527     return -1;
3528     /* Ok, generate an index. */
3529 root 1.2 return hc->hs->autoindex_prog ? cgi( hc, hc->hs->autoindex_prog) : ls( hc );
3530 root 1.1 #else /* GENERATE_INDEXES */
3531     syslog(
3532     LOG_INFO, "%.80s URL \"%.80s\" tried to index a directory",
3533     httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3534     httpd_send_err(
3535     hc, 403, err403title, "",
3536     ERROR_FORM( err403form, "The requested URL '%.80s' is a directory, and directory indexing is disabled on this server.\n" ),
3537     hc->encodedurl );
3538     return -1;
3539     #endif /* GENERATE_INDEXES */
3540    
3541     got_one: ;
3542     /* Got an index file. Expand symlinks again. More pathinfo means
3543     ** something went wrong.
3544     */
3545     cp = expand_symlinks( indexname, &pi, hc->hs->no_symlink, hc->tildemapped );
3546     if ( cp == (char*) 0 || pi[0] != '\0' )
3547     {
3548     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3549     return -1;
3550     }
3551     expnlen = strlen( cp );
3552     httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, expnlen );
3553     (void) strcpy( hc->expnfilename, cp );
3554    
3555     /* Now, is the index version world-readable or world-executable? */
3556     if ( ! ( hc->sb.st_mode & ( S_IROTH | S_IXOTH ) ) )
3557     {
3558     syslog(
3559     LOG_INFO,
3560     "%.80s URL \"%.80s\" resolves to a non-world-readable index file",
3561     httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3562     httpd_send_err(
3563     hc, 403, err403title, "",
3564     ERROR_FORM( err403form, "The requested URL '%.80s' resolves to an index file that is not world-readable.\n" ),
3565     hc->encodedurl );
3566     return -1;
3567     }
3568     }
3569    
3570     #ifdef AUTH_FILE
3571     /* Check authorization for this directory. */
3572     httpd_realloc_str( &dirname, &maxdirname, expnlen );
3573     (void) strcpy( dirname, hc->expnfilename );
3574     cp = strrchr( dirname, '/' );
3575     if ( cp == (char*) 0 )
3576     (void) strcpy( dirname, "." );
3577     else
3578     *cp = '\0';
3579     if ( auth_check( hc, dirname ) == -1 )
3580     return -1;
3581    
3582     /* Check if the filename is the AUTH_FILE itself - that's verboten. */
3583     if ( expnlen == sizeof(AUTH_FILE) - 1 )
3584     {
3585     if ( strcmp( hc->expnfilename, AUTH_FILE ) == 0 )
3586     {
3587     syslog(
3588     LOG_NOTICE,
3589     "%.80s URL \"%.80s\" tried to retrieve an auth file",
3590     httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3591     httpd_send_err(
3592     hc, 403, err403title, "",
3593     ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ),
3594     hc->encodedurl );
3595     return -1;
3596     }
3597     }
3598     else if ( expnlen >= sizeof(AUTH_FILE) &&
3599     strcmp( &(hc->expnfilename[expnlen - sizeof(AUTH_FILE) + 1]), AUTH_FILE ) == 0 &&
3600     hc->expnfilename[expnlen - sizeof(AUTH_FILE)] == '/' )
3601     {
3602     syslog(
3603     LOG_NOTICE,
3604     "%.80s URL \"%.80s\" tried to retrieve an auth file",
3605     httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3606     httpd_send_err(
3607     hc, 403, err403title, "",
3608     ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ),
3609     hc->encodedurl );
3610     return -1;
3611     }
3612     #endif /* AUTH_FILE */
3613    
3614     /* Referer check. */
3615     if ( ! check_referer( hc ) )
3616     return -1;
3617    
3618     /* Is it world-executable and in the CGI area? */
3619     if ( hc->hs->cgi_pattern != (char*) 0 &&
3620     ( hc->sb.st_mode & S_IXOTH ) &&
3621     match( hc->hs->cgi_pattern, hc->expnfilename ) )
3622 root 1.2 return cgi( hc, 0 );
3623 root 1.1
3624     /* It's not CGI. If it's executable or there's pathinfo, someone's
3625     ** trying to either serve or run a non-CGI file as CGI. Either case
3626     ** is prohibited.
3627     */
3628     if ( hc->sb.st_mode & S_IXOTH )
3629     {
3630     syslog(
3631     LOG_NOTICE, "%.80s URL \"%.80s\" is executable but isn't CGI",
3632     httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3633     httpd_send_err(
3634     hc, 403, err403title, "",
3635     ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file which is marked executable but is not a CGI file; retrieving it is forbidden.\n" ),
3636     hc->encodedurl );
3637     return -1;
3638     }
3639     if ( hc->pathinfo[0] != '\0' )
3640     {
3641     syslog(
3642     LOG_INFO, "%.80s URL \"%.80s\" has pathinfo but isn't CGI",
3643     httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3644     httpd_send_err(
3645     hc, 403, err403title, "",
3646     ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file plus CGI-style pathinfo, but the file is not a valid CGI file.\n" ),
3647     hc->encodedurl );
3648     return -1;
3649     }
3650    
3651     /* Fill in end_byte_loc, if necessary. */
3652     if ( hc->got_range &&
3653     ( hc->end_byte_loc == -1 || hc->end_byte_loc >= hc->sb.st_size ) )
3654     hc->end_byte_loc = hc->sb.st_size - 1;
3655    
3656     figure_mime( hc );
3657    
3658     if ( hc->method == METHOD_HEAD )
3659     {
3660     send_mime(
3661     hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,
3662     hc->sb.st_mtime );
3663     }
3664     else if ( hc->if_modified_since != (time_t) -1 &&
3665     hc->if_modified_since >= hc->sb.st_mtime )
3666     {
3667     hc->method = METHOD_HEAD;
3668     send_mime(
3669     hc, 304, err304title, hc->encodings, "", hc->type, hc->sb.st_size,
3670     hc->sb.st_mtime );
3671     }
3672     else
3673     {
3674 root 1.2 #ifdef MMAP_MAX
3675     if ( hc->sb.st_size < MMAP_MAX)
3676     #endif
3677     hc->file_address = mmc_map( hc->expnfilename, &(hc->sb), nowP );
3678 root 1.1 if ( hc->file_address == (char*) 0 )
3679     {
3680 root 1.2 #ifdef MMAP_MAX
3681     hc->file_fd = open ( hc->expnfilename, O_RDONLY);
3682     hc->write_buf = malloc (WRITE_BUFFER);
3683     hc->write_ofs = WRITE_BUFFER;
3684     if ( hc->file_fd < 0 || !hc->write_buf )
3685     #endif
3686     {
3687     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3688     return -1;
3689     }
3690 root 1.1 }
3691     send_mime(
3692     hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,
3693     hc->sb.st_mtime );
3694     }
3695    
3696     return 0;
3697     }
3698    
3699    
3700     int
3701     httpd_start_request( httpd_conn* hc, struct timeval* nowP )
3702     {
3703     int r;
3704    
3705     /* Really start the request. */
3706     r = really_start_request( hc, nowP );
3707    
3708     /* And return the status. */
3709     return r;
3710     }
3711    
3712    
3713     static void
3714     make_log_entry( httpd_conn* hc, struct timeval* nowP )
3715     {
3716     char* ru;
3717     char url[305];
3718     char bytes[40];
3719    
3720     if ( hc->hs->no_log )
3721     return;
3722 root 1.3
3723     /* don't log UNKNOWN protocol requests (blocks etc..) */
3724 root 1.4 if ( hc->method == METHOD_UNKNOWN )
3725 root 1.3 return;
3726 root 1.1
3727     /* This is straight CERN Combined Log Format - the only tweak
3728     ** being that if we're using syslog() we leave out the date, because
3729     ** syslogd puts it in. The included syslogtocern script turns the
3730     ** results into true CERN format.
3731     */
3732    
3733     /* Format remote user. */
3734     if ( hc->remoteuser[0] != '\0' )
3735     ru = hc->remoteuser;
3736     else
3737     ru = "-";
3738     /* If we're vhosting, prepend the hostname to the url. This is
3739     ** a little weird, perhaps writing separate log files for
3740     ** each vhost would make more sense.
3741     */
3742     if ( hc->hs->vhost && ! hc->tildemapped )
3743     (void) my_snprintf( url, sizeof(url),
3744     "/%.100s%.200s",
3745     hc->hostname == (char*) 0 ? hc->hs->server_hostname : hc->hostname,
3746     hc->encodedurl );
3747     else
3748     (void) my_snprintf( url, sizeof(url),
3749     "%.200s", hc->encodedurl );
3750     /* Format the bytes. */
3751     if ( (long) hc->bytes_sent >= 0 )
3752     (void) my_snprintf( bytes, sizeof(bytes),
3753     "%ld", (long) hc->bytes_sent );
3754     else
3755     (void) strcpy( bytes, "-" );
3756    
3757     /* Logfile or syslog? */
3758     if ( hc->hs->logfp != (FILE*) 0 )
3759     {
3760     time_t now;
3761     struct tm* t;
3762     const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S";
3763     char date_nozone[100];
3764     int zone;
3765     char sign;
3766     char date[100];
3767    
3768     /* Get the current time, if necessary. */
3769     if ( nowP != (struct timeval*) 0 )
3770     now = nowP->tv_sec;
3771     else
3772     now = time( (time_t*) 0 );
3773     /* Format the time, forcing a numeric timezone (some log analyzers
3774     ** are stoooopid about this).
3775     */
3776     t = localtime( &now );
3777     (void) strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t );
3778     #ifdef HAVE_TM_GMTOFF
3779     zone = t->tm_gmtoff / 60L;
3780     #else
3781     zone = -timezone / 60L;
3782     /* Probably have to add something about daylight time here. */
3783     #endif
3784     if ( zone >= 0 )
3785     sign = '+';
3786     else
3787     {
3788     sign = '-';
3789     zone = -zone;
3790     }
3791     zone = ( zone / 60 ) * 100 + zone % 60;
3792     (void) my_snprintf( date, sizeof(date),
3793     "%s %c%04d", date_nozone, sign, zone );
3794     /* And write the log entry. */
3795     (void) fprintf( hc->hs->logfp,
3796     "%.80s - %.80s [%s] \"%.80s %.300s %.80s\" %d %s \"%.200s\" \"%.80s\"\n",
3797     httpd_ntoa( &hc->client_addr ), ru, date,
3798     httpd_method_str( hc->method ), url, hc->protocol,
3799     hc->status, bytes, hc->referer, hc->useragent );
3800     (void) fflush( hc->hs->logfp ); /* don't need to flush every time */
3801     }
3802     else
3803     syslog( LOG_INFO,
3804     "%.80s - %.80s \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.80s\"",
3805     httpd_ntoa( &hc->client_addr ), ru,
3806     httpd_method_str( hc->method ), url, hc->protocol,
3807     hc->status, bytes, hc->referer, hc->useragent );
3808     }
3809    
3810    
3811     /* Returns 1 if ok to serve the url, 0 if not. */
3812     static int
3813     check_referer( httpd_conn* hc )
3814     {
3815     int r;
3816    
3817     /* Are we doing referer checking at all? */
3818     if ( hc->hs->url_pattern == (char*) 0 )
3819     return 1;
3820    
3821     r = really_check_referer( hc );
3822    
3823     if ( ! r )
3824     {
3825     syslog(
3826     LOG_INFO, "%.80s non-local referer \"%.80s\" \"%.80s\"",
3827     httpd_ntoa( &hc->client_addr ), hc->encodedurl,
3828     hc->referer );
3829     httpd_send_err(
3830     hc, 403, err403title, "",
3831     ERROR_FORM( err403form, "You must supply a local referer to get URL '%.80s' from this server.\n" ),
3832     hc->encodedurl );
3833     }
3834     return r;
3835     }
3836    
3837    
3838     /* Returns 1 if ok to serve the url, 0 if not. */
3839     static int
3840     really_check_referer( httpd_conn* hc )
3841     {
3842     httpd_server* hs;
3843     char* cp1;
3844     char* cp2;
3845     char* cp3;
3846     static char* refhost = (char*) 0;
3847     static int refhost_size = 0;
3848     char *lp;
3849    
3850     hs = hc->hs;
3851    
3852     /* Check for an empty referer. */
3853     if ( hc->referer == (char*) 0 || hc->referer[0] == '\0' ||
3854     ( cp1 = strstr( hc->referer, "//" ) ) == (char*) 0 )
3855     {
3856     /* Disallow if we require a referer and the url matches. */
3857     if ( hs->no_empty_referers && match( hs->url_pattern, hc->decodedurl ) )
3858     return 0;
3859     /* Otherwise ok. */
3860     return 1;
3861     }
3862    
3863     /* Extract referer host. */
3864     cp1 += 2;
3865     for ( cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2 )
3866     continue;
3867     httpd_realloc_str( &refhost, &refhost_size, cp2 - cp1 );
3868     for ( cp3 = refhost; cp1 < cp2; ++cp1, ++cp3 )
3869     if ( isupper(*cp1) )
3870     *cp3 = tolower(*cp1);
3871     else
3872     *cp3 = *cp1;
3873     *cp3 = '\0';
3874    
3875     /* Local pattern? */
3876     if ( hs->local_pattern != (char*) 0 )
3877     lp = hs->local_pattern;
3878     else
3879     {
3880     /* No local pattern. What's our hostname? */
3881     if ( ! hs->vhost )
3882     {
3883     /* Not vhosting, use the server name. */
3884     lp = hs->server_hostname;
3885     if ( lp == (char*) 0 )
3886     /* Couldn't figure out local hostname - give up. */
3887     return 1;
3888     }
3889     else
3890     {
3891     /* We are vhosting, use the hostname on this connection. */
3892     lp = hc->hostname;
3893     if ( lp == (char*) 0 )
3894     /* Oops, no hostname. Maybe it's an old browser that
3895     ** doesn't send a Host: header. We could figure out
3896     ** the default hostname for this IP address, but it's
3897     ** not worth it for the few requests like this.
3898     */
3899     return 1;
3900     }
3901     }
3902    
3903     /* If the referer host doesn't match the local host pattern, and
3904     ** the URL does match the url pattern, it's an illegal reference.
3905     */
3906     if ( ! match( lp, refhost ) && match( hs->url_pattern, hc->decodedurl ) )
3907     return 0;
3908     /* Otherwise ok. */
3909     return 1;
3910     }
3911    
3912    
3913     char*
3914     httpd_ntoa( httpd_sockaddr* saP )
3915     {
3916     #ifdef HAVE_GETNAMEINFO
3917     static char str[200];
3918    
3919     if ( getnameinfo( &saP->sa, sockaddr_len( saP ), str, sizeof(str), 0, 0, NI_NUMERICHOST ) != 0 )
3920     {
3921     str[0] = '?';
3922     str[1] = '\0';
3923     }
3924     return str;
3925    
3926     #else /* HAVE_GETNAMEINFO */
3927    
3928     return inet_ntoa( saP->sa_in.sin_addr );
3929    
3930     #endif /* HAVE_GETNAMEINFO */
3931     }
3932    
3933    
3934     static int
3935     sockaddr_check( httpd_sockaddr* saP )
3936     {
3937     switch ( saP->sa.sa_family )
3938     {
3939     case AF_INET: return 1;
3940     #if defined(AF_INET6) && defined(HAVE_SOCKADDR_IN6)
3941     case AF_INET6: return 1;
3942     #endif /* AF_INET6 && HAVE_SOCKADDR_IN6 */
3943     default:
3944     return 0;
3945     }
3946     }
3947    
3948    
3949     static size_t
3950     sockaddr_len( httpd_sockaddr* saP )
3951     {
3952     switch ( saP->sa.sa_family )
3953     {
3954     case AF_INET: return sizeof(struct sockaddr_in);
3955     #if defined(AF_INET6) && defined(HAVE_SOCKADDR_IN6)
3956     case AF_INET6: return sizeof(struct sockaddr_in6);
3957     #endif /* AF_INET6 && HAVE_SOCKADDR_IN6 */
3958     default:
3959     return 0; /* shouldn't happen */
3960     }
3961     }
3962    
3963    
3964     /* Some systems don't have snprintf(), so we make our own that uses
3965     ** either vsnprintf() or vsprintf(). If your system doesn't have
3966     ** vsnprintf(), it is probably vulnerable to buffer overruns.
3967     ** Upgrade!
3968     */
3969     static int
3970     my_snprintf( char* str, size_t size, const char* format, ... )
3971     {
3972     va_list ap;
3973     int r;
3974    
3975     va_start( ap, format );
3976     #ifdef HAVE_VSNPRINTF
3977     r = vsnprintf( str, size, format, ap );
3978     #else /* HAVE_VSNPRINTF */
3979     r = vsprintf( str, format, ap );
3980     #endif /* HAVE_VSNPRINTF */
3981     va_end( ap );
3982     return r;
3983     }
3984    
3985    
3986     /* Generate debugging statistics syslog message. */
3987     void
3988     httpd_logstats( long secs )
3989     {
3990     syslog( LOG_NOTICE,
3991     " libhttpd - %d strings allocated, %ld bytes (%g bytes/str)",
3992     str_alloc_count, str_alloc_size,
3993     (float) str_alloc_size / str_alloc_count );
3994     }