📄 ftpd.c
字号:
#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 + -