1 /* mini_httpd - small HTTP server
3 ** Copyright © 1999,2000 by Jef Poskanzer <jef@acme.com>.
4 ** All rights reserved.
6 ** Redistribution and use in source and binary forms, with or without
7 ** modification, are permitted provided that the following conditions
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.
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
37 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
53 #include <openssl/ssl.h>
57 #define ERR_DIR "errors"
58 #define DEFAULT_HTTP_PORT 80
60 #define DEFAULT_HTTPS_PORT 443
62 #define CERT_FILE "/etc/config/cert.pem"
63 #define KEY_FILE "/etc/config/key.pem"
65 #define CERT_FILE "cert.pem"
66 #define KEY_FILE "key.pem"
69 #define DEFAULT_USER "nobody"
71 #define CGI_PATH "/usr/local/bin:/usr/ucb:/bin:/usr/bin"
72 #define CGI_LD_LIBRARY_PATH "/usr/local/lib:/usr/lib"
73 #define AUTH_FILE ".htpasswd"
80 #define MIN(a,b) ((a) < (b) ? (a) : (b))
84 /* A multi-family sockaddr. */
87 struct sockaddr_in sa_in;
88 #ifdef HAVE_SOCKADDR_IN6
89 struct sockaddr_in6 sa_in6;
90 #endif /* HAVE_SOCKADDR_IN6 */
91 #ifdef HAVE_SOCKADDR_STORAGE
92 struct sockaddr_storage sa_stor;
93 #endif /* HAVE_SOCKADDR_STORAGE */
100 static int do_chroot;
103 static char* cgi_pattern;
104 static char* hostname;
105 static char hostname_buf[500];
106 static u_int hostaddr;
107 static char* logfile;
108 static char* pidfile;
109 static char* charset;
111 static int listen4_fd, listen6_fd;
114 static SSL_CTX* ssl_ctx;
118 /* Request variables. */
123 static usockaddr client_addr;
124 static char* request;
125 static int request_size, request_len, request_idx;
131 static char* protocol;
134 static char* req_hostname;
136 static char* authorization;
137 static long content_length;
138 static char* content_type;
141 static time_t if_modified_since;
142 static char* referer;
143 static char* useragent;
145 static char* remoteuser;
149 static void usage( void );
150 static int initialize_listen_socket( usockaddr* usaP );
151 static void handle_request( void );
152 static void de_dotdot( char* file );
153 static void do_file( void );
154 static void do_dir( void );
155 static void do_cgi( void );
156 static void cgi_interpose_input( int wfd );
157 static void cgi_interpose_output( int rfd, int parse_headers );
158 static char** make_argp( void );
159 static char** make_envp( void );
160 static char* build_env( char* fmt, char* arg );
161 static void auth_check( char* dirname );
162 static void send_authenticate( char* realm );
163 static char* virtual_file( char* file );
164 static void send_error( int s, char* title, char* extra_header, char* text );
165 static void send_error_body( int s, char* title, char* text );
166 static int send_error_file( char* filename );
167 static void send_error_tail( void );
168 static void add_headers( int s, char* title, char* extra_header, char* mime_type, long b, time_t mod );
169 static void start_request( void );
170 static void add_to_request( char* str, int len );
171 static char* get_request_line( void );
172 static void start_response( void );
173 static void add_to_response( char* str, int len );
174 static void send_response( void );
175 static int my_read( char* buf, int size );
176 static int my_write( char* buf, int size );
177 static void add_to_buf( char** bufP, int* bufsizeP, int* buflenP, char* str, int len );
178 static void make_log_entry( void );
179 static char* get_method_str( int m );
180 static char* get_mime_type( char* name );
181 static void handle_sigterm( int sig );
182 static void handle_sigchld( int sig );
183 static void lookup_hostname( usockaddr* usa4P, size_t sa4_len, int* gotv4P, usockaddr* usa6P, size_t sa6_len, int* gotv6P );
184 static char* ntoa( usockaddr* usaP );
185 static size_t sockaddr_len( usockaddr* usaP );
186 static void strdecode( char* to, char* from );
187 static int hexit( char c );
188 static int b64_decode( const char* str, unsigned char* space, int size );
189 static int match( const char* pattern, const char* string );
190 static int match_one( const char* pattern, int patternlen, const char* string );
194 main( int argc, char** argv )
198 usockaddr host_addr4;
199 usockaddr host_addr6;
212 cgi_pattern = (char*) 0;
213 charset = "iso-8859-1";
215 hostname = (char*) 0;
223 while ( argn < argc && argv[argn][0] == '-' )
225 if ( strcmp( argv[argn], "-D" ) == 0 )
228 else if ( strcmp( argv[argn], "-S" ) == 0 )
231 else if ( strcmp( argv[argn], "-p" ) == 0 && argn + 1 < argc )
234 port = atoi( argv[argn] );
236 else if ( strcmp( argv[argn], "-c" ) == 0 && argn + 1 < argc )
239 cgi_pattern = argv[argn];
241 else if ( strcmp( argv[argn], "-u" ) == 0 && argn + 1 < argc )
246 else if ( strcmp( argv[argn], "-h" ) == 0 && argn + 1 < argc )
249 hostname = argv[argn];
251 else if ( strcmp( argv[argn], "-r" ) == 0 )
253 else if ( strcmp( argv[argn], "-v" ) == 0 )
255 else if ( strcmp( argv[argn], "-l" ) == 0 && argn + 1 < argc )
258 logfile = argv[argn];
260 else if ( strcmp( argv[argn], "-i" ) == 0 && argn + 1 < argc )
263 pidfile = argv[argn];
265 else if ( strcmp( argv[argn], "-T" ) == 0 && argn + 1 < argc )
268 charset = argv[argn];
281 port = DEFAULT_HTTPS_PORT;
283 port = DEFAULT_HTTP_PORT;
285 port = DEFAULT_HTTP_PORT;
289 if ( logfile != (char*) 0 )
291 /* Open the log file. */
292 logfp = fopen( logfile, "a" );
293 if ( logfp == (FILE*) 0 )
300 /* Look up hostname. */
302 &host_addr4, sizeof(host_addr4), &gotv4,
303 &host_addr6, sizeof(host_addr6), &gotv6 );
304 if ( hostname == (char*) 0 )
306 (void) gethostname( hostname_buf, sizeof(hostname_buf) );
307 hostname = hostname_buf;
309 if ( ! ( gotv4 || gotv6 ) )
311 (void) fprintf( stderr, "can't find any valid address\n" );
315 /* Initialize listen sockets. Try v6 first because of a Linux peculiarity;
316 ** unlike other systems, it has magical v6 sockets that also listen for v4,
317 ** but if you bind a v4 socket first then the v6 bind fails.
320 listen6_fd = initialize_listen_socket( &host_addr6 );
324 listen4_fd = initialize_listen_socket( &host_addr4 );
327 /* If we didn't get any valid sockets, fail. */
328 if ( listen4_fd == -1 && listen6_fd == -1 )
330 (void) fprintf( stderr, "can't bind to any address\n" );
337 SSLeay_add_ssl_algorithms();
338 SSL_load_error_strings();
339 ssl_ctx = SSL_CTX_new( SSLv23_server_method() );
340 if ( SSL_CTX_use_certificate_file( ssl_ctx, CERT_FILE, SSL_FILETYPE_PEM ) == 0 ||
341 SSL_CTX_use_PrivateKey_file( ssl_ctx, KEY_FILE, SSL_FILETYPE_PEM ) == 0 ||
342 SSL_CTX_check_private_key( ssl_ctx ) == 0 )
344 ERR_print_errors_fp( stderr );
352 /* Make ourselves a daemon. */
354 if ( daemon( 1, 1 ) < 0 )
377 /* Even if we don't daemonize, we still want to disown our parent
382 #endif /* HAVE_SETSID */
385 if ( pidfile != (char*) 0 )
387 /* Write the PID file. */
388 FILE* pidfp = fopen( pidfile, "w" );
389 if ( pidfp == (FILE*) 0 )
394 (void) fprintf( pidfp, "%d\n", (int) getpid() );
395 (void) fclose( pidfp );
398 /* Read zone info now, in case we chroot(). */
402 /* If we're root, start becoming someone else. */
406 pwd = getpwnam( user );
407 if ( pwd == (struct passwd*) 0 )
409 (void) fprintf( stderr, "%s: unknown user - '%s'\n", argv0, user );
412 /* Set aux groups to null. */
413 if ( setgroups( 0, (const gid_t*) 0 ) < 0 )
415 perror( "setgroups" );
418 /* Set primary group. */
419 if ( setgid( pwd->pw_gid ) < 0 )
424 /* Try setting aux groups correctly - not critical if this fails. */
425 if ( initgroups( user, pwd->pw_gid ) < 0 )
426 perror( "initgroups" );
428 /* Set login name. */
429 (void) setlogin( user );
430 #endif /* HAVE_SETLOGIN */
431 /* Save the new uid for setting after we chroot(). */
436 /* Chroot if requested. */
440 (void) getcwd( cwd, sizeof(cwd) - 1 );
441 if ( chroot( cwd ) < 0 )
446 /* Always chdir to / after a chroot. */
447 if ( chdir( "/" ) < 0 )
449 perror( "chroot chdir" );
456 /* If we're root, become someone else. */
460 if ( setuid( uid ) < 0 )
465 /* Check for unnecessary security exposure. */
467 (void) fprintf( stderr,
468 "%s: started as root without requesting chroot(), warning only\n", argv0 );
472 /* Catch various termination signals. */
473 (void) signal( SIGTERM, handle_sigterm );
474 (void) signal( SIGINT, handle_sigterm );
475 (void) signal( SIGHUP, handle_sigterm );
476 (void) signal( SIGUSR1, handle_sigterm );
478 /* Catch defunct children. */
479 (void) signal( SIGCHLD, handle_sigchld );
481 /* And get EPIPE instead of SIGPIPE. */
482 (void) signal( SIGPIPE, SIG_IGN );
487 /* Possibly do a select() on the possible two listen fds. */
490 if ( listen4_fd != -1 )
492 FD_SET( listen4_fd, &afdset );
493 if ( listen4_fd > maxfd )
496 if ( listen6_fd != -1 )
498 FD_SET( listen6_fd, &afdset );
499 if ( listen6_fd > maxfd )
502 if ( listen4_fd != -1 && listen6_fd != -1 )
503 if ( select( maxfd + 1, &afdset, (fd_set*) 0, (fd_set*) 0, (struct timeval*) 0 ) < 0 )
508 /* (If we don't have two listen fds, we can just skip the select()
509 ** and fall through. Whichever listen fd we do have will do a
510 ** blocking accept() instead.)
513 /* Accept the new connection. */
515 if ( listen4_fd != -1 && FD_ISSET( listen4_fd, &afdset ) )
516 conn_fd = accept( listen4_fd, &usa.sa, &sz );
517 else if ( listen6_fd != -1 && FD_ISSET( listen6_fd, &afdset ) )
518 conn_fd = accept( listen6_fd, &usa.sa, &sz );
521 (void) fprintf( stderr, "%s: select failure\n", argv0 );
526 if ( errno == EINTR )
527 continue; /* try again */
532 /* Fork a sub-process to handle the connection. */
543 if ( listen4_fd != -1 )
544 (void) close( listen4_fd );
545 if ( listen6_fd != -1 )
546 (void) close( listen6_fd );
550 (void) close( conn_fd );
559 (void) fprintf( stderr, "usage: %s [-D] [-S] [-p port] [-c cgipat] [-u user] [-h hostname] [-r] [-v] [-l logfile] [-i pidfile] [-T charset]\n", argv0 );
561 (void) fprintf( stderr, "usage: %s [-D] [-p port] [-c cgipat] [-u user] [-h hostname] [-r] [-v] [-l logfile] [-i pidfile] [-T charset]\n", argv0 );
568 initialize_listen_socket( usockaddr* usaP )
573 listen_fd = socket( usaP->sa.sa_family, SOCK_STREAM, 0 );
579 (void) fcntl( listen_fd, F_SETFD, 1 );
581 if ( setsockopt( listen_fd, SOL_SOCKET, SO_REUSEADDR, (char*) &i, sizeof(i) ) < 0 )
583 perror( "setsockopt" );
586 if ( bind( listen_fd, &usaP->sa, sockaddr_len( usaP ) ) < 0 )
591 if ( listen( listen_fd, 1024 ) < 0 )
600 /* This runs in a child process, and exits when done, so cleanup is
604 handle_request( void )
610 /* Initialize the request variables. */
611 remoteuser = (char*) 0;
616 protocol = "HTTP/1.0";
619 req_hostname = (char*) 0;
621 authorization = (char*) 0;
622 content_type = (char*) 0;
626 if_modified_since = (time_t) -1;
633 ssl = SSL_new( ssl_ctx );
634 SSL_set_fd( ssl, conn_fd );
635 if ( SSL_accept( ssl ) == 0 )
637 ERR_print_errors_fp( stderr );
643 /* Read in the request. */
648 int r = my_read( buf, sizeof(buf) );
651 add_to_request( buf, r );
652 if ( strstr( request, "\r\n\r\n" ) != (char*) 0 ||
653 strstr( request, "\n\n" ) != (char*) 0 )
657 /* Parse the first line of the request. */
658 method_str = get_request_line();
659 path = strpbrk( method_str, " \t\n\r" );
660 if ( path == (char*) 0 )
661 send_error( 400, "Bad Request", (char*) 0, "Can't parse request." );
663 path += strspn( path, " \t\n\r" );
664 protocol = strpbrk( path, " \t\n\r" );
665 if ( protocol == (char*) 0 )
666 send_error( 400, "Bad Request", (char*) 0, "Can't parse request." );
668 query = strchr( path, '?' );
669 if ( query == (char*) 0 )
674 /* Parse the rest of the request headers. */
675 while ( ( line = get_request_line() ) != (char*) 0 )
677 if ( line[0] == '\0' )
679 else if ( strncasecmp( line, "Authorization:", 14 ) == 0 )
682 cp += strspn( cp, " \t" );
685 else if ( strncasecmp( line, "Content-Length:", 15 ) == 0 )
688 cp += strspn( cp, " \t" );
689 content_length = atol( cp );
691 else if ( strncasecmp( line, "Content-Type:", 13 ) == 0 )
694 cp += strspn( cp, " \t" );
697 else if ( strncasecmp( line, "Cookie:", 7 ) == 0 )
700 cp += strspn( cp, " \t" );
703 else if ( strncasecmp( line, "Host:", 5 ) == 0 )
706 cp += strspn( cp, " \t" );
709 else if ( strncasecmp( line, "If-Modified-Since:", 18 ) == 0 )
712 cp += strspn( cp, " \t" );
713 if_modified_since = tdate_parse( cp );
715 else if ( strncasecmp( line, "Referer:", 8 ) == 0 )
718 cp += strspn( cp, " \t" );
721 else if ( strncasecmp( line, "User-Agent:", 11 ) == 0 )
724 cp += strspn( cp, " \t" );
729 if ( strcasecmp( method_str, get_method_str( METHOD_GET ) ) == 0 )
731 else if ( strcasecmp( method_str, get_method_str( METHOD_HEAD ) ) == 0 )
732 method = METHOD_HEAD;
733 else if ( strcasecmp( method_str, get_method_str( METHOD_POST ) ) == 0 )
734 method = METHOD_POST;
736 send_error( 501, "Not Implemented", (char*) 0, "That method is not implemented." );
738 strdecode( path, path );
739 if ( path[0] != '/' )
740 send_error( 400, "Bad Request", (char*) 0, "Bad filename." );
742 if ( file[0] == '\0' )
745 if ( file[0] == '/' ||
746 ( file[0] == '.' && file[1] == '.' &&
747 ( file[2] == '\0' || file[2] == '/' ) ) )
748 send_error( 400, "Bad Request", (char*) 0, "Illegal filename." );
750 file = virtual_file( file );
752 if ( stat( file, &sb ) < 0 )
753 send_error( 404, "Not Found", (char*) 0, "File not found." );
754 if ( ! S_ISDIR( sb.st_mode ) )
759 if ( file[strlen(file) - 1] != '/' )
761 char location[10000];
762 (void) snprintf( location, sizeof(location), "Location: %s/", path );
763 send_error( 302, "Found", location, "Directories must end with a slash." );
765 (void) snprintf( idx, sizeof(idx), "%sindex.html", file );
766 if ( stat( idx, &sb ) >= 0 )
773 (void) snprintf( idx, sizeof(idx), "%sindex.htm", file );
774 if ( stat( idx, &sb ) >= 0 )
781 (void) snprintf( idx, sizeof(idx), "%sindex.cgi", file );
782 if ( stat( idx, &sb ) >= 0 )
800 de_dotdot( char* file )
806 /* Elide any xxx/../ sequences. */
807 while ( ( cp = strstr( file, "/../" ) ) != (char*) 0 )
809 for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 )
813 (void) strcpy( cp2, cp + 3 );
816 /* Also elide any xxx/.. at the end. */
817 while ( ( l = strlen( file ) ) > 3 &&
818 strcmp( ( cp = file + l - 3 ), "/.." ) == 0 )
820 for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 )
834 char fixed_type[500];
839 /* Check authorization for this directory. */
840 (void) strncpy( buf, file, sizeof(buf) );
841 cp = strrchr( buf, '/' );
842 if ( cp == (char*) 0 )
843 (void) strcpy( buf, "." );
848 /* Check if the filename is the AUTH_FILE itself - that's verboten. */
849 if ( strcmp( file, AUTH_FILE ) == 0 ||
850 ( strcmp( &(file[strlen(file) - sizeof(AUTH_FILE) + 1]), AUTH_FILE ) == 0 &&
851 file[strlen(file) - sizeof(AUTH_FILE)] == '/' ) )
852 send_error( 403, "Forbidden", (char*) 0, "File is protected." );
855 if ( cgi_pattern != (char*) 0 && match( cgi_pattern, file ) )
861 fd = open( file, O_RDONLY );
863 send_error( 403, "Forbidden", (char*) 0, "File is protected." );
864 type = get_mime_type( file );
865 (void) snprintf( fixed_type, sizeof(fixed_type), type, charset );
866 if ( if_modified_since != (time_t) -1 &&
867 if_modified_since >= sb.st_mtime )
870 304, "Not Modified", (char*) 0, fixed_type, sb.st_size,
875 add_headers( 200, "Ok", (char*) 0, fixed_type, sb.st_size, sb.st_mtime );
877 if ( method == METHOD_HEAD )
879 if ( sb.st_size > 0 ) /* avoid zero-length mmap */
881 ptr = mmap( 0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0 );
882 if ( ptr != (void*) -1 )
884 (void) my_write( ptr, sb.st_size );
885 (void) munmap( ptr, sb.st_size );
899 int contents_size, contents_len;
902 /* Check authorization for this directory. */
906 buflen = snprintf( buf, sizeof(buf),
907 "<HTML><HEAD><TITLE>Index of %s</TITLE></HEAD>\n<BODY BGCOLOR=\"#99cc99\"><H4>Index of %s</H4>\n<PRE>\n",
909 add_to_buf( &contents, &contents_size, &contents_len, buf, buflen );
911 /* Magic HTML ls command! */
912 if ( strchr( file, '\'' ) == (char*) 0 )
915 command, sizeof(command),
916 "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/|//'",
918 fp = popen( command, "r" );
922 r = fread( buf, 1, sizeof(buf), fp );
925 add_to_buf( &contents, &contents_size, &contents_len, buf, r );
930 buflen = snprintf( buf, sizeof(buf),
931 "</PRE>\n<HR>\n<ADDRESS><A HREF=\"%s\">%s</A></ADDRESS>\n</BODY></HTML>\n",
932 SERVER_URL, SERVER_SOFTWARE );
933 add_to_buf( &contents, &contents_size, &contents_len, buf, buflen );
935 add_headers( 200, "Ok", (char*) 0, "text/html", contents_len, sb.st_mtime );
936 if ( method == METHOD_HEAD )
941 add_to_response( contents, contents_len );
955 if ( method != METHOD_GET && method != METHOD_POST )
956 send_error( 501, "Not Implemented", (char*) 0, "That method is not implemented for CGI." );
958 /* Make the environment vector. */
961 /* Make the argument vector. */
964 /* Set up stdin. For POSTs we may have to set up a pipe from an
965 ** interposer process, depending on if we've read some of the data
966 ** into our buffer. We also have to do this for all SSL CGIs.
969 if ( ( method == METHOD_POST && request_len > request_idx ) || do_ssl )
971 if ( ( method == METHOD_POST && request_len > request_idx ) )
978 send_error( 500, "Internal Error", (char*) 0, "Something unexpected went wrong making a pipe." );
981 send_error( 500, "Internal Error", (char*) 0, "Something unexpected went wrong forking an interposer." );
984 /* Interposer process. */
985 (void) close( p[0] );
986 cgi_interpose_input( p[1] );
989 (void) close( p[1] );
990 (void) dup2( p[0], STDIN_FILENO );
994 /* Otherwise, the request socket is stdin. */
995 (void) dup2( conn_fd, STDIN_FILENO );
998 /* Set up stdout/stderr. For SSL, or if we're doing CGI header parsing,
999 ** we need an output interposer too.
1001 if ( strncmp( argp[0], "nph-", 4 ) == 0 )
1006 if ( parse_headers || do_ssl )
1008 if ( parse_headers )
1009 #endif /* USE_SSL */
1014 if ( pipe( p ) < 0 )
1015 send_error( 500, "Internal Error", (char*) 0, "Something unexpected went wrong making a pipe." );
1018 send_error( 500, "Internal Error", (char*) 0, "Something unexpected went wrong forking an interposer." );
1021 /* Interposer process. */
1022 (void) close( p[1] );
1023 cgi_interpose_output( p[0], parse_headers );
1026 (void) close( p[0] );
1027 (void) dup2( p[1], STDOUT_FILENO );
1028 (void) dup2( p[1], STDERR_FILENO );
1032 /* Otherwise, the request socket is stdout/stderr. */
1033 (void) dup2( conn_fd, STDOUT_FILENO );
1034 (void) dup2( conn_fd, STDERR_FILENO );
1038 (void) nice( CGI_NICE );
1040 /* Split the program into directory and binary, so we can chdir()
1041 ** to the program's own directory. This isn't in the CGI 1.1
1042 ** spec, but it's what other HTTP servers do.
1044 directory = strdup( file );
1045 if ( directory == (char*) 0 )
1046 binary = file; /* ignore errors */
1049 binary = strrchr( directory, '/' );
1050 if ( binary == (char*) 0 )
1055 (void) chdir( directory ); /* ignore errors */
1059 /* Default behavior for SIGPIPE. */
1060 (void) signal( SIGPIPE, SIG_DFL );
1062 /* Run the program. */
1063 (void) execve( binary, argp, envp );
1065 /* Something went wrong. */
1066 send_error( 500, "Internal Error", (char*) 0, "Something unexpected went wrong running a CGI program." );
1070 /* This routine is used only for POST requests. It reads the data
1071 ** from the request and sends it to the child process. The only reason
1072 ** we need to do it this way instead of just letting the child read
1073 ** directly is that we have already read part of the data into our
1076 ** Oh, and it's also used for all SSL CGIs.
1079 cgi_interpose_input( int wfd )
1084 c = request_len - request_idx;
1087 if ( write( wfd, &(request[request_idx]), c ) != c )
1090 while ( c < content_length )
1092 r = my_read( buf, MIN( sizeof(buf), content_length - c ) );
1097 if ( errno == EAGAIN )
1104 if ( write( wfd, buf, r ) != r )
1112 /* This routine is used for parsed-header CGIs and for all SSL CGIs. */
1114 cgi_interpose_output( int rfd, int parse_headers )
1119 if ( ! parse_headers )
1121 /* If we're not parsing headers, write out the default status line
1122 ** and proceed to the echo phase.
1124 char http_head[] = "HTTP/1.0 200 OK\r\n";
1125 (void) my_write( http_head, sizeof(http_head) );
1129 /* Header parsing. The idea here is that the CGI can return special
1130 ** headers such as "Status:" and "Location:" which change the return
1131 ** status of the response. Since the return status has to be the very
1132 ** first line written out, we have to accumulate all the headers
1133 ** and check for the special ones before writing the status. Then
1134 ** we write out the saved headers and proceed to echo the rest of
1137 int headers_size, headers_len;
1144 /* Slurp in all headers. */
1146 add_to_buf( &headers, &headers_size, &headers_len, (char*) 0, 0 );
1149 r = read( rfd, buf, sizeof(buf) );
1152 br = &(headers[headers_len]);
1155 add_to_buf( &headers, &headers_size, &headers_len, buf, r );
1156 if ( ( br = strstr( headers, "\r\n\r\n" ) ) != (char*) 0 ||
1157 ( br = strstr( headers, "\n\n" ) ) != (char*) 0 )
1161 /* Figure out the status. */
1163 if ( ( cp = strstr( headers, "Status:" ) ) != (char*) 0 &&
1165 ( cp == headers || *(cp-1) == '\n' ) )
1168 cp += strspn( cp, " \t" );
1169 status = atoi( cp );
1171 if ( ( cp = strstr( headers, "Location:" ) ) != (char*) 0 &&
1173 ( cp == headers || *(cp-1) == '\n' ) )
1176 /* Write the status line. */
1179 case 200: title = "OK"; break;
1180 case 302: title = "Found"; break;
1181 case 304: title = "Not Modified"; break;
1182 case 400: title = "Bad Request"; break;
1183 case 401: title = "Unauthorized"; break;
1184 case 403: title = "Forbidden"; break;
1185 case 404: title = "Not Found"; break;
1186 case 408: title = "Request Timeout"; break;
1187 case 500: title = "Internal Error"; break;
1188 case 501: title = "Not Implemented"; break;
1189 case 503: title = "Service Temporarily Overloaded"; break;
1190 default: title = "Something"; break;
1193 buf, sizeof(buf), "HTTP/1.0 %d %s\r\n", status, title );
1194 (void) my_write( buf, strlen( buf ) );
1196 /* Write the saved headers. */
1197 (void) my_write( headers, headers_len );
1200 /* Echo the rest of the output. */
1203 r = read( rfd, buf, sizeof(buf) );
1206 if ( my_write( buf, r ) != r )
1212 /* Set up CGI argument vector. We don't have to worry about freeing
1213 ** stuff since we're a sub-process. This gets done after make_envp() because
1214 ** we scribble on query.
1224 /* By allocating an arg slot for every character in the query, plus
1225 ** one for the filename and one for the NULL, we are guaranteed to
1226 ** have enough. We could actually use strlen/2.
1228 argp = (char**) malloc( ( strlen( query ) + 2 ) * sizeof(char*) );
1229 if ( argp == (char**) 0 )
1232 argp[0] = strrchr( file, '/' );
1233 if ( argp[0] != (char*) 0 )
1239 /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html,
1240 ** "The server should search the query information for a non-encoded =
1241 ** character to determine if the command line is to be used, if it finds
1242 ** one, the command line is not to be used."
1244 if ( strchr( query, '=' ) == (char*) 0 )
1246 for ( cp1 = cp2 = query; *cp2 != '\0'; ++cp2 )
1251 strdecode( cp1, cp1 );
1258 strdecode( cp1, cp1 );
1263 argp[argn] = (char*) 0;
1268 /* Set up CGI environment variables. Be real careful here to avoid
1269 ** letting malicious clients overrun a buffer. We don't have
1270 ** to worry about freeing stuff since we're a sub-process.
1275 static char* envp[50];
1281 envp[envn++] = build_env( "PATH=%s", CGI_PATH );
1282 envp[envn++] = build_env( "LD_LIBRARY_PATH=%s", CGI_LD_LIBRARY_PATH );
1283 envp[envn++] = build_env( "SERVER_SOFTWARE=%s", SERVER_SOFTWARE );
1287 cp = req_hostname; /* already computed by virtual_file() */
1288 if ( cp != (char*) 0 )
1289 envp[envn++] = build_env( "SERVER_NAME=%s", cp );
1290 envp[envn++] = "GATEWAY_INTERFACE=CGI/1.1";
1291 envp[envn++] = "SERVER_PROTOCOL=HTTP/1.0";
1292 (void) snprintf( buf, sizeof(buf), "%d", port );
1293 envp[envn++] = build_env( "SERVER_PORT=%s", buf );
1294 envp[envn++] = build_env(
1295 "REQUEST_METHOD=%s", get_method_str( method ) );
1296 envp[envn++] = build_env( "SCRIPT_NAME=%s", path );
1297 if ( query[0] != '\0' )
1298 envp[envn++] = build_env( "QUERY_STRING=%s", query );
1299 envp[envn++] = build_env( "REMOTE_ADDR=%s", ntoa( &client_addr ) );
1300 if ( referer[0] != '\0' )
1301 envp[envn++] = build_env( "HTTP_REFERER=%s", referer );
1302 if ( useragent[0] != '\0' )
1303 envp[envn++] = build_env( "HTTP_USER_AGENT=%s", useragent );
1304 if ( cookie != (char*) 0 )
1305 envp[envn++] = build_env( "HTTP_COOKIE=%s", cookie );
1306 if ( content_type != (char*) 0 )
1307 envp[envn++] = build_env( "CONTENT_TYPE=%s", content_type );
1308 if ( content_length != -1 )
1310 (void) snprintf( buf, sizeof(buf), "%ld", content_length );
1311 envp[envn++] = build_env( "CONTENT_LENGTH=%s", buf );
1313 if ( remoteuser != (char*) 0 )
1314 envp[envn++] = build_env( "REMOTE_USER=%s", remoteuser );
1315 if ( authorization != (char*) 0 )
1316 envp[envn++] = build_env( "AUTH_TYPE=%s", "Basic" );
1317 if ( getenv( "TZ" ) != (char*) 0 )
1318 envp[envn++] = build_env( "TZ=%s", getenv( "TZ" ) );
1320 envp[envn] = (char*) 0;
1326 build_env( char* fmt, char* arg )
1331 static int maxbuf = 0;
1333 size = strlen( fmt ) + strlen( arg );
1334 if ( size > maxbuf )
1339 buf = (char*) malloc( maxbuf );
1344 buf = (char*) realloc( (void*) buf, maxbuf );
1346 if ( buf == (char*) 0 )
1348 (void) fprintf( stderr, "%s: out of memory\n", argv0 );
1352 (void) snprintf( buf, maxbuf, fmt, arg );
1354 if ( cp == (char*) 0 )
1356 (void) fprintf( stderr, "%s: out of memory\n", argv0 );
1364 auth_check( char* dirname )
1366 char authpath[10000];
1370 static char line[10000];
1375 /* Construct auth filename. */
1376 if ( dirname[strlen(dirname) - 1] == '/' )
1377 (void) snprintf( authpath, sizeof(authpath), "%s%s", dirname, AUTH_FILE );
1379 (void) snprintf( authpath, sizeof(authpath), "%s/%s", dirname, AUTH_FILE );
1381 /* Does this directory have an auth file? */
1382 if ( stat( authpath, &sb ) < 0 )
1383 /* Nope, let the request go through. */
1386 /* Does this request contain authorization info? */
1387 if ( authorization == (char*) 0 )
1388 /* Nope, return a 401 Unauthorized. */
1389 send_authenticate( dirname );
1391 /* Basic authorization info? */
1392 if ( strncmp( authorization, "Basic ", 6 ) != 0 )
1393 send_authenticate( dirname );
1396 l = b64_decode( &(authorization[6]), authinfo, sizeof(authinfo) );
1398 /* Split into user and password. */
1399 authpass = strchr( authinfo, ':' );
1400 if ( authpass == (char*) 0 )
1401 /* No colon? Bogus auth info. */
1402 send_authenticate( dirname );
1405 /* Open the password file. */
1406 fp = fopen( authpath, "r" );
1407 if ( fp == (FILE*) 0 )
1408 /* The file exists but we can't open it? Disallow access. */
1409 send_error( 403, "Forbidden", (char*) 0, "File is protected." );
1412 while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
1416 if ( line[l - 1] == '\n' )
1418 /* Split into user and encrypted password. */
1419 cryp = strchr( line, ':' );
1420 if ( cryp == (char*) 0 )
1423 /* Is this the right user? */
1424 if ( strcmp( line, authinfo ) == 0 )
1427 (void) fclose( fp );
1428 /* So is the password right? */
1429 if ( strcmp( crypt( authpass, cryp ), cryp ) == 0 )
1437 send_authenticate( dirname );
1441 /* Didn't find that user. Access denied. */
1442 (void) fclose( fp );
1443 send_authenticate( dirname );
1448 send_authenticate( char* realm )
1453 header, sizeof(header), "WWW-Authenticate: Basic realm=\"%s\"", realm );
1454 send_error( 401, "Unauthorized", header, "Authorization required." );
1459 virtual_file( char* file )
1462 static char vfile[10000];
1464 /* Use the request's hostname, or fall back on the IP address. */
1465 if ( host != (char*) 0 )
1466 req_hostname = host;
1470 int sz = sizeof(usa);
1471 if ( getsockname( conn_fd, &usa.sa, &sz ) < 0 )
1472 req_hostname = "UNKNOWN_HOST";
1474 req_hostname = ntoa( &usa );
1476 /* Pound it to lower case. */
1477 for ( cp = req_hostname; *cp != '\0'; ++cp )
1478 if ( isupper( *cp ) )
1479 *cp = tolower( *cp );
1480 (void) snprintf( vfile, sizeof(vfile), "%s/%s", req_hostname, file );
1486 send_error( int s, char* title, char* extra_header, char* text )
1488 add_headers( s, title, extra_header, "text/html", -1, -1 );
1490 send_error_body( s, title, text );
1498 #endif /* USE_SSL */
1504 send_error_body( int s, char* title, char* text )
1506 char filename[1000];
1510 if ( vhost && req_hostname != (char*) 0 )
1512 /* Try virtual-host custom error page. */
1514 filename, sizeof(filename), "%s/%s/err%d.html",
1515 req_hostname, ERR_DIR, s );
1516 if ( send_error_file( filename ) )
1520 /* Try server-wide custom error page. */
1522 filename, sizeof(filename), "%s/err%d.html", ERR_DIR, s );
1523 if ( send_error_file( filename ) )
1526 /* Send built-in error page. */
1529 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n<BODY BGCOLOR=\"#cc9999\"><H4>%d %s</H4>\n",
1530 s, title, s, title );
1531 add_to_response( buf, buflen );
1532 buflen = snprintf( buf, sizeof(buf), "%s\n", text );
1533 add_to_response( buf, buflen );
1538 send_error_file( char* filename )
1544 fp = fopen( filename, "r" );
1545 if ( fp == (FILE*) 0 )
1549 r = fread( buf, 1, sizeof(buf), fp );
1552 add_to_response( buf, r );
1554 (void) fclose( fp );
1560 send_error_tail( void )
1565 if ( match( "**MSIE**", useragent ) )
1568 buflen = snprintf( buf, sizeof(buf), "<!--\n" );
1569 add_to_response( buf, buflen );
1570 for ( n = 0; n < 6; ++n )
1572 buflen = snprintf( buf, sizeof(buf), "Padding so that MSIE deigns to show this error instead of its own canned one.\n" );
1573 add_to_response( buf, buflen );
1575 buflen = snprintf( buf, sizeof(buf), "-->\n" );
1576 add_to_response( buf, buflen );
1579 buflen = snprintf( buf, sizeof(buf), "<HR>\n<ADDRESS><A HREF=\"%s\">%s</A></ADDRESS>\n</BODY></HTML>\n", SERVER_URL, SERVER_SOFTWARE );
1580 add_to_response( buf, buflen );
1585 add_headers( int s, char* title, char* extra_header, char* mime_type, long b, time_t mod )
1591 const char* rfc1123_fmt = "%a, %d %b %Y %H:%M:%S GMT";
1597 buflen = snprintf( buf, sizeof(buf), "%s %d %s\r\n", protocol, status, title );
1598 add_to_response( buf, buflen );
1599 buflen = snprintf( buf, sizeof(buf), "Server: %s\r\n", SERVER_SOFTWARE );
1600 add_to_response( buf, buflen );
1601 now = time( (time_t*) 0 );
1602 (void) strftime( timebuf, sizeof(timebuf), rfc1123_fmt, gmtime( &now ) );
1603 buflen = snprintf( buf, sizeof(buf), "Date: %s\r\n", timebuf );
1604 add_to_response( buf, buflen );
1605 if ( extra_header != (char*) 0 )
1607 buflen = snprintf( buf, sizeof(buf), "%s\r\n", extra_header );
1608 add_to_response( buf, buflen );
1610 if ( mime_type != (char*) 0 )
1612 buflen = snprintf( buf, sizeof(buf), "Content-type: %s\r\n", mime_type );
1613 add_to_response( buf, buflen );
1617 buflen = snprintf( buf, sizeof(buf), "Content-length: %ld\r\n", bytes );
1618 add_to_response( buf, buflen );
1620 if ( mod != (time_t) -1 )
1622 (void) strftime( timebuf, sizeof(timebuf), rfc1123_fmt, gmtime( &mod ) );
1623 buflen = snprintf( buf, sizeof(buf), "Last-modified: %s\r\n", timebuf );
1624 add_to_response( buf, buflen );
1626 buflen = snprintf( buf, sizeof(buf), "Connection: close\r\n\r\n" );
1627 add_to_response( buf, buflen );
1632 start_request( void )
1639 add_to_request( char* str, int len )
1641 add_to_buf( &request, &request_size, &request_len, str, len );
1645 get_request_line( void )
1650 for ( i = request_idx; request_idx < request_len; ++request_idx )
1652 c = request[request_idx];
1653 if ( c == '\n' || c == '\r' )
1655 request[request_idx] = '\0';
1657 if ( c == '\r' && request_idx < request_len &&
1658 request[request_idx] == '\n' )
1660 request[request_idx] = '\0';
1663 return &(request[i]);
1670 static char* response;
1671 static int response_size, response_len;
1674 start_response( void )
1680 add_to_response( char* str, int len )
1682 add_to_buf( &response, &response_size, &response_len, str, len );
1686 send_response( void )
1688 (void) my_write( response, response_len );
1693 my_read( char* buf, int size )
1697 return SSL_read( ssl, buf, size );
1699 return read( conn_fd, buf, size );
1701 return read( conn_fd, buf, size );
1702 #endif /* USE_SSL */
1707 my_write( char* buf, int size )
1711 return SSL_write( ssl, buf, size );
1713 return write( conn_fd, buf, size );
1715 return write( conn_fd, buf, size );
1716 #endif /* USE_SSL */
1721 add_to_buf( char** bufP, int* bufsizeP, int* buflenP, char* str, int len )
1723 if ( *bufsizeP == 0 )
1725 *bufsizeP = len + 500;
1727 *bufP = (char*) malloc( *bufsizeP );
1729 else if ( *buflenP + len >= *bufsizeP )
1731 *bufsizeP = *buflenP + len + 500;
1732 *bufP = (char*) realloc( (void*) *bufP, *bufsizeP );
1734 if ( *bufP == (char*) 0 )
1736 (void) fprintf( stderr, "%s: out of memory\n", argv0 );
1739 (void) memcpy( &((*bufP)[*buflenP]), str, len );
1741 (*bufP)[*buflenP] = '\0';
1746 make_log_entry( void )
1753 const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S";
1754 char date_nozone[100];
1759 if ( logfp == (FILE*) 0 )
1762 /* Format the user. */
1763 if ( remoteuser != (char*) 0 )
1767 now = time( (time_t*) 0 );
1768 /* If we're vhosting, prepend the hostname to the url. This is
1769 ** a little weird, perhaps writing separate log files for
1770 ** each vhost would make more sense.
1773 (void) snprintf( url, sizeof(url), "/%s%s", req_hostname, path );
1775 (void) snprintf( url, sizeof(url), "%s", path );
1776 /* Format the bytes. */
1778 (void) snprintf( bytes_str, sizeof(bytes_str), "%ld", bytes );
1780 (void) strcpy( bytes_str, "-" );
1781 /* Format the time, forcing a numeric timezone (some log analyzers
1782 ** are stoooopid about this).
1784 t = localtime( &now );
1785 (void) strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t );
1786 #ifdef HAVE_TM_GMTOFF
1787 zone = t->tm_gmtoff / 60L;
1789 zone = - ( timezone / 60L );
1790 /* Probably have to add something about daylight time here. */
1799 zone = ( zone / 60 ) * 100 + zone % 60;
1800 (void) snprintf( date, sizeof(date), "%s %c%04d", date_nozone, sign, zone );
1801 /* And write the log entry. */
1802 (void) fprintf( logfp,
1803 "%.80s - %.80s [%s] \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.80s\"\n",
1804 ntoa( &client_addr ), ru, date, get_method_str( method ), url,
1805 protocol, status, bytes_str, referer, useragent );
1806 (void) fflush( logfp );
1811 get_method_str( int m )
1815 case METHOD_GET: return "GET";
1816 case METHOD_HEAD: return "HEAD";
1817 case METHOD_POST: return "POST";
1818 default: return (char*) 0;
1824 get_mime_type( char* name )
1830 #include "mime_types.h"
1832 int fl = strlen( name );
1835 for ( i = 0; i < sizeof(table) / sizeof(*table); ++i )
1837 int el = strlen( table[i].ext );
1838 if ( strcasecmp( &(name[fl - el]), table[i].ext ) == 0 )
1839 return table[i].type;
1841 return "text/plain; charset=%s";
1846 handle_sigterm( int sig )
1848 (void) fprintf( stderr, "%s: exiting due to signal %d\n", argv0, sig );
1854 handle_sigchld( int sig )
1859 /* Reap defunct children until there aren't any more. */
1863 pid = waitpid( (pid_t) -1, &status, WNOHANG );
1864 #else /* HAVE_WAITPID */
1865 pid = wait3( &status, WNOHANG, (struct rusage*) 0 );
1866 #endif /* HAVE_WAITPID */
1867 if ( (int) pid == 0 ) /* none left */
1869 if ( (int) pid < 0 )
1871 if ( errno == EINTR ) /* because of ptrace */
1873 /* ECHILD shouldn't happen with the WNOHANG option,
1874 ** but with some kernels it does anyway. Ignore it.
1876 if ( errno != ECHILD )
1877 perror( "child wait" );
1885 lookup_hostname( usockaddr* usa4P, size_t sa4_len, int* gotv4P, usockaddr* usa6P, size_t sa6_len, int* gotv6P )
1887 #if defined(HAVE_GETADDRINFO) && defined(HAVE_GAI_STRERROR)
1889 struct addrinfo hints;
1890 struct addrinfo* ai;
1891 struct addrinfo* ai2;
1892 struct addrinfo* aiv4;
1893 struct addrinfo* aiv6;
1897 memset( &hints, 0, sizeof(hints) );
1898 hints.ai_family = AF_UNSPEC;
1899 hints.ai_flags = AI_PASSIVE;
1900 hints.ai_socktype = SOCK_STREAM;
1901 (void) snprintf( strport, sizeof(strport), "%d", port );
1902 if ( (gaierr = getaddrinfo( hostname, strport, &hints, &ai )) != 0 )
1904 (void) fprintf( stderr, "getaddrinfo %.80s - %s\n", hostname, gai_strerror( gaierr ) );
1908 /* Find the first IPv4 and IPv6 entries. */
1909 aiv4 = (struct addrinfo*) 0;
1910 aiv6 = (struct addrinfo*) 0;
1911 for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
1913 switch ( ai2->ai_family )
1916 if ( aiv4 == (struct addrinfo*) 0 )
1919 #if defined(AF_INET6) && defined(HAVE_SOCKADDR_IN6)
1921 if ( aiv6 == (struct addrinfo*) 0 )
1924 #endif /* AF_INET6 && HAVE_SOCKADDR_IN6 */
1928 if ( aiv4 == (struct addrinfo*) 0 )
1932 if ( sa4_len < aiv4->ai_addrlen )
1935 stderr, "%.80s - sockaddr too small (%d < %d)\n",
1936 hostname, sa4_len, aiv4->ai_addrlen );
1939 memset( usa4P, 0, sa4_len );
1940 memcpy( usa4P, aiv4->ai_addr, aiv4->ai_addrlen );
1943 if ( aiv6 == (struct addrinfo*) 0 )
1947 if ( sa6_len < aiv6->ai_addrlen )
1950 stderr, "%.80s - sockaddr too small (%d < %d)\n",
1951 hostname, sa6_len, aiv6->ai_addrlen );
1954 memset( usa6P, 0, sa6_len );
1955 memcpy( usa6P, aiv6->ai_addr, aiv6->ai_addrlen );
1961 #else /* HAVE_GETADDRINFO && HAVE_GAI_STRERROR */
1967 memset( usa4P, 0, sa4_len );
1968 usa4P->sa.sa_family = AF_INET;
1969 if ( hostname == (char*) 0 )
1970 usa4P->sa_in.sin_addr.s_addr = htonl( INADDR_ANY );
1973 usa4P->sa_in.sin_addr.s_addr = inet_addr( hostname );
1974 if ( (int) usa4P->sa_in.sin_addr.s_addr == -1 )
1976 he = gethostbyname( hostname );
1977 if ( he == (struct hostent*) 0 )
1979 #ifdef HAVE_HSTRERROR
1980 (void) fprintf( stderr, "gethostbyname %.80s - %s\n", hostname, hstrerror( h_errno ) );
1981 #else /* HAVE_HSTRERROR */
1982 (void) fprintf( stderr, "gethostbyname %.80s failed\n", hostname );
1983 #endif /* HAVE_HSTRERROR */
1986 if ( he->h_addrtype != AF_INET )
1988 (void) fprintf( stderr, "%.80s - non-IP network address\n", hostname );
1992 &usa4P->sa_in.sin_addr.s_addr, he->h_addr, he->h_length );
1995 usa4P->sa_in.sin_port = htons( port );
1998 #endif /* HAVE_GETADDRINFO && HAVE_GAI_STRERROR */
2003 ntoa( usockaddr* usaP )
2005 #ifdef HAVE_GETNAMEINFO
2006 static char str[200];
2008 if ( getnameinfo( &usaP->sa, sockaddr_len( usaP ), str, sizeof(str), 0, 0, NI_NUMERICHOST ) != 0 )
2015 #else /* HAVE_GETNAMEINFO */
2017 return inet_ntoa( usaP->sa_in.sin_addr );
2019 #endif /* HAVE_GETNAMEINFO */
2024 sockaddr_len( usockaddr* usaP )
2026 switch ( usaP->sa.sa_family )
2028 case AF_INET: return sizeof(struct sockaddr_in);
2029 #if defined(AF_INET6) && defined(HAVE_SOCKADDR_IN6)
2030 case AF_INET6: return sizeof(struct sockaddr_in6);
2031 #endif /* AF_INET6 && HAVE_SOCKADDR_IN6 */
2034 stderr, "unknown sockaddr family - %d\n", usaP->sa.sa_family );
2040 /* Copies and decodes a string. It's ok for from and to to be the
2044 strdecode( char* to, char* from )
2046 for ( ; *from != '\0'; ++to, ++from )
2048 if ( from[0] == '%' && isxdigit( from[1] ) && isxdigit( from[2] ) )
2050 *to = hexit( from[1] ) * 16 + hexit( from[2] );
2063 if ( c >= '0' && c <= '9' )
2065 if ( c >= 'a' && c <= 'f' )
2066 return c - 'a' + 10;
2067 if ( c >= 'A' && c <= 'F' )
2068 return c - 'A' + 10;
2069 return 0; /* shouldn't happen, we're guarded by isxdigit() */
2073 /* Base-64 decoding. This represents binary data as printable ASCII
2074 ** characters. Three 8-bit binary bytes are turned into four 6-bit
2077 ** [11111111] [22222222] [33333333]
2079 ** [111111] [112222] [222233] [333333]
2081 ** Then the 6-bit values are represented using the characters "A-Za-z0-9+/".
2084 static int b64_decode_table[256] = {
2085 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
2086 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
2087 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
2088 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
2089 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
2090 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
2091 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
2092 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
2093 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
2094 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
2095 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
2096 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
2097 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
2098 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
2099 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
2100 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
2103 /* Do base-64 decoding on a string. Ignore any non-base64 bytes.
2104 ** Return the actual number of bytes generated. The decoded size will
2105 ** be at most 3/4 the size of the encoded, and may be smaller if there
2106 ** are padding characters (blanks, newlines).
2109 b64_decode( const char* str, unsigned char* space, int size )
2112 int space_idx, phase;
2118 for ( cp = str; *cp != '\0'; ++cp )
2120 d = b64_decode_table[*cp];
2129 c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) );
2130 if ( space_idx < size )
2131 space[space_idx++] = c;
2135 c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) );
2136 if ( space_idx < size )
2137 space[space_idx++] = c;
2141 c = ( ( ( prev_d & 0x03 ) << 6 ) | d );
2142 if ( space_idx < size )
2143 space[space_idx++] = c;
2154 /* Simple shell-style filename matcher. Only does ? * and **, and multiple
2155 ** patterns separated by |. Returns 1 or 0.
2158 match( const char* pattern, const char* string )
2164 or = strchr( pattern, '|' );
2165 if ( or == (char*) 0 )
2166 return match_one( pattern, strlen( pattern ), string );
2167 if ( match_one( pattern, or - pattern, string ) )
2175 match_one( const char* pattern, int patternlen, const char* string )
2179 for ( p = pattern; p - pattern < patternlen; ++p, ++string )
2181 if ( *p == '?' && *string != '\0' )
2189 /* Double-wildcard matches anything. */
2191 i = strlen( string );
2194 /* Single-wildcard matches anything but slash. */
2195 i = strcspn( string, "/" );
2196 pl = patternlen - ( p - pattern );
2197 for ( ; i >= 0; --i )
2198 if ( match_one( p, pl, &(string[i]) ) )
2202 if ( *p != *string )
2205 if ( *string == '\0' )