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