Coverage Report

Created: 2024-05-20 01:00

/src/openiked-portable/compat/imsg.c
Line
Count
Source (jump to first uncovered line)
1
/*  $OpenBSD: imsg.c,v 1.19 2023/06/19 17:19:50 claudio Exp $ */
2
3
/*
4
 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/queue.h>
21
#include <sys/socket.h>
22
#include <sys/uio.h>
23
24
#include <errno.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#include <unistd.h>
28
29
#include "openbsd-compat.h"
30
#include "imsg.h"
31
32
int  imsg_fd_overhead = 0;
33
34
static int   imsg_get_fd(struct imsgbuf *);
35
36
void
37
imsg_init(struct imsgbuf *ibuf, int fd)
38
0
{
39
0
  msgbuf_init(&ibuf->w);
40
0
  memset(&ibuf->r, 0, sizeof(ibuf->r));
41
0
  ibuf->fd = fd;
42
0
  ibuf->w.fd = fd;
43
0
  ibuf->pid = getpid();
44
0
  TAILQ_INIT(&ibuf->fds);
45
0
}
46
47
#ifdef _WIN32
48
ssize_t
49
imsg_read(struct imsgbuf *ibuf)
50
{
51
  ssize_t      n;
52
  uint8_t     *base;
53
  size_t       len;
54
55
  base = ibuf->r.buf + ibuf->r.wpos;
56
  len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
57
58
  while ((n = recv(ibuf->fd, base, len, 0)) == -1) {
59
    if (errno != EINTR)
60
      return (-1);
61
  }
62
  ibuf->r.wpos += n;
63
  return (n);
64
}
65
#else
66
ssize_t
67
imsg_read(struct imsgbuf *ibuf)
68
0
{
69
0
  struct msghdr    msg;
70
0
  struct cmsghdr    *cmsg;
71
0
  union {
72
0
    struct cmsghdr hdr;
73
0
    char  buf[CMSG_SPACE(sizeof(int) * 1)];
74
0
  } cmsgbuf;
75
0
  struct iovec     iov;
76
0
  ssize_t      n = -1;
77
0
  int      fd;
78
0
  struct imsg_fd    *ifd;
79
80
0
  memset(&msg, 0, sizeof(msg));
81
0
  memset(&cmsgbuf, 0, sizeof(cmsgbuf));
82
83
0
  iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
84
0
  iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
85
0
  msg.msg_iov = &iov;
86
0
  msg.msg_iovlen = 1;
87
0
  msg.msg_control = &cmsgbuf.buf;
88
0
  msg.msg_controllen = sizeof(cmsgbuf.buf);
89
90
0
  if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
91
0
    return (-1);
92
93
0
again:
94
0
  if (getdtablecount() + imsg_fd_overhead +
95
0
      (int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))
96
0
      >= getdtablesize()) {
97
0
    errno = EAGAIN;
98
0
    free(ifd);
99
0
    return (-1);
100
0
  }
101
102
0
  if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
103
0
    if (errno == EINTR)
104
0
      goto again;
105
0
    goto fail;
106
0
  }
107
108
0
  ibuf->r.wpos += n;
109
110
0
  for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
111
0
      cmsg = CMSG_NXTHDR(&msg, cmsg)) {
112
0
    if (cmsg->cmsg_level == SOL_SOCKET &&
113
0
        cmsg->cmsg_type == SCM_RIGHTS) {
114
0
      int i;
115
0
      int j;
116
117
      /*
118
       * We only accept one file descriptor.  Due to C
119
       * padding rules, our control buffer might contain
120
       * more than one fd, and we must close them.
121
       */
122
0
      j = ((char *)cmsg + cmsg->cmsg_len -
123
0
          (char *)CMSG_DATA(cmsg)) / sizeof(int);
124
0
      for (i = 0; i < j; i++) {
125
0
        fd = ((int *)CMSG_DATA(cmsg))[i];
126
0
        if (ifd != NULL) {
127
0
          ifd->fd = fd;
128
0
          TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
129
0
              entry);
130
0
          ifd = NULL;
131
0
        } else
132
0
          close(fd);
133
0
      }
134
0
    }
135
    /* we do not handle other ctl data level */
136
0
  }
137
138
0
fail:
139
0
  free(ifd);
140
0
  return (n);
141
0
}
142
#endif
143
144
ssize_t
145
imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
146
0
{
147
0
  size_t       av, left, datalen;
148
149
0
  av = ibuf->r.wpos;
150
151
0
  if (IMSG_HEADER_SIZE > av)
152
0
    return (0);
153
154
0
  memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
155
0
  if (imsg->hdr.len < IMSG_HEADER_SIZE ||
156
0
      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 (datalen != 0)
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
188
imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
189
    int fd, const void *data, uint16_t datalen)
190
0
{
191
0
  struct ibuf *wbuf;
192
193
0
  if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
194
0
    return (-1);
195
196
0
  if (imsg_add(wbuf, data, datalen) == -1)
197
0
    return (-1);
198
199
0
  ibuf_fd_set(wbuf, fd);
200
201
0
  imsg_close(ibuf, wbuf);
202
203
0
  return (1);
204
0
}
205
206
int
207
imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
208
    int fd, const struct iovec *iov, int iovcnt)
