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