OpenSolaris_b135/lib/libnisdb/yptol/dit_access_utils.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * DESCRIPTION:	Contains dit_access interface support functions.
 */
#include <sys/systeminfo.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/systeminfo.h>
#include <unistd.h>
#include <stdlib.h>
#include <syslog.h>
#include <ndbm.h>
#include <strings.h>
#include <errno.h>
#include "../ldap_util.h"
#include "../ldap_map.h"
#include "../ldap_parse.h"
#include "../ldap_structs.h"
#include "../ldap_val.h"
#include "../ldap_ruleval.h"
#include "../ldap_op.h"
#include "../ldap_attr.h"
#include "../ldap_nisdbquery.h"
#include "../nisdb_mt.h"
#include "shim.h"
#include "yptol.h"
#include "dit_access_utils.h"

/*
 * Returns 'map,domain.'
 */
char *
getFullMapName(char *map, char *domain) {
	char	*myself = "getFullMapName";
	char	*objPath;
	if (map == 0 || domain == 0) {
		return (0);
	}
	objPath =  scat(myself, T, scat(myself, F, map, ","),
		scat(myself, F, domain, "."));

	return (objPath);
}

/*
 * Convert string to __nis_value_t
 */
__nis_value_t *stringToValue(char *dptr, int dsize) {
	char		*myself = "stringToValue";
	char		*emptystr = "";
	__nis_value_t	*val;

	if ((val = am(myself, sizeof (*val))) == 0) {
		return (0);
	}

	val->type = vt_string;
	val->repeat = 0;
	val->numVals = 1;
	if ((val->val = am(myself, sizeof (val->val[0]))) == 0) {
		sfree(val);
		return (0);
	}

	/*
	 * Null strings or strings with length 0 are treated
	 * as empty strings with length 1
	 */
	if (dptr == 0 || dsize <= 0) {
		dptr = emptystr;
		dsize = 1;
	}

	val->val->length = dsize;
	if (dptr[dsize - 1] != '\0') {
		val->val->length = dsize + 1;
	}

	val->val->value = am(myself, val->val->length);
	if (val->val->value == 0) {
		freeValue(val, 1);
		return (0);
	}
	(void) memcpy(val->val->value, dptr, dsize);

	return (val);
}

/*
 * Returns an array of rule-values corresponding to the
 * splitfields.
 */
__nis_rule_value_t *
processSplitField(__nis_table_mapping_t *sf, __nis_value_t *inVal,
			int *nv, int *statP) {

	char			*sepset;
	__nis_rule_value_t	*rvq;
	__nis_mapping_format_t  *ftmp;
	__nis_value_t		**valA, *tempVal;
	int			i, j, res, numVals, oldlen, count;
	char			*str, *oldstr;
	char			*myself = "processSplitField";

	/* sf will be non NULL */

	if (inVal == 0 || inVal->type != vt_string) {
		*statP =  MAP_PARAM_ERROR;
		return (0);
	}

	/* Get the separator list */
	sepset = sf->separatorStr;

	/* Initialize rule-value */
	rvq = 0;
	count = 0;

	if ((tempVal = stringToValue(inVal->val->value,
			inVal->val->length)) == 0) {
		*statP = MAP_NO_MEMORY;
		return (0);
	}

	str = oldstr = tempVal->val->value;
	oldlen = tempVal->val->length;

	while (str) {
		tempVal->val->value = str;
		tempVal->val->length = strlen(str) + 1;

		/* Loop to check which format matches str */
		for (i = 0; i <= sf->numSplits; i++) {
			valA = matchMappingItem(sf->e[i].element.match.fmt,
				tempVal, &numVals, sepset, &str);
			if (valA == 0) {
				/* The format didn't match. Try the next one */
				continue;
			}

			/*
			 * If we are here means we had a match.
			 * Each new set of values obtained from the match is
			 * added to a new rule-value. This is to preserve the
			 * the distinction between each set.
			 */
			rvq = growRuleValue(count, count + 1, rvq, 0);
			if (rvq == 0) {
				*statP = MAP_INTERNAL_ERROR;
				for (j = 0; j < numVals; j++)
					freeValue(valA[j], 1);
				sfree(valA);
				tempVal->val->value = oldstr;
				tempVal->val->length = oldlen;
				freeValue(tempVal, 1);
				return (0);
			}
			count++;

			for (j = 0; j < numVals; j++) {
				res = addCol2RuleValue(vt_string,
					sf->e[i].element.match.item[j].name,
					valA[j]->val->value,
					valA[j]->val->length,
					&rvq[count - 1]);
				if (res == -1) {
					*statP = MAP_INTERNAL_ERROR;
					for (; j < numVals; j++)
						freeValue(valA[j], 1);
					sfree(valA);
					tempVal->val->value = oldstr;
					tempVal->val->length = oldlen;
					freeValue(tempVal, 1);
					freeRuleValue(rvq, count);
					return (0);
				}
				freeValue(valA[j], 1);
			}
			sfree(valA);

			/*
			 * Since we had a match, break out of this loop
			 * to parse remainder of str
			 */
			break;
		}

		/* Didn't find any match, so get out of the loop */
		if (i > sf->numSplits) {
			str = 0;
			break;
		}

		/* Skip the separators before looping back */
		if (str) {
			str = str + strspn(str, sepset);
			if (*str == '\0')
				break;
		}
	}

	tempVal->val->value = oldstr;
	tempVal->val->length = oldlen;
	freeValue(tempVal, 1);

	if (str == 0) {
		freeRuleValue(rvq, count);
		return (0);
	}

	if (nv != 0)
		*nv = count;

	return (rvq);
}

/*
 * Convert the datum to an array of RuleValues
 */
