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