]> git.ipfire.org Git - thirdparty/dhcp.git/blob - omapip/connection.c
handle ISC_R_FILENOTFOUND now being returned from irs_resconf_load
[thirdparty/dhcp.git] / omapip / connection.c
1 /* connection.c
2
3 Subroutines for dealing with connections. */
4
5 /*
6 * Copyright (c) 2009-2013 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
8 * Copyright (c) 1999-2003 by Internet Software Consortium
9 *
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 *
22 * Internet Systems Consortium, Inc.
23 * 950 Charter Street
24 * Redwood City, CA 94063
25 * <info@isc.org>
26 * https://www.isc.org/
27 *
28 * This software has been written for Internet Systems Consortium
29 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
30 * To learn more about Internet Systems Consortium, see
31 * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
32 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
33 * ``http://www.nominum.com''.
34 */
35
36 #include "dhcpd.h"
37
38 #include <omapip/omapip_p.h>
39 #include <arpa/inet.h>
40 #include <arpa/nameser.h>
41 #include <errno.h>
42
43 #if defined (TRACING)
44 static void trace_connect_input (trace_type_t *, unsigned, char *);
45 static void trace_connect_stop (trace_type_t *);
46 static void trace_disconnect_input (trace_type_t *, unsigned, char *);
47 static void trace_disconnect_stop (trace_type_t *);
48 trace_type_t *trace_connect;
49 trace_type_t *trace_disconnect;
50 extern omapi_array_t *trace_listeners;
51 #endif
52 static isc_result_t omapi_connection_connect_internal (omapi_object_t *);
53
54 OMAPI_OBJECT_ALLOC (omapi_connection,
55 omapi_connection_object_t, omapi_type_connection)
56
57 isc_result_t omapi_connect (omapi_object_t *c,
58 const char *server_name,
59 unsigned port)
60 {
61 struct hostent *he;
62 unsigned i, hix;
63 omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
64 struct in_addr foo;
65 isc_result_t status;
66
67 #ifdef DEBUG_PROTOCOL
68 log_debug ("omapi_connect(%s, port=%d)", server_name, port);
69 #endif
70
71 if (!inet_aton (server_name, &foo)) {
72 /* If we didn't get a numeric address, try for a domain
73 name. It's okay for this call to block. */
74 he = gethostbyname (server_name);
75 if (!he)
76 return DHCP_R_HOSTUNKNOWN;
77 for (i = 0; he -> h_addr_list [i]; i++)
78 ;
79 if (i == 0)
80 return DHCP_R_HOSTUNKNOWN;
81 hix = i;
82
83 status = omapi_addr_list_new (&addrs, hix, MDL);
84 if (status != ISC_R_SUCCESS)
85 return status;
86 for (i = 0; i < hix; i++) {
87 addrs -> addresses [i].addrtype = he -> h_addrtype;
88 addrs -> addresses [i].addrlen = he -> h_length;
89 memcpy (addrs -> addresses [i].address,
90 he -> h_addr_list [i],
91 (unsigned)he -> h_length);
92 addrs -> addresses [i].port = port;
93 }
94 } else {
95 status = omapi_addr_list_new (&addrs, 1, MDL);
96 if (status != ISC_R_SUCCESS)
97 return status;
98 addrs -> addresses [0].addrtype = AF_INET;
99 addrs -> addresses [0].addrlen = sizeof foo;
100 memcpy (addrs -> addresses [0].address, &foo, sizeof foo);
101 addrs -> addresses [0].port = port;
102 }
103 status = omapi_connect_list (c, addrs, (omapi_addr_t *)0);
104 omapi_addr_list_dereference (&addrs, MDL);
105 return status;
106 }
107
108 isc_result_t omapi_connect_list (omapi_object_t *c,
109 omapi_addr_list_t *remote_addrs,
110 omapi_addr_t *local_addr)
111 {
112 isc_result_t status;
113 omapi_connection_object_t *obj;
114 int flag;
115 struct sockaddr_in local_sin;
116
117 obj = (omapi_connection_object_t *)0;
118 status = omapi_connection_allocate (&obj, MDL);
119 if (status != ISC_R_SUCCESS)
120 return status;
121
122 status = omapi_object_reference (&c -> outer, (omapi_object_t *)obj,
123 MDL);
124 if (status != ISC_R_SUCCESS) {
125 omapi_connection_dereference (&obj, MDL);
126 return status;
127 }
128 status = omapi_object_reference (&obj -> inner, c, MDL);
129 if (status != ISC_R_SUCCESS) {
130 omapi_connection_dereference (&obj, MDL);
131 return status;
132 }
133
134 /* Store the address list on the object. */
135 omapi_addr_list_reference (&obj -> connect_list, remote_addrs, MDL);
136 obj -> cptr = 0;
137 obj -> state = omapi_connection_unconnected;
138
139 #if defined (TRACING)
140 /* If we're playing back, don't actually try to connect - just leave
141 the object available for a subsequent connect or disconnect. */
142 if (!trace_playback ()) {
143 #endif
144 /* Create a socket on which to communicate. */
145 obj -> socket =
146 socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
147 if (obj -> socket < 0) {
148 omapi_connection_dereference (&obj, MDL);
149 if (errno == EMFILE || errno == ENFILE
150 || errno == ENOBUFS)
151 return ISC_R_NORESOURCES;
152 return ISC_R_UNEXPECTED;
153 }
154
155 /* Set up the local address, if any. */
156 if (local_addr) {
157 /* Only do TCPv4 so far. */
158 if (local_addr -> addrtype != AF_INET) {
159 omapi_connection_dereference (&obj, MDL);
160 return DHCP_R_INVALIDARG;
161 }
162 local_sin.sin_port = htons (local_addr -> port);
163 memcpy (&local_sin.sin_addr,
164 local_addr -> address,
165 local_addr -> addrlen);
166 #if defined (HAVE_SA_LEN)
167 local_sin.sin_len = sizeof local_addr;
168 #endif
169 local_sin.sin_family = AF_INET;
170 memset (&local_sin.sin_zero, 0,
171 sizeof local_sin.sin_zero);
172
173 if (bind (obj -> socket, (struct sockaddr *)&local_sin,
174 sizeof local_sin) < 0) {
175 omapi_connection_object_t **objp = &obj;
176 omapi_object_t **o = (omapi_object_t **)objp;
177 omapi_object_dereference(o, MDL);
178 if (errno == EADDRINUSE)
179 return ISC_R_ADDRINUSE;
180 if (errno == EADDRNOTAVAIL)
181 return ISC_R_ADDRNOTAVAIL;
182 if (errno == EACCES)
183 return ISC_R_NOPERM;
184 return ISC_R_UNEXPECTED;
185 }
186 obj -> local_addr = local_sin;
187 }
188
189 #if defined(F_SETFD)
190 if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
191 close (obj -> socket);
192 omapi_connection_dereference (&obj, MDL);
193 return ISC_R_UNEXPECTED;
194 }
195 #endif
196
197 /* Set the SO_REUSEADDR flag (this should not fail). */
198 flag = 1;
199 if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR,
200 (char *)&flag, sizeof flag) < 0) {
201 omapi_connection_dereference (&obj, MDL);
202 return ISC_R_UNEXPECTED;
203 }
204
205 /* Set the file to nonblocking mode. */
206 if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
207 omapi_connection_dereference (&obj, MDL);
208 return ISC_R_UNEXPECTED;
209 }
210
211 #ifdef SO_NOSIGPIPE
212 /*
213 * If available stop the OS from killing our
214 * program on a SIGPIPE failure
215 */
216 flag = 1;
217 if (setsockopt(obj->socket, SOL_SOCKET, SO_NOSIGPIPE,
218 (char *)&flag, sizeof(flag)) < 0) {
219 omapi_connection_dereference (&obj, MDL);
220 return ISC_R_UNEXPECTED;
221 }
222 #endif
223
224 status = (omapi_register_io_object
225 ((omapi_object_t *)obj,
226 0, omapi_connection_writefd,
227 0, omapi_connection_connect,
228 omapi_connection_reaper));
229 if (status != ISC_R_SUCCESS)
230 goto out;
231 status = omapi_connection_connect_internal ((omapi_object_t *)
232 obj);
233 /*
234 * inprogress is the same as success but used
235 * to indicate to the dispatch code that we should
236 * mark the socket as requiring more attention.
237 * Routines calling this function should handle
238 * success properly.
239 */
240 if (status == ISC_R_INPROGRESS) {
241 status = ISC_R_SUCCESS;
242 }
243 #if defined (TRACING)
244 }
245 omapi_connection_register (obj, MDL);
246 #endif
247
248 out:
249 omapi_connection_dereference (&obj, MDL);
250 return status;
251 }
252
253 #if defined (TRACING)
254 omapi_array_t *omapi_connections;
255
256 OMAPI_ARRAY_TYPE(omapi_connection, omapi_connection_object_t)
257
258 void omapi_connection_trace_setup (void) {
259 trace_connect = trace_type_register ("connect", (void *)0,
260 trace_connect_input,
261 trace_connect_stop, MDL);
262 trace_disconnect = trace_type_register ("disconnect", (void *)0,
263 trace_disconnect_input,
264 trace_disconnect_stop, MDL);
265 }
266
267 void omapi_connection_register (omapi_connection_object_t *obj,
268 const char *file, int line)
269 {
270 isc_result_t status;
271 trace_iov_t iov [6];
272 int iov_count = 0;
273 int32_t connect_index, listener_index;
274 static int32_t index;
275
276 if (!omapi_connections) {
277 status = omapi_connection_array_allocate (&omapi_connections,
278 file, line);
279 if (status != ISC_R_SUCCESS)
280 return;
281 }
282
283 status = omapi_connection_array_extend (omapi_connections, obj,
284 (int *)0, file, line);
285 if (status != ISC_R_SUCCESS) {
286 obj -> index = -1;
287 return;
288 }
289
290 #if defined (TRACING)
291 if (trace_record ()) {
292 /* Connection registration packet:
293
294 int32_t index
295 int32_t listener_index [-1 means no listener]
296 u_int16_t remote_port
297 u_int16_t local_port
298 u_int32_t remote_addr
299 u_int32_t local_addr */
300
301 connect_index = htonl (index);
302 index++;
303 if (obj -> listener)
304 listener_index = htonl (obj -> listener -> index);
305 else
306 listener_index = htonl (-1);
307 iov [iov_count].buf = (char *)&connect_index;
308 iov [iov_count++].len = sizeof connect_index;
309 iov [iov_count].buf = (char *)&listener_index;
310 iov [iov_count++].len = sizeof listener_index;
311 iov [iov_count].buf = (char *)&obj -> remote_addr.sin_port;
312 iov [iov_count++].len = sizeof obj -> remote_addr.sin_port;
313 iov [iov_count].buf = (char *)&obj -> local_addr.sin_port;
314 iov [iov_count++].len = sizeof obj -> local_addr.sin_port;
315 iov [iov_count].buf = (char *)&obj -> remote_addr.sin_addr;
316 iov [iov_count++].len = sizeof obj -> remote_addr.sin_addr;
317 iov [iov_count].buf = (char *)&obj -> local_addr.sin_addr;
318 iov [iov_count++].len = sizeof obj -> local_addr.sin_addr;
319
320 status = trace_write_packet_iov (trace_connect,
321 iov_count, iov, file, line);
322 }
323 #endif
324 }
325
326 static void trace_connect_input (trace_type_t *ttype,
327 unsigned length, char *buf)
328 {
329 struct sockaddr_in remote, local;
330 int32_t connect_index, listener_index;
331 char *s = buf;
332 omapi_connection_object_t *obj;
333 isc_result_t status;
334 int i;
335
336 if (length != ((sizeof connect_index) +
337 (sizeof remote.sin_port) +
338 (sizeof remote.sin_addr)) * 2) {
339 log_error ("Trace connect: invalid length %d", length);
340 return;
341 }
342
343 memset (&remote, 0, sizeof remote);
344 memset (&local, 0, sizeof local);
345 memcpy (&connect_index, s, sizeof connect_index);
346 s += sizeof connect_index;
347 memcpy (&listener_index, s, sizeof listener_index);
348 s += sizeof listener_index;
349 memcpy (&remote.sin_port, s, sizeof remote.sin_port);
350 s += sizeof remote.sin_port;
351 memcpy (&local.sin_port, s, sizeof local.sin_port);
352 s += sizeof local.sin_port;
353 memcpy (&remote.sin_addr, s, sizeof remote.sin_addr);
354 s += sizeof remote.sin_addr;
355 memcpy (&local.sin_addr, s, sizeof local.sin_addr);
356 s += sizeof local.sin_addr;
357 POST(s);
358
359 connect_index = ntohl (connect_index);
360 listener_index = ntohl (listener_index);
361
362 /* If this was a connect to a listener, then we just slap together
363 a new connection. */
364 if (listener_index != -1) {
365 omapi_listener_object_t *listener;
366 listener = (omapi_listener_object_t *)0;
367 omapi_array_foreach_begin (trace_listeners,
368 omapi_listener_object_t, lp) {
369 if (lp -> address.sin_port == local.sin_port) {
370 omapi_listener_reference (&listener, lp, MDL);
371 omapi_listener_dereference (&lp, MDL);
372 break;
373 }
374 } omapi_array_foreach_end (trace_listeners,
375 omapi_listener_object_t, lp);
376 if (!listener) {
377 log_error ("%s%ld, addr %s, port %d",
378 "Spurious traced listener connect - index ",
379 (long int)listener_index,
380 inet_ntoa (local.sin_addr),
381 ntohs (local.sin_port));
382 return;
383 }
384 obj = (omapi_connection_object_t *)0;
385 status = omapi_listener_connect (&obj, listener, -1, &remote);
386 if (status != ISC_R_SUCCESS) {
387 log_error ("traced listener connect: %s",
388 isc_result_totext (status));
389 }
390 if (obj)
391 omapi_connection_dereference (&obj, MDL);
392 omapi_listener_dereference (&listener, MDL);
393 return;
394 }
395
396 /* Find the matching connect object, if there is one. */
397 omapi_array_foreach_begin (omapi_connections,
398 omapi_connection_object_t, lp) {
399 for (i = 0; (lp->connect_list &&
400 i < lp->connect_list->count); i++) {
401 if (!memcmp (&remote.sin_addr,
402 &lp->connect_list->addresses[i].address,
403 sizeof remote.sin_addr) &&
404 (ntohs (remote.sin_port) ==
405 lp->connect_list->addresses[i].port)) {
406 lp->state = omapi_connection_connected;
407 lp->remote_addr = remote;
408 lp->remote_addr.sin_family = AF_INET;
409 omapi_addr_list_dereference(&lp->connect_list, MDL);
410 lp->index = connect_index;
411 status = omapi_signal_in((omapi_object_t *)lp,
412 "connect");
413 omapi_connection_dereference (&lp, MDL);
414 return;
415 }
416 }
417 } omapi_array_foreach_end (omapi_connections,
418 omapi_connection_object_t, lp);
419
420 log_error ("Spurious traced connect - index %ld, addr %s, port %d",
421 (long int)connect_index, inet_ntoa (remote.sin_addr),
422 ntohs (remote.sin_port));
423 return;
424 }
425
426 static void trace_connect_stop (trace_type_t *ttype) { }
427
428 static void trace_disconnect_input (trace_type_t *ttype,
429 unsigned length, char *buf)
430 {
431 int32_t *index;
432 if (length != sizeof *index) {
433 log_error ("trace disconnect: wrong length %d", length);
434 return;
435 }
436
437 index = (int32_t *)buf;
438
439 omapi_array_foreach_begin (omapi_connections,
440 omapi_connection_object_t, lp) {
441 if (lp -> index == ntohl (*index)) {
442 omapi_disconnect ((omapi_object_t *)lp, 1);
443 omapi_connection_dereference (&lp, MDL);
444 return;
445 }
446 } omapi_array_foreach_end (omapi_connections,
447 omapi_connection_object_t, lp);
448
449 log_error ("trace disconnect: no connection matching index %ld",
450 (long int)ntohl (*index));
451 }
452
453 static void trace_disconnect_stop (trace_type_t *ttype) { }
454 #endif
455
456 /* Disconnect a connection object from the remote end. If force is nonzero,
457 close the connection immediately. Otherwise, shut down the receiving end
458 but allow any unsent data to be sent before actually closing the socket. */
459
460 isc_result_t omapi_disconnect (omapi_object_t *h,
461 int force)
462 {
463 omapi_connection_object_t *c;
464
465 #ifdef DEBUG_PROTOCOL
466 log_debug ("omapi_disconnect(%s)", force ? "force" : "");
467 #endif
468
469 c = (omapi_connection_object_t *)h;
470 if (c -> type != omapi_type_connection)
471 return DHCP_R_INVALIDARG;
472
473 #if defined (TRACING)
474 if (trace_record ()) {
475 isc_result_t status;
476 int32_t index;
477
478 index = htonl (c -> index);
479 status = trace_write_packet (trace_disconnect,
480 sizeof index, (char *)&index,
481 MDL);
482 if (status != ISC_R_SUCCESS) {
483 trace_stop ();
484 log_error ("trace_write_packet: %s",
485 isc_result_totext (status));
486 }
487 }
488 if (!trace_playback ()) {
489 #endif
490 if (!force) {
491 /* If we're already disconnecting, we don't have to do
492 anything. */
493 if (c -> state == omapi_connection_disconnecting)
494 return ISC_R_SUCCESS;
495
496 /* Try to shut down the socket - this sends a FIN to
497 the remote end, so that it won't send us any more
498 data. If the shutdown succeeds, and we still
499 have bytes left to write, defer closing the socket
500 until that's done. */
501 if (!shutdown (c -> socket, SHUT_RD)) {
502 if (c -> out_bytes > 0) {
503 c -> state =
504 omapi_connection_disconnecting;
505 return ISC_R_SUCCESS;
506 }
507 }
508 }
509 close (c -> socket);
510 #if defined (TRACING)
511 }
512 #endif
513 c -> state = omapi_connection_closed;
514
515 #if 0
516 /*
517 * Disconnecting from the I/O object seems incorrect as it doesn't
518 * cause the I/O object to be cleaned and released. Previous to
519 * using the isc socket library this wouldn't have caused a problem
520 * with the socket library we would have a reference to a closed
521 * socket. Instead we now do an unregister to properly free the
522 * I/O object.
523 */
524
525 /* Disconnect from I/O object, if any. */
526 if (h -> outer) {
527 if (h -> outer -> inner)
528 omapi_object_dereference (&h -> outer -> inner, MDL);
529 omapi_object_dereference (&h -> outer, MDL);
530 }
531 #else
532 if (h->outer) {
533 omapi_unregister_io_object(h);
534 }
535 #endif
536
537 /* If whatever created us registered a signal handler, send it
538 a disconnect signal. */
539 omapi_signal (h, "disconnect", h);
540
541 /* Disconnect from protocol object, if any. */
542 if (h->inner != NULL) {
543 if (h->inner->outer != NULL) {
544 omapi_object_dereference(&h->inner->outer, MDL);
545 }
546 omapi_object_dereference(&h->inner, MDL);
547 }
548
549 /* XXX: the code to free buffers should be in the dereference
550 function, but there is no special-purpose function to
551 dereference connections, so these just get leaked */
552 /* Free any buffers */
553 if (c->inbufs != NULL) {
554 omapi_buffer_dereference(&c->inbufs, MDL);
555 }
556 c->in_bytes = 0;
557 if (c->outbufs != NULL) {
558 omapi_buffer_dereference(&c->outbufs, MDL);
559 }
560 c->out_bytes = 0;
561
562 return ISC_R_SUCCESS;
563 }
564
565 isc_result_t omapi_connection_require (omapi_object_t *h, unsigned bytes)
566 {
567 omapi_connection_object_t *c;
568
569 if (h -> type != omapi_type_connection)
570 return DHCP_R_INVALIDARG;
571 c = (omapi_connection_object_t *)h;
572
573 c -> bytes_needed = bytes;
574 if (c -> bytes_needed <= c -> in_bytes) {
575 return ISC_R_SUCCESS;
576 }
577 return DHCP_R_NOTYET;
578 }
579
580 /* Return the socket on which the dispatcher should wait for readiness
581 to read, for a connection object. */
582 int omapi_connection_readfd (omapi_object_t *h)
583 {
584 omapi_connection_object_t *c;
585 if (h -> type != omapi_type_connection)
586 return -1;
587 c = (omapi_connection_object_t *)h;
588 if (c -> state != omapi_connection_connected)
589 return -1;
590 return c -> socket;
591 }
592
593 /*
594 * Return the socket on which the dispatcher should wait for readiness
595 * to write, for a connection object. When bytes are buffered we should
596 * also poke the dispatcher to tell it to start or re-start watching the
597 * socket.
598 */
599 int omapi_connection_writefd (omapi_object_t *h)
600 {
601 omapi_connection_object_t *c;
602 if (h -> type != omapi_type_connection)
603 return -1;
604 c = (omapi_connection_object_t *)h;
605 return c->socket;
606 }
607
608 isc_result_t omapi_connection_connect (omapi_object_t *h)
609 {
610 isc_result_t status;
611
612 /*
613 * We use the INPROGRESS status to indicate that
614 * we want more from the socket. In this case we
615 * have now connected and are trying to write to
616 * the socket for the first time. For the signaling
617 * code this is the same as a SUCCESS so we don't
618 * pass it on as a signal.
619 */
620 status = omapi_connection_connect_internal (h);
621 if (status == ISC_R_INPROGRESS)
622 return ISC_R_INPROGRESS;
623
624 if (status != ISC_R_SUCCESS)
625 omapi_signal (h, "status", status);
626
627 return ISC_R_SUCCESS;
628 }
629
630 static isc_result_t omapi_connection_connect_internal (omapi_object_t *h)
631 {
632 int error = 0;
633 omapi_connection_object_t *c;
634 socklen_t sl;
635 isc_result_t status;
636
637 if (h -> type != omapi_type_connection)
638 return DHCP_R_INVALIDARG;
639 c = (omapi_connection_object_t *)h;
640
641 if (c -> state == omapi_connection_connecting) {
642 sl = sizeof error;
643 if (getsockopt (c -> socket, SOL_SOCKET, SO_ERROR,
644 (char *)&error, &sl) < 0) {
645 omapi_disconnect (h, 1);
646 return ISC_R_SUCCESS;
647 }
648 if (!error)
649 c -> state = omapi_connection_connected;
650 }
651 if (c -> state == omapi_connection_connecting ||
652 c -> state == omapi_connection_unconnected) {
653 if (c -> cptr >= c -> connect_list -> count) {
654 switch (error) {
655 case ECONNREFUSED:
656 status = ISC_R_CONNREFUSED;
657 break;
658 case ENETUNREACH:
659 status = ISC_R_NETUNREACH;
660 break;
661 default:
662 status = uerr2isc (error);
663 break;
664 }
665 omapi_disconnect (h, 1);
666 return status;
667 }
668
669 if (c -> connect_list -> addresses [c -> cptr].addrtype !=
670 AF_INET) {
671 omapi_disconnect (h, 1);
672 return DHCP_R_INVALIDARG;
673 }
674
675 memcpy (&c -> remote_addr.sin_addr,
676 &c -> connect_list -> addresses [c -> cptr].address,
677 sizeof c -> remote_addr.sin_addr);
678 c -> remote_addr.sin_family = AF_INET;
679 c -> remote_addr.sin_port =
680 htons (c -> connect_list -> addresses [c -> cptr].port);
681 #if defined (HAVE_SA_LEN)
682 c -> remote_addr.sin_len = sizeof c -> remote_addr;
683 #endif
684 memset (&c -> remote_addr.sin_zero, 0,
685 sizeof c -> remote_addr.sin_zero);
686 ++c -> cptr;
687
688 error = connect (c -> socket,
689 (struct sockaddr *)&c -> remote_addr,
690 sizeof c -> remote_addr);
691 if (error < 0) {
692 error = errno;
693 if (error != EINPROGRESS) {
694 omapi_disconnect (h, 1);
695 switch (error) {
696 case ECONNREFUSED:
697 status = ISC_R_CONNREFUSED;
698 break;
699 case ENETUNREACH:
700 status = ISC_R_NETUNREACH;
701 break;
702 default:
703 status = uerr2isc (error);
704 break;
705 }
706 return status;
707 }
708 c -> state = omapi_connection_connecting;
709 return DHCP_R_INCOMPLETE;
710 }
711 c -> state = omapi_connection_connected;
712 }
713
714 /* I don't know why this would fail, so I'm tempted not to test
715 the return value. */
716 sl = sizeof (c -> local_addr);
717 if (getsockname (c -> socket,
718 (struct sockaddr *)&c -> local_addr, &sl) < 0) {
719 }
720
721 /* Reregister with the I/O object. If we don't already have an
722 I/O object this turns into a register call, otherwise we simply
723 modify the pointers in the I/O object. */
724
725 status = omapi_reregister_io_object (h,
726 omapi_connection_readfd,
727 omapi_connection_writefd,
728 omapi_connection_reader,
729 omapi_connection_writer,
730 omapi_connection_reaper);
731
732 if (status != ISC_R_SUCCESS) {
733 omapi_disconnect (h, 1);
734 return status;
735 }
736
737 omapi_signal_in (h, "connect");
738 omapi_addr_list_dereference (&c -> connect_list, MDL);
739 return ISC_R_INPROGRESS;
740 }
741
742 /* Reaper function for connection - if the connection is completely closed,
743 reap it. If it's in the disconnecting state, there were bytes left
744 to write when the user closed it, so if there are now no bytes left to
745 write, we can close it. */
746 isc_result_t omapi_connection_reaper (omapi_object_t *h)
747 {
748 omapi_connection_object_t *c;
749
750 if (h -> type != omapi_type_connection)
751 return DHCP_R_INVALIDARG;
752
753 c = (omapi_connection_object_t *)h;
754 if (c -> state == omapi_connection_disconnecting &&
755 c -> out_bytes == 0) {
756 #ifdef DEBUG_PROTOCOL
757 log_debug ("omapi_connection_reaper(): disconnect");
758 #endif
759 omapi_disconnect (h, 1);
760 }
761 if (c -> state == omapi_connection_closed) {
762 #ifdef DEBUG_PROTOCOL
763 log_debug ("omapi_connection_reaper(): closed");
764 #endif
765 return ISC_R_NOTCONNECTED;
766 }
767 return ISC_R_SUCCESS;
768 }
769
770 static isc_result_t make_dst_key (dst_key_t **dst_key, omapi_object_t *a) {
771 omapi_value_t *name = (omapi_value_t *)0;
772 omapi_value_t *algorithm = (omapi_value_t *)0;
773 omapi_value_t *key = (omapi_value_t *)0;
774 char *name_str = NULL;
775 isc_result_t status = ISC_R_SUCCESS;
776
777 if (status == ISC_R_SUCCESS)
778 status = omapi_get_value_str
779 (a, (omapi_object_t *)0, "name", &name);
780
781 if (status == ISC_R_SUCCESS)
782 status = omapi_get_value_str
783 (a, (omapi_object_t *)0, "algorithm", &algorithm);
784
785 if (status == ISC_R_SUCCESS)
786 status = omapi_get_value_str
787 (a, (omapi_object_t *)0, "key", &key);
788
789 if (status == ISC_R_SUCCESS) {
790 if ((algorithm->value->type != omapi_datatype_data &&
791 algorithm->value->type != omapi_datatype_string) ||
792 strncasecmp((char *)algorithm->value->u.buffer.value,
793 NS_TSIG_ALG_HMAC_MD5 ".",
794 algorithm->value->u.buffer.len) != 0) {
795 status = DHCP_R_INVALIDARG;
796 }
797 }
798
799 if (status == ISC_R_SUCCESS) {
800 name_str = dmalloc (name -> value -> u.buffer.len + 1, MDL);
801 if (!name_str)
802 status = ISC_R_NOMEMORY;
803 }
804
805 if (status == ISC_R_SUCCESS) {
806 memcpy (name_str,
807 name -> value -> u.buffer.value,
808 name -> value -> u.buffer.len);
809 name_str [name -> value -> u.buffer.len] = 0;
810
811 status = isclib_make_dst_key(name_str,
812 DHCP_HMAC_MD5_NAME,
813 key->value->u.buffer.value,
814 key->value->u.buffer.len,
815 dst_key);
816
817 if (*dst_key == NULL)
818 status = ISC_R_NOMEMORY;
819 }
820
821 if (name_str)
822 dfree (name_str, MDL);
823 if (key)
824 omapi_value_dereference (&key, MDL);
825 if (algorithm)
826 omapi_value_dereference (&algorithm, MDL);
827 if (name)
828 omapi_value_dereference (&name, MDL);
829
830 return status;
831 }
832
833 isc_result_t omapi_connection_sign_data (int mode,
834 dst_key_t *key,
835 void **context,
836 const unsigned char *data,
837 const unsigned len,
838 omapi_typed_data_t **result)
839 {
840 omapi_typed_data_t *td = (omapi_typed_data_t *)0;
841 isc_result_t status;
842 dst_context_t **dctx = (dst_context_t **)context;
843
844 /* Create the context for the dst module */
845 if (mode & SIG_MODE_INIT) {
846 status = dst_context_create(key, dhcp_gbl_ctx.mctx, dctx);
847 if (status != ISC_R_SUCCESS) {
848 return status;
849 }
850 }
851
852 /* If we have any data add it to the context */
853 if (len != 0) {
854 isc_region_t region;
855 region.base = (unsigned char *)data;
856 region.length = len;
857 dst_context_adddata(*dctx, &region);
858 }
859
860 /* Finish the signature and clean up the context */
861 if (mode & SIG_MODE_FINAL) {
862 unsigned int sigsize;
863 isc_buffer_t sigbuf;
864
865 status = dst_key_sigsize(key, &sigsize);
866 if (status != ISC_R_SUCCESS) {
867 goto cleanup;
868 }
869
870 status = omapi_typed_data_new (MDL, &td,
871 omapi_datatype_data,
872 sigsize);
873 if (status != ISC_R_SUCCESS) {
874 goto cleanup;
875 }
876
877 isc_buffer_init(&sigbuf, td->u.buffer.value, td->u.buffer.len);
878 status = dst_context_sign(*dctx, &sigbuf);
879 if (status != ISC_R_SUCCESS) {
880 goto cleanup;
881 }
882
883 if (result) {
884 omapi_typed_data_reference (result, td, MDL);
885 }
886
887 cleanup:
888 /* We are done with the context and the td. On success
889 * the td is now referenced from result, on failure we
890 * don't need it any more */
891 if (td) {
892 omapi_typed_data_dereference (&td, MDL);
893 }
894 dst_context_destroy(dctx);
895 return status;
896 }
897
898 return ISC_R_SUCCESS;
899 }
900
901 isc_result_t omapi_connection_output_auth_length (omapi_object_t *h,
902 unsigned *l)
903 {
904 omapi_connection_object_t *c;
905
906 if (h->type != omapi_type_connection)
907 return DHCP_R_INVALIDARG;
908 c = (omapi_connection_object_t *)h;
909
910 if (c->out_key == NULL)
911 return ISC_R_NOTFOUND;
912
913 return(dst_key_sigsize(c->out_key, l));
914 }
915
916 isc_result_t omapi_connection_set_value (omapi_object_t *h,
917 omapi_object_t *id,
918 omapi_data_string_t *name,
919 omapi_typed_data_t *value)
920 {
921 omapi_connection_object_t *c;
922 isc_result_t status;
923
924 if (h -> type != omapi_type_connection)
925 return DHCP_R_INVALIDARG;
926 c = (omapi_connection_object_t *)h;
927
928 if (omapi_ds_strcmp (name, "input-authenticator") == 0) {
929 if (value && value -> type != omapi_datatype_object)
930 return DHCP_R_INVALIDARG;
931
932 if (c -> in_context) {
933 omapi_connection_sign_data (SIG_MODE_FINAL,
934 c -> in_key,
935 &c -> in_context,
936 0, 0,
937 (omapi_typed_data_t **) 0);
938 }
939
940 if (c->in_key != NULL) {
941 dst_key_free(&c->in_key);
942 }
943
944 if (value) {
945 status = make_dst_key (&c -> in_key,
946 value -> u.object);
947 if (status != ISC_R_SUCCESS)
948 return status;
949 }
950
951 return ISC_R_SUCCESS;
952 }
953 else if (omapi_ds_strcmp (name, "output-authenticator") == 0) {
954 if (value && value -> type != omapi_datatype_object)
955 return DHCP_R_INVALIDARG;
956
957 if (c -> out_context) {
958 omapi_connection_sign_data (SIG_MODE_FINAL,
959 c -> out_key,
960 &c -> out_context,
961 0, 0,
962 (omapi_typed_data_t **) 0);
963 }
964
965 if (c->out_key != NULL) {
966 dst_key_free(&c->out_key);
967 }
968
969 if (value) {
970 status = make_dst_key (&c -> out_key,
971 value -> u.object);
972 if (status != ISC_R_SUCCESS)
973 return status;
974 }
975
976 return ISC_R_SUCCESS;
977 }
978
979 if (h -> inner && h -> inner -> type -> set_value)
980 return (*(h -> inner -> type -> set_value))
981 (h -> inner, id, name, value);
982 return ISC_R_NOTFOUND;
983 }
984
985 isc_result_t omapi_connection_get_value (omapi_object_t *h,
986 omapi_object_t *id,
987 omapi_data_string_t *name,
988 omapi_value_t **value)
989 {
990 omapi_connection_object_t *c;
991 omapi_typed_data_t *td = (omapi_typed_data_t *)0;
992 isc_result_t status;
993 unsigned int sigsize;
994
995 if (h -> type != omapi_type_connection)
996 return DHCP_R_INVALIDARG;
997 c = (omapi_connection_object_t *)h;
998
999 if (omapi_ds_strcmp (name, "input-signature") == 0) {
1000 if (!c -> in_key || !c -> in_context)
1001 return ISC_R_NOTFOUND;
1002
1003 status = omapi_connection_sign_data (SIG_MODE_FINAL,
1004 c -> in_key,
1005 &c -> in_context,
1006 0, 0, &td);
1007 if (status != ISC_R_SUCCESS)
1008 return status;
1009
1010 status = omapi_make_value (value, name, td, MDL);
1011 omapi_typed_data_dereference (&td, MDL);
1012 return status;
1013
1014 } else if (omapi_ds_strcmp (name, "input-signature-size") == 0) {
1015 if (c->in_key == NULL)
1016 return ISC_R_NOTFOUND;
1017
1018 status = dst_key_sigsize(c->in_key, &sigsize);
1019 if (status != ISC_R_SUCCESS) {
1020 return(status);
1021 }
1022
1023 return omapi_make_int_value(value, name, sigsize, MDL);
1024
1025 } else if (omapi_ds_strcmp (name, "output-signature") == 0) {
1026 if (!c -> out_key || !c -> out_context)
1027 return ISC_R_NOTFOUND;
1028
1029 status = omapi_connection_sign_data (SIG_MODE_FINAL,
1030 c -> out_key,
1031 &c -> out_context,
1032 0, 0, &td);
1033 if (status != ISC_R_SUCCESS)
1034 return status;
1035
1036 status = omapi_make_value (value, name, td, MDL);
1037 omapi_typed_data_dereference (&td, MDL);
1038 return status;
1039
1040 } else if (omapi_ds_strcmp (name, "output-signature-size") == 0) {
1041 if (c->out_key == NULL)
1042 return ISC_R_NOTFOUND;
1043
1044
1045 status = dst_key_sigsize(c->out_key, &sigsize);
1046 if (status != ISC_R_SUCCESS) {
1047 return(status);
1048 }
1049
1050 return omapi_make_int_value(value, name, sigsize, MDL);
1051 }
1052
1053 if (h -> inner && h -> inner -> type -> get_value)
1054 return (*(h -> inner -> type -> get_value))
1055 (h -> inner, id, name, value);
1056 return ISC_R_NOTFOUND;
1057 }
1058
1059 isc_result_t omapi_connection_destroy (omapi_object_t *h,
1060 const char *file, int line)
1061 {
1062 omapi_connection_object_t *c;
1063
1064 #ifdef DEBUG_PROTOCOL
1065 log_debug ("omapi_connection_destroy()");
1066 #endif
1067
1068 if (h -> type != omapi_type_connection)
1069 return ISC_R_UNEXPECTED;
1070 c = (omapi_connection_object_t *)(h);
1071 if (c -> state == omapi_connection_connected)
1072 omapi_disconnect (h, 1);
1073 if (c -> listener)
1074 omapi_listener_dereference (&c -> listener, file, line);
1075 if (c -> connect_list)
1076 omapi_addr_list_dereference (&c -> connect_list, file, line);
1077 return ISC_R_SUCCESS;
1078 }
1079
1080 isc_result_t omapi_connection_signal_handler (omapi_object_t *h,
1081 const char *name, va_list ap)
1082 {
1083 if (h -> type != omapi_type_connection)
1084 return DHCP_R_INVALIDARG;
1085
1086 #ifdef DEBUG_PROTOCOL
1087 log_debug ("omapi_connection_signal_handler(%s)", name);
1088 #endif
1089
1090 if (h -> inner && h -> inner -> type -> signal_handler)
1091 return (*(h -> inner -> type -> signal_handler)) (h -> inner,
1092 name, ap);
1093 return ISC_R_NOTFOUND;
1094 }
1095
1096 /* Write all the published values associated with the object through the
1097 specified connection. */
1098
1099 isc_result_t omapi_connection_stuff_values (omapi_object_t *c,
1100 omapi_object_t *id,
1101 omapi_object_t *m)
1102 {
1103 if (m -> type != omapi_type_connection)
1104 return DHCP_R_INVALIDARG;
1105
1106 if (m -> inner && m -> inner -> type -> stuff_values)
1107 return (*(m -> inner -> type -> stuff_values)) (c, id,
1108 m -> inner);
1109 return ISC_R_SUCCESS;
1110 }