Files
repo-smod-atsc/src/mini_httpd/mini_httpd.c
T

4313 lines
114 KiB
C

/* mini_httpd - small HTTP server
**
** Copyright ?1999,2000 by Jef Poskanzer <jef@acme.com>.
** 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 <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <limits.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <time.h>
#include <pwd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <ctype.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#include <sys/sysinfo.h>
#include <linux/rtnetlink.h>
#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 <linux/rtnetlink.h>
#include "mini_httpd_raw.h"
#include "mini_httpd_app.h"
#endif
#define WEBVERSTION "Switch Web Server"
#ifdef HAVE_SENDFILE
# ifdef HAVE_LINUX_SENDFILE
# include <sys/sendfile.h>
# else /* HAVE_LINUX_SENDFILE */
# include <sys/uio.h>
# endif /* HAVE_LINUX_SENDFILE */
#endif /* HAVE_SENDFILE */
#ifdef USE_SSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#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), "\
<HTML>\n\
<HEAD><TITLE>Index of %s</TITLE></HEAD>\n\
<BODY BGCOLOR=\"#99cc99\" TEXT=\"#000000\" LINK=\"#2020ff\" VLINK=\"#4040cc\">\n\
<H4>Index of %s</H4>\n\
<PRE>\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,|\\([^*]*\\)$,|<A HREF=\"\\1\">\\1</A>,' -e '/ -> /!s,|\\(.*\\)\\([*]\\)$,|<A HREF=\"\\1\">\\1</A>\\2,' -e '/ -> /s,|\\([^@]*\\)\\(@* -> \\),|<A HREF=\"\\1\">\\1</A>\\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), "\
</PRE>\n\
<HR>\n\
<ADDRESS><A HREF=\"%s\">%s</A></ADDRESS>\n\
</BODY>\n\
</HTML>\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 ), "<A HREF=\"%s\">%-32.32s</A> %15s %14lld\n",
encname, name, f_time, (int64_t) sb.st_size );
#else /* HAVE_INT64T */
(void) snprintf(
buf, sizeof( buf ), "<A HREF=\"%s\">%-32.32s</A> %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), "\
<HTML>\n\
<HEAD><TITLE>%d %s</TITLE></HEAD>\n\
<CENTER><H1>%d %s</H1></CENTER>\n\
<BODY BGCOLOR=\"#EEEEEE\" TEXT=\"#000000\" LINK=\"#2020ff\" VLINK=\"#4040cc\">\n\
<hr><center><address>%s at %s port %d</address></center>\n",
s, title, s, title ,WEBVERSTION,remote_ip,port);
add_to_response( buf, buflen );
buflen = snprintf( buf, sizeof(buf), "<center><address>%s</address></center>\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 );
for ( n = 0; n < 6; ++n )
{
buflen = snprintf( buf, sizeof(buf), "Padding so that MSIE deigns to show this error instead of its own canned one.\n" );
add_to_response( buf, buflen );
}
buflen = snprintf( buf, sizeof(buf), "-->\n" );
add_to_response( buf, buflen );
}
buflen = snprintf( buf, sizeof(buf), "\
<HR>\n\
<ADDRESS><A HREF=\"%s\">%s</A></ADDRESS>\n\
</BODY>\n\
</HTML>\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 */