Deleted Added
sdiff udiff text old ( 210869 ) new ( 212033 )
full compact
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 212033 2010-08-30 22:26:42Z 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 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 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 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 assert(conn != NULL);
131 assert(conn->pc_magic == PROTO_CONN_MAGIC);
132 assert(conn->pc_side == PROTO_SIDE_CLIENT);
133 assert(conn->pc_proto != NULL);
134
135 ret = conn->pc_proto->hp_connect(conn->pc_ctx);
136 if (ret != 0) {
137 errno = ret;
138 return (-1);
139 }
140
141 return (0);
142}
143
144int
145proto_server(const char *addr, struct proto_conn **connp)
146{
147
148 return (proto_common_setup(addr, connp, PROTO_SIDE_SERVER_LISTEN));
149}
150
151int
152proto_accept(struct proto_conn *conn, struct proto_conn **newconnp)
153{
154 struct proto_conn *newconn;
155 int ret;
156
157 assert(conn != NULL);
158 assert(conn->pc_magic == PROTO_CONN_MAGIC);
159 assert(conn->pc_side == PROTO_SIDE_SERVER_LISTEN);
160 assert(conn->pc_proto != NULL);
161
162 newconn = malloc(sizeof(*newconn));
163 if (newconn == NULL)
164 return (-1);
165
166 ret = conn->pc_proto->hp_accept(conn->pc_ctx, &newconn->pc_ctx);
167 if (ret != 0) {
168 free(newconn);
169 errno = ret;
170 return (-1);
171 }
172
173 newconn->pc_proto = conn->pc_proto;
174 newconn->pc_side = PROTO_SIDE_SERVER_WORK;
175 newconn->pc_magic = PROTO_CONN_MAGIC;
176 *newconnp = newconn;
177
178 return (0);
179}
180
181int
182proto_send(const struct proto_conn *conn, const void *data, size_t size)
183{
184 int ret;
185
186 assert(conn != NULL);
187 assert(conn->pc_magic == PROTO_CONN_MAGIC);
188 assert(conn->pc_proto != NULL);
189
190 ret = conn->pc_proto->hp_send(conn->pc_ctx, data, size);
191 if (ret != 0) {
192 errno = ret;
193 return (-1);
194 }
195 return (0);
196}
197
198int
199proto_recv(const struct proto_conn *conn, void *data, size_t size)
200{
201 int ret;
202
203 assert(conn != NULL);
204 assert(conn->pc_magic == PROTO_CONN_MAGIC);
205 assert(conn->pc_proto != NULL);
206
207 ret = conn->pc_proto->hp_recv(conn->pc_ctx, data, size);
208 if (ret != 0) {
209 errno = ret;
210 return (-1);
211 }
212 return (0);
213}
214
215int
216proto_descriptor(const struct proto_conn *conn)
217{
218
219 assert(conn != NULL);
220 assert(conn->pc_magic == PROTO_CONN_MAGIC);
221 assert(conn->pc_proto != NULL);
222
223 return (conn->pc_proto->hp_descriptor(conn->pc_ctx));
224}
225
226bool
227proto_address_match(const struct proto_conn *conn, const char *addr)
228{
229
230 assert(conn != NULL);
231 assert(conn->pc_magic == PROTO_CONN_MAGIC);
232 assert(conn->pc_proto != NULL);
233
234 return (conn->pc_proto->hp_address_match(conn->pc_ctx, addr));
235}
236
237void
238proto_local_address(const struct proto_conn *conn, char *addr, size_t size)
239{
240
241 assert(conn != NULL);
242 assert(conn->pc_magic == PROTO_CONN_MAGIC);
243 assert(conn->pc_proto != NULL);
244
245 conn->pc_proto->hp_local_address(conn->pc_ctx, addr, size);
246}
247
248void
249proto_remote_address(const struct proto_conn *conn, char *addr, size_t size)
250{
251
252 assert(conn != NULL);
253 assert(conn->pc_magic == PROTO_CONN_MAGIC);
254 assert(conn->pc_proto != NULL);
255
256 conn->pc_proto->hp_remote_address(conn->pc_ctx, addr, size);
257}
258
259int
260proto_timeout(const struct proto_conn *conn, int timeout)
261{
262 struct timeval tv;
263 int fd;
264
265 assert(conn != NULL);
266 assert(conn->pc_magic == PROTO_CONN_MAGIC);
267 assert(conn->pc_proto != NULL);
268
269 fd = proto_descriptor(conn);
270 if (fd < 0)
271 return (-1);
272
273 tv.tv_sec = timeout;
274 tv.tv_usec = 0;
275 if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
276 return (-1);
277 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
278 return (-1);
279
280 return (0);
281}
282
283void
284proto_close(struct proto_conn *conn)
285{
286
287 assert(conn != NULL);
288 assert(conn->pc_magic == PROTO_CONN_MAGIC);
289 assert(conn->pc_proto != NULL);
290
291 conn->pc_proto->hp_close(conn->pc_ctx);
292 conn->pc_magic = 0;
293 free(conn);
294}