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