Home | History | Annotate | Line # | Download | only in util
      1 /*	$NetBSD: sane_accept.c,v 1.2 2017/02/14 01:16:49 christos 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 			  (void *) &on, sizeof(on));
    125     }
    126     return (fd);
    127 }
    128