📄 ftpd.c
字号:
*(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);}intretrievecmd(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 */intlfwrite(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;}voidstore(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");}intstorecmd(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;}intappendcmd(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;}intstoreucmd(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;}intmkdircmd(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);}intdelcmd(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) */intabortcmd(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");}intsystemcmd(char *arg){ USED(arg); return reply("215 UNIX Type: L8 Version: Plan 9");}inthelpcmd(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;intrnfrcmd(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));}intrntocmd(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. */intdialdata(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;}intpostnote(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;}voidlinkpath(Path *p, int level){ p->next = pathlevel[level]; pathlevel[level] = p; p->inuse = 1;}voidaddpath(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. */intaccessok(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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -