xref: /freebsd/sbin/hastd/proto.c (revision 8046c499abd461a51517067641dc7a13de88a1cb)
1 /*-
2  * Copyright (c) 2009-2010 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/types.h>
34 #include <sys/queue.h>
35 #include <sys/socket.h>
36 
37 #include <errno.h>
38 #include <stdint.h>
39 
40 #include "pjdlog.h"
41 #include "proto.h"
42 #include "proto_impl.h"
43 
44 #define	PROTO_CONN_MAGIC	0x907041c
45 struct proto_conn {
46 	int			 pc_magic;
47 	struct hast_proto	*pc_proto;
48 	void			*pc_ctx;
49 	int			 pc_side;
50 #define	PROTO_SIDE_CLIENT		0
51 #define	PROTO_SIDE_SERVER_LISTEN	1
52 #define	PROTO_SIDE_SERVER_WORK		2
53 };
54 
55 static TAILQ_HEAD(, hast_proto) protos = TAILQ_HEAD_INITIALIZER(protos);
56 
57 void
58 proto_register(struct hast_proto *proto, bool isdefault)
59 {
60 	static bool seen_default = false;
61 
62 	if (!isdefault)
63 		TAILQ_INSERT_HEAD(&protos, proto, hp_next);
64 	else {
65 		PJDLOG_ASSERT(!seen_default);
66 		seen_default = true;
67 		TAILQ_INSERT_TAIL(&protos, proto, hp_next);
68 	}
69 }
70 
71 static int
72 proto_common_setup(const char *addr, struct proto_conn **connp, int side)
73 {
74 	struct hast_proto *proto;
75 	struct proto_conn *conn;
76 	void *ctx;
77 	int ret;
78 
79 	PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT || side == PROTO_SIDE_SERVER_LISTEN);
80 
81 	conn = malloc(sizeof(*conn));
82 	if (conn == NULL)
83 		return (-1);
84 
85 	TAILQ_FOREACH(proto, &protos, hp_next) {
86 		if (side == PROTO_SIDE_CLIENT)
87 			ret = proto->hp_client(addr, &ctx);
88 		else /* if (side == PROTO_SIDE_SERVER_LISTEN) */
89 			ret = proto->hp_server(addr, &ctx);
90 		/*
91 		 * ret == 0  - success
92 		 * ret == -1 - addr is not for this protocol
93 		 * ret > 0   - right protocol, but an error occured
94 		 */
95 		if (ret >= 0)
96 			break;
97 	}
98 	if (proto == NULL) {
99 		/* Unrecognized address. */
100 		free(conn);
101 		errno = EINVAL;
102 		return (-1);
103 	}
104 	if (ret > 0) {
105 		/* An error occured. */
106 		free(conn);
107 		errno = ret;
108 		return (-1);
109 	}
110 	conn->pc_proto = proto;
111 	conn->pc_ctx = ctx;
112 	conn->pc_side = side;
113 	conn->pc_magic = PROTO_CONN_MAGIC;
114 	*connp = conn;
115 	return (0);
116 }
117 
118 int
119 proto_client(const char *addr, struct proto_conn **connp)
120 {
121 
122 	return (proto_common_setup(addr, connp, PROTO_SIDE_CLIENT));
123 }
124 
125 int
126 proto_connect(struct proto_conn *conn)
127 {
128 	int ret;
129 
130 	PJDLOG_ASSERT(conn != NULL);
131 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
132 	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT);
133 	PJDLOG_ASSERT(conn->pc_proto != NULL);
134 	PJDLOG_ASSERT(conn->pc_proto->hp_connect != NULL);
135 
136 	ret = conn->pc_proto->hp_connect(conn->pc_ctx);
137 	if (ret != 0) {
138 		errno = ret;
139 		return (-1);
140 	}
141 
142 	return (0);
143 }
144 
145 int
146 proto_server(const char *addr, struct proto_conn **connp)
147 {
148 
149 	return (proto_common_setup(addr, connp, PROTO_SIDE_SERVER_LISTEN));
150 }
151 
152 int
153 proto_accept(struct proto_conn *conn, struct proto_conn **newconnp)
154 {
155 	struct proto_conn *newconn;
156 	int ret;
157 
158 	PJDLOG_ASSERT(conn != NULL);
159 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
160 	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_SERVER_LISTEN);
161 	PJDLOG_ASSERT(conn->pc_proto != NULL);
162 	PJDLOG_ASSERT(conn->pc_proto->hp_accept != NULL);
163 
164 	newconn = malloc(sizeof(*newconn));
165 	if (newconn == NULL)
166 		return (-1);
167 
168 	ret = conn->pc_proto->hp_accept(conn->pc_ctx, &newconn->pc_ctx);
169 	if (ret != 0) {
170 		free(newconn);
171 		errno = ret;
172 		return (-1);
173 	}
174 
175 	newconn->pc_proto = conn->pc_proto;
176 	newconn->pc_side = PROTO_SIDE_SERVER_WORK;
177 	newconn->pc_magic = PROTO_CONN_MAGIC;
178 	*newconnp = newconn;
179 
180 	return (0);
181 }
182 
183 int
184 proto_send(const struct proto_conn *conn, const void *data, size_t size)
185 {
186 	int ret;
187 
188 	PJDLOG_ASSERT(conn != NULL);
189 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
190 	PJDLOG_ASSERT(conn->pc_proto != NULL);
191 	PJDLOG_ASSERT(conn->pc_proto->hp_send != NULL);
192 
193 	ret = conn->pc_proto->hp_send(conn->pc_ctx, data, size);
194 	if (ret != 0) {
195 		errno = ret;
196 		return (-1);
197 	}
198 	return (0);
199 }
200 
201 int
202 proto_recv(const struct proto_conn *conn, void *data, size_t size)
203 {
204 	int ret;
205 
206 	PJDLOG_ASSERT(conn != NULL);
207 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
208 	PJDLOG_ASSERT(conn->pc_proto != NULL);
209 	PJDLOG_ASSERT(conn->pc_proto->hp_recv != NULL);
210 
211 	ret = conn->pc_proto->hp_recv(conn->pc_ctx, data, size);
212 	if (ret != 0) {
213 		errno = ret;
214 		return (-1);
215 	}
216 	return (0);
217 }
218 
219 int
220 proto_descriptor_send(const struct proto_conn *conn, int fd)
221 {
222 	int ret;
223 
224 	PJDLOG_ASSERT(conn != NULL);
225 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
226 	PJDLOG_ASSERT(conn->pc_proto != NULL);
227 	PJDLOG_ASSERT(conn->pc_proto->hp_descriptor_send != NULL);
228 
229 	ret = conn->pc_proto->hp_descriptor_send(conn->pc_ctx, fd);
230 	if (ret != 0) {
231 		errno = ret;
232 		return (-1);
233 	}
234 	return (0);
235 }
236 
237 int
238 proto_descriptor_recv(const struct proto_conn *conn, int *fdp)
239 {
240 	int ret;
241 
242 	PJDLOG_ASSERT(conn != NULL);
243 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
244 	PJDLOG_ASSERT(conn->pc_proto != NULL);
245 	PJDLOG_ASSERT(conn->pc_proto->hp_descriptor_recv != NULL);
246 
247 	ret = conn->pc_proto->hp_descriptor_recv(conn->pc_ctx, fdp);
248 	if (ret != 0) {
249 		errno = ret;
250 		return (-1);
251 	}
252 	return (0);
253 }
254 
255 int
256 proto_descriptor(const struct proto_conn *conn)
257 {
258 
259 	PJDLOG_ASSERT(conn != NULL);
260 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
261 	PJDLOG_ASSERT(conn->pc_proto != NULL);
262 	PJDLOG_ASSERT(conn->pc_proto->hp_descriptor != NULL);
263 
264 	return (conn->pc_proto->hp_descriptor(conn->pc_ctx));
265 }
266 
267 bool
268 proto_address_match(const struct proto_conn *conn, const char *addr)
269 {
270 
271 	PJDLOG_ASSERT(conn != NULL);
272 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
273 	PJDLOG_ASSERT(conn->pc_proto != NULL);
274 	PJDLOG_ASSERT(conn->pc_proto->hp_address_match != NULL);
275 
276 	return (conn->pc_proto->hp_address_match(conn->pc_ctx, addr));
277 }
278 
279 void
280 proto_local_address(const struct proto_conn *conn, char *addr, size_t size)
281 {
282 
283 	PJDLOG_ASSERT(conn != NULL);
284 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
285 	PJDLOG_ASSERT(conn->pc_proto != NULL);
286 	PJDLOG_ASSERT(conn->pc_proto->hp_local_address != NULL);
287 
288 	conn->pc_proto->hp_local_address(conn->pc_ctx, addr, size);
289 }
290 
291 void
292 proto_remote_address(const struct proto_conn *conn, char *addr, size_t size)
293 {
294 
295 	PJDLOG_ASSERT(conn != NULL);
296 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
297 	PJDLOG_ASSERT(conn->pc_proto != NULL);
298 	PJDLOG_ASSERT(conn->pc_proto->hp_remote_address != NULL);
299 
300 	conn->pc_proto->hp_remote_address(conn->pc_ctx, addr, size);
301 }
302 
303 int
304 proto_timeout(const struct proto_conn *conn, int timeout)
305 {
306 	struct timeval tv;
307 	int fd;
308 
309 	PJDLOG_ASSERT(conn != NULL);
310 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
311 	PJDLOG_ASSERT(conn->pc_proto != NULL);
312 
313 	fd = proto_descriptor(conn);
314 	if (fd < 0)
315 		return (-1);
316 
317 	tv.tv_sec = timeout;
318 	tv.tv_usec = 0;
319 	if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
320 		return (-1);
321 	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
322 		return (-1);
323 
324 	return (0);
325 }
326 
327 void
328 proto_close(struct proto_conn *conn)
329 {
330 
331 	PJDLOG_ASSERT(conn != NULL);
332 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
333 	PJDLOG_ASSERT(conn->pc_proto != NULL);
334 	PJDLOG_ASSERT(conn->pc_proto->hp_close != NULL);
335 
336 	conn->pc_proto->hp_close(conn->pc_ctx);
337 	conn->pc_magic = 0;
338 	free(conn);
339 }
340