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