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