Coverage Report

Created: 2026-08-13 09:19

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