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