3 Subroutines that support the generic listener object. */
6 * Copyright (c) 2012,2014 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/
32 #include <omapip/omapip_p.h>
36 omapi_array_t
*trace_listeners
;
37 static void trace_listener_accept_input (trace_type_t
*, unsigned, char *);
38 static void trace_listener_remember (omapi_listener_object_t
*,
40 static void trace_listener_accept_stop (trace_type_t
*);
41 trace_type_t
*trace_listener_accept
;
44 OMAPI_OBJECT_ALLOC (omapi_listener
,
45 omapi_listener_object_t
, omapi_type_listener
)
47 isc_result_t
omapi_listen (omapi_object_t
*h
,
54 log_debug ("omapi_listen(port=%d, max=%d)", port
, max
);
57 addr
.addrtype
= AF_INET
;
58 addr
.addrlen
= sizeof (struct in_addr
);
59 memset (addr
.address
, 0, sizeof addr
.address
); /* INADDR_ANY */
62 return omapi_listen_addr (h
, &addr
, max
);
65 isc_result_t
omapi_listen_addr (omapi_object_t
*h
,
70 omapi_listener_object_t
*obj
;
73 /* Currently only support IPv4 addresses. */
74 if (addr
->addrtype
!= AF_INET
)
75 return DHCP_R_INVALIDARG
;
78 obj
= (omapi_listener_object_t
*)0;
79 status
= omapi_listener_allocate (&obj
, MDL
);
80 if (status
!= ISC_R_SUCCESS
)
82 * we could simply return here but by going to
83 * error_exit we keep the code check tools happy
84 * without removing the NULL check on obj at
85 * the exit, which we could skip curently but
86 * might want in the future.
91 /* Connect this object to the inner object. */
92 status
= omapi_object_reference (&h
-> outer
,
93 (omapi_object_t
*)obj
, MDL
);
94 if (status
!= ISC_R_SUCCESS
)
96 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
97 if (status
!= ISC_R_SUCCESS
)
100 /* Set up the address on which we will listen... */
101 obj
-> address
.sin_port
= htons (addr
-> port
);
102 memcpy (&obj
-> address
.sin_addr
,
103 addr
-> address
, sizeof obj
-> address
.sin_addr
);
104 #if defined (HAVE_SA_LEN)
105 obj
-> address
.sin_len
=
106 sizeof (struct sockaddr_in
);
108 obj
-> address
.sin_family
= AF_INET
;
109 memset (&(obj
-> address
.sin_zero
), 0,
110 sizeof obj
-> address
.sin_zero
);
112 #if defined (TRACING)
113 /* If we're playing back a trace file, we remember the object
114 on the trace listener queue. */
115 if (trace_playback ()) {
116 trace_listener_remember (obj
, MDL
);
119 /* Create a socket on which to listen. */
120 obj
-> socket
= socket (PF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
121 if (obj
->socket
== -1) {
123 || errno
== ENFILE
|| errno
== ENOBUFS
)
124 status
= ISC_R_NORESOURCES
;
126 status
= ISC_R_UNEXPECTED
;
130 #if defined (HAVE_SETFD)
131 if (fcntl (obj
-> socket
, F_SETFD
, 1) < 0) {
132 status
= ISC_R_UNEXPECTED
;
137 /* Set the REUSEADDR option so that we don't fail to start if
138 we're being restarted. */
140 if (setsockopt (obj
-> socket
, SOL_SOCKET
, SO_REUSEADDR
,
141 (char *)&i
, sizeof i
) < 0) {
142 status
= ISC_R_UNEXPECTED
;
146 /* Try to bind to the wildcard address using the port number
148 i
= bind (obj
-> socket
,
149 (struct sockaddr
*)&obj
-> address
,
150 sizeof obj
-> address
);
152 if (errno
== EADDRINUSE
)
153 status
= ISC_R_ADDRNOTAVAIL
;
154 else if (errno
== EPERM
)
155 status
= ISC_R_NOPERM
;
157 status
= ISC_R_UNEXPECTED
;
161 /* Now tell the kernel to listen for connections. */
162 if (listen (obj
-> socket
, max
)) {
163 status
= ISC_R_UNEXPECTED
;
167 if (fcntl (obj
-> socket
, F_SETFL
, O_NONBLOCK
) < 0) {
168 status
= ISC_R_UNEXPECTED
;
172 status
= omapi_register_io_object ((omapi_object_t
*)obj
,
173 omapi_listener_readfd
, 0,
175 #if defined (TRACING)
179 omapi_listener_dereference (&obj
, MDL
);
184 if (h
->outer
== (omapi_object_t
*)obj
) {
185 omapi_object_dereference((omapi_object_t
**)&h
->outer
,
188 if (obj
->inner
== h
) {
189 omapi_object_dereference((omapi_object_t
**)&obj
->inner
,
192 if (obj
->socket
!= -1) {
195 omapi_listener_dereference(&obj
, MDL
);
200 /* Return the socket on which the dispatcher should wait for readiness
201 to read, for a listener object. */
202 int omapi_listener_readfd (omapi_object_t
*h
)
204 omapi_listener_object_t
*l
;
206 if (h
-> type
!= omapi_type_listener
)
208 l
= (omapi_listener_object_t
*)h
;
213 /* Reader callback for a listener object. Accept an incoming connection. */
214 isc_result_t
omapi_accept (omapi_object_t
*h
)
218 omapi_connection_object_t
*obj
;
219 omapi_listener_object_t
*listener
;
220 struct sockaddr_in addr
;
223 if (h
-> type
!= omapi_type_listener
)
224 return DHCP_R_INVALIDARG
;
225 listener
= (omapi_listener_object_t
*)h
;
227 /* Accept the connection. */
229 socket
= accept (listener
-> socket
,
230 ((struct sockaddr
*)&(addr
)), &len
);
232 if (errno
== EMFILE
|| errno
== ENFILE
|| errno
== ENOBUFS
)
233 return ISC_R_NORESOURCES
;
234 return ISC_R_UNEXPECTED
;
237 #if defined (TRACING)
238 /* If we're recording a trace, remember the connection. */
239 if (trace_record ()) {
241 iov
[0].buf
= (char *)&addr
.sin_port
;
242 iov
[0].len
= sizeof addr
.sin_port
;
243 iov
[1].buf
= (char *)&addr
.sin_addr
;
244 iov
[1].len
= sizeof addr
.sin_addr
;
245 iov
[2].buf
= (char *)&listener
-> address
.sin_port
;
246 iov
[2].len
= sizeof listener
-> address
.sin_port
;
247 trace_write_packet_iov (trace_listener_accept
,
252 obj
= (omapi_connection_object_t
*)0;
253 status
= omapi_listener_connect (&obj
, listener
, socket
, &addr
);
254 if (status
!= ISC_R_SUCCESS
) {
259 status
= omapi_register_io_object ((omapi_object_t
*)obj
,
260 omapi_connection_readfd
,
261 omapi_connection_writefd
,
262 omapi_connection_reader
,
263 omapi_connection_writer
,
264 omapi_connection_reaper
);
266 /* Lose our reference to the connection, so it'll be gc'd when it's
268 omapi_connection_dereference (&obj
, MDL
);
269 if (status
!= ISC_R_SUCCESS
)
270 omapi_disconnect ((omapi_object_t
*)(obj
), 1);
274 isc_result_t
omapi_listener_connect (omapi_connection_object_t
**obj
,
275 omapi_listener_object_t
*listener
,
277 struct sockaddr_in
*remote_addr
)
280 omapi_object_t
*h
= (omapi_object_t
*)listener
;
283 #ifdef DEBUG_PROTOCOL
284 log_debug ("omapi_accept()");
287 /* Get the handle. */
288 status
= omapi_connection_allocate (obj
, MDL
);
289 if (status
!= ISC_R_SUCCESS
)
292 (*obj
) -> state
= omapi_connection_connected
;
293 (*obj
) -> remote_addr
= *remote_addr
;
294 (*obj
) -> socket
= socket
;
296 /* Verify that this host is allowed to connect. */
297 if (listener
-> verify_addr
) {
298 addr
.addrtype
= AF_INET
;
299 addr
.addrlen
= sizeof (remote_addr
-> sin_addr
);
300 memcpy (addr
.address
, &remote_addr
-> sin_addr
,
301 sizeof (remote_addr
-> sin_addr
));
302 addr
.port
= ntohs(remote_addr
-> sin_port
);
304 status
= (listener
-> verify_addr
) (h
, &addr
);
305 if (status
!= ISC_R_SUCCESS
) {
306 omapi_disconnect ((omapi_object_t
*)(*obj
), 1);
307 omapi_connection_dereference (obj
, MDL
);
312 omapi_listener_reference (&(*obj
) -> listener
, listener
, MDL
);
313 #if defined (TRACING)
314 omapi_connection_register (*obj
, MDL
);
316 status
= omapi_signal (h
, "connect", (*obj
));
320 #if defined (TRACING)
321 OMAPI_ARRAY_TYPE(omapi_listener
, omapi_listener_object_t
)
323 void omapi_listener_trace_setup (void) {
324 trace_listener_accept
=
325 trace_type_register ("listener-accept", (void *)0,
326 trace_listener_accept_input
,
327 trace_listener_accept_stop
, MDL
);
330 static void trace_listener_remember (omapi_listener_object_t
*obj
,
331 const char *file
, int line
)
334 if (!trace_listeners
) {
335 status
= omapi_listener_array_allocate (&trace_listeners
,
337 if (status
!= ISC_R_SUCCESS
) {
339 log_error ("trace_listener_remember: %s",
340 isc_result_totext (status
));
344 status
= omapi_listener_array_extend (trace_listeners
, obj
,
346 if (status
!= ISC_R_SUCCESS
)
350 static void trace_listener_accept_input (trace_type_t
*ttype
,
351 unsigned length
, char *buf
)
353 struct in_addr
*addr
;
354 u_int16_t
*remote_port
;
355 u_int16_t
*local_port
;
356 omapi_connection_object_t
*obj
;
358 struct sockaddr_in remote_addr
;
360 addr
= (struct in_addr
*)buf
;
361 remote_port
= (u_int16_t
*)(addr
+ 1);
362 local_port
= remote_port
+ 1;
364 memset (&remote_addr
, 0, sizeof remote_addr
);
365 remote_addr
.sin_addr
= *addr
;
366 remote_addr
.sin_port
= *remote_port
;
368 omapi_array_foreach_begin (trace_listeners
,
369 omapi_listener_object_t
, lp
) {
370 if (lp
-> address
.sin_port
== *local_port
) {
371 obj
= (omapi_connection_object_t
*)0;
372 status
= omapi_listener_connect (&obj
,
373 lp
, 0, &remote_addr
);
374 if (status
!= ISC_R_SUCCESS
) {
375 log_error("%s:%d: OMAPI: Failed to connect "
378 omapi_listener_dereference (&lp
, MDL
);
381 } omapi_array_foreach_end (trace_listeners
,
382 omapi_listener_object_t
, lp
);
383 log_error ("trace_listener_accept: %s from %s/%d to port %d",
384 "unexpected connect",
385 inet_ntoa (*addr
), *remote_port
, *local_port
);
388 static void trace_listener_accept_stop (trace_type_t
*ttype
) { }
393 isc_result_t
omapi_listener_configure_security (omapi_object_t
*h
,
394 isc_result_t (*verify_addr
)
398 omapi_listener_object_t
*l
;
400 if (h
-> type
!= omapi_type_listener
)
401 return DHCP_R_INVALIDARG
;
402 l
= (omapi_listener_object_t
*)h
;
404 l
-> verify_addr
= verify_addr
;
406 return ISC_R_SUCCESS
;
409 isc_result_t
omapi_listener_set_value (omapi_object_t
*h
,
411 omapi_data_string_t
*name
,
412 omapi_typed_data_t
*value
)
414 if (h
-> type
!= omapi_type_listener
)
415 return DHCP_R_INVALIDARG
;
417 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
418 return (*(h
-> inner
-> type
-> set_value
))
419 (h
-> inner
, id
, name
, value
);
420 return ISC_R_NOTFOUND
;
423 isc_result_t
omapi_listener_get_value (omapi_object_t
*h
,
425 omapi_data_string_t
*name
,
426 omapi_value_t
**value
)
428 if (h
-> type
!= omapi_type_listener
)
429 return DHCP_R_INVALIDARG
;
431 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
432 return (*(h
-> inner
-> type
-> get_value
))
433 (h
-> inner
, id
, name
, value
);
434 return ISC_R_NOTFOUND
;
437 isc_result_t
omapi_listener_destroy (omapi_object_t
*h
,
438 const char *file
, int line
)
440 omapi_listener_object_t
*l
;
442 if (h
-> type
!= omapi_type_listener
)
443 return DHCP_R_INVALIDARG
;
444 l
= (omapi_listener_object_t
*)h
;
446 #ifdef DEBUG_PROTOCOL
447 log_debug ("omapi_listener_destroy()");
450 if (l
-> socket
!= -1) {
454 return ISC_R_SUCCESS
;
457 isc_result_t
omapi_listener_signal_handler (omapi_object_t
*h
,
458 const char *name
, va_list ap
)
460 if (h
-> type
!= omapi_type_listener
)
461 return DHCP_R_INVALIDARG
;
463 if (h
-> inner
&& h
-> inner
-> type
-> signal_handler
)
464 return (*(h
-> inner
-> type
-> signal_handler
)) (h
-> inner
,
466 return ISC_R_NOTFOUND
;
469 /* Write all the published values associated with the object through the
470 specified connection. */
472 isc_result_t
omapi_listener_stuff_values (omapi_object_t
*c
,
476 if (l
-> type
!= omapi_type_listener
)
477 return DHCP_R_INVALIDARG
;
479 if (l
-> inner
&& l
-> inner
-> type
-> stuff_values
)
480 return (*(l
-> inner
-> type
-> stuff_values
)) (c
, id
,
482 return ISC_R_SUCCESS
;