Line data Source code
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 "memory.h"
11 : #include "queue.h"
12 : #include "imsg.h"
13 :
14 : static int imsg_fd_overhead = 0;
15 :
16 : static int imsg_get_fd(struct imsgbuf *);
17 :
18 : #ifndef __OpenBSD__
19 : /*
20 : * The original code calls getdtablecount() which is OpenBSD specific. Use
21 : * available_fds() from OpenSMTPD instead.
22 : */
23 0 : static int available_fds(unsigned int n)
24 : {
25 0 : unsigned int i;
26 0 : int ret, fds[256];
27 :
28 0 : if (n > (unsigned int)array_size(fds))
29 : return 1;
30 :
31 0 : ret = 0;
32 0 : for (i = 0; i < n; i++) {
33 0 : fds[i] = -1;
34 0 : if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
35 0 : if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT)
36 0 : fds[i] = socket(AF_INET6, SOCK_DGRAM, 0);
37 0 : if (fds[i] < 0) {
38 : ret = 1;
39 : break;
40 : }
41 : }
42 : }
43 :
44 0 : for (i = 0; i < n && fds[i] >= 0; i++)
45 0 : close(fds[i]);
46 :
47 : return (ret);
48 : }
49 : #endif
50 :
51 0 : void imsg_init(struct imsgbuf *ibuf, int fd)
52 : {
53 0 : msgbuf_init(&ibuf->w);
54 0 : memset(&ibuf->r, 0, sizeof(ibuf->r));
55 0 : ibuf->fd = fd;
56 0 : ibuf->w.fd = fd;
57 0 : ibuf->pid = getpid();
58 0 : TAILQ_INIT(&ibuf->fds);
59 0 : }
60 :
61 0 : ssize_t imsg_read(struct imsgbuf *ibuf)
62 : {
63 0 : struct msghdr msg;
64 0 : struct cmsghdr *cmsg;
65 0 : union {
66 : struct cmsghdr hdr;
67 : char buf[CMSG_SPACE(sizeof(int) * 1)];
68 : } cmsgbuf;
69 0 : struct iovec iov;
70 0 : ssize_t n;
71 0 : int fd;
72 0 : struct imsg_fd *ifd;
73 :
74 0 : memset(&msg, 0, sizeof(msg));
75 0 : memset(&cmsgbuf, 0, sizeof(cmsgbuf));
76 :
77 0 : iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
78 0 : iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
79 0 : msg.msg_iov = &iov;
80 0 : msg.msg_iovlen = 1;
81 0 : msg.msg_control = &cmsgbuf.buf;
82 0 : msg.msg_controllen = sizeof(cmsgbuf.buf);
83 :
84 0 : if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
85 : return -1;
86 :
87 0 : again:
88 : #ifdef __OpenBSD__
89 : if (getdtablecount() + imsg_fd_overhead
90 : + (int)((CMSG_SPACE(sizeof(int)) - CMSG_SPACE(0))
91 : / sizeof(int))
92 : >= getdtablesize()) {
93 : #else
94 0 : if (available_fds(imsg_fd_overhead
95 : + (CMSG_SPACE(sizeof(int)) - CMSG_SPACE(0))
96 : / sizeof(int))) {
97 : #endif
98 0 : errno = EAGAIN;
99 0 : free(ifd);
100 0 : return -1;
101 : }
102 :
103 0 : n = recvmsg(ibuf->fd, &msg, 0);
104 0 : if (n == -1) {
105 0 : if (errno == EINTR)
106 0 : goto again;
107 0 : goto fail;
108 : }
109 :
110 0 : ibuf->r.wpos += n;
111 :
112 0 : for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
113 0 : cmsg = CMSG_NXTHDR(&msg, cmsg)) {
114 0 : if (cmsg->cmsg_level == SOL_SOCKET
115 0 : && cmsg->cmsg_type == SCM_RIGHTS) {
116 0 : int i;
117 0 : int j;
118 :
119 : /*
120 : * We only accept one file descriptor. Due to C
121 : * padding rules, our control buffer might contain
122 : * more than one fd, and we must close them.
123 : */
124 0 : j = ((char *)cmsg + cmsg->cmsg_len
125 0 : - (char *)CMSG_DATA(cmsg))
126 0 : / sizeof(int);
127 0 : for (i = 0; i < j; i++) {
128 0 : fd = ((int *)CMSG_DATA(cmsg))[i];
129 0 : if (ifd != NULL) {
130 0 : ifd->fd = fd;
131 0 : TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
132 : entry);
133 0 : ifd = NULL;
134 : } else
135 0 : close(fd);
136 : }
137 : }
138 : /* we do not handle other ctl data level */
139 : }
140 :
141 0 : fail:
142 0 : free(ifd);
143 0 : return (n);
144 : }
145 :
146 0 : ssize_t imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
147 : {
148 0 : size_t av, left, datalen;
149 :
150 0 : av = ibuf->r.wpos;
151 :
152 0 : if (IMSG_HEADER_SIZE > av)
153 : return 0;
154 :
155 0 : memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
156 0 : if (imsg->hdr.len < IMSG_HEADER_SIZE || imsg->hdr.len > MAX_IMSGSIZE) {
157 0 : errno = ERANGE;
158 0 : return -1;
159 : }
160 0 : if (imsg->hdr.len > av)
161 : return 0;
162 0 : datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
163 0 : ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
164 0 : if (datalen == 0)
165 0 : imsg->data = NULL;
166 0 : else if ((imsg->data = malloc(datalen)) == NULL)
167 : return -1;
168 :
169 0 : if (imsg->hdr.flags & IMSGF_HASFD)
170 0 : imsg->fd = imsg_get_fd(ibuf);
171 : else
172 0 : imsg->fd = -1;
173 :
174 0 : if (imsg->data)
175 0 : memcpy(imsg->data, ibuf->r.rptr, datalen);
176 :
177 0 : if (imsg->hdr.len < av) {
178 0 : left = av - imsg->hdr.len;
179 0 : memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
180 0 : ibuf->r.wpos = left;
181 : } else
182 0 : ibuf->r.wpos = 0;
183 :
184 0 : return (datalen + IMSG_HEADER_SIZE);
185 : }
186 :
187 0 : int imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid,
188 : pid_t pid, int fd, const void *data, uint16_t datalen)
189 : {
190 0 : struct ibuf *wbuf;
191 :
192 0 : if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
193 : return -1;
194 :
195 0 : if (imsg_add(wbuf, data, datalen) == -1)
196 : return -1;
197 :
198 0 : wbuf->fd = fd;
199 :
200 0 : imsg_close(ibuf, wbuf);
201 :
202 0 : return 1;
203 : }
204 :
205 0 : int imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid,
206 : pid_t pid, int fd, const struct iovec *iov, int iovcnt)
207 : {
208 0 : struct ibuf *wbuf;
209 0 : int i, datalen = 0;
210 :
211 0 : for (i = 0; i < iovcnt; i++)
212 0 : datalen += iov[i].iov_len;
213 :
214 0 : if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
215 : return -1;
216 :
217 0 : for (i = 0; i < iovcnt; i++)
218 0 : if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
219 : return -1;
220 :
221 0 : wbuf->fd = fd;
222 :
223 0 : imsg_close(ibuf, wbuf);
224 :
225 0 : return 1;
226 : }
227 :
228 : /* ARGSUSED */
229 0 : struct ibuf *imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid,
230 : pid_t pid, uint16_t datalen)
231 : {
232 0 : struct ibuf *wbuf;
233 0 : struct imsg_hdr hdr;
234 :
235 0 : memset(&hdr, 0x00, IMSG_HEADER_SIZE);
236 :
237 0 : datalen += IMSG_HEADER_SIZE;
238 0 : if (datalen > MAX_IMSGSIZE) {
239 0 : errno = ERANGE;
240 0 : return NULL;
241 : }
242 :
243 0 : hdr.type = type;
244 0 : hdr.flags = 0;
245 0 : hdr.peerid = peerid;
246 0 : if ((hdr.pid = pid) == 0)
247 0 : hdr.pid = ibuf->pid;
248 0 : if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
249 : return NULL;
250 : }
251 0 : if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
252 : return NULL;
253 :
254 : return (wbuf);
255 : }
256 :
257 0 : int imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
258 : {
259 0 : if (datalen)
260 0 : if (ibuf_add(msg, data, datalen) == -1) {
261 0 : ibuf_free(msg);
262 0 : return -1;
263 : }
264 0 : return (datalen);
265 : }
266 :
267 0 : void imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
268 : {
269 0 : struct imsg_hdr *hdr;
270 :
271 0 : hdr = (struct imsg_hdr *)msg->buf;
272 :
273 0 : hdr->flags &= ~IMSGF_HASFD;
274 0 : if (msg->fd != -1)
275 0 : hdr->flags |= IMSGF_HASFD;
276 :
277 0 : hdr->len = (uint16_t)msg->wpos;
278 :
279 0 : ibuf_close(&ibuf->w, msg);
280 0 : }
281 :
282 0 : void imsg_free(struct imsg *imsg)
283 : {
284 0 : free(imsg->data);
285 0 : }
286 :
287 0 : int imsg_get_fd(struct imsgbuf *ibuf)
288 : {
289 0 : int fd;
290 0 : struct imsg_fd *ifd;
291 :
292 0 : if ((ifd = TAILQ_POP_FIRST(&ibuf->fds, entry)) == NULL)
293 : return -1;
294 :
295 0 : fd = ifd->fd;
296 0 : free(ifd);
297 :
298 0 : return (fd);
299 : }
300 :
301 0 : int imsg_flush(struct imsgbuf *ibuf)
302 : {
303 0 : while (ibuf->w.queued)
304 0 : if (msgbuf_write(&ibuf->w) <= 0)
305 : return -1;
306 : return 0;
307 : }
308 :
309 0 : void imsg_clear(struct imsgbuf *ibuf)
310 : {
311 0 : int fd;
312 :
313 0 : msgbuf_clear(&ibuf->w);
314 0 : while ((fd = imsg_get_fd(ibuf)) != -1)
315 0 : close(fd);
316 0 : }
|