Coverage Report

Created: 2026-01-21 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/imsg-buffer.c
Line
Count
Source
1
// SPDX-License-Identifier: ISC
2
/*  $OpenBSD$ */
3
4
/*
5
 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6
 */
7
8
#include <zebra.h>
9
10
#include "queue.h"
11
#include "imsg.h"
12
13
static int ibuf_realloc(struct ibuf *, size_t);
14
static void ibuf_enqueue(struct msgbuf *, struct ibuf *);
15
static void ibuf_dequeue(struct msgbuf *, struct ibuf *);
16
17
struct ibuf *ibuf_open(size_t len)
18
0
{
19
0
  struct ibuf *buf;
20
21
0
  if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
22
0
    return NULL;
23
0
  if ((buf->buf = malloc(len)) == NULL) {
24
0
    free(buf);
25
0
    return NULL;
26
0
  }
27
0
  buf->size = buf->max = len;
28
0
  buf->fd = -1;
29
30
0
  return (buf);
31
0
}
32
33
struct ibuf *ibuf_dynamic(size_t len, size_t max)
34
0
{
35
0
  struct ibuf *buf;
36
37
0
  if (max < len)
38
0
    return NULL;
39
40
0
  if ((buf = ibuf_open(len)) == NULL)
41
0
    return NULL;
42
43
0
  if (max > 0)
44
0
    buf->max = max;
45
46
0
  return (buf);
47
0
}
48
49
static int ibuf_realloc(struct ibuf *buf, size_t len)
50
0
{
51
0
  uint8_t *b;
52
53
  /* on static buffers max is eq size and so the following fails */
54
0
  if (buf->wpos + len > buf->max) {
55
0
    errno = ERANGE;
56
0
    return -1;
57
0
  }
58
59
0
  b = realloc(buf->buf, buf->wpos + len);
60
0
  if (b == NULL)
61
0
    return -1;
62
0
  buf->buf = b;
63
0
  buf->size = buf->wpos + len;
64
65
0
  return 0;
66
0
}
67
68
int ibuf_add(struct ibuf *buf, const void *data, size_t len)
69
0
{
70
0
  if (buf->wpos + len > buf->size)
71
0
    if (ibuf_realloc(buf, len) == -1)
72
0
      return -1;
73
74
0
  memcpy(buf->buf + buf->wpos, data, len);
75
0
  buf->wpos += len;
76
0
  return 0;
77
0
}
78
79
void *ibuf_reserve(struct ibuf *buf, size_t len)
80
0
{
81
0
  void *b;
82
83
0
  if (buf->wpos + len > buf->size)
84
0
    if (ibuf_realloc(buf, len) == -1)
85
0
      return NULL;
86
87
0
  b = buf->buf + buf->wpos;
88
0
  buf->wpos += len;
89
0
  return (b);
90
0
}
91
92
void *ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
93
0
{
94
  /* only allowed to seek in already written parts */
95
0
  if (pos + len > buf->wpos)
96
0
    return NULL;
97
98
0
  return (buf->buf + pos);
99
0
}
100
101
size_t ibuf_size(struct ibuf *buf)
102
0
{
103
0
  return (buf->wpos);
104
0
}
105
106
size_t ibuf_left(struct ibuf *buf)
107
0
{
108
0
  return (buf->max - buf->wpos);
109
0
}
110
111
void ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
112
0
{
113
0
  ibuf_enqueue(msgbuf, buf);
114
0
}
115
116
int ibuf_write(struct msgbuf *msgbuf)
117
0
{
118
0
  struct iovec iov[IOV_MAX];
119
0
  struct ibuf *buf;
120
0
  unsigned int i = 0;
121
0
  ssize_t n;
122
123
0
  memset(&iov, 0, sizeof(iov));
124
0
  TAILQ_FOREACH (buf, &msgbuf->bufs, entry) {
125
0
    if (i >= IOV_MAX)
126
0
      break;
127
0
    iov[i].iov_base = buf->buf + buf->rpos;
128
0
    iov[i].iov_len = buf->wpos - buf->rpos;
129
0
    i++;
130
0
  }
131
132
0
again:
133
0
  if ((n = writev(msgbuf->fd, iov, i)) == -1) {
134
0
    if (errno == EINTR)
135
0
      goto again;
136
0
    if (errno == ENOBUFS)
137
0
      errno = EAGAIN;
138
0
    return -1;
139
0
  }
140
141
0
  if (n == 0) { /* connection closed */
142
0
    errno = 0;
143
0
    return 0;
144
0
  }
145
146
0
  msgbuf_drain(msgbuf, n);
147
148
0
  return 1;
149
0
}
150
151
void ibuf_free(struct ibuf *buf)
152
0
{
153
0
  if (buf == NULL)
154
0
    return;
155
0
  free(buf->buf);
156
0
  free(buf);
157
0
}
158
159
void msgbuf_init(struct msgbuf *msgbuf)
160
0
{
161
0
  msgbuf->queued = 0;
162
0
  msgbuf->fd = -1;
163
0
  TAILQ_INIT(&msgbuf->bufs);
164
0
}
165
166
void msgbuf_drain(struct msgbuf *msgbuf, size_t n)
167
0
{
168
0
  struct ibuf *buf, *next;
169
170
0
  for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
171
0
       buf = next) {
172
0
    next = TAILQ_NEXT(buf, entry);
173
0
    if (buf->rpos + n >= buf->wpos) {
174
0
      n -= buf->wpos - buf->rpos;
175
176
0
      TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
177
0
      ibuf_dequeue(msgbuf, buf);
178
0
    } else {
179
0
      buf->rpos += n;
180
0
      n = 0;
181
0
    }
182
0
  }
