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