1 #define _POSIX_C_SOURCE 200809
2 #define _ATFILE_SOURCE
3 #define _DEFAULT_SOURCE
4
5 #if !defined(__FreeBSD__) && !defined(__DragonFly__)
6 #define _XOPEN_SOURCE 500
7 #endif
8
9 #include "gatling.h"
10
11 #include "buffer.h"
12 #include "fmt.h"
13 #include "ip6.h"
14 #include "mmap.h"
15 #include "str.h"
16 #include "textcode.h"
17 #include "scan.h"
18 #include "socket.h"
19 #include "case.h"
20 #include "ip4.h"
21
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <dirent.h>
27 #ifdef __dietlibc__
28 #include <md5.h>
29 #elif defined(USE_POLARSSL)
30 #include <polarssl/md5.h>
31 #define MD5_CTX md5_context
32 #define MD5Init md5_starts
33 #define MD5Update md5_update
34 #define MD5Final(out,ctx) md5_finish(ctx,out)
35 #else
36 #include <openssl/md5.h>
37 #define MD5Init MD5_Init
38 #define MD5Update MD5_Update
39 #define MD5Final MD5_Final
40 #endif
41 #include <errno.h>
42 #include <string.h>
43 #include <time.h>
44 #include <fcntl.h>
45 #include <sys/mman.h>
46 #include <ctype.h>
47 #include <sys/socket.h>
48 #include <limits.h>
49
50 #include "havealloca.h"
51
52 #include "dirfd.h"
53
54 struct http_data* kludge;
55
56 char* defaultindex;
57
58 static const char* methods[]={
59 "GET", "HEAD", "POST",
60 #ifdef SUPPORT_PUT
61 "PUT",
62 #endif
63 #ifdef SUPPORT_DAV
64 "PROPFIND", "OPTIONS" // , "LOCK", "UNLOCK"
65 #endif
66 };
67 enum Method {
68 GET, HEAD, POST,
69 #ifdef SUPPORT_PUT
70 PUT,
71 #endif
72 #ifdef SUPPORT_DAV
73 PROPFIND, OPTIONS, // LOCK, UNLOCK,
74 #endif
75 };
76
77 MD5_CTX md5_ctx;
78
http_header_blob(char * b,long l,char * h)79 char* http_header_blob(char* b,long l,char* h) {
80 long i;
81 long sl=str_len(h);
82 for (i=0; i+sl+2<l; ++i)
83 if (b[i]=='\n' && b[i+sl+1]==':' && case_equalb(b+i+1,sl,h)) {
84 b+=i+sl+2;
85 while (*b==' ' || *b=='\t') ++b;
86 return b;
87 }
88 return 0;
89 }
90
http_header(struct http_data * r,char * h)91 char* http_header(struct http_data* r,char* h) {
92 return http_header_blob(array_start(&r->r),array_bytes(&r->r),h);
93 }
94
issafe(unsigned char c)95 static inline int issafe(unsigned char c) {
96 return (c!='"' && c!='%' && (c>=' ' && c<0x7f) && c!='+' && c!=':' && c!='#');
97 }
98
fmt_urlencoded(char * dest,const char * src,size_t len)99 size_t fmt_urlencoded(char* dest,const char* src,size_t len) {
100 register const unsigned char* s=(const unsigned char*) src;
101 size_t written=0,i;
102 for (i=0; i<len; ++i) {
103 if (!issafe(s[i])) {
104 if (dest) {
105 dest[written]='%';
106 dest[written+1]=fmt_tohex(s[i]>>4);
107 dest[written+2]=fmt_tohex(s[i]&15);
108 }
109 written+=3;
110 } else {
111 if (dest) dest[written]=s[i];
112 ++written;
113 }
114 }
115 return written;
116 }
117
catencoded(array * a,const char * s)118 void catencoded(array* a,const char* s) {
119 unsigned int len=str_len(s);
120 if (len) {
121 char* buf=alloca(fmt_urlencoded(0,s,len));
122 array_catb(a,buf,fmt_urlencoded(buf,s,len));
123 }
124 }
125
cathtml(array * a,const char * s)126 void cathtml(array* a,const char* s) {
127 unsigned int len=str_len(s);
128 if (len) {
129 char* buf=alloca(fmt_html(0,s,len));
130 array_catb(a,buf,fmt_html(buf,s,len));
131 }
132 }
133
catxmlencoded(array * a,const char * s)134 void catxmlencoded(array* a,const char* s) {
135 unsigned int len=str_len(s);
136 if (len) {
137 char* buf=alloca(fmt_xml(0,s,len));
138 array_catb(a,buf,fmt_xml(buf,s,len));
139 }
140 }
141
cathtmlutf8(array * a,const char * s)142 void cathtmlutf8(array* a,const char* s) {
143 /* The purpose of this function is to convert a file name into UTF-8
144 * and escape HTML-relevant characters such as '<' and '&'. Chars that
145 * are not valid UTF-8 are assumed to be latin1 and converted */
146 size_t i,l,r;
147 char* buf;
148 r=0;
149 /* This will be a short string, a file name, so assuming all chars are
150 * '&', the max expansion is '&', i.e. *5. */
151 l=strlen(s);
152 buf=alloca(l*5);
153 for (i=0; i<l; ++i) {
154 if (s[i]&0x80) {
155 size_t n=scan_utf8(s+i,l-i,NULL);
156 if (n==0) {
157 r+=fmt_utf8(buf+r,(unsigned char)(s[i]));
158 } else {
159 memcpy(buf+r,s+i,n);
160 i+=n-1;
161 r+=n;
162 }
163 } else {
164 const char* x=0;
165 size_t n;
166 switch (s[i]) {
167 case '&': x="&"; n=5; break;
168 case '<': x="<"; n=4; break;
169 case '>': x=">"; n=4; break;
170 case '\n': x="<br>"; n=4; break;
171 }
172 if (x) {
173 memcpy(buf+r,x,n);
174 r+=n;
175 } else
176 buf[r++]=s[i];
177 }
178 }
179 array_catb(a,buf,r);
180 }
181
http_dirlisting(struct http_data * h,DIR * D,const char * path,const char * arg,enum Method method,int dirfd)182 static int http_dirlisting(struct http_data* h,DIR* D,const char* path,const char* arg,enum Method method,int dirfd) {
183 long i,o,n;
184 struct dirent* d;
185 int (*sortfun)(de*,de*);
186 #ifdef SUPPORT_DAV
187 char* baseurl;
188 char* depth;
189 #endif
190 array a,b,c;
191 de* ab;
192 byte_zero(&a,sizeof(a));
193 byte_zero(&b,sizeof(b));
194 byte_zero(&c,sizeof(c));
195 o=n=0;
196 #ifdef SUPPORT_DAV
197 depth=http_header(h,"Depth");
198 if (method==PROPFIND) {
199 de* x=array_allocate(&a,sizeof(de),n);
200 if (x) {
201 x->name=0;
202 fstatat(dirfd,".",&x->ss,AT_SYMLINK_NOFOLLOW); /* if this fails, we just serve bogus data */
203 array_cats0(&b,".");
204 ++n;
205 o=2;
206 }
207 }
208 if (method!=PROPFIND || (depth && *depth=='1'))
209 #endif
210 while ((d=readdir(D))) {
211 #ifdef SUPPORT_DAV
212 if (d->d_name[0]=='.' && d->d_name[1]==0) continue;
213 #endif
214 de* x=array_allocate(&a,sizeof(de),n);
215 if (!x) break;
216 x->name=o;
217 if (fstatat(dirfd,d->d_name,&x->ss,AT_SYMLINK_NOFOLLOW)==-1) continue;
218 if (S_ISLNK(x->ss.st_mode)) {
219 struct stat tmp;
220 if (fstatat(dirfd,d->d_name,&tmp,0)==0)
221 if (S_ISDIR(tmp.st_mode))
222 x->todir=1;
223 }
224 array_cats0(&b,d->d_name);
225 o+=str_len(d->d_name)+1;
226 ++n;
227 }
228 closedir(D);
229 if (array_failed(&a) || array_failed(&b)) {
230 array_reset(&a);
231 array_reset(&b);
232 return 0;
233 }
234 base=array_start(&b);
235 sortfun=sort_name_a;
236 if (
237 #ifdef SUPPORT_DAV
238 method!=PROPFIND &&
239 #endif
240 arg) {
241 if (str_equal(arg,"N=D")) sortfun=sort_name_d;
242 else if (str_equal(arg,"N=A")) sortfun=sort_name_a;
243 else if (str_equal(arg,"M=A")) sortfun=sort_mtime_a;
244 else if (str_equal(arg,"M=D")) sortfun=sort_mtime_d;
245 else if (str_equal(arg,"S=A")) sortfun=sort_size_a;
246 else if (str_equal(arg,"S=D")) sortfun=sort_size_d;
247 }
248 qsort(array_start(&a),n,sizeof(de),(int(*)(const void*,const void*))sortfun);
249 #ifdef SUPPORT_DAV
250 if (method==PROPFIND) {
251 char* host=http_header(h,"Host");
252 size_t i,j;
253 if (host) {
254 for (i=0; isalnum(host[i]) || host[i]=='-' || host[i]=='.' || host[i]==':'; ++i) ;
255 } else
256 i=FMT_IP6+sizeof(":65535");
257 baseurl=alloca(i+sizeof("https://")+strlen(path)+2);
258 #ifdef SUPPORT_HTTPS
259 if (h->t==HTTPSREQUEST)
260 j=fmt_str(baseurl,"https://");
261 else
262 #endif
263 j=fmt_str(baseurl,"http://");
264 if (host)
265 j+=fmt_copybytes(baseurl+j,host,i);
266 else {
267 j+=fmt_ip6c(baseurl+j,h->myip);
268 baseurl[j++]=':';
269 j+=fmt_ulong(baseurl+j,h->myport);
270 }
271 j+=fmt_str(baseurl+j,path);
272 baseurl[j]=0;
273 array_cats(&c,"<?xml version=\"1.0\" encoding=\"utf-8\"?><D:multistatus xmlns:D=\"DAV:\">");
274 } else {
275 #endif
276 array_cats(&c,"<title>Index of ");
277 cathtml(&c,path);
278 array_cats(&c,"</title>\n<h1>Index of ");
279 cathtml(&c,path);
280 {
281 char* tmp=http_header(h,"User-Agent");
282 /* don't give wget the column sorting interface so wget -m does not
283 * mirror it needlessly */
284 if (tmp && byte_equal(tmp,5,"Wget/"))
285 array_cats(&c,"</h1>\n<table><tr><th>Name<th>Last Modified<th>Size\n");
286 else {
287 array_cats(&c,"</h1>\n<table><tr><th><a href=\"?N=");
288 array_cats(&c,sortfun==sort_name_a?"D":"A");
289 array_cats(&c,"\">Name</a><th><a href=\"?M=");
290 array_cats(&c,sortfun==sort_mtime_a?"D":"A");
291 array_cats(&c,"\">Last Modified</a><th><a href=\"?S=");
292 array_cats(&c,sortfun==sort_size_a?"D":"A");
293 array_cats(&c,"\">Size</a>\n");
294 }
295 }
296 #ifdef SUPPORT_DAV
297 }
298 #endif
299
300 ab=array_start(&a);
301 for (i=0; i<n; ++i) {
302 char* name=base+ab[i].name;
303 char buf[FMT_ULONG];
304 int j;
305 struct tm* x=localtime(&ab[i].ss.st_mtime);
306 #ifdef SUPPORT_DAV
307 if (method==PROPFIND) {
308 /* skip dotfiles but not "." */
309 if (name[0]=='.' && name[1]) continue;
310 } else
311 #endif
312 if (name[0]=='.') {
313 if (name[1]==0) continue; /* skip "." */
314 if (name[1]!='.' || name[2]!=0) /* skip dot-files */
315 continue;
316 }
317 if (!(ab[i].ss.st_mode & S_IROTH)) continue; /* skip files that are not world readable */
318 if (name[0]==':') name[0]='.';
319
320 #ifdef SUPPORT_DAV
321 if (method==PROPFIND) {
322 array_cats(&c,"<D:response><D:href>");
323 catxmlencoded(&c,baseurl);
324 if (name[0]!='.' || name[1])
325 catxmlencoded(&c,name);
326 array_cats(&c,"</D:href><D:propstat><D:status>HTTP/1.1 200 OK</D:status><D:prop>");
327 if (S_ISDIR(ab[i].ss.st_mode))
328 array_cats(&c,"<D:getcontenttype/>");
329 else {
330 array_cats(&c,"<D:getcontenttype>");
331 array_cats(&c,mimetype(name,-1));
332 array_cats(&c,"</D:getcontenttype>");
333 }
334 array_cats(&c,"<D:getlastmodified>");
335 buf[fmt_httpdate(buf,ab[i].ss.st_mtime)]=0;
336 array_cats(&c,buf);
337 array_cats(&c,"</D:getlastmodified><D:lockdiscovery/><D:ishidden>0</D:ishidden><D:supportedlock><D:lockentry><D:lockscope><D:exclusive/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry><D:lockentry><D:lockscope><D:shared/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry></D:supportedlock>");
338 if (S_ISDIR(ab[i].ss.st_mode))
339 array_cats(&c,"<D:getetag/><D:displayname>");
340 else {
341 array_cats(&c,"<D:getetag>\"");
342 buf[j=fmt_ulong(buf,ab[i].ss.st_dev)]='.';
343 array_catb(&c,buf,j+1);
344 j=fmt_ulong(buf,ab[i].ss.st_ino);
345 array_catb(&c,buf,j);
346 array_cats(&c,"\"</D:getetag><D:displayname>");
347 }
348 catxmlencoded(&c,name);
349 array_cats(&c,"</D:displayname><D:getcontentlanguage/><D:getcontentlength>");
350 buf[fmt_ulonglong(buf,ab[i].ss.st_size)]=0;
351 array_cats(&c,buf);
352 array_cats(&c,S_ISDIR(ab[i].ss.st_mode) ?
353 "</D:getcontentlength><D:iscollection>1</D:iscollection><D:creationdate>" :
354 "</D:getcontentlength><D:iscollection>0</D:iscollection><D:creationdate>" );
355 buf[fmt_iso8601(buf,ab[i].ss.st_ctime)]=0;
356 array_cats(&c,buf);
357 array_cats(&c,S_ISDIR(ab[i].ss.st_mode) ?
358 "</D:creationdate><D:resourcetype><D:collection/></D:resourcetype></D:prop></D:propstat></D:response>" :
359 "</D:creationdate><D:resourcetype/></D:prop></D:propstat></D:response>" );
360 } else {
361 #endif
362 array_cats(&c,"<tr><td><a href=\"");
363 catencoded(&c,base+ab[i].name);
364 if (S_ISDIR(ab[i].ss.st_mode) || ab[i].todir) array_cats(&c,"/");
365 array_cats(&c,"\">");
366 cathtmlutf8(&c,base+ab[i].name);
367 #ifndef __MINGW32__
368 if (S_ISLNK(ab[i].ss.st_mode)) array_cats(&c,"@"); else
369 #endif
370 if (S_ISDIR(ab[i].ss.st_mode)) array_cats(&c,"/");
371 array_cats(&c,"</a><td>");
372
373 j=fmt_2digits(buf,x->tm_mday);
374 j+=fmt_str(buf+j,"-");
375 byte_copy(buf+j,3,months+3*x->tm_mon); j+=3;
376 j+=fmt_str(buf+j,"-");
377 j+=fmt_2digits(buf+j,(x->tm_year+1900)/100);
378 j+=fmt_2digits(buf+j,(x->tm_year+1900)%100);
379 j+=fmt_str(buf+j," ");
380 j+=fmt_2digits(buf+j,x->tm_hour);
381 j+=fmt_str(buf+j,":");
382 j+=fmt_2digits(buf+j,x->tm_min);
383
384 array_catb(&c,buf,j);
385 array_cats(&c,"<td align=right>");
386 array_catb(&c,buf,fmt_humank(buf,ab[i].ss.st_size));
387 #ifdef SUPPORT_DAV
388 }
389 #endif
390 }
391 #ifdef SUPPORT_DAV
392 if (method==PROPFIND)
393 array_cats(&c,"</D:multistatus>");
394 else
395 #endif
396 array_cats(&c,"</table>");
397
398 array_reset(&a);
399 array_reset(&b);
400 if (array_failed(&c)) return 0;
401 h->bodybuf=array_start(&c);
402 h->blen=array_bytes(&c);
403 return 1;
404 }
405
buffer_putlogstr(buffer * b,const char * s)406 int buffer_putlogstr(buffer* b,const char* s) {
407 unsigned long l;
408 char* x;
409 for (l=0; s[l] && s[l]!='\r' && s[l]!='\n'; ++l) ;
410 if (!l) return 0;
411 x=alloca(l);
412 return buffer_put(b,x,fmt_foldwhitespace(x,s,l));
413 }
414
415 #ifdef SUPPORT_PROXY
add_proxy(const char * c)416 int add_proxy(const char* c) {
417 struct cgi_proxy* x=malloc(sizeof(struct cgi_proxy));
418 int i;
419 if (!x) return -1;
420 byte_zero(x,sizeof(struct cgi_proxy));
421 if (c[1]=='/') {
422 if (c[0]=='F')
423 x->proxyproto=FASTCGI;
424 else if (c[0]=='S')
425 x->proxyproto=SCGI;
426 else if (c[0]=='H')
427 x->proxyproto=HTTP;
428 else
429 goto nixgut;
430 c+=2;
431 }
432 if (*c=='|') {
433 const char* d;
434 ++c;
435 d=strchr(c,'|');
436 if (!d) goto nixgut;
437 if (d-c>sizeof(x->uds.sun_path)) goto nixgut;
438 x->port=-1;
439 x->uds.sun_family=AF_UNIX;
440 memcpy(x->uds.sun_path,c,d-c);
441 c=d+1;
442 } else {
443 uint16 tmp;
444 i=scan_ip6if(c,x->ip,&x->scope_id);
445 if (c[i]!='/') { nixgut: free(x); return -1; }
446 c+=i+1;
447 i=scan_ushort(c,&tmp);
448 x->port=tmp;
449 if (c[i]!='/') goto nixgut;
450 c+=i+1;
451 }
452 if (regcomp(&x->r,c,REG_EXTENDED)) goto nixgut;
453 if (!last)
454 cgis=x;
455 else
456 last->next=x;
457 last=x;
458 return 0;
459 }
460
fmt_strblob(char * dst,const char * str,const char * blob,size_t n)461 static size_t fmt_strblob(char* dst,const char* str,const char* blob,size_t n) {
462 size_t x;
463 if (!dst) return strlen(str)+n+1;
464 x=fmt_str(dst,str);
465 memcpy(dst+x,blob,n);
466 x+=n;
467 dst[x]='\n';
468 return x+1;
469 }
470
fmt_cgivars(char * dst,struct http_data * h,const char * uri,size_t urilen,const char * vhostdir,size_t * headers)471 static size_t fmt_cgivars(char* dst,struct http_data* h,const char* uri,size_t urilen,const char* vhostdir,size_t* headers) {
472 /* input:
473 * dst: destination buffer, may be NULL
474 * h: http context, used to get to HTTP request
475 * uri: pointer to decoded URI, truncated at '?', after leading '/'
476 * urilen: last char in regex match
477 * uri="script.php/path_info"
478 * ^ uri+urilen
479 * vhostdir: virtual hosting dir, e.g. "www.fefe.de:80" or "default"
480 * needs global: char serverroot[]
481 * output:
482 * returns number of bytes written to dst
483 * if dst is NULL, returns number of buffer size needed
484 * writes environment entries to dst, separated by \n
485 * writes count of headers written to *headers if non-NULL
486 */
487 char remoteaddr[IP6_FMT];
488 char myaddr[IP6_FMT];
489 char tmp[FMT_ULONG];
490 size_t n,s,hc;
491 s=0;
492
493 while (urilen && uri[0]=='/') { ++uri; --urilen; }
494
495 remoteaddr[fmt_ip6c(remoteaddr,h->peerip)]=0;
496 myaddr[fmt_ip6c(myaddr,h->myip)]=0;
497
498 {
499 char* x=http_header(h,"Content-Length");
500 if (x) {
501 size_t j=str_chr(x,'\n'); if (j && x[j-1]=='\r') { --j; }
502 n=fmt_strblob(dst,"CONTENT_LENGTH=",x,j);
503 } else {
504 n=fmt_str(dst,"CONTENT_LENGTH=0\n");
505 }
506 s+=n; if (dst) dst+=n;
507 }
508
509 n=fmt_strm(dst,"SERVER_SOFTWARE=gatling\n"); s+=n; if (dst) dst+=n;
510 {
511 char* x=http_header(h,"Host");
512 if (x) {
513 size_t j;
514 for (j=0; x[j]!=':' && x[j]!='\r' && x[j]!='\n'; ++j) ;
515 n=fmt_strblob(dst,"SERVER_NAME=",x,j);
516 } else
517 n=fmt_strm(dst,"SERVER_NAME=",remoteaddr,"\n");
518 s+=n; if (dst) dst+=n;
519 }
520 n=fmt_strm(dst,"SERVER_ADDR=",myaddr,"\n"); s+=n; if (dst) dst+=n;
521 tmp[fmt_ulong(tmp,h->myport)]=0;
522 n=fmt_strm(dst,"SERVER_PORT=",tmp,"\n"); s+=n; if (dst) dst+=n;
523 n=fmt_strm(dst,"REMOTE_ADDR=",remoteaddr,"\n"); s+=n; if (dst) dst+=n;
524 tmp[fmt_ulong(tmp,h->peerport)]=0;
525 n=fmt_strm(dst,"REMOTE_PORT=",tmp,"\n"); s+=n; if (dst) dst+=n;
526 n=fmt_strm(dst,"DOCUMENT_ROOT=",serverroot,"/",vhostdir,"\n"); s+=n; if (dst) dst+=n;
527 n=fmt_strm(dst,"GATEWAY_INTERFACE=CGI/1.1\nSERVER_PROTOCOL=HTTP/1.1\n"); s+=n; if (dst) dst+=n;
528 {
529 char* x=array_start(&h->r);
530 size_t z,y=str_chr(x,' ');
531 n=fmt_strblob(dst,"REQUEST_METHOD=",x,y); s+=n; if (dst) dst+=n;
532 x+=y+1;
533 y=str_chr(x,' ');
534 /* REQUEST_URI is not actually part of the CGI 1.1 spec (RFC3875) */
535 n=fmt_strblob(dst,"REQUEST_URI=",x,y); s+=n; if (dst) dst+=n;
536 z=byte_chr(x,y,'?')+1;
537 if (z<y) {
538 n=fmt_strblob(dst,"QUERY_STRING=",x+z,y-z); s+=n; if (dst) dst+=n;
539 }
540
541 n=fmt_strblob(dst,"SCRIPT_NAME=/",uri,urilen); s+=n; if (dst) dst+=n;
542 n=fmt_strm(dst,"SCRIPT_FILENAME=",serverroot,"/",vhostdir); s+=n; if (dst) dst+=n;
543 n=fmt_strblob(dst,"/",uri,urilen); s+=n; if (dst) dst+=n;
544
545 if (uri[urilen]=='/') { /* we have a PATH_INFO */
546 /* the situation is like this:
547 uri="script.cgi/pathinfo"
548 ^urilen
549 */
550
551 n=fmt_strm(dst,"PATH_INFO=",uri+urilen,"\n"); s+=n; if (dst) dst+=n;
552
553 /* PATH_TRANSLATED is "$PWD$PATH_INFO" */
554 while (uri[urilen]=='/') ++urilen;
555 n=fmt_strm(dst,"PATH_TRANSLATED=",serverroot,"/",vhostdir,"/",uri+urilen,"\n"); s+=n; if (dst) dst+=n;
556 }
557
558 }
559
560 hc=17;
561 if (h->proxyproto==SCGI) {
562 n=fmt_strm(dst,"SCGI=1\n"); s+=n; if (dst) dst+=n;
563 ++hc;
564 }
565
566 #ifdef SUPPORT_HTTPS
567 if (h->t == HTTPSPOST) {
568 n=fmt_strm(dst,"HTTPS=1\n"); s+=n; if (dst) dst+=n;
569 ++hc;
570 }
571 #endif
572
573 /* now translate all header lines into HTTP_* */
574 /* for example Accept: -> HTTP_ACCEPT= */
575 {
576 char* x=array_start(&h->r);
577 char* max=x+array_bytes(&h->r);
578 for (; x<max && *x!='\n'; ++x) ;
579 while (x) {
580 char* olddst=dst;
581 ++x;
582 if (*x<=' ') break;
583 if (!case_starts(x,"Content-Length:") && !case_starts(x,"Content-Type:")) {
584 n=fmt_strm(dst,"HTTP_"); s+=n; if (dst) dst+=n;
585 }
586 while (*x!=':') {
587 char c=*x;
588 if (c>='a' && c<='z') c-='a'-'A'; /* toupper */
589 if (c=='-') c='_'; else
590 if (c<'A' || c>'Z') {
591 dst=olddst;
592 goto skipheader;
593 }
594 if (dst) { *dst=c; ++dst; } ++s;
595 ++x;
596 }
597 if (dst) { *dst='='; ++dst; } ++s;
598 ++x; while (*x==' ') ++x;
599 {
600 char* start=x;
601 while (*x && *x!='\r' && *x!='\n') ++x;
602 n=x-start;
603 if (dst) { byte_copy(dst,n,start); dst+=n+1; dst[-1]='\n'; } s+=n+1;
604 }
605 ++hc;
606 skipheader:
607 x=strchr(x,'\n');
608 }
609 if (headers) *headers=hc;
610 }
611 return s;
612 }
613
proxy_connection(int sockfd,char * c,const char * dir,struct http_data * ctx_for_sockfd,int isexec,const char * args,struct dircacheentry * de,int dirfd)614 static int proxy_connection(int sockfd,char* c,const char* dir,struct http_data* ctx_for_sockfd,int isexec,const char* args,struct dircacheentry* de,int dirfd) {
615 /* c is the filename
616 * dir is the virtual hosting dir ("www.fefe.de:80")
617 * the current working directory is inside the virtual hosting dir */
618 struct cgi_proxy* x=cgis;
619 struct stat ss;
620 regmatch_t matches;
621
622 /* if isexec is set, we already found that .proxy is there */
623 if (de && de->proxy==-1) return -3;
624 if (de && de->proxy!=1) {
625 if (!isexec && fstatat(de->fd,".proxy",&ss,0)==-1) {
626 if (de) de->proxy=-1;
627 return -3;
628 }
629 }
630 if (de) de->proxy=1;
631 while (x) {
632 if (x->file_executable && (!isexec || x->port)) {
633 x=x->next;
634 continue;
635 }
636
637 matches.rm_so=matches.rm_eo=0;
638 if (x->file_executable || regexec(&x->r,c,1,&matches,0)==0) {
639 /* if the port is zero, then use local execution proxy mode instead */
640 int fd_to_gateway;
641 struct http_data* ctx_for_gatewayfd;
642 char* d=c;
643 while (*d=='/') ++d;
644
645 ctx_for_sockfd->proxyproto=x->proxyproto;
646
647 if (!(ctx_for_gatewayfd=(struct http_data*)malloc(sizeof(struct http_data)))) return -1;
648 byte_zero(ctx_for_gatewayfd,sizeof(struct http_data));
649 ctx_for_gatewayfd->filefd=-1;
650
651 if (!x->file_executable) {
652
653 #if 0
654 printf("%u %u\n",matches.rm_so,matches.rm_eo);
655 printf("got data \"%s\"\n",c+matches.rm_eo);
656 #endif
657
658 /* for SCGI and FASTCGI we expect the file to exist (if not empty) */
659 if (*d!=0 && (x->proxyproto == SCGI || x->proxyproto == FASTCGI)) {
660 struct stat ss;
661 /* does the file actually exist? */
662 if (fstatat(dirfd,d,&ss,AT_SYMLINK_NOFOLLOW)) {
663 if (errno==ENOTDIR) { /* we have PATH_INFO */
664 char save=c[matches.rm_eo];
665 int r;
666 c[matches.rm_eo]=0;
667 r=fstatat(dirfd,d,&ss,AT_SYMLINK_NOFOLLOW);
668 c[matches.rm_eo]=save;
669 if (r) goto freeandfail;
670 } else {
671 freeandfail:
672 free(ctx_for_gatewayfd);
673 return -1;
674 }
675 }
676 }
677 ctx_for_gatewayfd->proxyproto=x->proxyproto;
678 if (x->proxyproto == SCGI) {
679 size_t l=fmt_cgivars(0,ctx_for_sockfd,c,matches.rm_eo,dir,0);
680 char* x,* y;
681 /* array_allocate gets the index of the last element you want
682 * to access, not the number of bytes; so +1, not +2 */
683 if (!array_allocate(&ctx_for_gatewayfd->r,1,l+fmt_ulong(0,l)+1))
684 goto freeandfail;
685 x=array_start(&ctx_for_gatewayfd->r);
686 x+=fmt_ulong(x,l);
687 *x++=':';
688 y=x;
689 x+=fmt_cgivars(x,ctx_for_sockfd,c,matches.rm_eo,dir,0);
690
691 /* fmt_cgivars uses "FOO=bar\n" but we want "FOO\000bar\000" */
692 while (y<x) {
693 if (*y=='=') {
694 *y=0;
695 while (y<x) {
696 if (*y=='\n') { *y=0; break; }
697 ++y;
698 }
699 }
700 ++y;
701 }
702
703 *x=',';
704 } else if (x->proxyproto == FASTCGI) {
705 size_t hc;
706 size_t l=fmt_cgivars(0,ctx_for_sockfd,c,matches.rm_eo,dir,&hc);
707 char* x,* y;
708 /* fmt_cgivars writes "FOO=barbaz\n" but we need
709 * "\003\006FOObarbaz"; if a key or value is longer than 127,
710 * the length takes up four bytes instead of one. A
711 * conservative upper bound on additional space used is
712 * thus the number of headers (hc) times 6. */
713
714 /* space calculation with fastcgi boilerplate overhead:
715 * 16 for {FCGI_BEGIN_REQUEST, 1, {FCGI_RESPONDER, 0}}
716 * 8 for {FCGI_PARAMS, 1, ...}
717 * l for the actual params
718 * 8 for {FCGI_PARAMS, 1, ""}
719 * 8 for {FCGI_STDIN, 1, ""}
720 */
721 if (!array_allocate(&ctx_for_gatewayfd->r,1,l+hc*6+16+8+8+8+2))
722 goto freeandfail;
723 x=array_start(&ctx_for_gatewayfd->r);
724 byte_copy(x,24,"\x01\x01\x00\x01\x00\x08\x00\x00" /* FCGI_Record: FCGI_BEGIN_REQUEST (1) */
725 "\x00\x01\x00\x00\x00\x00\x00\x00" /* FCGI_BeginRequestBody */
726 "\x01\x04\x00\x01\x00\x00\x00\x00" /* FCGI_Record: FCGI_PARAMS (4) */
727 );
728 /* We need to convert the key-value pairs, but unfortunately
729 * that expansion may require more space than the unexpanded
730 * version. So we allocate for the worst case and write the
731 * original towards the end of the allocated space, so we can
732 * expand inside the same buffer. */
733 y=x+hc*6+16+8+8+8+2;
734 fmt_cgivars(y,ctx_for_sockfd,c,matches.rm_eo,dir,&hc);
735 x+=24;
736 {
737 size_t a=0;
738 size_t b;
739 size_t kl,vl,prev;
740 for (b=kl=vl=prev=0; b<l; ++b) {
741 if (y[b]=='=' && kl==0) {
742 kl=b-prev;
743 prev=b+1;
744 } else if (y[b]=='\n') {
745 vl=b-prev;
746 prev=b+1;
747 if (kl<127) {
748 x[a]=kl;
749 ++a;
750 } else {
751 uint32_pack_big(x+a,kl|0x80000000u);
752 a+=4;
753 }
754 if (vl<127) {
755 x[a]=vl;
756 ++a;
757 } else {
758 uint32_pack_big(x+a,vl|0x80000000u);
759 a+=4;
760 }
761 byte_copy(x+a,kl,y+b-vl-kl-1); a+=kl;
762 byte_copy(x+a,vl,y+b-vl); a+=vl;
763 kl=0;
764 }
765 }
766 x[a]=x[a+1]=0; a+=2;
767 x[-4]=a>>8; /* adjust length field in FCGI_Record */
768 x[-3]=a&0xff;
769 array_truncate(&ctx_for_gatewayfd->r,1,a+24+8+8);
770 x+=a;
771 byte_copy(x,8,"\x01\x04\x00\x01\x00\x00\x00\x00"); /* FCGI_Record: FCGI_PARAMS (4) */
772 x+=8;
773
774 {
775 char* cl=http_header(ctx_for_sockfd,"Content-Length");
776 unsigned long long content_length=0;
777 if (cl) {
778 char c;
779 if ((c=cl[scan_ulonglong(cl,&content_length)])!='\r' && c!='\n') content_length=0;
780 }
781 if (content_length)
782 array_truncate(&ctx_for_gatewayfd->r,1,a+24+8); /* shave off 8 bytes */
783 else {
784 byte_copy(x,8,"\x01\x05\x00\x01\x00\x00\x00\x00"); /* FCGI_Record: FCGI_STDIN (5) */
785 }
786 }
787 }
788 } else if (x->proxyproto==HTTP) {
789 size_t size_of_header=header_complete(ctx_for_sockfd,sockfd);
790 size_t i;
791 char* x=array_start(&ctx_for_sockfd->r);
792 for (i=0; i<size_of_header && x[i]!='\n'; ++i)
793 if (x[i]==0) x[i]=' ';
794 array_catb(&ctx_for_gatewayfd->r,x,size_of_header);
795 }
796 }
797
798 if (logging) {
799 char buf[IP6_FMT+10];
800 char* tmp;
801 const char* method="???";
802 {
803 int x;
804 x=fmt_ip6c(buf,ctx_for_gatewayfd->myip);
805 x+=fmt_str(buf+x,"/");
806 x+=fmt_ulong(buf+x,ctx_for_gatewayfd->myport);
807 buf[x]=0;
808 }
809 tmp=array_start(&ctx_for_sockfd->r);
810 #ifdef SUPPORT_HTTPS
811 switch (*tmp) {
812 case 'H': method=(ctx_for_sockfd->t==HTTPREQUEST)?"HEAD":"HEAD/SSL"; break;
813 case 'G': method=(ctx_for_sockfd->t==HTTPREQUEST)?"GET":"GET/SSL"; break;
814 case 'P':
815 if (tmp[1]=='O')
816 method=(ctx_for_sockfd->t==HTTPREQUEST)?"POST":"POST/SSL";
817 #ifdef SUPPORT_DAV
818 else if (tmp[1]=='R')
819 method=(ctx_for_sockfd->t==HTTPREQUEST)?"PROPFIND":"PROPFIND/SSL";
820 #endif
821 #ifdef SUPPORT_PUT
822 else
823 method=(ctx_for_sockfd->t==HTTPREQUEST)?"PUT":"PUT/SSL";
824 #endif
825 break;
826 #ifdef SUPPORT_DAV
827 case 'O': method=(ctx_for_sockfd->t==HTTPREQUEST)?"OPTIONS":"OPTIONS/SSL"; break;
828 // case 'L': method=(ctx_for_sockfd->t==HTTPREQUEST)?"LOCK":"LOCK/SSL"; break;
829 // case 'U': method=(ctx_for_sockfd->t==HTTPREQUEST)?"UNLOCK":"UNLOCK/SSL"; break;
830 #endif
831 }
832 #else
833 switch (*tmp) {
834 case 'H': method="HEAD"; break;
835 case 'G': method="GET"; break;
836 case 'P': method=(tmp[1]=='O')?"POST":
837 #ifdef SUPPORT_DAV
838 ((tmp[1]=='R')?"PROPFIND":"PUT");
839 #else
840 "PUT";
841 #endif
842 break;
843 #ifdef SUPPORT_DAV
844 case 'O': method="OPTIONS"; break;
845 // case 'L': method="LOCK"; break;
846 // case 'U': method="UNLOCK"; break;
847 #endif
848 }
849 #endif
850 buffer_putm(buffer_1,method,x->port?"/PROXY ":"/CGI ");
851 buffer_putulong(buffer_1,sockfd);
852 buffer_puts(buffer_1," ");
853 buffer_putlogstr(buffer_1,c);
854 if (args) {
855 buffer_puts(buffer_1,"?");
856 buffer_putlogstr(buffer_1,args);
857 }
858 buffer_puts(buffer_1," 0 ");
859 buffer_putlogstr(buffer_1,(tmp=http_header(ctx_for_sockfd,"User-Agent"))?tmp:"[no_user_agent]");
860 buffer_puts(buffer_1," ");
861 buffer_putlogstr(buffer_1,(tmp=http_header(ctx_for_sockfd,"Referer"))?tmp:"[no_referrer]");
862 buffer_puts(buffer_1," ");
863 buffer_putlogstr(buffer_1,(tmp=http_header(ctx_for_sockfd,"Host"))?tmp:buf);
864 buffer_putsflush(buffer_1,"\n");
865 }
866 ++rps1;
867
868 if (x->port) {
869 /* proxy mode */
870 if (x->port>0xffff) { /* unix domain socket mode */
871 fd_to_gateway=socket(AF_UNIX,SOCK_STREAM,0);
872 } else
873 fd_to_gateway=socket_tcp6();
874 #ifdef STATE_DEBUG
875 ctx_for_gatewayfd->myfd=fd_to_gateway;
876 #endif
877 if (fd_to_gateway==-1) goto punt2;
878 changestate(ctx_for_gatewayfd,PROXYSLAVE);
879 #ifdef HAVE_IO_FD_FLAGS
880 if (!io_fd_flags(fd_to_gateway,IO_FD_NONBLOCK))
881 #else
882 if (!io_fd(fd_to_gateway))
883 #endif
884 {
885 punt:
886 io_close(fd_to_gateway);
887 punt2:
888 array_reset(&ctx_for_gatewayfd->r);
889 free(ctx_for_gatewayfd);
890 return -1;
891 }
892 io_block(fd_to_gateway);
893 /* this does not appear to make any sense...?!
894 io_eagain(fd_to_gateway);
895 */
896 if (x->port>0xffff) {
897 if (connect(fd_to_gateway,(struct sockaddr*)&x->uds,sizeof(x->uds))==-1)
898 if (errno!=EINPROGRESS)
899 goto punt;
900 } else {
901 if (socket_connect6(fd_to_gateway,x->ip,x->port,x->scope_id)==-1)
902 if (errno!=EINPROGRESS)
903 goto punt;
904 }
905 if (logging) {
906 char tmp[100];
907 char bufsockfd[FMT_ULONG];
908 char bufs[FMT_ULONG];
909 char bufport[FMT_ULONG];
910
911 bufsockfd[fmt_ulong(bufsockfd,sockfd)]=0;
912 bufs[fmt_ulong(bufs,fd_to_gateway)]=0;
913 if (x->port>0xffff) {
914 buffer_putm(buffer_1,"proxy_connect ",bufsockfd," ",bufs," ",x->uds.sun_path," ");
915 } else {
916 bufport[fmt_ulong(bufport,x->port)]=0;
917 tmp[fmt_ip6ifc(tmp,x->ip,x->scope_id)]=0;
918
919 buffer_putm(buffer_1,"proxy_connect ",bufsockfd," ",bufs," ",tmp,"/",bufport," ");
920 }
921 buffer_putlogstr(buffer_1,c);
922 buffer_putnlflush(buffer_1);
923 }
924 io_wantwrite(fd_to_gateway);
925 #ifdef SUPPORT_CGI
926 } else {
927 /* local CGI mode */
928 uint32 a,len; uint16 b;
929 pid_t pid;
930 size_t reqlen;
931 char* req=array_start(&ctx_for_sockfd->r); /* "GET /t.cgi/foo/bar?fnord HTTP/1.0\r\nHost: localhost:80\r\n\r\n"; */
932 char ra[IP6_FMT];
933 req[strlen(req)]=' ';
934
935 {
936 char* tmp;
937 reqlen=0;
938 for (tmp=req; tmp; tmp=strchr(tmp,'\n')) {
939 if (tmp[1]=='\r' && tmp[2]=='\n') {
940 reqlen=tmp+2-req;
941 break;
942 } else if (tmp[1]=='\n') {
943 reqlen=tmp+1-req;
944 break;
945 }
946 ++tmp;
947 }
948 }
949
950 // ctx_for_sockfd->keepalive=0;
951
952 ra[fmt_ip6c(ra,ctx_for_sockfd->peerip)]=0;
953 a=reqlen; write(forksock[0],&a,4);
954 a=strlen(dir); write(forksock[0],&a,4);
955 a=strlen(ra); write(forksock[0],&a,4);
956 write(forksock[0],req,reqlen);
957 write(forksock[0],dir,strlen(dir));
958 write(forksock[0],ra,strlen(ra));
959 b=ctx_for_sockfd->peerport; write(forksock[0],&b,2);
960 b=ctx_for_sockfd->myport; write(forksock[0],&b,2);
961 #ifdef SUPPORT_HTTPS
962 {
963 char ssl=ctx_for_sockfd->t==HTTPSREQUEST;
964 write(forksock[0],&ssl,1);
965 }
966 #endif
967
968 read(forksock[0],&a,4); /* code; 0 means OK */
969 read(forksock[0],&len,4); /* length of error message */
970 read(forksock[0],&pid,sizeof(pid));
971 if (a || len) {
972 char* c=alloca(len+1);
973 read(forksock[0],c,len);
974 c[len]=0;
975 httperror(ctx_for_sockfd,"502 Gateway Broken",c,*ctx_for_sockfd->r.p=='H'?1:0);
976 free(ctx_for_gatewayfd);
977 return -1;
978 } else {
979 fd_to_gateway=io_receivefd(forksock[0]);
980 #ifdef STATE_DEBUG
981 ctx_for_gatewayfd->myfd=fd_to_gateway;
982 #endif
983 changestate(ctx_for_gatewayfd,PROXYPOST);
984 if (fd_to_gateway==-1) {
985 buffer_putsflush(buffer_2,"received no file descriptor for CGI\n");
986 free(ctx_for_gatewayfd);
987 return -1;
988 }
989 #ifdef HAVE_IO_FD_FLAGS
990 if (!io_fd_flags(fd_to_gateway,IO_FD_CANWRITE|IO_FD_NONBLOCK))
991 #else
992 if (!io_fd_canwrite(fd_to_gateway))
993 #endif
994 {
995 httperror(ctx_for_sockfd,"502 Gateway Broken",c,*ctx_for_sockfd->r.p=='H'?1:0);
996 io_close(fd_to_gateway);
997 free(ctx_for_gatewayfd);
998 return -1;
999 }
1000 }
1001 #ifdef SUPPORT_HTTPS
1002 if (ctx_for_sockfd->t==HTTPSREQUEST)
1003 changestate(ctx_for_sockfd,HTTPSPOST);
1004 else
1005 #endif
1006 changestate(ctx_for_sockfd,HTTPPOST);
1007 if (logging) {
1008 char bufsfd[FMT_ULONG];
1009 char bufs[FMT_ULONG];
1010 char bufpid[FMT_ULONG];
1011
1012 bufsfd[fmt_ulong(bufsfd,sockfd)]=0;
1013 bufs[fmt_ulong(bufs,fd_to_gateway)]=0;
1014 bufpid[fmt_ulong(bufpid,pid)]=0;
1015
1016 buffer_putmflush(buffer_1,"cgi_fork ",bufsfd," ",bufs," ",bufpid,"\n");
1017 }
1018 #endif
1019 }
1020
1021 ctx_for_gatewayfd->buddy=sockfd;
1022 ctx_for_sockfd->buddy=fd_to_gateway;
1023 io_setcookie(fd_to_gateway,ctx_for_gatewayfd);
1024
1025 /* Have:
1026 * - the header and possibly some data left in ctx_for_sockfd->r.
1027 * Want:
1028 * - leave the data (not the header) in ctx_for_sockfd->r.
1029 * - set ctx_for_gatewayfd->still_to_copy to Content-Length.
1030 * - set ctx_for_sockfd->still_to_copy to Content-Length -
1031 * the size of the copied data. If that is non-zero, set t to
1032 * HTTPPOST.
1033 */
1034
1035 {
1036 char* cl=http_header(ctx_for_sockfd,"Content-Length");
1037 unsigned long long content_length=0;
1038 if (cl) {
1039 char c;
1040 if ((c=cl[scan_ulonglong(cl,&content_length)])!='\r' && c!='\n') content_length=0;
1041 }
1042
1043 ctx_for_gatewayfd->still_to_copy=content_length;
1044
1045 /* If the client sent "Expect: 100-continue", do so */
1046 {
1047 char* e=http_header(ctx_for_sockfd,"Expect");
1048 if (e && byte_equal(e,4,"100-")) {
1049 const char contmsg[]="HTTP/1.1 100 Continue\r\n\r\n";
1050 /* if this fails, tough luck. I'm not bloating my state
1051 * engine for this crap. */
1052 #ifdef SUPPORT_HTTPS
1053 if (ctx_for_sockfd->t==HTTPSREQUEST)
1054 #if defined(USE_OPENSSL)
1055 SSL_write(ctx_for_sockfd->ssl,contmsg,sizeof(contmsg)-1);
1056 #elif defined(USE_POLARSSL)
1057 mbedtls_ssl_write(&ctx_for_sockfd->ssldata->ssl,(const unsigned char*)contmsg,sizeof(contmsg)-1);
1058 #else
1059 #warn fixme update SSL code in http.c
1060 #endif
1061 else
1062 #endif
1063 io_trywrite(sockfd,contmsg,sizeof(contmsg)-1);
1064 }
1065 }
1066
1067 /* figure out how much data we have */
1068 {
1069 size_t size_of_header=header_complete(ctx_for_sockfd,sockfd);
1070 size_t size_of_data_in_packet=array_bytes(&ctx_for_sockfd->r) - size_of_header;
1071
1072 // printf("proxy_connection: size_of_header=%lu, size_of_data_in_packet=%lu, content_length=%lu\n",size_of_header,size_of_data_in_packet,content_length);
1073
1074 #ifdef SUPPORT_HTTPS
1075 if (ctx_for_sockfd->t==HTTPSREQUEST)
1076 changestate(ctx_for_sockfd,HTTPSPOST);
1077 if (ctx_for_sockfd->t!=HTTPSPOST)
1078 #endif
1079 changestate(ctx_for_sockfd,HTTPPOST);
1080
1081 /* slight complication: we might have more data already than
1082 * we need for this request, if the content length is small
1083 * and the client uses pipelining and added the next request
1084 * already. */
1085 #if 0
1086 if (size_of_data_in_packet > content_length)
1087 size_of_data_in_packet -= content_length;
1088 #endif
1089
1090 if (size_of_data_in_packet) {
1091 byte_copy(array_start(&ctx_for_sockfd->r),
1092 size_of_data_in_packet,
1093 array_start(&ctx_for_sockfd->r)+size_of_header);
1094 byte_zero(array_start(&ctx_for_sockfd->r)+size_of_data_in_packet,
1095 size_of_header);
1096 array_truncate(&ctx_for_sockfd->r,1,size_of_data_in_packet);
1097 } else
1098 array_trunc(&ctx_for_sockfd->r);
1099 ctx_for_sockfd->still_to_copy=content_length;
1100
1101 if (ctx_for_gatewayfd->still_to_copy && array_bytes(&ctx_for_sockfd->r))
1102 io_wantwrite(fd_to_gateway);
1103 else
1104 io_wantread(fd_to_gateway);
1105
1106 if (ctx_for_sockfd->still_to_copy)
1107 io_wantread(sockfd);
1108 else
1109 io_dontwantread(sockfd);
1110
1111 // printf("proxy_connection: ctx_for_sockfd->still_to_copy=%lu, ctx_for_gatewayfd->still_to_copy=%lu\n",ctx_for_sockfd->still_to_copy, ctx_for_gatewayfd->still_to_copy);
1112
1113 }
1114 }
1115
1116 if (timeout_secs)
1117 io_timeout(fd_to_gateway,next);
1118 return fd_to_gateway;
1119 }
1120 x=x->next;
1121 }
1122 return -2;
1123 }
1124
proxy_write_header(int sockfd,struct http_data * h)1125 int proxy_write_header(int sockfd,struct http_data* h) {
1126 /* assume we can write the header in full. */
1127 /* slight complication: we need to turn keep-alive off and we need to
1128 * add a X-Forwarded-For header so the handling web server can write
1129 * the real IP to the log file. */
1130 struct http_data* H=io_getcookie(h->buddy);
1131 int i,j=0;
1132 long hlen=array_bytes(&h->r);
1133 char* hdr=array_start(&h->r);
1134 char* newheader=0;
1135 if (h->proxyproto==HTTP) {
1136 newheader=alloca(hlen+200);
1137 for (i=j=0; i<hlen; ) {
1138 int k=str_chr(hdr+i,'\n');
1139 if (k==0) break;
1140 if ( /* case_starts(hdr+i,"Connection: ") || */ case_starts(hdr+i,"X-Forwarded-For: "))
1141 i+=k+1;
1142 else {
1143 byte_copy(newheader+j,k+1,hdr+i);
1144 i+=k+1;
1145 j+=k+1;
1146 }
1147 }
1148 if (j) j-=2;
1149 // H->keepalive=0;
1150 // j+=fmt_str(newheader+j,"Connection: close\r\nX-Forwarded-For: ");
1151 j+=fmt_str(newheader+j,"X-Forwarded-For: ");
1152 j+=fmt_ip6c(newheader+j,H->peerip);
1153 j+=fmt_str(newheader+j,"\r\n\r\n");
1154 } else if (h->proxyproto==FASTCGI || h->proxyproto==SCGI) {
1155 newheader=array_start(&h->r);
1156 j=array_bytes(&h->r);
1157 }
1158 if (write(sockfd,newheader,j)!=j)
1159 return -1;
1160 if (h->proxyproto==SCGI)
1161 array_trunc(&h->r);
1162 H->sent+=j;
1163 return 0;
1164 }
1165
1166
1167
proxy_is_readable(int sockfd,struct http_data * H)1168 int proxy_is_readable(int sockfd,struct http_data* H) {
1169 /* read data from proxy and queue it for writing to browser
1170 * connection, also add "HTTP/1.0 200 OK" header if necessary */
1171 char Buf[8194];
1172 char* buf=Buf+1;
1173 int i;
1174 char* x = "";
1175 struct http_data* peer=io_getcookie(H->buddy);
1176 if (!peer) return -1;
1177 /* sockfd = the socket to the proxy / CGI
1178 * H = the context of the socket to the proxy / CGI
1179 * H->buddy = the socket to the client / browser
1180 * peer = the context of the socket to the client / browser
1181 * peer->buddy = sockfd
1182 */
1183 i=read(sockfd,buf,sizeof(Buf)-2);
1184 if (i==-1) return -1;
1185 H->sent+=i;
1186 if (i==0) {
1187 eof:
1188 if (logging) {
1189 char numbuf[FMT_ULONG];
1190 char r[FMT_ULONG];
1191 char s[FMT_ULONG];
1192 numbuf[fmt_ulong(numbuf,sockfd)]=0;
1193 r[fmt_ulonglong(r,peer->received)]=0;
1194 s[fmt_ulonglong(s,H->sent)]=0;
1195 buffer_putmflush(buffer_1,"cgiproxy_read0 ",numbuf," ",r," ",s,"\n");
1196 }
1197 if (H->hss.state==HSS_INFINITE) peer->keepalive=0;
1198 if (peer->keepalive) {
1199 H->buddy=peer->buddy=-1;
1200 cleanup(sockfd);
1201 return -4;
1202 }
1203 peer->buddy=-1;
1204 cleanup(sockfd);
1205 return -3;
1206 } else {
1207 int needheader=0;
1208 size_t cl=0,rs=0;
1209 int gotone=0;
1210
1211 if (H->proxyproto==FASTCGI) {
1212 /* For FastCGI, we get the data in packets, which we need to parse.
1213 * Which also means we have to deal with partial packets. We do
1214 * this by putting the packets in our H->r until we have assembled
1215 * one. */
1216 array_catb(&H->r,buf,i);
1217 if (array_failed(&H->r)) return -1;
1218 nextpacket:
1219 x=array_start(&H->r);
1220 rs=array_bytes(&H->r);
1221 if (rs<8) return 0; /* not done, need more data */
1222 /* we have a header */
1223 errno=EINVAL;
1224 if (x[0]!=1) return -1;
1225 /* we expect one of FCGI_STDOUT, FCGI_STDERR, or
1226 * FCGI_END_REQUEST */
1227 if (x[1]!=6 && x[1]!=7 && x[1]!=3) return -1;
1228 /* the request ID must be 1, because that is what we sent */
1229 if (x[2]!=0 || x[3]!=1) return -1;
1230 cl=((unsigned char)x[4]<<8)|(unsigned char)x[5];
1231 if (rs<8+cl+(unsigned char)(x[6])) {
1232 if (gotone) goto success;
1233 return 0; /* not done, need more data */
1234 }
1235 /* got enough data for one packet. look at packet. */
1236 if (x[1]==3) {
1237 io_wantwrite(H->buddy);
1238 H->buddy=-1;
1239 goto eof; /* FCGI_END_REQUEST */
1240 }
1241 if (x[1]==6) { /* FCGI_STDOUT */
1242 buf=x+8;
1243 i=cl;
1244 }
1245 }
1246
1247 if (!H->havefirst) {
1248 H->havefirst=1;
1249 httpstream_initstate(&H->hss);
1250
1251 /* Problem: the HTTP stream parser does not know whether the
1252 * request was a GET, a POST or a HEAD when it parses the
1253 * response. If somebody uses HTTP keep-alive and first sends a
1254 * HEAD and the na GET, the stream parser will not reqlize it was
1255 * a HEAD and still expect the content after the header.
1256
1257 * So we need to tell it not to do that. Fortunately, it already
1258 * has a mode for that, that is triggered by setting the type to
1259 * REQUEST. */
1260 if (*(char*)array_start(&peer->r)=='H') H->hss.type=REQUEST;
1261
1262 if (H->proxyproto==SCGI || H->proxyproto==FASTCGI) {
1263 if (case_starts(buf,"Status:")) {
1264 --buf; ++i;
1265 memcpy(buf,"HTTP/1.1 ",9);
1266 } else
1267 needheader=1;
1268 } else if (byte_diff(buf,5,"HTTP/"))
1269 /* No "HTTP/1.0 200 OK", need to write our own header. */
1270 needheader=1;
1271 }
1272 if (needheader) {
1273 size_t j;
1274 x=malloc(i+100);
1275 if (!x) goto nomem;
1276 j=fmt_str(x,"HTTP/1.1 200 Here you go\r\nServer: " RELEASE "\r\n");
1277 byte_copy(x+j,i,buf);
1278 i+=j;
1279 } else if (i > 0) {
1280 x=malloc(i);
1281 if (!x) goto nomem;
1282 byte_copy(x,i,buf);
1283 }
1284 {
1285 size_t j,k;
1286 k=i-sizeof("\nLocation:");
1287 if (k<i) {
1288 for (j=0; j<k; ++j) {
1289 if (x[j]=='\n' && case_equalb(x+j+1,sizeof("Location"),"Location:")) {
1290 byte_copy(x+9,3,"302");
1291 break;
1292 }
1293 }
1294 }
1295 }
1296 {
1297 size_t y;
1298 y=httpstream(&H->hss,x,i);
1299 if (y!=i) {
1300 buffer_putmflush(buffer_1,"proxy sent more data than http stream parser expected!\n");
1301 }
1302 i=y;
1303 }
1304 iob_addbuf_free(&peer->iob,x,i);
1305 gotone=1;
1306
1307 if (H->proxyproto==FASTCGI) {
1308 /* now, if we got this far, we need to remove the packet from the
1309 * buffer */
1310 x=array_start(&H->r);
1311 cl+=8+(unsigned char)(x[6]);
1312 if (rs>cl) byte_copy(x,rs-cl,x+cl);
1313 array_truncate(&H->r,1,rs-cl);
1314 if (rs>cl && rs-cl>=8) goto nextpacket;
1315 }
1316 }
1317 success:
1318 if (H->hss.state==HSS_DONE) {
1319 io_wantwrite(H->buddy);
1320 H->buddy=peer->buddy=-1;
1321 #ifdef SUPPORT_HTTPS
1322 if (peer->t == HTTPSPOST)
1323 peer->t=HTTPSRESPONSE;
1324 else
1325 #endif
1326 peer->t = HTTPREQUEST;
1327 cleanup(sockfd);
1328 return 0;
1329 }
1330 io_wantwrite(H->buddy);
1331 io_dontwantread(sockfd);
1332 return 0;
1333 nomem:
1334 if (logging) {
1335 char numbuf[FMT_ULONG];
1336 numbuf[fmt_ulong(numbuf,sockfd)]=0;
1337 buffer_putmflush(buffer_1,"outofmemory ",numbuf,"\n");
1338 }
1339 cleanup(sockfd);
1340 return -1;
1341 }
1342
read_http_post(int sockfd,struct http_data * H)1343 int read_http_post(int sockfd,struct http_data* H) {
1344 /* read post data from browser, write to proxy */
1345 char buf[8192];
1346 int i;
1347 unsigned long long l=H->still_to_copy;
1348 if (l>sizeof(buf)) l=sizeof(buf);
1349 #ifdef SUPPORT_HTTPS
1350 if (H->t==HTTPSPOST) {
1351 #ifdef USE_OPENSSL
1352 // printf("calling SSL_read in read_http_post on fd %d\n",sockfd); fflush(stdout);
1353 i=SSL_read(H->ssl,buf,l);
1354 // printf("SSL_read(sock %d,buf %p,n %d) -> %d\n",sockfd,buf,(int)l,(int)i); fflush(stdout);
1355 if (i<0) {
1356 i=SSL_get_error(H->ssl,i);
1357 // printf("SSL_get_error -> %d (SSL_ERROR_WANT_READ=%d, SSL_ERROR_WANT_WRITE=%d)\n",l,SSL_ERROR_WANT_READ,SSL_ERROR_WANT_WRITE); fflush(stdout);
1358 if (i==SSL_ERROR_WANT_READ || i==SSL_ERROR_WANT_WRITE) {
1359 #elif defined(USE_POLARSSL)
1360 i=mbedtls_ssl_read(&H->ssldata->ssl,(unsigned char*)buf,l);
1361 if (i<0) {
1362 if (l==MBEDTLS_ERR_SSL_WANT_READ || l==MBEDTLS_ERR_SSL_WANT_WRITE) {
1363 #endif
1364 #ifdef USE_OPENSSL
1365 if (i==SSL_ERROR_WANT_READ) io_eagain_read(sockfd);
1366 else if (i==SSL_ERROR_WANT_WRITE) io_eagain_write(sockfd);
1367 #elif defined(USE_POLARSSL)
1368 if (i==MBEDTLS_ERR_SSL_WANT_READ) io_eagain_read(sockfd);
1369 else if (i==MBEDTLS_ERR_SSL_WANT_WRITE) io_eagain_write(sockfd);
1370 #else
1371 #error fixme add io_again_* logic for this SSL library
1372 #endif
1373 if (handle_ssl_error_code((int)sockfd,i,1)==-1)
1374 return -1;
1375 }
1376 return 0;
1377 }
1378 #ifdef USE_OPENSSL
1379 if (i==0 && (H->t == HTTPSPOST || H->t == HTTPSREQUEST))
1380 SSL_shutdown(H->ssl);
1381 #endif
1382 } else
1383 #endif
1384 i=read(sockfd,buf,l);
1385 if (i<1) return -1;
1386
1387 H->received+=i;
1388 H->still_to_copy-=i;
1389 /* we got some data. Now, for FastCGI we need to add a header before
1390 * writing it to the proxy */
1391 if (H->proxyproto==FASTCGI) {
1392 char tmp[8]="\x01\x05\x00\x01\x00\x00\x00\x00";
1393 tmp[4]=i>>8;
1394 tmp[5]=i&0xff;
1395
1396 array_catb(&H->r,tmp,8); /* FCGI_Record: FCGI_STDIN (5) */
1397 }
1398 array_catb(&H->r,buf,i);
1399 if (H->proxyproto==FASTCGI && H->still_to_copy==0) {
1400 array_catb(&H->r,"\x01\x05\x00\x01\x00\x00\x00\x00",8); /* FCGI_Record: FCGI_STDIN (5) */
1401 }
1402 if (array_failed(&H->r))
1403 return -1;
1404 return 0;
1405 }
1406
1407 #endif
1408
1409
1410
1411
1412 #ifdef SUPPORT_HTACCESS
1413 /* check whether there is a .htaccess file in the current directory.
1414 * if it is, expect the following format:
1415
1416 Realm
1417 username:password
1418 username2:password2
1419 ...
1420
1421 * Realm is the HTTP realm (transmitted in the http authentication
1422 * required message and usually displayed by the browser). Only basic
1423 * authentication is supported. Please note that .htaccess files are
1424 * not looked for in other directories. If you want subdirectories
1425 * covered, use hard or symbolic links. The function returns 0 if the
1426 * authentication was OK or -1 if authentication is needed (the HTTP
1427 * response was then already written to the iob). */
1428 int http_dohtaccess(struct http_data* h,const char* filename,int nobody,int dirfd,struct dircacheentry* de) {
1429 size_t filesize;
1430 const char* map;
1431 const char* s;
1432 char* auth;
1433 char* realm;
1434 int r=0;
1435 if (de) {
1436 if (de->htaccess_global_len) {
1437 if (!de->htaccess_global) return 1;
1438 map=de->htaccess_global;
1439 filesize=de->htaccess_global_len;
1440 } else {
1441 map=mmap_readat(filename,&filesize,dirfd);
1442 de->htaccess_global=map;
1443 de->htaccess_global_len=map?filesize:1;
1444 }
1445 } else
1446 map=mmap_readat(filename,&filesize,dirfd);
1447 if (!map) return 1;
1448 for (s=map; (s<map+filesize) && (*s!='\n'); ++s); /* XXX */
1449 if (s>=map+filesize) goto done;
1450 realm=alloca(s-map+1);
1451 memmove(realm,map,s-map);
1452 realm[s-map]=0;
1453 ++s;
1454 auth=http_header(h,"Authorization");
1455 if (auth) {
1456 if (str_start(auth,"Basic ")) {
1457 char* username,* password;
1458 char* decoded;
1459 int i;
1460 size_t l,dl,ul;
1461 auth+=6;
1462 while (*auth==' ' || *auth=='\t') ++auth;
1463 i=str_chr(auth,'\n');
1464 if (i && auth[i-1]=='\r') --i;
1465 decoded=alloca(i+1);
1466 l=scan_base64(auth,decoded,&dl);
1467 if (auth[l]!='\n' && auth[l]!='\r') goto needauth;
1468 decoded[dl]=0;
1469 l=str_rchr(decoded,':');
1470 if (decoded[l]!=':') goto needauth;
1471 username=decoded; ul=l;
1472 decoded[l]=0; password=decoded+l+1;
1473
1474 for (l=0; l<filesize; ) {
1475 while (l<filesize && map[l]!='\n') ++l;
1476 if (map[l]=='\n') ++l;
1477 if (l>=filesize) break;
1478 if (byte_equal(map+l,ul,username) && map[l+ul]==':') {
1479 char* crypted=crypt(password,map+l+ul+1);
1480 i=str_len(crypted);
1481 if (l+ul+1+i <= filesize)
1482 if (byte_equal(map+l+ul+1,i,crypted)) {
1483 r=1;
1484 goto done;
1485 }
1486 }
1487 }
1488 }
1489 }
1490 needauth:
1491 httperror_realm(h,"401 Authorization Required","Authorization required to view this web page",realm,nobody);
1492 done:
1493 if (!de)
1494 mmap_unmap(map,filesize);
1495 return r;
1496 }
1497 #endif
1498
1499 int http_redirect(struct http_data* h,const char* Filename) {
1500 #ifndef __MINGW32__
1501 char buf[2048];
1502 int i;
1503 if ((i=readlink(Filename,buf,sizeof(buf)))!=-1) {
1504 buf[i]=0;
1505 if (strstr(buf,"://")) {
1506 h->bodybuf=malloc(strlen(buf)+300);
1507 h->hdrbuf=malloc(strlen(buf)+300);
1508 if (h->bodybuf && h->hdrbuf) {
1509 int i;
1510 i=fmt_str(h->bodybuf,"Look <a href=\"");
1511 i+=fmt_str(h->bodybuf+i,buf);
1512 i+=fmt_str(h->bodybuf+i,"\">here</a>!\n");
1513 h->blen=i;
1514
1515 i=fmt_str(h->hdrbuf,"HTTP/1.0 301 Go Away\r\nConnection: ");
1516 i+=fmt_str(h->hdrbuf+i,h->keepalive?"keep-alive":"close");
1517 i+=fmt_str(h->hdrbuf+i,"\r\nServer: " RELEASE "\r\nContent-Length: ");
1518 i+=fmt_ulong(h->hdrbuf+i,h->blen);
1519 i+=fmt_str(h->hdrbuf+i,"\r\nLocation: ");
1520 i+=fmt_str(h->hdrbuf+i,buf);
1521 i+=fmt_str(h->hdrbuf+i,"\r\n\r\n");
1522 h->hlen=i;
1523 return -4;
1524 }
1525 free(h->bodybuf); free(h->hdrbuf);
1526 h->bodybuf=h->hdrbuf=0;
1527 }
1528 }
1529 #endif
1530 return 0;
1531 }
1532
1533 #ifdef SUPPORT_DIR_REDIRECT
1534 void do_dir_redirect(struct http_data* h,const char* filename,int64 s,enum Method method) {
1535 char* nh;
1536 int i;
1537 char* host=http_header(h,"Host");
1538 #ifdef SUPPORT_HTTPS
1539 const char* proto=h->t==HTTPSREQUEST?"https://":"http://";
1540 #else
1541 const char* proto="http://";
1542 #endif
1543 size_t hl;
1544 if (!host) {
1545 /* This should not happen. Someone is sending us a GET for a
1546 * directory but no Host header. Without a host header, we cannot
1547 * construct a redirect URL to send him to, so we will just 404. */
1548 httperror(h,"404 Not Found","No such file or directory.",method==HEAD);
1549 h->hdrbuf=h->bodybuf=0; /* httperror already calls iob_addbuf_free, so we need to make sure the caller doesn't call it again */
1550 return;
1551 }
1552 hl=str_chr(host,'\n');
1553 if (hl && host[hl-1]=='\r') --hl;
1554 nh=malloc((strlen(filename)+hl)*2+300);
1555 if (!nh) {
1556 if (logging) {
1557 char numbuf[FMT_ULONG];
1558 numbuf[fmt_ulong(numbuf,s)]=0;
1559 buffer_putmflush(buffer_1,"outofmemory ",numbuf,"\n");
1560 }
1561 cleanup(s);
1562 return;
1563 }
1564 i=fmt_str(nh,"HTTP/1.0 302 Over There\r\nServer: " RELEASE "\r\nLocation: ");
1565 i+=fmt_str(nh+i,proto);
1566 i+=fmt_strn(nh+i,host,hl);
1567 i+=fmt_str(nh+i,filename);
1568 i+=fmt_str(nh+i,"/\r\nContent-Type: text/html\r\nContent-Length: ");
1569 i+=fmt_ulong(nh+i,strlen(filename)+hl+23);
1570 i+=fmt_str(nh+i,"\r\n\r\n");
1571 i+=fmt_str(nh+i,"Look <a href=");
1572 i+=fmt_str(nh+i,proto);
1573 i+=fmt_strn(nh+i,host,hl);
1574 i+=fmt_str(nh+i,filename);
1575 i+=fmt_str(nh+i,"/>here!</a>\n");
1576 if (logging) {
1577 char numbuf[FMT_ULONG];
1578 numbuf[fmt_ulong(numbuf,s)]=0;
1579 buffer_putmflush(buffer_1,"dir_redirect ",numbuf,"\n");
1580 }
1581 iob_addbuf_free(&h->iob,nh,i);
1582 }
1583 #endif
1584
1585 int word(const char* s,const char* end,const char* theword,size_t n) {
1586 return (end-s>=n && !memcmp(s,theword,n) &&
1587 (s[n]==',' || s[n]==' ' || s[n]=='\r' || s[n]=='\n'));
1588 }
1589
1590 int64 http_openfile(struct http_data* h,char* filename,struct stat* ss,int sockfd,enum Method method) {
1591 #ifdef SUPPORT_PROXY
1592 int noproxy=0;
1593 #endif
1594 char* dir=0;
1595 char* s;
1596 char* args;
1597 size_t i;
1598 int64 fd;
1599 int doesgzip;
1600 #ifdef SUPPORT_BZIP2
1601 int doesbzip2;
1602 #endif
1603 #ifdef SUPPORT_BROTLI
1604 int doesbrotli;
1605 #endif
1606
1607 char* Filename;
1608
1609 doesgzip=0; h->encoding=NORMAL;
1610 #ifdef SUPPORT_BZIP2
1611 doesbzip2=0;
1612 #endif
1613 #ifdef SUPPORT_BROTLI
1614 doesbrotli=0;
1615 #endif
1616 {
1617 char* tmp=http_header(h,"Accept-Encoding");
1618 if (tmp) { /* yeah this is crude, but it gets the job done */
1619 const char* end=strchr(tmp,'\n');
1620 const char* cur;
1621 for (cur=tmp; cur<end; ) {
1622 if (word(cur,end,"gzip",4))
1623 doesgzip=1;
1624 #ifdef SUPPORT_BZIP2
1625 else if (word(cur,end,"bzip2",5))
1626 doesbzip2=1;
1627 #endif
1628 #ifdef SUPPORT_BROTLI
1629 else if (word(cur,end,"br",2))
1630 doesbrotli=1;
1631 #endif
1632 cur=memchr(cur,',',end-cur);
1633 if (!cur) break;
1634 while (cur<end && (*cur==',' || *cur==' ')) ++cur;
1635 }
1636 }
1637 }
1638
1639 args=0;
1640 /* the file name needs to start with a / */
1641 if (filename[0]!='/') return -1;
1642
1643
1644 /* first, we need to strip "?.*" from the end */
1645 i=str_chr(filename,'?');
1646 Filename=alloca(i+6+(defaultindex?strlen(defaultindex):0)); /* enough space for .gz and .bz2 */
1647 byte_copy(Filename,i+1,filename);
1648 if (Filename[i]=='?') { Filename[i]=0; args=filename+i+1; }
1649 /* second, we need to un-urlencode the file name */
1650 /* we can do it in-place, the decoded string can never be longer */
1651 {
1652 size_t j,src,dst;
1653 for (src=dst=0;;) {
1654 j=scan_urlencoded2(Filename+src,Filename+dst,&i);
1655 src+=j;
1656 dst+=i;
1657 if (Filename[src]==0) break;
1658 Filename[dst++]=Filename[src++];
1659 }
1660 Filename[dst]=0;
1661 }
1662 /* third, change /. to /: so .procmailrc is visible in ls as
1663 * :procmailrc, and it also thwarts most web root escape attacks */
1664 for (i=0; Filename[i]; ++i)
1665 if (Filename[i]=='/' && Filename[i+1]=='.')
1666 Filename[i+1]=':';
1667 /* fourth, try to do some el-cheapo virtual hosting */
1668 if (!(s=http_header(h,"Host"))) {
1669 makefakeheader:
1670 /* construct artificial Host header from IP */
1671 s=alloca(IP6_FMT+7);
1672 i=fmt_ip6c(s,h->myip);
1673 i+=fmt_str(s+i,":");
1674 i+=fmt_ulong(s+i,h->myport);
1675 s[i]=0;
1676 } else {
1677 size_t k;
1678 char* t;
1679 for (k=0; s[k] && s[k]!='/' && s[k]>' '; ++k) ;
1680 t=alloca(k+2);
1681 memcpy(t,s,k);
1682 t[k]='\r'; t[k+1]=0;
1683 s=t;
1684 if (s[0]=='.' || !s[0]) goto makefakeheader;
1685 if (virtual_hosts>=0) {
1686 char* tmp;
1687 int j=str_chr(s,'\r');
1688 /* replace port in Host: with actual port */
1689 if (!s[i=str_chr(s,':')] || i>j || !transproxy) { /* add :port */
1690 if (i>j) i=j;
1691 tmp=alloca(i+7);
1692 byte_copy(tmp,i,s);
1693 tmp[i]=':'; ++i;
1694 i+=fmt_ulong(tmp+i,h->myport);
1695 tmp[i]=0;
1696 s=tmp;
1697 }
1698 }
1699 }
1700
1701 int dirfd=AT_FDCWD;
1702 struct dircacheentry* de=0;
1703 if (virtual_hosts>=0)
1704 if ((dirfd=getdirfd2(dir=s,now.sec.x-4611686018427387914ULL,&de))==-1)
1705 if ((dirfd=getdirfd2(dir="default",now.sec.x-4611686018427387914ULL,&de))==-1) {
1706 if (virtual_hosts==1) {
1707 buffer_putsflush(buffer_2,"chdir FAILED and virtual_hosts is 1\n");
1708 return -1;
1709 } else {
1710 dir=".";
1711 dirfd=AT_FDCWD;
1712 }
1713 }
1714 if (!dir) dir=".";
1715 while (Filename[1]=='/') ++Filename;
1716
1717 #ifdef SUPPORT_HTACCESS
1718 if (http_dohtaccess(h,".htaccess_global",method==HEAD,dirfd,de)==0) return -5;
1719 #endif
1720
1721 #ifdef SUPPORT_PROXY
1722 noproxy=0;
1723 {
1724 int res;
1725 switch ((res=proxy_connection(sockfd,Filename,dir,h,0,args,de,dirfd))) {
1726 case -3: noproxy=1; /* fall through */
1727 case -2: break;
1728 case -1: return -1;
1729 default:
1730 if (res>=0) {
1731 h->buddy=res;
1732 return -3;
1733 }
1734 }
1735 }
1736 #else
1737 (void)sockfd; /* shut up gcc warning about unused variable */
1738 #endif
1739 if (Filename[(i=str_len(Filename))-1] == '/') {
1740 /* Damn. Directory. */
1741
1742 if (defaultindex) {
1743 strcpy(Filename+i,defaultindex);
1744 if (fstatat(dirfd,Filename+1,ss,0)==0) {
1745 /* check if the new filename matches any proxy rule */
1746 #ifdef SUPPORT_PROXY
1747 if (!noproxy) {
1748 int res;
1749 switch ((res=proxy_connection(sockfd,Filename,dir,h,0,args,de,dirfd))) {
1750 case -2: break;
1751 case -1: return -1;
1752 default:
1753 if (res>=0) {
1754 h->buddy=res;
1755 return -3;
1756 }
1757 }
1758 }
1759 #endif
1760 goto itsafile;
1761 } else
1762 Filename[i]=0;
1763 }
1764
1765 #ifndef O_PATH
1766 #define O_PATH 0
1767 #endif
1768 #ifndef O_DIRECTORY
1769 #define O_DIRECTORY 0
1770 #endif
1771 #ifndef O_CLOEXEC
1772 #define O_CLOEXEC 0
1773 #endif
1774 int dir2fd=dirfd;
1775 if (Filename[1] && (dir2fd=openat(dirfd,Filename+1,O_RDONLY|O_PATH|O_DIRECTORY|O_CLOEXEC))==-1) return -1;
1776 #ifdef SUPPORT_HTACCESS
1777 if (http_dohtaccess(h,".htaccess",method==HEAD,dir2fd,0)==0) {
1778 if (dir2fd!=-1) close(dir2fd);
1779 return -5;
1780 }
1781 #endif
1782 h->mimetype="text/html";
1783 errno=0;
1784 if (
1785 #ifdef SUPPORT_DAV
1786 method==PROPFIND ||
1787 #endif
1788 !open_for_reading(&fd,"index.html",ss,dir2fd)) {
1789 DIR* d;
1790 if (errno==ENOENT)
1791 if (http_redirect(h,"index.html")) {
1792 if (dir2fd!=-1) close(dir2fd);
1793 return -4;
1794 }
1795 if (!directory_index) {
1796 if (dir2fd!=-1) close(dir2fd);
1797 return -1;
1798 }
1799 if (!(d=fdopendir(dir2fd))) {
1800 if (dir2fd!=-1) close(dir2fd);
1801 return -1;
1802 }
1803 if (!http_dirlisting(h,d,Filename,args,method,dir2fd)) return -1;
1804 #ifdef USE_ZLIB
1805 if (doesgzip) {
1806 uLongf destlen=h->blen+30+h->blen/1000;
1807 unsigned char *compressed=malloc(destlen+15);
1808 if (!compressed) return -2;
1809 if (compress2(compressed+8,&destlen,(unsigned char*)h->bodybuf,h->blen,3)==Z_OK && destlen<h->blen) {
1810 /* I am absolutely _not_ sure why this works, but we
1811 * apparently have to ignore the first two and the last four
1812 * bytes of the output of compress2. I got this from googling
1813 * for "compress2 header" and finding some obscure gzip
1814 * integration in aolserver */
1815 unsigned int crc=crc32(0,0,0);
1816 crc=crc32(crc,(unsigned char*)h->bodybuf,h->blen);
1817 free(h->bodybuf);
1818 h->bodybuf=(char*)compressed;
1819 h->encoding=GZIP;
1820 byte_zero(compressed,10);
1821 compressed[0]=0x1f; compressed[1]=0x8b;
1822 compressed[2]=8; /* deflate */
1823 compressed[3]=1; /* indicate ASCII */
1824 compressed[9]=3; /* OS = Unix */
1825 uint32_pack((char*)compressed+10-2-4+destlen,crc);
1826 uint32_pack((char*)compressed+14-2-4+destlen,h->blen);
1827 h->blen=destlen+18-2-4;
1828 } else {
1829 free(compressed);
1830 }
1831 }
1832 #endif
1833 return -2;
1834 }
1835 #ifdef SUPPORT_PROXY
1836 /* if index.html is executable, see if we have a file_executable
1837 * CGI rule */
1838 if (!noproxy && (ss->st_mode&S_IXOTH)) {
1839 char* temp=alloca(strlen(Filename)+10);
1840 if (pread(fd,temp,4,0)==4) {
1841 if (byte_equal(temp,2,"#!") || byte_equal(temp,4,"\177ELF")) {
1842 int res;
1843 i=fmt_str(temp,Filename);
1844 i+=fmt_str(temp+i,"index.html");
1845 temp[i]=0;
1846 switch ((res=proxy_connection(sockfd,temp,dir,h,1,args,de,dirfd))) {
1847 case -2: break;
1848 case -1: return -1;
1849 default:
1850 if (res>=0) {
1851 close(fd);
1852 h->buddy=res;
1853 return -3;
1854 }
1855 }
1856 }
1857 }
1858 }
1859 #endif
1860 #ifdef SUPPORT_BROTLI
1861 if (doesbrotli) {
1862 int64 gfd;
1863 if (open_for_reading(&gfd,"index.html.br",ss,dir2fd)) {
1864 io_close(fd);
1865 fd=gfd;
1866 h->encoding=BROTLI;
1867 }
1868 } else
1869 #endif
1870 #ifdef SUPPORT_BZIP2
1871 if (doesbzip2) {
1872 int64 gfd;
1873 if (open_for_reading(&gfd,"index.html.bz2",ss,dir2fd)) {
1874 io_close(fd);
1875 fd=gfd;
1876 h->encoding=BZIP2;
1877 }
1878 } else
1879 #endif
1880 if (doesgzip) {
1881 int64 gfd;
1882 if (open_for_reading(&gfd,"index.html.gz",ss,dir2fd)) {
1883 io_close(fd);
1884 fd=gfd;
1885 h->encoding=GZIP;
1886 }
1887 }
1888 if (dir2fd!=-1) close(dir2fd);
1889 } else {
1890 itsafile:
1891 #ifdef SUPPORT_HTACCESS
1892 {
1893 char* fn=Filename+1;
1894 char* x=alloca(strlen(fn)+30);
1895 int lso=str_rchr(fn,'/');
1896 if (fn[lso]=='/') {
1897 byte_copy(x,lso+1,fn);
1898 str_copy(x+lso+1,".htaccess");
1899 if (http_dohtaccess(h,x,method==HEAD,dirfd,0)==0) return -5;
1900 } else
1901 if (http_dohtaccess(h,".htaccess",method==HEAD,dirfd,0)==0) return -5;
1902 }
1903 #endif
1904
1905 /* For /test/t.cgi/fnord open_for_reading fails with ENOTDIR. */
1906 if (!open_for_reading(&fd,Filename+1,ss,dirfd)) {
1907 if (errno==ENOENT)
1908 if (http_redirect(h,Filename+1)) return -4;
1909 if (errno==ENOTDIR) {
1910 /* we have no choice: we need to test /test, then /test/t.cgi,
1911 * to find the actual file name. Fortunately, the CGI code
1912 * already does that in forkslave(). */
1913 /* We could take it on faith here and let the CGI code handle the
1914 * error, but that is very inefficient (one fork per 404). */
1915 char* fn=alloca(strlen(Filename));
1916 size_t i;
1917 strcpy(fn,Filename+1);
1918 for (i=0; fn[i]; ++i) {
1919 if (fn[i]=='/') {
1920 char c=fn[i];
1921 fn[i]=0;
1922 if (stat(fn,ss))
1923 break; /* genuine 404, can't happen (should have been ENOENT and not ENOTDIR) */
1924 if (!S_ISDIR(ss->st_mode)) { /* found first non-dir entry, hopefully our CGI */
1925 if (!(ss->st_mode&S_IROTH) || (fd=openat(dirfd,fn,O_RDONLY)==-1))
1926 return -1;
1927 h->mimetype=mimetype(fn,fd);
1928 fn[i]=c;
1929 goto foundcgi;
1930 }
1931 fn[i]=c;
1932 }
1933 }
1934 }
1935 return -1;
1936 }
1937 #ifdef SUPPORT_DIR_REDIRECT
1938 if (S_ISDIR(ss->st_mode)) {
1939 io_close(fd);
1940 /* someone asked for http://example.com/foo
1941 * when he should have asked for http://example.com/foo/
1942 * redirect */
1943 do_dir_redirect(h,Filename,sockfd,method);
1944 return -4;
1945 }
1946 #endif
1947 h->mimetype=mimetype(Filename,fd);
1948 foundcgi:
1949 #ifdef SUPPORT_PROXY
1950 if (!noproxy && (ss->st_mode&S_IXOTH)) {
1951 char temp[5];
1952 if (
1953 #ifdef SUPPORT_MIMEMAGIC
1954 /* no need to call pread twice */
1955 h->mimetype==magicelfvalue ||
1956 #endif
1957 ((pread(fd,temp,4,0)==4) && (byte_equal(temp,2,"#!") || byte_equal(temp,4,"\177ELF")))) {
1958 int res;
1959 switch ((res=proxy_connection(sockfd,Filename,dir,h,1,args,de,dirfd))) {
1960 case -2: break;
1961 case -1: return -1;
1962 default:
1963 if (res>=0) {
1964 close(fd);
1965 h->buddy=res;
1966 return -3;
1967 }
1968 }
1969 }
1970 }
1971 #endif
1972 if (h->mimetype==magicelfvalue) h->mimetype="application/octet-stream";
1973 #ifdef DEBUG
1974 if (logging) {
1975 buffer_puts(buffer_1,"open_file ");
1976 buffer_putulong(buffer_1,sockfd);
1977 buffer_putspace(buffer_1);
1978 buffer_putulong(buffer_1,fd);
1979 buffer_putspace(buffer_1);
1980 buffer_puts(buffer_1,Filename);
1981 buffer_putnlflush(buffer_1);
1982 }
1983 #endif
1984 if (doesgzip
1985 #ifdef SUPPORT_BZIP2
1986 || doesbzip2
1987 #endif
1988 #ifdef SUPPORT_BROTLI
1989 || doesbrotli
1990 #endif
1991 ) {
1992 int64 gfd;
1993 i=str_len(Filename);
1994 #ifdef SUPPORT_BROTLI
1995 if (doesbrotli) {
1996 Filename[i+fmt_str(Filename+i,".br")]=0;
1997 if (open_for_reading(&gfd,Filename+1,ss,dirfd)) {
1998 io_close(fd);
1999 fd=gfd;
2000 h->encoding=BROTLI;
2001 }
2002 } else
2003 #endif
2004 #ifdef SUPPORT_BZIP2
2005 if (doesbzip2) {
2006 Filename[i+fmt_str(Filename+i,".bz2")]=0;
2007 if (open_for_reading(&gfd,Filename+1,ss,dirfd)) {
2008 io_close(fd);
2009 fd=gfd;
2010 h->encoding=BZIP2;
2011 }
2012 } else
2013 #endif
2014 if (doesgzip && h->encoding==NORMAL) {
2015 Filename[i+fmt_str(Filename+i,".gz")]=0;
2016 if (open_for_reading(&gfd,Filename+1,ss,dirfd)) {
2017 io_close(fd);
2018 fd=gfd;
2019 h->encoding=GZIP;
2020 }
2021 }
2022 Filename[i]=0;
2023 }
2024 }
2025 #ifndef __MINGW32__
2026 if (S_ISDIR(ss->st_mode)) {
2027 io_close(fd);
2028 return -1;
2029 }
2030 #endif
2031 return fd;
2032 }
2033
2034 #ifdef SUPPORT_FALLBACK_REDIR
2035 const char* redir;
2036
2037 void do_redirect(struct http_data* h,const char* filename,int64 s) {
2038 char* nh=malloc((strlen(filename)+strlen(redir))*2+300);
2039 int i;
2040 if (!nh) {
2041 if (logging) {
2042 char numbuf[FMT_ULONG];
2043 numbuf[fmt_ulong(numbuf,s)]=0;
2044 buffer_putmflush(buffer_1,"outofmemory ",numbuf,"\n");
2045 }
2046 cleanup(s);
2047 return;
2048 }
2049 i=fmt_str(nh,"HTTP/1.0 302 Over There\r\nServer: " RELEASE "\r\nLocation: ");
2050 i+=fmt_str(nh+i,redir);
2051 i+=fmt_str(nh+i,filename);
2052 i+=fmt_str(nh+i,"\r\nContent-Type: text/html\r\nContent-Length: ");
2053 i+=fmt_ulong(nh+i,strlen(filename)+strlen(redir)+23);
2054 i+=fmt_str(nh+i,"\r\n\r\n");
2055 i+=fmt_str(nh+i,"Look <a href=");
2056 i+=fmt_str(nh+i,redir);
2057 i+=fmt_str(nh+i,filename);
2058 i+=fmt_str(nh+i,">here!</a>\n");
2059 iob_addbuf_free(&h->iob,nh,i);
2060 }
2061 #endif
2062
2063 #ifdef SUPPORT_SERVERSTATUS
2064 void do_server_status(struct http_data* h,int64 s) {
2065 char* nh=malloc(1000);
2066 int i,l;
2067 char buf[FMT_ULONG*10+600];
2068 if (!nh) {
2069 if (logging) {
2070 char numbuf[FMT_ULONG];
2071 numbuf[fmt_ulong(numbuf,s)]=0;
2072 buffer_putmflush(buffer_1,"outofmemory ",numbuf,"\n");
2073 }
2074 cleanup(s);
2075 return;
2076 }
2077 i=fmt_str(buf,"<title>Gatling Server Status</title>\n<h2>Open Connections</h2>\nHTTP: ");
2078 i+=fmt_ulong(buf+i,http_connections);
2079 i+=fmt_str(buf+i,"<br>\nHTTPS: ");
2080 i+=fmt_ulong(buf+i,https_connections);
2081 i+=fmt_str(buf+i,"<br>\nFTP: ");
2082 i+=fmt_ulong(buf+i,ftp_connections);
2083 i+=fmt_str(buf+i,"<br>\nSMB: ");
2084 i+=fmt_ulong(buf+i,smb_connections);
2085 i+=fmt_str(buf+i,"<p>\n<h2>Per second:</h2>Connections: ");
2086 i+=fmt_ulong(buf+i,cps);
2087 i+=fmt_str(buf+i,"<br>\nRequests: ");
2088 i+=fmt_ulong(buf+i,rps);
2089 i+=fmt_str(buf+i,"<br>\nEvents: ");
2090 i+=fmt_ulong(buf+i,eps);
2091 i+=fmt_str(buf+i,"<br>\nInbound Traffic: ");
2092 i+=fmt_ulong(buf+i,tin/1024);
2093 i+=fmt_str(buf+i," KiB<br>\nOutbound Traffic: ");
2094 i+=fmt_ulong(buf+i,tout/1024);
2095 i+=fmt_str(buf+i," KiB");
2096 buf[i]=0;
2097 l=i;
2098
2099 i=fmt_str(nh,"HTTP/1.1 200 OK\r\nServer: " RELEASE "\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: ");
2100 i+=fmt_ulong(nh+i,l);
2101 i+=fmt_str(nh+i,"\r\n\r\n");
2102 i+=fmt_str(nh+i,buf);
2103 iob_addbuf_free(&h->iob,nh,i);
2104 io_wantwrite(s);
2105
2106 if (logging) {
2107 char buf[IP6_FMT+10];
2108 int x;
2109 char* tmp;
2110 x=fmt_ip6c(buf,h->myip);
2111 x+=fmt_str(buf+x,"/");
2112 x+=fmt_ulong(buf+x,h->myport);
2113 buf[x]=0;
2114 #ifdef SUPPORT_HTTPS
2115 if (h->t == HTTPSREQUEST)
2116 buffer_puts(buffer_1,"HTTPS/");
2117 #endif
2118 buffer_puts(buffer_1,"GET ");
2119 buffer_putulong(buffer_1,s);
2120 buffer_puts(buffer_1," ");
2121 buffer_putlogstr(buffer_1,"server-status");
2122 buffer_puts(buffer_1," ");
2123 buffer_putulonglong(buffer_1,l);
2124 buffer_puts(buffer_1," ");
2125 buffer_putlogstr(buffer_1,(tmp=http_header(h,"User-Agent"))?tmp:"[no_user_agent]");
2126 buffer_puts(buffer_1," ");
2127 buffer_putlogstr(buffer_1,(tmp=http_header(h,"Referer"))?tmp:"[no_referrer]");
2128 buffer_puts(buffer_1," ");
2129 buffer_putlogstr(buffer_1,(tmp=http_header(h,"Host"))?tmp:buf);
2130 buffer_putsflush(buffer_1,"\n");
2131 }
2132 }
2133
2134 static int is_private_ip(const unsigned char* ip) {
2135 if (ip6_isv4mapped(ip))
2136 return byte_equal(ip+12,4,ip4loopback) || /* localhost */
2137 (ip[12]==10) || /* rfc1918 */
2138 (ip[12]==192 && ip[13]==168) ||
2139 (ip[12]==172 && (ip[13]>=16 && ip[13]<=31));
2140 return byte_equal(ip,16,V6loopback) || (ip[0]==0xfe && (ip[1]==0x80 || ip[1]==0xc0));
2141 /* localhost or link-local or site-local */
2142 }
2143 #endif
2144
2145 static void get_md5_randomness(const uint8_t* randomness,size_t len,char digest[16]) {
2146 MD5_CTX temp;
2147 static int initialized;
2148 if (!initialized) {
2149 int fd=open("/dev/urandom",O_RDONLY);
2150 unsigned char buf[16];
2151 read(fd,buf,16);
2152 close(fd);
2153 MD5Init(&md5_ctx);
2154 MD5Update(&md5_ctx,buf,16);
2155 initialized=1;
2156 }
2157 MD5Update(&md5_ctx,randomness,len);
2158 memcpy(&temp,&md5_ctx,sizeof(temp));
2159 MD5Final((uint8_t*)digest,&temp);
2160 }
2161
2162 size_t scan_range(const char* s,unsigned long long* x,size_t maxranges,unsigned long long filesize) {
2163 unsigned long long start,end;
2164 size_t used=0;
2165 /* possible formats: "-5", "1-3", "10-" */
2166 while (*s!='\r' && *s!='\n' && *s) {
2167 if (isdigit(*s))
2168 s+=scan_ulonglong(s,&start);
2169 else
2170 start=0;
2171 end=filesize;
2172 if (*s=='-') {
2173 ++s;
2174 if (isdigit(*s))
2175 s+=scan_ulonglong(s,&end);
2176 }
2177 if (used+1<maxranges) {
2178 x[used]=start;
2179 x[used+1]=end;
2180 } else
2181 return 0;
2182 used+=2;
2183 if (*s==',') {
2184 ++s;
2185 continue;
2186 }
2187 if (*s=='\r' || *s=='\n' || *s==0)
2188 return used;
2189 }
2190 return 0;
2191 }
2192
2193 static int mytolower(int a) {
2194 return a>='A' && a<='Z' ? a-'A'+'a' : a;
2195 }
2196
2197 static int header_diff(const char* s,const char* t) {
2198 /* like str_diff but s may also end with '\r' or '\n' */
2199 register int j;
2200 for (;;) {
2201 if ((j=(mytolower(*s)-mytolower(*t)))) break;
2202 if (!*t) break;
2203 ++s; ++t;
2204 }
2205 if (*s=='\r' || *s=='\n') j=-*t;
2206 return j;
2207 }
2208
2209 void httpresponse(struct http_data* h,int64 s,long headerlen) {
2210 enum Method method;
2211 char* c;
2212 const char* m;
2213 time_t ims=0;
2214 unsigned long long range_first,range_last;
2215 h->filefd=-1;
2216
2217 ++rps1;
2218 array_cat0(&h->r); h->r.initialized--;
2219 c=array_start(&h->r);
2220 if (byte_equal(c,5,"GET /")) { method=GET; c+=4; }
2221 else if (byte_equal(c,6,"POST /")) { method=POST; c+=5; }
2222 #ifdef SUPPORT_PUT
2223 else if (byte_equal(c,5,"PUT /")) { method=PUT; c+=4; }
2224 #endif
2225 #ifdef SUPPORT_DAV
2226 else if (byte_equal(c,10,"PROPFIND /")) { method=PROPFIND; c+=9; }
2227 else if (byte_equal(c,9,"OPTIONS /")) { method=OPTIONS; c+=8; }
2228 // else if (byte_equal(c,8,"UNLOCK /")) { method=UNLOCK; c+=7; }
2229 // else if (byte_equal(c,6,"LOCK /")) { method=LOCK; c+=5; }
2230 #endif
2231 else if (byte_equal(c,6,"HEAD /")) { method=HEAD; c+=5; }
2232 else {
2233 e400:
2234 httperror(h,"400 Invalid Request","This server does not understand this HTTP verb.",0);
2235
2236 if (logging) {
2237 char numbuf[FMT_ULONG];
2238 numbuf[fmt_ulong(numbuf,s)]=0;
2239 buffer_putmflush(buffer_1,"error_400 ",numbuf,"\n");
2240 }
2241 goto fini;
2242
2243 }
2244 {
2245 char *d;
2246 int64 fd;
2247 struct stat ss;
2248 char* tmp;
2249 for (d=c; *d!=' '&&*d!='\t'&&*d!='\n'&&*d!='\r'; ++d) ;
2250 if (*d!=' ') goto e400;
2251 *d=0;
2252
2253 if ((m=http_header(h,"Connection"))) {
2254 if (!header_diff(m,"keep-alive"))
2255 h->keepalive=1;
2256 else
2257 h->keepalive=0;
2258 } else {
2259 if (byte_equal(d+1,8,"HTTP/1.0"))
2260 h->keepalive=0;
2261 else
2262 h->keepalive=1;
2263 }
2264
2265 if (c[0]!='/') goto e404;
2266 #ifdef SUPPORT_SERVERSTATUS
2267 if (!strcmp(c,"/server-status") && is_private_ip((const unsigned char*)h->myip)) {
2268 do_server_status(h,s);
2269 return;
2270 }
2271 #endif
2272 #ifdef SUPPORT_DAV
2273 if (method==OPTIONS) {
2274 char* nh=malloc(200);
2275 if (!nh) goto oom;
2276 char* c=nh;
2277
2278 c+=fmt_strm(c,"HTTP/1.1 200 OK\r\n"
2279 "Server: " RELEASE "\r\n",
2280 (ss.st_mode & S_IWOTH) ?
2281 "Allow: OPTIONS, GET, HEAD, PUT, POST, PROPFIND\r\n"
2282 "Public: OPTIONS, GET, HEAD, PUT, POST, PROPFIND\r\n" :
2283 "Allow: OPTIONS, GET, HEAD, POST, PROPFIND, LOCK, UNLOCK\r\n"
2284 "Public: OPTIONS, GET, HEAD, POST, PROPFIND, LOCK, UNLOCK\r\n",
2285 "DAV: 1,2,3\r\n"
2286 "Date: ");
2287 c+=fmt_httpdate(c,now.sec.x-4611686018427387914ULL);
2288 c+=fmt_str(c,"\r\nContent-Length: 0\r\n\r\n");
2289
2290 iob_addbuf_free(&h->iob,nh,c-nh);
2291
2292 goto fini;
2293 }
2294 #endif
2295 fd=http_openfile(h,c,&ss,s,method);
2296 if (fd==-1) {
2297 e404:
2298 #ifdef SUPPORT_FALLBACK_REDIR
2299 if (redir)
2300 do_redirect(h,c,s);
2301 #endif
2302 if (logging) {
2303 char buf[IP6_FMT+10];
2304 int x;
2305 x=fmt_ip6c(buf,h->myip);
2306 x+=fmt_str(buf+x,"/");
2307 x+=fmt_ulong(buf+x,h->myport);
2308 buf[x]=0;
2309 #ifdef SUPPORT_HTTPS
2310 if (h->t == HTTPSREQUEST)
2311 buffer_puts(buffer_1,"HTTPS/");
2312 #endif
2313 buffer_putm(buffer_1,methods[method],"/404 ");
2314 buffer_putulong(buffer_1,s);
2315 buffer_puts(buffer_1," ");
2316 buffer_putlogstr(buffer_1,c);
2317 buffer_puts(buffer_1," 0 ");
2318 buffer_putlogstr(buffer_1,(tmp=http_header(h,"User-Agent"))?tmp:"[no_user_agent]");
2319 buffer_puts(buffer_1," ");
2320 buffer_putlogstr(buffer_1,(tmp=http_header(h,"Referer"))?tmp:"[no_referrer]");
2321 buffer_puts(buffer_1," ");
2322 buffer_putlogstr(buffer_1,(tmp=http_header(h,"Host"))?tmp:buf);
2323 buffer_putsflush(buffer_1,"\n");
2324 }
2325 #ifdef SUPPORT_FALLBACK_REDIR
2326 if (redir)
2327 goto fini;
2328 #endif
2329 httperror(h,"404 Not Found","No such file or directory.",method==HEAD);
2330
2331 } else {
2332 char* filename=c;
2333 if (fd==-4) { /* redirect */
2334 if (h->hdrbuf || h->bodybuf) {
2335 iob_addbuf_free(&h->iob,h->hdrbuf,h->hlen);
2336 iob_addbuf_free(&h->iob,h->bodybuf,h->blen);
2337 h->hdrbuf=h->bodybuf=0;
2338 }
2339 } else if (fd==-5) {
2340 /* 401 -> log nothing. */
2341 } else if (fd==-2) {
2342 char* c;
2343 c=h->hdrbuf=(char*)malloc(250);
2344 if (!c)
2345 httperror(h,"500 Sorry","Out of Memory.",method==HEAD);
2346 else {
2347
2348 if (logging) {
2349 char buf[IP6_FMT+10];
2350 int x;
2351 x=fmt_ip6c(buf,h->myip);
2352 x+=fmt_str(buf+x,"/");
2353 x+=fmt_ulong(buf+x,h->myport);
2354 buf[x]=0;
2355 #ifdef SUPPORT_HTTPS
2356 if (h->t == HTTPSREQUEST)
2357 buffer_puts(buffer_1,"HTTPS/");
2358 #endif
2359 buffer_putm(buffer_1,methods[method]," ");
2360 buffer_putulong(buffer_1,s);
2361 buffer_puts(buffer_1," ");
2362 buffer_putlogstr(buffer_1,filename);
2363 buffer_puts(buffer_1," ");
2364 buffer_putulonglong(buffer_1,h->blen);
2365 buffer_puts(buffer_1," ");
2366 buffer_putlogstr(buffer_1,(tmp=http_header(h,"User-Agent"))?tmp:"[no_user_agent]");
2367 buffer_puts(buffer_1," ");
2368 buffer_putlogstr(buffer_1,(tmp=http_header(h,"Referer"))?tmp:"[no_referrer]");
2369 buffer_puts(buffer_1," ");
2370 buffer_putlogstr(buffer_1,(tmp=http_header(h,"Host"))?tmp:buf);
2371 buffer_putsflush(buffer_1,"\n");
2372 }
2373
2374 #ifdef SUPPORT_DAV
2375 if (method==PROPFIND)
2376 c+=fmt_str(c,"HTTP/1.1 207 Multi Status\r\nContent-Type: text/xml\r\nConnection: ");
2377 else
2378 #endif
2379 c+=fmt_str(c,"HTTP/1.1 200 Here you go\r\nContent-Type: text/html; charset=utf-8\r\nConnection: ");
2380 c+=fmt_str(c,h->keepalive?"keep-alive":"close");
2381 c+=fmt_str(c,"\r\nServer: " RELEASE "\r\nContent-Length: ");
2382 c+=fmt_ulong(c,h->blen);
2383 if (h->encoding!=NORMAL) {
2384 c+=fmt_str(c,"\r\nContent-Encoding: ");
2385 #if defined(SUPPORT_BROTLI)
2386 if (h->encoding==BROTLI)
2387 c+=fmt_str(c,"br");
2388 else
2389 #endif
2390 #if defined(SUPPORT_BZIP2)
2391 if (h->encoding==BZIP2)
2392 c+=fmt_str(c,"bzip2");
2393 else
2394 #endif
2395 c+=fmt_str(c,"gzip");
2396 }
2397 c+=fmt_str(c,"\r\n\r\n");
2398
2399 h->hlen=c-h->hdrbuf;
2400 iob_addbuf_free(&h->iob,h->hdrbuf,h->hlen);
2401 if (method==HEAD)
2402 free(h->bodybuf);
2403 else
2404 iob_addbuf_free(&h->iob,h->bodybuf,h->blen);
2405 }
2406 #ifdef SUPPORT_PROXY
2407 } else if (fd==-3) {
2408 return;
2409 #endif
2410 } else {
2411 char* multirange=0;
2412 unsigned long long ranges[20];
2413 unsigned long long bytes=0;
2414 size_t n=0,headersize=0;
2415 char hex[16];
2416 #ifdef DEBUG
2417 if (logging) {
2418 buffer_puts(buffer_1,"filefd ");
2419 buffer_putulong(buffer_1,s);
2420 buffer_putspace(buffer_1);
2421 buffer_putulong(buffer_1,fd);
2422 buffer_putnlflush(buffer_1);
2423 }
2424 #endif
2425 h->filefd=fd;
2426 range_first=0; range_last=ss.st_size;
2427
2428 #ifdef SUPPORT_DAV
2429 if (method==PROPFIND) {
2430 array x;
2431 char* baseurl;
2432 char* host=http_header(h,"Host");
2433 size_t i,j;
2434 char buf[FMT_ULONG*2];
2435 if (host) {
2436 for (i=0; isalnum(host[i]) || host[i]=='-' || host[i]=='.' || host[i]==':'; ++i) ;
2437 } else
2438 i=FMT_IP6+sizeof(":65535");
2439 memset(&x,0,sizeof(x));
2440 baseurl=alloca(i+sizeof("https://")+strlen(c)+2);
2441 #ifdef SUPPORT_HTTPS
2442 if (h->t==HTTPSREQUEST)
2443 j=fmt_str(baseurl,"https://");
2444 else
2445 #endif
2446 j=fmt_str(baseurl,"http://");
2447 if (host)
2448 j+=fmt_copybytes(baseurl+j,host,i);
2449 else {
2450 j+=fmt_ip6c(baseurl+j,h->myip);
2451 baseurl[j++]=':';
2452 j+=fmt_ulong(baseurl+j,h->myport);
2453 }
2454 j+=fmt_str(baseurl+j,c);
2455 baseurl[j]=0;
2456 array_cats(&x,"<?xml version=\"1.0\" encoding=\"utf-8\"?><D:multistatus xmlns:D=\"DAV:\"><D:response><D:href>");
2457 catxmlencoded(&x,baseurl);
2458
2459 array_cats(&x,"</D:href><D:propstat><D:status>HTTP/1.1 200 OK</D:status><D:prop>");
2460 if (S_ISDIR(ss.st_mode))
2461 array_cats(&x,"<D:getcontenttype/>");
2462 else {
2463 array_cats(&x,"<D:getcontenttype>");
2464 array_cats(&x,mimetype(c,-1));
2465 array_cats(&x,"</D:getcontenttype>");
2466 }
2467 array_cats(&x,"<D:getlastmodified>");
2468
2469 buf[fmt_httpdate(buf,ss.st_mtime)]=0;
2470 array_cats(&x,buf);
2471 array_cats(&x,"</D:getlastmodified><D:lockdiscovery/><D:ishidden>0</D:ishidden><D:supportedlock><D:lockentry><D:lockscope><D:exclusive/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry><D:lockentry><D:lockscope><D:shared/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry></D:supportedlock>");
2472
2473 if (S_ISDIR(ss.st_mode))
2474 array_cats(&x,"<D:getetag/><D:displayname>");
2475 else {
2476 array_cats(&x,"<D:getetag>\"");
2477 buf[j=fmt_ulong(buf,ss.st_dev)]='.';
2478 array_catb(&x,buf,j+1);
2479 j=fmt_ulong(buf,ss.st_ino);
2480 array_catb(&x,buf,j);
2481 array_cats(&x,"\"</D:getetag><D:displayname>");
2482 }
2483 {
2484 const char* name=strrchr(baseurl,'/');
2485 catxmlencoded(&x,name+1);
2486 }
2487 array_cats(&x,"</D:displayname><D:getcontentlanguage/><D:getcontentlength>");
2488 buf[fmt_ulonglong(buf,ss.st_size)]=0;
2489 array_cats(&x,buf);
2490
2491 array_cats(&x,S_ISDIR(ss.st_mode) ?
2492 "</D:getcontentlength><D:iscollection>1</D:iscollection><D:creationdate>" :
2493 "</D:getcontentlength><D:iscollection>0</D:iscollection><D:creationdate>" );
2494
2495 buf[fmt_iso8601(buf,ss.st_ctime)]=0;
2496 array_cats(&x,buf);
2497 array_cats(&x,S_ISDIR(ss.st_mode) ?
2498 "</D:creationdate><D:resourcetype><D:collection/></D:resourcetype></D:prop></D:propstat></D:response>" :
2499 "</D:creationdate><D:resourcetype/></D:prop></D:propstat></D:response>");
2500 array_cats(&x,"</D:multistatus>");
2501 c=h->hdrbuf=malloc(200+array_bytes(&x));
2502 if (!c) goto oom;
2503 c+=fmt_str(c,"HTTP/1.1 207 Multi Status\r\nContent-Type: text/xml\r\nServer: " RELEASE "\r\nDate: ");
2504 c+=fmt_httpdate(c,now.sec.x-4611686018427387914ULL);
2505 c+=fmt_str(c,"\r\nContent-Length: ");
2506 c+=fmt_ulong(c,array_bytes(&x));
2507 c+=fmt_str(c,"\r\n\r\n");
2508 c+=fmt_copybytes(c,array_start(&x),array_bytes(&x));
2509 iob_addbuf_free(&h->iob,h->hdrbuf,c - h->hdrbuf);
2510 array_reset(&x);
2511 goto fini;
2512 }
2513 #endif
2514
2515 if ((c=http_header(h,"If-Modified-Since")))
2516 if ((unsigned char)(c[scan_httpdate(c,&ims)])>' ')
2517 ims=0;
2518 if ((c=http_header(h,"Range"))) {
2519 if (byte_equal(c,6,"bytes=")) {
2520 size_t i;
2521 c+=6;
2522 n=scan_range(c,ranges,sizeof(ranges)/sizeof(ranges[0])/2,ss.st_size-1);
2523
2524 /* the ranges could still be bogus, i.e. 4-2, or the sum
2525 * could be more than just sending the whole file. */
2526 for (i=0; i<n; i+=2) {
2527 if (ranges[i]>=ranges[i+1] || // zero or less size
2528 bytes+(ranges[i+1]-ranges[i]+1) < bytes) { /* int overflow */
2529 n=0;
2530 break;
2531 }
2532 bytes+=(ranges[i+1]-ranges[i]+1);
2533 if (bytes>ss.st_size+1) {
2534 n=0;
2535 break;
2536 }
2537 }
2538
2539 if (n) {
2540 /* n will be a multiple of two here */
2541 if (n==2) {
2542 /* just one range, common case */
2543 range_first=ranges[0];
2544 range_last=ranges[1];
2545 } else {
2546 size_t overhead=(sizeof("\r\n--eba5aaeb1a3913f0ed90259cf85a1ea7\r\nContent-Type: \r\nContent-Range: bytes -/\r\n\r\n")-1+
2547 strlen(h->mimetype)+fmt_ulonglong(NULL,ss.st_size))*(n/2)+
2548 sizeof("\r\n--eba5aaeb1a3913f0ed90259cf85a1ea7--\r\n")-1;
2549 for (i=0; i<n; i+=2)
2550 overhead+=fmt_ulonglong(NULL,ranges[i])+fmt_ulonglong(NULL,ranges[i+1]);
2551 if (bytes+overhead<bytes)
2552 goto rangekaputt;
2553 headersize=overhead;
2554 // printf("bytes=%llu, overhead=%zu, header size=%zu\n",bytes,overhead,headersize);
2555 bytes+=overhead;
2556 if (bytes<ss.st_size)
2557 multirange=c;
2558 }
2559 } else {
2560 rangekaputt:
2561 #ifdef DEBUG
2562 if (logging) {
2563 buffer_puts(buffer_1,"bad_range_close ");
2564 buffer_putulong(buffer_1,s);
2565 buffer_putspace(buffer_1);
2566 buffer_putulong(buffer_1,fd);
2567 buffer_putnlflush(buffer_1);
2568 }
2569 #endif
2570 io_close(h->filefd); h->filefd=-1;
2571 httperror(h,"416 Bad Range","The requested range can not be satisfied.",method==HEAD);
2572 goto fini;
2573 }
2574 }
2575 }
2576 if (range_last<range_first) {
2577 /* rfc2616, page 123 */
2578 range_first=0; range_last=ss.st_size;
2579 }
2580 if (range_last>ss.st_size) range_last=ss.st_size;
2581
2582 c=h->hdrbuf=(char*)malloc(500);
2583 if (!c) { oom: httperror(h,"500 Sorry","Out of Memory.",method==HEAD); goto fini; }
2584 if (ss.st_mtime<=ims) {
2585 c+=fmt_str(c,"HTTP/1.1 304 Not Changed");
2586 method=HEAD; range_last=range_first;
2587 io_close(fd); fd=-1;
2588 multirange=0;
2589 } else {
2590 if (multirange || range_first || range_last!=ss.st_size)
2591 c+=fmt_str(c,"HTTP/1.1 206 Partial Content");
2592 else
2593 c+=fmt_str(c,"HTTP/1.1 200 Coming Up");
2594 }
2595
2596 c+=fmt_str(c,"\r\nAccept-Ranges: bytes\r\nServer: " RELEASE "\r\nContent-Type: ");
2597 if (multirange) {
2598 c+=fmt_str(c,"multipart/byteranges; boundary=");
2599 get_md5_randomness((uint8_t*)multirange,str_chr(multirange,'\n'),hex);
2600 c+=fmt_hexdump(c,hex,16);
2601 c+=fmt_str(c,"\r\nContent-Length: ");
2602 c+=fmt_ulonglong(c,bytes);
2603 } else {
2604 int add=0;
2605 c+=fmt_str(c,h->mimetype);
2606 c+=fmt_str(c,"\r\nContent-Length: ");
2607 if (range_last==ss.st_size && range_last>0) range_last-=(add=1);
2608 c+=fmt_ulonglong(c,range_last-range_first+add);
2609 }
2610
2611 c+=fmt_str(c,"\r\nDate: ");
2612 c+=fmt_httpdate(c,now.sec.x-4611686018427387914ULL);
2613
2614 c+=fmt_str(c,"\r\nLast-Modified: ");
2615 c+=fmt_httpdate(c,ss.st_mtime);
2616 if (h->encoding!=NORMAL) {
2617 c+=fmt_str(c,"\r\nContent-Encoding: ");
2618 #ifdef SUPPORT_BROTLI
2619 if (h->encoding==BROTLI)
2620 c+=fmt_str(c,"br");
2621 else
2622 #endif
2623 #ifdef SUPPORT_BZIP2
2624 if (h->encoding==BZIP2)
2625 c+=fmt_str(c,"bzip2");
2626 else
2627 #endif
2628 c+=fmt_str(c,"gzip");
2629 }
2630 if (method!=HEAD && (range_first || range_last!=ss.st_size)) {
2631 c+=fmt_str(c,"\r\nContent-Range: bytes ");
2632 c+=fmt_ulonglong(c,range_first);
2633 c+=fmt_str(c,"-");
2634 c+=fmt_ulonglong(c,range_last);
2635 c+=fmt_str(c,"/");
2636 c+=fmt_ulonglong(c,ss.st_size);
2637 }
2638 if (range_first>ss.st_size) {
2639 free(h->hdrbuf);
2640 httperror(h,"416 Bad Range","The requested range can not be satisfied.",method==HEAD);
2641 buffer_puts(buffer_1,"error_416 ");
2642 } else {
2643 c+=fmt_str(c,"\r\nConnection: ");
2644 c+=fmt_str(c,h->keepalive?"keep-alive":"close");
2645 c+=fmt_str(c,"\r\n\r\n");
2646 iob_addbuf_free(&h->iob,h->hdrbuf,c - h->hdrbuf);
2647 if (method!=HEAD) {
2648 if (multirange) {
2649 char* buf=malloc(headersize+5);
2650 char* c,* x;
2651 size_t i,flen;
2652
2653 if (!buf) goto oom;
2654 c=buf+fmt_str(buf,"\r\n--");
2655 c+=fmt_hexdump(c,hex,16);
2656 c+=fmt_str(c,"--\r\n");
2657 flen=c-buf;
2658
2659 x=c;
2660
2661 for (i=0; i<n; i+=2) {
2662 c=x+fmt_str(x,"\r\n--");
2663 c+=fmt_hexdump(c,hex,16);
2664 c+=fmt_str(c,"\r\nContent-Type: ");
2665 c+=fmt_str(c,h->mimetype);
2666 c+=fmt_str(c,"\r\nContent-Range: bytes ");
2667 c+=fmt_ulonglong(c,ranges[i]);
2668 c+=fmt_str(c,"-");
2669 c+=fmt_ulonglong(c,ranges[i+1]);
2670 c+=fmt_str(c,"/");
2671 c+=fmt_ulonglong(c,ss.st_size);
2672 c+=fmt_str(c,"\r\n\r\n");
2673 iob_addbuf(&h->iob,x,c-x);
2674 if (i+2<n)
2675 iob_addfile(&h->iob,fd,ranges[i],ranges[i+1]-ranges[i]+1);
2676 else
2677 iob_addfile_close(&h->iob,fd,ranges[i],ranges[i+1]-ranges[i]+1);
2678 x=c;
2679 }
2680 iob_addbuf_free(&h->iob,buf,flen);
2681 } else
2682 iob_addfile_close(&h->iob,fd,range_first,range_last-range_first+(range_last>range_first));
2683 } else
2684 if (fd!=-1) io_close(fd);
2685 if (logging) {
2686 if (h->hdrbuf[9]=='3') {
2687 buffer_putm(buffer_1,methods[method],"/304 ");
2688 } else {
2689 buffer_putm(buffer_1,methods[method]," ");
2690 }
2691 }
2692 }
2693
2694 if (logging) {
2695 char buf[IP6_FMT+10];
2696 int x;
2697 x=fmt_ip6c(buf,h->myip);
2698 x+=fmt_str(buf+x,"/");
2699 x+=fmt_ulong(buf+x,h->myport);
2700 buf[x]=0;
2701 buffer_putulong(buffer_1,s);
2702 buffer_puts(buffer_1," ");
2703 buffer_putlogstr(buffer_1,filename);
2704 switch (h->encoding) {
2705 case GZIP: buffer_puts(buffer_1,".gz"); break;
2706 #ifdef SUPPORT_BROTLI
2707 case BROTLI: buffer_puts(buffer_1,".br"); break;
2708 #endif
2709 #ifdef SUPPORT_BZIP2
2710 case BZIP2: buffer_puts(buffer_1,".bz2");
2711 #endif
2712 case NORMAL: break;
2713 }
2714 buffer_puts(buffer_1," ");
2715 buffer_putulonglong(buffer_1,range_last-range_first);
2716 buffer_puts(buffer_1," ");
2717 buffer_putlogstr(buffer_1,(tmp=http_header(h,"User-Agent"))?tmp:"[no_user_agent]");
2718 buffer_puts(buffer_1," ");
2719 buffer_putlogstr(buffer_1,(tmp=http_header(h,"Referer"))?tmp:"[no_referrer]");
2720 buffer_puts(buffer_1," ");
2721 buffer_putlogstr(buffer_1,(tmp=http_header(h,"Host"))?tmp:buf);
2722 buffer_putsflush(buffer_1,"\n");
2723 }
2724 h->filefd=-1; /* iob_addfile_close closes the file for us, we don't want cleanup to close it again */
2725 }
2726 }
2727 }
2728 fini:
2729 io_dontwantread(s);
2730 io_wantwrite(s);
2731 }
2732
2733
2734 #ifdef SUPPORT_PROXY
2735 int handle_read_proxypost(int64 i,struct http_data* h) {
2736 switch (proxy_is_readable(i,h)) {
2737 case -1:
2738 {
2739 if (logging) {
2740 char numbuf[FMT_ULONG];
2741 numbuf[fmt_ulong(numbuf,i)]=0;
2742
2743 buffer_putmflush(buffer_1,"proxy_read_error ",numbuf," ",strerror(errno),"\nclose/acceptfail ",numbuf,"\n");
2744 }
2745 cleanup(i);
2746 }
2747 break;
2748 case -4:
2749 return -4;
2750 }
2751 return 0;
2752 }
2753
2754 void handle_read_httppost(int64 i,struct http_data* h) {
2755 /* read POST data. */
2756 if (h->still_to_copy) {
2757 if (array_bytes(&h->r)>0) {
2758 io_dontwantread(i);
2759 io_wantwrite(h->buddy);
2760 } else if (read_http_post(i,h)==-1) {
2761 if (logging) {
2762 char a[FMT_ULONG];
2763 a[fmt_ulong(a,i)]=0;
2764 buffer_putmflush(buffer_1,"http_postdata_read_error ",a," ",strerror(errno),"\nclose/acceptfail ",a,"\n");
2765 }
2766 cleanup(i);
2767 } else {
2768 io_dontwantread(i);
2769 io_wantwrite(h->buddy);
2770 }
2771 } else {
2772 /* should not happen */
2773 io_dontwantread(i);
2774 }
2775 }
2776
2777 int handle_write_proxypost(int64 i,struct http_data* h) {
2778 struct http_data* H=io_getcookie(h->buddy);
2779 /* do we have some POST data to write? */
2780 if (!array_bytes(&H->r)) {
2781 io_dontwantwrite(i); /* nope */
2782 io_wantread(h->buddy);
2783 } else {
2784 // printf(" yeah!\n");
2785 if (H) {
2786 char* c=array_start(&H->r);
2787 long alen=array_bytes(&H->r);
2788 long l;
2789
2790 if (h->proxyproto!=FASTCGI) {
2791 /* this looks like the right thing to sanity-check but it is not
2792 * in the fastcgi case. For fastcgi, we have to append an 8
2793 * byte header for each chunk, and if it's the last chunk, we
2794 * have to append another 8 byte header. So alen can be 8 or 16
2795 * bytes off. */
2796 if (alen>h->still_to_copy) alen=h->still_to_copy;
2797 }
2798
2799 if (alen==0) goto nothingmoretocopy;
2800 l=write(i,c,alen);
2801 if (l<1) {
2802 /* ARGH! Proxy crashed! *groan* */
2803 if (logging) {
2804 buffer_puts(buffer_1,"http_postdata_write_error ");
2805 buffer_putulong(buffer_1,i);
2806 buffer_putspace(buffer_1);
2807 buffer_puterror(buffer_1);
2808 buffer_puts(buffer_1,"\nclose/acceptfail ");
2809 buffer_putulong(buffer_1,i);
2810 buffer_putnlflush(buffer_1);
2811 }
2812 cleanup(i);
2813 } else {
2814 byte_copy(c,array_bytes(&H->r)-l,c+l);
2815 array_truncate(&H->r,1,array_bytes(&H->r)-l);
2816 // printf("still_to_copy PROXYPOST write handler: %p %llu -> %llu\n",H,H->still_to_copy,H->still_to_copy-l);
2817 h->still_to_copy-=l;
2818 // printf("still_to_copy PROXYPOST write handler: %p %llu -> %llu\n",h,h->still_to_copy,h->still_to_copy-i);
2819 // h->still_to_copy-=i;
2820 if (alen-l==0) {
2821 /* we wrote everything we have in the buffer */
2822 io_dontwantwrite(i);
2823 /* check if we need to copy more data */
2824 if (h->still_to_copy)
2825 io_wantread(h->buddy);
2826 }
2827 if (h->still_to_copy==0) {
2828 /* we got all we asked for */
2829 nothingmoretocopy:
2830 io_dontwantwrite(i);
2831 io_wantread(i);
2832 io_dontwantread(h->buddy);
2833 io_wantwrite(h->buddy);
2834 }
2835 }
2836 }
2837 }
2838 return 0;
2839 }
2840
2841 static void handle_write_error(int64 i,struct http_data* h,int64 r) {
2842 if (r==-1)
2843 io_eagain_write(i);
2844 else if (r<=0) {
2845 if (r==-3) {
2846 if (logging) {
2847 char a[FMT_ULONG];
2848 char r[FMT_ULONG];
2849 char s[FMT_ULONG];
2850 a[fmt_ulong(a,i)]=0;
2851 r[fmt_ulonglong(r,h->received)]=0;
2852 s[fmt_ulonglong(s,h->sent)]=0;
2853 buffer_putmflush(buffer_1,"socket_error ",a," ",strerror(errno),"\nclose/writefail ",a," ",r," ",s,"\n");
2854 }
2855 cleanup(i);
2856 return;
2857 }
2858 }
2859 if (h->buddy==-1) {
2860 if (logging) {
2861 char a[FMT_ULONG];
2862 char r[FMT_ULONG];
2863 char s[FMT_ULONG];
2864 a[fmt_ulong(a,i)]=0;
2865 r[fmt_ulonglong(r,h->received)]=0;
2866 s[fmt_ulonglong(s,h->sent)]=0;
2867 buffer_putmflush(buffer_1,"close/proxydone ",a," ",r," ",s,"\n");
2868 }
2869 cleanup(i);
2870 } else {
2871 io_dontwantwrite(i);
2872 io_wantread(h->buddy);
2873 }
2874 }
2875
2876 void handle_write_httppost(int64 i,struct http_data* h) {
2877 int64 r;
2878 #ifdef SUPPORT_HTTPS
2879 if (h->t==HTTPSPOST)
2880 r=iob_write(i,&h->iob,https_write_callback);
2881 else
2882 #endif
2883 r=iob_send(i,&h->iob);
2884 if (r>0) h->sent+=r;
2885 if (iob_bytesleft(&h->iob)==0) {
2886 iob_reset(&h->iob);
2887 #if 0
2888 /* this case is now handled in proxy_is_readable() now */
2889 struct http_data* peer=io_getcookie(h->buddy);
2890 if (peer && peer->hss.state==HSS_DONE) {
2891 int64 buddy=h->buddy;
2892 if (logging) {
2893 char numbuf[FMT_ULONG];
2894 char r[FMT_ULONG];
2895 char s[FMT_ULONG];
2896 numbuf[fmt_ulong(numbuf,buddy)]=0;
2897 r[fmt_ulonglong(r,h->received)]=0;
2898 s[fmt_ulonglong(s,h->sent)]=0;
2899 buffer_putmflush(buffer_1,"cgiproxy_reqdone ",numbuf," ",r," ",s,"\n");
2900 }
2901 h->buddy=peer->buddy=-1;
2902 cleanup(buddy);
2903 io_dontwantwrite(i);
2904 io_wantread(i);
2905 return;
2906 #ifdef SUPPORT_HTTPS
2907 #ifdef USE_OPENSSL
2908 SSL_shutdown(h->ssl);
2909 #endif
2910 #endif
2911 cleanup(i);
2912 } else {
2913 #endif
2914 /* The proxy has more data for us */
2915 io_dontwantwrite(i);
2916 io_wantread(h->buddy);
2917 #if 0
2918 }
2919 #endif
2920 }
2921 handle_write_error(i,h,r);
2922 }
2923
2924 void handle_write_proxyslave(int64 i,struct http_data* h) {
2925 /* the connect() to the proxy just finished or failed */
2926 struct http_data* H;
2927 H=io_getcookie(h->buddy);
2928 if (proxy_write_header(i,h)==-1) {
2929 kaputt:
2930 if (logging) {
2931 buffer_puts(buffer_1,"proxy_connect_error ");
2932 buffer_putulong(buffer_1,i);
2933 buffer_putspace(buffer_1);
2934 buffer_puterror(buffer_1);
2935 buffer_puts(buffer_1,"\nclose/connectfail ");
2936 buffer_putulong(buffer_1,i);
2937 buffer_putnlflush(buffer_1);
2938 }
2939 H->buddy=-1;
2940 httperror(H,"502 Gateway Broken","Request relaying error.",0); /* FIXME, what about HEAD? */
2941 h->buddy=-1;
2942 free(h);
2943 io_close(i);
2944 return;
2945 }
2946 /* it worked. We wrote the header. Now see if there is
2947 * POST data to write. h->still_to_copy is Content-Length. */
2948 changestate(h,PROXYPOST);
2949 array_trunc(&h->r);
2950 if (h->still_to_copy) {
2951 size_t l=h->still_to_copy;
2952 if (l>array_bytes(&H->r)) l=array_bytes(&H->r);
2953 if (l) {
2954 /* for FASTCGI, we need to add a header */
2955 if (H->proxyproto==FASTCGI) {
2956 char* tmp,* cur;
2957 cur=array_start(&H->r);
2958 while (l) { /* this basically can't happen */
2959 size_t chunk=l;
2960 if (chunk>32768) chunk=32768;
2961 tmp=malloc(8);
2962 if (!tmp) goto kaputt;
2963 memcpy(tmp,"\x01\x05\x00\x01\x00\x00\x00\x00",8);
2964 tmp[4]=chunk>>8;
2965 tmp[5]=chunk&0xff;
2966 if (!iob_addbuf_free(&H->iob,tmp,8) ||
2967 !iob_addbuf(&H->iob,cur,chunk))
2968 goto kaputt;
2969 cur+=chunk;
2970 l-=chunk;
2971 }
2972 } else {
2973 if (!iob_addbuf(&H->iob,array_start(&H->r),l))
2974 goto kaputt;
2975 H->r.initialized=0;
2976 }
2977 }
2978 handle_write_error(i,H,iob_send(i,&H->iob));
2979 } else {
2980 io_dontwantwrite(i);
2981 io_wantread(i);
2982 }
2983 }
2984
2985 #endif
2986
2987 #ifdef SUPPORT_CGI
2988 /* gatling is expected to have 10000 file descriptors open.
2989 * so forking off CGIs is bound to be expensive because after the fork
2990 * all the file descriptors have to be closed. So this code makes
2991 * gatling fork off a child first thing in main(). gatling has a Unix
2992 * domain socket open to the child. When gatling needs to start a CGI,
2993 * it sends a message to the child. The child then creates a new socket
2994 * pair, sets up the CGI environment, forks a grandchild, and passes the
2995 * socket to the grandchild back to gatling over the Unix domain socket. */
2996 char fsbuf[8192];
2997
2998 static const char *cgivars[] = {
2999 "GATEWAY_INTERFACE=",
3000 "SERVER_PROTOCOL=",
3001 "SERVER_SOFTWARE=",
3002 "SERVER_NAME=",
3003 "SERVER_PORT=",
3004 "REQUEST_METHOD=",
3005 "REQUEST_URI=",
3006 "SCRIPT_NAME=",
3007 "REMOTE_ADDR=",
3008 "REMOTE_PORT=",
3009 "REMOTE_IDENT=",
3010 "AUTH_TYPE=",
3011 "CONTENT_TYPE=",
3012 "CONTENT_LENGTH=",
3013 "QUERY_STRING=",
3014 "PATH_INFO=",
3015 "PATH_TRANSLATED=",
3016 "REMOTE_USER=",
3017 0
3018 };
3019
3020 int cgienvneeded(const char* httpreq,size_t reqlen) {
3021 int i,j,envc;
3022 for (i=envc=0; _envp[i]; ++i) {
3023 int found=0;
3024 if (str_start(_envp[i],"HTTP_"))
3025 found=1;
3026 else
3027 for (j=0; cgivars[j]; ++j)
3028 if (str_start(_envp[i],cgivars[j])) { found=1; break; }
3029 if (!found) ++envc;
3030 }
3031
3032 /* now collect all normal HTTP headers */
3033
3034 {
3035 const char* x=httpreq;
3036 const char* max=x+reqlen;
3037 for (;x<max && *x!='\n';++x) ; /* Skip GET */
3038 for (;x<max;++x)
3039 if (*x=='\n')
3040 ++envc;
3041 }
3042 return envc;
3043 }
3044
3045 extern int switch_uid();
3046
3047 void forkslave(int fd,buffer* in,int savedir,const char* chroot_to) {
3048 /* receive query, create socketpair, fork, set up environment,
3049 * pass file descriptor of our side of socketpair */
3050
3051 /* protocol:
3052 * in:
3053 * uint32 reqlen,dirlen,ralen
3054 * char httprequest[reqlen]
3055 * char dir[dirlen] // "www.fefe.de:80"
3056 * char remoteaddr[ralen]
3057 * uint16 remoteport
3058 * uint16 myport
3059 * out:
3060 * uint32 code,alen
3061 * char answer[alen]
3062
3063 * reqlen==0 means sshd mode. In this case a connection on port 443
3064 * came in, ssh forwarding is activated, and the timeout expired
3065 * before the client sent anything. Fork an sshd, and pass the
3066 * descriptor.
3067 */
3068
3069 uint32 i,reqlen,dirlen,code,ralen;
3070 uint16 port,myport;
3071 const char* msg="protocol error";
3072
3073 code=1;
3074 if (read(fd,(char*)&reqlen,4)!=4) goto error;
3075
3076 // printf("CGI: reqlen %u\n",reqlen);
3077
3078 #ifdef SUPPORT_HTTPS
3079 if (reqlen==0) { /* SSH MODE */
3080 int s,r;
3081 if ((s=io_receivefd(fd))==-1) goto error;
3082 #ifdef sgi
3083 r=fork();
3084 #else
3085 r=vfork();
3086 #endif
3087 if (r==-1) { close(s); return; } /* nothing we can do */
3088 if (r==0) { /* child */
3089 /* sshd might be something like /opt/diet/bin/sshd -u0 */
3090 /* so tokenize and add -i (inetd mode) */
3091 size_t args,i;
3092 char** argv;
3093 close(savedir);
3094 for (i=0,args=3; sshd[i]; ++i)
3095 if (sshd[i]==' ') ++args;
3096 argv=malloc(args*sizeof(argv[0]));
3097 argv[0]=sshd; args=1;
3098 for (i=0; sshd[i]; ++i) {
3099 if (sshd[i]==' ') {
3100 do {
3101 sshd[i]=0;
3102 ++i;
3103 } while (sshd[i]==' ');
3104 argv[args]=sshd+i;
3105 ++args;
3106 }
3107 }
3108 argv[args]="-i";
3109 argv[args+1]=0;
3110 dup2(s,0);
3111 dup2(s,1);
3112 close(s);
3113 close(fd);
3114 execvp(argv[0],argv);
3115 exit(127);
3116 }
3117 close(s);
3118 return;
3119 }
3120 #endif
3121
3122 if (buffer_get(in,(char*)&dirlen,4)==4 &&
3123 buffer_get(in,(char*)&ralen,4)==4) {
3124 // printf("CGI: dirlen %u, ralen %u\n",dirlen,ralen);
3125 if (dirlen<PATH_MAX && reqlen<MAX_HEADER_SIZE) {
3126 char* httpreq=alloca(reqlen+1);
3127 char* path=alloca(dirlen+1);
3128 char* remoteaddr=alloca(ralen+1);
3129 char* servername,* httpversion,* authtype,* contenttype,* contentlength,* remoteuser;
3130 char* path_translated;
3131 #ifdef SUPPORT_HTTPS
3132 char ssl;
3133 #endif
3134
3135 if (buffer_get(in,httpreq,reqlen) == reqlen &&
3136 buffer_get(in,path,dirlen) == dirlen &&
3137 buffer_get(in,remoteaddr,ralen) == ralen &&
3138 buffer_get(in,(char*)&port,2) == 2 &&
3139 buffer_get(in,(char*)&myport,2) == 2
3140 #ifdef SUPPORT_HTTPS
3141 && buffer_get(in,&ssl,1) == 1
3142 #endif
3143 ) {
3144
3145 httpreq[reqlen]=0;
3146 path[dirlen]=0;
3147 remoteaddr[ralen]=0;
3148
3149 if (dirlen==0 || chdir(path)==0) {
3150 /* now find cgi */
3151 char* cginame,* origcginame;
3152
3153 origcginame=cginame=httpreq+5+(httpreq[0]=='P');
3154 while (*cginame=='/') ++cginame;
3155 for (i=0; cginame+i<httpreq+reqlen; ++i)
3156 if (cginame[i]==' ' || cginame[i]=='\r' || cginame[i]=='\n') break;
3157
3158 if (cginame[i]==' ') {
3159 char* args,* pathinfo;
3160 int j,k;
3161 struct stat ss;
3162 cginame[i]=0; args=0; pathinfo=0;
3163
3164 httpversion=alloca(30+(j=str_chr(cginame+i+1,'\n')));
3165 k=fmt_str(httpversion,"SERVER_PROTOCOL=");
3166 byte_copy(httpversion+k,j,cginame+i+1);
3167 if (j && httpversion[k+j-1]=='\r') --j;
3168 httpversion[k+j]=0;
3169
3170 /* now cginame is something like "test/t.cgi?foo=bar"
3171 * but it might also be "test/t.cgi/something/else" or even
3172 * "test/t.cgi/something/?uid=23" */
3173
3174 /* extract ?foo=bar */
3175 j=str_chr(cginame,'?');
3176 if (cginame[j]=='?') {
3177 args=cginame+j+1;
3178 cginame[j]=0;
3179 i=j;
3180 }
3181
3182 /* now cginame is test/t.cgi/something */
3183 if (stat(cginame,&ss)==0)
3184 /* no "/something" */
3185 pathinfo=0;
3186 else {
3187 errno=0;
3188 /* try paths */
3189 for (j=0; j<i; ++j) {
3190 if (cginame[j]=='/') {
3191 cginame[j]=0;
3192 if (stat(cginame,&ss)==0 && !S_ISDIR(ss.st_mode)) {
3193 pathinfo=cginame+j+1;
3194 break;
3195 }
3196 cginame[j]='/';
3197 if (errno==ENOENT || errno==ENOTDIR) {
3198 msg="404";
3199 goto error;
3200 }
3201 }
3202 }
3203 }
3204
3205 {
3206 char* x=http_header_blob(httpreq,reqlen,"Host");
3207 if (x) {
3208 j=str_chr(x,'\n'); if (j && x[j-1]=='\r') { --j; }
3209 } else {
3210 x=remoteaddr; j=str_len(x);
3211 }
3212 servername=alloca(30+j+1);
3213 i=fmt_str(servername,"SERVER_NAME=");
3214 byte_copy(servername+i,j,x);
3215 servername[i+j]=0;
3216
3217 if (pathinfo) {
3218 size_t pilen;
3219 scan_urlencoded2(pathinfo,pathinfo,&pilen); pathinfo[pilen]=0;
3220 path_translated=alloca(PATH_MAX+30);
3221 i=fmt_str(path_translated,"PATH_TRANSLATED=");
3222 if (!realpath(pathinfo,path_translated+i))
3223 path_translated=0;
3224 } else
3225 path_translated=0;
3226
3227 x=http_header_blob(httpreq,reqlen,"Authorization");
3228 if (x) {
3229 int k;
3230 remoteuser=0;
3231
3232 j=str_chr(x,'\n'); if (j && x[j-1]=='\r') { --j; }
3233 k=str_chr(x,' ');
3234 if (k<j) {
3235 size_t dl;
3236 remoteuser=alloca(20+k-j);
3237 i=fmt_str(remoteuser,"REMOTE_USER=");
3238 scan_base64(x+k+1,remoteuser+i,&dl);
3239 remoteuser[i+dl]=0;
3240 dl=str_chr(remoteuser+i,':');
3241 if (remoteuser[i+dl]==':') remoteuser[i+dl]=0;
3242 j=k;
3243 }
3244 authtype=alloca(20+j+1);
3245 i=fmt_str(authtype,"AUTH_TYPE=");
3246 byte_copy(authtype+i,j,x);
3247 authtype[i+j]=0;
3248 } else
3249 authtype=remoteuser=0;
3250
3251 x=http_header_blob(httpreq,reqlen,"Content-Type");
3252 if (x) {
3253 j=str_chr(x,'\n'); if (j && x[j-1]=='\r') { --j; }
3254 contenttype=alloca(30+j+1);
3255 i=fmt_str(contenttype,"CONTENT_TYPE=");
3256 byte_copy(contenttype+i,j,x);
3257 contenttype[i+j]=0;
3258 } else
3259 contenttype=0;
3260
3261 x=http_header_blob(httpreq,reqlen,"Content-Length");
3262 if (x) {
3263 j=str_chr(x,'\n'); if (j && x[j-1]=='\r') { --j; }
3264 contentlength=alloca(30+j+1);
3265 i=fmt_str(contentlength,"CONTENT_LENGTH=");
3266 byte_copy(contentlength+i,j,x);
3267 contentlength[i+j]=0;
3268 } else
3269 contentlength=0;
3270 }
3271
3272 {
3273 int sock[2];
3274 if (socketpair(AF_UNIX,SOCK_STREAM,0,sock)==0) {
3275 #ifdef sgi
3276 int r=fork();
3277 #else
3278 int r=vfork();
3279 #endif
3280 if (r==-1)
3281 msg="vfork failed!";
3282 else if (r==0) {
3283 /* child */
3284 pid_t pid;
3285
3286 if (chroot_to) { chdir(chroot_to); chroot(chroot_to); }
3287 if (switch_uid()==-1) return;
3288
3289 close(savedir);
3290 code=0;
3291 write(fd,&code,4);
3292 write(fd,&code,4);
3293 pid=getpid();
3294 write(fd,&pid,sizeof(pid));
3295 if (cginame[(j=strlen(cginame))-1]=='/') { /* can happen in the -C+x case */
3296 char* temp=alloca(j+10);
3297 j=fmt_str(temp,cginame);
3298 j+=fmt_str(temp+j,"index.html");
3299 temp[j]=0;
3300 cginame=temp;
3301 }
3302 if (io_passfd(fd,sock[0])==0) {
3303 char* argv[]={cginame,0};
3304 char** envp;
3305 int envc;
3306
3307 envc=cgienvneeded(httpreq,reqlen);
3308
3309 envp=(char**)alloca(sizeof(char*)*(envc+21));
3310 envc=0;
3311
3312 #ifdef SUPPORT_HTTPS
3313 if (ssl)
3314 envp[envc++]="HTTPS=1";
3315 #endif
3316
3317 for (i=0; _envp[i]; ++i) {
3318 int found=0;
3319 if (str_start(_envp[i],"HTTP_"))
3320 found=1;
3321 else
3322 for (j=0; cgivars[j]; ++j)
3323 if (str_start(_envp[i],cgivars[j])) { found=1; break; }
3324 if (!found) envp[envc++]=_envp[i];
3325 }
3326 envp[envc++]="SERVER_SOFTWARE=" RELEASE;
3327 envp[envc++]=servername;
3328 envp[envc++]="GATEWAY_INTERFACE=CGI/1.1";
3329 envp[envc++]=httpversion;
3330
3331 envp[envc]=alloca(30);
3332 i=fmt_str(envp[envc],"SERVER_PORT=");
3333 i+=fmt_ulong(envp[envc]+i,myport);
3334 envp[envc][i]=0;
3335 ++envc;
3336
3337 envp[envc++]=httpreq[0]=='G'?"REQUEST_METHOD=GET":"REQUEST_METHOD=POST";
3338 if (pathinfo) envp[envc++]=fmt_strm_alloca("PATH_INFO=",pathinfo);
3339 if (path_translated) envp[envc++]=path_translated;
3340
3341 envp[envc]=alloca(30+str_len(origcginame));
3342 i=fmt_str(envp[envc],"SCRIPT_NAME=");
3343 i+=fmt_str(envp[envc]+i,origcginame-1);
3344 envp[envc][i]=0;
3345 ++envc;
3346
3347 if (args) {
3348 envp[envc]=alloca(30+str_len(args));
3349 i=fmt_str(envp[envc],"QUERY_STRING=");
3350 i+=fmt_str(envp[envc]+i,args);
3351 envp[envc][i]=0;
3352 ++envc;
3353 }
3354
3355 envp[envc]=alloca(30+str_len(remoteaddr));
3356 i=fmt_str(envp[envc],"REMOTE_ADDR=");
3357 i+=fmt_str(envp[envc]+i,remoteaddr);
3358 envp[envc][i]=0;
3359 ++envc;
3360
3361 envp[envc]=alloca(30);
3362 i=fmt_str(envp[envc],"REMOTE_PORT=");
3363 i+=fmt_ulong(envp[envc]+i,port);
3364 envp[envc][i]=0;
3365 ++envc;
3366
3367 if (authtype) envp[envc++]=authtype;
3368 if (remoteuser) envp[envc++]=remoteuser;
3369 if (contenttype) envp[envc++]=contenttype;
3370 if (contentlength) envp[envc++]=contentlength;
3371
3372 /* walk through all the headers in the http request and put them in the
3373 * environment, e.g. Host: foo:80 -> HTTP_HOST=foo:80 */
3374 {
3375 char* x=httpreq;
3376 char* max=x+reqlen;
3377 char* y;
3378
3379 for (;x<max && *x!='\n';++x) ; /* Skip GET */
3380
3381 for (y=++x;x<max;++x)
3382 if (*x=='\n') {
3383
3384 if (x>y && x[-1]=='\r') --x;
3385
3386 if (x>y) {
3387 char* s=alloca(x-y+7);
3388 int i,j;
3389
3390 byte_copy(s,5,"HTTP_");
3391 j=5;
3392 for (i=0; i<x-y; ++i) {
3393 if (y[i]==':') {
3394 ++i;
3395 while (i<x-y && (y[i]==' ' || y[i]=='\t')) ++i;
3396 s[j]='='; ++j;
3397 for (; i<x-y; ++i) {
3398 s[j]=y[i];
3399 ++j;
3400 }
3401 s[j]=0;
3402 envp[envc]=s;
3403 ++envc;
3404 break;
3405 }
3406 if (y[i]=='-')
3407 s[j]='_';
3408 else if (y[i]>='a' && y[i]<='z')
3409 s[j]=y[i]-'a'+'A';
3410 else if (y[i]>='A' && y[i]<='Z')
3411 s[j]=y[i];
3412 else {
3413 s=0; break;
3414 }
3415 ++j;
3416 }
3417 }
3418 if (*x=='\r') ++x;
3419 y=x+1;
3420 }
3421 }
3422
3423 envp[envc]=0;
3424
3425 dup2(sock[1],0);
3426 dup2(sock[1],1);
3427 dup2(sock[1],2);
3428 close(sock[0]); close(sock[1]); close(fd);
3429
3430 {
3431 char* path,* file;
3432 path=cginame;
3433 file=strrchr(path,'/');
3434 if (file) {
3435 *file=0;
3436 ++file;
3437 chdir(path);
3438 cginame=file;
3439 }
3440 /* attempt to defend against CVE-2014-6271 */
3441 {
3442 char* c;
3443 size_t i;
3444 for (i=0; (c=envp[i]); ++i) {
3445 while (*c && *c!='=') ++c;
3446 if (c[0]=='=' && c[1]=='(') c[1]='_';
3447 }
3448 }
3449 execve(cginame,argv,envp);
3450 }
3451 }
3452 {
3453 static char e[]="HTTP/1.0 503 Gateway Broken\r\nServer: " RELEASE "\r\nContent-Length: 15\r\nContent-Type: text/html\r\n\r\nGateway Broken.";
3454 write(1,e,sizeof(e)-1);
3455 }
3456 exit(127);
3457 } else {
3458 /* father */
3459 close(sock[0]);
3460 close(sock[1]);
3461 return;
3462 }
3463 } else
3464 msg="socketpair failed!";
3465 }
3466
3467 }
3468 }
3469 }
3470 }
3471 }
3472 error:
3473 if (write(fd,&code,4)!=4) exit(0);
3474 code=strlen(msg);
3475 write(fd,&code,4);
3476 {
3477 pid_t pid=0;
3478 write(fd,&pid,sizeof(pid));
3479 }
3480 write(fd,msg,code);
3481 }
3482 #endif
3483
3484