Coverage Report

Created: 2024-07-27 06:05

/src/net-snmp/snmplib/sd-daemon.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Systemd integration parts.
3
 *
4
 * Most of this file is directly copied from systemd sources.
5
 * Changes:
6
 * - all exported functions were renamed to have a netsnmp_ prefix
7
 * - all nonexported functions were made static
8
 * - includes were  changed to match Net-SNMP style.
9
 * - removed gcc export macros
10
 * - removed POSIX message queues
11
 * - removed log level macros
12
 * - removed unused functions
13
 * - made SD_LISTEN_FDS_START as it is only used internally
14
 */
15
16
#include <net-snmp/net-snmp-config.h>
17
#include <net-snmp/net-snmp-features.h>
18
#include <net-snmp/types.h>
19
#include <net-snmp/library/tools.h>
20
#include <net-snmp/library/snmp_debug.h>
21
22
#ifndef NETSNMP_NO_SYSTEMD
23
24
/***
25
  Copyright 2010 Lennart Poettering
26
27
  Permission is hereby granted, free of charge, to any person
28
  obtaining a copy of this software and associated documentation files
29
  (the "Software"), to deal in the Software without restriction,
30
  including without limitation the rights to use, copy, modify, merge,
31
  publish, distribute, sublicense, and/or sell copies of the Software,
32
  and to permit persons to whom the Software is furnished to do so,
33
  subject to the following conditions:
34
35
  The above copyright notice and this permission notice shall be
36
  included in all copies or substantial portions of the Software.
37
38
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
39
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
40
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
41
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
42
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
43
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
44
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
45
  SOFTWARE.
46
***/
47
48
#ifndef _GNU_SOURCE
49
#define _GNU_SOURCE
50
#endif
51
52
#include <sys/types.h>
53
#include <sys/stat.h>
54
#include <sys/socket.h>
55
#include <sys/un.h>
56
#include <fcntl.h>
57
#include <netinet/in.h>
58
#include <stdlib.h>
59
#include <errno.h>
60
#include <unistd.h>
61
#include <string.h>
62
#include <stdarg.h>
63
#include <stdio.h>
64
#include <stddef.h>
65
#include <limits.h>
66
67
#include <net-snmp/library/sd-daemon.h>
68
69
/* The first passed file descriptor is fd 3 */
70
0
#define SD_LISTEN_FDS_START 3
71
72
0
int netsnmp_sd_listen_fds(int unset_environment) {
73
74
0
        int r, fd;
75
0
        const char *e;
76
0
        char *p = NULL;
77
0
        unsigned long l;
78
79
0
        if (!(e = getenv("LISTEN_PID"))) {
80
0
                r = 0;
81
0
                goto finish;
82
0
        }
83
84
0
        errno = 0;
85
0
        l = strtoul(e, &p, 10);
86
87
0
        if (errno != 0) {
88
0
                r = -errno;
89
0
                goto finish;
90
0
        }
91
92
0
        if (!p || *p || l <= 0) {
93
0
                r = -EINVAL;
94
0
                goto finish;
95
0
        }
96
97
        /* Is this for us? */
98
0
        if (getpid() != (pid_t) l) {
99
0
                r = 0;
100
0
                goto finish;
101
0
        }
102
103
0
        if (!(e = getenv("LISTEN_FDS"))) {
104
0
                r = 0;
105
0
                goto finish;
106
0
        }
107
108
0
        errno = 0;
109
0
        l = strtoul(e, &p, 10);
110
111
0
        if (errno != 0 || l != (int)l) {
112
0
                r = errno ? -errno : -EINVAL;
113
0
                goto finish;
114
0
        }
115
116
0
        if (!p || *p) {
117
0
                r = -EINVAL;
118
0
                goto finish;
119
0
        }
120
121
0
        for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
122
0
                int flags;
123
124
0
                if ((flags = fcntl(fd, F_GETFD)) < 0) {
125
0
                        r = -errno;
126
0
                        goto finish;
127
0
                }
128
129
0
                if (flags & FD_CLOEXEC)
130
0
                        continue;
131
132
0
                if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
133
0
                        r = -errno;
134
0
                        goto finish;
135
0
                }
136
0
        }
137
138
0
        r = (int) l;
139
140
0
finish:
141
0
        if (unset_environment) {
142
0
                unsetenv("LISTEN_PID");
143
0
                unsetenv("LISTEN_FDS");
144
0
        }
145
146
0
        return r;
