- Hide the following characters '(', ')', '{', '}' inside of #defines
[dragonfly.git] / contrib / dhcp-3.0 / omapip / connection.c
1 /* connection.c
2
3    Subroutines for dealing with connections. */
4
5 /*
6  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7  * Copyright (c) 1999-2003 by Internet Software Consortium
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
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  *   http://www.isc.org/
26  *
27  * This software has been written for Internet Systems Consortium
28  * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29  * To learn more about Internet Systems Consortium, see
30  * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
31  * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
32  * ``http://www.nominum.com''.
33  */
34
35 #include <omapip/omapip_p.h>
36 #include <arpa/inet.h>
37 #include <arpa/nameser.h>
38
39
40 #if defined (TRACING)
41 static void trace_connect_input (trace_type_t *, unsigned, char *);
42 static void trace_connect_stop (trace_type_t *);
43 static void trace_disconnect_input (trace_type_t *, unsigned, char *);
44 static void trace_disconnect_stop (trace_type_t *);
45 trace_type_t *trace_connect;
46 trace_type_t *trace_disconnect;
47 extern omapi_array_t *trace_listeners;
48 #endif
49 static isc_result_t omapi_connection_connect_internal (omapi_object_t *);
50
51 OMAPI_OBJECT_ALLOC (omapi_connection,
52                     omapi_connection_object_t, omapi_type_connection)
53
54 isc_result_t omapi_connect (omapi_object_t *c,
55                             const char *server_name,
56                             unsigned port)
57 {
58         struct hostent *he;
59         unsigned i, hix;
60         omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
61         struct in_addr foo;
62         isc_result_t status;
63
64 #ifdef DEBUG_PROTOCOL
65         log_debug ("omapi_connect(%s, port=%d)", server_name, port);
66 #endif
67
68         if (!inet_aton (server_name, &foo)) {
69                 /* If we didn't get a numeric address, try for a domain
70                    name.  It's okay for this call to block. */
71                 he = gethostbyname (server_name);
72                 if (!he)
73                         return ISC_R_HOSTUNKNOWN;
74                 for (i = 0; he -> h_addr_list [i]; i++)
75                         ;
76                 if (i == 0)
77                         return ISC_R_HOSTUNKNOWN;
78                 hix = i;
79
80                 status = omapi_addr_list_new (&addrs, hix, MDL);
81                 if (status != ISC_R_SUCCESS)
82                         return status;
83                 for (i = 0; i < hix; i++) {
84                         addrs -> addresses [i].addrtype = he -> h_addrtype;
85                         addrs -> addresses [i].addrlen = he -> h_length;
86                         memcpy (addrs -> addresses [i].address,
87                                 he -> h_addr_list [i],
88                                 (unsigned)he -> h_length);
89                         addrs -> addresses [i].port = port;
90                 }
91         } else {
92                 status = omapi_addr_list_new (&addrs, 1, MDL);
93                 if (status != ISC_R_SUCCESS)
94                         return status;
95                 addrs -> addresses [0].addrtype = AF_INET;
96                 addrs -> addresses [0].addrlen = sizeof foo;
97                 memcpy (addrs -> addresses [0].address, &foo, sizeof foo);
98                 addrs -> addresses [0].port = port;
99                 hix = 1;
100         }
101         status = omapi_connect_list (c, addrs, (omapi_addr_t *)0);
102         omapi_addr_list_dereference (&addrs, MDL);
103         return status;
104 }
105
106 isc_result_t omapi_connect_list (omapi_object_t *c,
107                                  omapi_addr_list_t *remote_addrs,
108                                  omapi_addr_t *local_addr)
109 {
110         isc_result_t status;
111         omapi_connection_object_t *obj;
112         int flag;
113         struct sockaddr_in local_sin;
114 #if defined (TRACING)
115         trace_addr_t *addrs;
116         u_int16_t naddrs;
117 #endif
118
119         obj = (omapi_connection_object_t *)0;
120         status = omapi_connection_allocate (&obj, MDL);
121         if (status != ISC_R_SUCCESS)
122                 return status;
123
124         status = omapi_object_reference (&c -> outer, (omapi_object_t *)obj,
125                                          MDL);
126         if (status != ISC_R_SUCCESS) {
127                 omapi_connection_dereference (&obj, MDL);
128                 return status;
129         }
130         status = omapi_object_reference (&obj -> inner, c, MDL);
131         if (status != ISC_R_SUCCESS) {
132                 omapi_connection_dereference (&obj, MDL);
133                 return status;
134         }
135
136         /* Store the address list on the object. */
137         omapi_addr_list_reference (&obj -> connect_list, remote_addrs, MDL);
138         obj -> cptr = 0;
139         obj -> state = omapi_connection_unconnected;
140
141 #if defined (TRACING)
142         /* If we're playing back, don't actually try to connect - just leave
143            the object available for a subsequent connect or disconnect. */
144         if (!trace_playback ()) {
145 #endif
146                 /* Create a socket on which to communicate. */
147                 obj -> socket =
148                         socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
149                 if (obj -> socket < 0) {
150                         omapi_connection_dereference (&obj, MDL);
151                         if (errno == EMFILE || errno == ENFILE
152                             || errno == ENOBUFS)
153                                 return ISC_R_NORESOURCES;
154                         return ISC_R_UNEXPECTED;
155                 }
156
157                 /* Set up the local address, if any. */
158                 if (local_addr) {
159                         /* Only do TCPv4 so far. */
160                         if (local_addr -> addrtype != AF_INET) {
161                                 omapi_connection_dereference (&obj, MDL);
162                                 return ISC_R_INVALIDARG;
163                         }
164                         local_sin.sin_port = htons (local_addr -> port);
165                         memcpy (&local_sin.sin_addr,
166                                 local_addr -> address,
167                                 local_addr -> addrlen);
168 #if defined (HAVE_SA_LEN)
169                         local_sin.sin_len = sizeof local_addr;
170 #endif
171                         local_sin.sin_family = AF_INET;
172                         memset (&local_sin.sin_zero, 0,
173                                 sizeof local_sin.sin_zero);
174                         
175                         if (bind (obj -> socket, (struct sockaddr *)&local_sin,
176                                   sizeof local_sin) < 0) {
177                                 omapi_object_dereference ((omapi_object_t **)
178                                                           &obj, MDL);
179                                 if (errno == EADDRINUSE)
180                                         return ISC_R_ADDRINUSE;
181                                 if (errno == EADDRNOTAVAIL)
182                                         return ISC_R_ADDRNOTAVAIL;
183                                 if (errno == EACCES)
184                                         return ISC_R_NOPERM;
185                                 return ISC_R_UNEXPECTED;
186                         }
187                         obj -> local_addr = local_sin;
188                 }
189
190 #if defined (HAVE_SETFD)
191                 if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
192                         close (obj -> socket);
193                         omapi_connection_dereference (&obj, MDL);
194                         return ISC_R_UNEXPECTED;
195                 }
196 #endif
197
198                 /* Set the SO_REUSEADDR flag (this should not fail). */
199                 flag = 1;
200                 if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR,
201                                 (char *)&flag, sizeof flag) < 0) {
202                         omapi_connection_dereference (&obj, MDL);
203                         return ISC_R_UNEXPECTED;
204                 }
205         
206                 /* Set the file to nonblocking mode. */
207                 if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
208                         omapi_connection_dereference (&obj, MDL);
209                         return ISC_R_UNEXPECTED;
210                 }
211
212                 status = (omapi_register_io_object
213                           ((omapi_object_t *)obj,
214                            0, omapi_connection_writefd,
215                            0, omapi_connection_connect,
216                            omapi_connection_reaper));
217                 if (status != ISC_R_SUCCESS)
218                         goto out;
219                 status = omapi_connection_connect_internal ((omapi_object_t *)
220                                                             obj);
221 #if defined (TRACING)
222         }
223         omapi_connection_register (obj, MDL);
224 #endif
225
226       out:
227         omapi_connection_dereference (&obj, MDL);
228         return status;
229 }
230
231 #if defined (TRACING)
232 omapi_array_t *omapi_connections;
233
234 OMAPI_ARRAY_TYPE(omapi_connection, omapi_connection_object_t)
235
236 void omapi_connection_trace_setup (void) {
237         trace_connect = trace_type_register ("connect", (void *)0,
238                                              trace_connect_input,
239                                              trace_connect_stop, MDL);
240         trace_disconnect = trace_type_register ("disconnect", (void *)0,
241                                                 trace_disconnect_input,
242                                                 trace_disconnect_stop, MDL);
243 }
244
245 void omapi_connection_register (omapi_connection_object_t *obj,
246                                 const char *file, int line)
247 {
248         isc_result_t status;
249         trace_iov_t iov [6];
250         int iov_count = 0;
251         int32_t connect_index, listener_index;
252         static int32_t index;
253
254         if (!omapi_connections) {
255                 status = omapi_connection_array_allocate (&omapi_connections,
256                                                           file, line);
257                 if (status != ISC_R_SUCCESS)
258                         return;
259         }
260
261         status = omapi_connection_array_extend (omapi_connections, obj,
262                                                 (int *)0, file, line);
263         if (status != ISC_R_SUCCESS) {
264                 obj -> index = -1;
265                 return;
266         }
267
268 #if defined (TRACING)
269         if (trace_record ()) {
270                 /* Connection registration packet:
271                    
272                      int32_t index
273                      int32_t listener_index [-1 means no listener]
274                    u_int16_t remote_port
275                    u_int16_t local_port
276                    u_int32_t remote_addr
277                    u_int32_t local_addr */
278
279                 connect_index = htonl (index);
280                 index++;
281                 if (obj -> listener)
282                         listener_index = htonl (obj -> listener -> index);
283                 else
284                         listener_index = htonl (-1);
285                 iov [iov_count].buf = (char *)&connect_index;
286                 iov [iov_count++].len = sizeof connect_index;
287                 iov [iov_count].buf = (char *)&listener_index;
288                 iov [iov_count++].len = sizeof listener_index;
289                 iov [iov_count].buf = (char *)&obj -> remote_addr.sin_port;
290                 iov [iov_count++].len = sizeof obj -> remote_addr.sin_port;
291                 iov [iov_count].buf = (char *)&obj -> local_addr.sin_port;
292                 iov [iov_count++].len = sizeof obj -> local_addr.sin_port;
293                 iov [iov_count].buf = (char *)&obj -> remote_addr.sin_addr;
294                 iov [iov_count++].len = sizeof obj -> remote_addr.sin_addr;
295                 iov [iov_count].buf = (char *)&obj -> local_addr.sin_addr;
296                 iov [iov_count++].len = sizeof obj -> local_addr.sin_addr;
297
298                 status = trace_write_packet_iov (trace_connect,
299                                                  iov_count, iov, file, line);
300         }
301 #endif
302 }
303
304 static void trace_connect_input (trace_type_t *ttype,
305                                  unsigned length, char *buf)
306 {
307         struct sockaddr_in remote, local;
308         int32_t connect_index, listener_index;
309         char *s = buf;
310         omapi_connection_object_t *obj;
311         isc_result_t status;
312         int i;
313
314         if (length != ((sizeof connect_index) +
315                        (sizeof remote.sin_port) +
316                        (sizeof remote.sin_addr)) * 2) {
317                 log_error ("Trace connect: invalid length %d", length);
318                 return;
319         }
320
321         memset (&remote, 0, sizeof remote);
322         memset (&local, 0, sizeof local);
323         memcpy (&connect_index, s, sizeof connect_index);
324         s += sizeof connect_index;
325         memcpy (&listener_index, s, sizeof listener_index);
326         s += sizeof listener_index;
327         memcpy (&remote.sin_port, s, sizeof remote.sin_port);
328         s += sizeof remote.sin_port;
329         memcpy (&local.sin_port, s, sizeof local.sin_port);
330         s += sizeof local.sin_port;
331         memcpy (&remote.sin_addr, s, sizeof remote.sin_addr);
332         s += sizeof remote.sin_addr;
333         memcpy (&local.sin_addr, s, sizeof local.sin_addr);
334         s += sizeof local.sin_addr;
335
336         connect_index = ntohl (connect_index);
337         listener_index = ntohl (listener_index);
338
339         /* If this was a connect to a listener, then we just slap together
340            a new connection. */
341         if (listener_index != -1) {
342                 omapi_listener_object_t *listener;
343                 listener = (omapi_listener_object_t *)0;
344                 omapi_array_foreach_begin (trace_listeners,
345                                            omapi_listener_object_t, lp) {
346                         if (lp -> address.sin_port == local.sin_port) {
347                                 omapi_listener_reference (&listener, lp, MDL);
348                                 omapi_listener_dereference (&lp, MDL);
349                                 break;
350                         } 
351                 } omapi_array_foreach_end (trace_listeners,
352                                            omapi_listener_object_t, lp);
353                 if (!listener) {
354                         log_error ("%s%ld, addr %s, port %d",
355                                    "Spurious traced listener connect - index ",
356                                    (long int)listener_index,
357                                    inet_ntoa (local.sin_addr),
358                                    ntohs (local.sin_port));
359                         return;
360                 }
361                 obj = (omapi_connection_object_t *)0;
362                 status = omapi_listener_connect (&obj, listener, -1, &remote);
363                 if (status != ISC_R_SUCCESS) {
364                         log_error ("traced listener connect: %s",
365                                    isc_result_totext (status));
366                 }
367                 if (obj)
368                         omapi_connection_dereference (&obj, MDL);
369                 omapi_listener_dereference (&listener, MDL);
370                 return;
371         }
372
373         /* Find the matching connect object, if there is one. */
374         omapi_array_foreach_begin (omapi_connections,
375                                    omapi_connection_object_t, lp) {
376             for (i = 0; (lp -> connect_list &&
377                          i < lp -> connect_list -> count); i++) {
378                     if (!memcmp (&remote.sin_addr,
379                                  &lp -> connect_list -> addresses [i].address,
380                                  sizeof remote.sin_addr) &&
381                         (ntohs (remote.sin_port) ==
382                          lp -> connect_list -> addresses [i].port))
383                         lp -> state = omapi_connection_connected;
384                         lp -> remote_addr = remote;
385                         lp -> remote_addr.sin_family = AF_INET;
386 #if defined (HAVE_SIN_LEN)
387                         lp -> remote_addr.sin_len = sizeof remote;
388 #endif
389                         omapi_addr_list_dereference (&lp -> connect_list, MDL);
390                         lp -> index = connect_index;
391                         status = omapi_signal_in ((omapi_object_t *)lp,
392                                                   "connect");
393                         omapi_connection_dereference (&lp, MDL);
394                         return;
395                 }
396         } omapi_array_foreach_end (omapi_connections,
397                                    omapi_connection_object_t, lp);
398                                                  
399         log_error ("Spurious traced connect - index %ld, addr %s, port %d",
400                    (long int)connect_index, inet_ntoa (remote.sin_addr),
401                    ntohs (remote.sin_port));
402         return;
403 }
404
405 static void trace_connect_stop (trace_type_t *ttype) { }
406
407 static void trace_disconnect_input (trace_type_t *ttype,
408                                     unsigned length, char *buf)
409 {
410         int32_t *index;
411         if (length != sizeof *index) {
412                 log_error ("trace disconnect: wrong length %d", length);
413                 return;
414         }
415         
416         index = (int32_t *)buf;
417
418         omapi_array_foreach_begin (omapi_connections,
419                                    omapi_connection_object_t, lp) {
420                 if (lp -> index == ntohl (*index)) {
421                         omapi_disconnect ((omapi_object_t *)lp, 1);
422                         omapi_connection_dereference (&lp, MDL);
423                         return;
424                 }
425         } omapi_array_foreach_end (omapi_connections,
426                                    omapi_connection_object_t, lp);
427
428         log_error ("trace disconnect: no connection matching index %ld",
429                    (long int)ntohl (*index));
430 }
431
432 static void trace_disconnect_stop (trace_type_t *ttype) { }
433 #endif
434
435 /* Disconnect a connection object from the remote end.   If force is nonzero,
436    close the connection immediately.   Otherwise, shut down the receiving end
437    but allow any unsent data to be sent before actually closing the socket. */
438
439 isc_result_t omapi_disconnect (omapi_object_t *h,
440                                int force)
441 {
442         omapi_connection_object_t *c;
443         isc_result_t status;
444
445 #ifdef DEBUG_PROTOCOL
446         log_debug ("omapi_disconnect(%s)", force ? "force" : "");
447 #endif
448
449         c = (omapi_connection_object_t *)h;
450         if (c -> type != omapi_type_connection)
451                 return ISC_R_INVALIDARG;
452
453 #if defined (TRACING)
454         if (trace_record ()) {
455                 int32_t index;
456
457                 index = htonl (c -> index);
458                 status = trace_write_packet (trace_disconnect,
459                                              sizeof index, (char *)&index,
460                                              MDL);
461                 if (status != ISC_R_SUCCESS) {
462                         trace_stop ();
463                         log_error ("trace_write_packet: %s",
464                                    isc_result_totext (status));
465                 }
466         }
467         if (!trace_playback ()) {
468 #endif
469                 if (!force) {
470                         /* If we're already disconnecting, we don't have to do
471                            anything. */
472                         if (c -> state == omapi_connection_disconnecting)
473                                 return ISC_R_SUCCESS;
474
475                         /* Try to shut down the socket - this sends a FIN to
476                            the remote end, so that it won't send us any more
477                            data.   If the shutdown succeeds, and we still
478                            have bytes left to write, defer closing the socket
479                            until that's done. */
480                         if (!shutdown (c -> socket, SHUT_RD)) {
481                                 if (c -> out_bytes > 0) {
482                                         c -> state =
483                                                 omapi_connection_disconnecting;
484                                         return ISC_R_SUCCESS;
485                                 }
486                         }
487                 }
488                 close (c -> socket);
489 #if defined (TRACING)
490         }
491 #endif
492         c -> state = omapi_connection_closed;
493
494         /* Disconnect from I/O object, if any. */
495         if (h -> outer) {
496                 if (h -> outer -> inner)
497                         omapi_object_dereference (&h -> outer -> inner, MDL);
498                 omapi_object_dereference (&h -> outer, MDL);
499         }
500
501         /* If whatever created us registered a signal handler, send it
502            a disconnect signal. */
503         omapi_signal (h, "disconnect", h);
504         return ISC_R_SUCCESS;
505 }
506
507 isc_result_t omapi_connection_require (omapi_object_t *h, unsigned bytes)
508 {
509         omapi_connection_object_t *c;
510
511         if (h -> type != omapi_type_connection)
512                 return ISC_R_INVALIDARG;
513         c = (omapi_connection_object_t *)h;
514
515         c -> bytes_needed = bytes;
516         if (c -> bytes_needed <= c -> in_bytes) {
517                 return ISC_R_SUCCESS;
518         }
519         return ISC_R_NOTYET;
520 }
521
522 /* Return the socket on which the dispatcher should wait for readiness
523    to read, for a connection object.   If we already have more bytes than
524    we need to do the next thing, and we have at least a single full input
525    buffer, then don't indicate that we're ready to read. */
526 int omapi_connection_readfd (omapi_object_t *h)
527 {
528         omapi_connection_object_t *c;
529         if (h -> type != omapi_type_connection)
530                 return -1;
531         c = (omapi_connection_object_t *)h;
532         if (c -> state != omapi_connection_connected)
533                 return -1;
534         if (c -> in_bytes >= OMAPI_BUF_SIZE - 1 &&
535             c -> in_bytes > c -> bytes_needed)
536                 return -1;
537         return c -> socket;
538 }
539
540 /* Return the socket on which the dispatcher should wait for readiness
541    to write, for a connection object.   If there are no bytes buffered
542    for writing, then don't indicate that we're ready to write. */
543 int omapi_connection_writefd (omapi_object_t *h)
544 {
545         omapi_connection_object_t *c;
546         if (h -> type != omapi_type_connection)
547                 return -1;
548         c = (omapi_connection_object_t *)h;
549         if (c -> state == omapi_connection_connecting)
550                 return c -> socket;
551         if (c -> out_bytes)
552                 return c -> socket;
553         else
554                 return -1;
555 }
556
557 isc_result_t omapi_connection_connect (omapi_object_t *h)
558 {
559         isc_result_t status;
560
561         status = omapi_connection_connect_internal (h);
562         if (status != ISC_R_SUCCESS)
563                 omapi_signal (h, "status", status);
564         return ISC_R_SUCCESS;
565 }
566
567 static isc_result_t omapi_connection_connect_internal (omapi_object_t *h)
568 {
569         int error;
570         omapi_connection_object_t *c;
571         SOCKLEN_T sl;
572         isc_result_t status;
573
574         if (h -> type != omapi_type_connection)
575                 return ISC_R_INVALIDARG;
576         c = (omapi_connection_object_t *)h;
577
578         if (c -> state == omapi_connection_connecting) {
579                 sl = sizeof error;
580                 if (getsockopt (c -> socket, SOL_SOCKET, SO_ERROR,
581                                 (char *)&error, &sl) < 0) {
582                         omapi_disconnect (h, 1);
583                         return ISC_R_SUCCESS;
584                 }
585                 if (!error)
586                         c -> state = omapi_connection_connected;
587         }
588         if (c -> state == omapi_connection_connecting ||
589             c -> state == omapi_connection_unconnected) {
590                 if (c -> cptr >= c -> connect_list -> count) {
591                         switch (error) {
592                               case ECONNREFUSED:
593                                 status = ISC_R_CONNREFUSED;
594                                 break;
595                               case ENETUNREACH:
596                                 status = ISC_R_NETUNREACH;
597                                 break;
598                               default:
599                                 status = uerr2isc (error);
600                                 break;
601                         }
602                         omapi_disconnect (h, 1);
603                         return status;
604                 }
605
606                 if (c -> connect_list -> addresses [c -> cptr].addrtype !=
607                     AF_INET) {
608                         omapi_disconnect (h, 1);
609                         return ISC_R_INVALIDARG;
610                 }
611
612                 memcpy (&c -> remote_addr.sin_addr,
613                         &c -> connect_list -> addresses [c -> cptr].address,
614                         sizeof c -> remote_addr.sin_addr);
615                 c -> remote_addr.sin_family = AF_INET;
616                 c -> remote_addr.sin_port =
617                        htons (c -> connect_list -> addresses [c -> cptr].port);
618 #if defined (HAVE_SA_LEN)
619                 c -> remote_addr.sin_len = sizeof c -> remote_addr;
620 #endif
621                 memset (&c -> remote_addr.sin_zero, 0,
622                         sizeof c -> remote_addr.sin_zero);
623                 ++c -> cptr;
624
625                 error = connect (c -> socket,
626                                  (struct sockaddr *)&c -> remote_addr,
627                                  sizeof c -> remote_addr);
628                 if (error < 0) {
629                         error = errno;
630                         if (error != EINPROGRESS) {
631                                 omapi_disconnect (h, 1);
632                                 switch (error) {
633                                       case ECONNREFUSED:
634                                         status = ISC_R_CONNREFUSED;
635                                         break;
636                                       case ENETUNREACH:
637                                         status = ISC_R_NETUNREACH;
638                                         break;
639                                       default:
640                                         status = uerr2isc (error);
641                                         break;
642                                 }
643                                 return status;
644                         }
645                         c -> state = omapi_connection_connecting;
646                         return ISC_R_INCOMPLETE;
647                 }
648                 c -> state = omapi_connection_connected;
649         }
650         
651         /* I don't know why this would fail, so I'm tempted not to test
652            the return value. */
653         sl = sizeof (c -> local_addr);
654         if (getsockname (c -> socket,
655                          (struct sockaddr *)&c -> local_addr, &sl) < 0) {
656         }
657
658         /* Disconnect from I/O object, if any. */
659         if (h -> outer)
660                 omapi_unregister_io_object (h);
661
662         status = omapi_register_io_object (h,
663                                            omapi_connection_readfd,
664                                            omapi_connection_writefd,
665                                            omapi_connection_reader,
666                                            omapi_connection_writer,
667                                            omapi_connection_reaper);
668
669         if (status != ISC_R_SUCCESS) {
670                 omapi_disconnect (h, 1);
671                 return status;
672         }
673
674         omapi_signal_in (h, "connect");
675         omapi_addr_list_dereference (&c -> connect_list, MDL);
676         return ISC_R_SUCCESS;
677 }
678
679 /* Reaper function for connection - if the connection is completely closed,
680    reap it.   If it's in the disconnecting state, there were bytes left
681    to write when the user closed it, so if there are now no bytes left to
682    write, we can close it. */
683 isc_result_t omapi_connection_reaper (omapi_object_t *h)
684 {
685         omapi_connection_object_t *c;
686
687         if (h -> type != omapi_type_connection)
688                 return ISC_R_INVALIDARG;
689
690         c = (omapi_connection_object_t *)h;
691         if (c -> state == omapi_connection_disconnecting &&
692             c -> out_bytes == 0) {
693 #ifdef DEBUG_PROTOCOL
694                 log_debug ("omapi_connection_reaper(): disconnect");
695 #endif
696                 omapi_disconnect (h, 1);
697         }
698         if (c -> state == omapi_connection_closed) {
699 #ifdef DEBUG_PROTOCOL
700                 log_debug ("omapi_connection_reaper(): closed");
701 #endif
702                 return ISC_R_NOTCONNECTED;
703         }
704         return ISC_R_SUCCESS;
705 }
706
707 static isc_result_t make_dst_key (DST_KEY **dst_key, omapi_object_t *a) {
708         omapi_value_t *name      = (omapi_value_t *)0;
709         omapi_value_t *algorithm = (omapi_value_t *)0;
710         omapi_value_t *key       = (omapi_value_t *)0;
711         int algorithm_id = UNKNOWN_KEYALG;
712         char *name_str = NULL;
713         isc_result_t status = ISC_R_SUCCESS;
714
715         if (status == ISC_R_SUCCESS)
716                 status = omapi_get_value_str
717                         (a, (omapi_object_t *)0, "name", &name);
718
719         if (status == ISC_R_SUCCESS)
720                 status = omapi_get_value_str
721                         (a, (omapi_object_t *)0, "algorithm", &algorithm);
722
723         if (status == ISC_R_SUCCESS)
724                 status = omapi_get_value_str
725                         (a, (omapi_object_t *)0, "key", &key);
726
727         if (status == ISC_R_SUCCESS) {
728                 if ((algorithm -> value -> type == omapi_datatype_data ||
729                      algorithm -> value -> type == omapi_datatype_string) &&
730                     strncasecmp ((char *)algorithm -> value -> u.buffer.value,
731                                  NS_TSIG_ALG_HMAC_MD5 ".",
732                                  algorithm -> value -> u.buffer.len) == 0) {
733                         algorithm_id = KEY_HMAC_MD5;
734                 } else {
735                         status = ISC_R_INVALIDARG;
736                 }
737         }
738
739         if (status == ISC_R_SUCCESS) {
740                 name_str = dmalloc (name -> value -> u.buffer.len + 1, MDL);
741                 if (!name_str)
742                         status = ISC_R_NOMEMORY;
743         }
744
745         if (status == ISC_R_SUCCESS) {
746                 memcpy (name_str,
747                         name -> value -> u.buffer.value,
748                         name -> value -> u.buffer.len);
749                 name_str [name -> value -> u.buffer.len] = 0;
750
751                 *dst_key = dst_buffer_to_key (name_str, algorithm_id, 0, 0,
752                                               key -> value -> u.buffer.value,
753                                               key -> value -> u.buffer.len);
754                 if (!*dst_key)
755                         status = ISC_R_NOMEMORY;
756         }
757
758         if (name_str)
759                 dfree (name_str, MDL);
760         if (key)
761                 omapi_value_dereference (&key, MDL);
762         if (algorithm)
763                 omapi_value_dereference (&algorithm, MDL);
764         if (name)
765                 omapi_value_dereference (&name, MDL);
766
767         return status;
768 }
769
770 isc_result_t omapi_connection_sign_data (int mode,
771                                          DST_KEY *key,
772                                          void **context,
773                                          const unsigned char *data,
774                                          const unsigned len,
775                                          omapi_typed_data_t **result)
776 {
777         omapi_typed_data_t *td = (omapi_typed_data_t *)0;
778         isc_result_t status;
779         int r;
780
781         if (mode & SIG_MODE_FINAL) {
782                 status = omapi_typed_data_new (MDL, &td,
783                                                omapi_datatype_data,
784                                                dst_sig_size (key));
785                 if (status != ISC_R_SUCCESS)
786                         return status;
787         }
788
789         r = dst_sign_data (mode, key, context, data, len,
790                            td ? td -> u.buffer.value : (u_char *)0,
791                            td ? td -> u.buffer.len   : 0);
792
793         /* dst_sign_data() really should do this for us, shouldn't it? */
794         if (mode & SIG_MODE_FINAL)
795                 *context = (void *)0;
796
797         if (r < 0) {
798                 if (td)
799                         omapi_typed_data_dereference (&td, MDL);
800                 return ISC_R_INVALIDKEY;
801         }
802
803         if (result && td) {
804                 omapi_typed_data_reference (result, td, MDL);
805         }
806
807         if (td)
808                 omapi_typed_data_dereference (&td, MDL);
809
810         return ISC_R_SUCCESS;
811 }
812
813 isc_result_t omapi_connection_output_auth_length (omapi_object_t *h,
814                                                   unsigned *l)
815 {
816         omapi_connection_object_t *c;
817
818         if (h -> type != omapi_type_connection)
819                 return ISC_R_INVALIDARG;
820         c = (omapi_connection_object_t *)h;
821
822         if (!c -> out_key)
823                 return ISC_R_NOTFOUND;
824
825         *l = dst_sig_size (c -> out_key);
826         return ISC_R_SUCCESS;
827 }
828
829 isc_result_t omapi_connection_set_value (omapi_object_t *h,
830                                          omapi_object_t *id,
831                                          omapi_data_string_t *name,
832                                          omapi_typed_data_t *value)
833 {
834         omapi_connection_object_t *c;
835         isc_result_t status;
836
837         if (h -> type != omapi_type_connection)
838                 return ISC_R_INVALIDARG;
839         c = (omapi_connection_object_t *)h;
840
841         if (omapi_ds_strcmp (name, "input-authenticator") == 0) {
842                 if (value && value -> type != omapi_datatype_object)
843                         return ISC_R_INVALIDARG;
844
845                 if (c -> in_context) {
846                         omapi_connection_sign_data (SIG_MODE_FINAL,
847                                                     c -> in_key,
848                                                     &c -> in_context,
849                                                     0, 0,
850                                                     (omapi_typed_data_t **) 0);
851                 }
852
853                 if (c -> in_key) {
854                         dst_free_key (c -> in_key);
855                         c -> in_key = (DST_KEY *)0;
856                 }
857
858                 if (value) {
859                         status = make_dst_key (&c -> in_key,
860                                                value -> u.object);
861                         if (status != ISC_R_SUCCESS)
862                                 return status;
863                 }
864
865                 return ISC_R_SUCCESS;
866         }
867         else if (omapi_ds_strcmp (name, "output-authenticator") == 0) {
868                 if (value && value -> type != omapi_datatype_object)
869                         return ISC_R_INVALIDARG;
870
871                 if (c -> out_context) {
872                         omapi_connection_sign_data (SIG_MODE_FINAL,
873                                                     c -> out_key,
874                                                     &c -> out_context,
875                                                     0, 0,
876                                                     (omapi_typed_data_t **) 0);
877                 }
878
879                 if (c -> out_key) {
880                         dst_free_key (c -> out_key);
881                         c -> out_key = (DST_KEY *)0;
882                 }
883
884                 if (value) {
885                         status = make_dst_key (&c -> out_key,
886                                                value -> u.object);
887                         if (status != ISC_R_SUCCESS)
888                                 return status;
889                 }
890
891                 return ISC_R_SUCCESS;
892         }
893         
894         if (h -> inner && h -> inner -> type -> set_value)
895                 return (*(h -> inner -> type -> set_value))
896                         (h -> inner, id, name, value);
897         return ISC_R_NOTFOUND;
898 }
899
900 isc_result_t omapi_connection_get_value (omapi_object_t *h,
901                                          omapi_object_t *id,
902                                          omapi_data_string_t *name,
903                                          omapi_value_t **value)
904 {
905         omapi_connection_object_t *c;
906         omapi_typed_data_t *td = (omapi_typed_data_t *)0;
907         isc_result_t status;
908
909         if (h -> type != omapi_type_connection)
910                 return ISC_R_INVALIDARG;
911         c = (omapi_connection_object_t *)h;
912
913         if (omapi_ds_strcmp (name, "input-signature") == 0) {
914                 if (!c -> in_key || !c -> in_context)
915                         return ISC_R_NOTFOUND;
916
917                 status = omapi_connection_sign_data (SIG_MODE_FINAL,
918                                                      c -> in_key,
919                                                      &c -> in_context,
920                                                      0, 0, &td);
921                 if (status != ISC_R_SUCCESS)
922                         return status;
923
924                 status = omapi_make_value (value, name, td, MDL);
925                 omapi_typed_data_dereference (&td, MDL);
926                 return status;
927
928         } else if (omapi_ds_strcmp (name, "input-signature-size") == 0) {
929                 if (!c -> in_key)
930                         return ISC_R_NOTFOUND;
931
932                 return omapi_make_int_value (value, name,
933                                              dst_sig_size (c -> in_key), MDL);
934
935         } else if (omapi_ds_strcmp (name, "output-signature") == 0) {
936                 if (!c -> out_key || !c -> out_context)
937                         return ISC_R_NOTFOUND;
938
939                 status = omapi_connection_sign_data (SIG_MODE_FINAL,
940                                                      c -> out_key,
941                                                      &c -> out_context,
942                                                      0, 0, &td);
943                 if (status != ISC_R_SUCCESS)
944                         return status;
945
946                 status = omapi_make_value (value, name, td, MDL);
947                 omapi_typed_data_dereference (&td, MDL);
948                 return status;
949
950         } else if (omapi_ds_strcmp (name, "output-signature-size") == 0) {
951                 if (!c -> out_key)
952                         return ISC_R_NOTFOUND;
953
954                 return omapi_make_int_value (value, name,
955                                              dst_sig_size (c -> out_key), MDL);
956         }
957         
958         if (h -> inner && h -> inner -> type -> get_value)
959                 return (*(h -> inner -> type -> get_value))
960                         (h -> inner, id, name, value);
961         return ISC_R_NOTFOUND;
962 }
963
964 isc_result_t omapi_connection_destroy (omapi_object_t *h,
965                                        const char *file, int line)
966 {
967         omapi_connection_object_t *c;
968
969 #ifdef DEBUG_PROTOCOL
970         log_debug ("omapi_connection_destroy()");
971 #endif
972
973         if (h -> type != omapi_type_connection)
974                 return ISC_R_UNEXPECTED;
975         c = (omapi_connection_object_t *)(h);
976         if (c -> state == omapi_connection_connected)
977                 omapi_disconnect (h, 1);
978         if (c -> listener)
979                 omapi_listener_dereference (&c -> listener, file, line);
980         if (c -> connect_list)
981                 omapi_addr_list_dereference (&c -> connect_list, file, line);
982         return ISC_R_SUCCESS;
983 }
984
985 isc_result_t omapi_connection_signal_handler (omapi_object_t *h,
986                                               const char *name, va_list ap)
987 {
988         if (h -> type != omapi_type_connection)
989                 return ISC_R_INVALIDARG;
990
991 #ifdef DEBUG_PROTOCOL
992         log_debug ("omapi_connection_signal_handler(%s)", name);
993 #endif
994         
995         if (h -> inner && h -> inner -> type -> signal_handler)
996                 return (*(h -> inner -> type -> signal_handler)) (h -> inner,
997                                                                   name, ap);
998         return ISC_R_NOTFOUND;
999 }
1000
1001 /* Write all the published values associated with the object through the
1002    specified connection. */
1003
1004 isc_result_t omapi_connection_stuff_values (omapi_object_t *c,
1005                                             omapi_object_t *id,
1006                                             omapi_object_t *m)
1007 {
1008         int i;
1009
1010         if (m -> type != omapi_type_connection)
1011                 return ISC_R_INVALIDARG;
1012
1013         if (m -> inner && m -> inner -> type -> stuff_values)
1014                 return (*(m -> inner -> type -> stuff_values)) (c, id,
1015                                                                 m -> inner);
1016         return ISC_R_SUCCESS;
1017 }