ISC DHCP  4.3.0
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 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,
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 
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)
315 #endif
316  status = omapi_signal (h, "connect", (*obj));
317  return status;
318 }
319 
320 #if defined (TRACING)
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;
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,
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,
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 
394  isc_result_t (*verify_addr)
395  (omapi_object_t *,
396  omapi_addr_t *))
397 {
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 
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 
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 
438  const char *file, int line)
439 {
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 
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 
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 
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:274
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:3535
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:423
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:393
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:472
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
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_set_value(omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value)
Definition: listener.c:409
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:457
unsigned len
Definition: trace.h:76
isc_result_t omapi_listener_destroy(omapi_object_t *h, const char *file, int line)
Definition: listener.c:437
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:87
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)
unsigned addrlen
Definition: omapip.h:137
isc_result_t omapi_listen(omapi_object_t *, unsigned, int)
unsigned char address[16]
Definition: omapip.h:138
#define omapi_array_foreach_end(array, stype, var)
Definition: omapip.h:257
#define OMAPI_ARRAY_TYPE(name, stype)
Definition: omapip.h:198
u_int16_t remote_port
Definition: dhclient.c:88
const char * file
Definition: dhcpd.h:3535
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 omapi_array_foreach_begin(array, stype, var)
Definition: omapip.h:243
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