2 * Copyright (C) 2004, 2005, 2007 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.45.18.7 2007/08/28 07:20:06 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);
162 * If we were not given anything special to use, use our own
163 * functions. These are just wrappers around malloc() and free().
165 if (malloc_function == NULL || free_function == NULL) {
166 REQUIRE(malloc_function == NULL);
167 REQUIRE(free_function == NULL);
168 malloc_function = lwres_malloc;
169 free_function = lwres_free;
172 ctx = malloc_function(arg, sizeof(lwres_context_t));
174 return (LWRES_R_NOMEMORY);
177 * Set up the context.
179 ctx->malloc = malloc_function;
180 ctx->free = free_function;
184 ctx->timeout = LWRES_DEFAULT_TIMEOUT;
185 ctx->serial = time(NULL); /* XXXMLG or BEW */
188 * Init resolv.conf bits.
190 lwres_conf_init(ctx);
193 return (LWRES_R_SUCCESS);
197 Destroys a #lwres_context_t, closing its socket.
198 contextp is a pointer to a pointer to the context that is
199 to be destroyed. The pointer will be set to NULL
200 when the context has been destroyed.
203 lwres_context_destroy(lwres_context_t **contextp) {
204 lwres_context_t *ctx;
206 REQUIRE(contextp != NULL && *contextp != NULL);
211 if (ctx->sock != -1) {
215 (void)close(ctx->sock);
219 CTXFREE(ctx, sizeof(lwres_context_t));
221 /*% Increments the serial number and returns the previous value. */
223 lwres_context_nextserial(lwres_context_t *ctx) {
224 REQUIRE(ctx != NULL);
226 return (ctx->serial++);
229 /*% Sets the serial number for context *ctx to serial. */
231 lwres_context_initserial(lwres_context_t *ctx, lwres_uint32_t serial) {
232 REQUIRE(ctx != NULL);
234 ctx->serial = serial;
237 /*% Frees len bytes of space starting at location mem. */
239 lwres_context_freemem(lwres_context_t *ctx, void *mem, size_t len) {
240 REQUIRE(mem != NULL);
246 /*% Allocates len bytes of memory and if successful returns a pointer to the allocated storage. */
248 lwres_context_allocmem(lwres_context_t *ctx, size_t len) {
251 return (CTXMALLOC(len));
255 lwres_malloc(void *arg, size_t len) {
264 memset(mem, 0xe5, len);
270 lwres_free(void *arg, void *mem, size_t len) {
273 memset(mem, 0xa9, len);
277 static lwres_result_t
278 context_connect(lwres_context_t *ctx) {
281 struct sockaddr_in sin;
282 struct sockaddr_in6 sin6;
284 LWRES_SOCKADDR_LEN_T salen;
287 if (ctx->confdata.lwnext != 0) {
288 memcpy(&ctx->address, &ctx->confdata.lwservers[0],
289 sizeof(lwres_addr_t));
290 LWRES_LINK_INIT(&ctx->address, link);
292 /* The default is the IPv4 loopback address 127.0.0.1. */
293 memset(&ctx->address, 0, sizeof(ctx->address));
294 ctx->address.family = LWRES_ADDRTYPE_V4;
295 ctx->address.length = 4;
296 ctx->address.address[0] = 127;
297 ctx->address.address[1] = 0;
298 ctx->address.address[2] = 0;
299 ctx->address.address[3] = 1;
302 if (ctx->address.family == LWRES_ADDRTYPE_V4) {
303 memcpy(&sin.sin_addr, ctx->address.address,
304 sizeof(sin.sin_addr));
305 sin.sin_port = htons(lwres_udp_port);
306 sin.sin_family = AF_INET;
307 sa = (struct sockaddr *)&sin;
310 } else if (ctx->address.family == LWRES_ADDRTYPE_V6) {
311 memcpy(&sin6.sin6_addr, ctx->address.address,
312 sizeof(sin6.sin6_addr));
313 sin6.sin6_port = htons(lwres_udp_port);
314 sin6.sin6_family = AF_INET6;
315 sa = (struct sockaddr *)&sin6;
316 salen = sizeof(sin6);
319 return (LWRES_R_IOERROR);
324 s = socket(domain, SOCK_DGRAM, IPPROTO_UDP);
329 return (LWRES_R_IOERROR);
332 ret = connect(s, sa, salen);
338 return (LWRES_R_IOERROR);
341 MAKE_NONBLOCKING(s, ret);
347 return (LWRES_R_IOERROR);
352 return (LWRES_R_SUCCESS);
356 lwres_context_getsocket(lwres_context_t *ctx) {
361 lwres_context_send(lwres_context_t *ctx,
362 void *sendbase, int sendlen) {
364 lwres_result_t lwresult;
366 if (ctx->sock == -1) {
367 lwresult = context_connect(ctx);
368 if (lwresult != LWRES_R_SUCCESS)
372 ret = sendto(ctx->sock, sendbase, sendlen, 0, NULL, 0);
374 return (LWRES_R_IOERROR);
376 return (LWRES_R_IOERROR);
378 return (LWRES_R_SUCCESS);
382 lwres_context_recv(lwres_context_t *ctx,
383 void *recvbase, int recvlen,
386 LWRES_SOCKADDR_LEN_T fromlen;
387 struct sockaddr_in sin;
388 struct sockaddr_in6 sin6;
392 if (ctx->address.family == LWRES_ADDRTYPE_V4) {
393 sa = (struct sockaddr *)&sin;
394 fromlen = sizeof(sin);
396 sa = (struct sockaddr *)&sin6;
397 fromlen = sizeof(sin6);
401 * The address of fromlen is cast to void * to shut up compiler
402 * warnings, namely on systems that have the sixth parameter
403 * prototyped as a signed int when LWRES_SOCKADDR_LEN_T is
404 * defined as unsigned.
406 ret = recvfrom(ctx->sock, recvbase, recvlen, 0, sa, (void *)&fromlen);
409 return (LWRES_R_IOERROR);
412 return (LWRES_R_TOOLARGE);
415 * If we got something other than what we expect, have the caller
416 * wait for another packet. This can happen if an old result
417 * comes in, or if someone is sending us random stuff.
419 if (ctx->address.family == LWRES_ADDRTYPE_V4) {
420 if (fromlen != sizeof(sin)
421 || memcmp(&sin.sin_addr, ctx->address.address,
422 sizeof(sin.sin_addr)) != 0
423 || sin.sin_port != htons(lwres_udp_port))
424 return (LWRES_R_RETRY);
426 if (fromlen != sizeof(sin6)
427 || memcmp(&sin6.sin6_addr, ctx->address.address,
428 sizeof(sin6.sin6_addr)) != 0
429 || sin6.sin6_port != htons(lwres_udp_port))
430 return (LWRES_R_RETRY);
433 if (recvd_len != NULL)
436 return (LWRES_R_SUCCESS);
439 /*% performs I/O for the context ctx. */
441 lwres_context_sendrecv(lwres_context_t *ctx,
442 void *sendbase, int sendlen,
443 void *recvbase, int recvlen,
446 lwres_result_t result;
449 struct timeval timeout;
452 * Type of tv_sec is 32 bits long.
454 if (ctx->timeout <= 0x7FFFFFFFU)
455 timeout.tv_sec = (int)ctx->timeout;
457 timeout.tv_sec = 0x7FFFFFFF;
461 result = lwres_context_send(ctx, sendbase, sendlen);
462 if (result != LWRES_R_SUCCESS)
466 FD_SET(ctx->sock, &readfds);
467 ret2 = select(ctx->sock + 1, &readfds, NULL, NULL, &timeout);
470 * What happened with select?
473 return (LWRES_R_IOERROR);
475 return (LWRES_R_TIMEOUT);
477 result = lwres_context_recv(ctx, recvbase, recvlen, recvd_len);
478 if (result == LWRES_R_RETRY)