version 1.48.2.2, 2001/03/29 14:14:17 |
version 1.92, 2011/07/01 02:46:15 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
|
|
/*- |
/*- |
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc. |
* Copyright (c) 1997-2009 The NetBSD Foundation, Inc. |
* All rights reserved. |
* All rights reserved. |
* |
* |
* This code is derived from software contributed to The NetBSD Foundation |
* This code is derived from software contributed to The NetBSD Foundation |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
* 2. Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* documentation and/or other materials provided with the distribution. |
* 3. All advertising materials mentioning features or use of this software |
|
* must display the following acknowledgement: |
|
* This product includes software developed by the NetBSD |
|
* Foundation, Inc. and its contributors. |
|
* 4. Neither the name of The NetBSD Foundation nor the names of its |
|
* contributors may be used to endorse or promote products derived |
|
* from this software without specific prior written permission. |
|
* |
* |
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
* 2. Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* documentation and/or other materials provided with the distribution. |
* 3. All advertising materials mentioning features or use of this software |
* 3. Neither the name of the University nor the names of its contributors |
* must display the following acknowledgement: |
|
* This product includes software developed by the University of |
|
* California, Berkeley and its contributors. |
|
* 4. Neither the name of the University nor the names of its contributors |
|
* may be used to endorse or promote products derived from this software |
* may be used to endorse or promote products derived from this software |
* without specific prior written permission. |
* without specific prior written permission. |
* |
* |
Line 97 __RCSID("$NetBSD$"); |
|
Line 86 __RCSID("$NetBSD$"); |
|
|
|
#include <ctype.h> |
#include <ctype.h> |
#include <errno.h> |
#include <errno.h> |
#include <glob.h> |
|
#include <pwd.h> |
#include <pwd.h> |
#include <setjmp.h> |
|
#include <signal.h> |
|
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
Line 121 static int cmd_type; |
|
Line 107 static int cmd_type; |
|
static int cmd_form; |
static int cmd_form; |
static int cmd_bytesz; |
static int cmd_bytesz; |
|
|
char cbuf[512]; |
char cbuf[FTP_BUFLEN]; |
|
char *cmdp; |
char *fromname; |
char *fromname; |
|
|
|
extern int epsvall; |
|
struct tab sitetab[]; |
|
|
|
static int check_write(const char *, int); |
|
static void help(struct tab *, const char *); |
|
static void port_check(const char *, int); |
|
int yylex(void); |
|
|
%} |
%} |
|
|
%union { |
%union { |
int i; |
struct { |
char *s; |
LLT ll; |
|
int i; |
|
} u; |
|
char *s; |
|
const char *cs; |
} |
} |
|
|
%token |
%token |
A B C E F I |
A B C E F I |
L N P R S T |
L N P R S T |
|
|
SP CRLF COMMA |
SP CRLF COMMA ALL |
|
|
USER PASS ACCT CWD CDUP SMNT |
USER PASS ACCT CWD CDUP SMNT |
QUIT REIN PORT PASV TYPE STRU |
QUIT REIN PORT PASV TYPE STRU |
|
|
LEXERR |
LEXERR |
|
|
%token <s> STRING |
%token <s> STRING |
%token <s> ALL |
%token <u> NUMBER |
%token <i> NUMBER |
|
|
|
%type <i> check_login octal_number byte_size |
%type <u.i> check_login octal_number byte_size |
%type <i> struct_code mode_code type_code form_code decimal_integer |
%type <u.i> struct_code mode_code type_code form_code decimal_integer |
%type <s> pathstring pathname password username |
%type <s> pathstring pathname password username |
%type <s> mechanism_name base64data prot_code |
%type <s> mechanism_name base64data prot_code |
|
|
%start cmd_list |
%start cmd_sel |
|
|
%% |
%% |
|
|
cmd_list |
cmd_sel |
: /* empty */ |
: cmd |
|
|
| cmd_list cmd |
|
{ |
{ |
fromname = NULL; |
REASSIGN(fromname, NULL); |
restart_point = (off_t) 0; |
restart_point = (off_t) 0; |
} |
} |
|
|
| cmd_list rcmd |
| rcmd |
|
|
; |
; |
|
|
|
|
| LPSV check_login CRLF |
| LPSV check_login CRLF |
{ |
{ |
if ($2) { |
if ($2) { |
if (epsvall) |
if (CURCLASS_FLAGS_ISSET(passive)) { |
reply(501, |
if (epsvall) |
"LPSV disallowed after EPSV ALL"); |
reply(501, |
else |
"LPSV disallowed after EPSV ALL"); |
long_passive("LPSV", PF_UNSPEC); |
else |
|
long_passive("LPSV", PF_UNSPEC); |
|
} else |
|
reply(500, "LPSV mode not available."); |
} |
} |
} |
} |
|
|
| EPSV check_login SP NUMBER CRLF |
| EPSV check_login SP NUMBER CRLF |
{ |
{ |
if ($2) |
if ($2) { |
long_passive("EPSV", epsvproto2af($4)); |
if (CURCLASS_FLAGS_ISSET(passive)) |
|
long_passive("EPSV", |
|
epsvproto2af($4.i)); |
|
else |
|
reply(500, "EPSV mode not available."); |
|
} |
} |
} |
|
|
| EPSV check_login SP ALL CRLF |
| EPSV check_login SP ALL CRLF |
{ |
{ |
if ($2) { |
if ($2) { |
reply(200, "EPSV ALL command successful."); |
if (CURCLASS_FLAGS_ISSET(passive)) { |
epsvall++; |
reply(200, |
|
"EPSV ALL command successful."); |
|
epsvall++; |
|
} else |
|
reply(500, "EPSV mode not available."); |
} |
} |
} |
} |
|
|
| EPSV check_login CRLF |
| EPSV check_login CRLF |
{ |
{ |
if ($2) |
if ($2) { |
long_passive("EPSV", PF_UNSPEC); |
if (CURCLASS_FLAGS_ISSET(passive)) |
|
long_passive("EPSV", PF_UNSPEC); |
|
else |
|
reply(500, "EPSV mode not available."); |
|
} |
} |
} |
|
|
| TYPE check_login SP type_code CRLF |
| TYPE check_login SP type_code CRLF |
|
|
if (check_write($3, 0)) { |
if (check_write($3, 0)) { |
if (fromname) { |
if (fromname) { |
renamecmd(fromname, $3); |
renamecmd(fromname, $3); |
free(fromname); |
REASSIGN(fromname, NULL); |
fromname = NULL; |
|
} else { |
} else { |
reply(503, "Bad sequence of commands."); |
reply(503, "Bad sequence of commands."); |
} |
} |
|
|
|
|
| ABOR check_login CRLF |
| ABOR check_login CRLF |
{ |
{ |
if ($2) |
if (is_oob) |
|
abor(); |
|
else if ($2) |
reply(225, "ABOR command successful."); |
reply(225, "ABOR command successful."); |
} |
} |
|
|
|
|
|
|
| LIST check_login CRLF |
| LIST check_login CRLF |
{ |
{ |
char *argv[] = { INTERNAL_LS, "-lgA", NULL }; |
const char *argv[] = { INTERNAL_LS, "-lgA", NULL }; |
|
|
|
if (CURCLASS_FLAGS_ISSET(hidesymlinks)) |
|
argv[1] = "-LlgA"; |
if ($2) |
if ($2) |
retrieve(argv, ""); |
retrieve(argv, ""); |
} |
} |
|
|
| LIST check_login SP pathname CRLF |
| LIST check_login SP pathname CRLF |
{ |
{ |
char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; |
const char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; |
|
|
|
if (CURCLASS_FLAGS_ISSET(hidesymlinks)) |
|
argv[1] = "-LlgA"; |
if ($2 && $4 != NULL) { |
if ($2 && $4 != NULL) { |
argv[2] = $4; |
argv[2] = $4; |
retrieve(argv, $4); |
retrieve(argv, $4); |
|
|
| SITE SP CHMOD SP octal_number SP pathname CRLF |
| SITE SP CHMOD SP octal_number SP pathname CRLF |
{ |
{ |
if (check_write($7, 0)) { |
if (check_write($7, 0)) { |
if ($5 > 0777) |
if (($5 == -1) || ($5 > 0777)) |
reply(501, |
reply(501, |
"CHMOD: Mode value must be between 0 and 0777"); |
"CHMOD: Mode value must be between 0 and 0777"); |
else if (chmod($7, $5) < 0) |
else if (chmod($7, $5) < 0) |
|
|
{ |
{ |
if ($4) { |
if ($4) { |
reply(200, |
reply(200, |
"Current IDLE time limit is %d seconds; max %d", |
"Current IDLE time limit is " LLF |
curclass.timeout, curclass.maxtimeout); |
" seconds; max " LLF, |
|
(LLT)curclass.timeout, |
|
(LLT)curclass.maxtimeout); |
} |
} |
} |
} |
|
|
| SITE SP IDLE check_login SP NUMBER CRLF |
| SITE SP IDLE check_login SP NUMBER CRLF |
{ |
{ |
if ($4) { |
if ($4) { |
if ($6 < 30 || $6 > curclass.maxtimeout) { |
if ($6.i < 30 || $6.i > curclass.maxtimeout) { |
reply(501, |
reply(501, |
"IDLE time limit must be between 30 and %d seconds", |
"IDLE time limit must be between 30 and " |
curclass.maxtimeout); |
LLF " seconds", |
|
(LLT)curclass.maxtimeout); |
} else { |
} else { |
curclass.timeout = $6; |
curclass.timeout = $6.i; |
(void) alarm(curclass.timeout); |
(void) alarm(curclass.timeout); |
reply(200, |
reply(200, |
"IDLE time limit set to %d seconds", |
"IDLE time limit set to " |
curclass.timeout); |
LLF " seconds", |
|
(LLT)curclass.timeout); |
} |
} |
} |
} |
} |
} |
|
|
|
|
| SITE SP RATEGET check_login SP STRING CRLF |
| SITE SP RATEGET check_login SP STRING CRLF |
{ |
{ |
|
char errbuf[100]; |
char *p = $6; |
char *p = $6; |
LLT rate; |
LLT rate; |
|
|
if ($4) { |
if ($4) { |
rate = strsuftoll(p); |
rate = strsuftollx("RATEGET", p, 0, |
if (rate == -1) |
curclass.maxrateget |
reply(501, "Invalid RATEGET %s", p); |
? curclass.maxrateget |
else if (curclass.maxrateget && |
: LLTMAX, errbuf, sizeof(errbuf)); |
rate > curclass.maxrateget) |
if (errbuf[0]) |
reply(501, |
reply(501, "%s", errbuf); |
"RATEGET " LLF " is larger than maximum RATEGET " LLF, |
|
(LLT)rate, |
|
(LLT)curclass.maxrateget); |
|
else { |
else { |
curclass.rateget = rate; |
curclass.rateget = rate; |
reply(200, |
reply(200, |
|
|
|
|
| SITE SP RATEPUT check_login SP STRING CRLF |
| SITE SP RATEPUT check_login SP STRING CRLF |
{ |
{ |
|
char errbuf[100]; |
char *p = $6; |
char *p = $6; |
LLT rate; |
LLT rate; |
|
|
if ($4) { |
if ($4) { |
rate = strsuftoll(p); |
rate = strsuftollx("RATEPUT", p, 0, |
if (rate == -1) |
curclass.maxrateput |
reply(501, "Invalid RATEPUT %s", p); |
? curclass.maxrateput |
else if (curclass.maxrateput && |
: LLTMAX, errbuf, sizeof(errbuf)); |
rate > curclass.maxrateput) |
if (errbuf[0]) |
reply(501, |
reply(501, "%s", errbuf); |
"RATEPUT " LLF " is larger than maximum RATEPUT " LLF, |
|
(LLT)rate, |
|
(LLT)curclass.maxrateput); |
|
else { |
else { |
curclass.rateput = rate; |
curclass.rateput = rate; |
reply(200, |
reply(200, |
|
|
{ |
{ |
int oldmask; |
int oldmask; |
|
|
if ($4 && CURCLASS_FLAGS_ISSET(modify)) { |
if ($4 && check_write("", 0)) { |
if (($6 == -1) || ($6 > 0777)) { |
if (($6 == -1) || ($6 > 0777)) { |
reply(501, "Bad UMASK value"); |
reply(501, "Bad UMASK value"); |
} else { |
} else { |
|
|
|
|
| STAT CRLF |
| STAT CRLF |
{ |
{ |
statcmd(); |
if (is_oob) |
|
statxfer(); |
|
else |
|
statcmd(); |
} |
} |
|
|
| HELP CRLF |
| HELP CRLF |
|
|
} |
} |
|
|
|
|
/* extensions from draft-ietf-ftpext-mlst-11 */ |
/* RFC 3659 */ |
|
|
/* |
/* |
* Return size of file in a format suitable for |
* Return size of file in a format suitable for |
|
|
; |
; |
|
|
rcmd |
rcmd |
: REST check_login SP byte_size CRLF |
: REST check_login SP NUMBER CRLF |
{ |
{ |
if ($2) { |
if ($2) { |
fromname = NULL; |
REASSIGN(fromname, NULL); |
restart_point = $4; /* XXX: $4 is only "int" */ |
restart_point = (off_t)$4.ll; |
reply(350, |
reply(350, |
"Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", |
"Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", |
(LLT)restart_point); |
(LLT)restart_point); |
|
|
| RNFR SP pathname CRLF |
| RNFR SP pathname CRLF |
{ |
{ |
restart_point = (off_t) 0; |
restart_point = (off_t) 0; |
if (check_write($3, 0)) |
if (check_write($3, 0)) { |
|
REASSIGN(fromname, NULL); |
fromname = renamefrom($3); |
fromname = renamefrom($3); |
|
} |
if ($3 != NULL) |
if ($3 != NULL) |
free($3); |
free($3); |
} |
} |
|
|
|
|
byte_size |
byte_size |
: NUMBER |
: NUMBER |
|
{ |
|
$$ = $1.i; |
|
} |
; |
; |
|
|
host_port |
host_port |
|
|
data_dest.su_len = sizeof(struct sockaddr_in); |
data_dest.su_len = sizeof(struct sockaddr_in); |
data_dest.su_family = AF_INET; |
data_dest.su_family = AF_INET; |
p = (char *)&data_dest.su_port; |
p = (char *)&data_dest.su_port; |
p[0] = $9; p[1] = $11; |
p[0] = $9.i; p[1] = $11.i; |
a = (char *)&data_dest.su_addr; |
a = (char *)&data_dest.su_addr; |
a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; |
a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; |
} |
} |
; |
; |
|
|
|
|
data_dest.su_len = sizeof(struct sockaddr_in); |
data_dest.su_len = sizeof(struct sockaddr_in); |
data_dest.su_family = AF_INET; |
data_dest.su_family = AF_INET; |
p = (char *)&data_dest.su_port; |
p = (char *)&data_dest.su_port; |
p[0] = $15; p[1] = $17; |
p[0] = $15.i; p[1] = $17.i; |
a = (char *)&data_dest.su_addr; |
a = (char *)&data_dest.su_addr; |
a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; |
a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; |
|
|
/* reject invalid LPRT command */ |
/* reject invalid LPRT command */ |
if ($1 != 4 || $3 != 4 || $13 != 2) |
if ($1.i != 4 || $3.i != 4 || $13.i != 2) |
memset(&data_dest, 0, sizeof(data_dest)); |
memset(&data_dest, 0, sizeof(data_dest)); |
} |
} |
; |
; |
|
|
NUMBER |
NUMBER |
{ |
{ |
#ifdef INET6 |
#ifdef INET6 |
char *a, *p; |
unsigned char buf[16]; |
|
|
memset(&data_dest, 0, sizeof(data_dest)); |
(void)memset(&data_dest, 0, sizeof(data_dest)); |
data_dest.su_len = sizeof(struct sockaddr_in6); |
data_dest.su_len = sizeof(struct sockaddr_in6); |
data_dest.su_family = AF_INET6; |
data_dest.su_family = AF_INET6; |
p = (char *)&data_dest.su_port; |
buf[0] = $39.i; buf[1] = $41.i; |
p[0] = $39; p[1] = $41; |
(void)memcpy(&data_dest.su_port, buf, |
a = (char *)&data_dest.si_su.su_sin6.sin6_addr; |
sizeof(data_dest.su_port)); |
a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; |
buf[0] = $5.i; buf[1] = $7.i; |
a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; |
buf[2] = $9.i; buf[3] = $11.i; |
a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; |
buf[4] = $13.i; buf[5] = $15.i; |
a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; |
buf[6] = $17.i; buf[7] = $19.i; |
|
buf[8] = $21.i; buf[9] = $23.i; |
|
buf[10] = $25.i; buf[11] = $27.i; |
|
buf[12] = $29.i; buf[13] = $31.i; |
|
buf[14] = $33.i; buf[15] = $35.i; |
|
(void)memcpy(&data_dest.si_su.su_sin6.sin6_addr, |
|
buf, sizeof(data_dest.si_su.su_sin6.sin6_addr)); |
if (his_addr.su_family == AF_INET6) { |
if (his_addr.su_family == AF_INET6) { |
/* XXX: more sanity checks! */ |
/* XXX: more sanity checks! */ |
data_dest.su_scope_id = his_addr.su_scope_id; |
data_dest.su_scope_id = his_addr.su_scope_id; |
|
|
memset(&data_dest, 0, sizeof(data_dest)); |
memset(&data_dest, 0, sizeof(data_dest)); |
#endif /* INET6 */ |
#endif /* INET6 */ |
/* reject invalid LPRT command */ |
/* reject invalid LPRT command */ |
if ($1 != 6 || $3 != 16 || $37 != 2) |
if ($1.i != 6 || $3.i != 16 || $37.i != 2) |
memset(&data_dest, 0, sizeof(data_dest)); |
memset(&data_dest, 0, sizeof(data_dest)); |
} |
} |
; |
; |
|
|
* others. |
* others. |
*/ |
*/ |
if (logged_in && $1 && *$1 == '~') { |
if (logged_in && $1 && *$1 == '~') { |
glob_t gl; |
char *path, *home, *result; |
int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; |
size_t len; |
|
|
|
path = strchr($1 + 1, '/'); |
|
if (path != NULL) |
|
*path++ = '\0'; |
if ($1[1] == '\0') |
if ($1[1] == '\0') |
$$ = xstrdup(homedir); |
home = homedir; |
else { |
else { |
memset(&gl, 0, sizeof(gl)); |
struct passwd *hpw; |
if (glob($1, flags, NULL, &gl) || |
|
gl.gl_pathc == 0) { |
if ((hpw = getpwnam($1 + 1)) != NULL) |
reply(550, "not found"); |
home = hpw->pw_dir; |
$$ = NULL; |
else |
} else |
home = $1; |
$$ = xstrdup(gl.gl_pathv[0]); |
} |
globfree(&gl); |
len = strlen(home) + 1; |
|
if (path != NULL) |
|
len += strlen(path) + 1; |
|
if ((result = malloc(len)) == NULL) |
|
fatal("Local resource failure: malloc"); |
|
strlcpy(result, home, len); |
|
if (path != NULL) { |
|
strlcat(result, "/", len); |
|
strlcat(result, path, len); |
} |
} |
|
$$ = result; |
free($1); |
free($1); |
} else |
} else |
$$ = $1; |
$$ = $1; |
|
|
* Convert a number that was read as decimal number |
* Convert a number that was read as decimal number |
* to what it would be if it had been read as octal. |
* to what it would be if it had been read as octal. |
*/ |
*/ |
dec = $1; |
dec = $1.i; |
multby = 1; |
multby = 1; |
ret = 0; |
ret = 0; |
while (dec) { |
while (dec) { |
|
|
|
|
decimal_integer |
decimal_integer |
: NUMBER |
: NUMBER |
|
{ |
|
$$ = $1.i; |
|
} |
; |
; |
|
|
check_login |
check_login |
|
|
#define SITECMD 7 /* SITE command */ |
#define SITECMD 7 /* SITE command */ |
#define NSTR 8 /* Number followed by a string */ |
#define NSTR 8 /* Number followed by a string */ |
#define NOARGS 9 /* No arguments allowed */ |
#define NOARGS 9 /* No arguments allowed */ |
|
#define EOLN 10 /* End of line */ |
|
|
struct tab cmdtab[] = { |
struct tab cmdtab[] = { |
/* From RFC 959, in order defined (5.3.1) */ |
/* From RFC 959, in order defined (5.3.1) */ |
{ "USER", USER, STR1, 1, "<sp> username" }, |
{ "USER", USER, STR1, 1, "<sp> username", 0, }, |
{ "PASS", PASS, ZSTR1, 1, "<sp> password" }, |
{ "PASS", PASS, ZSTR1, 1, "<sp> password", 0, }, |
{ "ACCT", ACCT, STR1, 0, "(specify account)" }, |
{ "ACCT", ACCT, STR1, 0, "(specify account)", 0, }, |
{ "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, |
{ "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]", 0, }, |
{ "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" }, |
{ "CDUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, |
{ "SMNT", SMNT, ARGS, 0, "(structure mount)" }, |
{ "SMNT", SMNT, ARGS, 0, "(structure mount)", 0, }, |
{ "QUIT", QUIT, NOARGS, 1, "(terminate service)" }, |
{ "QUIT", QUIT, NOARGS, 1, "(terminate service)", 0, }, |
{ "REIN", REIN, NOARGS, 0, "(reinitialize server state)" }, |
{ "REIN", REIN, NOARGS, 0, "(reinitialize server state)", 0, }, |
{ "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, |
{ "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5", 0, }, |
{ "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, |
{ "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2...", 0, }, |
{ "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, |
{ "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|", 0, }, |
{ "PASV", PASV, NOARGS, 1, "(set server in passive mode)" }, |
{ "PASV", PASV, NOARGS, 1, "(set server in passive mode)", 0, }, |
{ "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, |
{ "LPSV", LPSV, ARGS, 1, "(set server in passive mode)", 0, }, |
{ "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, |
{ "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]", 0, }, |
{ "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, |
{ "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]", 0, }, |
{ "STRU", STRU, ARGS, 1, "(specify file structure)" }, |
{ "STRU", STRU, ARGS, 1, "(specify file structure)", 0, }, |
{ "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, |
{ "MODE", MODE, ARGS, 1, "(specify transfer mode)", 0, }, |
{ "RETR", RETR, STR1, 1, "<sp> file-name" }, |
{ "RETR", RETR, STR1, 1, "<sp> file-name", 0, }, |
{ "STOR", STOR, STR1, 1, "<sp> file-name" }, |
{ "STOR", STOR, STR1, 1, "<sp> file-name", 0, }, |
{ "STOU", STOU, STR1, 1, "<sp> file-name" }, |
{ "STOU", STOU, STR1, 1, "<sp> file-name", 0, }, |
{ "APPE", APPE, STR1, 1, "<sp> file-name" }, |
{ "APPE", APPE, STR1, 1, "<sp> file-name", 0, }, |
{ "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, |
{ "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)", 0, }, |
{ "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, |
{ "REST", REST, ARGS, 1, "<sp> offset (restart command)", 0, }, |
{ "RNFR", RNFR, STR1, 1, "<sp> file-name" }, |
{ "RNFR", RNFR, STR1, 1, "<sp> file-name", 0, }, |
{ "RNTO", RNTO, STR1, 1, "<sp> file-name" }, |
{ "RNTO", RNTO, STR1, 1, "<sp> file-name", 0, }, |
{ "ABOR", ABOR, NOARGS, 1, "(abort operation)" }, |
{ "ABOR", ABOR, NOARGS, 4, "(abort operation)", 0, }, |
{ "DELE", DELE, STR1, 1, "<sp> file-name" }, |
{ "DELE", DELE, STR1, 1, "<sp> file-name", 0, }, |
{ "RMD", RMD, STR1, 1, "<sp> path-name" }, |
{ "RMD", RMD, STR1, 1, "<sp> path-name", 0, }, |
{ "MKD", MKD, STR1, 1, "<sp> path-name" }, |
{ "MKD", MKD, STR1, 1, "<sp> path-name", 0, }, |
{ "PWD", PWD, NOARGS, 1, "(return current directory)" }, |
{ "PWD", PWD, NOARGS, 1, "(return current directory)", 0, }, |
{ "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, |
{ "LIST", LIST, OSTR, 1, "[ <sp> path-name ]", 0, }, |
{ "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, |
{ "NLST", NLST, OSTR, 1, "[ <sp> path-name ]", 0, }, |
{ "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, |
{ "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]", 0, }, |
{ "SYST", SYST, NOARGS, 1, "(get type of operating system)" }, |
{ "SYST", SYST, NOARGS, 1, "(get type of operating system)", 0, }, |
{ "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, |
{ "STAT", STAT, OSTR, 4, "[ <sp> path-name ]", 0, }, |
{ "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, |
{ "HELP", HELP, OSTR, 1, "[ <sp> <string> ]", 0, }, |
{ "NOOP", NOOP, NOARGS, 2, "" }, |
{ "NOOP", NOOP, NOARGS, 2, "", 0, }, |
|
|
/* From RFC 2228, in order defined */ |
/* From RFC 2228, in order defined */ |
{ "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" }, |
{ "AUTH", AUTH, STR1, 1, "<sp> mechanism-name", 0, }, |
{ "ADAT", ADAT, STR1, 1, "<sp> base-64-data" }, |
{ "ADAT", ADAT, STR1, 1, "<sp> base-64-data", 0, }, |
{ "PROT", PROT, STR1, 1, "<sp> prot-code" }, |
{ "PROT", PROT, STR1, 1, "<sp> prot-code", 0, }, |
{ "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" }, |
{ "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer", 0, }, |
{ "CCC", CCC, NOARGS, 1, "(Disable data protection)" }, |
{ "CCC", CCC, NOARGS, 1, "(Disable data protection)", 0, }, |
{ "MIC", MIC, STR1, 1, "<sp> base64data" }, |
{ "MIC", MIC, STR1, 4, "<sp> base64data", 0, }, |
{ "CONF", CONF, STR1, 1, "<sp> base64data" }, |
{ "CONF", CONF, STR1, 4, "<sp> base64data", 0, }, |
{ "ENC", ENC, STR1, 1, "<sp> base64data" }, |
{ "ENC", ENC, STR1, 4, "<sp> base64data", 0, }, |
|
|
/* From RFC 2389, in order defined */ |
/* From RFC 2389, in order defined */ |
{ "FEAT", FEAT, NOARGS, 1, "(display extended features)" }, |
{ "FEAT", FEAT, NOARGS, 1, "(display extended features)", 0, }, |
{ "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" }, |
{ "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]", 0, }, |
|
|
/* from draft-ietf-ftpext-mlst-11 */ |
/* From RFC 3659, in order defined */ |
{ "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, |
{ "MDTM", MDTM, OSTR, 1, "<sp> path-name", 0, }, |
{ "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, |
{ "SIZE", SIZE, OSTR, 1, "<sp> path-name", 0, }, |
{ "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" }, |
{ "MLST", MLST, OSTR, 2, "[ <sp> path-name ]", 0, }, |
{ "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" }, |
{ "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]", 0, }, |
|
|
/* obsolete commands */ |
/* obsolete commands */ |
{ "MAIL", MAIL, OSTR, 0, "(mail to user)" }, |
{ "MAIL", MAIL, OSTR, 0, "(mail to user)", 0, }, |
{ "MLFL", MLFL, OSTR, 0, "(mail file)" }, |
{ "MLFL", MLFL, OSTR, 0, "(mail file)", 0, }, |
{ "MRCP", MRCP, STR1, 0, "(mail recipient)" }, |
{ "MRCP", MRCP, STR1, 0, "(mail recipient)", 0, }, |
{ "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, |
{ "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)", 0, }, |
{ "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, |
{ "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)", 0, }, |
{ "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, |
{ "MSND", MSND, OSTR, 0, "(mail send to terminal)", 0, }, |
{ "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, |
{ "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)", 0, }, |
{ "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" }, |
{ "XCUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, |
{ "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, |
{ "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]", 0, }, |
{ "XMKD", MKD, STR1, 1, "<sp> path-name" }, |
{ "XMKD", MKD, STR1, 1, "<sp> path-name", 0, }, |
{ "XPWD", PWD, NOARGS, 1, "(return current directory)" }, |
{ "XPWD", PWD, NOARGS, 1, "(return current directory)", 0, }, |
{ "XRMD", RMD, STR1, 1, "<sp> path-name" }, |
{ "XRMD", RMD, STR1, 1, "<sp> path-name", 0, }, |
|
|
{ NULL, 0, 0, 0, 0 } |
{ NULL, 0, 0, 0, 0, 0, } |
}; |
}; |
|
|
struct tab sitetab[] = { |
struct tab sitetab[] = { |
{ "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, |
{ "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name", 0, }, |
{ "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, |
{ "HELP", HELP, OSTR, 1, "[ <sp> <string> ]", 0, }, |
{ "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, |
{ "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]", 0, }, |
{ "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" }, |
{ "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]", 0, }, |
{ "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" }, |
{ "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]", 0, }, |
{ "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, |
{ "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]", 0, }, |
{ NULL, 0, 0, 0, NULL } |
{ NULL, 0, 0, 0, 0, 0, } |
}; |
}; |
|
|
static int check_write(const char *, int); |
|
static void help(struct tab *, const char *); |
|
static void port_check(const char *, int); |
|
static void toolong(int); |
|
static int yylex(void); |
|
|
|
extern int epsvall; |
|
|
|
/* |
/* |
* Check if a filename is allowed to be modified (isupload == 0) or |
* Check if a filename is allowed to be modified (isupload == 0) or |
* uploaded (isupload == 1), and if necessary, check the filename is `sane'. |
* uploaded (isupload == 1), and if necessary, check the filename is `sane'. |
|
* If the filename is NULL, fail. |
|
* If the filename is "", don't do the sane name check. |
*/ |
*/ |
static int |
static int |
check_write(const char *file, int isupload) |
check_write(const char *file, int isupload) |
Line 1289 check_write(const char *file, int isuplo |
|
Line 1330 check_write(const char *file, int isuplo |
|
reply(502, "No permission to use this command."); |
reply(502, "No permission to use this command."); |
return (0); |
return (0); |
} |
} |
|
|
/* checking sanenames */ |
/* checking sanenames */ |
if (CURCLASS_FLAGS_ISSET(sanenames)) { |
if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) { |
const char *p; |
const char *p; |
|
|
if (file[0] == '.') |
if (file[0] == '.') |
goto insane_name; |
goto insane_name; |
for (p = file; *p; p++) { |
for (p = file; *p; p++) { |
if (isalnum(*p) || *p == '-' || *p == '+' || |
if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' || |
*p == ',' || *p == '.' || *p == '_') |
*p == ',' || *p == '.' || *p == '_') |
continue; |
continue; |
insane_name: |
insane_name: |
Line 1320 lookup(struct tab *p, const char *cmd) |
|
Line 1362 lookup(struct tab *p, const char *cmd) |
|
#include <arpa/telnet.h> |
#include <arpa/telnet.h> |
|
|
/* |
/* |
* getline - a hacked up version of fgets to ignore TELNET escape codes. |
* get_line - a hacked up version of fgets to ignore TELNET escape codes. |
|
* `s' is the buffer to read into. |
|
* `n' is the 1 less than the size of the buffer, to allow trailing NUL |
|
* `iop' is the FILE to read from. |
|
* Returns 0 on success, -1 on EOF, -2 if the command was too long. |
*/ |
*/ |
char * |
int |
getline(char *s, int n, FILE *iop) |
get_line(char *s, int n, FILE *iop) |
{ |
{ |
int c; |
int c; |
char *cs; |
char *cs; |
Line 1334 getline(char *s, int n, FILE *iop) |
|
Line 1380 getline(char *s, int n, FILE *iop) |
|
*cs++ = tmpline[c]; |
*cs++ = tmpline[c]; |
if (tmpline[c] == '\n') { |
if (tmpline[c] == '\n') { |
*cs++ = '\0'; |
*cs++ = '\0'; |
if (debug) |
if (ftpd_debug) |
syslog(LOG_DEBUG, "command: %s", s); |
syslog(LOG_DEBUG, "command: %s", s); |
tmpline[0] = '\0'; |
tmpline[0] = '\0'; |
return(s); |
return(0); |
} |
} |
if (c == 0) |
if (c == 0) |
tmpline[0] = '\0'; |
tmpline[0] = '\0'; |
Line 1376 getline(char *s, int n, FILE *iop) |
|
Line 1422 getline(char *s, int n, FILE *iop) |
|
} |
} |
} |
} |
*cs++ = c; |
*cs++ = c; |
if (--n <= 0 || c == '\n') |
if (--n <= 0) { |
|
/* |
|
* If command doesn't fit into buffer, discard the |
|
* rest of the command and indicate truncation. |
|
* This prevents the command to be split up into |
|
* multiple commands. |
|
*/ |
|
if (ftpd_debug) |
|
syslog(LOG_DEBUG, |
|
"command too long, last char: %d", c); |
|
while (c != '\n' && (c = getc(iop)) != EOF) |
|
continue; |
|
return (-2); |
|
} |
|
if (c == '\n') |
break; |
break; |
} |
} |
if (c == EOF && cs == s) |
if (c == EOF && cs == s) |
return (NULL); |
return (-1); |
*cs++ = '\0'; |
*cs++ = '\0'; |
if (debug) { |
if (ftpd_debug) { |
if ((curclass.type != CLASS_GUEST && |
if ((curclass.type != CLASS_GUEST && |
strncasecmp(s, "PASS ", 5) == 0) || |
strncasecmp(s, "PASS ", 5) == 0) || |
strncasecmp(s, "ACCT ", 5) == 0) { |
strncasecmp(s, "ACCT ", 5) == 0) { |
Line 1402 getline(char *s, int n, FILE *iop) |
|
Line 1462 getline(char *s, int n, FILE *iop) |
|
syslog(LOG_DEBUG, "command: %.*s", len, s); |
syslog(LOG_DEBUG, "command: %.*s", len, s); |
} |
} |
} |
} |
return (s); |
return (0); |
} |
} |
|
|
static void |
void |
toolong(int signo) |
ftp_handle_line(char *cp) |
{ |
{ |
|
|
reply(421, |
cmdp = cp; |
"Timeout (%d seconds): closing control connection.", |
yyparse(); |
curclass.timeout); |
|
if (logging) |
|
syslog(LOG_INFO, "User %s timed out after %d seconds", |
|
(pw ? pw->pw_name : "unknown"), curclass.timeout); |
|
dologout(1); |
|
} |
} |
|
|
static int |
void |
|
ftp_loop(void) |
|
{ |
|
int ret; |
|
|
|
while (1) { |
|
(void) alarm(curclass.timeout); |
|
ret = get_line(cbuf, sizeof(cbuf)-1, stdin); |
|
(void) alarm(0); |
|
if (ret == -1) { |
|
reply(221, "You could at least say goodbye."); |
|
dologout(0); |
|
} else if (ret == -2) { |
|
reply(500, "Command too long."); |
|
} else { |
|
ftp_handle_line(cbuf); |
|
} |
|
} |
|
/*NOTREACHED*/ |
|
} |
|
|
|
int |
yylex(void) |
yylex(void) |
{ |
{ |
static int cpos, state; |
static int cpos, state; |
|
|
|
|
case CMD: |
case CMD: |
hasyyerrored = 0; |
hasyyerrored = 0; |
(void) signal(SIGALRM, toolong); |
if ((cp = strchr(cmdp, '\r'))) { |
(void) alarm(curclass.timeout); |
|
if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { |
|
reply(221, "You could at least say goodbye."); |
|
dologout(0); |
|
} |
|
(void) alarm(0); |
|
if ((cp = strchr(cbuf, '\r'))) { |
|
*cp = '\0'; |
*cp = '\0'; |
#if HAVE_SETPROCTITLE |
#if defined(HAVE_SETPROCTITLE) |
if (strncasecmp(cbuf, "PASS", 4) != 0 && |
if (strncasecmp(cmdp, "PASS", 4) != 0 && |
strncasecmp(cbuf, "ACCT", 4) != 0) |
strncasecmp(cmdp, "ACCT", 4) != 0) |
setproctitle("%s: %s", proctitle, cbuf); |
setproctitle("%s: %s", proctitle, cmdp); |
#endif /* HAVE_SETPROCTITLE */ |
#endif /* defined(HAVE_SETPROCTITLE) */ |
*cp++ = '\n'; |
*cp++ = '\n'; |
*cp = '\0'; |
*cp = '\0'; |
} |
} |
if ((cp = strpbrk(cbuf, " \n"))) |
if ((cp = strpbrk(cmdp, " \n"))) |
cpos = cp - cbuf; |
cpos = cp - cmdp; |
if (cpos == 0) |
if (cpos == 0) |
cpos = 4; |
cpos = 4; |
c = cbuf[cpos]; |
c = cmdp[cpos]; |
cbuf[cpos] = '\0'; |
cmdp[cpos] = '\0'; |
p = lookup(cmdtab, cbuf); |
p = lookup(cmdtab, cmdp); |
cbuf[cpos] = c; |
cmdp[cpos] = c; |
if (p != NULL) { |
if (p != NULL) { |
if (! CMD_IMPLEMENTED(p)) { |
if (is_oob && ! CMD_OOB(p)) { |
|
/* command will be handled in-band */ |
|
return (0); |
|
} else if (! CMD_IMPLEMENTED(p)) { |
reply(502, "%s command not implemented.", |
reply(502, "%s command not implemented.", |
p->name); |
p->name); |
hasyyerrored = 1; |
hasyyerrored = 1; |
break; |
break; |
} |
} |
state = p->state; |
state = p->state; |
yylval.s = p->name; |
yylval.cs = p->name; |
return (p->token); |
return (p->token); |
} |
} |
break; |
break; |
|
|
case SITECMD: |
case SITECMD: |
if (cbuf[cpos] == ' ') { |
if (cmdp[cpos] == ' ') { |
cpos++; |
cpos++; |
return (SP); |
return (SP); |
} |
} |
cp = &cbuf[cpos]; |
cp = &cmdp[cpos]; |
if ((cp2 = strpbrk(cp, " \n"))) |
if ((cp2 = strpbrk(cp, " \n"))) |
cpos = cp2 - cbuf; |
cpos = cp2 - cmdp; |
c = cbuf[cpos]; |
c = cmdp[cpos]; |
cbuf[cpos] = '\0'; |
cmdp[cpos] = '\0'; |
p = lookup(sitetab, cp); |
p = lookup(sitetab, cp); |
cbuf[cpos] = c; |
cmdp[cpos] = c; |
if (p != NULL) { |
if (p != NULL) { |
if (!CMD_IMPLEMENTED(p)) { |
if (!CMD_IMPLEMENTED(p)) { |
reply(502, "SITE %s command not implemented.", |
reply(502, "SITE %s command not implemented.", |
|
|
break; |
break; |
} |
} |
state = p->state; |
state = p->state; |
yylval.s = p->name; |
yylval.cs = p->name; |
return (p->token); |
return (p->token); |
} |
} |
break; |
break; |
|
|
case OSTR: |
case OSTR: |
if (cbuf[cpos] == '\n') { |
if (cmdp[cpos] == '\n') { |
state = CMD; |
state = EOLN; |
return (CRLF); |
return (CRLF); |
} |
} |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
|
|
case STR1: |
case STR1: |
case ZSTR1: |
case ZSTR1: |
dostr1: |
dostr1: |
if (cbuf[cpos] == ' ') { |
if (cmdp[cpos] == ' ') { |
cpos++; |
cpos++; |
state = state == OSTR ? STR2 : state+1; |
state = state == OSTR ? STR2 : state+1; |
return (SP); |
return (SP); |
|
|
break; |
break; |
|
|
case ZSTR2: |
case ZSTR2: |
if (cbuf[cpos] == '\n') { |
if (cmdp[cpos] == '\n') { |
state = CMD; |
state = EOLN; |
return (CRLF); |
return (CRLF); |
} |
} |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
|
|
case STR2: |
case STR2: |
cp = &cbuf[cpos]; |
cp = &cmdp[cpos]; |
n = strlen(cp); |
n = strlen(cp); |
cpos += n - 1; |
cpos += n - 1; |
/* |
/* |
* Make sure the string is nonempty and \n terminated. |
* Make sure the string is nonempty and \n terminated. |
*/ |
*/ |
if (n > 1 && cbuf[cpos] == '\n') { |
if (n > 1 && cmdp[cpos] == '\n') { |
cbuf[cpos] = '\0'; |
cmdp[cpos] = '\0'; |
yylval.s = xstrdup(cp); |
yylval.s = ftpd_strdup(cp); |
cbuf[cpos] = '\n'; |
cmdp[cpos] = '\n'; |
state = ARGS; |
state = ARGS; |
return (STRING); |
return (STRING); |
} |
} |
break; |
break; |
|
|
case NSTR: |
case NSTR: |
if (cbuf[cpos] == ' ') { |
if (cmdp[cpos] == ' ') { |
cpos++; |
cpos++; |
return (SP); |
return (SP); |
} |
} |
if (isdigit(cbuf[cpos])) { |
if (isdigit((unsigned char)cmdp[cpos])) { |
cp = &cbuf[cpos]; |
cp = &cmdp[cpos]; |
while (isdigit(cbuf[++cpos])) |
while (isdigit((unsigned char)cmdp[++cpos])) |
; |
; |
c = cbuf[cpos]; |
c = cmdp[cpos]; |
cbuf[cpos] = '\0'; |
cmdp[cpos] = '\0'; |
yylval.i = atoi(cp); |
yylval.u.i = atoi(cp); |
cbuf[cpos] = c; |
cmdp[cpos] = c; |
state = STR1; |
state = STR1; |
return (NUMBER); |
return (NUMBER); |
} |
} |
|
|
goto dostr1; |
goto dostr1; |
|
|
case ARGS: |
case ARGS: |
if (isdigit(cbuf[cpos])) { |
if (isdigit((unsigned char)cmdp[cpos])) { |
cp = &cbuf[cpos]; |
cp = &cmdp[cpos]; |
while (isdigit(cbuf[++cpos])) |
while (isdigit((unsigned char)cmdp[++cpos])) |
; |
; |
c = cbuf[cpos]; |
c = cmdp[cpos]; |
cbuf[cpos] = '\0'; |
cmdp[cpos] = '\0'; |
yylval.i = atoi(cp); |
yylval.u.i = atoi(cp); |
cbuf[cpos] = c; |
yylval.u.ll = STRTOLL(cp, (char **)NULL, 10); |
|
cmdp[cpos] = c; |
return (NUMBER); |
return (NUMBER); |
} |
} |
if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 |
if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0 |
&& !isalnum(cbuf[cpos + 3])) { |
&& !isalnum((unsigned char)cmdp[cpos + 3])) { |
yylval.s = xstrdup("ALL"); |
|
cpos += 3; |
cpos += 3; |
return ALL; |
return (ALL); |
} |
} |
switch (cbuf[cpos++]) { |
switch (cmdp[cpos++]) { |
|
|
case '\n': |
case '\n': |
state = CMD; |
state = EOLN; |
return (CRLF); |
return (CRLF); |
|
|
case ' ': |
case ' ': |
|
|
break; |
break; |
|
|
case NOARGS: |
case NOARGS: |
if (cbuf[cpos] == '\n') { |
if (cmdp[cpos] == '\n') { |
state = CMD; |
state = EOLN; |
return (CRLF); |
return (CRLF); |
} |
} |
c = cbuf[cpos]; |
c = cmdp[cpos]; |
cbuf[cpos] = '\0'; |
cmdp[cpos] = '\0'; |
reply(501, "'%s' command does not take any arguments.", cbuf); |
reply(501, "'%s' command does not take any arguments.", cmdp); |
hasyyerrored = 1; |
hasyyerrored = 1; |
cbuf[cpos] = c; |
cmdp[cpos] = c; |
break; |
break; |
|
|
|
case EOLN: |
|
state = CMD; |
|
return (0); |
|
|
default: |
default: |
fatal("Unknown state in scanner."); |
fatal("Unknown state in scanner."); |
} |
} |
yyerror(NULL); |
yyerror(NULL); |
state = CMD; |
state = CMD; |
longjmp(errcatch, 0); |
return (0); |
/* NOTREACHED */ |
|
} |
} |
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
void |
void |
yyerror(char *s) |
yyerror(const char *s) |
{ |
{ |
char *cp; |
char *cp; |
|
|
if (hasyyerrored) |
if (hasyyerrored || is_oob) |
return; |
return; |
if ((cp = strchr(cbuf,'\n')) != NULL) |
if ((cp = strchr(cmdp,'\n')) != NULL) |
*cp = '\0'; |
*cp = '\0'; |
reply(500, "'%s': command not understood.", cbuf); |
reply(500, "'%s': command not understood.", cmdp); |
hasyyerrored = 1; |
hasyyerrored = 1; |
} |
} |
|
|
Line 1673 help(struct tab *ctab, const char *s) |
|
Line 1748 help(struct tab *ctab, const char *s) |
|
{ |
{ |
struct tab *c; |
struct tab *c; |
int width, NCMDS; |
int width, NCMDS; |
char *type; |
const char *htype; |
|
|
if (ctab == sitetab) |
if (ctab == sitetab) |
type = "SITE "; |
htype = "SITE "; |
else |
else |
type = ""; |
htype = ""; |
width = 0, NCMDS = 0; |
width = 0, NCMDS = 0; |
for (c = ctab; c->name != NULL; c++) { |
for (c = ctab; c->name != NULL; c++) { |
int len = strlen(c->name); |
int len = strlen(c->name); |
Line 1693 help(struct tab *ctab, const char *s) |
|
Line 1768 help(struct tab *ctab, const char *s) |
|
int columns, lines; |
int columns, lines; |
|
|
reply(-214, "%s", ""); |
reply(-214, "%s", ""); |
reply(0, "The following %scommands are recognized.", type); |
reply(0, "The following %scommands are recognized.", htype); |
reply(0, "(`-' = not implemented, `+' = supports options)"); |
reply(0, "(`-' = not implemented, `+' = supports options)"); |
columns = 76 / width; |
columns = 76 / width; |
if (columns == 0) |
if (columns == 0) |
Line 1728 help(struct tab *ctab, const char *s) |
|
Line 1803 help(struct tab *ctab, const char *s) |
|
} |
} |
c = lookup(ctab, s); |
c = lookup(ctab, s); |
if (c == (struct tab *)0) { |
if (c == (struct tab *)0) { |
reply(502, "Unknown command %s.", s); |
reply(502, "Unknown command '%s'.", s); |
return; |
return; |
} |
} |
if (CMD_IMPLEMENTED(c)) |
if (CMD_IMPLEMENTED(c)) |
reply(214, "Syntax: %s%s %s", type, c->name, c->help); |
reply(214, "Syntax: %s%s %s", htype, c->name, c->help); |
else |
else |
reply(214, "%s%-*s\t%s; not implemented.", type, width, |
reply(504, "%s%-*s\t%s; not implemented.", htype, width, |
c->name, c->help); |
c->name, c->help); |
} |
} |
|
|
Line 1746 help(struct tab *ctab, const char *s) |
|
Line 1821 help(struct tab *ctab, const char *s) |
|
static void |
static void |
port_check(const char *cmd, int family) |
port_check(const char *cmd, int family) |
{ |
{ |
|
char h1[NI_MAXHOST], h2[NI_MAXHOST]; |
|
char s1[NI_MAXHOST], s2[NI_MAXHOST]; |
|
#ifdef NI_WITHSCOPEID |
|
const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; |
|
#else |
|
const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; |
|
#endif |
|
|
if (epsvall) { |
if (epsvall) { |
reply(501, "%s disallowed after EPSV ALL", cmd); |
reply(501, "%s disallowed after EPSV ALL", cmd); |
Line 1763 port_check(const char *cmd, int family) |
|
Line 1845 port_check(const char *cmd, int family) |
|
|
|
/* be paranoid, if told so */ |
/* be paranoid, if told so */ |
if (CURCLASS_FLAGS_ISSET(checkportcmd)) { |
if (CURCLASS_FLAGS_ISSET(checkportcmd)) { |
if ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || |
|
(data_dest.su_len != his_addr.su_len)) |
|
goto port_check_fail; |
|
switch (data_dest.su_family) { |
|
case AF_INET: |
|
if (memcmp(&data_dest.su_addr, &his_addr.su_addr, |
|
data_dest.su_len) != 0) |
|
goto port_check_fail; |
|
break; |
|
#ifdef INET6 |
#ifdef INET6 |
case AF_INET6: |
/* |
if (memcmp(&data_dest.su_6addr, &his_addr.su_6addr, |
* be paranoid, there are getnameinfo implementation that does |
sizeof(data_dest.su_6addr)) != 0) |
* not present scopeid portion |
goto port_check_fail; |
*/ |
if (data_dest.su_scope_id != his_addr.su_scope_id) |
if (data_dest.su_family == AF_INET6 && |
goto port_check_fail; |
data_dest.su_scope_id != his_addr.su_scope_id) |
break; |
goto port_check_fail; |
#endif |
#endif |
default: |
|
|
if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len, |
|
h1, sizeof(h1), s1, sizeof(s1), niflags)) |
|
goto port_check_fail; |
|
if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, |
|
h2, sizeof(h2), s2, sizeof(s2), niflags)) |
|
goto port_check_fail; |
|
|
|
if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0) |
goto port_check_fail; |
goto port_check_fail; |
} |
|
} |
} |
|
|
usedefault = 0; |
usedefault = 0; |