ftpcmd.y revision 1.58 1 /* $NetBSD: ftpcmd.y,v 1.58 2000/11/30 02:59:11 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.58 2000/11/30 02:59:11 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(s, "PASS ", 5) == 0) ||
1388 strncasecmp(s, "ACCT ", 5) == 0) {
1389 /* Don't syslog passwords */
1390 syslog(LOG_DEBUG, "command: %.4s ???", s);
1391 } else {
1392 char *cp;
1393 int len;
1394
1395 /* Don't syslog trailing CR-LF */
1396 len = strlen(s);
1397 cp = s + len - 1;
1398 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1399 --cp;
1400 --len;
1401 }
1402 syslog(LOG_DEBUG, "command: %.*s", len, s);
1403 }
1404 }
1405 return (s);
1406 }
1407
1408 static void
1409 toolong(int signo)
1410 {
1411
1412 reply(421,
1413 "Timeout (%d seconds): closing control connection.",
1414 curclass.timeout);
1415 if (logging)
1416 syslog(LOG_INFO, "User %s timed out after %d seconds",
1417 (pw ? pw->pw_name : "unknown"), curclass.timeout);
1418 dologout(1);
1419 }
1420
1421 static int
1422 yylex(void)
1423 {
1424 static int cpos, state;
1425 char *cp, *cp2;
1426 struct tab *p;
1427 int n;
1428 char c;
1429
1430 switch (state) {
1431
1432 case CMD:
1433 hasyyerrored = 0;
1434 (void) signal(SIGALRM, toolong);
1435 (void) alarm(curclass.timeout);
1436 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1437 reply(221, "You could at least say goodbye.");
1438 dologout(0);
1439 }
1440 (void) alarm(0);
1441 if ((cp = strchr(cbuf, '\r'))) {
1442 *cp = '\0';
1443 #if HAVE_SETPROCTITLE
1444 if (strncasecmp(cbuf, "PASS", 4) != 0 &&
1445 strncasecmp(cbuf, "ACCT", 4) != 0)
1446 setproctitle("%s: %s", proctitle, cbuf);
1447 #endif /* HAVE_SETPROCTITLE */
1448 *cp++ = '\n';
1449 *cp = '\0';
1450 }
1451 if ((cp = strpbrk(cbuf, " \n")))
1452 cpos = cp - cbuf;
1453 if (cpos == 0)
1454 cpos = 4;
1455 c = cbuf[cpos];
1456 cbuf[cpos] = '\0';
1457 p = lookup(cmdtab, cbuf);
1458 cbuf[cpos] = c;
1459 if (p != NULL) {
1460 if (! CMD_IMPLEMENTED(p)) {
1461 reply(502, "%s command not implemented.",
1462 p->name);
1463 hasyyerrored = 1;
1464 break;
1465 }
1466 state = p->state;
1467 yylval.s = p->name;
1468 return (p->token);
1469 }
1470 break;
1471
1472 case SITECMD:
1473 if (cbuf[cpos] == ' ') {
1474 cpos++;
1475 return (SP);
1476 }
1477 cp = &cbuf[cpos];
1478 if ((cp2 = strpbrk(cp, " \n")))
1479 cpos = cp2 - cbuf;
1480 c = cbuf[cpos];
1481 cbuf[cpos] = '\0';
1482 p = lookup(sitetab, cp);
1483 cbuf[cpos] = c;
1484 if (p != NULL) {
1485 if (!CMD_IMPLEMENTED(p)) {
1486 reply(502, "SITE %s command not implemented.",
1487 p->name);
1488 hasyyerrored = 1;
1489 break;
1490 }
1491 state = p->state;
1492 yylval.s = p->name;
1493 return (p->token);
1494 }
1495 break;
1496
1497 case OSTR:
1498 if (cbuf[cpos] == '\n') {
1499 state = CMD;
1500 return (CRLF);
1501 }
1502 /* FALLTHROUGH */
1503
1504 case STR1:
1505 case ZSTR1:
1506 dostr1:
1507 if (cbuf[cpos] == ' ') {
1508 cpos++;
1509 state = state == OSTR ? STR2 : state+1;
1510 return (SP);
1511 }
1512 break;
1513
1514 case ZSTR2:
1515 if (cbuf[cpos] == '\n') {
1516 state = CMD;
1517 return (CRLF);
1518 }
1519 /* FALLTHROUGH */
1520
1521 case STR2:
1522 cp = &cbuf[cpos];
1523 n = strlen(cp);
1524 cpos += n - 1;
1525 /*
1526 * Make sure the string is nonempty and \n terminated.
1527 */
1528 if (n > 1 && cbuf[cpos] == '\n') {
1529 cbuf[cpos] = '\0';
1530 yylval.s = xstrdup(cp);
1531 cbuf[cpos] = '\n';
1532 state = ARGS;
1533 return (STRING);
1534 }
1535 break;
1536
1537 case NSTR:
1538 if (cbuf[cpos] == ' ') {
1539 cpos++;
1540 return (SP);
1541 }
1542 if (isdigit(cbuf[cpos])) {
1543 cp = &cbuf[cpos];
1544 while (isdigit(cbuf[++cpos]))
1545 ;
1546 c = cbuf[cpos];
1547 cbuf[cpos] = '\0';
1548 yylval.i = atoi(cp);
1549 cbuf[cpos] = c;
1550 state = STR1;
1551 return (NUMBER);
1552 }
1553 state = STR1;
1554 goto dostr1;
1555
1556 case ARGS:
1557 if (isdigit(cbuf[cpos])) {
1558 cp = &cbuf[cpos];
1559 while (isdigit(cbuf[++cpos]))
1560 ;
1561 c = cbuf[cpos];
1562 cbuf[cpos] = '\0';
1563 yylval.i = atoi(cp);
1564 cbuf[cpos] = c;
1565 return (NUMBER);
1566 }
1567 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1568 && !isalnum(cbuf[cpos + 3])) {
1569 yylval.s = xstrdup("ALL");
1570 cpos += 3;
1571 return ALL;
1572 }
1573 switch (cbuf[cpos++]) {
1574
1575 case '\n':
1576 state = CMD;
1577 return (CRLF);
1578
1579 case ' ':
1580 return (SP);
1581
1582 case ',':
1583 return (COMMA);
1584
1585 case 'A':
1586 case 'a':
1587 return (A);
1588
1589 case 'B':
1590 case 'b':
1591 return (B);
1592
1593 case 'C':
1594 case 'c':
1595 return (C);
1596
1597 case 'E':
1598 case 'e':
1599 return (E);
1600
1601 case 'F':
1602 case 'f':
1603 return (F);
1604
1605 case 'I':
1606 case 'i':
1607 return (I);
1608
1609 case 'L':
1610 case 'l':
1611 return (L);
1612
1613 case 'N':
1614 case 'n':
1615 return (N);
1616
1617 case 'P':
1618 case 'p':
1619 return (P);
1620
1621 case 'R':
1622 case 'r':
1623 return (R);
1624
1625 case 'S':
1626 case 's':
1627 return (S);
1628
1629 case 'T':
1630 case 't':
1631 return (T);
1632
1633 }
1634 break;
1635
1636 case NOARGS:
1637 if (cbuf[cpos] == '\n') {
1638 state = CMD;
1639 return (CRLF);
1640 }
1641 c = cbuf[cpos];
1642 cbuf[cpos] = '\0';
1643 reply(501, "'%s' command does not take any arguments.", cbuf);
1644 hasyyerrored = 1;
1645 cbuf[cpos] = c;
1646 break;
1647
1648 default:
1649 fatal("Unknown state in scanner.");
1650 }
1651 yyerror(NULL);
1652 state = CMD;
1653 longjmp(errcatch, 0);
1654 /* NOTREACHED */
1655 }
1656
1657 /* ARGSUSED */
1658 void
1659 yyerror(char *s)
1660 {
1661 char *cp;
1662
1663 if (hasyyerrored)
1664 return;
1665 if ((cp = strchr(cbuf,'\n')) != NULL)
1666 *cp = '\0';
1667 reply(500, "'%s': command not understood.", cbuf);
1668 hasyyerrored = 1;
1669 }
1670
1671 static void
1672 help(struct tab *ctab, const char *s)
1673 {
1674 struct tab *c;
1675 int width, NCMDS;
1676 char *type;
1677
1678 if (ctab == sitetab)
1679 type = "SITE ";
1680 else
1681 type = "";
1682 width = 0, NCMDS = 0;
1683 for (c = ctab; c->name != NULL; c++) {
1684 int len = strlen(c->name);
1685
1686 if (len > width)
1687 width = len;
1688 NCMDS++;
1689 }
1690 width = (width + 8) &~ 7;
1691 if (s == 0) {
1692 int i, j, w;
1693 int columns, lines;
1694
1695 reply(-214, "%s", "");
1696 reply(0, "The following %scommands are recognized.", type);
1697 reply(0, "(`-' = not implemented, `+' = supports options)");
1698 columns = 76 / width;
1699 if (columns == 0)
1700 columns = 1;
1701 lines = (NCMDS + columns - 1) / columns;
1702 for (i = 0; i < lines; i++) {
1703 cprintf(stdout, " ");
1704 for (j = 0; j < columns; j++) {
1705 c = ctab + j * lines + i;
1706 cprintf(stdout, "%s", c->name);
1707 w = strlen(c->name);
1708 if (! CMD_IMPLEMENTED(c)) {
1709 CPUTC('-', stdout);
1710 w++;
1711 }
1712 if (CMD_HAS_OPTIONS(c)) {
1713 CPUTC('+', stdout);
1714 w++;
1715 }
1716 if (c + lines >= &ctab[NCMDS])
1717 break;
1718 while (w < width) {
1719 CPUTC(' ', stdout);
1720 w++;
1721 }
1722 }
1723 cprintf(stdout, "\r\n");
1724 }
1725 (void) fflush(stdout);
1726 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1727 return;
1728 }
1729 c = lookup(ctab, s);
1730 if (c == (struct tab *)0) {
1731 reply(502, "Unknown command %s.", s);
1732 return;
1733 }
1734 if (CMD_IMPLEMENTED(c))
1735 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1736 else
1737 reply(214, "%s%-*s\t%s; not implemented.", type, width,
1738 c->name, c->help);
1739 }
1740
1741 /*
1742 * Check that the structures used for a PORT, LPRT or EPRT command are
1743 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
1744 * If family != -1 check that his_addr.su_family == family.
1745 */
1746 static void
1747 port_check(const char *cmd, int family)
1748 {
1749
1750 if (epsvall) {
1751 reply(501, "%s disallowed after EPSV ALL", cmd);
1752 return;
1753 }
1754
1755 if (family != -1 && his_addr.su_family != family) {
1756 port_check_fail:
1757 reply(500, "Illegal %s command rejected", cmd);
1758 return;
1759 }
1760
1761 if (data_dest.su_family != his_addr.su_family)
1762 goto port_check_fail;
1763
1764 /* be paranoid, if told so */
1765 if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
1766 if ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1767 (data_dest.su_len != his_addr.su_len))
1768 goto port_check_fail;
1769 switch (data_dest.su_family) {
1770 case AF_INET:
1771 if (memcmp(&data_dest.su_addr, &his_addr.su_addr,
1772 data_dest.su_len) != 0)
1773 goto port_check_fail;
1774 break;
1775 #ifdef INET6
1776 case AF_INET6:
1777 if (memcmp(&data_dest.su_6addr, &his_addr.su_6addr,
1778 sizeof(data_dest.su_6addr)) != 0)
1779 goto port_check_fail;
1780 if (data_dest.su_scope_id != his_addr.su_scope_id)
1781 goto port_check_fail;
1782 break;
1783 #endif
1784 default:
1785 goto port_check_fail;
1786 }
1787 }
1788
1789 usedefault = 0;
1790 if (pdata >= 0) {
1791 (void) close(pdata);
1792 pdata = -1;
1793 }
1794 reply(200, "%s command successful.", cmd);
1795 }
1796