Coverage Report

Created: 2025-10-08 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/imsg.c
Line
Count
Source
1
// SPDX-License-Identifier: ISC
2
/*  $OpenBSD$ */
3
4
/*
5
 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6
 */
7
8
#include <zebra.h>
9
10
#include "memory.h"
11
#include "queue.h"
12
#include "imsg.h"
13
14
static int imsg_fd_overhead = 0;
15
16
static int imsg_get_fd(struct imsgbuf *);
17
18
#ifndef __OpenBSD__
19
/*
20
 * The original code calls getdtablecount() which is OpenBSD specific. Use
21
 * available_fds() from OpenSMTPD instead.
22
 */
23
static int available_fds(unsigned int n)
24
0
{
25
0
  unsigned int i;
26
0
  int ret, fds[256];
27
28
0
  if (n > (unsigned int)array_size(fds))
29
0
    return 1;
30
31
0
  ret = 0;
32
0
  for (i = 0; i < n; i++) {
33
0
    fds[i] = -1;
34
0
    if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
35
0
      if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT)
36
0
        fds[i] = socket(AF_INET6, SOCK_DGRAM, 0);
37
0
      if (fds[i] < 0) {
38
0
        ret = 1;
39
0
        break;
40
0
      }
41
0
    }
42
0
  }
43
44
0
  for (i = 0; i < n && fds[i] >= 0; i++)
45
0
    close(fds[i]);
46
47
0
  return (ret);
48
0
}
49
#endif
50
51
void imsg_init(struct imsgbuf *ibuf, int fd)
52
0
{
53
0
  msgbuf_init(&ibuf->w);
54
0
  memset(&ibuf->r, 0, sizeof(ibuf->r));
55
0
  ibuf->fd = fd;
56
0
  ibuf->w.fd = fd;
57
0
  ibuf->pid = getpid();
58
0
  TAILQ_INIT(&ibuf->fds);
59
0
}
60
61
ssize_t imsg_read(struct imsgbuf *ibuf)
62
0
{
63
0
  struct msghdr msg;
64
0
  struct cmsghdr *cmsg;
65
0
  union {
66
0
    struct cmsghdr hdr;
67
0
    char buf[CMSG_SPACE(sizeof(int) * 1)];
68
0
  } cmsgbuf;
69
0
  struct iovec iov;
70
0
  ssize_t n;
71
0
  int fd;
72
0
  struct imsg_fd *ifd;
73
74
0
  memset(&msg, 0, sizeof(msg));
75
0
  memset(&cmsgbuf, 0, sizeof(cmsgbuf));
76
77
0
  iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
78
0
  iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
79
0
  msg.msg_iov = &iov;
80
0
  msg.msg_iovlen = 1;
81
0
  msg.msg_control = &cmsgbuf.buf;
82
0
  msg.msg_controllen = sizeof(cmsgbuf.buf);
83
84
0
  if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
85
0
    return -1;
86
87
0
again:
88
#ifdef __OpenBSD__
89
  if (getdtablecount() + imsg_fd_overhead
90
        + (int)((CMSG_SPACE(sizeof(int)) - CMSG_SPACE(0))
91
          / sizeof(int))
92
      >= getdtablesize()) {
93
#else
94
0
  if (available_fds(imsg_fd_overhead
95
0
        + (CMSG_SPACE(sizeof(int)) - CMSG_SPACE(0))
96
0
            / sizeof(int))) {
97
0
#endif
98
0
    errno = EAGAIN;
99
0
    free(ifd);
100
0
    return -1;
101
0
  }
102
103
0
  n = recvmsg(ibuf->fd, &msg, 0);
104
0
  if (n == -1) {
105
0
    if (errno == EINTR)
106
0
      goto again;
107
0
    goto fail;
108
0
  }
109
110
0
  ibuf->r.wpos += n;
111
112
0
  for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
113
0
       cmsg = CMSG_NXTHDR(&msg, cmsg)) {
114
0
    if (cmsg->cmsg_level == SOL_SOCKET
115
0
        && cmsg->cmsg_type == SCM_RIGHTS) {
116
0
      int i;
117
0
      int j;
118
119
      /*
120
       * We only accept one file descriptor.  Due to C
121
       * padding rules, our control buffer might contain
122
       * more than one fd, and we must close them.
123
       */
124
0
      j = ((char *)cmsg + cmsg->cmsg_len
125
0
           - (char *)CMSG_DATA(cmsg))
126
0
          / sizeof(int);
127
0
      for (i = 0; i < j; i++) {
128
0
        fd = ((int *)CMSG_DATA(cmsg))[i];
129
0
        if (ifd != NULL) {
130
0
          ifd->fd = fd;
131
0
          TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
132
0
                entry);
133
0
          ifd = NULL;
134
0
        } else
135
0
          close(fd);
136
0
      }
137
0
    }
138
    /* we do not handle other ctl data level */
139
0
  }
140
141
0
fail:
142
0
  free(ifd);
143
0
  return (n);