209
0
{
210
0
  struct ibuf *wbuf;
211
0
  int    i, datalen = 0;
212
213
0
  for (i = 0; i < iovcnt; i++)
214
0
    datalen += iov[i].iov_len;
215
216
0
  if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
217
0
    return (-1);
218
219
0
  for (i = 0; i < iovcnt; i++)
220
0
    if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
221
0
      return (-1);
222
223
0
  ibuf_fd_set(wbuf, fd);
224
225
0
  imsg_close(ibuf, wbuf);
226
227
0
  return (1);
228
0
}
229
230
int
231
imsg_compose_ibuf(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid,
232
    pid_t pid, struct ibuf *buf)
233
0
{
234
0
  struct ibuf *wbuf = NULL;
235
0
  struct imsg_hdr  hdr;
236
0
  int save_errno;
237
238
0
  if (ibuf_size(buf) + IMSG_HEADER_SIZE > MAX_IMSGSIZE) {
239
0
    errno = ERANGE;
240
0
    goto fail;
241
0
  }
242
243
0
  hdr.type = type;
244
0
  hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
245
0
  hdr.flags = 0;
246
0
  hdr.peerid = peerid;
247
0
  if ((hdr.pid = pid) == 0)
248
0
    hdr.pid = ibuf->pid;
249
250
0
  if ((wbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
251
0
    goto fail;
252
0
  if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
253
0
    goto fail;
254
255
0
  ibuf_close(&ibuf->w, wbuf);
256
0
  ibuf_close(&ibuf->w, buf);
257
0
  return (1);
258
259
0
 fail:
260
0
  save_errno = errno;
261
0
  ibuf_free(buf);
262
0
  ibuf_free(wbuf);
263
0
  errno = save_errno;
264
0
  return (-1);
265
0
}
266
267
struct ibuf *
268
imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
269
    uint16_t datalen)
270
0
{
271
0
  struct ibuf *wbuf;
272
0
  struct imsg_hdr  hdr;
273
274
0
  datalen += IMSG_HEADER_SIZE;
275
0
  if (datalen > MAX_IMSGSIZE) {
276
0
    errno = ERANGE;
277
0
    return (NULL);
278
0
  }
279
280
0
  hdr.type = type;
281
0
  hdr.flags = 0;
282
0
  hdr.peerid = peerid;
283
0
  if ((hdr.pid = pid) == 0)
284
0
    hdr.pid = ibuf->pid;
285
0
  if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
286
0
    return (NULL);
287
0
  }
288
0
  if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
289
0
    return (NULL);
290
291
0
  return (wbuf);
292
0
}
293
294
int
295
imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
296
0
{
297
0
  if (datalen)
298
0
    if (ibuf_add(msg, data, datalen) == -1) {
299
0
      ibuf_free(msg);
300
0
      return (-1);
301
0
    }
302
0
  return (datalen);
303
0
}
304
305
void
306
imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
307
0
{
308
0
  struct imsg_hdr *hdr;
309
310
0
  hdr = (struct imsg_hdr *)msg->buf;
311
312
0
  hdr->flags &= ~IMSGF_HASFD;
313
0
  if (ibuf_fd_avail(msg))
314
0
    hdr->flags |= IMSGF_HASFD;
315
0
  hdr->len = ibuf_size(msg);
316
317
0
  ibuf_close(&ibuf->w, msg);
318
0
}
319
320
void
321
imsg_free(struct imsg *imsg)
322
0
{
323
0
  freezero(imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
324
0
}
325
326
static int
327
imsg_get_fd(struct imsgbuf *ibuf)
328
0
{
329
0
  int    fd;
330
0
  struct imsg_fd  *ifd;
331
332
0
  if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
333
0
    return (-1);
334
335
0
  fd = ifd->fd;
336
0
  TAILQ_REMOVE(&ibuf->fds, ifd, entry);
337
0
  free(ifd);
338
339
0
  return (fd);
340
0
}
341
342
int
343
imsg_flush(struct imsgbuf *ibuf)
344
0
{
345
0
  while (ibuf->w.queued)
346
0
    if (msgbuf_write(&ibuf->w) <= 0)
347
0
      return (-1);
348
0
  return (0);
349
0
}
350
351
void
352
imsg_clear(struct imsgbuf *ibuf)
353
0
{
354
0
  int fd;
355
356
0
  msgbuf_clear(&ibuf->w);
357
0
  while ((fd = imsg_get_fd(ibuf)) != -1)
358
0
    close(fd);
359
0
}