From bdff98ffb3152c24c090e4d8ed131aa7756cb2b2 Mon Sep 17 00:00:00 2001 From: Glen Wiley Date: Mon, 10 Feb 2014 19:59:45 -0500 Subject: [PATCH] steps toward proper support of namespaces and system files --- README.md | 51 ++++--- doc/Makefile.in | 2 +- src/context.c | 215 +++++++++++++++++++++------ src/context.h | 55 +++++-- src/example/example-simple-answers.c | 128 ++++++++++------ src/general.c | 43 ++++-- src/general.h | 4 +- src/service.c | 25 +++- src/util-internal.c | 6 +- src/util-internal.h | 1 + 10 files changed, 388 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index 73d8f31c..2658e2f4 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,23 @@ getdns API {#mainpage} ========== -* Date: 2013-11-03 +* Date: 2014-02-10 * GitHub: getdns is a [modern asynchronous DNS API](http://www.vpnc.org/getdns-api/) intended to make all types of DNS information easily available as described by Paul Hoffman. This implementation is licensed under the New BSD License (BSD-new). The [getdns-api mailing list](http://www.vpnc.org/mailman/listinfo/getdns-api) is a good place to engage in discussions regarding the design of the API. +If you are just getting started with the library take a look at the section below that +describes building and handling external dependencies for the library. Once it is +built you should take a look at src/examples to see how the library is used. + This file captures the goals and direction of the project and the current state of the implementation. The goals of this implementation of the getdns API are: * Provide an open source implementation, in C, of the formally described getdns API by Paul Hoffman at -* Initial support for FreeBSD x.y, MS-Windows Ver. X, OSX 10.x, Linux (CentOS/RHEL R6uX, Ubuntu Ver X) via functional "configure" script +* Initial support for FreeBSD, MS-Windows, OSX, Linux (CentOS/RHEL, Ubuntu) via functional "configure" script * Initial support to include the Android platform * Include examples and tests as part of the build * Document code using doxygen @@ -24,9 +28,10 @@ The goals of this implementation of the getdns API are: ** the develop branch contains the latest development changes which are merged from develop into master once they are considered production ready * Both synchronous and asynchronous entry points with an early focus on the asynchronous model -Non-goals (things we will not be doing) include: +Non-goals (things we will not be doing at least initially) include: * implementation of the traditional DNS related routines (gethostbyname, etc.) + Releases ======== Release numbering follows the [Semantic Versioning](http://semver.org/) approach. We are currently in the early stages of building the API so the code should be considered incomplete. @@ -37,6 +42,7 @@ The 0.1.0 release will be issued when the repository is opened to the public, ou * examples must compile and be clean * clearly document supported/unsupported elements of the API + Tickets/Bug Reports =================== Tickets and bug reports from external contacts are received via a mailing list and managed in the git issues list. @@ -44,8 +50,7 @@ Tickets and bug reports from external contacts are received via a mailing list a TBD: mailing list address -External Dependencies -===================== +#Building/External Dependencies External dependencies are linked outside the getdns API build tree (we rely on configure to find them). We would like to keep the dependency tree short. * [libevent](http://libevent.org) version 2.0.21 stable @@ -72,8 +77,14 @@ Assuming that the getdns sources are in a diretory named getdns in your home dir # make install ``` -Unsupported Features -==================== +##Regression Tests + +A suite of regression tests are included with the library, if you make changes or just +want to sanity check things on your system take a look at src/test. You will need +to install [libcheck](http://check.sourceforge.net/). Check is also available from +many of the package repositories for the more popular operating systems. + +#Unsupported Features The following API calls are documented in getDNS but *not supported* by the implementation at this time: @@ -92,6 +103,12 @@ The following API calls are documented in getDNS but *not supported* by the impl * `getdns_context_set_dnssec_trust_anchors` * `getdns_validate_dnssec` * Detecting changes to resolv.conf and hosts +* MDNS and NetBIOS namespaces (only DNS and LOCALFILES are supported) + +Some platform specific features are not implemented in the first public release of getdns, however they are on the radar. These include: + +* Respecting settings in /etc/nsswitch.conf (linux and some other OSes), for the first release we simply check local files (/etc/hosts) before checking the DNS. +* Search suffixes specified in /etc/resolv.conf Spec Differences ================ @@ -107,21 +124,20 @@ The primary platforms targeted are Linux and FreeBSD, other platform are support Where at all possible we need to make sure that both 32 and 64 bit implementations work. -* Android, Neel -* FreeBSD 9.2, gcc/clang Melinda -* FreeBSD 10.0 (not yet released), gcc/clang Melinda -* Linux RHEL/CentOS 6.x, Glen -* MS-Windows 8, cygwin, Neel -* NetBSD x.x, Wouter -* OpenBSD 5.3, Wouter -* OSX 10.8, Glen -* OSX 10.9, Allison -* Ubuntu 12.x, Melinda +* Debian 7.0, 7.3 +* FreeBSD 8.4, 9.2, 10.0 +* RHEL/CentOS 6.4, 6.5 +* OSX 10.8, 10.9 +* Ubuntu 12.04, 13.10 The NLNet folks offered to build on a number of legacy platforms as well to help ensure that the code is clean. These include some big endian hardware and a few more obscure operating systems which will not be publicly supported but might work if someone wants to try them. +We intend to add MS-Windows, Android and other platforms to the releases as we have time to port it. + + ##Build Reports + TBD Contributors @@ -132,6 +148,7 @@ Contributors * Willem Toorop, NLNet Labs * Glen Wiley, Verisign, Inc. * Wouter Wijngaards, NLNet Labs +* Craig Despeaux, Verisign, Inc. -- end README diff --git a/doc/Makefile.in b/doc/Makefile.in index fa644110..ffbfe983 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -21,7 +21,7 @@ VPATH = @srcdir@ EDITS=-e 's/@''version@/$(version)/g' DOCDIRS = html latex man -MANPAGES3 = libgetdns.3 getdns_address.3 getdns_dict.3 getdns_general.3 getdns_hostname.3 getdns_service.3 +MANPAGES3 = libgetdns.3 getdns_address.3 getdns_context.3 getdns_context_set.3 getdns_dict.3 getdns_general.3 getdns_hostname.3 getdns_service.3 default: all diff --git a/src/context.c b/src/context.c index acc6581c..cb52e59e 100644 --- a/src/context.c +++ b/src/context.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -50,8 +51,7 @@ void *plain_mem_funcs_user_arg = MF_PLAIN; /* Private functions */ -static getdns_namespace_t *create_default_namespaces( - struct getdns_context *context); +getdns_return_t create_default_namespaces(struct getdns_context *context); static struct getdns_list *create_default_root_servers(); static getdns_return_t add_ip_str(struct getdns_dict *); static struct getdns_dict *create_ipaddr_dict_from_rdf(struct getdns_context *, @@ -69,21 +69,24 @@ static void cancel_dns_req(getdns_dns_req *); static void cancel_outstanding_requests(struct getdns_context*, int); /* Stuff to make it compile pedantically */ -#define UNUSED_PARAM(x) ((void)(x)) #define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code; /** * Helper to get default lookup namespaces. * TODO: Determine from OS */ -static getdns_namespace_t* +getdns_return_t create_default_namespaces(struct getdns_context *context) { - getdns_namespace_t *result = GETDNS_XMALLOC( - context->my_mf, getdns_namespace_t, 2); - result[0] = GETDNS_NAMESPACE_LOCALNAMES; - result[1] = GETDNS_NAMESPACE_DNS; - return result; + context->namespaces = GETDNS_XMALLOC(context->my_mf, getdns_namespace_t, 2); + if(context->namespaces == NULL) + return GETDNS_RETURN_GENERIC_ERROR; + + context->namespaces[0] = GETDNS_NAMESPACE_LOCALNAMES; + context->namespaces[1] = GETDNS_NAMESPACE_DNS; + context->namespace_count = 2; + + return GETDNS_RETURN_GOOD; } /** @@ -151,6 +154,57 @@ add_ip_str(struct getdns_dict * ip) return GETDNS_RETURN_GOOD; } +/** + * check a file for changes since the last check + * and refresh the current data if changes are detected + * @param file to check + * @returns changes as OR'd list of GETDNS_FCHG_* values + * @returns GETDNS_FCHG_NONE if no changes + * @returns GETDNS_FCHG_ERRORS if problems (see fchg->errors for details) + */ +int +filechg_check(struct getdns_context *context, struct filechg *fchg) +{ + struct stat *finfo; + + if(fchg == NULL) + return 0; + + fchg->errors = GETDNS_FCHG_NOERROR; + fchg->changes = GETDNS_FCHG_NOCHANGES; + + finfo = GETDNS_MALLOC(context->my_mf, struct stat); + if(finfo == NULL) + { + fchg->errors = errno; + return GETDNS_FCHG_ERRORS; + } + + if(stat(fchg->fn, finfo) != 0) + { + GETDNS_FREE(context->my_mf, finfo); + fchg->errors = errno; + return GETDNS_FCHG_ERRORS; + } + + /* we want to consider a file that previously returned error for stat() as a + change */ + + if(fchg->prevstat == NULL) + fchg->changes = GETDNS_FCHG_MTIME | GETDNS_FCHG_CTIME; + else + { + if(fchg->prevstat->st_mtimespec.tv_sec != finfo->st_mtimespec.tv_sec) + fchg->changes |= GETDNS_FCHG_MTIME; + if(fchg->prevstat->st_ctimespec.tv_sec != finfo->st_ctimespec.tv_sec) + fchg->changes |= GETDNS_FCHG_CTIME; + GETDNS_FREE(context->my_mf, fchg->prevstat); + } + fchg->prevstat = finfo; + + return fchg->changes; +} /* filechg */ + static struct getdns_dict * create_ipaddr_dict_from_rdf(struct getdns_context *context, ldns_rdf * rdf) { @@ -212,27 +266,45 @@ create_from_ldns_list(struct getdns_context *context, ldns_rdf ** ldns_list, return result; } -/*---------------------------------------- set_os_defaults */ +/*---------------------------------------- set_os_defaults + we use ldns to read the resolv.conf file - the ldns resolver is + destroyed once the file is read +*/ static getdns_return_t set_os_defaults(struct getdns_context *context) { ldns_resolver *lr = NULL; - if (ldns_resolver_new_frm_file(&lr, NULL) != LDNS_STATUS_OK) { + ldns_rdf **rdf_list; + size_t rdf_list_sz; + + if (ldns_resolver_new_frm_file(&lr, NULL) != LDNS_STATUS_OK) return GETDNS_RETURN_GENERIC_ERROR; - } - ldns_rdf **rdf_list = ldns_resolver_nameservers(lr); - size_t rdf_list_sz = ldns_resolver_nameserver_count(lr); + + if(context->fchg_resolvconf == NULL) + { + context->fchg_resolvconf = (struct filechg *) malloc(sizeof(struct filechg)); + if(context->fchg_resolvconf == NULL) + return GETDNS_RETURN_GENERIC_ERROR; + context->fchg_resolvconf->fn = "/etc/resolv.conf"; + context->fchg_resolvconf->prevstat = NULL; + context->fchg_resolvconf->changes = GETDNS_FCHG_NOCHANGES; + context->fchg_resolvconf->errors = GETDNS_FCHG_NOERROR; + } + filechg_check(context, context->fchg_resolvconf); + + rdf_list = ldns_resolver_nameservers(lr); + rdf_list_sz = ldns_resolver_nameserver_count(lr); if (rdf_list_sz > 0) { context->upstream_list = create_from_ldns_list(context, rdf_list, rdf_list_sz); } + rdf_list = ldns_resolver_searchlist(lr); rdf_list_sz = ldns_resolver_searchlist_count(lr); if (rdf_list_sz > 0) { context->suffix = create_from_ldns_list(context, rdf_list, rdf_list_sz); } - /** cleanup **/ ldns_resolver_deep_free(lr); return GETDNS_RETURN_GOOD; @@ -265,7 +337,9 @@ transaction_id_cmp(const void *id1, const void *id2) } } -static int timeout_cmp(const void *to1, const void *to2) { +static int +timeout_cmp(const void *to1, const void *to2) +{ if (to1 == NULL && to2 == NULL) { return 0; } else if (to1 == NULL && to2 != NULL) { @@ -340,7 +414,8 @@ getdns_context_create_with_extended_memory_functions( result->outbound_requests = ldns_rbtree_create(transaction_id_cmp); result->resolution_type = GETDNS_RESOLUTION_RECURSING; - result->namespaces = create_default_namespaces(result); + if(create_default_namespaces(result) != GETDNS_RETURN_GOOD) + return GETDNS_RETURN_GENERIC_ERROR; result->timeout = 5000; result->follow_redirects = GETDNS_REDIRECTS_FOLLOW; @@ -360,6 +435,8 @@ getdns_context_create_with_extended_memory_functions( result->timeouts_by_time = ldns_rbtree_create(timeout_cmp); result->timeouts_by_id = ldns_rbtree_create(transaction_id_cmp); + result->fchg_resolvconf = NULL; + result->fchg_hosts = NULL; if (set_from_os) { if (GETDNS_RETURN_GOOD != set_os_defaults(result)) { getdns_context_destroy(result); @@ -376,7 +453,7 @@ getdns_context_create_with_extended_memory_functions( GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP); return GETDNS_RETURN_GOOD; -} /* getdns_context_create */ +} /* getdns_context_create_with_extended_memory_functions */ /* * getdns_context_create @@ -427,6 +504,18 @@ getdns_context_destroy(struct getdns_context *context) } if (context->namespaces) GETDNS_FREE(context->my_mf, context->namespaces); + if(context->fchg_resolvconf) + { + if(context->fchg_resolvconf->prevstat) + GETDNS_FREE(context->my_mf, context->fchg_resolvconf->prevstat); + GETDNS_FREE(context->my_mf, context->fchg_resolvconf); + } + if(context->fchg_hosts) + { + if(context->fchg_hosts->prevstat) + GETDNS_FREE(context->my_mf, context->fchg_hosts->prevstat); + GETDNS_FREE(context->my_mf, context->fchg_hosts); + } cancel_outstanding_requests(context, 0); getdns_extension_detach_eventloop(context); @@ -529,12 +618,23 @@ getdns_return_t getdns_context_set_namespaces(struct getdns_context *context, size_t namespace_count, getdns_namespace_t *namespaces) { + int i; + RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); if (namespace_count == 0 || namespaces == NULL) { return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; } - /** clean up old namespaces **/ + for(i=0; imy_mf, context->namespaces); /** duplicate **/ @@ -542,7 +642,7 @@ getdns_context_set_namespaces(struct getdns_context *context, namespace_count); memcpy(context->namespaces, namespaces, namespace_count * sizeof(getdns_namespace_t)); - + context->namespace_count = namespace_count; dispatch_updated(context, GETDNS_CONTEXT_CODE_NAMESPACES); return GETDNS_RETURN_GOOD; @@ -1079,41 +1179,58 @@ ub_setup_stub(struct ub_ctx *ctx, struct getdns_list * upstreams, size_t count) } getdns_return_t -getdns_context_prepare_for_resolution(struct getdns_context *context) +getdns_context_prepare_for_resolution(struct getdns_context *context, int usenamespaces) { + int i; + size_t upstream_len = 0; + RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - if (context->resolution_type_set == context->resolution_type) { - /* already set and no config changes have caused this to be - * bad. - */ - return GETDNS_RETURN_GOOD; - } - if (context->resolution_type == GETDNS_RESOLUTION_STUB) { - size_t upstream_len = 0; - getdns_return_t r = - getdns_list_get_length(context->upstream_list, - &upstream_len); - if (r != GETDNS_RETURN_GOOD || upstream_len == 0) { - return GETDNS_RETURN_BAD_CONTEXT; - } - /* set upstreams */ - ub_setup_stub(context->unbound_ctx, context->upstream_list, - upstream_len); - /* use /etc/hosts */ - ub_ctx_hosts(context->unbound_ctx, NULL); - - } else if (context->resolution_type == GETDNS_RESOLUTION_RECURSING) { - /* set recursive */ - /* TODO: use the root servers via root hints file */ - ub_ctx_set_fwd(context->unbound_ctx, NULL); - - } else { - /* bogus? */ + if (context->resolution_type_set == context->resolution_type) + { + /* already set and no config changes have caused this to be bad. */ return GETDNS_RETURN_BAD_CONTEXT; } + + /* TODO: respect namespace order (unbound always uses local first if cfg + * the spec calls for us to treat the namespace list as ordered + * so we need to respect that order + */ + + if(usenamespaces == 0) + { + ub_setup_stub(context->unbound_ctx, context->upstream_list, + upstream_len); + } + else + { + for(i=0; inamespace_count; i++) + { + if(context->namespaces[i] == GETDNS_NAMESPACE_LOCALNAMES) + { + ub_ctx_hosts(context->unbound_ctx, NULL); + } + else if(context->namespaces[i] == GETDNS_NAMESPACE_DNS) + { + if (context->resolution_type == GETDNS_RESOLUTION_STUB) + { + getdns_return_t r = + getdns_list_get_length(context->upstream_list, &upstream_len); + if (r != GETDNS_RETURN_GOOD || upstream_len == 0) + return GETDNS_RETURN_BAD_CONTEXT; + } else if (context->resolution_type == GETDNS_RESOLUTION_RECURSING) + { + /* TODO: use the root servers via root hints file */ + ub_ctx_set_fwd(context->unbound_ctx, NULL); + } else + return GETDNS_RETURN_BAD_CONTEXT; + } /* DNS */ + } /* for i */ + } /* if usenamespaces = 0 else */ + context->resolution_type_set = context->resolution_type; + return GETDNS_RETURN_GOOD; -} +} /* getdns_context_prepare_for_resolution */ getdns_return_t getdns_context_track_outbound_request(getdns_dns_req * req) diff --git a/src/context.h b/src/context.h index d51cae2f..ff1ed018 100644 --- a/src/context.h +++ b/src/context.h @@ -1,7 +1,7 @@ /** * - * /file - * /brief getdns context management functions + * \file context.h + * @brief getdns context management functions * * Originally taken from the getdns API description pseudo implementation. * @@ -44,22 +44,40 @@ struct getdns_dns_req; struct ldns_rbtree_t; struct ub_ctx; +#define GETDNS_FN_RESOLVCONF "/etc/resolv.conf" +#define GETDNS_FN_HOSTS "/etc/hosts" + +enum filechgs { GETDNS_FCHG_ERRORS = -1 + , GETDNS_FCHG_NOERROR = 0 + , GETDNS_FCHG_NOCHANGES = 0 + , GETDNS_FCHG_MTIME = 1 + , GETDNS_FCHG_CTIME = 2}; + /** function pointer typedefs */ typedef void (*getdns_update_callback) (struct getdns_context *, getdns_context_code_t); +/* internal use only for detecting changes to system files */ +struct filechg { + char *fn; + int changes; + int errors; + struct stat *prevstat; +}; + struct getdns_context { /* Context values */ - getdns_resolution_t resolution_type; - getdns_namespace_t *namespaces; - uint64_t timeout; - getdns_redirects_t follow_redirects; - struct getdns_list *dns_root_servers; + getdns_resolution_t resolution_type; + getdns_namespace_t *namespaces; + int namespace_count; + uint64_t timeout; + getdns_redirects_t follow_redirects; + struct getdns_list *dns_root_servers; getdns_append_name_t append_name; - struct getdns_list *suffix; - struct getdns_list *dnssec_trust_anchors; - struct getdns_list *upstream_list; + struct getdns_list *suffix; + struct getdns_list *dnssec_trust_anchors; + struct getdns_list *upstream_list; uint8_t edns_extended_rcode; uint8_t edns_version; @@ -103,14 +121,25 @@ struct getdns_context { */ struct ldns_rbtree_t *timeouts_by_id; struct ldns_rbtree_t *timeouts_by_time; -}; + + /* + * state data used to detect changes to the system config files + */ + struct filechg *fchg_resolvconf; + struct filechg *fchg_hosts; + +}; /* getdns_context */ /** internal functions **/ /** * Sets up the unbound contexts with stub or recursive behavior * if needed. + * @param context previously initialized getdns_context + * @param usenamespaces if 0 then only use the DNS, else use context namespace list + * @return GETDNS_RETURN_GOOD on success */ -getdns_return_t getdns_context_prepare_for_resolution(struct getdns_context *context); +getdns_return_t getdns_context_prepare_for_resolution(struct getdns_context *context, + int usenamespaces); /* track an outbound request */ getdns_return_t getdns_context_track_outbound_request(struct getdns_dns_req @@ -148,4 +177,6 @@ getdns_return_t getdns_context_schedule_timeout(struct getdns_context* context, getdns_return_t getdns_context_clear_timeout(struct getdns_context* context, getdns_transaction_t id); +int filechg_check(struct getdns_context *context, struct filechg *fchg); + #endif /* _GETDNS_CONTEXT_H_ */ diff --git a/src/example/example-simple-answers.c b/src/example/example-simple-answers.c index 722c3c5a..2f318555 100644 --- a/src/example/example-simple-answers.c +++ b/src/example/example-simple-answers.c @@ -4,6 +4,7 @@ * @brief example using getdns to resolve a simple query * * Originally taken from the getdns API description pseudo implementation. + * Modified to more clearly demonstrate some of the API features. * */ @@ -34,16 +35,11 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#ifdef HAVE_EVENT2_EVENT_H -# include -#else -# include -#endif #include #include #include #include +#include #include #include @@ -51,10 +47,10 @@ /* Set up the callback function, which will also do the processing of the results */ void this_callbackfn(struct getdns_context *this_context, - getdns_callback_type_t this_callback_type, - struct getdns_dict *this_response, - void *this_userarg, - getdns_transaction_t this_transaction_id) + uint16_t this_callback_type, + struct getdns_dict *this_response, + void *this_userarg, + getdns_transaction_t this_transaction_id) { getdns_return_t this_ret; uint32_t this_error; @@ -62,8 +58,8 @@ void this_callbackfn(struct getdns_context *this_context, struct getdns_list *just_the_addresses_ptr; struct getdns_dict *this_address; struct getdns_bindata *this_address_data; - size_t rec_count; - char *this_address_str; + size_t rec_count; + char *this_address_str; UNUSED_PARAM(this_userarg); UNUSED_PARAM(this_context); @@ -73,12 +69,12 @@ void this_callbackfn(struct getdns_context *this_context, /* Be sure the search returned something */ this_ret = getdns_dict_get_int(this_response, "status", &this_error); - if (this_ret != GETDNS_RETURN_GOOD) + if (this_ret != GETDNS_RETURN_GOOD) { fprintf(stderr, "The dictionary does not contain \"status\" (this shouldn't have happened). Exiting\n"); getdns_dict_destroy(this_response); return; - } + } if (this_error != GETDNS_RESPSTATUS_GOOD) { @@ -96,39 +92,39 @@ void this_callbackfn(struct getdns_context *this_context, } this_ret = getdns_list_get_length(just_the_addresses_ptr, &num_addresses); - if (this_ret != GETDNS_RETURN_GOOD) + if (this_ret != GETDNS_RETURN_GOOD) { fprintf(stderr, "The address list is invalid (this shouldn't have happened). Exiting\n"); getdns_dict_destroy(this_response); return; - } + } - if (num_addresses == 0) + if (num_addresses == 0) fprintf(stderr, "The address list has 0 records. Exiting\n"); /* Go through each record */ for (rec_count = 0; rec_count < num_addresses; ++rec_count) { this_ret = getdns_list_get_dict(just_the_addresses_ptr, rec_count, &this_address); - if(this_ret != GETDNS_RETURN_GOOD) - { - fprintf(stderr, "Record %d is invalid (this shouldn't have happened). skipping.\n", (int) rec_count); - continue; - } + if(this_ret != GETDNS_RETURN_GOOD) + { + fprintf(stderr, "Record %d is invalid (this shouldn't have happened). skipping.\n", (int) rec_count); + continue; + } /* Just print the address */ this_ret = getdns_dict_get_bindata(this_address, "address_data", &this_address_data); - if(this_ret != GETDNS_RETURN_GOOD) - { - fprintf(stderr, "Record %d does not contain \"address_data\" (this shouldn't happen), skipping\n", (int) rec_count); - } - else - { - this_address_str = getdns_display_ip_address(this_address_data); - printf("The address is %s\n", this_address_str); - free(this_address_str); - } - } + if(this_ret != GETDNS_RETURN_GOOD) + { + fprintf(stderr, "Record %d does not contain \"address_data\" (this shouldn't happen), skipping\n", (int) rec_count); + } + else + { + this_address_str = getdns_display_ip_address(this_address_data); + printf("The address is %s\n", this_address_str); + free(this_address_str); + } + } // for rec_count } else if (this_callback_type == GETDNS_CALLBACK_CANCEL) fprintf(stderr, "The callback with ID %"PRIu64" was cancelled. Exiting.\n", this_transaction_id); @@ -138,24 +134,57 @@ void this_callbackfn(struct getdns_context *this_context, getdns_dict_destroy(this_response); } /* this_callbackfn */ +void +usage(void) +{ + printf( + "USAGE: example-simple-answers [-s] [hostname]\n" + "\n" + "-s act as stub resolver (default is to act as a recursive resolver)\n" + "\n" + "The example program demonstrates the simplest use of the getdns API to\n" + "resolve a hostname to an IP address\n" + "\n"); + + return; +} /* usage */ + /*---------------------------------------- main */ int main(int argc, char *argv[]) { - char *this_name = "www.example.com"; + char *this_name = "www.example.com"; char *this_userarg = "somestring"; - int dispatch_return; - int exitval = EXIT_SUCCESS; + char opt; + int stubonly = 0; + int dispatch_return; + int exitval = EXIT_SUCCESS; struct getdns_context *this_context = NULL; struct event_base *this_event_base; - getdns_return_t dns_request_return; + getdns_return_t dns_request_return; getdns_transaction_t this_transaction_id; - getdns_return_t context_create_return; + getdns_return_t context_create_return; - if(argc > 1) - this_name = argv[1]; + while((opt = getopt(argc, argv, "?s")) != -1) + { + switch(opt) + { + case 's': + stubonly = 1; + break; + case '?': + default: + usage(); + exit(1); + } + } + argc -= optind; + argv += optind; - printf("resolving %s\n", this_name); + if(argc > 0) + this_name = argv[0]; + + printf("resolving %s\n", this_name); /* Create the DNS context for this call, use OS configs such as resolv.conf */ @@ -166,6 +195,16 @@ main(int argc, char *argv[]) return(GETDNS_RETURN_GENERIC_ERROR); } + if(stubonly == 1) + { + if(getdns_context_set_resolution_type(this_context, GETDNS_RESOLUTION_STUB) + != GETDNS_RETURN_GOOD) + { + fprintf(stderr, "Failed to set stub resolver in context (this should never fail). Exiting.\n"); + return(GETDNS_RETURN_GENERIC_ERROR); + } + } + /* Create an event base and put it in the context using the unknown function name */ this_event_base = event_base_new(); @@ -188,16 +227,15 @@ main(int argc, char *argv[]) if (dns_request_return == GETDNS_RETURN_BAD_DOMAIN_NAME) { fprintf(stderr, "A bad domain name was used: %s. Exiting.\n", this_name); - exitval = GETDNS_RETURN_GENERIC_ERROR; + exitval = GETDNS_RETURN_GENERIC_ERROR; } else { /* Call the event loop */ dispatch_return = event_base_dispatch(this_event_base); - - if(dispatch_return < 0) - fprintf(stderr, "event_base_dispatch() failed, returned %d\n", dispatch_return); + if(dispatch_return < 0) + fprintf(stderr, "event_base_dispatch() failed, returned %d\n", dispatch_return); } /* Clean up */ diff --git a/src/general.c b/src/general.c index 790828bd..a0870133 100644 --- a/src/general.c +++ b/src/general.c @@ -1,6 +1,7 @@ /** * - * /brief getdns_general and related support functions + * \file general.c + * @brief getdns_general and related support functions * * The getdns_general function is called by most of the other public entry * points to the library. Private support functions are also included in this @@ -209,7 +210,7 @@ ub_resolve_callback(void* arg, int err, struct ub_result* ub_res) submit_network_request(netreq->next); } } -} +} /* ub_resolve_callback */ getdns_return_t getdns_general_ub(struct getdns_context *context, @@ -218,7 +219,8 @@ getdns_general_ub(struct getdns_context *context, struct getdns_dict *extensions, void *userarg, getdns_transaction_t * transaction_id, - getdns_callback_t callbackfn) + getdns_callback_t callbackfn, + int usenamespaces) { getdns_return_t gr; int r; @@ -227,7 +229,7 @@ getdns_general_ub(struct getdns_context *context, return GETDNS_RETURN_INVALID_PARAMETER; } - gr = getdns_context_prepare_for_resolution(context); + gr = getdns_context_prepare_for_resolution(context, usenamespaces); if (gr != GETDNS_RETURN_GOOD) { return gr; } @@ -305,7 +307,7 @@ getdns_general(struct getdns_context *context, return extcheck; return getdns_general_ub(context, - name, request_type, extensions, userarg, transaction_id, callback); + name, request_type, extensions, userarg, transaction_id, callback, 0); } /* getdns_general */ @@ -321,20 +323,37 @@ getdns_address(struct getdns_context *context, getdns_transaction_t * transaction_id, getdns_callback_t callback) { int cleanup_extensions = 0; - if (!extensions) { + int extcheck; + getdns_return_t result; + + if (!context) + return GETDNS_RETURN_INVALID_PARAMETER; + if (!callback || !name) + return GETDNS_RETURN_INVALID_PARAMETER; + + extcheck = validate_dname(name); + if (extcheck != GETDNS_RETURN_GOOD) + return extcheck; + + /* we set the extensions that make general behave like getdns_address */ + if (!extensions) + { extensions = getdns_dict_create_with_context(context); cleanup_extensions = 1; } getdns_dict_set_int(extensions, GETDNS_STR_EXTENSION_RETURN_BOTH_V4_AND_V6, GETDNS_EXTENSION_TRUE); + extcheck = validate_extensions(extensions); + if (extcheck != GETDNS_RETURN_GOOD) + return extcheck; - getdns_return_t result = getdns_general(context, name, GETDNS_RRTYPE_A, - extensions, userarg, transaction_id, - callback); - if (cleanup_extensions) { + result = getdns_general_ub(context, + name, GETDNS_RRTYPE_A, extensions, userarg, transaction_id, callback, 1); + + if (cleanup_extensions) getdns_dict_destroy(extensions); - } + return result; -} +} /* getdns_address */ /* getdns_general.c */ diff --git a/src/general.h b/src/general.h index 4a699524..0f74db99 100644 --- a/src/general.h +++ b/src/general.h @@ -46,7 +46,9 @@ getdns_general_ub(struct getdns_context *context, uint16_t request_type, struct getdns_dict *extensions, void *userarg, - getdns_transaction_t * transaction_id, getdns_callback_t callbackfn); + getdns_transaction_t * transaction_id, + getdns_callback_t callbackfn, + int usenamespaces); void priv_getdns_call_user_callback(getdns_dns_req *, struct getdns_dict *); diff --git a/src/service.c b/src/service.c index 393aa322..5921d1cb 100644 --- a/src/service.c +++ b/src/service.c @@ -47,8 +47,29 @@ getdns_service(struct getdns_context *context, void *userarg, getdns_transaction_t * transaction_id, getdns_callback_t callback) { - return getdns_general(context, name, GETDNS_RRTYPE_SRV, - extensions, userarg, transaction_id, callback); + int parmcheck; + getdns_return_t result; + + if (!context) + return GETDNS_RETURN_INVALID_PARAMETER; + if (!callback || !name) + return GETDNS_RETURN_INVALID_PARAMETER; + + parmcheck = validate_dname(name); + if (parmcheck != GETDNS_RETURN_GOOD) + return parmcheck; + + if(extensions) + { + parmcheck = validate_extensions(extensions); + if (parmcheck != GETDNS_RETURN_GOOD) + return parmcheck; + } + + result = getdns_general_ub(context, + name, GETDNS_RRTYPE_SRV, extensions, userarg, transaction_id, callback, 1); + + return result; } /* getdns_service */ /* service.c */ diff --git a/src/util-internal.c b/src/util-internal.c index d2a8b133..93cb034d 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -1,7 +1,7 @@ /** * - * /file - * /brief private library routines + * \file util-internal.c + * @brief private library routines * * These routines are not intended to be used by applications calling into * the library. @@ -768,6 +768,6 @@ validate_dname(const char* dname) { return GETDNS_RETURN_BAD_DOMAIN_NAME; } return GETDNS_RETURN_GOOD; -} +} /* validate_dname */ /* util-internal.c */ diff --git a/src/util-internal.h b/src/util-internal.h index b2a3e1bf..f8759f6f 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -1,5 +1,6 @@ /** * + * \file util-internal.h * /brief getdns contect management functions * * This is the meat of the API