/src/net-snmp/snmplib/transports/snmpSocketBaseDomain.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file snmpSocketBaseDomain.c |
3 | | * |
4 | | * @brief Socket support functions. |
5 | | */ |
6 | | |
7 | | #include <net-snmp/net-snmp-config.h> |
8 | | |
9 | | #include <net-snmp/types.h> |
10 | | #include <net-snmp/library/snmpSocketBaseDomain.h> |
11 | | |
12 | | #include <stddef.h> |
13 | | #include <stdio.h> |
14 | | #ifdef HAVE_UNISTD_H |
15 | | #include <unistd.h> |
16 | | #endif |
17 | | #include <sys/types.h> |
18 | | #include <ctype.h> |
19 | | #ifdef HAVE_STDLIB_H |
20 | | #include <stdlib.h> |
21 | | #endif |
22 | | #ifdef HAVE_STRING_H |
23 | | #include <string.h> |
24 | | #else |
25 | | #include <strings.h> |
26 | | #endif |
27 | | #ifdef HAVE_FCNTL_H |
28 | | #include <fcntl.h> |
29 | | #endif |
30 | | #ifdef HAVE_SYS_SOCKET_H |
31 | | #include <sys/socket.h> |
32 | | #endif |
33 | | #include <errno.h> |
34 | | |
35 | | #include <net-snmp/types.h> |
36 | | #include <net-snmp/library/snmp_debug.h> |
37 | | #include <net-snmp/library/tools.h> |
38 | | #include <net-snmp/library/default_store.h> |
39 | | #include <net-snmp/library/system.h> |
40 | | #include <net-snmp/library/snmp_assert.h> |
41 | | |
42 | | /* all sockets pretty much close the same way */ |
43 | 2.98k | int netsnmp_socketbase_close(netsnmp_transport *t) { |
44 | 2.98k | int rc = -1; |
45 | 2.98k | if (t->sock >= 0) { |
46 | 2.98k | #ifndef HAVE_CLOSESOCKET |
47 | 2.98k | rc = close(t->sock); |
48 | | #else |
49 | | rc = closesocket(t->sock); |
50 | | #endif |
51 | 2.98k | t->sock = -1; |
52 | 2.98k | } |
53 | 2.98k | return rc; |
54 | 2.98k | } |
55 | | |
56 | | /* |
57 | | * find largest possible buffer between current size and specified size. |
58 | | * |
59 | | * Try to maximize the current buffer of type "optname" |
60 | | * to the maximum allowable size by the OS (as close to |
61 | | * size as possible) |
62 | | */ |
63 | | static int |
64 | | _sock_buffer_maximize(int s, int optname, const char *buftype, int size) |
65 | 0 | { |
66 | 0 | int curbuf = 0; |
67 | 0 | socklen_t curbuflen = sizeof(int); |
68 | 0 | int lo, mid, hi; |
69 | | |
70 | | /* |
71 | | * First we need to determine our current buffer |
72 | | */ |
73 | 0 | if ((getsockopt(s, SOL_SOCKET, optname, (void *) &curbuf, |
74 | 0 | &curbuflen) == 0) |
75 | 0 | && (curbuflen == sizeof(int))) { |
76 | |
|
77 | 0 | DEBUGMSGTL(("verbose:socket:buffer:max", "Current %s is %d\n", |
78 | 0 | buftype, curbuf)); |
79 | | |
80 | | /* |
81 | | * Let's not be stupid ... if we were asked for less than what we |
82 | | * already have, then forget about it |
83 | | */ |
84 | 0 | if (size <= curbuf) { |
85 | 0 | DEBUGMSGTL(("verbose:socket:buffer:max", |
86 | 0 | "Requested %s <= current buffer\n", buftype)); |
87 | 0 | return curbuf; |
88 | 0 | } |
89 | | |
90 | | /* |
91 | | * Do a binary search the optimal buffer within 1k of the point of |
92 | | * failure. This is rather bruteforce, but simple |
93 | | */ |
94 | 0 | hi = size; |
95 | 0 | lo = curbuf; |
96 | |
|
97 | 0 | while (hi - lo > 1024) { |
98 | 0 | mid = (lo + hi) / 2; |
99 | 0 | if (setsockopt(s, SOL_SOCKET, optname, (void *) &mid, |
100 | 0 | sizeof(int)) == 0) { |
101 | 0 | lo = mid; /* Success: search between mid and hi */ |
102 | 0 | } else { |
103 | 0 | hi = mid; /* Failed: search between lo and mid */ |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | | /* |
108 | | * Now print if this optimization helped or not |
109 | | */ |
110 | 0 | if (getsockopt(s,SOL_SOCKET, optname, (void *) &curbuf, |
111 | 0 | &curbuflen) == 0) { |
112 | 0 | DEBUGMSGTL(("socket:buffer:max", |
113 | 0 | "Maximized %s: %d\n",buftype, curbuf)); |
114 | 0 | } |
115 | 0 | } else { |
116 | | /* |
117 | | * There is really not a lot we can do anymore. |
118 | | * If the OS doesn't give us the current buffer, then what's the |
119 | | * point in trying to make it better |
120 | | */ |
121 | 0 | DEBUGMSGTL(("socket:buffer:max", "Get %s failed ... giving up!\n", |
122 | 0 | buftype)); |
123 | 0 | curbuf = -1; |
124 | 0 | } |
125 | | |
126 | 0 | return curbuf; |
127 | 0 | } |
128 | | |
129 | | |
130 | | static const char * |
131 | | _sock_buf_type_get(int optname, int local) |
132 | 5.97k | { |
133 | 5.97k | if (optname == SO_SNDBUF) { |
134 | 2.98k | if (local) |
135 | 2.98k | return "server send buffer"; |
136 | 0 | else |
137 | 0 | return "client send buffer"; |
138 | 2.98k | } else if (optname == SO_RCVBUF) { |
139 | 2.98k | if (local) |
140 | 2.98k | return "server receive buffer"; |
141 | 0 | else |
142 | 0 | return "client receive buffer"; |
143 | 2.98k | } |
144 | | |
145 | 0 | return "unknown buffer"; |
146 | 5.97k | } |
147 | | |
148 | | /* |
149 | | * |
150 | | * Get the requested buffersize, based on |
151 | | * - sockettype : client (local = 0) or server (local = 1) |
152 | | * - buffertype : send (optname = SO_SNDBUF) or recv (SO_RCVBUF) |
153 | | * |
154 | | * In case a compile time buffer was specified, then use that one |
155 | | * if there was no runtime configuration override |
156 | | */ |
157 | | static int |
158 | | _sock_buffer_size_get(int optname, int local, const char **buftype) |
159 | 5.97k | { |
160 | 5.97k | int size; |
161 | | |
162 | 5.97k | if (NULL != buftype) |
163 | 5.97k | *buftype = _sock_buf_type_get(optname, local); |
164 | | |
165 | 5.97k | if (optname == SO_SNDBUF) { |
166 | 2.98k | if (local) { |
167 | 2.98k | size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, |
168 | 2.98k | NETSNMP_DS_LIB_SERVERSENDBUF); |
169 | | #ifdef NETSNMP_DEFAULT_SERVER_SEND_BUF |
170 | | if (size <= 0) |
171 | | size = NETSNMP_DEFAULT_SERVER_SEND_BUF; |
172 | | #endif |
173 | 2.98k | } else { |
174 | 0 | size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, |
175 | 0 | NETSNMP_DS_LIB_CLIENTSENDBUF); |
176 | | #ifdef NETSNMP_DEFAULT_CLIENT_SEND_BUF |
177 | | if (size <= 0) |
178 | | size = NETSNMP_DEFAULT_CLIENT_SEND_BUF; |
179 | | #endif |
180 | 0 | } |
181 | 2.98k | } else if (optname == SO_RCVBUF) { |
182 | 2.98k | if (local) { |
183 | 2.98k | size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, |
184 | 2.98k | NETSNMP_DS_LIB_SERVERRECVBUF); |
185 | | #ifdef NETSNMP_DEFAULT_SERVER_RECV_BUF |
186 | | if (size <= 0) |
187 | | size = NETSNMP_DEFAULT_SERVER_RECV_BUF; |
188 | | #endif |
189 | 2.98k | } else { |
190 | 0 | size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, |
191 | 0 | NETSNMP_DS_LIB_CLIENTRECVBUF); |
192 | | #ifdef NETSNMP_DEFAULT_CLIENT_RECV_BUF |
193 | | if (size <= 0) |
194 | | size = NETSNMP_DEFAULT_CLIENT_RECV_BUF; |
195 | | #endif |
196 | 0 | } |
197 | 2.98k | } else { |
198 | 0 | size = 0; |
199 | 0 | } |
200 | | |
201 | 5.97k | DEBUGMSGTL(("socket:buffer", "Requested %s is %d\n", |
202 | 5.97k | (buftype) ? *buftype : "unknown buffer", size)); |
203 | | |
204 | 5.97k | return(size); |
205 | 5.97k | } |
206 | | |
207 | | /* |
208 | | * set socket buffer size |
209 | | * |
210 | | * @param ss : socket |
211 | | * @param optname: SO_SNDBUF or SO_RCVBUF |
212 | | * @param local : 1 for server, 0 for client |
213 | | * @param reqbuf : requested size, or 0 for default |
214 | | * |
215 | | * @retval -1 : error |
216 | | * @retval >0 : new buffer size |
217 | | */ |
218 | | int |
219 | | netsnmp_sock_buffer_set(int s, int optname, int local, int size) |
220 | 5.97k | { |
221 | | #if ! defined(SO_SNDBUF) && ! defined(SO_RCVBUF) |
222 | | DEBUGMSGTL(("socket:buffer", "Changing socket buffer is not supported\n")); |
223 | | return -1; |
224 | | #else |
225 | 5.97k | const char *buftype; |
226 | 5.97k | int curbuf = 0; |
227 | 5.97k | socklen_t curbuflen = sizeof(int); |
228 | | |
229 | | # ifndef SO_SNDBUF |
230 | | if (SO_SNDBUF == optname) { |
231 | | DEBUGMSGTL(("socket:buffer", |
232 | | "Changing socket send buffer is not supported\n")); |
233 | | return -1; |
234 | | } |
235 | | # endif /*SO_SNDBUF */ |
236 | | # ifndef SO_RCVBUF |
237 | | if (SO_RCVBUF == optname) { |
238 | | DEBUGMSGTL(("socket:buffer", |
239 | | "Changing socket receive buffer is not supported\n")); |
240 | | return -1; |
241 | | } |
242 | | # endif /*SO_RCVBUF */ |
243 | | |
244 | | /* |
245 | | * What is the requested buffer size ? |
246 | | */ |
247 | 5.97k | if (0 == size) |
248 | 5.97k | size = _sock_buffer_size_get(optname, local, &buftype); |
249 | 0 | else { |
250 | 0 | buftype = _sock_buf_type_get(optname, local); |
251 | 0 | DEBUGMSGT(("verbose:socket:buffer", "Requested %s is %d\n", |
252 | 0 | buftype, size)); |
253 | 0 | } |
254 | | |
255 | 5.97k | if ((getsockopt(s, SOL_SOCKET, optname, (void *) &curbuf, |
256 | 5.97k | &curbuflen) == 0) |
257 | 5.97k | && (curbuflen == sizeof(int))) { |
258 | | |
259 | 5.97k | DEBUGMSGT(("verbose:socket:buffer", "Original %s is %d\n", |
260 | 5.97k | buftype, curbuf)); |
261 | 5.97k | if (curbuf >= size) { |
262 | 5.97k | DEBUGMSGT(("verbose:socket:buffer", |
263 | 5.97k | "New %s size is smaller than original!\n", buftype)); |
264 | 5.97k | } |
265 | 5.97k | } |
266 | | |
267 | | /* |
268 | | * If the buffersize was not specified or it was a negative value |
269 | | * then don't change the OS buffers at all |
270 | | */ |
271 | 5.97k | if (size <= 0) { |
272 | 5.97k | DEBUGMSGT(("socket:buffer", |
273 | 5.97k | "%s not valid or not specified; using OS default(%d)\n", |
274 | 5.97k | buftype,curbuf)); |
275 | 5.97k | return curbuf; |
276 | 5.97k | } |
277 | | |
278 | | /* |
279 | | * Try to set the requested send buffer |
280 | | */ |
281 | 0 | if (setsockopt(s, SOL_SOCKET, optname, (void *) &size, sizeof(int)) == 0) { |
282 | | /* |
283 | | * Because some platforms lie about the actual buffer that has been |
284 | | * set (Linux will always say it worked ...), we print some |
285 | | * diagnostic output for debugging |
286 | | */ |
287 | 0 | DEBUGIF("socket:buffer") { |
288 | 0 | DEBUGMSGT(("socket:buffer", "Set %s to %d\n", |
289 | 0 | buftype, size)); |
290 | 0 | if ((getsockopt(s, SOL_SOCKET, optname, (void *) &curbuf, |
291 | 0 | &curbuflen) == 0) |
292 | 0 | && (curbuflen == sizeof(int))) { |
293 | |
|
294 | 0 | DEBUGMSGT(("verbose:socket:buffer", |
295 | 0 | "Now %s is %d\n", buftype, curbuf)); |
296 | 0 | } |
297 | 0 | } |
298 | | /* |
299 | | * If the new buffer is smaller than the size we requested, we will |
300 | | * try to increment the new buffer with 1k increments |
301 | | * (this will sometime allow us to reach a more optimal buffer.) |
302 | | * For example : On Solaris, if the max OS buffer is 100k and you |
303 | | * request 110k, you end up with the default 8k :-( |
304 | | */ |
305 | 0 | if (curbuf < size) { |
306 | 0 | curbuf = _sock_buffer_maximize(s, optname, buftype, size); |
307 | 0 | if(-1 != curbuf) |
308 | 0 | size = curbuf; |
309 | 0 | } |
310 | |
|
311 | 0 | } else { |
312 | | /* |
313 | | * Obviously changing the buffer failed, most like like because we |
314 | | * requested a buffer greater than the OS limit. |
315 | | * Therefore we need to search for an optimal buffer that is close |
316 | | * enough to the point of failure. |
317 | | * This will allow us to reach a more optimal buffer. |
318 | | * For example : On Solaris, if the max OS buffer is 100k and you |
319 | | * request 110k, you end up with the default 8k :-( |
320 | | * After this quick seach we would get 1k close to 100k (the max) |
321 | | */ |
322 | 0 | DEBUGMSGTL(("socket:buffer", "couldn't set %s to %d\n", |
323 | 0 | buftype, size)); |
324 | |
|
325 | 0 | curbuf = _sock_buffer_maximize(s, optname, buftype, size); |
326 | 0 | if(-1 != curbuf) |
327 | 0 | size = curbuf; |
328 | 0 | } |
329 | |
|
330 | 0 | return size; |
331 | 5.97k | #endif |
332 | 5.97k | } |
333 | | |
334 | | |
335 | | /** |
336 | | * Sets the mode of a socket for all subsequent I/O operations. |
337 | | * |
338 | | * @param[in] sock Socket descriptor (Unix) or socket handle (Windows). |
339 | | * @param[in] non_blocking_mode I/O mode: non-zero selects non-blocking mode; |
340 | | * zero selects blocking mode. |
341 | | * |
342 | | * @return zero upon success and a negative value upon error. |
343 | | */ |
344 | | int |
345 | | netsnmp_set_non_blocking_mode(int sock, int non_blocking_mode) |
346 | 0 | { |
347 | | #ifdef WIN32 |
348 | | NETSNMP_IOCTLSOCKET_ARG arg; |
349 | | |
350 | | arg = non_blocking_mode; |
351 | | return ioctlsocket(sock, FIONBIO, &arg); |
352 | | #else |
353 | 0 | int sockflags; |
354 | |
|
355 | 0 | if ((sockflags = fcntl(sock, F_GETFL, 0)) >= 0) { |
356 | 0 | return fcntl(sock, F_SETFL, |
357 | 0 | non_blocking_mode ? sockflags | O_NONBLOCK |
358 | 0 | : sockflags & ~O_NONBLOCK); |
359 | 0 | } else |
360 | 0 | return -1; |
361 | 0 | #endif |
362 | 0 | } |