1 /* $OpenBSD: imsg-buffer.c,v 1.18 2023/12/12 15:47:41 claudio Exp $ */
4 * Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
5 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
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.
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.
20 #include <sys/types.h>
21 #include <sys/socket.h>
23 #include <arpa/inet.h>
40 #define htobe64 htonll
46 #define be64toh ntohll
48 static int ibuf_realloc(struct ibuf
*, size_t);
49 static void ibuf_enqueue(struct msgbuf
*, struct ibuf
*);
50 static void ibuf_dequeue(struct msgbuf
*, struct ibuf
*);
51 static void msgbuf_drain(struct msgbuf
*, size_t);
62 if ((buf
= calloc(1, sizeof(struct ibuf
))) == NULL
)
64 if ((buf
->buf
= calloc(len
, 1)) == NULL
) {
68 buf
->size
= buf
->max
= len
;
75 ibuf_dynamic(size_t len
, size_t max
)
79 if (max
== 0 || max
< len
) {
84 if ((buf
= calloc(1, sizeof(struct ibuf
))) == NULL
)
87 if ((buf
->buf
= calloc(len
, 1)) == NULL
) {
100 ibuf_realloc(struct ibuf
*buf
, size_t len
)
104 /* on static buffers max is eq size and so the following fails */
105 if (len
> SIZE_MAX
- buf
->wpos
|| buf
->wpos
+ len
> buf
->max
) {
110 b
= recallocarray(buf
->buf
, buf
->size
, buf
->wpos
+ len
, 1);
114 buf
->size
= buf
->wpos
+ len
;
120 ibuf_reserve(struct ibuf
*buf
, size_t len
)
124 if (len
> SIZE_MAX
- buf
->wpos
|| buf
->max
== 0) {
129 if (buf
->wpos
+ len
> buf
->size
)
130 if (ibuf_realloc(buf
, len
) == -1)
133 b
= buf
->buf
+ buf
->wpos
;
139 ibuf_add(struct ibuf
*buf
, const void *data
, size_t len
)
143 if ((b
= ibuf_reserve(buf
, len
)) == NULL
)
146 memcpy(b
, data
, len
);
151 ibuf_add_ibuf(struct ibuf
*buf
, const struct ibuf
*from
)
153 return ibuf_add(buf
, ibuf_data(from
), ibuf_size(from
));
156 /* remove after tree is converted */
158 ibuf_add_buf(struct ibuf
*buf
, const struct ibuf
*from
)
160 return ibuf_add_ibuf(buf
, from
);
164 ibuf_add_n8(struct ibuf
*buf
, uint64_t value
)
168 if (value
> UINT8_MAX
) {
173 return ibuf_add(buf
, &v
, sizeof(v
));
177 ibuf_add_n16(struct ibuf
*buf
, uint64_t value
)
181 if (value
> UINT16_MAX
) {
186 return ibuf_add(buf
, &v
, sizeof(v
));
190 ibuf_add_n32(struct ibuf
*buf
, uint64_t value
)
194 if (value
> UINT32_MAX
) {
199 return ibuf_add(buf
, &v
, sizeof(v
));
203 ibuf_add_n64(struct ibuf
*buf
, uint64_t value
)
205 value
= htobe64(value
);
206 return ibuf_add(buf
, &value
, sizeof(value
));
210 ibuf_add_h16(struct ibuf
*buf
, uint64_t value
)
214 if (value
> UINT16_MAX
) {
219 return ibuf_add(buf
, &v
, sizeof(v
));
223 ibuf_add_h32(struct ibuf
*buf
, uint64_t value
)
227 if (value
> UINT32_MAX
) {
232 return ibuf_add(buf
, &v
, sizeof(v
));
236 ibuf_add_h64(struct ibuf
*buf
, uint64_t value
)
238 return ibuf_add(buf
, &value
, sizeof(value
));
242 ibuf_add_zero(struct ibuf
*buf
, size_t len
)
246 if ((b
= ibuf_reserve(buf
, len
)) == NULL
)
253 ibuf_seek(struct ibuf
*buf
, size_t pos
, size_t len
)
255 /* only allow seeking between rpos and wpos */
256 if (ibuf_size(buf
) < pos
|| SIZE_MAX
- pos
< len
||
257 ibuf_size(buf
) < pos
+ len
) {
262 return (buf
->buf
+ buf
->rpos
+ pos
);
266 ibuf_set(struct ibuf
*buf
, size_t pos
, const void *data
, size_t len
)
270 if ((b
= ibuf_seek(buf
, pos
, len
)) == NULL
)
273 memcpy(b
, data
, len
);
278 ibuf_set_n8(struct ibuf
*buf
, size_t pos
, uint64_t value
)
282 if (value
> UINT8_MAX
) {
287 return (ibuf_set(buf
, pos
, &v
, sizeof(v
)));
291 ibuf_set_n16(struct ibuf
*buf
, size_t pos
, uint64_t value
)
295 if (value
> UINT16_MAX
) {
300 return (ibuf_set(buf
, pos
, &v
, sizeof(v
)));
304 ibuf_set_n32(struct ibuf
*buf
, size_t pos
, uint64_t value
)
308 if (value
> UINT32_MAX
) {
313 return (ibuf_set(buf
, pos
, &v
, sizeof(v
)));
317 ibuf_set_n64(struct ibuf
*buf
, size_t pos
, uint64_t value
)
319 value
= htobe64(value
);
320 return (ibuf_set(buf
, pos
, &value
, sizeof(value
)));
324 ibuf_set_h16(struct ibuf
*buf
, size_t pos
, uint64_t value
)
328 if (value
> UINT16_MAX
) {
333 return (ibuf_set(buf
, pos
, &v
, sizeof(v
)));
337 ibuf_set_h32(struct ibuf
*buf
, size_t pos
, uint64_t value
)
341 if (value
> UINT32_MAX
) {
346 return (ibuf_set(buf
, pos
, &v
, sizeof(v
)));
350 ibuf_set_h64(struct ibuf
*buf
, size_t pos
, uint64_t value
)
352 return (ibuf_set(buf
, pos
, &value
, sizeof(value
)));
356 ibuf_data(const struct ibuf
*buf
)
358 return (buf
->buf
+ buf
->rpos
);
362 ibuf_size(const struct ibuf
*buf
)
364 return (buf
->wpos
- buf
->rpos
);
368 ibuf_left(const struct ibuf
*buf
)
372 return (buf
->max
- buf
->wpos
);
376 ibuf_truncate(struct ibuf
*buf
, size_t len
)
378 if (ibuf_size(buf
) >= len
) {
379 buf
->wpos
= buf
->rpos
+ len
;
383 /* only allow to truncate down */
387 return ibuf_add_zero(buf
, len
- ibuf_size(buf
));
391 ibuf_rewind(struct ibuf
*buf
)
397 ibuf_close(struct msgbuf
*msgbuf
, struct ibuf
*buf
)
399 ibuf_enqueue(msgbuf
, buf
);
403 ibuf_from_buffer(struct ibuf
*buf
, void *data
, size_t len
)
405 memset(buf
, 0, sizeof(*buf
));
407 buf
->size
= buf
->wpos
= len
;
412 ibuf_from_ibuf(struct ibuf
*buf
, const struct ibuf
*from
)
414 ibuf_from_buffer(buf
, ibuf_data(from
), ibuf_size(from
));
418 ibuf_get(struct ibuf
*buf
, void *data
, size_t len
)
420 if (ibuf_size(buf
) < len
) {
425 memcpy(data
, ibuf_data(buf
), len
);
431 ibuf_get_ibuf(struct ibuf
*buf
, size_t len
, struct ibuf
*new)
433 if (ibuf_size(buf
) < len
) {
438 ibuf_from_buffer(new, ibuf_data(buf
), len
);
444 ibuf_get_n8(struct ibuf
*buf
, uint8_t *value
)
446 return ibuf_get(buf
, value
, sizeof(*value
));
450 ibuf_get_n16(struct ibuf
*buf
, uint16_t *value
)
454 rv
= ibuf_get(buf
, value
, sizeof(*value
));
455 *value
= be16toh(*value
);
460 ibuf_get_n32(struct ibuf
*buf
, uint32_t *value
)
464 rv
= ibuf_get(buf
, value
, sizeof(*value
));
465 *value
= be32toh(*value
);
470 ibuf_get_n64(struct ibuf
*buf
, uint64_t *value
)
474 rv
= ibuf_get(buf
, value
, sizeof(*value
));
475 *value
= be64toh(*value
);
480 ibuf_get_h16(struct ibuf
*buf
, uint16_t *value
)
482 return ibuf_get(buf
, value
, sizeof(*value
));
486 ibuf_get_h32(struct ibuf
*buf
, uint32_t *value
)
488 return ibuf_get(buf
, value
, sizeof(*value
));
492 ibuf_get_h64(struct ibuf
*buf
, uint64_t *value
)
494 return ibuf_get(buf
, value
, sizeof(*value
));
498 ibuf_skip(struct ibuf
*buf
, size_t len
)
500 if (ibuf_size(buf
) < len
) {
510 ibuf_free(struct ibuf
*buf
)
514 if (buf
->max
== 0) /* if buf lives on the stack */
515 abort(); /* abort before causing more harm */
518 freezero(buf
->buf
, buf
->size
);
523 ibuf_fd_avail(struct ibuf
*buf
)
525 return (buf
->fd
!= -1);
529 ibuf_fd_get(struct ibuf
*buf
)
539 ibuf_fd_set(struct ibuf
*buf
, int fd
)
541 if (buf
->max
== 0) /* if buf lives on the stack */
542 abort(); /* abort before causing more harm */
549 ibuf_write(struct msgbuf
*msgbuf
)
551 struct iovec iov
[IOV_MAX
];
556 memset(&iov
, 0, sizeof(iov
));
557 TAILQ_FOREACH(buf
, &msgbuf
->bufs
, entry
) {
560 iov
[i
].iov_base
= ibuf_data(buf
);
561 iov
[i
].iov_len
= ibuf_size(buf
);
566 if ((n
= writev(msgbuf
->fd
, iov
, i
)) == -1) {
569 if (errno
== ENOBUFS
)
574 if (n
== 0) { /* connection closed */
579 msgbuf_drain(msgbuf
, n
);
585 msgbuf_init(struct msgbuf
*msgbuf
)
589 TAILQ_INIT(&msgbuf
->bufs
);
593 msgbuf_drain(struct msgbuf
*msgbuf
, size_t n
)
595 struct ibuf
*buf
, *next
;
597 for (buf
= TAILQ_FIRST(&msgbuf
->bufs
); buf
!= NULL
&& n
> 0;
599 next
= TAILQ_NEXT(buf
, entry
);
600 if (n
>= ibuf_size(buf
)) {
602 ibuf_dequeue(msgbuf
, buf
);
611 msgbuf_clear(struct msgbuf
*msgbuf
)
615 while ((buf
= TAILQ_FIRST(&msgbuf
->bufs
)) != NULL
)
616 ibuf_dequeue(msgbuf
, buf
);
620 msgbuf_write(struct msgbuf
*msgbuf
)
622 struct iovec iov
[IOV_MAX
];
623 struct ibuf
*buf
, *buf0
= NULL
;
627 struct cmsghdr
*cmsg
;
630 char buf
[CMSG_SPACE(sizeof(int))];
633 memset(&iov
, 0, sizeof(iov
));
634 memset(&msg
, 0, sizeof(msg
));
635 memset(&cmsgbuf
, 0, sizeof(cmsgbuf
));
636 TAILQ_FOREACH(buf
, &msgbuf
->bufs
, entry
) {
639 if (i
> 0 && buf
->fd
!= -1)
641 iov
[i
].iov_base
= ibuf_data(buf
);
642 iov
[i
].iov_len
= ibuf_size(buf
);
652 msg
.msg_control
= (caddr_t
)&cmsgbuf
.buf
;
653 msg
.msg_controllen
= sizeof(cmsgbuf
.buf
);
654 cmsg
= CMSG_FIRSTHDR(&msg
);
655 cmsg
->cmsg_len
= CMSG_LEN(sizeof(int));
656 cmsg
->cmsg_level
= SOL_SOCKET
;
657 cmsg
->cmsg_type
= SCM_RIGHTS
;
658 *(int *)CMSG_DATA(cmsg
) = buf0
->fd
;
662 if ((n
= sendmsg(msgbuf
->fd
, &msg
, 0)) == -1) {
665 if (errno
== ENOBUFS
)
670 if (n
== 0) { /* connection closed */
676 * assumption: fd got sent if sendmsg sent anything
677 * this works because fds are passed one at a time
684 msgbuf_drain(msgbuf
, n
);
690 msgbuf_queuelen(struct msgbuf
*msgbuf
)
692 return (msgbuf
->queued
);
696 ibuf_enqueue(struct msgbuf
*msgbuf
, struct ibuf
*buf
)
698 if (buf
->max
== 0) /* if buf lives on the stack */
699 abort(); /* abort before causing more harm */
700 TAILQ_INSERT_TAIL(&msgbuf
->bufs
, buf
, entry
);
705 ibuf_dequeue(struct msgbuf
*msgbuf
, struct ibuf
*buf
)
707 TAILQ_REMOVE(&msgbuf
->bufs
, buf
, entry
);