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