You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
258 lines
6.0 KiB
C
258 lines
6.0 KiB
C
10 years ago
|
/*
|
||
|
* started writing (limited) CGI support for webfsd
|
||
|
*/
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#include <errno.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <ctype.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
|
||
|
#include <netinet/in.h>
|
||
|
#include <netinet/tcp.h>
|
||
|
#include <arpa/inet.h>
|
||
|
#include <netdb.h>
|
||
|
|
||
|
#include "httpd.h"
|
||
|
|
||
|
/* ---------------------------------------------------------------------- */
|
||
|
|
||
|
extern char **environ;
|
||
|
|
||
|
static char *env_wlist[] = {
|
||
|
"PATH", "HOME",
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
static void env_add(struct strlist **list, char *name, char *value)
|
||
|
{
|
||
|
char *line;
|
||
|
|
||
|
line = malloc(strlen(name) + strlen(value) + 2);
|
||
|
sprintf(line,"%s=%s",name,value);
|
||
|
if (debug)
|
||
|
fprintf(stderr,"cgi: env %s\n",line);
|
||
|
list_add(list,line,1);
|
||
|
}
|
||
|
|
||
|
static char**
|
||
|
env_convert(struct strlist *list)
|
||
|
{
|
||
|
struct strlist *elem;
|
||
|
char **env;
|
||
|
int i;
|
||
|
|
||
|
for (i = 2, elem = list; NULL != elem; elem = elem->next)
|
||
|
i++;
|
||
|
env = malloc(sizeof(char*)*i);
|
||
|
for (i = 0, elem = list; NULL != elem; elem = elem->next)
|
||
|
env[i++] = elem->line;
|
||
|
env[i++] = NULL;
|
||
|
return env;
|
||
|
}
|
||
|
|
||
|
static void env_copy(struct strlist **list)
|
||
|
{
|
||
|
int i,j,l;
|
||
|
|
||
|
for (i = 0; environ[i] != NULL; i++) {
|
||
|
for (j = 0; env_wlist[j] != NULL; j++) {
|
||
|
l = strlen(env_wlist[j]);
|
||
|
if (0 == strncmp(environ[i],env_wlist[j],l) &&
|
||
|
environ[i][l] == '=') {
|
||
|
env_add(list,env_wlist[j],environ[i]+l+1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* ---------------------------------------------------------------------- */
|
||
|
|
||
|
void
|
||
|
cgi_request(struct REQUEST *req)
|
||
|
{
|
||
|
struct sockaddr_storage addr;
|
||
|
struct strlist *env = NULL, *item;
|
||
|
char host[65],serv[9];
|
||
|
char filename[1024], *h, *argv[2], envname[128];
|
||
|
int pid,p[2],i,length;
|
||
|
|
||
|
if (debug)
|
||
|
fprintf(stderr,"%03d: is cgi request\n",req->fd);
|
||
|
if (-1 == pipe(p)) {
|
||
|
mkerror(req,500,0);
|
||
|
return;
|
||
|
}
|
||
|
pid = fork();
|
||
|
switch (pid) {
|
||
|
case -1:
|
||
|
/* error */
|
||
|
if (debug)
|
||
|
perror("fork");
|
||
|
mkerror(req,500,0);
|
||
|
return;
|
||
|
case 0:
|
||
|
break;
|
||
|
default:
|
||
|
/* parent - webfsd */
|
||
|
close(p[1]);
|
||
|
req->cgipid = pid;
|
||
|
req->cgipipe = p[0];
|
||
|
req->state = STATE_CGI_HEADER;
|
||
|
close_on_exec(req->cgipipe);
|
||
|
fcntl(req->cgipipe,F_SETFL,O_NONBLOCK);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* -------- below is the child process (cgi) code -------- */
|
||
|
|
||
|
/* lookup local socket (before it gets closed) */
|
||
|
length = sizeof(addr);
|
||
|
getsockname(req->fd,(struct sockaddr*)&addr,&length);
|
||
|
getnameinfo((struct sockaddr*)&addr,length,host,64,serv,8,
|
||
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
||
|
|
||
|
/* setup file descriptors */
|
||
|
dup2(p[1],1); /* pipe -> stdout */
|
||
|
if (have_tty) {
|
||
|
int devnull = open("/dev/null",O_RDWR);
|
||
|
dup2(devnull,0); /* stdin */
|
||
|
dup2(devnull,2); /* stderr */
|
||
|
close(devnull);
|
||
|
} else {
|
||
|
/* nothing -- already attached to /dev/null */
|
||
|
}
|
||
|
close_on_exec(p[0]);
|
||
|
close_on_exec(p[1]);
|
||
|
|
||
|
/* setup environment */
|
||
|
env_copy(&env);
|
||
|
|
||
|
env_add(&env,"DOCUMENT_ROOT",doc_root);
|
||
|
env_add(&env,"GATEWAY_INTERFACE","CGI/1.1");
|
||
|
env_add(&env,"QUERY_STRING",req->query);
|
||
|
env_add(&env,"REQUEST_URI",req->uri);
|
||
|
env_add(&env,"REMOTE_ADDR",req->peerhost);
|
||
|
env_add(&env,"REMOTE_PORT",req->peerserv);
|
||
|
env_add(&env,"REQUEST_METHOD",req->type);
|
||
|
env_add(&env,"SERVER_ADMIN","root@localhost");
|
||
|
env_add(&env,"SERVER_NAME",server_host);
|
||
|
env_add(&env,"SERVER_PROTOCOL","HTTP/1.1");
|
||
|
env_add(&env,"SERVER_SOFTWARE",server_name);
|
||
|
env_add(&env,"SERVER_ADDR",host);
|
||
|
env_add(&env,"SERVER_PORT",serv);
|
||
|
|
||
|
for (item = req->header; NULL != item; item = item->next) {
|
||
|
strcpy(envname,"HTTP_");
|
||
|
if (1 != sscanf(item->line,"%120[-A-Za-z]: %n",envname+5,&length))
|
||
|
continue;
|
||
|
for (i = 0; envname[i]; i++) {
|
||
|
if (isalpha(envname[i]))
|
||
|
envname[i] = toupper(envname[i]);
|
||
|
if ('-' == envname[i])
|
||
|
envname[i] = '_';
|
||
|
}
|
||
|
env_add(&env,envname,item->line+length);
|
||
|
}
|
||
|
|
||
|
h = req->path + strlen(cgipath);
|
||
|
h = strchr(h,'/');
|
||
|
if (h) {
|
||
|
env_add(&env,"PATH_INFO",h);
|
||
|
*h = 0;
|
||
|
} else {
|
||
|
env_add(&env,"PATH_INFO","");
|
||
|
}
|
||
|
env_add(&env,"SCRIPT_NAME",req->path);
|
||
|
snprintf(filename,sizeof(filename)-1,"%s%s",doc_root,req->path);
|
||
|
env_add(&env,"SCRIPT_FILENAME",filename);
|
||
|
|
||
|
/* start cgi app */
|
||
|
argv[0] = filename;
|
||
|
argv[1] = NULL;
|
||
|
execve(filename,argv,env_convert(env));
|
||
|
|
||
|
/* exec failed ... */
|
||
|
printf("Content-Type: text/plain\n"
|
||
|
"\n"
|
||
|
"execve %s: %s\n",
|
||
|
filename,strerror(errno));
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
/* ---------------------------------------------------------------------- */
|
||
|
|
||
|
void
|
||
|
cgi_read_header(struct REQUEST *req)
|
||
|
{
|
||
|
struct strlist *list = NULL;
|
||
|
char *h,*next,*status = NULL;
|
||
|
int rc;
|
||
|
|
||
|
restart:
|
||
|
rc = read(req->cgipipe, req->cgibuf+req->cgilen, MAX_HEADER-req->cgilen);
|
||
|
switch (rc) {
|
||
|
case -1:
|
||
|
if (errno == EAGAIN)
|
||
|
return;
|
||
|
if (errno == EINTR)
|
||
|
goto restart;
|
||
|
/* fall through */
|
||
|
case 0:
|
||
|
mkerror(req,500,0);
|
||
|
return;
|
||
|
default:
|
||
|
req->cgilen += rc;
|
||
|
req->cgibuf[req->cgilen] = 0;
|
||
|
}
|
||
|
|
||
|
/* header complete ?? */
|
||
|
if (NULL != (h = strstr(req->cgibuf,"\r\n\r\n")) ||
|
||
|
NULL != (h = strstr(req->cgibuf,"\n\n"))) {
|
||
|
|
||
|
/* parse cgi header */
|
||
|
for (h = req->cgibuf;; h = next) {
|
||
|
next = strstr(h,"\n");
|
||
|
next[0] = 0;
|
||
|
if (next[-1] == '\r')
|
||
|
next[-1] = 0;
|
||
|
next++;
|
||
|
|
||
|
if (0 == strlen(h))
|
||
|
break;
|
||
|
if (debug)
|
||
|
fprintf(stderr,"%03d: cgi: hdr %s\n",req->fd,h);
|
||
|
if (0 == strncasecmp(h,"Status: ",8)) {
|
||
|
status = h+8;
|
||
|
if (debug)
|
||
|
fprintf(stderr,"%03d: cgi: status %s\n",req->fd,status);
|
||
|
continue;
|
||
|
}
|
||
|
if (0 == strncasecmp(h,"Server:",7) ||
|
||
|
0 == strncasecmp(h,"Connection:",11) ||
|
||
|
0 == strncasecmp(h,"Accept-Ranges:",14) ||
|
||
|
0 == strncasecmp(h,"Date:",5))
|
||
|
/* webfsd adds them -- filter out */
|
||
|
continue;
|
||
|
list_add(&list,h,0);
|
||
|
}
|
||
|
mkcgi(req, status ? status : "200 OK", list);
|
||
|
list_free(&list);
|
||
|
req->cgipos = next - req->cgibuf;
|
||
|
if (debug)
|
||
|
fprintf(stderr,"%03d: cgi: pos=%d len=%d\n",req->fd,
|
||
|
req->cgipos, req->cgilen);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (req->cgilen == MAX_HEADER) {
|
||
|
mkerror(req,400,0);
|
||
|
return;
|
||
|
}
|
||
|
return;
|
||
|
}
|