1 /* $NetBSD: context.c,v 1.7 2014/12/10 04:38:02 christos Exp $ */
4 * Copyright (C) 2004, 2005, 2007-2009, 2012-2014 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000, 2001, 2003 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: context.c,v 1.55 2009/09/02 23:48:03 tbox Exp */
23 lwres_context_create() creates a #lwres_context_t structure for use in
24 lightweight resolver operations. It holds a socket and other data
25 needed for communicating with a resolver daemon. The new
26 lwres_context_t is returned through contextp, a pointer to a
27 lwres_context_t pointer. This lwres_context_t pointer must initially
28 be NULL, and is modified to point to the newly created
31 When the lightweight resolver needs to perform dynamic memory
32 allocation, it will call malloc_function to allocate memory and
33 free_function to free it. If malloc_function and free_function are
34 NULL, memory is allocated using malloc and free. It is not
35 permitted to have a NULL malloc_function and a non-NULL free_function
36 or vice versa. arg is passed as the first parameter to the memory
37 allocation functions. If malloc_function and free_function are NULL,
38 arg is unused and should be passed as NULL.
40 Once memory for the structure has been allocated, it is initialized
41 using lwres_conf_init() and returned via *contextp.
43 lwres_context_destroy() destroys a #lwres_context_t, closing its
44 socket. contextp is a pointer to a pointer to the context that is to
45 be destroyed. The pointer will be set to NULL when the context has
48 The context holds a serial number that is used to identify resolver
49 request packets and associate responses with the corresponding
50 requests. This serial number is controlled using
51 lwres_context_initserial() and lwres_context_nextserial().
52 lwres_context_initserial() sets the serial number for context *ctx to
53 serial. lwres_context_nextserial() increments the serial number and
54 returns the previous value.
56 Memory for a lightweight resolver context is allocated and freed using
57 lwres_context_allocmem() and lwres_context_freemem(). These use
58 whatever allocations were defined when the context was created with
59 lwres_context_create(). lwres_context_allocmem() allocates len bytes
60 of memory and if successful returns a pointer to the allocated
61 storage. lwres_context_freemem() frees len bytes of space starting at
64 lwres_context_sendrecv() performs I/O for the context ctx. Data are
65 read and written from the context's socket. It writes data from
66 sendbase -- typically a lightweight resolver query packet -- and waits
67 for a reply which is copied to the receive buffer at recvbase. The
68 number of bytes that were written to this receive buffer is returned
71 \section context_return Return Values
73 lwres_context_create() returns #LWRES_R_NOMEMORY if memory for the
74 struct lwres_context could not be allocated, #LWRES_R_SUCCESS
77 Successful calls to the memory allocator lwres_context_allocmem()
78 return a pointer to the start of the allocated space. It returns NULL
79 if memory could not be allocated.
81 #LWRES_R_SUCCESS is returned when lwres_context_sendrecv() completes
82 successfully. #LWRES_R_IOERROR is returned if an I/O error occurs and
83 #LWRES_R_TIMEOUT is returned if lwres_context_sendrecv() times out
84 waiting for a response.
86 \section context_see See Also
88 lwres_conf_init(), malloc, free.
99 #include <lwres/lwres.h>
100 #include <lwres/net.h>
101 #include <lwres/platform.h>
103 #ifdef LWRES_PLATFORM_NEEDSYSSELECTH
104 #include <sys/select.h>
107 #include "context_p.h"
108 #include "assert_p.h"
111 * Some systems define the socket length argument as an int, some as size_t,
112 * some as socklen_t. The last is what the current POSIX standard mandates.
113 * This definition is here so it can be portable but easily changed if needed.
115 #ifndef LWRES_SOCKADDR_LEN_T
116 #define LWRES_SOCKADDR_LEN_T unsigned int
120 * Make a socket nonblocking.
122 #ifndef MAKE_NONBLOCKING
123 #define MAKE_NONBLOCKING(sd, retval) \
125 retval = fcntl(sd, F_GETFL, 0); \
126 if (retval != -1) { \
127 retval |= O_NONBLOCK; \
128 retval = fcntl(sd, F_SETFL, retval); \
130 } while (/*CONSTCOND*/0)
133 LIBLWRES_EXTERNAL_DATA lwres_uint16_t lwres_udp_port
= LWRES_UDP_PORT
;
134 LIBLWRES_EXTERNAL_DATA
const char *lwres_resolv_conf
= LWRES_RESOLV_CONF
;
137 lwres_malloc(void *, size_t);
140 lwres_free(void *, void *, size_t);
145 static lwres_result_t
146 context_connect(lwres_context_t
*);
149 * Creates a #lwres_context_t structure for use in
150 * lightweight resolver operations.
153 lwres_context_create(lwres_context_t
**contextp
, void *arg
,
154 lwres_malloc_t malloc_function
,
155 lwres_free_t free_function
,
158 lwres_context_t
*ctx
;
160 REQUIRE(contextp
!= NULL
&& *contextp
== NULL
);
163 * If we were not given anything special to use, use our own
164 * functions. These are just wrappers around malloc() and free().
166 if (malloc_function
== NULL
|| free_function
== NULL
) {
167 REQUIRE(malloc_function
== NULL
);
168 REQUIRE(free_function
== NULL
);
169 malloc_function
= lwres_malloc
;
170 free_function
= lwres_free
;
173 ctx
= malloc_function(arg
, sizeof(lwres_context_t
));
175 return (LWRES_R_NOMEMORY
);
178 * Set up the context.
180 ctx
->malloc
= malloc_function
;
181 ctx
->free
= free_function
;
185 ctx
->timeout
= LWRES_DEFAULT_TIMEOUT
;
187 ctx
->serial
= time(NULL
); /* XXXMLG or BEW */
189 ctx
->serial
= _time32(NULL
);
194 if ((flags
& (LWRES_CONTEXT_USEIPV4
| LWRES_CONTEXT_USEIPV6
)) ==
195 LWRES_CONTEXT_USEIPV6
) {
198 if ((flags
& (LWRES_CONTEXT_USEIPV4
| LWRES_CONTEXT_USEIPV6
)) ==
199 LWRES_CONTEXT_USEIPV4
) {
204 * Init resolv.conf bits.
206 lwres_conf_init(ctx
);
209 return (LWRES_R_SUCCESS
);
213 Destroys a #lwres_context_t, closing its socket.
214 contextp is a pointer to a pointer to the context that is
215 to be destroyed. The pointer will be set to NULL
216 when the context has been destroyed.
219 lwres_context_destroy(lwres_context_t
**contextp
) {
220 lwres_context_t
*ctx
;
222 REQUIRE(contextp
!= NULL
&& *contextp
!= NULL
);
227 if (ctx
->sock
!= -1) {
231 (void)close(ctx
->sock
);
235 CTXFREE(ctx
, sizeof(lwres_context_t
));
237 /*% Increments the serial number and returns the previous value. */
239 lwres_context_nextserial(lwres_context_t
*ctx
) {
240 REQUIRE(ctx
!= NULL
);
242 return (ctx
->serial
++);
245 /*% Sets the serial number for context *ctx to serial. */
247 lwres_context_initserial(lwres_context_t
*ctx
, lwres_uint32_t serial
) {
248 REQUIRE(ctx
!= NULL
);
250 ctx
->serial
= serial
;
253 /*% Frees len bytes of space starting at location mem. */
255 lwres_context_freemem(lwres_context_t
*ctx
, void *mem
, size_t len
) {
256 REQUIRE(mem
!= NULL
);
262 /*% Allocates len bytes of memory and if successful returns a pointer to the allocated storage. */
264 lwres_context_allocmem(lwres_context_t
*ctx
, size_t len
) {
267 return (CTXMALLOC(len
));
271 lwres_malloc(void *arg
, size_t len
) {
280 memset(mem
, 0xe5, len
);
286 lwres_free(void *arg
, void *mem
, size_t len
) {
289 memset(mem
, 0xa9, len
);
293 static lwres_result_t
294 context_connect(lwres_context_t
*ctx
) {
301 struct sockaddr_in sin
;
302 struct sockaddr_in6 sin6
;
304 LWRES_SOCKADDR_LEN_T salen
;
307 if (ctx
->confdata
.lwnext
!= 0) {
308 memmove(&ctx
->address
, &ctx
->confdata
.lwservers
[0],
309 sizeof(lwres_addr_t
));
310 LWRES_LINK_INIT(&ctx
->address
, link
);
312 /* The default is the IPv4 loopback address 127.0.0.1. */
313 memset(&ctx
->address
, 0, sizeof(ctx
->address
));
314 ctx
->address
.family
= LWRES_ADDRTYPE_V4
;
315 ctx
->address
.length
= 4;
316 ctx
->address
.address
[0] = 127;
317 ctx
->address
.address
[1] = 0;
318 ctx
->address
.address
[2] = 0;
319 ctx
->address
.address
[3] = 1;
322 if (ctx
->address
.family
== LWRES_ADDRTYPE_V4
) {
323 memmove(&sin
.sin_addr
, ctx
->address
.address
,
324 sizeof(sin
.sin_addr
));
325 sin
.sin_port
= htons(lwres_udp_port
);
326 sin
.sin_family
= AF_INET
;
327 sa
= (struct sockaddr
*)&sin
;
330 } else if (ctx
->address
.family
== LWRES_ADDRTYPE_V6
) {
331 memmove(&sin6
.sin6_addr
, ctx
->address
.address
,
332 sizeof(sin6
.sin6_addr
));
333 sin6
.sin6_port
= htons(lwres_udp_port
);
334 sin6
.sin6_family
= AF_INET6
;
335 sa
= (struct sockaddr
*)&sin6
;
336 salen
= sizeof(sin6
);
339 return (LWRES_R_IOERROR
);
344 s
= socket(domain
, SOCK_DGRAM
, IPPROTO_UDP
);
347 return (LWRES_R_IOERROR
);
350 if (s
== INVALID_SOCKET
) {
352 return (LWRES_R_IOERROR
);
356 ret
= connect(s
, sa
, salen
);
362 return (LWRES_R_IOERROR
);
365 MAKE_NONBLOCKING(s
, ret
);
371 return (LWRES_R_IOERROR
);
376 return (LWRES_R_SUCCESS
);
380 lwres_context_getsocket(lwres_context_t
*ctx
) {
385 lwres_context_send(lwres_context_t
*ctx
,
386 void *sendbase
, int sendlen
) {
388 lwres_result_t lwresult
;
390 if (ctx
->sock
== -1) {
391 lwresult
= context_connect(ctx
);
392 if (lwresult
!= LWRES_R_SUCCESS
)
394 INSIST(ctx
->sock
>= 0);
397 ret
= sendto(ctx
->sock
, sendbase
, sendlen
, 0, NULL
, 0);
399 return (LWRES_R_IOERROR
);
401 return (LWRES_R_IOERROR
);
403 return (LWRES_R_SUCCESS
);
407 lwres_context_recv(lwres_context_t
*ctx
,
408 void *recvbase
, int recvlen
,
411 LWRES_SOCKADDR_LEN_T fromlen
;
412 struct sockaddr_in sin
;
413 struct sockaddr_in6 sin6
;
417 if (ctx
->address
.family
== LWRES_ADDRTYPE_V4
) {
418 sa
= (struct sockaddr
*)&sin
;
419 fromlen
= sizeof(sin
);
421 sa
= (struct sockaddr
*)&sin6
;
422 fromlen
= sizeof(sin6
);
426 * The address of fromlen is cast to void * to shut up compiler
427 * warnings, namely on systems that have the sixth parameter
428 * prototyped as a signed int when LWRES_SOCKADDR_LEN_T is
429 * defined as unsigned.
431 ret
= recvfrom(ctx
->sock
, recvbase
, recvlen
, 0, sa
, (void *)&fromlen
);
434 return (LWRES_R_IOERROR
);
437 return (LWRES_R_TOOLARGE
);
440 * If we got something other than what we expect, have the caller
441 * wait for another packet. This can happen if an old result
442 * comes in, or if someone is sending us random stuff.
444 if (ctx
->address
.family
== LWRES_ADDRTYPE_V4
) {
445 if (fromlen
!= sizeof(sin
)
446 || memcmp(&sin
.sin_addr
, ctx
->address
.address
,
447 sizeof(sin
.sin_addr
)) != 0
448 || sin
.sin_port
!= htons(lwres_udp_port
))
449 return (LWRES_R_RETRY
);
451 if (fromlen
!= sizeof(sin6
)
452 || memcmp(&sin6
.sin6_addr
, ctx
->address
.address
,
453 sizeof(sin6
.sin6_addr
)) != 0
454 || sin6
.sin6_port
!= htons(lwres_udp_port
))
455 return (LWRES_R_RETRY
);
458 if (recvd_len
!= NULL
)
461 return (LWRES_R_SUCCESS
);
464 /*% performs I/O for the context ctx. */
466 lwres_context_sendrecv(lwres_context_t
*ctx
,
467 void *sendbase
, int sendlen
,
468 void *recvbase
, int recvlen
,
471 lwres_result_t result
;
474 struct timeval timeout
;
477 * Type of tv_sec is 32 bits long.
479 if (ctx
->timeout
<= 0x7FFFFFFFU
)
480 timeout
.tv_sec
= (int)ctx
->timeout
;
482 timeout
.tv_sec
= 0x7FFFFFFF;
486 result
= lwres_context_send(ctx
, sendbase
, sendlen
);
487 if (result
!= LWRES_R_SUCCESS
)
491 * If this is not checked, select() can overflow,
492 * causing corruption elsewhere.
494 if (ctx
->sock
>= (int)FD_SETSIZE
) {
497 return (LWRES_R_IOERROR
);
502 FD_SET(ctx
->sock
, &readfds
);
503 ret2
= select(ctx
->sock
+ 1, &readfds
, NULL
, NULL
, &timeout
);
506 * What happened with select?
509 return (LWRES_R_IOERROR
);
511 return (LWRES_R_TIMEOUT
);
513 result
= lwres_context_recv(ctx
, recvbase
, recvlen
, recvd_len
);
514 if (result
== LWRES_R_RETRY
)