1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 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
#ifdef 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
NETSNMP_IOCTLSOCKET_ARG 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
}
|