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: lwres_gabn.c,v 1.33 2007/06/19 23:47:22 tbox Exp $ */
19
20/*! \file lwres_gabn.c
21   These are low-level routines for creating and parsing lightweight
22   resolver name-to-address lookup request and response messages.
23
24   There are four main functions for the getaddrbyname opcode. One render
25   function converts a getaddrbyname request structure --
26   lwres_gabnrequest_t -- to the lighweight resolver's canonical format.
27   It is complemented by a parse function that converts a packet in this
28   canonical format to a getaddrbyname request structure. Another render
29   function converts the getaddrbyname response structure --
30   lwres_gabnresponse_t -- to the canonical format. This is complemented
31   by a parse function which converts a packet in canonical format to a
32   getaddrbyname response structure.
33
34   These structures are defined in \link lwres.h <lwres/lwres.h>.\endlink They are shown below.
35
36\code
37#define LWRES_OPCODE_GETADDRSBYNAME     0x00010001U
38
39typedef struct lwres_addr lwres_addr_t;
40typedef LWRES_LIST(lwres_addr_t) lwres_addrlist_t;
41
42typedef struct {
43        lwres_uint32_t  flags;
44        lwres_uint32_t  addrtypes;
45        lwres_uint16_t  namelen;
46        char           *name;
47} lwres_gabnrequest_t;
48
49typedef struct {
50        lwres_uint32_t          flags;
51        lwres_uint16_t          naliases;
52        lwres_uint16_t          naddrs;
53        char                   *realname;
54        char                  **aliases;
55        lwres_uint16_t          realnamelen;
56        lwres_uint16_t         *aliaslen;
57        lwres_addrlist_t        addrs;
58        void                   *base;
59        size_t                  baselen;
60} lwres_gabnresponse_t;
61\endcode
62
63   lwres_gabnrequest_render() uses resolver context ctx to convert
64   getaddrbyname request structure req to canonical format. The packet
65   header structure pkt is initialised and transferred to buffer b. The
66   contents of *req are then appended to the buffer in canonical format.
67   lwres_gabnresponse_render() performs the same task, except it converts
68   a getaddrbyname response structure lwres_gabnresponse_t to the
69   lightweight resolver's canonical format.
70
71   lwres_gabnrequest_parse() uses context ctx to convert the contents of
72   packet pkt to a lwres_gabnrequest_t structure. Buffer b provides space
73   to be used for storing this structure. When the function succeeds, the
74   resulting lwres_gabnrequest_t is made available through *structp.
75   lwres_gabnresponse_parse() offers the same semantics as
76   lwres_gabnrequest_parse() except it yields a lwres_gabnresponse_t
77   structure.
78
79   lwres_gabnresponse_free() and lwres_gabnrequest_free() release the
80   memory in resolver context ctx that was allocated to the
81   lwres_gabnresponse_t or lwres_gabnrequest_t structures referenced via
82   structp. Any memory associated with ancillary buffers and strings for
83   those structures is also discarded.
84
85\section lwres_gabn_return Return Values
86
87   The getaddrbyname opcode functions lwres_gabnrequest_render(),
88   lwres_gabnresponse_render() lwres_gabnrequest_parse() and
89   lwres_gabnresponse_parse() all return #LWRES_R_SUCCESS on success. They
90   return #LWRES_R_NOMEMORY if memory allocation fails.
91   #LWRES_R_UNEXPECTEDEND is returned if the available space in the buffer
92   b is too small to accommodate the packet header or the
93   lwres_gabnrequest_t and lwres_gabnresponse_t structures.
94   lwres_gabnrequest_parse() and lwres_gabnresponse_parse() will return
95   #LWRES_R_UNEXPECTEDEND if the buffer is not empty after decoding the
96   received packet. These functions will return #LWRES_R_FAILURE if
97   pktflags in the packet header structure #lwres_lwpacket_t indicate that
98   the packet is not a response to an earlier query.
99
100\section lwres_gabn_see See Also
101
102   \link lwpacket.c lwres_lwpacket \endlink
103 */
104
105#include <config.h>
106
107#include <assert.h>
108#include <stdlib.h>
109#include <string.h>
110
111#include <lwres/lwbuffer.h>
112#include <lwres/lwpacket.h>
113#include <lwres/lwres.h>
114#include <lwres/result.h>
115
116#include "context_p.h"
117#include "assert_p.h"
118
119/*% uses resolver context ctx to convert getaddrbyname request structure req to canonical format. */
120lwres_result_t
121lwres_gabnrequest_render(lwres_context_t *ctx, lwres_gabnrequest_t *req,
122			 lwres_lwpacket_t *pkt, lwres_buffer_t *b)
123{
124	unsigned char *buf;
125	size_t buflen;
126	int ret;
127	size_t payload_length;
128	lwres_uint16_t datalen;
129
130	REQUIRE(ctx != NULL);
131	REQUIRE(req != NULL);
132	REQUIRE(req->name != NULL);
133	REQUIRE(pkt != NULL);
134	REQUIRE(b != NULL);
135
136	datalen = strlen(req->name);
137
138	payload_length = 4 + 4 + 2 + req->namelen + 1;
139
140	buflen = LWRES_LWPACKET_LENGTH + payload_length;
141	buf = CTXMALLOC(buflen);
142	if (buf == NULL)
143		return (LWRES_R_NOMEMORY);
144
145	lwres_buffer_init(b, buf, buflen);
146
147	pkt->length = buflen;
148	pkt->version = LWRES_LWPACKETVERSION_0;
149	pkt->pktflags &= ~LWRES_LWPACKETFLAG_RESPONSE;
150	pkt->opcode = LWRES_OPCODE_GETADDRSBYNAME;
151	pkt->result = 0;
152	pkt->authtype = 0;
153	pkt->authlength = 0;
154
155	ret = lwres_lwpacket_renderheader(b, pkt);
156	if (ret != LWRES_R_SUCCESS) {
157		lwres_buffer_invalidate(b);
158		CTXFREE(buf, buflen);
159		return (ret);
160	}
161
162	INSIST(SPACE_OK(b, payload_length));
163
164	/*
165	 * Flags.
166	 */
167	lwres_buffer_putuint32(b, req->flags);
168
169	/*
170	 * Address types we'll accept.
171	 */
172	lwres_buffer_putuint32(b, req->addrtypes);
173
174	/*
175	 * Put the length and the data.  We know this will fit because we
176	 * just checked for it.
177	 */
178	lwres_buffer_putuint16(b, datalen);
179	lwres_buffer_putmem(b, (unsigned char *)req->name, datalen);
180	lwres_buffer_putuint8(b, 0); /* trailing NUL */
181
182	INSIST(LWRES_BUFFER_AVAILABLECOUNT(b) == 0);
183
184	return (LWRES_R_SUCCESS);
185}
186/*% converts a getaddrbyname response structure lwres_gabnresponse_t to the lightweight resolver's canonical format. */
187lwres_result_t
188lwres_gabnresponse_render(lwres_context_t *ctx, lwres_gabnresponse_t *req,
189			  lwres_lwpacket_t *pkt, lwres_buffer_t *b)
190{
191	unsigned char *buf;
192	size_t buflen;
193	int ret;
194	size_t payload_length;
195	lwres_uint16_t datalen;
196	lwres_addr_t *addr;
197	int x;
198
199	REQUIRE(ctx != NULL);
200	REQUIRE(req != NULL);
201	REQUIRE(pkt != NULL);
202	REQUIRE(b != NULL);
203
204	/* naliases, naddrs */
205	payload_length = 4 + 2 + 2;
206	/* real name encoding */
207	payload_length += 2 + req->realnamelen + 1;
208	/* each alias */
209	for (x = 0; x < req->naliases; x++)
210		payload_length += 2 + req->aliaslen[x] + 1;
211	/* each address */
212	x = 0;
213	addr = LWRES_LIST_HEAD(req->addrs);
214	while (addr != NULL) {
215		payload_length += 4 + 2;
216		payload_length += addr->length;
217		addr = LWRES_LIST_NEXT(addr, link);
218		x++;
219	}
220	INSIST(x == req->naddrs);
221
222	buflen = LWRES_LWPACKET_LENGTH + payload_length;
223	buf = CTXMALLOC(buflen);
224	if (buf == NULL)
225		return (LWRES_R_NOMEMORY);
226	lwres_buffer_init(b, buf, buflen);
227
228	pkt->length = buflen;
229	pkt->version = LWRES_LWPACKETVERSION_0;
230	pkt->pktflags |= LWRES_LWPACKETFLAG_RESPONSE;
231	pkt->opcode = LWRES_OPCODE_GETADDRSBYNAME;
232	pkt->authtype = 0;
233	pkt->authlength = 0;
234
235	ret = lwres_lwpacket_renderheader(b, pkt);
236	if (ret != LWRES_R_SUCCESS) {
237		lwres_buffer_invalidate(b);
238		CTXFREE(buf, buflen);
239		return (ret);
240	}
241
242	/*
243	 * Check space needed here.
244	 */
245	INSIST(SPACE_OK(b, payload_length));
246
247	/* Flags. */
248	lwres_buffer_putuint32(b, req->flags);
249
250	/* encode naliases and naddrs */
251	lwres_buffer_putuint16(b, req->naliases);
252	lwres_buffer_putuint16(b, req->naddrs);
253
254	/* encode the real name */
255	datalen = req->realnamelen;
256	lwres_buffer_putuint16(b, datalen);
257	lwres_buffer_putmem(b, (unsigned char *)req->realname, datalen);
258	lwres_buffer_putuint8(b, 0);
259
260	/* encode the aliases */
261	for (x = 0; x < req->naliases; x++) {
262		datalen = req->aliaslen[x];
263		lwres_buffer_putuint16(b, datalen);
264		lwres_buffer_putmem(b, (unsigned char *)req->aliases[x],
265				    datalen);
266		lwres_buffer_putuint8(b, 0);
267	}
268
269	/* encode the addresses */
270	addr = LWRES_LIST_HEAD(req->addrs);
271	while (addr != NULL) {
272		lwres_buffer_putuint32(b, addr->family);
273		lwres_buffer_putuint16(b, addr->length);
274		lwres_buffer_putmem(b, addr->address, addr->length);
275		addr = LWRES_LIST_NEXT(addr, link);
276	}
277
278	INSIST(LWRES_BUFFER_AVAILABLECOUNT(b) == 0);
279	INSIST(LWRES_BUFFER_USEDCOUNT(b) == pkt->length);
280
281	return (LWRES_R_SUCCESS);
282}
283/*% Uses context ctx to convert the contents of packet pkt to a lwres_gabnrequest_t structure. */
284lwres_result_t
285lwres_gabnrequest_parse(lwres_context_t *ctx, lwres_buffer_t *b,
286			lwres_lwpacket_t *pkt, lwres_gabnrequest_t **structp)
287{
288	int ret;
289	char *name;
290	lwres_gabnrequest_t *gabn;
291	lwres_uint32_t addrtypes;
292	lwres_uint32_t flags;
293	lwres_uint16_t namelen;
294
295	REQUIRE(ctx != NULL);
296	REQUIRE(pkt != NULL);
297	REQUIRE(b != NULL);
298	REQUIRE(structp != NULL && *structp == NULL);
299
300	if ((pkt->pktflags & LWRES_LWPACKETFLAG_RESPONSE) != 0)
301		return (LWRES_R_FAILURE);
302
303	if (!SPACE_REMAINING(b, 4 + 4))
304		return (LWRES_R_UNEXPECTEDEND);
305
306	flags = lwres_buffer_getuint32(b);
307	addrtypes = lwres_buffer_getuint32(b);
308
309	/*
310	 * Pull off the name itself
311	 */
312	ret = lwres_string_parse(b, &name, &namelen);
313	if (ret != LWRES_R_SUCCESS)
314		return (ret);
315
316	if (LWRES_BUFFER_REMAINING(b) != 0)
317		return (LWRES_R_TRAILINGDATA);
318
319	gabn = CTXMALLOC(sizeof(lwres_gabnrequest_t));
320	if (gabn == NULL)
321		return (LWRES_R_NOMEMORY);
322
323	gabn->flags = flags;
324	gabn->addrtypes = addrtypes;
325	gabn->name = name;
326	gabn->namelen = namelen;
327
328	*structp = gabn;
329	return (LWRES_R_SUCCESS);
330}
331
332/*% Offers the same semantics as lwres_gabnrequest_parse() except it yields a lwres_gabnresponse_t structure. */
333
334lwres_result_t
335lwres_gabnresponse_parse(lwres_context_t *ctx, lwres_buffer_t *b,
336			lwres_lwpacket_t *pkt, lwres_gabnresponse_t **structp)
337{
338	lwres_result_t ret;
339	unsigned int x;
340	lwres_uint32_t flags;
341	lwres_uint16_t naliases;
342	lwres_uint16_t naddrs;
343	lwres_gabnresponse_t *gabn;
344	lwres_addrlist_t addrlist;
345	lwres_addr_t *addr;
346
347	REQUIRE(ctx != NULL);
348	REQUIRE(pkt != NULL);
349	REQUIRE(b != NULL);
350	REQUIRE(structp != NULL && *structp == NULL);
351
352	gabn = NULL;
353
354	if ((pkt->pktflags & LWRES_LWPACKETFLAG_RESPONSE) == 0)
355		return (LWRES_R_FAILURE);
356
357	/*
358	 * Pull off the name itself
359	 */
360	if (!SPACE_REMAINING(b, 4 + 2 + 2))
361		return (LWRES_R_UNEXPECTEDEND);
362	flags = lwres_buffer_getuint32(b);
363	naliases = lwres_buffer_getuint16(b);
364	naddrs = lwres_buffer_getuint16(b);
365
366	gabn = CTXMALLOC(sizeof(lwres_gabnresponse_t));
367	if (gabn == NULL)
368		return (LWRES_R_NOMEMORY);
369	gabn->aliases = NULL;
370	gabn->aliaslen = NULL;
371	LWRES_LIST_INIT(gabn->addrs);
372	gabn->base = NULL;
373
374	gabn->flags = flags;
375	gabn->naliases = naliases;
376	gabn->naddrs = naddrs;
377
378	LWRES_LIST_INIT(addrlist);
379
380	if (naliases > 0) {
381		gabn->aliases = CTXMALLOC(sizeof(char *) * naliases);
382		if (gabn->aliases == NULL) {
383			ret = LWRES_R_NOMEMORY;
384			goto out;
385		}
386
387		gabn->aliaslen = CTXMALLOC(sizeof(lwres_uint16_t) * naliases);
388		if (gabn->aliaslen == NULL) {
389			ret = LWRES_R_NOMEMORY;
390			goto out;
391		}
392	}
393
394	for (x = 0; x < naddrs; x++) {
395		addr = CTXMALLOC(sizeof(lwres_addr_t));
396		if (addr == NULL) {
397			ret = LWRES_R_NOMEMORY;
398			goto out;
399		}
400		LWRES_LINK_INIT(addr, link);
401		LWRES_LIST_APPEND(addrlist, addr, link);
402	}
403
404	/*
405	 * Now, pull off the real name.
406	 */
407	ret = lwres_string_parse(b, &gabn->realname, &gabn->realnamelen);
408	if (ret != LWRES_R_SUCCESS)
409		goto out;
410
411	/*
412	 * Parse off the aliases.
413	 */
414	for (x = 0; x < gabn->naliases; x++) {
415		ret = lwres_string_parse(b, &gabn->aliases[x],
416					 &gabn->aliaslen[x]);
417		if (ret != LWRES_R_SUCCESS)
418			goto out;
419	}
420
421	/*
422	 * Pull off the addresses.  We already strung the linked list
423	 * up above.
424	 */
425	addr = LWRES_LIST_HEAD(addrlist);
426	for (x = 0; x < gabn->naddrs; x++) {
427		INSIST(addr != NULL);
428		ret = lwres_addr_parse(b, addr);
429		if (ret != LWRES_R_SUCCESS)
430			goto out;
431		addr = LWRES_LIST_NEXT(addr, link);
432	}
433
434	if (LWRES_BUFFER_REMAINING(b) != 0) {
435		ret = LWRES_R_TRAILINGDATA;
436		goto out;
437	}
438
439	gabn->addrs = addrlist;
440
441	*structp = gabn;
442	return (LWRES_R_SUCCESS);
443
444 out:
445	if (gabn != NULL) {
446		if (gabn->aliases != NULL)
447			CTXFREE(gabn->aliases, sizeof(char *) * naliases);
448		if (gabn->aliaslen != NULL)
449			CTXFREE(gabn->aliaslen,
450				sizeof(lwres_uint16_t) * naliases);
451		addr = LWRES_LIST_HEAD(addrlist);
452		while (addr != NULL) {
453			LWRES_LIST_UNLINK(addrlist, addr, link);
454			CTXFREE(addr, sizeof(lwres_addr_t));
455			addr = LWRES_LIST_HEAD(addrlist);
456		}
457		CTXFREE(gabn, sizeof(lwres_gabnresponse_t));
458	}
459
460	return (ret);
461}
462
463/*% Release the memory in resolver context ctx that was allocated to the lwres_gabnrequest_t. */
464void
465lwres_gabnrequest_free(lwres_context_t *ctx, lwres_gabnrequest_t **structp)
466{
467	lwres_gabnrequest_t *gabn;
468
469	REQUIRE(ctx != NULL);
470	REQUIRE(structp != NULL && *structp != NULL);
471
472	gabn = *structp;
473	*structp = NULL;
474
475	CTXFREE(gabn, sizeof(lwres_gabnrequest_t));
476}
477
478/*% Release the memory in resolver context ctx that was allocated to the lwres_gabnresponse_t. */
479void
480lwres_gabnresponse_free(lwres_context_t *ctx, lwres_gabnresponse_t **structp)
481{
482	lwres_gabnresponse_t *gabn;
483	lwres_addr_t *addr;
484
485	REQUIRE(ctx != NULL);
486	REQUIRE(structp != NULL && *structp != NULL);
487
488	gabn = *structp;
489	*structp = NULL;
490
491	if (gabn->naliases > 0) {
492		CTXFREE(gabn->aliases, sizeof(char *) * gabn->naliases);
493		CTXFREE(gabn->aliaslen,
494			sizeof(lwres_uint16_t) * gabn->naliases);
495	}
496	addr = LWRES_LIST_HEAD(gabn->addrs);
497	while (addr != NULL) {
498		LWRES_LIST_UNLINK(gabn->addrs, addr, link);
499		CTXFREE(addr, sizeof(lwres_addr_t));
500		addr = LWRES_LIST_HEAD(gabn->addrs);
501	}
502	if (gabn->base != NULL)
503		CTXFREE(gabn->base, gabn->baselen);
504	CTXFREE(gabn, sizeof(lwres_gabnresponse_t));
505}
506