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