xref: /onnv-gate/usr/src/lib/libdhcpsvc/modules/files/dhcp_network.c (revision 871:f917b2ed7d21)
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