1 #include "bsdtar_platform.h"
2 
3 #include <sys/types.h>
4 #include <sys/socket.h>
5 
6 #include <netinet/in.h>
7 #include <netinet/tcp.h>
8 
9 #include <errno.h>
10 
11 #include "warnp.h"
12 
13 #include "tsnetwork_internal.h"
14 
15 /**
16  * XXX Portability
17  * XXX These functions serve two purposes:
18  * XXX 1. To avoid wasting bandwidth, by ensuring that multiple small writes
19  * XXX over a socket are aggregated into a single TCP/IP packet.
20  * XXX 2. To avoid severe performance issues which would otherwise result
21  * XXX from nagling, by allowing data to be "pushed" out once there are no
22  * XXX more writes queued.
23  * XXX
24  * XXX POSIX defines TCP_NODELAY for purpose #2, although it does not require
25  * XXX that implementations obey it; BSD and Linux respectively define
26  * XXX TCP_NOPUSH and TCP_CORK for purpose #1.  On OS X, TCP_NOPUSH is
27  * XXX defined, but seems to be broken; we use autoconf to detect OS X and
28  * XXX define BROKEN_TCP_NOPUSH.  On Cygwin, TCP_NOPUSH is defined, but
29  * XXX using it produces a ENOPROTOOPT error; we define BROKEN_TCP_NOPUSH
30  * XXX in this case, too.  On Minix, TCP_NODELAY fails with ENOSYS; since
31  * XXX corking occurs for performance reasons only, we ignore this errno.
32  */
33 
34 /* Macro to simplify setting options. */
35 #define setopt(fd, opt, value, err0) do {				\
36 	int val;							\
37 									\
38 	val = value;							\
39 	if (setsockopt(fd, IPPROTO_TCP, opt, &val, sizeof(int))) {	\
40 		if ((errno != ETIMEDOUT) &&				\
41 		    (errno != ECONNRESET) &&				\
42 		    (errno != ENOSYS)) {				\
43 			warnp("setsockopt(%s, %d)", #opt, val);		\
44 			goto err0;					\
45 		}							\
46 	}								\
47 } while (0);
48 
49 /**
50  * network_cork(fd):
51  * Clear the TCP_NODELAY socket option, and set TCP_CORK or TCP_NOPUSH if
52  * either is defined.
53  */
54 int
network_cork(int fd)55 network_cork(int fd)
56 {
57 
58 	/* Clear TCP_NODELAY. */
59 	setopt(fd, TCP_NODELAY, 0, err0);
60 
61 	/* Set TCP_CORK or TCP_NOPUSH as appropriate. */
62 #ifdef TCP_CORK
63 	setopt(fd, TCP_CORK, 1, err0);
64 #else
65 #ifdef TCP_NOPUSH
66 #ifndef BROKEN_TCP_NOPUSH
67 	setopt(fd, TCP_NOPUSH, 1, err0);
68 #endif
69 #endif
70 #endif
71 
72 	/* Success! */
73 	return (0);
74 
75 err0:
76 	/* Failure! */
77 	return (-1);
78 }
79 
80 /**
81  * network_uncork(fd):
82  * Set the TCP_NODELAY socket option, and clear TCP_CORK or TCP_NOPUSH if
83  * either is defined.
84  */
85 int
network_uncork(int fd)86 network_uncork(int fd)
87 {
88 
89 	/* Clear TCP_CORK or TCP_NOPUSH as appropriate. */
90 #ifdef TCP_CORK
91 	setopt(fd, TCP_CORK, 0, err0);
92 #else
93 #ifdef TCP_NOPUSH
94 #ifndef BROKEN_TCP_NOPUSH
95 	setopt(fd, TCP_NOPUSH, 0, err0);
96 #endif
97 #endif
98 #endif
99 
100 	/* Set TCP_NODELAY. */
101 	/*
102 	 * For compatibility with Linux 2.4, this must be done after we
103 	 * clear TCP_CORK; otherwise it will throw an EINVAL back at us.
104 	 */
105 	setopt(fd, TCP_NODELAY, 1, err0);
106 
107 	/* Success! */
108 	return (0);
109 
110 err0:
111 	/* Failure! */
112 	return (-1);
113 }
114