Coverage Report

Created: 2026-08-28 09:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/snmplib/transports/snmpSocketBaseDomain.c
Line
Count
Source
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
#ifdef HAVE_NETINET_IN_H
34
#include <netinet/in.h>
35
#endif
36
#ifdef HAVE_NETINET_TCP_H
37
#include <netinet/tcp.h>
38
#endif
39
#include <errno.h>
40
41
#include <net-snmp/types.h>
42
#include <net-snmp/library/snmp_debug.h>
43
#include <net-snmp/library/tools.h>
44
#include <net-snmp/library/default_store.h>
45
#include <net-snmp/library/system.h>
46
#include <net-snmp/library/snmp_assert.h>
47
48
/* all sockets pretty much close the same way */
49
3.40k
int netsnmp_socketbase_close(netsnmp_transport *t) {
50
3.40k
    int rc = -1;
51
3.40k
    if (NETSNMP_IS_VALID_SOCKET(t->sock)) {
52
3.40k
#ifndef HAVE_CLOSESOCKET
53
3.40k
        rc = close(t->sock);
54
#else
55
        rc = closesocket(t->sock);
56
#endif
57
3.40k
        t->sock = NETSNMP_INVALID_SOCKET;
58
3.40k
    }
59
3.40k
    return rc;
60
3.40k
}
61
62
/*
63
 * find largest possible buffer between current size and specified size.
64
 *
65
 * Try to maximize the current buffer of type "optname"
66
 * to the maximum allowable size by the OS (as close to
67
 * size as possible)
68
 */
69
static int
70
_sock_buffer_maximize(NETSNMP_SOCKET sock, int optname, const char *buftype,
71
                      int size)
72
0
{
73
0
    int            curbuf = 0;
74
0
    socklen_t      curbuflen = sizeof(int);
75
0
    int            lo, mid, hi;
76
77
    /*
78
     * First we need to determine our current buffer
79
     */
80
0
    if ((getsockopt(sock, SOL_SOCKET, optname, (void *) &curbuf,
81
0
                    &curbuflen) == 0) 
82
0
            && (curbuflen == sizeof(int))) {
83
84
0
        DEBUGMSGTL(("verbose:socket:buffer:max", "Current %s is %d\n",
85
0
                    buftype, curbuf));
86
87
        /*
88
         * Let's not be stupid ... if we were asked for less than what we
89
         * already have, then forget about it
90
         */
91
0
        if (size <= curbuf) {
92
0
            DEBUGMSGTL(("verbose:socket:buffer:max",
93
0
                        "Requested %s <= current buffer\n", buftype));
94
0
            return curbuf;
95
0
        }
96
97
        /*
98
         * Do a binary search the optimal buffer within 1k of the point of
99
         * failure. This is rather bruteforce, but simple
100
         */
101
0
        hi = size;
102
0
        lo = curbuf;
103
104
0
        while (hi - lo > 1024) {
105
0
            mid = (lo + hi) / 2;
106
0
            if (setsockopt(sock, SOL_SOCKET, optname, (void *) &mid,
107
0
                        sizeof(int)) == 0) {
108
0
                lo = mid; /* Success: search between mid and hi */
109
0
            } else {
110
0
                hi = mid; /* Failed: search between lo and mid */
111
0
            }
112
0
        }
113
114
        /*
115
         * Now print if this optimization helped or not
116
         */
117
0
        if (getsockopt(sock, SOL_SOCKET, optname, (void *) &curbuf,
118
0
                    &curbuflen) == 0) {
119
0
            DEBUGMSGTL(("socket:buffer:max", 
120
0
                        "Maximized %s: %d\n",buftype, curbuf));
121
0
        } 
122
0
    } else {
123
        /*
124
         * There is really not a lot we can do anymore.
125
         * If the OS doesn't give us the current buffer, then what's the 
126
         * point in trying to make it better
127
         */
128
0
        DEBUGMSGTL(("socket:buffer:max", "Get %s failed ... giving up!\n",
129
0
                    buftype));
130
0
        curbuf = -1;
131
0
    }
132
133
0
    return curbuf;
134
0
}
135
136
137
static const char *
138
_sock_buf_type_get(int optname, int local)
139
6.80k
{
140
6.80k
    if (optname == SO_SNDBUF) {
141
3.40k
        if (local)
142
3.40k
            return "server send buffer";
143
0
        else
144
0
            return "client send buffer";
145
3.40k
    } else if (optname == SO_RCVBUF) {
146
3.40k
        if (local)
147
3.40k
            return "server receive buffer";
148
0
        else
149
0
            return "client receive buffer";
150
3.40k
    }
151
152
0
    return "unknown buffer";
153
6.80k
}
154
155
/*
156
 *
157
 * Get the requested buffersize, based on
158
 * - sockettype : client (local = 0) or server (local = 1) 
159
 * - buffertype : send (optname = SO_SNDBUF) or recv (SO_RCVBUF)
160
 *
161
 * In case a compile time buffer was specified, then use that one
162
 * if there was no runtime configuration override
163
 */
