proto.c revision 218194
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: head/sbin/hastd/proto.c 218194 2011-02-02 15:53:09Z pjd $");
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#include <string.h>
40#include <strings.h>
41
42#include "pjdlog.h"
43#include "proto.h"
44#include "proto_impl.h"
45
46#define	PROTO_CONN_MAGIC	0x907041c
47struct proto_conn {
48	int			 pc_magic;
49	struct hast_proto	*pc_proto;
50	void			*pc_ctx;
51	int			 pc_side;
52#define	PROTO_SIDE_CLIENT		0
53#define	PROTO_SIDE_SERVER_LISTEN	1
54#define	PROTO_SIDE_SERVER_WORK		2
55};
56
57static TAILQ_HEAD(, hast_proto) protos = TAILQ_HEAD_INITIALIZER(protos);
58
59void
60proto_register(struct hast_proto *proto, bool isdefault)
61{
62	static bool seen_default = false;
63
64	if (!isdefault)
65		TAILQ_INSERT_HEAD(&protos, proto, hp_next);
66	else {
67		PJDLOG_ASSERT(!seen_default);
68		seen_default = true;
69		TAILQ_INSERT_TAIL(&protos, proto, hp_next);
70	}
71}
72
73static struct proto_conn *
74proto_alloc(struct hast_proto *proto, int side)
75{
76	struct proto_conn *conn;
77
78	PJDLOG_ASSERT(proto != NULL);
79	PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT ||
80	    side == PROTO_SIDE_SERVER_LISTEN ||
81	    side == PROTO_SIDE_SERVER_WORK);
82
83	conn = malloc(sizeof(*conn));
84	if (conn != NULL) {
85		conn->pc_proto = proto;
86		conn->pc_side = side;
87		conn->pc_magic = PROTO_CONN_MAGIC;
88	}
89	return (conn);
90}
91
92static void
93proto_free(struct proto_conn *conn)
94{
95
96	PJDLOG_ASSERT(conn != NULL);
97	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
98	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT ||
99	    conn->pc_side == PROTO_SIDE_SERVER_LISTEN ||
100	    conn->pc_side == PROTO_SIDE_SERVER_WORK);
101	PJDLOG_ASSERT(conn->pc_proto != NULL);
102
103	bzero(conn, sizeof(*conn));
104	free(conn);
105}
106
107static int
108proto_common_setup(const char *addr, struct proto_conn **connp, int side)
109{
110	struct hast_proto *proto;
111	struct proto_conn *conn;
112	void *ctx;
113	int ret;
114
115	PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT ||
116	    side == PROTO_SIDE_SERVER_LISTEN);
117
118	TAILQ_FOREACH(proto, &protos, hp_next) {
119		if (side == PROTO_SIDE_CLIENT) {
120			if (proto->hp_client == NULL)
121				ret = -1;
122			else
123				ret = proto->hp_client(addr, &ctx);
124		} else /* if (side == PROTO_SIDE_SERVER_LISTEN) */ {
125			if (proto->hp_server == NULL)
126				ret = -1;
127			else
128				ret = proto->hp_server(addr, &ctx);
129		}
130		/*
131		 * ret == 0  - success
132		 * ret == -1 - addr is not for this protocol
133		 * ret > 0   - right protocol, but an error occured
134		 */
135		if (ret >= 0)
136			break;
137	}
138	if (proto == NULL) {
139		/* Unrecognized address. */
140		errno = EINVAL;
141		return (-1);
142	}
143	if (ret > 0) {
144		/* An error occured. */
145		errno = ret;
146		return (-1);
147	}
148	conn = proto_alloc(proto, side);
149	if (conn == NULL) {
150		if (proto->hp_close != NULL)
151			proto->hp_close(ctx);
152		errno = ENOMEM;
153		return (-1);
154	}
155	conn->pc_ctx = ctx;
156	*connp = conn;
157
158	return (0);
159}
160
161int
162proto_client(const char *addr, struct proto_conn **connp)
163{
164
165	return (proto_common_setup(addr, connp, PROTO_SIDE_CLIENT));
166}
167
168int
169proto_connect(struct proto_conn *conn, int timeout)
170{
171	int ret;
172
173	PJDLOG_ASSERT(conn != NULL);
174	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
175	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT);
176	PJDLOG_ASSERT(conn->pc_proto != NULL);
177	PJDLOG_ASSERT(conn->pc_proto->hp_connect != NULL);
178	PJDLOG_ASSERT(timeout >= -1);
179
180	ret = conn->pc_proto->hp_connect(conn->pc_ctx, timeout);
181	if (ret != 0) {
182		errno = ret;
183		return (-1);
184	}
185
186	return (0);
187}
188
189int
190proto_connect_wait(struct proto_conn *conn, int timeout)
191{
192	int ret;
193
194	PJDLOG_ASSERT(conn != NULL);
195	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
196	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT);
197	PJDLOG_ASSERT(conn->pc_proto != NULL);
198	PJDLOG_ASSERT(conn->pc_proto->hp_connect_wait != NULL);
199	PJDLOG_ASSERT(timeout >= 0);
200
201	ret = conn->pc_proto->hp_connect_wait(conn->pc_ctx, timeout);
202	if (ret != 0) {
203		errno = ret;
204		return (-1);
205	}
206
207	return (0);
208}
209
210int
211proto_server(const char *addr, struct proto_conn **connp)
212{
213
214	return (proto_common_setup(addr, connp, PROTO_SIDE_SERVER_LISTEN));
215}
216
217int
218proto_accept(struct proto_conn *conn, struct proto_conn **newconnp)
219{
220	struct proto_conn *newconn;
221	int ret;
222
223	PJDLOG_ASSERT(conn != NULL);
224	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
225	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_SERVER_LISTEN);
226	PJDLOG_ASSERT(conn->pc_proto != NULL);
227	PJDLOG_ASSERT(conn->pc_proto->hp_accept != NULL);
228
229	newconn = proto_alloc(conn->pc_proto, PROTO_SIDE_SERVER_WORK);
230	if (newconn == NULL)
231		return (-1);
232
233	ret = conn->pc_proto->hp_accept(conn->pc_ctx, &newconn->pc_ctx);
234	if (ret != 0) {
235		proto_free(newconn);
236		errno = ret;
237		return (-1);
238	}
239
240	*newconnp = newconn;
241
242	return (0);
243}
244
245int
246proto_send(const struct proto_conn *conn, const void *data, size_t size)
247{
248	int ret;
249
250	PJDLOG_ASSERT(conn != NULL);
251	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
252	PJDLOG_ASSERT(conn->pc_proto != NULL);
253	PJDLOG_ASSERT(conn->pc_proto->hp_send != NULL);
254
255	ret = conn->pc_proto->hp_send(conn->pc_ctx, data, size, -1);
256	if (ret != 0) {
257		errno = ret;
258		return (-1);
259	}
260	return (0);
261}
262
263int
264proto_recv(const struct proto_conn *conn, void *data, size_t size)
265{
266	int ret;
267
268	PJDLOG_ASSERT(conn != NULL);
269	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
270	PJDLOG_ASSERT(conn->pc_proto != NULL);
271	PJDLOG_ASSERT(conn->pc_proto->hp_recv != NULL);
272
273	ret = conn->pc_proto->hp_recv(conn->pc_ctx, data, size, NULL);
274	if (ret != 0) {
275		errno = ret;
276		return (-1);
277	}
278	return (0);
279}
280
281int
282proto_connection_send(const struct proto_conn *conn, struct proto_conn *mconn)
283{
284	const char *protoname;
285	int ret, fd;
286
287	PJDLOG_ASSERT(conn != NULL);
288	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
289	PJDLOG_ASSERT(conn->pc_proto != NULL);
290	PJDLOG_ASSERT(conn->pc_proto->hp_send != NULL);
291	PJDLOG_ASSERT(mconn != NULL);
292	PJDLOG_ASSERT(mconn->pc_magic == PROTO_CONN_MAGIC);
293	PJDLOG_ASSERT(mconn->pc_proto != NULL);
294	fd = proto_descriptor(mconn);
295	PJDLOG_ASSERT(fd >= 0);
296	protoname = mconn->pc_proto->hp_name;
297	PJDLOG_ASSERT(protoname != NULL);
298
299	ret = conn->pc_proto->hp_send(conn->pc_ctx, protoname,
300	    strlen(protoname) + 1, fd);
301	proto_close(mconn);
302	if (ret != 0) {
303		errno = ret;
304		return (-1);
305	}
306	return (0);
307}
308
309int
310proto_connection_recv(const struct proto_conn *conn, bool client,
311    struct proto_conn **newconnp)
312{
313	char protoname[128];
314	struct hast_proto *proto;
315	struct proto_conn *newconn;
316	int ret, fd;
317
318	PJDLOG_ASSERT(conn != NULL);
319	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
320	PJDLOG_ASSERT(conn->pc_proto != NULL);
321	PJDLOG_ASSERT(conn->pc_proto->hp_recv != NULL);
322	PJDLOG_ASSERT(newconnp != NULL);
323
324	bzero(protoname, sizeof(protoname));
325
326	ret = conn->pc_proto->hp_recv(conn->pc_ctx, protoname,
327	    sizeof(protoname) - 1, &fd);
328	if (ret != 0) {
329		errno = ret;
330		return (-1);
331	}
332
333	PJDLOG_ASSERT(fd >= 0);
334
335	TAILQ_FOREACH(proto, &protos, hp_next) {
336		if (strcmp(proto->hp_name, protoname) == 0)
337			break;
338	}
339	if (proto == NULL) {
340		errno = EINVAL;
341		return (-1);
342	}
343
344	newconn = proto_alloc(proto,
345	    client ? PROTO_SIDE_CLIENT : PROTO_SIDE_SERVER_WORK);
346	if (newconn == NULL)
347		return (-1);
348	PJDLOG_ASSERT(newconn->pc_proto->hp_wrap != NULL);
349	ret = newconn->pc_proto->hp_wrap(fd, client, &newconn->pc_ctx);
350	if (ret != 0) {
351		proto_free(newconn);
352		errno = ret;
353		return (-1);
354	}
355
356	*newconnp = newconn;
357
358	return (0);
359}
360
361int
362proto_descriptor(const struct proto_conn *conn)
363{
364
365	PJDLOG_ASSERT(conn != NULL);
366	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
367	PJDLOG_ASSERT(conn->pc_proto != NULL);
368	PJDLOG_ASSERT(conn->pc_proto->hp_descriptor != NULL);
369
370	return (conn->pc_proto->hp_descriptor(conn->pc_ctx));
371}
372
373bool
374proto_address_match(const struct proto_conn *conn, const char *addr)
375{
376
377	PJDLOG_ASSERT(conn != NULL);
378	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
379	PJDLOG_ASSERT(conn->pc_proto != NULL);
380	PJDLOG_ASSERT(conn->pc_proto->hp_address_match != NULL);
381
382	return (conn->pc_proto->hp_address_match(conn->pc_ctx, addr));
383}
384
385void
386proto_local_address(const struct proto_conn *conn, char *addr, size_t size)
387{
388
389	PJDLOG_ASSERT(conn != NULL);
390	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
391	PJDLOG_ASSERT(conn->pc_proto != NULL);
392	PJDLOG_ASSERT(conn->pc_proto->hp_local_address != NULL);
393
394	conn->pc_proto->hp_local_address(conn->pc_ctx, addr, size);
395}
396
397void
398proto_remote_address(const struct proto_conn *conn, char *addr, size_t size)
399{
400
401	PJDLOG_ASSERT(conn != NULL);
402	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
403	PJDLOG_ASSERT(conn->pc_proto != NULL);
404	PJDLOG_ASSERT(conn->pc_proto->hp_remote_address != NULL);
405
406	conn->pc_proto->hp_remote_address(conn->pc_ctx, addr, size);
407}
408
409int
410proto_timeout(const struct proto_conn *conn, int timeout)
411{
412	struct timeval tv;
413	int fd;
414
415	PJDLOG_ASSERT(conn != NULL);
416	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
417	PJDLOG_ASSERT(conn->pc_proto != NULL);
418
419	fd = proto_descriptor(conn);
420	if (fd < 0)
421		return (-1);
422
423	tv.tv_sec = timeout;
424	tv.tv_usec = 0;
425	if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
426		return (-1);
427	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
428		return (-1);
429
430	return (0);
431}
432
433void
434proto_close(struct proto_conn *conn)
435{
436
437	PJDLOG_ASSERT(conn != NULL);
438	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
439	PJDLOG_ASSERT(conn->pc_proto != NULL);
440	PJDLOG_ASSERT(conn->pc_proto->hp_close != NULL);
441
442	conn->pc_proto->hp_close(conn->pc_ctx);
443	proto_free(conn);
444}
445