147
0
}
148
149
0
static int sd_is_socket_internal(int fd, int type, int listening) {
150
0
        struct stat st_fd;
151
152
0
        if (fd < 0 || type < 0)
153
0
                return -EINVAL;
154
155
0
        if (fstat(fd, &st_fd) < 0)
156
0
                return -errno;
157
158
0
        if (!S_ISSOCK(st_fd.st_mode))
159
0
                return 0;
160
161
0
        if (type != 0) {
162
0
                int other_type = 0;
163
0
                socklen_t l = sizeof(other_type);
164
165
0
                if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
166
0
                        return -errno;
167
168
0
                if (l != sizeof(other_type))
169
0
                        return -EINVAL;
170
171
0
                if (other_type != type)
172
0
                        return 0;
173
0
        }
174
175
0
        if (listening >= 0) {
176
0
                int accepting = 0;
177
0
                socklen_t l = sizeof(accepting);
178
179
0
                if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
180
0
                        return -errno;
181
182
0
                if (l != sizeof(accepting))
183
0
                        return -EINVAL;
184
185
0
                if (!accepting != !listening)
186
0
                        return 0;
187
0
        }
188
189
0
        return 1;
190
0
}
191
192
union sockaddr_union {
193
        struct sockaddr sa;
194
        struct sockaddr_in in4;
195
        struct sockaddr_in6 in6;
196
        struct sockaddr_un un;
197
        struct sockaddr_storage storage;
198
};
199
200
0
static int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
201
0
        union sockaddr_union sockaddr;
202
0
        socklen_t l;
203
0
        int r;
204
205
0
        if (family != 0 && family != AF_INET && family != AF_INET6)
206
0
                return -EINVAL;
207
208
0
        if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
209
0
                return r;
210
211
0
        memset(&sockaddr, 0, sizeof(sockaddr));
212
0
        l = sizeof(sockaddr);
213
214
0
        if (getsockname(fd, &sockaddr.sa, &l) < 0)
215
0
                return -errno;
216
217
0
        if (l < sizeof(sa_family_t))
218
0
                return -EINVAL;
219
220
0
        if (sockaddr.sa.sa_family != AF_INET &&
221
0
            sockaddr.sa.sa_family != AF_INET6)
222
0
                return 0;
223
224
0
        if (family > 0)
225
0
                if (sockaddr.sa.sa_family != family)
226
0
                        return 0;
227
228
0
        if (port > 0) {
229
0
                if (sockaddr.sa.sa_family == AF_INET) {
230
0
                        if (l < sizeof(struct sockaddr_in))
231
0
                                return -EINVAL;
232
233
0
                        return htons(port) == sockaddr.in4.sin_port;
234
0
                } else {
235
0
                        if (l < sizeof(struct sockaddr_in6))
236
0
                                return -EINVAL;
237
238
0
                        return htons(port) == sockaddr.in6.sin6_port;
239
0
                }
240
0
        }
241
242
0
        return 1;
243
0
}
244
245
0
static int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
246
0
        union sockaddr_union sockaddr;
247
0
        socklen_t l;
248
0
        int r;
249
250
0
        if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
251
0
                return r;
252
253
0
        memset(&sockaddr, 0, sizeof(sockaddr));
254
0
        l = sizeof(sockaddr);
255
256
0
        if (getsockname(fd, &sockaddr.sa, &l) < 0)
257
0
                return -errno;
258
259
0
        if (l < sizeof(sa_family_t))
260
0
                return -EINVAL;
261
262
0
        if (sockaddr.sa.sa_family != AF_UNIX)
263
0
                return 0;
264
265
0
        if (path) {
266
0
                if (length <= 0)
267
0
                        length = strlen(path);
268
269
0
                if (length <= 0)
270
                        /* Unnamed socket */
271
0
                        return l == offsetof(struct sockaddr_un, sun_path);
272
273
0
                if (path[0])
274
                        /* Normal path socket */
275
0
                        return
276
0
                                (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
277
0
                                memcmp(path, sockaddr.un.sun_path, length+1) == 0;
278
0
                else
279
                        /* Abstract namespace socket */
280
0
                        return
281
0
                                (l == offsetof(struct sockaddr_un, sun_path) + length) &&
282
0
                                memcmp(path, sockaddr.un.sun_path, length) == 0;
283
0
        }
284
285
0
        return 1;
286
0
}
287
288
0
int netsnmp_sd_notify(int unset_environment, const char *state) {
289
0
        int fd = -1, r;
290
0
        struct msghdr msghdr;
291
0
        struct iovec iovec;
292
0
        union sockaddr_union sockaddr;
293
0
        const char *e;
294
295
0
        if (!state) {
296
0
                r = -EINVAL;
297
0
                goto finish;
298
0
        }
299
300
0
        if (!(e = getenv("NOTIFY_SOCKET")))
301
0
                return 0;
302
303
        /* Must be an abstract socket, or an absolute path */
304
0
        if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
305
0
                r = -EINVAL;
306
0
                goto finish;
307
0
        }
