xref: /openbsd-src/usr.sbin/rpki-client/io.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: io.c,v 1.22 2022/12/14 15:19:16 claudio Exp $ */
2 /*
3  * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
4  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
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/queue.h>
20 #include <sys/socket.h>
21 
22 #include <err.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdint.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <imsg.h>
30 
31 #include "extern.h"
32 
33 /*
34  * Create new io buffer, call io_close() when done with it.
35  * Function always returns a new buffer.
36  */
37 struct ibuf *
38 io_new_buffer(void)
39 {
40 	struct ibuf *b;
41 
42 	if ((b = ibuf_dynamic(64, INT32_MAX)) == NULL)
43 		err(1, NULL);
44 	ibuf_reserve(b, sizeof(size_t));	/* can not fail */
45 	return b;
46 }
47 
48 /*
49  * Add a simple object of static size to the io buffer.
50  */
51 void
52 io_simple_buffer(struct ibuf *b, const void *res, size_t sz)
53 {
54 	if (ibuf_add(b, res, sz) == -1)
55 		err(1, NULL);
56 }
57 
58 /*
59  * Add a sz sized buffer into the io buffer.
60  */
61 void
62 io_buf_buffer(struct ibuf *b, const void *p, size_t sz)
63 {
64 	if (ibuf_add(b, &sz, sizeof(size_t)) == -1)
65 		err(1, NULL);
66 	if (sz > 0)
67 		if (ibuf_add(b, p, sz) == -1)
68 			err(1, NULL);
69 }
70 
71 /*
72  * Add a string into the io buffer.
73  */
74 void
75 io_str_buffer(struct ibuf *b, const char *p)
76 {
77 	size_t sz = (p == NULL) ? 0 : strlen(p);
78 
79 	io_buf_buffer(b, p, sz);
80 }
81 
82 /*
83  * Finish and enqueue a io buffer.
84  */
85 void
86 io_close_buffer(struct msgbuf *msgbuf, struct ibuf *b)
87 {
88 	size_t len;
89 
90 	len = ibuf_size(b) - sizeof(len);
91 	memcpy(ibuf_seek(b, 0, sizeof(len)), &len, sizeof(len));
92 	ibuf_close(msgbuf, b);
93 }
94 
95 /*
96  * Read of an ibuf and extract sz byte from there.
97  * Does nothing if "sz" is zero.
98  * Return 1 on success or 0 if there was not enough data.
99  */
100 void
101 io_read_buf(struct ibuf *b, void *res, size_t sz)
102 {
103 	char	*tmp;
104 
105 	if (sz == 0)
106 		return;
107 	tmp = ibuf_seek(b, b->rpos, sz);
108 	if (tmp == NULL)
109 		errx(1, "bad internal framing, buffer too short");
110 	b->rpos += sz;
111 	memcpy(res, tmp, sz);
112 }
113 
114 /*
115  * Read a string (returns NULL for zero-length strings), allocating
116  * space for it.
117  * Return 1 on success or 0 if there was not enough data.
118  */
119 void
120 io_read_str(struct ibuf *b, char **res)
121 {
122 	size_t	 sz;
123 
124 	io_read_buf(b, &sz, sizeof(sz));
125 	if (sz == 0) {
126 		*res = NULL;
127 		return;
128 	}
129 	if ((*res = calloc(sz + 1, 1)) == NULL)
130 		err(1, NULL);
131 	io_read_buf(b, *res, sz);
132 }
133 
134 /*
135  * Read a binary buffer, allocating space for it.
136  * If the buffer is zero-sized, this won't allocate "res", but
137  * will still initialise it to NULL.
138  * Return 1 on success or 0 if there was not enough data.
139  */
140 void
141 io_read_buf_alloc(struct ibuf *b, void **res, size_t *sz)
142 {
143 	*res = NULL;
144 	io_read_buf(b, sz, sizeof(*sz));
145 	if (*sz == 0)
146 		return;
147 	if ((*res = malloc(*sz)) == NULL)
148 		err(1, NULL);
149 	io_read_buf(b, *res, *sz);
150 }
151 
152 /* XXX copy from imsg-buffer.c */
153 static int
154 ibuf_realloc(struct ibuf *buf, size_t len)
155 {
156 	unsigned char	*b;
157 
158 	/* on static buffers max is eq size and so the following fails */
159 	if (buf->wpos + len > buf->max) {
160 		errno = ERANGE;
161 		return (-1);
162 	}
163 
164 	b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1);
165 	if (b == NULL)
166 		return (-1);
167 	buf->buf = b;
168 	buf->size = buf->wpos + len;
169 
170 	return (0);
171 }
172 
173 /*
174  * Read once and fill a ibuf until it is finished.
175  * Returns NULL if more data is needed, returns a full ibuf once
176  * all data is received.
177  */
178 struct ibuf *
179 io_buf_read(int fd, struct ibuf **ib)
180 {
181 	struct ibuf *b = *ib;
182 	ssize_t n;
183 	size_t sz;
184 
185 	/* if ibuf == NULL allocate a new buffer */
186 	if (b == NULL) {
187 		if ((b = ibuf_dynamic(sizeof(sz), INT32_MAX)) == NULL)
188 			err(1, NULL);
189 		*ib = b;
190 	}
191 
192  again:
193 	/* read some data */
194 	while ((n = read(fd, b->buf + b->wpos, b->size - b->wpos)) == -1) {
195 		if (errno == EINTR)
196 			continue;
197 		if (errno == EAGAIN)
198 			return NULL;
199 		err(1, "read");
200 	}
201 
202 	if (n == 0)
203 		errx(1, "read: unexpected end of file");
204 	b->wpos += n;
205 
206 	/* got full message */
207 	if (b->wpos == b->size) {
208 		/* only header received */
209 		if (b->wpos == sizeof(sz)) {
210 			memcpy(&sz, b->buf, sizeof(sz));
211 			if (sz == 0 || sz > INT32_MAX)
212 				errx(1, "bad internal framing, bad size");
213 			if (ibuf_realloc(b, sz) == -1)
214 				err(1, "ibuf_realloc");
215 			goto again;
216 		}
217 
218 		/* skip over initial size header */
219 		b->rpos += sizeof(sz);
220 		*ib = NULL;
221 		return b;
222 	}
223 
224 	return NULL;
225 }
226 
227 /*
228  * Read data from socket but receive a file descriptor at the same time.
229  */
230 struct ibuf *
231 io_buf_recvfd(int fd, struct ibuf **ib)
232 {
233 	struct ibuf *b = *ib;
234 	struct iovec iov;
235 	struct msghdr msg;
236 	struct cmsghdr *cmsg;
237 	union {
238 		struct cmsghdr	hdr;
239 		char		buf[CMSG_SPACE(sizeof(int))];
240 	} cmsgbuf;
241 	ssize_t n;
242 	size_t sz;
243 
244 	/* fd are only passed on the head, just use regular read afterwards */
245 	if (b != NULL)
246 		return io_buf_read(fd, ib);
247 
248 	if ((b = ibuf_dynamic(sizeof(sz), INT32_MAX)) == NULL)
249 		err(1, NULL);
250 	*ib = b;
251 
252 	memset(&msg, 0, sizeof(msg));
253 	memset(&cmsgbuf, 0, sizeof(cmsgbuf));
254 
255 	iov.iov_base = b->buf;
256 	iov.iov_len = b->size;
257 
258 	msg.msg_iov = &iov;
259 	msg.msg_iovlen = 1;
260 	msg.msg_control = &cmsgbuf.buf;
261 	msg.msg_controllen = sizeof(cmsgbuf.buf);
262 
263 	while ((n = recvmsg(fd, &msg, 0)) == -1) {
264 		if (errno == EINTR)
265 			continue;
266 		err(1, "recvmsg");
267 	}
268 
269 	if (n == 0)
270 		errx(1, "recvmsg: unexpected end of file");
271 
272 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
273 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
274 		if (cmsg->cmsg_level == SOL_SOCKET &&
275 		    cmsg->cmsg_type == SCM_RIGHTS) {
276 			int i, j, f;
277 
278 			j = ((char *)cmsg + cmsg->cmsg_len -
279 			    (char *)CMSG_DATA(cmsg)) / sizeof(int);
280 			for (i = 0; i < j; i++) {
281 				f = ((int *)CMSG_DATA(cmsg))[i];
282 				if (i == 0)
283 					b->fd = f;
284 				else
285 					close(f);
286 			}
287 		}
288 	}
289 
290 	b->wpos += n;
291 
292 	/* got full message */
293 	if (b->wpos == b->size) {
294 		/* only header received */
295 		if (b->wpos == sizeof(sz)) {
296 			memcpy(&sz, b->buf, sizeof(sz));
297 			if (sz == 0 || sz > INT32_MAX)
298 				errx(1, "read: bad internal framing, %zu", sz);
299 			if (ibuf_realloc(b, sz) == -1)
300 				err(1, "ibuf_realloc");
301 			return NULL;
302 		}
303 
304 		/* skip over initial size header */
305 		b->rpos += sizeof(sz);
306 		*ib = NULL;
307 		return b;
308 	}
309 
310 	return NULL;
311 }
312