1: /* listener.c
2:
3: Subroutines that support the generic listener object. */
4:
5: /*
6: * Copyright (c) 2012 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: * This software has been written for Internet Systems Consortium
29: * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
30: * To learn more about Internet Systems Consortium, see
31: * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
32: * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
33: * ``http://www.nominum.com''.
34: */
35:
36: #include "dhcpd.h"
37:
38: #include <omapip/omapip_p.h>
39: #include <errno.h>
40:
41: #if defined (TRACING)
42: omapi_array_t *trace_listeners;
43: static void trace_listener_accept_input (trace_type_t *, unsigned, char *);
44: static void trace_listener_remember (omapi_listener_object_t *,
45: const char *, int);
46: static void trace_listener_accept_stop (trace_type_t *);
47: trace_type_t *trace_listener_accept;
48: #endif
49:
50: OMAPI_OBJECT_ALLOC (omapi_listener,
51: omapi_listener_object_t, omapi_type_listener)
52:
53: isc_result_t omapi_listen (omapi_object_t *h,
54: unsigned port,
55: int max)
56: {
57: omapi_addr_t addr;
58:
59: #ifdef DEBUG_PROTOCOL
60: log_debug ("omapi_listen(port=%d, max=%d)", port, max);
61: #endif
62:
63: addr.addrtype = AF_INET;
64: addr.addrlen = sizeof (struct in_addr);
65: memset (addr.address, 0, sizeof addr.address); /* INADDR_ANY */
66: addr.port = port;
67:
68: return omapi_listen_addr (h, &addr, max);
69: }
70:
71: isc_result_t omapi_listen_addr (omapi_object_t *h,
72: omapi_addr_t *addr,
73: int max)
74: {
75: isc_result_t status;
76: omapi_listener_object_t *obj;
77: int i;
78:
79: /* Currently only support IPv4 addresses. */
80: if (addr->addrtype != AF_INET)
81: return ISC_R_INVALIDARG;
82:
83: /* Get the handle. */
84: obj = (omapi_listener_object_t *)0;
85: status = omapi_listener_allocate (&obj, MDL);
86: if (status != ISC_R_SUCCESS)
87: return status;
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,
172: omapi_listener_readfd, 0,
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) {
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;
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;
216: socklen_t len;
217: omapi_connection_object_t *obj;
218: omapi_listener_object_t *listener;
219: struct sockaddr_in addr;
220: int socket;
221:
222: if (h -> type != omapi_type_listener)
223: return ISC_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 defined (TRACING)
237: /* If we're recording a trace, remember the connection. */
238: if (trace_record ()) {
239: trace_iov_t iov [3];
240: iov [0].buf = (char *)&addr.sin_port;
241: iov [0].len = sizeof addr.sin_port;
242: iov [1].buf = (char *)&addr.sin_addr;
243: iov [1].len = sizeof addr.sin_addr;
244: iov [2].buf = (char *)&listener -> address.sin_port;
245: iov [2].len = sizeof listener -> address.sin_port;
246: trace_write_packet_iov (trace_listener_accept,
247: 3, iov, MDL);
248: }
249: #endif
250:
251: obj = (omapi_connection_object_t *)0;
252: status = omapi_listener_connect (&obj, listener, socket, &addr);
253: if (status != ISC_R_SUCCESS) {
254: close (socket);
255: return status;
256: }
257:
258: status = omapi_register_io_object ((omapi_object_t *)obj,
259: omapi_connection_readfd,
260: omapi_connection_writefd,
261: omapi_connection_reader,
262: omapi_connection_writer,
263: omapi_connection_reaper);
264:
265: /* Lose our reference to the connection, so it'll be gc'd when it's
266: reaped. */
267: omapi_connection_dereference (&obj, MDL);
268: if (status != ISC_R_SUCCESS)
269: omapi_disconnect ((omapi_object_t *)(obj), 1);
270: return status;
271: }
272:
273: isc_result_t omapi_listener_connect (omapi_connection_object_t **obj,
274: omapi_listener_object_t *listener,
275: int socket,
276: struct sockaddr_in *remote_addr)
277: {
278: isc_result_t status;
279: omapi_object_t *h = (omapi_object_t *)listener;
280: omapi_addr_t addr;
281:
282: #ifdef DEBUG_PROTOCOL
283: log_debug ("omapi_accept()");
284: #endif
285:
286: /* Get the handle. */
287: status = omapi_connection_allocate (obj, MDL);
288: if (status != ISC_R_SUCCESS)
289: return status;
290:
291: (*obj) -> state = omapi_connection_connected;
292: (*obj) -> remote_addr = *remote_addr;
293: (*obj) -> socket = socket;
294:
295: /* Verify that this host is allowed to connect. */
296: if (listener -> verify_addr) {
297: addr.addrtype = AF_INET;
298: addr.addrlen = sizeof (remote_addr -> sin_addr);
299: memcpy (addr.address, &remote_addr -> sin_addr,
300: sizeof (remote_addr -> sin_addr));
301: addr.port = ntohs(remote_addr -> sin_port);
302:
303: status = (listener -> verify_addr) (h, &addr);
304: if (status != ISC_R_SUCCESS) {
305: omapi_disconnect ((omapi_object_t *)(*obj), 1);
306: omapi_connection_dereference (obj, MDL);
307: return status;
308: }
309: }
310:
311: omapi_listener_reference (&(*obj) -> listener, listener, MDL);
312: #if defined (TRACING)
313: omapi_connection_register (*obj, MDL);
314: #endif
315: status = omapi_signal (h, "connect", (*obj));
316: return status;
317: }
318:
319: #if defined (TRACING)
320: OMAPI_ARRAY_TYPE(omapi_listener, omapi_listener_object_t)
321:
322: void omapi_listener_trace_setup (void) {
323: trace_listener_accept =
324: trace_type_register ("listener-accept", (void *)0,
325: trace_listener_accept_input,
326: trace_listener_accept_stop, MDL);
327: }
328:
329: static void trace_listener_remember (omapi_listener_object_t *obj,
330: const char *file, int line)
331: {
332: isc_result_t status;
333: if (!trace_listeners) {
334: status = omapi_listener_array_allocate (&trace_listeners,
335: file, line);
336: if (status != ISC_R_SUCCESS) {
337: foo:
338: log_error ("trace_listener_remember: %s",
339: isc_result_totext (status));
340: return;
341: }
342: }
343: status = omapi_listener_array_extend (trace_listeners, obj,
344: &obj -> index, MDL);
345: if (status != ISC_R_SUCCESS)
346: goto foo;
347: }
348:
349: static void trace_listener_accept_input (trace_type_t *ttype,
350: unsigned length, char *buf)
351: {
352: struct in_addr *addr;
353: u_int16_t *remote_port;
354: u_int16_t *local_port;
355: omapi_connection_object_t *obj;
356: isc_result_t status;
357: struct sockaddr_in remote_addr;
358:
359: addr = (struct in_addr *)buf;
360: remote_port = (u_int16_t *)(addr + 1);
361: local_port = remote_port + 1;
362:
363: memset (&remote_addr, 0, sizeof remote_addr);
364: remote_addr.sin_addr = *addr;
365: remote_addr.sin_port = *remote_port;
366:
367: omapi_array_foreach_begin (trace_listeners,
368: omapi_listener_object_t, lp) {
369: if (lp -> address.sin_port == *local_port) {
370: obj = (omapi_connection_object_t *)0;
371: status = omapi_listener_connect (&obj,
372: lp, 0, &remote_addr);
373: if (status != ISC_R_SUCCESS) {
374: log_error("%s:%d: OMAPI: Failed to connect "
375: "a listener.", MDL);
376: }
377: omapi_listener_dereference (&lp, MDL);
378: return;
379: }
380: } omapi_array_foreach_end (trace_listeners,
381: omapi_listener_object_t, lp);
382: log_error ("trace_listener_accept: %s from %s/%d to port %d",
383: "unexpected connect",
384: inet_ntoa (*addr), *remote_port, *local_port);
385: }
386:
387: static void trace_listener_accept_stop (trace_type_t *ttype) { }
388:
389:
390: #endif
391:
392: isc_result_t omapi_listener_configure_security (omapi_object_t *h,
393: isc_result_t (*verify_addr)
394: (omapi_object_t *,
395: omapi_addr_t *))
396: {
397: omapi_listener_object_t *l;
398:
399: if (h -> type != omapi_type_listener)
400: return ISC_R_INVALIDARG;
401: l = (omapi_listener_object_t *)h;
402:
403: l -> verify_addr = verify_addr;
404:
405: return ISC_R_SUCCESS;
406: }
407:
408: isc_result_t omapi_listener_set_value (omapi_object_t *h,
409: omapi_object_t *id,
410: omapi_data_string_t *name,
411: omapi_typed_data_t *value)
412: {
413: if (h -> type != omapi_type_listener)
414: return ISC_R_INVALIDARG;
415:
416: if (h -> inner && h -> inner -> type -> set_value)
417: return (*(h -> inner -> type -> set_value))
418: (h -> inner, id, name, value);
419: return ISC_R_NOTFOUND;
420: }
421:
422: isc_result_t omapi_listener_get_value (omapi_object_t *h,
423: omapi_object_t *id,
424: omapi_data_string_t *name,
425: omapi_value_t **value)
426: {
427: if (h -> type != omapi_type_listener)
428: return ISC_R_INVALIDARG;
429:
430: if (h -> inner && h -> inner -> type -> get_value)
431: return (*(h -> inner -> type -> get_value))
432: (h -> inner, id, name, value);
433: return ISC_R_NOTFOUND;
434: }
435:
436: isc_result_t omapi_listener_destroy (omapi_object_t *h,
437: const char *file, int line)
438: {
439: omapi_listener_object_t *l;
440:
441: if (h -> type != omapi_type_listener)
442: return ISC_R_INVALIDARG;
443: l = (omapi_listener_object_t *)h;
444:
445: #ifdef DEBUG_PROTOCOL
446: log_debug ("omapi_listener_destroy()");
447: #endif
448:
449: if (l -> socket != -1) {
450: close (l -> socket);
451: l -> socket = -1;
452: }
453: return ISC_R_SUCCESS;
454: }
455:
456: isc_result_t omapi_listener_signal_handler (omapi_object_t *h,
457: const char *name, va_list ap)
458: {
459: if (h -> type != omapi_type_listener)
460: return ISC_R_INVALIDARG;
461:
462: if (h -> inner && h -> inner -> type -> signal_handler)
463: return (*(h -> inner -> type -> signal_handler)) (h -> inner,
464: name, ap);
465: return ISC_R_NOTFOUND;
466: }
467:
468: /* Write all the published values associated with the object through the
469: specified connection. */
470:
471: isc_result_t omapi_listener_stuff_values (omapi_object_t *c,
472: omapi_object_t *id,
473: omapi_object_t *l)
474: {
475: if (l -> type != omapi_type_listener)
476: return ISC_R_INVALIDARG;
477:
478: if (l -> inner && l -> inner -> type -> stuff_values)
479: return (*(l -> inner -> type -> stuff_values)) (c, id,
480: l -> inner);
481: return ISC_R_SUCCESS;
482: }
483:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>