__nis_rule_value_t *
datumToRuleValue(datum *key, datum *value, __nis_table_mapping_t *t,
			int *nv, char *domain, bool_t readonly, int *statP) {

	__nis_rule_value_t	*rvq, *subrvq, *newrvq;
	__nis_value_t		*val;
	__nis_value_t		**valA;
	__nis_table_mapping_t	*sf;
	int			valueLen, comLen, numVals, nr, count = 1;
	int			i, j, k, l, af;
	char			*ipaddr, *ipvalue;

	/*  At this point, 't' is always non NULL */

	/* Initialize rule-value */
	if ((rvq = initRuleValue(1, 0)) == 0) {
		*statP = MAP_INTERNAL_ERROR;
		return (0);
	}

	/* Add domainname to rule-value */
	if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain),
						rvq)) {
		freeRuleValue(rvq, 1);
		*statP = MAP_INTERNAL_ERROR;
		return (0);
	}

	/* Handle key */
	if (key != 0) {
		/* Add field=value pair for N2LKEY */
		i = addCol2RuleValue(vt_string, N2LKEY, key->dptr, key->dsize,
						rvq);

		/* For readonly, add field=value pair for N2LSEARCHKEY */
		if (readonly == TRUE && i == 0) {
			i = addCol2RuleValue(vt_string, N2LSEARCHKEY, key->dptr,
						key->dsize, rvq);
		}
		if (i) {
			freeRuleValue(rvq, 1);
			*statP = MAP_INTERNAL_ERROR;
			return (0);
		}

		/* Add field=value pairs for IP addresses */
		if (checkIPaddress(key->dptr, key->dsize, &ipaddr) > 0) {
			/* If key is IPaddress, use preferred format */
			ipvalue = ipaddr;
			valueLen = strlen(ipaddr);
			i = addCol2RuleValue(vt_string, N2LIPKEY, ipvalue,
						valueLen, rvq);
		} else {
			/* If not, use original value for N2LSEARCHIPKEY */
			ipaddr = 0;
			ipvalue = key->dptr;
			valueLen = key->dsize;
			i = 0;
		}

		if (readonly == TRUE && i == 0) {
			i = addCol2RuleValue(vt_string, N2LSEARCHIPKEY, ipvalue,
								valueLen, rvq);
		}
		sfree(ipaddr);
		if (i) {
			freeRuleValue(rvq, 1);
			*statP = MAP_INTERNAL_ERROR;
			return (0);
		}
	}

	/* Handle datum value */
	if (value != 0 && t->e) {
		valueLen = value->dsize;
		/*
		 * Extract the comment, if any, and add it to
		 * the rule-value.
		 */
		if (t->commentChar != '\0') {
			/*
			 * We loop on value->dsize because value->dptr
			 * may not be NULL-terminated.
			 */
			for (i = 0; i < value->dsize; i++) {
				if (value->dptr[i] == t->commentChar) {
					valueLen = i;
					comLen = value->dsize - i - 1;
					if (comLen == 0)
						break;
					if (addCol2RuleValue(vt_string,
						N2LCOMMENT, value->dptr + i + 1,
						comLen, rvq)) {
						freeRuleValue(rvq, 1);
						*statP = MAP_INTERNAL_ERROR;
						return (0);
					}
					break;
				}
			}
		}

		/* Skip trailing whitespaces */
		for (; valueLen > 0 && (value->dptr[valueLen - 1] == ' ' ||
			value->dptr[valueLen - 1] == '\t'); valueLen--);

		/*
		 * At this point valueLen is the effective length of
		 * the data. Convert value into __nis_value_t so that
		 * we can use the matchMappingItem function to break it
		 * into fields.
		 */
		if ((val = stringToValue(value->dptr, valueLen)) == 0) {
			freeRuleValue(rvq, 1);
			*statP = MAP_NO_MEMORY;
			return (0);
		}

		/* Perform namefield match */
		valA = matchMappingItem(t->e->element.match.fmt, val,
							&numVals, 0, 0);
		if (valA == 0) {
			freeValue(val, 1);
			freeRuleValue(rvq, 1);
			*statP = MAP_NAMEFIELD_MATCH_ERROR;
			return (0);
		}

		/* We don't need val anymore, so free it */
		freeValue(val, 1);

		/*
		 * Since matchMappingItem only returns us an array of
		 * __nis_value_t's, we need to associate each value
		 * in the array with the corresponding item name.
		 * This code assumes that numVals will be less than or
		 * equal to the number of item names associated with
		 * the format.
		 * These name=value pairs are added to rvq.
		 */
		for (i = 0, *statP = SUCCESS; i < numVals; i++) {
			for (j = 0; j < count; j++) {
				if (addCol2RuleValue(vt_string,
					t->e->element.match.item[i].name,
					valA[i]->val->value,
					valA[i]->val->length, &rvq[j])) {
					*statP = MAP_INTERNAL_ERROR;
					break;
				}
			}
			if (*statP == MAP_INTERNAL_ERROR)
				break;

			/*
			 * Check if splitField exists for the field.
			 * Since splitfields are also stored as mapping
			 * structures, we need to get the hash table entry
			 * corresponding to the splitfield name
			 */
			sf = mappingFromMap(t->e->element.match.item[i].name,
					domain, statP);
			if (*statP == MAP_NO_MEMORY)
				break;
			*statP = SUCCESS;
			if (sf == 0)
				continue;

			/*
			 * Process and add splitFields to rule-value rvq
			 */
			subrvq = processSplitField(sf, valA[i], &nr, statP);

			if (subrvq == 0) {
				/* statP would have been set */
				break;
			}

			/*
			 * We merge 'count' rule-values in rvq with 'nr'
			 * rule-values from subrvq to give us a whopping
			 * 'count * nr' rule-values
			 */

			/* Initialize the new rule-value array */
			if ((newrvq = initRuleValue(count * nr, 0)) == 0) {
				*statP = MAP_INTERNAL_ERROR;
				freeRuleValue(subrvq, nr);
				break;
			}

			for (j = 0, l = 0; j < nr; j++) {
				for (k = 0; k < count; k++, l++) {
					if ((mergeRuleValue(&newrvq[l],
							&rvq[k]) == -1) ||
							(mergeRuleValue(
							&newrvq[l],
							&subrvq[j]) == -1)) {
						*statP = MAP_INTERNAL_ERROR;
						for (i = 0; i < numVals; i++)
							freeValue(valA[i], 1);
						sfree(valA);
						freeRuleValue(rvq, count);
						freeRuleValue(newrvq,
								count * nr);
						freeRuleValue(subrvq, nr);
						return (0);
					}
				}
			}

			freeRuleValue(rvq, count);
			rvq = newrvq;
			count = l;
			freeRuleValue(subrvq, nr);

		}

		/* We don't need valA anymore, so free it */
		for (i = 0; i < numVals; i++)
			freeValue(valA[i], 1);
		sfree(valA);

		if (*statP != SUCCESS) {
			freeRuleValue(rvq, count);
			return (0);
		}

	} /* if value */

	if (nv != 0)
		*nv = count;
	return (rvq);

}

/*
 * Generate name=values pairs for splitfield names
 *
 * Consider Example:
 *	nisLDAPnameFields club:
 *			("%s %s %s", name, code, members)
 *	nisLDAPsplitField members:
 *			("(%s,%s,%s)", host, user, domain),
 *			("%s", group)
 * On entry,
 * - rv is an array of numVals rule-values each containing
 * name=value pairs for names occuring in nisLDAPsplitField.
 * (i.e host, user, domain, group)
 * - trv contains name=value pairs for names occuring in
 * nisLDAPnameFields. (i.e name, code but not members)
 *
 * For every name in nisLDAPnamefields that is a splitfield,
 * this function applies the data in rv to the corresponding
 * splitfield formats (accessed thru t), to generate a single
 * string value for the corresponding splitfield (members).
 * This new name=value pair is then added to trv.
 * Besides, any uninitialized namefield names are set to empty strings.
 */
