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