]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sbin/hastd/proto.c
Fix named(8) DNSSEC validation Denial of Service.
[FreeBSD/releng/8.2.git] / sbin / hastd / proto.c
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 TAILQ_HEAD(, hast_proto) protos = TAILQ_HEAD_INITIALIZER(protos);
56
57 void
58 proto_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
71 static int
72 proto_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
118 int
119 proto_client(const char *addr, struct proto_conn **connp)
120 {
121
122         return (proto_common_setup(addr, connp, PROTO_SIDE_CLIENT));
123 }
124
125 int
126 proto_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
144 int
145 proto_server(const char *addr, struct proto_conn **connp)
146 {
147
148         return (proto_common_setup(addr, connp, PROTO_SIDE_SERVER_LISTEN));
149 }
150
151 int
152 proto_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
181 int
182 proto_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
198 int
199 proto_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
215 int
216 proto_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
226 bool
227 proto_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
237 void
238 proto_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
248 void
249 proto_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
259 int
260 proto_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
283 void
284 proto_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 }