/* Copyright (C) 2002-2003 XIAO, Gang of Universite de Nice - Sophia Antipolis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* http dawmon for WIMS */
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "../wims.h"
#if !HAVE_SOCKLEN_T
typedef size_t socklen_t;
#endif
#define WIMS_TIMEOUT 200
#define WIMSD_TICK 10
#define WIMSD_IDLE 500
#define PIDLIM 100
char *acceptstr="127.0.0.1";
int pidtab[PIDLIM];
int pidcnt;
int sock;
int sport;
int idle; /* limited to WIMSD_TICK * WIMSD_IDLE */
void errorquit(char *msg)
{
}
void checkpid(void)
{
int i,k;
for(i=0;i<pidcnt;i++) {
waitpid(pidtab[i],&k,WNOHANG);
if(WIFEXITED(k)) {
memmove(pidtab
+i
,pidtab
+i
+1,(pidcnt
-i
-1)*sizeof(pidtab
[0]));
pidcnt--;
}
}
}
void addpid(int pid)
{
while(pidcnt>=PIDLIM) {
sleep(1); checkpid();
}
pidtab[pidcnt++]=pid;
}
void alarm1(int s)
{
}
void alarm2(int s)
{
checkpid(); idle++; alarm(WIMSD_TICK);
if(idle>=WIMSD_IDLE) alarm1(0);
}
/* Points to the end of the word */
char *find_word_end(char *p)
{
int i;
for(i
=0;!isspace(*p
) && *p
!=0 && i
<MAX_LINELEN
; p
++,i
++);
return p;
}
/* Strips leading spaces */
char *find_word_start(char *p)
{
int i;
for(i
=0; isspace(*p
) && i
<MAX_LINELEN
; p
++,i
++);
return p;
}
/* Read/write to a file with variable parms to print filename */
void wimsdaccessfile(char *content, char *type, char *s,...)
{
va_list vp;
char buf[MAX_LINELEN+1];
FILE *f;
int l;
vsnprintf(buf,sizeof(buf),s,vp);
f
=fopen(buf
,type
); if(f
==NULL
) {
if(*type=='r') content[0]=0; return;
}
switch(*type) {
case 'a':
case 'w': {
}
case 'r': {
l
=fread(content
,1,MAX_LINELEN
-1,f
);
if(l>0 && l<MAX_LINELEN) content[l]=0;
else content[0]=0;
break;
}
default: {
content[0]=0; break;
}
}
}
void badreq(void)
{
printf("HTTP/1.0 400 Bad Request\r\n\r\nBad Request.\r\n");
}
/* open a TCP/IP socket with host/port
* returns the file descriptor for the socket */
int net_connect(int port)
{
int soc, vrai;
if ((soc = socket(AF_INET, SOCK_STREAM, 0)) == -1)
errorquit("socket() error");
if(setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, &vrai, sizeof(vrai)) == -1)
errorquit("setsockopt() error");
sin.
sin_port=htons
(port
);
sin.
sin_family = AF_INET
;
inet_aton
(acceptstr
,&sin.
sin_addr);
if(bind
(soc
,(struct sockaddr
*)&sin, sizeof(sin))==-1)
errorquit("bind() error");
if(listen(soc,10)==-1) errorquit("listen() error");
return soc;
}
void putfile(char *fname,int soc)
{
int l;
long int siz;
char *p, namebuf[4096], buf[4096];
char cbuf[MAX_LINELEN+1];
struct stat st;
FILE *f;
if(fname
[strlen(fname
)-1]=='/')
snprintf(namebuf
,sizeof(namebuf
),"%sindex.html",fname
);
else snprintf(namebuf
,sizeof(namebuf
),"%s",fname
);
statit: if(stat(namebuf,&st)!=0) badreq();
if(S_ISDIR(st.st_mode)) {
snprintf(namebuf
,sizeof(namebuf
),"%s/index.html",fname
);
goto statit;
}
if(!S_ISREG(st.st_mode)) badreq();
badreq();
*p
=0; snprintf(buf
,sizeof(buf
),"%s/.htaccess",namebuf
); *p
='/';
if(stat(cbuf,&st)==0) {
wimsdaccessfile(cbuf,"r","%s",buf);
if(strstr(cbuf
,"deny from all")!=NULL
) badreq
();
}
}
siz=st.st_size;
Content-Length: %ld\r\n\r\n",
siz);
f
=fopen(namebuf
,"r"); if(f
==NULL
) badreq
();
do {
l
=fread(buf
,1,sizeof(buf
),f
);
if(l
>0 && l
<=sizeof(buf
)) fwrite(buf
,1,l
,stdout
);
} while(l==sizeof(buf));
}
struct sockaddr_in saddr;
void onereq(int soc)
{
char buf[QUERY_STRING_LIMIT+1], buf2[MAX_LINELEN+1];
int i, k, l;
/* FILE *fin, *fout; */
struct stat st;
char *query_method, *query_url, *p, *pp, *p2, *parms;
i=fork(); if(i!=0) {
if(i==-1) errorquit("fork() error");
addpid(i); return;
}
alarm(WIMS_TIMEOUT);
signal(SIGALRM,alarm1);
(void)chdir("public_html");
i=0; do {
l=read(soc, buf+i, 1); i++;
}
while(i
<QUERY_STRING_LIMIT
&& l
>0 && (i
<4 || memcmp(buf
+i
-4,"\r\n\r\n",4)!=0));
buf[i]=0;
/*printf("%s\n",buf);*/
dup2(soc,0); /* fin=fdopen(0,"r"); stdin=fin; */
dup2(soc,1); /* fout=fdopen(1,"w"); stdout=fout; */
query_method=find_word_start(buf);
query_url=find_word_end(query_method); if(query_url) *query_url++=0;
query_url=find_word_start(query_url);
p=find_word_end(query_url); if(*p) *p++=0;
parms
=strchr(p
,'\n'); if(parms
) parms
++;
if(strcmp(query_method
,"GET")!=0 &&
strcmp(query_method
,"HEAD")!=0 &&
strcmp(query_method
,"POST")!=0) badreq
();
if(query_url
[0]!='/' || query_url
[1]=='/' || strstr(query_url
,"..")!=NULL
)
badreq();
setenv("REQUEST_URL",query_url,1);
p
=strchr(query_url
,'?'); if(p
!=NULL
) *p
++=0; else p
="";
k=stat(query_url+1,&st);
if(k
!=0 && strchr(query_url
+1,'/')==NULL
)
query_url="/wims.cgi";
if(i<4 || strcasecmp(query_url+i-4,".cgi")!=0)
putfile(query_url+1,soc);
badreq();
setenv("QUERY_STRING",p,1);
setenv("QUERY_URL",query_url,1);
setenv("SCRIPT_NAME",query_url,1);
setenv("REQUEST_METHOD",query_method,1);
setenv("SERVER_SOFTWARE","WIMS",1);
setenv("SERVER_PORT",buf2,1);
for(; *parms; parms=p) {
if(p
==NULL
) p
=parms
+strlen(parms
);
else {
if(*(p-1)=='\r') *(p-1)=0;
*p++=0;
}
for(pp
=parms
; isalnum(*pp
) || *pp
=='-'; pp
++);
if(*pp==':') {
*pp++=0; pp=find_word_start(pp);
for(p2=parms; *p2; p2++) {
if(*p2
=='-') *p2
='_'; else *p2
=toupper(*p2
);
}
if(strcmp(parms
,"CONTENT_LENGTH")==0 ||
strcmp(parms
,"CONTENT_TYPE")==0) {
setenv(buf2,pp,1);
}
snprintf(buf2
,sizeof(buf2
),"HTTP_%s",parms
);
setenv(buf2,pp,1);
}
}
setenv("REMOTE_ADDR",inet_ntoa(saddr.sin_addr),1);
snprintf(buf2
,sizeof(buf2
),"%u",ntohs
(saddr.
sin_port));
setenv("REMOTE_PORT",buf2,1);
snprintf(buf2
,sizeof(buf2
),".%s",query_url
);
execl(buf2,buf2,NULL);
/* fclose(fout); fclose(fin); */ close
(soc
); exit(0);
}
void serve(int soc)
{
int newsoc;
socklen_t slen=sizeof(saddr);
wait:
alarm(WIMSD_TICK);
newsoc=accept(soc,(struct sockaddr *)&saddr, &slen);
if(newsoc==-1) errorquit("accept() error");
checkpid(); idle=0;
onereq(newsoc); close(newsoc); goto wait;
}
void parm(void)
{
char *p;
if(p!=NULL && *p!=0) (void)chdir(p);
if(p!=NULL && *p!=0) {
int i;
if(i>0 && i<65536) sport=i;
}
if(p
!=NULL
&& strstr(p
,"all")!=NULL
) acceptstr
="0.0.0.0";
}
int main(int argc, char *argv[])
{
struct stat st;
signal(SIGALRM,alarm2);
sport=8088; pidcnt=0; idle=0;
parm();
if(stat("public_html/wims.cgi",&st)!=0) {
char buf[1024];
(void)getcwd(buf,sizeof(buf));
fprintf(stderr
,"Bad home directory %s: \
wims.cgi not found.
\n"
,buf
); exit(1);
}
/* if(stat("public_html/bin/pari",&st)==0) {
fprintf(stderr,"wimsd must be run as nobody, \
for security reasons!\n\n\
Run bin/wrapuid as root to set things up correctly.\n"); exit(1);
}
*/ setreuid(geteuid(),getuid());setregid(getegid(),getgid());
sock=net_connect(sport);
serve(sock);
close(sock); return 0;
}