ISC DHCP  4.3.2
A reference DHCPv4 and DHCPv6 implementation
listener.c
Go to the documentation of this file.
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,
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 
66  omapi_addr_t *addr,
67  int max)
68 {
69  isc_result_t status;
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,
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) {
186  MDL);
187  }
188  if (obj->inner == h) {
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. */
203 {
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;
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 ((MAX_FD_VALUE != 0) && (socket > MAX_FD_VALUE)) {
238  close(socket);
239  return (ISC_R_NORESOURCES);
240  }
241 
242 #if defined (TRACING)
243  /* If we're recording a trace, remember the connection. */
244  if (trace_record ()) {
245  trace_iov_t iov [3];
246  iov [0].buf = (char *)&addr.sin_port;
247  iov [0].len = sizeof addr.sin_port;
248  iov [1].buf = (char *)&addr.sin_addr;
249  iov [1].len = sizeof addr.sin_addr;
250  iov [2].buf = (char *)&listener -> address.sin_port;
251  iov [2].len = sizeof listener -> address.sin_port;
252  trace_write_packet_iov (trace_listener_accept,
253  3, iov, MDL);
254  }
255 #endif
256 
257  obj = (omapi_connection_object_t *)0;
258  status = omapi_listener_connect (&obj, listener, socket, &addr);
259  if (status != ISC_R_SUCCESS) {
260  close (socket);
261  return status;
262  }
263 
264  status = omapi_register_io_object ((omapi_object_t *)obj,
270 
271  /* Lose our reference to the connection, so it'll be gc'd when it's
272  reaped. */
273  omapi_connection_dereference (&obj, MDL);
274  if (status != ISC_R_SUCCESS)
275  omapi_disconnect ((omapi_object_t *)(obj), 1);
276  return status;
277 }
278 
280  omapi_listener_object_t *listener,
281  int socket,
282  struct sockaddr_in *remote_addr)
283 {
284  isc_result_t status;
285  omapi_object_t *h = (omapi_object_t *)listener;
286  omapi_addr_t addr;
287 
288 #ifdef DEBUG_PROTOCOL
289  log_debug ("omapi_accept()");
290 #endif
291 
292  /* Get the handle. */
293  status = omapi_connection_allocate (obj, MDL);
294  if (status != ISC_R_SUCCESS)
295  return status;
296 
297  (*obj) -> state = omapi_connection_connected;
298  (*obj) -> remote_addr = *remote_addr;
299  (*obj) -> socket = socket;
300 
301  /* Verify that this host is allowed to connect. */
302  if (listener -> verify_addr) {
303  addr.addrtype = AF_INET;
304  addr.addrlen = sizeof (remote_addr -> sin_addr);
305  memcpy (addr.address, &remote_addr -> sin_addr,
306  sizeof (remote_addr -> sin_addr));
307  addr.port = ntohs(remote_addr -> sin_port);
308 
309  status = (listener -> verify_addr) (h, &addr);
310  if (status != ISC_R_SUCCESS) {
311  omapi_disconnect ((omapi_object_t *)(*obj), 1);
312  omapi_connection_dereference (obj, MDL);
313  return status;
314  }
315  }
316 
317  omapi_listener_reference (&(*obj) -> listener, listener, MDL);
318 #if defined (TRACING)
320 #endif
321  status = omapi_signal (h, "connect", (*obj));
322  return status;
323 }
324 
325 #if defined (TRACING)
327 
328 void omapi_listener_trace_setup (void) {
329  trace_listener_accept =
330  trace_type_register ("listener-accept", (void *)0,
331  trace_listener_accept_input,
332  trace_listener_accept_stop, MDL);
333 }
334 
335 static void trace_listener_remember (omapi_listener_object_t *obj,
336  const char *file, int line)
337 {
338  isc_result_t status;
339  if (!trace_listeners) {
340  status = omapi_listener_array_allocate (&trace_listeners,
341  file, line);
342  if (status != ISC_R_SUCCESS) {
343  foo:
344  log_error ("trace_listener_remember: %s",
345  isc_result_totext (status));
346  return;
347  }
348  }
349  status = omapi_listener_array_extend (trace_listeners, obj,
350  &obj -> index, MDL);
351  if (status != ISC_R_SUCCESS)
352  goto foo;
353 }
354 
355 static void trace_listener_accept_input (trace_type_t *ttype,
356  unsigned length, char *buf)
357 {
358  struct in_addr *addr;
359  u_int16_t *remote_port;
360  u_int16_t *local_port;
362  isc_result_t status;
363  struct sockaddr_in remote_addr;
364 
365  addr = (struct in_addr *)buf;
366  remote_port = (u_int16_t *)(addr + 1);
367  local_port = remote_port + 1;
368 
369  memset (&remote_addr, 0, sizeof remote_addr);
370  remote_addr.sin_addr = *addr;
371  remote_addr.sin_port = *remote_port;
372 
373  omapi_array_foreach_begin (trace_listeners,
375  if (lp -> address.sin_port == *local_port) {
376  obj = (omapi_connection_object_t *)0;
377  status = omapi_listener_connect (&obj,
378  lp, 0, &remote_addr);
379  if (status != ISC_R_SUCCESS) {
380  log_error("%s:%d: OMAPI: Failed to connect "
381  "a listener.", MDL);
382  }
383  omapi_listener_dereference (&lp, MDL);
384  return;
385  }
386  } omapi_array_foreach_end (trace_listeners,
388  log_error ("trace_listener_accept: %s from %s/%d to port %d",
389  "unexpected connect",
390  inet_ntoa (*addr), *remote_port, *local_port);
391 }
392 
393 static void trace_listener_accept_stop (trace_type_t *ttype) { }
394 
395 
396 #endif
397 
399  isc_result_t (*verify_addr)
400  (omapi_object_t *,
401  omapi_addr_t *))
402 {
404 
405  if (h -> type != omapi_type_listener)
406  return DHCP_R_INVALIDARG;
407  l = (omapi_listener_object_t *)h;
408 
409  l -> verify_addr = verify_addr;
410 
411  return ISC_R_SUCCESS;
412 }
413 
415  omapi_object_t *id,
416  omapi_data_string_t *name,
417  omapi_typed_data_t *value)
418 {
419  if (h -> type != omapi_type_listener)
420  return DHCP_R_INVALIDARG;
421 
422  if (h -> inner && h -> inner -> type -> set_value)
423  return (*(h -> inner -> type -> set_value))
424  (h -> inner, id, name, value);
425  return ISC_R_NOTFOUND;
426 }
427 
429  omapi_object_t *id,
430  omapi_data_string_t *name,
431  omapi_value_t **value)
432 {
433  if (h -> type != omapi_type_listener)
434  return DHCP_R_INVALIDARG;
435 
436  if (h -> inner && h -> inner -> type -> get_value)
437  return (*(h -> inner -> type -> get_value))
438  (h -> inner, id, name, value);
439  return ISC_R_NOTFOUND;
440 }
441 
443  const char *file, int line)
444 {
446 
447  if (h -> type != omapi_type_listener)
448  return DHCP_R_INVALIDARG;
449  l = (omapi_listener_object_t *)h;
450 
451 #ifdef DEBUG_PROTOCOL
452  log_debug ("omapi_listener_destroy()");
453 #endif
454 
455  if (l -> socket != -1) {
456  close (l -> socket);
457  l -> socket = -1;
458  }
459  return ISC_R_SUCCESS;
460 }
461 
463  const char *name, va_list ap)
464 {
465  if (h -> type != omapi_type_listener)
466  return DHCP_R_INVALIDARG;
467 
468  if (h -> inner && h -> inner -> type -> signal_handler)
469  return (*(h -> inner -> type -> signal_handler)) (h -> inner,
470  name, ap);
471  return ISC_R_NOTFOUND;
472 }
473 
474 /* Write all the published values associated with the object through the
475  specified connection. */
476 
478  omapi_object_t *id,
479  omapi_object_t *l)
480 {
481  if (l -> type != omapi_type_listener)
482  return DHCP_R_INVALIDARG;
483 
484  if (l -> inner && l -> inner -> type -> stuff_values)
485  return (*(l -> inner -> type -> stuff_values)) (c, id,
486  l -> inner);
487  return ISC_R_SUCCESS;
488 }
489 
const char * buf
Definition: trace.h:75
int trace_playback(void)
isc_result_t omapi_listener_connect(omapi_connection_object_t **obj, omapi_listener_object_t *listener, int socket, struct sockaddr_in *remote_addr)
Definition: listener.c:279
unsigned port
Definition: omapip.h:139
isc_result_t omapi_connection_reader(omapi_object_t *)
Definition: buffer.c:132
const char int line
Definition: dhcpd.h:3615
#define omapi_array_foreach_end(array, stype, var)
Definition: omapip.h:257
isc_result_t omapi_register_io_object(omapi_object_t *, int(*)(omapi_object_t *), int(*)(omapi_object_t *), isc_result_t(*)(omapi_object_t *), isc_result_t(*)(omapi_object_t *), isc_result_t(*)(omapi_object_t *))
Definition: dispatch.c:199
isc_result_t omapi_object_reference(omapi_object_t **, omapi_object_t *, const char *, int)
Definition: alloc.c:557
isc_result_t omapi_listener_get_value(omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value)
Definition: listener.c:428
isc_result_t omapi_listener_configure_security(omapi_object_t *h, isc_result_t(*verify_addr)(omapi_object_t *, omapi_addr_t *))
Definition: listener.c:398
void omapi_listener_trace_setup(void)
#define MDL
Definition: omapip.h:568
#define DHCP_R_INVALIDARG
Definition: result.h:48
int int int log_debug(const char *,...) __attribute__((__format__(__printf__
isc_result_t trace_write_packet_iov(trace_type_t *, int, trace_iov_t *, const char *, int)
isc_result_t omapi_listener_stuff_values(omapi_object_t *c, omapi_object_t *id, omapi_object_t *l)
Definition: listener.c:477
int log_error(const char *,...) __attribute__((__format__(__printf__
isc_result_t omapi_connection_reaper(omapi_object_t *)
Definition: connection.c:740
OMAPI_OBJECT_ALLOC(omapi_listener, omapi_listener_object_t, omapi_type_listener)
Definition: listener.c:44
isc_result_t omapi_listener_set_value(omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value)
Definition: listener.c:414
#define omapi_array_foreach_begin(array, stype, var)
Definition: omapip.h:243
int omapi_connection_writefd(omapi_object_t *)
Definition: connection.c:593
omapi_object_type_t * omapi_type_listener
Definition: support.c:35
isc_result_t omapi_listener_signal_handler(omapi_object_t *h, const char *name, va_list ap)
Definition: listener.c:462
unsigned len
Definition: trace.h:76
trace_type_t * trace_type_register(const char *, void *, void(*)(trace_type_t *, unsigned, char *), void(*)(trace_type_t *), const char *, int)
isc_result_t omapi_listener_destroy(omapi_object_t *h, const char *file, int line)
Definition: listener.c:442
isc_result_t omapi_connection_writer(omapi_object_t *)
Definition: buffer.c:449
int omapi_listener_readfd(omapi_object_t *h)
Definition: listener.c:202
u_int16_t local_port
Definition: dhclient.c:88
isc_result_t omapi_object_dereference(omapi_object_t **, const char *, int)
Definition: alloc.c:579
isc_result_t omapi_signal(omapi_object_t *, const char *,...)
Definition: support.c:268
int trace_record(void)
void omapi_connection_register(omapi_connection_object_t *, const char *, int)
#define OMAPI_ARRAY_TYPE(name, stype)
Definition: omapip.h:198
unsigned addrlen
Definition: omapip.h:137
isc_result_t omapi_listen(omapi_object_t *, unsigned, int)
unsigned char address[16]
Definition: omapip.h:138
u_int16_t remote_port
Definition: dhclient.c:89
const char * file
Definition: dhcpd.h:3615
isc_result_t omapi_accept(omapi_object_t *h)
Definition: listener.c:214
unsigned addrtype
Definition: omapip.h:136
isc_result_t omapi_disconnect(omapi_object_t *, int)
Definition: connection.c:454
#define MAX_FD_VALUE
Definition: site.h:296
isc_result_t omapi_listen_addr(omapi_object_t *h, omapi_addr_t *addr, int max)
Definition: listener.c:65
int omapi_connection_readfd(omapi_object_t *)
Definition: connection.c:576