suc_code
addSplitFieldValues(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
			__nis_rule_value_t *trv, int numVals, char *domain) {
	__nis_table_mapping_t	*sf;
	__nis_value_t		*val;
	int			i, j, k, nitems, res, statP;
	char			*str, *tempstr;
	char			delim[2] = {0, 0};
	char			*emptystr = "";
	char			*myself = "addSplitFieldValues";

	if (trv == 0)
		return (MAP_INTERNAL_ERROR);

	if (t->e == 0)
		return (SUCCESS);

	nitems = t->e->element.match.numItems;

	/*
	 * Procedure:
	 * - Check each name in nisLDAPnamefield
	 * - if it's a splifield, construct its value and add it to trv
	 * - if not, check if it has a value
	 * - if not, add empty string
	 */
	for (i = 0, sf = 0; i < nitems; i++) {
		if (rv) {
			/*
			 * str will eventually contain the single string
			 * value for the corresponding  splitfield.
			 * No point initializing str if rv == 0 because
			 * splitfield cannot be constructed without rv.
			 * So, only initialized here.
			 */
			str = 0;

			/* Check if it's a splitfield name */
			sf = mappingFromMap(t->e->element.match.item[i].name,
				domain, &statP);

			/*
			 * Return only incase of memory allocation failure.
			 * The other error case (MAP_NO_MAPPING_EXISTS),
			 * indicates that the item name is not a splitfieldname
			 * i.e it's a namefieldname. This case is handled by
			 * the following if (sf == 0)
			 */
			if (statP == MAP_NO_MEMORY)
				return (statP);
		}

		if (sf == 0) {
			/*
			 * Not a splitfield name. Verify if it has a value
			 */
			if (findVal(t->e->element.match.item[i].name,
				trv, mit_nisplus) == 0) {
				/* if not, use empty string */
				res = addCol2RuleValue(vt_string,
					t->e->element.match.item[i].name,
					emptystr, 0, trv);
				if (res == -1) {
					return (MAP_INTERNAL_ERROR);
				}
			}
			/*
			 * If rv == 0 then sf == 0 so we will continue here
			 * i.e. does not matter that str is not yet set up.
			 */
			continue;
		}

		/* Code to construct a single value */

		/* Use the first separator character as the delimiter */
		delim[0] = sf->separatorStr[0];

		for (j = 0; j < numVals; j++) {
			/* sf->numSplits is zero-based */
			for (k = 0; k <= sf->numSplits; k++) {
				val = getMappingFormatArray(
					sf->e[k].element.match.fmt, &rv[j],
					fa_item,
					sf->e[k].element.match.numItems,
					sf->e[k].element.match.item);
				if (val == 0)
					continue;
				if (val->numVals > 0) {
					if (str) {
						tempstr = scat(myself,
							0, str, delim);
						sfree(str);
						if (tempstr)
							str = tempstr;
						else {
							freeValue(val, 1);
							return (MAP_NO_MEMORY);
						}
					}
					tempstr = scat(myself, 0, str,
						val->val->value);
					sfree(str);
					if (tempstr)
						str = tempstr;
					else {
						freeValue(val, 1);
						return (MAP_NO_MEMORY);
					}
				}
				freeValue(val, 1);
			}
		}
		if (str == 0)
			str = emptystr;

		res = addCol2RuleValue(vt_string,
				t->e->element.match.item[i].name,
				str, strlen(str), trv);

		if (str != emptystr)
			sfree(str);

		if (res == -1) {
			return (MAP_INTERNAL_ERROR);
		}
	}

	return (SUCCESS);
}

/*
 * Updates 'rv' with NIS name=value pairs suitable to
 * construct datum from namefield information.
 * Some part based on createNisPlusEntry (from ldap_nisdbquery.c)
 * This code assumes that from a given LDAP entry, applying the
 * mapping rules, would give us one or more NIS entries, differing
 * only in key.
 */
suc_code
buildNISRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
					char *domain) {
	int			r, i, j, k, l, index, nrq, res, len;
	int			numItems, splitname, count, statP;
	__nis_value_t		*rval;
	__nis_mapping_item_t	*litem;
	__nis_mapping_rule_t	*rl;
	__nis_rule_value_t	*rvq;
	char			*value, *emptystr = "";

	statP = SUCCESS;

	/* Initialize default base */
	__nisdb_get_tsd()->searchBase = t->objectDN->read.base;

	/* Initialize rule-value rvq */
	rvq = 0;
	count = 0;

	/* Add domainname to rule-value */
	if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain),
						rv)) {
		return (MAP_INTERNAL_ERROR);
	}

	for (r = 0; r < t->numRulesFromLDAP; r++) {
		rl = t->ruleFromLDAP[r];

		/* Set escapeFlag if RHS is "dn" to remove escape chars */
		if (rl->rhs.numElements == 1 &&
			rl->rhs.element->type == me_item &&
			rl->rhs.element->element.item.type == mit_ldap &&
			strcasecmp(rl->rhs.element->element.item.name, "dn")
									== 0) {
				__nisdb_get_tsd()->escapeFlag = '2';
		}

		rval = buildRvalue(&rl->rhs, mit_ldap, rv, NULL);

		/* Reset escapeFlag */
		__nisdb_get_tsd()->escapeFlag = '\0';

		if (rval == 0) {
			continue;
		}

		if (rval->numVals <= 0) {
			/* Treat as invalid */
			freeValue(rval, 1);
			continue;
		}

		litem = buildLvalue(&rl->lhs, &rval, &numItems);
		if (litem == 0) {
			/* This will take care of numItems == 0 */
			freeValue(rval, 1);
			continue;
		}

		if (rval->numVals > 1) {
			if (numItems == 1 && litem->repeat)
				nrq = rval->numVals;
			else if (numItems > 1 && rval->repeat)
				nrq = 1 + ((rval->numVals-1)/numItems);
			else
				nrq = 1;
		} else
			nrq = 1;

		/* Set splitname if splitfield names are specified */
		for (i = 0; i < numItems; i++) {
			if (strcasecmp(litem[i].name, N2LKEY) == 0 ||
				strcasecmp(litem[i].name, N2LIPKEY) == 0 ||
				strcasecmp(litem[i].name, N2LCOMMENT) == 0)
				continue;
			for (j = 0; j < t->numColumns; j++) {
				if (strcmp(litem[i].name, t->column[j]) == 0)
					break;
			}
			if (j == t->numColumns)
				break;
		}

		splitname = (i < numItems)?1:0;

		for (j = 0; j < nrq; j++) {
			if (splitname == 1) {
				/*
				 * Put every value of splitfieldname in a new
				 * rule-value. Helps generating splitfields.
				 */
				rvq = growRuleValue(count, count + 1, rvq, 0);
				if (rvq == 0) {
					freeRuleValue(rvq, count);
					freeValue(rval, 1);
					freeMappingItem(litem, numItems);
					return (MAP_INTERNAL_ERROR);
				}
				count++;
			}

			for (k = j % nrq, l = 0; l < numItems; k += nrq, l++) {
				/* If we run out of values, use empty strings */
				if (k >= rval->numVals) {
					value = emptystr;
					len = 0;
				} else {
					value = rval->val[k].value;
					len = rval->val[k].length;
				}
				res = (splitname == 1)?addCol2RuleValue(
					vt_string, litem[l].name, value,
					len, &rvq[count - 1]):0;
				if (res != -1)
					res = addCol2RuleValue(vt_string,
						litem[l].name, value, len, rv);
				if (res == -1) {
					freeRuleValue(rvq, count);
					freeValue(rval, 1);
					freeMappingItem(litem, numItems);
					return (MAP_INTERNAL_ERROR);
				}
			}
		}
		freeValue(rval, 1);
		rval = 0;
		freeMappingItem(litem, numItems);
		litem = 0;
		numItems = 0;
	} /* for r < t->numRulesFromLDAP */

	statP = addSplitFieldValues(t, rvq, rv, count, domain);

	if (rvq)
		freeRuleValue(rvq, count);

	if (verifyIndexMatch(t, 0, rv, 0, 0) == 0)
		return (MAP_INDEXLIST_ERROR);
	return (statP);

} /* end of buildNISRuleValue */

