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 2005 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  * This file contains public functions for managing DHCP network
  31  * containers.  For the semantics of these functions, please see the
  32  * Enterprise DHCP Architecture Document.
  33  */
  34 
  35 #include <sys/types.h>
  36 #include <sys/socket.h>
  37 #include <netinet/in.h>
  38 #include <stdlib.h>
  39 #include <arpa/inet.h>
  40 #include <sys/stat.h>
  41 #include <string.h>
  42 #include <errno.h>
  43 #include <unistd.h>
  44 #include <fcntl.h>
  45 #include <alloca.h>
  46 #include <dhcp_svc_public.h>
  47 #include <dirent.h>
  48 #include <libgen.h>
  49 #include <libinetutil.h>
  50 #include <sys/mman.h>
  51 
  52 #include "dhcp_network.h"
  53 #include "util.h"
  54 
  55 static void net2path(char *, size_t, const char *, ipaddr_t, const char *);
  56 static boolean_t record_match(char *[], dn_rec_t *, const dn_rec_t *, uint_t);
  57 static int write_rec(int, dn_rec_t *, off_t);
  58 
  59 /* ARGSUSED */
  60 int
  61 open_dn(void **handlep, const char *location, uint_t flags,
  62     const struct in_addr *netp, const struct in_addr *maskp)
  63 {
  64         char            connet[INET_ADDRSTRLEN];
  65         char            dnpath[MAXPATHLEN];
  66         unsigned int    conver;
  67         dn_handle_t     *dhp;
  68         FILE            *fp;
  69         int             retval;
  70         int             i, nelems;
  71         char            nl;
  72         struct in_addr  net_nbo;
  73         int             fd;
  74 
  75         dhp = malloc(sizeof (dn_handle_t));
  76         if (dhp == NULL)
  77                 return (DSVC_NO_MEMORY);
  78 
  79         dhp->dh_net = netp->s_addr;
  80         dhp->dh_oflags = flags;
  81         (void) strlcpy(dhp->dh_location, location, MAXPATHLEN);
  82 
  83         net2path(dnpath, MAXPATHLEN, location, netp->s_addr, "");
  84         retval = open_file(dnpath, flags, &fd);
  85         if (retval != DSVC_SUCCESS) {
  86                 free(dhp);
  87                 return (retval);
  88         }
  89 
  90         fp = fdopen(fd, flags & DSVC_WRITE ? "r+" : "r");
  91         if (fp == NULL) {
  92                 (void) close(fd);
  93                 free(dhp);
  94                 return (DSVC_INTERNAL);
  95         }
  96 
  97         if (flags & DSVC_CREATE) {
  98                 /*
  99                  * We just created the per-network container; put the
 100                  * header on for future use...
 101                  */
 102                 net_nbo.s_addr = htonl(netp->s_addr);
 103                 (void) inet_ntop(AF_INET, &net_nbo, connet, INET_ADDRSTRLEN);
 104 
 105                 for (i = 0; connet[i] != '\0'; i++)
 106                         if (connet[i] == '.')
 107                                 connet[i] = '_';
 108 
 109                 retval = fprintf(fp, "# SUNWfiles%u_%s\n", DSVC_CONVER, connet);
 110                 if (retval < 0 || fflush(fp) == EOF) {
 111                         (void) fclose(fp);
 112                         (void) free(dhp);
 113                         return (DSVC_INTERNAL);
 114                 }
 115 
 116                 (void) fprintf(fp, "#\n# Do NOT edit this file by hand -- use");
 117                 (void) fprintf(fp, " pntadm(1M) or dhcpmgr(1M) instead\n#\n");
 118         } else {
 119                 /*
 120                  * Container already exists; sanity check against the
 121                  * header that's on-disk.
 122                  */
 123                 nelems = fscanf(fp, "#%*1[ ]SUNWfiles%u_%15s%c", &conver,
 124                     connet, &nl);
 125 
 126                 for (i = 0; connet[i] != '\0'; i++)
 127                         if (connet[i] == '_')
 128                                 connet[i] = '.';
 129 
 130                 if (nelems != 3 || inet_addr(connet) != htonl(netp->s_addr) ||
 131                     conver != DSVC_CONVER || nl != '\n') {
 132                         (void) fclose(fp);
 133                         (void) free(dhp);
 134                         return (DSVC_INTERNAL);
 135                 }
 136         }
 137 
 138         (void) fclose(fp);
 139         *handlep = dhp;
 140         return (DSVC_SUCCESS);
 141 }
 142 
 143 int
 144 close_dn(void **handlep)
 145 {
 146         free(*handlep);
 147         return (DSVC_SUCCESS);
 148 }
 149 
 150 int
 151 remove_dn(const char *dir, const struct in_addr *netp)
 152 {
 153         char dnpath[MAXPATHLEN];
 154 
 155         net2path(dnpath, MAXPATHLEN, dir, netp->s_addr, "");
 156         if (unlink(dnpath) == -1)
 157                 return (syserr_to_dsvcerr(errno));
 158 
 159         return (DSVC_SUCCESS);
 160 }
 161 
 162 /*
 163  * Internal version lookup routine used by both lookup_dn() and
 164  * update_dn(); same semantics as lookup_dn() except that the `partial'
 165  * argument has been generalized into a `flags' field.
 166  */
 167 static int
 168 find_dn(int fd, uint_t flags, uint_t query, int count, const dn_rec_t *targetp,
 169     dn_rec_list_t **recordsp, uint_t *nrecordsp)
 170 {
 171         int             retval = DSVC_SUCCESS;
 172         char            *fields[DNF_FIELDS];
 173         uint_t          nrecords;
 174         dn_rec_t        dn, *recordp;
 175         dn_rec_list_t   *records, *new_records;
 176         unsigned int    nfields;
 177         struct stat     st;
 178         struct in_addr  cip_nbo;
 179         char            *ent0, *ent, *entend;
 180         char            cip[INET_ADDRSTRLEN + 2];
 181 
 182         /*
 183          * Page the whole container into memory via mmap() so we can scan it
 184          * quickly; map it MAP_PRIVATE so that we can change newlines to
 185          * NULs without changing the actual container itself.
 186          */
 187         if (fstat(fd, &st) == -1 || st.st_size < 1)
 188                 return (DSVC_INTERNAL);
 189 
 190         ent0 = mmap(0, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
 191         if (ent0 == MAP_FAILED)
 192                 return (DSVC_INTERNAL);
 193 
 194         /*
 195          * NUL-terminate the last byte (which should be a newline) so that
 196          * we can safely use string functions on the mapped container.
 197          */
 198         ent0[st.st_size - 1] = '\0';
 199 
 200         /*
 201          * If we're searching by client IP address, then build a target
 202          * string we can use to find it quickly.
 203          */
 204         if (DSVC_QISEQ(query, DN_QCIP)) {
 205                 cip[0] = '\n';
 206                 cip_nbo.s_addr = htonl(targetp->dn_cip.s_addr);
 207                 (void) inet_ntop(AF_INET, &cip_nbo, cip + 1, INET_ADDRSTRLEN);
 208                 (void) strlcat(cip, "|", sizeof (cip));
 209         }
 210 
 211         records = NULL;
 212         ent = ent0;
 213         for (nrecords = 0; count < 0 || nrecords < count; ent = entend + 1) {
 214                 /*
 215                  * Bail if we've reached the end of the container.
 216                  */
 217                 if (ent - ent0 >= st.st_size)
 218                         break;
 219 
 220                 /*
 221                  * If we're searching by client IP address, locate it
 222                  * quickly using strstr(3C); if we can't find it by this
 223                  * technique then it's not in the container.
 224                  */
 225                 if (DSVC_QISEQ(query, DN_QCIP)) {
 226                         /*
 227                          * If we've already found the DN_QCIP record, bail.
 228                          */
 229                         if (nrecords > 0)
 230                                 break;
 231 
 232                         ent = strstr(ent, cip);
 233                         if (ent == NULL)
 234                                 break;
 235                         ent++;
 236                 }
 237 
 238                 /*
 239                  * Find the end of the record and change it a NUL byte so
 240                  * that it is interpreted correctly with field_split() and
 241                  * record_match() below.  If we can't find a trailing
 242                  * newline, then it must be the last record (whose newline
 243                  * we already changed to a NUL above).
 244                  */
 245                 entend = strchr(ent, '\n');
 246                 if (entend != NULL)
 247                         *entend = '\0';
 248                 else
 249                         entend = &ent0[st.st_size - 1];
 250 
 251                 /*
 252                  * Skip pure comment lines; for now this just skips the
 253                  * header information at the top of the container.
 254                  */
 255                 if (ent[0] == DNF_COMMENT_CHAR)
 256                         continue;
 257 
 258                 /*
 259                  * Split the buffer up into DNF_FIELDS fields.
 260                  */
 261                 nfields = field_split(ent, DNF_FIELDS, fields, "|");
 262                 if (nfields < DNF_FIELDS)
 263                         continue;
 264 
 265                 /*
 266                  * See if we've got a match, filling in dnf.dnf_rec as
 267                  * we go.  If record_match() succeeds, dnf.dnf_rec will
 268                  * be completely filled in.
 269                  */
 270                 if (!record_match(fields, &dn, targetp, query))
 271                         continue;
 272 
 273                 /*
 274                  * Caller just wants a count of the number of matching
 275                  * records, not the records themselves; continue.
 276                  */
 277                 if (recordsp == NULL) {
 278                         nrecords++;
 279                         continue;
 280                 }
 281 
 282                 /*
 283                  * Allocate record; if FIND_POSITION flag is set, then
 284                  * we need to allocate an extended (dn_recpos_t) record.
 285                  */
 286                 if (flags & FIND_POSITION)
 287                         recordp = malloc(sizeof (dn_recpos_t));
 288                 else
 289                         recordp = malloc(sizeof (dn_rec_t));
 290 
 291                 if (recordp == NULL) {
 292                         if ((flags & FIND_PARTIAL) == 0)
 293                                 retval = DSVC_NO_MEMORY;
 294                         break;
 295                 }
 296 
 297                 /*
 298                  * Fill in record; do a structure copy from our automatic
 299                  * dn.  If FIND_POSITION flag is on, pass back additional
 300                  * position information.
 301                  */
 302                 *recordp = dn;
 303                 if (flags & FIND_POSITION) {
 304                         ((dn_recpos_t *)recordp)->dnp_off = ent - ent0;
 305                         ((dn_recpos_t *)recordp)->dnp_size = entend - ent + 1;
 306                 }
 307 
 308                 /*
 309                  * Chuck the record on the list; up the counter.
 310                  */
 311                 new_records = add_dnrec_to_list(recordp, records);
 312                 if (new_records == NULL) {
 313                         free(recordp);
 314                         if ((flags & FIND_PARTIAL) == 0)
 315                                 retval = DSVC_NO_MEMORY;
 316                         break;
 317                 }
 318 
 319                 records = new_records;
 320                 nrecords++;
 321         }
 322 
 323         (void) munmap(ent0, st.st_size);
 324 
 325         if (retval == DSVC_SUCCESS) {
 326                 *nrecordsp = nrecords;
 327                 if (recordsp != NULL)
 328                         *recordsp = records;
 329                 return (DSVC_SUCCESS);
 330         }
 331 
 332         if (records != NULL)
 333                 free_dnrec_list(records);
 334 
 335         return (retval);
 336 }
 337 
 338 int
 339 lookup_dn(void *handle, boolean_t partial, uint_t query, int count,
 340     const dn_rec_t *targetp, dn_rec_list_t **recordsp, uint_t *nrecordsp)
 341 {
 342         int             retval;
 343         char            dnpath[MAXPATHLEN];
 344         int             fd;
 345         dn_handle_t     *dhp = (dn_handle_t *)handle;
 346 
 347         if ((dhp->dh_oflags & DSVC_READ) == 0)
 348                 return (DSVC_ACCESS);
 349 
 350         net2path(dnpath, MAXPATHLEN, dhp->dh_location, dhp->dh_net, "");
 351         fd = open(dnpath, O_RDONLY);
 352         if (fd == -1)
 353                 return (syserr_to_dsvcerr(errno));
 354 
 355         retval = find_dn(fd, partial ? FIND_PARTIAL : 0, query, count, targetp,
 356             recordsp, nrecordsp);
 357 
 358         (void) close(fd);
 359         return (retval);
 360 }
 361 
 362 /*
 363  * Compares the fields in fields[] agains the fields in target `targetp',
 364  * using `query' to decide what fields to compare.  Returns B_TRUE if `dnp'
 365  * matches `targetp', B_FALSE if not.  On success, `dnp' is completely
 366  * filled in.
 367  */
 368 static boolean_t
 369 record_match(char *fields[], dn_rec_t *dnp, const dn_rec_t *targetp,
 370     uint_t query)
 371 {
 372         unsigned int    qflags[] = { DN_QFDYNAMIC, DN_QFAUTOMATIC, DN_QFMANUAL,
 373                                     DN_QFUNUSABLE, DN_QFBOOTP_ONLY };
 374         unsigned int    flags[]  = { DN_FDYNAMIC, DN_FAUTOMATIC, DN_FMANUAL,
 375                                     DN_FUNUSABLE, DN_FBOOTP_ONLY };
 376         unsigned int    i;
 377         uint_t          dn_cid_len;
 378 
 379         dnp->dn_cip.s_addr = ntohl(inet_addr(fields[DNF_CIP]));
 380         if (DSVC_QISEQ(query, DN_QCIP) &&
 381             dnp->dn_cip.s_addr != targetp->dn_cip.s_addr)
 382                 return (B_FALSE);
 383         if (DSVC_QISNEQ(query, DN_QCIP) &&
 384             dnp->dn_cip.s_addr == targetp->dn_cip.s_addr)
 385                 return (B_FALSE);
 386 
 387         dnp->dn_lease = atoi(fields[DNF_LEASE]);
 388         if (DSVC_QISEQ(query, DN_QLEASE) && targetp->dn_lease != dnp->dn_lease)
 389                 return (B_FALSE);
 390         if (DSVC_QISNEQ(query, DN_QLEASE) && targetp->dn_lease == dnp->dn_lease)
 391                 return (B_FALSE);
 392 
 393         /*
 394          * We use dn_cid_len since dnp->dn_cid_len is of type uchar_t but
 395          * hexascii_to_octet() expects an uint_t *
 396          */
 397         dn_cid_len = DN_MAX_CID_LEN;
 398         if (hexascii_to_octet(fields[DNF_CID], strlen(fields[DNF_CID]),
 399             dnp->dn_cid, &dn_cid_len) != 0)
 400                 return (B_FALSE);
 401 
 402         dnp->dn_cid_len = dn_cid_len;
 403         if (DSVC_QISEQ(query, DN_QCID) &&
 404             (dnp->dn_cid_len != targetp->dn_cid_len ||
 405             (memcmp(dnp->dn_cid, targetp->dn_cid, dnp->dn_cid_len) != 0)))
 406                 return (B_FALSE);
 407         if (DSVC_QISNEQ(query, DN_QCID) &&
 408             (dnp->dn_cid_len == targetp->dn_cid_len &&
 409             (memcmp(dnp->dn_cid, targetp->dn_cid, dnp->dn_cid_len) == 0)))
 410                 return (B_FALSE);
 411 
 412         dnp->dn_sip.s_addr = ntohl(inet_addr(fields[DNF_SIP]));
 413         if (DSVC_QISEQ(query, DN_QSIP) &&
 414             dnp->dn_sip.s_addr != targetp->dn_sip.s_addr)
 415                 return (B_FALSE);
 416         if (DSVC_QISNEQ(query, DN_QSIP) &&
 417             dnp->dn_sip.s_addr == targetp->dn_sip.s_addr)
 418                 return (B_FALSE);
 419 
 420         unescape('|', fields[DNF_MACRO], dnp->dn_macro, sizeof (dnp->dn_macro));
 421         if (DSVC_QISEQ(query, DN_QMACRO) &&
 422             strcmp(targetp->dn_macro, dnp->dn_macro) != 0)
 423                 return (B_FALSE);
 424         if (DSVC_QISNEQ(query, DN_QMACRO) &&
 425             strcmp(targetp->dn_macro, dnp->dn_macro) == 0)
 426                 return (B_FALSE);
 427 
 428         dnp->dn_flags = atoi(fields[DNF_FLAGS]);
 429         for (i = 0; i < sizeof (qflags) / sizeof (unsigned int); i++) {
 430                 if (DSVC_QISEQ(query, qflags[i]) &&
 431                     (dnp->dn_flags & flags[i]) !=
 432                     (targetp->dn_flags & flags[i]))
 433                         return (B_FALSE);
 434                 if (DSVC_QISNEQ(query, qflags[i]) &&
 435                     (dnp->dn_flags & flags[i]) ==
 436                     (targetp->dn_flags & flags[i]))
 437                         return (B_FALSE);
 438         }
 439 
 440         dnp->dn_sig = atoll(fields[DNF_SIG]);
 441         unescape('|', fields[DNF_COMMENT], dnp->dn_comment,
 442             sizeof (dnp->dn_comment));
 443 
 444         return (B_TRUE);
 445 }
 446 
 447 /*
 448  * Internal dhcp_network record update routine, used to factor out the
 449  * common code between add_dn(), delete_dn(), and modify_dn().  If
 450  * `origp' is NULL, then act like add_dn(); if `newp' is NULL, then
 451  * act like delete_dn(); otherwise act like modify_dn().
 452  */
 453 static int
 454 update_dn(const dn_handle_t *dhp, const dn_rec_t *origp, dn_rec_t *newp)
 455 {
 456         char            dnpath[MAXPATHLEN], newpath[MAXPATHLEN];
 457         int             retval = DSVC_SUCCESS;
 458         off_t           recoff, recnext;
 459         dn_rec_list_t   *reclist;
 460         int             fd, newfd;
 461         uint_t          found;
 462         int             query;
 463         struct stat     st;
 464 
 465 
 466         if ((dhp->dh_oflags & DSVC_WRITE) == 0)
 467                 return (DSVC_ACCESS);
 468 
 469         /*
 470          * Open the container to update and a new container file which we
 471          * will store the updated version of the container in.  When the
 472          * update is done, rename the new file to be the real container.
 473          */
 474         net2path(dnpath, MAXPATHLEN, dhp->dh_location, dhp->dh_net, "");
 475         fd = open(dnpath, O_RDONLY);
 476         if (fd == -1)
 477                 return (syserr_to_dsvcerr(errno));
 478 
 479         net2path(newpath, MAXPATHLEN, dhp->dh_location, dhp->dh_net, ".new");
 480         newfd = open(newpath, O_CREAT|O_TRUNC|O_WRONLY, 0644);
 481         if (newfd == -1) {
 482                 (void) close(fd);
 483                 return (syserr_to_dsvcerr(errno));
 484         }
 485 
 486         DSVC_QINIT(query);
 487         DSVC_QEQ(query, DN_QCIP);
 488 
 489         /*
 490          * If we're changing the key for this record, make sure the key
 491          * we're changing to doesn't already exist.
 492          */
 493         if (origp != NULL && newp != NULL) {
 494                 if (origp->dn_cip.s_addr != newp->dn_cip.s_addr) {
 495                         retval = find_dn(fd, 0, query, 1, newp, NULL, &found);
 496                         if (retval != DSVC_SUCCESS)
 497                                 goto out;
 498                         if (found != 0) {
 499                                 retval = DSVC_EXISTS;
 500                                 goto out;
 501                         }
 502                 }
 503         }
 504 
 505         /*
 506          * If we're adding a new record, make sure the record doesn't
 507          * already exist.
 508          */
 509         if (newp != NULL && origp == NULL) {
 510                 retval = find_dn(fd, 0, query, 1, newp, NULL, &found);
 511                 if (retval != DSVC_SUCCESS)
 512                         goto out;
 513                 if (found != 0) {
 514                         retval = DSVC_EXISTS;
 515                         goto out;
 516                 }
 517         }
 518 
 519         /*
 520          * If we're deleting or modifying record, make sure the record
 521          * still exists and that our copy isn't stale.  Note that we don't
 522          * check signatures if we're deleting the record and origp->dn_sig
 523          * is zero, so that records that weren't looked up can be deleted.
 524          */
 525         if (origp != NULL) {
 526                 retval = find_dn(fd, FIND_POSITION, query, 1, origp, &reclist,
 527                     &found);
 528                 if (retval != DSVC_SUCCESS)
 529                         goto out;
 530                 if (found == 0) {
 531                         retval = DSVC_NOENT;
 532                         goto out;
 533                 }
 534 
 535                 if (reclist->dnl_rec->dn_sig != origp->dn_sig) {
 536                         if (newp != NULL || origp->dn_sig != 0) {
 537                                 free_dnrec_list(reclist);
 538                                 retval = DSVC_COLLISION;
 539                                 goto out;
 540                         }
 541                 }
 542 
 543                 /*
 544                  * Note the offset of the record we're modifying or deleting
 545                  * for use down below.
 546                  */
 547                 recoff  = ((dn_recpos_t *)reclist->dnl_rec)->dnp_off;
 548                 recnext = recoff + ((dn_recpos_t *)reclist->dnl_rec)->dnp_size;
 549 
 550                 free_dnrec_list(reclist);
 551         } else {
 552                 /*
 553                  * No record to modify or delete, so set `recoff' and
 554                  * `recnext' appropriately.
 555                  */
 556                 recoff = 0;
 557                 recnext = 0;
 558         }
 559 
 560         /*
 561          * Make a new copy of the container.  If we're deleting or
 562          * modifying a record, don't copy that record to the new container.
 563          */
 564         if (fstat(fd, &st) == -1) {
 565                 retval = DSVC_INTERNAL;
 566                 goto out;
 567         }
 568 
 569         retval = copy_range(fd, 0, newfd, 0, recoff);
 570         if (retval != DSVC_SUCCESS)
 571                 goto out;
 572 
 573         retval = copy_range(fd, recnext, newfd, recoff, st.st_size - recnext);
 574         if (retval != DSVC_SUCCESS)
 575                 goto out;
 576 
 577         /*
 578          * If there's a new/modified record, append it to the new container.
 579          */
 580         if (newp != NULL) {
 581                 if (origp == NULL)
 582                         newp->dn_sig = gensig();
 583                 else
 584                         newp->dn_sig = origp->dn_sig + 1;
 585 
 586                 retval = write_rec(newfd, newp, recoff + st.st_size - recnext);
 587                 if (retval != DSVC_SUCCESS)
 588                         goto out;
 589         }
 590 
 591         /*
 592          * Note: we close these descriptors before the rename(2) (rather
 593          * than just having the `out:' label clean them up) to save NFS
 594          * some work (otherwise, NFS has to save `dnpath' to an alternate
 595          * name since its vnode would still be active).
 596          */
 597         (void) close(fd);
 598         (void) close(newfd);
 599 
 600         if (rename(newpath, dnpath) == -1)
 601                 retval = syserr_to_dsvcerr(errno);
 602 
 603         return (retval);
 604 out:
 605         (void) close(fd);
 606         (void) close(newfd);
 607         (void) unlink(newpath);
 608         return (retval);
 609 }
 610 
 611 int
 612 add_dn(void *handle, dn_rec_t *addp)
 613 {
 614         return (update_dn((dn_handle_t *)handle, NULL, addp));
 615 }
 616 
 617 int
 618 modify_dn(void *handle, const dn_rec_t *origp, dn_rec_t *newp)
 619 {
 620         return (update_dn((dn_handle_t *)handle, origp, newp));
 621 }
 622 
 623 int
 624 delete_dn(void *handle, const dn_rec_t *delp)
 625 {
 626         return (update_dn((dn_handle_t *)handle, delp, NULL));
 627 }
 628 
 629 int
 630 list_dn(const char *location, char ***listppp, uint_t *countp)
 631 {
 632         char            ipaddr[INET_ADDRSTRLEN];
 633         struct dirent   *result;
 634         DIR             *dirp;
 635         unsigned int    i, count = 0;
 636         char            *re, **new_listpp, **listpp = NULL;
 637         char            conver[4];
 638         int             error;
 639 
 640         dirp = opendir(location);
 641         if (dirp == NULL) {
 642                 switch (errno) {
 643                 case EACCES:
 644                 case EPERM:
 645                         return (DSVC_ACCESS);
 646                 case ENOENT:
 647                         return (DSVC_NO_LOCATION);
 648                 default:
 649                         break;
 650                 }
 651                 return (DSVC_INTERNAL);
 652         }
 653 
 654         /*
 655          * Compile a regular expression matching "SUNWfilesX_" (where X is
 656          * a container version number) followed by an IP address (roughly
 657          * speaking).  Note that the $N constructions allow us to get the
 658          * container version and IP address when calling regex(3C).
 659          */
 660         re = regcmp("^SUNWfiles([0-9]{1,3})$0_"
 661             "(([0-9]{1,3}_){3}[0-9]{1,3})$1$", (char *)0);
 662         if (re == NULL)
 663                 return (DSVC_NO_MEMORY);
 664 
 665         while ((result = readdir(dirp)) != NULL) {
 666                 if (regex(re, result->d_name, conver, ipaddr) != NULL) {
 667                         if (atoi(conver) != DSVC_CONVER)
 668                                 continue;
 669 
 670                         for (i = 0; ipaddr[i] != '\0'; i++)
 671                                 if (ipaddr[i] == '_')
 672                                         ipaddr[i] = '.';
 673 
 674                         new_listpp = realloc(listpp,
 675                             (sizeof (char **)) * (count + 1));
 676                         if (new_listpp == NULL) {
 677                                 error = DSVC_NO_MEMORY;
 678                                 goto fail;
 679                         }
 680                         listpp = new_listpp;
 681                         listpp[count] = strdup(ipaddr);
 682                         if (listpp[count] == NULL) {
 683                                 error = DSVC_NO_MEMORY;
 684                                 goto fail;
 685                         }
 686                         count++;
 687                 }
 688         }
 689         free(re);
 690         (void) closedir(dirp);
 691 
 692         *countp = count;
 693         *listppp = listpp;
 694         return (DSVC_SUCCESS);
 695 
 696 fail:
 697         free(re);
 698         (void) closedir(dirp);
 699 
 700         for (i = 0; i < count; i++)
 701                 free(listpp[i]);
 702         free(listpp);
 703         return (error);
 704 }
 705 
 706 /*
 707  * Given a buffer `path' of `pathlen' bytes, fill it in with a path to the
 708  * DHCP Network table for IP network `ip' located in directory `dir' with a
 709  * suffix of `suffix'.
 710  */
 711 static void
 712 net2path(char *path, size_t pathlen, const char *dir, ipaddr_t ip,
 713     const char *suffix)
 714 {
 715         (void) snprintf(path, pathlen, "%s/SUNWfiles%u_%d_%d_%d_%d%s", dir,
 716             DSVC_CONVER, ip >> 24, (ip >> 16) & 0xff, (ip >> 8) & 0xff,
 717             ip & 0xff, suffix);
 718 }
 719 
 720 /*
 721  * Write the dn_rec_t `recp' into the open container `fd' at offset
 722  * `recoff'.  Returns DSVC_* error code.
 723  */
 724 static int
 725 write_rec(int fd, dn_rec_t *recp, off_t recoff)
 726 {
 727         char            entbuf[1024], *ent = entbuf;
 728         size_t          entsize = sizeof (entbuf);
 729         int             entlen;
 730         dn_filerec_t    dnf;
 731         struct in_addr  nip;
 732         unsigned int    cid_len = sizeof (dnf.dnf_cid);
 733 
 734         /*
 735          * Copy data into a dn_filerec_t, since that's what we can
 736          * actually put on disk.
 737          */
 738         if (octet_to_hexascii(recp->dn_cid, recp->dn_cid_len, dnf.dnf_cid,
 739             &cid_len) != 0)
 740                 return (DSVC_INTERNAL);
 741 
 742         nip.s_addr = htonl(recp->dn_cip.s_addr);
 743         (void) inet_ntop(AF_INET, &nip, dnf.dnf_cip, sizeof (dnf.dnf_cip));
 744         nip.s_addr = htonl(recp->dn_sip.s_addr);
 745         (void) inet_ntop(AF_INET, &nip, dnf.dnf_sip, sizeof (dnf.dnf_cip));
 746 
 747         dnf.dnf_sig     = recp->dn_sig;
 748         dnf.dnf_flags   = recp->dn_flags;
 749         dnf.dnf_lease   = recp->dn_lease;
 750 
 751         escape('|', recp->dn_macro, dnf.dnf_macro, sizeof (dnf.dnf_macro));
 752         escape('|', recp->dn_comment, dnf.dnf_comment,
 753             sizeof (dnf.dnf_comment));
 754 again:
 755         entlen = snprintf(ent, entsize, "%s|%s|%02hu|%s|%u|%llu|%s|%s\n",
 756             dnf.dnf_cip, dnf.dnf_cid, dnf.dnf_flags, dnf.dnf_sip,
 757             dnf.dnf_lease, dnf.dnf_sig, dnf.dnf_macro, dnf.dnf_comment);
 758         if (entlen == -1)
 759                 return (syserr_to_dsvcerr(errno));
 760 
 761         if (entlen > entsize) {
 762                 entsize = entlen;
 763                 ent = alloca(entlen);
 764                 goto again;
 765         }
 766 
 767         if (pnwrite(fd, ent, entlen, recoff) == -1)
 768                 return (syserr_to_dsvcerr(errno));
 769 
 770         return (DSVC_SUCCESS);
 771 }