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