/*
 * Convert rule-value to datum using namefield information
 */
datum *
ruleValueToDatum(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *statP) {
	__nis_value_t	*val;
	datum		*value;
	char		*str, *cstr, commentSep[3] = {' ', 0, 0};
	char		*myself = "ruleValueToDatum";

	/* No error yet */
	*statP = 0;

	/* Return empty datum if no namefield information available */
	if (t->e == 0) {
		if ((value = am(myself, sizeof (*value))) == 0)
			*statP = MAP_NO_MEMORY;
		return (value);
	}

	val = getMappingFormatArray(t->e->element.match.fmt, rv,
				fa_item, t->e->element.match.numItems,
				t->e->element.match.item);

	if (val && val->val && val->val->value) {
		if ((value = am(myself, sizeof (*value))) == 0) {
			*statP = MAP_NO_MEMORY;
			freeValue(val, 1);
			return (0);
		}

		/* Strip trailing whitespaces */
		cstr = (char *)val->val->value + val->val->length;
		for (; cstr >= (char *)val->val->value &&
			(*cstr == ' ' || *cstr == '\t'); *cstr-- = '\0');

		if (t->commentChar != '\0' &&
		    (str = findVal(N2LCOMMENT, rv, mit_nisplus)) != 0 &&
		    *str != '\0') {
			commentSep[1] = t->commentChar;
			cstr = scat(myself, F, commentSep, str);
			if (cstr) {
				value->dptr = scat(myself, F,
						val->val->value, cstr);
				sfree(cstr);
			}
		} else {
			value->dptr = sdup(myself, T, val->val->value);
		}
		freeValue(val, 1);
		if (value->dptr) {
			value->dsize = strlen(value->dptr);
			return (value);
		} else {
			*statP = MAP_NO_MEMORY;
			sfree(value);
			return (0);
		}
	}

	*statP = MAP_NAMEFIELD_MATCH_ERROR;
	return (0);
}

datum *
getKeyFromRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *nv,
					int *statP) {
	int	i, j;
	datum	*key = 0;
	char	*str;
	char	*myself = "getKeyFromRuleValue";

	/* No error yet */
	*statP = 0;

	if (rv == 0 || nv == 0)
		return (0);

	for (i = 0; i < rv->numColumns; i++) {
		if (rv->colName[i] == 0)
			continue;
		if (strcasecmp(N2LKEY, rv->colName[i]) == 0 ||
				strcasecmp(N2LIPKEY, rv->colName[i]) == 0) {
			if ((*nv = rv->colVal[i].numVals) == 0)
				return (0);
			if ((key = am(myself, sizeof (key[0]) * *nv)) == 0) {
				*statP = MAP_NO_MEMORY;
				return (0);
			}
			for (j = 0; j < *nv; j++) {
				if ((str = rv->colVal[i].val[j].value) == 0) {
					key[j].dsize = 0;
					key[j].dptr = 0;
				} else {
					if (verifyIndexMatch(t, 0, 0,
							rv->colName[i],
								str) == 0) {
						key[j].dsize = 0;
						key[j].dptr = 0;
						continue;
					}
					key[j].dsize = strlen(str);
					key[j].dptr = am(myself,
							key[j].dsize + 1);
					if (key[j].dptr == 0) {
						*statP = MAP_NO_MEMORY;
						for (--j; j >= 0; j--)
							sfree(key[j].dptr);
						sfree(key);
						return (0);
					}
					bcopy(str, key[j].dptr, key[j].dsize);
				}
			}
			return (key);
		}
	}
	return (0);
}

/*
 * Get the mapping structure corresponding to `map,domain.'
 */
__nis_table_mapping_t *
mappingFromMap(char *map, char *domain, int *statP) {
	char			*mapPath;
	__nis_table_mapping_t	*t;

	/* No error yet */
	*statP = 0;

	/* Construct map,domain. */
	if ((mapPath = getFullMapName(map, domain)) == 0) {
		*statP = MAP_NO_MEMORY;
		return (0);
	}

	/* Get the hash table entry for the mapPath */
	if ((t = __nis_find_item_mt(mapPath, &ldapMappingList, 1, 0))
			== 0) {
		*statP = MAP_NO_MAPPING_EXISTS;
	}
	sfree(mapPath);
	return (t);
}

/*
 * Verify at least one key value obtained from DIT matches the search key
 * RETURNS:	 1	MATCH
 *		 0	NO MATCH
 *		-1	NO KEY FOUND
 */
static int
verifyKey(char *key, __nis_rule_value_t *rv) {
	int	i, j;
	char	*sipkey, *str;

	for (i = 0; i < rv->numColumns; i++) {
		if (rv->colName[i] == 0)
			continue;
		if (strcasecmp(N2LKEY, rv->colName[i]) == 0) {
			if (rv->colVal[i].val == 0)
				return (0);
			for (j = 0; j < rv->colVal[i].numVals; j++) {
				str = (char *)rv->colVal[i].val[j].value;
				if (str && strcmp(str, key) == 0)
					return (1);
			}
			return (0);
		} else if (strcasecmp(N2LIPKEY, rv->colName[i]) == 0) {
			if (checkIPaddress(key, strlen(key), &sipkey) > 0) {
				if (rv->colVal[i].val == 0)
					return (0);
				for (j = 0; j < rv->colVal[i].numVals; j++) {
					str = rv->colVal[i].val[j].value;
					if (str && strcmp(str, sipkey) == 0) {
						sfree(sipkey);
						return (1);
					}
				}
				sfree(sipkey);
			}
			return (0);
		}
	}
	return (-1);
}

/*
 * Read (i.e get and map) a single NIS entry from the LDAP DIT
 */
