Newer
Older
/* Eat leaing blank lines... as apache does...
* "This is a legacy issue. The CERN webserver required POST data to have an extra
* CRLF following it. Thus many clients send an extra CRLF that is not included in the
* Content-Length of the request. Apache works around this problem by eating any empty
* lines which appear before a request."
* http://httpd.apache.org/docs/misc/known_client_problems.html
*/
while((len=sockreadline(session,req_line,sizeof(req_line)-1))==0);
if(len<0)
return(FALSE);
if(req_line[0])
lprintf(LOG_DEBUG,"%04d Request: %s",session->socket,req_line);
if(session->req.ld!=NULL && session->req.ld->request==NULL)
session->req.ld->request=strdup(req_line);
}
else {
lprintf(LOG_DEBUG,"%04d Handling Internal Redirect to: %s",session->socket,request_line);
SAFECOPY(req_line,request_line);
}
if(req_line[0]) {
p=NULL;
p=get_method(session,req_line);
if(p!=NULL) {
p=get_request(session,p);
session->http_ver=get_version(p);
if(session->http_ver>=HTTP_1_1)
session->req.keep_alive=TRUE;
if(!is_redir)
get_request_headers(session);
if(!get_fullpath(session)) {
send_error(session,error_500);
if(session->req.ld!=NULL && session->req.ld->vhost==NULL)
session->req.ld->vhost=strdup(session->req.vhost);
session->req.dynamic=is_dynamic_req(session);
if(session->req.query_str[0]) {
add_env(session,"QUERY_STRING",session->req.query_str);
switch(session->req.dynamic) {
case IS_JS:
case IS_SSJS:
js_add_request_prop(session,"query_string",session->req.query_str);
js_parse_query(session,session->req.query_str);
break;
}
}
add_env(session,"REQUEST_METHOD",methods[session->req.method]);
add_env(session,"SERVER_PROTOCOL",session->http_ver ?
http_vers[session->http_ver] : "HTTP/0.9");
send_error(session,"400 Bad Request");
return FALSE;
}
/* This may exist somewhere else - ToDo */
static char *find_last_slash(char *str)
{
#ifdef _WIN32
char * LastFSlash;
char * LastBSlash;
LastFSlash=strrchr(str,'/');
LastBSlash=strrchr(str,'\\');
if(LastFSlash==NULL)
return(LastBSlash);
if(LastBSlash==NULL)
return(LastFSlash);
if(LastBSlash < LastFSlash)
return(LastFSlash);
return(LastBSlash);
#else
return(strrchr(str,'/'));
#endif
}
/* This may exist somewhere else - ToDo */
static char *find_first_slash(char *str)
{
#ifdef _WIN32
char * FirstFSlash;
char * FirstBSlash;
FirstFSlash=strchr(str,'/');
FirstBSlash=strchr(str,'\\');
if(FirstFSlash==NULL)
return(FirstBSlash);
if(FirstBSlash==NULL)
return(FirstFSlash);
if(FirstBSlash > FirstFSlash)
return(FirstFSlash);
return(FirstBSlash);
#else
return(strchr(str,'/'));
#endif
}
static BOOL check_extra_path(http_session_t * session)
char *rp_slash;
char *vp_slash;
char rpath[MAX_PATH+1];
char vpath[MAX_PATH+1];
char epath[MAX_PATH+1];
char str[MAX_PATH+1];
struct stat sb;
int i;
char *end;
if(IS_PATH_DELIM(*lastchar(session->req.physical_path)) || stat(session->req.physical_path,&sb)==-1 /* && errno==ENOTDIR */)
{
SAFECOPY(vpath,session->req.virtual_path);
SAFECOPY(rpath,session->req.physical_path);
while((vp_slash=find_last_slash(vpath))!=NULL)
*vp_slash=0;
if((rp_slash=find_last_slash(rpath))==NULL)
return(FALSE);
SAFECOPY(str,epath);
if(*rp_slash)
sprintf(epath,"%s%s",rp_slash,str);
*(rp_slash+1)=0;
/* Check if this contains an index */
end=strchr(rpath,0);
if(isdir(rpath) && !isdir(session->req.physical_path)) {
for(i=0; startup->index_file_name!=NULL && startup->index_file_name[i]!=NULL ;i++) {
*end=0;
strcat(rpath,startup->index_file_name[i]);
if(!stat(rpath,&sb)) {
*end=0;
SAFECOPY(session->req.extra_path_info,epath);
SAFECOPY(session->req.virtual_path,vpath);
strcat(session->req.virtual_path,"/");
SAFECOPY(session->req.physical_path,rpath);
return(TRUE);
}
}
}
if(vp_slash==vpath)
return(FALSE);
/* Check if this is a script */
*rp_slash=0;
if(vp_slash!=vpath) {
if(stat(rpath,&sb)!=-1 && (!(sb.st_mode&S_IFDIR)))
{
SAFECOPY(session->req.extra_path_info,epath);
SAFECOPY(session->req.virtual_path,vpath);
SAFECOPY(session->req.physical_path,rpath);
return(TRUE);
}
}
}
}
return(FALSE);
}
static BOOL check_request(http_session_t * session)
{
char path[MAX_PATH+1];
char curdir[MAX_PATH+1];
char str[MAX_PATH+1];
char last_ch;
char* last_slash;
char filename[MAX_PATH+1];
char *spec;
str_list_t specs;
BOOL recheck_dynamic=FALSE;
if(session->req.finished)
return(FALSE);
SAFECOPY(path,session->req.physical_path);
lprintf(LOG_DEBUG,"%04d Path is: %s",session->socket,path);
if(isdir(path)) {
last_ch=*lastchar(path);
if(!IS_PATH_DELIM(last_ch)) {
last_ch=*lastchar(session->req.virtual_path);
if(!IS_PATH_DELIM(last_ch)) {
strcat(session->req.virtual_path,"/");
}
last_slash=find_last_slash(path);
send_error(session,error_500);
last_slash++;
for(i=0; startup->index_file_name!=NULL && startup->index_file_name[i]!=NULL ;i++) {
*last_slash=0;
strcat(path,startup->index_file_name[i]);
lprintf(LOG_DEBUG,"%04d Checking for %s",session->socket,path);
/* Don't send 404 unless authourized... prevent info leak */
if(startup->index_file_name==NULL || startup->index_file_name[i] == NULL)
send404=1;
else {
strcat(session->req.virtual_path,startup->index_file_name[i]);
if(session->req.send_location != MOVED_PERM)
session->req.send_location=MOVED_STAT;
filename[0]=0;
}
else {
last_slash=find_last_slash(path);
if(last_slash==NULL)
last_slash=path;
else
last_slash++;
strcpy(filename,last_slash);
if(strnicmp(path,root_dir,strlen(root_dir))) {
session->req.keep_alive=FALSE;
send_error(session,"400 Bad Request");
lprintf(LOG_NOTICE,"%04d !ERROR Request for %s is outside of web root %s"
,session->socket,path,root_dir);
return(FALSE);
}
/* Set default ARS to a 0-length string */
session->req.ars[0]=0;
/* Walk up from root_dir checking for access.ars and webconfig.ini */
SAFECOPY(curdir,path);
last_slash=curdir+strlen(root_dir)-1;
/* Loop while there's more /s in path*/
while((last_slash=find_first_slash(p+1))!=NULL) {
/* Terminate the path after the slash */
*(last_slash+1)=0;
sprintf(str,"%saccess.ars",curdir);
/* NEVER serve up an access.ars file */
if(!strcmp(path,str)) {
send_error(session,"403 Forbidden");
return(FALSE);
}
/* Read access.ars file */
if((file=fopen(str,"r"))!=NULL) {
fgets(session->req.ars,sizeof(session->req.ars),file);
fclose(file);
}
else {
/* If cannot open access.ars, only allow sysop access */
SAFECOPY(session->req.ars,"LEVEL 90");
break;
}
/* Truncate at \r or \n - can use last_slash since I'm done with it.*/
truncsp(session->req.ars);
}
sprintf(str,"%swebctrl.ini",curdir);
if(!stat(str,&sb)) {
/* NEVER serve up a webctrl.ini file */
if(!strcmp(path,str)) {
send_error(session,"403 Forbidden");
return(FALSE);
}
/* Read webctrl.ars file */
if((file=fopen(str,"r"))!=NULL) {
specs=iniReadSectionList(file,NULL);
/* Read in globals */
if(iniReadString(file, NULL, "AccessRequirements", session->req.ars,str)==str)
SAFECOPY(session->req.ars,str);
if(iniReadString(file, NULL, "Realm", scfg.sys_name,str)==str)
session->req.realm=strdup(str);
if(iniReadString(file, NULL, "ErrorDirectory", error_dir,str)==str) {
prep_dir(root_dir, str, sizeof(str));
session->req.error_dir=strdup(str);
}
if(iniReadString(file, NULL, "CGIDirectory", cgi_dir,str)==str) {
prep_dir(root_dir, str, sizeof(str));
session->req.cgi_dir=strdup(str);
recheck_dynamic=TRUE;
}
/* Read in per-filespec */
while((spec=strListPop(&specs))!=NULL) {
if(wildmatch(filename,spec,TRUE)) {
if(iniReadString(file, spec, "AccessRequirements", session->req.ars,str)==str)
SAFECOPY(session->req.ars,str);
if(iniReadString(file, spec, "Realm", scfg.sys_name,str)==str)
session->req.realm=strdup(str);
if(iniReadString(file, spec, "ErrorDirectory", error_dir,str)==str) {
prep_dir(root_dir, str, sizeof(str));
session->req.error_dir=strdup(str);
}
if(iniReadString(file, spec, "CGIDirectory", cgi_dir,str)==str) {
prep_dir(root_dir, str, sizeof(str));
session->req.cgi_dir=strdup(str);
recheck_dynamic=TRUE;
}
}
free(spec);
}
strListFreeStrings(specs);
fclose(file);
}
else {
/* If cannot open webctrl.ars, only allow sysop access */
SAFECOPY(session->req.ars,"LEVEL 90");
break;
}
/* Truncate at \r or \n - can use last_slash since I'm done with it.*/
truncsp(session->req.ars);
}
SAFECOPY(curdir,path);
if(recheck_dynamic)
session->req.dynamic=is_dynamic_req(session);
if(!session->req.dynamic && session->req.extra_path_info[0])
send404=TRUE;
if(!check_ars(session)) {
/* No authentication provided */
sprintf(str,"401 Unauthorized%s%s: Basic realm=\"%s\""
,newline,get_header(HEAD_WWWAUTH),session->req.realm?session->req.realm:scfg.sys_name);
send_error(session,str);
return(FALSE);
if(stat(path,&sb) || IS_PATH_DELIM(*(lastchar(path))) || send404) {
/* OPTIONS requests never return 404 errors (ala Apache) */
if(session->req.method!=HTTP_OPTIONS) {
if(startup->options&WEB_OPT_DEBUG_TX)
lprintf(LOG_DEBUG,"%04d 404 - %s does not exist",session->socket,path);
strcat(session->req.physical_path,session->req.extra_path_info);
strcat(session->req.virtual_path,session->req.extra_path_info);
send_error(session,error_404);
return(FALSE);
}
}
SAFECOPY(session->req.physical_path,path);
add_env(session,"SCRIPT_NAME",session->req.virtual_path);
add_env(session,"SCRIPT_FILENAME",session->req.physical_path);
SAFECOPY(str,session->req.virtual_path);
last_slash=find_last_slash(str);
if(last_slash!=NULL)
*(last_slash+1)=0;
if(*(session->req.extra_path_info))
{
sprintf(str,"%s%s",startup->root_dir,session->req.extra_path_info);
add_env(session,"PATH_TRANSLATED",str);
add_env(session,"PATH_INFO",session->req.extra_path_info);
}
return(TRUE);
}
static str_list_t get_cgi_env(http_session_t *session)
{
char value[INI_MAX_VALUE_LEN+1];
char* deflt;
char defltbuf[INI_MAX_VALUE_LEN+1];
char append[INI_MAX_VALUE_LEN+1];
char prepend[INI_MAX_VALUE_LEN+1];
char env_str[(INI_MAX_VALUE_LEN*4)+2];
FILE* fp;
size_t i;
str_list_t env_list;
str_list_t add_list;
if((env_list=strListInit())==NULL)
return(NULL);
strListAppendList(&env_list, session->req.cgi_env);
strListPush(&env_list,"REDIRECT_STATUS=200"); /* Kludge for php-cgi */
if((fp=iniOpenFile(cgi_env_ini,/* create? */FALSE))==NULL)
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
return(env_list);
if((add_list=iniReadSectionList(fp,NULL))!=NULL) {
for(i=0; add_list[i]!=NULL; i++) {
if((deflt=getenv(add_list[i]))==NULL)
deflt=iniReadString(fp,add_list[i],"default",NULL,defltbuf);
if(iniReadString(fp,add_list[i],"value",deflt,value)==NULL)
continue;
iniReadString(fp,add_list[i],"append","",append);
iniReadString(fp,add_list[i],"prepend","",prepend);
safe_snprintf(env_str,sizeof(env_str),"%s=%s%s%s"
,add_list[i], prepend, value, append);
strListPush(&env_list,env_str);
}
strListFree(&add_list);
}
fclose(fp);
return(env_list);
}
static BOOL exec_cgi(http_session_t *session)
char cmdline[MAX_PATH+256];
/* ToDo: Damn, that's WAY too many variables */
int i=0;
int out_pipe[2];
int err_pipe[2];
fd_set read_set;
fd_set write_set;
BOOL done_parsing_headers=FALSE;
BOOL done_reading=FALSE;
char cgi_status[MAX_REQUEST_LINE+1];
char header[MAX_REQUEST_LINE+1];
char *directive=NULL;
char *value=NULL;
BOOL done_wait=FALSE;
BOOL got_valid_headers=FALSE;
time_t start;
char cgipath[MAX_PATH+1];
char *p;
size_t idx;
BOOL set_chunked=FALSE;
SAFECOPY(cmdline,session->req.physical_path);
lprintf(LOG_INFO,"%04d Executing CGI: %s",session->socket,cmdline);
orig_keep=session->req.keep_alive;
/* Set up I/O pipes */
if(pipe(out_pipe)!=0) {
lprintf(LOG_ERR,"%04d Can't create out_pipe",session->socket);
return(FALSE);
}
if(pipe(err_pipe)!=0) {
lprintf(LOG_ERR,"%04d Can't create err_pipe",session->socket);
return(FALSE);
}
if((child=fork())==0) {
/* Do a full suid thing. */
if(startup->setuid!=NULL)
startup->setuid(TRUE);
/* Set up STDIO */
dup2(session->socket,0); /* redirect stdin */
close(out_pipe[0]); /* close read-end of pipe */
dup2(out_pipe[1],1); /* stdout */
close(out_pipe[1]); /* close excess file descriptor */
close(err_pipe[0]); /* close read-end of pipe */
dup2(err_pipe[1],2); /* stderr */
close(err_pipe[1]); /* close excess file descriptor */
SAFECOPY(cgipath,cmdline);
if((p=strrchr(cgipath,'/'))!=NULL)
{
*p=0;
chdir(cgipath);
}
/* Execute command */
if(get_cgi_handler(cgipath, sizeof(cgipath))) {
char* shell=os_cmdshell();
lprintf(LOG_INFO,"%04d Using handler %s to execute %s",session->socket,cgipath,cmdline);
execle(shell,shell,"-c",cgipath,NULL,env_list);
}
else {
execle(cmdline,cmdline,NULL,env_list);
}
lprintf(LOG_ERR,"%04d !FAILED! execle() (%d)",session->socket,errno);
exit(EXIT_FAILURE); /* Should never happen */
}
if(child==-1) {
lprintf(LOG_ERR,"%04d !FAILED! fork() errno=%d",session->socket,errno);
close(out_pipe[0]); /* close read-end of pipe */
close(err_pipe[0]); /* close read-end of pipe */
}
close(out_pipe[1]); /* close excess file descriptor */
close(err_pipe[1]); /* close excess file descriptor */
high_fd=out_pipe[0];
if(err_pipe[0]>high_fd)
high_fd=err_pipe[0];
/* ToDo: Magically set done_parsing_headers for nph-* scripts */
cgi_status[0]=0;
while(!done_reading) {
tv.tv_sec=startup->max_cgi_inactivity;
tv.tv_usec=0;
FD_ZERO(&read_set);
FD_SET(out_pipe[0],&read_set);
FD_SET(err_pipe[0],&read_set);
FD_ZERO(&write_set);
if(select(high_fd+1,&read_set,&write_set,NULL,&tv)>0) {
if(FD_ISSET(out_pipe[0],&read_set)) {
if(done_parsing_headers && got_valid_headers) {
int snt=0;
start=time(NULL);
if(session->req.method!=HTTP_HEAD) {
snt=writebuf(session,buf,i);
session->req.ld->size+=snt;
done_reading=TRUE;
}
else {
/* This is the tricky part */
i=pipereadline(out_pipe[0],buf,sizeof(buf), fbuf, sizeof(fbuf));
done_reading=TRUE;
got_valid_headers=FALSE;
}
start=time(NULL);
if(!done_parsing_headers && *buf) {
SAFECOPY(header,buf);
directive=strtok(header,":");
if(directive != NULL) {
value=strtok(NULL,"");
i=get_header_type(directive);
switch (i) {
case HEAD_LOCATION:
got_valid_headers=TRUE;
if(*value=='/') {
unescape(value);
SAFECOPY(session->req.virtual_path,value);
session->req.send_location=MOVED_STAT;
if(cgi_status[0]==0)
SAFECOPY(cgi_status,error_302);
} else {
SAFECOPY(session->req.virtual_path,value);
session->req.send_location=MOVED_TEMP;
if(cgi_status[0]==0)
SAFECOPY(cgi_status,error_302);
}
break;
case HEAD_STATUS:
SAFECOPY(cgi_status,value);
break;
case HEAD_LENGTH:
session->req.keep_alive=orig_keep;
strListPush(&session->req.dynamic_heads,buf);
break;
case HEAD_TYPE:
got_valid_headers=TRUE;
strListPush(&session->req.dynamic_heads,buf);
break;
case HEAD_TRANSFER_ENCODING:
no_chunked=TRUE;
break;
default:
strListPush(&session->req.dynamic_heads,buf);
}
}
/* Invalid header line */
done_parsing_headers=TRUE;
}
}
else {
if(!no_chunked && session->http_ver>=HTTP_1_1) {
session->req.keep_alive=orig_keep;
set_chunked=TRUE;
if(got_valid_headers) {
session->req.dynamic=IS_CGI;
if(cgi_status[0]==0)
SAFECOPY(cgi_status,session->req.status);
send_headers(session,cgi_status,set_chunked);
}
else {
/* Invalid headers... send 'er all as plain-text */
lprintf(LOG_DEBUG,"%04d Recieved invalid CGI headers, sending result as plain-text",session->socket);
/* free() the non-headers so they don't get sent, then recreate the list */
strListFreeStrings(session->req.dynamic_heads);
/* Copy current status */
SAFECOPY(cgi_status,session->req.status);
/* Add the content-type header (REQUIRED) */
SAFEPRINTF2(content_type,"%s: %s",get_header(HEAD_TYPE),startup->default_cgi_content);
strListPush(&session->req.dynamic_heads,content_type);
send_headers(session,cgi_status,FALSE);
/* Now send the tmpbuf */
for(i=0; tmpbuf != NULL && tmpbuf[i] != NULL; i++) {
snt=writebuf(session,tmpbuf[i],strlen(tmpbuf[i]));
session->req.ld->size+=snt;
}
}
}
if(strlen(fbuf)>0) {
snt=writebuf(session,fbuf,strlen(fbuf));
if(session->req.ld!=NULL && snt>0) {
session->req.ld->size+=snt;
}
done_parsing_headers=TRUE;
}
}
}
if(FD_ISSET(err_pipe[0],&read_set)) {
i=read(err_pipe[0],buf,sizeof(buf)-1);
if(i>0) {
buf[i]=0;
lprintf(LOG_ERR,"%04d CGI Error: %s",session->socket,buf);
start=time(NULL);
if(!done_wait)
done_wait = (waitpid(child,&status,WNOHANG)==child);
if(!FD_ISSET(err_pipe[0],&read_set) && !FD_ISSET(out_pipe[0],&read_set) && done_wait)
done_reading=TRUE;
}
else {
if((time(NULL)-start) >= startup->max_cgi_inactivity) {
lprintf(LOG_ERR,"%04d CGI Process %s Timed out",session->socket,getfname(cmdline));
done_reading=TRUE;
start=0;
if(!done_wait)
done_wait = (waitpid(child,&status,WNOHANG)==child);
if(!done_wait) {
if(start)
lprintf(LOG_NOTICE,"%04d CGI Process %s still alive on client exit"
,session->socket,getfname(cmdline));
kill(child,SIGTERM);
mswait(1000);
done_wait = (waitpid(child,&status,WNOHANG)==child);
if(!done_wait) {
kill(child,SIGKILL);
done_wait = (waitpid(child,&status,0)==child);
}
}
/* Drain STDERR & STDOUT */
tv.tv_sec=1;
tv.tv_usec=0;
FD_ZERO(&read_set);
FD_SET(err_pipe[0],&read_set);
FD_SET(out_pipe[0],&read_set);
while(select(high_fd+1,&read_set,NULL,NULL,&tv)>0) {
if(FD_ISSET(err_pipe[0],&read_set)) {
i=read(err_pipe[0],buf,sizeof(buf)-1);
buf[i]=0;
lprintf(LOG_ERR,"%04d CGI Error: %s",session->socket,buf);
start=time(NULL);
}
}
if(FD_ISSET(out_pipe[0],&read_set)) {
i=read(out_pipe[0],buf,sizeof(buf));
if(i!=-1 && i!=0) {
int snt=0;
start=time(NULL);
if(session->req.method!=HTTP_HEAD) {
snt=writebuf(session,buf,i);
if(session->req.ld!=NULL) {
session->req.ld->size+=snt;
}
}
}
}
if(i==0 || i==-1)
break;
tv.tv_sec=1;
tv.tv_usec=0;
FD_ZERO(&read_set);
FD_SET(err_pipe[0],&read_set);
FD_SET(out_pipe[0],&read_set);
close(out_pipe[0]); /* close read-end of pipe */
close(err_pipe[0]); /* close read-end of pipe */
if(!got_valid_headers) {
lprintf(LOG_ERR,"%04d CGI Process %s did not generate valid headers"
,session->socket,getfname(cmdline));
return(FALSE);
}
if(!done_parsing_headers) {
lprintf(LOG_ERR,"%04d CGI Process %s did not send data header termination"
,session->socket,getfname(cmdline));
return(FALSE);
return(TRUE);
/* Win32 exec_cgi() */
/* These are (more or less) copied from the Unix version */
char* p;
char cmdline[MAX_PATH+256];
char buf[4096];
int i;
BOOL orig_keep;
BOOL done_parsing_headers=FALSE;
BOOL got_valid_headers=FALSE;
char cgi_status[MAX_REQUEST_LINE+1];
char content_type[MAX_REQUEST_LINE+1];
char header[MAX_REQUEST_LINE+1];
char *directive=NULL;
char *value=NULL;
time_t start;
int set_chunked=FALSE;
/* Win32-specific */
char startup_dir[MAX_PATH+1];
int wr;
HANDLE rdpipe=INVALID_HANDLE_VALUE;
HANDLE wrpipe=INVALID_HANDLE_VALUE;
HANDLE rdoutpipe;
HANDLE wrinpipe;
DWORD waiting;
DWORD msglen;
DWORD retval;
BOOL process_terminated=FALSE;
PROCESS_INFORMATION process_info;
SECURITY_ATTRIBUTES sa;
STARTUPINFO startup_info={0};
str_list_t env_list;
startup_info.cb=sizeof(startup_info);
startup_info.dwFlags|=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
startup_info.wShowWindow=SW_HIDE;
SAFECOPY(cmdline,session->req.physical_path);
SAFECOPY(startup_dir,cmdline);
if((p=strrchr(startup_dir,'/'))!=NULL || (p=strrchr(startup_dir,'\\'))!=NULL)
*p=0;
else
SAFECOPY(startup_dir,session->req.cgi_dir?session->req.cgi_dir:cgi_dir);
lprintf(LOG_DEBUG,"%04d CGI startup dir: %s", session->socket, startup_dir);
get_cgi_handler(cmdline, sizeof(cmdline));
lprintf(LOG_INFO,"%04d Executing CGI: %s",session->socket,cmdline);
orig_keep=session->req.keep_alive;
session->req.keep_alive=FALSE;
memset(&sa,0,sizeof(sa));
sa.nLength= sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
/* Create the child output pipe (override default 4K buffer size) */
if(!CreatePipe(&rdoutpipe,&startup_info.hStdOutput,&sa,sizeof(buf))) {
lprintf(LOG_ERR,"%04d !ERROR %d creating stdout pipe",session->socket,GetLastError());
return(FALSE);
}
startup_info.hStdError=startup_info.hStdOutput;
if(!CreatePipe(&startup_info.hStdInput,&wrinpipe,&sa,0 /* default buffer size */)) {
lprintf(LOG_ERR,"%04d !ERROR %d creating stdin pipe",session->socket,GetLastError());
return(FALSE);
}
DuplicateHandle(
GetCurrentProcess(), rdoutpipe,
GetCurrentProcess(), &rdpipe, 0, FALSE, DUPLICATE_SAME_ACCESS);
DuplicateHandle(
GetCurrentProcess(), wrinpipe,
GetCurrentProcess(), &wrpipe, 0, FALSE, DUPLICATE_SAME_ACCESS);
CloseHandle(rdoutpipe);
CloseHandle(wrinpipe);
env_list=get_cgi_env(session);
env_block = strListCreateBlock(env_list);
strListFree(&env_list);
NULL, /* pointer to name of executable module */
cmdline, /* pointer to command line string */
NULL, /* process security attributes */
NULL, /* thread security attributes */
TRUE, /* handle inheritance flag */
CREATE_NEW_CONSOLE, /* creation flags */
env_block, /* pointer to new environment block */
startup_dir, /* pointer to current directory name */
&startup_info, /* pointer to STARTUPINFO */
&process_info /* pointer to PROCESS_INFORMATION */
);
strListFreeBlock(env_block);
if(!success) {
lprintf(LOG_ERR,"%04d !ERROR %d running %s",session->socket,GetLastError(),cmdline);
return(FALSE);
}
start=time(NULL);
SAFECOPY(cgi_status,session->req.status);
SAFEPRINTF2(content_type,"%s: %s",get_header(HEAD_TYPE),startup->default_cgi_content);
while(server_socket!=INVALID_SOCKET) {
if(WaitForSingleObject(process_info.hProcess,0)==WAIT_OBJECT_0)
process_terminated=TRUE; /* handle remaining data in pipe before breaking */
if((time(NULL)-start) >= startup->max_cgi_inactivity) {
lprintf(LOG_WARNING,"%04d CGI Process %s timed out after %u seconds of inactivity"
,session->socket,getfname(cmdline),startup->max_cgi_inactivity);
break;
}
waiting = 0;
PeekNamedPipe(
rdpipe, /* handle to pipe to copy from */
NULL, /* pointer to data buffer */
0, /* size, in bytes, of data buffer */
NULL, /* pointer to number of bytes read */
&waiting, /* pointer to total number of bytes available */
NULL /* pointer to unread bytes in this message */
);
if(!waiting) {
if(process_terminated)
break;
Sleep(1);
continue;
}
/* reset inactivity timer */
start=time(NULL);
msglen=0;
if(done_parsing_headers) {
if(ReadFile(rdpipe,buf,sizeof(buf),&msglen,NULL)==FALSE) {
lprintf(LOG_ERR,"%04d !ERROR %d reading from pipe"
,session->socket,GetLastError());
break;
}
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
}
else {
/* This is the tricky part */
buf[0]=0;
i=pipereadline(rdpipe,buf,sizeof(buf),NULL,0);
if(i<0) {
lprintf(LOG_WARNING,"%04d CGI pipereadline returned %d",session->socket,i);
got_valid_headers=FALSE;
break;
}
lprintf(LOG_DEBUG,"%04d CGI header line: %s"
,session->socket, buf);
SAFECOPY(header,buf);
if(strchr(header,':')!=NULL) {
directive=strtok(header,":");
value=strtok(NULL,"");
i=get_header_type(directive);
switch (i) {
case HEAD_LOCATION:
got_valid_headers=TRUE;
if(*value=='/') {
unescape(value);
SAFECOPY(session->req.virtual_path,value);
session->req.send_location=MOVED_STAT;
if(cgi_status[0]==0)
SAFECOPY(cgi_status,error_302);
} else {
SAFECOPY(session->req.virtual_path,value);
session->req.send_location=MOVED_TEMP;
if(cgi_status[0]==0)
SAFECOPY(cgi_status,error_302);
}
break;
case HEAD_STATUS:
SAFECOPY(cgi_status,value);
break;
case HEAD_LENGTH:
session->req.keep_alive=orig_keep;
strListPush(&session->req.dynamic_heads,buf);
break;
case HEAD_TYPE:
got_valid_headers=TRUE;
SAFECOPY(content_type,buf);
break;
case HEAD_TRANSFER_ENCODING:
no_chunked=TRUE;
break;
default:
strListPush(&session->req.dynamic_heads,buf);
}
continue;
}
if(i) {
strcat(buf,"\r\n"); /* Add back the missing line terminator */
msglen=strlen(buf); /* we will send this text later */
}
done_parsing_headers = TRUE; /* invalid header */
session->req.dynamic=IS_CGI;
if(!no_chunked && session->http_ver>=HTTP_1_1) {
session->req.keep_alive=orig_keep;
set_chunked=TRUE;
strListPush(&session->req.dynamic_heads,content_type);
send_headers(session,cgi_status,set_chunked);
}
if(msglen) {
lprintf(LOG_DEBUG,"%04d Sending %d bytes: %.*s"
,session->socket,msglen,msglen,buf);
wr=writebuf(session,buf,msglen);
/* log actual bytes sent */
if(session->req.ld!=NULL && wr>0)
session->req.ld->size+=wr;
}
}
if(GetExitCodeProcess(process_info.hProcess, &retval)==FALSE)
lprintf(LOG_ERR,"%04d !ERROR GetExitCodeProcess(%s) returned %d"
,session->socket,getfname(cmdline),GetLastError());
if(retval==STILL_ACTIVE) {
lprintf(LOG_WARNING,"%04d Terminating CGI process: %s"
,session->socket,getfname(cmdline));