1 /* $NetBSD: lwresutil.c,v 1.5 2014/12/10 04:38:02 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2004, 2005, 2007, 2014 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 2000, 2001 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* Id: lwresutil.c,v 1.34 2007/06/19 23:47:22 tbox Exp */ 21 22 /*! \file */ 23 24 /** 25 * lwres_string_parse() retrieves a DNS-encoded string starting the 26 * current pointer of lightweight resolver buffer b: i.e. b->current. 27 * When the function returns, the address of the first byte of the 28 * encoded string is returned via *c and the length of that string is 29 * given by *len. The buffer's current pointer is advanced to point at 30 * the character following the string length, the encoded string, and 31 * the trailing NULL character. 32 * 33 * lwres_addr_parse() extracts an address from the buffer b. The 34 * buffer's current pointer b->current is presumed to point at an 35 * encoded address: the address preceded by a 32-bit protocol family 36 * identifier and a 16-bit length field. The encoded address is copied 37 * to addr->address and addr->length indicates the size in bytes of 38 * the address that was copied. b->current is advanced to point at the 39 * next byte of available data in the buffer following the encoded 40 * address. 41 * 42 * lwres_getaddrsbyname() and lwres_getnamebyaddr() use the 43 * lwres_gnbaresponse_t structure defined below: 44 * 45 * \code 46 * typedef struct { 47 * lwres_uint32_t flags; 48 * lwres_uint16_t naliases; 49 * lwres_uint16_t naddrs; 50 * char *realname; 51 * char **aliases; 52 * lwres_uint16_t realnamelen; 53 * lwres_uint16_t *aliaslen; 54 * lwres_addrlist_t addrs; 55 * void *base; 56 * size_t baselen; 57 * } lwres_gabnresponse_t; 58 * \endcode 59 * 60 * The contents of this structure are not manipulated directly but 61 * they are controlled through the \link lwres_gabn.c lwres_gabn*\endlink functions. 62 * 63 * The lightweight resolver uses lwres_getaddrsbyname() to perform 64 * foward lookups. Hostname name is looked up using the resolver 65 * context ctx for memory allocation. addrtypes is a bitmask 66 * indicating which type of addresses are to be looked up. Current 67 * values for this bitmask are #LWRES_ADDRTYPE_V4 for IPv4 addresses 68 * and #LWRES_ADDRTYPE_V6 for IPv6 addresses. Results of the lookup are 69 * returned in *structp. 70 * 71 * lwres_getnamebyaddr() performs reverse lookups. Resolver context 72 * ctx is used for memory allocation. The address type is indicated by 73 * addrtype: #LWRES_ADDRTYPE_V4 or #LWRES_ADDRTYPE_V6. The address to be 74 * looked up is given by addr and its length is addrlen bytes. The 75 * result of the function call is made available through *structp. 76 * 77 * \section lwresutil_return Return Values 78 * 79 * Successful calls to lwres_string_parse() and lwres_addr_parse() 80 * return #LWRES_R_SUCCESS. Both functions return #LWRES_R_FAILURE if 81 * the buffer is corrupt or #LWRES_R_UNEXPECTEDEND if the buffer has 82 * less space than expected for the components of the encoded string 83 * or address. 84 * 85 * lwres_getaddrsbyname() returns #LWRES_R_SUCCESS on success and it 86 * returns #LWRES_R_NOTFOUND if the hostname name could not be found. 87 * 88 * #LWRES_R_SUCCESS is returned by a successful call to 89 * lwres_getnamebyaddr(). 90 * 91 * Both lwres_getaddrsbyname() and lwres_getnamebyaddr() return 92 * #LWRES_R_NOMEMORY when memory allocation requests fail and 93 * #LWRES_R_UNEXPECTEDEND if the buffers used for sending queries and 94 * receiving replies are too small. 95 * 96 * \section lwresutil_see See Also 97 * 98 * lwbuffer.c, lwres_gabn.c 99 */ 100 101 #include <config.h> 102 103 #include <assert.h> 104 #include <stdlib.h> 105 #include <string.h> 106 #include <unistd.h> 107 108 #include <lwres/lwbuffer.h> 109 #include <lwres/lwres.h> 110 #include <lwres/result.h> 111 112 #include "assert_p.h" 113 #include "context_p.h" 114 115 /*% Parse data. */ 116 /*! 117 * Requires: 118 * 119 * The "current" pointer in "b" points to encoded raw data. 120 * 121 * Ensures: 122 * 123 * The address of the first byte of the data is returned via "p", 124 * and the length is returned via "len". If NULL, they are not 125 * set. 126 * 127 * On return, the current pointer of "b" will point to the character 128 * following the data length and the data. 129 * 130 */ 131 lwres_result_t 132 lwres_data_parse(lwres_buffer_t *b, unsigned char **p, lwres_uint16_t *len) 133 { 134 lwres_uint16_t datalen; 135 unsigned char *data; 136 137 REQUIRE(b != NULL); 138 139 /* 140 * Pull off the length (2 bytes) 141 */ 142 if (!SPACE_REMAINING(b, 2)) 143 return (LWRES_R_UNEXPECTEDEND); 144 datalen = lwres_buffer_getuint16(b); 145 146 /* 147 * Set the pointer to this string to the right place, then 148 * advance the buffer pointer. 149 */ 150 if (!SPACE_REMAINING(b, datalen)) 151 return (LWRES_R_UNEXPECTEDEND); 152 data = b->base + b->current; 153 lwres_buffer_forward(b, datalen); 154 155 if (len != NULL) 156 *len = datalen; 157 if (p != NULL) 158 *p = data; 159 160 return (LWRES_R_SUCCESS); 161 } 162 163 /*% Retrieves a DNS-encoded string. */ 164 /*! 165 * Requires: 166 * 167 * The "current" pointer in "b" point to an encoded string. 168 * 169 * Ensures: 170 * 171 * The address of the first byte of the string is returned via "c", 172 * and the length is returned via "len". If NULL, they are not 173 * set. 174 * 175 * On return, the current pointer of "b" will point to the character 176 * following the string length, the string, and the trailing NULL. 177 * 178 */ 179 lwres_result_t 180 lwres_string_parse(lwres_buffer_t *b, char **c, lwres_uint16_t *len) 181 { 182 lwres_uint16_t datalen; 183 char *string; 184 185 REQUIRE(b != NULL); 186 187 /* 188 * Pull off the length (2 bytes) 189 */ 190 if (!SPACE_REMAINING(b, 2)) 191 return (LWRES_R_UNEXPECTEDEND); 192 datalen = lwres_buffer_getuint16(b); 193 194 /* 195 * Set the pointer to this string to the right place, then 196 * advance the buffer pointer. 197 */ 198 if (!SPACE_REMAINING(b, datalen)) 199 return (LWRES_R_UNEXPECTEDEND); 200 string = (char *)b->base + b->current; 201 lwres_buffer_forward(b, datalen); 202 203 /* 204 * Skip the "must be zero" byte. 205 */ 206 if (!SPACE_REMAINING(b, 1)) 207 return (LWRES_R_UNEXPECTEDEND); 208 if (0 != lwres_buffer_getuint8(b)) 209 return (LWRES_R_FAILURE); 210 211 if (len != NULL) 212 *len = datalen; 213 if (c != NULL) 214 *c = string; 215 216 return (LWRES_R_SUCCESS); 217 } 218 219 /*% Extracts an address from the buffer b. */ 220 lwres_result_t 221 lwres_addr_parse(lwres_buffer_t *b, lwres_addr_t *addr) 222 { 223 REQUIRE(addr != NULL); 224 225 if (!SPACE_REMAINING(b, 6)) 226 return (LWRES_R_UNEXPECTEDEND); 227 228 addr->family = lwres_buffer_getuint32(b); 229 addr->length = lwres_buffer_getuint16(b); 230 231 if (!SPACE_REMAINING(b, addr->length)) 232 return (LWRES_R_UNEXPECTEDEND); 233 if (addr->length > LWRES_ADDR_MAXLEN) 234 return (LWRES_R_FAILURE); 235 236 lwres_buffer_getmem(b, addr->address, addr->length); 237 238 return (LWRES_R_SUCCESS); 239 } 240 241 /*% Used to perform forward lookups. */ 242 lwres_result_t 243 lwres_getaddrsbyname(lwres_context_t *ctx, const char *name, 244 lwres_uint32_t addrtypes, lwres_gabnresponse_t **structp) 245 { 246 lwres_gabnrequest_t request; 247 lwres_gabnresponse_t *response; 248 int ret; 249 int recvlen; 250 lwres_buffer_t b_in, b_out; 251 lwres_lwpacket_t pkt; 252 lwres_uint32_t serial; 253 char *buffer; 254 char target_name[1024]; 255 unsigned int target_length; 256 257 REQUIRE(ctx != NULL); 258 REQUIRE(name != NULL); 259 REQUIRE(addrtypes != 0); 260 REQUIRE(structp != NULL && *structp == NULL); 261 262 b_in.base = NULL; 263 b_out.base = NULL; 264 response = NULL; 265 buffer = NULL; 266 serial = lwres_context_nextserial(ctx); 267 268 buffer = CTXMALLOC(LWRES_RECVLENGTH); 269 if (buffer == NULL) { 270 ret = LWRES_R_NOMEMORY; 271 goto out; 272 } 273 274 target_length = strlen(name); 275 if (target_length >= sizeof(target_name)) 276 return (LWRES_R_FAILURE); 277 strcpy(target_name, name); /* strcpy is safe */ 278 279 /* 280 * Set up our request and render it to a buffer. 281 */ 282 request.flags = 0; 283 request.addrtypes = addrtypes; 284 request.name = target_name; 285 request.namelen = target_length; 286 pkt.pktflags = 0; 287 pkt.serial = serial; 288 pkt.result = 0; 289 pkt.recvlength = LWRES_RECVLENGTH; 290 291 again: 292 ret = lwres_gabnrequest_render(ctx, &request, &pkt, &b_out); 293 if (ret != LWRES_R_SUCCESS) 294 goto out; 295 296 ret = lwres_context_sendrecv(ctx, b_out.base, b_out.length, buffer, 297 LWRES_RECVLENGTH, &recvlen); 298 if (ret != LWRES_R_SUCCESS) 299 goto out; 300 301 lwres_buffer_init(&b_in, buffer, recvlen); 302 b_in.used = recvlen; 303 304 /* 305 * Parse the packet header. 306 */ 307 ret = lwres_lwpacket_parseheader(&b_in, &pkt); 308 if (ret != LWRES_R_SUCCESS) 309 goto out; 310 311 /* 312 * Sanity check. 313 */ 314 if (pkt.serial != serial) 315 goto again; 316 if (pkt.opcode != LWRES_OPCODE_GETADDRSBYNAME) 317 goto again; 318 319 /* 320 * Free what we've transmitted 321 */ 322 CTXFREE(b_out.base, b_out.length); 323 b_out.base = NULL; 324 b_out.length = 0; 325 326 if (pkt.result != LWRES_R_SUCCESS) { 327 ret = pkt.result; 328 goto out; 329 } 330 331 /* 332 * Parse the response. 333 */ 334 ret = lwres_gabnresponse_parse(ctx, &b_in, &pkt, &response); 335 if (ret != LWRES_R_SUCCESS) 336 goto out; 337 response->base = buffer; 338 response->baselen = LWRES_RECVLENGTH; 339 buffer = NULL; /* don't free this below */ 340 341 *structp = response; 342 return (LWRES_R_SUCCESS); 343 344 out: 345 if (b_out.base != NULL) 346 CTXFREE(b_out.base, b_out.length); 347 if (buffer != NULL) 348 CTXFREE(buffer, LWRES_RECVLENGTH); 349 if (response != NULL) 350 lwres_gabnresponse_free(ctx, &response); 351 352 return (ret); 353 } 354 355 356 /*% Used to perform reverse lookups. */ 357 lwres_result_t 358 lwres_getnamebyaddr(lwres_context_t *ctx, lwres_uint32_t addrtype, 359 lwres_uint16_t addrlen, const unsigned char *addr, 360 lwres_gnbaresponse_t **structp) 361 { 362 lwres_gnbarequest_t request; 363 lwres_gnbaresponse_t *response; 364 int ret; 365 int recvlen; 366 lwres_buffer_t b_in, b_out; 367 lwres_lwpacket_t pkt; 368 lwres_uint32_t serial; 369 char *buffer; 370 371 REQUIRE(ctx != NULL); 372 REQUIRE(addrtype != 0); 373 REQUIRE(addrlen != 0); 374 REQUIRE(addr != NULL); 375 REQUIRE(structp != NULL && *structp == NULL); 376 377 b_in.base = NULL; 378 b_out.base = NULL; 379 response = NULL; 380 buffer = NULL; 381 serial = lwres_context_nextserial(ctx); 382 383 buffer = CTXMALLOC(LWRES_RECVLENGTH); 384 if (buffer == NULL) { 385 ret = LWRES_R_NOMEMORY; 386 goto out; 387 } 388 389 /* 390 * Set up our request and render it to a buffer. 391 */ 392 request.flags = 0; 393 request.addr.family = addrtype; 394 request.addr.length = addrlen; 395 memmove(request.addr.address, addr, addrlen); 396 pkt.pktflags = 0; 397 pkt.serial = serial; 398 pkt.result = 0; 399 pkt.recvlength = LWRES_RECVLENGTH; 400 401 again: 402 ret = lwres_gnbarequest_render(ctx, &request, &pkt, &b_out); 403 if (ret != LWRES_R_SUCCESS) 404 goto out; 405 406 ret = lwres_context_sendrecv(ctx, b_out.base, b_out.length, buffer, 407 LWRES_RECVLENGTH, &recvlen); 408 if (ret != LWRES_R_SUCCESS) 409 goto out; 410 411 lwres_buffer_init(&b_in, buffer, recvlen); 412 b_in.used = recvlen; 413 414 /* 415 * Parse the packet header. 416 */ 417 ret = lwres_lwpacket_parseheader(&b_in, &pkt); 418 if (ret != LWRES_R_SUCCESS) 419 goto out; 420 421 /* 422 * Sanity check. 423 */ 424 if (pkt.serial != serial) 425 goto again; 426 if (pkt.opcode != LWRES_OPCODE_GETNAMEBYADDR) 427 goto again; 428 429 /* 430 * Free what we've transmitted 431 */ 432 CTXFREE(b_out.base, b_out.length); 433 b_out.base = NULL; 434 b_out.length = 0; 435 436 if (pkt.result != LWRES_R_SUCCESS) { 437 ret = pkt.result; 438 goto out; 439 } 440 441 /* 442 * Parse the response. 443 */ 444 ret = lwres_gnbaresponse_parse(ctx, &b_in, &pkt, &response); 445 if (ret != LWRES_R_SUCCESS) 446 goto out; 447 response->base = buffer; 448 response->baselen = LWRES_RECVLENGTH; 449 buffer = NULL; /* don't free this below */ 450 451 *structp = response; 452 return (LWRES_R_SUCCESS); 453 454 out: 455 if (b_out.base != NULL) 456 CTXFREE(b_out.base, b_out.length); 457 if (buffer != NULL) 458 CTXFREE(buffer, LWRES_RECVLENGTH); 459 if (response != NULL) 460 lwres_gnbaresponse_free(ctx, &response); 461 462 return (ret); 463 } 464 465 /*% Get rdata by name. */ 466 lwres_result_t 467 lwres_getrdatabyname(lwres_context_t *ctx, const char *name, 468 lwres_uint16_t rdclass, lwres_uint16_t rdtype, 469 lwres_uint32_t flags, lwres_grbnresponse_t **structp) 470 { 471 int ret; 472 int recvlen; 473 lwres_buffer_t b_in, b_out; 474 lwres_lwpacket_t pkt; 475 lwres_uint32_t serial; 476 char *buffer; 477 lwres_grbnrequest_t request; 478 lwres_grbnresponse_t *response; 479 char target_name[1024]; 480 unsigned int target_length; 481 482 REQUIRE(ctx != NULL); 483 REQUIRE(name != NULL); 484 REQUIRE(structp != NULL && *structp == NULL); 485 486 b_in.base = NULL; 487 b_out.base = NULL; 488 response = NULL; 489 buffer = NULL; 490 serial = lwres_context_nextserial(ctx); 491 492 buffer = CTXMALLOC(LWRES_RECVLENGTH); 493 if (buffer == NULL) { 494 ret = LWRES_R_NOMEMORY; 495 goto out; 496 } 497 498 target_length = strlen(name); 499 if (target_length >= sizeof(target_name)) 500 return (LWRES_R_FAILURE); 501 strcpy(target_name, name); /* strcpy is safe */ 502 503 /* 504 * Set up our request and render it to a buffer. 505 */ 506 request.rdclass = rdclass; 507 request.rdtype = rdtype; 508 request.flags = flags; 509 request.name = target_name; 510 request.namelen = target_length; 511 pkt.pktflags = 0; 512 pkt.serial = serial; 513 pkt.result = 0; 514 pkt.recvlength = LWRES_RECVLENGTH; 515 516 again: 517 ret = lwres_grbnrequest_render(ctx, &request, &pkt, &b_out); 518 if (ret != LWRES_R_SUCCESS) 519 goto out; 520 521 ret = lwres_context_sendrecv(ctx, b_out.base, b_out.length, buffer, 522 LWRES_RECVLENGTH, &recvlen); 523 if (ret != LWRES_R_SUCCESS) 524 goto out; 525 526 lwres_buffer_init(&b_in, buffer, recvlen); 527 b_in.used = recvlen; 528 529 /* 530 * Parse the packet header. 531 */ 532 ret = lwres_lwpacket_parseheader(&b_in, &pkt); 533 if (ret != LWRES_R_SUCCESS) 534 goto out; 535 536 /* 537 * Sanity check. 538 */ 539 if (pkt.serial != serial) 540 goto again; 541 if (pkt.opcode != LWRES_OPCODE_GETRDATABYNAME) 542 goto again; 543 544 /* 545 * Free what we've transmitted 546 */ 547 CTXFREE(b_out.base, b_out.length); 548 b_out.base = NULL; 549 b_out.length = 0; 550 551 if (pkt.result != LWRES_R_SUCCESS) { 552 ret = pkt.result; 553 goto out; 554 } 555 556 /* 557 * Parse the response. 558 */ 559 ret = lwres_grbnresponse_parse(ctx, &b_in, &pkt, &response); 560 if (ret != LWRES_R_SUCCESS) 561 goto out; 562 response->base = buffer; 563 response->baselen = LWRES_RECVLENGTH; 564 buffer = NULL; /* don't free this below */ 565 566 *structp = response; 567 return (LWRES_R_SUCCESS); 568 569 out: 570 if (b_out.base != NULL) 571 CTXFREE(b_out.base, b_out.length); 572 if (buffer != NULL) 573 CTXFREE(buffer, LWRES_RECVLENGTH); 574 if (response != NULL) 575 lwres_grbnresponse_free(ctx, &response); 576 577 return (ret); 578 } 579