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