123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836 |
- #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);
- }
- /*
- * 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 '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 && *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");
- return -1;
- }
- 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.
- */
- int
- usercmd(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.
- */
- 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)
- 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
- */
- 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);
- break;
- }
- }
- 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);
- break;
- }
- 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);
- break;
- }
- }
- 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;
- }
|