Newer
Older
struct fastcgi_body {
uint16_t len;
char data[];
};
struct fastcgi_data {
SOCKET sock;
struct fastcgi_header header;
struct fastcgi_body *body;
size_t used;
};
static struct fastcgi_body * fastcgi_read_body(SOCKET sock)
{
struct fastcgi_header header;
struct fastcgi_body *body;
if (recv(sock, (char*)&header.len
,sizeof(header) - offsetof(struct fastcgi_header, len), MSG_WAITALL)
!= sizeof(header) - offsetof(struct fastcgi_header, len)) {
lprintf(LOG_ERR, "Error reading FastCGI message header");
return NULL;
}
body = (struct fastcgi_body *)malloc(offsetof(struct fastcgi_body, data) + htons(header.len));
body->len = htons(header.len);
if (recv(sock, body->data, body->len, MSG_WAITALL) != body->len) {
free(body);
lprintf(LOG_ERR, "Error reading FastCGI message");
return NULL;
}
if (recv(sock, padding, header.padlen, MSG_WAITALL) != header.padlen) {
free(body);
lprintf(LOG_ERR, "Error reading FastCGI padding");
return NULL;
}
return body;
}
static int fastcgi_read_wait_timeout(void *arg)
{
int ret = 0;
struct fastcgi_data *cd = (struct fastcgi_data *)arg;
struct fastcgi_body *body;
switch (cd->header.type) {
case FCGI_STDOUT:
return CGI_OUTPUT_READY;
break;
case FCGI_STDERR:
return CGI_ERROR_READY;
break;
}
if (socket_check(cd->sock, &rd, NULL, startup->max_cgi_inactivity*1000)) {
if (rd) {
if (recv(cd->sock, (void *)&cd->header, offsetof(struct fastcgi_header, len), MSG_WAITALL) != offsetof(struct fastcgi_header, len)) {
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
lprintf(LOG_ERR, "FastCGI failed to read header");
return ret;
}
if (cd->header.ver != FCGI_VERSION_1) {
lprintf(LOG_ERR, "Unknown FastCGI version %d", cd->header.ver);
return ret;
}
if (htons(cd->header.id) != 1) {
lprintf(LOG_ERR, "Unknown FastCGI session ID %d", htons(cd->header.id));
return ret;
}
switch(cd->header.type) {
case FCGI_STDOUT:
ret |= CGI_OUTPUT_READY;
break;
case FCGI_STDERR:
ret |= CGI_OUTPUT_READY;
break;
case FCGI_END_REQUEST:
ret |= CGI_PROCESS_TERMINATED;
// Fall-through
case FCGI_BEGIN_REQUEST:
case FCGI_ABORT_REQUEST:
case FCGI_PARAMS:
case FCGI_STDIN:
case FCGI_DATA:
case FCGI_GET_VALUES:
case FCGI_GET_VALUES_RESULT:
case FCGI_UNKNOWN_TYPE:
// Read and discard the entire message...
body = fastcgi_read_body(cd->sock);
if (body == NULL)
free(body);
break;
default:
lprintf(LOG_ERR, "Unhandled FastCGI message type %d", cd->header.type);
// Read and discard the entire message...
body = fastcgi_read_body(cd->sock);
if (body == NULL)
return ret;
}
static int fastcgi_read(void *arg, char *buf, size_t sz)
{
struct fastcgi_data *cd = (struct fastcgi_data *)arg;
cd->body = fastcgi_read_body(cd->sock);
if (cd->body == NULL)
return -1;
}
if (sz > (cd->body->len - cd->used))
sz = cd->body->len - cd->used;
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
cd->used += sz;
if (cd->used >= cd->body->len) {
FREE_AND_NULL(cd->body);
cd->header.type = 0;
cd->used = 0;
}
return sz;
}
/*
* This one is extra tricky since it may need multiple messages to fill...
* and those messages may not follow each other in the stream.
* For now, we just hack and hope.
*/
static int fastcgi_readln_out(void *arg, char *buf, size_t bufsz, char *fbuf, size_t fbufsz)
{
size_t inpos, outpos;
struct fastcgi_data *cd = (struct fastcgi_data *)arg;
outpos = 0;
cd->body = fastcgi_read_body(cd->sock);
if (cd->body == NULL)
return -1;
}
for (outpos = 0, inpos = cd->used; inpos < cd->body->len && outpos < bufsz; inpos++) {
if (cd->body->data[inpos] == '\n') {
inpos++;
break;
buf[outpos++] = cd->body->data[inpos];
}
// Terminate... even if we need to truncate.
if (outpos >= bufsz)
outpos--;
buf[outpos] = 0;
cd->used = inpos;
if (cd->used >= cd->body->len) {
FREE_AND_NULL(cd->body);
cd->header.type = 0;
cd->used = 0;
static int fastcgi_write_in(void *arg, char *buf, size_t bufsz)
{
struct fastcgi_header head;
struct fastcgi_data *cd = (struct fastcgi_data *)arg;
size_t pos;
size_t chunk_size;
for (pos = 0; pos < bufsz;) {
chunk_size = bufsz - pos;
if (chunk_size > UINT16_MAX)
chunk_size = UINT16_MAX;
head.len = htons((uint16_t)chunk_size);
if (sendsocket(cd->sock, (void *)&head, sizeof(head)) != sizeof(head))
return -1;
if (sendsocket(cd->sock, buf+pos, chunk_size) != chunk_size)
return -1;
pos += chunk_size;
}
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
return bufsz;
}
static int fastcgi_done_wait(void *arg)
{
struct fastcgi_data *cd = (struct fastcgi_data *)arg;
return (!socket_check(cd->sock, NULL, NULL, /* timeout: */0));
}
#ifdef __unix__
struct cgi_data {
int out_pipe; // out_pipe[0]
int err_pipe; // err_pipe[0]
pid_t child; // child
};
static int cgi_read_wait_timeout(void *arg)
{
int ret = 0;
int status=0;
int high_fd;
fd_set read_set;
fd_set write_set;
struct cgi_data *cd = (struct cgi_data *)arg;
struct timeval tv;
high_fd = cd->err_pipe;
if (cd->out_pipe > cd->err_pipe)
high_fd = cd->out_pipe;
tv.tv_sec=startup->max_cgi_inactivity;
tv.tv_usec=0;
FD_ZERO(&read_set);
FD_SET(cd->out_pipe,&read_set);
FD_SET(cd->err_pipe,&read_set);
FD_ZERO(&write_set);
if(select(high_fd+1,&read_set,&write_set,NULL,&tv)>0) {
if (FD_ISSET(cd->out_pipe,&read_set))
ret |= CGI_OUTPUT_READY;
if(FD_ISSET(cd->err_pipe,&read_set))
ret |= CGI_ERROR_READY;
if (waitpid(cd->child,&status,WNOHANG)==cd->child)
ret |= CGI_PROCESS_TERMINATED;
return ret;
}
static int cgi_read_out(void *arg, char *buf, size_t sz)
{
struct cgi_data *cd = (struct cgi_data *)arg;
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
static int cgi_read_err(void *arg, char *buf, size_t sz)
{
struct cgi_data *cd = (struct cgi_data *)arg;
return read(cd->err_pipe,buf,sz);
}
static int cgi_readln_out(void *arg, char *buf, size_t bufsz, char *fbuf, size_t fbufsz)
{
struct cgi_data *cd = (struct cgi_data *)arg;
return pipereadline(cd->out_pipe, buf, bufsz, fbuf, fbufsz);
}
static int cgi_write_in(void *arg, char *buf, size_t bufsz)
{
// *nix doesn't have an input pipe
return 0;
}
static int cgi_done_wait(void *arg)
{
int status=0;
struct cgi_data *cd = (struct cgi_data *)arg;
return waitpid(cd->child,&status,WNOHANG)==cd->child;
}
#else
struct cgi_data {
HANDLE rdpipe;
HANDLE wrpipe;
HANDLE child;
};
static int cgi_read_wait_timeout(void *arg)
{
int ret = 0;
struct cgi_data *cd = (struct cgi_data *)arg;
DWORD waiting;
time_t end = time(NULL) + startup->max_cgi_inactivity;
while(ret == 0) {
if(WaitForSingleObject(cd->child,0)==WAIT_OBJECT_0)
ret |= CGI_PROCESS_TERMINATED;
waiting = 0;
PeekNamedPipe(
cd->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)
ret |= CGI_OUTPUT_READY;
if(!session_check(cd->session, &rd, NULL, /* timeout: */0))
ret |= CGI_INPUT_READY;
if (rd)
ret |= CGI_INPUT_READY;
if (time(NULL) >= end)
break;
if (ret == 0)
Sleep(1);
}
return ret;
}
static int cgi_read_out(void *arg, char *buf, size_t sz)
{
DWORD msglen = 0;
struct cgi_data *cd = (struct cgi_data *)arg;
if(ReadFile(cd->rdpipe,buf,sz,&msglen,NULL)==FALSE) {
lprintf(LOG_ERR,"%04d !ERROR %d reading from pipe"
,cd->session->socket,GetLastError());
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
}
static int cgi_read_err(void *arg, char *buf, size_t sz)
{
// Win32 doesn't have an error pipe
return 0;
}
static int cgi_readln_out(void *arg, char *buf, size_t bufsz, char *fbuf, size_t fbufsz)
{
struct cgi_data *cd = (struct cgi_data *)arg;
return pipereadline(cd->rdpipe, buf, bufsz, NULL, 0);
}
static int cgi_write_in(void *arg, char *buf, size_t bufsz)
{
int wr;
struct cgi_data *cd = (struct cgi_data *)arg;
WriteFile(cd->wrpipe, buf, bufsz, &wr, /* Overlapped: */NULL);
return wr;
}
static int cgi_done_wait(void *arg)
{
struct cgi_data *cd = (struct cgi_data *)arg;
return (WaitForSingleObject(cd->child,0)==WAIT_OBJECT_0);
}
#endif
struct cgi_api {
int (*read_wait_timeout)(void *arg);
int (*read_out)(void *arg, char *buf, size_t sz);
int (*read_err)(void *arg, char *buf, size_t sz);
int (*readln_out)(void *arg, char *buf, size_t bufsz, char *fbuf, size_t fbufsz);
int (*write_in)(void *arg, char *buf, size_t bufsz);
int (*done_wait)(void *arg);
void *arg;
};
/*
* Need to return:
* Success/fail
* Timeout out or not
* Done parsing headers or not
* Got valid headers or not
* Process exited or not.
*/
static int do_cgi_stuff(http_session_t *session, struct cgi_api *cgi, BOOL orig_keep)
{
int ret = 0;
#define CGI_STUFF_FAILED (1<<0)
#define CGI_STUFF_TIMEDOUT (1<<1)
#define CGI_STUFF_DONE_PARSING (1<<2)
#define CGI_STUFF_VALID_HEADERS (1<<3)
#define CGI_STUFF_PROCESS_EXITED (1<<4)
int ready;
int i;
char cgi_status[MAX_REQUEST_LINE+1];
char header[MAX_REQUEST_LINE+1];
char buf[1024];
char fbuf[1026];
char *directive=NULL;
char *value=NULL;
char *last;
BOOL done_reading=FALSE;
BOOL done_wait=FALSE;
BOOL no_chunked=FALSE;
BOOL set_chunked=FALSE;
time_t start;
str_list_t tmpbuf;
start=time(NULL);
/* ToDo: Magically set done_parsing_headers for nph-* scripts */
cgi_status[0]=0;
while(!done_reading) {
ready = cgi->read_wait_timeout(cgi->arg);
if(ready) {
if(ready & CGI_OUTPUT_READY) {
if((ret & CGI_STUFF_DONE_PARSING) && (ret & CGI_STUFF_VALID_HEADERS)) {
i=cgi->read_out(cgi->arg,buf,sizeof(buf));
int snt=0;
start=time(NULL);
snt=writebuf(session,buf,i);
if(session->req.ld!=NULL)
session->req.ld->size+=snt;
done_reading=TRUE;
}
else {
/* This is the tricky part */
i=cgi->readln_out(cgi->arg, buf, sizeof(buf), fbuf, sizeof(fbuf));
done_reading=TRUE;
start=time(NULL);
SAFECOPY(header,buf);
directive=strtok_r(header,":",&last);
if(directive != NULL) {
value=strtok_r(NULL,"",&last);
if(value != NULL) {
SKIP_WHITESPACE(value);
i=get_header_type(directive);
switch (i) {
case HEAD_LOCATION:
ret |= CGI_STUFF_VALID_HEADERS;
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
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);
/*
* 1xx, 204, and 304 responses don't have bodies, so don't
* need a Location or Content-Type header to be valid.
*/
if (value[0] == '1' || ((value[0] == '2' || value[0] == '3') && value[1] == '0' && value[2] == '4'))
ret |= CGI_STUFF_VALID_HEADERS;
break;
case HEAD_LENGTH:
session->req.keep_alive=orig_keep;
strListPush(&session->req.dynamic_heads,buf);
no_chunked=TRUE;
break;
case HEAD_TYPE:
ret |= CGI_STUFF_VALID_HEADERS;
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 */
}
}
else {
if(!no_chunked && session->http_ver>=HTTP_1_1) {
session->req.keep_alive=orig_keep;
if (session->req.method != HTTP_HEAD)
set_chunked=TRUE;
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;
}
}
}
if(ready & CGI_ERROR_READY) {
i=cgi->read_err(cgi->arg,buf,sizeof(buf)-1);
if(i>0) {
buf[i]=0;
lprintf(LOG_ERR,"%04d CGI Error: %s",session->socket,buf);
start=time(NULL);
if(ready & CGI_INPUT_READY) {
/* Send received POST Data to stdin of CGI process */
if((i=sess_recv(session, buf, sizeof(buf), 0)) > 0) {
lprintf(LOG_DEBUG,"%04d CGI Received %d bytes of POST data"
,session->socket, i);
cgi->write_in(cgi->arg, buf, i);
}
}
done_wait = TRUE;
}
if(!done_wait)
done_wait = cgi->done_wait(cgi->arg);
if((!(ready & (CGI_OUTPUT_READY|CGI_ERROR_READY))) && 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(session->req.physical_path));
done_reading=TRUE;
start=0;
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
return ret;
}
static BOOL exec_fastcgi(http_session_t *session)
{
int msglen;
BOOL orig_keep=FALSE;
SOCKET sock;
struct fastcgi_message *msg;
struct fastcgi_begin_request *br;
struct fastcgi_data cd;
struct cgi_api cgi = {
.read_wait_timeout = fastcgi_read_wait_timeout,
.read_out = fastcgi_read,
.read_err = fastcgi_read,
.readln_out = fastcgi_readln_out,
.write_in = fastcgi_write_in,
.done_wait = fastcgi_done_wait,
.arg = &cd
};
lprintf(LOG_INFO,"%04d Executing FastCGI: %s",session->socket,session->req.physical_path);
if (session->req.fastcgi_socket == NULL) {
lprintf(LOG_ERR, "%04d No FastCGI socket configured!",session->socket);
return FALSE;
}
orig_keep=session->req.keep_alive;
session->req.keep_alive=FALSE;
sock = fastcgi_connect(session->req.fastcgi_socket, session->socket);
if (sock == INVALID_SOCKET)
return FALSE;
// Set up request...
msglen = sizeof(struct fastcgi_header) + sizeof(struct fastcgi_begin_request);
msg = (struct fastcgi_message *)malloc(msglen);
if (msg == NULL) {
closesocket(sock);
lprintf(LOG_ERR, "%04d Failure to allocate memory for FastCGI message!", session->socket);
return FALSE;
}
fastcgi_init_header(&msg->head, FCGI_BEGIN_REQUEST);
msg->head.len = htons(sizeof(struct fastcgi_begin_request));
br = (struct fastcgi_begin_request *)&msg->body;
br->flags = 0;
memset(br->reserved, 0, sizeof(br->reserved));
if (sendsocket(sock, (void *)msg, msglen) != msglen) {
lprintf(LOG_WARNING, "%04d Failure to send to FastCGI socket!", session->socket);
return FALSE;
}
if (!fastcgi_send_params(sock, session)) {
free(msg);
closesocket(sock);
return FALSE;
}
// TODO handle stdin better
memset(&cd, 0, sizeof(cd));
cd.sock = sock;
fastcgi_write_in(&cd, session->req.post_data, session->req.post_len);
if (sendsocket(sock, (void *)msg, sizeof(struct fastcgi_header)) != sizeof(struct fastcgi_header)) {
lprintf(LOG_WARNING, "%04d Failure to send stdin to FastCGI socket!", session->socket);
return FALSE;
}
free(msg);
// Now handle stuff coming back from the FastCGI socket...
int ret = do_cgi_stuff(session, &cgi, orig_keep);
FREE_AND_NULL(cd.body);
closesocket(sock);
if(!(ret & CGI_STUFF_VALID_HEADERS)) {
lprintf(LOG_ERR,"%04d FastCGI Process did not generate valid headers", session->socket);
return(FALSE);
}
if(!(ret & CGI_STUFF_DONE_PARSING)) {
lprintf(LOG_ERR,"%04d FastCGI Process did not send data header termination", session->socket);
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
return(FALSE);
}
return TRUE;
}
static BOOL exec_cgi(http_session_t *session)
{
struct cgi_data cd;
struct cgi_api cgi = {
.read_wait_timeout = cgi_read_wait_timeout,
.read_out = cgi_read_out,
.read_err = cgi_read_err,
.readln_out = cgi_readln_out,
.write_in = cgi_write_in,
.done_wait = cgi_done_wait,
.arg = &cd
};
#ifdef __unix__
char cmdline[MAX_PATH+256];
int i=0;
int status=0;
pid_t child=0;
int out_pipe[2];
int err_pipe[2];
struct timeval tv={0,0};
fd_set read_set;
int high_fd=0;
char buf[1024];
BOOL done_parsing_headers=FALSE;
BOOL done_wait=FALSE;
BOOL got_valid_headers=FALSE;
time_t start;
char cgipath[MAX_PATH+1];
char *p;
BOOL orig_keep=FALSE;
SAFECOPY(cmdline,session->req.physical_path);
lprintf(LOG_INFO,"%04d Executing CGI: %s",session->socket,cmdline);
orig_keep=session->req.keep_alive;
session->req.keep_alive=FALSE;
/* 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) {
str_list_t env_list;
/* Do a full suid thing. */
if(startup->setuid!=NULL)
startup->setuid(TRUE);
env_list=get_cgi_env(session);
/* 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((p=get_cgi_handler(cmdline))!=NULL) {
char* shell=os_cmdshell();
lprintf(LOG_INFO,"%04d Using handler %s to execute %s",session->socket,p,cmdline);
execle(shell,shell,"-c",p,cmdline,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 */
if(child==-1)
return(FALSE);
start=time(NULL);
high_fd=out_pipe[0];
if(err_pipe[0]>high_fd)
high_fd=err_pipe[0];
cd.out_pipe = out_pipe[0];
cd.err_pipe = err_pipe[0];
cd.child = child;
int ret = do_cgi_stuff(session, &cgi, orig_keep);
if (ret & CGI_STUFF_DONE_PARSING)
done_parsing_headers = TRUE;
if (ret & CGI_STUFF_PROCESS_EXITED)
done_wait = TRUE;
if (ret & CGI_STUFF_TIMEDOUT)
start = 1;
if (ret & CGI_STUFF_VALID_HEADERS)
got_valid_headers = TRUE;
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);
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];
BOOL orig_keep;
BOOL done_parsing_headers=FALSE;
BOOL got_valid_headers=FALSE;
char *directive=NULL;
char *value=NULL;
int set_chunked=FALSE;
/* Win32-specific */
char startup_dir[MAX_PATH+1];
HANDLE rdpipe=INVALID_HANDLE_VALUE;
HANDLE wrpipe=INVALID_HANDLE_VALUE;
HANDLE rdoutpipe;
HANDLE wrinpipe;
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(startup_dir,session->req.physical_path);
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);
if((p=get_cgi_handler(session->req.physical_path))!=NULL)
SAFEPRINTF2(cmdline,"%s %s",p,session->req.physical_path);
else
SAFECOPY(cmdline,session->req.physical_path);
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);
lprintf(LOG_ERR,"%04d !ERROR %d running %s",session->socket,GetLastError(),cmdline);
return(FALSE);
}
cd.wrpipe = wrpipe;
cd.rdpipe = rdpipe;
cd.child = process_info.hProcess;
int ret = do_cgi_stuff(session, &cgi, orig_keep);
if (ret & CGI_STUFF_DONE_PARSING)
done_parsing_headers = TRUE;
if (ret & CGI_STUFF_VALID_HEADERS)
got_valid_headers = TRUE;
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));
TerminateProcess(process_info.hProcess, GetLastError());