bool_t
singleReadFromDIT(char *map, char *domain, datum *key, datum *value,
							int *statP) {
	__nis_table_mapping_t	*t;
	__nis_rule_value_t	*rv_request = 0, *rv_result = 0;
	__nis_ldap_search_t	*ls;
	__nis_object_dn_t	*objectDN = NULL;
	int			i, rc, nr = 0, nv = 0;
	datum			*datval = 0;
	char			*skey, *str, *sipkey;
	char			*myself = "singleReadFromDIT";

	*statP = SUCCESS;

	if (!map || !domain || !key || !value) {
		*statP = MAP_PARAM_ERROR;
		return (FALSE);
	}


	/* Get the mapping information for the map */
	if ((t = mappingFromMap(map, domain, statP)) == 0) {
		/*
		 * No problem. We don't handle this map and domain. Maybe it's
		 * handled by a service other than NIS.
		 */
		return (FALSE);
	}

	/* NULL-terminated version of datum key for logging */
	if ((skey = am(myself, key->dsize + 1)) == 0) {
		*statP = MAP_NO_MEMORY;
		return (FALSE);
	}
	(void) memcpy(skey, key->dptr, key->dsize);

	if ((str = getFullMapName(map, domain)) == 0) {
		*statP = MAP_NO_MEMORY;
		return (FALSE);
	}

	/* For each alternate mapping */
	for (; t != 0; t = t->next) {
		/* Verify objName */
		if (strcmp(str, t->objName) != 0) {
			continue;
		}

		/* Verify if key matches the index */
		if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 ||
			verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0)
			continue;

		/* Check if rulesFromLDAP are provided */
		if (t->numRulesFromLDAP == 0) {
			logmsg(MSG_NOTIMECHECK, LOG_INFO,
				"%s: No rulesFromLDAP information available "
				"for %s (%s)", myself, t->dbId, map);
			continue;
		}

		/* Convert key into rule-value */
		if ((rv_request = datumToRuleValue(key, 0, t, 0, domain, TRUE,
								statP)) == 0) {
			logmsg(MSG_NOTIMECHECK, LOG_ERR,
				"%s: Conversion error %d (NIS to name=value "
				"pairs) for NIS key (%s) for %s (%s)",
				myself, *statP, skey, t->dbId, map);
			continue;
		}
		/* Convert rule-value into ldap request */
		for (objectDN = t->objectDN; objectDN &&
				objectDN->read.base;
				objectDN = objectDN->next) {
			ls = createLdapRequest(t, rv_request, 0, 1, NULL,
								objectDN);
			if (ls == 0) {
				*statP = MAP_CREATE_LDAP_REQUEST_ERROR;
				logmsg(MSG_NOTIMECHECK, LOG_ERR,
					"%s: Failed to create ldapSearch "
					"request for "
					"NIS key (%s) for %s (%s) "
					"for base %s",
					myself, skey, t->dbId, map,
					objectDN->read.base);
				continue;
			}
			ls->timeout.tv_sec = SINGLE_ACCESS_TIMEOUT_SEC;
			ls->timeout.tv_usec = SINGLE_ACCESS_TIMEOUT_USEC;
			/* Query LDAP */
			nr = (ls->isDN)?0:-1;
			rv_result = ldapSearch(ls, &nr, 0, statP);
			freeLdapSearch(ls);
			if (rv_result == 0) {
				if (*statP == LDAP_NO_SUCH_OBJECT) {
					/* Entry does not exist in */
					/* the ldap server */
				}
				continue;
			}
			freeRuleValue(rv_request, 1);
			rv_request = 0;

			/* if result > 1, first match will be returned */
			if (nr > 1) {
				logmsg(MSG_NOTIMECHECK, LOG_INFO,
					"%s: %d ldapSearch results "
					"for NIS key (%s) "
					"for %s (%s) for base %s. "
					"First match will be returned ",
					myself, nr, skey, t->dbId, map,
					objectDN->read.base);
			}

			for (i = 0; i < nr; i++) {
				/* Convert LDAP data to NIS equivalents */
				*statP = buildNISRuleValue(t, &rv_result[i],
								domain);
				if (*statP == MAP_INDEXLIST_ERROR)
					continue;

				if (*statP != SUCCESS) {
					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
					"%s: Conversion error %d (LDAP to "
					"name=value pairs) for NIS key (%s) "
					"for %s (%s) for base %s", myself,
					*statP, skey,
					t->dbId, map, objectDN->read.base);
					continue;
				}

			/*
			 * Check if 'key' from the ldap result matches the key
			 * provided by our caller
			 */
				if ((rc = verifyKey(skey, &rv_result[i]))
						== -1) {
					logmsg(MSG_NOTIMECHECK, LOG_INFO,
					"%s: Cannot verify key from ldap "
					"result for NIS key (%s) for %s (%s) "
					"for base %s",
					myself, skey, t->dbId, map,
					objectDN->read.base);
					continue;
				}

				if (rc == 1) {
					datval = ruleValueToDatum(t,
							&rv_result[i], statP);
					if (datval == 0) {
						logmsg(MSG_NOTIMECHECK,
						LOG_WARNING,
						"%s: Conversion error %d "
						"(name=value pairs to NIS) "
						"for NIS key (%s) for %s (%s)"
						" for base %s",
						myself,
						*statP, skey, t->dbId, map,
						objectDN->read.base);
						continue;
					}
					if (value) {
						value->dptr = datval->dptr;
						value->dsize = datval->dsize;
					}
					sfree(datval);
					sfree(skey);
					freeRuleValue(rv_result, nr);
					rv_result = 0;
					*statP = SUCCESS;

					/* Free full map name */
					sfree(str);

					return (TRUE);
				}
			}
			freeRuleValue(rv_result, nr);
			rv_result = 0;
		}   /* end of for over objectDN */

		if (rv_request != 0) {
			freeRuleValue(rv_request, 1);
			rv_request = 0;
		}
		if (rv_result != 0) {
			freeRuleValue(rv_result, nr);
			rv_result = 0;
		}
	}
	sfree(skey);
	*statP = MAP_NO_MATCHING_KEY;

	/* Free full map name */
	sfree(str);

	return (FALSE);
}


/*
 * Maps and writes a single NIS entry to the LDAP DIT
 */
