13 /* telnet control character */
16 /* representation types */
20 /* transmission modes */
30 /* read/write buffer size */
33 /* maximum ms we'll wait for a command */
34 Maxwait= 1000*60*30, /* inactive for 30 minutes, we hang up */
49 int namelistcmd(char*);
59 int reply(char*, ...);
60 int restartcmd(char*);
61 int retrievecmd(char*);
73 int crlfwrite(int, char*, int);
77 typedef struct Cmd Cmd;
87 { "abor", abortcmd, 0, },
88 { "allo", nopcmd, 1, },
89 { "appe", appendcmd, 1, },
90 { "cdup", cdupcmd, 1, },
91 { "cwd", cwdcmd, 1, },
92 { "dele", delcmd, 1, },
93 { "help", helpcmd, 0, },
94 { "list", listcmd, 1, },
95 { "mdtm", mdtmcmd, 1, },
96 { "mkd", mkdircmd, 1, },
97 { "mode", modecmd, 0, },
98 { "nlst", namelistcmd, 1, },
99 { "noop", nopcmd, 0, },
100 { "opts", optscmd, 0, },
101 { "pass", passcmd, 0, },
102 { "pasv", pasvcmd, 1, },
103 { "pwd", pwdcmd, 0, },
104 { "port", portcmd, 1, },
105 { "quit", quitcmd, 0, },
106 { "rest", restartcmd, 1, },
107 { "retr", retrievecmd, 1, },
108 { "rmd", delcmd, 1, },
109 { "rnfr", rnfrcmd, 1, },
110 { "rnto", rntocmd, 1, },
111 { "site", sitecmd, 1, },
112 { "size", sizecmd, 1, },
113 { "stor", storecmd, 1, },
114 { "stou", storeucmd, 1, },
115 { "stru", structcmd, 1, },
116 { "syst", systemcmd, 0, },
117 { "type", typecmd, 0, },
118 { "user", usercmd, 0, },
122 #define NONENS "/lib/namespace.ftp" /* default ns for none */
124 char user[Maxpath]; /* logged in user */
125 char curdir[Maxpath]; /* current directory path */
128 int type; /* transmission type */
129 int mode; /* transmission mode */
130 int structure; /* file structure */
131 char data[64]; /* data address */
132 int pid; /* transfer process */
133 int encryption; /* encryption state */
134 int isnone, anon_ok, anon_only, anon_everybody;
135 char cputype[Maxpath]; /* the environment variable of the same name */
136 char bindir[Maxpath]; /* bin directory for this architecture */
137 char mailaddr[Maxpath];
138 char *namespace = NONENS;
141 int createperm = 0660;
143 vlong offset; /* from restart command */
147 typedef struct Passive Passive;
154 uchar ipaddr[IPaddrlen];
160 logit(char *fmt, ...)
166 vseprint(buf, buf+sizeof(buf), fmt, arg);
168 syslog(0, FTPLOG, "%s.%s %s", nci->rsys, nci->rserv, buf);
174 syslog(0, "ftp", "usage: %s [-aAde] [-n nsfile]", argv0);
175 fprint(2, "usage: %s [-aAde] [-n nsfile]\n", argv0);
180 * read commands from the control stream and dispatch
183 main(int argc, char **argv)
193 case 'a': /* anonymous OK */
208 namespace = EARGF(usage());
214 /* open log file before doing a newns */
215 syslog(0, FTPLOG, nil);
217 /* find out who is calling */
219 nci = getnetconninfo(nil, 0);
221 nci = getnetconninfo(argv[argc-1], 0);
223 sysfatal("ftpd needs a network address");
225 strcpy(mailaddr, "?");
228 /* figure out which binaries to bind in later (only for none) */
229 arg = getenv("cputype");
231 strecpy(cputype, cputype+sizeof cputype, arg);
233 strcpy(cputype, "mips");
234 /* shurely /%s/bin */
235 snprint(bindir, sizeof(bindir), "/bin/%s/bin", cputype);
237 Binit(&in, 0, OREAD);
238 reply("220 Plan 9 FTP server ready");
240 while(cmd = Brdline(&in, '\n')){
244 * strip out trailing cr's & lf and delimit with null
250 while(i > 0 && cmd[i-1] == '\r')
254 * hack for GatorFTP+, look for a 0x10 used as a delimiter
256 p = strchr(cmd, 0x10);
261 * get rid of telnet control sequences (we don't need them)
263 while(*cmd && (uchar)*cmd == Iac){
270 * parse the message (command arg)
272 arg = strchr(cmd, ' ');
280 * ignore blank commands
286 * lookup the command and do it
288 for(p = cmd; *p; p++)
290 for(t = cmdtab; t->name; t++)
291 if(strcmp(cmd, t->name) == 0){
292 if(t->needlogin && !loggedin)
294 else if((*t->f)(arg) < 0)
298 if(t->f != restartcmd){
300 * the file offset is set to zero following
301 * all commands except the restart command
307 * the OOB bytes preceding an abort from UCB machines
308 * comes out as something unrecognizable instead of
309 * IAC's. Certainly a Plan 9 bug but I can't find it.
310 * This is a major hack to avoid the problem. -- presotto
313 if(i > 4 && strcmp(cmd+i-4, "abor") == 0){
316 logit("%s (%s) command not implemented", cmd, arg?arg:"");
317 reply("502 %s command not implemented", cmd);
323 postnote(PNPROC, pid, "kill");
330 reply(char *fmt, ...)
336 s = vseprint(buf, buf+sizeof(buf)-3, fmt, arg);
344 write(1, buf, s - buf);
351 return reply("530 Sod off, service requires login");
355 * run a command in a separate process
358 asproc(void (*f)(char*, int), char *arg, int arg2)
363 /* wait for previous command to finish */
366 if(i == pid || i < 0)
371 switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){
373 return reply("450 Out of processes: %r");
384 * run a command to filter a tail
387 transfer(char *cmd, char *a1, char *a2, char *a3, int image)
389 int n, dfd, fd, bytes, eofs, pid;
394 reply("150 Opening data connection for %s (%s)", cmd, data);
397 return reply("425 Error opening data connection: %r");
400 return reply("520 Internal Error: %r");
403 switch(pid = rfork(RFFDG|RFPROC|RFNAMEG)){
405 return reply("450 Out of processes: %r");
407 logit("running %s %s %s %s pid %d",
408 cmd, a1?a1:"", a2?a2:"" , a3?a3:"",getpid());
414 fd = open("#s/boot", ORDWR);
416 || bind("#/", "/", MAFTER) == -1
417 || amount(fd, "/bin", MREPL, "") == -1
418 || bind("#c", "/dev", MAFTER) == -1
419 || bind(bindir, "/bin", MREPL) == -1)
420 exits("building name space");
423 execl(cmd, cmd, a1, a2, a3, nil);
428 while((n = read(pfd[1], buf, sizeof buf)) >= 0){
448 n = crlfwrite(dfd, p, n);
450 n = write(dfd, p, n);
452 postnote(PNPROC, pid, "kill");
463 /* wait for this command to finish */
466 if(w == nil || w->pid == pid)
470 if(w != nil && w->msg != nil && w->msg[0] != 0){
473 logit("%s %s %s %s failed %s", cmd, a1?a1:"", a2?a2:"" , a3?a3:"", w->msg);
476 reply("226 Transfer complete");
485 if(arg == 0 || *arg == 0){
486 reply("501 Syntax error in parameters or arguments");
489 if(p = strchr(arg, ' '))
491 if(cistrcmp(arg, "UTF-8") == 0 || cistrcmp(arg, "UTF8") == 0){
492 reply("200 Command okay");
495 reply("502 %s option not implemented", arg);
506 reply("510 Plan 9 FTP daemon still alive");
514 loginuser(char *user, char *nsfile, int gotoslash)
516 logit("login %s %s %s %s", user, mailaddr, nci->rsys, nsfile);
517 if(nsfile != nil && newns(user, nsfile) < 0){
518 logit("namespace file %s does not exist", nsfile);
519 return reply("530 Not logged in: login out of service");
521 getwd(curdir, sizeof(curdir));
526 putenv("service", "ftp");
529 reply("230- If you have problems, send mail to 'postmaster'.");
530 return reply("230 Logged in");
539 sleep(pause); /* deter guessers */
540 if (pause < (1UL << 20))
547 * get a user id, reply with a challenge. The users 'anonymous'
548 * and 'ftp' are equivalent to 'none'. The user 'none' requires
556 logit("user %s %s", name, nci->rsys);
558 return reply("530 Already logged in as %s", user);
559 if(name == 0 || *name == 0)
560 return reply("530 user command needs user name");
566 strncpy(user, name, sizeof(user));
569 user[sizeof(user)-1] = 0;
570 if(strcmp(user, "anonymous") == 0 || strcmp(user, "ftp") == 0)
571 strcpy(user, "none");
572 else if(anon_everybody)
575 if(strcmp(user, "Administrator") == 0 || strcmp(user, "admin") == 0)
576 return reply("530 go away, script kiddie");
577 else if(strcmp(user, "*none") == 0){
579 return reply("530 Not logged in: anonymous disallowed");
580 return loginuser("none", namespace, 1);
582 else if(strcmp(user, "none") == 0){
584 return reply("530 Not logged in: anonymous disallowed");
585 return reply("331 Send email address as password");
588 return reply("530 Not logged in: anonymous access only");
590 isnoworld = noworld(name);
592 return reply("331 OK");
594 /* consult the auth server */
597 if((ch = auth_challenge("proto=p9cr role=server user=%q", user)) == nil)
598 return reply("421 %r");
599 return reply("331 encrypt challenge, %s, as a password", ch->chal);
603 * get a password, set up user if it works.
606 passcmd(char *response)
615 if(strcmp(user, "none") == 0 || strcmp(user, "*none") == 0){
616 /* for none, accept anything as a password */
618 strncpy(mailaddr, response, sizeof(mailaddr)-1);
619 return loginuser("none", namespace, 1);
623 /* noworld gets a password in the clear */
624 if(login(user, response, "/lib/namespace.noworld") < 0)
625 return reply("530 Not logged in");
627 /* login has already setup the namespace */
628 return loginuser(user, nil, 0);
630 /* for everyone else, do challenge response */
632 return reply("531 Send user id before encrypted challenge");
634 ch->nresp = strlen(response);
635 ai = auth_response(ch);
636 if(ai == nil || auth_chuid(ai, nil) < 0) {
639 return reply("530 Not logged in: %r");
641 /* chown network connection */
651 /* if the user has specified a namespace for ftp, use it */
652 snprint(namefile, sizeof(namefile), "/usr/%s/lib/namespace.ftp", user);
653 strcpy(mailaddr, user);
655 if(access(namefile, 0) == 0)
656 return loginuser(user, namefile, 0);
658 return loginuser(user, "/lib/namespace", 0);
663 * print working directory
669 return reply("550 Pwd takes no argument");
670 return reply("257 \"%s\" is the current directory", curdir);
682 /* shell cd semantics */
683 if(dir == 0 || *dir == 0){
687 snprint(buf, sizeof buf, "/usr/%s", user);
690 if(accessok(rp) == 0)
696 return reply("550 Permission denied");
699 return reply("550 Cwd failed: %r");
701 return reply("250 directory changed to %s", curdir);
720 postnote(PNPROC, pid, "kill");
732 return reply("501 Type command needs arguments");
750 return reply("501 Unimplemented type %s", x);
753 return reply("200 Type %s", type==Tascii ? "Ascii" : "Image");
760 return reply("501 Mode command needs arguments");
762 switch(tolower(*arg)){
767 return reply("501 Unimplemented mode %c", *arg);
771 return reply("200 Stream mode");
778 return reply("501 Struct command needs arguments");
780 switch(tolower(*arg)){
785 return reply("501 Unimplemented structure %c", *arg);
788 return reply("200 File structure");
798 return reply("501 Port command needs arguments");
799 n = getfields(arg, field, 7, 0, ", ");
801 return reply("501 Incorrect port specification");
802 snprint(data, sizeof data, "tcp!%.3s.%.3s.%.3s.%.3s!%d", field[0], field[1], field[2],
803 field[3], atoi(field[4])*256 + atoi(field[5]));
804 return reply("200 Data port is %s", data);
814 if(bind("#/", "/", MAFTER) == -1){
815 logit("can't bind #/ to /: %r");
816 return reply("500 can't bind #/ to /: %r");
819 if(bind(nci->spec, "/net", MBEFORE) == -1){
820 logit("can't bind %s to /net: %r", nci->spec);
821 rv = reply("500 can't bind %s to /net: %r", nci->spec);
852 p->afd = announce("tcp!*!0", passive.adir);
855 return reply("500 No free ports");
857 nnci = getnetconninfo(p->adir, -1);
860 /* parse the local address */
862 logit("local sys is %s", nci->lsys);
863 parseip(p->ipaddr, nci->lsys);
864 if(ipcmp(p->ipaddr, v4prefix) == 0 || ipcmp(p->ipaddr, IPnoaddr) == 0)
865 parseip(p->ipaddr, nci->lsys);
866 p->port = atoi(nnci->lserv);
868 freenetconninfo(nnci);
871 return reply("227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)",
872 p->ipaddr[IPv4off+0], p->ipaddr[IPv4off+1], p->ipaddr[IPv4off+2], p->ipaddr[IPv4off+3],
873 p->port>>8, p->port&0xff);
880 int Cflag, rflag, tflag, Rflag;
890 strcpy(asc, "----------");
898 for(p = asc+1; p < asc + 10; p += 3, m<<=3){
909 listfile(Biobufhdr *b, char *name, int lflag, char *dname)
924 if(strncmp(x, "/incoming/", sizeof("/incoming/")-1) != 0)
930 strcpy(ts, ctime(d->mtime));
933 if(now - d->mtime > 6*30*24*60*60)
934 memmove(ts+11, ts+23, 5);
936 /* Unix style long listing */
943 Bprint(b, "%s %3d %-8s %-8s %7lld %s ",
944 mode2asc(d->mode), links,
945 d->uid, d->gid, d->length, ts+4);
947 if(Cflag && maxnamelen < 40){
949 pad = ((col+maxnamelen)/(maxnamelen+1))*(maxnamelen+1);
950 if(pad+maxnamelen+1 < 60){
951 Bprint(b, "%*s", pad-col+n, name);
955 Bprint(b, "\r\n%s", name);
961 Bprint(b, "%s/", dname);
962 Bprint(b, "%s\r\n", name);
967 dircomp(void *va, void *vb)
976 rv = b->mtime - a->mtime;
978 rv = strcmp(a->name, b->name);
979 return (rflag?-1:1)*rv;
982 listdir(char *name, Biobufhdr *b, int lflag, int *printname, Globlist *gl)
991 fd = open(name, OREAD);
993 Bprint(b, "can't read %s: %r\r\n", name);
999 Bprint(b, "\r\n%s:\r\n", name);
1003 n = dirreadall(fd, &p);
1006 for(i = 0; i < n; i++){
1007 l = strlen(p[i].name);
1013 /* Unix style total line */
1016 for(i = 0; i < n; i++){
1017 if(p[i].qid.type & QTDIR)
1020 total += p[i].length;
1022 Bprint(b, "total %ulld\r\n", total/512);
1025 qsort(p, n, sizeof(Dir), dircomp);
1026 for(i = 0; i < n; i++){
1027 if(Rflag && (p[i].qid.type & QTDIR)){
1029 globadd(gl, name, p[i].name);
1031 listfile(b, p[i].name, lflag, dname);
1036 list(char *arg, int lflag)
1053 logit("ls %s (. = %s)", arg, curdir);
1055 /* process arguments, understand /bin/ls -l option */
1057 argv[0] = "/bin/ls";
1058 argc = getfields(arg, argv+1, Narg-2, 1, " \t") + 1;
1087 reply("425 Error opening data connection: %r");
1090 reply("150 Opened data connection (%s)", data);
1092 Binits(&bh, dfd, OWRITE, buf, sizeof(buf));
1099 for(i = 0; i < argc; i++){
1105 printname = gl->first != nil && gl->first->next != nil;
1109 for(g = gl->first; g; g = g->next)
1110 if(g->glob && (n = strlen(s_to_c(g->glob))) > maxnamelen)
1112 while(s = globiter(gl)){
1114 logit("glob %s", s);
1125 if(d->qid.type & QTDIR)
1126 listdir(s, &bh, lflag, &printname, gl);
1128 listfile(&bh, s, lflag, 0);
1135 Bprint(&bh, "\r\n");
1139 reply("226 Transfer complete (list %s)", arg);
1142 namelistcmd(char *arg)
1144 return asproc(list, arg, 0);
1149 return asproc(list, arg, 1);
1153 * fuse compatability
1161 fd = open("#c/user", OREAD);
1164 n = read(fd, buf, sizeof buf - 1);
1167 if(strcmp(buf, "none") == 0)
1182 return reply("501 bad site command");
1183 nf = tokenize(arg, f, nelem(f));
1184 if(nf != 3 || cistrcmp(f[0], "chmod") != 0)
1185 return reply("501 bad site command");
1187 return reply("550 Permission denied");
1190 return reply("501 site chmod: file does not exist");
1192 d->mode |= strtoul(f[1], 0, 8) & 0777;
1193 r = dirwstat(f[2], d);
1196 return reply("550 Permission denied %r");
1197 return reply("200 very well, then");
1201 * return the size of the file
1210 return reply("501 Size command requires pathname");
1214 return reply("501 %r accessing %s", arg);
1215 rv = reply("213 %lld", d->length);
1221 * return the modify time of the file
1231 return reply("501 Mdtm command requires pathname");
1233 return reply("550 Permission denied");
1236 return reply("501 %r accessing %s", arg);
1237 t = gmtime(d->mtime);
1238 rv = reply("213 %4.4d%2.2d%2.2d%2.2d%2.2d%2.2d",
1239 t->year+1900, t->mon+1, t->mday,
1240 t->hour, t->min, t->sec);
1246 * set an offset to start reading a file from
1247 * only lasts for one command
1250 restartcmd(char *arg)
1253 return reply("501 Restart command requires offset");
1254 offset = atoll(arg);
1257 return reply("501 Bad offset");
1260 return reply("350 Restarting at %lld. Send STORE or RETRIEVE", offset);
1264 * send a file to the user
1267 crlfwrite(int fd, char *p, int n)
1272 for(np = buf, ep = p + n; p < ep; p++){
1277 if(write(fd, buf, np - buf) == np - buf)
1283 retrievedir(char *arg)
1290 reply("550 This file requires type binary/image");
1295 p = strrchr(s_to_c(file), '/');
1296 if(p != s_to_c(file)){
1298 chdir(s_to_c(file));
1304 n = transfer("/bin/tar", "c", p, 0, 1);
1306 logit("get %s failed", arg);
1308 logit("get %s OK %d", arg, n);
1312 retrieve(char *arg, int arg2)
1314 int dfd, fd, n, i, bytes;
1321 p = strchr(arg, '\r');
1323 logit("cr in file name", arg);
1327 fd = open(arg, OREAD);
1330 if(n > 4 && strcmp(arg+n-4, ".tar") == 0){
1334 if(d->qid.type & QTDIR){
1342 logit("get %s failed", arg);
1343 reply("550 Error opening %s: %r", arg);
1347 if(seek(fd, offset, 0) < 0){
1348 reply("550 %s: seek to %lld failed", arg, offset);
1354 if(d->qid.type & QTDIR){
1355 reply("550 %s: not a plain file.", arg);
1363 n = read(fd, buf, sizeof(buf));
1365 logit("get %s failed", arg, mailaddr, nci->rsys);
1366 reply("550 Error reading %s: %r", arg);
1372 for(p = buf, ep = &buf[n]; p < ep; p++)
1375 reply("550 This file requires type binary/image");
1379 reply("150 Opening data connection for %s (%s)", arg, data);
1382 reply("425 Error opening data connection: %r");
1391 i = write(dfd, buf, n);
1394 i = crlfwrite(dfd, buf, n);
1400 logit("get %s %r to data connection after %d", arg, bytes);
1401 reply("550 Error writing to data connection: %r");
1405 } while((n = read(fd, buf, sizeof(buf))) > 0);
1408 logit("get %s %r after %d", arg, bytes);
1412 reply("226 Transfer complete");
1413 logit("get %s OK %d", arg, bytes);
1416 retrievecmd(char *arg)
1419 return reply("501 Retrieve command requires an argument");
1422 return reply("550 Permission denied");
1424 return asproc(retrieve, arg, 0);
1428 * get a file from the user
1431 lfwrite(int fd, char *p, int n)
1436 for(np = buf, ep = p + n; p < ep; p++){
1440 if(write(fd, buf, np - buf) == np - buf)
1446 store(char *arg, int fd)
1451 reply("150 Opening data connection for %s (%s)", arg, data);
1454 reply("425 Error opening data connection: %r");
1459 while((n = read(dfd, buf, sizeof(buf))) > 0){
1462 i = write(fd, buf, n);
1465 i = lfwrite(fd, buf, n);
1471 reply("550 Error writing file");
1477 logit("put %s OK", arg);
1478 reply("226 Transfer complete");
1486 return reply("501 Store command requires an argument");
1489 return reply("550 Permission denied");
1490 if(isnone && strncmp(arg, "/incoming/", sizeof("/incoming/")-1))
1491 return reply("550 Permission denied");
1493 fd = open(arg, OWRITE);
1495 return reply("550 Error opening %s: %r", arg);
1496 if(seek(fd, offset, 0) == -1)
1497 return reply("550 Error seeking %s to %d: %r",
1500 fd = create(arg, OWRITE, createperm);
1502 return reply("550 Error creating %s: %r", arg);
1505 rv = asproc(store, arg, fd);
1510 appendcmd(char *arg)
1515 return reply("501 Append command requires an argument");
1517 return reply("550 Permission denied");
1520 return reply("550 Error creating %s: Permission denied", arg);
1521 fd = open(arg, OWRITE);
1523 fd = create(arg, OWRITE, createperm);
1525 return reply("550 Error creating %s: %r", arg);
1529 rv = asproc(store, arg, fd);
1534 storeucmd(char *arg)
1541 return reply("550 Permission denied");
1542 strncpy(name, "ftpXXXXXXXXXXX", sizeof name);
1544 fd = create(name, OWRITE, createperm);
1546 return reply("550 Error creating %s: %r", name);
1548 rv = asproc(store, name, fd);
1554 mkdircmd(char *name)
1559 return reply("501 Mkdir command requires an argument");
1561 return reply("550 Permission denied");
1562 name = abspath(name);
1564 return reply("550 Permission denied");
1565 fd = create(name, OREAD, DMDIR|0775);
1567 return reply("550 Can't create %s: %r", name);
1569 return reply("226 %s created", name);
1576 return reply("501 Rmdir/delete command requires an argument");
1578 return reply("550 Permission denied");
1579 name = abspath(name);
1581 return reply("550 Permission denied");
1582 if(remove(name) < 0)
1583 return reply("550 Can't remove %s: %r", name);
1585 return reply("226 %s removed", name);
1589 * kill off the last transfer (if the process still exists)
1596 logit("abort pid %d", pid);
1598 if(postnote(PNPROC, pid, "kill") == 0)
1599 reply("426 Command aborted");
1601 logit("postnote pid %d %r", pid);
1603 return reply("226 Abort processed");
1607 systemcmd(char *arg)
1610 return reply("215 UNIX Type: L8 Version: Plan 9");
1621 reply("214- the following commands are implemented:");
1625 for(i = 0; cmdtab[i].name; i++){
1627 reply("214-%s", buf);
1630 p = seprint(p, e, " %-5.5s", cmdtab[i].name);
1633 reply("214-%s", buf);
1639 * renaming a file takes two commands
1641 static String *filepath;
1647 return reply("550 Permission denied");
1649 return reply("501 Rename command requires an argument");
1650 from = abspath(from);
1652 return reply("550 Permission denied");
1654 filepath = s_copy(from);
1657 s_append(filepath, from);
1659 return reply("350 Rename %s to ...", s_to_c(filepath));
1669 return reply("550 Permission denied");
1671 return reply("501 Rename command requires an argument");
1674 return reply("550 Permission denied");
1675 if(filepath == nil || *(s_to_c(filepath)) == 0)
1676 return reply("503 Rnto must be preceeded by an rnfr");
1678 tp = strrchr(to, '/');
1679 fp = strrchr(s_to_c(filepath), '/');
1680 if((tp && fp == 0) || (fp && tp == 0)
1681 || (fp && tp && (fp-s_to_c(filepath) != tp-to || memcmp(s_to_c(filepath), to, tp-to))))
1682 return reply("550 Rename can't change directory");
1688 if(dirwstat(s_to_c(filepath), &nd) < 0)
1689 r = reply("550 Can't rename %s to %s: %r\n", s_to_c(filepath), to);
1691 r = reply("250 %s now %s", s_to_c(filepath), to);
1698 * to dial out we need the network file system in our
1712 fd = dial(data, "20", 0, 0);
1716 cfd = listen(passive.adir, ldir);
1719 fd = accept(cfd, ldir);
1724 errstr(err, sizeof err);
1726 logit("can't dial %s: %s", data, err);
1728 errstr(err, sizeof err);
1733 postnote(int group, int pid, char *note)
1739 * Use #p because /proc may not be in the namespace.
1743 sprint(file, "#p/%d/note", pid);
1746 sprint(file, "#p/%d/notepg", pid);
1752 f = open(file, OWRITE);
1757 if(write(f, note, r) != r) {
1766 * to circumscribe the accessible files we have to eliminate ..'s
1767 * and resolve all names from the root. We also remove any /bin/rc
1768 * special characters to avoid later problems with executed commands.
1770 char *special = "`;| ";
1773 abspath(char *origpath)
1775 char *p, *sp, *path;
1776 static String *rpath;
1784 s_append(rpath, curdir);
1786 if(*origpath != '/'){
1787 s_append(rpath, curdir);
1788 s_append(rpath, "/");
1790 s_append(rpath, origpath);
1792 path = s_to_c(rpath);
1794 for(sp = special; *sp; sp++){
1795 p = strchr(path, *sp);
1800 cleanname(s_to_c(rpath));
1801 rpath->ptr = rpath->base+strlen(rpath->base);
1803 if(!accessok(s_to_c(rpath)))
1806 return s_to_c(rpath);
1809 typedef struct Path Path;
1823 Path *pathlevel[Maxlevel];
1826 unlinkpath(char *path, int level)
1833 for(l = &pathlevel[level]; *l; l = &(*l)->next){
1836 if(strcmp(s_to_c(p->path), path) == 0){
1842 if(++n >= Maxperlevel){
1846 memset(p, 0, sizeof *p);
1847 p->path = s_append(s, path);
1853 p = mallocz(sizeof *p, 1);
1854 p->path = s_copy(path);
1859 linkpath(Path *p, int level)
1861 p->next = pathlevel[level];
1862 pathlevel[level] = p;
1867 addpath(Path *p, int level, int ok)
1870 p->next = pathlevel[level];
1871 pathlevel[level] = p;
1875 _accessok(String *s, int level)
1880 static char httplogin[] = "/.httplogin";
1888 p = unlinkpath(s_to_c(s), lvl);
1894 cp = strrchr(s_to_c(s), '/');
1898 offset = cp - s_to_c(s);
1899 s_append(s, httplogin);
1900 if(access(s_to_c(s), AEXIST) == 0){
1906 * There's no way to shorten a String without
1907 * knowing the implementation.
1909 s->ptr = s->base+offset;
1911 addpath(p, lvl, _accessok(s, level-1));
1917 * check for a subdirectory containing .httplogin
1918 * at each level of the path.
1921 accessok(char *path)
1927 npath = s_copy(path);
1928 p = s_to_c(npath)+1;
1929 for(level = 1; level < Maxlevel; level++){
1936 r = _accessok(npath, level-1);