xref: /netbsd-src/external/bsd/ntp/dist/libntp/socket.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: socket.c,v 1.7 2024/08/18 20:47:13 christos Exp $	*/
28585484eSchristos 
38585484eSchristos /*
48585484eSchristos  * socket.c - low-level socket operations
58585484eSchristos  */
68585484eSchristos 
78585484eSchristos #ifdef HAVE_CONFIG_H
88585484eSchristos # include <config.h>
98585484eSchristos #endif
108585484eSchristos 
118585484eSchristos #include <stdio.h>
128585484eSchristos 
138585484eSchristos #include "ntp.h"
148585484eSchristos #include "ntp_io.h"
158585484eSchristos #include "ntp_net.h"
168585484eSchristos #include "ntp_debug.h"
178585484eSchristos 
188585484eSchristos /*
198585484eSchristos  * Windows C runtime ioctl() can't deal properly with sockets,
208585484eSchristos  * map to ioctlsocket for this source file.
218585484eSchristos  */
228585484eSchristos #ifdef SYS_WINNT
238585484eSchristos #define ioctl(fd, opt, val)  ioctlsocket(fd, opt, (u_long *)(val))
248585484eSchristos #endif
258585484eSchristos 
268585484eSchristos /*
278585484eSchristos  * on Unix systems the stdio library typically
288585484eSchristos  * makes use of file descriptors in the lower
298585484eSchristos  * integer range.  stdio usually will make use
308585484eSchristos  * of the file descriptors in the range of
318585484eSchristos  * [0..FOPEN_MAX)
328585484eSchristos  * in order to keep this range clean, for socket
338585484eSchristos  * file descriptors we attempt to move them above
348585484eSchristos  * FOPEN_MAX. This is not as easy as it sounds as
358585484eSchristos  * FOPEN_MAX changes from implementation to implementation
368585484eSchristos  * and may exceed to current file decriptor limits.
378585484eSchristos  * We are using following strategy:
388585484eSchristos  * - keep a current socket fd boundary initialized with
398585484eSchristos  *   max(0, min(GETDTABLESIZE() - FD_CHUNK, FOPEN_MAX))
408585484eSchristos  * - attempt to move the descriptor to the boundary or
418585484eSchristos  *   above.
428585484eSchristos  *   - if that fails and boundary > 0 set boundary
438585484eSchristos  *     to min(0, socket_fd_boundary - FD_CHUNK)
448585484eSchristos  *     -> retry
458585484eSchristos  *     if failure and boundary == 0 return old fd
468585484eSchristos  *   - on success close old fd return new fd
478585484eSchristos  *
488585484eSchristos  * effects:
498585484eSchristos  *   - fds will be moved above the socket fd boundary
508585484eSchristos  *     if at all possible.
518585484eSchristos  *   - the socket boundary will be reduced until
528585484eSchristos  *     allocation is possible or 0 is reached - at this
538585484eSchristos  *     point the algrithm will be disabled
548585484eSchristos  */
558585484eSchristos SOCKET
568585484eSchristos move_fd(
578585484eSchristos 	SOCKET fd
588585484eSchristos 	)
598585484eSchristos {
608585484eSchristos #if !defined(SYS_WINNT) && defined(F_DUPFD)
618585484eSchristos #ifndef FD_CHUNK
628585484eSchristos #define FD_CHUNK	10
638585484eSchristos #endif
648585484eSchristos #ifndef FOPEN_MAX
658585484eSchristos #define FOPEN_MAX	20
668585484eSchristos #endif
678585484eSchristos /*
688585484eSchristos  * number of fds we would like to have for
698585484eSchristos  * stdio FILE* available.
708585484eSchristos  * we can pick a "low" number as our use of
718585484eSchristos  * FILE* is limited to log files and temporarily
728585484eSchristos  * to data and config files. Except for log files
738585484eSchristos  * we don't keep the other FILE* open beyond the
748585484eSchristos  * scope of the function that opened it.
758585484eSchristos  */
768585484eSchristos #ifndef FD_PREFERRED_SOCKBOUNDARY
778585484eSchristos #define FD_PREFERRED_SOCKBOUNDARY 48
788585484eSchristos #endif
798585484eSchristos 
808585484eSchristos 	static SOCKET socket_boundary = -1;
818585484eSchristos 	SOCKET newfd;
828585484eSchristos 
83af12ab5eSchristos 	REQUIRE((int)fd >= 0);
848585484eSchristos 
858585484eSchristos 	/*
868585484eSchristos 	 * check whether boundary has be set up
878585484eSchristos 	 * already
888585484eSchristos 	 */
898585484eSchristos 	if (socket_boundary == -1) {
908585484eSchristos 		socket_boundary = max(0, min(GETDTABLESIZE() - FD_CHUNK,
918585484eSchristos 					     min(FOPEN_MAX, FD_PREFERRED_SOCKBOUNDARY)));
928585484eSchristos 		TRACE(1, ("move_fd: estimated max descriptors: %d, "
938585484eSchristos 			  "initial socket boundary: %d\n",
948585484eSchristos 			  GETDTABLESIZE(), socket_boundary));
958585484eSchristos 	}
968585484eSchristos 
978585484eSchristos 	/*
988585484eSchristos 	 * Leave a space for stdio to work in. potentially moving the
998585484eSchristos 	 * socket_boundary lower until allocation succeeds.
1008585484eSchristos 	 */
1018585484eSchristos 	do {
1028585484eSchristos 		if (fd >= 0 && fd < socket_boundary) {
1038585484eSchristos 			/* inside reserved range: attempt to move fd */
1048585484eSchristos 			newfd = fcntl(fd, F_DUPFD, socket_boundary);
1058585484eSchristos 
1068585484eSchristos 			if (newfd != -1) {
1078585484eSchristos 				/* success: drop the old one - return the new one */
1088585484eSchristos 				close(fd);
1098585484eSchristos 				return newfd;
1108585484eSchristos 			}
1118585484eSchristos 		} else {
1128585484eSchristos 			/* outside reserved range: no work - return the original one */
1138585484eSchristos 			return fd;
1148585484eSchristos 		}
1158585484eSchristos 		socket_boundary = max(0, socket_boundary - FD_CHUNK);
1168585484eSchristos 		TRACE(1, ("move_fd: selecting new socket boundary: %d\n",
1178585484eSchristos 			  socket_boundary));
1188585484eSchristos 	} while (socket_boundary > 0);
1198585484eSchristos #else
120af12ab5eSchristos 	ENSURE((int)fd >= 0);
1218585484eSchristos #endif /* !defined(SYS_WINNT) && defined(F_DUPFD) */
1228585484eSchristos 	return fd;
1238585484eSchristos }
1248585484eSchristos 
1258585484eSchristos 
1268585484eSchristos /*
1278585484eSchristos  * make_socket_nonblocking() - set up descriptor to be non blocking
1288585484eSchristos  */
1298585484eSchristos void
1308585484eSchristos make_socket_nonblocking(
1318585484eSchristos 	SOCKET fd
1328585484eSchristos 	)
1338585484eSchristos {
1348585484eSchristos 	/*
1358585484eSchristos 	 * set non-blocking,
1368585484eSchristos 	 */
1378585484eSchristos 
1388585484eSchristos #ifdef USE_FIONBIO
1398585484eSchristos 	/* in vxWorks we use FIONBIO, but the others are defined for old
1408585484eSchristos 	 * systems, so all hell breaks loose if we leave them defined
1418585484eSchristos 	 */
1428585484eSchristos #undef O_NONBLOCK
1438585484eSchristos #undef FNDELAY
1448585484eSchristos #undef O_NDELAY
1458585484eSchristos #endif
1468585484eSchristos 
1478585484eSchristos #if defined(O_NONBLOCK) /* POSIX */
1488585484eSchristos 	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
1498585484eSchristos 		msyslog(LOG_ERR,
1508585484eSchristos 			"fcntl(O_NONBLOCK) fails on fd #%d: %m", fd);
1518585484eSchristos 		exit(1);
1528585484eSchristos 	}
1538585484eSchristos #elif defined(FNDELAY)
1548585484eSchristos 	if (fcntl(fd, F_SETFL, FNDELAY) < 0) {
1558585484eSchristos 		msyslog(LOG_ERR, "fcntl(FNDELAY) fails on fd #%d: %m",
1568585484eSchristos 			fd);
1578585484eSchristos 		exit(1);
1588585484eSchristos 	}
1598585484eSchristos #elif defined(O_NDELAY) /* generally the same as FNDELAY */
1608585484eSchristos 	if (fcntl(fd, F_SETFL, O_NDELAY) < 0) {
1618585484eSchristos 		msyslog(LOG_ERR, "fcntl(O_NDELAY) fails on fd #%d: %m",
1628585484eSchristos 			fd);
1638585484eSchristos 		exit(1);
1648585484eSchristos 	}
1658585484eSchristos #elif defined(FIONBIO)
1668585484eSchristos 	{
1678585484eSchristos 		int on = 1;
1688585484eSchristos 
1698585484eSchristos 		if (ioctl(fd, FIONBIO, &on) < 0) {
1708585484eSchristos 			msyslog(LOG_ERR,
1718585484eSchristos 				"ioctl(FIONBIO) fails on fd #%d: %m",
1728585484eSchristos 				fd);
1738585484eSchristos 			exit(1);
1748585484eSchristos 		}
1758585484eSchristos 	}
1768585484eSchristos #elif defined(FIOSNBIO)
1778585484eSchristos 	if (ioctl(fd, FIOSNBIO, &on) < 0) {
1788585484eSchristos 		msyslog(LOG_ERR,
1798585484eSchristos 			"ioctl(FIOSNBIO) fails on fd #%d: %m", fd);
1808585484eSchristos 		exit(1);
1818585484eSchristos 	}
1828585484eSchristos #else
1838585484eSchristos # include "Bletch: Need non-blocking I/O!"
1848585484eSchristos #endif
1858585484eSchristos }
1868585484eSchristos 
1878585484eSchristos #if 0
1888585484eSchristos 
1898585484eSchristos /* The following subroutines should probably be moved here */
1908585484eSchristos 
1918585484eSchristos static SOCKET
1928585484eSchristos open_socket(
1938585484eSchristos 	sockaddr_u *	addr,
1948585484eSchristos 	int		bcast,
1958585484eSchristos 	int		turn_off_reuse,
1968585484eSchristos 	endpt *		interf
1978585484eSchristos 	)
1988585484eSchristos void
1998585484eSchristos sendpkt(
2008585484eSchristos 	sockaddr_u *	dest,
201*eabc0478Schristos 	endpt *		ep,
2028585484eSchristos 	int		ttl,
2038585484eSchristos 	struct pkt *	pkt,
2048585484eSchristos 	int		len
2058585484eSchristos 	)
2068585484eSchristos 
2078585484eSchristos static inline int
2088585484eSchristos read_refclock_packet(SOCKET fd, struct refclockio *rp, l_fp ts)
2098585484eSchristos 
2108585484eSchristos static inline int
2118585484eSchristos read_network_packet(
2128585484eSchristos 	SOCKET	fd,
213*eabc0478Schristos 	endpt *	itf,
2148585484eSchristos 	l_fp	ts
2158585484eSchristos 	)
2168585484eSchristos 
2178585484eSchristos void
2188585484eSchristos kill_asyncio(int startfd)
2198585484eSchristos 
2208585484eSchristos #endif /* 0 */
221