int
singleWriteToDIT(char *map, char *domain, datum *key, datum *value,
						bool_t replace) {
	__nis_table_mapping_t	*t;
	__nis_rule_value_t	*rv, *frv;
	__nis_ldap_search_t	*ls;
	int			statP = SUCCESS, flag;
	int			nv, nr, i, rc, collapse;
	char			*dn = 0, *skey, *svalue, *str;
	char			*myself = "singleWriteToDIT";

	if (!map || !domain || !key || !value) {
		return (MAP_PARAM_ERROR);
	}

	/* Return SUCCESS for empty or whitespace key */
	for (i = 0; i < key->dsize && (key->dptr[i] == 0 ||
		key->dptr[i] == ' ' || key->dptr[i] == '\t'); i++);
	if (i >= key->dsize)
		return (SUCCESS);

	/* Get the mapping information for the map */
	if ((t = mappingFromMap(map, domain, &statP)) == 0) {
		/*
		 * No problem. We don't handle this map and domain. Maybe it's
		 * handled by a service other than NIS.
		 */
		return (statP);
	}

	/* NULL-terminated version of key and value for logging */
	if ((skey = am(myself, key->dsize + 1)) == 0)
		return (MAP_NO_MEMORY);
	(void) memcpy(skey, key->dptr, key->dsize);

	if ((svalue = am(myself, value->dsize + 1)) == 0) {
		sfree(skey);
		return (MAP_NO_MEMORY);
	}
	(void) memcpy(svalue, value->dptr, value->dsize);

	if ((str = getFullMapName(map, domain)) == 0) {
		sfree(skey);
		sfree(svalue);
		return (MAP_NO_MEMORY);
	}

	/* For each alternate mapping */
	for (flag = 0; t != 0; t = t->next) {
		/* Verify objName */
		if (strcmp(str, t->objName) != 0) {
			continue;
		}

		/* Verify if key matches the index */
		if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 ||
			verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0)
			continue;

		/* Check the writespecs */
		if (t->objectDN->write.base == 0) {
			logmsg(MSG_NOTIMECHECK, LOG_INFO,
				"%s: No baseDN in writespec. Write disabled "
				"for %s (%s)", myself, t->dbId, map);
			continue;
		}

		/* Check if rulesToLDAP are provided */
		if (t->numRulesToLDAP == 0) {
			logmsg(MSG_NOTIMECHECK, LOG_INFO,
				"%s: No rulesToLDAP. Write disabled for "
				"%s (%s)", myself, t->dbId, map);
			continue;
		}

		/* Set flag to indicate write is enabled */
		flag = 1;

		/* Convert key  and value into an array of rule-values */
		if ((rv = datumToRuleValue(key, value, t, &nv, domain, FALSE,
								&statP)) == 0) {
			logmsg(MSG_NOTIMECHECK, LOG_ERR,
				"%s: Conversion error %d (NIS to name=value "
				"pairs) for NIS data (key=%s, value=%s) "
				"for %s (%s)",
				myself, statP, skey, svalue, t->dbId, map);
			sfree(skey);
			sfree(svalue);

			/* Free full map name */
			sfree(str);

			return (statP);
		}

		/* Convert NIS data to LDAP equivalents for each rule-value */
		for (i = 0; i < nv; i++) {
			/* Verify indexlist with name=value pairs */
			if (verifyIndexMatch(t, 0, &rv[i], 0, 0) == 0)
				break;

			/* Create LDAP request and LDAP name=value pairs */
			if ((ls = createLdapRequest(t, &rv[i],
			    0, 0, NULL, NULL)) == 0) {
				logmsg(MSG_NOTIMECHECK, LOG_ERR,
					"%s: Conversion error (name=value pairs"
					" to LDAP) for NIS data "
					"(key=%s, value=%s) for %s (%s)",
					myself, skey, svalue, t->dbId, map);
				freeRuleValue(rv, nv);
				sfree(skey);
				sfree(svalue);

				/* Free full map name */
				sfree(str);

				return (MAP_CREATE_LDAP_REQUEST_ERROR);
			}
			freeLdapSearch(ls);
			/* printRuleValue(&rv[i]); */
		}

		/* If i < nv then this alternate mapping isn't the one */
		if (i < nv)
			continue;

		/*
		 * Merge rule-values with the same DN so that we have
		 * one ldap write request for each DN
		 */
		nr = nv;
		frv = mergeRuleValueWithSameDN(rv, &nr);
		freeRuleValue(rv, nv);
		if (frv == 0) {
			if (nr == -1) {
				logmsg(MSG_NOTIMECHECK, LOG_ERR,
					"%s: Unable to merge LDAP write "
					"requests to same DN for NIS data "
					"(key=%s, value=%s) for %s (%s)",
					myself, skey, svalue, t->dbId, map);
				statP = MAP_INTERNAL_ERROR;
			} else if (nr == 0) {
				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
					"%s: Cannot generate write DN due to "
					"missing information for NIS data "
					"(key=%s, value=%s) for %s (%s)",
					myself, skey, svalue, t->dbId, map);
				statP = MAP_NO_DN;
			}
			sfree(skey);
			sfree(svalue);

			/* Free full map name */
			sfree(str);

			return (statP);
		}

		/* Write to the LDAP server */
		for (collapse = 0, i = 0; i < nr; i++) {
			if ((dn = findVal("dn", &frv[i], mit_ldap)) != 0) {
				if (replace == FALSE) {
					/* ldap add */
					rc = ldapAdd(dn, &frv[i],
						t->objectDN->write.attrs, 0);
				} else {
					/* ldap modify with addFirst set */
					rc = ldapModify(dn, &frv[i],
						t->objectDN->write.attrs, 1);
				}

				/* if we get err=20, collapse and try again */
				if (!collapse &&
					(rc == LDAP_TYPE_OR_VALUE_EXISTS) &&
					(collapseRuleValue(&frv[i]) == 1)) {
					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
						"%s: Ignoring values differing "
						"in case from NIS data (key=%s,"
						" value=%s) for (dn: %s) for "
						"%s (%s)", myself, skey,
						svalue, dn, t->dbId, map);
					collapse = 1;
					i--;
					continue;
				}

				collapse = 0;
				if (rc != LDAP_SUCCESS) {
					/* Log error */
					logmsg(MSG_NOTIMECHECK, LOG_ERR,
						"%s: %s error %d (%s) for "
						"(dn: %s) for NIS data "
						"(key=%s, value=%s) "
						"for %s (%s)",
						myself, (replace == TRUE) ?
						"ldapModify" : "ldapAdd", rc,
						ldap_err2string(rc), dn, skey,
						svalue, t->dbId, map);

					/* Dumping failed call may be useful */
					/* printRuleValue(&frv[i]); */

					/*
					 * Return the error code and let wrapper
					 * sort out if mapping should continue
					 * or abort.
					 */
					statP = rc;
					sfree(skey);
					sfree(svalue);
					freeRuleValue(frv, nr);

					/* Free full map name */
					sfree(str);

					return (statP);
				}
			}
		}

		freeRuleValue(frv, nr);
	}

	sfree(skey);
	sfree(svalue);

	/* Free full map name */
	sfree(str);

	return ((flag)?SUCCESS:MAP_WRITE_DISABLED);
}

suc_code
collapseRuleValue(__nis_rule_value_t *rv) {
	int		i, j, k, flag;

	/* Using 'val' to appease cstyle's 80 chars/line limit */
	__nis_value_t	*val;

	for (i = 0, flag = 0; i < rv->numAttrs; i++) {
		val = &rv->attrVal[i];
		for (j = 1; j < val->numVals; j++) {
			for (k = 0; k < j; k++) {
				if (val->val[j].length != val->val[k].length)
					continue;
				if (val->val[k].length == 0)
					continue;
				if (strncasecmp(val->val[j].value,
						val->val[k].value,
						val->val[j].length) != 0)
					continue;
				flag = 1;
				sfree(val->val[j].value);

#ifdef	ORDER_NOT_IMPORTANT
				val->val[j--] = val->val[--val->numVals];
#else
				/* Order needs to be maintained */
				for (k = j + 1; k < val->numVals; k++)
					val->val[k - 1] = val->val[k];
				j--;
				val->numVals--;
#endif
				break;
			}
		}
	}
	return (flag);
}

/* ObjectClass lookup table */
static struct {
	const char *attrType;
	const char *objectClass;
} oc_lookup[] = {
	{ "o",				"objectclass=organization"},
	{ "organizationname",		"objectclass=organization"},
	{ "2.5.4.10",			"objectclass=organization"},
	{ "ou",				"objectclass=organizationalunit"},
	{ "organizationalunitname",	"objectclass=organizationalunit"},
	{ "2.5.4.11",			"objectclass=organizationalunit"},
	{ "c",				"objectclass=country"},
	{ "countryname",		"objectclass=country"},
	{ "2.5.4.6",			"objectclass=country"},
	{ "dc",				"objectclass=domain"},
	{ "domaincomponent",		"objectclass=domain"},
	{ "0.9.2342.19200300.100.1.25",	"objectclass=domain"},
	{ "nismapname",			"objectclass=nismap"},
	{ "1.3.6.1.1.1.1.26",		"objectclass=nismap"},
	{ "automountmapname",		"objectclass=automountmap"},
	{ "1.3.6.1.1.1.1.31",		"objectclass=automountmap"},
	{ 0,				0}
};

/*
 * Returns the name of the objectclass to which the object
 * represented by the given 'rdn' will most likely belong to.
 * The return value is in static memory so it should not be
 * freed
 */
