ftpcmd.y revision 1.83.2.1 1 /* $NetBSD: ftpcmd.y,v 1.83.2.1 2008/09/18 19:28:41 bouyer Exp $ */
2
3 /*-
4 * Copyright (c) 1997-2008 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.83.2.1 2008/09/18 19:28:41 bouyer 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 extern int epsvall;
122 struct tab sitetab[];
123
124 static int check_write(const char *, int);
125 static void help(struct tab *, const char *);
126 static void port_check(const char *, int);
127 int yylex(void);
128
129 %}
130
131 %union {
132 struct {
133 LLT ll;
134 int i;
135 } u;
136 char *s;
137 }
138
139 %token
140 A B C E F I
141 L N P R S T
142
143 SP CRLF COMMA ALL
144
145 USER PASS ACCT CWD CDUP SMNT
146 QUIT REIN PORT PASV TYPE STRU
147 MODE RETR STOR STOU APPE ALLO
148 REST RNFR RNTO ABOR DELE RMD
149 MKD PWD LIST NLST SITE SYST
150 STAT HELP NOOP
151
152 AUTH ADAT PROT PBSZ CCC MIC
153 CONF ENC
154
155 FEAT OPTS
156
157 SIZE MDTM MLST MLSD
158
159 LPRT LPSV EPRT EPSV
160
161 MAIL MLFL MRCP MRSQ MSAM MSND
162 MSOM
163
164 CHMOD IDLE RATEGET RATEPUT UMASK
165
166 LEXERR
167
168 %token <s> STRING
169 %token <u> NUMBER
170
171 %type <u.i> check_login octal_number byte_size
172 %type <u.i> struct_code mode_code type_code form_code decimal_integer
173 %type <s> pathstring pathname password username
174 %type <s> mechanism_name base64data prot_code
175
176 %start cmd_sel
177
178 %%
179
180 cmd_sel
181 : cmd
182 {
183 REASSIGN(fromname, NULL);
184 restart_point = (off_t) 0;
185 }
186
187 | rcmd
188
189 ;
190
191 cmd
192 /* RFC 959 */
193 : USER SP username CRLF
194 {
195 user($3);
196 free($3);
197 }
198
199 | PASS SP password CRLF
200 {
201 pass($3);
202 memset($3, 0, strlen($3));
203 free($3);
204 }
205
206 | CWD check_login CRLF
207 {
208 if ($2)
209 cwd(homedir);
210 }
211
212 | CWD check_login SP pathname CRLF
213 {
214 if ($2 && $4 != NULL)
215 cwd($4);
216 if ($4 != NULL)
217 free($4);
218 }
219
220 | CDUP check_login CRLF
221 {
222 if ($2)
223 cwd("..");
224 }
225
226 | QUIT CRLF
227 {
228 if (logged_in) {
229 reply(-221, "%s", "");
230 reply(0,
231 "Data traffic for this session was " LLF " byte%s in " LLF " file%s.",
232 (LLT)total_data, PLURAL(total_data),
233 (LLT)total_files, PLURAL(total_files));
234 reply(0,
235 "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.",
236 (LLT)total_bytes, PLURAL(total_bytes),
237 (LLT)total_xfers, PLURAL(total_xfers));
238 }
239 reply(221,
240 "Thank you for using the FTP service on %s.",
241 hostname);
242 if (logged_in && logging) {
243 syslog(LOG_INFO,
244 "Data traffic: " LLF " byte%s in " LLF " file%s",
245 (LLT)total_data, PLURAL(total_data),
246 (LLT)total_files, PLURAL(total_files));
247 syslog(LOG_INFO,
248 "Total traffic: " LLF " byte%s in " LLF " transfer%s",
249 (LLT)total_bytes, PLURAL(total_bytes),
250 (LLT)total_xfers, PLURAL(total_xfers));
251 }
252
253 dologout(0);
254 }
255
256 | PORT check_login SP host_port CRLF
257 {
258 if ($2)
259 port_check("PORT", AF_INET);
260 }
261
262 | LPRT check_login SP host_long_port4 CRLF
263 {
264 if ($2)
265 port_check("LPRT", AF_INET);
266 }
267
268 | LPRT check_login SP host_long_port6 CRLF
269 {
270 #ifdef INET6
271 if ($2)
272 port_check("LPRT", AF_INET6);
273 #else
274 reply(500, "IPv6 support not available.");
275 #endif
276 }
277
278 | EPRT check_login SP STRING CRLF
279 {
280 if ($2) {
281 if (extended_port($4) == 0)
282 port_check("EPRT", -1);
283 }
284 free($4);
285 }
286
287 | PASV check_login CRLF
288 {
289 if ($2) {
290 if (CURCLASS_FLAGS_ISSET(passive))
291 passive();
292 else
293 reply(500, "PASV mode not available.");
294 }
295 }
296
297 | LPSV check_login CRLF
298 {
299 if ($2) {
300 if (CURCLASS_FLAGS_ISSET(passive)) {
301 if (epsvall)
302 reply(501,
303 "LPSV disallowed after EPSV ALL");
304 else
305 long_passive("LPSV", PF_UNSPEC);
306 } else
307 reply(500, "LPSV mode not available.");
308 }
309 }
310
311 | EPSV check_login SP NUMBER CRLF
312 {
313 if ($2) {
314 if (CURCLASS_FLAGS_ISSET(passive))
315 long_passive("EPSV",
316 epsvproto2af($4.i));
317 else
318 reply(500, "EPSV mode not available.");
319 }
320 }
321
322 | EPSV check_login SP ALL CRLF
323 {
324 if ($2) {
325 if (CURCLASS_FLAGS_ISSET(passive)) {
326 reply(200,
327 "EPSV ALL command successful.");
328 epsvall++;
329 } else
330 reply(500, "EPSV mode not available.");
331 }
332 }
333
334 | EPSV check_login CRLF
335 {
336 if ($2) {
337 if (CURCLASS_FLAGS_ISSET(passive))
338 long_passive("EPSV", PF_UNSPEC);
339 else
340 reply(500, "EPSV mode not available.");
341 }
342 }
343
344 | TYPE check_login SP type_code CRLF
345 {
346 if ($2) {
347
348 switch (cmd_type) {
349
350 case TYPE_A:
351 if (cmd_form == FORM_N) {
352 reply(200, "Type set to A.");
353 type = cmd_type;
354 form = cmd_form;
355 } else
356 reply(504, "Form must be N.");
357 break;
358
359 case TYPE_E:
360 reply(504, "Type E not implemented.");
361 break;
362
363 case TYPE_I:
364 reply(200, "Type set to I.");
365 type = cmd_type;
366 break;
367
368 case TYPE_L:
369 #if NBBY == 8
370 if (cmd_bytesz == 8) {
371 reply(200,
372 "Type set to L (byte size 8).");
373 type = cmd_type;
374 } else
375 reply(504, "Byte size must be 8.");
376 #else /* NBBY == 8 */
377 UNIMPLEMENTED for NBBY != 8
378 #endif /* NBBY == 8 */
379 }
380
381 }
382 }
383
384 | STRU check_login SP struct_code CRLF
385 {
386 if ($2) {
387 switch ($4) {
388
389 case STRU_F:
390 reply(200, "STRU F ok.");
391 break;
392
393 default:
394 reply(504, "Unimplemented STRU type.");
395 }
396 }
397 }
398
399 | MODE check_login SP mode_code CRLF
400 {
401 if ($2) {
402 switch ($4) {
403
404 case MODE_S:
405 reply(200, "MODE S ok.");
406 break;
407
408 default:
409 reply(502, "Unimplemented MODE type.");
410 }
411 }
412 }
413
414 | RETR check_login SP pathname CRLF
415 {
416 if ($2 && $4 != NULL)
417 retrieve(NULL, $4);
418 if ($4 != NULL)
419 free($4);
420 }
421
422 | STOR SP pathname CRLF
423 {
424 if (check_write($3, 1))
425 store($3, "w", 0);
426 if ($3 != NULL)
427 free($3);
428 }
429
430 | STOU SP pathname CRLF
431 {
432 if (check_write($3, 1))
433 store($3, "w", 1);
434 if ($3 != NULL)
435 free($3);
436 }
437
438 | APPE SP pathname CRLF
439 {
440 if (check_write($3, 1))
441 store($3, "a", 0);
442 if ($3 != NULL)
443 free($3);
444 }
445
446 | ALLO check_login SP NUMBER CRLF
447 {
448 if ($2)
449 reply(202, "ALLO command ignored.");
450 }
451
452 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
453 {
454 if ($2)
455 reply(202, "ALLO command ignored.");
456 }
457
458 | RNTO SP pathname CRLF
459 {
460 if (check_write($3, 0)) {
461 if (fromname) {
462 renamecmd(fromname, $3);
463 REASSIGN(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 (CURCLASS_FLAGS_ISSET(hidesymlinks))
515 argv[1] = "-LlgA";
516 if ($2)
517 retrieve(argv, "");
518 }
519
520 | LIST check_login SP pathname CRLF
521 {
522 char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
523
524 if (CURCLASS_FLAGS_ISSET(hidesymlinks))
525 argv[1] = "-LlgA";
526 if ($2 && $4 != NULL) {
527 argv[2] = $4;
528 retrieve(argv, $4);
529 }
530 if ($4 != NULL)
531 free($4);
532 }
533
534 | NLST check_login CRLF
535 {
536 if ($2)
537 send_file_list(".");
538 }
539
540 | NLST check_login SP pathname CRLF
541 {
542 if ($2)
543 send_file_list($4);
544 free($4);
545 }
546
547 | SITE SP HELP CRLF
548 {
549 help(sitetab, NULL);
550 }
551
552 | SITE SP CHMOD SP octal_number SP pathname CRLF
553 {
554 if (check_write($7, 0)) {
555 if (($5 == -1) || ($5 > 0777))
556 reply(501,
557 "CHMOD: Mode value must be between 0 and 0777");
558 else if (chmod($7, $5) < 0)
559 perror_reply(550, $7);
560 else
561 reply(200, "CHMOD command successful.");
562 }
563 if ($7 != NULL)
564 free($7);
565 }
566
567 | SITE SP HELP SP STRING CRLF
568 {
569 help(sitetab, $5);
570 free($5);
571 }
572
573 | SITE SP IDLE check_login CRLF
574 {
575 if ($4) {
576 reply(200,
577 "Current IDLE time limit is " LLF
578 " seconds; max " LLF,
579 (LLT)curclass.timeout,
580 (LLT)curclass.maxtimeout);
581 }
582 }
583
584 | SITE SP IDLE check_login SP NUMBER CRLF
585 {
586 if ($4) {
587 if ($6.i < 30 || $6.i > curclass.maxtimeout) {
588 reply(501,
589 "IDLE time limit must be between 30 and "
590 LLF " seconds",
591 (LLT)curclass.maxtimeout);
592 } else {
593 curclass.timeout = $6.i;
594 (void) alarm(curclass.timeout);
595 reply(200,
596 "IDLE time limit set to "
597 LLF " seconds",
598 (LLT)curclass.timeout);
599 }
600 }
601 }
602
603 | SITE SP RATEGET check_login CRLF
604 {
605 if ($4) {
606 reply(200,
607 "Current RATEGET is " LLF " bytes/sec",
608 (LLT)curclass.rateget);
609 }
610 }
611
612 | SITE SP RATEGET check_login SP STRING CRLF
613 {
614 char errbuf[100];
615 char *p = $6;
616 LLT rate;
617
618 if ($4) {
619 rate = strsuftollx("RATEGET", p, 0,
620 curclass.maxrateget
621 ? curclass.maxrateget
622 : LLTMAX, errbuf, sizeof(errbuf));
623 if (errbuf[0])
624 reply(501, "%s", errbuf);
625 else {
626 curclass.rateget = rate;
627 reply(200,
628 "RATEGET set to " LLF " bytes/sec",
629 (LLT)curclass.rateget);
630 }
631 }
632 free($6);
633 }
634
635 | SITE SP RATEPUT check_login CRLF
636 {
637 if ($4) {
638 reply(200,
639 "Current RATEPUT is " LLF " bytes/sec",
640 (LLT)curclass.rateput);
641 }
642 }
643
644 | SITE SP RATEPUT check_login SP STRING CRLF
645 {
646 char errbuf[100];
647 char *p = $6;
648 LLT rate;
649
650 if ($4) {
651 rate = strsuftollx("RATEPUT", p, 0,
652 curclass.maxrateput
653 ? curclass.maxrateput
654 : LLTMAX, errbuf, sizeof(errbuf));
655 if (errbuf[0])
656 reply(501, "%s", errbuf);
657 else {
658 curclass.rateput = rate;
659 reply(200,
660 "RATEPUT set to " LLF " bytes/sec",
661 (LLT)curclass.rateput);
662 }
663 }
664 free($6);
665 }
666
667 | SITE SP UMASK check_login CRLF
668 {
669 int oldmask;
670
671 if ($4) {
672 oldmask = umask(0);
673 (void) umask(oldmask);
674 reply(200, "Current UMASK is %03o", oldmask);
675 }
676 }
677
678 | SITE SP UMASK check_login SP octal_number CRLF
679 {
680 int oldmask;
681
682 if ($4 && check_write("", 0)) {
683 if (($6 == -1) || ($6 > 0777)) {
684 reply(501, "Bad UMASK value");
685 } else {
686 oldmask = umask($6);
687 reply(200,
688 "UMASK set to %03o (was %03o)",
689 $6, oldmask);
690 }
691 }
692 }
693
694 | SYST CRLF
695 {
696 if (EMPTYSTR(version))
697 reply(215, "UNIX Type: L%d", NBBY);
698 else
699 reply(215, "UNIX Type: L%d Version: %s", NBBY,
700 version);
701 }
702
703 | STAT check_login SP pathname CRLF
704 {
705 if ($2 && $4 != NULL)
706 statfilecmd($4);
707 if ($4 != NULL)
708 free($4);
709 }
710
711 | STAT CRLF
712 {
713 if (is_oob)
714 statxfer();
715 else
716 statcmd();
717 }
718
719 | HELP CRLF
720 {
721 help(cmdtab, NULL);
722 }
723
724 | HELP SP STRING CRLF
725 {
726 char *cp = $3;
727
728 if (strncasecmp(cp, "SITE", 4) == 0) {
729 cp = $3 + 4;
730 if (*cp == ' ')
731 cp++;
732 if (*cp)
733 help(sitetab, cp);
734 else
735 help(sitetab, NULL);
736 } else
737 help(cmdtab, $3);
738 free($3);
739 }
740
741 | NOOP CRLF
742 {
743 reply(200, "NOOP command successful.");
744 }
745
746 /* RFC 2228 */
747 | AUTH SP mechanism_name CRLF
748 {
749 reply(502, "RFC 2228 authentication not implemented.");
750 free($3);
751 }
752
753 | ADAT SP base64data CRLF
754 {
755 reply(503,
756 "Please set authentication state with AUTH.");
757 free($3);
758 }
759
760 | PROT SP prot_code CRLF
761 {
762 reply(503,
763 "Please set protection buffer size with PBSZ.");
764 free($3);
765 }
766
767 | PBSZ SP decimal_integer CRLF
768 {
769 reply(503,
770 "Please set authentication state with AUTH.");
771 }
772
773 | CCC CRLF
774 {
775 reply(533, "No protection enabled.");
776 }
777
778 | MIC SP base64data CRLF
779 {
780 reply(502, "RFC 2228 authentication not implemented.");
781 free($3);
782 }
783
784 | CONF SP base64data CRLF
785 {
786 reply(502, "RFC 2228 authentication not implemented.");
787 free($3);
788 }
789
790 | ENC SP base64data CRLF
791 {
792 reply(502, "RFC 2228 authentication not implemented.");
793 free($3);
794 }
795
796 /* RFC 2389 */
797 | FEAT CRLF
798 {
799
800 feat();
801 }
802
803 | OPTS SP STRING CRLF
804 {
805
806 opts($3);
807 free($3);
808 }
809
810
811 /* extensions from draft-ietf-ftpext-mlst-11 */
812
813 /*
814 * Return size of file in a format suitable for
815 * using with RESTART (we just count bytes).
816 */
817 | SIZE check_login SP pathname CRLF
818 {
819 if ($2 && $4 != NULL)
820 sizecmd($4);
821 if ($4 != NULL)
822 free($4);
823 }
824
825 /*
826 * Return modification time of file as an ISO 3307
827 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
828 * where xxx is the fractional second (of any precision,
829 * not necessarily 3 digits)
830 */
831 | MDTM check_login SP pathname CRLF
832 {
833 if ($2 && $4 != NULL) {
834 struct stat stbuf;
835 if (stat($4, &stbuf) < 0)
836 perror_reply(550, $4);
837 else if (!S_ISREG(stbuf.st_mode)) {
838 reply(550, "%s: not a plain file.", $4);
839 } else {
840 struct tm *t;
841
842 t = gmtime(&stbuf.st_mtime);
843 reply(213,
844 "%04d%02d%02d%02d%02d%02d",
845 TM_YEAR_BASE + t->tm_year,
846 t->tm_mon+1, t->tm_mday,
847 t->tm_hour, t->tm_min, t->tm_sec);
848 }
849 }
850 if ($4 != NULL)
851 free($4);
852 }
853
854 | MLST check_login SP pathname CRLF
855 {
856 if ($2 && $4 != NULL)
857 mlst($4);
858 if ($4 != NULL)
859 free($4);
860 }
861
862 | MLST check_login CRLF
863 {
864 mlst(NULL);
865 }
866
867 | MLSD check_login SP pathname CRLF
868 {
869 if ($2 && $4 != NULL)
870 mlsd($4);
871 if ($4 != NULL)
872 free($4);
873 }
874
875 | MLSD check_login CRLF
876 {
877 mlsd(NULL);
878 }
879
880 | error CRLF
881 {
882 yyerrok;
883 }
884 ;
885
886 rcmd
887 : REST check_login SP NUMBER CRLF
888 {
889 if ($2) {
890 REASSIGN(fromname, NULL);
891 restart_point = (off_t)$4.ll;
892 reply(350,
893 "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
894 (LLT)restart_point);
895 }
896 }
897
898 | RNFR SP pathname CRLF
899 {
900 restart_point = (off_t) 0;
901 if (check_write($3, 0)) {
902 REASSIGN(fromname, NULL);
903 fromname = renamefrom($3);
904 }
905 if ($3 != NULL)
906 free($3);
907 }
908 ;
909
910 username
911 : STRING
912 ;
913
914 password
915 : /* empty */
916 {
917 $$ = (char *)calloc(1, sizeof(char));
918 }
919
920 | STRING
921 ;
922
923 byte_size
924 : NUMBER
925 {
926 $$ = $1.i;
927 }
928 ;
929
930 host_port
931 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
932 NUMBER COMMA NUMBER
933 {
934 char *a, *p;
935
936 memset(&data_dest, 0, sizeof(data_dest));
937 data_dest.su_len = sizeof(struct sockaddr_in);
938 data_dest.su_family = AF_INET;
939 p = (char *)&data_dest.su_port;
940 p[0] = $9.i; p[1] = $11.i;
941 a = (char *)&data_dest.su_addr;
942 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
943 }
944 ;
945
946 host_long_port4
947 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
948 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
949 NUMBER
950 {
951 char *a, *p;
952
953 memset(&data_dest, 0, sizeof(data_dest));
954 data_dest.su_len = sizeof(struct sockaddr_in);
955 data_dest.su_family = AF_INET;
956 p = (char *)&data_dest.su_port;
957 p[0] = $15.i; p[1] = $17.i;
958 a = (char *)&data_dest.su_addr;
959 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
960
961 /* reject invalid LPRT command */
962 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
963 memset(&data_dest, 0, sizeof(data_dest));
964 }
965 ;
966
967 host_long_port6
968 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
969 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
970 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
971 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
972 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
973 NUMBER
974 {
975 #ifdef INET6
976 char *a, *p;
977
978 memset(&data_dest, 0, sizeof(data_dest));
979 data_dest.su_len = sizeof(struct sockaddr_in6);
980 data_dest.su_family = AF_INET6;
981 p = (char *)&data_dest.su_port;
982 p[0] = $39.i; p[1] = $41.i;
983 a = (char *)&data_dest.si_su.su_sin6.sin6_addr;
984 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
985 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
986 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
987 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
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" },
1224 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
1225 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1226 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1227 { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1228 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1229 { "QUIT", QUIT, NOARGS, 1, "(terminate service)" },
1230 { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" },
1231 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1232 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1233 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1234 { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" },
1235 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1236 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1237 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1238 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1239 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1240 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1241 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1242 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1243 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1244 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1245 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1246 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1247 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1248 { "ABOR", ABOR, NOARGS, 4, "(abort operation)" },
1249 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1250 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1251 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1252 { "PWD", PWD, NOARGS, 1, "(return current directory)" },
1253 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1254 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1255 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1256 { "SYST", SYST, NOARGS, 1, "(get type of operating system)" },
1257 { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" },
1258 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1259 { "NOOP", NOOP, NOARGS, 2, "" },
1260
1261 /* From RFC 2228, in order defined */
1262 { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" },
1263 { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" },
1264 { "PROT", PROT, STR1, 1, "<sp> prot-code" },
1265 { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" },
1266 { "CCC", CCC, NOARGS, 1, "(Disable data protection)" },
1267 { "MIC", MIC, STR1, 4, "<sp> base64data" },
1268 { "CONF", CONF, STR1, 4, "<sp> base64data" },
1269 { "ENC", ENC, STR1, 4, "<sp> base64data" },
1270
1271 /* From RFC 2389, in order defined */
1272 { "FEAT", FEAT, NOARGS, 1, "(display extended features)" },
1273 { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" },
1274
1275 /* from draft-ietf-ftpext-mlst-11 */
1276 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1277 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1278 { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" },
1279 { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" },
1280
1281 /* obsolete commands */
1282 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1283 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1284 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1285 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1286 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1287 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1288 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1289 { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1290 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1291 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1292 { "XPWD", PWD, NOARGS, 1, "(return current directory)" },
1293 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1294
1295 { NULL, 0, 0, 0, 0 }
1296 };
1297
1298 struct tab sitetab[] = {
1299 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1300 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1301 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1302 { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" },
1303 { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" },
1304 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1305 { NULL, 0, 0, 0, NULL }
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 * getline - 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 getline(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 (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 (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 (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 = getline(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 HAVE_SETPROCTITLE
1513 if (strncasecmp(cmdp, "PASS", 4) != 0 &&
1514 strncasecmp(cmdp, "ACCT", 4) != 0)
1515 setproctitle("%s: %s", proctitle, cmdp);
1516 #endif /* 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.s = 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.s = 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 = xstrdup(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(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 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