forsyth20121205-174063a80cc13 years agoHistory
469 lines · 8.1 KB · Brainfuck
RawDownload
implement Telnet; include "sys.m";	sys: Sys; include "draw.m"; include "dial.m";	dial: Dial;	Connection: import dial; Telnet: module{	init:	fn(ctxt: ref Draw->Context, args: list of string);}; Debug: con 0; Inbuf: adt {	fd:	ref Sys->FD;	out:	ref Outbuf;	buf:	array of byte;	ptr:	int;	nbyte:	int;}; Outbuf: adt {	buf:	array of byte;	ptr:	int;}; BS:		con 8;		# ^h backspace characterBSW:		con 23;		# ^w bacspace wordBSL:		con 21;		# ^u backspace lineEOT:		con 4;		# ^d end of fileESC:		con 27;		# hold mode net:	ref Connection;stdin, stdout, stderr: ref Sys->FD; # control charactersSe:			con 240;	# end subnegotiationNOP:			con 241;Mark:		con 242;	# data markBreak:		con 243;Interrupt:		con 244;Abort:		con 245;	# TENEX ^OAreYouThere:	con 246;Erasechar:	con 247;	# erase last characterEraseline:		con 248;	# erase lineGoAhead:		con 249;	# half duplex clear to sendSb:			con 250;	# start subnegotiationWill:			con 251;Wont:		con 252;Do:			con 253;Dont:		con 254;Iac:			con 255; # optionsBinary, Echo, SGA, Stat, Timing,Det, Term, EOR, Uid, Outmark,Ttyloc, M3270, Padx3, Window, Speed,Flow, Line, Xloc, Extend: con iota; Opt: adt{	name:	string;	code:	int;	noway:	int;		remote:	int;		# remote value	local:	int;		# local value}; opt := array[] of{	Binary =>		Opt("binary",			0,	0,	0, 	0),	Echo	=>		Opt("echo",			1,  	0, 	0,	0),	SGA	=>		Opt("suppress go ahead",	3,  	0, 	0,	0),	Stat =>		Opt("status",			5,  	1, 	0,	0),	Timing =>		Opt("timing",			6,  	1, 	0,	0),	Det=>		Opt("det",				20, 	1, 	0,	0),	Term =>		Opt("terminal",			24, 	0, 	0,	0),	EOR =>		Opt("end of record",		25, 	1, 	0,	0),	Uid =>		Opt("uid",				26, 	1, 	0,	0),	Outmark => 	Opt("outmark",			27, 	1, 	0,	0),	Ttyloc =>		Opt("ttyloc",			28, 	1, 	0,	0),	M3270 =>		Opt("3270 mode",		29, 	1, 	0,	0),	Padx3 =>		Opt("pad x.3",			30, 	1, 	0,	0),	Window =>	Opt("window size",		31, 	1, 	0,	0),	Speed =>		Opt("speed",			32, 	1, 	0,	0),	Flow	=>		Opt("flow control",		33, 	1, 	0,	0),	Line	=>		Opt("line mode",		34, 	1, 	0,	0),	Xloc	=>		Opt("X display loc",		35, 	1, 	0,	0),	Extend =>		Opt("Extended",		255,	1, 	0,	0),}; usage(){	sys->fprint(stderr, "usage: telnet host [port]\n");	raise "fail:usage";} init(nil: ref Draw->Context, argv: list of string){	sys = load Sys Sys->PATH;	stderr = sys->fildes(2);	stdout = sys->fildes(1);	stdin = sys->fildes(0);	dial = load Dial Dial->PATH; 	if (len argv < 2)		usage();	argv = tl argv;	host := hd argv;	argv = tl argv;	port := "23";	if(argv != nil)		port = hd argv;	connect(host, port);} ccfd: ref Sys->FD;connect(addr: string, port: string){	dest := dial->netmkaddr(addr, "tcp", port);	net = dial->dial(dest, nil);	if(net == nil) {		sys->fprint(stderr, "telnet: can't dial %s: %r\n", dest);		raise "fail:dial";	}	sys->fprint(stderr, "telnet: connected to %s\n", addr); 	raw(1);	pidch := chan of int;	finished := chan of int;	spawn fromnet(pidch, finished);	spawn fromuser(pidch, finished);	pids := array[2] of {* => <-pidch};	kill(pids[<-finished == pids[0]]);	raw(0);}  fromuser(pidch, finished: chan of int){	pidch <-= sys->pctl(0, nil);	b := array[1024] of byte;	while((n := sys->read(stdin, b, len b)) > 0) {		if (opt[Echo].remote == 0)			sys->write(stdout, b, n);		sys->write(net.dfd, b, n);	}	sys->fprint(stderr, "telnet: error reading stdin: %r\n");	finished <-= sys->pctl(0, nil);} getc(b: ref Inbuf): int{	if(b.nbyte == 0) {		if(b.out != nil)			flushout(b.out);		b.nbyte = sys->read(b.fd, b.buf, len b.buf);		if(b.nbyte <= 0)			return -1;		b.ptr = 0;	}	b.nbyte--;	return int b.buf[b.ptr++];} putc(b: ref Outbuf, c: int){	b.buf[b.ptr++] = byte c;	if(b.ptr == len b.buf)		flushout(b);} flushout(b: ref Outbuf){	sys->write(stdout, b.buf, b.ptr);	b.ptr = 0;} BUFSIZE: con 2048;fromnet(pidch, finished: chan of int){	pidch <-= sys->pctl(0, nil);	conout := ref Outbuf(array[BUFSIZE] of byte, 0);	netinp := ref Inbuf(net.dfd, conout, array[BUFSIZE] of byte, 0, 0); loop:	for(;;) {		c := getc(netinp);			case c {		-1 =>			break loop;		Iac  =>			c = getc(netinp);			if(c != Iac) {				flushout(conout);				if(control(netinp, c) < 0)					break loop;			} else				putc(conout, c);		* =>			putc(conout, c);		}	}	sys->fprint(stderr, "telnet: remote host closed connection\n");	finished <-= sys->pctl(0, nil);} control(bp: ref Inbuf, c: int): int{	r := 0;	case c {	AreYouThere =>		sys->fprint(net.dfd, "Inferno telnet\r\n");	Sb =>		r = sub(bp);	Will =>		r = will(bp);	Wont =>		r = wont(bp);	Do =>		r = doit(bp);	Dont =>		r = dont(bp);	Se =>		sys->fprint(stderr, "telnet: SE without an SB\n");	-1 =>		r = -1;	} 	return r;} sub(bp: ref Inbuf): int{	subneg: string;	i := 0;	for(;;){		c := getc(bp);		if(c == Iac) {			c = getc(bp);			if(c == Se)				break;			subneg[i++] = Iac;		}		if(c < 0)			return -1;		subneg[i++] = c;	}	if(i == 0)		return 0; 	if (Debug)		sys->fprint(stderr, "telnet: sub(%s, %d, n = %d)\n", optname(subneg[0]), subneg[1], i); 	for(i = 0; i < len opt; i++)		if(opt[i].code == subneg[0])			break; 	if(i >= len opt)		return 0; 	case i {	Term =>		sbsend(opt[Term].code, array of byte "network");		} 	return 0;} sbsend(code: int, data: array of byte): int{	buf := array[4+len data+2] of byte;	o := 4+len data; 	buf[0] = byte Iac;	buf[1] = byte Sb;	buf[2] = byte code;	buf[3] = byte 0;	buf[4:] = data;	buf[o] = byte Iac;	o++;	buf[o] = byte Se; 	return sys->write(net.dfd, buf, len buf);} will(bp: ref Inbuf): int{	c := getc(bp);	if(c < 0)		return -1; 	if (Debug)		sys->fprint(stderr, "telnet: will(%s)\n", optname(c)); 	for(i := 0; i < len opt; i++)		if(opt[i].code == c)			break; 	if(i >= len opt) {		send3(bp, Iac, Dont, c);		return 0;	} 	rv := 0;	if(opt[i].noway)		send3(bp, Iac, Dont, c);	else	if(opt[i].remote == 0)		rv |= send3(bp, Iac, Do, c); 	if(opt[i].remote == 0)		rv |= change(bp, i, Will);	opt[i].remote = 1;	return rv;} wont(bp: ref Inbuf): int{	c := getc(bp);	if(c < 0)		return -1; 	if (Debug)		sys->fprint(stderr, "telnet: wont(%s)\n", optname(c)); 	for(i := 0; i < len opt; i++)		if(opt[i].code == c)			break; 	if(i >= len opt)		return 0; 	rv := 0;	if(opt[i].remote) {		rv |= change(bp, i, Wont);		rv |= send3(bp, Iac, Dont, c);	}	opt[i].remote = 0;	return rv;} doit(bp: ref Inbuf): int{	c := getc(bp);	if(c < 0)		return -1; 	if (Debug)		sys->fprint(stderr, "telnet: do(%s)\n", optname(c)); 	for(i := 0; i < len opt; i++)		if(opt[i].code == c)			break; 	if(i >= len opt || opt[i].noway) {		send3(bp, Iac, Wont, c);		return 0;	}	rv := 0;	if(opt[i].local == 0) {		rv |= change(bp, i, Do);		rv |= send3(bp, Iac, Will, c);	}	opt[i].local = 1;	return rv;} dont(bp: ref Inbuf): int{	c := getc(bp);	if(c < 0)		return -1; 	if (Debug)		sys->fprint(stderr, "telnet: dont(%s)\n", optname(c)); 	for(i := 0; i < len opt; i++)		if(opt[i].code == c)			break; 	if(i >= len opt || opt[i].noway)		return 0; 	rv := 0;	if(opt[i].local){		opt[i].local = 0;		rv |= change(bp, i, Dont);		rv |= send3(bp, Iac, Wont, c);	}	opt[i].local = 0;	return rv;} change(bp: ref Inbuf, o: int, what: int): int{	if(bp != nil)		{}	if(o != 0)		{}	if(what != 0)		{}	return 0;} send3(bp: ref Inbuf, c0: int, c1: int, c2: int): int{	if (Debug)		sys->fprint(stderr, "telnet: reply(%s(%s))\n", negname(c1), optname(c2));			buf := array[3] of byte; 	buf[0] = byte c0;	buf[1] = byte c1;	buf[2] = byte c2; 	if (sys->write(bp.fd, buf, 3) != 3)		return -1;	return 0;} kill(pid: int): int{	fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE);	if (fd == nil)		return -1;	if (sys->write(fd, array of byte "kill", 4) != 4)		return -1;	return 0;} negname(c: int): string{	t := "Unknown";	case c {	Will =>	t = "will";	Wont =>	t = "wont";	Do =>	t = "do";	Dont =>	t = "dont";	}	return t;} optname(c: int): string{	for (i := 0; i < len opt; i++)		if (opt[i].code == c)			return opt[i].name;	return "unknown";} raw(on: int){	if(ccfd == nil) {		ccfd = sys->open("/dev/consctl", Sys->OWRITE);		if(ccfd == nil) {			sys->fprint(stderr, "telnet: cannot open /dev/consctl: %r\n");			return;		}	}	if(on)		sys->fprint(ccfd, "rawon");	else		sys->fprint(ccfd, "rawoff");}