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;
98 extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
104 static int cmd_bytesz;
107 char *fromname = (char *) 0;
128 USER PASS ACCT REIN QUIT PORT
129 PASV TYPE STRU MODE RETR STOR
130 APPE MLFL MAIL MSND MSOM MSAM
131 MRSQ MRCP ALLO REST RNFR RNTO
132 ABOR DELE CWD LIST NLST SITE
133 STAT HELP NOOP MKD RMD PWD
134 CDUP STOU SMNT SYST SIZE MDTM
137 UMASK IDLE CHMOD MDFIVE
144 %type <u.i> check_login octal_number byte_size
145 %type <u.i> check_login_ro check_login_epsv
146 %type <u.i> struct_code mode_code type_code form_code
147 %type <s> pathstring pathname password username
160 fromname = (char *) 0;
161 restart_point = (off_t) 0;
167 : USER SP username CRLF
172 | PASS SP password CRLF
181 | PORT check_login SP host_port CRLF
184 reply(501, "no PORT allowed after EPSV ALL");
189 if (port_check("PORT") == 1)
192 if ((his_addr.su_family != AF_INET6 ||
193 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
194 /* shoud never happen */
196 reply(500, "Invalid address rejected.");
199 port_check_v6("pcmd");
203 | LPRT check_login SP host_long_port CRLF
206 reply(501, "no LPRT allowed after EPSV ALL");
211 if (port_check("LPRT") == 1)
214 if (his_addr.su_family != AF_INET6) {
216 reply(500, "Invalid address rejected.");
219 if (port_check_v6("LPRT") == 1)
224 | EPRT check_login SP STRING CRLF
230 struct addrinfo hints;
231 struct addrinfo *res;
235 reply(501, "no EPRT allowed after EPSV ALL");
241 memset(&data_dest, 0, sizeof(data_dest));
244 syslog(LOG_DEBUG, "%s", tmp);
246 fatalerror("not enough core");
252 memset(result, 0, sizeof(result));
253 for (i = 0; i < 3; i++) {
254 q = strchr(p, delim);
255 if (!q || *q != delim) {
258 "Invalid argument, rejected.");
267 syslog(LOG_DEBUG, "%d: %s", i, p);
271 /* some more sanity check */
286 memset(&hints, 0, sizeof(hints));
287 if (atoi(result[0]) == 1)
288 hints.ai_family = PF_INET;
290 else if (atoi(result[0]) == 2)
291 hints.ai_family = PF_INET6;
294 hints.ai_family = PF_UNSPEC; /*XXX*/
295 hints.ai_socktype = SOCK_STREAM;
296 i = getaddrinfo(result[1], result[2], &hints, &res);
299 memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
301 if (his_addr.su_family == AF_INET6
302 && data_dest.su_family == AF_INET6) {
303 /* XXX more sanity checks! */
304 data_dest.su_sin6.sin6_scope_id =
305 his_addr.su_sin6.sin6_scope_id;
311 if (port_check("EPRT") == 1)
314 if (his_addr.su_family != AF_INET6) {
316 reply(500, "Invalid address rejected.");
319 if (port_check_v6("EPRT") == 1)
325 | PASV check_login CRLF
328 reply(501, "no PASV allowed after EPSV ALL");
332 | LPSV check_login CRLF
335 reply(501, "no LPSV allowed after EPSV ALL");
337 long_passive("LPSV", PF_UNSPEC);
339 | EPSV check_login_epsv SP NUMBER CRLF
353 pf = -1; /*junk value*/
356 long_passive("EPSV", pf);
359 | EPSV check_login_epsv SP ALL CRLF
363 "EPSV ALL command successful.");
367 | EPSV check_login_epsv CRLF
370 long_passive("EPSV", PF_UNSPEC);
372 | TYPE check_login SP type_code CRLF
378 if (cmd_form == FORM_N) {
379 reply(200, "Type set to A.");
383 reply(504, "Form must be N.");
387 reply(504, "Type E not implemented.");
391 reply(200, "Type set to I.");
397 if (cmd_bytesz == 8) {
399 "Type set to L (byte size 8).");
402 reply(504, "Byte size must be 8.");
403 #else /* NBBY == 8 */
404 UNIMPLEMENTED for NBBY != 8
405 #endif /* NBBY == 8 */
409 | STRU check_login SP struct_code CRLF
415 reply(200, "STRU F ok.");
419 reply(504, "Unimplemented STRU type.");
423 | MODE check_login SP mode_code CRLF
429 reply(200, "MODE S ok.");
433 reply(502, "Unimplemented MODE type.");
437 | ALLO check_login SP NUMBER CRLF
440 reply(202, "ALLO command ignored.");
443 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
446 reply(202, "ALLO command ignored.");
449 | RETR check_login SP pathname CRLF
451 if (noretr || (guest && noguestretr))
452 reply(500, "RETR command is disabled");
453 else if ($2 && $4 != NULL)
454 retrieve((char *) 0, $4);
459 | STOR check_login_ro SP pathname CRLF
461 if ($2 && $4 != NULL)
466 | APPE check_login_ro SP pathname CRLF
468 if ($2 && $4 != NULL)
473 | NLST check_login CRLF
478 | NLST check_login SP pathstring CRLF
484 | LIST check_login CRLF
487 retrieve("/bin/ls -lgA", "");
489 | LIST check_login SP pathstring CRLF
492 retrieve("/bin/ls -lgA %s", $4);
495 | STAT check_login SP pathname CRLF
497 if ($2 && $4 != NULL)
502 | STAT check_login CRLF
508 | DELE check_login_ro SP pathname CRLF
510 if ($2 && $4 != NULL)
515 | RNTO check_login_ro SP pathname CRLF
517 if ($2 && $4 != NULL) {
519 renamecmd(fromname, $4);
521 fromname = (char *) 0;
523 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)) {
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.");
757 yyclearin; /* discard lookahead data */
758 yyerrok; /* clear error condition */
759 state = 0; /* 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) {
971 GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
973 memset(&gl, 0, sizeof(gl));
974 flags |= GLOB_MAXPATH;
975 gl.gl_matchc = MAXGLOBARGS;
976 if (glob($1, flags, NULL, &gl) ||
978 reply(550, "wildcard expansion error");
980 } else if (gl.gl_pathc > 1) {
981 reply(550, "ambiguous");
984 $$ = strdup(gl.gl_pathv[0]);
1000 int ret, dec, multby, digit;
1003 * Convert a number that was read as decimal number
1004 * to what it would be if it had been read as octal.
1015 ret += digit * multby;
1027 $$ = check_login1();
1035 reply(500, "EPSV command disabled");
1039 $$ = check_login1();
1047 reply(550, "Permission denied.");
1051 $$ = check_login1();
1057 #define CMD 0 /* beginning of command */
1058 #define ARGS 1 /* expect miscellaneous arguments */
1059 #define STR1 2 /* expect SP followed by STRING */
1060 #define STR2 3 /* expect STRING */
1061 #define OSTR 4 /* optional SP then STRING */
1062 #define ZSTR1 5 /* optional SP then optional STRING */
1063 #define ZSTR2 6 /* optional STRING after SP */
1064 #define SITECMD 7 /* SITE command */
1065 #define NSTR 8 /* Number followed by a string */
1067 #define MAXGLOBARGS 1000
1069 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1075 short implemented; /* 1 if command is implemented */
1079 struct tab cmdtab[] = { /* In order defined in RFC 765 */
1080 { "USER", USER, STR1, 1, "<sp> username" },
1081 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1082 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1083 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1084 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1085 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1086 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
1087 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1088 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1089 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1090 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1091 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1092 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1093 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1094 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1095 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1096 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1097 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1098 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1099 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1100 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1101 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1102 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1103 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1104 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1105 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1106 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1107 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1108 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1109 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1110 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1111 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1112 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1113 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1114 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1115 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1116 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1117 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1118 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1119 { "NOOP", NOOP, ARGS, 1, "" },
1120 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1121 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1122 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1123 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1124 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1125 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1126 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1127 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1128 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1129 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1130 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1131 { NULL, 0, 0, 0, 0 }
1134 struct tab sitetab[] = {
1135 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1136 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1137 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1138 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1139 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1140 { NULL, 0, 0, 0, 0 }
1143 static char *copy(char *);
1144 static void help(struct tab *, char *);
1146 lookup(struct tab *, char *);
1147 static int port_check(const char *);
1148 static int port_check_v6(const char *);
1149 static void sizecmd(char *);
1150 static void toolong(int);
1151 static void v4map_data_dest(void);
1152 static int yylex(void);
1155 lookup(struct tab *p, char *cmd)
1158 for (; p->name != NULL; p++)
1159 if (strcmp(cmd, p->name) == 0)
1164 #include <arpa/telnet.h>
1167 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1170 getline(char *s, int n, FILE *iop)
1176 /* tmpline may contain saved command from urgent mode interruption */
1177 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1179 if (tmpline[c] == '\n') {
1182 syslog(LOG_DEBUG, "command: %s", s);
1189 while ((c = getc(iop)) != EOF) {
1192 if ((c = getc(iop)) != EOF) {
1198 printf("%c%c%c", IAC, DONT, 0377&c);
1199 (void) fflush(stdout);
1204 printf("%c%c%c", IAC, WONT, 0377&c);
1205 (void) fflush(stdout);
1210 continue; /* ignore command */
1215 if (--n <= 0 || c == '\n')
1218 if (c == EOF && cs == s)
1222 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1223 /* Don't syslog passwords */
1224 syslog(LOG_DEBUG, "command: %.5s ???", s);
1229 /* Don't syslog trailing CR-LF */
1232 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1236 syslog(LOG_DEBUG, "command: %.*s", len, s);
1247 "Timeout (%d seconds): closing control connection.", timeout);
1249 syslog(LOG_INFO, "User %s timed out after %d seconds",
1250 (pw ? pw -> pw_name : "unknown"), timeout);
1267 (void) signal(SIGALRM, toolong);
1268 (void) alarm((unsigned) timeout);
1269 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1270 reply(221, "You could at least say goodbye.");
1275 if (strncasecmp(cbuf, "PASS", 4) != 0)
1276 setproctitle("%s: %s", proctitle, cbuf);
1277 #endif /* SETPROCTITLE */
1278 if ((cp = strchr(cbuf, '\r'))) {
1282 if ((cp = strpbrk(cbuf, " \n")))
1289 p = lookup(cmdtab, cbuf);
1292 if (p->implemented == 0) {
1303 if (cbuf[cpos] == ' ') {
1308 if ((cp2 = strpbrk(cp, " \n")))
1313 p = lookup(sitetab, cp);
1315 if (guest == 0 && p != 0) {
1316 if (p->implemented == 0) {
1330 if (cbuf[cpos] == '\n') {
1338 if (cbuf[cpos] == ' ') {
1340 state = state == OSTR ? STR2 : state+1;
1346 if (cbuf[cpos] == '\n') {
1357 * Make sure the string is nonempty and \n terminated.
1359 if (n > 1 && cbuf[cpos] == '\n') {
1361 yylval.s = copy(cp);
1369 if (cbuf[cpos] == ' ') {
1373 if (isdigit(cbuf[cpos])) {
1375 while (isdigit(cbuf[++cpos]))
1379 yylval.u.i = atoi(cp);
1388 if (isdigit(cbuf[cpos])) {
1390 while (isdigit(cbuf[++cpos]))
1394 yylval.u.i = atoi(cp);
1395 yylval.u.o = strtoull(cp, (char **)NULL, 10);
1399 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1400 && !isalnum(cbuf[cpos + 3])) {
1404 switch (cbuf[cpos++]) {
1468 fatalerror("Unknown state in scanner.");
1478 while (*s != '\0') {
1490 p = malloc((unsigned) strlen(s) + 1);
1492 fatalerror("Ran out of memory.");
1493 (void) strcpy(p, s);
1498 help(struct tab *ctab, char *s)
1504 if (ctab == sitetab)
1508 width = 0, NCMDS = 0;
1509 for (c = ctab; c->name != NULL; c++) {
1510 int len = strlen(c->name);
1516 width = (width + 8) &~ 7;
1521 lreply(214, "The following %scommands are recognized %s.",
1522 type, "(* =>'s unimplemented)");
1523 columns = 76 / width;
1526 lines = (NCMDS + columns - 1) / columns;
1527 for (i = 0; i < lines; i++) {
1529 for (j = 0; j < columns; j++) {
1530 c = ctab + j * lines + i;
1531 printf("%s%c", c->name,
1532 c->implemented ? ' ' : '*');
1533 if (c + lines >= &ctab[NCMDS])
1535 w = strlen(c->name) + 1;
1543 (void) fflush(stdout);
1544 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1548 c = lookup(ctab, s);
1549 if (c == (struct tab *)0) {
1550 reply(502, "Unknown command %s.", s);
1554 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1556 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1561 sizecmd(char *filename)
1567 if (stat(filename, &stbuf) < 0)
1568 perror_reply(550, filename);
1569 else if (!S_ISREG(stbuf.st_mode))
1570 reply(550, "%s: not a plain file.", filename);
1572 reply(213, "%qu", stbuf.st_size);
1579 fin = fopen(filename, "r");
1581 perror_reply(550, filename);
1584 if (fstat(fileno(fin), &stbuf) < 0) {
1585 perror_reply(550, filename);
1588 } else if (!S_ISREG(stbuf.st_mode)) {
1589 reply(550, "%s: not a plain file.", filename);
1592 } else if (stbuf.st_size > MAXASIZE) {
1593 reply(550, "%s: too large for type A SIZE.", filename);
1599 while((c=getc(fin)) != EOF) {
1600 if (c == '\n') /* will get expanded to \r\n */
1606 reply(213, "%qd", count);
1609 reply(504, "SIZE not implemented for type %s.",
1614 /* Return 1, if port check is done. Return 0, if not yet. */
1616 port_check(const char *pcmd)
1618 if (his_addr.su_family == AF_INET) {
1619 if (data_dest.su_family != AF_INET) {
1621 reply(500, "Invalid address rejected.");
1625 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1626 memcmp(&data_dest.su_sin.sin_addr,
1627 &his_addr.su_sin.sin_addr,
1628 sizeof(data_dest.su_sin.sin_addr)))) {
1630 reply(500, "Illegal PORT range rejected.");
1634 (void) close(pdata);
1637 reply(200, "%s command successful.", pcmd);
1650 reply(530, "Please login with USER and PASS.");
1656 /* Return 1, if port check is done. Return 0, if not yet. */
1658 port_check_v6(const char *pcmd)
1660 if (his_addr.su_family == AF_INET6) {
1661 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1662 /* Convert data_dest into v4 mapped sockaddr.*/
1664 if (data_dest.su_family != AF_INET6) {
1666 reply(500, "Invalid address rejected.");
1670 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1671 memcmp(&data_dest.su_sin6.sin6_addr,
1672 &his_addr.su_sin6.sin6_addr,
1673 sizeof(data_dest.su_sin6.sin6_addr)))) {
1675 reply(500, "Illegal PORT range rejected.");
1679 (void) close(pdata);
1682 reply(200, "%s command successful.", pcmd);
1690 v4map_data_dest(void)
1692 struct in_addr savedaddr;
1695 if (data_dest.su_family != AF_INET) {
1697 reply(500, "Invalid address rejected.");
1701 savedaddr = data_dest.su_sin.sin_addr;
1702 savedport = data_dest.su_port;
1704 memset(&data_dest, 0, sizeof(data_dest));
1705 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1706 data_dest.su_sin6.sin6_family = AF_INET6;
1707 data_dest.su_sin6.sin6_port = savedport;
1708 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1709 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1710 (caddr_t)&savedaddr, sizeof(savedaddr));