]>
Commit | Line | Data |
---|---|---|
61b844bf TL |
1 | /* listener.c |
2 | ||
3 | Subroutines that support the generic listener object. */ | |
4 | ||
5 | /* | |
7512d88b | 6 | * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") |
98311e4b | 7 | * Copyright (c) 1999-2003 by Internet Software Consortium |
61b844bf | 8 | * |
7512d88b TM |
9 | * This Source Code Form is subject to the terms of the Mozilla Public |
10 | * License, v. 2.0. If a copy of the MPL was not distributed with this | |
11 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
61b844bf | 12 | * |
98311e4b DH |
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. | |
61b844bf | 20 | * |
98311e4b DH |
21 | * Internet Systems Consortium, Inc. |
22 | * 950 Charter Street | |
23 | * Redwood City, CA 94063 | |
24 | * <info@isc.org> | |
2c85ac9b | 25 | * https://www.isc.org/ |
49733f31 | 26 | * |
61b844bf TL |
27 | */ |
28 | ||
fe5b0fdd DH |
29 | #include "dhcpd.h" |
30 | ||
6a4c4be8 | 31 | #include <omapip/omapip_p.h> |
fe5b0fdd | 32 | #include <errno.h> |
61b844bf | 33 | |
3ce5a420 | 34 | #if defined (TRACING) |
1a3bb68c | 35 | omapi_array_t *trace_listeners; |
3ce5a420 TL |
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 | ||
20916cae TL |
43 | OMAPI_OBJECT_ALLOC (omapi_listener, |
44 | omapi_listener_object_t, omapi_type_listener) | |
45 | ||
61b844bf | 46 | isc_result_t omapi_listen (omapi_object_t *h, |
d9eefc5d | 47 | unsigned port, |
61b844bf | 48 | int max) |
85e9641d TL |
49 | { |
50 | omapi_addr_t addr; | |
e11a162f DN |
51 | |
52 | #ifdef DEBUG_PROTOCOL | |
53 | log_debug ("omapi_listen(port=%d, max=%d)", port, max); | |
54 | #endif | |
55 | ||
85e9641d TL |
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 | ||
64 | isc_result_t omapi_listen_addr (omapi_object_t *h, | |
65 | omapi_addr_t *addr, | |
66 | int max) | |
61b844bf | 67 | { |
61b844bf TL |
68 | isc_result_t status; |
69 | omapi_listener_object_t *obj; | |
85e9641d | 70 | int i; |
61b844bf | 71 | |
763cba6b SK |
72 | /* Currently only support IPv4 addresses. */ |
73 | if (addr->addrtype != AF_INET) | |
98bf1607 | 74 | return DHCP_R_INVALIDARG; |
763cba6b | 75 | |
61b844bf | 76 | /* Get the handle. */ |
20916cae TL |
77 | obj = (omapi_listener_object_t *)0; |
78 | status = omapi_listener_allocate (&obj, MDL); | |
79 | if (status != ISC_R_SUCCESS) | |
0f750c4f SR |
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; | |
763cba6b | 88 | obj->socket = -1; |
61b844bf TL |
89 | |
90 | /* Connect this object to the inner object. */ | |
4bd8800e TL |
91 | status = omapi_object_reference (&h -> outer, |
92 | (omapi_object_t *)obj, MDL); | |
763cba6b SK |
93 | if (status != ISC_R_SUCCESS) |
94 | goto error_exit; | |
4bd8800e | 95 | status = omapi_object_reference (&obj -> inner, h, MDL); |
763cba6b SK |
96 | if (status != ISC_R_SUCCESS) |
97 | goto error_exit; | |
85e9641d | 98 | |
61b844bf | 99 | /* Set up the address on which we will listen... */ |
85e9641d TL |
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); | |
61b844bf | 110 | |
3ce5a420 TL |
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); | |
763cba6b | 120 | if (obj->socket == -1) { |
3ce5a420 TL |
121 | if (errno == EMFILE |
122 | || errno == ENFILE || errno == ENOBUFS) | |
763cba6b SK |
123 | status = ISC_R_NORESOURCES; |
124 | else | |
125 | status = ISC_R_UNEXPECTED; | |
126 | goto error_exit; | |
3ce5a420 | 127 | } |
dd9237c3 | 128 | |
892aa61a | 129 | #if defined (HAVE_SETFD) |
3ce5a420 | 130 | if (fcntl (obj -> socket, F_SETFD, 1) < 0) { |
763cba6b SK |
131 | status = ISC_R_UNEXPECTED; |
132 | goto error_exit; | |
3ce5a420 | 133 | } |
892aa61a TL |
134 | #endif |
135 | ||
3ce5a420 TL |
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) { | |
763cba6b SK |
141 | status = ISC_R_UNEXPECTED; |
142 | goto error_exit; | |
3ce5a420 | 143 | } |
dd9237c3 | 144 | |
3ce5a420 TL |
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) { | |
3ce5a420 | 151 | if (errno == EADDRINUSE) |
763cba6b SK |
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; | |
3ce5a420 | 158 | } |
49146f3c | 159 | |
3ce5a420 TL |
160 | /* Now tell the kernel to listen for connections. */ |
161 | if (listen (obj -> socket, max)) { | |
763cba6b SK |
162 | status = ISC_R_UNEXPECTED; |
163 | goto error_exit; | |
3ce5a420 | 164 | } |
61b844bf | 165 | |
3ce5a420 | 166 | if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) { |
763cba6b SK |
167 | status = ISC_R_UNEXPECTED; |
168 | goto error_exit; | |
3ce5a420 | 169 | } |
61b844bf | 170 | |
3ce5a420 TL |
171 | status = omapi_register_io_object ((omapi_object_t *)obj, |
172 | omapi_listener_readfd, 0, | |
173 | omapi_accept, 0, 0); | |
174 | #if defined (TRACING) | |
e857fa8c | 175 | } |
3ce5a420 | 176 | #endif |
763cba6b | 177 | |
20916cae TL |
178 | omapi_listener_dereference (&obj, MDL); |
179 | return status; | |
763cba6b SK |
180 | |
181 | error_exit: | |
182 | if (obj != NULL) { | |
183 | if (h->outer == (omapi_object_t *)obj) { | |
184 | omapi_object_dereference((omapi_object_t **)&h->outer, | |
185 | MDL); | |
186 | } | |
187 | if (obj->inner == h) { | |
188 | omapi_object_dereference((omapi_object_t **)&obj->inner, | |
189 | MDL); | |
190 | } | |
191 | if (obj->socket != -1) { | |
192 | close(obj->socket); | |
193 | } | |
194 | omapi_listener_dereference(&obj, MDL); | |
195 | } | |
196 | return status; | |
61b844bf TL |
197 | } |
198 | ||
199 | /* Return the socket on which the dispatcher should wait for readiness | |
200 | to read, for a listener object. */ | |
201 | int omapi_listener_readfd (omapi_object_t *h) | |
202 | { | |
203 | omapi_listener_object_t *l; | |
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; | |
fe5b0fdd | 216 | socklen_t len; |
61b844bf TL |
217 | omapi_connection_object_t *obj; |
218 | omapi_listener_object_t *listener; | |
3ce5a420 TL |
219 | struct sockaddr_in addr; |
220 | int socket; | |
61b844bf TL |
221 | |
222 | if (h -> type != omapi_type_listener) | |
98bf1607 | 223 | return DHCP_R_INVALIDARG; |
61b844bf | 224 | listener = (omapi_listener_object_t *)h; |
e11a162f | 225 | |
3ce5a420 TL |
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 | } | |
0b209ea5 SR |
235 | |
236 | if ((MAX_FD_VALUE != 0) && (socket > MAX_FD_VALUE)) { | |
237 | close(socket); | |
238 | return (ISC_R_NORESOURCES); | |
239 | } | |
240 | ||
3ce5a420 TL |
241 | #if defined (TRACING) |
242 | /* If we're recording a trace, remember the connection. */ | |
243 | if (trace_record ()) { | |
244 | trace_iov_t iov [3]; | |
3ce5a420 TL |
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, | |
264 | omapi_connection_readfd, | |
265 | omapi_connection_writefd, | |
266 | omapi_connection_reader, | |
267 | omapi_connection_writer, | |
268 | omapi_connection_reaper); | |
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 | ||
278 | isc_result_t omapi_listener_connect (omapi_connection_object_t **obj, | |
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 | ||
e11a162f DN |
287 | #ifdef DEBUG_PROTOCOL |
288 | log_debug ("omapi_accept()"); | |
289 | #endif | |
61b844bf TL |
290 | |
291 | /* Get the handle. */ | |
3ce5a420 | 292 | status = omapi_connection_allocate (obj, MDL); |
20916cae TL |
293 | if (status != ISC_R_SUCCESS) |
294 | return status; | |
61b844bf | 295 | |
3ce5a420 TL |
296 | (*obj) -> state = omapi_connection_connected; |
297 | (*obj) -> remote_addr = *remote_addr; | |
298 | (*obj) -> socket = socket; | |
61b844bf | 299 | |
49146f3c DN |
300 | /* Verify that this host is allowed to connect. */ |
301 | if (listener -> verify_addr) { | |
302 | addr.addrtype = AF_INET; | |
3ce5a420 TL |
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); | |
49146f3c DN |
307 | |
308 | status = (listener -> verify_addr) (h, &addr); | |
309 | if (status != ISC_R_SUCCESS) { | |
3ce5a420 TL |
310 | omapi_disconnect ((omapi_object_t *)(*obj), 1); |
311 | omapi_connection_dereference (obj, MDL); | |
49146f3c DN |
312 | return status; |
313 | } | |
314 | } | |
315 | ||
3ce5a420 TL |
316 | omapi_listener_reference (&(*obj) -> listener, listener, MDL); |
317 | #if defined (TRACING) | |
318 | omapi_connection_register (*obj, MDL); | |
319 | #endif | |
320 | status = omapi_signal (h, "connect", (*obj)); | |
321 | return status; | |
322 | } | |
61b844bf | 323 | |
3ce5a420 | 324 | #if defined (TRACING) |
a7394d15 | 325 | OMAPI_ARRAY_TYPE(omapi_listener, omapi_listener_object_t) |
61b844bf | 326 | |
3ce5a420 TL |
327 | void omapi_listener_trace_setup (void) { |
328 | trace_listener_accept = | |
1a3bb68c | 329 | trace_type_register ("listener-accept", (void *)0, |
3ce5a420 TL |
330 | trace_listener_accept_input, |
331 | trace_listener_accept_stop, MDL); | |
332 | } | |
61b844bf | 333 | |
3ce5a420 TL |
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 | } | |
1a3bb68c TL |
348 | status = omapi_listener_array_extend (trace_listeners, obj, |
349 | &obj -> index, MDL); | |
3ce5a420 TL |
350 | if (status != ISC_R_SUCCESS) |
351 | goto foo; | |
61b844bf TL |
352 | } |
353 | ||
3ce5a420 TL |
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; | |
360 | omapi_connection_object_t *obj; | |
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, | |
373 | omapi_listener_object_t, lp) { | |
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); | |
dd9237c3 TM |
378 | if (status != ISC_R_SUCCESS) { |
379 | log_error("%s:%d: OMAPI: Failed to connect " | |
380 | "a listener.", MDL); | |
381 | } | |
1a3bb68c | 382 | omapi_listener_dereference (&lp, MDL); |
3ce5a420 TL |
383 | return; |
384 | } | |
385 | } omapi_array_foreach_end (trace_listeners, | |
386 | omapi_listener_object_t, lp); | |
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 | ||
49146f3c DN |
397 | isc_result_t omapi_listener_configure_security (omapi_object_t *h, |
398 | isc_result_t (*verify_addr) | |
399 | (omapi_object_t *, | |
400 | omapi_addr_t *)) | |
401 | { | |
402 | omapi_listener_object_t *l; | |
403 | ||
404 | if (h -> type != omapi_type_listener) | |
98bf1607 | 405 | return DHCP_R_INVALIDARG; |
49146f3c DN |
406 | l = (omapi_listener_object_t *)h; |
407 | ||
408 | l -> verify_addr = verify_addr; | |
409 | ||
410 | return ISC_R_SUCCESS; | |
411 | } | |
412 | ||
61b844bf TL |
413 | isc_result_t omapi_listener_set_value (omapi_object_t *h, |
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) | |
98bf1607 | 419 | return DHCP_R_INVALIDARG; |
61b844bf TL |
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 | ||
427 | isc_result_t omapi_listener_get_value (omapi_object_t *h, | |
428 | omapi_object_t *id, | |
429 | omapi_data_string_t *name, | |
430 | omapi_value_t **value) | |
431 | { | |
432 | if (h -> type != omapi_type_listener) | |
98bf1607 | 433 | return DHCP_R_INVALIDARG; |
61b844bf TL |
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 | ||
4bd8800e TL |
441 | isc_result_t omapi_listener_destroy (omapi_object_t *h, |
442 | const char *file, int line) | |
61b844bf TL |
443 | { |
444 | omapi_listener_object_t *l; | |
445 | ||
446 | if (h -> type != omapi_type_listener) | |
98bf1607 | 447 | return DHCP_R_INVALIDARG; |
20916cae | 448 | l = (omapi_listener_object_t *)h; |
e11a162f DN |
449 | |
450 | #ifdef DEBUG_PROTOCOL | |
451 | log_debug ("omapi_listener_destroy()"); | |
452 | #endif | |
61b844bf TL |
453 | |
454 | if (l -> socket != -1) { | |
455 | close (l -> socket); | |
456 | l -> socket = -1; | |
457 | } | |
458 | return ISC_R_SUCCESS; | |
459 | } | |
460 | ||
461 | isc_result_t omapi_listener_signal_handler (omapi_object_t *h, | |
b1b7b521 | 462 | const char *name, va_list ap) |
61b844bf TL |
463 | { |
464 | if (h -> type != omapi_type_listener) | |
98bf1607 | 465 | return DHCP_R_INVALIDARG; |
61b844bf TL |
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 | ||
476 | isc_result_t omapi_listener_stuff_values (omapi_object_t *c, | |
477 | omapi_object_t *id, | |
478 | omapi_object_t *l) | |
479 | { | |
61b844bf | 480 | if (l -> type != omapi_type_listener) |
98bf1607 | 481 | return DHCP_R_INVALIDARG; |
61b844bf TL |
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 |