1135446Strhodes/*
2262706Serwin * Copyright (C) 2004, 2005, 2007, 2014  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2000, 2001  Internet Software Consortium.
4135446Strhodes *
5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id: lwresutil.c,v 1.34 2007/06/19 23:47:22 tbox Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22170222Sdougb/**
23170222Sdougb *    lwres_string_parse() retrieves a DNS-encoded string starting the
24170222Sdougb *    current pointer of lightweight resolver buffer b: i.e. b->current.
25170222Sdougb *    When the function returns, the address of the first byte of the
26170222Sdougb *    encoded string is returned via *c and the length of that string is
27170222Sdougb *    given by *len. The buffer's current pointer is advanced to point at
28170222Sdougb *    the character following the string length, the encoded string, and
29170222Sdougb *    the trailing NULL character.
30262706Serwin *
31170222Sdougb *    lwres_addr_parse() extracts an address from the buffer b. The
32170222Sdougb *    buffer's current pointer b->current is presumed to point at an
33170222Sdougb *    encoded address: the address preceded by a 32-bit protocol family
34170222Sdougb *    identifier and a 16-bit length field. The encoded address is copied
35170222Sdougb *    to addr->address and addr->length indicates the size in bytes of
36170222Sdougb *    the address that was copied. b->current is advanced to point at the
37170222Sdougb *  next byte of available data in the buffer following the encoded
38170222Sdougb *    address.
39262706Serwin *
40170222Sdougb *    lwres_getaddrsbyname() and lwres_getnamebyaddr() use the
41170222Sdougb *    lwres_gnbaresponse_t structure defined below:
42262706Serwin *
43170222Sdougb * \code
44170222Sdougb * typedef struct {
45170222Sdougb *         lwres_uint32_t          flags;
46170222Sdougb *         lwres_uint16_t          naliases;
47170222Sdougb *         lwres_uint16_t          naddrs;
48170222Sdougb *         char                   *realname;
49170222Sdougb *         char                  **aliases;
50170222Sdougb *         lwres_uint16_t          realnamelen;
51170222Sdougb *         lwres_uint16_t         *aliaslen;
52170222Sdougb *         lwres_addrlist_t        addrs;
53170222Sdougb *         void                   *base;
54170222Sdougb *         size_t                  baselen;
55170222Sdougb * } lwres_gabnresponse_t;
56170222Sdougb * \endcode
57262706Serwin *
58170222Sdougb *    The contents of this structure are not manipulated directly but
59262706Serwin *    they are controlled through the \link lwres_gabn.c lwres_gabn*\endlink functions.
60262706Serwin *
61170222Sdougb *    The lightweight resolver uses lwres_getaddrsbyname() to perform
62170222Sdougb *    foward lookups. Hostname name is looked up using the resolver
63262706Serwin *    context ctx for memory allocation. addrtypes is a bitmask
64170222Sdougb *    indicating which type of addresses are to be looked up. Current
65170222Sdougb *    values for this bitmask are #LWRES_ADDRTYPE_V4 for IPv4 addresses
66170222Sdougb *    and #LWRES_ADDRTYPE_V6 for IPv6 addresses. Results of the lookup are
67170222Sdougb *    returned in *structp.
68262706Serwin *
69262706Serwin *    lwres_getnamebyaddr() performs reverse lookups. Resolver context
70170222Sdougb *    ctx is used for memory allocation. The address type is indicated by
71170222Sdougb *    addrtype: #LWRES_ADDRTYPE_V4 or #LWRES_ADDRTYPE_V6. The address to be
72262706Serwin *    looked up is given by addr and its length is addrlen bytes. The
73262706Serwin *    result of the function call is made available through *structp.
74262706Serwin *
75170222Sdougb * \section lwresutil_return Return Values
76262706Serwin *
77170222Sdougb *    Successful calls to lwres_string_parse() and lwres_addr_parse()
78262706Serwin *    return #LWRES_R_SUCCESS. Both functions return #LWRES_R_FAILURE if
79262706Serwin *    the buffer is corrupt or #LWRES_R_UNEXPECTEDEND if the buffer has
80170222Sdougb *    less space than expected for the components of the encoded string
81170222Sdougb *    or address.
82262706Serwin *
83170222Sdougb * lwres_getaddrsbyname() returns #LWRES_R_SUCCESS on success and it
84170222Sdougb *    returns #LWRES_R_NOTFOUND if the hostname name could not be found.
85262706Serwin *
86170222Sdougb *    #LWRES_R_SUCCESS is returned by a successful call to
87170222Sdougb *    lwres_getnamebyaddr().
88262706Serwin *
89170222Sdougb *    Both lwres_getaddrsbyname() and lwres_getnamebyaddr() return
90170222Sdougb *    #LWRES_R_NOMEMORY when memory allocation requests fail and
91170222Sdougb *    #LWRES_R_UNEXPECTEDEND if the buffers used for sending queries and
92262706Serwin *    receiving replies are too small.
93262706Serwin *
94170222Sdougb * \section lwresutil_see See Also
95262706Serwin *
96170222Sdougb *    lwbuffer.c, lwres_gabn.c
97170222Sdougb */
98170222Sdougb
99135446Strhodes#include <config.h>
100135446Strhodes
101135446Strhodes#include <assert.h>
102135446Strhodes#include <stdlib.h>
103135446Strhodes#include <string.h>
104135446Strhodes#include <unistd.h>
105135446Strhodes
106135446Strhodes#include <lwres/lwbuffer.h>
107135446Strhodes#include <lwres/lwres.h>
108135446Strhodes#include <lwres/result.h>
109135446Strhodes
110135446Strhodes#include "assert_p.h"
111135446Strhodes#include "context_p.h"
112135446Strhodes
113170222Sdougb/*% Parse data. */
114170222Sdougb/*!
115135446Strhodes * Requires:
116135446Strhodes *
117135446Strhodes *	The "current" pointer in "b" points to encoded raw data.
118135446Strhodes *
119135446Strhodes * Ensures:
120135446Strhodes *
121135446Strhodes *	The address of the first byte of the data is returned via "p",
122135446Strhodes *	and the length is returned via "len".  If NULL, they are not
123135446Strhodes *	set.
124135446Strhodes *
125135446Strhodes *	On return, the current pointer of "b" will point to the character
126135446Strhodes *	following the data length and the data.
127135446Strhodes *
128135446Strhodes */
129135446Strhodeslwres_result_t
130135446Strhodeslwres_data_parse(lwres_buffer_t *b, unsigned char **p, lwres_uint16_t *len)
131135446Strhodes{
132135446Strhodes	lwres_uint16_t datalen;
133135446Strhodes	unsigned char *data;
134135446Strhodes
135135446Strhodes	REQUIRE(b != NULL);
136135446Strhodes
137135446Strhodes	/*
138135446Strhodes	 * Pull off the length (2 bytes)
139135446Strhodes	 */
140135446Strhodes	if (!SPACE_REMAINING(b, 2))
141135446Strhodes		return (LWRES_R_UNEXPECTEDEND);
142135446Strhodes	datalen = lwres_buffer_getuint16(b);
143135446Strhodes
144135446Strhodes	/*
145135446Strhodes	 * Set the pointer to this string to the right place, then
146135446Strhodes	 * advance the buffer pointer.
147135446Strhodes	 */
148135446Strhodes	if (!SPACE_REMAINING(b, datalen))
149135446Strhodes		return (LWRES_R_UNEXPECTEDEND);
150135446Strhodes	data = b->base + b->current;
151135446Strhodes	lwres_buffer_forward(b, datalen);
152135446Strhodes
153135446Strhodes	if (len != NULL)
154135446Strhodes		*len = datalen;
155135446Strhodes	if (p != NULL)
156135446Strhodes		*p = data;
157135446Strhodes
158135446Strhodes	return (LWRES_R_SUCCESS);
159135446Strhodes}
160135446Strhodes
161170222Sdougb/*% Retrieves a DNS-encoded string. */
162170222Sdougb/*!
163135446Strhodes * Requires:
164135446Strhodes *
165135446Strhodes *	The "current" pointer in "b" point to an encoded string.
166135446Strhodes *
167135446Strhodes * Ensures:
168135446Strhodes *
169135446Strhodes *	The address of the first byte of the string is returned via "c",
170135446Strhodes *	and the length is returned via "len".  If NULL, they are not
171135446Strhodes *	set.
172135446Strhodes *
173135446Strhodes *	On return, the current pointer of "b" will point to the character
174135446Strhodes *	following the string length, the string, and the trailing NULL.
175135446Strhodes *
176135446Strhodes */
177135446Strhodeslwres_result_t
178135446Strhodeslwres_string_parse(lwres_buffer_t *b, char **c, lwres_uint16_t *len)
179135446Strhodes{
180135446Strhodes	lwres_uint16_t datalen;
181135446Strhodes	char *string;
182135446Strhodes
183135446Strhodes	REQUIRE(b != NULL);
184135446Strhodes
185135446Strhodes	/*
186135446Strhodes	 * Pull off the length (2 bytes)
187135446Strhodes	 */
188135446Strhodes	if (!SPACE_REMAINING(b, 2))
189135446Strhodes		return (LWRES_R_UNEXPECTEDEND);
190135446Strhodes	datalen = lwres_buffer_getuint16(b);
191135446Strhodes
192135446Strhodes	/*
193135446Strhodes	 * Set the pointer to this string to the right place, then
194135446Strhodes	 * advance the buffer pointer.
195135446Strhodes	 */
196135446Strhodes	if (!SPACE_REMAINING(b, datalen))
197135446Strhodes		return (LWRES_R_UNEXPECTEDEND);
198135446Strhodes	string = (char *)b->base + b->current;
199135446Strhodes	lwres_buffer_forward(b, datalen);
200135446Strhodes
201135446Strhodes	/*
202135446Strhodes	 * Skip the "must be zero" byte.
203135446Strhodes	 */
204135446Strhodes	if (!SPACE_REMAINING(b, 1))
205135446Strhodes		return (LWRES_R_UNEXPECTEDEND);
206135446Strhodes	if (0 != lwres_buffer_getuint8(b))
207135446Strhodes		return (LWRES_R_FAILURE);
208135446Strhodes
209135446Strhodes	if (len != NULL)
210135446Strhodes		*len = datalen;
211135446Strhodes	if (c != NULL)
212135446Strhodes		*c = string;
213135446Strhodes
214135446Strhodes	return (LWRES_R_SUCCESS);
215135446Strhodes}
216135446Strhodes
217170222Sdougb/*% Extracts an address from the buffer b. */
218135446Strhodeslwres_result_t
219135446Strhodeslwres_addr_parse(lwres_buffer_t *b, lwres_addr_t *addr)
220135446Strhodes{
221135446Strhodes	REQUIRE(addr != NULL);
222135446Strhodes
223135446Strhodes	if (!SPACE_REMAINING(b, 6))
224135446Strhodes		return (LWRES_R_UNEXPECTEDEND);
225135446Strhodes
226135446Strhodes	addr->family = lwres_buffer_getuint32(b);
227135446Strhodes	addr->length = lwres_buffer_getuint16(b);
228135446Strhodes
229135446Strhodes	if (!SPACE_REMAINING(b, addr->length))
230135446Strhodes		return (LWRES_R_UNEXPECTEDEND);
231135446Strhodes	if (addr->length > LWRES_ADDR_MAXLEN)
232135446Strhodes		return (LWRES_R_FAILURE);
233135446Strhodes
234135446Strhodes	lwres_buffer_getmem(b, addr->address, addr->length);
235135446Strhodes
236135446Strhodes	return (LWRES_R_SUCCESS);
237135446Strhodes}
238135446Strhodes
239170222Sdougb/*% Used to perform forward lookups. */
240135446Strhodeslwres_result_t
241135446Strhodeslwres_getaddrsbyname(lwres_context_t *ctx, const char *name,
242135446Strhodes		     lwres_uint32_t addrtypes, lwres_gabnresponse_t **structp)
243135446Strhodes{
244135446Strhodes	lwres_gabnrequest_t request;
245135446Strhodes	lwres_gabnresponse_t *response;
246135446Strhodes	int ret;
247135446Strhodes	int recvlen;
248135446Strhodes	lwres_buffer_t b_in, b_out;
249135446Strhodes	lwres_lwpacket_t pkt;
250135446Strhodes	lwres_uint32_t serial;
251135446Strhodes	char *buffer;
252135446Strhodes	char target_name[1024];
253135446Strhodes	unsigned int target_length;
254135446Strhodes
255135446Strhodes	REQUIRE(ctx != NULL);
256135446Strhodes	REQUIRE(name != NULL);
257135446Strhodes	REQUIRE(addrtypes != 0);
258135446Strhodes	REQUIRE(structp != NULL && *structp == NULL);
259135446Strhodes
260135446Strhodes	b_in.base = NULL;
261135446Strhodes	b_out.base = NULL;
262135446Strhodes	response = NULL;
263135446Strhodes	buffer = NULL;
264135446Strhodes	serial = lwres_context_nextserial(ctx);
265135446Strhodes
266135446Strhodes	buffer = CTXMALLOC(LWRES_RECVLENGTH);
267135446Strhodes	if (buffer == NULL) {
268135446Strhodes		ret = LWRES_R_NOMEMORY;
269135446Strhodes		goto out;
270135446Strhodes	}
271135446Strhodes
272135446Strhodes	target_length = strlen(name);
273135446Strhodes	if (target_length >= sizeof(target_name))
274135446Strhodes		return (LWRES_R_FAILURE);
275135446Strhodes	strcpy(target_name, name); /* strcpy is safe */
276135446Strhodes
277135446Strhodes	/*
278135446Strhodes	 * Set up our request and render it to a buffer.
279135446Strhodes	 */
280135446Strhodes	request.flags = 0;
281135446Strhodes	request.addrtypes = addrtypes;
282135446Strhodes	request.name = target_name;
283135446Strhodes	request.namelen = target_length;
284135446Strhodes	pkt.pktflags = 0;
285135446Strhodes	pkt.serial = serial;
286135446Strhodes	pkt.result = 0;
287135446Strhodes	pkt.recvlength = LWRES_RECVLENGTH;
288135446Strhodes
289135446Strhodes again:
290135446Strhodes	ret = lwres_gabnrequest_render(ctx, &request, &pkt, &b_out);
291135446Strhodes	if (ret != LWRES_R_SUCCESS)
292135446Strhodes		goto out;
293135446Strhodes
294135446Strhodes	ret = lwres_context_sendrecv(ctx, b_out.base, b_out.length, buffer,
295135446Strhodes				     LWRES_RECVLENGTH, &recvlen);
296135446Strhodes	if (ret != LWRES_R_SUCCESS)
297135446Strhodes		goto out;
298135446Strhodes
299135446Strhodes	lwres_buffer_init(&b_in, buffer, recvlen);
300135446Strhodes	b_in.used = recvlen;
301135446Strhodes
302135446Strhodes	/*
303135446Strhodes	 * Parse the packet header.
304135446Strhodes	 */
305135446Strhodes	ret = lwres_lwpacket_parseheader(&b_in, &pkt);
306135446Strhodes	if (ret != LWRES_R_SUCCESS)
307135446Strhodes		goto out;
308135446Strhodes
309135446Strhodes	/*
310135446Strhodes	 * Sanity check.
311135446Strhodes	 */
312135446Strhodes	if (pkt.serial != serial)
313135446Strhodes		goto again;
314135446Strhodes	if (pkt.opcode != LWRES_OPCODE_GETADDRSBYNAME)
315135446Strhodes		goto again;
316135446Strhodes
317135446Strhodes	/*
318135446Strhodes	 * Free what we've transmitted
319135446Strhodes	 */
320135446Strhodes	CTXFREE(b_out.base, b_out.length);
321135446Strhodes	b_out.base = NULL;
322135446Strhodes	b_out.length = 0;
323135446Strhodes
324135446Strhodes	if (pkt.result != LWRES_R_SUCCESS) {
325135446Strhodes		ret = pkt.result;
326135446Strhodes		goto out;
327135446Strhodes	}
328135446Strhodes
329135446Strhodes	/*
330135446Strhodes	 * Parse the response.
331135446Strhodes	 */
332135446Strhodes	ret = lwres_gabnresponse_parse(ctx, &b_in, &pkt, &response);
333135446Strhodes	if (ret != LWRES_R_SUCCESS)
334135446Strhodes		goto out;
335135446Strhodes	response->base = buffer;
336135446Strhodes	response->baselen = LWRES_RECVLENGTH;
337135446Strhodes	buffer = NULL; /* don't free this below */
338135446Strhodes
339135446Strhodes	*structp = response;
340135446Strhodes	return (LWRES_R_SUCCESS);
341135446Strhodes
342135446Strhodes out:
343135446Strhodes	if (b_out.base != NULL)
344135446Strhodes		CTXFREE(b_out.base, b_out.length);
345135446Strhodes	if (buffer != NULL)
346135446Strhodes		CTXFREE(buffer, LWRES_RECVLENGTH);
347135446Strhodes	if (response != NULL)
348135446Strhodes		lwres_gabnresponse_free(ctx, &response);
349135446Strhodes
350135446Strhodes	return (ret);
351135446Strhodes}
352135446Strhodes
353135446Strhodes
354170222Sdougb/*% Used to perform reverse lookups. */
355135446Strhodeslwres_result_t
356135446Strhodeslwres_getnamebyaddr(lwres_context_t *ctx, lwres_uint32_t addrtype,
357135446Strhodes		    lwres_uint16_t addrlen, const unsigned char *addr,
358135446Strhodes		    lwres_gnbaresponse_t **structp)
359135446Strhodes{
360135446Strhodes	lwres_gnbarequest_t request;
361135446Strhodes	lwres_gnbaresponse_t *response;
362135446Strhodes	int ret;
363135446Strhodes	int recvlen;
364135446Strhodes	lwres_buffer_t b_in, b_out;
365135446Strhodes	lwres_lwpacket_t pkt;
366135446Strhodes	lwres_uint32_t serial;
367135446Strhodes	char *buffer;
368135446Strhodes
369135446Strhodes	REQUIRE(ctx != NULL);
370135446Strhodes	REQUIRE(addrtype != 0);
371135446Strhodes	REQUIRE(addrlen != 0);
372135446Strhodes	REQUIRE(addr != NULL);
373135446Strhodes	REQUIRE(structp != NULL && *structp == NULL);
374135446Strhodes
375135446Strhodes	b_in.base = NULL;
376135446Strhodes	b_out.base = NULL;
377135446Strhodes	response = NULL;
378135446Strhodes	buffer = NULL;
379135446Strhodes	serial = lwres_context_nextserial(ctx);
380135446Strhodes
381135446Strhodes	buffer = CTXMALLOC(LWRES_RECVLENGTH);
382135446Strhodes	if (buffer == NULL) {
383135446Strhodes		ret = LWRES_R_NOMEMORY;
384135446Strhodes		goto out;
385135446Strhodes	}
386135446Strhodes
387135446Strhodes	/*
388135446Strhodes	 * Set up our request and render it to a buffer.
389135446Strhodes	 */
390135446Strhodes	request.flags = 0;
391135446Strhodes	request.addr.family = addrtype;
392135446Strhodes	request.addr.length = addrlen;
393262706Serwin	memmove(request.addr.address, addr, addrlen);
394135446Strhodes	pkt.pktflags = 0;
395135446Strhodes	pkt.serial = serial;
396135446Strhodes	pkt.result = 0;
397135446Strhodes	pkt.recvlength = LWRES_RECVLENGTH;
398135446Strhodes
399135446Strhodes again:
400135446Strhodes	ret = lwres_gnbarequest_render(ctx, &request, &pkt, &b_out);
401135446Strhodes	if (ret != LWRES_R_SUCCESS)
402135446Strhodes		goto out;
403135446Strhodes
404135446Strhodes	ret = lwres_context_sendrecv(ctx, b_out.base, b_out.length, buffer,
405135446Strhodes				     LWRES_RECVLENGTH, &recvlen);
406135446Strhodes	if (ret != LWRES_R_SUCCESS)
407135446Strhodes		goto out;
408135446Strhodes
409135446Strhodes	lwres_buffer_init(&b_in, buffer, recvlen);
410135446Strhodes	b_in.used = recvlen;
411135446Strhodes
412135446Strhodes	/*
413135446Strhodes	 * Parse the packet header.
414135446Strhodes	 */
415135446Strhodes	ret = lwres_lwpacket_parseheader(&b_in, &pkt);
416135446Strhodes	if (ret != LWRES_R_SUCCESS)
417135446Strhodes		goto out;
418135446Strhodes
419135446Strhodes	/*
420135446Strhodes	 * Sanity check.
421135446Strhodes	 */
422135446Strhodes	if (pkt.serial != serial)
423135446Strhodes		goto again;
424135446Strhodes	if (pkt.opcode != LWRES_OPCODE_GETNAMEBYADDR)
425135446Strhodes		goto again;
426135446Strhodes
427135446Strhodes	/*
428135446Strhodes	 * Free what we've transmitted
429135446Strhodes	 */
430135446Strhodes	CTXFREE(b_out.base, b_out.length);
431135446Strhodes	b_out.base = NULL;
432135446Strhodes	b_out.length = 0;
433135446Strhodes
434135446Strhodes	if (pkt.result != LWRES_R_SUCCESS) {
435135446Strhodes		ret = pkt.result;
436135446Strhodes		goto out;
437135446Strhodes	}
438135446Strhodes
439135446Strhodes	/*
440135446Strhodes	 * Parse the response.
441135446Strhodes	 */
442135446Strhodes	ret = lwres_gnbaresponse_parse(ctx, &b_in, &pkt, &response);
443135446Strhodes	if (ret != LWRES_R_SUCCESS)
444135446Strhodes		goto out;
445135446Strhodes	response->base = buffer;
446135446Strhodes	response->baselen = LWRES_RECVLENGTH;
447135446Strhodes	buffer = NULL; /* don't free this below */
448135446Strhodes
449135446Strhodes	*structp = response;
450135446Strhodes	return (LWRES_R_SUCCESS);
451135446Strhodes
452135446Strhodes out:
453135446Strhodes	if (b_out.base != NULL)
454135446Strhodes		CTXFREE(b_out.base, b_out.length);
455135446Strhodes	if (buffer != NULL)
456135446Strhodes		CTXFREE(buffer, LWRES_RECVLENGTH);
457135446Strhodes	if (response != NULL)
458135446Strhodes		lwres_gnbaresponse_free(ctx, &response);
459135446Strhodes
460135446Strhodes	return (ret);
461135446Strhodes}
462135446Strhodes
463170222Sdougb/*% Get rdata by name. */
464135446Strhodeslwres_result_t
465135446Strhodeslwres_getrdatabyname(lwres_context_t *ctx, const char *name,
466135446Strhodes		     lwres_uint16_t rdclass, lwres_uint16_t rdtype,
467135446Strhodes		     lwres_uint32_t flags, lwres_grbnresponse_t **structp)
468135446Strhodes{
469135446Strhodes	int ret;
470135446Strhodes	int recvlen;
471135446Strhodes	lwres_buffer_t b_in, b_out;
472135446Strhodes	lwres_lwpacket_t pkt;
473135446Strhodes	lwres_uint32_t serial;
474135446Strhodes	char *buffer;
475135446Strhodes	lwres_grbnrequest_t request;
476135446Strhodes	lwres_grbnresponse_t *response;
477135446Strhodes	char target_name[1024];
478135446Strhodes	unsigned int target_length;
479135446Strhodes
480135446Strhodes	REQUIRE(ctx != NULL);
481135446Strhodes	REQUIRE(name != NULL);
482135446Strhodes	REQUIRE(structp != NULL && *structp == NULL);
483135446Strhodes
484135446Strhodes	b_in.base = NULL;
485135446Strhodes	b_out.base = NULL;
486135446Strhodes	response = NULL;
487135446Strhodes	buffer = NULL;
488135446Strhodes	serial = lwres_context_nextserial(ctx);
489135446Strhodes
490135446Strhodes	buffer = CTXMALLOC(LWRES_RECVLENGTH);
491135446Strhodes	if (buffer == NULL) {
492135446Strhodes		ret = LWRES_R_NOMEMORY;
493135446Strhodes		goto out;
494135446Strhodes	}
495135446Strhodes
496135446Strhodes	target_length = strlen(name);
497135446Strhodes	if (target_length >= sizeof(target_name))
498135446Strhodes		return (LWRES_R_FAILURE);
499135446Strhodes	strcpy(target_name, name); /* strcpy is safe */
500135446Strhodes
501135446Strhodes	/*
502135446Strhodes	 * Set up our request and render it to a buffer.
503135446Strhodes	 */
504135446Strhodes	request.rdclass = rdclass;
505135446Strhodes	request.rdtype = rdtype;
506135446Strhodes	request.flags = flags;
507135446Strhodes	request.name = target_name;
508135446Strhodes	request.namelen = target_length;
509135446Strhodes	pkt.pktflags = 0;
510135446Strhodes	pkt.serial = serial;
511135446Strhodes	pkt.result = 0;
512135446Strhodes	pkt.recvlength = LWRES_RECVLENGTH;
513135446Strhodes
514135446Strhodes again:
515135446Strhodes	ret = lwres_grbnrequest_render(ctx, &request, &pkt, &b_out);
516135446Strhodes	if (ret != LWRES_R_SUCCESS)
517135446Strhodes		goto out;
518135446Strhodes
519135446Strhodes	ret = lwres_context_sendrecv(ctx, b_out.base, b_out.length, buffer,
520135446Strhodes				     LWRES_RECVLENGTH, &recvlen);
521135446Strhodes	if (ret != LWRES_R_SUCCESS)
522135446Strhodes		goto out;
523135446Strhodes
524135446Strhodes	lwres_buffer_init(&b_in, buffer, recvlen);
525135446Strhodes	b_in.used = recvlen;
526135446Strhodes
527135446Strhodes	/*
528135446Strhodes	 * Parse the packet header.
529135446Strhodes	 */
530135446Strhodes	ret = lwres_lwpacket_parseheader(&b_in, &pkt);
531135446Strhodes	if (ret != LWRES_R_SUCCESS)
532135446Strhodes		goto out;
533135446Strhodes
534135446Strhodes	/*
535135446Strhodes	 * Sanity check.
536135446Strhodes	 */
537135446Strhodes	if (pkt.serial != serial)
538135446Strhodes		goto again;
539135446Strhodes	if (pkt.opcode != LWRES_OPCODE_GETRDATABYNAME)
540135446Strhodes		goto again;
541135446Strhodes
542135446Strhodes	/*
543135446Strhodes	 * Free what we've transmitted
544135446Strhodes	 */
545135446Strhodes	CTXFREE(b_out.base, b_out.length);
546135446Strhodes	b_out.base = NULL;
547135446Strhodes	b_out.length = 0;
548135446Strhodes
549135446Strhodes	if (pkt.result != LWRES_R_SUCCESS) {
550135446Strhodes		ret = pkt.result;
551135446Strhodes		goto out;
552135446Strhodes	}
553135446Strhodes
554135446Strhodes	/*
555135446Strhodes	 * Parse the response.
556135446Strhodes	 */
557135446Strhodes	ret = lwres_grbnresponse_parse(ctx, &b_in, &pkt, &response);
558135446Strhodes	if (ret != LWRES_R_SUCCESS)
559135446Strhodes		goto out;
560135446Strhodes	response->base = buffer;
561135446Strhodes	response->baselen = LWRES_RECVLENGTH;
562135446Strhodes	buffer = NULL; /* don't free this below */
563135446Strhodes
564135446Strhodes	*structp = response;
565135446Strhodes	return (LWRES_R_SUCCESS);
566135446Strhodes
567135446Strhodes out:
568135446Strhodes	if (b_out.base != NULL)
569135446Strhodes		CTXFREE(b_out.base, b_out.length);
570135446Strhodes	if (buffer != NULL)
571135446Strhodes		CTXFREE(buffer, LWRES_RECVLENGTH);
572135446Strhodes	if (response != NULL)
573135446Strhodes		lwres_grbnresponse_free(ctx, &response);
574135446Strhodes
575135446Strhodes	return (ret);
576135446Strhodes}
577