164
static int
165
_sock_buffer_size_get(int optname, int local, const char **buftype)
166
6.80k
{
167
6.80k
    int size;
168
169
6.80k
    if (NULL != buftype)
170
6.80k
        *buftype = _sock_buf_type_get(optname, local);
171
172
6.80k
    if (optname == SO_SNDBUF) {
173
3.40k
        if (local) {
174
3.40k
            size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
175
3.40k
                    NETSNMP_DS_LIB_SERVERSENDBUF);
176
#ifdef NETSNMP_DEFAULT_SERVER_SEND_BUF
177
            if (size <= 0)
178
               size = NETSNMP_DEFAULT_SERVER_SEND_BUF;
179
#endif
180
3.40k
        } else {
181
0
            size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
182
0
                    NETSNMP_DS_LIB_CLIENTSENDBUF);
183
#ifdef NETSNMP_DEFAULT_CLIENT_SEND_BUF
184
            if (size <= 0)
185
               size = NETSNMP_DEFAULT_CLIENT_SEND_BUF;
186
#endif
187
0
        }
188
3.40k
    } else if (optname == SO_RCVBUF) {
189
3.40k
        if (local) {
190
3.40k
            size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
191
3.40k
                    NETSNMP_DS_LIB_SERVERRECVBUF);
192
#ifdef NETSNMP_DEFAULT_SERVER_RECV_BUF
193
            if (size <= 0)
194
               size = NETSNMP_DEFAULT_SERVER_RECV_BUF;
195
#endif
196
3.40k
        } else {
197
0
            size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
198
0
                    NETSNMP_DS_LIB_CLIENTRECVBUF);
199
#ifdef NETSNMP_DEFAULT_CLIENT_RECV_BUF
200
            if (size <= 0)
201
               size = NETSNMP_DEFAULT_CLIENT_RECV_BUF;
202
#endif
203
0
        }
204
3.40k
    } else {
205
0
        size = 0;
206
0
    }
207
208
6.80k
    DEBUGMSGTL(("socket:buffer", "Requested %s is %d\n",
209
6.80k
                (buftype) ? *buftype : "unknown buffer", size));
210
211
6.80k
    return(size);
212
6.80k
}
213
214
/*
215
 * set socket buffer size
216
 *
217
 * @param sock   : socket descriptor
218
 * @param optname: SO_SNDBUF or SO_RCVBUF
219
 * @param local  : 1 for server, 0 for client
220
 * @param reqbuf : requested size, or 0 for default
221
 *
222
 * @retval    -1 : error
223
 * @retval    >0 : new buffer size
224
 */
