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