proto.c revision 218138
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 218138 2011-01-31 18:32:17Z 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			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
118int
119proto_client(const char *addr, struct proto_conn **connp)
120{
121
122	return (proto_common_setup(addr, connp, PROTO_SIDE_CLIENT));
123}
124
125int
126proto_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
145int
146proto_server(const char *addr, struct proto_conn **connp)
147{
148
149	return (proto_common_setup(addr, connp, PROTO_SIDE_SERVER_LISTEN));
150}
151
152int
153proto_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
183int
184proto_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
201int
202proto_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
219int
220proto_descriptor(const struct proto_conn *conn)
221{
222
223	PJDLOG_ASSERT(conn != NULL);
224	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
225	PJDLOG_ASSERT(conn->pc_proto != NULL);
226	PJDLOG_ASSERT(conn->pc_proto->hp_descriptor != NULL);
227
228	return (conn->pc_proto->hp_descriptor(conn->pc_ctx));
229}
230
231bool
232proto_address_match(const struct proto_conn *conn, const char *addr)
233{
234
235	PJDLOG_ASSERT(conn != NULL);
236	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
237	PJDLOG_ASSERT(conn->pc_proto != NULL);
238	PJDLOG_ASSERT(conn->pc_proto->hp_address_match != NULL);
239
240	return (conn->pc_proto->hp_address_match(conn->pc_ctx, addr));
241}
242
243void
244proto_local_address(const struct proto_conn *conn, char *addr, size_t size)
245{
246
247	PJDLOG_ASSERT(conn != NULL);
248	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
249	PJDLOG_ASSERT(conn->pc_proto != NULL);
250	PJDLOG_ASSERT(conn->pc_proto->hp_local_address != NULL);
251
252	conn->pc_proto->hp_local_address(conn->pc_ctx, addr, size);
253}
254
255void
256proto_remote_address(const struct proto_conn *conn, char *addr, size_t size)
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_remote_address != NULL);
263
264	conn->pc_proto->hp_remote_address(conn->pc_ctx, addr, size);
265}
266
267int
268proto_timeout(const struct proto_conn *conn, int timeout)
269{
270	struct timeval tv;
271	int fd;
272
273	PJDLOG_ASSERT(conn != NULL);
274	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
275	PJDLOG_ASSERT(conn->pc_proto != NULL);
276
277	fd = proto_descriptor(conn);
278	if (fd < 0)
279		return (-1);
280
281	tv.tv_sec = timeout;
282	tv.tv_usec = 0;
283	if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
284		return (-1);
285	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
286		return (-1);
287
288	return (0);
289}
290
291void
292proto_close(struct proto_conn *conn)
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_close != NULL);
299
300	conn->pc_proto->hp_close(conn->pc_ctx);
301	conn->pc_magic = 0;
302	free(conn);
303}
304