1 /**
2 * @file large_fd_set.c
3 *
4 * @brief Macro's and functions for manipulation of large file descriptor sets.
5 *
6 * Portions of this file are copyrighted by:
7 * Copyright (c) 2016 VMware, Inc. All rights reserved.
8 * Use is subject to license terms specified in the COPYING file
9 * distributed with the Net-SNMP package.
10 */
11
12 #include <net-snmp/net-snmp-config.h>
13
14 #ifdef HAVE_STDLIB_H
15 #include <stdlib.h>
16 #else
17 #error broken
18 #endif
19
20 #include <stdio.h>
21 #include <string.h> /* memset(), which is invoked by FD_ZERO() */
22
23 #include <stddef.h>
24
25 #include <net-snmp/net-snmp-includes.h>
26 #include <net-snmp/library/snmp_assert.h>
27 #include <net-snmp/library/large_fd_set.h>
28
29
30 #if !defined(cygwin) && defined(HAVE_WINSOCK_H)
31
32 #define LFD_SET(n, p) FD_SET(n, p)
33 #define LFD_CLR(n, p) FD_CLR(n, p)
34 #define LFD_ISSET(n, p) FD_ISSET(n, p)
35
36 void
netsnmp_large_fd_setfd(SOCKET fd,netsnmp_large_fd_set * fdset)37 netsnmp_large_fd_setfd(SOCKET fd, netsnmp_large_fd_set * fdset)
38 {
39 unsigned i;
40
41 netsnmp_assert(fd != INVALID_SOCKET);
42
43 if (fdset->lfs_setptr->fd_count == fdset->lfs_setsize)
44 netsnmp_large_fd_set_resize(fdset, 2 * (fdset->lfs_setsize + 1));
45
46 for (i = 0; i < fdset->lfs_setptr->fd_count; i++) {
47 if (fdset->lfs_setptr->fd_array[i] == fd)
48 break;
49 }
50
51 if (i == fdset->lfs_setptr->fd_count &&
52 fdset->lfs_setptr->fd_count < fdset->lfs_setsize) {
53 fdset->lfs_setptr->fd_count++;
54 fdset->lfs_setptr->fd_array[i] = fd;
55 }
56 }
57
58 void
netsnmp_large_fd_clr(SOCKET fd,netsnmp_large_fd_set * fdset)59 netsnmp_large_fd_clr(SOCKET fd, netsnmp_large_fd_set * fdset)
60 {
61 unsigned i;
62
63 netsnmp_assert(fd != INVALID_SOCKET);
64
65 for (i = 0; i < fdset->lfs_setptr->fd_count; i++) {
66 if (fdset->lfs_setptr->fd_array[i] == fd) {
67 while (i < fdset->lfs_setptr->fd_count - 1) {
68 fdset->lfs_setptr->fd_array[i] =
69 fdset->lfs_setptr->fd_array[i + 1];
70 i++;
71 }
72 fdset->lfs_setptr->fd_count--;
73 break;
74 }
75 }
76 }
77
78 int
netsnmp_large_fd_is_set(SOCKET fd,netsnmp_large_fd_set * fdset)79 netsnmp_large_fd_is_set(SOCKET fd, netsnmp_large_fd_set * fdset)
80 {
81 unsigned int i;
82
83 netsnmp_assert(fd != INVALID_SOCKET);
84
85 for (i = 0; i < fdset->lfs_setptr->fd_count; i++) {
86 if (fdset->lfs_setptr->fd_array[i] == fd)
87 return 1;
88 }
89 return 0;
90 }
91
92 #else
93
94 /*
95 * Recent versions of glibc trigger abort() if FD_SET(), FD_CLR() or
96 * FD_ISSET() is invoked with n >= FD_SETSIZE. Hence these replacement
97 * functions. However, since NFDBITS != 8 * sizeof(fd_set.fds_bits[0]) for at
98 * least HP-UX on ia64 and since that combination uses big endian, use the
99 * macros from <sys/select.h> on such systems.
100 */
LFD_SET(unsigned n,fd_set * p)101 NETSNMP_STATIC_INLINE void LFD_SET(unsigned n, fd_set *p)
102 {
103 enum { nfdbits = 8 * sizeof(p->fds_bits[0]) };
104
105 if (nfdbits == NFDBITS)
106 p->fds_bits[n / nfdbits] |= (1ULL << (n % nfdbits));
107 else
108 FD_SET(n, p);
109 }
110
LFD_CLR(unsigned n,fd_set * p)111 NETSNMP_STATIC_INLINE void LFD_CLR(unsigned n, fd_set *p)
112 {
113 enum { nfdbits = 8 * sizeof(p->fds_bits[0]) };
114
115 if (nfdbits == NFDBITS)
116 p->fds_bits[n / nfdbits] &= ~(1ULL << (n % nfdbits));
117 else
118 FD_CLR(n, p);
119 }
120
LFD_ISSET(unsigned n,const fd_set * p)121 NETSNMP_STATIC_INLINE unsigned LFD_ISSET(unsigned n, const fd_set *p)
122 {
123 enum { nfdbits = 8 * sizeof(p->fds_bits[0]) };
124
125 if (nfdbits == NFDBITS)
126 return (p->fds_bits[n / nfdbits] & (1ULL << (n % nfdbits))) != 0;
127 else
128 return FD_ISSET(n, p) != 0;
129 }
130
131 void
netsnmp_large_fd_setfd(int fd,netsnmp_large_fd_set * fdset)132 netsnmp_large_fd_setfd(int fd, netsnmp_large_fd_set * fdset)
133 {
134 netsnmp_assert(fd >= 0);
135
136 while (fd >= (int)fdset->lfs_setsize)
137 netsnmp_large_fd_set_resize(fdset, 2 * (fdset->lfs_setsize + 1));
138
139 LFD_SET(fd, fdset->lfs_setptr);
140 }
141
142 void
netsnmp_large_fd_clr(int fd,netsnmp_large_fd_set * fdset)143 netsnmp_large_fd_clr(int fd, netsnmp_large_fd_set * fdset)
144 {
145 netsnmp_assert(fd >= 0);
146
147 if ((unsigned)fd < fdset->lfs_setsize)
148 LFD_CLR(fd, fdset->lfs_setptr);
149 }
150
151 int
netsnmp_large_fd_is_set(int fd,netsnmp_large_fd_set * fdset)152 netsnmp_large_fd_is_set(int fd, netsnmp_large_fd_set * fdset)
153 {
154 netsnmp_assert(fd >= 0);
155
156 return ((unsigned)fd < fdset->lfs_setsize &&
157 LFD_ISSET(fd, fdset->lfs_setptr));
158 }
159
160 #endif
161
162 void
netsnmp_large_fd_set_init(netsnmp_large_fd_set * fdset,int setsize)163 netsnmp_large_fd_set_init(netsnmp_large_fd_set * fdset, int setsize)
164 {
165 fdset->lfs_setsize = 0;
166 fdset->lfs_setptr = NULL;
167 #if !defined(cygwin) && defined(HAVE_WINSOCK_H)
168 fdset->lfs_set.fd_count = 0;
169 #endif
170 netsnmp_large_fd_set_resize(fdset, setsize);
171 }
172
173 int
netsnmp_large_fd_set_select(int numfds,netsnmp_large_fd_set * readfds,netsnmp_large_fd_set * writefds,netsnmp_large_fd_set * exceptfds,struct timeval * timeout)174 netsnmp_large_fd_set_select(int numfds, netsnmp_large_fd_set *readfds,
175 netsnmp_large_fd_set *writefds,
176 netsnmp_large_fd_set *exceptfds,
177 struct timeval *timeout)
178 {
179 NETSNMP_SELECT_TIMEVAL tmo;
180
181 if (timeout) {
182 tmo.tv_sec = timeout->tv_sec;
183 tmo.tv_usec = timeout->tv_usec;
184 }
185 #if defined(cygwin) || !defined(HAVE_WINSOCK_H)
186 /* Bit-set representation: make sure all fds have at least size 'numfds'. */
187 if (readfds && readfds->lfs_setsize < numfds)
188 netsnmp_large_fd_set_resize(readfds, numfds);
189 if (writefds && writefds->lfs_setsize < numfds)
190 netsnmp_large_fd_set_resize(writefds, numfds);
191 if (exceptfds && exceptfds->lfs_setsize < numfds)
192 netsnmp_large_fd_set_resize(exceptfds, numfds);
193 #else
194 /* Array representation: no resizing is necessary. */
195 #endif
196
197 return select(numfds, (readfds) ? readfds->lfs_setptr : NULL,
198 (writefds) ? writefds->lfs_setptr : NULL,
199 (exceptfds) ? exceptfds->lfs_setptr : NULL,
200 timeout ? &tmo : NULL);
201 }
202
203 int
netsnmp_large_fd_set_resize(netsnmp_large_fd_set * fdset,int setsize)204 netsnmp_large_fd_set_resize(netsnmp_large_fd_set * fdset, int setsize)
205 {
206 int fd_set_bytes;
207
208 if (fdset->lfs_setsize == setsize)
209 goto success;
210
211 if (setsize > FD_SETSIZE) {
212 fd_set_bytes = NETSNMP_FD_SET_BYTES(setsize);
213 if (fdset->lfs_setsize > FD_SETSIZE) {
214 fdset->lfs_setptr = realloc(fdset->lfs_setptr, fd_set_bytes);
215 if (!fdset->lfs_setptr)
216 goto out_of_mem;
217 } else {
218 fdset->lfs_setptr = malloc(fd_set_bytes);
219 if (!fdset->lfs_setptr)
220 goto out_of_mem;
221 *fdset->lfs_setptr = fdset->lfs_set;
222 }
223 } else {
224 if (fdset->lfs_setsize > FD_SETSIZE) {
225 fdset->lfs_set = *fdset->lfs_setptr;
226 free(fdset->lfs_setptr);
227 }
228 fdset->lfs_setptr = &fdset->lfs_set;
229 }
230
231 #if defined(cygwin) || !defined(HAVE_WINSOCK_H)
232 /*
233 * Unix: when enlarging, clear the file descriptors defined in the
234 * resized *fdset but that were not defined in the original *fdset.
235 */
236 if ( fdset->lfs_setsize == 0 && setsize == FD_SETSIZE ) {
237 /* In this case we can use the OS's FD_ZERO */
238 FD_ZERO(fdset->lfs_setptr);
239 } else {
240 int i;
241
242 for (i = fdset->lfs_setsize; i < setsize; i++)
243 LFD_CLR(i, fdset->lfs_setptr);
244 }
245 #endif
246
247 fdset->lfs_setsize = setsize;
248 #if !defined(cygwin) && defined(HAVE_WINSOCK_H)
249 if (setsize < fdset->lfs_setptr->fd_count)
250 fdset->lfs_setptr->fd_count = setsize;
251 #endif
252 success:
253 return 1;
254
255 out_of_mem:
256 fdset->lfs_setsize = 0;
257 #if !defined(cygwin) && defined(HAVE_WINSOCK_H)
258 fdset->lfs_setptr->fd_count = 0;
259 #endif
260 return 0;
261 }
262
263 void
netsnmp_large_fd_set_cleanup(netsnmp_large_fd_set * fdset)264 netsnmp_large_fd_set_cleanup(netsnmp_large_fd_set * fdset)
265 {
266 netsnmp_large_fd_set_resize(fdset, 0);
267 fdset->lfs_setsize = 0;
268 fdset->lfs_setptr = NULL;
269 }
270
271 void
netsnmp_copy_fd_set_to_large_fd_set(netsnmp_large_fd_set * dst,const fd_set * src)272 netsnmp_copy_fd_set_to_large_fd_set(netsnmp_large_fd_set * dst,
273 const fd_set * src)
274 {
275 netsnmp_large_fd_set_resize(dst, FD_SETSIZE);
276 *dst->lfs_setptr = *src;
277 }
278
279 int
netsnmp_copy_large_fd_set_to_fd_set(fd_set * dst,const netsnmp_large_fd_set * src)280 netsnmp_copy_large_fd_set_to_fd_set(fd_set * dst,
281 const netsnmp_large_fd_set * src)
282 {
283 /* Report failure if *src is larger than FD_SETSIZE. */
284 if (src->lfs_setsize > FD_SETSIZE) {
285 FD_ZERO(dst);
286 return -1;
287 }
288
289 *dst = *src->lfs_setptr;
290
291 #if !(!defined(cygwin) && defined(HAVE_WINSOCK_H))
292 {
293 int i;
294
295 /* Unix: clear any file descriptors defined in *dst but not in *src. */
296 for (i = src->lfs_setsize; i < FD_SETSIZE; ++i)
297 FD_CLR(i, dst);
298 }
299 #endif
300
301 return 0;
302 }
303