]> git.ipfire.org Git - thirdparty/dhcp.git/blob - omapip/listener.c
Merged rt38836b (cross compile)
[thirdparty/dhcp.git] / omapip / listener.c
1 /* listener.c
2
3 Subroutines that support the generic listener object. */
4
5 /*
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
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 */
29
30 #include "dhcpd.h"
31
32 #include <omapip/omapip_p.h>
33 #include <errno.h>
34
35 #if defined (TRACING)
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 *,
39 const char *, int);
40 static void trace_listener_accept_stop (trace_type_t *);
41 trace_type_t *trace_listener_accept;
42 #endif
43
44 OMAPI_OBJECT_ALLOC (omapi_listener,
45 omapi_listener_object_t, omapi_type_listener)
46
47 isc_result_t omapi_listen (omapi_object_t *h,
48 unsigned port,
49 int max)
50 {
51 omapi_addr_t addr;
52
53 #ifdef DEBUG_PROTOCOL
54 log_debug ("omapi_listen(port=%d, max=%d)", port, max);
55 #endif
56
57 addr.addrtype = AF_INET;
58 addr.addrlen = sizeof (struct in_addr);
59 memset (addr.address, 0, sizeof addr.address); /* INADDR_ANY */
60 addr.port = port;
61
62 return omapi_listen_addr (h, &addr, max);
63 }
64
65 isc_result_t omapi_listen_addr (omapi_object_t *h,
66 omapi_addr_t *addr,
67 int max)
68 {
69 isc_result_t status;
70 omapi_listener_object_t *obj;
71 int i;
72
73 /* Currently only support IPv4 addresses. */
74 if (addr->addrtype != AF_INET)
75 return DHCP_R_INVALIDARG;
76
77 /* Get the handle. */
78 obj = (omapi_listener_object_t *)0;
79 status = omapi_listener_allocate (&obj, MDL);
80 if (status != ISC_R_SUCCESS)
81 /*
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.
87 */
88 goto error_exit;
89 obj->socket = -1;
90
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)
95 goto error_exit;
96 status = omapi_object_reference (&obj -> inner, h, MDL);
97 if (status != ISC_R_SUCCESS)
98 goto error_exit;
99
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);
107 #endif
108 obj -> address.sin_family = AF_INET;
109 memset (&(obj -> address.sin_zero), 0,
110 sizeof obj -> address.sin_zero);
111
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);
117 } else {
118 #endif
119 /* Create a socket on which to listen. */
120 obj -> socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
121 if (obj->socket == -1) {
122 if (errno == EMFILE
123 || errno == ENFILE || errno == ENOBUFS)
124 status = ISC_R_NORESOURCES;
125 else
126 status = ISC_R_UNEXPECTED;
127 goto error_exit;
128 }
129
130 #if defined (HAVE_SETFD)
131 if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
132 status = ISC_R_UNEXPECTED;
133 goto error_exit;
134 }
135 #endif
136
137 /* Set the REUSEADDR option so that we don't fail to start if
138 we're being restarted. */
139 i = 1;
140 if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR,
141 (char *)&i, sizeof i) < 0) {
142 status = ISC_R_UNEXPECTED;
143 goto error_exit;
144 }
145
146 /* Try to bind to the wildcard address using the port number
147 we were given. */
148 i = bind (obj -> socket,
149 (struct sockaddr *)&obj -> address,
150 sizeof obj -> address);
151 if (i < 0) {
152 if (errno == EADDRINUSE)
153 status = ISC_R_ADDRNOTAVAIL;
154 else if (errno == EPERM)
155 status = ISC_R_NOPERM;
156 else
157 status = ISC_R_UNEXPECTED;
158 goto error_exit;
159 }
160
161 /* Now tell the kernel to listen for connections. */
162 if (listen (obj -> socket, max)) {
163 status = ISC_R_UNEXPECTED;
164 goto error_exit;
165 }
166
167 if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
168 status = ISC_R_UNEXPECTED;
169 goto error_exit;
170 }
171
172 status = omapi_register_io_object ((omapi_object_t *)obj,
173 omapi_listener_readfd, 0,
174 omapi_accept, 0, 0);
175 #if defined (TRACING)
176 }
177 #endif
178
179 omapi_listener_dereference (&obj, MDL);
180 return status;
181
182 error_exit:
183 if (obj != NULL) {
184 if (h->outer == (omapi_object_t *)obj) {
185 omapi_object_dereference((omapi_object_t **)&h->outer,
186 MDL);
187 }
188 if (obj->inner == h) {
189 omapi_object_dereference((omapi_object_t **)&obj->inner,
190 MDL);
191 }
192 if (obj->socket != -1) {
193 close(obj->socket);
194 }
195 omapi_listener_dereference(&obj, MDL);
196 }
197 return status;
198 }
199
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)
203 {
204 omapi_listener_object_t *l;
205
206 if (h -> type != omapi_type_listener)
207 return -1;
208 l = (omapi_listener_object_t *)h;
209
210 return l -> socket;
211 }
212
213 /* Reader callback for a listener object. Accept an incoming connection. */
214 isc_result_t omapi_accept (omapi_object_t *h)
215 {
216 isc_result_t status;
217 socklen_t len;
218 omapi_connection_object_t *obj;
219 omapi_listener_object_t *listener;
220 struct sockaddr_in addr;
221 int socket;
222
223 if (h -> type != omapi_type_listener)
224 return DHCP_R_INVALIDARG;
225 listener = (omapi_listener_object_t *)h;
226
227 /* Accept the connection. */
228 len = sizeof addr;
229 socket = accept (listener -> socket,
230 ((struct sockaddr *)&(addr)), &len);
231 if (socket < 0) {
232 if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS)
233 return ISC_R_NORESOURCES;
234 return ISC_R_UNEXPECTED;
235 }
236
237 #if defined (TRACING)
238 /* If we're recording a trace, remember the connection. */
239 if (trace_record ()) {
240 trace_iov_t iov [3];
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,
248 3, iov, MDL);
249 }
250 #endif
251
252 obj = (omapi_connection_object_t *)0;
253 status = omapi_listener_connect (&obj, listener, socket, &addr);
254 if (status != ISC_R_SUCCESS) {
255 close (socket);
256 return status;
257 }
258
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);
265
266 /* Lose our reference to the connection, so it'll be gc'd when it's
267 reaped. */
268 omapi_connection_dereference (&obj, MDL);
269 if (status != ISC_R_SUCCESS)
270 omapi_disconnect ((omapi_object_t *)(obj), 1);
271 return status;
272 }
273
274 isc_result_t omapi_listener_connect (omapi_connection_object_t **obj,
275 omapi_listener_object_t *listener,
276 int socket,
277 struct sockaddr_in *remote_addr)
278 {
279 isc_result_t status;
280 omapi_object_t *h = (omapi_object_t *)listener;
281 omapi_addr_t addr;
282
283 #ifdef DEBUG_PROTOCOL
284 log_debug ("omapi_accept()");
285 #endif
286
287 /* Get the handle. */
288 status = omapi_connection_allocate (obj, MDL);
289 if (status != ISC_R_SUCCESS)
290 return status;
291
292 (*obj) -> state = omapi_connection_connected;
293 (*obj) -> remote_addr = *remote_addr;
294 (*obj) -> socket = socket;
295
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);
303
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);
308 return status;
309 }
310 }
311
312 omapi_listener_reference (&(*obj) -> listener, listener, MDL);
313 #if defined (TRACING)
314 omapi_connection_register (*obj, MDL);
315 #endif
316 status = omapi_signal (h, "connect", (*obj));
317 return status;
318 }
319
320 #if defined (TRACING)
321 OMAPI_ARRAY_TYPE(omapi_listener, omapi_listener_object_t)
322
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);
328 }
329
330 static void trace_listener_remember (omapi_listener_object_t *obj,
331 const char *file, int line)
332 {
333 isc_result_t status;
334 if (!trace_listeners) {
335 status = omapi_listener_array_allocate (&trace_listeners,
336 file, line);
337 if (status != ISC_R_SUCCESS) {
338 foo:
339 log_error ("trace_listener_remember: %s",
340 isc_result_totext (status));
341 return;
342 }
343 }
344 status = omapi_listener_array_extend (trace_listeners, obj,
345 &obj -> index, MDL);
346 if (status != ISC_R_SUCCESS)
347 goto foo;
348 }
349
350 static void trace_listener_accept_input (trace_type_t *ttype,
351 unsigned length, char *buf)
352 {
353 struct in_addr *addr;
354 u_int16_t *remote_port;
355 u_int16_t *local_port;
356 omapi_connection_object_t *obj;
357 isc_result_t status;
358 struct sockaddr_in remote_addr;
359
360 addr = (struct in_addr *)buf;
361 remote_port = (u_int16_t *)(addr + 1);
362 local_port = remote_port + 1;
363
364 memset (&remote_addr, 0, sizeof remote_addr);
365 remote_addr.sin_addr = *addr;
366 remote_addr.sin_port = *remote_port;
367
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 "
376 "a listener.", MDL);
377 }
378 omapi_listener_dereference (&lp, MDL);
379 return;
380 }
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);
386 }
387
388 static void trace_listener_accept_stop (trace_type_t *ttype) { }
389
390
391 #endif
392
393 isc_result_t omapi_listener_configure_security (omapi_object_t *h,
394 isc_result_t (*verify_addr)
395 (omapi_object_t *,
396 omapi_addr_t *))
397 {
398 omapi_listener_object_t *l;
399
400 if (h -> type != omapi_type_listener)
401 return DHCP_R_INVALIDARG;
402 l = (omapi_listener_object_t *)h;
403
404 l -> verify_addr = verify_addr;
405
406 return ISC_R_SUCCESS;
407 }
408
409 isc_result_t omapi_listener_set_value (omapi_object_t *h,
410 omapi_object_t *id,
411 omapi_data_string_t *name,
412 omapi_typed_data_t *value)
413 {
414 if (h -> type != omapi_type_listener)
415 return DHCP_R_INVALIDARG;
416
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;
421 }
422
423 isc_result_t omapi_listener_get_value (omapi_object_t *h,
424 omapi_object_t *id,
425 omapi_data_string_t *name,
426 omapi_value_t **value)
427 {
428 if (h -> type != omapi_type_listener)
429 return DHCP_R_INVALIDARG;
430
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;
435 }
436
437 isc_result_t omapi_listener_destroy (omapi_object_t *h,
438 const char *file, int line)
439 {
440 omapi_listener_object_t *l;
441
442 if (h -> type != omapi_type_listener)
443 return DHCP_R_INVALIDARG;
444 l = (omapi_listener_object_t *)h;
445
446 #ifdef DEBUG_PROTOCOL
447 log_debug ("omapi_listener_destroy()");
448 #endif
449
450 if (l -> socket != -1) {
451 close (l -> socket);
452 l -> socket = -1;
453 }
454 return ISC_R_SUCCESS;
455 }
456
457 isc_result_t omapi_listener_signal_handler (omapi_object_t *h,
458 const char *name, va_list ap)
459 {
460 if (h -> type != omapi_type_listener)
461 return DHCP_R_INVALIDARG;
462
463 if (h -> inner && h -> inner -> type -> signal_handler)
464 return (*(h -> inner -> type -> signal_handler)) (h -> inner,
465 name, ap);
466 return ISC_R_NOTFOUND;
467 }
468
469 /* Write all the published values associated with the object through the
470 specified connection. */
471
472 isc_result_t omapi_listener_stuff_values (omapi_object_t *c,
473 omapi_object_t *id,
474 omapi_object_t *l)
475 {
476 if (l -> type != omapi_type_listener)
477 return DHCP_R_INVALIDARG;
478
479 if (l -> inner && l -> inner -> type -> stuff_values)
480 return (*(l -> inner -> type -> stuff_values)) (c, id,
481 l -> inner);
482 return ISC_R_SUCCESS;
483 }
484