1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864 |
- #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"
- 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;
- }
- /*
- * login as user
- */
- int
- loginuser(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");
- }
- static void
- slowdown(void)
- {
- static ulong pause;
- if (pause) {
- sleep(pause); /* deter guessers */
- if (pause < (1UL << 20))
- pause *= 2;
- } else
- pause = 1000;
- }
- /*
- * get a user id, reply with a challenge. The users 'anonymous'
- * and 'ftp' are equivalent to 'none'. The user 'none' requires
- * no challenge.
- */
- int
- usercmd(char *name)
- {
- slowdown();
- 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, "Administrator") == 0 || strcmp(user, "admin") == 0)
- return reply("530 go away, script kiddie");
- else if(strcmp(user, "*none") == 0){
- if(!anon_ok)
- return reply("530 Not logged in: anonymous disallowed");
- return loginuser("none", namespace, 1);
- }
- else if(strcmp(user, "none") == 0){
- if(!anon_ok)
- return reply("530 Not logged in: anonymous disallowed");
- return reply("331 Send email address as password");
- }
- else if(anon_only)
- return reply("530 Not logged in: anonymous access only");
- isnoworld = noworld(name);
- if(isnoworld)
- return reply("331 OK");
- /* consult the auth server */
- 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.
- */
- int
- passcmd(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 || auth_chuid(ai, nil) < 0) {
- slowdown();
- 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
- */
- int
- pwdcmd(char *arg)
- {
- if(arg)
- return reply("550 Pwd takes no argument");
- return reply("257 \"%s\" is the current directory", curdir);
- }
- /*
- * chdir
- */
- int
- cwdcmd(char *dir)
- {
- char *rp;
- char buf[Maxpath];
- /* shell cd semantics */
- if(dir == 0 || *dir == 0){
- if(isnone)
- rp = "/";
- else {
- snprint(buf, sizeof buf, "/usr/%s", user);
- rp = buf;
- }
- if(accessok(rp) == 0)
- rp = nil;
- } else
- rp = abspath(dir);
- if(rp == nil)
- return reply("550 Permission denied");
- if(chdir(rp) < 0)
- return reply("550 Cwd failed: %r");
- strcpy(curdir, rp);
- return reply("250 directory changed to %s", curdir);
- }
- /*
- * chdir ..
- */
- int
- cdupcmd(char *dp)
- {
- USED(dp);
- return cwdcmd("..");
- }
- int
- quitcmd(char *arg)
- {
- USED(arg);
- reply("200 Bye");
- if(pid)
- postnote(PNPROC, pid, "kill");
- return -1;
- }
- int
- typecmd(char *arg)
- {
- int c;
- char *x;
- x = arg;
- if(arg == 0)
- return reply("501 Type command needs arguments");
- while(c = *arg++){
- switch(tolower(c)){
- case 'a':
- type = Tascii;
- break;
- case 'i':
- case 'l':
- type = Timage;
- break;
- case '8':
- case ' ':
- case 'n':
- case 't':
- case 'c':
- break;
- default:
- return reply("501 Unimplemented type %s", x);
- }
- }
- return reply("200 Type %s", type==Tascii ? "Ascii" : "Image");
- }
- int
- modecmd(char *arg)
- {
- if(arg == 0)
- return reply("501 Mode command needs arguments");
- while(*arg){
- switch(tolower(*arg)){
- case 's':
- mode = Mstream;
- break;
- default:
- return reply("501 Unimplemented mode %c", *arg);
- }
- arg++;
- }
- return reply("200 Stream mode");
- }
- int
- structcmd(char *arg)
- {
- if(arg == 0)
- return reply("501 Struct command needs arguments");
- for(; *arg; arg++){
- switch(tolower(*arg)){
- case 'f':
- structure = Sfile;
- break;
- default:
- return reply("501 Unimplemented structure %c", *arg);
- }
- }
- return reply("200 File structure");
- }
- int
- portcmd(char *arg)
- {
- char *field[7];
- int n;
- if(arg == 0)
- return reply("501 Port command needs arguments");
- n = getfields(arg, field, 7, 0, ", ");
- if(n != 6)
- return reply("501 Incorrect port specification");
- snprint(data, sizeof data, "tcp!%.3s.%.3s.%.3s.%.3s!%d", field[0], field[1], field[2],
- field[3], atoi(field[4])*256 + atoi(field[5]));
- return reply("200 Data port is %s", data);
- }
- int
- mountnet(void)
- {
- int rv;
- rv = 0;
- if(bind("#/", "/", MAFTER) < 0){
- logit("can't bind #/ to /: %r");
- return reply("500 can't bind #/ to /: %r");
- }
- if(bind(nci->spec, "/net", MBEFORE) < 0){
- logit("can't bind %s to /net: %r", nci->spec);
- rv = reply("500 can't bind %s to /net: %r", nci->spec);
- unmount("#/", "/");
- }
- return rv;
- }
- void
- unmountnet(void)
- {
- unmount(0, "/net");
- unmount("#/", "/");
- }
- int
- pasvcmd(char *arg)
- {
- NetConnInfo *nnci;
- Passive *p;
- USED(arg);
- p = &passive;
- if(p->inuse){
- close(p->afd);
- p->inuse = 0;
- }
- if(mountnet() < 0)
- return 0;
- p->afd = announce("tcp!*!0", passive.adir);
- if(p->afd < 0){
- unmountnet();
- return reply("500 No free ports");
- }
- nnci = getnetconninfo(p->adir, -1);
- unmountnet();
- /* parse the local address */
- if(debug)
- logit("local sys is %s", nci->lsys);
- parseip(p->ipaddr, nci->lsys);
- if(ipcmp(p->ipaddr, v4prefix) == 0 || ipcmp(p->ipaddr, IPnoaddr) == 0)
- parseip(p->ipaddr, nci->lsys);
- p->port = atoi(nnci->lserv);
- freenetconninfo(nnci);
- p->inuse = 1;
- return reply("227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)",
- p->ipaddr[IPv4off+0], p->ipaddr[IPv4off+1], p->ipaddr[IPv4off+2], p->ipaddr[IPv4off+3],
- p->port>>8, p->port&0xff);
- }
- enum
- {
- Narg=32,
- };
- int Cflag, rflag, tflag, Rflag;
- int maxnamelen;
- int col;
- char*
- mode2asc(int m)
- {
- static char asc[12];
- char *p;
- strcpy(asc, "----------");
- if(DMDIR & m)
- asc[0] = 'd';
- if(DMAPPEND & m)
- asc[0] = 'a';
- else if(DMEXCL & m)
- asc[3] = 'l';
- for(p = asc+1; p < asc + 10; p += 3, m<<=3){
- if(m & 0400)
- p[0] = 'r';
- if(m & 0200)
- p[1] = 'w';
- if(m & 0100)
- p[2] = 'x';
- }
- return asc;
- }
- void
- listfile(Biobufhdr *b, char *name, int lflag, char *dname)
- {
- char ts[32];
- int n, links, pad;
- long now;
- char *x;
- Dir *d;
- x = abspath(name);
- if(x == nil)
- return;
- d = dirstat(x);
- if(d == nil)
- return;
- if(isnone){
- if(strncmp(x, "/incoming/", sizeof("/incoming/")-1) != 0)
- d->mode &= ~0222;
- d->uid = "none";
- d->gid = "none";
- }
- strcpy(ts, ctime(d->mtime));
- ts[16] = 0;
- now = time(0);
- if(now - d->mtime > 6*30*24*60*60)
- memmove(ts+11, ts+23, 5);
- if(lflag){
- /* Unix style long listing */
- if(DMDIR&d->mode){
- links = 2;
- d->length = 512;
- } else
- links = 1;
- Bprint(b, "%s %3d %-8s %-8s %7lld %s ",
- mode2asc(d->mode), links,
- d->uid, d->gid, d->length, ts+4);
- }
- if(Cflag && maxnamelen < 40){
- n = strlen(name);
- pad = ((col+maxnamelen)/(maxnamelen+1))*(maxnamelen+1);
- if(pad+maxnamelen+1 < 60){
- Bprint(b, "%*s", pad-col+n, name);
- col = pad+n;
- }
- else{
- Bprint(b, "\r\n%s", name);
- col = n;
- }
- }
- else{
- if(dname)
- Bprint(b, "%s/", dname);
- Bprint(b, "%s\r\n", name);
- }
- free(d);
- }
- int
- dircomp(void *va, void *vb)
- {
- int rv;
- Dir *a, *b;
- a = va;
- b = vb;
- if(tflag)
- rv = b->mtime - a->mtime;
- else
- rv = strcmp(a->name, b->name);
- return (rflag?-1:1)*rv;
- }
- void
- listdir(char *name, Biobufhdr *b, int lflag, int *printname, Globlist *gl)
- {
- Dir *p;
- int fd, n, i, l;
- char *dname;
- uvlong total;
- col = 0;
- fd = open(name, OREAD);
- if(fd < 0){
- Bprint(b, "can't read %s: %r\r\n", name);
- return;
- }
- dname = 0;
- if(*printname){
- if(Rflag || lflag)
- Bprint(b, "\r\n%s:\r\n", name);
- else
- dname = name;
- }
- n = dirreadall(fd, &p);
- close(fd);
- if(Cflag){
- for(i = 0; i < n; i++){
- l = strlen(p[i].name);
- if(l > maxnamelen)
- maxnamelen = l;
- }
- }
- /* Unix style total line */
- if(lflag){
- total = 0;
- for(i = 0; i < n; i++){
- if(p[i].qid.type & QTDIR)
- total += 512;
- else
- total += p[i].length;
- }
- Bprint(b, "total %ulld\r\n", total/512);
- }
- qsort(p, n, sizeof(Dir), dircomp);
- for(i = 0; i < n; i++){
- if(Rflag && (p[i].qid.type & QTDIR)){
- *printname = 1;
- globadd(gl, name, p[i].name);
- }
- listfile(b, p[i].name, lflag, dname);
- }
- free(p);
- }
- void
- list(char *arg, int lflag)
- {
- Dir *d;
- Globlist *gl;
- Glob *g;
- int dfd, printname;
- int i, n, argc;
- char *alist[Narg];
- char **argv;
- Biobufhdr bh;
- uchar buf[512];
- char *p, *s;
- if(arg == 0)
- arg = "";
- if(debug)
- logit("ls %s (. = %s)", arg, curdir);
- /* process arguments, understand /bin/ls -l option */
- argv = alist;
- argv[0] = "/bin/ls";
- argc = getfields(arg, argv+1, Narg-2, 1, " \t") + 1;
- argv[argc] = 0;
- rflag = 0;
- tflag = 0;
- Rflag = 0;
- Cflag = 0;
- col = 0;
- ARGBEGIN{
- case 'l':
- lflag++;
- break;
- case 'R':
- Rflag++;
- break;
- case 'C':
- Cflag++;
- break;
- case 'r':
- rflag++;
- break;
- case 't':
- tflag++;
- break;
- }ARGEND;
- if(Cflag)
- lflag = 0;
- dfd = dialdata();
- if(dfd < 0){
- reply("425 Error opening data connection:%r");
- return;
- }
- reply("150 Opened data connection (%s)", data);
- Binits(&bh, dfd, OWRITE, buf, sizeof(buf));
- if(argc == 0){
- argc = 1;
- argv = alist;
- argv[0] = ".";
- }
- for(i = 0; i < argc; i++){
- chdir(curdir);
- gl = glob(argv[i]);
- if(gl == nil)
- continue;
- printname = gl->first != nil && gl->first->next != nil;
- maxnamelen = 8;
- if(Cflag)
- for(g = gl->first; g; g = g->next)
- if(g->glob && (n = strlen(s_to_c(g->glob))) > maxnamelen)
- maxnamelen = n;
- while(s = globiter(gl)){
- if(debug)
- logit("glob %s", s);
- p = abspath(s);
- if(p == nil){
- free(s);
- continue;
- }
- d = dirstat(p);
- if(d == nil){
- free(s);
- continue;
- }
- if(d->qid.type & QTDIR)
- listdir(s, &bh, lflag, &printname, gl);
- else
- listfile(&bh, s, lflag, 0);
- free(s);
- free(d);
- }
- globlistfree(gl);
- }
- if(Cflag)
- Bprint(&bh, "\r\n");
- Bflush(&bh);
- close(dfd);
- reply("226 Transfer complete (list %s)", arg);
- }
- int
- namelistcmd(char *arg)
- {
- return asproc(list, arg, 0);
- }
- int
- listcmd(char *arg)
- {
- return asproc(list, arg, 1);
- }
- /*
- * return the size of the file
- */
- int
- sizecmd(char *arg)
- {
- Dir *d;
- int rv;
- if(arg == 0)
- return reply("501 Size command requires pathname");
- arg = abspath(arg);
- d = dirstat(arg);
- if(d == nil)
- return reply("501 %r accessing %s", arg);
- rv = reply("213 %lld", d->length);
- free(d);
- return rv;
- }
- /*
- * return the modify time of the file
- */
- int
- mdtmcmd(char *arg)
- {
- Dir *d;
- Tm *t;
- int rv;
- if(arg == 0)
- return reply("501 Mdtm command requires pathname");
- if(arg == 0)
- return reply("550 Permission denied");
- d = dirstat(arg);
- if(d == nil)
- return reply("501 %r accessing %s", arg);
- t = gmtime(d->mtime);
- rv = reply("213 %4.4d%2.2d%2.2d%2.2d%2.2d%2.2d",
- t->year+1900, t->mon+1, t->mday,
- t->hour, t->min, t->sec);
- free(d);
- return rv;
- }
- /*
- * set an offset to start reading a file from
- * only lasts for one command
- */
- int
- restartcmd(char *arg)
- {
- if(arg == 0)
- return reply("501 Restart command requires offset");
- offset = atoll(arg);
- if(offset < 0){
- offset = 0;
- return reply("501 Bad offset");
- }
- return reply("350 Restarting at %lld. Send STORE or RETRIEVE", offset);
- }
- /*
- * send a file to the user
- */
- int
- crlfwrite(int fd, char *p, int n)
- {
- char *ep, *np;
- char buf[2*Nbuf];
- for(np = buf, ep = p + n; p < ep; p++){
- if(*p == '\n')
- *np++ = '\r';
- *np++ = *p;
- }
- if(write(fd, buf, np - buf) == np - buf)
- return n;
- else
- return -1;
- }
- void
- retrievedir(char *arg)
- {
- int n;
- char *p;
- String *file;
- if(type != Timage){
- reply("550 This file requires type binary/image");
- return;
- }
- file = s_copy(arg);
- p = strrchr(s_to_c(file), '/');
- if(p != s_to_c(file)){
- *p++ = 0;
- chdir(s_to_c(file));
- } else {
- chdir("/");
- p = s_to_c(file)+1;
- }
- n = transfer("/bin/tar", "c", p, 0, 1);
- if(n < 0)
- logit("get %s failed", arg);
- else
- logit("get %s OK %d", arg, n);
- s_free(file);
- }
- void
- retrieve(char *arg, int arg2)
- {
- int dfd, fd, n, i, bytes;
- Dir *d;
- char buf[Nbuf];
- char *p, *ep;
- USED(arg2);
- p = strchr(arg, '\r');
- if(p){
- logit("cr in file name", arg);
- *p = 0;
- }
- fd = open(arg, OREAD);
- if(fd == -1){
- n = strlen(arg);
- if(n > 4 && strcmp(arg+n-4, ".tar") == 0){
- *(arg+n-4) = 0;
- d = dirstat(arg);
- if(d != nil){
- if(d->qid.type & QTDIR){
- retrievedir(arg);
- free(d);
- return;
- }
- free(d);
- }
- }
- logit("get %s failed", arg);
- reply("550 Error opening %s: %r", arg);
- return;
- }
- if(offset != 0)
- if(seek(fd, offset, 0) < 0){
- reply("550 %s: seek to %lld failed", arg, offset);
- close(fd);
- return;
- }
- d = dirfstat(fd);
- if(d != nil){
- if(d->qid.type & QTDIR){
- reply("550 %s: not a plain file.", arg);
- close(fd);
- free(d);
- return;
- }
- free(d);
- }
- n = read(fd, buf, sizeof(buf));
- if(n < 0){
- logit("get %s failed", arg, mailaddr, nci->rsys);
- reply("550 Error reading %s: %r", arg);
- close(fd);
- return;
- }
- if(type != Timage)
- for(p = buf, ep = &buf[n]; p < ep; p++)
- if(*p & 0x80){
- close(fd);
- reply("550 This file requires type binary/image");
- return;
- }
- reply("150 Opening data connection for %s (%s)", arg, data);
- dfd = dialdata();
- if(dfd < 0){
- reply("425 Error opening data connection:%r");
- close(fd);
- return;
- }
- bytes = 0;
- do {
- switch(type){
- case Timage:
- i = write(dfd, buf, n);
- break;
- default:
- i = crlfwrite(dfd, buf, n);
- break;
- }
- if(i != n){
- close(fd);
- close(dfd);
- logit("get %s %r to data connection after %d", arg, bytes);
- reply("550 Error writing to data connection: %r");
- return;
- }
- bytes += n;
- } while((n = read(fd, buf, sizeof(buf))) > 0);
- if(n < 0)
- logit("get %s %r after %d", arg, bytes);
- close(fd);
- close(dfd);
- reply("226 Transfer complete");
- logit("get %s OK %d", arg, bytes);
- }
- int
- retrievecmd(char *arg)
- {
- if(arg == 0)
- return reply("501 Retrieve command requires an argument");
- arg = abspath(arg);
- if(arg == 0)
- return reply("550 Permission denied");
- return asproc(retrieve, arg, 0);
- }
- /*
- * get a file from the user
- */
- int
- lfwrite(int fd, char *p, int n)
- {
- char *ep, *np;
- char buf[Nbuf];
- for(np = buf, ep = p + n; p < ep; p++){
- if(*p != '\r')
- *np++ = *p;
- }
- if(write(fd, buf, np - buf) == np - buf)
- return n;
- else
- return -1;
- }
- void
- store(char *arg, int fd)
- {
- int dfd, n, i;
- char buf[Nbuf];
- reply("150 Opening data connection for %s (%s)", arg, data);
- dfd = dialdata();
- if(dfd < 0){
- reply("425 Error opening data connection:%r");
- close(fd);
- return;
- }
- while((n = read(dfd, buf, sizeof(buf))) > 0){
- switch(type){
- case Timage:
- i = write(fd, buf, n);
- break;
- default:
- i = lfwrite(fd, buf, n);
- break;
- }
- if(i != n){
- close(fd);
- close(dfd);
- reply("550 Error writing file");
- return;
- }
- }
- close(fd);
- close(dfd);
- logit("put %s OK", arg);
- reply("226 Transfer complete");
- }
- int
- storecmd(char *arg)
- {
- int fd, rv;
- if(arg == 0)
- return reply("501 Store command requires an argument");
- arg = abspath(arg);
- if(arg == 0)
- return reply("550 Permission denied");
- if(isnone && strncmp(arg, "/incoming/", sizeof("/incoming/")-1))
- return reply("550 Permission denied");
- if(offset){
- fd = open(arg, OWRITE);
- if(fd == -1)
- return reply("550 Error opening %s: %r", arg);
- if(seek(fd, offset, 0) == -1)
- return reply("550 Error seeking %s to %d: %r",
- arg, offset);
- } else {
- fd = create(arg, OWRITE, createperm);
- if(fd == -1)
- return reply("550 Error creating %s: %r", arg);
- }
- rv = asproc(store, arg, fd);
- close(fd);
- return rv;
- }
- int
- appendcmd(char *arg)
- {
- int fd, rv;
- if(arg == 0)
- return reply("501 Append command requires an argument");
- if(isnone)
- return reply("550 Permission denied");
- arg = abspath(arg);
- if(arg == 0)
- return reply("550 Error creating %s: Permission denied", arg);
- fd = open(arg, OWRITE);
- if(fd == -1){
- fd = create(arg, OWRITE, createperm);
- if(fd == -1)
- return reply("550 Error creating %s: %r", arg);
- }
- seek(fd, 0, 2);
- rv = asproc(store, arg, fd);
- close(fd);
- return rv;
- }
- int
- storeucmd(char *arg)
- {
- int fd, rv;
- char name[Maxpath];
- USED(arg);
- if(isnone)
- return reply("550 Permission denied");
- strncpy(name, "ftpXXXXXXXXXXX", sizeof name);
- mktemp(name);
- fd = create(name, OWRITE, createperm);
- if(fd == -1)
- return reply("550 Error creating %s: %r", name);
- rv = asproc(store, name, fd);
- close(fd);
- return rv;
- }
- int
- mkdircmd(char *name)
- {
- int fd;
- if(name == 0)
- return reply("501 Mkdir command requires an argument");
- if(isnone)
- return reply("550 Permission denied");
- name = abspath(name);
- if(name == 0)
- return reply("550 Permission denied");
- fd = create(name, OREAD, DMDIR|0775);
- if(fd < 0)
- return reply("550 Can't create %s: %r", name);
- close(fd);
- return reply("226 %s created", name);
- }
- int
- delcmd(char *name)
- {
- if(name == 0)
- return reply("501 Rmdir/delete command requires an argument");
- if(isnone)
- return reply("550 Permission denied");
- name = abspath(name);
- if(name == 0)
- return reply("550 Permission denied");
- if(remove(name) < 0)
- return reply("550 Can't remove %s: %r", name);
- else
- return reply("226 %s removed", name);
- }
- /*
- * kill off the last transfer (if the process still exists)
- */
- int
- abortcmd(char *arg)
- {
- USED(arg);
- logit("abort pid %d", pid);
- if(pid){
- if(postnote(PNPROC, pid, "kill") == 0)
- reply("426 Command aborted");
- else
- logit("postnote pid %d %r", pid);
- }
- return reply("226 Abort processed");
- }
- int
- systemcmd(char *arg)
- {
- USED(arg);
- return reply("215 UNIX Type: L8 Version: Plan 9");
- }
- int
- helpcmd(char *arg)
- {
- int i;
- char buf[80];
- char *p, *e;
- USED(arg);
- reply("214- the following commands are implemented:");
- p = buf;
- e = buf+sizeof buf;
- for(i = 0; cmdtab[i].name; i++){
- if((i%8) == 0){
- reply("214-%s", buf);
- p = buf;
- }
- p = seprint(p, e, " %-5.5s", cmdtab[i].name);
- }
- if(p != buf)
- reply("214-%s", buf);
- reply("214 ");
- return 0;
- }
- /*
- * renaming a file takes two commands
- */
- static String *filepath;
- int
- rnfrcmd(char *from)
- {
- if(isnone)
- return reply("550 Permission denied");
- if(from == 0)
- return reply("501 Rename command requires an argument");
- from = abspath(from);
- if(from == 0)
- return reply("550 Permission denied");
- if(filepath == nil)
- filepath = s_copy(from);
- else{
- s_reset(filepath);
- s_append(filepath, from);
- }
- return reply("350 Rename %s to ...", s_to_c(filepath));
- }
- int
- rntocmd(char *to)
- {
- int r;
- Dir nd;
- char *fp, *tp;
- if(isnone)
- return reply("550 Permission denied");
- if(to == 0)
- return reply("501 Rename command requires an argument");
- to = abspath(to);
- if(to == 0)
- return reply("550 Permission denied");
- if(filepath == nil || *(s_to_c(filepath)) == 0)
- return reply("503 Rnto must be preceeded by an rnfr");
- tp = strrchr(to, '/');
- fp = strrchr(s_to_c(filepath), '/');
- if((tp && fp == 0) || (fp && tp == 0)
- || (fp && tp && (fp-s_to_c(filepath) != tp-to || memcmp(s_to_c(filepath), to, tp-to))))
- return reply("550 Rename can't change directory");
- if(tp)
- to = tp+1;
- nulldir(&nd);
- nd.name = to;
- if(dirwstat(s_to_c(filepath), &nd) < 0)
- r = reply("550 Can't rename %s to %s: %r\n", s_to_c(filepath), to);
- else
- r = reply("250 %s now %s", s_to_c(filepath), to);
- s_reset(filepath);
- return r;
- }
- /*
- * to dial out we need the network file system in our
- * name space.
- */
- int
- dialdata(void)
- {
- int fd, cfd;
- char ldir[40];
- char err[Maxerr];
- if(mountnet() < 0)
- return -1;
- if(!passive.inuse){
- fd = dial(data, "20", 0, 0);
- errstr(err, sizeof err);
- } else {
- alarm(5*60*1000);
- cfd = listen(passive.adir, ldir);
- alarm(0);
- errstr(err, sizeof err);
- if(cfd < 0)
- return -1;
- fd = accept(cfd, ldir);
- errstr(err, sizeof err);
- close(cfd);
- }
- if(fd < 0)
- logit("can't dial %s: %s", data, err);
- unmountnet();
- werrstr(err, sizeof err);
- return fd;
- }
- int
- postnote(int group, int pid, char *note)
- {
- char file[128];
- int f, r;
- /*
- * Use #p because /proc may not be in the namespace.
- */
- switch(group) {
- case PNPROC:
- sprint(file, "#p/%d/note", pid);
- break;
- case PNGROUP:
- sprint(file, "#p/%d/notepg", pid);
- break;
- default:
- return -1;
- }
- f = open(file, OWRITE);
- if(f < 0)
- return -1;
- r = strlen(note);
- if(write(f, note, r) != r) {
- close(f);
- return -1;
- }
- close(f);
- return 0;
- }
- /*
- * to circumscribe the accessible files we have to eliminate ..'s
- * and resolve all names from the root. We also remove any /bin/rc
- * special characters to avoid later problems with executed commands.
- */
- char *special = "`;| ";
- char*
- abspath(char *origpath)
- {
- char *p, *sp, *path;
- static String *rpath;
- if(rpath == nil)
- rpath = s_new();
- else
- s_reset(rpath);
- if(origpath == nil)
- s_append(rpath, curdir);
- else{
- if(*origpath != '/'){
- s_append(rpath, curdir);
- s_append(rpath, "/");
- }
- s_append(rpath, origpath);
- }
- path = s_to_c(rpath);
- for(sp = special; *sp; sp++){
- p = strchr(path, *sp);
- if(p)
- *p = 0;
- }
- cleanname(s_to_c(rpath));
- rpath->ptr = rpath->base+strlen(rpath->base);
- if(!accessok(s_to_c(rpath)))
- return nil;
- return s_to_c(rpath);
- }
- typedef struct Path Path;
- struct Path {
- Path *next;
- String *path;
- int inuse;
- int ok;
- };
- enum
- {
- Maxlevel = 16,
- Maxperlevel= 8,
- };
- Path *pathlevel[Maxlevel];
- Path*
- unlinkpath(char *path, int level)
- {
- String *s;
- Path **l, *p;
- int n;
- n = 0;
- for(l = &pathlevel[level]; *l; l = &(*l)->next){
- p = *l;
- /* hit */
- if(strcmp(s_to_c(p->path), path) == 0){
- *l = p->next;
- p->next = nil;
- return p;
- }
- /* reuse */
- if(++n >= Maxperlevel){
- *l = p->next;
- s = p->path;
- s_reset(p->path);
- memset(p, 0, sizeof *p);
- p->path = s_append(s, path);
- return p;
- }
- }
- /* allocate */
- p = mallocz(sizeof *p, 1);
- p->path = s_copy(path);
- return p;
- }
- void
- linkpath(Path *p, int level)
- {
- p->next = pathlevel[level];
- pathlevel[level] = p;
- p->inuse = 1;
- }
- void
- addpath(Path *p, int level, int ok)
- {
- p->ok = ok;
- p->next = pathlevel[level];
- pathlevel[level] = p;
- }
- int
- _accessok(String *s, int level)
- {
- Path *p;
- char *cp;
- int lvl, offset;
- static char httplogin[] = "/.httplogin";
- if(level < 0)
- return 1;
- lvl = level;
- if(lvl >= Maxlevel)
- lvl = Maxlevel - 1;
- p = unlinkpath(s_to_c(s), lvl);
- if(p->inuse){
- /* move to front */
- linkpath(p, lvl);
- return p->ok;
- }
- cp = strrchr(s_to_c(s), '/');
- if(cp == nil)
- offset = 0;
- else
- offset = cp - s_to_c(s);
- s_append(s, httplogin);
- if(access(s_to_c(s), AEXIST) == 0){
- addpath(p, lvl, 0);
- return 0;
- }
- /*
- * There's no way to shorten a String without
- * knowing the implementation.
- */
- s->ptr = s->base+offset;
- s_terminate(s);
- addpath(p, lvl, _accessok(s, level-1));
- return p->ok;
- }
- /*
- * check for a subdirectory containing .httplogin
- * at each level of the path.
- */
- int
- accessok(char *path)
- {
- int level, r;
- char *p;
- String *npath;
- npath = s_copy(path);
- p = s_to_c(npath)+1;
- for(level = 1; level < Maxlevel; level++){
- p = strchr(p, '/');
- if(p == nil)
- break;
- p++;
- }
- r = _accessok(npath, level-1);
- s_free(npath);
- return r;
- }
|