"Inital commit to Gerrit"
[profile/ivi/dhcp.git] / omapip / listener.c
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 DHCP_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 DHCP_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 DHCP_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 DHCP_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 DHCP_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 DHCP_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 DHCP_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 DHCP_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