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