12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838 |
- #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 && (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");
- }
- /*
- * 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) {
- static long delay = 100;
- sleep(delay); /* deter password-guessers */
- if (delay < 60*1000)
- delay *= 2;
- 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);
- }
- }
- 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;
- }
|