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
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
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.3 2003/11/14 03:54:30 dillon Exp $
41 * Grammar for FTP commands.
47 #include <sys/param.h>
48 #include <sys/socket.h>
51 #include <netinet/in.h>
71 #include "pathnames.h"
73 extern union sockunion data_dest, his_addr;
76 extern struct passwd *pw;
85 extern int maxtimeout;
87 extern char *hostname;
88 extern char remotehost[];
89 extern char proctitle[];
90 extern int usedefault;
92 extern char tmpline[];
96 extern int noguestretr;
97 extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
103 static int cmd_bytesz;
106 char *fromname = (char *) 0;
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
136 UMASK IDLE CHMOD MDFIVE
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
159 fromname = (char *) 0;
160 restart_point = (off_t) 0;
166 : USER SP username CRLF
171 | PASS SP password CRLF
180 | PORT check_login SP host_port CRLF
183 reply(501, "no PORT allowed after EPSV ALL");
188 if (port_check("PORT") == 1)
191 if ((his_addr.su_family != AF_INET6 ||
192 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
193 /* shoud never happen */
195 reply(500, "Invalid address rejected.");
198 port_check_v6("pcmd");
202 | LPRT check_login SP host_long_port CRLF
205 reply(501, "no LPRT allowed after EPSV ALL");
210 if (port_check("LPRT") == 1)
213 if (his_addr.su_family != AF_INET6) {
215 reply(500, "Invalid address rejected.");
218 if (port_check_v6("LPRT") == 1)
223 | EPRT check_login SP STRING CRLF
229 struct addrinfo hints;
230 struct addrinfo *res;
234 reply(501, "no EPRT allowed after EPSV ALL");
240 memset(&data_dest, 0, sizeof(data_dest));
243 syslog(LOG_DEBUG, "%s", tmp);
245 fatalerror("not enough core");
251 memset(result, 0, sizeof(result));
252 for (i = 0; i < 3; i++) {
253 q = strchr(p, delim);
254 if (!q || *q != delim) {
257 "Invalid argument, rejected.");
266 syslog(LOG_DEBUG, "%d: %s", i, p);
270 /* some more sanity check */
285 memset(&hints, 0, sizeof(hints));
286 if (atoi(result[0]) == 1)
287 hints.ai_family = PF_INET;
289 else if (atoi(result[0]) == 2)
290 hints.ai_family = PF_INET6;
293 hints.ai_family = PF_UNSPEC; /*XXX*/
294 hints.ai_socktype = SOCK_STREAM;
295 i = getaddrinfo(result[1], result[2], &hints, &res);
298 memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
300 if (his_addr.su_family == AF_INET6
301 && data_dest.su_family == AF_INET6) {
302 /* XXX more sanity checks! */
303 data_dest.su_sin6.sin6_scope_id =
304 his_addr.su_sin6.sin6_scope_id;
310 if (port_check("EPRT") == 1)
313 if (his_addr.su_family != AF_INET6) {
315 reply(500, "Invalid address rejected.");
318 if (port_check_v6("EPRT") == 1)
324 | PASV check_login CRLF
327 reply(501, "no PASV allowed after EPSV ALL");
331 | LPSV check_login CRLF
334 reply(501, "no LPSV allowed after EPSV ALL");
336 long_passive("LPSV", PF_UNSPEC);
338 | EPSV check_login_epsv SP NUMBER CRLF
352 pf = -1; /*junk value*/
355 long_passive("EPSV", pf);
358 | EPSV check_login_epsv SP ALL CRLF
362 "EPSV ALL command successful.");
366 | EPSV check_login_epsv CRLF
369 long_passive("EPSV", PF_UNSPEC);
371 | TYPE check_login SP type_code CRLF
377 if (cmd_form == FORM_N) {
378 reply(200, "Type set to A.");
382 reply(504, "Form must be N.");
386 reply(504, "Type E not implemented.");
390 reply(200, "Type set to I.");
396 if (cmd_bytesz == 8) {
398 "Type set to L (byte size 8).");
401 reply(504, "Byte size must be 8.");
402 #else /* NBBY == 8 */
403 UNIMPLEMENTED for NBBY != 8
404 #endif /* NBBY == 8 */
408 | STRU check_login SP struct_code CRLF
414 reply(200, "STRU F ok.");
418 reply(504, "Unimplemented STRU type.");
422 | MODE check_login SP mode_code CRLF
428 reply(200, "MODE S ok.");
432 reply(502, "Unimplemented MODE type.");
436 | ALLO check_login SP NUMBER CRLF
439 reply(202, "ALLO command ignored.");
442 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
445 reply(202, "ALLO command ignored.");
448 | RETR check_login SP pathname CRLF
450 if (noretr || (guest && noguestretr))
451 reply(500, "RETR command is disabled");
452 else if ($2 && $4 != NULL)
453 retrieve((char *) 0, $4);
458 | STOR check_login_ro SP pathname CRLF
460 if ($2 && $4 != NULL)
465 | APPE check_login_ro SP pathname CRLF
467 if ($2 && $4 != NULL)
472 | NLST check_login CRLF
477 | NLST check_login SP pathstring CRLF
483 | LIST check_login CRLF
486 retrieve(_PATH_LS " -lgA", "");
488 | LIST check_login SP pathstring CRLF
491 retrieve(_PATH_LS " -lgA %s", $4);
494 | STAT check_login SP pathname CRLF
496 if ($2 && $4 != NULL)
501 | STAT check_login CRLF
507 | DELE check_login_ro SP pathname CRLF
509 if ($2 && $4 != NULL)
514 | RNTO check_login_ro SP pathname CRLF
516 if ($2 && $4 != NULL) {
518 renamecmd(fromname, $4);
520 fromname = (char *) 0;
522 reply(503, "Bad sequence of commands.");
528 | ABOR check_login CRLF
531 reply(225, "ABOR command successful.");
533 | CWD check_login CRLF
539 | CWD check_login SP pathname CRLF
541 if ($2 && $4 != NULL)
548 help(cmdtab, (char *) 0);
550 | HELP SP STRING CRLF
554 if (strncasecmp(cp, "SITE", 4) == 0) {
561 help(sitetab, (char *) 0);
568 reply(200, "NOOP command successful.");
570 | MKD check_login_ro SP pathname CRLF
572 if ($2 && $4 != NULL)
577 | RMD check_login_ro SP pathname CRLF
579 if ($2 && $4 != NULL)
584 | PWD check_login CRLF
589 | CDUP check_login CRLF
596 help(sitetab, (char *) 0);
598 | SITE SP HELP SP STRING CRLF
603 | SITE SP MDFIVE check_login SP pathname CRLF
610 reply(200, "MD5(%s) = %s", $6, p);
612 perror_reply(550, $6);
617 | SITE SP UMASK check_login CRLF
623 (void) umask(oldmask);
624 reply(200, "Current UMASK is %03o", oldmask);
627 | SITE SP UMASK check_login SP octal_number CRLF
632 if (($6 == -1) || ($6 > 0777)) {
633 reply(501, "Bad UMASK value");
637 "UMASK set to %03o (was %03o)",
642 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
644 if ($4 && ($8 != NULL)) {
645 if (($6 == -1 ) || ($6 > 0777))
646 reply(501, "Bad mode value");
647 else if (chmod($8, $6) < 0)
648 perror_reply(550, $8);
650 reply(200, "CHMOD command successful.");
655 | SITE SP check_login IDLE CRLF
659 "Current IDLE time limit is %d seconds; max %d",
660 timeout, maxtimeout);
662 | SITE SP check_login IDLE SP NUMBER CRLF
665 if ($6.i < 30 || $6.i > maxtimeout) {
667 "Maximum IDLE time must be between 30 and %d seconds",
671 (void) alarm((unsigned) timeout);
673 "Maximum IDLE time set to %d seconds",
678 | STOU check_login_ro SP pathname CRLF
680 if ($2 && $4 != NULL)
685 | SYST check_login CRLF
690 reply(215, "UNIX Type: L%d Version: BSD-%d",
693 reply(215, "UNIX Type: L%d", NBBY);
696 reply(215, "UNKNOWN Type: L%d", NBBY);
701 * SIZE is not in RFC959, but Postel has blessed it and
702 * it will be in the updated RFC.
704 * Return size of file in a format suitable for
705 * using with RESTART (we just count bytes).
707 | SIZE check_login SP pathname CRLF
709 if ($2 && $4 != NULL)
716 * MDTM is not in RFC959, but Postel has blessed it and
717 * it will be in the updated RFC.
719 * Return modification time of file as an ISO 3307
720 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
721 * where xxx is the fractional second (of any precision,
722 * not necessarily 3 digits)
724 | MDTM check_login SP pathname CRLF
726 if ($2 && $4 != NULL) {
728 if (stat($4, &stbuf) < 0)
730 $4, strerror(errno));
731 else if (!S_ISREG(stbuf.st_mode)) {
732 reply(550, "%s: not a plain file.", $4);
735 t = gmtime(&stbuf.st_mtime);
737 "%04d%02d%02d%02d%02d%02d",
739 t->tm_mon+1, t->tm_mday,
740 t->tm_hour, t->tm_min, t->tm_sec);
748 reply(221, "Goodbye.");
757 yyclearin; /* discard lookahead data */
758 yyerrok; /* clear error condition */
759 state = CMD; /* reset lexer state */
763 : RNFR check_login_ro SP pathname CRLF
765 restart_point = (off_t) 0;
769 fromname = (char *) 0;
778 | REST check_login SP NUMBER CRLF
783 fromname = (char *) 0;
784 restart_point = $4.o;
785 reply(350, "Restarting at %llu. %s",
787 "Send STORE or RETRIEVE to initiate transfer.");
799 $$ = (char *)calloc(1, sizeof(char));
812 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
817 data_dest.su_len = sizeof(struct sockaddr_in);
818 data_dest.su_family = AF_INET;
819 p = (char *)&data_dest.su_sin.sin_port;
820 p[0] = $9.i; p[1] = $11.i;
821 a = (char *)&data_dest.su_sin.sin_addr;
822 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
827 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
828 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
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
836 memset(&data_dest, 0, sizeof(data_dest));
837 data_dest.su_len = sizeof(struct sockaddr_in6);
838 data_dest.su_family = AF_INET6;
839 p = (char *)&data_dest.su_port;
840 p[0] = $39.i; p[1] = $41.i;
841 a = (char *)&data_dest.su_sin6.sin6_addr;
842 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
843 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
844 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
845 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
846 if (his_addr.su_family == AF_INET6) {
847 /* XXX more sanity checks! */
848 data_dest.su_sin6.sin6_scope_id =
849 his_addr.su_sin6.sin6_scope_id;
851 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
852 memset(&data_dest, 0, sizeof(data_dest));
854 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
855 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
860 memset(&data_dest, 0, sizeof(data_dest));
861 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
862 data_dest.su_family = AF_INET;
863 p = (char *)&data_dest.su_port;
864 p[0] = $15.i; p[1] = $17.i;
865 a = (char *)&data_dest.su_sin.sin_addr;
866 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
867 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
868 memset(&data_dest, 0, sizeof(data_dest));
922 /* this is for a bug in the BBN ftp */
964 * Problem: this production is used for all pathname
965 * processing, but only gives a 550 error reply.
966 * This is a valid reply in some cases but not in others.
968 if (logged_in && $1) {
972 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
975 memset(&gl, 0, sizeof(gl));
977 gl.gl_matchc = MAXGLOBARGS;
978 if (glob($1, flags, NULL, &gl) ||
980 reply(550, "wildcard expansion error");
984 for (pp = gl.gl_pathv; *pp; pp++)
985 if (strcspn(*pp, "\r\n") ==
995 reply(550, "ambiguous");
1013 int ret, dec, multby, digit;
1016 * Convert a number that was read as decimal number
1017 * to what it would be if it had been read as octal.
1028 ret += digit * multby;
1040 $$ = check_login1();
1048 reply(500, "EPSV command disabled");
1052 $$ = check_login1();
1060 reply(550, "Permission denied.");
1064 $$ = check_login1();
1070 #define CMD 0 /* beginning of command */
1071 #define ARGS 1 /* expect miscellaneous arguments */
1072 #define STR1 2 /* expect SP followed by STRING */
1073 #define STR2 3 /* expect STRING */
1074 #define OSTR 4 /* optional SP then STRING */
1075 #define ZSTR1 5 /* optional SP then optional STRING */
1076 #define ZSTR2 6 /* optional STRING after SP */
1077 #define SITECMD 7 /* SITE command */
1078 #define NSTR 8 /* Number followed by a string */
1080 #define MAXGLOBARGS 1000
1082 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1088 short implemented; /* 1 if command is implemented */
1092 struct tab cmdtab[] = { /* In order defined in RFC 765 */
1093 { "USER", USER, STR1, 1, "<sp> username" },
1094 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1095 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1096 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1097 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1098 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1099 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1100 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1101 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1102 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1103 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1104 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1105 { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" },
1106 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1107 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1108 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1109 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1110 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1111 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1112 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1113 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1114 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1115 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1116 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1117 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1118 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1119 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1120 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1121 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1122 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1123 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1124 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1125 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1126 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1127 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1128 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1129 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1130 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1131 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1132 { "NOOP", NOOP, ARGS, 1, "" },
1133 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1134 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1135 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1136 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1137 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1138 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1139 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1140 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1141 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1142 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1143 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1144 { NULL, 0, 0, 0, 0 }
1147 struct tab sitetab[] = {
1148 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1149 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1150 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1151 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1152 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1153 { NULL, 0, 0, 0, 0 }
1156 static char *copy (char *);
1157 static void help (struct tab *, char *);
1159 lookup (struct tab *, char *);
1160 static int port_check (const char *);
1161 static int port_check_v6 (const char *);
1162 static void sizecmd (char *);
1163 static void toolong (int);
1164 static void v4map_data_dest (void);
1165 static int yylex (void);
1173 for (; p->name != NULL; p++)
1174 if (strcmp(cmd, p->name) == 0)
1179 #include <arpa/telnet.h>
1182 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1194 /* tmpline may contain saved command from urgent mode interruption */
1195 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1197 if (tmpline[c] == '\n') {
1200 syslog(LOG_DEBUG, "command: %s", s);
1207 while ((c = getc(iop)) != EOF) {
1210 if ((c = getc(iop)) != EOF) {
1216 printf("%c%c%c", IAC, DONT, 0377&c);
1217 (void) fflush(stdout);
1222 printf("%c%c%c", IAC, WONT, 0377&c);
1223 (void) fflush(stdout);
1228 continue; /* ignore command */
1233 if (--n <= 0 || c == '\n')
1236 if (c == EOF && cs == s)
1240 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1241 /* Don't syslog passwords */
1242 syslog(LOG_DEBUG, "command: %.5s ???", s);
1247 /* Don't syslog trailing CR-LF */
1250 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1254 syslog(LOG_DEBUG, "command: %.*s", len, s);
1266 "Timeout (%d seconds): closing control connection.", timeout);
1268 syslog(LOG_INFO, "User %s timed out after %d seconds",
1269 (pw ? pw -> pw_name : "unknown"), timeout);
1286 (void) signal(SIGALRM, toolong);
1287 (void) alarm((unsigned) timeout);
1288 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1289 reply(221, "You could at least say goodbye.");
1294 if (strncasecmp(cbuf, "PASS", 4) != 0)
1295 setproctitle("%s: %s", proctitle, cbuf);
1296 #endif /* SETPROCTITLE */
1297 if ((cp = strchr(cbuf, '\r'))) {
1301 if ((cp = strpbrk(cbuf, " \n")))
1308 p = lookup(cmdtab, cbuf);
1312 if (!p->implemented)
1313 return (NOTIMPL); /* state remains CMD */
1320 if (cbuf[cpos] == ' ') {
1325 if ((cp2 = strpbrk(cp, " \n")))
1330 p = lookup(sitetab, cp);
1332 if (guest == 0 && p != 0) {
1334 if (!p->implemented) {
1346 if (cbuf[cpos] == '\n') {
1354 if (cbuf[cpos] == ' ') {
1356 state = state == OSTR ? STR2 : state+1;
1362 if (cbuf[cpos] == '\n') {
1373 * Make sure the string is nonempty and \n terminated.
1375 if (n > 1 && cbuf[cpos] == '\n') {
1377 yylval.s = copy(cp);
1385 if (cbuf[cpos] == ' ') {
1389 if (isdigit(cbuf[cpos])) {
1391 while (isdigit(cbuf[++cpos]))
1395 yylval.u.i = atoi(cp);
1404 if (isdigit(cbuf[cpos])) {
1406 while (isdigit(cbuf[++cpos]))
1410 yylval.u.i = atoi(cp);
1411 yylval.u.o = strtoull(cp, (char **)NULL, 10);
1415 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1416 && !isalnum(cbuf[cpos + 3])) {
1420 switch (cbuf[cpos++]) {
1484 fatalerror("Unknown state in scanner.");
1495 while (*s != '\0') {
1508 p = malloc((unsigned) strlen(s) + 1);
1510 fatalerror("Ran out of memory.");
1511 (void) strcpy(p, s);
1524 if (ctab == sitetab)
1528 width = 0, NCMDS = 0;
1529 for (c = ctab; c->name != NULL; c++) {
1530 int len = strlen(c->name);
1536 width = (width + 8) &~ 7;
1541 lreply(214, "The following %scommands are recognized %s.",
1542 type, "(* =>'s unimplemented)");
1543 columns = 76 / width;
1546 lines = (NCMDS + columns - 1) / columns;
1547 for (i = 0; i < lines; i++) {
1549 for (j = 0; j < columns; j++) {
1550 c = ctab + j * lines + i;
1551 printf("%s%c", c->name,
1552 c->implemented ? ' ' : '*');
1553 if (c + lines >= &ctab[NCMDS])
1555 w = strlen(c->name) + 1;
1563 (void) fflush(stdout);
1565 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1571 c = lookup(ctab, s);
1572 if (c == (struct tab *)0) {
1573 reply(502, "Unknown command %s.", s);
1577 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1579 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1591 if (stat(filename, &stbuf) < 0)
1592 perror_reply(550, filename);
1593 else if (!S_ISREG(stbuf.st_mode))
1594 reply(550, "%s: not a plain file.", filename);
1596 reply(213, "%qu", stbuf.st_size);
1603 fin = fopen(filename, "r");
1605 perror_reply(550, filename);
1608 if (fstat(fileno(fin), &stbuf) < 0) {
1609 perror_reply(550, filename);
1612 } else if (!S_ISREG(stbuf.st_mode)) {
1613 reply(550, "%s: not a plain file.", filename);
1616 } else if (stbuf.st_size > MAXASIZE) {
1617 reply(550, "%s: too large for type A SIZE.", filename);
1623 while((c=getc(fin)) != EOF) {
1624 if (c == '\n') /* will get expanded to \r\n */
1630 reply(213, "%qd", count);
1633 reply(504, "SIZE not implemented for type %s.",
1638 /* Return 1, if port check is done. Return 0, if not yet. */
1643 if (his_addr.su_family == AF_INET) {
1644 if (data_dest.su_family != AF_INET) {
1646 reply(500, "Invalid address rejected.");
1650 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1651 memcmp(&data_dest.su_sin.sin_addr,
1652 &his_addr.su_sin.sin_addr,
1653 sizeof(data_dest.su_sin.sin_addr)))) {
1655 reply(500, "Illegal PORT range rejected.");
1659 (void) close(pdata);
1662 reply(200, "%s command successful.", pcmd);
1675 reply(530, "Please login with USER and PASS.");
1681 /* Return 1, if port check is done. Return 0, if not yet. */
1686 if (his_addr.su_family == AF_INET6) {
1687 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1688 /* Convert data_dest into v4 mapped sockaddr.*/
1690 if (data_dest.su_family != AF_INET6) {
1692 reply(500, "Invalid address rejected.");
1696 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1697 memcmp(&data_dest.su_sin6.sin6_addr,
1698 &his_addr.su_sin6.sin6_addr,
1699 sizeof(data_dest.su_sin6.sin6_addr)))) {
1701 reply(500, "Illegal PORT range rejected.");
1705 (void) close(pdata);
1708 reply(200, "%s command successful.", pcmd);
1718 struct in_addr savedaddr;
1721 if (data_dest.su_family != AF_INET) {
1723 reply(500, "Invalid address rejected.");
1727 savedaddr = data_dest.su_sin.sin_addr;
1728 savedport = data_dest.su_port;
1730 memset(&data_dest, 0, sizeof(data_dest));
1731 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1732 data_dest.su_sin6.sin6_family = AF_INET6;
1733 data_dest.su_sin6.sin6_port = savedport;
1734 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1735 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1736 (caddr_t)&savedaddr, sizeof(savedaddr));