2 * Copyright (C) 2004, 2005, 2007-2009 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000, 2001, 2003 Internet Software Consortium.
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.
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.
18 /* $Id: context.c,v 1.50.332.5 2009/09/01 23:47:05 tbox Exp $ */
21 lwres_context_create() creates a #lwres_context_t structure for use in
22 lightweight resolver operations. It holds a socket and other data
23 needed for communicating with a resolver daemon. The new
24 lwres_context_t is returned through contextp, a pointer to a
25 lwres_context_t pointer. This lwres_context_t pointer must initially
26 be NULL, and is modified to point to the newly created
29 When the lightweight resolver needs to perform dynamic memory
30 allocation, it will call malloc_function to allocate memory and
31 free_function to free it. If malloc_function and free_function are
32 NULL, memory is allocated using malloc and free. It is not
33 permitted to have a NULL malloc_function and a non-NULL free_function
34 or vice versa. arg is passed as the first parameter to the memory
35 allocation functions. If malloc_function and free_function are NULL,
36 arg is unused and should be passed as NULL.
38 Once memory for the structure has been allocated, it is initialized
39 using lwres_conf_init() and returned via *contextp.
41 lwres_context_destroy() destroys a #lwres_context_t, closing its
42 socket. contextp is a pointer to a pointer to the context that is to
43 be destroyed. The pointer will be set to NULL when the context has
46 The context holds a serial number that is used to identify resolver
47 request packets and associate responses with the corresponding
48 requests. This serial number is controlled using
49 lwres_context_initserial() and lwres_context_nextserial().
50 lwres_context_initserial() sets the serial number for context *ctx to
51 serial. lwres_context_nextserial() increments the serial number and
52 returns the previous value.
54 Memory for a lightweight resolver context is allocated and freed using
55 lwres_context_allocmem() and lwres_context_freemem(). These use
56 whatever allocations were defined when the context was created with
57 lwres_context_create(). lwres_context_allocmem() allocates len bytes
58 of memory and if successful returns a pointer to the allocated
59 storage. lwres_context_freemem() frees len bytes of space starting at
62 lwres_context_sendrecv() performs I/O for the context ctx. Data are
63 read and written from the context's socket. It writes data from
64 sendbase -- typically a lightweight resolver query packet -- and waits
65 for a reply which is copied to the receive buffer at recvbase. The
66 number of bytes that were written to this receive buffer is returned
69 \section context_return Return Values
71 lwres_context_create() returns #LWRES_R_NOMEMORY if memory for the
72 struct lwres_context could not be allocated, #LWRES_R_SUCCESS
75 Successful calls to the memory allocator lwres_context_allocmem()
76 return a pointer to the start of the allocated space. It returns NULL
77 if memory could not be allocated.
79 #LWRES_R_SUCCESS is returned when lwres_context_sendrecv() completes
80 successfully. #LWRES_R_IOERROR is returned if an I/O error occurs and
81 #LWRES_R_TIMEOUT is returned if lwres_context_sendrecv() times out
82 waiting for a response.
84 \section context_see See Also
86 lwres_conf_init(), malloc, free.
97 #include <lwres/lwres.h>
98 #include <lwres/net.h>
99 #include <lwres/platform.h>
101 #ifdef LWRES_PLATFORM_NEEDSYSSELECTH
102 #include <sys/select.h>
105 #include "context_p.h"
106 #include "assert_p.h"
109 * Some systems define the socket length argument as an int, some as size_t,
110 * some as socklen_t. The last is what the current POSIX standard mandates.
111 * This definition is here so it can be portable but easily changed if needed.
113 #ifndef LWRES_SOCKADDR_LEN_T
114 #define LWRES_SOCKADDR_LEN_T unsigned int
118 * Make a socket nonblocking.
120 #ifndef MAKE_NONBLOCKING
121 #define MAKE_NONBLOCKING(sd, retval) \
123 retval = fcntl(sd, F_GETFL, 0); \
124 if (retval != -1) { \
125 retval |= O_NONBLOCK; \
126 retval = fcntl(sd, F_SETFL, retval); \
131 LIBLWRES_EXTERNAL_DATA lwres_uint16_t lwres_udp_port = LWRES_UDP_PORT;
132 LIBLWRES_EXTERNAL_DATA const char *lwres_resolv_conf = LWRES_RESOLV_CONF;
135 lwres_malloc(void *, size_t);
138 lwres_free(void *, void *, size_t);
143 static lwres_result_t
144 context_connect(lwres_context_t *);
147 * Creates a #lwres_context_t structure for use in
148 * lightweight resolver operations.
151 lwres_context_create(lwres_context_t **contextp, void *arg,
152 lwres_malloc_t malloc_function,
153 lwres_free_t free_function,
156 lwres_context_t *ctx;
158 REQUIRE(contextp != NULL && *contextp == NULL);
161 * If we were not given anything special to use, use our own
162 * functions. These are just wrappers around malloc() and free().
164 if (malloc_function == NULL || free_function == NULL) {
165 REQUIRE(malloc_function == NULL);
166 REQUIRE(free_function == NULL);
167 malloc_function = lwres_malloc;
168 free_function = lwres_free;
171 ctx = malloc_function(arg, sizeof(lwres_context_t));
173 return (LWRES_R_NOMEMORY);
176 * Set up the context.
178 ctx->malloc = malloc_function;
179 ctx->free = free_function;
183 ctx->timeout = LWRES_DEFAULT_TIMEOUT;
184 ctx->serial = time(NULL); /* XXXMLG or BEW */
188 if ((flags & (LWRES_CONTEXT_USEIPV4 | LWRES_CONTEXT_USEIPV6)) ==
189 LWRES_CONTEXT_USEIPV6) {
192 if ((flags & (LWRES_CONTEXT_USEIPV4 | LWRES_CONTEXT_USEIPV6)) ==
193 LWRES_CONTEXT_USEIPV4) {
198 * Init resolv.conf bits.
200 lwres_conf_init(ctx);
203 return (LWRES_R_SUCCESS);
207 Destroys a #lwres_context_t, closing its socket.
208 contextp is a pointer to a pointer to the context that is
209 to be destroyed. The pointer will be set to NULL
210 when the context has been destroyed.
213 lwres_context_destroy(lwres_context_t **contextp) {
214 lwres_context_t *ctx;
216 REQUIRE(contextp != NULL && *contextp != NULL);
221 if (ctx->sock != -1) {
225 (void)close(ctx->sock);
229 CTXFREE(ctx, sizeof(lwres_context_t));
231 /*% Increments the serial number and returns the previous value. */
233 lwres_context_nextserial(lwres_context_t *ctx) {
234 REQUIRE(ctx != NULL);
236 return (ctx->serial++);
239 /*% Sets the serial number for context *ctx to serial. */
241 lwres_context_initserial(lwres_context_t *ctx, lwres_uint32_t serial) {
242 REQUIRE(ctx != NULL);
244 ctx->serial = serial;
247 /*% Frees len bytes of space starting at location mem. */
249 lwres_context_freemem(lwres_context_t *ctx, void *mem, size_t len) {
250 REQUIRE(mem != NULL);
256 /*% Allocates len bytes of memory and if successful returns a pointer to the allocated storage. */
258 lwres_context_allocmem(lwres_context_t *ctx, size_t len) {
261 return (CTXMALLOC(len));
265 lwres_malloc(void *arg, size_t len) {
274 memset(mem, 0xe5, len);
280 lwres_free(void *arg, void *mem, size_t len) {
283 memset(mem, 0xa9, len);
287 static lwres_result_t
288 context_connect(lwres_context_t *ctx) {
291 struct sockaddr_in sin;
292 struct sockaddr_in6 sin6;
294 LWRES_SOCKADDR_LEN_T salen;
297 if (ctx->confdata.lwnext != 0) {
298 memcpy(&ctx->address, &ctx->confdata.lwservers[0],
299 sizeof(lwres_addr_t));
300 LWRES_LINK_INIT(&ctx->address, link);
302 /* The default is the IPv4 loopback address 127.0.0.1. */
303 memset(&ctx->address, 0, sizeof(ctx->address));
304 ctx->address.family = LWRES_ADDRTYPE_V4;
305 ctx->address.length = 4;
306 ctx->address.address[0] = 127;
307 ctx->address.address[1] = 0;
308 ctx->address.address[2] = 0;
309 ctx->address.address[3] = 1;
312 if (ctx->address.family == LWRES_ADDRTYPE_V4) {
313 memcpy(&sin.sin_addr, ctx->address.address,
314 sizeof(sin.sin_addr));
315 sin.sin_port = htons(lwres_udp_port);
316 sin.sin_family = AF_INET;
317 sa = (struct sockaddr *)&sin;
320 } else if (ctx->address.family == LWRES_ADDRTYPE_V6) {
321 memcpy(&sin6.sin6_addr, ctx->address.address,
322 sizeof(sin6.sin6_addr));
323 sin6.sin6_port = htons(lwres_udp_port);
324 sin6.sin6_family = AF_INET6;
325 sa = (struct sockaddr *)&sin6;
326 salen = sizeof(sin6);
329 return (LWRES_R_IOERROR);
334 s = socket(domain, SOCK_DGRAM, IPPROTO_UDP);
339 return (LWRES_R_IOERROR);
342 ret = connect(s, sa, salen);
348 return (LWRES_R_IOERROR);
351 MAKE_NONBLOCKING(s, ret);
357 return (LWRES_R_IOERROR);
362 return (LWRES_R_SUCCESS);
366 lwres_context_getsocket(lwres_context_t *ctx) {
371 lwres_context_send(lwres_context_t *ctx,
372 void *sendbase, int sendlen) {
374 lwres_result_t lwresult;
376 if (ctx->sock == -1) {
377 lwresult = context_connect(ctx);
378 if (lwresult != LWRES_R_SUCCESS)
382 ret = sendto(ctx->sock, sendbase, sendlen, 0, NULL, 0);
384 return (LWRES_R_IOERROR);
386 return (LWRES_R_IOERROR);
388 return (LWRES_R_SUCCESS);
392 lwres_context_recv(lwres_context_t *ctx,
393 void *recvbase, int recvlen,
396 LWRES_SOCKADDR_LEN_T fromlen;
397 struct sockaddr_in sin;
398 struct sockaddr_in6 sin6;
402 if (ctx->address.family == LWRES_ADDRTYPE_V4) {
403 sa = (struct sockaddr *)&sin;
404 fromlen = sizeof(sin);
406 sa = (struct sockaddr *)&sin6;
407 fromlen = sizeof(sin6);
411 * The address of fromlen is cast to void * to shut up compiler
412 * warnings, namely on systems that have the sixth parameter
413 * prototyped as a signed int when LWRES_SOCKADDR_LEN_T is
414 * defined as unsigned.
416 ret = recvfrom(ctx->sock, recvbase, recvlen, 0, sa, (void *)&fromlen);
419 return (LWRES_R_IOERROR);
422 return (LWRES_R_TOOLARGE);
425 * If we got something other than what we expect, have the caller
426 * wait for another packet. This can happen if an old result
427 * comes in, or if someone is sending us random stuff.
429 if (ctx->address.family == LWRES_ADDRTYPE_V4) {
430 if (fromlen != sizeof(sin)
431 || memcmp(&sin.sin_addr, ctx->address.address,
432 sizeof(sin.sin_addr)) != 0
433 || sin.sin_port != htons(lwres_udp_port))
434 return (LWRES_R_RETRY);
436 if (fromlen != sizeof(sin6)
437 || memcmp(&sin6.sin6_addr, ctx->address.address,
438 sizeof(sin6.sin6_addr)) != 0
439 || sin6.sin6_port != htons(lwres_udp_port))
440 return (LWRES_R_RETRY);
443 if (recvd_len != NULL)
446 return (LWRES_R_SUCCESS);
449 /*% performs I/O for the context ctx. */
451 lwres_context_sendrecv(lwres_context_t *ctx,
452 void *sendbase, int sendlen,
453 void *recvbase, int recvlen,
456 lwres_result_t result;
459 struct timeval timeout;
462 * Type of tv_sec is 32 bits long.
464 if (ctx->timeout <= 0x7FFFFFFFU)
465 timeout.tv_sec = (int)ctx->timeout;
467 timeout.tv_sec = 0x7FFFFFFF;
471 result = lwres_context_send(ctx, sendbase, sendlen);
472 if (result != LWRES_R_SUCCESS)
476 * If this is not checked, select() can overflow,
477 * causing corruption elsewhere.
479 if (ctx->sock >= (int)FD_SETSIZE) {
482 return (LWRES_R_IOERROR);
487 FD_SET(ctx->sock, &readfds);
488 ret2 = select(ctx->sock + 1, &readfds, NULL, NULL, &timeout);
491 * What happened with select?
494 return (LWRES_R_IOERROR);
496 return (LWRES_R_TIMEOUT);
498 result = lwres_context_recv(ctx, recvbase, recvlen, recvd_len);
499 if (result == LWRES_R_RETRY)