10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance
70Sstevel@tonic-gate * with the License.
80Sstevel@tonic-gate *
90Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate * See the License for the specific language governing permissions
120Sstevel@tonic-gate * and limitations under the License.
130Sstevel@tonic-gate *
140Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate *
200Sstevel@tonic-gate * CDDL HEADER END
210Sstevel@tonic-gate */
220Sstevel@tonic-gate /*
23*871Scasper * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24*871Scasper * Use is subject to license terms.
250Sstevel@tonic-gate */
260Sstevel@tonic-gate
270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
280Sstevel@tonic-gate
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate * This file contains public functions for managing legacy DHCP network
310Sstevel@tonic-gate * containers. For the semantics of these functions, please see the
320Sstevel@tonic-gate * Enterprise DHCP Architecture Document.
330Sstevel@tonic-gate */
340Sstevel@tonic-gate
350Sstevel@tonic-gate #include <alloca.h>
360Sstevel@tonic-gate #include <arpa/inet.h>
370Sstevel@tonic-gate #include <ctype.h>
380Sstevel@tonic-gate #include <dhcp_svc_public.h>
390Sstevel@tonic-gate #include <dirent.h>
400Sstevel@tonic-gate #include <errno.h>
410Sstevel@tonic-gate #include <fcntl.h>
420Sstevel@tonic-gate #include <libgen.h>
430Sstevel@tonic-gate #include <libinetutil.h>
440Sstevel@tonic-gate #include <netinet/in.h>
450Sstevel@tonic-gate #include <stdlib.h>
460Sstevel@tonic-gate #include <string.h>
470Sstevel@tonic-gate #include <sys/socket.h>
480Sstevel@tonic-gate #include <sys/stat.h>
490Sstevel@tonic-gate #include <sys/types.h>
500Sstevel@tonic-gate #include <unistd.h>
510Sstevel@tonic-gate
520Sstevel@tonic-gate #include "dhcp_network.h"
530Sstevel@tonic-gate #include "util.h"
540Sstevel@tonic-gate
550Sstevel@tonic-gate static void net2path(char *, size_t, const char *, ipaddr_t, const char *);
560Sstevel@tonic-gate static boolean_t record_match(char *[], dn_rec_t *, const dn_rec_t *, uint_t);
570Sstevel@tonic-gate static int write_rec(int, dn_rec_t *, off_t);
580Sstevel@tonic-gate
590Sstevel@tonic-gate /* ARGSUSED */
600Sstevel@tonic-gate int
open_dn(void ** handlep,const char * location,uint_t flags,const struct in_addr * netp,const struct in_addr * maskp)610Sstevel@tonic-gate open_dn(void **handlep, const char *location, uint_t flags,
620Sstevel@tonic-gate const struct in_addr *netp, const struct in_addr *maskp)
630Sstevel@tonic-gate {
640Sstevel@tonic-gate char dnpath[MAXPATHLEN];
650Sstevel@tonic-gate dn_handle_t *dhp;
660Sstevel@tonic-gate int retval;
670Sstevel@tonic-gate int fd;
680Sstevel@tonic-gate
690Sstevel@tonic-gate dhp = malloc(sizeof (dn_handle_t));
700Sstevel@tonic-gate if (dhp == NULL)
710Sstevel@tonic-gate return (DSVC_NO_MEMORY);
720Sstevel@tonic-gate
730Sstevel@tonic-gate dhp->dh_net = netp->s_addr;
740Sstevel@tonic-gate dhp->dh_oflags = flags;
750Sstevel@tonic-gate (void) strlcpy(dhp->dh_location, location, MAXPATHLEN);
760Sstevel@tonic-gate
770Sstevel@tonic-gate /*
780Sstevel@tonic-gate * This is a legacy format which has no header, so we neither write
790Sstevel@tonic-gate * nor verify a header (we just create the file or make sure it
800Sstevel@tonic-gate * exists, depending on the value of `flags').
810Sstevel@tonic-gate */
820Sstevel@tonic-gate net2path(dnpath, MAXPATHLEN, location, netp->s_addr, "");
830Sstevel@tonic-gate retval = open_file(dnpath, flags, &fd);
840Sstevel@tonic-gate if (retval != DSVC_SUCCESS) {
850Sstevel@tonic-gate free(dhp);
860Sstevel@tonic-gate return (retval);
870Sstevel@tonic-gate }
880Sstevel@tonic-gate (void) close(fd);
890Sstevel@tonic-gate
900Sstevel@tonic-gate *handlep = dhp;
910Sstevel@tonic-gate return (DSVC_SUCCESS);
920Sstevel@tonic-gate }
930Sstevel@tonic-gate
940Sstevel@tonic-gate int
close_dn(void ** handlep)950Sstevel@tonic-gate close_dn(void **handlep)
960Sstevel@tonic-gate {
970Sstevel@tonic-gate free(*handlep);
980Sstevel@tonic-gate return (DSVC_SUCCESS);
990Sstevel@tonic-gate }
1000Sstevel@tonic-gate
1010Sstevel@tonic-gate int
remove_dn(const char * dir,const struct in_addr * netp)1020Sstevel@tonic-gate remove_dn(const char *dir, const struct in_addr *netp)
1030Sstevel@tonic-gate {
1040Sstevel@tonic-gate char dnpath[MAXPATHLEN];
1050Sstevel@tonic-gate
1060Sstevel@tonic-gate net2path(dnpath, MAXPATHLEN, dir, netp->s_addr, "");
1070Sstevel@tonic-gate if (unlink(dnpath) == -1)
1080Sstevel@tonic-gate return (syserr_to_dsvcerr(errno));
1090Sstevel@tonic-gate
1100Sstevel@tonic-gate return (DSVC_SUCCESS);
1110Sstevel@tonic-gate }
1120Sstevel@tonic-gate
1130Sstevel@tonic-gate static int
find_dn(FILE * fp,uint_t flags,uint_t query,int count,const dn_rec_t * targetp,dn_rec_list_t ** recordsp,uint_t * nrecordsp)1140Sstevel@tonic-gate find_dn(FILE *fp, uint_t flags, uint_t query, int count,
1150Sstevel@tonic-gate const dn_rec_t *targetp, dn_rec_list_t **recordsp, uint_t *nrecordsp)
1160Sstevel@tonic-gate {
1170Sstevel@tonic-gate int retval = DSVC_SUCCESS;
1180Sstevel@tonic-gate char *commentp, *fields[DNF_MAX_FIELDS];
1190Sstevel@tonic-gate char *buf = NULL;
1200Sstevel@tonic-gate uint_t nrecords;
1210Sstevel@tonic-gate dn_rec_t dn, *recordp;
1220Sstevel@tonic-gate dn_rec_list_t *records, *new_records;
1230Sstevel@tonic-gate unsigned int nfields;
1240Sstevel@tonic-gate off_t recoff;
1250Sstevel@tonic-gate
1260Sstevel@tonic-gate if (fseek(fp, 0, SEEK_SET) == -1)
1270Sstevel@tonic-gate return (DSVC_INTERNAL);
1280Sstevel@tonic-gate
1290Sstevel@tonic-gate records = NULL;
1300Sstevel@tonic-gate for (nrecords = 0; count < 0 || nrecords < count; ) {
1310Sstevel@tonic-gate free(buf);
1320Sstevel@tonic-gate
1330Sstevel@tonic-gate if (flags & FIND_POSITION)
1340Sstevel@tonic-gate recoff = ftello(fp);
1350Sstevel@tonic-gate
1360Sstevel@tonic-gate buf = read_entry(fp);
1370Sstevel@tonic-gate if (buf == NULL) {
1380Sstevel@tonic-gate if (!feof(fp))
1390Sstevel@tonic-gate retval = DSVC_NO_MEMORY;
1400Sstevel@tonic-gate break;
1410Sstevel@tonic-gate }
1420Sstevel@tonic-gate
1430Sstevel@tonic-gate /*
1440Sstevel@tonic-gate * Skip pure comment lines; for now this just skips the
1450Sstevel@tonic-gate * header information at the top of the container.
1460Sstevel@tonic-gate */
1470Sstevel@tonic-gate if (buf[0] == DNF_COMMENT_CHAR)
1480Sstevel@tonic-gate continue;
1490Sstevel@tonic-gate
1500Sstevel@tonic-gate /*
1510Sstevel@tonic-gate * Tell field_split() that there's one less field than
1520Sstevel@tonic-gate * there really is. We do this so that the comment and the
1530Sstevel@tonic-gate * macro field both end up in the DNF_MACRO field, since
1540Sstevel@tonic-gate * both fields are optional and it requires some fancy
1550Sstevel@tonic-gate * footwork (below) to tell which (if any) the record
1560Sstevel@tonic-gate * contains.
1570Sstevel@tonic-gate */
1580Sstevel@tonic-gate nfields = field_split(buf, DNF_MAX_FIELDS - 1, fields, " \t");
1590Sstevel@tonic-gate if (nfields < DNF_REQ_FIELDS)
1600Sstevel@tonic-gate continue;
1610Sstevel@tonic-gate
1620Sstevel@tonic-gate if (nfields == DNF_REQ_FIELDS) {
1630Sstevel@tonic-gate fields[DNF_MACRO] = "";
1640Sstevel@tonic-gate fields[DNF_COMMENT] = "";
1650Sstevel@tonic-gate } else {
1660Sstevel@tonic-gate /*
1670Sstevel@tonic-gate * Assume there is a comment; if we hit a comment
1680Sstevel@tonic-gate * delimiter char (DNF_COMMENT_CHAR), then simply
1690Sstevel@tonic-gate * change it to a NUL and advance commentp. If we
1700Sstevel@tonic-gate * hit whitespace, replace the first instance with
1710Sstevel@tonic-gate * NUL, and go searching for DNF_COMMENT_CHAR.
1720Sstevel@tonic-gate * This step is important since it efficiently
1730Sstevel@tonic-gate * handles the common case where a comment is
1740Sstevel@tonic-gate * preceded by a space.
1750Sstevel@tonic-gate */
1760Sstevel@tonic-gate commentp = fields[DNF_MACRO];
1770Sstevel@tonic-gate while (!isspace(*commentp) &&
1780Sstevel@tonic-gate *commentp != DNF_COMMENT_CHAR && *commentp != '\0')
1790Sstevel@tonic-gate commentp++;
1800Sstevel@tonic-gate
1810Sstevel@tonic-gate if (isspace(*commentp)) {
1820Sstevel@tonic-gate *commentp++ = '\0';
1830Sstevel@tonic-gate commentp = strchr(commentp, DNF_COMMENT_CHAR);
1840Sstevel@tonic-gate if (commentp == NULL)
1850Sstevel@tonic-gate commentp = "";
1860Sstevel@tonic-gate }
1870Sstevel@tonic-gate
1880Sstevel@tonic-gate if (*commentp == DNF_COMMENT_CHAR)
1890Sstevel@tonic-gate *commentp++ = '\0';
1900Sstevel@tonic-gate
1910Sstevel@tonic-gate fields[DNF_COMMENT] = commentp;
1920Sstevel@tonic-gate }
1930Sstevel@tonic-gate
1940Sstevel@tonic-gate /*
1950Sstevel@tonic-gate * See if we've got a match, filling in dnf.dnf_rec as
1960Sstevel@tonic-gate * we go. If record_match() succeeds, dnf.dnf_rec will
1970Sstevel@tonic-gate * be completely filled in.
1980Sstevel@tonic-gate */
1990Sstevel@tonic-gate if (!record_match(fields, &dn, targetp, query))
2000Sstevel@tonic-gate continue;
2010Sstevel@tonic-gate
2020Sstevel@tonic-gate /*
2030Sstevel@tonic-gate * Caller just wants a count of the number of matching
2040Sstevel@tonic-gate * records, not the records themselves; continue.
2050Sstevel@tonic-gate */
2060Sstevel@tonic-gate if (recordsp == NULL) {
2070Sstevel@tonic-gate nrecords++;
2080Sstevel@tonic-gate continue;
2090Sstevel@tonic-gate }
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate /*
2120Sstevel@tonic-gate * Allocate record; if FIND_POSITION flag is set, then
2130Sstevel@tonic-gate * we need to allocate an extended (dn_recpos_t) record.
2140Sstevel@tonic-gate */
2150Sstevel@tonic-gate if (flags & FIND_POSITION)
2160Sstevel@tonic-gate recordp = malloc(sizeof (dn_recpos_t));
2170Sstevel@tonic-gate else
2180Sstevel@tonic-gate recordp = malloc(sizeof (dn_rec_t));
2190Sstevel@tonic-gate
2200Sstevel@tonic-gate if (recordp == NULL) {
2210Sstevel@tonic-gate if ((flags & FIND_PARTIAL) == 0)
2220Sstevel@tonic-gate retval = DSVC_NO_MEMORY;
2230Sstevel@tonic-gate break;
2240Sstevel@tonic-gate }
2250Sstevel@tonic-gate
2260Sstevel@tonic-gate /*
2270Sstevel@tonic-gate * Fill in record; do a structure copy from our automatic
2280Sstevel@tonic-gate * dn. If FIND_POSITION flag is on, pass back additional
2290Sstevel@tonic-gate * position information.
2300Sstevel@tonic-gate */
2310Sstevel@tonic-gate *recordp = dn;
2320Sstevel@tonic-gate if (flags & FIND_POSITION) {
2330Sstevel@tonic-gate ((dn_recpos_t *)recordp)->dnp_off = recoff;
2340Sstevel@tonic-gate ((dn_recpos_t *)recordp)->dnp_size = ftello(fp) -
2350Sstevel@tonic-gate recoff;
2360Sstevel@tonic-gate }
2370Sstevel@tonic-gate
2380Sstevel@tonic-gate /*
2390Sstevel@tonic-gate * Chuck the record on the list and up the counter.
2400Sstevel@tonic-gate */
2410Sstevel@tonic-gate new_records = add_dnrec_to_list(recordp, records);
2420Sstevel@tonic-gate if (new_records == NULL) {
2430Sstevel@tonic-gate free(recordp);
2440Sstevel@tonic-gate if ((flags & FIND_PARTIAL) == 0)
2450Sstevel@tonic-gate retval = DSVC_NO_MEMORY;
2460Sstevel@tonic-gate break;
2470Sstevel@tonic-gate }
2480Sstevel@tonic-gate
2490Sstevel@tonic-gate records = new_records;
2500Sstevel@tonic-gate nrecords++;
2510Sstevel@tonic-gate }
2520Sstevel@tonic-gate
2530Sstevel@tonic-gate free(buf);
2540Sstevel@tonic-gate
2550Sstevel@tonic-gate if (retval == DSVC_SUCCESS) {
2560Sstevel@tonic-gate *nrecordsp = nrecords;
2570Sstevel@tonic-gate if (recordsp != NULL)
2580Sstevel@tonic-gate *recordsp = records;
2590Sstevel@tonic-gate return (DSVC_SUCCESS);
2600Sstevel@tonic-gate }
2610Sstevel@tonic-gate
2620Sstevel@tonic-gate if (records != NULL)
2630Sstevel@tonic-gate free_dnrec_list(records);
2640Sstevel@tonic-gate
2650Sstevel@tonic-gate return (retval);
2660Sstevel@tonic-gate }
2670Sstevel@tonic-gate
2680Sstevel@tonic-gate 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)2690Sstevel@tonic-gate lookup_dn(void *handle, boolean_t partial, uint_t query, int count,
2700Sstevel@tonic-gate const dn_rec_t *targetp, dn_rec_list_t **recordsp, uint_t *nrecordsp)
2710Sstevel@tonic-gate {
2720Sstevel@tonic-gate int retval;
2730Sstevel@tonic-gate char dnpath[MAXPATHLEN];
2740Sstevel@tonic-gate FILE *fp;
2750Sstevel@tonic-gate dn_handle_t *dhp = (dn_handle_t *)handle;
2760Sstevel@tonic-gate
2770Sstevel@tonic-gate if ((dhp->dh_oflags & DSVC_READ) == 0)
2780Sstevel@tonic-gate return (DSVC_ACCESS);
2790Sstevel@tonic-gate
2800Sstevel@tonic-gate net2path(dnpath, MAXPATHLEN, dhp->dh_location, dhp->dh_net, "");
2810Sstevel@tonic-gate fp = fopen(dnpath, "r");
2820Sstevel@tonic-gate if (fp == NULL)
2830Sstevel@tonic-gate return (syserr_to_dsvcerr(errno));
2840Sstevel@tonic-gate
2850Sstevel@tonic-gate retval = find_dn(fp, partial ? FIND_PARTIAL : 0, query, count, targetp,
2860Sstevel@tonic-gate recordsp, nrecordsp);
2870Sstevel@tonic-gate
2880Sstevel@tonic-gate (void) fclose(fp);
2890Sstevel@tonic-gate return (retval);
2900Sstevel@tonic-gate }
2910Sstevel@tonic-gate
2920Sstevel@tonic-gate /*
2930Sstevel@tonic-gate * Compares the fields in fields[] agains the fields in target `targetp',
2940Sstevel@tonic-gate * using `query' to decide what fields to compare. Returns B_TRUE if `dnp'
2950Sstevel@tonic-gate * matches `targetp', B_FALSE if not. On success, `dnp' is completely
2960Sstevel@tonic-gate * filled in.
2970Sstevel@tonic-gate */
2980Sstevel@tonic-gate static boolean_t
record_match(char * fields[],dn_rec_t * dnp,const dn_rec_t * targetp,uint_t query)2990Sstevel@tonic-gate record_match(char *fields[], dn_rec_t *dnp, const dn_rec_t *targetp,
3000Sstevel@tonic-gate uint_t query)
3010Sstevel@tonic-gate {
3020Sstevel@tonic-gate unsigned int qflags[] = { DN_QFDYNAMIC, DN_QFAUTOMATIC, DN_QFMANUAL,
3030Sstevel@tonic-gate DN_QFUNUSABLE, DN_QFBOOTP_ONLY };
3040Sstevel@tonic-gate unsigned int flags[] = { DN_FDYNAMIC, DN_FAUTOMATIC, DN_FMANUAL,
3050Sstevel@tonic-gate DN_FUNUSABLE, DN_FBOOTP_ONLY };
3060Sstevel@tonic-gate unsigned int i;
3070Sstevel@tonic-gate uint_t dn_cid_len;
3080Sstevel@tonic-gate
3090Sstevel@tonic-gate dnp->dn_cip.s_addr = ntohl(inet_addr(fields[DNF_CIP]));
3100Sstevel@tonic-gate if (DSVC_QISEQ(query, DN_QCIP) &&
3110Sstevel@tonic-gate dnp->dn_cip.s_addr != targetp->dn_cip.s_addr)
3120Sstevel@tonic-gate return (B_FALSE);
3130Sstevel@tonic-gate if (DSVC_QISNEQ(query, DN_QCIP) &&
3140Sstevel@tonic-gate dnp->dn_cip.s_addr == targetp->dn_cip.s_addr)
3150Sstevel@tonic-gate return (B_FALSE);
3160Sstevel@tonic-gate
3170Sstevel@tonic-gate dnp->dn_lease = atoi(fields[DNF_LEASE]);
3180Sstevel@tonic-gate if (DSVC_QISEQ(query, DN_QLEASE) && targetp->dn_lease != dnp->dn_lease)
3190Sstevel@tonic-gate return (B_FALSE);
3200Sstevel@tonic-gate if (DSVC_QISNEQ(query, DN_QLEASE) && targetp->dn_lease == dnp->dn_lease)
3210Sstevel@tonic-gate return (B_FALSE);
3220Sstevel@tonic-gate
3230Sstevel@tonic-gate /*
3240Sstevel@tonic-gate * We use dn_cid_len since dnp->dn_cid_len is of type uchar_t but
3250Sstevel@tonic-gate * hexascii_to_octet() expects a uint_t *
3260Sstevel@tonic-gate */
3270Sstevel@tonic-gate dn_cid_len = DN_MAX_CID_LEN;
3280Sstevel@tonic-gate if (hexascii_to_octet(fields[DNF_CID], strlen(fields[DNF_CID]),
3290Sstevel@tonic-gate dnp->dn_cid, &dn_cid_len) != 0)
3300Sstevel@tonic-gate return (B_FALSE);
3310Sstevel@tonic-gate
3320Sstevel@tonic-gate dnp->dn_cid_len = dn_cid_len;
3330Sstevel@tonic-gate if (DSVC_QISEQ(query, DN_QCID) &&
3340Sstevel@tonic-gate (dnp->dn_cid_len != targetp->dn_cid_len ||
3350Sstevel@tonic-gate (memcmp(dnp->dn_cid, targetp->dn_cid, dnp->dn_cid_len) != 0)))
3360Sstevel@tonic-gate return (B_FALSE);
3370Sstevel@tonic-gate if (DSVC_QISNEQ(query, DN_QCID) &&
3380Sstevel@tonic-gate (dnp->dn_cid_len == targetp->dn_cid_len &&
3390Sstevel@tonic-gate (memcmp(dnp->dn_cid, targetp->dn_cid, dnp->dn_cid_len) == 0)))
3400Sstevel@tonic-gate return (B_FALSE);
3410Sstevel@tonic-gate
3420Sstevel@tonic-gate dnp->dn_sip.s_addr = ntohl(inet_addr(fields[DNF_SIP]));
3430Sstevel@tonic-gate if (DSVC_QISEQ(query, DN_QSIP) &&
3440Sstevel@tonic-gate dnp->dn_sip.s_addr != targetp->dn_sip.s_addr)
3450Sstevel@tonic-gate return (B_FALSE);
3460Sstevel@tonic-gate if (DSVC_QISNEQ(query, DN_QSIP) &&
3470Sstevel@tonic-gate dnp->dn_sip.s_addr == targetp->dn_sip.s_addr)
3480Sstevel@tonic-gate return (B_FALSE);
3490Sstevel@tonic-gate
3500Sstevel@tonic-gate (void) strlcpy(dnp->dn_macro, fields[DNF_MACRO],
3510Sstevel@tonic-gate sizeof (dnp->dn_macro));
3520Sstevel@tonic-gate if (DSVC_QISEQ(query, DN_QMACRO) &&
3530Sstevel@tonic-gate strcmp(targetp->dn_macro, dnp->dn_macro) != 0)
3540Sstevel@tonic-gate return (B_FALSE);
3550Sstevel@tonic-gate if (DSVC_QISNEQ(query, DN_QMACRO) &&
3560Sstevel@tonic-gate strcmp(targetp->dn_macro, dnp->dn_macro) == 0)
3570Sstevel@tonic-gate return (B_FALSE);
3580Sstevel@tonic-gate
3590Sstevel@tonic-gate dnp->dn_flags = atoi(fields[DNF_FLAGS]);
3600Sstevel@tonic-gate for (i = 0; i < sizeof (qflags) / sizeof (unsigned int); i++) {
3610Sstevel@tonic-gate if (DSVC_QISEQ(query, qflags[i]) &&
3620Sstevel@tonic-gate (dnp->dn_flags & flags[i]) !=
3630Sstevel@tonic-gate (targetp->dn_flags & flags[i]))
3640Sstevel@tonic-gate return (B_FALSE);
3650Sstevel@tonic-gate if (DSVC_QISNEQ(query, qflags[i]) &&
3660Sstevel@tonic-gate (dnp->dn_flags & flags[i]) ==
3670Sstevel@tonic-gate (targetp->dn_flags & flags[i]))
3680Sstevel@tonic-gate return (B_FALSE);
3690Sstevel@tonic-gate }
3700Sstevel@tonic-gate (void) strlcpy(dnp->dn_comment, fields[DNF_COMMENT],
3710Sstevel@tonic-gate sizeof (dnp->dn_comment));
3720Sstevel@tonic-gate
3730Sstevel@tonic-gate return (B_TRUE);
3740Sstevel@tonic-gate }
3750Sstevel@tonic-gate
3760Sstevel@tonic-gate /*
3770Sstevel@tonic-gate * Internal dhcp_network record update routine, used to factor out the
3780Sstevel@tonic-gate * common code between add_dn(), delete_dn(), and modify_dn(). If `origp'
3790Sstevel@tonic-gate * is NULL, then act like add_dn(); if `newp' is NULL, then act like
3800Sstevel@tonic-gate * delete_dn(); otherwise act like modify_dn().
3810Sstevel@tonic-gate */
3820Sstevel@tonic-gate static int
update_dn(const dn_handle_t * dhp,const dn_rec_t * origp,dn_rec_t * newp)3830Sstevel@tonic-gate update_dn(const dn_handle_t *dhp, const dn_rec_t *origp, dn_rec_t *newp)
3840Sstevel@tonic-gate {
3850Sstevel@tonic-gate char dnpath[MAXPATHLEN], newpath[MAXPATHLEN];
3860Sstevel@tonic-gate int retval = DSVC_SUCCESS;
3870Sstevel@tonic-gate off_t recoff, recnext;
3880Sstevel@tonic-gate dn_rec_list_t *reclist;
3890Sstevel@tonic-gate FILE *fp;
3900Sstevel@tonic-gate int newfd;
3910Sstevel@tonic-gate uint_t found;
3920Sstevel@tonic-gate int query;
3930Sstevel@tonic-gate struct stat st;
3940Sstevel@tonic-gate
3950Sstevel@tonic-gate if ((dhp->dh_oflags & DSVC_WRITE) == 0)
3960Sstevel@tonic-gate return (DSVC_ACCESS);
3970Sstevel@tonic-gate
3980Sstevel@tonic-gate /*
3990Sstevel@tonic-gate * Open the container to update and a new container file which we
4000Sstevel@tonic-gate * will store the updated version of the container in. When the
4010Sstevel@tonic-gate * update is done, rename the new file to be the real container.
4020Sstevel@tonic-gate */
4030Sstevel@tonic-gate net2path(dnpath, MAXPATHLEN, dhp->dh_location, dhp->dh_net, "");
4040Sstevel@tonic-gate fp = fopen(dnpath, "r");
4050Sstevel@tonic-gate if (fp == NULL)
4060Sstevel@tonic-gate return (syserr_to_dsvcerr(errno));
4070Sstevel@tonic-gate
4080Sstevel@tonic-gate net2path(newpath, MAXPATHLEN, dhp->dh_location, dhp->dh_net, ".new");
4090Sstevel@tonic-gate newfd = open(newpath, O_CREAT|O_TRUNC|O_WRONLY, 0644);
4100Sstevel@tonic-gate if (newfd == -1) {
4110Sstevel@tonic-gate (void) fclose(fp);
4120Sstevel@tonic-gate return (syserr_to_dsvcerr(errno));
4130Sstevel@tonic-gate }
4140Sstevel@tonic-gate
4150Sstevel@tonic-gate DSVC_QINIT(query);
4160Sstevel@tonic-gate DSVC_QEQ(query, DN_QCIP);
4170Sstevel@tonic-gate
4180Sstevel@tonic-gate /*
4190Sstevel@tonic-gate * If we're adding a new record or changing a key for an existing
4200Sstevel@tonic-gate * record, bail if the record we want to add already exists.
4210Sstevel@tonic-gate */
4220Sstevel@tonic-gate if (newp != NULL) {
4230Sstevel@tonic-gate if (origp == NULL ||
4240Sstevel@tonic-gate origp->dn_cip.s_addr != newp->dn_cip.s_addr) {
4250Sstevel@tonic-gate retval = find_dn(fp, 0, query, 1, newp, NULL, &found);
4260Sstevel@tonic-gate if (retval != DSVC_SUCCESS)
4270Sstevel@tonic-gate goto out;
4280Sstevel@tonic-gate if (found != 0) {
4290Sstevel@tonic-gate retval = DSVC_EXISTS;
4300Sstevel@tonic-gate goto out;
4310Sstevel@tonic-gate }
4320Sstevel@tonic-gate }
4330Sstevel@tonic-gate }
4340Sstevel@tonic-gate
4350Sstevel@tonic-gate /*
4360Sstevel@tonic-gate * If we're deleting or modifying record, make sure the record
4370Sstevel@tonic-gate * still exists. Note that we don't check signatures because this
4380Sstevel@tonic-gate * is a legacy format that has no signatures.
4390Sstevel@tonic-gate */
4400Sstevel@tonic-gate if (origp != NULL) {
4410Sstevel@tonic-gate retval = find_dn(fp, FIND_POSITION, query, 1, origp, &reclist,
4420Sstevel@tonic-gate &found);
4430Sstevel@tonic-gate if (retval != DSVC_SUCCESS)
4440Sstevel@tonic-gate goto out;
4450Sstevel@tonic-gate if (found == 0) {
4460Sstevel@tonic-gate retval = DSVC_NOENT;
4470Sstevel@tonic-gate goto out;
4480Sstevel@tonic-gate }
4490Sstevel@tonic-gate
4500Sstevel@tonic-gate /*
4510Sstevel@tonic-gate * Note the offset of the record we're modifying or deleting
4520Sstevel@tonic-gate * for use down below.
4530Sstevel@tonic-gate */
4540Sstevel@tonic-gate recoff = ((dn_recpos_t *)reclist->dnl_rec)->dnp_off;
4550Sstevel@tonic-gate recnext = recoff + ((dn_recpos_t *)reclist->dnl_rec)->dnp_size;
4560Sstevel@tonic-gate
4570Sstevel@tonic-gate free_dnrec_list(reclist);
4580Sstevel@tonic-gate } else {
4590Sstevel@tonic-gate /*
4600Sstevel@tonic-gate * No record to modify or delete, so set `recoff' and
4610Sstevel@tonic-gate * `recnext' appropriately.
4620Sstevel@tonic-gate */
4630Sstevel@tonic-gate recoff = 0;
4640Sstevel@tonic-gate recnext = 0;
4650Sstevel@tonic-gate }
4660Sstevel@tonic-gate
4670Sstevel@tonic-gate /*
4680Sstevel@tonic-gate * Make a new copy of the container. If we're deleting or
4690Sstevel@tonic-gate * modifying a record, don't copy that record to the new container.
4700Sstevel@tonic-gate */
4710Sstevel@tonic-gate if (fstat(fileno(fp), &st) == -1) {
4720Sstevel@tonic-gate retval = DSVC_INTERNAL;
4730Sstevel@tonic-gate goto out;
4740Sstevel@tonic-gate }
4750Sstevel@tonic-gate
4760Sstevel@tonic-gate retval = copy_range(fileno(fp), 0, newfd, 0, recoff);
4770Sstevel@tonic-gate if (retval != DSVC_SUCCESS)
4780Sstevel@tonic-gate goto out;
4790Sstevel@tonic-gate
4800Sstevel@tonic-gate retval = copy_range(fileno(fp), recnext, newfd, recoff,
4810Sstevel@tonic-gate st.st_size - recnext);
4820Sstevel@tonic-gate if (retval != DSVC_SUCCESS)
4830Sstevel@tonic-gate goto out;
4840Sstevel@tonic-gate
4850Sstevel@tonic-gate /*
4860Sstevel@tonic-gate * If there's a new/modified record, append it to the new container.
4870Sstevel@tonic-gate */
4880Sstevel@tonic-gate if (newp != NULL) {
4890Sstevel@tonic-gate retval = write_rec(newfd, newp, recoff + st.st_size - recnext);
4900Sstevel@tonic-gate if (retval != DSVC_SUCCESS)
4910Sstevel@tonic-gate goto out;
4920Sstevel@tonic-gate }
4930Sstevel@tonic-gate
4940Sstevel@tonic-gate /*
4950Sstevel@tonic-gate * Note: we close these descriptors before the rename(2) (rather
4960Sstevel@tonic-gate * than just having the `out:' label clean them up) to save NFS
4970Sstevel@tonic-gate * some work (otherwise, NFS has to save `dnpath' to an alternate
4980Sstevel@tonic-gate * name since its vnode would still be active).
4990Sstevel@tonic-gate */
5000Sstevel@tonic-gate (void) fclose(fp);
5010Sstevel@tonic-gate (void) close(newfd);
5020Sstevel@tonic-gate
5030Sstevel@tonic-gate if (rename(newpath, dnpath) == -1)
5040Sstevel@tonic-gate retval = syserr_to_dsvcerr(errno);
5050Sstevel@tonic-gate
5060Sstevel@tonic-gate return (retval);
5070Sstevel@tonic-gate out:
5080Sstevel@tonic-gate (void) fclose(fp);
5090Sstevel@tonic-gate (void) close(newfd);
5100Sstevel@tonic-gate (void) unlink(newpath);
5110Sstevel@tonic-gate return (retval);
5120Sstevel@tonic-gate }
5130Sstevel@tonic-gate
5140Sstevel@tonic-gate int
add_dn(void * handle,dn_rec_t * addp)5150Sstevel@tonic-gate add_dn(void *handle, dn_rec_t *addp)
5160Sstevel@tonic-gate {
5170Sstevel@tonic-gate return (update_dn((dn_handle_t *)handle, NULL, addp));
5180Sstevel@tonic-gate }
5190Sstevel@tonic-gate
5200Sstevel@tonic-gate int
modify_dn(void * handle,const dn_rec_t * origp,dn_rec_t * newp)5210Sstevel@tonic-gate modify_dn(void *handle, const dn_rec_t *origp, dn_rec_t *newp)
5220Sstevel@tonic-gate {
5230Sstevel@tonic-gate return (update_dn((dn_handle_t *)handle, origp, newp));
5240Sstevel@tonic-gate }
5250Sstevel@tonic-gate
5260Sstevel@tonic-gate int
delete_dn(void * handle,const dn_rec_t * delp)5270Sstevel@tonic-gate delete_dn(void *handle, const dn_rec_t *delp)
5280Sstevel@tonic-gate {
5290Sstevel@tonic-gate return (update_dn((dn_handle_t *)handle, delp, NULL));
5300Sstevel@tonic-gate }
5310Sstevel@tonic-gate
5320Sstevel@tonic-gate int
list_dn(const char * location,char *** listppp,uint_t * countp)5330Sstevel@tonic-gate list_dn(const char *location, char ***listppp, uint_t *countp)
5340Sstevel@tonic-gate {
5350Sstevel@tonic-gate char ipaddr[INET_ADDRSTRLEN];
536*871Scasper struct dirent *result;
5370Sstevel@tonic-gate DIR *dirp;
5380Sstevel@tonic-gate unsigned int i, count = 0;
5390Sstevel@tonic-gate char *re, **new_listpp, **listpp = NULL;
5400Sstevel@tonic-gate int error;
5410Sstevel@tonic-gate
5420Sstevel@tonic-gate dirp = opendir(location);
5430Sstevel@tonic-gate if (dirp == NULL) {
5440Sstevel@tonic-gate switch (errno) {
5450Sstevel@tonic-gate case EACCES:
5460Sstevel@tonic-gate case EPERM:
5470Sstevel@tonic-gate return (DSVC_ACCESS);
5480Sstevel@tonic-gate case ENOENT:
5490Sstevel@tonic-gate return (DSVC_NO_LOCATION);
5500Sstevel@tonic-gate default:
5510Sstevel@tonic-gate break;
5520Sstevel@tonic-gate }
5530Sstevel@tonic-gate return (DSVC_INTERNAL);
5540Sstevel@tonic-gate }
5550Sstevel@tonic-gate
5560Sstevel@tonic-gate /*
5570Sstevel@tonic-gate * Compile a regular expression matching an IP address delimited by
5580Sstevel@tonic-gate * underscores. Note that the `$0' at the end allows us to save the
5590Sstevel@tonic-gate * IP address in ipaddr when calling regex(3C).
5600Sstevel@tonic-gate */
5610Sstevel@tonic-gate re = regcmp("^(([0-9]{1,3}\\_){3}[0-9]{1,3})$0$", (char *)0);
5620Sstevel@tonic-gate if (re == NULL)
5630Sstevel@tonic-gate return (DSVC_NO_MEMORY);
5640Sstevel@tonic-gate
565*871Scasper while ((result = readdir(dirp)) != NULL) {
5660Sstevel@tonic-gate if (regex(re, result->d_name, ipaddr) != NULL) {
5670Sstevel@tonic-gate new_listpp = realloc(listpp,
5680Sstevel@tonic-gate (sizeof (char **)) * (count + 1));
5690Sstevel@tonic-gate if (new_listpp == NULL) {
5700Sstevel@tonic-gate error = DSVC_NO_MEMORY;
5710Sstevel@tonic-gate goto fail;
5720Sstevel@tonic-gate }
5730Sstevel@tonic-gate listpp = new_listpp;
5740Sstevel@tonic-gate listpp[count] = strdup(ipaddr);
5750Sstevel@tonic-gate if (listpp[count] == NULL) {
5760Sstevel@tonic-gate error = DSVC_NO_MEMORY;
5770Sstevel@tonic-gate goto fail;
5780Sstevel@tonic-gate }
5790Sstevel@tonic-gate
5800Sstevel@tonic-gate /*
5810Sstevel@tonic-gate * Change all underscores to dots.
5820Sstevel@tonic-gate */
5830Sstevel@tonic-gate for (i = 0; listpp[count][i] != '\0'; i++) {
5840Sstevel@tonic-gate if (listpp[count][i] == '_')
5850Sstevel@tonic-gate listpp[count][i] = '.';
5860Sstevel@tonic-gate }
5870Sstevel@tonic-gate
5880Sstevel@tonic-gate count++;
5890Sstevel@tonic-gate }
5900Sstevel@tonic-gate }
5910Sstevel@tonic-gate free(re);
5920Sstevel@tonic-gate (void) closedir(dirp);
5930Sstevel@tonic-gate
5940Sstevel@tonic-gate *countp = count;
5950Sstevel@tonic-gate *listppp = listpp;
5960Sstevel@tonic-gate return (DSVC_SUCCESS);
5970Sstevel@tonic-gate
5980Sstevel@tonic-gate fail:
5990Sstevel@tonic-gate free(re);
6000Sstevel@tonic-gate (void) closedir(dirp);
6010Sstevel@tonic-gate
6020Sstevel@tonic-gate for (i = 0; i < count; i++)
6030Sstevel@tonic-gate free(listpp[i]);
6040Sstevel@tonic-gate free(listpp);
6050Sstevel@tonic-gate return (error);
6060Sstevel@tonic-gate }
6070Sstevel@tonic-gate
6080Sstevel@tonic-gate /*
6090Sstevel@tonic-gate * Given a buffer `path' of `pathlen' bytes, fill it in with a path to the
6100Sstevel@tonic-gate * DHCP Network table for IP network `ip' located in directory `dir' with a
6110Sstevel@tonic-gate * suffix of `suffix'.
6120Sstevel@tonic-gate */
6130Sstevel@tonic-gate static void
net2path(char * path,size_t pathlen,const char * dir,ipaddr_t ip,const char * suffix)6140Sstevel@tonic-gate net2path(char *path, size_t pathlen, const char *dir, ipaddr_t ip,
6150Sstevel@tonic-gate const char *suffix)
6160Sstevel@tonic-gate {
6170Sstevel@tonic-gate (void) snprintf(path, pathlen, "%s/%d_%d_%d_%d%s", dir, ip >> 24,
6180Sstevel@tonic-gate (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff, suffix);
6190Sstevel@tonic-gate }
6200Sstevel@tonic-gate
6210Sstevel@tonic-gate /*
6220Sstevel@tonic-gate * Write the dn_rec_t `recp' into the open container `fd' at offset
6230Sstevel@tonic-gate * `recoff'. Returns DSVC_* error code.
6240Sstevel@tonic-gate */
6250Sstevel@tonic-gate static int
write_rec(int fd,dn_rec_t * recp,off_t recoff)6260Sstevel@tonic-gate write_rec(int fd, dn_rec_t *recp, off_t recoff)
6270Sstevel@tonic-gate {
6280Sstevel@tonic-gate char entbuf[1024], *ent = entbuf;
6290Sstevel@tonic-gate size_t entsize = sizeof (entbuf);
6300Sstevel@tonic-gate int entlen;
6310Sstevel@tonic-gate char dn_cip[INET_ADDRSTRLEN], dn_sip[INET_ADDRSTRLEN];
6320Sstevel@tonic-gate char dn_cid[DN_MAX_CID_LEN * 2 + 1];
6330Sstevel@tonic-gate unsigned int dn_cid_len = sizeof (dn_cid);
6340Sstevel@tonic-gate struct in_addr nip;
6350Sstevel@tonic-gate
6360Sstevel@tonic-gate if (octet_to_hexascii(recp->dn_cid, recp->dn_cid_len, dn_cid,
6370Sstevel@tonic-gate &dn_cid_len) != 0)
6380Sstevel@tonic-gate return (DSVC_INTERNAL);
6390Sstevel@tonic-gate
6400Sstevel@tonic-gate nip.s_addr = htonl(recp->dn_cip.s_addr);
6410Sstevel@tonic-gate (void) inet_ntop(AF_INET, &nip, dn_cip, sizeof (dn_cip));
6420Sstevel@tonic-gate nip.s_addr = htonl(recp->dn_sip.s_addr);
6430Sstevel@tonic-gate (void) inet_ntop(AF_INET, &nip, dn_sip, sizeof (dn_sip));
6440Sstevel@tonic-gate again:
6450Sstevel@tonic-gate if (recp->dn_comment[0] != '\0') {
6460Sstevel@tonic-gate entlen = snprintf(ent, entsize, "%s %02hu %s %s %u %s %c%s\n",
6470Sstevel@tonic-gate dn_cid, recp->dn_flags, dn_cip, dn_sip, recp->dn_lease,
6480Sstevel@tonic-gate recp->dn_macro, DNF_COMMENT_CHAR, recp->dn_comment);
6490Sstevel@tonic-gate } else {
6500Sstevel@tonic-gate entlen = snprintf(ent, entsize, "%s %02hu %s %s %u %s\n",
6510Sstevel@tonic-gate dn_cid, recp->dn_flags, dn_cip, dn_sip, recp->dn_lease,
6520Sstevel@tonic-gate recp->dn_macro);
6530Sstevel@tonic-gate }
6540Sstevel@tonic-gate
6550Sstevel@tonic-gate if (entlen == -1)
6560Sstevel@tonic-gate return (syserr_to_dsvcerr(errno));
6570Sstevel@tonic-gate
6580Sstevel@tonic-gate if (entlen > entsize) {
6590Sstevel@tonic-gate entsize = entlen;
6600Sstevel@tonic-gate ent = alloca(entlen);
6610Sstevel@tonic-gate goto again;
6620Sstevel@tonic-gate }
6630Sstevel@tonic-gate
6640Sstevel@tonic-gate if (pnwrite(fd, ent, entlen, recoff) == -1)
6650Sstevel@tonic-gate return (syserr_to_dsvcerr(errno));
6660Sstevel@tonic-gate
6670Sstevel@tonic-gate return (DSVC_SUCCESS);
6680Sstevel@tonic-gate }
669