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
lwres_data_parse(lwres_buffer_t * b,unsigned char ** p,lwres_uint16_t * len)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
lwres_string_parse(lwres_buffer_t * b,char ** c,lwres_uint16_t * len)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
lwres_addr_parse(lwres_buffer_t * b,lwres_addr_t * addr)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
lwres_getaddrsbyname(lwres_context_t * ctx,const char * name,lwres_uint32_t addrtypes,lwres_gabnresponse_t ** structp)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
lwres_getnamebyaddr(lwres_context_t * ctx,lwres_uint32_t addrtype,lwres_uint16_t addrlen,const unsigned char * addr,lwres_gnbaresponse_t ** structp)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
lwres_getrdatabyname(lwres_context_t * ctx,const char * name,lwres_uint16_t rdclass,lwres_uint16_t rdtype,lwres_uint32_t flags,lwres_grbnresponse_t ** structp)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