144
0
}
145
146
ssize_t imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
147
0
{
148
0
  size_t av, left, datalen;
149
150
0
  av = ibuf->r.wpos;
151
152
0
  if (IMSG_HEADER_SIZE > av)
153
0
    return 0;
154
155
0
  memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
156
0
  if (imsg->hdr.len < IMSG_HEADER_SIZE || imsg->hdr.len > MAX_IMSGSIZE) {
157
0
    errno = ERANGE;
158
0
    return -1;
159
0
  }
160
0
  if (imsg->hdr.len > av)
161
0
    return 0;
162
0
  datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
163
0
  ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
164
0
  if (datalen == 0)
165
0
    imsg->data = NULL;
166
0
  else if ((imsg->data = malloc(datalen)) == NULL)
167
0
    return -1;
168
169
0
  if (imsg->hdr.flags & IMSGF_HASFD)
170
0
    imsg->fd = imsg_get_fd(ibuf);
171
0
  else
172
0
    imsg->fd = -1;
173
174
0
  if (imsg->data)
175
0
    memcpy(imsg->data, ibuf->r.rptr, datalen);
176
177
0
  if (imsg->hdr.len < av) {
178
0
    left = av - imsg->hdr.len;
179
0
    memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
180
0
    ibuf->r.wpos = left;
181
0
  } else
182
0
    ibuf->r.wpos = 0;
183
184
0
  return (datalen + IMSG_HEADER_SIZE);
185
0
}
186
187
int imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid,
188
     pid_t pid, int fd, const void *data, uint16_t datalen)
189
0
{
190
0
  struct ibuf *wbuf;
191
192
0
  if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
193
0
    return -1;
194
195
0
  if (imsg_add(wbuf, data, datalen) == -1)
196
0
    return -1;
197
198
0
  wbuf->fd = fd;
199
200
0
  imsg_close(ibuf, wbuf);
201
202
0
  return 1;
203
0
}
204
205
int imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid,
206
      pid_t pid, int fd, const struct iovec *iov, int iovcnt)
207
0
{
208
0
  struct ibuf *wbuf;
209
0
  int i, datalen = 0;
210
211
0
  for (i = 0; i < iovcnt; i++)
212
0
    datalen += iov[i].iov_len;
213
214
0
  if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
215
0
    return -1;
216
217
0
  for (i = 0; i < iovcnt; i++)
218
0
    if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
219
0
      return -1;
220
221
0
  wbuf->fd = fd;
222
223
0
  imsg_close(ibuf, wbuf);
224
225
0
  return 1;
226
0
}
227
228
/* ARGSUSED */
229
struct ibuf *imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid,
230
       pid_t pid, uint16_t datalen)
231
0
{
232
0
  struct ibuf *wbuf;
233
0
  struct imsg_hdr hdr;
234
235
0
  memset(&hdr, 0x00, IMSG_HEADER_SIZE);
236
237
0
  datalen += IMSG_HEADER_SIZE;
238
0
  if (datalen > MAX_IMSGSIZE) {
239
0
    errno = ERANGE;
240
0
    return NULL;
241
0
  }
242
243
0
  hdr.type = type;
244
0
  hdr.flags = 0;
245
0
  hdr.peerid = peerid;
246
0
  if ((hdr.pid = pid) == 0)
247
0
    hdr.pid = ibuf->pid;
248
0
  if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
249
0
    return NULL;
250
0
  }
251
0
  if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
252
0
    return NULL;
253
254
0
  return (wbuf);
255
0
}
256
257
int imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
258
0
{
259
0
  if (datalen)
260
0
    if (ibuf_add(msg, data, datalen) == -1) {
261
0
      ibuf_free(msg);
262
0
      return -1;
263
0
    }
264
0
  return (datalen);
265
0
}
266
267
void imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
268
0
{
269
0
  struct imsg_hdr *hdr;
270
271
0
  hdr = (struct imsg_hdr *)msg->buf;
272
273
0
  hdr->flags &= ~IMSGF_HASFD;
274
0
  if (msg->fd != -1)
275
0
    hdr->flags |= IMSGF_HASFD;
276
277
0
  hdr->len = (uint16_t)msg->wpos;
278
279
0
  ibuf_close(&ibuf->w, msg);
280
0
}
281
282
void imsg_free(struct imsg *imsg)
283
0
{
284
0
  free(imsg->data);
285
0
}
286
287
int imsg_get_fd(struct imsgbuf *ibuf)
288
0
{
289
0
  int fd;
290
0
  struct imsg_fd *ifd;
291
292
0
  if ((ifd = TAILQ_POP_FIRST(&ibuf->fds, entry)) == NULL)
293
0
    return -1;
294
295
0
  fd = ifd->fd;
296
0
  free(ifd);
297
298
0
  return (fd);
299
0
}
300
301
int imsg_flush(struct imsgbuf *ibuf)
302
0
{
303
0
  while (ibuf->w.queued)
304
0
    if (msgbuf_write(&ibuf->w) <= 0)
305
0
      return -1;
306
0
  return 0;
307
0
}
308
309
void imsg_clear(struct imsgbuf *ibuf)
310
0
{
311
0
  int fd;
312
313
0
  msgbuf_clear(&ibuf->w);
314
0
  while ((fd = imsg_get_fd(ibuf)) != -1)
315
0
    close(fd);
316
0
}