📄 telnetd.c
字号:
#include <u.h>#include <libc.h>#include <bio.h>#include <auth.h>#include <libsec.h>#include "../ip/telnet.h"/* console state (for consctl) */typedef struct Consstate Consstate;struct Consstate{ int raw; int hold;};Consstate *cons;int notefd; /* for sending notes to the child */int noproto; /* true if we shouldn't be using the telnet protocol */int trusted; /* true if we need not authenticate - current user is ok */int nonone = 1; /* don't allow none logins */int noworldonly; /* only noworld accounts */enum{ Maxpath= 256, Maxuser= 64, Maxvar= 32,};/* input and output buffers for network connection */Biobuf netib;Biobuf childib;char remotesys[Maxpath]; /* name of remote system */int alnum(int);int conssim(void);int fromchild(char*, int);int fromnet(char*, int);int termchange(Biobuf*, int);int termsub(Biobuf*, uchar*, int);int xlocchange(Biobuf*, int);int xlocsub(Biobuf*, uchar*, int);int challuser(char*);int noworldlogin(char*);void* share(ulong);int doauth(char*);#define TELNETLOG "telnet"voidlogit(char *fmt, ...){ va_list arg; char buf[8192]; va_start(arg, fmt); vseprint(buf, buf + sizeof(buf) / sizeof(*buf), fmt, arg); va_end(arg); syslog(0, TELNETLOG, "(%s) %s", remotesys, buf);}voidgetremote(char *dir){ int fd, n; char remfile[Maxpath]; sprint(remfile, "%s/remote", dir); fd = open(remfile, OREAD); if(fd < 0) strcpy(remotesys, "unknown2"); n = read(fd, remotesys, sizeof(remotesys)-1); if(n>0) remotesys[n-1] = 0; else strcpy(remotesys, remfile); close(fd);}voidmain(int argc, char *argv[]){ char buf[1024]; int fd; char user[Maxuser]; int tries = 0; int childpid; int n, eofs; memset(user, 0, sizeof(user)); ARGBEGIN { case 'n': opt[Echo].local = 1; noproto = 1; break; case 'p': noproto = 1; break; case 'a': nonone = 0; break; case 't': trusted = 1; strncpy(user, getuser(), sizeof(user)-1); break; case 'u': strncpy(user, ARGF(), sizeof(user)-1); break; case 'd': debug = 1; break; case 'N': noworldonly = 1; break; } ARGEND if(argc) getremote(argv[argc-1]); else strcpy(remotesys, "unknown"); /* options we need routines for */ opt[Term].change = termchange; opt[Term].sub = termsub; opt[Xloc].sub = xlocsub; /* setup default telnet options */ if(!noproto){ send3(1, Iac, Will, opt[Echo].code); send3(1, Iac, Do, opt[Term].code); send3(1, Iac, Do, opt[Xloc].code); } /* shared data for console state */ cons = share(sizeof(Consstate)); if(cons == 0) fatal("shared memory", 0, 0); /* authenticate and create new name space */ Binit(&netib, 0, OREAD); if (!trusted){ while(doauth(user) < 0) if(++tries == 5){ logit("failed as %s: %r", user); print("authentication failure:%r\r\n"); exits("authentication"); } } logit("logged in as %s", user); putenv("service", "con"); /* simulate /dev/consctl and /dev/cons using pipes */ fd = conssim(); if(fd < 0) fatal("simulating", 0, 0); Binit(&childib, fd, OREAD); /* start a shell in a different process group */ switch(childpid = rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG)){ case -1: fatal("fork", 0, 0); case 0: close(fd); fd = open("/dev/cons", OREAD); dup(fd, 0); close(fd); fd = open("/dev/cons", OWRITE); dup(fd, 1); dup(fd, 2); close(fd); segdetach(cons); execl("/bin/rc", "rc", "-il", nil); fatal("/bin/rc", 0, 0); default: sprint(buf, "/proc/%d/notepg", childpid); notefd = open(buf, OWRITE); break; } /* two processes to shuttle bytes twixt children and network */ switch(fork()){ case -1: fatal("fork", 0, 0); case 0: eofs = 0; for(;;){ n = fromchild(buf, sizeof(buf)); if(n <= 0){ if(eofs++ > 2) break; continue; } eofs = 0; if(write(1, buf, n) != n) break; } break; default: while((n = fromnet(buf, sizeof(buf))) >= 0) if(write(fd, buf, n) != n) break; break; } /* kill off all server processes */ sprint(buf, "/proc/%d/notepg", getpid()); fd = open(buf, OWRITE); write(fd, "die", 3); exits(0);}voidprompt(char *p, char *b, int n, int raw){ char *e; int i; int echo; echo = opt[Echo].local; if(raw) opt[Echo].local = 0; print("%s: ", p); for(e = b+n; b < e;){ i = fromnet(b, e-b); if(i <= 0) exits("fromnet: hungup"); b += i; if(*(b-1) == '\n' || *(b-1) == '\r'){ *(b-1) = 0; break; } } if(raw) opt[Echo].local = echo;}/* * challenge user */intchalluser(char *user){ char nchall[64]; char response[64]; Chalstate *ch; AuthInfo *ai; if(strcmp(user, "none") == 0){ if(nonone) return -1; newns("none", nil); return 0; } if((ch = auth_challenge("proto=p9cr role=server user=%q", user)) == nil) return -1; snprint(nchall, sizeof nchall, "challenge: %s\r\nresponse", ch->chal); prompt(nchall, response, sizeof response, 0); ch->resp = response; ch->nresp = strlen(response); ai = auth_response(ch); auth_freechal(ch); if(ai == nil){ rerrstr(response, sizeof response); print("!%s\n", response); return -1; } if(auth_chuid(ai, nil) < 0) return -1; return 0;}/* * use the in the clear apop password to change user id */intnoworldlogin(char *user){ char password[256]; prompt("password", password, sizeof(password), 1); if(login(user, password, "/lib/namespace.noworld") < 0) return -1; rfork(RFNOMNT); /* sandbox */ return 0;}intdoauth(char *user){ if(*user == 0) prompt("user", user, Maxuser, 0); if(noworld(user)) return noworldlogin(user); if(noworldonly) return -1; return challuser(user); }/* * Process some input from the child, add protocol if needed. If * the input buffer goes empty, return. */intfromchild(char *bp, int len){ int c; char *start; for(start = bp; bp-start < len-1; ){ c = Bgetc(&childib); if(c < 0){ if(bp == start) return -1; else break; } if(cons->raw == 0 && c == '\n') *bp++ = '\r'; *bp++ = c; if(Bbuffered(&childib) == 0) break; } return bp-start;}/* * Read from the network up to a '\n' or some other break. * * If in binary mode, buffer characters but don't * * The following characters are special: * '\r\n's and '\r's get turned into '\n's. * ^H erases the last character buffered. * ^U kills the whole line buffered. * ^W erases the last word * ^D causes a 0-lenght line to be returned. * Intr causes an "interrupt" note to be sent to the children. */#define ECHO(c) { *ebp++ = (c); }intfromnet(char *bp, int len){ int c; char echobuf[1024]; char *ebp; char *start; static int crnl; static int doeof; /* simulate an EOF as a 0 length input */ if(doeof){ doeof = 0; return 0; } for(ebp = echobuf,start = bp; bp-start < len && ebp-echobuf < sizeof(echobuf); ){ c = Bgetc(&netib); if(c < 0){ if(bp == start) return -1; else break; } /* telnet protocol only */ if(!noproto){ /* protocol messages */ switch(c){ case Iac: crnl = 0; c = Bgetc(&netib); if(c == Iac) break; control(&netib, c); continue; } } /* \r\n or \n\r become \n */ if(c == '\r' || c == '\n'){ if(crnl && crnl != c){ crnl = 0; continue; } if(cons->raw == 0 && opt[Echo].local){ ECHO('\r'); ECHO('\n'); } crnl = c; if(cons->raw == 0) *bp++ = '\n'; else *bp++ = c; break; } else crnl = 0; /* raw processing (each character terminates */ if(cons->raw){ *bp++ = c; break; } /* in binary mode, there are no control characters */ if(opt[Binary].local){ if(opt[Echo].local) ECHO(c); *bp++ = c; continue; } /* cooked processing */ switch(c){ case 0x00: if(noproto) /* telnet ignores nulls */ *bp++ = c; continue; case 0x04: if(bp != start) doeof = 1; goto out; case 0x08: /* ^H */ if(start < bp) bp--; if(opt[Echo].local) ECHO(c); break; case 0x15: /* ^U */ bp = start; if(opt[Echo].local){ ECHO('^'); ECHO('U'); ECHO('\r'); ECHO('\n'); } break; case 0x17: /* ^W */ if (opt[Echo].local) { while (--bp >= start && !alnum(*bp)) ECHO('\b'); while (bp >= start && alnum(*bp)) { ECHO('\b'); bp--; } bp++; } break; case 0x7f: /* Del */ write(notefd, "interrupt", 9); bp = start; break; default: if(opt[Echo].local) ECHO(c); *bp++ = c; } if(ebp != echobuf) write(1, echobuf, ebp-echobuf); ebp = echobuf; }out: if(ebp != echobuf) write(1, echobuf, ebp-echobuf); return bp - start;}inttermchange(Biobuf *bp, int cmd){ char buf[8]; char *p = buf; if(cmd != Will) return 0; /* ask other side to send term type info */ *p++ = Iac; *p++ = Sb; *p++ = opt[Term].code; *p++ = 1; *p++ = Iac; *p++ = Se; return iwrite(Bfildes(bp), buf, p-buf);}inttermsub(Biobuf *bp, uchar *sub, int n){ char term[Maxvar]; USED(bp); if(n-- < 1 || sub[0] != 0) return 0; if(n >= sizeof term) n = sizeof term; strncpy(term, (char*)sub, n); putenv("TERM", term); return 0;}intxlocchange(Biobuf *bp, int cmd){ char buf[8]; char *p = buf; if(cmd != Will) return 0; /* ask other side to send x display info */ *p++ = Iac; *p++ = Sb; *p++ = opt[Xloc].code; *p++ = 1; *p++ = Iac; *p++ = Se; return iwrite(Bfildes(bp), buf, p-buf);}intxlocsub(Biobuf *bp, uchar *sub, int n){ char xloc[Maxvar]; USED(bp); if(n-- < 1 || sub[0] != 0) return 0; if(n >= sizeof xloc) n = sizeof xloc; strncpy(xloc, (char*)sub, n); putenv("DISPLAY", xloc); return 0;}/* * create a shared segment. Make is start 2 meg higher than the current * end of process memory. */void*share(ulong len){ uchar *vastart; vastart = sbrk(0); if(vastart == (void*)-1) return 0; vastart += 2*1024*1024; if(segattach(0, "shared", vastart, len) == (void*)-1) return 0; return vastart;}/* * bind a pipe onto consctl and keep reading it to * get changes to console state. */intconssim(void){ int i, n; int fd; int tries; char buf[128]; char *field[10]; /* a pipe to simulate the /dev/cons */ if(bind("#|", "/mnt/cons/cons", MREPL) < 0) fatal("/dev/cons1", 0, 0); if(bind("/mnt/cons/cons/data1", "/dev/cons", MREPL) < 0) fatal("/dev/cons2", 0, 0); /* a pipe to simulate consctl */ if(bind("#|", "/mnt/cons/consctl", MBEFORE) < 0 || bind("/mnt/cons/consctl/data1", "/dev/consctl", MREPL) < 0) fatal("/dev/consctl", 0, 0); /* a process to read /dev/consctl and set the state in cons */ switch(fork()){ case -1: fatal("forking", 0, 0); case 0: break; default: return open("/mnt/cons/cons/data", ORDWR); } for(tries = 0; tries < 100; tries++){ cons->raw = 0; cons->hold = 0; fd = open("/mnt/cons/consctl/data", OREAD); if(fd < 0) continue; tries = 0; for(;;){ n = read(fd, buf, sizeof(buf)-1); if(n <= 0) break; buf[n] = 0; n = getfields(buf, field, 10, 1, " "); for(i = 0; i < n; i++){ if(strcmp(field[i], "rawon") == 0) { if(debug) fprint(2, "raw = 1\n"); cons->raw = 1; } else if(strcmp(field[i], "rawoff") == 0) { if(debug) fprint(2, "raw = 0\n"); cons->raw = 0; } else if(strcmp(field[i], "holdon") == 0) { cons->hold = 1; if(debug) fprint(2, "raw = 1\n"); } else if(strcmp(field[i], "holdoff") == 0) { cons->hold = 0; if(debug) fprint(2, "raw = 0\n"); } } } close(fd); } exits(0); return -1;}intalnum(int c){ /* * Hard to get absolutely right. Use what we know about ASCII * and assume anything above the Latin control characters is * potentially an alphanumeric. */ if(c <= ' ') return 0; if(0x7F<=c && c<=0xA0) return 0; if(strchr("!\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", c)) return 0; return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -