576 lines · 8.9 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <bio.h>#include "telnet.h" 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 interrupted;int localecho;int notkbd; static char *srv; typedef struct Comm Comm;struct Comm {	int returns;	int stopped;};Comm *comm; int	dodial(char*);void	fromkbd(int);void	fromnet(int);int	menu(Biobuf*,  int);void	notifyf(void*, char*);void	rawoff(void);void	rawon(void);void	telnet(int);char*	system(int, char*);int	echochange(Biobuf*, int);int	termsub(Biobuf*, uchar*, int);int	xlocsub(Biobuf*, uchar*, int);void*	share(usize); static int islikeatty(int); voidusage(void){	fatal("usage: telnet [-Cdnr] [-s srv] net!host[!service]", 0, 0);} voidmain(int argc, char *argv[]){	int returns; 	returns = 1;	ARGBEGIN{	case 'C':		opt[Echo].noway = 1;		break;	case 'd':		debug = 1;		break;	case 'n':		notkbd = 1;		break; 	case 'r':		returns = 0;		break;	case 's':		srv = EARGF(usage());		break;	default:		usage();	}ARGEND 	if(argc != 1)		usage(); 	/* options we need routines for */	opt[Echo].change = echochange;	opt[Term].sub = termsub;	opt[Xloc].sub = xlocsub; 	comm = share(sizeof(comm));	comm->returns = returns; 	telnet(dodial(argv[0]));} /* *  dial and return a data connection */intdodial(char *dest){	char *name;	int data;	char devdir[NETPATHLEN]; 	name = netmkaddr(dest, "tcp", "telnet");	data = dial(name, 0, devdir, 0);	if(data < 0)		fatal("%s: %r", name, 0);	if(srv != nil){		if(rfork(RFPROC | RFNOWAIT | RFNOTEG) != 0)			exits("");	}	else		fprint(2, "connected to %s on %s\n", name, devdir);	return data;} voidpost(char *srv, int fd){	int f;	char buf[32]; 	f = create(srv, OWRITE, 0666);	if(f < 0)		sysfatal("create %s: %r", srv);	snprint(buf, sizeof buf, "%d", fd);	if(write(f, buf, strlen(buf)) != strlen(buf))		sysfatal("write %s: %r", srv);	close(f);} /* *  two processes pass bytes back and forth between the *  terminal and the network. */voidtelnet(int net){	int pid;	int p[2];	char *svc; 	rawoff();	svc = nil;	if (srv) {		if(pipe(p) < 0)			sysfatal("pipe: %r");		if (srv[0] != '/')			svc = smprint("/srv/%s", srv);		else			svc = srv;		post(svc, p[0]);		close(p[0]);		dup(p[1], 0);		dup(p[1], 1);		/* pipe is now std in & out */	}	ttypid = getpid();	switch(pid = rfork(RFPROC|RFFDG|RFMEM)){	case -1:		perror("con");		exits("fork");	case 0:		rawoff();		notify(notifyf);		fromnet(net);		if (svc)			remove(svc);		sendnote(ttypid, "die");		exits(0);	default:		netpid = pid;		notify(notifyf);		fromkbd(net);		if(notkbd)			for(;;)				sleep(0);		if (svc)			remove(svc);		sendnote(netpid, "die");		exits(0);	}} /* *  Read the keyboard and write it to the network.  '^\' gets us into *  the menu. */voidfromkbd(int net){	Biobuf ib, ob;	int c, likeatty;	int eofs; 	Binit(&ib, 0, OREAD);	Binit(&ob, net, OWRITE); 	likeatty = islikeatty(0);	eofs = 0;	for(;;){		c = Bgetc(&ib); 		/*		 *  with raw off, all ^D's get turned into Eof's.		 *  change them back.		 *  10 in a row implies that the terminal is really gone so		 *  just hang up.		 */		if(c < 0){			if(notkbd)				return;			if(eofs++ > 10)				return;			c = 004;		} else			eofs = 0; 		/*		 *  if not in binary mode, look for the ^\ escape to menu.		 *  also turn \n into \r\n		 */		if(likeatty || !opt[Binary].local){			if(c == 0034){ /* CTRL \ */				if(Bflush(&ob) < 0)					return;				if(menu(&ib, net) < 0)					return;				continue;			}		}		if(!opt[Binary].local){			if(c == '\n'){				/*				 *  This is a very strange use of the SGA option.				 *  I did this because some systems that don't				 *  announce a willingness to supress-go-ahead				 *  need the \r\n sequence to recognize input.				 *  If someone can explain this to me, please				 *  send me mail. - presotto				 */				if(opt[SGA].remote){					c = '\r';				} else {					if(Bputc(&ob, '\r') < 0)						return;				}			}		}		if(Bputc(&ob, c) < 0)			return;		if(Bbuffered(&ib) == 0)			if(Bflush(&ob) < 0)				return;	}} /* *  Read from the network and write to the screen.  If 'stopped' is set *  spin and don't read.  Filter out spurious carriage returns. */voidfromnet(int net){	int c;	int crnls = 0, freenl = 0, eofs;	Biobuf ib, ob; 	Binit(&ib, net, OREAD);	Binit(&ob, 1, OWRITE);	eofs = 0;	for(;;){		if(Bbuffered(&ib) == 0)			Bflush(&ob);		if(interrupted){			interrupted = 0;			send2(net, Iac, Interrupt);		}		c = Bgetc(&ib);		if(c < 0){			if(eofs++ >= 2)				return;			continue;		}		eofs = 0;		switch(c){		case '\n':	/* skip nl after string of cr's */			if(!opt[Binary].local && !comm->returns){				++crnls;				if(freenl == 0)					break;				freenl = 0;				continue;			}			break;		case '\r':	/* first cr becomes nl, remainder dropped */			if(!opt[Binary].local && !comm->returns){				if(crnls++ == 0){					freenl = 1;					c = '\n';					break;				}				continue;			}			break;		case 0:		/* remove nulls from crnl string */			if(crnls)				continue;			break; 		case Iac:			crnls = 0;			freenl = 0;			c = Bgetc(&ib);			if(c == Iac)				break;			if(Bflush(&ob) < 0)				return;			if(control(&ib, c) < 0)				return;			continue; 		default:			crnls = 0;			freenl = 0;			break;		}		if(Bputc(&ob, c) < 0)			return;	}} voidconsctlcmd(char *s){	if(srv != nil)		return;	if(debug)		fprint(2, "consctl: %s\n", s);	if(consctl < 0)		consctl = open("/dev/consctl", OWRITE);	if(consctl < 0){		fprint(2, "can't open consctl: %r\n");		return;	}	write(consctl, s, strlen(s));} /* *  turn keyboard raw mode on */voidrawon(void){	consctlcmd("rawon");} /* *  turn keyboard raw mode off */voidrawoff(void){	consctlcmd("rawoff");} /* *  control menu */#define STDHELP	"\t(b)reak, (i)nterrupt, (q)uit, (r)eturns, (!cmd), (.)continue\n" intmenu(Biobuf *bp, int net){	char *cp;	int done; 	comm->stopped = 1; 	rawoff();	fprint(2, ">>> ");	for(done = 0; !done; ){		cp = Brdline(bp, '\n');		if(cp == 0){			comm->stopped = 0;			return -1;		}		cp[Blinelen(bp)-1] = 0;		switch(*cp){		case '!':			system(Bfildes(bp), cp+1);			done = 1;			break;		case '.':			done = 1;			break;		case 'q':			comm->stopped = 0;			return -1;		case 'o':			switch(*(cp+1)){			case 'd':				send3(net, Iac, Do, atoi(cp+2));				break;			case 'w':				send3(net, Iac, Will, atoi(cp+2));				break;			}			break;		case 'r':			comm->returns = !comm->returns;			done = 1;			break;		case 'i':			send2(net, Iac, Interrupt);			break;		case 'b':			send2(net, Iac, Break);			break;		default:			fprint(2, STDHELP);			break;		}		if(!done)			fprint(2, ">>> ");	} 	rawon();	comm->stopped = 0;	return 0;} /* *  ignore interrupts */voidnotifyf(void *a, char *msg){	USED(a);	if(strcmp(msg, "interrupt") == 0){		interrupted = 1;		noted(NCONT);	}	if(strcmp(msg, "hangup") == 0)		noted(NCONT);	noted(NDFLT);} /* *  run a command with the network connection as standard IO */char *system(int fd, char *cmd){	int pid;	int p;	static Waitmsg msg; 	if((pid = fork()) == -1){		perror("con");		return "fork failed";	}	else if(pid == 0){		dup(fd, 0);		close(ctl);		close(fd);		if(*cmd)			execl("/bin/rc", "rc", "-c", cmd, nil);		else			execl("/bin/rc", "rc", nil);		perror("con");		exits("exec");	}	for(p = waitpid(); p >= 0; p = waitpid()){		if(p == pid)			return msg.msg;		}	return "lost child";} /* *  suppress local echo if the remote side is doing it */intechochange(Biobuf *bp, int cmd){	USED(bp); 	switch(cmd){	case Will:		rawon();		break;	case Wont:		rawoff();		break;	}	return 0;} /* *  send terminal type to the other side */inttermsub(Biobuf *bp, uchar *sub, int n){	char buf[64];	char *term;	char *p = buf;

Lines 1–500 of 576.