Coverage Report

Created: 2023-06-07 06:43

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