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