225
int
226
netsnmp_sock_buffer_set(NETSNMP_SOCKET sock, int optname, int local, int size)
227
6.80k
{
228
#if ! defined(SO_SNDBUF) && ! defined(SO_RCVBUF)
229
    DEBUGMSGTL(("socket:buffer", "Changing socket buffer is not supported\n"));
230
    return -1;
231
#else
232
6.80k
    const char     *buftype;
233
6.80k
    int            curbuf = 0;
234
6.80k
    socklen_t      curbuflen = sizeof(int);
235
236
#   ifndef  SO_SNDBUF
237
    if (SO_SNDBUF == optname) {
238
        DEBUGMSGTL(("socket:buffer",
239
                    "Changing socket send buffer is not supported\n"));
240
        return -1;
241
    }
242
#   endif                          /*SO_SNDBUF */
243
#   ifndef  SO_RCVBUF
244
    if (SO_RCVBUF == optname) {
245
        DEBUGMSGTL(("socket:buffer",
246
                    "Changing socket receive buffer is not supported\n"));
247
        return -1;
248
    }
249
#   endif                          /*SO_RCVBUF */
250
251
    /*
252
     * What is the requested buffer size ?
253
     */
254
6.80k
    if (0 == size)
255
6.80k
        size = _sock_buffer_size_get(optname, local, &buftype);
256
0
    else {
257
0
        buftype = _sock_buf_type_get(optname, local);
258
0
        DEBUGMSGT(("verbose:socket:buffer", "Requested %s is %d\n",
259
0
                   buftype, size));
260
0
    }
261
262
6.80k
    if ((getsockopt(sock, SOL_SOCKET, optname, (void *) &curbuf,
263
6.80k
                    &curbuflen) == 0) 
264
6.80k
        && (curbuflen == sizeof(int))) {
265
        
266
6.80k
        DEBUGMSGT(("verbose:socket:buffer", "Original %s is %d\n",
267
6.80k
                   buftype, curbuf));
268
6.80k
        if (curbuf >= size) {
269
6.80k
            DEBUGMSGT(("verbose:socket:buffer",
270
6.80k
                      "New %s size is smaller than original!\n", buftype));
271
6.80k
        }
272
6.80k
    }
273
274
    /*
275
     * If the buffersize was not specified or it was a negative value
276
     * then don't change the OS buffers at all
277
     */
278
6.80k
    if (size <= 0) {
279
6.80k
       DEBUGMSGT(("socket:buffer",
280
6.80k
                    "%s not valid or not specified; using OS default(%d)\n",
281
6.80k
                    buftype,curbuf));
282
6.80k
       return curbuf;
283
6.80k
    }
284
285
    /*
286
     * Try to set the requested send buffer
287
     */
288
0
    if (setsockopt(sock, SOL_SOCKET, optname, (void *) &size, sizeof(int)) == 0) {
289
        /*
290
         * Because some platforms lie about the actual buffer that has been 
291
         * set (Linux will always say it worked ...), we print some 
292
         * diagnostic output for debugging
293
         */
294
0
        DEBUGIF("socket:buffer") {
295
0
            DEBUGMSGT(("socket:buffer", "Set %s to %d\n",
296
0
                       buftype, size));
297
0
            if ((getsockopt(sock, SOL_SOCKET, optname, (void *) &curbuf,
298
0
                            &curbuflen) == 0) 
299
0
                    && (curbuflen == sizeof(int))) {
300
301
0
                DEBUGMSGT(("verbose:socket:buffer",
302
0
                           "Now %s is %d\n", buftype, curbuf));
303
0
            }
304
0
        }
305
        /*
306
         * If the new buffer is smaller than the size we requested, we will
307
         * try to increment the new buffer with 1k increments 
308
         * (this will sometime allow us to reach a more optimal buffer.)
309
         *   For example : On Solaris, if the max OS buffer is 100k and you
310
         *   request 110k, you end up with the default 8k :-(
311
         */
312
0
        if (curbuf < size) {
313
0
            curbuf = _sock_buffer_maximize(sock, optname, buftype, size);
314
0
            if(-1 != curbuf)
315
0
                size = curbuf;
316
0
        }
317
318
0
    } else {
319
        /*
320
         * Obviously changing the buffer failed, most like like because we 
321
         * requested a buffer greater than the OS limit.
322
         * Therefore we need to search for an optimal buffer that is close
323
         * enough to the point of failure.
324
         * This will allow us to reach a more optimal buffer.
325
         *   For example : On Solaris, if the max OS buffer is 100k and you 
326
         *   request 110k, you end up with the default 8k :-(
327
         *   After this quick seach we would get 1k close to 100k (the max)
328
         */
329
0
        DEBUGMSGTL(("socket:buffer", "couldn't set %s to %d\n",
330
0
                    buftype, size));
331
332
0
        curbuf = _sock_buffer_maximize(sock, optname, buftype, size);
333
0
        if(-1 != curbuf)
334
0
            size = curbuf;
335
0
    }
336
337
0
    return size;
338
6.80k
#endif
339
6.80k
}
340
341
342
/**
343
 * Sets the mode of a socket for all subsequent I/O operations.
344
 *
345
 * @param[in] sock Socket descriptor (Unix) or socket handle (Windows).
346
 * @param[in] non_blocking_mode I/O mode: non-zero selects non-blocking mode;
347
 *   zero selects blocking mode.
348
 *
349
 * @return zero upon success and a negative value upon error.
350
 */
351
int
352
netsnmp_set_non_blocking_mode(NETSNMP_SOCKET sock, int non_blocking_mode)
353
0
{
354
#ifdef WIN32
355
    NETSNMP_IOCTLSOCKET_ARG arg;
356
357
    arg = non_blocking_mode;
358
    return ioctlsocket(sock, FIONBIO, &arg);
359
#else
360
0
    int             sockflags;
361
362
0
    if ((sockflags = fcntl(sock, F_GETFL, 0)) >= 0) {
363
0
        return fcntl(sock, F_SETFL,
364
0
                     non_blocking_mode ? sockflags | O_NONBLOCK
365
0
                     : sockflags & ~O_NONBLOCK);
366
0
    } else
367
0
        return -1;
368
0
#endif
369
0
}
370
371
/**
372
 * Sets or clears the TCP_NODELAY option on a socket.
373
 *
374
 * @param[in] sock Socket descriptor (Unix) or socket handle (Windows).
375
 * @param[in] nodelay Non-zero to enable TCP_NODELAY (disable Nagle's algorithm),
376
 *                    zero to disable TCP_NODELAY (enable Nagle's algorithm).
377
 *
378
 * @return zero upon success and a negative value upon error.
379
 */
380
int
381
netsnmp_set_tcp_nodelay(NETSNMP_SOCKET sock, int nodelay)
382
0
{
383
0
    int rc = -1;
384
385
0
#if defined(TCP_NODELAY)
386
0
    if (NETSNMP_IS_VALID_SOCKET(sock)) {
387
0
        rc = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&nodelay,
388
0
                        sizeof(nodelay));
389
0
        if (rc < 0) {
390
0
            DEBUGMSGTL(("socket:nodelay",
391
0
                        "could not set TCP_NODELAY to %d on socket %" NETSNMP_FMT_SKT ": %s\n",
392
0
                        nodelay, sock, strerror(errno)));
393
0
        } else {
394
0
            DEBUGMSGTL(("socket:nodelay",
395
0
                        "set TCP_NODELAY to %d on socket %" NETSNMP_FMT_SKT "\n",
396
0
                        nodelay, sock));
397
0
        }
398
0
    }
399
0
#endif
400
0
    return rc;
401
0
}