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