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