Skip to content

Commit

Permalink
cleanup for merge
Browse files Browse the repository at this point in the history
  • Loading branch information
MelindaShore committed Apr 10, 2015
2 parents 9fbf79c + 8f564c8 commit 0600f91
Show file tree
Hide file tree
Showing 26 changed files with 1,591 additions and 1,051 deletions.
35 changes: 35 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Changes in version 0.3.1 (10 April 2015)

* implemented asynchronous queries, bound to Context()
objects, and introduced Context.run() method. See
getdns.readthedocs.org

* queries now return a Result() object, with attributes.
See getdns.readthedocs.org

* removed leading GETDNS_ in all constants
(i.e. getdns.GETDNS_RRTYPE_AAAA is now getdns.RRTYPE_AAAA)

* added getdns.get_errorstr_by_id() method, making it easier
to provide user-friendly error messages

* prettied up printing of canonical names in Result object

* str and repr printing has been added to both Context and
Result objects

* dead code removed

* replaced instances of getdns_strerror() with
getdns_get_errorstr_by_id()

* fixed incorrect error return from Result construction

* moved __version__ attribute from Context to top-level
getdns module

* made exception handling within the module more consistent

* added documentation describing getdns.error

* broke out query types
568 changes: 360 additions & 208 deletions context.c

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions context_util.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include <Python.h>
#include <getdns/getdns.h>
#include <arpa/inet.h>
#include "pygetdns.h"


/*
* Get the address of the Python function object
* being passed in by name to the context
* query methods
*/

PyObject *
get_callback(char *py_main, char *callback)
{
PyObject *main_module;
PyObject *main_dict;
PyObject *callback_func;

if ((main_module = PyImport_AddModule(py_main)) == 0) {
PyErr_SetString(getdns_error, "No 'main'");
return NULL;
}
main_dict = PyModule_GetDict(main_module);
if ((callback_func = PyDict_GetItemString(main_dict, callback)) == 0) {
PyErr_SetString(getdns_error, "callback not found\n");
return NULL;
}
if (!PyCallable_Check(callback_func)) {
PyErr_SetString(getdns_error, "The callback function is not runnable");
return NULL;
}
return callback_func;
}


void
callback_shim(struct getdns_context *context,
getdns_callback_type_t type,
struct getdns_dict *response,
void *userarg,
getdns_transaction_t tid)
{
PyObject *py_callback_type;
PyObject *py_result;
PyObject *py_tid;
PyObject *py_userarg;

userarg_blob *u = (userarg_blob *)userarg;
if ((py_callback_type = PyInt_FromLong((long)type)) == NULL) {
PyObject *err_type, *err_value, *err_traceback;
PyErr_Fetch(&err_type, &err_value, &err_traceback);
PyErr_Restore(err_type, err_value, err_traceback);
return;
}
if (type == GETDNS_CALLBACK_CANCEL) {
py_result = Py_None;
py_tid = Py_None;
py_userarg = Py_None;
} else {
py_result = result_create(response);
py_tid = PyInt_FromLong((long)tid);
if (u->userarg)
py_userarg = PyString_FromString(u->userarg);
else
py_userarg = Py_None;
}
PyObject_CallFunctionObjArgs(u->callback_func, py_callback_type, py_result, py_userarg, py_tid, NULL);
}
4 changes: 2 additions & 2 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@
# built documents.
#
# The short X.Y version.
version = '0.2.1'
version = '0.3.1'
# The full version, including alpha/beta/rc tags.
release = '0.2.1'
release = '0.3.1'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
52 changes: 52 additions & 0 deletions doc/exceptions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
:mod:`getdns` exceptions
=============================

.. module:: getdns
:synopsis: getdns exception description and explanation
.. sectionauthor:: Melinda Shore <[email protected]>


getdns exceptions
-----------------

.. py:exception:: getdns.error
getdns will throw an exception, ``getdns.error``, under
certain conditions. Those conditions include:

* a required parameter having a bad value
* a badly-formed domain name in the query
* a bad ``Context()`` object
* a failed ``Context()`` update
* an out-of-bounds error for a getdns data structure
* requesting an extension that doesn't exist
* requesting DNSSEC validation while using stub
resolution

