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