/* mini_httpd - small HTTP server ** ** Copyright ?1999,2000 by Jef Poskanzer . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. */ /* *Mender : wsg,2016-09-30,set web language */ #include "version.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mini_httpd_raw.h" #include "port.h" #include "match.h" #include "tdate_parse.h" #include "mini_httpd_debug.h" #if defined (CC3020_633) || defined(X86) #include #include "mini_httpd_raw.h" #include "mini_httpd_app.h" #endif #define WEBVERSTION "Switch Web Server" #ifdef HAVE_SENDFILE # ifdef HAVE_LINUX_SENDFILE # include # else /* HAVE_LINUX_SENDFILE */ # include # endif /* HAVE_LINUX_SENDFILE */ #endif /* HAVE_SENDFILE */ #ifdef USE_SSL #include #include #endif /* USE_SSL */ //extern char* crypt( const char* key, const char* setting ); //#if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED) //#define USE_IPV6 //#endif #ifndef STDIN_FILENO #define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO #define STDERR_FILENO 2 #endif #ifndef SHUT_WR #define SHUT_WR 1 #endif #ifndef SIZE_T_MAX #define SIZE_T_MAX 2147483647L #endif #ifndef max #define max(a,b) ((a) > (b) ? (a) : (b)) #ifndef MAX #define MAX max #endif #endif #ifndef min #define min(a,b) ((a) < (b) ? (a) : (b)) #ifndef MIN #define MIN min #endif #endif //#define HAVE_MY_IPV6 #define HTPASSWD_FILE "/tmp/.htpasswd" #define LOGIN_EVENT "login_event_file" #ifndef ERR_DIR #define ERR_DIR "." #endif /* ERR_DIR */ #ifndef DEFAULT_HTTP_PORT #define DEFAULT_HTTP_PORT 80 #endif /* DEFAULT_HTTP_PORT */ #ifdef USE_SSL #ifndef DEFAULT_HTTPS_PORT #define DEFAULT_HTTPS_PORT 443 #endif /* DEFAULT_HTTPS_PORT */ #ifndef DEFAULT_CERTFILE #define DEFAULT_CERTFILE "mini_httpd.pem" #endif /* DEFAULT_CERTFILE */ #endif /* USE_SSL */ #ifndef DEFAULT_USER #define DEFAULT_USER "root" #endif /* DEFAULT_USER */ #ifndef CGI_NICE #define CGI_NICE 10 #endif /* CGI_NICE */ #ifndef CGI_PATH #define CGI_PATH "./" #endif /* CGI_PATH */ #ifndef CGI_LD_LIBRARY_PATH #define CGI_LD_LIBRARY_PATH "/lib:/usr/local/lib:/usr/lib" #endif /* CGI_LD_LIBRARY_PATH */ #ifndef AUTH_FILE #define AUTH_FILE HTPASSWD_FILE #endif /* AUTH_FILE */ #ifndef TIMEOUT #define TIMEOUT 60 #endif /* TIMEOUT */ #ifndef DEFAULT_CHARSET #define DEFAULT_CHARSET "iso-8859-1" #endif /* DEFAULT_CHARSET */ #define METHOD_UNKNOWN 0 #define METHOD_GET 1 #define METHOD_HEAD 2 #define METHOD_POST 3 #define SYSLOG 1 /* A multi-family sockaddr. */ typedef union { struct sockaddr sa; struct sockaddr_in sa_in; #ifdef USE_IPV6 struct sockaddr_in6 sa_in6; struct sockaddr_storage sa_stor; #endif /* USE_IPV6 */ } usockaddr; //extern char *crypt(const char *key, const char *setting); extern web_lang_flags; int is_mini_raw_sockfd = 0; int mini_raw_sock_busy = 0; int mini_http_raw_sockfd; netlinksock_t mini_http_raw_pkt_sock; int httpd_ifindex; extern unsigned char minidstmac[]; extern unsigned char minisrcmac[]; static char* argv0; static int debug; static int port; static char* dir; static int do_chroot; static int vhost; static char* user; static char* cgi_pattern; static char* url_pattern; static int no_empty_referers; static char* local_pattern; static char* hostname; static char hostname_buf[500]; static char* logfile; static char* pidfile; static char* charset; static char* p3p; static int max_age; static FILE* logfp; static int listen4_fd, listen6_fd; #ifdef HAVE_MY_IPV6 static int mylisten6_fd; #endif #ifdef USE_SSL static int do_ssl; static char* certfile; static char* cipher; static SSL_CTX* ssl_ctx; #endif /* USE_SSL */ static char cwd[MAXPATHLEN]; /* Ron */ #define LOGOUT kill(getppid(),SIGHUP) #define SAVETIME kill(getppid(),SIGUSR2) static char *http_realm=NULL; /* Auth title */ static char static_title[]="L2+ Managed Switch "; /* Auth title */ static int web_timeout; /* timeout */ static time_t last_access_time=0; /* last access time*/ static int someone_in_use=0; /*1=someone in use, 0=nobody*/ static char remote_ip[16]=""; /* current user's ip */ static char last_remote_ip[16]=""; /*lastest user's ip*/ static char *rondir; static int still_timeout=0; static int not_auth=0; #if defined (CC3020_633) || defined(X86) int is_http = 1; #endif static void handle_chdir( int sig ) { if(chdir(rondir?:"/www") == -1) { perror("chdir"); } if(!getcwd( cwd, sizeof(cwd) - 1 )) perror("getcwd"); if ( cwd[strlen( cwd ) - 1] != '/' ) (void) strcat( cwd, "/" ); } static void handle_logout( int sig ) { /* logout */ last_access_time=0; still_timeout=0; } static void handle_web_time( int sig ) { struct sysinfo info; sysinfo(&info); last_access_time=info.uptime; } static int check_timeout(void) { struct sysinfo info; sysinfo(&info); if(strcmp(last_remote_ip,remote_ip)==0 && still_timeout==1){ usleep(1); return 1; } if(last_access_time==0){ usleep(1); return 0; } else if(((info.uptime-last_access_time)>web_timeout) && web_timeout!=0) { usleep(1); return 1; } return 0; } static void handle_timeout_stat(int sig) { /* enter timeout stat */ still_timeout=1; } static void mark_login(char *file) { int myfd; char bufm[2] = {"1"}; if((myfd = open(file, O_RDWR | O_CREAT, 0666)) != -1) { write(myfd,bufm,1); close(myfd); } } static int mark_exist(char *file) { struct stat sb; if(!stat(file, &sb)) return 1; return 0; } static void remove_mark(char *file) { /* char cmd[50] = {0}; sprintf(cmd,"rm -f %s", file); system(cmd);*/ /*CC3952 print error when use system() rm -f files, so use the unlink() to remove files*/ unlink(file); //long.yi } /* Ron */ /* Request variables. */ static int conn_fd; #ifdef USE_SSL static SSL* ssl; #endif /* USE_SSL */ static usockaddr client_addr; static char* request; static size_t request_size, request_len, request_idx; static int method; static char* path; static char fakepath[128]=""; static char* file; static char* pathinfo; struct stat sb; static char* query; static char* protocol; static int status; static off_t bytes; static char* req_hostname; static char* authorization; static size_t content_length; static char* content_type; static char* cookie; static char* host; static time_t if_modified_since; static char* referer; static char* useragent; static char* remoteuser; static char* access_user_info; /* Forwards. */ //static void usage( void ); static void read_config( char* filename ); static void value_required( char* name, char* value ); static void no_value_required( char* name, char* value ); static int initialize_listen_socket( usockaddr* usaP ); static void handle_request( void ); #if defined (CC3020_633) || defined(X86) void raw_pkt_handle(void); #endif static void de_dotdot( char* file ); static int get_pathinfo( void ); static void do_file( void ); //static void do_dir( void ); #ifdef HAVE_SCANDIR static char* file_details( const char* dir, const char* name ); static void strencode( char* to, size_t tosize, const char* from ); #endif /* HAVE_SCANDIR */ static void do_cgi( void ); static void cgi_interpose_input( int wfd ); static void post_post_garbage_hack( void ); static void cgi_interpose_output( int rfd, int parse_headers ); static char** make_argp( void ); static char** make_envp( void ); static char* build_env( char* fmt, char* arg ); static void auth_check( char* dirname ); static void send_authenticate( char* realm ); static char* virtual_file( char* file ); static void send_error( int s, char* title, char* extra_header, char* text ); static void send_error_body( int s, char* title, char* text ); static int send_error_file( char* filename ); static void send_error_tail( void ); static void add_headers( int s, char* title, char* extra_header, char* me, char* mt, off_t b, time_t mod ); static void start_request( void ); static void add_to_request( char* str, size_t len ); static char* get_request_line( void ); static void start_response( void ); static void add_to_response( char* str, size_t len ); static void send_response( void ); static void send_via_write( int fd, off_t size ); static ssize_t my_read( char* buf, size_t size ); static ssize_t my_write( char* buf, size_t size ); #ifdef HAVE_SENDFILE static int my_sendfile( int fd, int socket, off_t offset, size_t nbytes ); #endif /* HAVE_SENDFILE */ static void add_to_buf( char** bufP, size_t* bufsizeP, size_t* buflenP, char* str, size_t len ); static void make_log_entry( void ); static void check_referer( void ); static int really_check_referer( void ); static char* get_method_str( int m ); static void init_mime( void ); static char* figure_mime( char* name, char* me, size_t me_size ); static void handle_sigterm( int sig ); static void handle_sigchld( int sig ); static void handle_sigalrm( int sig ); static void lookup_hostname( usockaddr* usa4P, size_t sa4_len, int* gotv4P, usockaddr* usa6P, size_t sa6_len, int* gotv6P ); static char* ntoa( usockaddr* usaP ); static int sockaddr_check( usockaddr* usaP ); static size_t sockaddr_len( usockaddr* usaP ); static void strdecode( char* to, char* from ); static int hexit( char c ); static int b64_decode( const char* str, unsigned char* space, int size ); static void set_ndelay( int fd ); static void clear_ndelay( int fd ); static void* e_malloc( size_t size ); static void* e_realloc( void* optr, size_t size ); static char* e_strdup( char* ostr ); #ifdef NO_SNPRINTF static int snprintf( char* str, size_t size, const char* format, ... ); #endif /* NO_SNPRINTF */ #ifdef HAVE_MY_IPV6 int http_create_server_sock_v4v6(const char *hostname,const char *port,int sin_family) { struct addrinfo hints; struct addrinfo *result, *rp; int iret = 0; int sfd = 0; int on = 1; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* IPv4-IPv6 independencies */ hints.ai_socktype = sin_family; hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ hints.ai_protocol = IPPROTO_TCP; /* Any protocol */ iret = getaddrinfo(hostname, port, &hints, &result); if(iret != 0) { //zlog_err("getaddrinfo: %s\n", gai_strerror(iret)); return 1; } for (rp = result; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype,rp->ai_protocol); if (sfd == -1) { continue; } if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) { break; /* Success */ } close(sfd); } (void)setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (rp == NULL) { /* No address succeeded */ //zlog_err("Could not bind\n"); return 1; } freeaddrinfo(result);/*No longer needed */ listen(sfd,1024); return sfd; } static int get_ipv6_socket(void) { char pstr[20] = ""; int lisfd6 = -1; sprintf(pstr, "%d", port); lisfd6 = http_create_server_sock_v4v6(NULL, pstr, SOCK_STREAM); #if 0 struct sockaddr_in6 sa_in6; memset(&sa_in6, 0, sizeof(sa_in6)); if ((lisfd6 = socket(AF_INET6, SOCK_STREAM, 0)) == -1) { perror("socket ipv6"); exit(1); } sa_in6.sin6_family = AF_INET6; sa_in6.sin6_port = htons(port); sa_in6.sin6_addr = in6addr_any; if (bind(lisfd6, (struct sockaddr *) &sa_in6, sizeof(struct sockaddr_in6)) < 0) { perror("bind ipv6"); exit(1); } if ( listen( lisfd6, 1024 ) < 0 ) { perror("listen ipv6"); exit(1); } #endif return lisfd6; } #endif int main( int argc, char** argv ) { int argn; uid_t uid = 32767; usockaddr host_addr4; usockaddr host_addr6; int gotv4, gotv6; fd_set lfdset; int maxfd; usockaddr usa; int sz, r; char* cp; FILE *wl_p; char *wlan_mgr; char wl_tmp[256]; char wl_ipaddr[17]; static char wl_mac[19]; int wl_mac_match=0; struct sysinfo info; struct timeval tv = {0,0}; /* Parse args. */ argv0 = argv[0]; debug = 0; port = -1; dir = (char*) 0; do_chroot = 0; vhost = 0; cgi_pattern = (char*) 0; url_pattern = (char*) 0; no_empty_referers = 0; local_pattern = (char*) 0; charset = DEFAULT_CHARSET; p3p = (char*) 0; max_age = -1; user = DEFAULT_USER; hostname = (char*) 0; logfile = (char*) 0; pidfile = (char*) 0; logfp = (FILE*) 0; #ifdef USE_SSL do_ssl = 0; certfile = DEFAULT_CERTFILE; cipher = (char*) 0; #endif /* USE_SSL */ argn = 1; while ( argn < argc && argv[argn][0] == '-' ) { if ( strcmp( argv[argn], "-V" ) == 0) { (void) printf( "%s\n", SERVER_SOFTWARE ); exit( 0 ); } else if ( strcmp( argv[argn], "-C" ) == 0 && argn + 1 < argc ) { ++argn; read_config( argv[argn] ); } else if ( strcmp( argv[argn], "-D" ) == 0 ) debug = 1; #ifdef USE_SSL else if ( strcmp( argv[argn], "-S" ) == 0 ) do_ssl = 1; else if ( strcmp( argv[argn], "-E" ) == 0 && argn + 1 < argc ) { ++argn; certfile = argv[argn]; } else if ( strcmp( argv[argn], "-Y" ) == 0 && argn + 1 < argc ) { ++argn; cipher = argv[argn]; } #endif /* USE_SSL */ else if ( strcmp( argv[argn], "-p" ) == 0 && argn + 1 < argc ) { ++argn; port = atoi( argv[argn] ); } else if ( strcmp( argv[argn], "-d" ) == 0 && argn + 1 < argc ) { ++argn; dir = argv[argn]; rondir = strdup(argv[argn]); } else if ( strcmp( argv[argn], "-c" ) == 0 && argn + 1 < argc ) { ++argn; cgi_pattern = argv[argn]; } else if ( strcmp( argv[argn], "-u" ) == 0 && argn + 1 < argc ) { ++argn; user = argv[argn]; } else if ( strcmp( argv[argn], "-h" ) == 0 && argn + 1 < argc ) { ++argn; hostname = argv[argn]; } else if ( strcmp( argv[argn], "-r" ) == 0 ){ //do_chroot = 1; ++argn; if(argv[argn]!=NULL) http_realm=static_title;//argv[argn]; } else if ( strcmp( argv[argn], "-v" ) == 0 ) vhost = 1; else if ( strcmp( argv[argn], "-l" ) == 0 && argn + 1 < argc ) { ++argn; logfile = argv[argn]; } else if ( strcmp( argv[argn], "-i" ) == 0 && argn + 1 < argc ) { ++argn; pidfile = argv[argn]; } else if ( strcmp( argv[argn], "-t" ) == 0 && argn + 1 < argc ) { ++argn; web_timeout = atoi(argv[argn]); } else if ( strcmp( argv[argn], "-T" ) == 0 && argn + 1 < argc ) { ++argn; charset = argv[argn]; } else if ( strcmp( argv[argn], "-P" ) == 0 && argn + 1 < argc ) { ++argn; p3p = argv[argn]; } else if ( strcmp( argv[argn], "-M" ) == 0 && argn + 1 < argc ) { ++argn; max_age = atoi( argv[argn] ); } else exit(1);//usage(); ++argn; } if ( argn != argc ) exit(1);//usage(); cp = strrchr( argv0, '/' ); if ( cp != (char*) 0 ) ++cp; else cp = argv0; // openlog( cp, LOG_NDELAY|LOG_PID, LOG_DAEMON ); openlog( "auth", 0 , LOG_AUTH ); if ( port == -1 ) { #ifdef USE_SSL if ( do_ssl ) port = DEFAULT_HTTPS_PORT; else port = DEFAULT_HTTP_PORT; #else /* USE_SSL */ port = DEFAULT_HTTP_PORT; #endif /* USE_SSL */ } /* Log file. */ if ( logfile != (char*) 0 ) { logfp = fopen( logfile, "a" ); if ( logfp == (FILE*) 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "%s - %m", logfile ); perror( logfile ); #endif exit( 1 ); } } /* Look up hostname. */ lookup_hostname( &host_addr4, sizeof(host_addr4), &gotv4, &host_addr6, sizeof(host_addr6), &gotv6 ); if ( hostname == (char*) 0 ) { (void) gethostname( hostname_buf, sizeof(hostname_buf) ); hostname = hostname_buf; } if ( ! ( gotv4 || gotv6 ) ) { #ifdef SYSLOG syslog( LOG_CRIT, "can't find any valid address" ); #endif // (void) fprintf( stderr, "%s: can't find any valid address\n", argv0 ); exit( 1 ); } #ifdef HAVE_MY_IPV6 struct sockaddr_in6 clientIpv6Addr; gotv6 = 0; gotv4 = 0; listen6_fd = -1; listen4_fd = -1; mylisten6_fd = get_ipv6_socket(); #else /* Initialize listen sockets. Try v6 first because of a Linux peculiarity; ** like some other systems, it has magical v6 sockets that also listen for ** v4, but in Linux if you bind a v4 socket first then the v6 bind fails. */ if ( gotv6 ) listen6_fd = initialize_listen_socket( &host_addr6 ); else listen6_fd = -1; if ( gotv4 ) listen4_fd = initialize_listen_socket( &host_addr4 ); else listen4_fd = -1; /* If we didn't get any valid sockets, fail. */ if ( listen4_fd == -1 && listen6_fd == -1 ) { #ifdef SYSLOG syslog( LOG_CRIT, "can't bind to any address" ); #endif // (void) fprintf( stderr, "%s: can't bind to any address\n", argv0 ); exit( 1 ); } #endif /* end HAVE_MY_IPV6 */ #ifdef USE_SSL if ( do_ssl ) { SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); ssl_ctx = SSL_CTX_new( SSLv23_server_method() ); if ( certfile[0] != '\0' ) if ( SSL_CTX_use_certificate_file( ssl_ctx, certfile, SSL_FILETYPE_PEM ) == 0 || SSL_CTX_use_PrivateKey_file( ssl_ctx, certfile, SSL_FILETYPE_PEM ) == 0 || SSL_CTX_check_private_key( ssl_ctx ) == 0 ) { ERR_print_errors_fp( stderr ); exit( 1 ); } if ( cipher != (char*) 0 ) { if ( SSL_CTX_set_cipher_list( ssl_ctx, cipher ) == 0 ) { ERR_print_errors_fp( stderr ); exit( 1 ); } } } #endif /* USE_SSL */ if ( ! debug ) { /* Make ourselves a daemon. */ #ifdef HAVE_DAEMON if ( daemon( 1, 1 ) < 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "daemon - %m" ); perror( "daemon" ); #endif exit( 1 ); } #else switch ( fork() ) { case 0: break; case -1: #ifdef SYSLOG syslog( LOG_CRIT, "fork - %m" ); perror( "fork" ); #endif exit( 1 ); default: exit( 0 ); } #ifdef HAVE_SETSID (void) setsid(); #endif #endif } else { /* Even if we don't daemonize, we still want to disown our parent ** process. */ #ifdef HAVE_SETSID (void) setsid(); #endif /* HAVE_SETSID */ } if ( pidfile != (char*) 0 ) { /* Write the PID file. */ FILE* pidfp = fopen( pidfile, "w" ); if ( pidfp == (FILE*) 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "%s - %m", pidfile ); perror( pidfile ); #endif exit( 1 ); } (void) fprintf( pidfp, "%d\n", (int) getpid() ); (void) fclose( pidfp ); } /* Read zone info now, in case we chroot(). */ tzset(); /* If we're root, start becoming someone else. */ if ( getuid() == 0 ) { struct passwd *iuser, * pwd = NULL; FILE *stream; stream = fopen("/etc/passwd", "r"); while((iuser = fgetpwent(stream))!=0){ if(!strcmp(iuser->pw_name, user)) { pwd = iuser; break; } } fclose(stream); // struct passwd* pwd; // pwd = getpwnam( user ); if ( pwd == (struct passwd*) 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "unknown user - '%s'", user ); #endif // (void) fprintf( stderr, "%s: unknown user - '%s'\n", argv0, user ); exit( 1 ); } /* Set aux groups to null. */ if ( setgroups( 0, (gid_t*) 0 ) < 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "setgroups - %m" ); perror( "setgroups" ); #endif exit( 1 ); } /* Set primary group. */ if ( setgid( pwd->pw_gid ) < 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "setgid - %m" ); perror( "setgid" ); #endif exit( 1 ); } /* Try setting aux groups correctly - not critical if this fails. */ if ( initgroups( user, pwd->pw_gid ) < 0 ) { #ifdef SYSLOG syslog( LOG_ERR, "initgroups - %m" ); perror( "initgroups" ); #endif } #ifdef HAVE_SETLOGIN /* Set login name. */ (void) setlogin( user ); #endif /* HAVE_SETLOGIN */ /* Save the new uid for setting after we chroot(). */ uid = pwd->pw_uid; } /* Switch directories if requested. */ if ( dir != (char*) 0 ) { if ( chdir( dir ) < 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "chdir - %m" ); perror( "chdir" ); #endif exit( 1 ); } } /* Ron Get current directory */ /* Get current directory. */ if(!getcwd( cwd, sizeof(cwd) - 1 )) perror("getcwd"); if ( cwd[strlen( cwd ) - 1] != '/' ) (void) strcat( cwd, "/" ); /* Chroot if requested. */ //printf("do_chroot = %d\n",do_chroot); if ( do_chroot ) { if ( chroot( cwd ) < 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "chroot - %m" ); perror( "chroot" ); #endif exit( 1 ); } (void) strcpy( cwd, "/" ); /* Always chdir to / after a chroot. */ if ( chdir( cwd ) < 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "chroot chdir - %m" ); perror( "chroot chdir" ); #endif exit( 1 ); } } /* If we're root, become someone else. */ if ( getuid() == 0 ) { /* Set uid. */ if ( setuid( uid ) < 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "setuid - %m" ); perror( "setuid" ); #endif exit( 1 ); } /* Check for unnecessary security exposure. */ if ( ! do_chroot ) { #ifdef SYSLOG //syslog( LOG_NOTICE, // "started as root without requesting chroot(), warning only" ); #endif // (void) fprintf( stderr, // "%s: started as root without requesting chroot(), warning only\n", argv0 ); } } /* Catch various signals. */ #ifdef HAVE_SIGSET //printf("<%s,%d> --------\n",__FUNCTION__,__LINE__); (void) sigset( SIGTERM, handle_sigterm ); //(void) sigset( SIGINT, handle_sigterm ); //(void) sigset( SIGHUP, handle_sigterm ); //(void) sigset( SIGUSR1, handle_sigterm ); (void) sigset( SIGCHLD, handle_sigchld ); (void) sigset( SIGPIPE, SIG_IGN ); #else /* HAVE_SIGSET */ //printf("<%s,%d> --------\n",__FUNCTION__,__LINE__); (void) signal( SIGTERM, handle_sigterm ); //(void) signal( SIGINT, handle_sigterm ); //(void) signal( SIGHUP, handle_sigterm ); (void) signal( SIGCHLD, handle_sigchld ); (void) signal( SIGPIPE, SIG_IGN ); #endif /* HAVE_SIGSET */ /* Ron */ /* change www dir */ (void) signal( SIGUSR1, handle_chdir); /* record last access */ (void) signal( SIGUSR2, handle_web_time ); /* logout */ (void) signal( SIGHUP, handle_logout); (void) signal( SIGINT, handle_timeout_stat); /* Ron */ init_mime(); #if defined (CC3020_633) || defined(X86) int udp_fd; udp_fd = mini_httpd_app_init(); #endif #ifdef HAVE_RAW_WEB mini_raw_socket(&mini_http_raw_pkt_sock, MOS_RAW_PKT_FOR_WEB, HAL_NETLINK_CMD_FOR_WEB); #endif #ifdef SYSLOG if ( hostname == (char*) 0 ) syslog( LOG_NOTICE, "%.80s starting on port %d", SERVER_SOFTWARE, port ); else syslog( LOG_NOTICE, "%.80s starting on %.80s, port %d", SERVER_SOFTWARE, hostname, port ); #endif /* Main loop. */ for (;;) { /* Possibly do a select() on the possible two listen fds. */ FD_ZERO( &lfdset ); maxfd = -1; if ( listen4_fd != -1 ) { FD_SET( listen4_fd, &lfdset ); if ( listen4_fd > maxfd ) maxfd = listen4_fd; } if ( listen6_fd != -1 ) { FD_SET( listen6_fd, &lfdset ); if ( listen6_fd > maxfd ) maxfd = listen6_fd; } #if defined (CC3020_633) || defined(X86) FD_SET( udp_fd, &lfdset ); if ( udp_fd > maxfd ) maxfd = udp_fd; #endif #ifdef HAVE_RAW_WEB if(mini_http_raw_pkt_sock.sock != -1) { FD_SET( mini_http_raw_pkt_sock.sock, &lfdset ); if ( mini_http_raw_pkt_sock.sock > maxfd ) maxfd = mini_http_raw_pkt_sock.sock; } #endif #ifdef HAVE_MY_IPV6 if ( mylisten6_fd != -1 ) { FD_SET( mylisten6_fd, &lfdset ); if ( mylisten6_fd > maxfd ) maxfd = mylisten6_fd; } if ( mylisten6_fd != -1) #else if ( listen4_fd != -1 #ifdef USE_IPV6 && listen6_fd != -1 #endif ) #endif { //if ( select( maxfd + 1, &lfdset, (fd_set*) 0, (fd_set*) 0, (struct timeval*) 0 ) < 0 ) if ( select( maxfd + 1, &lfdset,(fd_set*) 0, (fd_set*) 0,NULL ) > 0 ) { /* (If we don't have two listen fds, we can just skip the select() ** and fall through. Whichever listen fd we do have will do a ** blocking accept() instead.) */ /* Accept the new connection. */ sz = sizeof(usa); if ( listen4_fd != -1 && FD_ISSET( listen4_fd, &lfdset ) ) conn_fd = accept( listen4_fd, &usa.sa, &sz ); else if ( listen6_fd != -1 && FD_ISSET( listen6_fd, &lfdset ) ) conn_fd = accept( listen6_fd, &usa.sa, &sz ); #ifdef HAVE_MY_IPV6 else if ( mylisten6_fd != -1 && FD_ISSET( mylisten6_fd, &lfdset ) ) { sz = sizeof(struct sockaddr); conn_fd = accept( mylisten6_fd, (struct sockaddr *) &clientIpv6Addr, &sz ); } #endif #if defined (CC3020_633) || defined(X86) else if ( udp_fd!= -1 && FD_ISSET( udp_fd, &lfdset ) ) { if(fork() == 0 ) mini_httpd_app_handle(udp_fd); else continue; } #endif #ifdef HAVE_RAW_WEB else if ( mini_http_raw_pkt_sock.sock != -1 && FD_ISSET( mini_http_raw_pkt_sock.sock, &lfdset ) ) { is_mini_raw_sockfd = 1; mini_raw_sock_busy = 1; goto PP; } #endif else { #ifdef SYSLOG syslog( LOG_CRIT, "select failure" ); #endif // (void) fprintf( stderr, "%s: select failure\n", argv0 ); exit( 1 ); } if ( conn_fd < 0 ) { if ( errno == EINTR ) continue; /* try again */ #ifdef EPROTO if ( errno == EPROTO ) continue; /* try again */ #endif /* EPROTO */ #ifdef SYSLOG syslog( LOG_CRIT, "accept - %m" ); perror( "accept" ); #endif exit( 1 ); } /* Fork a sub-process to handle the connection. */ /* if someone_in_use=1 will show auth fail msg */ /* web_last_access_time is record user last action */ /* if does not the same user and per user not timeout*/ //printf("client IP: %s remote_ip:%s\n",ntoa(&usa),remote_ip); if (*last_remote_ip=='\0'){ //printf("-----------------line %d ---------------\n",__LINE__); someone_in_use=0; strcpy(last_remote_ip,remote_ip); strcpy(remote_ip,ntoa(&usa)); }else if (strcmp(remote_ip,ntoa(&usa))!=0 && !check_timeout() && last_access_time!=0){ /* For enable multi-user login */ LOGOUT; someone_in_use=0; //someone_in_use=1; } else{ someone_in_use=0; strcpy(last_remote_ip,remote_ip); strcpy(remote_ip,ntoa(&usa)); } //syslog(LOG_INFO,"some %d DEBUG IP:%s <%s>",someone_in_use,remote_ip,last_remote_ip); /* Ron */ PP: r = fork(); if ( r < 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "fork - %m" ); perror( "fork" ); #endif exit( 1 ); } else if ( r == 0 ) { //is_mini_raw_sockfd = 1; //printf("Child process: fd=%d, is_mini_raw_sockfd=%d\n",mini_http_raw_pkt_sock.sock, is_mini_raw_sockfd); /* Child process. */ client_addr = usa; if ( listen4_fd != -1 ) { FD_CLR(listen4_fd, &lfdset); (void) close( listen4_fd ); } if ( listen6_fd != -1 ) { FD_CLR(listen6_fd, &lfdset); (void) close( listen6_fd ); } #ifdef HAVE_RAW_WEB mini_httpd_debug("<%s,%d> webserver Child process handle start\n", __FUNCTION__, __LINE__); handle_request(); mini_raw_sock_busy = 0; mini_httpd_debug("<%s,%d> webserver Child process handle finished\n", __FUNCTION__, __LINE__); if(is_mini_raw_sockfd) { FILE *fp; fp = fopen("/tmp/raw_finished", "w"); fclose(fp); mini_raw_shutdown(); mini_httpd_debug("<%s,%d> do-file: \"mini_raw_shutdown and exit\"************\n", __FUNCTION__, __LINE__); } #else handle_request(); #endif exit( 0 ); } #ifdef HAVE_RAW_WEB if(is_mini_raw_sockfd) { int cnt = 0; struct stat st; memset(&st, 0, sizeof(st)); while(stat("/tmp/raw_finished", &st) == -1) { cnt++; if(cnt == 1000) break; usleep(5000); } if(cnt != 1000) { unlink("/tmp/raw_finished"); printf("<%s,%d>webserver Child process handle finished!\n", __FUNCTION__, __LINE__); } else { printf("<%s,%d>webserver Child process handle timeout!\n", __FUNCTION__, __LINE__); } is_mini_raw_sockfd = 0; mini_raw_sock_busy = 0; FD_CLR(mini_http_raw_pkt_sock.sock, &lfdset); } else #endif { (void) close( conn_fd ); } } } } } #if 0 static void usage( void ) { #ifdef USE_SSL (void) fprintf( stderr, "usage: %s [-C configfile] [-D] [-S] [-E certfile] [-Y cipher] [-p port] [-d dir] [-c cgipat] [-u user] [-h hostname] [-r] [-v] [-l logfile] [-i pidfile] [-T charset] [-P P3P] [-M maxage]\n", argv0 ); #else /* USE_SSL */ (void) fprintf( stderr, "usage: %s [-C configfile] [-D] [-p port] [-d dir] [-c cgipat] [-u user] [-h hostname] [-r] [-v] [-l logfile] [-i pidfile] [-T charset] [-P P3P] [-M maxage]\n", argv0 ); #endif /* USE_SSL */ exit( 1 ); } #endif static void read_config( char* filename ) { FILE* fp; char line[10000]; char* cp; char* cp2; char* name; char* value; fp = fopen( filename, "r" ); if ( fp == (FILE*) 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "%s - %m", filename ); perror( filename ); #endif exit( 1 ); } while ( fgets( line, sizeof(line), fp ) != (char*) 0 ) { /* Trim comments. */ if ( ( cp = strchr( line, '#' ) ) != (char*) 0 ) *cp = '\0'; /* Split line into words. */ for ( cp = line; *cp != '\0'; cp = cp2 ) { /* Skip leading whitespace. */ cp += strspn( cp, " \t\n\r" ); /* Find next whitespace. */ cp2 = cp + strcspn( cp, " \t\n\r" ); /* Insert EOS and advance next-word pointer. */ while ( *cp2 == ' ' || *cp2 == '\t' || *cp2 == '\n' || *cp2 == '\r' ) *cp2++ = '\0'; /* Split into name and value. */ name = cp; value = strchr( name, '=' ); if ( value != (char*) 0 ) *value++ = '\0'; /* Interpret. */ if ( strcasecmp( name, "debug" ) == 0 ) { no_value_required( name, value ); debug = 1; } else if ( strcasecmp( name, "port" ) == 0 ) { value_required( name, value ); port = atoi( value ); } else if ( strcasecmp( name, "dir" ) == 0 ) { value_required( name, value ); dir = e_strdup( value ); } else if ( strcasecmp( name, "chroot" ) == 0 ) { no_value_required( name, value ); do_chroot = 1; } else if ( strcasecmp( name, "nochroot" ) == 0 ) { no_value_required( name, value ); do_chroot = 0; } else if ( strcasecmp( name, "user" ) == 0 ) { value_required( name, value ); user = e_strdup( value ); } else if ( strcasecmp( name, "cgipat" ) == 0 ) { value_required( name, value ); cgi_pattern = e_strdup( value ); } else if ( strcasecmp( name, "urlpat" ) == 0 ) { value_required( name, value ); url_pattern = e_strdup( value ); } else if ( strcasecmp( name, "noemptyreferers" ) == 0 ) { value_required( name, value ); no_empty_referers = 1; } else if ( strcasecmp( name, "localpat" ) == 0 ) { value_required( name, value ); local_pattern = e_strdup( value ); } else if ( strcasecmp( name, "host" ) == 0 ) { value_required( name, value ); hostname = e_strdup( value ); } else if ( strcasecmp( name, "logfile" ) == 0 ) { value_required( name, value ); logfile = e_strdup( value ); } else if ( strcasecmp( name, "vhost" ) == 0 ) { no_value_required( name, value ); vhost = 1; } else if ( strcasecmp( name, "pidfile" ) == 0 ) { value_required( name, value ); pidfile = e_strdup( value ); } else if ( strcasecmp( name, "charset" ) == 0 ) { value_required( name, value ); charset = e_strdup( value ); } else if ( strcasecmp( name, "p3p" ) == 0 ) { value_required( name, value ); p3p = e_strdup( value ); } else if ( strcasecmp( name, "max_age" ) == 0 ) { value_required( name, value ); max_age = atoi( value ); } #ifdef USE_SSL else if ( strcasecmp( name, "ssl" ) == 0 ) { no_value_required( name, value ); do_ssl = 1; } else if ( strcasecmp( name, "certfile" ) == 0 ) { value_required( name, value ); certfile = e_strdup( value ); } else if ( strcasecmp( name, "cipher" ) == 0 ) { value_required( name, value ); cipher = e_strdup( value ); } #endif /* USE_SSL */ else { // (void) fprintf( // stderr, "%s: unknown config option '%s'\n", argv0, name ); exit( 1 ); } } } (void) fclose( fp ); } static void value_required( char* name, char* value ) { if ( value == (char*) 0 ) { // (void) fprintf( // stderr, "%s: value required for %s option\n", argv0, name ); exit( 1 ); } } static void no_value_required( char* name, char* value ) { if ( value != (char*) 0 ) { // (void) fprintf( // stderr, "%s: no value required for %s option\n", // argv0, name ); exit( 1 ); } } static int initialize_listen_socket( usockaddr* usaP ) { int listen_fd; int i; /* Check sockaddr. */ if ( ! sockaddr_check( usaP ) ) { #ifdef SYSLOG syslog( LOG_ERR, "unknown sockaddr family on listen socket - %d", usaP->sa.sa_family ); #endif // (void) fprintf( // stderr, "%s: unknown sockaddr family on listen socket - %d\n", // argv0, usaP->sa.sa_family ); return -1; } listen_fd = socket( usaP->sa.sa_family, SOCK_STREAM, 0 ); if ( listen_fd < 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "socket %.80s - %m", ntoa( usaP ) ); perror( "socket" ); #endif return -1; } (void) fcntl( listen_fd, F_SETFD, 1 ); i = 1; if ( setsockopt( listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*) &i, sizeof(i) ) < 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "setsockopt SO_REUSEADDR - %m" ); perror( "setsockopt SO_REUSEADDR" ); #endif return -1; } if ( bind( listen_fd, &usaP->sa, sockaddr_len( usaP ) ) < 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "bind %.80s - %m", ntoa( usaP ) ); perror( "bind" ); #endif return -1; } if ( listen( listen_fd, 1024 ) < 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "listen - %m" ); perror( "listen" ); #endif return -1; } #ifdef HAVE_ACCEPT_FILTERS { struct accept_filter_arg af; (void) bzero( &af, sizeof(af) ); (void) strcpy( af.af_name, ACCEPT_FILTER_NAME ); (void) setsockopt( listen_fd, SOL_SOCKET, SO_ACCEPTFILTER, (char*) &af, sizeof(af) ); } #endif /* HAVE_ACCEPT_FILTERS */ return listen_fd; } /* This runs in a child process, and exits when done, so cleanup is ** not needed. */ static void handle_request( void ) { char* method_str; char* line; char* cp; int r, file_len, i; const char* index_names[] = { "login.html","index.html", "index.htm", "index_en.htm", "index.xhtml", "index.xht", "Default.htm", }; /* Set up a timeout. */ #ifdef HAVE_SIGSET (void) sigset( SIGALRM, handle_sigalrm ); #else /* HAVE_SIGSET */ (void) signal( SIGALRM, handle_sigalrm ); #endif /* HAVE_SIGSET */ if(!is_mini_raw_sockfd) (void) alarm( TIMEOUT ); /* Initialize the request variables. */ remoteuser = (char*) 0; method = METHOD_UNKNOWN; path = (char*) 0; file = (char*) 0; pathinfo = (char*) 0; query = ""; protocol = (char*) 0; status = 0; bytes = -1; req_hostname = (char*) 0; authorization = (char*) 0; content_type = (char*) 0; content_length = -1; cookie = (char*) 0; host = (char*) 0; if_modified_since = (time_t) -1; referer = ""; useragent = ""; #ifdef TCP_NOPUSH /* Set the TCP_NOPUSH socket option, to try and avoid the 0.2 second ** delay between sending the headers and sending the data. A better ** solution is writev() (as used in thttpd), or send the headers with ** send(MSG_MORE) (only available in Linux so far). */ r = 1; (void) setsockopt( conn_fd, IPPROTO_TCP, TCP_NOPUSH, (void*) &r, sizeof(r) ); #endif /* TCP_NOPUSH */ #ifdef USE_SSL if ( do_ssl ) { ssl = SSL_new( ssl_ctx ); SSL_set_fd( ssl, conn_fd ); if ( SSL_accept( ssl ) == 0 ) { ERR_print_errors_fp( stderr ); exit( 1 ); } } #endif /* USE_SSL */ /* Read in the request. */ start_request(); char buf[20000]; memset(buf,0,sizeof(buf)); if(is_mini_raw_sockfd) { r = my_read( buf, sizeof(buf) ); //printf("<%s,%d> is_mini_raw_sockfd=%d!!!!\n", __FUNCTION__, __LINE__, is_mini_raw_sockfd); //printf("\t\nrecv buf:\n%s\n",buf); add_to_request( buf, r ); } else { for (;;) { r = my_read( buf, sizeof(buf) ); //printf("<%s,%d> is_mini_raw_sockfd=%d!!!!\n", __FUNCTION__, __LINE__, is_mini_raw_sockfd); //printf("\t\nrecv buf:\n%s\n",buf); if ( r <= 0 ) break; #if defined (CC3020_633) || defined(X86) is_http = 1; #endif (void) alarm( TIMEOUT ); add_to_request( buf, r ); if ( strstr( request, "\r\n\r\n" ) != (char*) 0 || strstr( request, "\n\n" ) != (char*) 0 ) break; } } //printf("\t\nrequest:\n%s",request); /* Parse the first line of the request. */ method_str = get_request_line(); /* then the request only store the first line string */ //printf("\t\nmethod_str:%s\n",method_str); if ( method_str == (char*) 0 ) send_error( 400, "Bad Request", "", "Can't parse request." ); path = strpbrk( method_str, " \t\n\r" ); if ( path == (char*) 0 ) send_error( 400, "Bad Request", "", "Can't parse request." ); *path++ = '\0'; /* Ron add for auto add setup.cgi?next_file*/ // if(strstr(path,".cgi")==NULL && strstr(path,".htm")){ // if(*path=='/') path++; // sprintf(fakepath,"/setup.cgi?next_file=%s",path); // path=fakepath; // } path += strspn( path, " \t\n\r" ); protocol = strpbrk( path, " \t\n\r" ); if ( protocol == (char*) 0 ) send_error( 400, "Bad Request", "", "Can't parse request." ); *protocol++ = '\0'; query = strchr( path, '?' ); if ( query == (char*) 0 ) query = ""; else *query++ = '\0'; /* Parse the rest of the request headers except the fist line. */ while ( ( line = get_request_line() ) != (char*) 0 ) { if ( line[0] == '\0' ) break; else if ( strncasecmp( line, "Authorization:", 14 ) == 0 ) { cp = &line[14]; cp += strspn( cp, " \t" ); authorization = cp; } else if ( strncasecmp( line, "Content-Length:", 15 ) == 0 ) { cp = &line[15]; cp += strspn( cp, " \t" ); content_length = atol( cp ); } else if ( strncasecmp( line, "Content-Type:", 13 ) == 0 ) { cp = &line[13]; cp += strspn( cp, " \t" ); content_type = cp; } else if ( strncasecmp( line, "Cookie:", 7 ) == 0 ) { cp = &line[7]; cp += strspn( cp, " \t" ); cookie = cp; } else if ( strncasecmp( line, "Host:", 5 ) == 0 ) { cp = &line[5]; cp += strspn( cp, " \t" ); host = cp; } else if ( strncasecmp( line, "If-Modified-Since:", 18 ) == 0 ) { cp = &line[18]; cp += strspn( cp, " \t" ); if_modified_since = tdate_parse( cp ); } else if ( strncasecmp( line, "Referer:", 8 ) == 0 ) { cp = &line[8]; cp += strspn( cp, " \t" ); referer = cp; } else if ( strncasecmp( line, "User-Agent:", 11 ) == 0 ) { cp = &line[11]; cp += strspn( cp, " \t" ); useragent = cp; } } if ( strcasecmp( method_str, get_method_str( METHOD_GET ) ) == 0 ) method = METHOD_GET; else if ( strcasecmp( method_str, get_method_str( METHOD_HEAD ) ) == 0 ) method = METHOD_HEAD; else if ( strcasecmp( method_str, get_method_str( METHOD_POST ) ) == 0 ) method = METHOD_POST; else send_error( 501, "Not Implemented", "", "That method is not implemented." ); strdecode( path, path ); if ( path[0] != '/' ) send_error( 400, "Bad Request", "", "Bad filename." ); /*Ron*/ if ( someone_in_use == 1 ) send_error( 401, "Unauthorized", "", "Another Administrator online." ); /*Ron*/ file = &(path[1]); de_dotdot( file ); if ( file[0] == '\0' ) file = "./"; if ( file[0] == '/' || ( file[0] == '.' && file[1] == '.' && ( file[2] == '\0' || file[2] == '/' ) ) ) send_error( 400, "Bad Request", "", "Illegal filename." ); if ( vhost ) file = virtual_file( file ); //printf("####################file: %s\n",file); (void) alarm( 0 ); r = stat( file, &sb ); if ( r < 0 ) r = get_pathinfo(); if ( r < 0 ) send_error( 404, "Not Found", "", "File not found." ); file_len = strlen( file ); if ( ! S_ISDIR( sb.st_mode ) ) { /* Not a directory. */ while ( file[file_len - 1] == '/' ) { file[file_len - 1] = '\0'; --file_len; } do_file(); } else { char idx[10000]; /* The filename is a directory. Is it missing the trailing slash? */ if ( file[file_len - 1] != '/' && pathinfo == (char*) 0 ) { char location[10000]; if ( query[0] != '\0' ) (void) snprintf( location, sizeof(location), "Location: %s/?%s", path, query ); else (void) snprintf( location, sizeof(location), "Location: %s/", path ); //printf("-----line %d location:%s\n",__LINE__,location); send_error( 302, "Found", location, "Directories must end with a slash." ); } /* Check for an index file. */ for ( i = 0; i < sizeof(index_names) / sizeof(char*); ++i ) { if(strcmp(file,"./")==0) (void) snprintf( idx, sizeof(idx), "%s",index_names[i] ); else (void) snprintf( idx, sizeof(idx), "%s%s", file, index_names[i] ); if ( stat( idx, &sb ) >= 0 ) { file = idx; do_file(); continue; } } //printf("do_dir\n"); /* Nope, no index file, so it's an actual directory request. */ // do_dir(); // got_one: } #ifdef USE_SSL SSL_free( ssl ); #endif /* USE_SSL */ } static void de_dotdot( char* file ) { char* cp; char* cp2; int l; /* Collapse any multiple / sequences. */ while ( ( cp = strstr( file, "//") ) != (char*) 0 ) { for ( cp2 = cp + 2; *cp2 == '/'; ++cp2 ) continue; (void) strcpy( cp + 1, cp2 ); } /* Remove leading ./ and any /./ sequences. */ while ( strncmp( file, "./", 2 ) == 0 ) (void) strcpy( file, file + 2 ); while ( ( cp = strstr( file, "/./") ) != (char*) 0 ) (void) strcpy( cp, cp + 2 ); /* Alternate between removing leading ../ and removing xxx/../ */ for (;;) { while ( strncmp( file, "../", 3 ) == 0 ) (void) strcpy( file, file + 3 ); cp = strstr( file, "/../" ); if ( cp == (char*) 0 ) break; for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 ) continue; (void) strcpy( cp2 + 1, cp + 4 ); } /* Also elide any xxx/.. at the end. */ while ( ( l = strlen( file ) ) > 3 && strcmp( ( cp = file + l - 3 ), "/.." ) == 0 ) { for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 ) continue; if ( cp2 < file ) break; *cp2 = '\0'; } } static int get_pathinfo( void ) { int r; pathinfo = &file[strlen(file)]; for (;;) { do { --pathinfo; if ( pathinfo <= file ) { pathinfo = (char*) 0; return -1; } } while ( *pathinfo != '/' ); *pathinfo = '\0'; r = stat( file, &sb ); if ( r >= 0 ) { ++pathinfo; return r; } else *pathinfo = '/'; } } static void do_file( void ) { char buf[10000]; char mime_encodings[500]; char* mime_type; char fixed_mime_type[500]; char* cp; int fd; //printf("----------%s line %d file:%s\n",__FUNCTION__,__LINE__,file); /* Check authorization for this directory. */ (void) strncpy( buf, file, sizeof(buf) ); if(strstr(buf,"upload_lang.cgi")!=0) goto kk; /* reboot page needn't auth after upgrade finished */ if(strstr(buf,"reboot_guage.htm")!=0 || strstr(buf,".gif")!=0 || strstr(buf,".css")!=0) goto kk; cp = strrchr( buf, '/' ); if ( cp == (char*) 0 ) (void) strcpy( buf, "." ); else *cp = '\0'; auth_check( buf ); kk: /* Check if the filename is the AUTH_FILE itself - that's verboten. */ if ( strcmp( file, AUTH_FILE ) == 0 || ( strcmp( &(file[strlen(file) - sizeof(AUTH_FILE) + 1]), AUTH_FILE ) == 0 && file[strlen(file) - sizeof(AUTH_FILE)] == '/' ) ) { #ifdef SYSLOG syslog( LOG_NOTICE, "%.80s URL \"%.80s\" tried to retrieve an auth file", ntoa( &client_addr ), path ); #endif send_error( 403, "Forbidden", "", "File is protected." ); } /* Referer check. */ check_referer(); /* Is it CGI? */ char userinfo[50] = {0}; if ( cgi_pattern != (char*) 0 && match( cgi_pattern, file ) ) { #if 1 if(mark_exist(LOGIN_EVENT)) { sprintf(userinfo, "user:%s,ip:%s,%s",remoteuser,remote_ip,"login on the web"); access_user_info = userinfo; if(strstr(referer,"setup.cgi")) remove_mark(LOGIN_EVENT); } #endif do_cgi(); return; } else if ( pathinfo != (char*) 0 ) send_error( 404, "Not Found", "", "File not found." ); fd = open( file, O_RDONLY ); if ( fd < 0 ) { #ifdef SYSLOG syslog( LOG_INFO, "%.80s File \"%.80s\" is protected", ntoa( &client_addr ), path ); #endif send_error( 403, "Forbidden", "", "File is protected." ); } mime_type = figure_mime( file, mime_encodings, sizeof(mime_encodings) ); (void) snprintf( fixed_mime_type, sizeof(fixed_mime_type), mime_type, charset ); #if 0 if ( if_modified_since != (time_t) -1 && if_modified_since >= sb.st_mtime ) { add_headers( 304, "Not Modified", "", mime_encodings, fixed_mime_type, sb.st_size, sb.st_mtime ); send_response(); return; } #endif add_headers( 200, "Ok", "", mime_encodings, fixed_mime_type, sb.st_size, sb.st_mtime ); send_response(); if ( method == METHOD_HEAD ) return; if ( sb.st_size > 0 ) /* ignore zero-length files */ { #ifdef HAVE_SENDFILE #ifndef USE_SSL (void) my_sendfile( fd, conn_fd, 0, sb.st_size ); //printf("------line:%d----------file:%s -------------\n",__LINE__,file); #else /* USE_SSL */ if ( do_ssl ) send_via_write( fd, sb.st_size ); else { (void) my_sendfile( fd, conn_fd, 0, sb.st_size ); //printf("------line:%d----------file:%s -------------\n",__LINE__,file); } #endif /* USE_SSL */ #else /* HAVE_SENDFILE */ send_via_write( fd, sb.st_size ); //printf("------line:%d----------file:%s -------------\n",__LINE__,file); #endif /* HAVE_SENDFILE */ } (void) close( fd ); } #if 0 static void do_dir( void ) { char buf[10000]; size_t buflen; char* contents; size_t contents_size, contents_len; #ifdef HAVE_SCANDIR int n, i; struct dirent **dl; char* name_info; #else /* HAVE_SCANDIR */ char command[10000]; FILE* fp; #endif /* HAVE_SCANDIR */ if ( pathinfo != (char*) 0 ) send_error( 404, "Not Found", "", "File not found." ); /* Check authorization for this directory. */ auth_check( file ); /* Referer check. */ check_referer(); #ifdef HAVE_SCANDIR n = scandir( file, &dl, NULL, alphasort ); if ( n < 0 ) { syslog( LOG_INFO, "%.80s Directory \"%.80s\" is protected", ntoa( &client_addr ), path ); send_error( 403, "Forbidden", "", "Directory is protected." ); } #endif /* HAVE_SCANDIR */ contents_size = 0; buflen = snprintf( buf, sizeof(buf), "\ \n\ Index of %s\n\ \n\

