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>
2006-06-03 21:49:42 +02:00
//#define NDEBUG
# 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
// #define _POSIX_C_SOURCE 200507
# 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
# include <netinet/in6.h>
# 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
# 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
// libevent doesn't work without this
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
2006-09-07 18:32:06 +02:00
# define MAX_ADDRS 4 // maximum number of addresses from a single packet
2006-06-03 21:49:42 +02:00
// which we bother recording
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 {
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 ;
int tx_count ; // the number of times that this packet has been sent
unsigned int request_type ; // TYPE_PTR or TYPE_A
2006-09-07 18:32:06 +02:00
void * user_pointer ; // the pointer given to us for this request
2006-09-24 19:52:23 +02:00
evdns_callback_type user_callback ;
2006-09-07 18:32:06 +02:00
struct nameserver * ns ; // the server which we last sent it
2006-06-03 21:49:42 +02:00
// elements used by the searching code
int search_index ;
struct search_state * search_state ;
2006-09-07 18:32:06 +02:00
char * search_origname ; // needs to be free()ed
2006-06-03 21:49:42 +02:00
int search_flags ;
// these objects are kept in a circular list
struct request * next , * prev ;
struct event timeout_event ;
u16 trans_id ; // the transaction id
2006-09-07 18:32:06 +02:00
char request_appended ; // true if the request pointer is data which follows this struct
2006-06-03 21:49:42 +02:00
char transmit_me ; // needs to be transmitted
} ;
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 {
2006-09-07 18:32:06 +02:00
int socket ; // a connected UDP socket
2006-06-03 21:49:42 +02:00
u32 address ;
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
struct event event ;
// these objects are kept in a circular list
struct nameserver * next , * prev ;
2006-09-07 18:24:19 +02:00
struct event timeout_event ; // used to keep the timeout for
// when we next probe this server.
2006-09-07 18:32:06 +02:00
// Valid if state == 0
char state ; // zero if we think that this server is down
2006-12-12 21:20:48 +01:00
char choked ; // true if we have an EAGAIN from this server's socket
2006-09-07 18:32:06 +02:00
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 ;
2006-12-12 03:57:02 +01: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 {
2006-12-12 03:57:02 +01:00
int socket ; // socket we use to read queries and write replies.
int refcnt ; // reference count.
2006-12-12 21:20:48 +01:00
char choked ; // Are we currently blocked from writing?
2006-12-12 03:57:02 +01:00
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
2006-12-15 07:12:14 +01:00
// circular list of replies that we want to write.
2006-12-12 03:49:45 +01:00
struct server_request * pending_replies ;
} ;
2006-12-15 07:12:14 +01: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 {
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
2006-12-15 07:12:14 +01:00
char is_name ; // True iff data is a label
u16 datalen ; // Length of data; -1 if data is a label
2006-12-12 03:57:02 +01:00
void * data ; // The contents of the RR
2006-12-12 03:49:45 +01:00
} ;
2006-12-12 03:57:02 +01: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 {
2006-12-15 07:12:14 +01: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 ;
2006-12-12 03:57:02 +01: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
2006-12-12 03:57:02 +01: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
2006-12-12 03:57:02 +01: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
2006-12-15 07:12:14 +01: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 ;
2006-12-15 07:12:14 +01: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
// helper macro
2006-12-12 03:49:45 +01:00
# define OFFSET_OF(st, member) ((off_t) (((char*)&((st*)0)->member)-(char*)0))
2006-12-12 03:57:02 +01: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 ) ) ) )
2006-06-03 21:49:42 +02:00
// The number of good nameservers that we have
static int global_good_nameservers = 0 ;
// inflight requests are contained in the req_head list
// and are actually going out across the network
static int global_requests_inflight = 0 ;
// requests which aren't inflight are in the waiting list
// and are counted here
static int global_requests_waiting = 0 ;
static int global_max_requests_inflight = 64 ;
2006-12-08 06:59:21 +01:00
static struct timeval global_timeout = { 5 , 0 } ; // 5 seconds
2006-09-24 19:52:23 +02:00
static int global_max_reissues = 1 ; // a reissue occurs when we get some errors from the server
2006-12-15 07:12:14 +01:00
static int global_max_retransmits = 3 ; // number of times we'll retransmit a request which timed out
2006-06-03 21:49:42 +02:00
// number of timeouts in a row before we consider this server to be down
static int global_max_nameserver_timeout = 3 ;
// 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.
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
2006-06-03 21:49:42 +02:00
// This walks the list of inflight requests to find the
// one with a matching transaction id. Returns NULL on
// failure
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 ;
}
// 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
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 ) ;
}
// 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.
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 ) {
// This can happen if the nameserver acts in a way which makes us mark
// it as bad and then starts sending good replies.
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 ) ) ;
// ???? Do more?
}
2006-06-03 21:49:42 +02:00
}
// called when a nameserver has been deemed to have failed. For example, too
// many packets have timed out etc
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 ;
2006-06-03 21:49:42 +02:00
// if this nameserver has already been marked as failed
// then don't do anything
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 ) ) ;
// ???? Do more?
}
2006-06-03 21:49:42 +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
// if we don't have *any* good nameservers then there's no point
2006-06-03 21:49:42 +02:00
// trying to reassign requests to one
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 ) {
// still waiting to go out, can be moved
// to another server
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 ) ;
}
// 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.
static void
request_finished ( struct request * const req , struct request * * head ) {
if ( head ) {
if ( req - > next = = req ) {
// only item in the list
* 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 ) {
// need to free the request data on it's own
free ( req - > request ) ;
} else {
// the request data is appended onto the header
// so everything gets free()ed when we:
}
2007-01-10 20:49:21 +01:00
CLEAR ( req ) ;
2006-06-03 21:49:42 +02:00
free ( req ) ;
2006-09-24 19:52:23 +02:00
evdns_requests_pump_waiting_queue ( ) ;
2006-06-03 21:49:42 +02:00
}
// This is called when a server returns a funny error code.
// We try the request again with another server.
//
// return:
2006-09-07 18:32:06 +02:00
// 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 ;
// the last nameserver should have been marked as failing
// by the caller of this function, therefore pick will try
// not to return it
req - > ns = nameserver_pick ( ) ;
if ( req - > ns = = last_ns ) {
// ... but pick did return it
// not a lot of point in trying again with the
// same server
return 1 ;
}
req - > reissue_count + + ;
req - > tx_count = 0 ;
req - > transmit_me = 1 ;
return 0 ;
}
// this function looks for space on the inflight queue and promotes
// requests from the waiting queue if it can.
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 ;
2006-06-03 21:49:42 +02:00
// move a request from the waiting queue to the inflight queue
assert ( req_waiting_head ) ;
if ( req_waiting_head - > next = = req_waiting_head ) {
// only one item in the queue
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 ) ;
}
2006-06-03 21:49:42 +02:00
// this processes a parsed reply packet
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 ) {
2006-06-03 21:49:42 +02:00
// there was an error
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 :
// we regard these errors as marking a bad nameserver
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 :
// 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.
/*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 :
// we got a good reply from the nameserver
nameserver_up ( req - > ns ) ;
}
2006-08-16 20:47:24 +02:00
if ( req - > search_state & & req - > request_type ! = TYPE_PTR ) {
2006-06-03 21:49:42 +02:00
// if we have a list of domains to search in, try the next one
if ( ! search_try_next ( req ) ) {
// 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.
request_finished ( req , & req_head ) ;
return ;
}
}
2006-07-23 07:40:24 +02:00
2006-06-03 21:49:42 +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 {
// 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 ;
// 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.
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-02-24 09:18:59 +01: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 ) {
2006-09-07 18:32:06 +02:00
int j = 0 ; // index into packet
u16 _t ; // used by the macros
2006-06-03 21:49:42 +02:00
u32 _t32 ; // used by the macros
2006-08-16 20:47:24 +02:00
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. */
2006-08-16 20:47:24 +02:00
if ( ! ( flags & 0x8000 ) ) return - 1 ; // must be an answer
2006-06-03 21:49:42 +02:00
if ( flags & 0x020f ) {
// there was an error
2007-02-24 09:18:59 +01:00
goto err ;
2006-06-03 21:49:42 +02:00
}
// if (!answers) return; // must have an answer of some form
2006-07-23 07:40:24 +02:00
2006-12-15 07:12:01 +01:00
// This macro skips a name in the DNS reply.
# 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
2006-12-12 03:56:41 +01:00
// skip over each question in the reply
2006-06-03 21:49:42 +02:00
for ( i = 0 ; i < questions ; + + i ) {
// the question looks like
2006-09-07 18:32:06 +02:00
// <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
// 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 ;
2006-08-16 20:47:24 +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 ) ;
2006-06-03 21:49:42 +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 ;
2006-12-12 04:48:46 +01:00
addrcount = datalength > > 4 ; // each address is 16 bytes long
addrtocopy = MIN ( MAX_ADDRS - reply . data . aaaa . addrcount , ( unsigned ) addrcount ) ;
ttl_r = MIN ( ttl_r , ttl ) ;
// 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 {
2006-06-03 21:49:42 +02:00
// skip over any other type of resource
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
}
2006-12-12 03:57:02 +01: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
{
int j = 0 ; // index into packet
u16 _t ; // used by the macros
char tmp_name [ 256 ] ; // used by the macros
int i ;
u16 trans_id , flags , questions , answers , authority , additional ;
struct server_request * server_req = NULL ;
2006-12-15 07:12:14 +01: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 ) ;
if ( flags & 0x8000 ) return - 1 ; // Must not be an answer.
2007-06-01 01:40:35 +02:00
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 ;
}
2006-12-12 03:57:02 +01: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
// Only standard queries are supported.
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
2006-06-03 21:49:42 +02:00
// Try to choose a strong transaction id which isn't already in flight
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 ) {
/* // in the case that the RAND call fails we back
// down to using gettimeofday.
struct timeval tv ;
gettimeofday ( & tv , NULL ) ;
trans_id = tv . tv_usec & 0xffff ; */
abort ( ) ;
}
# endif
if ( trans_id = = 0xffff ) continue ;
// 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 ) ;
}
// we didn't find it, so this is a good id
if ( req = = started_at ) return trans_id ;
}
}
// choose a namesever to use. This function will try to ignore
2006-07-07 21:08:44 +02:00
// nameservers which we think are down and load balance across the rest
2006-06-03 21:49:42 +02:00
// by updating the server_head global each time.
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 ;
// if we don't have any good nameservers then there's no
// point in trying to find one.
if ( ! global_good_nameservers ) {
server_head = server_head - > next ;
return server_head ;
}
2006-06-04 00:05:23 +02:00
2006-06-03 21:49:42 +02:00
// remember that nameservers are in a circular list
for ( ; ; ) {
if ( server_head - > state ) {
// we think this server is currently good
picked = server_head ;
server_head = server_head - > next ;
return picked ;
}
server_head = server_head - > next ;
if ( server_head = = started_at ) {
// all the nameservers seem to be down
// so we just return this one and hope for the
// best
assert ( global_good_nameservers = = 0 ) ;
picked = server_head ;
server_head = server_head - > next ;
return picked ;
}
}
}
// this is called when a namesever socket is ready for reading
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 ) ;
}
}
2006-12-12 03:57:02 +01: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 ) ;
}
}
2006-12-12 03:57:02 +01: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 ) ) {
2006-12-15 07:12:14 +01: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
}
2006-12-15 07:12:14 +01: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. " ) ;
// ???? Do more?
}
2006-12-12 03:56:20 +01:00
}
2006-06-03 21:49:42 +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.
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 ) ) ;
// ???? Do more?
}
2006-06-03 21:49:42 +02:00
}
// a callback function. Called by libevent when the kernel says that
// a nameserver socket is ready for writing or reading
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 ) ;
}
}
2006-12-12 03:56:20 +01:00
// a callback function. Called by libevent when the kernel says that
// a server socket is ready for writing or reading.
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
2006-12-12 03:57:02 +01: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 {
2006-12-12 03:57:02 +01:00
int n_labels ; // number of current entries
2006-12-15 07:12:14 +01:00
// map from name to position in message
2006-12-12 03:50:17 +01:00
struct dnslabel_entry labels [ MAX_LABELS ] ;
} ;
2006-12-12 03:57:02 +01: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 ;
}
2006-12-12 03:57:02 +01: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 ;
}
2006-12-12 03:57:02 +01: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 ;
}
2006-12-12 03:57:02 +01: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 ) ;
}
2006-12-12 03:56:47 +01: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
2006-12-15 07:12:14 +01:00
// of name. table is optional, and is used for compression.
2006-06-03 21:49:42 +02:00
//
// Input: abc.def
// Output: <3>abc<3>def<0>
//
2006-12-12 03:56:47 +01:00
// Returns the first index after the encoded name, or negative on error.
2006-09-07 18:32:06 +02:00
// -1 label was > 63 bytes
2006-12-12 03:50:17 +01:00
// -2 name too long to fit in buffer.
//
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 {
// 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 ;
// hop over the '.'
name + + ;
}
}
// 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
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
}
// 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
static int
2006-09-24 19:52:23 +02:00
evdns_request_len ( const int name_len ) {
2006-06-03 21:49:42 +02:00
return 96 + // length of the DNS standard header
2006-07-23 07:40:24 +02:00
name_len + 2 +
2006-09-07 18:32:06 +02:00
4 ; // space for the resource type
2006-06-03 21:49:42 +02:00
}
// build a dns request packet into buf. buf should be at least as long
2006-09-24 19:52:23 +02:00
// as evdns_request_len told you it should be.
2006-06-03 21:49:42 +02:00
//
// Returns the amount of space used. Negative on error.
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 ) {
2006-10-08 23:05:06 +02:00
off_t j = 0 ; // current offset into buf
2006-09-07 18:32:06 +02:00
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 ) ;
APPEND16 ( 0x0100 ) ; // standard query, recusion needed
APPEND16 ( 1 ) ; // one question
APPEND16 ( 0 ) ; // no answers
APPEND16 ( 0 ) ; // no authority
APPEND16 ( 0 ) ; // no additional
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
}
2006-12-12 03:56:20 +01:00
// exported function
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
assert ( ! is_tcp ) ; // TCP sockets not yet implemented
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 ) ;
event_add ( & port - > event , NULL ) ; // check return.
return port ;
}
2006-12-12 03:56:47 +01:00
// exported function
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
}
2006-12-12 03:49:45 +01:00
// exported function
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 ;
}
// exported function
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 ) ;
}
2006-12-12 03:56:54 +01: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 ) ;
}
2006-12-12 03:56:54 +01: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
}
2006-12-12 03:56:54 +01: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
}
// exported function
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 ;
}
2006-12-12 03:57:02 +01: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-01-10 20:49:21 +01:00
/* XXXX free(victim?) -NM */
2006-12-12 03:56:20 +01:00
victim = next ;
}
* list = NULL ;
}
}
2006-12-12 03:57:02 +01:00
// Free all storage held by req, and remove links to it.
2006-12-12 03:56:47 +01:00
// return true iff we just wound up freeing the server_port.
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 ] ) ;
}
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 ) ;
}
2006-12-12 03:57:02 +01: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-05-24 22:31:27 +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
}
2006-12-12 03:56:20 +01:00
// exported function
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-05-24 22:31:27 +02:00
// exported function
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
2006-06-03 21:49:42 +02:00
// this is a libevent callback function which is called when a request
// has timed out.
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 ) {
// 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 {
// retransmit it
2006-09-24 19:52:23 +02:00
evdns_request_transmit ( req ) ;
2006-06-03 21:49:42 +02:00
}
}
// try to send a request to a given server.
//
// return:
2006-09-07 18:32:06 +02:00
// 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 ) {
2006-06-03 21:49:42 +02:00
return 1 ; // short write
} else {
return 0 ;
}
}
// try to send a request, updating the fields of the request
// as needed
//
// return:
2006-09-07 18:32:06 +02:00
// 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
2006-06-03 21:49:42 +02:00
// if we fail to send this packet then this flag marks it
2006-09-24 19:52:23 +02:00
// 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 ) {
2006-06-03 21:49:42 +02:00
// don't bother trying to write to a socket
// which we have had EAGAIN from
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 :
// 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 :
// failed in some other way
retcode = 1 ;
2007-02-28 21:24:23 +01:00
break ;
2006-06-03 21:49:42 +02:00
default :
2007-02-28 21:24:23 +01: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 ) ;
2006-08-28 05:15:38 +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 ) {
// this is a good reply
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 ;
2006-06-03 21:49:42 +02:00
// here we need to send a probe to a given nameserver
// in the hope that it is up now.
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 ;
2006-06-03 21:49:42 +02:00
// we force this into the inflight queue no matter what
request_trans_id_set ( req , transaction_id_pick ( ) ) ;
req - > ns = ns ;
request_submit ( req ) ;
}
// returns:
2006-09-07 18:32:06 +02:00
// 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 ;
// first transmit all the requests which are currently waiting
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 ;
}
2006-08-04 20:26:13 +02:00
// exported function
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 ;
}
// exported function
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 ;
// ???? 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
2006-08-04 20:26:13 +02:00
// exported function
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 ) {
2006-06-03 21:49:42 +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
2006-06-03 21:49:42 +02:00
// insert this nameserver into the list of them
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-01-29 19:13:42 +01:00
// exported function
int
evdns_nameserver_add ( unsigned long int address ) {
return _evdns_nameserver_add_impl ( address , 53 ) ;
}
2006-06-03 21:49:42 +02:00
// exported function
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
}
// insert into the tail of the queue
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 ;
// 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 ) ) ;
// request data lives just after the header
req - > request = ( ( u8 * ) req ) + sizeof ( struct request ) ;
2006-12-12 03:56:41 +01: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 ;
2006-12-12 06:45:19 +01:00
err1 :
2007-01-10 20:49:21 +01:00
CLEAR ( req ) ;
2006-09-19 22:45: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 ) {
// 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 + + ;
}
}
// 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
}
}
2006-12-12 04:48:46 +01:00
// exported function
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 ) {
char buf [ 64 ] ;
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
}
assert ( cp + strlen ( " .ip6.arpa " ) < buf + sizeof ( buf ) ) ;
2007-01-16 00:56:38 +01:00
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 ;
}
2006-12-15 07:12:01 +01:00
2006-06-03 21:49:42 +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.
struct search_domain {
int len ;
struct search_domain * next ;
// the text string is appended to this structure
} ;
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 ) ;
2006-06-03 21:49:42 +02:00
free ( dom ) ;
}
2007-01-10 20:49:21 +01:00
CLEAR ( state ) ;
2006-06-03 21:49:42 +02:00
free ( state ) ;
}
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 ( ) ;
}
// exported function
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 ;
}
// reverse the order of members in the postfix list. This is needed because,
// when parsing resolv.conf we push elements in the wrong order
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 ;
}
// exported function
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 ) ;
}
// exported function
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 ) ;
}
// warning: returns malloced string
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 - - ) {
// this is the postfix we want
// the actual postfix string is kept at the end of the structure
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 ;
}
}
// we ran off the end of the list and still didn't find the requested string
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 ) {
// we have some domains to search
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 ) ;
2006-06-03 21:49:42 +02:00
free ( new_name ) ;
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 ;
}
}
// 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:
2006-09-07 18:32:06 +02:00
// 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 ) {
// 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 ) {
// no more postfixes to try, however we may need to try
// this name without a postfix
if ( string_num_dots ( req - > search_origname ) < req - > search_state - > ndots ) {
// 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 ;
}
}
/////////////////////////////////////////////////////////////////////
// Parsing resolv.conf files
static void
2006-09-24 19:52:23 +02:00
evdns_resolv_set_defaults ( int flags ) {
2006-06-03 21:49:42 +02:00
// if the file isn't found then we assume a local resolver
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
// helper version of atoi which returns -1 on error
static int
strtoint ( const char * const str ) {
char * endptr ;
const int r = strtol ( str , & endptr , 10 ) ;
if ( * endptr ) return - 1 ;
return r ;
}
2006-12-28 22:29:31 +01:00
// helper version of atoi that returns -1 on error and clips to bounds.
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 ;
}
// exported function
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 ) ) {
// 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
}
// exported function
// returns:
2006-09-07 18:32:06 +02:00
// 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 ;
}
2006-09-07 18:32:06 +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 ; }
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 ) ) {
// 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
2006-07-31 19:59:11 +02:00
// Add multiple nameservers from a space-or-comma-separated list.
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
2006-07-31 19:59:11 +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 )
{
2006-09-07 18:32:06 +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 " ) ;
2006-09-07 18:32:06 +02:00
//right now status = 0, doesn't that mean "good" - mikec
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. " ) ;
2006-09-07 18:32:06 +02:00
//same as above
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
2006-09-07 18:24:19 +02:00
// Local Variables:
// tab-width: 4
// c-basic-offset: 4
// indent-tabs-mode: t
// End: