Home | History | Annotate | Download | only in ypcmd
      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  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
     27 /*	  All Rights Reserved   */
     28 
     29 /*
     30  * Portions of this source code were derived from Berkeley
     31  * under license from the Regents of the University of
     32  * California.
     33  */
     34 
     35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     36 
     37 /*
     38  * This is a user command which tells which yp server is being used by a
     39  * given machine, or which yp server is the master for a named map.
     40  *
     41  * Usage is:
     42  *	ypwhich [-d domain] [-m [mname] [-t] | [-Vn] host]
     43  *	ypwhich -x
     44  * where:  the -d switch can be used to specify a domain other than the
     45  * default domain.  -m tells the master of that map.  mname is a mapname
     46  * If the -m option is used, ypwhich will act like a vanilla yp client,
     47  * and will not attempt to choose a particular yp server.  On the
     48  * other hand, if no -m switch is used, ypwhich will talk directly to the yp
     49  * bind process on the named host, or to the local ypbind process if no host
     50  * name is specified. -t switch inhibits nickname translation of map names.
     51  * -x is to dump the nickname translation table from file /var/yp/nicknames.
     52  *
     53  */
     54 
     55 #include <stdio.h>
     56 #include <ctype.h>
     57 #include <rpc/rpc.h>
     58 #include <rpcsvc/yp_prot.h>
     59 #include <rpcsvc/ypclnt.h>
     60 #include "yp_b.h"
     61 #include "ypv2_bind.h"
     62 #include <string.h>
     63 #include <netdir.h>
     64 #include <unistd.h>
     65 #include <netdb.h>
     66 #include <arpa/inet.h>
     67 #include <inet/ip.h>
     68 #include <inet/ip6.h>
     69 #include <netinet/ip6.h>
     70 #include <sys/utsname.h>
     71 
     72 #define	YPSLEEPTIME 5 /* between two tries of bind */
     73 
     74 #define	TIMEOUT 30			/* Total seconds for timeout */
     75 #define	INTER_TRY 10			/* Seconds between tries */
     76 
     77 static int translate = TRUE;
     78 static int dodump = FALSE;
     79 static char *domain = NULL;
     80 static char default_domain_name[YPMAXDOMAIN];
     81 static char *host = NULL;
     82 static int vers = YPBINDVERS;
     83 static char default_host_name[256];
     84 static bool get_master = FALSE;
     85 static bool get_server = FALSE;
     86 static char *map = NULL;
     87 static char nm[YPMAXMAP+1];
     88 static struct timeval timeout = {
     89 		TIMEOUT,			/* Seconds */
     90 		0				/* Microseconds */
     91 };
     92 static char nullstring[] = "\000";
     93 static char err_usage[] =
     94 "Usage:\n\
     95 	ypwhich [-d domain] [[-t] -m [mname] | [-Vn] host]\n\
     96 	ypwhich -x\n\
     97 where\n\
     98 	mname may be either a mapname or a nickname for a map.\n\
     99 	host if specified, is the machine whose NIS server is to be found.\n\
    100 	-t inhibits map nickname translation.\n\
    101 	-Vn version of ypbind, V3 is default.\n\
    102 	-x dumps the map nickname translation table.\n";
    103 static char err_bad_args[] =
    104 "ypwhich:  %s argument is bad.\n";
    105 static char err_cant_get_kname[] =
    106 "ypwhich:  can't get %s back from system call.\n";
    107 static char err_null_kname[] =
    108 "ypwhich:  the %s hasn't been set on this machine.\n";
    109 static char err_bad_mapname[] = "mapname";
    110 static char err_bad_domainname[] = "domainname";
    111 static char err_bad_hostname[] = "hostname";
    112 
    113 static void get_command_line_args();
    114 static void getdomain();
    115 static void getlochost();
    116 static void get_server_name();
    117 static int call_binder();
    118 static void get_map_master();
    119 extern void maketable();
    120 extern int getmapname();
    121 #ifdef DEBUG
    122 static void dump_response();
    123 #endif
    124 static void dump_ypmaps();
    125 static void dumpmaps();
    126 
    127 static bool xdr_yp_inaddr();
    128 static bool xdr_old_ypbind_resp();
    129 static bool xdr_old_yp_binding();
    130 static int old_call_binder();
    131 static void print_server();
    132 
    133 /* need these for call to (remote) V2 ypbind */
    134 struct old_ypbind_binding {
    135 	struct in_addr ypbind_binding_addr;	/* In network order */
    136 	unsigned short int ypbind_binding_port;	/* In network order */
    137 };
    138 
    139 struct old_ypbind_resp {
    140 	enum ypbind_resptype ypbind_status;
    141 	union {
    142 		unsigned long ypbind_error;
    143 		struct old_ypbind_binding ypbind_bindinfo;
    144 	} ypbind_respbody;
    145 };
    146 
    147 /*
    148  * This is the main line for the ypwhich process.
    149  */
    150 int
    151 main(argc, argv)
    152 char **argv;
    153 {
    154 	get_command_line_args(argc, argv);
    155 
    156 	if (dodump) {
    157 		maketable(dodump);
    158 		exit(0);
    159 	}
    160 
    161 	if (!domain) {
    162 		getdomain();
    163 	}
    164 
    165 	if (map && translate && (strchr(map, '.') == NULL) &&
    166 		(getmapname(map, nm))) {
    167 		map = nm;
    168 	}
    169 
    170 	if (get_server) {
    171 		if (!host)
    172 			getlochost();
    173 		get_server_name();
    174 	} else {
    175 		if (map)
    176 			get_map_master();
    177 		else
    178 			dump_ypmaps();
    179 	}
    180 
    181 	return (0);
    182 }
    183 
    184 /*
    185  * This does the command line argument processing.
    186  */
    187 static void
    188 get_command_line_args(argc, argv)
    189 int argc;
    190 char **argv;
    191 
    192 {
    193 	argv++;
    194 
    195 	if (argc == 1) {
    196 		get_server = TRUE;
    197 		return;
    198 	}
    199 
    200 	while (--argc) {
    201 
    202 		if ((*argv)[0] == '-') {
    203 
    204 			switch ((*argv)[1]) {
    205 
    206 			case 'V':
    207 
    208 				vers = atoi(argv[0]+2);
    209 				if (vers <  1) {
    210 					(void) fprintf(stderr, err_usage);
    211 					exit(1);
    212 				}
    213 				argv++;
    214 				break;
    215 
    216 			case 'm':
    217 				get_master = TRUE;
    218 				argv++;
    219 
    220 				if (argc > 1) {
    221 
    222 					if ((*(argv))[0] == '-') {
    223 						break;
    224 					}
    225 
    226 					argc--;
    227 					map = *argv;
    228 					argv++;
    229 
    230 					if ((int)strlen(map) > YPMAXMAP) {
    231 				(void) fprintf(stderr, err_bad_args,
    232 						    err_bad_mapname);
    233 						exit(1);
    234 					}
    235 
    236 				}
    237 
    238 				break;
    239 
    240 			case 'd':
    241 
    242 				if (argc > 1) {
    243 					argv++;
    244 					argc--;
    245 					domain = *argv;
    246 					argv++;
    247 
    248 					if ((int)strlen(domain) > YPMAXDOMAIN) {
    249 				(void) fprintf(stderr, err_bad_args,
    250 				err_bad_domainname);
    251 						exit(1);
    252 					}
    253 
    254 				} else {
    255 					(void) fprintf(stderr, err_usage);
    256 					exit(1);
    257 				}
    258 
    259 				break;
    260 
    261 			case 't':
    262 				translate = FALSE;
    263 				argv++;
    264 				break;
    265 
    266 			case 'x':
    267 				dodump = TRUE;
    268 				argv++;
    269 				break;
    270 
    271 			default:
    272 				(void) fprintf(stderr, err_usage);
    273 				exit(1);
    274 			}
    275 
    276 		} else {
    277 
    278 			if (get_server) {
    279 				(void) fprintf(stderr, err_usage);
    280 				exit(1);
    281 			}
    282 
    283 			get_server = TRUE;
    284 			host = *argv;
    285 			argv++;
    286 
    287 			if ((int)strlen(host) > 256) {
    288 				(void) fprintf(stderr,
    289 			err_bad_args, err_bad_hostname);
    290 				exit(1);
    291 			}
    292 		}
    293 	}
    294 
    295 	if (get_master && get_server) {
    296 		(void) fprintf(stderr, err_usage);
    297 		exit(1);
    298 	}
    299 
    300 	if (!get_master && !get_server) {
    301 		get_server = TRUE;
    302 	}
    303 }
    304 
    305 /*
    306  * This gets the local default domainname, and makes sure that it's set
    307  * to something reasonable.  domain is set here.
    308  */
    309 static void
    310 getdomain()
    311 {
    312 	if (!getdomainname(default_domain_name, YPMAXDOMAIN)) {
    313 		domain = default_domain_name;
    314 	} else {
    315 		(void) fprintf(stderr, err_cant_get_kname, err_bad_domainname);
    316 		exit(1);
    317 	}
    318 
    319 	if ((int)strlen(domain) == 0) {
    320 		(void) fprintf(stderr, err_null_kname, err_bad_domainname);
    321 		exit(1);
    322 	}
    323 }
    324 
    325 /*
    326  * This gets the local hostname back from the kernel
    327  */
    328 static void
    329 getlochost()
    330 {
    331 	struct utsname utsname;
    332 
    333 	if (uname(&utsname) != -1) {
    334 		strcpy(default_host_name, utsname.nodename);
    335 		host = default_host_name;
    336 	} else {
    337 		(void) fprintf(stderr, err_cant_get_kname, err_bad_hostname);
    338 		exit(1);
    339 	}
    340 
    341 }
    342 
    343 /*
    344  * This tries to find the name of the server to which the binder in question
    345  * is bound.  If one of the -Vx flags was specified, it will try only for
    346  * that protocol version, otherwise, it will start with the current version,
    347  * then drop back to the previous version.
    348  */
    349 static void
    350 get_server_name()
    351 {
    352 	char *notbound = "Domain %s not bound on %s.\n";
    353 
    354 	if (vers >= 3) {
    355 		if (!call_binder(vers))
    356 			(void) fprintf(stderr, notbound, domain, host);
    357 	} else {
    358 		if (!old_call_binder(vers))
    359 			(void) fprintf(stderr, notbound, domain, host);
    360 	}
    361 }
    362 
    363 extern CLIENT *__clnt_create_loopback();
    364 
    365 /*
    366  * This sends a message to the ypbind process on the node with
    367  * the host name
    368  */
    369 static int
    370 call_binder(vers)
    371 int vers;
    372 {
    373 	CLIENT *client;
    374 	struct ypbind_resp *response;
    375 	struct ypbind_domain ypbd;
    376 	char errstring[256];
    377 	extern struct rpc_createerr rpc_createerr;
    378 	int yperr = 0;
    379 	struct utsname utsname;
    380 	const char *str;
    381 
    382 	/*
    383 	 * CAUTION: Do not go to NIS if the host is the same as the local host
    384 	 * XXX: Lots of special magic to distinguish between local and remote
    385 	 * case. We want to make sure the local case doesn't hang.
    386 	 */
    387 
    388 	if ((uname(&utsname) != -1) &&
    389 		(strcmp(host, utsname.nodename) == 0))
    390 		client = __clnt_create_loopback(YPBINDPROG, vers, &yperr);
    391 	else
    392 		client = clnt_create(host, YPBINDPROG, vers, "netpath");
    393 	if (client == NULL) {
    394 		if (yperr)
    395 			(void) fprintf(stderr,
    396 				"ypwhich: %s\n", yperr_string(yperr));
    397 		else {
    398 			if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED ||
    399 				rpc_createerr.cf_stat == RPC_PROGUNAVAIL) {
    400 				(void) fprintf(stderr,
    401 			"ypwhich: %s is not running ypbind\n", host);
    402 			} else if (rpc_createerr.cf_stat == RPC_PMAPFAILURE) {
    403 				(void) fprintf(stderr,
    404 			"ypwhich: %s is not running rpcbind\n",
    405 					host);
    406 			} else
    407 				(void) clnt_pcreateerror("ypwhich: \
    408 clnt_create error");
    409 		}
    410 		exit(1);
    411 	}
    412 	ypbd.ypbind_domainname = domain;
    413 	ypbd.ypbind_vers = vers;
    414 	response = ypbindproc_domain_3(&ypbd, client);
    415 
    416 	if (response == NULL) {
    417 		(void) sprintf(errstring,
    418 		    "ypwhich: can't call ypbind on %s", host);
    419 		(void) clnt_perror(client, errstring);
    420 		exit(1);
    421 	}
    422 
    423 	clnt_destroy(client);
    424 
    425 	if (response->ypbind_status != YPBIND_SUCC_VAL)  {
    426 		return (FALSE);
    427 	}
    428 
    429 	if (response->ypbind_resp_u.ypbind_bindinfo) {
    430 		char *server =
    431 	response->ypbind_resp_u.ypbind_bindinfo->ypbind_servername;
    432 
    433 		if (strcmp(server, nullstring) == 0) {
    434 		/* depends on a hack in ypbind */
    435 			struct nd_hostservlist *nhs = NULL;
    436 			struct netconfig *nconf =
    437 		response->ypbind_resp_u.ypbind_bindinfo->ypbind_nconf;
    438 			struct netbuf *svcaddr =
    439 		response->ypbind_resp_u.ypbind_bindinfo->ypbind_svcaddr;
    440 
    441 			if (netdir_getbyaddr(nconf, &nhs, svcaddr) != ND_OK) {
    442 				struct sockaddr_in	*sa4;
    443 				struct sockaddr_in6	*sa6;
    444 				char			buf[INET6_ADDRSTRLEN];
    445 				char			xbuf[IPV6_ADDR_LEN];
    446 				int			af;
    447 				void			*addr;
    448 				XDR			xdrs;
    449 
    450 				sa4 = (struct sockaddr_in *)svcaddr->buf;
    451 				af = ntohs(sa4->sin_family);
    452 				if (af != sa4->sin_family) {
    453 					xdrmem_create(&xdrs,
    454 						(caddr_t)xbuf, IPV6_ADDR_LEN,
    455 						XDR_DECODE);
    456 					if (af == AF_INET6) {
    457 						xdr_opaque(&xdrs,
    458 							(caddr_t)svcaddr->buf,
    459 							IPV6_ADDR_LEN);
    460 						sa6 = (struct sockaddr_in6 *)
    461 							xbuf;
    462 						addr = &sa6->sin6_addr;
    463 					} else {
    464 						xdr_opaque(&xdrs,
    465 							(caddr_t)svcaddr->buf,
    466 							IPV4_ADDR_LEN);
    467 						sa4 = (struct sockaddr_in *)
    468 							xbuf;
    469 						addr = &sa4->sin_addr;
    470 					}
    471 				} else {
    472 					if (af == AF_INET6) {
    473 						sa6 = (struct sockaddr_in6 *)
    474 							svcaddr->buf;
    475 						addr = &sa6->sin6_addr;
    476 					} else {
    477 						addr = &sa4->sin_addr;
    478 					}
    479 				}
    480 				str = inet_ntop(af, addr, buf, sizeof (buf));
    481 				if (str == NULL)
    482 					perror("inet_ntop");
    483 				else
    484 					fprintf(stdout, "%s\n", str);
    485 			} else {
    486 				str = nhs->h_hostservs->h_host;
    487 				if (str == NULL)
    488 					str = "<unknown>";
    489 				fprintf(stdout, "%s\n", str);
    490 			}
    491 			netdir_free((char *)nhs, ND_HOSTSERVLIST);
    492 		} else {
    493 			fprintf(stdout, "%s\n", server);
    494 		}
    495 	}
    496 #ifdef DEBUG
    497 	dump_response(response);
    498 #endif
    499 	return (TRUE);
    500 }
    501 
    502 /*
    503  * Serializes/deserializes an in_addr struct.
    504  *
    505  * Note:  There is a data coupling between the "definition" of a struct
    506  * in_addr implicit in this xdr routine, and the true data definition in
    507  * <netinet/in.h>.
    508  */
    509 static bool xdr_yp_inaddr(xdrs, ps)
    510 	XDR * xdrs;
    511 	struct in_addr *ps;
    512 
    513 {
    514 	return (xdr_opaque(xdrs, (caddr_t)&ps->s_addr, 4));
    515 }
    516 
    517 /*
    518  * Serializes/deserializes an old ypbind_binding struct.
    519  */
    520 static bool xdr_old_yp_binding(xdrs, ps)
    521 	XDR * xdrs;
    522 	struct old_ypbind_binding *ps;
    523 
    524 {
    525 	return (xdr_yp_inaddr(xdrs, &ps->ypbind_binding_addr) &&
    526 	    xdr_opaque(xdrs, (caddr_t)&ps->ypbind_binding_port, 2));
    527 }
    528 
    529 /*
    530  * Serializes/deserializes a ypbind_resp structure.
    531  */
    532 static bool xdr_old_ypbind_resp(xdrs, ps)
    533 	XDR * xdrs;
    534 	struct old_ypbind_resp *ps;
    535 
    536 {
    537 	if (!xdr_enum(xdrs, (enum_t *)&ps->ypbind_status)) {
    538 		return (FALSE);
    539 	}
    540 	switch (ps->ypbind_status) {
    541 	case YPBIND_SUCC_VAL:
    542 		return (xdr_old_yp_binding(xdrs,
    543 				&ps->ypbind_respbody.ypbind_bindinfo));
    544 	case YPBIND_FAIL_VAL:
    545 		return (xdr_u_long(xdrs,
    546 				&ps->ypbind_respbody.ypbind_error));
    547 	}
    548 	return (FALSE);
    549 }
    550 /* This sends a message to the old ypbind process on host. */
    551 static int old_call_binder(vers)
    552 	int vers;
    553 {
    554 	CLIENT *client;
    555 	struct hostent *hp;
    556 	int sock = RPC_ANYSOCK;
    557 	enum clnt_stat rpc_stat;
    558 	struct old_ypbind_resp response;
    559 	char errstring[256];
    560 	extern struct rpc_createerr rpc_createerr;
    561 	struct in_addr *server;
    562 
    563 	if ((client = clnt_create(host, YPBINDPROG, vers, "udp")) == NULL) {
    564 		if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) {
    565 			(void) printf("ypwhich: %s is not running ypbind\n",
    566 				host);
    567 			exit(1);
    568 		}
    569 		if (rpc_createerr.cf_stat == RPC_PMAPFAILURE) {
    570 		    (void) printf("ypwhich: %s is not running port mapper\n",
    571 				host);
    572 			exit(1);
    573 		}
    574 		(void) clnt_pcreateerror("ypwhich:  clnt_create error");
    575 		exit(1);
    576 	}
    577 
    578 	rpc_stat = clnt_call(client, YPBINDPROC_DOMAIN,
    579 			(xdrproc_t)xdr_ypdomain_wrap_string, (caddr_t)&domain,
    580 			(xdrproc_t)xdr_old_ypbind_resp, (caddr_t)&response,
    581 			timeout);
    582 
    583 	if ((rpc_stat != RPC_SUCCESS) &&
    584 	    (rpc_stat != RPC_PROGVERSMISMATCH)) {
    585 		(void) sprintf(errstring,
    586 		    "ypwhich: can't call ypbind on %s", host);
    587 		(void) clnt_perror(client, errstring);
    588 		exit(1);
    589 	}
    590 
    591 	clnt_destroy(client);
    592 	close(sock);
    593 
    594 	if ((rpc_stat != RPC_SUCCESS) ||
    595 	    (response.ypbind_status != YPBIND_SUCC_VAL)) {
    596 		return (FALSE);
    597 	}
    598 
    599 	server = &response.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr;
    600 	print_server  (server);
    601 
    602 	return (TRUE);
    603 }
    604 
    605 /*
    606  * For old version:
    607  * This translates a server address to a name and prints it.
    608  * We'll get a name by using the standard library routine.
    609  */
    610 static void print_server(server)
    611 	struct in_addr *server;
    612 {
    613 	char buf[256];
    614 	struct hostent *hp;
    615 
    616 	strcpy(buf, inet_ntoa(*server));
    617 	hp = gethostbyaddr((char *)&server->s_addr,
    618 			sizeof (struct in_addr), AF_INET);
    619 
    620 	printf("%s\n", hp ? hp->h_name : buf);
    621 }
    622 
    623 #ifdef DEBUG
    624 static void
    625 dump_response(which)
    626 ypbind_resp * which;
    627 {
    628 	struct netconfig *nc;
    629 	struct netbuf *ua;
    630 	ypbind_binding * b;
    631 
    632 	int i;
    633 
    634 	{
    635 		b = which->ypbind_resp_u.ypbind_bindinfo;
    636 		if (b == NULL)
    637 			(void) fprintf(stderr, "???NO Binding information\n");
    638 		else {
    639 			(void) fprintf(stderr,
    640 		"server=%s lovers=%ld hivers=%ld\n",
    641 			    b->ypbind_servername,
    642 				b->ypbind_lo_vers, b->ypbind_hi_vers);
    643 			nc = b->ypbind_nconf;
    644 			ua = b->ypbind_svcaddr;
    645 			if (nc == NULL)
    646 				(void) fprintf(stderr,
    647 			"ypwhich: NO netconfig information\n");
    648 			else {
    649 				(void) fprintf(stderr,
    650 		"ypwhich: id %s device %s flag %x protofmly %s proto %s\n",
    651 		nc->nc_netid, nc->nc_device,
    652 		(int)nc->nc_flag, nc->nc_protofmly,
    653 		nc->nc_proto);
    654 			}
    655 			if (ua == NULL)
    656 				(void) fprintf(stderr,
    657 		"ypwhich: NO netbuf information available from binder\n");
    658 			else {
    659 				(void) fprintf(stderr,
    660 			"maxlen=%d len=%d\naddr=", ua->maxlen, ua->len);
    661 				for (i = 0; i < ua->len; i++) {
    662 					if (i != (ua->len - 1))
    663 						(void) fprintf(stderr,
    664 					"%d.", ua->buf[i]);
    665 					else
    666 						(void) fprintf(stderr,
    667 					"%d\n", ua->buf[i]);
    668 				}
    669 			}
    670 		}
    671 	}
    672 
    673 }
    674 #endif
    675 
    676 /*
    677  * This translates a server address to a name and prints it.  If the address
    678  * is the same as the local address as returned by get_myaddress, the name
    679  * is that retrieved from the kernel.  If it's any other address (including
    680  * another ip address for the local machine), we'll get a name by using the
    681  * standard library routine (which calls the yp).
    682  */
    683 
    684 /*
    685  * This asks any yp server for the map's master.
    686  */
    687 static void
    688 get_map_master()
    689 {
    690 	int err;
    691 	char *master;
    692 
    693 	err = __yp_master_rsvdport(domain, map, &master);
    694 
    695 	if (err) {
    696 		(void) fprintf(stderr,
    697 		    "ypwhich:  Can't find the master of %s.  Reason: %s.\n",
    698 		    map, yperr_string(err));
    699 		exit(1);
    700 	} else {
    701 		(void) printf("%s\n", master);
    702 	}
    703 }
    704 
    705 /*
    706  * This enumerates the entries within map "ypmaps" in the domain at global
    707  * "domain", and prints them out key and value per single line.  dump_ypmaps
    708  * just decides whether we are (probably) able to speak the new YP protocol,
    709  * and dispatches to the appropriate function.
    710  */
    711 static void
    712 dump_ypmaps()
    713 {
    714 	int err;
    715 	struct dom_binding *binding;
    716 
    717 	if (err = __yp_dobind(domain, &binding)) {
    718 		(void) fprintf(stderr,
    719 		    "dump_ypmaps: Can't bind for domain %s.  Reason: %s\n",
    720 		    domain, yperr_string(err));
    721 		return;
    722 	}
    723 
    724 	if (binding->dom_binding->ypbind_hi_vers  >= YPVERS) {
    725 		dumpmaps(binding);
    726 	}
    727 }
    728 
    729 static void
    730 dumpmaps(binding)
    731 struct dom_binding *binding;
    732 {
    733 	enum clnt_stat rpc_stat;
    734 	int err;
    735 	char *master;
    736 	struct ypmaplist *pmpl;
    737 	struct ypresp_maplist maplist;
    738 
    739 	maplist.list = (struct ypmaplist *)NULL;
    740 
    741 	rpc_stat = clnt_call(binding->dom_client, YPPROC_MAPLIST,
    742 	    (xdrproc_t)xdr_ypdomain_wrap_string, (caddr_t)&domain,
    743 	    (xdrproc_t)xdr_ypresp_maplist, (caddr_t)&maplist,
    744 	    timeout);
    745 
    746 	if (rpc_stat != RPC_SUCCESS) {
    747 		(void) clnt_perror(binding->dom_client,
    748 		    "ypwhich(dumpmaps): can't get maplist");
    749 		__yp_rel_binding(binding);
    750 		exit(1);
    751 	}
    752 
    753 	if (maplist.status != YP_TRUE) {
    754 		(void) fprintf(stderr,
    755 		    "ypwhich:  Can't get maplist.  Reason:  %s.\n",
    756 		    yperr_string(ypprot_err(maplist.status)));
    757 		exit(1);
    758 	}
    759 	__yp_rel_binding(binding);
    760 
    761 	for (pmpl = maplist.list; pmpl; pmpl = pmpl->ypml_next) {
    762 		(void) printf("%s ", pmpl->ypml_name);
    763 
    764 		err = __yp_master_rsvdport(domain, pmpl->ypml_name, &master);
    765 
    766 		if (err) {
    767 			(void) printf("????????\n");
    768 			(void) fprintf(stderr,
    769 		"ypwhich:  Can't find the master of %s.  Reason: %s.\n",
    770 		pmpl->ypml_name, yperr_string(err));
    771 		} else {
    772 			(void) printf("%s\n", master);
    773 		}
    774 	}
    775 }
    776