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