/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #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 sitecmd(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, }, { "site", sitecmd, 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);} static voidusage(void){ syslog(0, "ftp", "usage: %s [-aAde] [-n nsfile]", argv0); fprint(2, "usage: %s [-aAde] [-n nsfile]\n", argv0); exits("usage");} /* * 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 'a': /* anonymous OK */ anon_ok = 1; break; case 'A': anon_ok = 1; anon_only = 1; break; case 'd': debug++; break; case 'e': anon_ok = 1; anon_everybody = 1; break; case 'n': namespace = EARGF(usage()); break; default: usage(); }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 (only for none) */ arg = getenv("cputype"); if(arg) strecpy(cputype, cputype+sizeof cputype, arg); else strcpy(cputype, "mips"); /* shurely /%s/bin */ 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;}Lines 1–500 of 1923. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #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 sitecmd(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, }, { "site", sitecmd, 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" void logit(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); } static void usage(void) { syslog(0, "ftp", "usage: %s [-aAde] [-n nsfile]", argv0); fprint(2, "usage: %s [-aAde] [-n nsfile]\n", argv0); exits("usage"); } /* * read commands from the control stream and dispatch */ void main(int argc, char **argv) { char *cmd; char *arg; char *p; Cmd *t; Biobuf in; int i; ARGBEGIN{ case 'a': /* anonymous OK */ anon_ok = 1; break; case 'A': anon_ok = 1; anon_only = 1; break; case 'd': debug++; break; case 'e': anon_ok = 1; anon_everybody = 1; break; case 'n': namespace = EARGF(usage()); break; default: usage(); }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 (only for none) */ arg = getenv("cputype"); if(arg) strecpy(cputype, cputype+sizeof cputype, arg); else strcpy(cputype, "mips"); /* shurely /%s/bin */ 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 */ int reply(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; } int sodoff(void) { return reply("530 Sod off, service requires login"); } /* * run a command in a separate process */ int asproc(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 */ int transfer(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 */ int nopcmd(char *arg) { USED(arg); reply("510 Plan 9 FTP daemon still alive"); return 0; }