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