xref: /inferno-os/appl/cmd/telnet.b (revision 9274481003af38a88988b4e9a3a2c3e0df206bee)
1implement Telnet;
2
3include "sys.m";
4	sys: Sys;
5
6include "draw.m";
7
8include "dial.m";
9	dial: Dial;
10	Connection: import dial;
11
12Telnet: module
13{
14	init:	fn(ctxt: ref Draw->Context, args: list of string);
15};
16
17Debug: con 0;
18
19Inbuf: adt {
20	fd:	ref Sys->FD;
21	out:	ref Outbuf;
22	buf:	array of byte;
23	ptr:	int;
24	nbyte:	int;
25};
26
27Outbuf: adt {
28	buf:	array of byte;
29	ptr:	int;
30};
31
32BS:		con 8;		# ^h backspace character
33BSW:		con 23;		# ^w bacspace word
34BSL:		con 21;		# ^u backspace line
35EOT:		con 4;		# ^d end of file
36ESC:		con 27;		# hold mode
37
38net:	ref Connection;
39stdin, stdout, stderr: ref Sys->FD;
40
41# control characters
42Se:			con 240;	# end subnegotiation
43NOP:			con 241;
44Mark:		con 242;	# data mark
45Break:		con 243;
46Interrupt:		con 244;
47Abort:		con 245;	# TENEX ^O
48AreYouThere:	con 246;
49Erasechar:	con 247;	# erase last character
50Eraseline:		con 248;	# erase line
51GoAhead:		con 249;	# half duplex clear to send
52Sb:			con 250;	# start subnegotiation
53Will:			con 251;
54Wont:		con 252;
55Do:			con 253;
56Dont:		con 254;
57Iac:			con 255;
58
59# options
60Binary, Echo, SGA, Stat, Timing,
61Det, Term, EOR, Uid, Outmark,
62Ttyloc, M3270, Padx3, Window, Speed,
63Flow, Line, Xloc, Extend: con iota;
64
65Opt: adt
66{
67	name:	string;
68	code:	int;
69	noway:	int;
70	remote:	int;		# remote value
71	local:	int;		# local value
72};
73
74opt := array[] of
75{
76	Binary =>		Opt("binary",			0,	0,	0, 	0),
77	Echo	=>		Opt("echo",			1,  	0, 	0,	0),
78	SGA	=>		Opt("suppress go ahead",	3,  	0, 	0,	0),
79	Stat =>		Opt("status",			5,  	1, 	0,	0),
80	Timing =>		Opt("timing",			6,  	1, 	0,	0),
81	Det=>		Opt("det",				20, 	1, 	0,	0),
82	Term =>		Opt("terminal",			24, 	0, 	0,	0),
83	EOR =>		Opt("end of record",		25, 	1, 	0,	0),
84	Uid =>		Opt("uid",				26, 	1, 	0,	0),
85	Outmark => 	Opt("outmark",			27, 	1, 	0,	0),
86	Ttyloc =>		Opt("ttyloc",			28, 	1, 	0,	0),
87	M3270 =>		Opt("3270 mode",		29, 	1, 	0,	0),
88	Padx3 =>		Opt("pad x.3",			30, 	1, 	0,	0),
89	Window =>	Opt("window size",		31, 	1, 	0,	0),
90	Speed =>		Opt("speed",			32, 	1, 	0,	0),
91	Flow	=>		Opt("flow control",		33, 	1, 	0,	0),
92	Line	=>		Opt("line mode",		34, 	1, 	0,	0),
93	Xloc	=>		Opt("X display loc",		35, 	1, 	0,	0),
94	Extend =>		Opt("Extended",		255,	1, 	0,	0),
95};
96
97usage()
98{
99	sys->fprint(stderr, "usage: telnet host [port]\n");
100	raise "fail:usage";
101}
102
103init(nil: ref Draw->Context, argv: list of string)
104{
105	sys = load Sys Sys->PATH;
106	stderr = sys->fildes(2);
107	stdout = sys->fildes(1);
108	stdin = sys->fildes(0);
109	dial = load Dial Dial->PATH;
110
111	if (len argv < 2)
112		usage();
113	argv = tl argv;
114	host := hd argv;
115	argv = tl argv;
116	port := "23";
117	if(argv != nil)
118		port = hd argv;
119	connect(host, port);
120}
121
122ccfd: ref Sys->FD;
123connect(addr: string, port: string)
124{
125	dest := dial->netmkaddr(addr, "tcp", port);
126	net = dial->dial(dest, nil);
127	if(net == nil) {
128		sys->fprint(stderr, "telnet: can't dial %s: %r\n", dest);
129		raise "fail:dial";
130	}
131	sys->fprint(stderr, "telnet: connected to %s\n", addr);
132
133	raw(1);
134	pidch := chan of int;
135	finished := chan of int;
136	spawn fromnet(pidch, finished);
137	spawn fromuser(pidch, finished);
138	pids := array[2] of {* => <-pidch};
139	kill(pids[<-finished == pids[0]]);
140	raw(0);
141}
142
143
144fromuser(pidch, finished: chan of int)
145{
146	pidch <-= sys->pctl(0, nil);
147	b := array[1024] of byte;
148	while((n := sys->read(stdin, b, len b)) > 0) {
149		if (opt[Echo].remote == 0)
150			sys->write(stdout, b, n);
151		sys->write(net.dfd, b, n);
152	}
153	sys->fprint(stderr, "telnet: error reading stdin: %r\n");
154	finished <-= sys->pctl(0, nil);
155}
156
157getc(b: ref Inbuf): int
158{
159	if(b.nbyte == 0) {
160		if(b.out != nil)
161			flushout(b.out);
162		b.nbyte = sys->read(b.fd, b.buf, len b.buf);
163		if(b.nbyte <= 0)
164			return -1;
165		b.ptr = 0;
166	}
167	b.nbyte--;
168	return int b.buf[b.ptr++];
169}
170
171putc(b: ref Outbuf, c: int)
172{
173	b.buf[b.ptr++] = byte c;
174	if(b.ptr == len b.buf)
175		flushout(b);
176}
177
178flushout(b: ref Outbuf)
179{
180	sys->write(stdout, b.buf, b.ptr);
181	b.ptr = 0;
182}
183
184BUFSIZE: con 2048;
185fromnet(pidch, finished: chan of int)
186{
187	pidch <-= sys->pctl(0, nil);
188	conout := ref Outbuf(array[BUFSIZE] of byte, 0);
189	netinp := ref Inbuf(net.dfd, conout, array[BUFSIZE] of byte, 0, 0);
190
191loop:	for(;;) {
192		c := getc(netinp);
193		case c {
194		-1 =>
195			break loop;
196		Iac  =>
197			c = getc(netinp);
198			if(c != Iac) {
199				flushout(conout);
200				if(control(netinp, c) < 0)
201					break loop;
202			} else
203				putc(conout, c);
204		* =>
205			putc(conout, c);
206		}
207	}
208	sys->fprint(stderr, "telnet: remote host closed connection\n");
209	finished <-= sys->pctl(0, nil);
210}
211
212control(bp: ref Inbuf, c: int): int
213{
214	r := 0;
215	case c {
216	AreYouThere =>
217		sys->fprint(net.dfd, "Inferno telnet\r\n");
218	Sb =>
219		r = sub(bp);
220	Will =>
221		r = will(bp);
222	Wont =>
223		r = wont(bp);
224	Do =>
225		r = doit(bp);
226	Dont =>
227		r = dont(bp);
228	Se =>
229		sys->fprint(stderr, "telnet: SE without an SB\n");
230	-1 =>
231		r = -1;
232	}
233
234	return r;
235}
236
237sub(bp: ref Inbuf): int
238{
239	subneg: string;
240	i := 0;
241	for(;;){
242		c := getc(bp);
243		if(c == Iac) {
244			c = getc(bp);
245			if(c == Se)
246				break;
247			subneg[i++] = Iac;
248		}
249		if(c < 0)
250			return -1;
251		subneg[i++] = c;
252	}
253	if(i == 0)
254		return 0;
255
256	if (Debug)
257		sys->fprint(stderr, "telnet: sub(%s, %d, n = %d)\n", optname(subneg[0]), subneg[1], i);
258
259	for(i = 0; i < len opt; i++)
260		if(opt[i].code == subneg[0])
261			break;
262
263	if(i >= len opt)
264		return 0;
265
266	case i {
267	Term =>
268		sbsend(opt[Term].code, array of byte "network");
269	}
270
271	return 0;
272}
273
274sbsend(code: int, data: array of byte): int
275{
276	buf := array[4+len data+2] of byte;
277	o := 4+len data;
278
279	buf[0] = byte Iac;
280	buf[1] = byte Sb;
281	buf[2] = byte code;
282	buf[3] = byte 0;
283	buf[4:] = data;
284	buf[o] = byte Iac;
285	o++;
286	buf[o] = byte Se;
287
288	return sys->write(net.dfd, buf, len buf);
289}
290
291will(bp: ref Inbuf): int
292{
293	c := getc(bp);
294	if(c < 0)
295		return -1;
296
297	if (Debug)
298		sys->fprint(stderr, "telnet: will(%s)\n", optname(c));
299
300	for(i := 0; i < len opt; i++)
301		if(opt[i].code == c)
302			break;
303
304	if(i >= len opt) {
305		send3(bp, Iac, Dont, c);
306		return 0;
307	}
308
309	rv := 0;
310	if(opt[i].noway)
311		send3(bp, Iac, Dont, c);
312	else
313	if(opt[i].remote == 0)
314		rv |= send3(bp, Iac, Do, c);
315
316	if(opt[i].remote == 0)
317		rv |= change(bp, i, Will);
318	opt[i].remote = 1;
319	return rv;
320}
321
322wont(bp: ref Inbuf): int
323{
324	c := getc(bp);
325	if(c < 0)
326		return -1;
327
328	if (Debug)
329		sys->fprint(stderr, "telnet: wont(%s)\n", optname(c));
330
331	for(i := 0; i < len opt; i++)
332		if(opt[i].code == c)
333			break;
334
335	if(i >= len opt)
336		return 0;
337
338	rv := 0;
339	if(opt[i].remote) {
340		rv |= change(bp, i, Wont);
341		rv |= send3(bp, Iac, Dont, c);
342	}
343	opt[i].remote = 0;
344	return rv;
345}
346
347doit(bp: ref Inbuf): int
348{
349	c := getc(bp);
350	if(c < 0)
351		return -1;
352
353	if (Debug)
354		sys->fprint(stderr, "telnet: do(%s)\n", optname(c));
355
356	for(i := 0; i < len opt; i++)
357		if(opt[i].code == c)
358			break;
359
360	if(i >= len opt || opt[i].noway) {
361		send3(bp, Iac, Wont, c);
362		return 0;
363	}
364	rv := 0;
365	if(opt[i].local == 0) {
366		rv |= change(bp, i, Do);
367		rv |= send3(bp, Iac, Will, c);
368	}
369	opt[i].local = 1;
370	return rv;
371}
372
373dont(bp: ref Inbuf): int
374{
375	c := getc(bp);
376	if(c < 0)
377		return -1;
378
379	if (Debug)
380		sys->fprint(stderr, "telnet: dont(%s)\n", optname(c));
381
382	for(i := 0; i < len opt; i++)
383		if(opt[i].code == c)
384			break;
385
386	if(i >= len opt || opt[i].noway)
387		return 0;
388
389	rv := 0;
390	if(opt[i].local){
391		opt[i].local = 0;
392		rv |= change(bp, i, Dont);
393		rv |= send3(bp, Iac, Wont, c);
394	}
395	opt[i].local = 0;
396	return rv;
397}
398
399change(bp: ref Inbuf, o: int, what: int): int
400{
401	if(bp != nil)
402		{}
403	if(o != 0)
404		{}
405	if(what != 0)
406		{}
407	return 0;
408}
409
410send3(bp: ref Inbuf, c0: int, c1: int, c2: int): int
411{
412	if (Debug)
413		sys->fprint(stderr, "telnet: reply(%s(%s))\n", negname(c1), optname(c2));
414
415	buf := array[3] of byte;
416
417	buf[0] = byte c0;
418	buf[1] = byte c1;
419	buf[2] = byte c2;
420
421	if (sys->write(bp.fd, buf, 3) != 3)
422		return -1;
423	return 0;
424}
425
426kill(pid: int): int
427{
428	fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE);
429	if (fd == nil)
430		return -1;
431	if (sys->write(fd, array of byte "kill", 4) != 4)
432		return -1;
433	return 0;
434}
435
436negname(c: int): string
437{
438	t := "Unknown";
439	case c {
440	Will =>	t = "will";
441	Wont =>	t = "wont";
442	Do =>	t = "do";
443	Dont =>	t = "dont";
444	}
445	return t;
446}
447
448optname(c: int): string
449{
450	for (i := 0; i < len opt; i++)
451		if (opt[i].code == c)
452			return opt[i].name;
453	return "unknown";
454}
455
456raw(on: int)
457{
458	if(ccfd == nil) {
459		ccfd = sys->open("/dev/consctl", Sys->OWRITE);
460		if(ccfd == nil) {
461			sys->fprint(stderr, "telnet: cannot open /dev/consctl: %r\n");
462			return;
463		}
464	}
465	if(on)
466		sys->fprint(ccfd, "rawon");
467	else
468		sys->fprint(ccfd, "rawoff");
469}
470