1 /* $OpenBSD: imsg-buffer.c,v 1.15 2023/05/23 12:41:28 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 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/types.h> 20 #include <sys/queue.h> 21 #include <sys/socket.h> 22 #include <sys/uio.h> 23 24 #include <limits.h> 25 #include <errno.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 #include "imsg.h" 31 32 static int ibuf_realloc(struct ibuf *, size_t); 33 static void ibuf_enqueue(struct msgbuf *, struct ibuf *); 34 static void ibuf_dequeue(struct msgbuf *, struct ibuf *); 35 36 struct ibuf * 37 ibuf_open(size_t len) 38 { 39 struct ibuf *buf; 40 41 if (len == 0) { 42 errno = EINVAL; 43 return (NULL); 44 } 45 if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) 46 return (NULL); 47 if ((buf->buf = calloc(len, 1)) == NULL) { 48 free(buf); 49 return (NULL); 50 } 51 buf->size = buf->max = len; 52 buf->fd = -1; 53 54 return (buf); 55 } 56 57 struct ibuf * 58 ibuf_dynamic(size_t len, size_t max) 59 { 60 struct ibuf *buf; 61 62 if (max < len) { 63 errno = EINVAL; 64 return (NULL); 65 } 66 67 if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) 68 return (NULL); 69 if (len > 0) { 70 if ((buf->buf = calloc(len, 1)) == NULL) { 71 free(buf); 72 return (NULL); 73 } 74 } 75 buf->size = len; 76 buf->max = max; 77 buf->fd = -1; 78 79 return (buf); 80 } 81 82 static int 83 ibuf_realloc(struct ibuf *buf, size_t len) 84 { 85 unsigned char *b; 86 87 /* on static buffers max is eq size and so the following fails */ 88 if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) { 89 errno = ERANGE; 90 return (-1); 91 } 92 93 b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1); 94 if (b == NULL) 95 return (-1); 96 buf->buf = b; 97 buf->size = buf->wpos + len; 98 99 return (0); 100 } 101 102 int 103 ibuf_add(struct ibuf *buf, const void *data, size_t len) 104 { 105 if (len > SIZE_MAX - buf->wpos) { 106 errno = ERANGE; 107 return (-1); 108 } 109 110 if (buf->wpos + len > buf->size) 111 if (ibuf_realloc(buf, len) == -1) 112 return (-1); 113 114 memcpy(buf->buf + buf->wpos, data, len); 115 buf->wpos += len; 116 return (0); 117 } 118 119 void * 120 ibuf_reserve(struct ibuf *buf, size_t len) 121 { 122 void *b; 123 124 if (len > SIZE_MAX - buf->wpos) { 125 errno = ERANGE; 126 return (NULL); 127 } 128 129 if (buf->wpos + len > buf->size) 130 if (ibuf_realloc(buf, len) == -1) 131 return (NULL); 132 133 b = buf->buf + buf->wpos; 134 buf->wpos += len; 135 memset(b, 0, len); 136 return (b); 137 } 138 139 void * 140 ibuf_seek(struct ibuf *buf, size_t pos, size_t len) 141 { 142 /* only allowed to seek in already written parts */ 143 if (len > SIZE_MAX - pos || pos + len > buf->wpos) 144 return (NULL); 145 146 return (buf->buf + pos); 147 } 148 149 size_t 150 ibuf_size(struct ibuf *buf) 151 { 152 return (buf->wpos); 153 } 154 155 size_t 156 ibuf_left(struct ibuf *buf) 157 { 158 return (buf->max - buf->wpos); 159 } 160 161 void 162 ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) 163 { 164 ibuf_enqueue(msgbuf, buf); 165 } 166 167 int 168 ibuf_write(struct msgbuf *msgbuf) 169 { 170 struct iovec iov[IOV_MAX]; 171 struct ibuf *buf; 172 unsigned int i = 0; 173 ssize_t n; 174 175 memset(&iov, 0, sizeof(iov)); 176 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { 177 if (i >= IOV_MAX) 178 break; 179 iov[i].iov_base = buf->buf + buf->rpos; 180 iov[i].iov_len = buf->wpos - buf->rpos; 181 i++; 182 } 183 184 again: 185 if ((n = writev(msgbuf->fd, iov, i)) == -1) { 186 if (errno == EINTR) 187 goto again; 188 if (errno == ENOBUFS) 189 errno = EAGAIN; 190 return (-1); 191 } 192 193 if (n == 0) { /* connection closed */ 194 errno = 0; 195 return (0); 196 } 197 198 msgbuf_drain(msgbuf, n); 199 200 return (1); 201 } 202 203 void 204 ibuf_free(struct ibuf *buf) 205 { 206 if (buf == NULL) 207 return; 208 freezero(buf->buf, buf->size); 209 free(buf); 210 } 211 212 void 213 msgbuf_init(struct msgbuf *msgbuf) 214 { 215 msgbuf->queued = 0; 216 msgbuf->fd = -1; 217 TAILQ_INIT(&msgbuf->bufs); 218 } 219 220 void 221 msgbuf_drain(struct msgbuf *msgbuf, size_t n) 222 { 223 struct ibuf *buf, *next; 224 225 for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; 226 buf = next) { 227 next = TAILQ_NEXT(buf, entry); 228 if (n >= buf->wpos - buf->rpos) { 229 n -= buf->wpos - buf->rpos; 230 ibuf_dequeue(msgbuf, buf); 231 } else { 232 buf->rpos += n; 233 n = 0; 234 } 235 } 236 } 237 238 void 239 msgbuf_clear(struct msgbuf *msgbuf) 240 { 241 struct ibuf *buf; 242 243 while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) 244 ibuf_dequeue(msgbuf, buf); 245 } 246 247 int 248 msgbuf_write(struct msgbuf *msgbuf) 249 { 250 struct iovec iov[IOV_MAX]; 251 struct ibuf *buf, *buf0 = NULL; 252 unsigned int i = 0; 253 ssize_t n; 254 struct msghdr msg; 255 struct cmsghdr *cmsg; 256 union { 257 struct cmsghdr hdr; 258 char buf[CMSG_SPACE(sizeof(int))]; 259 } cmsgbuf; 260 261 memset(&iov, 0, sizeof(iov)); 262 memset(&msg, 0, sizeof(msg)); 263 memset(&cmsgbuf, 0, sizeof(cmsgbuf)); 264 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { 265 if (i >= IOV_MAX) 266 break; 267 if (i > 0 && buf->fd != -1) 268 break; 269 iov[i].iov_base = buf->buf + buf->rpos; 270 iov[i].iov_len = buf->wpos - buf->rpos; 271 i++; 272 if (buf->fd != -1) 273 buf0 = buf; 274 } 275 276 msg.msg_iov = iov; 277 msg.msg_iovlen = i; 278 279 if (buf0 != NULL) { 280 msg.msg_control = (caddr_t)&cmsgbuf.buf; 281 msg.msg_controllen = sizeof(cmsgbuf.buf); 282 cmsg = CMSG_FIRSTHDR(&msg); 283 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 284 cmsg->cmsg_level = SOL_SOCKET; 285 cmsg->cmsg_type = SCM_RIGHTS; 286 *(int *)CMSG_DATA(cmsg) = buf0->fd; 287 } 288 289 again: 290 if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { 291 if (errno == EINTR) 292 goto again; 293 if (errno == ENOBUFS) 294 errno = EAGAIN; 295 return (-1); 296 } 297 298 if (n == 0) { /* connection closed */ 299 errno = 0; 300 return (0); 301 } 302 303 /* 304 * assumption: fd got sent if sendmsg sent anything 305 * this works because fds are passed one at a time 306 */ 307 if (buf0 != NULL) { 308 close(buf0->fd); 309 buf0->fd = -1; 310 } 311 312 msgbuf_drain(msgbuf, n); 313 314 return (1); 315 } 316 317 static void 318 ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) 319 { 320 TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); 321 msgbuf->queued++; 322 } 323 324 static void 325 ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) 326 { 327 TAILQ_REMOVE(&msgbuf->bufs, buf, entry); 328 329 if (buf->fd != -1) 330 close(buf->fd); 331 332 msgbuf->queued--; 333 ibuf_free(buf); 334 } 335