1 #define _POSIX_C_SOURCE 200809
2 #define _ATFILE_SOURCE
3 
4 #include "gatling.h"
5 
6 #include "fmt.h"
7 #include "ip6.h"
8 #include "buffer.h"
9 #include "case.h"
10 #include "socket.h"
11 #include "str.h"
12 #include "ip4.h"
13 #include "scan.h"
14 
15 #include <sys/types.h>
16 #include <fcntl.h>
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <sys/stat.h>
20 #include <errno.h>
21 #include <time.h>
22 #include <dirent.h>
23 #include <fnmatch.h>
24 #include <assert.h>
25 
26 #include "havealloca.h"
27 
28 #ifdef SUPPORT_FTP
29 /*
30   __ _
31  / _| |_ _ __
32 | |_| __| '_ \
33 |  _| |_| |_) |
34 |_|  \__| .__/
35         |_|
36 */
37 
38 int askforpassword;
39 
ftp_open(struct http_data * h,const char * s,int forreading,int sock,const char * what,struct stat * ss)40 static int ftp_open(struct http_data* h,const char* s,int forreading,int sock,const char* what,struct stat* ss) {
41   int l=h->ftppath?str_len(h->ftppath):0;
42   char* x=alloca(l+str_len(s)+5);
43   char* y;
44   int64 fd;
45 
46   /* first, append to path */
47   if (s[0]!='/' && h->ftppath)
48     y=x+fmt_str(x,h->ftppath);
49   else
50     y=x;
51   y+=fmt_str(y,"/");
52   y+=fmt_str(y,s);
53   if (y[-1]=='\n') --y;
54   if (y[-1]=='\r') --y;
55   *y=0;
56 
57   /* now reduce "//" and "/./" and "/[^/]+/../" to "/" */
58   canonpath(x);
59 
60   int dirfd=ip_vhost(h);
61   if (dirfd==-2) return -1;
62 
63   errno=0; fd=-1;
64   h->hdrbuf=forreading?"550 No such file or directory.\r\n":"550 Uploading not permitted here!\r\n";
65   if (x[1]) {
66     switch (forreading) {
67     case 1: open_for_reading(&fd,x+1,ss,dirfd); break;
68     case 0: open_for_writing(&fd,x+1,dirfd); break;
69     case 2: fd=mkdirat(dirfd,x+1,0777);
70 	    if (!fd) fchmodat(dirfd,x+1,0777,0);
71 	    break;
72     }
73   }
74 #ifdef DEBUG
75   if (forreading<2)
76     if (logging) {
77       buffer_puts(buffer_1,"ftp_open_file ");
78       buffer_putulong(buffer_1,sock);
79       buffer_putspace(buffer_1);
80       buffer_putulong(buffer_1,fd);
81       buffer_putspace(buffer_1);
82       buffer_puts(buffer_1,x+1);
83       buffer_putnlflush(buffer_1);
84     }
85 #endif
86 
87   if (logging && what) {
88     buffer_puts(buffer_1,what);
89     if (fd==-1) buffer_puts(buffer_1,"/404");
90     buffer_putspace(buffer_1);
91     buffer_putulong(buffer_1,sock);
92     buffer_putspace(buffer_1);
93     buffer_putlogstr(buffer_1,x[1]?x:"/");
94     buffer_putspace(buffer_1);
95   }
96   return fd;
97 }
98 
ftp_retrstor(struct http_data * h,const char * s,int64 sock,int forwriting)99 static int ftp_retrstor(struct http_data* h,const char* s,int64 sock,int forwriting) {
100   uint64 range_first,range_last;
101   struct stat ss;
102   struct http_data* b;
103 
104   char buf[IP6_FMT+10];
105   int x;
106   x=fmt_ip6c(buf,h->myip);
107   x+=fmt_str(buf+x,"/");
108   x+=fmt_ulong(buf+x,h->myport);
109   buf[x]=0;
110 
111   if (h->buddy==-1 || !(b=io_getcookie(h->buddy))) {
112     h->hdrbuf="425 Could not establish data connection.\r\n";
113     return -1;
114   }
115   if (b->filefd!=-1) { io_close(b->filefd); b->filefd=-1; }
116   b->filefd=ftp_open(h,s,forwriting^1,sock,forwriting?"STOR":"RETR",&ss);
117   if (forwriting) ss.st_size=0;
118   if (b->filefd==-1) {
119     if (logging) {
120       buffer_putulonglong(buffer_1,0);
121       buffer_putspace(buffer_1);
122       buffer_putlogstr(buffer_1,buf);
123       buffer_putnlflush(buffer_1);
124     }
125     return -1;
126   }
127 
128   if (!forwriting) {
129     if (fstat(b->filefd,&ss)==-1)
130       range_last=0;
131     else
132       range_last=ss.st_size;
133     range_first=h->ftp_rest; h->ftp_rest=0;
134     if (range_first>range_last) range_first=range_last;
135     iob_addfile_close(&b->iob,b->filefd,range_first,range_last-range_first);
136     if (logging) {
137       buffer_putulonglong(buffer_1,range_last-range_first);
138       buffer_putspace(buffer_1);
139     }
140     b->filefd=-1;	/* iob_addfile_close will close the fd, we don't want cleanup() to close it twice */
141   }
142 
143   if (logging) {
144     buffer_putlogstr(buffer_1,buf);
145     buffer_putnlflush(buffer_1);
146   }
147 
148   h->f=WAITCONNECT;
149   h->hdrbuf=malloc(100);
150   b->f=forwriting?UPLOADING:DOWNLOADING;
151   if (!h->hdrbuf) {
152     h->hdrbuf=(b->t==FTPSLAVE)?"125 go on\r\n":"150 go on\r\n";
153     return -1;
154   } else {
155     int i;
156     if (b->t==FTPSLAVE) {
157       i=fmt_str(h->hdrbuf,"125 go on (");
158       if (forwriting)
159 	io_wantread(h->buddy);
160       else
161 	io_wantwrite(h->buddy);
162       h->f=LOGGEDIN;
163     } else if (b->t==FTPACTIVE)
164       i=fmt_str(h->hdrbuf,"150 connecting (");
165     else
166       i=fmt_str(h->hdrbuf,"150 listening (");
167     if (forwriting)
168       i+=fmt_str(h->hdrbuf+i,"for upload)\r\n");
169     else {
170       i+=fmt_ulonglong(h->hdrbuf+i,ss.st_size);
171       i+=fmt_str(h->hdrbuf+i," bytes)\r\n");
172     }
173     h->hdrbuf[i]=0;
174   }
175 
176   return 0;
177 }
178 
ftp_mdtm(struct http_data * h,const char * s)179 static int ftp_mdtm(struct http_data* h,const char* s) {
180   struct stat ss;
181   int fd;
182   int i;
183   struct tm* t;
184   if ((fd=ftp_open(h,s,1,0,0,&ss))==-1) return -1;
185   io_close(fd);
186   t=gmtime(&ss.st_mtime);
187   h->hdrbuf=malloc(100);
188   if (!h->hdrbuf) {
189     h->hdrbuf="500 out of memory\r\n";
190     return -1;
191   }
192   i=fmt_str(h->hdrbuf,"213 ");
193   i+=fmt_2digits(h->hdrbuf+i,(t->tm_year+1900)/100);
194   i+=fmt_2digits(h->hdrbuf+i,(t->tm_year+1900)%100);
195   i+=fmt_2digits(h->hdrbuf+i,t->tm_mon+1);
196   i+=fmt_2digits(h->hdrbuf+i,t->tm_mday);
197   i+=fmt_2digits(h->hdrbuf+i,t->tm_hour);
198   i+=fmt_2digits(h->hdrbuf+i,t->tm_min);
199   i+=fmt_2digits(h->hdrbuf+i,t->tm_sec);
200   i+=fmt_str(h->hdrbuf+i,"\r\n");
201   h->hdrbuf[i]=0;
202   return 0;
203 }
204 
ftp_size(struct http_data * h,const char * s)205 static int ftp_size(struct http_data* h,const char* s) {
206   struct stat ss;
207   int fd;
208   int i;
209   if ((fd=ftp_open(h,s,1,0,0,&ss))==-1) return -1;
210   io_close(fd);
211   h->hdrbuf=malloc(100);
212   if (!h->hdrbuf) {
213     h->hdrbuf="500 out of memory\r\n";
214     return -1;
215   }
216   i=fmt_str(h->hdrbuf,"213 ");
217   i+=fmt_ulonglong(h->hdrbuf+i,ss.st_size);
218   i+=fmt_str(h->hdrbuf+i,"\r\n");
219   h->hdrbuf[i]=0;
220   return 0;
221 }
222 
223 
ftp_ls(array * x,const char * s,const struct stat * const ss,time_t now,const char * pathprefix)224 static void ftp_ls(array* x,const char* s,const struct stat* const ss,time_t now,const char* pathprefix) {
225   char buf[2048];
226   int i,j;
227   struct tm* t;
228   {
229     int i,m=ss->st_mode;
230     for (i=0; i<10; ++i) buf[i]='-';
231     if (S_ISDIR(m)) buf[0]='d'; else
232     if (S_ISLNK(m)) buf[0]='l';	/* other specials not supported */
233     if (m&S_IRUSR) buf[1]='r';
234     if (m&S_IWUSR) buf[2]='w';
235     if (m&S_IXUSR) buf[3]='x';
236     if (m&S_IRGRP) buf[4]='r';
237     if (m&S_IWGRP) buf[5]='w';
238     if (m&S_IXGRP) buf[6]='x';
239     if (m&S_IROTH) buf[7]='r';
240     if (m&S_IWOTH) buf[8]='w';
241     if (m&S_IXOTH) buf[9]='x';
242     buf[10]=' ';
243   }
244   array_catb(x,buf,11);
245 
246   i=j=fmt_ulong(buf,ss->st_nlink);
247   if (i<3) j=3;
248   array_catb(x,buf+100,fmt_pad(buf+100,buf,i,j,j));
249   array_cats(x," root     root     ");
250 
251   buf[i=fmt_ulonglong(buf,ss->st_size)]=' ';
252   j=++i; if (i<8) j=8;
253   array_catb(x,buf+100,fmt_pad(buf+100,buf,i,j,j));
254 
255   {
256     t=localtime(&ss->st_mtime);
257     array_catb(x,months+3*t->tm_mon,3);
258     array_cats(x," ");
259     array_catb(x,buf,fmt_2digits(buf,t->tm_mday));
260     array_cats(x," ");
261     if (ss->st_mtime<=now && ss->st_mtime>=now-60*60*12*356) {
262       array_catb(x,buf,fmt_2digits(buf,t->tm_hour));
263       array_cats(x,":");
264       array_catb(x,buf,fmt_2digits(buf,t->tm_min));
265     } else {
266       array_cats(x," ");
267       array_catb(x,buf,fmt_ulong0(buf,t->tm_year+1900,4));
268     }
269   }
270   array_cats(x," ");
271   array_cats(x,pathprefix);
272   array_cats(x,s);
273   if (S_ISLNK(ss->st_mode)) {
274     array_cats(x," -> ");
275     array_cats(x,readlink(s,buf,sizeof(buf))?"[error]":buf);
276   }
277   array_cats(x,"\r\n");
278 }
279 
ftp_list(struct http_data * h,char * s,int _long,int sock)280 static int ftp_list(struct http_data* h,char* s,int _long,int sock) {
281   int i,l=h->ftppath?str_len(h->ftppath):0;
282   char* x=alloca(l+str_len(s)+5);
283   char* y;
284   DIR* D;
285   struct dirent* d;
286   int rev=0;
287   int what=0;
288   time_t now;
289 
290   char* pathprefix="";
291   char* match=0;
292 
293   unsigned long o,n;
294   int (*sortfun)(de*,de*);
295   array a,b,c;
296   de* ab;
297 
298   if (h->buddy==-1 || !io_getcookie(h->buddy)) {
299     h->hdrbuf="425 Could not establish data connection\r\n";
300     return -1;
301   }
302 
303   i=str_len(s);
304   if (i>1) {
305     if (s[i-1]=='\n') --i;
306     if (s[i-1]=='\r') --i;
307     s[i]=0;
308   }
309 
310   byte_zero(&a,sizeof(a));
311   byte_zero(&b,sizeof(b));
312   byte_zero(&c,sizeof(c));
313   o=n=0;
314 
315   if (s[0]=='-') {
316     for (++s; *s && *s!=' '; ++s) {
317       switch (*s) {
318       case 'l': _long=1; break;
319       case 'r': rev=1; break;
320       case 'S': what=1; break;
321       case 't': what=2; break;
322       }
323     }
324     while (*s==' ') ++s;
325   }
326   {
327     switch (what) {
328     case 1: sortfun=rev?sort_size_a:sort_size_d; break;
329     case 2: sortfun=rev?sort_mtime_a:sort_mtime_d; break;
330     default: sortfun=rev?sort_name_d:sort_name_a; break;
331     }
332   }
333 
334   /* first, append to path */
335   if (h->ftppath && s[0]!='/')
336     y=x+fmt_str(x,h->ftppath);
337   else
338     y=x;
339   y+=fmt_str(y,"/");
340   y+=fmt_str(y,s);
341   if (y[-1]=='\n') --y;
342   if (y[-1]=='\r') --y;
343   *y=0;
344 
345   /* now reduce "//" and "/./" and "/[^/]+/../" to "/" */
346   canonpath(x);
347 
348   int dirfd=ip_vhost(h);
349   if (dirfd==-2) return 0;
350 
351   /* cases:
352    *   it's a directory
353    *     -> opendir(foo/bar), ...
354    *   foo/$fnord
355    *     -> pathprefix="foo/"; chdir(foo); opendir(...); fnmatch($fnord)
356    *   /pub/$fnord
357    *     -> pathprefix="/pub/"; chdir(/pub); opendir(...); fnmatch($fnord)
358    */
359 
360 #ifndef O_PATH
361 #define O_PATH 0
362 #endif
363 #ifndef O_DIRECTORY
364 #define O_DIRECTORY 0
365 #endif
366 #ifndef O_CLOEXEC
367 #define O_CLOEXEC 0
368 #endif
369   int dir2fd=-1;
370   if (!x[1] || (dir2fd=openat(dirfd,x+1,O_RDONLY|O_PATH|O_DIRECTORY|O_CLOEXEC))!=-1) {		/* it's a directory */
371     pathprefix="";
372     match=0;
373   } else {
374     if (s[0]!='/') {	/* foo/$fnord */
375       int z=str_rchr(s,'/');
376       if (s[z]!='/') {
377 	pathprefix="";
378 	match=s;
379       } else {
380 	pathprefix=alloca(z+2);
381 	byte_copy(pathprefix,z,s);
382 	pathprefix[z]='/';
383 	pathprefix[z+1]=0;
384 	match=0;
385 	z=str_rchr(x,'/');
386 	x[z]=0;
387 	if (x[0]=='/' && x[1] && (dir2fd=openat(dirfd,x+1,O_RDONLY|O_PATH|O_DIRECTORY|O_CLOEXEC))==-1) {
388 notfound:
389 	  h->hdrbuf="450 no such file or directory.\r\n";
390 	  return -1;
391 	}
392 	x[z]='/';
393 	match=x+z+1;
394       }
395     } else {		/* /pub/$fnord */
396       int z=str_rchr(x,'/');
397       x[z]=0;
398       if (x[0]=='/' && x[1] && (dir2fd=openat(dirfd,x+1,O_RDONLY|O_PATH|O_DIRECTORY|O_CLOEXEC))==-1) goto notfound;
399       match=x+z+1;
400       pathprefix=alloca(z+2);
401       byte_copy(pathprefix,z,x);
402       pathprefix[z]='/';
403       pathprefix[z+1]=0;
404     }
405   }
406 
407   if (dir2fd==-1) goto notfound;
408   D=fdopendir(dir2fd);
409   if (!D) {
410     close(dir2fd);
411     goto notfound;
412   } else {
413     while ((d=readdir(D))) {
414       de* X=array_allocate(&a,sizeof(de),n);
415       if (!X) break;
416       X->name=o;
417       if (lstat(d->d_name,&X->ss)==-1) continue;
418       if (!match || fnmatch(match,d->d_name,FNM_PATHNAME)==0) {
419 	array_cats0(&b,d->d_name);
420 	o+=str_len(d->d_name)+1;
421 	++n;
422       }
423     }
424     closedir(D);
425   }
426   if (array_failed(&a) || array_failed(&b)) {
427     array_reset(&a);
428     array_reset(&b);
429 nomem:
430     h->hdrbuf="500 out of memory\r\n";
431     return -1;
432   }
433   base=array_start(&b);
434   qsort(array_start(&a),n,sizeof(de),(int(*)(const void*,const void*))sortfun);
435 
436   ab=array_start(&a);
437   now=time(0);
438   for (i=0; i<n; ++i) {
439     char* name=base+ab[i].name;
440 
441     if (name[0]=='.') {
442       if (name[1]==0) continue; /* skip "." */
443       if (name[1]!='.' || name[2]!=0)	/* skip dot-files */
444 	continue;
445     } else if (name[0]==':')
446       name[0]='.';
447     if (_long)
448       ftp_ls(&c,name,&ab[i].ss,now,pathprefix);
449     else {
450       array_cats(&c,pathprefix);
451       array_cats(&c,name);
452       array_cats(&c,"\r\n");
453     }
454   }
455   array_reset(&a);
456   array_reset(&b);
457   if (array_failed(&c)) goto nomem;
458   if (array_bytes(&c)==0) {
459     h->hdrbuf="450 no match\r\n";
460     return -1;
461   } else {
462     struct http_data* b=io_getcookie(h->buddy);
463     assert(b);
464     if (b) {
465       iob_addbuf_free(&b->iob,array_start(&c),array_bytes(&c));
466       b->f=DOWNLOADING;
467       h->f=WAITCONNECT;
468       if (b->t==FTPSLAVE) {
469 	h->hdrbuf="125 go on\r\n";
470 	io_wantwrite(h->buddy);
471 	h->f=LOGGEDIN;
472       } else if (b->t==FTPACTIVE)
473 	h->hdrbuf="150 connecting\r\n";
474       else
475 	h->hdrbuf="150 I'm listening\r\n";
476     }
477   }
478   if (logging) {
479     buffer_puts(buffer_1,_long?"LIST ":"NLST ");
480     buffer_putulong(buffer_1,sock);
481     buffer_putspace(buffer_1);
482     buffer_putlogstr(buffer_1,x[1]?x:"/");
483     buffer_putspace(buffer_1);
484     buffer_putulong(buffer_1,array_bytes(&c));
485     buffer_putspace(buffer_1);
486     {
487       char buf[IP6_FMT+10];
488       int x;
489       x=fmt_ip6c(buf,h->peerip);
490       x+=fmt_str(buf+x,"/");
491       x+=fmt_ulong(buf+x,h->peerport);
492       buffer_put(buffer_1,buf,x);
493     }
494     buffer_putnlflush(buffer_1);
495   }
496   return 0;
497 }
498 
ftp_cwd(struct http_data * h,char * s)499 static int ftp_cwd(struct http_data* h,char* s) {
500   int l=h->ftppath?str_len(h->ftppath):0;
501   char* x=alloca(l+str_len(s)+5);
502   char* y;
503   /* first, append to path */
504   if (s[0]!='/' && h->ftppath)
505     y=x+fmt_str(x,h->ftppath);
506   else
507     y=x;
508   y+=fmt_str(y,"/");
509   y+=fmt_str(y,s);
510   if (y[-1]=='\n') --y;
511   if (y[-1]=='\r') --y;
512   *y=0;
513 
514   /* now reduce "//" and "/./" and "/[^/]+/../" to "/" */
515   l=canonpath(x);
516 
517   int dirfd=ip_vhost(h);
518   if (dirfd==-2) return -1;
519 
520   int dir2fd=-1;
521 
522   if (x[1] && (dir2fd=openat(dirfd,x+1,O_RDONLY|O_PATH|O_DIRECTORY|O_CLOEXEC))==-1) {
523     h->hdrbuf="525 directory not found.\r\n";
524     return -1;
525   }
526   if (dir2fd!=-1) close(dir2fd);
527   y=realloc(h->ftppath,l+1);
528   if (!y) {
529     h->hdrbuf="500 out of memory.\r\n";
530     return -1;
531   }
532   y[fmt_str(y,x)]=0;
533   h->ftppath=y;
534   h->hdrbuf="250 ok.\r\n";
535   return 0;
536 }
537 
ftp_mkdir(struct http_data * h,const char * s)538 static int ftp_mkdir(struct http_data* h,const char* s) {
539   if (ftp_open(h,s,2,0,"mkdir",0)==-1) return -1;
540   h->hdrbuf="257 directory created.\r\n";
541   return 0;
542 }
543 
ftpresponse(struct http_data * h,int64 s)544 void ftpresponse(struct http_data* h,int64 s) {
545   char* c;
546   h->filefd=-1;
547 
548   ++rps1;
549   c=array_start(&h->r);
550   {
551     char* d,* e=c+array_bytes(&h->r);
552 
553 /*    write(1,c,e-c); */
554 
555     for (d=c; d<e; ++d) {
556       if (*d=='\n') {
557 	if (d>c && d[-1]=='\r') --d;
558 	*d=0;
559 	break;
560       }
561       if (*d==0) *d='\n';
562     }
563   }
564   if (case_equals(c,"QUIT")) {
565     h->hdrbuf="221 Goodbye.\r\n";
566     h->keepalive=0;
567   } else if (case_equals(c,"ABOR") ||
568 	     case_equals(c,"\xff\xf4\xff\xf2""ABOR") ||
569 	     case_equals(c,"\xff\xf4\xff""ABOR")) {
570     /* for some reason, on Linux 2.6 the trailing \xf2 sometimes does
571      * not arrive although it is visible in the tcpdump */
572     if (h->buddy==-1)
573       h->hdrbuf="226 Ok.\r\n";
574     else {
575       io_close(h->buddy);
576       h->buddy=-1;
577       h->hdrbuf="426 Ok.\r\n226 Connection closed.\r\n";
578     }
579   } else if (case_starts(c,"USER ")) {
580     c+=5;
581     if (case_equals(c,"ftp") || case_equals(c,"anonymous")) {
582       if (askforpassword)
583 	h->hdrbuf="331 User name OK, please use your email address as password.\r\n";
584       else
585 	h->hdrbuf="230 No need for passwords, you're logged in now.\r\n";
586     } else {
587       if (askforpassword)
588 	h->hdrbuf="331 I only serve anonymous users.  But I'll make an exception.\r\n";
589       else
590 	h->hdrbuf="230 I only serve anonymous users.  But I'll make an exception.\r\n";
591     }
592     h->f=LOGGEDIN;
593   } else if (case_starts(c,"PASS ")) {
594     h->hdrbuf="230 If you insist...\r\n";
595   } else if (case_starts(c,"TYPE ")) {
596     h->hdrbuf="200 yeah, whatever.\r\n";
597   } else if (case_equals(c,"PASV") || case_equals(c,"EPSV")) {
598     int epsv=(*c=='e' || *c=='E');
599     char ip[16];
600     uint16 port;
601 #ifdef __broken_itojun_v6__
602 #warning fixme
603 #endif
604     if (h->buddy!=-1) {
605       if (logging) {
606 	buffer_puts(buffer_1,"close/olddataconn ");
607 	buffer_putulong(buffer_1,h->buddy);
608 	buffer_putnlflush(buffer_1);
609       }
610       io_close(h->buddy);
611     }
612     h->buddy=socket_tcp6();
613     if (h->buddy==-1) {
614       h->hdrbuf="425 socket() failed.\r\n";
615       goto ABEND;
616     }
617     if (socket_bind6_reuse(h->buddy,h->myip,0,h->myscope_id)==-1) {
618 closeandgo:
619       io_close(h->buddy);
620       h->hdrbuf="425 socket error.\r\n";
621       goto ABEND;
622     }
623     if (socket_local6(h->buddy,ip,&port,0)==-1) goto closeandgo;
624     if (!(h->hdrbuf=malloc(100))) goto closeandgo;
625     if (epsv==0) {
626       c=h->hdrbuf+fmt_str(h->hdrbuf,"227 Passive Mode OK (");
627       {
628 	int i;
629 	for (i=0; i<4; ++i) {
630 	  c+=fmt_ulong(c,h->myip[12+i]&0xff);
631 	  c+=fmt_str(c,",");
632 	}
633       }
634       c+=fmt_ulong(c,(port>>8)&0xff);
635       c+=fmt_str(c,",");
636       c+=fmt_ulong(c,port&0xff);
637       c+=fmt_str(c,")\r\n");
638     } else {
639       c=h->hdrbuf+fmt_str(h->hdrbuf,"229 Passive Mode OK (|||");
640       c+=fmt_ulong(c,port);
641       c+=fmt_str(c,"|)\r\n");
642     }
643     *c=0;
644 #ifdef HAVE_IO_FD_FLAGS
645     if (io_fd_flags(h->buddy,IO_FD_NONBLOCK))
646 #else
647     if (io_fd(h->buddy))
648 #endif
649     {
650       struct http_data* x=malloc(sizeof(struct http_data));
651       if (!x) {
652 freecloseabort:
653 	free(h->hdrbuf);
654 	c=0;
655 	goto closeandgo;
656       }
657       byte_zero(x,sizeof(struct http_data));
658       x->buddy=s; x->filefd=-1;
659       changestate(x,FTPPASSIVE);
660 //      x->t=FTPPASSIVE;
661 #ifdef STATE_DEBUG
662       x->myfd=h->buddy;
663 #endif
664       io_setcookie(h->buddy,x);
665       socket_listen(h->buddy,1);
666       io_wantread(h->buddy);
667       if (logging) {
668 	buffer_puts(buffer_1,epsv?"epsv_listen ":"pasv_listen ");
669 	buffer_putulong(buffer_1,s);
670 	buffer_putspace(buffer_1);
671 	buffer_putulong(buffer_1,h->buddy);
672 	buffer_putspace(buffer_1);
673 	buffer_putulong(buffer_1,port);
674 	buffer_putnlflush(buffer_1);
675       }
676     } else
677       goto freecloseabort;
678   } else if (case_starts(c,"PORT ") || case_starts(c,"EPRT ")) {
679     int eprt=(*c=='e' || *c=='E');
680     char ip[16];
681     uint16 port;
682 #ifdef __broken_itojun_v6__
683 #warning fixme
684 #endif
685     if (h->buddy!=-1) {
686       if (logging) {
687 	buffer_puts(buffer_1,"close/olddataconn ");
688 	buffer_putulong(buffer_1,h->buddy);
689 	buffer_putnlflush(buffer_1);
690       }
691       io_close(h->buddy);
692       h->buddy=-1;
693     }
694     c+=5;
695     if (eprt) {
696       /* |1|10.0.0.4|1025| or @2@::1@1026@ */
697       char sep;
698       int i;
699       if (!(sep=*c)) goto syntaxerror;
700       if (c[2]!=sep) goto syntaxerror;
701       if (c[1]=='1') {
702 	byte_copy(ip,12,V4mappedprefix);
703 	if (c[3+(i=scan_ip4(c+3,ip+12))]!=sep || !i) goto syntaxerror;
704       } else if (c[1]=='2') {
705 	if (c[3+(i=scan_ip6(c+3,ip))]!=sep || !i) goto syntaxerror;
706       } else goto syntaxerror;
707       c+=i+4;
708       if (c[i=scan_ushort(c,&port)]!=sep || !i) goto syntaxerror;
709     } else {
710       /* 10,0,0,1,4,1 -> 10.0.0.1:1025 */
711       unsigned long l;
712       int r,i;
713       for (i=0; i<4; ++i) {
714 	if (c[r=scan_ulong(c,&l)]!=',' || l>255) {
715 syntaxerror:
716 	  h->hdrbuf="501 Huh?  What?!  Where am I?\r\n";
717 	  goto ABEND;
718 	}
719 	c+=r+1;
720 	ip[12+i]=l;
721 	byte_copy(ip,12,V4mappedprefix);
722       }
723       if (c[r=scan_ulong(c,&l)]!=',' || l>255) goto syntaxerror;
724       c+=r+1;
725       port=l<<8;
726       r=scan_ulong(c,&l); if (!r || l>255) goto syntaxerror;
727       port+=l;
728     }
729     if (byte_diff(h->peerip,16,ip)) {
730       h->hdrbuf="425 Sorry, but I will only connect back to your own IP.\r\n";
731       goto ABEND;
732     }
733     h->buddy=socket_tcp6();
734     if (h->buddy==-1) {
735       h->hdrbuf="425 socket() failed.\r\n";
736       goto ABEND;
737     }
738     h->hdrbuf="200 Okay, go ahead.\r\n";
739 #ifdef HAVE_IO_FD_FLAGS
740     if (io_fd_flags(h->buddy,IO_FD_NONBLOCK))
741 #else
742     if (io_fd(h->buddy))
743 #endif
744     {
745       struct http_data* x=malloc(sizeof(struct http_data));
746       if (!x) goto closeandgo;
747       byte_zero(x,sizeof(struct http_data));
748       x->buddy=s; x->filefd=-1;
749       changestate(x,FTPACTIVE);
750 //      x->t=FTPACTIVE;
751       x->destport=port;
752       byte_copy(x->peerip,16,ip);
753 
754 #ifdef STATE_DEBUG
755       x->myfd=h->buddy;
756 #endif
757       io_setcookie(h->buddy,x);
758     } else
759       goto closeandgo;
760 
761     socket_connect6(h->buddy,ip,port,h->myscope_id);
762 
763     if (logging) {
764       buffer_puts(buffer_1,eprt?"eprt ":"port ");
765       buffer_putulong(buffer_1,s);
766       buffer_putspace(buffer_1);
767       buffer_putulong(buffer_1,h->buddy);
768       buffer_putspace(buffer_1);
769       buffer_putulong(buffer_1,port);
770       buffer_putnlflush(buffer_1);
771     }
772     io_dontwantread(h->buddy);
773     io_wantwrite(h->buddy);
774   } else if (case_equals(c,"PWD") || case_equals(c,"XPWD") /* fsck windoze */) {
775     c=h->ftppath; if (!c) c="/";
776     h->hdrbuf=malloc(50+str_len(c));
777     if (h->hdrbuf) {
778       c=h->hdrbuf;
779       c+=fmt_str(c,"257 \"");
780       c+=fmt_str(c,h->ftppath?h->ftppath:"/");
781       c+=fmt_str(c,"\" \r\n");
782       *c=0;
783     } else
784       h->hdrbuf="500 out of memory\r\n";
785   } else if (case_starts(c,"CWD ")) {
786     ftp_cwd(h,c+4);
787   } else if (case_equals(c,"CDUP") || case_equals(c,"XCUP")) {
788     ftp_cwd(h,"..");
789   } else if (case_starts(c,"MDTM ")) {
790     c+=5;
791     if (ftp_mdtm(h,c)==0)
792       c=h->hdrbuf;
793   } else if (case_starts(c,"SIZE ")) {
794     c+=5;
795     if (ftp_size(h,c)==0)
796       c=h->hdrbuf;
797   } else if (case_starts(c,"MKD ")) {
798     c+=4;
799     ftp_mkdir(h,c);
800   } else if (case_equals(c,"FEAT")) {
801     h->hdrbuf="211-Features:\r\n MDTM\r\n REST STREAM\r\n SIZE\r\n211 End\r\n";
802   } else if (case_equals(c,"SYST")) {
803     h->hdrbuf="215 UNIX Type: L8\r\n";
804   } else if (case_starts(c,"REST ")) {
805     unsigned long long x;
806     c+=5;
807     if (!c[scan_ulonglong(c,&x)]) {
808       h->hdrbuf="350 ok.\r\n";
809       h->ftp_rest=x;
810     } else
811       h->hdrbuf="501 invalid number\r\n";
812   } else if (case_starts(c,"RETR ")) {
813     c+=5;
814     if (ftp_retrstor(h,c,s,0)==0)
815       c=h->hdrbuf;
816   } else if (case_starts(c,"STOR ")) {
817     if (nouploads)
818       h->hdrbuf="553 no upload allowed here.\r\n";
819     else {
820       c+=5;
821       if (ftp_retrstor(h,c,s,1)==0)
822 	c=h->hdrbuf;
823     }
824   } else if (case_starts(c,"LIST")) {
825     c+=4;
826     if (*c==' ') ++c;
827     ftp_list(h,c,1,s);
828   } else if (case_starts(c,"NLST")) {
829     c+=4;
830     if (*c==' ') ++c;
831     ftp_list(h,c,0,s);
832   } else if (case_equals(c,"NOOP")) {
833     h->hdrbuf="200 no reply.\r\n";
834   } else if (case_starts(c,"HELP")) {
835     h->hdrbuf="214-This is gatling (www.fefe.de/gatling/); No help available.\r\n214 See http://cr.yp.to/ftp.html for FTP help.\r\n";
836   } else {
837     static int funny;
838     switch (++funny) {
839     case 1: h->hdrbuf="550 The heck you say.\r\n"; break;
840     case 2: h->hdrbuf="550 No, really?\r\n"; break;
841     case 3: h->hdrbuf="550 Yeah, whatever...\r\n"; break;
842     case 4: h->hdrbuf="550 How intriguing!\r\n"; break;
843     default: h->hdrbuf="550 I'm just a simple FTP server, you know?\r\n"; funny=0; break;
844     }
845   }
846 ABEND:
847   {
848     char* d=array_start(&h->r);
849     if (c>=d && c<=d+array_bytes(&h->r))
850       iob_addbuf(&h->iob,h->hdrbuf,str_len(h->hdrbuf));
851     else
852       iob_addbuf_free(&h->iob,h->hdrbuf,str_len(h->hdrbuf));
853   }
854   io_dontwantread(s);
855   io_wantwrite(s);
856 }
857 
handle_read_ftppassive(int64 i,struct http_data * H)858 void handle_read_ftppassive(int64 i,struct http_data* H) {
859   /* This is the server socket for a passive FTP data connections.
860     * A read event means the peer established a TCP connection.
861     * accept() it and close server connection */
862   struct http_data* h;
863   int n;
864   h=io_getcookie(H->buddy);
865   if (!h) {
866     /* This used to be an assert() but it turns out it can be triggered.
867      * I can't actually reproduce this but I think it happens if someone
868      * does PASV and sends the connection but then drops the control
869      * connection and we get that drop event before we get the read
870      * event here signalling the incoming connection.  Now, if this
871      * happens, just drop everything.  We got conned. */
872     if (logging) {
873       buffer_puts(buffer_1,"pasv_accept_without_buddy ");
874       buffer_putulong(buffer_1,i);
875       buffer_puts(buffer_1,"\nclose/statefail ");
876       buffer_putulong(buffer_1,i);
877       buffer_putnlflush(buffer_1);
878     }
879     cleanup(i);
880     return;
881   }
882   n=socket_accept6(i,H->myip,&H->myport,&H->myscope_id);
883   if (n==-1) {
884 pasverror:
885     if (logging) {
886       buffer_puts(buffer_1,"pasv_accept_error ");
887       buffer_putulong(buffer_1,i);
888       buffer_putspace(buffer_1);
889       buffer_puterror(buffer_1);
890       buffer_puts(buffer_1,"\nclose/acceptfail ");
891       buffer_putulong(buffer_1,i);
892       buffer_putnlflush(buffer_1);
893     }
894     h->buddy=-1;
895     free(H);
896     io_close(i);
897   } else {
898 #ifdef HAVE_IO_FD_FLAGS
899     if (!io_fd_flags(n,IO_FD_NONBLOCK|IO_FD_CANWRITE)) goto pasverror;
900 #else
901     if (!io_fd_canwrite(n)) goto pasverror;
902 #endif
903     io_nonblock(n);
904     if (logging) {
905       buffer_puts(buffer_1,"pasv_accept ");
906       buffer_putulong(buffer_1,i);
907       buffer_putspace(buffer_1);
908       buffer_putulong(buffer_1,n);
909       buffer_puts(buffer_1,"\nclose/accepted ");
910       buffer_putulong(buffer_1,i);
911       buffer_putnlflush(buffer_1);
912     }
913     h->buddy=n;
914 #ifdef STATE_DEBUG
915     H->myfd=n;
916 #endif
917     io_setcookie(n,H);
918     io_close(i);
919     changestate(H,FTPSLAVE);
920 //    H->t=FTPSLAVE;
921 #ifdef TCP_NODELAY
922     {
923       int x=1;
924       setsockopt(n,IPPROTO_TCP,TCP_NODELAY,&x,sizeof(x));
925     }
926 #endif
927     if (h->f==WAITCONNECT) {
928       h->f=LOGGEDIN;
929       if (H->f==DOWNLOADING)
930 	io_wantwrite(h->buddy);
931       else
932 	io_wantread(h->buddy);
933     }
934   }
935 }
936 
handle_write_ftpactive(int64 i,struct http_data * h)937 void handle_write_ftpactive(int64 i,struct http_data* h) {
938   struct http_data* H;
939   H=io_getcookie(h->buddy);
940   assert(H);
941   if (socket_connect6(i,h->peerip,h->destport,h->myscope_id)==-1 && errno!=EISCONN) {
942     if (logging) {
943       buffer_puts(buffer_1,"port_connect_error ");
944       buffer_putulong(buffer_1,i);
945       buffer_putspace(buffer_1);
946       buffer_puterror(buffer_1);
947       buffer_puts(buffer_1,"\nclose/connectfail ");
948       buffer_putulong(buffer_1,i);
949       buffer_putnlflush(buffer_1);
950     }
951     H->buddy=-1;
952     free(h);
953     io_close(i);
954   } else {
955     if (logging) {
956       char buf[IP6_FMT];
957       buffer_puts(buffer_1,"port_connect ");
958       buffer_putulong(buffer_1,i);
959       buffer_putspace(buffer_1);
960       buffer_put(buffer_1,buf,fmt_ip6c(buf,h->peerip));
961       buffer_putspace(buffer_1);
962       buffer_put(buffer_1,buf,fmt_ulong(buf,h->destport));
963       buffer_putnlflush(buffer_1);
964     }
965     changestate(h,FTPSLAVE);
966 //    h->t=FTPSLAVE;
967 #ifdef TCP_NODELAY
968     {
969       int x=1;
970       setsockopt(i,IPPROTO_TCP,TCP_NODELAY,&x,sizeof(x));
971     }
972 #endif
973     if (h->f != DOWNLOADING)
974       io_dontwantwrite(i);
975     if (H->f==WAITCONNECT) {
976       H->f=LOGGEDIN;
977       if (h->f==DOWNLOADING)
978 	io_wantwrite(H->buddy);
979       else
980 	io_wantread(H->buddy);
981     }
982   }
983 }
984 
985 #endif /* SUPPORT_FTP */
986 
987 
988