1 /*
2  * This file is part of DGD, https://github.com/dworkin/dgd
3  * Copyright (C) 1993-2010 Dworkin B.V.
4  * Copyright (C) 2010-2013 DGD Authors (see the commit log for details)
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 # define INCLUDE_FILE_IO
21 # define INCLUDE_TELNET
22 # include "dgd.h"
23 # include "str.h"
24 # include "array.h"
25 # include "object.h"
26 # include "interpret.h"
27 # include "data.h"
28 # include "comm.h"
29 # include "version.h"
30 # include <errno.h>
31 
32 # ifndef TELOPT_LINEMODE
33 # define TELOPT_LINEMODE	34	/* linemode option */
34 # define LM_MODE		1
35 # define MODE_EDIT		0x01
36 # endif
37 
38 # define MAXIACSEQLEN		7	/* longest IAC sequence sent */
39 
40 typedef struct _user_ {
41     uindex oindex;		/* associated object index */
42     struct _user_ *prev;	/* preceding user */
43     struct _user_ *next;	/* next user */
44     struct _user_ *flush;	/* next in flush list */
45     short flags;		/* connection flags */
46     char state;			/* telnet state */
47     short newlines;		/* # of newlines in input buffer */
48     connection *conn;		/* connection */
49     char *inbuf;		/* input buffer */
50     array *extra;		/* object's extra value */
51     string *outbuf;		/* output buffer string */
52     ssizet inbufsz;		/* bytes in input buffer */
53     ssizet osdone;		/* bytes of output string done */
54 } user;
55 
56 /* flags */
57 # define CF_BINARY	0x0000	/* binary connection */
58 # define  CF_UDP	0x0002	/* receive UDP datagrams */
59 # define  CF_UDPDATA	0x0004	/* UDP data received */
60 # define CF_TELNET	0x0001	/* telnet connection */
61 # define  CF_ECHO	0x0002	/* client echoes input */
62 # define  CF_GA		0x0004	/* send GA after prompt */
63 # define  CF_PROMPT	0x0008	/* prompt in telnet output */
64 # define CF_BLOCKED	0x0010	/* input blocked */
65 # define CF_FLUSH	0x0020	/* in flush list */
66 # define CF_OUTPUT	0x0040	/* pending output */
67 # define CF_ODONE	0x0080	/* output done */
68 # define CF_OPENDING	0x0100	/* waiting for connect() to complete */
69 
70 #ifdef NETWORK_EXTENSIONS
71 # define CF_PORT	0x0200	/* port (listening) connection */
72 # define CF_DATAGRAM	0x0400	/* independent UDP socket */
73 #endif
74 
75 /* state */
76 # define TS_DATA	0
77 # define TS_CRDATA	1
78 # define TS_IAC		2
79 # define TS_DO		3
80 # define TS_DONT	4
81 # define TS_WILL	5
82 # define TS_WONT	6
83 # define TS_SB		7
84 # define TS_SE		8
85 
86 static user *users;		/* array of users */
87 static user *lastuser;		/* last user checked */
88 static user *freeuser;		/* linked list of free users */
89 static user *flush;		/* flush list */
90 static user *outbound;		/* pending outbound list */
91 static int maxusers;		/* max # of users */
92 #ifdef NETWORK_EXTENSIONS
93 static int maxports;		/* max # of ports */
94 static int nports;		/* # of ports */
95 #endif
96 static int nusers;		/* # of users */
97 static int odone;		/* # of users with output done */
98 static long newlines;		/* # of newlines in all input buffers */
99 static uindex this_user;	/* current user */
100 static int ntport, nbport;	/* # telnet/binary ports */
101 static int nexttport;		/* next telnet port to check */
102 static int nextbport;		/* next binary port to check */
103 static char ayt[22];		/* are you there? */
104 
105 /*
106  * NAME:	comm->init()
107  * DESCRIPTION:	initialize communications
108  */
109 #ifdef NETWORK_EXTENSIONS
comm_init(int n,int p,char ** thosts,char ** bhosts,unsigned short * tports,unsigned short * bports,int ntelnet,int nbinary)110 bool comm_init(int n, int p, char **thosts, char **bhosts,
111 	unsigned short *tports, unsigned short *bports,
112 	int ntelnet, int nbinary)
113 #else
114 bool comm_init(int n, char **thosts, char **bhosts,
115 	unsigned short *tports, unsigned short *bports,
116 	int ntelnet, int nbinary)
117 #endif
118 {
119     int i;
120     user *usr;
121 
122 #ifdef NETWORK_EXTENSIONS
123     maxusers = n;
124     maxports = p;
125     n += p;
126     users = ALLOC(user, n);
127 #else
128     users = ALLOC(user, maxusers = n);
129 #endif
130     for (i = n, usr = users + i; i > 0; --i) {
131 	--usr;
132 	usr->oindex = OBJ_NONE;
133 	usr->next = usr + 1;
134     }
135     users[n - 1].next = (user *) NULL;
136 
137     freeuser = usr;
138     lastuser = (user *) NULL;
139     flush = outbound = (user *) NULL;
140     nusers = odone = newlines = 0;
141 #ifdef NETWORK_EXTENSIONS
142     nports = 0;
143 #endif
144     this_user = OBJ_NONE;
145 
146     sprintf(ayt, "\15\12[%s]\15\12", VERSION);
147 
148     nexttport = nextbport = 0;
149 
150     return conn_init(n, thosts, bhosts, tports, bports, ntport = ntelnet,
151 		     nbport = nbinary);
152 }
153 
154 #ifdef NETWORK_EXTENSIONS
comm_openport(frame * f,object * obj,unsigned char protocol,unsigned short portnr)155 void comm_openport(frame *f, object *obj, unsigned char protocol,
156 	unsigned short portnr)
157 {
158     connection *conn;
159     uindex olduser;
160     short flags;
161     user *usr;
162     dataspace *data;
163     array *arr;
164     value val;
165 
166     flags = 0;
167 
168     if (nports >= maxports)
169 	error("Max number of port objects exceeded");
170 
171     switch (protocol)
172     {
173     case P_TELNET:
174 	flags = CF_TELNET | CF_PORT;
175 	protocol = P_TCP;
176 	break;
177     case P_UDP:
178 	flags = CF_DATAGRAM | CF_PORT;
179 	break;
180     case P_TCP:
181 	flags = CF_PORT;
182 	break;
183     default:
184 	error("Unknown protocol");
185     }
186 
187     conn = (connection *) conn_openlisten(protocol, portnr);
188     if (conn == (connection *) NULL)
189 	error("Can't open port");
190 
191     usr = freeuser;
192     freeuser = usr->next;
193     if (lastuser != (user *) NULL) {
194 	usr->prev = lastuser->prev;
195 	usr->prev->next = usr;
196 	usr->next = lastuser;
197 	lastuser->prev = usr;
198     } else {
199 	usr->prev = usr;
200 	usr->next = usr;
201 	lastuser = usr;
202     }
203     d_wipe_extravar(data = o_dataspace(obj));
204     switch (protocol)
205     {
206     case P_TCP:
207 	arr = arr_new(data, 3L);
208 	arr->elts[0] = zero_int;
209 	arr->elts[1] = arr->elts[2] = nil_value;
210 	PUT_ARRVAL_NOREF(&val, arr);
211 	d_set_extravar(data, &val);
212 	break;
213     case P_UDP:
214 	arr = arr_new(data, 4L);
215 	arr->elts[0] = zero_int;
216 	arr->elts[1] = nil_value;
217 	arr->elts[2] = nil_value;
218 	arr->elts[3] = nil_value;
219 	PUT_ARRVAL_NOREF(&val, arr);
220 	d_set_extravar(data, &val);
221 	break;
222     }
223     usr->oindex = obj->index;
224     obj->flags |= O_USER;
225     obj->etabi = usr-users;
226     usr->conn = conn;
227     usr->outbuf = (string *) NULL;
228     usr->osdone = 0;
229     usr->flags = flags;
230     olduser = this_user;
231     this_user = obj->index;
232     (--f->sp)->type = T_INT;
233     f->sp->u.number = conn_at(conn);
234     if (i_call(f, obj, (array *) NULL, "open", 4, TRUE, 1)) {
235 	i_del_value(f->sp++);
236     }
237     nports++;
238     this_user = olduser;
239 }
240 #endif
241 
242 /*
243  * NAME:	comm->clear()
244  * DESCRIPTION:	clean up connections
245  */
comm_clear()246 void comm_clear()
247 {
248     conn_clear();
249 }
250 
251 /*
252  * NAME:	comm->finish()
253  * DESCRIPTION:	terminate connections
254  */
comm_finish()255 void comm_finish()
256 {
257     conn_finish();
258 }
259 
260 #ifndef NETWORK_EXTENSIONS
261 /*
262  * NAME:	comm->listen()
263  * DESCRIPTION:	start listening on telnet port and binary port
264  */
comm_listen()265 void comm_listen()
266 {
267     conn_listen();
268 }
269 #endif
270 
271 /*
272  * NAME:	addtoflush()
273  * DESCRIPTION:	add a user to the flush list
274  */
addtoflush(user * usr,array * arr)275 static void addtoflush(user *usr, array *arr)
276 {
277     usr->flags |= CF_FLUSH;
278     usr->flush = flush;
279     flush = usr;
280     arr_ref(usr->extra = arr);
281 
282     /* remember initial buffer */
283     if (d_get_elts(arr)[1].type == T_STRING) {
284 	str_ref(usr->outbuf = arr->elts[1].u.string);
285     }
286 }
287 
288 /*
289  * NAME:	comm->setup()
290  * DESCRIPTION:	setup a user
291  */
comm_setup(user * usr,frame * f,object * obj)292 static array *comm_setup(user *usr, frame *f, object *obj)
293 {
294     dataspace *data;
295     array *arr;
296     value val;
297 
298     if (obj->flags & O_DRIVER) {
299 	error("Cannot use driver object as user object");
300     }
301 
302     /* initialize dataspace before the object receives the user role */
303     if (!O_HASDATA(obj) &&
304 	i_call(f, obj, (array *) NULL, (char *) NULL, 0, TRUE, 0)) {
305 	i_del_value(f->sp++);
306     }
307 
308     d_wipe_extravar(data = o_dataspace(obj));
309     arr = arr_new(data, 3L);
310     arr->elts[0] = zero_int;
311     arr->elts[1] = arr->elts[2] = nil_value;
312     PUT_ARRVAL_NOREF(&val, arr);
313     d_set_extravar(data, &val);
314 
315     usr->oindex = obj->index;
316     obj->flags |= O_USER;
317     obj->etabi = usr - users;
318     usr->conn = NULL;
319     usr->outbuf = (string *) NULL;
320     usr->osdone = 0;
321     usr->flags = 0;
322 
323     return arr;
324 }
325 
326 /*
327  * NAME:	comm->new()
328  * DESCRIPTION:	accept a new connection
329  */
comm_new(frame * f,object * obj,connection * conn,bool telnet)330 static user *comm_new(frame *f, object *obj, connection *conn, bool telnet)
331 {
332     static char init[] = { (char) IAC, (char) WONT, (char) TELOPT_ECHO,
333 			   (char) IAC, (char) DO,   (char) TELOPT_LINEMODE };
334     user *usr;
335     array *arr;
336     value val;
337 
338     if (obj->flags & O_SPECIAL) {
339 	error("User object is already special purpose");
340     }
341 
342     if (obj->flags & O_DRIVER) {
343 	error("Cannot use driver object as user object");
344     }
345 
346     usr = freeuser;
347     freeuser = usr->next;
348     if (lastuser != (user *) NULL) {
349 	usr->prev = lastuser->prev;
350 	usr->prev->next = usr;
351 	usr->next = lastuser;
352 	lastuser->prev = usr;
353     } else {
354 	usr->prev = usr;
355 	usr->next = usr;
356 	lastuser = usr;
357     }
358 
359     arr = comm_setup(usr, f, obj);
360     usr->conn = conn;
361     if (telnet) {
362 	/* initialize connection */
363 	usr->flags = CF_TELNET | CF_ECHO | CF_OUTPUT;
364 	usr->state = TS_DATA;
365 	usr->newlines = 0;
366 	usr->inbufsz = 0;
367 	m_static();
368 	usr->inbuf = ALLOC(char, INBUF_SIZE + 1);
369 	*usr->inbuf++ = LF;	/* sentinel */
370 	m_dynamic();
371 
372 	arr->elts[0].u.number = CF_ECHO;
373 	PUT_STRVAL_NOREF(&val, str_new(init, (long) sizeof(init)));
374 	d_assign_elt(obj->data, arr, &arr->elts[1], &val);
375     }
376     nusers++;
377 
378     return usr;
379 }
380 
381 /*
382  * NAME:	comm->connect()
383  * DESCRIPTION:	attempt to establish an outbound connection
384  */
comm_connect(frame * f,object * obj,char * addr,unsigned char protocol,unsigned short port)385 void comm_connect(frame *f, object *obj, char *addr, unsigned char protocol,
386 	unsigned short port)
387 {
388     void *host;
389     int len;
390     user *usr;
391     array *arr;
392     value val;
393 
394     if (nusers >= maxusers)
395 	error("Max number of connection objects exceeded");
396 
397     host = conn_host(addr, port, &len);
398     if (host == (void *) NULL) {
399 	error("Unknown address");
400     }
401 
402     for (usr = outbound; ; usr = usr->flush) {
403 	if (usr == (user *) NULL) {
404 	    usr = comm_new(f, obj, (connection *) NULL, (protocol == P_TELNET));
405 	    arr = d_get_extravar(obj->data)->u.array;
406 	    usr->flush = outbound;
407 	    outbound = usr;
408 	    break;
409 	}
410 	if ((OBJR(usr->oindex)->flags & O_SPECIAL) != O_USER) {
411 	    /*
412 	     * a previous outbound connection was undone, reuse it
413 	     */
414 	    arr_del(usr->extra);
415 	    arr = comm_setup(usr, f, obj);
416 	    break;
417 	}
418     }
419 
420     PUT_STRVAL_NOREF(&val, str_new(host, len));
421     d_assign_elt(obj->data, arr, &arr->elts[1], &val);
422     usr->flags |= CF_FLUSH;
423     arr_ref(usr->extra = arr);
424     usr->flags |= CF_OPENDING;
425 }
426 
427 #ifdef NETWORK_EXTENSIONS
comm_senddatagram(object * obj,string * str,string * ip,int port)428 int comm_senddatagram(object *obj, string *str, string *ip, int port)
429 {
430     user *usr;
431     dataspace *data;
432     array *arr;
433     value *v1, *v2, *v3;
434     value val;
435 
436     usr = &users[EINDEX(obj->etabi)];
437 
438     if ((usr->flags & (CF_PORT | CF_DATAGRAM)) != (CF_PORT | CF_DATAGRAM)) {
439 	error("Object is not a udp port object");
440     }
441     if (!conn_checkaddr(ip->text)) {
442 	error("Not a IP address");
443     }
444     arr = d_get_extravar(data = obj->data)->u.array;
445     if (!(usr->flags & CF_FLUSH)) {
446 	addtoflush(usr, arr);
447     }
448 
449     v1 = arr->elts + 1;/* used for string */
450     v2 = arr->elts + 2;/* used for ip */
451     v3 = arr->elts + 3;/* used for port */
452     usr->flags |= CF_OUTPUT;
453     PUT_STRVAL_NOREF(&val, str);
454     d_assign_elt(data, arr, v1, &val);
455     PUT_STRVAL_NOREF(&val, ip);
456     d_assign_elt(data, arr, v2, &val);
457     PUT_INTVAL(&val, port);
458     d_assign_elt(data, arr, v3, &val);
459     return str->len;
460 }
461 #endif
462 
463 /*
464  * NAME:	comm->del()
465  * DESCRIPTION:	delete a connection
466  */
comm_del(frame * f,user * usr,object * obj,bool destruct)467 static void comm_del(frame *f, user *usr, object *obj, bool destruct)
468 {
469     dataspace *data;
470     uindex olduser;
471 
472     data = o_dataspace(obj);
473     if (!destruct) {
474 	/* if not destructing, make sure the connection terminates */
475 	if (!(usr->flags & CF_FLUSH)) {
476 	    addtoflush(usr, d_get_extravar(data)->u.array);
477 	}
478 	obj->flags &= ~O_USER;
479     }
480     olduser = this_user;
481     if (ec_push((ec_ftn) NULL)) {
482 	this_user = olduser;
483 	error((char *) NULL);
484     } else {
485 	this_user = obj->index;
486 	PUSH_INTVAL(f, destruct);
487 	if (i_call(f, obj, (array *) NULL, "close", 5, TRUE, 1)) {
488 	    i_del_value(f->sp++);
489 	}
490 	this_user = olduser;
491 	ec_pop();
492     }
493     if (destruct) {
494 	/* if destructing, don't disconnect if there's an error in close() */
495 	if (!(usr->flags & CF_FLUSH)) {
496 	    addtoflush(usr, d_get_extravar(data)->u.array);
497 	}
498 	obj->flags &= ~O_USER;
499     }
500 }
501 
502 /*
503  * NAME:	comm->challenge()
504  * DESCRIPTION:	set the UDP challenge for a binary connection
505  */
comm_challenge(object * obj,string * str)506 void comm_challenge(object *obj, string *str)
507 {
508     user *usr;
509     dataspace *data;
510     array *arr;
511     value *v;
512     value val;
513 
514     usr = &users[obj->etabi];
515     if (usr->flags & CF_TELNET) {
516 	error("Datagram channel cannot be attached to telnet connection");
517     }
518     arr = d_get_extravar(data = obj->data)->u.array;
519     if (!(usr->flags & CF_FLUSH)) {
520 	addtoflush(usr, arr);
521     }
522 
523     v = arr->elts + 2;
524     if ((usr->flags & CF_UDP) || v->type == T_STRING) {
525 	error("Datagram challenge already set");
526     }
527     usr->flags |= CF_OUTPUT;
528     PUT_STRVAL_NOREF(&val, str);
529     d_assign_elt(data, arr, v, &val);
530 }
531 
532 /*
533  * NAME:	comm->write()
534  * DESCRIPTION:	add bytes to output buffer
535  */
comm_write(user * usr,object * obj,string * str,char * text,unsigned int len)536 static int comm_write(user *usr, object *obj, string *str, char *text,
537 	unsigned int len)
538 {
539     dataspace *data;
540     array *arr;
541     value *v;
542     ssizet osdone, olen;
543     value val;
544 
545     arr = d_get_extravar(data = o_dataspace(obj))->u.array;
546     if (!(usr->flags & CF_FLUSH)) {
547 	addtoflush(usr, arr);
548     }
549 
550     v = arr->elts + 1;
551     if (v->type == T_STRING) {
552 	/* append to existing buffer */
553 	osdone = (usr->outbuf == v->u.string) ? usr->osdone : 0;
554 	olen = v->u.string->len - osdone;
555 	if (olen + len > MAX_STRLEN) {
556 	    len = MAX_STRLEN - olen;
557 	    if (len == 0 ||
558 		((usr->flags & CF_TELNET) && text[0] == (char) IAC &&
559 		 len < MAXIACSEQLEN)) {
560 		return 0;
561 	    }
562 	}
563 	str = str_new((char *) NULL, (long) olen + len);
564 	memcpy(str->text, v->u.string->text + osdone, olen);
565 	memcpy(str->text + olen, text, len);
566     } else {
567 	/* create new buffer */
568 	if (usr->flags & CF_ODONE) {
569 	    usr->flags &= ~CF_ODONE;
570 	    --odone;
571 	}
572 	usr->flags |= CF_OUTPUT;
573 	if (str == (string *) NULL) {
574 	    str = str_new(text, (long) len);
575 	}
576     }
577 
578     PUT_STRVAL_NOREF(&val, str);
579     d_assign_elt(data, arr, v, &val);
580     return len;
581 }
582 
583 /*
584  * NAME:	comm->send()
585  * DESCRIPTION:	send a message to a user
586  */
comm_send(object * obj,string * str)587 int comm_send(object *obj, string *str)
588 {
589     user *usr;
590 
591     usr = &users[EINDEX(obj->etabi)];
592     if (usr->flags & CF_TELNET) {
593 	char outbuf[OUTBUF_SIZE];
594 	char *p, *q;
595 	unsigned int len, size, n;
596 
597 	/*
598 	 * telnet connection
599 	 */
600 	p = str->text;
601 	len = str->len;
602 	q = outbuf;
603 	size = 0;
604 	for (;;) {
605 	    if (len == 0 || size >= OUTBUF_SIZE - 1 || UCHAR(*p) == IAC) {
606 		n = comm_write(usr, obj, (string *) NULL, outbuf, size);
607 		if (n != size) {
608 		    /*
609 		     * count how many bytes of original string were written
610 		     */
611 		    for (n = size - n; n != 0; --n) {
612 			if (*--p == *--q) {
613 			    len++;
614 			    if (UCHAR(*p) == IAC) {
615 				break;
616 			    }
617 			} else if (*q == CR) {
618 			    /* inserted CR */
619 			    p++;
620 			} else {
621 			    /* skipped char */
622 			    q++;
623 			    len++;
624 			}
625 		    }
626 		    return str->len - len;
627 		}
628 		if (len == 0) {
629 		    return str->len;
630 		}
631 		size = 0;
632 		q = outbuf;
633 	    }
634 	    if (UCHAR(*p) == IAC) {
635 		/*
636 		 * double the telnet IAC character
637 		 */
638 		*q++ = (char) IAC;
639 		size++;
640 	    } else if (*p == LF) {
641 		/*
642 		 * insert CR before LF
643 		 */
644 		*q++ = CR;
645 		size++;
646 	    }
647 	    *q++ = *p++;
648 	    --len;
649 	    size++;
650 	}
651     } else {
652 	/*
653 	 * binary connection
654 	 */
655 	return comm_write(usr, obj, str, str->text, str->len);
656     }
657 }
658 
659 /*
660  * NAME:	comm->udpsend()
661  * DESCRIPTION:	send a message on the UDP channel of a binary connection
662  */
comm_udpsend(object * obj,string * str)663 int comm_udpsend(object *obj, string *str)
664 {
665     user *usr;
666     dataspace *data;
667     array *arr;
668     value *v;
669     value val;
670 
671     usr = &users[EINDEX(obj->etabi)];
672     if ((usr->flags & (CF_TELNET | CF_UDP)) != CF_UDP) {
673 	error("Object has no datagram channel");
674     }
675     if (!(usr->flags & CF_UDPDATA)) {
676 	error("No response to datagram challenge received yet");
677     }
678 
679     arr = d_get_extravar(data = obj->data)->u.array;
680     if (!(usr->flags & CF_FLUSH)) {
681 	addtoflush(usr, arr);
682     }
683 
684     v = arr->elts + 2;
685     if (v->type == T_STRING) {
686 	return 0;	/* datagram queued already */
687     }
688     usr->flags |= CF_OUTPUT;
689     PUT_STRVAL_NOREF(&val, str);
690     d_assign_elt(data, arr, v, &val);
691 
692     return str->len;
693 }
694 
695 /*
696  * NAME:	comm->echo()
697  * DESCRIPTION:	turn on/off input echoing for a user
698  */
comm_echo(object * obj,int echo)699 bool comm_echo(object *obj, int echo)
700 {
701     user *usr;
702     dataspace *data;
703     array *arr;
704     value *v;
705 
706     usr = &users[EINDEX(obj->etabi)];
707     if (usr->flags & CF_TELNET) {
708 	arr = d_get_extravar(data = obj->data)->u.array;
709 	v = d_get_elts(arr);
710 	if (echo != (v->u.number & CF_ECHO) >> 1) {
711 	    value val;
712 
713 	    if (!(usr->flags & CF_FLUSH)) {
714 		addtoflush(usr, arr);
715 	    }
716 	    val = *v;
717 	    val.u.number ^= CF_ECHO;
718 	    d_assign_elt(data, arr, v, &val);
719 	}
720 	return TRUE;
721     }
722     return FALSE;
723 }
724 
725 /*
726  * NAME:	comm->block()
727  * DESCRIPTION:	suspend or release input from a user
728  */
comm_block(object * obj,int block)729 void comm_block(object *obj, int block)
730 {
731     user *usr;
732     dataspace *data;
733     array *arr;
734     value *v;
735 
736     usr = &users[EINDEX(obj->etabi)];
737     arr = d_get_extravar(data = obj->data)->u.array;
738     v = d_get_elts(arr);
739     if (block != (v->u.number & CF_BLOCKED) >> 4) {
740 	value val;
741 
742 	if (!(usr->flags & CF_FLUSH)) {
743 	    addtoflush(usr, arr);
744 	}
745 	val = *v;
746 	val.u.number ^= CF_BLOCKED;
747 	d_assign_elt(data, arr, v, &val);
748     }
749 }
750 
751 #ifdef NETWORK_EXTENSIONS
comm_udpflush(user * usr,object * obj,dataspace * data,array * arr)752 static void comm_udpflush(user *usr, object *obj, dataspace *data, array *arr)
753 {
754     value *v;
755     char *buf;
756     int res;
757 
758     UNREFERENCED_PARAMETER(data);
759     UNREFERENCED_PARAMETER(obj);
760 
761     v = d_get_elts(arr);
762     if (!conn_wrdone(usr->conn)) {
763 	return;
764     }
765     buf = v[1].u.string->text;
766     res = conn_udpsend(usr->conn, buf, strlen(buf), v[2].u.string->text,
767 	(unsigned short) v[3].u.number);
768     if (res == -1) {
769 	 /* EAGAIN occured, datagram could not be sent */
770     }
771     usr->flags &= ~CF_OUTPUT;
772     usr->flags |= CF_ODONE;
773     odone++;
774 }
775 #endif
776 
777 /*
778  * NAME:	comm->uflush()
779  * DESCRIPTION:	flush output buffers for a single user only
780  */
comm_uflush(user * usr,object * obj,dataspace * data,array * arr)781 static void comm_uflush(user *usr, object *obj, dataspace *data, array *arr)
782 {
783     value *v;
784     int n;
785 
786     UNREFERENCED_PARAMETER(obj);
787 
788     v = d_get_elts(arr);
789 
790     if (v[1].type == T_STRING) {
791 	if (conn_wrdone(usr->conn)) {
792 	    n = conn_write(usr->conn, v[1].u.string->text + usr->osdone,
793 			   v[1].u.string->len - usr->osdone);
794 	    if (n >= 0) {
795 		n += usr->osdone;
796 		if (n == v[1].u.string->len) {
797 		    /* buffer fully drained */
798 		    n = 0;
799 		    usr->flags &= ~CF_OUTPUT;
800 		    usr->flags |= CF_ODONE;
801 		    odone++;
802 		    d_assign_elt(data, arr, &v[1], &nil_value);
803 		}
804 		usr->osdone = n;
805 	    } else {
806 		/* wait for conn_read() to discover the problem */
807 		usr->flags &= ~CF_OUTPUT;
808 	    }
809 	}
810     } else {
811 	/* just a datagram */
812 	usr->flags &= ~CF_OUTPUT;
813     }
814 
815     if (v[2].type == T_STRING) {
816 	if (usr->flags & CF_UDP) {
817 	    conn_udpwrite(usr->conn, v[2].u.string->text, v[2].u.string->len);
818 #ifndef NETWORK_EXTENSIONS
819 	} else if (conn_udp(usr->conn, v[2].u.string->text, v[2].u.string->len))
820 	{
821 	    usr->flags |= CF_UDP;
822 #endif
823 	}
824 	d_assign_elt(data, arr, &v[2], &nil_value);
825     }
826 }
827 
828 /*
829  * NAME:	comm->flush()
830  * DESCRIPTION:	flush state, output and connections
831  */
comm_flush()832 void comm_flush()
833 {
834     user *usr;
835     object *obj;
836     array *arr;
837     value *v;
838 
839     while (outbound != (user *) NULL) {
840 	usr = outbound;
841 	outbound = usr->flush;
842 
843 	arr = usr->extra;
844 	obj = OBJ(usr->oindex);
845 	if ((obj->flags & O_SPECIAL) == O_USER) {
846 	    /* connect */
847 	    usr->conn = conn_connect(arr->elts[1].u.string->text,
848 				     arr->elts[1].u.string->len);
849 	    if (usr->conn == (connection *) NULL) {
850 		fatal("can't connect to server");
851 	    }
852 
853 	    d_assign_elt(obj->data, arr, &arr->elts[1], &nil_value);
854 	    arr_del(arr);
855 	    usr->flags &= ~CF_FLUSH;
856 	} else {
857 	    /* discard */
858 	    usr->flush = flush;
859 	    flush = usr;
860 	}
861     }
862 
863     while (flush != (user *) NULL) {
864 	usr = flush;
865 	flush = usr->flush;
866 
867 	/*
868 	 * status change
869 	 */
870 	obj = OBJ(usr->oindex);
871 	arr = usr->extra;
872 	v = arr->elts;
873 	if (usr->flags & CF_TELNET) {
874 	    if ((v->u.number ^ usr->flags) & CF_ECHO) {
875 		char buf[3];
876 
877 		/* change echo */
878 		buf[0] = (char) IAC;
879 		buf[1] = (v->u.number & CF_ECHO) ? WONT : WILL;
880 		buf[2] = TELOPT_ECHO;
881 		if (comm_write(usr, obj, (string *) NULL, buf, 3) != 0) {
882 		    usr->flags ^= CF_ECHO;
883 		}
884 	    }
885 	    if (usr->flags & CF_PROMPT) {
886 		usr->flags &= ~CF_PROMPT;
887 		if ((usr->flags & CF_GA) && v[1].type == T_STRING &&
888 		    usr->outbuf != v[1].u.string) {
889 		    static char ga[] = { (char) IAC, (char) GA };
890 
891 		    /* append go-ahead */
892 		    comm_write(usr, obj, (string *) NULL, ga, 2);
893 		}
894 	    }
895 	}
896 	if ((v->u.number ^ usr->flags) & CF_BLOCKED) {
897 	    usr->flags ^= CF_BLOCKED;
898 	    conn_block(usr->conn, ((usr->flags & CF_BLOCKED) != 0));
899 	}
900 
901 	/*
902 	 * write
903 	 */
904 	if (usr->outbuf != (string *) NULL) {
905 	    if (usr->outbuf != v[1].u.string) {
906 		usr->osdone = 0;	/* new mesg before buffer drained */
907 	    }
908 	    str_del(usr->outbuf);
909 	    usr->outbuf = (string *) NULL;
910 	}
911 	if (usr->flags & CF_OUTPUT) {
912 #ifdef NETWORK_EXTENSIONS
913 	    if ((usr->flags & CF_DATAGRAM)) {
914 		comm_udpflush(usr, obj, obj->data, arr);
915 	    } else
916 #endif
917 	    comm_uflush(usr, obj, obj->data, arr);
918 	}
919 	/*
920 	 * disconnect
921 	 */
922 	if ((obj->flags & O_SPECIAL) != O_USER) {
923 	    d_wipe_extravar(obj->data);
924 	    if (usr->conn != (connection *) NULL) {
925 		conn_del(usr->conn);
926 	    }
927 #ifdef NETWORK_EXTENSIONS
928 	    if ((usr->flags & (CF_TELNET | CF_PORT)) == CF_TELNET) {
929 #else
930 	    if (usr->flags & CF_TELNET) {
931 #endif
932 		newlines -= usr->newlines;
933 		FREE(usr->inbuf - 1);
934 	    }
935 	    if (usr->flags & CF_ODONE) {
936 		--odone;
937 	    }
938 
939 	    usr->oindex = OBJ_NONE;
940 	    if (usr->next == usr) {
941 		lastuser = (user *) NULL;
942 	    } else {
943 		usr->next->prev = usr->prev;
944 		usr->prev->next = usr->next;
945 		if (usr == lastuser) {
946 		    lastuser = usr->next;
947 		}
948 	    }
949 	    usr->next = freeuser;
950 	    freeuser = usr;
951 #ifdef NETWORK_EXTENSIONS
952 	    if (usr->flags & CF_PORT) {
953 		--nports;
954 	    } else {
955 		--nusers;
956 	    }
957 #else
958 	    --nusers;
959 #endif
960 	}
961 
962 	arr_del(arr);
963 	usr->flags &= ~CF_FLUSH;
964     }
965 }
966 
967 /*
968  * NAME:	comm->taccept()
969  * DESCRIPTION:	accept a telnet connection
970  */
971 static void comm_taccept(frame *f, struct _connection_ *conn, int port)
972 {
973     user *usr;
974     object *obj;
975 
976     if (ec_push((ec_ftn) NULL)) {
977 	conn_del(conn);		/* delete connection */
978 	error((char *) NULL);	/* pass on error */
979     }
980     PUSH_INTVAL(f, port);
981     call_driver_object(f, "telnet_connect", 1);
982     if (f->sp->type != T_OBJECT) {
983 	fatal("driver->telnet_connect() did not return persistent object");
984     }
985     obj = OBJ(f->sp->oindex);
986     f->sp++;
987     usr = comm_new(f, obj, conn, TRUE);
988     ec_pop();
989 
990     usr->flags |= CF_PROMPT;
991     addtoflush(usr, d_get_extravar(o_dataspace(obj))->u.array);
992     this_user = obj->index;
993     if (i_call(f, obj, (array *) NULL, "open", 4, TRUE, 0)) {
994 	i_del_value(f->sp++);
995     }
996     endthread();
997     this_user = OBJ_NONE;
998 }
999 
1000 /*
1001  * NAME:	comm->baccept()
1002  * DESCRIPTION:	accept a binary connection
1003  */
1004 static void comm_baccept(frame *f, struct _connection_ *conn, int port)
1005 {
1006     object *obj;
1007 
1008     if (ec_push((ec_ftn) NULL)) {
1009 	conn_del(conn);		/* delete connection */
1010 	error((char *) NULL);	/* pass on error */
1011     }
1012     PUSH_INTVAL(f, port);
1013     call_driver_object(f, "binary_connect", 1);
1014     if (f->sp->type != T_OBJECT) {
1015 	fatal("driver->binary_connect() did not return persistent object");
1016     }
1017     obj = OBJ(f->sp->oindex);
1018     f->sp++;
1019     comm_new(f, obj, conn, FALSE);
1020     ec_pop();
1021 
1022     this_user = obj->index;
1023     if (i_call(f, obj, (array *) NULL, "open", 4, TRUE, 0)) {
1024 	i_del_value(f->sp++);
1025     }
1026     endthread();
1027     this_user = OBJ_NONE;
1028 }
1029 
1030 /*
1031  * NAME:	comm->receive()
1032  * DESCRIPTION:	receive a message from a user
1033  */
1034 void comm_receive(frame *f, Uint timeout, unsigned int mtime)
1035 {
1036     static char intr[] =	{ '\177' };
1037     static char brk[] =		{ '\034' };
1038     static char tm[] =		{ (char) IAC, (char) WONT, (char) TELOPT_TM };
1039     static char will_sga[] =	{ (char) IAC, (char) WILL, (char) TELOPT_SGA };
1040     static char wont_sga[] =	{ (char) IAC, (char) WONT, (char) TELOPT_SGA };
1041     static char mode_edit[] =	{ (char) IAC, (char) SB,
1042 				  (char) TELOPT_LINEMODE, (char) LM_MODE,
1043 				  (char) MODE_EDIT, (char) IAC, (char) SE };
1044     char buffer[BINBUF_SIZE];
1045     object *obj;
1046     user *usr;
1047     int n, i, state, nls;
1048     char *p, *q;
1049 #ifndef NETWORK_EXTENSIONS
1050     connection *conn;
1051 #endif
1052 
1053     if (newlines != 0 || odone != 0) {
1054 	timeout = mtime = 0;
1055     }
1056     n = conn_select(timeout, mtime);
1057     if ((n <= 0) && (newlines == 0) && (odone == 0)) {
1058 	/*
1059 	 * call_out to do, or timeout
1060 	 */
1061 	return;
1062     }
1063 
1064     if (ec_push(errhandler)) {
1065 	endthread();
1066 	this_user = OBJ_NONE;
1067 	return;
1068     }
1069 
1070 #ifndef NETWORK_EXTENSIONS
1071     if (ntport != 0 && nusers < maxusers) {
1072 	n = nexttport;
1073 	do {
1074 	    /*
1075 	     * accept new telnet connection
1076 	     */
1077 	    conn = conn_tnew6(n);
1078 	    if (conn != (connection *) NULL) {
1079 		comm_taccept(f, conn, n);
1080 		nexttport = (n + 1) % ntport;
1081 	    }
1082 	    if (nusers < maxusers) {
1083 		conn = conn_tnew(n);
1084 		if (conn != (connection *) NULL) {
1085 		    comm_taccept(f, conn, n);
1086 		    nexttport = (n + 1) % ntport;
1087 		}
1088 	    }
1089 
1090 	    n = (n + 1) % ntport;
1091 	} while (n != nexttport);
1092     }
1093 
1094     if (nbport != 0 && nusers < maxusers) {
1095 	n = nextbport;
1096 	do {
1097 	    /*
1098 	     * accept new binary connection
1099 	     */
1100 	    conn = conn_bnew6(n);
1101 	    if (conn != (connection *) NULL) {
1102 		comm_baccept(f, conn, n);
1103 	    }
1104 	    if (nusers < maxusers) {
1105 		conn = conn_bnew(n);
1106 		if (conn != (connection *) NULL) {
1107 		    comm_baccept(f, conn, n);
1108 		}
1109 	    }
1110 	    n = (n + 1) % nbport;
1111 	    if (nusers == maxusers) {
1112 		nextbport = n;
1113 		break;
1114 	    }
1115 	} while (n != nextbport);
1116     }
1117 
1118     for (i = nusers; i > 0; --i) {
1119 #else
1120     for (i = nusers + nports; i > 0; --i) {
1121 #endif
1122 	usr = lastuser;
1123 	lastuser = usr->next;
1124 
1125 	obj = OBJ(usr->oindex);
1126 
1127 
1128 	/*
1129 	 * Check if we have an event pending from connect() and if so, handle it.
1130 	 */
1131 	if (usr->flags & CF_OPENDING) {
1132 	    int retval;
1133 	    uindex old_user;
1134 	    bool refused;
1135 	    retval = conn_check_connected(usr->conn, &refused);
1136 	    /*
1137 	     * Something happened to the connection..
1138 	     * its either connected or in error state now.
1139 	     */
1140 	    if (retval != 0) {
1141 		usr->flags &= ~CF_OPENDING;
1142 		if (!(usr->flags & CF_FLUSH)) {
1143 		    addtoflush(usr, d_get_extravar(o_dataspace(obj))->u.array);
1144 		}
1145 		old_user = this_user;
1146 		this_user = obj->index;
1147 		/*
1148 		 * Error, report it to the user object.
1149 		 */
1150 		if (retval < 0) {
1151 		    obj->flags &= ~O_USER;
1152 #ifdef NETWORK_EXTENSIONS
1153 		    if (retval == -1) {
1154 			PUSH_STRVAL(f, str_new(strerror(errno), strlen(strerror(errno))));
1155 		    } else {
1156 			PUSH_STRVAL(f, str_new("socket unexpectedly closed", 26));
1157 		    }
1158 
1159 		    if (i_call(f, obj, (array *) NULL, "receive_error", 13, TRUE, 1)) {
1160 			i_del_value(f->sp++);
1161 		    }
1162 #else
1163 		    PUSH_INTVAL(f, refused);
1164 		    if (i_call(f, obj, (array *) NULL, "unconnected", 11, TRUE, 1)) {
1165 			i_del_value(f->sp++);
1166 		    }
1167 #endif
1168 		    endthread();
1169 		/*
1170 		 * Connection completed, call open in the user object.
1171 		 */
1172 		} else if (retval > 0) {
1173 		    if (i_call(f, obj, (array *) NULL, "open", 4, TRUE, 0)) {
1174 			i_del_value(f->sp++);
1175 		    }
1176 		    endthread();
1177 		}
1178 		this_user = old_user;
1179 	    }
1180 	    /*
1181 	     * Don't do anything else for user objects with pending
1182 	     * connects.
1183 	     */
1184 	    continue;
1185 	}
1186 	if (usr->flags & CF_OUTPUT) {
1187 	    dataspace *data;
1188 
1189 	    data = o_dataspace(obj);
1190 	    comm_uflush(usr, obj, data, d_get_extravar(data)->u.array);
1191 	}
1192 	if (usr->flags & CF_ODONE) {
1193 	    /* callback */
1194 	    usr->flags &= ~CF_ODONE;
1195 	    --odone;
1196 	    this_user = obj->index;
1197 #ifdef NETWORK_EXTENSIONS
1198 	    /*
1199 	     * message_done for tcp, datagram_done for udp
1200 	     */
1201 	    if (usr->flags & CF_DATAGRAM) {
1202 		if (i_call(f, obj, (array *) NULL, "datagram_done", 13, TRUE, 0)) {
1203 		    i_del_value(f->sp++);
1204 		    endthread();
1205 		}
1206 	    } else
1207 #endif
1208 	    if (i_call(f, obj, (array *) NULL, "message_done", 12, TRUE, 0)) {
1209 		i_del_value(f->sp++);
1210 		endthread();
1211 	    }
1212 
1213 	    this_user = OBJ_NONE;
1214 	    if (obj->count == 0) {
1215 		break;	/* continue, unless the connection was closed */
1216 	    }
1217 	}
1218 
1219 	if (usr->flags & CF_BLOCKED) {
1220 	    continue;	/* no input on this connection */
1221 	}
1222 
1223 #ifdef NETWORK_EXTENSIONS
1224 	if ((usr->flags & (CF_DATAGRAM | CF_PORT)) == (CF_PORT | CF_DATAGRAM)) {
1225 	    char *addr;
1226 	    int port;
1227 	    n = conn_udpreceive(usr->conn, buffer, BINBUF_SIZE, &addr, &port);
1228 	    if (n >= 0) {
1229 		PUSH_STRVAL(f, str_new(buffer, (long) n));
1230 		PUSH_STRVAL(f, str_new(addr, strlen(addr)));
1231 		PUSH_INTVAL(f, port);
1232 		if (i_call(f, obj, (array *) NULL, "receive_datagram", 16,
1233 			   TRUE, 3)) {
1234 		    i_del_value(f->sp++);
1235 		    endthread();
1236 		}
1237 	    }
1238 	    continue;
1239 	}
1240 
1241 	if ((usr->flags & (CF_PORT | CF_DATAGRAM)) == CF_PORT) {
1242 	    if (nusers <= maxusers) {
1243 		connection *conn;
1244 		char ip[40];
1245 		user *newusr;
1246 		uindex olduser;
1247 
1248 		conn = (connection *) conn_accept(usr->conn);
1249 
1250 		if (conn == (connection *) NULL) {
1251 		    break;
1252 		}
1253 		if (nusers == maxusers) {
1254 		    conn_del(conn);
1255 		    error("Maximum number of users exceeded");
1256 		}
1257 
1258 		if (ec_push((ec_ftn) NULL)) {
1259 		    conn_del(conn);
1260 		    error((char *) NULL);
1261 		}
1262 		(--f->sp)->type = T_STRING;
1263 		conn_ipnum(conn,ip);
1264 		PUT_STRVAL(f->sp, str_new(ip, strlen(ip)));
1265 		(--f->sp)->type = T_INT;
1266 		f->sp->u.number = conn_at(conn);
1267 		if (!i_call(f, OBJ(usr->oindex), (array *) NULL, "connection",
1268 			    10, TRUE, 2)) {
1269 		    conn_del(conn);
1270 		    error("Missing connection()-function");
1271 		}
1272 		if (f->sp->type != T_OBJECT) {
1273 		    conn_del(conn);
1274 		    error("object->connection() did not return a persistent object");
1275 		}
1276 		obj = OBJ(f->sp->oindex);
1277 		f->sp++;
1278 		newusr = comm_new(f,obj, conn, usr->flags & CF_TELNET);
1279 		ec_pop();
1280 		endthread();
1281 
1282 		newusr->flags |= CF_PROMPT;
1283 		addtoflush(newusr,
1284 			   d_get_extravar(o_dataspace(obj))->u.array);
1285 		olduser = this_user;
1286 		this_user = obj->index;
1287 		if (i_call(f, obj, (array *) NULL, "open", 4, TRUE, 0)) {
1288 		    i_del_value(f->sp++);
1289 		    endthread();
1290 		}
1291 		this_user=olduser;
1292 	    }
1293 	    continue;
1294 
1295 	}
1296 
1297 	if ((obj->flags & O_USER) != O_USER) {
1298 	    continue;
1299 	}
1300 
1301 #endif
1302 	if (usr->flags & CF_TELNET) {
1303 	    /*
1304 	     * telnet connection
1305 	     */
1306 	    if (usr->inbufsz != INBUF_SIZE) {
1307 		p = usr->inbuf + usr->inbufsz;
1308 		n = conn_read(usr->conn, p, INBUF_SIZE - usr->inbufsz);
1309 		if (n < 0) {
1310 		    if (usr->inbufsz != 0) {
1311 			if (p[-1] != LF) {
1312 			    /*
1313 			     * add a newline at the end
1314 			     */
1315 			    *p = LF;
1316 			    n = 1;
1317 			}
1318 		    } else if (!(usr->flags & CF_OUTPUT)) {
1319 			/*
1320 			 * empty buffer, no more input, no pending output
1321 			 */
1322 			comm_del(f, usr, obj, FALSE);
1323 			endthread();	/* this cannot be in comm_del() */
1324 			break;
1325 		    }
1326 		}
1327 
1328 		state = usr->state;
1329 		nls = usr->newlines;
1330 		q = p;
1331 		while (n > 0) {
1332 		    switch (state) {
1333 		    case TS_DATA:
1334 			switch (UCHAR(*p)) {
1335 			case IAC:
1336 			    state = TS_IAC;
1337 			    break;
1338 
1339 			case BS:
1340 			case 0x7f:
1341 			    if (q[-1] != LF) {
1342 				--q;
1343 			    }
1344 			    break;
1345 
1346 			case CR:
1347 			    nls++;
1348 			    newlines++;
1349 			    *q++ = LF;
1350 			    state = TS_CRDATA;
1351 			    break;
1352 
1353 			case LF:
1354 			    nls++;
1355 			    newlines++;
1356 			    /* fall through */
1357 			default:
1358 			    *q++ = *p;
1359 			    /* fall through */
1360 			case '\0':
1361 			    break;
1362 			}
1363 			break;
1364 
1365 		    case TS_CRDATA:
1366 			switch (UCHAR(*p)) {
1367 			case IAC:
1368 			    state = TS_IAC;
1369 			    break;
1370 
1371 			case CR:
1372 			    nls++;
1373 			    newlines++;
1374 			    *q++ = LF;
1375 			    break;
1376 
1377 			default:
1378 			    *q++ = *p;
1379 			    /* fall through */
1380 			case '\0':
1381 			case LF:
1382 			case BS:
1383 			case 0x7f:
1384 			    state = TS_DATA;
1385 			    break;
1386 			}
1387 			break;
1388 
1389 		    case TS_IAC:
1390 			switch (UCHAR(*p)) {
1391 			case IAC:
1392 			    *q++ = *p;
1393 			    state = TS_DATA;
1394 			    break;
1395 
1396 			case DO:
1397 			    state = TS_DO;
1398 			    break;
1399 
1400 			case DONT:
1401 			    state = TS_DONT;
1402 			    break;
1403 
1404 			case WILL:
1405 			    state = TS_WILL;
1406 			    break;
1407 
1408 			case WONT:
1409 			    state = TS_WONT;
1410 			    break;
1411 
1412 			case SB:
1413 			    state = TS_SB;
1414 			    break;
1415 
1416 			case IP:
1417 			    comm_write(usr, obj, (string *) NULL, intr,
1418 				       sizeof(intr));
1419 			    state = TS_DATA;
1420 			    break;
1421 
1422 			case BREAK:
1423 			    comm_write(usr, obj, (string *) NULL, brk,
1424 				       sizeof(brk));
1425 			    state = TS_DATA;
1426 			    break;
1427 
1428 			case AYT:
1429 			    comm_write(usr, obj, (string *) NULL, ayt,
1430 				       strlen(ayt));
1431 			    state = TS_DATA;
1432 			    break;
1433 
1434 			default:
1435 			    /* let's hope it wasn't important */
1436 			    state = TS_DATA;
1437 			    break;
1438 			}
1439 			break;
1440 
1441 		    case TS_DO:
1442 			if (UCHAR(*p) == TELOPT_TM) {
1443 			    comm_write(usr, obj, (string *) NULL, tm,
1444 				       sizeof(tm));
1445 			} else if (UCHAR(*p) == TELOPT_SGA) {
1446 			    usr->flags &= ~CF_GA;
1447 			    comm_write(usr, obj, (string *) NULL, will_sga,
1448 				       sizeof(will_sga));
1449 			}
1450 			state = TS_DATA;
1451 			break;
1452 
1453 		    case TS_DONT:
1454 			if (UCHAR(*p) == TELOPT_SGA) {
1455 			    usr->flags |= CF_GA;
1456 			    comm_write(usr, obj, (string *) NULL, wont_sga,
1457 				       sizeof(wont_sga));
1458 			}
1459 			state = TS_DATA;
1460 			break;
1461 
1462 		    case TS_WILL:
1463 			if (UCHAR(*p) == TELOPT_LINEMODE) {
1464 			    /* linemode confirmed; now request editing */
1465 			    comm_write(usr, obj, (string *) NULL, mode_edit,
1466 				       sizeof(mode_edit));
1467 			}
1468 			/* fall through */
1469 		    case TS_WONT:
1470 			state = TS_DATA;
1471 			break;
1472 
1473 		    case TS_SB:
1474 			/* skip to the end */
1475 			if (UCHAR(*p) == IAC) {
1476 			    state = TS_SE;
1477 			}
1478 			break;
1479 
1480 		    case TS_SE:
1481 			if (UCHAR(*p) == SE) {
1482 			    /* end of subnegotiation */
1483 			    state = TS_DATA;
1484 			} else {
1485 			    state = TS_SB;
1486 			}
1487 			break;
1488 		    }
1489 		    p++;
1490 		    --n;
1491 		}
1492 		usr->state = state;
1493 		usr->newlines = nls;
1494 		usr->inbufsz = q - usr->inbuf;
1495 		if (nls == 0) {
1496 		    continue;
1497 		}
1498 
1499 		/*
1500 		 * input terminated by \n
1501 		 */
1502 		p = (char *) memchr(q = usr->inbuf, LF, usr->inbufsz);
1503 		usr->newlines--;
1504 		--newlines;
1505 		n = p - usr->inbuf;
1506 		p++;			/* skip \n */
1507 		usr->inbufsz -= n + 1;
1508 
1509 		PUSH_STRVAL(f, str_new(usr->inbuf, (long) n));
1510 		for (n = usr->inbufsz; n != 0; --n) {
1511 		    *q++ = *p++;
1512 		}
1513 	    } else {
1514 		/*
1515 		 * input buffer full
1516 		 */
1517 		n = usr->inbufsz;
1518 		usr->inbufsz = 0;
1519 		PUSH_STRVAL(f, str_new(usr->inbuf, (long) n));
1520 	    }
1521 	    usr->flags |= CF_PROMPT;
1522 	    if (!(usr->flags & CF_FLUSH)) {
1523 		addtoflush(usr, d_get_extravar(o_dataspace(obj))->u.array);
1524 	    }
1525 	} else {
1526 	    /*
1527 	     * binary connection
1528 	     */
1529 #ifndef NETWORK_EXTENSIONS
1530 	    if (usr->flags & CF_UDP) {
1531 		if (usr->flags & CF_UDPDATA) {
1532 		    n = conn_udpread(usr->conn, buffer, BINBUF_SIZE);
1533 		    if (n >= 0) {
1534 			/*
1535 			 * received datagram
1536 			 */
1537 			PUSH_STRVAL(f, str_new(buffer, (long) n));
1538 			this_user = obj->index;
1539 			if (i_call(f, obj, (array *) NULL, "receive_datagram",
1540 				   16, TRUE, 1)) {
1541 			    i_del_value(f->sp++);
1542 			    endthread();
1543 			}
1544 			this_user = OBJ_NONE;
1545 		    }
1546 		} else if (conn_udpcheck(usr->conn)) {
1547 		    usr->flags |= CF_UDPDATA;
1548 		    this_user = obj->index;
1549 		    if (i_call(f, obj, (array *) NULL, "open_datagram", 13,
1550 			       TRUE, 0)) {
1551 			i_del_value(f->sp++);
1552 			endthread();
1553 		    }
1554 		    this_user = OBJ_NONE;
1555 		}
1556 	    }
1557 #endif
1558 
1559 	    n = conn_read(usr->conn, p = buffer, BINBUF_SIZE);
1560 	    if (n <= 0) {
1561 		if (n < 0 && !(usr->flags & CF_OUTPUT)) {
1562 		    /*
1563 		     * no more input and no pending output
1564 		     */
1565 		    comm_del(f, usr, obj, FALSE);
1566 		    endthread();	/* this cannot be in comm_del() */
1567 		    break;
1568 		}
1569 		continue;
1570 	    }
1571 
1572 	    PUSH_STRVAL(f, str_new(buffer, (long) n));
1573 	}
1574 
1575 	this_user = obj->index;
1576 	if (i_call(f, obj, (array *) NULL, "receive_message", 15, TRUE, 1)) {
1577 	    i_del_value(f->sp++);
1578 	    endthread();
1579 	}
1580 	this_user = OBJ_NONE;
1581 	break;
1582     }
1583 
1584     ec_pop();
1585     comm_flush();
1586 }
1587 
1588 /*
1589  * NAME:	comm->ip_number()
1590  * DESCRIPTION:	return the ip number of a user (as a string)
1591  */
1592 string *comm_ip_number(object *obj)
1593 {
1594     char ipnum[40];
1595 
1596     conn_ipnum(users[EINDEX(obj->etabi)].conn, ipnum);
1597     return str_new(ipnum, (long) strlen(ipnum));
1598 }
1599 
1600 /*
1601  * NAME:	comm->ip_name()
1602  * DESCRIPTION:	return the ip name of a user
1603  */
1604 string *comm_ip_name(object *obj)
1605 {
1606     char ipname[1024];
1607 
1608     conn_ipname(users[EINDEX(obj->etabi)].conn, ipname);
1609     return str_new(ipname, (long) strlen(ipname));
1610 }
1611 
1612 /*
1613  * NAME:	comm->close()
1614  * DESCRIPTION:	remove a user
1615  */
1616 void comm_close(frame *f, object *obj)
1617 {
1618     comm_del(f, &users[EINDEX(obj->etabi)], obj, TRUE);
1619 }
1620 
1621 /*
1622  * NAME:	comm->user()
1623  * DESCRIPTION:	return the current user
1624  */
1625 object *comm_user()
1626 {
1627     object *obj;
1628 
1629     return (this_user != OBJ_NONE && (obj=OBJR(this_user))->count != 0) ?
1630 	    obj : (object *) NULL;
1631 }
1632 
1633 # ifdef NETWORK_EXTENSIONS
1634 /*
1635  * NAME:	comm->ports()
1636  * DESCRIPTION:	return an array with all port objects
1637  */
1638 array *comm_ports(dataspace *data)
1639 {
1640     array *a;
1641     int i, n;
1642     user *usr;
1643     value *v;
1644     object *obj;
1645 
1646     n = 0;
1647     for (i = nports, usr = users; i > 0; usr++) {
1648 	if (usr->oindex != OBJ_NONE && (usr->flags & CF_PORT)) {
1649 	    --i;
1650 	    if (OBJR(usr->oindex)->count != 0) {
1651 		n++;
1652 	    }
1653 	}
1654     }
1655 
1656     a = arr_new(data, (long) n);
1657     v = a->elts;
1658     for (usr = users; n > 0; usr++) {
1659 	if (usr->oindex != OBJ_NONE && (usr->flags & CF_PORT) &&
1660 	    (obj=OBJR(usr->oindex))->count != 0) {
1661 	    PUT_OBJVAL(v, obj);
1662 	    v++;
1663 	    --n;
1664 	}
1665     }
1666     return a;
1667 }
1668 # endif
1669 
1670 /*
1671  * NAME:	comm->users()
1672  * DESCRIPTION:	return an array with all user objects
1673  */
1674 array *comm_users(dataspace *data)
1675 {
1676     array *a;
1677     int i, n;
1678     user *usr;
1679     value *v;
1680     object *obj;
1681 
1682     n = 0;
1683     for (i = nusers, usr = users; i > 0; usr++) {
1684 	if (usr->oindex != OBJ_NONE) {
1685 #ifdef NETWORK_EXTENSIONS
1686 	    if (usr->flags & CF_PORT) {
1687 		continue;
1688 	    }
1689 #endif
1690 	    --i;
1691 	    if (!(usr->flags & CF_OPENDING)) {
1692 		if (OBJR(usr->oindex)->count != 0) {
1693 		    n++;
1694 		}
1695 	    }
1696 	}
1697     }
1698 
1699     a = arr_new(data, (long) n);
1700     v = a->elts;
1701     for (usr = users; n > 0; usr++) {
1702 	if (usr->oindex != OBJ_NONE && (obj=OBJR(usr->oindex))->count != 0) {
1703 #ifdef NETWORK_EXTENSIONS
1704 	    if (usr->flags & CF_PORT) {
1705 		continue;
1706 	    }
1707 #endif
1708 	    if (!(usr->flags & CF_OPENDING)) {
1709 		PUT_OBJVAL(v, obj);
1710 		v++;
1711 		--n;
1712 	    }
1713 	}
1714     }
1715     return a;
1716 }
1717 
1718 /*
1719  * NAME:	comm->is_connection()
1720  * DESCRIPTION: is this REALLY a user object?
1721  */
1722 bool comm_is_connection(object *obj)
1723 {
1724     user *usr;
1725 
1726     if ((obj->flags & O_SPECIAL) == O_USER) {
1727 	usr = &users[EINDEX(obj->etabi)];
1728 # ifdef NETWORK_EXTENSIONS
1729 	if (usr->flags & CF_PORT) {
1730 	    return FALSE;
1731 	}
1732 # endif
1733 	if (!(usr->flags & CF_OPENDING)) {
1734 	    return TRUE;
1735 	}
1736     }
1737     return FALSE;
1738 }
1739 
1740 typedef struct {
1741     short version;		/* hotboot version */
1742     Uint nusers;		/* # users */
1743     Uint tbufsz;		/* total telnet buffer size */
1744     Uint ubufsz;		/* total UDP buffer size */
1745 } dump_header;
1746 
1747 static char dh_layout[] = "siii";
1748 
1749 typedef struct {
1750     uindex oindex;		/* object index */
1751     short flags;		/* user flags */
1752     char state;			/* user state */
1753     char cflags;		/* connection flags */
1754     short newlines;		/* # newlines in input buffer */
1755     Uint tbufsz;		/* telnet buffer size */
1756     Uint osdone;		/* amount of output string done */
1757     Int fd;			/* file descriptor */
1758     Uint npkts;			/* # packets in UDP buffer */
1759     Uint ubufsz;		/* UDB buffer size */
1760     unsigned short port;	/* connection port */
1761     short at;			/* connected at */
1762 } duser;
1763 
1764 static char du_layout[] = "usccsiiiiiss";
1765 
1766 /*
1767  * NAME:	comm->dump()
1768  * DESCRIPTION:	save users
1769  */
1770 bool comm_dump(int fd)
1771 {
1772     dump_header dh;
1773     duser *du;
1774     char **bufs, *tbuf, *ubuf;
1775     user *usr;
1776     int i;
1777 
1778     du = (duser *) NULL;
1779     bufs = (char **) NULL;
1780     tbuf = ubuf = (char *) NULL;
1781 
1782     /* header */
1783     dh.version = 0;
1784     dh.nusers = nusers;
1785     dh.tbufsz = 0;
1786     dh.ubufsz = 0;
1787 
1788     /*
1789      * gather information about users
1790      */
1791     if (nusers != 0) {
1792 	du = ALLOC(duser, nusers);
1793 	bufs = ALLOC(char*, 2 * nusers);
1794 
1795 	for (i = nusers, usr = users; i > 0; usr++) {
1796 	    if (usr->oindex != OBJ_NONE) {
1797 		int npkts, ubufsz;
1798 
1799 		du->oindex = usr->oindex;
1800 		du->flags = usr->flags;
1801 		du->state = usr->state;
1802 		du->newlines = usr->newlines;
1803 		du->tbufsz = usr->inbufsz;
1804 		du->osdone = usr->osdone;
1805 		*bufs++ = usr->inbuf;
1806 		if (!conn_export(usr->conn, &du->fd, &du->port, &du->at,
1807 				 &npkts, &ubufsz, bufs++, &du->cflags)) {
1808 		    /* no hotbooting support */
1809 		    FREE(du);
1810 		    FREE(bufs - 2);
1811 		    return FALSE;
1812 		}
1813 		du->npkts = npkts;
1814 		du->ubufsz = ubufsz;
1815 		dh.tbufsz += du->tbufsz;
1816 		dh.ubufsz += du->ubufsz;
1817 
1818 		du++;
1819 		--i;
1820 	    }
1821 	}
1822 	du -= nusers;
1823 	bufs -= 2 * nusers;
1824     }
1825 
1826     /* write header */
1827     if (P_write(fd, (char *) &dh, sizeof(dump_header)) != sizeof(dump_header)) {
1828 	fatal("failed to dump user header");
1829     }
1830 
1831     if (nusers != 0) {
1832 	/*
1833 	 * write users
1834 	 */
1835 	if (P_write(fd, (char *) du, nusers * sizeof(duser)) !=
1836 						    nusers * sizeof(duser)) {
1837 	    fatal("failed to dump users");
1838 	}
1839 
1840 	if (dh.tbufsz != 0) {
1841 	    tbuf = ALLOC(char, dh.tbufsz);
1842 	}
1843 	if (dh.ubufsz != 0) {
1844 	    ubuf = ALLOC(char, dh.ubufsz);
1845 	}
1846 
1847 	/*
1848 	 * copy buffer content
1849 	 */
1850 	for (i = nusers; i > 0; --i, du++) {
1851 	    if (du->tbufsz != 0) {
1852 		memcpy(tbuf, *bufs, du->tbufsz);
1853 		tbuf += du->tbufsz;
1854 	    }
1855 	    bufs++;
1856 	    if (du->ubufsz != 0) {
1857 		memcpy(ubuf, *bufs, du->ubufsz);
1858 		ubuf += du->ubufsz;
1859 	    }
1860 	    bufs++;
1861 	}
1862 	tbuf -= dh.tbufsz;
1863 	ubuf -= dh.ubufsz;
1864 
1865 	/*
1866 	 * write buffer content
1867 	 */
1868 	if (dh.tbufsz != 0) {
1869 	    if (P_write(fd, tbuf, dh.tbufsz) != dh.tbufsz) {
1870 		fatal("failed to dump telnet buffers");
1871 	    }
1872 	    FREE(tbuf);
1873 	}
1874 	if (dh.ubufsz != 0) {
1875 	    if (P_write(fd, ubuf, dh.ubufsz) != dh.ubufsz) {
1876 		fatal("failed to dump UDP buffers");
1877 	    }
1878 	    FREE(ubuf);
1879 	}
1880 
1881 	FREE(du - nusers);
1882 	FREE(bufs - 2 * nusers);
1883     }
1884 
1885     return TRUE;
1886 }
1887 
1888 /*
1889  * NAME:	comm->restore()
1890  * DESCRIPTION:	restore users
1891  */
1892 bool comm_restore(int fd)
1893 {
1894     dump_header dh;
1895     duser *du;
1896     char *tbuf, *ubuf;
1897     int i;
1898     user *usr;
1899     connection *conn;
1900 
1901     tbuf = ubuf = (char *) NULL;
1902 
1903     /* read header */
1904     conf_dread(fd, (char *) &dh, dh_layout, 1);
1905     if (dh.nusers > maxusers) {
1906 	fatal("too many users");
1907     }
1908 
1909     if (dh.nusers != 0) {
1910 	/* read users and buffers */
1911 	du = ALLOC(duser, dh.nusers);
1912 	conf_dread(fd, (char *) du, du_layout, dh.nusers);
1913 	if (dh.tbufsz != 0) {
1914 	    tbuf = ALLOC(char, dh.tbufsz);
1915 	    if (P_read(fd, tbuf, dh.tbufsz) != dh.tbufsz) {
1916 		fatal("cannot read telnet buffer");
1917 	    }
1918 	}
1919 	if (dh.ubufsz != 0) {
1920 	    ubuf = ALLOC(char, dh.ubufsz);
1921 	    if (P_read(fd, ubuf, dh.ubufsz) != dh.ubufsz) {
1922 		fatal("cannot read UDP buffer");
1923 	    }
1924 	}
1925 
1926 	for (i = dh.nusers; i > 0; --i) {
1927 	    /* import connection */
1928 	    conn = conn_import(du->fd, du->port, du->at, du->npkts, du->ubufsz,
1929 			       ubuf, du->cflags, (du->flags & CF_TELNET) != 0);
1930 	    if (conn == (connection *) NULL) {
1931 		if (nusers == 0) {
1932 		    if (dh.ubufsz != 0) {
1933 			FREE(ubuf);
1934 		    }
1935 		    if (dh.tbufsz != 0) {
1936 			FREE(tbuf);
1937 		    }
1938 		    FREE(du);
1939 		    return FALSE;
1940 		}
1941 		fatal("cannot restore user");
1942 	    }
1943 	    ubuf += du->ubufsz;
1944 
1945 	    /* allocate user */
1946 	    usr = freeuser;
1947 	    freeuser = usr->next;
1948 	    if (lastuser != (user *) NULL) {
1949 		usr->prev = lastuser->prev;
1950 		usr->prev->next = usr;
1951 		usr->next = lastuser;
1952 		lastuser->prev = usr;
1953 	    } else {
1954 		usr->prev = usr;
1955 		usr->next = usr;
1956 		lastuser = usr;
1957 	    }
1958 	    nusers++;
1959 
1960 	    /* initialize user */
1961 	    usr->oindex = du->oindex;
1962 	    OBJ(usr->oindex)->etabi = usr - users;
1963 	    OBJ(usr->oindex)->flags |= O_USER;
1964 	    usr->flags = du->flags;
1965 	    if (usr->flags & CF_ODONE) {
1966 		odone++;
1967 	    }
1968 	    usr->state = du->state;
1969 	    usr->newlines = du->newlines;
1970 	    newlines += usr->newlines;
1971 	    usr->conn = conn;
1972 	    if (usr->flags & CF_TELNET) {
1973 		m_static();
1974 		usr->inbuf = ALLOC(char, INBUF_SIZE + 1);
1975 		*usr->inbuf++ = LF;	/* sentinel */
1976 		m_dynamic();
1977 	    } else {
1978 		usr->inbuf = (char *) NULL;
1979 	    }
1980 	    usr->extra = (array *) NULL;
1981 	    usr->outbuf = (string *) NULL;
1982 	    usr->inbufsz = du->tbufsz;
1983 	    if (usr->inbufsz != 0) {
1984 		memcpy(usr->inbuf, tbuf, usr->inbufsz);
1985 		tbuf += usr->inbufsz;
1986 	    }
1987 	    usr->osdone = du->osdone;
1988 
1989 	    du++;
1990 	}
1991 	if (dh.ubufsz != 0) {
1992 	    FREE(ubuf - dh.ubufsz);
1993 	}
1994 	if (dh.tbufsz != 0) {
1995 	    FREE(tbuf - dh.tbufsz);
1996 	}
1997 	FREE(du - dh.nusers);
1998     }
1999 
2000     return TRUE;
2001 }
2002