ftpcmd.y revision 1.62 1 /* $NetBSD: ftpcmd.y,v 1.62 2001/04/12 02:28:59 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.62 2001/04/12 02:28:59 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|GLOB_TILDE;
1087
1088 if ($1[1] == '\0')
1089 $$ = xstrdup(homedir);
1090 else {
1091 memset(&gl, 0, sizeof(gl));
1092 if (glob($1, flags, NULL, &gl) ||
1093 gl.gl_pathc == 0) {
1094 reply(550, "not found");
1095 $$ = NULL;
1096 } else
1097 $$ = xstrdup(gl.gl_pathv[0]);
1098 globfree(&gl);
1099 }
1100 free($1);
1101 } else
1102 $$ = $1;
1103 }
1104 ;
1105
1106 pathstring
1107 : STRING
1108 ;
1109
1110 octal_number
1111 : NUMBER
1112 {
1113 int ret, dec, multby, digit;
1114
1115 /*
1116 * Convert a number that was read as decimal number
1117 * to what it would be if it had been read as octal.
1118 */
1119 dec = $1;
1120 multby = 1;
1121 ret = 0;
1122 while (dec) {
1123 digit = dec%10;
1124 if (digit > 7) {
1125 ret = -1;
1126 break;
1127 }
1128 ret += digit * multby;
1129 multby *= 8;
1130 dec /= 10;
1131 }
1132 $$ = ret;
1133 }
1134 ;
1135
1136 mechanism_name
1137 : STRING
1138 ;
1139
1140 base64data
1141 : STRING
1142 ;
1143
1144 prot_code
1145 : STRING
1146 ;
1147
1148 decimal_integer
1149 : NUMBER
1150 ;
1151
1152 check_login
1153 : /* empty */
1154 {
1155 if (logged_in)
1156 $$ = 1;
1157 else {
1158 reply(530, "Please login with USER and PASS.");
1159 $$ = 0;
1160 hasyyerrored = 1;
1161 }
1162 }
1163 ;
1164
1165 %%
1166
1167 #define CMD 0 /* beginning of command */
1168 #define ARGS 1 /* expect miscellaneous arguments */
1169 #define STR1 2 /* expect SP followed by STRING */
1170 #define STR2 3 /* expect STRING */
1171 #define OSTR 4 /* optional SP then STRING */
1172 #define ZSTR1 5 /* SP then optional STRING */
1173 #define ZSTR2 6 /* optional STRING after SP */
1174 #define SITECMD 7 /* SITE command */
1175 #define NSTR 8 /* Number followed by a string */
1176 #define NOARGS 9 /* No arguments allowed */
1177 #define EOLN 10 /* End of line */
1178
1179 struct tab cmdtab[] = {
1180 /* From RFC 959, in order defined (5.3.1) */
1181 { "USER", USER, STR1, 1, "<sp> username" },
1182 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
1183 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1184 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1185 { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1186 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1187 { "QUIT", QUIT, NOARGS, 1, "(terminate service)" },
1188 { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" },
1189 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
1190 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1191 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1192 { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" },
1193 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1194 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1195 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1196 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1197 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1198 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1199 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1200 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1201 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1202 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1203 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1204 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1205 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1206 { "ABOR", ABOR, NOARGS, 4, "(abort operation)" },
1207 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1208 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1209 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1210 { "PWD", PWD, NOARGS, 1, "(return current directory)" },
1211 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1212 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1213 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1214 { "SYST", SYST, NOARGS, 1, "(get type of operating system)" },
1215 { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" },
1216 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1217 { "NOOP", NOOP, NOARGS, 2, "" },
1218
1219 /* From RFC 2228, in order defined */
1220 { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" },
1221 { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" },
1222 { "PROT", PROT, STR1, 1, "<sp> prot-code" },
1223 { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" },
1224 { "CCC", CCC, NOARGS, 1, "(Disable data protection)" },
1225 { "MIC", MIC, STR1, 4, "<sp> base64data" },
1226 { "CONF", CONF, STR1, 4, "<sp> base64data" },
1227 { "ENC", ENC, STR1, 4, "<sp> base64data" },
1228
1229 /* From RFC 2389, in order defined */
1230 { "FEAT", FEAT, NOARGS, 1, "(display extended features)" },
1231 { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" },
1232
1233 /* from draft-ietf-ftpext-mlst-11 */
1234 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1235 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1236 { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" },
1237 { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" },
1238
1239 /* obsolete commands */
1240 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1241 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1242 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1243 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1244 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1245 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1246 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1247 { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1248 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1249 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1250 { "XPWD", PWD, NOARGS, 1, "(return current directory)" },
1251 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1252
1253 { NULL, 0, 0, 0, 0 }
1254 };
1255
1256 struct tab sitetab[] = {
1257 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1258 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1259 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1260 { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" },
1261 { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" },
1262 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1263 { NULL, 0, 0, 0, NULL }
1264 };
1265
1266 static int check_write(const char *, int);
1267 static void help(struct tab *, const char *);
1268 static void port_check(const char *, int);
1269 static void toolong(int);
1270 static int yylex(void);
1271
1272 extern int epsvall;
1273
1274 /*
1275 * Check if a filename is allowed to be modified (isupload == 0) or
1276 * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
1277 */
1278 static int
1279 check_write(const char *file, int isupload)
1280 {
1281 if (file == NULL)
1282 return (0);
1283 if (! logged_in) {
1284 reply(530, "Please login with USER and PASS.");
1285 return (0);
1286 }
1287 /* checking modify */
1288 if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
1289 reply(502, "No permission to use this command.");
1290 return (0);
1291 }
1292 /* checking upload */
1293 if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
1294 reply(502, "No permission to use this command.");
1295 return (0);
1296 }
1297 /* checking sanenames */
1298 if (CURCLASS_FLAGS_ISSET(sanenames)) {
1299 const char *p;
1300
1301 if (file[0] == '.')
1302 goto insane_name;
1303 for (p = file; *p; p++) {
1304 if (isalnum(*p) || *p == '-' || *p == '+' ||
1305 *p == ',' || *p == '.' || *p == '_')
1306 continue;
1307 insane_name:
1308 reply(553, "File name `%s' not allowed.", file);
1309 return (0);
1310 }
1311 }
1312 return (1);
1313 }
1314
1315 struct tab *
1316 lookup(struct tab *p, const char *cmd)
1317 {
1318
1319 for (; p->name != NULL; p++)
1320 if (strcasecmp(cmd, p->name) == 0)
1321 return (p);
1322 return (0);
1323 }
1324
1325 #include <arpa/telnet.h>
1326
1327 /*
1328 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1329 */
1330 char *
1331 getline(char *s, int n, FILE *iop)
1332 {
1333 int c;
1334 char *cs;
1335
1336 cs = s;
1337 /* tmpline may contain saved command from urgent mode interruption */
1338 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1339 *cs++ = tmpline[c];
1340 if (tmpline[c] == '\n') {
1341 *cs++ = '\0';
1342 if (debug)
1343 syslog(LOG_DEBUG, "command: %s", s);
1344 tmpline[0] = '\0';
1345 return(s);
1346 }
1347 if (c == 0)
1348 tmpline[0] = '\0';
1349 }
1350 while ((c = getc(iop)) != EOF) {
1351 total_bytes++;
1352 total_bytes_in++;
1353 c &= 0377;
1354 if (c == IAC) {
1355 if ((c = getc(iop)) != EOF) {
1356 total_bytes++;
1357 total_bytes_in++;
1358 c &= 0377;
1359 switch (c) {
1360 case WILL:
1361 case WONT:
1362 c = getc(iop);
1363 total_bytes++;
1364 total_bytes_in++;
1365 cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
1366 (void) fflush(stdout);
1367 continue;
1368 case DO:
1369 case DONT:
1370 c = getc(iop);
1371 total_bytes++;
1372 total_bytes_in++;
1373 cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
1374 (void) fflush(stdout);
1375 continue;
1376 case IAC:
1377 break;
1378 default:
1379 continue; /* ignore command */
1380 }
1381 }
1382 }
1383 *cs++ = c;
1384 if (--n <= 0 || c == '\n')
1385 break;
1386 }
1387 if (c == EOF && cs == s)
1388 return (NULL);
1389 *cs++ = '\0';
1390 if (debug) {
1391 if ((curclass.type != CLASS_GUEST &&
1392 strncasecmp(s, "PASS ", 5) == 0) ||
1393 strncasecmp(s, "ACCT ", 5) == 0) {
1394 /* Don't syslog passwords */
1395 syslog(LOG_DEBUG, "command: %.4s ???", s);
1396 } else {
1397 char *cp;
1398 int len;
1399
1400 /* Don't syslog trailing CR-LF */
1401 len = strlen(s);
1402 cp = s + len - 1;
1403 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1404 --cp;
1405 --len;
1406 }
1407 syslog(LOG_DEBUG, "command: %.*s", len, s);
1408 }
1409 }
1410 return (s);
1411 }
1412
1413 static void
1414 toolong(int signo)
1415 {
1416
1417 reply(421,
1418 "Timeout (%d seconds): closing control connection.",
1419 curclass.timeout);
1420 if (logging)
1421 syslog(LOG_INFO, "User %s timed out after %d seconds",
1422 (pw ? pw->pw_name : "unknown"), curclass.timeout);
1423 dologout(1);
1424 }
1425
1426 void
1427 ftp_handle_line(char *cp)
1428 {
1429
1430 cmdp = cp;
1431 yyparse();
1432 }
1433
1434 void
1435 ftp_loop(void)
1436 {
1437
1438 while (1) {
1439 (void) signal(SIGALRM, toolong);
1440 (void) alarm(curclass.timeout);
1441 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1442 reply(221, "You could at least say goodbye.");
1443 dologout(0);
1444 }
1445 (void) alarm(0);
1446 ftp_handle_line(cbuf);
1447 }
1448 /*NOTREACHED*/
1449 }
1450
1451 static int
1452 yylex(void)
1453 {
1454 static int cpos, state;
1455 char *cp, *cp2;
1456 struct tab *p;
1457 int n;
1458 char c;
1459
1460 switch (state) {
1461
1462 case CMD:
1463 hasyyerrored = 0;
1464 if ((cp = strchr(cmdp, '\r'))) {
1465 *cp = '\0';
1466 #if HAVE_SETPROCTITLE
1467 if (strncasecmp(cmdp, "PASS", 4) != 0 &&
1468 strncasecmp(cmdp, "ACCT", 4) != 0)
1469 setproctitle("%s: %s", proctitle, cmdp);
1470 #endif /* HAVE_SETPROCTITLE */
1471 *cp++ = '\n';
1472 *cp = '\0';
1473 }
1474 if ((cp = strpbrk(cmdp, " \n")))
1475 cpos = cp - cmdp;
1476 if (cpos == 0)
1477 cpos = 4;
1478 c = cmdp[cpos];
1479 cmdp[cpos] = '\0';
1480 p = lookup(cmdtab, cmdp);
1481 cmdp[cpos] = c;
1482 if (p != NULL) {
1483 if (is_oob && ! CMD_OOB(p)) {
1484 /* command will be handled in-band */
1485 return (0);
1486 } else if (! CMD_IMPLEMENTED(p)) {
1487 reply(502, "%s command not implemented.",
1488 p->name);
1489 hasyyerrored = 1;
1490 break;
1491 }
1492 state = p->state;
1493 yylval.s = p->name;
1494 return (p->token);
1495 }
1496 break;
1497
1498 case SITECMD:
1499 if (cmdp[cpos] == ' ') {
1500 cpos++;
1501 return (SP);
1502 }
1503 cp = &cmdp[cpos];
1504 if ((cp2 = strpbrk(cp, " \n")))
1505 cpos = cp2 - cmdp;
1506 c = cmdp[cpos];
1507 cmdp[cpos] = '\0';
1508 p = lookup(sitetab, cp);
1509 cmdp[cpos] = c;
1510 if (p != NULL) {
1511 if (!CMD_IMPLEMENTED(p)) {
1512 reply(502, "SITE %s command not implemented.",
1513 p->name);
1514 hasyyerrored = 1;
1515 break;
1516 }
1517 state = p->state;
1518 yylval.s = p->name;
1519 return (p->token);
1520 }
1521 break;
1522
1523 case OSTR:
1524 if (cmdp[cpos] == '\n') {
1525 state = EOLN;
1526 return (CRLF);
1527 }
1528 /* FALLTHROUGH */
1529
1530 case STR1:
1531 case ZSTR1:
1532 dostr1:
1533 if (cmdp[cpos] == ' ') {
1534 cpos++;
1535 state = state == OSTR ? STR2 : state+1;
1536 return (SP);
1537 }
1538 break;
1539
1540 case ZSTR2:
1541 if (cmdp[cpos] == '\n') {
1542 state = EOLN;
1543 return (CRLF);
1544 }
1545 /* FALLTHROUGH */
1546
1547 case STR2:
1548 cp = &cmdp[cpos];
1549 n = strlen(cp);
1550 cpos += n - 1;
1551 /*
1552 * Make sure the string is nonempty and \n terminated.
1553 */
1554 if (n > 1 && cmdp[cpos] == '\n') {
1555 cmdp[cpos] = '\0';
1556 yylval.s = xstrdup(cp);
1557 cmdp[cpos] = '\n';
1558 state = ARGS;
1559 return (STRING);
1560 }
1561 break;
1562
1563 case NSTR:
1564 if (cmdp[cpos] == ' ') {
1565 cpos++;
1566 return (SP);
1567 }
1568 if (isdigit(cmdp[cpos])) {
1569 cp = &cmdp[cpos];
1570 while (isdigit(cmdp[++cpos]))
1571 ;
1572 c = cmdp[cpos];
1573 cmdp[cpos] = '\0';
1574 yylval.i = atoi(cp);
1575 cmdp[cpos] = c;
1576 state = STR1;
1577 return (NUMBER);
1578 }
1579 state = STR1;
1580 goto dostr1;
1581
1582 case ARGS:
1583 if (isdigit(cmdp[cpos])) {
1584 cp = &cmdp[cpos];
1585 while (isdigit(cmdp[++cpos]))
1586 ;
1587 c = cmdp[cpos];
1588 cmdp[cpos] = '\0';
1589 yylval.i = atoi(cp);
1590 cmdp[cpos] = c;
1591 return (NUMBER);
1592 }
1593 if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0
1594 && !isalnum(cmdp[cpos + 3])) {
1595 yylval.s = xstrdup("ALL");
1596 cpos += 3;
1597 return ALL;
1598 }
1599 switch (cmdp[cpos++]) {
1600
1601 case '\n':
1602 state = EOLN;
1603 return (CRLF);
1604
1605 case ' ':
1606 return (SP);
1607
1608 case ',':
1609 return (COMMA);
1610
1611 case 'A':
1612 case 'a':
1613 return (A);
1614
1615 case 'B':
1616 case 'b':
1617 return (B);
1618
1619 case 'C':
1620 case 'c':
1621 return (C);
1622
1623 case 'E':
1624 case 'e':
1625 return (E);
1626
1627 case 'F':
1628 case 'f':
1629 return (F);
1630
1631 case 'I':
1632 case 'i':
1633 return (I);
1634
1635 case 'L':
1636 case 'l':
1637 return (L);
1638
1639 case 'N':
1640 case 'n':
1641 return (N);
1642
1643 case 'P':
1644 case 'p':
1645 return (P);
1646
1647 case 'R':
1648 case 'r':
1649 return (R);
1650
1651 case 'S':
1652 case 's':
1653 return (S);
1654
1655 case 'T':
1656 case 't':
1657 return (T);
1658
1659 }
1660 break;
1661
1662 case NOARGS:
1663 if (cmdp[cpos] == '\n') {
1664 state = EOLN;
1665 return (CRLF);
1666 }
1667 c = cmdp[cpos];
1668 cmdp[cpos] = '\0';
1669 reply(501, "'%s' command does not take any arguments.", cmdp);
1670 hasyyerrored = 1;
1671 cmdp[cpos] = c;
1672 break;
1673
1674 case EOLN:
1675 state = CMD;
1676 return (0);
1677
1678 default:
1679 fatal("Unknown state in scanner.");
1680 }
1681 yyerror(NULL);
1682 state = CMD;
1683 is_oob = 0;
1684 longjmp(errcatch, 0);
1685 /* NOTREACHED */
1686 }
1687
1688 /* ARGSUSED */
1689 void
1690 yyerror(char *s)
1691 {
1692 char *cp;
1693
1694 if (hasyyerrored || is_oob)
1695 return;
1696 if ((cp = strchr(cmdp,'\n')) != NULL)
1697 *cp = '\0';
1698 reply(500, "'%s': command not understood.", cmdp);
1699 hasyyerrored = 1;
1700 }
1701
1702 static void
1703 help(struct tab *ctab, const char *s)
1704 {
1705 struct tab *c;
1706 int width, NCMDS;
1707 char *type;
1708
1709 if (ctab == sitetab)
1710 type = "SITE ";
1711 else
1712 type = "";
1713 width = 0, NCMDS = 0;
1714 for (c = ctab; c->name != NULL; c++) {
1715 int len = strlen(c->name);
1716
1717 if (len > width)
1718 width = len;
1719 NCMDS++;
1720 }
1721 width = (width + 8) &~ 7;
1722 if (s == 0) {
1723 int i, j, w;
1724 int columns, lines;
1725
1726 reply(-214, "%s", "");
1727 reply(0, "The following %scommands are recognized.", type);
1728 reply(0, "(`-' = not implemented, `+' = supports options)");
1729 columns = 76 / width;
1730 if (columns == 0)
1731 columns = 1;
1732 lines = (NCMDS + columns - 1) / columns;
1733 for (i = 0; i < lines; i++) {
1734 cprintf(stdout, " ");
1735 for (j = 0; j < columns; j++) {
1736 c = ctab + j * lines + i;
1737 cprintf(stdout, "%s", c->name);
1738 w = strlen(c->name);
1739 if (! CMD_IMPLEMENTED(c)) {
1740 CPUTC('-', stdout);
1741 w++;
1742 }
1743 if (CMD_HAS_OPTIONS(c)) {
1744 CPUTC('+', stdout);
1745 w++;
1746 }
1747 if (c + lines >= &ctab[NCMDS])
1748 break;
1749 while (w < width) {
1750 CPUTC(' ', stdout);
1751 w++;
1752 }
1753 }
1754 cprintf(stdout, "\r\n");
1755 }
1756 (void) fflush(stdout);
1757 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1758 return;
1759 }
1760 c = lookup(ctab, s);
1761 if (c == (struct tab *)0) {
1762 reply(502, "Unknown command %s.", s);
1763 return;
1764 }
1765 if (CMD_IMPLEMENTED(c))
1766 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1767 else
1768 reply(214, "%s%-*s\t%s; not implemented.", type, width,
1769 c->name, c->help);
1770 }
1771
1772 /*
1773 * Check that the structures used for a PORT, LPRT or EPRT command are
1774 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
1775 * If family != -1 check that his_addr.su_family == family.
1776 */
1777 static void
1778 port_check(const char *cmd, int family)
1779 {
1780 char h1[NI_MAXHOST], h2[NI_MAXHOST];
1781 char s1[NI_MAXHOST], s2[NI_MAXHOST];
1782 #ifdef NI_WITHSCOPEID
1783 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1784 #else
1785 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1786 #endif
1787
1788 if (epsvall) {
1789 reply(501, "%s disallowed after EPSV ALL", cmd);
1790 return;
1791 }
1792
1793 if (family != -1 && his_addr.su_family != family) {
1794 port_check_fail:
1795 reply(500, "Illegal %s command rejected", cmd);
1796 return;
1797 }
1798
1799 if (data_dest.su_family != his_addr.su_family)
1800 goto port_check_fail;
1801
1802 /* be paranoid, if told so */
1803 if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
1804 #ifdef INET6
1805 /*
1806 * be paranoid, there are getnameinfo implementation that does
1807 * not present scopeid portion
1808 */
1809 if (data_dest.su_family == AF_INET6 &&
1810 data_dest.su_scope_id != his_addr.su_scope_id)
1811 goto port_check_fail;
1812 #endif
1813
1814 if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len,
1815 h1, sizeof(h1), s1, sizeof(s1), niflags))
1816 goto port_check_fail;
1817 if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1818 h2, sizeof(h2), s2, sizeof(s2), niflags))
1819 goto port_check_fail;
1820
1821 if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0)
1822 goto port_check_fail;
1823 }
1824
1825 usedefault = 0;
1826 if (pdata >= 0) {
1827 (void) close(pdata);
1828 pdata = -1;
1829 }
1830 reply(200, "%s command successful.", cmd);
1831 }
1832