⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cmds.c

📁 ftp服务器源程序
💻 C
📖 第 1 页 / 共 4 页
字号:
/*  cmds.c: BetaFTPD command handlers    Copyright (C) 1999-2000 Steinar H. Gunderson    This program is is free software; you can redistribute it and/or modify    it under the terms of the GNU General Public License, version 2 if the    License as published by the Free Software Foundation.    This program is distributed in the hope that it will be useful,    but WITHOUT ANY WARRANTY; without even the implied warranty of    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    GNU General Public License for more details.    You should have received a copy of the GNU General Public License    along with this program; if not, write to the Free Software    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/#define _GNU_SOURCE#if HAVE_CONFIG_H#include <config.h>#endif#if HAVE_STROPTS_H#include <stropts.h>#endif#if HAVE_SYS_CONF_H#include <sys/conf.h>#endif#if HAVE_DIRENT_H#include <dirent.h>#endif#if HAVE_CRYPT_H#include <crypt.h>#endif#if HAVE_ERRNO_H#include <errno.h>#endif#if HAVE_GLOB_H#include <glob.h>#endif#if HAVE_STDIO_H#include <stdio.h>#endif#if HAVE_STDLIB_H#include <stdlib.h>#endif#if HAVE_STRING_H#include <string.h>#endif#if HAVE_STRINGS_H#include <strings.h>#endif#if HAVE_UNISTD_H#include <unistd.h>#endif#if HAVE_TIME_H#include <time.h>#endif#if HAVE_FCNTL_H#include <fcntl.h>#endif#if HAVE_GRP_H#include <grp.h>#endif#if HAVE_SYS_IOCTL_H#include <sys/ioctl.h>#endif#if HAVE_SYS_STAT_H#include <sys/stat.h>#endif#if HAVE_SYS_PARAM_H#include <sys/param.h>#endif#if HAVE_STROPTS_H#include <stropts.h>#endif#if HAVE_SYS_CONF_H#include <sys/conf.h>#endif#if HAVE_SHADOW_H#include <shadow.h>#endif#if HAVE_SYS_FILIO_H#include <sys/filio.h>#endif#if HAVE_SYS_POLL_H#include <sys/poll.h>#endif#if WANT_NONROOT#define NO_SETUID#define DO_SETUID#else#define NO_SETUID ,0#define DO_SETUID ,1#endif#ifndef NBBY#define NBBY 8#endif#include <ftpd.h>#include <cmds.h>#include <nonroot.h>#if WANT_DCACHE#include <dcache.h>#endif#define lstat statextern struct conn *first_conn;#if WANT_DCACHEextern struct dcache *first_dcache;#endif#if HAVE_POLLextern struct pollfd fds[];#elseextern fd_set master_fds, master_send_fds;#endifstruct handler {	char cmd_name[6];	char add_cmlen;		/* =1 if the command takes an argument */	int (*callback)(struct conn * const);	char min_auth;#if !WANT_NONROOT	char do_setuid;		/* =1 if root is not *really* needed */#endif};static const struct handler handler_table[] = {	{ "user ", 1, cmd_user, 0	NO_SETUID },	{ "pass ", 1, cmd_pass, 1	NO_SETUID },	{ "retr ", 1, cmd_retr, 3	DO_SETUID },	{ "acct ", 1, cmd_acct, 0	NO_SETUID },	{ "port ", 1, cmd_port, 3	DO_SETUID },	{ "pasv" , 0, cmd_pasv, 3	DO_SETUID },	{ "pwd"  , 0, cmd_pwd,  3	DO_SETUID },	{ "cwd " , 1, cmd_cwd,  3	DO_SETUID },	{ "cdup" , 0, cmd_cdup, 3	DO_SETUID },	{ "rest ", 1, cmd_rest, 3	DO_SETUID },	{ "list" , 0, cmd_list, 3	DO_SETUID },	{ "nlst" , 0, cmd_nlst, 3	DO_SETUID },	{ "type ", 1, cmd_type, 3	DO_SETUID },	{ "mode ", 1, cmd_mode, 3	DO_SETUID },	{ "stru ", 1, cmd_stru, 3	DO_SETUID },	{ "size ", 1, cmd_size, 3	DO_SETUID },	{ "mdtm ", 1, cmd_mdtm, 3	DO_SETUID },	{ "abor" , 0, cmd_abor, 3	DO_SETUID },	{ "dele ", 1, cmd_dele, 3	DO_SETUID },	{ "rnfr ", 1, cmd_rnfr, 3	DO_SETUID },	{ "rnto ", 1, cmd_rnto, 3	DO_SETUID },	{ "mkd " , 1, cmd_mkd,  3	DO_SETUID },	{ "rmd " , 1, cmd_rmd,  3	DO_SETUID },	{ "allo ", 1, cmd_allo, 3	DO_SETUID },	{ "stat" , 0, cmd_stat, 0	NO_SETUID },	{ "noop" , 0, cmd_noop, 0	DO_SETUID },	{ "syst" , 0, cmd_syst, 0	DO_SETUID },	{ "help" , 0, cmd_help, 0	NO_SETUID },	{ "quit" , 0, cmd_quit, 0	DO_SETUID },	{ "rein" , 0, cmd_rein, 0	DO_SETUID },	/* deprecated forms */	{ "xcup" , 0, cmd_cdup, 3	DO_SETUID },	{ "xcwd ", 1, cmd_cwd,  3	DO_SETUID },	{ "xpwd" , 0, cmd_pwd,  3	DO_SETUID },	{ "xmkd ", 1, cmd_mkd,  3	DO_SETUID },	{ "xrmd ", 1, cmd_rmd,  3	DO_SETUID },#if WANT_UPLOAD	{ "stor ", 1, cmd_stor, 3	DO_SETUID },	{ "appe ", 1, cmd_appe, 3	DO_SETUID },#endif#if DOING_PROFILING#warning Use DOING_PROFILING with caution, and NEVER on a production server! :-)	{ "exit",  0, cmd_exit, 0	NO_SETUID },#endif	{ ""    ,  0, NULL,	0	NO_SETUID }};/* * do_chdir():	Does a chdir() to newd on c, staying inside the *		limits of root_dir. Use this instead of a chdir() whenever * 		you can, and possibly even when you can't :-) * *		This command quirks around some problems in the rest of *		the code (namely translate_path()), so a blank newdir is *		interpreted as the root directory. */int do_chdir(struct conn * const c, const char * const newd){	char chd[512], temp[512];	TRAP_ERROR(chdir(c->curr_dir) == -1, 550, return -1);	/* handle `absolute' paths */	if (newd[0] == '/' || newd[0] == '\0') {		strcpy(temp, c->root_dir);		/*		 * is this the root directory? if not, remove the trailing `/'		 * and concatenate the new directory on		 */		if (newd[1] != '\0' && newd[0] != '\0') {			temp[strlen(temp) - 1] = 0;			strcat(temp, newd);		}	} else {		strcpy(temp, newd);	}#if WANT_NONROOT	if (nr_check_permission(c->uid, temp, 1, 1, NULL) == -1) {	        numeric(c, 550, "Permission denied");	        return -1;	}#endif	TRAP_ERROR(chdir(temp) == -1, 550, return -1);	getcwd(chd, 254);	if (chd[strlen(chd) - 1] != '/') {		strcat(chd, "/");	}	if (strncmp(chd, c->root_dir, strlen(c->root_dir)) != 0) {		numeric(c, 550, "No such file or directory.");		return -1;	}	return 0;}/* * cmd_user():	Handles the USER command, and does most of the initial *		authentication work. User names are limited to 16 *		characters, by force... */int cmd_user(struct conn * const c){	strncpy(c->username, c->recv_buf, 16);	c->username[16] = 0;	if (strcasecmp(c->username, "anonymous") == 0) {		strcpy(c->username, "ftp");	}       	if (strcasecmp(c->username, "ftp") == 0) {	       	numeric(c, 331, "Login OK, send password (your e-mail).");		c->auth = 1;	} else {		numeric(c, 331, "Password required for %s.", c->username);		c->auth = 2;	}	return 1;}/* * cmd_pass():	Handles the PASS command, and checks the password. *		This function is rather long and complicated, mostly *		because there are so many ways of doing users *		(including my nonroot system) out there... And we *		don't even support PAM or real shadow passwords (with *		expiry etc) yet... */int cmd_pass(struct conn * const c){#if WANT_NONROOT	c->auth = nr_userinfo(c->username, &c->uid, c->curr_dir, c->root_dir,		              c->recv_buf);#else /* !WANT_NONROOT */#if WANT_SHADOW && HAVE_SHADOW_H	struct spwd *s;#endif	struct passwd *p;       	p = getpwnam(c->username);#if WANT_SHADOW && HAVE_SHADOW_H	s = getspnam(c->username);#endif       		if (p == NULL) {		c->auth = 0;	} else {		c->uid = p->pw_uid;		strncpy(c->curr_dir, p->pw_dir, 254);		c->curr_dir[254] = 0;	}       	if (c->auth == 1) {		if (c->curr_dir[strlen(c->curr_dir) - 1] != '/') {			strcat(c->curr_dir, "/");		}		strcpy(c->root_dir, c->curr_dir);			c->auth = 3;	} else if (c->auth != 0) {		strcpy(c->root_dir, "/");		if (strcmp(crypt(c->recv_buf, p->pw_passwd), p->pw_passwd) != 0#if WANT_SHADOW && HAVE_SHADOW_H		    && (s == NULL || strcmp(crypt(c->recv_buf, s->sp_pwdp), s->sp_pwdp) != 0)#endif		) {			c->auth = 0;		} else {			c->auth = 3;		}	}#endif /* !WANT_NONROOT */	/* root should not be allowed to FTP */	if (c->uid == 0) {		c->auth = 0;	}	if (c->auth == 0) {		numeric(c, 530, "Login incorrect.");	} else {#if WANT_MESSAGE		chdir(c->curr_dir);		dump_file(c, 230, "welcome.msg");#endif		numeric(c, 230, "User logged in.");	}	return 1;}/* * cmd_acct():	Handle (ignore) the ACCT command. I don't see how we *		could make use of this command... wu-ftpd doesn't, either. *		However, wu-ftpd (at least the version I have here) uses *		502, which isn't a legal error code according to RFC959. *		202, on the other hand, is, and seems to be applicable. * *		I'm unsure if this one should require setuid or not, but *		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){	numeric(c, 202, "ACCT ignored OK -- not applicable on this system.");	return 1;}/* * cmd_port():	Handles the PORT command, and sets up the data socket. *		Making a brief uid=0 (root) appearance (to bind the socket) -- * 		I feel it's safer that way (instead of running as root *		the whole way), in case there are some weird overflows * 		somewhere. */int cmd_port(struct conn * const c){	short int a0, a1, a2, a3, p0, p1;	int i, sock, err;	struct ftran *f;	struct sockaddr_in sin;    	if ((c->transfer != NULL) && (c->transfer->state >= 4)) {		numeric(c, 500, "Sorry, only one transfer at a time.");		return 1;	}	sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);	TRAP_ERROR(sock == -1, 500, return 1);	destroy_ftran(c->transfer);	c->transfer = f = alloc_new_ftran(sock, c);	i = sscanf(c->recv_buf, "%3hu,%3hu,%3hu,%3hu,%3hu,%3hu", &a0, &a1, &a2, &a3, &p0, &p1);	if (i < 6) {		numeric(c, 501, "Parse error.");	} else {		const int one = 1;		int tmp;		/* 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);		sin.sin_port = FTP_PORT - 1;		numeric(c, 200, "PORT command OK.");		/* note that bind() might well fail, so we don't error check */#if !WANT_NONROOT		/* need root privilegies for a short while */		seteuid(getuid());#endif		bind(sock, (struct sockaddr *)&sin, sizeof(sin));#if !WANT_NONROOT		seteuid(c->uid);#endif		f->sin.sin_family = AF_INET;		f->sin.sin_addr.s_addr = htonl(			((unsigned char)(a0) << 24) +			((unsigned char)(a1) << 16) +			((unsigned char)(a2) <<  8) +			((unsigned char)(a3)      ));		f->sin.sin_port = htons(			((unsigned char)(p0) << 8) +			((unsigned char)(p1)     ));		f->sock = sock;		f->state = 3;		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){	struct ftran *f;	int tmp, sock, err;	unsigned int one = 1;	struct sockaddr_in addr;	if ((c->transfer != NULL) && (c->transfer->state >= 4)) {		numeric(c, 503, "Sorry, only one transfer at once.");		return 1;	}	destroy_ftran(c->transfer);	sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);	TRAP_ERROR(sock == -1, 500, return 1);	err = add_fd(sock, POLLIN);	TRAP_ERROR(err != 0, 501, return 1);	c->transfer = f = alloc_new_ftran(sock, c);	ioctl(sock, FIONBIO, &one);	/* setup socket */	tmp = sizeof(addr);	err = getsockname(c->sock, (struct sockaddr *)&addr, &tmp);	TRAP_ERROR(err == -1, 500, return 1);	addr.sin_port = 0;	/* let the system choose */	err = bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr));	TRAP_ERROR(err == -1, 500, return 1);	tmp = sizeof(addr);	err = getsockname(sock, (struct sockaddr *)&addr, &tmp);	TRAP_ERROR(err == -1, 500, return 1);	err = listen(f->sock, 1);	TRAP_ERROR(err == -1, 500, return 1);	f->state = 1;	numeric(c, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)",		(htonl(addr.sin_addr.s_addr) & 0xff000000) >> 24,		(htonl(addr.sin_addr.s_addr) & 0x00ff0000) >> 16,		(htonl(addr.sin_addr.s_addr) & 0x0000ff00) >>  8,		(htonl(addr.sin_addr.s_addr) & 0x000000ff),

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -