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
open_dn(void ** handlep,const char * location,uint_t flags,const struct in_addr * netp,const struct in_addr * maskp)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
close_dn(void ** handlep)144 close_dn(void **handlep)
145 {
146 free(*handlep);
147 return (DSVC_SUCCESS);
148 }
149
150 int
remove_dn(const char * dir,const struct in_addr * netp)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
find_dn(int fd,uint_t flags,uint_t query,int count,const dn_rec_t * targetp,dn_rec_list_t ** recordsp,uint_t * nrecordsp)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
lookup_dn(void * handle,boolean_t partial,uint_t query,int count,const dn_rec_t * targetp,dn_rec_list_t ** recordsp,uint_t * nrecordsp)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
record_match(char * fields[],dn_rec_t * dnp,const dn_rec_t * targetp,uint_t query)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
update_dn(const dn_handle_t * dhp,const dn_rec_t * origp,dn_rec_t * newp)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
add_dn(void * handle,dn_rec_t * addp)612 add_dn(void *handle, dn_rec_t *addp)
613 {
614 return (update_dn((dn_handle_t *)handle, NULL, addp));
615 }
616
617 int
modify_dn(void * handle,const dn_rec_t * origp,dn_rec_t * newp)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
delete_dn(void * handle,const dn_rec_t * delp)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
list_dn(const char * location,char *** listppp,uint_t * countp)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
net2path(char * path,size_t pathlen,const char * dir,ipaddr_t ip,const char * suffix)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
write_rec(int fd,dn_rec_t * recp,off_t recoff)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 }
772