1/*	$NetBSD: socket.c,v 1.6 2020/05/25 20:47:24 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 */
55SOCKET
56move_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 */
129void
130make_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
191static SOCKET
192open_socket(
193	sockaddr_u *	addr,
194	int		bcast,
195	int		turn_off_reuse,
196	endpt *		interf
197	)
198void
199sendpkt(
200	sockaddr_u *		dest,
201	struct interface *	ep,
202	int			ttl,
203	struct pkt *		pkt,
204	int			len
205	)
206
207static inline int
208read_refclock_packet(SOCKET fd, struct refclockio *rp, l_fp ts)
209
210static inline int
211read_network_packet(
212	SOCKET			fd,
213	struct interface *	itf,
214	l_fp			ts
215	)
216
217void
218kill_asyncio(int startfd)
219
220#endif /* 0 */
221