2006-06-13 23:49:56 +02:00
/* $Id$ */
2006-08-04 20:24:25 +02:00
/* The original version of this module was written by Adam Langley; for
* a history of modifications , check out the subversion logs .
*
2006-08-14 23:44:45 +02:00
* When editing this module , try to keep it re - mergeable by Adam . Don ' t
2006-08-04 20:24:25 +02:00
* reformat the whitespace , add Tor dependencies , or so on .
*
* TODO :
2006-12-15 07:12:14 +01:00
* - Replace all externally visible magic numbers with # defined constants .
* - Write documentation for APIs of all external functions .
2006-08-04 20:24:25 +02:00
*/
2006-06-13 23:49:56 +02:00
2006-06-03 21:49:42 +02:00
/* Async DNS Library
* Adam Langley < agl @ imperialviolet . org >
2006-09-19 23:37:03 +02:00
* Public Domain code
2006-06-03 21:49:42 +02:00
*
* This software is Public Domain . To view a copy of the public domain dedication ,
* visit http : //creativecommons.org/licenses/publicdomain/ or send a letter to
* Creative Commons , 559 Nathan Abbott Way , Stanford , California 94305 , USA .
*
* I ask and expect , but do not require , that all derivative works contain an
* attribution similar to :
2006-12-15 07:12:14 +01:00
* Parts developed by Adam Langley < agl @ imperialviolet . org >
2006-06-03 21:49:42 +02:00
*
* You may wish to replace the word " Parts " with something else depending on
* the amount of original code .
*
* ( Derivative works does not include programs which link against , run or include
* the source verbatim in their source distributions )
*
* Version : 0.1 b
*/
# include "eventdns_tor.h"
2007-06-17 19:43:25 +02:00
# include <sys/types.h>
2007-09-10 16:05:12 +02:00
/* #define NDEBUG */
2006-06-03 21:49:42 +02:00
# ifndef DNS_USE_CPU_CLOCK_FOR_ID
# ifndef DNS_USE_GETTIMEOFDAY_FOR_ID
# ifndef DNS_USE_OPENSSL_FOR_ID
# error Must configure at least one id generation method.
2006-08-14 10:55:41 +02:00
# error Please see the documentation.
2006-06-03 21:49:42 +02:00
# endif
# endif
# endif
2007-09-10 16:05:12 +02:00
/* #define _POSIX_C_SOURCE 200507 */
2006-06-03 21:49:42 +02:00
# define _GNU_SOURCE
# ifdef DNS_USE_CPU_CLOCK_FOR_ID
# ifdef DNS_USE_OPENSSL_FOR_ID
# error Multiple id options selected
# endif
# ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
# error Multiple id options selected
# endif
# include <time.h>
# endif
# ifdef DNS_USE_OPENSSL_FOR_ID
# ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
# error Multiple id options selected
# endif
# include <openssl/rand.h>
# endif
# define _FORTIFY_SOURCE 3
# include <string.h>
2007-04-30 16:52:07 +02:00
# ifdef HAVE_FCNTL_H
2006-06-03 21:49:42 +02:00
# include <fcntl.h>
2007-04-30 16:52:07 +02:00
# endif
# ifdef HAVE_SYS_TIME_H
2006-06-03 21:49:42 +02:00
# include <sys/time.h>
2007-04-30 16:52:07 +02:00
# endif
2006-12-15 07:12:01 +01:00
# ifdef HAVE_STDINT_H
# include <stdint.h>
# endif
2006-06-03 21:49:42 +02:00
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include <assert.h>
2007-04-30 16:52:07 +02:00
# ifdef HAVE_UNISTD_H
2006-06-03 21:49:42 +02:00
# include <unistd.h>
2007-04-30 16:52:07 +02:00
# endif
# ifdef HAVE_LIMITS_H
2006-06-03 21:49:42 +02:00
# include <limits.h>
2007-04-30 16:52:07 +02:00
# endif
2006-06-03 21:49:42 +02:00
# include <sys/stat.h>
2006-06-13 23:49:56 +02:00
# include <ctype.h>
2006-07-05 23:28:37 +02:00
# include <stdio.h>
# include <stdarg.h>
2006-06-03 21:49:42 +02:00
2006-12-15 07:12:01 +01:00
# include "eventdns.h"
2006-09-06 22:33:28 +02:00
# ifdef WIN32
# include <windows.h>
# include <winsock2.h>
# include <iphlpapi.h>
# else
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# endif
2007-01-05 23:12:15 +01:00
# ifdef HAVE_NETINET_IN6_H
2007-07-19 22:00:45 +02:00
# include <netinet6/in6.h>
2007-01-05 23:12:15 +01:00
# endif
2006-12-18 04:02:30 +01:00
# ifdef WIN32
typedef int socklen_t ;
# endif
2006-09-24 19:52:23 +02:00
# define EVDNS_LOG_DEBUG 0
# define EVDNS_LOG_WARN 1
2006-08-28 10:24:36 +02:00
2006-06-03 21:49:42 +02:00
# ifndef HOST_NAME_MAX
# define HOST_NAME_MAX 255
# endif
# ifndef NDEBUG
# include <stdio.h>
# endif
2007-08-17 23:46:34 +02:00
/*XXXX020 for debugging possible memory leaks. */
2007-07-23 06:18:20 +02:00
# define malloc(x) tor_malloc(x)
# define realloc(x,y) tor_realloc((x),(y))
# define free(x) tor_free(x)
# define _free(x) _tor_free(x)
2006-06-03 21:49:42 +02:00
# undef MIN
# define MIN(a,b) ((a)<(b)?(a):(b))
2006-09-03 04:13:52 +02:00
#if 0
2006-06-03 21:49:42 +02:00
# ifdef __USE_ISOC99B
2007-09-10 16:05:12 +02:00
/* libevent doesn't work without this */
2006-06-03 21:49:42 +02:00
typedef uint8_t u_char ;
typedef unsigned int uint ;
# endif
2006-09-03 04:13:52 +02:00
# endif
2006-06-03 21:49:42 +02:00
# include <event.h>
# define u64 uint64_t
# define u32 uint32_t
# define u16 uint16_t
2007-01-10 20:49:21 +01:00
# define u8 uint8_t
2006-06-03 21:49:42 +02:00
2007-09-10 16:05:12 +02:00
# define MAX_ADDRS 4 /* maximum number of addresses from a single packet */
/* which we bother recording */
2006-06-03 21:49:42 +02:00
2006-12-12 03:56:35 +01:00
# define TYPE_A EVDNS_TYPE_A
# define TYPE_PTR EVDNS_TYPE_PTR
# define TYPE_AAAA EVDNS_TYPE_AAAA
2006-07-07 19:31:56 +02:00
2006-12-12 03:56:35 +01:00
# define CLASS_INET EVDNS_CLASS_INET
2006-06-03 21:49:42 +02:00
2007-01-10 20:49:21 +01:00
# define CLEAR(x) do { memset((x), 0, sizeof(*(x))); } while(0)
2006-06-03 21:49:42 +02:00
struct request {
2007-09-10 16:05:12 +02:00
u8 * request ; /* the dns packet data */
2006-09-03 04:13:52 +02:00
unsigned int request_len ;
2006-09-24 19:52:23 +02:00
int reissue_count ;
2007-09-10 16:05:12 +02:00
int tx_count ; /* the number of times that this packet has been sent */
unsigned int request_type ; /* TYPE_PTR or TYPE_A */
void * user_pointer ; /* the pointer given to us for this request */
2006-09-24 19:52:23 +02:00
evdns_callback_type user_callback ;
2007-09-10 16:05:12 +02:00
struct nameserver * ns ; /* the server which we last sent it */
2006-06-03 21:49:42 +02:00
2007-09-10 16:05:12 +02:00
/* elements used by the searching code */
2006-06-03 21:49:42 +02:00
int search_index ;
struct search_state * search_state ;
2007-09-10 16:05:12 +02:00
char * search_origname ; /* needs to be free()ed */
2006-06-03 21:49:42 +02:00
int search_flags ;
2007-09-10 16:05:12 +02:00
/* these objects are kept in a circular list */
2006-06-03 21:49:42 +02:00
struct request * next , * prev ;
struct event timeout_event ;
2007-09-10 16:05:12 +02:00
u16 trans_id ; /* the transaction id */
char request_appended ; /* true if the request pointer is data which follows this struct */
char transmit_me ; /* needs to be transmitted */
2006-06-03 21:49:42 +02:00
} ;
2006-12-12 04:48:46 +01:00
# ifndef HAVE_STRUCT_IN6_ADDR
struct in6_addr {
2006-12-15 07:12:14 +01:00
u8 s6_addr [ 16 ] ;
2006-12-12 04:48:46 +01:00
} ;
# endif
2006-08-16 20:47:24 +02:00
struct reply {
2006-09-24 19:52:23 +02:00
unsigned int type ;
unsigned int have_answer ;
2006-08-16 20:47:24 +02:00
union {
struct {
u32 addrcount ;
u32 addresses [ MAX_ADDRS ] ;
} a ;
2006-12-12 04:48:46 +01:00
struct {
u32 addrcount ;
struct in6_addr addresses [ MAX_ADDRS ] ;
} aaaa ;
2006-08-16 20:47:24 +02:00
struct {
char name [ HOST_NAME_MAX ] ;
} ptr ;
} data ;
} ;
2006-06-03 21:49:42 +02:00
struct nameserver {
2007-09-10 16:05:12 +02:00
int socket ; /* a connected UDP socket */
2006-06-03 21:49:42 +02:00
u32 address ;
2007-09-10 16:05:12 +02:00
int failed_times ; /* number of times which we have given this server a chance */
int timedout ; /* number of times in a row a request has timed out */
2006-06-03 21:49:42 +02:00
struct event event ;
2007-09-10 16:05:12 +02:00
/* these objects are kept in a circular list */
2006-06-03 21:49:42 +02:00
struct nameserver * next , * prev ;
2007-09-10 16:05:12 +02:00
struct event timeout_event ; /* used to keep the timeout for */
/* when we next probe this server. */
/* Valid if state == 0 */
char state ; /* zero if we think that this server is down */
char choked ; /* true if we have an EAGAIN from this server's socket */
char write_waiting ; /* true if we are waiting for EV_WRITE events */
2006-06-03 21:49:42 +02:00
} ;
static struct request * req_head = NULL , * req_waiting_head = NULL ;
static struct nameserver * server_head = NULL ;
2007-09-10 16:05:12 +02:00
/* Represents a local port where we're listening for DNS requests. Right now, */
/* only UDP is supported. */
2006-12-12 03:56:20 +01:00
struct evdns_server_port {
2007-09-10 16:05:12 +02:00
int socket ; /* socket we use to read queries and write replies. */
int refcnt ; /* reference count. */
char choked ; /* Are we currently blocked from writing? */
char closing ; /* Are we trying to close this port, pending writes? */
evdns_request_callback_fn_type user_callback ; /* Fn to handle requests */
void * user_data ; /* Opaque pointer passed to user_callback */
struct event event ; /* Read/write event */
/* circular list of replies that we want to write. */
2006-12-12 03:49:45 +01:00
struct server_request * pending_replies ;
} ;
2007-09-10 16:05:12 +02:00
/* Represents part of a reply being built. (That is, a single RR.) */
2006-12-12 03:57:02 +01:00
struct server_reply_item {
2007-09-10 16:05:12 +02:00
struct server_reply_item * next ; /* next item in sequence. */
char * name ; /* name part of the RR */
u16 type : 16 ; /* The RR type */
u16 class : 16 ; /* The RR class (usually CLASS_INET) */
u32 ttl ; /* The RR TTL */
char is_name ; /* True iff data is a label */
u16 datalen ; /* Length of data; -1 if data is a label */
void * data ; /* The contents of the RR */
2006-12-12 03:49:45 +01:00
} ;
2007-09-10 16:05:12 +02:00
/* Represents a request that we've received as a DNS server, and holds */
/* the components of the reply as we're constructing it. */
2006-12-12 03:49:45 +01:00
struct server_request {
2007-09-10 16:05:12 +02:00
/* Pointers to the next and previous entries on the list of replies */
/* that we're waiting to write. Only set if we have tried to respond */
/* and gotten EAGAIN. */
2006-12-12 03:49:45 +01:00
struct server_request * next_pending ;
struct server_request * prev_pending ;
2007-09-10 16:05:12 +02:00
u16 trans_id ; /* Transaction id. */
struct evdns_server_port * port ; /* Which port received this request on? */
struct sockaddr_storage addr ; /* Where to send the response */
socklen_t addrlen ; /* length of addr */
2006-12-12 03:49:45 +01:00
2007-09-10 16:05:12 +02:00
int n_answer ; /* how many answer RRs have been set? */
int n_authority ; /* how many authority RRs have been set? */
int n_additional ; /* how many additional RRs have been set? */
2006-12-12 03:56:20 +01:00
2007-09-10 16:05:12 +02:00
struct server_reply_item * answer ; /* linked list of answer RRs */
struct server_reply_item * authority ; /* linked list of authority RRs */
struct server_reply_item * additional ; /* linked list of additional RRs */
2006-12-12 03:49:45 +01:00
2007-09-10 16:05:12 +02:00
/* Constructed response. Only set once we're ready to send a reply. */
/* Once this is set, the RR fields are cleared, and no more should be set. */
2006-12-12 03:56:20 +01:00
char * response ;
size_t response_len ;
2007-09-10 16:05:12 +02:00
/* Caller-visible fields: flags, questions. */
2006-12-12 03:56:41 +01:00
struct evdns_server_request base ;
2006-12-12 03:49:45 +01:00
} ;
2006-12-12 03:57:02 +01:00
2007-09-10 16:05:12 +02:00
/* helper macro */
2006-12-12 03:49:45 +01:00
# define OFFSET_OF(st, member) ((off_t) (((char*)&((st*)0)->member)-(char*)0))
2007-09-10 16:05:12 +02:00
/* Given a pointer to an evdns_server_request, get the corresponding */
/* server_request. */
2006-12-12 03:49:45 +01:00
# define TO_SERVER_REQUEST(base_ptr) \
( ( struct server_request * ) \
( ( ( char * ) ( base_ptr ) - OFFSET_OF ( struct server_request , base ) ) ) )
2007-09-10 16:05:12 +02:00
/* The number of good nameservers that we have */
2006-06-03 21:49:42 +02:00
static int global_good_nameservers = 0 ;
2007-09-10 16:05:12 +02:00
/* inflight requests are contained in the req_head list */
/* and are actually going out across the network */
2006-06-03 21:49:42 +02:00
static int global_requests_inflight = 0 ;
2007-09-10 16:05:12 +02:00
/* requests which aren't inflight are in the waiting list */
/* and are counted here */
2006-06-03 21:49:42 +02:00
static int global_requests_waiting = 0 ;
static int global_max_requests_inflight = 64 ;
2007-09-10 16:05:12 +02:00
static struct timeval global_timeout = { 5 , 0 } ; /* 5 seconds */
static int global_max_reissues = 1 ; /* a reissue occurs when we get some errors from the server */
static int global_max_retransmits = 3 ; /* number of times we'll retransmit a request which timed out */
/* number of timeouts in a row before we consider this server to be down */
2006-06-03 21:49:42 +02:00
static int global_max_nameserver_timeout = 3 ;
2007-09-10 16:05:12 +02:00
/* These are the timeout values for nameservers. If we find a nameserver is down */
/* we try to probe it at intervals as given below. Values are in seconds. */
2006-06-03 21:49:42 +02:00
static const struct timeval global_nameserver_timeouts [ ] = { { 10 , 0 } , { 60 , 0 } , { 300 , 0 } , { 900 , 0 } , { 3600 , 0 } } ;
static const int global_nameserver_timeouts_length = sizeof ( global_nameserver_timeouts ) / sizeof ( struct timeval ) ;
2006-12-28 22:29:31 +01:00
static const char * const evdns_error_strings [ ] = { " no error " , " The name server was unable to interpret the query " , " The name server suffered an internal error " , " The requested domain name does not exist " , " The name server refused to reply to the request " } ;
2006-06-03 21:49:42 +02:00
2006-06-04 00:05:23 +02:00
static struct nameserver * nameserver_pick ( void ) ;
2006-09-24 19:52:23 +02:00
static void evdns_request_insert ( struct request * req , struct request * * head ) ;
2006-06-03 21:49:42 +02:00
static void nameserver_ready_callback ( int fd , short events , void * arg ) ;
2006-09-24 19:52:23 +02:00
static int evdns_transmit ( void ) ;
static int evdns_request_transmit ( struct request * req ) ;
2006-06-03 21:49:42 +02:00
static void nameserver_send_probe ( struct nameserver * const ns ) ;
static void search_request_finished ( struct request * const ) ;
static int search_try_next ( struct request * const req ) ;
2006-09-24 19:52:23 +02:00
static int search_request_new ( int type , const char * const name , int flags , evdns_callback_type user_callback , void * user_arg ) ;
static void evdns_requests_pump_waiting_queue ( void ) ;
2006-06-04 00:05:23 +02:00
static u16 transaction_id_pick ( void ) ;
2006-10-05 21:22:26 +02:00
static struct request * request_new ( int type , const char * name , int flags , evdns_callback_type callback , void * ptr ) ;
2006-06-03 21:49:42 +02:00
static void request_submit ( struct request * req ) ;
2006-12-12 03:56:54 +01:00
static int server_request_free ( struct server_request * req ) ;
static void server_request_free_answers ( struct server_request * req ) ;
static void server_port_free ( struct evdns_server_port * port ) ;
static void server_port_ready_callback ( int fd , short events , void * arg ) ;
2007-01-29 19:13:42 +01:00
static int strtoint ( const char * const str ) ;
2006-12-15 07:12:01 +01:00
# ifdef WIN32
2006-06-05 01:23:53 +02:00
static int
2006-09-24 19:52:23 +02:00
last_error ( int sock )
{
2006-06-05 01:23:53 +02:00
int optval , optvallen = sizeof ( optval ) ;
int err = WSAGetLastError ( ) ;
if ( err = = WSAEWOULDBLOCK & & sock > = 0 ) {
if ( getsockopt ( sock , SOL_SOCKET , SO_ERROR , ( void * ) & optval ,
2006-09-07 18:32:06 +02:00
& optvallen ) )
2006-06-05 01:23:53 +02:00
return err ;
if ( optval )
return optval ;
}
return err ;
}
static int
2006-09-24 19:52:23 +02:00
error_is_eagain ( int err )
{
2006-06-05 01:23:53 +02:00
return err = = EAGAIN | | err = = WSAEWOULDBLOCK ;
}
2006-09-06 22:33:28 +02:00
static int
2006-09-24 19:52:23 +02:00
inet_aton ( const char * c , struct in_addr * addr )
{
2006-09-07 18:32:06 +02:00
uint32_t r ;
if ( strcmp ( c , " 255.255.255.255 " ) = = 0 ) {
addr - > s_addr = 0xffffffffu ;
} else {
r = inet_addr ( c ) ;
if ( r = = INADDR_NONE )
return 0 ;
addr - > s_addr = r ;
}
return 1 ;
2006-09-06 22:33:28 +02:00
}
2006-08-04 20:26:13 +02:00
# define CLOSE_SOCKET(x) closesocket(x)
2006-06-05 01:23:53 +02:00
# else
# define last_error(sock) (errno)
2006-07-05 23:28:37 +02:00
# define error_is_eagain(err) ((err) == EAGAIN)
2006-08-04 20:26:13 +02:00
# define CLOSE_SOCKET(x) close(x)
2006-06-05 01:23:53 +02:00
# endif
2006-06-13 23:49:56 +02:00
# define ISSPACE(c) isspace((int)(unsigned char)(c))
# define ISDIGIT(c) isdigit((int)(unsigned char)(c))
2006-06-05 03:59:12 +02:00
# ifndef NDEBUG
static const char *
2006-09-24 19:52:23 +02:00
debug_ntoa ( u32 address )
{
2006-06-05 03:59:12 +02:00
static char buf [ 32 ] ;
u32 a = ntohl ( address ) ;
2007-01-16 00:56:38 +01:00
snprintf ( buf , sizeof ( buf ) , " %d.%d.%d.%d " ,
2006-09-07 18:32:06 +02:00
( int ) ( u8 ) ( ( a > > 24 ) & 0xff ) ,
( int ) ( u8 ) ( ( a > > 16 ) & 0xff ) ,
( int ) ( u8 ) ( ( a > > 8 ) & 0xff ) ,
( int ) ( u8 ) ( ( a ) & 0xff ) ) ;
2006-06-05 03:59:12 +02:00
return buf ;
}
# endif
2006-09-24 19:52:23 +02:00
static evdns_debug_log_fn_type evdns_log_fn = NULL ;
2006-07-05 23:28:37 +02:00
void
2006-09-24 19:52:23 +02:00
evdns_set_log_fn ( evdns_debug_log_fn_type fn )
{
evdns_log_fn = fn ;
2006-07-05 23:28:37 +02:00
}
2006-08-04 20:26:40 +02:00
# ifdef __GNUC__
2006-09-24 19:52:23 +02:00
# define EVDNS_LOG_CHECK __attribute__ ((format(printf, 2, 3)))
2006-08-04 20:26:40 +02:00
# else
2006-09-24 19:52:23 +02:00
# define EVDNS_LOG_CHECK
2006-08-04 20:26:40 +02:00
# endif
2006-09-24 19:52:23 +02:00
static void _evdns_log ( int warn , const char * fmt , . . . ) EVDNS_LOG_CHECK ;
2006-07-05 23:28:37 +02:00
static void
2006-12-12 06:45:19 +01:00
_evdns_log ( int warn , const char * fmt , . . . )
{
2006-09-07 18:32:06 +02:00
va_list args ;
static char buf [ 512 ] ;
2006-09-24 19:52:23 +02:00
if ( ! evdns_log_fn )
2006-09-07 18:32:06 +02:00
return ;
va_start ( args , fmt ) ;
2006-12-15 07:12:01 +01:00
# ifdef WIN32
2006-09-07 18:32:06 +02:00
_vsnprintf ( buf , sizeof ( buf ) , fmt , args ) ;
2006-07-05 23:28:37 +02:00
# else
2006-09-07 18:32:06 +02:00
vsnprintf ( buf , sizeof ( buf ) , fmt , args ) ;
2006-07-05 23:28:37 +02:00
# endif
2006-09-07 18:32:06 +02:00
buf [ sizeof ( buf ) - 1 ] = ' \0 ' ;
2006-09-24 19:52:23 +02:00
evdns_log_fn ( warn , buf ) ;
2006-09-07 18:32:06 +02:00
va_end ( args ) ;
2006-07-05 23:28:37 +02:00
}
2006-09-24 19:52:23 +02:00
# define log _evdns_log
2006-07-05 23:28:37 +02:00
2007-09-10 16:05:12 +02:00
/* This walks the list of inflight requests to find the */
/* one with a matching transaction id. Returns NULL on */
/* failure */
2006-06-03 21:49:42 +02:00
static struct request *
request_find_from_trans_id ( u16 trans_id ) {
struct request * req = req_head , * const started_at = req_head ;
if ( req ) {
do {
if ( req - > trans_id = = trans_id ) return req ;
req = req - > next ;
} while ( req ! = started_at ) ;
}
return NULL ;
}
2007-09-10 16:05:12 +02:00
/* a libevent callback function which is called when a nameserver */
/* has gone down and we want to test if it has came back to life yet */
2006-06-03 21:49:42 +02:00
static void
nameserver_prod_callback ( int fd , short events , void * arg ) {
struct nameserver * const ns = ( struct nameserver * ) arg ;
2006-09-07 18:32:06 +02:00
( void ) fd ;
( void ) events ;
2006-06-03 21:49:42 +02:00
nameserver_send_probe ( ns ) ;
}
2007-09-10 16:05:12 +02:00
/* a libevent callback which is called when a nameserver probe (to see if */
/* it has come back to life) times out. We increment the count of failed_times */
/* and wait longer to send the next probe packet. */
2006-06-03 21:49:42 +02:00
static void
nameserver_probe_failed ( struct nameserver * const ns ) {
2006-06-04 00:05:23 +02:00
const struct timeval * timeout ;
2006-08-04 20:27:10 +02:00
( void ) evtimer_del ( & ns - > timeout_event ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( & ns - > timeout_event ) ;
2006-08-31 01:34:56 +02:00
if ( ns - > state = = 1 ) {
2007-09-10 16:05:12 +02:00
/* This can happen if the nameserver acts in a way which makes us mark */
/* it as bad and then starts sending good replies. */
2006-08-31 01:34:56 +02:00
return ;
}
2006-06-04 00:05:23 +02:00
timeout =
2006-09-07 18:32:06 +02:00
& global_nameserver_timeouts [ MIN ( ns - > failed_times ,
global_nameserver_timeouts_length - 1 ) ] ;
2006-06-03 21:49:42 +02:00
ns - > failed_times + + ;
evtimer_set ( & ns - > timeout_event , nameserver_prod_callback , ns ) ;
2006-08-04 20:27:10 +02:00
if ( evtimer_add ( & ns - > timeout_event , ( struct timeval * ) timeout ) < 0 ) {
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_WARN ,
2006-09-07 18:32:06 +02:00
" Error from libevent when adding timer event for %s " ,
debug_ntoa ( ns - > address ) ) ;
2007-09-10 16:05:12 +02:00
/* ???? Do more? */
2006-09-07 18:32:06 +02:00
}
2006-06-03 21:49:42 +02:00
}
2007-09-10 16:05:12 +02:00
/* called when a nameserver has been deemed to have failed. For example, too */
/* many packets have timed out etc */
2006-06-03 21:49:42 +02:00
static void
2006-07-05 23:28:37 +02:00
nameserver_failed ( struct nameserver * const ns , const char * msg ) {
2006-06-04 00:05:23 +02:00
struct request * req , * started_at ;
2007-09-10 16:05:12 +02:00
/* if this nameserver has already been marked as failed */
/* then don't do anything */
2006-06-03 21:49:42 +02:00
if ( ! ns - > state ) return ;
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_WARN , " Nameserver %s has failed: %s " ,
2006-09-07 18:32:06 +02:00
debug_ntoa ( ns - > address ) , msg ) ;
2006-06-03 21:49:42 +02:00
global_good_nameservers - - ;
assert ( global_good_nameservers > = 0 ) ;
if ( global_good_nameservers = = 0 ) {
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_WARN , " All nameservers have failed " ) ;
2006-06-03 21:49:42 +02:00
}
ns - > state = 0 ;
ns - > failed_times = 1 ;
evtimer_set ( & ns - > timeout_event , nameserver_prod_callback , ns ) ;
2006-08-04 20:27:10 +02:00
if ( evtimer_add ( & ns - > timeout_event , ( struct timeval * ) & global_nameserver_timeouts [ 0 ] ) < 0 ) {
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_WARN ,
2006-09-07 18:32:06 +02:00
" Error from libevent when adding timer event for %s " ,
debug_ntoa ( ns - > address ) ) ;
2007-09-10 16:05:12 +02:00
/* ???? Do more? */
2006-09-07 18:32:06 +02:00
}
2006-06-03 21:49:42 +02:00
2007-09-10 16:05:12 +02:00
/* walk the list of inflight requests to see if any can be reassigned to */
/* a different server. Requests in the waiting queue don't have a */
/* nameserver assigned yet */
2006-07-23 07:40:24 +02:00
2007-09-10 16:05:12 +02:00
/* if we don't have *any* good nameservers then there's no point */
/* trying to reassign requests to one */
2006-06-03 21:49:42 +02:00
if ( ! global_good_nameservers ) return ;
2006-06-04 00:05:23 +02:00
req = req_head ;
started_at = req_head ;
2006-06-03 21:49:42 +02:00
if ( req ) {
do {
if ( req - > tx_count = = 0 & & req - > ns = = ns ) {
2007-09-10 16:05:12 +02:00
/* still waiting to go out, can be moved */
/* to another server */
2006-06-03 21:49:42 +02:00
req - > ns = nameserver_pick ( ) ;
}
req = req - > next ;
} while ( req ! = started_at ) ;
}
}
static void
nameserver_up ( struct nameserver * const ns ) {
if ( ns - > state ) return ;
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_WARN , " Nameserver %s is back up " ,
2006-09-07 18:32:06 +02:00
debug_ntoa ( ns - > address ) ) ;
2006-06-03 21:49:42 +02:00
evtimer_del ( & ns - > timeout_event ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( & ns - > timeout_event ) ;
2006-06-03 21:49:42 +02:00
ns - > state = 1 ;
ns - > failed_times = 0 ;
2006-12-08 06:59:21 +01:00
ns - > timedout = 0 ;
2006-06-03 21:49:42 +02:00
global_good_nameservers + + ;
}
static void
request_trans_id_set ( struct request * const req , const u16 trans_id ) {
req - > trans_id = trans_id ;
* ( ( u16 * ) req - > request ) = htons ( trans_id ) ;
}
2007-09-10 16:05:12 +02:00
/* Called to remove a request from a list and dealloc it. */
/* head is a pointer to the head of the list it should be */
/* removed from or NULL if the request isn't in a list. */
2006-06-03 21:49:42 +02:00
static void
request_finished ( struct request * const req , struct request * * head ) {
if ( head ) {
if ( req - > next = = req ) {
2007-09-10 16:05:12 +02:00
/* only item in the list */
2006-06-03 21:49:42 +02:00
* head = NULL ;
} else {
req - > next - > prev = req - > prev ;
req - > prev - > next = req - > next ;
if ( * head = = req ) * head = req - > next ;
}
}
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG , " Removing timeout for request %lx " ,
2006-09-07 18:32:06 +02:00
( unsigned long ) req ) ;
2006-06-03 21:49:42 +02:00
evtimer_del ( & req - > timeout_event ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( & req - > timeout_event ) ;
2006-06-03 21:49:42 +02:00
search_request_finished ( req ) ;
global_requests_inflight - - ;
if ( ! req - > request_appended ) {
2007-09-10 16:05:12 +02:00
/* need to free the request data on it's own */
2006-06-03 21:49:42 +02:00
free ( req - > request ) ;
} else {
2007-09-10 16:05:12 +02:00
/* the request data is appended onto the header */
/* so everything gets free()ed when we: */
2006-06-03 21:49:42 +02:00
}
2007-01-10 20:49:21 +01:00
CLEAR ( req ) ;
2007-07-23 06:18:20 +02:00
_free ( req ) ;
2006-06-03 21:49:42 +02:00
2006-09-24 19:52:23 +02:00
evdns_requests_pump_waiting_queue ( ) ;
2006-06-03 21:49:42 +02:00
}
2007-09-10 16:05:12 +02:00
/* This is called when a server returns a funny error code. */
/* We try the request again with another server. */
/* */
/* return: */
/* 0 ok */
/* 1 failed/reissue is pointless */
2006-06-03 21:49:42 +02:00
static int
request_reissue ( struct request * req ) {
const struct nameserver * const last_ns = req - > ns ;
2007-09-10 16:05:12 +02:00
/* the last nameserver should have been marked as failing */
/* by the caller of this function, therefore pick will try */
/* not to return it */
2006-06-03 21:49:42 +02:00
req - > ns = nameserver_pick ( ) ;
if ( req - > ns = = last_ns ) {
2007-09-10 16:05:12 +02:00
/* ... but pick did return it */
/* not a lot of point in trying again with the */
/* same server */
2006-06-03 21:49:42 +02:00
return 1 ;
}
req - > reissue_count + + ;
req - > tx_count = 0 ;
req - > transmit_me = 1 ;
return 0 ;
}
2007-09-10 16:05:12 +02:00
/* this function looks for space on the inflight queue and promotes */
/* requests from the waiting queue if it can. */
2006-06-03 21:49:42 +02:00
static void
2006-09-24 19:52:23 +02:00
evdns_requests_pump_waiting_queue ( void ) {
2006-06-03 21:49:42 +02:00
while ( global_requests_inflight < global_max_requests_inflight & &
2006-09-07 18:32:06 +02:00
global_requests_waiting ) {
2006-06-04 00:05:23 +02:00
struct request * req ;
2007-09-10 16:05:12 +02:00
/* move a request from the waiting queue to the inflight queue */
2006-06-03 21:49:42 +02:00
assert ( req_waiting_head ) ;
if ( req_waiting_head - > next = = req_waiting_head ) {
2007-09-10 16:05:12 +02:00
/* only one item in the queue */
2006-06-03 21:49:42 +02:00
req = req_waiting_head ;
req_waiting_head = NULL ;
} else {
req = req_waiting_head ;
req - > next - > prev = req - > prev ;
req - > prev - > next = req - > next ;
req_waiting_head = req - > next ;
}
global_requests_waiting - - ;
global_requests_inflight + + ;
2006-07-23 07:40:24 +02:00
2006-06-03 21:49:42 +02:00
req - > ns = nameserver_pick ( ) ;
request_trans_id_set ( req , transaction_id_pick ( ) ) ;
2006-09-24 19:52:23 +02:00
evdns_request_insert ( req , & req_head ) ;
evdns_request_transmit ( req ) ;
evdns_transmit ( ) ;
2006-06-03 21:49:42 +02:00
}
}
2006-08-16 20:47:24 +02:00
static void
reply_callback ( struct request * const req , u32 ttl , u32 err , struct reply * reply ) {
switch ( req - > request_type ) {
case TYPE_A :
if ( reply )
req - > user_callback ( DNS_ERR_NONE , DNS_IPv4_A ,
2006-09-07 18:32:06 +02:00
reply - > data . a . addrcount , ttl ,
2006-12-12 04:48:46 +01:00
reply - > data . a . addresses ,
2006-09-07 18:32:06 +02:00
req - > user_pointer ) ;
2006-08-16 20:47:24 +02:00
else
req - > user_callback ( err , 0 , 0 , 0 , NULL , req - > user_pointer ) ;
return ;
case TYPE_PTR :
if ( reply ) {
2006-09-07 18:32:06 +02:00
char * name = reply - > data . ptr . name ;
2006-08-16 20:47:24 +02:00
req - > user_callback ( DNS_ERR_NONE , DNS_PTR , 1 , ttl ,
2006-09-07 18:32:06 +02:00
& name , req - > user_pointer ) ;
} else {
2006-08-16 20:47:24 +02:00
req - > user_callback ( err , 0 , 0 , 0 , NULL ,
2006-09-07 18:32:06 +02:00
req - > user_pointer ) ;
}
return ;
2006-12-12 04:48:46 +01:00
case TYPE_AAAA :
if ( reply )
2006-12-12 06:45:19 +01:00
req - > user_callback ( DNS_ERR_NONE , DNS_IPv6_AAAA ,
2006-12-12 04:48:46 +01:00
reply - > data . aaaa . addrcount , ttl ,
reply - > data . aaaa . addresses ,
req - > user_pointer ) ;
else
req - > user_callback ( err , 0 , 0 , 0 , NULL , req - > user_pointer ) ;
2007-01-27 06:15:53 +01:00
return ;
2006-08-16 20:47:24 +02:00
}
assert ( 0 ) ;
}
2007-09-10 16:05:12 +02:00
/* this processes a parsed reply packet */
2006-06-03 21:49:42 +02:00
static void
2006-12-12 06:45:19 +01:00
reply_handle ( struct request * const req , u16 flags , u32 ttl , struct reply * reply ) {
2006-06-03 21:49:42 +02:00
int error ;
static const int error_codes [ ] = { DNS_ERR_FORMAT , DNS_ERR_SERVERFAILED , DNS_ERR_NOTEXIST , DNS_ERR_NOTIMPL , DNS_ERR_REFUSED } ;
2006-08-16 20:47:24 +02:00
if ( flags & 0x020f | | ! reply | | ! reply - > have_answer ) {
2007-09-10 16:05:12 +02:00
/* there was an error */
2006-06-03 21:49:42 +02:00
if ( flags & 0x0200 ) {
error = DNS_ERR_TRUNCATED ;
} else {
u16 error_code = ( flags & 0x000f ) - 1 ;
if ( error_code > 4 ) {
error = DNS_ERR_UNKNOWN ;
} else {
error = error_codes [ error_code ] ;
}
}
switch ( error ) {
case DNS_ERR_NOTIMPL :
case DNS_ERR_REFUSED :
2007-09-10 16:05:12 +02:00
/* we regard these errors as marking a bad nameserver */
2006-06-03 21:49:42 +02:00
if ( req - > reissue_count < global_max_reissues ) {
2006-07-05 23:28:37 +02:00
char msg [ 64 ] ;
2006-09-29 20:13:33 +02:00
snprintf ( msg , sizeof ( msg ) , " Bad response %d (%s) " ,
error , evdns_err_to_string ( error ) ) ;
2006-07-05 23:28:37 +02:00
nameserver_failed ( req - > ns , msg ) ;
2006-06-03 21:49:42 +02:00
if ( ! request_reissue ( req ) ) return ;
}
break ;
2007-02-28 21:24:23 +01:00
case DNS_ERR_SERVERFAILED :
2007-09-10 16:05:12 +02:00
/* rcode 2 (servfailed) sometimes means "we are broken" and
* sometimes ( with some binds ) means " that request was very
* confusing . " Treat this as a timeout, not a failure. */
2007-02-28 21:24:23 +01:00
/*XXXX refactor the parts of */
log ( EVDNS_LOG_DEBUG , " Got a SERVERFAILED from nameserver %s; "
" will allow the request to time out. " ,
2007-02-28 21:24:27 +01:00
debug_ntoa ( req - > ns - > address ) ) ;
2007-02-28 21:24:23 +01:00
break ;
2006-06-03 21:49:42 +02:00
default :
2007-09-10 16:05:12 +02:00
/* we got a good reply from the nameserver */
2006-06-03 21:49:42 +02:00
nameserver_up ( req - > ns ) ;
}
2006-08-16 20:47:24 +02:00
if ( req - > search_state & & req - > request_type ! = TYPE_PTR ) {
2007-09-10 16:05:12 +02:00
/* if we have a list of domains to search in, try the next one */
2006-06-03 21:49:42 +02:00
if ( ! search_try_next ( req ) ) {
2007-09-10 16:05:12 +02:00
/* a new request was issued so this request is finished and */
/* the user callback will be made when that request (or a */
/* child of it) finishes. */
2006-06-03 21:49:42 +02:00
request_finished ( req , & req_head ) ;
return ;
}
}
2006-07-23 07:40:24 +02:00
2007-09-10 16:05:12 +02:00
/* all else failed. Pass the failure up */
2006-08-16 20:47:24 +02:00
reply_callback ( req , 0 , error , NULL ) ;
2006-06-03 21:49:42 +02:00
request_finished ( req , & req_head ) ;
} else {
2007-09-10 16:05:12 +02:00
/* all ok, tell the user */
2006-08-16 20:47:24 +02:00
reply_callback ( req , ttl , 0 , reply ) ;
2006-06-03 21:49:42 +02:00
nameserver_up ( req - > ns ) ;
request_finished ( req , & req_head ) ;
}
}
2006-08-16 20:47:24 +02:00
static inline int
2006-09-07 18:24:19 +02:00
name_parse ( u8 * packet , int length , int * idx , char * name_out , int name_out_len ) {
2006-08-16 20:47:24 +02:00
int name_end = - 1 ;
int j = * idx ;
2007-01-21 18:05:10 +01:00
int ptr_count = 0 ;
2006-12-15 07:12:01 +01:00
# define GET32(x) do { if (j + 4 > length) goto err; memcpy(&_t32, packet + j, 4); j += 4; x = ntohl(_t32); } while(0);
# define GET16(x) do { if (j + 2 > length) goto err; memcpy(&_t, packet + j, 2); j += 2; x = ntohs(_t); } while(0);
# define GET8(x) do { if (j >= length) goto err; x = packet[j++]; } while(0);
2006-08-16 20:47:24 +02:00
char * cp = name_out ;
const char * const end = name_out + name_out_len ;
2007-09-10 16:05:12 +02:00
/* Normally, names are a series of length prefixed strings terminated */
/* with a length of 0 (the lengths are u8's < 63). */
/* However, the length can start with a pair of 1 bits and that */
/* means that the next 14 bits are a pointer within the current */
/* packet. */
2006-08-16 20:47:24 +02:00
for ( ; ; ) {
u8 label_len ;
2006-09-07 18:32:06 +02:00
if ( j > = length ) return - 1 ;
2006-08-16 20:47:24 +02:00
GET8 ( label_len ) ;
if ( ! label_len ) break ;
if ( label_len & 0xc0 ) {
u8 ptr_low ;
GET8 ( ptr_low ) ;
2006-09-07 18:32:06 +02:00
if ( name_end < 0 ) name_end = j ;
j = ( ( ( int ) label_len & 0x3f ) < < 8 ) + ptr_low ;
2007-01-21 18:05:10 +01:00
/* Make sure that the target offset is in-bounds. */
2006-09-07 18:32:06 +02:00
if ( j < 0 | | j > = length ) return - 1 ;
2007-01-21 18:05:10 +01:00
/* If we've jumped more times than there are characters in the
* message , we must have a loop . */
if ( + + ptr_count > length ) return - 1 ;
2006-08-16 20:47:24 +02:00
continue ;
}
if ( label_len > 63 ) return - 1 ;
if ( cp ! = name_out ) {
if ( cp + 1 > = end ) return - 1 ;
* cp + + = ' . ' ;
}
if ( cp + label_len > = end ) return - 1 ;
memcpy ( cp , packet + j , label_len ) ;
cp + = label_len ;
j + = label_len ;
}
if ( cp > = end ) return - 1 ;
* cp = ' \0 ' ;
if ( name_end < 0 )
* idx = j ;
else
* idx = name_end ;
return 0 ;
2006-12-15 07:12:01 +01:00
err :
return - 1 ;
2006-08-16 20:47:24 +02:00
}
2007-09-10 16:05:12 +02:00
/* parses a raw reply from a nameserver. */
2006-08-16 20:47:24 +02:00
static int
2006-12-12 06:45:19 +01:00
reply_parse ( u8 * packet , int length ) {
2007-09-10 16:05:12 +02:00
int j = 0 ; /* index into packet */
u16 _t ; /* used by the macros */
u32 _t32 ; /* used by the macros */
char tmp_name [ 256 ] ; /* used by the macros */
2006-06-03 21:49:42 +02:00
2007-02-24 09:18:59 +01:00
u16 trans_id , questions , answers , authority , additional , datalength ;
u16 flags = 0 ;
2006-06-03 21:49:42 +02:00
u32 ttl , ttl_r = 0xffffffff ;
2006-08-16 20:47:24 +02:00
struct reply reply ;
2007-02-24 09:18:59 +01:00
struct request * req = NULL ;
2006-09-03 04:13:52 +02:00
unsigned int i ;
2006-06-03 21:49:42 +02:00
GET16 ( trans_id ) ;
GET16 ( flags ) ;
GET16 ( questions ) ;
GET16 ( answers ) ;
GET16 ( authority ) ;
GET16 ( additional ) ;
2006-12-15 07:12:01 +01:00
( void ) authority ; /* suppress "unused variable" warnings. */
( void ) additional ; /* suppress "unused variable" warnings. */
2006-12-12 03:49:45 +01:00
2006-08-16 20:47:24 +02:00
req = request_find_from_trans_id ( trans_id ) ;
2007-02-24 09:18:59 +01:00
/* if no request, can't do anything. */
2006-08-16 20:47:24 +02:00
if ( ! req ) return - 1 ;
memset ( & reply , 0 , sizeof ( reply ) ) ;
2007-02-24 09:18:59 +01:00
/* if not an answer, it doesn't go with any of our requests. */
2007-09-10 16:05:12 +02:00
if ( ! ( flags & 0x8000 ) ) return - 1 ; /* must be an answer */
2006-06-03 21:49:42 +02:00
if ( flags & 0x020f ) {
2007-09-10 16:05:12 +02:00
/* there was an error */
2007-02-24 09:18:59 +01:00
goto err ;
2006-06-03 21:49:42 +02:00
}
2007-09-10 16:05:12 +02:00
/* if (!answers) return; */ /* must have an answer of some form */
2006-07-23 07:40:24 +02:00
2007-09-10 16:05:12 +02:00
/* This macro skips a name in the DNS reply. */
2006-12-15 07:12:01 +01:00
# define SKIP_NAME \
do { tmp_name [ 0 ] = ' \0 ' ; \
if ( name_parse ( packet , length , & j , tmp_name , sizeof ( tmp_name ) ) < 0 ) \
goto err ; \
} while ( 0 ) ;
2006-08-16 20:47:24 +02:00
reply . type = req - > request_type ;
2006-06-03 21:49:42 +02:00
2007-09-10 16:05:12 +02:00
/* skip over each question in the reply */
2006-06-03 21:49:42 +02:00
for ( i = 0 ; i < questions ; + + i ) {
2007-09-10 16:05:12 +02:00
/* the question looks like
* < label : name > < u16 : type > < u16 : class >
*/
2006-06-03 21:49:42 +02:00
SKIP_NAME ;
j + = 4 ;
2007-02-24 09:18:59 +01:00
if ( j > = length ) goto err ;
2006-12-12 03:56:41 +01:00
}
2006-06-03 21:49:42 +02:00
2007-09-10 16:05:12 +02:00
/* now we have the answer section which looks like
* < label : name > < u16 : type > < u16 : class > < u32 : ttl > < u16 : len > < data . . . >
*/
2006-10-05 21:22:26 +02:00
2006-06-03 21:49:42 +02:00
for ( i = 0 ; i < answers ; + + i ) {
u16 type , class ;
2007-09-10 16:05:12 +02:00
/* XXX I'd be more comfortable if we actually checked the name */
/* here. -NM */
2006-06-03 21:49:42 +02:00
SKIP_NAME ;
GET16 ( type ) ;
GET16 ( class ) ;
GET32 ( ttl ) ;
GET16 ( datalength ) ;
2006-12-12 03:56:41 +01:00
if ( type = = TYPE_A & & class = = CLASS_INET ) {
2006-08-16 20:47:24 +02:00
int addrcount , addrtocopy ;
2006-12-12 06:45:19 +01:00
if ( req - > request_type ! = TYPE_A ) {
2006-08-16 20:47:24 +02:00
j + = datalength ; continue ;
}
2007-01-21 19:21:39 +01:00
if ( ( datalength & 3 ) ! = 0 ) /* not an even number of As. */
2007-02-24 09:18:59 +01:00
goto err ;
2006-12-12 06:45:19 +01:00
addrcount = datalength > > 2 ;
2006-08-28 05:15:55 +02:00
addrtocopy = MIN ( MAX_ADDRS - reply . data . a . addrcount , ( unsigned ) addrcount ) ;
2006-10-05 21:22:26 +02:00
2006-12-12 06:45:19 +01:00
ttl_r = MIN ( ttl_r , ttl ) ;
2007-09-10 16:05:12 +02:00
/* we only bother with the first four addresses. */
2007-02-24 09:18:59 +01:00
if ( j + 4 * addrtocopy > length ) goto err ;
2006-08-16 20:47:24 +02:00
memcpy ( & reply . data . a . addresses [ reply . data . a . addrcount ] ,
2006-09-07 18:32:06 +02:00
packet + j , 4 * addrtocopy ) ;
2006-12-12 04:48:46 +01:00
j + = 4 * addrtocopy ;
2006-12-12 06:45:19 +01:00
reply . data . a . addrcount + = addrtocopy ;
2006-08-16 20:47:24 +02:00
reply . have_answer = 1 ;
if ( reply . data . a . addrcount = = MAX_ADDRS ) break ;
} else if ( type = = TYPE_PTR & & class = = CLASS_INET ) {
if ( req - > request_type ! = TYPE_PTR ) {
j + = datalength ; continue ;
}
if ( name_parse ( packet , length , & j , reply . data . ptr . name ,
2006-09-07 18:32:06 +02:00
sizeof ( reply . data . ptr . name ) ) < 0 )
2007-02-24 09:18:59 +01:00
goto err ;
2007-01-29 19:13:42 +01:00
ttl_r = MIN ( ttl_r , ttl ) ;
2006-08-16 20:47:24 +02:00
reply . have_answer = 1 ;
break ;
2006-12-15 07:12:14 +01:00
} else if ( type = = TYPE_AAAA & & class = = CLASS_INET ) {
2006-12-12 04:48:46 +01:00
int addrcount , addrtocopy ;
2006-12-12 06:45:19 +01:00
if ( req - > request_type ! = TYPE_AAAA ) {
2006-08-16 20:47:24 +02:00
j + = datalength ; continue ;
}
2007-01-21 19:21:39 +01:00
if ( ( datalength & 15 ) ! = 0 ) /* not an even number of AAAAs. */
2007-02-24 09:18:59 +01:00
goto err ;
2007-09-10 16:05:12 +02:00
addrcount = datalength > > 4 ; /* each address is 16 bytes long */
2006-12-12 04:48:46 +01:00
addrtocopy = MIN ( MAX_ADDRS - reply . data . aaaa . addrcount , ( unsigned ) addrcount ) ;
ttl_r = MIN ( ttl_r , ttl ) ;
2007-09-10 16:05:12 +02:00
/* we only bother with the first four addresses. */
2007-02-24 09:18:59 +01:00
if ( j + 16 * addrtocopy > length ) goto err ;
2006-12-12 04:48:46 +01:00
memcpy ( & reply . data . aaaa . addresses [ reply . data . aaaa . addrcount ] ,
packet + j , 16 * addrtocopy ) ;
reply . data . aaaa . addrcount + = addrtocopy ;
j + = 16 * addrtocopy ;
reply . have_answer = 1 ;
2007-01-21 19:21:39 +01:00
if ( reply . data . aaaa . addrcount = = MAX_ADDRS ) break ;
2006-09-07 18:32:06 +02:00
} else {
2007-09-10 16:05:12 +02:00
/* skip over any other type of resource */
2006-06-03 21:49:42 +02:00
j + = datalength ;
}
}
2006-08-16 20:47:24 +02:00
reply_handle ( req , flags , ttl_r , & reply ) ;
return 0 ;
2006-12-15 07:12:01 +01:00
err :
2007-02-24 09:18:59 +01:00
if ( req )
reply_handle ( req , flags , 0 , NULL ) ;
2006-12-15 07:12:01 +01:00
return - 1 ;
2006-12-12 03:49:45 +01:00
}
2007-09-10 16:05:12 +02:00
/* Parse a raw request (packet,length) sent to a nameserver port (port) from */
/* a DNS client (addr,addrlen), and if it's well-formed, call the corresponding */
/* callback. */
2006-12-12 03:49:45 +01:00
static int
2006-12-12 03:56:20 +01:00
request_parse ( u8 * packet , int length , struct evdns_server_port * port , struct sockaddr * addr , socklen_t addrlen )
2006-12-12 03:49:45 +01:00
{
2007-09-10 16:05:12 +02:00
int j = 0 ; /* index into packet */
u16 _t ; /* used by the macros */
char tmp_name [ 256 ] ; /* used by the macros */
2006-12-12 03:49:45 +01:00
int i ;
u16 trans_id , flags , questions , answers , authority , additional ;
struct server_request * server_req = NULL ;
2007-09-10 16:05:12 +02:00
/* Get the header fields */
2006-12-12 03:49:45 +01:00
GET16 ( trans_id ) ;
GET16 ( flags ) ;
GET16 ( questions ) ;
GET16 ( answers ) ;
GET16 ( authority ) ;
GET16 ( additional ) ;
2007-09-10 16:05:12 +02:00
if ( flags & 0x8000 ) return - 1 ; /* Must not be an answer. */
flags & = 0x0110 ; /* Only RD and CD get preserved. */
2006-12-12 03:49:45 +01:00
server_req = malloc ( sizeof ( struct server_request ) ) ;
if ( server_req = = NULL ) return - 1 ;
memset ( server_req , 0 , sizeof ( struct server_request ) ) ;
server_req - > trans_id = trans_id ;
memcpy ( & server_req - > addr , addr , addrlen ) ;
server_req - > addrlen = addrlen ;
server_req - > base . flags = flags ;
server_req - > base . nquestions = 0 ;
2006-12-12 03:56:20 +01:00
server_req - > base . questions = malloc ( sizeof ( struct evdns_server_question * ) * questions ) ;
2006-12-12 03:49:45 +01:00
if ( server_req - > base . questions = = NULL )
goto err ;
for ( i = 0 ; i < questions ; + + i ) {
u16 type , class ;
2006-12-12 03:56:20 +01:00
struct evdns_server_question * q ;
2006-12-12 03:49:45 +01:00
int namelen ;
if ( name_parse ( packet , length , & j , tmp_name , sizeof ( tmp_name ) ) < 0 )
goto err ;
GET16 ( type ) ;
GET16 ( class ) ;
namelen = strlen ( tmp_name ) ;
2006-12-12 03:56:20 +01:00
q = malloc ( sizeof ( struct evdns_server_question ) + namelen ) ;
2006-12-12 03:49:45 +01:00
if ( ! q )
goto err ;
q - > type = type ;
q - > class = class ;
memcpy ( q - > name , tmp_name , namelen + 1 ) ;
server_req - > base . questions [ server_req - > base . nquestions + + ] = q ;
}
2007-09-10 16:05:12 +02:00
/* Ignore answers, authority, and additional. */
2006-12-12 03:49:45 +01:00
server_req - > port = port ;
port - > refcnt + + ;
2007-06-01 01:40:35 +02:00
2007-09-10 16:05:12 +02:00
/* Only standard queries are supported. */
2007-06-01 01:40:35 +02:00
if ( flags & 0x7800 ) {
evdns_server_request_respond ( & ( server_req - > base ) , DNS_ERR_NOTIMPL ) ;
return - 1 ;
}
2006-12-12 03:49:45 +01:00
port - > user_callback ( & ( server_req - > base ) , port - > user_data ) ;
return 0 ;
err :
if ( server_req ) {
if ( server_req - > base . questions ) {
for ( i = 0 ; i < server_req - > base . nquestions ; + + i )
free ( server_req - > base . questions [ i ] ) ;
free ( server_req - > base . questions ) ;
}
2007-01-10 20:49:21 +01:00
CLEAR ( server_req ) ;
2006-12-12 03:49:45 +01:00
free ( server_req ) ;
}
return - 1 ;
2006-12-15 07:12:01 +01:00
# undef SKIP_NAME
# undef GET32
2006-12-12 03:49:45 +01:00
# undef GET16
# undef GET8
2006-12-15 07:12:01 +01:00
}
2006-12-12 03:49:45 +01:00
2007-09-10 16:05:12 +02:00
/* Try to choose a strong transaction id which isn't already in flight */
2006-06-03 21:49:42 +02:00
static u16
2006-06-04 00:05:23 +02:00
transaction_id_pick ( void ) {
2006-06-03 21:49:42 +02:00
for ( ; ; ) {
2006-06-04 00:05:23 +02:00
const struct request * req = req_head , * started_at ;
2006-06-03 21:49:42 +02:00
# ifdef DNS_USE_CPU_CLOCK_FOR_ID
struct timespec ts ;
2006-12-12 06:45:19 +01:00
u16 trans_id ;
# ifdef CLOCK_MONOTONIC
if ( clock_gettime ( CLOCK_MONOTONIC , & ts ) = = - 1 )
# else
if ( clock_gettime ( CLOCK_REALTIME , & ts ) = = - 1 )
# endif
event_err ( 1 , " clock_gettime " ) ;
trans_id = ts . tv_nsec & 0xffff ;
2006-06-03 21:49:42 +02:00
# endif
# ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
struct timeval tv ;
2006-12-12 06:45:19 +01:00
u16 trans_id ;
2006-06-04 00:05:23 +02:00
gettimeofday ( & tv , NULL ) ;
2006-12-12 06:45:19 +01:00
trans_id = tv . tv_usec & 0xffff ;
2006-06-03 21:49:42 +02:00
# endif
# ifdef DNS_USE_OPENSSL_FOR_ID
u16 trans_id ;
if ( RAND_pseudo_bytes ( ( u8 * ) & trans_id , 2 ) = = - 1 ) {
2007-09-10 16:05:12 +02:00
/* in the case that the RAND call fails we back */
/* down to using gettimeofday. */
/*
2006-06-03 21:49:42 +02:00
struct timeval tv ;
gettimeofday ( & tv , NULL ) ;
2007-09-10 16:05:12 +02:00
trans_id = tv . tv_usec & 0xffff ;
*/
2006-06-03 21:49:42 +02:00
abort ( ) ;
}
# endif
if ( trans_id = = 0xffff ) continue ;
2007-09-10 16:05:12 +02:00
/* now check to see if that id is already inflight */
2006-06-04 00:05:23 +02:00
req = started_at = req_head ;
2006-06-03 21:49:42 +02:00
if ( req ) {
do {
if ( req - > trans_id = = trans_id ) break ;
req = req - > next ;
} while ( req ! = started_at ) ;
}
2007-09-10 16:05:12 +02:00
/* we didn't find it, so this is a good id */
2006-06-03 21:49:42 +02:00
if ( req = = started_at ) return trans_id ;
}
}
2007-09-10 16:05:12 +02:00
/* choose a namesever to use. This function will try to ignore */
/* nameservers which we think are down and load balance across the rest */
/* by updating the server_head global each time. */
2006-06-03 21:49:42 +02:00
static struct nameserver *
2006-06-04 00:05:23 +02:00
nameserver_pick ( void ) {
struct nameserver * started_at = server_head , * picked ;
2006-06-03 21:49:42 +02:00
if ( ! server_head ) return NULL ;
2007-09-10 16:05:12 +02:00
/* if we don't have any good nameservers then there's no */
/* point in trying to find one. */
2006-06-03 21:49:42 +02:00
if ( ! global_good_nameservers ) {
server_head = server_head - > next ;
return server_head ;
}
2006-06-04 00:05:23 +02:00
2007-09-10 16:05:12 +02:00
/* remember that nameservers are in a circular list */
2006-06-03 21:49:42 +02:00
for ( ; ; ) {
if ( server_head - > state ) {
2007-09-10 16:05:12 +02:00
/* we think this server is currently good */
2006-06-03 21:49:42 +02:00
picked = server_head ;
server_head = server_head - > next ;
return picked ;
}
server_head = server_head - > next ;
if ( server_head = = started_at ) {
2007-09-10 16:05:12 +02:00
/* all the nameservers seem to be down */
/* so we just return this one and hope for the */
/* best */
2006-06-03 21:49:42 +02:00
assert ( global_good_nameservers = = 0 ) ;
picked = server_head ;
server_head = server_head - > next ;
return picked ;
}
}
}
2007-09-10 16:05:12 +02:00
/* this is called when a namesever socket is ready for reading */
2006-06-03 21:49:42 +02:00
static void
nameserver_read ( struct nameserver * ns ) {
u8 packet [ 1500 ] ;
for ( ; ; ) {
2006-09-07 18:32:06 +02:00
const int r = recv ( ns - > socket , packet , sizeof ( packet ) , 0 ) ;
2006-06-03 21:49:42 +02:00
if ( r < 0 ) {
2006-09-06 22:33:28 +02:00
int err = last_error ( ns - > socket ) ;
2006-07-05 23:28:37 +02:00
if ( error_is_eagain ( err ) ) return ;
nameserver_failed ( ns , strerror ( err ) ) ;
2006-06-03 21:49:42 +02:00
return ;
}
2006-12-08 06:59:21 +01:00
ns - > timedout = 0 ;
2006-06-03 21:49:42 +02:00
reply_parse ( packet , r ) ;
}
}
2007-09-10 16:05:12 +02:00
/* Read a packet from a DNS client on a server port s, parse it, and */
/* act accordingly. */
2006-12-12 03:49:45 +01:00
static void
2006-12-12 03:56:20 +01:00
server_port_read ( struct evdns_server_port * s ) {
2006-12-12 03:49:45 +01:00
u8 packet [ 1500 ] ;
struct sockaddr_storage addr ;
socklen_t addrlen ;
int r ;
for ( ; ; ) {
addrlen = sizeof ( struct sockaddr_storage ) ;
r = recvfrom ( s - > socket , packet , sizeof ( packet ) , 0 ,
( struct sockaddr * ) & addr , & addrlen ) ;
if ( r < 0 ) {
int err = last_error ( s - > socket ) ;
if ( error_is_eagain ( err ) ) return ;
2006-12-12 03:56:47 +01:00
log ( EVDNS_LOG_WARN , " Error %s (%d) while reading request. " ,
strerror ( err ) , err ) ;
2006-12-12 03:49:45 +01:00
return ;
}
request_parse ( packet , r , s , ( struct sockaddr * ) & addr , addrlen ) ;
}
}
2007-09-10 16:05:12 +02:00
/* Try to write all pending replies on a given DNS server port. */
2006-12-12 03:56:20 +01:00
static void
server_port_flush ( struct evdns_server_port * port )
{
2006-12-12 03:56:23 +01:00
while ( port - > pending_replies ) {
struct server_request * req = port - > pending_replies ;
int r = sendto ( port - > socket , req - > response , req - > response_len , 0 ,
( struct sockaddr * ) & req - > addr , req - > addrlen ) ;
2006-12-12 03:56:47 +01:00
if ( r < 0 ) {
int err = last_error ( port - > socket ) ;
if ( error_is_eagain ( err ) )
return ;
log ( EVDNS_LOG_WARN , " Error %s (%d) while writing response to port; dropping " , strerror ( err ) , err ) ;
}
2006-12-12 03:57:02 +01:00
if ( server_request_free ( req ) ) {
2007-09-10 16:05:12 +02:00
/* we released the last reference to req->port. */
2006-12-12 03:56:23 +01:00
return ;
2006-12-15 07:12:14 +01:00
}
2006-12-12 03:56:23 +01:00
}
2007-09-10 16:05:12 +02:00
/* We have no more pending requests; stop listening for 'writeable' events. */
2006-12-12 03:56:23 +01:00
( void ) event_del ( & port - > event ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( & port - > event ) ;
2006-12-12 03:56:23 +01:00
event_set ( & port - > event , port - > socket , EV_READ | EV_PERSIST ,
server_port_ready_callback , port ) ;
2006-12-12 03:56:47 +01:00
if ( event_add ( & port - > event , NULL ) < 0 ) {
2006-12-15 07:12:14 +01:00
log ( EVDNS_LOG_WARN , " Error from libevent when adding event for DNS server. " ) ;
2007-09-10 16:05:12 +02:00
/* ???? Do more? */
2006-12-15 07:12:14 +01:00
}
2006-12-12 03:56:20 +01:00
}
2007-09-10 16:05:12 +02:00
/* set if we are waiting for the ability to write to this server. */
/* if waiting is true then we ask libevent for EV_WRITE events, otherwise */
/* we stop these events. */
2006-06-03 21:49:42 +02:00
static void
nameserver_write_waiting ( struct nameserver * ns , char waiting ) {
if ( ns - > write_waiting = = waiting ) return ;
2006-07-23 07:40:24 +02:00
2006-06-03 21:49:42 +02:00
ns - > write_waiting = waiting ;
2006-08-04 20:27:10 +02:00
( void ) event_del ( & ns - > event ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( & ns - > event ) ;
2006-06-03 21:49:42 +02:00
event_set ( & ns - > event , ns - > socket , EV_READ | ( waiting ? EV_WRITE : 0 ) | EV_PERSIST ,
2006-09-07 18:32:06 +02:00
nameserver_ready_callback , ns ) ;
2006-08-04 20:27:10 +02:00
if ( event_add ( & ns - > event , NULL ) < 0 ) {
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_WARN , " Error from libevent when adding event for %s " ,
2006-09-07 18:32:06 +02:00
debug_ntoa ( ns - > address ) ) ;
2007-09-10 16:05:12 +02:00
/* ???? Do more? */
2006-09-07 18:32:06 +02:00
}
2006-06-03 21:49:42 +02:00
}
2007-09-10 16:05:12 +02:00
/* a callback function. Called by libevent when the kernel says that */
/* a nameserver socket is ready for writing or reading */
2006-06-03 21:49:42 +02:00
static void
nameserver_ready_callback ( int fd , short events , void * arg ) {
struct nameserver * ns = ( struct nameserver * ) arg ;
2006-09-07 18:32:06 +02:00
( void ) fd ;
2006-07-23 07:40:24 +02:00
2006-06-03 21:49:42 +02:00
if ( events & EV_WRITE ) {
2006-12-12 21:20:48 +01:00
ns - > choked = 0 ;
2006-09-24 19:52:23 +02:00
if ( ! evdns_transmit ( ) ) {
2006-06-03 21:49:42 +02:00
nameserver_write_waiting ( ns , 0 ) ;
}
}
if ( events & EV_READ ) {
nameserver_read ( ns ) ;
}
}
2007-09-10 16:05:12 +02:00
/* a callback function. Called by libevent when the kernel says that */
/* a server socket is ready for writing or reading. */
2006-12-12 03:56:20 +01:00
static void
server_port_ready_callback ( int fd , short events , void * arg ) {
struct evdns_server_port * port = ( struct evdns_server_port * ) arg ;
( void ) fd ;
if ( events & EV_WRITE ) {
2006-12-12 21:20:48 +01:00
port - > choked = 0 ;
2006-12-12 03:56:20 +01:00
server_port_flush ( port ) ;
}
if ( events & EV_READ ) {
server_port_read ( port ) ;
}
}
2006-12-12 03:50:17 +01:00
/* This is an inefficient representation; only use it via the dnslabel_table_*
2006-12-12 03:57:02 +01:00
* functions , so that is can be safely replaced with something smarter later . */
2006-12-12 03:50:17 +01:00
# define MAX_LABELS 128
2007-09-10 16:05:12 +02:00
/* Structures used to implement name compression */
2006-12-12 03:50:17 +01:00
struct dnslabel_entry { char * v ; int pos ; } ;
struct dnslabel_table {
2007-09-10 16:05:12 +02:00
int n_labels ; /* number of current entries */
/* map from name to position in message */
2006-12-12 03:50:17 +01:00
struct dnslabel_entry labels [ MAX_LABELS ] ;
} ;
2007-09-10 16:05:12 +02:00
/* Initialize dnslabel_table. */
2006-12-12 03:50:17 +01:00
static void
dnslabel_table_init ( struct dnslabel_table * table )
{
table - > n_labels = 0 ;
}
2007-09-10 16:05:12 +02:00
/* Free all storage held by table, but not the table itself. */
2006-12-12 03:50:17 +01:00
static void
dnslabel_clear ( struct dnslabel_table * table )
{
int i ;
for ( i = 0 ; i < table - > n_labels ; + + i )
free ( table - > labels [ i ] . v ) ;
table - > n_labels = 0 ;
}
2007-09-10 16:05:12 +02:00
/* return the position of the label in the current message, or -1 if the label */
/* hasn't been used yet. */
2006-12-12 03:50:17 +01:00
static int
dnslabel_table_get_pos ( const struct dnslabel_table * table , const char * label )
{
int i ;
for ( i = 0 ; i < table - > n_labels ; + + i ) {
if ( ! strcmp ( label , table - > labels [ i ] . v ) )
return table - > labels [ i ] . pos ;
}
return - 1 ;
}
2007-09-10 16:05:12 +02:00
/* remember that we've used the label at position pos */
2006-12-12 03:50:17 +01:00
static int
dnslabel_table_add ( struct dnslabel_table * table , const char * label , int pos )
{
char * v ;
int p ;
if ( table - > n_labels = = MAX_LABELS )
return ( - 1 ) ;
v = strdup ( label ) ;
if ( v = = NULL )
return ( - 1 ) ;
p = table - > n_labels + + ;
table - > labels [ p ] . v = v ;
table - > labels [ p ] . pos = pos ;
return ( 0 ) ;
}
2007-09-10 16:05:12 +02:00
/* Converts a string to a length-prefixed set of DNS labels, starting */
/* at buf[j]. name and buf must not overlap. name_len should be the length */
/* of name. table is optional, and is used for compression. */
/* */
/* Input: abc.def */
/* Output: <3>abc<3>def<0> */
/* */
/* Returns the first index after the encoded name, or negative on error. */
/* -1 label was > 63 bytes */
/* -2 name too long to fit in buffer. */
/* */
2006-12-12 03:50:17 +01:00
static off_t
dnsname_to_labels ( u8 * const buf , size_t buf_len , off_t j ,
const char * name , const int name_len ,
struct dnslabel_table * table ) {
2006-09-07 18:24:19 +02:00
const char * end = name + name_len ;
2006-12-12 03:50:17 +01:00
int ref = 0 ;
u16 _t ;
2006-12-15 07:12:14 +01:00
# define APPEND16(x) do { \
if ( j + 2 > ( off_t ) buf_len ) \
goto overflow ; \
_t = htons ( x ) ; \
memcpy ( buf + j , & _t , 2 ) ; \
j + = 2 ; \
} while ( 0 )
# define APPEND32(x) do { \
if ( j + 4 > ( off_t ) buf_len ) \
goto overflow ; \
_t32 = htonl ( x ) ; \
memcpy ( buf + j , & _t32 , 4 ) ; \
j + = 4 ; \
} while ( 0 )
2006-06-03 21:49:42 +02:00
if ( name_len > 255 ) return - 2 ;
2006-07-23 07:40:24 +02:00
2006-06-03 21:49:42 +02:00
for ( ; ; ) {
const char * const start = name ;
2006-12-12 03:50:17 +01:00
if ( table & & ( ref = dnslabel_table_get_pos ( table , name ) ) > = 0 ) {
APPEND16 ( ref | 0xc000 ) ;
return j ;
}
2006-06-03 21:49:42 +02:00
name = strchr ( name , ' . ' ) ;
if ( ! name ) {
2006-09-03 04:13:52 +02:00
const unsigned int label_len = end - start ;
2006-06-03 21:49:42 +02:00
if ( label_len > 63 ) return - 1 ;
2006-12-12 19:34:22 +01:00
if ( ( size_t ) ( j + label_len + 1 ) > buf_len ) return - 2 ;
2006-12-12 03:50:17 +01:00
if ( table ) dnslabel_table_add ( table , start , j ) ;
2006-06-03 21:49:42 +02:00
buf [ j + + ] = label_len ;
memcpy ( buf + j , start , end - start ) ;
j + = end - start ;
break ;
} else {
2007-09-10 16:05:12 +02:00
/* append length of the label. */
2006-09-03 04:13:52 +02:00
const unsigned int label_len = name - start ;
2006-06-03 21:49:42 +02:00
if ( label_len > 63 ) return - 1 ;
2006-12-12 19:34:22 +01:00
if ( ( size_t ) ( j + label_len + 1 ) > buf_len ) return - 2 ;
2006-12-12 03:50:17 +01:00
if ( table ) dnslabel_table_add ( table , start , j ) ;
2006-06-03 21:49:42 +02:00
buf [ j + + ] = label_len ;
memcpy ( buf + j , start , name - start ) ;
j + = name - start ;
2007-09-10 16:05:12 +02:00
/* hop over the '.' */
2006-06-03 21:49:42 +02:00
name + + ;
}
}
2007-09-10 16:05:12 +02:00
/* the labels must be terminated by a 0. */
/* It's possible that the name ended in a . */
/* in which case the zero is already there */
2006-06-03 21:49:42 +02:00
if ( ! j | | buf [ j - 1 ] ) buf [ j + + ] = 0 ;
return j ;
2006-12-12 03:56:41 +01:00
overflow :
return ( - 2 ) ;
2006-06-03 21:49:42 +02:00
}
2007-09-10 16:05:12 +02:00
/* Finds the length of a dns request for a DNS name of the given */
/* length. The actual request may be smaller than the value returned */
/* here */
2006-06-03 21:49:42 +02:00
static int
2006-09-24 19:52:23 +02:00
evdns_request_len ( const int name_len ) {
2007-09-10 16:05:12 +02:00
return 96 + /* length of the DNS standard header */
2006-07-23 07:40:24 +02:00
name_len + 2 +
2007-09-10 16:05:12 +02:00
4 ; /* space for the resource type */
2006-06-03 21:49:42 +02:00
}
2007-09-10 16:05:12 +02:00
/* build a dns request packet into buf. buf should be at least as long */
/* as evdns_request_len told you it should be. */
/* */
/* Returns the amount of space used. Negative on error. */
2006-06-03 21:49:42 +02:00
static int
2006-12-15 07:12:01 +01:00
evdns_request_data_build ( const char * const name , const int name_len ,
const u16 trans_id , const u16 type , const u16 class ,
2006-09-24 19:52:23 +02:00
u8 * const buf , size_t buf_len ) {
2007-09-10 16:05:12 +02:00
off_t j = 0 ; /* current offset into buf */
u16 _t ; /* used by the macros */
2006-07-23 07:40:24 +02:00
2006-06-03 21:49:42 +02:00
APPEND16 ( trans_id ) ;
2007-09-10 16:05:12 +02:00
APPEND16 ( 0x0100 ) ; /* standard query, recusion needed */
APPEND16 ( 1 ) ; /* one question */
APPEND16 ( 0 ) ; /* no answers */
APPEND16 ( 0 ) ; /* no authority */
APPEND16 ( 0 ) ; /* no additional */
2006-06-03 21:49:42 +02:00
2006-12-12 03:50:17 +01:00
j = dnsname_to_labels ( buf , buf_len , j , name , name_len , NULL ) ;
if ( j < 0 ) {
2006-12-12 03:56:17 +01:00
return ( int ) j ;
2006-09-24 19:52:23 +02:00
}
2006-06-03 21:49:42 +02:00
APPEND16 ( type ) ;
APPEND16 ( class ) ;
2006-11-14 02:07:52 +01:00
return ( int ) j ;
2006-12-12 03:56:41 +01:00
overflow :
return ( - 1 ) ;
2006-06-03 21:49:42 +02:00
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-12-12 03:56:20 +01:00
struct evdns_server_port *
evdns_add_server_port ( int socket , int is_tcp , evdns_request_callback_fn_type cb , void * user_data )
{
struct evdns_server_port * port ;
if ( ! ( port = malloc ( sizeof ( struct evdns_server_port ) ) ) )
return NULL ;
2007-01-10 20:49:21 +01:00
memset ( port , 0 , sizeof ( struct evdns_server_port ) ) ;
2006-12-12 03:56:20 +01:00
2007-09-10 16:05:12 +02:00
assert ( ! is_tcp ) ; /* TCP sockets not yet implemented */
2006-12-12 03:56:20 +01:00
port - > socket = socket ;
port - > refcnt = 1 ;
2006-12-12 21:20:48 +01:00
port - > choked = 0 ;
2006-12-12 03:56:47 +01:00
port - > closing = 0 ;
2006-12-12 03:56:20 +01:00
port - > user_callback = cb ;
port - > user_data = user_data ;
port - > pending_replies = NULL ;
2006-12-12 03:56:47 +01:00
2006-12-12 03:56:20 +01:00
event_set ( & port - > event , port - > socket , EV_READ | EV_PERSIST ,
server_port_ready_callback , port ) ;
2007-09-10 16:05:12 +02:00
event_add ( & port - > event , NULL ) ; /* check return. */
2006-12-12 03:56:20 +01:00
return port ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-12-12 03:56:47 +01:00
void
evdns_close_server_port ( struct evdns_server_port * port )
{
if ( - - port - > refcnt = = 0 )
2006-12-12 03:56:54 +01:00
server_port_free ( port ) ;
2006-12-15 07:12:14 +01:00
port - > closing = 1 ;
2006-12-12 03:56:47 +01:00
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-12-12 03:49:45 +01:00
int
2006-12-12 03:56:54 +01:00
evdns_server_request_add_reply ( struct evdns_server_request * _req , int section , const char * name , int type , int class , int ttl , int datalen , int is_name , const char * data )
2006-12-12 03:49:45 +01:00
{
struct server_request * req = TO_SERVER_REQUEST ( _req ) ;
2006-12-12 03:57:02 +01:00
struct server_reply_item * * itemp , * item ;
2006-12-12 03:56:20 +01:00
int * countp ;
if ( req - > response ) /* have we already answered? */
2006-12-12 03:57:02 +01:00
return ( - 1 ) ;
2006-12-12 03:49:45 +01:00
switch ( section ) {
case EVDNS_ANSWER_SECTION :
2006-12-12 03:56:20 +01:00
itemp = & req - > answer ;
countp = & req - > n_answer ;
2006-12-12 03:49:45 +01:00
break ;
case EVDNS_AUTHORITY_SECTION :
2006-12-12 03:56:20 +01:00
itemp = & req - > authority ;
countp = & req - > n_authority ;
2006-12-12 03:49:45 +01:00
break ;
case EVDNS_ADDITIONAL_SECTION :
2006-12-12 03:56:20 +01:00
itemp = & req - > additional ;
countp = & req - > n_additional ;
2006-12-12 03:49:45 +01:00
break ;
default :
2006-12-12 03:57:02 +01:00
return ( - 1 ) ;
2006-12-12 03:49:45 +01:00
}
2006-12-12 03:56:20 +01:00
while ( * itemp ) {
itemp = & ( ( * itemp ) - > next ) ;
2006-12-12 03:49:45 +01:00
}
2006-12-12 03:57:02 +01:00
item = malloc ( sizeof ( struct server_reply_item ) ) ;
2006-12-12 03:56:20 +01:00
if ( ! item )
2006-12-12 03:49:45 +01:00
return - 1 ;
2007-01-10 20:49:21 +01:00
CLEAR ( item ) ;
2006-12-12 03:56:20 +01:00
item - > next = NULL ;
if ( ! ( item - > name = strdup ( name ) ) ) {
2007-01-10 20:49:21 +01:00
CLEAR ( item ) ;
2006-12-12 03:56:20 +01:00
free ( item ) ;
return - 1 ;
}
item - > type = type ;
item - > class = class ;
item - > ttl = ttl ;
item - > is_name = is_name ! = 0 ;
item - > datalen = 0 ;
item - > data = NULL ;
if ( data ) {
if ( item - > is_name ) {
if ( ! ( item - > data = strdup ( data ) ) ) {
free ( item - > name ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( item ) ;
2006-12-12 03:56:20 +01:00
free ( item ) ;
return - 1 ;
}
item - > datalen = - 1 ;
} else {
if ( ! ( item - > data = malloc ( datalen ) ) ) {
free ( item - > name ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( item ) ;
2006-12-12 03:56:20 +01:00
free ( item ) ;
return - 1 ;
}
item - > datalen = datalen ;
memcpy ( item - > data , data , datalen ) ;
}
}
2006-12-12 03:54:48 +01:00
2006-12-12 03:56:20 +01:00
* itemp = item ;
+ + ( * countp ) ;
2006-12-12 03:49:45 +01:00
return 0 ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-12-12 03:49:45 +01:00
int
2006-12-12 03:56:54 +01:00
evdns_server_request_add_a_reply ( struct evdns_server_request * req , const char * name , int n , void * addrs , int ttl )
2006-12-12 03:49:45 +01:00
{
2006-12-12 03:56:54 +01:00
return evdns_server_request_add_reply (
2006-12-12 03:49:45 +01:00
req , EVDNS_ANSWER_SECTION , name , TYPE_A , CLASS_INET ,
2006-12-12 03:56:20 +01:00
ttl , n * 4 , 0 , addrs ) ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-12-12 03:56:20 +01:00
int
2006-12-12 03:56:54 +01:00
evdns_server_request_add_aaaa_reply ( struct evdns_server_request * req , const char * name , int n , void * addrs , int ttl )
2006-12-12 03:56:20 +01:00
{
2006-12-12 03:56:54 +01:00
return evdns_server_request_add_reply (
2006-12-12 03:56:20 +01:00
req , EVDNS_ANSWER_SECTION , name , TYPE_AAAA , CLASS_INET ,
ttl , n * 16 , 0 , addrs ) ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-12-12 03:56:20 +01:00
int
2006-12-12 03:56:54 +01:00
evdns_server_request_add_ptr_reply ( struct evdns_server_request * req , struct in_addr * in , const char * inaddr_name , const char * hostname , int ttl )
2006-12-12 03:56:20 +01:00
{
u32 a ;
char buf [ 32 ] ;
2006-12-12 03:56:28 +01:00
assert ( in | | inaddr_name ) ;
assert ( ! ( in & & inaddr_name ) ) ;
if ( in ) {
a = ntohl ( in - > s_addr ) ;
2007-01-16 00:56:38 +01:00
snprintf ( buf , sizeof ( buf ) , " %d.%d.%d.%d.in-addr.arpa " ,
2006-12-12 03:56:28 +01:00
( int ) ( u8 ) ( ( a ) & 0xff ) ,
( int ) ( u8 ) ( ( a > > 8 ) & 0xff ) ,
( int ) ( u8 ) ( ( a > > 16 ) & 0xff ) ,
( int ) ( u8 ) ( ( a > > 24 ) & 0xff ) ) ;
inaddr_name = buf ;
}
2006-12-12 03:56:54 +01:00
return evdns_server_request_add_reply (
2006-12-12 03:56:28 +01:00
req , EVDNS_ANSWER_SECTION , inaddr_name , TYPE_PTR , CLASS_INET ,
ttl , - 1 , 1 , hostname ) ;
2006-12-12 03:56:20 +01:00
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-12-12 03:56:20 +01:00
int
2006-12-12 03:56:54 +01:00
evdns_server_request_add_cname_reply ( struct evdns_server_request * req , const char * name , const char * cname , int ttl )
2006-12-12 03:56:20 +01:00
{
2006-12-12 03:56:54 +01:00
return evdns_server_request_add_reply (
2006-12-12 03:56:20 +01:00
req , EVDNS_ANSWER_SECTION , name , TYPE_A , CLASS_INET ,
ttl , - 1 , 1 , cname ) ;
}
2006-12-12 03:56:54 +01:00
2006-12-12 03:56:20 +01:00
static int
2006-12-12 03:56:54 +01:00
evdns_server_request_format_response ( struct server_request * req , int err )
2006-12-12 03:56:20 +01:00
{
unsigned char buf [ 1500 ] ;
size_t buf_len = sizeof ( buf ) ;
2006-12-12 03:56:41 +01:00
off_t j = 0 , r ;
2006-12-12 03:56:20 +01:00
u16 _t ;
u32 _t32 ;
int i ;
2006-12-12 03:56:41 +01:00
u16 flags ;
2006-12-12 03:56:20 +01:00
struct dnslabel_table table ;
2006-12-15 07:12:14 +01:00
if ( err < 0 | | err > 15 ) return - 1 ;
2006-12-12 03:56:41 +01:00
/* Set response bit and error code; copy OPCODE and RD fields from
* question ; copy RA and AA if set by caller . */
flags = req - > base . flags ;
flags | = ( 0x8000 | err ) ;
2006-12-12 03:56:47 +01:00
dnslabel_table_init ( & table ) ;
2006-12-12 03:56:20 +01:00
APPEND16 ( req - > trans_id ) ;
APPEND16 ( flags ) ;
2006-12-12 03:56:28 +01:00
APPEND16 ( req - > base . nquestions ) ;
2006-12-12 03:56:20 +01:00
APPEND16 ( req - > n_answer ) ;
APPEND16 ( req - > n_authority ) ;
APPEND16 ( req - > n_additional ) ;
2006-12-12 03:56:28 +01:00
/* Add questions. */
for ( i = 0 ; i < req - > base . nquestions ; + + i ) {
const char * s = req - > base . questions [ i ] - > name ;
j = dnsname_to_labels ( buf , buf_len , j , s , strlen ( s ) , & table ) ;
2006-12-12 03:56:47 +01:00
if ( j < 0 ) {
dnslabel_clear ( & table ) ;
2006-12-12 03:56:28 +01:00
return ( int ) j ;
2006-12-15 07:12:14 +01:00
}
2006-12-12 03:56:28 +01:00
APPEND16 ( req - > base . questions [ i ] - > type ) ;
APPEND16 ( req - > base . questions [ i ] - > class ) ;
}
2006-12-12 03:56:41 +01:00
/* Add answer, authority, and additional sections. */
2006-12-12 03:56:20 +01:00
for ( i = 0 ; i < 3 ; + + i ) {
2006-12-12 03:57:02 +01:00
struct server_reply_item * item ;
2006-12-12 03:56:20 +01:00
if ( i = = 0 )
item = req - > answer ;
else if ( i = = 1 )
item = req - > authority ;
else
item = req - > additional ;
while ( item ) {
2006-12-12 03:56:41 +01:00
r = dnsname_to_labels ( buf , buf_len , j , item - > name , strlen ( item - > name ) , & table ) ;
if ( r < 0 )
goto overflow ;
j = r ;
2006-12-12 03:56:20 +01:00
APPEND16 ( item - > type ) ;
APPEND16 ( item - > class ) ;
APPEND32 ( item - > ttl ) ;
if ( item - > is_name ) {
off_t len_idx = j , name_start ;
j + = 2 ;
name_start = j ;
2006-12-12 03:56:41 +01:00
r = dnsname_to_labels ( buf , buf_len , j , item - > data , strlen ( item - > data ) , & table ) ;
if ( r < 0 )
goto overflow ;
j = r ;
2006-12-12 03:56:20 +01:00
_t = htons ( ( j - name_start ) ) ;
memcpy ( buf + len_idx , & _t , 2 ) ;
} else {
APPEND16 ( item - > datalen ) ;
if ( j + item - > datalen > ( off_t ) buf_len )
2006-12-12 03:56:41 +01:00
goto overflow ;
2006-12-12 03:56:20 +01:00
memcpy ( buf + j , item - > data , item - > datalen ) ;
j + = item - > datalen ;
}
item = item - > next ;
}
}
2006-12-12 03:56:41 +01:00
if ( j > 512 ) {
overflow :
j = 512 ;
buf [ 3 ] | = 0x02 ; /* set the truncated bit. */
}
2006-12-12 03:56:20 +01:00
req - > response_len = j ;
2006-12-12 03:56:41 +01:00
if ( ! ( req - > response = malloc ( req - > response_len ) ) ) {
2006-12-12 03:56:54 +01:00
server_request_free_answers ( req ) ;
2006-12-12 03:56:41 +01:00
dnslabel_clear ( & table ) ;
2006-12-12 03:56:47 +01:00
return ( - 1 ) ;
2006-12-12 03:56:41 +01:00
}
2006-12-12 03:56:20 +01:00
memcpy ( req - > response , buf , req - > response_len ) ;
2006-12-12 03:56:54 +01:00
server_request_free_answers ( req ) ;
2006-12-12 03:56:20 +01:00
dnslabel_clear ( & table ) ;
2006-12-12 03:56:47 +01:00
return ( 0 ) ;
2006-12-12 03:49:45 +01:00
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-12-12 03:49:45 +01:00
int
2006-12-12 03:56:54 +01:00
evdns_server_request_respond ( struct evdns_server_request * _req , int err )
2006-12-12 03:49:45 +01:00
{
struct server_request * req = TO_SERVER_REQUEST ( _req ) ;
2006-12-12 03:56:23 +01:00
struct evdns_server_port * port = req - > port ;
2006-12-12 03:49:45 +01:00
int r ;
2006-12-12 03:56:20 +01:00
if ( ! req - > response ) {
2006-12-12 03:56:54 +01:00
if ( ( r = evdns_server_request_format_response ( req , err ) ) < 0 )
2006-12-12 03:56:20 +01:00
return r ;
}
2006-12-12 03:49:45 +01:00
2006-12-12 03:56:23 +01:00
r = sendto ( port - > socket , req - > response , req - > response_len , 0 ,
2006-12-12 03:49:45 +01:00
( struct sockaddr * ) & req - > addr , req - > addrlen ) ;
if ( r < 0 ) {
2007-05-17 00:15:14 +02:00
int e = last_error ( port - > socket ) ;
if ( ! error_is_eagain ( e ) )
2006-12-12 03:49:45 +01:00
return - 1 ;
2006-12-12 03:56:23 +01:00
if ( port - > pending_replies ) {
req - > prev_pending = port - > pending_replies - > prev_pending ;
req - > next_pending = port - > pending_replies ;
2006-12-12 03:49:45 +01:00
req - > prev_pending - > next_pending =
req - > next_pending - > prev_pending = req ;
} else {
req - > prev_pending = req - > next_pending = req ;
2006-12-12 03:56:23 +01:00
port - > pending_replies = req ;
2006-12-12 21:20:48 +01:00
port - > choked = 1 ;
2006-12-12 03:56:23 +01:00
( void ) event_del ( & port - > event ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( & port - > event ) ;
2006-12-12 03:56:47 +01:00
event_set ( & port - > event , port - > socket , ( port - > closing ? 0 : EV_READ ) | EV_WRITE | EV_PERSIST , server_port_ready_callback , port ) ;
if ( event_add ( & port - > event , NULL ) < 0 ) {
2006-12-15 07:12:14 +01:00
log ( EVDNS_LOG_WARN , " Error from libevent when adding event for DNS server " ) ;
}
2006-12-12 03:56:23 +01:00
2006-12-12 03:49:45 +01:00
}
2006-12-12 03:56:23 +01:00
return 1 ;
}
2006-12-12 03:56:54 +01:00
if ( server_request_free ( req ) )
2006-12-12 03:56:47 +01:00
return 0 ;
2006-12-12 03:56:23 +01:00
2007-05-24 22:31:27 +02:00
if ( port - > pending_replies )
2006-12-12 03:56:23 +01:00
server_port_flush ( port ) ;
2006-12-12 03:49:45 +01:00
return 0 ;
}
2007-09-10 16:05:12 +02:00
/* Free all storage held by RRs in req. */
2006-12-12 03:49:45 +01:00
static void
2006-12-12 03:56:54 +01:00
server_request_free_answers ( struct server_request * req )
2006-12-12 03:56:20 +01:00
{
2006-12-12 03:57:02 +01:00
struct server_reply_item * victim , * next , * * list ;
2006-12-12 03:56:20 +01:00
int i ;
for ( i = 0 ; i < 3 ; + + i ) {
if ( i = = 0 )
list = & req - > answer ;
else if ( i = = 1 )
list = & req - > authority ;
else
list = & req - > additional ;
victim = * list ;
while ( victim ) {
next = victim - > next ;
free ( victim - > name ) ;
if ( victim - > data )
free ( victim - > data ) ;
2007-09-10 15:48:01 +02:00
free ( victim ) ;
2006-12-12 03:56:20 +01:00
victim = next ;
}
* list = NULL ;
}
}
2007-09-10 16:05:12 +02:00
/* Free all storage held by req, and remove links to it. */
/* return true iff we just wound up freeing the server_port. */
2006-12-12 03:56:47 +01:00
static int
2006-12-12 03:56:54 +01:00
server_request_free ( struct server_request * req )
2006-12-12 03:49:45 +01:00
{
2006-12-12 03:56:47 +01:00
int i , rc = 1 ;
2006-12-12 03:49:45 +01:00
if ( req - > base . questions ) {
for ( i = 0 ; i < req - > base . nquestions ; + + i )
free ( req - > base . questions [ i ] ) ;
2007-09-10 15:48:01 +02:00
free ( req - > base . questions ) ;
2006-12-12 03:49:45 +01:00
}
if ( req - > port ) {
if ( req - > port - > pending_replies = = req ) {
if ( req - > next_pending )
req - > port - > pending_replies = req - > next_pending ;
else
req - > port - > pending_replies = NULL ;
}
2006-12-12 03:56:47 +01:00
rc = - - req - > port - > refcnt ;
2006-12-12 03:49:45 +01:00
}
2007-01-10 20:49:21 +01:00
if ( req - > response ) {
2006-12-12 03:56:20 +01:00
free ( req - > response ) ;
2007-01-10 20:49:21 +01:00
}
2006-12-12 03:56:20 +01:00
2006-12-12 03:56:54 +01:00
server_request_free_answers ( req ) ;
2006-12-12 03:56:20 +01:00
2006-12-12 03:49:45 +01:00
if ( req - > next_pending & & req - > next_pending ! = req ) {
req - > next_pending - > prev_pending = req - > prev_pending ;
req - > prev_pending - > next_pending = req - > next_pending ;
}
2006-12-12 03:56:47 +01:00
if ( rc = = 0 ) {
2006-12-12 03:56:54 +01:00
server_port_free ( req - > port ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( req ) ;
2006-12-12 03:56:47 +01:00
free ( req ) ;
return ( 1 ) ;
}
2007-01-10 20:49:21 +01:00
CLEAR ( req ) ;
2006-12-12 03:49:45 +01:00
free ( req ) ;
2006-12-12 03:56:47 +01:00
return ( 0 ) ;
}
2007-09-10 16:05:12 +02:00
/* Free all storage held by an evdns_server_port. Only called when */
2006-12-12 03:56:47 +01:00
static void
2006-12-12 03:56:54 +01:00
server_port_free ( struct evdns_server_port * port )
2006-12-12 03:56:47 +01:00
{
assert ( port ) ;
assert ( ! port - > refcnt ) ;
assert ( ! port - > pending_replies ) ;
if ( port - > socket > 0 ) {
CLOSE_SOCKET ( port - > socket ) ;
port - > socket = - 1 ;
}
( void ) event_del ( & port - > event ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( & port - > event ) ;
2007-09-10 16:05:12 +02:00
/* XXXX020 actually free the port? -NM */
/* XXXX yes, and fix up evdns_close_server_port to dtrt. -NM */
2006-12-12 03:49:45 +01:00
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-12-12 03:56:20 +01:00
int
2006-12-12 03:56:54 +01:00
evdns_server_request_drop ( struct evdns_server_request * _req )
2006-12-12 03:56:20 +01:00
{
struct server_request * req = TO_SERVER_REQUEST ( _req ) ;
2006-12-12 03:56:54 +01:00
server_request_free ( req ) ;
2006-12-12 03:56:20 +01:00
return 0 ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2007-05-24 22:31:27 +02:00
int
2007-05-26 02:15:58 +02:00
evdns_server_request_get_requesting_addr ( struct evdns_server_request * _req , struct sockaddr * sa , int addr_len )
2007-05-24 22:31:27 +02:00
{
struct server_request * req = TO_SERVER_REQUEST ( _req ) ;
2007-05-26 02:15:58 +02:00
if ( addr_len < ( int ) req - > addrlen )
2007-05-24 22:31:27 +02:00
return - 1 ;
memcpy ( sa , & ( req - > addr ) , req - > addrlen ) ;
return req - > addrlen ;
}
2006-12-12 03:49:45 +01:00
# undef APPEND16
# undef APPEND32
2007-09-10 16:05:12 +02:00
/* this is a libevent callback function which is called when a request */
/* has timed out. */
2006-06-03 21:49:42 +02:00
static void
2006-09-24 19:52:23 +02:00
evdns_request_timeout_callback ( int fd , short events , void * arg ) {
2006-06-03 21:49:42 +02:00
struct request * const req = ( struct request * ) arg ;
2006-09-07 18:32:06 +02:00
( void ) fd ;
( void ) events ;
2006-07-23 07:40:24 +02:00
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG , " Request %lx timed out " , ( unsigned long ) arg ) ;
2006-06-03 21:49:42 +02:00
req - > ns - > timedout + + ;
if ( req - > ns - > timedout > global_max_nameserver_timeout ) {
2006-12-12 03:56:41 +01:00
req - > ns - > timedout = 0 ;
2006-07-05 23:28:37 +02:00
nameserver_failed ( req - > ns , " request timed out. " ) ;
2006-06-03 21:49:42 +02:00
}
2006-08-04 20:27:10 +02:00
( void ) evtimer_del ( & req - > timeout_event ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( & req - > timeout_event ) ;
2006-06-03 21:49:42 +02:00
if ( req - > tx_count > = global_max_retransmits ) {
2007-09-10 16:05:12 +02:00
/* this request has failed */
2006-08-16 20:47:24 +02:00
reply_callback ( req , 0 , DNS_ERR_TIMEOUT , NULL ) ;
2006-06-03 21:49:42 +02:00
request_finished ( req , & req_head ) ;
} else {
2007-09-10 16:05:12 +02:00
/* retransmit it */
2006-09-24 19:52:23 +02:00
evdns_request_transmit ( req ) ;
2006-06-03 21:49:42 +02:00
}
}
2007-09-10 16:05:12 +02:00
/* try to send a request to a given server. */
/* */
/* return: */
/* 0 ok */
/* 1 temporary failure */
/* 2 other failure */
2006-06-03 21:49:42 +02:00
static int
2006-09-24 19:52:23 +02:00
evdns_request_transmit_to ( struct request * req , struct nameserver * server ) {
2006-06-03 21:49:42 +02:00
const int r = send ( server - > socket , req - > request , req - > request_len , 0 ) ;
if ( r < 0 ) {
2006-07-05 23:28:37 +02:00
int err = last_error ( server - > socket ) ;
if ( error_is_eagain ( err ) ) return 1 ;
nameserver_failed ( req - > ns , strerror ( err ) ) ;
2006-06-03 21:49:42 +02:00
return 2 ;
2006-06-04 00:05:23 +02:00
} else if ( r ! = ( int ) req - > request_len ) {
2007-09-10 16:05:12 +02:00
return 1 ; /* short write */
2006-06-03 21:49:42 +02:00
} else {
return 0 ;
}
}
2007-09-10 16:05:12 +02:00
/* try to send a request, updating the fields of the request */
/* as needed */
/* */
/* return: */
/* 0 ok */
/* 1 failed */
2006-06-03 21:49:42 +02:00
static int
2006-09-24 19:52:23 +02:00
evdns_request_transmit ( struct request * req ) {
2006-06-04 00:05:23 +02:00
int retcode = 0 , r ;
2006-07-23 07:40:24 +02:00
2007-09-10 16:05:12 +02:00
/* if we fail to send this packet then this flag marks it */
/* for evdns_transmit */
2006-06-03 21:49:42 +02:00
req - > transmit_me = 1 ;
if ( req - > trans_id = = 0xffff ) abort ( ) ;
2006-12-12 21:20:48 +01:00
if ( req - > ns - > choked ) {
2007-09-10 16:05:12 +02:00
/* don't bother trying to write to a socket */
/* which we have had EAGAIN from */
2006-06-03 21:49:42 +02:00
return 1 ;
}
2006-09-24 19:52:23 +02:00
r = evdns_request_transmit_to ( req , req - > ns ) ;
2006-06-03 21:49:42 +02:00
switch ( r ) {
case 1 :
2007-09-10 16:05:12 +02:00
/* temp failure */
2006-12-12 21:20:48 +01:00
req - > ns - > choked = 1 ;
2006-06-03 21:49:42 +02:00
nameserver_write_waiting ( req - > ns , 1 ) ;
return 1 ;
case 2 :
2007-09-10 16:05:12 +02:00
/* failed in some other way */
2006-06-03 21:49:42 +02:00
retcode = 1 ;
2007-02-28 21:24:23 +01:00
break ;
2006-06-03 21:49:42 +02:00
default :
2007-09-10 16:05:12 +02:00
/* transmitted; we need to check for timeout. */
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG ,
2006-09-07 18:32:06 +02:00
" Setting timeout for request %lx " , ( unsigned long ) req ) ;
2006-09-24 19:52:23 +02:00
evtimer_set ( & req - > timeout_event , evdns_request_timeout_callback , req ) ;
2006-08-04 20:27:10 +02:00
if ( evtimer_add ( & req - > timeout_event , & global_timeout ) < 0 ) {
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_WARN ,
2006-12-15 07:12:01 +01:00
" Error from libevent when adding timer for request %lx " ,
( unsigned long ) req ) ;
2007-09-10 16:05:12 +02:00
/* ???? Do more? */
2006-09-07 18:32:06 +02:00
}
2006-06-03 21:49:42 +02:00
}
2007-02-28 21:24:23 +01:00
req - > tx_count + + ;
req - > transmit_me = 0 ;
return retcode ;
2006-06-03 21:49:42 +02:00
}
static void
nameserver_probe_callback ( int result , char type , int count , int ttl , void * addresses , void * arg ) {
struct nameserver * const ns = ( struct nameserver * ) arg ;
2006-09-07 18:32:06 +02:00
( void ) type ;
( void ) count ;
( void ) ttl ;
( void ) addresses ;
2006-07-23 07:39:37 +02:00
2006-06-03 21:49:42 +02:00
if ( result = = DNS_ERR_NONE | | result = = DNS_ERR_NOTEXIST ) {
2007-09-10 16:05:12 +02:00
/* this is a good reply */
2006-06-03 21:49:42 +02:00
nameserver_up ( ns ) ;
} else nameserver_probe_failed ( ns ) ;
}
static void
nameserver_send_probe ( struct nameserver * const ns ) {
2006-06-04 00:05:23 +02:00
struct request * req ;
2007-09-10 16:05:12 +02:00
/* here we need to send a probe to a given nameserver */
/* in the hope that it is up now. */
2006-06-03 21:49:42 +02:00
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG , " Sending probe to %s " , debug_ntoa ( ns - > address ) ) ;
2006-10-05 21:22:26 +02:00
2006-08-16 20:47:24 +02:00
req = request_new ( TYPE_A , " www.google.com " , DNS_QUERY_NO_SEARCH , nameserver_probe_callback , ns ) ;
2006-09-07 18:32:06 +02:00
if ( ! req ) return ;
2007-09-10 16:05:12 +02:00
/* we force this into the inflight queue no matter what */
2006-06-03 21:49:42 +02:00
request_trans_id_set ( req , transaction_id_pick ( ) ) ;
req - > ns = ns ;
request_submit ( req ) ;
}
2007-09-10 16:05:12 +02:00
/* returns: */
/* 0 didn't try to transmit anything */
/* 1 tried to transmit something */
2006-06-03 21:49:42 +02:00
static int
2006-09-24 19:52:23 +02:00
evdns_transmit ( void ) {
2006-06-03 21:49:42 +02:00
char did_try_to_transmit = 0 ;
if ( req_head ) {
struct request * const started_at = req_head , * req = req_head ;
2007-09-10 16:05:12 +02:00
/* first transmit all the requests which are currently waiting */
2006-06-03 21:49:42 +02:00
do {
if ( req - > transmit_me ) {
did_try_to_transmit = 1 ;
2006-09-24 19:52:23 +02:00
evdns_request_transmit ( req ) ;
2006-06-03 21:49:42 +02:00
}
req = req - > next ;
} while ( req ! = started_at ) ;
}
return did_try_to_transmit ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-08-04 20:26:13 +02:00
int
2006-12-12 06:45:19 +01:00
evdns_count_nameservers ( void )
{
2006-08-04 20:26:13 +02:00
const struct nameserver * server = server_head ;
int n = 0 ;
if ( ! server )
return 0 ;
do {
+ + n ;
server = server - > next ;
} while ( server ! = server_head ) ;
return n ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-08-04 20:26:13 +02:00
int
2006-12-12 06:45:19 +01:00
evdns_clear_nameservers_and_suspend ( void )
{
2006-08-04 20:26:13 +02:00
struct nameserver * server = server_head , * started_at = server_head ;
struct request * req = req_head , * req_started_at = req_head ;
if ( ! server )
return 0 ;
while ( 1 ) {
struct nameserver * next = server - > next ;
2006-08-04 20:27:10 +02:00
( void ) event_del ( & server - > event ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( & server - > event ) ;
2006-08-04 20:27:10 +02:00
( void ) evtimer_del ( & server - > timeout_event ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( & server - > timeout_event ) ;
2006-08-04 20:26:13 +02:00
if ( server - > socket > = 0 )
CLOSE_SOCKET ( server - > socket ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( server ) ;
2006-08-04 20:26:13 +02:00
free ( server ) ;
if ( next = = started_at )
break ;
server = next ;
}
server_head = NULL ;
2006-08-04 20:27:10 +02:00
global_good_nameservers = 0 ;
2006-08-04 20:26:13 +02:00
while ( req ) {
struct request * next = req - > next ;
req - > tx_count = req - > reissue_count = 0 ;
req - > ns = NULL ;
2007-09-10 16:05:12 +02:00
/* ???? What to do about searches? */
2006-08-04 20:27:10 +02:00
( void ) evtimer_del ( & req - > timeout_event ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( & req - > timeout_event ) ;
2006-08-04 20:26:13 +02:00
req - > trans_id = 0 ;
req - > transmit_me = 0 ;
2006-08-04 20:27:10 +02:00
global_requests_waiting + + ;
2006-09-24 19:52:23 +02:00
evdns_request_insert ( req , & req_waiting_head ) ;
2006-08-04 20:27:10 +02:00
/* We want to insert these suspended elements at the front of
* the waiting queue , since they were pending before any of
2006-09-07 18:32:06 +02:00
* the waiting entries were added . This is a circular list ,
2006-08-04 20:27:10 +02:00
* so we can just shift the start back by one . */
req_waiting_head = req_waiting_head - > prev ;
2006-08-04 20:26:13 +02:00
if ( next = = req_started_at )
break ;
req = next ;
}
req_head = NULL ;
2006-08-04 20:27:10 +02:00
global_requests_inflight = 0 ;
2006-08-04 20:26:13 +02:00
return 0 ;
}
2006-12-15 07:12:01 +01:00
2007-09-10 16:05:12 +02:00
/* exported function */
2006-08-04 20:26:13 +02:00
int
2006-12-12 06:45:19 +01:00
evdns_resume ( void )
{
2006-09-24 19:52:23 +02:00
evdns_requests_pump_waiting_queue ( ) ;
2006-08-04 20:26:13 +02:00
return 0 ;
}
2007-01-29 19:13:42 +01:00
static int
_evdns_nameserver_add_impl ( unsigned long int address , int port ) {
2007-09-10 16:05:12 +02:00
/* first check to see if we already have this nameserver */
2006-07-23 07:40:24 +02:00
2006-06-03 21:49:42 +02:00
const struct nameserver * server = server_head , * const started_at = server_head ;
2006-06-04 00:05:23 +02:00
struct nameserver * ns ;
struct sockaddr_in sin ;
int err = 0 ;
2006-06-03 21:49:42 +02:00
if ( server ) {
do {
if ( server - > address = = address ) return 3 ;
server = server - > next ;
} while ( server ! = started_at ) ;
}
2006-06-04 00:05:23 +02:00
ns = ( struct nameserver * ) malloc ( sizeof ( struct nameserver ) ) ;
2006-09-07 18:32:06 +02:00
if ( ! ns ) return - 1 ;
2006-06-03 21:49:42 +02:00
memset ( ns , 0 , sizeof ( struct nameserver ) ) ;
ns - > socket = socket ( PF_INET , SOCK_DGRAM , 0 ) ;
if ( ns - > socket < 0 ) { err = 1 ; goto out1 ; }
2006-12-15 07:12:01 +01:00
# ifdef WIN32
2006-09-07 18:32:06 +02:00
{
2006-08-03 09:46:25 +02:00
u_long nonblocking = 1 ;
ioctlsocket ( ns - > socket , FIONBIO , & nonblocking ) ;
2006-06-05 01:23:53 +02:00
}
# else
2006-09-07 18:32:06 +02:00
fcntl ( ns - > socket , F_SETFL , O_NONBLOCK ) ;
2006-06-05 01:23:53 +02:00
# endif
2006-06-03 21:49:42 +02:00
sin . sin_addr . s_addr = address ;
2007-01-29 19:13:42 +01:00
sin . sin_port = htons ( port ) ;
2006-06-03 21:49:42 +02:00
sin . sin_family = AF_INET ;
if ( connect ( ns - > socket , ( struct sockaddr * ) & sin , sizeof ( sin ) ) ! = 0 ) {
err = 2 ;
goto out2 ;
}
ns - > address = address ;
ns - > state = 1 ;
event_set ( & ns - > event , ns - > socket , EV_READ | EV_PERSIST , nameserver_ready_callback , ns ) ;
2006-08-04 20:27:10 +02:00
if ( event_add ( & ns - > event , NULL ) < 0 ) {
2006-09-07 18:32:06 +02:00
err = 2 ;
goto out2 ;
}
2006-06-03 21:49:42 +02:00
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG , " Added nameserver %s " , debug_ntoa ( address ) ) ;
2006-08-04 20:26:13 +02:00
2007-09-10 16:05:12 +02:00
/* insert this nameserver into the list of them */
2006-06-03 21:49:42 +02:00
if ( ! server_head ) {
ns - > next = ns - > prev = ns ;
server_head = ns ;
} else {
ns - > next = server_head - > next ;
ns - > prev = server_head ;
server_head - > next = ns ;
if ( server_head - > prev = = server_head ) {
server_head - > prev = ns ;
}
}
global_good_nameservers + + ;
2006-07-23 07:40:24 +02:00
2006-06-03 21:49:42 +02:00
return 0 ;
2006-12-12 06:45:19 +01:00
out2 :
2006-08-04 20:26:13 +02:00
CLOSE_SOCKET ( ns - > socket ) ;
2006-12-12 06:45:19 +01:00
out1 :
2007-01-10 20:49:21 +01:00
CLEAR ( ns ) ;
2006-06-03 21:49:42 +02:00
free ( ns ) ;
2006-12-12 06:45:19 +01:00
log ( EVDNS_LOG_WARN , " Unable to add nameserver %s: error %d " , debug_ntoa ( address ) , err ) ;
2006-06-03 21:49:42 +02:00
return err ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2007-01-29 19:13:42 +01:00
int
evdns_nameserver_add ( unsigned long int address ) {
return _evdns_nameserver_add_impl ( address , 53 ) ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-06-03 21:49:42 +02:00
int
2006-09-24 19:52:23 +02:00
evdns_nameserver_ip_add ( const char * ip_as_string ) {
2006-06-03 21:49:42 +02:00
struct in_addr ina ;
2007-01-29 19:13:42 +01:00
int port ;
char buf [ 20 ] ;
const char * cp ;
cp = strchr ( ip_as_string , ' : ' ) ;
if ( ! cp ) {
cp = ip_as_string ;
port = 53 ;
} else {
port = strtoint ( cp + 1 ) ;
if ( port < 0 | | port > 65535 ) {
return 4 ;
}
if ( ( cp - ip_as_string ) > = ( int ) sizeof ( buf ) ) {
return 4 ;
}
memcpy ( buf , ip_as_string , cp - ip_as_string ) ;
buf [ cp - ip_as_string ] = ' \0 ' ;
cp = buf ;
}
if ( ! inet_aton ( cp , & ina ) ) {
2006-12-12 06:45:19 +01:00
return 4 ;
2007-01-29 19:13:42 +01:00
}
return _evdns_nameserver_add_impl ( ina . s_addr , port ) ;
2006-06-03 21:49:42 +02:00
}
2007-09-10 16:05:12 +02:00
/* insert into the tail of the queue */
2006-06-03 21:49:42 +02:00
static void
2006-09-24 19:52:23 +02:00
evdns_request_insert ( struct request * req , struct request * * head ) {
2006-06-03 21:49:42 +02:00
if ( ! * head ) {
* head = req ;
req - > next = req - > prev = req ;
return ;
}
req - > prev = ( * head ) - > prev ;
req - > prev - > next = req ;
req - > next = * head ;
( * head ) - > prev = req ;
}
static int
string_num_dots ( const char * s ) {
int count = 0 ;
while ( ( s = strchr ( s , ' . ' ) ) ) {
s + + ;
count + + ;
}
return count ;
}
static struct request *
2006-12-12 06:45:19 +01:00
request_new ( int type , const char * name , int flags ,
evdns_callback_type callback , void * user_ptr ) {
const char issuing_now =
( global_requests_inflight < global_max_requests_inflight ) ? 1 : 0 ;
2006-06-03 21:49:42 +02:00
const int name_len = strlen ( name ) ;
2006-09-24 19:52:23 +02:00
const int request_max_len = evdns_request_len ( name_len ) ;
2006-06-03 21:49:42 +02:00
const u16 trans_id = issuing_now ? transaction_id_pick ( ) : 0xffff ;
2007-09-10 16:05:12 +02:00
/* the request data is alloced in a single block with the header */
2006-12-12 06:45:19 +01:00
struct request * const req =
( struct request * ) malloc ( sizeof ( struct request ) + request_max_len ) ;
2006-06-04 00:05:23 +02:00
int rlen ;
2006-09-07 18:32:06 +02:00
( void ) flags ;
2006-07-23 07:39:37 +02:00
2006-09-07 18:32:06 +02:00
if ( ! req ) return NULL ;
2006-06-03 21:49:42 +02:00
memset ( req , 0 , sizeof ( struct request ) ) ;
2007-09-10 16:05:12 +02:00
/* request data lives just after the header */
2006-06-03 21:49:42 +02:00
req - > request = ( ( u8 * ) req ) + sizeof ( struct request ) ;
2007-09-10 16:05:12 +02:00
/* denotes that the request data shouldn't be free()ed */
2006-10-05 21:22:26 +02:00
req - > request_appended = 1 ;
rlen = evdns_request_data_build ( name , name_len , trans_id ,
2006-12-12 03:56:41 +01:00
type , CLASS_INET , req - > request , request_max_len ) ;
2006-10-05 21:22:26 +02:00
if ( rlen < 0 )
2006-12-12 03:56:41 +01:00
goto err1 ;
2006-06-04 00:05:23 +02:00
req - > request_len = rlen ;
2006-06-03 21:49:42 +02:00
req - > trans_id = trans_id ;
req - > tx_count = 0 ;
2006-08-16 20:47:24 +02:00
req - > request_type = type ;
req - > user_pointer = user_ptr ;
2006-06-03 21:49:42 +02:00
req - > user_callback = callback ;
req - > ns = issuing_now ? nameserver_pick ( ) : NULL ;
req - > next = req - > prev = NULL ;
return req ;
2007-07-23 06:18:20 +02:00
err1 :
2007-01-10 20:49:21 +01:00
CLEAR ( req ) ;
2007-07-23 06:18:20 +02:00
_free ( req ) ;
2006-06-03 21:49:42 +02:00
return NULL ;
}
static void
request_submit ( struct request * const req ) {
if ( req - > ns ) {
2007-09-10 16:05:12 +02:00
/* if it has a nameserver assigned then this is going */
/* straight into the inflight queue */
2006-09-24 19:52:23 +02:00
evdns_request_insert ( req , & req_head ) ;
2006-06-03 21:49:42 +02:00
global_requests_inflight + + ;
2006-09-24 19:52:23 +02:00
evdns_request_transmit ( req ) ;
2006-06-03 21:49:42 +02:00
} else {
2006-09-24 19:52:23 +02:00
evdns_request_insert ( req , & req_waiting_head ) ;
2006-06-03 21:49:42 +02:00
global_requests_waiting + + ;
}
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-10-05 21:22:26 +02:00
int evdns_resolve_ipv4 ( const char * name , int flags ,
2006-12-15 07:12:14 +01:00
evdns_callback_type callback , void * ptr ) {
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG , " Resolve requested for %s " , name ) ;
2006-06-03 21:49:42 +02:00
if ( flags & DNS_QUERY_NO_SEARCH ) {
2006-10-05 21:22:26 +02:00
struct request * const req =
2006-12-15 07:12:14 +01:00
request_new ( TYPE_A , name , flags , callback , ptr ) ;
2006-10-05 21:22:26 +02:00
if ( req = = NULL )
2006-12-15 07:12:14 +01:00
return ( 1 ) ;
2006-06-03 21:49:42 +02:00
request_submit ( req ) ;
2006-12-12 06:45:19 +01:00
return ( 0 ) ;
2006-06-03 21:49:42 +02:00
} else {
2006-12-15 07:12:01 +01:00
return ( search_request_new ( TYPE_A , name , flags , callback , ptr ) ) ;
2006-06-03 21:49:42 +02:00
}
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-12-12 04:48:46 +01:00
int evdns_resolve_ipv6 ( const char * name , int flags ,
2006-12-15 07:12:14 +01:00
evdns_callback_type callback , void * ptr ) {
2006-12-12 04:48:46 +01:00
log ( EVDNS_LOG_DEBUG , " Resolve requested for %s " , name ) ;
if ( flags & DNS_QUERY_NO_SEARCH ) {
struct request * const req =
2006-12-15 07:12:14 +01:00
request_new ( TYPE_AAAA , name , flags , callback , ptr ) ;
2006-12-12 04:48:46 +01:00
if ( req = = NULL )
2006-12-15 07:12:14 +01:00
return ( 1 ) ;
2006-12-12 04:48:46 +01:00
request_submit ( req ) ;
2006-12-12 06:45:19 +01:00
return ( 0 ) ;
2006-12-12 04:48:46 +01:00
} else {
2006-12-15 07:12:01 +01:00
return ( search_request_new ( TYPE_AAAA , name , flags , callback , ptr ) ) ;
2006-12-12 04:48:46 +01:00
}
}
2006-09-24 19:52:23 +02:00
int evdns_resolve_reverse ( struct in_addr * in , int flags , evdns_callback_type callback , void * ptr ) {
2006-08-16 20:47:24 +02:00
char buf [ 32 ] ;
struct request * req ;
u32 a ;
assert ( in ) ;
a = ntohl ( in - > s_addr ) ;
2007-01-16 00:56:38 +01:00
snprintf ( buf , sizeof ( buf ) , " %d.%d.%d.%d.in-addr.arpa " ,
2006-09-07 18:32:06 +02:00
( int ) ( u8 ) ( ( a ) & 0xff ) ,
( int ) ( u8 ) ( ( a > > 8 ) & 0xff ) ,
( int ) ( u8 ) ( ( a > > 16 ) & 0xff ) ,
( int ) ( u8 ) ( ( a > > 24 ) & 0xff ) ) ;
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG , " Resolve requested for %s (reverse) " , buf ) ;
2006-08-16 20:47:24 +02:00
req = request_new ( TYPE_PTR , buf , flags , callback , ptr ) ;
if ( ! req ) return 1 ;
request_submit ( req ) ;
return 0 ;
}
2006-12-12 04:48:46 +01:00
int evdns_resolve_reverse_ipv6 ( struct in6_addr * in , int flags , evdns_callback_type callback , void * ptr ) {
2007-07-26 00:56:47 +02:00
/* 32 nybbles, 32 periods, "ip6.arpa", NUL. */
char buf [ 73 ] ;
2006-12-15 07:12:14 +01:00
char * cp ;
2006-12-12 04:48:46 +01:00
struct request * req ;
2006-12-15 07:12:14 +01:00
int i ;
2006-12-12 04:48:46 +01:00
assert ( in ) ;
2006-12-15 07:12:14 +01:00
cp = buf ;
2007-01-21 19:21:39 +01:00
for ( i = 15 ; i > = 0 ; - - i ) {
2006-12-15 07:12:14 +01:00
u8 byte = in - > s6_addr [ i ] ;
2006-12-12 04:48:46 +01:00
* cp + + = " 0123456789abcdef " [ byte & 0x0f ] ;
2006-12-15 07:12:14 +01:00
* cp + + = ' . ' ;
2007-01-21 19:21:39 +01:00
* cp + + = " 0123456789abcdef " [ byte > > 4 ] ;
* cp + + = ' . ' ;
2006-12-15 07:12:14 +01:00
}
2007-07-26 00:56:47 +02:00
assert ( cp + strlen ( " ip6.arpa " ) < buf + sizeof ( buf ) ) ;
memcpy ( cp , " ip6.arpa " , strlen ( " ip6.arpa " ) + 1 ) ;
2006-12-12 04:48:46 +01:00
log ( EVDNS_LOG_DEBUG , " Resolve requested for %s (reverse) " , buf ) ;
req = request_new ( TYPE_PTR , buf , flags , callback , ptr ) ;
if ( ! req ) return 1 ;
request_submit ( req ) ;
return 0 ;
}
2007-09-10 16:05:12 +02:00
/*/////////////////////////////////////////////////////////////////// */
/* Search support */
/* */
/* the libc resolver has support for searching a number of domains */
/* to find a name. If nothing else then it takes the single domain */
/* from the gethostname() call. */
/* */
/* It can also be configured via the domain and search options in a */
/* resolv.conf. */
/* */
/* The ndots option controls how many dots it takes for the resolver */
/* to decide that a name is non-local and so try a raw lookup first. */
2006-06-03 21:49:42 +02:00
struct search_domain {
int len ;
struct search_domain * next ;
2007-09-10 16:05:12 +02:00
/* the text string is appended to this structure */
2006-06-03 21:49:42 +02:00
} ;
struct search_state {
int refcount ;
int ndots ;
int num_domains ;
struct search_domain * head ;
} ;
static struct search_state * global_search_state = NULL ;
static void
search_state_decref ( struct search_state * const state ) {
if ( ! state ) return ;
state - > refcount - - ;
if ( ! state - > refcount ) {
struct search_domain * next , * dom ;
for ( dom = state - > head ; dom ; dom = next ) {
next = dom - > next ;
2007-01-10 20:49:21 +01:00
CLEAR ( dom ) ;
2007-07-23 06:18:20 +02:00
_free ( dom ) ;
2006-06-03 21:49:42 +02:00
}
2007-01-10 20:49:21 +01:00
CLEAR ( state ) ;
2007-07-23 06:18:20 +02:00
_free ( state ) ;
2006-06-03 21:49:42 +02:00
}
2006-09-07 18:24:19 +02:00
}
2006-06-03 21:49:42 +02:00
static struct search_state *
2006-06-04 00:05:23 +02:00
search_state_new ( void ) {
2006-06-03 21:49:42 +02:00
struct search_state * state = ( struct search_state * ) malloc ( sizeof ( struct search_state ) ) ;
2006-09-07 18:32:06 +02:00
if ( ! state ) return NULL ;
2006-06-03 21:49:42 +02:00
memset ( state , 0 , sizeof ( struct search_state ) ) ;
state - > refcount = 1 ;
state - > ndots = 1 ;
return state ;
}
static void
2006-06-04 00:05:23 +02:00
search_postfix_clear ( void ) {
2006-06-03 21:49:42 +02:00
search_state_decref ( global_search_state ) ;
2006-07-23 07:40:24 +02:00
2006-06-03 21:49:42 +02:00
global_search_state = search_state_new ( ) ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-06-03 21:49:42 +02:00
void
2006-09-24 19:52:23 +02:00
evdns_search_clear ( void ) {
2006-06-03 21:49:42 +02:00
search_postfix_clear ( ) ;
}
static void
search_postfix_add ( const char * domain ) {
2006-06-04 00:05:23 +02:00
int domain_len ;
struct search_domain * sdomain ;
2006-06-03 21:49:42 +02:00
while ( domain [ 0 ] = = ' . ' ) domain + + ;
2006-06-04 00:05:23 +02:00
domain_len = strlen ( domain ) ;
2006-06-03 21:49:42 +02:00
if ( ! global_search_state ) global_search_state = search_state_new ( ) ;
2006-09-07 18:32:06 +02:00
if ( ! global_search_state ) return ;
2006-06-03 21:49:42 +02:00
global_search_state - > num_domains + + ;
2006-06-04 00:05:23 +02:00
sdomain = ( struct search_domain * ) malloc ( sizeof ( struct search_domain ) + domain_len ) ;
2006-09-07 18:32:06 +02:00
if ( ! sdomain ) return ;
2006-06-03 21:49:42 +02:00
memcpy ( ( ( u8 * ) sdomain ) + sizeof ( struct search_domain ) , domain , domain_len ) ;
sdomain - > next = global_search_state - > head ;
sdomain - > len = domain_len ;
global_search_state - > head = sdomain ;
}
2007-09-10 16:05:12 +02:00
/* reverse the order of members in the postfix list. This is needed because, */
/* when parsing resolv.conf we push elements in the wrong order */
2006-06-03 21:49:42 +02:00
static void
2006-06-04 00:05:23 +02:00
search_reverse ( void ) {
2006-06-03 21:49:42 +02:00
struct search_domain * cur , * prev = NULL , * next ;
cur = global_search_state - > head ;
while ( cur ) {
next = cur - > next ;
cur - > next = prev ;
prev = cur ;
cur = next ;
}
global_search_state - > head = prev ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-06-03 21:49:42 +02:00
void
2006-09-24 19:52:23 +02:00
evdns_search_add ( const char * domain ) {
2006-06-03 21:49:42 +02:00
search_postfix_add ( domain ) ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-06-03 21:49:42 +02:00
void
2006-09-24 19:52:23 +02:00
evdns_search_ndots_set ( const int ndots ) {
2006-06-03 21:49:42 +02:00
if ( ! global_search_state ) global_search_state = search_state_new ( ) ;
2006-09-07 18:32:06 +02:00
if ( ! global_search_state ) return ;
2006-06-03 21:49:42 +02:00
global_search_state - > ndots = ndots ;
}
static void
2006-06-04 00:05:23 +02:00
search_set_from_hostname ( void ) {
char hostname [ HOST_NAME_MAX + 1 ] , * domainname ;
2006-06-03 21:49:42 +02:00
search_postfix_clear ( ) ;
if ( gethostname ( hostname , sizeof ( hostname ) ) ) return ;
2006-06-04 00:05:23 +02:00
domainname = strchr ( hostname , ' . ' ) ;
2006-06-03 21:49:42 +02:00
if ( ! domainname ) return ;
search_postfix_add ( domainname ) ;
}
2007-09-10 16:05:12 +02:00
/* warning: returns malloced string */
2006-06-03 21:49:42 +02:00
static char *
search_make_new ( const struct search_state * const state , int n , const char * const base_name ) {
const int base_len = strlen ( base_name ) ;
const char need_to_append_dot = base_name [ base_len - 1 ] = = ' . ' ? 0 : 1 ;
struct search_domain * dom ;
for ( dom = state - > head ; dom ; dom = dom - > next ) {
if ( ! n - - ) {
2007-09-10 16:05:12 +02:00
/* this is the postfix we want */
/* the actual postfix string is kept at the end of the structure */
2006-06-03 21:49:42 +02:00
const u8 * const postfix = ( ( u8 * ) dom ) + sizeof ( struct search_domain ) ;
const int postfix_len = dom - > len ;
char * const newname = ( char * ) malloc ( base_len + need_to_append_dot + postfix_len + 1 ) ;
2007-01-10 20:49:21 +01:00
if ( ! newname ) return NULL ;
2006-06-03 21:49:42 +02:00
memcpy ( newname , base_name , base_len ) ;
if ( need_to_append_dot ) newname [ base_len ] = ' . ' ;
memcpy ( newname + base_len + need_to_append_dot , postfix , postfix_len ) ;
newname [ base_len + need_to_append_dot + postfix_len ] = 0 ;
return newname ;
}
}
2007-09-10 16:05:12 +02:00
/* we ran off the end of the list and still didn't find the requested string */
2006-06-03 21:49:42 +02:00
abort ( ) ;
2006-12-15 07:12:01 +01:00
return NULL ; /* unreachable; stops warnings in some compilers. */
2006-06-03 21:49:42 +02:00
}
static int
2006-09-24 19:52:23 +02:00
search_request_new ( int type , const char * const name , int flags , evdns_callback_type user_callback , void * user_arg ) {
2007-01-27 06:15:53 +01:00
assert ( type = = TYPE_A | | type = = TYPE_AAAA ) ;
2006-06-03 21:49:42 +02:00
if ( ( ( flags & DNS_QUERY_NO_SEARCH ) = = 0 ) & &
2006-09-07 18:32:06 +02:00
global_search_state & &
2006-06-03 21:49:42 +02:00
global_search_state - > num_domains ) {
2007-09-10 16:05:12 +02:00
/* we have some domains to search */
2006-06-03 21:49:42 +02:00
struct request * req ;
if ( string_num_dots ( name ) > = global_search_state - > ndots ) {
2006-08-16 20:47:24 +02:00
req = request_new ( type , name , flags , user_callback , user_arg ) ;
2006-06-03 21:49:42 +02:00
if ( ! req ) return 1 ;
req - > search_index = - 1 ;
} else {
char * const new_name = search_make_new ( global_search_state , 0 , name ) ;
2006-09-07 18:32:06 +02:00
if ( ! new_name ) return 1 ;
2006-08-16 20:47:24 +02:00
req = request_new ( type , new_name , flags , user_callback , user_arg ) ;
2007-07-23 06:18:20 +02:00
_free ( new_name ) ;
2006-06-03 21:49:42 +02:00
if ( ! req ) return 1 ;
req - > search_index = 0 ;
}
req - > search_origname = strdup ( name ) ;
req - > search_state = global_search_state ;
req - > search_flags = flags ;
global_search_state - > refcount + + ;
request_submit ( req ) ;
return 0 ;
} else {
2006-08-16 20:47:24 +02:00
struct request * const req = request_new ( type , name , flags , user_callback , user_arg ) ;
2006-06-03 21:49:42 +02:00
if ( ! req ) return 1 ;
request_submit ( req ) ;
return 0 ;
}
}
2007-09-10 16:05:12 +02:00
/* this is called when a request has failed to find a name. We need to check */
/* if it is part of a search and, if so, try the next name in the list */
/* returns: */
/* 0 another request has been submitted */
/* 1 no more requests needed */
2006-06-03 21:49:42 +02:00
static int
search_try_next ( struct request * const req ) {
if ( req - > search_state ) {
2007-09-10 16:05:12 +02:00
/* it is part of a search */
2006-06-04 00:05:23 +02:00
char * new_name ;
struct request * newreq ;
2006-06-03 21:49:42 +02:00
req - > search_index + + ;
if ( req - > search_index > = req - > search_state - > num_domains ) {
2007-09-10 16:05:12 +02:00
/* no more postfixes to try, however we may need to try */
/* this name without a postfix */
2006-06-03 21:49:42 +02:00
if ( string_num_dots ( req - > search_origname ) < req - > search_state - > ndots ) {
2007-09-10 16:05:12 +02:00
/* yep, we need to try it raw */
2007-05-17 00:15:14 +02:00
newreq = request_new ( req - > request_type , req - > search_origname , req - > search_flags , req - > user_callback , req - > user_pointer ) ;
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG , " Search: trying raw query %s " , req - > search_origname ) ;
2006-06-03 21:49:42 +02:00
if ( newreq ) {
request_submit ( newreq ) ;
return 0 ;
}
}
return 1 ;
}
2006-06-04 00:05:23 +02:00
new_name = search_make_new ( req - > search_state , req - > search_index , req - > search_origname ) ;
2006-09-07 18:32:06 +02:00
if ( ! new_name ) return 1 ;
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG , " Search: now trying %s (%d) " , new_name , req - > search_index ) ;
2006-08-16 20:47:24 +02:00
newreq = request_new ( req - > request_type , new_name , req - > search_flags , req - > user_callback , req - > user_pointer ) ;
2006-06-03 21:49:42 +02:00
free ( new_name ) ;
if ( ! newreq ) return 1 ;
newreq - > search_origname = req - > search_origname ;
req - > search_origname = NULL ;
newreq - > search_state = req - > search_state ;
newreq - > search_flags = req - > search_flags ;
newreq - > search_index = req - > search_index ;
newreq - > search_state - > refcount + + ;
request_submit ( newreq ) ;
return 0 ;
}
return 1 ;
}
static void
search_request_finished ( struct request * const req ) {
if ( req - > search_state ) {
search_state_decref ( req - > search_state ) ;
req - > search_state = NULL ;
}
if ( req - > search_origname ) {
free ( req - > search_origname ) ;
req - > search_origname = NULL ;
}
}
2007-09-10 16:05:12 +02:00
/*/////////////////////////////////////////////////////////////////// */
/* Parsing resolv.conf files */
2006-06-03 21:49:42 +02:00
static void
2006-09-24 19:52:23 +02:00
evdns_resolv_set_defaults ( int flags ) {
2007-09-10 16:05:12 +02:00
/* if the file isn't found then we assume a local resolver */
2006-06-03 21:49:42 +02:00
if ( flags & DNS_OPTION_SEARCH ) search_set_from_hostname ( ) ;
2006-09-24 19:52:23 +02:00
if ( flags & DNS_OPTION_NAMESERVERS ) evdns_nameserver_ip_add ( " 127.0.0.1 " ) ;
2006-06-03 21:49:42 +02:00
}
# ifndef HAVE_STRTOK_R
static char *
strtok_r ( char * s , const char * delim , char * * state ) {
2007-02-28 21:24:27 +01:00
( void ) state ;
2006-06-03 21:49:42 +02:00
return strtok ( s , delim ) ;
}
# endif
2007-09-10 16:05:12 +02:00
/* helper version of atoi which returns -1 on error */
2006-06-03 21:49:42 +02:00
static int
strtoint ( const char * const str ) {
char * endptr ;
const int r = strtol ( str , & endptr , 10 ) ;
if ( * endptr ) return - 1 ;
return r ;
}
2007-09-10 16:05:12 +02:00
/* helper version of atoi that returns -1 on error and clips to bounds. */
2006-12-28 22:29:31 +01:00
static int
strtoint_clipped ( const char * const str , int min , int max )
{
int r = strtoint ( str ) ;
if ( r = = - 1 )
return r ;
else if ( r < min )
return min ;
else if ( r > max )
return max ;
else
return r ;
}
2007-09-10 16:05:12 +02:00
/* exported function */
2006-12-28 22:29:31 +01:00
int
evdns_set_option ( const char * option , const char * val , int flags )
{
if ( ! strncmp ( option , " ndots: " , 6 ) ) {
const int ndots = strtoint ( val ) ;
if ( ndots = = - 1 ) return - 1 ;
if ( ! ( flags & DNS_OPTION_SEARCH ) ) return 0 ;
log ( EVDNS_LOG_DEBUG , " Setting ndots to %d " , ndots ) ;
if ( ! global_search_state ) global_search_state = search_state_new ( ) ;
if ( ! global_search_state ) return - 1 ;
global_search_state - > ndots = ndots ;
} else if ( ! strncmp ( option , " timeout: " , 8 ) ) {
const int timeout = strtoint ( val ) ;
if ( timeout = = - 1 ) return - 1 ;
if ( ! ( flags & DNS_OPTION_MISC ) ) return 0 ;
log ( EVDNS_LOG_DEBUG , " Setting timeout to %d " , timeout ) ;
global_timeout . tv_sec = timeout ;
} else if ( ! strncmp ( option , " max-timeouts: " , 12 ) ) {
const int maxtimeout = strtoint_clipped ( val , 1 , 255 ) ;
if ( maxtimeout = = - 1 ) return - 1 ;
if ( ! ( flags & DNS_OPTION_MISC ) ) return 0 ;
log ( EVDNS_LOG_DEBUG , " Setting maximum allowed timeouts to %d " ,
maxtimeout ) ;
global_max_nameserver_timeout = maxtimeout ;
} else if ( ! strncmp ( option , " max-inflight: " , 13 ) ) {
const int maxinflight = strtoint_clipped ( val , 1 , 65000 ) ;
if ( maxinflight = = - 1 ) return - 1 ;
if ( ! ( flags & DNS_OPTION_MISC ) ) return 0 ;
log ( EVDNS_LOG_DEBUG , " Setting maximum inflight requests to %d " ,
maxinflight ) ;
global_max_requests_inflight = maxinflight ;
} else if ( ! strncmp ( option , " attempts: " , 9 ) ) {
int retries = strtoint ( val ) ;
if ( retries = = - 1 ) return - 1 ;
if ( retries > 255 ) retries = 255 ;
if ( ! ( flags & DNS_OPTION_MISC ) ) return 0 ;
log ( EVDNS_LOG_DEBUG , " Setting retries to %d " , retries ) ;
global_max_retransmits = retries ;
}
return 0 ;
}
2006-06-03 21:49:42 +02:00
static void
resolv_conf_parse_line ( char * const start , int flags ) {
char * strtok_state ;
static const char * const delims = " \t " ;
# define NEXT_TOKEN strtok_r(NULL, delims, &strtok_state)
char * const first_token = strtok_r ( start , delims , & strtok_state ) ;
2007-05-29 21:54:54 +02:00
if ( ! first_token ) return ;
2006-07-23 07:40:24 +02:00
2006-12-19 20:49:03 +01:00
if ( ! strcmp ( first_token , " nameserver " ) & & ( flags & DNS_OPTION_NAMESERVERS ) ) {
2006-06-03 21:49:42 +02:00
const char * const nameserver = NEXT_TOKEN ;
struct in_addr ina ;
if ( inet_aton ( nameserver , & ina ) ) {
2007-09-10 16:05:12 +02:00
/* address is valid */
2006-09-24 19:52:23 +02:00
evdns_nameserver_add ( ina . s_addr ) ;
2006-06-03 21:49:42 +02:00
}
} else if ( ! strcmp ( first_token , " domain " ) & & ( flags & DNS_OPTION_SEARCH ) ) {
const char * const domain = NEXT_TOKEN ;
if ( domain ) {
search_postfix_clear ( ) ;
search_postfix_add ( domain ) ;
}
} else if ( ! strcmp ( first_token , " search " ) & & ( flags & DNS_OPTION_SEARCH ) ) {
const char * domain ;
2006-06-04 00:05:23 +02:00
search_postfix_clear ( ) ;
2006-06-03 21:49:42 +02:00
while ( ( domain = NEXT_TOKEN ) ) {
search_postfix_add ( domain ) ;
}
search_reverse ( ) ;
} else if ( ! strcmp ( first_token , " options " ) ) {
const char * option ;
2006-07-23 07:40:24 +02:00
while ( ( option = NEXT_TOKEN ) ) {
2006-12-28 22:29:31 +01:00
const char * val = strchr ( option , ' : ' ) ;
2007-01-10 20:49:21 +01:00
evdns_set_option ( option , val ? val + 1 : " " , flags ) ;
2006-06-03 21:49:42 +02:00
}
}
# undef NEXT_TOKEN
}
2007-09-10 16:05:12 +02:00
/* exported function */
/* returns: */
/* 0 no errors */
/* 1 failed to open file */
/* 2 failed to stat file */
/* 3 file too large */
/* 4 out of memory */
/* 5 short read from file */
2006-06-03 21:49:42 +02:00
int
2006-09-24 19:52:23 +02:00
evdns_resolv_conf_parse ( int flags , const char * const filename ) {
2006-06-03 21:49:42 +02:00
struct stat st ;
2007-05-21 23:48:02 +02:00
int fd , n , r ;
2006-06-04 00:05:23 +02:00
u8 * resolv ;
char * start ;
2006-06-03 21:49:42 +02:00
int err = 0 ;
2006-10-05 21:22:26 +02:00
log ( EVDNS_LOG_DEBUG , " Parsing resolv.conf file %s " , filename ) ;
2006-06-03 21:49:42 +02:00
2006-06-04 00:05:23 +02:00
fd = open ( filename , O_RDONLY ) ;
2006-06-03 21:49:42 +02:00
if ( fd < 0 ) {
2006-09-24 19:52:23 +02:00
evdns_resolv_set_defaults ( flags ) ;
2006-12-12 06:45:19 +01:00
return 1 ;
2006-06-03 21:49:42 +02:00
}
2006-07-23 07:40:24 +02:00
2006-06-03 21:49:42 +02:00
if ( fstat ( fd , & st ) ) { err = 2 ; goto out1 ; }
if ( ! st . st_size ) {
2006-09-24 19:52:23 +02:00
evdns_resolv_set_defaults ( flags ) ;
2006-12-19 20:49:03 +01:00
err = ( flags & DNS_OPTION_NAMESERVERS ) ? 6 : 0 ;
2006-06-03 21:49:42 +02:00
goto out1 ;
}
2007-09-10 16:05:12 +02:00
if ( st . st_size > 65535 ) { err = 3 ; goto out1 ; } /* no resolv.conf should be any bigger */
2006-06-03 21:49:42 +02:00
2006-11-14 02:07:52 +01:00
resolv = ( u8 * ) malloc ( ( size_t ) st . st_size + 1 ) ;
2006-06-03 21:49:42 +02:00
if ( ! resolv ) { err = 4 ; goto out1 ; }
2006-07-23 07:40:24 +02:00
2007-05-21 23:48:02 +02:00
n = 0 ;
while ( ( r = read ( fd , resolv + n , ( size_t ) st . st_size - n ) ) > 0 ) {
n + = r ;
if ( n = = st . st_size )
break ;
assert ( n < st . st_size ) ;
2006-11-14 02:07:52 +01:00
}
2007-05-21 23:48:02 +02:00
if ( r < 0 ) { err = 5 ; goto out2 ; }
2007-09-10 16:05:12 +02:00
resolv [ n ] = 0 ; /* we malloced an extra byte; this should be fine. */
2006-07-23 07:40:24 +02:00
2006-06-04 00:05:23 +02:00
start = ( char * ) resolv ;
2006-06-03 21:49:42 +02:00
for ( ; ; ) {
char * const newline = strchr ( start , ' \n ' ) ;
if ( ! newline ) {
resolv_conf_parse_line ( start , flags ) ;
break ;
} else {
* newline = 0 ;
resolv_conf_parse_line ( start , flags ) ;
start = newline + 1 ;
}
}
if ( ! server_head & & ( flags & DNS_OPTION_NAMESERVERS ) ) {
2007-09-10 16:05:12 +02:00
/* no nameservers were configured. */
2006-09-24 19:52:23 +02:00
evdns_nameserver_ip_add ( " 127.0.0.1 " ) ;
2007-01-10 20:49:21 +01:00
err = 6 ;
2006-06-03 21:49:42 +02:00
}
if ( flags & DNS_OPTION_SEARCH & & ( ! global_search_state | | global_search_state - > num_domains = = 0 ) ) {
search_set_from_hostname ( ) ;
}
out2 :
free ( resolv ) ;
out1 :
close ( fd ) ;
return err ;
}
2006-06-13 23:49:56 +02:00
2006-12-15 07:12:01 +01:00
# ifdef WIN32
2007-09-10 16:05:12 +02:00
/* Add multiple nameservers from a space-or-comma-separated list. */
2006-07-31 19:59:11 +02:00
static int
2006-09-24 19:52:23 +02:00
evdns_nameserver_ip_add_line ( const char * ips ) {
2006-07-31 19:59:11 +02:00
const char * addr ;
char * buf ;
int r ;
while ( * ips ) {
while ( ISSPACE ( * ips ) | | * ips = = ' , ' | | * ips = = ' \t ' )
+ + ips ;
addr = ips ;
2007-01-29 19:13:42 +01:00
while ( ISDIGIT ( * ips ) | | * ips = = ' . ' | | * ips = = ' : ' )
2006-07-31 19:59:11 +02:00
+ + ips ;
buf = malloc ( ips - addr + 1 ) ;
if ( ! buf ) return 4 ;
memcpy ( buf , addr , ips - addr ) ;
buf [ ips - addr ] = ' \0 ' ;
2006-09-24 19:52:23 +02:00
r = evdns_nameserver_ip_add ( buf ) ;
2006-07-31 19:59:11 +02:00
free ( buf ) ;
if ( r ) return r ;
}
return 0 ;
}
2006-09-08 11:05:07 +02:00
typedef DWORD ( WINAPI * GetNetworkParams_fn_t ) ( FIXED_INFO * , DWORD * ) ;
2006-09-07 18:24:19 +02:00
2007-09-10 16:05:12 +02:00
/* Use the windows GetNetworkParams interface in iphlpapi.dll to */
/* figure out what our nameservers are. */
2006-06-13 23:49:56 +02:00
static int
2006-12-12 06:45:19 +01:00
load_nameservers_with_getnetworkparams ( void )
{
2007-09-10 16:05:12 +02:00
/* Based on MSDN examples and inspection of c-ares code. */
2006-06-13 23:49:56 +02:00
FIXED_INFO * fixed ;
HMODULE handle = 0 ;
ULONG size = sizeof ( FIXED_INFO ) ;
void * buf = NULL ;
int status = 0 , r , added_any ;
IP_ADDR_STRING * ns ;
2006-09-08 11:02:14 +02:00
GetNetworkParams_fn_t fn ;
2006-06-13 23:49:56 +02:00
2007-02-01 19:33:02 +01:00
/* XXXX Possibly, we should hardcode the location of this DLL. */
2006-09-07 08:34:20 +02:00
if ( ! ( handle = LoadLibrary ( " iphlpapi.dll " ) ) ) {
2006-10-05 21:22:26 +02:00
log ( EVDNS_LOG_WARN , " Could not open iphlpapi.dll " ) ;
2007-09-10 16:05:12 +02:00
/* right now status = 0, doesn't that mean "good" - mikec */
2006-09-07 18:32:06 +02:00
status = - 1 ;
goto done ;
2006-09-07 08:34:20 +02:00
}
2006-10-05 21:22:26 +02:00
if ( ! ( fn = ( GetNetworkParams_fn_t ) GetProcAddress ( handle , " GetNetworkParams " ) ) ) {
log ( EVDNS_LOG_WARN , " Could not get address of function. " ) ;
2007-09-10 16:05:12 +02:00
/* same as above */
2006-09-07 18:32:06 +02:00
status = - 1 ;
goto done ;
2006-09-07 05:53:14 +02:00
}
2006-06-13 23:49:56 +02:00
buf = malloc ( size ) ;
2006-12-12 06:45:19 +01:00
if ( ! buf ) { status = 4 ; goto done ; }
2006-06-13 23:49:56 +02:00
fixed = buf ;
r = fn ( fixed , & size ) ;
if ( r ! = ERROR_SUCCESS & & r ! = ERROR_BUFFER_OVERFLOW ) {
status = - 1 ;
goto done ;
}
if ( r ! = ERROR_SUCCESS ) {
free ( buf ) ;
buf = malloc ( size ) ;
if ( ! buf ) { status = 4 ; goto done ; }
fixed = buf ;
r = fn ( fixed , & size ) ;
2006-09-07 08:34:20 +02:00
if ( r ! = ERROR_SUCCESS ) {
2006-10-05 21:22:26 +02:00
log ( EVDNS_LOG_DEBUG , " fn() failed. " ) ;
2006-09-07 18:32:06 +02:00
status = - 1 ;
goto done ;
2006-09-07 08:34:20 +02:00
}
2006-06-13 23:49:56 +02:00
}
assert ( fixed ) ;
added_any = 0 ;
2006-09-07 02:51:20 +02:00
ns = & ( fixed - > DnsServerList ) ;
2006-06-13 23:49:56 +02:00
while ( ns ) {
2006-09-24 19:52:23 +02:00
r = evdns_nameserver_ip_add_line ( ns - > IpAddress . String ) ;
2006-09-07 18:32:06 +02:00
if ( r ) {
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG , " Could not add nameserver %s to list,error: %d " ,
2006-09-07 18:32:06 +02:00
( ns - > IpAddress . String ) , ( int ) GetLastError ( ) ) ;
status = r ;
goto done ;
} else {
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG , " Succesfully added %s as nameserver " , ns - > IpAddress . String ) ;
2006-09-07 18:32:06 +02:00
}
2006-09-07 08:34:20 +02:00
2006-09-07 18:32:06 +02:00
added_any + + ;
ns = ns - > Next ;
2006-06-13 23:49:56 +02:00
}
2006-09-07 08:34:20 +02:00
if ( ! added_any ) {
2006-10-05 21:22:26 +02:00
log ( EVDNS_LOG_DEBUG , " No nameservers added. " ) ;
2006-09-07 18:32:06 +02:00
status = - 1 ;
2006-09-07 08:34:20 +02:00
}
2006-06-13 23:49:56 +02:00
done :
if ( buf )
free ( buf ) ;
if ( handle )
FreeLibrary ( handle ) ;
return status ;
}
2006-07-31 19:59:11 +02:00
static int
2006-12-12 06:45:19 +01:00
config_nameserver_from_reg_key ( HKEY key , const char * subkey )
{
2006-07-31 19:59:11 +02:00
char * buf ;
DWORD bufsz = 0 , type = 0 ;
int status = 0 ;
if ( RegQueryValueEx ( key , subkey , 0 , & type , NULL , & bufsz )
2006-09-07 18:32:06 +02:00
! = ERROR_MORE_DATA )
2006-07-31 19:59:11 +02:00
return - 1 ;
if ( ! ( buf = malloc ( bufsz ) ) )
return - 1 ;
if ( RegQueryValueEx ( key , subkey , 0 , & type , ( LPBYTE ) buf , & bufsz )
2006-09-07 18:32:06 +02:00
= = ERROR_SUCCESS & & bufsz > 1 ) {
2006-09-24 19:52:23 +02:00
status = evdns_nameserver_ip_add_line ( buf ) ;
2006-07-31 19:59:11 +02:00
}
free ( buf ) ;
return status ;
}
2006-09-06 23:35:30 +02:00
# define SERVICES_KEY "System\\CurrentControlSet\\Services\\"
# define WIN_NS_9X_KEY SERVICES_KEY "VxD\\MSTCP"
# define WIN_NS_NT_KEY SERVICES_KEY "Tcpip\\Parameters"
2006-07-31 19:59:11 +02:00
static int
2006-12-12 06:45:19 +01:00
load_nameservers_from_registry ( void )
{
2006-07-31 19:59:11 +02:00
int found = 0 ;
2006-09-07 05:53:14 +02:00
int r ;
2006-09-07 18:32:06 +02:00
# define TRY(k, name) \
if ( ! found & & config_nameserver_from_reg_key ( k , name ) = = 0 ) { \
2006-12-15 07:12:14 +01:00
log ( EVDNS_LOG_DEBUG , " Found nameservers in %s/%s " , # k , name ) ; \
2006-09-07 18:32:06 +02:00
found = 1 ; \
2006-12-15 07:12:14 +01:00
} else if ( ! found ) { \
log ( EVDNS_LOG_DEBUG , " Didn't find nameservers in %s/%s " , \
# k,#name); \
2006-07-31 19:59:11 +02:00
}
2006-09-06 22:33:28 +02:00
if ( ( ( int ) GetVersion ( ) ) > 0 ) { /* NT */
2006-07-31 19:59:11 +02:00
HKEY nt_key = 0 , interfaces_key = 0 ;
if ( RegOpenKeyEx ( HKEY_LOCAL_MACHINE , WIN_NS_NT_KEY , 0 ,
2006-09-07 18:32:06 +02:00
KEY_READ , & nt_key ) ! = ERROR_SUCCESS ) {
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG , " Couldn't open nt key, %d " , ( int ) GetLastError ( ) ) ;
2006-07-31 19:59:11 +02:00
return - 1 ;
2006-09-07 06:02:52 +02:00
}
2006-09-07 05:53:14 +02:00
r = RegOpenKeyEx ( nt_key , " Interfaces " , 0 ,
2006-09-07 18:32:06 +02:00
KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS ,
& interfaces_key ) ;
2006-10-05 21:22:26 +02:00
if ( r ! = ERROR_SUCCESS ) {
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_DEBUG , " Couldn't open interfaces key, %d " , ( int ) GetLastError ( ) ) ;
2006-09-07 05:53:14 +02:00
return - 1 ;
}
2006-09-06 23:35:30 +02:00
TRY ( nt_key , " NameServer " ) ;
TRY ( nt_key , " DhcpNameServer " ) ;
TRY ( interfaces_key , " NameServer " ) ;
TRY ( interfaces_key , " DhcpNameServer " ) ;
2006-07-31 19:59:11 +02:00
RegCloseKey ( interfaces_key ) ;
RegCloseKey ( nt_key ) ;
} else {
HKEY win_key = 0 ;
2006-09-06 23:35:30 +02:00
if ( RegOpenKeyEx ( HKEY_LOCAL_MACHINE , WIN_NS_9X_KEY , 0 ,
2006-09-07 18:32:06 +02:00
KEY_READ , & win_key ) ! = ERROR_SUCCESS ) {
2006-10-05 21:22:26 +02:00
log ( EVDNS_LOG_DEBUG , " Couldn't open registry key, %d " , ( int ) GetLastError ( ) ) ;
2006-07-31 19:59:11 +02:00
return - 1 ;
2006-09-07 05:53:14 +02:00
}
2006-09-06 23:35:30 +02:00
TRY ( win_key , " NameServer " ) ;
2006-07-31 19:59:11 +02:00
RegCloseKey ( win_key ) ;
}
2006-09-07 05:53:14 +02:00
if ( found = = 0 ) {
2006-09-24 19:52:23 +02:00
log ( EVDNS_LOG_WARN , " Didn't find any nameservers. " ) ;
2006-09-07 05:53:14 +02:00
}
2006-07-31 19:59:11 +02:00
return found ? 0 : - 1 ;
# undef TRY
}
int
2006-12-12 06:45:19 +01:00
evdns_config_windows_nameservers ( void )
{
2006-10-05 21:22:26 +02:00
if ( load_nameservers_with_getnetworkparams ( ) = = 0 )
2006-07-31 19:59:11 +02:00
return 0 ;
return load_nameservers_from_registry ( ) ;
}
2006-06-13 23:49:56 +02:00
# endif
2006-08-16 20:47:24 +02:00
2006-09-24 19:52:23 +02:00
int
2006-09-25 22:38:58 +02:00
evdns_init ( void )
2006-09-24 19:52:23 +02:00
{
2006-12-15 07:12:14 +01:00
int res = 0 ;
2006-12-15 07:12:01 +01:00
# ifdef WIN32
2006-12-15 07:12:14 +01:00
evdns_config_windows_nameservers ( ) ;
2006-09-24 19:52:23 +02:00
# else
2006-12-15 07:12:14 +01:00
res = evdns_resolv_conf_parse ( DNS_OPTIONS_ALL , " /etc/resolv.conf " ) ;
2006-09-24 19:52:23 +02:00
# endif
2006-12-15 07:12:14 +01:00
return ( res ) ;
2006-09-24 19:52:23 +02:00
}
2006-09-29 20:13:33 +02:00
const char *
evdns_err_to_string ( int err )
{
2006-12-15 07:12:14 +01:00
switch ( err ) {
2006-09-29 20:13:33 +02:00
case DNS_ERR_NONE : return " no error " ;
case DNS_ERR_FORMAT : return " misformatted query " ;
case DNS_ERR_SERVERFAILED : return " server failed " ;
case DNS_ERR_NOTEXIST : return " name does not exist " ;
case DNS_ERR_NOTIMPL : return " query not implemented " ;
case DNS_ERR_REFUSED : return " refused " ;
case DNS_ERR_TRUNCATED : return " reply truncated or ill-formed " ;
case DNS_ERR_UNKNOWN : return " unknown " ;
case DNS_ERR_TIMEOUT : return " request timed out " ;
case DNS_ERR_SHUTDOWN : return " dns subsystem shut down " ;
default : return " [Unknown error code] " ;
}
}
2006-09-07 18:24:19 +02:00
2006-09-25 22:38:58 +02:00
void
evdns_shutdown ( int fail_requests )
{
struct nameserver * server , * server_next ;
struct search_domain * dom , * dom_next ;
while ( req_head ) {
if ( fail_requests )
reply_callback ( req_head , 0 , DNS_ERR_SHUTDOWN , NULL ) ;
request_finished ( req_head , & req_head ) ;
}
while ( req_waiting_head ) {
if ( fail_requests )
reply_callback ( req_waiting_head , 0 , DNS_ERR_SHUTDOWN , NULL ) ;
request_finished ( req_waiting_head , & req_waiting_head ) ;
}
global_requests_inflight = global_requests_waiting = 0 ;
for ( server = server_head ; server ; server = server_next ) {
server_next = server - > next ;
if ( server - > socket > = 0 )
CLOSE_SOCKET ( server - > socket ) ;
( void ) event_del ( & server - > event ) ;
2007-05-29 16:41:16 +02:00
if ( server - > state = = 0 )
( void ) event_del ( & server - > timeout_event ) ;
2007-01-10 20:49:21 +01:00
CLEAR ( server ) ;
2006-09-25 22:38:58 +02:00
free ( server ) ;
2006-09-30 22:07:37 +02:00
if ( server_next = = server_head )
break ;
2006-09-25 22:38:58 +02:00
}
server_head = NULL ;
global_good_nameservers = 0 ;
if ( global_search_state ) {
for ( dom = global_search_state - > head ; dom ; dom = dom_next ) {
dom_next = dom - > next ;
2007-01-10 20:49:21 +01:00
CLEAR ( dom ) ;
2006-09-25 22:38:58 +02:00
free ( dom ) ;
}
2007-01-10 20:49:21 +01:00
CLEAR ( global_search_state ) ;
2006-09-25 22:38:58 +02:00
free ( global_search_state ) ;
global_search_state = NULL ;
}
evdns_log_fn = NULL ;
}
2006-12-12 03:49:45 +01:00
# ifdef EVDNS_MAIN
void
main_callback ( int result , char type , int count , int ttl ,
void * addrs , void * orig ) {
char * n = ( char * ) orig ;
int i ;
for ( i = 0 ; i < count ; + + i ) {
if ( type = = DNS_IPv4_A ) {
printf ( " %s: %s \n " , n , debug_ntoa ( ( ( u32 * ) addrs ) [ i ] ) ) ;
} else if ( type = = DNS_PTR ) {
printf ( " %s: %s \n " , n , ( ( char * * ) addrs ) [ i ] ) ;
}
}
if ( ! count ) {
printf ( " %s: No answer (%d) \n " , n , result ) ;
}
fflush ( stdout ) ;
}
2006-12-12 03:56:28 +01:00
void
evdns_server_callback ( struct evdns_server_request * req , void * data )
{
int i , r ;
( void ) data ;
2006-12-12 03:57:02 +01:00
/* dummy; give 192.168.11.11 as an answer for all A questions,
2006-12-15 07:12:14 +01:00
* give foo . bar . example . com as an answer for all PTR questions . */
2006-12-12 03:56:28 +01:00
for ( i = 0 ; i < req - > nquestions ; + + i ) {
u32 ans = htonl ( 0xc0a80b0bUL ) ;
2006-12-12 03:56:35 +01:00
if ( req - > questions [ i ] - > type = = EVDNS_TYPE_A & &
req - > questions [ i ] - > class = = EVDNS_CLASS_INET ) {
2006-12-12 03:56:28 +01:00
printf ( " -- replying for %s (A) \n " , req - > questions [ i ] - > name ) ;
2006-12-12 03:56:54 +01:00
r = evdns_server_request_add_a_reply ( req , req - > questions [ i ] - > name ,
2006-12-12 03:56:28 +01:00
1 , & ans , 10 ) ;
if ( r < 0 )
printf ( " eeep, didn't work. \n " ) ;
2006-12-12 03:56:35 +01:00
} else if ( req - > questions [ i ] - > type = = EVDNS_TYPE_PTR & &
req - > questions [ i ] - > class = = EVDNS_CLASS_INET ) {
2006-12-12 03:56:28 +01:00
printf ( " -- replying for %s (PTR) \n " , req - > questions [ i ] - > name ) ;
2006-12-12 03:56:54 +01:00
r = evdns_server_request_add_ptr_reply ( req , NULL , req - > questions [ i ] - > name ,
2006-12-12 03:56:28 +01:00
" foo.bar.example.com " , 10 ) ;
} else {
printf ( " -- skipping %s [%d %d] \n " , req - > questions [ i ] - > name ,
req - > questions [ i ] - > type , req - > questions [ i ] - > class ) ;
}
}
2006-12-12 03:56:41 +01:00
r = evdns_request_respond ( req , 0 ) ;
2006-12-12 03:56:28 +01:00
if ( r < 0 )
printf ( " eeek, couldn't send reply. \n " ) ;
}
2006-12-12 03:49:45 +01:00
void
2006-12-12 03:56:28 +01:00
logfn ( int is_warn , const char * msg ) {
( void ) is_warn ;
fprintf ( stderr , " %s \n " , msg ) ;
2006-12-12 03:49:45 +01:00
}
int
main ( int c , char * * v ) {
int idx ;
2006-12-12 03:56:28 +01:00
int reverse = 0 , verbose = 1 , servertest = 0 ;
2006-12-12 03:49:45 +01:00
if ( c < 2 ) {
fprintf ( stderr , " syntax: %s [-x] [-v] hostname \n " , v [ 0 ] ) ;
2006-12-12 03:56:28 +01:00
fprintf ( stderr , " syntax: %s [-servertest] \n " , v [ 0 ] ) ;
2006-12-12 03:49:45 +01:00
return 1 ;
}
idx = 1 ;
while ( idx < c & & v [ idx ] [ 0 ] = = ' - ' ) {
if ( ! strcmp ( v [ idx ] , " -x " ) )
reverse = 1 ;
else if ( ! strcmp ( v [ idx ] , " -v " ) )
verbose = 1 ;
2006-12-12 03:56:28 +01:00
else if ( ! strcmp ( v [ idx ] , " -servertest " ) )
servertest = 1 ;
2006-12-12 03:49:45 +01:00
else
fprintf ( stderr , " Unknown option %s \n " , v [ idx ] ) ;
+ + idx ;
}
event_init ( ) ;
if ( verbose )
evdns_set_log_fn ( logfn ) ;
evdns_resolv_conf_parse ( DNS_OPTION_NAMESERVERS , " /etc/resolv.conf " ) ;
2006-12-12 03:56:28 +01:00
if ( servertest ) {
int sock ;
struct sockaddr_in my_addr ;
sock = socket ( PF_INET , SOCK_DGRAM , 0 ) ;
fcntl ( sock , F_SETFL , O_NONBLOCK ) ;
my_addr . sin_family = AF_INET ;
my_addr . sin_port = htons ( 10053 ) ;
my_addr . sin_addr . s_addr = INADDR_ANY ;
if ( bind ( sock , ( struct sockaddr * ) & my_addr , sizeof ( my_addr ) ) < 0 ) {
perror ( " bind " ) ;
exit ( 1 ) ;
}
evdns_add_server_port ( sock , 0 , evdns_server_callback , NULL ) ;
}
2006-12-12 03:49:45 +01:00
for ( ; idx < c ; + + idx ) {
if ( reverse ) {
struct in_addr addr ;
if ( ! inet_aton ( v [ idx ] , & addr ) ) {
fprintf ( stderr , " Skipping non-IP %s \n " , v [ idx ] ) ;
continue ;
}
fprintf ( stderr , " resolving %s... \n " , v [ idx ] ) ;
evdns_resolve_reverse ( & addr , 0 , main_callback , v [ idx ] ) ;
} else {
fprintf ( stderr , " resolving (fwd) %s... \n " , v [ idx ] ) ;
evdns_resolve_ipv4 ( v [ idx ] , 0 , main_callback , v [ idx ] ) ;
}
}
fflush ( stdout ) ;
event_dispatch ( ) ;
return 0 ;
}
# endif
2007-09-10 16:05:12 +02:00
/* Local Variables: */
/* tab-width: 4 */
/* c-basic-offset: 4 */
/* indent-tabs-mode: t */
/* End: */
2006-09-07 18:24:19 +02:00