Index of %s

\n\
\n",
            file, file );
    add_to_buf( &contents, &contents_size, &contents_len, buf, buflen );

#ifdef HAVE_SCANDIR

    for ( i = 0; i < n; ++i )
    {
        name_info = file_details( file, dl[i]->d_name );
        add_to_buf(
                &contents, &contents_size, &contents_len, name_info,
                strlen( name_info ) );
    }

#else /* HAVE_SCANDIR */
    /* Magic HTML ls command! */
    if ( strchr( file, '\'' ) == (char*) 0 )
    {
        (void) snprintf(
                command, sizeof(command),
                "ls -lgF '%s' | tail +2 | sed -e 's/^\\([^ ][^ ]*\\)\\(  *[^ ][^ ]*  *[^ ][^ ]*  *[^ ][^ ]*\\)\\(  *[^ ][^ ]*\\)  *\\([^ ][^ ]*  *[^ ][^ ]*  *[^ ][^ ]*\\)  *\\(.*\\)$/\\1 \\3  \\4  |\\5/' -e '/ -> /!s,|\\([^*]*\\)$,|\\1,' -e '/ -> /!s,|\\(.*\\)\\([*]\\)$,|\\1\\2,' -e '/ -> /s,|\\([^@]*\\)\\(@* -> \\),|\\1\\2,' -e 's/|//'",
                file );
        fp = popen( command, "r" );
        for (;;)
        {
            size_t r;
            r = fread( buf, 1, sizeof(buf), fp );
            if ( r == 0 )
                break;
            add_to_buf( &contents, &contents_size, &contents_len, buf, r );
        }
        (void) pclose( fp );
    }
