proto.c revision 207371
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 207371 2010-04-29 15:36:32Z pjd $");
32
33#include <sys/types.h>
34#include <sys/queue.h>
35#include <sys/socket.h>
36
37#include <assert.h>
38#include <errno.h>
39#include <stdint.h>
40
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 LIST_HEAD(, hast_proto) protos = LIST_HEAD_INITIALIZER(protos);
56
57void
58proto_register(struct hast_proto *proto)
59{
60
61	LIST_INSERT_HEAD(&protos, proto, hp_next);
62}
63
64static int
65proto_common_setup(const char *addr, struct proto_conn **connp, int side)
66{
67	struct hast_proto *proto;
68	struct proto_conn *conn;
69	void *ctx;
70	int ret;
71
72	assert(side == PROTO_SIDE_CLIENT || side == PROTO_SIDE_SERVER_LISTEN);
73
74	conn = malloc(sizeof(*conn));
75	if (conn == NULL)
76		return (-1);
77
78	LIST_FOREACH(proto, &protos, hp_next) {
79		if (side == PROTO_SIDE_CLIENT)
80			ret = proto->hp_client(addr, &ctx);
81		else /* if (side == PROTO_SIDE_SERVER_LISTEN) */
82			ret = proto->hp_server(addr, &ctx);
83		/*
84		 * ret == 0  - success
85		 * ret == -1 - addr is not for this protocol
86		 * ret > 0   - right protocol, but an error occured
87		 */
88		if (ret >= 0)
89			break;
90	}
91	if (proto == NULL) {
92		/* Unrecognized address. */
93		free(conn);
94		errno = EINVAL;
95		return (-1);
96	}
97	if (ret > 0) {
98		/* An error occured. */
99		free(conn);
100		errno = ret;
101		return (-1);
102	}
103	conn->pc_proto = proto;
104	conn->pc_ctx = ctx;
105	conn->pc_side = side;
106	conn->pc_magic = PROTO_CONN_MAGIC;
107	*connp = conn;
108	return (0);
109}
110
111int
112proto_client(const char *addr, struct proto_conn **connp)
113{
114
115	return (proto_common_setup(addr, connp, PROTO_SIDE_CLIENT));
116}
117
118int
119proto_connect(struct proto_conn *conn)
120{
121	int ret;
122
123	assert(conn != NULL);
124	assert(conn->pc_magic == PROTO_CONN_MAGIC);
125	assert(conn->pc_side == PROTO_SIDE_CLIENT);
126	assert(conn->pc_proto != NULL);
127
128	ret = conn->pc_proto->hp_connect(conn->pc_ctx);
129	if (ret != 0) {
130		errno = ret;
131		return (-1);
132	}
133
134	return (0);
135}
136
137int
138proto_server(const char *addr, struct proto_conn **connp)
139{
140
141	return (proto_common_setup(addr, connp, PROTO_SIDE_SERVER_LISTEN));
142}
143
144int
145proto_accept(struct proto_conn *conn, struct proto_conn **newconnp)
146{
147	struct proto_conn *newconn;
148	int ret;
149
150	assert(conn != NULL);
151	assert(conn->pc_magic == PROTO_CONN_MAGIC);
152	assert(conn->pc_side == PROTO_SIDE_SERVER_LISTEN);
153	assert(conn->pc_proto != NULL);
154
155	newconn = malloc(sizeof(*newconn));
156	if (newconn == NULL)
157		return (-1);
158
159	ret = conn->pc_proto->hp_accept(conn->pc_ctx, &newconn->pc_ctx);
160	if (ret != 0) {
161		free(newconn);
162		errno = ret;
163		return (-1);
164	}
165
166	newconn->pc_proto = conn->pc_proto;
167	newconn->pc_side = PROTO_SIDE_SERVER_WORK;
168	newconn->pc_magic = PROTO_CONN_MAGIC;
169	*newconnp = newconn;
170
171	return (0);
172}
173
174int
175proto_send(struct proto_conn *conn, const void *data, size_t size)
176{
177	int ret;
178
179	assert(conn != NULL);
180	assert(conn->pc_magic == PROTO_CONN_MAGIC);
181	assert(conn->pc_proto != NULL);
182
183	ret = conn->pc_proto->hp_send(conn->pc_ctx, data, size);
184	if (ret != 0) {
185		errno = ret;
186		return (-1);
187	}
188	return (0);
189}
190
191int
192proto_recv(struct proto_conn *conn, void *data, size_t size)
193{
194	int ret;
195
196	assert(conn != NULL);
197	assert(conn->pc_magic == PROTO_CONN_MAGIC);
198	assert(conn->pc_proto != NULL);
199
200	ret = conn->pc_proto->hp_recv(conn->pc_ctx, data, size);
201	if (ret != 0) {
202		errno = ret;
203		return (-1);
204	}
205	return (0);
206}
207
208int
209proto_descriptor(const struct proto_conn *conn)
210{
211
212	assert(conn != NULL);
213	assert(conn->pc_magic == PROTO_CONN_MAGIC);
214	assert(conn->pc_proto != NULL);
215
216	return (conn->pc_proto->hp_descriptor(conn->pc_ctx));
217}
218
219bool
220proto_address_match(const struct proto_conn *conn, const char *addr)
221{
222
223	assert(conn != NULL);
224	assert(conn->pc_magic == PROTO_CONN_MAGIC);
225	assert(conn->pc_proto != NULL);
226
227	return (conn->pc_proto->hp_address_match(conn->pc_ctx, addr));
228}
229
230void
231proto_local_address(const struct proto_conn *conn, char *addr, size_t size)
232{
233
234	assert(conn != NULL);
235	assert(conn->pc_magic == PROTO_CONN_MAGIC);
236	assert(conn->pc_proto != NULL);
237
238	conn->pc_proto->hp_local_address(conn->pc_ctx, addr, size);
239}
240
241void
242proto_remote_address(const struct proto_conn *conn, char *addr, size_t size)
243{
244
245	assert(conn != NULL);
246	assert(conn->pc_magic == PROTO_CONN_MAGIC);
247	assert(conn->pc_proto != NULL);
248
249	conn->pc_proto->hp_remote_address(conn->pc_ctx, addr, size);
250}
251
252int
253proto_timeout(const struct proto_conn *conn, int timeout)
254{
255	struct timeval tv;
256	int fd;
257
258	assert(conn != NULL);
259	assert(conn->pc_magic == PROTO_CONN_MAGIC);
260	assert(conn->pc_proto != NULL);
261
262	fd = proto_descriptor(conn);
263	if (fd < 0)
264		return (-1);
265
266	tv.tv_sec = timeout;
267	tv.tv_usec = 0;
268	if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
269		return (-1);
270	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
271		return (-1);
272
273	return (0);
274}
275
276void
277proto_close(struct proto_conn *conn)
278{
279
280	assert(conn != NULL);
281	assert(conn->pc_magic == PROTO_CONN_MAGIC);
282	assert(conn->pc_proto != NULL);
283
284	conn->pc_proto->hp_close(conn->pc_ctx);
285	conn->pc_magic = 0;
286	free(conn);
287}
288