/* * dhcp_lookup_dd: perform lookup_dd. */ static int dhcp_lookup_dd(dsvc_handle_t hand, boolean_t partial, uint_t query, int count, const void *targetp, void **recordsp, uint_t *nrecordsp) { #ifndef DEBUG return (lookup_dd(hand, partial, query, count, targetp, recordsp, nrecordsp)); #else /* DEBUG */ int ret; int hind = (int)hand; int ind; dn_rec_t *inp; dn_rec_list_t **outp = (dn_rec_list_t **)recordsp; dbg_t *dbp; dbg_t *endp; dbg_rec_t *rp; dn_rec_t *recp; dn_rec_list_t *reclp; dbg_t *dbg_db; if (dbg_net && hind >= 0 && hind < DBG_MAXTABLE) { dbg_db = (dbg_t *)dbg_handle[hind]; if (outp) *outp = NULL; if (nrecordsp) *nrecordsp = 0; inp = (dn_rec_t *)targetp; (void) rw_rdlock(&dbg_lock[hind]); /* * Simple linear search, aided by the fact that * the server currently checks flags. */ if (DSVC_QISEQ(query, DN_QCIP)) { ind = inp->dn_cip.s_addr & dbg_mask; dbp = &dbg_db[ind]; endp = &dbg_db[ind + 1]; } else { ind = 0; dbp = dbg_db; endp = &dbg_db[dbg_size]; } for (; dbp < endp; dbp++, ind++) { /* * Initialize record. fields will be zero'd * when initially mmap'd. */ if (dbp->dn_cid_len == 0) { /* Skip server address to avoid arp issues. */ if (ind == (ntohl(server_ip.s_addr) % 256)) continue; if (dbg_cid) (void) snprintf(dbp->dn_cid, sizeof (dbp->dn_cid), "%8X", dbg_cid++); dbp->dn_flags = dbg_flags; dbp->dn_lease = dbg_lease; dbp->dn_macro[0] = dbg_macro; dbp->dn_macro[1] = '\0'; dbp->dn_cid_len = 1; } if (DSVC_QISEQ(query, DN_QCID) && (inp->dn_cid[0] != dbp->dn_cid[0] || memcmp(dbp->dn_cid, inp->dn_cid, inp->dn_cid_len))) continue; rp = (dbg_rec_t *)smalloc(sizeof (dbg_rec_t)); reclp = &rp->d_reclist; recp = &rp->d_rec; if (nrecordsp) (*nrecordsp)++; reclp->dnl_rec = recp; recp->dn_lease = dbp->dn_lease; recp->dn_sip.s_addr = ntohl(owner_ip->s_addr); recp->dn_cip.s_addr = 0xd000000 + (hind << 16) + ind; recp->dn_cid_len = dbp->dn_cid_len; recp->dn_flags = dbp->dn_flags; recp->dn_macro[0] = dbp->dn_macro[0]; recp->dn_macro[1] = '\0'; if ((recp->dn_cid[0] = dbp->dn_cid[0]) != '\0') (void) memcpy(recp->dn_cid, dbp->dn_cid, dbp->dn_cid_len); if (*outp == NULL) *outp = reclp; else { reclp->dnl_next = *outp; *outp = reclp; } if (count > 0 && nrecordsp && *nrecordsp >= count) break; } (void) rw_unlock(&dbg_lock[hind]); ret = DSVC_SUCCESS; } else ret = lookup_dd(hand, partial, query, count, targetp, recordsp, nrecordsp); return (ret); #endif /* DEBUG */ }
/* * Compares the fields in fields[] agains the fields in target `targetp', * using `query' to decide what fields to compare. Returns B_TRUE if `dnp' * matches `targetp', B_FALSE if not. On success, `dnp' is completely * filled in. */ static boolean_t record_match(char *fields[], dn_rec_t *dnp, const dn_rec_t *targetp, uint_t query) { unsigned int qflags[] = { DN_QFDYNAMIC, DN_QFAUTOMATIC, DN_QFMANUAL, DN_QFUNUSABLE, DN_QFBOOTP_ONLY }; unsigned int flags[] = { DN_FDYNAMIC, DN_FAUTOMATIC, DN_FMANUAL, DN_FUNUSABLE, DN_FBOOTP_ONLY }; unsigned int i; uint_t dn_cid_len; dnp->dn_cip.s_addr = ntohl(inet_addr(fields[DNF_CIP])); if (DSVC_QISEQ(query, DN_QCIP) && dnp->dn_cip.s_addr != targetp->dn_cip.s_addr) return (B_FALSE); if (DSVC_QISNEQ(query, DN_QCIP) && dnp->dn_cip.s_addr == targetp->dn_cip.s_addr) return (B_FALSE); dnp->dn_lease = atoi(fields[DNF_LEASE]); if (DSVC_QISEQ(query, DN_QLEASE) && targetp->dn_lease != dnp->dn_lease) return (B_FALSE); if (DSVC_QISNEQ(query, DN_QLEASE) && targetp->dn_lease == dnp->dn_lease) return (B_FALSE); /* * We use dn_cid_len since dnp->dn_cid_len is of type uchar_t but * hexascii_to_octet() expects an uint_t * */ dn_cid_len = DN_MAX_CID_LEN; if (hexascii_to_octet(fields[DNF_CID], strlen(fields[DNF_CID]), dnp->dn_cid, &dn_cid_len) != 0) return (B_FALSE); dnp->dn_cid_len = dn_cid_len; if (DSVC_QISEQ(query, DN_QCID) && (dnp->dn_cid_len != targetp->dn_cid_len || (memcmp(dnp->dn_cid, targetp->dn_cid, dnp->dn_cid_len) != 0))) return (B_FALSE); if (DSVC_QISNEQ(query, DN_QCID) && (dnp->dn_cid_len == targetp->dn_cid_len && (memcmp(dnp->dn_cid, targetp->dn_cid, dnp->dn_cid_len) == 0))) return (B_FALSE); dnp->dn_sip.s_addr = ntohl(inet_addr(fields[DNF_SIP])); if (DSVC_QISEQ(query, DN_QSIP) && dnp->dn_sip.s_addr != targetp->dn_sip.s_addr) return (B_FALSE); if (DSVC_QISNEQ(query, DN_QSIP) && dnp->dn_sip.s_addr == targetp->dn_sip.s_addr) return (B_FALSE); unescape('|', fields[DNF_MACRO], dnp->dn_macro, sizeof (dnp->dn_macro)); if (DSVC_QISEQ(query, DN_QMACRO) && strcmp(targetp->dn_macro, dnp->dn_macro) != 0) return (B_FALSE); if (DSVC_QISNEQ(query, DN_QMACRO) && strcmp(targetp->dn_macro, dnp->dn_macro) == 0) return (B_FALSE); dnp->dn_flags = atoi(fields[DNF_FLAGS]); for (i = 0; i < sizeof (qflags) / sizeof (unsigned int); i++) { if (DSVC_QISEQ(query, qflags[i]) && (dnp->dn_flags & flags[i]) != (targetp->dn_flags & flags[i])) return (B_FALSE); if (DSVC_QISNEQ(query, qflags[i]) && (dnp->dn_flags & flags[i]) == (targetp->dn_flags & flags[i])) return (B_FALSE); } dnp->dn_sig = atoll(fields[DNF_SIG]); unescape('|', fields[DNF_COMMENT], dnp->dn_comment, sizeof (dnp->dn_comment)); return (B_TRUE); }
/* * Internal version of lookup_dt() used by both lookup_dt() and * update_dt(); same semantics as lookup_dt() except that the `partial' * argument has been generalized into a `flags' field and the handle has * been turned into a FILE pointer. */ static int find_dt(FILE *fp, uint_t flags, uint_t query, int count, const dt_rec_t *targetp, dt_rec_list_t **recordsp, uint_t *nrecordsp) { int retval = DSVC_SUCCESS; char *buf = NULL, *fields[DTF_MAX_FIELDS]; uint_t nrecords; dt_rec_t *recordp; dt_rec_list_t *records, *new_records; unsigned int nfields; off_t recoff; if (fseek(fp, 0, SEEK_SET) == -1) return (DSVC_INTERNAL); records = NULL; for (nrecords = 0; count < 0 || nrecords < count; ) { free(buf); if (flags & FIND_POSITION) recoff = ftello(fp); buf = read_entry(fp); if (buf == NULL) { if (!feof(fp)) retval = DSVC_NO_MEMORY; break; } /* * Skip pure comment lines; for now this just skips the * header information at the top of the container. */ if (buf[0] == DTF_COMMENT_CHAR) continue; /* * Parse out the entry into the dt_rec_t */ nfields = field_split(buf, DTF_MAX_FIELDS, fields, " \t"); if (nfields < DTF_MAX_FIELDS) continue; /* * See if we've got a match. If so, allocate the new * record, fill it in, and continue. */ if (DSVC_QISEQ(query, DT_QTYPE) && targetp->dt_type != fields[DTF_TYPE][0]) continue; else if (DSVC_QISNEQ(query, DT_QTYPE) && targetp->dt_type == fields[DTF_TYPE][0]) continue; if (DSVC_QISEQ(query, DT_QKEY) && strcmp(targetp->dt_key, fields[DTF_KEY]) != 0) continue; else if (DSVC_QISNEQ(query, DT_QKEY) && strcmp(targetp->dt_key, fields[DTF_KEY]) == 0) continue; /* * Caller just wants a count of the number of matching * records, not the records themselves; continue. */ if (recordsp == NULL) { nrecords++; continue; } /* * Allocate record; if FIND_POSITION flag is set, then we * need to allocate an extended (dt_recpos_t) record. */ if (flags & FIND_POSITION) recordp = malloc(sizeof (dt_recpos_t)); else recordp = malloc(sizeof (dt_rec_t)); if (recordp == NULL) { if ((flags & FIND_PARTIAL) == 0) retval = DSVC_NO_MEMORY; break; } /* * Fill in record; if FIND_POSITION flag is set, then pass * back additional location information. */ (void) strlcpy(recordp->dt_key, fields[DTF_KEY], sizeof (recordp->dt_key)); recordp->dt_sig = 1; recordp->dt_type = fields[DTF_TYPE][0]; recordp->dt_value = strdup(fields[DTF_VALUE]); if (recordp->dt_value == NULL) { free(recordp); if ((flags & FIND_PARTIAL) == 0) retval = DSVC_NO_MEMORY; break; } if (flags & FIND_POSITION) { ((dt_recpos_t *)recordp)->dtp_off = recoff; ((dt_recpos_t *)recordp)->dtp_size = ftello(fp) - recoff; } /* * Chuck the record on the list; up the counter. */ new_records = add_dtrec_to_list(recordp, records); if (new_records == NULL) { free_dtrec(recordp); if ((flags & FIND_PARTIAL) == 0) retval = DSVC_NO_MEMORY; break; } records = new_records; nrecords++; } free(buf); if (retval == DSVC_SUCCESS) { *nrecordsp = nrecords; if (recordsp != NULL) *recordsp = records; return (DSVC_SUCCESS); } if (records != NULL) free_dtrec_list(records); return (retval); }
/* * Internal version lookup routine used by both lookup_dn() and * update_dn(); same semantics as lookup_dn() except that the `partial' * argument has been generalized into a `flags' field. */ 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) { int retval = DSVC_SUCCESS; char *fields[DNF_FIELDS]; uint_t nrecords; dn_rec_t dn, *recordp; dn_rec_list_t *records, *new_records; unsigned int nfields; struct stat st; struct in_addr cip_nbo; char *ent0, *ent, *entend; char cip[INET_ADDRSTRLEN + 2]; /* * Page the whole container into memory via mmap() so we can scan it * quickly; map it MAP_PRIVATE so that we can change newlines to * NULs without changing the actual container itself. */ if (fstat(fd, &st) == -1 || st.st_size < 1) return (DSVC_INTERNAL); ent0 = mmap(0, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); if (ent0 == MAP_FAILED) return (DSVC_INTERNAL); /* * NUL-terminate the last byte (which should be a newline) so that * we can safely use string functions on the mapped container. */ ent0[st.st_size - 1] = '\0'; /* * If we're searching by client IP address, then build a target * string we can use to find it quickly. */ if (DSVC_QISEQ(query, DN_QCIP)) { cip[0] = '\n'; cip_nbo.s_addr = htonl(targetp->dn_cip.s_addr); (void) inet_ntop(AF_INET, &cip_nbo, cip + 1, INET_ADDRSTRLEN); (void) strlcat(cip, "|", sizeof (cip)); } records = NULL; ent = ent0; for (nrecords = 0; count < 0 || nrecords < count; ent = entend + 1) { /* * Bail if we've reached the end of the container. */ if (ent - ent0 >= st.st_size) break; /* * If we're searching by client IP address, locate it * quickly using strstr(3C); if we can't find it by this * technique then it's not in the container. */ if (DSVC_QISEQ(query, DN_QCIP)) { /* * If we've already found the DN_QCIP record, bail. */ if (nrecords > 0) break; ent = strstr(ent, cip); if (ent == NULL) break; ent++; } /* * Find the end of the record and change it a NUL byte so * that it is interpreted correctly with field_split() and * record_match() below. If we can't find a trailing * newline, then it must be the last record (whose newline * we already changed to a NUL above). */ entend = strchr(ent, '\n'); if (entend != NULL) *entend = '\0'; else entend = &ent0[st.st_size - 1]; /* * Skip pure comment lines; for now this just skips the * header information at the top of the container. */ if (ent[0] == DNF_COMMENT_CHAR) continue; /* * Split the buffer up into DNF_FIELDS fields. */ nfields = field_split(ent, DNF_FIELDS, fields, "|"); if (nfields < DNF_FIELDS) continue; /* * See if we've got a match, filling in dnf.dnf_rec as * we go. If record_match() succeeds, dnf.dnf_rec will * be completely filled in. */ if (!record_match(fields, &dn, targetp, query)) continue; /* * Caller just wants a count of the number of matching * records, not the records themselves; continue. */ if (recordsp == NULL) { nrecords++; continue; } /* * Allocate record; if FIND_POSITION flag is set, then * we need to allocate an extended (dn_recpos_t) record. */ if (flags & FIND_POSITION) recordp = malloc(sizeof (dn_recpos_t)); else recordp = malloc(sizeof (dn_rec_t)); if (recordp == NULL) { if ((flags & FIND_PARTIAL) == 0) retval = DSVC_NO_MEMORY; break; } /* * Fill in record; do a structure copy from our automatic * dn. If FIND_POSITION flag is on, pass back additional * position information. */ *recordp = dn; if (flags & FIND_POSITION) { ((dn_recpos_t *)recordp)->dnp_off = ent - ent0; ((dn_recpos_t *)recordp)->dnp_size = entend - ent + 1; } /* * Chuck the record on the list; up the counter. */ new_records = add_dnrec_to_list(recordp, records); if (new_records == NULL) { free(recordp); if ((flags & FIND_PARTIAL) == 0) retval = DSVC_NO_MEMORY; break; } records = new_records; nrecords++; } (void) munmap(ent0, st.st_size); if (retval == DSVC_SUCCESS) { *nrecordsp = nrecords; if (recordsp != NULL) *recordsp = records; return (DSVC_SUCCESS); } if (records != NULL) free_dnrec_list(records); return (retval); }