xref: /netbsd-src/usr.bin/ypwhich/ypwhich.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: ypwhich.c,v 1.19 2011/08/30 17:06:21 plunky Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Charles D. Cranor
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /*
29  * ypwhich
30  * author: Chuck Cranor <chuck@netbsd>
31  * date: 31-Oct-97
32  *
33  * notes: this is a full rewrite of Theo de Raadt's ypwhich.
34  * this version allows you full control of which ypserv you
35  * talk to for the "-m" command.
36  */
37 
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/time.h>
41 
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44 
45 #include <err.h>
46 #include <netdb.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 
52 #include <rpc/rpc.h>
53 #include <rpcsvc/yp_prot.h>
54 #include <rpcsvc/ypclnt.h>
55 
56 #include "ypalias_init.h"
57 
58 /*
59  * ypwhich: query a host about its yp service
60  *
61  * usage:
62  *   ypwhich [-d domain] [[-h] host]
63  *	(who is host's ypserv?)
64  *   ypwhich [-h host] [-d domain] [-f] [-t] -m [mapname]
65  *	(who is the master of a map?)
66  *   ypwhich -x
67  *	(what nicknames do you use?)
68  *
69  *   -d: the domainname to ask about
70  *   -f: for -m, force us to talk directly to ypserv on the specified host
71  *       without going through ypbind.
72  *   -h: specify a host to ask [default = localhost]
73  *   -m: find master server for a specific map (no map means 'all maps')
74  *   -t: inhibit nickname translation
75  *   -T: use TCP instead of UDP
76  *   -x: print list of yp map aliases and exit
77  */
78 
79 /*
80  * prototypes
81  */
82 
83 static void find_mapmaster(const char *, const char *, const char *,
84     int, int, int, const struct ypalias *);
85 static struct in_addr *find_server(const char *, const char *, int);
86 static CLIENT *mkclient(struct sockaddr_in *, unsigned long, unsigned long,
87     int);
88 static void usage(void) __attribute__((__noreturn__));
89 
90 /*
91  * main
92  */
93 int
94 main(int argc, char *argv[])
95 
96 {
97 	const char   *targhost = "localhost";
98 	char   *ourdomain;
99 	int     inhibit = 0, force = 0, tcp = 0;
100 	char   *targmap = NULL;
101 	int     ch, saw_m;
102 	struct in_addr *inaddr;
103 	struct hostent *he;
104 	size_t i;
105 	const struct ypalias *ypaliases;
106 
107 	/*
108          * get default domainname and parse options
109          */
110 
111 	ypaliases = ypalias_init();
112 	(void)yp_get_default_domain(&ourdomain);
113 	saw_m = 0;
114 	while ((ch = getopt(argc, argv, "h:d:xtTfm")) != -1) {
115 		switch (ch) {
116 		case 'h':
117 			targhost = optarg;
118 			break;
119 		case 'd':
120 			ourdomain = optarg;
121 			break;
122 		case 'x':
123 			for (i = 0; ypaliases[i].alias; i++)
124 				(void)printf("Use \"%s\" for map \"%s\"\n",
125 				    ypaliases[i].alias, ypaliases[i].name);
126 			return 0;
127 		case 'f':
128 			force = 1;
129 			break;
130 		case 't':
131 			inhibit = 1;
132 			break;
133 		case 'T':
134 			tcp = 1;
135 			break;
136 		case 'm':
137 			if (optind < argc && argv[optind][0] != '-')
138 				targmap = argv[optind++];
139 			saw_m = 1;
140 			break;
141 		case '?':
142 		default:
143 			usage();
144 		}
145 	}
146 	argc -= optind;
147 	argv += optind;
148 	if (argc) {
149 		if (argc > 1)
150 			usage();
151 		targhost = argv[0];
152 	}
153 #ifdef DEBUG
154 	(void)printf("target_host=%s, domain=%s, inhibit=%d, saw_m=%d, map=%s, "
155 	    "force=%d, tcp=%d\n",
156 	    targhost, ourdomain, inhibit, saw_m, targmap, force, tcp);
157 #endif
158 
159 	/*
160          * need a valid domain
161          */
162 
163 	if (ourdomain == NULL)
164 		errx(1, "the domain hasn't been set on this machine.");
165 
166 	/*
167          * now do it
168          */
169 	if (saw_m)
170 		find_mapmaster(targhost, ourdomain, targmap, inhibit, force,
171 		    tcp, ypaliases);
172 	else {
173 		inaddr = find_server(targhost, ourdomain, tcp);
174 		he = gethostbyaddr((void *)&inaddr->s_addr,
175 		    sizeof(inaddr->s_addr), AF_INET);
176 		if (he)
177 			(void)printf("%s\n", he->h_name);
178 		else
179 			(void)printf("%s\n", inet_ntoa(*inaddr));
180 	}
181 	return 0;
182 }
183 
184 /*
185  * usage: print usage and exit
186  */
187 static void
188 usage(void)
189 {
190 	const char *pname = getprogname();
191 	(void)fprintf(stderr, "Usage:\t%s [-T] [-d domain] [[-h] host]\n"
192 	    "\t%s [-fTt] [-d domain] [-h host] -m [mapname]\n"
193 	    "\t%s [-T] -x\n", pname, pname, pname);
194 	exit(1);
195 }
196 
197 static CLIENT *
198 mkclient(struct sockaddr_in *sin, unsigned long prog, unsigned long vers,
199     int tcp)
200 {
201 	static struct timeval tv = { 15, 0 };
202 	int fd = RPC_ANYSOCK;
203 
204 	if (tcp)
205 		return clnttcp_create(sin, prog, vers, &fd, 0, 0);
206 	else
207 		return clntudp_create(sin, prog, vers, tv, &fd);
208 }
209 
210 /*
211  * find_server: ask a host's ypbind who its current ypserver is
212  */
213 static struct in_addr *
214 find_server(const char *host, const char *domain, int tcp)
215 {
216 	static struct in_addr result;
217 	struct sockaddr_in sin;
218 	CLIENT *ypbind;
219 	struct timeval tv;
220 	enum clnt_stat retval;
221 	struct ypbind_resp ypbind_resp;
222 
223 	/*
224          * get address of host
225          */
226 	(void)memset(&sin, 0, sizeof(sin));
227 	sin.sin_family = AF_INET;
228 	if (inet_aton(host, &sin.sin_addr) == 0) {
229 		struct hostent *he;
230 
231 		he = gethostbyname(host);
232 		if (he == NULL)
233 			errx(1, "%s: %s", host, hstrerror(h_errno));
234 		(void)memmove(&sin.sin_addr, he->h_addr, sizeof(sin.sin_addr));
235 	}
236 
237 	/*
238          * establish connection to ypbind
239          */
240 	ypbind = mkclient(&sin, YPBINDPROG, YPBINDVERS, tcp);
241 	if (ypbind == NULL)
242 		errx(1, "clnt%s_create: %s: %s", tcp ? "tcp" : "udp", 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, (unsigned int)YPBINDPROC_DOMAIN,
251 	    xdr_ypdomain_wrap_string, &domain, xdr_ypbind_resp, &ypbind_resp,
252 	    tv);
253 	clnt_destroy(ypbind);
254 	if (retval != RPC_SUCCESS)
255 		errx(1, "clnt_call: %s: %s", host, clnt_sperrno(retval));
256 	if (ypbind_resp.ypbind_status != YPBIND_SUCC_VAL)
257 		errx(1, "ypbind on %s for domain %s failed: %s", host, domain,
258 		    yperr_string(ypbind_resp.ypbind_status));
259 
260 	/*
261          * got it!
262          */
263 	result.s_addr = ypbind_resp.ypbind_respbody.
264 	    ypbind_bindinfo.ypbind_binding_addr.s_addr;	/* love that name! */
265 	return (&result);
266 }
267 
268 /*
269  * find_mapmaster: ask a host's ypserver who its map's master is
270  */
271 static void
272 find_mapmaster(const char *host, const char *domain, const char *map,
273     int inhibit, int force, int tcp, const struct ypalias *ypaliases)
274 {
275 	struct in_addr *inaddr, faddr;
276 	struct hostent *he;
277 	struct sockaddr_in sin;
278 	CLIENT *ypserv;
279 	int     yperr;
280 	enum clnt_stat retval;
281 	struct timeval tv;
282 	struct ypresp_maplist yprespmlist;
283 	struct ypmaplist fakelist, *ypml;
284 	struct ypresp_master yprespmaster;
285 	struct ypreq_nokey ypreqkey;
286 	size_t i;
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 			(void)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, tcp);
303 	}
304 
305 	/*
306          * now translate nicknames [unless inhibited]
307          */
308 	if (map && !inhibit) {
309 /*###325 [cc] error: 'i' undeclared (first use in this function)%%%*/
310 /*###325 [cc] error: (Each undeclared identifier is reported only once%%%*/
311 /*###325 [cc] error: for each function it appears in.)%%%*/
312 		for (i = 0; ypaliases[i].alias; i++) {
313 			if (strcmp(map, ypaliases[i].alias) == 0) {
314 				map = ypaliases[i].name;
315 				break;
316 			}
317 		}
318 #ifdef DEBUG
319 		(void)printf("translated map name = %s\n", map);
320 #endif
321 	}
322 
323 	/*
324          * now we try and connect to host's ypserv
325          */
326 	(void)memset(&sin, 0, sizeof(sin));
327 	sin.sin_family = AF_INET;
328 	sin.sin_addr.s_addr = inaddr->s_addr;
329 	ypserv = mkclient(&sin, YPPROG, YPVERS, tcp);
330 	if (ypserv == NULL) {
331 		warnx("clnt%s_create: %s: %s", tcp ? "tcp" : "udp", 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 		(void)memset(&yprespmlist, 0, sizeof(yprespmlist));
344 		tv.tv_sec = 5;
345 		tv.tv_usec = 0;
346 		retval = clnt_call(ypserv, (unsigned int)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 		(void)memset(&fakelist, 0, sizeof(fakelist));
366 		(void)strlcpy(fakelist.ypml_name, map, sizeof(fakelist.ypml_name));
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 		(void)memset(&yprespmaster, 0, sizeof(yprespmaster));
379 		tv.tv_sec = 5;
380 		tv.tv_usec = 0;
381 		retval = clnt_call(ypserv, (unsigned int)YPPROC_MASTER,
382 		    xdr_ypreq_nokey, &ypreqkey, xdr_ypresp_master,
383 		    &yprespmaster, tv);
384 		if (retval != RPC_SUCCESS) {
385 			warnx("clnt_call MASTER: %s: %s", host,
386 			    clnt_sperrno(retval));
387 			goto error;
388 		}
389 		yperr = ypprot_err(yprespmaster.status);
390 		if (yperr) {
391 			warnx("clnt_call: %s: %s: %s", host, ypml->ypml_name,
392 			    yperr_string(yperr));
393 		} else {
394 			(void)printf("%s %s\n", ypml->ypml_name,
395 			    yprespmaster.master);
396 		}
397 		xdr_free((xdrproc_t)xdr_ypresp_master, (void *)&yprespmaster);
398 	}
399 	clnt_destroy(ypserv);
400 
401 	/*
402          * done
403          */
404 	return;
405 
406 error:
407 	/* print host's ypserv's IP address to prevent confusion */
408 	if (ypserv)
409 		clnt_destroy(ypserv);
410 	if (!force)
411 		(void)fprintf(stderr,
412 		    "\t[note %s's ypserv running on host %s]\n",
413 		    host, inet_ntoa(*inaddr));
414 	exit(1);
415 }
416