Back to index

wims  3.65+svn20090927
wimsd.c
Go to the documentation of this file.
00001 /*    Copyright (C) 2002-2003 XIAO, Gang of Universite de Nice - Sophia Antipolis
00002  *
00003  *  This program is free software; you can redistribute it and/or modify
00004  *  it under the terms of the GNU General Public License as published by
00005  *  the Free Software Foundation; either version 2 of the License, or
00006  *  (at your option) any later version.
00007  *
00008  *  This program is distributed in the hope that it will be useful,
00009  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  *  GNU General Public License for more details.
00012  *
00013  *  You should have received a copy of the GNU General Public License
00014  *  along with this program; if not, write to the Free Software
00015  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00016  */
00017 
00018 /* http dawmon for WIMS */
00019 
00020 #include "../wims.h"
00021 #include <netdb.h>
00022 #include <sys/socket.h>
00023 #include <netinet/in.h>
00024 #include <arpa/inet.h>
00025 
00026 #if !HAVE_SOCKLEN_T
00027 typedef size_t socklen_t;
00028 #endif
00029 
00030 #define WIMS_TIMEOUT 200
00031 #define WIMSD_TICK   10
00032 #define WIMSD_IDLE   500
00033 #define PIDLIM              100
00034 
00035 char *acceptstr="127.0.0.1";
00036 
00037 int pidtab[PIDLIM];
00038 int pidcnt;
00039 
00040 int sock;
00041 int sport;
00042 int idle; /* limited to WIMSD_TICK * WIMSD_IDLE */
00043 
00044 void errorquit(char *msg)
00045 {
00046     fprintf(stderr,"%s: %s\n",msg,strerror(errno)); exit(1);
00047 }
00048 
00049 void checkpid(void)
00050 {
00051     int i,k;
00052     for(i=0;i<pidcnt;i++) {
00053        waitpid(pidtab[i],&k,WNOHANG);
00054        if(WIFEXITED(k)) {
00055            memmove(pidtab+i,pidtab+i+1,(pidcnt-i-1)*sizeof(pidtab[0]));
00056            pidcnt--;
00057        }
00058     }
00059 }
00060 
00061 void addpid(int pid)
00062 {
00063     while(pidcnt>=PIDLIM) {
00064        sleep(1); checkpid();
00065     }
00066     pidtab[pidcnt++]=pid;
00067 }
00068 
00069 void alarm1(int s)
00070 {
00071     fprintf(stderr,"Time out.\n"); close(sock); exit(1);
00072 }
00073 
00074 void alarm2(int s)
00075 {
00076     checkpid(); idle++; alarm(WIMSD_TICK);
00077     if(idle>=WIMSD_IDLE) alarm1(0);
00078 }
00079 
00080        /* Points to the end of the word */
00081 char *find_word_end(char *p)
00082 {
00083     int i;
00084     for(i=0;!isspace(*p) && *p!=0 && i<MAX_LINELEN; p++,i++);
00085     return p;
00086 }
00087 
00088        /* Strips leading spaces */
00089 char *find_word_start(char *p)
00090 {
00091     int i;
00092     for(i=0; isspace(*p) && i<MAX_LINELEN; p++,i++);
00093     return p;
00094 }
00095 
00096        /* Read/write to a file with variable parms to print filename */
00097 void accessfile(char *content, char *type, char *s,...)
00098 {
00099     va_list vp;
00100     char buf[MAX_LINELEN+1];
00101     FILE *f;
00102     int l;
00103 
00104     va_start(vp,s);
00105     vsnprintf(buf,sizeof(buf),s,vp);
00106     va_end(vp);
00107     f=fopen(buf,type); if(f==NULL) {
00108        if(*type=='r') content[0]=0; return;
00109     }
00110     switch(*type) {
00111        case 'a':
00112        case 'w': {
00113            l=strlen(content); fwrite(content,1,l,f); break;
00114        }
00115        case 'r': {
00116            l=fread(content,1,MAX_LINELEN-1,f);
00117            if(l>0 && l<MAX_LINELEN) content[l]=0;
00118            else content[0]=0;
00119            break;
00120        }
00121        default: {
00122            content[0]=0; break;
00123        }
00124     }
00125     fclose(f);
00126 }
00127 
00128 void badreq(void)
00129 {
00130     printf("HTTP/1.0 400 Bad Request\r\n\r\nBad Request.\r\n");
00131     exit(0);
00132 }
00133 
00134        /* open a TCP/IP socket with host/port
00135         * returns the file descriptor for the socket */
00136 int net_connect(int port)
00137 {
00138     struct sockaddr_in sin;
00139     int soc, vrai;
00140 
00141     if ((soc = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
00142       errorquit("socket() error");
00143     if(setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, &vrai, sizeof(vrai)) == -1)
00144       errorquit("setsockopt() error");
00145 
00146     sin.sin_port=htons(port);
00147     sin.sin_family = AF_INET;
00148     inet_aton(acceptstr,&sin.sin_addr);
00149     if(bind(soc,(struct sockaddr *)&sin, sizeof(sin))==-1)
00150       errorquit("bind() error");
00151     if(listen(soc,10)==-1) errorquit("listen() error");
00152     return soc;
00153 }
00154 
00155 void putfile(char *fname,int soc)
00156 {
00157     int l;
00158     long int siz;
00159     char *p, namebuf[4096], buf[4096];
00160     char cbuf[MAX_LINELEN+1];
00161     struct stat st;
00162     FILE *f;
00163     
00164     if(fname[strlen(fname)-1]=='/')
00165       snprintf(namebuf,sizeof(namebuf),"%sindex.html",fname);
00166     else snprintf(namebuf,sizeof(namebuf),"%s",fname);
00167     statit: if(stat(namebuf,&st)!=0) badreq();
00168     if(S_ISDIR(st.st_mode)) {
00169        snprintf(namebuf,sizeof(namebuf),"%s/index.html",fname);
00170        goto statit;
00171     }
00172     if(!S_ISREG(st.st_mode)) badreq();
00173     if(strncmp(namebuf,"modules/adm/",strlen("modules/adm/"))==0 ||
00174        strncmp(namebuf,"modules/home/",strlen("modules/home/"))==0)
00175       badreq();
00176     for(p=strchr(namebuf,'/'); p!=NULL; p=strchr(p+1,'/')) {
00177        *p=0; snprintf(buf,sizeof(buf),"%s/.htaccess",namebuf); *p='/';
00178        if(stat(cbuf,&st)==0) {
00179            accessfile(cbuf,"r","%s",buf);
00180            if(strstr(cbuf,"deny from all")!=NULL) badreq();
00181        }
00182     }
00183     siz=st.st_size;
00184     printf("HTTP/1.0 200 OK\r\n\
00185 Content-Length: %ld\r\n\r\n",
00186           siz);
00187     f=fopen(namebuf,"r"); if(f==NULL) badreq();
00188     do {
00189        l=fread(buf,1,sizeof(buf),f);
00190        if(l>0 && l<=sizeof(buf)) fwrite(buf,1,l,stdout);
00191     } while(l==sizeof(buf));
00192     fclose(f); fclose(stdin); fclose(stdout); close(soc);
00193     exit(0);
00194 }
00195 
00196 struct sockaddr_in saddr;
00197 
00198 void onereq(int soc)
00199 {
00200     char buf[QUERY_STRING_LIMIT+1], buf2[MAX_LINELEN+1];
00201     int i, k, l;
00202 /*     FILE *fin, *fout; */
00203     struct stat st;
00204     char *query_method, *query_url, *p, *pp, *p2, *parms;
00205 
00206     i=fork(); if(i!=0) {
00207        if(i==-1) errorquit("fork() error");
00208        addpid(i); return;
00209     }
00210     alarm(WIMS_TIMEOUT);
00211     signal(SIGALRM,alarm1);
00212     chdir("public_html");
00213     i=0; do {
00214         l=read(soc, buf+i, 1); i++;
00215     }
00216     while(i<QUERY_STRING_LIMIT && l>0 && (i<4 || memcmp(buf+i-4,"\r\n\r\n",4)!=0));
00217     buf[i]=0;
00218 /*printf("%s\n",buf);*/
00219     dup2(soc,0); /* fin=fdopen(0,"r"); stdin=fin; */
00220     dup2(soc,1); /* fout=fdopen(1,"w"); stdout=fout; */
00221     query_method=find_word_start(buf);
00222     query_url=find_word_end(query_method); if(query_url) *query_url++=0;
00223     query_url=find_word_start(query_url);
00224     p=find_word_end(query_url); if(*p) *p++=0;
00225     parms=strchr(p,'\n'); if(parms) parms++;
00226     if(strcmp(query_method,"GET")!=0 &&
00227        strcmp(query_method,"HEAD")!=0 &&
00228        strcmp(query_method,"POST")!=0) badreq();
00229     if(query_url[0]!='/' || query_url[1]=='/' || strstr(query_url,"..")!=NULL)
00230       badreq();
00231     setenv("REQUEST_URL",query_url,1);
00232     p=strchr(query_url,'?'); if(p!=NULL) *p++=0; else p="";
00233     k=stat(query_url+1,&st);
00234     if(k!=0 && strchr(query_url+1,'/')==NULL)
00235       query_url="/wims.cgi";
00236     i=strlen(query_url);
00237     if(i<4 || strcasecmp(query_url+i-4,".cgi")!=0)
00238       putfile(query_url+1,soc);
00239     if(strchr(query_url+1,'/')!=NULL && strncmp(query_url,"/test/",strlen("/test/"))!=0)
00240       badreq();
00241     setenv("QUERY_STRING",p,1);
00242     setenv("QUERY_URL",query_url,1);
00243     setenv("SCRIPT_NAME",query_url,1);
00244     setenv("REQUEST_METHOD",query_method,1);
00245     setenv("SERVER_SOFTWARE","WIMS",1);
00246     snprintf(buf2,sizeof(buf2),"%d",sport);
00247     setenv("SERVER_PORT",buf2,1);
00248     for(; *parms; parms=p) {
00249        p=strchr(parms,'\n'); 
00250        if(p==NULL) p=parms+strlen(parms);
00251        else {
00252            if(*(p-1)=='\r') *(p-1)=0;
00253            *p++=0;
00254        }
00255        for(pp=parms; isalnum(*pp) || *pp=='-'; pp++);
00256        if(*pp==':') {
00257            *pp++=0; pp=find_word_start(pp);
00258            for(p2=parms; *p2; p2++) {
00259               if(*p2=='-') *p2='_'; else *p2=toupper(*p2);
00260            }
00261            if(strcmp(parms,"CONTENT_LENGTH")==0 ||
00262               strcmp(parms,"CONTENT_TYPE")==0) {
00263               snprintf(buf2,sizeof(buf2),"%s",parms);
00264               setenv(buf2,pp,1);       
00265            }
00266            snprintf(buf2,sizeof(buf2),"HTTP_%s",parms);
00267            setenv(buf2,pp,1);          
00268        }
00269     }
00270     setenv("REMOTE_ADDR",inet_ntoa(saddr.sin_addr),1);
00271     snprintf(buf2,sizeof(buf2),"%u",ntohs(saddr.sin_port));
00272     setenv("REMOTE_PORT",buf2,1);
00273 
00274     snprintf(buf2,sizeof(buf2),".%s",query_url);
00275     execl(buf2,buf2,NULL);
00276     
00277     /* fclose(fout); fclose(fin); */ close(soc); exit(0);
00278 }
00279 
00280 void serve(int soc)
00281 {
00282     int newsoc;
00283     socklen_t slen=sizeof(saddr);
00284     
00285     wait:
00286     alarm(WIMSD_TICK);
00287     newsoc=accept(soc,(struct sockaddr *)&saddr, &slen);
00288     if(newsoc==-1) errorquit("accept() error");
00289     checkpid(); idle=0;
00290     onereq(newsoc); close(newsoc); goto wait;
00291 }
00292 
00293 void parm(void)
00294 {
00295     char *p;
00296     p=getenv("WIMS_HOME");
00297     if(p!=NULL && *p!=0) chdir(p);
00298     p=getenv("WIMSD_PORT");
00299     if(p!=NULL && *p!=0) {
00300        int i;
00301        i=atoi(p);
00302        if(i>0 && i<65536) sport=i;
00303     }
00304     p=getenv("WIMSD_ACCEPT");
00305     if(p!=NULL && strstr(p,"all")!=NULL) acceptstr="0.0.0.0";
00306 }
00307 
00308 int main(int argc, char *argv[])
00309 {
00310     struct stat st;
00311 
00312     signal(SIGALRM,alarm2);
00313     sport=8088; pidcnt=0; idle=0;
00314     parm();
00315     if(stat("public_html/wims.cgi",&st)!=0) {
00316        char buf[1024];
00317        getcwd(buf,sizeof(buf));
00318        fprintf(stderr,"Bad home directory %s: \
00319 wims.cgi not found.\n",buf); exit(1);
00320     }
00321 /*    if(stat("public_html/bin/pari",&st)==0) {
00322        fprintf(stderr,"wimsd must be run as nobody, \
00323 for security reasons!\n\n\
00324 Run bin/wrapuid as root to set things up correctly.\n"); exit(1);
00325     }
00326 */    setreuid(geteuid(),getuid());setregid(getegid(),getgid());
00327     sock=net_connect(sport);
00328     
00329     serve(sock);
00330     close(sock); return 0;
00331 }
00332