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