omapip/connection.c

Go to the documentation of this file.
00001 /* connection.c
00002 
00003    Subroutines for dealing with connections. */
00004 
00005 /*
00006  * Copyright (c) 2009-2014 by Internet Systems Consortium, Inc. ("ISC")
00007  * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
00008  * Copyright (c) 1999-2003 by Internet Software Consortium
00009  *
00010  * Permission to use, copy, modify, and distribute this software for any
00011  * purpose with or without fee is hereby granted, provided that the above
00012  * copyright notice and this permission notice appear in all copies.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
00015  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00016  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
00017  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00018  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00019  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
00020  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00021  *
00022  *   Internet Systems Consortium, Inc.
00023  *   950 Charter Street
00024  *   Redwood City, CA 94063
00025  *   <info@isc.org>
00026  *   https://www.isc.org/
00027  *
00028  */
00029 
00030 #include "dhcpd.h"
00031 
00032 #include <omapip/omapip_p.h>
00033 #include <arpa/inet.h>
00034 #include <arpa/nameser.h>
00035 #include <errno.h>
00036 
00037 #if defined (TRACING)
00038 static void trace_connect_input (trace_type_t *, unsigned, char *);
00039 static void trace_connect_stop (trace_type_t *);
00040 static void trace_disconnect_input (trace_type_t *, unsigned, char *);
00041 static void trace_disconnect_stop (trace_type_t *);
00042 trace_type_t *trace_connect;
00043 trace_type_t *trace_disconnect;
00044 extern omapi_array_t *trace_listeners;
00045 #endif
00046 static isc_result_t omapi_connection_connect_internal (omapi_object_t *);
00047 
00048 OMAPI_OBJECT_ALLOC (omapi_connection,
00049                     omapi_connection_object_t, omapi_type_connection)
00050 
00051 isc_result_t omapi_connect (omapi_object_t *c,
00052                             const char *server_name,
00053                             unsigned port)
00054 {
00055         struct hostent *he;
00056         unsigned i, hix;
00057         omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
00058         struct in_addr foo;
00059         isc_result_t status;
00060 
00061 #ifdef DEBUG_PROTOCOL
00062         log_debug ("omapi_connect(%s, port=%d)", server_name, port);
00063 #endif
00064 
00065         if (!inet_aton (server_name, &foo)) {
00066                 /* If we didn't get a numeric address, try for a domain
00067                    name.  It's okay for this call to block. */
00068                 he = gethostbyname (server_name);
00069                 if (!he)
00070                         return DHCP_R_HOSTUNKNOWN;
00071                 for (i = 0; he -> h_addr_list [i]; i++)
00072                         ;
00073                 if (i == 0)
00074                         return DHCP_R_HOSTUNKNOWN;
00075                 hix = i;
00076 
00077                 status = omapi_addr_list_new (&addrs, hix, MDL);
00078                 if (status != ISC_R_SUCCESS)
00079                         return status;
00080                 for (i = 0; i < hix; i++) {
00081                         addrs -> addresses [i].addrtype = he -> h_addrtype;
00082                         addrs -> addresses [i].addrlen = he -> h_length;
00083                         memcpy (addrs -> addresses [i].address,
00084                                 he -> h_addr_list [i],
00085                                 (unsigned)he -> h_length);
00086                         addrs -> addresses [i].port = port;
00087                 }
00088         } else {
00089                 status = omapi_addr_list_new (&addrs, 1, MDL);
00090                 if (status != ISC_R_SUCCESS)
00091                         return status;
00092                 addrs -> addresses [0].addrtype = AF_INET;
00093                 addrs -> addresses [0].addrlen = sizeof foo;
00094                 memcpy (addrs -> addresses [0].address, &foo, sizeof foo);
00095                 addrs -> addresses [0].port = port;
00096         }
00097         status = omapi_connect_list (c, addrs, (omapi_addr_t *)0);
00098         omapi_addr_list_dereference (&addrs, MDL);
00099         return status;
00100 }
00101 
00102 isc_result_t omapi_connect_list (omapi_object_t *c,
00103                                  omapi_addr_list_t *remote_addrs,
00104                                  omapi_addr_t *local_addr)
00105 {
00106         isc_result_t status;
00107         omapi_connection_object_t *obj;
00108         int flag;
00109         struct sockaddr_in local_sin;
00110 
00111         obj = (omapi_connection_object_t *)0;
00112         status = omapi_connection_allocate (&obj, MDL);
00113         if (status != ISC_R_SUCCESS)
00114                 return status;
00115 
00116         status = omapi_object_reference (&c -> outer, (omapi_object_t *)obj,
00117                                          MDL);
00118         if (status != ISC_R_SUCCESS) {
00119                 omapi_connection_dereference (&obj, MDL);
00120                 return status;
00121         }
00122         status = omapi_object_reference (&obj -> inner, c, MDL);
00123         if (status != ISC_R_SUCCESS) {
00124                 omapi_connection_dereference (&obj, MDL);
00125                 return status;
00126         }
00127 
00128         /* Store the address list on the object. */
00129         omapi_addr_list_reference (&obj -> connect_list, remote_addrs, MDL);
00130         obj -> cptr = 0;
00131         obj -> state = omapi_connection_unconnected;
00132 
00133 #if defined (TRACING)
00134         /* If we're playing back, don't actually try to connect - just leave
00135            the object available for a subsequent connect or disconnect. */
00136         if (!trace_playback ()) {
00137 #endif
00138                 /* Create a socket on which to communicate. */
00139                 obj -> socket =
00140                         socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
00141                 if (obj -> socket < 0) {
00142                         omapi_connection_dereference (&obj, MDL);
00143                         if (errno == EMFILE || errno == ENFILE
00144                             || errno == ENOBUFS)
00145                                 return ISC_R_NORESOURCES;
00146                         return ISC_R_UNEXPECTED;
00147                 }
00148 
00149                 /* Set up the local address, if any. */
00150                 if (local_addr) {
00151                         /* Only do TCPv4 so far. */
00152                         if (local_addr -> addrtype != AF_INET) {
00153                                 omapi_connection_dereference (&obj, MDL);
00154                                 return DHCP_R_INVALIDARG;
00155                         }
00156                         local_sin.sin_port = htons (local_addr -> port);
00157                         memcpy (&local_sin.sin_addr,
00158                                 local_addr -> address,
00159                                 local_addr -> addrlen);
00160 #if defined (HAVE_SA_LEN)
00161                         local_sin.sin_len = sizeof local_addr;
00162 #endif
00163                         local_sin.sin_family = AF_INET;
00164                         memset (&local_sin.sin_zero, 0,
00165                                 sizeof local_sin.sin_zero);
00166                         
00167                         if (bind (obj -> socket, (struct sockaddr *)&local_sin,
00168                                   sizeof local_sin) < 0) {
00169                                 omapi_connection_object_t **objp = &obj;
00170                                 omapi_object_t **o = (omapi_object_t **)objp;
00171                                 omapi_object_dereference(o, MDL);
00172                                 if (errno == EADDRINUSE)
00173                                         return ISC_R_ADDRINUSE;
00174                                 if (errno == EADDRNOTAVAIL)
00175                                         return ISC_R_ADDRNOTAVAIL;
00176                                 if (errno == EACCES)
00177                                         return ISC_R_NOPERM;
00178                                 return ISC_R_UNEXPECTED;
00179                         }
00180                         obj -> local_addr = local_sin;
00181                 }
00182 
00183 #if defined(F_SETFD)
00184                 if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
00185                         close (obj -> socket);
00186                         omapi_connection_dereference (&obj, MDL);
00187                         return ISC_R_UNEXPECTED;
00188                 }
00189 #endif
00190 
00191                 /* Set the SO_REUSEADDR flag (this should not fail). */
00192                 flag = 1;
00193                 if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR,
00194                                 (char *)&flag, sizeof flag) < 0) {
00195                         omapi_connection_dereference (&obj, MDL);
00196                         return ISC_R_UNEXPECTED;
00197                 }
00198         
00199                 /* Set the file to nonblocking mode. */
00200                 if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
00201                         omapi_connection_dereference (&obj, MDL);
00202                         return ISC_R_UNEXPECTED;
00203                 }
00204 
00205 #ifdef SO_NOSIGPIPE
00206                 /*
00207                  * If available stop the OS from killing our
00208                  * program on a SIGPIPE failure
00209                  */
00210                 flag = 1;
00211                 if (setsockopt(obj->socket, SOL_SOCKET, SO_NOSIGPIPE,
00212                                (char *)&flag, sizeof(flag)) < 0) {
00213                         omapi_connection_dereference (&obj, MDL);
00214                         return ISC_R_UNEXPECTED;
00215                 }                       
00216 #endif
00217 
00218                 status = (omapi_register_io_object
00219                           ((omapi_object_t *)obj,
00220                            0, omapi_connection_writefd,
00221                            0, omapi_connection_connect,
00222                            omapi_connection_reaper));
00223                 if (status != ISC_R_SUCCESS)
00224                         goto out;
00225                 status = omapi_connection_connect_internal ((omapi_object_t *)
00226                                                             obj);
00227                 /*
00228                  * inprogress is the same as success but used
00229                  * to indicate to the dispatch code that we should
00230                  * mark the socket as requiring more attention.
00231                  * Routines calling this function should handle
00232                  * success properly.
00233                  */
00234                 if (status == ISC_R_INPROGRESS) {
00235                         status = ISC_R_SUCCESS;
00236                 }
00237 #if defined (TRACING)
00238         }
00239         omapi_connection_register (obj, MDL);
00240 #endif
00241 
00242       out:
00243         omapi_connection_dereference (&obj, MDL);
00244         return status;
00245 }
00246 
00247 #if defined (TRACING)
00248 omapi_array_t *omapi_connections;
00249 
00250 OMAPI_ARRAY_TYPE(omapi_connection, omapi_connection_object_t)
00251 
00252 void omapi_connection_trace_setup (void) {
00253         trace_connect = trace_type_register ("connect", (void *)0,
00254                                              trace_connect_input,
00255                                              trace_connect_stop, MDL);
00256         trace_disconnect = trace_type_register ("disconnect", (void *)0,
00257                                                 trace_disconnect_input,
00258                                                 trace_disconnect_stop, MDL);
00259 }
00260 
00261 void omapi_connection_register (omapi_connection_object_t *obj,
00262                                 const char *file, int line)
00263 {
00264         isc_result_t status;
00265         trace_iov_t iov [6];
00266         int iov_count = 0;
00267         int32_t connect_index, listener_index;
00268         static int32_t index;
00269 
00270         if (!omapi_connections) {
00271                 status = omapi_connection_array_allocate (&omapi_connections,
00272                                                           file, line);
00273                 if (status != ISC_R_SUCCESS)
00274                         return;
00275         }
00276 
00277         status = omapi_connection_array_extend (omapi_connections, obj,
00278                                                 (int *)0, file, line);
00279         if (status != ISC_R_SUCCESS) {
00280                 obj -> index = -1;
00281                 return;
00282         }
00283 
00284 #if defined (TRACING)
00285         if (trace_record ()) {
00286                 /* Connection registration packet:
00287                    
00288                      int32_t index
00289                      int32_t listener_index [-1 means no listener]
00290                    u_int16_t remote_port
00291                    u_int16_t local_port
00292                    u_int32_t remote_addr
00293                    u_int32_t local_addr */
00294 
00295                 connect_index = htonl (index);
00296                 index++;
00297                 if (obj -> listener)
00298                         listener_index = htonl (obj -> listener -> index);
00299                 else
00300                         listener_index = htonl (-1);
00301                 iov [iov_count].buf = (char *)&connect_index;
00302                 iov [iov_count++].len = sizeof connect_index;
00303                 iov [iov_count].buf = (char *)&listener_index;
00304                 iov [iov_count++].len = sizeof listener_index;
00305                 iov [iov_count].buf = (char *)&obj -> remote_addr.sin_port;
00306                 iov [iov_count++].len = sizeof obj -> remote_addr.sin_port;
00307                 iov [iov_count].buf = (char *)&obj -> local_addr.sin_port;
00308                 iov [iov_count++].len = sizeof obj -> local_addr.sin_port;
00309                 iov [iov_count].buf = (char *)&obj -> remote_addr.sin_addr;
00310                 iov [iov_count++].len = sizeof obj -> remote_addr.sin_addr;
00311                 iov [iov_count].buf = (char *)&obj -> local_addr.sin_addr;
00312                 iov [iov_count++].len = sizeof obj -> local_addr.sin_addr;
00313 
00314                 status = trace_write_packet_iov (trace_connect,
00315                                                  iov_count, iov, file, line);
00316         }
00317 #endif
00318 }
00319 
00320 static void trace_connect_input (trace_type_t *ttype,
00321                                  unsigned length, char *buf)
00322 {
00323         struct sockaddr_in remote, local;
00324         int32_t connect_index, listener_index;
00325         char *s = buf;
00326         omapi_connection_object_t *obj;
00327         isc_result_t status;
00328         int i;
00329 
00330         if (length != ((sizeof connect_index) +
00331                        (sizeof remote.sin_port) +
00332                        (sizeof remote.sin_addr)) * 2) {
00333                 log_error ("Trace connect: invalid length %d", length);
00334                 return;
00335         }
00336 
00337         memset (&remote, 0, sizeof remote);
00338         memset (&local, 0, sizeof local);
00339         memcpy (&connect_index, s, sizeof connect_index);
00340         s += sizeof connect_index;
00341         memcpy (&listener_index, s, sizeof listener_index);
00342         s += sizeof listener_index;
00343         memcpy (&remote.sin_port, s, sizeof remote.sin_port);
00344         s += sizeof remote.sin_port;
00345         memcpy (&local.sin_port, s, sizeof local.sin_port);
00346         s += sizeof local.sin_port;
00347         memcpy (&remote.sin_addr, s, sizeof remote.sin_addr);
00348         s += sizeof remote.sin_addr;
00349         memcpy (&local.sin_addr, s, sizeof local.sin_addr);
00350         s += sizeof local.sin_addr;
00351         POST(s);
00352 
00353         connect_index = ntohl (connect_index);
00354         listener_index = ntohl (listener_index);
00355 
00356         /* If this was a connect to a listener, then we just slap together
00357            a new connection. */
00358         if (listener_index != -1) {
00359                 omapi_listener_object_t *listener;
00360                 listener = (omapi_listener_object_t *)0;
00361                 omapi_array_foreach_begin (trace_listeners,
00362                                            omapi_listener_object_t, lp) {
00363                         if (lp -> address.sin_port == local.sin_port) {
00364                                 omapi_listener_reference (&listener, lp, MDL);
00365                                 omapi_listener_dereference (&lp, MDL);
00366                                 break;
00367                         } 
00368                 } omapi_array_foreach_end (trace_listeners,
00369                                            omapi_listener_object_t, lp);
00370                 if (!listener) {
00371                         log_error ("%s%ld, addr %s, port %d",
00372                                    "Spurious traced listener connect - index ",
00373                                    (long int)listener_index,
00374                                    inet_ntoa (local.sin_addr),
00375                                    ntohs (local.sin_port));
00376                         return;
00377                 }
00378                 obj = (omapi_connection_object_t *)0;
00379                 status = omapi_listener_connect (&obj, listener, -1, &remote);
00380                 if (status != ISC_R_SUCCESS) {
00381                         log_error ("traced listener connect: %s",
00382                                    isc_result_totext (status));
00383                 }
00384                 if (obj)
00385                         omapi_connection_dereference (&obj, MDL);
00386                 omapi_listener_dereference (&listener, MDL);
00387                 return;
00388         }
00389 
00390         /* Find the matching connect object, if there is one. */
00391         omapi_array_foreach_begin (omapi_connections,
00392                                    omapi_connection_object_t, lp) {
00393             for (i = 0; (lp->connect_list &&
00394                          i < lp->connect_list->count); i++) {
00395                     if (!memcmp (&remote.sin_addr,
00396                                  &lp->connect_list->addresses[i].address,
00397                                  sizeof remote.sin_addr) &&
00398                         (ntohs (remote.sin_port) ==
00399                          lp->connect_list->addresses[i].port)) {
00400                             lp->state = omapi_connection_connected;
00401                             lp->remote_addr = remote;
00402                             lp->remote_addr.sin_family = AF_INET;
00403                             omapi_addr_list_dereference(&lp->connect_list, MDL);
00404                             lp->index = connect_index;
00405                             status = omapi_signal_in((omapi_object_t *)lp,
00406                                                      "connect");
00407                             omapi_connection_dereference (&lp, MDL);
00408                             return;
00409                     }
00410                 }
00411         } omapi_array_foreach_end (omapi_connections,
00412                                    omapi_connection_object_t, lp);
00413                                                  
00414         log_error ("Spurious traced connect - index %ld, addr %s, port %d",
00415                    (long int)connect_index, inet_ntoa (remote.sin_addr),
00416                    ntohs (remote.sin_port));
00417         return;
00418 }
00419 
00420 static void trace_connect_stop (trace_type_t *ttype) { }
00421 
00422 static void trace_disconnect_input (trace_type_t *ttype,
00423                                     unsigned length, char *buf)
00424 {
00425         int32_t *index;
00426         if (length != sizeof *index) {
00427                 log_error ("trace disconnect: wrong length %d", length);
00428                 return;
00429         }
00430         
00431         index = (int32_t *)buf;
00432 
00433         omapi_array_foreach_begin (omapi_connections,
00434                                    omapi_connection_object_t, lp) {
00435                 if (lp -> index == ntohl (*index)) {
00436                         omapi_disconnect ((omapi_object_t *)lp, 1);
00437                         omapi_connection_dereference (&lp, MDL);
00438                         return;
00439                 }
00440         } omapi_array_foreach_end (omapi_connections,
00441                                    omapi_connection_object_t, lp);
00442 
00443         log_error ("trace disconnect: no connection matching index %ld",
00444                    (long int)ntohl (*index));
00445 }
00446 
00447 static void trace_disconnect_stop (trace_type_t *ttype) { }
00448 #endif
00449 
00450 /* Disconnect a connection object from the remote end.   If force is nonzero,
00451    close the connection immediately.   Otherwise, shut down the receiving end
00452    but allow any unsent data to be sent before actually closing the socket. */
00453 
00454 isc_result_t omapi_disconnect (omapi_object_t *h,
00455                                int force)
00456 {
00457         omapi_connection_object_t *c;
00458 
00459 #ifdef DEBUG_PROTOCOL
00460         log_debug ("omapi_disconnect(%s)", force ? "force" : "");
00461 #endif
00462 
00463         c = (omapi_connection_object_t *)h;
00464         if (c -> type != omapi_type_connection)
00465                 return DHCP_R_INVALIDARG;
00466 
00467 #if defined (TRACING)
00468         if (trace_record ()) {
00469                 isc_result_t status;
00470                 int32_t index;
00471 
00472                 index = htonl (c -> index);
00473                 status = trace_write_packet (trace_disconnect,
00474                                              sizeof index, (char *)&index,
00475                                              MDL);
00476                 if (status != ISC_R_SUCCESS) {
00477                         trace_stop ();
00478                         log_error ("trace_write_packet: %s",
00479                                    isc_result_totext (status));
00480                 }
00481         }
00482         if (!trace_playback ()) {
00483 #endif
00484                 if (!force) {
00485                         /* If we're already disconnecting, we don't have to do
00486                            anything. */
00487                         if (c -> state == omapi_connection_disconnecting)
00488                                 return ISC_R_SUCCESS;
00489 
00490                         /* Try to shut down the socket - this sends a FIN to
00491                            the remote end, so that it won't send us any more
00492                            data.   If the shutdown succeeds, and we still
00493                            have bytes left to write, defer closing the socket
00494                            until that's done. */
00495                         if (!shutdown (c -> socket, SHUT_RD)) {
00496                                 if (c -> out_bytes > 0) {
00497                                         c -> state =
00498                                                 omapi_connection_disconnecting;
00499                                         return ISC_R_SUCCESS;
00500                                 }
00501                         }
00502                 }
00503                 close (c -> socket);
00504 #if defined (TRACING)
00505         }
00506 #endif
00507         c -> state = omapi_connection_closed;
00508 
00509 #if 0
00510         /*
00511          * Disconnecting from the I/O object seems incorrect as it doesn't
00512          * cause the I/O object to be cleaned and released.  Previous to
00513          * using the isc socket library this wouldn't have caused a problem
00514          * with the socket library we would have a reference to a closed
00515          * socket.  Instead we now do an unregister to properly free the
00516          * I/O object.
00517          */
00518 
00519         /* Disconnect from I/O object, if any. */
00520         if (h -> outer) {
00521                 if (h -> outer -> inner)
00522                         omapi_object_dereference (&h -> outer -> inner, MDL);
00523                 omapi_object_dereference (&h -> outer, MDL);
00524         }
00525 #else
00526         if (h->outer) {
00527                 omapi_unregister_io_object(h);
00528         }
00529 #endif
00530 
00531         /* If whatever created us registered a signal handler, send it
00532            a disconnect signal. */
00533         omapi_signal (h, "disconnect", h);
00534 
00535         /* Disconnect from protocol object, if any. */
00536         if (h->inner != NULL) {
00537                 if (h->inner->outer != NULL) {
00538                         omapi_object_dereference(&h->inner->outer, MDL);
00539                 }
00540                 omapi_object_dereference(&h->inner, MDL);
00541         }
00542 
00543         /* XXX: the code to free buffers should be in the dereference
00544                 function, but there is no special-purpose function to
00545                 dereference connections, so these just get leaked */
00546         /* Free any buffers */
00547         if (c->inbufs != NULL) {
00548                 omapi_buffer_dereference(&c->inbufs, MDL);
00549         }
00550         c->in_bytes = 0;
00551         if (c->outbufs != NULL) {
00552                 omapi_buffer_dereference(&c->outbufs, MDL);
00553         }
00554         c->out_bytes = 0;
00555 
00556         return ISC_R_SUCCESS;
00557 }
00558 
00559 isc_result_t omapi_connection_require (omapi_object_t *h, unsigned bytes)
00560 {
00561         omapi_connection_object_t *c;
00562 
00563         if (h -> type != omapi_type_connection)
00564                 return DHCP_R_INVALIDARG;
00565         c = (omapi_connection_object_t *)h;
00566 
00567         c -> bytes_needed = bytes;
00568         if (c -> bytes_needed <= c -> in_bytes) {
00569                 return ISC_R_SUCCESS;
00570         }
00571         return DHCP_R_NOTYET;
00572 }
00573 
00574 /* Return the socket on which the dispatcher should wait for readiness
00575    to read, for a connection object.  */
00576 int omapi_connection_readfd (omapi_object_t *h)
00577 {
00578         omapi_connection_object_t *c;
00579         if (h -> type != omapi_type_connection)
00580                 return -1;
00581         c = (omapi_connection_object_t *)h;
00582         if (c -> state != omapi_connection_connected)
00583                 return -1;
00584         return c -> socket;
00585 }
00586 
00587 /*
00588  * Return the socket on which the dispatcher should wait for readiness
00589  * to write, for a connection object.  When bytes are buffered we should
00590  * also poke the dispatcher to tell it to start or re-start watching the
00591  * socket.
00592  */
00593 int omapi_connection_writefd (omapi_object_t *h)
00594 {
00595         omapi_connection_object_t *c;
00596         if (h -> type != omapi_type_connection)
00597                 return -1;
00598         c = (omapi_connection_object_t *)h;
00599         return c->socket;
00600 }
00601 
00602 isc_result_t omapi_connection_connect (omapi_object_t *h)
00603 {
00604         isc_result_t status;
00605 
00606         /*
00607          * We use the INPROGRESS status to indicate that
00608          * we want more from the socket.  In this case we
00609          * have now connected and are trying to write to
00610          * the socket for the first time.  For the signaling
00611          * code this is the same as a SUCCESS so we don't
00612          * pass it on as a signal.
00613          */
00614         status = omapi_connection_connect_internal (h);
00615         if (status == ISC_R_INPROGRESS) 
00616                 return ISC_R_INPROGRESS;
00617 
00618         if (status != ISC_R_SUCCESS)
00619                 omapi_signal (h, "status", status);
00620 
00621         return ISC_R_SUCCESS;
00622 }
00623 
00624 static isc_result_t omapi_connection_connect_internal (omapi_object_t *h)
00625 {
00626         int error = 0;
00627         omapi_connection_object_t *c;
00628         socklen_t sl;
00629         isc_result_t status;
00630 
00631         if (h -> type != omapi_type_connection)
00632                 return DHCP_R_INVALIDARG;
00633         c = (omapi_connection_object_t *)h;
00634 
00635         if (c -> state == omapi_connection_connecting) {
00636                 sl = sizeof error;
00637                 if (getsockopt (c -> socket, SOL_SOCKET, SO_ERROR,
00638                                 (char *)&error, &sl) < 0) {
00639                         omapi_disconnect (h, 1);
00640                         return ISC_R_SUCCESS;
00641                 }
00642                 if (!error)
00643                         c -> state = omapi_connection_connected;
00644         }
00645         if (c -> state == omapi_connection_connecting ||
00646             c -> state == omapi_connection_unconnected) {
00647                 if (c -> cptr >= c -> connect_list -> count) {
00648                         switch (error) {
00649                               case ECONNREFUSED:
00650                                 status = ISC_R_CONNREFUSED;
00651                                 break;
00652                               case ENETUNREACH:
00653                                 status = ISC_R_NETUNREACH;
00654                                 break;
00655                               default:
00656                                 status = uerr2isc (error);
00657                                 break;
00658                         }
00659                         omapi_disconnect (h, 1);
00660                         return status;
00661                 }
00662 
00663                 if (c -> connect_list -> addresses [c -> cptr].addrtype !=
00664                     AF_INET) {
00665                         omapi_disconnect (h, 1);
00666                         return DHCP_R_INVALIDARG;
00667                 }
00668 
00669                 memcpy (&c -> remote_addr.sin_addr,
00670                         &c -> connect_list -> addresses [c -> cptr].address,
00671                         sizeof c -> remote_addr.sin_addr);
00672                 c -> remote_addr.sin_family = AF_INET;
00673                 c -> remote_addr.sin_port =
00674                        htons (c -> connect_list -> addresses [c -> cptr].port);
00675 #if defined (HAVE_SA_LEN)
00676                 c -> remote_addr.sin_len = sizeof c -> remote_addr;
00677 #endif
00678                 memset (&c -> remote_addr.sin_zero, 0,
00679                         sizeof c -> remote_addr.sin_zero);
00680                 ++c -> cptr;
00681 
00682                 error = connect (c -> socket,
00683                                  (struct sockaddr *)&c -> remote_addr,
00684                                  sizeof c -> remote_addr);
00685                 if (error < 0) {
00686                         error = errno;
00687                         if (error != EINPROGRESS) {
00688                                 omapi_disconnect (h, 1);
00689                                 switch (error) {
00690                                       case ECONNREFUSED:
00691                                         status = ISC_R_CONNREFUSED;
00692                                         break;
00693                                       case ENETUNREACH:
00694                                         status = ISC_R_NETUNREACH;
00695                                         break;
00696                                       default:
00697                                         status = uerr2isc (error);
00698                                         break;
00699                                 }
00700                                 return status;
00701                         }
00702                         c -> state = omapi_connection_connecting;
00703                         return DHCP_R_INCOMPLETE;
00704                 }
00705                 c -> state = omapi_connection_connected;
00706         }
00707         
00708         /* I don't know why this would fail, so I'm tempted not to test
00709            the return value. */
00710         sl = sizeof (c -> local_addr);
00711         if (getsockname (c -> socket,
00712                          (struct sockaddr *)&c -> local_addr, &sl) < 0) {
00713         }
00714 
00715         /* Reregister with the I/O object.  If we don't already have an
00716            I/O object this turns into a register call, otherwise we simply
00717            modify the pointers in the I/O object. */
00718 
00719         status = omapi_reregister_io_object (h,
00720                                              omapi_connection_readfd,
00721                                              omapi_connection_writefd,
00722                                              omapi_connection_reader,
00723                                              omapi_connection_writer,
00724                                              omapi_connection_reaper);
00725 
00726         if (status != ISC_R_SUCCESS) {
00727                 omapi_disconnect (h, 1);
00728                 return status;
00729         }
00730 
00731         omapi_signal_in (h, "connect");
00732         omapi_addr_list_dereference (&c -> connect_list, MDL);
00733         return ISC_R_INPROGRESS;
00734 }
00735 
00736 /* Reaper function for connection - if the connection is completely closed,
00737    reap it.   If it's in the disconnecting state, there were bytes left
00738    to write when the user closed it, so if there are now no bytes left to
00739    write, we can close it. */
00740 isc_result_t omapi_connection_reaper (omapi_object_t *h)
00741 {
00742         omapi_connection_object_t *c;
00743 
00744         if (h -> type != omapi_type_connection)
00745                 return DHCP_R_INVALIDARG;
00746 
00747         c = (omapi_connection_object_t *)h;
00748         if (c -> state == omapi_connection_disconnecting &&
00749             c -> out_bytes == 0) {
00750 #ifdef DEBUG_PROTOCOL
00751                 log_debug ("omapi_connection_reaper(): disconnect");
00752 #endif
00753                 omapi_disconnect (h, 1);
00754         }
00755         if (c -> state == omapi_connection_closed) {
00756 #ifdef DEBUG_PROTOCOL
00757                 log_debug ("omapi_connection_reaper(): closed");
00758 #endif
00759                 return ISC_R_NOTCONNECTED;
00760         }
00761         return ISC_R_SUCCESS;
00762 }
00763 
00764 static isc_result_t make_dst_key (dst_key_t **dst_key, omapi_object_t *a) {
00765         omapi_value_t *name      = (omapi_value_t *)0;
00766         omapi_value_t *algorithm = (omapi_value_t *)0;
00767         omapi_value_t *key       = (omapi_value_t *)0;
00768         char *name_str = NULL;
00769         isc_result_t status = ISC_R_SUCCESS;
00770 
00771         if (status == ISC_R_SUCCESS)
00772                 status = omapi_get_value_str
00773                         (a, (omapi_object_t *)0, "name", &name);
00774 
00775         if (status == ISC_R_SUCCESS)
00776                 status = omapi_get_value_str
00777                         (a, (omapi_object_t *)0, "algorithm", &algorithm);
00778 
00779         if (status == ISC_R_SUCCESS)
00780                 status = omapi_get_value_str
00781                         (a, (omapi_object_t *)0, "key", &key);
00782 
00783         if (status == ISC_R_SUCCESS) {
00784                 if ((algorithm->value->type != omapi_datatype_data &&
00785                      algorithm->value->type != omapi_datatype_string) ||
00786                     strncasecmp((char *)algorithm->value->u.buffer.value,
00787                                 NS_TSIG_ALG_HMAC_MD5 ".",
00788                                 algorithm->value->u.buffer.len) != 0) {
00789                         status = DHCP_R_INVALIDARG;
00790                 }
00791         }
00792 
00793         if (status == ISC_R_SUCCESS) {
00794                 name_str = dmalloc (name -> value -> u.buffer.len + 1, MDL);
00795                 if (!name_str)
00796                         status = ISC_R_NOMEMORY;
00797         }
00798 
00799         if (status == ISC_R_SUCCESS) {
00800                 memcpy (name_str,
00801                         name -> value -> u.buffer.value,
00802                         name -> value -> u.buffer.len);
00803                 name_str [name -> value -> u.buffer.len] = 0;
00804 
00805                 status = isclib_make_dst_key(name_str,
00806                                              DHCP_HMAC_MD5_NAME,
00807                                              key->value->u.buffer.value,
00808                                              key->value->u.buffer.len,
00809                                              dst_key);
00810 
00811                 if (*dst_key == NULL)
00812                         status = ISC_R_NOMEMORY;
00813         }
00814 
00815         if (name_str)
00816                 dfree (name_str, MDL);
00817         if (key)
00818                 omapi_value_dereference (&key, MDL);
00819         if (algorithm)
00820                 omapi_value_dereference (&algorithm, MDL);
00821         if (name)
00822                 omapi_value_dereference (&name, MDL);
00823 
00824         return status;
00825 }
00826 
00827 isc_result_t omapi_connection_sign_data (int mode,
00828                                          dst_key_t *key,
00829                                          void **context,
00830                                          const unsigned char *data,
00831                                          const unsigned len,
00832                                          omapi_typed_data_t **result)
00833 {
00834         omapi_typed_data_t *td = (omapi_typed_data_t *)0;
00835         isc_result_t status;
00836         dst_context_t **dctx = (dst_context_t **)context;
00837 
00838         /* Create the context for the dst module */
00839         if (mode & SIG_MODE_INIT) {
00840                 status = dst_context_create(key, dhcp_gbl_ctx.mctx, dctx);
00841                 if (status != ISC_R_SUCCESS) {
00842                         return status;
00843                 }
00844         }
00845 
00846         /* If we have any data add it to the context */
00847         if (len != 0) {
00848                 isc_region_t region;
00849                 region.base   = (unsigned char *)data;
00850                 region.length = len;
00851                 dst_context_adddata(*dctx, &region);
00852         }
00853 
00854         /* Finish the signature and clean up the context */
00855         if (mode & SIG_MODE_FINAL) {
00856                 unsigned int sigsize;
00857                 isc_buffer_t sigbuf;
00858 
00859                 status = dst_key_sigsize(key, &sigsize);
00860                 if (status != ISC_R_SUCCESS) {
00861                         goto cleanup;
00862                 }
00863 
00864                 status = omapi_typed_data_new (MDL, &td,
00865                                                omapi_datatype_data,
00866                                                sigsize);
00867                 if (status != ISC_R_SUCCESS) {
00868                         goto cleanup;
00869                 }
00870 
00871                 isc_buffer_init(&sigbuf, td->u.buffer.value, td->u.buffer.len);
00872                 status = dst_context_sign(*dctx, &sigbuf);
00873                 if (status != ISC_R_SUCCESS) {
00874                         goto cleanup;
00875                 }
00876 
00877                 if (result) {
00878                         omapi_typed_data_reference (result, td, MDL);
00879                 }
00880 
00881         cleanup:
00882                 /* We are done with the context and the td.  On success
00883                  * the td is now referenced from result, on failure we
00884                  * don't need it any more */
00885                 if (td) {
00886                         omapi_typed_data_dereference (&td, MDL);
00887                 }
00888                 dst_context_destroy(dctx);
00889                 return status;
00890         }
00891 
00892         return ISC_R_SUCCESS;
00893 }
00894 
00895 isc_result_t omapi_connection_output_auth_length (omapi_object_t *h,
00896                                                   unsigned *l)
00897 {
00898         omapi_connection_object_t *c;
00899 
00900         if (h->type != omapi_type_connection)
00901                 return DHCP_R_INVALIDARG;
00902         c = (omapi_connection_object_t *)h;
00903 
00904         if (c->out_key == NULL)
00905                 return ISC_R_NOTFOUND;
00906 
00907         return(dst_key_sigsize(c->out_key, l));
00908 }
00909 
00910 isc_result_t omapi_connection_set_value (omapi_object_t *h,
00911                                          omapi_object_t *id,
00912                                          omapi_data_string_t *name,
00913                                          omapi_typed_data_t *value)
00914 {
00915         omapi_connection_object_t *c;
00916         isc_result_t status;
00917 
00918         if (h -> type != omapi_type_connection)
00919                 return DHCP_R_INVALIDARG;
00920         c = (omapi_connection_object_t *)h;
00921 
00922         if (omapi_ds_strcmp (name, "input-authenticator") == 0) {
00923                 if (value && value -> type != omapi_datatype_object)
00924                         return DHCP_R_INVALIDARG;
00925 
00926                 if (c -> in_context) {
00927                         omapi_connection_sign_data (SIG_MODE_FINAL,
00928                                                     c -> in_key,
00929                                                     &c -> in_context,
00930                                                     0, 0,
00931                                                     (omapi_typed_data_t **) 0);
00932                 }
00933 
00934                 if (c->in_key != NULL) {
00935                         dst_key_free(&c->in_key);
00936                 }
00937 
00938                 if (value) {
00939                         status = make_dst_key (&c -> in_key,
00940                                                value -> u.object);
00941                         if (status != ISC_R_SUCCESS)
00942                                 return status;
00943                 }
00944 
00945                 return ISC_R_SUCCESS;
00946         }
00947         else if (omapi_ds_strcmp (name, "output-authenticator") == 0) {
00948                 if (value && value -> type != omapi_datatype_object)
00949                         return DHCP_R_INVALIDARG;
00950 
00951                 if (c -> out_context) {
00952                         omapi_connection_sign_data (SIG_MODE_FINAL,
00953                                                     c -> out_key,
00954                                                     &c -> out_context,
00955                                                     0, 0,
00956                                                     (omapi_typed_data_t **) 0);
00957                 }
00958 
00959                 if (c->out_key != NULL) {
00960                         dst_key_free(&c->out_key);
00961                 }
00962 
00963                 if (value) {
00964                         status = make_dst_key (&c -> out_key,
00965                                                value -> u.object);
00966                         if (status != ISC_R_SUCCESS)
00967                                 return status;
00968                 }
00969 
00970                 return ISC_R_SUCCESS;
00971         }
00972         
00973         if (h -> inner && h -> inner -> type -> set_value)
00974                 return (*(h -> inner -> type -> set_value))
00975                         (h -> inner, id, name, value);
00976         return ISC_R_NOTFOUND;
00977 }
00978 
00979 isc_result_t omapi_connection_get_value (omapi_object_t *h,
00980                                          omapi_object_t *id,
00981                                          omapi_data_string_t *name,
00982                                          omapi_value_t **value)
00983 {
00984         omapi_connection_object_t *c;
00985         omapi_typed_data_t *td = (omapi_typed_data_t *)0;
00986         isc_result_t status;
00987         unsigned int sigsize;
00988 
00989         if (h -> type != omapi_type_connection)
00990                 return DHCP_R_INVALIDARG;
00991         c = (omapi_connection_object_t *)h;
00992 
00993         if (omapi_ds_strcmp (name, "input-signature") == 0) {
00994                 if (!c -> in_key || !c -> in_context)
00995                         return ISC_R_NOTFOUND;
00996 
00997                 status = omapi_connection_sign_data (SIG_MODE_FINAL,
00998                                                      c -> in_key,
00999                                                      &c -> in_context,
01000                                                      0, 0, &td);
01001                 if (status != ISC_R_SUCCESS)
01002                         return status;
01003 
01004                 status = omapi_make_value (value, name, td, MDL);
01005                 omapi_typed_data_dereference (&td, MDL);
01006                 return status;
01007 
01008         } else if (omapi_ds_strcmp (name, "input-signature-size") == 0) {
01009                 if (c->in_key == NULL)
01010                         return ISC_R_NOTFOUND;
01011 
01012                 status = dst_key_sigsize(c->in_key, &sigsize);
01013                 if (status != ISC_R_SUCCESS) {
01014                         return(status);
01015                 }               
01016 
01017                 return omapi_make_int_value(value, name, sigsize, MDL);
01018 
01019         } else if (omapi_ds_strcmp (name, "output-signature") == 0) {
01020                 if (!c -> out_key || !c -> out_context)
01021                         return ISC_R_NOTFOUND;
01022 
01023                 status = omapi_connection_sign_data (SIG_MODE_FINAL,
01024                                                      c -> out_key,
01025                                                      &c -> out_context,
01026                                                      0, 0, &td);
01027                 if (status != ISC_R_SUCCESS)
01028                         return status;
01029 
01030                 status = omapi_make_value (value, name, td, MDL);
01031                 omapi_typed_data_dereference (&td, MDL);
01032                 return status;
01033 
01034         } else if (omapi_ds_strcmp (name, "output-signature-size") == 0) {
01035                 if (c->out_key == NULL)
01036                         return ISC_R_NOTFOUND;
01037 
01038 
01039                 status = dst_key_sigsize(c->out_key, &sigsize);
01040                 if (status != ISC_R_SUCCESS) {
01041                         return(status);
01042                 }               
01043 
01044                 return omapi_make_int_value(value, name, sigsize, MDL);
01045         }
01046         
01047         if (h -> inner && h -> inner -> type -> get_value)
01048                 return (*(h -> inner -> type -> get_value))
01049                         (h -> inner, id, name, value);
01050         return ISC_R_NOTFOUND;
01051 }
01052 
01053 isc_result_t omapi_connection_destroy (omapi_object_t *h,
01054                                        const char *file, int line)
01055 {
01056         omapi_connection_object_t *c;
01057 
01058 #ifdef DEBUG_PROTOCOL
01059         log_debug ("omapi_connection_destroy()");
01060 #endif
01061 
01062         if (h -> type != omapi_type_connection)
01063                 return ISC_R_UNEXPECTED;
01064         c = (omapi_connection_object_t *)(h);
01065         if (c -> state == omapi_connection_connected)
01066                 omapi_disconnect (h, 1);
01067         if (c -> listener)
01068                 omapi_listener_dereference (&c -> listener, file, line);
01069         if (c -> connect_list)
01070                 omapi_addr_list_dereference (&c -> connect_list, file, line);
01071         return ISC_R_SUCCESS;
01072 }
01073 
01074 isc_result_t omapi_connection_signal_handler (omapi_object_t *h,
01075                                               const char *name, va_list ap)
01076 {
01077         if (h -> type != omapi_type_connection)
01078                 return DHCP_R_INVALIDARG;
01079 
01080 #ifdef DEBUG_PROTOCOL
01081         log_debug ("omapi_connection_signal_handler(%s)", name);
01082 #endif
01083         
01084         if (h -> inner && h -> inner -> type -> signal_handler)
01085                 return (*(h -> inner -> type -> signal_handler)) (h -> inner,
01086                                                                   name, ap);
01087         return ISC_R_NOTFOUND;
01088 }
01089 
01090 /* Write all the published values associated with the object through the
01091    specified connection. */
01092 
01093 isc_result_t omapi_connection_stuff_values (omapi_object_t *c,
01094                                             omapi_object_t *id,
01095                                             omapi_object_t *m)
01096 {
01097         if (m -> type != omapi_type_connection)
01098                 return DHCP_R_INVALIDARG;
01099 
01100         if (m -> inner && m -> inner -> type -> stuff_values)
01101                 return (*(m -> inner -> type -> stuff_values)) (c, id,
01102                                                                 m -> inner);
01103         return ISC_R_SUCCESS;
01104 }

Generated on 5 Apr 2014 for ISC DHCP by  doxygen 1.6.1