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