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