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