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
move_fd(SOCKET fd)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
make_socket_nonblocking(SOCKET fd)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