1 /* $OpenBSD: io.c,v 1.25 2024/08/28 09:39:17 tb 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 *
io_new_buffer(void)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_add_zero(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
io_simple_buffer(struct ibuf * b,const void * res,size_t sz)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
io_buf_buffer(struct ibuf * b,const void * p,size_t sz)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
io_str_buffer(struct ibuf * b,const char * p)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
io_close_buffer(struct msgbuf * msgbuf,struct ibuf * b)86 io_close_buffer(struct msgbuf *msgbuf, struct ibuf *b)
87 {
88 size_t len;
89
90 len = ibuf_size(b) - sizeof(len);
91 ibuf_set(b, 0, &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
io_read_buf(struct ibuf * b,void * res,size_t sz)101 io_read_buf(struct ibuf *b, void *res, size_t sz)
102 {
103 if (sz == 0)
104 return;
105 if (ibuf_get(b, res, sz) == -1)
106 err(1, "bad internal framing");
107 }
108
109 /*
110 * Read a string (returns NULL for zero-length strings), allocating
111 * space for it.
112 * Return 1 on success or 0 if there was not enough data.
113 */
114 void
io_read_str(struct ibuf * b,char ** res)115 io_read_str(struct ibuf *b, char **res)
116 {
117 size_t sz;
118
119 io_read_buf(b, &sz, sizeof(sz));
120 if (sz == 0) {
121 *res = NULL;
122 return;
123 }
124 if ((*res = calloc(sz + 1, 1)) == NULL)
125 err(1, NULL);
126 io_read_buf(b, *res, sz);
127 }
128
129 /*
130 * Read a binary buffer, allocating space for it.
131 * If the buffer is zero-sized, this won't allocate "res", but
132 * will still initialise it to NULL.
133 * Return 1 on success or 0 if there was not enough data.
134 */
135 void
io_read_buf_alloc(struct ibuf * b,void ** res,size_t * sz)136 io_read_buf_alloc(struct ibuf *b, void **res, size_t *sz)
137 {
138 *res = NULL;
139 io_read_buf(b, sz, sizeof(*sz));
140 if (*sz == 0)
141 return;
142 if ((*res = malloc(*sz)) == NULL)
143 err(1, NULL);
144 io_read_buf(b, *res, *sz);
145 }
146
147 /* XXX copy from imsg-buffer.c */
148 static int
ibuf_realloc(struct ibuf * buf,size_t len)149 ibuf_realloc(struct ibuf *buf, size_t len)
150 {
151 unsigned char *b;
152
153 /* on static buffers max is eq size and so the following fails */
154 if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) {
155 errno = ERANGE;
156 return (-1);
157 }
158
159 b = realloc(buf->buf, buf->wpos + len);
160 if (b == NULL)
161 return (-1);
162 memset(b + buf->size, 0, buf->wpos + len - buf->size);
163 buf->buf = b;
164 buf->size = buf->wpos + len;
165
166 return (0);
167 }
168
169 /*
170 * Read once and fill a ibuf until it is finished.
171 * Returns NULL if more data is needed, returns a full ibuf once
172 * all data is received.
173 */
174 struct ibuf *
io_buf_read(int fd,struct ibuf ** ib)175 io_buf_read(int fd, struct ibuf **ib)
176 {
177 struct ibuf *b = *ib;
178 ssize_t n;
179 size_t sz;
180
181 /* if ibuf == NULL allocate a new buffer */
182 if (b == NULL) {
183 if ((b = ibuf_dynamic(sizeof(sz), INT32_MAX)) == NULL)
184 err(1, NULL);
185 *ib = b;
186 }
187
188 again:
189 /* read some data */
190 while ((n = read(fd, b->buf + b->wpos, b->size - b->wpos)) == -1) {
191 if (errno == EINTR)
192 continue;
193 if (errno == EAGAIN)
194 return NULL;
195 err(1, "read");
196 }
197
198 if (n == 0)
199 errx(1, "read: unexpected end of file");
200 b->wpos += n;
201
202 /* got full message */
203 if (b->wpos == b->size) {
204 /* only header received */
205 if (b->wpos == sizeof(sz)) {
206 memcpy(&sz, b->buf, sizeof(sz));
207 if (sz == 0 || sz > INT32_MAX)
208 errx(1, "bad internal framing, bad size");
209 if (ibuf_realloc(b, sz) == -1)
210 err(1, "ibuf_realloc");
211 goto again;
212 }
213
214 /* skip over initial size header */
215 b->rpos += sizeof(sz);
216 *ib = NULL;
217 return b;
218 }
219
220 return NULL;
221 }
222
223 /*
224 * Read data from socket but receive a file descriptor at the same time.
225 */
226 struct ibuf *
io_buf_recvfd(int fd,struct ibuf ** ib)227 io_buf_recvfd(int fd, struct ibuf **ib)
228 {
229 struct ibuf *b = *ib;
230 struct iovec iov;
231 struct msghdr msg;
232 struct cmsghdr *cmsg;
233 union {
234 struct cmsghdr hdr;
235 char buf[CMSG_SPACE(sizeof(int))];
236 } cmsgbuf;
237 ssize_t n;
238 size_t sz;
239
240 /* fd are only passed on the head, just use regular read afterwards */
241 if (b != NULL)
242 return io_buf_read(fd, ib);
243
244 if ((b = ibuf_dynamic(sizeof(sz), INT32_MAX)) == NULL)
245 err(1, NULL);
246 *ib = b;
247
248 memset(&msg, 0, sizeof(msg));
249 memset(&cmsgbuf, 0, sizeof(cmsgbuf));
250
251 iov.iov_base = b->buf;
252 iov.iov_len = b->size;
253
254 msg.msg_iov = &iov;
255 msg.msg_iovlen = 1;
256 msg.msg_control = &cmsgbuf.buf;
257 msg.msg_controllen = sizeof(cmsgbuf.buf);
258
259 while ((n = recvmsg(fd, &msg, 0)) == -1) {
260 if (errno == EINTR)
261 continue;
262 err(1, "recvmsg");
263 }
264
265 if (n == 0)
266 errx(1, "recvmsg: unexpected end of file");
267
268 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
269 cmsg = CMSG_NXTHDR(&msg, cmsg)) {
270 if (cmsg->cmsg_level == SOL_SOCKET &&
271 cmsg->cmsg_type == SCM_RIGHTS) {
272 int i, j, f;
273
274 j = ((char *)cmsg + cmsg->cmsg_len -
275 (char *)CMSG_DATA(cmsg)) / sizeof(int);
276 for (i = 0; i < j; i++) {
277 f = ((int *)CMSG_DATA(cmsg))[i];
278 if (i == 0)
279 ibuf_fd_set(b, f);
280 else
281 close(f);
282 }
283 }
284 }
285
286 b->wpos += n;
287
288 /* got full message */
289 if (b->wpos == b->size) {
290 /* only header received */
291 if (b->wpos == sizeof(sz)) {
292 memcpy(&sz, b->buf, sizeof(sz));
293 if (sz == 0 || sz > INT32_MAX)
294 errx(1, "read: bad internal framing, %zu", sz);
295 if (ibuf_realloc(b, sz) == -1)
296 err(1, "ibuf_realloc");
297 return NULL;
298 }
299
300 /* skip over initial size header */
301 b->rpos += sizeof(sz);
302 *ib = NULL;
303 return b;
304 }
305
306 return NULL;
307 }
308