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