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