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