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