Rev 8342 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
10 | reyssat | 1 | /* Copyright (C) 2002-2003 XIAO, Gang of Universite de Nice - Sophia Antipolis |
2 | * |
||
3 | * This program is free software; you can redistribute it and/or modify |
||
4 | * it under the terms of the GNU General Public License as published by |
||
5 | * the Free Software Foundation; either version 2 of the License, or |
||
6 | * (at your option) any later version. |
||
7 | * |
||
8 | * This program is distributed in the hope that it will be useful, |
||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
11 | * GNU General Public License for more details. |
||
12 | * |
||
13 | * You should have received a copy of the GNU General Public License |
||
14 | * along with this program; if not, write to the Free Software |
||
15 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
16 | */ |
||
17 | |||
11047 | bpr | 18 | /* http daemon for WIMS */ |
10 | reyssat | 19 | |
20 | #include <netdb.h> |
||
21 | #include <sys/socket.h> |
||
22 | #include <netinet/in.h> |
||
23 | #include <arpa/inet.h> |
||
8177 | bpr | 24 | #include "../wims.h" |
10 | reyssat | 25 | |
26 | #if !HAVE_SOCKLEN_T |
||
27 | typedef size_t socklen_t; |
||
28 | #endif |
||
29 | |||
30 | #define WIMS_TIMEOUT 200 |
||
31 | #define WIMSD_TICK 10 |
||
32 | #define WIMSD_IDLE 500 |
||
33 | #define PIDLIM 100 |
||
34 | |||
35 | char *acceptstr="127.0.0.1"; |
||
36 | |||
37 | int pidtab[PIDLIM]; |
||
38 | int pidcnt; |
||
39 | |||
40 | int sock; |
||
41 | int sport; |
||
42 | int idle; /* limited to WIMSD_TICK * WIMSD_IDLE */ |
||
43 | |||
44 | void errorquit(char *msg) |
||
45 | { |
||
46 | fprintf(stderr,"%s: %s\n",msg,strerror(errno)); exit(1); |
||
47 | } |
||
48 | |||
49 | void checkpid(void) |
||
50 | { |
||
51 | int i,k; |
||
52 | for(i=0;i<pidcnt;i++) { |
||
53 | waitpid(pidtab[i],&k,WNOHANG); |
||
54 | if(WIFEXITED(k)) { |
||
55 | memmove(pidtab+i,pidtab+i+1,(pidcnt-i-1)*sizeof(pidtab[0])); |
||
56 | pidcnt--; |
||
57 | } |
||
58 | } |
||
59 | } |
||
60 | |||
61 | void addpid(int pid) |
||
62 | { |
||
63 | while(pidcnt>=PIDLIM) { |
||
64 | sleep(1); checkpid(); |
||
65 | } |
||
66 | pidtab[pidcnt++]=pid; |
||
67 | } |
||
68 | |||
69 | void alarm1(int s) |
||
70 | { |
||
71 | fprintf(stderr,"Time out.\n"); close(sock); exit(1); |
||
72 | } |
||
73 | |||
74 | void alarm2(int s) |
||
75 | { |
||
76 | checkpid(); idle++; alarm(WIMSD_TICK); |
||
77 | if(idle>=WIMSD_IDLE) alarm1(0); |
||
78 | } |
||
79 | |||
80 | /* Points to the end of the word */ |
||
81 | char *find_word_end(char *p) |
||
82 | { |
||
83 | int i; |
||
84 | for(i=0;!isspace(*p) && *p!=0 && i<MAX_LINELEN; p++,i++); |
||
85 | return p; |
||
86 | } |
||
87 | |||
88 | /* Strips leading spaces */ |
||
89 | char *find_word_start(char *p) |
||
90 | { |
||
91 | int i; |
||
92 | for(i=0; isspace(*p) && i<MAX_LINELEN; p++,i++); |
||
93 | return p; |
||
94 | } |
||
95 | |||
96 | /* Read/write to a file with variable parms to print filename */ |
||
8342 | bpr | 97 | void wimsdaccessfile(char *content, char *type, char *s,...) |
10 | reyssat | 98 | { |
99 | va_list vp; |
||
100 | char buf[MAX_LINELEN+1]; |
||
101 | FILE *f; |
||
102 | int l; |
||
103 | |||
104 | va_start(vp,s); |
||
105 | vsnprintf(buf,sizeof(buf),s,vp); |
||
106 | va_end(vp); |
||
107 | f=fopen(buf,type); if(f==NULL) { |
||
108 | if(*type=='r') content[0]=0; return; |
||
109 | } |
||
110 | switch(*type) { |
||
111 | case 'a': |
||
112 | case 'w': { |
||
113 | l=strlen(content); fwrite(content,1,l,f); break; |
||
114 | } |
||
115 | case 'r': { |
||
116 | l=fread(content,1,MAX_LINELEN-1,f); |
||
117 | if(l>0 && l<MAX_LINELEN) content[l]=0; |
||
118 | else content[0]=0; |
||
119 | break; |
||
120 | } |
||
121 | default: { |
||
122 | content[0]=0; break; |
||
123 | } |
||
124 | } |
||
125 | fclose(f); |
||
126 | } |
||
127 | |||
128 | void badreq(void) |
||
129 | { |
||
130 | printf("HTTP/1.0 400 Bad Request\r\n\r\nBad Request.\r\n"); |
||
131 | exit(0); |
||
132 | } |
||
133 | |||
134 | /* open a TCP/IP socket with host/port |
||
135 | * returns the file descriptor for the socket */ |
||
136 | int net_connect(int port) |
||
137 | { |
||
138 | struct sockaddr_in sin; |
||
139 | int soc, vrai; |
||
140 | |||
8177 | bpr | 141 | if ((soc = socket(AF_INET, SOCK_STREAM, 0)) == -1) |
10 | reyssat | 142 | errorquit("socket() error"); |
143 | if(setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, &vrai, sizeof(vrai)) == -1) |
||
144 | errorquit("setsockopt() error"); |
||
145 | |||
146 | sin.sin_port=htons(port); |
||
147 | sin.sin_family = AF_INET; |
||
148 | inet_aton(acceptstr,&sin.sin_addr); |
||
149 | if(bind(soc,(struct sockaddr *)&sin, sizeof(sin))==-1) |
||
150 | errorquit("bind() error"); |
||
151 | if(listen(soc,10)==-1) errorquit("listen() error"); |
||
152 | return soc; |
||
153 | } |
||
154 | |||
155 | void putfile(char *fname,int soc) |
||
156 | { |
||
157 | int l; |
||
158 | long int siz; |
||
159 | char *p, namebuf[4096], buf[4096]; |
||
160 | char cbuf[MAX_LINELEN+1]; |
||
161 | struct stat st; |
||
162 | FILE *f; |
||
8177 | bpr | 163 | |
10 | reyssat | 164 | if(fname[strlen(fname)-1]=='/') |
165 | snprintf(namebuf,sizeof(namebuf),"%sindex.html",fname); |
||
166 | else snprintf(namebuf,sizeof(namebuf),"%s",fname); |
||
167 | statit: if(stat(namebuf,&st)!=0) badreq(); |
||
168 | if(S_ISDIR(st.st_mode)) { |
||
169 | snprintf(namebuf,sizeof(namebuf),"%s/index.html",fname); |
||
170 | goto statit; |
||
171 | } |
||
172 | if(!S_ISREG(st.st_mode)) badreq(); |
||
173 | if(strncmp(namebuf,"modules/adm/",strlen("modules/adm/"))==0 || |
||
174 | strncmp(namebuf,"modules/home/",strlen("modules/home/"))==0) |
||
175 | badreq(); |
||
176 | for(p=strchr(namebuf,'/'); p!=NULL; p=strchr(p+1,'/')) { |
||
177 | *p=0; snprintf(buf,sizeof(buf),"%s/.htaccess",namebuf); *p='/'; |
||
178 | if(stat(cbuf,&st)==0) { |
||
8342 | bpr | 179 | wimsdaccessfile(cbuf,"r","%s",buf); |
10 | reyssat | 180 | if(strstr(cbuf,"deny from all")!=NULL) badreq(); |
181 | } |
||
182 | } |
||
183 | siz=st.st_size; |
||
184 | printf("HTTP/1.0 200 OK\r\n\ |
||
185 | Content-Length: %ld\r\n\r\n", |
||
186 | siz); |
||
187 | f=fopen(namebuf,"r"); if(f==NULL) badreq(); |
||
188 | do { |
||
189 | l=fread(buf,1,sizeof(buf),f); |
||
190 | if(l>0 && l<=sizeof(buf)) fwrite(buf,1,l,stdout); |
||
191 | } while(l==sizeof(buf)); |
||
192 | fclose(f); fclose(stdin); fclose(stdout); close(soc); |
||
193 | exit(0); |
||
194 | } |
||
195 | |||
196 | struct sockaddr_in saddr; |
||
197 | |||
198 | void onereq(int soc) |
||
199 | { |
||
200 | char buf[QUERY_STRING_LIMIT+1], buf2[MAX_LINELEN+1]; |
||
201 | int i, k, l; |
||
202 | /* FILE *fin, *fout; */ |
||
203 | struct stat st; |
||
204 | char *query_method, *query_url, *p, *pp, *p2, *parms; |
||
205 | |||
206 | i=fork(); if(i!=0) { |
||
207 | if(i==-1) errorquit("fork() error"); |
||
208 | addpid(i); return; |
||
209 | } |
||
210 | alarm(WIMS_TIMEOUT); |
||
211 | signal(SIGALRM,alarm1); |
||
3841 | kbelabas | 212 | (void)chdir("public_html"); |
10 | reyssat | 213 | i=0; do { |
214 | l=read(soc, buf+i, 1); i++; |
||
215 | } |
||
216 | while(i<QUERY_STRING_LIMIT && l>0 && (i<4 || memcmp(buf+i-4,"\r\n\r\n",4)!=0)); |
||
217 | buf[i]=0; |
||
218 | /*printf("%s\n",buf);*/ |
||
219 | dup2(soc,0); /* fin=fdopen(0,"r"); stdin=fin; */ |
||
220 | dup2(soc,1); /* fout=fdopen(1,"w"); stdout=fout; */ |
||
221 | query_method=find_word_start(buf); |
||
222 | query_url=find_word_end(query_method); if(query_url) *query_url++=0; |
||
223 | query_url=find_word_start(query_url); |
||
224 | p=find_word_end(query_url); if(*p) *p++=0; |
||
225 | parms=strchr(p,'\n'); if(parms) parms++; |
||
226 | if(strcmp(query_method,"GET")!=0 && |
||
227 | strcmp(query_method,"HEAD")!=0 && |
||
228 | strcmp(query_method,"POST")!=0) badreq(); |
||
229 | if(query_url[0]!='/' || query_url[1]=='/' || strstr(query_url,"..")!=NULL) |
||
230 | badreq(); |
||
231 | setenv("REQUEST_URL",query_url,1); |
||
232 | p=strchr(query_url,'?'); if(p!=NULL) *p++=0; else p=""; |
||
233 | k=stat(query_url+1,&st); |
||
234 | if(k!=0 && strchr(query_url+1,'/')==NULL) |
||
235 | query_url="/wims.cgi"; |
||
236 | i=strlen(query_url); |
||
237 | if(i<4 || strcasecmp(query_url+i-4,".cgi")!=0) |
||
238 | putfile(query_url+1,soc); |
||
239 | if(strchr(query_url+1,'/')!=NULL && strncmp(query_url,"/test/",strlen("/test/"))!=0) |
||
240 | badreq(); |
||
241 | setenv("QUERY_STRING",p,1); |
||
242 | setenv("QUERY_URL",query_url,1); |
||
243 | setenv("SCRIPT_NAME",query_url,1); |
||
244 | setenv("REQUEST_METHOD",query_method,1); |
||
245 | setenv("SERVER_SOFTWARE","WIMS",1); |
||
246 | snprintf(buf2,sizeof(buf2),"%d",sport); |
||
247 | setenv("SERVER_PORT",buf2,1); |
||
248 | for(; *parms; parms=p) { |
||
8177 | bpr | 249 | p=strchr(parms,'\n'); |
10 | reyssat | 250 | if(p==NULL) p=parms+strlen(parms); |
251 | else { |
||
252 | if(*(p-1)=='\r') *(p-1)=0; |
||
253 | *p++=0; |
||
254 | } |
||
255 | for(pp=parms; isalnum(*pp) || *pp=='-'; pp++); |
||
256 | if(*pp==':') { |
||
257 | *pp++=0; pp=find_word_start(pp); |
||
258 | for(p2=parms; *p2; p2++) { |
||
259 | if(*p2=='-') *p2='_'; else *p2=toupper(*p2); |
||
260 | } |
||
261 | if(strcmp(parms,"CONTENT_LENGTH")==0 || |
||
262 | strcmp(parms,"CONTENT_TYPE")==0) { |
||
263 | snprintf(buf2,sizeof(buf2),"%s",parms); |
||
8177 | bpr | 264 | setenv(buf2,pp,1); |
10 | reyssat | 265 | } |
266 | snprintf(buf2,sizeof(buf2),"HTTP_%s",parms); |
||
8177 | bpr | 267 | setenv(buf2,pp,1); |
10 | reyssat | 268 | } |
269 | } |
||
270 | setenv("REMOTE_ADDR",inet_ntoa(saddr.sin_addr),1); |
||
271 | snprintf(buf2,sizeof(buf2),"%u",ntohs(saddr.sin_port)); |
||
272 | setenv("REMOTE_PORT",buf2,1); |
||
273 | |||
274 | snprintf(buf2,sizeof(buf2),".%s",query_url); |
||
275 | execl(buf2,buf2,NULL); |
||
8177 | bpr | 276 | |
10 | reyssat | 277 | /* fclose(fout); fclose(fin); */ close(soc); exit(0); |
278 | } |
||
279 | |||
280 | void serve(int soc) |
||
281 | { |
||
282 | int newsoc; |
||
283 | socklen_t slen=sizeof(saddr); |
||
8177 | bpr | 284 | |
10 | reyssat | 285 | wait: |
286 | alarm(WIMSD_TICK); |
||
287 | newsoc=accept(soc,(struct sockaddr *)&saddr, &slen); |
||
288 | if(newsoc==-1) errorquit("accept() error"); |
||
289 | checkpid(); idle=0; |
||
290 | onereq(newsoc); close(newsoc); goto wait; |
||
291 | } |
||
292 | |||
293 | void parm(void) |
||
294 | { |
||
295 | char *p; |
||
296 | p=getenv("WIMS_HOME"); |
||
3841 | kbelabas | 297 | if(p!=NULL && *p!=0) (void)chdir(p); |
10 | reyssat | 298 | p=getenv("WIMSD_PORT"); |
299 | if(p!=NULL && *p!=0) { |
||
300 | int i; |
||
301 | i=atoi(p); |
||
302 | if(i>0 && i<65536) sport=i; |
||
303 | } |
||
304 | p=getenv("WIMSD_ACCEPT"); |
||
305 | if(p!=NULL && strstr(p,"all")!=NULL) acceptstr="0.0.0.0"; |
||
306 | } |
||
307 | |||
308 | int main(int argc, char *argv[]) |
||
309 | { |
||
310 | struct stat st; |
||
311 | |||
312 | signal(SIGALRM,alarm2); |
||
313 | sport=8088; pidcnt=0; idle=0; |
||
314 | parm(); |
||
315 | if(stat("public_html/wims.cgi",&st)!=0) { |
||
316 | char buf[1024]; |
||
3841 | kbelabas | 317 | (void)getcwd(buf,sizeof(buf)); |
10 | reyssat | 318 | fprintf(stderr,"Bad home directory %s: \ |
319 | wims.cgi not found.\n",buf); exit(1); |
||
320 | } |
||
321 | /* if(stat("public_html/bin/pari",&st)==0) { |
||
322 | fprintf(stderr,"wimsd must be run as nobody, \ |
||
323 | for security reasons!\n\n\ |
||
324 | Run bin/wrapuid as root to set things up correctly.\n"); exit(1); |
||
325 | } |
||
326 | */ setreuid(geteuid(),getuid());setregid(getegid(),getgid()); |
||
327 | sock=net_connect(sport); |
||
8177 | bpr | 328 | |
10 | reyssat | 329 | serve(sock); |
330 | close(sock); return 0; |
||
331 | } |
||
332 |