3 Subroutines that support the generic listener object. */
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
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.
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.
22 * Internet Systems Consortium, Inc.
24 * Redwood City, CA 94063
26 * https://www.isc.org/
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''.
38 #include <omapip/omapip_p.h>
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
*,
46 static void trace_listener_accept_stop (trace_type_t
*);
47 trace_type_t
*trace_listener_accept
;
50 OMAPI_OBJECT_ALLOC (omapi_listener
,
51 omapi_listener_object_t
, omapi_type_listener
)
53 isc_result_t
omapi_listen (omapi_object_t
*h
,
60 log_debug ("omapi_listen(port=%d, max=%d)", port
, max
);
63 addr
.addrtype
= AF_INET
;
64 addr
.addrlen
= sizeof (struct in_addr
);
65 memset (addr
.address
, 0, sizeof addr
.address
); /* INADDR_ANY */
68 return omapi_listen_addr (h
, &addr
, max
);
71 isc_result_t
omapi_listen_addr (omapi_object_t
*h
,
76 omapi_listener_object_t
*obj
;
79 /* Currently only support IPv4 addresses. */
80 if (addr
->addrtype
!= AF_INET
)
81 return DHCP_R_INVALIDARG
;
84 obj
= (omapi_listener_object_t
*)0;
85 status
= omapi_listener_allocate (&obj
, MDL
);
86 if (status
!= ISC_R_SUCCESS
)
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.
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
)
102 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
103 if (status
!= ISC_R_SUCCESS
)
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
);
114 obj
-> address
.sin_family
= AF_INET
;
115 memset (&(obj
-> address
.sin_zero
), 0,
116 sizeof obj
-> address
.sin_zero
);
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
);
125 /* Create a socket on which to listen. */
126 obj
-> socket
= socket (PF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
127 if (obj
->socket
== -1) {
129 || errno
== ENFILE
|| errno
== ENOBUFS
)
130 status
= ISC_R_NORESOURCES
;
132 status
= ISC_R_UNEXPECTED
;
136 #if defined (HAVE_SETFD)
137 if (fcntl (obj
-> socket
, F_SETFD
, 1) < 0) {
138 status
= ISC_R_UNEXPECTED
;
143 /* Set the REUSEADDR option so that we don't fail to start if
144 we're being restarted. */
146 if (setsockopt (obj
-> socket
, SOL_SOCKET
, SO_REUSEADDR
,
147 (char *)&i
, sizeof i
) < 0) {
148 status
= ISC_R_UNEXPECTED
;
152 /* Try to bind to the wildcard address using the port number
154 i
= bind (obj
-> socket
,
155 (struct sockaddr
*)&obj
-> address
,
156 sizeof obj
-> address
);
158 if (errno
== EADDRINUSE
)
159 status
= ISC_R_ADDRNOTAVAIL
;
160 else if (errno
== EPERM
)
161 status
= ISC_R_NOPERM
;
163 status
= ISC_R_UNEXPECTED
;
167 /* Now tell the kernel to listen for connections. */
168 if (listen (obj
-> socket
, max
)) {
169 status
= ISC_R_UNEXPECTED
;
173 if (fcntl (obj
-> socket
, F_SETFL
, O_NONBLOCK
) < 0) {
174 status
= ISC_R_UNEXPECTED
;
178 status
= omapi_register_io_object ((omapi_object_t
*)obj
,
179 omapi_listener_readfd
, 0,
181 #if defined (TRACING)
185 omapi_listener_dereference (&obj
, MDL
);
190 if (h
->outer
== (omapi_object_t
*)obj
) {
191 omapi_object_dereference((omapi_object_t
**)&h
->outer
,
194 if (obj
->inner
== h
) {
195 omapi_object_dereference((omapi_object_t
**)&obj
->inner
,
198 if (obj
->socket
!= -1) {
201 omapi_listener_dereference(&obj
, MDL
);
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
)
210 omapi_listener_object_t
*l
;
212 if (h
-> type
!= omapi_type_listener
)
214 l
= (omapi_listener_object_t
*)h
;
219 /* Reader callback for a listener object. Accept an incoming connection. */
220 isc_result_t
omapi_accept (omapi_object_t
*h
)
224 omapi_connection_object_t
*obj
;
225 omapi_listener_object_t
*listener
;
226 struct sockaddr_in addr
;
229 if (h
-> type
!= omapi_type_listener
)
230 return DHCP_R_INVALIDARG
;
231 listener
= (omapi_listener_object_t
*)h
;
233 /* Accept the connection. */
235 socket
= accept (listener
-> socket
,
236 ((struct sockaddr
*)&(addr
)), &len
);
238 if (errno
== EMFILE
|| errno
== ENFILE
|| errno
== ENOBUFS
)
239 return ISC_R_NORESOURCES
;
240 return ISC_R_UNEXPECTED
;
243 #if defined (TRACING)
244 /* If we're recording a trace, remember the connection. */
245 if (trace_record ()) {
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
,
258 obj
= (omapi_connection_object_t
*)0;
259 status
= omapi_listener_connect (&obj
, listener
, socket
, &addr
);
260 if (status
!= ISC_R_SUCCESS
) {
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
);
272 /* Lose our reference to the connection, so it'll be gc'd when it's
274 omapi_connection_dereference (&obj
, MDL
);
275 if (status
!= ISC_R_SUCCESS
)
276 omapi_disconnect ((omapi_object_t
*)(obj
), 1);
280 isc_result_t
omapi_listener_connect (omapi_connection_object_t
**obj
,
281 omapi_listener_object_t
*listener
,
283 struct sockaddr_in
*remote_addr
)
286 omapi_object_t
*h
= (omapi_object_t
*)listener
;
289 #ifdef DEBUG_PROTOCOL
290 log_debug ("omapi_accept()");
293 /* Get the handle. */
294 status
= omapi_connection_allocate (obj
, MDL
);
295 if (status
!= ISC_R_SUCCESS
)
298 (*obj
) -> state
= omapi_connection_connected
;
299 (*obj
) -> remote_addr
= *remote_addr
;
300 (*obj
) -> socket
= socket
;
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
);
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
);
318 omapi_listener_reference (&(*obj
) -> listener
, listener
, MDL
);
319 #if defined (TRACING)
320 omapi_connection_register (*obj
, MDL
);
322 status
= omapi_signal (h
, "connect", (*obj
));
326 #if defined (TRACING)
327 OMAPI_ARRAY_TYPE(omapi_listener
, omapi_listener_object_t
)
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
);
336 static void trace_listener_remember (omapi_listener_object_t
*obj
,
337 const char *file
, int line
)
340 if (!trace_listeners
) {
341 status
= omapi_listener_array_allocate (&trace_listeners
,
343 if (status
!= ISC_R_SUCCESS
) {
345 log_error ("trace_listener_remember: %s",
346 isc_result_totext (status
));
350 status
= omapi_listener_array_extend (trace_listeners
, obj
,
352 if (status
!= ISC_R_SUCCESS
)
356 static void trace_listener_accept_input (trace_type_t
*ttype
,
357 unsigned length
, char *buf
)
359 struct in_addr
*addr
;
360 u_int16_t
*remote_port
;
361 u_int16_t
*local_port
;
362 omapi_connection_object_t
*obj
;
364 struct sockaddr_in remote_addr
;
366 addr
= (struct in_addr
*)buf
;
367 remote_port
= (u_int16_t
*)(addr
+ 1);
368 local_port
= remote_port
+ 1;
370 memset (&remote_addr
, 0, sizeof remote_addr
);
371 remote_addr
.sin_addr
= *addr
;
372 remote_addr
.sin_port
= *remote_port
;
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 "
384 omapi_listener_dereference (&lp
, MDL
);
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
);
394 static void trace_listener_accept_stop (trace_type_t
*ttype
) { }
399 isc_result_t
omapi_listener_configure_security (omapi_object_t
*h
,
400 isc_result_t (*verify_addr
)
404 omapi_listener_object_t
*l
;
406 if (h
-> type
!= omapi_type_listener
)
407 return DHCP_R_INVALIDARG
;
408 l
= (omapi_listener_object_t
*)h
;
410 l
-> verify_addr
= verify_addr
;
412 return ISC_R_SUCCESS
;
415 isc_result_t
omapi_listener_set_value (omapi_object_t
*h
,
417 omapi_data_string_t
*name
,
418 omapi_typed_data_t
*value
)
420 if (h
-> type
!= omapi_type_listener
)
421 return DHCP_R_INVALIDARG
;
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
;
429 isc_result_t
omapi_listener_get_value (omapi_object_t
*h
,
431 omapi_data_string_t
*name
,
432 omapi_value_t
**value
)
434 if (h
-> type
!= omapi_type_listener
)
435 return DHCP_R_INVALIDARG
;
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
;
443 isc_result_t
omapi_listener_destroy (omapi_object_t
*h
,
444 const char *file
, int line
)
446 omapi_listener_object_t
*l
;
448 if (h
-> type
!= omapi_type_listener
)
449 return DHCP_R_INVALIDARG
;
450 l
= (omapi_listener_object_t
*)h
;
452 #ifdef DEBUG_PROTOCOL
453 log_debug ("omapi_listener_destroy()");
456 if (l
-> socket
!= -1) {
460 return ISC_R_SUCCESS
;
463 isc_result_t
omapi_listener_signal_handler (omapi_object_t
*h
,
464 const char *name
, va_list ap
)
466 if (h
-> type
!= omapi_type_listener
)
467 return DHCP_R_INVALIDARG
;
469 if (h
-> inner
&& h
-> inner
-> type
-> signal_handler
)
470 return (*(h
-> inner
-> type
-> signal_handler
)) (h
-> inner
,
472 return ISC_R_NOTFOUND
;
475 /* Write all the published values associated with the object through the
476 specified connection. */
478 isc_result_t
omapi_listener_stuff_values (omapi_object_t
*c
,
482 if (l
-> type
!= omapi_type_listener
)
483 return DHCP_R_INVALIDARG
;
485 if (l
-> inner
&& l
-> inner
-> type
-> stuff_values
)
486 return (*(l
-> inner
-> type
-> stuff_values
)) (c
, id
,
488 return ISC_R_SUCCESS
;