1 /* $OpenBSD: io.c,v 1.24 2023/12/12 15:54:18 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_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 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 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 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 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 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 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 (buf->wpos + len > buf->max) { 155 errno = ERANGE; 156 return (-1); 157 } 158 159 b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1); 160 if (b == NULL) 161 return (-1); 162 buf->buf = b; 163 buf->size = buf->wpos + len; 164 165 return (0); 166 } 167 168 /* 169 * Read once and fill a ibuf until it is finished. 170 * Returns NULL if more data is needed, returns a full ibuf once 171 * all data is received. 172 */ 173 struct ibuf * 174 io_buf_read(int fd, struct ibuf **ib) 175 { 176 struct ibuf *b = *ib; 177 ssize_t n; 178 size_t sz; 179 180 /* if ibuf == NULL allocate a new buffer */ 181 if (b == NULL) { 182 if ((b = ibuf_dynamic(sizeof(sz), INT32_MAX)) == NULL) 183 err(1, NULL); 184 *ib = b; 185 } 186 187 again: 188 /* read some data */ 189 while ((n = read(fd, b->buf + b->wpos, b->size - b->wpos)) == -1) { 190 if (errno == EINTR) 191 continue; 192 if (errno == EAGAIN) 193 return NULL; 194 err(1, "read"); 195 } 196 197 if (n == 0) 198 errx(1, "read: unexpected end of file"); 199 b->wpos += n; 200 201 /* got full message */ 202 if (b->wpos == b->size) { 203 /* only header received */ 204 if (b->wpos == sizeof(sz)) { 205 memcpy(&sz, b->buf, sizeof(sz)); 206 if (sz == 0 || sz > INT32_MAX) 207 errx(1, "bad internal framing, bad size"); 208 if (ibuf_realloc(b, sz) == -1) 209 err(1, "ibuf_realloc"); 210 goto again; 211 } 212 213 /* skip over initial size header */ 214 b->rpos += sizeof(sz); 215 *ib = NULL; 216 return b; 217 } 218 219 return NULL; 220 } 221 222 /* 223 * Read data from socket but receive a file descriptor at the same time. 224 */ 225 struct ibuf * 226 io_buf_recvfd(int fd, struct ibuf **ib) 227 { 228 struct ibuf *b = *ib; 229 struct iovec iov; 230 struct msghdr msg; 231 struct cmsghdr *cmsg; 232 union { 233 struct cmsghdr hdr; 234 char buf[CMSG_SPACE(sizeof(int))]; 235 } cmsgbuf; 236 ssize_t n; 237 size_t sz; 238 239 /* fd are only passed on the head, just use regular read afterwards */ 240 if (b != NULL) 241 return io_buf_read(fd, ib); 242 243 if ((b = ibuf_dynamic(sizeof(sz), INT32_MAX)) == NULL) 244 err(1, NULL); 245 *ib = b; 246 247 memset(&msg, 0, sizeof(msg)); 248 memset(&cmsgbuf, 0, sizeof(cmsgbuf)); 249 250 iov.iov_base = b->buf; 251 iov.iov_len = b->size; 252 253 msg.msg_iov = &iov; 254 msg.msg_iovlen = 1; 255 msg.msg_control = &cmsgbuf.buf; 256 msg.msg_controllen = sizeof(cmsgbuf.buf); 257 258 while ((n = recvmsg(fd, &msg, 0)) == -1) { 259 if (errno == EINTR) 260 continue; 261 err(1, "recvmsg"); 262 } 263 264 if (n == 0) 265 errx(1, "recvmsg: unexpected end of file"); 266 267 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 268 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 269 if (cmsg->cmsg_level == SOL_SOCKET && 270 cmsg->cmsg_type == SCM_RIGHTS) { 271 int i, j, f; 272 273 j = ((char *)cmsg + cmsg->cmsg_len - 274 (char *)CMSG_DATA(cmsg)) / sizeof(int); 275 for (i = 0; i < j; i++) { 276 f = ((int *)CMSG_DATA(cmsg))[i]; 277 if (i == 0) 278 ibuf_fd_set(b, f); 279 else 280 close(f); 281 } 282 } 283 } 284 285 b->wpos += n; 286 287 /* got full message */ 288 if (b->wpos == b->size) { 289 /* only header received */ 290 if (b->wpos == sizeof(sz)) { 291 memcpy(&sz, b->buf, sizeof(sz)); 292 if (sz == 0 || sz > INT32_MAX) 293 errx(1, "read: bad internal framing, %zu", sz); 294 if (ibuf_realloc(b, sz) == -1) 295 err(1, "ibuf_realloc"); 296 return NULL; 297 } 298 299 /* skip over initial size header */ 300 b->rpos += sizeof(sz); 301 *ib = NULL; 302 return b; 303 } 304 305 return NULL; 306 } 307