yp_dnslookup.c revision 30827
1/* 2 * Copyright (c) 1995, 1996 3 * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Bill Paul. 16 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#ifndef lint 34static const char rcsid[] = 35 "$Id$"; 36#endif /* not lint */ 37 38/* 39 * Do standard and reverse DNS lookups using the resolver library. 40 * Take care of all the dirty work here so the main program only has to 41 * pass us a pointer to an array of characters. 42 * 43 * We have to use direct resolver calls here otherwise the YP server 44 * could end up looping by calling itself over and over again until 45 * it disappeared up its own belly button. 46 */ 47 48#include <sys/param.h> 49#include <sys/socket.h> 50#include <sys/time.h> 51#include <sys/fcntl.h> 52#include <sys/queue.h> 53#include <netinet/in.h> 54#include <arpa/inet.h> 55#include <arpa/nameser.h> 56 57#include <ctype.h> 58#include <errno.h> 59#include <netdb.h> 60#include <stdio.h> 61#include <stdlib.h> 62#include <string.h> 63#include <resolv.h> 64#include <unistd.h> 65 66#include <rpcsvc/yp.h> 67#include "yp_extern.h" 68 69static char *parse(hp) 70 struct hostent *hp; 71{ 72 static char result[MAXHOSTNAMELEN * 2]; 73 int len,i; 74 struct in_addr addr; 75 76 if (hp == NULL) 77 return(NULL); 78 79 len = 16 + strlen(hp->h_name); 80 for (i = 0; hp->h_aliases[i]; i++) 81 len += strlen(hp->h_aliases[i]) + 1; 82 83 bzero(result, sizeof(result)); 84 85 bcopy(hp->h_addr, &addr, sizeof(struct in_addr)); 86 snprintf(result, sizeof(result), "%s %s", inet_ntoa(addr), hp->h_name); 87 88 for (i = 0; hp->h_aliases[i]; i++) { 89 strcat(result, " "); 90 strcat(result, hp->h_aliases[i]); 91 } 92 93 return ((char *)&result); 94} 95 96#define MAXPACKET 1024 97#define DEF_TTL 50 98 99#define BY_DNS_ID 1 100#define BY_RPC_XID 2 101 102extern struct hostent *__dns_getanswer __P((char *, int, char *, int)); 103 104static CIRCLEQ_HEAD(dns_qhead, circleq_dnsentry) qhead; 105 106struct circleq_dnsentry { 107 SVCXPRT *xprt; 108 unsigned long xid; 109 struct sockaddr_in client_addr; 110 unsigned long ypvers; 111 unsigned long id; 112 unsigned long ttl; 113 unsigned long type; 114 unsigned short prot_type; 115 char **domain; 116 char *name; 117 struct in_addr addr; 118 CIRCLEQ_ENTRY(circleq_dnsentry) links; 119}; 120 121static int pending = 0; 122 123int yp_init_resolver() 124{ 125 CIRCLEQ_INIT(&qhead); 126 if (!(_res.options & RES_INIT) && res_init() == -1) { 127 yp_error("res_init failed"); 128 return(1); 129 } 130 if ((resfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 131 yp_error("couldn't create socket"); 132 return(1); 133 } 134 if (fcntl(resfd, F_SETFL, O_NONBLOCK) == -1) { 135 yp_error("couldn't make resolver socket non-blocking"); 136 return(1); 137 } 138 return(0); 139} 140 141static struct circleq_dnsentry *yp_malloc_dnsent() 142{ 143 register struct circleq_dnsentry *q; 144 145 q = (struct circleq_dnsentry *)malloc(sizeof(struct circleq_dnsentry)); 146 147 if (q == NULL) { 148 yp_error("failed to malloc() circleq dns entry"); 149 return(NULL); 150 } 151 152 return(q); 153} 154 155/* 156 * Transmit a query. 157 */ 158static unsigned long yp_send_dns_query(name, type) 159 char *name; 160 int type; 161{ 162 char buf[MAXPACKET]; 163 int n; 164 HEADER *hptr; 165 int ns; 166 int rval; 167 unsigned long id; 168 169 bzero(buf, sizeof(buf)); 170 171 n = res_mkquery(QUERY,name,C_IN,type,NULL,0,NULL,buf,sizeof(buf)); 172 173 if (n <= 0) { 174 yp_error("res_mkquery failed"); 175 return(0); 176 } 177 178 hptr = (HEADER *)&buf; 179 id = ntohs(hptr->id); 180 181 for (ns = 0; ns < _res.nscount; ns++) { 182 rval = sendto(resfd, buf, n, 0, 183 (struct sockaddr *)&_res.nsaddr_list[ns], 184 sizeof(struct sockaddr)); 185 if (rval == -1) { 186 yp_error("sendto failed"); 187 return(0); 188 } 189 } 190 191 return(id); 192} 193 194static struct circleq_dnsentry *yp_find_dnsqent(id, type) 195 unsigned long id; 196 int type; 197{ 198 register struct circleq_dnsentry *q; 199 200 for (q = qhead.cqh_first; q != (void *)&qhead; q = q->links.cqe_next) { 201 switch(type) { 202 case BY_RPC_XID: 203 if (id == q->xid) 204 return(q); 205 break; 206 case BY_DNS_ID: 207 default: 208 if (id == q->id) 209 return(q); 210 break; 211 } 212 } 213 return (NULL); 214} 215 216static void yp_send_dns_reply(q, buf) 217 struct circleq_dnsentry *q; 218 char *buf; 219{ 220 ypresponse result_v1; 221 ypresp_val result_v2; 222 unsigned long xid; 223 struct sockaddr_in client_addr; 224 xdrproc_t xdrfunc; 225 char *result; 226 227 /* 228 * Set up correct reply struct and 229 * XDR filter depending on ypvers. 230 */ 231 switch(q->ypvers) { 232 case YPVERS: 233 bzero((char *)&result_v2, sizeof(result_v2)); 234 235 if (buf == NULL) 236 result_v2.stat = YP_NOKEY; 237 else { 238 result_v2.val.valdat_len = strlen(buf); 239 result_v2.val.valdat_val = buf; 240 result_v2.stat = YP_TRUE; 241 } 242 result = (char *)&result_v2; 243 xdrfunc = (xdrproc_t)xdr_ypresp_val; 244 break; 245 case YPOLDVERS: 246 /* 247 * The odds are we will _never_ execute this 248 * particular code, but we include it anyway 249 * for the sake of completeness. 250 */ 251 bzero((char *)&result_v1, sizeof(result_v1)); 252 result_v1.yp_resptype = YPRESP_VAL; 253# define YPVAL ypresponse_u.yp_resp_valtype 254 255 if (buf == NULL) 256 result_v1.YPVAL.stat = YP_NOKEY; 257 else { 258 result_v1.YPVAL.val.valdat_len = strlen(buf); 259 result_v1.YPVAL.val.valdat_val = buf; 260 result_v1.YPVAL.stat = YP_TRUE; 261 } 262 result = (char *)&result_v1; 263 xdrfunc = (xdrproc_t)xdr_ypresponse; 264 break; 265 default: 266 yp_error("bad YP program version (%lu)!", q->ypvers); 267 return; 268 break; 269 } 270 271 if (debug) 272 yp_error("sending dns reply to %s (%lu)", 273 inet_ntoa(q->client_addr.sin_addr), q->id); 274 /* 275 * XXX This is disgusting. There's basically one transport 276 * handle for UDP, but we're holding off on replying to a 277 * client until we're ready, by which time we may have received 278 * several other queries from other clients with different 279 * transaction IDs. So to make the delayed response thing work, 280 * we have to save the transaction ID and client address of 281 * each request, then jam them into the transport handle when 282 * we're ready to send a reply. Then after we've send the reply, 283 * we put the old transaction ID and remote address back the 284 * way we found 'em. This is _INCREDIBLY_ non-portable; it's 285 * not even supported by the RPC library. 286 */ 287 /* 288 * XXX Don't frob the transaction ID for TCP handles. 289 */ 290 if (q->prot_type == SOCK_DGRAM) 291 xid = svcudp_set_xid(q->xprt, q->xid); 292 client_addr = q->xprt->xp_raddr; 293 q->xprt->xp_raddr = q->client_addr; 294 295 if (!svc_sendreply(q->xprt, xdrfunc, result)) 296 yp_error("svc_sendreply failed"); 297 298 /* 299 * Now that we sent the reply, 300 * put the handle back the way it was. 301 */ 302 if (q->prot_type == SOCK_DGRAM) 303 svcudp_set_xid(q->xprt, xid); 304 q->xprt->xp_raddr = client_addr; 305 306 return; 307} 308 309/* 310 * Decrement TTL on all queue entries, possibly nuking 311 * any that have been around too long without being serviced. 312 */ 313void yp_prune_dnsq() 314{ 315 register struct circleq_dnsentry *q, *n; 316 317 q = qhead.cqh_first; 318 while(q != (void *)&qhead) { 319 q->ttl--; 320 n = q->links.cqe_next; 321 if (!q->ttl) { 322 CIRCLEQ_REMOVE(&qhead, q, links); 323 free(q->name); 324 free(q); 325 pending--; 326 } 327 q = n; 328 } 329 330 if (pending < 0) 331 pending = 0; 332 333 return; 334} 335 336/* 337 * Data is pending on the DNS socket; check for valid replies 338 * to our queries and dispatch them to waiting clients. 339 */ 340void yp_run_dnsq() 341{ 342 register struct circleq_dnsentry *q; 343 char buf[sizeof(HEADER) + MAXPACKET]; 344 char retrybuf[MAXHOSTNAMELEN]; 345 struct sockaddr_in sin; 346 int rval; 347 int len; 348 HEADER *hptr; 349 struct hostent *hent; 350 351 if (debug) 352 yp_error("running dns queue"); 353 354 bzero(buf, sizeof(buf)); 355 356 len = sizeof(struct sockaddr_in); 357 rval = recvfrom(resfd, buf, sizeof(buf), 0, 358 (struct sockaddr *)&sin, &len); 359 360 if (rval == -1) { 361 yp_error("recvfrom failed: %s", strerror(errno)); 362 return; 363 } 364 365 /* 366 * We may have data left in the socket that represents 367 * replies to earlier queries that we don't care about 368 * anymore. If there are no lookups pending or the packet 369 * ID doesn't match any of the queue IDs, just drop it 370 * on the floor. 371 */ 372 hptr = (HEADER *)&buf; 373 if (!pending || 374 (q = yp_find_dnsqent(ntohs(hptr->id), BY_DNS_ID)) == NULL) { 375 /* ignore */ 376 return; 377 } 378 379 if (debug) 380 yp_error("got dns reply from %s", inet_ntoa(sin.sin_addr)); 381 382 hent = __dns_getanswer(buf, rval, q->name, q->type); 383 384 /* 385 * If the lookup failed, try appending one of the domains 386 * from resolv.conf. If we have no domains to test, the 387 * query has failed. 388 */ 389 if (hent == NULL) { 390 if ((h_errno == TRY_AGAIN || h_errno == NO_RECOVERY) 391 && q->domain && *q->domain) { 392 snprintf(retrybuf, sizeof(retrybuf), "%s.%s", 393 q->name, *q->domain); 394 if (debug) 395 yp_error("retrying with: %s", retrybuf); 396 q->id = yp_send_dns_query(retrybuf, q->type); 397 q->ttl = DEF_TTL; 398 q->domain++; 399 return; 400 } 401 } else { 402 if (q->type == T_PTR) { 403 hent->h_addr = (char *)&q->addr.s_addr; 404 hent->h_length = sizeof(struct in_addr); 405 } 406 } 407 408 /* Got an answer ready for a client -- send it off. */ 409 yp_send_dns_reply(q, parse(hent)); 410 pending--; 411 CIRCLEQ_REMOVE(&qhead, q, links); 412 free(q->name); 413 free(q); 414 415 /* Decrement TTLs on other entries while we're here. */ 416 yp_prune_dnsq(); 417 418 return; 419} 420 421/* 422 * Queue and transmit an asynchronous DNS hostname lookup. 423 */ 424ypstat yp_async_lookup_name(rqstp, name) 425 struct svc_req *rqstp; 426 char *name; 427{ 428 register struct circleq_dnsentry *q; 429 int type, len; 430 431 /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */ 432 type = -1; len = sizeof(type); 433 if (getsockopt(rqstp->rq_xprt->xp_sock, SOL_SOCKET, 434 SO_TYPE, &type, &len) == -1) { 435 yp_error("getsockopt failed: %s", strerror(errno)); 436 return(YP_YPERR); 437 } 438 439 /* Avoid transmitting dupe requests. */ 440 if (type == SOCK_DGRAM && 441 yp_find_dnsqent(svcudp_get_xid(rqstp->rq_xprt),BY_RPC_XID) != NULL) 442 return(YP_TRUE); 443 444 if ((q = yp_malloc_dnsent()) == NULL) 445 return(YP_YPERR); 446 447 q->type = T_A; 448 q->ttl = DEF_TTL; 449 q->xprt = rqstp->rq_xprt; 450 q->ypvers = rqstp->rq_vers; 451 q->prot_type = type; 452 if (q->prot_type == SOCK_DGRAM) 453 q->xid = svcudp_get_xid(q->xprt); 454 q->client_addr = q->xprt->xp_raddr; 455 if (!strchr(name, '.')) 456 q->domain = _res.dnsrch; 457 else 458 q->domain = NULL; 459 q->id = yp_send_dns_query(name, q->type); 460 461 if (q->id == 0) { 462 yp_error("DNS query failed"); 463 free(q); 464 return(YP_YPERR); 465 } 466 467 q->name = strdup(name); 468 CIRCLEQ_INSERT_HEAD(&qhead, q, links); 469 pending++; 470 471 if (debug) 472 yp_error("queueing async DNS name lookup (%d)", q->id); 473 474 yp_prune_dnsq(); 475 return(YP_TRUE); 476} 477 478/* 479 * Queue and transmit an asynchronous DNS IP address lookup. 480 */ 481ypstat yp_async_lookup_addr(rqstp, addr) 482 struct svc_req *rqstp; 483 char *addr; 484{ 485 register struct circleq_dnsentry *q; 486 char buf[MAXHOSTNAMELEN]; 487 int a, b, c, d; 488 int type, len; 489 490 /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */ 491 type = -1; len = sizeof(type); 492 if (getsockopt(rqstp->rq_xprt->xp_sock, SOL_SOCKET, 493 SO_TYPE, &type, &len) == -1) { 494 yp_error("getsockopt failed: %s", strerror(errno)); 495 return(YP_YPERR); 496 } 497 498 /* Avoid transmitting dupe requests. */ 499 if (type == SOCK_DGRAM && 500 yp_find_dnsqent(svcudp_get_xid(rqstp->rq_xprt),BY_RPC_XID) != NULL) 501 return(YP_TRUE); 502 503 if ((q = yp_malloc_dnsent()) == NULL) 504 return(YP_YPERR); 505 506 if (sscanf(addr, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) 507 return(YP_NOKEY); 508 509 snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", d, c, b, a); 510 511 if (debug) 512 yp_error("DNS address is: %s", buf); 513 514 q->type = T_PTR; 515 q->ttl = DEF_TTL; 516 q->xprt = rqstp->rq_xprt; 517 q->ypvers = rqstp->rq_vers; 518 q->domain = NULL; 519 q->prot_type = type; 520 if (q->prot_type == SOCK_DGRAM) 521 q->xid = svcudp_get_xid(q->xprt); 522 q->client_addr = q->xprt->xp_raddr; 523 q->id = yp_send_dns_query(buf, q->type); 524 525 if (q->id == 0) { 526 yp_error("DNS query failed"); 527 free(q); 528 return(YP_YPERR); 529 } 530 531 inet_aton(addr, &q->addr); 532 q->name = strdup(buf); 533 CIRCLEQ_INSERT_HEAD(&qhead, q, links); 534 pending++; 535 536 if (debug) 537 yp_error("queueing async DNS address lookup (%d)", q->id); 538 539 yp_prune_dnsq(); 540 return(YP_TRUE); 541} 542