Home | History | Annotate | Download | only in gen
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 
     23 /*
     24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     25  * Use is subject to license terms.
     26  */
     27 
     28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     29 
     30 /*
     31  *	nislib.c
     32  *
     33  *	This module contains the user visible functions for lookup, and list,
     34  *	add name service calls add/remove/modify, all information base calls
     35  *	add_entry/remove_entry/modify_entry, and mkdir, rmdir, and checkpoint.
     36  * 	nis server. It should be broken up into at least three separate modules
     37  *
     38  */
     39 
     40 #include "mt.h"
     41 #include <syslog.h>
     42 #include <string.h>
     43 #include <stdlib.h>
     44 #include <ctype.h>
     45 #include <rpcsvc/nis.h>
     46 #include <errno.h>
     47 #include "nis_clnt.h"
     48 #include "nis_local.h"
     49 
     50 /*
     51  * This is *NOT* used in /usr/lib/libnsl.so.1, but has to be here so a program
     52  * like "sendmail" can access it BEFORE it makes ANY getXXbyYY calls.
     53  */
     54 
     55 mutex_t __nis_force_lookup_policy_lock = DEFAULTMUTEX;
     56 uint_t  __nis_force_hard_lookups = 0;
     57 
     58 extern const nis_result	__nomem_nis_result;
     59 
     60 /*
     61  * __nis_nss_lookup_policy: set's the __nis_force_hard_lookups flag
     62  * to HARD_LOOKUP or SOFT_LOOKUP depending upon the argument.  It will
     63  * be OR-ed into the nis_list() calls in the
     64  * NIS+ Name Service Switch backend /usr/lib/nss_nisplus.so.1
     65  */
     66 unsigned int
     67 __nis_nss_lookup_policy(unsigned int in)
     68 {
     69 	unsigned int old;
     70 
     71 	sig_mutex_lock(&__nis_force_lookup_policy_lock);
     72 	old = (__nis_force_hard_lookups & HARD_LOOKUP) ?
     73 						HARD_LOOKUP : SOFT_LOOKUP;
     74 	if (in == HARD_LOOKUP)
     75 		__nis_force_hard_lookups = HARD_LOOKUP;
     76 	else if (in == SOFT_LOOKUP)
     77 		__nis_force_hard_lookups = 0;
     78 	else {
     79 		old = 0;
     80 		errno = EINVAL;
     81 	}
     82 	sig_mutex_unlock(&__nis_force_lookup_policy_lock);
     83 	return (old);
     84 }
     85 
     86 /* maximum number of times to loop on NIS_NOTMASTER error */
     87 #define	MAX_NOTMASTER 5
     88 
     89 /*
     90  * Prototypes for static functions.
     91  */
     92 static nis_result *__nis_path_list(nis_object *, int, nis_result *,
     93 				ib_request *, uint_t,
     94 				int (*)(nis_name, nis_object *, void *),
     95 				void *);
     96 static nis_result * nis_ibops(ib_request *, rpcproc_t);
     97 static nis_result * nis_nameops(nis_name, nis_object *, rpcproc_t);
     98 static nis_result *nis_list_partial(nis_result *, ib_request *, uint_t,
     99 				int (*)(), void *);
    100 
    101 extern int __nis_debug_rpc;
    102 extern int __nis_debug_calls;
    103 extern FILE *__nis_debug_file;
    104 
    105 
    106 /*
    107  * nis_freeresult()
    108  *
    109  * This function calls the XDR free function for the user to free up the
    110  * memory associated with a result structure. NB: It isn't a macro because
    111  * it needs to be exposed to the client. Internally the xdr routine should
    112  * be used to save a procedure call.
    113  */
    114 void
    115 nis_freeresult(nis_result *res)
    116 {
    117 	if (res == &__nomem_nis_result)
    118 		return;
    119 	xdr_free(xdr_nis_result, (char *)res);
    120 	free(res);
    121 }
    122 
    123 void
    124 __nis_freelogresult(log_result *lres)
    125 {
    126 	if (lres) {
    127 		xdr_free((xdrproc_t)xdr_log_result, (char *)lres);
    128 		free(lres);
    129 	}
    130 }
    131 
    132 /*
    133  * nis_bind_dir()
    134  *
    135  * This function is used to return a binding to a candidate server.
    136  * It has two side effects :
    137  *	1) It keeps track of time in the cache code
    138  *	2) it optionally mallocs a NIS+ result structure.
    139  *
    140  * 'name' contains the name of the object that will be operated on.
    141  * If the operation is a lookup or a table search, then this code
    142  * returns a directory object that describes that objects directory
    143  * and thus a server that will have knowledge about the object.
    144  *
    145  * However, since the directory object, and the directory itself
    146  * can be on two different machines, when listing directories we
    147  * attempt to bind to the name passed. This only occurs when nis_list
    148  * is called and the search criteria is NULL (which is the only legal
    149  * search on a directory).
    150  *
    151  */
    152 nis_error
    153 nis_bind_dir(
    154 	char *dname,
    155 	int parent_first,
    156 	nis_bound_directory **binding,
    157 	uint_t flags)
    158 {
    159 	nis_error stat;
    160 
    161 	if (parent_first) {
    162 		stat = __nis_CacheBindDir(nis_domain_of(dname),
    163 					binding, flags);
    164 		if (stat != NIS_SUCCESS)
    165 			stat = __nis_CacheBindDir(dname, binding, flags);
    166 	} else {
    167 		stat = __nis_CacheBindDir(dname, binding, flags);
    168 		if (stat != NIS_SUCCESS)
    169 			stat = __nis_CacheBindDir(nis_domain_of(dname),
    170 					binding, flags);
    171 	}
    172 	return (stat);
    173 }
    174 
    175 nis_error
    176 nis_bind_server(nis_server *srv, int nsrv, nis_bound_directory **binding)
    177 {
    178 	nis_error stat;
    179 
    180 	stat = __nis_CacheBindServer(srv, nsrv, binding);
    181 	return (stat);
    182 }
    183 
    184 /*
    185  * __nis_path_list()
    186  *
    187  * This internal function will list all of the tables in a path.
    188  * if the ALL_RESULTS flag is set, it keeps on going, and going.
    189  * otherwise it returns on the first match.
    190  *
    191  * NB: The nis_list() function initializes the request's search
    192  * criteria, we just swap in table names.
    193  *
    194  * All possible returns from call to NIS_IBLIST (all NIS_xxx)
    195  *
    196  * Successful returns
    197  *
    198  * CBRESULTS, SUCCESS, S_SUCCESS  - found something
    199  * NOTFOUND, PARTIAL,  - looked but no data.
    200  * PERMISSION - can't read the table
    201  *
    202  * Errors that generate syslog warnings
    203  * NOTMASTER, NOT_ME, BADNAME, NOSUCHTABLE, NOSUCHNAME
    204  * BADATTRIBUTE, INVALIDOBJ
    205  *
    206  * Errors that fail silently but change final result to "soft"
    207  * NOMEMORY, TRYAGAIN, SYSTEMERROR, BADOBJECT
    208  *
    209  * Fatal errors :
    210  * NOCALLBACK
    211  */
    212 static nis_result *
    213 __nis_path_list(
    214 	nis_object	*tbl_obj,	/* Path of tables to search */
    215 	int		sf,		/* search first object */
    216 	nis_result	*res,		/* result structure to use */
    217 	ib_request	*req,		/* Request structure */
    218 	unsigned int	flags,		/* Flags to this function */
    219 	int		(*cback)(nis_name, nis_object *, void *),
    220 	void		*cbdata)
    221 {
    222 	nis_name	pathlist[NIS_MAXPATHDEPTH];	/* Parsed table path */
    223 	char		pathbuf[NIS_MAXPATHLEN];
    224 	char		firstpath[NIS_MAXNAMELEN];
    225 	nis_result	*local_res;		/* local result */
    226 	nis_name	table;			/* current table */
    227 	int		tnum, 			/* # of tables to search */
    228 			i, j, 			/* counters */
    229 			cur_obj,		/* obj, link counters */
    230 			soft_error = 0; 	/* error detected */
    231 	unsigned int	aticks = 0, 		/* profiling vars */
    232 			dticks = 0,
    233 			cticks = 0,
    234 			zticks = 0;
    235 	nis_object	*obj_list;		/* list returned from call */
    236 	int		num_objs,
    237 			total_objs;		/* # of objects returned */
    238 	struct obj_lists {			/* returned objects from each */
    239 		nis_object	*objs;
    240 		int		len;
    241 	} 		ret_objs[NIS_MAXPATHDEPTH];
    242 
    243 	/* construct a list of tables to search */
    244 	(void) strncpy(pathbuf, tbl_obj->TA_data.ta_path, NIS_MAXPATHLEN);
    245 	if (sf) {
    246 		(void) snprintf(firstpath, sizeof (firstpath),
    247 						"%s.%s", tbl_obj->zo_name,
    248 						tbl_obj->zo_domain);
    249 		pathlist[0] = firstpath;
    250 		tnum = __nis_parse_path(pathbuf, &pathlist[1],
    251 						NIS_MAXPATHDEPTH - 1);
    252 		tnum++;
    253 	} else
    254 		tnum = __nis_parse_path(pathbuf, pathlist, NIS_MAXPATHDEPTH);
    255 
    256 	/* Take any existing objects from the result passed in */
    257 	ret_objs[0].objs = res->objects.objects_val;
    258 	ret_objs[0].len = res->objects.objects_len;
    259 	total_objs = ret_objs[0].len;
    260 	res->objects.objects_val = NULL;
    261 	res->objects.objects_len = 0;
    262 
    263 	/*
    264 	 * Either search until a match is found, or if ALL_RESULTS is
    265 	 * set, search until path is exhausted.
    266 	 */
    267 	for (i = 0; i < tnum; i++) {
    268 		table = pathlist[i];
    269 		/* Ignore non-fully qualified names in path */
    270 		if (table[strlen(table) - 1] != '.') {
    271 			syslog(LOG_WARNING,
    272 	"nis_list: non fully qualified name in table path, %s, ignored.\n",
    273 									table);
    274 			continue;
    275 		}
    276 		/* swap in the table name from the path */
    277 		req->ibr_name = table;
    278 		/* prepare to receive the objects returned */
    279 		ret_objs[i+1].objs = NULL;
    280 		ret_objs[i+1].len = 0;
    281 		if (cback) {
    282 			(void) mutex_lock(&__nis_callback_lock);
    283 			local_res = __nis_core_lookup(req, flags, 1, cbdata,
    284 			    cback);
    285 			(void) mutex_unlock(&__nis_callback_lock);
    286 		} else
    287 			local_res = __nis_core_lookup(req, flags, 1, cbdata,
    288 			    cback);
    289 		aticks += local_res->aticks;
    290 		dticks += local_res->dticks;
    291 		cticks += local_res->cticks;
    292 		zticks += local_res->zticks;
    293 		obj_list = local_res->objects.objects_val;
    294 		num_objs = local_res->objects.objects_len;
    295 
    296 		switch (local_res->status) {
    297 		case NIS_SUCCESS :
    298 			/* put these into the array */
    299 			ret_objs[i+1].objs = obj_list;
    300 			ret_objs[i+1].len = num_objs;
    301 			total_objs += num_objs;
    302 			/* zero this so freeresult won't free them */
    303 			local_res->objects.objects_val = NULL;
    304 			local_res->objects.objects_len = 0;
    305 			/* fall through to the CBRESULTS code */
    306 			/*FALLTHROUGH*/
    307 		case NIS_CBRESULTS :
    308 		case NIS_CBERROR :
    309 			break;
    310 		case NIS_PARTIAL :
    311 		case NIS_PERMISSION :
    312 		case NIS_NOTMASTER :
    313 			/* these errors, just break */
    314 			break;
    315 		case NIS_LINKNAMEERROR : /* message generated above */
    316 			soft_error = TRUE;
    317 			break;
    318 		case NIS_NOT_ME :
    319 		case NIS_RPCERROR :
    320 		case NIS_NAMEUNREACHABLE :
    321 		/* generate message and set soft_error */
    322 			syslog(LOG_WARNING,
    323 "nis_list: NIS+ error %s encountered on name %s in table %s.%s's path.",
    324 				nis_sperrno(local_res->status),
    325 				table, tbl_obj->zo_name, tbl_obj->zo_domain);
    326 			soft_error = TRUE;
    327 			break;
    328 		case NIS_NOTFOUND :
    329 		case NIS_BADNAME :
    330 		case NIS_NOSUCHTABLE :
    331 		case NIS_NOSUCHNAME :
    332 		case NIS_BADATTRIBUTE :
    333 		case NIS_INVALIDOBJ :
    334 		/* generate message but don't set soft_error */
    335 			syslog(LOG_WARNING,
    336 "nis_list: NIS+ error %s encountered on name %s in table %s.%s's path.",
    337 				nis_sperrno(local_res->status),
    338 				table, tbl_obj->zo_name, tbl_obj->zo_domain);
    339 			break;
    340 		default :
    341 			soft_error = TRUE;
    342 			break;
    343 		}
    344 		/*
    345 		 * POLICY : When one table in a path is unreachable,
    346 		 * should we continue on or stop with an error?
    347 		 * ANSWER : Continue on. Loss of a portion of the namespace
    348 		 * should not cause disruptions in all of the namespace.
    349 		 * NB: This can have interesting side effects such that a
    350 		 * name may suddenly change "value" because it is being
    351 		 * resolved from a different place.
    352 		 *
    353 		 * If we're not returning all results and we've had a
    354 		 * successful call, we just return those results.
    355 		 */
    356 		if (((flags & ALL_RESULTS) == 0) &&
    357 		    ((local_res->status == NIS_SUCCESS) ||
    358 		    (local_res->status == NIS_CBRESULTS))) {
    359 			res->status = local_res->status;
    360 			res->aticks += aticks;
    361 			res->dticks += dticks;
    362 			res->zticks += zticks;
    363 			res->objects.objects_val = obj_list;
    364 			res->objects.objects_len = num_objs;
    365 			/* reset so that caller does not free it */
    366 			req->ibr_name = NULL;
    367 			nis_freeresult(local_res);
    368 			/* return same result structure back to them */
    369 			return (res);
    370 		}
    371 
    372 		/* otherwise just free local result (we've got the objs) */
    373 		nis_freeresult(local_res);
    374 	}
    375 
    376 	/* name is already freed so null this out */
    377 	req->ibr_name = NULL;
    378 
    379 	/*
    380 	 * At this point, we've either exhausted the list of tables
    381 	 * (total_objs == 0), or we've asked for all results so the
    382 	 * ret_objs[] array has some data in it (total_objs > 0)
    383 	 * if soft_error is set we will adjust our result status
    384 	 * appropriately.
    385 	 */
    386 	if (total_objs) {
    387 		/* now build a list of objects that should be returned */
    388 		obj_list = calloc(total_objs, sizeof (nis_object));
    389 		if (obj_list == NULL) {
    390 			res->status = NIS_NOMEMORY;
    391 			res->aticks += aticks;
    392 			res->dticks += dticks;
    393 			res->zticks += zticks;
    394 			for (i = 0; i < (tnum+1); i++) {
    395 				if (ret_objs[i].objs == NULL)
    396 					continue;
    397 				for (j = 0; j < ret_objs[i].len; j++)
    398 					xdr_free(xdr_nis_object,
    399 						(char *)&(ret_objs[i].objs[j]));
    400 				free(ret_objs[i].objs);
    401 			}
    402 			return (res);
    403 		}
    404 
    405 		/* copyout all objects into this new array */
    406 		cur_obj = 0;
    407 		for (i = 0; i < (tnum+1); i++) {
    408 			if (ret_objs[i].objs == NULL)
    409 				continue;
    410 			for (j = 0; j < ret_objs[i].len; j++)
    411 				obj_list[cur_obj++] = ret_objs[i].objs[j];
    412 			free(ret_objs[i].objs);
    413 		}
    414 		res->objects.objects_val = obj_list;
    415 		res->objects.objects_len = cur_obj;
    416 		if (cur_obj)
    417 			res->status = NIS_SUCCESS;
    418 		else
    419 			res->status = NIS_NOTFOUND;
    420 	} else {
    421 		if (cback)
    422 			res->status = NIS_CBRESULTS;
    423 		else
    424 			res->status = NIS_NOTFOUND;
    425 	}
    426 
    427 	if (soft_error && (res->status == NIS_SUCCESS))
    428 		res->status = NIS_S_SUCCESS;
    429 	else if (soft_error && (res->status == NIS_NOTFOUND))
    430 		res->status = NIS_S_NOTFOUND;
    431 	res->aticks += aticks;
    432 	res->dticks += dticks;
    433 	res->cticks += cticks;
    434 	res->zticks += zticks;
    435 	return (res);
    436 }
    437 
    438 /*
    439  * nis_lookup()
    440  *
    441  * This is the main lookup function of the name service. It will look
    442  * for the named object and return it. If the object was a link and
    443  * the flag FOLLOW_LINKS was set it will look up the item named by
    444  * the LINK, if that is an indexed name the lookup may return multiple
    445  * objects. If the name is not fully qualified and EXPAND_NAME is set
    446  * this function will expand the name into several candidate names.
    447  */
    448 nis_result *
    449 nis_lookup(nis_name name, uint_t flags)
    450 {
    451 	nis_error	nis_err = NIS_SUCCESS;
    452 	nis_name	*namelist;
    453 	nis_result	*res;
    454 	ib_request	req;
    455 	int		i;
    456 	unsigned int	aticks = 0,
    457 			cticks = 0,
    458 			dticks = 0,
    459 			zticks = 0;
    460 
    461 	(void) __start_clock(CLOCK_CLIENT);
    462 	__nis_CacheStart();
    463 	if (__nis_debug_calls) {
    464 		(void) fprintf(__nis_debug_file, "nis_lookup(%s, 0x%x)\n",
    465 			name, flags);
    466 	}
    467 	(void) memset((char *)&req, 0, sizeof (ib_request));
    468 	req.ibr_name = name;
    469 	i = (int)strlen(name);
    470 	if ((flags & EXPAND_NAME) == 0 || (i > 0 && name[i-1] == '.')) {
    471 		res = __nis_core_lookup(&req, flags, 0, NULL, NULL);
    472 		res->cticks = __stop_clock(CLOCK_CLIENT);
    473 		if (__nis_debug_calls)
    474 			__nis_print_result(res);
    475 		return (res);
    476 	}
    477 	namelist = __nis_getnames(name, &nis_err);
    478 	if (! namelist) {
    479 		res = nis_make_error(nis_err, 0, 0, 0, 0);
    480 		res->cticks = __stop_clock(CLOCK_CLIENT);
    481 		if (__nis_debug_calls)
    482 			__nis_print_result(res);
    483 		return (res);
    484 	}
    485 	for (i = 0; namelist[i]; i++) {
    486 		req.ibr_name = namelist[i];
    487 		res = __nis_core_lookup(&req, flags, 0, NULL, NULL);
    488 		switch (res->status) {
    489 			/*
    490 			 * All of the errors that indicate the name
    491 			 * is bound.
    492 			 * NB: We include the "nis_list" errors as well
    493 			 * as the core_lookup call could have followed
    494 			 * a link into a table operation.
    495 			 */
    496 			case NIS_SUCCESS :
    497 			case NIS_PARTIAL :
    498 			case NIS_CBRESULTS :
    499 			case NIS_CBERROR :
    500 			case NIS_CLNTAUTH :
    501 			case NIS_SRVAUTH :
    502 			case NIS_PERMISSION :
    503 			case NIS_LINKNAMEERROR:
    504 			case NIS_NOTMASTER :
    505 				res->aticks += aticks;
    506 				res->dticks += dticks;
    507 				res->zticks += zticks;
    508 				res->cticks += cticks;
    509 				res->cticks += __stop_clock(CLOCK_CLIENT);
    510 				nis_freenames(namelist);
    511 				if (__nis_debug_calls)
    512 					__nis_print_result(res);
    513 				return (res);
    514 			default :
    515 				aticks += res->aticks;
    516 				cticks += res->cticks;
    517 				dticks += res->dticks;
    518 				zticks += res->zticks;
    519 				if (nis_err == NIS_SUCCESS)
    520 					nis_err = res->status;
    521 				nis_freeresult(res);
    522 		}
    523 	}
    524 	nis_freenames(namelist);
    525 	cticks += __stop_clock(CLOCK_CLIENT);
    526 	if (nis_err == NIS_SUCCESS) {
    527 		syslog(LOG_WARNING, "nis_lookup: empty namelist");
    528 		nis_err = NIS_NOTFOUND;    /* fix up in case namelist empty */
    529 	}
    530 	res = nis_make_error(nis_err, aticks, cticks, dticks, zticks);
    531 	if (__nis_debug_calls)
    532 		__nis_print_result(res);
    533 	return (res);
    534 }
    535 
    536 /*
    537  * nis_list()
    538  *
    539  * This function takes a "standard" NIS name with embedded search criteria
    540  * and does a list on the object.
    541  */
    542 nis_result *
    543 nis_list(
    544 	nis_name	name,	/* list name like '[foo=bar].table.name' */
    545 	uint_t		flags,		/* Flags for the search */
    546 	int		(*cback)(),	/* Callback function. */
    547 	void		*cbdata)	/* Callback private data */
    548 {
    549 	nis_error	nis_err = NIS_SUCCESS;
    550 	nis_name	*namelist;
    551 	nis_object	*obj;
    552 	nis_result	*res;
    553 	ib_request	req;
    554 	nis_error	stat;
    555 	uint32_t	zticks = 0,
    556 			aticks = 0,
    557 			dticks = 0,
    558 			cticks = 0;
    559 	int		i, done;
    560 
    561 	/* start the client profiling clock */
    562 	(void) __start_clock(CLOCK_CLIENT);
    563 
    564 	__nis_CacheStart();
    565 	if (__nis_debug_calls) {
    566 		(void) fprintf(__nis_debug_file,
    567 		    "nis_list(%s, 0x%x, 0x%p, 0x%p)\n", name, flags,
    568 		    (void *)cback, (void *)cbdata);
    569 	}
    570 
    571 	/* Parse the request into a table name and attr/value pairs */
    572 	stat = nis_get_request(name, NULL, NULL, &req);
    573 	if (stat != NIS_SUCCESS) {
    574 		res = nis_make_error(stat, 0, 0, 0, 0);
    575 		res->cticks = __stop_clock(CLOCK_CLIENT);
    576 		if (__nis_debug_calls)
    577 			__nis_print_result(res);
    578 		return (res);
    579 	}
    580 
    581 	/*
    582 	 * process the ALL_RESULTS flag specially. First fetch
    583 	 * the table object to get the path, then call path list
    584 	 * to read all of the data. Note if we returned the object
    585 	 * on a list we could save an RPC here.
    586 	 */
    587 	if (flags & ALL_RESULTS) {
    588 		res = nis_lookup(req.ibr_name, flags);
    589 		if (res->status != NIS_SUCCESS) {
    590 			nis_free_request(&req);
    591 			if (__nis_debug_calls)
    592 				__nis_print_result(res);
    593 			return (res);
    594 		}
    595 		aticks = res->aticks;
    596 		cticks = res->cticks;
    597 		dticks = res->dticks;
    598 		zticks = res->zticks;
    599 		obj = res->objects.objects_val;
    600 		if ((res->objects.objects_len > 1) ||
    601 		    (__type_of(obj) != NIS_TABLE_OBJ)) {
    602 			/* Note : can't do all results on directory obj. */
    603 			xdr_free(xdr_nis_result, (char *)res);
    604 			nis_free_request(&req);
    605 			(void) memset((char *)res, 0, sizeof (nis_result));
    606 			res->status = NIS_BADOBJECT;
    607 			res->aticks = aticks;
    608 			res->dticks = dticks;
    609 			res->cticks = cticks;
    610 			res->zticks = zticks;
    611 			if (__nis_debug_calls)
    612 				__nis_print_result(res);
    613 			return (res);
    614 		}
    615 		res->objects.objects_val = NULL;
    616 		res->objects.objects_len = 0;
    617 		free(req.ibr_name); /* won't be needing this */
    618 		req.ibr_name = NULL;
    619 		res = __nis_path_list(obj, 1, res, &req, flags, cback, cbdata);
    620 		nis_free_request(&req);
    621 		xdr_free(xdr_nis_object, (char *)obj);
    622 		free(obj);
    623 		res->aticks += aticks;
    624 		res->dticks += dticks;
    625 		res->cticks += cticks;
    626 		res->zticks += zticks;
    627 		if (__nis_debug_calls)
    628 			__nis_print_result(res);
    629 		return (res);
    630 	}
    631 
    632 	/*
    633 	 * Normal requests.  The server will return NIS_PARTIAL
    634 	 * if we specify a search criteria and the table exists
    635 	 * but the entry does not exist within the table.  We
    636 	 * need to handle this return value by checking for a
    637 	 * table path in nis_list_partial().
    638 	 */
    639 	i = (int)strlen(name);
    640 	if ((flags & EXPAND_NAME) == 0 || (i > 0 && name[i-1] == '.')) {
    641 		if (cback) {
    642 			(void) mutex_lock(&__nis_callback_lock);
    643 			res = __nis_core_lookup(&req, flags, 1, cbdata, cback);
    644 			(void) mutex_unlock(&__nis_callback_lock);
    645 		} else
    646 			res = __nis_core_lookup(&req, flags, 1, cbdata, cback);
    647 		free(req.ibr_name);
    648 		if (res->status == NIS_PARTIAL)
    649 			res = nis_list_partial(res, &req, flags, cback, cbdata);
    650 	} else {
    651 		namelist = __nis_getnames(req.ibr_name, &stat);
    652 		if (! namelist) {
    653 			res = nis_make_error(stat, 0, 0, 0, 0);
    654 			nis_free_request(&req);
    655 			res->cticks = __stop_clock(CLOCK_CLIENT);
    656 			if (__nis_debug_calls)
    657 				__nis_print_result(res);
    658 			return (res);
    659 		}
    660 		free(req.ibr_name); /* non fully qualified name */
    661 
    662 		for (i = 0, done = 0; !done && namelist[i]; i++) {
    663 			/* replace with the candidate name */
    664 			req.ibr_name = namelist[i];
    665 			if (cback) {
    666 				(void) mutex_lock(&__nis_callback_lock);
    667 				res = __nis_core_lookup(&req, flags, 1, cbdata,
    668 				    cback);
    669 				(void) mutex_unlock(&__nis_callback_lock);
    670 			} else
    671 				res = __nis_core_lookup(&req, flags, 1, cbdata,
    672 				    cback);
    673 			if (res->status == NIS_PARTIAL)
    674 				res = nis_list_partial(res, &req,
    675 							flags, cback, cbdata);
    676 			switch (res->status) {
    677 				/*
    678 				 * All of the errors that indicate the name
    679 				 * is bound.
    680 				 */
    681 				case NIS_SUCCESS :
    682 				case NIS_CBRESULTS :
    683 				case NIS_CBERROR :
    684 				case NIS_CLNTAUTH :
    685 				case NIS_SRVAUTH :
    686 				case NIS_PERMISSION :
    687 				case NIS_NOTMASTER :
    688 					done = 1;
    689 					break;
    690 				default :
    691 					aticks += res->aticks;
    692 					cticks += res->cticks;
    693 					dticks += res->dticks;
    694 					zticks += res->zticks;
    695 					if (nis_err == NIS_SUCCESS)
    696 						nis_err = res->status;
    697 					nis_freeresult(res);
    698 					break;
    699 			}
    700 		}
    701 		if (! done) {
    702 			if (nis_err == NIS_SUCCESS) {
    703 				syslog(LOG_WARNING, "nis_list: empty namelist");
    704 				nis_err = NIS_NOTFOUND; /* if empty namelist */
    705 			}
    706 			res = nis_make_error(nis_err, aticks, cticks,
    707 							    dticks, zticks);
    708 		}
    709 		nis_freenames(namelist); /* not needed any longer */
    710 	}
    711 	req.ibr_name = NULL; /* already freed */
    712 
    713 	/*
    714 	 * Returns from __nis_core_lookup :
    715 	 *	NIS_SUCCESS,  	Table/Name found, search suceeded.
    716 	 *	NIS_CBRESULTS,	Table/Name found, search suceeded to callback
    717 	 *	NIS_PARTIAL,  	found the name but didn't match any entries.
    718 	 *	NIS_CLNTAUTH,	Found table, couldn't authenticate callback
    719 	 *	NIS_SRVAUTH	Found table, couldn't authenticate server
    720 	 *	NIS_PERMISSION	Found table, couldn't read it.
    721 	 *	NIS_NOTMASTER	Found table, wasn't master (master requested)
    722 	 *	NIS_RPCERROR	unable to communicate with service.
    723 	 *	NIS_XXX 	Error somewhere.
    724 	 *
    725 	 */
    726 	res->aticks += aticks;
    727 	res->cticks += cticks;
    728 	res->dticks += dticks;
    729 	res->zticks += zticks;
    730 	res->cticks += __stop_clock(CLOCK_CLIENT);
    731 	nis_free_request(&req);
    732 	if (__nis_debug_calls)
    733 		__nis_print_result(res);
    734 	return (res);
    735 }
    736 
    737 /*
    738  * Deal with a "PARTIAL" result. Given a NIS name of
    739  * [search-criteria].table-name, this occurs when the
    740  * server found an object whose name was 'table-name' but
    741  * either the object couldn't be searched because it was the
    742  * wrong type or the search resulted in no results.
    743  *
    744  * If the object that matched 'table-name' was a LINK object
    745  * core lookup will have followed it for us.
    746  *
    747  * We increment a local copy of the statistics and then reset
    748  * them in 'res' before returning.
    749  */
    750 static
    751 nis_result *
    752 nis_list_partial(
    753 	nis_result	*res,
    754 	ib_request	*req,
    755 	uint_t		flags,		/* Flags for the search */
    756 	int		(*cback)(),	/* Callback function. */
    757 	void		*cbdata)	/* Callback private data */
    758 {
    759 	nis_object	*obj;
    760 	table_obj	*tdata;
    761 	unsigned int aticks = res->aticks;
    762 	unsigned int cticks = res->cticks;
    763 	unsigned int dticks = res->dticks;
    764 	unsigned int zticks = res->zticks;
    765 
    766 	obj = res->objects.objects_val;
    767 	if (__type_of(obj) ==  NIS_DIRECTORY_OBJ) {
    768 		/*
    769 		 * POLICY : What is the error when you search a
    770 		 * a DIRECTORY and the results are no entries ?
    771 		 * ANSWER : A NOT FOUND error, assuming the server
    772 		 * did not return a "bad attribute" error, AND
    773 		 * we do _NOT_ return the directory object that the
    774 		 * server returned.
    775 		 */
    776 		xdr_free(xdr_nis_result, (char *)res);
    777 		(void) memset((char *)res, 0, sizeof (nis_result));
    778 		res->status = NIS_NOTFOUND;
    779 	} else if (__type_of(obj) == NIS_LINK_OBJ) {
    780 		/* If the object that matched 'table-name' was a LINK object */
    781 		/* core lookup will have followed it for us.  If it's not */
    782 		/* found there and we somehow came to nis_list_partial() */
    783 		/* we need to return NIS_NOTFOUND */
    784 
    785 		xdr_free(xdr_nis_result, (char *)res);
    786 		(void) memset((char *)res, 0, sizeof (nis_result));
    787 		res->status = NIS_NOTFOUND;
    788 	} else if (__type_of(obj) != NIS_TABLE_OBJ) {
    789 		/*
    790 		 * This shouldn't happen because the server should
    791 		 * catch it when it attempts the search.
    792 		 */
    793 		xdr_free(xdr_nis_result, (char *)res);
    794 		(void) memset((char *)res, 0, sizeof (nis_result));
    795 		res->status = NIS_NOTSEARCHABLE;
    796 	} else {
    797 		/*
    798 		 * Now we know its a table object and that our search failed.
    799 		 */
    800 		tdata = &(obj->TA_data);
    801 		if (((flags & FOLLOW_PATH) != 0) && (tdata->ta_path) &&
    802 		    (strlen(tdata->ta_path) > (size_t)0)) {
    803 			obj = res->objects.objects_val;
    804 			res->objects.objects_val = NULL;
    805 			res->objects.objects_len = 0;
    806 			res = __nis_path_list(obj, 0, res, req, flags,
    807 								cback, cbdata);
    808 			/* free up the table object */
    809 			xdr_free(xdr_nis_object, (char *)obj);
    810 			free(obj);
    811 			/* ticks are updated by __nis_path_list */
    812 			aticks = res->aticks;
    813 			cticks = res->cticks;
    814 			dticks = res->dticks;
    815 			zticks = res->zticks;
    816 		} else {
    817 			xdr_free(xdr_nis_result, (char *)res);
    818 			(void) memset((char *)res, 0, sizeof (nis_result));
    819 			/*
    820 			 *  If a search criteria was specified, indicate
    821 			 *  that we didn't find the entry.  If there was
    822 			 *  no search criteria, then we return success
    823 			 *  (i.e., table was listed successfully, but
    824 			 *  there were no entries in the table).
    825 			 */
    826 			if (req->ibr_srch.ibr_srch_len)
    827 				res->status = NIS_NOTFOUND;
    828 			else if (cback)
    829 				res->status = NIS_CBRESULTS;
    830 			else
    831 				res->status = NIS_SUCCESS;
    832 		}
    833 	}
    834 	res->aticks = aticks;
    835 	res->dticks = dticks;
    836 	res->zticks = zticks;
    837 	res->cticks = cticks;
    838 	return (res);
    839 }
    840 
    841 /*
    842  *  Make a call to a nis+ server based on the call state in 'state'.
    843  *  We loop until we either get a response or until we can no longer
    844  *  get a client handle to any server.
    845  */
    846 nis_error
    847 nis_call(nis_call_state *state, rpcproc_t func,
    848 	xdrproc_t req_proc, char *req, xdrproc_t res_proc, char *res)
    849 {
    850 	CLIENT *clnt;
    851 	nis_error err = NIS_SUCCESS;
    852 	enum clnt_stat status;
    853 
    854 	for (;;) {
    855 		clnt = __nis_get_server(state);
    856 		if (clnt == NULL) {
    857 			err = state->niserror;
    858 			break;
    859 		}
    860 
    861 		if (__nis_debug_rpc)
    862 			__nis_print_call(clnt, func);
    863 
    864 		status = clnt_call(clnt, func,
    865 				req_proc, req, res_proc, res,
    866 				state->timeout);
    867 
    868 		if (__nis_debug_rpc)
    869 			__nis_print_rpc_result(status);
    870 
    871 		__nis_release_server(state, clnt, status);
    872 
    873 		if (status == RPC_SUCCESS)
    874 			break;
    875 	}
    876 
    877 	return (err);
    878 }
    879 
    880 /*
    881  * nis_nameops()
    882  *
    883  * This generic function calls all of the name operations.
    884  */
    885 
    886 static nis_result *
    887 nis_nameops(nis_name name, nis_object *obj, rpcproc_t func)
    888 {
    889 	nis_call_state		state;
    890 	nis_result		*res;
    891 	nis_error		err;
    892 	ns_request		req;
    893 	nis_name		oname, odomain;
    894 	nis_name		oowner, ogroup;
    895 	char			nname[1024], ndomain[1024];
    896 	nis_name		tname;
    897 	int			times = 0;
    898 
    899 	if (name != 0 && strlen(name) >= NIS_MAXNAMELEN) {
    900 		return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
    901 	}
    902 
    903 	if (obj) {
    904 		/*
    905 		 * Enforce correct name policy on NIS+ objects stored
    906 		 * into the namespace. This code insures that zo_name
    907 		 * and zo_domain are correct.
    908 		 */
    909 		oname = obj->zo_name;
    910 		if ((tname = nis_leaf_of(name)) == NULL ||
    911 		    strlcpy(nname, tname, sizeof (nname)) >= sizeof (nname))
    912 			return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
    913 		obj->zo_name = nname;
    914 		odomain = obj->zo_domain;
    915 		if ((tname = nis_domain_of(name)) == NULL ||
    916 		    strlcpy(ndomain, tname, sizeof (ndomain))
    917 				>= sizeof (ndomain))
    918 			return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
    919 		obj->zo_domain = ndomain;
    920 		if (ndomain[strlen(ndomain)-1] != '.' &&
    921 				strlcat(ndomain, ".", sizeof (ndomain)) >=
    922 					sizeof (ndomain))
    923 			return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
    924 
    925 		oowner = obj->zo_owner;
    926 		if (obj->zo_owner == 0)
    927 			obj->zo_owner = nis_local_principal();
    928 
    929 		ogroup = obj->zo_group;
    930 		if (obj->zo_group == 0)
    931 			obj->zo_group = nis_local_group();
    932 	}
    933 
    934 	(void) memset((char *)&req, 0, sizeof (req));
    935 	req.ns_name = name;
    936 	if (obj) {
    937 		req.ns_object.ns_object_len = 1;
    938 		req.ns_object.ns_object_val = obj;
    939 	} else {
    940 		req.ns_object.ns_object_len = 0;
    941 		req.ns_object.ns_object_val = NULL;
    942 	}
    943 
    944 	__nis_init_call_state(&state);
    945 	state.name = name;
    946 	state.flags = MASTER_ONLY;
    947 	state.parent_first = 1;
    948 
    949 	res = calloc(1, sizeof (nis_result));
    950 	if (res == NULL)
    951 		return (nis_make_error(NIS_NOMEMORY, 0, 0, 0, 0));
    952 
    953 again:
    954 	err = nis_call(&state, func,
    955 			(xdrproc_t)xdr_ns_request, (char *)&req,
    956 			(xdrproc_t)xdr_nis_result, (char *)res);
    957 	if (err == NIS_SUCCESS && res->status == NIS_NOTMASTER &&
    958 	    times++ < MAX_NOTMASTER)
    959 		goto again;
    960 	res->aticks = state.aticks;
    961 	__nis_reset_call_state(&state);
    962 	if (err != NIS_SUCCESS)
    963 		res->status = err;
    964 
    965 	if (obj) {
    966 		obj->zo_name = oname;
    967 		obj->zo_domain = odomain;
    968 		obj->zo_owner = oowner;
    969 		obj->zo_group = ogroup;
    970 	}
    971 	return (res);
    972 }
    973 
    974 /*
    975  * nis_add()
    976  *
    977  * This function will add an object to the namespace. If it is a
    978  * table type object the server will create a table for it as well.
    979  */
    980 
    981 nis_result *
    982 nis_add(nis_name name, nis_object *obj)
    983 {
    984 	nis_result	*res;
    985 
    986 	(void) __start_clock(CLOCK_CLIENT); /* start the client clock */
    987 	__nis_CacheStart();
    988 	if (__nis_debug_calls) {
    989 		(void) fprintf(__nis_debug_file, "nis_add(%s, 0x%p\n",
    990 			name?name:"(nil)", (void *)obj);
    991 	}
    992 	res = nis_nameops(name, obj, NIS_ADD);
    993 	res->cticks = __stop_clock(CLOCK_CLIENT);
    994 	if (__nis_debug_calls)
    995 		__nis_print_result(res);
    996 	return (res);
    997 }
    998 
    999 static void
   1000 nis_flush_cache(nis_name name, nis_object *obj)
   1001 {
   1002 	if (obj == 0 || (obj && __type_of(obj) == NIS_DIRECTORY_OBJ)) {
   1003 		directory_obj dobj;
   1004 		if (__nis_CacheSearch(name, &dobj) == NIS_SUCCESS &&
   1005 		    nis_dir_cmp(name, dobj.do_name) == SAME_NAME) {
   1006 			__nis_CacheRemoveEntry(&dobj);
   1007 			xdr_free((xdrproc_t)xdr_directory_obj, (char *)&dobj);
   1008 		}
   1009 	}
   1010 }
   1011 
   1012 /*
   1013  * nis_remove()
   1014  *
   1015  * This function will remove an object from the namespace. If it is a
   1016  * table type object the server will destroy the table for it as well.
   1017  */
   1018 
   1019 nis_result *
   1020 nis_remove(nis_name name, nis_object *obj)
   1021 {
   1022 	nis_result	*res;
   1023 
   1024 	(void) __start_clock(CLOCK_CLIENT); /* start the client clock */
   1025 	__nis_CacheStart();
   1026 	if (__nis_debug_calls) {
   1027 		(void) fprintf(__nis_debug_file, "nis_remove(%s, 0x%p)\n",
   1028 			name?name:"(nil)", (void *)obj);
   1029 	}
   1030 	res =  nis_nameops(name, obj, NIS_REMOVE);
   1031 	if (res->status == NIS_SUCCESS)
   1032 		nis_flush_cache(name, obj);
   1033 	res->cticks = __stop_clock(CLOCK_CLIENT);
   1034 	if (__nis_debug_calls)
   1035 		__nis_print_result(res);
   1036 	return (res);
   1037 }
   1038 
   1039 /*
   1040  * nis_modify()
   1041  *
   1042  * This function will modify an object in the namespace.
   1043  */
   1044 
   1045 nis_result *
   1046 nis_modify(nis_name name, nis_object *obj)
   1047 {
   1048 	nis_result	*res;
   1049 
   1050 	(void) __start_clock(CLOCK_CLIENT); /* start the client clock */
   1051 	__nis_CacheStart();
   1052 	if (__nis_debug_calls) {
   1053 		(void) fprintf(__nis_debug_file, "nis_modify(%s, 0x%p)\n",
   1054 			name?name:"(nil)", (void *)obj);
   1055 	}
   1056 	res = nis_nameops(name, obj, NIS_MODIFY);
   1057 	if (res->status == NIS_SUCCESS)
   1058 		nis_flush_cache(name, obj);
   1059 	res->cticks = __stop_clock(CLOCK_CLIENT);
   1060 	if (__nis_debug_calls)
   1061 		__nis_print_result(res);
   1062 	return (res);
   1063 }
   1064 
   1065 
   1066 /*
   1067  * The cookie has the name of the server (null-terminated), followed
   1068  * by the "real" cookie from the NIS server.  We make a copy of the
   1069  * server name, copy the "real" cookie to the beginning of the buffer,
   1070  * and then adjust the cookie length.  A zero-length cookie means
   1071  * that the cookie is not valid and we return 0.
   1072  */
   1073 static
   1074 char *
   1075 cookie_to_name(netobj *cookie)
   1076 {
   1077 	size_t len;
   1078 	size_t offset;
   1079 	char *s;
   1080 
   1081 	if (cookie->n_bytes == 0)
   1082 		return (0);
   1083 	if ((s = strdup(cookie->n_bytes)) == 0) {
   1084 		syslog(LOG_ERR, "cookie_to_name: strdup failed");
   1085 		return (0);
   1086 	}
   1087 	offset = strlen(s) + 1;
   1088 	len = cookie->n_len - offset;
   1089 	(void) memmove(cookie->n_bytes, cookie->n_bytes + offset, len);
   1090 	cookie->n_len = (uint_t)len;
   1091 
   1092 	return (s);
   1093 }
   1094 
   1095 /*
   1096  * We store a server name in a cookie along with the "real"
   1097  * cookie from the NIS server.  The new cookie will include
   1098  * the server name (null-terminated) with the "real" cookie
   1099  * following it.  If we can't allocate memory, we set the
   1100  * cookie length to 0 to indicate that it is an invalid cookie.
   1101  */
   1102 static
   1103 void
   1104 name_to_cookie(char *name, nis_result *res)
   1105 {
   1106 	size_t len;
   1107 	size_t offset;
   1108 	netobj *cookie = &res->cookie;
   1109 	char *p;
   1110 
   1111 	offset = strlen(name) + 1;
   1112 	len = offset + res->cookie.n_len;
   1113 	p = malloc(len);
   1114 	if (p == 0) {
   1115 		cookie->n_len = 0;    /* indicates a bad cookie */
   1116 		syslog(LOG_ERR, "name_to_cookie: malloc failed");
   1117 		return;
   1118 	}
   1119 	(void) strcpy(p, name);
   1120 	(void) memmove(p+offset, res->cookie.n_bytes, res->cookie.n_len);
   1121 	free(res->cookie.n_bytes);
   1122 	res->cookie.n_bytes = p;
   1123 	res->cookie.n_len = (uint_t)len;
   1124 }
   1125 
   1126 /*
   1127  * nis_ibops()
   1128  *
   1129  * This generic function calls all of the table operations.
   1130  *
   1131  * Note that although we use a virtual circuit, there are no keepalives.
   1132  * Because of this, the length of the timeout is vital, and we attempt
   1133  * to tune it here for the various operations.
   1134  */
   1135 
   1136 /* a single modify has been seen to take as long as 180 sec. */
   1137 #define	NIS_MODIFY_TIMEOUT	300 /* 5 minutes */
   1138 
   1139 /* netgroup.org_dir.ssi has 48K entries, and will take almost this long */
   1140 #define	NIS_REMOVE_MULT_TIMEOUT	(2*60*60) /* 2 hours */
   1141 
   1142 static nis_result *
   1143 nis_ibops(ib_request *req, rpcproc_t func)
   1144 {
   1145 	nis_result		*res;
   1146 	nis_object		*obj = NULL;
   1147 	nis_name		oname, odomain;
   1148 	nis_name		oowner, ogroup;
   1149 	nis_name		tname;
   1150 	char			nname[NIS_MAXNAMELEN], ndomain[NIS_MAXNAMELEN];
   1151 	int			timeout = NIS_GEN_TIMEOUT;	/* in seconds */
   1152 	nis_error		call_err;
   1153 	nis_call_state		state;
   1154 	int			make_cookie = FALSE;
   1155 	char			*server_name = NULL;
   1156 	uint_t			flags = 0;
   1157 	int			times = 0;
   1158 	extern char *__nis_server_name(nis_call_state *);
   1159 
   1160 	if (req->ibr_obj.ibr_obj_len) {
   1161 		/*
   1162 		 * Enforce correct name policy on objects stored into
   1163 		 * tables. This code insures that zo_name and zo_domain
   1164 		 * are correct.
   1165 		 */
   1166 		obj = req->ibr_obj.ibr_obj_val;
   1167 		oname = obj->zo_name;
   1168 		tname = nis_leaf_of(req->ibr_name);
   1169 		if (tname == NULL || strlcpy(nname, tname, sizeof (nname)) >=
   1170 				sizeof (nname))
   1171 			return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
   1172 		obj->zo_name = nname;
   1173 		odomain = obj->zo_domain;
   1174 		tname = nis_domain_of(req->ibr_name);
   1175 		if (tname == NULL || strlcpy(ndomain, tname,
   1176 				sizeof (ndomain)) >= sizeof (ndomain))
   1177 			return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
   1178 		obj->zo_domain = ndomain;
   1179 		if (ndomain[strlen(ndomain)-1] != '.' &&
   1180 				strlcat(ndomain, ".", sizeof (ndomain)) >=
   1181 					sizeof (ndomain))
   1182 			return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
   1183 
   1184 		oowner = obj->zo_owner;
   1185 		if (obj->zo_owner == 0)
   1186 			obj->zo_owner = nis_local_principal();
   1187 
   1188 		ogroup = obj->zo_group;
   1189 		if (obj->zo_group == 0)
   1190 			obj->zo_owner = nis_local_group();
   1191 	}
   1192 
   1193 	res = calloc(1, sizeof (nis_result));
   1194 	if (res == NULL)
   1195 		return (nis_make_error(NIS_NOMEMORY, 0, 0, 0, 0));
   1196 
   1197 	/* determine the timeout (heuristic) */
   1198 	switch (func) {
   1199 	    case NIS_IBMODIFY:
   1200 		flags = MASTER_ONLY;
   1201 		timeout = NIS_MODIFY_TIMEOUT;
   1202 		break;
   1203 	    case NIS_IBREMOVE:
   1204 		flags = MASTER_ONLY;
   1205 		if (req->ibr_flags & REM_MULTIPLE)
   1206 			timeout = NIS_REMOVE_MULT_TIMEOUT;
   1207 		else
   1208 			timeout = NIS_MODIFY_TIMEOUT;
   1209 		break;
   1210 	    case NIS_IBFIRST:
   1211 		make_cookie = TRUE;
   1212 		break;
   1213 	    case NIS_IBNEXT:
   1214 		make_cookie = TRUE;
   1215 		server_name = cookie_to_name(&req->ibr_cookie);
   1216 		if (server_name == 0)
   1217 			return (nis_make_error(NIS_NOMEMORY, 0,
   1218 				__stop_clock(CLOCK_CLIENT), 0, 0));
   1219 		break;
   1220 	    default:
   1221 		flags = MASTER_ONLY;
   1222 		timeout = NIS_GEN_TIMEOUT;
   1223 		break;
   1224 	}
   1225 
   1226 	__nis_init_call_state(&state);
   1227 	state.name = req->ibr_name;
   1228 	state.flags = flags;
   1229 	state.timeout.tv_sec = timeout;
   1230 	state.timeout.tv_usec = 0;
   1231 	state.parent_first = 1;
   1232 	state.server_name = server_name;
   1233 
   1234 again:
   1235 	call_err = nis_call(&state, func,
   1236 			(xdrproc_t)xdr_ib_request, (char *)req,
   1237 			(xdrproc_t)xdr_nis_result, (char *)res);
   1238 	if (call_err == NIS_SUCCESS && res->status == NIS_NOTMASTER &&
   1239 	    times++ < MAX_NOTMASTER)
   1240 		goto again;
   1241 	res->aticks = state.aticks;
   1242 	if (make_cookie) {
   1243 		if (server_name == NULL)
   1244 			server_name = __nis_server_name(&state);
   1245 		if (server_name != NULL)
   1246 			name_to_cookie(server_name, res);
   1247 	}
   1248 	if (server_name != NULL)
   1249 		free(server_name);
   1250 	__nis_reset_call_state(&state);
   1251 
   1252 	if (obj) {
   1253 		obj->zo_name = oname;
   1254 		obj->zo_domain = odomain;
   1255 		obj->zo_owner = oowner;
   1256 		obj->zo_group = ogroup;
   1257 	}
   1258 	if (call_err)
   1259 		res->status = call_err;
   1260 	return (res);
   1261 }
   1262 
   1263 
   1264 /*
   1265  * nis_add_entry()
   1266  *
   1267  * This function will add an entry to the named NIS table.
   1268  */
   1269 nis_result *
   1270 nis_add_entry(
   1271 	nis_name	name,		/* Table to use 		*/
   1272 	nis_object	*obj,		/* Entry object to add. 	*/
   1273 	uint_t		flags)		/* Semantic modification flags	*/
   1274 {
   1275 	nis_result	*res;
   1276 	ib_request	req;
   1277 	nis_error	stat;
   1278 
   1279 	(void) __start_clock(CLOCK_CLIENT);
   1280 	__nis_CacheStart();
   1281 	if (__nis_debug_calls) {
   1282 		(void) fprintf(__nis_debug_file,
   1283 		    "nis_add_entry(%s, 0x%p, 0x%x\n", name?name:"(nil)",
   1284 		    (void *)obj, flags);
   1285 	}
   1286 	stat = nis_get_request(name, obj, NULL, &req);
   1287 	if (stat != NIS_SUCCESS) {
   1288 		res = nis_make_error(stat, 0, __stop_clock(CLOCK_CLIENT), 0, 0);
   1289 		if (__nis_debug_calls)
   1290 			__nis_print_result(res);
   1291 		return (res);
   1292 	}
   1293 	req.ibr_flags = flags;
   1294 	res = nis_ibops(&req, NIS_IBADD);
   1295 	nis_free_request(&req); /* free up memory associated with request */
   1296 	res->cticks += __stop_clock(CLOCK_CLIENT);
   1297 	if (__nis_debug_calls)
   1298 		__nis_print_result(res);
   1299 	return (res);
   1300 }
   1301 
   1302 /*
   1303  * nis_remove_entry()
   1304  *
   1305  * This function will remove an entry to the named NIS table.
   1306  */
   1307 nis_result *
   1308 nis_remove_entry(
   1309 	nis_name	name,		/* Table to use 		*/
   1310 	nis_object	*obj,		/* Entry object to remove. 	*/
   1311 	uint_t		flags)		/* semantic modification flags	*/
   1312 {
   1313 	nis_result	*res;
   1314 	ib_request	req;
   1315 	nis_error	stat;
   1316 
   1317 	(void) __start_clock(CLOCK_CLIENT);
   1318 	__nis_CacheStart();
   1319 	if (__nis_debug_calls) {
   1320 		(void) fprintf(__nis_debug_file,
   1321 		    "nis_remove_entry(%s, 0x%p, 0x%x)\n", name?name:"(nil)",
   1322 		    (void *)obj, flags);
   1323 	}
   1324 	stat = nis_get_request(name, obj, NULL, &req);
   1325 	if (stat != NIS_SUCCESS) {
   1326 		res = nis_make_error(stat, 0, __stop_clock(CLOCK_CLIENT), 0, 0);
   1327 		if (__nis_debug_calls)
   1328 			__nis_print_result(res);
   1329 		return (res);
   1330 	}
   1331 
   1332 	req.ibr_flags = flags;
   1333 	res = nis_ibops(&req, NIS_IBREMOVE);
   1334 	nis_free_request(&req); /* free up memory associated with request */
   1335 	res->cticks += __stop_clock(CLOCK_CLIENT);
   1336 	if (__nis_debug_calls)
   1337 		__nis_print_result(res);
   1338 	return (res);
   1339 }
   1340 
   1341 /*
   1342  * nis_modify_entry()
   1343  *
   1344  * This function will modify an entry to the named NIS table.
   1345  */
   1346 nis_result *
   1347 nis_modify_entry(
   1348 	nis_name	name,		/* Table to use 		*/
   1349 	nis_object	*obj,		/* Entry object to modify. 	*/
   1350 	uint_t		flags)		/* Semantic modification flags	*/
   1351 {
   1352 	nis_result	*res;
   1353 	ib_request	req;
   1354 	nis_error	stat;
   1355 
   1356 	(void) __start_clock(CLOCK_CLIENT);
   1357 	__nis_CacheStart();
   1358 	if (__nis_debug_calls) {
   1359 		(void) fprintf(__nis_debug_file,
   1360 		    "nis_modify_entry(%s, 0x%p, 0x%x)\n", name?name:"(nil)",
   1361 		    (void *)obj, flags);
   1362 	}
   1363 	stat = nis_get_request(name, obj, NULL, &req);
   1364 	if (stat != NIS_SUCCESS) {
   1365 		res = nis_make_error(stat, 0, __stop_clock(CLOCK_CLIENT), 0, 0);
   1366 		if (__nis_debug_calls)
   1367 			__nis_print_result(res);
   1368 		return (res);
   1369 	}
   1370 
   1371 	req.ibr_flags = flags;
   1372 	res = nis_ibops(&req, NIS_IBMODIFY);
   1373 	nis_free_request(&req); /* free up memory associated with request */
   1374 	res->cticks += __stop_clock(CLOCK_CLIENT);
   1375 	if (__nis_debug_calls)
   1376 		__nis_print_result(res);
   1377 	return (res);
   1378 }
   1379 
   1380 /*
   1381  * nis_first_entry()
   1382  *
   1383  * This function will fetch the "first" entry in a table.
   1384  */
   1385 nis_result *
   1386 nis_first_entry(nis_name table)		/* Table to read 	*/
   1387 {
   1388 	nis_result	*res;
   1389 	ib_request	req;
   1390 	nis_error	stat;
   1391 
   1392 	(void) __start_clock(CLOCK_CLIENT);
   1393 	__nis_CacheStart();
   1394 	if (__nis_debug_calls) {
   1395 		(void) fprintf(__nis_debug_file, "nis_first_entry(%s)\n",
   1396 		    table);
   1397 	}
   1398 	stat = nis_get_request(table, NULL, NULL, &req);
   1399 	if (stat != NIS_SUCCESS) {
   1400 		res = nis_make_error(stat, 0, __stop_clock(CLOCK_CLIENT), 0, 0);
   1401 		if (__nis_debug_calls)
   1402 			__nis_print_result(res);
   1403 		return (res);
   1404 	}
   1405 
   1406 	if (req.ibr_srch.ibr_srch_len) {
   1407 		res = nis_make_error(NIS_TOOMANYATTRS, 0,
   1408 				__stop_clock(CLOCK_CLIENT), 0, 0);
   1409 		if (__nis_debug_calls)
   1410 			__nis_print_result(res);
   1411 		return (res);
   1412 	}
   1413 
   1414 	res = nis_ibops(&req, NIS_IBFIRST);
   1415 	nis_free_request(&req);
   1416 	/* XXX at this point we should put the server in the cookie. */
   1417 	/* free up memory associated with request */
   1418 	res->cticks += __stop_clock(CLOCK_CLIENT);
   1419 	if (__nis_debug_calls)
   1420 		__nis_print_result(res);
   1421 	return (res);
   1422 }
   1423 
   1424 /*
   1425  * nis_next_entry()
   1426  *
   1427  * This function will fetch the "first" entry in a table.
   1428  */
   1429 nis_result *
   1430 nis_next_entry(
   1431 	nis_name	table,		/* Table to read 	*/
   1432 	netobj		*cookie)	/* First/Next Cookie	*/
   1433 {
   1434 	nis_result	*res;
   1435 	ib_request	req;
   1436 	nis_error	stat;
   1437 
   1438 	(void) __start_clock(CLOCK_CLIENT);
   1439 	__nis_CacheStart();
   1440 	if (__nis_debug_calls) {
   1441 		(void) fprintf(__nis_debug_file, "nis_next_entry(%s, 0x%p)\n",
   1442 			table, (void *)cookie);
   1443 	}
   1444 	stat = nis_get_request(table, NULL, cookie, &req);
   1445 	if (stat != NIS_SUCCESS) {
   1446 		res = nis_make_error(stat, 0, __stop_clock(CLOCK_CLIENT), 0, 0);
   1447 		if (__nis_debug_calls)
   1448 			__nis_print_result(res);
   1449 		return (res);
   1450 	}
   1451 
   1452 	res = nis_ibops(&req, NIS_IBNEXT);
   1453 	/* free up memory associated with request */
   1454 	nis_free_request(&req);
   1455 	res->cticks += __stop_clock(CLOCK_CLIENT);
   1456 	if (__nis_debug_calls)
   1457 		__nis_print_result(res);
   1458 	return (res);
   1459 }
   1460 
   1461 nis_result *
   1462 nis_checkpoint(nis_name name)
   1463 {
   1464 	nis_result *res;
   1465 	cp_result cpr;
   1466 	nis_error call_err;
   1467 	nis_call_state state;
   1468 	int times = 0;
   1469 
   1470 	(void) __start_clock(CLOCK_CLIENT);
   1471 
   1472 	res = calloc(1, sizeof (nis_result));
   1473 	if (res == NULL) {
   1474 		(void) __stop_clock(CLOCK_CLIENT);
   1475 		return (NULL);
   1476 	}
   1477 
   1478 	__nis_init_call_state(&state);
   1479 	state.name = name;
   1480 	state.flags = MASTER_ONLY;
   1481 again:
   1482 	call_err = nis_call(&state, NIS_CHECKPOINT,
   1483 			(xdrproc_t)xdr_nis_name, (char *)&name,
   1484 			(xdrproc_t)xdr_cp_result, (char *)&cpr);
   1485 	if (call_err == NIS_SUCCESS && cpr.cp_status == NIS_NOTMASTER &&
   1486 	    times++ < MAX_NOTMASTER)
   1487 		goto again;
   1488 	res->zticks = cpr.cp_zticks;
   1489 	res->dticks = cpr.cp_dticks;
   1490 	res->cticks = __stop_clock(CLOCK_CLIENT);
   1491 	res->aticks = state.aticks;
   1492 	__nis_reset_call_state(&state);
   1493 	if (call_err != NIS_SUCCESS)
   1494 		res->status = call_err;
   1495 	return (res);
   1496 }
   1497 
   1498 /*
   1499  * nis_mkdir()
   1500  *
   1501  * This function is designed to allow a client to remotely create
   1502  * a directory on a NIS server. When the server is contacted, it
   1503  * will look up the directory object and determine if it should
   1504  * really execute this command and if it should then everythings
   1505  * cool. It returns an error if it can't create the directory.
   1506  */
   1507 
   1508 nis_error
   1509 nis_mkdir(nis_name name, nis_server *srv)
   1510 {
   1511 	nis_error err;
   1512 	nis_error call_err;
   1513 	nis_call_state state;
   1514 
   1515 	__nis_CacheStart();
   1516 	if (__nis_debug_calls) {
   1517 		(void) fprintf(__nis_debug_file, "nis_mkdir(%s, %s)\n",
   1518 			name, srv->name);
   1519 	}
   1520 	__nis_init_call_state(&state);
   1521 	state.srv = srv;
   1522 	state.nsrv = 1;
   1523 	call_err = nis_call(&state, NIS_MKDIR,
   1524 			(xdrproc_t)xdr_nis_name, (char *)&name,
   1525 			(xdrproc_t)xdr_nis_error, (char *)&err);
   1526 	__nis_reset_call_state(&state);
   1527 	if (call_err != NIS_SUCCESS)
   1528 		err = call_err;
   1529 	if (__nis_debug_calls) {
   1530 		(void) fprintf(__nis_debug_file, "status=%s\n",
   1531 		    nis_sperrno(err));
   1532 	}
   1533 	return (err);
   1534 }
   1535 
   1536 /*
   1537  * nis_rmdir()
   1538  *
   1539  * This function is designed to allow a client to remotely remove
   1540  * a directory on a NIS server. When the server is contacted, it
   1541  * will look up the directory object and determine if it should
   1542  * really execute this command and if it should then everythings
   1543  * cool. It returns an error if it can't remove the directory.
   1544  */
   1545 
   1546 nis_error
   1547 nis_rmdir(nis_name name, nis_server *srv)
   1548 {
   1549 	nis_error err;
   1550 	nis_error call_err;
   1551 	nis_call_state state;
   1552 
   1553 	__nis_CacheStart();
   1554 	if (__nis_debug_calls) {
   1555 		(void) fprintf(__nis_debug_file, "nis_rmdir(%s, %s)\n",
   1556 			name, srv->name);
   1557 	}
   1558 	__nis_init_call_state(&state);
   1559 	state.srv = srv;
   1560 	state.nsrv = 1;
   1561 	call_err = nis_call(&state, NIS_RMDIR,
   1562 		    (xdrproc_t)xdr_nis_name, (char *)&name,
   1563 		    (xdrproc_t)xdr_nis_error, (char *)&err);
   1564 	__nis_reset_call_state(&state);
   1565 	if (call_err != NIS_SUCCESS)
   1566 		err = call_err;
   1567 	if (__nis_debug_calls) {
   1568 		(void) fprintf(__nis_debug_file, "status=%s\n",
   1569 		    nis_sperrno(err));
   1570 	}
   1571 	return (err);
   1572 }
   1573 
   1574 nis_error
   1575 __nis_send_msg(nis_server *srv, int proc, xdrproc_t out, char *msg)
   1576 {
   1577 	nis_error err;
   1578 	nis_error call_err;
   1579 	nis_call_state state;
   1580 
   1581 	__nis_CacheStart();
   1582 	if (__nis_debug_calls) {
   1583 		(void) fprintf(__nis_debug_file, "nis_send_msg(%s, %d)\n",
   1584 			srv->name, proc);
   1585 	}
   1586 	__nis_init_call_state(&state);
   1587 	state.srv = srv;
   1588 	state.nsrv = 1;
   1589 	state.timeout.tv_sec = 0;
   1590 	state.timeout.tv_usec = 0;
   1591 	call_err = nis_call(&state, proc,
   1592 		    out, msg,
   1593 		    (xdrproc_t)0, (char *)0);
   1594 	__nis_reset_call_state(&state);
   1595 	if (call_err != NIS_SUCCESS)
   1596 		err = call_err;
   1597 	if (__nis_debug_calls) {
   1598 		(void) fprintf(__nis_debug_file, "status=%s\n",
   1599 		    nis_sperrno(err));
   1600 	}
   1601 	return (err);
   1602 }
   1603 
   1604 /*
   1605  * A version of nis_list that takes a callback function, but doesn't do
   1606  * callbacks over the wire (it gets the objects in the reply and then
   1607  * feeds them to the callback function itself).
   1608  */
   1609 
   1610 nis_result *
   1611 __nis_list_localcb(
   1612 	nis_name	name,	/* list name like '[foo=bar].table.name' */
   1613 	uint_t		flags,		/* Flags for the search */
   1614 	int		(*cback)(),	/* Callback function. */
   1615 	void		*cbdata)	/* Callback private data */
   1616 {
   1617 	nis_result *res;
   1618 	int no;
   1619 	nis_object *o;
   1620 	char *tab;
   1621 	int i;
   1622 
   1623 	/*
   1624 	 * Do list without callbacks
   1625 	 */
   1626 	if ((res = nis_list(name, flags, 0, 0)) == 0)
   1627 		return (0);
   1628 
   1629 	/*
   1630 	 * Run callback locally
   1631 	 */
   1632 	if (cback)
   1633 		switch (res->status) {
   1634 		case NIS_SUCCESS:
   1635 		case NIS_S_SUCCESS:
   1636 			/*
   1637 			 * Always at least one object on success
   1638 			 */
   1639 			no = res->objects.objects_len;
   1640 			o = res->objects.objects_val;
   1641 			/*
   1642 			 * Figure out the table name
   1643 			 */
   1644 			if (tab = strchr(name, ']')) {
   1645 				tab++;
   1646 				while (isspace(*tab) || (*tab == ','))
   1647 					tab++;
   1648 			} else
   1649 				tab = name;
   1650 			/*
   1651 			 * Run callback
   1652 			 */
   1653 			for (i = 0; i < no; i++) {
   1654 				if ((*cback)(tab, &(o[i]), cbdata))
   1655 					break;
   1656 			}
   1657 			/*
   1658 			 * Free objects
   1659 			 */
   1660 			for (i = 0; i < no; i++)
   1661 				xdr_free(xdr_nis_object, (char *)&(o[i]));
   1662 			free(res->objects.objects_val);
   1663 			/*
   1664 			 * Fixup result
   1665 			 */
   1666 			res->objects.objects_len = 0;
   1667 			res->objects.objects_val = 0;
   1668 			res->status = NIS_CBRESULTS;
   1669 			break;
   1670 		};
   1671 
   1672 	return (res);
   1673 }
   1674