xref: /freebsd/contrib/ntp/libntp/socket.c (revision 42249ef2)
1 /*
2  * socket.c - low-level socket operations
3  */
4 
5 #ifdef HAVE_CONFIG_H
6 # include <config.h>
7 #endif
8 
9 #include <stdio.h>
10 
11 #include "ntp.h"
12 #include "ntp_io.h"
13 #include "ntp_net.h"
14 #include "ntp_debug.h"
15 
16 /*
17  * Windows C runtime ioctl() can't deal properly with sockets,
18  * map to ioctlsocket for this source file.
19  */
20 #ifdef SYS_WINNT
21 #define ioctl(fd, opt, val)  ioctlsocket(fd, opt, (u_long *)(val))
22 #endif
23 
24 /*
25  * on Unix systems the stdio library typically
26  * makes use of file descriptors in the lower
27  * integer range.  stdio usually will make use
28  * of the file descriptors in the range of
29  * [0..FOPEN_MAX)
30  * in order to keep this range clean, for socket
31  * file descriptors we attempt to move them above
32  * FOPEN_MAX. This is not as easy as it sounds as
33  * FOPEN_MAX changes from implementation to implementation
34  * and may exceed to current file decriptor limits.
35  * We are using following strategy:
36  * - keep a current socket fd boundary initialized with
37  *   max(0, min(GETDTABLESIZE() - FD_CHUNK, FOPEN_MAX))
38  * - attempt to move the descriptor to the boundary or
39  *   above.
40  *   - if that fails and boundary > 0 set boundary
41  *     to min(0, socket_fd_boundary - FD_CHUNK)
42  *     -> retry
43  *     if failure and boundary == 0 return old fd
44  *   - on success close old fd return new fd
45  *
46  * effects:
47  *   - fds will be moved above the socket fd boundary
48  *     if at all possible.
49  *   - the socket boundary will be reduced until
50  *     allocation is possible or 0 is reached - at this
51  *     point the algrithm will be disabled
52  */
53 SOCKET
54 move_fd(
55 	SOCKET fd
56 	)
57 {
58 #if !defined(SYS_WINNT) && defined(F_DUPFD)
59 #ifndef FD_CHUNK
60 #define FD_CHUNK	10
61 #endif
62 #ifndef FOPEN_MAX
63 #define FOPEN_MAX	20
64 #endif
65 /*
66  * number of fds we would like to have for
67  * stdio FILE* available.
68  * we can pick a "low" number as our use of
69  * FILE* is limited to log files and temporarily
70  * to data and config files. Except for log files
71  * we don't keep the other FILE* open beyond the
72  * scope of the function that opened it.
73  */
74 #ifndef FD_PREFERRED_SOCKBOUNDARY
75 #define FD_PREFERRED_SOCKBOUNDARY 48
76 #endif
77 
78 	static SOCKET socket_boundary = -1;
79 	SOCKET newfd;
80 
81 	REQUIRE((int)fd >= 0);
82 
83 	/*
84 	 * check whether boundary has be set up
85 	 * already
86 	 */
87 	if (socket_boundary == -1) {
88 		socket_boundary = max(0, min(GETDTABLESIZE() - FD_CHUNK,
89 					     min(FOPEN_MAX, FD_PREFERRED_SOCKBOUNDARY)));
90 		TRACE(1, ("move_fd: estimated max descriptors: %d, "
91 			  "initial socket boundary: %d\n",
92 			  GETDTABLESIZE(), socket_boundary));
93 	}
94 
95 	/*
96 	 * Leave a space for stdio to work in. potentially moving the
97 	 * socket_boundary lower until allocation succeeds.
98 	 */
99 	do {
100 		if (fd >= 0 && fd < socket_boundary) {
101 			/* inside reserved range: attempt to move fd */
102 			newfd = fcntl(fd, F_DUPFD, socket_boundary);
103 
104 			if (newfd != -1) {
105 				/* success: drop the old one - return the new one */
106 				close(fd);
107 				return newfd;
108 			}
109 		} else {
110 			/* outside reserved range: no work - return the original one */
111 			return fd;
112 		}
113 		socket_boundary = max(0, socket_boundary - FD_CHUNK);
114 		TRACE(1, ("move_fd: selecting new socket boundary: %d\n",
115 			  socket_boundary));
116 	} while (socket_boundary > 0);
117 #else
118 	ENSURE((int)fd >= 0);
119 #endif /* !defined(SYS_WINNT) && defined(F_DUPFD) */
120 	return fd;
121 }
122 
123 
124 /*
125  * make_socket_nonblocking() - set up descriptor to be non blocking
126  */
127 void
128 make_socket_nonblocking(
129 	SOCKET fd
130 	)
131 {
132 	/*
133 	 * set non-blocking,
134 	 */
135 
136 #ifdef USE_FIONBIO
137 	/* in vxWorks we use FIONBIO, but the others are defined for old
138 	 * systems, so all hell breaks loose if we leave them defined
139 	 */
140 #undef O_NONBLOCK
141 #undef FNDELAY
142 #undef O_NDELAY
143 #endif
144 
145 #if defined(O_NONBLOCK) /* POSIX */
146 	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
147 		msyslog(LOG_ERR,
148 			"fcntl(O_NONBLOCK) fails on fd #%d: %m", fd);
149 		exit(1);
150 	}
151 #elif defined(FNDELAY)
152 	if (fcntl(fd, F_SETFL, FNDELAY) < 0) {
153 		msyslog(LOG_ERR, "fcntl(FNDELAY) fails on fd #%d: %m",
154 			fd);
155 		exit(1);
156 	}
157 #elif defined(O_NDELAY) /* generally the same as FNDELAY */
158 	if (fcntl(fd, F_SETFL, O_NDELAY) < 0) {
159 		msyslog(LOG_ERR, "fcntl(O_NDELAY) fails on fd #%d: %m",
160 			fd);
161 		exit(1);
162 	}
163 #elif defined(FIONBIO)
164 	{
165 		int on = 1;
166 
167 		if (ioctl(fd, FIONBIO, &on) < 0) {
168 			msyslog(LOG_ERR,
169 				"ioctl(FIONBIO) fails on fd #%d: %m",
170 				fd);
171 			exit(1);
172 		}
173 	}
174 #elif defined(FIOSNBIO)
175 	if (ioctl(fd, FIOSNBIO, &on) < 0) {
176 		msyslog(LOG_ERR,
177 			"ioctl(FIOSNBIO) fails on fd #%d: %m", fd);
178 		exit(1);
179 	}
180 #else
181 # include "Bletch: Need non-blocking I/O!"
182 #endif
183 }
184 
185 #if 0
186 
187 /* The following subroutines should probably be moved here */
188 
189 static SOCKET
190 open_socket(
191 	sockaddr_u *	addr,
192 	int		bcast,
193 	int		turn_off_reuse,
194 	endpt *		interf
195 	)
196 void
197 sendpkt(
198 	sockaddr_u *		dest,
199 	struct interface *	ep,
200 	int			ttl,
201 	struct pkt *		pkt,
202 	int			len
203 	)
204 
205 static inline int
206 read_refclock_packet(SOCKET fd, struct refclockio *rp, l_fp ts)
207 
208 static inline int
209 read_network_packet(
210 	SOCKET			fd,
211 	struct interface *	itf,
212 	l_fp			ts
213 	)
214 
215 void
216 kill_asyncio(int startfd)
217 
218 #endif /* 0 */
219