Please note that a successful return from a getdns method
does `not` indicate that the query returned the records
being requested, but rather that the query is formed
correctly and has been submitted to the DNS. A getdns
exception is typically the result of a coding error.

getdns will set the exception message to a diagnostic
string, which may be examined for help in resolving the
error.

Example
-------

::

import getdns, sys
c = getdns.Context()
try:
results = c.address('www.example.com', foo='bar')
except getdns.error, e:
print(str(e))
sys.exit(1)


This will result in "A required parameter had an invalid
value" being printed to the screen.
98 changes: 98 additions & 0 deletions doc/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -415,3 +415,101 @@ call to the API. Each member has the following names:
* ``end_time`` is the time the query was received in milliseconds since the epoch, represented as an integer
* ``entire_reply`` is the entire response received
* ``dnssec_result`` is the DNSSEC status, or ``getdns.GETDNS_DNSSEC_NOT_PERFORMED`` if DNSSEC validation was not performed


Asynchronous queries
^^^^^^^^^^^^^^^^^^^^

The getdns Python bindings support asynchronous queries, in
which a query returns immediately and a callback function is
invoked when the response data are returned. The query
method interfaces are fundamentally the same, with a few
differences:

* The query returns a transaction id. That transaction
id may be used to cancel future callbacks
* The query invocation includes the name of a callback
function. For example, if you'd like to call the
function "my_callback" when the query returns, an
address lookup could look like

>>> c = getdns.Context()
>>> tid = c.address('www.example.org', callback='my_callback')

* We've introduced a new ``Context`` method, called
``run``. When your program is ready to check to see
whether or not the query has returned, invoke the run()
method on your context. Note that we use the libevent
asynchronous event library and an event_base is
associated with a context. So, if you have multiple
outstanding events associated with a particular
context, ``run`` will invoke all of those that are
waiting and ready.

The callback script takes four arguments: ``type``,
``result``, ``userarg``, and ``transaction_id. The ``type``
argument contains the callback type, which may have one of
the following values:

* ``getdns.CALLBACK_COMPLETE``: The query was successful
and the results are contained in the ``result``
argument
* ``getdns.CALLBACK_CANCEL``: The callback was cancelled
before the results were processed
* ``getdns.CALLBACK_TIMEOUT``: The query timed out before
the results were processed
* ``getdns.CALLBACK_ERROR``: An unspecified error
occurred

The ``result`` argument contains a result object, with the
query response

The ``userarg`` argument contains the optional user argument
that was passed to the query at the time it was invoked.

The ``transaction_id`` argument contains the transaction_id
associated with a particular query; this is the same
transaction id that was returned when the query was invoked.

This is an example callback function:

.. code-block:: python
def cbk(type, result, userarg, tid):
if type == getdns.CALLBACK_COMPLETE:
status = result.status
if status == getdns.GETDNS_RESPSTATUS_GOOD:
for addr in result.just_address_answers:
addr_type = addr['address_type']
addr_data = addr['address_data']
print '{0}: {1} {2}'.format(userarg, addr_type, addr_data)
elif status == getdns.GETDNS_RESPSTATUS_NO_SECURE_ANSWERS:
print "{0}: No DNSSEC secured responses found".format(hostname)
else:
print "{0}: getdns.address() returned error: {1}".format(hostname, status)
elif type == getdns.CALLBACK_CANCEL:
print 'Callback cancelled'
elif type == getdns.CALLBACK_TIMEOUT:
print 'Query timed out'
else:
print 'Unknown error'
Utility methods
---------------

At the present time we support one utility method.

.. py:method:: get_errorstr_by_id(id)
``getdns.get_errorstr_by_id`` returns a string containing
text describing a getdns return code, helping to make
reporting errors to users a little easier. For example:

