#include #include #include #include #include enum{ // states for prompt scanning None, Esc, Brack, Semi, Bell, Dle = 0x10, Intr = 0x7f, Eot = 4, }; int ctl = -1; /* control fd (for break's) */ int consctl = -1; /* consctl fd */ int ttypid; /* pid's if the 2 processes (used to kill them) */ int netpid; int debug; int interrupted; enum { Tbind, Tmap, Npath = 8192, Nuser = 128, Nwindom = 32, Nprocpath = 128 }; typedef struct Map Map; struct Map { Map *next; char *local; // path on plan9 int nl; // strlen(local) char *remote; // path on windows int nr; // strlen(remote) int type; // one of the enum's above char *server; // windows server (type == Tmap only) char *share; // share on windows server (type == Tmap only) }; Map *Maproot = nil; int putstr(int fd, char *dbgfmt, char *netfmt, ...) { int rc; va_list arg; va_start(arg, netfmt); rc = vfprint(fd, netfmt, arg); if(debug){ fprint(2, "> "); vfprint(2, dbgfmt, arg); } va_end(arg); return rc; } int getstr(int fd, char *dbgfmt, char *buf, int len) { char *p; for(p = buf; len-- > 0; p++){ if(read(fd, p, 1) <= 0){ werrstr("hangup"); return -1; } if(*p == '\n') break; } *p = 0; if(debug){ fprint(2, "< "); fprint(2, dbgfmt, buf); } return 0; } char * strlwr(char *s) { char *p; for(p = s; *p; p++) *p = tolower(*p); return s; } char * estrdup(char *s) { char *p; if((p = strdup(s)) == nil) sysfatal("strdup: no memory %r\n"); return p; } void * emallocz(int n, int z) { void *p; if((p = mallocz(n, z)) == nil) sysfatal("mallocz: no memory %r\n"); return p; } int getmap(char *name) { Biobuf *bp; char *line, *a[8], *fn; Map *m, **p; if((fn = smprint("/usr/%s/lib/dosmap/%s", getuser(), name)) == nil) return -1; if((bp = Bopen(fn, OREAD)) == nil){ free(fn); return -1; } if(debug) print(" found: %s\n", fn); p = &Maproot; while((line = Brdline(bp, '\n')) != nil){ line[Blinelen(bp)-1] = 0; if(tokenize(line, a, nelem(a)) < 4) continue; if(*a[0] == '#') continue; m = emallocz(sizeof(Map), 1); m->remote = estrdup(a[0]); if(strlen(m->remote) != 2 || m->remote[1] != ':') sysfatal("%s: %s bad drive specification\n", fn, m->remote); m->nr = strlen(m->remote); m->server = estrdup(a[1]); m->share = estrdup(a[2]); m->local = estrdup(a[3]); m->nl = strlen(m->local); m->type = Tmap; *p = m; p = &(m->next); *p = nil; } Bterm(bp); free(fn); return 0; } void getnamespace(void) { int i; Biobuf *bp; Map *m, *n; char *line, *a[8], *fn; if((fn = smprint("/proc/%d/ns", getpid())) == nil) return; if((bp = Bopen(fn, OREAD)) == nil){ free(fn); return; } if(debug) print("namespace: %s\n", fn); while((line = Brdline(bp, '\n')) != nil){ line[Blinelen(bp)-1] = 0; if((i = tokenize(line, a, nelem(a))) < 3) continue; if(strcmp(a[0], "bind") != 0) continue; for(n = Maproot; n; n = n->next) if(n->nl > 1 && strncmp(n->local, a[i-2], n->nl) == 0) break; if(n == nil) continue; m = emallocz(sizeof(Map), 1); m->local = estrdup(a[i-1]); m->nl = strlen(m->local); if((m->remote = smprint("%s%s", n->remote, a[i-2]+n->nl)) == nil) sysfatal("no memory - %r\n"); m->nr = strlen(m->remote); m->type = Tbind; m->next = Maproot; Maproot = m; } Bterm(bp); free(fn); } int sendnote(int pid, char *msg) { int fd; char name[Nprocpath]; sprint(name, "/proc/%d/note", pid); fd = open(name, OWRITE); if(fd < 0) return -1; if(write(fd, msg, strlen(msg))!=strlen(msg)) return -1; return close(fd); } int dodial(char *dest) { char *name; int data; char devdir[NETPATHLEN]; name = netmkaddr(dest, "tcp", "9998"); if(debug) fprint(2, "dial %s...", name); data = dial(name, 0, devdir, 0); if(data < 0) sysfatal("%s cannot dial - %r\n", name); if(debug) fprint(2, "connected\n"); return data; } void setpath(char *path) { char *p; Map *m, *n; int fd, len; static char opath[Npath] = ""; for(p = path; *p; p++) if(*p == '\\') *p = '/'; if(strcmp(path, opath) == 0) return; snprint(opath, sizeof(opath), "%s", path); /* find longest match */ n = nil; len = 0; for(m = Maproot; m; m = m->next) if(cistrncmp(m->remote, path, m->nr) == 0) if(m->nr > len){ len = m->nr; n = m; } if(n == nil) return; if(debug) print("setpath: '%s' '%s'+%d -> %s%s\n", n->local, path, n->nr, n->local, path+n->nr); if((fd = open("/dev/wdir", OWRITE)) != 0){ fprint(fd, "%s%s", n->local, path+n->nr); close(fd); } } void dumpmap(void) { Map *m; for(m = Maproot; m; m = m->next){ if(m->type == Tmap) fprint(2, "map: %s %s \\\\%s\\%s\n", m->local, m->remote, m->server, m->share); if(m->type == Tbind) fprint(2, "bind: %s %s\n", m->local, m->remote); } } /* * parse windows prompt to extract * the current path (ugh) */ int conswdir(char c) { static char *p, buf[Npath]; static int state = None; if((p-buf) >= sizeof(buf)) p = buf; switch(state){ case None: p = buf; if(c == '\033') state++; break; case Esc: if(c == ']') state++; else state = None; break; case Brack: if(c == ';') state++; else state = None; break; case Semi: if(c == '\007') state++; else if(p-buf >= sizeof(buf)-1) state = None; else *p++ = c; break; case Bell: *p++ = 0; setpath(buf); state = None; break; } return state; } void fromkbd(int net) { int c, eofs; Biobuf ib, ob; Binit(&ib, 0, OREAD); Binit(&ob, net, OWRITE); eofs = 0; for(;;){ c = Bgetc(&ib); if(c == Dle) Bputc(&ob, Dle); if(c == -1){ Binit(&ib, 0, OREAD); if(interrupted){ interrupted = 0; Bputc(&ob, Dle); c = Intr; } else{ if(eofs++ > 8) break; Bputc(&ob, Dle); c = Eot; } } else eofs = 0; Bputc(&ob, c); if(Bbuffered(&ib) == 0) Bflush(&ob); } sleep(500); /* allow last message from other end to drain */ } void fromnet(int net) { Biobuf ib, ob; int c, eofs, crnls, freenl; Binit(&ib, net, OREAD); Binit(&ob, 1, OWRITE); eofs = 0; crnls = 0; freenl = 0; while(1){ if(Bbuffered(&ib) == 0) Bflush(&ob); c = Bgetc(&ib); if(c == -1){ if(interrupted){ interrupted = 0; Binit(&ib, net, OREAD); continue; } if(eofs++ >= 8) break; continue; } eofs = 0; switch(c){ case '\n': /* skip nl after string of cr's */ ++crnls; if(freenl == 0) break; freenl = 0; continue; case '\r': /* first cr becomes nl, remainder dropped */ if(crnls++ == 0){ freenl = 1; c = '\n'; break; } continue; case 0: /* remove nulls from crnl string */ if(crnls) continue; break; default: crnls = 0; freenl = 0; break; } if(conswdir(c) == None) Bputc(&ob, c); } } int auth(int net, char *windom, char *usr) { UserPasswd *up; if((up = auth_getuserpasswd(auth_getkey, "proto=pass service=w9cpu windom=%s %s", windom, usr)) == nil){ werrstr("cannot get key - %r"); return -1; } if(putstr(net, "user=%s\n", "%s\n", up->user) < 0) return -1; if(putstr(net, "passwd=%s\n", "%s\n", up->passwd) < 0) return -1; memset(up->passwd, 0, strlen(up->passwd)); free(up); return 0; } /* * only specify a username if it is explicit on the * command line. the usernames we use are in fact * user@domain tupels, this is useful as the username * is often different to that of the plan9 user, and * the domain you wish to authenticate against may also * differ from the remote hosts default domain */ void mkusr(char *buf, int n, char *uid) { *buf = 0; if(uid) snprint(buf, n, "user=%s", uid); } int mntserv(int net, char *usr) { Map *m; UserPasswd *up; for(m = Maproot; m; m = m->next){ if((up = auth_getuserpasswd(auth_getkey, "proto=pass service=cifs server=%s %s", m->server, usr)) == nil){ fprint(2, "%s: %s cannot get key - %r\n", argv0, m->server); continue; } putstr(net, "remote=%s\n", "%s\n", m->remote); putstr(net, "mount=\\\\%s\\%s\n", "\\\\%s\\%s\n", m->server, m->share); putstr(net, "user=%s\n", "%s\n", up->user); putstr(net, "passwd=%s\n", "%s\n", up->passwd); memset(up->passwd, 0, strlen(up->passwd)); free(up); } fprint(net, "\n"); // end of mapping list return 0; } int initcwd(int net) { int n; Map *m; char cwd[Npath]; getwd(cwd, sizeof(cwd)); for(m = Maproot; m; m = m->next){ if(strncmp(m->local, cwd, m->nl) == 0){ n = m->nl; if(n == 1) /* don't trim leading slash */ n = 0; if(putstr(net, "cwd='%s%s'", "%s%s\n", m->remote, cwd+n) < 0) return -1; return 0; } } if(putstr(net, "cwd='%s'\n", "%s\n", cwd) < 0) return -1; return 0; } int handshake(int net, char *host, char *uid, char *cmd) { char usr[Nuser], windom[Nwindom]; if(getstr(net, "windom='%s'\n", windom, sizeof(windom)) < 0) return -1; /* Windows doesn't care but factotum is case sensitive */ strlwr(windom); mkusr(usr, sizeof(usr), uid); if(auth(net, windom, usr) == -1) return -1; if(putstr(net, "host=%s\n", "%s\n", host) < 0) return -1; if(getmap(host) == -1) getmap(windom); mntserv(net, usr); getnamespace(); if(debug) dumpmap(); if(initcwd(net) < 0) return -1; if(putstr(net, "cmd='%s'\n", "%s\n", cmd) < 0) return -1; return 0; } static void home_toto(void) { int fd; char buf[Npath]; if((fd = open("/dev/wdir", OWRITE)) != 0){ getwd(buf, sizeof(buf)); fprint(fd, "%s", buf); close(fd); } } static void ding(void *u, char *msg) { USED(u); if(strstr(msg, "interrupt")){ interrupted = 1; noted(NCONT); } noted(NDFLT); } void dos(char *host, char *uid, char *cmd) { int pid, net; net = dodial(host); if(handshake(net, host, uid, cmd) < 0) sysfatal("authentication failed - %r\n"); ttypid = getpid(); switch(pid = rfork(RFPROC|RFFDG|RFMEM)){ case -1: fprint(2, "fork failed: %r"); exits("fork"); case 0: notify(ding); fromnet(net); home_toto(); sendnote(ttypid, "die"); exits(0); default: netpid = pid; notify(ding); fromkbd(net); sendnote(netpid, "die"); exits(0); } } void usage(void) { fprint(2, "usage: %s [-u user] [-h host] [-c cmd args...]\n", argv0); exits("usage"); } void main(int argc, char *argv[]) { int n, fd; char *p, *uid, cmd[4096], *host; *cmd = 0; debug = 0; uid = nil; host = nil; ARGBEGIN{ case 'd': debug++; break; case 'h': host = EARGF(usage()); break; case 'c': n = 0; p = EARGF(usage()); do n += snprint(cmd+n, sizeof(cmd)-n, " %s", p); while(p = ARGF()); break; case 'u': uid = EARGF(usage()); break; default: usage(); }ARGEND notify(ding); if(uid && strchr(uid, '@') == nil) sysfatal("%s - bad uid, wanted user@domain\n", uid); if(host == nil) if((host = getenv("dos")) == nil) sysfatal("set $dos\n"); if((fd = open("/dev/label", OWRITE)) != 0) fprint(fd, "dos %s", host); dos(host, uid, cmd); exits(nil); }