xref: /openbsd-src/lib/libutil/imsg-buffer.c (revision 1ad61ae0a79a724d2d3ec69e69c8e1d1ff6b53a0)
1 /*	$OpenBSD: imsg-buffer.c,v 1.17 2023/10/24 14:05:23 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 <limits.h>
25 #include <errno.h>
26 #include <endian.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include "imsg.h"
32 
33 static int	ibuf_realloc(struct ibuf *, size_t);
34 static void	ibuf_enqueue(struct msgbuf *, struct ibuf *);
35 static void	ibuf_dequeue(struct msgbuf *, struct ibuf *);
36 static void	msgbuf_drain(struct msgbuf *, size_t);
37 
38 struct ibuf *
39 ibuf_open(size_t len)
40 {
41 	struct ibuf	*buf;
42 
43 	if (len == 0) {
44 		errno = EINVAL;
45 		return (NULL);
46 	}
47 	if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
48 		return (NULL);
49 	if ((buf->buf = calloc(len, 1)) == NULL) {
50 		free(buf);
51 		return (NULL);
52 	}
53 	buf->size = buf->max = len;
54 	buf->fd = -1;
55 
56 	return (buf);
57 }
58 
59 struct ibuf *
60 ibuf_dynamic(size_t len, size_t max)
61 {
62 	struct ibuf	*buf;
63 
64 	if (max < len) {
65 		errno = EINVAL;
66 		return (NULL);
67 	}
68 
69 	if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
70 		return (NULL);
71 	if (len > 0) {
72 		if ((buf->buf = calloc(len, 1)) == NULL) {
73 			free(buf);
74 			return (NULL);
75 		}
76 	}
77 	buf->size = len;
78 	buf->max = max;
79 	buf->fd = -1;
80 
81 	return (buf);
82 }
83 
84 static int
85 ibuf_realloc(struct ibuf *buf, size_t len)
86 {
87 	unsigned char	*b;
88 
89 	/* on static buffers max is eq size and so the following fails */
90 	if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) {
91 		errno = ERANGE;
92 		return (-1);
93 	}
94 
95 	b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1);
96 	if (b == NULL)
97 		return (-1);
98 	buf->buf = b;
99 	buf->size = buf->wpos + len;
100 
101 	return (0);
102 }
103 
104 void *
105 ibuf_reserve(struct ibuf *buf, size_t len)
106 {
107 	void	*b;
108 
109 	if (len > SIZE_MAX - buf->wpos) {
110 		errno = ERANGE;
111 		return (NULL);
112 	}
113 
114 	if (buf->wpos + len > buf->size)
115 		if (ibuf_realloc(buf, len) == -1)
116 			return (NULL);
117 
118 	b = buf->buf + buf->wpos;
119 	buf->wpos += len;
120 	memset(b, 0, len);
121 	return (b);
122 }
123 
124 int
125 ibuf_add(struct ibuf *buf, const void *data, size_t len)
126 {
127 	void *b;
128 
129 	if ((b = ibuf_reserve(buf, len)) == NULL)
130 		return (-1);
131 
132 	memcpy(b, data, len);
133 	return (0);
134 }
135 
136 int
137 ibuf_add_buf(struct ibuf *buf, const struct ibuf *from)
138 {
139 	return ibuf_add(buf, from->buf, from->wpos);
140 }
141 
142 int
143 ibuf_add_n8(struct ibuf *buf, uint64_t value)
144 {
145 	uint8_t v;
146 
147 	if (value > UINT8_MAX) {
148 		errno = EINVAL;
149 		return (-1);
150 	}
151 	v = value;
152 	return ibuf_add(buf, &v, sizeof(v));
153 }
154 
155 int
156 ibuf_add_n16(struct ibuf *buf, uint64_t value)
157 {
158 	uint16_t v;
159 
160 	if (value > UINT16_MAX) {
161 		errno = EINVAL;
162 		return (-1);
163 	}
164 	v = htobe16(value);
165 	return ibuf_add(buf, &v, sizeof(v));
166 }
167 
168 int
169 ibuf_add_n32(struct ibuf *buf, uint64_t value)
170 {
171 	uint32_t v;
172 
173 	if (value > UINT32_MAX) {
174 		errno = EINVAL;
175 		return (-1);
176 	}
177 	v = htobe32(value);
178 	return ibuf_add(buf, &v, sizeof(v));
179 }
180 
181 int
182 ibuf_add_n64(struct ibuf *buf, uint64_t value)
183 {
184 	value = htobe64(value);
185 	return ibuf_add(buf, &value, sizeof(value));
186 }
187 
188 int
189 ibuf_add_zero(struct ibuf *buf, size_t len)
190 {
191 	void *b;
192 
193 	if ((b = ibuf_reserve(buf, len)) == NULL)
194 		return (-1);
195 	return (0);
196 }
197 
198 void *
199 ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
200 {
201 	/* only allowed to seek in already written parts */
202 	if (len > SIZE_MAX - pos || pos + len > buf->wpos) {
203 		errno = ERANGE;
204 		return (NULL);
205 	}
206 
207 	return (buf->buf + pos);
208 }
209 
210 int
211 ibuf_set(struct ibuf *buf, size_t pos, const void *data, size_t len)
212 {
213 	void *b;
214 
215 	if ((b = ibuf_seek(buf, pos, len)) == NULL)
216 		return (-1);
217 
218 	memcpy(b, data, len);
219 	return (0);
220 }
221 
222 int
223 ibuf_set_n8(struct ibuf *buf, size_t pos, uint64_t value)
224 {
225 	uint8_t v;
226 
227 	if (value > UINT8_MAX) {
228 		errno = EINVAL;
229 		return (-1);
230 	}
231 	v = value;
232 	return (ibuf_set(buf, pos, &v, sizeof(v)));
233 }
234 
235 int
236 ibuf_set_n16(struct ibuf *buf, size_t pos, uint64_t value)
237 {
238 	uint16_t v;
239 
240 	if (value > UINT16_MAX) {
241 		errno = EINVAL;
242 		return (-1);
243 	}
244 	v = htobe16(value);
245 	return (ibuf_set(buf, pos, &v, sizeof(v)));
246 }
247 
248 int
249 ibuf_set_n32(struct ibuf *buf, size_t pos, uint64_t value)
250 {
251 	uint32_t v;
252 
253 	if (value > UINT32_MAX) {
254 		errno = EINVAL;
255 		return (-1);
256 	}
257 	v = htobe32(value);
258 	return (ibuf_set(buf, pos, &v, sizeof(v)));
259 }
260 
261 int
262 ibuf_set_n64(struct ibuf *buf, size_t pos, uint64_t value)
263 {
264 	value = htobe64(value);
265 	return (ibuf_set(buf, pos, &value, sizeof(value)));
266 }
267 
268 void *
269 ibuf_data(struct ibuf *buf)
270 {
271 	return (buf->buf);
272 }
273 
274 size_t
275 ibuf_size(struct ibuf *buf)
276 {
277 	return (buf->wpos);
278 }
279 
280 size_t
281 ibuf_left(struct ibuf *buf)
282 {
283 	return (buf->max - buf->wpos);
284 }
285 
286 void
287 ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
288 {
289 	ibuf_enqueue(msgbuf, buf);
290 }
291 
292 void
293 ibuf_free(struct ibuf *buf)
294 {
295 	if (buf == NULL)
296 		return;
297 	if (buf->fd != -1)
298 		close(buf->fd);
299 	freezero(buf->buf, buf->size);
300 	free(buf);
301 }
302 
303 int
304 ibuf_fd_avail(struct ibuf *buf)
305 {
306 	return (buf->fd != -1);
307 }
308 
309 int
310 ibuf_fd_get(struct ibuf *buf)
311 {
312 	int fd;
313 
314 	fd = buf->fd;
315 	buf->fd = -1;
316 	return (fd);
317 }
318 
319 void
320 ibuf_fd_set(struct ibuf *buf, int fd)
321 {
322 	if (buf->fd != -1)
323 		close(buf->fd);
324 	buf->fd = fd;
325 }
326 
327 int
328 ibuf_write(struct msgbuf *msgbuf)
329 {
330 	struct iovec	 iov[IOV_MAX];
331 	struct ibuf	*buf;
332 	unsigned int	 i = 0;
333 	ssize_t	n;
334 
335 	memset(&iov, 0, sizeof(iov));
336 	TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
337 		if (i >= IOV_MAX)
338 			break;
339 		iov[i].iov_base = buf->buf + buf->rpos;
340 		iov[i].iov_len = buf->wpos - buf->rpos;
341 		i++;
342 	}
343 
344 again:
345 	if ((n = writev(msgbuf->fd, iov, i)) == -1) {
346 		if (errno == EINTR)
347 			goto again;
348 		if (errno == ENOBUFS)
349 			errno = EAGAIN;
350 		return (-1);
351 	}
352 
353 	if (n == 0) {			/* connection closed */
354 		errno = 0;
355 		return (0);
356 	}
357 
358 	msgbuf_drain(msgbuf, n);
359 
360 	return (1);
361 }
362 
363 void
364 msgbuf_init(struct msgbuf *msgbuf)
365 {
366 	msgbuf->queued = 0;
367 	msgbuf->fd = -1;
368 	TAILQ_INIT(&msgbuf->bufs);
369 }
370 
371 static void
372 msgbuf_drain(struct msgbuf *msgbuf, size_t n)
373 {
374 	struct ibuf	*buf, *next;
375 
376 	for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
377 	    buf = next) {
378 		next = TAILQ_NEXT(buf, entry);
379 		if (n >= buf->wpos - buf->rpos) {
380 			n -= buf->wpos - buf->rpos;
381 			ibuf_dequeue(msgbuf, buf);
382 		} else {
383 			buf->rpos += n;
384 			n = 0;
385 		}
386 	}
387 }
388 
389 void
390 msgbuf_clear(struct msgbuf *msgbuf)
391 {
392 	struct ibuf	*buf;
393 
394 	while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
395 		ibuf_dequeue(msgbuf, buf);
396 }
397 
398 int
399 msgbuf_write(struct msgbuf *msgbuf)
400 {
401 	struct iovec	 iov[IOV_MAX];
402 	struct ibuf	*buf, *buf0 = NULL;
403 	unsigned int	 i = 0;
404 	ssize_t		 n;
405 	struct msghdr	 msg;
406 	struct cmsghdr	*cmsg;
407 	union {
408 		struct cmsghdr	hdr;
409 		char		buf[CMSG_SPACE(sizeof(int))];
410 	} cmsgbuf;
411 
412 	memset(&iov, 0, sizeof(iov));
413 	memset(&msg, 0, sizeof(msg));
414 	memset(&cmsgbuf, 0, sizeof(cmsgbuf));
415 	TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
416 		if (i >= IOV_MAX)
417 			break;
418 		if (i > 0 && buf->fd != -1)
419 			break;
420 		iov[i].iov_base = buf->buf + buf->rpos;
421 		iov[i].iov_len = buf->wpos - buf->rpos;
422 		i++;
423 		if (buf->fd != -1)
424 			buf0 = buf;
425 	}
426 
427 	msg.msg_iov = iov;
428 	msg.msg_iovlen = i;
429 
430 	if (buf0 != NULL) {
431 		msg.msg_control = (caddr_t)&cmsgbuf.buf;
432 		msg.msg_controllen = sizeof(cmsgbuf.buf);
433 		cmsg = CMSG_FIRSTHDR(&msg);
434 		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
435 		cmsg->cmsg_level = SOL_SOCKET;
436 		cmsg->cmsg_type = SCM_RIGHTS;
437 		*(int *)CMSG_DATA(cmsg) = buf0->fd;
438 	}
439 
440 again:
441 	if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
442 		if (errno == EINTR)
443 			goto again;
444 		if (errno == ENOBUFS)
445 			errno = EAGAIN;
446 		return (-1);
447 	}
448 
449 	if (n == 0) {			/* connection closed */
450 		errno = 0;
451 		return (0);
452 	}
453 
454 	/*
455 	 * assumption: fd got sent if sendmsg sent anything
456 	 * this works because fds are passed one at a time
457 	 */
458 	if (buf0 != NULL) {
459 		close(buf0->fd);
460 		buf0->fd = -1;
461 	}
462 
463 	msgbuf_drain(msgbuf, n);
464 
465 	return (1);
466 }
467 
468 static void
469 ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
470 {
471 	TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
472 	msgbuf->queued++;
473 }
474 
475 static void
476 ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
477 {
478 	TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
479 
480 	msgbuf->queued--;
481 	ibuf_free(buf);
482 }
483