const char *
getObjectClass(char *rdn) {

	char *attrtype, *p;
	int len, i;

	/* Skip leading whitespaces */
	for (p = rdn; *p == ' ' || *p == '\t'; p++);
	if (*p == '\0')
		return (0);
	attrtype = p;

	/* Find '=' */
	if ((p = strchr(attrtype, '=')) == 0 || p == attrtype ||
						*(p - 1) == '\\')
		return (0);

	/*
	 * Skip trailing whitespaces in attrtype
	 * Don't worry, p won't decrease beyond attrtype
	 */
	for (--p; *p == ' ' || *p == '\t'; p--);
	len = p - attrtype + 1;

	for (i = 0; oc_lookup[i].attrType; i++)
		if (!strncasecmp(oc_lookup[i].attrType, attrtype, len))
			/* Check length is right */
			if (len == strlen(oc_lookup[i].attrType))
				return (oc_lookup[i].objectClass);

	return (0);
}

/*
 * Split 'dn' into rdn and parentdn based on the first
 * occurrence of unescaped 'comma' or 'semicolon'. rdn
 * lies on the LHS while parentdn lies on the RHS of the
 * split. If none found, then an empty string ("") is
 * assigned to parentdn
 */
int
splitDN(char *dn, char **rdn, char **parentdn) {
	char	*value, *name;
	char	*myself = "splitDN";

	if ((name = sdup(myself, T, dn)) == 0)
		return (-1);

	for (value = name; *value != '\0'; value++) {
		if (*value == ',' || *value == ';')
			if (value == name || *(value - 1) != '\\')
				break;
	}

	if (*value != '\0') {
		*value = '\0';
		value++;
	} else
		value = 0;

	if (parentdn) {
		if ((*parentdn = sdup(myself, T, value)) == 0) {
			sfree(name);
			return (-1);
		}
	}
	if (rdn)
		*rdn = name;
	else
		sfree(name);

	return (1);
}

/*
 * FUNCTION :	makeNISObject()
 *
 * DESCRIPTION: Sets up a nis Object in the DIT.
 *
 * GIVEN :
 *		Case 1: Both 'domain' and 'dn' are non-NULL
 *			Create nisDomainObject with the given information
 *		Case 2: Only 'domain' is  non-NULL
 *			Obtain the 'dn' from the nisLDAPdomainContext list
 *			Create nisDomainObject with the above information
 *		Case 3: Only 'dn' is  non-NULL
 *			Create an object with the 'dn'
 *			Here we guess the objectclass attribute, based on
 *			oc_lookup table
 *		Case 4: Both 'domain' and 'dn' are NULL
 *			Error
 *
 * RETURNS :	SUCCESS = It worked
 *		FAILURE = There was a problem.
 */
suc_code
makeNISObject(char *domain, char *dn) {
	__nis_rule_value_t	*rv;
	__nis_ldap_search_t	*ls;
	int			i, rc, nr, add_rc;
	char			*val;
	char			*myself = "makeNISObject";

	if (!dn && !domain)
		return (FAILURE);

	/*
	 * If only 'domain' name is provided, then
	 * try to find dn from the nisLDAPdomainContext
	 * list generated by the parser
	 */
	if (!dn) {
		for (i = 0; i < ypDomains.numDomains; i++) {
			if (ypDomains.domainLabels[i] == 0)
				continue;
			if (strcasecmp(domain, ypDomains.domainLabels[i])
								== 0) {
				dn = ypDomains.domains[i];
				break;
			}
		}
		if (!dn)
			return (FAILURE);
	}

	/*
	 * If only 'dn' is given, then it means that the
	 * caller simply wants to a create an entry for
	 * that 'dn'.
	 *
	 * If 'domain' is given, then check if the 'dn'
	 * has already been set up as a nis domain object.
	 * If not, see if we can make it become one.
	 */
	if (domain) {
		/*
		 * Check to see if the nis domain object has
		 * already been set up
		 */
		ls = buildLdapSearch(dn, LDAP_SCOPE_BASE, 0, 0,
			"objectclass=*", 0, 0, 0);
		if (ls == 0) {
			logmsg(MSG_NOTIMECHECK, LOG_ERR,
				"%s: Unable to create ldapSearch "
				"request for dn: %s", myself, dn);
			return (FAILURE);
		}
		nr = -1;
		rv = ldapSearch(ls, &nr, 0, &rc);
		freeLdapSearch(ls);
		if (rc == LDAP_SUCCESS) {
			val = findVal("nisDomain", rv, mit_ldap);
			if (val != NULL) {
				/*
				 * Yes, nis domain object found. Check
				 * to see if the domain names match.
				 * If so, we are done. If not, log
				 * a warning message, and return SUCCESS.
				 */
				if (strcasecmp(val, domain) == 0) {
					freeRuleValue(rv, nr);
					return (SUCCESS);
				} else {
					logmsg(MSG_NOTIMECHECK,
						LOG_WARNING,
						"%s: Entry (dn: %s) already "
						"contains a nis domain name "
						"(%s). The domain name (%s) "
						"is not added.",
						myself, dn, val, domain);
					freeRuleValue(rv, nr);
					return (SUCCESS);
				}
			} else {
				freeRuleValue(rv, nr);
				/*
				 * Entry for the 'dn' exists, but it
				 * is not a nis domain object yet.
				 * Add the nisDoamin attribute and
				 * the nisDomainObject objectclass to
				 * the entry.
				 */
				if ((rv = initRuleValue(1, 0)) == 0)
					return (FAILURE);

				if (addSAttr2RuleValue("nisDomain",
						domain, rv) == -1) {
					freeRuleValue(rv, 1);
					return (FAILURE);
				}
				rc = ldapModify(dn, rv,
					"objectclass=nisDomainObject",
					0);
				freeRuleValue(rv, 1);
				if (rc == LDAP_SUCCESS) {
					logmsg(MSG_NOTIMECHECK,
						LOG_INFO,
						"%s: entry (dn: %s) "
						"modified to be an "
						"nis domain object",
						myself, dn);
					return (SUCCESS);
				} else {
					logmsg(MSG_NOTIMECHECK,
						LOG_ERR,
						"%s: unable to modify "
						"entry (dn: %s) to be "
						"a nis domain object: "
						"ldapModify error %d (%s)",
						myself, dn, rc,
						ldap_err2string(rc));
					return (FAILURE);
				}
			}
		} else { /* search for 'dn' failed */
			freeRuleValue(rv, nr);

			/*
			 * It is OK if no such object, otherwise
			 * log an error.
			 */
			if (rc != LDAP_NO_SUCH_OBJECT) {
				logmsg(MSG_NOTIMECHECK, LOG_ERR,
					"%s: unable to retrieve "
					"entry (dn: %s): "
					"ldapSearch error %d (%s)",
					myself, dn, rc,
					ldap_err2string(rc));
				return (FAILURE);
			}
		}

		/*
		 * If the 'dn' is actually the naming context of
		 * the DIT, we should be able to make it a nis domain
		 * object without worrying about missing parent
		 * entries. If unable to add the entry for the 'dn'
		 * due to missing parent entries, fall through
		 * to create them and then add the nis domain object.
		 */
		if (addNISObject(domain, dn, &add_rc) == SUCCESS)
			return (SUCCESS);
		else if (add_rc != LDAP_NO_SUCH_OBJECT)
			return (FAILURE);
	}

	/* Create parent */
	if (addParent(dn, NULL) == FAILURE)
		return (FAILURE);

	if (addNISObject(domain, dn, NULL) == FAILURE)
		return (FAILURE);

	return (SUCCESS);
}

