1 /******************************************************************************
2   Copyright (c) 1992, 1995, 1996 Xerox Corporation.  All rights reserved.
3   Portions of this code were written by Stephen White, aka ghond.
4   Use and copying of this software and preparation of derivative works based
5   upon this software are permitted.  Any distribution of this software or
6   derivative works must comply with all applicable United States export
7   control laws.  This software is made available AS IS, and Xerox Corporation
8   makes no warranty about the software, its performance or its conformity to
9   any specification.  Any person obtaining a copy of this software is requested
10   to send their name and post office or electronic mail address to:
11     Pavel Curtis
12     Xerox PARC
13     3333 Coyote Hill Rd.
14     Palo Alto, CA 94304
15     Pavel@Xerox.Com
16  *****************************************************************************/
17 
18 #include "my-ctype.h"
19 #include <errno.h>
20 #include "my-fcntl.h"
21 #include "my-ioctl.h"
22 #include "my-signal.h"
23 #include "my-stdio.h"
24 #include "my-stdlib.h"
25 #include "my-string.h"
26 #include "my-unistd.h"
27 
28 #include "config.h"
29 #include "exceptions.h"
30 #include "list.h"
31 #include "log.h"
32 #include "net_mplex.h"
33 #include "net_multi.h"
34 #include "net_proto.h"
35 #include "network.h"
36 #include "options.h"
37 #include "server.h"
38 #include "streams.h"
39 #include "structures.h"
40 #include "storage.h"
41 #include "timers.h"
42 #include "utils.h"
43 
44 static struct proto proto;
45 static int eol_length;		/* == strlen(proto.eol_out_string) */
46 
47 #ifdef EAGAIN
48 static int eagain = EAGAIN;
49 #else
50 static int eagain = -1;
51 #endif
52 
53 #ifdef EWOULDBLOCK
54 static int ewouldblock = EWOULDBLOCK;
55 #else
56 static int ewouldblock = -1;
57 #endif
58 
59 static int *pocket_descriptors = 0;	/* fds we keep around in case we need
60 					 * one and no others are left... */
61 
62 typedef struct text_block {
63     struct text_block *next;
64     int length;
65     char *buffer;
66     char *start;
67 } text_block;
68 
69 typedef struct nhandle {
70     struct nhandle *next, **prev;
71     server_handle shandle;
72     int rfd, wfd;
73     char *name;
74     Stream *input;
75     int last_input_was_CR;
76     int input_suspended;
77     text_block *output_head;
78     text_block **output_tail;
79     int output_length;
80     int output_lines_flushed;
81     int outbound, binary;
82 #if NETWORK_PROTOCOL == NP_TCP
83     int client_echo;
84 #endif
85 } nhandle;
86 
87 static nhandle *all_nhandles = 0;
88 
89 typedef struct nlistener {
90     struct nlistener *next, **prev;
91     server_listener slistener;
92     int fd;
93     const char *name;
94 } nlistener;
95 
96 static nlistener *all_nlisteners = 0;
97 
98 
99 typedef struct {
100     int fd;
101     network_fd_callback readable;
102     network_fd_callback writable;
103     void *data;
104 } fd_reg;
105 
106 static fd_reg *reg_fds = 0;
107 static int max_reg_fds = 0;
108 
109 void
network_register_fd(int fd,network_fd_callback readable,network_fd_callback writable,void * data)110 network_register_fd(int fd, network_fd_callback readable,
111 		    network_fd_callback writable, void *data)
112 {
113     int i;
114 
115     if (!reg_fds) {
116 	max_reg_fds = 5;
117 	reg_fds = mymalloc(max_reg_fds * sizeof(fd_reg), M_NETWORK);
118 	for (i = 0; i < max_reg_fds; i++)
119 	    reg_fds[i].fd = -1;
120     }
121     /* Find an empty slot */
122     for (i = 0; i < max_reg_fds; i++)
123 	if (reg_fds[i].fd == -1)
124 	    break;
125     if (i >= max_reg_fds) {	/* No free slots */
126 	int new_max = 2 * max_reg_fds;
127 	fd_reg *new = mymalloc(new_max * sizeof(fd_reg), M_NETWORK);
128 
129 	for (i = 0; i < new_max; i++)
130 	    if (i < max_reg_fds)
131 		new[i] = reg_fds[i];
132 	    else
133 		new[i].fd = -1;
134 
135 	myfree(reg_fds, M_NETWORK);
136 	i = max_reg_fds;	/* first free slot */
137 	max_reg_fds = new_max;
138 	reg_fds = new;
139     }
140     reg_fds[i].fd = fd;
141     reg_fds[i].readable = readable;
142     reg_fds[i].writable = writable;
143     reg_fds[i].data = data;
144 }
145 
146 void
network_unregister_fd(int fd)147 network_unregister_fd(int fd)
148 {
149     int i;
150 
151     for (i = 0; i < max_reg_fds; i++)
152 	if (reg_fds[i].fd == fd)
153 	    reg_fds[i].fd = -1;
154 }
155 
156 static void
add_registered_fds(void)157 add_registered_fds(void)
158 {
159     fd_reg *reg;
160 
161     for (reg = reg_fds; reg < reg_fds + max_reg_fds; reg++)
162 	if (reg->fd != -1) {
163 	    if (reg->readable)
164 		mplex_add_reader(reg->fd);
165 	    if (reg->writable)
166 		mplex_add_writer(reg->fd);
167 	}
168 }
169 
170 static void
check_registered_fds(void)171 check_registered_fds(void)
172 {
173     fd_reg *reg;
174 
175     for (reg = reg_fds; reg < reg_fds + max_reg_fds; reg++)
176 	if (reg->fd != -1) {
177 	    if (reg->readable && mplex_is_readable(reg->fd))
178 		(*reg->readable) (reg->fd, reg->data);
179 	    if (reg->writable && mplex_is_writable(reg->fd))
180 		(*reg->writable) (reg->fd, reg->data);
181 	}
182 }
183 
184 
185 static void
free_text_block(text_block * b)186 free_text_block(text_block * b)
187 {
188     myfree(b->buffer, M_NETWORK);
189     myfree(b, M_NETWORK);
190 }
191 
192 int
network_set_nonblocking(int fd)193 network_set_nonblocking(int fd)
194 {
195 #ifdef FIONBIO
196     /* Prefer this implementation, since the second one fails on some SysV
197      * platforms, including HP/UX.
198      */
199     int yes = 1;
200 
201     if (ioctl(fd, FIONBIO, &yes) < 0)
202 	return 0;
203     else
204 	return 1;
205 #else
206     int flags;
207 
208     if ((flags = fcntl(fd, F_GETFL, 0)) < 0
209 	|| fcntl(fd, F_SETFL, flags | NONBLOCK_FLAG) < 0)
210 	return 0;
211     else
212 	return 1;
213 #endif
214 }
215 
216 static int
push_output(nhandle * h)217 push_output(nhandle * h)
218 {
219     text_block *b;
220     int count;
221 
222     if (h->output_lines_flushed > 0) {
223 	char buf[100];
224 	int length;
225 
226 	sprintf(buf,
227 		"%s>> Network buffer overflow: %u line%s of output to you %s been lost <<%s",
228 		proto.eol_out_string,
229 		h->output_lines_flushed,
230 		h->output_lines_flushed == 1 ? "" : "s",
231 		h->output_lines_flushed == 1 ? "has" : "have",
232 		proto.eol_out_string);
233 	length = strlen(buf);
234 	count = write(h->wfd, buf, length);
235 	if (count == length)
236 	    h->output_lines_flushed = 0;
237 	else
238 	    return count >= 0 || errno == eagain || errno == ewouldblock;
239     }
240     while ((b = h->output_head) != 0) {
241 	count = write(h->wfd, b->start, b->length);
242 	if (count < 0)
243 	    return (errno == eagain || errno == ewouldblock);
244 	h->output_length -= count;
245 	if (count == b->length) {
246 	    h->output_head = b->next;
247 	    free_text_block(b);
248 	} else {
249 	    b->start += count;
250 	    b->length -= count;
251 	}
252     }
253     if (h->output_head == 0)
254 	h->output_tail = &(h->output_head);
255     return 1;
256 }
257 
258 static int
pull_input(nhandle * h)259 pull_input(nhandle * h)
260 {
261     Stream *s = h->input;
262     int count;
263     char buffer[1024];
264     char *ptr, *end;
265 
266     if ((count = read(h->rfd, buffer, sizeof(buffer))) > 0) {
267 	if (h->binary) {
268 	    stream_add_string(s, raw_bytes_to_binary(buffer, count));
269 	    server_receive_line(h->shandle, reset_stream(s));
270 	    h->last_input_was_CR = 0;
271 	} else {
272 	    for (ptr = buffer, end = buffer + count; ptr < end; ptr++) {
273 		unsigned char c = *ptr;
274 
275 		if (isgraph(c) || c == ' ' || c == '\t')
276 		    stream_add_char(s, c);
277 		else if (c == '\r' || (c == '\n' && !h->last_input_was_CR))
278 		    server_receive_line(h->shandle, reset_stream(s));
279 
280 		h->last_input_was_CR = (c == '\r');
281 	    }
282 	}
283 	return 1;
284     } else
285 	return (count == 0 && !proto.believe_eof)
286 	    || (count < 0 && (errno == eagain || errno == ewouldblock));
287 }
288 
289 static nhandle *
new_nhandle(int rfd,int wfd,const char * local_name,const char * remote_name,int outbound)290 new_nhandle(int rfd, int wfd, const char *local_name, const char *remote_name,
291 	    int outbound)
292 {
293     nhandle *h;
294     static Stream *s = 0;
295 
296     if (s == 0)
297 	s = new_stream(100);
298 
299     if (!network_set_nonblocking(rfd)
300 	|| (rfd != wfd && !network_set_nonblocking(wfd)))
301 	log_perror("Setting connection non-blocking");
302 
303     h = mymalloc(sizeof(nhandle), M_NETWORK);
304 
305     if (all_nhandles)
306 	all_nhandles->prev = &(h->next);
307     h->next = all_nhandles;
308     h->prev = &all_nhandles;
309     all_nhandles = h;
310 
311     h->rfd = rfd;
312     h->wfd = wfd;
313     h->input = new_stream(100);
314     h->last_input_was_CR = 0;
315     h->input_suspended = 0;
316     h->output_head = 0;
317     h->output_tail = &(h->output_head);
318     h->output_length = 0;
319     h->output_lines_flushed = 0;
320     h->outbound = outbound;
321     h->binary = 0;
322 #if NETWORK_PROTOCOL == NP_TCP
323     h->client_echo = 1;
324 #endif
325 
326     stream_printf(s, "%s %s %s",
327 		  local_name, outbound ? "to" : "from", remote_name);
328     h->name = str_dup(reset_stream(s));
329 
330     return h;
331 }
332 
333 static void
close_nhandle(nhandle * h)334 close_nhandle(nhandle * h)
335 {
336     text_block *b, *bb;
337 
338     (void) push_output(h);
339     *(h->prev) = h->next;
340     if (h->next)
341 	h->next->prev = h->prev;
342     b = h->output_head;
343     while (b) {
344 	bb = b->next;
345 	free_text_block(b);
346 	b = bb;
347     }
348     free_stream(h->input);
349     proto_close_connection(h->rfd, h->wfd);
350     free_str(h->name);
351     myfree(h, M_NETWORK);
352 }
353 
354 static void
close_nlistener(nlistener * l)355 close_nlistener(nlistener * l)
356 {
357     *(l->prev) = l->next;
358     if (l->next)
359 	l->next->prev = l->prev;
360     proto_close_listener(l->fd);
361     free_str(l->name);
362     myfree(l, M_NETWORK);
363 }
364 
365 static void
make_new_connection(server_listener sl,int rfd,int wfd,const char * local_name,const char * remote_name,int outbound)366 make_new_connection(server_listener sl, int rfd, int wfd,
367 		    const char *local_name, const char *remote_name,
368 		    int outbound)
369 {
370     nhandle *h;
371     network_handle nh;
372 
373     nh.ptr = h = new_nhandle(rfd, wfd, local_name, remote_name, outbound);
374     h->shandle = server_new_connection(sl, nh, outbound);
375 }
376 
377 static void
get_pocket_descriptors()378 get_pocket_descriptors()
379 {
380     int i;
381 
382     if (!pocket_descriptors)
383 	pocket_descriptors =
384 	    (int *) mymalloc(proto.pocket_size * sizeof(int), M_NETWORK);
385 
386     for (i = 0; i < proto.pocket_size; i++) {
387 	pocket_descriptors[i] = dup(0);
388 	if (!pocket_descriptors[i]) {
389 	    log_perror("Can't get a pocket descriptor");
390 	    panic("Need pocket descriptors to continue");
391 	}
392     }
393 }
394 
395 static void
accept_new_connection(nlistener * l)396 accept_new_connection(nlistener * l)
397 {
398     network_handle nh;
399     nhandle *h;
400     int rfd, wfd, i;
401     const char *host_name;
402 
403     switch (proto_accept_connection(l->fd, &rfd, &wfd, &host_name)) {
404     case PA_OKAY:
405 	make_new_connection(l->slistener, rfd, wfd, l->name, host_name, 0);
406 	break;
407 
408     case PA_FULL:
409 	for (i = 0; i < proto.pocket_size; i++)
410 	    close(pocket_descriptors[i]);
411 	if (proto_accept_connection(l->fd, &rfd, &wfd, &host_name) != PA_OKAY)
412 	    errlog("Can't accept connection even by emptying pockets!\n");
413 	else {
414 	    nh.ptr = h = new_nhandle(rfd, wfd, l->name, host_name, 0);
415 	    server_refuse_connection(l->slistener, nh);
416 	    close_nhandle(h);
417 	}
418 	get_pocket_descriptors();
419 	break;
420 
421     case PA_OTHER:
422 	/* Do nothing.  The protocol implementation has already logged it. */
423 	break;
424     }
425 }
426 
427 static int
enqueue_output(network_handle nh,const char * line,int line_length,int add_eol,int flush_ok)428 enqueue_output(network_handle nh, const char *line, int line_length,
429 	       int add_eol, int flush_ok)
430 {
431     nhandle *h = nh.ptr;
432     int length = line_length + (add_eol ? eol_length : 0);
433     char *buffer;
434     text_block *block;
435 
436     if (h->output_length != 0
437 	&& h->output_length + length > MAX_QUEUED_OUTPUT) {	/* must flush... */
438 	int to_flush;
439 	text_block *b;
440 
441 	(void) push_output(h);
442 	to_flush = h->output_length + length - MAX_QUEUED_OUTPUT;
443 	if (to_flush > 0 && !flush_ok)
444 	    return 0;
445 	while (to_flush > 0 && (b = h->output_head)) {
446 	    h->output_length -= b->length;
447 	    to_flush -= b->length;
448 	    h->output_lines_flushed++;
449 	    h->output_head = b->next;
450 	    free_text_block(b);
451 	}
452 	if (h->output_head == 0)
453 	    h->output_tail = &(h->output_head);
454     }
455     buffer = (char *) mymalloc(length * sizeof(char), M_NETWORK);
456     block = (text_block *) mymalloc(sizeof(text_block), M_NETWORK);
457     memcpy(buffer, line, line_length);
458     if (add_eol)
459 	memcpy(buffer + line_length, proto.eol_out_string, eol_length);
460     block->buffer = block->start = buffer;
461     block->length = length;
462     block->next = 0;
463     *(h->output_tail) = block;
464     h->output_tail = &(block->next);
465     h->output_length += length;
466 
467     return 1;
468 }
469 
470 
471 /*************************
472  * External entry points *
473  *************************/
474 
475 const char *
network_protocol_name(void)476 network_protocol_name(void)
477 {
478     return proto_name();
479 }
480 
481 const char *
network_usage_string(void)482 network_usage_string(void)
483 {
484     return proto_usage_string();
485 }
486 
487 int
network_initialize(int argc,char ** argv,Var * desc)488 network_initialize(int argc, char **argv, Var * desc)
489 {
490     if (!proto_initialize(&proto, desc, argc, argv))
491 	return 0;
492 
493     eol_length = strlen(proto.eol_out_string);
494     get_pocket_descriptors();
495 
496     /* we don't care about SIGPIPE, we notice it in mplex_wait() and write() */
497     signal(SIGPIPE, SIG_IGN);
498 
499     return 1;
500 }
501 
502 enum error
network_make_listener(server_listener sl,Var desc,network_listener * nl,Var * canon,const char ** name)503 network_make_listener(server_listener sl, Var desc,
504 		   network_listener * nl, Var * canon, const char **name)
505 {
506     int fd;
507     enum error e = proto_make_listener(desc, &fd, canon, name);
508     nlistener *l;
509 
510     if (e == E_NONE) {
511 	nl->ptr = l = mymalloc(sizeof(nlistener), M_NETWORK);
512 	l->fd = fd;
513 	l->slistener = sl;
514 	l->name = str_dup(*name);
515 	if (all_nlisteners)
516 	    all_nlisteners->prev = &(l->next);
517 	l->next = all_nlisteners;
518 	l->prev = &all_nlisteners;
519 	all_nlisteners = l;
520     }
521     return e;
522 }
523 
524 int
network_listen(network_listener nl)525 network_listen(network_listener nl)
526 {
527     nlistener *l = nl.ptr;
528 
529     return proto_listen(l->fd);
530 }
531 
532 int
network_send_line(network_handle nh,const char * line,int flush_ok)533 network_send_line(network_handle nh, const char *line, int flush_ok)
534 {
535     return enqueue_output(nh, line, strlen(line), 1, flush_ok);
536 }
537 
538 int
network_send_bytes(network_handle nh,const char * buffer,int buflen,int flush_ok)539 network_send_bytes(network_handle nh, const char *buffer, int buflen,
540 		   int flush_ok)
541 {
542     return enqueue_output(nh, buffer, buflen, 0, flush_ok);
543 }
544 
545 int
network_buffered_output_length(network_handle nh)546 network_buffered_output_length(network_handle nh)
547 {
548     nhandle *h = nh.ptr;
549 
550     return h->output_length;
551 }
552 
553 void
network_suspend_input(network_handle nh)554 network_suspend_input(network_handle nh)
555 {
556     nhandle *h = nh.ptr;
557 
558     h->input_suspended = 1;
559 }
560 
561 void
network_resume_input(network_handle nh)562 network_resume_input(network_handle nh)
563 {
564     nhandle *h = nh.ptr;
565 
566     h->input_suspended = 0;
567 }
568 
569 int
network_process_io(int timeout)570 network_process_io(int timeout)
571 {
572     nhandle *h, *hnext;
573     nlistener *l;
574 
575     mplex_clear();
576     for (l = all_nlisteners; l; l = l->next)
577 	mplex_add_reader(l->fd);
578     for (h = all_nhandles; h; h = h->next) {
579 	if (!h->input_suspended)
580 	    mplex_add_reader(h->rfd);
581 	if (h->output_head)
582 	    mplex_add_writer(h->wfd);
583     }
584     add_registered_fds();
585 
586     if (mplex_wait(timeout))
587 	return 0;
588     else {
589 	for (l = all_nlisteners; l; l = l->next)
590 	    if (mplex_is_readable(l->fd))
591 		accept_new_connection(l);
592 	for (h = all_nhandles; h; h = hnext) {
593 	    hnext = h->next;
594 	    if ((mplex_is_readable(h->rfd) && !pull_input(h))
595 		|| (mplex_is_writable(h->wfd) && !push_output(h))) {
596 		server_close(h->shandle);
597 		close_nhandle(h);
598 	    }
599 	}
600 	check_registered_fds();
601 	return 1;
602     }
603 }
604 
605 const char *
network_connection_name(network_handle nh)606 network_connection_name(network_handle nh)
607 {
608     nhandle *h = (nhandle *) nh.ptr;
609 
610     return h->name;
611 }
612 
613 void
network_set_connection_binary(network_handle nh,int do_binary)614 network_set_connection_binary(network_handle nh, int do_binary)
615 {
616     nhandle *h = nh.ptr;
617 
618     h->binary = do_binary;
619 }
620 
621 Var
network_connection_options(network_handle nh,Var list)622 network_connection_options(network_handle nh, Var list)
623 {
624 #if NETWORK_PROTOCOL == NP_TCP
625     nhandle *h = nh.ptr;
626     Var pair;
627 
628     pair = new_list(2);
629     pair.v.list[1].type = TYPE_STR;
630     pair.v.list[1].v.str = str_dup("client-echo");
631     pair.v.list[2].type = TYPE_INT;
632     pair.v.list[2].v.num = h->client_echo;
633     list = listappend(list, pair);
634 #endif
635 
636     return list;
637 }
638 
639 int
network_connection_option(network_handle nh,const char * option,Var * value)640 network_connection_option(network_handle nh, const char *option, Var * value)
641 {
642 #if NETWORK_PROTOCOL == NP_TCP
643     nhandle *h = nh.ptr;
644 
645     if (!mystrcasecmp(option, "client-echo")) {
646 	value->type = TYPE_INT;
647 	value->v.num = h->client_echo;
648 	return 1;
649     }
650 #endif
651 
652     return 0;
653 }
654 
655 int
network_set_connection_option(network_handle nh,const char * option,Var value)656 network_set_connection_option(network_handle nh, const char *option, Var value)
657 {
658 #if NETWORK_PROTOCOL == NP_TCP
659     nhandle *h = nh.ptr;
660 
661     /* These values taken from RFC 854 and RFC 857. */
662 #define TN_IAC	255		/* Interpret As Command */
663 #define TN_WILL	251
664 #define TN_WONT	252
665 #define TN_ECHO	1
666 
667     {
668 	static char telnet_cmd[4] =
669 	{TN_IAC, 0, TN_ECHO, 0};
670 
671 	if (!mystrcasecmp(option, "client-echo")) {
672 	    h->client_echo = is_true(value);
673 	    if (h->client_echo)
674 		telnet_cmd[1] = TN_WONT;
675 	    else
676 		telnet_cmd[1] = TN_WILL;
677 	    enqueue_output(nh, telnet_cmd, 3, 0, 1);
678 	    return 1;
679 	}
680     }
681 #endif
682 
683     return 0;
684 }
685 
686 #ifdef OUTBOUND_NETWORK
687 
688 enum error
network_open_connection(Var arglist)689 network_open_connection(Var arglist)
690 {
691     int rfd, wfd;
692     const char *local_name, *remote_name;
693     enum error e;
694 
695     e = proto_open_connection(arglist, &rfd, &wfd, &local_name, &remote_name);
696     if (e == E_NONE)
697 	make_new_connection(null_server_listener, rfd, wfd,
698 			    local_name, remote_name, 1);
699 
700     return e;
701 }
702 #endif
703 
704 void
network_close(network_handle h)705 network_close(network_handle h)
706 {
707     close_nhandle(h.ptr);
708 }
709 
710 void
network_close_listener(network_listener nl)711 network_close_listener(network_listener nl)
712 {
713     close_nlistener(nl.ptr);
714 }
715 
716 void
network_shutdown(void)717 network_shutdown(void)
718 {
719     while (all_nhandles)
720 	close_nhandle(all_nhandles);
721     while (all_nlisteners)
722 	close_nlistener(all_nlisteners);
723 }
724 
725 char rcsid_net_multi[] = "$Id: net_multi.c,v 1.3 1998/12/14 13:18:31 nop Exp $";
726 
727 /*
728  * $Log: net_multi.c,v $
729  * Revision 1.3  1998/12/14 13:18:31  nop
730  * Merge UNSAFE_OPTS (ref fixups); fix Log tag placement to fit CVS whims
731  *
732  * Revision 1.2  1997/03/03 04:19:05  nop
733  * GNU Indent normalization
734  *
735  * Revision 1.1.1.1  1997/03/03 03:45:02  nop
736  * LambdaMOO 1.8.0p5
737  *
738  * Revision 2.6  1996/05/12  21:29:09  pavel
739  * Fixed mis-initialization of "client-echo" option.  Release 1.8.0p5.
740  *
741  * Revision 2.5  1996/03/10  01:24:18  pavel
742  * Added support for `connection_option()'.  Fixed `unused variable'
743  * warnings for non-TCP configurations.  Release 1.8.0.
744  *
745  * Revision 2.4  1996/02/08  06:38:05  pavel
746  * Renamed err/logf() to errlog/oklog().  Added memory of client_echo setting
747  * for connection_options().  Added network_set_connection_binary() and
748  * network_connection_options().  Updated copyright notice for 1996.
749  * Release 1.8.0beta1.
750  *
751  * Revision 2.3  1996/01/11  07:38:58  pavel
752  * Added support for binary I/O.  Added network_buffered_output_length().
753  * Removed a few more `unsigned' declarations.  Release 1.8.0alpha5.
754  *
755  * Revision 2.2  1995/12/31  03:24:08  pavel
756  * Moved server-full handling to server.c.  Added support for multiple
757  * listening points.  Release 1.8.0alpha4.
758  *
759  * Revision 2.1  1995/12/28  00:36:05  pavel
760  * Changed input-side EOL handling to include CR, LR, and CRLF.
761  * Release 1.8.0alpha3.
762  *
763  * Revision 2.0  1995/11/30  04:45:36  pavel
764  * New baseline version, corresponding to release 1.8.0alpha1.
765  *
766  * Revision 1.16  1993/08/12  21:10:13  pavel
767  * Fix long-standing denial-of-service attack vulnerability due to a connection
768  * sending an essentially infinite stream of input to the server.
769  *
770  * Revision 1.15  1993/08/11  03:11:17  pavel
771  * -- Fixed a syntax error in the new support for outbound connections.
772  * -- Changed some bogus %d's to %u's in calls to *scanf() and *printf(),
773  *    guided by warnings from GCC...
774  *
775  * Revision 1.14  1993/08/04  02:21:48  pavel
776  * -- Added support for distinguishing between inbound and outbound
777  *    connections.
778  * -- Added support for better logging of outbound connections.
779  * -- Added check to connection-timeout code to exempt outbound connections.
780  *
781  * Revision 1.13  1993/08/04  01:33:04  pavel
782  * -- Added support for the BSD-style ioctl(fd, FIONBIO, ...) non-blocking I/O
783  *    protocol.  The SysV/POSIX-style fcntl(fd, F_GETFL, ...) doesn't work on
784  *    BSD sockets on some hybrid systems, including HP/UX.
785  * -- Vastly improved the clarity of the `output flushed' message users see
786  *    when their output-side network buffer overflows.
787  * -- Now accepts tab as a normal input character mapping to itself, necessary
788  *    for communicating with Gopher from the MOO.
789  * -- Fixed a =/== bug in pull_input().
790  * -- Added a log message printed when the server has to refuse a connection
791  *    because there aren't any file descriptors left.
792  * -- Added support for the new network_listen() protocol.
793  *
794  * Revision 1.12  1992/10/23  23:03:47  pavel
795  * Added copyright notice.
796  *
797  * Revision 1.11  1992/10/21  03:02:35  pavel
798  * Converted to use new automatic configuration system.
799  *
800  * Revision 1.10  1992/10/17  20:44:14  pavel
801  * Changed to use NONBLOCK_FLAG instead of O_NDELAY to allow of using
802  * POSIX-style non-blocking on systems where it is available.
803  *
804  * Revision 1.9  1992/10/06  18:16:36  pavel
805  * Moved non-blocking code to here from individual protocol implementations.
806  *
807  * Revision 1.8  1992/09/30  06:18:08  pavel
808  * Fixed small bug in the handling of the case where even emptying our pockets
809  * of extra file descriptors doesn't make it possible to accept a connection.
810  *
811  * Revision 1.7  1992/09/26  02:22:07  pavel
812  * Added support for printing the network protocol name on server start-up.
813  *
814  * Revision 1.6  1992/09/23  17:17:36  pavel
815  * Stripped out all BSD-specific code, instead relying on the net_proto.h
816  * and net_mplex.h interfaces.
817  *
818  * Revision 1.5  1992/09/11  21:21:54  pavel
819  * Tracked change to network.h.
820  *
821  * Revision 1.4  1992/09/04  22:41:47  pavel
822  * Fixed some picky ANSI C problems with (const char *)'s.
823  *
824  * Revision 1.3  1992/08/10  17:22:45  pjames
825  * Updated #includes.
826  *
827  * Revision 1.2  1992/07/20  23:56:16  pavel
828  * Added rcsid_<filename-root> declaration to hold the RCS ident. string.
829  *
830  * Revision 1.1  1992/07/20  23:23:12  pavel
831  * Initial RCS-controlled version.
832  */
833