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