xref: /freebsd/sbin/hastd/proto.c (revision 3c4ba5f55438f7afd4f4b0b56f88f2bb505fd6a6)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2009-2010 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software was developed by Pawel Jakub Dawidek under sponsorship from
8  * the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/types.h>
36 #include <sys/queue.h>
37 #include <sys/socket.h>
38 
39 #include <errno.h>
40 #include <stdint.h>
41 #include <string.h>
42 #include <strings.h>
43 
44 #include "pjdlog.h"
45 #include "proto.h"
46 #include "proto_impl.h"
47 
48 #define	PROTO_CONN_MAGIC	0x907041c
49 struct proto_conn {
50 	int		 pc_magic;
51 	struct proto	*pc_proto;
52 	void		*pc_ctx;
53 	int		 pc_side;
54 #define	PROTO_SIDE_CLIENT		0
55 #define	PROTO_SIDE_SERVER_LISTEN	1
56 #define	PROTO_SIDE_SERVER_WORK		2
57 };
58 
59 static TAILQ_HEAD(, proto) protos = TAILQ_HEAD_INITIALIZER(protos);
60 
61 void
62 proto_register(struct proto *proto, bool isdefault)
63 {
64 	static bool seen_default = false;
65 
66 	if (!isdefault)
67 		TAILQ_INSERT_HEAD(&protos, proto, prt_next);
68 	else {
69 		PJDLOG_ASSERT(!seen_default);
70 		seen_default = true;
71 		TAILQ_INSERT_TAIL(&protos, proto, prt_next);
72 	}
73 }
74 
75 static struct proto_conn *
76 proto_alloc(struct proto *proto, int side)
77 {
78 	struct proto_conn *conn;
79 
80 	PJDLOG_ASSERT(proto != NULL);
81 	PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT ||
82 	    side == PROTO_SIDE_SERVER_LISTEN ||
83 	    side == PROTO_SIDE_SERVER_WORK);
84 
85 	conn = malloc(sizeof(*conn));
86 	if (conn != NULL) {
87 		conn->pc_proto = proto;
88 		conn->pc_side = side;
89 		conn->pc_magic = PROTO_CONN_MAGIC;
90 	}
91 	return (conn);
92 }
93 
94 static void
95 proto_free(struct proto_conn *conn)
96 {
97 
98 	PJDLOG_ASSERT(conn != NULL);
99 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
100 	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT ||
101 	    conn->pc_side == PROTO_SIDE_SERVER_LISTEN ||
102 	    conn->pc_side == PROTO_SIDE_SERVER_WORK);
103 	PJDLOG_ASSERT(conn->pc_proto != NULL);
104 
105 	bzero(conn, sizeof(*conn));
106 	free(conn);
107 }
108 
109 static int
110 proto_common_setup(const char *srcaddr, const char *dstaddr,
111     struct proto_conn **connp, int side)
112 {
113 	struct proto *proto;
114 	struct proto_conn *conn;
115 	void *ctx;
116 	int ret;
117 
118 	PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT ||
119 	    side == PROTO_SIDE_SERVER_LISTEN);
120 
121 	TAILQ_FOREACH(proto, &protos, prt_next) {
122 		if (side == PROTO_SIDE_CLIENT) {
123 			if (proto->prt_client == NULL)
124 				ret = -1;
125 			else
126 				ret = proto->prt_client(srcaddr, dstaddr, &ctx);
127 		} else /* if (side == PROTO_SIDE_SERVER_LISTEN) */ {
128 			if (proto->prt_server == NULL)
129 				ret = -1;
130 			else
131 				ret = proto->prt_server(dstaddr, &ctx);
132 		}
133 		/*
134 		 * ret == 0  - success
135 		 * ret == -1 - dstaddr is not for this protocol
136 		 * ret > 0   - right protocol, but an error occurred
137 		 */
138 		if (ret >= 0)
139 			break;
140 	}
141 	if (proto == NULL) {
142 		/* Unrecognized address. */
143 		errno = EINVAL;
144 		return (-1);
145 	}
146 	if (ret > 0) {
147 		/* An error occurred. */
148 		errno = ret;
149 		return (-1);
150 	}
151 	conn = proto_alloc(proto, side);
152 	if (conn == NULL) {
153 		if (proto->prt_close != NULL)
154 			proto->prt_close(ctx);
155 		errno = ENOMEM;
156 		return (-1);
157 	}
158 	conn->pc_ctx = ctx;
159 	*connp = conn;
160 
161 	return (0);
162 }
163 
164 int
165 proto_client(const char *srcaddr, const char *dstaddr,
166     struct proto_conn **connp)
167 {
168 
169 	return (proto_common_setup(srcaddr, dstaddr, connp, PROTO_SIDE_CLIENT));
170 }
171 
172 int
173 proto_connect(struct proto_conn *conn, int timeout)
174 {
175 	int ret;
176 
177 	PJDLOG_ASSERT(conn != NULL);
178 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
179 	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT);
180 	PJDLOG_ASSERT(conn->pc_proto != NULL);
181 	PJDLOG_ASSERT(conn->pc_proto->prt_connect != NULL);
182 	PJDLOG_ASSERT(timeout >= -1);
183 
184 	ret = conn->pc_proto->prt_connect(conn->pc_ctx, timeout);
185 	if (ret != 0) {
186 		errno = ret;
187 		return (-1);
188 	}
189 
190 	return (0);
191 }
192 
193 int
194 proto_connect_wait(struct proto_conn *conn, int timeout)
195 {
196 	int ret;
197 
198 	PJDLOG_ASSERT(conn != NULL);
199 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
200 	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT);
201 	PJDLOG_ASSERT(conn->pc_proto != NULL);
202 	PJDLOG_ASSERT(conn->pc_proto->prt_connect_wait != NULL);
203 	PJDLOG_ASSERT(timeout >= 0);
204 
205 	ret = conn->pc_proto->prt_connect_wait(conn->pc_ctx, timeout);
206 	if (ret != 0) {
207 		errno = ret;
208 		return (-1);
209 	}
210 
211 	return (0);
212 }
213 
214 int
215 proto_server(const char *addr, struct proto_conn **connp)
216 {
217 
218 	return (proto_common_setup(NULL, addr, connp, PROTO_SIDE_SERVER_LISTEN));
219 }
220 
221 int
222 proto_accept(struct proto_conn *conn, struct proto_conn **newconnp)
223 {
224 	struct proto_conn *newconn;
225 	int ret;
226 
227 	PJDLOG_ASSERT(conn != NULL);
228 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
229 	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_SERVER_LISTEN);
230 	PJDLOG_ASSERT(conn->pc_proto != NULL);
231 	PJDLOG_ASSERT(conn->pc_proto->prt_accept != NULL);
232 
233 	newconn = proto_alloc(conn->pc_proto, PROTO_SIDE_SERVER_WORK);
234 	if (newconn == NULL)
235 		return (-1);
236 
237 	ret = conn->pc_proto->prt_accept(conn->pc_ctx, &newconn->pc_ctx);
238 	if (ret != 0) {
239 		proto_free(newconn);
240 		errno = ret;
241 		return (-1);
242 	}
243 
244 	*newconnp = newconn;
245 
246 	return (0);
247 }
248 
249 int
250 proto_send(const struct proto_conn *conn, const void *data, size_t size)
251 {
252 	int ret;
253 
254 	PJDLOG_ASSERT(conn != NULL);
255 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
256 	PJDLOG_ASSERT(conn->pc_proto != NULL);
257 	PJDLOG_ASSERT(conn->pc_proto->prt_send != NULL);
258 
259 	ret = conn->pc_proto->prt_send(conn->pc_ctx, data, size, -1);
260 	if (ret != 0) {
261 		errno = ret;
262 		return (-1);
263 	}
264 	return (0);
265 }
266 
267 int
268 proto_recv(const struct proto_conn *conn, void *data, size_t size)
269 {
270 	int ret;
271 
272 	PJDLOG_ASSERT(conn != NULL);
273 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
274 	PJDLOG_ASSERT(conn->pc_proto != NULL);
275 	PJDLOG_ASSERT(conn->pc_proto->prt_recv != NULL);
276 
277 	ret = conn->pc_proto->prt_recv(conn->pc_ctx, data, size, NULL);
278 	if (ret != 0) {
279 		errno = ret;
280 		return (-1);
281 	}
282 	return (0);
283 }
284 
285 int
286 proto_connection_send(const struct proto_conn *conn, struct proto_conn *mconn)
287 {
288 	const char *protoname;
289 	int ret, fd;
290 
291 	PJDLOG_ASSERT(conn != NULL);
292 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
293 	PJDLOG_ASSERT(conn->pc_proto != NULL);
294 	PJDLOG_ASSERT(conn->pc_proto->prt_send != NULL);
295 	PJDLOG_ASSERT(mconn != NULL);
296 	PJDLOG_ASSERT(mconn->pc_magic == PROTO_CONN_MAGIC);
297 	PJDLOG_ASSERT(mconn->pc_proto != NULL);
298 	fd = proto_descriptor(mconn);
299 	PJDLOG_ASSERT(fd >= 0);
300 	protoname = mconn->pc_proto->prt_name;
301 	PJDLOG_ASSERT(protoname != NULL);
302 
303 	ret = conn->pc_proto->prt_send(conn->pc_ctx,
304 	    (const unsigned char *)protoname, strlen(protoname) + 1, fd);
305 	proto_close(mconn);
306 	if (ret != 0) {
307 		errno = ret;
308 		return (-1);
309 	}
310 	return (0);
311 }
312 
313 int
314 proto_connection_recv(const struct proto_conn *conn, bool client,
315     struct proto_conn **newconnp)
316 {
317 	char protoname[128];
318 	struct proto *proto;
319 	struct proto_conn *newconn;
320 	int ret, fd;
321 
322 	PJDLOG_ASSERT(conn != NULL);
323 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
324 	PJDLOG_ASSERT(conn->pc_proto != NULL);
325 	PJDLOG_ASSERT(conn->pc_proto->prt_recv != NULL);
326 	PJDLOG_ASSERT(newconnp != NULL);
327 
328 	bzero(protoname, sizeof(protoname));
329 
330 	ret = conn->pc_proto->prt_recv(conn->pc_ctx, (unsigned char *)protoname,
331 	    sizeof(protoname) - 1, &fd);
332 	if (ret != 0) {
333 		errno = ret;
334 		return (-1);
335 	}
336 
337 	PJDLOG_ASSERT(fd >= 0);
338 
339 	TAILQ_FOREACH(proto, &protos, prt_next) {
340 		if (strcmp(proto->prt_name, protoname) == 0)
341 			break;
342 	}
343 	if (proto == NULL) {
344 		errno = EINVAL;
345 		return (-1);
346 	}
347 
348 	newconn = proto_alloc(proto,
349 	    client ? PROTO_SIDE_CLIENT : PROTO_SIDE_SERVER_WORK);
350 	if (newconn == NULL)
351 		return (-1);
352 	PJDLOG_ASSERT(newconn->pc_proto->prt_wrap != NULL);
353 	ret = newconn->pc_proto->prt_wrap(fd, client, &newconn->pc_ctx);
354 	if (ret != 0) {
355 		proto_free(newconn);
356 		errno = ret;
357 		return (-1);
358 	}
359 
360 	*newconnp = newconn;
361 
362 	return (0);
363 }
364 
365 int
366 proto_descriptor(const struct proto_conn *conn)
367 {
368 
369 	PJDLOG_ASSERT(conn != NULL);
370 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
371 	PJDLOG_ASSERT(conn->pc_proto != NULL);
372 	PJDLOG_ASSERT(conn->pc_proto->prt_descriptor != NULL);
373 
374 	return (conn->pc_proto->prt_descriptor(conn->pc_ctx));
375 }
376 
377 bool
378 proto_address_match(const struct proto_conn *conn, const char *addr)
379 {
380 
381 	PJDLOG_ASSERT(conn != NULL);
382 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
383 	PJDLOG_ASSERT(conn->pc_proto != NULL);
384 	PJDLOG_ASSERT(conn->pc_proto->prt_address_match != NULL);
385 
386 	return (conn->pc_proto->prt_address_match(conn->pc_ctx, addr));
387 }
388 
389 void
390 proto_local_address(const struct proto_conn *conn, char *addr, size_t size)
391 {
392 
393 	PJDLOG_ASSERT(conn != NULL);
394 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
395 	PJDLOG_ASSERT(conn->pc_proto != NULL);
396 	PJDLOG_ASSERT(conn->pc_proto->prt_local_address != NULL);
397 
398 	conn->pc_proto->prt_local_address(conn->pc_ctx, addr, size);
399 }
400 
401 void
402 proto_remote_address(const struct proto_conn *conn, char *addr, size_t size)
403 {
404 
405 	PJDLOG_ASSERT(conn != NULL);
406 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
407 	PJDLOG_ASSERT(conn->pc_proto != NULL);
408 	PJDLOG_ASSERT(conn->pc_proto->prt_remote_address != NULL);
409 
410 	conn->pc_proto->prt_remote_address(conn->pc_ctx, addr, size);
411 }
412 
413 int
414 proto_timeout(const struct proto_conn *conn, int timeout)
415 {
416 	struct timeval tv;
417 	int fd;
418 
419 	PJDLOG_ASSERT(conn != NULL);
420 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
421 	PJDLOG_ASSERT(conn->pc_proto != NULL);
422 
423 	fd = proto_descriptor(conn);
424 	if (fd == -1)
425 		return (-1);
426 
427 	tv.tv_sec = timeout;
428 	tv.tv_usec = 0;
429 	if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1)
430 		return (-1);
431 	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)
432 		return (-1);
433 
434 	return (0);
435 }
436 
437 void
438 proto_close(struct proto_conn *conn)
439 {
440 
441 	PJDLOG_ASSERT(conn != NULL);
442 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
443 	PJDLOG_ASSERT(conn->pc_proto != NULL);
444 	PJDLOG_ASSERT(conn->pc_proto->prt_close != NULL);
445 
446 	conn->pc_proto->prt_close(conn->pc_ctx);
447 	proto_free(conn);
448 }
449