diff options
author | Erwin Jansen <jansene@google.com> | 2019-04-04 11:24:54 -0700 |
---|---|---|
committer | Erwin Jansen <jansene@google.com> | 2019-04-05 14:28:33 -0700 |
commit | 42ca08a52a1b458cac8cbc6d097c1ae17299ccad (patch) | |
tree | 65d700375fe7d71f9a1ddfc496c8b264f5c3dbe6 | |
parent | 24e124e42be81e5a2e3438cb4f6fa946bc8f8d99 (diff) | |
download | c-ares-42ca08a52a1b458cac8cbc6d097c1ae17299ccad.tar.gz |
Merge c-ares 1.13.0.
Version od grpc we are building (1.16.0) needs this version of c-ares.
70 files changed, 4196 insertions, 1819 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bcd932..65b36be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,56 +8,56 @@ if(WINDOWS) endif() set(cares_src - acountry.c - adig.c - ahost.c - ares__close_sockets.c - ares__get_hostent.c - ares__read_line.c - ares__timeval.c - ares_cancel.c - ares_data.c - ares_destroy.c - ares_expand_name.c - ares_expand_string.c - ares_fds.c - ares_free_hostent.c - ares_free_string.c - ares_getenv.c - ares_gethostbyaddr.c - ares_gethostbyname.c - ares_getnameinfo.c - ares_getopt.c - ares_getsock.c - ares_init.c - ares_library_init.c - ares_llist.c - ares_mkquery.c - ares_nowarn.c - ares_options.c - ares_parse_a_reply.c - ares_parse_aaaa_reply.c - ares_parse_mx_reply.c - ares_parse_ns_reply.c - ares_parse_ptr_reply.c - ares_parse_srv_reply.c - ares_parse_txt_reply.c - ares_platform.c - ares_process.c - ares_query.c - ares_search.c - ares_send.c - ares_strcasecmp.c - ares_strdup.c - ares_strerror.c - ares_timeout.c - ares_version.c - ares_writev.c - bitncmp.c - inet_net_pton.c - inet_ntop.c - windows_port.c) - + ares__close_sockets.c + ares__get_hostent.c + ares__read_line.c + ares__timeval.c + ares_cancel.c + ares_create_query.c + ares_data.c + ares_destroy.c + ares_expand_name.c + ares_expand_string.c + ares_fds.c + ares_free_hostent.c + ares_free_string.c + ares_getenv.c + ares_gethostbyaddr.c + ares_gethostbyname.c + ares_getnameinfo.c + ares_getopt.c + ares_getsock.c + ares_init.c + ares_library_init.c + ares_llist.c + ares_mkquery.c + ares_nowarn.c + ares_options.c + ares_parse_a_reply.c + ares_parse_aaaa_reply.c + ares_parse_mx_reply.c + ares_parse_naptr_reply.c + ares_parse_ns_reply.c + ares_parse_ptr_reply.c + ares_parse_soa_reply.c + ares_parse_srv_reply.c + ares_parse_txt_reply.c + ares_platform.c + ares_process.c + ares_query.c + ares_search.c + ares_send.c + ares_strcasecmp.c + ares_strdup.c + ares_strerror.c + ares_timeout.c + ares_version.c + ares_writev.c + bitncmp.c + inet_net_pton.c + inet_ntop.c + windows_port.c + ) android_add_library(cares) target_compile_definitions(cares @@ -15,7 +15,7 @@ * * Ref: http://countries.nerd.dk/more.html * - * Written by G. Vanem <gvanem@broadpark.no> 2006, 2007 + * Written by G. Vanem <gvanem@yahoo.no> 2006, 2007 * * NB! This program may not be big-endian aware. * @@ -34,14 +34,6 @@ #include "ares_setup.h" -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <string.h> -#include <ctype.h> -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif #ifdef HAVE_STRINGS_H #include <strings.h> #endif @@ -49,7 +41,6 @@ #if defined(WIN32) && !defined(WATT32) #include <winsock.h> #else - #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netdb.h> @@ -57,8 +48,6 @@ #include "ares.h" #include "ares_getopt.h" -#include "inet_net_pton.h" -#include "inet_ntop.h" #include "ares_nowarn.h" #ifndef HAVE_STRDUP @@ -82,14 +71,14 @@ static const char *usage = "acountry [-vh?] {host|addr} ...\n"; static const char nerd_fmt[] = "%u.%u.%u.%u.zz.countries.nerd.dk"; -static const char *nerd_ver1 = nerd_fmt + 14; -static const char *nerd_ver2 = nerd_fmt + 11; +static const char *nerd_ver1 = nerd_fmt + 14; /* .countries.nerd.dk */ +static const char *nerd_ver2 = nerd_fmt + 11; /* .zz.countries.nerd.dk */ static int verbose = 0; #define TRACE(fmt) do { \ if (verbose > 0) \ printf fmt ; \ - } while (0) + } WHILE_FALSE static void wait_ares(ares_channel channel); static void callback(void *arg, int status, int timeouts, struct hostent *host); @@ -211,7 +200,9 @@ static void wait_ares(ares_channel channel) if (nfds == 0) break; tvp = ares_timeout(channel, NULL, &tv); - select(nfds, &read_fds, &write_fds, NULL, tvp); + nfds = select(nfds, &read_fds, &write_fds, NULL, tvp); + if (nfds < 0) + continue; ares_process(channel, &read_fds, &write_fds); } } @@ -282,7 +273,7 @@ static const struct search_list *list_lookup(int number, const struct search_lis */ static const struct search_list country_list[] = { { 4, "af", "Afghanistan" }, - { 248, "ax", "Åland Island" }, + { 248, "ax", "Ã…land Island" }, { 8, "al", "Albania" }, { 12, "dz", "Algeria" }, { 16, "as", "American Samoa" }, @@ -570,9 +561,10 @@ static void find_country_from_cname(const char *cname, struct in_addr addr) if (ver_1) { const char *dot = strchr(cname, '.'); - if ((z0 != 'z' && z1 != 'z') || dot != cname+4) + if (dot != cname+4) { printf("Unexpected CNAME %s (ver_1)\n", cname); + free(ccopy); return; } } @@ -583,12 +575,14 @@ static void find_country_from_cname(const char *cname, struct in_addr addr) if (z0 != 'z' && z1 != 'z') { printf("Unexpected CNAME %s (ver_2)\n", cname); + free(ccopy); return; } } else { printf("Unexpected CNAME %s (ver?)\n", cname); + free(ccopy); return; } @@ -625,4 +619,3 @@ static void find_country_from_cname(const char *cname, struct in_addr addr) } free(ccopy); } - @@ -16,9 +16,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -37,25 +34,12 @@ # include <arpa/nameser_compat.h> #endif -#ifdef HAVE_SYS_TIME_H -# include <sys/time.h> -#endif -#ifdef HAVE_UNISTD_H -# include <unistd.h> -#endif #ifdef HAVE_STRINGS_H # include <strings.h> #endif -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> - #include "ares.h" #include "ares_dns.h" -#include "inet_ntop.h" -#include "inet_net_pton.h" #include "ares_getopt.h" #include "ares_nowarn.h" @@ -390,9 +374,9 @@ int main(int argc, char **argv) break; tvp = ares_timeout(channel, NULL, &tv); count = select(nfds, &read_fds, &write_fds, NULL, tvp); - if (count < 0 && SOCKERRNO != EINVAL) + if (count < 0 && (status = SOCKERRNO) != EINVAL) { - perror("select"); + printf("select fail: %d", status); return 1; } ares_process(channel, &read_fds, &write_fds); @@ -649,7 +633,7 @@ static const unsigned char *display_rr(const unsigned char *aptr, */ if (dlen < 2) return NULL; - printf("\t%d", DNS__16BIT(aptr)); + printf("\t%d", (int)DNS__16BIT(aptr)); status = ares_expand_name(aptr + 2, abuf, alen, &name.as_char, &len); if (status != ARES_SUCCESS) return NULL; @@ -676,10 +660,10 @@ static const unsigned char *display_rr(const unsigned char *aptr, p += len; if (p + 20 > aptr + dlen) return NULL; - printf("\t\t\t\t\t\t( %lu %lu %lu %lu %lu )", - (unsigned long)DNS__32BIT(p), (unsigned long)DNS__32BIT(p+4), - (unsigned long)DNS__32BIT(p+8), (unsigned long)DNS__32BIT(p+12), - (unsigned long)DNS__32BIT(p+16)); + printf("\t\t\t\t\t\t( %u %u %u %u %u )", + DNS__32BIT(p), DNS__32BIT(p+4), + DNS__32BIT(p+8), DNS__32BIT(p+12), + DNS__32BIT(p+16)); break; case T_TXT: @@ -723,9 +707,9 @@ static const unsigned char *display_rr(const unsigned char *aptr, * priority, weight, and port, followed by a domain name. */ - printf("\t%d", DNS__16BIT(aptr)); - printf(" %d", DNS__16BIT(aptr + 2)); - printf(" %d", DNS__16BIT(aptr + 4)); + printf("\t%d", (int)DNS__16BIT(aptr)); + printf(" %d", (int)DNS__16BIT(aptr + 2)); + printf(" %d", (int)DNS__16BIT(aptr + 4)); status = ares_expand_name(aptr + 6, abuf, alen, &name.as_char, &len); if (status != ARES_SUCCESS) @@ -736,8 +720,8 @@ static const unsigned char *display_rr(const unsigned char *aptr, case T_NAPTR: - printf("\t%d", DNS__16BIT(aptr)); /* order */ - printf(" %d\n", DNS__16BIT(aptr + 2)); /* preference */ + printf("\t%d", (int)DNS__16BIT(aptr)); /* order */ + printf(" %d\n", (int)DNS__16BIT(aptr + 2)); /* preference */ p = aptr + 4; status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len); @@ -17,29 +17,17 @@ #include "ares_setup.h" #if !defined(WIN32) || defined(WATT32) -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif -#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif #endif + #ifdef HAVE_STRINGS_H #include <strings.h> #endif -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - #include "ares.h" #include "ares_dns.h" -#include "inet_ntop.h" -#include "inet_net_pton.h" #include "ares_getopt.h" #include "ares_ipv6.h" #include "ares_nowarn.h" @@ -64,6 +52,8 @@ static void usage(void); int main(int argc, char **argv) { + struct ares_options options; + int optmask = 0; ares_channel channel; int status, nfds, c, addr_family = AF_INET; fd_set read_fds, write_fds; @@ -77,6 +67,8 @@ int main(int argc, char **argv) WSAStartup(wVersionRequested, &wsaData); #endif + memset(&options, 0, sizeof(options)); + status = ares_library_init(ARES_LIB_INIT_ALL); if (status != ARES_SUCCESS) { @@ -84,7 +76,7 @@ int main(int argc, char **argv) return 1; } - while ((c = ares_getopt(argc,argv,"dt:h")) != -1) + while ((c = ares_getopt(argc,argv,"dt:hs:")) != -1) { switch (c) { @@ -93,11 +85,20 @@ int main(int argc, char **argv) dbug_init(); #endif break; + case 's': + optmask |= ARES_OPT_DOMAINS; + options.ndomains++; + options.domains = (char **)realloc(options.domains, + options.ndomains * sizeof(char *)); + options.domains[options.ndomains - 1] = strdup(optarg); + break; case 't': if (!strcasecmp(optarg,"a")) addr_family = AF_INET; else if (!strcasecmp(optarg,"aaaa")) addr_family = AF_INET6; + else if (!strcasecmp(optarg,"u")) + addr_family = AF_UNSPEC; else usage(); break; @@ -113,7 +114,7 @@ int main(int argc, char **argv) if (argc < 1) usage(); - status = ares_init(&channel); + status = ares_init_options(&channel, &options, optmask); if (status != ARES_SUCCESS) { fprintf(stderr, "ares_init: %s\n", ares_strerror(status)); @@ -142,13 +143,16 @@ int main(int argc, char **argv) /* Wait for all queries to complete. */ for (;;) { + int res; FD_ZERO(&read_fds); FD_ZERO(&write_fds); nfds = ares_fds(channel, &read_fds, &write_fds); if (nfds == 0) break; tvp = ares_timeout(channel, NULL, &tv); - select(nfds, &read_fds, &write_fds, NULL, tvp); + res = select(nfds, &read_fds, &write_fds, NULL, tvp); + if (-1 == res) + break; ares_process(channel, &read_fds, &write_fds); } @@ -197,6 +201,6 @@ static void callback(void *arg, int status, int timeouts, struct hostent *host) static void usage(void) { - fprintf(stderr, "usage: ahost [-t {a|aaaa}] {host|addr} ...\n"); + fprintf(stderr, "usage: ahost [-t {a|aaaa|u}] {host|addr} ...\n"); exit(1); } @@ -1,6 +1,6 @@ -/* Copyright 1998, 2009 by the Massachusetts Institute of Technology. - * Copyright (C) 2007-2011 by Daniel Stenberg +/* Copyright 1998 by the Massachusetts Institute of Technology. + * Copyright (C) 2007-2013 by Daniel Stenberg * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -38,7 +38,8 @@ require it! */ #if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \ defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \ - defined(__ANDROID__) + defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \ + defined(__QNXNTO__) #include <sys/select.h> #endif #if (defined(NETWARE) && !defined(__NOVELL_LIBC__)) @@ -75,22 +76,18 @@ extern "C" { ** c-ares external API function linkage decorations. */ -#if !defined(CARES_STATICLIB) && \ - (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__)) - /* __declspec function decoration for Win32 and Symbian DLL's */ +#ifdef CARES_STATICLIB +# define CARES_EXTERN +#elif defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__) # if defined(CARES_BUILDING_LIBRARY) # define CARES_EXTERN __declspec(dllexport) # else # define CARES_EXTERN __declspec(dllimport) # endif +#elif defined(CARES_BUILDING_LIBRARY) && defined(CARES_SYMBOL_HIDING) +# define CARES_EXTERN CARES_SYMBOL_SCOPE_EXTERN #else - /* visibility function decoration for other cases */ -# if !defined(CARES_SYMBOL_HIDING) || \ - defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__) -# define CARES_EXTERN -# else -# define CARES_EXTERN CARES_SYMBOL_SCOPE_EXTERN -# endif +# define CARES_EXTERN #endif @@ -143,6 +140,7 @@ extern "C" { #define ARES_FLAG_NOSEARCH (1 << 5) #define ARES_FLAG_NOALIASES (1 << 6) #define ARES_FLAG_NOCHECKRESP (1 << 7) +#define ARES_FLAG_EDNS (1 << 8) /* Option mask values */ #define ARES_OPT_FLAGS (1 << 0) @@ -160,6 +158,8 @@ extern "C" { #define ARES_OPT_SOCK_RCVBUF (1 << 12) #define ARES_OPT_TIMEOUTMS (1 << 13) #define ARES_OPT_ROTATE (1 << 14) +#define ARES_OPT_EDNSPSZ (1 << 15) +#define ARES_OPT_NOROTATE (1 << 16) /* Nameinfo flag values */ #define ARES_NI_NOFQDN (1 << 0) @@ -265,6 +265,7 @@ struct ares_options { void *sock_state_cb_data; struct apattern *sortlist; int nsort; + int ednspsz; }; struct hostent; @@ -295,8 +296,19 @@ typedef int (*ares_sock_create_callback)(ares_socket_t socket_fd, int type, void *data); +typedef int (*ares_sock_config_callback)(ares_socket_t socket_fd, + int type, + void *data); + CARES_EXTERN int ares_library_init(int flags); +CARES_EXTERN int ares_library_init_mem(int flags, + void *(*amalloc)(size_t size), + void (*afree)(void *ptr), + void *(*arealloc)(void *ptr, size_t size)); + +CARES_EXTERN int ares_library_initialized(void); + CARES_EXTERN void ares_library_cleanup(void); CARES_EXTERN const char *ares_version(int *version); @@ -338,6 +350,34 @@ CARES_EXTERN void ares_set_socket_callback(ares_channel channel, ares_sock_create_callback callback, void *user_data); +CARES_EXTERN void ares_set_socket_configure_callback(ares_channel channel, + ares_sock_config_callback callback, + void *user_data); + +CARES_EXTERN int ares_set_sortlist(ares_channel channel, + const char *sortstr); + +/* + * Virtual function set to have user-managed socket IO. + * Note that all functions need to be defined, and when + * set, the library will not do any bind nor set any + * socket options, assuming the client handles these + * through either socket creation or the + * ares_sock_config_callback call. + */ +struct iovec; +struct ares_socket_functions { + ares_socket_t(*asocket)(int, int, int, void *); + int(*aclose)(ares_socket_t, void *); + int(*aconnect)(ares_socket_t, const struct sockaddr *, ares_socklen_t, void *); + ares_ssize_t(*arecvfrom)(ares_socket_t, void *, size_t, int, struct sockaddr *, ares_socklen_t *, void *); + ares_ssize_t(*asendv)(ares_socket_t, const struct iovec *, int, void *); +}; + +CARES_EXTERN void ares_set_socket_functions(ares_channel channel, + const struct ares_socket_functions * funcs, + void *user_data); + CARES_EXTERN void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen, @@ -403,6 +443,15 @@ CARES_EXTERN void ares_process_fd(ares_channel channel, ares_socket_t read_fd, ares_socket_t write_fd); +CARES_EXTERN int ares_create_query(const char *name, + int dnsclass, + int type, + unsigned short id, + int rd, + unsigned char **buf, + int *buflen, + int max_udp_size); + CARES_EXTERN int ares_mkquery(const char *name, int dnsclass, int type, @@ -466,6 +515,37 @@ struct ares_txt_reply { size_t length; /* length excludes null termination */ }; +/* NOTE: This structure is a superset of ares_txt_reply + */ +struct ares_txt_ext { + struct ares_txt_ext *next; + unsigned char *txt; + size_t length; + /* 1 - if start of new record + * 0 - if a chunk in the same record */ + unsigned char record_start; +}; + +struct ares_naptr_reply { + struct ares_naptr_reply *next; + unsigned char *flags; + unsigned char *service; + unsigned char *regexp; + char *replacement; + unsigned short order; + unsigned short preference; +}; + +struct ares_soa_reply { + char *nsname; + char *hostmaster; + unsigned int serial; + unsigned int refresh; + unsigned int retry; + unsigned int expire; + unsigned int minttl; +}; + /* ** Parse the buffer, starting at *abuf and of length alen bytes, previously ** obtained from an ares_search call. Put the results in *host, if nonnull. @@ -509,6 +589,18 @@ CARES_EXTERN int ares_parse_txt_reply(const unsigned char* abuf, int alen, struct ares_txt_reply** txt_out); +CARES_EXTERN int ares_parse_txt_reply_ext(const unsigned char* abuf, + int alen, + struct ares_txt_ext** txt_out); + +CARES_EXTERN int ares_parse_naptr_reply(const unsigned char* abuf, + int alen, + struct ares_naptr_reply** naptr_out); + +CARES_EXTERN int ares_parse_soa_reply(const unsigned char* abuf, + int alen, + struct ares_soa_reply** soa_out); + CARES_EXTERN void ares_free_string(void *str); CARES_EXTERN void ares_free_hostent(struct hostent *host); @@ -517,7 +609,6 @@ CARES_EXTERN void ares_free_data(void *dataptr); CARES_EXTERN const char *ares_strerror(int code); -/* TODO: Hold port here as well. */ struct ares_addr_node { struct ares_addr_node *next; int family; @@ -527,15 +618,38 @@ struct ares_addr_node { } addr; }; +struct ares_addr_port_node { + struct ares_addr_port_node *next; + int family; + union { + struct in_addr addr4; + struct ares_in6_addr addr6; + } addr; + int udp_port; + int tcp_port; +}; + CARES_EXTERN int ares_set_servers(ares_channel channel, struct ares_addr_node *servers); +CARES_EXTERN int ares_set_servers_ports(ares_channel channel, + struct ares_addr_port_node *servers); /* Incomming string format: host[:port][,host[:port]]... */ CARES_EXTERN int ares_set_servers_csv(ares_channel channel, const char* servers); +CARES_EXTERN int ares_set_servers_ports_csv(ares_channel channel, + const char* servers); CARES_EXTERN int ares_get_servers(ares_channel channel, struct ares_addr_node **servers); +CARES_EXTERN int ares_get_servers_ports(ares_channel channel, + struct ares_addr_port_node **servers); + +CARES_EXTERN const char *ares_inet_ntop(int af, const void *src, char *dst, + ares_socklen_t size); + +CARES_EXTERN int ares_inet_pton(int af, const char *src, void *dst); + #ifdef __cplusplus } diff --git a/ares__close_sockets.c b/ares__close_sockets.c index 5d391a9..f07904e 100644 --- a/ares__close_sockets.c +++ b/ares__close_sockets.c @@ -16,11 +16,6 @@ #include "ares_setup.h" -#include <stdlib.h> -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - #include "ares.h" #include "ares_private.h" @@ -35,14 +30,14 @@ void ares__close_sockets(ares_channel channel, struct server_state *server) sendreq = server->qhead; server->qhead = sendreq->next; if (sendreq->data_storage != NULL) - free(sendreq->data_storage); - free(sendreq); + ares_free(sendreq->data_storage); + ares_free(sendreq); } server->qtail = NULL; /* Reset any existing input buffer. */ if (server->tcp_buffer) - free(server->tcp_buffer); + ares_free(server->tcp_buffer); server->tcp_buffer = NULL; server->tcp_lenbuf_pos = 0; @@ -53,14 +48,14 @@ void ares__close_sockets(ares_channel channel, struct server_state *server) if (server->tcp_socket != ARES_SOCKET_BAD) { SOCK_STATE_CALLBACK(channel, server->tcp_socket, 0, 0); - sclose(server->tcp_socket); + ares__socket_close(channel, server->tcp_socket); server->tcp_socket = ARES_SOCKET_BAD; server->tcp_connection_generation = ++channel->tcp_connection_generation; } if (server->udp_socket != ARES_SOCKET_BAD) { SOCK_STATE_CALLBACK(channel, server->udp_socket, 0, 0); - sclose(server->udp_socket); + ares__socket_close(channel, server->udp_socket); server->udp_socket = ARES_SOCKET_BAD; } } diff --git a/ares__get_hostent.c b/ares__get_hostent.c index 298df09..d2f9503 100644 --- a/ares__get_hostent.c +++ b/ares__get_hostent.c @@ -1,5 +1,5 @@ -/* Copyright 1998, 2010 by the Massachusetts Institute of Technology. +/* Copyright 1998, 2011 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -16,9 +16,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -30,7 +27,8 @@ #endif #include "ares.h" -#include "inet_net_pton.h" +#include "ares_inet_net_pton.h" +#include "ares_nowarn.h" #include "ares_private.h" int ares__get_hostent(FILE *fp, int family, struct hostent **host) @@ -96,7 +94,7 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host) p++; if (!*p) /* Ignore line if reached end of line. */ - continue; + continue; /* LCOV_EXCL_LINE: trailing whitespace already stripped */ /* Pointer to start of host name. */ txthost = p; @@ -166,7 +164,7 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host) */ /* Allocate memory for the hostent structure. */ - hostent = malloc(sizeof(struct hostent)); + hostent = ares_malloc(sizeof(struct hostent)); if (!hostent) break; @@ -175,16 +173,16 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host) hostent->h_addr_list = NULL; /* Copy official host name. */ - hostent->h_name = strdup(txthost); + hostent->h_name = ares_strdup(txthost); if (!hostent->h_name) break; /* Copy network address. */ - hostent->h_addr_list = malloc(2 * sizeof(char *)); + hostent->h_addr_list = ares_malloc(2 * sizeof(char *)); if (!hostent->h_addr_list) break; hostent->h_addr_list[1] = NULL; - hostent->h_addr_list[0] = malloc(addrlen); + hostent->h_addr_list[0] = ares_malloc(addrlen); if (!hostent->h_addr_list[0]) break; if (addr.family == AF_INET) @@ -193,7 +191,7 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host) memcpy(hostent->h_addr_list[0], &addr.addrV6, sizeof(addr.addrV6)); /* Copy aliases. */ - hostent->h_aliases = malloc((naliases + 1) * sizeof(char *)); + hostent->h_aliases = ares_malloc((naliases + 1) * sizeof(char *)); if (!hostent->h_aliases) break; alias = hostent->h_aliases; @@ -209,7 +207,7 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host) while (*q && ISSPACE(*q)) q++; *p = '\0'; - if ((*alias = strdup(txtalias)) == NULL) + if ((*alias = ares_strdup(txtalias)) == NULL) break; alias++; txtalias = *q ? q : NULL; @@ -219,11 +217,11 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host) break; /* Copy actual network address family and length. */ - hostent->h_addrtype = addr.family; - hostent->h_length = (int)addrlen; + hostent->h_addrtype = aresx_sitoss(addr.family); + hostent->h_length = aresx_uztoss(addrlen); /* Free line buffer. */ - free(line); + ares_free(line); /* Return hostent successfully */ *host = hostent; @@ -233,7 +231,7 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host) /* If allocated, free line buffer. */ if (line) - free(line); + ares_free(line); if (status == ARES_SUCCESS) { @@ -241,20 +239,20 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host) if (hostent) { if (hostent->h_name) - free((char *) hostent->h_name); + ares_free((char *) hostent->h_name); if (hostent->h_aliases) { for (alias = hostent->h_aliases; *alias; alias++) - free(*alias); - free(hostent->h_aliases); + ares_free(*alias); + ares_free(hostent->h_aliases); } if (hostent->h_addr_list) { if (hostent->h_addr_list[0]) - free(hostent->h_addr_list[0]); - free(hostent->h_addr_list); + ares_free(hostent->h_addr_list[0]); + ares_free(hostent->h_addr_list); } - free(hostent); + ares_free(hostent); } return ARES_ENOMEM; } diff --git a/ares__read_line.c b/ares__read_line.c index ca01803..c62ad2a 100644 --- a/ares__read_line.c +++ b/ares__read_line.c @@ -15,9 +15,7 @@ */ #include "ares_setup.h" -#include <stdio.h> -#include <stdlib.h> -#include <string.h> + #include "ares.h" #include "ares_nowarn.h" #include "ares_private.h" @@ -38,7 +36,7 @@ int ares__read_line(FILE *fp, char **buf, size_t *bufsize) if (*buf == NULL) { - *buf = malloc(128); + *buf = ares_malloc(128); if (!*buf) return ARES_ENOMEM; *bufsize = 128; @@ -61,9 +59,13 @@ int ares__read_line(FILE *fp, char **buf, size_t *bufsize) continue; /* Allocate more space. */ - newbuf = realloc(*buf, *bufsize * 2); + newbuf = ares_realloc(*buf, *bufsize * 2); if (!newbuf) - return ARES_ENOMEM; + { + ares_free(*buf); + *buf = NULL; + return ARES_ENOMEM; + } *buf = newbuf; *bufsize *= 2; } diff --git a/ares__timeval.c b/ares__timeval.c index f7aa788..94efb7d 100644 --- a/ares__timeval.c +++ b/ares__timeval.c @@ -56,7 +56,7 @@ struct timeval ares__tvnow(void) */ #ifdef HAVE_GETTIMEOFDAY else - (void)gettimeofday(&now, NULL); + (void)gettimeofday(&now, NULL); /* LCOV_EXCL_LINE */ #else else { now.tv_sec = (long)time(NULL); diff --git a/ares_android.c b/ares_android.c new file mode 100644 index 0000000..bf77131 --- /dev/null +++ b/ares_android.c @@ -0,0 +1,446 @@ +/* Copyright (C) 2017 by John Schember <john@nachtimwald.com> + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ +#if defined(ANDROID) || defined(__ANDROID__) + +#include <jni.h> + +#include "ares_setup.h" +#include "ares.h" +#include "ares_android.h" +#include "ares_private.h" + +static JavaVM *android_jvm = NULL; +static jobject android_connectivity_manager = NULL; + +/* ConnectivityManager.getActiveNetwork */ +static jmethodID android_cm_active_net_mid = NULL; +/* ConnectivityManager.getLinkProperties */ +static jmethodID android_cm_link_props_mid = NULL; +/* LinkProperties.getDnsServers */ +static jmethodID android_lp_dns_servers_mid = NULL; +/* LinkProperties.getDomains */ +static jmethodID android_lp_domains_mid = NULL; +/* List.size */ +static jmethodID android_list_size_mid = NULL; +/* List.get */ +static jmethodID android_list_get_mid = NULL; +/* InetAddress.getHostAddress */ +static jmethodID android_ia_host_addr_mid = NULL; + +static jclass jni_get_class(JNIEnv *env, const char *path) +{ + jclass cls = NULL; + + if (env == NULL || path == NULL || *path == '\0') + return NULL; + + cls = (*env)->FindClass(env, path); + if ((*env)->ExceptionOccurred(env)) { + (*env)->ExceptionClear(env); + return NULL; + } + return cls; +} + +static jmethodID jni_get_method_id(JNIEnv *env, jclass cls, + const char *func_name, const char *signature) +{ + jmethodID mid = NULL; + + if (env == NULL || cls == NULL || func_name == NULL || *func_name == '\0' || + signature == NULL || *signature == '\0') + { + return NULL; + } + + mid = (*env)->GetMethodID(env, cls, func_name, signature); + if ((*env)->ExceptionOccurred(env)) + { + (*env)->ExceptionClear(env); + return NULL; + } + + return mid; +} + +void ares_library_init_jvm(JavaVM *jvm) +{ + android_jvm = jvm; +} + +int ares_library_init_android(jobject connectivity_manager) +{ + JNIEnv *env = NULL; + int need_detatch = 0; + int res; + int ret = ARES_ENOTINITIALIZED; + jclass obj_cls = NULL; + + if (android_jvm == NULL) + goto cleanup; + + res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6); + if (res == JNI_EDETACHED) + { + env = NULL; + res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL); + need_detatch = 1; + } + if (res != JNI_OK || env == NULL) + goto cleanup; + + android_connectivity_manager = + (*env)->NewGlobalRef(env, connectivity_manager); + if (android_connectivity_manager == NULL) + goto cleanup; + + /* Initialization has succeeded. Now attempt to cache the methods that will be + * called by ares_get_android_server_list. */ + ret = ARES_SUCCESS; + + /* ConnectivityManager in API 1. */ + obj_cls = jni_get_class(env, "android/net/ConnectivityManager"); + if (obj_cls == NULL) + goto cleanup; + + /* ConnectivityManager.getActiveNetwork in API 23. */ + android_cm_active_net_mid = + jni_get_method_id(env, obj_cls, "getActiveNetwork", + "()Landroid/net/Network;"); + if (android_cm_active_net_mid == NULL) + goto cleanup; + + /* ConnectivityManager.getLinkProperties in API 21. */ + android_cm_link_props_mid = + jni_get_method_id(env, obj_cls, "getLinkProperties", + "(Landroid/net/Network;)Landroid/net/LinkProperties;"); + if (android_cm_link_props_mid == NULL) + goto cleanup; + + /* LinkProperties in API 21. */ + (*env)->DeleteLocalRef(env, obj_cls); + obj_cls = jni_get_class(env, "android/net/LinkProperties"); + if (obj_cls == NULL) + goto cleanup; + + /* getDnsServers in API 21. */ + android_lp_dns_servers_mid = jni_get_method_id(env, obj_cls, "getDnsServers", + "()Ljava/util/List;"); + if (android_lp_dns_servers_mid == NULL) + goto cleanup; + + /* getDomains in API 21. */ + android_lp_domains_mid = jni_get_method_id(env, obj_cls, "getDomains", + "()Ljava/lang/String;"); + if (android_lp_domains_mid == NULL) + goto cleanup; + + (*env)->DeleteLocalRef(env, obj_cls); + obj_cls = jni_get_class(env, "java/util/List"); + if (obj_cls == NULL) + goto cleanup; + + android_list_size_mid = jni_get_method_id(env, obj_cls, "size", "()I"); + if (android_list_size_mid == NULL) + goto cleanup; + + android_list_get_mid = jni_get_method_id(env, obj_cls, "get", + "(I)Ljava/lang/Object;"); + if (android_list_get_mid == NULL) + goto cleanup; + + (*env)->DeleteLocalRef(env, obj_cls); + obj_cls = jni_get_class(env, "java/net/InetAddress"); + if (obj_cls == NULL) + goto cleanup; + + android_ia_host_addr_mid = jni_get_method_id(env, obj_cls, "getHostAddress", + "()Ljava/lang/String;"); + if (android_ia_host_addr_mid == NULL) + goto cleanup; + + (*env)->DeleteLocalRef(env, obj_cls); + goto done; + +cleanup: + if (obj_cls != NULL) + (*env)->DeleteLocalRef(env, obj_cls); + + android_cm_active_net_mid = NULL; + android_cm_link_props_mid = NULL; + android_lp_dns_servers_mid = NULL; + android_lp_domains_mid = NULL; + android_list_size_mid = NULL; + android_list_get_mid = NULL; + android_ia_host_addr_mid = NULL; + +done: + if (need_detatch) + (*android_jvm)->DetachCurrentThread(android_jvm); + + return ret; +} + +int ares_library_android_initialized(void) +{ + if (android_jvm == NULL || android_connectivity_manager == NULL) + return ARES_ENOTINITIALIZED; + return ARES_SUCCESS; +} + +void ares_library_cleanup_android(void) +{ + JNIEnv *env = NULL; + int need_detatch = 0; + int res; + + if (android_jvm == NULL || android_connectivity_manager == NULL) + return; + + res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6); + if (res == JNI_EDETACHED) + { + env = NULL; + res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL); + need_detatch = 1; + } + if (res != JNI_OK || env == NULL) + return; + + android_cm_active_net_mid = NULL; + android_cm_link_props_mid = NULL; + android_lp_dns_servers_mid = NULL; + android_lp_domains_mid = NULL; + android_list_size_mid = NULL; + android_list_get_mid = NULL; + android_ia_host_addr_mid = NULL; + + (*env)->DeleteGlobalRef(env, android_connectivity_manager); + android_connectivity_manager = NULL; + + if (need_detatch) + (*android_jvm)->DetachCurrentThread(android_jvm); +} + +char **ares_get_android_server_list(size_t max_servers, + size_t *num_servers) +{ + JNIEnv *env = NULL; + jobject active_network = NULL; + jobject link_properties = NULL; + jobject server_list = NULL; + jobject server = NULL; + jstring str = NULL; + jint nserv; + const char *ch_server_address; + int res; + size_t i; + char **dns_list = NULL; + int need_detatch = 0; + + if (android_jvm == NULL || android_connectivity_manager == NULL || + max_servers == 0 || num_servers == NULL) + { + return NULL; + } + + if (android_cm_active_net_mid == NULL || android_cm_link_props_mid == NULL || + android_lp_dns_servers_mid == NULL || android_list_size_mid == NULL || + android_list_get_mid == NULL || android_ia_host_addr_mid == NULL) + { + return NULL; + } + + res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6); + if (res == JNI_EDETACHED) + { + env = NULL; + res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL); + need_detatch = 1; + } + if (res != JNI_OK || env == NULL) + goto done; + + /* JNI below is equivalent to this Java code. + import android.content.Context; + import android.net.ConnectivityManager; + import android.net.LinkProperties; + import android.net.Network; + import java.net.InetAddress; + import java.util.List; + + ConnectivityManager cm = (ConnectivityManager)this.getApplicationContext() + .getSystemService(Context.CONNECTIVITY_SERVICE); + Network an = cm.getActiveNetwork(); + LinkProperties lp = cm.getLinkProperties(an); + List<InetAddress> dns = lp.getDnsServers(); + for (InetAddress ia: dns) { + String ha = ia.getHostAddress(); + } + + Note: The JNI ConnectivityManager object and all method IDs were previously + initialized in ares_library_init_android. + */ + + active_network = (*env)->CallObjectMethod(env, android_connectivity_manager, + android_cm_active_net_mid); + if (active_network == NULL) + goto done; + + link_properties = + (*env)->CallObjectMethod(env, android_connectivity_manager, + android_cm_link_props_mid, active_network); + if (link_properties == NULL) + goto done; + + server_list = (*env)->CallObjectMethod(env, link_properties, + android_lp_dns_servers_mid); + if (server_list == NULL) + goto done; + + nserv = (*env)->CallIntMethod(env, server_list, android_list_size_mid); + if (nserv > (jint)max_servers) + nserv = (jint)max_servers; + if (nserv <= 0) + goto done; + *num_servers = (size_t)nserv; + + dns_list = ares_malloc(sizeof(*dns_list)*(*num_servers)); + for (i=0; i<*num_servers; i++) + { + server = (*env)->CallObjectMethod(env, server_list, android_list_get_mid, + (jint)i); + dns_list[i] = ares_malloc(64); + dns_list[i][0] = 0; + if (server == NULL) + { + continue; + } + str = (*env)->CallObjectMethod(env, server, android_ia_host_addr_mid); + ch_server_address = (*env)->GetStringUTFChars(env, str, 0); + strncpy(dns_list[i], ch_server_address, 64); + (*env)->ReleaseStringUTFChars(env, str, ch_server_address); + (*env)->DeleteLocalRef(env, str); + (*env)->DeleteLocalRef(env, server); + } + +done: + if ((*env)->ExceptionOccurred(env)) + (*env)->ExceptionClear(env); + + if (server_list != NULL) + (*env)->DeleteLocalRef(env, server_list); + if (link_properties != NULL) + (*env)->DeleteLocalRef(env, link_properties); + if (active_network != NULL) + (*env)->DeleteLocalRef(env, active_network); + + if (need_detatch) + (*android_jvm)->DetachCurrentThread(android_jvm); + return dns_list; +} + +char *ares_get_android_search_domains_list(void) +{ + JNIEnv *env = NULL; + jobject active_network = NULL; + jobject link_properties = NULL; + jstring domains = NULL; + const char *domain; + int res; + size_t i; + size_t cnt = 0; + char *domain_list = NULL; + int need_detatch = 0; + + if (android_jvm == NULL || android_connectivity_manager == NULL) + { + return NULL; + } + + if (android_cm_active_net_mid == NULL || android_cm_link_props_mid == NULL || + android_lp_domains_mid == NULL) + { + return NULL; + } + + res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6); + if (res == JNI_EDETACHED) + { + env = NULL; + res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL); + need_detatch = 1; + } + if (res != JNI_OK || env == NULL) + goto done; + + /* JNI below is equivalent to this Java code. + import android.content.Context; + import android.net.ConnectivityManager; + import android.net.LinkProperties; + + ConnectivityManager cm = (ConnectivityManager)this.getApplicationContext() + .getSystemService(Context.CONNECTIVITY_SERVICE); + Network an = cm.getActiveNetwork(); + LinkProperties lp = cm.getLinkProperties(an); + String domains = lp.getDomains(); + for (String domain: domains.split(",")) { + String d = domain; + } + + Note: The JNI ConnectivityManager object and all method IDs were previously + initialized in ares_library_init_android. + */ + + active_network = (*env)->CallObjectMethod(env, android_connectivity_manager, + android_cm_active_net_mid); + if (active_network == NULL) + goto done; + + link_properties = + (*env)->CallObjectMethod(env, android_connectivity_manager, + android_cm_link_props_mid, active_network); + if (link_properties == NULL) + goto done; + + /* Get the domains. It is a common separated list of domains to search. */ + domains = (*env)->CallObjectMethod(env, link_properties, + android_lp_domains_mid); + if (domains == NULL) + goto done; + + /* Split on , */ + domain = (*env)->GetStringUTFChars(env, domains, 0); + domain_list = ares_strdup(domain); + (*env)->ReleaseStringUTFChars(env, domains, domain); + (*env)->DeleteLocalRef(env, domains); + +done: + if ((*env)->ExceptionOccurred(env)) + (*env)->ExceptionClear(env); + + if (link_properties != NULL) + (*env)->DeleteLocalRef(env, link_properties); + if (active_network != NULL) + (*env)->DeleteLocalRef(env, active_network); + + if (need_detatch) + (*android_jvm)->DetachCurrentThread(android_jvm); + return domain_list; +} +#else +/* warning: ISO C forbids an empty translation unit */ +typedef int dummy_make_iso_compilers_happy; +#endif diff --git a/ares_android.h b/ares_android.h new file mode 100644 index 0000000..93fb75f --- /dev/null +++ b/ares_android.h @@ -0,0 +1,27 @@ +/* Copyright (C) 2017 by John Schember <john@nachtimwald.com> + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifndef __ARES_ANDROID_H__ +#define __ARES_ANDROID_H__ + +#if defined(ANDROID) || defined(__ANDROID__) + +char **ares_get_android_server_list(size_t max_servers, size_t *num_servers); +char *ares_get_android_search_domains_list(void); +void ares_library_cleanup_android(void); + +#endif + +#endif /* __ARES_ANDROID_H__ */ diff --git a/ares_build.h b/ares_build.h index e2c14c6..e5988cf 100644 --- a/ares_build.h +++ b/ares_build.h @@ -2,7 +2,7 @@ #define __CARES_BUILD_H -/* Copyright (C) 2009 - 2010 by Daniel Stenberg et al +/* Copyright (C) 2009 - 2013 by Daniel Stenberg et al * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided @@ -83,148 +83,88 @@ /* DEFINITION OF THESE SYMBOLS SHALL NOT TAKE PLACE ANYWHERE ELSE */ /* ================================================================ */ -#ifdef CARES_SIZEOF_LONG -# error "CARES_SIZEOF_LONG shall not be defined except in ares_build.h" - Error Compilation_aborted_CARES_SIZEOF_LONG_already_defined -#endif - #ifdef CARES_TYPEOF_ARES_SOCKLEN_T # error "CARES_TYPEOF_ARES_SOCKLEN_T shall not be defined except in ares_build.h" Error Compilation_aborted_CARES_TYPEOF_ARES_SOCKLEN_T_already_defined #endif -#ifdef CARES_SIZEOF_ARES_SOCKLEN_T -# error "CARES_SIZEOF_ARES_SOCKLEN_T shall not be defined except in ares_build.h" - Error Compilation_aborted_CARES_SIZEOF_ARES_SOCKLEN_T_already_defined -#endif - /* ================================================================ */ /* EXTERNAL INTERFACE SETTINGS FOR NON-CONFIGURE SYSTEMS ONLY */ /* ================================================================ */ #if defined(__DJGPP__) || defined(__GO32__) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 #elif defined(__SALFORDC__) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 #elif defined(__BORLANDC__) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 #elif defined(__TURBOC__) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 #elif defined(__WATCOMC__) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 #elif defined(__POCC__) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 #elif defined(__LCC__) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 #elif defined(__SYMBIAN32__) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T unsigned int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 #elif defined(__MWERKS__) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 #elif defined(_WIN32_WCE) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 #elif defined(__MINGW32__) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 #elif defined(__VMS) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T unsigned int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 #elif defined(__OS400__) # if defined(__ILEC400__) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T socklen_t -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 # define CARES_PULL_SYS_TYPES_H 1 # define CARES_PULL_SYS_SOCKET_H 1 # endif #elif defined(__MVS__) # if defined(__IBMC__) || defined(__IBMCPP__) -# if defined(_ILP32) -# define CARES_SIZEOF_LONG 4 -# elif defined(_LP64) -# define CARES_SIZEOF_LONG 8 -# endif # define CARES_TYPEOF_ARES_SOCKLEN_T socklen_t -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 # define CARES_PULL_SYS_TYPES_H 1 # define CARES_PULL_SYS_SOCKET_H 1 # endif #elif defined(__370__) # if defined(__IBMC__) || defined(__IBMCPP__) -# if defined(_ILP32) -# define CARES_SIZEOF_LONG 4 -# elif defined(_LP64) -# define CARES_SIZEOF_LONG 8 -# endif # define CARES_TYPEOF_ARES_SOCKLEN_T socklen_t -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 # define CARES_PULL_SYS_TYPES_H 1 # define CARES_PULL_SYS_SOCKET_H 1 # endif #elif defined(TPF) -# define CARES_SIZEOF_LONG 8 # define CARES_TYPEOF_ARES_SOCKLEN_T int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 /* ===================================== */ /* KEEP MSVC THE PENULTIMATE ENTRY */ /* ===================================== */ #elif defined(_MSC_VER) -# define CARES_SIZEOF_LONG 4 # define CARES_TYPEOF_ARES_SOCKLEN_T int -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 /* ===================================== */ /* KEEP GENERIC GCC THE LAST ENTRY */ /* ===================================== */ #elif defined(__GNUC__) -# if defined(__i386__) || defined(__ppc__) -# define CARES_SIZEOF_LONG 4 -# elif defined(__x86_64__) || defined(__ppc64__) -# define CARES_SIZEOF_LONG 8 -# else -# define CARES_SIZEOF_LONG sizeof(long) -# endif # define CARES_TYPEOF_ARES_SOCKLEN_T socklen_t -# define CARES_SIZEOF_ARES_SOCKLEN_T 4 # define CARES_PULL_SYS_TYPES_H 1 # define CARES_PULL_SYS_SOCKET_H 1 @@ -251,4 +191,33 @@ typedef CARES_TYPEOF_ARES_SOCKLEN_T ares_socklen_t; #endif +/* Data type definition of ares_ssize_t. */ +/* gRPC Manuel edit here! + * Possibly include <_mingw.h> header to define __int64 type under mingw */ +#ifdef _WIN32 +# ifdef _WIN64 +# ifdef __MINGW32__ +# include <_mingw.h> +# endif +# define CARES_TYPEOF_ARES_SSIZE_T __int64 +# else +# define CARES_TYPEOF_ARES_SSIZE_T long +# endif +#else +# define CARES_TYPEOF_ARES_SSIZE_T ssize_t +#endif + +typedef CARES_TYPEOF_ARES_SSIZE_T ares_ssize_t; + +/* IMPORTANT: gRPC MANUAL EDIT HERE! + * Undefine UNICODE, as c-ares does not use the ANSI version of functions + * explicitly. */ +#ifdef UNICODE +# undef UNICODE +#endif + +#ifdef _UNICODE +# undef _UNICODE +#endif + #endif /* __CARES_BUILD_H */ diff --git a/ares_cancel.c b/ares_cancel.c index eb790ae..465cc9e 100644 --- a/ares_cancel.c +++ b/ares_cancel.c @@ -14,7 +14,7 @@ #include "ares_setup.h" #include <assert.h> -#include <stdlib.h> + #include "ares.h" #include "ares_private.h" @@ -26,33 +26,33 @@ void ares_cancel(ares_channel channel) { struct query *query; + struct list_node list_head_copy; struct list_node* list_head; struct list_node* list_node; int i; - list_head = &(channel->all_queries); - for (list_node = list_head->next; list_node != list_head; ) + if (!ares__is_list_empty(&(channel->all_queries))) { - query = list_node->data; - list_node = list_node->next; /* since we're deleting the query */ - query->callback(query->arg, ARES_ECANCELLED, 0, NULL, 0); - ares__free_query(query); - } -#ifndef NDEBUG - /* Freeing the query should remove it from all the lists in which it sits, - * so all query lists should be empty now. - */ - assert(ares__is_list_empty(&(channel->all_queries))); - for (i = 0; i < ARES_QID_TABLE_SIZE; i++) + /* Swap list heads, so that only those queries which were present on entry + * into this function are cancelled. New queries added by callbacks of + * queries being cancelled will not be cancelled themselves. + */ + list_head = &(channel->all_queries); + list_head_copy.prev = list_head->prev; + list_head_copy.next = list_head->next; + list_head_copy.prev->next = &list_head_copy; + list_head_copy.next->prev = &list_head_copy; + list_head->prev = list_head; + list_head->next = list_head; + for (list_node = list_head_copy.next; list_node != &list_head_copy; ) { - assert(ares__is_list_empty(&(channel->queries_by_qid[i]))); + query = list_node->data; + list_node = list_node->next; /* since we're deleting the query */ + query->callback(query->arg, ARES_ECANCELLED, 0, NULL, 0); + ares__free_query(query); } - for (i = 0; i < ARES_TIMEOUT_TABLE_SIZE; i++) - { - assert(ares__is_list_empty(&(channel->queries_by_timeout[i]))); - } -#endif - if (!(channel->flags & ARES_FLAG_STAYOPEN)) + } + if (!(channel->flags & ARES_FLAG_STAYOPEN) && ares__is_list_empty(&(channel->all_queries))) { if (channel->servers) { diff --git a/ares_create_query.c b/ares_create_query.c new file mode 100644 index 0000000..07d7570 --- /dev/null +++ b/ares_create_query.c @@ -0,0 +1,202 @@ + +/* Copyright 1998 by the Massachusetts Institute of Technology. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_NAMESER_H +# include <arpa/nameser.h> +#else +# include "nameser.h" +#endif +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +# include <arpa/nameser_compat.h> +#endif + +#include "ares.h" +#include "ares_dns.h" +#include "ares_private.h" + +#ifndef T_OPT +# define T_OPT 41 /* EDNS0 option (meta-RR) */ +#endif + +/* Header format, from RFC 1035: + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ID | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |QR| Opcode |AA|TC|RD|RA| Z | RCODE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QDCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ANCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | NSCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ARCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * AA, TC, RA, and RCODE are only set in responses. Brief description + * of the remaining fields: + * ID Identifier to match responses with queries + * QR Query (0) or response (1) + * Opcode For our purposes, always QUERY + * RD Recursion desired + * Z Reserved (zero) + * QDCOUNT Number of queries + * ANCOUNT Number of answers + * NSCOUNT Number of name server records + * ARCOUNT Number of additional records + * + * Question format, from RFC 1035: + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * / QNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QTYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QCLASS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * The query name is encoded as a series of labels, each represented + * as a one-byte length (maximum 63) followed by the text of the + * label. The list is terminated by a label of length zero (which can + * be thought of as the root domain). + */ + +int ares_create_query(const char *name, int dnsclass, int type, + unsigned short id, int rd, unsigned char **bufp, + int *buflenp, int max_udp_size) +{ + size_t len; + unsigned char *q; + const char *p; + size_t buflen; + unsigned char *buf; + + /* Set our results early, in case we bail out early with an error. */ + *buflenp = 0; + *bufp = NULL; + + /* Allocate a memory area for the maximum size this packet might need. +2 + * is for the length byte and zero termination if no dots or ecscaping is + * used. + */ + len = strlen(name) + 2 + HFIXEDSZ + QFIXEDSZ + + (max_udp_size ? EDNSFIXEDSZ : 0); + buf = ares_malloc(len); + if (!buf) + return ARES_ENOMEM; + + /* Set up the header. */ + q = buf; + memset(q, 0, HFIXEDSZ); + DNS_HEADER_SET_QID(q, id); + DNS_HEADER_SET_OPCODE(q, QUERY); + if (rd) { + DNS_HEADER_SET_RD(q, 1); + } + else { + DNS_HEADER_SET_RD(q, 0); + } + DNS_HEADER_SET_QDCOUNT(q, 1); + + if (max_udp_size) { + DNS_HEADER_SET_ARCOUNT(q, 1); + } + + /* A name of "." is a screw case for the loop below, so adjust it. */ + if (strcmp(name, ".") == 0) + name++; + + /* Start writing out the name after the header. */ + q += HFIXEDSZ; + while (*name) + { + if (*name == '.') { + ares_free (buf); + return ARES_EBADNAME; + } + + /* Count the number of bytes in this label. */ + len = 0; + for (p = name; *p && *p != '.'; p++) + { + if (*p == '\\' && *(p + 1) != 0) + p++; + len++; + } + if (len > MAXLABEL) { + ares_free (buf); + return ARES_EBADNAME; + } + + /* Encode the length and copy the data. */ + *q++ = (unsigned char)len; + for (p = name; *p && *p != '.'; p++) + { + if (*p == '\\' && *(p + 1) != 0) + p++; + *q++ = *p; + } + + /* Go to the next label and repeat, unless we hit the end. */ + if (!*p) + break; + name = p + 1; + } + + /* Add the zero-length label at the end. */ + *q++ = 0; + + /* Finish off the question with the type and class. */ + DNS_QUESTION_SET_TYPE(q, type); + DNS_QUESTION_SET_CLASS(q, dnsclass); + + q += QFIXEDSZ; + if (max_udp_size) + { + memset(q, 0, EDNSFIXEDSZ); + q++; + DNS_RR_SET_TYPE(q, T_OPT); + DNS_RR_SET_CLASS(q, max_udp_size); + q += (EDNSFIXEDSZ-1); + } + buflen = (q - buf); + + /* Reject names that are longer than the maximum of 255 bytes that's + * specified in RFC 1035 ("To simplify implementations, the total length of + * a domain name (i.e., label octets and label length octets) is restricted + * to 255 octets or less."). */ + if (buflen > (MAXCDNAME + HFIXEDSZ + QFIXEDSZ + + (max_udp_size ? EDNSFIXEDSZ : 0))) { + ares_free (buf); + return ARES_EBADNAME; + } + + /* we know this fits in an int at this point */ + *buflenp = (int) buflen; + *bufp = buf; + + return ARES_SUCCESS; +} diff --git a/ares_data.c b/ares_data.c index a2477be..f891113 100644 --- a/ares_data.c +++ b/ares_data.c @@ -1,5 +1,5 @@ -/* Copyright (C) 2009-2010 by Daniel Stenberg +/* Copyright (C) 2009-2013 by Daniel Stenberg * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -67,7 +67,7 @@ void ares_free_data(void *dataptr) if (ptr->data.mx_reply.next) ares_free_data(ptr->data.mx_reply.next); if (ptr->data.mx_reply.host) - free(ptr->data.mx_reply.host); + ares_free(ptr->data.mx_reply.host); break; case ARES_DATATYPE_SRV_REPLY: @@ -75,15 +75,16 @@ void ares_free_data(void *dataptr) if (ptr->data.srv_reply.next) ares_free_data(ptr->data.srv_reply.next); if (ptr->data.srv_reply.host) - free(ptr->data.srv_reply.host); + ares_free(ptr->data.srv_reply.host); break; case ARES_DATATYPE_TXT_REPLY: + case ARES_DATATYPE_TXT_EXT: if (ptr->data.txt_reply.next) ares_free_data(ptr->data.txt_reply.next); if (ptr->data.txt_reply.txt) - free(ptr->data.txt_reply.txt); + ares_free(ptr->data.txt_reply.txt); break; case ARES_DATATYPE_ADDR_NODE: @@ -92,11 +93,38 @@ void ares_free_data(void *dataptr) ares_free_data(ptr->data.addr_node.next); break; + case ARES_DATATYPE_ADDR_PORT_NODE: + + if (ptr->data.addr_port_node.next) + ares_free_data(ptr->data.addr_port_node.next); + break; + + case ARES_DATATYPE_NAPTR_REPLY: + + if (ptr->data.naptr_reply.next) + ares_free_data(ptr->data.naptr_reply.next); + if (ptr->data.naptr_reply.flags) + ares_free(ptr->data.naptr_reply.flags); + if (ptr->data.naptr_reply.service) + ares_free(ptr->data.naptr_reply.service); + if (ptr->data.naptr_reply.regexp) + ares_free(ptr->data.naptr_reply.regexp); + if (ptr->data.naptr_reply.replacement) + ares_free(ptr->data.naptr_reply.replacement); + break; + + case ARES_DATATYPE_SOA_REPLY: + if (ptr->data.soa_reply.nsname) + ares_free(ptr->data.soa_reply.nsname); + if (ptr->data.soa_reply.hostmaster) + ares_free(ptr->data.soa_reply.hostmaster); + break; + default: return; } - free(ptr); + ares_free(ptr); } @@ -115,7 +143,7 @@ void *ares_malloc_data(ares_datatype type) { struct ares_data *ptr; - ptr = malloc(sizeof(struct ares_data)); + ptr = ares_malloc(sizeof(struct ares_data)); if (!ptr) return NULL; @@ -135,10 +163,14 @@ void *ares_malloc_data(ares_datatype type) ptr->data.srv_reply.port = 0; break; + case ARES_DATATYPE_TXT_EXT: + ptr->data.txt_ext.record_start = 0; + /* FALLTHROUGH */ + case ARES_DATATYPE_TXT_REPLY: ptr->data.txt_reply.next = NULL; ptr->data.txt_reply.txt = NULL; - ptr->data.txt_reply.length = 0; + ptr->data.txt_reply.length = 0; break; case ARES_DATATYPE_ADDR_NODE: @@ -148,8 +180,37 @@ void *ares_malloc_data(ares_datatype type) sizeof(ptr->data.addr_node.addrV6)); break; + case ARES_DATATYPE_ADDR_PORT_NODE: + ptr->data.addr_port_node.next = NULL; + ptr->data.addr_port_node.family = 0; + ptr->data.addr_port_node.udp_port = 0; + ptr->data.addr_port_node.tcp_port = 0; + memset(&ptr->data.addr_port_node.addrV6, 0, + sizeof(ptr->data.addr_port_node.addrV6)); + break; + + case ARES_DATATYPE_NAPTR_REPLY: + ptr->data.naptr_reply.next = NULL; + ptr->data.naptr_reply.flags = NULL; + ptr->data.naptr_reply.service = NULL; + ptr->data.naptr_reply.regexp = NULL; + ptr->data.naptr_reply.replacement = NULL; + ptr->data.naptr_reply.order = 0; + ptr->data.naptr_reply.preference = 0; + break; + + case ARES_DATATYPE_SOA_REPLY: + ptr->data.soa_reply.nsname = NULL; + ptr->data.soa_reply.hostmaster = NULL; + ptr->data.soa_reply.serial = 0; + ptr->data.soa_reply.refresh = 0; + ptr->data.soa_reply.retry = 0; + ptr->data.soa_reply.expire = 0; + ptr->data.soa_reply.minttl = 0; + break; + default: - free(ptr); + ares_free(ptr); return NULL; } @@ -158,33 +219,3 @@ void *ares_malloc_data(ares_datatype type) return &ptr->data; } - - -/* -** ares_get_datatype() - c-ares internal helper function. -** -** This function returns the ares_datatype of the data stored in a -** private ares_data struct when given the public API pointer. -*/ - -ares_datatype ares_get_datatype(void * dataptr) -{ - struct ares_data *ptr; - -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:1684) - /* 1684: conversion from pointer to same-sized integral type */ -#endif - - ptr = (void *)((char *)dataptr - offsetof(struct ares_data, data)); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif - - if (ptr->mark == ARES_DATATYPE_MARK) - return ptr->type; - - return ARES_DATATYPE_UNKNOWN; -} diff --git a/ares_data.h b/ares_data.h index de1608b..ffee2be 100644 --- a/ares_data.h +++ b/ares_data.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2009-2010 by Daniel Stenberg +/* Copyright (C) 2009-2013 by Daniel Stenberg * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -18,14 +18,18 @@ typedef enum { ARES_DATATYPE_UNKNOWN = 1, /* unknown data type - introduced in 1.7.0 */ ARES_DATATYPE_SRV_REPLY, /* struct ares_srv_reply - introduced in 1.7.0 */ ARES_DATATYPE_TXT_REPLY, /* struct ares_txt_reply - introduced in 1.7.0 */ + ARES_DATATYPE_TXT_EXT, /* struct ares_txt_ext - introduced in 1.11.0 */ ARES_DATATYPE_ADDR_NODE, /* struct ares_addr_node - introduced in 1.7.1 */ ARES_DATATYPE_MX_REPLY, /* struct ares_mx_reply - introduced in 1.7.2 */ + ARES_DATATYPE_NAPTR_REPLY,/* struct ares_naptr_reply - introduced in 1.7.6 */ + ARES_DATATYPE_SOA_REPLY, /* struct ares_soa_reply - introduced in 1.9.0 */ #if 0 ARES_DATATYPE_ADDR6TTL, /* struct ares_addrttl */ ARES_DATATYPE_ADDRTTL, /* struct ares_addr6ttl */ ARES_DATATYPE_HOSTENT, /* struct hostent */ ARES_DATATYPE_OPTIONS, /* struct ares_options */ #endif + ARES_DATATYPE_ADDR_PORT_NODE, /* struct ares_addr_port_node - introduced in 1.11.0 */ ARES_DATATYPE_LAST /* not used - introduced in 1.7.0 */ } ares_datatype; @@ -53,13 +57,16 @@ struct ares_data { ares_datatype type; /* Actual data type identifier. */ unsigned int mark; /* Private ares_data signature. */ union { - struct ares_txt_reply txt_reply; - struct ares_srv_reply srv_reply; - struct ares_addr_node addr_node; - struct ares_mx_reply mx_reply; + struct ares_txt_reply txt_reply; + struct ares_txt_ext txt_ext; + struct ares_srv_reply srv_reply; + struct ares_addr_node addr_node; + struct ares_addr_port_node addr_port_node; + struct ares_mx_reply mx_reply; + struct ares_naptr_reply naptr_reply; + struct ares_soa_reply soa_reply; } data; }; void *ares_malloc_data(ares_datatype type); -ares_datatype ares_get_datatype(void * dataptr); diff --git a/ares_destroy.c b/ares_destroy.c index 5e274da..8aa4223 100644 --- a/ares_destroy.c +++ b/ares_destroy.c @@ -1,6 +1,6 @@ /* Copyright 1998 by the Massachusetts Institute of Technology. - * Copyright (C) 2004-2010 by Daniel Stenberg + * Copyright (C) 2004-2011 by Daniel Stenberg * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -16,8 +16,9 @@ */ #include "ares_setup.h" + #include <assert.h> -#include <stdlib.h> + #include "ares.h" #include "ares_private.h" @@ -26,13 +27,15 @@ void ares_destroy_options(struct ares_options *options) int i; if(options->servers) - free(options->servers); + ares_free(options->servers); for (i = 0; i < options->ndomains; i++) - free(options->domains[i]); - free(options->domains); + ares_free(options->domains[i]); + if(options->domains) + ares_free(options->domains); if(options->sortlist) - free(options->sortlist); - free(options->lookups); + ares_free(options->sortlist); + if(options->lookups) + ares_free(options->lookups); } void ares_destroy(ares_channel channel) @@ -72,17 +75,17 @@ void ares_destroy(ares_channel channel) if (channel->domains) { for (i = 0; i < channel->ndomains; i++) - free(channel->domains[i]); - free(channel->domains); + ares_free(channel->domains[i]); + ares_free(channel->domains); } if(channel->sortlist) - free(channel->sortlist); + ares_free(channel->sortlist); if (channel->lookups) - free(channel->lookups); + ares_free(channel->lookups); - free(channel); + ares_free(channel); } void ares__destroy_servers_state(ares_channel channel) @@ -98,7 +101,7 @@ void ares__destroy_servers_state(ares_channel channel) ares__close_sockets(channel, server); assert(ares__is_list_empty(&server->queries_to_server)); } - free(channel->servers); + ares_free(channel->servers); channel->servers = NULL; } channel->nservers = -1; @@ -1,5 +1,7 @@ +#ifndef HEADER_CARES_DNS_H +#define HEADER_CARES_DNS_H -/* Copyright 1998 by the Massachusetts Institute of Technology. +/* Copyright 1998, 2011 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -14,12 +16,23 @@ * without express or implied warranty. */ -#ifndef ARES__DNS_H -#define ARES__DNS_H +/* + * Macro DNS__16BIT reads a network short (16 bit) given in network + * byte order, and returns its value as an unsigned short. + */ +#define DNS__16BIT(p) ((unsigned short)((unsigned int) 0xffff & \ + (((unsigned int)((unsigned char)(p)[0]) << 8U) | \ + ((unsigned int)((unsigned char)(p)[1]))))) -#define DNS__16BIT(p) (((p)[0] << 8) | (p)[1]) -#define DNS__32BIT(p) (((p)[0] << 24) | ((p)[1] << 16) | \ - ((p)[2] << 8) | (p)[3]) +/* + * Macro DNS__32BIT reads a network long (32 bit) given in network + * byte order, and returns its value as an unsigned int. + */ +#define DNS__32BIT(p) ((unsigned int) \ + (((unsigned int)((unsigned char)(p)[0]) << 24U) | \ + ((unsigned int)((unsigned char)(p)[1]) << 16U) | \ + ((unsigned int)((unsigned char)(p)[2]) << 8U) | \ + ((unsigned int)((unsigned char)(p)[3])))) #define DNS__SET16BIT(p, v) (((p)[0] = (unsigned char)(((v) >> 8) & 0xff)), \ ((p)[1] = (unsigned char)((v) & 0xff))) @@ -82,9 +95,9 @@ #define DNS_RR_LEN(r) DNS__16BIT((r) + 8) /* Macros for constructing the fixed part of a DNS resource record */ -#define DNS_RR_SET_TYPE(r) DNS__SET16BIT(r, v) -#define DNS_RR_SET_CLASS(r) DNS__SET16BIT((r) + 2, v) -#define DNS_RR_SET_TTL(r) DNS__SET32BIT((r) + 4, v) -#define DNS_RR_SET_LEN(r) DNS__SET16BIT((r) + 8, v) +#define DNS_RR_SET_TYPE(r, v) DNS__SET16BIT(r, v) +#define DNS_RR_SET_CLASS(r, v) DNS__SET16BIT((r) + 2, v) +#define DNS_RR_SET_TTL(r, v) DNS__SET32BIT((r) + 4, v) +#define DNS_RR_SET_LEN(r, v) DNS__SET16BIT((r) + 8, v) -#endif /* ARES__DNS_H */ +#endif /* HEADER_CARES_DNS_H */ diff --git a/ares_expand_name.c b/ares_expand_name.c index e3eccd2..3a38e67 100644 --- a/ares_expand_name.c +++ b/ares_expand_name.c @@ -1,5 +1,5 @@ -/* Copyright 1998 by the Massachusetts Institute of Technology. +/* Copyright 1998, 2011 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -16,9 +16,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -31,10 +28,13 @@ # include <arpa/nameser_compat.h> #endif -#include <stdlib.h> #include "ares.h" +#include "ares_nowarn.h" #include "ares_private.h" /* for the memdebug */ +/* Maximum number of indirections allowed for a name */ +#define MAX_INDIRS 50 + static int name_length(const unsigned char *encoded, const unsigned char *abuf, int alen); @@ -69,7 +69,7 @@ int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf, char *q; const unsigned char *p; union { - ssize_t sig; + ares_ssize_t sig; size_t uns; } nlen; @@ -77,7 +77,7 @@ int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf, if (nlen.sig < 0) return ARES_EBADNAME; - *s = malloc(nlen.uns + 1); + *s = ares_malloc(nlen.uns + 1); if (!*s) return ARES_ENOMEM; q = *s; @@ -91,9 +91,9 @@ int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf, /* indirect root label (like 0xc0 0x0c) is 2 bytes long (stupid, but valid) */ if ((*encoded & INDIR_MASK) == INDIR_MASK) - *enclen = 2; + *enclen = 2L; else - *enclen = 1; /* the caller should move one byte to get past this */ + *enclen = 1L; /* the caller should move one byte to get past this */ return ARES_SUCCESS; } @@ -106,7 +106,7 @@ int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf, { if (!indir) { - *enclen = p + 2 - encoded; + *enclen = aresx_uztosl(p + 2U - encoded); indir = 1; } p = abuf + ((*p & ~INDIR_MASK) << 8 | *(p + 1)); @@ -126,13 +126,13 @@ int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf, } } if (!indir) - *enclen = p + 1 - encoded; + *enclen = aresx_uztosl(p + 1U - encoded); /* Nuke the trailing period if we wrote one. */ if (q > *s) *(q - 1) = 0; else - *q = 0; /* zero terminate */ + *q = 0; /* zero terminate; LCOV_EXCL_LINE: empty names exit above */ return ARES_SUCCESS; } @@ -143,15 +143,16 @@ int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf, static int name_length(const unsigned char *encoded, const unsigned char *abuf, int alen) { - int n = 0, offset, indir = 0; + int n = 0, offset, indir = 0, top; /* Allow the caller to pass us abuf + alen and have us check for it. */ - if (encoded == abuf + alen) + if (encoded >= abuf + alen) return -1; while (*encoded) { - if ((*encoded & INDIR_MASK) == INDIR_MASK) + top = (*encoded & INDIR_MASK); + if (top == INDIR_MASK) { /* Check the offset and go there. */ if (encoded + 1 >= abuf + alen) @@ -164,10 +165,11 @@ static int name_length(const unsigned char *encoded, const unsigned char *abuf, /* If we've seen more indirects than the message length, * then there's a loop. */ - if (++indir > alen) + ++indir; + if (indir > alen || indir > MAX_INDIRS) return -1; } - else + else if (top == 0x00) { offset = *encoded; if (encoded + offset + 1 >= abuf + alen) @@ -180,6 +182,13 @@ static int name_length(const unsigned char *encoded, const unsigned char *abuf, } n++; } + else + { + /* RFC 1035 4.1.4 says other options (01, 10) for top 2 + * bits are reserved. + */ + return -1; + } } /* If there were any labels at all, then the number of dots is one diff --git a/ares_expand_string.c b/ares_expand_string.c index f24cccf..d35df75 100644 --- a/ares_expand_string.c +++ b/ares_expand_string.c @@ -16,9 +16,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -28,8 +25,6 @@ # include "nameser.h" #endif -#include <string.h> -#include <stdlib.h> #include "ares.h" #include "ares_private.h" /* for the memdebug */ @@ -46,7 +41,7 @@ int ares_expand_string(const unsigned char *encoded, { unsigned char *q; union { - ssize_t sig; + ares_ssize_t sig; size_t uns; } elen; @@ -59,7 +54,7 @@ int ares_expand_string(const unsigned char *encoded, encoded++; - *s = malloc(elen.uns+1); + *s = ares_malloc(elen.uns+1); if (*s == NULL) return ARES_ENOMEM; q = *s; @@ -16,10 +16,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif - #include "ares.h" #include "ares_nowarn.h" #include "ares_private.h" diff --git a/ares_free_hostent.c b/ares_free_hostent.c index 349d379..cfc5f81 100644 --- a/ares_free_hostent.c +++ b/ares_free_hostent.c @@ -15,7 +15,6 @@ */ #include "ares_setup.h" -#include <stdlib.h> #ifdef HAVE_NETDB_H #include <netdb.h> @@ -31,12 +30,12 @@ void ares_free_hostent(struct hostent *host) if (!host) return; - free((char *)(host->h_name)); + ares_free((char *)(host->h_name)); for (p = host->h_aliases; *p; p++) - free(*p); - free(host->h_aliases); - free(host->h_addr_list[0]); /* no matter if there is one or many entries, + ares_free(*p); + ares_free(host->h_aliases); + ares_free(host->h_addr_list[0]); /* no matter if there is one or many entries, there is only one malloc for all of them */ - free(host->h_addr_list); - free(host); + ares_free(host->h_addr_list); + ares_free(host); } diff --git a/ares_free_string.c b/ares_free_string.c index e0545c1..024992e 100644 --- a/ares_free_string.c +++ b/ares_free_string.c @@ -15,11 +15,11 @@ */ #include "ares_setup.h" -#include <stdlib.h> + #include "ares.h" #include "ares_private.h" void ares_free_string(void *str) { - free(str); + ares_free(str); } diff --git a/ares_gethostbyaddr.c b/ares_gethostbyaddr.c index 4b4c8a7..9258919 100644 --- a/ares_gethostbyaddr.c +++ b/ares_gethostbyaddr.c @@ -15,9 +15,6 @@ */ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -36,12 +33,8 @@ # include <arpa/nameser_compat.h> #endif -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - #include "ares.h" -#include "inet_net_pton.h" +#include "ares_inet_net_pton.h" #include "ares_platform.h" #include "ares_private.h" @@ -86,7 +79,7 @@ void ares_gethostbyaddr(ares_channel channel, const void *addr, int addrlen, return; } - aquery = malloc(sizeof(struct addr_query)); + aquery = ares_malloc(sizeof(struct addr_query)); if (!aquery) { callback(arg, ARES_ENOMEM, 0, NULL); @@ -176,7 +169,7 @@ static void end_aquery(struct addr_query *aquery, int status, aquery->callback(aquery->arg, status, aquery->timeouts, host); if (host) ares_free_hostent(host); - free(aquery); + ares_free(aquery); } static int file_lookup(struct ares_addr *addr, struct hostent **host) diff --git a/ares_gethostbyname.c b/ares_gethostbyname.c index ad89dc2..caab6ae 100644 --- a/ares_gethostbyname.c +++ b/ares_gethostbyname.c @@ -1,5 +1,5 @@ -/* Copyright 1998 by the Massachusetts Institute of Technology. +/* Copyright 1998, 2011, 2013 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -16,9 +16,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -37,18 +34,15 @@ # include <arpa/nameser_compat.h> #endif -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> #ifdef HAVE_STRINGS_H #include <strings.h> #endif #include "ares.h" -#include "inet_net_pton.h" +#include "ares_inet_net_pton.h" #include "bitncmp.h" #include "ares_platform.h" +#include "ares_nowarn.h" #include "ares_private.h" #ifdef WATT32 @@ -105,18 +99,18 @@ void ares_gethostbyname(ares_channel channel, const char *name, int family, return; /* Allocate and fill in the host query structure. */ - hquery = malloc(sizeof(struct host_query)); + hquery = ares_malloc(sizeof(struct host_query)); if (!hquery) { callback(arg, ARES_ENOMEM, 0, NULL); return; } hquery->channel = channel; - hquery->name = strdup(name); + hquery->name = ares_strdup(name); hquery->want_family = family; hquery->sent_family = -1; /* nothing is sent yet */ if (!hquery->name) { - free(hquery); + ares_free(hquery); callback(arg, ARES_ENOMEM, 0, NULL); return; } @@ -194,11 +188,14 @@ static void host_callback(void *arg, int status, int timeouts, else if (hquery->sent_family == AF_INET6) { status = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL); - if ((status == ARES_ENODATA || status == ARES_EBADRESP) && - hquery->want_family == AF_UNSPEC) { + if ((status == ARES_ENODATA || status == ARES_EBADRESP || + (status == ARES_SUCCESS && host && host->h_addr_list[0] == NULL)) && + hquery->want_family == AF_UNSPEC) { /* The query returned something but either there were no AAAA records (e.g. just CNAME) or the response was malformed. Try looking up A instead. */ + if (host) + ares_free_hostent(host); hquery->sent_family = AF_INET; ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback, hquery); @@ -230,8 +227,8 @@ static void end_hquery(struct host_query *hquery, int status, hquery->callback(hquery->arg, status, hquery->timeouts, host); if (host) ares_free_hostent(host); - free(hquery->name); - free(hquery); + ares_free(hquery->name); + ares_free(hquery); } /* If the name looks like an IP address, fake up a host entry, end the @@ -290,7 +287,7 @@ static int fake_hostent(const char *name, int family, addrs[0] = (char *)&in6; } /* Duplicate the name, to avoid a constness violation. */ - hostent.h_name = strdup(name); + hostent.h_name = ares_strdup(name); if (!hostent.h_name) { callback(arg, ARES_ENOMEM, 0, NULL); @@ -300,11 +297,11 @@ static int fake_hostent(const char *name, int family, /* Fill in the rest of the host structure and terminate the query. */ addrs[1] = NULL; hostent.h_aliases = aliases; - hostent.h_addrtype = family; + hostent.h_addrtype = aresx_sitoss(family); hostent.h_addr_list = addrs; callback(arg, ARES_SUCCESS, 0, &hostent); - free((char *)(hostent.h_name)); + ares_free((char *)(hostent.h_name)); return 1; } @@ -466,8 +463,8 @@ static int get_address_index(const struct in_addr *addr, } else { - if (!ares_bitncmp(&addr->s_addr, &sortlist[i].addrV4.s_addr, - sortlist[i].mask.bits)) + if (!ares__bitncmp(&addr->s_addr, &sortlist[i].addrV4.s_addr, + sortlist[i].mask.bits)) break; } } @@ -514,10 +511,8 @@ static int get6_address_index(const struct ares_in6_addr *addr, { if (sortlist[i].family != AF_INET6) continue; - if (!ares_bitncmp(addr, - &sortlist[i].addrV6, - sortlist[i].mask.bits)) - break; + if (!ares__bitncmp(addr, &sortlist[i].addrV6, sortlist[i].mask.bits)) + break; } return i; } diff --git a/ares_getnameinfo.c b/ares_getnameinfo.c index 82e261d..c77b1f8 100644 --- a/ares_getnameinfo.c +++ b/ares_getnameinfo.c @@ -22,9 +22,6 @@ # endif #endif -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -47,17 +44,8 @@ #include <net/if.h> #endif -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - #include "ares.h" #include "ares_ipv6.h" -#include "inet_ntop.h" #include "ares_nowarn.h" #include "ares_private.h" @@ -89,7 +77,7 @@ static char *lookup_service(unsigned short port, int flags, static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int scopeid, char *buf, size_t buflen); #endif -static char *ares_striendstr(const char *s1, const char *s2); +STATIC_TESTABLE char *ares_striendstr(const char *s1, const char *s2); void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa, ares_socklen_t salen, @@ -175,7 +163,7 @@ void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa, /* This is where a DNS lookup becomes necessary */ else { - niquery = malloc(sizeof(struct nameinfo_query)); + niquery = ares_malloc(sizeof(struct nameinfo_query)); if (!niquery) { callback(arg, ARES_ENOMEM, 0, NULL, NULL); @@ -188,7 +176,7 @@ void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa, if (sa->sa_family == AF_INET) { niquery->family = AF_INET; - memcpy(&niquery->addr.addr4, addr, sizeof(struct in_addr)); + memcpy(&niquery->addr.addr4, addr, sizeof(niquery->addr.addr4)); ares_gethostbyaddr(channel, &addr->sin_addr, sizeof(struct in_addr), AF_INET, nameinfo_callback, niquery); @@ -196,7 +184,7 @@ void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa, else { niquery->family = AF_INET6; - memcpy(&niquery->addr.addr6, addr6, sizeof(struct ares_in6_addr)); + memcpy(&niquery->addr.addr6, addr6, sizeof(niquery->addr.addr6)); ares_gethostbyaddr(channel, &addr6->sin6_addr, sizeof(struct ares_in6_addr), AF_INET6, nameinfo_callback, niquery); @@ -246,7 +234,7 @@ static void nameinfo_callback(void *arg, int status, int timeouts, niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, (char *)(host->h_name), service); - free(niquery); + ares_free(niquery); return; } /* We couldn't find the host, but it's OK, we can use the IP */ @@ -277,11 +265,11 @@ static void nameinfo_callback(void *arg, int status, int timeouts, } niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, ipbuf, service); - free(niquery); + ares_free(niquery); return; } niquery->callback(niquery->arg, status, niquery->timeouts, NULL, NULL); - free(niquery); + ares_free(niquery); } static char *lookup_service(unsigned short port, int flags, @@ -293,6 +281,8 @@ static char *lookup_service(unsigned short port, int flags, struct servent se; #endif char tmpbuf[4096]; + char *name; + size_t name_len; if (port) { @@ -309,12 +299,13 @@ static char *lookup_service(unsigned short port, int flags, else proto = "tcp"; #ifdef HAVE_GETSERVBYPORT_R + memset(&se, 0, sizeof(se)); sep = &se; memset(tmpbuf, 0, sizeof(tmpbuf)); #if GETSERVBYPORT_R_ARGS == 6 if (getservbyport_r(port, proto, &se, (void *)tmpbuf, sizeof(tmpbuf), &sep) != 0) - sep = NULL; + sep = NULL; /* LCOV_EXCL_LINE: buffer large so this never fails */ #elif GETSERVBYPORT_R_ARGS == 5 sep = getservbyport_r(port, proto, &se, (void *)tmpbuf, sizeof(tmpbuf)); @@ -335,17 +326,23 @@ static char *lookup_service(unsigned short port, int flags, #endif } if (sep && sep->s_name) - /* get service name */ - strcpy(tmpbuf, sep->s_name); + { + /* get service name */ + name = sep->s_name; + } else - /* get port as a string */ - sprintf(tmpbuf, "%u", (unsigned int)ntohs(port)); - if (strlen(tmpbuf) < buflen) + { + /* get port as a string */ + sprintf(tmpbuf, "%u", (unsigned int)ntohs(port)); + name = tmpbuf; + } + name_len = strlen(name); + if (name_len < buflen) /* return it if buffer big enough */ - strcpy(buf, tmpbuf); + memcpy(buf, name, name_len + 1); else /* avoid reusing previous one */ - buf[0] = '\0'; + buf[0] = '\0'; /* LCOV_EXCL_LINE: no real service names are too big */ return buf; } buf[0] = '\0'; @@ -359,12 +356,9 @@ static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags, #ifdef HAVE_IF_INDEXTONAME int is_ll, is_mcll; #endif - static const char fmt_u[] = "%u"; - static const char fmt_lu[] = "%lu"; char tmpbuf[IF_NAMESIZE + 2]; size_t bufl; - const char *fmt = (sizeof(addr6->sin6_scope_id) > sizeof(unsigned int))? - fmt_lu:fmt_u; + int is_scope_long = sizeof(addr6->sin6_scope_id) > sizeof(unsigned int); tmpbuf[0] = '%'; @@ -374,15 +368,38 @@ static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags, if ((flags & ARES_NI_NUMERICSCOPE) || (!is_ll && !is_mcll)) { - sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id); + if (is_scope_long) + { + sprintf(&tmpbuf[1], "%lu", (unsigned long)addr6->sin6_scope_id); + } + else + { + sprintf(&tmpbuf[1], "%u", (unsigned int)addr6->sin6_scope_id); + } } else { if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL) - sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id); + { + if (is_scope_long) + { + sprintf(&tmpbuf[1], "%lu", (unsigned long)addr6->sin6_scope_id); + } + else + { + sprintf(&tmpbuf[1], "%u", (unsigned int)addr6->sin6_scope_id); + } + } } #else - sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id); + if (is_scope_long) + { + sprintf(&tmpbuf[1], "%lu", (unsigned long)addr6->sin6_scope_id); + } + else + { + sprintf(&tmpbuf[1], "%u", (unsigned int)addr6->sin6_scope_id); + } (void) flags; #endif tmpbuf[IF_NAMESIZE + 1] = '\0'; @@ -395,7 +412,7 @@ static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags, #endif /* Determines if s1 ends with the string in s2 (case-insensitive) */ -static char *ares_striendstr(const char *s1, const char *s2) +STATIC_TESTABLE char *ares_striendstr(const char *s1, const char *s2) { const char *c1, *c2, *c1_begin; int lo1, lo2; @@ -421,7 +438,5 @@ static char *ares_striendstr(const char *s1, const char *s2) c2++; } } - if (c2 == c1 && c2 == NULL) - return (char *)c1_begin; - return NULL; + return (char *)c1_begin; } diff --git a/ares_getsock.c b/ares_getsock.c index 72e467f..22d3446 100644 --- a/ares_getsock.c +++ b/ares_getsock.c @@ -14,10 +14,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif - #include "ares.h" #include "ares_private.h" @@ -34,9 +30,7 @@ int ares_getsock(ares_channel channel, /* Are there any active queries? */ int active_queries = !ares__is_list_empty(&(channel->all_queries)); - for (i = 0; - (i < channel->nservers) && (sockindex < ARES_GETSOCK_MAXNUM); - i++) + for (i = 0; i < channel->nservers; i++) { server = &channel->servers[i]; /* We only need to register interest in UDP sockets if we have @@ -44,7 +38,7 @@ int ares_getsock(ares_channel channel, */ if (active_queries && server->udp_socket != ARES_SOCKET_BAD) { - if(sockindex >= numsocks) + if(sockindex >= numsocks || sockindex >= ARES_GETSOCK_MAXNUM) break; socks[sockindex] = server->udp_socket; bitmap |= ARES_GETSOCK_READABLE(setbits, sockindex); @@ -56,7 +50,7 @@ int ares_getsock(ares_channel channel, */ if (server->tcp_socket != ARES_SOCKET_BAD) { - if(sockindex >= numsocks) + if(sockindex >= numsocks || sockindex >= ARES_GETSOCK_MAXNUM) break; socks[sockindex] = server->tcp_socket; bitmap |= ARES_GETSOCK_READABLE(setbits, sockindex); diff --git a/ares_inet_net_pton.h b/ares_inet_net_pton.h new file mode 100644 index 0000000..90da2cc --- /dev/null +++ b/ares_inet_net_pton.h @@ -0,0 +1,25 @@ +#ifndef HEADER_CARES_INET_NET_PTON_H +#define HEADER_CARES_INET_NET_PTON_H + +/* Copyright (C) 2005-2013 by Daniel Stenberg et al + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifdef HAVE_INET_NET_PTON +#define ares_inet_net_pton(w,x,y,z) inet_net_pton(w,x,y,z) +#else +int ares_inet_net_pton(int af, const char *src, void *dst, size_t size); +#endif + +#endif /* HEADER_CARES_INET_NET_PTON_H */ diff --git a/ares_init.c b/ares_init.c index 44d4cf9..f7b700b 100644 --- a/ares_init.c +++ b/ares_init.c @@ -1,6 +1,6 @@ /* Copyright 1998 by the Massachusetts Institute of Technology. - * Copyright (C) 2007-2011 by Daniel Stenberg + * Copyright (C) 2007-2013 by Daniel Stenberg * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -21,14 +21,6 @@ #include <sys/param.h> #endif -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif - -#ifdef HAVE_SYS_SOCKET_H -#include <sys/socket.h> -#endif - #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -50,33 +42,30 @@ # include <arpa/nameser_compat.h> #endif -#ifdef HAVE_UNISTD_H -#include <unistd.h> +#if defined(ANDROID) || defined(__ANDROID__) +#include <sys/system_properties.h> +/* From the Bionic sources */ +#define DNS_PROP_NAME_PREFIX "net.dns" +#define MAX_DNS_PROPERTIES 8 #endif -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <time.h> - -#ifdef ANDROID -#include <sys/system_properties.h> +#if defined(CARES_USE_LIBRESOLV) +#include <resolv.h> #endif #include "ares.h" -#include "inet_net_pton.h" +#include "ares_inet_net_pton.h" #include "ares_library_init.h" #include "ares_nowarn.h" #include "ares_platform.h" -#include "inet_ntop.h" #include "ares_private.h" #ifdef WATT32 #undef WIN32 /* Redefined in MingW/MSVC headers */ #endif -static int init_by_options(ares_channel channel, const struct ares_options *options, +static int init_by_options(ares_channel channel, + const struct ares_options *options, int optmask); static int init_by_environment(ares_channel channel); static int init_by_resolv_conf(ares_channel channel); @@ -91,15 +80,18 @@ static int set_options(ares_channel channel, const char *str); static const char *try_option(const char *p, const char *q, const char *opt); static int init_id_key(rc4_key* key,int key_data_len); -#if !defined(WIN32) && !defined(WATT32) -static int sortlist_alloc(struct apattern **sortlist, int *nsort, struct apattern *pat); -static int ip_addr(const char *s, ssize_t len, struct in_addr *addr); +static int config_sortlist(struct apattern **sortlist, int *nsort, + const char *str); +static int sortlist_alloc(struct apattern **sortlist, int *nsort, + struct apattern *pat); +static int ip_addr(const char *s, ares_ssize_t len, struct in_addr *addr); static void natural_mask(struct apattern *pat); +#if !defined(WIN32) && !defined(WATT32) && \ + !defined(ANDROID) && !defined(__ANDROID__) && !defined(CARES_USE_LIBRESOLV) static int config_domain(ares_channel channel, char *str); static int config_lookup(ares_channel channel, const char *str, - const char *bindch, const char *filech); -static int config_sortlist(struct apattern **sortlist, int *nsort, - const char *str); + const char *bindch, const char *altbindch, + const char *filech); static char *try_config(char *s, const char *opt, char scc); #endif @@ -137,9 +129,9 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options, #endif if (ares_library_initialized() != ARES_SUCCESS) - return ARES_ENOTINITIALIZED; + return ARES_ENOTINITIALIZED; /* LCOV_EXCL_LINE: n/a on non-WinSock */ - channel = malloc(sizeof(struct ares_channeldata)); + channel = ares_malloc(sizeof(struct ares_channeldata)); if (!channel) { *channelptr = NULL; return ARES_ENOMEM; @@ -157,6 +149,7 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options, channel->rotate = -1; channel->udp_port = -1; channel->tcp_port = -1; + channel->ednspsz = -1; channel->socket_send_buffer_size = -1; channel->socket_receive_buffer_size = -1; channel->nservers = -1; @@ -171,6 +164,10 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options, channel->sock_state_cb_data = NULL; channel->sock_create_cb = NULL; channel->sock_create_cb_data = NULL; + channel->sock_config_cb = NULL; + channel->sock_config_cb_data = NULL; + channel->sock_funcs = NULL; + channel->sock_func_cb_data = NULL; channel->last_server = 0; channel->last_timeout_processed = (time_t)now.tv_sec; @@ -194,18 +191,17 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options, * precedence to lowest. */ - if (status == ARES_SUCCESS) { - status = init_by_options(channel, options, optmask); - if (status != ARES_SUCCESS) - DEBUGF(fprintf(stderr, "Error: init_by_options failed: %s\n", - ares_strerror(status))); - } - if (status == ARES_SUCCESS) { - status = init_by_environment(channel); - if (status != ARES_SUCCESS) - DEBUGF(fprintf(stderr, "Error: init_by_environment failed: %s\n", - ares_strerror(status))); + status = init_by_options(channel, options, optmask); + if (status != ARES_SUCCESS) { + DEBUGF(fprintf(stderr, "Error: init_by_options failed: %s\n", + ares_strerror(status))); + /* If we fail to apply user-specified options, fail the whole init process */ + goto done; } + status = init_by_environment(channel); + if (status != ARES_SUCCESS) + DEBUGF(fprintf(stderr, "Error: init_by_environment failed: %s\n", + ares_strerror(status))); if (status == ARES_SUCCESS) { status = init_by_resolv_conf(channel); if (status != ARES_SUCCESS) @@ -233,22 +229,23 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options, ares_strerror(status))); } +done: if (status != ARES_SUCCESS) { /* Something failed; clean up memory we may have allocated. */ if (channel->servers) - free(channel->servers); + ares_free(channel->servers); if (channel->domains) { for (i = 0; i < channel->ndomains; i++) - free(channel->domains[i]); - free(channel->domains); + ares_free(channel->domains[i]); + ares_free(channel->domains); } if (channel->sortlist) - free(channel->sortlist); + ares_free(channel->sortlist); if(channel->lookups) - free(channel->lookups); - free(channel); + ares_free(channel->lookups); + ares_free(channel); return status; } @@ -267,8 +264,8 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options, int ares_dup(ares_channel *dest, ares_channel src) { struct ares_options opts; - struct ares_addr_node *servers; - int ipv6_nservers = 0; + struct ares_addr_port_node *servers; + int non_v4_default_port = 0; int i, rc; int optmask; @@ -278,7 +275,10 @@ int ares_dup(ares_channel *dest, ares_channel src) which is most of them */ rc = ares_save_options(src, &opts, &optmask); if(rc) + { + ares_destroy_options(&opts); return rc; + } /* Then create the new channel with those options */ rc = ares_init_options(dest, &opts, optmask); @@ -292,27 +292,40 @@ int ares_dup(ares_channel *dest, ares_channel src) /* Now clone the options that ares_save_options() doesn't support. */ (*dest)->sock_create_cb = src->sock_create_cb; (*dest)->sock_create_cb_data = src->sock_create_cb_data; + (*dest)->sock_config_cb = src->sock_config_cb; + (*dest)->sock_config_cb_data = src->sock_config_cb_data; + (*dest)->sock_funcs = src->sock_funcs; + (*dest)->sock_func_cb_data = src->sock_func_cb_data; - strncpy((*dest)->local_dev_name, src->local_dev_name, sizeof(src->local_dev_name)); + strncpy((*dest)->local_dev_name, src->local_dev_name, + sizeof(src->local_dev_name)); (*dest)->local_ip4 = src->local_ip4; memcpy((*dest)->local_ip6, src->local_ip6, sizeof(src->local_ip6)); - /* Full name server cloning required when not all are IPv4 */ + /* Full name server cloning required if there is a non-IPv4, or non-default port, nameserver */ for (i = 0; i < src->nservers; i++) { - if (src->servers[i].addr.family != AF_INET) { - ipv6_nservers++; + if ((src->servers[i].addr.family != AF_INET) || + (src->servers[i].addr.udp_port != 0) || + (src->servers[i].addr.tcp_port != 0)) { + non_v4_default_port++; break; } } - if (ipv6_nservers) { - rc = ares_get_servers(src, &servers); - if (rc != ARES_SUCCESS) + if (non_v4_default_port) { + rc = ares_get_servers_ports(src, &servers); + if (rc != ARES_SUCCESS) { + ares_destroy(*dest); + *dest = NULL; return rc; - rc = ares_set_servers(*dest, servers); + } + rc = ares_set_servers_ports(*dest, servers); ares_free_data(servers); - if (rc != ARES_SUCCESS) + if (rc != ARES_SUCCESS) { + ares_destroy(*dest); + *dest = NULL; return rc; + } } return ARES_SUCCESS; /* everything went fine */ @@ -337,8 +350,8 @@ int ares_save_options(ares_channel channel, struct ares_options *options, (*optmask) = (ARES_OPT_FLAGS|ARES_OPT_TRIES|ARES_OPT_NDOTS| ARES_OPT_UDP_PORT|ARES_OPT_TCP_PORT|ARES_OPT_SOCK_STATE_CB| ARES_OPT_SERVERS|ARES_OPT_DOMAINS|ARES_OPT_LOOKUPS| - ARES_OPT_SORTLIST|ARES_OPT_TIMEOUTMS) | - (channel->optmask & ARES_OPT_ROTATE); + ARES_OPT_SORTLIST|ARES_OPT_TIMEOUTMS); + (*optmask) |= (channel->rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE); /* Copy easy stuff */ options->flags = channel->flags; @@ -348,25 +361,29 @@ int ares_save_options(ares_channel channel, struct ares_options *options, options->timeout = channel->timeout; options->tries = channel->tries; options->ndots = channel->ndots; - options->udp_port = (unsigned short)channel->udp_port; - options->tcp_port = (unsigned short)channel->tcp_port; + options->udp_port = ntohs(aresx_sitous(channel->udp_port)); + options->tcp_port = ntohs(aresx_sitous(channel->tcp_port)); options->sock_state_cb = channel->sock_state_cb; options->sock_state_cb_data = channel->sock_state_cb_data; - /* Copy IPv4 servers */ + /* Copy IPv4 servers that use the default port */ if (channel->nservers) { for (i = 0; i < channel->nservers; i++) { - if (channel->servers[i].addr.family == AF_INET) + if ((channel->servers[i].addr.family == AF_INET) && + (channel->servers[i].addr.udp_port == 0) && + (channel->servers[i].addr.tcp_port == 0)) ipv4_nservers++; } if (ipv4_nservers) { - options->servers = malloc(ipv4_nservers * sizeof(struct in_addr)); + options->servers = ares_malloc(ipv4_nservers * sizeof(struct in_addr)); if (!options->servers) return ARES_ENOMEM; for (i = j = 0; i < channel->nservers; i++) { - if (channel->servers[i].addr.family == AF_INET) + if ((channel->servers[i].addr.family == AF_INET) && + (channel->servers[i].addr.udp_port == 0) && + (channel->servers[i].addr.tcp_port == 0)) memcpy(&options->servers[j++], &channel->servers[i].addr.addrV4, sizeof(channel->servers[i].addr.addrV4)); @@ -377,14 +394,14 @@ int ares_save_options(ares_channel channel, struct ares_options *options, /* copy domains */ if (channel->ndomains) { - options->domains = malloc(channel->ndomains * sizeof(char *)); + options->domains = ares_malloc(channel->ndomains * sizeof(char *)); if (!options->domains) return ARES_ENOMEM; for (i = 0; i < channel->ndomains; i++) { options->ndomains = i; - options->domains[i] = strdup(channel->domains[i]); + options->domains[i] = ares_strdup(channel->domains[i]); if (!options->domains[i]) return ARES_ENOMEM; } @@ -393,14 +410,14 @@ int ares_save_options(ares_channel channel, struct ares_options *options, /* copy lookups */ if (channel->lookups) { - options->lookups = strdup(channel->lookups); + options->lookups = ares_strdup(channel->lookups); if (!options->lookups && channel->lookups) return ARES_ENOMEM; } /* copy sortlist */ if (channel->nsort) { - options->sortlist = malloc(channel->nsort * sizeof(struct apattern)); + options->sortlist = ares_malloc(channel->nsort * sizeof(struct apattern)); if (!options->sortlist) return ARES_ENOMEM; for (i = 0; i < channel->nsort; i++) @@ -430,10 +447,12 @@ static int init_by_options(ares_channel channel, channel->ndots = options->ndots; if ((optmask & ARES_OPT_ROTATE) && channel->rotate == -1) channel->rotate = 1; + if ((optmask & ARES_OPT_NOROTATE) && channel->rotate == -1) + channel->rotate = 0; if ((optmask & ARES_OPT_UDP_PORT) && channel->udp_port == -1) - channel->udp_port = options->udp_port; + channel->udp_port = htons(options->udp_port); if ((optmask & ARES_OPT_TCP_PORT) && channel->tcp_port == -1) - channel->tcp_port = options->tcp_port; + channel->tcp_port = htons(options->tcp_port); if ((optmask & ARES_OPT_SOCK_STATE_CB) && channel->sock_state_cb == NULL) { channel->sock_state_cb = options->sock_state_cb; @@ -446,6 +465,9 @@ static int init_by_options(ares_channel channel, && channel->socket_receive_buffer_size == -1) channel->socket_receive_buffer_size = options->socket_receive_buffer_size; + if ((optmask & ARES_OPT_EDNSPSZ) && channel->ednspsz == -1) + channel->ednspsz = options->ednspsz; + /* Copy the IPv4 servers, if given. */ if ((optmask & ARES_OPT_SERVERS) && channel->nservers == -1) { @@ -453,12 +475,14 @@ static int init_by_options(ares_channel channel, if (options->nservers > 0) { channel->servers = - malloc(options->nservers * sizeof(struct server_state)); + ares_malloc(options->nservers * sizeof(struct server_state)); if (!channel->servers) return ARES_ENOMEM; for (i = 0; i < options->nservers; i++) { channel->servers[i].addr.family = AF_INET; + channel->servers[i].addr.udp_port = 0; + channel->servers[i].addr.tcp_port = 0; memcpy(&channel->servers[i].addr.addrV4, &options->servers[i], sizeof(channel->servers[i].addr.addrV4)); @@ -475,13 +499,13 @@ static int init_by_options(ares_channel channel, /* Avoid zero size allocations at any cost */ if (options->ndomains > 0) { - channel->domains = malloc(options->ndomains * sizeof(char *)); + channel->domains = ares_malloc(options->ndomains * sizeof(char *)); if (!channel->domains) return ARES_ENOMEM; for (i = 0; i < options->ndomains; i++) { channel->ndomains = i; - channel->domains[i] = strdup(options->domains[i]); + channel->domains[i] = ares_strdup(options->domains[i]); if (!channel->domains[i]) return ARES_ENOMEM; } @@ -492,19 +516,20 @@ static int init_by_options(ares_channel channel, /* Set lookups, if given. */ if ((optmask & ARES_OPT_LOOKUPS) && !channel->lookups) { - channel->lookups = strdup(options->lookups); + channel->lookups = ares_strdup(options->lookups); if (!channel->lookups) return ARES_ENOMEM; } /* copy sortlist */ - if ((optmask & ARES_OPT_SORTLIST) && (channel->nsort == -1) && - (options->nsort>0)) { - channel->sortlist = malloc(options->nsort * sizeof(struct apattern)); - if (!channel->sortlist) - return ARES_ENOMEM; - for (i = 0; i < options->nsort; i++) - channel->sortlist[i] = options->sortlist[i]; + if ((optmask & ARES_OPT_SORTLIST) && (channel->nsort == -1)) { + if (options->nsort > 0) { + channel->sortlist = ares_malloc(options->nsort * sizeof(struct apattern)); + if (!channel->sortlist) + return ARES_ENOMEM; + for (i = 0; i < options->nsort; i++) + channel->sortlist[i] = options->sortlist[i]; + } channel->nsort = options->nsort; } @@ -531,7 +556,7 @@ static int init_by_environment(ares_channel channel) { status = set_options(channel, res_options); if (status != ARES_SUCCESS) - return status; + return status; /* LCOV_EXCL_LINE: set_options() never fails */ } return ARES_SUCCESS; @@ -539,255 +564,891 @@ static int init_by_environment(ares_channel channel) #ifdef WIN32 /* - * Warning: returns a dynamically allocated buffer, the user MUST - * use free() if the function returns 1 + * get_REG_SZ() + * + * Given a 'hKey' handle to an open registry key and a 'leafKeyName' pointer + * to the name of the registry leaf key to be queried, fetch it's string + * value and return a pointer in *outptr to a newly allocated memory area + * holding it as a null-terminated string. + * + * Returns 0 and nullifies *outptr upon inability to return a string value. + * + * Returns 1 and sets *outptr when returning a dynamically allocated string. + * + * Supported on Windows NT 3.5 and newer. */ -static int get_res_nt(HKEY hKey, const char *subkey, char **obuf) +static int get_REG_SZ(HKEY hKey, const char *leafKeyName, char **outptr) { - /* Test for the size we need */ DWORD size = 0; - int result; + int res; - result = RegQueryValueEx(hKey, subkey, 0, NULL, NULL, &size); - if ((result != ERROR_SUCCESS && result != ERROR_MORE_DATA) || !size) + *outptr = NULL; + + /* Find out size of string stored in registry */ + res = RegQueryValueEx(hKey, leafKeyName, 0, NULL, NULL, &size); + if ((res != ERROR_SUCCESS && res != ERROR_MORE_DATA) || !size) return 0; - *obuf = malloc(size+1); - if (!*obuf) + + /* Allocate buffer of indicated size plus one given that string + might have been stored without null termination */ + *outptr = ares_malloc(size+1); + if (!*outptr) return 0; - if (RegQueryValueEx(hKey, subkey, 0, NULL, - (LPBYTE)*obuf, &size) != ERROR_SUCCESS) + /* Get the value for real */ + res = RegQueryValueEx(hKey, leafKeyName, 0, NULL, + (unsigned char *)*outptr, &size); + if ((res != ERROR_SUCCESS) || (size == 1)) { - free(*obuf); + ares_free(*outptr); + *outptr = NULL; return 0; } - if (size == 1) + + /* Null terminate buffer allways */ + *(*outptr + size) = '\0'; + + return 1; +} + +/* + * get_REG_SZ_9X() + * + * Functionally identical to get_REG_SZ() + * + * Supported on Windows 95, 98 and ME. + */ +static int get_REG_SZ_9X(HKEY hKey, const char *leafKeyName, char **outptr) +{ + DWORD dataType = 0; + DWORD size = 0; + int res; + + *outptr = NULL; + + /* Find out size of string stored in registry */ + res = RegQueryValueEx(hKey, leafKeyName, 0, &dataType, NULL, &size); + if ((res != ERROR_SUCCESS && res != ERROR_MORE_DATA) || !size) + return 0; + + /* Allocate buffer of indicated size plus one given that string + might have been stored without null termination */ + *outptr = ares_malloc(size+1); + if (!*outptr) + return 0; + + /* Get the value for real */ + res = RegQueryValueEx(hKey, leafKeyName, 0, &dataType, + (unsigned char *)*outptr, &size); + if ((res != ERROR_SUCCESS) || (size == 1)) { - free(*obuf); + ares_free(*outptr); + *outptr = NULL; return 0; } + + /* Null terminate buffer allways */ + *(*outptr + size) = '\0'; + return 1; } -static int get_res_interfaces_nt(HKEY hKey, const char *subkey, char **obuf) +/* + * get_enum_REG_SZ() + * + * Given a 'hKeyParent' handle to an open registry key and a 'leafKeyName' + * pointer to the name of the registry leaf key to be queried, parent key + * is enumerated searching in child keys for given leaf key name and its + * associated string value. When located, this returns a pointer in *outptr + * to a newly allocated memory area holding it as a null-terminated string. + * + * Returns 0 and nullifies *outptr upon inability to return a string value. + * + * Returns 1 and sets *outptr when returning a dynamically allocated string. + * + * Supported on Windows NT 3.5 and newer. + */ +static int get_enum_REG_SZ(HKEY hKeyParent, const char *leafKeyName, + char **outptr) { - char enumbuf[39]; /* GUIDs are 38 chars + 1 for NULL */ - DWORD enum_size = 39; - int idx = 0; - HKEY hVal; + char enumKeyName[256]; + DWORD enumKeyNameBuffSize; + DWORD enumKeyIdx = 0; + HKEY hKeyEnum; + int gotString; + int res; - while (RegEnumKeyEx(hKey, idx++, enumbuf, &enum_size, 0, - NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS) - { - int rc; + *outptr = NULL; - enum_size = 39; - if (RegOpenKeyEx(hKey, enumbuf, 0, KEY_QUERY_VALUE, &hVal) != - ERROR_SUCCESS) + for(;;) + { + enumKeyNameBuffSize = sizeof(enumKeyName); + res = RegEnumKeyEx(hKeyParent, enumKeyIdx++, enumKeyName, + &enumKeyNameBuffSize, 0, NULL, NULL, NULL); + if (res != ERROR_SUCCESS) + break; + res = RegOpenKeyEx(hKeyParent, enumKeyName, 0, KEY_QUERY_VALUE, + &hKeyEnum); + if (res != ERROR_SUCCESS) continue; - rc = get_res_nt(hVal, subkey, obuf); - RegCloseKey(hVal); - if (rc) - return 1; - } - return 0; + gotString = get_REG_SZ(hKeyEnum, leafKeyName, outptr); + RegCloseKey(hKeyEnum); + if (gotString) + break; + } + + if (!*outptr) + return 0; + + return 1; +} + +/* + * get_DNS_Registry_9X() + * + * Functionally identical to get_DNS_Registry() + * + * Implementation supports Windows 95, 98 and ME. + */ +static int get_DNS_Registry_9X(char **outptr) +{ + HKEY hKey_VxD_MStcp; + int gotString; + int res; + + *outptr = NULL; + + res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X, 0, KEY_READ, + &hKey_VxD_MStcp); + if (res != ERROR_SUCCESS) + return 0; + + gotString = get_REG_SZ_9X(hKey_VxD_MStcp, NAMESERVER, outptr); + RegCloseKey(hKey_VxD_MStcp); + + if (!gotString || !*outptr) + return 0; + + return 1; +} + +/* + * get_DNS_Registry_NT() + * + * Functionally identical to get_DNS_Registry() + * + * Refs: Microsoft Knowledge Base articles KB120642 and KB314053. + * + * Implementation supports Windows NT 3.5 and newer. + */ +static int get_DNS_Registry_NT(char **outptr) +{ + HKEY hKey_Interfaces = NULL; + HKEY hKey_Tcpip_Parameters; + int gotString; + int res; + + *outptr = NULL; + + res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, + &hKey_Tcpip_Parameters); + if (res != ERROR_SUCCESS) + return 0; + + /* + ** Global DNS settings override adapter specific parameters when both + ** are set. Additionally static DNS settings override DHCP-configured + ** parameters when both are set. + */ + + /* Global DNS static parameters */ + gotString = get_REG_SZ(hKey_Tcpip_Parameters, NAMESERVER, outptr); + if (gotString) + goto done; + + /* Global DNS DHCP-configured parameters */ + gotString = get_REG_SZ(hKey_Tcpip_Parameters, DHCPNAMESERVER, outptr); + if (gotString) + goto done; + + /* Try adapter specific parameters */ + res = RegOpenKeyEx(hKey_Tcpip_Parameters, "Interfaces", 0, + KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, + &hKey_Interfaces); + if (res != ERROR_SUCCESS) + { + hKey_Interfaces = NULL; + goto done; + } + + /* Adapter specific DNS static parameters */ + gotString = get_enum_REG_SZ(hKey_Interfaces, NAMESERVER, outptr); + if (gotString) + goto done; + + /* Adapter specific DNS DHCP-configured parameters */ + gotString = get_enum_REG_SZ(hKey_Interfaces, DHCPNAMESERVER, outptr); + +done: + if (hKey_Interfaces) + RegCloseKey(hKey_Interfaces); + + RegCloseKey(hKey_Tcpip_Parameters); + + if (!gotString || !*outptr) + return 0; + + return 1; +} + +/* + * get_DNS_Registry() + * + * Locates DNS info in the registry. When located, this returns a pointer + * in *outptr to a newly allocated memory area holding a null-terminated + * string with a space or comma seperated list of DNS IP addresses. + * + * Returns 0 and nullifies *outptr upon inability to return DNSes string. + * + * Returns 1 and sets *outptr when returning a dynamically allocated string. + */ +static int get_DNS_Registry(char **outptr) +{ + win_platform platform; + int gotString = 0; + + *outptr = NULL; + + platform = ares__getplatform(); + + if (platform == WIN_NT) + gotString = get_DNS_Registry_NT(outptr); + else if (platform == WIN_9X) + gotString = get_DNS_Registry_9X(outptr); + + if (!gotString) + return 0; + + return 1; } -/** - * The desired output for this method is that we set "ret_buf" to - * something like: +static void commanjoin(char** dst, const char* const src, const size_t len) +{ + char *newbuf; + size_t newsize; + + /* 1 for terminating 0 and 2 for , and terminating 0 */ + newsize = len + (*dst ? (strlen(*dst) + 2) : 1); + newbuf = ares_realloc(*dst, newsize); + if (!newbuf) + return; + if (*dst == NULL) + *newbuf = '\0'; + *dst = newbuf; + if (strlen(*dst) != 0) + strcat(*dst, ","); + strncat(*dst, src, len); +} + +/* + * commajoin() + * + * RTF code. + */ +static void commajoin(char **dst, const char *src) +{ + commanjoin(dst, src, strlen(src)); +} + +/* + * get_DNS_NetworkParams() + * + * Locates DNS info using GetNetworkParams() function from the Internet + * Protocol Helper (IP Helper) API. When located, this returns a pointer + * in *outptr to a newly allocated memory area holding a null-terminated + * string with a space or comma seperated list of DNS IP addresses. * - * 192.168.0.1,dns01.my.domain,fe80::200:f8ff:fe21:67cf + * Returns 0 and nullifies *outptr upon inability to return DNSes string. * - * The only ordering requirement is that primary servers are listed - * before secondary. There is no requirement that IPv4 addresses should - * necessarily be before IPv6. + * Returns 1 and sets *outptr when returning a dynamically allocated string. * - * Note that ret_size should ideally be big enough to hold around - * 2-3 IPv4 and 2-3 IPv6 addresses. + * Implementation supports Windows 98 and newer. * - * Finally, we need to return the total number of DNS servers located. + * Note: Ancient PSDK required in order to build a W98 target. */ -static int get_iphlpapi_dns_info (char *ret_buf, size_t ret_size) +static int get_DNS_NetworkParams(char **outptr) { - const size_t ipv4_size = INET_ADDRSTRLEN + 1; /* +1 for ',' at end */ - const size_t ipv6_size = INET6_ADDRSTRLEN + 12; /* +12 for "%0123456789," at end */ - size_t left = ret_size; - char *ret = ret_buf; - int count = 0; - - /* Use the GetAdaptersAddresses method if it's available, otherwise - fall back to GetNetworkParams. */ - if (ares_fpGetAdaptersAddresses != ZERO_NULL) + FIXED_INFO *fi, *newfi; + struct ares_addr namesrvr; + char *txtaddr; + IP_ADDR_STRING *ipAddr; + int res; + DWORD size = sizeof (*fi); + + *outptr = NULL; + + /* Verify run-time availability of GetNetworkParams() */ + if (ares_fpGetNetworkParams == ZERO_NULL) + return 0; + + fi = ares_malloc(size); + if (!fi) + return 0; + + res = (*ares_fpGetNetworkParams) (fi, &size); + if ((res != ERROR_BUFFER_OVERFLOW) && (res != ERROR_SUCCESS)) + goto done; + + newfi = ares_realloc(fi, size); + if (!newfi) + goto done; + + fi = newfi; + res = (*ares_fpGetNetworkParams) (fi, &size); + if (res != ERROR_SUCCESS) + goto done; + + for (ipAddr = &fi->DnsServerList; ipAddr; ipAddr = ipAddr->Next) { - const ULONG working_buf_size = 15000; - IP_ADAPTER_ADDRESSES *pFirstEntry = NULL; - IP_ADAPTER_ADDRESSES *pEntry = NULL; - ULONG bufSize = 0; - ULONG result = 0; - - /* According to MSDN, the recommended way to do this is to use a temporary - buffer of 15K, to "dramatically reduce the chance that the GetAdaptersAddresses - method returns ERROR_BUFFER_OVERFLOW" */ - pFirstEntry = ( IP_ADAPTER_ADDRESSES * ) malloc( working_buf_size ); - bufSize = working_buf_size; - if( !pFirstEntry ) - return 0; - - /* Call the method one time */ - result = ( *ares_fpGetAdaptersAddresses )( AF_UNSPEC, 0, 0, pFirstEntry, &bufSize ); - if( result == ERROR_BUFFER_OVERFLOW ) + txtaddr = &ipAddr->IpAddress.String[0]; + + /* Validate converting textual address to binary format. */ + if (ares_inet_pton(AF_INET, txtaddr, &namesrvr.addrV4) == 1) { - /* Reallocate, bufSize should now be set to the required size */ - pFirstEntry = ( IP_ADAPTER_ADDRESSES * ) realloc( pFirstEntry, bufSize ); - if( !pFirstEntry ) - return 0; - - /* Call the method a second time */ - result = ( *ares_fpGetAdaptersAddresses )( AF_UNSPEC, 0, 0, pFirstEntry, &bufSize ); - if( result == ERROR_BUFFER_OVERFLOW ) - { - /* Reallocate, bufSize should now be set to the required size */ - pFirstEntry = ( IP_ADAPTER_ADDRESSES * ) realloc( pFirstEntry, bufSize ); - if( !pFirstEntry ) - return 0; - - /* Call the method a third time. The maximum number of times we're going to do - this is 3. Three shall be the number thou shalt count, and the number of the - counting shall be three. Five is right out. */ - result = ( *ares_fpGetAdaptersAddresses )( AF_UNSPEC, 0, 0, pFirstEntry, &bufSize ); - } + if ((namesrvr.addrV4.S_un.S_addr == INADDR_ANY) || + (namesrvr.addrV4.S_un.S_addr == INADDR_NONE)) + continue; + } + else if (ares_inet_pton(AF_INET6, txtaddr, &namesrvr.addrV6) == 1) + { + if (memcmp(&namesrvr.addrV6, &ares_in6addr_any, + sizeof(namesrvr.addrV6)) == 0) + continue; } + else + continue; + + commajoin(outptr, txtaddr); + + if (!*outptr) + break; + } + +done: + if (fi) + ares_free(fi); + + if (!*outptr) + return 0; + + return 1; +} + +static BOOL ares_IsWindowsVistaOrGreater(void) +{ + OSVERSIONINFO vinfo; + memset(&vinfo, 0, sizeof(vinfo)); + vinfo.dwOSVersionInfoSize = sizeof(vinfo); + if (!GetVersionEx(&vinfo) || vinfo.dwMajorVersion < 6) + return FALSE; + return TRUE; +} - /* Check the current result for failure */ - if( result != ERROR_SUCCESS ) +/* A structure to hold the string form of IPv4 and IPv6 addresses so we can + * sort them by a metric. + */ +typedef struct +{ + /* The metric we sort them by. */ + ULONG metric; + + /* Room enough for the string form of any IPv4 or IPv6 address that + * ares_inet_ntop() will create. Based on the existing c-ares practice. + */ + char text[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; +} Address; + +/* Sort Address values \a left and \a right by metric, returning the usual + * indicators for qsort(). + */ +static int compareAddresses(const void *arg1, + const void *arg2) +{ + const Address * const left = arg1; + const Address * const right = arg2; + if(left->metric < right->metric) return -1; + if(left->metric > right->metric) return 1; + return 0; +} + +/* There can be multiple routes to "the Internet". And there can be different + * DNS servers associated with each of the interfaces that offer those routes. + * We have to assume that any DNS server can serve any request. But, some DNS + * servers may only respond if requested over their associated interface. But + * we also want to use "the preferred route to the Internet" whenever possible + * (and not use DNS servers on a non-preferred route even by forcing request + * to go out on the associated non-preferred interface). i.e. We want to use + * the DNS servers associated with the same interface that we would use to + * make a general request to anything else. + * + * But, Windows won't sort the DNS servers by the metrics associated with the + * routes and interfaces _even_ though it obviously sends IP packets based on + * those same routes and metrics. So, we must do it ourselves. + * + * So, we sort the DNS servers by the same metric values used to determine how + * an outgoing IP packet will go, thus effectively using the DNS servers + * associated with the interface that the DNS requests themselves will + * travel. This gives us optimal routing and avoids issues where DNS servers + * won't respond to requests that don't arrive via some specific subnetwork + * (and thus some specific interface). + * + * This function computes the metric we use to sort. On the interface + * identified by \a luid, it determines the best route to \a dest and combines + * that route's metric with \a interfaceMetric to compute a metric for the + * destination address on that interface. This metric can be used as a weight + * to sort the DNS server addresses associated with each interface (lower is + * better). + * + * Note that by restricting the route search to the specific interface with + * which the DNS servers are associated, this function asks the question "What + * is the metric for sending IP packets to this DNS server?" which allows us + * to sort the DNS servers correctly. + */ +static ULONG getBestRouteMetric(IF_LUID * const luid, /* Can't be const :( */ + const SOCKADDR_INET * const dest, + const ULONG interfaceMetric) +{ + /* On this interface, get the best route to that destination. */ + MIB_IPFORWARD_ROW2 row; + SOCKADDR_INET ignored; + if(!ares_fpGetBestRoute2 || + ares_fpGetBestRoute2(/* The interface to use. The index is ignored since we are + * passing a LUID. + */ + luid, 0, + /* No specific source address. */ + NULL, + /* Our destination address. */ + dest, + /* No options. */ + 0, + /* The route row. */ + &row, + /* The best source address, which we don't need. */ + &ignored) != NO_ERROR + /* If the metric is "unused" (-1) or too large for us to add the two + * metrics, use the worst possible, thus sorting this last. + */ + || row.Metric == (ULONG)-1 + || row.Metric > ((ULONG)-1) - interfaceMetric) { + /* Return the worst possible metric. */ + return (ULONG)-1; + } + + /* Return the metric value from that row, plus the interface metric. + * + * See + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa814494(v=vs.85).aspx + * which describes the combination as a "sum". + */ + return row.Metric + interfaceMetric; +} + +/* + * get_DNS_AdaptersAddresses() + * + * Locates DNS info using GetAdaptersAddresses() function from the Internet + * Protocol Helper (IP Helper) API. When located, this returns a pointer + * in *outptr to a newly allocated memory area holding a null-terminated + * string with a space or comma seperated list of DNS IP addresses. + * + * Returns 0 and nullifies *outptr upon inability to return DNSes string. + * + * Returns 1 and sets *outptr when returning a dynamically allocated string. + * + * Implementation supports Windows XP and newer. + */ +#define IPAA_INITIAL_BUF_SZ 15 * 1024 +#define IPAA_MAX_TRIES 3 +static int get_DNS_AdaptersAddresses(char **outptr) +{ + IP_ADAPTER_DNS_SERVER_ADDRESS *ipaDNSAddr; + IP_ADAPTER_ADDRESSES *ipaa, *newipaa, *ipaaEntry; + ULONG ReqBufsz = IPAA_INITIAL_BUF_SZ; + ULONG Bufsz = IPAA_INITIAL_BUF_SZ; + ULONG AddrFlags = 0; + int trying = IPAA_MAX_TRIES; + int res; + + /* The capacity of addresses, in elements. */ + size_t addressesSize; + /* The number of elements in addresses. */ + size_t addressesIndex = 0; + /* The addresses we will sort. */ + Address *addresses; + + union { + struct sockaddr *sa; + struct sockaddr_in *sa4; + struct sockaddr_in6 *sa6; + } namesrvr; + + *outptr = NULL; + + /* Verify run-time availability of GetAdaptersAddresses() */ + if (ares_fpGetAdaptersAddresses == ZERO_NULL) + return 0; + + ipaa = ares_malloc(Bufsz); + if (!ipaa) + return 0; + + /* Start with enough room for a few DNS server addresses and we'll grow it + * as we encounter more. + */ + addressesSize = 4; + addresses = (Address*)ares_malloc(sizeof(Address) * addressesSize); + if(addresses == NULL) { + /* We need room for at least some addresses to function. */ + ares_free(ipaa); + return 0; + } + + /* Usually this call suceeds with initial buffer size */ + res = (*ares_fpGetAdaptersAddresses) (AF_UNSPEC, AddrFlags, NULL, + ipaa, &ReqBufsz); + if ((res != ERROR_BUFFER_OVERFLOW) && (res != ERROR_SUCCESS)) + goto done; + + while ((res == ERROR_BUFFER_OVERFLOW) && (--trying)) + { + if (Bufsz < ReqBufsz) { - free( pFirstEntry ); - return 0; + newipaa = ares_realloc(ipaa, ReqBufsz); + if (!newipaa) + goto done; + Bufsz = ReqBufsz; + ipaa = newipaa; } + res = (*ares_fpGetAdaptersAddresses) (AF_UNSPEC, AddrFlags, NULL, + ipaa, &ReqBufsz); + if (res == ERROR_SUCCESS) + break; + } + if (res != ERROR_SUCCESS) + goto done; - /* process the results */ - for( pEntry = pFirstEntry ; pEntry != NULL ; pEntry = pEntry->Next ) + for (ipaaEntry = ipaa; ipaaEntry; ipaaEntry = ipaaEntry->Next) + { + if(ipaaEntry->OperStatus != IfOperStatusUp) + continue; + + /* For each interface, find any associated DNS servers as IPv4 or IPv6 + * addresses. For each found address, find the best route to that DNS + * server address _on_ _that_ _interface_ (at this moment in time) and + * compute the resulting total metric, just as Windows routing will do. + * Then, sort all the addresses found by the metric. + */ + for (ipaDNSAddr = ipaaEntry->FirstDnsServerAddress; + ipaDNSAddr; + ipaDNSAddr = ipaDNSAddr->Next) { - IP_ADAPTER_DNS_SERVER_ADDRESS* pDNSAddr = pEntry->FirstDnsServerAddress; - for( ; pDNSAddr != NULL ; pDNSAddr = pDNSAddr->Next ) + namesrvr.sa = ipaDNSAddr->Address.lpSockaddr; + + if (namesrvr.sa->sa_family == AF_INET) { - struct sockaddr *pGenericAddr = pDNSAddr->Address.lpSockaddr; - size_t stringlen = 0; + if ((namesrvr.sa4->sin_addr.S_un.S_addr == INADDR_ANY) || + (namesrvr.sa4->sin_addr.S_un.S_addr == INADDR_NONE)) + continue; + + /* Allocate room for another address, if necessary, else skip. */ + if(addressesIndex == addressesSize) { + const size_t newSize = addressesSize + 4; + Address * const newMem = + (Address*)ares_realloc(addresses, sizeof(Address) * newSize); + if(newMem == NULL) { + continue; + } + addresses = newMem; + addressesSize = newSize; + } - if( pGenericAddr->sa_family == AF_INET && left > ipv4_size ) + /* Vista required for Luid or Ipv4Metric */ + if (ares_IsWindowsVistaOrGreater()) { - /* Handle the v4 case */ - struct sockaddr_in *pIPv4Addr = ( struct sockaddr_in * ) pGenericAddr; - ares_inet_ntop( AF_INET, &pIPv4Addr->sin_addr, ret, ipv4_size - 1 ); /* -1 for comma */ - - /* Append a comma to the end, THEN NULL. Should be OK because we - already tested the size at the top of the if statement. */ - stringlen = strlen( ret ); - ret[ stringlen ] = ','; - ret[ stringlen + 1 ] = '\0'; - ret += stringlen + 1; - left -= ret - ret_buf; - ++count; + /* Save the address as the next element in addresses. */ + addresses[addressesIndex].metric = + getBestRouteMetric(&ipaaEntry->Luid, + (SOCKADDR_INET*)(namesrvr.sa), + ipaaEntry->Ipv4Metric); } - else if( pGenericAddr->sa_family == AF_INET6 && left > ipv6_size ) + else { - /* Handle the v6 case */ - struct sockaddr_in6 *pIPv6Addr = ( struct sockaddr_in6 * ) pGenericAddr; - ares_inet_ntop( AF_INET6, &pIPv6Addr->sin6_addr, ret, ipv6_size - 1 ); /* -1 for comma */ - - /* Append a comma to the end, THEN NULL. Should be OK because we - already tested the size at the top of the if statement. */ - stringlen = strlen( ret ); - ret[ stringlen ] = ','; - ret[ stringlen + 1 ] = '\0'; - ret += stringlen + 1; - left -= ret - ret_buf; - ++count; - - /* NB on Windows this also returns stuff in the fec0::/10 range, - seems to be hard-coded somehow. Do we need to ignore them? */ + addresses[addressesIndex].metric = -1; + } + + if (! ares_inet_ntop(AF_INET, &namesrvr.sa4->sin_addr, + addresses[addressesIndex].text, + sizeof(addresses[0].text))) { + continue; + } + ++addressesIndex; + } + else if (namesrvr.sa->sa_family == AF_INET6) + { + if (memcmp(&namesrvr.sa6->sin6_addr, &ares_in6addr_any, + sizeof(namesrvr.sa6->sin6_addr)) == 0) + continue; + + /* Allocate room for another address, if necessary, else skip. */ + if(addressesIndex == addressesSize) { + const size_t newSize = addressesSize + 4; + Address * const newMem = + (Address*)ares_realloc(addresses, sizeof(Address) * newSize); + if(newMem == NULL) { + continue; + } + addresses = newMem; + addressesSize = newSize; + } + + /* Vista required for Luid or Ipv4Metric */ + if (ares_IsWindowsVistaOrGreater()) + { + /* Save the address as the next element in addresses. */ + addresses[addressesIndex].metric = + getBestRouteMetric(&ipaaEntry->Luid, + (SOCKADDR_INET*)(namesrvr.sa), + ipaaEntry->Ipv6Metric); + } + else + { + addresses[addressesIndex].metric = -1; + } + + if (! ares_inet_ntop(AF_INET6, &namesrvr.sa6->sin6_addr, + addresses[addressesIndex].text, + sizeof(addresses[0].text))) { + continue; + } + ++addressesIndex; + } + else { + /* Skip non-IPv4/IPv6 addresses completely. */ + continue; + } + } + } + + /* Sort all of the textual addresses by their metric. */ + qsort(addresses, addressesIndex, sizeof(*addresses), compareAddresses); + + /* Join them all into a single string, removing duplicates. */ + { + size_t i; + for(i = 0; i < addressesIndex; ++i) { + size_t j; + /* Look for this address text appearing previously in the results. */ + for(j = 0; j < i; ++j) { + if(strcmp(addresses[j].text, addresses[i].text) == 0) { + break; } } + /* Iff we didn't emit this address already, emit it now. */ + if(j == i) { + /* Add that to outptr (if we can). */ + commajoin(outptr, addresses[i].text); + } } + } + +done: + ares_free(addresses); + + if (ipaa) + ares_free(ipaa); - if( pFirstEntry ) - free( pFirstEntry ); - if (ret > ret_buf) - ret[-1] = '\0'; - return count; + if (!*outptr) { + return 0; } - else + + return 1; +} + +/* + * get_DNS_Windows() + * + * Locates DNS info from Windows employing most suitable methods available at + * run-time no matter which Windows version it is. When located, this returns + * a pointer in *outptr to a newly allocated memory area holding a string with + * a space or comma seperated list of DNS IP addresses, null-terminated. + * + * Returns 0 and nullifies *outptr upon inability to return DNSes string. + * + * Returns 1 and sets *outptr when returning a dynamically allocated string. + * + * Implementation supports Windows 95 and newer. + */ +static int get_DNS_Windows(char **outptr) +{ + /* Try using IP helper API GetAdaptersAddresses(). IPv4 + IPv6, also sorts + * DNS servers by interface route metrics to try to use the best DNS server. */ + if (get_DNS_AdaptersAddresses(outptr)) + return 1; + + /* Try using IP helper API GetNetworkParams(). IPv4 only. */ + if (get_DNS_NetworkParams(outptr)) + return 1; + + /* Fall-back to registry information */ + return get_DNS_Registry(outptr); +} + +static void replace_comma_by_space(char* str) +{ + /* replace ',' by ' ' to coincide with resolv.conf search parameter */ + char *p; + for (p = str; *p != '\0'; p++) { - FIXED_INFO *fi, *newfi; - DWORD size = sizeof (*fi); - IP_ADDR_STRING *ipAddr; - int i; - int debug = 0; - HRESULT res; - - fi = malloc(size); - if (!fi) - return 0; - - res = (*ares_fpGetNetworkParams) (fi, &size); - if ((res != ERROR_BUFFER_OVERFLOW) && (res != ERROR_SUCCESS)) - goto quit; - - newfi = realloc(fi, size); - if (!newfi) - goto quit; - - fi = newfi; - res = (*ares_fpGetNetworkParams) (fi, &size); - if (res != ERROR_SUCCESS) - goto quit; + if (*p == ',') + *p = ' '; + } +} - if (debug) - { - printf ("Host Name: %s\n", fi->HostName); - printf ("Domain Name: %s\n", fi->DomainName); - printf ("DNS Servers:\n" - " %s (primary)\n", fi->DnsServerList.IpAddress.String); - } - if (strlen(fi->DnsServerList.IpAddress.String) > 0 && - inet_addr(fi->DnsServerList.IpAddress.String) != INADDR_NONE && - left > ipv4_size) - { - ret += sprintf (ret, "%s,", fi->DnsServerList.IpAddress.String); - left -= ret - ret_buf; - ++count; - } +/* Search if 'suffix' is containted in the 'searchlist'. Returns true if yes, + * otherwise false. 'searchlist' is a comma separated list of domain suffixes, + * 'suffix' is one domain suffix, 'len' is the length of 'suffix'. + * The search ignores case. E.g.: + * contains_suffix("abc.def,ghi.jkl", "ghi.JKL") returns true */ +static bool contains_suffix(const char* const searchlist, + const char* const suffix, const size_t len) +{ + const char* beg = searchlist; + const char* end; + if (!*suffix) + return true; + for (;;) + { + while (*beg && (ISSPACE(*beg) || (*beg == ','))) + ++beg; + if (!*beg) + return false; + end = beg; + while (*end && !ISSPACE(*end) && (*end != ',')) + ++end; + if (len == (end - beg) && !strnicmp(beg, suffix, len)) + return true; + beg = end; + } +} - for (i = 0, ipAddr = fi->DnsServerList.Next; ipAddr && left > ipv4_size; - ipAddr = ipAddr->Next, i++) +/* advances list to the next suffix within a comma separated search list. + * len is the length of the next suffix. */ +static size_t next_suffix(const char** list, const size_t advance) +{ + const char* beg = *list + advance; + const char* end; + while (*beg && (ISSPACE(*beg) || (*beg == ','))) + ++beg; + end = beg; + while (*end && !ISSPACE(*end) && (*end != ',')) + ++end; + *list = beg; + return end - beg; +} + +/* + * get_SuffixList_Windows() + * + * Reads the "DNS Suffix Search List" from registry and writes the list items + * whitespace separated to outptr. If the Search List is empty, the + * "Primary Dns Suffix" is written to outptr. + * + * Returns 0 and nullifies *outptr upon inability to return the suffix list. + * + * Returns 1 and sets *outptr when returning a dynamically allocated string. + * + * Implementation supports Windows Server 2003 and newer + */ +static int get_SuffixList_Windows(char **outptr) +{ + HKEY hKey, hKeyEnum; + char keyName[256]; + DWORD keyNameBuffSize; + DWORD keyIdx = 0; + char *p = NULL; + char *pp; + size_t len = 0; + + *outptr = NULL; + + if (ares__getplatform() != WIN_NT) + return 0; + + /* 1. Global DNS Suffix Search List */ + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, + KEY_READ, &hKey) == ERROR_SUCCESS) + { + if (get_REG_SZ(hKey, SEARCHLIST_KEY, outptr)) + replace_comma_by_space(*outptr); + RegCloseKey(hKey); + if (*outptr) + return 1; + } + + /* 2. Connection Specific Search List composed of: + * a. Primary DNS Suffix */ + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_DNSCLIENT, 0, + KEY_READ, &hKey) == ERROR_SUCCESS) + { + get_REG_SZ(hKey, PRIMARYDNSSUFFIX_KEY, outptr); + RegCloseKey(hKey); + } + if (!*outptr) + return 0; + + /* b. Interface SearchList, Domain, DhcpDomain */ + if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY "\\" INTERFACES_KEY, 0, + KEY_READ, &hKey) == ERROR_SUCCESS) + return 0; + for(;;) + { + keyNameBuffSize = sizeof(keyName); + if (RegEnumKeyEx(hKey, keyIdx++, keyName, &keyNameBuffSize, + 0, NULL, NULL, NULL) + != ERROR_SUCCESS) + break; + if (RegOpenKeyEx(hKey, keyName, 0, KEY_QUERY_VALUE, &hKeyEnum) + != ERROR_SUCCESS) + continue; + if (get_REG_SZ(hKeyEnum, SEARCHLIST_KEY, &p) || + get_REG_SZ(hKeyEnum, DOMAIN_KEY, &p) || + get_REG_SZ(hKeyEnum, DHCPDOMAIN_KEY, &p)) { - if (inet_addr(ipAddr->IpAddress.String) != INADDR_NONE) + /* p can be comma separated (SearchList) */ + pp = p; + while (len = next_suffix(&pp, len)) { - ret += sprintf (ret, "%s,", ipAddr->IpAddress.String); - left -= ret - ret_buf; - ++count; + if (!contains_suffix(*outptr, pp, len)) + commanjoin(outptr, pp, len); } - if (debug) - printf (" %s (secondary %d)\n", ipAddr->IpAddress.String, i+1); + ares_free(p); + p = NULL; } - -quit: - if (fi) - free(fi); - - if (debug && left <= ipv4_size) - printf ("Too many nameservers. Truncating to %d addressess", count); - if (ret > ret_buf) - ret[-1] = '\0'; - return count; + RegCloseKey(hKeyEnum); } + RegCloseKey(hKey); + if (*outptr) + replace_comma_by_space(*outptr); + return *outptr != NULL; } + #endif static int init_by_resolv_conf(ares_channel channel) { -#ifndef WATT32 +#if !defined(ANDROID) && !defined(__ANDROID__) && !defined(WATT32) && \ + !defined(CARES_USE_LIBRESOLV) char *line = NULL; #endif int status = -1, nservers = 0, nsort = 0; @@ -796,111 +1457,19 @@ static int init_by_resolv_conf(ares_channel channel) #ifdef WIN32 - /* - NameServer info via IPHLPAPI (IP helper API): - GetNetworkParams() should be the trusted source for this. - Available in Win-98/2000 and later. If that fail, fall-back to - registry information. - - NameServer Registry: - - On Windows 9X, the DNS server can be found in: -HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VxD\MSTCP\NameServer - - On Windows NT/2000/XP/2003: -HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\NameServer - or -HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\DhcpNameServer - or -HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\{AdapterID}\ -NameServer - or -HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\{AdapterID}\ -DhcpNameServer - */ - - HKEY mykey; - HKEY subkey; - DWORD data_type; - DWORD bytes; - DWORD result; - char buf[512]; - win_platform platform; - if (channel->nservers > -1) /* don't override ARES_OPT_SERVER */ return ARES_SUCCESS; - if (get_iphlpapi_dns_info(buf,sizeof(buf)) > 0) + if (get_DNS_Windows(&line)) { - status = config_nameserver(&servers, &nservers, buf); - if (status == ARES_SUCCESS) - goto okay; + status = config_nameserver(&servers, &nservers, line); + ares_free(line); } - platform = ares__getplatform(); - - if (platform == WIN_NT) - { - if (RegOpenKeyEx( - HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, - KEY_READ, &mykey - ) == ERROR_SUCCESS) - { - RegOpenKeyEx(mykey, "Interfaces", 0, - KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS, &subkey); - if (get_res_nt(mykey, NAMESERVER, &line)) - { - status = config_nameserver(&servers, &nservers, line); - free(line); - } - else if (get_res_nt(mykey, DHCPNAMESERVER, &line)) - { - status = config_nameserver(&servers, &nservers, line); - free(line); - } - /* Try the interfaces */ - else if (get_res_interfaces_nt(subkey, NAMESERVER, &line)) - { - status = config_nameserver(&servers, &nservers, line); - free(line); - } - else if (get_res_interfaces_nt(subkey, DHCPNAMESERVER, &line)) - { - status = config_nameserver(&servers, &nservers, line); - free(line); - } - RegCloseKey(subkey); - RegCloseKey(mykey); - } - } - else if (platform == WIN_9X) + if (channel->ndomains == -1 && get_SuffixList_Windows(&line)) { - if (RegOpenKeyEx( - HKEY_LOCAL_MACHINE, WIN_NS_9X, 0, - KEY_READ, &mykey - ) == ERROR_SUCCESS) - { - if ((result = RegQueryValueEx( - mykey, NAMESERVER, NULL, &data_type, - NULL, &bytes - ) - ) == ERROR_SUCCESS || - result == ERROR_MORE_DATA) - { - if (bytes) - { - line = malloc(bytes+1); - if (RegQueryValueEx(mykey, NAMESERVER, NULL, &data_type, - (unsigned char *)line, &bytes) == - ERROR_SUCCESS) - { - status = config_nameserver(&servers, &nservers, line); - } - free(line); - } - } - } - RegCloseKey(mykey); + status = set_search(channel, line); + ares_free(line); } if (status == ARES_SUCCESS) @@ -918,7 +1487,7 @@ DhcpNameServer line = getenv("Inet$Resolvers"); status = ARES_EOF; if (line) { - char *resolvers = strdup(line), *pos, *space; + char *resolvers = ares_strdup(line), *pos, *space; if (!resolvers) return ARES_ENOMEM; @@ -937,7 +1506,7 @@ DhcpNameServer if (status == ARES_SUCCESS) status = ARES_EOF; - free(resolvers); + ares_free(resolvers); } #elif defined(WATT32) @@ -950,43 +1519,117 @@ DhcpNameServer return ARES_SUCCESS; /* use localhost DNS server */ nservers = i; - servers = calloc(i, sizeof(struct server_state)); + servers = ares_malloc(sizeof(struct server_state)); if (!servers) return ARES_ENOMEM; + memset(servers, 0, sizeof(struct server_state)); for (i = 0; def_nameservers[i]; i++) { servers[i].addr.addrV4.s_addr = htonl(def_nameservers[i]); servers[i].addr.family = AF_INET; + servers[i].addr.udp_port = 0; + servers[i].addr.tcp_port = 0; } status = ARES_EOF; -#elif defined(ANDROID) - char value[PROP_VALUE_MAX]=""; - __system_property_get("net.dns1", value); - status = config_nameserver(&servers, &nservers, value); - if (status == ARES_SUCCESS) +#elif defined(ANDROID) || defined(__ANDROID__) + unsigned int i; + char propname[PROP_NAME_MAX]; + char propvalue[PROP_VALUE_MAX]=""; + + for (i = 1; i <= MAX_DNS_PROPERTIES; i++) { + snprintf(propname, sizeof(propname), "%s%u", DNS_PROP_NAME_PREFIX, i); + if (__system_property_get(propname, propvalue) < 1) { + status = ARES_EOF; + break; + } + status = config_nameserver(&servers, &nservers, propvalue); + if (status != ARES_SUCCESS) + break; + status = ARES_EOF; + } +#elif defined(CARES_USE_LIBRESOLV) + struct __res_state res; + memset(&res, 0, sizeof(res)); + int result = res_ninit(&res); + if (result == 0 && (res.options & RES_INIT)) { status = ARES_EOF; + + if (channel->nservers == -1) { + union res_sockaddr_union addr[MAXNS]; + int nscount = res_getservers(&res, addr, MAXNS); + for (int i = 0; i < nscount; ++i) { + char str[INET6_ADDRSTRLEN]; + int config_status; + sa_family_t family = addr[i].sin.sin_family; + if (family == AF_INET) { + ares_inet_ntop(family, &addr[i].sin.sin_addr, str, sizeof(str)); + } else if (family == AF_INET6) { + ares_inet_ntop(family, &addr[i].sin6.sin6_addr, str, sizeof(str)); + } else { + continue; + } + + config_status = config_nameserver(&servers, &nservers, str); + if (config_status != ARES_SUCCESS) { + status = config_status; + break; + } + } + } + if (channel->ndomains == -1) { + int entries = 0; + while ((entries < MAXDNSRCH) && res.dnsrch[entries]) + entries++; + + channel->domains = ares_malloc(entries * sizeof(char *)); + if (!channel->domains) { + status = ARES_ENOMEM; + } else { + channel->ndomains = entries; + for (int i = 0; i < channel->ndomains; ++i) { + channel->domains[i] = ares_strdup(res.dnsrch[i]); + if (!channel->domains[i]) + status = ARES_ENOMEM; + } + } + } + if (channel->ndots == -1) + channel->ndots = res.ndots; + if (channel->tries == -1) + channel->tries = res.retry; + if (channel->rotate == -1) + channel->rotate = res.options & RES_ROTATE; + if (channel->timeout == -1) + channel->timeout = res.retrans * 1000; + + res_ndestroy(&res); + } #else { char *p; FILE *fp; size_t linesize; int error; + int update_domains; /* Don't read resolv.conf and friends if we don't have to */ if (ARES_CONFIG_CHECK(channel)) return ARES_SUCCESS; + /* Only update search domains if they're not already specified */ + update_domains = (channel->ndomains == -1); + fp = fopen(PATH_RESOLV_CONF, "r"); if (fp) { while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) { - if ((p = try_config(line, "domain", ';'))) + if ((p = try_config(line, "domain", ';')) && update_domains) status = config_domain(channel, p); else if ((p = try_config(line, "lookup", ';')) && !channel->lookups) - status = config_lookup(channel, p, "bind", "file"); - else if ((p = try_config(line, "search", ';'))) + status = config_lookup(channel, p, "bind", NULL, "file"); + else if ((p = try_config(line, "search", ';')) && update_domains) status = set_search(channel, p); else if ((p = try_config(line, "nameserver", ';')) && channel->nservers == -1) @@ -1022,11 +1665,11 @@ DhcpNameServer /* Many systems (Solaris, Linux, BSD's) use nsswitch.conf */ fp = fopen("/etc/nsswitch.conf", "r"); if (fp) { - while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) + while ((status = ares__read_line(fp, &line, &linesize)) == + ARES_SUCCESS) { if ((p = try_config(line, "hosts:", '\0')) && !channel->lookups) - /* ignore errors */ - (void)config_lookup(channel, p, "dns", "files"); + (void)config_lookup(channel, p, "dns", "resolve", "files"); } fclose(fp); } @@ -1035,14 +1678,16 @@ DhcpNameServer switch(error) { case ENOENT: case ESRCH: - status = ARES_EOF; break; default: DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error, strerror(error))); - DEBUGF(fprintf(stderr, "Error opening file: %s\n", "/etc/nsswitch.conf")); - status = ARES_EFILE; + DEBUGF(fprintf(stderr, "Error opening file: %s\n", + "/etc/nsswitch.conf")); } + + /* ignore error, maybe we will get luck in next if clause */ + status = ARES_EOF; } } @@ -1050,11 +1695,12 @@ DhcpNameServer /* Linux / GNU libc 2.x and possibly others have host.conf */ fp = fopen("/etc/host.conf", "r"); if (fp) { - while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) + while ((status = ares__read_line(fp, &line, &linesize)) == + ARES_SUCCESS) { if ((p = try_config(line, "order", '\0')) && !channel->lookups) /* ignore errors */ - (void)config_lookup(channel, p, "bind", "hosts"); + (void)config_lookup(channel, p, "bind", NULL, "hosts"); } fclose(fp); } @@ -1063,14 +1709,16 @@ DhcpNameServer switch(error) { case ENOENT: case ESRCH: - status = ARES_EOF; break; default: DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error, strerror(error))); - DEBUGF(fprintf(stderr, "Error opening file: %s\n", "/etc/host.conf")); - status = ARES_EFILE; + DEBUGF(fprintf(stderr, "Error opening file: %s\n", + "/etc/host.conf")); } + + /* ignore error, maybe we will get luck in next if clause */ + status = ARES_EOF; } } @@ -1078,11 +1726,12 @@ DhcpNameServer /* Tru64 uses /etc/svc.conf */ fp = fopen("/etc/svc.conf", "r"); if (fp) { - while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) + while ((status = ares__read_line(fp, &line, &linesize)) == + ARES_SUCCESS) { if ((p = try_config(line, "hosts=", '\0')) && !channel->lookups) /* ignore errors */ - (void)config_lookup(channel, p, "bind", "local"); + (void)config_lookup(channel, p, "bind", NULL, "local"); } fclose(fp); } @@ -1091,19 +1740,20 @@ DhcpNameServer switch(error) { case ENOENT: case ESRCH: - status = ARES_EOF; break; default: DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error, strerror(error))); DEBUGF(fprintf(stderr, "Error opening file: %s\n", "/etc/svc.conf")); - status = ARES_EFILE; } + + /* ignore error, default value will be chosen for `channel->lookups` */ + status = ARES_EOF; } } if(line) - free(line); + ares_free(line); } #endif @@ -1112,16 +1762,13 @@ DhcpNameServer if (status != ARES_EOF) { if (servers != NULL) - free(servers); + ares_free(servers); if (sortlist != NULL) - free(sortlist); + ares_free(sortlist); return status; } /* If we got any name server entries, fill them in. */ -#ifdef WIN32 -okay: -#endif if (servers) { channel->servers = servers; @@ -1161,15 +1808,20 @@ static int init_by_defaults(ares_channel channel) if (channel->tcp_port == -1) channel->tcp_port = htons(NAMESERVER_PORT); + if (channel->ednspsz == -1) + channel->ednspsz = EDNSPACKETSZ; + if (channel->nservers == -1) { /* If nobody specified servers, try a local named. */ - channel->servers = malloc(sizeof(struct server_state)); + channel->servers = ares_malloc(sizeof(struct server_state)); if (!channel->servers) { rc = ARES_ENOMEM; goto error; } channel->servers[0].addr.family = AF_INET; channel->servers[0].addr.addrV4.s_addr = htonl(INADDR_LOOPBACK); + channel->servers[0].addr.udp_port = 0; + channel->servers[0].addr.tcp_port = 0; channel->nservers = 1; } @@ -1186,24 +1838,28 @@ static int init_by_defaults(ares_channel channel) /* Derive a default domain search list from the kernel hostname, * or set it to empty if the hostname isn't helpful. */ +#ifndef HAVE_GETHOSTNAME + channel->ndomains = 0; /* default to none */ +#else + GETHOSTNAME_TYPE_ARG2 lenv = 64; size_t len = 64; int res; channel->ndomains = 0; /* default to none */ -#ifdef HAVE_GETHOSTNAME - hostname = malloc(len); + hostname = ares_malloc(len); if(!hostname) { rc = ARES_ENOMEM; goto error; } do { - res = gethostname(hostname, len); + res = gethostname(hostname, lenv); if(toolong(res)) { char *p; len *= 2; - p = realloc(hostname, len); + lenv *= 2; + p = ares_realloc(hostname, len); if(!p) { rc = ARES_ENOMEM; goto error; @@ -1216,17 +1872,17 @@ static int init_by_defaults(ares_channel channel) goto error; } - } while(0); + } while (res != 0); dot = strchr(hostname, '.'); if (dot) { /* a dot was found */ - channel->domains = malloc(sizeof(char *)); + channel->domains = ares_malloc(sizeof(char *)); if (!channel->domains) { rc = ARES_ENOMEM; goto error; } - channel->domains[0] = strdup(dot + 1); + channel->domains[0] = ares_strdup(dot + 1); if (!channel->domains[0]) { rc = ARES_ENOMEM; goto error; @@ -1242,31 +1898,39 @@ static int init_by_defaults(ares_channel channel) } if (!channel->lookups) { - channel->lookups = strdup("fb"); + channel->lookups = ares_strdup("fb"); if (!channel->lookups) rc = ARES_ENOMEM; } error: if(rc) { - if(channel->servers) - free(channel->servers); + if(channel->servers) { + ares_free(channel->servers); + channel->servers = NULL; + } if(channel->domains && channel->domains[0]) - free(channel->domains[0]); - if(channel->domains) - free(channel->domains); - if(channel->lookups) - free(channel->lookups); + ares_free(channel->domains[0]); + if(channel->domains) { + ares_free(channel->domains); + channel->domains = NULL; + } + + if(channel->lookups) { + ares_free(channel->lookups); + channel->lookups = NULL; + } } if(hostname) - free(hostname); + ares_free(hostname); return rc; } -#if !defined(WIN32) && !defined(WATT32) +#if !defined(WIN32) && !defined(WATT32) && \ + !defined(ANDROID) && !defined(__ANDROID__) && !defined(CARES_USE_LIBRESOLV) static int config_domain(ares_channel channel, char *str) { char *q; @@ -1288,11 +1952,15 @@ static int config_domain(ares_channel channel, char *str) #endif static int config_lookup(ares_channel channel, const char *str, - const char *bindch, const char *filech) + const char *bindch, const char *altbindch, + const char *filech) { char lookups[3], *l; const char *vqualifier p; + if (altbindch == NULL) + altbindch = bindch; + /* Set the lookup order. Only the first letter of each work * is relevant, and it has to be "b" for DNS or "f" for the * host file. Ignore everything else. @@ -1301,8 +1969,8 @@ static int config_lookup(ares_channel channel, const char *str, p = str; while (*p) { - if ((*p == *bindch || *p == *filech) && l < lookups + 2) { - if (*p == *bindch) *l++ = 'b'; + if ((*p == *bindch || *p == *altbindch || *p == *filech) && l < lookups + 2) { + if (*p == *bindch || *p == *altbindch) *l++ = 'b'; else *l++ = 'f'; } while (*p && !ISSPACE(*p) && (*p != ',')) @@ -1311,10 +1979,10 @@ static int config_lookup(ares_channel channel, const char *str, p++; } *l = '\0'; - channel->lookups = strdup(lookups); + channel->lookups = ares_strdup(lookups); return (channel->lookups) ? ARES_SUCCESS : ARES_ENOMEM; } -#endif /* !WIN32 & !WATT32 */ +#endif /* !WIN32 & !WATT32 & !ANDROID & !__ANDROID__ & !CARES_USE_LIBRESOLV */ #ifndef WATT32 static int config_nameserver(struct server_state **servers, int *nservers, @@ -1357,13 +2025,15 @@ static int config_nameserver(struct server_state **servers, int *nservers, continue; /* Resize servers state array. */ - newserv = realloc(*servers, (*nservers + 1) * - sizeof(struct server_state)); + newserv = ares_realloc(*servers, (*nservers + 1) * + sizeof(struct server_state)); if (!newserv) return ARES_ENOMEM; /* Store address data. */ newserv[*nservers].addr.family = host.family; + newserv[*nservers].addr.udp_port = 0; + newserv[*nservers].addr.tcp_port = 0; if (host.family == AF_INET) memcpy(&newserv[*nservers].addr.addrV4, &host.addrV4, sizeof(host.addrV4)); @@ -1378,8 +2048,8 @@ static int config_nameserver(struct server_state **servers, int *nservers, return ARES_SUCCESS; } +#endif /* !WATT32 */ -#ifndef WIN32 static int config_sortlist(struct apattern **sortlist, int *nsort, const char *str) { @@ -1418,8 +2088,11 @@ static int config_sortlist(struct apattern **sortlist, int *nsort, pat.type = PATTERN_CIDR; pat.mask.bits = (unsigned short)bits; pat.family = AF_INET6; - if (!sortlist_alloc(sortlist, nsort, &pat)) + if (!sortlist_alloc(sortlist, nsort, &pat)) { + ares_free(*sortlist); + *sortlist = NULL; return ARES_ENOMEM; + } } else if (ipbufpfx[0] && (bits = ares_inet_net_pton(AF_INET, ipbufpfx, &pat.addrV4, @@ -1428,8 +2101,11 @@ static int config_sortlist(struct apattern **sortlist, int *nsort, pat.type = PATTERN_CIDR; pat.mask.bits = (unsigned short)bits; pat.family = AF_INET; - if (!sortlist_alloc(sortlist, nsort, &pat)) + if (!sortlist_alloc(sortlist, nsort, &pat)) { + ares_free(*sortlist); + *sortlist = NULL; return ARES_ENOMEM; + } } /* See if it is just a regular IP */ else if (ip_addr(ipbuf, q-str, &pat.addrV4) == 0) @@ -1445,8 +2121,11 @@ static int config_sortlist(struct apattern **sortlist, int *nsort, natural_mask(&pat); pat.family = AF_INET; pat.type = PATTERN_MASK; - if (!sortlist_alloc(sortlist, nsort, &pat)) + if (!sortlist_alloc(sortlist, nsort, &pat)) { + ares_free(*sortlist); + *sortlist = NULL; return ARES_ENOMEM; + } } else { @@ -1460,8 +2139,6 @@ static int config_sortlist(struct apattern **sortlist, int *nsort, return ARES_SUCCESS; } -#endif /* !WIN32 */ -#endif /* !WATT32 */ static int set_search(ares_channel channel, const char *str) { @@ -1469,13 +2146,14 @@ static int set_search(ares_channel channel, const char *str) const char *p, *q; if(channel->ndomains != -1) { + /* LCOV_EXCL_START: all callers check ndomains == -1 */ /* if we already have some domains present, free them first */ for(n=0; n < channel->ndomains; n++) - free(channel->domains[n]); - free(channel->domains); + ares_free(channel->domains[n]); + ares_free(channel->domains); channel->domains = NULL; channel->ndomains = -1; - } + } /* LCOV_EXCL_STOP */ /* Count the domains given. */ n = 0; @@ -1495,7 +2173,7 @@ static int set_search(ares_channel channel, const char *str) return ARES_SUCCESS; } - channel->domains = malloc(n * sizeof(char *)); + channel->domains = ares_malloc(n * sizeof(char *)); if (!channel->domains) return ARES_ENOMEM; @@ -1508,7 +2186,7 @@ static int set_search(ares_channel channel, const char *str) q = p; while (*q && !ISSPACE(*q)) q++; - channel->domains[n] = malloc(q - p + 1); + channel->domains[n] = ares_malloc(q - p + 1); if (!channel->domains[n]) return ARES_ENOMEM; memcpy(channel->domains[n], p, q - p); @@ -1559,7 +2237,8 @@ static const char *try_option(const char *p, const char *q, const char *opt) return ((size_t)(q - p) >= len && !strncmp(p, opt, len)) ? &p[len] : NULL; } -#if !defined(WIN32) && !defined(WATT32) +#if !defined(WIN32) && !defined(WATT32) && \ + !defined(ANDROID) && !defined(__ANDROID__) && !defined(CARES_USE_LIBRESOLV) static char *try_config(char *s, const char *opt, char scc) { size_t len; @@ -1568,7 +2247,7 @@ static char *try_config(char *s, const char *opt, char scc) if (!s || !opt) /* no line or no option */ - return NULL; + return NULL; /* LCOV_EXCL_LINE */ /* Hash '#' character is always used as primary comment char, additionally a not-NUL secondary comment char will be considered when specified. */ @@ -1600,7 +2279,7 @@ static char *try_config(char *s, const char *opt, char scc) if ((len = strlen(opt)) == 0) /* empty option */ - return NULL; + return NULL; /* LCOV_EXCL_LINE */ if (strncmp(p, opt, len) != 0) /* line and option do not match */ @@ -1611,7 +2290,7 @@ static char *try_config(char *s, const char *opt, char scc) if (!*p) /* no option value */ - return NULL; + return NULL; /* LCOV_EXCL_LINE */ if ((opt[len-1] != ':') && (opt[len-1] != '=') && !ISSPACE(*p)) /* whitespace between option name and value is mandatory @@ -1629,21 +2308,9 @@ static char *try_config(char *s, const char *opt, char scc) /* return pointer to option value */ return p; } +#endif /* !WIN32 & !WATT32 & !ANDROID & !__ANDROID__ */ -static int sortlist_alloc(struct apattern **sortlist, int *nsort, - struct apattern *pat) -{ - struct apattern *newsort; - newsort = realloc(*sortlist, (*nsort + 1) * sizeof(struct apattern)); - if (!newsort) - return 0; - newsort[*nsort] = *pat; - *sortlist = newsort; - (*nsort)++; - return 1; -} - -static int ip_addr(const char *ipbuf, ssize_t len, struct in_addr *addr) +static int ip_addr(const char *ipbuf, ares_ssize_t len, struct in_addr *addr) { /* Four octets and three periods yields at most 15 characters. */ @@ -1675,7 +2342,19 @@ static void natural_mask(struct apattern *pat) else pat->mask.addr4.s_addr = htonl(IN_CLASSC_NET); } -#endif /* !WIN32 && !WATT32 */ + +static int sortlist_alloc(struct apattern **sortlist, int *nsort, + struct apattern *pat) +{ + struct apattern *newsort; + newsort = ares_realloc(*sortlist, (*nsort + 1) * sizeof(struct apattern)); + if (!newsort) + return 0; + newsort[*nsort] = *pat; + *sortlist = newsort; + (*nsort)++; + return 1; +} /* initialize an rc4 key. If possible a cryptographically secure random key is generated using a suitable function (for example win32's RtlGenRandom as @@ -1706,9 +2385,9 @@ static void randomize_key(unsigned char* key,int key_data_len) #endif #endif /* WIN32 */ - if ( !randomized ) { + if (!randomized) { for (;counter<key_data_len;counter++) - key[counter]=(unsigned char)(rand() % 256); + key[counter]=(unsigned char)(rand() % 256); /* LCOV_EXCL_LINE */ } } @@ -1720,9 +2399,10 @@ static int init_id_key(rc4_key* key,int key_data_len) short counter; unsigned char *key_data_ptr = 0; - key_data_ptr = calloc(1,key_data_len); + key_data_ptr = ares_malloc(key_data_len); if (!key_data_ptr) return ARES_ENOMEM; + memset(key_data_ptr, 0, key_data_len); state = &key->state[0]; for(counter = 0; counter < 256; counter++) @@ -1741,17 +2421,10 @@ static int init_id_key(rc4_key* key,int key_data_len) index1 = (unsigned char)((index1 + 1) % key_data_len); } - free(key_data_ptr); + ares_free(key_data_ptr); return ARES_SUCCESS; } -unsigned short ares__generate_new_id(rc4_key* key) -{ - unsigned short r=0; - ares__rc4(key, (unsigned char *)&r, sizeof(r)); - return r; -} - void ares_set_local_ip4(ares_channel channel, unsigned int local_ip) { channel->local_ip4 = local_ip; @@ -1782,6 +2455,41 @@ void ares_set_socket_callback(ares_channel channel, channel->sock_create_cb_data = data; } +void ares_set_socket_configure_callback(ares_channel channel, + ares_sock_config_callback cb, + void *data) +{ + channel->sock_config_cb = cb; + channel->sock_config_cb_data = data; +} + +void ares_set_socket_functions(ares_channel channel, + const struct ares_socket_functions * funcs, + void *data) +{ + channel->sock_funcs = funcs; + channel->sock_func_cb_data = data; +} + +int ares_set_sortlist(ares_channel channel, const char *sortstr) +{ + int nsort = 0; + struct apattern *sortlist = NULL; + int status; + + if (!channel) + return ARES_ENODATA; + + status = config_sortlist(&sortlist, &nsort, sortstr); + if (status == ARES_SUCCESS && sortlist) { + if (channel->sortlist) + ares_free(channel->sortlist); + channel->sortlist = sortlist; + channel->nsort = nsort; + } + return status; +} + void ares__init_servers_state(ares_channel channel) { struct server_state *server; diff --git a/ares_ipv6.h b/ares_ipv6.h index 6f1022a..b0017f1 100644 --- a/ares_ipv6.h +++ b/ares_ipv6.h @@ -47,16 +47,16 @@ struct addrinfo #endif #ifndef NS_IN6ADDRSZ -#if SIZEOF_STRUCT_IN6_ADDR == 0 +#ifndef HAVE_STRUCT_IN6_ADDR /* We cannot have it set to zero, so we pick a fixed value here */ #define NS_IN6ADDRSZ 16 #else -#define NS_IN6ADDRSZ SIZEOF_STRUCT_IN6_ADDR +#define NS_IN6ADDRSZ sizeof(struct in6_addr) #endif #endif #ifndef NS_INADDRSZ -#define NS_INADDRSZ SIZEOF_STRUCT_IN_ADDR +#define NS_INADDRSZ sizeof(struct in_addr) #endif #ifndef NS_INT16SZ @@ -71,7 +71,7 @@ struct addrinfo #endif #endif -/* Defined in ares_net_pton.c for no particular reason. */ +/* Defined in inet_net_pton.c for no particular reason. */ extern const struct ares_in6_addr ares_in6addr_any; /* :: */ diff --git a/ares_library_init.c b/ares_library_init.c index f0137a1..0a853d7 100644 --- a/ares_library_init.c +++ b/ares_library_init.c @@ -27,6 +27,7 @@ fpGetNetworkParams_t ares_fpGetNetworkParams = ZERO_NULL; fpSystemFunction036_t ares_fpSystemFunction036 = ZERO_NULL; fpGetAdaptersAddresses_t ares_fpGetAdaptersAddresses = ZERO_NULL; +fpGetBestRoute2_t ares_fpGetBestRoute2 = ZERO_NULL; #endif /* library-private global vars with source visibility restricted to this file */ @@ -34,6 +35,11 @@ fpGetAdaptersAddresses_t ares_fpGetAdaptersAddresses = ZERO_NULL; static unsigned int ares_initialized; static int ares_init_flags; +/* library-private global vars with visibility across the whole library */ +void *(*ares_malloc)(size_t size) = malloc; +void *(*ares_realloc)(void *ptr, size_t size) = realloc; +void (*ares_free)(void *ptr) = free; + #ifdef USE_WINSOCK static HMODULE hnd_iphlpapi; static HMODULE hnd_advapi32; @@ -45,7 +51,7 @@ static int ares_win32_init(void) #ifdef USE_WINSOCK hnd_iphlpapi = 0; - hnd_iphlpapi = LoadLibrary("iphlpapi.dll"); + hnd_iphlpapi = LoadLibraryW(L"iphlpapi.dll"); if (!hnd_iphlpapi) return ARES_ELOADIPHLPAPI; @@ -66,6 +72,15 @@ static int ares_win32_init(void) support Windows 2000 anymore */ } + ares_fpGetBestRoute2 = (fpGetBestRoute2_t) + GetProcAddress(hnd_iphlpapi, "GetBestRoute2"); + if (!ares_fpGetBestRoute2) + { + /* This can happen on clients before Vista, I don't + think it should be an error, unless we don't want to + support Windows XP anymore */ + } + /* * When advapi32.dll is unavailable or advapi32.dll has no SystemFunction036, * also known as RtlGenRandom, which is the case for Windows versions prior @@ -73,7 +88,7 @@ static int ares_win32_init(void) */ hnd_advapi32 = 0; - hnd_advapi32 = LoadLibrary("advapi32.dll"); + hnd_advapi32 = LoadLibraryW(L"advapi32.dll"); if (hnd_advapi32) { ares_fpSystemFunction036 = (fpSystemFunction036_t) @@ -101,14 +116,17 @@ int ares_library_init(int flags) int res; if (ares_initialized) - return ARES_SUCCESS; + { + ares_initialized++; + return ARES_SUCCESS; + } ares_initialized++; if (flags & ARES_LIB_INIT_WIN32) { res = ares_win32_init(); if (res != ARES_SUCCESS) - return res; + return res; /* LCOV_EXCL_LINE: can't test Win32 init failure */ } ares_init_flags = flags; @@ -116,17 +134,36 @@ int ares_library_init(int flags) return ARES_SUCCESS; } +int ares_library_init_mem(int flags, + void *(*amalloc)(size_t size), + void (*afree)(void *ptr), + void *(*arealloc)(void *ptr, size_t size)) +{ + if (amalloc) + ares_malloc = amalloc; + if (arealloc) + ares_realloc = arealloc; + if (afree) + ares_free = afree; + return ares_library_init(flags); +} + void ares_library_cleanup(void) { if (!ares_initialized) return; ares_initialized--; + if (ares_initialized) + return; if (ares_init_flags & ARES_LIB_INIT_WIN32) ares_win32_cleanup(); ares_init_flags = ARES_LIB_INIT_NONE; + ares_malloc = malloc; + ares_realloc = realloc; + ares_free = free; } @@ -138,5 +175,3 @@ int ares_library_initialized(void) #endif return ARES_SUCCESS; } - - diff --git a/ares_library_init.h b/ares_library_init.h index 59e5cc5..2a2ba11 100644 --- a/ares_library_init.h +++ b/ares_library_init.h @@ -28,13 +28,14 @@ typedef DWORD (WINAPI *fpGetNetworkParams_t) (FIXED_INFO*, DWORD*); typedef BOOLEAN (APIENTRY *fpSystemFunction036_t) (void*, ULONG); typedef ULONG (WINAPI *fpGetAdaptersAddresses_t) ( ULONG, ULONG, void*, IP_ADAPTER_ADDRESSES*, ULONG* ); - +typedef NETIO_STATUS (WINAPI *fpGetBestRoute2_t) ( NET_LUID *, NET_IFINDEX, const SOCKADDR_INET *, const SOCKADDR_INET *, ULONG, PMIB_IPFORWARD_ROW2, SOCKADDR_INET * ); /* Forward-declaration of variables defined in ares_library_init.c */ /* that are global and unique instances for whole c-ares library. */ extern fpGetNetworkParams_t ares_fpGetNetworkParams; extern fpSystemFunction036_t ares_fpSystemFunction036; extern fpGetAdaptersAddresses_t ares_fpGetAdaptersAddresses; +extern fpGetBestRoute2_t ares_fpGetBestRoute2; #endif /* USE_WINSOCK */ diff --git a/ares_llist.c b/ares_llist.c index c0acd90..36ca84c 100644 --- a/ares_llist.c +++ b/ares_llist.c @@ -61,26 +61,3 @@ void ares__remove_from_list(struct list_node* node) { } } -/* Swap the contents of two lists */ -void ares__swap_lists(struct list_node* head_a, - struct list_node* head_b) { - int is_a_empty = ares__is_list_empty(head_a); - int is_b_empty = ares__is_list_empty(head_b); - struct list_node old_a = *head_a; - struct list_node old_b = *head_b; - - if (is_a_empty) { - ares__init_list_head(head_b); - } else { - *head_b = old_a; - old_a.next->prev = head_b; - old_a.prev->next = head_b; - } - if (is_b_empty) { - ares__init_list_head(head_a); - } else { - *head_a = old_b; - old_b.next->prev = head_a; - old_b.prev->next = head_a; - } -} diff --git a/ares_llist.h b/ares_llist.h index b09f0de..20f4d1c 100644 --- a/ares_llist.h +++ b/ares_llist.h @@ -36,7 +36,4 @@ void ares__insert_in_list(struct list_node* new_node, void ares__remove_from_list(struct list_node* node); -void ares__swap_lists(struct list_node* head_a, - struct list_node* head_b); - #endif /* __ARES_LLIST_H */ diff --git a/ares_mkquery.c b/ares_mkquery.c index aa41e6b..5aea914 100644 --- a/ares_mkquery.c +++ b/ares_mkquery.c @@ -15,175 +15,10 @@ */ #include "ares_setup.h" - -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif -#ifdef HAVE_NETINET_IN_H -# include <netinet/in.h> -#endif -#ifdef HAVE_ARPA_NAMESER_H -# include <arpa/nameser.h> -#else -# include "nameser.h" -#endif -#ifdef HAVE_ARPA_NAMESER_COMPAT_H -# include <arpa/nameser_compat.h> -#endif - -#include <stdlib.h> -#include <string.h> #include "ares.h" -#include "ares_dns.h" -#include "ares_private.h" - -/* Header format, from RFC 1035: - * 1 1 1 1 1 1 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * | ID | - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * |QR| Opcode |AA|TC|RD|RA| Z | RCODE | - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * | QDCOUNT | - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * | ANCOUNT | - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * | NSCOUNT | - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * | ARCOUNT | - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * - * AA, TC, RA, and RCODE are only set in responses. Brief description - * of the remaining fields: - * ID Identifier to match responses with queries - * QR Query (0) or response (1) - * Opcode For our purposes, always QUERY - * RD Recursion desired - * Z Reserved (zero) - * QDCOUNT Number of queries - * ANCOUNT Number of answers - * NSCOUNT Number of name server records - * ARCOUNT Number of additional records - * - * Question format, from RFC 1035: - * 1 1 1 1 1 1 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * | | - * / QNAME / - * / / - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * | QTYPE | - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * | QCLASS | - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * - * The query name is encoded as a series of labels, each represented - * as a one-byte length (maximum 63) followed by the text of the - * label. The list is terminated by a label of length zero (which can - * be thought of as the root domain). - */ int ares_mkquery(const char *name, int dnsclass, int type, unsigned short id, - int rd, unsigned char **bufp, int *buflenp) + int rd, unsigned char **buf, int *buflen) { - size_t len; - unsigned char *q; - const char *p; - size_t buflen; - unsigned char *buf; - - /* Set our results early, in case we bail out early with an error. */ - *buflenp = 0; - *bufp = NULL; - - /* Allocate a memory area for the maximum size this packet might need. +2 - * is for the length byte and zero termination if no dots or ecscaping is - * used. - */ - len = strlen(name) + 2 + HFIXEDSZ + QFIXEDSZ; - buf = malloc(len); - if (!buf) - return ARES_ENOMEM; - - /* Set up the header. */ - q = buf; - memset(q, 0, HFIXEDSZ); - DNS_HEADER_SET_QID(q, id); - DNS_HEADER_SET_OPCODE(q, QUERY); - if (rd) { - DNS_HEADER_SET_RD(q, 1); - } - else { - DNS_HEADER_SET_RD(q, 0); - } - DNS_HEADER_SET_QDCOUNT(q, 1); - - /* A name of "." is a screw case for the loop below, so adjust it. */ - if (strcmp(name, ".") == 0) - name++; - - /* Start writing out the name after the header. */ - q += HFIXEDSZ; - while (*name) - { - if (*name == '.') { - free(buf); - return ARES_EBADNAME; - } - - /* Count the number of bytes in this label. */ - len = 0; - for (p = name; *p && *p != '.'; p++) - { - if (*p == '\\' && *(p + 1) != 0) - p++; - len++; - } - if (len > MAXLABEL) { - free(buf); - return ARES_EBADNAME; - } - - /* Encode the length and copy the data. */ - *q++ = (unsigned char)len; - for (p = name; *p && *p != '.'; p++) - { - if (*p == '\\' && *(p + 1) != 0) - p++; - *q++ = *p; - } - - /* Go to the next label and repeat, unless we hit the end. */ - if (!*p) - break; - name = p + 1; - } - - /* Add the zero-length label at the end. */ - *q++ = 0; - - /* Finish off the question with the type and class. */ - DNS_QUESTION_SET_TYPE(q, type); - DNS_QUESTION_SET_CLASS(q, dnsclass); - - q += QFIXEDSZ; - - buflen = (q - buf); - - /* Reject names that are longer than the maximum of 255 bytes that's - * specified in RFC 1035 ("To simplify implementations, the total length of - * a domain name (i.e., label octets and label length octets) is restricted - * to 255 octets or less."). */ - if (buflen > (MAXCDNAME + HFIXEDSZ + QFIXEDSZ)) { - free(buf); - return ARES_EBADNAME; - } - - /* we know this fits in an int at this point */ - *buflenp = (int) buflen; - *bufp = buf; - - return ARES_SUCCESS; + return ares_create_query(name, dnsclass, type, id, rd, buf, buflen, 0); } diff --git a/ares_nowarn.c b/ares_nowarn.c index 0ed015c..f63d913 100644 --- a/ares_nowarn.c +++ b/ares_nowarn.c @@ -1,5 +1,5 @@ -/* Copyright (C) 2010-2011 by Daniel Stenberg +/* Copyright (C) 2010-2013 by Daniel Stenberg * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -22,14 +22,11 @@ #endif #ifdef HAVE_LIMITS_H -# include <limits.h> +#include <limits.h> #endif #if defined(__INTEL_COMPILER) && defined(__unix__) -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -43,24 +40,43 @@ #include "ares_nowarn.h" -#if (SIZEOF_INT == 2) -# define CARES_MASK_SINT 0x7FFF -# define CARES_MASK_UINT 0xFFFF -#elif (SIZEOF_INT == 4) -# define CARES_MASK_SINT 0x7FFFFFFF -# define CARES_MASK_UINT 0xFFFFFFFF -#elif (SIZEOF_INT == 8) -# define CARES_MASK_SINT 0x7FFFFFFFFFFFFFFF -# define CARES_MASK_UINT 0xFFFFFFFFFFFFFFFF -#elif (SIZEOF_INT == 16) -# define CARES_MASK_SINT 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -# define CARES_MASK_UINT 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -#elif defined(HAVE_LIMITS_H) -# define CARES_MASK_SINT INT_MAX -# define CARES_MASK_UINT UINT_MAX +#ifndef HAVE_LIMITS_H +/* systems without <limits.h> we guess have 16 bit shorts, 32bit ints and + 32bit longs */ +# define CARES_MASK_SSHORT 0x7FFF +# define CARES_MASK_USHORT 0xFFFF +# define CARES_MASK_SINT 0x7FFFFFFF +# define CARES_MASK_UINT 0xFFFFFFFF +# define CARES_MASK_SLONG 0x7FFFFFFFL +# define CARES_MASK_ULONG 0xFFFFFFFFUL +#else +# define CARES_MASK_SSHORT SHRT_MAX +# define CARES_MASK_USHORT USHRT_MAX +# define CARES_MASK_SINT INT_MAX +# define CARES_MASK_UINT UINT_MAX +# define CARES_MASK_SLONG LONG_MAX +# define CARES_MASK_ULONG ULONG_MAX #endif /* +** unsigned size_t to signed long +*/ + +long aresx_uztosl(size_t uznum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + return (long)(uznum & (size_t) CARES_MASK_SLONG); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* ** unsigned size_t to signed int */ @@ -79,6 +95,43 @@ int aresx_uztosi(size_t uznum) } /* +** unsigned size_t to signed short +*/ + +short aresx_uztoss(size_t uznum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + return (short)(uznum & (size_t) CARES_MASK_SSHORT); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** signed int to signed short +*/ + +short aresx_sitoss(int sinum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(sinum >= 0); + return (short)(sinum & (int) CARES_MASK_SSHORT); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* ** signed long to signed int */ @@ -98,10 +151,10 @@ int aresx_sltosi(long slnum) } /* -** signed ssize_t to signed int +** signed ares_ssize_t to signed int */ -int aresx_sztosi(ssize_t sznum) +int aresx_sztosi(ares_ssize_t sznum) { #ifdef __INTEL_COMPILER # pragma warning(push) @@ -109,7 +162,7 @@ int aresx_sztosi(ssize_t sznum) #endif DEBUGASSERT(sznum >= 0); - return (int)(sznum & (ssize_t) CARES_MASK_SINT); + return (int)(sznum & (ares_ssize_t) CARES_MASK_SINT); #ifdef __INTEL_COMPILER # pragma warning(pop) @@ -117,10 +170,10 @@ int aresx_sztosi(ssize_t sznum) } /* -** signed ssize_t to unsigned int +** signed ares_ssize_t to unsigned int */ -unsigned int aresx_sztoui(ssize_t sznum) +unsigned int aresx_sztoui(ares_ssize_t sznum) { #ifdef __INTEL_COMPILER # pragma warning(push) @@ -128,7 +181,26 @@ unsigned int aresx_sztoui(ssize_t sznum) #endif DEBUGASSERT(sznum >= 0); - return (unsigned int)(sznum & (ssize_t) CARES_MASK_UINT); + return (unsigned int)(sznum & (ares_ssize_t) CARES_MASK_UINT); + +#ifdef __INTEL_COMPILER +# pragma warning(pop) +#endif +} + +/* +** signed int to unsigned short +*/ + +unsigned short aresx_sitous(int sinum) +{ +#ifdef __INTEL_COMPILER +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ +#endif + + DEBUGASSERT(sinum >= 0); + return (unsigned short)(sinum & (int) CARES_MASK_USHORT); #ifdef __INTEL_COMPILER # pragma warning(pop) diff --git a/ares_nowarn.h b/ares_nowarn.h index bcaa227..505e622 100644 --- a/ares_nowarn.h +++ b/ares_nowarn.h @@ -2,7 +2,7 @@ #define HEADER_CARES_NOWARN_H -/* Copyright (C) 2010-2011 by Daniel Stenberg +/* Copyright (C) 2010-2012 by Daniel Stenberg * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -17,13 +17,19 @@ * without express or implied warranty. */ -int aresx_uztosi(size_t uznum); +long aresx_uztosl(size_t uznum); +int aresx_uztosi(size_t uznum); +short aresx_uztoss(size_t uznum); + +short aresx_sitoss(int sinum); int aresx_sltosi(long slnum); -int aresx_sztosi(ssize_t sznum); +int aresx_sztosi(ares_ssize_t sznum); + +unsigned int aresx_sztoui(ares_ssize_t sznum); -unsigned int aresx_sztoui(ssize_t sznum); +unsigned short aresx_sitous(int sinum); #if defined(__INTEL_COMPILER) && defined(__unix__) diff --git a/ares_options.c b/ares_options.c index 5174ef2..c3cbd1d 100644 --- a/ares_options.c +++ b/ares_options.c @@ -1,6 +1,6 @@ /* Copyright 1998 by the Massachusetts Institute of Technology. - * Copyright (C) 2008-2011 by Daniel Stenberg + * Copyright (C) 2008-2013 by Daniel Stenberg * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -24,7 +24,7 @@ #include "ares.h" #include "ares_data.h" -#include "inet_net_pton.h" +#include "ares_inet_net_pton.h" #include "ares_private.h" @@ -83,6 +83,62 @@ int ares_get_servers(ares_channel channel, return status; } +int ares_get_servers_ports(ares_channel channel, + struct ares_addr_port_node **servers) +{ + struct ares_addr_port_node *srvr_head = NULL; + struct ares_addr_port_node *srvr_last = NULL; + struct ares_addr_port_node *srvr_curr; + int status = ARES_SUCCESS; + int i; + + if (!channel) + return ARES_ENODATA; + + for (i = 0; i < channel->nservers; i++) + { + /* Allocate storage for this server node appending it to the list */ + srvr_curr = ares_malloc_data(ARES_DATATYPE_ADDR_PORT_NODE); + if (!srvr_curr) + { + status = ARES_ENOMEM; + break; + } + if (srvr_last) + { + srvr_last->next = srvr_curr; + } + else + { + srvr_head = srvr_curr; + } + srvr_last = srvr_curr; + + /* Fill this server node data */ + srvr_curr->family = channel->servers[i].addr.family; + srvr_curr->udp_port = ntohs((unsigned short)channel->servers[i].addr.udp_port); + srvr_curr->tcp_port = ntohs((unsigned short)channel->servers[i].addr.tcp_port); + if (srvr_curr->family == AF_INET) + memcpy(&srvr_curr->addrV4, &channel->servers[i].addr.addrV4, + sizeof(srvr_curr->addrV4)); + else + memcpy(&srvr_curr->addrV6, &channel->servers[i].addr.addrV6, + sizeof(srvr_curr->addrV6)); + } + + if (status != ARES_SUCCESS) + { + if (srvr_head) + { + ares_free_data(srvr_head); + srvr_head = NULL; + } + } + + *servers = srvr_head; + + return status; +} int ares_set_servers(ares_channel channel, struct ares_addr_node *servers) @@ -92,7 +148,7 @@ int ares_set_servers(ares_channel channel, int i; if (ares_library_initialized() != ARES_SUCCESS) - return ARES_ENOTINITIALIZED; + return ARES_ENOTINITIALIZED; /* LCOV_EXCL_LINE: n/a on non-WinSock */ if (!channel) return ARES_ENODATA; @@ -107,7 +163,7 @@ int ares_set_servers(ares_channel channel, if (num_srvrs > 0) { /* Allocate storage for servers state */ - channel->servers = malloc(num_srvrs * sizeof(struct server_state)); + channel->servers = ares_malloc(num_srvrs * sizeof(struct server_state)); if (!channel->servers) { return ARES_ENOMEM; @@ -117,6 +173,57 @@ int ares_set_servers(ares_channel channel, for (i = 0, srvr = servers; srvr; i++, srvr = srvr->next) { channel->servers[i].addr.family = srvr->family; + channel->servers[i].addr.udp_port = 0; + channel->servers[i].addr.tcp_port = 0; + if (srvr->family == AF_INET) + memcpy(&channel->servers[i].addr.addrV4, &srvr->addrV4, + sizeof(srvr->addrV4)); + else + memcpy(&channel->servers[i].addr.addrV6, &srvr->addrV6, + sizeof(srvr->addrV6)); + } + /* Initialize servers state remaining data */ + ares__init_servers_state(channel); + } + + return ARES_SUCCESS; +} + +int ares_set_servers_ports(ares_channel channel, + struct ares_addr_port_node *servers) +{ + struct ares_addr_port_node *srvr; + int num_srvrs = 0; + int i; + + if (ares_library_initialized() != ARES_SUCCESS) + return ARES_ENOTINITIALIZED; /* LCOV_EXCL_LINE: n/a on non-WinSock */ + + if (!channel) + return ARES_ENODATA; + + ares__destroy_servers_state(channel); + + for (srvr = servers; srvr; srvr = srvr->next) + { + num_srvrs++; + } + + if (num_srvrs > 0) + { + /* Allocate storage for servers state */ + channel->servers = ares_malloc(num_srvrs * sizeof(struct server_state)); + if (!channel->servers) + { + return ARES_ENOMEM; + } + channel->nservers = num_srvrs; + /* Fill servers state address data */ + for (i = 0, srvr = servers; srvr; i++, srvr = srvr->next) + { + channel->servers[i].addr.family = srvr->family; + channel->servers[i].addr.udp_port = htons((unsigned short)srvr->udp_port); + channel->servers[i].addr.tcp_port = htons((unsigned short)srvr->tcp_port); if (srvr->family == AF_INET) memcpy(&channel->servers[i].addr.addrV4, &srvr->addrV4, sizeof(srvr->addrV4)); @@ -132,19 +239,21 @@ int ares_set_servers(ares_channel channel, } /* Incomming string format: host[:port][,host[:port]]... */ -int ares_set_servers_csv(ares_channel channel, - const char* _csv) +/* IPv6 addresses with ports require square brackets [fe80::1%lo0]:53 */ +static int set_servers_csv(ares_channel channel, + const char* _csv, int use_port) { size_t i; char* csv = NULL; char* ptr; char* start_host; + int cc = 0; int rv = ARES_SUCCESS; - struct ares_addr_node *servers = NULL; - struct ares_addr_node *last = NULL; + struct ares_addr_port_node *servers = NULL; + struct ares_addr_port_node *last = NULL; if (ares_library_initialized() != ARES_SUCCESS) - return ARES_ENOTINITIALIZED; + return ARES_ENOTINITIALIZED; /* LCOV_EXCL_LINE: n/a on non-WinSock */ if (!channel) return ARES_ENODATA; @@ -155,7 +264,10 @@ int ares_set_servers_csv(ares_channel channel, if (i == 0) return ARES_SUCCESS; /* blank all servers */ - csv = malloc(i + 2); + csv = ares_malloc(i + 2); + if (!csv) + return ARES_ENOMEM; + strcpy(csv, _csv); if (csv[i-1] != ',') { /* make parsing easier by ensuring ending ',' */ csv[i] = ','; @@ -164,28 +276,54 @@ int ares_set_servers_csv(ares_channel channel, start_host = csv; for (ptr = csv; *ptr; ptr++) { - if (*ptr == ',') { + if (*ptr == ':') { + /* count colons to determine if we have an IPv6 number or IPv4 with + port */ + cc++; + } + else if (*ptr == '[') { + /* move start_host if an open square bracket is found wrapping an IPv6 + address */ + start_host = ptr + 1; + } + else if (*ptr == ',') { char* pp = ptr - 1; + char* p = ptr; + int port = 0; struct in_addr in4; struct ares_in6_addr in6; - struct ares_addr_node *s = NULL; + struct ares_addr_port_node *s = NULL; *ptr = 0; /* null terminate host:port string */ - /* Got an entry..see if port was specified. */ - while (pp > start_host) { - if (*pp == ':') - break; /* yes */ - if (!ISDIGIT(*pp)) { - /* Found end of digits before we found :, so wasn't a port */ - pp = ptr; - break; + /* Got an entry..see if the port was specified. */ + if (cc > 0) { + while (pp > start_host) { + /* a single close square bracket followed by a colon, ']:' indicates + an IPv6 address with port */ + if ((*pp == ']') && (*p == ':')) + break; /* found port */ + /* a single colon, ':' indicates an IPv4 address with port */ + if ((*pp == ':') && (cc == 1)) + break; /* found port */ + if (!(ISDIGIT(*pp) || (*pp == ':'))) { + /* Found end of digits before we found :, so wasn't a port */ + /* must allow ':' for IPv6 case of ']:' indicates we found a port */ + pp = p = ptr; + break; + } + pp--; + p--; + } + if ((pp != start_host) && ((pp + 1) < ptr)) { + /* Found it. Parse over the port number */ + /* when an IPv6 address is wrapped with square brackets the port + starts at pp + 2 */ + if (*pp == ']') + p++; /* move p before ':' */ + /* p will point to the start of the port */ + port = (int)strtol(p, NULL, 10); + *pp = 0; /* null terminate host */ } - pp--; - } - if ((pp != start_host) && ((pp + 1) < ptr)) { - /* Found it. Parse over the port number */ - (void)strtol(pp + 1, NULL, 10); - *pp = 0; /* null terminate host */ } /* resolve host, try ipv4 first, rslt is in network byte order */ rv = ares_inet_pton(AF_INET, start_host, &in4); @@ -197,7 +335,7 @@ int ares_set_servers_csv(ares_channel channel, goto out; } /* was ipv6, add new server */ - s = malloc(sizeof(*s)); + s = ares_malloc(sizeof(*s)); if (!s) { rv = ARES_ENOMEM; goto out; @@ -207,7 +345,7 @@ int ares_set_servers_csv(ares_channel channel, } else { /* was ipv4, add new server */ - s = malloc(sizeof(*s)); + s = ares_malloc(sizeof(*s)); if (!s) { rv = ARES_ENOMEM; goto out; @@ -216,11 +354,13 @@ int ares_set_servers_csv(ares_channel channel, memcpy(&s->addr, &in4, sizeof(struct in_addr)); } if (s) { - /* TODO: Add port to ares_addr_node and assign it here. */ - + s->udp_port = use_port ? port: 0; + s->tcp_port = s->udp_port; s->next = NULL; if (last) { last->next = s; + /* need to move last to maintain the linked list */ + last = last->next; } else { servers = s; @@ -230,19 +370,33 @@ int ares_set_servers_csv(ares_channel channel, /* Set up for next one */ start_host = ptr + 1; + cc = 0; } } - rv = ares_set_servers(channel, servers); + rv = ares_set_servers_ports(channel, servers); out: if (csv) - free(csv); + ares_free(csv); while (servers) { - struct ares_addr_node *s = servers; + struct ares_addr_port_node *s = servers; servers = servers->next; - free(s); + ares_free(s); } return rv; } + +int ares_set_servers_csv(ares_channel channel, + const char* _csv) +{ + return set_servers_csv(channel, _csv, FALSE); +} + +int ares_set_servers_ports_csv(ares_channel channel, + const char* _csv) +{ + return set_servers_csv(channel, _csv, TRUE); +} + diff --git a/ares_parse_a_reply.c b/ares_parse_a_reply.c index 4bd0845..0422bd3 100644 --- a/ares_parse_a_reply.c +++ b/ares_parse_a_reply.c @@ -16,9 +16,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -41,8 +38,6 @@ # include <strings.h> #endif -#include <stdlib.h> -#include <string.h> #ifdef HAVE_LIMITS_H # include <limits.h> #endif @@ -90,7 +85,7 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, return status; if (aptr + len + QFIXEDSZ > abuf + alen) { - free(hostname); + ares_free(hostname); return ARES_EBADRESP; } aptr += len + QFIXEDSZ; @@ -99,17 +94,17 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, { /* Allocate addresses and aliases; ancount gives an upper bound for both. */ - addrs = malloc(ancount * sizeof(struct in_addr)); + addrs = ares_malloc(ancount * sizeof(struct in_addr)); if (!addrs) { - free(hostname); + ares_free(hostname); return ARES_ENOMEM; } - aliases = malloc((ancount + 1) * sizeof(char *)); + aliases = ares_malloc((ancount + 1) * sizeof(char *)); if (!aliases) { - free(hostname); - free(addrs); + ares_free(hostname); + ares_free(addrs); return ARES_ENOMEM; } } @@ -132,7 +127,7 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, aptr += len; if (aptr + RRFIXEDSZ > abuf + alen) { - free(rr_name); + ares_free(rr_name); status = ARES_EBADRESP; break; } @@ -141,6 +136,12 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, rr_len = DNS_RR_LEN(aptr); rr_ttl = DNS_RR_TTL(aptr); aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + ares_free(rr_name); + status = ARES_EBADRESP; + break; + } if (rr_class == C_IN && rr_type == T_A && rr_len == sizeof(struct in_addr) @@ -149,22 +150,22 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, if (addrs) { if (aptr + sizeof(struct in_addr) > abuf + alen) - { - free(rr_name); + { /* LCOV_EXCL_START: already checked above */ + ares_free(rr_name); status = ARES_EBADRESP; break; - } + } /* LCOV_EXCL_STOP */ memcpy(&addrs[naddrs], aptr, sizeof(struct in_addr)); } if (naddrs < max_addr_ttls) { struct ares_addrttl * const at = &addrttls[naddrs]; if (aptr + sizeof(struct in_addr) > abuf + alen) - { - free(rr_name); + { /* LCOV_EXCL_START: already checked above */ + ares_free(rr_name); status = ARES_EBADRESP; break; - } + } /* LCOV_EXCL_STOP */ memcpy(&at->ipaddr, aptr, sizeof(struct in_addr)); at->ttl = rr_ttl; } @@ -178,7 +179,7 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, if (aliases) aliases[naliases] = rr_name; else - free(rr_name); + ares_free(rr_name); naliases++; /* Decode the RR data and replace the hostname with it. */ @@ -186,7 +187,7 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, &len); if (status != ARES_SUCCESS) break; - free(hostname); + ares_free(hostname); hostname = rr_data; /* Take the min of the TTLs we see in the CNAME chain. */ @@ -194,14 +195,14 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, cname_ttl = rr_ttl; } else - free(rr_name); + ares_free(rr_name); aptr += rr_len; if (aptr > abuf + alen) - { + { /* LCOV_EXCL_START: already checked above */ status = ARES_EBADRESP; break; - } + } /* LCOV_EXCL_STOP */ } if (status == ARES_SUCCESS && naddrs == 0 && naliases == 0) @@ -227,10 +228,10 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, if (host) { /* Allocate memory to build the host entry. */ - hostent = malloc(sizeof(struct hostent)); + hostent = ares_malloc(sizeof(struct hostent)); if (hostent) { - hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *)); + hostent->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *)); if (hostent->h_addr_list) { /* Fill in the hostent and return successfully. */ @@ -242,11 +243,11 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, hostent->h_addr_list[i] = (char *) &addrs[i]; hostent->h_addr_list[naddrs] = NULL; if (!naddrs && addrs) - free(addrs); + ares_free(addrs); *host = hostent; return ARES_SUCCESS; } - free(hostent); + ares_free(hostent); } status = ARES_ENOMEM; } @@ -254,10 +255,10 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, if (aliases) { for (i = 0; i < naliases; i++) - free(aliases[i]); - free(aliases); + ares_free(aliases[i]); + ares_free(aliases); } - free(addrs); - free(hostname); + ares_free(addrs); + ares_free(hostname); return status; } diff --git a/ares_parse_aaaa_reply.c b/ares_parse_aaaa_reply.c index 1fbe838..5b38bb5 100644 --- a/ares_parse_aaaa_reply.c +++ b/ares_parse_aaaa_reply.c @@ -17,9 +17,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -42,15 +39,13 @@ # include <strings.h> #endif -#include <stdlib.h> -#include <string.h> #ifdef HAVE_LIMITS_H # include <limits.h> #endif #include "ares.h" #include "ares_dns.h" -#include "inet_net_pton.h" +#include "ares_inet_net_pton.h" #include "ares_private.h" int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, @@ -92,7 +87,7 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, return status; if (aptr + len + QFIXEDSZ > abuf + alen) { - free(hostname); + ares_free(hostname); return ARES_EBADRESP; } aptr += len + QFIXEDSZ; @@ -100,17 +95,17 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, /* Allocate addresses and aliases; ancount gives an upper bound for both. */ if (host) { - addrs = malloc(ancount * sizeof(struct ares_in6_addr)); + addrs = ares_malloc(ancount * sizeof(struct ares_in6_addr)); if (!addrs) { - free(hostname); + ares_free(hostname); return ARES_ENOMEM; } - aliases = malloc((ancount + 1) * sizeof(char *)); + aliases = ares_malloc((ancount + 1) * sizeof(char *)); if (!aliases) { - free(hostname); - free(addrs); + ares_free(hostname); + ares_free(addrs); return ARES_ENOMEM; } } @@ -132,7 +127,7 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, aptr += len; if (aptr + RRFIXEDSZ > abuf + alen) { - free(rr_name); + ares_free(rr_name); status = ARES_EBADRESP; break; } @@ -141,6 +136,12 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, rr_len = DNS_RR_LEN(aptr); rr_ttl = DNS_RR_TTL(aptr); aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + ares_free(rr_name); + status = ARES_EBADRESP; + break; + } if (rr_class == C_IN && rr_type == T_AAAA && rr_len == sizeof(struct ares_in6_addr) @@ -149,22 +150,22 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, if (addrs) { if (aptr + sizeof(struct ares_in6_addr) > abuf + alen) - { - free(rr_name); + { /* LCOV_EXCL_START: already checked above */ + ares_free(rr_name); status = ARES_EBADRESP; break; - } + } /* LCOV_EXCL_STOP */ memcpy(&addrs[naddrs], aptr, sizeof(struct ares_in6_addr)); } if (naddrs < max_addr_ttls) { struct ares_addr6ttl * const at = &addrttls[naddrs]; if (aptr + sizeof(struct ares_in6_addr) > abuf + alen) - { - free(rr_name); + { /* LCOV_EXCL_START: already checked above */ + ares_free(rr_name); status = ARES_EBADRESP; break; - } + } /* LCOV_EXCL_STOP */ memcpy(&at->ip6addr, aptr, sizeof(struct ares_in6_addr)); at->ttl = rr_ttl; } @@ -178,7 +179,7 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, if (aliases) aliases[naliases] = rr_name; else - free(rr_name); + ares_free(rr_name); naliases++; /* Decode the RR data and replace the hostname with it. */ @@ -186,7 +187,7 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, &len); if (status != ARES_SUCCESS) break; - free(hostname); + ares_free(hostname); hostname = rr_data; /* Take the min of the TTLs we see in the CNAME chain. */ @@ -194,17 +195,19 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, cname_ttl = rr_ttl; } else - free(rr_name); + ares_free(rr_name); aptr += rr_len; if (aptr > abuf + alen) - { + { /* LCOV_EXCL_START: already checked above */ status = ARES_EBADRESP; break; - } + } /* LCOV_EXCL_STOP */ } - if (status == ARES_SUCCESS && naddrs == 0) + /* the check for naliases to be zero is to make sure CNAME responses + don't get caught here */ + if (status == ARES_SUCCESS && naddrs == 0 && naliases == 0) status = ARES_ENODATA; if (status == ARES_SUCCESS) { @@ -225,10 +228,10 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, if (host) { /* Allocate memory to build the host entry. */ - hostent = malloc(sizeof(struct hostent)); + hostent = ares_malloc(sizeof(struct hostent)); if (hostent) { - hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *)); + hostent->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *)); if (hostent->h_addr_list) { /* Fill in the hostent and return successfully. */ @@ -239,10 +242,12 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, for (i = 0; i < naddrs; i++) hostent->h_addr_list[i] = (char *) &addrs[i]; hostent->h_addr_list[naddrs] = NULL; + if (!naddrs && addrs) + ares_free(addrs); *host = hostent; return ARES_SUCCESS; } - free(hostent); + ares_free(hostent); } status = ARES_ENOMEM; } @@ -250,10 +255,10 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, if (aliases) { for (i = 0; i < naliases; i++) - free(aliases[i]); - free(aliases); + ares_free(aliases[i]); + ares_free(aliases); } - free(addrs); - free(hostname); + ares_free(addrs); + ares_free(hostname); return status; } diff --git a/ares_parse_mx_reply.c b/ares_parse_mx_reply.c index 2180054..e633647 100644 --- a/ares_parse_mx_reply.c +++ b/ares_parse_mx_reply.c @@ -17,9 +17,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -38,8 +35,6 @@ # include <arpa/nameser_compat.h> #endif -#include <stdlib.h> -#include <string.h> #include "ares.h" #include "ares_dns.h" #include "ares_data.h" @@ -81,7 +76,7 @@ ares_parse_mx_reply (const unsigned char *abuf, int alen, if (aptr + len + QFIXEDSZ > abuf + alen) { - free (hostname); + ares_free (hostname); return ARES_EBADRESP; } aptr += len + QFIXEDSZ; @@ -105,6 +100,11 @@ ares_parse_mx_reply (const unsigned char *abuf, int alen, rr_class = DNS_RR_CLASS (aptr); rr_len = DNS_RR_LEN (aptr); aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + status = ARES_EBADRESP; + break; + } /* Check if we are really looking at a MX record */ if (rr_class == C_IN && rr_type == T_MX) @@ -143,7 +143,7 @@ ares_parse_mx_reply (const unsigned char *abuf, int alen, } /* Don't lose memory in the next iteration */ - free (rr_name); + ares_free (rr_name); rr_name = NULL; /* Move on to the next record */ @@ -151,9 +151,9 @@ ares_parse_mx_reply (const unsigned char *abuf, int alen, } if (hostname) - free (hostname); + ares_free (hostname); if (rr_name) - free (rr_name); + ares_free (rr_name); /* clean up on error */ if (status != ARES_SUCCESS) diff --git a/ares_parse_naptr_reply.c b/ares_parse_naptr_reply.c new file mode 100644 index 0000000..717d355 --- /dev/null +++ b/ares_parse_naptr_reply.c @@ -0,0 +1,193 @@ + +/* Copyright 1998 by the Massachusetts Institute of Technology. + * Copyright (C) 2009 by Jakub Hrozek <jhrozek@redhat.com> + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif +#ifdef HAVE_NETDB_H +# include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +# include <arpa/inet.h> +#endif +#ifdef HAVE_ARPA_NAMESER_H +# include <arpa/nameser.h> +#else +# include "nameser.h" +#endif +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +# include <arpa/nameser_compat.h> +#endif + +#include "ares.h" +#include "ares_dns.h" +#include "ares_data.h" +#include "ares_private.h" + +/* AIX portability check */ +#ifndef T_NAPTR + #define T_NAPTR 35 /* naming authority pointer */ +#endif + +int +ares_parse_naptr_reply (const unsigned char *abuf, int alen, + struct ares_naptr_reply **naptr_out) +{ + unsigned int qdcount, ancount, i; + const unsigned char *aptr, *vptr; + int status, rr_type, rr_class, rr_len; + long len; + char *hostname = NULL, *rr_name = NULL; + struct ares_naptr_reply *naptr_head = NULL; + struct ares_naptr_reply *naptr_last = NULL; + struct ares_naptr_reply *naptr_curr; + + /* Set *naptr_out to NULL for all failure cases. */ + *naptr_out = NULL; + + /* Give up if abuf doesn't have room for a header. */ + if (alen < HFIXEDSZ) + return ARES_EBADRESP; + + /* Fetch the question and answer count from the header. */ + qdcount = DNS_HEADER_QDCOUNT (abuf); + ancount = DNS_HEADER_ANCOUNT (abuf); + if (qdcount != 1) + return ARES_EBADRESP; + if (ancount == 0) + return ARES_ENODATA; + + /* Expand the name from the question, and skip past the question. */ + aptr = abuf + HFIXEDSZ; + status = ares_expand_name (aptr, abuf, alen, &hostname, &len); + if (status != ARES_SUCCESS) + return status; + + if (aptr + len + QFIXEDSZ > abuf + alen) + { + ares_free (hostname); + return ARES_EBADRESP; + } + aptr += len + QFIXEDSZ; + + /* Examine each answer resource record (RR) in turn. */ + for (i = 0; i < ancount; i++) + { + /* Decode the RR up to the data field. */ + status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); + if (status != ARES_SUCCESS) + { + break; + } + aptr += len; + if (aptr + RRFIXEDSZ > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + rr_type = DNS_RR_TYPE (aptr); + rr_class = DNS_RR_CLASS (aptr); + rr_len = DNS_RR_LEN (aptr); + aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + /* RR must contain at least 7 bytes = 2 x int16 + 3 x name */ + if (rr_len < 7) + { + status = ARES_EBADRESP; + break; + } + + /* Check if we are really looking at a NAPTR record */ + if (rr_class == C_IN && rr_type == T_NAPTR) + { + /* parse the NAPTR record itself */ + + /* Allocate storage for this NAPTR answer appending it to the list */ + naptr_curr = ares_malloc_data(ARES_DATATYPE_NAPTR_REPLY); + if (!naptr_curr) + { + status = ARES_ENOMEM; + break; + } + if (naptr_last) + { + naptr_last->next = naptr_curr; + } + else + { + naptr_head = naptr_curr; + } + naptr_last = naptr_curr; + + vptr = aptr; + naptr_curr->order = DNS__16BIT(vptr); + vptr += sizeof(unsigned short); + naptr_curr->preference = DNS__16BIT(vptr); + vptr += sizeof(unsigned short); + + status = ares_expand_string(vptr, abuf, alen, &naptr_curr->flags, &len); + if (status != ARES_SUCCESS) + break; + vptr += len; + + status = ares_expand_string(vptr, abuf, alen, &naptr_curr->service, &len); + if (status != ARES_SUCCESS) + break; + vptr += len; + + status = ares_expand_string(vptr, abuf, alen, &naptr_curr->regexp, &len); + if (status != ARES_SUCCESS) + break; + vptr += len; + + status = ares_expand_name(vptr, abuf, alen, &naptr_curr->replacement, &len); + if (status != ARES_SUCCESS) + break; + } + + /* Don't lose memory in the next iteration */ + ares_free (rr_name); + rr_name = NULL; + + /* Move on to the next record */ + aptr += rr_len; + } + + if (hostname) + ares_free (hostname); + if (rr_name) + ares_free (rr_name); + + /* clean up on error */ + if (status != ARES_SUCCESS) + { + if (naptr_head) + ares_free_data (naptr_head); + return status; + } + + /* everything looks fine, return the data */ + *naptr_out = naptr_head; + + return ARES_SUCCESS; +} diff --git a/ares_parse_ns_reply.c b/ares_parse_ns_reply.c index 5e9af71..7bb5142 100644 --- a/ares_parse_ns_reply.c +++ b/ares_parse_ns_reply.c @@ -20,9 +20,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -41,8 +38,6 @@ # include <arpa/nameser_compat.h> #endif -#include <stdlib.h> -#include <string.h> #include "ares.h" #include "ares_dns.h" #include "ares_private.h" @@ -78,16 +73,16 @@ int ares_parse_ns_reply( const unsigned char* abuf, int alen, return status; if ( aptr + len + QFIXEDSZ > abuf + alen ) { - free( hostname ); + ares_free( hostname ); return ARES_EBADRESP; } aptr += len + QFIXEDSZ; /* Allocate nameservers array; ancount gives an upper bound */ - nameservers = malloc( ( ancount + 1 ) * sizeof( char * ) ); + nameservers = ares_malloc( ( ancount + 1 ) * sizeof( char * ) ); if ( !nameservers ) { - free( hostname ); + ares_free( hostname ); return ARES_ENOMEM; } nameservers_num = 0; @@ -103,13 +98,19 @@ int ares_parse_ns_reply( const unsigned char* abuf, int alen, if ( aptr + RRFIXEDSZ > abuf + alen ) { status = ARES_EBADRESP; - free(rr_name); + ares_free(rr_name); break; } rr_type = DNS_RR_TYPE( aptr ); rr_class = DNS_RR_CLASS( aptr ); rr_len = DNS_RR_LEN( aptr ); aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + ares_free(rr_name); + status = ARES_EBADRESP; + break; + } if ( rr_class == C_IN && rr_type == T_NS ) { @@ -118,33 +119,33 @@ int ares_parse_ns_reply( const unsigned char* abuf, int alen, &len); if ( status != ARES_SUCCESS ) { - free(rr_name); + ares_free(rr_name); break; } - nameservers[nameservers_num] = malloc(strlen(rr_data)+1); + nameservers[nameservers_num] = ares_malloc(strlen(rr_data)+1); if (nameservers[nameservers_num]==NULL) { - free(rr_name); - free(rr_data); + ares_free(rr_name); + ares_free(rr_data); status=ARES_ENOMEM; break; } strcpy(nameservers[nameservers_num],rr_data); - free(rr_data); + ares_free(rr_data); nameservers_num++; } - free( rr_name ); + ares_free( rr_name ); aptr += rr_len; if ( aptr > abuf + alen ) - { + { /* LCOV_EXCL_START: already checked above */ status = ARES_EBADRESP; break; - } + } /* LCOV_EXCL_STOP */ } if ( status == ARES_SUCCESS && nameservers_num == 0 ) @@ -155,10 +156,10 @@ int ares_parse_ns_reply( const unsigned char* abuf, int alen, { /* We got our answer. Allocate memory to build the host entry. */ nameservers[nameservers_num] = NULL; - hostent = malloc( sizeof( struct hostent ) ); + hostent = ares_malloc( sizeof( struct hostent ) ); if ( hostent ) { - hostent->h_addr_list = malloc( 1 * sizeof( char * ) ); + hostent->h_addr_list = ares_malloc( 1 * sizeof( char * ) ); if ( hostent->h_addr_list ) { /* Fill in the hostent and return successfully. */ @@ -170,13 +171,13 @@ int ares_parse_ns_reply( const unsigned char* abuf, int alen, *host = hostent; return ARES_SUCCESS; } - free( hostent ); + ares_free( hostent ); } status = ARES_ENOMEM; } for ( i = 0; i < nameservers_num; i++ ) - free( nameservers[i] ); - free( nameservers ); - free( hostname ); + ares_free( nameservers[i] ); + ares_free( nameservers ); + ares_free( hostname ); return status; } diff --git a/ares_parse_ptr_reply.c b/ares_parse_ptr_reply.c index 3b6dbc3..976a531 100644 --- a/ares_parse_ptr_reply.c +++ b/ares_parse_ptr_reply.c @@ -16,9 +16,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -38,10 +35,9 @@ # include <strings.h> #endif -#include <stdlib.h> -#include <string.h> #include "ares.h" #include "ares_dns.h" +#include "ares_nowarn.h" #include "ares_private.h" int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, @@ -77,17 +73,17 @@ int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, return status; if (aptr + len + QFIXEDSZ > abuf + alen) { - free(ptrname); + ares_free(ptrname); return ARES_EBADRESP; } aptr += len + QFIXEDSZ; /* Examine each answer resource record (RR) in turn. */ hostname = NULL; - aliases = malloc(alias_alloc * sizeof(char *)); + aliases = ares_malloc(alias_alloc * sizeof(char *)); if (!aliases) { - free(ptrname); + ares_free(ptrname); return ARES_ENOMEM; } for (i = 0; i < (int)ancount; i++) @@ -99,7 +95,7 @@ int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, aptr += len; if (aptr + RRFIXEDSZ > abuf + alen) { - free(rr_name); + ares_free(rr_name); status = ARES_EBADRESP; break; } @@ -107,6 +103,12 @@ int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, rr_class = DNS_RR_CLASS(aptr); rr_len = DNS_RR_LEN(aptr); aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + ares_free(rr_name); + status = ARES_EBADRESP; + break; + } if (rr_class == C_IN && rr_type == T_PTR && strcasecmp(rr_name, ptrname) == 0) @@ -116,16 +118,16 @@ int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, &len); if (status != ARES_SUCCESS) { - free(rr_name); + ares_free(rr_name); break; } if (hostname) - free(hostname); + ares_free(hostname); hostname = rr_data; - aliases[aliascnt] = malloc((strlen(rr_data)+1) * sizeof(char)); + aliases[aliascnt] = ares_malloc((strlen(rr_data)+1) * sizeof(char)); if (!aliases[aliascnt]) { - free(rr_name); + ares_free(rr_name); status = ARES_ENOMEM; break; } @@ -134,9 +136,9 @@ int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, if (aliascnt >= alias_alloc) { char **ptr; alias_alloc *= 2; - ptr = realloc(aliases, alias_alloc * sizeof(char *)); + ptr = ares_realloc(aliases, alias_alloc * sizeof(char *)); if(!ptr) { - free(rr_name); + ares_free(rr_name); status = ARES_ENOMEM; break; } @@ -151,20 +153,20 @@ int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, &len); if (status != ARES_SUCCESS) { - free(rr_name); + ares_free(rr_name); break; } - free(ptrname); + ares_free(ptrname); ptrname = rr_data; } - free(rr_name); + ares_free(rr_name); aptr += rr_len; if (aptr > abuf + alen) - { + { /* LCOV_EXCL_START: already checked above */ status = ARES_EBADRESP; break; - } + } /* LCOV_EXCL_STOP */ } if (status == ARES_SUCCESS && !hostname) @@ -172,16 +174,16 @@ int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, if (status == ARES_SUCCESS) { /* We got our answer. Allocate memory to build the host entry. */ - hostent = malloc(sizeof(struct hostent)); + hostent = ares_malloc(sizeof(struct hostent)); if (hostent) { - hostent->h_addr_list = malloc(2 * sizeof(char *)); + hostent->h_addr_list = ares_malloc(2 * sizeof(char *)); if (hostent->h_addr_list) { - hostent->h_addr_list[0] = malloc(addrlen); + hostent->h_addr_list[0] = ares_malloc(addrlen); if (hostent->h_addr_list[0]) { - hostent->h_aliases = malloc((aliascnt+1) * sizeof (char *)); + hostent->h_aliases = ares_malloc((aliascnt+1) * sizeof (char *)); if (hostent->h_aliases) { /* Fill in the hostent and return successfully. */ @@ -189,29 +191,29 @@ int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, for (i=0 ; i<aliascnt ; i++) hostent->h_aliases[i] = aliases[i]; hostent->h_aliases[aliascnt] = NULL; - hostent->h_addrtype = family; - hostent->h_length = addrlen; + hostent->h_addrtype = aresx_sitoss(family); + hostent->h_length = aresx_sitoss(addrlen); memcpy(hostent->h_addr_list[0], addr, addrlen); hostent->h_addr_list[1] = NULL; *host = hostent; - free(aliases); - free(ptrname); + ares_free(aliases); + ares_free(ptrname); return ARES_SUCCESS; } - free(hostent->h_addr_list[0]); + ares_free(hostent->h_addr_list[0]); } - free(hostent->h_addr_list); + ares_free(hostent->h_addr_list); } - free(hostent); + ares_free(hostent); } status = ARES_ENOMEM; } for (i=0 ; i<aliascnt ; i++) - if (aliases[i]) - free(aliases[i]); - free(aliases); + if (aliases[i]) + ares_free(aliases[i]); + ares_free(aliases); if (hostname) - free(hostname); - free(ptrname); + ares_free(hostname); + ares_free(ptrname); return status; } diff --git a/ares_parse_soa_reply.c b/ares_parse_soa_reply.c new file mode 100644 index 0000000..35af0a7 --- /dev/null +++ b/ares_parse_soa_reply.c @@ -0,0 +1,133 @@ + +/* Copyright 1998 by the Massachusetts Institute of Technology. + * Copyright (C) 2012 Marko Kreen <markokr@gmail.com> + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif +#ifdef HAVE_NETDB_H +# include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +# include <arpa/inet.h> +#endif +#ifdef HAVE_ARPA_NAMESER_H +# include <arpa/nameser.h> +#else +# include "nameser.h" +#endif +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +# include <arpa/nameser_compat.h> +#endif + +#include "ares.h" +#include "ares_dns.h" +#include "ares_data.h" +#include "ares_private.h" + +int +ares_parse_soa_reply(const unsigned char *abuf, int alen, + struct ares_soa_reply **soa_out) +{ + const unsigned char *aptr; + long len; + char *qname = NULL, *rr_name = NULL; + struct ares_soa_reply *soa = NULL; + int qdcount, ancount; + int status; + + if (alen < HFIXEDSZ) + return ARES_EBADRESP; + + /* parse message header */ + qdcount = DNS_HEADER_QDCOUNT(abuf); + ancount = DNS_HEADER_ANCOUNT(abuf); + if (qdcount != 1 || ancount != 1) + return ARES_EBADRESP; + aptr = abuf + HFIXEDSZ; + + /* query name */ + status = ares__expand_name_for_response(aptr, abuf, alen, &qname, &len); + if (status != ARES_SUCCESS) + goto failed_stat; + aptr += len; + + /* skip qtype & qclass */ + if (aptr + QFIXEDSZ > abuf + alen) + goto failed; + aptr += QFIXEDSZ; + + /* rr_name */ + status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len); + if (status != ARES_SUCCESS) + goto failed_stat; + aptr += len; + + /* skip rr_type, rr_class, rr_ttl, rr_rdlen */ + if (aptr + RRFIXEDSZ > abuf + alen) + goto failed; + aptr += RRFIXEDSZ; + + /* allocate result struct */ + soa = ares_malloc_data(ARES_DATATYPE_SOA_REPLY); + if (!soa) + { + status = ARES_ENOMEM; + goto failed_stat; + } + + /* nsname */ + status = ares__expand_name_for_response(aptr, abuf, alen, &soa->nsname, &len); + if (status != ARES_SUCCESS) + goto failed_stat; + aptr += len; + + /* hostmaster */ + status = ares__expand_name_for_response(aptr, abuf, alen, &soa->hostmaster, &len); + if (status != ARES_SUCCESS) + goto failed_stat; + aptr += len; + + /* integer fields */ + if (aptr + 5 * 4 > abuf + alen) + goto failed; + soa->serial = DNS__32BIT(aptr + 0 * 4); + soa->refresh = DNS__32BIT(aptr + 1 * 4); + soa->retry = DNS__32BIT(aptr + 2 * 4); + soa->expire = DNS__32BIT(aptr + 3 * 4); + soa->minttl = DNS__32BIT(aptr + 4 * 4); + + ares_free(qname); + ares_free(rr_name); + + *soa_out = soa; + + return ARES_SUCCESS; + +failed: + status = ARES_EBADRESP; + +failed_stat: + ares_free_data(soa); + if (qname) + ares_free(qname); + if (rr_name) + ares_free(rr_name); + return status; +} + diff --git a/ares_parse_srv_reply.c b/ares_parse_srv_reply.c index 9c7eb6e..824ff3a 100644 --- a/ares_parse_srv_reply.c +++ b/ares_parse_srv_reply.c @@ -17,9 +17,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -38,8 +35,6 @@ # include <arpa/nameser_compat.h> #endif -#include <stdlib.h> -#include <string.h> #include "ares.h" #include "ares_dns.h" #include "ares_data.h" @@ -86,7 +81,7 @@ ares_parse_srv_reply (const unsigned char *abuf, int alen, if (aptr + len + QFIXEDSZ > abuf + alen) { - free (hostname); + ares_free (hostname); return ARES_EBADRESP; } aptr += len + QFIXEDSZ; @@ -110,6 +105,11 @@ ares_parse_srv_reply (const unsigned char *abuf, int alen, rr_class = DNS_RR_CLASS (aptr); rr_len = DNS_RR_LEN (aptr); aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + status = ARES_EBADRESP; + break; + } /* Check if we are really looking at a SRV record */ if (rr_class == C_IN && rr_type == T_SRV) @@ -152,7 +152,7 @@ ares_parse_srv_reply (const unsigned char *abuf, int alen, } /* Don't lose memory in the next iteration */ - free (rr_name); + ares_free (rr_name); rr_name = NULL; /* Move on to the next record */ @@ -160,9 +160,9 @@ ares_parse_srv_reply (const unsigned char *abuf, int alen, } if (hostname) - free (hostname); + ares_free (hostname); if (rr_name) - free (rr_name); + ares_free (rr_name); /* clean up on error */ if (status != ARES_SUCCESS) diff --git a/ares_parse_txt_reply.c b/ares_parse_txt_reply.c index 5165332..4856b4c 100644 --- a/ares_parse_txt_reply.c +++ b/ares_parse_txt_reply.c @@ -17,9 +17,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -42,28 +39,25 @@ # include <strings.h> #endif -#include <stdlib.h> -#include <string.h> - #include "ares.h" #include "ares_dns.h" #include "ares_data.h" #include "ares_private.h" -int -ares_parse_txt_reply (const unsigned char *abuf, int alen, - struct ares_txt_reply **txt_out) +static int +ares__parse_txt_reply (const unsigned char *abuf, int alen, + int ex, void **txt_out) { - size_t substr_len, str_len; + size_t substr_len; unsigned int qdcount, ancount, i; const unsigned char *aptr; const unsigned char *strptr; int status, rr_type, rr_class, rr_len; long len; char *hostname = NULL, *rr_name = NULL; - struct ares_txt_reply *txt_head = NULL; - struct ares_txt_reply *txt_last = NULL; - struct ares_txt_reply *txt_curr; + struct ares_txt_ext *txt_head = NULL; + struct ares_txt_ext *txt_last = NULL; + struct ares_txt_ext *txt_curr; /* Set *txt_out to NULL for all failure cases. */ *txt_out = NULL; @@ -88,7 +82,7 @@ ares_parse_txt_reply (const unsigned char *abuf, int alen, if (aptr + len + QFIXEDSZ > abuf + alen) { - free (hostname); + ares_free (hostname); return ARES_EBADRESP; } aptr += len + QFIXEDSZ; @@ -112,27 +106,15 @@ ares_parse_txt_reply (const unsigned char *abuf, int alen, rr_class = DNS_RR_CLASS (aptr); rr_len = DNS_RR_LEN (aptr); aptr += RRFIXEDSZ; + if (aptr + rr_len > abuf + alen) + { + status = ARES_EBADRESP; + break; + } /* Check if we are really looking at a TXT record */ if (rr_class == C_IN && rr_type == T_TXT) { - /* Allocate storage for this TXT answer appending it to the list */ - txt_curr = ares_malloc_data(ARES_DATATYPE_TXT_REPLY); - if (!txt_curr) - { - status = ARES_ENOMEM; - break; - } - if (txt_last) - { - txt_last->next = txt_curr; - } - else - { - txt_head = txt_curr; - } - txt_last = txt_curr; - /* * There may be multiple substrings in a single TXT record. Each * substring may be up to 255 characters in length, with a @@ -141,40 +123,62 @@ ares_parse_txt_reply (const unsigned char *abuf, int alen, * substrings contained therein. */ - /* Compute total length to allow a single memory allocation */ strptr = aptr; while (strptr < (aptr + rr_len)) { substr_len = (unsigned char)*strptr; - txt_curr->length += substr_len; - strptr += substr_len + 1; - } - - /* Including null byte */ - txt_curr->txt = malloc (txt_curr->length + 1); - if (txt_curr->txt == NULL) - { - status = ARES_ENOMEM; - break; - } + if (strptr + substr_len + 1 > aptr + rr_len) + { + status = ARES_EBADRESP; + break; + } + + /* Allocate storage for this TXT answer appending it to the list */ + txt_curr = ares_malloc_data(ex ? ARES_DATATYPE_TXT_EXT : + ARES_DATATYPE_TXT_REPLY); + if (!txt_curr) + { + status = ARES_ENOMEM; + break; + } + if (txt_last) + { + txt_last->next = txt_curr; + } + else + { + txt_head = txt_curr; + } + txt_last = txt_curr; + + if (ex) + txt_curr->record_start = (strptr == aptr); + txt_curr->length = substr_len; + txt_curr->txt = ares_malloc (substr_len + 1/* Including null byte */); + if (txt_curr->txt == NULL) + { + status = ARES_ENOMEM; + break; + } + + ++strptr; + memcpy ((char *) txt_curr->txt, strptr, substr_len); + + /* Make sure we NULL-terminate */ + txt_curr->txt[substr_len] = 0; - /* Step through the list of substrings, concatenating them */ - str_len = 0; - strptr = aptr; - while (strptr < (aptr + rr_len)) - { - substr_len = (unsigned char)*strptr; - strptr++; - memcpy ((char *) txt_curr->txt + str_len, strptr, substr_len); - str_len += substr_len; strptr += substr_len; } - /* Make sure we NULL-terminate */ - *((char *) txt_curr->txt + txt_curr->length) = '\0'; + } + + /* Propagate any failures */ + if (status != ARES_SUCCESS) + { + break; } /* Don't lose memory in the next iteration */ - free (rr_name); + ares_free (rr_name); rr_name = NULL; /* Move on to the next record */ @@ -182,9 +186,9 @@ ares_parse_txt_reply (const unsigned char *abuf, int alen, } if (hostname) - free (hostname); + ares_free (hostname); if (rr_name) - free (rr_name); + ares_free (rr_name); /* clean up on error */ if (status != ARES_SUCCESS) @@ -199,3 +203,18 @@ ares_parse_txt_reply (const unsigned char *abuf, int alen, return ARES_SUCCESS; } + +int +ares_parse_txt_reply (const unsigned char *abuf, int alen, + struct ares_txt_reply **txt_out) +{ + return ares__parse_txt_reply(abuf, alen, 0, (void **) txt_out); +} + + +int +ares_parse_txt_reply_ext (const unsigned char *abuf, int alen, + struct ares_txt_ext **txt_out) +{ + return ares__parse_txt_reply(abuf, alen, 1, (void **) txt_out); +} diff --git a/ares_private.h b/ares_private.h index ff45ba7..5d77386 100644 --- a/ares_private.h +++ b/ares_private.h @@ -26,9 +26,6 @@ #define WIN32 #endif -#include <stdio.h> -#include <sys/types.h> - #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -40,24 +37,33 @@ #define HAVE_WRITEV 1 #endif -#ifdef NETWARE -#include <time.h> -#endif - #define DEFAULT_TIMEOUT 5000 /* milliseconds */ #define DEFAULT_TRIES 4 #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff #endif +#ifdef CARES_EXPOSE_STATICS +/* Make some internal functions visible for testing */ +#define STATIC_TESTABLE +#else +#define STATIC_TESTABLE static +#endif + #if defined(WIN32) && !defined(WATT32) #define WIN_NS_9X "System\\CurrentControlSet\\Services\\VxD\\MSTCP" #define WIN_NS_NT_KEY "System\\CurrentControlSet\\Services\\Tcpip\\Parameters" +#define WIN_DNSCLIENT "Software\\Policies\\Microsoft\\System\\DNSClient" #define NAMESERVER "NameServer" #define DHCPNAMESERVER "DhcpNameServer" #define DATABASEPATH "DatabasePath" #define WIN_PATH_HOSTS "\\hosts" +#define SEARCHLIST_KEY "SearchList" +#define PRIMARYDNSSUFFIX_KEY "PrimaryDNSSuffix" +#define INTERFACES_KEY "Interfaces" +#define DOMAIN_KEY "Domain" +#define DHCPDOMAIN_KEY "DhcpDomain" #elif defined(WATT32) @@ -93,10 +99,7 @@ # define getenv(ptr) ares_getenv(ptr) #endif -#ifndef HAVE_STRDUP -# include "ares_strdup.h" -# define strdup(ptr) ares_strdup(ptr) -#endif +#include "ares_strdup.h" #ifndef HAVE_STRCASECMP # include "ares_strcasecmp.h" @@ -113,12 +116,21 @@ # define writev(s,ptr,cnt) ares_writev(s,ptr,cnt) #endif +/********* EDNS defines section ******/ +#define EDNSPACKETSZ 1280 /* Reasonable UDP payload size, as suggested + in RFC2671 */ +#define MAXENDSSZ 4096 /* Maximum (local) limit for edns packet size */ +#define EDNSFIXEDSZ 11 /* Size of EDNS header */ +/********* EDNS defines section ******/ + struct ares_addr { int family; union { struct in_addr addr4; struct ares_in6_addr addr6; } addr; + int udp_port; /* stored in network order */ + int tcp_port; /* stored in network order */ }; #define addrV4 addr.addr4 #define addrV6 addr.addr6 @@ -251,8 +263,8 @@ struct ares_channeldata { int tries; int ndots; int rotate; /* if true, all servers specified are used */ - int udp_port; - int tcp_port; + int udp_port; /* stored in network order */ + int tcp_port; /* stored in network order */ int socket_send_buffer_size; int socket_receive_buffer_size; char **domains; @@ -260,6 +272,7 @@ struct ares_channeldata { struct apattern *sortlist; int nsort; char *lookups; + int ednspsz; /* For binding to local devices and/or IP addresses. Leave * them null/zero for no binding. @@ -304,20 +317,23 @@ struct ares_channeldata { ares_sock_create_callback sock_create_cb; void *sock_create_cb_data; + + ares_sock_config_callback sock_config_cb; + void *sock_config_cb_data; + + const struct ares_socket_functions * sock_funcs; + void *sock_func_cb_data; }; +/* Memory management functions */ +extern void *(*ares_malloc)(size_t size); +extern void *(*ares_realloc)(void *ptr, size_t size); +extern void (*ares_free)(void *ptr); + /* return true if now is exactly check time or later */ int ares__timedout(struct timeval *now, struct timeval *check); -/* add the specific number of milliseconds to the time in the first argument */ -int ares__timeadd(struct timeval *now, - int millisecs); -/* return time offset between now and (future) check, in milliseconds */ -long ares__timeoffset(struct timeval *now, - struct timeval *check); -/* returns ARES_SUCCESS if library has been initialized */ -int ares_library_initialized(void); -void ares__rc4(rc4_key* key,unsigned char *buffer_ptr, int buffer_len); + void ares__send_query(ares_channel channel, struct query *query, struct timeval *now); void ares__close_sockets(ares_channel channel, struct server_state *server); @@ -335,6 +351,8 @@ void ares__destroy_servers_state(ares_channel channel); long ares__tvdiff(struct timeval t1, struct timeval t2); #endif +void ares__socket_close(ares_channel, ares_socket_t); + #define ARES_SWAP_BYTE(a,b) \ { unsigned char swapByte = *(a); *(a) = *(b); *(b) = swapByte; } @@ -342,13 +360,14 @@ long ares__tvdiff(struct timeval t1, struct timeval t2); do { \ if ((c)->sock_state_cb) \ (c)->sock_state_cb((c)->sock_state_cb_data, (s), (r), (w)); \ - } while (0) + } WHILE_FALSE #ifdef CURLDEBUG /* This is low-level hard-hacking memory leak tracking and similar. Using the libcurl lowlevel code from within library is ugly and only works when c-ares is built and linked with a similarly curldebug-enabled libcurl, but we do this anyway for convenience. */ +#define HEADER_CURL_SETUP_ONCE_H #include "../lib/memdebug.h" #endif diff --git a/ares_process.c b/ares_process.c index e5efa5f..df85524 100644 --- a/ares_process.c +++ b/ares_process.c @@ -1,6 +1,6 @@ /* Copyright 1998 by the Massachusetts Institute of Technology. - * Copyright (C) 2004-2010 by Daniel Stenberg + * Copyright (C) 2004-2017 by Daniel Stenberg * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -17,9 +17,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_SYS_UIO_H # include <sys/uio.h> #endif @@ -32,6 +29,9 @@ #ifdef HAVE_NETDB_H # include <netdb.h> #endif +#ifdef HAVE_ARPA_INET_H +# include <arpa/inet.h> +#endif #ifdef HAVE_ARPA_NAMESER_H # include <arpa/nameser.h> #else @@ -41,16 +41,9 @@ # include <arpa/nameser_compat.h> #endif -#ifdef HAVE_SYS_TIME_H -# include <sys/time.h> -#endif - #ifdef HAVE_STRINGS_H # include <strings.h> #endif -#ifdef HAVE_UNISTD_H -# include <unistd.h> -#endif #ifdef HAVE_SYS_IOCTL_H # include <sys/ioctl.h> #endif @@ -59,10 +52,7 @@ #endif #include <assert.h> -#include <string.h> -#include <stdlib.h> #include <fcntl.h> -#include <time.h> #include "ares.h" #include "ares_dns.h" @@ -78,7 +68,7 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds, static void read_udp_packets(ares_channel channel, fd_set *read_fds, ares_socket_t read_fd, struct timeval *now); static void advance_tcp_send_queue(ares_channel channel, int whichserver, - ssize_t num_bytes); + ares_ssize_t num_bytes); static void process_timeouts(ares_channel channel, struct timeval *now); static void process_broken_connections(ares_channel channel, struct timeval *now); @@ -115,8 +105,7 @@ int ares__timedout(struct timeval *now, } /* add the specific number of milliseconds to the time in the first argument */ -int ares__timeadd(struct timeval *now, - int millisecs) +static void timeadd(struct timeval *now, int millisecs) { now->tv_sec += millisecs/1000; now->tv_usec += (millisecs%1000)*1000; @@ -125,19 +114,8 @@ int ares__timeadd(struct timeval *now, ++(now->tv_sec); now->tv_usec -= 1000000; } - - return 0; -} - -/* return time offset between now and (future) check, in milliseconds */ -long ares__timeoffset(struct timeval *now, - struct timeval *check) -{ - return (check->tv_sec - now->tv_sec)*1000 + - (check->tv_usec - now->tv_usec)/1000; } - /* * generic process function */ @@ -200,6 +178,26 @@ static int try_again(int errnum) return 0; } +static ares_ssize_t socket_writev(ares_channel channel, ares_socket_t s, const struct iovec * vec, int len) +{ + if (channel->sock_funcs) + return channel->sock_funcs->asendv(s, vec, len, channel->sock_func_cb_data); + + return writev(s, vec, len); +} + +static ares_ssize_t socket_write(ares_channel channel, ares_socket_t s, const void * data, size_t len) +{ + if (channel->sock_funcs) + { + struct iovec vec; + vec.iov_base = (void*)data; + vec.iov_len = len; + return channel->sock_funcs->asendv(s, &vec, 1, channel->sock_func_cb_data); + } + return swrite(s, data, len); +} + /* If any TCP sockets select true for writing, write out queued data * we have for them. */ @@ -212,8 +210,8 @@ static void write_tcp_data(ares_channel channel, struct send_request *sendreq; struct iovec *vec; int i; - ssize_t scount; - ssize_t wcount; + ares_ssize_t scount; + ares_ssize_t wcount; size_t n; if(!write_fds && (write_fd == ARES_SOCKET_BAD)) @@ -252,7 +250,7 @@ static void write_tcp_data(ares_channel channel, n++; /* Allocate iovecs so we can send all our data at once. */ - vec = malloc(n * sizeof(struct iovec)); + vec = ares_malloc(n * sizeof(struct iovec)); if (vec) { /* Fill in the iovecs and send. */ @@ -263,12 +261,12 @@ static void write_tcp_data(ares_channel channel, vec[n].iov_len = sendreq->len; n++; } - wcount = (ssize_t)writev(server->tcp_socket, vec, (int)n); - free(vec); + wcount = socket_writev(channel, server->tcp_socket, vec, (int)n); + ares_free(vec); if (wcount < 0) { if (!try_again(SOCKERRNO)) - handle_error(channel, i, now); + handle_error(channel, i, now); continue; } @@ -280,11 +278,11 @@ static void write_tcp_data(ares_channel channel, /* Can't allocate iovecs; just send the first request. */ sendreq = server->qhead; - scount = swrite(server->tcp_socket, sendreq->data, sendreq->len); + scount = socket_write(channel, server->tcp_socket, sendreq->data, sendreq->len); if (scount < 0) { if (!try_again(SOCKERRNO)) - handle_error(channel, i, now); + handle_error(channel, i, now); continue; } @@ -296,7 +294,7 @@ static void write_tcp_data(ares_channel channel, /* Consume the given number of bytes from the head of the TCP send queue. */ static void advance_tcp_send_queue(ares_channel channel, int whichserver, - ssize_t num_bytes) + ares_ssize_t num_bytes) { struct send_request *sendreq; struct server_state *server = &channel->servers[whichserver]; @@ -306,8 +304,8 @@ static void advance_tcp_send_queue(ares_channel channel, int whichserver, num_bytes -= sendreq->len; server->qhead = sendreq->next; if (sendreq->data_storage) - free(sendreq->data_storage); - free(sendreq); + ares_free(sendreq->data_storage); + ares_free(sendreq); if (server->qhead == NULL) { SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0); server->qtail = NULL; @@ -324,6 +322,38 @@ static void advance_tcp_send_queue(ares_channel channel, int whichserver, } } +static ares_ssize_t socket_recvfrom(ares_channel channel, + ares_socket_t s, + void * data, + size_t data_len, + int flags, + struct sockaddr *from, + ares_socklen_t *from_len) +{ + if (channel->sock_funcs) + return channel->sock_funcs->arecvfrom(s, data, data_len, + flags, from, from_len, + channel->sock_func_cb_data); + +#ifdef HAVE_RECVFROM + return recvfrom(s, data, data_len, flags, from, from_len); +#else + return sread(s, data, data_len); +#endif +} + +static ares_ssize_t socket_recv(ares_channel channel, + ares_socket_t s, + void * data, + size_t data_len) +{ + if (channel->sock_funcs) + return channel->sock_funcs->arecvfrom(s, data, data_len, 0, 0, 0, + channel->sock_func_cb_data); + + return sread(s, data, data_len); +} + /* If any TCP socket selects true for reading, read some data, * allocate a buffer if we finish reading the length word, and process * a packet if we finish reading one. @@ -333,7 +363,7 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds, { struct server_state *server; int i; - ssize_t count; + ares_ssize_t count; if(!read_fds && (read_fd == ARES_SOCKET_BAD)) /* no possible action */ @@ -356,11 +386,11 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds, } if(read_fds) - /* If there's an error and we close this socket, then open - * another with the same fd to talk to another server, then we - * don't want to think that it was the new socket that was - * ready. This is not disastrous, but is likely to result in - * extra system calls and confusion. */ + /* If there's an error and we close this socket, then open another + * with the same fd to talk to another server, then we don't want to + * think that it was the new socket that was ready. This is not + * disastrous, but is likely to result in extra system calls and + * confusion. */ FD_CLR(server->tcp_socket, read_fds); if (server->tcp_lenbuf_pos != 2) @@ -368,13 +398,13 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds, /* We haven't yet read a length word, so read that (or * what's left to read of it). */ - count = sread(server->tcp_socket, - server->tcp_lenbuf + server->tcp_lenbuf_pos, - 2 - server->tcp_lenbuf_pos); + count = socket_recv(channel, server->tcp_socket, + server->tcp_lenbuf + server->tcp_lenbuf_pos, + 2 - server->tcp_lenbuf_pos); if (count <= 0) { if (!(count == -1 && try_again(SOCKERRNO))) - handle_error(channel, i, now); + handle_error(channel, i, now); continue; } @@ -386,22 +416,25 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds, */ server->tcp_length = server->tcp_lenbuf[0] << 8 | server->tcp_lenbuf[1]; - server->tcp_buffer = malloc(server->tcp_length); - if (!server->tcp_buffer) + server->tcp_buffer = ares_malloc(server->tcp_length); + if (!server->tcp_buffer) { handle_error(channel, i, now); + return; /* bail out on malloc failure. TODO: make this + function return error codes */ + } server->tcp_buffer_pos = 0; } } else { /* Read data into the allocated buffer. */ - count = sread(server->tcp_socket, - server->tcp_buffer + server->tcp_buffer_pos, - server->tcp_length - server->tcp_buffer_pos); + count = socket_recv(channel, server->tcp_socket, + server->tcp_buffer + server->tcp_buffer_pos, + server->tcp_length - server->tcp_buffer_pos); if (count <= 0) { if (!(count == -1 && try_again(SOCKERRNO))) - handle_error(channel, i, now); + handle_error(channel, i, now); continue; } @@ -413,8 +446,7 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds, */ process_answer(channel, server->tcp_buffer, server->tcp_length, i, 1, now); - if (server->tcp_buffer) - free(server->tcp_buffer); + ares_free(server->tcp_buffer); server->tcp_buffer = NULL; server->tcp_lenbuf_pos = 0; server->tcp_buffer_pos = 0; @@ -429,8 +461,8 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds, { struct server_state *server; int i; - ssize_t count; - unsigned char buf[PACKETSZ + 1]; + ares_ssize_t count; + unsigned char buf[MAXENDSSZ + 1]; #ifdef HAVE_RECVFROM ares_socklen_t fromlen; union { @@ -472,25 +504,27 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds, /* To reduce event loop overhead, read and process as many * packets as we can. */ do { -#ifdef HAVE_RECVFROM - if (server->addr.family == AF_INET) - fromlen = sizeof(from.sa4); - else - fromlen = sizeof(from.sa6); - count = (ssize_t)recvfrom(server->udp_socket, (void *)buf, sizeof(buf), - 0, &from.sa, &fromlen); -#else - count = sread(server->udp_socket, buf, sizeof(buf)); -#endif + if (server->udp_socket == ARES_SOCKET_BAD) + count = 0; + + else { + if (server->addr.family == AF_INET) + fromlen = sizeof(from.sa4); + else + fromlen = sizeof(from.sa6); + count = socket_recvfrom(channel, server->udp_socket, (void *)buf, + sizeof(buf), 0, &from.sa, &fromlen); + } + if (count == -1 && try_again(SOCKERRNO)) continue; else if (count <= 0) handle_error(channel, i, now); #ifdef HAVE_RECVFROM else if (!same_address(&from.sa, &server->addr)) - /* The address the response comes from does not match - * the address we sent the request to. Someone may be - * attempting to perform a cache poisoning attack. */ + /* The address the response comes from does not match the address we + * sent the request to. Someone may be attempting to perform a cache + * poisoning attack. */ break; #endif else @@ -507,11 +541,10 @@ static void process_timeouts(ares_channel channel, struct timeval *now) struct list_node* list_head; struct list_node* list_node; - /* Process all the timeouts that have fired since the last time we - * processed timeouts. If things are going well, then we'll have - * hundreds/thousands of queries that fall into future buckets, and - * only a handful of requests that fall into the "now" bucket, so - * this should be quite quick. + /* Process all the timeouts that have fired since the last time we processed + * timeouts. If things are going well, then we'll have hundreds/thousands of + * queries that fall into future buckets, and only a handful of requests + * that fall into the "now" bucket, so this should be quite quick. */ for (t = channel->last_timeout_processed; t <= now->tv_sec; t++) { @@ -536,7 +569,7 @@ static void process_answer(ares_channel channel, unsigned char *abuf, int alen, int whichserver, int tcp, struct timeval *now) { - int tc, rcode; + int tc, rcode, packetsz; unsigned short id; struct query *query; struct list_node* list_head; @@ -553,11 +586,10 @@ static void process_answer(ares_channel channel, unsigned char *abuf, rcode = DNS_HEADER_RCODE(abuf); /* Find the query corresponding to this packet. The queries are - * hashed/bucketed by query id, so this lookup should be quick. - * Note that both the query id and the questions must be the same; - * when the query id wraps around we can have multiple outstanding - * queries with the same query id, so we need to check both the id and - * question. + * hashed/bucketed by query id, so this lookup should be quick. Note that + * both the query id and the questions must be the same; when the query id + * wraps around we can have multiple outstanding queries with the same query + * id, so we need to check both the id and question. */ query = NULL; list_head = &(channel->queries_by_qid[id % ARES_QID_TABLE_SIZE]); @@ -574,11 +606,35 @@ static void process_answer(ares_channel channel, unsigned char *abuf, if (!query) return; + packetsz = PACKETSZ; + /* If we use EDNS and server answers with one of these RCODES, the protocol + * extension is not understood by the responder. We must retry the query + * without EDNS enabled. + */ + if (channel->flags & ARES_FLAG_EDNS) + { + packetsz = channel->ednspsz; + if (rcode == NOTIMP || rcode == FORMERR || rcode == SERVFAIL) + { + int qlen = (query->tcplen - 2) - EDNSFIXEDSZ; + channel->flags ^= ARES_FLAG_EDNS; + query->tcplen -= EDNSFIXEDSZ; + query->qlen -= EDNSFIXEDSZ; + query->tcpbuf[0] = (unsigned char)((qlen >> 8) & 0xff); + query->tcpbuf[1] = (unsigned char)(qlen & 0xff); + DNS_HEADER_SET_ARCOUNT(query->tcpbuf + 2, 0); + query->tcpbuf = ares_realloc(query->tcpbuf, query->tcplen); + query->qbuf = query->tcpbuf + 2; + ares__send_query(channel, query, now); + return; + } + } + /* If we got a truncated UDP packet and are not ignoring truncation, * don't accept the packet, and switch the query to TCP if we hadn't * done so already. */ - if ((tc || alen > PACKETSZ) && !tcp && !(channel->flags & ARES_FLAG_IGNTC)) + if ((tc || alen > packetsz) && !tcp && !(channel->flags & ARES_FLAG_IGNTC)) { if (!query->using_tcp) { @@ -591,8 +647,8 @@ static void process_answer(ares_channel channel, unsigned char *abuf, /* Limit alen to PACKETSZ if we aren't using TCP (only relevant if we * are ignoring truncation. */ - if (alen > PACKETSZ && !tcp) - alen = PACKETSZ; + if (alen > packetsz && !tcp) + alen = packetsz; /* If we aren't passing through all error packets, discard packets * with SERVFAIL, NOTIMP, or REFUSED response codes. @@ -626,6 +682,31 @@ static void process_broken_connections(ares_channel channel, } } +/* Swap the contents of two lists */ +static void swap_lists(struct list_node* head_a, + struct list_node* head_b) +{ + int is_a_empty = ares__is_list_empty(head_a); + int is_b_empty = ares__is_list_empty(head_b); + struct list_node old_a = *head_a; + struct list_node old_b = *head_b; + + if (is_a_empty) { + ares__init_list_head(head_b); + } else { + *head_b = old_a; + old_a.next->prev = head_b; + old_a.prev->next = head_b; + } + if (is_b_empty) { + ares__init_list_head(head_a); + } else { + *head_a = old_b; + old_b.next->prev = head_a; + old_b.prev->next = head_a; + } +} + static void handle_error(ares_channel channel, int whichserver, struct timeval *now) { @@ -639,15 +720,14 @@ static void handle_error(ares_channel channel, int whichserver, /* Reset communications with this server. */ ares__close_sockets(channel, server); - /* Tell all queries talking to this server to move on and not try - * this server again. We steal the current list of queries that were - * in-flight to this server, since when we call next_server this can - * cause the queries to be re-sent to this server, which will - * re-insert these queries in that same server->queries_to_server - * list. + /* Tell all queries talking to this server to move on and not try this + * server again. We steal the current list of queries that were in-flight to + * this server, since when we call next_server this can cause the queries to + * be re-sent to this server, which will re-insert these queries in that + * same server->queries_to_server list. */ ares__init_list_head(&list_head); - ares__swap_lists(&list_head, &(server->queries_to_server)); + swap_lists(&list_head, &(server->queries_to_server)); for (list_node = list_head.next; list_node != &list_head; ) { query = list_node->data; @@ -663,14 +743,15 @@ static void handle_error(ares_channel channel, int whichserver, } static void skip_server(ares_channel channel, struct query *query, - int whichserver) { - /* The given server gave us problems with this query, so if we have - * the luxury of using other servers, then let's skip the - * potentially broken server and just use the others. If we only - * have one server and we need to retry then we should just go ahead - * and re-use that server, since it's our only hope; perhaps we - * just got unlucky, and retrying will work (eg, the server timed - * out our TCP connection just as we were sending another request). + int whichserver) +{ + /* The given server gave us problems with this query, so if we have the + * luxury of using other servers, then let's skip the potentially broken + * server and just use the others. If we only have one server and we need to + * retry then we should just go ahead and re-use that server, since it's our + * only hope; perhaps we just got unlucky, and retrying will work (eg, the + * server timed out our TCP connection just as we were sending another + * request). */ if (channel->nservers > 1) { @@ -693,11 +774,10 @@ static void next_server(ares_channel channel, struct query *query, query->server = (query->server + 1) % channel->nservers; server = &channel->servers[query->server]; - /* We don't want to use this server if (1) we decided this - * connection is broken, and thus about to be closed, (2) - * we've decided to skip this server because of earlier - * errors we encountered, or (3) we already sent this query - * over this exact connection. + /* We don't want to use this server if (1) we decided this connection is + * broken, and thus about to be closed, (2) we've decided to skip this + * server because of earlier errors we encountered, or (3) we already + * sent this query over this exact connection. */ if (!server->is_broken && !query->server_info[query->server].skip_server && @@ -709,11 +789,11 @@ static void next_server(ares_channel channel, struct query *query, return; } - /* You might think that with TCP we only need one try. However, - * even when using TCP, servers can time-out our connection just - * as we're sending a request, or close our connection because - * they die, or never send us a reply because they get wedged or - * tickle a bug that drops our request. + /* You might think that with TCP we only need one try. However, even + * when using TCP, servers can time-out our connection just as we're + * sending a request, or close our connection because they die, or never + * send us a reply because they get wedged or tickle a bug that drops + * our request. */ } @@ -743,17 +823,18 @@ void ares__send_query(ares_channel channel, struct query *query, return; } } - sendreq = calloc(1, sizeof(struct send_request)); + sendreq = ares_malloc(sizeof(struct send_request)); if (!sendreq) { end_query(channel, query, ARES_ENOMEM, NULL, 0); return; } - /* To make the common case fast, we avoid copies by using the - * query's tcpbuf for as long as the query is alive. In the rare - * case where the query ends while it's queued for transmission, - * then we give the sendreq its own copy of the request packet - * and put it in sendreq->data_storage. + memset(sendreq, 0, sizeof(struct send_request)); + /* To make the common case fast, we avoid copies by using the query's + * tcpbuf for as long as the query is alive. In the rare case where the + * query ends while it's queued for transmission, then we give the + * sendreq its own copy of the request packet and put it in + * sendreq->data_storage. */ sendreq->data_storage = NULL; sendreq->data = query->tcpbuf; @@ -782,7 +863,7 @@ void ares__send_query(ares_channel channel, struct query *query, return; } } - if (swrite(server->udp_socket, query->qbuf, query->qlen) == -1) + if (socket_write(channel, server->udp_socket, query->qbuf, query->qlen) == -1) { /* FIXME: Handle EAGAIN here since it likely can happen. */ skip_server(channel, query, query->server); @@ -793,8 +874,7 @@ void ares__send_query(ares_channel channel, struct query *query, timeplus = channel->timeout << (query->try_count / channel->nservers); timeplus = (timeplus * (9 + (rand () & 7))) / 16; query->timeout = *now; - ares__timeadd(&query->timeout, - timeplus); + timeadd(&query->timeout, timeplus); /* Keep track of queries bucketed by timeout, so we can process * timeout events quickly. */ @@ -818,7 +898,7 @@ void ares__send_query(ares_channel channel, struct query *query, * portable. */ static int setsocknonblock(ares_socket_t sockfd, /* operate on this */ - int nonblock /* TRUE or FALSE */) + int nonblock /* TRUE or FALSE */) { #if defined(USE_BLOCKING_SOCKETS) @@ -832,35 +912,34 @@ static int setsocknonblock(ares_socket_t sockfd, /* operate on this */ if (FALSE != nonblock) return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); else - return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); + return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); /* LCOV_EXCL_LINE */ #elif defined(HAVE_IOCTL_FIONBIO) /* older unix versions */ - int flags; - flags = nonblock; + int flags = nonblock ? 1 : 0; return ioctl(sockfd, FIONBIO, &flags); #elif defined(HAVE_IOCTLSOCKET_FIONBIO) #ifdef WATT32 - char flags; + char flags = nonblock ? 1 : 0; #else /* Windows */ - unsigned long flags; + unsigned long flags = nonblock ? 1UL : 0UL; #endif - flags = nonblock; return ioctlsocket(sockfd, FIONBIO, &flags); #elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO) /* Amiga */ - return IoctlSocket(sockfd, FIONBIO, (long)nonblock); + long flags = nonblock ? 1L : 0L; + return IoctlSocket(sockfd, FIONBIO, flags); #elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK) /* BeOS */ - long b = nonblock ? 1 : 0; + long b = nonblock ? 1L : 0L; return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); #else @@ -876,12 +955,16 @@ static int configure_socket(ares_socket_t s, int family, ares_channel channel) struct sockaddr_in6 sa6; } local; - setsocknonblock(s, TRUE); + /* do not set options for user-managed sockets */ + if (channel->sock_funcs) + return 0; + + (void)setsocknonblock(s, TRUE); #if defined(FD_CLOEXEC) && !defined(MSDOS) /* Configure the socket fd as close-on-exec. */ if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) - return -1; + return -1; /* LCOV_EXCL_LINE */ #endif /* Set the socket's send and receive buffer sizes. */ @@ -917,10 +1000,12 @@ static int configure_socket(ares_socket_t s, int family, ares_channel channel) } } else if (family == AF_INET6) { - if (memcmp(channel->local_ip6, &ares_in6addr_any, sizeof(channel->local_ip6)) != 0) { + if (memcmp(channel->local_ip6, &ares_in6addr_any, + sizeof(channel->local_ip6)) != 0) { memset(&local.sa6, 0, sizeof(local.sa6)); local.sa6.sin6_family = AF_INET6; - memcpy(&local.sa6.sin6_addr, channel->local_ip6, sizeof(channel->local_ip6)); + memcpy(&local.sa6.sin6_addr, channel->local_ip6, + sizeof(channel->local_ip6)); if (bind(s, &local.sa, sizeof(local.sa6)) < 0) return -1; } @@ -929,6 +1014,30 @@ static int configure_socket(ares_socket_t s, int family, ares_channel channel) return 0; } +static ares_socket_t open_socket(ares_channel channel, int af, int type, int protocol) +{ + if (channel->sock_funcs != 0) + return channel->sock_funcs->asocket(af, + type, + protocol, + channel->sock_func_cb_data); + + return socket(af, type, protocol); +} + +static int connect_socket(ares_channel channel, ares_socket_t sockfd, + const struct sockaddr * addr, + ares_socklen_t addrlen) +{ + if (channel->sock_funcs != 0) + return channel->sock_funcs->aconnect(sockfd, + addr, + addrlen, + channel->sock_func_cb_data); + + return connect(sockfd, addr, addrlen); +} + static int open_tcp_socket(ares_channel channel, struct server_state *server) { ares_socket_t s; @@ -947,7 +1056,11 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server) salen = sizeof(saddr.sa4); memset(sa, 0, salen); saddr.sa4.sin_family = AF_INET; - saddr.sa4.sin_port = (unsigned short)(channel->tcp_port & 0xffff); + if (server->addr.tcp_port) { + saddr.sa4.sin_port = aresx_sitous(server->addr.tcp_port); + } else { + saddr.sa4.sin_port = aresx_sitous(channel->tcp_port); + } memcpy(&saddr.sa4.sin_addr, &server->addr.addrV4, sizeof(server->addr.addrV4)); break; @@ -956,23 +1069,27 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server) salen = sizeof(saddr.sa6); memset(sa, 0, salen); saddr.sa6.sin6_family = AF_INET6; - saddr.sa6.sin6_port = (unsigned short)(channel->tcp_port & 0xffff); + if (server->addr.tcp_port) { + saddr.sa6.sin6_port = aresx_sitous(server->addr.tcp_port); + } else { + saddr.sa6.sin6_port = aresx_sitous(channel->tcp_port); + } memcpy(&saddr.sa6.sin6_addr, &server->addr.addrV6, sizeof(server->addr.addrV6)); break; default: - return -1; + return -1; /* LCOV_EXCL_LINE */ } /* Acquire a socket. */ - s = socket(server->addr.family, SOCK_STREAM, 0); + s = open_socket(channel, server->addr.family, SOCK_STREAM, 0); if (s == ARES_SOCKET_BAD) return -1; /* Configure it. */ if (configure_socket(s, server->addr.family, channel) < 0) { - sclose(s); + ares__socket_close(channel, s); return -1; } @@ -984,22 +1101,35 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server) * so batching isn't very interesting. */ opt = 1; - if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, - (void *)&opt, sizeof(opt)) == -1) + if (channel->sock_funcs == 0 + && + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, + (void *)&opt, sizeof(opt)) == -1) { - sclose(s); + ares__socket_close(channel, s); return -1; } #endif + if (channel->sock_config_cb) + { + int err = channel->sock_config_cb(s, SOCK_STREAM, + channel->sock_config_cb_data); + if (err < 0) + { + ares__socket_close(channel, s); + return err; + } + } + /* Connect to the server. */ - if (connect(s, sa, salen) == -1) + if (connect_socket(channel, s, sa, salen) == -1) { int err = SOCKERRNO; if (err != EINPROGRESS && err != EWOULDBLOCK) { - sclose(s); + ares__socket_close(channel, s); return -1; } } @@ -1010,7 +1140,7 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server) channel->sock_create_cb_data); if (err < 0) { - sclose(s); + ares__socket_close(channel, s); return err; } } @@ -1039,7 +1169,11 @@ static int open_udp_socket(ares_channel channel, struct server_state *server) salen = sizeof(saddr.sa4); memset(sa, 0, salen); saddr.sa4.sin_family = AF_INET; - saddr.sa4.sin_port = (unsigned short)(channel->udp_port & 0xffff); + if (server->addr.udp_port) { + saddr.sa4.sin_port = aresx_sitous(server->addr.udp_port); + } else { + saddr.sa4.sin_port = aresx_sitous(channel->udp_port); + } memcpy(&saddr.sa4.sin_addr, &server->addr.addrV4, sizeof(server->addr.addrV4)); break; @@ -1048,34 +1182,49 @@ static int open_udp_socket(ares_channel channel, struct server_state *server) salen = sizeof(saddr.sa6); memset(sa, 0, salen); saddr.sa6.sin6_family = AF_INET6; - saddr.sa6.sin6_port = (unsigned short)(channel->udp_port & 0xffff); + if (server->addr.udp_port) { + saddr.sa6.sin6_port = aresx_sitous(server->addr.udp_port); + } else { + saddr.sa6.sin6_port = aresx_sitous(channel->udp_port); + } memcpy(&saddr.sa6.sin6_addr, &server->addr.addrV6, sizeof(server->addr.addrV6)); break; default: - return -1; + return -1; /* LCOV_EXCL_LINE */ } /* Acquire a socket. */ - s = socket(server->addr.family, SOCK_DGRAM, 0); + s = open_socket(channel, server->addr.family, SOCK_DGRAM, 0); if (s == ARES_SOCKET_BAD) return -1; /* Set the socket non-blocking. */ if (configure_socket(s, server->addr.family, channel) < 0) { - sclose(s); + ares__socket_close(channel, s); return -1; } + if (channel->sock_config_cb) + { + int err = channel->sock_config_cb(s, SOCK_DGRAM, + channel->sock_config_cb_data); + if (err < 0) + { + ares__socket_close(channel, s); + return err; + } + } + /* Connect to the server. */ - if (connect(s, sa, salen) == -1) + if (connect_socket(channel, s, sa, salen) == -1) { int err = SOCKERRNO; if (err != EINPROGRESS && err != EWOULDBLOCK) { - sclose(s); + ares__socket_close(channel, s); return -1; } } @@ -1086,7 +1235,7 @@ static int open_udp_socket(ares_channel channel, struct server_state *server) channel->sock_create_cb_data); if (err < 0) { - sclose(s); + ares__socket_close(channel, s); return err; } } @@ -1130,7 +1279,7 @@ static int same_questions(const unsigned char *qbuf, int qlen, q.p += q.namelen; if (q.p + QFIXEDSZ > qbuf + qlen) { - free(q.name); + ares_free(q.name); return 0; } q.type = DNS_QUESTION_TYPE(q.p); @@ -1145,14 +1294,14 @@ static int same_questions(const unsigned char *qbuf, int qlen, if (ares_expand_name(a.p, abuf, alen, &a.name, &a.namelen) != ARES_SUCCESS) { - free(q.name); + ares_free(q.name); return 0; } a.p += a.namelen; if (a.p + QFIXEDSZ > abuf + alen) { - free(q.name); - free(a.name); + ares_free(q.name); + ares_free(a.name); return 0; } a.type = DNS_QUESTION_TYPE(a.p); @@ -1163,13 +1312,13 @@ static int same_questions(const unsigned char *qbuf, int qlen, if (strcasecmp(q.name, a.name) == 0 && q.type == a.type && q.dnsclass == a.dnsclass) { - free(a.name); + ares_free(a.name); break; } - free(a.name); + ares_free(a.name); } - free(q.name); + ares_free(q.name); if (j == a.qdcount) return 0; } @@ -1198,7 +1347,7 @@ static int same_address(struct sockaddr *sa, struct ares_addr *aa) return 1; /* match */ break; default: - break; + break; /* LCOV_EXCL_LINE */ } } return 0; /* different */ @@ -1223,21 +1372,19 @@ static void end_query (ares_channel channel, struct query *query, int status, assert(sendreq->data_storage == NULL); if (status == ARES_SUCCESS) { - /* We got a reply for this query, but this queued - * sendreq points into this soon-to-be-gone query's - * tcpbuf. Probably this means we timed out and queued - * the query for retransmission, then received a - * response before actually retransmitting. This is - * perfectly fine, so we want to keep the connection - * running smoothly if we can. But in the worst case - * we may have sent only some prefix of the query, - * with some suffix of the query left to send. Also, - * the buffer may be queued on multiple queues. To - * prevent dangling pointers to the query's tcpbuf and - * handle these cases, we just give such sendreqs - * their own copy of the query packet. + /* We got a reply for this query, but this queued sendreq + * points into this soon-to-be-gone query's tcpbuf. Probably + * this means we timed out and queued the query for + * retransmission, then received a response before actually + * retransmitting. This is perfectly fine, so we want to keep + * the connection running smoothly if we can. But in the worst + * case we may have sent only some prefix of the query, with + * some suffix of the query left to send. Also, the buffer may + * be queued on multiple queues. To prevent dangling pointers + * to the query's tcpbuf and handle these cases, we just give + * such sendreqs their own copy of the query packet. */ - sendreq->data_storage = malloc(sendreq->len); + sendreq->data_storage = ares_malloc(sendreq->len); if (sendreq->data_storage != NULL) { memcpy(sendreq->data_storage, sendreq->data, sendreq->len); @@ -1246,14 +1393,12 @@ static void end_query (ares_channel channel, struct query *query, int status, } if ((status != ARES_SUCCESS) || (sendreq->data_storage == NULL)) { - /* We encountered an error (probably a timeout, - * suggesting the DNS server we're talking to is - * probably unreachable, wedged, or severely - * overloaded) or we couldn't copy the request, so - * mark the connection as broken. When we get to - * process_broken_connections() we'll close the - * connection and try to re-send requests to another - * server. + /* We encountered an error (probably a timeout, suggesting the + * DNS server we're talking to is probably unreachable, + * wedged, or severely overloaded) or we couldn't copy the + * request, so mark the connection as broken. When we get to + * process_broken_connections() we'll close the connection and + * try to re-send requests to another server. */ server->is_broken = 1; /* Just to be paranoid, zero out this sendreq... */ @@ -1267,8 +1412,8 @@ static void end_query (ares_channel channel, struct query *query, int status, query->callback(query->arg, status, query->timeouts, abuf, alen); ares__free_query(query); - /* Simple cleanup policy: if no queries are remaining, close all - * network sockets unless STAYOPEN is set. + /* Simple cleanup policy: if no queries are remaining, close all network + * sockets unless STAYOPEN is set. */ if (!(channel->flags & ARES_FLAG_STAYOPEN) && ares__is_list_empty(&(channel->all_queries))) @@ -1289,7 +1434,15 @@ void ares__free_query(struct query *query) query->callback = NULL; query->arg = NULL; /* Deallocate the memory associated with the query */ - free(query->tcpbuf); - free(query->server_info); - free(query); + ares_free(query->tcpbuf); + ares_free(query->server_info); + ares_free(query); +} + +void ares__socket_close(ares_channel channel, ares_socket_t s) +{ + if (channel->sock_funcs) + channel->sock_funcs->aclose(s, channel->sock_func_cb_data); + else + sclose(s); } diff --git a/ares_query.c b/ares_query.c index 63652e2..b38b8a6 100644 --- a/ares_query.c +++ b/ares_query.c @@ -16,9 +16,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -31,7 +28,6 @@ # include <arpa/nameser_compat.h> #endif -#include <stdlib.h> #include "ares.h" #include "ares_dns.h" #include "ares_private.h" @@ -43,7 +39,7 @@ struct qquery { static void qcallback(void *arg, int status, int timeouts, unsigned char *abuf, int alen); -void ares__rc4(rc4_key* key, unsigned char *buffer_ptr, int buffer_len) +static void rc4(rc4_key* key, unsigned char *buffer_ptr, int buffer_len) { unsigned char x; unsigned char y; @@ -105,6 +101,13 @@ static unsigned short generate_unique_id(ares_channel channel) return (unsigned short)id; } +unsigned short ares__generate_new_id(rc4_key* key) +{ + unsigned short r=0; + rc4(key, (unsigned char *)&r, sizeof(r)); + return r; +} + void ares_query(ares_channel channel, const char *name, int dnsclass, int type, ares_callback callback, void *arg) { @@ -114,11 +117,11 @@ void ares_query(ares_channel channel, const char *name, int dnsclass, /* Compose the query. */ rd = !(channel->flags & ARES_FLAG_NORECURSE); - status = ares_mkquery(name, dnsclass, type, channel->next_id, rd, &qbuf, - &qlen); + status = ares_create_query(name, dnsclass, type, channel->next_id, rd, &qbuf, + &qlen, (channel->flags & ARES_FLAG_EDNS) ? channel->ednspsz : 0); if (status != ARES_SUCCESS) { - if (qbuf != NULL) free(qbuf); + if (qbuf != NULL) ares_free(qbuf); callback(arg, status, 0, NULL, 0); return; } @@ -126,7 +129,7 @@ void ares_query(ares_channel channel, const char *name, int dnsclass, channel->next_id = generate_unique_id(channel); /* Allocate and fill in the query structure. */ - qquery = malloc(sizeof(struct qquery)); + qquery = ares_malloc(sizeof(struct qquery)); if (!qquery) { ares_free_string(qbuf); @@ -179,5 +182,5 @@ static void qcallback(void *arg, int status, int timeouts, unsigned char *abuf, } qquery->callback(qquery->arg, status, timeouts, abuf, alen); } - free(qquery); + ares_free(qquery); } diff --git a/ares_rules.h b/ares_rules.h index f94c5b5..e617fdc 100644 --- a/ares_rules.h +++ b/ares_rules.h @@ -68,21 +68,11 @@ * Verify that some macros are actually defined. */ -#ifndef CARES_SIZEOF_LONG -# error "CARES_SIZEOF_LONG definition is missing!" - Error Compilation_aborted_CARES_SIZEOF_LONG_is_missing -#endif - #ifndef CARES_TYPEOF_ARES_SOCKLEN_T # error "CARES_TYPEOF_ARES_SOCKLEN_T definition is missing!" Error Compilation_aborted_CARES_TYPEOF_ARES_SOCKLEN_T_is_missing #endif -#ifndef CARES_SIZEOF_ARES_SOCKLEN_T -# error "CARES_SIZEOF_ARES_SOCKLEN_T definition is missing!" - Error Compilation_aborted_CARES_SIZEOF_ARES_SOCKLEN_T_is_missing -#endif - /* * Macros private to this header file. */ @@ -92,15 +82,6 @@ #define CareschkszGE(t1, t2) sizeof(t1) >= sizeof(t2) ? 1 : -1 /* - * Verify that the size previously defined and expected for long - * is the same as the one reported by sizeof() at compile time. - */ - -typedef char - __cares_rule_01__ - [CareschkszEQ(long, CARES_SIZEOF_LONG)]; - -/* * Verify that the size previously defined and expected for * ares_socklen_t is actually the the same as the one reported * by sizeof() at compile time. @@ -108,7 +89,7 @@ typedef char typedef char __cares_rule_02__ - [CareschkszEQ(ares_socklen_t, CARES_SIZEOF_ARES_SOCKLEN_T)]; + [CareschkszEQ(ares_socklen_t, sizeof(CARES_TYPEOF_ARES_SOCKLEN_T))]; /* * Verify at compile time that the size of ares_socklen_t as reported diff --git a/ares_search.c b/ares_search.c index 1877c19..68e8525 100644 --- a/ares_search.c +++ b/ares_search.c @@ -16,11 +16,6 @@ #include "ares_setup.h" -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> - #ifdef HAVE_STRINGS_H # include <strings.h> #endif @@ -49,7 +44,7 @@ static void search_callback(void *arg, int status, int timeouts, static void end_squery(struct search_query *squery, int status, unsigned char *abuf, int alen); static int cat_domain(const char *name, const char *domain, char **s); -static int single_domain(ares_channel channel, const char *name, char **s); +STATIC_TESTABLE int single_domain(ares_channel channel, const char *name, char **s); void ares_search(ares_channel channel, const char *name, int dnsclass, int type, ares_callback callback, void *arg) @@ -71,24 +66,24 @@ void ares_search(ares_channel channel, const char *name, int dnsclass, if (s) { ares_query(channel, s, dnsclass, type, callback, arg); - free(s); + ares_free(s); return; } /* Allocate a search_query structure to hold the state necessary for * doing multiple lookups. */ - squery = malloc(sizeof(struct search_query)); + squery = ares_malloc(sizeof(struct search_query)); if (!squery) { callback(arg, ARES_ENOMEM, 0, NULL, 0); return; } squery->channel = channel; - squery->name = strdup(name); + squery->name = ares_strdup(name); if (!squery->name) { - free(squery); + ares_free(squery); callback(arg, ARES_ENOMEM, 0, NULL, 0); return; } @@ -128,13 +123,13 @@ void ares_search(ares_channel channel, const char *name, int dnsclass, if (status == ARES_SUCCESS) { ares_query(channel, s, dnsclass, type, search_callback, squery); - free(s); + ares_free(s); } else { /* failed, free the malloc()ed memory */ - free(squery->name); - free(squery); + ares_free(squery->name); + ares_free(squery); callback(arg, status, 0, NULL, 0); } } @@ -182,7 +177,7 @@ static void search_callback(void *arg, int status, int timeouts, squery->next_domain++; ares_query(channel, s, squery->dnsclass, squery->type, search_callback, squery); - free(s); + ares_free(s); } } else if (squery->status_as_is == -1) @@ -206,8 +201,8 @@ static void end_squery(struct search_query *squery, int status, unsigned char *abuf, int alen) { squery->callback(squery->arg, status, squery->timeouts, abuf, alen); - free(squery->name); - free(squery); + ares_free(squery->name); + ares_free(squery); } /* Concatenate two domains. */ @@ -216,7 +211,7 @@ static int cat_domain(const char *name, const char *domain, char **s) size_t nlen = strlen(name); size_t dlen = strlen(domain); - *s = malloc(nlen + 1 + dlen + 1); + *s = ares_malloc(nlen + 1 + dlen + 1); if (!*s) return ARES_ENOMEM; memcpy(*s, name, nlen); @@ -230,7 +225,7 @@ static int cat_domain(const char *name, const char *domain, char **s) * the string we should query, in an allocated buffer. If not, set *s * to NULL. */ -static int single_domain(ares_channel channel, const char *name, char **s) +STATIC_TESTABLE int single_domain(ares_channel channel, const char *name, char **s) { size_t len = strlen(name); const char *hostaliases; @@ -244,9 +239,9 @@ static int single_domain(ares_channel channel, const char *name, char **s) /* If the name contains a trailing dot, then the single query is the name * sans the trailing dot. */ - if (name[len - 1] == '.') + if ((len > 0) && (name[len - 1] == '.')) { - *s = strdup(name); + *s = ares_strdup(name); return (*s) ? ARES_SUCCESS : ARES_ENOMEM; } @@ -273,18 +268,18 @@ static int single_domain(ares_channel channel, const char *name, char **s) q = p + 1; while (*q && !ISSPACE(*q)) q++; - *s = malloc(q - p + 1); + *s = ares_malloc(q - p + 1); if (*s) { memcpy(*s, p, q - p); (*s)[q - p] = 0; } - free(line); + ares_free(line); fclose(fp); return (*s) ? ARES_SUCCESS : ARES_ENOMEM; } } - free(line); + ares_free(line); fclose(fp); if (status != ARES_SUCCESS && status != ARES_EOF) return status; @@ -312,7 +307,7 @@ static int single_domain(ares_channel channel, const char *name, char **s) if (channel->flags & ARES_FLAG_NOSEARCH || channel->ndomains == 0) { /* No domain search to do; just try the name as-is. */ - *s = strdup(name); + *s = ares_strdup(name); return (*s) ? ARES_SUCCESS : ARES_ENOMEM; } diff --git a/ares_send.c b/ares_send.c index 37b0704..88c0035 100644 --- a/ares_send.c +++ b/ares_send.c @@ -16,9 +16,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -31,9 +28,6 @@ # include <arpa/nameser_compat.h> #endif -#include <stdlib.h> -#include <string.h> -#include <time.h> #include "ares.h" #include "ares_dns.h" #include "ares_private.h" @@ -42,7 +36,7 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen, ares_callback callback, void *arg) { struct query *query; - int i; + int i, packetsz; struct timeval now; /* Verify that the query is at least long enough to hold the header. */ @@ -53,31 +47,31 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen, } /* Allocate space for query and allocated fields. */ - query = malloc(sizeof(struct query)); + query = ares_malloc(sizeof(struct query)); if (!query) { callback(arg, ARES_ENOMEM, 0, NULL, 0); return; } - query->tcpbuf = malloc(qlen + 2); + query->tcpbuf = ares_malloc(qlen + 2); if (!query->tcpbuf) { - free(query); + ares_free(query); callback(arg, ARES_ENOMEM, 0, NULL, 0); return; } - query->server_info = malloc(channel->nservers * - sizeof(query->server_info[0])); + query->server_info = ares_malloc(channel->nservers * + sizeof(query->server_info[0])); if (!query->server_info) { - free(query->tcpbuf); - free(query); + ares_free(query->tcpbuf); + ares_free(query); callback(arg, ARES_ENOMEM, 0, NULL, 0); return; } /* Compute the query ID. Start with no timeout. */ - query->qid = (unsigned short)DNS_HEADER_QID(qbuf); + query->qid = DNS_HEADER_QID(qbuf); query->timeout.tv_sec = 0; query->timeout.tv_usec = 0; @@ -109,7 +103,10 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen, query->server_info[i].skip_server = 0; query->server_info[i].tcp_connection_generation = 0; } - query->using_tcp = (channel->flags & ARES_FLAG_USEVC) || qlen > PACKETSZ; + + packetsz = (channel->flags & ARES_FLAG_EDNS) ? channel->ednspsz : PACKETSZ; + query->using_tcp = (channel->flags & ARES_FLAG_USEVC) || qlen > packetsz; + query->error_status = ARES_ECONNREFUSED; query->timeouts = 0; diff --git a/ares_setup.h b/ares_setup.h index a46c510..4df7961 100644 --- a/ares_setup.h +++ b/ares_setup.h @@ -2,7 +2,7 @@ #define HEADER_CARES_SETUP_H -/* Copyright (C) 2004 - 2009 by Daniel Stenberg et al +/* Copyright (C) 2004 - 2012 by Daniel Stenberg et al * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided @@ -157,6 +157,18 @@ #endif /* HAVE_CONFIG_H */ +/* + * Arg 2 type for gethostname in case it hasn't been defined in config file. + */ + +#ifndef GETHOSTNAME_TYPE_ARG2 +# ifdef USE_WINSOCK +# define GETHOSTNAME_TYPE_ARG2 int +# else +# define GETHOSTNAME_TYPE_ARG2 size_t +# endif +#endif + #ifdef __POCC__ # include <sys/types.h> # include <unistd.h> @@ -164,6 +176,14 @@ #endif /* + * Android does have the arpa/nameser.h header which is detected by configure + * but it appears to be empty with recent NDK r7b / r7c, so we undefine here. + */ +#if (defined(ANDROID) || defined(__ANDROID__)) && defined(HAVE_ARPA_NAMESER_H) +# undef HAVE_ARPA_NAMESER_H +#endif + +/* * Recent autoconf versions define these symbols in ares_config.h. We don't * want them (since they collide with the libcurl ones when we build * --enable-debug) so we undef them again here. diff --git a/ares_strdup.c b/ares_strdup.c index 1804327..0c3dcff 100644 --- a/ares_strdup.c +++ b/ares_strdup.c @@ -17,26 +17,33 @@ #include "ares_setup.h" #include "ares_strdup.h" +#include "ares.h" +#include "ares_private.h" -#ifndef HAVE_STRDUP char *ares_strdup(const char *s1) { - size_t sz; - char * s2; +#ifdef HAVE_STRDUP + if (ares_malloc == malloc) + return strdup(s1); + else +#endif + { + size_t sz; + char * s2; - if(s1) { - sz = strlen(s1); - if(sz < (size_t)-1) { - sz++; - if(sz < ((size_t)-1) / sizeof(char)) { - s2 = malloc(sz * sizeof(char)); - if(s2) { - memcpy(s2, s1, sz * sizeof(char)); - return s2; + if(s1) { + sz = strlen(s1); + if(sz < (size_t)-1) { + sz++; + if(sz < ((size_t)-1) / sizeof(char)) { + s2 = ares_malloc(sz * sizeof(char)); + if(s2) { + memcpy(s2, s1, sz * sizeof(char)); + return s2; + } } } } + return (char *)NULL; } - return (char *)NULL; } -#endif diff --git a/ares_strdup.h b/ares_strdup.h index c413a94..67f2a74 100644 --- a/ares_strdup.h +++ b/ares_strdup.h @@ -19,8 +19,6 @@ #include "ares_setup.h" -#ifndef HAVE_STRDUP extern char *ares_strdup(const char *s1); -#endif #endif /* HEADER_CARES_STRDUP_H */ diff --git a/ares_strsplit.c b/ares_strsplit.c new file mode 100644 index 0000000..b57a30f --- /dev/null +++ b/ares_strsplit.c @@ -0,0 +1,174 @@ +/* Copyright (C) 2018 by John Schember <john@nachtimwald.com> + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" +#include "ares_strsplit.h" +#include "ares.h" +#include "ares_private.h" + +static int list_contains(char * const *list, size_t num_elem, const char *str, int insensitive) +{ + size_t len; + size_t i; + + len = strlen(str); + for (i=0; i<num_elem; i++) + { + if (insensitive) + { +#ifdef WIN32 + if (strnicmp(list[i], str, len) == 0) +#else + if (strncasecmp(list[i], str, len) == 0) +#endif + return 1; + } + else + { + if (strncmp(list[i], str, len) == 0) + return 1; + } + } + + return 0; +} + +static int is_delim(char c, const char *delims, size_t num_delims) +{ + size_t i; + + for (i=0; i<num_delims; i++) + { + if (c == delims[i]) + return 1; + } + return 0; +} + + +void ares_strsplit_free(char **elms, size_t num_elm) +{ + size_t i; + + if (elms == NULL) + return; + + for (i=0; i<num_elm; i++) + ares_free(elms[i]); + ares_free(elms); +} + + +char **ares_strsplit(const char *in, const char *delms, int make_set, size_t *num_elm) +{ + char *parsestr; + char **temp; + char **out; + size_t cnt; + size_t nelms; + size_t in_len; + size_t num_delims; + size_t i; + + if (in == NULL || delms == NULL || num_elm == NULL) + return NULL; + + *num_elm = 0; + + in_len = strlen(in); + num_delims = strlen(delms); + + /* Figure out how many elements. */ + nelms = 1; + for (i=0; i<in_len; i++) + { + if (is_delim(in[i], delms, num_delims)) + { + nelms++; + } + } + + /* Copy of input so we can cut it up. */ + parsestr = ares_strdup(in); + if (parsestr == NULL) + return NULL; + + /* Temporary array to store locations of start of each element + * within parsestr. */ + temp = ares_malloc(nelms * sizeof(*temp)); + if (temp == NULL) + { + ares_free(parsestr); + return NULL; + } + temp[0] = parsestr; + cnt = 1; + for (i=0; i<in_len && cnt<nelms; i++) + { + if (!is_delim(parsestr[i], delms, num_delims)) + continue; + + /* Replace sep with NULL. */ + parsestr[i] = '\0'; + /* Add the pointer to the array of elements */ + temp[cnt] = parsestr+i+1; + cnt++; + } + + /* Copy each element to our output array. */ + out = ares_malloc(nelms * sizeof(*out)); + if (out == NULL) + { + ares_free(parsestr); + ares_free(temp); + return NULL; + } + + nelms = 0; + for (i=0; i<cnt; i++) + { + if (temp[i][0] == '\0') + continue; + + if (make_set && list_contains(out, nelms, temp[i], 1)) + continue; + + out[nelms] = ares_strdup(temp[i]); + if (out[nelms] == NULL) + { + ares_strsplit_free(out, nelms); + ares_free(parsestr); + ares_free(temp); + return NULL; + } + nelms++; + } + + + /* If there are no elements don't return an empty allocated + * array. */ + if (nelms == 0) + { + ares_strsplit_free(out, nelms); + out = NULL; + } + + /* Get the true number of elements (recalculated because of make_set) */ + *num_elm = nelms; + + ares_free(parsestr); + ares_free(temp); + return out; +} diff --git a/ares_strsplit.h b/ares_strsplit.h new file mode 100644 index 0000000..e00fd14 --- /dev/null +++ b/ares_strsplit.h @@ -0,0 +1,43 @@ +#ifndef HEADER_CARES_STRSPLIT_H +#define HEADER_CARES_STRSPLIT_H + +/* Copyright (C) 2018 by John Schember <john@nachtimwald.com> + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "ares_setup.h" + +/* Split a string on delem skipping empty elements. + * + * param in String to split. + * param delims String of characters to treat as a delimitor. + * Each character in the string is a delimitor so + * there can be multiple delimitors to split on. + * E.g. ", " will split on all comma's and spaces. + * param make_set Have the list be a Set where there are no + * duplicate entries. 1 for true, 0 or false. + * param num_elm Return parameter of the number of elements + * in the result array. + * + * returns an allocated array of allocated string elements. + * + */ +char **ares_strsplit(const char *in, const char *delms, int make_set, size_t *num_elm); + +/* Frees the result returned from ares_strsplit(). */ +void ares_strsplit_free(char **elms, size_t num_elm); + + +#endif /* HEADER_CARES_STRSPLIT_H */ + diff --git a/ares_timeout.c b/ares_timeout.c index 2da4f5f..293e4af 100644 --- a/ares_timeout.c +++ b/ares_timeout.c @@ -16,15 +16,20 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> +#ifdef HAVE_LIMITS_H +#include <limits.h> #endif -#include <time.h> - #include "ares.h" #include "ares_private.h" +/* return time offset between now and (future) check, in milliseconds */ +static long timeoffset(struct timeval *now, struct timeval *check) +{ + return (check->tv_sec - now->tv_sec)*1000 + + (check->tv_usec - now->tv_usec)/1000; +} + /* WARNING: Beware that this is linear in the number of outstanding * requests! You are probably far better off just calling ares_process() * once per second, rather than calling ares_timeout() to figure out @@ -55,26 +60,29 @@ struct timeval *ares_timeout(ares_channel channel, struct timeval *maxtv, query = list_node->data; if (query->timeout.tv_sec == 0) continue; - offset = ares__timeoffset(&now, &query->timeout); + offset = timeoffset(&now, &query->timeout); if (offset < 0) offset = 0; if (min_offset == -1 || offset < min_offset) min_offset = offset; } - if(min_offset != -1) { - nextstop.tv_sec = min_offset/1000; - nextstop.tv_usec = (min_offset%1000)*1000; - } - /* If we found a minimum timeout and it's sooner than the one specified in * maxtv (if any), return it. Otherwise go with maxtv. */ - if (min_offset != -1 && (!maxtv || ares__timedout(maxtv, &nextstop))) + if (min_offset != -1) { - *tvbuf = nextstop; - return tvbuf; + int ioffset = (min_offset > (long)INT_MAX) ? INT_MAX : (int)min_offset; + + nextstop.tv_sec = ioffset/1000; + nextstop.tv_usec = (ioffset%1000)*1000; + + if (!maxtv || ares__timedout(maxtv, &nextstop)) + { + *tvbuf = nextstop; + return tvbuf; + } } - else - return maxtv; + + return maxtv; } diff --git a/ares_version.h b/ares_version.h index 50eb638..ed623d1 100644 --- a/ares_version.h +++ b/ares_version.h @@ -3,15 +3,15 @@ #define ARES__VERSION_H /* This is the global package copyright */ -#define ARES_COPYRIGHT "2004 - 2010 Daniel Stenberg, <daniel@haxx.se>." +#define ARES_COPYRIGHT "2004 - 2016 Daniel Stenberg, <daniel@haxx.se>." #define ARES_VERSION_MAJOR 1 -#define ARES_VERSION_MINOR 7 -#define ARES_VERSION_PATCH 5 +#define ARES_VERSION_MINOR 12 +#define ARES_VERSION_PATCH 1 #define ARES_VERSION ((ARES_VERSION_MAJOR<<16)|\ (ARES_VERSION_MINOR<<8)|\ (ARES_VERSION_PATCH)) -#define ARES_VERSION_STR "1.7.5" +#define ARES_VERSION_STR "1.12.1-DEV" #if (ARES_VERSION >= 0x010700) # define CARES_HAVE_ARES_LIBRARY_INIT 1 diff --git a/ares_writev.c b/ares_writev.c index 9e8e2d6..e812c09 100644 --- a/ares_writev.c +++ b/ares_writev.c @@ -25,12 +25,12 @@ #include "ares_private.h" #ifndef HAVE_WRITEV -ssize_t ares_writev(ares_socket_t s, const struct iovec *iov, int iovcnt) +ares_ssize_t ares_writev(ares_socket_t s, const struct iovec *iov, int iovcnt) { char *buffer, *bp; int i; size_t bytes = 0; - ssize_t result; + ares_ssize_t result; /* Validate iovcnt */ if (iovcnt <= 0) @@ -54,7 +54,7 @@ ssize_t ares_writev(ares_socket_t s, const struct iovec *iov, int iovcnt) return (0); /* Allocate a temporary buffer to hold the data */ - buffer = malloc(bytes); + buffer = ares_malloc(bytes); if (!buffer) { SET_ERRNO(ENOMEM); @@ -71,7 +71,7 @@ ssize_t ares_writev(ares_socket_t s, const struct iovec *iov, int iovcnt) /* Send buffer contents */ result = swrite(s, buffer, bytes); - free(buffer); + ares_free(buffer); return (result); } diff --git a/ares_writev.h b/ares_writev.h index 1a23a0f..65cea87 100644 --- a/ares_writev.h +++ b/ares_writev.h @@ -29,7 +29,7 @@ struct iovec size_t iov_len; /* Length of data. */ }; -extern ssize_t ares_writev(ares_socket_t s, const struct iovec *iov, int iovcnt); +extern ares_ssize_t ares_writev(ares_socket_t s, const struct iovec *iov, int iovcnt); #endif @@ -26,34 +26,34 @@ * bitncmp(l, r, n) * compare bit masks l and r, for n bits. * return: - * -1, 1, or 0 in the libc tradition. + * <0, >0, or 0 in the libc tradition. * note: * network byte order assumed. this means 192.5.5.240/28 has * 0x11110000 in its fourth octet. * author: * Paul Vixie (ISC), June 1996 */ -int -ares_bitncmp(const void *l, const void *r, int n) { - unsigned int lb, rb; - int x, b; +int ares__bitncmp(const void *l, const void *r, int n) +{ + unsigned int lb, rb; + int x, b; - b = n / 8; - x = memcmp(l, r, b); - if (x || (n % 8) == 0) - return (x); + b = n / 8; + x = memcmp(l, r, b); + if (x || (n % 8) == 0) + return (x); - lb = ((const unsigned char *)l)[b]; - rb = ((const unsigned char *)r)[b]; - for (b = n % 8; b > 0; b--) { - if ((lb & 0x80) != (rb & 0x80)) { - if (lb & 0x80) - return (1); - return (-1); - } - lb <<= 1; - rb <<= 1; - } - return (0); + lb = ((const unsigned char *)l)[b]; + rb = ((const unsigned char *)r)[b]; + for (b = n % 8; b > 0; b--) { + if ((lb & 0x80) != (rb & 0x80)) { + if (lb & 0x80) + return (1); + return (-1); + } + lb <<= 1; + rb <<= 1; + } + return (0); } #endif @@ -2,7 +2,7 @@ #define __ARES_BITNCMP_H -/* Copyright (C) 2005 by Dominick Meglio +/* Copyright (C) 2005, 2013 by Dominick Meglio * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -18,9 +18,9 @@ */ #ifndef HAVE_BITNCMP -int ares_bitncmp(const void *l, const void *r, int n); +int ares__bitncmp(const void *l, const void *r, int n); #else -#define ares_bitncmp(x,y,z) bitncmp(x,y,z) +#define ares__bitncmp(x,y,z) bitncmp(x,y,z) #endif #endif /* __ARES_BITNCMP_H */ diff --git a/config-dos.h b/config-dos.h index d15a177..114db51 100644 --- a/config-dos.h +++ b/config-dos.h @@ -32,9 +32,6 @@ #define NEED_MALLOC_H 1 #define RETSIGTYPE void -#define SIZEOF_INT 4 -#define SIZEOF_SHORT 2 -#define SIZEOF_SIZE_T 4 #define TIME_WITH_SYS_TIME 1 /* Qualifiers for send(), recv(), recvfrom() and getnameinfo(). */ @@ -63,11 +60,6 @@ #define BSD -#if defined(__HIGHC__) || \ - (defined(__GNUC__) && (__GNUC__ < 4)) -#define ssize_t int -#endif - /* Target HAVE_x section */ #if defined(DJGPP) @@ -86,13 +78,14 @@ #elif defined(__HIGHC__) #define HAVE_SYS_TIME_H 1 + #define strerror(e) strerror_s_((e)) #endif #ifdef WATT32 #define HAVE_AF_INET6 1 #define HAVE_ARPA_INET_H 1 #define HAVE_ARPA_NAMESER_H 1 - #define HAVE_CLOSESOCKET_CAMEL 1 + #define HAVE_CLOSE_S 1 #define HAVE_GETHOSTNAME 1 #define HAVE_NETDB_H 1 #define HAVE_NETINET_IN_H 1 @@ -106,7 +99,6 @@ #define HAVE_SYS_UIO_H 1 #define NS_INADDRSZ 4 #define HAVE_STRUCT_SOCKADDR_IN6 1 - #define CloseSocket(s) close_s((s)) #endif #undef word diff --git a/config-win32.h b/config-win32.h index 48ce512..8d70474 100644 --- a/config-win32.h +++ b/config-win32.h @@ -211,37 +211,15 @@ /* Define to the return type of signal handlers (int or void). */ #define RETSIGTYPE void -/* Define if ssize_t is not an available 'typedefed' type. */ -#ifndef _SSIZE_T_DEFINED -# if (defined(__WATCOMC__) && (__WATCOMC__ >= 1240)) || \ - defined(__POCC__) || \ - defined(__MINGW32__) -# elif defined(_WIN64) -# define _SSIZE_T_DEFINED -# define ssize_t __int64 -# else -# define _SSIZE_T_DEFINED -# define ssize_t int -# endif +#ifdef __cplusplus +/* Compiling headers in C++ mode means bool is available */ +#define HAVE_BOOL_T #endif /* ---------------------------------------------------------------- */ /* TYPE SIZES */ /* ---------------------------------------------------------------- */ -/* Define to the size of `int', as computed by sizeof. */ -#define SIZEOF_INT 4 - -/* Define to the size of `short', as computed by sizeof. */ -#define SIZEOF_SHORT 2 - -/* Define to the size of `size_t', as computed by sizeof. */ -#if defined(_WIN64) -# define SIZEOF_SIZE_T 8 -#else -# define SIZEOF_SIZE_T 4 -#endif - /* ---------------------------------------------------------------- */ /* STRUCT RELATED */ /* ---------------------------------------------------------------- */ @@ -267,31 +245,19 @@ # define _CRT_NONSTDC_NO_DEPRECATE 1 #endif -/* Officially, Microsoft's Windows SDK versions 6.X do not support Windows - 2000 as a supported build target. VS2008 default installations provide - an embedded Windows SDK v6.0A along with the claim that Windows 2000 is - a valid build target for VS2008. Popular belief is that binaries built - with VS2008 using Windows SDK versions 6.X and Windows 2000 as a build - target are functional. */ -#if defined(_MSC_VER) && (_MSC_VER >= 1500) -# define VS2008_MIN_TARGET 0x0500 -#endif - -/* When no build target is specified VS2008 default build target is Windows - Vista, which leaves out even Winsows XP. If no build target has been given - for VS2008 we will target the minimum Officially supported build target, - which happens to be Windows XP. */ +/* Set the Target to Vista. However, any symbols required above Win2000 + * should be loaded via LoadLibrary() */ #if defined(_MSC_VER) && (_MSC_VER >= 1500) -# define VS2008_DEF_TARGET 0x0501 +# define VS2008_MIN_TARGET 0x0600 #endif /* VS2008 default target settings and minimum build target check. */ #if defined(_MSC_VER) && (_MSC_VER >= 1500) # ifndef _WIN32_WINNT -# define _WIN32_WINNT VS2008_DEF_TARGET +# define _WIN32_WINNT VS2008_MIN_TARGET # endif # ifndef WINVER -# define WINVER VS2008_DEF_TARGET +# define WINVER VS2008_MIN_TARGET # endif # if (_WIN32_WINNT < VS2008_MIN_TARGET) || (WINVER < VS2008_MIN_TARGET) # error VS2008 does not support Windows build targets prior to Windows 2000 @@ -299,13 +265,13 @@ #endif /* When no build target is specified Pelles C 5.00 and later default build - target is Windows Vista. We override default target to be Windows 2000. */ + target is Windows Vista. */ #if defined(__POCC__) && (__POCC__ >= 500) # ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0500 +# define _WIN32_WINNT 0x0600 # endif # ifndef WINVER -# define WINVER 0x0500 +# define WINVER 0x0600 # endif #endif @@ -365,5 +331,21 @@ #define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 #endif +/* ---------------------------------------------------------------- */ +/* Win CE */ +/* ---------------------------------------------------------------- */ + +/* FIXME: A proper config-win32ce.h should be created to hold these */ + +/* + * System error codes for Windows CE + */ + +#if defined(_WIN32_WCE) && !defined(HAVE_ERRNO_H) +# define ENOENT ERROR_FILE_NOT_FOUND +# define ESRCH ERROR_PATH_NOT_FOUND +# define ENOMEM ERROR_NOT_ENOUGH_MEMORY +# define ENOSPC ERROR_INVALID_PARAMETER +#endif #endif /* HEADER_CARES_CONFIG_WIN32_H */ diff --git a/inet_net_pton.c b/inet_net_pton.c index 45bb5d4..af1a534 100644 --- a/inet_net_pton.c +++ b/inet_net_pton.c @@ -18,9 +18,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -36,15 +33,10 @@ # include <arpa/nameser_compat.h> #endif -#include <ctype.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - #include "ares.h" #include "ares_ipv6.h" #include "ares_nowarn.h" -#include "inet_net_pton.h" +#include "ares_inet_net_pton.h" const struct ares_in6_addr ares_in6addr_any = { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }; @@ -159,7 +151,7 @@ inet_net_pton_ipv4(const char *src, unsigned char *dst, size_t size) /* If nothing was written to the destination, we found no address. */ if (dst == odst) - goto enoent; + goto enoent; /* LCOV_EXCL_LINE: all valid paths above increment dst */ /* If no CIDR spec was given, infer width from net class. */ if (bits == -1) { if (*odst >= 240) /* Class E */ @@ -365,8 +357,8 @@ inet_net_pton_ipv6(const char *src, unsigned char *dst, size_t size) * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ - const ssize_t n = tp - colonp; - ssize_t i; + const ares_ssize_t n = tp - colonp; + ares_ssize_t i; if (tp == endp) goto enoent; @@ -448,4 +440,11 @@ int ares_inet_pton(int af, const char *src, void *dst) return 0; return (result > -1 ? 1 : -1); } +#else /* HAVE_INET_PTON */ +int ares_inet_pton(int af, const char *src, void *dst) +{ + /* just relay this to the underlying function */ + return inet_pton(af, src, dst); +} + #endif diff --git a/inet_ntop.c b/inet_ntop.c index 57e1146..9420f6c 100644 --- a/inet_ntop.c +++ b/inet_ntop.c @@ -17,9 +17,6 @@ #include "ares_setup.h" -#ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -35,15 +32,8 @@ # include <arpa/nameser_compat.h> #endif -#include <ctype.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - #include "ares.h" #include "ares_ipv6.h" -#include "inet_ntop.h" - #ifndef HAVE_INET_NTOP @@ -69,13 +59,13 @@ static const char *inet_ntop6(const unsigned char *src, char *dst, size_t size); * Paul Vixie, 1996. */ const char * -ares_inet_ntop(int af, const void *src, char *dst, size_t size) +ares_inet_ntop(int af, const void *src, char *dst, ares_socklen_t size) { switch (af) { case AF_INET: - return (inet_ntop4(src, dst, size)); + return (inet_ntop4(src, dst, (size_t)size)); case AF_INET6: - return (inet_ntop6(src, dst, size)); + return (inet_ntop6(src, dst, (size_t)size)); default: SET_ERRNO(EAFNOSUPPORT); return (NULL); @@ -205,4 +195,14 @@ inet_ntop6(const unsigned char *src, char *dst, size_t size) strcpy(dst, tmp); return (dst); } -#endif + +#else /* HAVE_INET_NTOP */ + +const char * +ares_inet_ntop(int af, const void *src, char *dst, ares_socklen_t size) +{ + /* just relay this to the underlying function */ + return inet_ntop(af, src, dst, size); +} + +#endif /* HAVE_INET_NTOP */ @@ -186,11 +186,19 @@ typedef enum __ns_rcode { #define T_SRV ns_t_srv #define T_ATMA ns_t_atma #define T_NAPTR ns_t_naptr +#define T_KX ns_t_kx +#define T_CERT ns_t_cert +#define T_A6 ns_t_a6 +#define T_DNAME ns_t_dname +#define T_SINK ns_t_sink +#define T_OPT ns_t_opt +#define T_APL ns_t_apl #define T_DS ns_t_ds #define T_SSHFP ns_t_sshfp #define T_RRSIG ns_t_rrsig #define T_NSEC ns_t_nsec #define T_DNSKEY ns_t_dnskey +#define T_TKEY ns_t_tkey #define T_TSIG ns_t_tsig #define T_IXFR ns_t_ixfr #define T_AXFR ns_t_axfr diff --git a/setup_once.h b/setup_once.h index a333f54..a8cfe6b 100644 --- a/setup_once.h +++ b/setup_once.h @@ -2,7 +2,7 @@ #define __SETUP_ONCE_H -/* Copyright (C) 2004 - 2011 by Daniel Stenberg et al +/* Copyright (C) 2004 - 2013 by Daniel Stenberg et al * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided @@ -72,10 +72,38 @@ #include <fcntl.h> #endif -#ifdef HAVE_STDBOOL_H +#if defined(HAVE_STDBOOL_H) && defined(HAVE_BOOL_T) #include <stdbool.h> #endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef __hpux +# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) +# ifdef _APP32_64BIT_OFF_T +# define OLD_APP32_64BIT_OFF_T _APP32_64BIT_OFF_T +# undef _APP32_64BIT_OFF_T +# else +# undef OLD_APP32_64BIT_OFF_T +# endif +# endif +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef __hpux +# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) +# ifdef OLD_APP32_64BIT_OFF_T +# define _APP32_64BIT_OFF_T OLD_APP32_64BIT_OFF_T +# undef OLD_APP32_64BIT_OFF_T +# endif +# endif +#endif + /* * Definition of timeval struct for platforms that don't have it. @@ -103,7 +131,7 @@ struct timeval { #if defined(__minix) /* Minix doesn't support recv on TCP sockets */ -#define sread(x,y,z) (ssize_t)read((RECV_TYPE_ARG1)(x), \ +#define sread(x,y,z) (ares_ssize_t)read((RECV_TYPE_ARG1)(x), \ (RECV_TYPE_ARG2)(y), \ (RECV_TYPE_ARG3)(z)) @@ -139,7 +167,7 @@ struct timeval { Error Missing_definition_of_return_and_arguments_types_of_recv /* */ #else -#define sread(x,y,z) (ssize_t)recv((RECV_TYPE_ARG1)(x), \ +#define sread(x,y,z) (ares_ssize_t)recv((RECV_TYPE_ARG1)(x), \ (RECV_TYPE_ARG2)(y), \ (RECV_TYPE_ARG3)(z), \ (RECV_TYPE_ARG4)(0)) @@ -155,7 +183,7 @@ struct timeval { #if defined(__minix) /* Minix doesn't support send on TCP sockets */ -#define swrite(x,y,z) (ssize_t)write((SEND_TYPE_ARG1)(x), \ +#define swrite(x,y,z) (ares_ssize_t)write((SEND_TYPE_ARG1)(x), \ (SEND_TYPE_ARG2)(y), \ (SEND_TYPE_ARG3)(z)) @@ -170,7 +198,7 @@ struct timeval { Error Missing_definition_of_return_and_arguments_types_of_send /* */ #else -#define swrite(x,y,z) (ssize_t)send((SEND_TYPE_ARG1)(x), \ +#define swrite(x,y,z) (ares_ssize_t)send((SEND_TYPE_ARG1)(x), \ (SEND_TYPE_ARG2)(y), \ (SEND_TYPE_ARG3)(z), \ (SEND_TYPE_ARG4)(SEND_4TH_ARG)) @@ -200,7 +228,7 @@ struct timeval { Error Missing_definition_of_return_and_arguments_types_of_recvfrom /* */ #else -#define sreadfrom(s,b,bl,f,fl) (ssize_t)recvfrom((RECVFROM_TYPE_ARG1) (s), \ +#define sreadfrom(s,b,bl,f,fl) (ares_ssize_t)recvfrom((RECVFROM_TYPE_ARG1) (s), \ (RECVFROM_TYPE_ARG2 *)(b), \ (RECVFROM_TYPE_ARG3) (bl), \ (RECVFROM_TYPE_ARG4) (0), \ @@ -232,6 +260,8 @@ struct timeval { # define sclose(x) closesocket((x)) #elif defined(HAVE_CLOSESOCKET_CAMEL) # define sclose(x) CloseSocket((x)) +#elif defined(HAVE_CLOSE_S) +# define sclose(x) close_s((x)) #else # define sclose(x) close((x)) #endif @@ -260,6 +290,18 @@ struct timeval { /* + * 'bool' stuff compatible with HP-UX headers. + */ + +#if defined(__hpux) && !defined(HAVE_BOOL_T) + typedef int bool; +# define false 0 +# define true 1 +# define HAVE_BOOL_T +#endif + + +/* * 'bool' exists on platforms with <stdbool.h>, i.e. C99 platforms. * On non-C99 platforms there's no bool, so define an enum for that. * On C99 platforms 'false' and 'true' also exist. Enum uses a @@ -300,6 +342,27 @@ struct timeval { /* + * Macro WHILE_FALSE may be used to build single-iteration do-while loops, + * avoiding compiler warnings. Mostly intended for other macro definitions. + */ + +#define WHILE_FALSE while(0) + +#if defined(_MSC_VER) && !defined(__POCC__) +# undef WHILE_FALSE +# if (_MSC_VER < 1500) +# define WHILE_FALSE while(1, 0) +# else +# define WHILE_FALSE \ +__pragma(warning(push)) \ +__pragma(warning(disable:4127)) \ +while(0) \ +__pragma(warning(pop)) +# endif +#endif + + +/* * Typedef to 'int' if sig_atomic_t is not an available 'typedefed' type. */ @@ -336,7 +399,7 @@ typedef int sig_atomic_t; #ifdef DEBUGBUILD #define DEBUGF(x) x #else -#define DEBUGF(x) do { } while (0) +#define DEBUGF(x) do { } WHILE_FALSE #endif @@ -347,7 +410,7 @@ typedef int sig_atomic_t; #if defined(DEBUGBUILD) && defined(HAVE_ASSERT_H) #define DEBUGASSERT(x) assert(x) #else -#define DEBUGASSERT(x) do { } while (0) +#define DEBUGASSERT(x) do { } WHILE_FALSE #endif @@ -460,18 +523,6 @@ typedef int sig_atomic_t; /* - * System error codes for Windows CE - */ - -#if defined(WIN32) && !defined(HAVE_ERRNO_H) -#define ENOENT ERROR_FILE_NOT_FOUND -#define ESRCH ERROR_PATH_NOT_FOUND -#define ENOMEM ERROR_NOT_ENOUGH_MEMORY -#define ENOSPC ERROR_INVALID_PARAMETER -#endif - - -/* * Actually use __32_getpwuid() on 64-bit VMS builds for getpwuid() */ @@ -501,4 +552,3 @@ typedef int sig_atomic_t; #endif /* __SETUP_ONCE_H */ - |