.. code-block:: python
if results.replies_full['status'] != getdns.GETDNS_RESPSTATUS_GOOD:
print(getdns.get_errorstr_by_id(id=results.replies_full['status'])
sys.exit(1)
49 changes: 27 additions & 22 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ getdns: Python bindings for getdns
for the `getdns <http://getdnsapi.net/>`_ API. getdns is a
modern, asynchronous DNS API that simplifies access to
advanced DNS features, including DNSSEC. The API
`specification <http://www.vpnc.org/getdns-api/>`_ was
`specification <http://getdnsapi.net/spec/>`_ was
developed by Paul Hoffman. getdns is built on top of the
getdns implementation developed as a joint project between
`Verisign Labs
Expand All @@ -29,7 +29,7 @@ This version of getdns has been built and tested against Python
2.7. We also expect these other prerequisites to be
installed:

* `libgetdns <http://getdnsapi.net/>`_, version 0.1.2 or later
* `libgetdns <http://getdnsapi.net/>`_, version 0.1.7 or later
* `libldns <https://www.nlnetlabs.nl/projects/ldns/>`_,
version 1.6.11 or later
* `libunbound
Expand All @@ -45,7 +45,7 @@ as follows:

./configure --with-libevent

This release has been tested against libgetdns 0.1.5.
This release has been tested against libgetdns 0.1.7.

Building
========
Expand Down Expand Up @@ -86,7 +86,7 @@ examined directly, and the overall state of a given context can be
queried with the Context.get_api_information() method.

See section 8 of the `API
specification <http://www.vpnc.org/getdns-api/>`_
specification <http://getdnsapi.net/spec/>`_


Examples
Expand All @@ -105,20 +105,21 @@ results to the screen:
sys.exit(1)
ctx = getdns.Context()
extensions = { "return_both_v4_and_v6" : getdns.GETDNS_EXTENSION_TRUE }
results = ctx.address(name=sys.argv[1], extensions=extensions)
if results["status"] == getdns.GETDNS_RESPSTATUS_GOOD:
extensions = { "return_both_v4_and_v6" :
getdns.GETDNS_EXTENSION_TRUE }
results = ctx.address(name=sys.argv[1],
extensions=extensions)
if results.status == getdns.RESPSTATUS_GOOD:
sys.stdout.write("Addresses: ")
for addr in results["just_address_answers"]:
for addr in results.just_address_answers:
print " {0}".format(addr["address_data"])
sys.stdout.write("\n\n")
print "Entire results tree: "
pprint.pprint(results)
if results["status"] == getdns.GETDNS_RESPSTATUS_NO_NAME:
pprint.pprint(results.replies_tree)
if results.status == getdns.RESPSTATUS_NO_NAME:
print "{0} not found".format(sys.argv[1])
if __name__ == "__main__":
main()
Expand Down Expand Up @@ -149,27 +150,31 @@ In this example, we do a DNSSEC query and check the response:
sys.exit(1)
ctx = getdns.Context()
extensions = { "return_both_v4_and_v6" : getdns.GETDNS_EXTENSION_TRUE,
"dnssec_return_status" : getdns.GETDNS_EXTENSION_TRUE }
results = ctx.address(name=sys.argv[1], extensions=extensions)
if results["status"] == getdns.GETDNS_RESPSTATUS_GOOD:
extensions = { "return_both_v4_and_v6" :
getdns.EXTENSION_TRUE,
"dnssec_return_status" :
getdns.EXTENSION_TRUE }
results = ctx.address(name=sys.argv[1],
extensions=extensions)
if results.status == getdns.RESPSTATUS_GOOD:
sys.stdout.write("Addresses: ")
for addr in results["just_address_answers"]:
for addr in results.just_address_answers:
print " {0}".format(addr["address_data"])
sys.stdout.write("\n")
for result in results["replies_tree"]:
for result in results.replies_tree:
if "dnssec_status" in result.keys():
print "{0}: dnssec_status: {1}".format(result["canonical_name"],
print "{0}: dnssec_status:
{1}".format(result["canonical_name"],
dnssec_message(result["dnssec_status"]))
if results["status"] == getdns.GETDNS_RESPSTATUS_NO_NAME:
if results.status == getdns.RESPSTATUS_NO_NAME:
print "{0} not found".format(sys.argv[1])
if __name__ == "__main__":
main()
Known issues
============
Expand All @@ -186,7 +191,7 @@ Contents:

functions
response

exceptions


Indices and tables
Expand Down
Loading

0 comments on commit 0600f91

Please sign in to comment.