ISC DHCP  4.3.6
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) 2004-2016 by Internet Systems Consortium, Inc. ("ISC")
7  * Copyright (c) 1999-2003 by Internet Software Consortium
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  *
21  * Internet Systems Consortium, Inc.
22  * 950 Charter Street
23  * Redwood City, CA 94063
24  * <info@isc.org>
25  * https://www.isc.org/
26  *
27  */
28 
29 #include "dhcpd.h"
30 
31 #include <omapip/omapip_p.h>
32 #include <errno.h>
33 
34 #if defined (TRACING)
35 omapi_array_t *trace_listeners;
36 static void trace_listener_accept_input (trace_type_t *, unsigned, char *);
37 static void trace_listener_remember (omapi_listener_object_t *,
38  const char *, int);
39 static void trace_listener_accept_stop (trace_type_t *);
40 trace_type_t *trace_listener_accept;
41 #endif
42 
43 OMAPI_OBJECT_ALLOC (omapi_listener,
45 
46 isc_result_t omapi_listen (omapi_object_t *h,
47  unsigned port,
48  int max)
49 {
50  omapi_addr_t addr;
51 
52 #ifdef DEBUG_PROTOCOL
53  log_debug ("omapi_listen(port=%d, max=%d)", port, max);
54 #endif
55 
56  addr.addrtype = AF_INET;
57  addr.addrlen = sizeof (struct in_addr);
58  memset (addr.address, 0, sizeof addr.address); /* INADDR_ANY */
59  addr.port = port;
60 
61  return omapi_listen_addr (h, &addr, max);
62 }
63 
65  omapi_addr_t *addr,
66  int max)
67 {
68  isc_result_t status;
70  int i;
71 
72  /* Currently only support IPv4 addresses. */
73  if (addr->addrtype != AF_INET)
74  return DHCP_R_INVALIDARG;
75 
76  /* Get the handle. */
77  obj = (omapi_listener_object_t *)0;
78  status = omapi_listener_allocate (&obj, MDL);
79  if (status != ISC_R_SUCCESS)
80  /*
81  * we could simply return here but by going to
82  * error_exit we keep the code check tools happy
83  * without removing the NULL check on obj at
84  * the exit, which we could skip curently but
85  * might want in the future.
86  */
87  goto error_exit;
88  obj->socket = -1;
89 
90  /* Connect this object to the inner object. */
91  status = omapi_object_reference (&h -> outer,
92  (omapi_object_t *)obj, MDL);
93  if (status != ISC_R_SUCCESS)
94  goto error_exit;
95  status = omapi_object_reference (&obj -> inner, h, MDL);
96  if (status != ISC_R_SUCCESS)
97  goto error_exit;
98 
99  /* Set up the address on which we will listen... */
100  obj -> address.sin_port = htons (addr -> port);
101  memcpy (&obj -> address.sin_addr,
102  addr -> address, sizeof obj -> address.sin_addr);
103 #if defined (HAVE_SA_LEN)
104  obj -> address.sin_len =
105  sizeof (struct sockaddr_in);
106 #endif
107  obj -> address.sin_family = AF_INET;
108  memset (&(obj -> address.sin_zero), 0,
109  sizeof obj -> address.sin_zero);
110 
111 #if defined (TRACING)
112  /* If we're playing back a trace file, we remember the object
113  on the trace listener queue. */
114  if (trace_playback ()) {
115  trace_listener_remember (obj, MDL);
116  } else {
117 #endif
118  /* Create a socket on which to listen. */
119  obj -> socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
120  if (obj->socket == -1) {
121  if (errno == EMFILE
122  || errno == ENFILE || errno == ENOBUFS)
123  status = ISC_R_NORESOURCES;
124  else
125  status = ISC_R_UNEXPECTED;
126  goto error_exit;
127  }
128 
129 #if defined (HAVE_SETFD)
130  if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
131  status = ISC_R_UNEXPECTED;
132  goto error_exit;
133  }
134 #endif
135 
136  /* Set the REUSEADDR option so that we don't fail to start if
137  we're being restarted. */
138  i = 1;
139  if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR,
140  (char *)&i, sizeof i) < 0) {
141  status = ISC_R_UNEXPECTED;
142  goto error_exit;
143  }
144 
145  /* Try to bind to the wildcard address using the port number
146  we were given. */
147  i = bind (obj -> socket,
148  (struct sockaddr *)&obj -> address,
149  sizeof obj -> address);
150  if (i < 0) {
151  if (errno == EADDRINUSE)
152  status = ISC_R_ADDRNOTAVAIL;
153  else if (errno == EPERM)
154  status = ISC_R_NOPERM;
155  else
156  status = ISC_R_UNEXPECTED;
157  goto error_exit;
158  }
159 
160  /* Now tell the kernel to listen for connections. */
161  if (listen (obj -> socket, max)) {
162  status = ISC_R_UNEXPECTED;
163  goto error_exit;
164  }
165 
166  if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
167  status = ISC_R_UNEXPECTED;
168  goto error_exit;
169  }
170 
171  status = omapi_register_io_object ((omapi_object_t *)obj,
173  omapi_accept, 0, 0);
174 #if defined (TRACING)
175  }
176 #endif
177 
178  omapi_listener_dereference (&obj, MDL);
179  return status;
180 
181 error_exit:
182  if (obj != NULL) {
183  if (h->outer == (omapi_object_t *)obj) {
185  MDL);
186  }
187  if (obj->inner == h) {
189  MDL);
190  }
191  if (obj->socket != -1) {
192  close(obj->socket);
193  }
194  omapi_listener_dereference(&obj, MDL);
195  }
196  return status;
197 }
198 
199 /* Return the socket on which the dispatcher should wait for readiness
200  to read, for a listener object. */
202 {
204 
205  if (h -> type != omapi_type_listener)
206  return -1;
207  l = (omapi_listener_object_t *)h;
208 
209  return l -> socket;
210 }
211 
212 /* Reader callback for a listener object. Accept an incoming connection. */
213 isc_result_t omapi_accept (omapi_object_t *h)
214 {
215  isc_result_t status;
216  socklen_t len;
218  omapi_listener_object_t *listener;
219  struct sockaddr_in addr;
220  int socket;
221 
222  if (h -> type != omapi_type_listener)
223  return DHCP_R_INVALIDARG;
224  listener = (omapi_listener_object_t *)h;
225 
226  /* Accept the connection. */
227  len = sizeof addr;
228  socket = accept (listener -> socket,
229  ((struct sockaddr *)&(addr)), &len);
230  if (socket < 0) {
231  if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS)
232  return ISC_R_NORESOURCES;
233  return ISC_R_UNEXPECTED;
234  }
235 
236  if ((MAX_FD_VALUE != 0) && (socket > MAX_FD_VALUE)) {
237  close(socket);
238  return (ISC_R_NORESOURCES);
239  }
240 
241 #if defined (TRACING)
242  /* If we're recording a trace, remember the connection. */
243  if (trace_record ()) {
244  trace_iov_t iov [3];
245  iov [0].buf = (char *)&addr.sin_port;
246  iov [0].len = sizeof addr.sin_port;
247  iov [1].buf = (char *)&addr.sin_addr;
248  iov [1].len = sizeof addr.sin_addr;
249  iov [2].buf = (char *)&listener -> address.sin_port;
250  iov [2].len = sizeof listener -> address.sin_port;
251  trace_write_packet_iov (trace_listener_accept,
252  3, iov, MDL);
253  }
254 #endif
255 
256  obj = (omapi_connection_object_t *)0;
257  status = omapi_listener_connect (&obj, listener, socket, &addr);
258  if (status != ISC_R_SUCCESS) {
259  close (socket);
260  return status;
261  }
262 
263  status = omapi_register_io_object ((omapi_object_t *)obj,
269 
270  /* Lose our reference to the connection, so it'll be gc'd when it's
271  reaped. */
272  omapi_connection_dereference (&obj, MDL);
273  if (status != ISC_R_SUCCESS)
274  omapi_disconnect ((omapi_object_t *)(obj), 1);
275  return status;
276 }
277 
279  omapi_listener_object_t *listener,
280  int socket,
281  struct sockaddr_in *remote_addr)
282 {
283  isc_result_t status;
284  omapi_object_t *h = (omapi_object_t *)listener;
285  omapi_addr_t addr;
286 
287 #ifdef DEBUG_PROTOCOL
288  log_debug ("omapi_accept()");
289 #endif
290 
291  /* Get the handle. */
292  status = omapi_connection_allocate (obj, MDL);
293  if (status != ISC_R_SUCCESS)
294  return status;
295 
296  (*obj) -> state = omapi_connection_connected;
297  (*obj) -> remote_addr = *remote_addr;
298  (*obj) -> socket = socket;
299 
300  /* Verify that this host is allowed to connect. */
301  if (listener -> verify_addr) {
302  addr.addrtype = AF_INET;
303  addr.addrlen = sizeof (remote_addr -> sin_addr);
304  memcpy (addr.address, &remote_addr -> sin_addr,
305  sizeof (remote_addr -> sin_addr));
306  addr.port = ntohs(remote_addr -> sin_port);
307 
308  status = (listener -> verify_addr) (h, &addr);
309  if (status != ISC_R_SUCCESS) {
310  omapi_disconnect ((omapi_object_t *)(*obj), 1);
311  omapi_connection_dereference (obj, MDL);
312  return status;
313  }
314  }
315 
316  omapi_listener_reference (&(*obj) -> listener, listener, MDL);
317 #if defined (TRACING)
319 #endif
320  status = omapi_signal (h, "connect", (*obj));
321  return status;
322 }
323 
324 #if defined (TRACING)
326 
327 void omapi_listener_trace_setup (void) {
328  trace_listener_accept =
329  trace_type_register ("listener-accept", (void *)0,
330  trace_listener_accept_input,
331  trace_listener_accept_stop, MDL);
332 }
333 
334 static void trace_listener_remember (omapi_listener_object_t *obj,
335  const char *file, int line)
336 {
337  isc_result_t status;
338  if (!trace_listeners) {
339  status = omapi_listener_array_allocate (&trace_listeners,
340  file, line);
341  if (status != ISC_R_SUCCESS) {
342  foo:
343  log_error ("trace_listener_remember: %s",
344  isc_result_totext (status));
345  return;
346  }
347  }
348  status = omapi_listener_array_extend (trace_listeners, obj,
349  &obj -> index, MDL);
350  if (status != ISC_R_SUCCESS)
351  goto foo;
352 }
353 
354 static void trace_listener_accept_input (trace_type_t *ttype,
355  unsigned length, char *buf)
356 {
357  struct in_addr *addr;
358  u_int16_t *remote_port;
359  u_int16_t *local_port;
361  isc_result_t status;
362  struct sockaddr_in remote_addr;
363 
364  addr = (struct in_addr *)buf;
365  remote_port = (u_int16_t *)(addr + 1);
366  local_port = remote_port + 1;
367 
368  memset (&remote_addr, 0, sizeof remote_addr);
369  remote_addr.sin_addr = *addr;
370  remote_addr.sin_port = *remote_port;
371 
372  omapi_array_foreach_begin (trace_listeners,
374  if (lp -> address.sin_port == *local_port) {
375  obj = (omapi_connection_object_t *)0;
376  status = omapi_listener_connect (&obj,
377  lp, 0, &remote_addr);
378  if (status != ISC_R_SUCCESS) {
379  log_error("%s:%d: OMAPI: Failed to connect "
380  "a listener.", MDL);
381  }
382  omapi_listener_dereference (&lp, MDL);
383  return;
384  }
385  } omapi_array_foreach_end (trace_listeners,
387  log_error ("trace_listener_accept: %s from %s/%d to port %d",
388  "unexpected connect",
389  inet_ntoa (*addr), *remote_port, *local_port);
390 }
391 
392 static void trace_listener_accept_stop (trace_type_t *ttype) { }
393 
394 
395 #endif
396 
398  isc_result_t (*verify_addr)
399  (omapi_object_t *,
400  omapi_addr_t *))
401 {
403 
404  if (h -> type != omapi_type_listener)
405  return DHCP_R_INVALIDARG;
406  l = (omapi_listener_object_t *)h;
407 
408  l -> verify_addr = verify_addr;
409 
410  return ISC_R_SUCCESS;
411 }
412 
414  omapi_object_t *id,
415  omapi_data_string_t *name,
416  omapi_typed_data_t *value)
417 {
418  if (h -> type != omapi_type_listener)
419  return DHCP_R_INVALIDARG;
420 
421  if (h -> inner && h -> inner -> type -> set_value)
422  return (*(h -> inner -> type -> set_value))
423  (h -> inner, id, name, value);
424  return ISC_R_NOTFOUND;
425 }
426 
428  omapi_object_t *id,
429  omapi_data_string_t *name,
430  omapi_value_t **value)
431 {
432  if (h -> type != omapi_type_listener)
433  return DHCP_R_INVALIDARG;
434 
435  if (h -> inner && h -> inner -> type -> get_value)
436  return (*(h -> inner -> type -> get_value))
437  (h -> inner, id, name, value);
438  return ISC_R_NOTFOUND;
439 }
440 
442  const char *file, int line)
443 {
445 
446  if (h -> type != omapi_type_listener)
447  return DHCP_R_INVALIDARG;
448  l = (omapi_listener_object_t *)h;
449 
450 #ifdef DEBUG_PROTOCOL
451  log_debug ("omapi_listener_destroy()");
452 #endif
453 
454  if (l -> socket != -1) {
455  close (l -> socket);
456  l -> socket = -1;
457  }
458  return ISC_R_SUCCESS;
459 }
460 
462  const char *name, va_list ap)
463 {
464  if (h -> type != omapi_type_listener)
465  return DHCP_R_INVALIDARG;
466 
467  if (h -> inner && h -> inner -> type -> signal_handler)
468  return (*(h -> inner -> type -> signal_handler)) (h -> inner,
469  name, ap);
470  return ISC_R_NOTFOUND;
471 }
472 
473 /* Write all the published values associated with the object through the
474  specified connection. */
475 
477  omapi_object_t *id,
478  omapi_object_t *l)
479 {
480  if (l -> type != omapi_type_listener)
481  return DHCP_R_INVALIDARG;
482 
483  if (l -> inner && l -> inner -> type -> stuff_values)
484  return (*(l -> inner -> type -> stuff_values)) (c, id,
485  l -> inner);
486  return ISC_R_SUCCESS;
487 }
488 
const char * buf
Definition: trace.h:75
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:278
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:3724
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:571
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:427
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:397
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 omapi_listener_stuff_values(omapi_object_t *c, omapi_object_t *id, omapi_object_t *l)
Definition: listener.c:476
int trace_playback(void)
int log_error(const char *,...) __attribute__((__format__(__printf__
isc_result_t omapi_connection_reaper(omapi_object_t *)
Definition: connection.c:743
OMAPI_OBJECT_ALLOC(omapi_listener, omapi_listener_object_t, omapi_type_listener)
Definition: listener.c:43
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:413
int omapi_connection_writefd(omapi_object_t *)
Definition: connection.c:596
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:461
trace_type_t * trace_type_register(const char *, void *, void(*)(trace_type_t *, unsigned, char *), void(*)(trace_type_t *), const char *, int)
unsigned len
Definition: trace.h:76
isc_result_t omapi_listener_destroy(omapi_object_t *h, const char *file, int line)
Definition: listener.c:441
isc_result_t omapi_connection_writer(omapi_object_t *)
Definition: buffer.c:449
int omapi_listener_readfd(omapi_object_t *h)
Definition: listener.c:201
u_int16_t local_port
Definition: dhclient.c:92
isc_result_t omapi_object_dereference(omapi_object_t **, const char *, int)
Definition: alloc.c:593
isc_result_t omapi_signal(omapi_object_t *, const char *,...)
Definition: support.c:268
isc_result_t trace_write_packet_iov(trace_type_t *, int, trace_iov_t *, const char *, int)
void omapi_connection_register(omapi_connection_object_t *, const char *, int)
int trace_record(void)
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:93
const char * file
Definition: dhcpd.h:3724
isc_result_t omapi_accept(omapi_object_t *h)
Definition: listener.c:213
unsigned addrtype
Definition: omapip.h:136
isc_result_t omapi_disconnect(omapi_object_t *, int)
Definition: connection.c:457
#define MAX_FD_VALUE
Definition: site.h:326
#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:64
int omapi_connection_readfd(omapi_object_t *)
Definition: connection.c:579