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