1: /* listener.c
2:
3: Subroutines that support the generic listener object. */
4:
5: /*
6: * Copyright (c) 2004,2007,2009 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: * This software has been written for Internet Systems Consortium
28: * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29: * To learn more about Internet Systems Consortium, see
30: * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
31: * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
32: * ``http://www.nominum.com''.
33: */
34:
35: #include "dhcpd.h"
36:
37: #include <omapip/omapip_p.h>
38: #include <errno.h>
39:
40: #if defined (TRACING)
41: omapi_array_t *trace_listeners;
42: static void trace_listener_accept_input (trace_type_t *, unsigned, char *);
43: static void trace_listener_remember (omapi_listener_object_t *,
44: const char *, int);
45: static void trace_listener_accept_stop (trace_type_t *);
46: trace_type_t *trace_listener_accept;
47: #endif
48:
49: OMAPI_OBJECT_ALLOC (omapi_listener,
50: omapi_listener_object_t, omapi_type_listener)
51:
52: isc_result_t omapi_listen (omapi_object_t *h,
53: unsigned port,
54: int max)
55: {
56: omapi_addr_t addr;
57:
58: #ifdef DEBUG_PROTOCOL
59: log_debug ("omapi_listen(port=%d, max=%d)", port, max);
60: #endif
61:
62: addr.addrtype = AF_INET;
63: addr.addrlen = sizeof (struct in_addr);
64: memset (addr.address, 0, sizeof addr.address); /* INADDR_ANY */
65: addr.port = port;
66:
67: return omapi_listen_addr (h, &addr, max);
68: }
69:
70: isc_result_t omapi_listen_addr (omapi_object_t *h,
71: omapi_addr_t *addr,
72: int max)
73: {
74: isc_result_t status;
75: omapi_listener_object_t *obj;
76: int i;
77:
78: /* Currently only support IPv4 addresses. */
79: if (addr->addrtype != AF_INET)
80: return ISC_R_INVALIDARG;
81:
82: /* Get the handle. */
83: obj = (omapi_listener_object_t *)0;
84: status = omapi_listener_allocate (&obj, MDL);
85: if (status != ISC_R_SUCCESS)
86: return status;
87: obj->socket = -1;
88:
89: /* Connect this object to the inner object. */
90: status = omapi_object_reference (&h -> outer,
91: (omapi_object_t *)obj, MDL);
92: if (status != ISC_R_SUCCESS)
93: goto error_exit;
94: status = omapi_object_reference (&obj -> inner, h, MDL);
95: if (status != ISC_R_SUCCESS)
96: goto error_exit;
97:
98: /* Set up the address on which we will listen... */
99: obj -> address.sin_port = htons (addr -> port);
100: memcpy (&obj -> address.sin_addr,
101: addr -> address, sizeof obj -> address.sin_addr);
102: #if defined (HAVE_SA_LEN)
103: obj -> address.sin_len =
104: sizeof (struct sockaddr_in);
105: #endif
106: obj -> address.sin_family = AF_INET;
107: memset (&(obj -> address.sin_zero), 0,
108: sizeof obj -> address.sin_zero);
109:
110: #if defined (TRACING)
111: /* If we're playing back a trace file, we remember the object
112: on the trace listener queue. */
113: if (trace_playback ()) {
114: trace_listener_remember (obj, MDL);
115: } else {
116: #endif
117: /* Create a socket on which to listen. */
118: obj -> socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
119: if (obj->socket == -1) {
120: if (errno == EMFILE
121: || errno == ENFILE || errno == ENOBUFS)
122: status = ISC_R_NORESOURCES;
123: else
124: status = ISC_R_UNEXPECTED;
125: goto error_exit;
126: }
127:
128: #if defined (HAVE_SETFD)
129: if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
130: status = ISC_R_UNEXPECTED;
131: goto error_exit;
132: }
133: #endif
134:
135: /* Set the REUSEADDR option so that we don't fail to start if
136: we're being restarted. */
137: i = 1;
138: if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR,
139: (char *)&i, sizeof i) < 0) {
140: status = ISC_R_UNEXPECTED;
141: goto error_exit;
142: }
143:
144: /* Try to bind to the wildcard address using the port number
145: we were given. */
146: i = bind (obj -> socket,
147: (struct sockaddr *)&obj -> address,
148: sizeof obj -> address);
149: if (i < 0) {
150: if (errno == EADDRINUSE)
151: status = ISC_R_ADDRNOTAVAIL;
152: else if (errno == EPERM)
153: status = ISC_R_NOPERM;
154: else
155: status = ISC_R_UNEXPECTED;
156: goto error_exit;
157: }
158:
159: /* Now tell the kernel to listen for connections. */
160: if (listen (obj -> socket, max)) {
161: status = ISC_R_UNEXPECTED;
162: goto error_exit;
163: }
164:
165: if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
166: status = ISC_R_UNEXPECTED;
167: goto error_exit;
168: }
169:
170: status = omapi_register_io_object ((omapi_object_t *)obj,
171: omapi_listener_readfd, 0,
172: omapi_accept, 0, 0);
173: #if defined (TRACING)
174: }
175: #endif
176:
177: omapi_listener_dereference (&obj, MDL);
178: return status;
179:
180: error_exit:
181: if (obj != NULL) {
182: if (h->outer == (omapi_object_t *)obj) {
183: omapi_object_dereference((omapi_object_t **)&h->outer,
184: MDL);
185: }
186: if (obj->inner == h) {
187: omapi_object_dereference((omapi_object_t **)&obj->inner,
188: MDL);
189: }
190: if (obj->socket != -1) {
191: close(obj->socket);
192: }
193: omapi_listener_dereference(&obj, MDL);
194: }
195: return status;
196: }
197:
198: /* Return the socket on which the dispatcher should wait for readiness
199: to read, for a listener object. */
200: int omapi_listener_readfd (omapi_object_t *h)
201: {
202: omapi_listener_object_t *l;
203:
204: if (h -> type != omapi_type_listener)
205: return -1;
206: l = (omapi_listener_object_t *)h;
207:
208: return l -> socket;
209: }
210:
211: /* Reader callback for a listener object. Accept an incoming connection. */
212: isc_result_t omapi_accept (omapi_object_t *h)
213: {
214: isc_result_t status;
215: socklen_t len;
216: omapi_connection_object_t *obj;
217: omapi_listener_object_t *listener;
218: struct sockaddr_in addr;
219: int socket;
220:
221: if (h -> type != omapi_type_listener)
222: return ISC_R_INVALIDARG;
223: listener = (omapi_listener_object_t *)h;
224:
225: /* Accept the connection. */
226: len = sizeof addr;
227: socket = accept (listener -> socket,
228: ((struct sockaddr *)&(addr)), &len);
229: if (socket < 0) {
230: if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS)
231: return ISC_R_NORESOURCES;
232: return ISC_R_UNEXPECTED;
233: }
234:
235: #if defined (TRACING)
236: /* If we're recording a trace, remember the connection. */
237: if (trace_record ()) {
238: trace_iov_t iov [3];
239: iov [0].buf = (char *)&addr.sin_port;
240: iov [0].len = sizeof addr.sin_port;
241: iov [1].buf = (char *)&addr.sin_addr;
242: iov [1].len = sizeof addr.sin_addr;
243: iov [2].buf = (char *)&listener -> address.sin_port;
244: iov [2].len = sizeof listener -> address.sin_port;
245: trace_write_packet_iov (trace_listener_accept,
246: 3, iov, MDL);
247: }
248: #endif
249:
250: obj = (omapi_connection_object_t *)0;
251: status = omapi_listener_connect (&obj, listener, socket, &addr);
252: if (status != ISC_R_SUCCESS) {
253: close (socket);
254: return status;
255: }
256:
257: status = omapi_register_io_object ((omapi_object_t *)obj,
258: omapi_connection_readfd,
259: omapi_connection_writefd,
260: omapi_connection_reader,
261: omapi_connection_writer,
262: omapi_connection_reaper);
263:
264: /* Lose our reference to the connection, so it'll be gc'd when it's
265: reaped. */
266: omapi_connection_dereference (&obj, MDL);
267: if (status != ISC_R_SUCCESS)
268: omapi_disconnect ((omapi_object_t *)(obj), 1);
269: return status;
270: }
271:
272: isc_result_t omapi_listener_connect (omapi_connection_object_t **obj,
273: omapi_listener_object_t *listener,
274: int socket,
275: struct sockaddr_in *remote_addr)
276: {
277: isc_result_t status;
278: omapi_object_t *h = (omapi_object_t *)listener;
279: omapi_addr_t addr;
280:
281: #ifdef DEBUG_PROTOCOL
282: log_debug ("omapi_accept()");
283: #endif
284:
285: /* Get the handle. */
286: status = omapi_connection_allocate (obj, MDL);
287: if (status != ISC_R_SUCCESS)
288: return status;
289:
290: (*obj) -> state = omapi_connection_connected;
291: (*obj) -> remote_addr = *remote_addr;
292: (*obj) -> socket = socket;
293:
294: /* Verify that this host is allowed to connect. */
295: if (listener -> verify_addr) {
296: addr.addrtype = AF_INET;
297: addr.addrlen = sizeof (remote_addr -> sin_addr);
298: memcpy (addr.address, &remote_addr -> sin_addr,
299: sizeof (remote_addr -> sin_addr));
300: addr.port = ntohs(remote_addr -> sin_port);
301:
302: status = (listener -> verify_addr) (h, &addr);
303: if (status != ISC_R_SUCCESS) {
304: omapi_disconnect ((omapi_object_t *)(*obj), 1);
305: omapi_connection_dereference (obj, MDL);
306: return status;
307: }
308: }
309:
310: omapi_listener_reference (&(*obj) -> listener, listener, MDL);
311: #if defined (TRACING)
312: omapi_connection_register (*obj, MDL);
313: #endif
314: status = omapi_signal (h, "connect", (*obj));
315: return status;
316: }
317:
318: #if defined (TRACING)
319: OMAPI_ARRAY_TYPE(omapi_listener, omapi_listener_object_t)
320:
321: void omapi_listener_trace_setup (void) {
322: trace_listener_accept =
323: trace_type_register ("listener-accept", (void *)0,
324: trace_listener_accept_input,
325: trace_listener_accept_stop, MDL);
326: }
327:
328: static void trace_listener_remember (omapi_listener_object_t *obj,
329: const char *file, int line)
330: {
331: isc_result_t status;
332: if (!trace_listeners) {
333: status = omapi_listener_array_allocate (&trace_listeners,
334: file, line);
335: if (status != ISC_R_SUCCESS) {
336: foo:
337: log_error ("trace_listener_remember: %s",
338: isc_result_totext (status));
339: return;
340: }
341: }
342: status = omapi_listener_array_extend (trace_listeners, obj,
343: &obj -> index, MDL);
344: if (status != ISC_R_SUCCESS)
345: goto foo;
346: }
347:
348: static void trace_listener_accept_input (trace_type_t *ttype,
349: unsigned length, char *buf)
350: {
351: struct in_addr *addr;
352: u_int16_t *remote_port;
353: u_int16_t *local_port;
354: omapi_connection_object_t *obj;
355: isc_result_t status;
356: struct sockaddr_in remote_addr;
357:
358: addr = (struct in_addr *)buf;
359: remote_port = (u_int16_t *)(addr + 1);
360: local_port = remote_port + 1;
361:
362: memset (&remote_addr, 0, sizeof remote_addr);
363: remote_addr.sin_addr = *addr;
364: remote_addr.sin_port = *remote_port;
365:
366: omapi_array_foreach_begin (trace_listeners,
367: omapi_listener_object_t, lp) {
368: if (lp -> address.sin_port == *local_port) {
369: obj = (omapi_connection_object_t *)0;
370: status = omapi_listener_connect (&obj,
371: lp, 0, &remote_addr);
372: omapi_listener_dereference (&lp, MDL);
373: return;
374: }
375: } omapi_array_foreach_end (trace_listeners,
376: omapi_listener_object_t, lp);
377: log_error ("trace_listener_accept: %s from %s/%d to port %d",
378: "unexpected connect",
379: inet_ntoa (*addr), *remote_port, *local_port);
380: }
381:
382: static void trace_listener_accept_stop (trace_type_t *ttype) { }
383:
384:
385: #endif
386:
387: isc_result_t omapi_listener_configure_security (omapi_object_t *h,
388: isc_result_t (*verify_addr)
389: (omapi_object_t *,
390: omapi_addr_t *))
391: {
392: omapi_listener_object_t *l;
393:
394: if (h -> type != omapi_type_listener)
395: return ISC_R_INVALIDARG;
396: l = (omapi_listener_object_t *)h;
397:
398: l -> verify_addr = verify_addr;
399:
400: return ISC_R_SUCCESS;
401: }
402:
403: isc_result_t omapi_listener_set_value (omapi_object_t *h,
404: omapi_object_t *id,
405: omapi_data_string_t *name,
406: omapi_typed_data_t *value)
407: {
408: if (h -> type != omapi_type_listener)
409: return ISC_R_INVALIDARG;
410:
411: if (h -> inner && h -> inner -> type -> set_value)
412: return (*(h -> inner -> type -> set_value))
413: (h -> inner, id, name, value);
414: return ISC_R_NOTFOUND;
415: }
416:
417: isc_result_t omapi_listener_get_value (omapi_object_t *h,
418: omapi_object_t *id,
419: omapi_data_string_t *name,
420: omapi_value_t **value)
421: {
422: if (h -> type != omapi_type_listener)
423: return ISC_R_INVALIDARG;
424:
425: if (h -> inner && h -> inner -> type -> get_value)
426: return (*(h -> inner -> type -> get_value))
427: (h -> inner, id, name, value);
428: return ISC_R_NOTFOUND;
429: }
430:
431: isc_result_t omapi_listener_destroy (omapi_object_t *h,
432: const char *file, int line)
433: {
434: omapi_listener_object_t *l;
435:
436: if (h -> type != omapi_type_listener)
437: return ISC_R_INVALIDARG;
438: l = (omapi_listener_object_t *)h;
439:
440: #ifdef DEBUG_PROTOCOL
441: log_debug ("omapi_listener_destroy()");
442: #endif
443:
444: if (l -> socket != -1) {
445: close (l -> socket);
446: l -> socket = -1;
447: }
448: return ISC_R_SUCCESS;
449: }
450:
451: isc_result_t omapi_listener_signal_handler (omapi_object_t *h,
452: const char *name, va_list ap)
453: {
454: if (h -> type != omapi_type_listener)
455: return ISC_R_INVALIDARG;
456:
457: if (h -> inner && h -> inner -> type -> signal_handler)
458: return (*(h -> inner -> type -> signal_handler)) (h -> inner,
459: name, ap);
460: return ISC_R_NOTFOUND;
461: }
462:
463: /* Write all the published values associated with the object through the
464: specified connection. */
465:
466: isc_result_t omapi_listener_stuff_values (omapi_object_t *c,
467: omapi_object_t *id,
468: omapi_object_t *l)
469: {
470: if (l -> type != omapi_type_listener)
471: return ISC_R_INVALIDARG;
472:
473: if (l -> inner && l -> inner -> type -> stuff_values)
474: return (*(l -> inner -> type -> stuff_values)) (c, id,
475: l -> inner);
476: return ISC_R_SUCCESS;
477: }
478:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>