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 '&amp;', 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="&amp;"; n=5; break;
168       case '<': x="&lt;"; n=4; break;
169       case '>': x="&gt;"; 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