xref: /freebsd/sbin/hastd/proto.c (revision 1670a1c2a47d10ecccd001970b859caf93cd3b6e)
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 <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
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 LIST_HEAD(, hast_proto) protos = LIST_HEAD_INITIALIZER(protos);
56 
57 void
58 proto_register(struct hast_proto *proto)
59 {
60 
61 	LIST_INSERT_HEAD(&protos, proto, hp_next);
62 }
63 
64 static int
65 proto_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 
111 int
112 proto_client(const char *addr, struct proto_conn **connp)
113 {
114 
115 	return (proto_common_setup(addr, connp, PROTO_SIDE_CLIENT));
116 }
117 
118 int
119 proto_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 
137 int
138 proto_server(const char *addr, struct proto_conn **connp)
139 {
140 
141 	return (proto_common_setup(addr, connp, PROTO_SIDE_SERVER_LISTEN));
142 }
143 
144 int
145 proto_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 
174 int
175 proto_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 
191 int
192 proto_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 
208 int
209 proto_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 
219 bool
220 proto_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 
230 void
231 proto_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 
241 void
242 proto_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 
252 int
253 proto_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 
276 void
277 proto_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