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