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