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