#endif /* HAVE_SCANDIR */

    buflen = snprintf( buf, sizeof(buf), "\
            
\n\
\n\
%s
\n\ \n\ \n", SERVER_URL, SERVER_SOFTWARE ); add_to_buf( &contents, &contents_size, &contents_len, buf, buflen ); add_headers( 200, "Ok", "", "", "text/html", contents_len, sb.st_mtime ); if ( method != METHOD_HEAD ) add_to_response( contents, contents_len ); send_response(); } #endif #ifdef HAVE_SCANDIR static char* file_details( const char* dir, const char* name ) { struct stat sb; char f_time[20]; static char encname[1000]; static char buf[2000]; (void) snprintf( buf, sizeof(buf), "%s/%s", dir, name ); if ( lstat( buf, &sb ) < 0 ) return "???"; (void) strftime( f_time, sizeof( f_time ), "%d%b%Y %H:%M", localtime( &sb.st_mtime ) ); strencode( encname, sizeof(encname), name ); #ifdef HAVE_INT64T (void) snprintf( buf, sizeof( buf ), "%-32.32s %15s %14lld\n", encname, name, f_time, (int64_t) sb.st_size ); #else /* HAVE_INT64T */ (void) snprintf( buf, sizeof( buf ), "%-32.32s %15s %14ld\n", encname, name, f_time, (long) sb.st_size ); #endif /* HAVE_INT64T */ return buf; } /* Copies and encodes a string. */ static void strencode( char* to, size_t tosize, const char* from ) { int tolen; for ( tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from ) { if ( isalnum(*from) || strchr( "/_.-~", *from ) != (char*) 0 ) { *to = *from; ++to; ++tolen; } else { (void) sprintf( to, "%%%02x", (int) *from & 0xff ); to += 3; tolen += 3; } } *to = '\0'; } #endif /* HAVE_SCANDIR */ static void do_cgi( void ) { char** argp; char** envp; int parse_headers; char* binary; char* directory; if ( method != METHOD_GET && method != METHOD_POST ) send_error( 501, "Not Implemented", "", "That method is not implemented for CGI." ); /* If the socket happens to be using one of the stdin/stdout/stderr ** descriptors, move it to another descriptor so that the dup2 calls ** below don't screw things up. We arbitrarily pick fd 3 - if there ** was already something on it, we clobber it, but that doesn't matter ** since at this point the only fd of interest is the connection. ** All others will be closed on exec. */ if ( conn_fd == STDIN_FILENO || conn_fd == STDOUT_FILENO || conn_fd == STDERR_FILENO ) { int newfd = dup2( conn_fd, STDERR_FILENO + 1 ); if ( newfd >= 0 ) conn_fd = newfd; /* If the dup2 fails, shrug. We'll just take our chances. ** Shouldn't happen though. */ } /* Make the environment vector. */ envp = make_envp(); /* Make the argument vector. */ argp = make_argp(); /* Set up stdin. For POSTs we may have to set up a pipe from an ** interposer process, depending on if we've read some of the data ** into our buffer. We also have to do this for all SSL CGIs. */ #ifdef USE_SSL if ( ( method == METHOD_POST && request_len > request_idx ) || do_ssl ) #else /* USE_SSL */ if ( ( method == METHOD_POST && request_len > request_idx ) ) #endif /* USE_SSL */ { int p[2]; int r; if ( pipe( p ) < 0 ) send_error( 500, "Internal Error", "", "Something unexpected went wrong making a pipe." ); r = fork(); if ( r < 0 ) send_error( 500, "Internal Error", "", "Something unexpected went wrong forking an interposer." ); if ( r == 0 ) { /* Interposer process. */ (void) close( p[0] ); cgi_interpose_input( p[1] ); exit( 0 ); } (void) close( p[1] ); if ( p[0] != STDIN_FILENO ) { (void) dup2( p[0], STDIN_FILENO ); (void) close( p[0] ); } } else { /* Otherwise, the request socket is stdin. */ if ( conn_fd != STDIN_FILENO ) (void) dup2( conn_fd, STDIN_FILENO ); } /* Set up stdout/stderr. For SSL, or if we're doing CGI header parsing, ** we need an output interposer too. */ if ( strncmp( argp[0], "nph-", 4 ) == 0 ) parse_headers = 0; else parse_headers = 1; #ifdef USE_SSL if ( parse_headers || do_ssl ) #else /* USE_SSL */ if ( parse_headers ) #endif /* USE_SSL */ { int p[2]; int r; if ( pipe( p ) < 0 ) send_error( 500, "Internal Error", "", "Something unexpected went wrong making a pipe." ); r = fork(); if ( r < 0 ) send_error( 500, "Internal Error", "", "Something unexpected went wrong forking an interposer." ); if ( r == 0 ) { /* Interposer process. */ (void) close( p[1] ); cgi_interpose_output( p[0], parse_headers ); exit( 0 ); } (void) close( p[0] ); if ( p[1] != STDOUT_FILENO ) (void) dup2( p[1], STDOUT_FILENO ); if ( p[1] != STDERR_FILENO ) (void) dup2( p[1], STDERR_FILENO ); if ( p[1] != STDOUT_FILENO && p[1] != STDERR_FILENO ) (void) close( p[1] ); } else { /* Otherwise, the request socket is stdout/stderr. */ if ( conn_fd != STDOUT_FILENO ) (void) dup2( conn_fd, STDOUT_FILENO ); if ( conn_fd != STDERR_FILENO ) (void) dup2( conn_fd, STDERR_FILENO ); } /* At this point we would like to set conn_fd to be close-on-exec. ** Unfortunately there seems to be a Linux problem here - if we ** do this close-on-exec in Linux, the socket stays open but stderr ** gets closed - the last fd duped from the socket. What a mess. ** So we'll just leave the socket as is, which under other OSs means ** an extra file descriptor gets passed to the child process. Since ** the child probably already has that file open via stdin stdout ** and/or stderr, this is not a problem. */ /* (void) fcntl( conn_fd, F_SETFD, 1 ); */ /* Close the log file. */ if ( logfp != (FILE*) 0 ) (void) fclose( logfp ); /* Close syslog. */ closelog(); /* Set priority. */ if(nice( CGI_NICE ) == -1) perror("nice"); /* Split the program into directory and binary, so we can chdir() ** to the program's own directory. This isn't in the CGI 1.1 ** spec, but it's what other HTTP servers do. */ directory = e_strdup( file ); binary = strrchr( directory, '/' ); if ( binary == (char*) 0 ) binary = file; else { *binary++ = '\0'; if(chdir( directory ) == -1) perror("chdir"); /* ignore errors */ } /* Default behavior for SIGPIPE. */ #ifdef HAVE_SIGSET (void) sigset( SIGPIPE, SIG_DFL ); #else /* HAVE_SIGSET */ (void) signal( SIGPIPE, SIG_DFL ); #endif /* HAVE_SIGSET */ /* Run the program. */ mini_httpd_debug("cgi binary name:%s\n",binary); mini_httpd_debug("argp:%s envp:%s\n",*argp,*envp); (void) execve( binary, argp, envp ); /* Something went wrong. */ send_error( 500, "Internal Error", "", "Something unexpected went wrong running a CGI program." ); } /* This routine is used only for POST requests. It reads the data ** from the request and sends it to the child process. The only reason ** we need to do it this way instead of just letting the child read ** directly is that we have already read part of the data into our ** buffer. ** ** Oh, and it's also used for all SSL CGIs. */ static void cgi_interpose_input( int wfd ) { size_t c; ssize_t r, r2; char buf[1024]; c = request_len - request_idx; if ( c > 0) { if ( write( wfd, &(request[request_idx]), c ) != c ) return; } while ( c < content_length ) { r = my_read( buf, MIN( sizeof(buf), content_length - c ) ); if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) { sleep( 1 ); continue; } if ( r <= 0 ) return; for (;;) { r2 = write( wfd, buf, r ); if ( r2 < 0 && ( errno == EINTR || errno == EAGAIN ) ) { sleep( 1 ); continue; } if ( r2 != r ) return; break; } c += r; } if(!is_mini_raw_sockfd) post_post_garbage_hack(); mini_httpd_debug("<%s,%d>****************************\n", __FUNCTION__, __LINE__); } /* Special hack to deal with broken browsers that send a LF or CRLF ** after POST data, causing TCP resets - we just read and discard up ** to 2 bytes. Unfortunately this doesn't fix the problem for CGIs ** which avoid the interposer process due to their POST data being ** short. Creating an interposer process for all POST CGIs is ** unacceptably expensive. */ static void post_post_garbage_hack( void ) { char buf[2]; #ifdef USE_SSL if ( do_ssl ) /* We don't need to do this for SSL, since the garbage has ** already been read. Probably. */ return; #endif /* USE_SSL */ set_ndelay( conn_fd ); (void) read( conn_fd, buf, sizeof(buf) ); clear_ndelay( conn_fd ); } /* This routine is used for parsed-header CGIs and for all SSL CGIs. */ static void cgi_interpose_output( int rfd, int parse_headers ) { ssize_t r, r2; char buf[1024]; #if defined (CC3020_633) || defined(X86) if ( ! parse_headers && is_http) #else if ( ! parse_headers ) #endif { /* If we're not parsing headers, write out the default status line ** and proceed to the echo phase. */ char http_head[] = "HTTP/1.0 200 OK\015\012"; (void) my_write( http_head, sizeof(http_head) ); } else { /* Header parsing. The idea here is that the CGI can return special ** headers such as "Status:" and "Location:" which change the return ** status of the response. Since the return status has to be the very ** first line written out, we have to accumulate all the headers ** and check for the special ones before writing the status. Then ** we write out the saved headers and proceed to echo the rest of ** the response. */ size_t headers_size, headers_len; char* headers; char* br; int status; char* title; char* cp; /* Slurp in all headers. */ headers_size = 0; add_to_buf( &headers, &headers_size, &headers_len, (char*) 0, 0 ); for (;;) { r = read( rfd, buf, sizeof(buf) ); if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) { sleep( 1 ); continue; } if ( r <= 0 ) { br = &(headers[headers_len]); break; } add_to_buf( &headers, &headers_size, &headers_len, buf, r ); if ( ( br = strstr( headers, "\015\012\015\012" ) ) != (char*) 0 || ( br = strstr( headers, "\012\012" ) ) != (char*) 0 ) break; } /* If there were no headers, bail. */ if ( headers[0] == '\0' ) return; /* Figure out the status. */ status = 200; if ( ( cp = strstr( headers, "Status:" ) ) != (char*) 0 && cp < br && ( cp == headers || *(cp-1) == '\012' ) ) { cp += 7; cp += strspn( cp, " \t" ); status = atoi( cp ); } if ( ( cp = strstr( headers, "Location:" ) ) != (char*) 0 && cp < br && ( cp == headers || *(cp-1) == '\012' ) ) status = 302; /* Write the status line. */ switch ( status ) { case 200: title = "OK"; break; case 302: title = "Found"; break; case 304: title = "Not Modified"; break; case 400: title = "Bad Request"; break; case 401: title = "Unauthorized"; break; case 403: title = "Forbidden"; break; case 404: title = "Not Found"; break; case 408: title = "Request Timeout"; break; case 500: title = "Internal Error"; break; case 501: title = "Not Implemented"; break; case 503: title = "Service Temporarily Overloaded"; break; default: title = "Something"; break; } #if defined (CC3020_633) || defined(X86) if(is_http) #endif { (void) snprintf( buf, sizeof(buf), "HTTP/1.0 %d %s\015\012", status, title ); (void) my_write( buf, strlen( buf ) ); } /* Write the saved headers. */ (void) my_write( headers, headers_len ); } /* Echo the rest of the output. */ if(!is_mini_raw_sockfd) { for (;;) { r = read( rfd, buf, sizeof(buf) ); if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) { sleep( 1 ); continue; } if ( r <= 0 ) goto done; for (;;) { r2 = my_write( buf, r ); if ( r2 < 0 && ( errno == EINTR || errno == EAGAIN ) ) { sleep( 1 ); continue; } if ( r2 != r ) goto done; break; } } } else { for (;;) { r = read( rfd, buf, sizeof(buf) ); if(r > 0) { my_write( buf, r ); } else { goto done; } } } done: if(is_mini_raw_sockfd) { FILE *fp; fp = fopen("/tmp/raw_finished", "w"); fclose(fp); mini_raw_shutdown(); mini_httpd_debug("<%s,%d>\"do-cgi: mini_raw_shutdown and exit\"************\n", __FUNCTION__, __LINE__); exit(0); } else { shutdown( conn_fd, SHUT_WR ); } mini_httpd_debug("<%s,%d>****************************\n", __FUNCTION__, __LINE__); } /* Set up CGI argument vector. We don't have to worry about freeing ** stuff since we're a sub-process. This gets done after make_envp() because ** we scribble on query. */ static char** make_argp( void ) { char** argp; int argn; char* cp1; char* cp2; /* By allocating an arg slot for every character in the query, plus ** one for the filename and one for the NULL, we are guaranteed to ** have enough. We could actually use strlen/2. */ argp = (char**) malloc( ( strlen( query ) + 2 ) * sizeof(char*) ); if ( argp == (char**) 0 ) return (char**) 0; argp[0] = strrchr( file, '/' ); if ( argp[0] != (char*) 0 ) ++argp[0]; else argp[0] = file; argn = 1; /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html, ** "The server should search the query information for a non-encoded = ** character to determine if the command line is to be used, if it finds ** one, the command line is not to be used." */ if ( strchr( query, '=' ) == (char*) 0 ) { for ( cp1 = cp2 = query; *cp2 != '\0'; ++cp2 ) { if ( *cp2 == '+' ) { *cp2 = '\0'; strdecode( cp1, cp1 ); argp[argn++] = cp1; cp1 = cp2 + 1; } } if ( cp2 != cp1 ) { strdecode( cp1, cp1 ); argp[argn++] = cp1; } } argp[argn] = (char*) 0; return argp; } /* Set up CGI environment variables. Be real careful here to avoid ** letting malicious clients overrun a buffer. We don't have ** to worry about freeing stuff since we're a sub-process. */ static char** make_envp( void ) { static char* envp[50]; int envn; char* cp; char buf[256]; envn = 0; envp[envn++] = build_env( "PATH=%s", CGI_PATH ); envp[envn++] = build_env( "LD_LIBRARY_PATH=%s", CGI_LD_LIBRARY_PATH ); envp[envn++] = build_env( "SERVER_SOFTWARE=%s", SERVER_SOFTWARE ); if ( ! vhost ) cp = hostname; else cp = req_hostname; /* already computed by virtual_file() */ if ( cp != (char*) 0 ) envp[envn++] = build_env( "SERVER_NAME=%s", cp ); envp[envn++] = "GATEWAY_INTERFACE=CGI/1.1"; envp[envn++] = "SERVER_PROTOCOL=HTTP/1.0"; (void) snprintf( buf, sizeof(buf), "%d", port ); envp[envn++] = build_env( "SERVER_PORT=%s", buf ); envp[envn++] = build_env( "REQUEST_METHOD=%s", get_method_str( method ) ); envp[envn++] = build_env( "SCRIPT_NAME=%s", path ); #if defined(CC3020_633) || defined(X86) if(is_http) envp[envn++] = "IS_HTTP=1"; else envp[envn++] = "IS_HTTP=0"; #endif if ( pathinfo != (char*) 0 ) { envp[envn++] = build_env( "PATH_INFO=/%s", pathinfo ); (void) snprintf( buf, sizeof(buf), "%s%s", cwd, pathinfo ); envp[envn++] = build_env( "PATH_TRANSLATED=/%s", buf ); } if ( query[0] != '\0' ) envp[envn++] = build_env( "QUERY_STRING=%s", query ); envp[envn++] = build_env( "REMOTE_ADDR=%s", ntoa( &client_addr ) ); if ( referer[0] != '\0' ) envp[envn++] = build_env( "HTTP_REFERER=%s", referer ); if ( useragent[0] != '\0' ) envp[envn++] = build_env( "HTTP_USER_AGENT=%s", useragent ); if ( cookie != (char*) 0 ) envp[envn++] = build_env( "HTTP_COOKIE=%s", cookie ); if ( content_type != (char*) 0 ) envp[envn++] = build_env( "CONTENT_TYPE=%s", content_type ); if ( content_length != -1 ) { (void) snprintf( buf, sizeof(buf), "%lu", (unsigned long) content_length ); envp[envn++] = build_env( "CONTENT_LENGTH=%s", buf ); } if ( remoteuser != (char*) 0 ) envp[envn++] = build_env( "REMOTE_USER=%s", remoteuser ); if(access_user_info != (char *)0 ) envp[envn++] = build_env( "ACCESS_USER_INFO=%s", access_user_info ); if ( authorization != (char*) 0 ) envp[envn++] = build_env( "AUTH_TYPE=%s", "Basic" ); if ( getenv( "TZ" ) != (char*) 0 ) envp[envn++] = build_env( "TZ=%s", getenv( "TZ" ) ); envp[envn] = (char*) 0; return envp; } static char* build_env( char* fmt, char* arg ) { char* cp; int size; static char* buf; static int maxbuf = 0; size = strlen( fmt ) + strlen( arg ); while ( size > maxbuf ) { if ( maxbuf == 0 ) { maxbuf = 256; buf = (char*) e_malloc( maxbuf ); } else { maxbuf += 256; buf = (char*) e_realloc( (void*) buf, maxbuf ); } } (void) snprintf( buf, maxbuf, fmt, arg ); cp = e_strdup( buf ); return cp; } static int access_time_out(void) { int fd; time_t current; char buf[2] = {"1"}; char user[50] = {0}; struct stat sb; if(web_timeout) { current = time(NULL); sprintf(user, "help/%s", remote_ip); if(!stat(user, &sb)) { if(current - sb.st_ctime > web_timeout) { chmod(user, 0644); // change ctime return 1; } else if(current != sb.st_ctime) { chmod(user, 0644); // updata ctime } } else { if((fd = open(user, O_RDWR | O_CREAT, 0666)) != -1) { write(fd,buf,1); close(fd); } return 1; } } return 0; } #if 0 static void auth_check( char* dirname ) { char authpath[10000]; struct stat sb; char authinfo[500]; char* authpass, *pt; char* colon; static char line[10000]; int l; FILE* fp, *fd; char* cryp; unsigned char buf[128]=""; fd = fopen(HTPASSWD_FILE,"r"); if(fd != NULL) { if(fgets(buf, 128, fd) != NULL) pt = strchr(buf,':'); if(strlen(pt+1) == 1) return; } /* Construct auth filename. */ if ( dirname[strlen(dirname) - 1] == '/' ) (void) snprintf( authpath, sizeof(authpath), "%s%s", dirname, AUTH_FILE ); else (void) snprintf( authpath, sizeof(authpath), "%s/%s", dirname, AUTH_FILE ); /* carl modify for http authtication*/ printf("------------line=%d authpath:%s\n",__LINE__,authpath); sprintf(authpath,HTPASSWD_FILE); /* Does this directory have an auth file? */ if ( stat( authpath, &sb ) < 0 ){ /* Ron */ if(strcmp(last_remote_ip,remote_ip)!=0){ syslog(LOG_INFO,"Administrator login successful - IP:%s",remote_ip); printf("Administrator login successful - IP:%s\n",remote_ip); SAVETIME; }else if(check_timeout()) LOGOUT; else SAVETIME; /* Ron */ /* Nope, let the request go through. */ return; } not_auth=0; /* Does this request contain authorization info? */ if ( authorization == (char*) 0 ){ not_auth=1; /* Nope, return a 401 Unauthorized. */ send_authenticate( dirname ); } /* Basic authorization info? */ if ( strncmp( authorization, "Basic ", 6 ) != 0 ){ send_authenticate( dirname ); } /* Decode it. */ l = b64_decode( &(authorization[6]), (unsigned char*) authinfo, sizeof(authinfo) - 1 ); authinfo[l] = '\0'; /* Split into user and password. */ authpass = strchr( authinfo, ':' ); if ( authpass == (char*) 0 ) { /* No colon? Bogus auth info. */ send_authenticate( dirname ); } *authpass++ = '\0'; /* If there are more fields, cut them off. */ colon = strchr( authpass, ':' ); if ( colon != (char*) 0 ) *colon = '\0'; /* Open the password file. */ fp = fopen( authpath, "r" ); if ( fp == (FILE*) 0 ) { /* The file exists but we can't open it? Disallow access. */ #ifdef SYSLOG syslog( LOG_ERR, "%.80s auth file %.80s could not be opened - %m", ntoa( &client_addr ), authpath ); #endif send_error( 403, "Forbidden", "", "File is protected." ); } /* Read it. */ while ( fgets( line, sizeof(line), fp ) != (char*) 0 ) { /* Nuke newline. */ l = strlen( line ); if ( line[l - 1] == '\n' ) line[l - 1] = '\0'; /* Split into user and encrypted password. */ cryp = strchr( line, ':' ); if ( cryp == (char*) 0 ) continue; *cryp++ = '\0'; /* Is this the right user? */ if ( strcmp( line, authinfo ) == 0 ) { struct sysinfo info; sysinfo(&info); /* Yes. */ (void) fclose( fp ); /* So is the password right? */ /*Ron*/ #if 0 if ( strcmp( crypt( authpass, cryp ), cryp ) == 0 ) #endif /* OK !*/ if ( strcmp(authpass, cryp ) == 0){ #if 0 //remove by john qian, cause second client input passwd twice if((strcmp(last_remote_ip,remote_ip)!=0)){ syslog(LOG_INFO,"Administrator login successful - IP:%s",remote_ip); /* if different user must authentication in frist time */ if (last_remote_ip[0]!=0) send_authenticate( dirname ); /*Time out */ }else #endif if(check_timeout()) { /* 2005.05.13 add by john if path is reboot_guage.htm, not send auth message */ if (strstr(dirname,"reboot_guage.htm")==(char*)0) send_authenticate( dirname ); } remoteuser = line; SAVETIME; return; }else{ /* No. */ not_auth=1; send_authenticate( dirname ); return; } } } /* Didn't find that user. Access denied. */ (void) fclose( fp ); send_authenticate( dirname ); } #endif static void auth_check( char* dirname ) { char authpath[10000]; struct stat sb; char authinfo[500]; char* authpass; char* colon; static char line[10000]; int l; FILE* fp; char* cryp; /* Construct auth filename. */ if ( dirname[strlen(dirname) - 1] == '/') (void) snprintf( authpath, sizeof(authpath), "%s%s", dirname, AUTH_FILE); else (void) snprintf( authpath, sizeof(authpath), "%s/%s", dirname, AUTH_FILE); /* Does this directory have an auth file? */ snprintf(authpath, sizeof(authpath), "%s", HTPASSWD_FILE); if (stat( authpath, &sb ) < 0) /* Nope, let the request go through. */ return; /* if no user, it is no need to verify the user. */ if(!sb.st_size) return; /* Does this request contain authorization info? */ if (authorization == (char*) 0) /* Nope, return a 401 Unauthorized. */ { char userfile[50] = {0}; sprintf(userfile,"help/%s", remote_ip); if(mark_exist(userfile)) // if cookie empty, remove the mark file remove_mark(userfile); mark_login("first"); send_authenticate( dirname ); } /* Basic authorization info? */ if ( strncmp( authorization, "Basic ", 6) != 0 ) send_authenticate( dirname ); /* Decode it. */ l = b64_decode(&(authorization[6]), (unsigned char*) authinfo, sizeof(authinfo) - 1); authinfo[l] = '\0'; /* Split into user and password. */ authpass = strchr(authinfo, ':'); if ( authpass == (char*) 0) /* No colon? Bogus auth info. */ send_authenticate(dirname); *authpass++ = '\0'; /* If there are more fields, cut them off. */ colon = strchr( authpass, ':'); if (colon != (char*) 0) *colon = '\0'; /* Open the password file. */ fp = fopen( authpath, "r" ); if (fp == (FILE*) 0) { /* The file exists but we can't open it? Disallow access. */ syslog(LOG_ERR, "%.80s auth file %.80s could not be opened - %m", ntoa( &client_addr ), authpath); send_error( 403, "Forbidden", "", "File is protected."); } /* Read it. */ while (fgets( line, sizeof(line), fp ) != (char*) 0) { /* Nuke newline. */ l = strlen( line ); if ( line[l - 1] == '\n') line[l - 1] = '\0'; /* Split into user and encrypted password. */ cryp = strchr( line, ':'); if (cryp == (char*) 0) continue; *cryp++ = '\0'; /* Is this the right user? */ if (strcmp(line, authinfo) == 0) { /* Yes. */ (void) fclose(fp); /* So is the password right? */ if (strcmp(crypt(authpass, cryp), cryp) == 0) { /* Ok! */ remoteuser = line; if(access_time_out()) { mark_login(LOGIN_EVENT); if(!mark_exist("first")) // need not authenticate twice in the first(cookie empty) send_authenticate( dirname ); else remove_mark("first"); } return; } else { /* No. */ send_authenticate( dirname ); } } } /* Didn't find that user. Access denied. */ (void) fclose( fp ); send_authenticate( dirname ); } static void send_authenticate( char* realm ) { char header[10000]; char realm_buf[64]; /* Ron */ if(http_realm==NULL) http_realm=realm; LOGOUT; /* Ron */ strncpy(realm_buf,http_realm,strlen(http_realm)-1); memset(realm_buf,0,sizeof(realm_buf)); strcpy(realm_buf,WEBVERSTION); (void) snprintf(header, sizeof(header), "WWW-Authenticate: Basic realm=\"%s\"", realm_buf); send_error( 401, "Unauthorized", header, "Authorization required." ); } static char* virtual_file( char* file ) { char* cp; static char vfile[10000]; /* Use the request's hostname, or fall back on the IP address. */ if ( host != (char*) 0 ) req_hostname = host; else { usockaddr usa; int sz = sizeof(usa); if ( getsockname( conn_fd, &usa.sa, &sz ) < 0 ) req_hostname = "UNKNOWN_HOST"; else req_hostname = ntoa( &usa ); } /* Pound it to lower case. */ for ( cp = req_hostname; *cp != '\0'; ++cp ) if ( isupper( *cp ) ) *cp = tolower( *cp ); (void) snprintf( vfile, sizeof(vfile), "%s/%s", req_hostname, file ); return vfile; } static void send_error( int s, char* title, char* extra_header, char* text ) { add_headers( s, title, extra_header, "", "text/html", (off_t) -1, (time_t) -1 ); send_error_body( s, title, text ); // send_error_tail(); send_response(); #ifdef USE_SSL SSL_free( ssl ); #endif /* USE_SSL */ exit( 1 ); } static void send_error_body( int s, char* title, char* text ) { char filename[1000]; char buf[10000]; int buflen; if ( vhost && req_hostname != (char*) 0 ) { /* Try virtual-host custom error page. */ (void) snprintf( filename, sizeof(filename), "%s/%s/err%d.html", req_hostname, ERR_DIR, s ); if ( send_error_file( filename ) ) return; } /* Try server-wide custom error page. */ if(someone_in_use==1) (void) snprintf(filename, sizeof(filename), "%s/err888.html", ERR_DIR); else if(not_auth==1) (void) snprintf(filename, sizeof(filename), "%s/err%d.htm", ERR_DIR, s ); else (void) snprintf(filename, sizeof(filename), "%s/err%d.html", ERR_DIR, s ); if ( send_error_file( filename ) ) return; /* Send built-in error page. */ buflen = snprintf( buf, sizeof(buf), "\ \n\ %d %s\n\

%d %s

\n\ \n\
%s at %s port %d
\n", s, title, s, title ,WEBVERSTION,remote_ip,port); add_to_response( buf, buflen ); buflen = snprintf( buf, sizeof(buf), "
%s
\n", text ); add_to_response( buf, buflen ); } static int send_error_file( char* filename ) { FILE* fp; char buf[1000]; size_t r; fp = fopen( filename, "r" ); if ( fp == (FILE*) 0 ) return 0; for (;;) { r = fread( buf, 1, sizeof(buf), fp ); if ( r == 0 ) break; add_to_response( buf, r ); } (void) fclose( fp ); return 1; } static void send_error_tail( void ) { char buf[500]; int buflen; if ( match( "**MSIE**", useragent ) ) { int n; buflen = snprintf( buf, sizeof(buf), "\n" ); add_to_response( buf, buflen ); } buflen = snprintf( buf, sizeof(buf), "\
\n\
%s
\n\ \n\ \n", SERVER_URL, SERVER_SOFTWARE ); add_to_response( buf, buflen ); } static void add_headers( int s, char* title, char* extra_header, char* me, char* mt, off_t b, time_t mod ) { time_t now, expires; char timebuf[100]; char buf[10000]; int buflen; const char* rfc1123_fmt = "%a, %d %b %Y %H:%M:%S GMT"; status = s; bytes = b; make_log_entry(); start_response(); buflen = snprintf( buf, sizeof(buf), "%s %d %s\r\n", protocol, status, title ); add_to_response( buf, buflen ); buflen = snprintf( buf, sizeof(buf), "Server: %s\r\n", SERVER_SOFTWARE ); add_to_response( buf, buflen ); //now = time( (time_t*) 0 ); //now = sb.st_atime; //(void) strftime( timebuf, sizeof(timebuf), rfc1123_fmt, gmtime( &now ) ); //buflen = snprintf( buf, sizeof(buf), "Date: %s\r\n", timebuf ); //add_to_response( buf, buflen ); if ( extra_header != (char*) 0 && extra_header[0] != '\0' ) { buflen = snprintf( buf, sizeof(buf), "%s\r\n", extra_header ); add_to_response( buf, buflen ); } if ( me != (char*) 0 && me[0] != '\0' ) { buflen = snprintf( buf, sizeof(buf), "Content-Encoding: %s\r\n", me ); add_to_response( buf, buflen ); } if ( mt != (char*) 0 && mt[0] != '\0' ) { buflen = snprintf( buf, sizeof(buf), "Content-Type: %s\r\n", mt ); add_to_response( buf, buflen ); } if ( bytes >= 0 ) { #ifdef HAVE_INT64T buflen = snprintf( buf, sizeof(buf), "Content-Length: %lld\r\n", (int64_t) bytes ); #else /* HAVE_INT64T */ buflen = snprintf( buf, sizeof(buf), "Content-Length: %ld\r\n", (long) bytes ); #endif /* HAVE_INT64T */ add_to_response( buf, buflen ); } if ( p3p != (char*) 0 && p3p[0] != '\0' ) { buflen = snprintf( buf, sizeof(buf), "P3P: %s\r\n", p3p ); add_to_response( buf, buflen ); } if ( max_age >= 0 ) { buflen = snprintf( buf, sizeof(buf), "Cache-Control: max-age=%d\r\n", max_age); add_to_response( buf, buflen ); } buflen = snprintf( buf, sizeof(buf), "Connection: close\r\n\r\n" ); add_to_response( buf, buflen ); } static void start_request( void ) { request_size = 0; request_idx = 0; } static void add_to_request( char* str, size_t len ) { add_to_buf( &request, &request_size, &request_len, str, len ); } static char* get_request_line( void ) { int i; char c; for ( i = request_idx; request_idx < request_len; ++request_idx ) { c = request[request_idx]; if ( c == '\n' || c == '\r' ) { request[request_idx] = '\0'; ++request_idx; if ( c == '\r' && request_idx < request_len && request[request_idx] == '\n' ) { request[request_idx] = '\0'; ++request_idx; } return &(request[i]); } } return (char*) 0; } static char* response; static size_t response_size, response_len; static void start_response( void ) { response_size = 0; } static void add_to_response( char* str, size_t len ) { add_to_buf( &response, &response_size, &response_len, str, len ); } static void send_response( void ) { //printf("response:\n%s\n",response); (void) my_write( response, response_len ); } static void send_via_write( int fd, off_t size ) { if ( size <= SIZE_T_MAX ) { size_t size_size = (size_t) size; void* ptr = mmap( 0, size_size, PROT_READ, MAP_PRIVATE, fd, 0 ); if ( ptr != (void*) -1 ) { (void) my_write( ptr, size_size ); (void) munmap( ptr, size_size ); } #if 0 #ifdef MADV_SEQUENTIAL /* If we have madvise, might as well call it. Although sequential ** access is probably already the default. */ (void) madvise( ptr, size_size, MADV_SEQUENTIAL ); #endif /* MADV_SEQUENTIAL */ #endif } else { /* mmap can't deal with files larger than 2GB. */ char buf[30000]; ssize_t r, r2; for (;;) { r = read( fd, buf, sizeof(buf) ); if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) { sleep( 1 ); continue; } if ( r <= 0 ) return; for (;;) { r2 = my_write( buf, r ); if ( r2 < 0 && ( errno == EINTR || errno == EAGAIN ) ) { sleep( 1 ); continue; } if ( r2 != r ) return; break; } } } } static ssize_t my_read( char* buf, size_t size ) { size_t r, len, remain; if(is_mini_raw_sockfd) { size_t data_size = 0; int framenums = 0; uint8_t tmp[2048]; memset(tmp, 0, sizeof(tmp)); char *pdata; raw_recv_pkt_t *rawpkt; r = mini_raw_recv_pkt(&mini_http_raw_pkt_sock, HAL_MSG_PKT_FOR_WEB, (void *)tmp, &len); rawpkt = (raw_recv_pkt_t *)tmp; httpd_ifindex = rawpkt->ifindex; memcpy(minidstmac, rawpkt->srcmac, 6); mini_get_mac(minisrcmac); framenums = ntohl(rawpkt->framenums); data_size = ntohl(rawpkt->datalen); if(data_size >= size) return 0; rawpkt = (raw_recv_pkt_t *)tmp; pdata = (char *)rawpkt->data; memcpy(buf, pdata, data_size); buf[data_size] = '\0'; return data_size; } #ifdef USE_SSL if ( do_ssl ) return SSL_read( ssl, buf, size ); else return read( conn_fd, buf, size ); #else /* USE_SSL */ r = read( conn_fd, buf, size ); //printf("readbuf-size: %u, ret=%u\n", size, r); return r; #endif /* USE_SSL */ } static ssize_t my_write( char* buf, size_t size ) { ssize_t ret; if(is_mini_raw_sockfd) { ret = mini_raw_send_pkt(buf, size); return ret; } #ifdef USE_SSL if ( do_ssl ) return SSL_write( ssl, buf, size ); else return write( conn_fd, buf, size ); #else /* USE_SSL */ ret = write( conn_fd, buf, size ); //printf("send:=======================size=%u, ret:%u\n", size, ret); return ret; #endif /* USE_SSL */ } #ifdef HAVE_SENDFILE static int my_sendfile( int fd, int socket, off_t offset, size_t nbytes ) { #ifdef HAVE_LINUX_SENDFILE off_t lo = offset; //printf("-------%s %d-------\n",__FUNCTION__,__LINE__); return sendfile( socket, fd, &lo, nbytes ); #else /* HAVE_LINUX_SENDFILE */ //printf("-------%s %d-------\n",__FUNCTION__,__LINE__); return sendfile( fd, socket, offset, nbytes, (struct sf_hdtr*) 0, (off_t*) 0, 0 ); #endif /* HAVE_LINUX_SENDFILE */ } #endif /* HAVE_SENDFILE */ static void add_to_buf( char** bufP, size_t* bufsizeP, size_t* buflenP, char* str, size_t len ) { if ( *bufsizeP == 0 ) { *bufsizeP = len + 500; *buflenP = 0; *bufP = (char*) e_malloc( *bufsizeP ); } else if ( *buflenP + len >= *bufsizeP ) { *bufsizeP = *buflenP + len + 500; *bufP = (char*) e_realloc( (void*) *bufP, *bufsizeP ); } (void) memmove( &((*bufP)[*buflenP]), str, len ); *buflenP += len; (*bufP)[*buflenP] = '\0'; } static void make_log_entry( void ) { char* ru; char url[500]; char bytes_str[40]; time_t now; struct tm* t; const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S"; char date_nozone[100]; int zone; char sign; char date[100]; if ( logfp == (FILE*) 0 ) return; /* Fill in some null values. */ if ( protocol == (char*) 0 ) protocol = "UNKNOWN"; if ( path == (char*) 0 ) path = ""; if ( req_hostname == (char*) 0 ) req_hostname = hostname; /* Format the user. */ if ( remoteuser != (char*) 0 ) ru = remoteuser; else ru = "-"; now = time( (time_t*) 0 ); /* If we're vhosting, prepend the hostname to the url. This is ** a little weird, perhaps writing separate log files for ** each vhost would make more sense. */ if ( vhost ) (void) snprintf( url, sizeof(url), "/%s%s", req_hostname, path ); else (void) snprintf( url, sizeof(url), "%s", path ); /* Format the bytes. */ if ( bytes >= 0 ) #ifdef HAVE_INT64T (void) snprintf( bytes_str, sizeof(bytes_str), "%lld", (int64_t) bytes ); #else /* HAVE_INT64T */ (void) snprintf( bytes_str, sizeof(bytes_str), "%ld", (long) bytes ); #endif /* HAVE_INT64T */ else (void) strcpy( bytes_str, "-" ); /* Format the time, forcing a numeric timezone (some log analyzers ** are stoooopid about this). */ t = localtime( &now ); (void) strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t ); #ifdef HAVE_TM_GMTOFF zone = t->tm_gmtoff / 60L; #else zone = - ( timezone / 60L ); /* Probably have to add something about daylight time here. */ #endif if ( zone >= 0 ) sign = '+'; else { sign = '-'; zone = -zone; } zone = ( zone / 60 ) * 100 + zone % 60; (void) snprintf( date, sizeof(date), "%s %c%04d", date_nozone, sign, zone ); /* And write the log entry. */ (void) fprintf( logfp, "%.80s - %.80s [%s] \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.80s\"\n", ntoa( &client_addr ), ru, date, get_method_str( method ), url, protocol, status, bytes_str, referer, useragent ); (void) fflush( logfp ); } /* Returns if it's ok to serve the url, otherwise generates an error ** and exits. */ static void check_referer( void ) { char* cp; /* Are we doing referer checking at all? */ if ( url_pattern == (char*) 0 ) return; /* Is it ok? */ if ( really_check_referer() ) return; /* Lose. */ if ( vhost && req_hostname != (char*) 0 ) cp = req_hostname; else cp = hostname; if ( cp == (char*) 0 ) cp = ""; #ifdef SYSLOG syslog( LOG_INFO, "%.80s non-local referer \"%.80s%.80s\" \"%.80s\"", ntoa( &client_addr ), cp, path, referer ); #endif send_error( 403, "Forbidden", "", "You must supply a local referer." ); } /* Returns 1 if ok to serve the url, 0 if not. */ static int really_check_referer( void ) { char* cp1; char* cp2; char* cp3; char* refhost; char *lp; /* Check for an empty referer. */ if ( referer == (char*) 0 || referer[0] == '\0' || ( cp1 = strstr( referer, "//" ) ) == (char*) 0 ) { /* Disallow if we require a referer and the url matches. */ if ( no_empty_referers && match( url_pattern, path ) ) return 0; /* Otherwise ok. */ return 1; } /* Extract referer host. */ cp1 += 2; for ( cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2 ) continue; refhost = (char*) e_malloc( cp2 - cp1 + 1 ); for ( cp3 = refhost; cp1 < cp2; ++cp1, ++cp3 ) if ( isupper(*cp1) ) *cp3 = tolower(*cp1); else *cp3 = *cp1; *cp3 = '\0'; /* Local pattern? */ if ( local_pattern != (char*) 0 ) lp = local_pattern; else { /* No local pattern. What's our hostname? */ if ( ! vhost ) { /* Not vhosting, use the server name. */ lp = hostname; if ( lp == (char*) 0 ) /* Couldn't figure out local hostname - give up. */ return 1; } else { /* We are vhosting, use the hostname on this connection. */ lp = req_hostname; if ( lp == (char*) 0 ) /* Oops, no hostname. Maybe it's an old browser that ** doesn't send a Host: header. We could figure out ** the default hostname for this IP address, but it's ** not worth it for the few requests like this. */ return 1; } } /* If the referer host doesn't match the local host pattern, and ** the URL does match the url pattern, it's an illegal reference. */ if ( ! match( lp, refhost ) && match( url_pattern, path ) ) return 0; /* Otherwise ok. */ return 1; } static char* get_method_str( int m ) { switch ( m ) { case METHOD_GET: return "GET"; case METHOD_HEAD: return "HEAD"; case METHOD_POST: return "POST"; default: return "UNKNOWN"; } } struct mime_entry { char* ext; size_t ext_len; char* val; size_t val_len; }; static struct mime_entry enc_tab[] = { #include "mime_encodings.h" }; static int n_enc_tab = sizeof(enc_tab) / sizeof(*enc_tab); static struct mime_entry typ_tab[] = { #include "mime_types.h" }; static int n_typ_tab = sizeof(typ_tab) / sizeof(*typ_tab); /* qsort comparison routine - declared old-style on purpose, for portability. */ static int ext_compare( a, b ) struct mime_entry* a; struct mime_entry* b; { return strcmp( a->ext, b->ext ); } static void init_mime( void ) { int i; /* Sort the tables so we can do binary search. */ qsort( enc_tab, n_enc_tab, sizeof(*enc_tab), ext_compare ); qsort( typ_tab, n_typ_tab, sizeof(*typ_tab), ext_compare ); /* Fill in the lengths. */ for ( i = 0; i < n_enc_tab; ++i ) { enc_tab[i].ext_len = strlen( enc_tab[i].ext ); enc_tab[i].val_len = strlen( enc_tab[i].val ); } for ( i = 0; i < n_typ_tab; ++i ) { typ_tab[i].ext_len = strlen( typ_tab[i].ext ); typ_tab[i].val_len = strlen( typ_tab[i].val ); } } /* Figure out MIME encodings and type based on the filename. Multiple ** encodings are separated by semicolons. */ static char* figure_mime( char* name, char* me, size_t me_size ) { char* prev_dot; char* dot; char* ext; size_t ext_len, me_len; int i, top, bot, mid; int r; me[0] = '\0'; me_len = 0; /* Peel off encoding extensions until there aren't any more. */ for ( prev_dot = &name[strlen(name)]; ; prev_dot = dot ) { for ( dot = prev_dot - 1; dot >= name && *dot != '.'; --dot ) ; if ( dot < name ) { /* No dot found. No more encoding extensions, and no type ** extension either. */ return "text/plain; charset=%s"; } ext = dot + 1; ext_len = prev_dot - ext; /* Search the encodings table. Linear search is fine here, there ** are only a few entries. */ for ( i = 0; i < n_enc_tab; ++i ) { if ( ext_len == enc_tab[i].ext_len && strncasecmp( ext, enc_tab[i].ext, ext_len ) == 0 ) { if ( me[0] != '\0' && me_len + 1 < me_size ) { (void) strcpy( &me[me_len], ";" ); ++me_len; } if ( me_len + enc_tab[i].val_len < me_size ) { (void) strcpy( &me[me_len], enc_tab[i].val ); me_len += enc_tab[i].val_len; } goto next; } } /* No encoding extension found. Break and look for a type extension. */ break; next: ; } /* Binary search for a matching type extension. */ top = n_typ_tab - 1; bot = 0; while ( top >= bot ) { mid = ( top + bot ) / 2; r = strncasecmp( ext, typ_tab[mid].ext, ext_len ); if ( r < 0 ) top = mid - 1; else if ( r > 0 ) bot = mid + 1; else if ( ext_len < typ_tab[mid].ext_len ) top = mid - 1; else if ( ext_len > typ_tab[mid].ext_len ) bot = mid + 1; else return typ_tab[mid].val; } return "text/plain; charset=%s"; } static void handle_sigterm( int sig ) { #ifdef SYSLOG syslog( LOG_NOTICE, "exiting due to signal %d", sig ); #endif // (void) fprintf( stderr, "%s: exiting due to signal %d\n", argv0, sig ); closelog(); exit( 1 ); } static void handle_sigchld( int sig ) { pid_t pid; int status; #ifndef HAVE_SIGSET (void) signal( SIGCHLD, handle_sigchld ); /* setup handler again */ #endif /* ! HAVE_SIGSET */ /* Reap defunct children until there aren't any more. */ for (;;) { #ifdef HAVE_WAITPID pid = waitpid( (pid_t) -1, &status, WNOHANG ); #else /* HAVE_WAITPID */ pid = wait3( &status, WNOHANG, (struct rusage*) 0 ); #endif /* HAVE_WAITPID */ if ( (int) pid == 0 ) /* none left */ break; if ( (int) pid < 0 ) { if ( errno == EINTR ) /* because of ptrace */ continue; /* ECHILD shouldn't happen with the WNOHANG option, ** but with some kernels it does anyway. Ignore it. */ if ( errno != ECHILD ) { #ifdef SYSLOG syslog( LOG_ERR, "child wait - %m" ); perror( "child wait" ); #endif } break; } } } static void handle_sigalrm( int sig ) { #ifdef SYSLOG syslog( LOG_INFO, "%.80s connection timed out", ntoa( &client_addr ) ); #endif send_error( 408, "Request Timeout", "", "No request appeared within a reasonable time period." ); } static void lookup_hostname( usockaddr* usa4P, size_t sa4_len, int* gotv4P, usockaddr* usa6P, size_t sa6_len, int* gotv6P ) { #ifdef USE_IPV6 struct addrinfo hints; char portstr[10]; int gaierr; struct addrinfo* ai; struct addrinfo* ai2; struct addrinfo* aiv6; struct addrinfo* aiv4; (void) memset( &hints, 0, sizeof(hints) ); hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; (void) snprintf( portstr, sizeof(portstr), "%d", port ); if ( (gaierr = getaddrinfo( hostname, portstr, &hints, &ai )) != 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "getaddrinfo %.80s - %s", hostname, gai_strerror( gaierr ) ); #endif // (void) fprintf( // stderr, "%s: getaddrinfo %.80s - %s\n", argv0, hostname, // gai_strerror( gaierr ) ); exit( 1 ); } /* Find the first IPv6 and IPv4 entries. */ aiv6 = (struct addrinfo*) 0; aiv4 = (struct addrinfo*) 0; for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next ) { switch ( ai2->ai_family ) { case AF_INET6: if ( aiv6 == (struct addrinfo*) 0 ) aiv6 = ai2; break; case AF_INET: if ( aiv4 == (struct addrinfo*) 0 ) aiv4 = ai2; break; } } if ( aiv6 == (struct addrinfo*) 0 ) *gotv6P = 0; else { if ( sa6_len < aiv6->ai_addrlen ) { #ifdef SYSLOG syslog( LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)", hostname, (unsigned long) sa6_len, (unsigned long) aiv6->ai_addrlen ); #endif // (void) fprintf( // stderr, "%s: %.80s - sockaddr too small (%lu < %lu)\n", // argv0, hostname, (unsigned long) sa6_len, // (unsigned long) aiv6->ai_addrlen ); exit( 1 ); } (void) memset( usa6P, 0, sa6_len ); (void) memmove( usa6P, aiv6->ai_addr, aiv6->ai_addrlen ); *gotv6P = 1; } if ( aiv4 == (struct addrinfo*) 0 ) *gotv4P = 0; else { if ( sa4_len < aiv4->ai_addrlen ) { #ifdef SYSLOG syslog( LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)", hostname, (unsigned long) sa4_len, (unsigned long) aiv4->ai_addrlen ); #endif // (void) fprintf( // stderr, "%s: %.80s - sockaddr too small (%lu < %lu)\n", // argv0, hostname, (unsigned long) sa4_len, // (unsigned long) aiv4->ai_addrlen ); exit( 1 ); } (void) memset( usa4P, 0, sa4_len ); (void) memmove( usa4P, aiv4->ai_addr, aiv4->ai_addrlen ); *gotv4P = 1; } freeaddrinfo( ai ); #else /* USE_IPV6 */ struct hostent* he; *gotv6P = 0; (void) memset( usa4P, 0, sa4_len ); usa4P->sa.sa_family = AF_INET; if ( hostname == (char*) 0 ) usa4P->sa_in.sin_addr.s_addr = htonl( INADDR_ANY ); else { usa4P->sa_in.sin_addr.s_addr = inet_addr( hostname ); if ( (int) usa4P->sa_in.sin_addr.s_addr == -1 ) { he = gethostbyname( hostname ); if ( he == (struct hostent*) 0 ) { #ifdef HAVE_HSTRERROR #ifdef SYSLOG syslog( LOG_CRIT, "gethostbyname %.80s - %s", hostname, hstrerror( h_errno ) ); #endif // (void) fprintf( // stderr, "%s: gethostbyname %.80s - %s\n", argv0, hostname, // hstrerror( h_errno ) ); #else /* HAVE_HSTRERROR */ #ifdef SYSLOG syslog( LOG_CRIT, "gethostbyname %.80s failed", hostname ); #endif // (void) fprintf( // stderr, "%s: gethostbyname %.80s failed\n", argv0, // hostname ); #endif /* HAVE_HSTRERROR */ exit( 1 ); } if ( he->h_addrtype != AF_INET ) { #ifdef SYSLOG syslog( LOG_CRIT, "%.80s - non-IP network address", hostname ); #endif // (void) fprintf( // stderr, "%s: %.80s - non-IP network address\n", argv0, // hostname ); exit( 1 ); } (void) memmove( &usa4P->sa_in.sin_addr.s_addr, he->h_addr, he->h_length ); } } usa4P->sa_in.sin_port = htons( port ); *gotv4P = 1; #endif /* USE_IPV6 */ } static char* ntoa( usockaddr* usaP ) { #ifdef USE_IPV6 static char str[200]; if ( getnameinfo( &usaP->sa, sockaddr_len( usaP ), str, sizeof(str), 0, 0, NI_NUMERICHOST ) != 0 ) { str[0] = '?'; str[1] = '\0'; } else if ( IN6_IS_ADDR_V4MAPPED( &usaP->sa_in6.sin6_addr ) && strncmp( str, "::ffff:", 7 ) == 0 ) /* Elide IPv6ish prefix for IPv4 addresses. */ (void) strcpy( str, &str[7] ); return str; #else /* USE_IPV6 */ return inet_ntoa( usaP->sa_in.sin_addr ); #endif /* USE_IPV6 */ } static int sockaddr_check( usockaddr* usaP ) { switch ( usaP->sa.sa_family ) { case AF_INET: return 1; #ifdef USE_IPV6 case AF_INET6: return 1; #endif /* USE_IPV6 */ default: return 0; } } static size_t sockaddr_len( usockaddr* usaP ) { switch ( usaP->sa.sa_family ) { case AF_INET: return sizeof(struct sockaddr_in); #ifdef USE_IPV6 case AF_INET6: return sizeof(struct sockaddr_in6); #endif /* USE_IPV6 */ default: return 0; /* shouldn't happen */ } } /* Copies and decodes a string. It's ok for from and to to be the ** same string. */ static void strdecode( char* to, char* from ) { for ( ; *from != '\0'; ++to, ++from ) { if ( from[0] == '%' && isxdigit( from[1] ) && isxdigit( from[2] ) ) { *to = hexit( from[1] ) * 16 + hexit( from[2] ); from += 2; } else *to = *from; } *to = '\0'; } static int hexit( char c ) { if ( c >= '0' && c <= '9' ) return c - '0'; if ( c >= 'a' && c <= 'f' ) return c - 'a' + 10; if ( c >= 'A' && c <= 'F' ) return c - 'A' + 10; return 0; /* shouldn't happen, we're guarded by isxdigit() */ } /* Base-64 decoding. This represents binary data as printable ASCII ** characters. Three 8-bit binary bytes are turned into four 6-bit ** values, like so: ** ** [11111111] [22222222] [33333333] ** ** [111111] [112222] [222233] [333333] ** ** Then the 6-bit values are represented using the characters "A-Za-z0-9+/". */ static int b64_decode_table[256] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ }; /* Do base-64 decoding on a string. Ignore any non-base64 bytes. ** Return the actual number of bytes generated. The decoded size will ** be at most 3/4 the size of the encoded, and may be smaller if there ** are padding characters (blanks, newlines). */ static int b64_decode( const char* str, unsigned char* space, int size ) { const char* cp; int space_idx, phase; int d, prev_d = 0; unsigned char c; space_idx = 0; phase = 0; for ( cp = str; *cp != '\0'; ++cp ) { d = b64_decode_table[(int) *cp]; if ( d != -1 ) { switch ( phase ) { case 0: ++phase; break; case 1: c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) ); if ( space_idx < size ) space[space_idx++] = c; ++phase; break; case 2: c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) ); if ( space_idx < size ) space[space_idx++] = c; ++phase; break; case 3: c = ( ( ( prev_d & 0x03 ) << 6 ) | d ); if ( space_idx < size ) space[space_idx++] = c; phase = 0; break; } prev_d = d; } } return space_idx; } /* Set NDELAY mode on a socket. */ static void set_ndelay( int fd ) { int flags, newflags; flags = fcntl( fd, F_GETFL, 0 ); if ( flags != -1 ) { newflags = flags | (int) O_NDELAY; if ( newflags != flags ) (void) fcntl( fd, F_SETFL, newflags ); } } /* Clear NDELAY mode on a socket. */ static void clear_ndelay( int fd ) { int flags, newflags; flags = fcntl( fd, F_GETFL, 0 ); if ( flags != -1 ) { newflags = flags & ~ (int) O_NDELAY; if ( newflags != flags ) (void) fcntl( fd, F_SETFL, newflags ); } } static void* e_malloc( size_t size ) { void* ptr; ptr = malloc( size ); if ( ptr == (void*) 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "out of memory" ); #endif // (void) fprintf( stderr, "%s: out of memory\n", argv0 ); exit( 1 ); } return ptr; } static void* e_realloc( void* optr, size_t size ) { void* ptr; ptr = realloc( optr, size ); if ( ptr == (void*) 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "out of memory" ); #endif // (void) fprintf( stderr, "%s: out of memory\n", argv0 ); exit( 1 ); } return ptr; } static char* e_strdup( char* ostr ) { char* str; str = strdup( ostr ); if ( str == (char*) 0 ) { #ifdef SYSLOG syslog( LOG_CRIT, "out of memory copying a string" ); #endif // (void) fprintf( stderr, "%s: out of memory copying a string\n", argv0 ); exit( 1 ); } return str; } #ifdef NO_SNPRINTF /* Some systems don't have snprintf(), so we make our own that uses ** vsprintf(). This workaround is probably vulnerable to buffer overruns, ** so upgrade your OS! */ static int snprintf( char* str, size_t size, const char* format, ... ) { va_list ap; int r; va_start( ap, format ); r = vsprintf( str, format, ap ); va_end( ap ); return r; } #endif /* NO_SNPRINTF */