Deleted Added
full compact
ftpcmd.y (88935) ftpcmd.y (89935)
1/*
2 * Copyright (c) 1985, 1988, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
34 */
35
36/*
37 * Grammar for FTP commands.
38 * See RFC 959.
39 */
40
41%{
42
43#ifndef lint
44#if 0
45static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
46#endif
47static const char rcsid[] =
1/*
2 * Copyright (c) 1985, 1988, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
34 */
35
36/*
37 * Grammar for FTP commands.
38 * See RFC 959.
39 */
40
41%{
42
43#ifndef lint
44#if 0
45static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
46#endif
47static const char rcsid[] =
48 "$FreeBSD: head/libexec/ftpd/ftpcmd.y 88935 2002-01-05 20:13:01Z dwmalone $";
48 "$FreeBSD: head/libexec/ftpd/ftpcmd.y 89935 2002-01-28 19:28:14Z yar $";
49#endif /* not lint */
50
51#include <sys/param.h>
52#include <sys/socket.h>
53#include <sys/stat.h>
54
55#include <netinet/in.h>
56#include <arpa/ftp.h>
57
58#include <ctype.h>
59#include <errno.h>
60#include <glob.h>
61#include <netdb.h>
62#include <pwd.h>
49#endif /* not lint */
50
51#include <sys/param.h>
52#include <sys/socket.h>
53#include <sys/stat.h>
54
55#include <netinet/in.h>
56#include <arpa/ftp.h>
57
58#include <ctype.h>
59#include <errno.h>
60#include <glob.h>
61#include <netdb.h>
62#include <pwd.h>
63#include <setjmp.h>
64#include <signal.h>
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68#include <syslog.h>
69#include <time.h>
70#include <unistd.h>
71#include <libutil.h>
72#include <md5.h>
73
74#include "extern.h"
75
76extern union sockunion data_dest, his_addr;
77extern int logged_in;
78extern struct passwd *pw;
79extern int guest;
80extern int paranoid;
81extern int logging;
82extern int type;
83extern int form;
84extern int ftpdebug;
85extern int timeout;
86extern int maxtimeout;
87extern int pdata;
88extern char *hostname;
89extern char remotehost[];
90extern char proctitle[];
91extern int usedefault;
92extern int transflag;
93extern char tmpline[];
94extern int readonly;
95extern int noepsv;
96extern int noretr;
97extern int noguestretr;
98
99off_t restart_point;
100
101static int cmd_type;
102static int cmd_form;
103static int cmd_bytesz;
63#include <signal.h>
64#include <stdio.h>
65#include <stdlib.h>
66#include <string.h>
67#include <syslog.h>
68#include <time.h>
69#include <unistd.h>
70#include <libutil.h>
71#include <md5.h>
72
73#include "extern.h"
74
75extern union sockunion data_dest, his_addr;
76extern int logged_in;
77extern struct passwd *pw;
78extern int guest;
79extern int paranoid;
80extern int logging;
81extern int type;
82extern int form;
83extern int ftpdebug;
84extern int timeout;
85extern int maxtimeout;
86extern int pdata;
87extern char *hostname;
88extern char remotehost[];
89extern char proctitle[];
90extern int usedefault;
91extern int transflag;
92extern char tmpline[];
93extern int readonly;
94extern int noepsv;
95extern int noretr;
96extern int noguestretr;
97
98off_t restart_point;
99
100static int cmd_type;
101static int cmd_form;
102static int cmd_bytesz;
103static int state;
104char cbuf[512];
105char *fromname = (char *) 0;
106
107extern int epsvall;
108
109%}
110
111%union {
112 int i;
113 char *s;
114}
115
116%token
117 A B C E F I
118 L N P R S T
119 ALL
120
121 SP CRLF COMMA
122
123 USER PASS ACCT REIN QUIT PORT
124 PASV TYPE STRU MODE RETR STOR
125 APPE MLFL MAIL MSND MSOM MSAM
126 MRSQ MRCP ALLO REST RNFR RNTO
127 ABOR DELE CWD LIST NLST SITE
128 STAT HELP NOOP MKD RMD PWD
129 CDUP STOU SMNT SYST SIZE MDTM
130 LPRT LPSV EPRT EPSV
131
132 UMASK IDLE CHMOD MDFIVE
133
134 LEXERR
135
136%token <s> STRING
137%token <i> NUMBER
138
139%type <i> check_login octal_number byte_size
140%type <i> check_login_ro octal_number byte_size
141%type <i> check_login_epsv octal_number byte_size
142%type <i> struct_code mode_code type_code form_code
143%type <s> pathstring pathname password username
144%type <s> ALL
145
146%start cmd_list
147
148%%
149
150cmd_list
151 : /* empty */
152 | cmd_list cmd
153 {
154 if (fromname)
155 free(fromname);
156 fromname = (char *) 0;
157 restart_point = (off_t) 0;
158 }
159 | cmd_list rcmd
160 ;
161
162cmd
163 : USER SP username CRLF
164 {
165 user($3);
166 free($3);
167 }
168 | PASS SP password CRLF
169 {
170 pass($3);
171 free($3);
172 }
173 | PASS CRLF
174 {
175 pass("");
176 }
177 | PORT check_login SP host_port CRLF
178 {
179 if (epsvall) {
180 reply(501, "no PORT allowed after EPSV ALL");
181 goto port_done;
182 }
183 if (!$2)
184 goto port_done;
185 if (port_check("PORT") == 1)
186 goto port_done;
187#ifdef INET6
188 if ((his_addr.su_family != AF_INET6 ||
189 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
190 /* shoud never happen */
191 usedefault = 1;
192 reply(500, "Invalid address rejected.");
193 goto port_done;
194 }
195 port_check_v6("pcmd");
196#endif
197 port_done:
198 }
199 | LPRT check_login SP host_long_port CRLF
200 {
201 if (epsvall) {
202 reply(501, "no LPRT allowed after EPSV ALL");
203 goto lprt_done;
204 }
205 if (!$2)
206 goto lprt_done;
207 if (port_check("LPRT") == 1)
208 goto lprt_done;
209#ifdef INET6
210 if (his_addr.su_family != AF_INET6) {
211 usedefault = 1;
212 reply(500, "Invalid address rejected.");
213 goto lprt_done;
214 }
215 if (port_check_v6("LPRT") == 1)
216 goto lprt_done;
217#endif
218 lprt_done:
219 }
220 | EPRT check_login SP STRING CRLF
221 {
222 char delim;
223 char *tmp = NULL;
224 char *p, *q;
225 char *result[3];
226 struct addrinfo hints;
227 struct addrinfo *res;
228 int i;
229
230 if (epsvall) {
231 reply(501, "no EPRT allowed after EPSV ALL");
232 goto eprt_done;
233 }
234 if (!$2)
235 goto eprt_done;
236
237 memset(&data_dest, 0, sizeof(data_dest));
238 tmp = strdup($4);
239 if (ftpdebug)
240 syslog(LOG_DEBUG, "%s", tmp);
241 if (!tmp) {
242 fatalerror("not enough core");
243 /*NOTREACHED*/
244 }
245 p = tmp;
246 delim = p[0];
247 p++;
248 memset(result, 0, sizeof(result));
249 for (i = 0; i < 3; i++) {
250 q = strchr(p, delim);
251 if (!q || *q != delim) {
252 parsefail:
253 reply(500,
254 "Invalid argument, rejected.");
255 if (tmp)
256 free(tmp);
257 usedefault = 1;
258 goto eprt_done;
259 }
260 *q++ = '\0';
261 result[i] = p;
262 if (ftpdebug)
263 syslog(LOG_DEBUG, "%d: %s", i, p);
264 p = q;
265 }
266
267 /* some more sanity check */
268 p = result[0];
269 while (*p) {
270 if (!isdigit(*p))
271 goto parsefail;
272 p++;
273 }
274 p = result[2];
275 while (*p) {
276 if (!isdigit(*p))
277 goto parsefail;
278 p++;
279 }
280
281 /* grab address */
282 memset(&hints, 0, sizeof(hints));
283 if (atoi(result[0]) == 1)
284 hints.ai_family = PF_INET;
285#ifdef INET6
286 else if (atoi(result[0]) == 2)
287 hints.ai_family = PF_INET6;
288#endif
289 else
290 hints.ai_family = PF_UNSPEC; /*XXX*/
291 hints.ai_socktype = SOCK_STREAM;
292 i = getaddrinfo(result[1], result[2], &hints, &res);
293 if (i)
294 goto parsefail;
295 memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
296#ifdef INET6
297 if (his_addr.su_family == AF_INET6
298 && data_dest.su_family == AF_INET6) {
299 /* XXX more sanity checks! */
300 data_dest.su_sin6.sin6_scope_id =
301 his_addr.su_sin6.sin6_scope_id;
302 }
303#endif
304 free(tmp);
305 tmp = NULL;
306
307 if (port_check("EPRT") == 1)
308 goto eprt_done;
309#ifdef INET6
310 if (his_addr.su_family != AF_INET6) {
311 usedefault = 1;
312 reply(500, "Invalid address rejected.");
313 goto eprt_done;
314 }
315 if (port_check_v6("EPRT") == 1)
316 goto eprt_done;
317#endif
318 eprt_done:
319 free($4);
320 }
321 | PASV check_login CRLF
322 {
323 if (epsvall)
324 reply(501, "no PASV allowed after EPSV ALL");
325 else if ($2)
326 passive();
327 }
328 | LPSV check_login CRLF
329 {
330 if (epsvall)
331 reply(501, "no LPSV allowed after EPSV ALL");
332 else if ($2)
333 long_passive("LPSV", PF_UNSPEC);
334 }
335 | EPSV check_login_epsv SP NUMBER CRLF
336 {
337 if ($2) {
338 int pf;
339 switch ($4) {
340 case 1:
341 pf = PF_INET;
342 break;
343#ifdef INET6
344 case 2:
345 pf = PF_INET6;
346 break;
347#endif
348 default:
349 pf = -1; /*junk value*/
350 break;
351 }
352 long_passive("EPSV", pf);
353 }
354 }
355 | EPSV check_login_epsv SP ALL CRLF
356 {
357 if ($2) {
358 reply(200,
359 "EPSV ALL command successful.");
360 epsvall++;
361 }
362 }
363 | EPSV check_login_epsv CRLF
364 {
365 if ($2)
366 long_passive("EPSV", PF_UNSPEC);
367 }
368 | TYPE check_login SP type_code CRLF
369 {
370 if ($2) {
371 switch (cmd_type) {
372
373 case TYPE_A:
374 if (cmd_form == FORM_N) {
375 reply(200, "Type set to A.");
376 type = cmd_type;
377 form = cmd_form;
378 } else
379 reply(504, "Form must be N.");
380 break;
381
382 case TYPE_E:
383 reply(504, "Type E not implemented.");
384 break;
385
386 case TYPE_I:
387 reply(200, "Type set to I.");
388 type = cmd_type;
389 break;
390
391 case TYPE_L:
392#if NBBY == 8
393 if (cmd_bytesz == 8) {
394 reply(200,
395 "Type set to L (byte size 8).");
396 type = cmd_type;
397 } else
398 reply(504, "Byte size must be 8.");
399#else /* NBBY == 8 */
400 UNIMPLEMENTED for NBBY != 8
401#endif /* NBBY == 8 */
402 }
403 }
404 }
405 | STRU check_login SP struct_code CRLF
406 {
407 if ($2) {
408 switch ($4) {
409
410 case STRU_F:
411 reply(200, "STRU F ok.");
412 break;
413
414 default:
415 reply(504, "Unimplemented STRU type.");
416 }
417 }
418 }
419 | MODE check_login SP mode_code CRLF
420 {
421 if ($2) {
422 switch ($4) {
423
424 case MODE_S:
425 reply(200, "MODE S ok.");
426 break;
427
428 default:
429 reply(502, "Unimplemented MODE type.");
430 }
431 }
432 }
433 | ALLO check_login SP NUMBER CRLF
434 {
435 if ($2) {
436 reply(202, "ALLO command ignored.");
437 }
438 }
439 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
440 {
441 if ($2) {
442 reply(202, "ALLO command ignored.");
443 }
444 }
445 | RETR check_login SP pathname CRLF
446 {
447 if (noretr || (guest && noguestretr))
448 reply(500, "RETR command is disabled");
449 else if ($2 && $4 != NULL)
450 retrieve((char *) 0, $4);
451
452 if ($4 != NULL)
453 free($4);
454 }
455 | STOR check_login_ro SP pathname CRLF
456 {
457 if ($2 && $4 != NULL)
458 store($4, "w", 0);
459 if ($4 != NULL)
460 free($4);
461 }
462 | APPE check_login_ro SP pathname CRLF
463 {
464 if ($2 && $4 != NULL)
465 store($4, "a", 0);
466 if ($4 != NULL)
467 free($4);
468 }
469 | NLST check_login CRLF
470 {
471 if ($2)
472 send_file_list(".");
473 }
474 | NLST check_login SP STRING CRLF
475 {
476 if ($2 && $4 != NULL)
477 send_file_list($4);
478 if ($4 != NULL)
479 free($4);
480 }
481 | LIST check_login CRLF
482 {
483 if ($2)
484 retrieve("/bin/ls -lgA", "");
485 }
486 | LIST check_login SP pathstring CRLF
487 {
488 if ($2 && $4 != NULL)
489 retrieve("/bin/ls -lgA %s", $4);
490 if ($4 != NULL)
491 free($4);
492 }
493 | STAT check_login SP pathname CRLF
494 {
495 if ($2 && $4 != NULL)
496 statfilecmd($4);
497 if ($4 != NULL)
498 free($4);
499 }
500 | STAT check_login CRLF
501 {
502 if ($2) {
503 statcmd();
504 }
505 }
506 | DELE check_login_ro SP pathname CRLF
507 {
508 if ($2 && $4 != NULL)
509 delete($4);
510 if ($4 != NULL)
511 free($4);
512 }
513 | RNTO check_login_ro SP pathname CRLF
514 {
515 if ($2) {
516 if (fromname) {
517 renamecmd(fromname, $4);
518 free(fromname);
519 fromname = (char *) 0;
520 } else {
521 reply(503, "Bad sequence of commands.");
522 }
523 }
524 free($4);
525 }
526 | ABOR check_login CRLF
527 {
528 if ($2)
529 reply(225, "ABOR command successful.");
530 }
531 | CWD check_login CRLF
532 {
533 if ($2) {
534 if (guest)
535 cwd("/");
536 else
537 cwd(pw->pw_dir);
538 }
539 }
540 | CWD check_login SP pathname CRLF
541 {
542 if ($2 && $4 != NULL)
543 cwd($4);
544 if ($4 != NULL)
545 free($4);
546 }
547 | HELP CRLF
548 {
549 help(cmdtab, (char *) 0);
550 }
551 | HELP SP STRING CRLF
552 {
553 char *cp = $3;
554
555 if (strncasecmp(cp, "SITE", 4) == 0) {
556 cp = $3 + 4;
557 if (*cp == ' ')
558 cp++;
559 if (*cp)
560 help(sitetab, cp);
561 else
562 help(sitetab, (char *) 0);
563 } else
564 help(cmdtab, $3);
565 free($3);
566 }
567 | NOOP CRLF
568 {
569 reply(200, "NOOP command successful.");
570 }
571 | MKD check_login_ro SP pathname CRLF
572 {
573 if ($2 && $4 != NULL)
574 makedir($4);
575 if ($4 != NULL)
576 free($4);
577 }
578 | RMD check_login_ro SP pathname CRLF
579 {
580 if ($2 && $4 != NULL)
581 removedir($4);
582 if ($4 != NULL)
583 free($4);
584 }
585 | PWD check_login CRLF
586 {
587 if ($2)
588 pwd();
589 }
590 | CDUP check_login CRLF
591 {
592 if ($2)
593 cwd("..");
594 }
595 | SITE SP HELP CRLF
596 {
597 help(sitetab, (char *) 0);
598 }
599 | SITE SP HELP SP STRING CRLF
600 {
601 help(sitetab, $5);
602 free($5);
603 }
604 | SITE SP MDFIVE check_login SP pathname CRLF
605 {
606 char p[64], *q;
607
608 if ($4) {
609 q = MD5File($6, p);
610 if (q != NULL)
611 reply(200, "MD5(%s) = %s", $6, p);
612 else
613 perror_reply(550, $6);
614 }
615 if ($6)
616 free($6);
617 }
618 | SITE SP UMASK check_login CRLF
619 {
620 int oldmask;
621
622 if ($4) {
623 oldmask = umask(0);
624 (void) umask(oldmask);
625 reply(200, "Current UMASK is %03o", oldmask);
626 }
627 }
628 | SITE SP UMASK check_login SP octal_number CRLF
629 {
630 int oldmask;
631
632 if ($4) {
633 if (($6 == -1) || ($6 > 0777)) {
634 reply(501, "Bad UMASK value");
635 } else {
636 oldmask = umask($6);
637 reply(200,
638 "UMASK set to %03o (was %03o)",
639 $6, oldmask);
640 }
641 }
642 }
643 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
644 {
645 if ($4 && ($8 != NULL)) {
646 if ($6 > 0777)
647 reply(501,
648 "CHMOD: Mode value must be between 0 and 0777");
649 else if (chmod($8, $6) < 0)
650 perror_reply(550, $8);
651 else
652 reply(200, "CHMOD command successful.");
653 }
654 if ($8 != NULL)
655 free($8);
656 }
657 | SITE SP check_login IDLE CRLF
658 {
659 if ($3)
660 reply(200,
661 "Current IDLE time limit is %d seconds; max %d",
662 timeout, maxtimeout);
663 }
664 | SITE SP check_login IDLE SP NUMBER CRLF
665 {
666 if ($3) {
667 if ($6 < 30 || $6 > maxtimeout) {
668 reply(501,
669 "Maximum IDLE time must be between 30 and %d seconds",
670 maxtimeout);
671 } else {
672 timeout = $6;
673 (void) alarm((unsigned) timeout);
674 reply(200,
675 "Maximum IDLE time set to %d seconds",
676 timeout);
677 }
678 }
679 }
680 | STOU check_login_ro SP pathname CRLF
681 {
682 if ($2 && $4 != NULL)
683 store($4, "w", 1);
684 if ($4 != NULL)
685 free($4);
686 }
687 | SYST check_login CRLF
688 {
689 if ($2)
690#ifdef unix
691#ifdef BSD
692 reply(215, "UNIX Type: L%d Version: BSD-%d",
693 NBBY, BSD);
694#else /* BSD */
695 reply(215, "UNIX Type: L%d", NBBY);
696#endif /* BSD */
697#else /* unix */
698 reply(215, "UNKNOWN Type: L%d", NBBY);
699#endif /* unix */
700 }
701
702 /*
703 * SIZE is not in RFC959, but Postel has blessed it and
704 * it will be in the updated RFC.
705 *
706 * Return size of file in a format suitable for
707 * using with RESTART (we just count bytes).
708 */
709 | SIZE check_login SP pathname CRLF
710 {
711 if ($2 && $4 != NULL)
712 sizecmd($4);
713 if ($4 != NULL)
714 free($4);
715 }
716
717 /*
718 * MDTM is not in RFC959, but Postel has blessed it and
719 * it will be in the updated RFC.
720 *
721 * Return modification time of file as an ISO 3307
722 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
723 * where xxx is the fractional second (of any precision,
724 * not necessarily 3 digits)
725 */
726 | MDTM check_login SP pathname CRLF
727 {
728 if ($2 && $4 != NULL) {
729 struct stat stbuf;
730 if (stat($4, &stbuf) < 0)
731 reply(550, "%s: %s",
732 $4, strerror(errno));
733 else if (!S_ISREG(stbuf.st_mode)) {
734 reply(550, "%s: not a plain file.", $4);
735 } else {
736 struct tm *t;
737 t = gmtime(&stbuf.st_mtime);
738 reply(213,
739 "%04d%02d%02d%02d%02d%02d",
740 1900 + t->tm_year,
741 t->tm_mon+1, t->tm_mday,
742 t->tm_hour, t->tm_min, t->tm_sec);
743 }
744 }
745 if ($4 != NULL)
746 free($4);
747 }
748 | QUIT CRLF
749 {
750 reply(221, "Goodbye.");
751 dologout(0);
752 }
104char cbuf[512];
105char *fromname = (char *) 0;
106
107extern int epsvall;
108
109%}
110
111%union {
112 int i;
113 char *s;
114}
115
116%token
117 A B C E F I
118 L N P R S T
119 ALL
120
121 SP CRLF COMMA
122
123 USER PASS ACCT REIN QUIT PORT
124 PASV TYPE STRU MODE RETR STOR
125 APPE MLFL MAIL MSND MSOM MSAM
126 MRSQ MRCP ALLO REST RNFR RNTO
127 ABOR DELE CWD LIST NLST SITE
128 STAT HELP NOOP MKD RMD PWD
129 CDUP STOU SMNT SYST SIZE MDTM
130 LPRT LPSV EPRT EPSV
131
132 UMASK IDLE CHMOD MDFIVE
133
134 LEXERR
135
136%token <s> STRING
137%token <i> NUMBER
138
139%type <i> check_login octal_number byte_size
140%type <i> check_login_ro octal_number byte_size
141%type <i> check_login_epsv octal_number byte_size
142%type <i> struct_code mode_code type_code form_code
143%type <s> pathstring pathname password username
144%type <s> ALL
145
146%start cmd_list
147
148%%
149
150cmd_list
151 : /* empty */
152 | cmd_list cmd
153 {
154 if (fromname)
155 free(fromname);
156 fromname = (char *) 0;
157 restart_point = (off_t) 0;
158 }
159 | cmd_list rcmd
160 ;
161
162cmd
163 : USER SP username CRLF
164 {
165 user($3);
166 free($3);
167 }
168 | PASS SP password CRLF
169 {
170 pass($3);
171 free($3);
172 }
173 | PASS CRLF
174 {
175 pass("");
176 }
177 | PORT check_login SP host_port CRLF
178 {
179 if (epsvall) {
180 reply(501, "no PORT allowed after EPSV ALL");
181 goto port_done;
182 }
183 if (!$2)
184 goto port_done;
185 if (port_check("PORT") == 1)
186 goto port_done;
187#ifdef INET6
188 if ((his_addr.su_family != AF_INET6 ||
189 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
190 /* shoud never happen */
191 usedefault = 1;
192 reply(500, "Invalid address rejected.");
193 goto port_done;
194 }
195 port_check_v6("pcmd");
196#endif
197 port_done:
198 }
199 | LPRT check_login SP host_long_port CRLF
200 {
201 if (epsvall) {
202 reply(501, "no LPRT allowed after EPSV ALL");
203 goto lprt_done;
204 }
205 if (!$2)
206 goto lprt_done;
207 if (port_check("LPRT") == 1)
208 goto lprt_done;
209#ifdef INET6
210 if (his_addr.su_family != AF_INET6) {
211 usedefault = 1;
212 reply(500, "Invalid address rejected.");
213 goto lprt_done;
214 }
215 if (port_check_v6("LPRT") == 1)
216 goto lprt_done;
217#endif
218 lprt_done:
219 }
220 | EPRT check_login SP STRING CRLF
221 {
222 char delim;
223 char *tmp = NULL;
224 char *p, *q;
225 char *result[3];
226 struct addrinfo hints;
227 struct addrinfo *res;
228 int i;
229
230 if (epsvall) {
231 reply(501, "no EPRT allowed after EPSV ALL");
232 goto eprt_done;
233 }
234 if (!$2)
235 goto eprt_done;
236
237 memset(&data_dest, 0, sizeof(data_dest));
238 tmp = strdup($4);
239 if (ftpdebug)
240 syslog(LOG_DEBUG, "%s", tmp);
241 if (!tmp) {
242 fatalerror("not enough core");
243 /*NOTREACHED*/
244 }
245 p = tmp;
246 delim = p[0];
247 p++;
248 memset(result, 0, sizeof(result));
249 for (i = 0; i < 3; i++) {
250 q = strchr(p, delim);
251 if (!q || *q != delim) {
252 parsefail:
253 reply(500,
254 "Invalid argument, rejected.");
255 if (tmp)
256 free(tmp);
257 usedefault = 1;
258 goto eprt_done;
259 }
260 *q++ = '\0';
261 result[i] = p;
262 if (ftpdebug)
263 syslog(LOG_DEBUG, "%d: %s", i, p);
264 p = q;
265 }
266
267 /* some more sanity check */
268 p = result[0];
269 while (*p) {
270 if (!isdigit(*p))
271 goto parsefail;
272 p++;
273 }
274 p = result[2];
275 while (*p) {
276 if (!isdigit(*p))
277 goto parsefail;
278 p++;
279 }
280
281 /* grab address */
282 memset(&hints, 0, sizeof(hints));
283 if (atoi(result[0]) == 1)
284 hints.ai_family = PF_INET;
285#ifdef INET6
286 else if (atoi(result[0]) == 2)
287 hints.ai_family = PF_INET6;
288#endif
289 else
290 hints.ai_family = PF_UNSPEC; /*XXX*/
291 hints.ai_socktype = SOCK_STREAM;
292 i = getaddrinfo(result[1], result[2], &hints, &res);
293 if (i)
294 goto parsefail;
295 memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
296#ifdef INET6
297 if (his_addr.su_family == AF_INET6
298 && data_dest.su_family == AF_INET6) {
299 /* XXX more sanity checks! */
300 data_dest.su_sin6.sin6_scope_id =
301 his_addr.su_sin6.sin6_scope_id;
302 }
303#endif
304 free(tmp);
305 tmp = NULL;
306
307 if (port_check("EPRT") == 1)
308 goto eprt_done;
309#ifdef INET6
310 if (his_addr.su_family != AF_INET6) {
311 usedefault = 1;
312 reply(500, "Invalid address rejected.");
313 goto eprt_done;
314 }
315 if (port_check_v6("EPRT") == 1)
316 goto eprt_done;
317#endif
318 eprt_done:
319 free($4);
320 }
321 | PASV check_login CRLF
322 {
323 if (epsvall)
324 reply(501, "no PASV allowed after EPSV ALL");
325 else if ($2)
326 passive();
327 }
328 | LPSV check_login CRLF
329 {
330 if (epsvall)
331 reply(501, "no LPSV allowed after EPSV ALL");
332 else if ($2)
333 long_passive("LPSV", PF_UNSPEC);
334 }
335 | EPSV check_login_epsv SP NUMBER CRLF
336 {
337 if ($2) {
338 int pf;
339 switch ($4) {
340 case 1:
341 pf = PF_INET;
342 break;
343#ifdef INET6
344 case 2:
345 pf = PF_INET6;
346 break;
347#endif
348 default:
349 pf = -1; /*junk value*/
350 break;
351 }
352 long_passive("EPSV", pf);
353 }
354 }
355 | EPSV check_login_epsv SP ALL CRLF
356 {
357 if ($2) {
358 reply(200,
359 "EPSV ALL command successful.");
360 epsvall++;
361 }
362 }
363 | EPSV check_login_epsv CRLF
364 {
365 if ($2)
366 long_passive("EPSV", PF_UNSPEC);
367 }
368 | TYPE check_login SP type_code CRLF
369 {
370 if ($2) {
371 switch (cmd_type) {
372
373 case TYPE_A:
374 if (cmd_form == FORM_N) {
375 reply(200, "Type set to A.");
376 type = cmd_type;
377 form = cmd_form;
378 } else
379 reply(504, "Form must be N.");
380 break;
381
382 case TYPE_E:
383 reply(504, "Type E not implemented.");
384 break;
385
386 case TYPE_I:
387 reply(200, "Type set to I.");
388 type = cmd_type;
389 break;
390
391 case TYPE_L:
392#if NBBY == 8
393 if (cmd_bytesz == 8) {
394 reply(200,
395 "Type set to L (byte size 8).");
396 type = cmd_type;
397 } else
398 reply(504, "Byte size must be 8.");
399#else /* NBBY == 8 */
400 UNIMPLEMENTED for NBBY != 8
401#endif /* NBBY == 8 */
402 }
403 }
404 }
405 | STRU check_login SP struct_code CRLF
406 {
407 if ($2) {
408 switch ($4) {
409
410 case STRU_F:
411 reply(200, "STRU F ok.");
412 break;
413
414 default:
415 reply(504, "Unimplemented STRU type.");
416 }
417 }
418 }
419 | MODE check_login SP mode_code CRLF
420 {
421 if ($2) {
422 switch ($4) {
423
424 case MODE_S:
425 reply(200, "MODE S ok.");
426 break;
427
428 default:
429 reply(502, "Unimplemented MODE type.");
430 }
431 }
432 }
433 | ALLO check_login SP NUMBER CRLF
434 {
435 if ($2) {
436 reply(202, "ALLO command ignored.");
437 }
438 }
439 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
440 {
441 if ($2) {
442 reply(202, "ALLO command ignored.");
443 }
444 }
445 | RETR check_login SP pathname CRLF
446 {
447 if (noretr || (guest && noguestretr))
448 reply(500, "RETR command is disabled");
449 else if ($2 && $4 != NULL)
450 retrieve((char *) 0, $4);
451
452 if ($4 != NULL)
453 free($4);
454 }
455 | STOR check_login_ro SP pathname CRLF
456 {
457 if ($2 && $4 != NULL)
458 store($4, "w", 0);
459 if ($4 != NULL)
460 free($4);
461 }
462 | APPE check_login_ro SP pathname CRLF
463 {
464 if ($2 && $4 != NULL)
465 store($4, "a", 0);
466 if ($4 != NULL)
467 free($4);
468 }
469 | NLST check_login CRLF
470 {
471 if ($2)
472 send_file_list(".");
473 }
474 | NLST check_login SP STRING CRLF
475 {
476 if ($2 && $4 != NULL)
477 send_file_list($4);
478 if ($4 != NULL)
479 free($4);
480 }
481 | LIST check_login CRLF
482 {
483 if ($2)
484 retrieve("/bin/ls -lgA", "");
485 }
486 | LIST check_login SP pathstring CRLF
487 {
488 if ($2 && $4 != NULL)
489 retrieve("/bin/ls -lgA %s", $4);
490 if ($4 != NULL)
491 free($4);
492 }
493 | STAT check_login SP pathname CRLF
494 {
495 if ($2 && $4 != NULL)
496 statfilecmd($4);
497 if ($4 != NULL)
498 free($4);
499 }
500 | STAT check_login CRLF
501 {
502 if ($2) {
503 statcmd();
504 }
505 }
506 | DELE check_login_ro SP pathname CRLF
507 {
508 if ($2 && $4 != NULL)
509 delete($4);
510 if ($4 != NULL)
511 free($4);
512 }
513 | RNTO check_login_ro SP pathname CRLF
514 {
515 if ($2) {
516 if (fromname) {
517 renamecmd(fromname, $4);
518 free(fromname);
519 fromname = (char *) 0;
520 } else {
521 reply(503, "Bad sequence of commands.");
522 }
523 }
524 free($4);
525 }
526 | ABOR check_login CRLF
527 {
528 if ($2)
529 reply(225, "ABOR command successful.");
530 }
531 | CWD check_login CRLF
532 {
533 if ($2) {
534 if (guest)
535 cwd("/");
536 else
537 cwd(pw->pw_dir);
538 }
539 }
540 | CWD check_login SP pathname CRLF
541 {
542 if ($2 && $4 != NULL)
543 cwd($4);
544 if ($4 != NULL)
545 free($4);
546 }
547 | HELP CRLF
548 {
549 help(cmdtab, (char *) 0);
550 }
551 | HELP SP STRING CRLF
552 {
553 char *cp = $3;
554
555 if (strncasecmp(cp, "SITE", 4) == 0) {
556 cp = $3 + 4;
557 if (*cp == ' ')
558 cp++;
559 if (*cp)
560 help(sitetab, cp);
561 else
562 help(sitetab, (char *) 0);
563 } else
564 help(cmdtab, $3);
565 free($3);
566 }
567 | NOOP CRLF
568 {
569 reply(200, "NOOP command successful.");
570 }
571 | MKD check_login_ro SP pathname CRLF
572 {
573 if ($2 && $4 != NULL)
574 makedir($4);
575 if ($4 != NULL)
576 free($4);
577 }
578 | RMD check_login_ro SP pathname CRLF
579 {
580 if ($2 && $4 != NULL)
581 removedir($4);
582 if ($4 != NULL)
583 free($4);
584 }
585 | PWD check_login CRLF
586 {
587 if ($2)
588 pwd();
589 }
590 | CDUP check_login CRLF
591 {
592 if ($2)
593 cwd("..");
594 }
595 | SITE SP HELP CRLF
596 {
597 help(sitetab, (char *) 0);
598 }
599 | SITE SP HELP SP STRING CRLF
600 {
601 help(sitetab, $5);
602 free($5);
603 }
604 | SITE SP MDFIVE check_login SP pathname CRLF
605 {
606 char p[64], *q;
607
608 if ($4) {
609 q = MD5File($6, p);
610 if (q != NULL)
611 reply(200, "MD5(%s) = %s", $6, p);
612 else
613 perror_reply(550, $6);
614 }
615 if ($6)
616 free($6);
617 }
618 | SITE SP UMASK check_login CRLF
619 {
620 int oldmask;
621
622 if ($4) {
623 oldmask = umask(0);
624 (void) umask(oldmask);
625 reply(200, "Current UMASK is %03o", oldmask);
626 }
627 }
628 | SITE SP UMASK check_login SP octal_number CRLF
629 {
630 int oldmask;
631
632 if ($4) {
633 if (($6 == -1) || ($6 > 0777)) {
634 reply(501, "Bad UMASK value");
635 } else {
636 oldmask = umask($6);
637 reply(200,
638 "UMASK set to %03o (was %03o)",
639 $6, oldmask);
640 }
641 }
642 }
643 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
644 {
645 if ($4 && ($8 != NULL)) {
646 if ($6 > 0777)
647 reply(501,
648 "CHMOD: Mode value must be between 0 and 0777");
649 else if (chmod($8, $6) < 0)
650 perror_reply(550, $8);
651 else
652 reply(200, "CHMOD command successful.");
653 }
654 if ($8 != NULL)
655 free($8);
656 }
657 | SITE SP check_login IDLE CRLF
658 {
659 if ($3)
660 reply(200,
661 "Current IDLE time limit is %d seconds; max %d",
662 timeout, maxtimeout);
663 }
664 | SITE SP check_login IDLE SP NUMBER CRLF
665 {
666 if ($3) {
667 if ($6 < 30 || $6 > maxtimeout) {
668 reply(501,
669 "Maximum IDLE time must be between 30 and %d seconds",
670 maxtimeout);
671 } else {
672 timeout = $6;
673 (void) alarm((unsigned) timeout);
674 reply(200,
675 "Maximum IDLE time set to %d seconds",
676 timeout);
677 }
678 }
679 }
680 | STOU check_login_ro SP pathname CRLF
681 {
682 if ($2 && $4 != NULL)
683 store($4, "w", 1);
684 if ($4 != NULL)
685 free($4);
686 }
687 | SYST check_login CRLF
688 {
689 if ($2)
690#ifdef unix
691#ifdef BSD
692 reply(215, "UNIX Type: L%d Version: BSD-%d",
693 NBBY, BSD);
694#else /* BSD */
695 reply(215, "UNIX Type: L%d", NBBY);
696#endif /* BSD */
697#else /* unix */
698 reply(215, "UNKNOWN Type: L%d", NBBY);
699#endif /* unix */
700 }
701
702 /*
703 * SIZE is not in RFC959, but Postel has blessed it and
704 * it will be in the updated RFC.
705 *
706 * Return size of file in a format suitable for
707 * using with RESTART (we just count bytes).
708 */
709 | SIZE check_login SP pathname CRLF
710 {
711 if ($2 && $4 != NULL)
712 sizecmd($4);
713 if ($4 != NULL)
714 free($4);
715 }
716
717 /*
718 * MDTM is not in RFC959, but Postel has blessed it and
719 * it will be in the updated RFC.
720 *
721 * Return modification time of file as an ISO 3307
722 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
723 * where xxx is the fractional second (of any precision,
724 * not necessarily 3 digits)
725 */
726 | MDTM check_login SP pathname CRLF
727 {
728 if ($2 && $4 != NULL) {
729 struct stat stbuf;
730 if (stat($4, &stbuf) < 0)
731 reply(550, "%s: %s",
732 $4, strerror(errno));
733 else if (!S_ISREG(stbuf.st_mode)) {
734 reply(550, "%s: not a plain file.", $4);
735 } else {
736 struct tm *t;
737 t = gmtime(&stbuf.st_mtime);
738 reply(213,
739 "%04d%02d%02d%02d%02d%02d",
740 1900 + t->tm_year,
741 t->tm_mon+1, t->tm_mday,
742 t->tm_hour, t->tm_min, t->tm_sec);
743 }
744 }
745 if ($4 != NULL)
746 free($4);
747 }
748 | QUIT CRLF
749 {
750 reply(221, "Goodbye.");
751 dologout(0);
752 }
753 | error CRLF
753 | error
754 {
754 {
755 yyerrok;
755 yyclearin; /* discard lookahead data */
756 yyerrok; /* clear error condition */
757 state = 0; /* reset lexer state */
756 }
757 ;
758rcmd
759 : RNFR check_login_ro SP pathname CRLF
760 {
761 restart_point = (off_t) 0;
762 if ($2 && $4) {
763 if (fromname)
764 free(fromname);
765 fromname = (char *) 0;
766 if (renamefrom($4))
767 fromname = $4;
768 else
769 free($4);
770 } else if ($4) {
771 free($4);
772 }
773 }
774 | REST check_login SP byte_size CRLF
775 {
776 if ($2) {
777 if (fromname)
778 free(fromname);
779 fromname = (char *) 0;
780 restart_point = $4; /* XXX $4 is only "int" */
781 reply(350, "Restarting at %qd. %s",
782 restart_point,
783 "Send STORE or RETRIEVE to initiate transfer.");
784 }
785 }
786 ;
787
788username
789 : STRING
790 ;
791
792password
793 : /* empty */
794 {
795 $$ = (char *)calloc(1, sizeof(char));
796 }
797 | STRING
798 ;
799
800byte_size
801 : NUMBER
802 ;
803
804host_port
805 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
806 NUMBER COMMA NUMBER
807 {
808 char *a, *p;
809
810 data_dest.su_len = sizeof(struct sockaddr_in);
811 data_dest.su_family = AF_INET;
812 p = (char *)&data_dest.su_sin.sin_port;
813 p[0] = $9; p[1] = $11;
814 a = (char *)&data_dest.su_sin.sin_addr;
815 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
816 }
817 ;
818
819host_long_port
820 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
821 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
822 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
823 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
824 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
825 NUMBER
826 {
827 char *a, *p;
828
829 memset(&data_dest, 0, sizeof(data_dest));
830 data_dest.su_len = sizeof(struct sockaddr_in6);
831 data_dest.su_family = AF_INET6;
832 p = (char *)&data_dest.su_port;
833 p[0] = $39; p[1] = $41;
834 a = (char *)&data_dest.su_sin6.sin6_addr;
835 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
836 a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19;
837 a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27;
838 a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
839 if (his_addr.su_family == AF_INET6) {
840 /* XXX more sanity checks! */
841 data_dest.su_sin6.sin6_scope_id =
842 his_addr.su_sin6.sin6_scope_id;
843 }
844 if ($1 != 6 || $3 != 16 || $37 != 2)
845 memset(&data_dest, 0, sizeof(data_dest));
846 }
847 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
848 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
849 NUMBER
850 {
851 char *a, *p;
852
853 memset(&data_dest, 0, sizeof(data_dest));
854 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
855 data_dest.su_family = AF_INET;
856 p = (char *)&data_dest.su_port;
857 p[0] = $15; p[1] = $17;
858 a = (char *)&data_dest.su_sin.sin_addr;
859 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
860 if ($1 != 4 || $3 != 4 || $13 != 2)
861 memset(&data_dest, 0, sizeof(data_dest));
862 }
863 ;
864
865form_code
866 : N
867 {
868 $$ = FORM_N;
869 }
870 | T
871 {
872 $$ = FORM_T;
873 }
874 | C
875 {
876 $$ = FORM_C;
877 }
878 ;
879
880type_code
881 : A
882 {
883 cmd_type = TYPE_A;
884 cmd_form = FORM_N;
885 }
886 | A SP form_code
887 {
888 cmd_type = TYPE_A;
889 cmd_form = $3;
890 }
891 | E
892 {
893 cmd_type = TYPE_E;
894 cmd_form = FORM_N;
895 }
896 | E SP form_code
897 {
898 cmd_type = TYPE_E;
899 cmd_form = $3;
900 }
901 | I
902 {
903 cmd_type = TYPE_I;
904 }
905 | L
906 {
907 cmd_type = TYPE_L;
908 cmd_bytesz = NBBY;
909 }
910 | L SP byte_size
911 {
912 cmd_type = TYPE_L;
913 cmd_bytesz = $3;
914 }
915 /* this is for a bug in the BBN ftp */
916 | L byte_size
917 {
918 cmd_type = TYPE_L;
919 cmd_bytesz = $2;
920 }
921 ;
922
923struct_code
924 : F
925 {
926 $$ = STRU_F;
927 }
928 | R
929 {
930 $$ = STRU_R;
931 }
932 | P
933 {
934 $$ = STRU_P;
935 }
936 ;
937
938mode_code
939 : S
940 {
941 $$ = MODE_S;
942 }
943 | B
944 {
945 $$ = MODE_B;
946 }
947 | C
948 {
949 $$ = MODE_C;
950 }
951 ;
952
953pathname
954 : pathstring
955 {
956 /*
957 * Problem: this production is used for all pathname
958 * processing, but only gives a 550 error reply.
959 * This is a valid reply in some cases but not in others.
960 */
961 if (logged_in && $1) {
962 glob_t gl;
963 int flags =
964 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
965
966 memset(&gl, 0, sizeof(gl));
967 flags |= GLOB_MAXPATH;
968 gl.gl_matchc = MAXGLOBARGS;
969 if (glob($1, flags, NULL, &gl) ||
970 gl.gl_pathc == 0) {
971 reply(550, "not found");
972 $$ = NULL;
973 } else if (gl.gl_pathc > 1) {
974 reply(550, "ambiguous");
975 $$ = NULL;
976 } else {
977 $$ = strdup(gl.gl_pathv[0]);
978 }
979 globfree(&gl);
980 free($1);
981 } else
982 $$ = $1;
983 }
984 ;
985
986pathstring
987 : STRING
988 ;
989
990octal_number
991 : NUMBER
992 {
993 int ret, dec, multby, digit;
994
995 /*
996 * Convert a number that was read as decimal number
997 * to what it would be if it had been read as octal.
998 */
999 dec = $1;
1000 multby = 1;
1001 ret = 0;
1002 while (dec) {
1003 digit = dec%10;
1004 if (digit > 7) {
1005 ret = -1;
1006 break;
1007 }
1008 ret += digit * multby;
1009 multby *= 8;
1010 dec /= 10;
1011 }
1012 $$ = ret;
1013 }
1014 ;
1015
1016
1017check_login
1018 : /* empty */
1019 {
1020 $$ = check_login1();
1021 }
1022 ;
1023
1024check_login_epsv
1025 : /* empty */
1026 {
1027 if (noepsv) {
1028 reply(500, "EPSV command disabled");
1029 $$ = 0;
1030 }
1031 else
1032 $$ = check_login1();
1033 }
1034 ;
1035
1036check_login_ro
1037 : /* empty */
1038 {
1039 if (readonly) {
1040 reply(550, "Permission denied.");
1041 $$ = 0;
1042 }
1043 else
1044 $$ = check_login1();
1045 }
1046 ;
1047
1048%%
1049
758 }
759 ;
760rcmd
761 : RNFR check_login_ro SP pathname CRLF
762 {
763 restart_point = (off_t) 0;
764 if ($2 && $4) {
765 if (fromname)
766 free(fromname);
767 fromname = (char *) 0;
768 if (renamefrom($4))
769 fromname = $4;
770 else
771 free($4);
772 } else if ($4) {
773 free($4);
774 }
775 }
776 | REST check_login SP byte_size CRLF
777 {
778 if ($2) {
779 if (fromname)
780 free(fromname);
781 fromname = (char *) 0;
782 restart_point = $4; /* XXX $4 is only "int" */
783 reply(350, "Restarting at %qd. %s",
784 restart_point,
785 "Send STORE or RETRIEVE to initiate transfer.");
786 }
787 }
788 ;
789
790username
791 : STRING
792 ;
793
794password
795 : /* empty */
796 {
797 $$ = (char *)calloc(1, sizeof(char));
798 }
799 | STRING
800 ;
801
802byte_size
803 : NUMBER
804 ;
805
806host_port
807 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
808 NUMBER COMMA NUMBER
809 {
810 char *a, *p;
811
812 data_dest.su_len = sizeof(struct sockaddr_in);
813 data_dest.su_family = AF_INET;
814 p = (char *)&data_dest.su_sin.sin_port;
815 p[0] = $9; p[1] = $11;
816 a = (char *)&data_dest.su_sin.sin_addr;
817 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
818 }
819 ;
820
821host_long_port
822 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
823 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
824 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
825 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
826 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
827 NUMBER
828 {
829 char *a, *p;
830
831 memset(&data_dest, 0, sizeof(data_dest));
832 data_dest.su_len = sizeof(struct sockaddr_in6);
833 data_dest.su_family = AF_INET6;
834 p = (char *)&data_dest.su_port;
835 p[0] = $39; p[1] = $41;
836 a = (char *)&data_dest.su_sin6.sin6_addr;
837 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
838 a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19;
839 a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27;
840 a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
841 if (his_addr.su_family == AF_INET6) {
842 /* XXX more sanity checks! */
843 data_dest.su_sin6.sin6_scope_id =
844 his_addr.su_sin6.sin6_scope_id;
845 }
846 if ($1 != 6 || $3 != 16 || $37 != 2)
847 memset(&data_dest, 0, sizeof(data_dest));
848 }
849 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
850 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
851 NUMBER
852 {
853 char *a, *p;
854
855 memset(&data_dest, 0, sizeof(data_dest));
856 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
857 data_dest.su_family = AF_INET;
858 p = (char *)&data_dest.su_port;
859 p[0] = $15; p[1] = $17;
860 a = (char *)&data_dest.su_sin.sin_addr;
861 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
862 if ($1 != 4 || $3 != 4 || $13 != 2)
863 memset(&data_dest, 0, sizeof(data_dest));
864 }
865 ;
866
867form_code
868 : N
869 {
870 $$ = FORM_N;
871 }
872 | T
873 {
874 $$ = FORM_T;
875 }
876 | C
877 {
878 $$ = FORM_C;
879 }
880 ;
881
882type_code
883 : A
884 {
885 cmd_type = TYPE_A;
886 cmd_form = FORM_N;
887 }
888 | A SP form_code
889 {
890 cmd_type = TYPE_A;
891 cmd_form = $3;
892 }
893 | E
894 {
895 cmd_type = TYPE_E;
896 cmd_form = FORM_N;
897 }
898 | E SP form_code
899 {
900 cmd_type = TYPE_E;
901 cmd_form = $3;
902 }
903 | I
904 {
905 cmd_type = TYPE_I;
906 }
907 | L
908 {
909 cmd_type = TYPE_L;
910 cmd_bytesz = NBBY;
911 }
912 | L SP byte_size
913 {
914 cmd_type = TYPE_L;
915 cmd_bytesz = $3;
916 }
917 /* this is for a bug in the BBN ftp */
918 | L byte_size
919 {
920 cmd_type = TYPE_L;
921 cmd_bytesz = $2;
922 }
923 ;
924
925struct_code
926 : F
927 {
928 $$ = STRU_F;
929 }
930 | R
931 {
932 $$ = STRU_R;
933 }
934 | P
935 {
936 $$ = STRU_P;
937 }
938 ;
939
940mode_code
941 : S
942 {
943 $$ = MODE_S;
944 }
945 | B
946 {
947 $$ = MODE_B;
948 }
949 | C
950 {
951 $$ = MODE_C;
952 }
953 ;
954
955pathname
956 : pathstring
957 {
958 /*
959 * Problem: this production is used for all pathname
960 * processing, but only gives a 550 error reply.
961 * This is a valid reply in some cases but not in others.
962 */
963 if (logged_in && $1) {
964 glob_t gl;
965 int flags =
966 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
967
968 memset(&gl, 0, sizeof(gl));
969 flags |= GLOB_MAXPATH;
970 gl.gl_matchc = MAXGLOBARGS;
971 if (glob($1, flags, NULL, &gl) ||
972 gl.gl_pathc == 0) {
973 reply(550, "not found");
974 $$ = NULL;
975 } else if (gl.gl_pathc > 1) {
976 reply(550, "ambiguous");
977 $$ = NULL;
978 } else {
979 $$ = strdup(gl.gl_pathv[0]);
980 }
981 globfree(&gl);
982 free($1);
983 } else
984 $$ = $1;
985 }
986 ;
987
988pathstring
989 : STRING
990 ;
991
992octal_number
993 : NUMBER
994 {
995 int ret, dec, multby, digit;
996
997 /*
998 * Convert a number that was read as decimal number
999 * to what it would be if it had been read as octal.
1000 */
1001 dec = $1;
1002 multby = 1;
1003 ret = 0;
1004 while (dec) {
1005 digit = dec%10;
1006 if (digit > 7) {
1007 ret = -1;
1008 break;
1009 }
1010 ret += digit * multby;
1011 multby *= 8;
1012 dec /= 10;
1013 }
1014 $$ = ret;
1015 }
1016 ;
1017
1018
1019check_login
1020 : /* empty */
1021 {
1022 $$ = check_login1();
1023 }
1024 ;
1025
1026check_login_epsv
1027 : /* empty */
1028 {
1029 if (noepsv) {
1030 reply(500, "EPSV command disabled");
1031 $$ = 0;
1032 }
1033 else
1034 $$ = check_login1();
1035 }
1036 ;
1037
1038check_login_ro
1039 : /* empty */
1040 {
1041 if (readonly) {
1042 reply(550, "Permission denied.");
1043 $$ = 0;
1044 }
1045 else
1046 $$ = check_login1();
1047 }
1048 ;
1049
1050%%
1051
1050extern jmp_buf errcatch;
1051
1052#define CMD 0 /* beginning of command */
1053#define ARGS 1 /* expect miscellaneous arguments */
1054#define STR1 2 /* expect SP followed by STRING */
1055#define STR2 3 /* expect STRING */
1056#define OSTR 4 /* optional SP then STRING */
1057#define ZSTR1 5 /* optional SP then optional STRING */
1058#define ZSTR2 6 /* optional STRING after SP */
1059#define SITECMD 7 /* SITE command */
1060#define NSTR 8 /* Number followed by a string */
1061
1062#define MAXGLOBARGS 1000
1063
1064struct tab {
1065 char *name;
1066 short token;
1067 short state;
1068 short implemented; /* 1 if command is implemented */
1069 char *help;
1070};
1071
1072struct tab cmdtab[] = { /* In order defined in RFC 765 */
1073 { "USER", USER, STR1, 1, "<sp> username" },
1074 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1075 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1076 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1077 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1078 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1079 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
1080 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1081 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1082 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1083 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1084 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1085 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1086 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1087 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1088 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1089 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1090 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1091 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1092 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1093 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1094 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1095 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1096 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1097 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1098 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1099 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1100 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1101 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1102 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1103 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1104 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1105 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1106 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1107 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1108 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1109 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1110 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1111 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1112 { "NOOP", NOOP, ARGS, 1, "" },
1113 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1114 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1115 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1116 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1117 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1118 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1119 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1120 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1121 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1122 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1123 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1124 { NULL, 0, 0, 0, 0 }
1125};
1126
1127struct tab sitetab[] = {
1128 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1129 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1130 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1131 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1132 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1133 { NULL, 0, 0, 0, 0 }
1134};
1135
1136static char *copy __P((char *));
1137static void help __P((struct tab *, char *));
1138static struct tab *
1139 lookup __P((struct tab *, char *));
1140static int port_check __P((const char *));
1141static int port_check_v6 __P((const char *));
1142static void sizecmd __P((char *));
1143static void toolong __P((int));
1144static void v4map_data_dest __P((void));
1145static int yylex __P((void));
1146
1147static struct tab *
1148lookup(p, cmd)
1149 struct tab *p;
1150 char *cmd;
1151{
1152
1153 for (; p->name != NULL; p++)
1154 if (strcmp(cmd, p->name) == 0)
1155 return (p);
1156 return (0);
1157}
1158
1159#include <arpa/telnet.h>
1160
1161/*
1162 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1163 */
1164char *
1165getline(s, n, iop)
1166 char *s;
1167 int n;
1168 FILE *iop;
1169{
1170 int c;
1171 register char *cs;
1172
1173 cs = s;
1174/* tmpline may contain saved command from urgent mode interruption */
1175 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1176 *cs++ = tmpline[c];
1177 if (tmpline[c] == '\n') {
1178 *cs++ = '\0';
1179 if (ftpdebug)
1180 syslog(LOG_DEBUG, "command: %s", s);
1181 tmpline[0] = '\0';
1182 return(s);
1183 }
1184 if (c == 0)
1185 tmpline[0] = '\0';
1186 }
1187 while ((c = getc(iop)) != EOF) {
1188 c &= 0377;
1189 if (c == IAC) {
1190 if ((c = getc(iop)) != EOF) {
1191 c &= 0377;
1192 switch (c) {
1193 case WILL:
1194 case WONT:
1195 c = getc(iop);
1196 printf("%c%c%c", IAC, DONT, 0377&c);
1197 (void) fflush(stdout);
1198 continue;
1199 case DO:
1200 case DONT:
1201 c = getc(iop);
1202 printf("%c%c%c", IAC, WONT, 0377&c);
1203 (void) fflush(stdout);
1204 continue;
1205 case IAC:
1206 break;
1207 default:
1208 continue; /* ignore command */
1209 }
1210 }
1211 }
1212 *cs++ = c;
1213 if (--n <= 0 || c == '\n')
1214 break;
1215 }
1216 if (c == EOF && cs == s)
1217 return (NULL);
1218 *cs++ = '\0';
1219 if (ftpdebug) {
1220 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1221 /* Don't syslog passwords */
1222 syslog(LOG_DEBUG, "command: %.5s ???", s);
1223 } else {
1224 register char *cp;
1225 register int len;
1226
1227 /* Don't syslog trailing CR-LF */
1228 len = strlen(s);
1229 cp = s + len - 1;
1230 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1231 --cp;
1232 --len;
1233 }
1234 syslog(LOG_DEBUG, "command: %.*s", len, s);
1235 }
1236 }
1237 return (s);
1238}
1239
1240static void
1241toolong(signo)
1242 int signo;
1243{
1244
1245 reply(421,
1246 "Timeout (%d seconds): closing control connection.", timeout);
1247 if (logging)
1248 syslog(LOG_INFO, "User %s timed out after %d seconds",
1249 (pw ? pw -> pw_name : "unknown"), timeout);
1250 dologout(1);
1251}
1252
1253static int
1254yylex()
1255{
1052#define CMD 0 /* beginning of command */
1053#define ARGS 1 /* expect miscellaneous arguments */
1054#define STR1 2 /* expect SP followed by STRING */
1055#define STR2 3 /* expect STRING */
1056#define OSTR 4 /* optional SP then STRING */
1057#define ZSTR1 5 /* optional SP then optional STRING */
1058#define ZSTR2 6 /* optional STRING after SP */
1059#define SITECMD 7 /* SITE command */
1060#define NSTR 8 /* Number followed by a string */
1061
1062#define MAXGLOBARGS 1000
1063
1064struct tab {
1065 char *name;
1066 short token;
1067 short state;
1068 short implemented; /* 1 if command is implemented */
1069 char *help;
1070};
1071
1072struct tab cmdtab[] = { /* In order defined in RFC 765 */
1073 { "USER", USER, STR1, 1, "<sp> username" },
1074 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1075 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1076 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1077 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1078 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1079 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
1080 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1081 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1082 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1083 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1084 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1085 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1086 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1087 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1088 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1089 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1090 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1091 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1092 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1093 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1094 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1095 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1096 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1097 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1098 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1099 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1100 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1101 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1102 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1103 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1104 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1105 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1106 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1107 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1108 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1109 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1110 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1111 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1112 { "NOOP", NOOP, ARGS, 1, "" },
1113 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1114 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1115 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1116 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1117 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1118 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1119 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1120 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1121 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1122 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1123 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1124 { NULL, 0, 0, 0, 0 }
1125};
1126
1127struct tab sitetab[] = {
1128 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1129 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1130 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1131 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1132 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1133 { NULL, 0, 0, 0, 0 }
1134};
1135
1136static char *copy __P((char *));
1137static void help __P((struct tab *, char *));
1138static struct tab *
1139 lookup __P((struct tab *, char *));
1140static int port_check __P((const char *));
1141static int port_check_v6 __P((const char *));
1142static void sizecmd __P((char *));
1143static void toolong __P((int));
1144static void v4map_data_dest __P((void));
1145static int yylex __P((void));
1146
1147static struct tab *
1148lookup(p, cmd)
1149 struct tab *p;
1150 char *cmd;
1151{
1152
1153 for (; p->name != NULL; p++)
1154 if (strcmp(cmd, p->name) == 0)
1155 return (p);
1156 return (0);
1157}
1158
1159#include <arpa/telnet.h>
1160
1161/*
1162 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1163 */
1164char *
1165getline(s, n, iop)
1166 char *s;
1167 int n;
1168 FILE *iop;
1169{
1170 int c;
1171 register char *cs;
1172
1173 cs = s;
1174/* tmpline may contain saved command from urgent mode interruption */
1175 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1176 *cs++ = tmpline[c];
1177 if (tmpline[c] == '\n') {
1178 *cs++ = '\0';
1179 if (ftpdebug)
1180 syslog(LOG_DEBUG, "command: %s", s);
1181 tmpline[0] = '\0';
1182 return(s);
1183 }
1184 if (c == 0)
1185 tmpline[0] = '\0';
1186 }
1187 while ((c = getc(iop)) != EOF) {
1188 c &= 0377;
1189 if (c == IAC) {
1190 if ((c = getc(iop)) != EOF) {
1191 c &= 0377;
1192 switch (c) {
1193 case WILL:
1194 case WONT:
1195 c = getc(iop);
1196 printf("%c%c%c", IAC, DONT, 0377&c);
1197 (void) fflush(stdout);
1198 continue;
1199 case DO:
1200 case DONT:
1201 c = getc(iop);
1202 printf("%c%c%c", IAC, WONT, 0377&c);
1203 (void) fflush(stdout);
1204 continue;
1205 case IAC:
1206 break;
1207 default:
1208 continue; /* ignore command */
1209 }
1210 }
1211 }
1212 *cs++ = c;
1213 if (--n <= 0 || c == '\n')
1214 break;
1215 }
1216 if (c == EOF && cs == s)
1217 return (NULL);
1218 *cs++ = '\0';
1219 if (ftpdebug) {
1220 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1221 /* Don't syslog passwords */
1222 syslog(LOG_DEBUG, "command: %.5s ???", s);
1223 } else {
1224 register char *cp;
1225 register int len;
1226
1227 /* Don't syslog trailing CR-LF */
1228 len = strlen(s);
1229 cp = s + len - 1;
1230 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1231 --cp;
1232 --len;
1233 }
1234 syslog(LOG_DEBUG, "command: %.*s", len, s);
1235 }
1236 }
1237 return (s);
1238}
1239
1240static void
1241toolong(signo)
1242 int signo;
1243{
1244
1245 reply(421,
1246 "Timeout (%d seconds): closing control connection.", timeout);
1247 if (logging)
1248 syslog(LOG_INFO, "User %s timed out after %d seconds",
1249 (pw ? pw -> pw_name : "unknown"), timeout);
1250 dologout(1);
1251}
1252
1253static int
1254yylex()
1255{
1256 static int cpos, state;
1256 static int cpos;
1257 char *cp, *cp2;
1258 struct tab *p;
1259 int n;
1260 char c;
1261
1262 for (;;) {
1263 switch (state) {
1264
1265 case CMD:
1266 (void) signal(SIGALRM, toolong);
1267 (void) alarm((unsigned) timeout);
1268 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1269 reply(221, "You could at least say goodbye.");
1270 dologout(0);
1271 }
1272 (void) alarm(0);
1273#ifdef SETPROCTITLE
1274 if (strncasecmp(cbuf, "PASS", 4) != 0)
1275 setproctitle("%s: %s", proctitle, cbuf);
1276#endif /* SETPROCTITLE */
1277 if ((cp = strchr(cbuf, '\r'))) {
1278 *cp++ = '\n';
1279 *cp = '\0';
1280 }
1281 if ((cp = strpbrk(cbuf, " \n")))
1282 cpos = cp - cbuf;
1283 if (cpos == 0)
1284 cpos = 4;
1285 c = cbuf[cpos];
1286 cbuf[cpos] = '\0';
1287 upper(cbuf);
1288 p = lookup(cmdtab, cbuf);
1289 cbuf[cpos] = c;
1290 if (p != 0) {
1291 if (p->implemented == 0) {
1292 nack(p->name);
1257 char *cp, *cp2;
1258 struct tab *p;
1259 int n;
1260 char c;
1261
1262 for (;;) {
1263 switch (state) {
1264
1265 case CMD:
1266 (void) signal(SIGALRM, toolong);
1267 (void) alarm((unsigned) timeout);
1268 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1269 reply(221, "You could at least say goodbye.");
1270 dologout(0);
1271 }
1272 (void) alarm(0);
1273#ifdef SETPROCTITLE
1274 if (strncasecmp(cbuf, "PASS", 4) != 0)
1275 setproctitle("%s: %s", proctitle, cbuf);
1276#endif /* SETPROCTITLE */
1277 if ((cp = strchr(cbuf, '\r'))) {
1278 *cp++ = '\n';
1279 *cp = '\0';
1280 }
1281 if ((cp = strpbrk(cbuf, " \n")))
1282 cpos = cp - cbuf;
1283 if (cpos == 0)
1284 cpos = 4;
1285 c = cbuf[cpos];
1286 cbuf[cpos] = '\0';
1287 upper(cbuf);
1288 p = lookup(cmdtab, cbuf);
1289 cbuf[cpos] = c;
1290 if (p != 0) {
1291 if (p->implemented == 0) {
1292 nack(p->name);
1293 longjmp(errcatch,0);
1294 /* NOTREACHED */
1293 return (LEXERR);
1295 }
1296 state = p->state;
1297 yylval.s = p->name;
1298 return (p->token);
1299 }
1300 break;
1301
1302 case SITECMD:
1303 if (cbuf[cpos] == ' ') {
1304 cpos++;
1305 return (SP);
1306 }
1307 cp = &cbuf[cpos];
1308 if ((cp2 = strpbrk(cp, " \n")))
1309 cpos = cp2 - cbuf;
1310 c = cbuf[cpos];
1311 cbuf[cpos] = '\0';
1312 upper(cp);
1313 p = lookup(sitetab, cp);
1314 cbuf[cpos] = c;
1315 if (guest == 0 && p != 0) {
1316 if (p->implemented == 0) {
1317 state = CMD;
1318 nack(p->name);
1294 }
1295 state = p->state;
1296 yylval.s = p->name;
1297 return (p->token);
1298 }
1299 break;
1300
1301 case SITECMD:
1302 if (cbuf[cpos] == ' ') {
1303 cpos++;
1304 return (SP);
1305 }
1306 cp = &cbuf[cpos];
1307 if ((cp2 = strpbrk(cp, " \n")))
1308 cpos = cp2 - cbuf;
1309 c = cbuf[cpos];
1310 cbuf[cpos] = '\0';
1311 upper(cp);
1312 p = lookup(sitetab, cp);
1313 cbuf[cpos] = c;
1314 if (guest == 0 && p != 0) {
1315 if (p->implemented == 0) {
1316 state = CMD;
1317 nack(p->name);
1319 longjmp(errcatch,0);
1320 /* NOTREACHED */
1318 return (LEXERR);
1321 }
1322 state = p->state;
1323 yylval.s = p->name;
1324 return (p->token);
1325 }
1326 state = CMD;
1327 break;
1328
1329 case ZSTR1:
1330 case OSTR:
1331 if (cbuf[cpos] == '\n') {
1332 state = CMD;
1333 return (CRLF);
1334 }
1335 /* FALLTHROUGH */
1336
1337 case STR1:
1338 dostr1:
1339 if (cbuf[cpos] == ' ') {
1340 cpos++;
1341 state = state == OSTR ? STR2 : state+1;
1342 return (SP);
1343 }
1344 break;
1345
1346 case ZSTR2:
1347 if (cbuf[cpos] == '\n') {
1348 state = CMD;
1349 return (CRLF);
1350 }
1351 /* FALLTHROUGH */
1352
1353 case STR2:
1354 cp = &cbuf[cpos];
1355 n = strlen(cp);
1356 cpos += n - 1;
1357 /*
1358 * Make sure the string is nonempty and \n terminated.
1359 */
1360 if (n > 1 && cbuf[cpos] == '\n') {
1361 cbuf[cpos] = '\0';
1362 yylval.s = copy(cp);
1363 cbuf[cpos] = '\n';
1364 state = ARGS;
1365 return (STRING);
1366 }
1367 break;
1368
1369 case NSTR:
1370 if (cbuf[cpos] == ' ') {
1371 cpos++;
1372 return (SP);
1373 }
1374 if (isdigit(cbuf[cpos])) {
1375 cp = &cbuf[cpos];
1376 while (isdigit(cbuf[++cpos]))
1377 ;
1378 c = cbuf[cpos];
1379 cbuf[cpos] = '\0';
1380 yylval.i = atoi(cp);
1381 cbuf[cpos] = c;
1382 state = STR1;
1383 return (NUMBER);
1384 }
1385 state = STR1;
1386 goto dostr1;
1387
1388 case ARGS:
1389 if (isdigit(cbuf[cpos])) {
1390 cp = &cbuf[cpos];
1391 while (isdigit(cbuf[++cpos]))
1392 ;
1393 c = cbuf[cpos];
1394 cbuf[cpos] = '\0';
1395 yylval.i = atoi(cp);
1396 cbuf[cpos] = c;
1397 return (NUMBER);
1398 }
1399 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1400 && !isalnum(cbuf[cpos + 3])) {
1401 cpos += 3;
1402 return ALL;
1403 }
1404 switch (cbuf[cpos++]) {
1405
1406 case '\n':
1407 state = CMD;
1408 return (CRLF);
1409
1410 case ' ':
1411 return (SP);
1412
1413 case ',':
1414 return (COMMA);
1415
1416 case 'A':
1417 case 'a':
1418 return (A);
1419
1420 case 'B':
1421 case 'b':
1422 return (B);
1423
1424 case 'C':
1425 case 'c':
1426 return (C);
1427
1428 case 'E':
1429 case 'e':
1430 return (E);
1431
1432 case 'F':
1433 case 'f':
1434 return (F);
1435
1436 case 'I':
1437 case 'i':
1438 return (I);
1439
1440 case 'L':
1441 case 'l':
1442 return (L);
1443
1444 case 'N':
1445 case 'n':
1446 return (N);
1447
1448 case 'P':
1449 case 'p':
1450 return (P);
1451
1452 case 'R':
1453 case 'r':
1454 return (R);
1455
1456 case 'S':
1457 case 's':
1458 return (S);
1459
1460 case 'T':
1461 case 't':
1462 return (T);
1463
1464 }
1465 break;
1466
1467 default:
1468 fatalerror("Unknown state in scanner.");
1469 }
1319 }
1320 state = p->state;
1321 yylval.s = p->name;
1322 return (p->token);
1323 }
1324 state = CMD;
1325 break;
1326
1327 case ZSTR1:
1328 case OSTR:
1329 if (cbuf[cpos] == '\n') {
1330 state = CMD;
1331 return (CRLF);
1332 }
1333 /* FALLTHROUGH */
1334
1335 case STR1:
1336 dostr1:
1337 if (cbuf[cpos] == ' ') {
1338 cpos++;
1339 state = state == OSTR ? STR2 : state+1;
1340 return (SP);
1341 }
1342 break;
1343
1344 case ZSTR2:
1345 if (cbuf[cpos] == '\n') {
1346 state = CMD;
1347 return (CRLF);
1348 }
1349 /* FALLTHROUGH */
1350
1351 case STR2:
1352 cp = &cbuf[cpos];
1353 n = strlen(cp);
1354 cpos += n - 1;
1355 /*
1356 * Make sure the string is nonempty and \n terminated.
1357 */
1358 if (n > 1 && cbuf[cpos] == '\n') {
1359 cbuf[cpos] = '\0';
1360 yylval.s = copy(cp);
1361 cbuf[cpos] = '\n';
1362 state = ARGS;
1363 return (STRING);
1364 }
1365 break;
1366
1367 case NSTR:
1368 if (cbuf[cpos] == ' ') {
1369 cpos++;
1370 return (SP);
1371 }
1372 if (isdigit(cbuf[cpos])) {
1373 cp = &cbuf[cpos];
1374 while (isdigit(cbuf[++cpos]))
1375 ;
1376 c = cbuf[cpos];
1377 cbuf[cpos] = '\0';
1378 yylval.i = atoi(cp);
1379 cbuf[cpos] = c;
1380 state = STR1;
1381 return (NUMBER);
1382 }
1383 state = STR1;
1384 goto dostr1;
1385
1386 case ARGS:
1387 if (isdigit(cbuf[cpos])) {
1388 cp = &cbuf[cpos];
1389 while (isdigit(cbuf[++cpos]))
1390 ;
1391 c = cbuf[cpos];
1392 cbuf[cpos] = '\0';
1393 yylval.i = atoi(cp);
1394 cbuf[cpos] = c;
1395 return (NUMBER);
1396 }
1397 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1398 && !isalnum(cbuf[cpos + 3])) {
1399 cpos += 3;
1400 return ALL;
1401 }
1402 switch (cbuf[cpos++]) {
1403
1404 case '\n':
1405 state = CMD;
1406 return (CRLF);
1407
1408 case ' ':
1409 return (SP);
1410
1411 case ',':
1412 return (COMMA);
1413
1414 case 'A':
1415 case 'a':
1416 return (A);
1417
1418 case 'B':
1419 case 'b':
1420 return (B);
1421
1422 case 'C':
1423 case 'c':
1424 return (C);
1425
1426 case 'E':
1427 case 'e':
1428 return (E);
1429
1430 case 'F':
1431 case 'f':
1432 return (F);
1433
1434 case 'I':
1435 case 'i':
1436 return (I);
1437
1438 case 'L':
1439 case 'l':
1440 return (L);
1441
1442 case 'N':
1443 case 'n':
1444 return (N);
1445
1446 case 'P':
1447 case 'p':
1448 return (P);
1449
1450 case 'R':
1451 case 'r':
1452 return (R);
1453
1454 case 'S':
1455 case 's':
1456 return (S);
1457
1458 case 'T':
1459 case 't':
1460 return (T);
1461
1462 }
1463 break;
1464
1465 default:
1466 fatalerror("Unknown state in scanner.");
1467 }
1470 yyerror((char *) 0);
1471 state = CMD;
1468 state = CMD;
1472 longjmp(errcatch,0);
1469 return (LEXERR);
1473 }
1474}
1475
1476void
1477upper(s)
1478 char *s;
1479{
1480 while (*s != '\0') {
1481 if (islower(*s))
1482 *s = toupper(*s);
1483 s++;
1484 }
1485}
1486
1487static char *
1488copy(s)
1489 char *s;
1490{
1491 char *p;
1492
1493 p = malloc((unsigned) strlen(s) + 1);
1494 if (p == NULL)
1495 fatalerror("Ran out of memory.");
1496 (void) strcpy(p, s);
1497 return (p);
1498}
1499
1500static void
1501help(ctab, s)
1502 struct tab *ctab;
1503 char *s;
1504{
1505 struct tab *c;
1506 int width, NCMDS;
1507 char *type;
1508
1509 if (ctab == sitetab)
1510 type = "SITE ";
1511 else
1512 type = "";
1513 width = 0, NCMDS = 0;
1514 for (c = ctab; c->name != NULL; c++) {
1515 int len = strlen(c->name);
1516
1517 if (len > width)
1518 width = len;
1519 NCMDS++;
1520 }
1521 width = (width + 8) &~ 7;
1522 if (s == 0) {
1523 int i, j, w;
1524 int columns, lines;
1525
1526 lreply(214, "The following %scommands are recognized %s.",
1527 type, "(* =>'s unimplemented)");
1528 columns = 76 / width;
1529 if (columns == 0)
1530 columns = 1;
1531 lines = (NCMDS + columns - 1) / columns;
1532 for (i = 0; i < lines; i++) {
1533 printf(" ");
1534 for (j = 0; j < columns; j++) {
1535 c = ctab + j * lines + i;
1536 printf("%s%c", c->name,
1537 c->implemented ? ' ' : '*');
1538 if (c + lines >= &ctab[NCMDS])
1539 break;
1540 w = strlen(c->name) + 1;
1541 while (w < width) {
1542 putchar(' ');
1543 w++;
1544 }
1545 }
1546 printf("\r\n");
1547 }
1548 (void) fflush(stdout);
1549 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1550 return;
1551 }
1552 upper(s);
1553 c = lookup(ctab, s);
1554 if (c == (struct tab *)0) {
1555 reply(502, "Unknown command %s.", s);
1556 return;
1557 }
1558 if (c->implemented)
1559 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1560 else
1561 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1562 c->name, c->help);
1563}
1564
1565static void
1566sizecmd(filename)
1567 char *filename;
1568{
1569 switch (type) {
1570 case TYPE_L:
1571 case TYPE_I: {
1572 struct stat stbuf;
1573 if (stat(filename, &stbuf) < 0)
1574 perror_reply(550, filename);
1575 else if (!S_ISREG(stbuf.st_mode))
1576 reply(550, "%s: not a plain file.", filename);
1577 else
1578 reply(213, "%qu", stbuf.st_size);
1579 break; }
1580 case TYPE_A: {
1581 FILE *fin;
1582 int c;
1583 off_t count;
1584 struct stat stbuf;
1585 fin = fopen(filename, "r");
1586 if (fin == NULL) {
1587 perror_reply(550, filename);
1588 return;
1589 }
1590 if (fstat(fileno(fin), &stbuf) < 0) {
1591 perror_reply(550, filename);
1592 (void) fclose(fin);
1593 return;
1594 } else if (!S_ISREG(stbuf.st_mode)) {
1595 reply(550, "%s: not a plain file.", filename);
1596 (void) fclose(fin);
1597 return;
1598 }
1599
1600 count = 0;
1601 while((c=getc(fin)) != EOF) {
1602 if (c == '\n') /* will get expanded to \r\n */
1603 count++;
1604 count++;
1605 }
1606 (void) fclose(fin);
1607
1608 reply(213, "%qd", count);
1609 break; }
1610 default:
1611 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1612 }
1613}
1614
1615/* Return 1, if port check is done. Return 0, if not yet. */
1616static int
1617port_check(pcmd)
1618 const char *pcmd;
1619{
1620 if (his_addr.su_family == AF_INET) {
1621 if (data_dest.su_family != AF_INET) {
1622 usedefault = 1;
1623 reply(500, "Invalid address rejected.");
1624 return 1;
1625 }
1626 if (paranoid &&
1627 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1628 memcmp(&data_dest.su_sin.sin_addr,
1629 &his_addr.su_sin.sin_addr,
1630 sizeof(data_dest.su_sin.sin_addr)))) {
1631 usedefault = 1;
1632 reply(500, "Illegal PORT range rejected.");
1633 } else {
1634 usedefault = 0;
1635 if (pdata >= 0) {
1636 (void) close(pdata);
1637 pdata = -1;
1638 }
1639 reply(200, "%s command successful.", pcmd);
1640 }
1641 return 1;
1642 }
1643 return 0;
1644}
1645
1646static int
1647check_login1()
1648{
1649 if (logged_in)
1650 return 1;
1651 else {
1652 reply(530, "Please login with USER and PASS.");
1653 return 0;
1654 }
1655}
1656
1657#ifdef INET6
1658/* Return 1, if port check is done. Return 0, if not yet. */
1659static int
1660port_check_v6(pcmd)
1661 const char *pcmd;
1662{
1663 if (his_addr.su_family == AF_INET6) {
1664 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1665 /* Convert data_dest into v4 mapped sockaddr.*/
1666 v4map_data_dest();
1667 if (data_dest.su_family != AF_INET6) {
1668 usedefault = 1;
1669 reply(500, "Invalid address rejected.");
1670 return 1;
1671 }
1672 if (paranoid &&
1673 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1674 memcmp(&data_dest.su_sin6.sin6_addr,
1675 &his_addr.su_sin6.sin6_addr,
1676 sizeof(data_dest.su_sin6.sin6_addr)))) {
1677 usedefault = 1;
1678 reply(500, "Illegal PORT range rejected.");
1679 } else {
1680 usedefault = 0;
1681 if (pdata >= 0) {
1682 (void) close(pdata);
1683 pdata = -1;
1684 }
1685 reply(200, "%s command successful.", pcmd);
1686 }
1687 return 1;
1688 }
1689 return 0;
1690}
1691
1692static void
1693v4map_data_dest()
1694{
1695 struct in_addr savedaddr;
1696 int savedport;
1697
1698 if (data_dest.su_family != AF_INET) {
1699 usedefault = 1;
1700 reply(500, "Invalid address rejected.");
1701 return;
1702 }
1703
1704 savedaddr = data_dest.su_sin.sin_addr;
1705 savedport = data_dest.su_port;
1706
1707 memset(&data_dest, 0, sizeof(data_dest));
1708 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1709 data_dest.su_sin6.sin6_family = AF_INET6;
1710 data_dest.su_sin6.sin6_port = savedport;
1711 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1712 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1713 (caddr_t)&savedaddr, sizeof(savedaddr));
1714}
1715#endif
1470 }
1471}
1472
1473void
1474upper(s)
1475 char *s;
1476{
1477 while (*s != '\0') {
1478 if (islower(*s))
1479 *s = toupper(*s);
1480 s++;
1481 }
1482}
1483
1484static char *
1485copy(s)
1486 char *s;
1487{
1488 char *p;
1489
1490 p = malloc((unsigned) strlen(s) + 1);
1491 if (p == NULL)
1492 fatalerror("Ran out of memory.");
1493 (void) strcpy(p, s);
1494 return (p);
1495}
1496
1497static void
1498help(ctab, s)
1499 struct tab *ctab;
1500 char *s;
1501{
1502 struct tab *c;
1503 int width, NCMDS;
1504 char *type;
1505
1506 if (ctab == sitetab)
1507 type = "SITE ";
1508 else
1509 type = "";
1510 width = 0, NCMDS = 0;
1511 for (c = ctab; c->name != NULL; c++) {
1512 int len = strlen(c->name);
1513
1514 if (len > width)
1515 width = len;
1516 NCMDS++;
1517 }
1518 width = (width + 8) &~ 7;
1519 if (s == 0) {
1520 int i, j, w;
1521 int columns, lines;
1522
1523 lreply(214, "The following %scommands are recognized %s.",
1524 type, "(* =>'s unimplemented)");
1525 columns = 76 / width;
1526 if (columns == 0)
1527 columns = 1;
1528 lines = (NCMDS + columns - 1) / columns;
1529 for (i = 0; i < lines; i++) {
1530 printf(" ");
1531 for (j = 0; j < columns; j++) {
1532 c = ctab + j * lines + i;
1533 printf("%s%c", c->name,
1534 c->implemented ? ' ' : '*');
1535 if (c + lines >= &ctab[NCMDS])
1536 break;
1537 w = strlen(c->name) + 1;
1538 while (w < width) {
1539 putchar(' ');
1540 w++;
1541 }
1542 }
1543 printf("\r\n");
1544 }
1545 (void) fflush(stdout);
1546 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1547 return;
1548 }
1549 upper(s);
1550 c = lookup(ctab, s);
1551 if (c == (struct tab *)0) {
1552 reply(502, "Unknown command %s.", s);
1553 return;
1554 }
1555 if (c->implemented)
1556 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1557 else
1558 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1559 c->name, c->help);
1560}
1561
1562static void
1563sizecmd(filename)
1564 char *filename;
1565{
1566 switch (type) {
1567 case TYPE_L:
1568 case TYPE_I: {
1569 struct stat stbuf;
1570 if (stat(filename, &stbuf) < 0)
1571 perror_reply(550, filename);
1572 else if (!S_ISREG(stbuf.st_mode))
1573 reply(550, "%s: not a plain file.", filename);
1574 else
1575 reply(213, "%qu", stbuf.st_size);
1576 break; }
1577 case TYPE_A: {
1578 FILE *fin;
1579 int c;
1580 off_t count;
1581 struct stat stbuf;
1582 fin = fopen(filename, "r");
1583 if (fin == NULL) {
1584 perror_reply(550, filename);
1585 return;
1586 }
1587 if (fstat(fileno(fin), &stbuf) < 0) {
1588 perror_reply(550, filename);
1589 (void) fclose(fin);
1590 return;
1591 } else if (!S_ISREG(stbuf.st_mode)) {
1592 reply(550, "%s: not a plain file.", filename);
1593 (void) fclose(fin);
1594 return;
1595 }
1596
1597 count = 0;
1598 while((c=getc(fin)) != EOF) {
1599 if (c == '\n') /* will get expanded to \r\n */
1600 count++;
1601 count++;
1602 }
1603 (void) fclose(fin);
1604
1605 reply(213, "%qd", count);
1606 break; }
1607 default:
1608 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1609 }
1610}
1611
1612/* Return 1, if port check is done. Return 0, if not yet. */
1613static int
1614port_check(pcmd)
1615 const char *pcmd;
1616{
1617 if (his_addr.su_family == AF_INET) {
1618 if (data_dest.su_family != AF_INET) {
1619 usedefault = 1;
1620 reply(500, "Invalid address rejected.");
1621 return 1;
1622 }
1623 if (paranoid &&
1624 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1625 memcmp(&data_dest.su_sin.sin_addr,
1626 &his_addr.su_sin.sin_addr,
1627 sizeof(data_dest.su_sin.sin_addr)))) {
1628 usedefault = 1;
1629 reply(500, "Illegal PORT range rejected.");
1630 } else {
1631 usedefault = 0;
1632 if (pdata >= 0) {
1633 (void) close(pdata);
1634 pdata = -1;
1635 }
1636 reply(200, "%s command successful.", pcmd);
1637 }
1638 return 1;
1639 }
1640 return 0;
1641}
1642
1643static int
1644check_login1()
1645{
1646 if (logged_in)
1647 return 1;
1648 else {
1649 reply(530, "Please login with USER and PASS.");
1650 return 0;
1651 }
1652}
1653
1654#ifdef INET6
1655/* Return 1, if port check is done. Return 0, if not yet. */
1656static int
1657port_check_v6(pcmd)
1658 const char *pcmd;
1659{
1660 if (his_addr.su_family == AF_INET6) {
1661 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1662 /* Convert data_dest into v4 mapped sockaddr.*/
1663 v4map_data_dest();
1664 if (data_dest.su_family != AF_INET6) {
1665 usedefault = 1;
1666 reply(500, "Invalid address rejected.");
1667 return 1;
1668 }
1669 if (paranoid &&
1670 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1671 memcmp(&data_dest.su_sin6.sin6_addr,
1672 &his_addr.su_sin6.sin6_addr,
1673 sizeof(data_dest.su_sin6.sin6_addr)))) {
1674 usedefault = 1;
1675 reply(500, "Illegal PORT range rejected.");
1676 } else {
1677 usedefault = 0;
1678 if (pdata >= 0) {
1679 (void) close(pdata);
1680 pdata = -1;
1681 }
1682 reply(200, "%s command successful.", pcmd);
1683 }
1684 return 1;
1685 }
1686 return 0;
1687}
1688
1689static void
1690v4map_data_dest()
1691{
1692 struct in_addr savedaddr;
1693 int savedport;
1694
1695 if (data_dest.su_family != AF_INET) {
1696 usedefault = 1;
1697 reply(500, "Invalid address rejected.");
1698 return;
1699 }
1700
1701 savedaddr = data_dest.su_sin.sin_addr;
1702 savedport = data_dest.su_port;
1703
1704 memset(&data_dest, 0, sizeof(data_dest));
1705 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1706 data_dest.su_sin6.sin6_family = AF_INET6;
1707 data_dest.su_sin6.sin6_port = savedport;
1708 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1709 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1710 (caddr_t)&savedaddr, sizeof(savedaddr));
1711}
1712#endif