/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 | } |