1275970Scy/*
2275970Scy * socket.c - low-level socket operations
3275970Scy */
4275970Scy
5275970Scy#ifdef HAVE_CONFIG_H
6275970Scy# include <config.h>
7275970Scy#endif
8275970Scy
9275970Scy#include <stdio.h>
10275970Scy
11275970Scy#include "ntp.h"
12275970Scy#include "ntp_io.h"
13275970Scy#include "ntp_net.h"
14275970Scy#include "ntp_debug.h"
15275970Scy
16275970Scy/*
17275970Scy * Windows C runtime ioctl() can't deal properly with sockets,
18275970Scy * map to ioctlsocket for this source file.
19275970Scy */
20275970Scy#ifdef SYS_WINNT
21275970Scy#define ioctl(fd, opt, val)  ioctlsocket(fd, opt, (u_long *)(val))
22275970Scy#endif
23275970Scy
24275970Scy/*
25275970Scy * on Unix systems the stdio library typically
26275970Scy * makes use of file descriptors in the lower
27275970Scy * integer range.  stdio usually will make use
28275970Scy * of the file descriptors in the range of
29275970Scy * [0..FOPEN_MAX)
30275970Scy * in order to keep this range clean, for socket
31275970Scy * file descriptors we attempt to move them above
32275970Scy * FOPEN_MAX. This is not as easy as it sounds as
33275970Scy * FOPEN_MAX changes from implementation to implementation
34275970Scy * and may exceed to current file decriptor limits.
35275970Scy * We are using following strategy:
36275970Scy * - keep a current socket fd boundary initialized with
37275970Scy *   max(0, min(GETDTABLESIZE() - FD_CHUNK, FOPEN_MAX))
38275970Scy * - attempt to move the descriptor to the boundary or
39275970Scy *   above.
40275970Scy *   - if that fails and boundary > 0 set boundary
41275970Scy *     to min(0, socket_fd_boundary - FD_CHUNK)
42275970Scy *     -> retry
43275970Scy *     if failure and boundary == 0 return old fd
44275970Scy *   - on success close old fd return new fd
45275970Scy *
46275970Scy * effects:
47275970Scy *   - fds will be moved above the socket fd boundary
48275970Scy *     if at all possible.
49275970Scy *   - the socket boundary will be reduced until
50275970Scy *     allocation is possible or 0 is reached - at this
51275970Scy *     point the algrithm will be disabled
52275970Scy */
53275970ScySOCKET
54275970Scymove_fd(
55275970Scy	SOCKET fd
56275970Scy	)
57275970Scy{
58275970Scy#if !defined(SYS_WINNT) && defined(F_DUPFD)
59275970Scy#ifndef FD_CHUNK
60275970Scy#define FD_CHUNK	10
61275970Scy#endif
62275970Scy#ifndef FOPEN_MAX
63275970Scy#define FOPEN_MAX	20
64275970Scy#endif
65275970Scy/*
66275970Scy * number of fds we would like to have for
67275970Scy * stdio FILE* available.
68275970Scy * we can pick a "low" number as our use of
69275970Scy * FILE* is limited to log files and temporarily
70275970Scy * to data and config files. Except for log files
71275970Scy * we don't keep the other FILE* open beyond the
72275970Scy * scope of the function that opened it.
73275970Scy */
74275970Scy#ifndef FD_PREFERRED_SOCKBOUNDARY
75275970Scy#define FD_PREFERRED_SOCKBOUNDARY 48
76275970Scy#endif
77275970Scy
78275970Scy	static SOCKET socket_boundary = -1;
79275970Scy	SOCKET newfd;
80275970Scy
81289997Sglebius	REQUIRE((int)fd >= 0);
82275970Scy
83275970Scy	/*
84275970Scy	 * check whether boundary has be set up
85275970Scy	 * already
86275970Scy	 */
87275970Scy	if (socket_boundary == -1) {
88275970Scy		socket_boundary = max(0, min(GETDTABLESIZE() - FD_CHUNK,
89275970Scy					     min(FOPEN_MAX, FD_PREFERRED_SOCKBOUNDARY)));
90275970Scy		TRACE(1, ("move_fd: estimated max descriptors: %d, "
91275970Scy			  "initial socket boundary: %d\n",
92275970Scy			  GETDTABLESIZE(), socket_boundary));
93275970Scy	}
94275970Scy
95275970Scy	/*
96275970Scy	 * Leave a space for stdio to work in. potentially moving the
97275970Scy	 * socket_boundary lower until allocation succeeds.
98275970Scy	 */
99275970Scy	do {
100275970Scy		if (fd >= 0 && fd < socket_boundary) {
101275970Scy			/* inside reserved range: attempt to move fd */
102275970Scy			newfd = fcntl(fd, F_DUPFD, socket_boundary);
103275970Scy
104275970Scy			if (newfd != -1) {
105275970Scy				/* success: drop the old one - return the new one */
106275970Scy				close(fd);
107275970Scy				return newfd;
108275970Scy			}
109275970Scy		} else {
110275970Scy			/* outside reserved range: no work - return the original one */
111275970Scy			return fd;
112275970Scy		}
113275970Scy		socket_boundary = max(0, socket_boundary - FD_CHUNK);
114275970Scy		TRACE(1, ("move_fd: selecting new socket boundary: %d\n",
115275970Scy			  socket_boundary));
116275970Scy	} while (socket_boundary > 0);
117275970Scy#else
118289997Sglebius	ENSURE((int)fd >= 0);
119275970Scy#endif /* !defined(SYS_WINNT) && defined(F_DUPFD) */
120275970Scy	return fd;
121275970Scy}
122275970Scy
123275970Scy
124275970Scy/*
125275970Scy * make_socket_nonblocking() - set up descriptor to be non blocking
126275970Scy */
127275970Scyvoid
128275970Scymake_socket_nonblocking(
129275970Scy	SOCKET fd
130275970Scy	)
131275970Scy{
132275970Scy	/*
133275970Scy	 * set non-blocking,
134275970Scy	 */
135275970Scy
136275970Scy#ifdef USE_FIONBIO
137275970Scy	/* in vxWorks we use FIONBIO, but the others are defined for old
138275970Scy	 * systems, so all hell breaks loose if we leave them defined
139275970Scy	 */
140275970Scy#undef O_NONBLOCK
141275970Scy#undef FNDELAY
142275970Scy#undef O_NDELAY
143275970Scy#endif
144275970Scy
145275970Scy#if defined(O_NONBLOCK) /* POSIX */
146275970Scy	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
147275970Scy		msyslog(LOG_ERR,
148275970Scy			"fcntl(O_NONBLOCK) fails on fd #%d: %m", fd);
149275970Scy		exit(1);
150275970Scy	}
151275970Scy#elif defined(FNDELAY)
152275970Scy	if (fcntl(fd, F_SETFL, FNDELAY) < 0) {
153275970Scy		msyslog(LOG_ERR, "fcntl(FNDELAY) fails on fd #%d: %m",
154275970Scy			fd);
155275970Scy		exit(1);
156275970Scy	}
157275970Scy#elif defined(O_NDELAY) /* generally the same as FNDELAY */
158275970Scy	if (fcntl(fd, F_SETFL, O_NDELAY) < 0) {
159275970Scy		msyslog(LOG_ERR, "fcntl(O_NDELAY) fails on fd #%d: %m",
160275970Scy			fd);
161275970Scy		exit(1);
162275970Scy	}
163275970Scy#elif defined(FIONBIO)
164275970Scy	{
165275970Scy		int on = 1;
166275970Scy
167275970Scy		if (ioctl(fd, FIONBIO, &on) < 0) {
168275970Scy			msyslog(LOG_ERR,
169275970Scy				"ioctl(FIONBIO) fails on fd #%d: %m",
170275970Scy				fd);
171275970Scy			exit(1);
172275970Scy		}
173275970Scy	}
174275970Scy#elif defined(FIOSNBIO)
175275970Scy	if (ioctl(fd, FIOSNBIO, &on) < 0) {
176275970Scy		msyslog(LOG_ERR,
177275970Scy			"ioctl(FIOSNBIO) fails on fd #%d: %m", fd);
178275970Scy		exit(1);
179275970Scy	}
180275970Scy#else
181275970Scy# include "Bletch: Need non-blocking I/O!"
182275970Scy#endif
183275970Scy}
184275970Scy
185275970Scy#if 0
186275970Scy
187275970Scy/* The following subroutines should probably be moved here */
188275970Scy
189275970Scystatic SOCKET
190275970Scyopen_socket(
191275970Scy	sockaddr_u *	addr,
192275970Scy	int		bcast,
193275970Scy	int		turn_off_reuse,
194275970Scy	endpt *		interf
195275970Scy	)
196275970Scyvoid
197275970Scysendpkt(
198275970Scy	sockaddr_u *		dest,
199275970Scy	struct interface *	ep,
200275970Scy	int			ttl,
201275970Scy	struct pkt *		pkt,
202275970Scy	int			len
203275970Scy	)
204275970Scy
205275970Scystatic inline int
206275970Scyread_refclock_packet(SOCKET fd, struct refclockio *rp, l_fp ts)
207275970Scy
208275970Scystatic inline int
209275970Scyread_network_packet(
210275970Scy	SOCKET			fd,
211275970Scy	struct interface *	itf,
212275970Scy	l_fp			ts
213275970Scy	)
214275970Scy
215275970Scyvoid
216275970Scykill_asyncio(int startfd)
217275970Scy
218275970Scy#endif /* 0 */
219