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