1 /* mini_httpd - small HTTP server
2 **
3 ** Copyright � 1999,2000 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 "version.h"
30
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <syslog.h>
37 #include <limits.h>
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/mman.h>
42 #include <time.h>
43 #include <pwd.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <signal.h>
47 #include <ctype.h>
48 #include <sys/wait.h>
49 #include <sys/socket.h>
50 #include <netinet/in.h>
51 #include <netinet/tcp.h>
52 #include <arpa/inet.h>
53 #include <netdb.h>
54 #include <dirent.h>
55
56 #include "port.h"
57 #include "match.h"
58 #include "tdate_parse.h"
59
60 #ifdef HAVE_SENDFILE
61 # ifdef HAVE_LINUX_SENDFILE
62 # include <sys/sendfile.h>
63 # else /* HAVE_LINUX_SENDFILE */
64 # include <sys/uio.h>
65 # endif /* HAVE_LINUX_SENDFILE */
66 #endif /* HAVE_SENDFILE */
67
68 #if defined(TCP_CORK) && !defined(TCP_NOPUSH)
69 #define TCP_NOPUSH TCP_CORK
70 /* (Linux's TCP_CORK is basically the same as BSD's TCP_NOPUSH.) */
71 #endif
72
73 #ifdef USE_SSL
74 #include <openssl/ssl.h>
75 #include <openssl/err.h>
76 #endif /* USE_SSL */
77
78
79 #if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED)
80 #define USE_IPV6
81 #endif
82
83 #ifndef STDIN_FILENO
84 #define STDIN_FILENO 0
85 #endif
86 #ifndef STDOUT_FILENO
87 #define STDOUT_FILENO 1
88 #endif
89 #ifndef STDERR_FILENO
90 #define STDERR_FILENO 2
91 #endif
92
93 #ifndef SHUT_WR
94 #define SHUT_WR 1
95 #endif
96
97 #ifndef SIZE_T_MAX
98 #define SIZE_T_MAX 2147483647L
99 #endif
100
101 #ifndef HAVE_INT64T
102 typedef long long int64_t;
103 #endif
104
105 #ifdef __CYGWIN__
106 #define timezone _timezone
107 #endif
108
109 #ifndef MAX
110 #define MAX(a,b) ((a) > (b) ? (a) : (b))
111 #endif
112 #ifndef MIN
113 #define MIN(a,b) ((a) < (b) ? (a) : (b))
114 #endif
115
116 /* Do overlapping strcpy safely, by using memmove. */
117 #define ol_strcpy(dst,src) memmove(dst,src,strlen(src)+1)
118
119 #ifndef ERR_DIR
120 #define ERR_DIR "errors"
121 #endif /* ERR_DIR */
122 #ifndef DEFAULT_HTTP_PORT
123 #define DEFAULT_HTTP_PORT 80
124 #endif /* DEFAULT_HTTP_PORT */
125 #ifdef USE_SSL
126 #ifndef DEFAULT_HTTPS_PORT
127 #define DEFAULT_HTTPS_PORT 443
128 #endif /* DEFAULT_HTTPS_PORT */
129 #ifndef DEFAULT_CERTFILE
130 #define DEFAULT_CERTFILE "mini_httpd.pem"
131 #endif /* DEFAULT_CERTFILE */
132 #endif /* USE_SSL */
133 #ifndef DEFAULT_USER
134 #define DEFAULT_USER "nobody"
135 #endif /* DEFAULT_USER */
136 #ifndef CGI_NICE
137 #define CGI_NICE 10
138 #endif /* CGI_NICE */
139 #ifndef CGI_PATH
140 #define CGI_PATH "/usr/local/bin:/usr/ucb:/bin:/usr/bin"
141 #endif /* CGI_PATH */
142 #ifndef CGI_LD_LIBRARY_PATH
143 #define CGI_LD_LIBRARY_PATH "/usr/local/lib:/usr/lib"
144 #endif /* CGI_LD_LIBRARY_PATH */
145 #ifndef AUTH_FILE
146 #define AUTH_FILE ".htpasswd"
147 #endif /* AUTH_FILE */
148 #ifndef READ_TIMEOUT
149 #define READ_TIMEOUT 60
150 #endif /* READ_TIMEOUT */
151 #ifndef WRITE_TIMEOUT
152 #define WRITE_TIMEOUT 300
153 #endif /* WRITE_TIMEOUT */
154 #ifndef DEFAULT_CHARSET
155 #define DEFAULT_CHARSET "UTF-8"
156 #endif /* DEFAULT_CHARSET */
157 #ifndef MAX_SEND_BUFFER_SIZE
158 #define MAX_SEND_BUFFER_SIZE 1048576
159 #endif /* MAX_SEND_BUFFER_SIZE */
160
161
162 #define METHOD_UNKNOWN 0
163 #define METHOD_GET 1
164 #define METHOD_HEAD 2
165 #define METHOD_POST 3
166 #define METHOD_PUT 4
167 #define METHOD_DELETE 5
168 #define METHOD_TRACE 6
169
170
171 /* A multi-family sockaddr. */
172 typedef union {
173 struct sockaddr sa;
174 struct sockaddr_in sa_in;
175 #ifdef USE_IPV6
176 struct sockaddr_in6 sa_in6;
177 struct sockaddr_storage sa_stor;
178 #endif /* USE_IPV6 */
179 } usockaddr;
180
181
182 static char* argv0;
183 static int debug;
184 static unsigned short port;
185 static char* dir;
186 static char* data_dir;
187 static int do_chroot;
188 static int vhost;
189 static char* user;
190 static char* cgi_pattern;
191 static char* url_pattern;
192 static int no_empty_referrers;
193 static char* local_pattern;
194 static char* hostname;
195 static char hostname_buf[500];
196 static char* logfile;
197 static char* pidfile;
198 static char* charset;
199 static char* p3p;
200 static int max_age;
201 static FILE* logfp;
202 static int listen4_fd, listen6_fd;
203 static int do_ssl;
204 #ifdef USE_SSL
205 static char* certfile;
206 static char* cipher;
207 static SSL_CTX* ssl_ctx;
208 #endif /* USE_SSL */
209 static char cwd[MAXPATHLEN];
210 static int got_hup;
211
212
213 /* Request variables. */
214 static int conn_fd;
215 #ifdef USE_SSL
216 static SSL* ssl;
217 #endif /* USE_SSL */
218 static usockaddr client_addr;
219 static char* request;
220 static size_t request_size, request_len, request_idx;
221 static int method;
222 static char* path;
223 static char* file;
224 static char* pathinfo;
225 struct stat sb;
226 static char* query;
227 static char* protocol;
228 static int status;
229 static off_t bytes;
230 static char* req_hostname;
231
232 static char* authorization;
233 static size_t content_length;
234 static char* content_type;
235 static char* cookie;
236 static char* host;
237 static time_t if_modified_since;
238 static char* referrer;
239 static char* useragent;
240
241 static char* remoteuser;
242
243
244 /* Forwards. */
245 static void usage( void );
246 static void read_config( char* filename );
247 static void value_required( char* name, char* value );
248 static void no_value_required( char* name, char* value );
249 static int initialize_listen_socket( usockaddr* usaP );
250 static void handle_request( void ) __attribute__((noreturn));
251 static void finish_request( int exitstatus ) __attribute__((noreturn));
252 static void de_dotdot( char* f );
253 static int get_pathinfo( void );
254 static void do_file( void );
255 static void do_dir( void );
256 #ifdef HAVE_SCANDIR
257 static char* file_details( const char* d, const char* name );
258 static void strencode( char* to, size_t tosize, const char* from );
259 #endif /* HAVE_SCANDIR */
260 static void do_cgi( void );
261 static void cgi_interpose_input( int wfd );
262 static void post_post_garbage_hack( void );
263 static void cgi_interpose_output( int rfd, int parse_headers );
264 static char** make_argp( void );
265 static char** make_envp( void );
266 static char* build_env( char* fmt, char* arg );
267 static void auth_check( char* dirname );
268 static void send_authenticate( char* realm );
269 static char* virtual_file( char* f );
270 static void send_error( int s, char* title, char* extra_header, char* text );
271 static void send_error_body( int s, char* title, char* text );
272 static int send_error_file( char* filename );
273 static void send_error_tail( void );
274 static void add_headers( int s, char* title, char* extra_header, char* me, char* mt, off_t b, time_t mod );
275 static void start_request( void );
276 static void add_to_request( char* str, size_t len );
277 static char* get_request_line( void );
278 static void start_response( void );
279 static void add_to_response( char* str );
280 static void send_response( void );
281 static void send_via_write( int fd, off_t size );
282 static void send_via_sendfile( int fd, int s, off_t size );
283 static ssize_t my_read( char* buf, size_t size );
284 static ssize_t my_write( void* buf, size_t size );
285 #ifdef HAVE_SENDFILE
286 static ssize_t my_sendfile( int fd, int s, off_t offset, size_t nbytes );
287 #endif /* HAVE_SENDFILE */
288 static void add_str( char** bufP, size_t* bufsizeP, size_t* buflenP, char* str );
289 static void add_data( char** bufP, size_t* bufsizeP, size_t* buflenP, char* str, size_t len );
290 static void make_log_entry( void );
291 static void check_referrer( void );
292 static int really_check_referrer( void );
293 static char* get_method_str( int m );
294 static void init_mime( void );
295 static const char* figure_mime( char* name, char* me, size_t me_size );
296 static void handle_sigterm( int sig );
297 static void handle_sighup( int sig );
298 static void handle_sigchld( int sig );
299 static void re_open_logfile( void );
300 static void handle_read_timeout( int sig );
301 static void handle_write_timeout( int sig );
302 static void lookup_hostname( usockaddr* usa4P, size_t sa4_len, int* gotv4P, usockaddr* usa6P, size_t sa6_len, int* gotv6P );
303 static char* ntoa( usockaddr* usaP );
304 static int sockaddr_check( usockaddr* usaP );
305 static size_t sockaddr_len( usockaddr* usaP );
306 static void strdecode( char* to, char* from );
307 static int hexit( char c );
308 static int b64_decode( const char* str, unsigned char* space, int size );
309 static void set_ndelay( int fd );
310 static void clear_ndelay( int fd );
311 static void* e_malloc( size_t size );
312 static void* e_realloc( void* optr, size_t size );
313 static char* e_strdup( char* ostr );
314 #ifdef NO_SNPRINTF
315 static int snprintf( char* str, size_t size, const char* format, ... );
316 #endif /* NO_SNPRINTF */
317
318
319 int
main(int argc,char ** argv)320 main( int argc, char** argv )
321 {
322 int argn;
323 struct passwd* pwd;
324 uid_t uid = 32767;
325 gid_t gid = 32767;
326 usockaddr host_addr4;
327 usockaddr host_addr6;
328 int gotv4, gotv6;
329 fd_set lfdset;
330 int maxfd;
331 usockaddr usa;
332 socklen_t sz;
333 int r;
334 char* cp;
335
336 /* Parse args. */
337 argv0 = argv[0];
338 debug = 0;
339 port = 0;
340 dir = (char*) 0;
341 data_dir = (char*) 0;
342 do_chroot = 0;
343 vhost = 0;
344 cgi_pattern = (char*) 0;
345 url_pattern = (char*) 0;
346 no_empty_referrers = 0;
347 local_pattern = (char*) 0;
348 charset = DEFAULT_CHARSET;
349 p3p = (char*) 0;
350 max_age = -1;
351 user = DEFAULT_USER;
352 hostname = (char*) 0;
353 logfile = (char*) 0;
354 pidfile = (char*) 0;
355 logfp = (FILE*) 0;
356 do_ssl = 0;
357 #ifdef USE_SSL
358 certfile = DEFAULT_CERTFILE;
359 cipher = (char*) 0;
360 #endif /* USE_SSL */
361 argn = 1;
362 while ( argn < argc && argv[argn][0] == '-' )
363 {
364 if ( strcmp( argv[argn], "-V" ) == 0 )
365 {
366 (void) printf( "%s\n", SERVER_SOFTWARE );
367 exit( 0 );
368 }
369 else if ( strcmp( argv[argn], "-C" ) == 0 && argn + 1 < argc )
370 {
371 ++argn;
372 read_config( argv[argn] );
373 }
374 else if ( strcmp( argv[argn], "-D" ) == 0 )
375 debug = 1;
376 #ifdef USE_SSL
377 else if ( strcmp( argv[argn], "-S" ) == 0 )
378 do_ssl = 1;
379 else if ( strcmp( argv[argn], "-E" ) == 0 && argn + 1 < argc )
380 {
381 ++argn;
382 certfile = argv[argn];
383 }
384 else if ( strcmp( argv[argn], "-Y" ) == 0 && argn + 1 < argc )
385 {
386 ++argn;
387 cipher = argv[argn];
388 }
389 #endif /* USE_SSL */
390 else if ( strcmp( argv[argn], "-p" ) == 0 && argn + 1 < argc )
391 {
392 ++argn;
393 port = (unsigned short) atoi( argv[argn] );
394 }
395 else if ( strcmp( argv[argn], "-d" ) == 0 && argn + 1 < argc )
396 {
397 ++argn;
398 dir = argv[argn];
399 }
400 else if ( strcmp( argv[argn], "-dd" ) == 0 && argn + 1 < argc )
401 {
402 ++argn;
403 data_dir = argv[argn];
404 }
405 else if ( strcmp( argv[argn], "-c" ) == 0 && argn + 1 < argc )
406 {
407 ++argn;
408 cgi_pattern = argv[argn];
409 }
410 else if ( strcmp( argv[argn], "-u" ) == 0 && argn + 1 < argc )
411 {
412 ++argn;
413 user = argv[argn];
414 }
415 else if ( strcmp( argv[argn], "-h" ) == 0 && argn + 1 < argc )
416 {
417 ++argn;
418 hostname = argv[argn];
419 }
420 else if ( strcmp( argv[argn], "-r" ) == 0 )
421 do_chroot = 1;
422 else if ( strcmp( argv[argn], "-v" ) == 0 )
423 vhost = 1;
424 else if ( strcmp( argv[argn], "-l" ) == 0 && argn + 1 < argc )
425 {
426 ++argn;
427 logfile = argv[argn];
428 }
429 else if ( strcmp( argv[argn], "-i" ) == 0 && argn + 1 < argc )
430 {
431 ++argn;
432 pidfile = argv[argn];
433 }
434 else if ( strcmp( argv[argn], "-T" ) == 0 && argn + 1 < argc )
435 {
436 ++argn;
437 charset = argv[argn];
438 }
439 else if ( strcmp( argv[argn], "-P" ) == 0 && argn + 1 < argc )
440 {
441 ++argn;
442 p3p = argv[argn];
443 }
444 else if ( strcmp( argv[argn], "-M" ) == 0 && argn + 1 < argc )
445 {
446 ++argn;
447 max_age = atoi( argv[argn] );
448 }
449 else
450 usage();
451 ++argn;
452 }
453 if ( argn != argc )
454 usage();
455
456 cp = strrchr( argv0, '/' );
457 if ( cp != (char*) 0 )
458 ++cp;
459 else
460 cp = argv0;
461 openlog( cp, LOG_NDELAY|LOG_PID, LOG_DAEMON );
462
463 if ( port == 0 )
464 {
465 #ifdef USE_SSL
466 if ( do_ssl )
467 port = DEFAULT_HTTPS_PORT;
468 else
469 port = DEFAULT_HTTP_PORT;
470 #else /* USE_SSL */
471 port = DEFAULT_HTTP_PORT;
472 #endif /* USE_SSL */
473 }
474
475 /* If we're root and we're going to become another user, get the uid/gid
476 ** now.
477 */
478 if ( getuid() == 0 )
479 {
480 pwd = getpwnam( user );
481 if ( pwd == (struct passwd*) 0 )
482 {
483 syslog( LOG_CRIT, "unknown user - '%s'", user );
484 (void) fprintf( stderr, "%s: unknown user - '%s'\n", argv0, user );
485 exit( 1 );
486 }
487 uid = pwd->pw_uid;
488 gid = pwd->pw_gid;
489 }
490
491 /* Log file. */
492 if ( logfile != (char*) 0 )
493 {
494 logfp = fopen( logfile, "a" );
495 if ( logfp == (FILE*) 0 )
496 {
497 syslog( LOG_CRIT, "%s - %m", logfile );
498 perror( logfile );
499 exit( 1 );
500 }
501 if ( logfile[0] != '/' )
502 {
503 syslog( LOG_WARNING, "logfile is not an absolute path, you may not be able to re-open it" );
504 (void) fprintf( stderr, "%s: logfile is not an absolute path, you may not be able to re-open it\n", argv0 );
505 }
506 if ( getuid() == 0 )
507 {
508 /* If we are root then we chown the log file to the user we'll
509 ** be switching to.
510 */
511 if ( fchown( fileno( logfp ), uid, gid ) < 0 )
512 {
513 syslog( LOG_WARNING, "fchown logfile - %m" );
514 perror( "fchown logfile" );
515 }
516 }
517 }
518
519 /* Look up hostname. */
520 lookup_hostname(
521 &host_addr4, sizeof(host_addr4), &gotv4,
522 &host_addr6, sizeof(host_addr6), &gotv6 );
523 if ( hostname == (char*) 0 )
524 {
525 (void) gethostname( hostname_buf, sizeof(hostname_buf) );
526 hostname = hostname_buf;
527 }
528 if ( ! ( gotv4 || gotv6 ) )
529 {
530 syslog( LOG_CRIT, "can't find any valid address" );
531 (void) fprintf( stderr, "%s: can't find any valid address\n", argv0 );
532 exit( 1 );
533 }
534
535 /* Initialize listen sockets. Try v6 first because of a Linux peculiarity;
536 ** like some other systems, it has magical v6 sockets that also listen for
537 ** v4, but in Linux if you bind a v4 socket first then the v6 bind fails.
538 */
539 if ( gotv6 )
540 listen6_fd = initialize_listen_socket( &host_addr6 );
541 else
542 listen6_fd = -1;
543 if ( gotv4 )
544 listen4_fd = initialize_listen_socket( &host_addr4 );
545 else
546 listen4_fd = -1;
547 /* If we didn't get any valid sockets, fail. */
548 if ( listen4_fd == -1 && listen6_fd == -1 )
549 {
550 syslog( LOG_CRIT, "can't bind to any address" );
551 (void) fprintf( stderr, "%s: can't bind to any address\n", argv0 );
552 exit( 1 );
553 }
554
555 #ifdef USE_SSL
556 if ( do_ssl )
557 {
558 SSL_load_error_strings();
559 SSLeay_add_ssl_algorithms();
560 ssl_ctx = SSL_CTX_new( SSLv23_server_method() );
561 SSL_CTX_set_options( ssl_ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3 );
562 if ( certfile[0] != '\0' )
563 if ( SSL_CTX_use_certificate_file( ssl_ctx, certfile, SSL_FILETYPE_PEM ) == 0 ||
564 SSL_CTX_use_certificate_chain_file( ssl_ctx, certfile ) == 0 ||
565 SSL_CTX_use_PrivateKey_file( ssl_ctx, certfile, SSL_FILETYPE_PEM ) == 0 ||
566 SSL_CTX_check_private_key( ssl_ctx ) == 0 )
567 {
568 ERR_print_errors_fp( stderr );
569 exit( 1 );
570 }
571 if ( cipher != (char*) 0 )
572 {
573 if ( SSL_CTX_set_cipher_list( ssl_ctx, cipher ) == 0 )
574 {
575 ERR_print_errors_fp( stderr );
576 exit( 1 );
577 }
578 }
579 }
580 #endif /* USE_SSL */
581
582 if ( ! debug )
583 {
584 /* Make ourselves a daemon. */
585 #ifdef HAVE_DAEMON
586 if ( daemon( 1, 1 ) < 0 )
587 {
588 syslog( LOG_CRIT, "daemon - %m" );
589 perror( "daemon" );
590 exit( 1 );
591 }
592 #else
593 switch ( fork() )
594 {
595 case 0:
596 break;
597 case -1:
598 syslog( LOG_CRIT, "fork - %m" );
599 perror( "fork" );
600 exit( 1 );
601 default:
602 exit( 0 );
603 }
604 #ifdef HAVE_SETSID
605 (void) setsid();
606 #endif
607 #endif
608 }
609 else
610 {
611 /* Even if we don't daemonize, we still want to disown our parent
612 ** process.
613 */
614 #ifdef HAVE_SETSID
615 (void) setsid();
616 #endif /* HAVE_SETSID */
617 }
618
619 if ( pidfile != (char*) 0 )
620 {
621 /* Write the PID file. */
622 FILE* pidfp = fopen( pidfile, "w" );
623 if ( pidfp == (FILE*) 0 )
624 {
625 syslog( LOG_CRIT, "%s - %m", pidfile );
626 perror( pidfile );
627 exit( 1 );
628 }
629 (void) fprintf( pidfp, "%d\n", (int) getpid() );
630 (void) fclose( pidfp );
631 }
632
633 /* Read zone info now, in case we chroot(). */
634 tzset();
635
636 /* If we're root, start becoming someone else. */
637 if ( getuid() == 0 )
638 {
639 /* Set aux groups to null. */
640 if ( setgroups( 0, (gid_t*) 0 ) < 0 )
641 {
642 syslog( LOG_CRIT, "setgroups - %m" );
643 perror( "setgroups" );
644 exit( 1 );
645 }
646 /* Set primary group. */
647 if ( setgid( gid ) < 0 )
648 {
649 syslog( LOG_CRIT, "setgid - %m" );
650 perror( "setgid" );
651 exit( 1 );
652 }
653 /* Try setting aux groups correctly - not critical if this fails. */
654 if ( initgroups( user, gid ) < 0 )
655 {
656 syslog( LOG_ERR, "initgroups - %m" );
657 perror( "initgroups" );
658 }
659 #ifdef HAVE_SETLOGIN
660 /* Set login name. */
661 (void) setlogin( user );
662 #endif /* HAVE_SETLOGIN */
663 }
664
665 /* Switch directories if requested. */
666 if ( dir != (char*) 0 )
667 {
668 if ( chdir( dir ) < 0 )
669 {
670 syslog( LOG_CRIT, "chdir - %m" );
671 perror( "chdir" );
672 exit( 1 );
673 }
674 }
675
676 /* Get current directory. */
677 (void) getcwd( cwd, sizeof(cwd) - 1 );
678 if ( cwd[strlen( cwd ) - 1] != '/' )
679 (void) strcat( cwd, "/" );
680
681 /* Chroot if requested. */
682 if ( do_chroot )
683 {
684 if ( chroot( cwd ) < 0 )
685 {
686 syslog( LOG_CRIT, "chroot - %m" );
687 perror( "chroot" );
688 exit( 1 );
689 }
690 /* If we're logging and the logfile's pathname begins with the
691 ** chroot tree's pathname, then elide the chroot pathname so
692 ** that the logfile pathname still works from inside the chroot
693 ** tree.
694 */
695 if ( logfile != (char*) 0 )
696 {
697 if ( strncmp( logfile, cwd, strlen( cwd ) ) == 0 )
698 {
699 (void) ol_strcpy( logfile, &logfile[strlen( cwd ) - 1] );
700 /* (We already guaranteed that cwd ends with a slash, so leaving
701 ** that slash in logfile makes it an absolute pathname within
702 ** the chroot tree.)
703 */
704 }
705 else
706 {
707 syslog( LOG_WARNING, "logfile is not within the chroot tree, you will not be able to re-open it" );
708 (void) fprintf( stderr, "%s: logfile is not within the chroot tree, you will not be able to re-open it\n", argv0 );
709 }
710 }
711 (void) strcpy( cwd, "/" );
712 /* Always chdir to / after a chroot. */
713 if ( chdir( cwd ) < 0 )
714 {
715 syslog( LOG_CRIT, "chroot chdir - %m" );
716 perror( "chroot chdir" );
717 exit( 1 );
718 }
719
720 }
721
722 /* Switch directories again if requested. */
723 if ( data_dir != (char*) 0 )
724 {
725 if ( chdir( data_dir ) < 0 )
726 {
727 syslog( LOG_CRIT, "data_dir chdir - %m" );
728 perror( "data_dir chdir" );
729 exit( 1 );
730 }
731 }
732
733 /* If we're root, become someone else. */
734 if ( getuid() == 0 )
735 {
736 /* Set uid. */
737 if ( setuid( uid ) < 0 )
738 {
739 syslog( LOG_CRIT, "setuid - %m" );
740 perror( "setuid" );
741 exit( 1 );
742 }
743 /* Check for unnecessary security exposure. */
744 if ( ! do_chroot )
745 {
746 syslog( LOG_WARNING,
747 "started as root without requesting chroot(), warning only" );
748 (void) fprintf( stderr,
749 "%s: started as root without requesting chroot(), warning only\n", argv0 );
750 }
751 }
752
753 /* Catch various signals. */
754 #ifdef HAVE_SIGSET
755 (void) sigset( SIGTERM, handle_sigterm );
756 (void) sigset( SIGINT, handle_sigterm );
757 (void) sigset( SIGUSR1, handle_sigterm );
758 (void) sigset( SIGHUP, handle_sighup );
759 (void) sigset( SIGCHLD, handle_sigchld );
760 (void) sigset( SIGPIPE, SIG_IGN );
761 #else /* HAVE_SIGSET */
762 (void) signal( SIGTERM, handle_sigterm );
763 (void) signal( SIGINT, handle_sigterm );
764 (void) signal( SIGUSR1, handle_sigterm );
765 (void) signal( SIGHUP, handle_sighup );
766 (void) signal( SIGCHLD, handle_sigchld );
767 (void) signal( SIGPIPE, SIG_IGN );
768 #endif /* HAVE_SIGSET */
769 got_hup = 0;
770
771 init_mime();
772
773 if ( hostname == (char*) 0 )
774 syslog(
775 LOG_NOTICE, "%.80s starting on port %d", SERVER_SOFTWARE,
776 (int) port );
777 else
778 syslog(
779 LOG_NOTICE, "%.80s starting on %.80s, port %d", SERVER_SOFTWARE,
780 hostname, (int) port );
781
782 /* Main loop. */
783 for (;;)
784 {
785 /* Do we need to re-open the log file? */
786 if ( got_hup )
787 {
788 re_open_logfile();
789 got_hup = 0;
790 }
791
792 /* Do a select() on at least one and possibly two listen fds.
793 ** If there's only one listen fd then we could skip the select
794 ** and just do the (blocking) accept(), saving one system call;
795 ** that's what happened up through version 1.18. However there
796 ** is one slight drawback to that method: the blocking accept()
797 ** is not interrupted by a signal call. Since we definitely want
798 ** signals to interrupt a waiting server, we use select() even
799 ** if there's only one fd.
800 */
801 FD_ZERO( &lfdset );
802 maxfd = -1;
803 if ( listen4_fd != -1 )
804 {
805 FD_SET( listen4_fd, &lfdset );
806 if ( listen4_fd > maxfd )
807 maxfd = listen4_fd;
808 }
809 if ( listen6_fd != -1 )
810 {
811 FD_SET( listen6_fd, &lfdset );
812 if ( listen6_fd > maxfd )
813 maxfd = listen6_fd;
814 }
815 if ( select( maxfd + 1, &lfdset, (fd_set*) 0, (fd_set*) 0, (struct timeval*) 0 ) < 0 )
816 {
817 if ( errno == EINTR || errno == EAGAIN )
818 continue; /* try again */
819 syslog( LOG_CRIT, "select - %m" );
820 perror( "select" );
821 exit( 1 );
822 }
823
824 /* Accept the new connection. */
825 sz = sizeof(usa);
826 if ( listen4_fd != -1 && FD_ISSET( listen4_fd, &lfdset ) )
827 conn_fd = accept( listen4_fd, &usa.sa, &sz );
828 else if ( listen6_fd != -1 && FD_ISSET( listen6_fd, &lfdset ) )
829 conn_fd = accept( listen6_fd, &usa.sa, &sz );
830 else
831 {
832 syslog( LOG_CRIT, "select failure" );
833 (void) fprintf( stderr, "%s: select failure\n", argv0 );
834 exit( 1 );
835 }
836 if ( conn_fd < 0 )
837 {
838 if ( errno == EINTR || errno == EAGAIN || errno == ECONNABORTED )
839 continue; /* try again */
840 #ifdef EPROTO
841 if ( errno == EPROTO )
842 continue; /* try again */
843 #endif /* EPROTO */
844 syslog( LOG_CRIT, "accept - %m" );
845 perror( "accept" );
846 exit( 1 );
847 }
848
849 /* Fork a sub-process to handle the connection. */
850 r = fork();
851 if ( r < 0 )
852 {
853 syslog( LOG_CRIT, "fork - %m" );
854 perror( "fork" );
855 exit( 1 );
856 }
857 if ( r == 0 )
858 {
859 /* Child process. */
860 client_addr = usa;
861 if ( listen4_fd != -1 )
862 (void) close( listen4_fd );
863 if ( listen6_fd != -1 )
864 (void) close( listen6_fd );
865 handle_request();
866 }
867 (void) close( conn_fd );
868 }
869 }
870
871
872 static void
usage(void)873 usage( void )
874 {
875 #ifdef USE_SSL
876 (void) fprintf( stderr, "usage: %s [-C configfile] [-D] [-S] [-E certfile] [-Y cipher] [-p port] [-d dir] [-dd data_dir] [-c cgipat] [-u user] [-h hostname] [-r] [-v] [-l logfile] [-i pidfile] [-T charset] [-P P3P] [-M maxage]\n", argv0 );
877 #else /* USE_SSL */
878 (void) fprintf( stderr, "usage: %s [-C configfile] [-D] [-p port] [-d dir] [-dd data_dir] [-c cgipat] [-u user] [-h hostname] [-r] [-v] [-l logfile] [-i pidfile] [-T charset] [-P P3P] [-M maxage]\n", argv0 );
879 #endif /* USE_SSL */
880 exit( 1 );
881 }
882
883
884 static void
read_config(char * filename)885 read_config( char* filename )
886 {
887 FILE* fp;
888 char line[10000];
889 char* cp;
890 char* cp2;
891 char* name;
892 char* value;
893
894 fp = fopen( filename, "r" );
895 if ( fp == (FILE*) 0 )
896 {
897 syslog( LOG_CRIT, "%s - %m", filename );
898 perror( filename );
899 exit( 1 );
900 }
901
902 while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
903 {
904 /* Trim comments. */
905 if ( ( cp = strchr( line, '#' ) ) != (char*) 0 )
906 *cp = '\0';
907
908 /* Skip leading whitespace. */
909 cp = line;
910 cp += strspn( cp, " \t\012\015" );
911
912 /* Split line into words. */
913 while ( *cp != '\0' )
914 {
915 /* Find next whitespace. */
916 cp2 = cp + strcspn( cp, " \t\012\015" );
917 /* Insert EOS and advance next-word pointer. */
918 while ( *cp2 == ' ' || *cp2 == '\t' || *cp2 == '\012' || *cp2 == '\015' )
919 *cp2++ = '\0';
920 /* Split into name and value. */
921 name = cp;
922 value = strchr( name, '=' );
923 if ( value != (char*) 0 )
924 *value++ = '\0';
925 /* Interpret. */
926 if ( strcasecmp( name, "debug" ) == 0 )
927 {
928 no_value_required( name, value );
929 debug = 1;
930 }
931 else if ( strcasecmp( name, "port" ) == 0 )
932 {
933 value_required( name, value );
934 port = (unsigned short) atoi( value );
935 }
936 else if ( strcasecmp( name, "dir" ) == 0 )
937 {
938 value_required( name, value );
939 dir = e_strdup( value );
940 }
941 else if ( strcasecmp( name, "data_dir" ) == 0 )
942 {
943 value_required( name, value );
944 data_dir = e_strdup( value );
945 }
946 else if ( strcasecmp( name, "chroot" ) == 0 )
947 {
948 no_value_required( name, value );
949 do_chroot = 1;
950 }
951 else if ( strcasecmp( name, "nochroot" ) == 0 )
952 {
953 no_value_required( name, value );
954 do_chroot = 0;
955 }
956 else if ( strcasecmp( name, "user" ) == 0 )
957 {
958 value_required( name, value );
959 user = e_strdup( value );
960 }
961 else if ( strcasecmp( name, "cgipat" ) == 0 )
962 {
963 value_required( name, value );
964 cgi_pattern = e_strdup( value );
965 }
966 else if ( strcasecmp( name, "urlpat" ) == 0 )
967 {
968 value_required( name, value );
969 url_pattern = e_strdup( value );
970 }
971 else if ( strcasecmp( name, "noemptyreferers" ) == 0 ||
972 strcasecmp( name, "noemptyreferrers" ) == 0 )
973 {
974 value_required( name, value );
975 no_empty_referrers = 1;
976 }
977 else if ( strcasecmp( name, "localpat" ) == 0 )
978 {
979 value_required( name, value );
980 local_pattern = e_strdup( value );
981 }
982 else if ( strcasecmp( name, "host" ) == 0 )
983 {
984 value_required( name, value );
985 hostname = e_strdup( value );
986 }
987 else if ( strcasecmp( name, "logfile" ) == 0 )
988 {
989 value_required( name, value );
990 logfile = e_strdup( value );
991 }
992 else if ( strcasecmp( name, "vhost" ) == 0 )
993 {
994 no_value_required( name, value );
995 vhost = 1;
996 }
997 else if ( strcasecmp( name, "pidfile" ) == 0 )
998 {
999 value_required( name, value );
1000 pidfile = e_strdup( value );
1001 }
1002 else if ( strcasecmp( name, "charset" ) == 0 )
1003 {
1004 value_required( name, value );
1005 charset = e_strdup( value );
1006 }
1007 else if ( strcasecmp( name, "p3p" ) == 0 )
1008 {
1009 value_required( name, value );
1010 p3p = e_strdup( value );
1011 }
1012 else if ( strcasecmp( name, "max_age" ) == 0 )
1013 {
1014 value_required( name, value );
1015 max_age = atoi( value );
1016 }
1017 #ifdef USE_SSL
1018 else if ( strcasecmp( name, "ssl" ) == 0 )
1019 {
1020 no_value_required( name, value );
1021 do_ssl = 1;
1022 }
1023 else if ( strcasecmp( name, "certfile" ) == 0 )
1024 {
1025 value_required( name, value );
1026 certfile = e_strdup( value );
1027 }
1028 else if ( strcasecmp( name, "cipher" ) == 0 )
1029 {
1030 value_required( name, value );
1031 cipher = e_strdup( value );
1032 }
1033 #endif /* USE_SSL */
1034 else
1035 {
1036 (void) fprintf(
1037 stderr, "%s: unknown config option '%s'\n", argv0, name );
1038 exit( 1 );
1039 }
1040
1041 /* Advance to next word. */
1042 cp = cp2;
1043 cp += strspn( cp, " \t\012\015" );
1044 }
1045 }
1046
1047 (void) fclose( fp );
1048 }
1049
1050
1051 static void
value_required(char * name,char * value)1052 value_required( char* name, char* value )
1053 {
1054 if ( value == (char*) 0 )
1055 {
1056 (void) fprintf(
1057 stderr, "%s: value required for %s option\n", argv0, name );
1058 exit( 1 );
1059 }
1060 }
1061
1062
1063 static void
no_value_required(char * name,char * value)1064 no_value_required( char* name, char* value )
1065 {
1066 if ( value != (char*) 0 )
1067 {
1068 (void) fprintf(
1069 stderr, "%s: no value required for %s option\n",
1070 argv0, name );
1071 exit( 1 );
1072 }
1073 }
1074
1075
1076 static int
initialize_listen_socket(usockaddr * usaP)1077 initialize_listen_socket( usockaddr* usaP )
1078 {
1079 int listen_fd;
1080 int i;
1081
1082 /* Check sockaddr. */
1083 if ( ! sockaddr_check( usaP ) )
1084 {
1085 syslog(
1086 LOG_ERR, "unknown sockaddr family on listen socket - %d",
1087 usaP->sa.sa_family );
1088 (void) fprintf(
1089 stderr, "%s: unknown sockaddr family on listen socket - %d\n",
1090 argv0, usaP->sa.sa_family );
1091 return -1;
1092 }
1093
1094 listen_fd = socket( usaP->sa.sa_family, SOCK_STREAM, 0 );
1095 if ( listen_fd < 0 )
1096 {
1097 syslog( LOG_CRIT, "socket %.80s - %m", ntoa( usaP ) );
1098 perror( "socket" );
1099 return -1;
1100 }
1101
1102 (void) fcntl( listen_fd, F_SETFD, 1 );
1103
1104 i = 1;
1105 if ( setsockopt( listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*) &i, sizeof(i) ) < 0 )
1106 {
1107 syslog( LOG_CRIT, "setsockopt SO_REUSEADDR - %m" );
1108 perror( "setsockopt SO_REUSEADDR" );
1109 return -1;
1110 }
1111
1112 if ( bind( listen_fd, &usaP->sa, sockaddr_len( usaP ) ) < 0 )
1113 {
1114 syslog( LOG_CRIT, "bind %.80s - %m", ntoa( usaP ) );
1115 perror( "bind" );
1116 return -1;
1117 }
1118
1119 if ( listen( listen_fd, 1024 ) < 0 )
1120 {
1121 syslog( LOG_CRIT, "listen - %m" );
1122 perror( "listen" );
1123 return -1;
1124 }
1125
1126 #ifdef HAVE_ACCEPT_FILTERS
1127 {
1128 struct accept_filter_arg af;
1129 (void) bzero( &af, sizeof(af) );
1130 (void) strcpy( af.af_name, ACCEPT_FILTER_NAME );
1131 (void) setsockopt( listen_fd, SOL_SOCKET, SO_ACCEPTFILTER, (char*) &af, sizeof(af) );
1132 }
1133 #endif /* HAVE_ACCEPT_FILTERS */
1134
1135 return listen_fd;
1136 }
1137
1138
1139 /* This runs in a child process, and exits when done, so cleanup is
1140 ** not needed.
1141 */
1142 static void
handle_request(void)1143 handle_request( void )
1144 {
1145 char* method_str;
1146 char* line;
1147 char* cp;
1148 int r, file_len, i;
1149 const char* index_names[] = {
1150 "index.html", "index.htm", "index.xhtml", "index.xht", "Default.htm",
1151 "index.cgi" };
1152
1153 /* Set up the timeout for reading. */
1154 #ifdef HAVE_SIGSET
1155 (void) sigset( SIGALRM, handle_read_timeout );
1156 #else /* HAVE_SIGSET */
1157 (void) signal( SIGALRM, handle_read_timeout );
1158 #endif /* HAVE_SIGSET */
1159 (void) alarm( READ_TIMEOUT );
1160
1161 /* Initialize the request variables. */
1162 remoteuser = (char*) 0;
1163 method = METHOD_UNKNOWN;
1164 path = (char*) 0;
1165 file = (char*) 0;
1166 pathinfo = (char*) 0;
1167 query = "";
1168 protocol = (char*) 0;
1169 status = 0;
1170 bytes = -1;
1171 req_hostname = (char*) 0;
1172
1173 authorization = (char*) 0;
1174 content_type = (char*) 0;
1175 content_length = -1;
1176 cookie = (char*) 0;
1177 host = (char*) 0;
1178 if_modified_since = (time_t) -1;
1179 referrer = "";
1180 useragent = "";
1181
1182 #ifdef TCP_NOPUSH
1183 if ( ! do_ssl )
1184 {
1185 /* Set the TCP_NOPUSH socket option, to try and avoid the 0.2 second
1186 ** delay between sending the headers and sending the data. A better
1187 ** solution is writev() (as used in thttpd), or send the headers with
1188 ** send(MSG_MORE) (only available in Linux so far).
1189 */
1190 r = 1;
1191 (void) setsockopt(
1192 conn_fd, IPPROTO_TCP, TCP_NOPUSH, (void*) &r, sizeof(r) );
1193 }
1194 #endif /* TCP_NOPUSH */
1195
1196 #ifdef USE_SSL
1197 if ( do_ssl )
1198 {
1199 ssl = SSL_new( ssl_ctx );
1200 SSL_set_fd( ssl, conn_fd );
1201 if ( SSL_accept( ssl ) == 0 )
1202 {
1203 ERR_print_errors_fp( stderr );
1204 finish_request( 1 );
1205 }
1206 }
1207 #endif /* USE_SSL */
1208
1209 /* Read in the request. */
1210 start_request();
1211 for (;;)
1212 {
1213 char buf[50000];
1214 int rr = my_read( buf, sizeof(buf) - 1 );
1215 if ( rr < 0 && ( errno == EINTR || errno == EAGAIN ) )
1216 continue;
1217 if ( rr <= 0 )
1218 break;
1219 (void) alarm( READ_TIMEOUT );
1220 add_to_request( buf, rr );
1221 if ( strstr( request, "\015\012\015\012" ) != (char*) 0 ||
1222 strstr( request, "\012\012" ) != (char*) 0 )
1223 break;
1224 }
1225
1226 /* Parse the first line of the request. */
1227 method_str = get_request_line();
1228 if ( method_str == (char*) 0 )
1229 send_error( 400, "Bad Request", "", "Can't parse request." );
1230 path = strpbrk( method_str, " \t\012\015" );
1231 if ( path == (char*) 0 )
1232 send_error( 400, "Bad Request", "", "Can't parse request." );
1233 *path++ = '\0';
1234 path += strspn( path, " \t\012\015" );
1235 protocol = strpbrk( path, " \t\012\015" );
1236 if ( protocol == (char*) 0 )
1237 send_error( 400, "Bad Request", "", "Can't parse request." );
1238 *protocol++ = '\0';
1239 protocol += strspn( protocol, " \t\012\015" );
1240 query = strchr( path, '?' );
1241 if ( query == (char*) 0 )
1242 query = "";
1243 else
1244 *query++ = '\0';
1245
1246 /* Parse the rest of the request headers. */
1247 while ( ( line = get_request_line() ) != (char*) 0 )
1248 {
1249 if ( line[0] == '\0' )
1250 break;
1251 else if ( strncasecmp( line, "Authorization:", 14 ) == 0 )
1252 {
1253 cp = &line[14];
1254 cp += strspn( cp, " \t" );
1255 authorization = cp;
1256 }
1257 else if ( strncasecmp( line, "Content-Length:", 15 ) == 0 )
1258 {
1259 cp = &line[15];
1260 cp += strspn( cp, " \t" );
1261 content_length = atol( cp );
1262 }
1263 else if ( strncasecmp( line, "Content-Type:", 13 ) == 0 )
1264 {
1265 cp = &line[13];
1266 cp += strspn( cp, " \t" );
1267 content_type = cp;
1268 }
1269 else if ( strncasecmp( line, "Cookie:", 7 ) == 0 )
1270 {
1271 cp = &line[7];
1272 cp += strspn( cp, " \t" );
1273 cookie = cp;
1274 }
1275 else if ( strncasecmp( line, "Host:", 5 ) == 0 )
1276 {
1277 cp = &line[5];
1278 cp += strspn( cp, " \t" );
1279 host = cp;
1280 if ( host[0] == '\0' || host[0] == '.' ||
1281 strchr( host, '/' ) != (char*) 0 )
1282 send_error( 400, "Bad Request", "", "Can't parse request." );
1283 }
1284 else if ( strncasecmp( line, "If-Modified-Since:", 18 ) == 0 )
1285 {
1286 cp = &line[18];
1287 cp += strspn( cp, " \t" );
1288 if_modified_since = tdate_parse( cp );
1289 }
1290 else if ( strncasecmp( line, "Referer:", 8 ) == 0 )
1291 {
1292 cp = &line[8];
1293 cp += strspn( cp, " \t" );
1294 referrer = cp;
1295 }
1296 else if ( strncasecmp( line, "Referrer:", 9 ) == 0 )
1297 {
1298 cp = &line[9];
1299 cp += strspn( cp, " \t" );
1300 referrer = cp;
1301 }
1302 else if ( strncasecmp( line, "User-Agent:", 11 ) == 0 )
1303 {
1304 cp = &line[11];
1305 cp += strspn( cp, " \t" );
1306 useragent = cp;
1307 }
1308 }
1309
1310 if ( strcasecmp( method_str, get_method_str( METHOD_GET ) ) == 0 )
1311 method = METHOD_GET;
1312 else if ( strcasecmp( method_str, get_method_str( METHOD_HEAD ) ) == 0 )
1313 method = METHOD_HEAD;
1314 else if ( strcasecmp( method_str, get_method_str( METHOD_POST ) ) == 0 )
1315 method = METHOD_POST;
1316 else if ( strcasecmp( method_str, get_method_str( METHOD_PUT ) ) == 0 )
1317 method = METHOD_PUT;
1318 else if ( strcasecmp( method_str, get_method_str( METHOD_DELETE ) ) == 0 )
1319 method = METHOD_DELETE;
1320 else if ( strcasecmp( method_str, get_method_str( METHOD_TRACE ) ) == 0 )
1321 method = METHOD_TRACE;
1322 else
1323 send_error( 501, "Not Implemented", "", "That method is not implemented." );
1324
1325 strdecode( path, path );
1326 if ( path[0] != '/' )
1327 send_error( 400, "Bad Request", "", "Bad filename." );
1328 file = &(path[1]);
1329 de_dotdot( file );
1330 if ( file[0] == '\0' )
1331 file = "./";
1332 if ( file[0] == '/' ||
1333 ( file[0] == '.' && file[1] == '.' &&
1334 ( file[2] == '\0' || file[2] == '/' ) ) )
1335 send_error( 400, "Bad Request", "", "Illegal filename." );
1336 if ( vhost )
1337 file = virtual_file( file );
1338
1339 /* Set up the timeout for writing. */
1340 #ifdef HAVE_SIGSET
1341 (void) sigset( SIGALRM, handle_write_timeout );
1342 #else /* HAVE_SIGSET */
1343 (void) signal( SIGALRM, handle_write_timeout );
1344 #endif /* HAVE_SIGSET */
1345 (void) alarm( WRITE_TIMEOUT );
1346
1347 r = stat( file, &sb );
1348 if ( r < 0 )
1349 r = get_pathinfo();
1350 if ( r < 0 )
1351 send_error( 404, "Not Found", "", "File not found." );
1352 file_len = strlen( file );
1353 if ( ! S_ISDIR( sb.st_mode ) )
1354 {
1355 /* Not a directory. */
1356 while ( file[file_len - 1] == '/' )
1357 {
1358 file[file_len - 1] = '\0';
1359 --file_len;
1360 }
1361 do_file();
1362 }
1363 else
1364 {
1365 char idx[10000];
1366
1367 /* The filename is a directory. Is it missing the trailing slash? */
1368 if ( file[file_len - 1] != '/' && pathinfo == (char*) 0 )
1369 {
1370 char location[10000];
1371 if ( query[0] != '\0' )
1372 (void) snprintf(
1373 location, sizeof(location), "Location: %s/?%s", path,
1374 query );
1375 else
1376 (void) snprintf(
1377 location, sizeof(location), "Location: %s/", path );
1378 send_error( 302, "Found", location, "Directories must end with a slash." );
1379 }
1380
1381 /* Check for an index file. */
1382 for ( i = 0; i < sizeof(index_names) / sizeof(char*); ++i )
1383 {
1384 (void) snprintf( idx, sizeof(idx), "%s%s", file, index_names[i] );
1385 if ( stat( idx, &sb ) >= 0 )
1386 {
1387 file = idx;
1388 do_file();
1389 goto got_one;
1390 }
1391 }
1392
1393 /* Nope, no index file, so it's an actual directory request. */
1394 do_dir();
1395
1396 got_one: ;
1397 }
1398
1399 #ifdef USE_SSL
1400 SSL_free( ssl );
1401 #endif /* USE_SSL */
1402
1403 finish_request( 0 );
1404 }
1405
1406
1407 static void
finish_request(int exitstatus)1408 finish_request( int exitstatus )
1409 {
1410 #undef LINGER_SOCKOPT
1411 #define LINGER_READ
1412
1413 #define LINGER_SECS 5
1414
1415 #ifdef LINGER_SOCKOPT
1416 /* The sockopt version of lingering close. Doesn't actually work. */
1417 struct linger lin;
1418
1419 shutdown( conn_fd, SHUT_WR );
1420 lin.l_onoff = 1;
1421 lin.l_linger = LINGER_SECS;
1422 (void) setsockopt(
1423 conn_fd, SOL_SOCKET, SO_LINGER, (void*) &lin, sizeof(lin) );
1424 #endif /* LINGER_SOCKOPT */
1425
1426 #ifdef LINGER_READ
1427 /* The "non-blocking read until error/eof/timeout" version of
1428 ** lingering close.
1429 */
1430 int flags;
1431 fd_set rfds;
1432 struct timeval tv;
1433 int r;
1434 char* buf[1024];
1435
1436 flags = fcntl( conn_fd, F_GETFL, 0 );
1437 if ( flags != -1 )
1438 {
1439 flags |= (int) O_NDELAY;
1440 (void) fcntl( conn_fd, F_SETFL, flags );
1441 }
1442 shutdown( conn_fd, SHUT_WR );
1443 for (;;)
1444 {
1445 FD_ZERO( &rfds );
1446 FD_SET( conn_fd, &rfds );
1447 tv.tv_sec = LINGER_SECS;
1448 tv.tv_usec = 0;
1449 r = select( conn_fd + 1, &rfds, (fd_set*) 0, (fd_set*) 0, &tv );
1450 if ( r <= 0 ) /* timeout or error */
1451 break;
1452 r = read( conn_fd, (void*) buf, sizeof(buf) );
1453 if ( r <= 0 ) /* eof or error */
1454 break;
1455 }
1456 #endif /* LINGER_READ */
1457
1458 exit( exitstatus );
1459 }
1460
1461
1462 static void
de_dotdot(char * f)1463 de_dotdot( char* f )
1464 {
1465 char* cp;
1466 char* cp2;
1467 int l;
1468
1469 /* Collapse any multiple / sequences. */
1470 while ( ( cp = strstr( f, "//") ) != (char*) 0 )
1471 {
1472 for ( cp2 = cp + 2; *cp2 == '/'; ++cp2 )
1473 continue;
1474 (void) ol_strcpy( cp + 1, cp2 );
1475 }
1476
1477 /* Remove leading ./ and any /./ sequences. */
1478 while ( strncmp( f, "./", 2 ) == 0 )
1479 (void) ol_strcpy( f, f + 2 );
1480 while ( ( cp = strstr( f, "/./") ) != (char*) 0 )
1481 (void) ol_strcpy( cp, cp + 2 );
1482
1483 /* Alternate between removing leading ../ and removing xxx/../ */
1484 for (;;)
1485 {
1486 while ( strncmp( f, "../", 3 ) == 0 )
1487 (void) ol_strcpy( f, f + 3 );
1488 cp = strstr( f, "/../" );
1489 if ( cp == (char*) 0 )
1490 break;
1491 for ( cp2 = cp - 1; cp2 >= f && *cp2 != '/'; --cp2 )
1492 continue;
1493 (void) ol_strcpy( cp2 + 1, cp + 4 );
1494 }
1495
1496 /* Also elide any xxx/.. at the end. */
1497 while ( ( l = strlen( f ) ) > 3 &&
1498 strcmp( ( cp = f + l - 3 ), "/.." ) == 0 )
1499 {
1500 for ( cp2 = cp - 1; cp2 >= f && *cp2 != '/'; --cp2 )
1501 continue;
1502 if ( cp2 < f )
1503 break;
1504 *cp2 = '\0';
1505 }
1506 }
1507
1508
1509 static int
get_pathinfo(void)1510 get_pathinfo( void )
1511 {
1512 int r;
1513
1514 pathinfo = &file[strlen(file)];
1515 for (;;)
1516 {
1517 do
1518 {
1519 --pathinfo;
1520 if ( pathinfo <= file )
1521 {
1522 pathinfo = (char*) 0;
1523 return -1;
1524 }
1525 }
1526 while ( *pathinfo != '/' );
1527 *pathinfo = '\0';
1528 r = stat( file, &sb );
1529 if ( r >= 0 )
1530 {
1531 ++pathinfo;
1532 return r;
1533 }
1534 else
1535 *pathinfo = '/';
1536 }
1537 }
1538
1539
1540 static void
do_file(void)1541 do_file( void )
1542 {
1543 char buf[10000];
1544 char mime_encodings[500];
1545 const char* mime_type;
1546 char fixed_mime_type[500];
1547 char* cp;
1548 int fd;
1549
1550 /* Check authorization for this directory. */
1551 (void) strncpy( buf, file, sizeof(buf) );
1552 cp = strrchr( buf, '/' );
1553 if ( cp == (char*) 0 )
1554 (void) strcpy( buf, "." );
1555 else
1556 *cp = '\0';
1557 auth_check( buf );
1558
1559 /* Check if the filename is the AUTH_FILE itself - that's verboten. */
1560 if ( strcmp( file, AUTH_FILE ) == 0 ||
1561 ( strcmp( &(file[strlen(file) - sizeof(AUTH_FILE) + 1]), AUTH_FILE ) == 0 &&
1562 file[strlen(file) - sizeof(AUTH_FILE)] == '/' ) )
1563 {
1564 syslog(
1565 LOG_NOTICE, "%.80s URL \"%.80s\" tried to retrieve an auth file",
1566 ntoa( &client_addr ), path );
1567 send_error( 403, "Forbidden", "", "File is protected." );
1568 }
1569
1570 /* Referrer check. */
1571 check_referrer();
1572
1573 /* Is it CGI? */
1574 if ( cgi_pattern != (char*) 0 && match( cgi_pattern, file ) )
1575 {
1576 do_cgi();
1577 return;
1578 }
1579 if ( pathinfo != (char*) 0 )
1580 send_error( 404, "Not Found", "", "File not found." );
1581
1582 if ( method != METHOD_GET && method != METHOD_HEAD )
1583 send_error( 501, "Not Implemented", "", "That method is not implemented." );
1584
1585 fd = open( file, O_RDONLY );
1586 if ( fd < 0 )
1587 {
1588 syslog(
1589 LOG_INFO, "%.80s File \"%.80s\" is protected",
1590 ntoa( &client_addr ), path );
1591 send_error( 403, "Forbidden", "", "File is protected." );
1592 }
1593 mime_type = figure_mime( file, mime_encodings, sizeof(mime_encodings) );
1594 (void) snprintf(
1595 fixed_mime_type, sizeof(fixed_mime_type), mime_type, charset );
1596 if ( if_modified_since != (time_t) -1 &&
1597 if_modified_since >= sb.st_mtime )
1598 {
1599 add_headers(
1600 304, "Not Modified", "", mime_encodings, fixed_mime_type,
1601 (off_t) -1, sb.st_mtime );
1602 send_response();
1603 return;
1604 }
1605 add_headers(
1606 200, "Ok", "", mime_encodings, fixed_mime_type, sb.st_size,
1607 sb.st_mtime );
1608 send_response();
1609 if ( method == METHOD_HEAD )
1610 return;
1611
1612 if ( sb.st_size > 0 ) /* ignore zero-length files */
1613 {
1614 #ifdef HAVE_SENDFILE
1615
1616 #ifndef USE_SSL
1617 send_via_sendfile( fd, conn_fd, sb.st_size );
1618 #else /* USE_SSL */
1619 if ( do_ssl )
1620 send_via_write( fd, sb.st_size );
1621 else
1622 send_via_sendfile( fd, conn_fd, sb.st_size );
1623 #endif /* USE_SSL */
1624
1625 #else /* HAVE_SENDFILE */
1626
1627 send_via_write( fd, sb.st_size );
1628
1629 #endif /* HAVE_SENDFILE */
1630 }
1631
1632 (void) close( fd );
1633 }
1634
1635
1636 static void
do_dir(void)1637 do_dir( void )
1638 {
1639 char buf[10000];
1640 char* contents;
1641 size_t contents_size, contents_len;
1642 #ifdef HAVE_SCANDIR
1643 int n, i;
1644 struct dirent **dl;
1645 char* name_info;
1646 #else /* HAVE_SCANDIR */
1647 char command[10000];
1648 FILE* fp;
1649 #endif /* HAVE_SCANDIR */
1650
1651 if ( pathinfo != (char*) 0 )
1652 send_error( 404, "Not Found", "", "File not found." );
1653
1654 /* Check authorization for this directory. */
1655 auth_check( file );
1656
1657 /* Referrer check. */
1658 check_referrer();
1659
1660 #ifdef HAVE_SCANDIR
1661 n = scandir( file, &dl, NULL, alphasort );
1662 if ( n < 0 )
1663 {
1664 syslog(
1665 LOG_INFO, "%.80s Directory \"%.80s\" is protected",
1666 ntoa( &client_addr ), path );
1667 send_error( 403, "Forbidden", "", "Directory is protected." );
1668 }
1669 #endif /* HAVE_SCANDIR */
1670
1671 contents_size = 0;
1672 (void) snprintf( buf, sizeof(buf), "\
1673 <!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n\
1674 \n\
1675 <html>\n\
1676 \n\
1677 <head>\n\
1678 <meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\">\n\
1679 <title>Index of %s</title>\n\
1680 </head>\n\
1681 \n\
1682 <body bgcolor=\"#99cc99\" text=\"#000000\" link=\"#2020ff\" vlink=\"#4040cc\">\n\
1683 <h4>Index of %s</h4>\n\
1684 <pre>\n",
1685 file, file );
1686 add_str( &contents, &contents_size, &contents_len, buf );
1687
1688 #ifdef HAVE_SCANDIR
1689
1690 for ( i = 0; i < n; ++i )
1691 {
1692 name_info = file_details( file, dl[i]->d_name );
1693 add_str( &contents, &contents_size, &contents_len, name_info );
1694 }
1695
1696 #else /* HAVE_SCANDIR */
1697 /* Magic HTML ls command! */
1698 if ( strchr( file, '\'' ) == (char*) 0 )
1699 {
1700 (void) snprintf(
1701 command, sizeof(command),
1702 "ls -lgF '%s' | tail +2 | sed -e 's/^\\([^ ][^ ]*\\)\\( *[^ ][^ ]* *[^ ][^ ]* *[^ ][^ ]*\\)\\( *[^ ][^ ]*\\) *\\([^ ][^ ]* *[^ ][^ ]* *[^ ][^ ]*\\) *\\(.*\\)$/\\1 \\3 \\4 |\\5/' -e '/ -> /!s,|\\([^*]*\\)$,|<a href=\"\\1\">\\1</a>,' -e '/ -> /!s,|\\(.*\\)\\([*]\\)$,|<a href=\"\\1\">\\1</a>\\2,' -e '/ -> /s,|\\([^@]*\\)\\(@* -> \\),|<a href=\"\\1\">\\1</a>\\2,' -e 's/|//'",
1703 file );
1704 fp = popen( command, "r" );
1705 for (;;)
1706 {
1707 size_t r;
1708 r = fread( buf, 1, sizeof(buf) - 1, fp );
1709 if ( r == 0 )
1710 break;
1711 buf[r] = '\0';
1712 add_str( &contents, &contents_size, &contents_len, buf );
1713 }
1714 (void) pclose( fp );
1715 }
1716 #endif /* HAVE_SCANDIR */
1717
1718 (void) snprintf( buf, sizeof(buf), "\
1719 </pre>\n\
1720 \n\
1721 <hr>\n\
1722 \n\
1723 <address><a href=\"%s\">%s</a></address>\n\
1724 \n\
1725 </body>\n\
1726 \n\
1727 </html>\n",
1728 SERVER_URL, SERVER_SOFTWARE );
1729 add_str( &contents, &contents_size, &contents_len, buf );
1730
1731 add_headers( 200, "Ok", "", "", "text/html; charset=%s", contents_len, sb.st_mtime );
1732 if ( method != METHOD_HEAD )
1733 {
1734 contents[contents_len] = '\0';
1735 add_to_response( contents );
1736 }
1737 send_response();
1738 }
1739
1740
1741 #ifdef HAVE_SCANDIR
1742
1743 static char*
file_details(const char * d,const char * name)1744 file_details( const char* d, const char* name )
1745 {
1746 struct stat sb2;
1747 char timestr[16];
1748 static char encname[1000];
1749 static char buf[2000];
1750
1751 (void) snprintf( buf, sizeof(buf), "%s/%s", d, name );
1752 if ( lstat( buf, &sb2 ) < 0 )
1753 return "???";
1754 (void) strftime( timestr, sizeof(timestr), "%d%b%Y %H:%M", localtime( &sb2.st_mtime ) );
1755 strencode( encname, sizeof(encname), name );
1756 (void) snprintf(
1757 buf, sizeof( buf ), "<a href=\"%s\">%-32.32s</a> %15s %14lld\n",
1758 encname, name, timestr, (long long) sb2.st_size );
1759 return buf;
1760 }
1761
1762
1763 /* Copies and encodes a string. */
1764 static void
strencode(char * to,size_t tosize,const char * from)1765 strencode( char* to, size_t tosize, const char* from )
1766 {
1767 int tolen;
1768
1769 for ( tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from )
1770 {
1771 if ( isalnum(*from) || strchr( "/_.-~", *from ) != (char*) 0 )
1772 {
1773 *to = *from;
1774 ++to;
1775 ++tolen;
1776 }
1777 else
1778 {
1779 (void) sprintf( to, "%%%02x", (int) *from & 0xff );
1780 to += 3;
1781 tolen += 3;
1782 }
1783 }
1784 *to = '\0';
1785 }
1786
1787 #endif /* HAVE_SCANDIR */
1788
1789
1790 static void
do_cgi(void)1791 do_cgi( void )
1792 {
1793 char** argp;
1794 char** envp;
1795 int parse_headers;
1796 char* binary;
1797 char* directory;
1798
1799 /* If the socket happens to be using one of the stdin/stdout/stderr
1800 ** descriptors, move it to another descriptor so that the dup2 calls
1801 ** below don't screw things up. We arbitrarily pick fd 3 - if there
1802 ** was already something on it, we clobber it, but that doesn't matter
1803 ** since at this point the only fd of interest is the connection.
1804 ** All others will be closed on exec.
1805 */
1806 if ( conn_fd == STDIN_FILENO || conn_fd == STDOUT_FILENO || conn_fd == STDERR_FILENO )
1807 {
1808 int newfd = dup2( conn_fd, STDERR_FILENO + 1 );
1809 if ( newfd >= 0 )
1810 conn_fd = newfd;
1811 /* If the dup2 fails, shrug. We'll just take our chances.
1812 ** Shouldn't happen though.
1813 */
1814 }
1815
1816 /* Make the environment vector. */
1817 envp = make_envp();
1818
1819 /* Make the argument vector. */
1820 argp = make_argp();
1821
1822 /* Set up stdin. For POSTs we may have to set up a pipe from an
1823 ** interposer process, depending on if we've read some of the data
1824 ** into our buffer. We also have to do this for all SSL CGIs.
1825 */
1826 #ifdef USE_SSL
1827 if ( ( method == METHOD_POST && request_len > request_idx ) || do_ssl )
1828 #else /* USE_SSL */
1829 if ( ( method == METHOD_POST && request_len > request_idx ) )
1830 #endif /* USE_SSL */
1831 {
1832 int p[2];
1833 int r;
1834
1835 if ( pipe( p ) < 0 )
1836 send_error( 500, "Internal Error", "", "Something unexpected went wrong making a pipe." );
1837 r = fork();
1838 if ( r < 0 )
1839 send_error( 500, "Internal Error", "", "Something unexpected went wrong forking an interposer." );
1840 if ( r == 0 )
1841 {
1842 /* Interposer process. */
1843 (void) close( p[0] );
1844 cgi_interpose_input( p[1] );
1845 finish_request( 0 );
1846 }
1847 (void) close( p[1] );
1848 if ( p[0] != STDIN_FILENO )
1849 {
1850 (void) dup2( p[0], STDIN_FILENO );
1851 (void) close( p[0] );
1852 }
1853 }
1854 else
1855 {
1856 /* Otherwise, the request socket is stdin. */
1857 if ( conn_fd != STDIN_FILENO )
1858 (void) dup2( conn_fd, STDIN_FILENO );
1859 }
1860
1861 /* Set up stdout/stderr. For SSL, or if we're doing CGI header parsing,
1862 ** we need an output interposer too.
1863 */
1864 if ( strncmp( argp[0], "nph-", 4 ) == 0 )
1865 parse_headers = 0;
1866 else
1867 parse_headers = 1;
1868 #ifdef USE_SSL
1869 if ( parse_headers || do_ssl )
1870 #else /* USE_SSL */
1871 if ( parse_headers )
1872 #endif /* USE_SSL */
1873 {
1874 int p[2];
1875 int r;
1876
1877 if ( pipe( p ) < 0 )
1878 send_error( 500, "Internal Error", "", "Something unexpected went wrong making a pipe." );
1879 r = fork();
1880 if ( r < 0 )
1881 send_error( 500, "Internal Error", "", "Something unexpected went wrong forking an interposer." );
1882 if ( r == 0 )
1883 {
1884 /* Interposer process. */
1885 (void) close( p[1] );
1886 cgi_interpose_output( p[0], parse_headers );
1887 finish_request( 0 );
1888 }
1889 (void) close( p[0] );
1890 if ( p[1] != STDOUT_FILENO )
1891 (void) dup2( p[1], STDOUT_FILENO );
1892 if ( p[1] != STDERR_FILENO )
1893 (void) dup2( p[1], STDERR_FILENO );
1894 if ( p[1] != STDOUT_FILENO && p[1] != STDERR_FILENO )
1895 (void) close( p[1] );
1896 }
1897 else
1898 {
1899 /* Otherwise, the request socket is stdout/stderr. */
1900 if ( conn_fd != STDOUT_FILENO )
1901 (void) dup2( conn_fd, STDOUT_FILENO );
1902 if ( conn_fd != STDERR_FILENO )
1903 (void) dup2( conn_fd, STDERR_FILENO );
1904 }
1905
1906 /* At this point we would like to set conn_fd to be close-on-exec.
1907 ** Unfortunately there seems to be a Linux problem here - if we
1908 ** do this close-on-exec in Linux, the socket stays open but stderr
1909 ** gets closed - the last fd duped from the socket. What a mess.
1910 ** So we'll just leave the socket as is, which under other OSs means
1911 ** an extra file descriptor gets passed to the child process. Since
1912 ** the child probably already has that file open via stdin stdout
1913 ** and/or stderr, this is not a problem.
1914 */
1915 /* (void) fcntl( conn_fd, F_SETFD, 1 ); */
1916
1917 /* Close the log file. */
1918 if ( logfp != (FILE*) 0 )
1919 (void) fclose( logfp );
1920
1921 /* Close syslog. */
1922 closelog();
1923
1924 /* Set priority. */
1925 (void) nice( CGI_NICE );
1926
1927 /* Split the program into directory and binary, so we can chdir()
1928 ** to the program's own directory. This isn't in the CGI 1.1
1929 ** spec, but it's what other HTTP servers do.
1930 */
1931 directory = e_strdup( file );
1932 binary = strrchr( directory, '/' );
1933 if ( binary == (char*) 0 )
1934 binary = file;
1935 else
1936 {
1937 *binary++ = '\0';
1938 (void) chdir( directory ); /* ignore errors */
1939 }
1940
1941 /* Default behavior for SIGPIPE. */
1942 #ifdef HAVE_SIGSET
1943 (void) sigset( SIGPIPE, SIG_DFL );
1944 #else /* HAVE_SIGSET */
1945 (void) signal( SIGPIPE, SIG_DFL );
1946 #endif /* HAVE_SIGSET */
1947
1948 /* Run the program. */
1949 (void) execve( binary, argp, envp );
1950
1951 /* Something went wrong. */
1952 send_error( 500, "Internal Error", "", "Something unexpected went wrong running a CGI program." );
1953 }
1954
1955
1956 /* This routine is used only for POST requests. It reads the data
1957 ** from the request and sends it to the child process. The only reason
1958 ** we need to do it this way instead of just letting the child read
1959 ** directly is that we have already read part of the data into our
1960 ** buffer.
1961 **
1962 ** Oh, and it's also used for all SSL CGIs.
1963 */
1964 static void
cgi_interpose_input(int wfd)1965 cgi_interpose_input( int wfd )
1966 {
1967 size_t c;
1968 ssize_t r, r2;
1969 char buf[1024];
1970
1971 /* Set up the timeout for reading again, since we're in a sub-process. */
1972 #ifdef HAVE_SIGSET
1973 (void) sigset( SIGALRM, handle_read_timeout );
1974 #else /* HAVE_SIGSET */
1975 (void) signal( SIGALRM, handle_read_timeout );
1976 #endif /* HAVE_SIGSET */
1977 (void) alarm( READ_TIMEOUT );
1978
1979 c = request_len - request_idx;
1980 if ( c > 0 )
1981 {
1982 if ( write( wfd, &(request[request_idx]), c ) != c )
1983 return;
1984 }
1985 while ( c < content_length )
1986 {
1987 r = my_read( buf, MIN( sizeof(buf), content_length - c ) );
1988 if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
1989 {
1990 sleep( 1 );
1991 continue;
1992 }
1993 if ( r <= 0 )
1994 return;
1995 for (;;)
1996 {
1997 r2 = write( wfd, buf, r );
1998 if ( r2 < 0 && ( errno == EINTR || errno == EAGAIN ) )
1999 {
2000 sleep( 1 );
2001 continue;
2002 }
2003 if ( r2 != r )
2004 return;
2005 break;
2006 }
2007 c += r;
2008 (void) alarm( READ_TIMEOUT );
2009 }
2010 post_post_garbage_hack();
2011 }
2012
2013
2014 /* Special hack to deal with broken browsers that send a LF or CRLF
2015 ** after POST data, causing TCP resets - we just read and discard up
2016 ** to 2 bytes. Unfortunately this doesn't fix the problem for CGIs
2017 ** which avoid the interposer process due to their POST data being
2018 ** short. Creating an interposer process for all POST CGIs is
2019 ** unacceptably expensive.
2020 */
2021 static void
post_post_garbage_hack(void)2022 post_post_garbage_hack( void )
2023 {
2024 char buf[2];
2025
2026 #ifdef USE_SSL
2027 if ( do_ssl )
2028 /* We don't need to do this for SSL, since the garbage has
2029 ** already been read. Probably.
2030 */
2031 return;
2032 #endif /* USE_SSL */
2033
2034 set_ndelay( conn_fd );
2035 (void) read( conn_fd, buf, sizeof(buf) );
2036 clear_ndelay( conn_fd );
2037 }
2038
2039
2040 /* This routine is used for parsed-header CGIs and for all SSL CGIs. */
2041 static void
cgi_interpose_output(int rfd,int parse_headers)2042 cgi_interpose_output( int rfd, int parse_headers )
2043 {
2044 ssize_t r, r2;
2045 char buf[1024];
2046
2047 /* Set up the timeout for writing again, since we're in a sub-process. */
2048 #ifdef HAVE_SIGSET
2049 (void) sigset( SIGALRM, handle_write_timeout );
2050 #else /* HAVE_SIGSET */
2051 (void) signal( SIGALRM, handle_write_timeout );
2052 #endif /* HAVE_SIGSET */
2053 (void) alarm( WRITE_TIMEOUT );
2054
2055 if ( ! parse_headers )
2056 {
2057 /* If we're not parsing headers, write out the default status line
2058 ** and proceed to the echo phase.
2059 */
2060 char http_head[] = "HTTP/1.0 200 OK\015\012";
2061 (void) my_write( http_head, sizeof(http_head) );
2062 }
2063 else
2064 {
2065 /* Header parsing. The idea here is that the CGI can return special
2066 ** headers such as "Status:" and "Location:" which change the return
2067 ** status of the response. Since the return status has to be the very
2068 ** first line written out, we have to accumulate all the headers
2069 ** and check for the special ones before writing the status. Then
2070 ** we write out the saved headers and proceed to echo the rest of
2071 ** the response.
2072 */
2073 size_t headers_size, headers_len;
2074 char* headers;
2075 char* br;
2076 int s;
2077 char* title;
2078 char* cp;
2079
2080 /* Slurp in all headers. */
2081 headers_size = 0;
2082 add_data( &headers, &headers_size, &headers_len, (char*) 0, 0 );
2083 for (;;)
2084 {
2085 r = read( rfd, buf, sizeof(buf) );
2086 if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
2087 {
2088 sleep( 1 );
2089 continue;
2090 }
2091 if ( r <= 0 )
2092 {
2093 br = &(headers[headers_len]);
2094 break;
2095 }
2096 add_data( &headers, &headers_size, &headers_len, buf, r );
2097 if ( ( br = strstr( headers, "\015\012\015\012" ) ) != (char*) 0 ||
2098 ( br = strstr( headers, "\012\012" ) ) != (char*) 0 )
2099 break;
2100 }
2101
2102 /* If there were no headers, bail. */
2103 if ( headers_len == 0 )
2104 return;
2105
2106 /* Figure out the status. */
2107 s = 200;
2108 if ( ( cp = strstr( headers, "Location:" ) ) != (char*) 0 &&
2109 cp < br &&
2110 ( cp == headers || *(cp-1) == '\012' ) )
2111 s = 302;
2112 if ( ( cp = strstr( headers, "Status:" ) ) != (char*) 0 &&
2113 cp < br &&
2114 ( cp == headers || *(cp-1) == '\012' ) )
2115 {
2116 cp += 7;
2117 cp += strspn( cp, " \t" );
2118 s = atoi( cp );
2119 }
2120
2121 /* Write the status line. */
2122 switch ( s )
2123 {
2124 case 200: title = "OK"; break;
2125 case 302: title = "Found"; break;
2126 case 304: title = "Not Modified"; break;
2127 case 400: title = "Bad Request"; break;
2128 case 401: title = "Unauthorized"; break;
2129 case 403: title = "Forbidden"; break;
2130 case 404: title = "Not Found"; break;
2131 case 408: title = "Request Timeout"; break;
2132 case 451: title = "Unavailable For Legal Reasons"; break;
2133 case 500: title = "Internal Error"; break;
2134 case 501: title = "Not Implemented"; break;
2135 case 503: title = "Service Temporarily Overloaded"; break;
2136 default: title = "Something"; break;
2137 }
2138 (void) snprintf(
2139 buf, sizeof(buf), "HTTP/1.0 %d %s\015\012", s, title );
2140 (void) my_write( buf, strlen( buf ) );
2141
2142 /* Write the saved headers. */
2143 (void) my_write( headers, headers_len );
2144 }
2145
2146 /* Echo the rest of the output. */
2147 for (;;)
2148 {
2149 r = read( rfd, buf, sizeof(buf) );
2150 if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
2151 {
2152 sleep( 1 );
2153 continue;
2154 }
2155 if ( r <= 0 )
2156 return;
2157 for (;;)
2158 {
2159 r2 = my_write( buf, r );
2160 if ( r2 < 0 && ( errno == EINTR || errno == EAGAIN ) )
2161 {
2162 sleep( 1 );
2163 continue;
2164 }
2165 if ( r2 != r )
2166 return;
2167 break;
2168 }
2169 (void) alarm( WRITE_TIMEOUT );
2170 }
2171 }
2172
2173
2174 /* Set up CGI argument vector. We don't have to worry about freeing
2175 ** stuff since we're a sub-process. This gets done after make_envp() because
2176 ** we scribble on query.
2177 */
2178 static char**
make_argp(void)2179 make_argp( void )
2180 {
2181 char** argp;
2182 int argn;
2183 char* cp1;
2184 char* cp2;
2185
2186 /* By allocating an arg slot for every character in the query, plus
2187 ** one for the filename and one for the NULL, we are guaranteed to
2188 ** have enough. We could actually use strlen/2.
2189 */
2190 argp = (char**) malloc( ( strlen( query ) + 2 ) * sizeof(char*) );
2191 if ( argp == (char**) 0 )
2192 return (char**) 0;
2193
2194 argp[0] = strrchr( file, '/' );
2195 if ( argp[0] != (char*) 0 )
2196 ++argp[0];
2197 else
2198 argp[0] = file;
2199
2200 argn = 1;
2201 /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html,
2202 ** "The server should search the query information for a non-encoded =
2203 ** character to determine if the command line is to be used, if it finds
2204 ** one, the command line is not to be used."
2205 */
2206 if ( strchr( query, '=' ) == (char*) 0 )
2207 {
2208 for ( cp1 = cp2 = query; *cp2 != '\0'; ++cp2 )
2209 {
2210 if ( *cp2 == '+' )
2211 {
2212 *cp2 = '\0';
2213 strdecode( cp1, cp1 );
2214 argp[argn++] = cp1;
2215 cp1 = cp2 + 1;
2216 }
2217 }
2218 if ( cp2 != cp1 )
2219 {
2220 strdecode( cp1, cp1 );
2221 argp[argn++] = cp1;
2222 }
2223 }
2224
2225 argp[argn] = (char*) 0;
2226 return argp;
2227 }
2228
2229
2230 /* Set up CGI environment variables. Be real careful here to avoid
2231 ** letting malicious clients overrun a buffer. We don't have
2232 ** to worry about freeing stuff since we're a sub-process.
2233 */
2234 static char**
make_envp(void)2235 make_envp( void )
2236 {
2237 static char* envp[50];
2238 int envn;
2239 char* cp;
2240 char buf[256];
2241
2242 envn = 0;
2243 envp[envn++] = build_env( "PATH=%s", CGI_PATH );
2244 envp[envn++] = build_env( "LD_LIBRARY_PATH=%s", CGI_LD_LIBRARY_PATH );
2245 envp[envn++] = build_env( "SERVER_SOFTWARE=%s", SERVER_SOFTWARE );
2246 if ( vhost && req_hostname != (char*) 0 && req_hostname[0] != '\0' )
2247 cp = req_hostname; /* already computed by virtual_file() */
2248 else if ( host != (char*) 0 && host[0] != '\0' )
2249 cp = host;
2250 else
2251 cp = hostname;
2252 if ( cp != (char*) 0 )
2253 envp[envn++] = build_env( "SERVER_NAME=%s", cp );
2254 envp[envn++] = "GATEWAY_INTERFACE=CGI/1.1";
2255 envp[envn++] = "SERVER_PROTOCOL=HTTP/1.0";
2256 (void) snprintf( buf, sizeof(buf), "%d", (int) port );
2257 envp[envn++] = build_env( "SERVER_PORT=%s", buf );
2258 envp[envn++] = build_env(
2259 "REQUEST_METHOD=%s", get_method_str( method ) );
2260 envp[envn++] = build_env( "SCRIPT_NAME=%s", path );
2261 if ( pathinfo != (char*) 0 )
2262 {
2263 envp[envn++] = build_env( "PATH_INFO=/%s", pathinfo );
2264 (void) snprintf( buf, sizeof(buf), "%s%s", cwd, pathinfo );
2265 envp[envn++] = build_env( "PATH_TRANSLATED=%s", buf );
2266 }
2267 if ( query[0] != '\0' )
2268 envp[envn++] = build_env( "QUERY_STRING=%s", query );
2269 envp[envn++] = build_env( "REMOTE_ADDR=%s", ntoa( &client_addr ) );
2270 if ( referrer[0] != '\0' )
2271 {
2272 envp[envn++] = build_env( "HTTP_REFERER=%s", referrer );
2273 envp[envn++] = build_env( "HTTP_REFERRER=%s", referrer );
2274 }
2275 if ( useragent[0] != '\0' )
2276 envp[envn++] = build_env( "HTTP_USER_AGENT=%s", useragent );
2277 if ( cookie != (char*) 0 )
2278 envp[envn++] = build_env( "HTTP_COOKIE=%s", cookie );
2279 if ( host != (char*) 0 )
2280 envp[envn++] = build_env( "HTTP_HOST=%s", host );
2281 if ( content_type != (char*) 0 )
2282 envp[envn++] = build_env( "CONTENT_TYPE=%s", content_type );
2283 if ( content_length != -1 )
2284 {
2285 (void) snprintf(
2286 buf, sizeof(buf), "%lu", (unsigned long) content_length );
2287 envp[envn++] = build_env( "CONTENT_LENGTH=%s", buf );
2288 }
2289 if ( remoteuser != (char*) 0 )
2290 envp[envn++] = build_env( "REMOTE_USER=%s", remoteuser );
2291 if ( authorization != (char*) 0 )
2292 envp[envn++] = build_env( "AUTH_TYPE=%s", "Basic" );
2293 if ( getenv( "TZ" ) != (char*) 0 )
2294 envp[envn++] = build_env( "TZ=%s", getenv( "TZ" ) );
2295
2296 envp[envn] = (char*) 0;
2297 return envp;
2298 }
2299
2300
2301 static char*
build_env(char * fmt,char * arg)2302 build_env( char* fmt, char* arg )
2303 {
2304 char* cp;
2305 int size;
2306 static char* buf;
2307 static int maxbuf = 0;
2308
2309 size = strlen( fmt ) + strlen( arg );
2310 if ( size > maxbuf )
2311 {
2312 if ( maxbuf == 0 )
2313 {
2314 maxbuf = MAX( 200, size + 100 );
2315 buf = (char*) e_malloc( maxbuf );
2316 }
2317 else
2318 {
2319 maxbuf = MAX( maxbuf * 2, size * 5 / 4 );
2320 buf = (char*) e_realloc( (void*) buf, maxbuf );
2321 }
2322 }
2323 (void) snprintf( buf, maxbuf, fmt, arg );
2324 cp = e_strdup( buf );
2325 return cp;
2326 }
2327
2328
2329 static void
auth_check(char * dirname)2330 auth_check( char* dirname )
2331 {
2332 char authpath[10000];
2333 struct stat sb2;
2334 char authinfo[500];
2335 char* authpass;
2336 char* colon;
2337 static char line[10000];
2338 int l;
2339 FILE* fp;
2340 char* cryp;
2341
2342 /* Construct auth filename. */
2343 if ( dirname[strlen(dirname) - 1] == '/' )
2344 (void) snprintf( authpath, sizeof(authpath), "%s%s", dirname, AUTH_FILE );
2345 else
2346 (void) snprintf( authpath, sizeof(authpath), "%s/%s", dirname, AUTH_FILE );
2347
2348 /* Does this directory have an auth file? */
2349 if ( stat( authpath, &sb2 ) < 0 )
2350 /* Nope, let the request go through. */
2351 return;
2352
2353 /* Does this request contain authorization info? */
2354 if ( authorization == (char*) 0 )
2355 /* Nope, return a 401 Unauthorized. */
2356 send_authenticate( dirname );
2357
2358 /* Basic authorization info? */
2359 if ( strncmp( authorization, "Basic ", 6 ) != 0 )
2360 send_authenticate( dirname );
2361
2362 /* Decode it. */
2363 l = b64_decode(
2364 &(authorization[6]), (unsigned char*) authinfo, sizeof(authinfo) - 1 );
2365 authinfo[l] = '\0';
2366 /* Split into user and password. */
2367 authpass = strchr( authinfo, ':' );
2368 if ( authpass == (char*) 0 )
2369 /* No colon? Bogus auth info. */
2370 send_authenticate( dirname );
2371 *authpass++ = '\0';
2372 /* If there are more fields, cut them off. */
2373 colon = strchr( authpass, ':' );
2374 if ( colon != (char*) 0 )
2375 *colon = '\0';
2376
2377 /* Open the password file. */
2378 fp = fopen( authpath, "r" );
2379 if ( fp == (FILE*) 0 )
2380 {
2381 /* The file exists but we can't open it? Disallow access. */
2382 syslog(
2383 LOG_ERR, "%.80s auth file %.80s could not be opened - %m",
2384 ntoa( &client_addr ), authpath );
2385 send_error( 403, "Forbidden", "", "File is protected." );
2386 }
2387
2388 /* Read it. */
2389 while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
2390 {
2391 /* Nuke newline. */
2392 l = strlen( line );
2393 if ( line[l - 1] == '\n' )
2394 line[l - 1] = '\0';
2395 /* Split into user and encrypted password. */
2396 cryp = strchr( line, ':' );
2397 if ( cryp == (char*) 0 )
2398 continue;
2399 *cryp++ = '\0';
2400 /* Is this the right user? */
2401 if ( strcmp( line, authinfo ) == 0 )
2402 {
2403 /* Yes. */
2404 (void) fclose( fp );
2405 /* So is the password right? */
2406 if ( strcmp( crypt( authpass, cryp ), cryp ) == 0 )
2407 {
2408 /* Ok! */
2409 remoteuser = line;
2410 return;
2411 }
2412 else
2413 /* No. */
2414 send_authenticate( dirname );
2415 }
2416 }
2417
2418 /* Didn't find that user. Access denied. */
2419 (void) fclose( fp );
2420 send_authenticate( dirname );
2421 }
2422
2423
2424 static void
send_authenticate(char * realm)2425 send_authenticate( char* realm )
2426 {
2427 char header[10000];
2428
2429 (void) snprintf(
2430 header, sizeof(header), "WWW-Authenticate: Basic realm=\"%s\"", realm );
2431 send_error( 401, "Unauthorized", header, "Authorization required." );
2432 }
2433
2434
2435 static char*
virtual_file(char * f)2436 virtual_file( char* f )
2437 {
2438 char* cp;
2439 static char vfile[10000];
2440
2441 /* Use the request's hostname, or fall back on the IP address. */
2442 if ( host != (char*) 0 )
2443 req_hostname = host;
2444 else
2445 {
2446 usockaddr usa;
2447 socklen_t sz = sizeof(usa);
2448 if ( getsockname( conn_fd, &usa.sa, &sz ) < 0 )
2449 req_hostname = "UNKNOWN_HOST";
2450 else
2451 req_hostname = ntoa( &usa );
2452 }
2453 /* Pound it to lower case. */
2454 for ( cp = req_hostname; *cp != '\0'; ++cp )
2455 if ( isupper( *cp ) )
2456 *cp = tolower( *cp );
2457 (void) snprintf( vfile, sizeof(vfile), "%s/%s", req_hostname, f );
2458 return vfile;
2459 }
2460
2461
2462 static void
send_error(int s,char * title,char * extra_header,char * text)2463 send_error( int s, char* title, char* extra_header, char* text )
2464 {
2465 add_headers(
2466 s, title, extra_header, "", "text/html; charset=%s", (off_t) -1, (time_t) -1 );
2467
2468 send_error_body( s, title, text );
2469
2470 send_error_tail();
2471
2472 send_response();
2473
2474 #ifdef USE_SSL
2475 SSL_free( ssl );
2476 #endif /* USE_SSL */
2477 finish_request( 1 );
2478 }
2479
2480
2481 static void
send_error_body(int s,char * title,char * text)2482 send_error_body( int s, char* title, char* text )
2483 {
2484 char filename[1000];
2485 char buf[10000];
2486
2487 if ( vhost && req_hostname != (char*) 0 )
2488 {
2489 /* Try virtual-host custom error page. */
2490 (void) snprintf(
2491 filename, sizeof(filename), "%s/%s/err%d.html",
2492 req_hostname, ERR_DIR, s );
2493 if ( send_error_file( filename ) )
2494 return;
2495 }
2496
2497 /* Try server-wide custom error page. */
2498 (void) snprintf(
2499 filename, sizeof(filename), "%s/err%d.html", ERR_DIR, s );
2500 if ( send_error_file( filename ) )
2501 return;
2502
2503 /* Send built-in error page. */
2504 (void) snprintf(
2505 buf, sizeof(buf), "\
2506 <!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n\
2507 \n\
2508 <html>\n\
2509 \n\
2510 <head>\n\
2511 <meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\">\n\
2512 <title>%d %s</title>\n\
2513 </head>\n\
2514 \n\
2515 <body bgcolor=\"#cc9999\" text=\"#000000\" link=\"#2020ff\" vlink=\"#4040cc\">\n\
2516 \n\
2517 <h4>%d %s</h4>\n",
2518 s, title, s, title );
2519 add_to_response( buf );
2520 (void) snprintf( buf, sizeof(buf), "%s\n", text );
2521 add_to_response( buf );
2522 }
2523
2524
2525 static int
send_error_file(char * filename)2526 send_error_file( char* filename )
2527 {
2528 FILE* fp;
2529 char buf[1000];
2530 size_t r;
2531
2532 fp = fopen( filename, "r" );
2533 if ( fp == (FILE*) 0 )
2534 return 0;
2535 for (;;)
2536 {
2537 r = fread( buf, 1, sizeof(buf) - 1, fp );
2538 if ( r == 0 )
2539 break;
2540 buf[r] = '\0';
2541 add_to_response( buf );
2542 }
2543 (void) fclose( fp );
2544 return 1;
2545 }
2546
2547
2548 static void
send_error_tail(void)2549 send_error_tail( void )
2550 {
2551 char buf[500];
2552
2553 if ( match( "**MSIE**", useragent ) )
2554 {
2555 int n;
2556 (void) snprintf( buf, sizeof(buf), "<!--\n" );
2557 add_to_response( buf );
2558 for ( n = 0; n < 6; ++n )
2559 {
2560 (void) snprintf( buf, sizeof(buf), "Padding so that MSIE deigns to show this error instead of its own canned one.\n" );
2561 add_to_response( buf );
2562 }
2563 (void) snprintf( buf, sizeof(buf), "-->\n" );
2564 add_to_response( buf );
2565 }
2566
2567 (void) snprintf( buf, sizeof(buf), "\
2568 <hr>\n\
2569 \n\
2570 <address><a href=\"%s\">%s</a></address>\n\
2571 \n\
2572 </body>\n\
2573 \n\
2574 </html>\n",
2575 SERVER_URL, SERVER_SOFTWARE );
2576 add_to_response( buf );
2577 }
2578
2579
2580 static void
add_headers(int s,char * title,char * extra_header,char * me,char * mt,off_t b,time_t mod)2581 add_headers( int s, char* title, char* extra_header, char* me, char* mt, off_t b, time_t mod )
2582 {
2583 time_t now, expires;
2584 char timebuf[100];
2585 char buf[10000];
2586 int s100;
2587 const char* rfc1123_fmt = "%a, %d %b %Y %H:%M:%S GMT";
2588
2589 status = s;
2590 bytes = b;
2591 make_log_entry();
2592 start_response();
2593 (void) snprintf( buf, sizeof(buf), "%s %d %s\015\012", protocol, status, title );
2594 add_to_response( buf );
2595 (void) snprintf( buf, sizeof(buf), "Server: %s\015\012", SERVER_SOFTWARE );
2596 add_to_response( buf );
2597 now = time( (time_t*) 0 );
2598 (void) strftime( timebuf, sizeof(timebuf), rfc1123_fmt, gmtime( &now ) );
2599 (void) snprintf( buf, sizeof(buf), "Date: %s\015\012", timebuf );
2600 add_to_response( buf );
2601 s100 = status / 100;
2602 if ( s100 != 2 && s100 != 3 )
2603 {
2604 (void) snprintf( buf, sizeof(buf), "Cache-Control: no-cache,no-store\015\012" );
2605 add_to_response( buf );
2606 }
2607 if ( extra_header != (char*) 0 && extra_header[0] != '\0' )
2608 {
2609 (void) snprintf( buf, sizeof(buf), "%s\015\012", extra_header );
2610 add_to_response( buf );
2611 }
2612 if ( me != (char*) 0 && me[0] != '\0' )
2613 {
2614 (void) snprintf( buf, sizeof(buf), "Content-Encoding: %s\015\012", me );
2615 add_to_response( buf );
2616 }
2617 if ( mt != (char*) 0 && mt[0] != '\0' )
2618 {
2619 (void) snprintf( buf, sizeof(buf), "Content-Type: %s\015\012", mt );
2620 add_to_response( buf );
2621 }
2622 if ( bytes >= 0 )
2623 {
2624 (void) snprintf(
2625 buf, sizeof(buf), "Content-Length: %lld\015\012", (long long) bytes );
2626 add_to_response( buf );
2627 }
2628 if ( p3p != (char*) 0 && p3p[0] != '\0' )
2629 {
2630 (void) snprintf( buf, sizeof(buf), "P3P: %s\015\012", p3p );
2631 add_to_response( buf );
2632 }
2633 if ( max_age >= 0 )
2634 {
2635 expires = now + max_age;
2636 (void) strftime(
2637 timebuf, sizeof(timebuf), rfc1123_fmt, gmtime( &expires ) );
2638 (void) snprintf( buf, sizeof(buf),
2639 "Cache-Control: max-age=%d\015\012Expires: %s\015\012", max_age, timebuf );
2640 add_to_response( buf );
2641 }
2642 if ( mod != (time_t) -1 )
2643 {
2644 (void) strftime(
2645 timebuf, sizeof(timebuf), rfc1123_fmt, gmtime( &mod ) );
2646 (void) snprintf( buf, sizeof(buf), "Last-Modified: %s\015\012", timebuf );
2647 add_to_response( buf );
2648 }
2649 (void) snprintf( buf, sizeof(buf), "Connection: close\015\012\015\012" );
2650 add_to_response( buf );
2651 }
2652
2653
2654 static void
start_request(void)2655 start_request( void )
2656 {
2657 request_size = 0;
2658 request_idx = 0;
2659 }
2660
2661 static void
add_to_request(char * str,size_t len)2662 add_to_request( char* str, size_t len )
2663 {
2664 add_data( &request, &request_size, &request_len, str, len );
2665 }
2666
2667 static char*
get_request_line(void)2668 get_request_line( void )
2669 {
2670 int i;
2671 char c;
2672
2673 for ( i = request_idx; request_idx < request_len; ++request_idx )
2674 {
2675 c = request[request_idx];
2676 if ( c == '\012' || c == '\015' )
2677 {
2678 request[request_idx] = '\0';
2679 ++request_idx;
2680 if ( c == '\015' && request_idx < request_len &&
2681 request[request_idx] == '\012' )
2682 {
2683 request[request_idx] = '\0';
2684 ++request_idx;
2685 }
2686 return &(request[i]);
2687 }
2688 }
2689 return (char*) 0;
2690 }
2691
2692
2693 static char* response;
2694 static size_t response_size, response_len;
2695
2696 static void
start_response(void)2697 start_response( void )
2698 {
2699 response_size = 0;
2700 }
2701
2702 static void
add_to_response(char * str)2703 add_to_response( char* str )
2704 {
2705 add_str( &response, &response_size, &response_len, str );
2706 }
2707
2708 static void
send_response(void)2709 send_response( void )
2710 {
2711 (void) my_write( response, response_len );
2712 }
2713
2714
2715 static void
send_via_write(int fd,off_t size)2716 send_via_write( int fd, off_t size )
2717 {
2718 /* On some systems an off_t is 64 bits while a size_t is still only
2719 ** 32 bits. The mmap() system call takes a size_t as the length argument,
2720 ** so we can only use mmap() if the size will fit into a size_t.
2721 */
2722 if ( size <= SIZE_T_MAX )
2723 {
2724 size_t size_size = (size_t) size;
2725 unsigned char* ptr = mmap( 0, size_size, PROT_READ, MAP_PRIVATE, fd, 0 );
2726 if ( ptr != (unsigned char*) -1 )
2727 {
2728 unsigned char* p = ptr;
2729 size_t remaining_size = size_size;
2730 #ifdef MADV_SEQUENTIAL
2731 /* If we have madvise, might as well call it. Although sequential
2732 ** access is probably already the default.
2733 */
2734 (void) madvise( ptr, size_size, MADV_SEQUENTIAL );
2735 #endif /* MADV_SEQUENTIAL */
2736 /* We could send the whole file in a single write, but if
2737 ** it's huge then we run the risk of hitting the timeout.
2738 ** So we do a loop writing large segments, and reseting the
2739 ** timeout each time through.
2740 */
2741 while ( remaining_size > 0 )
2742 {
2743 size_t buf_size = MIN( remaining_size, MAX_SEND_BUFFER_SIZE );
2744 ssize_t r = my_write( p, buf_size );
2745 if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
2746 {
2747 sleep( 1 );
2748 continue;
2749 }
2750 if ( r != buf_size )
2751 return;
2752 remaining_size -= r;
2753 p += r;
2754 (void) alarm( WRITE_TIMEOUT );
2755 }
2756 (void) munmap( ptr, size_size );
2757 }
2758 }
2759 else
2760 {
2761 /* The file is too big for mmap, so we'll send it via read & write
2762 ** instead. This would be less efficient for small files because
2763 ** it bypasses the buffer cache, but with a file this huge the
2764 ** cache will get blown anyway.
2765 */
2766 char buf[30000];
2767
2768 for (;;)
2769 {
2770 ssize_t r = read( fd, buf, sizeof(buf) );
2771 if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
2772 {
2773 sleep( 1 );
2774 continue;
2775 }
2776 if ( r <= 0 )
2777 return;
2778 for (;;)
2779 {
2780 ssize_t r2 = my_write( buf, r );
2781 if ( r2 < 0 && ( errno == EINTR || errno == EAGAIN ) )
2782 {
2783 sleep( 1 );
2784 continue;
2785 }
2786 if ( r2 != r )
2787 return;
2788 break;
2789 }
2790 (void) alarm( WRITE_TIMEOUT );
2791 }
2792 }
2793 }
2794
2795
2796 static void
send_via_sendfile(int fd,int s,off_t size)2797 send_via_sendfile( int fd, int s, off_t size )
2798 {
2799 /* We could send the whole file in a single sendfile, but if
2800 ** it's huge then we run the risk of hitting the timeout.
2801 ** So we do a loop writing large segments, and reseting the
2802 ** timeout each time through.
2803 **
2804 ** This also avoids the problem of using sendfile on a file larger
2805 ** than 2GB, since each segment size will now fit into a size_t.
2806 */
2807 off_t remaining_size = size;
2808 off_t off = 0;
2809 while ( remaining_size > 0 )
2810 {
2811 size_t buf_size = MIN( remaining_size, MAX_SEND_BUFFER_SIZE );
2812 ssize_t r = my_sendfile( fd, s, off, buf_size );
2813 if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
2814 {
2815 sleep( 1 );
2816 continue;
2817 }
2818 if ( r != buf_size )
2819 return;
2820 remaining_size -= r;
2821 off += r;
2822 (void) alarm( WRITE_TIMEOUT );
2823 }
2824 }
2825
2826
2827 static ssize_t
my_read(char * buf,size_t size)2828 my_read( char* buf, size_t size )
2829 {
2830 #ifdef USE_SSL
2831 if ( do_ssl )
2832 return SSL_read( ssl, buf, size );
2833 else
2834 return read( conn_fd, buf, size );
2835 #else /* USE_SSL */
2836 return read( conn_fd, buf, size );
2837 #endif /* USE_SSL */
2838 }
2839
2840
2841 static ssize_t
my_write(void * buf,size_t size)2842 my_write( void* buf, size_t size )
2843 {
2844 #ifdef USE_SSL
2845 if ( do_ssl )
2846 return SSL_write( ssl, buf, size );
2847 else
2848 return write( conn_fd, buf, size );
2849 #else /* USE_SSL */
2850 return write( conn_fd, buf, size );
2851 #endif /* USE_SSL */
2852 }
2853
2854
2855 #ifdef HAVE_SENDFILE
2856 static ssize_t
my_sendfile(int fd,int s,off_t offset,size_t nbytes)2857 my_sendfile( int fd, int s, off_t offset, size_t nbytes )
2858 {
2859 #ifdef HAVE_LINUX_SENDFILE
2860 off_t lo = offset;
2861 return sendfile( s, fd, &lo, nbytes );
2862 #else /* HAVE_LINUX_SENDFILE */
2863 int r;
2864 r = sendfile( fd, s, offset, nbytes, (struct sf_hdtr*) 0, (off_t*) 0, 0 );
2865 if ( r == 0 )
2866 return nbytes;
2867 else
2868 return r;
2869 #endif /* HAVE_LINUX_SENDFILE */
2870 }
2871 #endif /* HAVE_SENDFILE */
2872
2873
2874 static void
add_str(char ** bufP,size_t * bufsizeP,size_t * buflenP,char * str)2875 add_str( char** bufP, size_t* bufsizeP, size_t* buflenP, char* str )
2876 {
2877 size_t len;
2878
2879 if ( str == (char*) 0 )
2880 len = 0;
2881 else
2882 len = strlen( str );
2883 add_data( bufP, bufsizeP, buflenP, str, len );
2884 }
2885
2886
2887 static void
add_data(char ** bufP,size_t * bufsizeP,size_t * buflenP,char * str,size_t len)2888 add_data( char** bufP, size_t* bufsizeP, size_t* buflenP, char* str, size_t len )
2889 {
2890 if ( *bufsizeP == 0 )
2891 {
2892 *bufsizeP = len + 500;
2893 *buflenP = 0;
2894 *bufP = (char*) e_malloc( *bufsizeP );
2895 }
2896 else if ( *buflenP + len >= *bufsizeP ) /* allow for NUL */
2897 {
2898 *bufsizeP = *buflenP + len + 500;
2899 *bufP = (char*) e_realloc( (void*) *bufP, *bufsizeP );
2900 }
2901
2902 if ( len > 0 )
2903 {
2904 (void) memmove( &((*bufP)[*buflenP]), str, len );
2905 *buflenP += len;
2906 }
2907
2908 (*bufP)[*buflenP] = '\0';
2909 }
2910
2911
2912 static void
make_log_entry(void)2913 make_log_entry( void )
2914 {
2915 char* ru;
2916 char url[500];
2917 char bytes_str[40];
2918 time_t now;
2919 struct tm* t;
2920 const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S";
2921 char date_nozone[100];
2922 int zone;
2923 char sign;
2924 char date[100];
2925
2926 if ( logfp == (FILE*) 0 )
2927 return;
2928
2929 /* Fill in some null values. */
2930 if ( protocol == (char*) 0 )
2931 protocol = "UNKNOWN";
2932 if ( path == (char*) 0 )
2933 path = "";
2934 if ( req_hostname == (char*) 0 )
2935 req_hostname = hostname;
2936
2937 /* Format the user. */
2938 if ( remoteuser != (char*) 0 )
2939 ru = remoteuser;
2940 else
2941 ru = "-";
2942 now = time( (time_t*) 0 );
2943 /* If we're vhosting, prepend the hostname to the url. This is
2944 ** a little weird, perhaps writing separate log files for
2945 ** each vhost would make more sense.
2946 */
2947 if ( vhost )
2948 (void) snprintf( url, sizeof(url), "/%s%s", req_hostname, path );
2949 else
2950 (void) snprintf( url, sizeof(url), "%s", path );
2951 /* Format the bytes. */
2952 if ( bytes >= 0 )
2953 (void) snprintf(
2954 bytes_str, sizeof(bytes_str), "%lld", (long long) bytes );
2955 else
2956 (void) strcpy( bytes_str, "-" );
2957 /* Format the time, forcing a numeric timezone (some log analyzers
2958 ** are stoooopid about this).
2959 */
2960 t = localtime( &now );
2961 (void) strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t );
2962 #ifdef HAVE_TM_GMTOFF
2963 zone = t->tm_gmtoff / 60L;
2964 #else
2965 zone = - ( timezone / 60L );
2966 /* Probably have to add something about daylight time here. */
2967 #endif
2968 if ( zone >= 0 )
2969 sign = '+';
2970 else
2971 {
2972 sign = '-';
2973 zone = -zone;
2974 }
2975 zone = ( zone / 60 ) * 100 + zone % 60;
2976 (void) snprintf( date, sizeof(date), "%s %c%04d", date_nozone, sign, zone );
2977 /* And write the log entry. */
2978 (void) fprintf( logfp,
2979 "%.80s - %.80s [%s] \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.200s\"\n",
2980 ntoa( &client_addr ), ru, date, get_method_str( method ), url,
2981 protocol, status, bytes_str, referrer, useragent );
2982 (void) fflush( logfp );
2983 }
2984
2985
2986 /* Returns if it's ok to serve the url, otherwise generates an error
2987 ** and exits.
2988 */
2989 static void
check_referrer(void)2990 check_referrer( void )
2991 {
2992 char* cp;
2993
2994 /* Are we doing referrer checking at all? */
2995 if ( url_pattern == (char*) 0 )
2996 return;
2997
2998 /* Is it ok? */
2999 if ( really_check_referrer() )
3000 return;
3001
3002 /* Lose. */
3003 if ( vhost && req_hostname != (char*) 0 )
3004 cp = req_hostname;
3005 else
3006 cp = hostname;
3007 if ( cp == (char*) 0 )
3008 cp = "";
3009 syslog(
3010 LOG_INFO, "%.80s non-local referrer \"%.80s%.80s\" \"%.80s\"",
3011 ntoa( &client_addr ), cp, path, referrer );
3012 send_error( 403, "Forbidden", "", "You must supply a local referrer." );
3013 }
3014
3015
3016 /* Returns 1 if ok to serve the url, 0 if not. */
3017 static int
really_check_referrer(void)3018 really_check_referrer( void )
3019 {
3020 char* cp1;
3021 char* cp2;
3022 char* cp3;
3023 char* refhost;
3024 char *lp;
3025
3026 /* Check for an empty referrer. */
3027 if ( referrer == (char*) 0 || referrer[0] == '\0' ||
3028 ( cp1 = strstr( referrer, "//" ) ) == (char*) 0 )
3029 {
3030 /* Disallow if we require a referrer and the url matches. */
3031 if ( no_empty_referrers && match( url_pattern, path ) )
3032 return 0;
3033 /* Otherwise ok. */
3034 return 1;
3035 }
3036
3037 /* Extract referrer host. */
3038 cp1 += 2;
3039 for ( cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2 )
3040 continue;
3041 refhost = (char*) e_malloc( cp2 - cp1 + 1 );
3042 for ( cp3 = refhost; cp1 < cp2; ++cp1, ++cp3 )
3043 if ( isupper(*cp1) )
3044 *cp3 = tolower(*cp1);
3045 else
3046 *cp3 = *cp1;
3047 *cp3 = '\0';
3048
3049 /* Local pattern? */
3050 if ( local_pattern != (char*) 0 )
3051 lp = local_pattern;
3052 else
3053 {
3054 /* No local pattern. What's our hostname? */
3055 if ( ! vhost )
3056 {
3057 /* Not vhosting, use the server name. */
3058 lp = hostname;
3059 if ( lp == (char*) 0 )
3060 /* Couldn't figure out local hostname - give up. */
3061 return 1;
3062 }
3063 else
3064 {
3065 /* We are vhosting, use the hostname on this connection. */
3066 lp = req_hostname;
3067 if ( lp == (char*) 0 )
3068 /* Oops, no hostname. Maybe it's an old browser that
3069 ** doesn't send a Host: header. We could figure out
3070 ** the default hostname for this IP address, but it's
3071 ** not worth it for the few requests like this.
3072 */
3073 return 1;
3074 }
3075 }
3076
3077 /* If the referrer host doesn't match the local host pattern, and
3078 ** the URL does match the url pattern, it's an illegal reference.
3079 */
3080 if ( ! match( lp, refhost ) && match( url_pattern, path ) )
3081 return 0;
3082 /* Otherwise ok. */
3083 return 1;
3084 }
3085
3086
3087 static char*
get_method_str(int m)3088 get_method_str( int m )
3089 {
3090 switch ( m )
3091 {
3092 case METHOD_GET: return "GET";
3093 case METHOD_HEAD: return "HEAD";
3094 case METHOD_POST: return "POST";
3095 case METHOD_PUT: return "PUT";
3096 case METHOD_DELETE: return "DELETE";
3097 case METHOD_TRACE: return "TRACE";
3098 default: return "UNKNOWN";
3099 }
3100 }
3101
3102
3103 struct mime_entry {
3104 char* ext;
3105 size_t ext_len;
3106 char* val;
3107 size_t val_len;
3108 };
3109 static struct mime_entry enc_tab[] = {
3110 #include "mime_encodings.h"
3111 };
3112 static const int n_enc_tab = sizeof(enc_tab) / sizeof(*enc_tab);
3113 static struct mime_entry typ_tab[] = {
3114 #include "mime_types.h"
3115 };
3116 static const int n_typ_tab = sizeof(typ_tab) / sizeof(*typ_tab);
3117
3118
3119 /* qsort comparison routine */
3120 static int
ext_compare(const void * v1,const void * v2)3121 ext_compare( const void* v1, const void* v2 )
3122 {
3123 const struct mime_entry* m1 = (const struct mime_entry*) v1;
3124 const struct mime_entry* m2 = (const struct mime_entry*) v2;
3125 return strcmp( m1->ext, m2->ext );
3126 }
3127
3128
3129 static void
init_mime(void)3130 init_mime( void )
3131 {
3132 int i;
3133
3134 /* Sort the tables so we can do binary search. */
3135 qsort( enc_tab, n_enc_tab, sizeof(*enc_tab), ext_compare );
3136 qsort( typ_tab, n_typ_tab, sizeof(*typ_tab), ext_compare );
3137
3138 /* Fill in the lengths. */
3139 for ( i = 0; i < n_enc_tab; ++i )
3140 {
3141 enc_tab[i].ext_len = strlen( enc_tab[i].ext );
3142 enc_tab[i].val_len = strlen( enc_tab[i].val );
3143 }
3144 for ( i = 0; i < n_typ_tab; ++i )
3145 {
3146 typ_tab[i].ext_len = strlen( typ_tab[i].ext );
3147 typ_tab[i].val_len = strlen( typ_tab[i].val );
3148 }
3149 }
3150
3151
3152 /* Figure out MIME encodings and type based on the filename. Multiple
3153 ** encodings are separated by commas, and are listed in the order in
3154 ** which they were applied to the file.
3155 */
3156 static const char*
figure_mime(char * name,char * me,size_t me_size)3157 figure_mime( char* name, char* me, size_t me_size )
3158 {
3159 char* prev_dot;
3160 char* dot;
3161 char* ext;
3162 int me_indexes[100], n_me_indexes;
3163 size_t ext_len, me_len;
3164 int i, top, bot, mid;
3165 int r;
3166 const char* default_type = "text/plain; charset=%s";
3167 const char* type;
3168
3169 /* Peel off encoding extensions until there aren't any more. */
3170 n_me_indexes = 0;
3171 for ( prev_dot = &name[strlen(name)]; ; prev_dot = dot )
3172 {
3173 for ( dot = prev_dot - 1; dot >= name && *dot != '.'; --dot )
3174 ;
3175 if ( dot < name )
3176 {
3177 /* No dot found. No more encoding extensions, and no type
3178 ** extension either.
3179 */
3180 type = default_type;
3181 goto done;
3182 }
3183 ext = dot + 1;
3184 ext_len = prev_dot - ext;
3185 /* Search the encodings table. Linear search is fine here, there
3186 ** are only a few entries.
3187 */
3188 for ( i = 0; i < n_enc_tab; ++i )
3189 {
3190 if ( ext_len == enc_tab[i].ext_len && strncasecmp( ext, enc_tab[i].ext, ext_len ) == 0 )
3191 {
3192 if ( n_me_indexes < sizeof(me_indexes)/sizeof(*me_indexes) )
3193 {
3194 me_indexes[n_me_indexes] = i;
3195 ++n_me_indexes;
3196 }
3197 goto next;
3198 }
3199 }
3200 /* No encoding extension found. Break and look for a type extension. */
3201 break;
3202
3203 next: ;
3204 }
3205
3206 /* Binary search for a matching type extension. */
3207 top = n_typ_tab - 1;
3208 bot = 0;
3209 while ( top >= bot )
3210 {
3211 mid = ( top + bot ) / 2;
3212 r = strncasecmp( ext, typ_tab[mid].ext, ext_len );
3213 if ( r < 0 )
3214 top = mid - 1;
3215 else if ( r > 0 )
3216 bot = mid + 1;
3217 else
3218 if ( ext_len < typ_tab[mid].ext_len )
3219 top = mid - 1;
3220 else if ( ext_len > typ_tab[mid].ext_len )
3221 bot = mid + 1;
3222 else
3223 {
3224 type = typ_tab[mid].val;
3225 goto done;
3226 }
3227 }
3228 type = default_type;
3229
3230 done:
3231
3232 /* The last thing we do is actually generate the mime-encoding header. */
3233 me[0] = '\0';
3234 me_len = 0;
3235 for ( i = n_me_indexes - 1; i >= 0; --i )
3236 {
3237 if ( me_len + enc_tab[me_indexes[i]].val_len + 1 < me_size )
3238 {
3239 if ( me[0] != '\0' )
3240 {
3241 (void) strcpy( &me[me_len], "," );
3242 ++me_len;
3243 }
3244 (void) strcpy( &me[me_len], enc_tab[me_indexes[i]].val );
3245 me_len += enc_tab[me_indexes[i]].val_len;
3246 }
3247 }
3248
3249 return type;
3250 }
3251
3252
3253 static void
handle_sigterm(int sig)3254 handle_sigterm( int sig )
3255 {
3256 /* Don't need to set up the handler again, since it's a one-shot. */
3257
3258 syslog( LOG_NOTICE, "exiting due to signal %d", sig );
3259 (void) fprintf( stderr, "%s: exiting due to signal %d\n", argv0, sig );
3260 closelog();
3261 exit( 1 );
3262 }
3263
3264
3265 /* SIGHUP says to re-open the log file. */
3266 static void
handle_sighup(int sig)3267 handle_sighup( int sig )
3268 {
3269 const int oerrno = errno;
3270
3271 #ifndef HAVE_SIGSET
3272 /* Set up handler again. */
3273 (void) signal( SIGHUP, handle_sighup );
3274 #endif /* ! HAVE_SIGSET */
3275
3276 /* Just set a flag that we got the signal. */
3277 got_hup = 1;
3278
3279 /* Restore previous errno. */
3280 errno = oerrno;
3281 }
3282
3283
3284 static void
handle_sigchld(int sig)3285 handle_sigchld( int sig )
3286 {
3287 const int oerrno = errno;
3288 pid_t pid;
3289 int s;
3290
3291 #ifndef HAVE_SIGSET
3292 /* Set up handler again. */
3293 (void) signal( SIGCHLD, handle_sigchld );
3294 #endif /* ! HAVE_SIGSET */
3295
3296 /* Reap defunct children until there aren't any more. */
3297 for (;;)
3298 {
3299 #ifdef HAVE_WAITPID
3300 pid = waitpid( (pid_t) -1, &s, WNOHANG );
3301 #else /* HAVE_WAITPID */
3302 pid = wait3( &s, WNOHANG, (struct rusage*) 0 );
3303 #endif /* HAVE_WAITPID */
3304 if ( (int) pid == 0 ) /* none left */
3305 break;
3306 if ( (int) pid < 0 )
3307 {
3308 if ( errno == EINTR || errno == EAGAIN )
3309 continue;
3310 /* ECHILD shouldn't happen with the WNOHANG option,
3311 ** but with some kernels it does anyway. Ignore it.
3312 */
3313 if ( errno != ECHILD )
3314 {
3315 syslog( LOG_ERR, "child wait - %m" );
3316 perror( "child wait" );
3317 }
3318 break;
3319 }
3320 }
3321
3322 /* Restore previous errno. */
3323 errno = oerrno;
3324 }
3325
3326
3327 static void
re_open_logfile(void)3328 re_open_logfile( void )
3329 {
3330 if ( logfp != (FILE*) 0 )
3331 {
3332 (void) fclose( logfp );
3333 logfp = (FILE*) 0;
3334 }
3335 if ( logfile != (char*) 0 )
3336 {
3337 syslog( LOG_NOTICE, "re-opening logfile" );
3338 logfp = fopen( logfile, "a" );
3339 if ( logfp == (FILE*) 0 )
3340 {
3341 syslog( LOG_CRIT, "%s - %m", logfile );
3342 perror( logfile );
3343 exit( 1 );
3344 }
3345 }
3346 }
3347
3348
3349 static void
handle_read_timeout(int sig)3350 handle_read_timeout( int sig )
3351 {
3352 syslog( LOG_INFO, "%.80s connection timed out reading", ntoa( &client_addr ) );
3353 send_error(
3354 408, "Request Timeout", "",
3355 "No request appeared within a reasonable time period." );
3356 }
3357
3358
3359 static void
handle_write_timeout(int sig)3360 handle_write_timeout( int sig )
3361 {
3362 syslog( LOG_INFO, "%.80s connection timed out writing", ntoa( &client_addr ) );
3363 finish_request( 1 );
3364 }
3365
3366
3367
3368 static void
lookup_hostname(usockaddr * usa4P,size_t sa4_len,int * gotv4P,usockaddr * usa6P,size_t sa6_len,int * gotv6P)3369 lookup_hostname( usockaddr* usa4P, size_t sa4_len, int* gotv4P, usockaddr* usa6P, size_t sa6_len, int* gotv6P )
3370 {
3371 #ifdef USE_IPV6
3372
3373 struct addrinfo hints;
3374 char portstr[10];
3375 int gaierr;
3376 struct addrinfo* ai;
3377 struct addrinfo* ai2;
3378 struct addrinfo* aiv6;
3379 struct addrinfo* aiv4;
3380
3381 (void) memset( &hints, 0, sizeof(hints) );
3382 hints.ai_family = PF_UNSPEC;
3383 hints.ai_flags = AI_PASSIVE;
3384 hints.ai_socktype = SOCK_STREAM;
3385 (void) snprintf( portstr, sizeof(portstr), "%d", (int) port );
3386 if ( (gaierr = getaddrinfo( hostname, portstr, &hints, &ai )) != 0 )
3387 {
3388 syslog(
3389 LOG_CRIT, "getaddrinfo %.80s - %s", hostname,
3390 gai_strerror( gaierr ) );
3391 (void) fprintf(
3392 stderr, "%s: getaddrinfo %.80s - %s\n", argv0, hostname,
3393 gai_strerror( gaierr ) );
3394 exit( 1 );
3395 }
3396
3397 /* Find the first IPv6 and IPv4 entries. */
3398 aiv6 = (struct addrinfo*) 0;
3399 aiv4 = (struct addrinfo*) 0;
3400 for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
3401 {
3402 switch ( ai2->ai_family )
3403 {
3404 case AF_INET6:
3405 if ( aiv6 == (struct addrinfo*) 0 )
3406 aiv6 = ai2;
3407 break;
3408 case AF_INET:
3409 if ( aiv4 == (struct addrinfo*) 0 )
3410 aiv4 = ai2;
3411 break;
3412 }
3413 }
3414
3415 if ( aiv6 == (struct addrinfo*) 0 )
3416 *gotv6P = 0;
3417 else
3418 {
3419 if ( sa6_len < aiv6->ai_addrlen )
3420 {
3421 syslog(
3422 LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)",
3423 hostname, (unsigned long) sa6_len,
3424 (unsigned long) aiv6->ai_addrlen );
3425 (void) fprintf(
3426 stderr, "%s: %.80s - sockaddr too small (%lu < %lu)\n",
3427 argv0, hostname, (unsigned long) sa6_len,
3428 (unsigned long) aiv6->ai_addrlen );
3429 exit( 1 );
3430 }
3431 (void) memset( usa6P, 0, sa6_len );
3432 (void) memmove( usa6P, aiv6->ai_addr, aiv6->ai_addrlen );
3433 *gotv6P = 1;
3434 }
3435
3436 if ( aiv4 == (struct addrinfo*) 0 )
3437 *gotv4P = 0;
3438 else
3439 {
3440 if ( sa4_len < aiv4->ai_addrlen )
3441 {
3442 syslog(
3443 LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)",
3444 hostname, (unsigned long) sa4_len,
3445 (unsigned long) aiv4->ai_addrlen );
3446 (void) fprintf(
3447 stderr, "%s: %.80s - sockaddr too small (%lu < %lu)\n",
3448 argv0, hostname, (unsigned long) sa4_len,
3449 (unsigned long) aiv4->ai_addrlen );
3450 exit( 1 );
3451 }
3452 (void) memset( usa4P, 0, sa4_len );
3453 (void) memmove( usa4P, aiv4->ai_addr, aiv4->ai_addrlen );
3454 *gotv4P = 1;
3455 }
3456
3457 freeaddrinfo( ai );
3458
3459 #else /* USE_IPV6 */
3460
3461 struct hostent* he;
3462
3463 *gotv6P = 0;
3464
3465 (void) memset( usa4P, 0, sa4_len );
3466 usa4P->sa.sa_family = AF_INET;
3467 if ( hostname == (char*) 0 )
3468 usa4P->sa_in.sin_addr.s_addr = htonl( INADDR_ANY );
3469 else
3470 {
3471 usa4P->sa_in.sin_addr.s_addr = inet_addr( hostname );
3472 if ( (int) usa4P->sa_in.sin_addr.s_addr == -1 )
3473 {
3474 he = gethostbyname( hostname );
3475 if ( he == (struct hostent*) 0 )
3476 {
3477 #ifdef HAVE_HSTRERROR
3478 syslog(
3479 LOG_CRIT, "gethostbyname %.80s - %s", hostname,
3480 hstrerror( h_errno ) );
3481 (void) fprintf(
3482 stderr, "%s: gethostbyname %.80s - %s\n", argv0, hostname,
3483 hstrerror( h_errno ) );
3484 #else /* HAVE_HSTRERROR */
3485 syslog( LOG_CRIT, "gethostbyname %.80s failed", hostname );
3486 (void) fprintf(
3487 stderr, "%s: gethostbyname %.80s failed\n", argv0,
3488 hostname );
3489 #endif /* HAVE_HSTRERROR */
3490 exit( 1 );
3491 }
3492 if ( he->h_addrtype != AF_INET )
3493 {
3494 syslog( LOG_CRIT, "%.80s - non-IP network address", hostname );
3495 (void) fprintf(
3496 stderr, "%s: %.80s - non-IP network address\n", argv0,
3497 hostname );
3498 exit( 1 );
3499 }
3500 (void) memmove(
3501 &usa4P->sa_in.sin_addr.s_addr, he->h_addr, he->h_length );
3502 }
3503 }
3504 usa4P->sa_in.sin_port = htons( port );
3505 *gotv4P = 1;
3506
3507 #endif /* USE_IPV6 */
3508 }
3509
3510
3511 static char*
ntoa(usockaddr * usaP)3512 ntoa( usockaddr* usaP )
3513 {
3514 #ifdef USE_IPV6
3515 static char str[200];
3516
3517 if ( getnameinfo( &usaP->sa, sockaddr_len( usaP ), str, sizeof(str), 0, 0, NI_NUMERICHOST ) != 0 )
3518 {
3519 str[0] = '?';
3520 str[1] = '\0';
3521 }
3522 else if ( IN6_IS_ADDR_V4MAPPED( &usaP->sa_in6.sin6_addr ) && strncmp( str, "::ffff:", 7 ) == 0 )
3523 /* Elide IPv6ish prefix for IPv4 addresses. */
3524 (void) ol_strcpy( str, &str[7] );
3525
3526 return str;
3527
3528 #else /* USE_IPV6 */
3529
3530 return inet_ntoa( usaP->sa_in.sin_addr );
3531
3532 #endif /* USE_IPV6 */
3533 }
3534
3535
3536 static int
sockaddr_check(usockaddr * usaP)3537 sockaddr_check( usockaddr* usaP )
3538 {
3539 switch ( usaP->sa.sa_family )
3540 {
3541 case AF_INET: return 1;
3542 #ifdef USE_IPV6
3543 case AF_INET6: return 1;
3544 #endif /* USE_IPV6 */
3545 default:
3546 return 0;
3547 }
3548 }
3549
3550
3551 static size_t
sockaddr_len(usockaddr * usaP)3552 sockaddr_len( usockaddr* usaP )
3553 {
3554 switch ( usaP->sa.sa_family )
3555 {
3556 case AF_INET: return sizeof(struct sockaddr_in);
3557 #ifdef USE_IPV6
3558 case AF_INET6: return sizeof(struct sockaddr_in6);
3559 #endif /* USE_IPV6 */
3560 default:
3561 return 0; /* shouldn't happen */
3562 }
3563 }
3564
3565
3566 /* Copies and decodes a string. It's ok for from and to to be the
3567 ** same string.
3568 */
3569 static void
strdecode(char * to,char * from)3570 strdecode( char* to, char* from )
3571 {
3572 for ( ; *from != '\0'; ++to, ++from )
3573 {
3574 if ( from[0] == '%' && isxdigit( from[1] ) && isxdigit( from[2] ) )
3575 {
3576 *to = hexit( from[1] ) * 16 + hexit( from[2] );
3577 from += 2;
3578 }
3579 else
3580 *to = *from;
3581 }
3582 *to = '\0';
3583 }
3584
3585
3586 static int
hexit(char c)3587 hexit( char c )
3588 {
3589 if ( c >= '0' && c <= '9' )
3590 return c - '0';
3591 if ( c >= 'a' && c <= 'f' )
3592 return c - 'a' + 10;
3593 if ( c >= 'A' && c <= 'F' )
3594 return c - 'A' + 10;
3595 return 0; /* shouldn't happen, we're guarded by isxdigit() */
3596 }
3597
3598
3599 /* Base-64 decoding. This represents binary data as printable ASCII
3600 ** characters. Three 8-bit binary bytes are turned into four 6-bit
3601 ** values, like so:
3602 **
3603 ** [11111111] [22222222] [33333333]
3604 **
3605 ** [111111] [112222] [222233] [333333]
3606 **
3607 ** Then the 6-bit values are represented using the characters "A-Za-z0-9+/".
3608 */
3609
3610 static int b64_decode_table[256] = {
3611 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
3612 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
3613 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
3614 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
3615 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
3616 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
3617 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
3618 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
3619 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
3620 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
3621 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
3622 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
3623 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
3624 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
3625 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
3626 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
3627 };
3628
3629 /* Do base-64 decoding on a string. Ignore any non-base64 bytes.
3630 ** Return the actual number of bytes generated. The decoded size will
3631 ** be at most 3/4 the size of the encoded, and may be smaller if there
3632 ** are padding characters (blanks, newlines).
3633 */
3634 static int
b64_decode(const char * str,unsigned char * space,int size)3635 b64_decode( const char* str, unsigned char* space, int size )
3636 {
3637 const char* cp;
3638 int space_idx, phase;
3639 int d, prev_d = 0;
3640 unsigned char c;
3641
3642 space_idx = 0;
3643 phase = 0;
3644 for ( cp = str; *cp != '\0'; ++cp )
3645 {
3646 d = b64_decode_table[(int) ((unsigned char) *cp)];
3647 if ( d != -1 )
3648 {
3649 switch ( phase )
3650 {
3651 case 0:
3652 ++phase;
3653 break;
3654 case 1:
3655 c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) );
3656 if ( space_idx < size )
3657 space[space_idx++] = c;
3658 ++phase;
3659 break;
3660 case 2:
3661 c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) );
3662 if ( space_idx < size )
3663 space[space_idx++] = c;
3664 ++phase;
3665 break;
3666 case 3:
3667 c = ( ( ( prev_d & 0x03 ) << 6 ) | d );
3668 if ( space_idx < size )
3669 space[space_idx++] = c;
3670 phase = 0;
3671 break;
3672 }
3673 prev_d = d;
3674 }
3675 }
3676 return space_idx;
3677 }
3678
3679
3680 /* Set NDELAY mode on a socket. */
3681 static void
set_ndelay(int fd)3682 set_ndelay( int fd )
3683 {
3684 int flags, newflags;
3685
3686 flags = fcntl( fd, F_GETFL, 0 );
3687 if ( flags != -1 )
3688 {
3689 newflags = flags | (int) O_NDELAY;
3690 if ( newflags != flags )
3691 (void) fcntl( fd, F_SETFL, newflags );
3692 }
3693 }
3694
3695
3696 /* Clear NDELAY mode on a socket. */
3697 static void
clear_ndelay(int fd)3698 clear_ndelay( int fd )
3699 {
3700 int flags, newflags;
3701
3702 flags = fcntl( fd, F_GETFL, 0 );
3703 if ( flags != -1 )
3704 {
3705 newflags = flags & ~ (int) O_NDELAY;
3706 if ( newflags != flags )
3707 (void) fcntl( fd, F_SETFL, newflags );
3708 }
3709 }
3710
3711
3712 static void*
e_malloc(size_t size)3713 e_malloc( size_t size )
3714 {
3715 void* ptr;
3716
3717 ptr = malloc( size );
3718 if ( ptr == (void*) 0 )
3719 {
3720 syslog( LOG_CRIT, "out of memory" );
3721 (void) fprintf( stderr, "%s: out of memory\n", argv0 );
3722 exit( 1 );
3723 }
3724 return ptr;
3725 }
3726
3727
3728 static void*
e_realloc(void * optr,size_t size)3729 e_realloc( void* optr, size_t size )
3730 {
3731 void* ptr;
3732
3733 ptr = realloc( optr, size );
3734 if ( ptr == (void*) 0 )
3735 {
3736 syslog( LOG_CRIT, "out of memory" );
3737 (void) fprintf( stderr, "%s: out of memory\n", argv0 );
3738 exit( 1 );
3739 }
3740 return ptr;
3741 }
3742
3743
3744 static char*
e_strdup(char * ostr)3745 e_strdup( char* ostr )
3746 {
3747 char* str;
3748
3749 str = strdup( ostr );
3750 if ( str == (char*) 0 )
3751 {
3752 syslog( LOG_CRIT, "out of memory copying a string" );
3753 (void) fprintf( stderr, "%s: out of memory copying a string\n", argv0 );
3754 exit( 1 );
3755 }
3756 return str;
3757 }
3758
3759
3760 #ifdef NO_SNPRINTF
3761 /* Some systems don't have snprintf(), so we make our own that uses
3762 ** vsprintf(). This workaround is probably vulnerable to buffer overruns,
3763 ** so upgrade your OS!
3764 */
3765 static int
snprintf(char * str,size_t size,const char * format,...)3766 snprintf( char* str, size_t size, const char* format, ... )
3767 {
3768 va_list ap;
3769 int r;
3770
3771 va_start( ap, format );
3772 r = vsprintf( str, format, ap );
3773 va_end( ap );
3774 return r;
3775 }
3776 #endif /* NO_SNPRINTF */
3777