xref: /netbsd/usr.bin/ypwhich/ypwhich.c (revision bf9ec67e)
1 /*	$NetBSD: ypwhich.c,v 1.11 2001/02/19 23:03:54 cgd Exp $	*/
2 
3 /*
4  *
5  * Copyright (c) 1997 Charles D. Cranor
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 /*
32  * ypwhich
33  * author: Chuck Cranor <chuck@ccrc.wustl.edu>
34  * date: 31-Oct-97
35  *
36  * notes: this is a full rewrite of Theo de Raadt's ypwhich.
37  * this version allows you full control of which ypserv you
38  * talk to for the "-m" command.
39  */
40 
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <sys/time.h>
44 
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 
48 #include <err.h>
49 #include <netdb.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 
55 #include <rpc/rpc.h>
56 #include <rpcsvc/yp_prot.h>
57 #include <rpcsvc/ypclnt.h>
58 
59 
60 /*
61  * ypwhich: query a host about its yp service
62  *
63  * usage:
64  *   ypwhich [-d domain] [[-h] host]
65  *	(who is host's ypserv?)
66  *   ypwhich [-h host] [-d domain] [-f] [-t] -m [mapname]
67  *	(who is the master of a map?)
68  *   ypwhich -x
69  *	(what nicknames do you use?)
70  *
71  *   -d: the domainname to ask about
72  *   -f: for -m, force us to talk directly to ypserv on the specified host
73  *       without going through ypbind.
74  *   -h: specify a host to ask [default = localhost]
75  *   -m: find master server for a specific map (no map means 'all maps')
76  *   -t: inhibit nickname translation
77  *   -x: print list of yp map aliases and exit
78  */
79 
80 static char *ypnicknames[] = {
81 	"aliases",	"mail.aliases",
82 	"ethers",	"ethers.byname",
83 	"group",	"group.byname",
84 	"hosts",	"hosts.byaddr",
85 	"networks",	"networks.byaddr",
86 	"passwd",	"passwd.byname",
87 	"protocols",	"protocols.bynumber",
88 	"services",	"services.byname",
89 	0,		0,
90 };
91 
92 
93 /*
94  * prototypes
95  */
96 
97 void		find_mapmaster __P((const char *, const char *, const char *,
98 				    int, int));
99 struct in_addr *find_server __P((const char *, const char *));
100 int		main __P((int, char *[]));
101 void		usage __P((void));
102 
103 /*
104  * main
105  */
106 int
107 main(argc, argv)
108 	int     argc;
109 	char  **argv;
110 
111 {
112 	char   *targhost = "localhost";
113 	char   *ourdomain;
114 	int     inhibit = 0, force = 0;
115 	char   *targmap = NULL;
116 	int     ch, saw_m, lcv;
117 	struct in_addr *inaddr;
118 	struct hostent *he;
119 
120 	/*
121          * get default domainname and parse options
122          */
123 
124 	yp_get_default_domain(&ourdomain);
125 	saw_m = 0;
126 	while ((ch = getopt(argc, argv, "h:d:xtfm")) != -1) {
127 		switch (ch) {
128 		case 'h':
129 			targhost = optarg;
130 			break;
131 		case 'd':
132 			ourdomain = optarg;
133 			break;
134 		case 'x':
135 			for (lcv = 0; ypnicknames[lcv]; lcv += 2)
136 				printf("Use \"%s\" for map \"%s\"\n",
137 				    ypnicknames[lcv], ypnicknames[lcv + 1]);
138 			exit(0);
139 		case 'f':
140 			force = 1;
141 			break;
142 		case 't':
143 			inhibit = 1;
144 			break;
145 		case 'm':
146 			if (optind < argc && argv[optind][0] != '-')
147 				targmap = argv[optind++];
148 			saw_m = 1;
149 			break;
150 		case '?':
151 		default:
152 			usage();
153 		}
154 	}
155 	argc -= optind;
156 	argv += optind;
157 	if (argc) {
158 		if (argc > 1)
159 			usage();
160 		targhost = argv[0];
161 	}
162 #ifdef DEBUG
163 	printf("target_host=%s, domain=%s, inhibit=%d, saw_m=%d, map=%s, force=%d\n",
164 	    targhost, ourdomain, inhibit, saw_m, targmap, force);
165 #endif
166 
167 	/*
168          * need a valid domain
169          */
170 
171 	if (ourdomain == NULL)
172 		errx(1, "the domain hasn't been set on this machine.");
173 
174 	/*
175          * now do it
176          */
177 	if (saw_m)
178 		find_mapmaster(targhost, ourdomain, targmap, inhibit, force);
179 	else {
180 		inaddr = find_server(targhost, ourdomain);
181 		he = gethostbyaddr((char *) &inaddr->s_addr,
182 		    sizeof(inaddr->s_addr), AF_INET);
183 		if (he)
184 			printf("%s\n", he->h_name);
185 		else
186 			printf("%s\n", inet_ntoa(*inaddr));
187 	}
188 	exit(0);
189 }
190 
191 /*
192  * usage: print usage and exit
193  */
194 void
195 usage()
196 {
197 	fprintf(stderr, "usage:\n");
198 	fprintf(stderr, "\t%s [-d domain] [[-h] host]\n", getprogname());
199 	fprintf(stderr, "\t%s [-h host] [-d domain] [-f] [-t] -m [mapname]\n",
200 	    getprogname());
201 	fprintf(stderr, "\t%s -x\n", getprogname());
202 	exit(1);
203 }
204 
205 /*
206  * find_server: ask a host's ypbind who its current ypserver is
207  */
208 struct in_addr *
209 find_server(host, domain)
210 	const char *host, *domain;
211 {
212 	static struct in_addr result;
213 	struct sockaddr_in sin;
214 	CLIENT *ypbind;
215 	int     ypbind_fd;
216 	struct timeval tv;
217 	enum clnt_stat retval;
218 	struct ypbind_resp ypbind_resp;
219 
220 	/*
221          * get address of host
222          */
223 	memset(&sin, 0, sizeof(sin));
224 	sin.sin_family = AF_INET;
225 	if (inet_aton(host, &sin.sin_addr) == 0) {
226 		struct hostent *he;
227 
228 		he = gethostbyname(host);
229 		if (he == NULL)
230 			errx(1, "%s: %s", host, hstrerror(h_errno));
231 		memmove(&sin.sin_addr, he->h_addr, sizeof(sin.sin_addr));
232 	}
233 
234 	/*
235          * establish connection to ypbind
236          */
237 	tv.tv_sec = 15;
238 	tv.tv_usec = 0;
239 	ypbind_fd = RPC_ANYSOCK;
240 	ypbind = clntudp_create(&sin, YPBINDPROG, YPBINDVERS, tv, &ypbind_fd);
241 	if (ypbind == NULL)
242 		errx(1, "clntudp_create: %s: %s", host,
243 		    yperr_string(YPERR_YPBIND));
244 
245 	/*
246          * now call ypbind's "DOMAIN" procedure to get the server name
247          */
248 	tv.tv_sec = 5;
249 	tv.tv_usec = 0;
250 	retval = clnt_call(ypbind, YPBINDPROC_DOMAIN, xdr_ypdomain_wrap_string,
251 	    &domain, xdr_ypbind_resp, &ypbind_resp, tv);
252 	clnt_destroy(ypbind);
253 	if (retval != RPC_SUCCESS)
254 		errx(1, "clnt_call: %s: %s", host, clnt_sperrno(retval));
255 	if (ypbind_resp.ypbind_status != YPBIND_SUCC_VAL)
256 		errx(1, "ypbind on %s for domain %s failed: %s\n", host, domain,
257 		    yperr_string(ypbind_resp.ypbind_status));
258 
259 	/*
260          * got it!
261          */
262 	result.s_addr = ypbind_resp.ypbind_respbody.
263 	    ypbind_bindinfo.ypbind_binding_addr.s_addr;	/* love that name! */
264 	return (&result);
265 }
266 
267 /*
268  * find_mapmaster: ask a host's ypserver who its map's master is
269  */
270 void
271 find_mapmaster(host, domain, map, inhibit, force)
272 	const char	*host, *domain, *map;
273 	int		 inhibit, force;
274 {
275 	struct in_addr *inaddr, faddr;
276 	struct hostent *he;
277 	int     lcv;
278 	struct sockaddr_in sin;
279 	CLIENT *ypserv;
280 	int     ypserv_fd, yperr;
281 	struct timeval tv;
282 	enum clnt_stat retval;
283 	struct ypresp_maplist yprespmlist;
284 	struct ypmaplist fakelist, *ypml;
285 	struct ypresp_master yprespmaster;
286 	struct ypreq_nokey ypreqkey;
287 
288 	/*
289          * we can either ask the hosts ypbind where it's ypserv is located,
290          * or we can be forced to assume that ypserv is running on the host.
291          */
292 	if (force) {
293 		if (inet_aton(host, &faddr) == 0) {
294 			he = gethostbyname(host);
295 			if (he == NULL)
296 				errx(1, "%s: %s", host, hstrerror(h_errno));
297 			memmove(&faddr, he->h_addr, sizeof(faddr));
298 		}
299 		inaddr = &faddr;
300 	} else {
301 		/* ask host "host" who is currently serving its maps  */
302 		inaddr = find_server(host, domain);
303 	}
304 
305 	/*
306          * now translate nicknames [unless inhibited]
307          */
308 	if (map && !inhibit) {
309 		for (lcv = 0; ypnicknames[lcv]; lcv += 2) {
310 			if (strcmp(map, ypnicknames[lcv]) == 0) {
311 				map = ypnicknames[lcv + 1];
312 				break;
313 			}
314 		}
315 #ifdef DEBUG
316 		printf("translated map name = %s\n", map);
317 #endif
318 	}
319 
320 	/*
321          * now we try and connect to host's ypserv
322          */
323 	memset(&sin, 0, sizeof(sin));
324 	sin.sin_family = AF_INET;
325 	sin.sin_addr.s_addr = inaddr->s_addr;
326 	tv.tv_sec = 15;
327 	tv.tv_usec = 0;
328 	ypserv_fd = RPC_ANYSOCK;
329 	ypserv = clntudp_create(&sin, YPPROG, YPVERS, tv, &ypserv_fd);
330 	if (ypserv == NULL) {
331 		warnx("clntudp_create: %s: %s", host,
332 		    yperr_string(YPERR_YPSERV));
333 		goto error;
334 	}
335 
336 	/*
337          * did the user specify a map?
338          */
339 	if (map == NULL) {
340 		/*
341 	         * if no map specified, we ask ypserv for a list of all maps
342 	         */
343 		memset(&yprespmlist, 0, sizeof(yprespmlist));
344 		tv.tv_sec = 5;
345 		tv.tv_usec = 0;
346 		retval = clnt_call(ypserv, YPPROC_MAPLIST,
347 		    xdr_ypdomain_wrap_string, &domain, xdr_ypresp_maplist,
348 		    &yprespmlist, tv);
349 		if (retval != RPC_SUCCESS) {
350 			warnx("clnt_call MAPLIST: %s: %s", host,
351 			    clnt_sperrno(retval));
352 			goto error;
353 		}
354 		yperr = ypprot_err(yprespmlist.status);
355 		if (yperr) {
356 			warnx("clnt_call: %s: %s", host, yperr_string(yperr));
357 			goto error;
358 		}
359 		ypml = yprespmlist.list;
360 	} else {
361 		/*
362 	         * build a fake "list" of maps containing only the list the user
363 	         * asked about in it.
364 	         */
365 		memset(&fakelist, 0, sizeof(fakelist));
366 		strncpy(fakelist.ypml_name, map, YPMAXMAP);
367 		fakelist.ypml_next = NULL;
368 		ypml = &fakelist;
369 	}
370 
371 	/*
372          * we now have a list of maps.   ask ypserv who is the master for
373          * each map...
374          */
375 	for ( /* null */ ; ypml != NULL; ypml = ypml->ypml_next) {
376 		ypreqkey.domain = domain;
377 		ypreqkey.map = ypml->ypml_name;
378 		memset(&yprespmaster, 0, sizeof(yprespmaster));
379 		tv.tv_sec = 5;
380 		tv.tv_usec = 0;
381 		retval = clnt_call(ypserv, YPPROC_MASTER, xdr_ypreq_nokey,
382 		    &ypreqkey, xdr_ypresp_master, &yprespmaster, tv);
383 		if (retval != RPC_SUCCESS) {
384 			warnx("clnt_call MASTER: %s: %s", host,
385 			    clnt_sperrno(retval));
386 			goto error;
387 		}
388 		yperr = ypprot_err(yprespmaster.status);
389 		if (yperr) {
390 			warnx("clnt_call: %s: %s: %s", host, ypml->ypml_name,
391 			    yperr_string(yperr));
392 		} else {
393 			printf("%s %s\n", ypml->ypml_name, yprespmaster.master);
394 		}
395 		xdr_free(xdr_ypresp_master, (char *) &yprespmaster);
396 	}
397 	clnt_destroy(ypserv);
398 
399 	/*
400          * done
401          */
402 	return;
403 
404 error:
405 	/* print host's ypserv's IP address to prevent confusion */
406 	if (ypserv)
407 		clnt_destroy(ypserv);
408 	if (!force)
409 		fprintf(stderr, "\t[note %s's ypserv running on host %s]\n",
410 		    host, inet_ntoa(*inaddr));
411 	exit(1);
412 }
413