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