308
309
0
        if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
310
0
                r = -errno;
311
0
                goto finish;
312
0
        }
313
314
0
        memset(&sockaddr, 0, sizeof(sockaddr));
315
0
        sockaddr.sa.sa_family = AF_UNIX;
316
0
        strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
317
318
0
        if (sockaddr.un.sun_path[0] == '@')
319
0
                sockaddr.un.sun_path[0] = 0;
320
321
0
        memset(&iovec, 0, sizeof(iovec));
322
0
        iovec.iov_base = NETSNMP_REMOVE_CONST(char *, state);
323
0
        iovec.iov_len = strlen(state);
324
325
0
        memset(&msghdr, 0, sizeof(msghdr));
326
0
        msghdr.msg_name = &sockaddr;
327
0
        msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
328
329
0
        if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
330
0
                msghdr.msg_namelen = sizeof(struct sockaddr_un);
331
332
0
        msghdr.msg_iov = &iovec;
333
0
        msghdr.msg_iovlen = 1;
334
335
0
        if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
336
0
                r = -errno;
337
0
                goto finish;
338
0
        }
339
340
0
        r = 1;
341
342
0
finish:
343
0
        if (unset_environment)
344
0
                unsetenv("NOTIFY_SOCKET");
345
346
0
        if (fd >= 0)
347
0
                close(fd);
348
349
0
        return r;
350
0
}
351
352
/* End of original sd-daemon.c from systemd sources */
353
354
int
355
netsnmp_sd_find_inet_socket(int family, int type, int listening, int port)
356
0
{
357
0
    int count, fd;
358
359
0
    count = netsnmp_sd_listen_fds(0);
360
0
    if (count <= 0) {
361
0
        DEBUGMSGTL(("systemd:find_inet_socket", "No LISTEN_FDS found.\n"));
362
0
        return -1;
363
0
    }
364
0
    DEBUGMSGTL(("systemd:find_inet_socket", "LISTEN_FDS reports %d sockets.\n",
365
0
            count));
366
367
0
    for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
368
0
        int rc = sd_is_socket_inet(fd, family, type, listening, port);
369
0
        if (rc < 0)
370
0
            DEBUGMSGTL(("systemd:find_inet_socket",
371
0
                    "sd_is_socket_inet error: %d\n", rc));
372
0
        if (rc > 0) {
373
0
            DEBUGMSGTL(("systemd:find_inet_socket",
374
0
                    "Found the socket in LISTEN_FDS\n"));
375
0
            return fd;
376
0
        }
377
0
    }
378
0
    DEBUGMSGTL(("systemd:find_inet_socket", "Socket not found in LISTEN_FDS\n"));
379
0
    return -1;
380
0
}
381
382
int
383
netsnmp_sd_find_unix_socket(int type, int listening, const char *path)
384
0
{
385
0
    int count, fd;
386
387
0
    count = netsnmp_sd_listen_fds(0);
388
0
    if (count <= 0) {
389
0
        DEBUGMSGTL(("systemd:find_unix_socket", "No LISTEN_FDS found.\n"));
390
0
        return -1;
391
0
    }
392
0
    DEBUGMSGTL(("systemd:find_unix_socket", "LISTEN_FDS reports %d sockets.\n",
393
0
            count));
394
395
0
    for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
396
0
        int rc = sd_is_socket_unix(fd, type, listening, path, 0);
397
0
        if (rc < 0)
398
0
            DEBUGMSGTL(("systemd:find_unix_socket",
399
0
                    "sd_is_socket_unix error: %d\n", rc));
400
0
        if (rc > 0) {
401
0
            DEBUGMSGTL(("systemd:find_unix_socket",
402
0
                    "Found the socket in LISTEN_FDS\n"));
403
0
            return fd;
404
0
        }
405
0
    }
406
0
    DEBUGMSGTL(("systemd:find_unix_socket", "Socket not found in LISTEN_FDS\n"));
407
0
    return -1;
408
0
}
409
410
#endif /* ! NETSNMP_NO_SYSTEMD */