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

📄 ftpd.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 3 页
字号:
#include <u.h>#include <libc.h>#include <bio.h>#include <auth.h>#include <ip.h>#include <libsec.h>#include <String.h>#include "glob.h"enum{	/* telnet control character */	Iac=		255,		/* representation types */	Tascii=		0,	Timage=		1,	/* transmission modes */	Mstream=	0,	Mblock=		1,	Mpage=		2,	/* file structure */	Sfile=		0,	Sblock=		1,	Scompressed=	2,	/* read/write buffer size */	Nbuf=		4096,	/* maximum ms we'll wait for a command */	Maxwait=	1000*60*30,		/* inactive for 30 minutes, we hang up */	Maxerr=		128,	Maxpath=	512,};int	abortcmd(char*);int	appendcmd(char*);int	cdupcmd(char*);int	cwdcmd(char*);int	delcmd(char*);int	helpcmd(char*);int	listcmd(char*);int	mdtmcmd(char*);int	mkdircmd(char*);int	modecmd(char*);int	namelistcmd(char*);int	nopcmd(char*);int	passcmd(char*);int	pasvcmd(char*);int	portcmd(char*);int	pwdcmd(char*);int	quitcmd(char*);int	rnfrcmd(char*);int	rntocmd(char*);int	reply(char*, ...);int	restartcmd(char*);int	retrievecmd(char*);int	sizecmd(char*);int	storecmd(char*);int	storeucmd(char*);int	structcmd(char*);int	systemcmd(char*);int	typecmd(char*);int	usercmd(char*);int	dialdata(void);char*	abspath(char*);int	crlfwrite(int, char*, int);int	sodoff(void);int	accessok(char*);typedef struct Cmd	Cmd;struct Cmd{	char	*name;	int	(*f)(char*);	int	needlogin;};Cmd cmdtab[] ={	{ "abor",	abortcmd,	0, },	{ "appe",	appendcmd,	1, },	{ "cdup",	cdupcmd,	1, },	{ "cwd",	cwdcmd,		1, },	{ "dele",	delcmd,		1, },	{ "help",	helpcmd,	0, },	{ "list",	listcmd,	1, },	{ "mdtm",	mdtmcmd,	1, },	{ "mkd",	mkdircmd,	1, },	{ "mode",	modecmd,	0, },	{ "nlst",	namelistcmd,	1, },	{ "noop",	nopcmd,		0, },	{ "pass",	passcmd,	0, },	{ "pasv",	pasvcmd,	1, },	{ "pwd",	pwdcmd,		0, },	{ "port", 	portcmd,	1, },	{ "quit",	quitcmd,	0, },	{ "rest",	restartcmd,	1, },	{ "retr",	retrievecmd,	1, },	{ "rmd",	delcmd,		1, },	{ "rnfr",	rnfrcmd,	1, },	{ "rnto",	rntocmd,	1, },	{ "size", 	sizecmd,	1, },	{ "stor", 	storecmd,	1, },	{ "stou", 	storeucmd,	1, },	{ "stru",	structcmd,	1, },	{ "syst",	systemcmd,	0, },	{ "type", 	typecmd,	0, },	{ "user",	usercmd,	0, },	{ 0, 0, 0 },};#define NONENS "/lib/namespace.ftp"	/* default ns for none */char	user[Maxpath];		/* logged in user */char	curdir[Maxpath];	/* current directory path */Chalstate	*ch;int	loggedin;int	type;			/* transmission type */int	mode;			/* transmission mode */int	structure;		/* file structure */char	data[64];		/* data address */int	pid;			/* transfer process */int	encryption;		/* encryption state */int	isnone, anon_ok, anon_only, anon_everybody;char	cputype[Maxpath];	/* the environment variable of the same name */char	bindir[Maxpath];	/* bin directory for this architecture */char	mailaddr[Maxpath];char	*namespace = NONENS;int	debug;NetConnInfo	*nci;int	createperm = 0660;int	isnoworld;vlong	offset;			/* from restart command */ulong id;typedef struct Passive Passive;struct Passive{	int	inuse;	char	adir[40];	int	afd;	int	port;	uchar	ipaddr[IPaddrlen];} passive;#define FTPLOG "ftp"voidlogit(char *fmt, ...){	char buf[8192];	va_list arg;	char errstr[128];	rerrstr(errstr, sizeof errstr);	va_start(arg, fmt);	vseprint(buf, buf+sizeof(buf), fmt, arg);	va_end(arg);	syslog(0, FTPLOG, "%s.%s %s", nci->rsys, nci->rserv, buf);	werrstr(errstr, sizeof errstr);}/* *  read commands from the control stream and dispatch */voidmain(int argc, char **argv){	char *cmd;	char *arg;	char *p;	Cmd *t;	Biobuf in;	int i;	ARGBEGIN{	case 'd':		debug++;		break;	case 'a':		/* anonymous OK */		anon_ok = 1;		break;	case 'A':		anon_ok = 1;		anon_only = 1;		break;	case 'e':		anon_ok = 1;		anon_everybody = 1;		break;	case 'n':		namespace = ARGF();		break;	}ARGEND	/* open log file before doing a newns */	syslog(0, FTPLOG, nil);	/* find out who is calling */	if(argc < 1)		nci = getnetconninfo(nil, 0);	else		nci = getnetconninfo(argv[argc-1], 0);	if(nci == nil)		sysfatal("ftpd needs a network address");	strcpy(mailaddr, "?");	id = getpid();	/* figure out which binaries to bind in later */	arg = getenv("cputype");	if(arg)		strecpy(cputype, cputype+sizeof cputype, arg);	else		strcpy(cputype, "mips");	snprint(bindir, sizeof(bindir), "/bin/%s/bin", cputype);	Binit(&in, 0, OREAD);	reply("220 Plan 9 FTP server ready");	alarm(Maxwait);	while(cmd = Brdline(&in, '\n')){		alarm(0);		/*		 *  strip out trailing cr's & lf and delimit with null		 */		i = Blinelen(&in)-1;		cmd[i] = 0;		if(debug)			logit("%s", cmd);		while(i > 0 && cmd[i-1] == '\r')			cmd[--i] = 0;		/*		 *  hack for GatorFTP+, look for a 0x10 used as a delimiter		 */		p = strchr(cmd, 0x10);		if(p)			*p = 0;		/*		 *  get rid of telnet control sequences (we don't need them)		 */		while(*cmd && (uchar)*cmd == Iac){			cmd++;			if(*cmd)				cmd++;		}		/*		 *  parse the message (command arg)		 */		arg = strchr(cmd, ' ');		if(arg){			*arg++ = 0;			while(*arg == ' ')				arg++;		}		/*		 *  ignore blank commands		 */		if(*cmd == 0)			continue;		/*		 *  lookup the command and do it		 */		for(p = cmd; *p; p++)			*p = tolower(*p);		for(t = cmdtab; t->name; t++)			if(strcmp(cmd, t->name) == 0){				if(t->needlogin && !loggedin)					sodoff();				else if((*t->f)(arg) < 0)					exits(0);				break;			}		if(t->f != restartcmd){			/*			 *  the file offset is set to zero following			 *  all commands except the restart command			 */			offset = 0;		}		if(t->name == 0){			/*			 *  the OOB bytes preceding an abort from UCB machines			 *  comes out as something unrecognizable instead of			 *  IAC's.  Certainly a Plan 9 bug but I can't find it.			 *  This is a major hack to avoid the problem. -- presotto			 */ 			i = strlen(cmd);			if(i > 4 && strcmp(cmd+i-4, "abor") == 0){				abortcmd(0);				} else{				logit("%s (%s) command not implemented", cmd, arg?arg:"");				reply("502 %s command not implemented", cmd);			}		}		alarm(Maxwait);	}	if(pid)		postnote(PNPROC, pid, "kill");}/* *  reply to a command */intreply(char *fmt, ...){	va_list arg;	char buf[8192], *s;	va_start(arg, fmt);	s = vseprint(buf, buf+sizeof(buf)-3, fmt, arg);	va_end(arg);	if(debug){		*s = 0;		logit("%s", buf);	}	*s++ = '\r';	*s++ = '\n';	write(1, buf, s - buf);	return 0;}intsodoff(void){	return reply("530 Sod off, service requires login");}/* *  run a command in a separate process */intasproc(void (*f)(char*, int), char *arg, int arg2){	int i;	if(pid){		/* wait for previous command to finish */		for(;;){			i = waitpid();			if(i == pid || i < 0)				break;		}	}	switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){	case -1:		return reply("450 Out of processes: %r");	case 0:		(*f)(arg, arg2);		exits(0);	default:		break;	}	return 0;}/* * run a command to filter a tail */inttransfer(char *cmd, char *a1, char *a2, char *a3, int image){	int n, dfd, fd, bytes, eofs, pid;	int pfd[2];	char buf[Nbuf], *p;	Waitmsg *w;	reply("150 Opening data connection for %s (%s)", cmd, data);	dfd = dialdata();	if(dfd < 0)		return reply("425 Error opening data connection: %r");	if(pipe(pfd) < 0)		return reply("520 Internal Error: %r");	bytes = 0;	switch(pid = rfork(RFFDG|RFPROC|RFNAMEG)){	case -1:		return reply("450 Out of processes: %r");	case 0:		logit("running %s %s %s %s pid %d",			cmd, a1?a1:"", a2?a2:"" , a3?a3:"",getpid());		close(pfd[1]);		close(dfd);		dup(pfd[0], 1);		dup(pfd[0], 2);		if(isnone){			fd = open("#s/boot", ORDWR);			if(fd < 0			|| bind("#/", "/", MAFTER) < 0			|| amount(fd, "/bin", MREPL, "") < 0			|| bind("#c", "/dev", MAFTER) < 0			|| bind(bindir, "/bin", MREPL) < 0)				exits("building name space");			close(fd);		}		execl(cmd, cmd, a1, a2, a3, nil);		exits(cmd);	default:		close(pfd[0]);		eofs = 0;		while((n = read(pfd[1], buf, sizeof buf)) >= 0){			if(n == 0){				if(eofs++ > 5)					break;				else					continue;			}			eofs = 0;			p = buf;			if(offset > 0){				if(n > offset){					p = buf+offset;					n -= offset;					offset = 0;				} else {					offset -= n;					continue;				}			}			if(!image)				n = crlfwrite(dfd, p, n);			else				n = write(dfd, p, n);			if(n < 0){				postnote(PNPROC, pid, "kill");				bytes = -1;				break;			}			bytes += n;		}		close(pfd[1]);		close(dfd);		break;	}	/* wait for this command to finish */	for(;;){		w = wait();		if(w == nil || w->pid == pid)			break;		free(w);	}	if(w != nil && w->msg != nil && w->msg[0] != 0){		bytes = -1;		logit("%s", w->msg);		logit("%s %s %s %s failed %s", cmd, a1?a1:"", a2?a2:"" , a3?a3:"", w->msg);	}	free(w);	reply("226 Transfer complete");	return bytes;}/* *  just reply OK */intnopcmd(char *arg){	USED(arg);	reply("510 Plan 9 FTP daemon still alive");	return 0;}/* *  login as user */intloginuser(char *user, char *nsfile, int gotoslash){	logit("login %s %s %s %s", user, mailaddr, nci->rsys, nsfile);	if(nsfile != nil && newns(user, nsfile) < 0){		logit("namespace file %s does not exist", nsfile);		return reply("530 Not logged in: login out of service");	}	getwd(curdir, sizeof(curdir));	if(gotoslash){		chdir("/");		strcpy(curdir, "/");	}	putenv("service", "ftp");	loggedin = 1;	if(debug == 0)		reply("230- If you have problems, send mail to 'postmaster'.");	return reply("230 Logged in");}/* *  get a user id, reply with a challenge.  The users 'anonymous' *  and 'ftp' are equivalent to 'none'.  The user 'none' requires *  no challenge. */intusercmd(char *name){	logit("user %s %s", name, nci->rsys);	if(loggedin)		return reply("530 Already logged in as %s", user);	if(name == 0 || *name == 0)		return reply("530 user command needs user name");	isnoworld = 0;	if(*name == ':'){		debug = 1;		name++;	}	strncpy(user, name, sizeof(user));	if(debug)		logit("debugging");	user[sizeof(user)-1] = 0;	if(strcmp(user, "anonymous") == 0 || strcmp(user, "ftp") == 0)		strcpy(user, "none");	else if(anon_everybody)		strcpy(user,"none");	if(strcmp(user, "*none") == 0){		if(!anon_ok)			return reply("530 Not logged in: anonymous disallowed");		return loginuser("none", namespace, 1);	}	if(strcmp(user, "none") == 0){		if(!anon_ok)			return reply("530 Not logged in: anonymous disallowed");		return reply("331 Send email address as password");	}	if(anon_only)		return reply("530 Not logged in: anonymous access only");	isnoworld = noworld(name);	if(isnoworld)		return reply("331 OK");	if(ch)		auth_freechal(ch);	if((ch = auth_challenge("proto=p9cr role=server user=%q", user)) == nil)		return reply("421 %r");	return reply("331 encrypt challenge, %s, as a password", ch->chal);}/* *  get a password, set up user if it works. */intpasscmd(char *response){	char namefile[128];	AuthInfo *ai;	if(response == nil)		response = "";	if(strcmp(user, "none") == 0 || strcmp(user, "*none") == 0){		/* for none, accept anything as a password */		isnone = 1;		strncpy(mailaddr, response, sizeof(mailaddr)-1);		return loginuser("none", namespace, 1);	}	if(isnoworld){		/* noworld gets a password in the clear */		if(login(user, response, "/lib/namespace.noworld") < 0)			return reply("530 Not logged in");		createperm = 0664;		/* login has already setup the namespace */		return loginuser(user, nil, 0);	} else {		/* for everyone else, do challenge response */		if(ch == nil)			return reply("531 Send user id before encrypted challenge");		ch->resp = response;		ch->nresp = strlen(response);		ai = auth_response(ch);		if(ai == nil)			return reply("530 Not logged in: %r");		if(auth_chuid(ai, nil) < 0)			return reply("530 Not logged in: %r");		auth_freechal(ch);		ch = nil;		/* if the user has specified a namespace for ftp, use it */		snprint(namefile, sizeof(namefile), "/usr/%s/lib/namespace.ftp", user);		strcpy(mailaddr, user);		createperm = 0660;		if(access(namefile, 0) == 0)			return loginuser(user, namefile, 0);		else			return loginuser(user, "/lib/namespace", 0);	}}/* *  print working directory */intpwdcmd(char *arg){	if(arg)		return reply("550 Pwd takes no argument");	return reply("257 \"%s\" is the current directory", curdir);}

⌨️ 快捷键说明

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