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