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