proto.c revision 218185
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 218185 2011-02-02 08:24:26Z 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
40#include "pjdlog.h"
41#include "proto.h"
42#include "proto_impl.h"
43
44#define	PROTO_CONN_MAGIC	0x907041c
45struct 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
55static TAILQ_HEAD(, hast_proto) protos = TAILQ_HEAD_INITIALIZER(protos);
56
57void
58proto_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
71static int
72proto_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			if (proto->hp_client == NULL)
88				ret = -1;
89			else
90				ret = proto->hp_client(addr, &ctx);
91		} else /* if (side == PROTO_SIDE_SERVER_LISTEN) */ {
92			if (proto->hp_server == NULL)
93				ret = -1;
94			else
95				ret = proto->hp_server(addr, &ctx);
96		}
97		/*
98		 * ret == 0  - success
99		 * ret == -1 - addr is not for this protocol
100		 * ret > 0   - right protocol, but an error occured
101		 */
102		if (ret >= 0)
103			break;
104	}
105	if (proto == NULL) {
106		/* Unrecognized address. */
107		free(conn);
108		errno = EINVAL;
109		return (-1);
110	}
111	if (ret > 0) {
112		/* An error occured. */
113		free(conn);
114		errno = ret;
115		return (-1);
116	}
117	conn->pc_proto = proto;
118	conn->pc_ctx = ctx;
119	conn->pc_side = side;
120	conn->pc_magic = PROTO_CONN_MAGIC;
121	*connp = conn;
122	return (0);
123}
124
125int
126proto_client(const char *addr, struct proto_conn **connp)
127{
128
129	return (proto_common_setup(addr, connp, PROTO_SIDE_CLIENT));
130}
131
132int
133proto_connect(struct proto_conn *conn)
134{
135	int ret;
136
137	PJDLOG_ASSERT(conn != NULL);
138	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
139	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT);
140	PJDLOG_ASSERT(conn->pc_proto != NULL);
141	PJDLOG_ASSERT(conn->pc_proto->hp_connect != NULL);
142
143	ret = conn->pc_proto->hp_connect(conn->pc_ctx);
144	if (ret != 0) {
145		errno = ret;
146		return (-1);
147	}
148
149	return (0);
150}
151
152int
153proto_server(const char *addr, struct proto_conn **connp)
154{
155
156	return (proto_common_setup(addr, connp, PROTO_SIDE_SERVER_LISTEN));
157}
158
159int
160proto_accept(struct proto_conn *conn, struct proto_conn **newconnp)
161{
162	struct proto_conn *newconn;
163	int ret;
164
165	PJDLOG_ASSERT(conn != NULL);
166	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
167	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_SERVER_LISTEN);
168	PJDLOG_ASSERT(conn->pc_proto != NULL);
169	PJDLOG_ASSERT(conn->pc_proto->hp_accept != NULL);
170
171	newconn = malloc(sizeof(*newconn));
172	if (newconn == NULL)
173		return (-1);
174
175	ret = conn->pc_proto->hp_accept(conn->pc_ctx, &newconn->pc_ctx);
176	if (ret != 0) {
177		free(newconn);
178		errno = ret;
179		return (-1);
180	}
181
182	newconn->pc_proto = conn->pc_proto;
183	newconn->pc_side = PROTO_SIDE_SERVER_WORK;
184	newconn->pc_magic = PROTO_CONN_MAGIC;
185	*newconnp = newconn;
186
187	return (0);
188}
189
190int
191proto_send(const struct proto_conn *conn, const void *data, size_t size)
192{
193	int ret;
194
195	PJDLOG_ASSERT(conn != NULL);
196	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
197	PJDLOG_ASSERT(conn->pc_proto != NULL);
198	PJDLOG_ASSERT(conn->pc_proto->hp_send != NULL);
199
200	ret = conn->pc_proto->hp_send(conn->pc_ctx, data, size);
201	if (ret != 0) {
202		errno = ret;
203		return (-1);
204	}
205	return (0);
206}
207
208int
209proto_recv(const struct proto_conn *conn, void *data, size_t size)
210{
211	int ret;
212
213	PJDLOG_ASSERT(conn != NULL);
214	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
215	PJDLOG_ASSERT(conn->pc_proto != NULL);
216	PJDLOG_ASSERT(conn->pc_proto->hp_recv != NULL);
217
218	ret = conn->pc_proto->hp_recv(conn->pc_ctx, data, size);
219	if (ret != 0) {
220		errno = ret;
221		return (-1);
222	}
223	return (0);
224}
225
226int
227proto_descriptor_send(const struct proto_conn *conn, int fd)
228{
229	int ret;
230
231	PJDLOG_ASSERT(conn != NULL);
232	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
233	PJDLOG_ASSERT(conn->pc_proto != NULL);
234	PJDLOG_ASSERT(conn->pc_proto->hp_descriptor_send != NULL);
235
236	ret = conn->pc_proto->hp_descriptor_send(conn->pc_ctx, fd);
237	if (ret != 0) {
238		errno = ret;
239		return (-1);
240	}
241	return (0);
242}
243
244int
245proto_descriptor_recv(const struct proto_conn *conn, int *fdp)
246{
247	int ret;
248
249	PJDLOG_ASSERT(conn != NULL);
250	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
251	PJDLOG_ASSERT(conn->pc_proto != NULL);
252	PJDLOG_ASSERT(conn->pc_proto->hp_descriptor_recv != NULL);
253
254	ret = conn->pc_proto->hp_descriptor_recv(conn->pc_ctx, fdp);
255	if (ret != 0) {
256		errno = ret;
257		return (-1);
258	}
259	return (0);
260}
261
262int
263proto_descriptor(const struct proto_conn *conn)
264{
265
266	PJDLOG_ASSERT(conn != NULL);
267	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
268	PJDLOG_ASSERT(conn->pc_proto != NULL);
269	PJDLOG_ASSERT(conn->pc_proto->hp_descriptor != NULL);
270
271	return (conn->pc_proto->hp_descriptor(conn->pc_ctx));
272}
273
274bool
275proto_address_match(const struct proto_conn *conn, const char *addr)
276{
277
278	PJDLOG_ASSERT(conn != NULL);
279	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
280	PJDLOG_ASSERT(conn->pc_proto != NULL);
281	PJDLOG_ASSERT(conn->pc_proto->hp_address_match != NULL);
282
283	return (conn->pc_proto->hp_address_match(conn->pc_ctx, addr));
284}
285
286void
287proto_local_address(const struct proto_conn *conn, char *addr, size_t size)
288{
289
290	PJDLOG_ASSERT(conn != NULL);
291	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
292	PJDLOG_ASSERT(conn->pc_proto != NULL);
293	PJDLOG_ASSERT(conn->pc_proto->hp_local_address != NULL);
294
295	conn->pc_proto->hp_local_address(conn->pc_ctx, addr, size);
296}
297
298void
299proto_remote_address(const struct proto_conn *conn, char *addr, size_t size)
300{
301
302	PJDLOG_ASSERT(conn != NULL);
303	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
304	PJDLOG_ASSERT(conn->pc_proto != NULL);
305	PJDLOG_ASSERT(conn->pc_proto->hp_remote_address != NULL);
306
307	conn->pc_proto->hp_remote_address(conn->pc_ctx, addr, size);
308}
309
310int
311proto_timeout(const struct proto_conn *conn, int timeout)
312{
313	struct timeval tv;
314	int fd;
315
316	PJDLOG_ASSERT(conn != NULL);
317	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
318	PJDLOG_ASSERT(conn->pc_proto != NULL);
319
320	fd = proto_descriptor(conn);
321	if (fd < 0)
322		return (-1);
323
324	tv.tv_sec = timeout;
325	tv.tv_usec = 0;
326	if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
327		return (-1);
328	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
329		return (-1);
330
331	return (0);
332}
333
334void
335proto_close(struct proto_conn *conn)
336{
337
338	PJDLOG_ASSERT(conn != NULL);
339	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
340	PJDLOG_ASSERT(conn->pc_proto != NULL);
341	PJDLOG_ASSERT(conn->pc_proto->hp_close != NULL);
342
343	conn->pc_proto->hp_close(conn->pc_ctx);
344	conn->pc_magic = 0;
345	free(conn);
346}
347