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