xref: /minix/external/bsd/bind/dist/lib/lwres/lwresutil.c (revision fb9c64b2)
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