diff options
Diffstat (limited to 'snmplib/transports/snmpSocketBaseDomain.c')
-rw-r--r-- | snmplib/transports/snmpSocketBaseDomain.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/snmplib/transports/snmpSocketBaseDomain.c b/snmplib/transports/snmpSocketBaseDomain.c new file mode 100644 index 0000000..3fb6632 --- /dev/null +++ b/snmplib/transports/snmpSocketBaseDomain.c @@ -0,0 +1,362 @@ +/** + * @file snmpSocketBaseDomain.c + * + * @brief Socket support functions. + */ + +#include <net-snmp/net-snmp-config.h> + +#include <net-snmp/types.h> +#include <net-snmp/library/snmpSocketBaseDomain.h> + +#include <stddef.h> +#include <stdio.h> +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <sys/types.h> +#include <ctype.h> +#if HAVE_STDLIB_H +#include <stdlib.h> +#endif +#if HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif +#if HAVE_FCNTL_H +#include <fcntl.h> +#endif +#if HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <errno.h> + +#include <net-snmp/types.h> +#include <net-snmp/library/snmp_debug.h> +#include <net-snmp/library/tools.h> +#include <net-snmp/library/default_store.h> +#include <net-snmp/library/system.h> +#include <net-snmp/library/snmp_assert.h> + +/* all sockets pretty much close the same way */ +int netsnmp_socketbase_close(netsnmp_transport *t) { + int rc = -1; + if (t->sock >= 0) { +#ifndef HAVE_CLOSESOCKET + rc = close(t->sock); +#else + rc = closesocket(t->sock); +#endif + t->sock = -1; + } + return rc; +} + +/* + * find largest possible buffer between current size and specified size. + * + * Try to maximize the current buffer of type "optname" + * to the maximum allowable size by the OS (as close to + * size as possible) + */ +static int +_sock_buffer_maximize(int s, int optname, const char *buftype, int size) +{ + int curbuf = 0; + socklen_t curbuflen = sizeof(int); + int lo, mid, hi; + + /* + * First we need to determine our current buffer + */ + if ((getsockopt(s, SOL_SOCKET, optname, (void *) &curbuf, + &curbuflen) == 0) + && (curbuflen == sizeof(int))) { + + DEBUGMSGTL(("verbose:socket:buffer:max", "Current %s is %d\n", + buftype, curbuf)); + + /* + * Let's not be stupid ... if we were asked for less than what we + * already have, then forget about it + */ + if (size <= curbuf) { + DEBUGMSGTL(("verbose:socket:buffer:max", + "Requested %s <= current buffer\n", buftype)); + return curbuf; + } + + /* + * Do a binary search the optimal buffer within 1k of the point of + * failure. This is rather bruteforce, but simple + */ + hi = size; + lo = curbuf; + + while (hi - lo > 1024) { + mid = (lo + hi) / 2; + if (setsockopt(s, SOL_SOCKET, optname, (void *) &mid, + sizeof(int)) == 0) { + lo = mid; /* Success: search between mid and hi */ + } else { + hi = mid; /* Failed: search between lo and mid */ + } + } + + /* + * Now print if this optimization helped or not + */ + if (getsockopt(s,SOL_SOCKET, optname, (void *) &curbuf, + &curbuflen) == 0) { + DEBUGMSGTL(("socket:buffer:max", + "Maximized %s: %d\n",buftype, curbuf)); + } + } else { + /* + * There is really not a lot we can do anymore. + * If the OS doesn't give us the current buffer, then what's the + * point in trying to make it better + */ + DEBUGMSGTL(("socket:buffer:max", "Get %s failed ... giving up!\n", + buftype)); + curbuf = -1; + } + + return curbuf; +} + + +static const char * +_sock_buf_type_get(int optname, int local) +{ + if (optname == SO_SNDBUF) { + if (local) + return "server send buffer"; + else + return "client send buffer"; + } else if (optname == SO_RCVBUF) { + if (local) + return "server receive buffer"; + else + return "client receive buffer"; + } + + return "unknown buffer"; +} + +/* + * + * Get the requested buffersize, based on + * - sockettype : client (local = 0) or server (local = 1) + * - buffertype : send (optname = SO_SNDBUF) or recv (SO_RCVBUF) + * + * In case a compile time buffer was specified, then use that one + * if there was no runtime configuration override + */ +static int +_sock_buffer_size_get(int optname, int local, const char **buftype) +{ + int size; + + if (NULL != buftype) + *buftype = _sock_buf_type_get(optname, local); + + if (optname == SO_SNDBUF) { + if (local) { + size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_SERVERSENDBUF); +#ifdef NETSNMP_DEFAULT_SERVER_SEND_BUF + if (size <= 0) + size = NETSNMP_DEFAULT_SERVER_SEND_BUF; +#endif + } else { + size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_CLIENTSENDBUF); +#ifdef NETSNMP_DEFAULT_CLIENT_SEND_BUF + if (size <= 0) + size = NETSNMP_DEFAULT_CLIENT_SEND_BUF; +#endif + } + } else if (optname == SO_RCVBUF) { + if (local) { + size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_SERVERRECVBUF); +#ifdef NETSNMP_DEFAULT_SERVER_RECV_BUF + if (size <= 0) + size = NETSNMP_DEFAULT_SERVER_RECV_BUF; +#endif + } else { + size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_CLIENTRECVBUF); +#ifdef NETSNMP_DEFAULT_CLIENT_RECV_BUF + if (size <= 0) + size = NETSNMP_DEFAULT_CLIENT_RECV_BUF; +#endif + } + } else { + size = 0; + } + + DEBUGMSGTL(("socket:buffer", "Requested %s is %d\n", + (buftype) ? *buftype : "unknown buffer", size)); + + return(size); +} + +/* + * set socket buffer size + * + * @param ss : socket + * @param optname: SO_SNDBUF or SO_RCVBUF + * @param local : 1 for server, 0 for client + * @param reqbuf : requested size, or 0 for default + * + * @retval -1 : error + * @retval >0 : new buffer size + */ +int +netsnmp_sock_buffer_set(int s, int optname, int local, int size) +{ +#if ! defined(SO_SNDBUF) && ! defined(SO_RCVBUF) + DEBUGMSGTL(("socket:buffer", "Changing socket buffer is not supported\n")); + return -1; +#else + const char *buftype; + int curbuf = 0; + socklen_t curbuflen = sizeof(int); + +# ifndef SO_SNDBUF + if (SO_SNDBUF == optname) { + DEBUGMSGTL(("socket:buffer", + "Changing socket send buffer is not supported\n")); + return -1; + } +# endif /*SO_SNDBUF */ +# ifndef SO_RCVBUF + if (SO_RCVBUF == optname) { + DEBUGMSGTL(("socket:buffer", + "Changing socket receive buffer is not supported\n")); + return -1; + } +# endif /*SO_RCVBUF */ + + /* + * What is the requested buffer size ? + */ + if (0 == size) + size = _sock_buffer_size_get(optname, local, &buftype); + else { + buftype = _sock_buf_type_get(optname, local); + DEBUGMSGT(("verbose:socket:buffer", "Requested %s is %d\n", + buftype, size)); + } + + if ((getsockopt(s, SOL_SOCKET, optname, (void *) &curbuf, + &curbuflen) == 0) + && (curbuflen == sizeof(int))) { + + DEBUGMSGT(("verbose:socket:buffer", "Original %s is %d\n", + buftype, curbuf)); + if (curbuf >= size) { + DEBUGMSGT(("verbose:socket:buffer", + "New %s size is smaller than original!\n", buftype)); + } + } + + /* + * If the buffersize was not specified or it was a negative value + * then don't change the OS buffers at all + */ + if (size <= 0) { + DEBUGMSGT(("socket:buffer", + "%s not valid or not specified; using OS default(%d)\n", + buftype,curbuf)); + return curbuf; + } + + /* + * Try to set the requested send buffer + */ + if (setsockopt(s, SOL_SOCKET, optname, (void *) &size, sizeof(int)) == 0) { + /* + * Because some platforms lie about the actual buffer that has been + * set (Linux will always say it worked ...), we print some + * diagnostic output for debugging + */ + DEBUGIF("socket:buffer") { + DEBUGMSGT(("socket:buffer", "Set %s to %d\n", + buftype, size)); + if ((getsockopt(s, SOL_SOCKET, optname, (void *) &curbuf, + &curbuflen) == 0) + && (curbuflen == sizeof(int))) { + + DEBUGMSGT(("verbose:socket:buffer", + "Now %s is %d\n", buftype, curbuf)); + } + } + /* + * If the new buffer is smaller than the size we requested, we will + * try to increment the new buffer with 1k increments + * (this will sometime allow us to reach a more optimal buffer.) + * For example : On Solaris, if the max OS buffer is 100k and you + * request 110k, you end up with the default 8k :-( + */ + if (curbuf < size) { + curbuf = _sock_buffer_maximize(s, optname, buftype, size); + if(-1 != curbuf) + size = curbuf; + } + + } else { + /* + * Obviously changing the buffer failed, most like like because we + * requested a buffer greater than the OS limit. + * Therefore we need to search for an optimal buffer that is close + * enough to the point of failure. + * This will allow us to reach a more optimal buffer. + * For example : On Solaris, if the max OS buffer is 100k and you + * request 110k, you end up with the default 8k :-( + * After this quick seach we would get 1k close to 100k (the max) + */ + DEBUGMSGTL(("socket:buffer", "couldn't set %s to %d\n", + buftype, size)); + + curbuf = _sock_buffer_maximize(s, optname, buftype, size); + if(-1 != curbuf) + size = curbuf; + } + + return size; +#endif +} + + +/** + * Sets the mode of a socket for all subsequent I/O operations. + * + * @param[in] sock Socket descriptor (Unix) or socket handle (Windows). + * @param[in] non_blocking_mode I/O mode: non-zero selects non-blocking mode; + * zero selects blocking mode. + * + * @return zero upon success and a negative value upon error. + */ +int +netsnmp_set_non_blocking_mode(int sock, int non_blocking_mode) +{ +#ifdef WIN32 + u_long arg; + + arg = non_blocking_mode; + return ioctlsocket(sock, FIONBIO, &arg); +#else + int sockflags; + + if ((sockflags = fcntl(sock, F_GETFL, 0)) >= 0) { + return fcntl(sock, F_SETFL, + non_blocking_mode ? sockflags | O_NONBLOCK + : sockflags & ~O_NONBLOCK); + } else + return -1; +#endif +} |