MFC: Fix 'Used' column in -h/-H output by using int64_t.
[dragonfly.git] / libexec / ftpd / ftpcmd.y
blobfeec74f9dc4c3001db1bd0daaf85860974b71eb5
1 /*
2 * Copyright (c) 1985, 1988, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
33 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
35 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
36 * $FreeBSD: src/libexec/ftpd/ftpcmd.y,v 1.16.2.19 2003/02/11 14:28:28 yar Exp $
37 * $DragonFly: src/libexec/ftpd/ftpcmd.y,v 1.4 2004/06/19 20:36:04 joerg Exp $
41 * Grammar for FTP commands.
42 * See RFC 959.
47 #include <sys/param.h>
48 #include <sys/socket.h>
49 #include <sys/stat.h>
51 #include <netinet/in.h>
52 #include <arpa/ftp.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <glob.h>
57 #include <libutil.h>
58 #include <limits.h>
59 #include <md5.h>
60 #include <netdb.h>
61 #include <pwd.h>
62 #include <signal.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <syslog.h>
67 #include <time.h>
68 #include <unistd.h>
70 #include "extern.h"
71 #include "pathnames.h"
73 extern union sockunion data_dest, his_addr;
74 extern int hostinfo;
75 extern int logged_in;
76 extern struct passwd *pw;
77 extern int guest;
78 extern char *homedir;
79 extern int paranoid;
80 extern int logging;
81 extern int type;
82 extern int form;
83 extern int ftpdebug;
84 extern int timeout;
85 extern int maxtimeout;
86 extern int pdata;
87 extern char *hostname;
88 extern char remotehost[];
89 extern char proctitle[];
90 extern int usedefault;
91 extern int transflag;
92 extern char tmpline[];
93 extern int readonly;
94 extern int noepsv;
95 extern int noretr;
96 extern int noguestretr;
97 extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
99 off_t restart_point;
101 static int cmd_type;
102 static int cmd_form;
103 static int cmd_bytesz;
104 static int state;
105 char cbuf[512];
106 char *fromname = (char *) 0;
108 extern int epsvall;
112 %union {
113 struct {
114 off_t o;
115 int i;
116 } u;
117 char *s;
120 %token
121 A B C E F I
122 L N P R S T
125 SP CRLF COMMA
127 USER PASS ACCT REIN QUIT PORT
128 PASV TYPE STRU MODE RETR STOR
129 APPE MLFL MAIL MSND MSOM MSAM
130 MRSQ MRCP ALLO REST RNFR RNTO
131 ABOR DELE CWD LIST NLST SITE
132 STAT HELP NOOP MKD RMD PWD
133 CDUP STOU SMNT SYST SIZE MDTM
134 LPRT LPSV EPRT EPSV
136 UMASK IDLE CHMOD MDFIVE
138 LEXERR NOTIMPL
140 %token <s> STRING
141 %token <u> NUMBER
143 %type <u.i> check_login octal_number byte_size
144 %type <u.i> check_login_ro check_login_epsv
145 %type <u.i> struct_code mode_code type_code form_code
146 %type <s> pathstring pathname password username
147 %type <s> ALL NOTIMPL
149 %start cmd_list
153 cmd_list
154 : /* empty */
155 | cmd_list cmd
157 if (fromname)
158 free(fromname);
159 fromname = (char *) 0;
160 restart_point = (off_t) 0;
162 | cmd_list rcmd
166 : USER SP username CRLF
168 user($3);
169 free($3);
171 | PASS SP password CRLF
173 pass($3);
174 free($3);
176 | PASS CRLF
178 pass("");
180 | PORT check_login SP host_port CRLF
182 if (epsvall) {
183 reply(501, "no PORT allowed after EPSV ALL");
184 goto port_done;
186 if (!$2)
187 goto port_done;
188 if (port_check("PORT") == 1)
189 goto port_done;
190 #ifdef INET6
191 if ((his_addr.su_family != AF_INET6 ||
192 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
193 /* shoud never happen */
194 usedefault = 1;
195 reply(500, "Invalid address rejected.");
196 goto port_done;
198 port_check_v6("pcmd");
199 #endif
200 port_done:
203 | LPRT check_login SP host_long_port CRLF
205 if (epsvall) {
206 reply(501, "no LPRT allowed after EPSV ALL");
207 goto lprt_done;
209 if (!$2)
210 goto lprt_done;
211 if (port_check("LPRT") == 1)
212 goto lprt_done;
213 #ifdef INET6
214 if (his_addr.su_family != AF_INET6) {
215 usedefault = 1;
216 reply(500, "Invalid address rejected.");
217 goto lprt_done;
219 if (port_check_v6("LPRT") == 1)
220 goto lprt_done;
221 #endif
222 lprt_done:
225 | EPRT check_login SP STRING CRLF
227 char delim;
228 char *tmp = NULL;
229 char *p, *q;
230 char *result[3];
231 struct addrinfo hints;
232 struct addrinfo *res;
233 int i;
235 if (epsvall) {
236 reply(501, "no EPRT allowed after EPSV ALL");
237 goto eprt_done;
239 if (!$2)
240 goto eprt_done;
242 memset(&data_dest, 0, sizeof(data_dest));
243 tmp = strdup($4);
244 if (ftpdebug)
245 syslog(LOG_DEBUG, "%s", tmp);
246 if (!tmp) {
247 fatalerror("not enough core");
248 /*NOTREACHED*/
250 p = tmp;
251 delim = p[0];
252 p++;
253 memset(result, 0, sizeof(result));
254 for (i = 0; i < 3; i++) {
255 q = strchr(p, delim);
256 if (!q || *q != delim) {
257 parsefail:
258 reply(500,
259 "Invalid argument, rejected.");
260 if (tmp)
261 free(tmp);
262 usedefault = 1;
263 goto eprt_done;
265 *q++ = '\0';
266 result[i] = p;
267 if (ftpdebug)
268 syslog(LOG_DEBUG, "%d: %s", i, p);
269 p = q;
272 /* some more sanity check */
273 p = result[0];
274 while (*p) {
275 if (!isdigit(*p))
276 goto parsefail;
277 p++;
279 p = result[2];
280 while (*p) {
281 if (!isdigit(*p))
282 goto parsefail;
283 p++;
286 /* grab address */
287 memset(&hints, 0, sizeof(hints));
288 if (atoi(result[0]) == 1)
289 hints.ai_family = PF_INET;
290 #ifdef INET6
291 else if (atoi(result[0]) == 2)
292 hints.ai_family = PF_INET6;
293 #endif
294 else
295 hints.ai_family = PF_UNSPEC; /*XXX*/
296 hints.ai_socktype = SOCK_STREAM;
297 i = getaddrinfo(result[1], result[2], &hints, &res);
298 if (i)
299 goto parsefail;
300 memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
301 #ifdef INET6
302 if (his_addr.su_family == AF_INET6
303 && data_dest.su_family == AF_INET6) {
304 /* XXX more sanity checks! */
305 data_dest.su_sin6.sin6_scope_id =
306 his_addr.su_sin6.sin6_scope_id;
308 #endif
309 free(tmp);
310 tmp = NULL;
312 if (port_check("EPRT") == 1)
313 goto eprt_done;
314 #ifdef INET6
315 if (his_addr.su_family != AF_INET6) {
316 usedefault = 1;
317 reply(500, "Invalid address rejected.");
318 goto eprt_done;
320 if (port_check_v6("EPRT") == 1)
321 goto eprt_done;
322 #endif
323 eprt_done:
324 free($4);
326 | PASV check_login CRLF
328 if (epsvall)
329 reply(501, "no PASV allowed after EPSV ALL");
330 else if ($2)
331 passive();
333 | LPSV check_login CRLF
335 if (epsvall)
336 reply(501, "no LPSV allowed after EPSV ALL");
337 else if ($2)
338 long_passive("LPSV", PF_UNSPEC);
340 | EPSV check_login_epsv SP NUMBER CRLF
342 if ($2) {
343 int pf;
344 switch ($4.i) {
345 case 1:
346 pf = PF_INET;
347 break;
348 #ifdef INET6
349 case 2:
350 pf = PF_INET6;
351 break;
352 #endif
353 default:
354 pf = -1; /*junk value*/
355 break;
357 long_passive("EPSV", pf);
360 | EPSV check_login_epsv SP ALL CRLF
362 if ($2) {
363 reply(200,
364 "EPSV ALL command successful.");
365 epsvall++;
368 | EPSV check_login_epsv CRLF
370 if ($2)
371 long_passive("EPSV", PF_UNSPEC);
373 | TYPE check_login SP type_code CRLF
375 if ($2) {
376 switch (cmd_type) {
378 case TYPE_A:
379 if (cmd_form == FORM_N) {
380 reply(200, "Type set to A.");
381 type = cmd_type;
382 form = cmd_form;
383 } else
384 reply(504, "Form must be N.");
385 break;
387 case TYPE_E:
388 reply(504, "Type E not implemented.");
389 break;
391 case TYPE_I:
392 reply(200, "Type set to I.");
393 type = cmd_type;
394 break;
396 case TYPE_L:
397 #if NBBY == 8
398 if (cmd_bytesz == 8) {
399 reply(200,
400 "Type set to L (byte size 8).");
401 type = cmd_type;
402 } else
403 reply(504, "Byte size must be 8.");
404 #else /* NBBY == 8 */
405 UNIMPLEMENTED for NBBY != 8
406 #endif /* NBBY == 8 */
410 | STRU check_login SP struct_code CRLF
412 if ($2) {
413 switch ($4) {
415 case STRU_F:
416 reply(200, "STRU F ok.");
417 break;
419 default:
420 reply(504, "Unimplemented STRU type.");
424 | MODE check_login SP mode_code CRLF
426 if ($2) {
427 switch ($4) {
429 case MODE_S:
430 reply(200, "MODE S ok.");
431 break;
433 default:
434 reply(502, "Unimplemented MODE type.");
438 | ALLO check_login SP NUMBER CRLF
440 if ($2) {
441 reply(202, "ALLO command ignored.");
444 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
446 if ($2) {
447 reply(202, "ALLO command ignored.");
450 | RETR check_login SP pathname CRLF
452 if (noretr || (guest && noguestretr))
453 reply(500, "RETR command is disabled");
454 else if ($2 && $4 != NULL)
455 retrieve((char *) 0, $4);
457 if ($4 != NULL)
458 free($4);
460 | STOR check_login_ro SP pathname CRLF
462 if ($2 && $4 != NULL)
463 store($4, "w", 0);
464 if ($4 != NULL)
465 free($4);
467 | APPE check_login_ro SP pathname CRLF
469 if ($2 && $4 != NULL)
470 store($4, "a", 0);
471 if ($4 != NULL)
472 free($4);
474 | NLST check_login CRLF
476 if ($2)
477 send_file_list(".");
479 | NLST check_login SP pathstring CRLF
481 if ($2)
482 send_file_list($4);
483 free($4);
485 | LIST check_login CRLF
487 if ($2)
488 retrieve(_PATH_LS " -lgA", "");
490 | LIST check_login SP pathstring CRLF
492 if ($2)
493 retrieve(_PATH_LS " -lgA %s", $4);
494 free($4);
496 | STAT check_login SP pathname CRLF
498 if ($2 && $4 != NULL)
499 statfilecmd($4);
500 if ($4 != NULL)
501 free($4);
503 | STAT check_login CRLF
505 if ($2) {
506 statcmd();
509 | DELE check_login_ro SP pathname CRLF
511 if ($2 && $4 != NULL)
512 delete($4);
513 if ($4 != NULL)
514 free($4);
516 | RNTO check_login_ro SP pathname CRLF
518 if ($2 && $4 != NULL) {
519 if (fromname) {
520 renamecmd(fromname, $4);
521 free(fromname);
522 fromname = (char *) 0;
523 } else {
524 reply(503, "Bad sequence of commands.");
527 if ($4 != NULL)
528 free($4);
530 | ABOR check_login CRLF
532 if ($2)
533 reply(225, "ABOR command successful.");
535 | CWD check_login CRLF
537 if ($2) {
538 cwd(homedir);
541 | CWD check_login SP pathname CRLF
543 if ($2 && $4 != NULL)
544 cwd($4);
545 if ($4 != NULL)
546 free($4);
548 | HELP CRLF
550 help(cmdtab, (char *) 0);
552 | HELP SP STRING CRLF
554 char *cp = $3;
556 if (strncasecmp(cp, "SITE", 4) == 0) {
557 cp = $3 + 4;
558 if (*cp == ' ')
559 cp++;
560 if (*cp)
561 help(sitetab, cp);
562 else
563 help(sitetab, (char *) 0);
564 } else
565 help(cmdtab, $3);
566 free($3);
568 | NOOP CRLF
570 reply(200, "NOOP command successful.");
572 | MKD check_login_ro SP pathname CRLF
574 if ($2 && $4 != NULL)
575 makedir($4);
576 if ($4 != NULL)
577 free($4);
579 | RMD check_login_ro SP pathname CRLF
581 if ($2 && $4 != NULL)
582 removedir($4);
583 if ($4 != NULL)
584 free($4);
586 | PWD check_login CRLF
588 if ($2)
589 pwd();
591 | CDUP check_login CRLF
593 if ($2)
594 cwd("..");
596 | SITE SP HELP CRLF
598 help(sitetab, (char *) 0);
600 | SITE SP HELP SP STRING CRLF
602 help(sitetab, $5);
603 free($5);
605 | SITE SP MDFIVE check_login SP pathname CRLF
607 char p[64], *q;
609 if ($4 && $6) {
610 q = MD5File($6, p);
611 if (q != NULL)
612 reply(200, "MD5(%s) = %s", $6, p);
613 else
614 perror_reply(550, $6);
616 if ($6)
617 free($6);
619 | SITE SP UMASK check_login CRLF
621 int oldmask;
623 if ($4) {
624 oldmask = umask(0);
625 (void) umask(oldmask);
626 reply(200, "Current UMASK is %03o", oldmask);
629 | SITE SP UMASK check_login SP octal_number CRLF
631 int oldmask;
633 if ($4) {
634 if (($6 == -1) || ($6 > 0777)) {
635 reply(501, "Bad UMASK value");
636 } else {
637 oldmask = umask($6);
638 reply(200,
639 "UMASK set to %03o (was %03o)",
640 $6, oldmask);
644 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
646 if ($4 && ($8 != NULL)) {
647 if (($6 == -1 ) || ($6 > 0777))
648 reply(501, "Bad mode value");
649 else if (chmod($8, $6) < 0)
650 perror_reply(550, $8);
651 else
652 reply(200, "CHMOD command successful.");
654 if ($8 != NULL)
655 free($8);
657 | SITE SP check_login IDLE CRLF
659 if ($3)
660 reply(200,
661 "Current IDLE time limit is %d seconds; max %d",
662 timeout, maxtimeout);
664 | SITE SP check_login IDLE SP NUMBER CRLF
666 if ($3) {
667 if ($6.i < 30 || $6.i > maxtimeout) {
668 reply(501,
669 "Maximum IDLE time must be between 30 and %d seconds",
670 maxtimeout);
671 } else {
672 timeout = $6.i;
673 (void) alarm((unsigned) timeout);
674 reply(200,
675 "Maximum IDLE time set to %d seconds",
676 timeout);
680 | STOU check_login_ro SP pathname CRLF
682 if ($2 && $4 != NULL)
683 store($4, "w", 1);
684 if ($4 != NULL)
685 free($4);
687 | SYST check_login CRLF
689 if ($2)
690 #ifdef unix
691 #ifdef BSD
692 reply(215, "UNIX Type: L%d Version: BSD-%d",
693 NBBY, BSD);
694 #else /* BSD */
695 reply(215, "UNIX Type: L%d", NBBY);
696 #endif /* BSD */
697 #else /* unix */
698 reply(215, "UNKNOWN Type: L%d", NBBY);
699 #endif /* unix */
703 * SIZE is not in RFC959, but Postel has blessed it and
704 * it will be in the updated RFC.
706 * Return size of file in a format suitable for
707 * using with RESTART (we just count bytes).
709 | SIZE check_login SP pathname CRLF
711 if ($2 && $4 != NULL)
712 sizecmd($4);
713 if ($4 != NULL)
714 free($4);
718 * MDTM is not in RFC959, but Postel has blessed it and
719 * it will be in the updated RFC.
721 * Return modification time of file as an ISO 3307
722 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
723 * where xxx is the fractional second (of any precision,
724 * not necessarily 3 digits)
726 | MDTM check_login SP pathname CRLF
728 if ($2 && $4 != NULL) {
729 struct stat stbuf;
730 if (stat($4, &stbuf) < 0)
731 reply(550, "%s: %s",
732 $4, strerror(errno));
733 else if (!S_ISREG(stbuf.st_mode)) {
734 reply(550, "%s: not a plain file.", $4);
735 } else {
736 struct tm *t;
737 t = gmtime(&stbuf.st_mtime);
738 reply(213,
739 "%04d%02d%02d%02d%02d%02d",
740 1900 + t->tm_year,
741 t->tm_mon+1, t->tm_mday,
742 t->tm_hour, t->tm_min, t->tm_sec);
745 if ($4 != NULL)
746 free($4);
748 | QUIT CRLF
750 reply(221, "Goodbye.");
751 dologout(0);
753 | NOTIMPL
755 nack($1);
757 | error
759 yyclearin; /* discard lookahead data */
760 yyerrok; /* clear error condition */
761 state = CMD; /* reset lexer state */
764 rcmd
765 : RNFR check_login_ro SP pathname CRLF
767 restart_point = (off_t) 0;
768 if ($2 && $4) {
769 if (fromname)
770 free(fromname);
771 fromname = (char *) 0;
772 if (renamefrom($4))
773 fromname = $4;
774 else
775 free($4);
776 } else if ($4) {
777 free($4);
780 | REST check_login SP NUMBER CRLF
782 if ($2) {
783 if (fromname)
784 free(fromname);
785 fromname = (char *) 0;
786 restart_point = $4.o;
787 reply(350, "Restarting at %llu. %s",
788 restart_point,
789 "Send STORE or RETRIEVE to initiate transfer.");
794 username
795 : STRING
798 password
799 : /* empty */
801 $$ = (char *)calloc(1, sizeof(char));
803 | STRING
806 byte_size
807 : NUMBER
809 $$ = $1.i;
813 host_port
814 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
815 NUMBER COMMA NUMBER
817 char *a, *p;
819 data_dest.su_len = sizeof(struct sockaddr_in);
820 data_dest.su_family = AF_INET;
821 p = (char *)&data_dest.su_sin.sin_port;
822 p[0] = $9.i; p[1] = $11.i;
823 a = (char *)&data_dest.su_sin.sin_addr;
824 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
828 host_long_port
829 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
830 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
831 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
832 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
833 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
834 NUMBER
836 char *a, *p;
838 memset(&data_dest, 0, sizeof(data_dest));
839 data_dest.su_len = sizeof(struct sockaddr_in6);
840 data_dest.su_family = AF_INET6;
841 p = (char *)&data_dest.su_port;
842 p[0] = $39.i; p[1] = $41.i;
843 a = (char *)&data_dest.su_sin6.sin6_addr;
844 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
845 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
846 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
847 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
848 if (his_addr.su_family == AF_INET6) {
849 /* XXX more sanity checks! */
850 data_dest.su_sin6.sin6_scope_id =
851 his_addr.su_sin6.sin6_scope_id;
853 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
854 memset(&data_dest, 0, sizeof(data_dest));
856 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
857 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
858 NUMBER
860 char *a, *p;
862 memset(&data_dest, 0, sizeof(data_dest));
863 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
864 data_dest.su_family = AF_INET;
865 p = (char *)&data_dest.su_port;
866 p[0] = $15.i; p[1] = $17.i;
867 a = (char *)&data_dest.su_sin.sin_addr;
868 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
869 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
870 memset(&data_dest, 0, sizeof(data_dest));
874 form_code
877 $$ = FORM_N;
881 $$ = FORM_T;
885 $$ = FORM_C;
889 type_code
892 cmd_type = TYPE_A;
893 cmd_form = FORM_N;
895 | A SP form_code
897 cmd_type = TYPE_A;
898 cmd_form = $3;
902 cmd_type = TYPE_E;
903 cmd_form = FORM_N;
905 | E SP form_code
907 cmd_type = TYPE_E;
908 cmd_form = $3;
912 cmd_type = TYPE_I;
916 cmd_type = TYPE_L;
917 cmd_bytesz = NBBY;
919 | L SP byte_size
921 cmd_type = TYPE_L;
922 cmd_bytesz = $3;
924 /* this is for a bug in the BBN ftp */
925 | L byte_size
927 cmd_type = TYPE_L;
928 cmd_bytesz = $2;
932 struct_code
935 $$ = STRU_F;
939 $$ = STRU_R;
943 $$ = STRU_P;
947 mode_code
950 $$ = MODE_S;
954 $$ = MODE_B;
958 $$ = MODE_C;
962 pathname
963 : pathstring
966 * Problem: this production is used for all pathname
967 * processing, but only gives a 550 error reply.
968 * This is a valid reply in some cases but not in others.
970 if (logged_in && $1) {
971 glob_t gl;
972 char *p, **pp;
973 int flags =
974 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
975 int n;
977 memset(&gl, 0, sizeof(gl));
978 flags |= GLOB_LIMIT;
979 gl.gl_matchc = MAXGLOBARGS;
980 if (glob($1, flags, NULL, &gl) ||
981 gl.gl_pathc == 0) {
982 reply(550, "wildcard expansion error");
983 $$ = NULL;
984 } else {
985 n = 0;
986 for (pp = gl.gl_pathv; *pp; pp++)
987 if (strcspn(*pp, "\r\n") ==
988 strlen(*pp)) {
989 p = *pp;
990 n++;
992 if (n == 0)
993 $$ = strdup($1);
994 else if (n == 1)
995 $$ = strdup(p);
996 else {
997 reply(550, "ambiguous");
998 $$ = NULL;
1001 globfree(&gl);
1002 free($1);
1003 } else
1004 $$ = $1;
1008 pathstring
1009 : STRING
1012 octal_number
1013 : NUMBER
1015 int ret, dec, multby, digit;
1018 * Convert a number that was read as decimal number
1019 * to what it would be if it had been read as octal.
1021 dec = $1.i;
1022 multby = 1;
1023 ret = 0;
1024 while (dec) {
1025 digit = dec%10;
1026 if (digit > 7) {
1027 ret = -1;
1028 break;
1030 ret += digit * multby;
1031 multby *= 8;
1032 dec /= 10;
1034 $$ = ret;
1039 check_login
1040 : /* empty */
1042 $$ = check_login1();
1046 check_login_epsv
1047 : /* empty */
1049 if (noepsv) {
1050 reply(500, "EPSV command disabled");
1051 $$ = 0;
1053 else
1054 $$ = check_login1();
1058 check_login_ro
1059 : /* empty */
1061 if (readonly) {
1062 reply(550, "Permission denied.");
1063 $$ = 0;
1065 else
1066 $$ = check_login1();
1072 #define CMD 0 /* beginning of command */
1073 #define ARGS 1 /* expect miscellaneous arguments */
1074 #define STR1 2 /* expect SP followed by STRING */
1075 #define STR2 3 /* expect STRING */
1076 #define OSTR 4 /* optional SP then STRING */
1077 #define ZSTR1 5 /* optional SP then optional STRING */
1078 #define ZSTR2 6 /* optional STRING after SP */
1079 #define SITECMD 7 /* SITE command */
1080 #define NSTR 8 /* Number followed by a string */
1082 #define MAXGLOBARGS 1000
1084 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1086 struct tab {
1087 char *name;
1088 short token;
1089 short state;
1090 short implemented; /* 1 if command is implemented */
1091 char *help;
1094 struct tab cmdtab[] = { /* In order defined in RFC 765 */
1095 { "USER", USER, STR1, 1, "<sp> username" },
1096 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1097 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1098 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1099 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1100 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1101 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1102 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1103 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1104 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1105 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1106 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1107 { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" },
1108 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1109 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1110 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1111 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1112 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1113 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1114 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1115 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1116 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1117 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1118 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1119 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1120 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1121 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1122 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1123 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1124 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1125 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1126 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1127 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1128 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1129 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1130 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1131 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1132 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1133 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1134 { "NOOP", NOOP, ARGS, 1, "" },
1135 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1136 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1137 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1138 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1139 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1140 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1141 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1142 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1143 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1144 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1145 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1146 { NULL, 0, 0, 0, 0 }
1149 struct tab sitetab[] = {
1150 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1151 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1152 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1153 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1154 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1155 { NULL, 0, 0, 0, 0 }
1158 static char *copy (char *);
1159 static void help (struct tab *, char *);
1160 static struct tab *
1161 lookup (struct tab *, char *);
1162 static int port_check (const char *);
1163 static int port_check_v6 (const char *);
1164 static void sizecmd (char *);
1165 static void toolong (int);
1166 static void v4map_data_dest (void);
1167 static int yylex (void);
1169 static struct tab *
1170 lookup(p, cmd)
1171 struct tab *p;
1172 char *cmd;
1175 for (; p->name != NULL; p++)
1176 if (strcmp(cmd, p->name) == 0)
1177 return (p);
1178 return (0);
1181 #include <arpa/telnet.h>
1184 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1186 char *
1187 getline(s, n, iop)
1188 char *s;
1189 int n;
1190 FILE *iop;
1192 int c;
1193 register char *cs;
1195 cs = s;
1196 /* tmpline may contain saved command from urgent mode interruption */
1197 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1198 *cs++ = tmpline[c];
1199 if (tmpline[c] == '\n') {
1200 *cs++ = '\0';
1201 if (ftpdebug)
1202 syslog(LOG_DEBUG, "command: %s", s);
1203 tmpline[0] = '\0';
1204 return(s);
1206 if (c == 0)
1207 tmpline[0] = '\0';
1209 while ((c = getc(iop)) != EOF) {
1210 c &= 0377;
1211 if (c == IAC) {
1212 if ((c = getc(iop)) != EOF) {
1213 c &= 0377;
1214 switch (c) {
1215 case WILL:
1216 case WONT:
1217 c = getc(iop);
1218 printf("%c%c%c", IAC, DONT, 0377&c);
1219 (void) fflush(stdout);
1220 continue;
1221 case DO:
1222 case DONT:
1223 c = getc(iop);
1224 printf("%c%c%c", IAC, WONT, 0377&c);
1225 (void) fflush(stdout);
1226 continue;
1227 case IAC:
1228 break;
1229 default:
1230 continue; /* ignore command */
1234 *cs++ = c;
1235 if (--n <= 0 || c == '\n')
1236 break;
1238 if (c == EOF && cs == s)
1239 return (NULL);
1240 *cs++ = '\0';
1241 if (ftpdebug) {
1242 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1243 /* Don't syslog passwords */
1244 syslog(LOG_DEBUG, "command: %.5s ???", s);
1245 } else {
1246 register char *cp;
1247 register int len;
1249 /* Don't syslog trailing CR-LF */
1250 len = strlen(s);
1251 cp = s + len - 1;
1252 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1253 --cp;
1254 --len;
1256 syslog(LOG_DEBUG, "command: %.*s", len, s);
1259 return (s);
1262 static void
1263 toolong(signo)
1264 int signo;
1267 reply(421,
1268 "Timeout (%d seconds): closing control connection.", timeout);
1269 if (logging)
1270 syslog(LOG_INFO, "User %s timed out after %d seconds",
1271 (pw ? pw -> pw_name : "unknown"), timeout);
1272 dologout(1);
1275 static int
1276 yylex()
1278 static int cpos;
1279 char *cp, *cp2;
1280 struct tab *p;
1281 int n;
1282 char c;
1284 for (;;) {
1285 switch (state) {
1287 case CMD:
1288 (void) signal(SIGALRM, toolong);
1289 (void) alarm((unsigned) timeout);
1290 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1291 reply(221, "You could at least say goodbye.");
1292 dologout(0);
1294 (void) alarm(0);
1295 #ifdef SETPROCTITLE
1296 if (strncasecmp(cbuf, "PASS", 4) != 0)
1297 setproctitle("%s: %s", proctitle, cbuf);
1298 #endif /* SETPROCTITLE */
1299 if ((cp = strchr(cbuf, '\r'))) {
1300 *cp++ = '\n';
1301 *cp = '\0';
1303 if ((cp = strpbrk(cbuf, " \n")))
1304 cpos = cp - cbuf;
1305 if (cpos == 0)
1306 cpos = 4;
1307 c = cbuf[cpos];
1308 cbuf[cpos] = '\0';
1309 upper(cbuf);
1310 p = lookup(cmdtab, cbuf);
1311 cbuf[cpos] = c;
1312 if (p != 0) {
1313 yylval.s = p->name;
1314 if (!p->implemented)
1315 return (NOTIMPL); /* state remains CMD */
1316 state = p->state;
1317 return (p->token);
1319 break;
1321 case SITECMD:
1322 if (cbuf[cpos] == ' ') {
1323 cpos++;
1324 return (SP);
1326 cp = &cbuf[cpos];
1327 if ((cp2 = strpbrk(cp, " \n")))
1328 cpos = cp2 - cbuf;
1329 c = cbuf[cpos];
1330 cbuf[cpos] = '\0';
1331 upper(cp);
1332 p = lookup(sitetab, cp);
1333 cbuf[cpos] = c;
1334 if (guest == 0 && p != 0) {
1335 yylval.s = p->name;
1336 if (!p->implemented) {
1337 state = CMD;
1338 return (NOTIMPL);
1340 state = p->state;
1341 return (p->token);
1343 state = CMD;
1344 break;
1346 case ZSTR1:
1347 case OSTR:
1348 if (cbuf[cpos] == '\n') {
1349 state = CMD;
1350 return (CRLF);
1352 /* FALLTHROUGH */
1354 case STR1:
1355 dostr1:
1356 if (cbuf[cpos] == ' ') {
1357 cpos++;
1358 state = state == OSTR ? STR2 : state+1;
1359 return (SP);
1361 break;
1363 case ZSTR2:
1364 if (cbuf[cpos] == '\n') {
1365 state = CMD;
1366 return (CRLF);
1368 /* FALLTHROUGH */
1370 case STR2:
1371 cp = &cbuf[cpos];
1372 n = strlen(cp);
1373 cpos += n - 1;
1375 * Make sure the string is nonempty and \n terminated.
1377 if (n > 1 && cbuf[cpos] == '\n') {
1378 cbuf[cpos] = '\0';
1379 yylval.s = copy(cp);
1380 cbuf[cpos] = '\n';
1381 state = ARGS;
1382 return (STRING);
1384 break;
1386 case NSTR:
1387 if (cbuf[cpos] == ' ') {
1388 cpos++;
1389 return (SP);
1391 if (isdigit(cbuf[cpos])) {
1392 cp = &cbuf[cpos];
1393 while (isdigit(cbuf[++cpos]))
1395 c = cbuf[cpos];
1396 cbuf[cpos] = '\0';
1397 yylval.u.i = atoi(cp);
1398 cbuf[cpos] = c;
1399 state = STR1;
1400 return (NUMBER);
1402 state = STR1;
1403 goto dostr1;
1405 case ARGS:
1406 if (isdigit(cbuf[cpos])) {
1407 cp = &cbuf[cpos];
1408 while (isdigit(cbuf[++cpos]))
1410 c = cbuf[cpos];
1411 cbuf[cpos] = '\0';
1412 yylval.u.i = atoi(cp);
1413 yylval.u.o = strtoull(cp, (char **)NULL, 10);
1414 cbuf[cpos] = c;
1415 return (NUMBER);
1417 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1418 && !isalnum(cbuf[cpos + 3])) {
1419 cpos += 3;
1420 return ALL;
1422 switch (cbuf[cpos++]) {
1424 case '\n':
1425 state = CMD;
1426 return (CRLF);
1428 case ' ':
1429 return (SP);
1431 case ',':
1432 return (COMMA);
1434 case 'A':
1435 case 'a':
1436 return (A);
1438 case 'B':
1439 case 'b':
1440 return (B);
1442 case 'C':
1443 case 'c':
1444 return (C);
1446 case 'E':
1447 case 'e':
1448 return (E);
1450 case 'F':
1451 case 'f':
1452 return (F);
1454 case 'I':
1455 case 'i':
1456 return (I);
1458 case 'L':
1459 case 'l':
1460 return (L);
1462 case 'N':
1463 case 'n':
1464 return (N);
1466 case 'P':
1467 case 'p':
1468 return (P);
1470 case 'R':
1471 case 'r':
1472 return (R);
1474 case 'S':
1475 case 's':
1476 return (S);
1478 case 'T':
1479 case 't':
1480 return (T);
1483 break;
1485 default:
1486 fatalerror("Unknown state in scanner.");
1488 state = CMD;
1489 return (LEXERR);
1493 void
1494 upper(s)
1495 char *s;
1497 while (*s != '\0') {
1498 if (islower(*s))
1499 *s = toupper(*s);
1500 s++;
1504 static char *
1505 copy(s)
1506 char *s;
1508 char *p;
1510 p = malloc((unsigned) strlen(s) + 1);
1511 if (p == NULL)
1512 fatalerror("Ran out of memory.");
1513 (void) strcpy(p, s);
1514 return (p);
1517 static void
1518 help(ctab, s)
1519 struct tab *ctab;
1520 char *s;
1522 struct tab *c;
1523 int width, NCMDS;
1524 char *type;
1526 if (ctab == sitetab)
1527 type = "SITE ";
1528 else
1529 type = "";
1530 width = 0, NCMDS = 0;
1531 for (c = ctab; c->name != NULL; c++) {
1532 int len = strlen(c->name);
1534 if (len > width)
1535 width = len;
1536 NCMDS++;
1538 width = (width + 8) &~ 7;
1539 if (s == 0) {
1540 int i, j, w;
1541 int columns, lines;
1543 lreply(214, "The following %scommands are recognized %s.",
1544 type, "(* =>'s unimplemented)");
1545 columns = 76 / width;
1546 if (columns == 0)
1547 columns = 1;
1548 lines = (NCMDS + columns - 1) / columns;
1549 for (i = 0; i < lines; i++) {
1550 printf(" ");
1551 for (j = 0; j < columns; j++) {
1552 c = ctab + j * lines + i;
1553 printf("%s%c", c->name,
1554 c->implemented ? ' ' : '*');
1555 if (c + lines >= &ctab[NCMDS])
1556 break;
1557 w = strlen(c->name) + 1;
1558 while (w < width) {
1559 putchar(' ');
1560 w++;
1563 printf("\r\n");
1565 (void) fflush(stdout);
1566 if (hostinfo)
1567 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1568 else
1569 reply(214, "End.");
1570 return;
1572 upper(s);
1573 c = lookup(ctab, s);
1574 if (c == (struct tab *)0) {
1575 reply(502, "Unknown command %s.", s);
1576 return;
1578 if (c->implemented)
1579 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1580 else
1581 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1582 c->name, c->help);
1585 static void
1586 sizecmd(filename)
1587 char *filename;
1589 switch (type) {
1590 case TYPE_L:
1591 case TYPE_I: {
1592 struct stat stbuf;
1593 if (stat(filename, &stbuf) < 0)
1594 perror_reply(550, filename);
1595 else if (!S_ISREG(stbuf.st_mode))
1596 reply(550, "%s: not a plain file.", filename);
1597 else
1598 reply(213, "%qu", stbuf.st_size);
1599 break; }
1600 case TYPE_A: {
1601 FILE *fin;
1602 int c;
1603 off_t count;
1604 struct stat stbuf;
1605 fin = fopen(filename, "r");
1606 if (fin == NULL) {
1607 perror_reply(550, filename);
1608 return;
1610 if (fstat(fileno(fin), &stbuf) < 0) {
1611 perror_reply(550, filename);
1612 (void) fclose(fin);
1613 return;
1614 } else if (!S_ISREG(stbuf.st_mode)) {
1615 reply(550, "%s: not a plain file.", filename);
1616 (void) fclose(fin);
1617 return;
1618 } else if (stbuf.st_size > MAXASIZE) {
1619 reply(550, "%s: too large for type A SIZE.", filename);
1620 (void) fclose(fin);
1621 return;
1624 count = 0;
1625 while((c=getc(fin)) != EOF) {
1626 if (c == '\n') /* will get expanded to \r\n */
1627 count++;
1628 count++;
1630 (void) fclose(fin);
1632 reply(213, "%qd", count);
1633 break; }
1634 default:
1635 reply(504, "SIZE not implemented for type %s.",
1636 typenames[type]);
1640 /* Return 1, if port check is done. Return 0, if not yet. */
1641 static int
1642 port_check(pcmd)
1643 const char *pcmd;
1645 if (his_addr.su_family == AF_INET) {
1646 if (data_dest.su_family != AF_INET) {
1647 usedefault = 1;
1648 reply(500, "Invalid address rejected.");
1649 return 1;
1651 if (paranoid &&
1652 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1653 memcmp(&data_dest.su_sin.sin_addr,
1654 &his_addr.su_sin.sin_addr,
1655 sizeof(data_dest.su_sin.sin_addr)))) {
1656 usedefault = 1;
1657 reply(500, "Illegal PORT range rejected.");
1658 } else {
1659 usedefault = 0;
1660 if (pdata >= 0) {
1661 (void) close(pdata);
1662 pdata = -1;
1664 reply(200, "%s command successful.", pcmd);
1666 return 1;
1668 return 0;
1671 static int
1672 check_login1()
1674 if (logged_in)
1675 return 1;
1676 else {
1677 reply(530, "Please login with USER and PASS.");
1678 return 0;
1682 #ifdef INET6
1683 /* Return 1, if port check is done. Return 0, if not yet. */
1684 static int
1685 port_check_v6(pcmd)
1686 const char *pcmd;
1688 if (his_addr.su_family == AF_INET6) {
1689 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1690 /* Convert data_dest into v4 mapped sockaddr.*/
1691 v4map_data_dest();
1692 if (data_dest.su_family != AF_INET6) {
1693 usedefault = 1;
1694 reply(500, "Invalid address rejected.");
1695 return 1;
1697 if (paranoid &&
1698 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1699 memcmp(&data_dest.su_sin6.sin6_addr,
1700 &his_addr.su_sin6.sin6_addr,
1701 sizeof(data_dest.su_sin6.sin6_addr)))) {
1702 usedefault = 1;
1703 reply(500, "Illegal PORT range rejected.");
1704 } else {
1705 usedefault = 0;
1706 if (pdata >= 0) {
1707 (void) close(pdata);
1708 pdata = -1;
1710 reply(200, "%s command successful.", pcmd);
1712 return 1;
1714 return 0;
1717 static void
1718 v4map_data_dest()
1720 struct in_addr savedaddr;
1721 int savedport;
1723 if (data_dest.su_family != AF_INET) {
1724 usedefault = 1;
1725 reply(500, "Invalid address rejected.");
1726 return;
1729 savedaddr = data_dest.su_sin.sin_addr;
1730 savedport = data_dest.su_port;
1732 memset(&data_dest, 0, sizeof(data_dest));
1733 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1734 data_dest.su_sin6.sin6_family = AF_INET6;
1735 data_dest.su_sin6.sin6_port = savedport;
1736 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1737 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1738 (caddr_t)&savedaddr, sizeof(savedaddr));
1740 #endif