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 #if HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #include <sys/types.h>
18 #include <ctype.h>
19 #if HAVE_STDLIB_H
20 #include <stdlib.h>
21 #endif
22 #if HAVE_STRING_H
23 #include <string.h>
24 #else
25 #include <strings.h>
26 #endif
27 #if HAVE_FCNTL_H
28 #include <fcntl.h>
29 #endif
30 #if 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 */
netsnmp_socketbase_close(netsnmp_transport * t)43 int netsnmp_socketbase_close(netsnmp_transport *t) {
44 int rc = -1;
45 if (t->sock >= 0) {
46 #ifndef HAVE_CLOSESOCKET
47 rc = close(t->sock);
48 #else
49 rc = closesocket(t->sock);
50 #endif
51 t->sock = -1;
52 }
53 return rc;
54 }
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
_sock_buffer_maximize(int s,int optname,const char * buftype,int size)64 _sock_buffer_maximize(int s, int optname, const char *buftype, int size)
65 {
66 int curbuf = 0;
67 socklen_t curbuflen = sizeof(int);
68 int lo, mid, hi;
69
70 /*
71 * First we need to determine our current buffer
72 */
73 if ((getsockopt(s, SOL_SOCKET, optname, (void *) &curbuf,
74 &curbuflen) == 0)
75 && (curbuflen == sizeof(int))) {
76
77 DEBUGMSGTL(("verbose:socket:buffer:max", "Current %s is %d\n",
78 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 if (size <= curbuf) {
85 DEBUGMSGTL(("verbose:socket:buffer:max",
86 "Requested %s <= current buffer\n", buftype));
87 return curbuf;
88 }
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 hi = size;
95 lo = curbuf;
96
97 while (hi - lo > 1024) {
98 mid = (lo + hi) / 2;
99 if (setsockopt(s, SOL_SOCKET, optname, (void *) &mid,
100 sizeof(int)) == 0) {
101 lo = mid; /* Success: search between mid and hi */
102 } else {
103 hi = mid; /* Failed: search between lo and mid */
104 }
105 }
106
107 /*
108 * Now print if this optimization helped or not
109 */
110 if (getsockopt(s,SOL_SOCKET, optname, (void *) &curbuf,
111 &curbuflen) == 0) {
112 DEBUGMSGTL(("socket:buffer:max",
113 "Maximized %s: %d\n",buftype, curbuf));
114 }
115 } 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 DEBUGMSGTL(("socket:buffer:max", "Get %s failed ... giving up!\n",
122 buftype));
123 curbuf = -1;
124 }
125
126 return curbuf;
127 }
128
129
130 static const char *
_sock_buf_type_get(int optname,int local)131 _sock_buf_type_get(int optname, int local)
132 {
133 if (optname == SO_SNDBUF) {
134 if (local)
135 return "server send buffer";
136 else
137 return "client send buffer";
138 } else if (optname == SO_RCVBUF) {
139 if (local)
140 return "server receive buffer";
141 else
142 return "client receive buffer";
143 }
144
145 return "unknown buffer";
146 }
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
_sock_buffer_size_get(int optname,int local,const char ** buftype)158 _sock_buffer_size_get(int optname, int local, const char **buftype)
159 {
160 int size;
161
162 if (NULL != buftype)
163 *buftype = _sock_buf_type_get(optname, local);
164
165 if (optname == SO_SNDBUF) {
166 if (local) {
167 size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
168 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 } else {
174 size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
175 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 }
181 } else if (optname == SO_RCVBUF) {
182 if (local) {
183 size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
184 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 } else {
190 size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
191 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 }
197 } else {
198 size = 0;
199 }
200
201 DEBUGMSGTL(("socket:buffer", "Requested %s is %d\n",
202 (buftype) ? *buftype : "unknown buffer", size));
203
204 return(size);
205 }
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
netsnmp_sock_buffer_set(int s,int optname,int local,int size)219 netsnmp_sock_buffer_set(int s, int optname, int local, int size)
220 {
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 const char *buftype;
226 int curbuf = 0;
227 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 if (0 == size)
248 size = _sock_buffer_size_get(optname, local, &buftype);
249 else {
250 buftype = _sock_buf_type_get(optname, local);
251 DEBUGMSGT(("verbose:socket:buffer", "Requested %s is %d\n",
252 buftype, size));
253 }
254
255 if ((getsockopt(s, SOL_SOCKET, optname, (void *) &curbuf,
256 &curbuflen) == 0)
257 && (curbuflen == sizeof(int))) {
258
259 DEBUGMSGT(("verbose:socket:buffer", "Original %s is %d\n",
260 buftype, curbuf));
261 if (curbuf >= size) {
262 DEBUGMSGT(("verbose:socket:buffer",
263 "New %s size is smaller than original!\n", buftype));
264 }
265 }
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 if (size <= 0) {
272 DEBUGMSGT(("socket:buffer",
273 "%s not valid or not specified; using OS default(%d)\n",
274 buftype,curbuf));
275 return curbuf;
276 }
277
278 /*
279 * Try to set the requested send buffer
280 */
281 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 DEBUGIF("socket:buffer") {
288 DEBUGMSGT(("socket:buffer", "Set %s to %d\n",
289 buftype, size));
290 if ((getsockopt(s, SOL_SOCKET, optname, (void *) &curbuf,
291 &curbuflen) == 0)
292 && (curbuflen == sizeof(int))) {
293
294 DEBUGMSGT(("verbose:socket:buffer",
295 "Now %s is %d\n", buftype, curbuf));
296 }
297 }
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 if (curbuf < size) {
306 curbuf = _sock_buffer_maximize(s, optname, buftype, size);
307 if(-1 != curbuf)
308 size = curbuf;
309 }
310
311 } 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 DEBUGMSGTL(("socket:buffer", "couldn't set %s to %d\n",
323 buftype, size));
324
325 curbuf = _sock_buffer_maximize(s, optname, buftype, size);
326 if(-1 != curbuf)
327 size = curbuf;
328 }
329
330 return size;
331 #endif
332 }
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
netsnmp_set_non_blocking_mode(int sock,int non_blocking_mode)345 netsnmp_set_non_blocking_mode(int sock, int non_blocking_mode)
346 {
347 #ifdef WIN32
348 NETSNMP_IOCTLSOCKET_ARG arg;
349
350 arg = non_blocking_mode;
351 return ioctlsocket(sock, FIONBIO, &arg);
352 #else
353 int sockflags;
354
355 if ((sockflags = fcntl(sock, F_GETFL, 0)) >= 0) {
356 return fcntl(sock, F_SETFL,
357 non_blocking_mode ? sockflags | O_NONBLOCK
358 : sockflags & ~O_NONBLOCK);
359 } else
360 return -1;
361 #endif
362 }
363