183
0
}
184
185
void msgbuf_clear(struct msgbuf *msgbuf)
186
0
{
187
0
  struct ibuf *buf;
188
189
0
  while ((buf = TAILQ_POP_FIRST(&msgbuf->bufs, entry)) != NULL)
190
0
    ibuf_dequeue(msgbuf, buf);
191
0
}
192
193
int msgbuf_write(struct msgbuf *msgbuf)
194
0
{
195
0
  struct iovec iov[IOV_MAX];
196
0
  struct ibuf *buf;
197
0
  unsigned int i = 0;
198
0
  ssize_t n;
199
0
  struct msghdr msg;
200
0
  struct cmsghdr *cmsg;
201
0
  union {
202
0
    struct cmsghdr hdr;
203
0
    char buf[CMSG_SPACE(sizeof(int))];
204
0
  } cmsgbuf;
205
206
0
  memset(&iov, 0, sizeof(iov));
207
0
  memset(&msg, 0, sizeof(msg));
208
0
  memset(&cmsgbuf, 0, sizeof(cmsgbuf));
209
0
  TAILQ_FOREACH (buf, &msgbuf->bufs, entry) {
210
0
    if (i >= IOV_MAX)
211
0
      break;
212
0
    iov[i].iov_base = buf->buf + buf->rpos;
213
0
    iov[i].iov_len = buf->wpos - buf->rpos;
214
0
    i++;
215
0
    if (buf->fd != -1)
216
0
      break;
217
0
  }
218
219
0
  msg.msg_iov = iov;
220
0
  msg.msg_iovlen = i;
221
222
0
  if (buf != NULL && buf->fd != -1) {
223
0
    msg.msg_control = (caddr_t)&cmsgbuf.buf;
224
0
    msg.msg_controllen = sizeof(cmsgbuf.buf);
225
0
    cmsg = CMSG_FIRSTHDR(&msg);
226
0
    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
227
0
    cmsg->cmsg_level = SOL_SOCKET;
228
0
    cmsg->cmsg_type = SCM_RIGHTS;
229
0
    memcpy(CMSG_DATA(cmsg), &buf->fd, sizeof(int));
230
0
  }
231
232
0
again:
233
0
  if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
234
0
    if (errno == EINTR)
235
0
      goto again;
236
0
    if (errno == ENOBUFS)
237
0
      errno = EAGAIN;
238
0
    return -1;
239
0
  }
240
241
0
  if (n == 0) { /* connection closed */
242
0
    errno = 0;
243
0
    return 0;
244
0
  }
245
246
  /*
247
   * assumption: fd got sent if sendmsg sent anything
248
   * this works because fds are passed one at a time
249
   */
250
0
  if (buf != NULL && buf->fd != -1) {
251
0
    close(buf->fd);
252
0
    buf->fd = -1;
253
0
  }
254
255
0
  msgbuf_drain(msgbuf, n);
256
257
0
  return 1;
258
0
}
259
260
static void ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
261
0
{
262
0
  TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
263
0
  msgbuf->queued++;
264
0
}
265
266
static void ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
267
0
{
268
  /* TAILQ_REMOVE done by caller */
269
0
  if (buf->fd != -1)
270
0
    close(buf->fd);
271
272
0
  msgbuf->queued--;
273
0
  ibuf_free(buf);
274
0
}