xref: /openbsd-src/usr.sbin/rpki-client/io.c (revision 824adb5411e4389b29bae28eba5c2c2bbd147f34)
1 /*	$OpenBSD: io.c,v 1.13 2021/03/04 13:01:41 claudio Exp $ */
2 /*
3  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/queue.h>
19 #include <sys/socket.h>
20 
21 #include <assert.h>
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 void
34 io_socket_blocking(int fd)
35 {
36 	int	 fl;
37 
38 	if ((fl = fcntl(fd, F_GETFL, 0)) == -1)
39 		err(1, "fcntl");
40 	if (fcntl(fd, F_SETFL, fl & ~O_NONBLOCK) == -1)
41 		err(1, "fcntl");
42 }
43 
44 void
45 io_socket_nonblocking(int fd)
46 {
47 	int	 fl;
48 
49 	if ((fl = fcntl(fd, F_GETFL, 0)) == -1)
50 		err(1, "fcntl");
51 	if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) == -1)
52 		err(1, "fcntl");
53 }
54 
55 /*
56  * Like io_simple_write() but into a buffer.
57  */
58 void
59 io_simple_buffer(struct ibuf *b, const void *res, size_t sz)
60 {
61 	if (ibuf_add(b, res, sz) == -1)
62 		err(1, NULL);
63 }
64 
65 /*
66  * Add a sz sized buffer into the io buffer.
67  */
68 void
69 io_buf_buffer(struct ibuf *b, const void *p, size_t sz)
70 {
71 	if (ibuf_add(b, &sz, sizeof(size_t)) == -1)
72 		err(1, NULL);
73 	if (sz > 0)
74 		if (ibuf_add(b, p, sz) == -1)
75 			err(1, NULL);
76 }
77 
78 /*
79  * Add a string into the io buffer.
80  */
81 void
82 io_str_buffer(struct ibuf *b, const char *p)
83 {
84 	size_t sz = (p == NULL) ? 0 : strlen(p);
85 
86 	io_buf_buffer(b, p, sz);
87 }
88 
89 /*
90  * Read of a binary buffer that must be on a blocking descriptor.
91  * Does nothing if "sz" is zero.
92  * This will fail and exit on EOF.
93  */
94 void
95 io_simple_read(int fd, void *res, size_t sz)
96 {
97 	ssize_t	 ssz;
98 	char	*tmp;
99 
100 	tmp = res; /* arithmetic on a pointer to void is a GNU extension */
101 again:
102 	if (sz == 0)
103 		return;
104 	if ((ssz = read(fd, tmp, sz)) == -1)
105 		err(1, "read");
106 	else if (ssz == 0)
107 		errx(1, "read: unexpected end of file");
108 	else if ((size_t)ssz == sz)
109 		return;
110 	sz -= ssz;
111 	tmp += ssz;
112 	goto again;
113 }
114 
115 /*
116  * Read a binary buffer, allocating space for it.
117  * If the buffer is zero-sized, this won't allocate "res", but
118  * will still initialise it to NULL.
119  */
120 void
121 io_buf_read_alloc(int fd, void **res, size_t *sz)
122 {
123 
124 	*res = NULL;
125 	io_simple_read(fd, sz, sizeof(size_t));
126 	if (*sz == 0)
127 		return;
128 	if ((*res = malloc(*sz)) == NULL)
129 		err(1, NULL);
130 	io_simple_read(fd, *res, *sz);
131 }
132 
133 /*
134  * Read a string (returns NULL for zero-length strings), allocating
135  * space for it.
136  */
137 void
138 io_str_read(int fd, char **res)
139 {
140 	size_t	 sz;
141 
142 	io_simple_read(fd, &sz, sizeof(size_t));
143 	if (sz == 0) {
144 		*res = NULL;
145 		return;
146 	}
147 	if ((*res = calloc(sz + 1, 1)) == NULL)
148 		err(1, NULL);
149 	io_simple_read(fd, *res, sz);
150 }
151 
152 /*
153  * Read data from socket but receive a file descriptor at the same time.
154  */
155 int
156 io_recvfd(int fd, void *res, size_t sz)
157 {
158 	struct iovec iov;
159 	struct msghdr msg;
160 	struct cmsghdr *cmsg;
161 	union {
162 		struct cmsghdr	hdr;
163 		char		buf[CMSG_SPACE(sizeof(int))];
164 	} cmsgbuf;
165 	int outfd = -1;
166 	char *b = res;
167 	ssize_t n;
168 
169 	memset(&msg, 0, sizeof(msg));
170 	memset(&cmsgbuf, 0, sizeof(cmsgbuf));
171 
172 	iov.iov_base = res;
173 	iov.iov_len = sz;
174 
175 	msg.msg_iov = &iov;
176 	msg.msg_iovlen = 1;
177 	msg.msg_control = &cmsgbuf.buf;
178 	msg.msg_controllen = sizeof(cmsgbuf.buf);
179 
180 	while ((n = recvmsg(fd, &msg, 0)) == -1) {
181 		if (errno == EINTR)
182 			continue;
183 		err(1, "recvmsg");
184 	}
185 
186 	if (n == 0)
187 		errx(1, "recvmsg: unexpected end of file");
188 
189 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
190 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
191 		if (cmsg->cmsg_level == SOL_SOCKET &&
192 		    cmsg->cmsg_type == SCM_RIGHTS) {
193 			int i, j, f;
194 
195 			j = ((char *)cmsg + cmsg->cmsg_len -
196 			    (char *)CMSG_DATA(cmsg)) / sizeof(int);
197 			for (i = 0; i < j; i++) {
198 				f = ((int *)CMSG_DATA(cmsg))[i];
199 				if (i == 0)
200 					outfd = f;
201 				else
202 					close(f);
203 			}
204 		}
205 	}
206 
207 	b += n;
208 	sz -= n;
209 	while (sz > 0) {
210 		/* short receive */
211 		n = recv(fd, b, sz, 0);
212 		if (n == -1) {
213 			if (errno == EINTR)
214 				continue;
215 			err(1, "recv");
216 		}
217 		if (n == 0)
218 			errx(1, "recv: unexpected end of file");
219 
220 		b += n;
221 		sz -= n;
222 	}
223 
224 	return outfd;
225 }
226