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