1 /* $OpenBSD: imsg.c,v 1.38 2024/11/29 04:35:13 tb 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 <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "imsg.h"
32
33 #define IMSG_ALLOW_FDPASS 0x01
34 #define IMSG_FD_MARK 0x80000000U
35
36 static struct ibuf *imsg_parse_hdr(struct ibuf *, void *, int *);
37
38 int
imsgbuf_init(struct imsgbuf * imsgbuf,int fd)39 imsgbuf_init(struct imsgbuf *imsgbuf, int fd)
40 {
41 imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr,
42 imsgbuf);
43 if (imsgbuf->w == NULL)
44 return (-1);
45 imsgbuf->pid = getpid();
46 imsgbuf->maxsize = MAX_IMSGSIZE;
47 imsgbuf->fd = fd;
48 imsgbuf->flags = 0;
49 return (0);
50 }
51
52 void
imsgbuf_allow_fdpass(struct imsgbuf * imsgbuf)53 imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf)
54 {
55 imsgbuf->flags |= IMSG_ALLOW_FDPASS;
56 }
57
58 int
imsgbuf_set_maxsize(struct imsgbuf * imsgbuf,uint32_t maxsize)59 imsgbuf_set_maxsize(struct imsgbuf *imsgbuf, uint32_t maxsize)
60 {
61 if (maxsize < IMSG_HEADER_SIZE || maxsize & IMSG_FD_MARK) {
62 errno = EINVAL;
63 return (-1);
64 }
65 imsgbuf->maxsize = maxsize;
66 return (0);
67 }
68
69 int
imsgbuf_read(struct imsgbuf * imsgbuf)70 imsgbuf_read(struct imsgbuf *imsgbuf)
71 {
72 if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
73 return msgbuf_read(imsgbuf->fd, imsgbuf->w);
74 else
75 return ibuf_read(imsgbuf->fd, imsgbuf->w);
76 }
77
78 int
imsgbuf_write(struct imsgbuf * imsgbuf)79 imsgbuf_write(struct imsgbuf *imsgbuf)
80 {
81 if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
82 return msgbuf_write(imsgbuf->fd, imsgbuf->w);
83 else
84 return ibuf_write(imsgbuf->fd, imsgbuf->w);
85 }
86
87 int
imsgbuf_flush(struct imsgbuf * imsgbuf)88 imsgbuf_flush(struct imsgbuf *imsgbuf)
89 {
90 while (imsgbuf_queuelen(imsgbuf) > 0) {
91 if (imsgbuf_write(imsgbuf) == -1)
92 return (-1);
93 }
94 return (0);
95 }
96
97 void
imsgbuf_clear(struct imsgbuf * imsgbuf)98 imsgbuf_clear(struct imsgbuf *imsgbuf)
99 {
100 msgbuf_free(imsgbuf->w);
101 imsgbuf->w = NULL;
102 }
103
104 uint32_t
imsgbuf_queuelen(struct imsgbuf * imsgbuf)105 imsgbuf_queuelen(struct imsgbuf *imsgbuf)
106 {
107 return msgbuf_queuelen(imsgbuf->w);
108 }
109
110 ssize_t
imsg_get(struct imsgbuf * imsgbuf,struct imsg * imsg)111 imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
112 {
113 struct imsg m;
114 struct ibuf *buf;
115
116 if ((buf = msgbuf_get(imsgbuf->w)) == NULL)
117 return (0);
118
119 if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
120 return (-1);
121
122 if (ibuf_size(buf))
123 m.data = ibuf_data(buf);
124 else
125 m.data = NULL;
126 m.buf = buf;
127 m.hdr.len &= ~IMSG_FD_MARK;
128
129 *imsg = m;
130 return (ibuf_size(buf) + IMSG_HEADER_SIZE);
131 }
132
133 int
imsg_get_ibuf(struct imsg * imsg,struct ibuf * ibuf)134 imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
135 {
136 if (ibuf_size(imsg->buf) == 0) {
137 errno = EBADMSG;
138 return (-1);
139 }
140 return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf);
141 }
142
143 int
imsg_get_data(struct imsg * imsg,void * data,size_t len)144 imsg_get_data(struct imsg *imsg, void *data, size_t len)
145 {
146 if (len == 0) {
147 errno = EINVAL;
148 return (-1);
149 }
150 if (ibuf_size(imsg->buf) != len) {
151 errno = EBADMSG;
152 return (-1);
153 }
154 return ibuf_get(imsg->buf, data, len);
155 }
156
157 int
imsg_get_fd(struct imsg * imsg)158 imsg_get_fd(struct imsg *imsg)
159 {
160 return ibuf_fd_get(imsg->buf);
161 }
162
163 uint32_t
imsg_get_id(struct imsg * imsg)164 imsg_get_id(struct imsg *imsg)
165 {
166 return (imsg->hdr.peerid);
167 }
168
169 size_t
imsg_get_len(struct imsg * imsg)170 imsg_get_len(struct imsg *imsg)
171 {
172 return ibuf_size(imsg->buf);
173 }
174
175 pid_t
imsg_get_pid(struct imsg * imsg)176 imsg_get_pid(struct imsg *imsg)
177 {
178 return (imsg->hdr.pid);
179 }
180
181 uint32_t
imsg_get_type(struct imsg * imsg)182 imsg_get_type(struct imsg *imsg)
183 {
184 return (imsg->hdr.type);
185 }
186
187 int
imsg_compose(struct imsgbuf * imsgbuf,uint32_t type,uint32_t id,pid_t pid,int fd,const void * data,size_t datalen)188 imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
189 int fd, const void *data, size_t datalen)
190 {
191 struct ibuf *wbuf;
192
193 if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
194 return (-1);
195
196 if (imsg_add(wbuf, data, datalen) == -1)
197 return (-1);
198
199 ibuf_fd_set(wbuf, fd);
200 imsg_close(imsgbuf, wbuf);
201
202 return (1);
203 }
204
205 int
imsg_composev(struct imsgbuf * imsgbuf,uint32_t type,uint32_t id,pid_t pid,int fd,const struct iovec * iov,int iovcnt)206 imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
207 int fd, const struct iovec *iov, int iovcnt)
208 {
209 struct ibuf *wbuf;
210 int i;
211 size_t datalen = 0;
212
213 for (i = 0; i < iovcnt; i++)
214 datalen += iov[i].iov_len;
215
216 if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
217 return (-1);
218
219 for (i = 0; i < iovcnt; i++)
220 if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
221 return (-1);
222
223 ibuf_fd_set(wbuf, fd);
224 imsg_close(imsgbuf, wbuf);
225
226 return (1);
227 }
228
229 /*
230 * Enqueue imsg with payload from ibuf buf. fd passing is not possible
231 * with this function.
232 */
233 int
imsg_compose_ibuf(struct imsgbuf * imsgbuf,uint32_t type,uint32_t id,pid_t pid,struct ibuf * buf)234 imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
235 pid_t pid, struct ibuf *buf)
236 {
237 struct ibuf *hdrbuf = NULL;
238 struct imsg_hdr hdr;
239 int save_errno;
240
241 if (ibuf_size(buf) + IMSG_HEADER_SIZE > imsgbuf->maxsize) {
242 errno = ERANGE;
243 goto fail;
244 }
245
246 hdr.type = type;
247 hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
248 hdr.peerid = id;
249 if ((hdr.pid = pid) == 0)
250 hdr.pid = imsgbuf->pid;
251
252 if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
253 goto fail;
254 if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
255 goto fail;
256
257 ibuf_close(imsgbuf->w, hdrbuf);
258 ibuf_close(imsgbuf->w, buf);
259 return (1);
260
261 fail:
262 save_errno = errno;
263 ibuf_free(buf);
264 ibuf_free(hdrbuf);
265 errno = save_errno;
266 return (-1);
267 }
268
269 /*
270 * Forward imsg to another channel. Any attached fd is closed.
271 */
272 int
imsg_forward(struct imsgbuf * imsgbuf,struct imsg * msg)273 imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
274 {
275 struct ibuf *wbuf;
276 size_t len;
277
278 ibuf_rewind(msg->buf);
279 ibuf_skip(msg->buf, sizeof(msg->hdr));
280 len = ibuf_size(msg->buf);
281
282 if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
283 msg->hdr.pid, len)) == NULL)
284 return (-1);
285
286 if (len != 0) {
287 if (ibuf_add_ibuf(wbuf, msg->buf) == -1) {
288 ibuf_free(wbuf);
289 return (-1);
290 }
291 }
292
293 imsg_close(imsgbuf, wbuf);
294 return (1);
295 }
296
297 struct ibuf *
imsg_create(struct imsgbuf * imsgbuf,uint32_t type,uint32_t id,pid_t pid,size_t datalen)298 imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
299 size_t datalen)
300 {
301 struct ibuf *wbuf;
302 struct imsg_hdr hdr;
303
304 datalen += IMSG_HEADER_SIZE;
305 if (datalen > imsgbuf->maxsize) {
306 errno = ERANGE;
307 return (NULL);
308 }
309
310 hdr.type = type;
311 hdr.peerid = id;
312 if ((hdr.pid = pid) == 0)
313 hdr.pid = imsgbuf->pid;
314 if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL) {
315 return (NULL);
316 }
317 if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
318 return (NULL);
319
320 return (wbuf);
321 }
322
323 int
imsg_add(struct ibuf * msg,const void * data,size_t datalen)324 imsg_add(struct ibuf *msg, const void *data, size_t datalen)
325 {
326 if (datalen)
327 if (ibuf_add(msg, data, datalen) == -1) {
328 ibuf_free(msg);
329 return (-1);
330 }
331 return (datalen);
332 }
333
334 void
imsg_close(struct imsgbuf * imsgbuf,struct ibuf * msg)335 imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
336 {
337 uint32_t len;
338
339 len = ibuf_size(msg);
340 if (ibuf_fd_avail(msg))
341 len |= IMSG_FD_MARK;
342 (void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len);
343 ibuf_close(imsgbuf->w, msg);
344 }
345
346 void
imsg_free(struct imsg * imsg)347 imsg_free(struct imsg *imsg)
348 {
349 ibuf_free(imsg->buf);
350 }
351
352 static struct ibuf *
imsg_parse_hdr(struct ibuf * buf,void * arg,int * fd)353 imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd)
354 {
355 struct imsgbuf *imsgbuf = arg;
356 struct imsg_hdr hdr;
357 struct ibuf *b;
358 uint32_t len;
359
360 if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1)
361 return (NULL);
362
363 len = hdr.len & ~IMSG_FD_MARK;
364
365 if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) {
366 errno = ERANGE;
367 return (NULL);
368 }
369 if ((b = ibuf_open(len)) == NULL)
370 return (NULL);
371 if (hdr.len & IMSG_FD_MARK) {
372 ibuf_fd_set(b, *fd);
373 *fd = -1;
374 }
375
376 return b;
377 }
378