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