xref: /titanic_50/usr/src/lib/libnsl/nis/gen/nis_misc.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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  * nis_misc.c
24  */
25 
26 /*
27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 /*
34  * nis_misc.c
35  *
36  * This module contains miscellaneous library functions.
37  */
38 
39 #include "mt.h"
40 #include <string.h>
41 #include <syslog.h>
42 #include <malloc.h>
43 #include <rpc/rpc.h>
44 #include <rpcsvc/nis.h>
45 #include <tiuser.h>
46 #include <netdir.h>
47 #include <netinet/in.h>
48 #include <strings.h>
49 #include "nis_clnt.h"
50 #include "nis_local.h"
51 
52 static void nis_sort_server_endpoints_inet(nis_server *);
53 extern char *handle_to_server_name(CLIENT *);
54 extern void *__inet_get_local_interfaces();
55 extern FILE *__nis_debug_file;
56 
57 
58 /* ARGSUSED */
59 int
60 __clear_directory_local(nis_name n)
61 {
62 	return (1);
63 }
64 
65 int (*__clear_directory_ptr)(nis_name) = __clear_directory_local;
66 
67 /*
68  * __nis_pingproc()
69  *
70  * This function will send a  ping "message" to a remote server.
71  * It doesn't bother to see if there are any results since the ping
72  * is defined to be unreliable.
73  */
74 void
75 __nis_pingproc(
76 	nis_server	*srv,	/* Server to talk to 		*/
77 	nis_name	name,	/* Directory that changed 	*/
78 	uint32_t	mtime)	/* When it changed		*/
79 {
80 	CLIENT		*clnt;
81 	ping_args	args;
82 	struct timeval	tv;
83 
84 	clnt = nis_make_rpchandle(srv, 0, NIS_PROG, NIS_VERSION,
85 				ZMH_DG|ZMH_AUTH, 0, 0);
86 	if (! clnt)
87 		return;
88 
89 	tv.tv_sec = 0;
90 	tv.tv_usec = 0;
91 	args.dir = name;
92 	args.stamp = mtime;
93 	(void) clnt_call(clnt, NIS_PING, xdr_ping_args, (char *)&args,
94 			xdr_void, 0, tv);
95 	clnt_destroy(clnt);
96 }
97 
98 /*
99  * nis_ping()
100  *
101  * This function is used to ping all of the servers that serve a given
102  * directory. The point of the ping is to inform them that something
103  * has changed in the directory and they should go off and find what it
104  * is. Note that we avoid pinging ourselves for optimisation. During a
105  * replica/master switch the location of the master object in the array
106  * will briefly change from server[0] to the offset corresponding to
107  * the new master (the old replica). When everything has settled down
108  * after the switch, the master will once again be in server[0] of the
109  * directory object. The object parameter is optional for clients
110  * (REQUIRED FOR SERVERS) this is the object describing the directory.
111  */
112 
113 void
114 nis_ping(nis_name name, uint32_t mtime, nis_object *obj)
115 {
116 	nis_server	**srvs;
117 	nis_server	*s, *list;
118 	int		i, ns;
119 	nis_name	thishost = nis_local_host();
120 
121 	if (obj) {
122 		if (name == 0)
123 			name = obj->DI_data.do_name;
124 		list = obj->DI_data.do_servers.do_servers_val;
125 		ns = obj->DI_data.do_servers.do_servers_len;
126 
127 		for (i = 0, s = &(list[0]); i < ns; i++, s = &(list[i])) {
128 
129 			if (nis_dir_cmp(s->name, thishost) == SAME_NAME) {
130 				continue;
131 			}
132 
133 			__nis_pingproc(s, name, mtime);
134 		}
135 	} else {
136 		srvs = nis_getservlist(name);
137 		if (! srvs)
138 			return;
139 
140 		for (i = 0, s = srvs[0]; s; i++, s = srvs[i]) {
141 
142 			if (nis_dir_cmp(s->name, thishost) == SAME_NAME) {
143 				continue;
144 			}
145 			__nis_pingproc(s, name, mtime);
146 
147 		}
148 		nis_freeservlist(srvs);
149 	}
150 }
151 
152 
153 /*
154  * nis_dumplog(host, name, time)
155  *
156  * This function will dump log entries from the indicated host to the
157  * caller. It is used by the replica servers to get the updates that have
158  * occurred on a directory since the indicated time.
159  */
160 
161 log_result *
162 nis_dumplog(
163 	nis_server	*host,	/* Server to talk to		*/
164 	nis_name	name,	/* Directory name to dump.	*/
165 	uint32_t	dtime)	/* Last _valid_ timestamp.	*/
166 {
167 	CLIENT			*clnt;
168 	dump_args		da;
169 	struct timeval		tv;
170 	enum clnt_stat		stat;
171 	log_result	*result_ptr;
172 
173 	result_ptr = (log_result *)calloc(1, sizeof (log_result));
174 	if (result_ptr == NULL) {
175 		syslog(LOG_ERR, "nis_dumplog: Client out of memory.");
176 		return (NULL);
177 	}
178 
179 	clnt = nis_make_rpchandle(host, 0, NIS_PROG, NIS_VERSION,
180 				ZMH_VC+ZMH_AUTH, 0, 0);
181 	if (! clnt) {
182 		result_ptr->lr_status = NIS_NAMEUNREACHABLE;
183 		return (result_ptr);
184 	}
185 	(void) memset((char *)&da, 0, sizeof (da));
186 	da.da_dir = name;
187 	da.da_time = dtime;
188 	tv.tv_sec = NIS_DUMP_TIMEOUT;
189 	tv.tv_usec = 0;
190 	stat = clnt_call(clnt, NIS_DUMPLOG,
191 			xdr_dump_args, (char *)&da,
192 			xdr_log_result, (char *)result_ptr, tv);
193 	auth_destroy(clnt->cl_auth);
194 	clnt_destroy(clnt);
195 
196 	/*
197 	 * Now see if the RPC succeeded. Note that we have
198 	 * to check for local vs. remote errors in order to
199 	 * know whether the contents of the log_result record
200 	 * (result_ptr) are meaningful.
201 	 */
202 	switch (stat) {
203 	case RPC_CANTENCODEARGS:
204 	case RPC_CANTDECODERES:
205 	case RPC_CANTSEND:
206 	case RPC_CANTRECV:
207 	case RPC_TIMEDOUT:
208 	case RPC_INTR:
209 		syslog(LOG_WARNING, "nis_dumplog: RPC error %d", stat);
210 		/*
211 		 * This is a local error, so just return a
212 		 * generic RPC error.
213 		 */
214 		result_ptr->lr_status = NIS_RPCERROR;
215 		break;
216 
217 	default:
218 		/*
219 		 * All other return values mean that result_ptr
220 		 * already has a valid status code.
221 		 */
222 		break;
223 	}
224 
225 	return (result_ptr);
226 }
227 
228 /*
229  * nis_dump(host, name, cback)
230  *
231  * This function will dump an entire directory from the indicated host.
232  * It uses a callback function to minimize the memory requirements on
233  * the client and server.
234  */
235 
236 log_result *
237 nis_dump(
238 	nis_server	*host,	/* Server to talk to		*/
239 	nis_name	name,	/* Directory name to dump.	*/
240 	int		(*cback)()) /* Callback function	*/
241 {
242 	CLIENT			*clnt;
243 	dump_args		da;
244 	struct timeval		tv;
245 	enum clnt_stat		stat;
246 	int			err;
247 	log_result		*result_ptr;
248 
249 	result_ptr = (log_result *)calloc(1, sizeof (log_result));
250 	if (result_ptr == NULL) {
251 		syslog(LOG_ERR, "nis_dump: Client out of memory.");
252 		return (NULL);
253 	}
254 
255 	clnt = nis_make_rpchandle(host, 0, NIS_PROG, NIS_VERSION,
256 				ZMH_VC+ZMH_AUTH, 0, 0);
257 	if (!clnt) {
258 		result_ptr->lr_status = NIS_NAMEUNREACHABLE;
259 		return (result_ptr);
260 	}
261 	(void) mutex_lock(&__nis_callback_lock);
262 	(void) memset((char *)&da, 0, sizeof (da));
263 	da.da_dir = name;
264 	da.da_time = 0;
265 	da.da_cbhost.da_cbhost_len = 1;
266 	da.da_cbhost.da_cbhost_val = __nis_init_dump_callback(clnt, cback,
267 								NULL);
268 	if (! da.da_cbhost.da_cbhost_val) {
269 		(void) mutex_unlock(&__nis_callback_lock);
270 		result_ptr->lr_status = NIS_CBERROR;
271 		auth_destroy(clnt->cl_auth);
272 		clnt_destroy(clnt);
273 		return (result_ptr);
274 	}
275 
276 	/*
277 	 * The value of the NIS_DUMP_TIMEOUT is applicable only for the
278 	 * dump to get initiated.
279 	 */
280 	tv.tv_sec = NIS_DUMP_TIMEOUT;
281 	tv.tv_usec = 0;
282 	stat = clnt_call(clnt, NIS_DUMP, xdr_dump_args, (char *)&da,
283 			xdr_log_result, (char *)result_ptr, tv);
284 	if (stat != RPC_SUCCESS) {
285 		result_ptr->lr_status = NIS_RPCERROR;
286 	} else if (result_ptr->lr_status == NIS_CBRESULTS) {
287 		(*__clear_directory_ptr)(name);
288 		err = __nis_run_dump_callback(&(result_ptr->lr_cookie),
289 					NIS_CALLBACK, 0, clnt);
290 		if (err < 0)
291 			result_ptr->lr_status = NIS_CBERROR;
292 	}
293 	(void) mutex_unlock(&__nis_callback_lock);
294 	auth_destroy(clnt->cl_auth);
295 	clnt_destroy(clnt);
296 	return (result_ptr);
297 }
298 
299 /*
300  *  Sort server endpoints so that local addresses appear
301  *  before remote addresses.
302  */
303 void
304 nis_sort_directory_servers(directory_obj *slist)
305 {
306 	int i;
307 
308 	int nsvrs = slist->do_servers.do_servers_len;
309 	nis_server *svrs = slist->do_servers.do_servers_val;
310 
311 	for (i = 0; i < nsvrs; i++) {
312 		nis_sort_server_endpoints_inet(&svrs[i]);
313 	}
314 }
315 
316 static
317 int
318 is_local(void *local_interfaces, struct netconfig *ncp, char *uaddr)
319 {
320 	return (__inet_uaddr_is_local(local_interfaces, ncp, uaddr));
321 }
322 
323 static
324 int
325 is_remote(void *local_interfaces, struct netconfig *ncp, char *uaddr)
326 {
327 	return (!is_local(local_interfaces, ncp, uaddr));
328 }
329 
330 void
331 __nis_swap_endpoints(endpoint *e1, endpoint *e2)
332 {
333 	char *t;
334 
335 	t = e1->uaddr;
336 	e1->uaddr = e2->uaddr;
337 	e2->uaddr = t;
338 
339 	t = e1->family;
340 	e1->family = e2->family;
341 	e2->family = t;
342 
343 	t = e1->proto;
344 	e1->proto = e2->proto;
345 	e2->proto = t;
346 }
347 
348 /*
349  *  Sort a list of server endpoints so that address for local interfaces
350  *  occur before remote interfaces.  If an error occurs (e.g., no memory),
351  *  we just clean up and return; we end up not sorting the endpoints, but
352  *  this is just for optimization anyway.
353  *
354  *  There is a lot of work in this routine, so it should not be called
355  *  frequently.
356  */
357 static
358 void
359 nis_sort_server_endpoints_inet(nis_server *svr)
360 {
361 	int i;
362 	int j;
363 	int neps = svr->ep.ep_len;
364 	endpoint *eps = svr->ep.ep_val;
365 	struct netconfig *ncp, *ncp_inet = 0, *ncp_inet6 = 0;
366 	void *local_interfaces;
367 	void *nch;
368 
369 	nch = setnetconfig();
370 	if (nch == 0)
371 		return;
372 
373 	/* find any inet entry so we can do uaddr2taddr */
374 	while ((ncp = getnetconfig(nch)) != 0 &&
375 		ncp_inet == 0 && ncp_inet6 == 0) {
376 		if (strcmp(ncp->nc_protofmly, NC_INET) == 0)
377 			ncp_inet = ncp;
378 		else if (strcmp(ncp->nc_protofmly, NC_INET6))
379 			ncp_inet6 = ncp;
380 	}
381 	if (ncp_inet == 0 && ncp_inet6 == 0) {
382 		endnetconfig(nch);
383 		return;
384 	}
385 
386 	local_interfaces = __inet_get_local_interfaces();
387 	if (local_interfaces == 0) {
388 		endnetconfig(nch);
389 		return;
390 	}
391 
392 	/*
393 	 *  Sort endpoints so local inet addresses are first.  The
394 	 *  variable 'i' points to the beginning of the array,
395 	 *  and 'j' points to the end.  We advance 'i' as long
396 	 *  as it indexes a non-inet endpoint or a local endpoint.
397 	 *  We retract 'j' as long as it indexes a non-inet endpoint
398 	 *  or a remote endpoint.  If either of these cases fail,
399 	 *  then 'i' is pointing at a remote endpoint and 'j' is
400 	 *  pointing at a local endpoint.  We swap them, adjust
401 	 *  the indexes, and continue.  When the indexes cross
402 	 *  we are done.
403 	 */
404 	i = 0;
405 	j = neps - 1;
406 	while (i < j) {
407 		if ((strcmp(eps[i].family, NC_INET) != 0 &&
408 			strcmp(eps[i].family, NC_INET6) != 0) ||
409 		    is_local(local_interfaces, ncp, eps[i].uaddr)) {
410 			i++;
411 			continue;
412 		}
413 
414 		if ((strcmp(eps[j].family, NC_INET) != 0 &&
415 			strcmp(eps[j].family, NC_INET6) != 0) ||
416 		    is_remote(local_interfaces, ncp, eps[j].uaddr)) {
417 			--j;
418 			continue;
419 		}
420 
421 		__nis_swap_endpoints(&eps[i], &eps[j]);
422 		i++;
423 		--j;
424 	}
425 
426 	/* clean up */
427 	__inet_free_local_interfaces(local_interfaces);
428 	endnetconfig(nch);
429 }
430 
431 /*
432  * In the pre-IPv6 code, secure RPC has a bug such that it doesn't look
433  * at the endpoint 'family' field when selecting an endpoint to use for
434  * time synchronization. In order to protect that broken code from itself,
435  * we set the endpoint 'proto' to 'nc_netid' (i.e., "udp6" or "tcp6")
436  * rather than 'nc_proto' ("udp"/"tcp") if 'nc_family' is "inet6".
437  *
438  * The __nis_netconfig2ep() and __nis_netconfig_matches_ep() service
439  * functions below simplify endpoint manipulation by implementing the
440  * rules above.
441  */
442 
443 void
444 __nis_netconfig2ep(struct netconfig *nc, endpoint *ep) {
445 
446 	if (nc == 0 || ep == 0)
447 		return;
448 
449 	ep->family = strdup(nc->nc_protofmly);
450 
451 	if (strcmp(ep->family, "inet6") == 0) {
452 		ep->proto = strdup(nc->nc_netid);
453 	} else {
454 		ep->proto = strdup(nc->nc_proto);
455 	}
456 }
457 
458 bool_t
459 __nis_netconfig_matches_ep(struct netconfig *nc, endpoint *ep) {
460 
461 	if (nc == 0 || ep == 0)
462 		return (FALSE);
463 
464 	if (strcmp(nc->nc_protofmly, ep->family) != 0)
465 		return (FALSE);
466 
467 	if (strcmp(ep->family, "inet6") == 0)
468 		return (strcmp(nc->nc_netid, ep->proto) == 0 ||
469 			strcmp(nc->nc_proto, ep->proto) == 0);
470 	else
471 		return (strcmp(nc->nc_proto, ep->proto) == 0);
472 
473 }
474 
475 struct netconfig_list {
476 	struct netconfig *nc;
477 	struct netconfig_list *next;
478 };
479 
480 static struct netconfig_list *ncl;
481 
482 struct netconfig *
483 __nis_get_netconfig(endpoint *ep)
484 {
485 	void *nch;
486 	struct netconfig *nc;
487 	struct netconfig_list *p;
488 
489 	for (p = ncl; p; p = p->next) {
490 		if (__nis_netconfig_matches_ep(p->nc, ep)) {
491 			return (p->nc);
492 		}
493 	}
494 
495 	nch = setnetconfig();
496 	if (nch == 0)
497 		return (0);
498 
499 	while ((nc = getnetconfig(nch)) != 0) {
500 		if (__nis_netconfig_matches_ep(nc, ep))
501 			break;
502 	}
503 	/*
504 	 *  We call getnetconfigent to allocate a copy of the
505 	 *  netconfig entry.
506 	 */
507 	if (nc) {
508 		p = (struct netconfig_list *)malloc(sizeof (*p));
509 		if (p == 0)
510 			return (0);
511 		p->nc = getnetconfigent(nc->nc_netid);
512 		p->next = ncl;
513 		ncl = p;
514 	}
515 	endnetconfig(nch);
516 
517 	return (nc);
518 }
519 
520 void
521 nis_free_binding(nis_bound_directory *binding)
522 {
523 	xdr_free((xdrproc_t)xdr_nis_bound_directory, (char *)binding);
524 	free(binding);
525 }
526 
527 void
528 __free_fdresult(fd_result *res)
529 {
530 	xdr_free((xdrproc_t)xdr_fd_result, (char *)res);
531 	free(res);
532 }
533 
534 endpoint *
535 __endpoint_dup(endpoint *src, endpoint *dst)
536 {
537 	if (dst == NULL) {
538 		dst = (endpoint *)malloc(sizeof (endpoint));
539 		if (dst == NULL)
540 			return (NULL);
541 	}
542 
543 	dst->family = src->family?strdup(src->family):0;
544 	dst->proto = src->proto?strdup(src->proto):0;
545 	dst->uaddr = src->uaddr?strdup(src->uaddr):0;
546 
547 	return (dst);
548 }
549 
550 void
551 __endpoint_free(endpoint *ep)
552 {
553 	if (ep) {
554 		free(ep->family);
555 		free(ep->proto);
556 		free(ep->uaddr);
557 		free(ep);
558 	}
559 }
560 
561 endpoint *
562 __get_bound_endpoint(nis_bound_directory *binding, int n)
563 {
564 	endpoint *ep;
565 	nis_server *srv;
566 	nis_bound_endpoint *bep;
567 
568 	bep = &binding->bep_val[n];
569 	srv = binding->dobj.do_servers.do_servers_val;
570 	ep = &srv[bep->hostnum].ep.ep_val[bep->epnum];
571 	return (ep);
572 }
573 
574 nis_server *
575 __nis_server_dup(nis_server *src, nis_server *dst)
576 {
577 	if (dst == NULL) {
578 		dst = (nis_server *)malloc(sizeof (nis_server));
579 		if (dst == NULL)
580 			return (NULL);
581 	}
582 	(void) memset((char *)dst, 0, sizeof (nis_server));
583 	return ((nis_server *)
584 		__nis_xdr_dup(xdr_nis_server, (char *)src, (char *)dst));
585 }
586 
587 
588 char *
589 __nis_xdr_dup(xdrproc_t proc, char *src, char *dst)
590 {
591 	uint_t size;
592 	char *data;
593 	XDR xdrs;
594 
595 	size = xdr_sizeof(proc, src);
596 	data = (char *)malloc(size);
597 	if (data == NULL)
598 		return (NULL);
599 
600 	xdrmem_create(&xdrs, data, size, XDR_ENCODE);
601 	if (!proc(&xdrs, src)) {
602 		free(data);
603 		return (NULL);
604 	}
605 
606 	xdrmem_create(&xdrs, data, size, XDR_DECODE);
607 	if (!proc(&xdrs, dst)) {
608 		free(data);
609 		return (NULL);
610 	}
611 
612 	free(data);
613 	return (dst);
614 }
615 
616 void
617 __nis_path_free(char **names, int len)
618 {
619 	int i;
620 
621 	for (i = 0; i < len; i++)
622 		free((void *)names[i]);
623 	free((void *)names);
624 }
625 
626 /*
627  * __nis_path
628  *
629  * Given two path strings, *from and *to, work out the list of names
630  * between them. The length of that path and the pointer to the list
631  * of pointers to the name strings are returned to the caller using
632  * path_length and namesp. As the names list is a pointer to a list of
633  * pointers, the caller must pass the address of the pointer to the
634  * pointer to the list of pointers, hence the unusual "char ***"
635  * type. It is the callers responsibility to free **namesp.
636  */
637 nis_error
638 __nis_path(char *from, char *to, int *path_length, char ***namesp)
639 {
640 	int i;
641 	int n;
642 	int start;
643 	int end;
644 	int st, dircmp, lastdircmp;
645 	char *tfrom = from;
646 	char *tto = to;
647 	char **names;
648 
649 	dircmp = st = nis_dir_cmp(from, to);
650 	if (st == BAD_NAME)
651 		return (NIS_BADNAME);
652 
653 	/* figure out how long path is */
654 	n = 0;
655 	if (st == HIGHER_NAME) {
656 		while ((dircmp = nis_dir_cmp(from, to)) == HIGHER_NAME) {
657 			n++;
658 			to = nis_domain_of(to);
659 		}
660 		if (dircmp != SAME_NAME) {
661 			/* Unrecoverable error */
662 			dircmp = BAD_NAME;
663 		}
664 	} else if (st == LOWER_NAME) {
665 		from = nis_domain_of(from);
666 		while ((dircmp = nis_dir_cmp(from, to)) == LOWER_NAME) {
667 			n++;
668 			from = nis_domain_of(from);
669 		}
670 		if (dircmp != SAME_NAME) {
671 			/* Unrecoverable error */
672 			dircmp = BAD_NAME;
673 		}
674 		n++;	/* include actual target */
675 	} else if (st == NOT_SEQUENTIAL) {
676 		/* names are not sequential */
677 		from = nis_domain_of(from);
678 		while ((dircmp = nis_dir_cmp(from, to)) == NOT_SEQUENTIAL) {
679 			n++;
680 			from = nis_domain_of(from);
681 		}
682 		n++;	/* include common parent */
683 		lastdircmp = dircmp; /* Handle HIGHER or LOWER */
684 		while ((dircmp = nis_dir_cmp(from, to)) == lastdircmp) {
685 			n++;
686 			lastdircmp = dircmp;
687 			to = nis_domain_of(to);
688 		}
689 		if (dircmp != SAME_NAME) {
690 			/* Unrecoverable error */
691 			dircmp = BAD_NAME;
692 		}
693 	}
694 
695 	if (dircmp == BAD_NAME) {
696 		syslog(LOG_WARNING, "__nis_path: Unable to walk "
697 		    "from %s to %s", tfrom, tto);
698 		return (NIS_BADNAME);
699 	}
700 
701 	names = malloc(n * sizeof (char *));
702 	if (names == NULL)
703 		return (NIS_NOMEMORY);
704 
705 	start = 0;
706 	end = n;
707 	from = tfrom;
708 	to = tto;
709 
710 	/*
711 	 * Go through again, this time storing names.
712 	 * We shouldn't need to check the loops will terminate
713 	 * on the SAME_NAME condition as we've already checked for
714 	 * errors in the previous loop.
715 	 */
716 	if (st == HIGHER_NAME) {
717 		while (nis_dir_cmp(from, to) != SAME_NAME) {
718 			names[--end] = strdup(to);
719 			to = nis_domain_of(to);
720 		}
721 	} else if (st == LOWER_NAME) {
722 		from = nis_domain_of(from);
723 		while (nis_dir_cmp(from, to) != SAME_NAME) {
724 			names[start++] = strdup(from);
725 			from = nis_domain_of(from);
726 		}
727 		names[start++] = strdup(to);	/* include actual target */
728 	} else if (st == NOT_SEQUENTIAL) {
729 		/* names are not sequential */
730 		from = nis_domain_of(from);
731 		while (nis_dir_cmp(from, to) == NOT_SEQUENTIAL) {
732 			names[start++] = strdup(from);
733 			from = nis_domain_of(from);
734 		}
735 		names[start++] = strdup(from);	/* include common parent */
736 		while (nis_dir_cmp(from, to) != SAME_NAME) {
737 			names[--end] = strdup(to);
738 			to = nis_domain_of(to);
739 		}
740 	}
741 
742 	/* make sure all of the allocations were successful */
743 	for (i = 0; i < n; i++) {
744 		if (names[i] == NULL) {
745 			__nis_path_free(names, n);
746 			names = NULL;
747 			break;
748 		}
749 	}
750 
751 	/* Set the return values */
752 
753 	*path_length = n;
754 	*namesp = names;
755 
756 	return (NIS_SUCCESS);
757 }
758 
759 /*
760  *  This is a stub function for clients.  There is a version of
761  *  it in rpc.nisd that checks to see if the host is listed in
762  *  the server list.
763  */
764 int
765 __nis_host_is_server(nis_server *srv, int nsrv)
766 {
767 #ifdef lint
768 	srv = srv;
769 	nsrv = nsrv;
770 #endif /* lint */
771 	return (0);
772 }
773 
774 char *call_names[] = {
775 	"NULL",
776 	"NIS_LOOKUP",
777 	"NIS_ADD",
778 	"NIS_MODIFY",
779 	"NIS_REMOVE",
780 	"NIS_IBLIST",
781 	"NIS_IBADD",
782 	"NIS_IBMODIFY",
783 	"NIS_IBREMOVE",
784 	"NIS_IBFIRST",
785 	"NIS_IBNEXT",
786 	"13",
787 	"NIS_FINDDIRECTORY",
788 	"NIS_STATUS",
789 	"NIS_DUMPLOG",
790 	"NIS_DUMP",
791 	"NIS_CALLBACK",
792 	"NIS_CPTIME",
793 	"NIS_CHECKPOINT",
794 	"NIS_PING",
795 	"NIS_SERVSTATE",
796 	"NIS_MKDIR",
797 	"NIS_RMDIR",
798 	"NIS_UPDKEYS",
799 };
800 
801 void
802 __nis_print_call(CLIENT *clnt, int proc)
803 {
804 	char *name;
805 	char *pname;
806 	char lbuf[10];
807 
808 	name = handle_to_server_name(clnt);
809 	if (proc > NIS_UPDKEYS)
810 		(void) sprintf(lbuf, "%d", proc);
811 	else
812 		pname = call_names[proc];
813 	(void) fprintf(__nis_debug_file, "calling server %s for %s\n",
814 	    name, pname);
815 }
816 
817 void
818 __nis_print_rpc_result(enum clnt_stat status)
819 {
820 	(void) fprintf(__nis_debug_file, "result:  %s\n", clnt_sperrno(status));
821 }
822 
823 void
824 __nis_print_req(ib_request *req)
825 {
826 	int i;
827 	int nattr = req->ibr_srch.ibr_srch_len;
828 	nis_attr *attr = req->ibr_srch.ibr_srch_val;
829 
830 	(void) fprintf(__nis_debug_file, "[");
831 	for (i = 0; i < nattr; i++) {
832 		if (i != 0)
833 			(void) fprintf(__nis_debug_file, ",");
834 		(void) fprintf(__nis_debug_file, "%s=%s",
835 			attr[i].zattr_ndx,
836 			attr[i].zattr_val.zattr_val_val);
837 	}
838 	(void) fprintf(__nis_debug_file, "],%s\n", req->ibr_name);
839 }
840 
841 void
842 __nis_print_nsreq(ns_request *req)
843 {
844 	(void) fprintf(__nis_debug_file, "%s\n", req->ns_name);
845 }
846 
847 void
848 __nis_print_result(nis_result *res)
849 {
850 	(void) fprintf(__nis_debug_file,
851 		"status=%s, %d object%s, [z=%d, d=%d, a=%d, c=%d]\n",
852 		nis_sperrno(res->status),
853 		res->objects.objects_len,
854 		res->objects.objects_len == 1 ? "" : "s",
855 		res->zticks,
856 		res->dticks,
857 		res->aticks,
858 		res->cticks);
859 }
860 
861 void
862 __nis_print_fdreq(fd_args *arg)
863 {
864 	(void) fprintf(__nis_debug_file, "%s (from %s)\n",
865 		arg->dir_name, arg->requester);
866 }
867