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