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