]> git.ipfire.org Git - thirdparty/dhcp.git/blob - omapip/listener.c
-n [master]
[thirdparty/dhcp.git] / omapip / listener.c
1 /* listener.c
2
3 Subroutines that support the generic listener object. */
4
5 /*
6 * Copyright (c) 2012 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
8 * Copyright (c) 1999-2003 by Internet Software Consortium
9 *
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 *
22 * Internet Systems Consortium, Inc.
23 * 950 Charter Street
24 * Redwood City, CA 94063
25 * <info@isc.org>
26 * https://www.isc.org/
27 *
28 * This software has been written for Internet Systems Consortium
29 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
30 * To learn more about Internet Systems Consortium, see
31 * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
32 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
33 * ``http://www.nominum.com''.
34 */
35
36 #include "dhcpd.h"
37
38 #include <omapip/omapip_p.h>
39 #include <errno.h>
40
41 #if defined (TRACING)
42 omapi_array_t *trace_listeners;
43 static void trace_listener_accept_input (trace_type_t *, unsigned, char *);
44 static void trace_listener_remember (omapi_listener_object_t *,
45 const char *, int);
46 static void trace_listener_accept_stop (trace_type_t *);
47 trace_type_t *trace_listener_accept;
48 #endif
49
50 OMAPI_OBJECT_ALLOC (omapi_listener,
51 omapi_listener_object_t, omapi_type_listener)
52
53 isc_result_t omapi_listen (omapi_object_t *h,
54 unsigned port,
55 int max)
56 {
57 omapi_addr_t addr;
58
59 #ifdef DEBUG_PROTOCOL
60 log_debug ("omapi_listen(port=%d, max=%d)", port, max);
61 #endif
62
63 addr.addrtype = AF_INET;
64 addr.addrlen = sizeof (struct in_addr);
65 memset (addr.address, 0, sizeof addr.address); /* INADDR_ANY */
66 addr.port = port;
67
68 return omapi_listen_addr (h, &addr, max);
69 }
70
71 isc_result_t omapi_listen_addr (omapi_object_t *h,
72 omapi_addr_t *addr,
73 int max)
74 {
75 isc_result_t status;
76 omapi_listener_object_t *obj;
77 int i;
78
79 /* Currently only support IPv4 addresses. */
80 if (addr->addrtype != AF_INET)
81 return DHCP_R_INVALIDARG;
82
83 /* Get the handle. */
84 obj = (omapi_listener_object_t *)0;
85 status = omapi_listener_allocate (&obj, MDL);
86 if (status != ISC_R_SUCCESS)
87 /*
88 * we could simply return here but by going to
89 * error_exit we keep the code check tools happy
90 * without removing the NULL check on obj at
91 * the exit, which we could skip curently but
92 * might want in the future.
93 */
94 goto error_exit;
95 obj->socket = -1;
96
97 /* Connect this object to the inner object. */
98 status = omapi_object_reference (&h -> outer,
99 (omapi_object_t *)obj, MDL);
100 if (status != ISC_R_SUCCESS)
101 goto error_exit;
102 status = omapi_object_reference (&obj -> inner, h, MDL);
103 if (status != ISC_R_SUCCESS)
104 goto error_exit;
105
106 /* Set up the address on which we will listen... */
107 obj -> address.sin_port = htons (addr -> port);
108 memcpy (&obj -> address.sin_addr,
109 addr -> address, sizeof obj -> address.sin_addr);
110 #if defined (HAVE_SA_LEN)
111 obj -> address.sin_len =
112 sizeof (struct sockaddr_in);
113 #endif
114 obj -> address.sin_family = AF_INET;
115 memset (&(obj -> address.sin_zero), 0,
116 sizeof obj -> address.sin_zero);
117
118 #if defined (TRACING)
119 /* If we're playing back a trace file, we remember the object
120 on the trace listener queue. */
121 if (trace_playback ()) {
122 trace_listener_remember (obj, MDL);
123 } else {
124 #endif
125 /* Create a socket on which to listen. */
126 obj -> socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
127 if (obj->socket == -1) {
128 if (errno == EMFILE
129 || errno == ENFILE || errno == ENOBUFS)
130 status = ISC_R_NORESOURCES;
131 else
132 status = ISC_R_UNEXPECTED;
133 goto error_exit;
134 }
135
136 #if defined (HAVE_SETFD)
137 if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
138 status = ISC_R_UNEXPECTED;
139 goto error_exit;
140 }
141 #endif
142
143 /* Set the REUSEADDR option so that we don't fail to start if
144 we're being restarted. */
145 i = 1;
146 if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR,
147 (char *)&i, sizeof i) < 0) {
148 status = ISC_R_UNEXPECTED;
149 goto error_exit;
150 }
151
152 /* Try to bind to the wildcard address using the port number
153 we were given. */
154 i = bind (obj -> socket,
155 (struct sockaddr *)&obj -> address,
156 sizeof obj -> address);
157 if (i < 0) {
158 if (errno == EADDRINUSE)
159 status = ISC_R_ADDRNOTAVAIL;
160 else if (errno == EPERM)
161 status = ISC_R_NOPERM;
162 else
163 status = ISC_R_UNEXPECTED;
164 goto error_exit;
165 }
166
167 /* Now tell the kernel to listen for connections. */
168 if (listen (obj -> socket, max)) {
169 status = ISC_R_UNEXPECTED;
170 goto error_exit;
171 }
172
173 if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
174 status = ISC_R_UNEXPECTED;
175 goto error_exit;
176 }
177
178 status = omapi_register_io_object ((omapi_object_t *)obj,
179 omapi_listener_readfd, 0,
180 omapi_accept, 0, 0);
181 #if defined (TRACING)
182 }
183 #endif
184
185 omapi_listener_dereference (&obj, MDL);
186 return status;
187
188 error_exit:
189 if (obj != NULL) {
190 if (h->outer == (omapi_object_t *)obj) {
191 omapi_object_dereference((omapi_object_t **)&h->outer,
192 MDL);
193 }
194 if (obj->inner == h) {
195 omapi_object_dereference((omapi_object_t **)&obj->inner,
196 MDL);
197 }
198 if (obj->socket != -1) {
199 close(obj->socket);
200 }
201 omapi_listener_dereference(&obj, MDL);
202 }
203 return status;
204 }
205
206 /* Return the socket on which the dispatcher should wait for readiness
207 to read, for a listener object. */
208 int omapi_listener_readfd (omapi_object_t *h)
209 {
210 omapi_listener_object_t *l;
211
212 if (h -> type != omapi_type_listener)
213 return -1;
214 l = (omapi_listener_object_t *)h;
215
216 return l -> socket;
217 }
218
219 /* Reader callback for a listener object. Accept an incoming connection. */
220 isc_result_t omapi_accept (omapi_object_t *h)
221 {
222 isc_result_t status;
223 socklen_t len;
224 omapi_connection_object_t *obj;
225 omapi_listener_object_t *listener;
226 struct sockaddr_in addr;
227 int socket;
228
229 if (h -> type != omapi_type_listener)
230 return DHCP_R_INVALIDARG;
231 listener = (omapi_listener_object_t *)h;
232
233 /* Accept the connection. */
234 len = sizeof addr;
235 socket = accept (listener -> socket,
236 ((struct sockaddr *)&(addr)), &len);
237 if (socket < 0) {
238 if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS)
239 return ISC_R_NORESOURCES;
240 return ISC_R_UNEXPECTED;
241 }
242
243 #if defined (TRACING)
244 /* If we're recording a trace, remember the connection. */
245 if (trace_record ()) {
246 trace_iov_t iov [3];
247 iov [0].buf = (char *)&addr.sin_port;
248 iov [0].len = sizeof addr.sin_port;
249 iov [1].buf = (char *)&addr.sin_addr;
250 iov [1].len = sizeof addr.sin_addr;
251 iov [2].buf = (char *)&listener -> address.sin_port;
252 iov [2].len = sizeof listener -> address.sin_port;
253 trace_write_packet_iov (trace_listener_accept,
254 3, iov, MDL);
255 }
256 #endif
257
258 obj = (omapi_connection_object_t *)0;
259 status = omapi_listener_connect (&obj, listener, socket, &addr);
260 if (status != ISC_R_SUCCESS) {
261 close (socket);
262 return status;
263 }
264
265 status = omapi_register_io_object ((omapi_object_t *)obj,
266 omapi_connection_readfd,
267 omapi_connection_writefd,
268 omapi_connection_reader,
269 omapi_connection_writer,
270 omapi_connection_reaper);
271
272 /* Lose our reference to the connection, so it'll be gc'd when it's
273 reaped. */
274 omapi_connection_dereference (&obj, MDL);
275 if (status != ISC_R_SUCCESS)
276 omapi_disconnect ((omapi_object_t *)(obj), 1);
277 return status;
278 }
279
280 isc_result_t omapi_listener_connect (omapi_connection_object_t **obj,
281 omapi_listener_object_t *listener,
282 int socket,
283 struct sockaddr_in *remote_addr)
284 {
285 isc_result_t status;
286 omapi_object_t *h = (omapi_object_t *)listener;
287 omapi_addr_t addr;
288
289 #ifdef DEBUG_PROTOCOL
290 log_debug ("omapi_accept()");
291 #endif
292
293 /* Get the handle. */
294 status = omapi_connection_allocate (obj, MDL);
295 if (status != ISC_R_SUCCESS)
296 return status;
297
298 (*obj) -> state = omapi_connection_connected;
299 (*obj) -> remote_addr = *remote_addr;
300 (*obj) -> socket = socket;
301
302 /* Verify that this host is allowed to connect. */
303 if (listener -> verify_addr) {
304 addr.addrtype = AF_INET;
305 addr.addrlen = sizeof (remote_addr -> sin_addr);
306 memcpy (addr.address, &remote_addr -> sin_addr,
307 sizeof (remote_addr -> sin_addr));
308 addr.port = ntohs(remote_addr -> sin_port);
309
310 status = (listener -> verify_addr) (h, &addr);
311 if (status != ISC_R_SUCCESS) {
312 omapi_disconnect ((omapi_object_t *)(*obj), 1);
313 omapi_connection_dereference (obj, MDL);
314 return status;
315 }
316 }
317
318 omapi_listener_reference (&(*obj) -> listener, listener, MDL);
319 #if defined (TRACING)
320 omapi_connection_register (*obj, MDL);
321 #endif
322 status = omapi_signal (h, "connect", (*obj));
323 return status;
324 }
325
326 #if defined (TRACING)
327 OMAPI_ARRAY_TYPE(omapi_listener, omapi_listener_object_t)
328
329 void omapi_listener_trace_setup (void) {
330 trace_listener_accept =
331 trace_type_register ("listener-accept", (void *)0,
332 trace_listener_accept_input,
333 trace_listener_accept_stop, MDL);
334 }
335
336 static void trace_listener_remember (omapi_listener_object_t *obj,
337 const char *file, int line)
338 {
339 isc_result_t status;
340 if (!trace_listeners) {
341 status = omapi_listener_array_allocate (&trace_listeners,
342 file, line);
343 if (status != ISC_R_SUCCESS) {
344 foo:
345 log_error ("trace_listener_remember: %s",
346 isc_result_totext (status));
347 return;
348 }
349 }
350 status = omapi_listener_array_extend (trace_listeners, obj,
351 &obj -> index, MDL);
352 if (status != ISC_R_SUCCESS)
353 goto foo;
354 }
355
356 static void trace_listener_accept_input (trace_type_t *ttype,
357 unsigned length, char *buf)
358 {
359 struct in_addr *addr;
360 u_int16_t *remote_port;
361 u_int16_t *local_port;
362 omapi_connection_object_t *obj;
363 isc_result_t status;
364 struct sockaddr_in remote_addr;
365
366 addr = (struct in_addr *)buf;
367 remote_port = (u_int16_t *)(addr + 1);
368 local_port = remote_port + 1;
369
370 memset (&remote_addr, 0, sizeof remote_addr);
371 remote_addr.sin_addr = *addr;
372 remote_addr.sin_port = *remote_port;
373
374 omapi_array_foreach_begin (trace_listeners,
375 omapi_listener_object_t, lp) {
376 if (lp -> address.sin_port == *local_port) {
377 obj = (omapi_connection_object_t *)0;
378 status = omapi_listener_connect (&obj,
379 lp, 0, &remote_addr);
380 if (status != ISC_R_SUCCESS) {
381 log_error("%s:%d: OMAPI: Failed to connect "
382 "a listener.", MDL);
383 }
384 omapi_listener_dereference (&lp, MDL);
385 return;
386 }
387 } omapi_array_foreach_end (trace_listeners,
388 omapi_listener_object_t, lp);
389 log_error ("trace_listener_accept: %s from %s/%d to port %d",
390 "unexpected connect",
391 inet_ntoa (*addr), *remote_port, *local_port);
392 }
393
394 static void trace_listener_accept_stop (trace_type_t *ttype) { }
395
396
397 #endif
398
399 isc_result_t omapi_listener_configure_security (omapi_object_t *h,
400 isc_result_t (*verify_addr)
401 (omapi_object_t *,
402 omapi_addr_t *))
403 {
404 omapi_listener_object_t *l;
405
406 if (h -> type != omapi_type_listener)
407 return DHCP_R_INVALIDARG;
408 l = (omapi_listener_object_t *)h;
409
410 l -> verify_addr = verify_addr;
411
412 return ISC_R_SUCCESS;
413 }
414
415 isc_result_t omapi_listener_set_value (omapi_object_t *h,
416 omapi_object_t *id,
417 omapi_data_string_t *name,
418 omapi_typed_data_t *value)
419 {
420 if (h -> type != omapi_type_listener)
421 return DHCP_R_INVALIDARG;
422
423 if (h -> inner && h -> inner -> type -> set_value)
424 return (*(h -> inner -> type -> set_value))
425 (h -> inner, id, name, value);
426 return ISC_R_NOTFOUND;
427 }
428
429 isc_result_t omapi_listener_get_value (omapi_object_t *h,
430 omapi_object_t *id,
431 omapi_data_string_t *name,
432 omapi_value_t **value)
433 {
434 if (h -> type != omapi_type_listener)
435 return DHCP_R_INVALIDARG;
436
437 if (h -> inner && h -> inner -> type -> get_value)
438 return (*(h -> inner -> type -> get_value))
439 (h -> inner, id, name, value);
440 return ISC_R_NOTFOUND;
441 }
442
443 isc_result_t omapi_listener_destroy (omapi_object_t *h,
444 const char *file, int line)
445 {
446 omapi_listener_object_t *l;
447
448 if (h -> type != omapi_type_listener)
449 return DHCP_R_INVALIDARG;
450 l = (omapi_listener_object_t *)h;
451
452 #ifdef DEBUG_PROTOCOL
453 log_debug ("omapi_listener_destroy()");
454 #endif
455
456 if (l -> socket != -1) {
457 close (l -> socket);
458 l -> socket = -1;
459 }
460 return ISC_R_SUCCESS;
461 }
462
463 isc_result_t omapi_listener_signal_handler (omapi_object_t *h,
464 const char *name, va_list ap)
465 {
466 if (h -> type != omapi_type_listener)
467 return DHCP_R_INVALIDARG;
468
469 if (h -> inner && h -> inner -> type -> signal_handler)
470 return (*(h -> inner -> type -> signal_handler)) (h -> inner,
471 name, ap);
472 return ISC_R_NOTFOUND;
473 }
474
475 /* Write all the published values associated with the object through the
476 specified connection. */
477
478 isc_result_t omapi_listener_stuff_values (omapi_object_t *c,
479 omapi_object_t *id,
480 omapi_object_t *l)
481 {
482 if (l -> type != omapi_type_listener)
483 return DHCP_R_INVALIDARG;
484
485 if (l -> inner && l -> inner -> type -> stuff_values)
486 return (*(l -> inner -> type -> stuff_values)) (c, id,
487 l -> inner);
488 return ISC_R_SUCCESS;
489 }
490