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
37 * Grammar for FTP commands.
45 static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
47 static const char rcsid[] =
48 "$FreeBSD: src/libexec/ftpd/ftpcmd.y,v 1.16.2.19 2003/02/11 14:28:28 yar Exp $";
51 #include <sys/param.h>
52 #include <sys/socket.h>
55 #include <netinet/in.h>
75 #include "pathnames.h"
77 extern union sockunion data_dest, his_addr;
80 extern struct passwd *pw;
89 extern int maxtimeout;
91 extern char *hostname;
92 extern char remotehost[];
93 extern char proctitle[];
94 extern int usedefault;
96 extern char tmpline[];
100 extern int noguestretr;
101 extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
107 static int cmd_bytesz;
110 char *fromname = (char *) 0;
131 USER PASS ACCT REIN QUIT PORT
132 PASV TYPE STRU MODE RETR STOR
133 APPE MLFL MAIL MSND MSOM MSAM
134 MRSQ MRCP ALLO REST RNFR RNTO
135 ABOR DELE CWD LIST NLST SITE
136 STAT HELP NOOP MKD RMD PWD
137 CDUP STOU SMNT SYST SIZE MDTM
140 UMASK IDLE CHMOD MDFIVE
147 %type <u.i> check_login octal_number byte_size
148 %type <u.i> check_login_ro check_login_epsv
149 %type <u.i> struct_code mode_code type_code form_code
150 %type <s> pathstring pathname password username
151 %type <s> ALL NOTIMPL
163 fromname = (char *) 0;
164 restart_point = (off_t) 0;
170 : USER SP username CRLF
175 | PASS SP password CRLF
184 | PORT check_login SP host_port CRLF
187 reply(501, "no PORT allowed after EPSV ALL");
192 if (port_check("PORT") == 1)
195 if ((his_addr.su_family != AF_INET6 ||
196 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
197 /* shoud never happen */
199 reply(500, "Invalid address rejected.");
202 port_check_v6("pcmd");
206 | LPRT check_login SP host_long_port CRLF
209 reply(501, "no LPRT allowed after EPSV ALL");
214 if (port_check("LPRT") == 1)
217 if (his_addr.su_family != AF_INET6) {
219 reply(500, "Invalid address rejected.");
222 if (port_check_v6("LPRT") == 1)
227 | EPRT check_login SP STRING CRLF
233 struct addrinfo hints;
234 struct addrinfo *res;
238 reply(501, "no EPRT allowed after EPSV ALL");
244 memset(&data_dest, 0, sizeof(data_dest));
247 syslog(LOG_DEBUG, "%s", tmp);
249 fatalerror("not enough core");
255 memset(result, 0, sizeof(result));
256 for (i = 0; i < 3; i++) {
257 q = strchr(p, delim);
258 if (!q || *q != delim) {
261 "Invalid argument, rejected.");
270 syslog(LOG_DEBUG, "%d: %s", i, p);
274 /* some more sanity check */
289 memset(&hints, 0, sizeof(hints));
290 if (atoi(result[0]) == 1)
291 hints.ai_family = PF_INET;
293 else if (atoi(result[0]) == 2)
294 hints.ai_family = PF_INET6;
297 hints.ai_family = PF_UNSPEC; /*XXX*/
298 hints.ai_socktype = SOCK_STREAM;
299 i = getaddrinfo(result[1], result[2], &hints, &res);
302 memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
304 if (his_addr.su_family == AF_INET6
305 && data_dest.su_family == AF_INET6) {
306 /* XXX more sanity checks! */
307 data_dest.su_sin6.sin6_scope_id =
308 his_addr.su_sin6.sin6_scope_id;
314 if (port_check("EPRT") == 1)
317 if (his_addr.su_family != AF_INET6) {
319 reply(500, "Invalid address rejected.");
322 if (port_check_v6("EPRT") == 1)
328 | PASV check_login CRLF
331 reply(501, "no PASV allowed after EPSV ALL");
335 | LPSV check_login CRLF
338 reply(501, "no LPSV allowed after EPSV ALL");
340 long_passive("LPSV", PF_UNSPEC);
342 | EPSV check_login_epsv SP NUMBER CRLF
356 pf = -1; /*junk value*/
359 long_passive("EPSV", pf);
362 | EPSV check_login_epsv SP ALL CRLF
366 "EPSV ALL command successful.");
370 | EPSV check_login_epsv CRLF
373 long_passive("EPSV", PF_UNSPEC);
375 | TYPE check_login SP type_code CRLF
381 if (cmd_form == FORM_N) {
382 reply(200, "Type set to A.");
386 reply(504, "Form must be N.");
390 reply(504, "Type E not implemented.");
394 reply(200, "Type set to I.");
400 if (cmd_bytesz == 8) {
402 "Type set to L (byte size 8).");
405 reply(504, "Byte size must be 8.");
406 #else /* NBBY == 8 */
407 UNIMPLEMENTED for NBBY != 8
408 #endif /* NBBY == 8 */
412 | STRU check_login SP struct_code CRLF
418 reply(200, "STRU F ok.");
422 reply(504, "Unimplemented STRU type.");
426 | MODE check_login SP mode_code CRLF
432 reply(200, "MODE S ok.");
436 reply(502, "Unimplemented MODE type.");
440 | ALLO check_login SP NUMBER CRLF
443 reply(202, "ALLO command ignored.");
446 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
449 reply(202, "ALLO command ignored.");
452 | RETR check_login SP pathname CRLF
454 if (noretr || (guest && noguestretr))
455 reply(500, "RETR command is disabled");
456 else if ($2 && $4 != NULL)
457 retrieve((char *) 0, $4);
462 | STOR check_login_ro SP pathname CRLF
464 if ($2 && $4 != NULL)
469 | APPE check_login_ro SP pathname CRLF
471 if ($2 && $4 != NULL)
476 | NLST check_login CRLF
481 | NLST check_login SP pathstring CRLF
487 | LIST check_login CRLF
490 retrieve(_PATH_LS " -lgA", "");
492 | LIST check_login SP pathstring CRLF
495 retrieve(_PATH_LS " -lgA %s", $4);
498 | STAT check_login SP pathname CRLF
500 if ($2 && $4 != NULL)
505 | STAT check_login CRLF
511 | DELE check_login_ro SP pathname CRLF
513 if ($2 && $4 != NULL)
518 | RNTO check_login_ro SP pathname CRLF
520 if ($2 && $4 != NULL) {
522 renamecmd(fromname, $4);
524 fromname = (char *) 0;
526 reply(503, "Bad sequence of commands.");
532 | ABOR check_login CRLF
535 reply(225, "ABOR command successful.");
537 | CWD check_login CRLF
543 | CWD check_login SP pathname CRLF
545 if ($2 && $4 != NULL)
552 help(cmdtab, (char *) 0);
554 | HELP SP STRING CRLF
558 if (strncasecmp(cp, "SITE", 4) == 0) {
565 help(sitetab, (char *) 0);
572 reply(200, "NOOP command successful.");
574 | MKD check_login_ro SP pathname CRLF
576 if ($2 && $4 != NULL)
581 | RMD check_login_ro SP pathname CRLF
583 if ($2 && $4 != NULL)
588 | PWD check_login CRLF
593 | CDUP check_login CRLF
600 help(sitetab, (char *) 0);
602 | SITE SP HELP SP STRING CRLF
607 | SITE SP MDFIVE check_login SP pathname CRLF
614 reply(200, "MD5(%s) = %s", $6, p);
616 perror_reply(550, $6);
621 | SITE SP UMASK check_login CRLF
627 (void) umask(oldmask);
628 reply(200, "Current UMASK is %03o", oldmask);
631 | SITE SP UMASK check_login SP octal_number CRLF
636 if (($6 == -1) || ($6 > 0777)) {
637 reply(501, "Bad UMASK value");
641 "UMASK set to %03o (was %03o)",
646 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
648 if ($4 && ($8 != NULL)) {
649 if (($6 == -1 ) || ($6 > 0777))
650 reply(501, "Bad mode value");
651 else if (chmod($8, $6) < 0)
652 perror_reply(550, $8);
654 reply(200, "CHMOD command successful.");
659 | SITE SP check_login IDLE CRLF
663 "Current IDLE time limit is %d seconds; max %d",
664 timeout, maxtimeout);
666 | SITE SP check_login IDLE SP NUMBER CRLF
669 if ($6.i < 30 || $6.i > maxtimeout) {
671 "Maximum IDLE time must be between 30 and %d seconds",
675 (void) alarm((unsigned) timeout);
677 "Maximum IDLE time set to %d seconds",
682 | STOU check_login_ro SP pathname CRLF
684 if ($2 && $4 != NULL)
689 | SYST check_login CRLF
694 reply(215, "UNIX Type: L%d Version: BSD-%d",
697 reply(215, "UNIX Type: L%d", NBBY);
700 reply(215, "UNKNOWN Type: L%d", NBBY);
705 * SIZE is not in RFC959, but Postel has blessed it and
706 * it will be in the updated RFC.
708 * Return size of file in a format suitable for
709 * using with RESTART (we just count bytes).
711 | SIZE check_login SP pathname CRLF
713 if ($2 && $4 != NULL)
720 * MDTM is not in RFC959, but Postel has blessed it and
721 * it will be in the updated RFC.
723 * Return modification time of file as an ISO 3307
724 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
725 * where xxx is the fractional second (of any precision,
726 * not necessarily 3 digits)
728 | MDTM check_login SP pathname CRLF
730 if ($2 && $4 != NULL) {
732 if (stat($4, &stbuf) < 0)
734 $4, strerror(errno));
735 else if (!S_ISREG(stbuf.st_mode)) {
736 reply(550, "%s: not a plain file.", $4);
739 t = gmtime(&stbuf.st_mtime);
741 "%04d%02d%02d%02d%02d%02d",
743 t->tm_mon+1, t->tm_mday,
744 t->tm_hour, t->tm_min, t->tm_sec);
752 reply(221, "Goodbye.");
761 yyclearin; /* discard lookahead data */
762 yyerrok; /* clear error condition */
763 state = CMD; /* reset lexer state */
767 : RNFR check_login_ro SP pathname CRLF
769 restart_point = (off_t) 0;
773 fromname = (char *) 0;
782 | REST check_login SP NUMBER CRLF
787 fromname = (char *) 0;
788 restart_point = $4.o;
789 reply(350, "Restarting at %llu. %s",
791 "Send STORE or RETRIEVE to initiate transfer.");
803 $$ = (char *)calloc(1, sizeof(char));
816 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
821 data_dest.su_len = sizeof(struct sockaddr_in);
822 data_dest.su_family = AF_INET;
823 p = (char *)&data_dest.su_sin.sin_port;
824 p[0] = $9.i; p[1] = $11.i;
825 a = (char *)&data_dest.su_sin.sin_addr;
826 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
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 COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
835 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
840 memset(&data_dest, 0, sizeof(data_dest));
841 data_dest.su_len = sizeof(struct sockaddr_in6);
842 data_dest.su_family = AF_INET6;
843 p = (char *)&data_dest.su_port;
844 p[0] = $39.i; p[1] = $41.i;
845 a = (char *)&data_dest.su_sin6.sin6_addr;
846 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
847 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
848 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
849 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
850 if (his_addr.su_family == AF_INET6) {
851 /* XXX more sanity checks! */
852 data_dest.su_sin6.sin6_scope_id =
853 his_addr.su_sin6.sin6_scope_id;
855 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
856 memset(&data_dest, 0, sizeof(data_dest));
858 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
859 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
864 memset(&data_dest, 0, sizeof(data_dest));
865 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
866 data_dest.su_family = AF_INET;
867 p = (char *)&data_dest.su_port;
868 p[0] = $15.i; p[1] = $17.i;
869 a = (char *)&data_dest.su_sin.sin_addr;
870 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
871 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
872 memset(&data_dest, 0, sizeof(data_dest));
926 /* this is for a bug in the BBN ftp */
968 * Problem: this production is used for all pathname
969 * processing, but only gives a 550 error reply.
970 * This is a valid reply in some cases but not in others.
972 if (logged_in && $1) {
976 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
979 memset(&gl, 0, sizeof(gl));
981 gl.gl_matchc = MAXGLOBARGS;
982 if (glob($1, flags, NULL, &gl) ||
984 reply(550, "wildcard expansion error");
988 for (pp = gl.gl_pathv; *pp; pp++)
989 if (strcspn(*pp, "\r\n") ==
999 reply(550, "ambiguous");
1017 int ret, dec, multby, digit;
1020 * Convert a number that was read as decimal number
1021 * to what it would be if it had been read as octal.
1032 ret += digit * multby;
1044 $$ = check_login1();
1052 reply(500, "EPSV command disabled");
1056 $$ = check_login1();
1064 reply(550, "Permission denied.");
1068 $$ = check_login1();
1074 #define CMD 0 /* beginning of command */
1075 #define ARGS 1 /* expect miscellaneous arguments */
1076 #define STR1 2 /* expect SP followed by STRING */
1077 #define STR2 3 /* expect STRING */
1078 #define OSTR 4 /* optional SP then STRING */
1079 #define ZSTR1 5 /* optional SP then optional STRING */
1080 #define ZSTR2 6 /* optional STRING after SP */
1081 #define SITECMD 7 /* SITE command */
1082 #define NSTR 8 /* Number followed by a string */
1084 #define MAXGLOBARGS 1000
1086 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1092 short implemented; /* 1 if command is implemented */
1096 struct tab cmdtab[] = { /* In order defined in RFC 765 */
1097 { "USER", USER, STR1, 1, "<sp> username" },
1098 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1099 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1100 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1101 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1102 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1103 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1104 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1105 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1106 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1107 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1108 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1109 { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" },
1110 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1111 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1112 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1113 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1114 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1115 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1116 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1117 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1118 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1119 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1120 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1121 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1122 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1123 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1124 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1125 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1126 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1127 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1128 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1129 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1130 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1131 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1132 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1133 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1134 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1135 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1136 { "NOOP", NOOP, ARGS, 1, "" },
1137 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1138 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1139 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1140 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1141 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1142 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1143 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1144 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1145 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1146 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1147 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1148 { NULL, 0, 0, 0, 0 }
1151 struct tab sitetab[] = {
1152 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1153 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1154 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1155 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1156 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1157 { NULL, 0, 0, 0, 0 }
1160 static char *copy __P((char *));
1161 static void help __P((struct tab *, char *));
1163 lookup __P((struct tab *, char *));
1164 static int port_check __P((const char *));
1165 static int port_check_v6 __P((const char *));
1166 static void sizecmd __P((char *));
1167 static void toolong __P((int));
1168 static void v4map_data_dest __P((void));
1169 static int yylex __P((void));
1177 for (; p->name != NULL; p++)
1178 if (strcmp(cmd, p->name) == 0)
1183 #include <arpa/telnet.h>
1186 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1198 /* tmpline may contain saved command from urgent mode interruption */
1199 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1201 if (tmpline[c] == '\n') {
1204 syslog(LOG_DEBUG, "command: %s", s);
1211 while ((c = getc(iop)) != EOF) {
1214 if ((c = getc(iop)) != EOF) {
1220 printf("%c%c%c", IAC, DONT, 0377&c);
1221 (void) fflush(stdout);
1226 printf("%c%c%c", IAC, WONT, 0377&c);
1227 (void) fflush(stdout);
1232 continue; /* ignore command */
1237 if (--n <= 0 || c == '\n')
1240 if (c == EOF && cs == s)
1244 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1245 /* Don't syslog passwords */
1246 syslog(LOG_DEBUG, "command: %.5s ???", s);
1251 /* Don't syslog trailing CR-LF */
1254 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1258 syslog(LOG_DEBUG, "command: %.*s", len, s);
1270 "Timeout (%d seconds): closing control connection.", timeout);
1272 syslog(LOG_INFO, "User %s timed out after %d seconds",
1273 (pw ? pw -> pw_name : "unknown"), timeout);
1290 (void) signal(SIGALRM, toolong);
1291 (void) alarm((unsigned) timeout);
1292 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1293 reply(221, "You could at least say goodbye.");
1298 if (strncasecmp(cbuf, "PASS", 4) != 0)
1299 setproctitle("%s: %s", proctitle, cbuf);
1300 #endif /* SETPROCTITLE */
1301 if ((cp = strchr(cbuf, '\r'))) {
1305 if ((cp = strpbrk(cbuf, " \n")))
1312 p = lookup(cmdtab, cbuf);
1316 if (!p->implemented)
1317 return (NOTIMPL); /* state remains CMD */
1324 if (cbuf[cpos] == ' ') {
1329 if ((cp2 = strpbrk(cp, " \n")))
1334 p = lookup(sitetab, cp);
1336 if (guest == 0 && p != 0) {
1338 if (!p->implemented) {
1350 if (cbuf[cpos] == '\n') {
1358 if (cbuf[cpos] == ' ') {
1360 state = state == OSTR ? STR2 : state+1;
1366 if (cbuf[cpos] == '\n') {
1377 * Make sure the string is nonempty and \n terminated.
1379 if (n > 1 && cbuf[cpos] == '\n') {
1381 yylval.s = copy(cp);
1389 if (cbuf[cpos] == ' ') {
1393 if (isdigit(cbuf[cpos])) {
1395 while (isdigit(cbuf[++cpos]))
1399 yylval.u.i = atoi(cp);
1408 if (isdigit(cbuf[cpos])) {
1410 while (isdigit(cbuf[++cpos]))
1414 yylval.u.i = atoi(cp);
1415 yylval.u.o = strtoull(cp, (char **)NULL, 10);
1419 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1420 && !isalnum(cbuf[cpos + 3])) {
1424 switch (cbuf[cpos++]) {
1488 fatalerror("Unknown state in scanner.");
1499 while (*s != '\0') {
1512 p = malloc((unsigned) strlen(s) + 1);
1514 fatalerror("Ran out of memory.");
1515 (void) strcpy(p, s);
1528 if (ctab == sitetab)
1532 width = 0, NCMDS = 0;
1533 for (c = ctab; c->name != NULL; c++) {
1534 int len = strlen(c->name);
1540 width = (width + 8) &~ 7;
1545 lreply(214, "The following %scommands are recognized %s.",
1546 type, "(* =>'s unimplemented)");
1547 columns = 76 / width;
1550 lines = (NCMDS + columns - 1) / columns;
1551 for (i = 0; i < lines; i++) {
1553 for (j = 0; j < columns; j++) {
1554 c = ctab + j * lines + i;
1555 printf("%s%c", c->name,
1556 c->implemented ? ' ' : '*');
1557 if (c + lines >= &ctab[NCMDS])
1559 w = strlen(c->name) + 1;
1567 (void) fflush(stdout);
1569 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1575 c = lookup(ctab, s);
1576 if (c == (struct tab *)0) {
1577 reply(502, "Unknown command %s.", s);
1581 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1583 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1595 if (stat(filename, &stbuf) < 0)
1596 perror_reply(550, filename);
1597 else if (!S_ISREG(stbuf.st_mode))
1598 reply(550, "%s: not a plain file.", filename);
1600 reply(213, "%qu", stbuf.st_size);
1607 fin = fopen(filename, "r");
1609 perror_reply(550, filename);
1612 if (fstat(fileno(fin), &stbuf) < 0) {
1613 perror_reply(550, filename);
1616 } else if (!S_ISREG(stbuf.st_mode)) {
1617 reply(550, "%s: not a plain file.", filename);
1620 } else if (stbuf.st_size > MAXASIZE) {
1621 reply(550, "%s: too large for type A SIZE.", filename);
1627 while((c=getc(fin)) != EOF) {
1628 if (c == '\n') /* will get expanded to \r\n */
1634 reply(213, "%qd", count);
1637 reply(504, "SIZE not implemented for type %s.",
1642 /* Return 1, if port check is done. Return 0, if not yet. */
1647 if (his_addr.su_family == AF_INET) {
1648 if (data_dest.su_family != AF_INET) {
1650 reply(500, "Invalid address rejected.");
1654 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1655 memcmp(&data_dest.su_sin.sin_addr,
1656 &his_addr.su_sin.sin_addr,
1657 sizeof(data_dest.su_sin.sin_addr)))) {
1659 reply(500, "Illegal PORT range rejected.");
1663 (void) close(pdata);
1666 reply(200, "%s command successful.", pcmd);
1679 reply(530, "Please login with USER and PASS.");
1685 /* Return 1, if port check is done. Return 0, if not yet. */
1690 if (his_addr.su_family == AF_INET6) {
1691 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1692 /* Convert data_dest into v4 mapped sockaddr.*/
1694 if (data_dest.su_family != AF_INET6) {
1696 reply(500, "Invalid address rejected.");
1700 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1701 memcmp(&data_dest.su_sin6.sin6_addr,
1702 &his_addr.su_sin6.sin6_addr,
1703 sizeof(data_dest.su_sin6.sin6_addr)))) {
1705 reply(500, "Illegal PORT range rejected.");
1709 (void) close(pdata);
1712 reply(200, "%s command successful.", pcmd);
1722 struct in_addr savedaddr;
1725 if (data_dest.su_family != AF_INET) {
1727 reply(500, "Invalid address rejected.");
1731 savedaddr = data_dest.su_sin.sin_addr;
1732 savedport = data_dest.su_port;
1734 memset(&data_dest, 0, sizeof(data_dest));
1735 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1736 data_dest.su_sin6.sin6_family = AF_INET6;
1737 data_dest.su_sin6.sin6_port = savedport;
1738 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1739 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1740 (caddr_t)&savedaddr, sizeof(savedaddr));