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