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 }