xref: /netbsd-src/usr.bin/ypwhich/ypwhich.c (revision 6361925b168fae9924fd5b4c3d7938744955bffd)
1 /*	$NetBSD: ypwhich.c,v 1.21 2017/08/11 20:32:34 ginsbach 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
main(int argc,char * argv[])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
usage(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 *
mkclient(struct sockaddr_in * sin,unsigned long prog,unsigned long vers,int tcp)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 *
find_server(const char * host,const char * domain,int tcp)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
find_mapmaster(const char * host,const char * domain,const char * map,int inhibit,int force,int tcp,const struct ypalias * ypaliases)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 its 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 		for (i = 0; ypaliases[i].alias; i++) {
310 			if (strcmp(map, ypaliases[i].alias) == 0) {
311 				map = ypaliases[i].name;
312 				break;
313 			}
314 		}
315 #ifdef DEBUG
316 		(void)printf("translated map name = %s\n", map);
317 #endif
318 	}
319 
320 	/*
321          * now we try and connect to host's ypserv
322          */
323 	(void)memset(&sin, 0, sizeof(sin));
324 	sin.sin_family = AF_INET;
325 	sin.sin_addr.s_addr = inaddr->s_addr;
326 	ypserv = mkclient(&sin, YPPROG, YPVERS, tcp);
327 	if (ypserv == NULL) {
328 		warnx("clnt%s_create: %s: %s", tcp ? "tcp" : "udp", host,
329 		    yperr_string(YPERR_YPSERV));
330 		goto error;
331 	}
332 
333 	/*
334          * did the user specify a map?
335          */
336 	if (map == NULL) {
337 		/*
338 	         * if no map specified, we ask ypserv for a list of all maps
339 	         */
340 		(void)memset(&yprespmlist, 0, sizeof(yprespmlist));
341 		tv.tv_sec = 5;
342 		tv.tv_usec = 0;
343 		retval = clnt_call(ypserv, (unsigned int)YPPROC_MAPLIST,
344 		    xdr_ypdomain_wrap_string, &domain, xdr_ypresp_maplist,
345 		    &yprespmlist, tv);
346 		if (retval != RPC_SUCCESS) {
347 			warnx("clnt_call MAPLIST: %s: %s", host,
348 			    clnt_sperrno(retval));
349 			goto error;
350 		}
351 		yperr = ypprot_err(yprespmlist.status);
352 		if (yperr) {
353 			warnx("clnt_call: %s: %s", host, yperr_string(yperr));
354 			goto error;
355 		}
356 		ypml = yprespmlist.list;
357 	} else {
358 		/*
359 	         * build a fake "list" of maps containing only the list the user
360 	         * asked about in it.
361 	         */
362 		(void)memset(&fakelist, 0, sizeof(fakelist));
363 		(void)strlcpy(fakelist.ypml_name, map, sizeof(fakelist.ypml_name));
364 		fakelist.ypml_next = NULL;
365 		ypml = &fakelist;
366 	}
367 
368 	/*
369          * we now have a list of maps.   ask ypserv who is the master for
370          * each map...
371          */
372 	for ( /* null */ ; ypml != NULL; ypml = ypml->ypml_next) {
373 		ypreqkey.domain = domain;
374 		ypreqkey.map = ypml->ypml_name;
375 		(void)memset(&yprespmaster, 0, sizeof(yprespmaster));
376 		tv.tv_sec = 5;
377 		tv.tv_usec = 0;
378 		retval = clnt_call(ypserv, (unsigned int)YPPROC_MASTER,
379 		    xdr_ypreq_nokey, &ypreqkey, xdr_ypresp_master,
380 		    &yprespmaster, tv);
381 		if (retval != RPC_SUCCESS) {
382 			warnx("clnt_call MASTER: %s: %s", host,
383 			    clnt_sperrno(retval));
384 			goto error;
385 		}
386 		yperr = ypprot_err(yprespmaster.status);
387 		if (yperr) {
388 			warnx("clnt_call: %s: %s: %s", host, ypml->ypml_name,
389 			    yperr_string(yperr));
390 		} else {
391 			(void)printf("%s %s\n", ypml->ypml_name,
392 			    yprespmaster.master);
393 		}
394 		xdr_free((xdrproc_t)xdr_ypresp_master, (void *)&yprespmaster);
395 	}
396 	clnt_destroy(ypserv);
397 
398 	/*
399          * done
400          */
401 	return;
402 
403 error:
404 	/* print host's ypserv's IP address to prevent confusion */
405 	if (ypserv)
406 		clnt_destroy(ypserv);
407 	if (!force)
408 		(void)fprintf(stderr,
409 		    "\t[note %s's ypserv running on host %s]\n",
410 		    host, inet_ntoa(*inaddr));
411 	exit(1);
412 }
413