struct handler {
char cmd_name[6];
char add_cmlen; /* =1 if the command takes an argument */
- int (*callback)(struct conn * const);
+ void (*callback)(struct conn * const);
char min_auth;
#if !WANT_NONROOT
char do_setuid; /* =1 if root is not *really* needed */
* authentication work. User names are limited to 16
* characters, by force...
*/
-int cmd_user(struct conn * const c)
+void cmd_user(struct conn * const c)
{
strncpy(c->username, c->recv_buf, 16);
c->username[16] = 0;
numeric(c, 331, "Password required for %s.", c->username);
c->auth = 2;
}
- return 1;
}
/*
* don't even support PAM or real shadow passwords (with
* expiry etc) yet...
*/
-int cmd_pass(struct conn * const c)
+void cmd_pass(struct conn * const c)
{
#if WANT_NONROOT
c->auth = nr_userinfo(c->username, &c->uid, c->curr_dir, c->root_dir,
) {
c->auth = 0;
} else {
- c->auth = 3;
+ c->auth = 4;
}
}
#endif /* !WANT_NONROOT */
chdir(c->curr_dir);
dump_file(c, 230, "welcome.msg");
#endif
+ /* Have a different message for anonymous users? */
numeric(c, 230, "User logged in.");
}
- return 1;
}
/*
* I feel that the RFC959 intention is having it _before_
* USER/PASS. Therefore, this one runs with root privilegies :-)
*/
-int cmd_acct(struct conn * const c)
+void cmd_acct(struct conn * const c)
{
numeric(c, 202, "ACCT ignored OK -- not applicable on this system.");
- return 1;
}
/*
* the whole way), in case there are some weird overflows
* somewhere.
*/
-int cmd_port(struct conn * const c)
+void cmd_port(struct conn * const c)
{
short int a0, a1, a2, a3, p0, p1;
int i, sock, err;
if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
numeric(c, 500, "Sorry, only one transfer at a time.");
- return 1;
+ return;
}
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- TRAP_ERROR(sock == -1, 500, return 1);
+ TRAP_ERROR(sock == -1, 500, return);
destroy_ftran(c->transfer);
c->transfer = f = alloc_new_ftran(sock, c);
/* bind to own address, port 20 (FTP data) */
tmp = sizeof(sin);
err = getsockname(c->sock, (struct sockaddr *)&sin, &tmp);
- TRAP_ERROR(err == -1, 500, return 1);
+ TRAP_ERROR(err == -1, 500, return);
sin.sin_port = FTP_PORT - 1;
numeric(c, 200, "PORT command OK.");
#endif
bind(sock, (struct sockaddr *)&sin, sizeof(sin));
#if !WANT_NONROOT
- seteuid(c->uid);
setegid(c->gid);
+ seteuid(c->uid);
#endif
f->sin.sin_family = AF_INET;
i = 1;
ioctl(f->sock, FIONBIO, &one);
}
- return 1;
}
/*
* cmd_pasv(): Handles the PASV command, and sets up the data socket.
* Uses port numbers > 1024, since it doesn't run as root.
*/
-int cmd_pasv(struct conn * const c)
+void cmd_pasv(struct conn * const c)
{
struct ftran *f;
int tmp, sock, err;
if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
numeric(c, 503, "Sorry, only one transfer at once.");
- return 1;
+ return;
}
destroy_ftran(c->transfer);
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- TRAP_ERROR(sock == -1, 500, return 1);
+ TRAP_ERROR(sock == -1, 500, return);
err = add_fd(sock, POLLIN);
- TRAP_ERROR(err != 0, 501, return 1);
+ TRAP_ERROR(err != 0, 501, return);
c->transfer = f = alloc_new_ftran(sock, c);
/* setup socket */
tmp = sizeof(addr);
err = getsockname(c->sock, (struct sockaddr *)&addr, &tmp);
- TRAP_ERROR(err == -1, 500, return 1);
+ TRAP_ERROR(err == -1, 500, return);
addr.sin_port = 0; /* let the system choose */
err = bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr));
- TRAP_ERROR(err == -1, 500, return 1);
+ TRAP_ERROR(err == -1, 500, return);
tmp = sizeof(addr);
err = getsockname(sock, (struct sockaddr *)&addr, &tmp);
- TRAP_ERROR(err == -1, 500, return 1);
+ TRAP_ERROR(err == -1, 500, return);
err = listen(f->sock, 1);
- TRAP_ERROR(err == -1, 500, return 1);
+ TRAP_ERROR(err == -1, 500, return);
f->state = 1;
numeric(c, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)",
(htonl(addr.sin_addr.s_addr) & 0x000000ff),
(htons(addr.sin_port) & 0xff00) >> 8,
(htons(addr.sin_port) & 0x00ff));
- return 1;
}
/*
* /betaftpd.users file, if you use nonroot. If not, it's a bug.
* Try to get it _reproducible_, and mail it to me.
*/
-int cmd_pwd(struct conn * const c)
+void cmd_pwd(struct conn * const c)
{
char temp[512], *cdir = NULL;
if (cdir != NULL) {
numeric(c, 257, "\"%s\" is current working directory.", cdir);
}
- return 1;
}
/*
char *cdir = NULL;
strcpy(retbuf, dir);
+
if (strncmp(retbuf, c->root_dir, strlen(c->root_dir)) != 0) {
numeric(c, 550, "curr_dir is outside root_dir, please contact site administrator.");
return NULL;
* cmd_cwd(): Handles CWD command (change working directory). Uses
* cmd_cwd_internal() (see below).
*/
-int cmd_cwd(struct conn * const c)
+void cmd_cwd(struct conn * const c)
{
cmd_cwd_internal(c, c->recv_buf);
- return 1;
}
/*
* an error, instead of just staying in the root directory (as
* the OS and thus wu-ftpd does).
*/
-int cmd_cdup(struct conn * const c)
+void cmd_cdup(struct conn * const c)
{
cmd_cwd_internal(c, "..");
- return 1;
}
/*
* sending functions to start at the correct number. We should
* perhaps add some better error checking to this?
*/
-int cmd_rest(struct conn * const c)
+void cmd_rest(struct conn * const c)
{
c->rest_pos = abs(atoi(c->recv_buf));
numeric(c, 350, "Setting resume at %u bytes.", c->rest_pos);
- return 1;
}
/*
* connection occurs (or succeeds, if we're using PORT mode),
* the actual file transfer begins.
*/
-int cmd_retr(struct conn * const c)
+void cmd_retr(struct conn * const c)
{
struct ftran *f = c->transfer;
if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
numeric(c, 425, "No data connection set up; please use PASV or PORT.");
- return 1;
+ return;
}
#if WANT_ASCII
if ((c->rest_pos > 0) && (c->ascii_mode == 1)) {
numeric(c, 500, "Cannot resume while in ASCII mode.");
- return 1;
+ return;
}
#endif
#endif
prepare_for_transfer(f);
}
- return 1;
}
#if WANT_UPLOAD
* cmd_stor(): Handles the STOR command (upload file). Pushes the
* work down to do_store(), below.
*/
-int cmd_stor(struct conn * const c)
+void cmd_stor(struct conn * const c)
{
do_store(c, 0);
- return 1;
}
/*
* cmd_appe(): Handles the APPE command (append to file). Pushes
* the work down to do_store(), below.
*/
-int cmd_appe(struct conn * const c)
+void cmd_appe(struct conn * const c)
{
do_store(c, 1);
- return 1;
}
/*
* size of all the files in the directory, rather how
* much space the directory inode uses.
*/
-int cmd_size(struct conn * const c)
+void cmd_size(struct conn * const c)
{
#if WANT_ASCII
if (c->ascii_mode) {
numeric(c, 550, "SIZE not available in ASCII mode.");
- return 1;
+ return;
}
#endif
{
const char * const fname = translate_path(c, c->recv_buf);
struct stat buf;
- TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1);
+ TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return);
numeric(c, 213, "%lu", (unsigned long)(buf.st_size));
- return 1;
}
}
* date/time of a file. See the comments on cmd_size(),
* above.
*/
-int cmd_mdtm(struct conn * const c)
+void cmd_mdtm(struct conn * const c)
{
const char * const fname = translate_path(c, c->recv_buf);
struct stat buf;
struct tm *m;
- TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1);
+ TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return);
m = gmtime(&(buf.st_mtime)); /* at least wu-ftpd does it in GMT */
numeric(c, 213, "%u%02u%02u%02u%02u%02u", m->tm_year + 1900,
m->tm_mon + 1, m->tm_mday, m->tm_hour, m->tm_min, m->tm_sec);
- return 1;
}
/*
* cmd_abor(): Handle the ABOR command (abort a file transfer). This should
* be clean enough, but isn't tested extensively.
*/
-int cmd_abor(struct conn * const c)
+void cmd_abor(struct conn * const c)
{
if (c->transfer != NULL) {
numeric(c, 426, "File transfer aborted.");
destroy_ftran(c->transfer);
}
numeric(c, 226, "ABOR command processed OK.");
- return 1;
}
/*
* cmd_dele(): Handle the DELE command (delete a file).
*/
-int cmd_dele(struct conn * const c)
+void cmd_dele(struct conn * const c)
{
const char * const fname = translate_path(c, c->recv_buf);
- TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return 1);
+ TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return);
numeric(c, 250, "File deleted OK.");
- return 1;
}
/*
* cmd_rnfr(): Handle the RNFR command (take a filename to rename from).
*/
-int cmd_rnfr(struct conn * const c)
+void cmd_rnfr(struct conn * const c)
{
const char * const fname = translate_path(c, c->recv_buf);
char cwd[256];
struct stat buf;
c->rename_from[0] = '\0';
- if (fname == NULL) return 1;
+ if (fname == NULL) return;
getcwd(cwd, 256);
snprintf(c->rename_from, 256, "%s/%s", cwd, fname);
/* Just check that the file exists. */
- TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, c->rename_from[0] = '\0'; return 1);
+ TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, c->rename_from[0] = '\0'; return);
numeric(c, 350, "File exists, send RNTO.");
- return 1;
}
/*
* cmd_rnto(): Handle the RNTO command (do the actual renaming).
*/
-int cmd_rnto(struct conn * const c)
+void cmd_rnto(struct conn * const c)
{
const char * const fname = translate_path(c, c->recv_buf);
- if (fname == NULL) return 1;
+ if (fname == NULL) return;
if (c->rename_from[0] == '\0') {
numeric(c, 503, "Please send RNFR first.");
- return 1;
+ return;
}
- TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, c->rename_from[0] = '\0'; return 1);
+ TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, c->rename_from[0] = '\0'; return);
c->rename_from[0] = '\0';
numeric(c, 250, "File renamed successfully.");
- return 1;
}
/*
* easy :-) (This code isn't quite easy to understand, because
* temp2 is used twice, in two different roles.)
*/
-int cmd_mkd(struct conn * const c)
+void cmd_mkd(struct conn * const c)
{
const char * const fname = translate_path(c, c->recv_buf);
char temp[512], temp2[1024], *cdir;
int i, j;
- TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return 1);
+ TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return);
chdir(fname);
getcwd(temp2, 512);
cdir = do_pwd(c, temp, temp2);
+ if (do_pwd == NULL) return;
+
/* double the quotes in the output */
for (i = 0, j = 0; i <= strlen(cdir); i++, j++) {
temp2[j] = cdir[i];
}
}
numeric(c, 257, "\"%s\" created.", temp2);
- return 1;
}
/*
* cmd_rmd(): Handle the RMD/XRMD command. Works just like DELE, only for
* directories.
*/
-int cmd_rmd(struct conn * const c)
+void cmd_rmd(struct conn * const c)
{
const char * const fname = translate_path(c, c->recv_buf);
- TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return 1);
+ TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return);
numeric(c, 250, "Directory deleted.");
- return 1;
}
/*
* to the full-screen mode, but close to no FTP clients send this
* command, and it would touch too much code.
*/
-int cmd_allo(struct conn * const c)
+void cmd_allo(struct conn * const c)
{
numeric(c, 202, "No storage allocation necessary.");
- return 1;
}
/*
"Waiting for e-mail address",
"Waiting for password",
"Logged in",
- "Waiting for password", /* actually non-existant user */
+ "Logged in", /* non-anonymous */
};
char ftran_state[6][42] = {
};
#endif
-int cmd_stat(struct conn * const c)
+void cmd_stat(struct conn * const c)
{
#if WANT_STAT
char buf[1024];
err = send(c->sock, buf, i, 0);
if (err == -1 && errno == EPIPE) {
- destroy_conn(c);
- return 0;
+ this->free_me = 1;
}
#else
numeric(c, 502, "STAT command disabled for security reasons.");
#endif
- return 1;
}
#if HAVE_MMAP
* long listing (of type `ls -l'). The listing work is
* done by do_listing(), below.
*/
-int cmd_list(struct conn * const c)
+void cmd_list(struct conn * const c)
{
struct list_options lo;
lo.classify = 0;
do_listing(c, &lo);
- return 1;
}
/*
* FTP clients don't have a clue about what they send out).
* The listing work is done by do_listing(), below.
*/
-int cmd_nlst(struct conn * const c)
+void cmd_nlst(struct conn * const c)
{
struct list_options lo;
lo.classify = 0;
do_listing(c, &lo);
- return 1;
}
/*
* cmd_noop(): Handles the NOOP command. Does nothing, doesn't even
* reset the timeout.
*/
-int cmd_noop(struct conn * const c)
+void cmd_noop(struct conn * const c)
{
numeric(c, 200, "NOOP command successful.");
- return 1;
}
/*
* cmd_syst(): Handles the SYST command. Returns the system identification.
*/
-int cmd_syst(struct conn * const c)
+void cmd_syst(struct conn * const c)
{
numeric(c, 215, "UNIX Type: L%u", NBBY);
- return 1;
}
/*
* cmd_type(): Handles the TYPE command.
*/
-int cmd_type(struct conn * const c)
+void cmd_type(struct conn * const c)
{
#if WANT_ASCII
c->recv_buf[0] &= (255-32); /* convert to upper case */
#else
numeric(c, 200, "TYPE ignored (always I)");
#endif
- return 1;
}
/*
* cmd_mode(): Handles the MODE command. Only stream mode is supported.
*/
-int cmd_mode(struct conn * const c)
+void cmd_mode(struct conn * const c)
{
c->recv_buf[0] &= (255-32); /* convert to upper case */
if (c->recv_buf[0] == 'S') {
} else {
numeric(c, 504, "Unknown mode.");
}
- return 1;
}
/*
* cmd_stru(): Handles the STRU command. Only file mode is supported.
*/
-int cmd_stru(struct conn * const c)
+void cmd_stru(struct conn * const c)
{
c->recv_buf[0] &= (255-32); /* convert to upper case */
if (c->recv_buf[0] == 'F') {
} else {
numeric(c, 504, "Unknown structure.");
}
- return 1;
}
/*
* like an error, since the error code is intended for helpful
* messages? :-)
*/
-int cmd_help(struct conn * const c)
+void cmd_help(struct conn * const c)
{
numeric(c, 414, "Sorry, no detailed help; use standard FTP commands.");
- return 1;
}
/*
* cmd_quit(): Handles the QUIT command, which shuts down the control
* and data sockets.
*/
-int cmd_quit(struct conn * const c)
+void cmd_quit(struct conn * const c)
{
numeric(c, 221, "Have a nice day!");
- destroy_conn(c);
- return 0;
+ c->free_me = 1;
}
/*
* copied directly from alloc_new_conn() -- perhaps we should
* modularize this?
*/
-int cmd_rein(struct conn * const c)
+void cmd_rein(struct conn * const c)
{
destroy_ftran(c->transfer);
c->buf_len = c->auth = c->rest_pos = 0;
time(&(c->last_transfer));
numeric(c, 220, "BetaFTPD " VERSION " ready.");
-
- return 1;
}
#if DOING_PROFILING
* down without clearing any sockets etc. In other words:
* Don't use it on a production site.
*/
-int cmd_exit(struct conn * const c)
+void cmd_exit(struct conn * const c)
{
while (first_conn->next_conn)
destroy_conn(first_conn->next_conn);
#if !WANT_NONROOT
if (h->do_setuid) {
- seteuid(c->uid);
setegid(c->gid);
+ seteuid(c->uid);
} else {
seteuid(getuid());
setegid(getgid());
schar = c->recv_buf[cmlen];
c->recv_buf[cmlen] = 0;
-
- /* result of zero means the connection is freed */
- if (h->callback(c)) {
+
+ message_buf[0] = '\0';
+ h->callback(c);
+ if (message_buf[0] != '\0') {
+ /* send any feedback we might have */
+ int err = send(c->sock, message_buf, strlen(message_buf), 0);
+ if (err == -1 && errno == EPIPE) {
+ c->free_me = 1;
+ }
+ }
+
+ if (!c->free_me) {
c->recv_buf[cmlen] = schar;
#if !WANT_NONROOT
if (h->do_setuid) {
}
} while ((++h)->callback != NULL);
- numeric(c, 500, "Sorry, no such command.");
remove_bytes(c, cmlen);
+ {
+ char error[] = "500 Sorry, no such command.\r\n";
+ if (send(c->sock, error, strlen(error), 0) == -1 && errno == EPIPE)
+ destroy_conn(c);
+ }
}
/*
case 'F':
lo->classify = 1;
break;
- case ' ':
+ case '\0':
fptr = optr + 1;
*(optr--) = 0;
break;
} else {
fptr = c->recv_buf;
}
-
+
/* then we chdir to the dir in fptr (if any) */
tmp = fptr ? strrchr(fptr, '/') : NULL;
if (tmp != NULL) {