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 * $Id: yp_dnslookup.c,v 1.9 1996/12/25 17:52:35 wpaul Exp $ 33 */ 34 35 /* 36 * Do standard and reverse DNS lookups using the resolver library. 37 * Take care of all the dirty work here so the main program only has to 38 * pass us a pointer to an array of characters. 39 * 40 * We have to use direct resolver calls here otherwise the YP server 41 * could end up looping by calling itself over and over again until 42 * it disappeared up its own belly button. 43 */ 44 45 #include <sys/param.h> 46 #include <sys/socket.h> 47 #include <sys/time.h> 48 #include <sys/fcntl.h> 49 #include <sys/queue.h> 50 #include <netinet/in.h> 51 #include <arpa/inet.h> 52 #include <arpa/nameser.h> 53 54 #include <stdio.h> 55 #include <ctype.h> 56 #include <resolv.h> 57 #include <netdb.h> 58 #include <unistd.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <errno.h> 62 #include <err.h> 63 64 #include <rpcsvc/yp.h> 65 #include "yp_extern.h" 66 67 #ifndef lint 68 static const char rcsid[] = "$Id: yp_dnslookup.c,v 1.9 1996/12/25 17:52:35 wpaul Exp $"; 69 #endif 70 71 static char *parse(hp) 72 struct hostent *hp; 73 { 74 static char result[MAXHOSTNAMELEN * 2]; 75 int len,i; 76 struct in_addr addr; 77 78 if (hp == NULL) 79 return(NULL); 80 81 len = 16 + strlen(hp->h_name); 82 for (i = 0; hp->h_aliases[i]; i++) 83 len += strlen(hp->h_aliases[i]) + 1; 84 85 bzero(result, sizeof(result)); 86 87 bcopy(hp->h_addr, &addr, sizeof(struct in_addr)); 88 snprintf(result, sizeof(result), "%s %s", inet_ntoa(addr), hp->h_name); 89 90 for (i = 0; hp->h_aliases[i]; i++) { 91 strcat(result, " "); 92 strcat(result, hp->h_aliases[i]); 93 } 94 95 return ((char *)&result); 96 } 97 98 #define MAXPACKET 1024 99 #define DEF_TTL 50 100 101 extern struct hostent *__dns_getanswer __P((char *, int, char *, int)); 102 103 static CIRCLEQ_HEAD(dns_qhead, circleq_dnsentry) qhead; 104 105 struct circleq_dnsentry { 106 SVCXPRT *xprt; 107 unsigned long xid; 108 struct sockaddr_in client_addr; 109 unsigned long ypvers; 110 unsigned long id; 111 unsigned long ttl; 112 unsigned long type; 113 unsigned short prot_type; 114 char **domain; 115 char *name; 116 struct in_addr addr; 117 CIRCLEQ_ENTRY(circleq_dnsentry) links; 118 }; 119 120 static int pending = 0; 121 122 int yp_init_resolver() 123 { 124 CIRCLEQ_INIT(&qhead); 125 if (!(_res.options & RES_INIT) && res_init() == -1) { 126 yp_error("res_init failed"); 127 return(1); 128 } 129 if ((resfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 130 yp_error("couldn't create socket"); 131 return(1); 132 } 133 if (fcntl(resfd, F_SETFL, O_NONBLOCK) == -1) { 134 yp_error("couldn't make resolver socket non-blocking"); 135 return(1); 136 } 137 return(0); 138 } 139 140 static struct circleq_dnsentry *yp_malloc_dnsent() 141 { 142 register struct circleq_dnsentry *q; 143 144 q = (struct circleq_dnsentry *)malloc(sizeof(struct circleq_dnsentry)); 145 146 if (q == NULL) { 147 yp_error("failed to malloc() circleq dns entry: %s", 148 strerror(errno)); 149 return(NULL); 150 } 151 152 return(q); 153 } 154 155 /* 156 * Transmit a query. 157 */ 158 static 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 194 static struct circleq_dnsentry *yp_find_dnsqent(id) 195 unsigned long id; 196 { 197 register struct circleq_dnsentry *q; 198 199 for (q = qhead.cqh_first; q != (void *)&qhead; q = q->links.cqe_next) { 200 if (id == q->id) 201 return(q); 202 } 203 return (NULL); 204 } 205 206 static void yp_send_dns_reply(q, buf) 207 struct circleq_dnsentry *q; 208 char *buf; 209 { 210 ypresponse result_v1; 211 ypresp_val result_v2; 212 unsigned long xid; 213 struct sockaddr_in client_addr; 214 xdrproc_t xdrfunc; 215 char *result; 216 217 /* 218 * Set up correct reply struct and 219 * XDR filter depending on ypvers. 220 */ 221 switch(q->ypvers) { 222 case YPVERS: 223 bzero((char *)&result_v2, sizeof(result_v2)); 224 225 if (buf == NULL) 226 result_v2.stat = YP_NOKEY; 227 else { 228 result_v2.val.valdat_len = strlen(buf); 229 result_v2.val.valdat_val = buf; 230 result_v2.stat = YP_TRUE; 231 } 232 result = (char *)&result_v2; 233 xdrfunc = (xdrproc_t)xdr_ypresp_val; 234 break; 235 case YPOLDVERS: 236 /* 237 * The odds are we will _never_ execute this 238 * particular code, but we include it anyway 239 * for the sake of completeness. 240 */ 241 bzero((char *)&result_v1, sizeof(result_v1)); 242 result_v1.yp_resptype = YPRESP_VAL; 243 # define YPVAL ypresponse_u.yp_resp_valtype 244 245 if (buf == NULL) 246 result_v1.YPVAL.stat = YP_NOKEY; 247 else { 248 result_v1.YPVAL.val.valdat_len = strlen(buf); 249 result_v1.YPVAL.val.valdat_val = buf; 250 result_v1.YPVAL.stat = YP_TRUE; 251 } 252 result = (char *)&result_v1; 253 xdrfunc = (xdrproc_t)xdr_ypresponse; 254 break; 255 default: 256 yp_error("Bad YP program version (%lu)!",q->ypvers); 257 return; 258 break; 259 } 260 261 if (debug) 262 yp_error("Sending dns reply to %s (%lu)", 263 inet_ntoa(q->client_addr.sin_addr), q->id); 264 /* 265 * XXX This is disgusting. There's basically one transport 266 * handle for UDP, but we're holding off on replying to a 267 * client until we're ready, by which time we may have received 268 * several other queries from other clients with different 269 * transaction IDs. So to make the delayed response thing work, 270 * we have to save the transaction ID and client address of 271 * each request, then jam them into the transport handle when 272 * we're ready to send a reply. Then after we've send the reply, 273 * we put the old transaction ID and remote address back the 274 * way we found 'em. This is _INCREDIBLY_ non-portable; it's 275 * not even supported by the RPC library. 276 */ 277 /* 278 * XXX Don't frob the transaction ID for TCP handles. 279 */ 280 if (q->prot_type == SOCK_DGRAM) 281 xid = svcudp_set_xid(q->xprt, q->xid); 282 client_addr = q->xprt->xp_raddr; 283 q->xprt->xp_raddr = q->client_addr; 284 285 if (!svc_sendreply(q->xprt, xdrfunc, result)) 286 yp_error("svc_sendreply failed"); 287 288 /* 289 * Now that we sent the reply, 290 * put the handle back the way it was. 291 */ 292 if (q->prot_type == SOCK_DGRAM) 293 svcudp_set_xid(q->xprt, xid); 294 q->xprt->xp_raddr = client_addr; 295 296 return; 297 } 298 299 /* 300 * Decrement TTL on all queue entries, possibly nuking 301 * any that have been around too long without being serviced. 302 */ 303 void yp_prune_dnsq() 304 { 305 register struct circleq_dnsentry *q; 306 307 for (q = qhead.cqh_first; q != (void *)&qhead; q = q->links.cqe_next) { 308 q->ttl--; 309 if (!q->ttl) { 310 CIRCLEQ_REMOVE(&qhead, q, links); 311 free(q->name); 312 free(q); 313 pending--; 314 } 315 } 316 317 if (pending < 0) 318 pending = 0; 319 320 return; 321 } 322 323 /* 324 * Data is pending on the DNS socket; check for valid replies 325 * to our queries and dispatch them to waiting clients. 326 */ 327 void yp_run_dnsq() 328 { 329 register struct circleq_dnsentry *q; 330 char buf[sizeof(HEADER) + MAXPACKET]; 331 char retrybuf[MAXHOSTNAMELEN]; 332 struct sockaddr_in sin; 333 int rval; 334 int len; 335 HEADER *hptr; 336 struct hostent *hent; 337 338 if (debug) 339 yp_error("Running dns queue"); 340 341 bzero(buf, sizeof(buf)); 342 343 len = sizeof(struct sockaddr_in); 344 rval = recvfrom(resfd, buf, sizeof(buf), 0, 345 (struct sockaddr *)&sin, &len); 346 347 if (rval == -1) { 348 yp_error("recvfrom failed: %s", strerror(errno)); 349 return; 350 } 351 352 /* 353 * We may have data left in the socket that represents 354 * replies to earlier queries that we don't care about 355 * anymore. If there are no lookups pending or the packet 356 * ID doesn't match any of the queue IDs, just drop it 357 * on the floor. 358 */ 359 hptr = (HEADER *)&buf; 360 if (!pending || (q = yp_find_dnsqent(ntohs(hptr->id))) == NULL) { 361 /* ignore */ 362 return; 363 } 364 365 if (debug) 366 yp_error("Got dns reply from %s", inet_ntoa(sin.sin_addr)); 367 368 hent = __dns_getanswer(buf, rval, q->name, q->type); 369 370 /* 371 * If the lookup failed, try appending one of the domains 372 * from resolv.conf. If we have no domains to test, the 373 * query has failed. 374 */ 375 if (hent == NULL) { 376 if (h_errno == TRY_AGAIN && q->domain && *q->domain) { 377 snprintf(retrybuf, sizeof(retrybuf), "%s.%s", 378 q->name, *q->domain); 379 if (debug) 380 yp_error("Retrying with: %s", retrybuf); 381 q->id = yp_send_dns_query(retrybuf, q->type); 382 q->ttl = DEF_TTL; 383 q->domain++; 384 return; 385 } 386 } else { 387 if (q->type == T_PTR) { 388 hent->h_addr = (char *)&q->addr.s_addr; 389 hent->h_length = sizeof(struct in_addr); 390 } 391 } 392 393 /* Got an answer ready for a client -- send it off. */ 394 yp_send_dns_reply(q, parse(hent)); 395 pending--; 396 CIRCLEQ_REMOVE(&qhead, q, links); 397 free(q->name); 398 free(q); 399 400 /* Decrement TTLs on other entries while we're here. */ 401 yp_prune_dnsq(); 402 403 return; 404 } 405 406 /* 407 * Queue and transmit an asynchronous DNS hostname lookup. 408 */ 409 ypstat yp_async_lookup_name(rqstp, name) 410 struct svc_req *rqstp; 411 char *name; 412 { 413 register struct circleq_dnsentry *q; 414 int type, len; 415 416 if ((q = yp_malloc_dnsent()) == NULL) 417 return(YP_YPERR); 418 419 q->type = T_A; 420 q->ttl = DEF_TTL; 421 q->xprt = rqstp->rq_xprt; 422 q->ypvers = rqstp->rq_vers; 423 type = -1; len = sizeof(type); 424 if (getsockopt(q->xprt->xp_sock,SOL_SOCKET,SO_TYPE,&type,&len) == -1) { 425 yp_error("getsockopt failed: %s", strerror(errno)); 426 return(YP_YPERR); 427 } 428 q->prot_type = type; 429 if (q->prot_type == SOCK_DGRAM) 430 q->xid = svcudp_get_xid(q->xprt); 431 q->client_addr = q->xprt->xp_raddr; 432 if (!strchr(name, '.')) 433 q->domain = _res.dnsrch; 434 else 435 q->domain = NULL; 436 q->id = yp_send_dns_query(name, q->type); 437 438 if (q->id == 0) { 439 yp_error("DNS query failed"); 440 free(q); 441 return(YP_YPERR); 442 } 443 444 q->name = strdup(name); 445 CIRCLEQ_INSERT_HEAD(&qhead, q, links); 446 pending++; 447 448 if (debug) 449 yp_error("Queueing async DNS name lookup (%d)", q->id); 450 451 return(YP_TRUE); 452 } 453 454 /* 455 * Queue and transmit an asynchronous DNS IP address lookup. 456 */ 457 ypstat yp_async_lookup_addr(rqstp, addr) 458 struct svc_req *rqstp; 459 char *addr; 460 { 461 register struct circleq_dnsentry *q; 462 char buf[MAXHOSTNAMELEN]; 463 int a, b, c, d; 464 int type, len; 465 466 if ((q = yp_malloc_dnsent()) == NULL) 467 return(YP_YPERR); 468 469 if (sscanf(addr, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) 470 return(YP_NOKEY); 471 472 snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", 473 d, c, b, a, addr); 474 475 if (debug) 476 yp_error("DNS address is: %s", buf); 477 478 q->type = T_PTR; 479 q->ttl = DEF_TTL; 480 q->xprt = rqstp->rq_xprt; 481 q->ypvers = rqstp->rq_vers; 482 q->domain = NULL; 483 type = -1; len = sizeof(type); 484 if (getsockopt(q->xprt->xp_sock,SOL_SOCKET,SO_TYPE,&type,&len) == -1) { 485 yp_error("getsockopt failed: %s", strerror(errno)); 486 return(YP_YPERR); 487 } 488 q->prot_type = type; 489 if (q->prot_type == SOCK_DGRAM) 490 q->xid = svcudp_get_xid(q->xprt); 491 q->client_addr = q->xprt->xp_raddr; 492 q->id = yp_send_dns_query(buf, q->type); 493 494 if (q->id == 0) { 495 yp_error("DNS query failed"); 496 free(q); 497 return(YP_YPERR); 498 } 499 500 inet_aton(addr, &q->addr); 501 q->name = strdup(buf); 502 CIRCLEQ_INSERT_HEAD(&qhead, q, links); 503 pending++; 504 505 if (debug) 506 yp_error("Queueing async DNS address lookup (%d)", q->id); 507 508 return(YP_TRUE); 509 } 510