1 /*	$NetBSD: sane_accept.c,v 1.1.1.1 2009/06/23 10:09:00 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	sane_accept 3
6 /* SUMMARY
7 /*	sanitize accept() error returns
8 /* SYNOPSIS
9 /*	#include <sane_accept.h>
10 /*
11 /*	int	sane_accept(sock, buf, len)
12 /*	int	sock;
13 /*	struct sockaddr	*buf;
14 /*	SOCKADDR_SIZE *len;
15 /* DESCRIPTION
16 /*	sane_accept() implements the accept(2) socket call, and maps
17 /*	known harmless error results to EAGAIN.
18 /*
19 /*	If the buf and len arguments are not null, then additional
20 /*	workarounds may be enabled that depend on the socket type.
21 /* BUGS
22 /*	Bizarre systems may have other harmless error results. Such
23 /*	systems encourage programmers to ignore error results, and
24 /*	penalize programmers who code defensively.
25 /* LICENSE
26 /* .ad
27 /* .fi
28 /*	The Secure Mailer license must be distributed with this software.
29 /* AUTHOR(S)
30 /*	Wietse Venema
31 /*	IBM T.J. Watson Research
32 /*	P.O. Box 704
33 /*	Yorktown Heights, NY 10598, USA
34 /*--*/
35 
36 /* System library. */
37 
38 #include "sys_defs.h"
39 #include <sys/socket.h>
40 #include <errno.h>
41 
42 /* Utility library. */
43 
44 #include "msg.h"
45 #include "sane_accept.h"
46 
47 /* sane_accept - sanitize accept() error returns */
48 
49 int     sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len)
50 {
51     static int accept_ok_errors[] = {
52 	EAGAIN,
53 	ECONNREFUSED,
54 	ECONNRESET,
55 	EHOSTDOWN,
56 	EHOSTUNREACH,
57 	EINTR,
58 	ENETDOWN,
59 	ENETUNREACH,
60 	ENOTCONN,
61 	EWOULDBLOCK,
62 	ENOBUFS,			/* HPUX11 */
63 	ECONNABORTED,
64 #ifdef EPROTO
65 	EPROTO,				/* SunOS 5.5.1 */
66 #endif
67 	0,
68     };
69     int     count;
70     int     err;
71     int     fd;
72 
73     /*
74      * XXX Solaris 2.4 accept() returns EPIPE when a UNIX-domain client has
75      * disconnected in the mean time. From then on, UNIX-domain sockets are
76      * hosed beyond recovery. There is no point treating this as a beneficial
77      * error result because the program would go into a tight loop.
78      *
79      * XXX Solaris 2.5.1 accept() returns EPROTO when a TCP client has
80      * disconnected in the mean time. Since there is no connection, it is
81      * safe to map the error code onto EAGAIN.
82      *
83      * XXX LINUX < 2.1 accept() wakes up before the three-way handshake is
84      * complete, so it can fail with ECONNRESET and other "false alarm"
85      * indications.
86      *
87      * XXX FreeBSD 4.2-STABLE accept() returns ECONNABORTED when a UNIX-domain
88      * client has disconnected in the mean time. The data that was sent with
89      * connect() write() close() is lost, even though the write() and close()
90      * reported successful completion. This was fixed shortly before FreeBSD
91      * 4.3.
92      *
93      * XXX HP-UX 11 returns ENOBUFS when the client has disconnected in the mean
94      * time.
95      */
96     if ((fd = accept(sock, sa, len)) < 0) {
97 	for (count = 0; (err = accept_ok_errors[count]) != 0; count++) {
98 	    if (errno == err) {
99 		errno = EAGAIN;
100 		break;
101 	    }
102 	}
103     }
104 
105     /*
106      * XXX Solaris select() produces false read events, so that read() blocks
107      * forever on a blocking socket, and fails with EAGAIN on a non-blocking
108      * socket. Turning on keepalives will fix a blocking socket provided that
109      * the kernel's keepalive timer expires before the Postfix watchdog
110      * timer.
111      *
112      * XXX Work around NAT induced damage by sending a keepalive before an idle
113      * connection is expired. This requires that the kernel keepalive timer
114      * is set to a short time, like 100s.
115      */
116     else if (sa && (sa->sa_family == AF_INET
117 #ifdef HAS_IPV6
118 		    || sa->sa_family == AF_INET6
119 #endif
120 		    )) {
121 	int     on = 1;
122 
123 	(void) setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
124 			  (char *) &on, sizeof(on));
125     }
126     return (fd);
127 }
128