jcs's openbsd hax
openbsd
1/* $OpenBSD: imsg.c,v 1.42 2025/06/16 13:56:11 claudio Exp $ */
2
3/*
4 * Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
5 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/types.h>
21#include <sys/queue.h>
22#include <sys/socket.h>
23#include <sys/uio.h>
24
25#include <errno.h>
26#include <stddef.h>
27#include <stdint.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31
32#include "imsg.h"
33
34#define IMSG_ALLOW_FDPASS 0x01
35#define IMSG_FD_MARK 0x80000000U
36
37static struct ibuf *imsg_parse_hdr(struct ibuf *, void *, int *);
38
39int
40imsgbuf_init(struct imsgbuf *imsgbuf, int fd)
41{
42 imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr,
43 imsgbuf);
44 if (imsgbuf->w == NULL)
45 return (-1);
46 imsgbuf->pid = getpid();
47 imsgbuf->maxsize = MAX_IMSGSIZE;
48 imsgbuf->fd = fd;
49 imsgbuf->flags = 0;
50 return (0);
51}
52
53void
54imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf)
55{
56 imsgbuf->flags |= IMSG_ALLOW_FDPASS;
57}
58
59int
60imsgbuf_set_maxsize(struct imsgbuf *imsgbuf, uint32_t max)
61{
62 if (max > UINT32_MAX - IMSG_HEADER_SIZE) {
63 errno = ERANGE;
64 return (-1);
65 }
66 max += IMSG_HEADER_SIZE;
67 if (max & IMSG_FD_MARK) {
68 errno = EINVAL;
69 return (-1);
70 }
71 imsgbuf->maxsize = max;
72 return (0);
73}
74
75int
76imsgbuf_read(struct imsgbuf *imsgbuf)
77{
78 if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
79 return msgbuf_read(imsgbuf->fd, imsgbuf->w);
80 else
81 return ibuf_read(imsgbuf->fd, imsgbuf->w);
82}
83
84int
85imsgbuf_write(struct imsgbuf *imsgbuf)
86{
87 if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
88 return msgbuf_write(imsgbuf->fd, imsgbuf->w);
89 else
90 return ibuf_write(imsgbuf->fd, imsgbuf->w);
91}
92
93int
94imsgbuf_flush(struct imsgbuf *imsgbuf)
95{
96 while (imsgbuf_queuelen(imsgbuf) > 0) {
97 if (imsgbuf_write(imsgbuf) == -1)
98 return (-1);
99 }
100 return (0);
101}
102
103void
104imsgbuf_clear(struct imsgbuf *imsgbuf)
105{
106 msgbuf_free(imsgbuf->w);
107 imsgbuf->w = NULL;
108}
109
110uint32_t
111imsgbuf_queuelen(struct imsgbuf *imsgbuf)
112{
113 return msgbuf_queuelen(imsgbuf->w);
114}
115
116int
117imsgbuf_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
118{
119 struct imsg m;
120 struct ibuf *buf;
121
122 if ((buf = msgbuf_get(imsgbuf->w)) == NULL)
123 return (0);
124
125 if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
126 return (-1);
127
128 if (ibuf_size(buf))
129 m.data = ibuf_data(buf);
130 else
131 m.data = NULL;
132 m.buf = buf;
133 m.hdr.len &= ~IMSG_FD_MARK;
134
135 *imsg = m;
136 return (1);
137}
138
139ssize_t
140imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
141{
142 int rv;
143
144 if ((rv = imsgbuf_get(imsgbuf, imsg)) != 1)
145 return rv;
146 return (imsg_get_len(imsg) + IMSG_HEADER_SIZE);
147}
148
149int
150imsg_ibufq_pop(struct ibufqueue *bufq, struct imsg *imsg)
151{
152 struct imsg m;
153 struct ibuf *buf;
154
155 if ((buf = ibufq_pop(bufq)) == NULL)
156 return (0);
157
158 if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
159 return (-1);
160
161 if (ibuf_size(buf))
162 m.data = ibuf_data(buf);
163 else
164 m.data = NULL;
165 m.buf = buf;
166 m.hdr.len &= ~IMSG_FD_MARK;
167
168 *imsg = m;
169 return (1);
170}
171
172void
173imsg_ibufq_push(struct ibufqueue *bufq, struct imsg *imsg)
174{
175 ibuf_rewind(imsg->buf);
176 ibufq_push(bufq, imsg->buf);
177 memset(imsg, 0, sizeof(*imsg));
178}
179
180int
181imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
182{
183 if (ibuf_size(imsg->buf) == 0) {
184 errno = EBADMSG;
185 return (-1);
186 }
187 return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf);
188}
189
190int
191imsg_get_data(struct imsg *imsg, void *data, size_t len)
192{
193 if (len == 0) {
194 errno = EINVAL;
195 return (-1);
196 }
197 if (ibuf_size(imsg->buf) != len) {
198 errno = EBADMSG;
199 return (-1);
200 }
201 return ibuf_get(imsg->buf, data, len);
202}
203
204int
205imsg_get_buf(struct imsg *imsg, void *data, size_t len)
206{
207 return ibuf_get(imsg->buf, data, len);
208}
209
210int
211imsg_get_strbuf(struct imsg *imsg, char *str, size_t len)
212{
213 return ibuf_get_strbuf(imsg->buf, str, len);
214}
215
216int
217imsg_get_fd(struct imsg *imsg)
218{
219 return ibuf_fd_get(imsg->buf);
220}
221
222uint32_t
223imsg_get_id(struct imsg *imsg)
224{
225 return (imsg->hdr.peerid);
226}
227
228size_t
229imsg_get_len(struct imsg *imsg)
230{
231 return ibuf_size(imsg->buf);
232}
233
234pid_t
235imsg_get_pid(struct imsg *imsg)
236{
237 return (imsg->hdr.pid);
238}
239
240uint32_t
241imsg_get_type(struct imsg *imsg)
242{
243 return (imsg->hdr.type);
244}
245
246int
247imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
248 int fd, const void *data, size_t datalen)
249{
250 struct ibuf *wbuf;
251
252 if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
253 goto fail;
254
255 if (ibuf_add(wbuf, data, datalen) == -1)
256 goto fail;
257
258 ibuf_fd_set(wbuf, fd);
259 imsg_close(imsgbuf, wbuf);
260
261 return (1);
262
263 fail:
264 ibuf_free(wbuf);
265 return (-1);
266}
267
268int
269imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
270 int fd, const struct iovec *iov, int iovcnt)
271{
272 struct ibuf *wbuf;
273 int i;
274 size_t datalen = 0;
275
276 for (i = 0; i < iovcnt; i++)
277 datalen += iov[i].iov_len;
278
279 if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
280 goto fail;
281
282 for (i = 0; i < iovcnt; i++)
283 if (ibuf_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
284 goto fail;
285
286 ibuf_fd_set(wbuf, fd);
287 imsg_close(imsgbuf, wbuf);
288
289 return (1);
290
291 fail:
292 ibuf_free(wbuf);
293 return (-1);
294}
295
296/*
297 * Enqueue imsg with payload from ibuf buf. fd passing is not possible
298 * with this function.
299 */
300int
301imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
302 pid_t pid, struct ibuf *buf)
303{
304 struct ibuf *hdrbuf = NULL;
305 struct imsg_hdr hdr;
306
307 if (ibuf_size(buf) + IMSG_HEADER_SIZE > imsgbuf->maxsize) {
308 errno = ERANGE;
309 goto fail;
310 }
311
312 hdr.type = type;
313 hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
314 hdr.peerid = id;
315 if ((hdr.pid = pid) == 0)
316 hdr.pid = imsgbuf->pid;
317
318 if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
319 goto fail;
320 if (ibuf_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
321 goto fail;
322
323 ibuf_close(imsgbuf->w, hdrbuf);
324 ibuf_close(imsgbuf->w, buf);
325 return (1);
326
327 fail:
328 ibuf_free(buf);
329 ibuf_free(hdrbuf);
330 return (-1);
331}
332
333/*
334 * Forward imsg to another channel. Any attached fd is closed.
335 */
336int
337imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
338{
339 struct ibuf *wbuf;
340 size_t len;
341
342 ibuf_rewind(msg->buf);
343 ibuf_skip(msg->buf, sizeof(msg->hdr));
344 len = ibuf_size(msg->buf);
345
346 if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
347 msg->hdr.pid, len)) == NULL)
348 return (-1);
349
350 if (len != 0) {
351 if (ibuf_add_ibuf(wbuf, msg->buf) == -1) {
352 ibuf_free(wbuf);
353 return (-1);
354 }
355 }
356
357 imsg_close(imsgbuf, wbuf);
358 return (1);
359}
360
361struct ibuf *
362imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
363 size_t datalen)
364{
365 struct ibuf *wbuf;
366 struct imsg_hdr hdr;
367
368 datalen += IMSG_HEADER_SIZE;
369 if (datalen > imsgbuf->maxsize) {
370 errno = ERANGE;
371 return (NULL);
372 }
373
374 hdr.len = 0;
375 hdr.type = type;
376 hdr.peerid = id;
377 if ((hdr.pid = pid) == 0)
378 hdr.pid = imsgbuf->pid;
379 if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL)
380 goto fail;
381 if (ibuf_add(wbuf, &hdr, sizeof(hdr)) == -1)
382 goto fail;
383
384 return (wbuf);
385
386 fail:
387 ibuf_free(wbuf);
388 return (NULL);
389}
390
391int
392imsg_add(struct ibuf *msg, const void *data, size_t datalen)
393{
394 if (datalen)
395 if (ibuf_add(msg, data, datalen) == -1) {
396 ibuf_free(msg);
397 return (-1);
398 }
399 return (datalen);
400}
401
402void
403imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
404{
405 uint32_t len;
406
407 len = ibuf_size(msg);
408 if (ibuf_fd_avail(msg))
409 len |= IMSG_FD_MARK;
410 (void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len);
411 ibuf_close(imsgbuf->w, msg);
412}
413
414void
415imsg_free(struct imsg *imsg)
416{
417 ibuf_free(imsg->buf);
418}
419
420int
421imsg_set_maxsize(struct ibuf *msg, size_t max)
422{
423 if (max > UINT32_MAX - IMSG_HEADER_SIZE) {
424 errno = ERANGE;
425 return (-1);
426 }
427 return ibuf_set_maxsize(msg, max + IMSG_HEADER_SIZE);
428}
429
430static struct ibuf *
431imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd)
432{
433 struct imsgbuf *imsgbuf = arg;
434 struct imsg_hdr hdr;
435 struct ibuf *b;
436 uint32_t len;
437
438 if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1)
439 return (NULL);
440
441 len = hdr.len & ~IMSG_FD_MARK;
442
443 if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) {
444 errno = ERANGE;
445 return (NULL);
446 }
447 if ((b = ibuf_open(len)) == NULL)
448 return (NULL);
449 if (hdr.len & IMSG_FD_MARK) {
450 ibuf_fd_set(b, *fd);
451 *fd = -1;
452 }
453
454 return b;
455}