📄 eventdns.c
字号:
/* $Id$ */
/* The original version of this module was written by Adam Langley; for
* a history of modifications, check out the subversion logs.
*
* When editing this module, try to keep it re-mergeable by Adam. Don't
* reformat the whitespace, add Tor dependencies, or so on.
*
* TODO:
* - Replace all externally visible magic numbers with #defined constants.
* - Write documentation for APIs of all external functions.
*/
/* Async DNS Library
* Adam Langley <agl@imperialviolet.org>
* Public Domain code
*
* 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:
* Parts developed by Adam Langley <agl@imperialviolet.org>
*
* 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.1b
*/
#include "eventdns_tor.h"
#include <sys/types.h>
/* #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.
#error Please see the documentation.
#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
#include <string.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <sys/stat.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include "eventdns.h"
#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
#ifdef HAVE_NETINET_IN6_H
#include <netinet6/in6.h>
#endif
#ifdef WIN32
typedef int socklen_t;
#endif
#define EVDNS_LOG_DEBUG 0
#define EVDNS_LOG_WARN 1
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 255
#endif
#ifndef NDEBUG
#include <stdio.h>
#endif
/* for debugging possible memory leaks. */
#define malloc(x) tor_malloc(x)
#define realloc(x,y) tor_realloc((x),(y))
#define free(x) tor_free(x)
#define _free(x) _tor_free(x)
#undef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#if 0
#ifdef __USE_ISOC99B
/* libevent doesn't work without this */
typedef uint8_t u_char;
typedef unsigned int uint;
#endif
#endif
#include <event.h>
#define u64 uint64_t
#define u32 uint32_t
#define u16 uint16_t
#define u8 uint8_t
#define MAX_ADDRS 4 /* maximum number of addresses from a single packet */
/* which we bother recording */
#define TYPE_A EVDNS_TYPE_A
#define TYPE_PTR EVDNS_TYPE_PTR
#define TYPE_AAAA EVDNS_TYPE_AAAA
#define CLASS_INET EVDNS_CLASS_INET
#define CLEAR(x) do { memset((x), 0, sizeof(*(x))); } while(0)
struct request {
u8 *request; /* the dns packet data */
unsigned int request_len;
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 */
void *user_pointer; /* the pointer given to us for this request */
evdns_callback_type user_callback;
struct nameserver *ns; /* the server which we last sent it */
/* elements used by the searching code */
int search_index;
struct search_state *search_state;
char *search_origname; /* needs to be free()ed */
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 */
char request_appended; /* true if the request pointer is data which follows this struct */
char transmit_me; /* needs to be transmitted */
};
#ifndef HAVE_STRUCT_IN6_ADDR
struct in6_addr {
u8 s6_addr[16];
};
#endif
struct reply {
unsigned int type;
unsigned int have_answer;
union {
struct {
u32 addrcount;
u32 addresses[MAX_ADDRS];
} a;
struct {
u32 addrcount;
struct in6_addr addresses[MAX_ADDRS];
} aaaa;
struct {
char name[HOST_NAME_MAX];
} ptr;
} data;
};
struct nameserver {
int socket; /* a connected UDP socket */
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;
struct event timeout_event; /* used to keep the timeout for */
/* when we next probe this server. */
/* Valid if state == 0 */
char state; /* zero if we think that this server is down */
char choked; /* true if we have an EAGAIN from this server's socket */
char write_waiting; /* true if we are waiting for EV_WRITE events */
};
static struct request *req_head = NULL, *req_waiting_head = NULL;
static struct nameserver *server_head = NULL;
/* Represents a local port where we're listening for DNS requests. Right now, */
/* only UDP is supported. */
struct evdns_server_port {
int socket; /* socket we use to read queries and write replies. */
int refcnt; /* reference count. */
char choked; /* Are we currently blocked from writing? */
char closing; /* Are we trying to close this port, pending writes? */
evdns_request_callback_fn_type user_callback; /* Fn to handle requests */
void *user_data; /* Opaque pointer passed to user_callback */
struct event event; /* Read/write event */
/* circular list of replies that we want to write. */
struct server_request *pending_replies;
};
/* Represents part of a reply being built. (That is, a single RR.) */
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 */
char is_name; /* True iff data is a label */
u16 datalen; /* Length of data; -1 if data is a label */
void *data; /* The contents of the RR */
};
/* Represents a request that we've received as a DNS server, and holds */
/* the components of the reply as we're constructing it. */
struct server_request {
/* 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. */
struct server_request *next_pending;
struct server_request *prev_pending;
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 */
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? */
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 */
/* 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. */
char *response;
size_t response_len;
/* Caller-visible fields: flags, questions. */
struct evdns_server_request base;
};
/* helper macro */
#define OFFSET_OF(st, member) ((off_t) (((char*)&((st*)0)->member)-(char*)0))
/* Given a pointer to an evdns_server_request, get the corresponding */
/* server_request. */
#define TO_SERVER_REQUEST(base_ptr) \
((struct server_request*) \
(((char*)(base_ptr) - OFFSET_OF(struct server_request, base))))
/* 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;
static struct timeval global_timeout = {5, 0}; /* 5 seconds */
static int global_max_reissues = 1; /* a reissue occurs when we get some errors from the server */
static int global_max_retransmits = 3; /* number of times we'll retransmit a request which timed out */
/* number of timeouts in a row before we consider this server to be down */
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 = (int)(sizeof(global_nameserver_timeouts)/sizeof(struct timeval));
static struct nameserver *nameserver_pick(void);
static void evdns_request_insert(struct request *req, struct request **head);
static void nameserver_ready_callback(int fd, short events, void *arg);
static int evdns_transmit(void);
static int evdns_request_transmit(struct request *req);
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);
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);
static u16 transaction_id_pick(void);
static struct request *request_new(int type, const char *name, int flags, evdns_callback_type callback, void *ptr);
static void request_submit(struct request *req);
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);
static int strtoint(const char *const str);
#ifdef WIN32
static int
last_error(int sock)
{
int optval, optvallen=sizeof(optval);
int err = WSAGetLastError();
if (err == WSAEWOULDBLOCK && sock >= 0) {
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&optval,
&optvallen))
return err;
if (optval)
return optval;
}
return err;
}
static int
error_is_eagain(int err)
{
return err == EAGAIN || err == WSAEWOULDBLOCK;
}
static int
inet_aton(const char *c, struct in_addr *addr)
{
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;
}
#define CLOSE_SOCKET(x) closesocket(x)
#else
#define last_error(sock) (errno)
#define error_is_eagain(err) ((err) == EAGAIN)
#define CLOSE_SOCKET(x) close(x)
#endif
#define ISSPACE(c) isspace((int)(unsigned char)(c))
#define ISDIGIT(c) isdigit((int)(unsigned char)(c))
#ifndef NDEBUG
static const char *
debug_ntoa(u32 address)
{
static char buf[32];
u32 a = ntohl(address);
snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
(int)(u8)((a>>24)&0xff),
(int)(u8)((a>>16)&0xff),
(int)(u8)((a>>8 )&0xff),
(int)(u8)((a )&0xff));
return buf;
}
#endif
static evdns_debug_log_fn_type evdns_log_fn = NULL;
void
evdns_set_log_fn(evdns_debug_log_fn_type fn)
{
evdns_log_fn = fn;
}
#ifdef __GNUC__
#define EVDNS_LOG_CHECK __attribute__ ((format(printf, 2, 3)))
#else
#define EVDNS_LOG_CHECK
#endif
static void _evdns_log(int warn, const char *fmt, ...) EVDNS_LOG_CHECK;
static void
_evdns_log(int warn, const char *fmt, ...)
{
va_list args;
static char buf[512];
if (!evdns_log_fn)
return;
va_start(args,fmt);
#ifdef WIN32
_vsnprintf(buf, sizeof(buf), fmt, args);
#else
vsnprintf(buf, sizeof(buf), fmt, args);
#endif
buf[sizeof(buf)-1] = '\0';
evdns_log_fn(warn, buf);
va_end(args);
}
#define log _evdns_log
/* 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;
(void)fd;
(void)events;
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) {
const struct timeval * timeout;
(void) evtimer_del(&ns->timeout_event);
CLEAR(&ns->timeout_event);
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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -