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.21 2001/02/19 21:51:26 des Exp $";
51 #include <sys/param.h>
52 #include <sys/socket.h>
55 #include <netinet/in.h>
71 //#include <libutil.h>
75 extern union sockunion data_dest, his_addr;
77 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[];
100 static int cmd_bytesz;
104 #if defined(VIRTUAL_HOSTING)
122 USER PASS ACCT REIN QUIT PORT
123 PASV TYPE STRU MODE RETR STOR
124 APPE MLFL MAIL MSND MSOM MSAM
125 MRSQ MRCP ALLO REST RNFR RNTO
126 ABOR DELE CWD LIST NLST SITE
127 STAT HELP NOOP MKD RMD PWD
128 CDUP STOU SMNT SYST SIZE MDTM
138 %type <i> check_login octal_number byte_size
139 %type <i> check_login_ro octal_number byte_size
140 %type <i> check_login_epsv octal_number byte_size
141 %type <i> struct_code mode_code type_code form_code
142 %type <s> pathstring pathname password username ext_arg
153 fromname = (char *) 0;
154 restart_point = (off_t) 0;
160 : USER SP username CRLF
165 | PASS SP password CRLF
170 | PORT check_login SP host_port CRLF
172 #if defined(VIRTUAL_HOSTING)
174 reply(501, "no PORT allowed after EPSV ALL");
180 if (port_check("PORT") == 1)
183 if ((his_addr.su_family != AF_INET6 ||
184 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
185 /* shoud never happen */
187 reply(500, "Invalid address rejected.");
190 port_check_v6("pcmd");
194 | LPRT check_login SP host_long_port CRLF
196 #if defined(VIRTUAL_HOSTING)
198 reply(501, "no LPRT allowed after EPSV ALL");
204 if (port_check("LPRT") == 1)
207 if (his_addr.su_family != AF_INET6) {
209 reply(500, "Invalid address rejected.");
212 if (port_check_v6("LPRT") == 1)
217 | EPRT check_login SP STRING CRLF
223 struct addrinfo hints;
224 struct addrinfo *res;
227 #if defined(VIRTUAL_HOSTING)
229 reply(501, "no EPRT allowed after EPSV ALL");
236 memset(&data_dest, 0, sizeof(data_dest));
239 syslog(LOG_DEBUG, "%s", tmp);
241 fatal("not enough core");
247 memset(result, 0, sizeof(result));
248 for (i = 0; i < 3; i++) {
249 q = strchr(p, delim);
250 if (!q || *q != delim) {
253 "Invalid argument, rejected.");
262 syslog(LOG_DEBUG, "%d: %s", i, p);
266 /* some more sanity check */
281 memset(&hints, 0, sizeof(hints));
282 if (atoi(result[0]) == 1)
283 hints.ai_family = PF_INET;
285 else if (atoi(result[0]) == 2)
286 hints.ai_family = PF_INET6;
289 hints.ai_family = PF_UNSPEC; /*XXX*/
290 hints.ai_socktype = SOCK_STREAM;
291 i = getaddrinfo(result[1], result[2], &hints, &res);
294 memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
296 if (his_addr.su_family == AF_INET6
297 && data_dest.su_family == AF_INET6) {
298 /* XXX more sanity checks! */
299 data_dest.su_sin6.sin6_scope_id =
300 his_addr.su_sin6.sin6_scope_id;
306 if (port_check("EPRT") == 1)
309 if (his_addr.su_family != AF_INET6) {
311 reply(500, "Invalid address rejected.");
314 if (port_check_v6("EPRT") == 1)
319 | PASV check_login CRLF
321 #if defined(VIRTUAL_HOSTING)
323 reply(501, "no PASV allowed after EPSV ALL");
329 | LPSV check_login CRLF
331 #if defined(VIRTUAL_HOSTING)
333 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.");
364 #if defined(VIRTUAL_HOSTING)
369 | EPSV check_login_epsv CRLF
372 long_passive("EPSV", PF_UNSPEC);
374 | TYPE check_login SP type_code CRLF
380 if (cmd_form == FORM_N) {
381 reply(200, "Type set to A.");
385 reply(504, "Form must be N.");
389 reply(504, "Type E not implemented.");
393 reply(200, "Type set to I.");
399 if (cmd_bytesz == 8) {
401 "Type set to L (byte size 8).");
404 reply(504, "Byte size must be 8.");
405 #else /* NBBY == 8 */
406 UNIMPLEMENTED for NBBY != 8
407 #endif /* NBBY == 8 */
411 | STRU check_login SP struct_code CRLF
417 reply(200, "STRU F ok.");
421 reply(504, "Unimplemented STRU type.");
425 | MODE check_login SP mode_code CRLF
431 reply(200, "MODE S ok.");
435 reply(502, "Unimplemented MODE type.");
439 | ALLO check_login SP NUMBER CRLF
442 reply(202, "ALLO command ignored.");
445 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
448 reply(202, "ALLO command ignored.");
451 | RETR check_login SP pathname CRLF
453 if ($2 && $4 != NULL)
454 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 pathname 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);
571 reply(200, "NOOP command successful.");
573 | MKD check_login_ro SP pathname CRLF
575 if ($2 && $4 != NULL)
580 | RMD check_login_ro SP pathname CRLF
582 if ($2 && $4 != NULL)
587 | PWD check_login CRLF
592 | CDUP check_login CRLF
599 help(sitetab, (char *) 0);
601 | SITE SP HELP SP STRING CRLF
605 | SITE SP UMASK check_login CRLF
611 (void) umask(oldmask);
612 reply(200, "Current UMASK is %03o", oldmask);
615 | SITE SP UMASK check_login SP octal_number CRLF
620 if (($6 == -1) || ($6 > 0777)) {
621 reply(501, "Bad UMASK value");
625 "UMASK set to %03o (was %03o)",
630 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
632 if ($4 && ($8 != NULL)) {
635 "CHMOD: Mode value must be between 0 and 0777");
636 else if (chmod($8, $6) < 0)
637 perror_reply(550, $8);
639 reply(200, "CHMOD command successful.");
644 | SITE SP check_login IDLE CRLF
648 "Current IDLE time limit is %d seconds; max %d",
649 timeout, maxtimeout);
651 | SITE SP check_login IDLE SP NUMBER CRLF
654 if ($6 < 30 || $6 > maxtimeout) {
656 "Maximum IDLE time must be between 30 and %d seconds",
660 (void) alarm((unsigned) timeout);
662 "Maximum IDLE time set to %d seconds",
667 | STOU check_login_ro SP pathname CRLF
669 if ($2 && $4 != NULL)
674 | SYST check_login CRLF
677 #if defined(unix) || defined(__APPLE__)
679 reply(215, "UNIX Type: L%d Version: BSD-%d",
682 reply(215, "UNIX Type: L%d", NBBY);
685 reply(215, "UNKNOWN Type: L%d", NBBY);
690 * SIZE is not in RFC959, but Postel has blessed it and
691 * it will be in the updated RFC.
693 * Return size of file in a format suitable for
694 * using with RESTART (we just count bytes).
696 | SIZE check_login SP pathname CRLF
698 if ($2 && $4 != NULL)
705 * MDTM is not in RFC959, but Postel has blessed it and
706 * it will be in the updated RFC.
708 * Return modification time of file as an ISO 3307
709 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
710 * where xxx is the fractional second (of any precision,
711 * not necessarily 3 digits)
713 | MDTM check_login SP pathname CRLF
715 if ($2 && $4 != NULL) {
717 if (stat($4, &stbuf) < 0)
719 $4, strerror(errno));
720 else if (!S_ISREG(stbuf.st_mode)) {
721 reply(550, "%s: not a plain file.", $4);
724 t = gmtime(&stbuf.st_mtime);
726 "%04d%02d%02d%02d%02d%02d",
728 t->tm_mon+1, t->tm_mday,
729 t->tm_hour, t->tm_min, t->tm_sec);
737 reply(221, "Goodbye.");
746 : RNFR check_login_ro SP pathname CRLF
750 restart_point = (off_t) 0;
752 fromname = renamefrom($4);
753 if (fromname == (char *) 0 && $4) {
758 | REST check_login SP byte_size CRLF
761 fromname = (char *) 0;
762 restart_point = $4; /* XXX $4 is only "int" */
763 reply(350, "Restarting at %qd. %s",
765 "Send STORE or RETRIEVE to initiate transfer.");
777 $$ = (char *)calloc(1, sizeof(char));
787 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
792 data_dest.su_len = sizeof(struct sockaddr_in);
793 data_dest.su_family = AF_INET;
794 p = (char *)&data_dest.su_sin.sin_port;
795 p[0] = $9; p[1] = $11;
796 a = (char *)&data_dest.su_sin.sin_addr;
797 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
802 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
803 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
804 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
805 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
806 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
811 memset(&data_dest, 0, sizeof(data_dest));
812 data_dest.su_len = sizeof(struct sockaddr_in6);
813 data_dest.su_family = AF_INET6;
814 p = (char *)&data_dest.su_port;
815 p[0] = $39; p[1] = $41;
816 a = (char *)&data_dest.su_sin6.sin6_addr;
817 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
818 a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19;
819 a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27;
820 a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
821 if (his_addr.su_family == AF_INET6) {
822 /* XXX more sanity checks! */
823 data_dest.su_sin6.sin6_scope_id =
824 his_addr.su_sin6.sin6_scope_id;
826 if ($1 != 6 || $3 != 16 || $37 != 2)
827 memset(&data_dest, 0, sizeof(data_dest));
829 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
830 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
835 memset(&data_dest, 0, sizeof(data_dest));
836 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
837 data_dest.su_family = AF_INET;
838 p = (char *)&data_dest.su_port;
839 p[0] = $15; p[1] = $17;
840 a = (char *)&data_dest.su_sin.sin_addr;
841 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
842 if ($1 != 4 || $3 != 4 || $13 != 2)
843 memset(&data_dest, 0, sizeof(data_dest));
897 /* this is for a bug in the BBN ftp */
939 * Problem: this production is used for all pathname
940 * processing, but only gives a 550 error reply.
941 * This is a valid reply in some cases but not in others.
943 if (logged_in && $1 && *$1 == '~') {
946 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
948 memset(&gl, 0, sizeof(gl));
949 if (glob($1, flags, NULL, &gl) ||
951 reply(550, "not found");
954 $$ = strdup(gl.gl_pathv[0]);
970 int ret, dec, multby, digit;
973 * Convert a number that was read as decimal number
974 * to what it would be if it had been read as octal.
985 ret += digit * multby;
1005 reply(500, "EPSV command disabled");
1009 $$ = check_login1();
1017 reply(550, "Permission denied.");
1021 $$ = check_login1();
1027 extern jmp_buf errcatch;
1029 #define CMD 0 /* beginning of command */
1030 #define ARGS 1 /* expect miscellaneous arguments */
1031 #define STR1 2 /* expect SP followed by STRING */
1032 #define STR2 3 /* expect STRING */
1033 #define OSTR 4 /* optional SP then STRING */
1034 #define ZSTR1 5 /* SP then optional STRING */
1035 #define ZSTR2 6 /* optional STRING after SP */
1036 #define SITECMD 7 /* SITE command */
1037 #define NSTR 8 /* Number followed by a string */
1043 short implemented; /* 1 if command is implemented */
1047 struct tab cmdtab[] = { /* In order defined in RFC 765 */
1048 { "USER", USER, STR1, 1, "<sp> username" },
1049 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
1050 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1051 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1052 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1053 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1054 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
1055 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1056 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1057 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1058 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1059 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1060 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1061 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1062 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1063 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1064 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1065 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1066 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1067 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1068 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1069 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1070 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1071 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1072 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1073 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1074 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1075 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1076 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1077 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1078 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1079 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1080 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1081 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1082 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1083 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1084 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1085 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1086 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1087 { "NOOP", NOOP, ARGS, 1, "" },
1088 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1089 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1090 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1091 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1092 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1093 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1094 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1095 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1096 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1097 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1098 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1099 { NULL, 0, 0, 0, 0 }
1102 struct tab sitetab[] = {
1103 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1104 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1105 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1106 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1107 { NULL, 0, 0, 0, 0 }
1110 static char *copy __P((char *));
1111 static void help __P((struct tab *, char *));
1113 lookup __P((struct tab *, char *));
1114 static int port_check __P((const char *));
1115 static int port_check_v6 __P((const char *));
1116 static void sizecmd __P((char *));
1117 static void toolong __P((int));
1118 static void v4map_data_dest __P((void));
1119 static int yylex __P((void));
1127 for (; p->name != NULL; p++)
1128 if (strcmp(cmd, p->name) == 0)
1133 #include <arpa/telnet.h>
1136 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1148 /* tmpline may contain saved command from urgent mode interruption */
1149 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1151 if (tmpline[c] == '\n') {
1154 syslog(LOG_DEBUG, "command: %s", s);
1161 while ((c = getc(iop)) != EOF) {
1164 if ((c = getc(iop)) != EOF) {
1170 printf("%c%c%c", IAC, DONT, 0377&c);
1171 (void) fflush(stdout);
1176 printf("%c%c%c", IAC, WONT, 0377&c);
1177 (void) fflush(stdout);
1182 continue; /* ignore command */
1187 if (--n <= 0 || c == '\n')
1190 if (c == EOF && cs == s)
1194 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1195 /* Don't syslog passwords */
1196 syslog(LOG_DEBUG, "command: %.5s ???", s);
1201 /* Don't syslog trailing CR-LF */
1204 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1208 syslog(LOG_DEBUG, "command: %.*s", len, s);
1220 "Timeout (%d seconds): closing control connection.", timeout);
1222 syslog(LOG_INFO, "User %s timed out after %d seconds",
1223 (pw ? pw -> pw_name : "unknown"), timeout);
1230 static int cpos, state;
1240 (void) signal(SIGALRM, toolong);
1241 (void) alarm((unsigned) timeout);
1242 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1243 reply(221, "You could at least say goodbye.");
1248 if (strncasecmp(cbuf, "PASS", 4) != 0)
1249 setproctitle("%s: %s", proctitle, cbuf);
1250 #endif /* SETPROCTITLE */
1251 if ((cp = strchr(cbuf, '\r'))) {
1255 if ((cp = strpbrk(cbuf, " \n")))
1262 p = lookup(cmdtab, cbuf);
1265 if (p->implemented == 0) {
1267 longjmp(errcatch,0);
1277 if (cbuf[cpos] == ' ') {
1282 if ((cp2 = strpbrk(cp, " \n")))
1287 p = lookup(sitetab, cp);
1289 if (guest == 0 && p != 0) {
1290 if (p->implemented == 0) {
1293 longjmp(errcatch,0);
1304 if (cbuf[cpos] == '\n') {
1313 if (cbuf[cpos] == ' ') {
1315 state = state == OSTR ? STR2 : state+1;
1321 if (cbuf[cpos] == '\n') {
1332 * Make sure the string is nonempty and \n terminated.
1334 if (n > 1 && cbuf[cpos] == '\n') {
1336 yylval.s = copy(cp);
1344 if (cbuf[cpos] == ' ') {
1348 if (isdigit(cbuf[cpos])) {
1350 while (isdigit(cbuf[++cpos]))
1354 yylval.i = atoi(cp);
1363 if (isdigit(cbuf[cpos])) {
1365 while (isdigit(cbuf[++cpos]))
1369 yylval.i = atoi(cp);
1373 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1374 && !isalnum(cbuf[cpos + 3])) {
1378 switch (cbuf[cpos++]) {
1442 fatal("Unknown state in scanner.");
1444 yyerror((char *) 0);
1446 longjmp(errcatch,0);
1454 while (*s != '\0') {
1467 p = malloc((unsigned) strlen(s) + 1);
1469 fatal("Ran out of memory.");
1470 (void) strcpy(p, s);
1483 if (ctab == sitetab)
1487 width = 0, NCMDS = 0;
1488 for (c = ctab; c->name != NULL; c++) {
1489 int len = strlen(c->name);
1495 width = (width + 8) &~ 7;
1500 lreply(214, "The following %scommands are recognized %s.",
1501 type, "(* =>'s unimplemented)");
1502 columns = 76 / width;
1505 lines = (NCMDS + columns - 1) / columns;
1506 for (i = 0; i < lines; i++) {
1508 for (j = 0; j < columns; j++) {
1509 c = ctab + j * lines + i;
1510 printf("%s%c", c->name,
1511 c->implemented ? ' ' : '*');
1512 if (c + lines >= &ctab[NCMDS])
1514 w = strlen(c->name) + 1;
1522 (void) fflush(stdout);
1523 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1527 c = lookup(ctab, s);
1528 if (c == (struct tab *)0) {
1529 reply(502, "Unknown command %s.", s);
1533 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1535 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1547 if (stat(filename, &stbuf) < 0)
1548 perror_reply(550, filename);
1549 else if (!S_ISREG(stbuf.st_mode))
1550 reply(550, "%s: not a plain file.", filename);
1552 reply(213, "%qu", stbuf.st_size);
1559 fin = fopen(filename, "r");
1561 perror_reply(550, filename);
1564 if (fstat(fileno(fin), &stbuf) < 0) {
1565 perror_reply(550, filename);
1568 } else if (!S_ISREG(stbuf.st_mode)) {
1569 reply(550, "%s: not a plain file.", filename);
1575 while((c=getc(fin)) != EOF) {
1576 if (c == '\n') /* will get expanded to \r\n */
1582 reply(213, "%qd", count);
1585 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1589 /* Return 1, if port check is done. Return 0, if not yet. */
1594 if (his_addr.su_family == AF_INET) {
1595 if (data_dest.su_family != AF_INET) {
1597 reply(500, "Invalid address rejected.");
1601 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1602 memcmp(&data_dest.su_sin.sin_addr,
1603 &his_addr.su_sin.sin_addr,
1604 sizeof(data_dest.su_sin.sin_addr)))) {
1606 reply(500, "Illegal PORT range rejected.");
1610 (void) close(pdata);
1613 reply(200, "%s command successful.", pcmd);
1626 reply(530, "Please login with USER and PASS.");
1632 /* Return 1, if port check is done. Return 0, if not yet. */
1637 if (his_addr.su_family == AF_INET6) {
1638 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1639 /* Convert data_dest into v4 mapped sockaddr.*/
1641 if (data_dest.su_family != AF_INET6) {
1643 reply(500, "Invalid address rejected.");
1647 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1648 memcmp(&data_dest.su_sin6.sin6_addr,
1649 &his_addr.su_sin6.sin6_addr,
1650 sizeof(data_dest.su_sin6.sin6_addr)))) {
1652 reply(500, "Illegal PORT range rejected.");
1656 (void) close(pdata);
1659 reply(200, "%s command successful.", pcmd);
1669 struct in_addr savedaddr;
1672 if (data_dest.su_family != AF_INET) {
1674 reply(500, "Invalid address rejected.");
1678 savedaddr = data_dest.su_sin.sin_addr;
1679 savedport = data_dest.su_port;
1681 memset(&data_dest, 0, sizeof(data_dest));
1682 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1683 data_dest.su_sin6.sin6_family = AF_INET6;
1684 data_dest.su_sin6.sin6_port = savedport;
1685 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1686 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1687 (caddr_t)&savedaddr, sizeof(savedaddr));