1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright (c) 2001 by Sun Microsystems, Inc.
  24  * All rights reserved.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  * This module contains the private layer API.
  31  */
  32 
  33 #include <stdio.h>
  34 #include <assert.h>
  35 #include <stdlib.h>
  36 #include <unistd.h>
  37 #include <string.h>
  38 #include <sys/param.h>
  39 #include <libelf.h>
  40 #include <gelf.h>
  41 #include <sys/types.h>
  42 #include <sys/socket.h>
  43 #include <netinet/in.h>
  44 #include <arpa/inet.h>
  45 #include <dlfcn.h>
  46 #include <glob.h>
  47 #include <fcntl.h>
  48 #include <libinetutil.h>
  49 #include <dhcp_svc_public.h>
  50 #include <dhcp_svc_private.h>
  51 
  52 /*
  53  * Threading notes for private layer consumers:
  54  *
  55  * The handles returned from open_dd() may be shared across multiple
  56  * threads with no adverse side effects.  However, it's up to that consumer
  57  * to ensure that all threads have finished using an instance before
  58  * closing the instance or removing the container it's referencing.
  59  * Phrased differently:
  60  *
  61  *      * Consumers must ensure all threads sharing a handle are
  62  *        finished before calling close_dd().
  63  *
  64  *      * Consumers must ensure all threads referencing a container are
  65  *        closed before calling remove_dd().
  66  */
  67 
  68 static boolean_t validate_dd_entry(dsvc_handle_t, const void *, boolean_t);
  69 static int  synch_init(dsvc_handle_t, const char *, uint_t);
  70 static void synch_fini(dsvc_handle_t);
  71 
  72 /*
  73  * Order here should match the function array in <dhcp_svc_private.h>
  74  */
  75 static char     *funcnames[] = {
  76         "status",       "version",      "mklocation",
  77         "list_dt",      "open_dt",      "close_dt",     "remove_dt",
  78         "lookup_dt",    "add_dt",       "modify_dt",    "delete_dt",
  79         "list_dn",      "open_dn",      "close_dn",     "remove_dn",
  80         "lookup_dn",    "add_dn",       "modify_dn",    "delete_dn"
  81 };
  82 
  83 extern dsvc_synch_ops_t dsvcd_synch_ops;
  84 
  85 /*
  86  * Retrieve the current version associated with the datastore named by
  87  * `resource' and store in `converp'.  One might think we could do this via
  88  * a simple readlink(2), but on internal release builds $(ROOTLINKS)
  89  * installs using hardlinks, not symlinks.  For this reason and to make it
  90  * harder for us to be fooled, we'll dredge up the actual soname through
  91  * ELF.  Close your eyes, it's gonna get ugly.
  92  */
  93 static int
  94 get_conver(const char *resource, int *converp)
  95 {
  96         int             elf_fd;
  97         int             i;
  98         GElf_Shdr       gelf_shdr;
  99         GElf_Dyn        gelf_dyn;
 100         Elf_Scn         *elf_scn = NULL;
 101         Elf             *elf_file;
 102         Elf_Data        *elf_data;
 103         char            *soname = NULL;
 104         char            path[MAXPATHLEN];
 105 
 106         (void) snprintf(path, sizeof (path), "%s%s/%s_%s.so", DHCP_CONFOPT_ROOT,
 107             DSVC_MODULE_DIR, DSVC_PUBLIC_PREFIX, resource);
 108 
 109         elf_fd = open(path, O_RDONLY);
 110         if (elf_fd == -1)
 111                 return (DSVC_MODULE_ERR);
 112 
 113         if (elf_version(EV_CURRENT) == EV_NONE) {
 114                 (void) close(elf_fd);
 115                 return (DSVC_INTERNAL);
 116         }
 117 
 118         elf_file = elf_begin(elf_fd, ELF_C_READ, NULL);
 119         if (elf_file == NULL || elf_kind(elf_file) != ELF_K_ELF) {
 120                 (void) close(elf_fd);
 121                 return (DSVC_INTERNAL);
 122         }
 123 
 124         while ((elf_scn = elf_nextscn(elf_file, elf_scn)) != NULL) {
 125                 if (gelf_getshdr(elf_scn, &gelf_shdr) == 0)
 126                         continue;
 127 
 128                 if (gelf_shdr.sh_type != SHT_DYNAMIC)
 129                         continue;
 130 
 131                 elf_data = elf_getdata(elf_scn, NULL);
 132                 if (elf_data == NULL)
 133                         continue;
 134 
 135                 i = 0;
 136                 do {
 137                         (void) gelf_getdyn(elf_data, i++, &gelf_dyn);
 138                         if (gelf_dyn.d_tag == DT_SONAME)
 139                                 soname = elf_strptr(elf_file, gelf_shdr.sh_link,
 140                                     gelf_dyn.d_un.d_ptr);
 141                 } while (gelf_dyn.d_tag != DT_NULL && soname == NULL);
 142         }
 143         if (soname == NULL || sscanf(soname, "%*[^.].so.%d", converp) != 1) {
 144                 (void) elf_end(elf_file);
 145                 (void) close(elf_fd);
 146                 return (DSVC_MODULE_ERR);
 147         }
 148         (void) elf_end(elf_file);
 149         (void) close(elf_fd);
 150 
 151         return (DSVC_SUCCESS);
 152 }
 153 
 154 /*
 155  * Unload a public datastore module.
 156  */
 157 static int
 158 unload_public_module(void **instance, dsvc_splapi_t *api)
 159 {
 160         static dsvc_splapi_t    null_api;
 161 
 162         if (dlclose(*instance) != 0)
 163                 return (DSVC_MODULE_UNLOAD_ERR);
 164 
 165         *instance = NULL;
 166         *api = null_api;
 167 
 168         return (DSVC_SUCCESS);
 169 }
 170 
 171 /*
 172  * Load public datastore module.  Validates version of module.  Returns
 173  * instance of opened module, and populates the api argument with the
 174  * function addresses exporting the API.
 175  */
 176 static int
 177 load_public_module(dsvc_datastore_t *ddp, void **instance, dsvc_splapi_t *api)
 178 {
 179         int             i, v;
 180         dsvc_splfuncp_t configure;
 181         char            path[MAXPATHLEN];
 182 
 183         (void) snprintf(path, sizeof (path), "%s%s/%s_%s.so", DHCP_CONFOPT_ROOT,
 184             DSVC_MODULE_DIR, DSVC_PUBLIC_PREFIX, ddp->d_resource);
 185 
 186         if (ddp->d_conver != DSVC_CUR_CONVER)
 187                 (void) snprintf(path, sizeof (path), "%s.%d", path,
 188                     ddp->d_conver);
 189 
 190         *instance = dlopen(path, RTLD_LAZY|RTLD_GROUP|RTLD_WORLD);
 191         if (*instance == NULL)
 192                 return (DSVC_MODULE_LOAD_ERR);
 193 
 194         /*
 195          * No requirement to duplicate the names - we can always reference
 196          * the same set.
 197          */
 198         api->version = (dsvc_splfuncp_t)dlsym(*instance, "version");
 199         if (api->version == NULL || api->version(&v) != DSVC_SUCCESS ||
 200             v != DSVC_PUBLIC_VERSION) {
 201                 (void) unload_public_module(instance, api);
 202                 return (DSVC_MODULE_VERSION);
 203         }
 204 
 205         configure = (dsvc_splfuncp_t)dlsym(*instance, "configure");
 206         if (configure != NULL) {
 207                 if (configure(ddp->d_config) != DSVC_SUCCESS) {
 208                         (void) unload_public_module(instance, api);
 209                         return (DSVC_MODULE_CFG_ERR);
 210                 }
 211         }
 212 
 213         for (i = 0; i < DSVC_NSPLFUNCS; i++) {
 214                 if ((((dsvc_splfuncp_t *)api)[i] =
 215                     (dsvc_splfuncp_t)dlsym(*instance, funcnames[i])) == NULL) {
 216                         (void) unload_public_module(instance, api);
 217                         return (DSVC_MODULE_ERR);
 218                 }
 219         }
 220 
 221         /*
 222          * Caller requested the current version; fill in what that current
 223          * version is.
 224          */
 225         if (ddp->d_conver == DSVC_CUR_CONVER) {
 226                 int     error;
 227                 error = get_conver(ddp->d_resource, &ddp->d_conver);
 228                 if (error != DSVC_SUCCESS) {
 229                         (void) unload_public_module(instance, api);
 230                         return (error);
 231                 }
 232         }
 233 
 234         return (DSVC_SUCCESS);
 235 }
 236 
 237 /*
 238  * Return a dynamically-allocated null-terminated list of the available
 239  * modules stored in the module directory.  A count of the available
 240  * modules is stored in the num argument.  Caller is responsible for
 241  * freeing the list.
 242  */
 243 int
 244 enumerate_dd(char ***modules, int *nump)
 245 {
 246         int     i, retval;
 247         char    *ptr;
 248         glob_t  globbuf;
 249         char    globpat[MAXPATHLEN];
 250 
 251         if (modules == NULL || nump == NULL)
 252                 return (DSVC_INVAL);
 253 
 254         (void) snprintf(globpat, sizeof (globpat), "%s%s/%s_*\\.so",
 255             DHCP_CONFOPT_ROOT, DSVC_MODULE_DIR, DSVC_PUBLIC_PREFIX);
 256 
 257         retval = glob(globpat, GLOB_NOSORT, NULL, &globbuf);
 258         if (retval != 0) {
 259                 globfree(&globbuf);
 260                 switch (retval) {
 261                 case GLOB_NOMATCH:
 262                         *nump = 0;
 263                         *modules = NULL;
 264                         return (DSVC_SUCCESS);
 265                 case GLOB_NOSPACE:
 266                         return (DSVC_NO_MEMORY);
 267                 default:
 268                         return (DSVC_INTERNAL);
 269                 }
 270         }
 271 
 272         *modules = calloc(globbuf.gl_pathc, sizeof (char **));
 273         if (*modules == NULL) {
 274                 globfree(&globbuf);
 275                 return (DSVC_NO_MEMORY);
 276         }
 277 
 278         for (i = 0; i < globbuf.gl_pathc; i++) {
 279                 ptr = strrchr(globbuf.gl_pathv[i], '/');
 280                 if (ptr == NULL)
 281                         ptr = globbuf.gl_pathv[i];
 282                 else
 283                         ptr++;
 284                 (*modules)[i] = malloc(strlen(ptr) + 1);
 285                 if ((*modules)[i] == NULL) {
 286                         while (i--)
 287                                 free((*modules)[i]);
 288                         free(modules);
 289                         globfree(&globbuf);
 290                         return (DSVC_NO_MEMORY);
 291                 }
 292 
 293                 (void) sscanf(ptr, "%*[^_]_%[^.]", (*modules)[i]);
 294         }
 295 
 296         globfree(&globbuf);
 297         *nump = i;
 298         return (DSVC_SUCCESS);
 299 }
 300 
 301 /*
 302  * Check the status of the underlying service supporting the data store.
 303  * Caller is responsible for freeing any dynamically allocated arguments.
 304  */
 305 int
 306 status_dd(dsvc_datastore_t *ddp)
 307 {
 308         void            *instance;
 309         dsvc_splapi_t   api;
 310         int             error;
 311 
 312         error = load_public_module(ddp, &instance, &api);
 313         if (error != DSVC_SUCCESS)
 314                 return (error);
 315 
 316         error = api.status(ddp->d_location);
 317 
 318         (void) unload_public_module(&instance, &api);
 319 
 320         return (error);
 321 }
 322 
 323 /*
 324  * Create within the data store the "location" where containers will be
 325  * stored.
 326  */
 327 int
 328 mklocation_dd(dsvc_datastore_t *ddp)
 329 {
 330         void            *instance;
 331         dsvc_splapi_t   api;
 332         int             error;
 333 
 334         error = load_public_module(ddp, &instance, &api);
 335         if (error != DSVC_SUCCESS)
 336                 return (error);
 337 
 338         error = api.mklocation(ddp->d_location);
 339 
 340         (void) unload_public_module(&instance, &api);
 341 
 342         return (error);
 343 }
 344 
 345 /*
 346  * Return a list of the current container objects of type 'type' located at
 347  * 'location' in listppp.  Return the number of list elements in 'count'.
 348  */
 349 int
 350 list_dd(dsvc_datastore_t *ddp, dsvc_contype_t type, char ***listppp,
 351     uint_t *count)
 352 {
 353         void            *instance;
 354         dsvc_splapi_t   api;
 355         int             error;
 356 
 357         error = load_public_module(ddp, &instance, &api);
 358         if (error != DSVC_SUCCESS)
 359                 return (error);
 360 
 361         if (type == DSVC_DHCPTAB)
 362                 error = api.list_dt(ddp->d_location, listppp, count);
 363         else
 364                 error = api.list_dn(ddp->d_location, listppp, count);
 365 
 366         (void) unload_public_module(&instance, &api);
 367 
 368         return (error);
 369 }
 370 
 371 /*
 372  * Creates or opens the DHCP container of type called name within the
 373  * specific datastore referenced by ddp, and returns a handle to this
 374  * container in the handp argument.  New containers are created with
 375  * the identity of the caller.  Caller is responsible for freeing any
 376  * dynamically allocated arguments.  The returned handle instance must
 377  * be released by calling close_dd().
 378  */
 379 int
 380 open_dd(dsvc_handle_t *handp, dsvc_datastore_t *ddp, dsvc_contype_t type,
 381     const char *name, uint_t flags)
 382 {
 383         int                     error;
 384         dsvc_handle_t           hp;
 385 
 386         *handp = NULL;
 387 
 388         if (type == DSVC_DHCPNETWORK && name == NULL)
 389                 return (DSVC_INVAL);
 390 
 391         if (flags & DSVC_CREATE && (flags & DSVC_WRITE) == 0)
 392                 return (DSVC_INVAL);
 393 
 394         if ((hp = calloc(1, sizeof (struct dsvc_handle))) == NULL)
 395                 return (DSVC_NO_MEMORY);
 396 
 397         if (type == DSVC_DHCPNETWORK) {
 398                 hp->d_conid.c_net.s_addr = ntohl(inet_addr(name));
 399                 if (hp->d_conid.c_net.s_addr == INADDR_BROADCAST) {
 400                         free(hp);
 401                         return (DSVC_INVAL);
 402                 }
 403                 get_netmask4(&hp->d_conid.c_net, &hp->d_conid.c_mask);
 404         }
 405 
 406         error = load_public_module(ddp, &hp->d_instance, &hp->d_api);
 407         if (error != DSVC_SUCCESS) {
 408                 free(hp);
 409                 return (error);
 410         }
 411 
 412         hp->d_type = type;
 413         hp->d_desc.d_conver = ddp->d_conver;
 414         hp->d_desc.d_resource = strdup(ddp->d_resource);
 415         hp->d_desc.d_location = strdup(ddp->d_location);
 416         if (hp->d_desc.d_resource == NULL || hp->d_desc.d_location == NULL) {
 417                 error = DSVC_NO_MEMORY;
 418                 goto error;
 419         }
 420 
 421         /*
 422          * Initialize the synchronization strategy (may not be any).
 423          */
 424         error = synch_init(hp, name, flags);
 425         if (error != DSVC_SUCCESS)
 426                 goto error;
 427 
 428         if (type == DSVC_DHCPTAB)
 429                 error = hp->d_api.open_dt(&hp->d_hand, ddp->d_location, flags);
 430         else
 431                 error = hp->d_api.open_dn(&hp->d_hand, ddp->d_location, flags,
 432                     &hp->d_conid.c_net, &hp->d_conid.c_mask);
 433 
 434         if (error != DSVC_SUCCESS) {
 435                 if (hp->d_synch != NULL)
 436                         synch_fini(hp);
 437                 goto error;
 438         }
 439 
 440         *handp = hp;
 441         return (DSVC_SUCCESS);
 442 error:
 443         (void) unload_public_module(&hp->d_instance, &hp->d_api);
 444         free(hp->d_desc.d_resource);
 445         free(hp->d_desc.d_location);
 446         free(hp);
 447         return (error);
 448 }
 449 
 450 /*
 451  * Remove DHCP container called name of type within the specific datastore
 452  * referenced by ddp.  Caller is responsible for freeing any dynamically
 453  * allocated arguments.
 454  */
 455 int
 456 remove_dd(dsvc_datastore_t *ddp, dsvc_contype_t type, const char *name)
 457 {
 458         void            *instance;
 459         int             error;
 460         dsvc_splapi_t   api;
 461         struct in_addr  ip, mask;
 462 
 463         if (type != DSVC_DHCPTAB) {
 464                 if ((ip.s_addr = inet_addr(name)) == INADDR_BROADCAST)
 465                         return (DSVC_INVAL);
 466                 ip.s_addr = ntohl(ip.s_addr);
 467                 get_netmask4(&ip, &mask);
 468         }
 469 
 470         error = load_public_module(ddp, &instance, &api);
 471         if (error != DSVC_SUCCESS)
 472                 return (error);
 473 
 474         /* remove the DHCP container */
 475         if (type == DSVC_DHCPTAB)
 476                 error = api.remove_dt(ddp->d_location);
 477         else
 478                 error = api.remove_dn(ddp->d_location, &ip, &mask);
 479 
 480         (void) unload_public_module(&instance, &api);
 481 
 482         return (error);
 483 }
 484 
 485 /*
 486  * Delete the handle instance referenced by hand. Frees hand if the close
 487  * operation was successful.  NOTE: Caller is responsible for synchronizing
 488  * multiple threads such that close_dd() is called when all consuming
 489  * threads have exited.
 490  */
 491 int
 492 close_dd(dsvc_handle_t *handp)
 493 {
 494         int     error;
 495 
 496         if (handp == NULL || DSVC_HANDLE_INVAL(*handp))
 497                 return (DSVC_INVAL);
 498 
 499         if ((*handp)->d_type == DSVC_DHCPTAB)
 500                 error = (*handp)->d_api.close_dt(&((*handp)->d_hand));
 501         else
 502                 error = (*handp)->d_api.close_dn(&((*handp)->d_hand));
 503 
 504         if (error == DSVC_SUCCESS) {
 505                 error = unload_public_module(&(*handp)->d_instance,
 506                     &(*handp)->d_api);
 507                 if ((*handp)->d_synch != NULL)
 508                         synch_fini(*handp);
 509                 free((*handp)->d_desc.d_resource);
 510                 free((*handp)->d_desc.d_location);
 511                 free(*handp);
 512                 *handp = NULL;
 513         }
 514 
 515         return (error);
 516 }
 517 
 518 /*
 519  * Searches hand container for records that match the query described by
 520  * the combination of query and targetp. If the partial field is true, then
 521  * lookup operations that have located some records but are unable to
 522  * complete entirely are allowed.  The query argument consists of 2 fields,
 523  * each 16 bits long. The lower 16 bits selects which fields in the targetp
 524  * record are to be considered in the query. The upper 16 bits identifies
 525  * whether a particular field value must match (bit set) or not match (bit
 526  * clear). Unused bits in both 16 bit fields must be 0. The count argument
 527  * specifies the maximum number of matching records to return. A count
 528  * value of -1 requests that all matching records be returned. recordsp is
 529  * set to point to the resulting list of records; if recordsp is NULL then
 530  * no records are actually returned. Note that these records are
 531  * dynamically allocated, thus the caller is responsible for freeing them.
 532  * The number of records found is returned in nrecordsp; a value of 0 means
 533  * that no records matched the query.
 534  */
 535 int
 536 lookup_dd(dsvc_handle_t hand, boolean_t partial, uint_t query,
 537     int count, const void *targetp, void **recordsp, uint_t *nrecordsp)
 538 {
 539         uint_t  mask = 0;
 540         int     error;
 541         void    *unlock_cookie;
 542         int     (*lookup)();
 543 
 544         if (targetp == NULL || nrecordsp == NULL || DSVC_HANDLE_INVAL(hand))
 545                 return (DSVC_INVAL);
 546 
 547         if (hand->d_type == DSVC_DHCPTAB) {
 548                 mask = (uint_t)~DT_QALL;
 549                 lookup = hand->d_api.lookup_dt;
 550         } else {
 551                 mask = (uint_t)~DN_QALL;
 552                 lookup = hand->d_api.lookup_dn;
 553         }
 554 
 555         /* validate query */
 556         if (((query & 0xffff) & mask) || ((query >> 16) & mask))
 557                 return (DSVC_INVAL);
 558 
 559         /*
 560          * XXX: need to validate the `targetp' -- what a mess cuz only the
 561          *      fields lit in `query' need to be valid.
 562          */
 563 
 564         if (hand->d_synch != NULL) {
 565                 error = DSVC_SYNCH_RDLOCK(hand->d_synch, &unlock_cookie);
 566                 if (error != DSVC_SUCCESS)
 567                         return (error);
 568         }
 569 
 570         error = lookup(hand->d_hand, partial, query, count, targetp, recordsp,
 571             nrecordsp);
 572 
 573         if (hand->d_synch != NULL)
 574                 (void) DSVC_SYNCH_UNLOCK(hand->d_synch, unlock_cookie);
 575 
 576         return (error);
 577 }
 578 
 579 /*
 580  * Frees the record pointed to by entryp.
 581  */
 582 void
 583 free_dd(dsvc_handle_t hand, void *entryp)
 584 {
 585         if (DSVC_HANDLE_INVAL(hand) || entryp == NULL)
 586                 return;
 587 
 588         if (hand->d_type == DSVC_DHCPTAB)
 589                 free_dtrec((dt_rec_t *)entryp);
 590         else
 591                 free_dnrec((dn_rec_t *)entryp);
 592 }
 593 
 594 /*
 595  * Frees the list of records pointed to by listp.
 596  */
 597 void
 598 free_dd_list(dsvc_handle_t hand, void *listp)
 599 {
 600         if (DSVC_HANDLE_INVAL(hand) || listp == NULL)
 601                 return;
 602 
 603         if (hand->d_type == DSVC_DHCPTAB)
 604                 free_dtrec_list((dt_rec_list_t *)listp);
 605         else
 606                 free_dnrec_list((dn_rec_list_t *)listp);
 607 }
 608 
 609 /*
 610  * Add the record newp to the DHCP container hand. newp's update signature
 611  * will be updated by the public layer module doing the update. Caller is
 612  * responsible for freeing newp if it was dynamically allocated.
 613  */
 614 int
 615 add_dd_entry(dsvc_handle_t hand, void *newp)
 616 {
 617         int     error;
 618         void    *unlock_cookie;
 619 
 620         if (DSVC_HANDLE_INVAL(hand))
 621                 return (DSVC_INVAL);
 622 
 623         if (!validate_dd_entry(hand, newp, B_FALSE))
 624                 return (DSVC_INVAL);
 625 
 626         if (hand->d_synch != NULL) {
 627                 error = DSVC_SYNCH_WRLOCK(hand->d_synch, &unlock_cookie);
 628                 if (error != DSVC_SUCCESS)
 629                         return (error);
 630         }
 631 
 632         if (hand->d_type == DSVC_DHCPTAB)
 633                 error = hand->d_api.add_dt(hand->d_hand, newp);
 634         else
 635                 error = hand->d_api.add_dn(hand->d_hand, newp);
 636 
 637         if (hand->d_synch != NULL)
 638                 (void) DSVC_SYNCH_UNLOCK(hand->d_synch, unlock_cookie);
 639 
 640         return (error);
 641 }
 642 
 643 /*
 644  * Modify the record origp with the record newp in the DHCP container hand.
 645  * newp's update signature will be updated by the public layer module doing
 646  * the update. Caller is responsible for freeing origp and/or newp if they
 647  * were dynamically allocated.
 648  */
 649 int
 650 modify_dd_entry(dsvc_handle_t hand, const void *origp, void *newp)
 651 {
 652         int     error;
 653         void    *unlock_cookie;
 654 
 655         if (DSVC_HANDLE_INVAL(hand))
 656                 return (DSVC_INVAL);
 657 
 658         if (!validate_dd_entry(hand, origp, B_TRUE))
 659                 return (DSVC_INVAL);
 660 
 661         if (!validate_dd_entry(hand, newp, B_FALSE))
 662                 return (DSVC_INVAL);
 663 
 664         if (hand->d_synch != NULL) {
 665                 error = DSVC_SYNCH_WRLOCK(hand->d_synch, &unlock_cookie);
 666                 if (error != DSVC_SUCCESS)
 667                         return (error);
 668         }
 669 
 670         if (hand->d_type == DSVC_DHCPTAB)
 671                 error = hand->d_api.modify_dt(hand->d_hand, origp, newp);
 672         else
 673                 error = hand->d_api.modify_dn(hand->d_hand, origp, newp);
 674 
 675         if (hand->d_synch != NULL)
 676                 (void) DSVC_SYNCH_UNLOCK(hand->d_synch, unlock_cookie);
 677 
 678         return (error);
 679 }
 680 
 681 /*
 682  * Deletes the record referred to by entryp from the DHCP container hand.
 683  * Caller is responsible for freeing entryp if it was dynamically
 684  * allocated.
 685  */
 686 int
 687 delete_dd_entry(dsvc_handle_t hand, void *entryp)
 688 {
 689         int     error;
 690         void    *unlock_cookie;
 691 
 692         if (DSVC_HANDLE_INVAL(hand))
 693                 return (DSVC_INVAL);
 694 
 695         if (!validate_dd_entry(hand, entryp, B_TRUE))
 696                 return (DSVC_INVAL);
 697 
 698         if (hand->d_synch != NULL) {
 699                 error = DSVC_SYNCH_WRLOCK(hand->d_synch, &unlock_cookie);
 700                 if (error != DSVC_SUCCESS)
 701                         return (error);
 702         }
 703 
 704         if (hand->d_type == DSVC_DHCPTAB)
 705                 error = hand->d_api.delete_dt(hand->d_hand, entryp);
 706         else
 707                 error = hand->d_api.delete_dn(hand->d_hand, entryp);
 708 
 709         if (hand->d_synch != NULL)
 710                 (void) DSVC_SYNCH_UNLOCK(hand->d_synch, unlock_cookie);
 711 
 712         return (error);
 713 }
 714 
 715 /*
 716  * Validate that the DHCP network record `dn' is correctly formed; returns
 717  * B_TRUE if it is, B_FALSE if it's not.  If `justkey' is set, then only
 718  * validate the key.
 719  */
 720 static boolean_t
 721 validate_dnrec(dsvc_handle_t hand, const dn_rec_t *dn, boolean_t justkey)
 722 {
 723         /* CIP must be on container's network */
 724         if (hand->d_conid.c_net.s_addr !=
 725             (dn->dn_cip.s_addr & hand->d_conid.c_mask.s_addr))
 726                 return (B_FALSE);
 727 
 728         if (justkey)
 729                 return (B_TRUE);
 730 
 731         if (dn->dn_cid_len < 1 || dn->dn_cid_len > DN_MAX_CID_LEN)
 732                 return (B_FALSE);
 733 
 734         if ((dn->dn_flags & ~DN_FALL) != 0)
 735                 return (B_FALSE);
 736 
 737         return (B_TRUE);
 738 }
 739 
 740 /*
 741  * Validate that the dhcptab record `dt' is correctly formed; returns
 742  * B_TRUE if it is, B_FALSE if it's not.  If `justkey' is set, then only
 743  * validate the key.
 744  */
 745 /* ARGSUSED */
 746 static boolean_t
 747 validate_dtrec(dsvc_handle_t hand, const dt_rec_t *dt, boolean_t justkey)
 748 {
 749         return (dt->dt_type == DT_SYMBOL || dt->dt_type == DT_MACRO);
 750 }
 751 
 752 /*
 753  * Validate that a DHCP record of type `hand->d_type' is correctly formed;
 754  * returns B_TRUE if it is, B_FALSE if it's not.  If `justkey' is set, then
 755  * only validate the key.
 756  */
 757 static boolean_t
 758 validate_dd_entry(dsvc_handle_t hand, const void *entryp, boolean_t justkey)
 759 {
 760         if (entryp == NULL)
 761                 return (B_FALSE);
 762 
 763         if (hand->d_type == DSVC_DHCPTAB)
 764                 return (validate_dtrec(hand, (dt_rec_t *)entryp, justkey));
 765         else if (hand->d_type == DSVC_DHCPNETWORK)
 766                 return (validate_dnrec(hand, (dn_rec_t *)entryp, justkey));
 767 
 768         return (B_FALSE);
 769 }
 770 
 771 /*
 772  * Get the type of synchronization needed for this module and store in
 773  * `synchtypep'.  Returns a DSVC_* code.  This function is exported so that
 774  * dsvclockd(1M) can use it.
 775  */
 776 int
 777 module_synchtype(dsvc_datastore_t *ddp, dsvc_synchtype_t *synchtypep)
 778 {
 779         void                    *instance;
 780         dsvc_splapi_t           api;
 781         dsvc_synchtype_t        *dsvc_synchtypep;
 782 
 783         if (load_public_module(ddp, &instance, &api) != DSVC_SUCCESS)
 784                 return (DSVC_INTERNAL);
 785 
 786         dsvc_synchtypep = dlsym(instance, "dsvc_synchtype");
 787         if (dsvc_synchtypep != NULL)
 788                 *synchtypep = *dsvc_synchtypep;
 789         else
 790                 *synchtypep = DSVC_SYNCH_NONE;
 791 
 792         (void) unload_public_module(&instance, &api);
 793 
 794         return (DSVC_SUCCESS);
 795 }
 796 
 797 /*
 798  * Initialize private-layer synchronization on handle `hand' for container
 799  * `conname'; `flags' is the same flags passed into open_dd().  If there's
 800  * no synchronization needed, always succeeds.  Returns a DSVC_* code.
 801  */
 802 int
 803 synch_init(dsvc_handle_t hand, const char *conname, uint_t flags)
 804 {
 805         dsvc_synchtype_t        synchtype;
 806         dsvc_synch_t            *sp;
 807         int                     error;
 808         int                     (*mkloctoken)(const char *, char *, size_t);
 809 
 810         error = module_synchtype(&hand->d_desc, &synchtype);
 811         if (error != DSVC_SUCCESS)
 812                 return (error);
 813 
 814         if (synchtype == DSVC_SYNCH_NONE)
 815                 return (DSVC_SUCCESS);
 816 
 817         sp = malloc(sizeof (dsvc_synch_t));
 818         if (sp == NULL)
 819                 return (DSVC_NO_MEMORY);
 820 
 821         sp->s_conname = strdup(conname);
 822         if (sp->s_conname == NULL) {
 823                 free(sp);
 824                 return (DSVC_NO_MEMORY);
 825         }
 826         sp->s_nonblock       = flags & DSVC_NONBLOCK;
 827         sp->s_datastore = &hand->d_desc;
 828 
 829         mkloctoken = (int (*)())dlsym(hand->d_instance, "mkloctoken");
 830         if (mkloctoken == NULL) {
 831                 (void) strlcpy(sp->s_loctoken, sp->s_datastore->d_location,
 832                     sizeof (sp->s_loctoken));
 833         } else {
 834                 error = mkloctoken(sp->s_datastore->d_location, sp->s_loctoken,
 835                     sizeof (sp->s_loctoken));
 836                 if (error != DSVC_SUCCESS) {
 837                         free(sp->s_conname);
 838                         free(sp);
 839                         return (error);
 840                 }
 841         }
 842 
 843         /*
 844          * The only synchtype supported is DSVC_SYNCH_DSVCD; if this
 845          * changes, we'll need to enhance this.
 846          */
 847         assert((synchtype & DSVC_SYNCH_STRATMASK) == DSVC_SYNCH_DSVCD);
 848         sp->s_ops = &dsvcd_synch_ops;
 849 
 850         error = DSVC_SYNCH_INIT(sp, synchtype & DSVC_SYNCH_FLAGMASK);
 851         if (error != DSVC_SUCCESS) {
 852                 free(sp->s_conname);
 853                 free(sp);
 854                 return (error);
 855         }
 856 
 857         hand->d_synch = sp;
 858         return (DSVC_SUCCESS);
 859 }
 860 
 861 /*
 862  * Finish using private-layer synchronization on handle `hand'.
 863  */
 864 void
 865 synch_fini(dsvc_handle_t hand)
 866 {
 867         DSVC_SYNCH_FINI(hand->d_synch);
 868         free(hand->d_synch->s_conname);
 869         free(hand->d_synch);
 870         hand->d_synch = NULL;
 871 }