1 /* $NetBSD: ypwhich.c,v 1.15 2008/01/25 19:54:40 christos 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 * -T: use TCP instead of UDP 78 * -x: print list of yp map aliases and exit 79 */ 80 81 static const char *ypnicknames[] = { 82 "aliases", "mail.aliases", 83 "ethers", "ethers.byname", 84 "group", "group.byname", 85 "hosts", "hosts.byaddr", 86 "networks", "networks.byaddr", 87 "passwd", "passwd.byname", 88 "protocols", "protocols.bynumber", 89 "services", "services.byname", 90 0, 0, 91 }; 92 93 94 /* 95 * prototypes 96 */ 97 98 static void find_mapmaster(const char *, const char *, const char *, 99 int, int, int); 100 static struct in_addr *find_server(const char *, const char *, int); 101 static CLIENT *mkclient(struct sockaddr_in *, unsigned long, unsigned long, 102 int); 103 static void usage(void) __attribute__((__noreturn__)); 104 105 /* 106 * main 107 */ 108 int 109 main(int argc, char *argv[]) 110 111 { 112 const char *targhost = "localhost"; 113 char *ourdomain; 114 int inhibit = 0, force = 0, tcp = 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 (void)yp_get_default_domain(&ourdomain); 125 saw_m = 0; 126 while ((ch = getopt(argc, argv, "h:d:xtTfm")) != -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 (void)printf("Use \"%s\" for map \"%s\"\n", 137 ypnicknames[lcv], ypnicknames[lcv + 1]); 138 return 0; 139 case 'f': 140 force = 1; 141 break; 142 case 't': 143 inhibit = 1; 144 break; 145 case 'T': 146 tcp = 1; 147 break; 148 case 'm': 149 if (optind < argc && argv[optind][0] != '-') 150 targmap = argv[optind++]; 151 saw_m = 1; 152 break; 153 case '?': 154 default: 155 usage(); 156 } 157 } 158 argc -= optind; 159 argv += optind; 160 if (argc) { 161 if (argc > 1) 162 usage(); 163 targhost = argv[0]; 164 } 165 #ifdef DEBUG 166 (void)printf("target_host=%s, domain=%s, inhibit=%d, saw_m=%d, map=%s, " 167 "force=%d, tcp=%d\n", 168 targhost, ourdomain, inhibit, saw_m, targmap, force, tcp); 169 #endif 170 171 /* 172 * need a valid domain 173 */ 174 175 if (ourdomain == NULL) 176 errx(1, "the domain hasn't been set on this machine."); 177 178 /* 179 * now do it 180 */ 181 if (saw_m) 182 find_mapmaster(targhost, ourdomain, targmap, inhibit, force, 183 tcp); 184 else { 185 inaddr = find_server(targhost, ourdomain, tcp); 186 he = gethostbyaddr((void *)&inaddr->s_addr, 187 sizeof(inaddr->s_addr), AF_INET); 188 if (he) 189 (void)printf("%s\n", he->h_name); 190 else 191 (void)printf("%s\n", inet_ntoa(*inaddr)); 192 } 193 return 0; 194 } 195 196 /* 197 * usage: print usage and exit 198 */ 199 static void 200 usage(void) 201 { 202 const char *pname = getprogname(); 203 (void)fprintf(stderr, "Usage:\t%s [-T] [-d domain] [[-h] host]\n" 204 "\t%s [-T] [-h host] [-d domain] [-f] [-t] -m [mapname]\n" 205 "\t%s [-T] -x\n", pname, pname, pname); 206 exit(1); 207 } 208 209 static CLIENT * 210 mkclient(struct sockaddr_in *sin, unsigned long prog, unsigned long vers, 211 int tcp) 212 { 213 static struct timeval tv = { 15, 0 }; 214 int fd = RPC_ANYSOCK; 215 216 if (tcp) 217 return clnttcp_create(sin, prog, vers, &fd, 0, 0); 218 else 219 return clntudp_create(sin, prog, vers, tv, &fd); 220 } 221 222 /* 223 * find_server: ask a host's ypbind who its current ypserver is 224 */ 225 static struct in_addr * 226 find_server(const char *host, const char *domain, int tcp) 227 { 228 static struct in_addr result; 229 struct sockaddr_in sin; 230 CLIENT *ypbind; 231 struct timeval tv; 232 enum clnt_stat retval; 233 struct ypbind_resp ypbind_resp; 234 235 /* 236 * get address of host 237 */ 238 (void)memset(&sin, 0, sizeof(sin)); 239 sin.sin_family = AF_INET; 240 if (inet_aton(host, &sin.sin_addr) == 0) { 241 struct hostent *he; 242 243 he = gethostbyname(host); 244 if (he == NULL) 245 errx(1, "%s: %s", host, hstrerror(h_errno)); 246 (void)memmove(&sin.sin_addr, he->h_addr, sizeof(sin.sin_addr)); 247 } 248 249 /* 250 * establish connection to ypbind 251 */ 252 ypbind = mkclient(&sin, YPBINDPROG, YPBINDVERS, tcp); 253 if (ypbind == NULL) 254 errx(1, "clnt%s_create: %s: %s", tcp ? "tcp" : "udp", host, 255 yperr_string(YPERR_YPBIND)); 256 257 /* 258 * now call ypbind's "DOMAIN" procedure to get the server name 259 */ 260 tv.tv_sec = 5; 261 tv.tv_usec = 0; 262 retval = clnt_call(ypbind, (unsigned int)YPBINDPROC_DOMAIN, 263 xdr_ypdomain_wrap_string, &domain, xdr_ypbind_resp, &ypbind_resp, 264 tv); 265 clnt_destroy(ypbind); 266 if (retval != RPC_SUCCESS) 267 errx(1, "clnt_call: %s: %s", host, clnt_sperrno(retval)); 268 if (ypbind_resp.ypbind_status != YPBIND_SUCC_VAL) 269 errx(1, "ypbind on %s for domain %s failed: %s", host, domain, 270 yperr_string(ypbind_resp.ypbind_status)); 271 272 /* 273 * got it! 274 */ 275 result.s_addr = ypbind_resp.ypbind_respbody. 276 ypbind_bindinfo.ypbind_binding_addr.s_addr; /* love that name! */ 277 return (&result); 278 } 279 280 /* 281 * find_mapmaster: ask a host's ypserver who its map's master is 282 */ 283 static void 284 find_mapmaster(const char *host, const char *domain, const char *map, 285 int inhibit, int force, int tcp) 286 { 287 struct in_addr *inaddr, faddr; 288 struct hostent *he; 289 int lcv; 290 struct sockaddr_in sin; 291 CLIENT *ypserv; 292 int yperr; 293 enum clnt_stat retval; 294 struct timeval tv; 295 struct ypresp_maplist yprespmlist; 296 struct ypmaplist fakelist, *ypml; 297 struct ypresp_master yprespmaster; 298 struct ypreq_nokey ypreqkey; 299 300 /* 301 * we can either ask the hosts ypbind where it's ypserv is located, 302 * or we can be forced to assume that ypserv is running on the host. 303 */ 304 if (force) { 305 if (inet_aton(host, &faddr) == 0) { 306 he = gethostbyname(host); 307 if (he == NULL) 308 errx(1, "%s: %s", host, hstrerror(h_errno)); 309 (void)memmove(&faddr, he->h_addr, sizeof(faddr)); 310 } 311 inaddr = &faddr; 312 } else { 313 /* ask host "host" who is currently serving its maps */ 314 inaddr = find_server(host, domain, tcp); 315 } 316 317 /* 318 * now translate nicknames [unless inhibited] 319 */ 320 if (map && !inhibit) { 321 for (lcv = 0; ypnicknames[lcv]; lcv += 2) { 322 if (strcmp(map, ypnicknames[lcv]) == 0) { 323 map = ypnicknames[lcv + 1]; 324 break; 325 } 326 } 327 #ifdef DEBUG 328 (void)printf("translated map name = %s\n", map); 329 #endif 330 } 331 332 /* 333 * now we try and connect to host's ypserv 334 */ 335 (void)memset(&sin, 0, sizeof(sin)); 336 sin.sin_family = AF_INET; 337 sin.sin_addr.s_addr = inaddr->s_addr; 338 ypserv = mkclient(&sin, YPPROG, YPVERS, tcp); 339 if (ypserv == NULL) { 340 warnx("clnt%s_create: %s: %s", tcp ? "tcp" : "udp", host, 341 yperr_string(YPERR_YPSERV)); 342 goto error; 343 } 344 345 /* 346 * did the user specify a map? 347 */ 348 if (map == NULL) { 349 /* 350 * if no map specified, we ask ypserv for a list of all maps 351 */ 352 (void)memset(&yprespmlist, 0, sizeof(yprespmlist)); 353 tv.tv_sec = 5; 354 tv.tv_usec = 0; 355 retval = clnt_call(ypserv, (unsigned int)YPPROC_MAPLIST, 356 xdr_ypdomain_wrap_string, &domain, xdr_ypresp_maplist, 357 &yprespmlist, tv); 358 if (retval != RPC_SUCCESS) { 359 warnx("clnt_call MAPLIST: %s: %s", host, 360 clnt_sperrno(retval)); 361 goto error; 362 } 363 yperr = ypprot_err(yprespmlist.status); 364 if (yperr) { 365 warnx("clnt_call: %s: %s", host, yperr_string(yperr)); 366 goto error; 367 } 368 ypml = yprespmlist.list; 369 } else { 370 /* 371 * build a fake "list" of maps containing only the list the user 372 * asked about in it. 373 */ 374 (void)memset(&fakelist, 0, sizeof(fakelist)); 375 (void)strlcpy(fakelist.ypml_name, map, sizeof(fakelist.ypml_name)); 376 fakelist.ypml_next = NULL; 377 ypml = &fakelist; 378 } 379 380 /* 381 * we now have a list of maps. ask ypserv who is the master for 382 * each map... 383 */ 384 for ( /* null */ ; ypml != NULL; ypml = ypml->ypml_next) { 385 ypreqkey.domain = domain; 386 ypreqkey.map = ypml->ypml_name; 387 (void)memset(&yprespmaster, 0, sizeof(yprespmaster)); 388 tv.tv_sec = 5; 389 tv.tv_usec = 0; 390 retval = clnt_call(ypserv, (unsigned int)YPPROC_MASTER, 391 xdr_ypreq_nokey, &ypreqkey, xdr_ypresp_master, 392 &yprespmaster, tv); 393 if (retval != RPC_SUCCESS) { 394 warnx("clnt_call MASTER: %s: %s", host, 395 clnt_sperrno(retval)); 396 goto error; 397 } 398 yperr = ypprot_err(yprespmaster.status); 399 if (yperr) { 400 warnx("clnt_call: %s: %s: %s", host, ypml->ypml_name, 401 yperr_string(yperr)); 402 } else { 403 (void)printf("%s %s\n", ypml->ypml_name, 404 yprespmaster.master); 405 } 406 xdr_free(xdr_ypresp_master, (void *)&yprespmaster); 407 } 408 clnt_destroy(ypserv); 409 410 /* 411 * done 412 */ 413 return; 414 415 error: 416 /* print host's ypserv's IP address to prevent confusion */ 417 if (ypserv) 418 clnt_destroy(ypserv); 419 if (!force) 420 (void)fprintf(stderr, 421 "\t[note %s's ypserv running on host %s]\n", 422 host, inet_ntoa(*inaddr)); 423 exit(1); 424 } 425