suc_code
addParent(char *dn, char **attr) {
	__nis_rule_value_t	*rv;
	__nis_ldap_search_t	*ls;
	int			rc, nr;
	char			*parentdn = 0, *rdn = 0;
	char			*myself = "addParent";

	/* Obtain parentdn */
	if (splitDN(dn, &rdn, &parentdn) == -1)
		return (FAILURE);
	if (!parentdn) {
		sfree(rdn);
		return (FAILURE);
	}

	/* Check if parentdn exists */
	ls = buildLdapSearch(parentdn, LDAP_SCOPE_BASE, 0, 0,
					"objectclass=*", 0, 0, 0);
	if (ls == 0) {
		logmsg(MSG_NOTIMECHECK, LOG_ERR,
			"%s: Unable to create ldapSearch request for "
			"parent (dn: %s) of (dn: %s)",
			myself, parentdn, dn);
		sfree(parentdn);
		sfree(rdn);
		return (FAILURE);
	}
	nr = -1;
	rv = ldapSearch(ls, &nr, 0, &rc);
	freeLdapSearch(ls);
	freeRuleValue(rv, nr);

	/* Create parent if it doesn't exists */
	if (rc == LDAP_NO_SUCH_OBJECT) {
		if (makeNISObject(0, parentdn) == FAILURE) {
			logmsg(MSG_NOTIMECHECK, LOG_ERR,
				"%s: Unable to create parent (dn: %s) of "
				"(dn: %s) in the DIT", myself, parentdn, dn);
			sfree(parentdn);
			sfree(rdn);
			return (FAILURE);
		}
	}
	sfree(parentdn);

	if (attr && rdn)
		*attr = (char *)getObjectClass(rdn);
	sfree(rdn);

	return (SUCCESS);
}



/*
 * FUNCTION :	is_fatal_error()
 *
 * DESCRIPTION:	Works out if a failed mapping operation should be retried.
 *
 * INPUTS :	Result code from operation
 *
 * OUTPUTS :	TRUE = Fatal error, don't retry.
 *		FALSE = Temporary error, retry.
 */
bool_t
is_fatal_error(int res)
{

	if (0 > res)
		/* An internal mapping error. Not going to go away. */
		return (TRUE);

	switch (res) {
		case (LDAP_PROTOCOL_ERROR):
		case (LDAP_TIMELIMIT_EXCEEDED):
		case (LDAP_PARTIAL_RESULTS):
		case (LDAP_BUSY):
		case (LDAP_UNAVAILABLE):
		case (LDAP_UNWILLING_TO_PERFORM):
		case (LDAP_OTHER):
		case (LDAP_SERVER_DOWN):
		case (LDAP_LOCAL_ERROR):
		case (LDAP_TIMEOUT):
		case (LDAP_NO_MEMORY):
			/* Probably worth a retry */
			return (FALSE);

		default:
			return (TRUE);
	}
}

/*
 * FUNCTION :	addNISObject()
 *
 * DESCRIPTION: Add a nis Object in the DIT.
 *
 * GIVEN :
 *		Case 1: 'dn' is NULL
 *			Error
 *		Case 2: 'domain' is non-NULL
 *			Create nisDomainObject with the given information
 *		Case 3: 'domain' is NULL
 *			Create an object with the 'dn'
 *			Here we guess the objectclass attribute, based on
 *			oc_lookup table
 *
 * RETURNS :	SUCCESS = It worked
 *		FAILURE = There was a problem. If the ldap add
 *                        operation failed, ldap_rc will be set
 *			  to the ldap error code.
 */
suc_code
addNISObject(char *domain, char *dn, int *ldap_rc) {
	__nis_rule_value_t	*rv;
	int			rc;
	char			*objClassAttrs = NULL, *attrs;
	char			*value, *svalue, *rdn = NULL;
	char			*myself = "addNISObject";

	if (!dn)
		return (FAILURE);

	if ((rv = initRuleValue(1, 0)) == 0)
		return (FAILURE);

	if (ldap_rc)
		*ldap_rc = -1;

	/*
	 * Add name=value pairs from RDN. Although this is not required
	 * for SunOne Directory Server, during openldap interoperabilty
	 * tests, it was found out that openldap server returned object
	 * class violation errors if MUST attributes were not specified
	 * explicitly.
	 */
	if (splitDN(dn, &rdn, 0) == -1)
		return (FAILURE);
	if (rdn != NULL) {
		objClassAttrs = (char *)getObjectClass(rdn);
		if (objClassAttrs == NULL) {
			sfree(rdn);
			return (FAILURE);
		}

		/*
		 * RDN can be composed of multiple name=value pairs
		 * concatenated by '+'. Hence, we need to determine each
		 * pair and add it to 'rv'
		 */
		for (value = rdn, svalue = NULL; *value != '\0'; value++) {
			if (*value == '+') {
				/* Make sure it's not escaped */
				if (value == rdn || *(value - 1) != '\\') {
					/*
					 * We are at the start of the new
					 * pair. 'svalue' now contains the
					 * value for the previous pair. Add
					 * the previous pair to 'rv'
					 */
					*value = '\0';
					if (svalue &&
					addSAttr2RuleValue(rdn, svalue, rv)
									== -1) {
						sfree(rdn);
						freeRuleValue(rv, 1);
						return (FAILURE);
					}
					svalue = NULL;
					rdn = value + 1;
					continue;
				}
			}

			if (*value == '=') {
				if (value == rdn || *(value - 1) != '\\') {
					/*
					 * 'rdn' now contains the name.
					 * Whatever follows till the next
					 * unescaped '+' or '\0' is the
					 * value for this pair.
					 */
					*value = '\0';
					svalue = value + 1;
					continue;
				}
			}
		}

		/*
		 * End of String. Add the previous name=value pair to 'rv'
		 */
		if (svalue && addSAttr2RuleValue(rdn, svalue, rv) == -1) {
			sfree(rdn);
			freeRuleValue(rv, 1);
			return (FAILURE);
		}
		sfree(rdn);
	} else /* rdn  == NULL */
		return (FAILURE);

	/* Create the entry */
	if (domain) {
		if (addSAttr2RuleValue("nisDomain", domain, rv) == -1) {
			freeRuleValue(rv, 1);
			return (FAILURE);
		}
		attrs = scat(myself, F, "objectclass=nisdomainobject,",
					objClassAttrs);
		if (!attrs) {
			freeRuleValue(rv, 1);
			return (FAILURE);
		}
		rc = ldapAdd(dn, rv, attrs, 0);
		sfree(attrs);
	} else {
		rc = ldapAdd(dn, rv, objClassAttrs, 0);
	}

	if (rc == LDAP_SUCCESS)
		logmsg(MSG_NOTIMECHECK, LOG_INFO,
				"%s: Entry (dn: %s) added to DIT",
				myself, dn);
	else if (rc == LDAP_ALREADY_EXISTS)
		/* Treat this as success */
		rc = LDAP_SUCCESS;
	else
		logmsg(MSG_NOTIMECHECK, LOG_ERR,
				"%s: ldapAdd error %d (%s) for (dn: %s)",
				myself, rc, ldap_err2string(rc), dn);

	freeRuleValue(rv, 1);
	if (ldap_rc)
		*ldap_rc = rc;
	return ((rc == LDAP_SUCCESS)?SUCCESS:FAILURE);
}