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[] =
51 #include <sys/param.h>
52 #include <sys/socket.h>
55 #include <netinet/in.h>
76 extern union sockunion data_dest, his_addr;
78 extern struct passwd *pw;
86 extern int maxtimeout;
88 extern char *hostname;
89 extern char remotehost[];
90 extern char proctitle[];
91 extern int usedefault;
93 extern char tmpline[];
97 extern int noguestretr;
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
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 STRING CRLF
479 if ($2 && $4 != NULL)
484 | LIST check_login CRLF
487 retrieve("/bin/ls -lgA", "");
489 | LIST check_login SP pathstring CRLF
491 if ($2 && $4 != NULL)
492 retrieve("/bin/ls -lgA %s", $4);
496 | STAT check_login SP pathname CRLF
498 if ($2 && $4 != NULL)
503 | STAT check_login CRLF
509 | DELE check_login_ro SP pathname CRLF
511 if ($2 && $4 != NULL)
516 | RNTO check_login_ro SP pathname CRLF
520 renamecmd(fromname, $4);
522 fromname = (char *) 0;
524 reply(503, "Bad sequence of commands.");
529 | ABOR check_login CRLF
532 reply(225, "ABOR command successful.");
534 | 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)) {
651 "CHMOD: Mode value must be between 0 and 0777");
652 else if (chmod($8, $6) < 0)
653 perror_reply(550, $8);
655 reply(200, "CHMOD command successful.");
660 | SITE SP check_login IDLE CRLF
664 "Current IDLE time limit is %d seconds; max %d",
665 timeout, maxtimeout);
667 | SITE SP check_login IDLE SP NUMBER CRLF
670 if ($6.i < 30 || $6.i > maxtimeout) {
672 "Maximum IDLE time must be between 30 and %d seconds",
676 (void) alarm((unsigned) timeout);
678 "Maximum IDLE time set to %d seconds",
683 | STOU check_login_ro SP pathname CRLF
685 if ($2 && $4 != NULL)
690 | SYST check_login CRLF
695 reply(215, "UNIX Type: L%d Version: BSD-%d",
698 reply(215, "UNIX Type: L%d", NBBY);
701 reply(215, "UNKNOWN Type: L%d", NBBY);
706 * SIZE is not in RFC959, but Postel has blessed it and
707 * it will be in the updated RFC.
709 * Return size of file in a format suitable for
710 * using with RESTART (we just count bytes).
712 | SIZE check_login SP pathname CRLF
714 if ($2 && $4 != NULL)
721 * MDTM is not in RFC959, but Postel has blessed it and
722 * it will be in the updated RFC.
724 * Return modification time of file as an ISO 3307
725 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
726 * where xxx is the fractional second (of any precision,
727 * not necessarily 3 digits)
729 | MDTM check_login SP pathname CRLF
731 if ($2 && $4 != NULL) {
733 if (stat($4, &stbuf) < 0)
735 $4, strerror(errno));
736 else if (!S_ISREG(stbuf.st_mode)) {
737 reply(550, "%s: not a plain file.", $4);
740 t = gmtime(&stbuf.st_mtime);
742 "%04d%02d%02d%02d%02d%02d",
744 t->tm_mon+1, t->tm_mday,
745 t->tm_hour, t->tm_min, t->tm_sec);
753 reply(221, "Goodbye.");
758 yyclearin; /* discard lookahead data */
759 yyerrok; /* clear error condition */
760 state = 0; /* reset lexer state */
764 : RNFR check_login_ro SP pathname CRLF
766 restart_point = (off_t) 0;
770 fromname = (char *) 0;
779 | REST check_login SP NUMBER CRLF
784 fromname = (char *) 0;
785 restart_point = $4.o;
786 reply(350, "Restarting at %llu. %s",
788 "Send STORE or RETRIEVE to initiate transfer.");
800 $$ = (char *)calloc(1, sizeof(char));
813 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
818 data_dest.su_len = sizeof(struct sockaddr_in);
819 data_dest.su_family = AF_INET;
820 p = (char *)&data_dest.su_sin.sin_port;
821 p[0] = $9.i; p[1] = $11.i;
822 a = (char *)&data_dest.su_sin.sin_addr;
823 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
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
832 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
837 memset(&data_dest, 0, sizeof(data_dest));
838 data_dest.su_len = sizeof(struct sockaddr_in6);
839 data_dest.su_family = AF_INET6;
840 p = (char *)&data_dest.su_port;
841 p[0] = $39.i; p[1] = $41.i;
842 a = (char *)&data_dest.su_sin6.sin6_addr;
843 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
844 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
845 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
846 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
847 if (his_addr.su_family == AF_INET6) {
848 /* XXX more sanity checks! */
849 data_dest.su_sin6.sin6_scope_id =
850 his_addr.su_sin6.sin6_scope_id;
852 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
853 memset(&data_dest, 0, sizeof(data_dest));
855 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
856 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
861 memset(&data_dest, 0, sizeof(data_dest));
862 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
863 data_dest.su_family = AF_INET;
864 p = (char *)&data_dest.su_port;
865 p[0] = $15.i; p[1] = $17.i;
866 a = (char *)&data_dest.su_sin.sin_addr;
867 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
868 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
869 memset(&data_dest, 0, sizeof(data_dest));
923 /* this is for a bug in the BBN ftp */
965 * Problem: this production is used for all pathname
966 * processing, but only gives a 550 error reply.
967 * This is a valid reply in some cases but not in others.
969 if (logged_in && $1) {
972 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
974 memset(&gl, 0, sizeof(gl));
975 flags |= GLOB_MAXPATH;
976 gl.gl_matchc = MAXGLOBARGS;
977 if (glob($1, flags, NULL, &gl) ||
979 reply(550, "not found");
981 } else if (gl.gl_pathc > 1) {
982 reply(550, "ambiguous");
985 $$ = strdup(gl.gl_pathv[0]);
1001 int ret, dec, multby, digit;
1004 * Convert a number that was read as decimal number
1005 * to what it would be if it had been read as octal.
1016 ret += digit * multby;
1028 $$ = check_login1();
1036 reply(500, "EPSV command disabled");
1040 $$ = check_login1();
1048 reply(550, "Permission denied.");
1052 $$ = check_login1();
1058 #define CMD 0 /* beginning of command */
1059 #define ARGS 1 /* expect miscellaneous arguments */
1060 #define STR1 2 /* expect SP followed by STRING */
1061 #define STR2 3 /* expect STRING */
1062 #define OSTR 4 /* optional SP then STRING */
1063 #define ZSTR1 5 /* optional SP then optional STRING */
1064 #define ZSTR2 6 /* optional STRING after SP */
1065 #define SITECMD 7 /* SITE command */
1066 #define NSTR 8 /* Number followed by a string */
1068 #define MAXGLOBARGS 1000
1074 short implemented; /* 1 if command is implemented */
1078 struct tab cmdtab[] = { /* In order defined in RFC 765 */
1079 { "USER", USER, STR1, 1, "<sp> username" },
1080 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1081 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1082 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1083 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1084 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1085 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
1086 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1087 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1088 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1089 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1090 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1091 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1092 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1093 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1094 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1095 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1096 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1097 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1098 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1099 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1100 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1101 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1102 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1103 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1104 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1105 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1106 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1107 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1108 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1109 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1110 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1111 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1112 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1113 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1114 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1115 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1116 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1117 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1118 { "NOOP", NOOP, ARGS, 1, "" },
1119 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1120 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1121 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1122 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1123 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1124 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1125 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1126 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1127 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1128 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1129 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1130 { NULL, 0, 0, 0, 0 }
1133 struct tab sitetab[] = {
1134 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1135 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1136 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1137 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1138 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1139 { NULL, 0, 0, 0, 0 }
1142 static char *copy(char *);
1143 static void help(struct tab *, char *);
1145 lookup(struct tab *, char *);
1146 static int port_check(const char *);
1147 static int port_check_v6(const char *);
1148 static void sizecmd(char *);
1149 static void toolong(int);
1150 static void v4map_data_dest(void);
1151 static int yylex(void);
1154 lookup(struct tab *p, char *cmd)
1157 for (; p->name != NULL; p++)
1158 if (strcmp(cmd, p->name) == 0)
1163 #include <arpa/telnet.h>
1166 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1169 getline(char *s, int n, FILE *iop)
1175 /* tmpline may contain saved command from urgent mode interruption */
1176 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1178 if (tmpline[c] == '\n') {
1181 syslog(LOG_DEBUG, "command: %s", s);
1188 while ((c = getc(iop)) != EOF) {
1191 if ((c = getc(iop)) != EOF) {
1197 printf("%c%c%c", IAC, DONT, 0377&c);
1198 (void) fflush(stdout);
1203 printf("%c%c%c", IAC, WONT, 0377&c);
1204 (void) fflush(stdout);
1209 continue; /* ignore command */
1214 if (--n <= 0 || c == '\n')
1217 if (c == EOF && cs == s)
1221 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1222 /* Don't syslog passwords */
1223 syslog(LOG_DEBUG, "command: %.5s ???", s);
1228 /* Don't syslog trailing CR-LF */
1231 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1235 syslog(LOG_DEBUG, "command: %.*s", len, s);
1246 "Timeout (%d seconds): closing control connection.", timeout);
1248 syslog(LOG_INFO, "User %s timed out after %d seconds",
1249 (pw ? pw -> pw_name : "unknown"), timeout);
1266 (void) signal(SIGALRM, toolong);
1267 (void) alarm((unsigned) timeout);
1268 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1269 reply(221, "You could at least say goodbye.");
1274 if (strncasecmp(cbuf, "PASS", 4) != 0)
1275 setproctitle("%s: %s", proctitle, cbuf);
1276 #endif /* SETPROCTITLE */
1277 if ((cp = strchr(cbuf, '\r'))) {
1281 if ((cp = strpbrk(cbuf, " \n")))
1288 p = lookup(cmdtab, cbuf);
1291 if (p->implemented == 0) {
1302 if (cbuf[cpos] == ' ') {
1307 if ((cp2 = strpbrk(cp, " \n")))
1312 p = lookup(sitetab, cp);
1314 if (guest == 0 && p != 0) {
1315 if (p->implemented == 0) {
1329 if (cbuf[cpos] == '\n') {
1337 if (cbuf[cpos] == ' ') {
1339 state = state == OSTR ? STR2 : state+1;
1345 if (cbuf[cpos] == '\n') {
1356 * Make sure the string is nonempty and \n terminated.
1358 if (n > 1 && cbuf[cpos] == '\n') {
1360 yylval.s = copy(cp);
1368 if (cbuf[cpos] == ' ') {
1372 if (isdigit(cbuf[cpos])) {
1374 while (isdigit(cbuf[++cpos]))
1378 yylval.u.i = atoi(cp);
1387 if (isdigit(cbuf[cpos])) {
1389 while (isdigit(cbuf[++cpos]))
1393 yylval.u.i = atoi(cp);
1394 yylval.u.o = strtoull(cp, (char **)NULL, 10);
1398 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1399 && !isalnum(cbuf[cpos + 3])) {
1403 switch (cbuf[cpos++]) {
1467 fatalerror("Unknown state in scanner.");
1477 while (*s != '\0') {
1489 p = malloc((unsigned) strlen(s) + 1);
1491 fatalerror("Ran out of memory.");
1492 (void) strcpy(p, s);
1497 help(struct tab *ctab, char *s)
1503 if (ctab == sitetab)
1507 width = 0, NCMDS = 0;
1508 for (c = ctab; c->name != NULL; c++) {
1509 int len = strlen(c->name);
1515 width = (width + 8) &~ 7;
1520 lreply(214, "The following %scommands are recognized %s.",
1521 type, "(* =>'s unimplemented)");
1522 columns = 76 / width;
1525 lines = (NCMDS + columns - 1) / columns;
1526 for (i = 0; i < lines; i++) {
1528 for (j = 0; j < columns; j++) {
1529 c = ctab + j * lines + i;
1530 printf("%s%c", c->name,
1531 c->implemented ? ' ' : '*');
1532 if (c + lines >= &ctab[NCMDS])
1534 w = strlen(c->name) + 1;
1542 (void) fflush(stdout);
1543 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1547 c = lookup(ctab, s);
1548 if (c == (struct tab *)0) {
1549 reply(502, "Unknown command %s.", s);
1553 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1555 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1560 sizecmd(char *filename)
1566 if (stat(filename, &stbuf) < 0)
1567 perror_reply(550, filename);
1568 else if (!S_ISREG(stbuf.st_mode))
1569 reply(550, "%s: not a plain file.", filename);
1571 reply(213, "%qu", stbuf.st_size);
1578 fin = fopen(filename, "r");
1580 perror_reply(550, filename);
1583 if (fstat(fileno(fin), &stbuf) < 0) {
1584 perror_reply(550, filename);
1587 } else if (!S_ISREG(stbuf.st_mode)) {
1588 reply(550, "%s: not a plain file.", filename);
1594 while((c=getc(fin)) != EOF) {
1595 if (c == '\n') /* will get expanded to \r\n */
1601 reply(213, "%qd", count);
1604 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1608 /* Return 1, if port check is done. Return 0, if not yet. */
1610 port_check(const char *pcmd)
1612 if (his_addr.su_family == AF_INET) {
1613 if (data_dest.su_family != AF_INET) {
1615 reply(500, "Invalid address rejected.");
1619 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1620 memcmp(&data_dest.su_sin.sin_addr,
1621 &his_addr.su_sin.sin_addr,
1622 sizeof(data_dest.su_sin.sin_addr)))) {
1624 reply(500, "Illegal PORT range rejected.");
1628 (void) close(pdata);
1631 reply(200, "%s command successful.", pcmd);
1644 reply(530, "Please login with USER and PASS.");
1650 /* Return 1, if port check is done. Return 0, if not yet. */
1652 port_check_v6(const char *pcmd)
1654 if (his_addr.su_family == AF_INET6) {
1655 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1656 /* Convert data_dest into v4 mapped sockaddr.*/
1658 if (data_dest.su_family != AF_INET6) {
1660 reply(500, "Invalid address rejected.");
1664 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1665 memcmp(&data_dest.su_sin6.sin6_addr,
1666 &his_addr.su_sin6.sin6_addr,
1667 sizeof(data_dest.su_sin6.sin6_addr)))) {
1669 reply(500, "Illegal PORT range rejected.");
1673 (void) close(pdata);
1676 reply(200, "%s command successful.", pcmd);
1684 v4map_data_dest(void)
1686 struct in_addr savedaddr;
1689 if (data_dest.su_family != AF_INET) {
1691 reply(500, "Invalid address rejected.");
1695 savedaddr = data_dest.su_sin.sin_addr;
1696 savedport = data_dest.su_port;
1698 memset(&data_dest, 0, sizeof(data_dest));
1699 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1700 data_dest.su_sin6.sin6_family = AF_INET6;
1701 data_dest.su_sin6.sin6_port = savedport;
1702 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1703 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1704 (caddr_t)&savedaddr, sizeof(savedaddr));